From 74963469a169b49d61196e3a3b33a903ea8bfede Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Wed, 12 Jul 2017 11:07:45 -0700 Subject: Add basic reflection query for checking if entry point is "sample-rate" - This really just checks two basic things: 1. Was there any global variable declared with `in` and `sample`? 2. Did any code encountered during lowering referenece `gl_SampleIndex`? - This doesn't cover what HLSL could need, nor what we would need for cross-compilation. Consider it GLSL-specific for now. - In order to generate the information with even a reasonable chance of being accurate (not giving a ton of false positives) I tried to integrate the checks into the lowering process (so they only see code that is referenced, one hopes). - For this to work with my testing setup, I needed to make sure that lowering is always performed, prior to emitting reflection info - This change broke several reflection tests, because they had been using code that wouldn't actually pass the downstream compiler. I checked in fixes for those. --- slang.h | 8 ++++ source/slang/compiler.cpp | 26 ++++++++--- source/slang/emit.cpp | 16 ++++--- source/slang/lower.cpp | 43 +++++++++++++++++- source/slang/reflection.cpp | 17 ++++++++ source/slang/type-layout.h | 6 +++ tests/reflection/image-types.glsl | 6 ++- tests/reflection/multi-file-extra.hlsl | 4 +- tests/reflection/multi-file.hlsl | 4 +- tests/reflection/multi-file.hlsl.expected | 6 +-- tests/reflection/sample-rate-input.glsl | 15 +++++++ tests/reflection/sample-rate-input.glsl.expected | 55 ++++++++++++++++++++++++ tests/reflection/std430-layout.glsl | 1 + 13 files changed, 185 insertions(+), 22 deletions(-) create mode 100644 tests/reflection/sample-rate-input.glsl create mode 100644 tests/reflection/sample-rate-input.glsl.expected diff --git a/slang.h b/slang.h index 28940fd03..14efc26c7 100644 --- a/slang.h +++ b/slang.h @@ -531,6 +531,9 @@ extern "C" SlangUInt axisCount, SlangUInt* outSizeAlongAxis); + SLANG_API int spReflectionEntryPoint_usesAnySampleRateInput( + SlangReflectionEntryPoint* entryPoint); + // Shader Reflection SLANG_API unsigned spReflection_GetParameterCount(SlangReflection* reflection); @@ -893,6 +896,11 @@ namespace slang { return spReflectionEntryPoint_getComputeThreadGroupSize((SlangReflectionEntryPoint*) this, axisCount, outSizeAlongAxis); } + + bool usesAnySampleRateInput() + { + return 0 != spReflectionEntryPoint_usesAnySampleRateInput((SlangReflectionEntryPoint*) this); + } }; struct ShaderReflection diff --git a/source/slang/compiler.cpp b/source/slang/compiler.cpp index 3155bb75a..7df978707 100644 --- a/source/slang/compiler.cpp +++ b/source/slang/compiler.cpp @@ -565,13 +565,31 @@ namespace Slang void generateOutput( CompileRequest* compileRequest) { - // Allow for an "extra" target to verride things first. + // Start of with per-translation-unit and per-entry-point lowering + for( auto translationUnit : compileRequest->translationUnits ) + { + CompileResult translationUnitResult = emitTranslationUnit(translationUnit.Ptr()); + translationUnit->result = translationUnitResult; + } + + + // Allow for an "extra" target to verride things before we finish. switch (compileRequest->extraTarget) { case CodeGenTarget::ReflectionJSON: { String reflectionJSON = emitReflectionJSON(compileRequest->layout.Ptr()); + // Clobber existing output so we don't have to deal with it + for( auto translationUnit : compileRequest->translationUnits ) + { + translationUnit->result = CompileResult(); + } + for( auto entryPoint : compileRequest->entryPoints ) + { + entryPoint->result = CompileResult(); + } + // HACK(tfoley): just print it out since that is what people probably expect. // TODO: need a way to control where output gets routed across all possible targets. fprintf(stdout, "%s", reflectionJSON.begin()); @@ -584,12 +602,6 @@ namespace Slang break; } - // For most targets, we will do things per-translation-unit - for( auto translationUnit : compileRequest->translationUnits ) - { - CompileResult translationUnitResult = emitTranslationUnit(translationUnit.Ptr()); - translationUnit->result = translationUnitResult; - } } } diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 0c39a98bd..03cc7d8aa 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -2833,7 +2833,10 @@ struct EmitVisitor // Emit a single `regsiter` semantic, as appropriate for a given resource-type-specific layout info void emitHLSLRegisterSemantic( - VarLayout::ResourceInfo const& info) + VarLayout::ResourceInfo const& info, + + // Keyword to use in the uniform case (`register` for globals, `packoffset` inside a `cbuffer`) + char const* uniformSemanticSpelling = "register") { if( info.kind == LayoutResourceKind::Uniform ) { @@ -2845,7 +2848,9 @@ struct EmitVisitor // units, and then a "component" within that register, based on 4-byte // offsets from there. We cannot support more fine-grained offsets than that. - Emit(": packoffset(c"); + Emit(": "); + Emit(uniformSemanticSpelling); + Emit("(c"); // Size of a logical `c` register in bytes auto registerSize = 16; @@ -2907,7 +2912,8 @@ struct EmitVisitor // Emit all the `register` semantics that are appropriate for a particular variable layout void emitHLSLRegisterSemantics( - RefPtr layout) + RefPtr layout, + char const* uniformSemanticSpelling = "register") { if (!layout) return; @@ -2922,7 +2928,7 @@ struct EmitVisitor for( auto rr : layout->resourceInfos ) { - emitHLSLRegisterSemantic(rr); + emitHLSLRegisterSemantic(rr, uniformSemanticSpelling); } } @@ -3026,7 +3032,7 @@ struct EmitVisitor offsetResource.space += cbufferResource->space; } - emitHLSLRegisterSemantic(offsetResource); + emitHLSLRegisterSemantic(offsetResource, "packoffset"); } Emit(";\n"); diff --git a/source/slang/lower.cpp b/source/slang/lower.cpp index ecf23d9ed..02da47ccb 100644 --- a/source/slang/lower.cpp +++ b/source/slang/lower.cpp @@ -210,7 +210,8 @@ struct SharedLoweringContext { CompileRequest* compileRequest; - ProgramLayout* programLayout; + ProgramLayout* programLayout; + EntryPointLayout* entryPointLayout; // The target we are going to generate code for. // @@ -607,6 +608,8 @@ struct LoweringVisitor RefPtr visitVarExpressionSyntaxNode( VarExpressionSyntaxNode* expr) { + doSampleRateInputCheck(expr->name); + // If the expression didn't get resolved, we can leave it as-is if (!expr->declRef) return expr; @@ -1019,6 +1022,12 @@ struct LoweringVisitor RefPtr loweredStmt = new UnparsedStmt(); lowerStmtFields(loweredStmt, stmt); + for (auto token : stmt->tokens) + { + if (token.Type == TokenType::Identifier) + doSampleRateInputCheck(token.Content); + } + loweredStmt->tokens = stmt->tokens; addStmt(loweredStmt); @@ -2052,9 +2061,39 @@ struct LoweringVisitor return SourceLanguage::Unknown; } + void setSampleRateFlag() + { + shared->entryPointLayout->flags |= EntryPointLayout::Flag::usesAnySampleRateInput; + } + + void doSampleRateInputCheck(VarDeclBase* decl) + { + if (decl->HasModifier()) + { + setSampleRateFlag(); + } + } + + void doSampleRateInputCheck(String const& name) + { + if (name == "gl_SampleIndex") + { + setSampleRateFlag(); + } + } + RefPtr visitVariable( Variable* decl) { + // Global variable? Check if it is a sample-rate input. + if (dynamic_cast(decl->ParentDecl)) + { + if (decl->HasModifier()) + { + doSampleRateInputCheck(decl); + } + } + auto loweredDecl = lowerVarDeclCommon(decl, getClass()); if(!loweredDecl) return nullptr; @@ -2905,6 +2944,8 @@ LoweredEntryPoint lowerEntryPoint( bool isRewrite = isRewriteRequest(translationUnit->sourceLanguage, target); sharedContext.isRewrite = isRewrite; + sharedContext.entryPointLayout = visitor.findEntryPointLayout(entryPoint); + LoweredEntryPoint result; if (isRewrite) { diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp index 8e5044d13..1beacc21b 100644 --- a/source/slang/reflection.cpp +++ b/source/slang/reflection.cpp @@ -751,6 +751,18 @@ SLANG_API void spReflectionEntryPoint_getComputeThreadGroupSize( } } +SLANG_API int spReflectionEntryPoint_usesAnySampleRateInput( + SlangReflectionEntryPoint* inEntryPoint) +{ + auto entryPointLayout = convert(inEntryPoint); + if(!entryPointLayout) + return 0; + + if (entryPointLayout->profile.GetStage() != Stage::Fragment) + return 0; + + return (entryPointLayout->flags & EntryPointLayout::Flag::usesAnySampleRateInput) != 0; +} // Shader Reflection @@ -1497,6 +1509,11 @@ static void emitReflectionEntryPointJSON( write(writer, "\n]"); } + if (entryPoint->usesAnySampleRateInput()) + { + write(writer, ",\n\"usesAnySampleRateInput\": true"); + } + dedent(writer); write(writer, "\n}"); } diff --git a/source/slang/type-layout.h b/source/slang/type-layout.h index 1a63a4883..add9930ae 100644 --- a/source/slang/type-layout.h +++ b/source/slang/type-layout.h @@ -352,6 +352,12 @@ public: // Layout for any results of the entry point RefPtr resultLayout; + + enum Flag : unsigned + { + usesAnySampleRateInput = 0x1, + }; + unsigned flags = 0; }; // Layout information for the global scope of a program diff --git a/tests/reflection/image-types.glsl b/tests/reflection/image-types.glsl index 21cd2b629..73ecdaa82 100644 --- a/tests/reflection/image-types.glsl +++ b/tests/reflection/image-types.glsl @@ -2,9 +2,11 @@ // Confirm that we expose GLSL `image` types through reflection -uniform imageBuffer iBuffer; +layout(rgba32f) +uniform writeonly imageBuffer iBuffer; -uniform image2D i2D; +layout(rgba32f) +uniform writeonly image2D i2D; void main() {} diff --git a/tests/reflection/multi-file-extra.hlsl b/tests/reflection/multi-file-extra.hlsl index 569ec2ce9..a5da70635 100644 --- a/tests/reflection/multi-file-extra.hlsl +++ b/tests/reflection/multi-file-extra.hlsl @@ -18,7 +18,7 @@ float4 use(float val) { return val; }; float4 use(float2 val) { return float4(val,0.0,0.0); }; float4 use(float3 val) { return float4(val,0.0); }; float4 use(float4 val) { return val; }; -float4 use(Texture2D t, SamplerState s) { return t.Sample(s, 0.0); } +float4 use(Texture2D t, SamplerState s) { return t.SampleLevel(s, 0.0, 0.0); } // Start with some parameters that will appear in both shaders Texture2D sharedT; @@ -51,7 +51,7 @@ Texture2D sharedTV; Texture2D sharedTF; -float4 main() : SV_Target +float4 mainVS() : SV_Position { // Go ahead and use everything here, just to make sure things got placed correctly return use(sharedT, sharedS) diff --git a/tests/reflection/multi-file.hlsl b/tests/reflection/multi-file.hlsl index b263a6b71..7f79f08c9 100644 --- a/tests/reflection/multi-file.hlsl +++ b/tests/reflection/multi-file.hlsl @@ -1,4 +1,4 @@ -//TEST:SIMPLE:-profile ps_4_0 -target reflection-json Tests/bindings/multi-file-extra.hlsl +//TEST:SIMPLE:-profile ps_4_0 -entry mainFS -target reflection-json tests/reflection/multi-file-extra.hlsl -profile vs_4_0 -entry mainVS // Here we are testing the case where multiple translation units are provided // at once, so that we want combined reflection information for the resulting @@ -44,7 +44,7 @@ Texture2D sharedTV; Texture2D sharedTF; -float4 main() : SV_Position +float4 mainFS() : SV_Target { // Go ahead and use everything here, just to make sure things got placed correctly return use(sharedT, sharedS) diff --git a/tests/reflection/multi-file.hlsl.expected b/tests/reflection/multi-file.hlsl.expected index 3dc23c11c..4ad95fb35 100644 --- a/tests/reflection/multi-file.hlsl.expected +++ b/tests/reflection/multi-file.hlsl.expected @@ -236,12 +236,12 @@ standard output = { ], "entryPoints": [ { - "name": "main", + "name": "mainFS", "stage:": "fragment" }, { - "name": "main", - "stage:": "fragment" + "name": "mainVS", + "stage:": "vertex" } ] } diff --git a/tests/reflection/sample-rate-input.glsl b/tests/reflection/sample-rate-input.glsl new file mode 100644 index 000000000..66763f45d --- /dev/null +++ b/tests/reflection/sample-rate-input.glsl @@ -0,0 +1,15 @@ +//TEST(smoke):SIMPLE:-profile ps_4_0 -no-checking -target reflection-json + +// Check that we report sample-rate entry point input correctly + +uniform texture2D t; +uniform sampler s; + +sample in vec2 uv; + +out vec4 c; + +void main() +{ + c = texture(sampler2D(t,s), uv); +} diff --git a/tests/reflection/sample-rate-input.glsl.expected b/tests/reflection/sample-rate-input.glsl.expected new file mode 100644 index 000000000..5800a3630 --- /dev/null +++ b/tests/reflection/sample-rate-input.glsl.expected @@ -0,0 +1,55 @@ +result code = 0 +standard error = { +} +standard output = { +{ + "parameters": [ + { + "name": "t", + "binding": {"kind": "descriptorTableSlot", "index": 0}, + "type": { + "kind": "resource", + "baseShape": "texture2D" + } + }, + { + "name": "s", + "binding": {"kind": "descriptorTableSlot", "index": 1}, + "type": { + "kind": "samplerState" + } + }, + { + "name": "uv", + "binding": {"kind": "vertexInput", "index": 0}, + "type": { + "kind": "vector", + "elementCount": 2, + "elementType": { + "kind": "scalar", + "scalarType": "float32" + } + } + }, + { + "name": "c", + "binding": {"kind": "fragmentOutput", "index": 0}, + "type": { + "kind": "vector", + "elementCount": 4, + "elementType": { + "kind": "scalar", + "scalarType": "float32" + } + } + } + ], + "entryPoints": [ + { + "name": "main", + "stage:": "fragment", + "usesAnySampleRateInput": true + } + ] +} +} diff --git a/tests/reflection/std430-layout.glsl b/tests/reflection/std430-layout.glsl index 5d4dee88f..0e61556fc 100644 --- a/tests/reflection/std430-layout.glsl +++ b/tests/reflection/std430-layout.glsl @@ -1,3 +1,4 @@ +#version 450 //TEST(smoke):SIMPLE:-profile ps_4_0 -target reflection-json // Confirm fix for GitHub issue #55 -- cgit v1.2.3