summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-stdlib-textures.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-stdlib-textures.cpp')
-rw-r--r--source/slang/slang-stdlib-textures.cpp1192
1 files changed, 89 insertions, 1103 deletions
diff --git a/source/slang/slang-stdlib-textures.cpp b/source/slang/slang-stdlib-textures.cpp
index 98380033c..9ebfc54d6 100644
--- a/source/slang/slang-stdlib-textures.cpp
+++ b/source/slang/slang-stdlib-textures.cpp
@@ -46,20 +46,16 @@ struct BraceScope
};
TextureTypeInfo::TextureTypeInfo(
- TextureTypePrefixInfo const& prefixInfo,
BaseTextureShapeInfo const& base,
bool isArray,
bool isMultisample,
bool isShadow,
- BaseTextureAccessInfo const& accessInfo,
StringBuilder& inSB,
String const& inPath)
- : prefixInfo(prefixInfo)
- , base(base)
+ : base(base)
, isArray(isArray)
, isMultisample(isMultisample)
, isShadow(isShadow)
- , accessInfo(accessInfo)
, sb(inSB)
, path(inPath)
{
@@ -82,7 +78,10 @@ void TextureTypeInfo::writeFuncBody(
if(glsl.getLength())
{
sb << i << "case glsl:\n";
- sb << i << "__intrinsic_asm \"" << glsl << "\";\n";
+ if (glsl.startsWith("if"))
+ sb << glsl;
+ else
+ sb << i << "__intrinsic_asm \"" << glsl << "\";\n";
}
if(cuda.getLength())
{
@@ -99,16 +98,6 @@ void TextureTypeInfo::writeFuncBody(
}
}
-void TextureTypeInfo::writeFuncDecorations(
- const String& glsl,
- const String& cuda)
-{
- if(glsl.getLength())
- sb << i << "__target_intrinsic(glsl, \"" << glsl << "\")\n";
- if(cuda.getLength())
- sb << i << "__target_intrinsic(cuda, \"" << cuda << "\")\n";
-}
-
void TextureTypeInfo::writeFuncWithSig(
const char* funcName,
const String& sig,
@@ -117,24 +106,12 @@ void TextureTypeInfo::writeFuncWithSig(
const String& cuda,
const ReadNoneMode readNoneMode)
{
- const bool isReadOnly = (accessInfo.access == SLANG_RESOURCE_ACCESS_READ);
- const bool rn =
- readNoneMode == ReadNoneMode::Always
- || readNoneMode == ReadNoneMode::IfReadOnly && isReadOnly;
- if(spirv.getLength())
- {
- if(rn)
- sb << i << "[__readNone]\n";
- sb << i << sig << "\n";
- writeFuncBody(funcName, glsl, cuda, spirv);
- }
- else
- {
- writeFuncDecorations(glsl, cuda);
- if(rn)
- sb << i << "[__readNone]\n";
- sb << i << sig << ";\n";
- }
+ if (readNoneMode == ReadNoneMode::Always)
+ sb << i << "[__readNone]\n";
+ sb << i << "[__readNone]\n";
+ sb << i << "[ForceInline]\n";
+ sb << i << sig << "\n";
+ writeFuncBody(funcName, glsl, cuda, spirv);
sb << "\n";
}
@@ -157,108 +134,14 @@ void TextureTypeInfo::writeFunc(
);
}
-void TextureTypeInfo::emitTypeDecl()
-{
- char const* baseName = prefixInfo.name;
- char const* baseShapeName = base.shapeName;
- TextureFlavor::Shape baseShape = base.baseShape;
-
- // Arrays of 3D textures aren't allowed
- if (isArray && baseShape == TextureFlavor::Shape::Shape3D) return;
-
- auto access = accessInfo.access;
-
- // No such thing as RWTextureCube
- if (access == SLANG_RESOURCE_ACCESS_READ_WRITE && baseShape == TextureFlavor::Shape::ShapeCube)
- {
- return;
- }
-
- // 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
- sb << "__generic<T = float4";
- // Multi-sample rw texture types have an optional sampleCount parameter.
- if (isMultisample)
- sb << ", let sampleCount : int = 0";
- sb << ">";
-
- if(prefixInfo.combined)
- {
- sb << "__magic_type(TextureSamplerType," << int(flavor) << ")\n";
- sb << "__intrinsic_type(" << (kIROp_TextureSamplerType + (int(flavor) << kIROpMeta_OtherShift)) << ")\n";
- }
- else
- {
- sb << "__magic_type(TextureType," << int(flavor) << ")\n";
- sb << "__intrinsic_type(" << (kIROp_TextureType + (int(flavor) << kIROpMeta_OtherShift)) << ")\n";
- }
- sb << "struct ";
- sb << accessInfo.name;
- sb << baseName;
- sb << baseShapeName;
- if (isMultisample) sb << "MS";
- if (isArray) sb << "Array";
- if (isShadow) sb << "Shadow";
- sb << "\n";
-
- // The struct body
- {
- BraceScope structBodyScope{i, sb, ";\n"};
-
- writeQueryFunctions();
-
- if(baseShape != TextureFlavor::Shape::ShapeCube)
- writeSubscriptFunctions();
-
- if( !isMultisample )
- writeSampleFunctions();
- }
-
- writeGatherExtensions();
-} // TextureTypeInfo::emitTypeDecl
-
-void TextureTypeInfo::writeQueryFunctions()
+void TextureTypeInfo::writeGetDimensionFunctions()
{
static const char* kComponentNames[]{ "x", "y", "z", "w" };
- TextureFlavor::Shape baseShape = base.baseShape;
-
- char const* samplerStateParam = prefixInfo.combined ? "" : "SamplerState s, ";
- auto access = accessInfo.access;
-
- if( !isMultisample )
- {
- writeFunc(
- "float",
- "CalculateLevelOfDetail",
- cat(samplerStateParam, "float", base.coordCount, " location"),
- cat("textureQueryLod($p, $2).x"),
- "",
- "",
- ReadNoneMode::Never
- );
-
- writeFunc(
- "float",
- "CalculateLevelOfDetailUnclamped",
- cat(samplerStateParam, "float", base.coordCount, " location"),
- cat("textureQueryLod($p, $2).y"),
- "",
- "",
- ReadNoneMode::Never
- );
- }
+ SlangResourceShape baseShape = base.baseShape;
// `GetDimensions`
- const char* dimParamTypes[] = {"out float ", "out int ", "out uint "};
+ const char* dimParamTypes[] = { "out float ", "out int ", "out uint " };
const char* dimParamTypesInner[] = { "float", "int", "uint" };
for (int tid = 0; tid < 3; tid++)
{
@@ -274,19 +157,19 @@ void TextureTypeInfo::writeQueryFunctions()
switch (baseShape)
{
- case TextureFlavor::Shape::Shape1D:
+ case SLANG_TEXTURE_1D:
params << t << "width";
sizeDimCount = 1;
break;
- case TextureFlavor::Shape::Shape2D:
- case TextureFlavor::Shape::ShapeCube:
+ case SLANG_TEXTURE_2D:
+ case SLANG_TEXTURE_CUBE:
params << t << "width,";
params << t << "height";
sizeDimCount = 2;
break;
- case TextureFlavor::Shape::Shape3D:
+ case SLANG_TEXTURE_3D:
params << t << "width,";
params << t << "height,";
params << t << "depth";
@@ -315,79 +198,77 @@ void TextureTypeInfo::writeQueryFunctions()
StringBuilder glsl;
{
- glsl << "(";
-
- int aa = 1;
- String lodStr = ", 0";
- if (includeMipInfo)
- {
- int mipLevelArg = aa++;
- lodStr = ", int($";
- lodStr.append(mipLevelArg);
- lodStr.append(")");
- }
-
- String opStr = " = textureSize($0" + lodStr;
- switch (access)
- {
- case SLANG_RESOURCE_ACCESS_READ_WRITE:
- case SLANG_RESOURCE_ACCESS_RASTER_ORDERED:
- opStr = " = imageSize($0";
- break;
-
- default:
- break;
- }
-
- int cc = 0;
- switch (baseShape)
- {
- case TextureFlavor::Shape::Shape1D:
- glsl << "($" << aa++ << opStr << ")";
- if (isArray)
+ auto emitIntrinsic = [&](UnownedStringSlice funcName, bool useLodStr)
{
- glsl << ".x";
- }
- glsl << ")";
- cc = 1;
- break;
-
- case TextureFlavor::Shape::Shape2D:
- case TextureFlavor::Shape::ShapeCube:
- glsl << "($" << aa++ << opStr << ").x)";
- glsl << ", ($" << aa++ << opStr << ").y)";
- cc = 2;
- break;
-
- case TextureFlavor::Shape::Shape3D:
- glsl << "($" << aa++ << opStr << ").x)";
- glsl << ", ($" << aa++ << opStr << ").y)";
- glsl << ", ($" << aa++ << opStr << ").z)";
- cc = 3;
- break;
-
- default:
- SLANG_UNEXPECTED("unhandled resource shape");
- break;
- }
-
- if (isArray)
- {
- glsl << ", ($" << aa++ << opStr << ")." << kComponentNames[cc] << ")";
- }
-
- if (isMultisample)
- {
- glsl << ", ($" << aa++ << " = textureSamples($0))";
- }
+ int aa = 1;
+ StringBuilder opStrSB;
+ opStrSB << " = " << funcName << "($0";
+ if (useLodStr)
+ {
+ String lodStr = ", 0";
+ if (includeMipInfo)
+ {
+ int mipLevelArg = aa++;
+ lodStr = ", int($";
+ lodStr.append(mipLevelArg);
+ lodStr.append(")");
+ }
+ opStrSB << lodStr;
+ }
+ auto opStr = opStrSB.produceString();
+ int cc = 0;
+ switch (baseShape)
+ {
+ case SLANG_TEXTURE_1D:
+ glsl << "($" << aa++ << opStr << ")";
+ if (isArray)
+ {
+ glsl << ".x";
+ }
+ glsl << ")";
+ cc = 1;
+ break;
+
+ case SLANG_TEXTURE_2D:
+ case SLANG_TEXTURE_CUBE:
+ glsl << "($" << aa++ << opStr << ").x)";
+ glsl << ", ($" << aa++ << opStr << ").y)";
+ cc = 2;
+ break;
+
+ case SLANG_TEXTURE_3D:
+ glsl << "($" << aa++ << opStr << ").x)";
+ glsl << ", ($" << aa++ << opStr << ").y)";
+ glsl << ", ($" << aa++ << opStr << ").z)";
+ cc = 3;
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unhandled resource shape");
+ break;
+ }
- if (includeMipInfo)
- {
- glsl << ", ($" << aa++ << " = textureQueryLevels($0))";
- }
+ if (isArray)
+ {
+ glsl << ", ($" << aa++ << opStr << ")." << kComponentNames[cc] << ")";
+ }
+ if (isMultisample)
+ {
+ glsl << ", ($" << aa++ << " = textureSamples($0))";
+ }
- glsl << ")";
+ if (includeMipInfo)
+ {
+ glsl << ", ($" << aa++ << " = textureQueryLevels($0))";
+ }
+ };
+ glsl << "if (access == " << kStdlibResourceAccessReadOnly << ") __intrinsic_asm \"";
+ emitIntrinsic(toSlice("textureSize"), true);
+ glsl << "\";\n";
+ glsl << "__intrinsic_asm \"";
+ emitIntrinsic(toSlice("imageSize"), false);
+ glsl << "\";\n";
}
StringBuilder spirv;
@@ -434,17 +315,17 @@ void TextureTypeInfo::writeQueryFunctions()
};
switch (baseShape)
{
- case TextureFlavor::Shape::Shape1D:
+ case SLANG_TEXTURE_1D:
extractSizeComponent(0, "width");
break;
- case TextureFlavor::Shape::Shape2D:
- case TextureFlavor::Shape::ShapeCube:
+ case SLANG_TEXTURE_2D:
+ case SLANG_TEXTURE_CUBE:
extractSizeComponent(0, "width");
extractSizeComponent(1, "height");
break;
- case TextureFlavor::Shape::Shape3D:
+ case SLANG_TEXTURE_3D:
extractSizeComponent(0, "width");
extractSizeComponent(1, "height");
extractSizeComponent(2, "depth");
@@ -485,901 +366,6 @@ void TextureTypeInfo::writeQueryFunctions()
ReadNoneMode::Always);
}
}
-
- // `GetSamplePosition()`
- if( isMultisample )
- {
- writeFunc("float2", "GetSamplePosition", "int s", "", "", "", ReadNoneMode::Never);
- }
-
- // `Load()`
-
- if( base.coordCount + isArray < 4 )
- {
- // The `Load()` operation on an ordinary `Texture2D` takes
- // an `int3` for the location, where `.xy` holds the texel
- // coordinates, and `.z` holds the mip level to use.
- //
- // The third coordinate for mip level is absent in
- // `Texure2DMS.Load()` and `RWTexture2D.Load`. This pattern
- // is repreated for all the other texture shapes.
- //
- bool needsMipLevel = !isMultisample && (access == SLANG_RESOURCE_ACCESS_READ);
-
- int loadCoordCount = base.coordCount + isArray + (needsMipLevel?1:0);
-
- char const* glslFuncName = (access == SLANG_RESOURCE_ACCESS_READ) ? "texelFetch" : "imageLoad";
-
- // When translating to GLSL, we need to break apart the `location` argument.
- //
- // TODO: this should realy be handled by having this member actually get lowered!
- static const char* kGLSLLoadCoordsSwizzle[] = { "", "", "x", "xy", "xyz", "xyzw" };
- static const char* kGLSLLoadLODSwizzle[] = { "", "", "y", "z", "w", "error" };
-
- // TODO: The GLSL translations here only handle the read-only texture
- // cases (stuff that lowers to `texture*` in GLSL) and not the stuff
- // that lowers to `image*`.
- //
- // At some point it may make sense to separate the read-only and
- // `RW`/`RasterizerOrdered` cases here rather than try to share code.
-
- // CUDA
- StringBuilder cudaBuilder;
- if(!isMultisample)
- {
- if (access == SLANG_RESOURCE_ACCESS_READ_WRITE)
- {
- const int coordCount = base.coordCount;
- const int vecCount = coordCount + int(isArray);
-
- if( baseShape != TextureFlavor::Shape::ShapeCube )
- {
- cudaBuilder << "surf" << coordCount << "D";
- if (isArray)
- {
- cudaBuilder << "Layered";
- }
- cudaBuilder << "read";
- cudaBuilder << "<$T0>($0";
- for (int j = 0; j < coordCount; ++j)
- {
- cudaBuilder << ", ($1)";
- if (vecCount > 1)
- {
- cudaBuilder << '.' << char(j + 'x');
- }
-
- // Surface access is *byte* addressed in x in CUDA
- if (j == 0)
- {
- cudaBuilder << " * $E";
- }
- }
- if (isArray)
- {
- cudaBuilder << ", int(($1)." << char(coordCount + 'x') << ")";
- }
- cudaBuilder << ", SLANG_CUDA_BOUNDARY_MODE)";
- }
- else
- {
- cudaBuilder << "__target_intrinsic(cuda, \"surfCubemap";
- if (isArray)
- {
- cudaBuilder << "Layered";
- }
- cudaBuilder << "read";
-
- // Surface access is *byte* addressed in x in CUDA
- cudaBuilder << "<$T0>($0, ($1).x * $E, ($1).y, ($1).z";
- if (isArray)
- {
- cudaBuilder << ", int(($1).w)";
- }
- cudaBuilder << ", SLANG_CUDA_BOUNDARY_MODE)";
- }
- }
- else if (access == SLANG_RESOURCE_ACCESS_READ)
- {
- // We can allow this on Texture1D
- if( baseShape == TextureFlavor::Shape::Shape1D && isArray == false)
- {
- cudaBuilder << "tex1Dfetch<$T0>($0, ($1).x)";
- }
- }
- }
-
- // SPIRV
- auto getSpirvIntrinsic = [&](bool hasSampleIndex, bool hasOffset)
- {
- StringBuilder spirv;
- spirv << "%lod:$$int = OpCompositeExtract $location " << base.coordCount + isArray << "; ";
- spirv << "%coord:$$int" << base.coordCount + isArray << " = OpVectorShuffle $location $location ";
- for (int i = 0; i < base.coordCount + isArray; i++)
- spirv << " " << i;
- spirv << "; ";
- spirv << "%sampled:__sampledType(T) = ";
- if (access == SLANG_RESOURCE_ACCESS_READ_WRITE)
- spirv << "OpImageRead";
- else
- spirv << "OpImageFetch";
- spirv << " $this %coord ";
- uint32_t operandMask = 0;
- if (!hasSampleIndex)
- operandMask |= SpvImageOperandsLodMask;
- if (hasOffset)
- operandMask |= SpvImageOperandsConstOffsetMask;
- if (hasSampleIndex)
- operandMask |= SpvImageOperandsSampleMask;
- spirv << operandMask << " ";
- if (operandMask & SpvImageOperandsLodMask)
- spirv << " %lod";
- if (operandMask & SpvImageOperandsConstOffsetMask)
- spirv << " $offset";
- if (operandMask & SpvImageOperandsSampleMask)
- spirv << " $sampleIndex";
- spirv << ";";
- spirv << i << "__truncate $$T result __sampledType(T) %sampled;";
- return spirv.produceString();
- };
-
- sb << i << "__glsl_extension(GL_EXT_samplerless_texture_functions)";
- writeFunc(
- "T",
- "Load",
- cat("int", loadCoordCount, " location", isMultisample ? ", int sampleIndex" : ""),
- isMultisample ? cat("$c", glslFuncName, "($0, $1, $2)$z")
- : needsMipLevel ? cat(
- "$c",
- glslFuncName,
- "($0, ($1).",
- kGLSLLoadCoordsSwizzle[loadCoordCount],
- ", ($1).",
- kGLSLLoadLODSwizzle[loadCoordCount],
- ")$z")
- : cat("$c", glslFuncName, "($0, $1)$z"),
- getSpirvIntrinsic(isMultisample, false),
- cudaBuilder
- );
-
- glslFuncName = (access == SLANG_RESOURCE_ACCESS_READ) ? "texelFetchOffset" : "imageLoad";
- sb << i << "__glsl_extension(GL_EXT_samplerless_texture_functions)";
- writeFunc(
- "T",
- "Load",
- cat(
- "int", loadCoordCount, " location",
- isMultisample ? ", int sampleIndex" : "",
- ", constexpr int", base.coordCount, " offset"
- ),
- isMultisample ? cat("$c", glslFuncName, "($0, $0, $1, $2)$z")
- : needsMipLevel ? cat(
- "$c", glslFuncName, "($0, ($1).", kGLSLLoadCoordsSwizzle[loadCoordCount],
- ", ($1).", kGLSLLoadLODSwizzle[loadCoordCount],
- ", $2)$z")
- : cat("$c", glslFuncName, "($0, $1, 0, $2)$z"),
- getSpirvIntrinsic(isMultisample, true)
- );
-
- writeFunc(
- "T",
- "Load",
- cat(
- "int", loadCoordCount, " location",
- isMultisample ? ", int sampleIndex" : "",
- ", constexpr int", base.coordCount, " offset",
- ", out uint status"
- )
- );
- }
-}
-
-static String spirvReadIntrinsic(SlangResourceAccess access)
-{
- StringBuilder spirvBuilder;
- const char* i = " ";
- switch (access)
- {
- case SLANG_RESOURCE_ACCESS_NONE:
- case SLANG_RESOURCE_ACCESS_READ:
- spirvBuilder << i << "%sampled : __sampledType(T) = OpImageFetch $this $location;\n";
- spirvBuilder << i << "__truncate $$T result __sampledType(T) %sampled;";
- break;
-
- default:
- spirvBuilder << i << "%sampled : __sampledType(T) = OpImageRead $this $location;\n";
- spirvBuilder << i << "__truncate $$T result __sampledType(T) %sampled;";
- break;
- }
- return spirvBuilder;
-}
-
-static String spirvWriteIntrinsic()
-{
- StringBuilder spirvBuilder;
- const char* i = " ";
- spirvBuilder << i << "OpImageWrite $this $location $newValue;";
- return spirvBuilder;
-}
-
-void TextureTypeInfo::writeSubscriptFunctions()
-{
- TextureFlavor::Shape baseShape = base.baseShape;
- auto access = accessInfo.access;
-
- int N = base.coordCount + isArray;
-
- char const* uintNs[] = { "", "uint", "uint2", "uint3", "uint4" };
- char const* ivecNs[] = { "", "int", "ivec2", "ivec3", "ivec4" };
-
- auto uintN = uintNs[N];
- auto ivecN = ivecNs[N];
-
- // subscript operator
- sb << i << "__subscript(" << uintN << " location) -> T\n";
- BraceScope subscriptScope{i, sb};
-
- // !!!!!!!!!!!!!!!!!!!! get !!!!!!!!!!!!!!!!!!!!!!!
-
- // GLSL/SPIR-V distinguishes sampled vs. non-sampled images
- StringBuilder glslBuilder;
- {
- switch( access )
- {
- case SLANG_RESOURCE_ACCESS_NONE:
- case SLANG_RESOURCE_ACCESS_READ:
- sb << i << "__glsl_extension(GL_EXT_samplerless_texture_functions)\n";
- glslBuilder << "$ctexelFetch($0, " << ivecN << "($1)";
- if( !isMultisample )
- {
- glslBuilder << ", 0";
- }
- else
- {
- // TODO: how to handle passing through sample index?
- glslBuilder << ", 0";
- }
- break;
-
- default:
- glslBuilder << "$cimageLoad($0, " << ivecN << "($1)";
- if( isMultisample )
- {
- // TODO: how to handle passing through sample index?
- glslBuilder << ", 0";
- }
- break;
- }
- glslBuilder << ")$z";
- }
-
- // CUDA
- StringBuilder cudaBuilder;
- {
- if (access == SLANG_RESOURCE_ACCESS_READ_WRITE)
- {
- const int coordCount = base.coordCount;
- const int vecCount = coordCount + int(isArray);
-
- cudaBuilder << "surf";
- if( baseShape != TextureFlavor::Shape::ShapeCube )
- {
- cudaBuilder << coordCount << "D";
- }
- else
- {
- cudaBuilder << "Cubemap";
- }
-
- cudaBuilder << (isArray ? "Layered" : "");
- cudaBuilder << "read$C<$T0>($0";
-
- for (int j = 0; j < vecCount; ++j)
- {
- cudaBuilder << ", ($1)";
- if (vecCount > 1)
- {
- cudaBuilder << '.' << char(j + 'x');
- }
- // Surface access is *byte* addressed in x in CUDA
- if (j == 0)
- {
- cudaBuilder << " * $E";
- }
- }
-
- cudaBuilder << ", SLANG_CUDA_BOUNDARY_MODE)";
- }
- else if (access == SLANG_RESOURCE_ACCESS_READ)
- {
- // We can allow this on Texture1D
- if( baseShape == TextureFlavor::Shape::Shape1D && isArray == false)
- {
- cudaBuilder << "tex1Dfetch<$T0>($0, $1)";
- }
- }
- }
-
- // Output that has get
- writeFuncWithSig(
- "operator[]",
- "get",
- glslBuilder,
- spirvReadIntrinsic(access),
- cudaBuilder
- );
-
- // !!!!!!!!!!!!!!!!!!!! set !!!!!!!!!!!!!!!!!!!!!!!
-
- if (!(access == SLANG_RESOURCE_ACCESS_NONE || access == SLANG_RESOURCE_ACCESS_READ))
- {
- // CUDA
- cudaBuilder.clear();
- {
- const int coordCount = base.coordCount;
- const int vecCount = coordCount + int(isArray);
-
- cudaBuilder << "surf";
- if( baseShape != TextureFlavor::Shape::ShapeCube )
- {
- cudaBuilder << coordCount << "D";
- }
- else
- {
- cudaBuilder << "Cubemap";
- }
-
- cudaBuilder << (isArray ? "Layered" : "");
- cudaBuilder << "write$C<$T0>($2, $0";
- for (int j = 0; j < vecCount; ++j)
- {
- cudaBuilder << ", ($1)";
- if (vecCount > 1)
- {
- cudaBuilder << '.' << char(j + 'x');
- }
-
- // Surface access is *byte* addressed in x in CUDA
- if (j == 0)
- {
- cudaBuilder << " * $E";
- }
- }
-
- cudaBuilder << ", SLANG_CUDA_BOUNDARY_MODE)";
- }
-
- // Set
- sb << i << "[nonmutating]\n";
- writeFuncWithSig(
- "operator[]",
- "set(T newValue)",
- cat("imageStore($0, ", ivecN, "($1), $V2)"),
- spirvWriteIntrinsic(),
- cudaBuilder
- );
- }
-
- // !!!!!!!!!!!!!!!!!! ref !!!!!!!!!!!!!!!!!!!!!!!!!
-
- // Depending on the access level of the texture type,
- // we either have just a getter (the default), or both
- // a getter and setter.
- switch( access )
- {
- case SLANG_RESOURCE_ACCESS_NONE:
- case SLANG_RESOURCE_ACCESS_READ:
- break;
- default:
- sb << i << "__intrinsic_op(" << int(kIROp_ImageSubscript) << ") ref;\n";
- break;
- }
-}
-
-static String cudaSampleIntrinsic(const bool isArray, const BaseTextureShapeInfo& base, bool sampleLevel)
-{
- StringBuilder cudaBuilder;
-
- TextureFlavor::Shape baseShape = base.baseShape;
- const int coordCount = base.coordCount;
-
- if( baseShape != TextureFlavor::Shape::ShapeCube )
- {
- cudaBuilder << "tex" << coordCount << "D";
- if (isArray)
- cudaBuilder << "Layered";
- if(sampleLevel)
- cudaBuilder << "Lod";
- cudaBuilder << "<$T0>($0";
- for (int i = 0; i < coordCount; ++i)
- {
- cudaBuilder << ", ($2)";
- cudaBuilder << '.' << "xyzw"[i];
- }
- if (isArray)
- cudaBuilder << ", int(($2)." << char(coordCount + 'x') << ")";
- if(sampleLevel)
- cudaBuilder << ", $3";
- cudaBuilder << ")";
- }
- else
- {
- cudaBuilder << "texCubemap";
- if (isArray)
- cudaBuilder << "Layered";
- if(sampleLevel)
- cudaBuilder << "Lod";
- cudaBuilder << "<$T0>($0, ($2).x, ($2).y, ($2).z";
- if (isArray)
- cudaBuilder << ", int(($2).w)";
- if(sampleLevel)
- cudaBuilder << ", $3";
- cudaBuilder << ")";
- }
-
- return cudaBuilder;
-}
-
-const char* noBias = nullptr;
-const char* noLodLevel = nullptr;
-const char* noGradX = nullptr;
-const char* noGradY = nullptr;
-const char* noConstOffset = nullptr;
-const char* noMinLod = nullptr;
-
-static String spirvSampleIntrinsic(
- const TextureTypePrefixInfo& prefixInfo,
- const char* bias = nullptr,
- const char* lodLevel = nullptr,
- const char* gradX = nullptr,
- const char* gradY = nullptr,
- const char* constOffset = nullptr,
- const char* minLod = nullptr)
-{
- StringBuilder spirvBuilder;
- const char* i = " ";
-
- SLANG_ASSERT(!(!gradX ^ !gradY));
-
- if(minLod)
- spirvBuilder << i << "OpCapability MinLod;\n";
-
- const char* sampledImage;
- if(prefixInfo.combined)
- {
- sampledImage = "$this";
- }
- else
- {
- const char* sampledImageType = "%sampledImageType";
- sampledImage = "%sampledImage";
- spirvBuilder << i << sampledImageType << " = OpTypeSampledImage $$This;\n";
- spirvBuilder << i << sampledImage << " : " << sampledImageType << " = OpSampledImage $this $s;\n";
- }
-
- const char* op = lodLevel || gradX ? "OpImageSampleExplicitLod" : "OpImageSampleImplicitLod";
- spirvBuilder << i << "%sampled : __sampledType(T) = " << op << " " << sampledImage << " $location";
- spirvBuilder << " None";
- if(bias)
- spirvBuilder << "|Bias";
- if(lodLevel)
- spirvBuilder << "|Lod";
- if(gradX)
- spirvBuilder << "|Grad";
- if(constOffset)
- spirvBuilder << "|ConstOffset";
- if(minLod)
- spirvBuilder << "|MinLod";
-
- if(bias)
- spirvBuilder << " $" << bias;
- if(lodLevel)
- spirvBuilder << " $" << lodLevel;
- if(gradX)
- spirvBuilder << " $" << gradX << " $" << gradY;
- if(constOffset)
- spirvBuilder << " $" << constOffset;
- if(minLod)
- spirvBuilder << " $" << minLod;
- spirvBuilder << ";\n";
- spirvBuilder << i << "__truncate $$T result __sampledType(T) %sampled;\n";
- return spirvBuilder;
-}
-
-void TextureTypeInfo::writeSampleFunctions()
-{
- TextureFlavor::Shape baseShape = base.baseShape;
- char const* samplerStateParam = prefixInfo.combined ? "" : "SamplerState s, ";
- char const* comparisonSamplerStateParam = prefixInfo.combined ? "" : "SamplerComparisonState s, ";
- // `Sample()`
-
- writeFunc(
- "T",
- "Sample",
- cat(samplerStateParam, "float", base.coordCount + isArray, " location"),
- "$ctexture($p, $2)$z",
- spirvSampleIntrinsic(prefixInfo),
- cudaSampleIntrinsic(isArray, base, false)
- );
-
- if( baseShape != TextureFlavor::Shape::ShapeCube )
- {
- writeFunc(
- "T",
- "Sample",
- cat(samplerStateParam, "float", base.coordCount + isArray, " location, ", "constexpr int", base.coordCount, " offset"),
- "$ctextureOffset($p, $2, $3)$z",
- spirvSampleIntrinsic(prefixInfo, noBias, noLodLevel, noGradX, noGradY, "offset")
- );
- }
-
- writeFunc(
- "T",
- "Sample",
- cat(
- samplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- baseShape == TextureFlavor::Shape::ShapeCube ? "" : cat("constexpr int", base.coordCount, " offset, "),
- "float clamp"
- ),
- "",
- spirvSampleIntrinsic(
- prefixInfo,
- noBias,
- noLodLevel,
- noGradX,
- noGradY,
- baseShape == TextureFlavor::Shape::ShapeCube ? nullptr : "offset",
- "clamp"
- )
- );
-
- // SPIR-V todo, use OpImageSparseSampleImplicitLod
- writeFunc(
- "T",
- "Sample",
- cat(
- samplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- baseShape != TextureFlavor::Shape::ShapeCube ? cat("constexpr int", base.coordCount, " offset, ") : "",
- "float clamp, out uint status"
- )
- );
-
- writeFunc(
- "T",
- "SampleBias",
- cat(
- samplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- "float bias"
- ),
- "$ctexture($p, $2, $3)$z",
- spirvSampleIntrinsic(prefixInfo, "bias")
- );
-
- if( baseShape != TextureFlavor::Shape::ShapeCube )
- {
- writeFunc(
- "T",
- "SampleBias",
- cat(
- samplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- "float bias, ",
- "constexpr int", base.coordCount, " offset"
- ),
- "$ctextureOffset($p, $2, $3, $4)$z",
- spirvSampleIntrinsic(prefixInfo, "bias", noLodLevel, noGradX, noGradY, "offset")
- );
- }
- int baseCoordCount = base.coordCount;
- int arrCoordCount = baseCoordCount + isArray;
- if (arrCoordCount <= 3)
- {
- // `SampleCmp()` and `SampleCmpLevelZero`
-
- writeFunc(
- "float",
- "SampleCmp",
- cat(
- comparisonSamplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- "float compareValue"
- ),
- cat("texture($p, vec", arrCoordCount + 1, "($2, $3))")
- );
-
- sb << "__glsl_extension(GL_EXT_texture_shadow_lod)\n";
- writeFunc(
- "float",
- "SampleCmpLevelZero",
- cat(
- comparisonSamplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- "float compareValue"
- ),
- cat("textureLod($p, vec", arrCoordCount + 1, "($2, $3), 0)")
- );
- }
-
- if( baseShape != TextureFlavor::Shape::ShapeCube )
- {
- // Note(tfoley): MSDN seems confused, and claims that the `offset`
- // parameter for `SampleCmp` is available for everything but 3D
- // textures, while `Sample` and `SampleBias` are consistent in
- // saying they only exclude `offset` for cube maps (which makes
- // sense). I'm going to assume the documentation for `SampleCmp`
- // is just wrong.
- writeFunc(
- "float",
- "SampleCmp",
- cat(
- comparisonSamplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- "float compareValue, "
- "constexpr int", base.coordCount, " offset"
- ),
- cat("textureOffset($p, vec", arrCoordCount + 1, "($2, $3), $4)")
- );
-
- sb << "__glsl_extension(GL_EXT_texture_shadow_lod)\n";
- writeFunc(
- "float",
- "SampleCmpLevelZero",
- cat(
- comparisonSamplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- "float compareValue, "
- "constexpr int", base.coordCount, " offset"
- ),
- cat("textureLodOffset($p, vec", arrCoordCount + 1, "($2, $3), 0, $4)")
- );
- }
-
- // TODO(JS): Not clear how to map this to CUDA, because in HLSL, the gradient is a vector based on
- // 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
- writeFunc(
- "T",
- "SampleGrad",
- cat(
- samplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- "float", base.coordCount, " gradX, ",
- "float", base.coordCount, " gradY, "
- ),
- "$ctextureGrad($p, $2, $3, $4)$z",
- spirvSampleIntrinsic(prefixInfo, noBias, noLodLevel, "gradX", "gradY")
- );
-
- if( baseShape != TextureFlavor::Shape::ShapeCube )
- {
- writeFunc(
- "T",
- "SampleGrad",
- cat(
- samplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- "float", base.coordCount, " gradX, ",
- "float", base.coordCount, " gradY, ",
- "constexpr int", base.coordCount, " offset "
- ),
- "$ctextureGradOffset($p, $2, $3, $4, $5)$z",
- spirvSampleIntrinsic(prefixInfo, noBias, noLodLevel, "gradX", "gradY", "offset")
- );
-
- sb << i << "__glsl_extension(GL_ARB_sparse_texture_clamp)\n";
- writeFunc(
- "T",
- "SampleGrad",
- cat(
- samplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- "float", base.coordCount, " gradX, ",
- "float", base.coordCount, " gradY, ",
- "constexpr int", base.coordCount, " offset, ",
- "float lodClamp"
- ),
- "$ctextureGradOffsetClampARB($p, $2, $3, $4, $5, $6)$z",
- spirvSampleIntrinsic(prefixInfo, noBias, noLodLevel, "gradX", "gradY", "offset", "lodClamp")
- );
- }
-
- // `SampleLevel`
-
- writeFunc(
- "T",
- "SampleLevel",
- cat(
- samplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- "float level"
- ),
- "$ctextureLod($p, $2, $3)$z",
- spirvSampleIntrinsic(prefixInfo, noBias, "level"),
- cudaSampleIntrinsic(isArray, base, true)
- );
-
- if( baseShape != TextureFlavor::Shape::ShapeCube )
- {
- writeFunc(
- "T",
- "SampleLevel",
- cat(
- samplerStateParam,
- "float", base.coordCount + isArray, " location, ",
- "float level, ",
- "constexpr int", base.coordCount, " offset"
- ),
- "$ctextureLodOffset($p, $2, $3, $4)$z",
- spirvSampleIntrinsic(prefixInfo, noBias, "level", noGradX, noGradY, "offset")
- );
- }
-}
-
-void TextureTypeInfo::writeGatherExtensions()
-{
- char const* baseName = prefixInfo.name;
- char const* baseShapeName = base.shapeName;
-
- auto access = accessInfo.access;
-
- bool isReadOnly = (access == SLANG_RESOURCE_ACCESS_READ);
-
- char const* samplerStateParam = prefixInfo.combined ? "" : "SamplerState s, ";
-
- // `Gather*()` operations are handled via an `extension` declaration,
- // because this lets us capture the element type of the texture.
- //
- // TODO: longer-term there should be something like a `TextureElementType`
- // interface, that both scalars and vectors implement, that then exposes
- // a `Scalar` associated type, and `Gather` can return `vector<T.Scalar, 4>`.
- //
- static const struct {
- char const* genericPrefix;
- char const* elementType;
- char const* outputType;
- } kGatherExtensionCases[] = {
- { "__generic<T, let N : int>", "vector<T,N>", "vector<T, 4>" },
- { "", "float", "vector<float, 4>" },
- { "", "int" , "vector<int, 4>"},
- { "", "uint", "vector<uint, 4>"},
-
- // TODO: need a case here for scalars `T`, but also
- // need to ensure that case doesn't accidentally match
- // for `T = vector<...>`, which requires actual checking
- // of constraints on generic parameters.
- };
- for(auto cc : kGatherExtensionCases)
- {
- // TODO: this should really be an `if` around the entire `Gather` logic
- if (isMultisample) break;
-
- EMIT_LINE_DIRECTIVE();
- sb << cc.genericPrefix << " __extension ";
- sb << accessInfo.name;
- sb << baseName;
- sb << baseShapeName;
- if (isArray) sb << "Array";
- sb << "<" << cc.elementType << " >";
- sb << "\n{\n";
-
- // `Gather`
- // (tricky because it returns a 4-vector of the element type
- // of the texture components...)
- //
- // TODO: is it actually correct to restrict these so that, e.g.,
- // `GatherAlpha()` isn't allowed on `Texture2D<float3>` because
- // it nominally doesn't have an alpha component?
- static const struct {
- int componentIndex;
- char const* componentName;
- } kGatherComponets[] = {
- { 0, "" },
- { 0, "Red" },
- { 1, "Green" },
- { 2, "Blue" },
- { 3, "Alpha" },
- };
- enum Cmp
- { NotCmp,
- Cmp
- };
-
- for(auto cmp : {NotCmp, Cmp})
- for(auto kk : kGatherComponets)
- {
- auto samplerOrComparisonSampler = cmp == Cmp ? "SamplerComparisonState s, " : samplerStateParam;
-
- auto componentIndex = kk.componentIndex;
- auto componentName = kk.componentName;
-
- auto outputType = cc.outputType;
-
- const auto cmpName = cmp == Cmp ? "Cmp" : "";
- const auto cmpValueParam = cmp == Cmp ? "float compareValue, " : "";
- const auto cmpValueParamEnd = cmp == Cmp ? ", float compareValue" : "";
- const auto supportsGLSL = componentIndex == 0 || cmp == NotCmp;
-
- EMIT_LINE_DIRECTIVE();
-
- if(supportsGLSL)
- {
- if(cmp == Cmp)
- sb << "__target_intrinsic(glsl, \"textureGather($p, $2, $3)\")\n";
- else
- sb << "__target_intrinsic(glsl, \"textureGather($p, $2, " << componentIndex << ")\")\n";
- }
- if (base.coordCount == 2 && cmp == NotCmp)
- {
- // Gather only works on 2D in CUDA without comparison
- // "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";
- }
- if (isReadOnly)
- sb << "[__readNone]\n";
- sb << outputType << " Gather" << cmpName << componentName << "(" << samplerOrComparisonSampler;
- sb << "float" << base.coordCount + isArray << " location" << cmpValueParamEnd << ");\n";
-
- if (isReadOnly)
- sb << "[__readNone]\n";
- EMIT_LINE_DIRECTIVE();
- if(supportsGLSL)
- {
- if(cmp == Cmp)
- sb << "__target_intrinsic(glsl, \"textureGatherOffset($p, $2, $3, $4)\")\n";
- else
- sb << "__target_intrinsic(glsl, \"textureGatherOffset($p, $2, $3, " << componentIndex << ")\")\n";
- }
- sb << outputType << " Gather" << cmpName << componentName << "(" << samplerOrComparisonSampler;
- sb << "float" << base.coordCount + isArray << " location, ";
- sb << cmpValueParam;
- sb << "constexpr int" << base.coordCount << " offset);\n";
-
- if (isReadOnly)
- sb << "[__readNone]\n";
- EMIT_LINE_DIRECTIVE();
- sb << outputType << " Gather" << cmpName << componentName << "(" << samplerOrComparisonSampler;
- sb << "float" << base.coordCount + isArray << " location, ";
- sb << cmpValueParam;
- sb << "constexpr int" << base.coordCount << " offset, ";
- sb << "out uint status);\n";
-
- if (isReadOnly)
- sb << "[__readNone]\n";
- EMIT_LINE_DIRECTIVE();
- if(supportsGLSL)
- {
- if(cmp == Cmp)
- sb << "__target_intrinsic(glsl, \"textureGatherOffsets($p, $2, $3, ivec" << base.coordCount << "[]($4, $5, $6, $7))\")\n";
- else
- sb << "__target_intrinsic(glsl, \"textureGatherOffsets($p, $2, ivec" << base.coordCount << "[]($3, $4, $5, $6), " << componentIndex << ")\")\n";
- }
- sb << outputType << " Gather" << cmpName << componentName << "(" << samplerOrComparisonSampler;
- sb << "float" << base.coordCount + isArray << " location, ";
- sb << cmpValueParam;
- sb << "int" << base.coordCount << " offset1, ";
- sb << "int" << base.coordCount << " offset2, ";
- sb << "int" << base.coordCount << " offset3, ";
- sb << "int" << base.coordCount << " offset4);\n";
-
- if (isReadOnly)
- sb << "[__readNone]\n";
- EMIT_LINE_DIRECTIVE();
- sb << outputType << " Gather" << cmpName << componentName << "(" << samplerOrComparisonSampler;
- sb << "float" << base.coordCount + isArray << " location, ";
- sb << cmpValueParam;
- sb << "int" << base.coordCount << " offset1, ";
- sb << "int" << base.coordCount << " offset2, ";
- sb << "int" << base.coordCount << " offset3, ";
- sb << "int" << base.coordCount << " offset4, ";
- sb << "out uint status);\n";
- }
-
- EMIT_LINE_DIRECTIVE();
- sb << "\n}\n";
- }
}
}