Vulkan: bring back the bounding box and the dynamic vertex loader

rslc now has storage buffers and atomics (updated from RetroArch's
libretro-common), so the Vulkan backend offers again what needs them:
the bounding box where the device has fragmentStoresAndAtomics, the
dynamic vertex loader, and the vertex-shader line and point
expansion. GPU texture decoding stays off until rslc has a compute
stage.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-11-06 02:43:17 +01:00
1 parent e3e2f2a230
commit e2c5a83f49
6 files changed
+215 -16

No files matched your search

+30
View File
@@ -357,6 +357,9 @@ static void pr_array_suffix(struct rslc_pr *p, const struct rslc_type *t)
while (t->kind == RSLC_TY_ARRAY)
{
pr_c(p, '[');
#ifdef RSLC_EXTENDED
if (t->array_len) /* a storage buffer's unsized last member */
#endif
pr_u(p, t->array_len);
pr_c(p, ']');
t = t->elem;
@@ -3610,9 +3613,25 @@ static void pr_block_decl(struct rslc_pr *p, unsigned idx)
/* CafeGLSL takes the push block as a second uniform block, laid out
* as the source declared it; the runtime reads offsets back from
* the .gsh */
#ifdef RSLC_EXTENDED
if (b->buffer)
{
pr_s(p, b->std140 ? "layout(std140) " : "layout(std430) ");
if (b->readonly)
pr_s(p, "readonly ");
if (b->coherent)
pr_s(p, "coherent ");
pr_s(p, "buffer ");
}
else
{
#endif
if (!b->push || b->std140)
pr_s(p, "layout(std140) ");
pr_s(p, "uniform ");
#ifdef RSLC_EXTENDED
}
#endif
pr_s(p, type_name);
pr_s(p, " {");
p->indent++;
@@ -5318,6 +5337,17 @@ bool rslc_emit_text(struct rslc_ctx *c, struct rslc_result *result)
return false;
}
if (p->hlsl || p->msl)
{
uint32_t b;
for (b = 2; b < RSLC_MAX_BLOCKS; b++)
if (c->prog.has_block[b] && c->prog.blocks[b].buffer)
{
rslc_err(c, p->hlsl ? "hlsl: no form of a storage buffer yet"
: "msl: no form of a storage buffer", NULL, c->prog.blocks[b].line);
return false;
}
}
if (p->hlsl || p->msl)
{
bool ioblock = false;
uint32_t k;
+90 -8
View File
@@ -1686,6 +1686,9 @@ struct rslc_quals
bool is_out;
bool is_inout;
#ifdef RSLC_EXTENDED
bool is_buffer;
bool readonly;
bool coherent;
bool early_fragment_tests;
uint8_t gs_prim; /* RSLC_GS_* named in the layout */
uint32_t index;
@@ -3047,7 +3050,15 @@ static const struct rslc_bsig rslc_bsigs[] = {
{ RSLC_BFN_FWIDTHFINE, "fwidthFine", "F:F" }, { RSLC_BFN_DFDXCOARSE, "dFdxCoarse", "F:F" },
{ RSLC_BFN_DFDYCOARSE, "dFdyCoarse", "F:F" }, { RSLC_BFN_FWIDTHCOARSE, "fwidthCoarse", "F:F" }
#ifdef RSLC_EXTENDED
, { RSLC_BFN_EMITVERTEX, "EmitVertex", ":v" }, { RSLC_BFN_ENDPRIMITIVE, "EndPrimitive", ":v" }
, { RSLC_BFN_EMITVERTEX, "EmitVertex", ":v" }, { RSLC_BFN_ENDPRIMITIVE, "EndPrimitive", ":v" },
{ RSLC_BFN_ATOMICADD, "atomicAdd", "i i:i;u u:u" },
{ RSLC_BFN_ATOMICMIN, "atomicMin", "i i:i;u u:u" },
{ RSLC_BFN_ATOMICMAX, "atomicMax", "i i:i;u u:u" },
{ RSLC_BFN_ATOMICAND, "atomicAnd", "i i:i;u u:u" },
{ RSLC_BFN_ATOMICOR, "atomicOr", "i i:i;u u:u" },
{ RSLC_BFN_ATOMICXOR, "atomicXor", "i i:i;u u:u" },
{ RSLC_BFN_ATOMICEXCHANGE, "atomicExchange", "i i:i;u u:u" },
{ RSLC_BFN_ATOMICCOMPSWAP, "atomicCompSwap", "i i i:i;u u u:u" }
#endif
};
@@ -3381,6 +3392,30 @@ static const struct rslc_type *rslc_p_type_spec(struct rslc_parser *p,
static const struct rslc_type *rslc_p_array_suffix(struct rslc_parser *p,
const struct rslc_type *ty);
#ifdef RSLC_EXTENDED
/* The block an access is rooted in, when it is a storage buffer. */
static const struct rslc_blockdecl *rslc_p_buffer_of(struct rslc_parser *p, uint32_t e)
{
for (;;)
{
struct rslc_node *n = rslc_p_node(p, e);
if (n->op == RSLC_OP_MEMBER || n->op == RSLC_OP_INDEX || n->op == RSLC_OP_SWIZZLE)
{
e = n->a;
continue;
}
if (n->op == RSLC_OP_VAR)
{
const struct rslc_var *v = p->c->prog.vars[n->a];
if ((v->storage == RSLC_ST_MEMBER || v->storage == RSLC_ST_BLOCK)
&& p->c->prog.blocks[v->block].buffer)
return &p->c->prog.blocks[v->block];
}
return NULL;
}
}
#endif
static bool rslc_p_is_lvalue(struct rslc_parser *p, uint32_t e)
{
struct rslc_node *n = rslc_p_node(p, e);
@@ -3389,6 +3424,12 @@ static bool rslc_p_is_lvalue(struct rslc_parser *p, uint32_t e)
case RSLC_OP_VAR:
{
struct rslc_var *v = p->c->prog.vars[n->a];
#ifdef RSLC_EXTENDED
/* a storage buffer is written unless it is readonly */
if ((v->storage == RSLC_ST_MEMBER || v->storage == RSLC_ST_BLOCK)
&& p->c->prog.blocks[v->block].buffer)
return !p->c->prog.blocks[v->block].readonly;
#endif
if (v->storage == RSLC_ST_CONST || v->storage == RSLC_ST_IN
|| v->storage == RSLC_ST_SAMPLER || v->storage == RSLC_ST_BLOCK
|| v->storage == RSLC_ST_MEMBER)
@@ -3710,6 +3751,15 @@ static uint32_t rslc_p_call(struct rslc_parser *p, struct rslc_sym *name, uint32
uint32_t i;
if (!rslc_p_resolve_builtin(p, bs, args, nargs, &ret, line))
return 0;
#ifdef RSLC_EXTENDED
/* an atomic works on a member of a storage buffer in place */
if (name->bfn >= RSLC_BFN_ATOMICADD && name->bfn <= RSLC_BFN_ATOMICCOMPSWAP
&& (!rslc_p_is_lvalue(p, args[0]) || !rslc_p_buffer_of(p, args[0])))
{
rslc_err(p->c, "an atomic needs a writable storage buffer member, in ", name->str, line);
return 0;
}
#endif
for (i = 0; i < nargs; i++)
prec = rslc_prec_max(prec, rslc_p_node(p, args[i])->prec);
r = rslc_p_mk(p, RSLC_OP_BUILTIN, ret, name->bfn, rslc_list_new(p->c, args, nargs),
@@ -4432,6 +4482,14 @@ static bool rslc_p_quals(struct rslc_parser *p, struct rslc_quals *q)
case RSLC_KW_LOWP: q->precision = RSLC_PREC_LOW; break;
case RSLC_KW_MEDIUMP: q->precision = RSLC_PREC_MEDIUM; break;
case RSLC_KW_HIGHP: q->precision = RSLC_PREC_HIGH; break;
#ifdef RSLC_EXTENDED
case RSLC_KW_BUFFER: q->is_buffer = true; break;
case RSLC_KW_READONLY: q->readonly = true; break;
case RSLC_KW_COHERENT: q->coherent = true; break;
case RSLC_KW_WRITEONLY:
case RSLC_KW_RESTRICT:
case RSLC_KW_VOLATILE: break;
#endif
default:
return true;
}
@@ -4665,6 +4723,8 @@ static bool rslc_p_starts_declaration(struct rslc_parser *p)
s = rslc_tok_sym(p->c, t);
if (s->kw >= RSLC_KW_CONST && s->kw <= RSLC_KW_STRUCT)
return true;
if (s->kw >= RSLC_KW_BUFFER && s->kw <= RSLC_KW_VOLATILE)
return true; /* RSLC_EXTENDED: only seeded there */
if (s->kw >= RSLC_KW_VOID && s->kw <= RSLC_KW_LAST_TYPE)
{
/* A type followed by '(' is a constructor expression; a type
@@ -5225,18 +5285,22 @@ static bool rslc_p_block_decl(struct rslc_parser *p, struct rslc_quals *q,
struct rslc_type *st;
struct rslc_sym *instance = NULL;
if (!q->is_uniform)
return rslc_p_fail(p, "only uniform blocks are supported", NULL);
#ifdef RSLC_EXTENDED
/* a further uniform block takes the next slot past the push block */
if (idx == 0 && pr->has_block[0])
if (!q->is_uniform && !q->is_buffer)
return rslc_p_fail(p, "only uniform and buffer blocks are supported", NULL);
/* a storage buffer, and a further uniform block, take the next slot
* past the push block */
if (q->is_buffer || (idx == 0 && pr->has_block[0]))
{
idx = 2;
while (idx < RSLC_MAX_BLOCKS && pr->has_block[idx])
idx++;
if (idx == RSLC_MAX_BLOCKS)
return rslc_p_fail(p, "too many uniform blocks", NULL);
return rslc_p_fail(p, "too many uniform and buffer blocks", NULL);
}
#else
if (!q->is_uniform)
return rslc_p_fail(p, "only uniform blocks are supported", NULL);
#endif
if (pr->has_block[idx])
return rslc_p_fail(p, idx ? "more than one push_constant block"
@@ -5306,6 +5370,20 @@ static bool rslc_p_block_decl(struct rslc_parser *p, struct rslc_quals *q,
b->line = line;
b->push = idx == 1;
b->std140 = idx == 0 || q->std140;
#ifdef RSLC_EXTENDED
/* a further uniform block is std140 as the first is; a storage
* buffer std430 unless it says otherwise, and only its last member
* may be an array of unstated length */
b->buffer = q->is_buffer;
b->readonly = q->readonly;
b->coherent = q->coherent;
if (idx >= 2 && !q->is_buffer)
b->std140 = true;
for (i = 0; i < nfields; i++)
if (fields[i].type->kind == RSLC_TY_ARRAY && !fields[i].type->array_len
&& (!q->is_buffer || i + 1 != nfields))
return rslc_p_fail(p, "only a storage buffer's last member may be unsized", NULL);
#endif
pr->has_block[idx] = true;
if (instance)
@@ -5551,8 +5629,12 @@ static uint32_t rslc_p_declaration(struct rslc_parser *p, bool global)
}
#endif
/* uniform Block { ... }; */
if (q.is_uniform && P_TOK(p)->kind == RSLC_TK_IDENT
/* uniform Block { ... }; RSLC_EXTENDED: buffer Block { ... }; */
if ((q.is_uniform
#ifdef RSLC_EXTENDED
|| q.is_buffer
#endif
) && P_TOK(p)->kind == RSLC_TK_IDENT
&& rslc_tok_sym(p->c, P_TOK(p))->kw == RSLC_KW_NONE
&& rslc_tok_is_punct(P_PEEK(p, 1), RSLC_P_LBRACE))
{
+80 -2
View File
@@ -415,7 +415,8 @@ static const char *const rslc_keywords[] = {
#ifdef RSLC_EXTENDED
, "sampler2DArray", "isampler2DArray", "usampler2DArray",
"sampler2DMSArray", "isampler2DMSArray", "usampler2DMSArray",
"samplerBuffer", "isamplerBuffer", "usamplerBuffer"
"samplerBuffer", "isamplerBuffer", "usamplerBuffer",
"buffer", "readonly", "writeonly", "coherent", "restrict", "volatile"
#endif
};
@@ -1017,6 +1018,9 @@ enum spv_section
#define SPV_DEC_RELAXEDPRECISION 0
#define SPV_DEC_BLOCK 2
#define SPV_DEC_BUFFERBLOCK 3
#define SPV_DEC_COHERENT 23
#define SPV_DEC_NONWRITABLE 24
#define SPV_DEC_COLMAJOR 5
#define SPV_DEC_ARRAYSTRIDE 6
#define SPV_DEC_MATRIXSTRIDE 7
@@ -1052,7 +1056,7 @@ enum spv_op
SpvOpEntryPoint = 15, SpvOpExecutionMode = 16, SpvOpCapability = 17,
SpvOpTypeVoid = 19, SpvOpTypeBool = 20, SpvOpTypeInt = 21, SpvOpTypeFloat = 22,
SpvOpTypeVector = 23, SpvOpTypeMatrix = 24, SpvOpTypeImage = 25,
SpvOpTypeSampledImage = 27, SpvOpTypeArray = 28, SpvOpTypeStruct = 30,
SpvOpTypeSampledImage = 27, SpvOpTypeArray = 28, SpvOpTypeRuntimeArray = 29, SpvOpTypeStruct = 30,
SpvOpTypePointer = 32, SpvOpTypeFunction = 33,
SpvOpConstantTrue = 41, SpvOpConstantFalse = 42, SpvOpConstant = 43,
SpvOpConstantComposite = 44,
@@ -1096,6 +1100,9 @@ enum spv_op
SpvOpPhi = 245, SpvOpLoopMerge = 246, SpvOpSelectionMerge = 247,
SpvOpLabel = 248, SpvOpBranch = 249, SpvOpBranchConditional = 250,
SpvOpEmitVertex = 218, SpvOpEndPrimitive = 219,
SpvOpAtomicExchange = 229, SpvOpAtomicCompareExchange = 230, SpvOpAtomicIAdd = 234,
SpvOpAtomicSMin = 236, SpvOpAtomicUMin = 237, SpvOpAtomicSMax = 238, SpvOpAtomicUMax = 239,
SpvOpAtomicAnd = 240, SpvOpAtomicOr = 241, SpvOpAtomicXor = 242,
SpvOpSwitch = 251, SpvOpKill = 252, SpvOpReturn = 253, SpvOpReturnValue = 254,
SpvOpUnreachable = 255
};
@@ -1347,6 +1354,19 @@ static uint32_t spv_type_laid(struct spv_emit *e, const struct rslc_type *t, boo
uint32_t w[3];
if (!elem)
return 0;
#ifdef RSLC_EXTENDED
/* a storage buffer's last member, as long as the buffer bound */
if (!t->array_len)
{
rslc_layout_of(t, std140, &a, &s, &st);
id = spv_id(e);
if (!spv_inst2(e, SPV_SEC_TYPES, SpvOpTypeRuntimeArray, id, elem)
|| !spv_decorate1(e, id, SPV_DEC_ARRAYSTRIDE, st))
return 0;
ti->laid[k] = id;
return id;
}
#endif
/* OpConstant for the length, int typed */
{
uint32_t i;
@@ -2832,6 +2852,47 @@ static uint32_t spv_builtin_node(struct spv_emit *e, const struct rslc_node *n)
w[0] = x; w[1] = ptr;
return spv_ext(e, tid, bfn == RSLC_BFN_MODF ? GLSLstd450Modf : GLSLstd450Frexp, w, 2, prec);
}
#ifdef RSLC_EXTENDED
/* atomics: on the member in place, device scope, no ordering, as
* glslang emits them */
if (bfn >= RSLC_BFN_ATOMICADD && bfn <= RSLC_BFN_ATOMICCOMPSWAP)
{
const struct rslc_type *at = rslc_node_at(e->c, rslc_list_item(e->c, n->b, 0))->type;
bool sgn = at->base == RSLC_BASE_INT;
uint32_t scope = spv_const_uint(e, 1); /* Device */
uint32_t sem = spv_const_uint(e, 0);
uint32_t ptr, val, w[8];
unsigned sc; bool laid, std140;
enum spv_op op;
if (!scope || !sem)
return 0;
ptr = spv_lvalue(e, rslc_list_item(e->c, n->b, 0), &sc, &laid, &std140);
val = ptr ? spv_rvalue(e, rslc_list_item(e->c, n->b, 1)) : 0;
if (!val)
return 0;
switch (bfn)
{
case RSLC_BFN_ATOMICADD: op = SpvOpAtomicIAdd; break;
case RSLC_BFN_ATOMICMIN: op = sgn ? SpvOpAtomicSMin : SpvOpAtomicUMin; break;
case RSLC_BFN_ATOMICMAX: op = sgn ? SpvOpAtomicSMax : SpvOpAtomicUMax; break;
case RSLC_BFN_ATOMICAND: op = SpvOpAtomicAnd; break;
case RSLC_BFN_ATOMICOR: op = SpvOpAtomicOr; break;
case RSLC_BFN_ATOMICXOR: op = SpvOpAtomicXor; break;
case RSLC_BFN_ATOMICEXCHANGE: op = SpvOpAtomicExchange; break;
default:
{
/* atomicCompSwap(mem, compare, data): data, then the comparator */
uint32_t data = spv_rvalue(e, rslc_list_item(e->c, n->b, 2));
if (!data)
return 0;
w[0] = ptr; w[1] = scope; w[2] = sem; w[3] = sem; w[4] = data; w[5] = val;
return spv_fn_result(e, SpvOpAtomicCompareExchange, tid, w, 6, prec);
}
}
w[0] = ptr; w[1] = scope; w[2] = sem; w[3] = val;
return spv_fn_result(e, op, tid, w, 4, prec);
}
#endif
if (bfn == RSLC_BFN_UADDCARRY || bfn == RSLC_BFN_USUBBORROW || bfn == RSLC_BFN_UMULEXTENDED
|| bfn == RSLC_BFN_IMULEXTENDED)
{
@@ -4421,6 +4482,23 @@ static bool spv_block(struct spv_emit *e, unsigned idx)
stid = spv_type_laid(e, b->type, std140);
if (!stid)
return false;
#ifdef RSLC_EXTENDED
/* SPIR-V 1.0: a storage buffer is a BufferBlock in the Uniform class */
if (b->buffer)
{
uint32_t k;
if (!spv_decorate(e, stid, SPV_DEC_BUFFERBLOCK))
return false;
for (k = 0; k < b->type->nfields; k++)
{
if (b->readonly && !spv_mdecorate(e, stid, k, SPV_DEC_NONWRITABLE))
return false;
if (b->coherent && !spv_mdecorate(e, stid, k, SPV_DEC_COHERENT))
return false;
}
}
else
#endif
if (!spv_decorate(e, stid, SPV_DEC_BLOCK))
return false;
ptr = spv_ptr(e, stid, sc);
+8
View File
@@ -216,6 +216,8 @@ enum rslc_kw
RSLC_KW_SAMPLER2DARRAY, RSLC_KW_ISAMPLER2DARRAY, RSLC_KW_USAMPLER2DARRAY,
RSLC_KW_SAMPLER2DMSARRAY, RSLC_KW_ISAMPLER2DMSARRAY, RSLC_KW_USAMPLER2DMSARRAY,
RSLC_KW_SAMPLERBUFFER, RSLC_KW_ISAMPLERBUFFER, RSLC_KW_USAMPLERBUFFER,
RSLC_KW_BUFFER, RSLC_KW_READONLY, RSLC_KW_WRITEONLY, RSLC_KW_COHERENT,
RSLC_KW_RESTRICT, RSLC_KW_VOLATILE,
RSLC_KW_COUNT
};
@@ -385,6 +387,9 @@ struct rslc_blockdecl
uint32_t line;
bool push;
bool std140; /* layout; push blocks default std430 */
bool buffer; /* RSLC_EXTENDED: a storage buffer, std430 by default */
bool readonly;
bool coherent;
};
struct rslc_func
@@ -527,6 +532,9 @@ enum rslc_bfn
RSLC_BFN_DFDX, RSLC_BFN_DFDY, RSLC_BFN_FWIDTH, RSLC_BFN_DFDXFINE, RSLC_BFN_DFDYFINE,
RSLC_BFN_FWIDTHFINE, RSLC_BFN_DFDXCOARSE, RSLC_BFN_DFDYCOARSE, RSLC_BFN_FWIDTHCOARSE,
RSLC_BFN_EMITVERTEX, RSLC_BFN_ENDPRIMITIVE, /* RSLC_EXTENDED, geometry */
/* RSLC_EXTENDED: on a member of a storage buffer */
RSLC_BFN_ATOMICADD, RSLC_BFN_ATOMICMIN, RSLC_BFN_ATOMICMAX, RSLC_BFN_ATOMICAND,
RSLC_BFN_ATOMICOR, RSLC_BFN_ATOMICXOR, RSLC_BFN_ATOMICEXCHANGE, RSLC_BFN_ATOMICCOMPSWAP,
RSLC_BFN_COUNT
};
+2 -1
View File
@@ -47,7 +47,8 @@
* outputs (layout index), layout(early_fragment_tests), and a vertex
* stage's gl_ClipDistance / gl_CullDistance (sized by the highest
* constant element written; HLSL and MSL refuse them yet), in / out
* interface blocks, and RSLC_STAGE_GEOMETRY (SPIR-V and GLSL only).
* interface blocks, RSLC_STAGE_GEOMETRY (SPIR-V and GLSL only), and
* storage buffers with the atomic builtins (SPIR-V and GLSL only).
* Without it
* none of that is accepted, nothing is compiled in for it, and every
* destination is word for word what it was. MSL has no form of the
@@ -428,7 +428,7 @@ void VulkanContext::PopulateBackendInfo(BackendInfo* backend_info)
backend_info->bSupportsPaletteConversion = true; // Assumed support.
backend_info->bSupportsClipControl = true; // Assumed support.
backend_info->bSupportsMultithreading = true; // Assumed support.
// rslc has no compute stage, storage buffers or atomics yet.
// rslc has no compute stage yet.
backend_info->bSupportsComputeShaders = false;
backend_info->bSupportsGPUTextureDecoding = false;
backend_info->bSupportsBitfield = true; // Assumed support.
@@ -456,8 +456,8 @@ void VulkanContext::PopulateBackendInfo(BackendInfo* backend_info)
backend_info->bSupportsLodBiasInSampler = false; // Dependent on OS.
backend_info->bSupportsSettingObjectNames = false; // Dependent on features.
backend_info->bSupportsPartialMultisampleResolve = true; // Assumed support.
backend_info->bSupportsDynamicVertexLoader = false; // Needs storage buffers.
backend_info->bSupportsVSLinePointExpand = false; // Needs storage buffers.
backend_info->bSupportsDynamicVertexLoader = true; // Assumed support.
backend_info->bSupportsVSLinePointExpand = true; // Assumed support.
backend_info->bSupportsHDROutput = true; // Assumed support.
backend_info->bSupportsUnrestrictedDepthRange = false; // Dependent on features.
}
@@ -481,8 +481,8 @@ void VulkanContext::PopulateBackendInfoFeatures(BackendInfo* backend_info, VkPhy
backend_info->bSupportsDualSourceBlend = info.dualSrcBlend;
backend_info->bSupportsGeometryShaders = info.geometryShader;
backend_info->bSupportsGSInstancing = info.geometryShader;
// The bounding box needs storage buffers and atomics, which rslc has no form of yet.
backend_info->bSupportsBBox = backend_info->bSupportsFragmentStoresAndAtomics = false;
backend_info->bSupportsBBox = backend_info->bSupportsFragmentStoresAndAtomics =
info.fragmentStoresAndAtomics;
backend_info->bSupportsSSAA = info.sampleRateShading;
backend_info->bSupportsLogicOp = info.logicOp;