diff --git a/Externals/libretro-common/gfx/shaders/rslc_emit.c b/Externals/libretro-common/gfx/shaders/rslc_emit.c index 4cadfa152a..3e2b9730c7 100644 --- a/Externals/libretro-common/gfx/shaders/rslc_emit.c +++ b/Externals/libretro-common/gfx/shaders/rslc_emit.c @@ -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; diff --git a/Externals/libretro-common/gfx/shaders/rslc_front.c b/Externals/libretro-common/gfx/shaders/rslc_front.c index 3b7e21960a..cf9314828f 100644 --- a/Externals/libretro-common/gfx/shaders/rslc_front.c +++ b/Externals/libretro-common/gfx/shaders/rslc_front.c @@ -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)) { diff --git a/Externals/libretro-common/gfx/shaders/rslc_ir.c b/Externals/libretro-common/gfx/shaders/rslc_ir.c index 5ae0be5931..45a7662868 100644 --- a/Externals/libretro-common/gfx/shaders/rslc_ir.c +++ b/Externals/libretro-common/gfx/shaders/rslc_ir.c @@ -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); diff --git a/Externals/libretro-common/gfx/shaders/rslc_share.h b/Externals/libretro-common/gfx/shaders/rslc_share.h index a34c3750b4..b749028806 100644 --- a/Externals/libretro-common/gfx/shaders/rslc_share.h +++ b/Externals/libretro-common/gfx/shaders/rslc_share.h @@ -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 }; diff --git a/Externals/libretro-common/include/gfx/shaders/rslc.h b/Externals/libretro-common/include/gfx/shaders/rslc.h index 8786d00f31..71e63873d0 100644 --- a/Externals/libretro-common/include/gfx/shaders/rslc.h +++ b/Externals/libretro-common/include/gfx/shaders/rslc.h @@ -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 diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index 447932e7bb..9fe513efc3 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -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;