diff options
Diffstat (limited to 'source/slang/core.meta.slang.h')
| -rw-r--r-- | source/slang/core.meta.slang.h | 916 |
1 files changed, 916 insertions, 0 deletions
diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h new file mode 100644 index 000000000..cf2052d3c --- /dev/null +++ b/source/slang/core.meta.slang.h @@ -0,0 +1,916 @@ +sb << "// Slang `core` library\n"; +sb << "\n"; +sb << "// A type that can be used as an operand for builtins\n"; +sb << "interface __BuiltinType {}\n"; +sb << "\n"; +sb << "// A type that can be used for arithmetic operations\n"; +sb << "interface __BuiltinArithmeticType : __BuiltinType {}\n"; +sb << "\n"; +sb << "// A type that logically has a sign (positive/negative/zero)\n"; +sb << "interface __BuiltinSignedArithmeticType : __BuiltinArithmeticType {}\n"; +sb << "\n"; +sb << "// A type that can represent integers\n"; +sb << "interface __BuiltinIntegerType : __BuiltinArithmeticType {}\n"; +sb << "\n"; +sb << "// A type that can represent non-integers\n"; +sb << "interface __BuiltinRealType : __BuiltinArithmeticType {}\n"; +sb << "\n"; +sb << "// A type that uses a floating-point representation\n"; +sb << "interface __BuiltinFloatingPointType : __BuiltinRealType, __BuiltinSignedArithmeticType {}\n"; +sb << "\n"; +sb << "__generic<T,U> __intrinsic_op(Sequence) U operator,(T left, U right);\n"; +sb << "\n"; +sb << "__generic<T> __intrinsic_op(select) T operator?:(bool condition, T ifTrue, T ifFalse);\n"; +sb << "__generic<T, let N : int> __intrinsic_op(select) vector<T,N> operator?:(vector<bool,N> condition, vector<T,N> ifTrue, vector<T,N> ifFalse);\n"; +sb << "\n"; +sb << ""; + +// We are going to use code generation to produce the +// declarations for all of our base types. + +static const int kBaseTypeCount = sizeof(kBaseTypes) / sizeof(kBaseTypes[0]); +for (int tt = 0; tt < kBaseTypeCount; ++tt) +{ + EMIT_LINE_DIRECTIVE(); + sb << "__builtin_type(" << int(kBaseTypes[tt].tag) << ") struct " << kBaseTypes[tt].name; + + // Declare interface conformances for this type + + sb << "\n : __BuiltinType\n"; + + switch (kBaseTypes[tt].tag) + { + case BaseType::Float: + sb << "\n , __BuiltinFloatingPointType\n"; + sb << "\n , __BuiltinRealType\n"; + // fall through to: + case BaseType::Int: + sb << "\n , __BuiltinSignedArithmeticType\n"; + // fall through to: + case BaseType::UInt: + case BaseType::UInt64: + sb << "\n , __BuiltinArithmeticType\n"; + // fall through to: + case BaseType::Bool: + sb << "\n , __BuiltinType\n"; + break; + + default: + break; + } + + sb << "\n{\n"; + + + // Declare initializers to convert from various other types + for (int ss = 0; ss < kBaseTypeCount; ++ss) + { + // Don't allow conversion from `void` + if (kBaseTypes[ss].tag == BaseType::Void) + continue; + + // We need to emit a modifier so that the semantic-checking + // layer will know it can use these operations for implicit + // conversion. + ConversionCost conversionCost = getBaseTypeConversionCost( + kBaseTypes[tt], + kBaseTypes[ss]); + + EMIT_LINE_DIRECTIVE(); + sb << "__implicit_conversion(" << conversionCost << ")\n"; + + EMIT_LINE_DIRECTIVE(); + sb << "__init(" << kBaseTypes[ss].name << " value);\n"; + } + + sb << "};\n"; +} + + + +// Declare vector and matrix types + +sb << "__generic<T = float, let N : int = 4> __magic_type(Vector) struct vector\n{\n"; +sb << " typedef T Element;\n"; + +// Declare initializer taking a single scalar of the elemnt type +sb << " __implicit_conversion(" << kConversionCost_ScalarToVector << ")\n"; +sb << " __intrinsic_op(" << kIROp_constructVectorFromScalar << ")\n"; +sb << " __init(T value);\n"; + +sb << "};\n"; + +// TODO: Probably need to do similar +sb << "\n"; +sb << "\n"; +sb << "__generic<T = float, let R : int = 4, let C : int = 4>\n"; +sb << "__magic_type(Matrix)\n"; +sb << "struct matrix {};\n"; +sb << "\n"; +sb << ""; + + + + +static const struct { + char const* name; + char const* glslPrefix; +} kTypes[] = +{ + {"float", ""}, + {"int", "i"}, + {"uint", "u"}, + {"bool", "b"}, +}; +static const int kTypeCount = sizeof(kTypes) / sizeof(kTypes[0]); + +for (int tt = 0; tt < kTypeCount; ++tt) +{ + // Declare HLSL vector types + for (int ii = 1; ii <= 4; ++ii) + { + sb << "typedef vector<" << kTypes[tt].name << "," << ii << "> " << kTypes[tt].name << ii << ";\n"; + } + + // Declare HLSL matrix types + for (int rr = 2; rr <= 4; ++rr) + for (int cc = 2; cc <= 4; ++cc) + { + sb << "typedef matrix<" << kTypes[tt].name << "," << rr << "," << cc << "> " << kTypes[tt].name << rr << "x" << cc << ";\n"; + } +} + +// Declare additional built-in generic types +// EMIT_LINE_DIRECTIVE(); + + +sb << "__generic<T>\n"; +sb << "__intrinsic_type(" << kIROp_ConstantBufferType << ")\n"; +sb << "__magic_type(ConstantBuffer) struct ConstantBuffer {};\n"; + +sb << "__generic<T>\n"; +sb << "__intrinsic_type(" << kIROp_TextureBufferType << ")\n"; +sb << "__magic_type(TextureBuffer) struct TextureBuffer {};\n"; + + +static const char* kComponentNames[]{ "x", "y", "z", "w" }; +static const char* kVectorNames[]{ "", "x", "xy", "xyz", "xyzw" }; + +// Need to add constructors to the types above +for (int N = 2; N <= 4; ++N) +{ + sb << "__generic<T> __extension vector<T, " << N << ">\n{\n"; + + // initialize from N scalars + sb << "__init("; + for (int ii = 0; ii < N; ++ii) + { + if (ii != 0) sb << ", "; + sb << "T " << kComponentNames[ii]; + } + sb << ");\n"; + + // Initialize from an M-vector and then scalars + for (int M = 2; M < N; ++M) + { + sb << "__init(vector<T," << M << "> " << kVectorNames[M]; + for (int ii = M; ii < N; ++ii) + { + sb << ", T " << kComponentNames[ii]; + } + sb << ");\n"; + } + + // initialize from another vector of the same size + // + // TODO(tfoley): this overlaps with implicit conversions. + // We should look for a way that we can define implicit + // conversions directly in the stdlib instead... + sb << "__generic<U> __init(vector<U," << N << ">);\n"; + + // Initialize from two vectors, of size M and N-M + for(int M = 2; M <= (N-2); ++M) + { + int K = N - M; + SLANG_ASSERT(K >= 2); + + sb << "__init(vector<T," << M << "> " << kVectorNames[M]; + sb << ", vector<T," << K << "> "; + for (int ii = 0; ii < K; ++ii) + { + sb << kComponentNames[ii]; + } + sb << ");\n"; + } + + sb << "}\n"; +} + +// The above extension was generic in the *type* of the vector, +// but explicit in the *size*. We will now declare an extension +// for each builtin type that is generic in the size. +// +for (int tt = 0; tt < kBaseTypeCount; ++tt) +{ + if(kBaseTypes[tt].tag == BaseType::Void) continue; + + sb << "__generic<let N : int> __extension vector<" + << kBaseTypes[tt].name << ",N>\n{\n"; + + for (int ff = 0; ff < kBaseTypeCount; ++ff) + { + if(kBaseTypes[ff].tag == BaseType::Void) continue; + + // We need a constructor to make a vector from a scalar + // of another type. + + if( tt != ff ) + { + auto cost = getBaseTypeConversionCost( + kBaseTypes[tt], + kBaseTypes[ff]); + cost += kConversionCost_ScalarToVector; + + sb << " __implicit_conversion(" << cost << ")\n"; + sb << " __init(" << kBaseTypes[ff].name << " value);\n"; + } + } + + sb << "}\n"; +} + +for( int R = 2; R <= 4; ++R ) +for( int C = 2; C <= 4; ++C ) +{ + sb << "__generic<T> __extension matrix<T, " << R << "," << C << ">\n{\n"; + + // initialize from R*C scalars + sb << "__init("; + for( int ii = 0; ii < R; ++ii ) + for( int jj = 0; jj < C; ++jj ) + { + if ((ii+jj) != 0) sb << ", "; + sb << "T m" << ii << jj; + } + sb << ");\n"; + + // Initialize from R C-vectors + sb << "__init("; + for (int ii = 0; ii < R; ++ii) + { + if(ii != 0) sb << ", "; + sb << "vector<T," << C << "> row" << ii; + } + sb << ");\n"; + + + // initialize from another matrix of the same size + // + // TODO(tfoley): See comment about how this overlaps + // with implicit conversion, in the `vector` case above + sb << "__generic<U> __init(matrix<U," << R << ", " << C << ">);\n"; + + // initialize from a matrix of larger size + for(int rr = R; rr <= 4; ++rr) + for( int cc = C; cc <= 4; ++cc ) + { + if(rr == R && cc == C) continue; + sb << "__init(matrix<T," << rr << "," << cc << "> value);\n"; + } + + sb << "}\n"; +} + +// Declare built-in texture and sampler types + + + +sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerState) << ")\n"; +sb << "__intrinsic_type(" << kIROp_SamplerType << ", " << int(SamplerStateType::Flavor::SamplerState) << ")\n"; +sb << "struct SamplerState {};"; + +sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerComparisonState) << ")\n"; +sb << "__intrinsic_type(" << kIROp_SamplerType << ", " << int(SamplerStateType::Flavor::SamplerComparisonState) << ")\n"; +sb << "struct SamplerComparisonState {};"; + +// TODO(tfoley): Need to handle `RW*` variants of texture types as well... +static const struct { + char const* name; + TextureType::Shape baseShape; + int coordCount; +} kBaseTextureTypes[] = { + { "Texture1D", TextureType::Shape1D, 1 }, + { "Texture2D", TextureType::Shape2D, 2 }, + { "Texture3D", TextureType::Shape3D, 3 }, + { "TextureCube", TextureType::ShapeCube, 3 }, +}; +static const int kBaseTextureTypeCount = sizeof(kBaseTextureTypes) / sizeof(kBaseTextureTypes[0]); + + +static const struct { + char const* name; + SlangResourceAccess access; +} kBaseTextureAccessLevels[] = { + { "", SLANG_RESOURCE_ACCESS_READ }, + { "RW", SLANG_RESOURCE_ACCESS_READ_WRITE }, + { "RasterizerOrdered", SLANG_RESOURCE_ACCESS_RASTER_ORDERED }, +}; +static const int kBaseTextureAccessLevelCount = sizeof(kBaseTextureAccessLevels) / sizeof(kBaseTextureAccessLevels[0]); + +for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) +{ + char const* name = kBaseTextureTypes[tt].name; + TextureType::Shape baseShape = kBaseTextureTypes[tt].baseShape; + + for (int isArray = 0; isArray < 2; ++isArray) + { + // Arrays of 3D textures aren't allowed + if (isArray && baseShape == TextureType::Shape3D) continue; + + for (int isMultisample = 0; isMultisample < 2; ++isMultisample) + for (int accessLevel = 0; accessLevel < kBaseTextureAccessLevelCount; ++accessLevel) + { + auto access = kBaseTextureAccessLevels[accessLevel].access; + + // TODO: any constraints to enforce on what gets to be multisampled? + + unsigned flavor = baseShape; + if (isArray) flavor |= TextureType::ArrayFlag; + if (isMultisample) flavor |= TextureType::MultisampleFlag; +// if (isShadow) flavor |= TextureType::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(Texture," << int(flavor) << ")\n"; + sb << "__intrinsic_type(" << kIROp_TextureType << ", " << flavor << ")\n"; + sb << "struct "; + sb << kBaseTextureAccessLevels[accessLevel].name; + sb << name; + if (isMultisample) sb << "MS"; + if (isArray) sb << "Array"; +// if (isShadow) sb << "Shadow"; + sb << "\n{"; + + if( !isMultisample ) + { + sb << "float CalculateLevelOfDetail(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " location);\n"; + + sb << "float CalculateLevelOfDetailUnclamped(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " location);\n"; + } + + // `GetDimensions` + + for(int isFloat = 0; isFloat < 2; ++isFloat) + for(int includeMipInfo = 0; includeMipInfo < 2; ++includeMipInfo) + { + { + sb << "__glsl_version(450)\n"; + sb << "__target_intrinsic(glsl, \"("; + + int aa = 0; + String lodStr = "0"; + if (includeMipInfo) + { + int mipLevelArg = aa++; + lodStr = "int($"; + lodStr.append(mipLevelArg); + lodStr.append(")"); + } + + int cc = 0; + switch(baseShape) + { + case TextureType::Shape1D: + sb << "($" << aa++ << " = textureSize($P, " << lodStr << "))"; + cc = 1; + break; + + case TextureType::Shape2D: + case TextureType::ShapeCube: + sb << "($" << aa++ << " = textureSize($P, " << lodStr << ").x)"; + sb << ", ($" << aa++ << " = textureSize($P, " << lodStr << ").y)"; + cc = 2; + break; + + case TextureType::Shape3D: + sb << "($" << aa++ << " = textureSize($P, " << lodStr << ").x)"; + sb << ", ($" << aa++ << " = textureSize($P, " << lodStr << ").y)"; + sb << ", ($" << aa++ << " = textureSize($P, " << lodStr << ").z)"; + cc = 3; + break; + + default: + SLANG_UNEXPECTED("unhandled resource shape"); + break; + } + + if(isArray) + { + sb << ", ($" << aa++ << " = textureSize($P, " << lodStr << ")." << kComponentNames[cc] << ")"; + } + + if(isMultisample) + { + sb << ", ($" << aa++ << " = textureSamples($P))"; + } + + if (includeMipInfo) + { + sb << ", ($" << aa++ << " = textureQueryLevels($P))"; + } + + + sb << ")\")\n"; + + + // TIM: Making `GetDimensions` *not* be marked as + // an intrinsic, just so we can see how defining + // things as `extern` functions would work. +// sb << "__intrinsic_op\n"; + + } + + char const* t = isFloat ? "out float " : "out uint "; + + sb << "void GetDimensions("; + if(includeMipInfo) + sb << "uint mipLevel, "; + + switch(baseShape) + { + case TextureType::Shape1D: + sb << t << "width"; + break; + + case TextureType::Shape2D: + case TextureType::ShapeCube: + sb << t << "width,"; + sb << t << "height"; + break; + + case TextureType::Shape3D: + sb << t << "width,"; + sb << t << "height,"; + sb << t << "depth"; + break; + + default: + assert(!"unexpected"); + break; + } + + if(isArray) + { + sb << ", " << t << "elements"; + } + + if(isMultisample) + { + sb << ", " << t << "sampleCount"; + } + + if(includeMipInfo) + sb << ", " << t << "numberOfLevels"; + + sb << ");\n"; + } + + // `GetSamplePosition()` + if( isMultisample ) + { + sb << "float2 GetSamplePosition(int s);\n"; + } + + // `Load()` + + if( kBaseTextureTypes[tt].coordCount + isArray < 4 ) + { + int loadCoordCount = kBaseTextureTypes[tt].coordCount + isArray + (isMultisample?0:1); + + // 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" }; + + if (isMultisample) + { + sb << "__target_intrinsic(glsl, \"texelFetch($P, $0, $1)\")\n"; + } + else + { + sb << "__target_intrinsic(glsl, \"texelFetch($P, ($0)." << kGLSLLoadCoordsSwizzle[loadCoordCount] << ", ($0)." << kGLSLLoadLODSwizzle[loadCoordCount] << ")\")\n"; + } + sb << "__intrinsic_op\n"; + sb << "T Load("; + sb << "int" << loadCoordCount << " location"; + if(isMultisample) + { + sb << ", int sampleIndex"; + } + sb << ");\n"; + + if (isMultisample) + { + sb << "__target_intrinsic(glsl, \"texelFetchOffset($P, $0, $1, $2)\")\n"; + } + else + { + sb << "__target_intrinsic(glsl, \"texelFetch($P, ($0)." << kGLSLLoadCoordsSwizzle[loadCoordCount] << ", ($0)." << kGLSLLoadLODSwizzle[loadCoordCount] << ", $1)\")\n"; + } + sb << "__intrinsic_op\n"; + sb << "T Load("; + sb << "int" << loadCoordCount << " location"; + if(isMultisample) + { + sb << ", int sampleIndex"; + } + sb << ", int" << loadCoordCount << " offset"; + sb << ");\n"; + + + sb << "T Load("; + sb << "int" << loadCoordCount << " location"; + if(isMultisample) + { + sb << ", int sampleIndex"; + } + sb << ", int" << kBaseTextureTypes[tt].coordCount << " offset"; + sb << ", out uint status"; + sb << ");\n"; + } + + if(baseShape != TextureType::ShapeCube) + { + // subscript operator + sb << "__intrinsic_op __subscript(uint" << kBaseTextureTypes[tt].coordCount + isArray << " location) -> T;\n"; + } + + if( !isMultisample ) + { + // `Sample()` + + sb << "__target_intrinsic(glsl, \"texture($p, $1)\")\n"; + + // TODO: only enable if IR is being used? + sb << "__intrinsic_op(sample)\n"; + + sb << "__intrinsic_op\n"; + sb << "T Sample(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location);\n"; + + if( baseShape != TextureType::ShapeCube ) + { + sb << "__target_intrinsic(glsl, \"textureOffset($p, $1, $2)\")\n"; + sb << "__intrinsic_op\n"; + sb << "T Sample(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; + } + + sb << "T Sample(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; + if( baseShape != TextureType::ShapeCube ) + { + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset, "; + } + sb << "float clamp);\n"; + + sb << "T Sample(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; + if( baseShape != TextureType::ShapeCube ) + { + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset, "; + } + sb << "float clamp, out uint status);\n"; + + + // `SampleBias()` + sb << "__target_intrinsic(glsl, \"texture($p, $1, $2)\")\n"; + sb << "__intrinsic_op\n"; + sb << "T SampleBias(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, float bias);\n"; + + if( baseShape != TextureType::ShapeCube ) + { + sb << "__target_intrinsic(glsl, \"textureOffset($p, $1, $2, $3)\")\n"; + sb << "__intrinsic_op\n"; + sb << "T SampleBias(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, float bias, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; + } + + // `SampleCmp()` and `SampleCmpLevelZero` + sb << "T SampleCmp(SamplerComparisonState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; + sb << "float compareValue"; + sb << ");\n"; + + int baseCoordCount = kBaseTextureTypes[tt].coordCount; + int arrCoordCount = baseCoordCount + isArray; + if (arrCoordCount < 3) + { + int extCoordCount = arrCoordCount + 1; + + if (extCoordCount < 3) + extCoordCount = 3; + + sb << "__target_intrinsic(glsl, \"textureLod($p, "; + + sb << "vec" << extCoordCount << "($1,"; + for (int ii = arrCoordCount; ii < extCoordCount - 1; ++ii) + { + sb << " 0.0,"; + } + sb << "$2)"; + + sb << ", 0.0)\")\n"; + } + else if(arrCoordCount <= 3) + { + int extCoordCount = arrCoordCount + 1; + + if (extCoordCount < 3) + extCoordCount = 3; + + sb << "__target_intrinsic(glsl, \"textureGrad($p, "; + + sb << "vec" << extCoordCount << "($1,"; + for (int ii = arrCoordCount; ii < extCoordCount - 1; ++ii) + { + sb << " 0.0,"; + } + sb << "$2)"; + + // Construct gradients + sb << ", vec" << baseCoordCount << "(0.0)"; + sb << ", vec" << baseCoordCount << "(0.0)"; + sb << ")\")\n"; + } + sb << "__intrinsic_op\n"; + sb << "T SampleCmpLevelZero(SamplerComparisonState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; + sb << "float compareValue"; + sb << ");\n"; + + if( baseShape != TextureType::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. + + sb << "T SampleCmp(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; + sb << "float compareValue, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; + + sb << "T SampleCmpLevelZero(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; + sb << "float compareValue, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; + } + + + sb << "__target_intrinsic(glsl, \"textureGrad($p, $1, $2, $3)\")\n"; + sb << "__intrinsic_op(sampleGrad)\n"; + sb << "T SampleGrad(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " gradX, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " gradY"; + sb << ");\n"; + + if( baseShape != TextureType::ShapeCube ) + { + sb << "__target_intrinsic(glsl, \"textureGradOffset($p, $1, $2, $3, $4)\")\n"; + sb << "__intrinsic_op(sampleGrad)\n"; + sb << "T SampleGrad(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " gradX, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " gradY, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; + } + + // `SampleLevel` + + sb << "__target_intrinsic(glsl, \"textureLod($p, $1, $2)\")\n"; + sb << "__intrinsic_op\n"; + sb << "T SampleLevel(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; + sb << "float level);\n"; + + if( baseShape != TextureType::ShapeCube ) + { + sb << "__target_intrinsic(glsl, \"textureLodOffset($p, $1, $2, $3)\")\n"; + sb << "__intrinsic_op\n"; + sb << "T SampleLevel(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, "; + sb << "float level, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; + } + } + + sb << "\n};\n"; + + // `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; + } kGatherExtensionCases[] = { + { "__generic<T, let N : int>", "vector<T,N>" }, + + // 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 << kBaseTextureAccessLevels[accessLevel].name; + sb << name; + 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" }, + }; + + for(auto kk : kGatherComponets) + { + auto componentIndex = kk.componentIndex; + auto componentName = kk.componentName; + + EMIT_LINE_DIRECTIVE(); + + sb << "__target_intrinsic(glsl, \"textureGather($p, $1, " << componentIndex << ")\")\n"; + sb << "__intrinsic_op\n"; + sb << "vector<T, 4> Gather" << componentName << "(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " location);\n"; + + EMIT_LINE_DIRECTIVE(); + sb << "__target_intrinsic(glsl, \"textureGatherOffset($p, $1, $2, " << componentIndex << ")\")\n"; + sb << "__intrinsic_op\n"; + sb << "vector<T, 4> Gather" << componentName << "(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " location, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; + + EMIT_LINE_DIRECTIVE(); + sb << "vector<T, 4> Gather" << componentName << "(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " location, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset, "; + sb << "out uint status);\n"; + + EMIT_LINE_DIRECTIVE(); + sb << "__target_intrinsic(glsl, \"textureGatherOffsets($p, $1, int" << kBaseTextureTypes[tt].coordCount << "[]($2, $3, $4, $5), " << componentIndex << ")\")\n"; + sb << "__intrinsic_op\n"; + sb << "vector<T, 4> Gather" << componentName << "(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " location, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset1, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset2, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset3, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset4);\n"; + + EMIT_LINE_DIRECTIVE(); + sb << "vector<T, 4> Gather" << componentName << "(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " location, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset1, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset2, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset3, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset4, "; + sb << "out uint status);\n"; + } + + EMIT_LINE_DIRECTIVE(); + sb << "\n}\n"; + } + } + } +} + + +for (auto op : unaryOps) +{ + for (auto type : kBaseTypes) + { + if ((type.flags & op.flags) == 0) + continue; + + char const* fixity = (op.flags & POSTFIX) != 0 ? "__postfix " : "__prefix "; + char const* qual = (op.flags & ASSIGNMENT) != 0 ? "in out " : ""; + + // scalar version + sb << fixity; + sb << "__intrinsic_op(" << int(op.opCode) << ") " << type.name << " operator" << op.opName << "(" << qual << type.name << " value);\n"; + + // vector version + sb << "__generic<let N : int> "; + sb << fixity; + sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << type.name << ",N> operator" << op.opName << "(" << qual << "vector<" << type.name << ",N> value);\n"; + + // matrix version + sb << "__generic<let N : int, let M : int> "; + sb << fixity; + sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << type.name << ",N,M> operator" << op.opName << "(" << qual << "matrix<" << type.name << ",N,M> value);\n"; + } +} + +for (auto op : binaryOps) +{ + for (auto type : kBaseTypes) + { + if ((type.flags & op.flags) == 0) + continue; + + char const* leftType = type.name; + char const* rightType = leftType; + char const* resultType = leftType; + + if (op.flags & COMPARISON) resultType = "bool"; + + char const* leftQual = ""; + if(op.flags & ASSIGNMENT) leftQual = "in out "; + + // TODO: handle `SHIFT` + + // scalar version + sb << "__intrinsic_op(" << int(op.opCode) << ") " << resultType << " operator" << op.opName << "(" << leftQual << leftType << " left, " << rightType << " right);\n"; + + // vector version + sb << "__generic<let N : int> "; + sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << resultType << ",N> operator" << op.opName << "(" << leftQual << "vector<" << leftType << ",N> left, vector<" << rightType << ",N> right);\n"; + + // matrix version + + // skip matrix-matrix multiply operations here, so that GLSL doesn't see them + switch (op.opCode) + { + case kIROp_Mul: + case kIRPseudoOp_MulAssign: + break; + + default: + sb << "__generic<let N : int, let M : int> "; + sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, matrix<" << rightType << ",N,M> right);\n"; + break; + } + + // We are going to go ahead and explicitly define combined + // operations for the scalar-op-vector, etc. cases, rather + // than rely on promotion rules. + + // scalar-vector and scalar-matrix + if (!(op.flags & ASSIGNMENT)) + { + sb << "__generic<let N : int> "; + sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << resultType << ",N> operator" << op.opName << "(" << leftQual << leftType << " left, vector<" << rightType << ",N> right);\n"; + + sb << "__generic<let N : int, let M : int> "; + sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << leftType << " left, matrix<" << rightType << ",N,M> right);\n"; + } + + // vector-scalar and matrix-scalar + sb << "__generic<let N : int> "; + sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << resultType << ",N> operator" << op.opName << "(" << leftQual << "vector<" << leftType << ",N> left, " << rightType << " right);\n"; + + sb << "__generic<let N : int, let M : int> "; + sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, " << rightType << " right);\n"; + } +} + +sb << "\n"; +sb << ""; |
