Metal: framebuffer fetch again, through rslc's subpass input

The vendored rslc reads a subpassInput and prints it to MSL as the
colour attachment's [[color(N)]] value, so the pixel header defines
INPUT_ATTACHMENT_BINDING and FB_FETCH_VALUE again and the backend
reports framebuffer fetch on Apple GPUs and on the Intel GPUs whose
probe draws a pixel that reads back doubled.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-11-06 15:10:21 +01:00
1 parent f073fc9448
commit e13a260b8c
7 files changed
+240 -17

No files matched your search

+38 -4
View File
@@ -341,6 +341,11 @@ static void pr_type(struct rslc_pr *p, const struct rslc_type *t)
pr_s(p, t->kind == RSLC_TY_SAMPLER2DARRAY ? "sampler2DArray"
: t->kind == RSLC_TY_SAMPLER2DMSARRAY ? "sampler2DMSArray" : "samplerBuffer");
break;
case RSLC_TY_SUBPASSINPUT:
/* MSL alone prints one: the attachment's value, read as a
* fragment input */
pr_s(p, "float4");
break;
case RSLC_TY_IMAGE2D:
case RSLC_TY_IMAGE2DARRAY:
if (p->hlsl)
@@ -436,8 +441,10 @@ static const char *pr_block_instance(struct rslc_pr *p, unsigned block)
{
const struct rslc_blockdecl *b = &p->c->prog.blocks[block];
const char *forced = NULL;
/* the frontend's names are for its two blocks; further ones keep theirs */
if (p->names && block < 2)
/* the frontend's names are for its two blocks; further ones keep
* theirs. Without RSLC_EXTENDED every block is one of those two,
* so the index test folds away. */
if (p->names && (RSLC_MAX_BLOCKS == 2 || block < 2))
forced = b->push ? p->names->push_instance : p->names->ubo_instance;
if (forced)
return forced;
@@ -1903,6 +1910,9 @@ static void pr_msl_builtin(struct rslc_pr *p, const struct rslc_node *n)
}
return;
}
case RSLC_BFN_SUBPASSLOAD:
pr_expr(p, rslc_list_item(p->c, list, 0), PR_PREC_POSTFIX);
return;
case RSLC_BFN_IMAGESIZE:
{
const struct rslc_type *it = pr_arg_type(p, list, 0);
@@ -2481,7 +2491,7 @@ static void pr_msl_env(struct rslc_pr *p, const struct rslc_func *f, bool decl,
continue;
}
#ifdef RSLC_EXTENDED
if (v->storage == RSLC_ST_IMAGE)
if (v->storage == RSLC_ST_IMAGE || v->storage == RSLC_ST_SUBPASS)
{
if (decl)
{
@@ -2618,7 +2628,7 @@ static void pr_msl_env_mark(struct rslc_pr *p, uint8_t *env, uint32_t node, bool
case RSLC_ST_IN: case RSLC_ST_OUT: case RSLC_ST_BUILTIN:
case RSLC_ST_GLOBAL: case RSLC_ST_SAMPLER:
#ifdef RSLC_EXTENDED
case RSLC_ST_IMAGE: case RSLC_ST_SHARED:
case RSLC_ST_IMAGE: case RSLC_ST_SHARED: case RSLC_ST_SUBPASS:
#endif
slot = n->a;
break;
@@ -6356,6 +6366,21 @@ static bool pr_msl(struct rslc_pr *p, struct rslc_result *result)
pr_u(p, pr_msl_index(v->binding));
pr_s(p, ")]]");
}
/* an input attachment is the colour attachment's value on entry */
for (i = 1; i < pr->nvars; i++)
{
const struct rslc_var *v = pr->vars[i];
if (v->storage != RSLC_ST_SUBPASS || !menv[i])
continue;
if (!first)
pr_s(p, ", ");
first = false;
pr_s(p, "float4 ");
pr_var_name(p, v);
pr_s(p, " [[color(");
pr_u(p, v->member);
pr_s(p, ")]]");
}
#endif
for (i = 1; i < pr->nvars; i++)
{
@@ -6636,6 +6661,15 @@ bool rslc_emit_text(struct rslc_ctx *c, struct rslc_result *result)
rslc_err(c, p->hlsl ? "hlsl: no geometry stage yet" : "msl: no geometry stage", NULL, 0);
return false;
}
/* an input attachment has a form in MSL alone */
if (!p->msl)
for (k = 1; k < pr->nvars; k++)
if (pr->vars[k]->storage == RSLC_ST_SUBPASS)
{
rslc_err(c, p->hlsl ? "hlsl: no form of subpassInput" : "glsl: no form of subpassInput",
NULL, pr->vars[k]->line);
return false;
}
if (p->hlsl && c->stage == RSLC_STAGE_COMPUTE)
{
uint32_t k;
+29 -2
View File
@@ -1698,6 +1698,7 @@ struct rslc_quals
bool early_fragment_tests;
uint8_t gs_prim; /* RSLC_GS_* named in the layout */
uint32_t index;
uint32_t input_att; /* input_attachment_index; RSLC_UNSET if absent */
uint32_t gs_invocations;
uint32_t gs_max_vertices;
#endif
@@ -2958,6 +2959,7 @@ static bool rslc_p_eval(struct rslc_parser *p, uint32_t e, struct rslc_cval *out
* A N Z sampler2DArray, sampler2DMSArray, samplerBuffer
* J K image2D, image2DArray; g the gvec4 of the image's base,
* matched exactly
* P subpassInput
* (RSLC_EXTENDED; their overloads are absent otherwise)
* M any matrix; Q square matrix; T transposed M result
* X any scalar or vector, same type as other X
@@ -3075,7 +3077,8 @@ static const struct rslc_bsig rslc_bsigs[] = {
{ RSLC_BFN_GROUPMEMORYBARRIER, "groupMemoryBarrier", ":v" },
{ RSLC_BFN_IMAGESTORE, "imageStore", "J i2 g:v;K i3 g:v" },
{ RSLC_BFN_IMAGELOAD, "imageLoad", "J i2:4;K i3:4" },
{ RSLC_BFN_IMAGESIZE, "imageSize", "J:i2;K:i3" }
{ RSLC_BFN_IMAGESIZE, "imageSize", "J:i2;K:i3" },
{ RSLC_BFN_SUBPASSLOAD, "subpassLoad", "P:4" }
#endif
};
@@ -3165,6 +3168,8 @@ static bool rslc_bsig_match_tok(struct rslc_parser *p, const char *tok, unsigned
return true;
}
#ifdef RSLC_EXTENDED
else if (len == 1 && tok[0] == 'P')
return t->kind == RSLC_TY_SUBPASSINPUT;
else if (len == 1 && (tok[0] == 'J' || tok[0] == 'K'))
{
if (t->kind != (tok[0] == 'J' ? RSLC_TY_IMAGE2D : RSLC_TY_IMAGE2DARRAY))
@@ -3261,7 +3266,7 @@ static const struct rslc_type *rslc_bsig_param_type(struct rslc_parser *p,
if (len == 1 && (tok[0] == 'S' || tok[0] == 'A' || tok[0] == 'N' || tok[0] == 'Z'))
return actual;
#ifdef RSLC_EXTENDED
if (len == 1 && (tok[0] == 'J' || tok[0] == 'K'))
if (len == 1 && (tok[0] == 'J' || tok[0] == 'K' || tok[0] == 'P'))
return actual;
if (len == 1 && tok[0] == 'g')
return actual; /* matched exactly */
@@ -4525,6 +4530,7 @@ static bool rslc_p_layout(struct rslc_parser *p, struct rslc_quals *q)
else if (len == 12 && !memcmp(s, "local_size_x", 12)) slot = &q->cs_local[0];
else if (len == 12 && !memcmp(s, "local_size_y", 12)) slot = &q->cs_local[1];
else if (len == 12 && !memcmp(s, "local_size_z", 12)) slot = &q->cs_local[2];
else if (len == 22 && !memcmp(s, "input_attachment_index", 22)) slot = &q->input_att;
else if (rslc_p_image_format(s, len, &q->img_fmt)) { }
#endif
else if (len == 7 && !memcmp(s, "index", 5)) { }
@@ -4554,6 +4560,9 @@ static bool rslc_p_quals(struct rslc_parser *p, struct rslc_quals *q)
{
memset(q, 0, sizeof(*q));
q->location = q->binding = q->set = q->offset = RSLC_UNSET;
#ifdef RSLC_EXTENDED
q->input_att = RSLC_UNSET;
#endif
for (;;)
{
const struct rslc_tok *t = P_TOK(p);
@@ -4657,6 +4666,8 @@ static const struct rslc_type *rslc_p_type_spec(struct rslc_parser *p,
ty = rslc_type_sampler2d(p->c, RSLC_BASE_INT);
else if (k == RSLC_KW_USAMPLER2D)
ty = rslc_type_sampler2d(p->c, RSLC_BASE_UINT);
else if (k == RSLC_KW_SUBPASSINPUT)
ty = rslc_type_basic(p->c, RSLC_TY_SUBPASSINPUT, RSLC_BASE_FLOAT, 1, 1);
else if (k >= RSLC_KW_IMAGE2D && k <= RSLC_KW_UIMAGE2DARRAY)
{
/* the format is the declaration's layout; it is applied there */
@@ -5251,6 +5262,11 @@ static bool rslc_p_params(struct rslc_parser *p, struct rslc_func *f)
ty = rslc_p_array_suffix(p, ty);
if (!ty)
return false;
#ifdef RSLC_EXTENDED
/* the printers hand it over as the value it reads */
if (ty->kind == RSLC_TY_SUBPASSINPUT)
return rslc_p_fail(p, "a subpassInput cannot be a parameter", NULL);
#endif
v = rslc_p_new_var(p, name, ty, line);
if (!v)
return false;
@@ -5930,6 +5946,17 @@ static uint32_t rslc_p_declaration(struct rslc_parser *p, bool global)
if (q.writeonly)
v->quals |= RSLC_VQ_WRITEONLY;
}
else if (vt->kind == RSLC_TY_SUBPASSINPUT)
{
/* an input attachment: the fragment stage's framebuffer read */
if (!global || !q.is_uniform || p->c->stage != RSLC_STAGE_FRAGMENT)
return rslc_p_fail(p, "a subpassInput must be a fragment stage\'s global uniform", NULL), 0;
if (q.input_att == RSLC_UNSET)
return rslc_p_fail(p, "a subpassInput needs its input_attachment_index", NULL), 0;
v->storage = RSLC_ST_SUBPASS;
v->member = q.input_att;
v->precision = 0;
}
else if (q.is_shared)
{
if (!global || p->c->stage != RSLC_STAGE_COMPUTE)
+41
View File
@@ -417,6 +417,7 @@ static const char *const rslc_keywords[] = {
"sampler2DMSArray", "isampler2DMSArray", "usampler2DMSArray",
"samplerBuffer", "isamplerBuffer", "usamplerBuffer",
"image2D", "iimage2D", "uimage2D", "image2DArray", "iimage2DArray", "uimage2DArray",
"subpassInput",
"buffer", "readonly", "writeonly", "coherent", "restrict", "volatile", "shared"
#endif
};
@@ -999,6 +1000,7 @@ enum spv_section
#define SPV_CAP_CLIPDISTANCE 32
#define SPV_CAP_CULLDISTANCE 33
#define SPV_CAP_GEOMETRY 2
#define SPV_CAP_INPUTATTACHMENT 40
#define SPV_MODEL_VERTEX 0
#define SPV_MODEL_FRAGMENT 4
#define SPV_MODEL_GEOMETRY 3
@@ -1039,6 +1041,7 @@ enum spv_section
#define SPV_DEC_INDEX 32
#define SPV_DEC_BINDING 33
#define SPV_DEC_DESCRIPTORSET 34
#define SPV_DEC_INPUTATTACHMENTINDEX 43
#define SPV_DEC_OFFSET 35
#define SPV_BI_POSITION 0
@@ -1207,6 +1210,7 @@ struct spv_emit
uint32_t cur_label; /* current block's label, 0 = terminated */
uint32_t entry_label;
bool need_imagequery;
bool need_inputattachment;
bool need_sampledbuffer;
bool need_samplerateshading;
bool need_clipdistance;
@@ -1577,6 +1581,20 @@ static uint32_t spv_type(struct spv_emit *e, const struct rslc_type *t)
ti->img = id;
break;
}
case RSLC_TY_SUBPASSINPUT:
{
/* Dim SubpassData, Sampled 2, format Unknown */
uint32_t s = spv_type(e, rslc_type_basic(e->c, RSLC_TY_SCALAR, RSLC_BASE_FLOAT, 1, 1));
if (!s)
return 0;
ti = &e->tinfo[t->id];
w[0] = id; w[1] = s; w[2] = 6; w[3] = 0; w[4] = 0; w[5] = 0; w[6] = 2; w[7] = 0;
if (!spv_inst(e, SPV_SEC_TYPES, SpvOpTypeImage, w, 8))
return 0;
ti->img = id;
e->need_inputattachment = true;
break;
}
#endif
case RSLC_TY_ARRAY:
{
@@ -3260,6 +3278,16 @@ static uint32_t spv_builtin_node(struct spv_emit *e, const struct rslc_node *n)
case RSLC_BFN_IMAGESIZE:
e->need_imagequery = true;
return spv_r1(e, SpvOpImageQuerySize, tid, args[0], prec);
case RSLC_BFN_SUBPASSLOAD:
{
/* the fragment's own texel: coordinate (0, 0) */
static const uint32_t zero[2] = { 0, 0 };
uint32_t used, coord = spv_const_composite(e,
rslc_type_basic(e->c, RSLC_TY_VEC, RSLC_BASE_INT, 2, 1), zero, &used);
if (!coord)
return 0;
return spv_r2(e, SpvOpImageRead, tid, args[0], coord, prec);
}
case RSLC_BFN_EMITVERTEX:
case RSLC_BFN_ENDPRIMITIVE:
/* no result: the void type's id stands for the expression */
@@ -4468,6 +4496,7 @@ static bool spv_global_var(struct spv_emit *e, const struct rslc_var *v)
case RSLC_ST_GLOBAL: sc = SPV_SC_PRIVATE; break;
#ifdef RSLC_EXTENDED
case RSLC_ST_IMAGE: sc = SPV_SC_UNIFORMCONSTANT; break;
case RSLC_ST_SUBPASS: sc = SPV_SC_UNIFORMCONSTANT; break;
case RSLC_ST_SHARED: sc = SPV_SC_WORKGROUP; break;
#endif
case RSLC_ST_CONST:
@@ -4511,6 +4540,13 @@ static bool spv_global_var(struct spv_emit *e, const struct rslc_var *v)
if ((v->quals & RSLC_VQ_READONLY) && !spv_decorate(e, id, SPV_DEC_NONWRITABLE))
return false;
}
if (v->storage == RSLC_ST_SUBPASS)
{
if (!spv_decorate1(e, id, SPV_DEC_DESCRIPTORSET, v->set == RSLC_UNSET ? 0 : v->set)
|| !spv_decorate1(e, id, SPV_DEC_BINDING, v->binding == RSLC_UNSET ? 0 : v->binding)
|| !spv_decorate1(e, id, SPV_DEC_INPUTATTACHMENTINDEX, v->member))
return false;
}
#endif
if (v->storage == RSLC_ST_SAMPLER)
{
@@ -5003,6 +5039,11 @@ static bool rslc_emit_spirv(struct rslc_ctx *c, struct rslc_result *result)
return false;
if (e->need_imagequery && !spv_inst1(e, SPV_SEC_CAPS, SpvOpCapability, SPV_CAP_IMAGEQUERY))
return false;
#ifdef RSLC_EXTENDED
if (e->need_inputattachment
&& !spv_inst1(e, SPV_SEC_CAPS, SpvOpCapability, SPV_CAP_INPUTATTACHMENT))
return false;
#endif
if (!spv_inst_str(e, SPV_SEC_CAPS, SpvOpExtInstImport, &e->ext_id, 1, "GLSL.std.450", NULL, 0))
return false;
if (!spv_inst2(e, SPV_SEC_CAPS, SpvOpMemoryModel, 0 /* Logical */, 1 /* GLSL450 */))
+12 -4
View File
@@ -219,6 +219,8 @@ enum rslc_kw
/* RSLC_EXTENDED image types: float, int, uint of each */
RSLC_KW_IMAGE2D, RSLC_KW_IIMAGE2D, RSLC_KW_UIMAGE2D,
RSLC_KW_IMAGE2DARRAY, RSLC_KW_IIMAGE2DARRAY, RSLC_KW_UIMAGE2DARRAY,
/* RSLC_EXTENDED: a fragment stage's input attachment */
RSLC_KW_SUBPASSINPUT,
/* RSLC_EXTENDED qualifiers */
RSLC_KW_BUFFER, RSLC_KW_READONLY, RSLC_KW_WRITEONLY, RSLC_KW_COHERENT,
RSLC_KW_RESTRICT, RSLC_KW_VOLATILE, RSLC_KW_SHARED,
@@ -249,7 +251,9 @@ enum rslc_tkind
RSLC_TY_SAMPLERBUFFER,
/* RSLC_EXTENDED storage images; cols is the RSLC_IMGFMT_* */
RSLC_TY_IMAGE2D,
RSLC_TY_IMAGE2DARRAY
RSLC_TY_IMAGE2DARRAY,
/* RSLC_EXTENDED: a float input attachment */
RSLC_TY_SUBPASSINPUT
};
#define RSLC_TY_IS_IMAGE(k) ((k) == RSLC_TY_IMAGE2D || (k) == RSLC_TY_IMAGE2DARRAY)
@@ -274,7 +278,7 @@ enum rslc_tkind
#define RSLC_IMGFMT_R32UI 33
/* The last keyword that names a type. */
#define RSLC_KW_LAST_TYPE RSLC_KW_UIMAGE2DARRAY
#define RSLC_KW_LAST_TYPE RSLC_KW_SUBPASSINPUT
/* Every sampler kind: sampler2D, and the RSLC_EXTENDED ones. */
#define RSLC_TY_IS_SAMPLER(k) ((k) == RSLC_TY_SAMPLER2D \
@@ -326,7 +330,9 @@ enum rslc_storage
RSLC_ST_MEMBER, /* a member of an unnamed block, visible bare */
RSLC_ST_BUILTIN, /* gl_Position and friends */
RSLC_ST_SHARED, /* RSLC_EXTENDED: compute workgroup memory */
RSLC_ST_IMAGE /* RSLC_EXTENDED: uniform image2D and the like */
RSLC_ST_IMAGE, /* RSLC_EXTENDED: uniform image2D and the like */
RSLC_ST_SUBPASS /* RSLC_EXTENDED: uniform subpassInput; member is
* its input_attachment_index */
};
#define RSLC_DIR_IN 0
@@ -364,7 +370,8 @@ struct rslc_var
uint32_t binding;
uint32_t set;
uint32_t block; /* RSLC_ST_MEMBER / BLOCK: block index */
uint32_t member; /* RSLC_ST_MEMBER: field index */
uint32_t member; /* RSLC_ST_MEMBER: field index; SUBPASS:
* input_attachment_index */
uint8_t storage; /* enum rslc_storage */
uint8_t dir; /* RSLC_DIR_* for params */
uint8_t interp;
@@ -583,6 +590,7 @@ enum rslc_bfn
RSLC_BFN_BARRIER, RSLC_BFN_MEMORYBARRIER, RSLC_BFN_MEMORYBARRIERSHARED,
RSLC_BFN_MEMORYBARRIERBUFFER, RSLC_BFN_MEMORYBARRIERIMAGE, RSLC_BFN_GROUPMEMORYBARRIER,
RSLC_BFN_IMAGESTORE, RSLC_BFN_IMAGELOAD, RSLC_BFN_IMAGESIZE,
RSLC_BFN_SUBPASSLOAD, /* RSLC_EXTENDED, fragment */
RSLC_BFN_COUNT
};
+118 -3
View File
@@ -94,6 +94,115 @@ void Metal::Util::PopulateBackendInfoAdapters(BackendInfo* backend_info,
}
}
#if TARGET_OS_OSX
/* Some Intel GPUs fetch from the framebuffer though no feature set says
* so, and others build the pipeline but leave the fetch out: draw one
* pixel whose shader doubles what it reads, and check it doubled. */
static bool DetectIntelGPUFBFetch(id<MTLDevice> dev)
{
static const char shader[] =
"vertex float4 fs_triangle(uint vid [[vertex_id]]) {\n"
" return float4(vid & 1 ? 3 : -1, vid & 2 ? 3 : -1, 0, 1);\n"
"}\n"
"fragment float4 fbfetch_test(float4 in [[color(0), raster_order_group(0)]]) {\n"
" return in * 2;\n"
"}\n";
const u32 px_in = 0x11223344;
u32 px_out = 0;
bool ok = false;
id<MTLLibrary> lib;
id<MTLFunction> vs;
id<MTLFunction> fs;
MTLRenderPipelineDescriptor* pdesc;
id<MTLRenderPipelineState> pipe = nil;
id<MTLBuffer> buf = nil;
MTLTextureDescriptor* tdesc;
id<MTLTexture> tex = nil;
id<MTLCommandQueue> q = nil;
id<MTLCommandBuffer> cmdbuf;
id<MTLBlitCommandEncoder> blit;
MTLRenderPassDescriptor* rpdesc;
id<MTLRenderCommandEncoder> renc;
lib = [dev newLibraryWithSource:[NSString stringWithUTF8String:shader] options:nil error:nil];
if (!lib)
return false;
vs = [lib newFunctionWithName:@"fs_triangle"];
fs = [lib newFunctionWithName:@"fbfetch_test"];
pdesc = [MTLRenderPipelineDescriptor new];
[pdesc setVertexFunction:vs];
[pdesc setFragmentFunction:fs];
[[pdesc colorAttachments][0] setPixelFormat:MTLPixelFormatRGBA8Unorm];
if (vs && fs)
pipe = [dev newRenderPipelineStateWithDescriptor:pdesc error:nil];
if (pipe)
{
buf = [dev newBufferWithLength:4 options:MTLResourceStorageModeShared];
memcpy([buf contents], &px_in, sizeof(px_in));
tdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
width:1
height:1
mipmapped:false];
[tdesc setUsage:MTLTextureUsageRenderTarget];
tex = [dev newTextureWithDescriptor:tdesc];
q = [dev newCommandQueue];
cmdbuf = [q commandBuffer];
blit = [cmdbuf blitCommandEncoder];
[blit copyFromBuffer:buf
sourceOffset:0
sourceBytesPerRow:4
sourceBytesPerImage:4
sourceSize:MTLSizeMake(1, 1, 1)
toTexture:tex
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blit endEncoding];
rpdesc = [MTLRenderPassDescriptor new];
[[rpdesc colorAttachments][0] setTexture:tex];
[[rpdesc colorAttachments][0] setLoadAction:MTLLoadActionLoad];
[[rpdesc colorAttachments][0] setStoreAction:MTLStoreActionStore];
renc = [cmdbuf renderCommandEncoderWithDescriptor:rpdesc];
[renc setRenderPipelineState:pipe];
[renc drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
[renc endEncoding];
[rpdesc release];
blit = [cmdbuf blitCommandEncoder];
[blit copyFromTexture:tex
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(0, 0, 0)
sourceSize:MTLSizeMake(1, 1, 1)
toBuffer:buf
destinationOffset:0
destinationBytesPerRow:4
destinationBytesPerImage:4];
[blit endEncoding];
[cmdbuf commit];
[cmdbuf waitUntilCompleted];
memcpy(&px_out, [buf contents], sizeof(px_out));
ok = [cmdbuf status] == MTLCommandBufferStatusCompleted;
}
[q release];
[tex release];
[buf release];
[pipe release];
[pdesc release];
[fs release];
[vs release];
[lib release];
/* Skylake and later double it; Broadwell leaves it, Haswell blackens it */
return ok && px_out == 0x22446688;
}
#endif
void Metal::Util::PopulateBackendInfoFeatures(const VideoConfig& config, BackendInfo* backend_info,
id<MTLDevice> device)
{
@@ -152,9 +261,11 @@ void Metal::Util::PopulateBackendInfoFeatures(const VideoConfig& config, Backend
break;
}
/* rslc has no form of a subpass input: blending is not emulated
* through framebuffer fetch */
backend_info->bSupportsFramebufferFetch = false;
backend_info->bSupportsFramebufferFetch = [device supportsFamily:MTLGPUFamilyApple1];
#if TARGET_OS_OSX
if (vendor == DriverDetails::VENDOR_INTEL)
backend_info->bSupportsFramebufferFetch |= DetectIntelGPUFBFetch(device);
#endif
if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DYNAMIC_SAMPLER_INDEXING))
backend_info->bSupportsDynamicSamplerIndexing = false;
@@ -268,6 +379,10 @@ static const std::string_view PIXEL_SHADER_HEADER = R"(
#define FORCE_EARLY_Z layout(early_fragment_tests) in
#define UBO_BINDING(packing, x) layout(packing, binding = ((x) - 1))
#define SSBO_BINDING(x) layout(std430, binding = ((x) + 2))
#define INPUT_ATTACHMENT_BINDING(x, y, z) layout(set = x, binding = y, input_attachment_index = z)
// Metal framebuffer fetch helpers.
#define FB_FETCH_VALUE subpassLoad(in_ocol0)
// hlsl to glsl function translation
#define API_METAL 1
+1 -2
View File
@@ -831,8 +831,7 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
if (use_framebuffer_fetch)
{
// A subpass input for the framebuffer read; no backend reports framebuffer fetch
// here while rslc has no form of one.
// rslc prints a subpass input as Metal's framebuffer fetch.
out.Write("INPUT_ATTACHMENT_BINDING(0, 0, 0) uniform subpassInput in_ocol0;\n");
}
}
+1 -2
View File
@@ -98,8 +98,7 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
if (use_framebuffer_fetch)
{
// A subpass input for the framebuffer read; no backend reports framebuffer fetch
// here while rslc has no form of one.
// rslc prints a subpass input as Metal's framebuffer fetch.
out.Write("INPUT_ATTACHMENT_BINDING(0, 0, 0) uniform subpassInput in_ocol0;\n");
}
}