| // |
| // Copyright (c) 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/UtilsHLSL.h" |
| |
| namespace sh |
| { |
| |
| namespace |
| { |
| |
| void OutputIntTexCoordWrap(TInfoSinkBase &out, |
| const char *wrapMode, |
| const char *size, |
| const TString &texCoord, |
| const TString &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"; |
| |
| // CLAMP_TO_EDGE |
| out << "if (" << wrapMode << " == 1)\n"; |
| out << "{\n"; |
| out << " " << texCoordOutName << " = clamp(int(floor(" << size << " * " << texCoordOutName |
| << "Offset)), 0, int(" << size << ") - 1);\n"; |
| out << "}\n"; |
| |
| // MIRRORED_REPEAT |
| out << "else if (" << wrapMode << " == 3)\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, |
| TString *texCoordX, |
| TString *texCoordY, |
| TString *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 = "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 = "tiy"; |
| |
| if (IsSamplerArray(textureFunction.sampler)) |
| { |
| *texCoordZ = "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 = "tiz"; |
| } |
| } |
| |
| void OutputHLSL4SampleFunctionPrefix(TInfoSinkBase &out, |
| const TextureFunctionHLSL::TextureFunction &textureFunction, |
| const TString &textureReference, |
| const TString &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) |
| { |
| if (IsIntegerSampler(textureFunction.sampler) || |
| textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH) |
| { |
| switch (hlslCoords) |
| { |
| case 2: |
| return "int3"; |
| case 3: |
| 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: |
| 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 |
| { |
| switch (textureFunction.sampler) |
| { |
| case EbtSampler2D: |
| return 2; |
| case EbtSampler3D: |
| return 3; |
| case EbtSamplerCube: |
| return 3; |
| case EbtSampler2DArray: |
| return 3; |
| case EbtSamplerExternalOES: |
| return 2; |
| case EbtISampler2D: |
| return 2; |
| case EbtISampler3D: |
| return 3; |
| case EbtISamplerCube: |
| return 3; |
| case EbtISampler2DArray: |
| return 3; |
| case EbtUSampler2D: |
| return 2; |
| case EbtUSampler3D: |
| return 3; |
| case EbtUSamplerCube: |
| return 3; |
| case EbtUSampler2DArray: |
| return 3; |
| case EbtSampler2DShadow: |
| return 2; |
| case EbtSamplerCubeShadow: |
| return 3; |
| case EbtSampler2DArrayShadow: |
| return 3; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| 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: |
| out << ", int mip"; |
| break; |
| case TextureFunctionHLSL::TextureFunction::GRAD: |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| 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: |
| UNREACHABLE(); |
| } |
| } |
| |
| if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS || |
| textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS) |
| { |
| out << ", float bias"; |
| } |
| } |
| |
| void GetTextureReference(TInfoSinkBase &out, |
| const TextureFunctionHLSL::TextureFunction &textureFunction, |
| const ShShaderOutput outputType, |
| TString *textureReference, |
| TString *samplerReference) |
| { |
| if (outputType == SH_HLSL_4_1_OUTPUT) |
| { |
| TString suffix = TextureGroupSuffix(textureFunction.sampler); |
| if (TextureGroup(textureFunction.sampler) == HLSL_TEXTURE_2D) |
| { |
| *textureReference = TString("textures") + suffix + "[samplerIndex]"; |
| *samplerReference = TString("samplers") + suffix + "[samplerIndex]"; |
| } |
| else |
| { |
| out << " const uint textureIndex = samplerIndex - textureIndexOffset" << suffix |
| << ";\n"; |
| *textureReference = TString("textures") + suffix + "[textureIndex]"; |
| out << " const uint samplerArrayIndex = samplerIndex - samplerIndexOffset" << suffix |
| << ";\n"; |
| *samplerReference = TString("samplers") + suffix + "[samplerArrayIndex]"; |
| } |
| } |
| else |
| { |
| *textureReference = "x"; |
| *samplerReference = "s"; |
| } |
| } |
| |
| void OutputTextureSizeFunctionBody(TInfoSinkBase &out, |
| const TextureFunctionHLSL::TextureFunction &textureFunction, |
| const TString &textureReference, |
| bool getDimensionsIgnoresBaseLevel) |
| { |
| if (IsSampler2DMS(textureFunction.sampler)) |
| { |
| out << " uint width; uint height; uint samples;\n" |
| << " " << textureReference << ".GetDimensions(width, height, 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, |
| TString *texCoordX, |
| TString *texCoordY, |
| TString *texCoordZ) |
| { |
| if (textureFunction.proj) |
| { |
| TString proj(""); |
| switch (textureFunction.coords) |
| { |
| case 3: |
| proj = " / t.z"; |
| break; |
| case 4: |
| proj = " / t.w"; |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| *texCoordX = "(" + *texCoordX + proj + ")"; |
| *texCoordY = "(" + *texCoordY + proj + ")"; |
| *texCoordZ = "(" + *texCoordZ + proj + ")"; |
| } |
| } |
| |
| void OutputIntegerTextureSampleFunctionComputations( |
| TInfoSinkBase &out, |
| const TextureFunctionHLSL::TextureFunction &textureFunction, |
| const ShShaderOutput outputType, |
| const TString &textureReference, |
| TString *texCoordX, |
| TString *texCoordY, |
| TString *texCoordZ) |
| { |
| if (!IsIntegerSampler(textureFunction.sampler)) |
| { |
| return; |
| } |
| if (IsSamplerCube(textureFunction.sampler)) |
| { |
| out << " float width; float height; float layers; float levels;\n"; |
| |
| out << " uint mip = 0;\n"; |
| |
| out << " " << textureReference |
| << ".GetDimensions(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 << " 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) |
| { |
| out << " float2 tSized = float2(t.x * width, t.y * height);\n" |
| " float2 dx = ddx(tSized);\n" |
| " float2 dy = ddy(tSized);\n" |
| " float lod = 0.5f * log2(max(dot(dx, dx), dot(dy, dy)));\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(mip, width, height, layers, levels);\n"; |
| } |
| |
| // Convert from normalized floating-point to integer |
| *texCoordX = "int(floor(width * frac(" + *texCoordX + ")))"; |
| *texCoordY = "int(floor(height * frac(" + *texCoordY + ")))"; |
| *texCoordZ = "face"; |
| } |
| else if (textureFunction.method != TextureFunctionHLSL::TextureFunction::FETCH) |
| { |
| if (IsSampler2D(textureFunction.sampler)) |
| { |
| 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(0, 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(mip, width, height, layers, levels);\n"; |
| } |
| else |
| { |
| 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(0, 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(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(0, 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(mip, width, height, depth, levels);\n"; |
| } |
| else |
| UNREACHABLE(); |
| |
| OutputIntTexCoordWraps(out, textureFunction, texCoordX, texCoordY, texCoordZ); |
| } |
| } |
| |
| void OutputTextureSampleFunctionReturnStatement( |
| TInfoSinkBase &out, |
| const TextureFunctionHLSL::TextureFunction &textureFunction, |
| const ShShaderOutput outputType, |
| const TString &textureReference, |
| const TString &samplerReference, |
| const TString &texCoordX, |
| const TString &texCoordY, |
| const TString &texCoordZ) |
| { |
| out << " return "; |
| |
| // 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) |
| { |
| 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 |
| |
| TString TextureFunctionHLSL::TextureFunction::name() const |
| { |
| TString name = "gl_texture"; |
| |
| // 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 += TextureTypeSuffix(this->sampler); |
| |
| 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; |
| 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 EbtSampler2DArrayShadow: |
| return "int3"; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| else // Sampling function |
| { |
| switch (sampler) |
| { |
| case EbtSampler2D: |
| case EbtSampler3D: |
| case EbtSamplerCube: |
| case EbtSampler2DArray: |
| case EbtSamplerExternalOES: |
| return "float4"; |
| case EbtISampler2D: |
| case EbtISampler3D: |
| case EbtISamplerCube: |
| case EbtISampler2DArray: |
| return "int4"; |
| case EbtUSampler2D: |
| case EbtUSampler3D: |
| case EbtUSamplerCube: |
| case EbtUSampler2DArray: |
| return "uint4"; |
| case EbtSampler2DShadow: |
| case EbtSamplerCubeShadow: |
| case EbtSampler2DArrayShadow: |
| 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); |
| } |
| |
| TString TextureFunctionHLSL::useTextureFunction(const TString &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 |
| 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) |
| { |
| 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. |
| TString textureReference; |
| TString samplerReference; |
| GetTextureReference(out, textureFunction, outputType, &textureReference, &samplerReference); |
| |
| if (textureFunction.method == TextureFunction::SIZE) |
| { |
| OutputTextureSizeFunctionBody(out, textureFunction, textureReference, |
| getDimensionsIgnoresBaseLevel); |
| } |
| else |
| { |
| TString texCoordX("t.x"); |
| TString texCoordY("t.y"); |
| TString texCoordZ("t.z"); |
| ProjectTextureCoordinates(textureFunction, &texCoordX, &texCoordY, &texCoordZ); |
| OutputIntegerTextureSampleFunctionComputations(out, textureFunction, outputType, |
| textureReference, &texCoordX, &texCoordY, |
| &texCoordZ); |
| OutputTextureSampleFunctionReturnStatement(out, textureFunction, outputType, |
| textureReference, samplerReference, |
| texCoordX, texCoordY, texCoordZ); |
| } |
| |
| out << "}\n" |
| "\n"; |
| } |
| } |
| |
| } // namespace sh |