diff options
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/TextureFunctionHLSL.cpp')
-rw-r--r-- | gfx/angle/checkout/src/compiler/translator/TextureFunctionHLSL.cpp | 1614 |
1 files changed, 1614 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/TextureFunctionHLSL.cpp b/gfx/angle/checkout/src/compiler/translator/TextureFunctionHLSL.cpp new file mode 100644 index 0000000000..b88e6c9ce4 --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/TextureFunctionHLSL.cpp @@ -0,0 +1,1614 @@ +// +// 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 1: + return "int"; + 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 1: + return "float"; + 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 EbtSamplerBuffer: + hlslCoords = 1; + break; + case EbtSampler2D: + case EbtSamplerExternalOES: + case EbtSampler2DMS: + case EbtSamplerVideoWEBGL: + 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 (IsSamplerBuffer(textureFunction.sampler)) + { + return 1; + } + 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 EbtSamplerVideoWEBGL: + 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 1: + out << ", int t"; + break; + 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: + case EbtSamplerVideoWEBGL: + 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 if (!IsSamplerBuffer(textureFunction.sampler)) + 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: + case EbtSamplerVideoWEBGL: + 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 (IsSamplerBuffer(textureFunction.sampler)) + { + out << " uint width;\n" + << " " << textureReference << ".GetDimensions(width);\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(); + } + + const char *returnType = textureFunction.getReturnType(); + if (strcmp(returnType, "int3") == 0) + { + out << " return int3(width, height, depth);\n"; + } + else if (strcmp(returnType, "int2") == 0) + { + out << " return int2(width, height);\n"; + } + else + { + out << " return int(width);\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 EbtSamplerVideoWEBGL: + 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); + + if (hlslCoords >= 2) + { + out << "(" << texCoordX << ", " << texCoordY; + } + else if (hlslCoords == 1) + { + std::string varName(texCoordX.data()); + if (size_t pos = varName.find_last_of('.') != std::string::npos) + { + varName = varName.substr(0, pos); + } + out << "(" << varName; + } + else + { + out << "("; + } + + 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 if (IsSamplerBuffer(textureFunction.sampler)) + out << ")"; + 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: + case EbtSamplerVideoWEBGL: + return "int2"; + case EbtSampler3D: + case EbtISampler3D: + case EbtUSampler3D: + case EbtSampler2DArray: + case EbtISampler2DArray: + case EbtUSampler2DArray: + case EbtSampler2DMSArray: + case EbtISampler2DMSArray: + case EbtUSampler2DMSArray: + case EbtSampler2DArrayShadow: + return "int3"; + case EbtISamplerBuffer: + case EbtUSamplerBuffer: + case EbtSamplerBuffer: + return "int"; + default: + UNREACHABLE(); + } + } + else // Sampling function + { + switch (sampler) + { + case EbtSampler2D: + case EbtSampler2DMS: + case EbtSampler2DMSArray: + case EbtSampler3D: + case EbtSamplerCube: + case EbtSampler2DArray: + case EbtSamplerExternalOES: + case EbtSamplerVideoWEBGL: + case EbtSamplerBuffer: + return "float4"; + case EbtISampler2D: + case EbtISampler2DMS: + case EbtISampler2DMSArray: + case EbtISampler3D: + case EbtISamplerCube: + case EbtISampler2DArray: + case EbtISamplerBuffer: + return "int4"; + case EbtUSampler2D: + case EbtUSampler2DMS: + case EbtUSampler2DMSArray: + case EbtUSampler3D: + case EbtUSamplerCube: + case EbtUSampler2DArray: + case EbtUSamplerBuffer: + 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 if (name == "textureVideoWEBGL") + { + textureFunction.method = TextureFunction::IMPLICIT; + } + 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 |