diff options
| author | Theresa Foley <tfoleyNV@users.noreply.github.com> | 2021-06-25 15:50:29 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-06-25 15:50:29 -0700 |
| commit | 9dfaabe0fbcc9ac7314f84bde89c658b276298e3 (patch) | |
| tree | 8074c983b73860cac1ee7504f35b0f99aacb3dcd /source/slang/core.meta.slang | |
| parent | 81c1692a63486578f25ecaed652eab0d370120ee (diff) | |
Fixes related to combined texture/sampler types (#1894)
* Fixes related to combined texture/sampler types
Work on #1891
Our intention has always been to support combined texture/sampler types in Slang, both targets like OpenGL where that is the only option available and for targets like Vulkan where it can be beneficial to performance. Because Slang's current users mostly focus on D3D12+Vulkan codebases, they strongly prefer separate textures and samplers, and the relevant support code in Slang has "bit-rotted" over many releases until the functionality that was there isn't useful any more.
This change significantly overhauls the implementation of combined texture/sampler types and adds a test that uses them in the hopes of avoiding future regressions.
The new combined texture/sampler types use the prefix `Sampler`, so where there is an existing standalone `Texture2D` type the equivalent combined texture/sampler type will be `Sampler2D`. The intention is that this naming mirrors the GLSL conventions (where the type is `sampler2d`) while following HLSL naming precedent (to the extent it exists).
The operations available on a `Sampler2D` are intended to be those that are available on a `Texture2D`, and it is just that in cases where the `Texture2D` operation would take a separate sampler argument:
Texture2D t = ...;
SamplerState s = ...;
float4 result = t.Sample(s, uv);
the equivalent `Sampler2D` operation just elides that argument:
Sampler2D s = ...;
float4 result = s.Sample(uv);
In terms of implementation, there are a lot of subtle details here:
* I've tried to use the same metaprogramming logic that generates all the stdlib declarations for `Texture*` to also generate `Sampler*` in the hopes that this helps keep them in sync.
* The big catch to the above is that it means that for certain operations the indidces of parameters depend on whether or not an explicit sampler parameter is used. Rather than try to tweak the indices in the stdlib generation logic (which is already complicated) I went and added Yet Another Hack to the logic that handles intrinsic definition strings. Basically, the special-case handling of `$p` has been modified so that it *also* applies a negative offset to future parameter references in the same intrinsic string.
* Trying to actually bring this up in our test framework revealed that the "flat" reflection API was seemingly not reflecting combined texture/sampler types correctly at all (it was reflecting them as just plain textures). Other than that issue, the Vulkan path seems to work fine with combined texture/samplers.
* I also had to add logic to the `TEST_INPUT` parsing to re-introduce handling of the combined types (that was something I consciously left out to reduce the amount of code in the earlier refactor there). Luckily, the architecture is such that a combined texture/sampler can leverage most of the existing logic for the separate cases.
* fixup: reveiw feedback
Diffstat (limited to 'source/slang/core.meta.slang')
| -rw-r--r-- | source/slang/core.meta.slang | 146 |
1 files changed, 50 insertions, 96 deletions
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index 761316d86..d1dc25cc7 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -574,14 +574,14 @@ struct SamplerComparisonState ${{{{ static const struct { - char const* name; + char const* shapeName; TextureFlavor::Shape baseShape; int coordCount; } kBaseTextureTypes[] = { - { "Texture1D", TextureFlavor::Shape::Shape1D, 1 }, - { "Texture2D", TextureFlavor::Shape::Shape2D, 2 }, - { "Texture3D", TextureFlavor::Shape::Shape3D, 3 }, - { "TextureCube", TextureFlavor::Shape::ShapeCube, 3 }, + { "1D", TextureFlavor::Shape::Shape1D, 1 }, + { "2D", TextureFlavor::Shape::Shape2D, 2 }, + { "3D", TextureFlavor::Shape::Shape3D, 3 }, + { "Cube", TextureFlavor::Shape::ShapeCube,3 }, }; static const int kBaseTextureTypeCount = sizeof(kBaseTextureTypes) / sizeof(kBaseTextureTypes[0]); @@ -596,79 +596,21 @@ static const struct { }; static const int kBaseTextureAccessLevelCount = sizeof(kBaseTextureAccessLevels) / sizeof(kBaseTextureAccessLevels[0]); -// Declare the GLSL types here for compatibility... -// -// TODO: The stdlib should include a module that declares the GLSL types, to keep -// them separate... -for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) +static const struct TextureTypePrefixInfo { - char const* name = kBaseTextureTypes[tt].name; - TextureFlavor::Shape baseShape = kBaseTextureTypes[tt].baseShape; - - for (int isArray = 0; isArray < 2; ++isArray) - { - // Arrays of 3D textures aren't allowed - if (isArray && baseShape == TextureFlavor::Shape::Shape3D) continue; - - for (int isMultisample = 0; isMultisample < 2; ++isMultisample) - { - for (int accessLevel = 0; accessLevel < kBaseTextureAccessLevelCount; ++accessLevel) - { - auto access = kBaseTextureAccessLevels[accessLevel].access; - - // No such thing as RWTextureCube - if (access == SLANG_RESOURCE_ACCESS_READ_WRITE && baseShape == TextureFlavor::Shape::ShapeCube) - { - continue; - } - - // TODO: any constraints to enforce on what gets to be multisampled? - - unsigned flavor = baseShape; - if (isArray) flavor |= TextureFlavor::ArrayFlag; - if (isMultisample) flavor |= TextureFlavor::MultisampleFlag; - // if (isShadow) flavor |= TextureFlavor::ShadowFlag; - - flavor |= (access << 8); - - // emit a generic signature - // TODO: allow for multisample count to come in as well... - sb << "__generic<T = float4> "; - - sb << "__magic_type(TextureSampler," << int(flavor) << ")\n"; - sb << "__intrinsic_type(" << (kIROp_TextureSamplerType + (int(flavor) << kIROpMeta_OtherShift)) << ")\n"; - sb << "struct Sampler"; - sb << kBaseTextureAccessLevels[accessLevel].name; - sb << name; - if (isMultisample) sb << "MS"; - if (isArray) sb << "Array"; - // if (isShadow) sb << "Shadow"; - sb << "\n{\n"; - sb << "__specialized_for_target(glsl)\n"; - sb << "__init("; - sb << kBaseTextureAccessLevels[accessLevel].name; - sb << name; - if (isMultisample) sb << "MS"; - if (isArray) sb << "Array"; - sb << "<T> t, "; - sb << "SamplerState s);\n"; - sb << "};\n"; - - sb << "__specialized_for_target(glsl)\n"; - sb << "T texture<T>(Sampler"; - sb << kBaseTextureAccessLevels[accessLevel].name; - sb << name; - if (isMultisample) sb << "MS"; - if (isArray) sb << "Array"; - sb << "<T> t, float" << kBaseTextureTypes[tt].coordCount + isArray << " location);\n"; - } - } - } -} + char const* name; + bool combined; +} kTexturePrefixes[] = +{ + { "Texture", false }, + { "Sampler", true }, +}; +for(auto& prefixInfo : kTexturePrefixes) for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) { - char const* name = kBaseTextureTypes[tt].name; + char const* baseName = prefixInfo.name; + char const* baseShapeName = kBaseTextureTypes[tt].shapeName; TextureFlavor::Shape baseShape = kBaseTextureTypes[tt].baseShape; for (int isArray = 0; isArray < 2; ++isArray) @@ -701,22 +643,33 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) // TODO: allow for multisample count to come in as well... sb << "__generic<T = float4> "; - sb << "__magic_type(Texture," << int(flavor) << ")\n"; - sb << "__intrinsic_type(" << (kIROp_TextureType + (int(flavor) << kIROpMeta_OtherShift)) << ")\n"; + if(prefixInfo.combined) + { + sb << "__magic_type(TextureSampler," << int(flavor) << ")\n"; + sb << "__intrinsic_type(" << (kIROp_TextureSamplerType + (int(flavor) << kIROpMeta_OtherShift)) << ")\n"; + } + else + { + sb << "__magic_type(Texture," << int(flavor) << ")\n"; + sb << "__intrinsic_type(" << (kIROp_TextureType + (int(flavor) << kIROpMeta_OtherShift)) << ")\n"; + } sb << "struct "; sb << kBaseTextureAccessLevels[accessLevel].name; - sb << name; + sb << baseName; + sb << baseShapeName; if (isMultisample) sb << "MS"; if (isArray) sb << "Array"; // if (isShadow) sb << "Shadow"; sb << "\n{"; + char const* samplerStateParam = prefixInfo.combined ? "" : "SamplerState s, "; + if( !isMultisample ) { - sb << "float CalculateLevelOfDetail(SamplerState s, "; + sb << "float CalculateLevelOfDetail(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount << " location);\n"; - sb << "float CalculateLevelOfDetailUnclamped(SamplerState s, "; + sb << "float CalculateLevelOfDetailUnclamped(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount << " location);\n"; } @@ -1235,18 +1188,18 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) } } - sb << "T Sample(SamplerState s, "; + sb << "T Sample(" << samplerStateParam;; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location);\n"; if( baseShape != TextureFlavor::Shape::ShapeCube ) { sb << "__target_intrinsic(glsl, \"$ctextureOffset($p, $2, $3)$z\")\n"; - sb << "T Sample(SamplerState s, "; + sb << "T Sample(" << samplerStateParam;; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; } - sb << "T Sample(SamplerState s, "; + sb << "T Sample(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; if( baseShape != TextureFlavor::Shape::ShapeCube ) { @@ -1254,7 +1207,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) } sb << "float clamp);\n"; - sb << "T Sample(SamplerState s, "; + sb << "T Sample(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; if( baseShape != TextureFlavor::Shape::ShapeCube ) { @@ -1264,13 +1217,13 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) // `SampleBias()` sb << "__target_intrinsic(glsl, \"$ctexture($p, $2, $3)$z\")\n"; - sb << "T SampleBias(SamplerState s, "; + sb << "T SampleBias(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, float bias);\n"; if( baseShape != TextureFlavor::Shape::ShapeCube ) { sb << "__target_intrinsic(glsl, \"$ctextureOffset($p, $2, $3, $4)$z\")\n"; - sb << "T SampleBias(SamplerState s, "; + sb << "T SampleBias(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, float bias, "; sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; } @@ -1355,7 +1308,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) // the dimension. On CUDA there is texNDGrad, but it always just takes ddx, ddy. // I could just assume 0 for elements not supplied, and ignore z. For now will just leave sb << "__target_intrinsic(glsl, \"$ctextureGrad($p, $2, $3, $4)$z\")\n"; - sb << "T SampleGrad(SamplerState s, "; + sb << "T SampleGrad(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "float" << kBaseTextureTypes[tt].coordCount << " gradX, "; sb << "float" << kBaseTextureTypes[tt].coordCount << " gradY"; @@ -1364,7 +1317,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) if( baseShape != TextureFlavor::Shape::ShapeCube ) { sb << "__target_intrinsic(glsl, \"$ctextureGradOffset($p, $2, $3, $4, $5)$z\")\n"; - sb << "T SampleGrad(SamplerState s, "; + sb << "T SampleGrad(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "float" << kBaseTextureTypes[tt].coordCount << " gradX, "; sb << "float" << kBaseTextureTypes[tt].coordCount << " gradY, "; @@ -1372,7 +1325,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) sb << "__glsl_extension(GL_ARB_sparse_texture_clamp)"; sb << "__target_intrinsic(glsl, \"$ctextureGradOffsetClampARB($p, $2, $3, $4, $5, $6)$z\")\n"; - sb << "T SampleGrad(SamplerState s, "; + sb << "T SampleGrad(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "float" << kBaseTextureTypes[tt].coordCount << " gradX, "; sb << "float" << kBaseTextureTypes[tt].coordCount << " gradY, "; @@ -1428,14 +1381,14 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) } } - sb << "T SampleLevel(SamplerState s, "; + sb << "T SampleLevel(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "float level);\n"; if( baseShape != TextureFlavor::Shape::ShapeCube ) { sb << "__target_intrinsic(glsl, \"$ctextureLodOffset($p, $2, $3, $4)$z\")\n"; - sb << "T SampleLevel(SamplerState s, "; + sb << "T SampleLevel(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "float level, "; sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; @@ -1474,7 +1427,8 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) EMIT_LINE_DIRECTIVE(); sb << cc.genericPrefix << " __extension "; sb << kBaseTextureAccessLevels[accessLevel].name; - sb << name; + sb << baseName; + sb << baseShapeName; if (isArray) sb << "Array"; sb << "<" << cc.elementType << " >"; sb << "\n{\n"; @@ -1513,24 +1467,24 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) // "It is based on the base type of DataType except when readMode is equal to cudaReadModeNormalizedFloat (see Texture Reference API), in which case it is always float4." sb << "__target_intrinsic(cuda, \"tex2Dgather<$T0>($0, ($2).x, ($2).y, " << componentIndex << ")\")\n"; } - sb << outputType << " Gather" << componentName << "(SamplerState s, "; + sb << outputType << " Gather" << componentName << "(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location);\n"; EMIT_LINE_DIRECTIVE(); sb << "__target_intrinsic(glsl, \"textureGatherOffset($p, $2, $3, " << componentIndex << ")\")\n"; - sb << outputType << " Gather" << componentName << "(SamplerState s, "; + sb << outputType << " Gather" << componentName << "(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; EMIT_LINE_DIRECTIVE(); - sb << outputType << " Gather" << componentName << "(SamplerState s, "; + sb << outputType << " Gather" << componentName << "(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "constexpr int" << kBaseTextureTypes[tt].coordCount << " offset, "; sb << "out uint status);\n"; EMIT_LINE_DIRECTIVE(); sb << "__target_intrinsic(glsl, \"textureGatherOffsets($p, $2, int" << kBaseTextureTypes[tt].coordCount << "[]($3, $4, $5, $6), " << componentIndex << ")\")\n"; - sb << outputType << " Gather" << componentName << "(SamplerState s, "; + sb << outputType << " Gather" << componentName << "(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "int" << kBaseTextureTypes[tt].coordCount << " offset1, "; sb << "int" << kBaseTextureTypes[tt].coordCount << " offset2, "; @@ -1538,7 +1492,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) sb << "int" << kBaseTextureTypes[tt].coordCount << " offset4);\n"; EMIT_LINE_DIRECTIVE(); - sb << outputType << " Gather" << componentName << "(SamplerState s, "; + sb << outputType << " Gather" << componentName << "(" << samplerStateParam; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; sb << "int" << kBaseTextureTypes[tt].coordCount << " offset1, "; sb << "int" << kBaseTextureTypes[tt].coordCount << " offset2, "; |
