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