blob: f87f1df01e9cd0dbffd6f1e1b9f41d1dfc7a1c99 [file] [log] [blame]
//
// 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