mirror of https://github.com/AxioDL/boo.git
1891 lines
67 KiB
C++
1891 lines
67 KiB
C++
#include "logvisor/logvisor.hpp"
|
|
#include "boo/graphicsdev/NX.hpp"
|
|
#include "boo/IGraphicsContext.hpp"
|
|
#include "boo/graphicsdev/GLSLMacros.hpp"
|
|
#include "../Common.hpp"
|
|
#include "xxhash/xxhash.h"
|
|
|
|
#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 <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(NULL);
|
|
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
|