mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
The game's scene-loading thread makes a few NVN calls with a null object while an area loads or Kakariko's time of day changes: a memory pool's flags (and the other pool getters), a texture builder's storage size or alignment, and the mapping of a buffer that has no pool, into which it then writes 0x3d0 bytes. Each read the object at address 0 plus a field's offset, an access violation. The pool getters, nvnMemoryPoolInvalidateMappedRange and the two builder queries now answer 0 for a null object (nvn_no_object), and nvnBufferMap of a buffer with no pool returns a block of scratch memory (64 KiB, or the buffer's size when it records a larger one) instead of 0. The first such calls are logged with their caller. These fit the crash reports in Castle Town and in Kakariko before Death Mountain (#605, #602), which have not been confirmed to be this. Checked: --probe-nvn gains three checks (pool getters of no pool, builder queries of no builder, a 0x3d0-byte write through the mapping of a buffer with no pool) and they pass; its four other failures are the same on the baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
436 lines
24 KiB
C
436 lines
24 KiB
C
// NVN entry points beyond the core implementation that only report stored object state, attach debugging metadata,
|
|
// convert timestamps or take hardware hints. None of them changes rendering
|
|
// results. The *Deferred binds and memory-callback switches live with the command buffer in nvn_api_cmd.c.
|
|
// Contains code Copyright (c) 2026 jhaizhou (MIT; see THIRD_PARTY_NOTICES.md).
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "gfx.h"
|
|
#include "host.h"
|
|
#include "nvn_api.h"
|
|
#include "nvn_tex.h"
|
|
|
|
// The CPU is about to read what the GPU wrote into pool storage: the back end writes its host-side copies (the Vulkan
|
|
// back end's images) back first. NX2navi invalidates its 260x260 R16 grass maps and 1x1 pick targets this way. A
|
|
// CPU-invisible pool has no storage to read.
|
|
void nvn_pool_invalidate(const pool_t *p, uint64_t off, uint64_t size) {
|
|
if (!p || !p->mem || nvn_gpu_only((uintptr_t)p->mem) || off >= p->size || !size) return;
|
|
if (size > p->size - off) size = p->size - off;
|
|
gfx_backend()->guest_read((uintptr_t)p->mem + off, size);
|
|
}
|
|
|
|
__attribute__((weak)) void nvn_pool_flushed(const pool_t *p, uint64_t off, uint64_t size) { (void)p, (void)off, (void)size; }
|
|
|
|
// ---------- debug labels, markers, groups, checkpoints: meaningful only to NVN debugging tools
|
|
// void nvn<Object>SetDebugLabel(NVN<object> *o, const char *label)
|
|
NVN_API(nvnDeviceSetDebugLabel) { (void)c; }
|
|
NVN_API(nvnQueueSetDebugLabel) { (void)c; }
|
|
NVN_API(nvnWindowSetDebugLabel) { (void)c; }
|
|
NVN_API(nvnProgramSetDebugLabel) { (void)c; }
|
|
NVN_API(nvnMemoryPoolSetDebugLabel) { (void)c; }
|
|
NVN_API(nvnTexturePoolSetDebugLabel) { (void)c; }
|
|
NVN_API(nvnSamplerPoolSetDebugLabel) { (void)c; }
|
|
NVN_API(nvnBufferSetDebugLabel) { (void)c; }
|
|
NVN_API(nvnTextureSetDebugLabel) { (void)c; }
|
|
NVN_API(nvnSamplerSetDebugLabel) { (void)c; }
|
|
NVN_API(nvnCommandBufferSetDebugLabel) { (void)c; }
|
|
NVN_API(nvnSyncSetDebugLabel) { (void)c; }
|
|
// void nvnCommandBufferPushDebugGroup{Static,Dynamic,}(NVNcommandBuffer *cmd, uint32_t domain_id, const char *text),
|
|
// PopDebugGroup(cmd), PopDebugGroupId(cmd, uint32_t id), InsertDebugMarker{Static,Dynamic}(cmd, uint32_t id,
|
|
// const char *text), InsertDebugMarker(cmd, const char *text), InsertCheckpoint{Static,Dynamic}(cmd, uint32_t id,
|
|
// const char *text)
|
|
NVN_API(nvnCommandBufferPushDebugGroupStatic) { (void)c; }
|
|
NVN_API(nvnCommandBufferPushDebugGroupDynamic) { (void)c; }
|
|
NVN_API(nvnCommandBufferPushDebugGroup) { (void)c; }
|
|
NVN_API(nvnCommandBufferPopDebugGroup) { (void)c; }
|
|
NVN_API(nvnCommandBufferPopDebugGroupId) { (void)c; }
|
|
NVN_API(nvnCommandBufferInsertDebugMarkerStatic) { (void)c; }
|
|
NVN_API(nvnCommandBufferInsertDebugMarkerDynamic) { (void)c; }
|
|
NVN_API(nvnCommandBufferInsertDebugMarker) { (void)c; }
|
|
NVN_API(nvnCommandBufferInsertCheckpointStatic) { (void)c; }
|
|
NVN_API(nvnCommandBufferInsertCheckpointDynamic) { (void)c; }
|
|
// NVNboolean nvnDeviceIsExternalDebuggerAttached(const NVNdevice *d)
|
|
NVN_API(nvnDeviceIsExternalDebuggerAttached) { ret_x(c, 0); }
|
|
// void nvnDeviceSetAutomaticCheckpointsMask(NVNdevice *d, uint32_t mask) (NVN 55, no prototype in nvn.h)
|
|
NVN_API(nvnDeviceSetAutomaticCheckpointsMask) { (void)c; }
|
|
|
|
// ---------- timestamps: counters are written in Tegra 19.2 MHz ticks, the base of GetSystemTick (rt_ticks)
|
|
static uint64_t ticks_to_ns(uint64_t t) { return t / 12 * 625 + t % 12 * 625 / 12; }
|
|
// uint64_t nvnDeviceGetCurrentTimestampInNanoseconds(const NVNdevice *d)
|
|
NVN_API(nvnDeviceGetCurrentTimestampInNanoseconds) { ret_x(c, ticks_to_ns(rt_ticks())); }
|
|
// uint64_t nvnDeviceGetTimestampInNanoseconds(const NVNdevice *d, const NVNcounterData *counter)
|
|
NVN_API(nvnDeviceGetTimestampInNanoseconds) { ret_x(c, ticks_to_ns(((const NVNcounterData *)arg_p(c, 1))->timestamp)); }
|
|
|
|
// ---------- memory pools and buffers
|
|
// size_t nvnMemoryPoolGetSize(const NVNmemoryPool *p)
|
|
NVN_API(nvnMemoryPoolGetSize) {
|
|
if (nvn_no_object(c, "nvnMemoryPoolGetSize")) return;
|
|
ret_x(c, ((const pool_t *)arg_p(c, 0))->size);
|
|
}
|
|
// void nvnMemoryPoolInvalidateMappedRange(const NVNmemoryPool *p, ptrdiff_t offset, size_t size)
|
|
NVN_API(nvnMemoryPoolInvalidateMappedRange) {
|
|
int64_t off = (int64_t)arg_x(c, 1);
|
|
if (off >= 0) nvn_pool_invalidate(arg_p(c, 0), (uint64_t)off, arg_x(c, 2));
|
|
}
|
|
// void nvnBufferInvalidateMappedRange(const NVNbuffer *b, ptrdiff_t offset, size_t size): buffers are pool storage,
|
|
// which the GPU writes directly
|
|
NVN_API(nvnBufferInvalidateMappedRange) { (void)c; }
|
|
|
|
// ---------- texture builder and texture queries
|
|
static const tex_builder_t *tb_of(const Cpu *c) { return arg_p(c, 0); }
|
|
static const tex_t *tex_of(const Cpu *c) { return arg_p(c, 0); }
|
|
// void ...GetSwizzle(const T *o, NVNtextureSwizzle *r, *g, *b, *a)
|
|
static void get_swizzle(const Cpu *c, const nvn_texdesc *d) {
|
|
*(int32_t *)arg_p(c, 1) = d->swizzle_r;
|
|
*(int32_t *)arg_p(c, 2) = d->swizzle_g;
|
|
*(int32_t *)arg_p(c, 3) = d->swizzle_b;
|
|
*(int32_t *)arg_p(c, 4) = d->swizzle_a;
|
|
}
|
|
NVN_API(nvnTextureBuilderGetDevice) { ret_x(c, (uintptr_t)tb_of(c)->dev); }
|
|
NVN_API(nvnTextureBuilderGetTarget) { ret_x(c, (uint32_t)tb_of(c)->d.target); }
|
|
NVN_API(nvnTextureBuilderGetWidth) { ret_x(c, tb_of(c)->d.width); }
|
|
NVN_API(nvnTextureBuilderGetHeight) { ret_x(c, tb_of(c)->d.height); }
|
|
NVN_API(nvnTextureBuilderGetDepth) { ret_x(c, tb_of(c)->d.depth); }
|
|
NVN_API(nvnTextureBuilderGetLevels) { ret_x(c, tb_of(c)->d.levels); }
|
|
NVN_API(nvnTextureBuilderGetFormat) { ret_x(c, (uint32_t)tb_of(c)->d.format); }
|
|
NVN_API(nvnTextureBuilderGetSamples) { ret_x(c, (uint32_t)tb_of(c)->d.samples); }
|
|
NVN_API(nvnTextureBuilderGetSwizzle) { get_swizzle(c, &tb_of(c)->d); }
|
|
NVN_API(nvnTextureBuilderGetDepthStencilMode) { ret_x(c, NVN_TEXTURE_DEPTH_STENCIL_MODE_DEPTH); }
|
|
NVN_API(nvnTextureBuilderGetStride) { ret_x(c, tb_of(c)->d.stride); }
|
|
NVN_API(nvnTextureBuilderGetMemoryPool) { ret_x(c, (uintptr_t)tb_of(c)->pool); }
|
|
NVN_API(nvnTextureBuilderGetMemoryOffset) { ret_x(c, (uint64_t)tb_of(c)->off); }
|
|
NVN_API(nvnTextureGetTarget) { ret_x(c, (uint32_t)tex_of(c)->d.target); }
|
|
NVN_API(nvnTextureGetLevels) { ret_x(c, tex_of(c)->d.levels); }
|
|
NVN_API(nvnTextureGetFormat) { ret_x(c, (uint32_t)tex_of(c)->d.format); }
|
|
NVN_API(nvnTextureGetSamples) { ret_x(c, (uint32_t)tex_of(c)->d.samples); }
|
|
NVN_API(nvnTextureGetSwizzle) { get_swizzle(c, &tex_of(c)->d); }
|
|
NVN_API(nvnTextureGetDepthStencilMode) { ret_x(c, NVN_TEXTURE_DEPTH_STENCIL_MODE_DEPTH); }
|
|
NVN_API(nvnTextureGetStride) { ret_x(c, tex_of(c)->d.stride); }
|
|
NVN_API(nvnTextureGetTextureAddress) { ret_x(c, (uintptr_t)tex_of(c)->mem); }
|
|
NVN_API(nvnTextureGetMemoryPool) { ret_x(c, (uintptr_t)tex_of(c)->pool); }
|
|
NVN_API(nvnTextureGetMemoryOffset) { ret_x(c, (uint64_t)tex_of(c)->off); }
|
|
NVN_API(nvnTextureGetDebugID) { ret_x(c, tex_of(c)->serial); }
|
|
// ptrdiff_t nvnTextureBuilderGetViewOffset(const NVNtextureBuilder *b, const NVNtextureView *view) (NVN 55)
|
|
NVN_API(nvnTextureBuilderGetViewOffset) {
|
|
const tex_builder_t *o = tb_of(c);
|
|
const view_t *w = arg_p(c, 1);
|
|
if (!w) { ret_x(c, 0); return; }
|
|
nvn_texlayout L;
|
|
nvn_tex_layout(&o->d, &L);
|
|
uint64_t off = w->set_levels ? nvn_tex_level_offset(&o->d, w->base_level) : 0;
|
|
if (w->set_layers) off += (uint64_t)w->min_layer * L.layer_size;
|
|
ret_x(c, off);
|
|
}
|
|
// size_t nvnTextureBuilderGetZCullStorageSize / nvnTextureGetZCullStorageSize: the depth-cull cache needs no storage
|
|
NVN_API(nvnTextureBuilderGetZCullStorageSize) { ret_x(c, 0); }
|
|
NVN_API(nvnTextureGetZCullStorageSize) { ret_x(c, 0); }
|
|
|
|
// ---------- window queries
|
|
static const win_builder_t *wb_of(const Cpu *c) { return arg_p(c, 0); }
|
|
static const win_t *win_of(const Cpu *c) { return arg_p(c, 0); }
|
|
NVN_API(nvnWindowBuilderGetDevice) { ret_x(c, (uintptr_t)wb_of(c)->dev); }
|
|
NVN_API(nvnWindowBuilderGetNativeWindow) { ret_x(c, (uintptr_t)wb_of(c)->native); }
|
|
NVN_API(nvnWindowBuilderGetPresentInterval) { ret_x(c, (uint32_t)wb_of(c)->interval); }
|
|
NVN_API(nvnWindowBuilderGetNumTextures) { ret_x(c, (uint32_t)wb_of(c)->ntex); }
|
|
// NVNtexture *nvnWindowBuilderGetTexture(const NVNwindowBuilder *b, int i)
|
|
NVN_API(nvnWindowBuilderGetTexture) {
|
|
const win_builder_t *b = wb_of(c);
|
|
int i = (int)arg_x(c, 1);
|
|
ret_x(c, i >= 0 && i < b->ntex && i < 4 ? (uintptr_t)b->tex[i] : 0);
|
|
}
|
|
NVN_API(nvnWindowGetNativeWindow) { ret_x(c, (uintptr_t)win_of(c)->native); }
|
|
NVN_API(nvnWindowGetPresentInterval) { ret_x(c, (uint32_t)win_of(c)->interval); }
|
|
NVN_API(nvnWindowGetNumTextures) { ret_x(c, (uint32_t)win_of(c)->ntex); }
|
|
// void nvnWindowGetCrop(const NVNwindow *w, NVNrectangle *rect): the default crop is the whole image
|
|
NVN_API(nvnWindowGetCrop) {
|
|
const win_t *o = win_of(c);
|
|
NVNrectangle *r = arg_p(c, 1);
|
|
if (!o->crop[2] && !o->crop[3] && o->ntex) {
|
|
const tex_t *t = (const tex_t *)o->tex[0];
|
|
*r = (NVNrectangle){0, 0, (int)t->d.width, (int)t->d.height};
|
|
return;
|
|
}
|
|
*r = (NVNrectangle){o->crop[0], o->crop[1], o->crop[2], o->crop[3]};
|
|
}
|
|
|
|
// ---------- texture views
|
|
static view_t *view_of(const Cpu *c, int i) { return arg_p(c, i); }
|
|
// void nvnTextureViewSetSwizzle(NVNtextureView *v, NVNtextureSwizzle r, g, b, a)
|
|
NVN_API(nvnTextureViewSetSwizzle) {
|
|
view_t *v = view_of(c, 0);
|
|
for (int i = 0; i < 4; i++) {
|
|
unsigned s = (unsigned)arg_x(c, 1 + i);
|
|
if (s > 5) rt_fatal("nvn: invalid texture view swizzle %u", s);
|
|
v->swz[i] = (uint8_t)s;
|
|
}
|
|
v->set_swizzle = 1;
|
|
}
|
|
// void nvnTextureViewSetDepthStencilMode(NVNtextureView *v, NVNtextureDepthStencilMode mode): STENCIL makes the replay
|
|
// sample a depth-stencil texture through a stencil-aspect view (vk_exec_draw.c get_view)
|
|
NVN_API(nvnTextureViewSetDepthStencilMode) {
|
|
uint32_t m = (uint32_t)arg_x(c, 1);
|
|
if (m > NVN_TEXTURE_DEPTH_STENCIL_MODE_STENCIL) rt_fatal("nvn: invalid texture view depth-stencil mode %u", m);
|
|
view_t *v = view_of(c, 0);
|
|
v->set_dsmode = 1;
|
|
v->dsmode = (uint8_t)m;
|
|
}
|
|
// NVNboolean nvnTextureViewGetLevels(const NVNtextureView *v, int *base, int *n)
|
|
NVN_API(nvnTextureViewGetLevels) {
|
|
const view_t *v = view_of(c, 0);
|
|
*(int32_t *)arg_p(c, 1) = v->base_level;
|
|
*(int32_t *)arg_p(c, 2) = v->nlevels;
|
|
ret_x(c, v->set_levels);
|
|
}
|
|
// NVNboolean nvnTextureViewGetFormat(const NVNtextureView *v, NVNformat *format)
|
|
NVN_API(nvnTextureViewGetFormat) {
|
|
const view_t *v = view_of(c, 0);
|
|
*(int32_t *)arg_p(c, 1) = v->format;
|
|
ret_x(c, v->set_format);
|
|
}
|
|
// NVNboolean nvnTextureViewGetTarget(const NVNtextureView *v, NVNtextureTarget *target)
|
|
NVN_API(nvnTextureViewGetTarget) {
|
|
const view_t *v = view_of(c, 0);
|
|
*(int32_t *)arg_p(c, 1) = v->target;
|
|
ret_x(c, v->set_target);
|
|
}
|
|
// NVNboolean nvnTextureViewGetSwizzle(const NVNtextureView *v, NVNtextureSwizzle *r, *g, *b, *a)
|
|
NVN_API(nvnTextureViewGetSwizzle) {
|
|
const view_t *v = view_of(c, 0);
|
|
for (int i = 0; i < 4; i++) *(int32_t *)arg_p(c, 1 + i) = v->swz[i];
|
|
ret_x(c, v->set_swizzle);
|
|
}
|
|
// NVNboolean nvnTextureViewGetDepthStencilMode(const NVNtextureView *v, NVNtextureDepthStencilMode *mode)
|
|
NVN_API(nvnTextureViewGetDepthStencilMode) {
|
|
const view_t *v = view_of(c, 0);
|
|
*(int32_t *)arg_p(c, 1) = v->dsmode;
|
|
ret_x(c, v->set_dsmode);
|
|
}
|
|
// NVNboolean nvnTextureViewCompare(const NVNtextureView *a, const NVNtextureView *b)
|
|
NVN_API(nvnTextureViewCompare) {
|
|
const view_t *x = view_of(c, 0), *y = view_of(c, 1);
|
|
int same = x->set_levels == y->set_levels && x->set_layers == y->set_layers && x->set_format == y->set_format &&
|
|
x->set_target == y->set_target && x->set_swizzle == y->set_swizzle && x->set_dsmode == y->set_dsmode;
|
|
if (same && x->set_levels) same = x->base_level == y->base_level && x->nlevels == y->nlevels;
|
|
if (same && x->set_layers) same = x->min_layer == y->min_layer && x->nlayers == y->nlayers;
|
|
if (same && x->set_format) same = x->format == y->format;
|
|
if (same && x->set_target) same = x->target == y->target;
|
|
if (same && x->set_swizzle) same = !memcmp(x->swz, y->swz, 4);
|
|
if (same && x->set_dsmode) same = x->dsmode == y->dsmode;
|
|
ret_x(c, same);
|
|
}
|
|
|
|
// ---------- sampler queries
|
|
static const samp_par *sbp_of(const Cpu *c) { return &((const samp_builder_t *)arg_p(c, 0))->p; }
|
|
static const samp_par *sp_of(const Cpu *c) { return &((const samp_t *)arg_p(c, 0))->p; }
|
|
// void nvnSamplerBuilderSetBorderColor{i,ui}(NVNsamplerBuilder *b, const int / uint32_t *rgba)
|
|
static void border_from_int(const Cpu *c, int is_signed) {
|
|
samp_par *p = &((samp_builder_t *)arg_p(c, 0))->p;
|
|
const int32_t *v = arg_p(c, 1);
|
|
for (int i = 0; i < 4; i++) p->border[i] = is_signed ? (float)v[i] : (float)(uint32_t)v[i];
|
|
}
|
|
NVN_API(nvnSamplerBuilderSetBorderColori) { border_from_int(c, 1); }
|
|
NVN_API(nvnSamplerBuilderSetBorderColorui) { border_from_int(c, 0); }
|
|
NVN_API(nvnSamplerBuilderGetDevice) { ret_x(c, (uintptr_t)((const samp_builder_t *)arg_p(c, 0))->dev); }
|
|
// Shared shapes: (o, float *lo, float *hi), (o, NVNcompareMode *, NVNcompareFunc *), (o, float / int / uint32_t *rgba)
|
|
static void lod_clamp(const Cpu *c, const samp_par *p) {
|
|
*(float *)arg_p(c, 1) = p->lod_min;
|
|
*(float *)arg_p(c, 2) = p->lod_max;
|
|
}
|
|
static void compare(const Cpu *c, const samp_par *p) {
|
|
*(int32_t *)arg_p(c, 1) = p->cmp_mode;
|
|
*(int32_t *)arg_p(c, 2) = p->cmp_func;
|
|
}
|
|
static void border_f(const Cpu *c, const samp_par *p) { memcpy(arg_p(c, 1), p->border, 16); }
|
|
static void border_i(const Cpu *c, const samp_par *p) {
|
|
for (int i = 0; i < 4; i++) ((int32_t *)arg_p(c, 1))[i] = (int32_t)p->border[i];
|
|
}
|
|
static void border_ui(const Cpu *c, const samp_par *p) {
|
|
for (int i = 0; i < 4; i++) ((uint32_t *)arg_p(c, 1))[i] = (uint32_t)p->border[i];
|
|
}
|
|
NVN_API(nvnSamplerBuilderGetLodClamp) { lod_clamp(c, sbp_of(c)); }
|
|
NVN_API(nvnSamplerBuilderGetLodBias) { ret_s(c, sbp_of(c)->lod_bias); }
|
|
NVN_API(nvnSamplerBuilderGetCompare) { compare(c, sbp_of(c)); }
|
|
NVN_API(nvnSamplerBuilderGetBorderColor) { border_f(c, sbp_of(c)); }
|
|
NVN_API(nvnSamplerBuilderGetBorderColori) { border_i(c, sbp_of(c)); }
|
|
NVN_API(nvnSamplerBuilderGetBorderColorui) { border_ui(c, sbp_of(c)); }
|
|
NVN_API(nvnSamplerBuilderGetMaxAnisotropy) { ret_s(c, sbp_of(c)->aniso); }
|
|
NVN_API(nvnSamplerBuilderGetLodSnap) { ret_s(c, sbp_of(c)->lod_snap); }
|
|
// void nvnSamplerGetMinMagFilter(const NVNsampler *s, NVNminFilter *min, NVNmagFilter *mag)
|
|
NVN_API(nvnSamplerGetMinMagFilter) {
|
|
const samp_par *p = sp_of(c);
|
|
*(int32_t *)arg_p(c, 1) = p->minf;
|
|
*(int32_t *)arg_p(c, 2) = p->magf;
|
|
}
|
|
// void nvnSamplerGetWrapMode(const NVNsampler *s, NVNwrapMode *s, *t, *r)
|
|
NVN_API(nvnSamplerGetWrapMode) {
|
|
const samp_par *p = sp_of(c);
|
|
*(int32_t *)arg_p(c, 1) = p->ws;
|
|
*(int32_t *)arg_p(c, 2) = p->wt;
|
|
*(int32_t *)arg_p(c, 3) = p->wr;
|
|
}
|
|
NVN_API(nvnSamplerGetLodClamp) { lod_clamp(c, sp_of(c)); }
|
|
NVN_API(nvnSamplerGetLodBias) { ret_s(c, sp_of(c)->lod_bias); }
|
|
NVN_API(nvnSamplerGetCompare) { compare(c, sp_of(c)); }
|
|
NVN_API(nvnSamplerGetBorderColor) { border_f(c, sp_of(c)); }
|
|
NVN_API(nvnSamplerGetBorderColori) { border_i(c, sp_of(c)); }
|
|
NVN_API(nvnSamplerGetBorderColorui) { border_ui(c, sp_of(c)); }
|
|
NVN_API(nvnSamplerGetMaxAnisotropy) { ret_s(c, sp_of(c)->aniso); }
|
|
// NVNboolean nvnSamplerCompare(const NVNsampler *a, const NVNsampler *b)
|
|
NVN_API(nvnSamplerCompare) { ret_x(c, !memcmp(sp_of(c), &((const samp_t *)arg_p(c, 1))->p, sizeof(samp_par))); }
|
|
// uint64_t nvnSamplerGetDebugID(const NVNsampler *s)
|
|
NVN_API(nvnSamplerGetDebugID) { ret_x(c, arg_x(c, 0)); }
|
|
|
|
// ---------- fixed-function state objects: setters without a host equivalent store the value (the replay reports any
|
|
// non-default use); getters return what was set
|
|
#define ST(T) ((T *)arg_p(c, 0))
|
|
// void nvnColorStateSetLogicOp(NVNcolorState *s, NVNlogicOp op) / SetAlphaTest(NVNcolorState *s, NVNalphaFunc f)
|
|
NVN_API(nvnColorStateSetLogicOp) {
|
|
unsigned op = (unsigned)arg_x(c, 1);
|
|
if (op > 15) rt_fatal("nvn: logic op %u", op);
|
|
ST(color_st)->logic_op = op;
|
|
}
|
|
NVN_API(nvnColorStateSetAlphaTest) {
|
|
unsigned f = (unsigned)arg_x(c, 1);
|
|
if (f > 15) rt_fatal("nvn: alpha func %u", f);
|
|
ST(color_st)->alpha_func = f;
|
|
// the replay does not apply the test (vk_pipeline.c): say once that the game asks for one
|
|
if (f && f != NVN_ALPHA_FUNC_ALWAYS) {
|
|
static int once_;
|
|
if (!once_++) rt_log("nvn: SetAlphaTest %u: the alpha test is not applied", f);
|
|
}
|
|
}
|
|
// NVNboolean nvnColorStateGetBlendEnable(const NVNcolorState *s, int index)
|
|
NVN_API(nvnColorStateGetBlendEnable) {
|
|
int i = (int)arg_x(c, 1);
|
|
ret_x(c, i >= 0 && i < 8 && (ST(color_st)->blend_en >> i & 1));
|
|
}
|
|
NVN_API(nvnColorStateGetLogicOp) { ret_x(c, ST(color_st)->logic_op); }
|
|
NVN_API(nvnColorStateGetAlphaTest) { ret_x(c, ST(color_st)->alpha_func); }
|
|
// void nvnChannelMaskStateGetChannelMask(const NVNchannelMaskState *s, int index, NVNboolean *r, *g, *b, *a)
|
|
NVN_API(nvnChannelMaskStateGetChannelMask) {
|
|
int i = (int)arg_x(c, 1);
|
|
uint32_t m = i >= 0 && i < 8 ? ST(chmask_st)->mask >> (4 * i) : 0;
|
|
for (int k = 0; k < 4; k++) *(NVNboolean *)arg_p(c, 2 + k) = (NVNboolean)(m >> k & 1);
|
|
}
|
|
NVN_API(nvnBlendStateGetBlendTarget) { ret_x(c, ST(blend_st)->target); }
|
|
// void nvnBlendStateGetBlendFunc(const NVNblendState *s, NVNblendFunc *src_rgb, *dst_rgb, *src_a, *dst_a)
|
|
NVN_API(nvnBlendStateGetBlendFunc) {
|
|
const blend_st *b = ST(blend_st);
|
|
*(int32_t *)arg_p(c, 1) = b->src_rgb;
|
|
*(int32_t *)arg_p(c, 2) = b->dst_rgb;
|
|
*(int32_t *)arg_p(c, 3) = b->src_a;
|
|
*(int32_t *)arg_p(c, 4) = b->dst_a;
|
|
}
|
|
// void nvnBlendStateGetBlendEquation(const NVNblendState *s, NVNblendEquation *rgb, NVNblendEquation *alpha)
|
|
NVN_API(nvnBlendStateGetBlendEquation) {
|
|
*(int32_t *)arg_p(c, 1) = ST(blend_st)->eq_rgb;
|
|
*(int32_t *)arg_p(c, 2) = ST(blend_st)->eq_a;
|
|
}
|
|
// void nvnBlendStateSetAdvancedMode(NVNblendState *s, NVNblendAdvancedMode mode): only "none" is implemented; overlap,
|
|
// premultiplied source and normalized destination only matter with an advanced mode
|
|
NVN_API(nvnBlendStateSetAdvancedMode) {
|
|
unsigned m = (unsigned)arg_x(c, 1);
|
|
if (m) rt_fatal("nvn: advanced blend mode %u is not implemented", m);
|
|
}
|
|
NVN_API(nvnBlendStateSetAdvancedOverlap) { (void)c; }
|
|
NVN_API(nvnBlendStateSetAdvancedPremultipliedSrc) { (void)c; }
|
|
NVN_API(nvnBlendStateSetAdvancedNormalizedDst) { (void)c; }
|
|
NVN_API(nvnMultisampleStateGetMultisampleEnable) { ret_x(c, ST(ms_st)->enable); }
|
|
NVN_API(nvnMultisampleStateGetSamples) { ret_x(c, ST(ms_st)->samples); }
|
|
NVN_API(nvnMultisampleStateGetAlphaToCoverageEnable) { ret_x(c, ST(ms_st)->a2c); }
|
|
NVN_API(nvnMultisampleStateGetRasterSamples) { ret_x(c, ST(ms_st)->raster_samples); }
|
|
// void nvnMultisampleStateSetRasterSamples(NVNmultisampleState *s, int n)
|
|
NVN_API(nvnMultisampleStateSetRasterSamples) {
|
|
int n = (int)arg_x(c, 1);
|
|
if (n < 0 || n > 16) rt_fatal("nvn: raster samples %d", n);
|
|
ST(ms_st)->raster_samples = (uint8_t)n;
|
|
}
|
|
// void nvnMultisampleStateSetAlphaToCoverageDither(NVNmultisampleState *s, NVNboolean enable)
|
|
NVN_API(nvnMultisampleStateSetAlphaToCoverageDither) { ST(ms_st)->a2c_dither = nvn_arg_bool(c, 1) != 0; }
|
|
NVN_API(nvnPolygonStateGetCullFace) { ret_x(c, ST(poly_st)->cull); }
|
|
NVN_API(nvnPolygonStateGetFrontFace) { ret_x(c, ST(poly_st)->front); }
|
|
NVN_API(nvnPolygonStateGetPolygonMode) { ret_x(c, ST(poly_st)->mode); }
|
|
NVN_API(nvnPolygonStateGetPolygonOffsetEnables) { ret_x(c, ST(poly_st)->offset_en); }
|
|
NVN_API(nvnDepthStencilStateGetDepthTestEnable) { ret_x(c, ST(ds_st)->dt); }
|
|
NVN_API(nvnDepthStencilStateGetDepthWriteEnable) { ret_x(c, ST(ds_st)->dw); }
|
|
NVN_API(nvnDepthStencilStateGetDepthFunc) { ret_x(c, ST(ds_st)->df); }
|
|
NVN_API(nvnDepthStencilStateGetStencilTestEnable) { ret_x(c, ST(ds_st)->st); }
|
|
// NVNstencilFunc nvnDepthStencilStateGetStencilFunc(const NVNdepthStencilState *s, NVNface face)
|
|
NVN_API(nvnDepthStencilStateGetStencilFunc) {
|
|
const ds_st *d = ST(ds_st);
|
|
ret_x(c, (uint32_t)arg_x(c, 1) == NVN_FACE_BACK ? d->sf_b : d->sf_f);
|
|
}
|
|
// void nvnDepthStencilStateGetStencilOp(const NVNdepthStencilState *s, NVNface face, NVNstencilOp *fail,
|
|
// NVNstencilOp *depth_fail, NVNstencilOp *depth_pass)
|
|
NVN_API(nvnDepthStencilStateGetStencilOp) {
|
|
const ds_st *d = ST(ds_st);
|
|
int back = (uint32_t)arg_x(c, 1) == NVN_FACE_BACK;
|
|
*(int32_t *)arg_p(c, 2) = (int32_t)(back ? d->fail_b : d->fail_f);
|
|
*(int32_t *)arg_p(c, 3) = (int32_t)(back ? d->zfail_b : d->zfail_f);
|
|
*(int32_t *)arg_p(c, 4) = (int32_t)(back ? d->zpass_b : d->zpass_f);
|
|
}
|
|
// void nvnVertexAttribStateGetFormat(const NVNvertexAttribState *s, NVNformat *format, ptrdiff_t *offset)
|
|
NVN_API(nvnVertexAttribStateGetFormat) {
|
|
*(int32_t *)arg_p(c, 1) = ST(va_st)->fmt;
|
|
*(int64_t *)arg_p(c, 2) = ST(va_st)->offset;
|
|
}
|
|
NVN_API(nvnVertexAttribStateGetStreamIndex) { ret_x(c, ST(va_st)->stream); }
|
|
NVN_API(nvnVertexStreamStateGetStride) { ret_x(c, ST(vs_st)->stride); }
|
|
NVN_API(nvnVertexStreamStateGetDivisor) { ret_x(c, ST(vs_st)->divisor); }
|
|
#undef ST
|
|
|
|
// ---------- rasterisation state without a host equivalent: lines are 1 pixel wide and point size comes from the vertex
|
|
// shader. Non-default values are reported once; the drawing proceeds without them.
|
|
#define ONCE(...) do { static int once_; if (!once_++) rt_log("nvn: " __VA_ARGS__); } while (0)
|
|
// void nvnCommandBufferSetLineWidth / SetPointSize(NVNcommandBuffer *cmd, float value)
|
|
NVN_API(nvnCommandBufferSetLineWidth) {
|
|
float w = arg_s(c, 0);
|
|
if (w != 1.0f) ONCE("SetLineWidth %g: lines are drawn 1 pixel wide", w);
|
|
}
|
|
// SetPointSize is recorded in nvn_api_cmd.c (OP_BIND_STATE kind 6): the replay feeds it to the vertex shaders through the
|
|
// support buffer (translator patch 0008).
|
|
// void nvnCommandBufferSetConservativeRasterEnable(NVNcommandBuffer *cmd, NVNboolean enable)
|
|
NVN_API(nvnCommandBufferSetConservativeRasterEnable) {
|
|
if (nvn_arg_bool(c, 1)) ONCE("conservative rasterisation requested: drawn normally");
|
|
}
|
|
// void nvnCommandBufferSetConservativeRasterDilate(NVNcommandBuffer *cmd, float dilate)
|
|
NVN_API(nvnCommandBufferSetConservativeRasterDilate) { (void)c; }
|
|
// void nvnCommandBufferSetSampleMask(NVNcommandBuffer *cmd, int mask)
|
|
NVN_API(nvnCommandBufferSetSampleMask) {
|
|
int m = (int)arg_x(c, 1);
|
|
if ((m & 0xffff) != 0xffff) ONCE("SetSampleMask %#x: fixed-function sample mask not applied", m);
|
|
}
|
|
// void nvnCommandBufferSetAlphaRef(NVNcommandBuffer *cmd, float ref): used only with the alpha test, which the replay
|
|
// reports
|
|
NVN_API(nvnCommandBufferSetAlphaRef) {
|
|
static int once_;
|
|
if (!once_++) rt_log("nvn: SetAlphaRef %g: the alpha test is not applied", (double)arg_s(c, 0));
|
|
}
|
|
// void nvnCommandBufferSetDepthBounds(NVNcommandBuffer *cmd, NVNboolean enable, float min, float max): disabling is
|
|
// exact; an enabled range is drawn without the test and reported
|
|
NVN_API(nvnCommandBufferSetDepthBounds) {
|
|
float lo = arg_s(c, 0), hi = arg_s(c, 1);
|
|
if (nvn_arg_bool(c, 1) && (lo > 0.0f || hi < 1.0f))
|
|
ONCE("SetDepthBounds [%g, %g]: depth-bounds test not applied (fragments outside the range are kept)", lo, hi);
|
|
}
|
|
// void nvnCommandBufferSetProvokingVertexMode(NVNcommandBuffer *cmd, int mode) (NVN 55): the NVN default is the last
|
|
// vertex (GL convention); only the default is accepted
|
|
NVN_API(nvnCommandBufferSetProvokingVertexMode) {
|
|
int mode = (int)arg_x(c, 1);
|
|
if (mode != 0) ONCE("SetProvokingVertexMode %d: provoking-vertex conversion not implemented", mode);
|
|
}
|
|
#undef ONCE
|
|
|
|
// ---------- Tegra tiled-cache, ZCull and rasterisation hints
|
|
NVN_API(nvnCommandBufferSetTiledCacheAction) { (void)c; } // tiled-cache flush / enable hint
|
|
NVN_API(nvnCommandBufferSetTiledCacheTileSize) { (void)c; }
|
|
NVN_API(nvnCommandBufferSetSubpixelPrecisionBias) { (void)c; }
|
|
NVN_API(nvnCommandBufferDiscardColor) { (void)c; } // contents become undefined: keeping them is valid
|
|
NVN_API(nvnCommandBufferDiscardDepthStencil) { (void)c; }
|
|
NVN_API(nvnCommandBufferSaveZCullData) { (void)c; } // no depth-cull cache: nothing to save or restore
|
|
NVN_API(nvnCommandBufferRestoreZCullData) { (void)c; }
|
|
NVN_API(nvnCommandBufferOverrideSubtileSize) { (void)c; } // NVN 55 hints, no prototype in nvn.h
|
|
NVN_API(nvnCommandBufferSetZCullZF32CompressionEnable) { (void)c; }
|
|
// nvnProgramGetSubtileSize (NVN 55): its signature is not identified
|
|
NVN_API(nvnProgramGetSubtileSize) { (void)c; rt_fatal("nvn: nvnProgramGetSubtileSize: signature not identified"); }
|