| // | |
| // Copyright 2016 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| // TextureFunctionHLSL: Class for writing implementations of ESSL texture functions into HLSL | |
| // output. Some of the implementations are straightforward and just call the HLSL equivalent of the | |
| // ESSL texture function, others do more work to emulate ESSL texture sampling or size query | |
| // behavior. | |
| // | |
| #include "compiler/translator/TextureFunctionHLSL.h" | |
| #include "compiler/translator/ImmutableStringBuilder.h" | |
| #include "compiler/translator/UtilsHLSL.h" | |
| namespace sh | |
| { | |
| namespace | |
| { | |
| void OutputIntTexCoordWrap(TInfoSinkBase &out, | |
| const char *wrapMode, | |
| const char *size, | |
| const ImmutableString &texCoord, | |
| const char *texCoordOffset, | |
| const char *texCoordOutName) | |
| { | |
| // GLES 3.0.4 table 3.22 specifies how the wrap modes work. We don't use the formulas verbatim | |
| // but rather use equivalent formulas that map better to HLSL. | |
| out << "int " << texCoordOutName << ";\n"; | |
| out << "float " << texCoordOutName << "Offset = " << texCoord << " + float(" << texCoordOffset | |
| << ") / " << size << ";\n"; | |
| out << "bool " << texCoordOutName << "UseBorderColor = false;\n"; | |
| // CLAMP_TO_EDGE | |
| out << "if (" << wrapMode << " == 0)\n"; | |
| out << "{\n"; | |
| out << " " << texCoordOutName << " = clamp(int(floor(" << size << " * " << texCoordOutName | |
| << "Offset)), 0, int(" << size << ") - 1);\n"; | |
| out << "}\n"; | |
| // CLAMP_TO_BORDER | |
| out << "else if (" << wrapMode << " == 3)\n"; | |
| out << "{\n"; | |
| out << " int texCoordInt = int(floor(" << size << " * " << texCoordOutName << "Offset));\n"; | |
| out << " " << texCoordOutName << " = clamp(texCoordInt, 0, int(" << size << ") - 1);\n"; | |
| out << " " << texCoordOutName << "UseBorderColor = (texCoordInt != " << texCoordOutName | |
| << ");\n"; | |
| out << "}\n"; | |
| // MIRRORED_REPEAT | |
| out << "else if (" << wrapMode << " == 2)\n"; | |
| out << "{\n"; | |
| out << " float coordWrapped = 1.0 - abs(frac(abs(" << texCoordOutName | |
| << "Offset) * 0.5) * 2.0 - 1.0);\n"; | |
| out << " " << texCoordOutName << " = int(floor(" << size << " * coordWrapped));\n"; | |
| out << "}\n"; | |
| // REPEAT | |
| out << "else\n"; | |
| out << "{\n"; | |
| out << " " << texCoordOutName << " = int(floor(" << size << " * frac(" << texCoordOutName | |
| << "Offset)));\n"; | |
| out << "}\n"; | |
| } | |
| void OutputIntTexCoordWraps(TInfoSinkBase &out, | |
| const TextureFunctionHLSL::TextureFunction &textureFunction, | |
| ImmutableString *texCoordX, | |
| ImmutableString *texCoordY, | |
| ImmutableString *texCoordZ) | |
| { | |
| // Convert from normalized floating-point to integer | |
| out << "int wrapS = samplerMetadata[samplerIndex].wrapModes & 0x3;\n"; | |
| if (textureFunction.offset) | |
| { | |
| OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "offset.x", "tix"); | |
| } | |
| else | |
| { | |
| OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "0", "tix"); | |
| } | |
| *texCoordX = ImmutableString("tix"); | |
| out << "int wrapT = (samplerMetadata[samplerIndex].wrapModes >> 2) & 0x3;\n"; | |
| if (textureFunction.offset) | |
| { | |
| OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "offset.y", "tiy"); | |
| } | |
| else | |
| { | |
| OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "0", "tiy"); | |
| } | |
| *texCoordY = ImmutableString("tiy"); | |
| bool tizAvailable = false; | |
| if (IsSamplerArray(textureFunction.sampler)) | |
| { | |
| *texCoordZ = ImmutableString("int(max(0, min(layers - 1, floor(0.5 + t.z))))"); | |
| } | |
| else if (!IsSamplerCube(textureFunction.sampler) && !IsSampler2D(textureFunction.sampler)) | |
| { | |
| out << "int wrapR = (samplerMetadata[samplerIndex].wrapModes >> 4) & 0x3;\n"; | |
| if (textureFunction.offset) | |
| { | |
| OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "offset.z", "tiz"); | |
| } | |
| else | |
| { | |
| OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "0", "tiz"); | |
| } | |
| *texCoordZ = ImmutableString("tiz"); | |
| tizAvailable = true; | |
| } | |
| out << "bool useBorderColor = tixUseBorderColor || tiyUseBorderColor" | |
| << (tizAvailable ? " || tizUseBorderColor" : "") << ";\n"; | |
| } | |
| void OutputHLSL4SampleFunctionPrefix(TInfoSinkBase &out, | |
| const TextureFunctionHLSL::TextureFunction &textureFunction, | |
| const ImmutableString &textureReference, | |
| const ImmutableString &samplerReference) | |
| { | |
| out << textureReference; | |
| if (IsIntegerSampler(textureFunction.sampler) || | |
| textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH) | |
| { | |
| out << ".Load("; | |
| return; | |
| } | |
| if (IsShadowSampler(textureFunction.sampler)) | |
| { | |
| switch (textureFunction.method) | |
| { | |
| case TextureFunctionHLSL::TextureFunction::IMPLICIT: | |
| case TextureFunctionHLSL::TextureFunction::BIAS: | |
| case TextureFunctionHLSL::TextureFunction::LOD: | |
| out << ".SampleCmp("; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD0: | |
| case TextureFunctionHLSL::TextureFunction::LOD0BIAS: | |
| case TextureFunctionHLSL::TextureFunction::GRAD: | |
| out << ".SampleCmpLevelZero("; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| else | |
| { | |
| switch (textureFunction.method) | |
| { | |
| case TextureFunctionHLSL::TextureFunction::IMPLICIT: | |
| out << ".Sample("; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::BIAS: | |
| out << ".SampleBias("; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD: | |
| case TextureFunctionHLSL::TextureFunction::LOD0: | |
| case TextureFunctionHLSL::TextureFunction::LOD0BIAS: | |
| out << ".SampleLevel("; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::GRAD: | |
| out << ".SampleGrad("; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| out << samplerReference << ", "; | |
| } | |
| const char *GetSamplerCoordinateTypeString( | |
| const TextureFunctionHLSL::TextureFunction &textureFunction, | |
| int hlslCoords) | |
| { | |
| // Gather[Red|Green|Blue|Alpha] accepts float texture coordinates on textures in integer or | |
| // unsigned integer formats. | |
| // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-to-gather | |
| if ((IsIntegerSampler(textureFunction.sampler) && | |
| textureFunction.method != TextureFunctionHLSL::TextureFunction::GATHER) || | |
| textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH) | |
| { | |
| switch (hlslCoords) | |
| { | |
| case 2: | |
| if (IsSampler2DMS(textureFunction.sampler)) | |
| { | |
| return "int2"; | |
| } | |
| else | |
| { | |
| return "int3"; | |
| } | |
| case 3: | |
| if (IsSampler2DMSArray(textureFunction.sampler)) | |
| { | |
| return "int3"; | |
| } | |
| else | |
| { | |
| return "int4"; | |
| } | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| else | |
| { | |
| switch (hlslCoords) | |
| { | |
| case 2: | |
| return "float2"; | |
| case 3: | |
| return "float3"; | |
| case 4: | |
| return "float4"; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| return ""; | |
| } | |
| int GetHLSLCoordCount(const TextureFunctionHLSL::TextureFunction &textureFunction, | |
| ShShaderOutput outputType) | |
| { | |
| if (outputType == SH_HLSL_3_0_OUTPUT) | |
| { | |
| int hlslCoords = 2; | |
| switch (textureFunction.sampler) | |
| { | |
| case EbtSampler2D: | |
| case EbtSamplerExternalOES: | |
| case EbtSampler2DMS: | |
| hlslCoords = 2; | |
| break; | |
| case EbtSamplerCube: | |
| hlslCoords = 3; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| switch (textureFunction.method) | |
| { | |
| case TextureFunctionHLSL::TextureFunction::IMPLICIT: | |
| case TextureFunctionHLSL::TextureFunction::GRAD: | |
| return hlslCoords; | |
| case TextureFunctionHLSL::TextureFunction::BIAS: | |
| case TextureFunctionHLSL::TextureFunction::LOD: | |
| case TextureFunctionHLSL::TextureFunction::LOD0: | |
| case TextureFunctionHLSL::TextureFunction::LOD0BIAS: | |
| return 4; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| else | |
| { | |
| if (IsSampler3D(textureFunction.sampler) || IsSamplerArray(textureFunction.sampler) || | |
| IsSamplerCube(textureFunction.sampler)) | |
| { | |
| return 3; | |
| } | |
| ASSERT(IsSampler2D(textureFunction.sampler)); | |
| return 2; | |
| } | |
| return 0; | |
| } | |
| void OutputTextureFunctionArgumentList(TInfoSinkBase &out, | |
| const TextureFunctionHLSL::TextureFunction &textureFunction, | |
| const ShShaderOutput outputType) | |
| { | |
| if (outputType == SH_HLSL_3_0_OUTPUT) | |
| { | |
| switch (textureFunction.sampler) | |
| { | |
| case EbtSampler2D: | |
| case EbtSamplerExternalOES: | |
| out << "sampler2D s"; | |
| break; | |
| case EbtSamplerCube: | |
| out << "samplerCUBE s"; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| else | |
| { | |
| if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT) | |
| { | |
| out << TextureString(textureFunction.sampler) << " x, " | |
| << SamplerString(textureFunction.sampler) << " s"; | |
| } | |
| else | |
| { | |
| ASSERT(outputType == SH_HLSL_4_1_OUTPUT); | |
| // A bug in the D3D compiler causes some nested sampling operations to fail. | |
| // See http://anglebug.com/1923 | |
| // TODO(jmadill): Reinstate the const keyword when possible. | |
| out << /*"const"*/ "uint samplerIndex"; | |
| } | |
| } | |
| if (textureFunction.method == | |
| TextureFunctionHLSL::TextureFunction::FETCH) // Integer coordinates | |
| { | |
| switch (textureFunction.coords) | |
| { | |
| case 2: | |
| out << ", int2 t"; | |
| break; | |
| case 3: | |
| out << ", int3 t"; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| else // Floating-point coordinates (except textureSize) | |
| { | |
| switch (textureFunction.coords) | |
| { | |
| case 0: | |
| break; // textureSize(gSampler2DMS sampler) | |
| case 1: | |
| out << ", int lod"; | |
| break; // textureSize() | |
| case 2: | |
| out << ", float2 t"; | |
| break; | |
| case 3: | |
| out << ", float3 t"; | |
| break; | |
| case 4: | |
| out << ", float4 t"; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) | |
| { | |
| switch (textureFunction.sampler) | |
| { | |
| case EbtSampler2D: | |
| case EbtISampler2D: | |
| case EbtUSampler2D: | |
| case EbtSampler2DArray: | |
| case EbtISampler2DArray: | |
| case EbtUSampler2DArray: | |
| case EbtSampler2DShadow: | |
| case EbtSampler2DArrayShadow: | |
| case EbtSamplerExternalOES: | |
| out << ", float2 ddx, float2 ddy"; | |
| break; | |
| case EbtSampler3D: | |
| case EbtISampler3D: | |
| case EbtUSampler3D: | |
| case EbtSamplerCube: | |
| case EbtISamplerCube: | |
| case EbtUSamplerCube: | |
| case EbtSamplerCubeShadow: | |
| out << ", float3 ddx, float3 ddy"; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| switch (textureFunction.method) | |
| { | |
| case TextureFunctionHLSL::TextureFunction::IMPLICIT: | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::BIAS: | |
| break; // Comes after the offset parameter | |
| case TextureFunctionHLSL::TextureFunction::LOD: | |
| out << ", float lod"; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD0: | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD0BIAS: | |
| break; // Comes after the offset parameter | |
| case TextureFunctionHLSL::TextureFunction::SIZE: | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::FETCH: | |
| if (IsSampler2DMS(textureFunction.sampler) || | |
| IsSampler2DMSArray(textureFunction.sampler)) | |
| out << ", int index"; | |
| else | |
| out << ", int mip"; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::GRAD: | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::GATHER: | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GATHER && | |
| IsShadowSampler(textureFunction.sampler)) | |
| { | |
| out << ", float refZ"; | |
| } | |
| if (textureFunction.offset) | |
| { | |
| switch (textureFunction.sampler) | |
| { | |
| case EbtSampler3D: | |
| case EbtISampler3D: | |
| case EbtUSampler3D: | |
| out << ", int3 offset"; | |
| break; | |
| case EbtSampler2D: | |
| case EbtSampler2DArray: | |
| case EbtISampler2D: | |
| case EbtISampler2DArray: | |
| case EbtUSampler2D: | |
| case EbtUSampler2DArray: | |
| case EbtSampler2DShadow: | |
| case EbtSampler2DArrayShadow: | |
| case EbtSamplerExternalOES: | |
| out << ", int2 offset"; | |
| break; | |
| default: | |
| // Offset is not supported for multisampled textures. | |
| UNREACHABLE(); | |
| } | |
| } | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS || | |
| textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS) | |
| { | |
| out << ", float bias"; | |
| } | |
| else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GATHER && | |
| !IsShadowSampler(textureFunction.sampler)) | |
| { | |
| out << ", int comp = 0"; | |
| } | |
| } | |
| void GetTextureReference(TInfoSinkBase &out, | |
| const TextureFunctionHLSL::TextureFunction &textureFunction, | |
| const ShShaderOutput outputType, | |
| ImmutableString *textureReference, | |
| ImmutableString *samplerReference) | |
| { | |
| if (outputType == SH_HLSL_4_1_OUTPUT) | |
| { | |
| static const ImmutableString kTexturesStr("textures"); | |
| static const ImmutableString kSamplersStr("samplers"); | |
| static const ImmutableString kSamplerIndexStr("[samplerIndex]"); | |
| static const ImmutableString kTextureIndexStr("[textureIndex]"); | |
| static const ImmutableString kSamplerArrayIndexStr("[samplerArrayIndex]"); | |
| ImmutableString suffix(TextureGroupSuffix(textureFunction.sampler)); | |
| if (TextureGroup(textureFunction.sampler) == HLSL_TEXTURE_2D) | |
| { | |
| ImmutableStringBuilder textureRefBuilder(kTexturesStr.length() + suffix.length() + | |
| kSamplerIndexStr.length()); | |
| textureRefBuilder << kTexturesStr << suffix << kSamplerIndexStr; | |
| *textureReference = textureRefBuilder; | |
| ImmutableStringBuilder samplerRefBuilder(kSamplersStr.length() + suffix.length() + | |
| kSamplerIndexStr.length()); | |
| samplerRefBuilder << kSamplersStr << suffix << kSamplerIndexStr; | |
| *samplerReference = samplerRefBuilder; | |
| } | |
| else | |
| { | |
| out << " const uint textureIndex = samplerIndex - textureIndexOffset" | |
| << suffix.data() << ";\n"; | |
| ImmutableStringBuilder textureRefBuilder(kTexturesStr.length() + suffix.length() + | |
| kTextureIndexStr.length()); | |
| textureRefBuilder << kTexturesStr << suffix << kTextureIndexStr; | |
| *textureReference = textureRefBuilder; | |
| out << " const uint samplerArrayIndex = samplerIndex - samplerIndexOffset" | |
| << suffix.data() << ";\n"; | |
| ImmutableStringBuilder samplerRefBuilder(kSamplersStr.length() + suffix.length() + | |
| kSamplerArrayIndexStr.length()); | |
| samplerRefBuilder << kSamplersStr << suffix << kSamplerArrayIndexStr; | |
| *samplerReference = samplerRefBuilder; | |
| } | |
| } | |
| else | |
| { | |
| *textureReference = ImmutableString("x"); | |
| *samplerReference = ImmutableString("s"); | |
| } | |
| } | |
| void OutputTextureSizeFunctionBody(TInfoSinkBase &out, | |
| const TextureFunctionHLSL::TextureFunction &textureFunction, | |
| const ImmutableString &textureReference, | |
| bool getDimensionsIgnoresBaseLevel) | |
| { | |
| if (IsSampler2DMS(textureFunction.sampler)) | |
| { | |
| out << " uint width; uint height; uint samples;\n" | |
| << " " << textureReference << ".GetDimensions(width, height, samples);\n"; | |
| } | |
| else if (IsSampler2DMSArray(textureFunction.sampler)) | |
| { | |
| out << " uint width; uint height; uint depth; uint samples;\n" | |
| << " " << textureReference << ".GetDimensions(width, height, depth, samples);\n"; | |
| } | |
| else | |
| { | |
| if (getDimensionsIgnoresBaseLevel) | |
| { | |
| out << " int baseLevel = samplerMetadata[samplerIndex].baseLevel;\n"; | |
| } | |
| else | |
| { | |
| out << " int baseLevel = 0;\n"; | |
| } | |
| if (IsSampler3D(textureFunction.sampler) || IsSamplerArray(textureFunction.sampler) || | |
| (IsIntegerSampler(textureFunction.sampler) && IsSamplerCube(textureFunction.sampler))) | |
| { | |
| // "depth" stores either the number of layers in an array texture or 3D depth | |
| out << " uint width; uint height; uint depth; uint numberOfLevels;\n" | |
| << " " << textureReference | |
| << ".GetDimensions(baseLevel, width, height, depth, numberOfLevels);\n" | |
| << " width = max(width >> lod, 1);\n" | |
| << " height = max(height >> lod, 1);\n"; | |
| if (!IsSamplerArray(textureFunction.sampler)) | |
| { | |
| out << " depth = max(depth >> lod, 1);\n"; | |
| } | |
| } | |
| else if (IsSampler2D(textureFunction.sampler) || IsSamplerCube(textureFunction.sampler)) | |
| { | |
| out << " uint width; uint height; uint numberOfLevels;\n" | |
| << " " << textureReference | |
| << ".GetDimensions(baseLevel, width, height, numberOfLevels);\n" | |
| << " width = max(width >> lod, 1);\n" | |
| << " height = max(height >> lod, 1);\n"; | |
| } | |
| else | |
| UNREACHABLE(); | |
| } | |
| if (strcmp(textureFunction.getReturnType(), "int3") == 0) | |
| { | |
| out << " return int3(width, height, depth);\n"; | |
| } | |
| else | |
| { | |
| out << " return int2(width, height);\n"; | |
| } | |
| } | |
| void ProjectTextureCoordinates(const TextureFunctionHLSL::TextureFunction &textureFunction, | |
| ImmutableString *texCoordX, | |
| ImmutableString *texCoordY, | |
| ImmutableString *texCoordZ) | |
| { | |
| if (textureFunction.proj) | |
| { | |
| ImmutableString proj(""); | |
| switch (textureFunction.coords) | |
| { | |
| case 3: | |
| proj = ImmutableString(" / t.z"); | |
| break; | |
| case 4: | |
| proj = ImmutableString(" / t.w"); | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| ImmutableStringBuilder texCoordXBuilder(texCoordX->length() + proj.length() + 2u); | |
| texCoordXBuilder << '(' << *texCoordX << proj << ')'; | |
| *texCoordX = texCoordXBuilder; | |
| ImmutableStringBuilder texCoordYBuilder(texCoordY->length() + proj.length() + 2u); | |
| texCoordYBuilder << '(' << *texCoordY << proj << ')'; | |
| *texCoordY = texCoordYBuilder; | |
| ImmutableStringBuilder texCoordZBuilder(texCoordZ->length() + proj.length() + 2u); | |
| texCoordZBuilder << '(' << *texCoordZ << proj << ')'; | |
| *texCoordZ = texCoordZBuilder; | |
| } | |
| } | |
| void OutputIntegerTextureSampleFunctionComputations( | |
| TInfoSinkBase &out, | |
| const TextureFunctionHLSL::TextureFunction &textureFunction, | |
| const ShShaderOutput outputType, | |
| const ImmutableString &textureReference, | |
| ImmutableString *texCoordX, | |
| ImmutableString *texCoordY, | |
| ImmutableString *texCoordZ, | |
| bool getDimensionsIgnoresBaseLevel) | |
| { | |
| if (!IsIntegerSampler(textureFunction.sampler)) | |
| { | |
| return; | |
| } | |
| if (getDimensionsIgnoresBaseLevel) | |
| { | |
| out << " int baseLevel = samplerMetadata[samplerIndex].baseLevel;\n"; | |
| } | |
| else | |
| { | |
| out << " int baseLevel = 0;\n"; | |
| } | |
| if (IsSamplerCube(textureFunction.sampler)) | |
| { | |
| out << " float width; float height; float layers; float levels;\n"; | |
| out << " uint mip = 0;\n"; | |
| out << " " << textureReference | |
| << ".GetDimensions(baseLevel + mip, width, height, layers, levels);\n"; | |
| out << " bool xMajor = abs(t.x) >= abs(t.y) && abs(t.x) >= abs(t.z);\n"; | |
| out << " bool yMajor = abs(t.y) >= abs(t.z) && abs(t.y) > abs(t.x);\n"; | |
| out << " bool zMajor = abs(t.z) > abs(t.x) && abs(t.z) > abs(t.y);\n"; | |
| out << " bool negative = (xMajor && t.x < 0.0f) || (yMajor && t.y < 0.0f) || " | |
| "(zMajor && t.z < 0.0f);\n"; | |
| // FACE_POSITIVE_X = 000b | |
| // FACE_NEGATIVE_X = 001b | |
| // FACE_POSITIVE_Y = 010b | |
| // FACE_NEGATIVE_Y = 011b | |
| // FACE_POSITIVE_Z = 100b | |
| // FACE_NEGATIVE_Z = 101b | |
| out << " int face = (int)negative + (int)yMajor * 2 + (int)zMajor * 4;\n"; | |
| out << " float u = xMajor ? -t.z : (yMajor && t.y < 0.0f ? -t.x : t.x);\n"; | |
| out << " float v = yMajor ? t.z : (negative ? t.y : -t.y);\n"; | |
| out << " float m = xMajor ? t.x : (yMajor ? t.y : t.z);\n"; | |
| out << " float3 r = any(t) ? t : float3(1, 0, 0);\n"; | |
| out << " t.x = (u * 0.5f / m) + 0.5f;\n"; | |
| out << " t.y = (v * 0.5f / m) + 0.5f;\n"; | |
| // Mip level computation. | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT || | |
| textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD || | |
| textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) | |
| { | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT) | |
| { | |
| // We would like to calculate tha maximum of how many texels we move in the major | |
| // face's texture as we move across the screen in any direction. Namely, we want the | |
| // length of the directional derivative of the function p (defined below), maximized | |
| // over screen space directions. (For short: we want the norm of Dp.) For | |
| // simplicity, assume that z-axis is the major axis. By symmetry, we can assume that | |
| // the positive z direction is major. (The calculated value will be the same even if | |
| // this is false.) Let r denote the function from screen position to cube texture | |
| // coordinates. Then p can be written as p = s . P . r, where P(r) = (r.x, r.y)/r.z | |
| // is the projection onto the major cube face, and s = diag(width, height)/2. (s | |
| // linearly maps from the cube face into texture space, so that p(r) is in units of | |
| // texels.) The derivative is | |
| // Dp(r) = s |1 0 -r.x/r.z| | |
| // |0 1 -r.y/r.z| |ddx(r) ddy(r)| / r.z | |
| // = |dot(a, ddx(r)) dot(a, ddy(r))| | |
| // |dot(b, ddx(r)) dot(b, ddy(r))| / (2 r.z) | |
| // where a = w * vec3(1, 0, -r.x/r.z) | |
| // b = h * vec3(0, 1, -r.y/r.z) | |
| // We would like to know max(L(x)) over unit vectors x, where L(x) = |Dp(r) x|^2. | |
| // Since ddx(r) and ddy(r) are unknown, the best we can do is to sample L in some | |
| // directions and take the maximum across the samples. | |
| // | |
| // Some implementations use max(L(n1), L(n2)) where n1 = vec2(1,0) and n2 = | |
| // vec2(0,1). | |
| // | |
| // Some implementations use max(L(n1), L(n2), L(n3), L(n4)), | |
| // where n3 = (n1 + n2) / |n1 + n2| = (n1 + n2)/sqrt(2) | |
| // n4 = (n1 - n2) / |n1 - n2| = (n1 - n2)/sqrt(2). | |
| // In other words, two samples along the diagonal screen space directions have been | |
| // added, giving a strictly better estimate of the true maximum. | |
| // | |
| // It turns out we can get twice the sample count very cheaply. | |
| // We can use the linearity of Dp(r) to get these extra samples of L cheaply in | |
| // terms of the already taken samples, L(n1) and L(n2): | |
| // Denoting | |
| // dpx = Dp(r)n1 | |
| // dpy = Dp(r)n2 | |
| // dpxx = dot(dpx, dpx) | |
| // dpyy = dot(dpy, dpy) | |
| // dpxy = dot(dpx, dpy) | |
| // we obtain | |
| // L(n3) = |Dp(r)n1 + Dp(r)n2|^2/2 = (dpxx + dpyy)/2 + dpxy | |
| // L(n4) = |Dp(r)n1 - Dp(r)n2|^2/2 = (dpxx + dpyy)/2 - dpxy | |
| // max(L(n1), L(n2), L(n3), L(n4)) | |
| // = max(max(L(n1), L(n2)), max(L(n3), L(n4))) | |
| // = max(max(dpxx, dpyy), (dpxx + dpyy)/2 + abs(dpxy)) | |
| // So the extra cost is: one dot, one abs, one add, one multiply-add and one max. | |
| // (All scalar.) | |
| // | |
| // In section 3.8.10.1, the OpenGL ES 3 specification defines the "scale factor", | |
| // rho. In our terminology, this definition works out to taking sqrt(max(L(n1), | |
| // L(n2))). Some implementations will use this estimate, here we use the strictly | |
| // better sqrt(max(L(n1), L(n2), L(n3), L(n4))), since it's not much more expensive | |
| // to calculate. | |
| // Swap coordinates such that we can assume that the positive z-axis is major, in | |
| // what follows. | |
| out << " float3 ddxr = xMajor ? ddx(r).yzx : yMajor ? ddx(r).zxy : ddx(r).xyz;\n" | |
| " float3 ddyr = xMajor ? ddy(r).yzx : yMajor ? ddy(r).zxy : ddy(r).xyz;\n" | |
| " r = xMajor ? r.yzx : yMajor ? r.zxy : r.xyz;\n"; | |
| out << " float2 s = 0.5*float2(width, height);\n" | |
| " float2 dpx = s * (ddxr.xy - ddxr.z*r.xy/r.z)/r.z;\n" | |
| " float2 dpy = s * (ddyr.xy - ddyr.z*r.xy/r.z)/r.z;\n" | |
| " float dpxx = dot(dpx, dpx);\n;" | |
| " float dpyy = dot(dpy, dpy);\n;" | |
| " float dpxy = dot(dpx, dpy);\n" | |
| " float ma = max(dpxx, dpyy);\n" | |
| " float mb = 0.5 * (dpxx + dpyy) + abs(dpxy);\n" | |
| " float mab = max(ma, mb);\n" | |
| " float lod = 0.5f * log2(mab);\n"; | |
| } | |
| else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) | |
| { | |
| // ESSL 3.00.6 spec section 8.8: "For the cube version, the partial | |
| // derivatives of P are assumed to be in the coordinate system used before | |
| // texture coordinates are projected onto the appropriate cube face." | |
| // ddx[0] and ddy[0] are the derivatives of t.x passed into the function | |
| // ddx[1] and ddy[1] are the derivatives of t.y passed into the function | |
| // ddx[2] and ddy[2] are the derivatives of t.z passed into the function | |
| // Determine the derivatives of u, v and m | |
| out << " float dudx = xMajor ? ddx[2] : (yMajor && t.y < 0.0f ? -ddx[0] " | |
| ": ddx[0]);\n" | |
| " float dudy = xMajor ? ddy[2] : (yMajor && t.y < 0.0f ? -ddy[0] " | |
| ": ddy[0]);\n" | |
| " float dvdx = yMajor ? ddx[2] : (negative ? ddx[1] : -ddx[1]);\n" | |
| " float dvdy = yMajor ? ddy[2] : (negative ? ddy[1] : -ddy[1]);\n" | |
| " float dmdx = xMajor ? ddx[0] : (yMajor ? ddx[1] : ddx[2]);\n" | |
| " float dmdy = xMajor ? ddy[0] : (yMajor ? ddy[1] : ddy[2]);\n"; | |
| // Now determine the derivatives of the face coordinates, using the | |
| // derivatives calculated above. | |
| // d / dx (u(x) * 0.5 / m(x) + 0.5) | |
| // = 0.5 * (m(x) * u'(x) - u(x) * m'(x)) / m(x)^2 | |
| out << " float dfacexdx = 0.5f * (m * dudx - u * dmdx) / (m * m);\n" | |
| " float dfaceydx = 0.5f * (m * dvdx - v * dmdx) / (m * m);\n" | |
| " float dfacexdy = 0.5f * (m * dudy - u * dmdy) / (m * m);\n" | |
| " float dfaceydy = 0.5f * (m * dvdy - v * dmdy) / (m * m);\n" | |
| " float2 sizeVec = float2(width, height);\n" | |
| " float2 faceddx = float2(dfacexdx, dfaceydx) * sizeVec;\n" | |
| " float2 faceddy = float2(dfacexdy, dfaceydy) * sizeVec;\n"; | |
| // Optimization: instead of: log2(max(length(faceddx), length(faceddy))) | |
| // we compute: log2(max(length(faceddx)^2, length(faceddy)^2)) / 2 | |
| out << " float lengthfaceddx2 = dot(faceddx, faceddx);\n" | |
| " float lengthfaceddy2 = dot(faceddy, faceddy);\n" | |
| " float lod = log2(max(lengthfaceddx2, lengthfaceddy2)) * 0.5f;\n"; | |
| } | |
| out << " mip = uint(min(max(round(lod), 0), levels - 1));\n" | |
| << " " << textureReference | |
| << ".GetDimensions(baseLevel + mip, width, height, layers, levels);\n"; | |
| } | |
| // Convert from normalized floating-point to integer | |
| static const ImmutableString kXPrefix("int(floor(width * frac("); | |
| static const ImmutableString kYPrefix("int(floor(height * frac("); | |
| static const ImmutableString kSuffix(")))"); | |
| ImmutableStringBuilder texCoordXBuilder(kXPrefix.length() + texCoordX->length() + | |
| kSuffix.length()); | |
| texCoordXBuilder << kXPrefix << *texCoordX << kSuffix; | |
| *texCoordX = texCoordXBuilder; | |
| ImmutableStringBuilder texCoordYBuilder(kYPrefix.length() + texCoordX->length() + | |
| kSuffix.length()); | |
| texCoordYBuilder << kYPrefix << *texCoordY << kSuffix; | |
| *texCoordY = texCoordYBuilder; | |
| *texCoordZ = ImmutableString("face"); | |
| } | |
| else if (textureFunction.method != TextureFunctionHLSL::TextureFunction::FETCH) | |
| { | |
| if (IsSamplerArray(textureFunction.sampler)) | |
| { | |
| out << " float width; float height; float layers; float levels;\n"; | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0) | |
| { | |
| out << " uint mip = 0;\n"; | |
| } | |
| else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS) | |
| { | |
| out << " uint mip = bias;\n"; | |
| } | |
| else | |
| { | |
| out << " " << textureReference | |
| << ".GetDimensions(baseLevel, width, height, layers, levels);\n"; | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT || | |
| textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS) | |
| { | |
| out << " float2 tSized = float2(t.x * width, t.y * height);\n" | |
| " float dx = length(ddx(tSized));\n" | |
| " float dy = length(ddy(tSized));\n" | |
| " float lod = log2(max(dx, dy));\n"; | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS) | |
| { | |
| out << " lod += bias;\n"; | |
| } | |
| } | |
| else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) | |
| { | |
| out << " float2 sizeVec = float2(width, height);\n" | |
| " float2 sizeDdx = ddx * sizeVec;\n" | |
| " float2 sizeDdy = ddy * sizeVec;\n" | |
| " float lod = log2(max(dot(sizeDdx, sizeDdx), " | |
| "dot(sizeDdy, sizeDdy))) * 0.5f;\n"; | |
| } | |
| out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; | |
| } | |
| out << " " << textureReference | |
| << ".GetDimensions(baseLevel + mip, width, height, layers, levels);\n"; | |
| } | |
| else if (IsSampler2D(textureFunction.sampler)) | |
| { | |
| out << " float width; float height; float levels;\n"; | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0) | |
| { | |
| out << " uint mip = 0;\n"; | |
| } | |
| else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS) | |
| { | |
| out << " uint mip = bias;\n"; | |
| } | |
| else | |
| { | |
| out << " " << textureReference | |
| << ".GetDimensions(baseLevel, width, height, levels);\n"; | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT || | |
| textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS) | |
| { | |
| out << " float2 tSized = float2(t.x * width, t.y * height);\n" | |
| " float dx = length(ddx(tSized));\n" | |
| " float dy = length(ddy(tSized));\n" | |
| " float lod = log2(max(dx, dy));\n"; | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS) | |
| { | |
| out << " lod += bias;\n"; | |
| } | |
| } | |
| else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) | |
| { | |
| out << " float2 sizeVec = float2(width, height);\n" | |
| " float2 sizeDdx = ddx * sizeVec;\n" | |
| " float2 sizeDdy = ddy * sizeVec;\n" | |
| " float lod = log2(max(dot(sizeDdx, sizeDdx), " | |
| "dot(sizeDdy, sizeDdy))) * 0.5f;\n"; | |
| } | |
| out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; | |
| } | |
| out << " " << textureReference | |
| << ".GetDimensions(baseLevel + mip, width, height, levels);\n"; | |
| } | |
| else if (IsSampler3D(textureFunction.sampler)) | |
| { | |
| out << " float width; float height; float depth; float levels;\n"; | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0) | |
| { | |
| out << " uint mip = 0;\n"; | |
| } | |
| else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS) | |
| { | |
| out << " uint mip = bias;\n"; | |
| } | |
| else | |
| { | |
| out << " " << textureReference | |
| << ".GetDimensions(baseLevel, width, height, depth, levels);\n"; | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT || | |
| textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS) | |
| { | |
| out << " float3 tSized = float3(t.x * width, t.y * height, t.z * depth);\n" | |
| " float dx = length(ddx(tSized));\n" | |
| " float dy = length(ddy(tSized));\n" | |
| " float lod = log2(max(dx, dy));\n"; | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS) | |
| { | |
| out << " lod += bias;\n"; | |
| } | |
| } | |
| else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) | |
| { | |
| out << " float3 sizeVec = float3(width, height, depth);\n" | |
| " float3 sizeDdx = ddx * sizeVec;\n" | |
| " float3 sizeDdy = ddy * sizeVec;\n" | |
| " float lod = log2(max(dot(sizeDdx, sizeDdx), dot(sizeDdy, " | |
| "sizeDdy))) * 0.5f;\n"; | |
| } | |
| out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; | |
| } | |
| out << " " << textureReference | |
| << ".GetDimensions(baseLevel + mip, width, height, depth, levels);\n"; | |
| } | |
| else | |
| UNREACHABLE(); | |
| OutputIntTexCoordWraps(out, textureFunction, texCoordX, texCoordY, texCoordZ); | |
| } | |
| } | |
| void OutputTextureGatherFunctionBody(TInfoSinkBase &out, | |
| const TextureFunctionHLSL::TextureFunction &textureFunction, | |
| ShShaderOutput outputType, | |
| const ImmutableString &textureReference, | |
| const ImmutableString &samplerReference, | |
| const ImmutableString &texCoordX, | |
| const ImmutableString &texCoordY, | |
| const ImmutableString &texCoordZ) | |
| { | |
| const int hlslCoords = GetHLSLCoordCount(textureFunction, outputType); | |
| ImmutableString samplerCoordTypeString( | |
| GetSamplerCoordinateTypeString(textureFunction, hlslCoords)); | |
| ImmutableStringBuilder samplerCoordBuilder( | |
| samplerCoordTypeString.length() + strlen("(") + texCoordX.length() + strlen(", ") + | |
| texCoordY.length() + strlen(", ") + texCoordZ.length() + strlen(")")); | |
| samplerCoordBuilder << samplerCoordTypeString << "(" << texCoordX << ", " << texCoordY; | |
| if (hlslCoords >= 3) | |
| { | |
| if (textureFunction.coords < 3) | |
| { | |
| samplerCoordBuilder << ", 0"; | |
| } | |
| else | |
| { | |
| samplerCoordBuilder << ", " << texCoordZ; | |
| } | |
| } | |
| samplerCoordBuilder << ")"; | |
| ImmutableString samplerCoordString(samplerCoordBuilder); | |
| if (IsShadowSampler(textureFunction.sampler)) | |
| { | |
| out << "return " << textureReference << ".GatherCmp(" << samplerReference << ", " | |
| << samplerCoordString << ", refZ"; | |
| if (textureFunction.offset) | |
| { | |
| out << ", offset"; | |
| } | |
| out << ");\n"; | |
| return; | |
| } | |
| constexpr std::array<const char *, 4> kHLSLGatherFunctions = { | |
| {"GatherRed", "GatherGreen", "GatherBlue", "GatherAlpha"}}; | |
| out << " switch(comp)\n" | |
| " {\n"; | |
| for (size_t component = 0; component < kHLSLGatherFunctions.size(); ++component) | |
| { | |
| out << " case " << component << ":\n" | |
| << " return " << textureReference << "." << kHLSLGatherFunctions[component] | |
| << "(" << samplerReference << ", " << samplerCoordString; | |
| if (textureFunction.offset) | |
| { | |
| out << ", offset"; | |
| } | |
| out << ");\n"; | |
| } | |
| out << " default:\n" | |
| " return float4(0.0, 0.0, 0.0, 1.0);\n" | |
| " }\n"; | |
| } | |
| void OutputTextureSampleFunctionReturnStatement( | |
| TInfoSinkBase &out, | |
| const TextureFunctionHLSL::TextureFunction &textureFunction, | |
| const ShShaderOutput outputType, | |
| const ImmutableString &textureReference, | |
| const ImmutableString &samplerReference, | |
| const ImmutableString &texCoordX, | |
| const ImmutableString &texCoordY, | |
| const ImmutableString &texCoordZ) | |
| { | |
| out << " return "; | |
| if (IsIntegerSampler(textureFunction.sampler) && !IsSamplerCube(textureFunction.sampler) && | |
| textureFunction.method != TextureFunctionHLSL::TextureFunction::FETCH) | |
| { | |
| out << " useBorderColor ? "; | |
| if (IsIntegerSamplerUnsigned(textureFunction.sampler)) | |
| { | |
| out << "asuint"; | |
| } | |
| out << "(samplerMetadata[samplerIndex].intBorderColor) : "; | |
| } | |
| // HLSL intrinsic | |
| if (outputType == SH_HLSL_3_0_OUTPUT) | |
| { | |
| switch (textureFunction.sampler) | |
| { | |
| case EbtSampler2D: | |
| case EbtSamplerExternalOES: | |
| out << "tex2D"; | |
| break; | |
| case EbtSamplerCube: | |
| out << "texCUBE"; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| switch (textureFunction.method) | |
| { | |
| case TextureFunctionHLSL::TextureFunction::IMPLICIT: | |
| out << "(" << samplerReference << ", "; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::BIAS: | |
| out << "bias(" << samplerReference << ", "; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD: | |
| out << "lod(" << samplerReference << ", "; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD0: | |
| out << "lod(" << samplerReference << ", "; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD0BIAS: | |
| out << "lod(" << samplerReference << ", "; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::GRAD: | |
| out << "grad(" << samplerReference << ", "; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT) | |
| { | |
| OutputHLSL4SampleFunctionPrefix(out, textureFunction, textureReference, samplerReference); | |
| } | |
| else | |
| UNREACHABLE(); | |
| const int hlslCoords = GetHLSLCoordCount(textureFunction, outputType); | |
| out << GetSamplerCoordinateTypeString(textureFunction, hlslCoords) << "(" << texCoordX << ", " | |
| << texCoordY; | |
| if (outputType == SH_HLSL_3_0_OUTPUT) | |
| { | |
| if (hlslCoords >= 3) | |
| { | |
| if (textureFunction.coords < 3) | |
| { | |
| out << ", 0"; | |
| } | |
| else | |
| { | |
| out << ", " << texCoordZ; | |
| } | |
| } | |
| if (hlslCoords == 4) | |
| { | |
| switch (textureFunction.method) | |
| { | |
| case TextureFunctionHLSL::TextureFunction::BIAS: | |
| out << ", bias"; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD: | |
| out << ", lod"; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD0: | |
| out << ", 0"; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD0BIAS: | |
| out << ", bias"; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| out << ")"; | |
| } | |
| else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT) | |
| { | |
| if (hlslCoords >= 3) | |
| { | |
| ASSERT(!IsIntegerSampler(textureFunction.sampler) || | |
| !IsSamplerCube(textureFunction.sampler) || texCoordZ == "face"); | |
| out << ", " << texCoordZ; | |
| } | |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) | |
| { | |
| if (IsIntegerSampler(textureFunction.sampler)) | |
| { | |
| out << ", mip)"; | |
| } | |
| else if (IsShadowSampler(textureFunction.sampler)) | |
| { | |
| // Compare value | |
| if (textureFunction.proj) | |
| { | |
| // According to ESSL 3.00.4 sec 8.8 p95 on textureProj: | |
| // The resulting third component of P' in the shadow forms is used as | |
| // Dref | |
| out << "), " << texCoordZ; | |
| } | |
| else | |
| { | |
| switch (textureFunction.coords) | |
| { | |
| case 3: | |
| out << "), t.z"; | |
| break; | |
| case 4: | |
| out << "), t.w"; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| out << "), ddx, ddy"; | |
| } | |
| } | |
| else if (IsIntegerSampler(textureFunction.sampler) || | |
| textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH) | |
| { | |
| if (IsSampler2DMS(textureFunction.sampler) || | |
| IsSampler2DMSArray(textureFunction.sampler)) | |
| out << "), index"; | |
| else | |
| out << ", mip)"; | |
| } | |
| else if (IsShadowSampler(textureFunction.sampler)) | |
| { | |
| // Compare value | |
| if (textureFunction.proj) | |
| { | |
| // According to ESSL 3.00.4 sec 8.8 p95 on textureProj: | |
| // The resulting third component of P' in the shadow forms is used as Dref | |
| out << "), " << texCoordZ; | |
| } | |
| else | |
| { | |
| switch (textureFunction.coords) | |
| { | |
| case 3: | |
| out << "), t.z"; | |
| break; | |
| case 4: | |
| out << "), t.w"; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| switch (textureFunction.method) | |
| { | |
| case TextureFunctionHLSL::TextureFunction::IMPLICIT: | |
| out << ")"; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::BIAS: | |
| out << "), bias"; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD: | |
| out << "), lod"; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD0: | |
| out << "), 0"; | |
| break; | |
| case TextureFunctionHLSL::TextureFunction::LOD0BIAS: | |
| out << "), bias"; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| if (textureFunction.offset && | |
| (!IsIntegerSampler(textureFunction.sampler) || | |
| textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)) | |
| { | |
| out << ", offset"; | |
| } | |
| } | |
| else | |
| UNREACHABLE(); | |
| out << ");\n"; // Close the sample function call and return statement | |
| } | |
| } // Anonymous namespace | |
| ImmutableString TextureFunctionHLSL::TextureFunction::name() const | |
| { | |
| static const ImmutableString kGlTextureName("gl_texture"); | |
| ImmutableString suffix(TextureTypeSuffix(this->sampler)); | |
| ImmutableStringBuilder name(kGlTextureName.length() + suffix.length() + 4u + 6u + 5u); | |
| name << kGlTextureName; | |
| // We need to include full the sampler type in the function name to make the signature unique | |
| // on D3D11, where samplers are passed to texture functions as indices. | |
| name << suffix; | |
| if (proj) | |
| { | |
| name << "Proj"; | |
| } | |
| if (offset) | |
| { | |
| name << "Offset"; | |
| } | |
| switch (method) | |
| { | |
| case IMPLICIT: | |
| break; | |
| case BIAS: | |
| break; // Extra parameter makes the signature unique | |
| case LOD: | |
| name << "Lod"; | |
| break; | |
| case LOD0: | |
| name << "Lod0"; | |
| break; | |
| case LOD0BIAS: | |
| name << "Lod0"; | |
| break; // Extra parameter makes the signature unique | |
| case SIZE: | |
| name << "Size"; | |
| break; | |
| case FETCH: | |
| name << "Fetch"; | |
| break; | |
| case GRAD: | |
| name << "Grad"; | |
| break; | |
| case GATHER: | |
| name << "Gather"; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| return name; | |
| } | |
| const char *TextureFunctionHLSL::TextureFunction::getReturnType() const | |
| { | |
| if (method == TextureFunction::SIZE) | |
| { | |
| switch (sampler) | |
| { | |
| case EbtSampler2D: | |
| case EbtISampler2D: | |
| case EbtUSampler2D: | |
| case EbtSampler2DShadow: | |
| case EbtSamplerCube: | |
| case EbtISamplerCube: | |
| case EbtUSamplerCube: | |
| case EbtSamplerCubeShadow: | |
| case EbtSamplerExternalOES: | |
| case EbtSampler2DMS: | |
| case EbtISampler2DMS: | |
| case EbtUSampler2DMS: | |
| return "int2"; | |
| case EbtSampler3D: | |
| case EbtISampler3D: | |
| case EbtUSampler3D: | |
| case EbtSampler2DArray: | |
| case EbtISampler2DArray: | |
| case EbtUSampler2DArray: | |
| case EbtSampler2DMSArray: | |
| case EbtISampler2DMSArray: | |
| case EbtUSampler2DMSArray: | |
| case EbtSampler2DArrayShadow: | |
| return "int3"; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| else // Sampling function | |
| { | |
| switch (sampler) | |
| { | |
| case EbtSampler2D: | |
| case EbtSampler2DMS: | |
| case EbtSampler2DMSArray: | |
| case EbtSampler3D: | |
| case EbtSamplerCube: | |
| case EbtSampler2DArray: | |
| case EbtSamplerExternalOES: | |
| return "float4"; | |
| case EbtISampler2D: | |
| case EbtISampler2DMS: | |
| case EbtISampler2DMSArray: | |
| case EbtISampler3D: | |
| case EbtISamplerCube: | |
| case EbtISampler2DArray: | |
| return "int4"; | |
| case EbtUSampler2D: | |
| case EbtUSampler2DMS: | |
| case EbtUSampler2DMSArray: | |
| case EbtUSampler3D: | |
| case EbtUSamplerCube: | |
| case EbtUSampler2DArray: | |
| return "uint4"; | |
| case EbtSampler2DShadow: | |
| case EbtSamplerCubeShadow: | |
| case EbtSampler2DArrayShadow: | |
| if (method == TextureFunctionHLSL::TextureFunction::GATHER) | |
| { | |
| return "float4"; | |
| } | |
| else | |
| { | |
| return "float"; | |
| } | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| return ""; | |
| } | |
| bool TextureFunctionHLSL::TextureFunction::operator<(const TextureFunction &rhs) const | |
| { | |
| return std::tie(sampler, coords, proj, offset, method) < | |
| std::tie(rhs.sampler, rhs.coords, rhs.proj, rhs.offset, rhs.method); | |
| } | |
| ImmutableString TextureFunctionHLSL::useTextureFunction(const ImmutableString &name, | |
| TBasicType samplerType, | |
| int coords, | |
| size_t argumentCount, | |
| bool lod0, | |
| sh::GLenum shaderType) | |
| { | |
| TextureFunction textureFunction; | |
| textureFunction.sampler = samplerType; | |
| textureFunction.coords = coords; | |
| textureFunction.method = TextureFunction::IMPLICIT; | |
| textureFunction.proj = false; | |
| textureFunction.offset = false; | |
| if (name == "texture2D" || name == "textureCube" || name == "texture") | |
| { | |
| textureFunction.method = TextureFunction::IMPLICIT; | |
| } | |
| else if (name == "texture2DProj" || name == "textureProj") | |
| { | |
| textureFunction.method = TextureFunction::IMPLICIT; | |
| textureFunction.proj = true; | |
| } | |
| else if (name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod" || | |
| name == "texture2DLodEXT" || name == "textureCubeLodEXT") | |
| { | |
| textureFunction.method = TextureFunction::LOD; | |
| } | |
| else if (name == "texture2DProjLod" || name == "textureProjLod" || | |
| name == "texture2DProjLodEXT") | |
| { | |
| textureFunction.method = TextureFunction::LOD; | |
| textureFunction.proj = true; | |
| } | |
| else if (name == "textureSize") | |
| { | |
| textureFunction.method = TextureFunction::SIZE; | |
| } | |
| else if (name == "textureOffset") | |
| { | |
| textureFunction.method = TextureFunction::IMPLICIT; | |
| textureFunction.offset = true; | |
| } | |
| else if (name == "textureProjOffset") | |
| { | |
| textureFunction.method = TextureFunction::IMPLICIT; | |
| textureFunction.offset = true; | |
| textureFunction.proj = true; | |
| } | |
| else if (name == "textureLodOffset") | |
| { | |
| textureFunction.method = TextureFunction::LOD; | |
| textureFunction.offset = true; | |
| } | |
| else if (name == "textureProjLodOffset") | |
| { | |
| textureFunction.method = TextureFunction::LOD; | |
| textureFunction.proj = true; | |
| textureFunction.offset = true; | |
| } | |
| else if (name == "texelFetch") | |
| { | |
| textureFunction.method = TextureFunction::FETCH; | |
| } | |
| else if (name == "texelFetchOffset") | |
| { | |
| textureFunction.method = TextureFunction::FETCH; | |
| textureFunction.offset = true; | |
| } | |
| else if (name == "textureGrad" || name == "texture2DGradEXT") | |
| { | |
| textureFunction.method = TextureFunction::GRAD; | |
| } | |
| else if (name == "textureGradOffset") | |
| { | |
| textureFunction.method = TextureFunction::GRAD; | |
| textureFunction.offset = true; | |
| } | |
| else if (name == "textureProjGrad" || name == "texture2DProjGradEXT" || | |
| name == "textureCubeGradEXT") | |
| { | |
| textureFunction.method = TextureFunction::GRAD; | |
| textureFunction.proj = true; | |
| } | |
| else if (name == "textureProjGradOffset") | |
| { | |
| textureFunction.method = TextureFunction::GRAD; | |
| textureFunction.proj = true; | |
| textureFunction.offset = true; | |
| } | |
| else if (name == "textureGather") | |
| { | |
| textureFunction.method = TextureFunction::GATHER; | |
| } | |
| else if (name == "textureGatherOffset") | |
| { | |
| textureFunction.method = TextureFunction::GATHER; | |
| textureFunction.offset = true; | |
| } | |
| else | |
| UNREACHABLE(); | |
| if (textureFunction.method == | |
| TextureFunction::IMPLICIT) // Could require lod 0 or have a bias argument | |
| { | |
| size_t mandatoryArgumentCount = 2; // All functions have sampler and coordinate arguments | |
| if (textureFunction.offset) | |
| { | |
| mandatoryArgumentCount++; | |
| } | |
| bool bias = (argumentCount > mandatoryArgumentCount); // Bias argument is optional | |
| if (lod0 || shaderType == GL_VERTEX_SHADER || shaderType == GL_COMPUTE_SHADER) | |
| { | |
| if (bias) | |
| { | |
| textureFunction.method = TextureFunction::LOD0BIAS; | |
| } | |
| else | |
| { | |
| textureFunction.method = TextureFunction::LOD0; | |
| } | |
| } | |
| else if (bias) | |
| { | |
| textureFunction.method = TextureFunction::BIAS; | |
| } | |
| } | |
| mUsesTexture.insert(textureFunction); | |
| return textureFunction.name(); | |
| } | |
| void TextureFunctionHLSL::textureFunctionHeader(TInfoSinkBase &out, | |
| const ShShaderOutput outputType, | |
| bool getDimensionsIgnoresBaseLevel) | |
| { | |
| for (const TextureFunction &textureFunction : mUsesTexture) | |
| { | |
| // Function header | |
| out << textureFunction.getReturnType() << " " << textureFunction.name() << "("; | |
| OutputTextureFunctionArgumentList(out, textureFunction, outputType); | |
| out << ")\n" | |
| "{\n"; | |
| // In some cases we use a variable to store the texture/sampler objects, but to work around | |
| // a D3D11 compiler bug related to discard inside a loop that is conditional on texture | |
| // sampling we need to call the function directly on references to the texture and sampler | |
| // arrays. The bug was found using dEQP-GLES3.functional.shaders.discard*loop_texture* | |
| // tests. | |
| ImmutableString textureReference(""); | |
| ImmutableString samplerReference(""); | |
| GetTextureReference(out, textureFunction, outputType, &textureReference, &samplerReference); | |
| if (textureFunction.method == TextureFunction::SIZE) | |
| { | |
| OutputTextureSizeFunctionBody(out, textureFunction, textureReference, | |
| getDimensionsIgnoresBaseLevel); | |
| } | |
| else | |
| { | |
| ImmutableString texCoordX("t.x"); | |
| ImmutableString texCoordY("t.y"); | |
| ImmutableString texCoordZ("t.z"); | |
| if (textureFunction.method == TextureFunction::GATHER) | |
| { | |
| OutputTextureGatherFunctionBody(out, textureFunction, outputType, textureReference, | |
| samplerReference, texCoordX, texCoordY, texCoordZ); | |
| } | |
| else | |
| { | |
| ProjectTextureCoordinates(textureFunction, &texCoordX, &texCoordY, &texCoordZ); | |
| OutputIntegerTextureSampleFunctionComputations( | |
| out, textureFunction, outputType, textureReference, &texCoordX, &texCoordY, | |
| &texCoordZ, getDimensionsIgnoresBaseLevel); | |
| OutputTextureSampleFunctionReturnStatement(out, textureFunction, outputType, | |
| textureReference, samplerReference, | |
| texCoordX, texCoordY, texCoordZ); | |
| } | |
| } | |
| out << "}\n" | |
| "\n"; | |
| } | |
| } | |
| } // namespace sh |