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