• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // TextureFunctionHLSL: Class for writing implementations of ESSL texture functions into HLSL
7 // output. Some of the implementations are straightforward and just call the HLSL equivalent of the
8 // ESSL texture function, others do more work to emulate ESSL texture sampling or size query
9 // behavior.
10 //
11 
12 #include "compiler/translator/TextureFunctionHLSL.h"
13 
14 #include "compiler/translator/ImmutableStringBuilder.h"
15 #include "compiler/translator/UtilsHLSL.h"
16 
17 namespace sh
18 {
19 
20 namespace
21 {
22 
OutputIntTexCoordWrap(TInfoSinkBase & out,const char * wrapMode,const char * size,const ImmutableString & texCoord,const char * texCoordOffset,const char * texCoordOutName)23 void OutputIntTexCoordWrap(TInfoSinkBase &out,
24                            const char *wrapMode,
25                            const char *size,
26                            const ImmutableString &texCoord,
27                            const char *texCoordOffset,
28                            const char *texCoordOutName)
29 {
30     // GLES 3.0.4 table 3.22 specifies how the wrap modes work. We don't use the formulas verbatim
31     // but rather use equivalent formulas that map better to HLSL.
32     out << "int " << texCoordOutName << ";\n";
33     out << "float " << texCoordOutName << "Offset = " << texCoord << " + float(" << texCoordOffset
34         << ") / " << size << ";\n";
35     out << "bool " << texCoordOutName << "UseBorderColor = false;\n";
36 
37     // CLAMP_TO_EDGE
38     out << "if (" << wrapMode << " == 0)\n";
39     out << "{\n";
40     out << "    " << texCoordOutName << " = clamp(int(floor(" << size << " * " << texCoordOutName
41         << "Offset)), 0, int(" << size << ") - 1);\n";
42     out << "}\n";
43 
44     // CLAMP_TO_BORDER
45     out << "else if (" << wrapMode << " == 3)\n";
46     out << "{\n";
47     out << "    int texCoordInt = int(floor(" << size << " * " << texCoordOutName << "Offset));\n";
48     out << "    " << texCoordOutName << " = clamp(texCoordInt, 0, int(" << size << ") - 1);\n";
49     out << "    " << texCoordOutName << "UseBorderColor = (texCoordInt != " << texCoordOutName
50         << ");\n";
51     out << "}\n";
52 
53     // MIRRORED_REPEAT
54     out << "else if (" << wrapMode << " == 2)\n";
55     out << "{\n";
56     out << "    float coordWrapped = 1.0 - abs(frac(abs(" << texCoordOutName
57         << "Offset) * 0.5) * 2.0 - 1.0);\n";
58     out << "    " << texCoordOutName << " = int(floor(" << size << " * coordWrapped));\n";
59     out << "}\n";
60 
61     // REPEAT
62     out << "else\n";
63     out << "{\n";
64     out << "    " << texCoordOutName << " = int(floor(" << size << " * frac(" << texCoordOutName
65         << "Offset)));\n";
66     out << "}\n";
67 }
68 
OutputIntTexCoordWraps(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,ImmutableString * texCoordX,ImmutableString * texCoordY,ImmutableString * texCoordZ)69 void OutputIntTexCoordWraps(TInfoSinkBase &out,
70                             const TextureFunctionHLSL::TextureFunction &textureFunction,
71                             ImmutableString *texCoordX,
72                             ImmutableString *texCoordY,
73                             ImmutableString *texCoordZ)
74 {
75     // Convert from normalized floating-point to integer
76     out << "int wrapS = samplerMetadata[samplerIndex].wrapModes & 0x3;\n";
77     if (textureFunction.offset)
78     {
79         OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "offset.x", "tix");
80     }
81     else
82     {
83         OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "0", "tix");
84     }
85     *texCoordX = ImmutableString("tix");
86     out << "int wrapT = (samplerMetadata[samplerIndex].wrapModes >> 2) & 0x3;\n";
87     if (textureFunction.offset)
88     {
89         OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "offset.y", "tiy");
90     }
91     else
92     {
93         OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "0", "tiy");
94     }
95     *texCoordY = ImmutableString("tiy");
96 
97     bool tizAvailable = false;
98 
99     if (IsSamplerArray(textureFunction.sampler))
100     {
101         *texCoordZ = ImmutableString("int(max(0, min(layers - 1, floor(0.5 + t.z))))");
102     }
103     else if (!IsSamplerCube(textureFunction.sampler) && !IsSampler2D(textureFunction.sampler))
104     {
105         out << "int wrapR = (samplerMetadata[samplerIndex].wrapModes >> 4) & 0x3;\n";
106         if (textureFunction.offset)
107         {
108             OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "offset.z", "tiz");
109         }
110         else
111         {
112             OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "0", "tiz");
113         }
114         *texCoordZ   = ImmutableString("tiz");
115         tizAvailable = true;
116     }
117 
118     out << "bool useBorderColor = tixUseBorderColor || tiyUseBorderColor"
119         << (tizAvailable ? " || tizUseBorderColor" : "") << ";\n";
120 }
121 
OutputHLSL4SampleFunctionPrefix(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,const ImmutableString & textureReference,const ImmutableString & samplerReference)122 void OutputHLSL4SampleFunctionPrefix(TInfoSinkBase &out,
123                                      const TextureFunctionHLSL::TextureFunction &textureFunction,
124                                      const ImmutableString &textureReference,
125                                      const ImmutableString &samplerReference)
126 {
127     out << textureReference;
128     if (IsIntegerSampler(textureFunction.sampler) ||
129         textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
130     {
131         out << ".Load(";
132         return;
133     }
134 
135     if (IsShadowSampler(textureFunction.sampler))
136     {
137         switch (textureFunction.method)
138         {
139             case TextureFunctionHLSL::TextureFunction::IMPLICIT:
140             case TextureFunctionHLSL::TextureFunction::BIAS:
141             case TextureFunctionHLSL::TextureFunction::LOD:
142                 out << ".SampleCmp(";
143                 break;
144             case TextureFunctionHLSL::TextureFunction::LOD0:
145             case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
146             case TextureFunctionHLSL::TextureFunction::GRAD:
147                 out << ".SampleCmpLevelZero(";
148                 break;
149             default:
150                 UNREACHABLE();
151         }
152     }
153     else
154     {
155         switch (textureFunction.method)
156         {
157             case TextureFunctionHLSL::TextureFunction::IMPLICIT:
158                 out << ".Sample(";
159                 break;
160             case TextureFunctionHLSL::TextureFunction::BIAS:
161                 out << ".SampleBias(";
162                 break;
163             case TextureFunctionHLSL::TextureFunction::LOD:
164             case TextureFunctionHLSL::TextureFunction::LOD0:
165             case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
166                 out << ".SampleLevel(";
167                 break;
168             case TextureFunctionHLSL::TextureFunction::GRAD:
169                 out << ".SampleGrad(";
170                 break;
171             default:
172                 UNREACHABLE();
173         }
174     }
175     out << samplerReference << ", ";
176 }
177 
GetSamplerCoordinateTypeString(const TextureFunctionHLSL::TextureFunction & textureFunction,int hlslCoords)178 const char *GetSamplerCoordinateTypeString(
179     const TextureFunctionHLSL::TextureFunction &textureFunction,
180     int hlslCoords)
181 {
182     // Gather[Red|Green|Blue|Alpha] accepts float texture coordinates on textures in integer or
183     // unsigned integer formats.
184     // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-to-gather
185     if ((IsIntegerSampler(textureFunction.sampler) &&
186          textureFunction.method != TextureFunctionHLSL::TextureFunction::GATHER) ||
187         textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
188     {
189         switch (hlslCoords)
190         {
191             case 2:
192                 if (IsSampler2DMS(textureFunction.sampler))
193                 {
194                     return "int2";
195                 }
196                 else
197                 {
198                     return "int3";
199                 }
200             case 3:
201                 if (IsSampler2DMSArray(textureFunction.sampler))
202                 {
203                     return "int3";
204                 }
205                 else
206                 {
207                     return "int4";
208                 }
209             default:
210                 UNREACHABLE();
211         }
212     }
213     else
214     {
215         switch (hlslCoords)
216         {
217             case 2:
218                 return "float2";
219             case 3:
220                 return "float3";
221             case 4:
222                 return "float4";
223             default:
224                 UNREACHABLE();
225         }
226     }
227     return "";
228 }
229 
GetHLSLCoordCount(const TextureFunctionHLSL::TextureFunction & textureFunction,ShShaderOutput outputType)230 int GetHLSLCoordCount(const TextureFunctionHLSL::TextureFunction &textureFunction,
231                       ShShaderOutput outputType)
232 {
233     if (outputType == SH_HLSL_3_0_OUTPUT)
234     {
235         int hlslCoords = 2;
236         switch (textureFunction.sampler)
237         {
238             case EbtSampler2D:
239             case EbtSamplerExternalOES:
240             case EbtSampler2DMS:
241                 hlslCoords = 2;
242                 break;
243             case EbtSamplerCube:
244                 hlslCoords = 3;
245                 break;
246             default:
247                 UNREACHABLE();
248         }
249 
250         switch (textureFunction.method)
251         {
252             case TextureFunctionHLSL::TextureFunction::IMPLICIT:
253             case TextureFunctionHLSL::TextureFunction::GRAD:
254                 return hlslCoords;
255             case TextureFunctionHLSL::TextureFunction::BIAS:
256             case TextureFunctionHLSL::TextureFunction::LOD:
257             case TextureFunctionHLSL::TextureFunction::LOD0:
258             case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
259                 return 4;
260             default:
261                 UNREACHABLE();
262         }
263     }
264     else
265     {
266         if (IsSampler3D(textureFunction.sampler) || IsSamplerArray(textureFunction.sampler) ||
267             IsSamplerCube(textureFunction.sampler))
268         {
269             return 3;
270         }
271         ASSERT(IsSampler2D(textureFunction.sampler));
272         return 2;
273     }
274     return 0;
275 }
276 
OutputTextureFunctionArgumentList(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,const ShShaderOutput outputType)277 void OutputTextureFunctionArgumentList(TInfoSinkBase &out,
278                                        const TextureFunctionHLSL::TextureFunction &textureFunction,
279                                        const ShShaderOutput outputType)
280 {
281     if (outputType == SH_HLSL_3_0_OUTPUT)
282     {
283         switch (textureFunction.sampler)
284         {
285             case EbtSampler2D:
286             case EbtSamplerExternalOES:
287                 out << "sampler2D s";
288                 break;
289             case EbtSamplerCube:
290                 out << "samplerCUBE s";
291                 break;
292             default:
293                 UNREACHABLE();
294         }
295     }
296     else
297     {
298         if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
299         {
300             out << TextureString(textureFunction.sampler) << " x, "
301                 << SamplerString(textureFunction.sampler) << " s";
302         }
303         else
304         {
305             ASSERT(outputType == SH_HLSL_4_1_OUTPUT);
306             // A bug in the D3D compiler causes some nested sampling operations to fail.
307             // See http://anglebug.com/1923
308             // TODO(jmadill): Reinstate the const keyword when possible.
309             out << /*"const"*/ "uint samplerIndex";
310         }
311     }
312 
313     if (textureFunction.method ==
314         TextureFunctionHLSL::TextureFunction::FETCH)  // Integer coordinates
315     {
316         switch (textureFunction.coords)
317         {
318             case 2:
319                 out << ", int2 t";
320                 break;
321             case 3:
322                 out << ", int3 t";
323                 break;
324             default:
325                 UNREACHABLE();
326         }
327     }
328     else  // Floating-point coordinates (except textureSize)
329     {
330         switch (textureFunction.coords)
331         {
332             case 0:
333                 break;  // textureSize(gSampler2DMS sampler)
334             case 1:
335                 out << ", int lod";
336                 break;  // textureSize()
337             case 2:
338                 out << ", float2 t";
339                 break;
340             case 3:
341                 out << ", float3 t";
342                 break;
343             case 4:
344                 out << ", float4 t";
345                 break;
346             default:
347                 UNREACHABLE();
348         }
349     }
350 
351     if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
352     {
353         switch (textureFunction.sampler)
354         {
355             case EbtSampler2D:
356             case EbtISampler2D:
357             case EbtUSampler2D:
358             case EbtSampler2DArray:
359             case EbtISampler2DArray:
360             case EbtUSampler2DArray:
361             case EbtSampler2DShadow:
362             case EbtSampler2DArrayShadow:
363             case EbtSamplerExternalOES:
364                 out << ", float2 ddx, float2 ddy";
365                 break;
366             case EbtSampler3D:
367             case EbtISampler3D:
368             case EbtUSampler3D:
369             case EbtSamplerCube:
370             case EbtISamplerCube:
371             case EbtUSamplerCube:
372             case EbtSamplerCubeShadow:
373                 out << ", float3 ddx, float3 ddy";
374                 break;
375             default:
376                 UNREACHABLE();
377         }
378     }
379 
380     switch (textureFunction.method)
381     {
382         case TextureFunctionHLSL::TextureFunction::IMPLICIT:
383             break;
384         case TextureFunctionHLSL::TextureFunction::BIAS:
385             break;  // Comes after the offset parameter
386         case TextureFunctionHLSL::TextureFunction::LOD:
387             out << ", float lod";
388             break;
389         case TextureFunctionHLSL::TextureFunction::LOD0:
390             break;
391         case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
392             break;  // Comes after the offset parameter
393         case TextureFunctionHLSL::TextureFunction::SIZE:
394             break;
395         case TextureFunctionHLSL::TextureFunction::FETCH:
396             if (IsSampler2DMS(textureFunction.sampler) ||
397                 IsSampler2DMSArray(textureFunction.sampler))
398                 out << ", int index";
399             else
400                 out << ", int mip";
401             break;
402         case TextureFunctionHLSL::TextureFunction::GRAD:
403             break;
404         case TextureFunctionHLSL::TextureFunction::GATHER:
405             break;
406         default:
407             UNREACHABLE();
408     }
409 
410     if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GATHER &&
411         IsShadowSampler(textureFunction.sampler))
412     {
413         out << ", float refZ";
414     }
415 
416     if (textureFunction.offset)
417     {
418         switch (textureFunction.sampler)
419         {
420             case EbtSampler3D:
421             case EbtISampler3D:
422             case EbtUSampler3D:
423                 out << ", int3 offset";
424                 break;
425             case EbtSampler2D:
426             case EbtSampler2DArray:
427             case EbtISampler2D:
428             case EbtISampler2DArray:
429             case EbtUSampler2D:
430             case EbtUSampler2DArray:
431             case EbtSampler2DShadow:
432             case EbtSampler2DArrayShadow:
433             case EbtSamplerExternalOES:
434                 out << ", int2 offset";
435                 break;
436             default:
437                 // Offset is not supported for multisampled textures.
438                 UNREACHABLE();
439         }
440     }
441 
442     if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS ||
443         textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
444     {
445         out << ", float bias";
446     }
447     else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GATHER &&
448              !IsShadowSampler(textureFunction.sampler))
449     {
450         out << ", int comp = 0";
451     }
452 }
453 
GetTextureReference(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,const ShShaderOutput outputType,ImmutableString * textureReference,ImmutableString * samplerReference)454 void GetTextureReference(TInfoSinkBase &out,
455                          const TextureFunctionHLSL::TextureFunction &textureFunction,
456                          const ShShaderOutput outputType,
457                          ImmutableString *textureReference,
458                          ImmutableString *samplerReference)
459 {
460     if (outputType == SH_HLSL_4_1_OUTPUT)
461     {
462         static const ImmutableString kTexturesStr("textures");
463         static const ImmutableString kSamplersStr("samplers");
464         static const ImmutableString kSamplerIndexStr("[samplerIndex]");
465         static const ImmutableString kTextureIndexStr("[textureIndex]");
466         static const ImmutableString kSamplerArrayIndexStr("[samplerArrayIndex]");
467         ImmutableString suffix(TextureGroupSuffix(textureFunction.sampler));
468 
469         if (TextureGroup(textureFunction.sampler) == HLSL_TEXTURE_2D)
470         {
471             ImmutableStringBuilder textureRefBuilder(kTexturesStr.length() + suffix.length() +
472                                                      kSamplerIndexStr.length());
473             textureRefBuilder << kTexturesStr << suffix << kSamplerIndexStr;
474             *textureReference = textureRefBuilder;
475             ImmutableStringBuilder samplerRefBuilder(kSamplersStr.length() + suffix.length() +
476                                                      kSamplerIndexStr.length());
477             samplerRefBuilder << kSamplersStr << suffix << kSamplerIndexStr;
478             *samplerReference = samplerRefBuilder;
479         }
480         else
481         {
482             out << "    const uint textureIndex = samplerIndex - textureIndexOffset"
483                 << suffix.data() << ";\n";
484             ImmutableStringBuilder textureRefBuilder(kTexturesStr.length() + suffix.length() +
485                                                      kTextureIndexStr.length());
486             textureRefBuilder << kTexturesStr << suffix << kTextureIndexStr;
487             *textureReference = textureRefBuilder;
488 
489             out << "    const uint samplerArrayIndex = samplerIndex - samplerIndexOffset"
490                 << suffix.data() << ";\n";
491             ImmutableStringBuilder samplerRefBuilder(kSamplersStr.length() + suffix.length() +
492                                                      kSamplerArrayIndexStr.length());
493             samplerRefBuilder << kSamplersStr << suffix << kSamplerArrayIndexStr;
494             *samplerReference = samplerRefBuilder;
495         }
496     }
497     else
498     {
499         *textureReference = ImmutableString("x");
500         *samplerReference = ImmutableString("s");
501     }
502 }
503 
OutputTextureSizeFunctionBody(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,const ImmutableString & textureReference,bool getDimensionsIgnoresBaseLevel)504 void OutputTextureSizeFunctionBody(TInfoSinkBase &out,
505                                    const TextureFunctionHLSL::TextureFunction &textureFunction,
506                                    const ImmutableString &textureReference,
507                                    bool getDimensionsIgnoresBaseLevel)
508 {
509     if (IsSampler2DMS(textureFunction.sampler))
510     {
511         out << "    uint width; uint height; uint samples;\n"
512             << "    " << textureReference << ".GetDimensions(width, height, samples);\n";
513     }
514     else if (IsSampler2DMSArray(textureFunction.sampler))
515     {
516         out << "    uint width; uint height; uint depth; uint samples;\n"
517             << "    " << textureReference << ".GetDimensions(width, height, depth, samples);\n";
518     }
519     else
520     {
521         if (getDimensionsIgnoresBaseLevel)
522         {
523             out << "    int baseLevel = samplerMetadata[samplerIndex].baseLevel;\n";
524         }
525         else
526         {
527             out << "    int baseLevel = 0;\n";
528         }
529 
530         if (IsSampler3D(textureFunction.sampler) || IsSamplerArray(textureFunction.sampler) ||
531             (IsIntegerSampler(textureFunction.sampler) && IsSamplerCube(textureFunction.sampler)))
532         {
533             // "depth" stores either the number of layers in an array texture or 3D depth
534             out << "    uint width; uint height; uint depth; uint numberOfLevels;\n"
535                 << "    " << textureReference
536                 << ".GetDimensions(baseLevel, width, height, depth, numberOfLevels);\n"
537                 << "    width = max(width >> lod, 1);\n"
538                 << "    height = max(height >> lod, 1);\n";
539 
540             if (!IsSamplerArray(textureFunction.sampler))
541             {
542                 out << "    depth = max(depth >> lod, 1);\n";
543             }
544         }
545         else if (IsSampler2D(textureFunction.sampler) || IsSamplerCube(textureFunction.sampler))
546         {
547             out << "    uint width; uint height; uint numberOfLevels;\n"
548                 << "    " << textureReference
549                 << ".GetDimensions(baseLevel, width, height, numberOfLevels);\n"
550                 << "    width = max(width >> lod, 1);\n"
551                 << "    height = max(height >> lod, 1);\n";
552         }
553         else
554             UNREACHABLE();
555     }
556 
557     if (strcmp(textureFunction.getReturnType(), "int3") == 0)
558     {
559         out << "    return int3(width, height, depth);\n";
560     }
561     else
562     {
563         out << "    return int2(width, height);\n";
564     }
565 }
566 
ProjectTextureCoordinates(const TextureFunctionHLSL::TextureFunction & textureFunction,ImmutableString * texCoordX,ImmutableString * texCoordY,ImmutableString * texCoordZ)567 void ProjectTextureCoordinates(const TextureFunctionHLSL::TextureFunction &textureFunction,
568                                ImmutableString *texCoordX,
569                                ImmutableString *texCoordY,
570                                ImmutableString *texCoordZ)
571 {
572     if (textureFunction.proj)
573     {
574         ImmutableString proj("");
575         switch (textureFunction.coords)
576         {
577             case 3:
578                 proj = ImmutableString(" / t.z");
579                 break;
580             case 4:
581                 proj = ImmutableString(" / t.w");
582                 break;
583             default:
584                 UNREACHABLE();
585         }
586         ImmutableStringBuilder texCoordXBuilder(texCoordX->length() + proj.length() + 2u);
587         texCoordXBuilder << '(' << *texCoordX << proj << ')';
588         *texCoordX = texCoordXBuilder;
589         ImmutableStringBuilder texCoordYBuilder(texCoordY->length() + proj.length() + 2u);
590         texCoordYBuilder << '(' << *texCoordY << proj << ')';
591         *texCoordY = texCoordYBuilder;
592         ImmutableStringBuilder texCoordZBuilder(texCoordZ->length() + proj.length() + 2u);
593         texCoordZBuilder << '(' << *texCoordZ << proj << ')';
594         *texCoordZ = texCoordZBuilder;
595     }
596 }
597 
OutputIntegerTextureSampleFunctionComputations(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,const ShShaderOutput outputType,const ImmutableString & textureReference,ImmutableString * texCoordX,ImmutableString * texCoordY,ImmutableString * texCoordZ,bool getDimensionsIgnoresBaseLevel)598 void OutputIntegerTextureSampleFunctionComputations(
599     TInfoSinkBase &out,
600     const TextureFunctionHLSL::TextureFunction &textureFunction,
601     const ShShaderOutput outputType,
602     const ImmutableString &textureReference,
603     ImmutableString *texCoordX,
604     ImmutableString *texCoordY,
605     ImmutableString *texCoordZ,
606     bool getDimensionsIgnoresBaseLevel)
607 {
608     if (!IsIntegerSampler(textureFunction.sampler))
609     {
610         return;
611     }
612 
613     if (getDimensionsIgnoresBaseLevel)
614     {
615         out << "    int baseLevel = samplerMetadata[samplerIndex].baseLevel;\n";
616     }
617     else
618     {
619         out << "    int baseLevel = 0;\n";
620     }
621 
622     if (IsSamplerCube(textureFunction.sampler))
623     {
624         out << "    float width; float height; float layers; float levels;\n";
625 
626         out << "    uint mip = 0;\n";
627 
628         out << "    " << textureReference
629             << ".GetDimensions(baseLevel + mip, width, height, layers, levels);\n";
630 
631         out << "    bool xMajor = abs(t.x) >= abs(t.y) && abs(t.x) >= abs(t.z);\n";
632         out << "    bool yMajor = abs(t.y) >= abs(t.z) && abs(t.y) > abs(t.x);\n";
633         out << "    bool zMajor = abs(t.z) > abs(t.x) && abs(t.z) > abs(t.y);\n";
634         out << "    bool negative = (xMajor && t.x < 0.0f) || (yMajor && t.y < 0.0f) || "
635                "(zMajor && t.z < 0.0f);\n";
636 
637         // FACE_POSITIVE_X = 000b
638         // FACE_NEGATIVE_X = 001b
639         // FACE_POSITIVE_Y = 010b
640         // FACE_NEGATIVE_Y = 011b
641         // FACE_POSITIVE_Z = 100b
642         // FACE_NEGATIVE_Z = 101b
643         out << "    int face = (int)negative + (int)yMajor * 2 + (int)zMajor * 4;\n";
644 
645         out << "    float u = xMajor ? -t.z : (yMajor && t.y < 0.0f ? -t.x : t.x);\n";
646         out << "    float v = yMajor ? t.z : (negative ? t.y : -t.y);\n";
647         out << "    float m = xMajor ? t.x : (yMajor ? t.y : t.z);\n";
648 
649         out << "    float3 r = any(t) ? t : float3(1, 0, 0);\n";
650         out << "    t.x = (u * 0.5f / m) + 0.5f;\n";
651         out << "    t.y = (v * 0.5f / m) + 0.5f;\n";
652 
653         // Mip level computation.
654         if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
655             textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD ||
656             textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
657         {
658             if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT)
659             {
660                 // We would like to calculate tha maximum of how many texels we move in the major
661                 // face's texture as we move across the screen in any direction. Namely, we want the
662                 // length of the directional derivative of the function p (defined below), maximized
663                 // over screen space directions. (For short: we want the norm of Dp.) For
664                 // simplicity, assume that z-axis is the major axis. By symmetry, we can assume that
665                 // the positive z direction is major. (The calculated value will be the same even if
666                 // this is false.) Let r denote the function from screen position to cube texture
667                 // coordinates. Then p can be written as p = s . P . r, where P(r) = (r.x, r.y)/r.z
668                 // is the projection onto the major cube face, and s = diag(width, height)/2. (s
669                 // linearly maps from the cube face into texture space, so that p(r) is in units of
670                 // texels.) The derivative is
671                 // Dp(r) = s |1 0 -r.x/r.z|
672                 //           |0 1 -r.y/r.z| |ddx(r) ddy(r)| / r.z
673                 //       = |dot(a, ddx(r)) dot(a, ddy(r))|
674                 //         |dot(b, ddx(r)) dot(b, ddy(r))| / (2 r.z)
675                 // where a = w * vec3(1, 0, -r.x/r.z)
676                 //       b = h * vec3(0, 1, -r.y/r.z)
677                 // We would like to know max(L(x)) over unit vectors x, where L(x) = |Dp(r) x|^2.
678                 // Since ddx(r) and ddy(r) are unknown, the best we can do is to sample L in some
679                 // directions and take the maximum across the samples.
680                 //
681                 // Some implementations use max(L(n1), L(n2)) where n1 = vec2(1,0) and n2 =
682                 // vec2(0,1).
683                 //
684                 // Some implementations use max(L(n1), L(n2), L(n3), L(n4)),
685                 // where n3 = (n1 + n2) / |n1 + n2| = (n1 + n2)/sqrt(2)
686                 //       n4 = (n1 - n2) / |n1 - n2| = (n1 - n2)/sqrt(2).
687                 // In other words, two samples along the diagonal screen space directions have been
688                 // added, giving a strictly better estimate of the true maximum.
689                 //
690                 // It turns out we can get twice the sample count very cheaply.
691                 // We can use the linearity of Dp(r) to get these extra samples of L cheaply in
692                 // terms of the already taken samples, L(n1) and L(n2):
693                 // Denoting
694                 // dpx = Dp(r)n1
695                 // dpy = Dp(r)n2
696                 // dpxx = dot(dpx, dpx)
697                 // dpyy = dot(dpy, dpy)
698                 // dpxy = dot(dpx, dpy)
699                 // we obtain
700                 // L(n3) = |Dp(r)n1 + Dp(r)n2|^2/2 = (dpxx + dpyy)/2 + dpxy
701                 // L(n4) = |Dp(r)n1 - Dp(r)n2|^2/2 = (dpxx + dpyy)/2 - dpxy
702                 // max(L(n1), L(n2), L(n3), L(n4))
703                 // = max(max(L(n1), L(n2)), max(L(n3), L(n4)))
704                 // = max(max(dpxx, dpyy), (dpxx + dpyy)/2 + abs(dpxy))
705                 // So the extra cost is: one dot, one abs, one add, one multiply-add and one max.
706                 // (All scalar.)
707                 //
708                 // In section 3.8.10.1, the OpenGL ES 3 specification defines the "scale factor",
709                 // rho. In our terminology, this definition works out to taking sqrt(max(L(n1),
710                 // L(n2))). Some implementations will use this estimate, here we use the strictly
711                 // better sqrt(max(L(n1), L(n2), L(n3), L(n4))), since it's not much more expensive
712                 // to calculate.
713 
714                 // Swap coordinates such that we can assume that the positive z-axis is major, in
715                 // what follows.
716                 out << "    float3 ddxr = xMajor ? ddx(r).yzx : yMajor ? ddx(r).zxy : ddx(r).xyz;\n"
717                        "    float3 ddyr = xMajor ? ddy(r).yzx : yMajor ? ddy(r).zxy : ddy(r).xyz;\n"
718                        "    r = xMajor ? r.yzx : yMajor ? r.zxy : r.xyz;\n";
719 
720                 out << "    float2 s = 0.5*float2(width, height);\n"
721                        "    float2 dpx = s * (ddxr.xy - ddxr.z*r.xy/r.z)/r.z;\n"
722                        "    float2 dpy = s * (ddyr.xy - ddyr.z*r.xy/r.z)/r.z;\n"
723                        "    float dpxx = dot(dpx, dpx);\n;"
724                        "    float dpyy = dot(dpy, dpy);\n;"
725                        "    float dpxy = dot(dpx, dpy);\n"
726                        "    float ma = max(dpxx, dpyy);\n"
727                        "    float mb = 0.5 * (dpxx + dpyy) + abs(dpxy);\n"
728                        "    float mab = max(ma, mb);\n"
729                        "    float lod = 0.5f * log2(mab);\n";
730             }
731             else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
732             {
733                 // ESSL 3.00.6 spec section 8.8: "For the cube version, the partial
734                 // derivatives of P are assumed to be in the coordinate system used before
735                 // texture coordinates are projected onto the appropriate cube face."
736                 // ddx[0] and ddy[0] are the derivatives of t.x passed into the function
737                 // ddx[1] and ddy[1] are the derivatives of t.y passed into the function
738                 // ddx[2] and ddy[2] are the derivatives of t.z passed into the function
739                 // Determine the derivatives of u, v and m
740                 out << "    float dudx = xMajor ? ddx[2] : (yMajor && t.y < 0.0f ? -ddx[0] "
741                        ": ddx[0]);\n"
742                        "    float dudy = xMajor ? ddy[2] : (yMajor && t.y < 0.0f ? -ddy[0] "
743                        ": ddy[0]);\n"
744                        "    float dvdx = yMajor ? ddx[2] : (negative ? ddx[1] : -ddx[1]);\n"
745                        "    float dvdy = yMajor ? ddy[2] : (negative ? ddy[1] : -ddy[1]);\n"
746                        "    float dmdx = xMajor ? ddx[0] : (yMajor ? ddx[1] : ddx[2]);\n"
747                        "    float dmdy = xMajor ? ddy[0] : (yMajor ? ddy[1] : ddy[2]);\n";
748                 // Now determine the derivatives of the face coordinates, using the
749                 // derivatives calculated above.
750                 // d / dx (u(x) * 0.5 / m(x) + 0.5)
751                 // = 0.5 * (m(x) * u'(x) - u(x) * m'(x)) / m(x)^2
752                 out << "    float dfacexdx = 0.5f * (m * dudx - u * dmdx) / (m * m);\n"
753                        "    float dfaceydx = 0.5f * (m * dvdx - v * dmdx) / (m * m);\n"
754                        "    float dfacexdy = 0.5f * (m * dudy - u * dmdy) / (m * m);\n"
755                        "    float dfaceydy = 0.5f * (m * dvdy - v * dmdy) / (m * m);\n"
756                        "    float2 sizeVec = float2(width, height);\n"
757                        "    float2 faceddx = float2(dfacexdx, dfaceydx) * sizeVec;\n"
758                        "    float2 faceddy = float2(dfacexdy, dfaceydy) * sizeVec;\n";
759                 // Optimization: instead of: log2(max(length(faceddx), length(faceddy)))
760                 // we compute: log2(max(length(faceddx)^2, length(faceddy)^2)) / 2
761                 out << "    float lengthfaceddx2 = dot(faceddx, faceddx);\n"
762                        "    float lengthfaceddy2 = dot(faceddy, faceddy);\n"
763                        "    float lod = log2(max(lengthfaceddx2, lengthfaceddy2)) * 0.5f;\n";
764             }
765             out << "    mip = uint(min(max(round(lod), 0), levels - 1));\n"
766                 << "    " << textureReference
767                 << ".GetDimensions(baseLevel + mip, width, height, layers, levels);\n";
768         }
769 
770         // Convert from normalized floating-point to integer
771         static const ImmutableString kXPrefix("int(floor(width * frac(");
772         static const ImmutableString kYPrefix("int(floor(height * frac(");
773         static const ImmutableString kSuffix(")))");
774         ImmutableStringBuilder texCoordXBuilder(kXPrefix.length() + texCoordX->length() +
775                                                 kSuffix.length());
776         texCoordXBuilder << kXPrefix << *texCoordX << kSuffix;
777         *texCoordX = texCoordXBuilder;
778         ImmutableStringBuilder texCoordYBuilder(kYPrefix.length() + texCoordX->length() +
779                                                 kSuffix.length());
780         texCoordYBuilder << kYPrefix << *texCoordY << kSuffix;
781         *texCoordY = texCoordYBuilder;
782         *texCoordZ = ImmutableString("face");
783     }
784     else if (textureFunction.method != TextureFunctionHLSL::TextureFunction::FETCH)
785     {
786         if (IsSamplerArray(textureFunction.sampler))
787         {
788             out << "    float width; float height; float layers; float levels;\n";
789             if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
790             {
791                 out << "    uint mip = 0;\n";
792             }
793             else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
794             {
795                 out << "    uint mip = bias;\n";
796             }
797             else
798             {
799 
800                 out << "    " << textureReference
801                     << ".GetDimensions(baseLevel, width, height, layers, levels);\n";
802                 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
803                     textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
804                 {
805                     out << "    float2 tSized = float2(t.x * width, t.y * height);\n"
806                            "    float dx = length(ddx(tSized));\n"
807                            "    float dy = length(ddy(tSized));\n"
808                            "    float lod = log2(max(dx, dy));\n";
809 
810                     if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
811                     {
812                         out << "    lod += bias;\n";
813                     }
814                 }
815                 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
816                 {
817                     out << "    float2 sizeVec = float2(width, height);\n"
818                            "    float2 sizeDdx = ddx * sizeVec;\n"
819                            "    float2 sizeDdy = ddy * sizeVec;\n"
820                            "    float lod = log2(max(dot(sizeDdx, sizeDdx), "
821                            "dot(sizeDdy, sizeDdy))) * 0.5f;\n";
822                 }
823 
824                 out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
825             }
826 
827             out << "    " << textureReference
828                 << ".GetDimensions(baseLevel + mip, width, height, layers, levels);\n";
829         }
830         else if (IsSampler2D(textureFunction.sampler))
831         {
832             out << "    float width; float height; float levels;\n";
833 
834             if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
835             {
836                 out << "    uint mip = 0;\n";
837             }
838             else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
839             {
840                 out << "    uint mip = bias;\n";
841             }
842             else
843             {
844                 out << "    " << textureReference
845                     << ".GetDimensions(baseLevel, width, height, levels);\n";
846 
847                 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
848                     textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
849                 {
850                     out << "    float2 tSized = float2(t.x * width, t.y * height);\n"
851                            "    float dx = length(ddx(tSized));\n"
852                            "    float dy = length(ddy(tSized));\n"
853                            "    float lod = log2(max(dx, dy));\n";
854 
855                     if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
856                     {
857                         out << "    lod += bias;\n";
858                     }
859                 }
860                 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
861                 {
862                     out << "    float2 sizeVec = float2(width, height);\n"
863                            "    float2 sizeDdx = ddx * sizeVec;\n"
864                            "    float2 sizeDdy = ddy * sizeVec;\n"
865                            "    float lod = log2(max(dot(sizeDdx, sizeDdx), "
866                            "dot(sizeDdy, sizeDdy))) * 0.5f;\n";
867                 }
868 
869                 out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
870             }
871 
872             out << "    " << textureReference
873                 << ".GetDimensions(baseLevel + mip, width, height, levels);\n";
874         }
875         else if (IsSampler3D(textureFunction.sampler))
876         {
877             out << "    float width; float height; float depth; float levels;\n";
878 
879             if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
880             {
881                 out << "    uint mip = 0;\n";
882             }
883             else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
884             {
885                 out << "    uint mip = bias;\n";
886             }
887             else
888             {
889                 out << "    " << textureReference
890                     << ".GetDimensions(baseLevel, width, height, depth, levels);\n";
891 
892                 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
893                     textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
894                 {
895                     out << "    float3 tSized = float3(t.x * width, t.y * height, t.z * depth);\n"
896                            "    float dx = length(ddx(tSized));\n"
897                            "    float dy = length(ddy(tSized));\n"
898                            "    float lod = log2(max(dx, dy));\n";
899 
900                     if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
901                     {
902                         out << "    lod += bias;\n";
903                     }
904                 }
905                 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
906                 {
907                     out << "    float3 sizeVec = float3(width, height, depth);\n"
908                            "    float3 sizeDdx = ddx * sizeVec;\n"
909                            "    float3 sizeDdy = ddy * sizeVec;\n"
910                            "    float lod = log2(max(dot(sizeDdx, sizeDdx), dot(sizeDdy, "
911                            "sizeDdy))) * 0.5f;\n";
912                 }
913 
914                 out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
915             }
916 
917             out << "    " << textureReference
918                 << ".GetDimensions(baseLevel + mip, width, height, depth, levels);\n";
919         }
920         else
921             UNREACHABLE();
922 
923         OutputIntTexCoordWraps(out, textureFunction, texCoordX, texCoordY, texCoordZ);
924     }
925 }
926 
OutputTextureGatherFunctionBody(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,ShShaderOutput outputType,const ImmutableString & textureReference,const ImmutableString & samplerReference,const ImmutableString & texCoordX,const ImmutableString & texCoordY,const ImmutableString & texCoordZ)927 void OutputTextureGatherFunctionBody(TInfoSinkBase &out,
928                                      const TextureFunctionHLSL::TextureFunction &textureFunction,
929                                      ShShaderOutput outputType,
930                                      const ImmutableString &textureReference,
931                                      const ImmutableString &samplerReference,
932                                      const ImmutableString &texCoordX,
933                                      const ImmutableString &texCoordY,
934                                      const ImmutableString &texCoordZ)
935 {
936     const int hlslCoords = GetHLSLCoordCount(textureFunction, outputType);
937     ImmutableString samplerCoordTypeString(
938         GetSamplerCoordinateTypeString(textureFunction, hlslCoords));
939     ImmutableStringBuilder samplerCoordBuilder(
940         samplerCoordTypeString.length() + strlen("(") + texCoordX.length() + strlen(", ") +
941         texCoordY.length() + strlen(", ") + texCoordZ.length() + strlen(")"));
942 
943     samplerCoordBuilder << samplerCoordTypeString << "(" << texCoordX << ", " << texCoordY;
944     if (hlslCoords >= 3)
945     {
946         if (textureFunction.coords < 3)
947         {
948             samplerCoordBuilder << ", 0";
949         }
950         else
951         {
952             samplerCoordBuilder << ", " << texCoordZ;
953         }
954     }
955     samplerCoordBuilder << ")";
956 
957     ImmutableString samplerCoordString(samplerCoordBuilder);
958 
959     if (IsShadowSampler(textureFunction.sampler))
960     {
961         out << "return " << textureReference << ".GatherCmp(" << samplerReference << ", "
962             << samplerCoordString << ", refZ";
963         if (textureFunction.offset)
964         {
965             out << ", offset";
966         }
967         out << ");\n";
968         return;
969     }
970 
971     constexpr std::array<const char *, 4> kHLSLGatherFunctions = {
972         {"GatherRed", "GatherGreen", "GatherBlue", "GatherAlpha"}};
973 
974     out << "    switch(comp)\n"
975            "    {\n";
976     for (size_t component = 0; component < kHLSLGatherFunctions.size(); ++component)
977     {
978         out << "        case " << component << ":\n"
979             << "            return " << textureReference << "." << kHLSLGatherFunctions[component]
980             << "(" << samplerReference << ", " << samplerCoordString;
981         if (textureFunction.offset)
982         {
983             out << ", offset";
984         }
985         out << ");\n";
986     }
987 
988     out << "        default:\n"
989            "            return float4(0.0, 0.0, 0.0, 1.0);\n"
990            "    }\n";
991 }
992 
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)993 void OutputTextureSampleFunctionReturnStatement(
994     TInfoSinkBase &out,
995     const TextureFunctionHLSL::TextureFunction &textureFunction,
996     const ShShaderOutput outputType,
997     const ImmutableString &textureReference,
998     const ImmutableString &samplerReference,
999     const ImmutableString &texCoordX,
1000     const ImmutableString &texCoordY,
1001     const ImmutableString &texCoordZ)
1002 {
1003     out << "    return ";
1004 
1005     if (IsIntegerSampler(textureFunction.sampler) && !IsSamplerCube(textureFunction.sampler) &&
1006         textureFunction.method != TextureFunctionHLSL::TextureFunction::FETCH)
1007     {
1008         out << " useBorderColor ? ";
1009         if (IsIntegerSamplerUnsigned(textureFunction.sampler))
1010         {
1011             out << "asuint";
1012         }
1013         out << "(samplerMetadata[samplerIndex].intBorderColor) : ";
1014     }
1015 
1016     // HLSL intrinsic
1017     if (outputType == SH_HLSL_3_0_OUTPUT)
1018     {
1019         switch (textureFunction.sampler)
1020         {
1021             case EbtSampler2D:
1022             case EbtSamplerExternalOES:
1023                 out << "tex2D";
1024                 break;
1025             case EbtSamplerCube:
1026                 out << "texCUBE";
1027                 break;
1028             default:
1029                 UNREACHABLE();
1030         }
1031 
1032         switch (textureFunction.method)
1033         {
1034             case TextureFunctionHLSL::TextureFunction::IMPLICIT:
1035                 out << "(" << samplerReference << ", ";
1036                 break;
1037             case TextureFunctionHLSL::TextureFunction::BIAS:
1038                 out << "bias(" << samplerReference << ", ";
1039                 break;
1040             case TextureFunctionHLSL::TextureFunction::LOD:
1041                 out << "lod(" << samplerReference << ", ";
1042                 break;
1043             case TextureFunctionHLSL::TextureFunction::LOD0:
1044                 out << "lod(" << samplerReference << ", ";
1045                 break;
1046             case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
1047                 out << "lod(" << samplerReference << ", ";
1048                 break;
1049             case TextureFunctionHLSL::TextureFunction::GRAD:
1050                 out << "grad(" << samplerReference << ", ";
1051                 break;
1052             default:
1053                 UNREACHABLE();
1054         }
1055     }
1056     else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
1057     {
1058         OutputHLSL4SampleFunctionPrefix(out, textureFunction, textureReference, samplerReference);
1059     }
1060     else
1061         UNREACHABLE();
1062 
1063     const int hlslCoords = GetHLSLCoordCount(textureFunction, outputType);
1064 
1065     out << GetSamplerCoordinateTypeString(textureFunction, hlslCoords) << "(" << texCoordX << ", "
1066         << texCoordY;
1067 
1068     if (outputType == SH_HLSL_3_0_OUTPUT)
1069     {
1070         if (hlslCoords >= 3)
1071         {
1072             if (textureFunction.coords < 3)
1073             {
1074                 out << ", 0";
1075             }
1076             else
1077             {
1078                 out << ", " << texCoordZ;
1079             }
1080         }
1081 
1082         if (hlslCoords == 4)
1083         {
1084             switch (textureFunction.method)
1085             {
1086                 case TextureFunctionHLSL::TextureFunction::BIAS:
1087                     out << ", bias";
1088                     break;
1089                 case TextureFunctionHLSL::TextureFunction::LOD:
1090                     out << ", lod";
1091                     break;
1092                 case TextureFunctionHLSL::TextureFunction::LOD0:
1093                     out << ", 0";
1094                     break;
1095                 case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
1096                     out << ", bias";
1097                     break;
1098                 default:
1099                     UNREACHABLE();
1100             }
1101         }
1102 
1103         out << ")";
1104     }
1105     else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
1106     {
1107         if (hlslCoords >= 3)
1108         {
1109             ASSERT(!IsIntegerSampler(textureFunction.sampler) ||
1110                    !IsSamplerCube(textureFunction.sampler) || texCoordZ == "face");
1111             out << ", " << texCoordZ;
1112         }
1113 
1114         if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
1115         {
1116             if (IsIntegerSampler(textureFunction.sampler))
1117             {
1118                 out << ", mip)";
1119             }
1120             else if (IsShadowSampler(textureFunction.sampler))
1121             {
1122                 // Compare value
1123                 if (textureFunction.proj)
1124                 {
1125                     // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
1126                     // The resulting third component of P' in the shadow forms is used as
1127                     // Dref
1128                     out << "), " << texCoordZ;
1129                 }
1130                 else
1131                 {
1132                     switch (textureFunction.coords)
1133                     {
1134                         case 3:
1135                             out << "), t.z";
1136                             break;
1137                         case 4:
1138                             out << "), t.w";
1139                             break;
1140                         default:
1141                             UNREACHABLE();
1142                     }
1143                 }
1144             }
1145             else
1146             {
1147                 out << "), ddx, ddy";
1148             }
1149         }
1150         else if (IsIntegerSampler(textureFunction.sampler) ||
1151                  textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
1152         {
1153             if (IsSampler2DMS(textureFunction.sampler) ||
1154                 IsSampler2DMSArray(textureFunction.sampler))
1155                 out << "), index";
1156             else
1157                 out << ", mip)";
1158         }
1159         else if (IsShadowSampler(textureFunction.sampler))
1160         {
1161             // Compare value
1162             if (textureFunction.proj)
1163             {
1164                 // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
1165                 // The resulting third component of P' in the shadow forms is used as Dref
1166                 out << "), " << texCoordZ;
1167             }
1168             else
1169             {
1170                 switch (textureFunction.coords)
1171                 {
1172                     case 3:
1173                         out << "), t.z";
1174                         break;
1175                     case 4:
1176                         out << "), t.w";
1177                         break;
1178                     default:
1179                         UNREACHABLE();
1180                 }
1181             }
1182         }
1183         else
1184         {
1185             switch (textureFunction.method)
1186             {
1187                 case TextureFunctionHLSL::TextureFunction::IMPLICIT:
1188                     out << ")";
1189                     break;
1190                 case TextureFunctionHLSL::TextureFunction::BIAS:
1191                     out << "), bias";
1192                     break;
1193                 case TextureFunctionHLSL::TextureFunction::LOD:
1194                     out << "), lod";
1195                     break;
1196                 case TextureFunctionHLSL::TextureFunction::LOD0:
1197                     out << "), 0";
1198                     break;
1199                 case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
1200                     out << "), bias";
1201                     break;
1202                 default:
1203                     UNREACHABLE();
1204             }
1205         }
1206 
1207         if (textureFunction.offset &&
1208             (!IsIntegerSampler(textureFunction.sampler) ||
1209              textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH))
1210         {
1211             out << ", offset";
1212         }
1213     }
1214     else
1215         UNREACHABLE();
1216 
1217     out << ");\n";  // Close the sample function call and return statement
1218 }
1219 
1220 }  // Anonymous namespace
1221 
name() const1222 ImmutableString TextureFunctionHLSL::TextureFunction::name() const
1223 {
1224     static const ImmutableString kGlTextureName("gl_texture");
1225 
1226     ImmutableString suffix(TextureTypeSuffix(this->sampler));
1227 
1228     ImmutableStringBuilder name(kGlTextureName.length() + suffix.length() + 4u + 6u + 5u);
1229 
1230     name << kGlTextureName;
1231 
1232     // We need to include full the sampler type in the function name to make the signature unique
1233     // on D3D11, where samplers are passed to texture functions as indices.
1234     name << suffix;
1235 
1236     if (proj)
1237     {
1238         name << "Proj";
1239     }
1240 
1241     if (offset)
1242     {
1243         name << "Offset";
1244     }
1245 
1246     switch (method)
1247     {
1248         case IMPLICIT:
1249             break;
1250         case BIAS:
1251             break;  // Extra parameter makes the signature unique
1252         case LOD:
1253             name << "Lod";
1254             break;
1255         case LOD0:
1256             name << "Lod0";
1257             break;
1258         case LOD0BIAS:
1259             name << "Lod0";
1260             break;  // Extra parameter makes the signature unique
1261         case SIZE:
1262             name << "Size";
1263             break;
1264         case FETCH:
1265             name << "Fetch";
1266             break;
1267         case GRAD:
1268             name << "Grad";
1269             break;
1270         case GATHER:
1271             name << "Gather";
1272             break;
1273         default:
1274             UNREACHABLE();
1275     }
1276 
1277     return name;
1278 }
1279 
getReturnType() const1280 const char *TextureFunctionHLSL::TextureFunction::getReturnType() const
1281 {
1282     if (method == TextureFunction::SIZE)
1283     {
1284         switch (sampler)
1285         {
1286             case EbtSampler2D:
1287             case EbtISampler2D:
1288             case EbtUSampler2D:
1289             case EbtSampler2DShadow:
1290             case EbtSamplerCube:
1291             case EbtISamplerCube:
1292             case EbtUSamplerCube:
1293             case EbtSamplerCubeShadow:
1294             case EbtSamplerExternalOES:
1295             case EbtSampler2DMS:
1296             case EbtISampler2DMS:
1297             case EbtUSampler2DMS:
1298                 return "int2";
1299             case EbtSampler3D:
1300             case EbtISampler3D:
1301             case EbtUSampler3D:
1302             case EbtSampler2DArray:
1303             case EbtISampler2DArray:
1304             case EbtUSampler2DArray:
1305             case EbtSampler2DMSArray:
1306             case EbtISampler2DMSArray:
1307             case EbtUSampler2DMSArray:
1308             case EbtSampler2DArrayShadow:
1309                 return "int3";
1310             default:
1311                 UNREACHABLE();
1312         }
1313     }
1314     else  // Sampling function
1315     {
1316         switch (sampler)
1317         {
1318             case EbtSampler2D:
1319             case EbtSampler2DMS:
1320             case EbtSampler2DMSArray:
1321             case EbtSampler3D:
1322             case EbtSamplerCube:
1323             case EbtSampler2DArray:
1324             case EbtSamplerExternalOES:
1325                 return "float4";
1326             case EbtISampler2D:
1327             case EbtISampler2DMS:
1328             case EbtISampler2DMSArray:
1329             case EbtISampler3D:
1330             case EbtISamplerCube:
1331             case EbtISampler2DArray:
1332                 return "int4";
1333             case EbtUSampler2D:
1334             case EbtUSampler2DMS:
1335             case EbtUSampler2DMSArray:
1336             case EbtUSampler3D:
1337             case EbtUSamplerCube:
1338             case EbtUSampler2DArray:
1339                 return "uint4";
1340             case EbtSampler2DShadow:
1341             case EbtSamplerCubeShadow:
1342             case EbtSampler2DArrayShadow:
1343                 if (method == TextureFunctionHLSL::TextureFunction::GATHER)
1344                 {
1345                     return "float4";
1346                 }
1347                 else
1348                 {
1349                     return "float";
1350                 }
1351             default:
1352                 UNREACHABLE();
1353         }
1354     }
1355     return "";
1356 }
1357 
operator <(const TextureFunction & rhs) const1358 bool TextureFunctionHLSL::TextureFunction::operator<(const TextureFunction &rhs) const
1359 {
1360     return std::tie(sampler, coords, proj, offset, method) <
1361            std::tie(rhs.sampler, rhs.coords, rhs.proj, rhs.offset, rhs.method);
1362 }
1363 
useTextureFunction(const ImmutableString & name,TBasicType samplerType,int coords,size_t argumentCount,bool lod0,sh::GLenum shaderType)1364 ImmutableString TextureFunctionHLSL::useTextureFunction(const ImmutableString &name,
1365                                                         TBasicType samplerType,
1366                                                         int coords,
1367                                                         size_t argumentCount,
1368                                                         bool lod0,
1369                                                         sh::GLenum shaderType)
1370 {
1371     TextureFunction textureFunction;
1372     textureFunction.sampler = samplerType;
1373     textureFunction.coords  = coords;
1374     textureFunction.method  = TextureFunction::IMPLICIT;
1375     textureFunction.proj    = false;
1376     textureFunction.offset  = false;
1377 
1378     if (name == "texture2D" || name == "textureCube" || name == "texture")
1379     {
1380         textureFunction.method = TextureFunction::IMPLICIT;
1381     }
1382     else if (name == "texture2DProj" || name == "textureProj")
1383     {
1384         textureFunction.method = TextureFunction::IMPLICIT;
1385         textureFunction.proj   = true;
1386     }
1387     else if (name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod" ||
1388              name == "texture2DLodEXT" || name == "textureCubeLodEXT")
1389     {
1390         textureFunction.method = TextureFunction::LOD;
1391     }
1392     else if (name == "texture2DProjLod" || name == "textureProjLod" ||
1393              name == "texture2DProjLodEXT")
1394     {
1395         textureFunction.method = TextureFunction::LOD;
1396         textureFunction.proj   = true;
1397     }
1398     else if (name == "textureSize")
1399     {
1400         textureFunction.method = TextureFunction::SIZE;
1401     }
1402     else if (name == "textureOffset")
1403     {
1404         textureFunction.method = TextureFunction::IMPLICIT;
1405         textureFunction.offset = true;
1406     }
1407     else if (name == "textureProjOffset")
1408     {
1409         textureFunction.method = TextureFunction::IMPLICIT;
1410         textureFunction.offset = true;
1411         textureFunction.proj   = true;
1412     }
1413     else if (name == "textureLodOffset")
1414     {
1415         textureFunction.method = TextureFunction::LOD;
1416         textureFunction.offset = true;
1417     }
1418     else if (name == "textureProjLodOffset")
1419     {
1420         textureFunction.method = TextureFunction::LOD;
1421         textureFunction.proj   = true;
1422         textureFunction.offset = true;
1423     }
1424     else if (name == "texelFetch")
1425     {
1426         textureFunction.method = TextureFunction::FETCH;
1427     }
1428     else if (name == "texelFetchOffset")
1429     {
1430         textureFunction.method = TextureFunction::FETCH;
1431         textureFunction.offset = true;
1432     }
1433     else if (name == "textureGrad" || name == "texture2DGradEXT")
1434     {
1435         textureFunction.method = TextureFunction::GRAD;
1436     }
1437     else if (name == "textureGradOffset")
1438     {
1439         textureFunction.method = TextureFunction::GRAD;
1440         textureFunction.offset = true;
1441     }
1442     else if (name == "textureProjGrad" || name == "texture2DProjGradEXT" ||
1443              name == "textureCubeGradEXT")
1444     {
1445         textureFunction.method = TextureFunction::GRAD;
1446         textureFunction.proj   = true;
1447     }
1448     else if (name == "textureProjGradOffset")
1449     {
1450         textureFunction.method = TextureFunction::GRAD;
1451         textureFunction.proj   = true;
1452         textureFunction.offset = true;
1453     }
1454     else if (name == "textureGather")
1455     {
1456         textureFunction.method = TextureFunction::GATHER;
1457     }
1458     else if (name == "textureGatherOffset")
1459     {
1460         textureFunction.method = TextureFunction::GATHER;
1461         textureFunction.offset = true;
1462     }
1463     else
1464         UNREACHABLE();
1465 
1466     if (textureFunction.method ==
1467         TextureFunction::IMPLICIT)  // Could require lod 0 or have a bias argument
1468     {
1469         size_t mandatoryArgumentCount = 2;  // All functions have sampler and coordinate arguments
1470 
1471         if (textureFunction.offset)
1472         {
1473             mandatoryArgumentCount++;
1474         }
1475 
1476         bool bias = (argumentCount > mandatoryArgumentCount);  // Bias argument is optional
1477 
1478         if (lod0 || shaderType == GL_VERTEX_SHADER || shaderType == GL_COMPUTE_SHADER)
1479         {
1480             if (bias)
1481             {
1482                 textureFunction.method = TextureFunction::LOD0BIAS;
1483             }
1484             else
1485             {
1486                 textureFunction.method = TextureFunction::LOD0;
1487             }
1488         }
1489         else if (bias)
1490         {
1491             textureFunction.method = TextureFunction::BIAS;
1492         }
1493     }
1494 
1495     mUsesTexture.insert(textureFunction);
1496     return textureFunction.name();
1497 }
1498 
textureFunctionHeader(TInfoSinkBase & out,const ShShaderOutput outputType,bool getDimensionsIgnoresBaseLevel)1499 void TextureFunctionHLSL::textureFunctionHeader(TInfoSinkBase &out,
1500                                                 const ShShaderOutput outputType,
1501                                                 bool getDimensionsIgnoresBaseLevel)
1502 {
1503     for (const TextureFunction &textureFunction : mUsesTexture)
1504     {
1505         // Function header
1506         out << textureFunction.getReturnType() << " " << textureFunction.name() << "(";
1507 
1508         OutputTextureFunctionArgumentList(out, textureFunction, outputType);
1509 
1510         out << ")\n"
1511                "{\n";
1512 
1513         // In some cases we use a variable to store the texture/sampler objects, but to work around
1514         // a D3D11 compiler bug related to discard inside a loop that is conditional on texture
1515         // sampling we need to call the function directly on references to the texture and sampler
1516         // arrays. The bug was found using dEQP-GLES3.functional.shaders.discard*loop_texture*
1517         // tests.
1518         ImmutableString textureReference("");
1519         ImmutableString samplerReference("");
1520         GetTextureReference(out, textureFunction, outputType, &textureReference, &samplerReference);
1521 
1522         if (textureFunction.method == TextureFunction::SIZE)
1523         {
1524             OutputTextureSizeFunctionBody(out, textureFunction, textureReference,
1525                                           getDimensionsIgnoresBaseLevel);
1526         }
1527         else
1528         {
1529             ImmutableString texCoordX("t.x");
1530             ImmutableString texCoordY("t.y");
1531             ImmutableString texCoordZ("t.z");
1532             if (textureFunction.method == TextureFunction::GATHER)
1533             {
1534                 OutputTextureGatherFunctionBody(out, textureFunction, outputType, textureReference,
1535                                                 samplerReference, texCoordX, texCoordY, texCoordZ);
1536             }
1537             else
1538             {
1539                 ProjectTextureCoordinates(textureFunction, &texCoordX, &texCoordY, &texCoordZ);
1540                 OutputIntegerTextureSampleFunctionComputations(
1541                     out, textureFunction, outputType, textureReference, &texCoordX, &texCoordY,
1542                     &texCoordZ, getDimensionsIgnoresBaseLevel);
1543                 OutputTextureSampleFunctionReturnStatement(out, textureFunction, outputType,
1544                                                            textureReference, samplerReference,
1545                                                            texCoordX, texCoordY, texCoordZ);
1546             }
1547         }
1548 
1549         out << "}\n"
1550                "\n";
1551     }
1552 }
1553 
1554 }  // namespace sh
1555