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