Lioncash baff71cdc3 General: Tidy up includes
Alphabetizes includes and resolves quite a few instances of indirect
inclusions, making the requirements of several interfaces explicit. This
also trims out includes that aren't actually necessary (likely due to
changes in the API over time).
2019-08-19 21:02:56 -04:00

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