boo/lib/graphicsdev/nx/NX.cpp

1898 lines
67 KiB
C++

#include "boo/graphicsdev/NX.hpp"
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstring>
#include "boo/IGraphicsContext.hpp"
#include "boo/graphicsdev/GLSLMacros.hpp"
#include "lib/graphicsdev/Common.hpp"
#include "main/shaderobj.h"
#include "st_program.h"
#include "pipe/p_state.h"
#include "util/u_format.h"
#include "state_tracker/winsys_handle.h"
extern "C" {
#include "main/viewport.h"
#include "nouveau_winsys.h"
#include "nouveau_screen.h"
#include "nvc0/nvc0_program.h"
#include "gallium/winsys/nouveau/switch/nouveau_switch_public.h"
}
#include <logvisor/logvisor.hpp>
#include <xxhash/xxhash.h>
#include <switch.h>
namespace boo {
static logvisor::Module Log("boo::NX");
struct NXCommandQueue;
class NXDataFactoryImpl : public NXDataFactory, public GraphicsDataFactoryHead {
public:
float m_gamma = 1.f;
ObjToken<IShaderDataBinding> m_gammaBinding;
IGraphicsContext* m_parent;
NXContext* m_ctx;
NXDataFactoryImpl(IGraphicsContext* parent, NXContext* ctx) : m_parent(parent), m_ctx(ctx) {}
Platform platform() const { return Platform::NX; }
const SystemChar* platformName() const { return _SYS_STR("NX"); }
void SetupGammaResources() {}
void DestroyGammaResources() {}
void commitTransaction(const FactoryCommitFunc& __BooTraceArgs);
boo::ObjToken<IGraphicsBufferD> newPoolBuffer(BufferUse use, size_t stride, size_t count __BooTraceArgs);
void setDisplayGamma(float gamma) {
m_gamma = gamma;
// if (gamma != 1.f)
// UpdateGammaLUT(m_gammaLUT.get(), gamma);
}
bool isTessellationSupported(uint32_t& maxPatchSizeOut) {
maxPatchSizeOut = 0;
if (!m_ctx->m_st->ctx->Extensions.ARB_tessellation_shader)
return false;
maxPatchSizeOut = m_ctx->m_st->ctx->Const.MaxPatchVertices;
return true;
}
};
struct NXData : BaseGraphicsData {
NXContext* m_ctx;
/* Vertex, Index, Uniform */
struct pipe_resource* m_constantBuffers[3] = {};
explicit NXData(NXDataFactoryImpl& head __BooTraceArgs)
: BaseGraphicsData(head __BooTraceArgsUse), m_ctx(head.m_ctx) {}
~NXData() {
for (int i = 0; i < 3; ++i)
pipe_resource_reference(&m_constantBuffers[i], nullptr);
}
};
struct NXPool : BaseGraphicsPool {
NXContext* m_ctx;
struct pipe_resource* m_constantBuffer;
explicit NXPool(NXDataFactoryImpl& head __BooTraceArgs)
: BaseGraphicsPool(head __BooTraceArgsUse), m_ctx(head.m_ctx) {}
~NXPool() { pipe_resource_reference(&m_constantBuffer, nullptr); }
};
static const unsigned USE_TABLE[] = {0, PIPE_BIND_VERTEX_BUFFER, PIPE_BIND_INDEX_BUFFER, PIPE_BIND_CONSTANT_BUFFER};
union nx_buffer_info {
pipe_vertex_buffer v;
pipe_constant_buffer c;
};
class NXGraphicsBufferS : public GraphicsDataNode<IGraphicsBufferS> {
friend class NXDataFactory;
friend struct NXCommandQueue;
NXContext* m_ctx;
size_t m_sz;
std::unique_ptr<uint8_t[]> m_stagingBuf;
NXGraphicsBufferS(const boo::ObjToken<BaseGraphicsData>& parent, BufferUse use, NXContext* ctx, const void* data,
size_t stride, size_t count)
: GraphicsDataNode<IGraphicsBufferS>(parent)
, m_ctx(ctx)
, m_sz(stride * count)
, m_stagingBuf(new uint8_t[m_sz])
, m_use(use) {
memmove(m_stagingBuf.get(), data, m_sz);
if (m_use == BufferUse::Vertex)
m_bufferInfo.v.stride = uint16_t(stride);
}
public:
size_t size() const { return m_sz; }
nx_buffer_info m_bufferInfo;
BufferUse m_use;
unsigned sizeForGPU(NXContext* ctx, unsigned offset) {
if (m_use == BufferUse::Uniform) {
unsigned minOffset = std::max(256u, ctx->m_st->ctx->Const.UniformBufferOffsetAlignment);
offset = (offset + minOffset - 1) & ~(minOffset - 1);
m_bufferInfo.c.buffer_offset = offset;
m_bufferInfo.c.buffer_size = m_sz;
} else {
m_bufferInfo.v.buffer_offset = offset;
}
offset += m_sz;
return offset;
}
void placeForGPU(struct pipe_resource* bufObj, uint8_t* buf) {
if (m_use == BufferUse::Uniform)
m_bufferInfo.c.buffer = bufObj;
else
m_bufferInfo.v.buffer.resource = bufObj;
memmove(buf + m_bufferInfo.v.buffer_offset, m_stagingBuf.get(), m_sz);
m_stagingBuf.reset();
}
};
template <class DataCls>
class NXGraphicsBufferD : public GraphicsDataNode<IGraphicsBufferD, DataCls> {
friend class NXDataFactory;
friend class NXDataFactoryImpl;
friend struct NXCommandQueue;
NXContext* m_ctx;
size_t m_cpuSz;
std::unique_ptr<uint8_t[]> m_cpuBuf;
int m_validSlots = 0;
NXGraphicsBufferD(const boo::ObjToken<DataCls>& parent, BufferUse use, NXContext* ctx, size_t stride, size_t count)
: GraphicsDataNode<IGraphicsBufferD, DataCls>(parent)
, m_ctx(ctx)
, m_cpuSz(stride * count)
, m_cpuBuf(new uint8_t[m_cpuSz])
, m_use(use) {
if (m_use == BufferUse::Vertex) {
m_bufferInfo[0].v.stride = stride;
m_bufferInfo[1].v.stride = stride;
}
}
void update(int b) {
int slot = 1 << b;
if ((slot & m_validSlots) == 0) {
memcpy(m_bufferPtrs[b], m_cpuBuf.get(), m_cpuSz);
m_validSlots |= slot;
}
}
public:
nx_buffer_info m_bufferInfo[2];
uint8_t* m_bufferPtrs[2] = {};
BufferUse m_use;
void load(const void* data, size_t sz) {
size_t bufSz = std::min(sz, m_cpuSz);
memmove(m_cpuBuf.get(), data, bufSz);
m_validSlots = 0;
}
void* map(size_t sz) {
if (sz > m_cpuSz)
return nullptr;
return m_cpuBuf.get();
}
void unmap() { m_validSlots = 0; }
unsigned sizeForGPU(NXContext* ctx, unsigned offset) {
for (int i = 0; i < 2; ++i) {
if (m_use == BufferUse::Uniform) {
size_t minOffset = std::max(256u, ctx->m_st->ctx->Const.UniformBufferOffsetAlignment);
offset = (offset + minOffset - 1) & ~(minOffset - 1);
m_bufferInfo[i].c.buffer_offset = offset;
m_bufferInfo[i].c.buffer_size = m_cpuSz;
} else {
m_bufferInfo[i].v.buffer_offset = offset;
}
offset += m_cpuSz;
}
return offset;
}
void placeForGPU(struct pipe_resource* bufObj, uint8_t* buf) {
if (m_use == BufferUse::Uniform) {
m_bufferInfo[0].c.buffer = bufObj;
m_bufferInfo[1].c.buffer = bufObj;
m_bufferPtrs[0] = buf + m_bufferInfo[0].c.buffer_offset;
m_bufferPtrs[1] = buf + m_bufferInfo[1].c.buffer_offset;
} else {
m_bufferInfo[0].v.buffer.resource = bufObj;
m_bufferInfo[1].v.buffer.resource = bufObj;
m_bufferPtrs[0] = buf + m_bufferInfo[0].v.buffer_offset;
m_bufferPtrs[1] = buf + m_bufferInfo[1].v.buffer_offset;
}
}
};
static void MakeSampler(NXContext* ctx, void*& sampOut, TextureClampMode mode, int mips) {
uint32_t key = (uint32_t(mode) << 16) | mips;
auto search = ctx->m_samplers.find(key);
if (search != ctx->m_samplers.end()) {
sampOut = search->second;
return;
}
/* Create linear sampler */
pipe_sampler_state samplerInfo = {};
samplerInfo.min_img_filter = PIPE_TEX_FILTER_LINEAR;
samplerInfo.min_mip_filter = PIPE_TEX_MIPFILTER_LINEAR;
samplerInfo.mag_img_filter = PIPE_TEX_FILTER_LINEAR;
samplerInfo.compare_mode = PIPE_TEX_COMPARE_NONE;
samplerInfo.compare_func = PIPE_FUNC_ALWAYS;
samplerInfo.normalized_coords = 1;
samplerInfo.max_anisotropy = 16;
samplerInfo.seamless_cube_map = 0;
samplerInfo.lod_bias = 0;
samplerInfo.min_lod = 0;
samplerInfo.max_lod = mips - 1;
switch (mode) {
case TextureClampMode::Repeat:
default:
samplerInfo.wrap_s = PIPE_TEX_WRAP_REPEAT;
samplerInfo.wrap_t = PIPE_TEX_WRAP_REPEAT;
samplerInfo.wrap_r = PIPE_TEX_WRAP_REPEAT;
break;
case TextureClampMode::ClampToWhite:
samplerInfo.wrap_s = PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER;
samplerInfo.wrap_t = PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER;
samplerInfo.wrap_r = PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER;
for (int i = 0; i < 4; ++i)
samplerInfo.border_color.f[i] = 1.f;
break;
case TextureClampMode::ClampToBlack:
samplerInfo.wrap_s = PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER;
samplerInfo.wrap_t = PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER;
samplerInfo.wrap_r = PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER;
samplerInfo.border_color.f[3] = 1.f;
break;
case TextureClampMode::ClampToEdge:
samplerInfo.wrap_s = PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE;
samplerInfo.wrap_t = PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE;
samplerInfo.wrap_r = PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE;
break;
case TextureClampMode::ClampToEdgeNearest:
samplerInfo.mag_img_filter = PIPE_TEX_FILTER_NEAREST;
samplerInfo.min_mip_filter = PIPE_TEX_MIPFILTER_NEAREST;
samplerInfo.min_img_filter = PIPE_TEX_FILTER_NEAREST;
samplerInfo.max_anisotropy = 0;
samplerInfo.wrap_s = PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE;
samplerInfo.wrap_t = PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE;
samplerInfo.wrap_r = PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE;
break;
}
sampOut = ctx->m_pctx->create_sampler_state(ctx->m_pctx, &samplerInfo);
ctx->m_samplers[key] = sampOut;
}
class NXTextureS : public GraphicsDataNode<ITextureS> {
friend class NXDataFactory;
NXContext* m_ctx;
TextureFormat m_fmt;
size_t m_sz;
size_t m_width, m_height, m_mips;
TextureClampMode m_clampMode;
pipe_format m_nxFmt;
int m_pixelPitchNum = 1;
int m_pixelPitchDenom = 1;
NXTextureS(const boo::ObjToken<BaseGraphicsData>& parent, NXContext* ctx, size_t width, size_t height, size_t mips,
TextureFormat fmt, TextureClampMode clampMode, const void* data, size_t sz)
: GraphicsDataNode<ITextureS>(parent)
, m_ctx(ctx)
, m_fmt(fmt)
, m_sz(sz)
, m_width(width)
, m_height(height)
, m_mips(mips)
, m_clampMode(clampMode) {
pipe_format pfmt;
switch (fmt) {
case TextureFormat::RGBA8:
pfmt = PIPE_FORMAT_R8G8B8A8_UNORM;
m_pixelPitchNum = 4;
break;
case TextureFormat::I8:
pfmt = PIPE_FORMAT_R8_UNORM;
break;
case TextureFormat::I16:
pfmt = PIPE_FORMAT_R16_UNORM;
m_pixelPitchNum = 2;
break;
case TextureFormat::DXT1:
pfmt = PIPE_FORMAT_DXT1_RGBA;
m_pixelPitchNum = 1;
m_pixelPitchDenom = 2;
break;
default:
Log.report(logvisor::Fatal, fmt("unsupported tex format"));
}
m_nxFmt = pfmt;
struct pipe_resource texTempl = {};
texTempl.target = PIPE_TEXTURE_2D;
texTempl.format = m_nxFmt;
texTempl.width0 = width;
texTempl.height0 = height;
texTempl.depth0 = 1;
texTempl.last_level = mips - 1;
texTempl.array_size = 1;
texTempl.bind = PIPE_BIND_SAMPLER_VIEW;
m_gpuTex = ctx->m_screen->resource_create(ctx->m_screen, &texTempl);
if (!m_gpuTex) {
Log.report(logvisor::Fatal, fmt("Failed to create texture"));
return;
}
uint blockSize = util_format_get_blocksize(m_nxFmt);
uint8_t* ptr = (uint8_t*)data;
for (int i = 0; i < mips; ++i) {
size_t regionPitch = width * height * m_pixelPitchNum / m_pixelPitchDenom;
size_t rowStride = width * blockSize;
size_t imageBytes = rowStride * height;
struct pipe_box box;
u_box_2d(0, 0, width, height, &box);
ctx->m_pctx->texture_subdata(ctx->m_pctx, m_gpuTex, i, PIPE_TRANSFER_WRITE, &box, ptr, rowStride, imageBytes);
if (width > 1)
width /= 2;
if (height > 1)
height /= 2;
ptr += regionPitch;
}
struct pipe_sampler_view svTempl = {};
svTempl.format = m_nxFmt;
svTempl.texture = m_gpuTex;
svTempl.u.tex.last_level = mips - 1;
svTempl.swizzle_r = PIPE_SWIZZLE_X;
svTempl.swizzle_g = PIPE_SWIZZLE_Y;
svTempl.swizzle_b = PIPE_SWIZZLE_Z;
svTempl.swizzle_a = PIPE_SWIZZLE_W;
m_gpuView = ctx->m_pctx->create_sampler_view(ctx->m_pctx, m_gpuTex, &svTempl);
}
public:
struct pipe_resource* m_gpuTex;
struct pipe_sampler_view* m_gpuView = nullptr;
void* m_sampler = nullptr;
~NXTextureS() {
pipe_resource_reference(&m_gpuTex, nullptr);
pipe_sampler_view_reference(&m_gpuView, nullptr);
}
void setClampMode(TextureClampMode mode) {
m_clampMode = mode;
MakeSampler(m_ctx, m_sampler, mode, m_mips);
}
TextureFormat format() const { return m_fmt; }
};
class NXTextureSA : public GraphicsDataNode<ITextureSA> {
friend class NXDataFactory;
NXContext* m_ctx;
TextureFormat m_fmt;
size_t m_sz;
size_t m_width, m_height, m_layers, m_mips;
TextureClampMode m_clampMode;
pipe_format m_nxFmt;
int m_pixelPitchNum = 1;
int m_pixelPitchDenom = 1;
NXTextureSA(const boo::ObjToken<BaseGraphicsData>& parent, NXContext* ctx, size_t width, size_t height, size_t layers,
size_t mips, TextureFormat fmt, TextureClampMode clampMode, const void* data, size_t sz)
: GraphicsDataNode<ITextureSA>(parent)
, m_ctx(ctx)
, m_fmt(fmt)
, m_sz(sz)
, m_width(width)
, m_height(height)
, m_layers(layers)
, m_mips(mips)
, m_clampMode(clampMode) {
pipe_format pfmt;
switch (fmt) {
case TextureFormat::RGBA8:
pfmt = PIPE_FORMAT_R8G8B8A8_UNORM;
m_pixelPitchNum = 4;
break;
case TextureFormat::I8:
pfmt = PIPE_FORMAT_R8_UNORM;
break;
case TextureFormat::I16:
pfmt = PIPE_FORMAT_R16_UNORM;
m_pixelPitchNum = 2;
break;
default:
Log.report(logvisor::Fatal, fmt("unsupported tex format"));
}
m_nxFmt = pfmt;
struct pipe_resource texTempl = {};
texTempl.target = PIPE_TEXTURE_2D;
texTempl.format = m_nxFmt;
texTempl.width0 = width;
texTempl.height0 = height;
texTempl.depth0 = 1;
texTempl.last_level = mips - 1;
texTempl.array_size = layers;
texTempl.bind = PIPE_BIND_SAMPLER_VIEW;
m_gpuTex = ctx->m_screen->resource_create(ctx->m_screen, &texTempl);
if (!m_gpuTex) {
Log.report(logvisor::Fatal, fmt("Failed to create texture"));
return;
}
uint blockSize = util_format_get_blocksize(m_nxFmt);
uint8_t* ptr = (uint8_t*)data;
for (int i = 0; i < mips; ++i) {
size_t regionPitch = width * height * m_layers * m_pixelPitchNum / m_pixelPitchDenom;
size_t rowStride = width * blockSize;
size_t imageBytes = rowStride * height;
struct pipe_box box;
u_box_2d(0, 0, width, height, &box);
ctx->m_pctx->texture_subdata(ctx->m_pctx, m_gpuTex, i, PIPE_TRANSFER_WRITE, &box, ptr, rowStride, imageBytes);
if (width > 1)
width /= 2;
if (height > 1)
height /= 2;
ptr += regionPitch;
}
struct pipe_sampler_view svTempl = {};
svTempl.format = m_nxFmt;
svTempl.texture = m_gpuTex;
svTempl.u.tex.last_layer = layers - 1;
svTempl.u.tex.last_level = mips - 1;
svTempl.swizzle_r = PIPE_SWIZZLE_X;
svTempl.swizzle_g = PIPE_SWIZZLE_Y;
svTempl.swizzle_b = PIPE_SWIZZLE_Z;
svTempl.swizzle_a = PIPE_SWIZZLE_W;
m_gpuView = ctx->m_pctx->create_sampler_view(ctx->m_pctx, m_gpuTex, &svTempl);
}
public:
struct pipe_resource* m_gpuTex;
struct pipe_sampler_view* m_gpuView = nullptr;
void* m_sampler = nullptr;
~NXTextureSA() {
pipe_resource_reference(&m_gpuTex, nullptr);
pipe_sampler_view_reference(&m_gpuView, nullptr);
}
void setClampMode(TextureClampMode mode) {
m_clampMode = mode;
MakeSampler(m_ctx, m_sampler, mode, m_mips);
}
TextureFormat format() const { return m_fmt; }
size_t layers() const { return m_layers; }
};
class NXTextureD : public GraphicsDataNode<ITextureD> {
friend class NXDataFactory;
friend class NXCommandQueue;
NXCommandQueue* m_q;
size_t m_width;
size_t m_height;
TextureFormat m_fmt;
TextureClampMode m_clampMode;
std::unique_ptr<uint8_t[]> m_stagingBuf;
size_t m_cpuSz;
pipe_format m_nxFmt;
int m_validSlots = 0;
NXTextureD(const boo::ObjToken<BaseGraphicsData>& parent, NXCommandQueue* q, size_t width, size_t height,
TextureFormat fmt, TextureClampMode clampMode);
void update(int b);
public:
struct pipe_resource* m_gpuTex[2];
struct pipe_sampler_view* m_gpuView[2];
void* m_sampler = nullptr;
~NXTextureD() {
for (int i = 0; i < 2; ++i) {
pipe_resource_reference(&m_gpuTex[i], nullptr);
pipe_sampler_view_reference(&m_gpuView[i], nullptr);
}
}
void setClampMode(TextureClampMode mode);
void load(const void* data, size_t sz);
void* map(size_t sz);
void unmap();
TextureFormat format() const { return m_fmt; }
};
#define MAX_BIND_TEXS 4
static constexpr pipe_format ColorFormat = PIPE_FORMAT_R8G8B8A8_UNORM;
static constexpr pipe_format DepthFormat = PIPE_FORMAT_Z32_FLOAT;
class NXTextureR : public GraphicsDataNode<ITextureR> {
NXContext* m_ctx;
friend class NXDataFactory;
friend struct NXCommandQueue;
size_t m_width = 0;
size_t m_height = 0;
unsigned m_samplesColor, m_samplesDepth;
size_t m_colorBindCount;
size_t m_depthBindCount;
void Setup(NXContext* ctx) {
/* no-ops on first call */
doDestroy();
/* color target */
struct pipe_resource texTempl = {};
texTempl.target = PIPE_TEXTURE_2D;
texTempl.format = ColorFormat;
texTempl.width0 = m_width;
texTempl.height0 = m_height;
texTempl.depth0 = 1;
texTempl.array_size = 1;
texTempl.nr_samples = texTempl.nr_storage_samples = m_samplesColor;
texTempl.bind = PIPE_BIND_RENDER_TARGET;
m_colorTex = ctx->m_screen->resource_create(ctx->m_screen, &texTempl);
if (!m_colorTex) {
Log.report(logvisor::Fatal, fmt("Failed to create color target texture"));
return;
}
/* depth target */
texTempl.format = DepthFormat;
texTempl.nr_samples = texTempl.nr_storage_samples = m_samplesDepth;
texTempl.bind = PIPE_BIND_DEPTH_STENCIL;
m_depthTex = ctx->m_screen->resource_create(ctx->m_screen, &texTempl);
if (!m_depthTex) {
Log.report(logvisor::Fatal, fmt("Failed to create depth target texture"));
return;
}
texTempl.nr_samples = texTempl.nr_storage_samples = 1;
for (size_t i = 0; i < m_colorBindCount; ++i) {
texTempl.format = ColorFormat;
texTempl.bind = PIPE_BIND_SAMPLER_VIEW;
m_colorBindTex[i] = ctx->m_screen->resource_create(ctx->m_screen, &texTempl);
if (!m_colorBindTex[i]) {
Log.report(logvisor::Fatal, fmt("Failed to create color bind texture"));
return;
}
}
for (size_t i = 0; i < m_depthBindCount; ++i) {
texTempl.format = DepthFormat;
texTempl.bind = PIPE_BIND_SAMPLER_VIEW;
m_depthBindTex[i] = ctx->m_screen->resource_create(ctx->m_screen, &texTempl);
if (!m_depthBindTex[i]) {
Log.report(logvisor::Fatal, fmt("Failed to create depth bind texture"));
return;
}
}
/* Create resource views */
struct pipe_sampler_view svTempl = {};
svTempl.format = ColorFormat;
svTempl.swizzle_r = PIPE_SWIZZLE_X;
svTempl.swizzle_g = PIPE_SWIZZLE_Y;
svTempl.swizzle_b = PIPE_SWIZZLE_Z;
svTempl.swizzle_a = PIPE_SWIZZLE_W;
svTempl.texture = m_colorTex;
m_colorView = ctx->m_pctx->create_sampler_view(ctx->m_pctx, m_colorTex, &svTempl);
if (!m_colorView) {
Log.report(logvisor::Fatal, fmt("Failed to create color sampler view"));
return;
}
svTempl.format = DepthFormat;
svTempl.texture = m_depthTex;
m_depthView = ctx->m_pctx->create_sampler_view(ctx->m_pctx, m_depthTex, &svTempl);
if (!m_depthView) {
Log.report(logvisor::Fatal, fmt("Failed to create depth sampler view"));
return;
}
svTempl.format = ColorFormat;
for (size_t i = 0; i < m_colorBindCount; ++i) {
svTempl.texture = m_colorBindTex[i];
m_colorBindView[i] = ctx->m_pctx->create_sampler_view(ctx->m_pctx, m_colorBindTex[i], &svTempl);
if (!m_colorBindView[i]) {
Log.report(logvisor::Fatal, fmt("Failed to create color bind sampler view"));
return;
}
}
svTempl.format = DepthFormat;
for (size_t i = 0; i < m_depthBindCount; ++i) {
svTempl.texture = m_depthBindTex[i];
m_depthBindView[i] = ctx->m_pctx->create_sampler_view(ctx->m_pctx, m_depthBindTex[i], &svTempl);
if (!m_depthBindView[i]) {
Log.report(logvisor::Fatal, fmt("Failed to create depth bind sampler view"));
return;
}
}
/* surfaces */
struct pipe_surface surfTempl = {};
surfTempl.format = ColorFormat;
m_colorSurface = ctx->m_pctx->create_surface(ctx->m_pctx, m_colorTex, &surfTempl);
if (!m_colorSurface) {
Log.report(logvisor::Fatal, fmt("Failed to create color surface"));
return;
}
surfTempl.format = DepthFormat;
m_depthSurface = ctx->m_pctx->create_surface(ctx->m_pctx, m_depthTex, &surfTempl);
if (!m_depthSurface) {
Log.report(logvisor::Fatal, fmt("Failed to create depth surface"));
return;
}
/* framebuffer */
m_framebuffer.width = uint16_t(m_width);
m_framebuffer.height = uint16_t(m_height);
m_framebuffer.nr_cbufs = 1;
m_framebuffer.cbufs[0] = m_colorSurface;
m_framebuffer.zsbuf = m_depthSurface;
}
NXTextureR(const boo::ObjToken<BaseGraphicsData>& parent, NXContext* ctx, size_t width, size_t height,
TextureClampMode clampMode, size_t colorBindCount, size_t depthBindCount)
: GraphicsDataNode<ITextureR>(parent), m_ctx(ctx) {
if (colorBindCount > MAX_BIND_TEXS)
Log.report(logvisor::Fatal, fmt("too many color bindings for render texture"));
if (depthBindCount > MAX_BIND_TEXS)
Log.report(logvisor::Fatal, fmt("too many depth bindings for render texture"));
if (m_samplesColor == 0)
m_samplesColor = 1;
if (m_samplesDepth == 0)
m_samplesDepth = 1;
setClampMode(clampMode);
Setup(ctx);
}
public:
struct pipe_resource* m_colorTex;
struct pipe_sampler_view* m_colorView = nullptr;
struct pipe_resource* m_depthTex;
struct pipe_sampler_view* m_depthView = nullptr;
struct pipe_resource* m_colorBindTex[MAX_BIND_TEXS] = {};
struct pipe_sampler_view* m_colorBindView[MAX_BIND_TEXS] = {};
struct pipe_resource* m_depthBindTex[MAX_BIND_TEXS] = {};
struct pipe_sampler_view* m_depthBindView[MAX_BIND_TEXS] = {};
struct pipe_surface* m_colorSurface = nullptr;
struct pipe_surface* m_depthSurface = nullptr;
void* m_sampler = nullptr;
struct pipe_framebuffer_state m_framebuffer = {};
void setClampMode(TextureClampMode mode) { MakeSampler(m_ctx, m_sampler, mode, 1); }
void doDestroy() {
pipe_resource_reference(&m_colorTex, nullptr);
pipe_sampler_view_reference(&m_colorView, nullptr);
pipe_resource_reference(&m_depthTex, nullptr);
pipe_sampler_view_reference(&m_depthView, nullptr);
for (size_t i = 0; i < MAX_BIND_TEXS; ++i) {
pipe_resource_reference(&m_colorBindTex[i], nullptr);
pipe_sampler_view_reference(&m_colorBindView[i], nullptr);
pipe_resource_reference(&m_depthBindTex[i], nullptr);
pipe_sampler_view_reference(&m_depthBindView[i], nullptr);
}
pipe_surface_reference(&m_colorSurface, nullptr);
pipe_surface_reference(&m_depthSurface, nullptr);
}
~NXTextureR() { doDestroy(); }
void resize(NXContext* ctx, size_t width, size_t height) {
if (width < 1)
width = 1;
if (height < 1)
height = 1;
m_width = width;
m_height = height;
Setup(ctx);
}
};
static const size_t SEMANTIC_SIZE_TABLE[] = {0, 12, 16, 12, 16, 16, 4, 8, 16, 16, 16};
static const pipe_format SEMANTIC_TYPE_TABLE[] = {PIPE_FORMAT_NONE,
PIPE_FORMAT_R32G32B32_FLOAT,
PIPE_FORMAT_R32G32B32A32_FLOAT,
PIPE_FORMAT_R32G32B32_FLOAT,
PIPE_FORMAT_R32G32B32A32_FLOAT,
PIPE_FORMAT_R32G32B32A32_FLOAT,
PIPE_FORMAT_R8G8B8A8_UNORM,
PIPE_FORMAT_R32G32_FLOAT,
PIPE_FORMAT_R32G32B32A32_FLOAT,
PIPE_FORMAT_R32G32B32A32_FLOAT,
PIPE_FORMAT_R32G32B32A32_FLOAT};
struct NXVertexFormat {
void* m_vtxElem;
size_t m_stride = 0;
size_t m_instStride = 0;
NXVertexFormat(NXContext* ctx, const VertexFormatInfo& info) {
std::unique_ptr<pipe_vertex_element[]> attributes(new pipe_vertex_element[info.elementCount]);
for (size_t i = 0; i < info.elementCount; ++i) {
const VertexElementDescriptor* elemin = &info.elements[i];
pipe_vertex_element& attribute = attributes[i];
int semantic = int(elemin->semantic & boo::VertexSemantic::SemanticMask);
attribute.src_format = SEMANTIC_TYPE_TABLE[semantic];
if ((elemin->semantic & boo::VertexSemantic::Instanced) != boo::VertexSemantic::None) {
attribute.vertex_buffer_index = 1;
attribute.instance_divisor = 1;
attribute.src_offset = m_instStride;
m_instStride += SEMANTIC_SIZE_TABLE[semantic];
} else {
attribute.vertex_buffer_index = 0;
attribute.instance_divisor = 0;
attribute.src_offset = m_stride;
m_stride += SEMANTIC_SIZE_TABLE[semantic];
}
}
uint64_t key = XXH64(attributes.get(), sizeof(pipe_vertex_element) * info.elementCount, 0);
auto search = ctx->m_vtxElemStates.find(key);
if (search != ctx->m_vtxElemStates.end()) {
m_vtxElem = search->second;
return;
}
m_vtxElem = ctx->m_pctx->create_vertex_elements_state(ctx->m_pctx, info.elementCount, attributes.get());
ctx->m_vtxElemStates[key] = m_vtxElem;
}
};
static const pipe_prim_type PRIMITIVE_TABLE[] = {PIPE_PRIM_TRIANGLES, PIPE_PRIM_TRIANGLE_STRIP, PIPE_PRIM_PATCHES};
static const nx_shader_stage SHADER_TYPE_TABLE[] = {
nx_shader_stage::NONE, nx_shader_stage::VERTEX, nx_shader_stage::FRAGMENT,
nx_shader_stage::GEOMETRY, nx_shader_stage::TESS_CTRL, nx_shader_stage::TESS_EVAL,
};
class NXShaderStage : public GraphicsDataNode<IShaderStage> {
friend class NXDataFactory;
nx_shader_stage_object m_obj;
NXShaderStage(const boo::ObjToken<BaseGraphicsData>& parent, NXContext* ctx, const uint8_t* data, size_t size,
PipelineStage stage)
: GraphicsDataNode<IShaderStage>(parent), m_obj(ctx->m_compiler.compile(SHADER_TYPE_TABLE[int(stage)], (char*)data)) {
if (!m_obj)
Log.report(logvisor::Fatal, fmt("Shader compile fail:\n%s\n"), m_obj.info_log());
}
public:
const nx_shader_stage_object* shader() const { return &m_obj; }
};
static const unsigned BLEND_FACTOR_TABLE[] = {
PIPE_BLENDFACTOR_ZERO, PIPE_BLENDFACTOR_ONE, PIPE_BLENDFACTOR_SRC_COLOR,
PIPE_BLENDFACTOR_INV_SRC_COLOR, PIPE_BLENDFACTOR_DST_COLOR, PIPE_BLENDFACTOR_INV_DST_COLOR,
PIPE_BLENDFACTOR_SRC_ALPHA, PIPE_BLENDFACTOR_INV_SRC_ALPHA, PIPE_BLENDFACTOR_DST_ALPHA,
PIPE_BLENDFACTOR_INV_DST_ALPHA, PIPE_BLENDFACTOR_SRC1_COLOR, PIPE_BLENDFACTOR_INV_SRC1_COLOR};
static void MakeBlendState(NXContext* ctx, void*& bsOut, const AdditionalPipelineInfo& info) {
uint32_t key = (uint32_t(info.srcFac) << 16) | (uint32_t(info.dstFac) << 3) | info.colorWrite << 2 |
info.alphaWrite << 1 | info.overwriteAlpha;
auto search = ctx->m_blendStates.find(key);
if (search != ctx->m_blendStates.end()) {
bsOut = search->second;
return;
}
pipe_blend_state bs = {};
bs.rt->blend_enable = info.srcFac != BlendFactor::One || info.dstFac != BlendFactor::Zero;
if (info.srcFac == BlendFactor::Subtract || info.dstFac == BlendFactor::Subtract) {
bs.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_SRC_ALPHA;
bs.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ONE;
bs.rt[0].rgb_func = PIPE_BLEND_REVERSE_SUBTRACT;
if (info.overwriteAlpha) {
bs.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE;
bs.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO;
bs.rt[0].alpha_func = PIPE_BLEND_ADD;
} else {
bs.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_SRC_ALPHA;
bs.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ONE;
bs.rt[0].alpha_func = PIPE_BLEND_REVERSE_SUBTRACT;
}
} else {
bs.rt[0].rgb_src_factor = BLEND_FACTOR_TABLE[int(info.srcFac)];
bs.rt[0].rgb_dst_factor = BLEND_FACTOR_TABLE[int(info.dstFac)];
bs.rt[0].rgb_func = PIPE_BLEND_ADD;
if (info.overwriteAlpha) {
bs.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE;
bs.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO;
} else {
bs.rt[0].alpha_src_factor = BLEND_FACTOR_TABLE[int(info.srcFac)];
bs.rt[0].alpha_dst_factor = BLEND_FACTOR_TABLE[int(info.dstFac)];
}
bs.rt[0].alpha_func = PIPE_BLEND_ADD;
}
bs.rt[0].colormask =
(info.colorWrite ? (PIPE_MASK_R | PIPE_MASK_G | PIPE_MASK_B) : 0) | (info.alphaWrite ? PIPE_MASK_A : 0);
bsOut = ctx->m_pctx->create_blend_state(ctx->m_pctx, &bs);
ctx->m_blendStates[key] = bsOut;
}
static void MakeRasterizerState(NXContext* ctx, void*& rasOut, const AdditionalPipelineInfo& info) {
uint32_t key = uint32_t(info.culling);
auto search = ctx->m_rasStates.find(key);
if (search != ctx->m_rasStates.end()) {
rasOut = search->second;
return;
}
pipe_rasterizer_state ras = {};
ras.clamp_fragment_color = 1;
ras.front_ccw = 1;
switch (info.culling) {
case CullMode::None:
default:
ras.cull_face = PIPE_FACE_NONE;
break;
case CullMode::Backface:
ras.cull_face = PIPE_FACE_BACK;
break;
case CullMode::Frontface:
ras.cull_face = PIPE_FACE_FRONT;
break;
}
ras.scissor = 1;
ras.multisample = unsigned(ctx->m_sampleCount > 1);
ras.half_pixel_center = 1;
ras.bottom_edge_rule = 1;
ras.depth_clip_near = 1;
ras.depth_clip_far = 1;
rasOut = ctx->m_pctx->create_rasterizer_state(ctx->m_pctx, &ras);
ctx->m_rasStates[key] = rasOut;
}
static void MakeDepthStencilState(NXContext* ctx, void*& dsOut, const AdditionalPipelineInfo& info) {
uint32_t key = (uint32_t(info.depthTest) << 16) | info.depthWrite;
auto search = ctx->m_dsStates.find(key);
if (search != ctx->m_dsStates.end()) {
dsOut = search->second;
return;
}
pipe_depth_stencil_alpha_state ds = {};
ds.depth.enabled = info.depthTest != ZTest::None;
ds.depth.writemask = info.depthWrite;
switch (info.depthTest) {
case ZTest::None:
default:
ds.depth.func = PIPE_FUNC_ALWAYS;
break;
case ZTest::LEqual:
ds.depth.func = PIPE_FUNC_LEQUAL;
break;
case ZTest::Greater:
ds.depth.func = PIPE_FUNC_GREATER;
break;
case ZTest::Equal:
ds.depth.func = PIPE_FUNC_EQUAL;
break;
case ZTest::GEqual:
ds.depth.func = PIPE_FUNC_GEQUAL;
break;
}
dsOut = ctx->m_pctx->create_depth_stencil_alpha_state(ctx->m_pctx, &ds);
ctx->m_dsStates[key] = dsOut;
}
class NXShaderPipeline : public GraphicsDataNode<IShaderPipeline> {
protected:
friend class NXDataFactory;
friend struct NXShaderDataBinding;
NXVertexFormat m_vtxFmt;
nx_linked_shader m_shader;
Primitive m_prim;
pipe_prim_type m_nxPrim;
uint32_t m_patchSize;
void* m_blendState;
void* m_rasState;
void* m_dsState;
NXShaderPipeline(const boo::ObjToken<BaseGraphicsData>& parent, NXContext* ctx, ObjToken<IShaderStage> vertex,
ObjToken<IShaderStage> fragment, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& info)
: GraphicsDataNode<IShaderPipeline>(parent), m_vtxFmt(ctx, vtxFmt), m_prim(info.prim), m_patchSize(info.patchSize) {
m_nxPrim = PRIMITIVE_TABLE[int(m_prim)];
MakeBlendState(ctx, m_blendState, info);
MakeRasterizerState(ctx, m_rasState, info);
MakeDepthStencilState(ctx, m_dsState, info);
const nx_shader_stage_object* stages[5];
unsigned numStages = 0;
if (vertex)
stages[numStages++] = vertex.cast<NXShaderStage>()->shader();
if (control)
stages[numStages++] = control.cast<NXShaderStage>()->shader();
if (evaluation)
stages[numStages++] = evaluation.cast<NXShaderStage>()->shader();
if (geometry)
stages[numStages++] = geometry.cast<NXShaderStage>()->shader();
if (fragment)
stages[numStages++] = fragment.cast<NXShaderStage>()->shader();
std::string infoLog;
m_shader = ctx->m_compiler.link(numStages, stages, &infoLog);
if (!m_shader)
Log.report(logvisor::Fatal, fmt("Unable to link shader:\n%s\n"), infoLog.c_str());
}
public:
NXShaderPipeline& operator=(const NXShaderPipeline&) = delete;
NXShaderPipeline(const NXShaderPipeline&) = delete;
void bind(struct pipe_context* pctx) const {
const struct gl_shader_program* prog = m_shader.program();
if (gl_linked_shader* fs = prog->_LinkedShaders[MESA_SHADER_FRAGMENT]) {
struct st_fragment_program* p = (struct st_fragment_program*)fs->Program;
pctx->bind_fs_state(pctx, p->variants->driver_shader);
}
if (gl_linked_shader* gs = prog->_LinkedShaders[MESA_SHADER_GEOMETRY]) {
struct st_common_program* p = (struct st_common_program*)gs->Program;
pctx->bind_gs_state(pctx, p->variants->driver_shader);
}
if (gl_linked_shader* tes = prog->_LinkedShaders[MESA_SHADER_TESS_EVAL]) {
struct st_common_program* p = (struct st_common_program*)tes->Program;
pctx->bind_tes_state(pctx, p->variants->driver_shader);
}
if (gl_linked_shader* tcs = prog->_LinkedShaders[MESA_SHADER_TESS_CTRL]) {
struct st_common_program* p = (struct st_common_program*)tcs->Program;
pctx->bind_tcs_state(pctx, p->variants->driver_shader);
}
if (gl_linked_shader* vs = prog->_LinkedShaders[MESA_SHADER_VERTEX]) {
struct st_vertex_program* p = (struct st_vertex_program*)vs->Program;
pctx->bind_vs_state(pctx, p->variants->driver_shader);
}
pctx->bind_blend_state(pctx, m_blendState);
pctx->bind_rasterizer_state(pctx, m_rasState);
pctx->bind_depth_stencil_alpha_state(pctx, m_dsState);
}
};
static const nx_buffer_info* GetBufferGPUResource(const IGraphicsBuffer* buf, int idx) {
if (buf->dynamic()) {
const NXGraphicsBufferD<BaseGraphicsData>* cbuf = static_cast<const NXGraphicsBufferD<BaseGraphicsData>*>(buf);
return &cbuf->m_bufferInfo[idx];
} else {
const NXGraphicsBufferS* cbuf = static_cast<const NXGraphicsBufferS*>(buf);
return &cbuf->m_bufferInfo;
}
}
static const struct pipe_sampler_view* GetTextureGPUResource(const ITexture* tex, int idx, int bindIdx, bool depth,
void*& samplerOut) {
switch (tex->type()) {
case TextureType::Dynamic: {
const NXTextureD* ctex = static_cast<const NXTextureD*>(tex);
samplerOut = ctex->m_sampler;
return ctex->m_gpuView[idx];
}
case TextureType::Static: {
const NXTextureS* ctex = static_cast<const NXTextureS*>(tex);
samplerOut = ctex->m_sampler;
return ctex->m_gpuView;
}
case TextureType::StaticArray: {
const NXTextureSA* ctex = static_cast<const NXTextureSA*>(tex);
samplerOut = ctex->m_sampler;
return ctex->m_gpuView;
}
case TextureType::Render: {
const NXTextureR* ctex = static_cast<const NXTextureR*>(tex);
samplerOut = ctex->m_sampler;
return depth ? ctex->m_depthBindView[bindIdx] : ctex->m_colorBindView[bindIdx];
}
default:
break;
}
return nullptr;
}
struct NXShaderDataBinding : GraphicsDataNode<IShaderDataBinding> {
NXContext* m_ctx;
boo::ObjToken<IShaderPipeline> m_pipeline;
boo::ObjToken<IGraphicsBuffer> m_vbuf;
boo::ObjToken<IGraphicsBuffer> m_instVbuf;
boo::ObjToken<IGraphicsBuffer> m_ibuf;
std::vector<boo::ObjToken<IGraphicsBuffer>> m_ubufs;
std::vector<std::array<size_t, 2>> m_ubufOffs;
struct BindTex {
boo::ObjToken<ITexture> tex;
int idx;
bool depth;
};
std::vector<BindTex> m_texs;
struct pipe_vertex_buffer m_vboBufs[2][2] = {{}, {}};
std::vector<std::array<struct pipe_constant_buffer, 2>> m_ubufBinds[MESA_SHADER_STAGES];
size_t m_vertOffset;
size_t m_instOffset;
#ifndef NDEBUG
/* Debugging aids */
bool m_committed = false;
#endif
NXShaderDataBinding(const boo::ObjToken<BaseGraphicsData>& d, NXDataFactoryImpl& factory,
const boo::ObjToken<IShaderPipeline>& pipeline, const boo::ObjToken<IGraphicsBuffer>& vbuf,
const boo::ObjToken<IGraphicsBuffer>& instVbuf, const boo::ObjToken<IGraphicsBuffer>& ibuf,
size_t ubufCount, const boo::ObjToken<IGraphicsBuffer>* ubufs, const size_t* ubufOffs,
const size_t* ubufSizes, size_t texCount, const boo::ObjToken<ITexture>* texs,
const int* bindIdxs, const bool* depthBinds, size_t baseVert, size_t baseInst)
: GraphicsDataNode<IShaderDataBinding>(d)
, m_ctx(factory.m_ctx)
, m_pipeline(pipeline)
, m_vbuf(vbuf)
, m_instVbuf(instVbuf)
, m_ibuf(ibuf) {
NXShaderPipeline* cpipeline = m_pipeline.cast<NXShaderPipeline>();
NXVertexFormat& vtxFmt = cpipeline->m_vtxFmt;
m_vertOffset = baseVert * vtxFmt.m_stride;
m_instOffset = baseInst * vtxFmt.m_instStride;
m_ubufs.reserve(ubufCount);
if (ubufOffs && ubufSizes)
m_ubufOffs.reserve(ubufCount);
for (size_t i = 0; i < ubufCount; ++i) {
#ifndef NDEBUG
if (!ubufs[i])
Log.report(logvisor::Fatal, fmt("null uniform-buffer %d provided to newShaderDataBinding"), int(i));
#endif
m_ubufs.push_back(ubufs[i]);
if (ubufOffs && ubufSizes)
m_ubufOffs.push_back({ubufOffs[i], ubufSizes[i]});
}
m_texs.reserve(texCount);
for (size_t i = 0; i < texCount; ++i) {
m_texs.push_back({texs[i], bindIdxs ? bindIdxs[i] : 0, depthBinds ? depthBinds[i] : false});
}
}
void commit() {
struct pipe_context* pctx = m_ctx->m_pctx;
for (int i = 0; i < 2; ++i) {
if (m_vbuf) {
m_vboBufs[i][0] = GetBufferGPUResource(m_vbuf.get(), i)->v;
m_vboBufs[i][0].buffer_offset += m_vertOffset;
}
if (m_instVbuf) {
m_vboBufs[i][1] = GetBufferGPUResource(m_instVbuf.get(), i)->v;
m_vboBufs[i][1].buffer_offset += m_instOffset;
}
}
NXShaderPipeline* cpipeline = m_pipeline.cast<NXShaderPipeline>();
const struct gl_shader_program* program = cpipeline->m_shader.program();
for (uint i = 0; i < MESA_SHADER_STAGES; ++i) {
if (const struct gl_linked_shader* lsh = program->_LinkedShaders[i]) {
std::vector<std::array<struct pipe_constant_buffer, 2>>& bindings = m_ubufBinds[i];
const struct gl_shader_program_data* data = lsh->Program->sh.data;
bindings.reserve(data->NumUniformBlocks);
for (uint j = 0; j < data->NumUniformBlocks; ++j) {
const struct gl_uniform_block* block = &data->UniformBlocks[j];
assert(block->Binding < m_ubufs.size() && "Uniform binding oob");
bindings.emplace_back();
for (int k = 0; k < 2; ++k) {
struct pipe_constant_buffer& bufBind = bindings.back()[k];
const nx_buffer_info* buf = GetBufferGPUResource(m_ubufs[block->Binding].get(), k);
bufBind = buf->c;
if (!m_ubufOffs.empty()) {
bufBind.buffer_offset += m_ubufOffs[block->Binding][0];
bufBind.buffer_size = unsigned(m_ubufOffs[block->Binding][1]);
}
}
}
}
}
#ifndef NDEBUG
m_committed = true;
#endif
}
void bind(int b) {
#ifndef NDEBUG
if (!m_committed)
Log.report(logvisor::Fatal, fmt("attempted to use uncommitted NXShaderDataBinding"));
#endif
struct pipe_context* pctx = m_ctx->m_pctx;
NXShaderPipeline* pipeline = m_pipeline.cast<NXShaderPipeline>();
pipeline->bind(pctx);
const struct gl_shader_program* program = pipeline->m_shader.program();
for (uint i = 0; i < MESA_SHADER_STAGES; ++i) {
uint j = 0;
for (const auto& bind : m_ubufBinds[i])
pctx->set_constant_buffer(pctx, pipe_shader_type(i), j++, &bind[b]);
if (const struct gl_linked_shader* lsh = program->_LinkedShaders[i]) {
void* samplers[BOO_GLSL_MAX_TEXTURE_COUNT] = {};
struct pipe_sampler_view* samplerViews[BOO_GLSL_MAX_TEXTURE_COUNT] = {};
unsigned numSamplers = 0;
const struct gl_program* stprogram = lsh->Program;
for (int t = 0; t < BOO_GLSL_MAX_TEXTURE_COUNT; ++t) {
if (stprogram->SamplersUsed & (1 << t)) {
GLubyte unit = GLubyte(stprogram->SamplerUnits[t] - BOO_GLSL_MAX_UNIFORM_COUNT);
assert(unit < m_texs.size() && "Texture binding oob");
const BindTex& tex = m_texs[unit];
samplerViews[numSamplers] =
(pipe_sampler_view*)GetTextureGPUResource(tex.tex.get(), t, tex.idx, tex.depth, samplers[numSamplers]);
++numSamplers;
}
}
pctx->bind_sampler_states(pctx, pipe_shader_type(i), 0, numSamplers, samplers);
pctx->set_sampler_views(pctx, pipe_shader_type(i), 0, numSamplers, samplerViews);
}
}
if (m_vbuf && m_instVbuf)
pctx->set_vertex_buffers(pctx, 0, 2, m_vboBufs[b]);
else if (m_vbuf)
pctx->set_vertex_buffers(pctx, 0, 1, m_vboBufs[b]);
else if (m_instVbuf)
pctx->set_vertex_buffers(pctx, 1, 1, &m_vboBufs[b][1]);
}
pipe_prim_type getPrimitive() const { return m_pipeline.cast<NXShaderPipeline>()->m_nxPrim; }
uint32_t getPatchVerts() const { return m_pipeline.cast<NXShaderPipeline>()->m_patchSize; }
struct pipe_resource* getIndexBuf(int b) const {
return GetBufferGPUResource(m_ibuf.get(), b)->v.buffer.resource;
}
};
struct NXCommandQueue : IGraphicsCommandQueue {
Platform platform() const { return IGraphicsDataFactory::Platform::Vulkan; }
const SystemChar* platformName() const { return _SYS_STR("NX"); }
NXContext* m_ctx;
IGraphicsContext* m_parent;
bool m_running = true;
int m_fillBuf = 0;
int m_drawBuf = 0;
std::vector<boo::ObjToken<boo::IObj>> m_drawResTokens[2];
NXCommandQueue(NXContext* ctx, IGraphicsContext* parent) : m_ctx(ctx), m_parent(parent) {}
void startRenderer() { static_cast<NXDataFactoryImpl*>(m_parent->getDataFactory())->SetupGammaResources(); }
void stopRenderer() {
m_running = false;
static_cast<NXDataFactoryImpl*>(m_parent->getDataFactory())->DestroyGammaResources();
m_drawResTokens[0].clear();
m_drawResTokens[1].clear();
m_boundTarget.reset();
m_resolveDispSource.reset();
}
~NXCommandQueue() {
if (m_running)
stopRenderer();
}
boo::ObjToken<IShaderDataBinding> m_curSDBinding;
void setShaderDataBinding(const boo::ObjToken<IShaderDataBinding>& binding) {
m_curSDBinding = binding;
NXShaderDataBinding* cbind = binding.cast<NXShaderDataBinding>();
cbind->bind(m_fillBuf);
m_drawResTokens[m_fillBuf].push_back(binding.get());
}
boo::ObjToken<ITextureR> m_boundTarget;
void setRenderTarget(const boo::ObjToken<ITextureR>& target) {
NXTextureR* ctarget = target.cast<NXTextureR>();
if (m_boundTarget.get() != ctarget) {
m_boundTarget = target;
m_drawResTokens[m_fillBuf].push_back(target.get());
}
m_ctx->m_pctx->set_framebuffer_state(m_ctx->m_pctx, &ctarget->m_framebuffer);
}
void setViewport(const SWindowRect& rect, float znear, float zfar) {
if (m_boundTarget) {
NXTextureR* ctarget = m_boundTarget.cast<NXTextureR>();
struct gl_context* ctx = m_ctx->m_st->ctx;
ctx->ViewportArray[0].X = float(rect.location[0]);
ctx->ViewportArray[0].Y = float(std::max(0, int(ctarget->m_height) - rect.location[1] - rect.size[1]));
ctx->ViewportArray[0].Width = float(rect.size[0]);
ctx->ViewportArray[0].Height = float(rect.size[1]);
ctx->ViewportArray[0].Near = znear;
ctx->ViewportArray[0].Far = zfar;
pipe_viewport_state vp = {};
_mesa_get_viewport_xform(ctx, 0, vp.scale, vp.translate);
m_ctx->m_pctx->set_viewport_states(m_ctx->m_pctx, 0, 1, &vp);
}
}
void setScissor(const SWindowRect& rect) {
if (m_boundTarget) {
NXTextureR* ctarget = m_boundTarget.cast<NXTextureR>();
pipe_scissor_state scissor = {};
scissor.minx = unsigned(rect.location[0]);
scissor.miny = unsigned(std::max(0, int(ctarget->m_height) - rect.location[1] - rect.size[1]));
scissor.maxx = scissor.minx + unsigned(rect.size[0]);
scissor.maxy = scissor.miny + unsigned(rect.size[1]);
m_ctx->m_pctx->set_scissor_states(m_ctx->m_pctx, 0, 1, &scissor);
}
}
std::unordered_map<NXTextureR*, std::pair<size_t, size_t>> m_texResizes;
void resizeRenderTexture(const boo::ObjToken<ITextureR>& tex, size_t width, size_t height) {
NXTextureR* ctex = tex.cast<NXTextureR>();
m_texResizes[ctex] = std::make_pair(width, height);
m_drawResTokens[m_fillBuf].push_back(tex.get());
}
void schedulePostFrameHandler(std::function<void(void)>&& func) { func(); }
float m_clearColor[4] = {0.0, 0.0, 0.0, 0.0};
void setClearColor(const float rgba[4]) {
m_clearColor[0] = rgba[0];
m_clearColor[1] = rgba[1];
m_clearColor[2] = rgba[2];
m_clearColor[3] = rgba[3];
}
void clearTarget(bool render = true, bool depth = true) {
if (!m_boundTarget)
return;
unsigned buffers = 0;
if (render)
buffers |= PIPE_CLEAR_COLOR0;
if (depth)
buffers |= PIPE_CLEAR_DEPTH;
pipe_color_union cunion;
for (int i = 0; i < 4; ++i)
cunion.f[i] = m_clearColor[i];
m_ctx->m_pctx->clear(m_ctx->m_pctx, buffers, &cunion, 1.f, 0);
}
void draw(size_t start, size_t count) {
pipe_draw_info info = {};
NXShaderDataBinding* sdBinding = m_curSDBinding.cast<NXShaderDataBinding>();
info.mode = sdBinding->getPrimitive();
info.vertices_per_patch = sdBinding->getPatchVerts();
info.start = start;
info.count = count;
m_ctx->m_pctx->draw_vbo(m_ctx->m_pctx, &info);
}
void drawIndexed(size_t start, size_t count) {
pipe_draw_info info = {};
NXShaderDataBinding* sdBinding = m_curSDBinding.cast<NXShaderDataBinding>();
info.index_size = 4;
info.mode = sdBinding->getPrimitive();
info.primitive_restart = 1;
info.vertices_per_patch = sdBinding->getPatchVerts();
info.start = start;
info.count = count;
info.restart_index = 0xffffffff;
info.index.resource = sdBinding->getIndexBuf(m_fillBuf);
m_ctx->m_pctx->draw_vbo(m_ctx->m_pctx, &info);
}
void drawInstances(size_t start, size_t count, size_t instCount) {
pipe_draw_info info = {};
NXShaderDataBinding* sdBinding = m_curSDBinding.cast<NXShaderDataBinding>();
info.mode = sdBinding->getPrimitive();
info.vertices_per_patch = sdBinding->getPatchVerts();
info.start = start;
info.count = count;
info.instance_count = instCount;
m_ctx->m_pctx->draw_vbo(m_ctx->m_pctx, &info);
}
void drawInstancesIndexed(size_t start, size_t count, size_t instCount) {
pipe_draw_info info = {};
NXShaderDataBinding* sdBinding = m_curSDBinding.cast<NXShaderDataBinding>();
info.index_size = 4;
info.mode = sdBinding->getPrimitive();
info.primitive_restart = 1;
info.vertices_per_patch = sdBinding->getPatchVerts();
info.start = start;
info.count = count;
info.instance_count = instCount;
info.restart_index = 0xffffffff;
info.index.resource = sdBinding->getIndexBuf(m_fillBuf);
m_ctx->m_pctx->draw_vbo(m_ctx->m_pctx, &info);
}
boo::ObjToken<ITextureR> m_resolveDispSource;
void resolveDisplay(const boo::ObjToken<ITextureR>& source) { m_resolveDispSource = source; }
bool _resolveDisplay() {
if (!m_resolveDispSource)
return false;
NXTextureR* csource = m_resolveDispSource.cast<NXTextureR>();
#ifndef NDEBUG
if (!csource->m_colorBindCount)
Log.report(logvisor::Fatal, fmt("texture provided to resolveDisplay() must have at least 1 color binding"));
#endif
struct pipe_surface* backBuf = m_ctx->m_windowSurfaces[ST_ATTACHMENT_BACK_LEFT];
NXDataFactoryImpl* dataFactory = static_cast<NXDataFactoryImpl*>(m_parent->getDataFactory());
if (dataFactory->m_gamma != 1.f) {
SWindowRect rect(0, 0, csource->m_width, csource->m_height);
_resolveBindTexture(csource, rect, true, 0, true, false);
NXShaderDataBinding* gammaBinding = dataFactory->m_gammaBinding.cast<NXShaderDataBinding>();
pipe_framebuffer_state fstate = {};
fstate.width = backBuf->texture->width0;
fstate.height = backBuf->texture->height0;
fstate.nr_cbufs = 1;
fstate.cbufs[0] = backBuf;
m_ctx->m_pctx->set_framebuffer_state(m_ctx->m_pctx, &fstate);
gammaBinding->m_texs[0].tex = m_resolveDispSource.get();
gammaBinding->bind(m_drawBuf);
pipe_draw_info info = {};
info.mode = PIPE_PRIM_TRIANGLE_STRIP;
info.start = 0;
info.count = 4;
info.instance_count = 1;
m_ctx->m_pctx->draw_vbo(m_ctx->m_pctx, &info);
gammaBinding->m_texs[0].tex.reset();
} else {
pipe_blit_info binfo = {};
binfo.src.resource = csource->m_colorTex;
binfo.dst.resource = backBuf->texture;
u_box_2d(0, 0, csource->m_width, csource->m_height, &binfo.src.box);
binfo.dst.box = binfo.src.box;
binfo.src.format = binfo.dst.format = PIPE_FORMAT_R8G8B8A8_UNORM;
binfo.mask = PIPE_MASK_RGBA;
binfo.filter = PIPE_TEX_FILTER_NEAREST;
m_ctx->m_pctx->blit(m_ctx->m_pctx, &binfo);
}
m_resolveDispSource.reset();
return true;
}
void _resolveBindTexture(NXTextureR* ctexture, const SWindowRect& rect, bool tlOrigin, int bindIdx, bool color,
bool depth) {
SWindowRect intersectRect = rect.intersect(SWindowRect(0, 0, ctexture->m_width, ctexture->m_height));
if (color && ctexture->m_colorBindCount) {
pipe_blit_info binfo = {};
binfo.src.resource = ctexture->m_colorTex;
binfo.dst.resource = ctexture->m_colorBindTex[bindIdx];
u_box_2d(intersectRect.location[0],
tlOrigin ? intersectRect.location[1]
: (ctexture->m_height - intersectRect.size[1] - intersectRect.location[1]),
intersectRect.size[0], intersectRect.size[1], &binfo.src.box);
binfo.dst.box = binfo.src.box;
binfo.src.format = binfo.dst.format = PIPE_FORMAT_R8G8B8A8_UNORM;
binfo.mask = PIPE_MASK_RGBA;
binfo.filter = PIPE_TEX_FILTER_NEAREST;
m_ctx->m_pctx->blit(m_ctx->m_pctx, &binfo);
}
if (depth && ctexture->m_depthBindCount) {
pipe_blit_info binfo = {};
binfo.src.resource = ctexture->m_depthTex;
binfo.dst.resource = ctexture->m_depthBindTex[bindIdx];
u_box_2d(intersectRect.location[0],
tlOrigin ? intersectRect.location[1]
: (ctexture->m_height - intersectRect.size[1] - intersectRect.location[1]),
intersectRect.size[0], intersectRect.size[1], &binfo.src.box);
binfo.dst.box = binfo.src.box;
binfo.src.format = binfo.dst.format = PIPE_FORMAT_Z32_FLOAT;
binfo.mask = PIPE_MASK_Z;
binfo.filter = PIPE_TEX_FILTER_NEAREST;
m_ctx->m_pctx->blit(m_ctx->m_pctx, &binfo);
}
}
void resolveBindTexture(const boo::ObjToken<ITextureR>& texture, const SWindowRect& rect, bool tlOrigin, int bindIdx,
bool color, bool depth, bool clearDepth) {
NXTextureR* ctexture = texture.cast<NXTextureR>();
_resolveBindTexture(ctexture, rect, tlOrigin, bindIdx, color, depth);
if (clearDepth)
m_ctx->m_pctx->clear(m_ctx->m_pctx, PIPE_CLEAR_DEPTH, nullptr, 1.f, 0);
}
void execute();
};
NXDataFactory::Context::Context(NXDataFactory& parent __BooTraceArgs)
: m_parent(parent), m_data(new NXData(static_cast<NXDataFactoryImpl&>(parent) __BooTraceArgsUse)) {}
NXDataFactory::Context::~Context() {}
boo::ObjToken<IGraphicsBufferS> NXDataFactory::Context::newStaticBuffer(BufferUse use, const void* data, size_t stride,
size_t count) {
NXDataFactoryImpl& factory = static_cast<NXDataFactoryImpl&>(m_parent);
return {new NXGraphicsBufferS(m_data, use, factory.m_ctx, data, stride, count)};
}
boo::ObjToken<IGraphicsBufferD> NXDataFactory::Context::newDynamicBuffer(BufferUse use, size_t stride, size_t count) {
NXDataFactoryImpl& factory = static_cast<NXDataFactoryImpl&>(m_parent);
return {new NXGraphicsBufferD<BaseGraphicsData>(m_data, use, factory.m_ctx, stride, count)};
}
boo::ObjToken<ITextureS> NXDataFactory::Context::newStaticTexture(size_t width, size_t height, size_t mips,
TextureFormat fmt, TextureClampMode clampMode,
const void* data, size_t sz) {
NXDataFactoryImpl& factory = static_cast<NXDataFactoryImpl&>(m_parent);
return {new NXTextureS(m_data, factory.m_ctx, width, height, mips, fmt, clampMode, data, sz)};
}
boo::ObjToken<ITextureSA> NXDataFactory::Context::newStaticArrayTexture(size_t width, size_t height, size_t layers,
size_t mips, TextureFormat fmt,
TextureClampMode clampMode, const void* data,
size_t sz) {
NXDataFactoryImpl& factory = static_cast<NXDataFactoryImpl&>(m_parent);
return {new NXTextureSA(m_data, factory.m_ctx, width, height, layers, mips, fmt, clampMode, data, sz)};
}
boo::ObjToken<ITextureD> NXDataFactory::Context::newDynamicTexture(size_t width, size_t height, TextureFormat fmt,
TextureClampMode clampMode) {
NXDataFactoryImpl& factory = static_cast<NXDataFactoryImpl&>(m_parent);
NXCommandQueue* q = static_cast<NXCommandQueue*>(factory.m_parent->getCommandQueue());
return {new NXTextureD(m_data, q, width, height, fmt, clampMode)};
}
boo::ObjToken<ITextureR> NXDataFactory::Context::newRenderTexture(size_t width, size_t height,
TextureClampMode clampMode, size_t colorBindCount,
size_t depthBindCount) {
NXDataFactoryImpl& factory = static_cast<NXDataFactoryImpl&>(m_parent);
return {new NXTextureR(m_data, factory.m_ctx, width, height, clampMode, colorBindCount, depthBindCount)};
}
ObjToken<IShaderStage> NXDataFactory::Context::newShaderStage(const uint8_t* data, size_t size, PipelineStage stage) {
NXDataFactoryImpl& factory = static_cast<NXDataFactoryImpl&>(m_parent);
return {new NXShaderStage(m_data, factory.m_ctx, data, size, stage)};
}
ObjToken<IShaderPipeline> NXDataFactory::Context::newShaderPipeline(
ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment, ObjToken<IShaderStage> geometry,
ObjToken<IShaderStage> control, ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& info) {
NXDataFactoryImpl& factory = static_cast<NXDataFactoryImpl&>(m_parent);
return {new NXShaderPipeline(m_data, factory.m_ctx, vertex, fragment, geometry, control, evaluation, vtxFmt, info)};
}
boo::ObjToken<IShaderDataBinding> NXDataFactory::Context::newShaderDataBinding(
const boo::ObjToken<IShaderPipeline>& pipeline, const boo::ObjToken<IGraphicsBuffer>& vbo,
const boo::ObjToken<IGraphicsBuffer>& instVbo, const boo::ObjToken<IGraphicsBuffer>& ibo, size_t ubufCount,
const boo::ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages, const size_t* ubufOffs,
const size_t* ubufSizes, size_t texCount, const boo::ObjToken<ITexture>* texs, const int* bindIdxs,
const bool* bindDepth, size_t baseVert, size_t baseInst) {
NXDataFactoryImpl& factory = static_cast<NXDataFactoryImpl&>(m_parent);
return {new NXShaderDataBinding(m_data, factory, pipeline, vbo, instVbo, ibo, ubufCount, ubufs, ubufOffs, ubufSizes,
texCount, texs, bindIdxs, bindDepth, baseVert, baseInst)};
}
NXTextureD::NXTextureD(const boo::ObjToken<BaseGraphicsData>& parent, NXCommandQueue* q, size_t width, size_t height,
TextureFormat fmt, TextureClampMode clampMode)
: GraphicsDataNode<ITextureD>(parent), m_q(q), m_width(width), m_height(height), m_fmt(fmt), m_clampMode(clampMode) {
NXContext* ctx = m_q->m_ctx;
pipe_format pfmt;
switch (fmt) {
case TextureFormat::RGBA8:
pfmt = PIPE_FORMAT_R8G8B8A8_UNORM;
m_cpuSz = width * height * 4;
break;
case TextureFormat::I8:
pfmt = PIPE_FORMAT_R8_UNORM;
m_cpuSz = width * height;
break;
case TextureFormat::I16:
pfmt = PIPE_FORMAT_R16_UNORM;
m_cpuSz = width * height * 2;
break;
default:
Log.report(logvisor::Fatal, fmt("unsupported tex format"));
}
m_nxFmt = pfmt;
m_stagingBuf.reset(new uint8_t[m_cpuSz]);
struct pipe_resource texTempl = {};
texTempl.target = PIPE_TEXTURE_2D;
texTempl.format = m_nxFmt;
texTempl.width0 = width;
texTempl.height0 = height;
texTempl.depth0 = 1;
texTempl.array_size = 1;
texTempl.bind = PIPE_BIND_SAMPLER_VIEW;
for (int i = 0; i < 2; ++i) {
m_gpuTex[i] = ctx->m_screen->resource_create(ctx->m_screen, &texTempl);
if (!m_gpuTex[i]) {
Log.report(logvisor::Fatal, fmt("Failed to create texture"));
return;
}
}
struct pipe_sampler_view svTempl = {};
svTempl.format = m_nxFmt;
svTempl.swizzle_r = PIPE_SWIZZLE_X;
svTempl.swizzle_g = PIPE_SWIZZLE_Y;
svTempl.swizzle_b = PIPE_SWIZZLE_Z;
svTempl.swizzle_a = PIPE_SWIZZLE_W;
for (int i = 0; i < 2; ++i) {
svTempl.texture = m_gpuTex[i];
m_gpuView[i] = ctx->m_pctx->create_sampler_view(ctx->m_pctx, m_gpuTex[i], &svTempl);
}
}
void NXTextureD::update(int b) {
int slot = 1 << b;
if ((slot & m_validSlots) == 0) {
NXContext* ctx = m_q->m_ctx;
uint blockSize = util_format_get_blocksize(m_nxFmt);
uint8_t* ptr = m_stagingBuf.get();
size_t rowStride = m_width * blockSize;
size_t imageBytes = rowStride * m_height;
struct pipe_box box;
u_box_2d(0, 0, m_width, m_height, &box);
ctx->m_pctx->texture_subdata(ctx->m_pctx, m_gpuTex[m_q->m_fillBuf], 0, PIPE_TRANSFER_WRITE, &box, ptr, rowStride,
imageBytes);
m_validSlots |= slot;
}
}
void NXTextureD::setClampMode(TextureClampMode mode) {
m_clampMode = mode;
MakeSampler(m_q->m_ctx, m_sampler, mode, 1);
}
void NXTextureD::load(const void* data, size_t sz) {
size_t bufSz = std::min(sz, m_cpuSz);
memmove(m_stagingBuf.get(), data, bufSz);
m_validSlots = 0;
}
void* NXTextureD::map(size_t sz) {
if (sz > m_cpuSz)
return nullptr;
return m_stagingBuf.get();
}
void NXTextureD::unmap() { m_validSlots = 0; }
static inline struct pipe_resource* pipe_buffer_create_flags(struct pipe_screen* screen, unsigned bind,
enum pipe_resource_usage usage, unsigned flags,
unsigned size) {
struct pipe_resource buffer;
memset(&buffer, 0, sizeof buffer);
buffer.target = PIPE_BUFFER;
buffer.format = PIPE_FORMAT_R8_UNORM; /* want TYPELESS or similar */
buffer.bind = bind;
buffer.usage = usage;
buffer.flags = flags;
buffer.width0 = size;
buffer.height0 = 1;
buffer.depth0 = 1;
buffer.array_size = 1;
return screen->resource_create(screen, &buffer);
}
void NXDataFactoryImpl::commitTransaction(
const std::function<bool(IGraphicsDataFactory::Context&)>& trans __BooTraceArgs) {
Context ctx(*this __BooTraceArgsUse);
if (!trans(ctx))
return;
NXData* data = ctx.m_data.cast<NXData>();
/* size up resources */
unsigned constantMemSizes[3] = {};
if (data->m_SBufs)
for (IGraphicsBufferS& buf : *data->m_SBufs) {
auto& cbuf = static_cast<NXGraphicsBufferS&>(buf);
if (cbuf.m_use == BufferUse::Null)
continue;
unsigned& sz = constantMemSizes[int(cbuf.m_use) - 1];
sz = cbuf.sizeForGPU(m_ctx, sz);
}
if (data->m_DBufs)
for (IGraphicsBufferD& buf : *data->m_DBufs) {
auto& cbuf = static_cast<NXGraphicsBufferD<BaseGraphicsData>&>(buf);
if (cbuf.m_use == BufferUse::Null)
continue;
unsigned& sz = constantMemSizes[int(cbuf.m_use) - 1];
sz = cbuf.sizeForGPU(m_ctx, sz);
}
/* allocate memory and place buffers */
for (int i = 0; i < 3; ++i) {
if (constantMemSizes[i]) {
struct pipe_resource* poolBuf = pipe_buffer_create_flags(
m_ctx->m_screen, USE_TABLE[i + 1], PIPE_USAGE_DEFAULT,
PIPE_RESOURCE_FLAG_MAP_PERSISTENT | PIPE_RESOURCE_FLAG_MAP_COHERENT, constantMemSizes[i]);
data->m_constantBuffers[i] = poolBuf;
pipe_transfer* xfer;
uint8_t* mappedData = (uint8_t*)pipe_buffer_map(
m_ctx->m_pctx, poolBuf,
PIPE_TRANSFER_WRITE | PIPE_TRANSFER_MAP_DIRECTLY | PIPE_TRANSFER_PERSISTENT | PIPE_TRANSFER_COHERENT, &xfer);
if (data->m_SBufs)
for (IGraphicsBufferS& buf : *data->m_SBufs) {
auto& cbuf = static_cast<NXGraphicsBufferS&>(buf);
if (int(cbuf.m_use) - 1 != i)
continue;
cbuf.placeForGPU(poolBuf, mappedData);
}
if (data->m_DBufs)
for (IGraphicsBufferD& buf : *data->m_DBufs) {
auto& cbuf = static_cast<NXGraphicsBufferD<BaseGraphicsData>&>(buf);
if (int(cbuf.m_use) - 1 != i)
continue;
cbuf.placeForGPU(poolBuf, mappedData);
}
}
}
/* Commit data bindings (create descriptor sets) */
if (data->m_SBinds)
for (IShaderDataBinding& bind : *data->m_SBinds)
static_cast<NXShaderDataBinding&>(bind).commit();
}
boo::ObjToken<IGraphicsBufferD> NXDataFactoryImpl::newPoolBuffer(BufferUse use, size_t stride,
size_t count __BooTraceArgs) {
boo::ObjToken<BaseGraphicsPool> pool(new NXPool(*this __BooTraceArgsUse));
NXPool* cpool = pool.cast<NXPool>();
NXGraphicsBufferD<BaseGraphicsPool>* retval =
new NXGraphicsBufferD<BaseGraphicsPool>(pool, use, m_ctx, stride, count);
unsigned size = retval->sizeForGPU(m_ctx, 0);
/* allocate memory */
if (size) {
struct pipe_resource* poolBuf =
pipe_buffer_create_flags(m_ctx->m_screen, USE_TABLE[int(use)], PIPE_USAGE_DEFAULT,
PIPE_RESOURCE_FLAG_MAP_PERSISTENT | PIPE_RESOURCE_FLAG_MAP_COHERENT, size);
cpool->m_constantBuffer = poolBuf;
pipe_transfer* xfer;
uint8_t* mappedData = (uint8_t*)pipe_buffer_map(
m_ctx->m_pctx, poolBuf,
PIPE_TRANSFER_WRITE | PIPE_TRANSFER_MAP_DIRECTLY | PIPE_TRANSFER_PERSISTENT | PIPE_TRANSFER_COHERENT, &xfer);
retval->placeForGPU(poolBuf, mappedData);
}
return {retval};
}
void NXCommandQueue::execute() {
if (!m_running)
return;
/* Stage dynamic uploads */
NXDataFactoryImpl* gfxF = static_cast<NXDataFactoryImpl*>(m_parent->getDataFactory());
std::unique_lock<std::recursive_mutex> datalk(gfxF->m_dataMutex);
if (gfxF->m_dataHead) {
for (BaseGraphicsData& d : *gfxF->m_dataHead) {
if (d.m_DBufs)
for (IGraphicsBufferD& b : *d.m_DBufs)
static_cast<NXGraphicsBufferD<BaseGraphicsData>&>(b).update(m_fillBuf);
if (d.m_DTexs)
for (ITextureD& t : *d.m_DTexs)
static_cast<NXTextureD&>(t).update(m_fillBuf);
}
}
if (gfxF->m_poolHead) {
for (BaseGraphicsPool& p : *gfxF->m_poolHead) {
if (p.m_DBufs)
for (IGraphicsBufferD& b : *p.m_DBufs)
static_cast<NXGraphicsBufferD<BaseGraphicsData>&>(b).update(m_fillBuf);
}
}
datalk.unlock();
/* Perform texture and swap-chain resizes */
if (m_ctx->_resizeWindowSurfaces() || m_texResizes.size()) {
for (const auto& resize : m_texResizes) {
if (m_boundTarget.get() == resize.first)
m_boundTarget.reset();
resize.first->resize(m_ctx, resize.second.first, resize.second.second);
}
m_texResizes.clear();
m_resolveDispSource = nullptr;
return;
}
/* Clear dead data */
m_drawResTokens[m_drawBuf].clear();
/* Swap internal buffers */
m_drawBuf = m_fillBuf;
m_fillBuf ^= 1;
/* Flush the pipe */
m_ctx->m_pctx->flush(m_ctx->m_pctx, nullptr, PIPE_FLUSH_END_OF_FRAME);
/* Set framebuffer fence */
NvFence fence;
struct pipe_surface* old_back = m_ctx->m_windowSurfaces[ST_ATTACHMENT_BACK_LEFT];
fence.id = nouveau_switch_resource_get_syncpoint(old_back->texture, &fence.value);
if ((int)fence.id >= 0) {
NvFence* surf_fence = &m_ctx->m_fences[m_ctx->m_fence_swap];
if (surf_fence->id != fence.id || surf_fence->value != fence.value) {
*surf_fence = fence;
NvMultiFence mf;
nvMultiFenceCreate(&mf, &fence);
gfxAppendFence(&mf);
}
}
gfxSwapBuffers();
/* Swap buffer attachments and invalidate framebuffer */
m_ctx->m_fence_swap = !m_ctx->m_fence_swap;
m_ctx->m_windowSurfaces[ST_ATTACHMENT_BACK_LEFT] = m_ctx->m_windowSurfaces[ST_ATTACHMENT_FRONT_LEFT];
m_ctx->m_windowSurfaces[ST_ATTACHMENT_FRONT_LEFT] = old_back;
}
static void setMesaConfig() {
// Uncomment below to disable error checking and save CPU time (useful for production):
// setenv("MESA_NO_ERROR", "1", 1);
// Uncomment below to enable Mesa logging:
setenv("EGL_LOG_LEVEL", "debug", 1);
setenv("MESA_VERBOSE", "all", 1);
setenv("NOUVEAU_MESA_DEBUG", "1", 1);
// Uncomment below to enable shader debugging in Nouveau:
setenv("NV50_PROG_OPTIMIZE", "0", 1);
setenv("NV50_PROG_DEBUG", "1", 1);
setenv("NV50_PROG_CHIPSET", "0x120", 1);
}
bool NXContext::initialize() {
/* Set mesa configuration (useful for debugging) */
setMesaConfig();
gfxInitDefault();
gfxSetMode(GfxMode_TiledDouble);
consoleInit(nullptr);
printf("Activated console\n\n");
m_screen = nouveau_switch_screen_create();
if (!m_screen) {
Log.report(logvisor::Fatal, fmt("Failed to create nouveau screen"));
return false;
}
printf("nouveau_switch_screen_create done\n");
fflush(stdout);
m_pctx = m_screen->context_create(m_screen, nullptr, 0);
if (!m_pctx) {
Log.report(logvisor::Fatal, fmt("Failed to create pipe context"));
m_screen->destroy(m_screen);
return false;
}
printf("m_screen->context_create done\n");
st_config_options opts = {};
m_st = st_create_context(API_OPENGL_CORE, m_pctx, nullptr, nullptr, &opts, false);
if (!m_st) {
Log.report(logvisor::Fatal, fmt("Failed to create st context"));
m_screen->destroy(m_screen);
return false;
}
u32 width, height;
gfxGetFramebufferResolution(&width, &height);
for (int i = 0; i < 2; ++i) {
/* color target */
struct pipe_resource texTempl = {};
texTempl.target = PIPE_TEXTURE_RECT;
texTempl.format = ColorFormat;
texTempl.width0 = width;
texTempl.height0 = height;
texTempl.depth0 = 1;
texTempl.array_size = 1;
texTempl.usage = PIPE_USAGE_DEFAULT;
texTempl.nr_samples = texTempl.nr_storage_samples = 1;
texTempl.bind = PIPE_BIND_RENDER_TARGET;
u32 index = i == ST_ATTACHMENT_FRONT_LEFT ? 1 : 0;
struct winsys_handle whandle;
whandle.type = WINSYS_HANDLE_TYPE_SHARED;
whandle.handle = gfxGetFramebufferHandle(index, &whandle.offset);
whandle.stride = gfxGetFramebufferPitch();
struct pipe_resource* tex = m_screen->resource_from_handle(m_screen, &texTempl, &whandle, 0);
if (!tex) {
Log.report(logvisor::Fatal, fmt("Failed to create color target texture"));
return false;
}
/* surface */
struct pipe_surface surfTempl = {};
surfTempl.format = ColorFormat;
m_windowSurfaces[i] = m_pctx->create_surface(m_pctx, tex, &surfTempl);
if (!m_windowSurfaces[i]) {
Log.report(logvisor::Fatal, fmt("Failed to create color surface"));
return false;
}
m_fences[i].id = UINT32_MAX;
}
return m_compiler.initialize(m_screen, m_st);
}
bool NXContext::terminate() {
if (m_st)
st_destroy_context(m_st);
if (m_screen)
m_screen->destroy(m_screen);
gfxExit();
return true;
}
bool NXContext::_resizeWindowSurfaces() { return false; }
std::unique_ptr<IGraphicsCommandQueue> _NewNXCommandQueue(NXContext* ctx, IGraphicsContext* parent) {
return std::make_unique<NXCommandQueue>(ctx, parent);
}
std::unique_ptr<IGraphicsDataFactory> _NewNXDataFactory(IGraphicsContext* parent, NXContext* ctx) {
return std::make_unique<NXDataFactoryImpl>(parent, ctx);
}
} // namespace boo