1 //
2 // Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // TextureFunctionHLSL: Class for writing implementations of ESSL texture functions into HLSL
7 // output. Some of the implementations are straightforward and just call the HLSL equivalent of the
8 // ESSL texture function, others do more work to emulate ESSL texture sampling or size query
9 // behavior.
10 //
11 
12 #include "compiler/translator/TextureFunctionHLSL.h"
13 
14 #include "compiler/translator/UtilsHLSL.h"
15 
16 namespace sh
17 {
18 
19 namespace
20 {
21 
OutputIntTexCoordWrap(TInfoSinkBase & out,const char * wrapMode,const char * size,const TString & texCoord,const TString & texCoordOffset,const char * texCoordOutName)22 void OutputIntTexCoordWrap(TInfoSinkBase &out,
23                            const char *wrapMode,
24                            const char *size,
25                            const TString &texCoord,
26                            const TString &texCoordOffset,
27                            const char *texCoordOutName)
28 {
29     // GLES 3.0.4 table 3.22 specifies how the wrap modes work. We don't use the formulas verbatim
30     // but rather use equivalent formulas that map better to HLSL.
31     out << "int " << texCoordOutName << ";\n";
32     out << "float " << texCoordOutName << "Offset = " << texCoord << " + float(" << texCoordOffset
33         << ") / " << size << ";\n";
34 
35     // CLAMP_TO_EDGE
36     out << "if (" << wrapMode << " == 1)\n";
37     out << "{\n";
38     out << "    " << texCoordOutName << " = clamp(int(floor(" << size << " * " << texCoordOutName
39         << "Offset)), 0, int(" << size << ") - 1);\n";
40     out << "}\n";
41 
42     // MIRRORED_REPEAT
43     out << "else if (" << wrapMode << " == 3)\n";
44     out << "{\n";
45     out << "    float coordWrapped = 1.0 - abs(frac(abs(" << texCoordOutName
46         << "Offset) * 0.5) * 2.0 - 1.0);\n";
47     out << "    " << texCoordOutName << " = int(floor(" << size << " * coordWrapped));\n";
48     out << "}\n";
49 
50     // REPEAT
51     out << "else\n";
52     out << "{\n";
53     out << "    " << texCoordOutName << " = int(floor(" << size << " * frac(" << texCoordOutName
54         << "Offset)));\n";
55     out << "}\n";
56 }
57 
OutputIntTexCoordWraps(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,TString * texCoordX,TString * texCoordY,TString * texCoordZ)58 void OutputIntTexCoordWraps(TInfoSinkBase &out,
59                             const TextureFunctionHLSL::TextureFunction &textureFunction,
60                             TString *texCoordX,
61                             TString *texCoordY,
62                             TString *texCoordZ)
63 {
64     // Convert from normalized floating-point to integer
65     out << "int wrapS = samplerMetadata[samplerIndex].wrapModes & 0x3;\n";
66     if (textureFunction.offset)
67     {
68         OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "offset.x", "tix");
69     }
70     else
71     {
72         OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "0", "tix");
73     }
74     *texCoordX = "tix";
75     out << "int wrapT = (samplerMetadata[samplerIndex].wrapModes >> 2) & 0x3;\n";
76     if (textureFunction.offset)
77     {
78         OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "offset.y", "tiy");
79     }
80     else
81     {
82         OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "0", "tiy");
83     }
84     *texCoordY = "tiy";
85 
86     if (IsSamplerArray(textureFunction.sampler))
87     {
88         *texCoordZ = "int(max(0, min(layers - 1, floor(0.5 + t.z))))";
89     }
90     else if (!IsSamplerCube(textureFunction.sampler) && !IsSampler2D(textureFunction.sampler))
91     {
92         out << "int wrapR = (samplerMetadata[samplerIndex].wrapModes >> 4) & 0x3;\n";
93         if (textureFunction.offset)
94         {
95             OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "offset.z", "tiz");
96         }
97         else
98         {
99             OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "0", "tiz");
100         }
101         *texCoordZ = "tiz";
102     }
103 }
104 
OutputHLSL4SampleFunctionPrefix(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,const TString & textureReference,const TString & samplerReference)105 void OutputHLSL4SampleFunctionPrefix(TInfoSinkBase &out,
106                                      const TextureFunctionHLSL::TextureFunction &textureFunction,
107                                      const TString &textureReference,
108                                      const TString &samplerReference)
109 {
110     out << textureReference;
111     if (IsIntegerSampler(textureFunction.sampler) ||
112         textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
113     {
114         out << ".Load(";
115         return;
116     }
117 
118     if (IsShadowSampler(textureFunction.sampler))
119     {
120         switch (textureFunction.method)
121         {
122             case TextureFunctionHLSL::TextureFunction::IMPLICIT:
123             case TextureFunctionHLSL::TextureFunction::BIAS:
124             case TextureFunctionHLSL::TextureFunction::LOD:
125                 out << ".SampleCmp(";
126                 break;
127             case TextureFunctionHLSL::TextureFunction::LOD0:
128             case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
129             case TextureFunctionHLSL::TextureFunction::GRAD:
130                 out << ".SampleCmpLevelZero(";
131                 break;
132             default:
133                 UNREACHABLE();
134         }
135     }
136     else
137     {
138         switch (textureFunction.method)
139         {
140             case TextureFunctionHLSL::TextureFunction::IMPLICIT:
141                 out << ".Sample(";
142                 break;
143             case TextureFunctionHLSL::TextureFunction::BIAS:
144                 out << ".SampleBias(";
145                 break;
146             case TextureFunctionHLSL::TextureFunction::LOD:
147             case TextureFunctionHLSL::TextureFunction::LOD0:
148             case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
149                 out << ".SampleLevel(";
150                 break;
151             case TextureFunctionHLSL::TextureFunction::GRAD:
152                 out << ".SampleGrad(";
153                 break;
154             default:
155                 UNREACHABLE();
156         }
157     }
158     out << samplerReference << ", ";
159 }
160 
GetSamplerCoordinateTypeString(const TextureFunctionHLSL::TextureFunction & textureFunction,int hlslCoords)161 const char *GetSamplerCoordinateTypeString(
162     const TextureFunctionHLSL::TextureFunction &textureFunction,
163     int hlslCoords)
164 {
165     if (IsIntegerSampler(textureFunction.sampler) ||
166         textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
167     {
168         switch (hlslCoords)
169         {
170             case 2:
171                 return "int3";
172             case 3:
173                 return "int4";
174             default:
175                 UNREACHABLE();
176         }
177     }
178     else
179     {
180         switch (hlslCoords)
181         {
182             case 2:
183                 return "float2";
184             case 3:
185                 return "float3";
186             case 4:
187                 return "float4";
188             default:
189                 UNREACHABLE();
190         }
191     }
192     return "";
193 }
194 
GetHLSLCoordCount(const TextureFunctionHLSL::TextureFunction & textureFunction,ShShaderOutput outputType)195 int GetHLSLCoordCount(const TextureFunctionHLSL::TextureFunction &textureFunction,
196                       ShShaderOutput outputType)
197 {
198     if (outputType == SH_HLSL_3_0_OUTPUT)
199     {
200         int hlslCoords = 2;
201         switch (textureFunction.sampler)
202         {
203             case EbtSampler2D:
204             case EbtSamplerExternalOES:
205                 hlslCoords = 2;
206                 break;
207             case EbtSamplerCube:
208                 hlslCoords = 3;
209                 break;
210             default:
211                 UNREACHABLE();
212         }
213 
214         switch (textureFunction.method)
215         {
216             case TextureFunctionHLSL::TextureFunction::IMPLICIT:
217                 return hlslCoords;
218             case TextureFunctionHLSL::TextureFunction::BIAS:
219             case TextureFunctionHLSL::TextureFunction::LOD:
220             case TextureFunctionHLSL::TextureFunction::LOD0:
221             case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
222                 return 4;
223             default:
224                 UNREACHABLE();
225         }
226     }
227     else
228     {
229         switch (textureFunction.sampler)
230         {
231             case EbtSampler2D:
232                 return 2;
233             case EbtSampler3D:
234                 return 3;
235             case EbtSamplerCube:
236                 return 3;
237             case EbtSampler2DArray:
238                 return 3;
239             case EbtSamplerExternalOES:
240                 return 2;
241             case EbtISampler2D:
242                 return 2;
243             case EbtISampler3D:
244                 return 3;
245             case EbtISamplerCube:
246                 return 3;
247             case EbtISampler2DArray:
248                 return 3;
249             case EbtUSampler2D:
250                 return 2;
251             case EbtUSampler3D:
252                 return 3;
253             case EbtUSamplerCube:
254                 return 3;
255             case EbtUSampler2DArray:
256                 return 3;
257             case EbtSampler2DShadow:
258                 return 2;
259             case EbtSamplerCubeShadow:
260                 return 3;
261             case EbtSampler2DArrayShadow:
262                 return 3;
263             default:
264                 UNREACHABLE();
265         }
266     }
267     return 0;
268 }
269 
OutputTextureFunctionArgumentList(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,const ShShaderOutput outputType)270 void OutputTextureFunctionArgumentList(TInfoSinkBase &out,
271                                        const TextureFunctionHLSL::TextureFunction &textureFunction,
272                                        const ShShaderOutput outputType)
273 {
274     if (outputType == SH_HLSL_3_0_OUTPUT)
275     {
276         switch (textureFunction.sampler)
277         {
278             case EbtSampler2D:
279             case EbtSamplerExternalOES:
280                 out << "sampler2D s";
281                 break;
282             case EbtSamplerCube:
283                 out << "samplerCUBE s";
284                 break;
285             default:
286                 UNREACHABLE();
287         }
288     }
289     else
290     {
291         if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
292         {
293             out << TextureString(textureFunction.sampler) << " x, "
294                 << SamplerString(textureFunction.sampler) << " s";
295         }
296         else
297         {
298             ASSERT(outputType == SH_HLSL_4_1_OUTPUT);
299             out << "const uint samplerIndex";
300         }
301     }
302 
303     if (textureFunction.method ==
304         TextureFunctionHLSL::TextureFunction::FETCH)  // Integer coordinates
305     {
306         switch (textureFunction.coords)
307         {
308             case 2:
309                 out << ", int2 t";
310                 break;
311             case 3:
312                 out << ", int3 t";
313                 break;
314             default:
315                 UNREACHABLE();
316         }
317     }
318     else  // Floating-point coordinates (except textureSize)
319     {
320         switch (textureFunction.coords)
321         {
322             case 1:
323                 out << ", int lod";
324                 break;  // textureSize()
325             case 2:
326                 out << ", float2 t";
327                 break;
328             case 3:
329                 out << ", float3 t";
330                 break;
331             case 4:
332                 out << ", float4 t";
333                 break;
334             default:
335                 UNREACHABLE();
336         }
337     }
338 
339     if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
340     {
341         switch (textureFunction.sampler)
342         {
343             case EbtSampler2D:
344             case EbtISampler2D:
345             case EbtUSampler2D:
346             case EbtSampler2DArray:
347             case EbtISampler2DArray:
348             case EbtUSampler2DArray:
349             case EbtSampler2DShadow:
350             case EbtSampler2DArrayShadow:
351             case EbtSamplerExternalOES:
352                 out << ", float2 ddx, float2 ddy";
353                 break;
354             case EbtSampler3D:
355             case EbtISampler3D:
356             case EbtUSampler3D:
357             case EbtSamplerCube:
358             case EbtISamplerCube:
359             case EbtUSamplerCube:
360             case EbtSamplerCubeShadow:
361                 out << ", float3 ddx, float3 ddy";
362                 break;
363             default:
364                 UNREACHABLE();
365         }
366     }
367 
368     switch (textureFunction.method)
369     {
370         case TextureFunctionHLSL::TextureFunction::IMPLICIT:
371             break;
372         case TextureFunctionHLSL::TextureFunction::BIAS:
373             break;  // Comes after the offset parameter
374         case TextureFunctionHLSL::TextureFunction::LOD:
375             out << ", float lod";
376             break;
377         case TextureFunctionHLSL::TextureFunction::LOD0:
378             break;
379         case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
380             break;  // Comes after the offset parameter
381         case TextureFunctionHLSL::TextureFunction::SIZE:
382             break;
383         case TextureFunctionHLSL::TextureFunction::FETCH:
384             out << ", int mip";
385             break;
386         case TextureFunctionHLSL::TextureFunction::GRAD:
387             break;
388         default:
389             UNREACHABLE();
390     }
391 
392     if (textureFunction.offset)
393     {
394         switch (textureFunction.sampler)
395         {
396             case EbtSampler3D:
397             case EbtISampler3D:
398             case EbtUSampler3D:
399                 out << ", int3 offset";
400                 break;
401             case EbtSampler2D:
402             case EbtSampler2DArray:
403             case EbtISampler2D:
404             case EbtISampler2DArray:
405             case EbtUSampler2D:
406             case EbtUSampler2DArray:
407             case EbtSampler2DShadow:
408             case EbtSampler2DArrayShadow:
409             case EbtSamplerExternalOES:
410                 out << ", int2 offset";
411                 break;
412             default:
413                 UNREACHABLE();
414         }
415     }
416 
417     if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS ||
418         textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
419     {
420         out << ", float bias";
421     }
422 }
423 
GetTextureReference(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,const ShShaderOutput outputType,TString * textureReference,TString * samplerReference)424 void GetTextureReference(TInfoSinkBase &out,
425                          const TextureFunctionHLSL::TextureFunction &textureFunction,
426                          const ShShaderOutput outputType,
427                          TString *textureReference,
428                          TString *samplerReference)
429 {
430     if (outputType == SH_HLSL_4_1_OUTPUT)
431     {
432         TString suffix = TextureGroupSuffix(textureFunction.sampler);
433         if (TextureGroup(textureFunction.sampler) == HLSL_TEXTURE_2D)
434         {
435             *textureReference = TString("textures") + suffix + "[samplerIndex]";
436             *samplerReference = TString("samplers") + suffix + "[samplerIndex]";
437         }
438         else
439         {
440             out << "    const uint textureIndex = samplerIndex - textureIndexOffset" << suffix
441                 << ";\n";
442             *textureReference = TString("textures") + suffix + "[textureIndex]";
443             out << "    const uint samplerArrayIndex = samplerIndex - samplerIndexOffset" << suffix
444                 << ";\n";
445             *samplerReference = TString("samplers") + suffix + "[samplerArrayIndex]";
446         }
447     }
448     else
449     {
450         *textureReference = "x";
451         *samplerReference = "s";
452     }
453 }
454 
OutputTextureSizeFunctionBody(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,const TString & textureReference,bool getDimensionsIgnoresBaseLevel)455 void OutputTextureSizeFunctionBody(TInfoSinkBase &out,
456                                    const TextureFunctionHLSL::TextureFunction &textureFunction,
457                                    const TString &textureReference,
458                                    bool getDimensionsIgnoresBaseLevel)
459 {
460     if (getDimensionsIgnoresBaseLevel)
461     {
462         out << "int baseLevel = samplerMetadata[samplerIndex].baseLevel;\n";
463     }
464     else
465     {
466         out << "int baseLevel = 0;\n";
467     }
468 
469     if (IsSampler3D(textureFunction.sampler) || IsSamplerArray(textureFunction.sampler) ||
470         (IsIntegerSampler(textureFunction.sampler) && IsSamplerCube(textureFunction.sampler)))
471     {
472         // "depth" stores either the number of layers in an array texture or 3D depth
473         out << "    uint width; uint height; uint depth; uint numberOfLevels;\n"
474             << "    " << textureReference
475             << ".GetDimensions(baseLevel, width, height, depth, numberOfLevels);\n"
476             << "    width = max(width >> lod, 1);\n"
477             << "    height = max(height >> lod, 1);\n";
478 
479         if (!IsSamplerArray(textureFunction.sampler))
480         {
481             out << "    depth = max(depth >> lod, 1);\n";
482         }
483     }
484     else if (IsSampler2D(textureFunction.sampler) || IsSamplerCube(textureFunction.sampler))
485     {
486         out << "    uint width; uint height; uint numberOfLevels;\n"
487             << "    " << textureReference
488             << ".GetDimensions(baseLevel, width, height, numberOfLevels);\n"
489             << "    width = max(width >> lod, 1);\n"
490             << "    height = max(height >> lod, 1);\n";
491     }
492     else
493         UNREACHABLE();
494 
495     if (strcmp(textureFunction.getReturnType(), "int3") == 0)
496     {
497         out << "    return int3(width, height, depth);";
498     }
499     else
500     {
501         out << "    return int2(width, height);";
502     }
503 }
504 
ProjectTextureCoordinates(const TextureFunctionHLSL::TextureFunction & textureFunction,TString * texCoordX,TString * texCoordY,TString * texCoordZ)505 void ProjectTextureCoordinates(const TextureFunctionHLSL::TextureFunction &textureFunction,
506                                TString *texCoordX,
507                                TString *texCoordY,
508                                TString *texCoordZ)
509 {
510     if (textureFunction.proj)
511     {
512         TString proj("");
513         switch (textureFunction.coords)
514         {
515             case 3:
516                 proj = " / t.z";
517                 break;
518             case 4:
519                 proj = " / t.w";
520                 break;
521             default:
522                 UNREACHABLE();
523         }
524         *texCoordX = "(" + *texCoordX + proj + ")";
525         *texCoordY = "(" + *texCoordY + proj + ")";
526         *texCoordZ = "(" + *texCoordZ + proj + ")";
527     }
528 }
529 
OutputIntegerTextureSampleFunctionComputations(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,const ShShaderOutput outputType,const TString & textureReference,TString * texCoordX,TString * texCoordY,TString * texCoordZ)530 void OutputIntegerTextureSampleFunctionComputations(
531     TInfoSinkBase &out,
532     const TextureFunctionHLSL::TextureFunction &textureFunction,
533     const ShShaderOutput outputType,
534     const TString &textureReference,
535     TString *texCoordX,
536     TString *texCoordY,
537     TString *texCoordZ)
538 {
539     if (!IsIntegerSampler(textureFunction.sampler))
540     {
541         return;
542     }
543     if (IsSamplerCube(textureFunction.sampler))
544     {
545         out << "    float width; float height; float layers; float levels;\n";
546 
547         out << "    uint mip = 0;\n";
548 
549         out << "    " << textureReference
550             << ".GetDimensions(mip, width, height, layers, levels);\n";
551 
552         out << "    bool xMajor = abs(t.x) > abs(t.y) && abs(t.x) > abs(t.z);\n";
553         out << "    bool yMajor = abs(t.y) > abs(t.z) && abs(t.y) > abs(t.x);\n";
554         out << "    bool zMajor = abs(t.z) > abs(t.x) && abs(t.z) > abs(t.y);\n";
555         out << "    bool negative = (xMajor && t.x < 0.0f) || (yMajor && t.y < 0.0f) || "
556                "(zMajor && t.z < 0.0f);\n";
557 
558         // FACE_POSITIVE_X = 000b
559         // FACE_NEGATIVE_X = 001b
560         // FACE_POSITIVE_Y = 010b
561         // FACE_NEGATIVE_Y = 011b
562         // FACE_POSITIVE_Z = 100b
563         // FACE_NEGATIVE_Z = 101b
564         out << "    int face = (int)negative + (int)yMajor * 2 + (int)zMajor * 4;\n";
565 
566         out << "    float u = xMajor ? -t.z : (yMajor && t.y < 0.0f ? -t.x : t.x);\n";
567         out << "    float v = yMajor ? t.z : (negative ? t.y : -t.y);\n";
568         out << "    float m = xMajor ? t.x : (yMajor ? t.y : t.z);\n";
569 
570         out << "    t.x = (u * 0.5f / m) + 0.5f;\n";
571         out << "    t.y = (v * 0.5f / m) + 0.5f;\n";
572 
573         // Mip level computation.
574         if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
575             textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD ||
576             textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
577         {
578             if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT)
579             {
580                 out << "    float2 tSized = float2(t.x * width, t.y * height);\n"
581                        "    float2 dx = ddx(tSized);\n"
582                        "    float2 dy = ddy(tSized);\n"
583                        "    float lod = 0.5f * log2(max(dot(dx, dx), dot(dy, dy)));\n";
584             }
585             else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
586             {
587                 // ESSL 3.00.6 spec section 8.8: "For the cube version, the partial
588                 // derivatives of P are assumed to be in the coordinate system used before
589                 // texture coordinates are projected onto the appropriate cube face."
590                 // ddx[0] and ddy[0] are the derivatives of t.x passed into the function
591                 // ddx[1] and ddy[1] are the derivatives of t.y passed into the function
592                 // ddx[2] and ddy[2] are the derivatives of t.z passed into the function
593                 // Determine the derivatives of u, v and m
594                 out << "    float dudx = xMajor ? ddx[2] : (yMajor && t.y < 0.0f ? -ddx[0] "
595                        ": ddx[0]);\n"
596                        "    float dudy = xMajor ? ddy[2] : (yMajor && t.y < 0.0f ? -ddy[0] "
597                        ": ddy[0]);\n"
598                        "    float dvdx = yMajor ? ddx[2] : (negative ? ddx[1] : -ddx[1]);\n"
599                        "    float dvdy = yMajor ? ddy[2] : (negative ? ddy[1] : -ddy[1]);\n"
600                        "    float dmdx = xMajor ? ddx[0] : (yMajor ? ddx[1] : ddx[2]);\n"
601                        "    float dmdy = xMajor ? ddy[0] : (yMajor ? ddy[1] : ddy[2]);\n";
602                 // Now determine the derivatives of the face coordinates, using the
603                 // derivatives calculated above.
604                 // d / dx (u(x) * 0.5 / m(x) + 0.5)
605                 // = 0.5 * (m(x) * u'(x) - u(x) * m'(x)) / m(x)^2
606                 out << "    float dfacexdx = 0.5f * (m * dudx - u * dmdx) / (m * m);\n"
607                        "    float dfaceydx = 0.5f * (m * dvdx - v * dmdx) / (m * m);\n"
608                        "    float dfacexdy = 0.5f * (m * dudy - u * dmdy) / (m * m);\n"
609                        "    float dfaceydy = 0.5f * (m * dvdy - v * dmdy) / (m * m);\n"
610                        "    float2 sizeVec = float2(width, height);\n"
611                        "    float2 faceddx = float2(dfacexdx, dfaceydx) * sizeVec;\n"
612                        "    float2 faceddy = float2(dfacexdy, dfaceydy) * sizeVec;\n";
613                 // Optimization: instead of: log2(max(length(faceddx), length(faceddy)))
614                 // we compute: log2(max(length(faceddx)^2, length(faceddy)^2)) / 2
615                 out << "    float lengthfaceddx2 = dot(faceddx, faceddx);\n"
616                        "    float lengthfaceddy2 = dot(faceddy, faceddy);\n"
617                        "    float lod = log2(max(lengthfaceddx2, lengthfaceddy2)) * 0.5f;\n";
618             }
619             out << "    mip = uint(min(max(round(lod), 0), levels - 1));\n"
620                 << "    " << textureReference
621                 << ".GetDimensions(mip, width, height, layers, levels);\n";
622         }
623 
624         // Convert from normalized floating-point to integer
625         *texCoordX = "int(floor(width * frac(" + *texCoordX + ")))";
626         *texCoordY = "int(floor(height * frac(" + *texCoordY + ")))";
627         *texCoordZ = "face";
628     }
629     else if (textureFunction.method != TextureFunctionHLSL::TextureFunction::FETCH)
630     {
631         if (IsSampler2D(textureFunction.sampler))
632         {
633             if (IsSamplerArray(textureFunction.sampler))
634             {
635                 out << "    float width; float height; float layers; float levels;\n";
636 
637                 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
638                 {
639                     out << "    uint mip = 0;\n";
640                 }
641                 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
642                 {
643                     out << "    uint mip = bias;\n";
644                 }
645                 else
646                 {
647 
648                     out << "    " << textureReference
649                         << ".GetDimensions(0, width, height, layers, levels);\n";
650                     if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
651                         textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
652                     {
653                         out << "    float2 tSized = float2(t.x * width, t.y * height);\n"
654                                "    float dx = length(ddx(tSized));\n"
655                                "    float dy = length(ddy(tSized));\n"
656                                "    float lod = log2(max(dx, dy));\n";
657 
658                         if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
659                         {
660                             out << "    lod += bias;\n";
661                         }
662                     }
663                     else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
664                     {
665                         out << "    float2 sizeVec = float2(width, height);\n"
666                                "    float2 sizeDdx = ddx * sizeVec;\n"
667                                "    float2 sizeDdy = ddy * sizeVec;\n"
668                                "    float lod = log2(max(dot(sizeDdx, sizeDdx), "
669                                "dot(sizeDdy, sizeDdy))) * 0.5f;\n";
670                     }
671 
672                     out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
673                 }
674 
675                 out << "    " << textureReference
676                     << ".GetDimensions(mip, width, height, layers, levels);\n";
677             }
678             else
679             {
680                 out << "    float width; float height; float levels;\n";
681 
682                 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
683                 {
684                     out << "    uint mip = 0;\n";
685                 }
686                 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
687                 {
688                     out << "    uint mip = bias;\n";
689                 }
690                 else
691                 {
692                     out << "    " << textureReference
693                         << ".GetDimensions(0, width, height, levels);\n";
694 
695                     if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
696                         textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
697                     {
698                         out << "    float2 tSized = float2(t.x * width, t.y * height);\n"
699                                "    float dx = length(ddx(tSized));\n"
700                                "    float dy = length(ddy(tSized));\n"
701                                "    float lod = log2(max(dx, dy));\n";
702 
703                         if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
704                         {
705                             out << "    lod += bias;\n";
706                         }
707                     }
708                     else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
709                     {
710                         out << "    float2 sizeVec = float2(width, height);\n"
711                                "    float2 sizeDdx = ddx * sizeVec;\n"
712                                "    float2 sizeDdy = ddy * sizeVec;\n"
713                                "    float lod = log2(max(dot(sizeDdx, sizeDdx), "
714                                "dot(sizeDdy, sizeDdy))) * 0.5f;\n";
715                     }
716 
717                     out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
718                 }
719 
720                 out << "    " << textureReference
721                     << ".GetDimensions(mip, width, height, levels);\n";
722             }
723         }
724         else if (IsSampler3D(textureFunction.sampler))
725         {
726             out << "    float width; float height; float depth; float levels;\n";
727 
728             if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
729             {
730                 out << "    uint mip = 0;\n";
731             }
732             else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
733             {
734                 out << "    uint mip = bias;\n";
735             }
736             else
737             {
738                 out << "    " << textureReference
739                     << ".GetDimensions(0, width, height, depth, levels);\n";
740 
741                 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
742                     textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
743                 {
744                     out << "    float3 tSized = float3(t.x * width, t.y * height, t.z * depth);\n"
745                            "    float dx = length(ddx(tSized));\n"
746                            "    float dy = length(ddy(tSized));\n"
747                            "    float lod = log2(max(dx, dy));\n";
748 
749                     if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
750                     {
751                         out << "    lod += bias;\n";
752                     }
753                 }
754                 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
755                 {
756                     out << "    float3 sizeVec = float3(width, height, depth);\n"
757                            "    float3 sizeDdx = ddx * sizeVec;\n"
758                            "    float3 sizeDdy = ddy * sizeVec;\n"
759                            "    float lod = log2(max(dot(sizeDdx, sizeDdx), dot(sizeDdy, "
760                            "sizeDdy))) * 0.5f;\n";
761                 }
762 
763                 out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
764             }
765 
766             out << "    " << textureReference
767                 << ".GetDimensions(mip, width, height, depth, levels);\n";
768         }
769         else
770             UNREACHABLE();
771 
772         OutputIntTexCoordWraps(out, textureFunction, texCoordX, texCoordY, texCoordZ);
773     }
774 }
775 
OutputTextureSampleFunctionReturnStatement(TInfoSinkBase & out,const TextureFunctionHLSL::TextureFunction & textureFunction,const ShShaderOutput outputType,const TString & textureReference,const TString & samplerReference,const TString & texCoordX,const TString & texCoordY,const TString & texCoordZ)776 void OutputTextureSampleFunctionReturnStatement(
777     TInfoSinkBase &out,
778     const TextureFunctionHLSL::TextureFunction &textureFunction,
779     const ShShaderOutput outputType,
780     const TString &textureReference,
781     const TString &samplerReference,
782     const TString &texCoordX,
783     const TString &texCoordY,
784     const TString &texCoordZ)
785 {
786     out << "    return ";
787 
788     // HLSL intrinsic
789     if (outputType == SH_HLSL_3_0_OUTPUT)
790     {
791         switch (textureFunction.sampler)
792         {
793             case EbtSampler2D:
794             case EbtSamplerExternalOES:
795                 out << "tex2D";
796                 break;
797             case EbtSamplerCube:
798                 out << "texCUBE";
799                 break;
800             default:
801                 UNREACHABLE();
802         }
803 
804         switch (textureFunction.method)
805         {
806             case TextureFunctionHLSL::TextureFunction::IMPLICIT:
807                 out << "(" << samplerReference << ", ";
808                 break;
809             case TextureFunctionHLSL::TextureFunction::BIAS:
810                 out << "bias(" << samplerReference << ", ";
811                 break;
812             case TextureFunctionHLSL::TextureFunction::LOD:
813                 out << "lod(" << samplerReference << ", ";
814                 break;
815             case TextureFunctionHLSL::TextureFunction::LOD0:
816                 out << "lod(" << samplerReference << ", ";
817                 break;
818             case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
819                 out << "lod(" << samplerReference << ", ";
820                 break;
821             default:
822                 UNREACHABLE();
823         }
824     }
825     else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
826     {
827         OutputHLSL4SampleFunctionPrefix(out, textureFunction, textureReference, samplerReference);
828     }
829     else
830         UNREACHABLE();
831 
832     const int hlslCoords = GetHLSLCoordCount(textureFunction, outputType);
833 
834     out << GetSamplerCoordinateTypeString(textureFunction, hlslCoords) << "(" << texCoordX << ", "
835         << texCoordY;
836 
837     if (outputType == SH_HLSL_3_0_OUTPUT)
838     {
839         if (hlslCoords >= 3)
840         {
841             if (textureFunction.coords < 3)
842             {
843                 out << ", 0";
844             }
845             else
846             {
847                 out << ", " << texCoordZ;
848             }
849         }
850 
851         if (hlslCoords == 4)
852         {
853             switch (textureFunction.method)
854             {
855                 case TextureFunctionHLSL::TextureFunction::BIAS:
856                     out << ", bias";
857                     break;
858                 case TextureFunctionHLSL::TextureFunction::LOD:
859                     out << ", lod";
860                     break;
861                 case TextureFunctionHLSL::TextureFunction::LOD0:
862                     out << ", 0";
863                     break;
864                 case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
865                     out << ", bias";
866                     break;
867                 default:
868                     UNREACHABLE();
869             }
870         }
871 
872         out << ")";
873     }
874     else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
875     {
876         if (hlslCoords >= 3)
877         {
878             ASSERT(!IsIntegerSampler(textureFunction.sampler) ||
879                    !IsSamplerCube(textureFunction.sampler) || texCoordZ == "face");
880             out << ", " << texCoordZ;
881         }
882 
883         if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
884         {
885             if (IsIntegerSampler(textureFunction.sampler))
886             {
887                 out << ", mip)";
888             }
889             else if (IsShadowSampler(textureFunction.sampler))
890             {
891                 // Compare value
892                 if (textureFunction.proj)
893                 {
894                     // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
895                     // The resulting third component of P' in the shadow forms is used as
896                     // Dref
897                     out << "), " << texCoordZ;
898                 }
899                 else
900                 {
901                     switch (textureFunction.coords)
902                     {
903                         case 3:
904                             out << "), t.z";
905                             break;
906                         case 4:
907                             out << "), t.w";
908                             break;
909                         default:
910                             UNREACHABLE();
911                     }
912                 }
913             }
914             else
915             {
916                 out << "), ddx, ddy";
917             }
918         }
919         else if (IsIntegerSampler(textureFunction.sampler) ||
920                  textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
921         {
922             out << ", mip)";
923         }
924         else if (IsShadowSampler(textureFunction.sampler))
925         {
926             // Compare value
927             if (textureFunction.proj)
928             {
929                 // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
930                 // The resulting third component of P' in the shadow forms is used as Dref
931                 out << "), " << texCoordZ;
932             }
933             else
934             {
935                 switch (textureFunction.coords)
936                 {
937                     case 3:
938                         out << "), t.z";
939                         break;
940                     case 4:
941                         out << "), t.w";
942                         break;
943                     default:
944                         UNREACHABLE();
945                 }
946             }
947         }
948         else
949         {
950             switch (textureFunction.method)
951             {
952                 case TextureFunctionHLSL::TextureFunction::IMPLICIT:
953                     out << ")";
954                     break;
955                 case TextureFunctionHLSL::TextureFunction::BIAS:
956                     out << "), bias";
957                     break;
958                 case TextureFunctionHLSL::TextureFunction::LOD:
959                     out << "), lod";
960                     break;
961                 case TextureFunctionHLSL::TextureFunction::LOD0:
962                     out << "), 0";
963                     break;
964                 case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
965                     out << "), bias";
966                     break;
967                 default:
968                     UNREACHABLE();
969             }
970         }
971 
972         if (textureFunction.offset &&
973             (!IsIntegerSampler(textureFunction.sampler) ||
974              textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH))
975         {
976             out << ", offset";
977         }
978     }
979     else
980         UNREACHABLE();
981 
982     out << ");\n";  // Close the sample function call and return statement
983 }
984 
985 }  // Anonymous namespace
986 
name() const987 TString TextureFunctionHLSL::TextureFunction::name() const
988 {
989     TString name = "gl_texture";
990 
991     // We need to include full the sampler type in the function name to make the signature unique
992     // on D3D11, where samplers are passed to texture functions as indices.
993     name += TextureTypeSuffix(this->sampler);
994 
995     if (proj)
996     {
997         name += "Proj";
998     }
999 
1000     if (offset)
1001     {
1002         name += "Offset";
1003     }
1004 
1005     switch (method)
1006     {
1007         case IMPLICIT:
1008             break;
1009         case BIAS:
1010             break;  // Extra parameter makes the signature unique
1011         case LOD:
1012             name += "Lod";
1013             break;
1014         case LOD0:
1015             name += "Lod0";
1016             break;
1017         case LOD0BIAS:
1018             name += "Lod0";
1019             break;  // Extra parameter makes the signature unique
1020         case SIZE:
1021             name += "Size";
1022             break;
1023         case FETCH:
1024             name += "Fetch";
1025             break;
1026         case GRAD:
1027             name += "Grad";
1028             break;
1029         default:
1030             UNREACHABLE();
1031     }
1032 
1033     return name;
1034 }
1035 
getReturnType() const1036 const char *TextureFunctionHLSL::TextureFunction::getReturnType() const
1037 {
1038     if (method == TextureFunction::SIZE)
1039     {
1040         switch (sampler)
1041         {
1042             case EbtSampler2D:
1043             case EbtISampler2D:
1044             case EbtUSampler2D:
1045             case EbtSampler2DShadow:
1046             case EbtSamplerCube:
1047             case EbtISamplerCube:
1048             case EbtUSamplerCube:
1049             case EbtSamplerCubeShadow:
1050             case EbtSamplerExternalOES:
1051                 return "int2";
1052             case EbtSampler3D:
1053             case EbtISampler3D:
1054             case EbtUSampler3D:
1055             case EbtSampler2DArray:
1056             case EbtISampler2DArray:
1057             case EbtUSampler2DArray:
1058             case EbtSampler2DArrayShadow:
1059                 return "int3";
1060             default:
1061                 UNREACHABLE();
1062         }
1063     }
1064     else  // Sampling function
1065     {
1066         switch (sampler)
1067         {
1068             case EbtSampler2D:
1069             case EbtSampler3D:
1070             case EbtSamplerCube:
1071             case EbtSampler2DArray:
1072             case EbtSamplerExternalOES:
1073                 return "float4";
1074             case EbtISampler2D:
1075             case EbtISampler3D:
1076             case EbtISamplerCube:
1077             case EbtISampler2DArray:
1078                 return "int4";
1079             case EbtUSampler2D:
1080             case EbtUSampler3D:
1081             case EbtUSamplerCube:
1082             case EbtUSampler2DArray:
1083                 return "uint4";
1084             case EbtSampler2DShadow:
1085             case EbtSamplerCubeShadow:
1086             case EbtSampler2DArrayShadow:
1087                 return "float";
1088             default:
1089                 UNREACHABLE();
1090         }
1091     }
1092     return "";
1093 }
1094 
operator <(const TextureFunction & rhs) const1095 bool TextureFunctionHLSL::TextureFunction::operator<(const TextureFunction &rhs) const
1096 {
1097     return std::tie(sampler, coords, proj, offset, method) <
1098            std::tie(rhs.sampler, rhs.coords, rhs.proj, rhs.offset, rhs.method);
1099 }
1100 
useTextureFunction(const TString & name,TBasicType samplerType,int coords,size_t argumentCount,bool lod0,sh::GLenum shaderType)1101 TString TextureFunctionHLSL::useTextureFunction(const TString &name,
1102                                                 TBasicType samplerType,
1103                                                 int coords,
1104                                                 size_t argumentCount,
1105                                                 bool lod0,
1106                                                 sh::GLenum shaderType)
1107 {
1108     TextureFunction textureFunction;
1109     textureFunction.sampler = samplerType;
1110     textureFunction.coords  = coords;
1111     textureFunction.method  = TextureFunction::IMPLICIT;
1112     textureFunction.proj    = false;
1113     textureFunction.offset  = false;
1114 
1115     if (name == "texture2D" || name == "textureCube" || name == "texture")
1116     {
1117         textureFunction.method = TextureFunction::IMPLICIT;
1118     }
1119     else if (name == "texture2DProj" || name == "textureProj")
1120     {
1121         textureFunction.method = TextureFunction::IMPLICIT;
1122         textureFunction.proj   = true;
1123     }
1124     else if (name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod" ||
1125              name == "texture2DLodEXT" || name == "textureCubeLodEXT")
1126     {
1127         textureFunction.method = TextureFunction::LOD;
1128     }
1129     else if (name == "texture2DProjLod" || name == "textureProjLod" ||
1130              name == "texture2DProjLodEXT")
1131     {
1132         textureFunction.method = TextureFunction::LOD;
1133         textureFunction.proj   = true;
1134     }
1135     else if (name == "textureSize")
1136     {
1137         textureFunction.method = TextureFunction::SIZE;
1138     }
1139     else if (name == "textureOffset")
1140     {
1141         textureFunction.method = TextureFunction::IMPLICIT;
1142         textureFunction.offset = true;
1143     }
1144     else if (name == "textureProjOffset")
1145     {
1146         textureFunction.method = TextureFunction::IMPLICIT;
1147         textureFunction.offset = true;
1148         textureFunction.proj   = true;
1149     }
1150     else if (name == "textureLodOffset")
1151     {
1152         textureFunction.method = TextureFunction::LOD;
1153         textureFunction.offset = true;
1154     }
1155     else if (name == "textureProjLodOffset")
1156     {
1157         textureFunction.method = TextureFunction::LOD;
1158         textureFunction.proj   = true;
1159         textureFunction.offset = true;
1160     }
1161     else if (name == "texelFetch")
1162     {
1163         textureFunction.method = TextureFunction::FETCH;
1164     }
1165     else if (name == "texelFetchOffset")
1166     {
1167         textureFunction.method = TextureFunction::FETCH;
1168         textureFunction.offset = true;
1169     }
1170     else if (name == "textureGrad" || name == "texture2DGradEXT")
1171     {
1172         textureFunction.method = TextureFunction::GRAD;
1173     }
1174     else if (name == "textureGradOffset")
1175     {
1176         textureFunction.method = TextureFunction::GRAD;
1177         textureFunction.offset = true;
1178     }
1179     else if (name == "textureProjGrad" || name == "texture2DProjGradEXT" ||
1180              name == "textureCubeGradEXT")
1181     {
1182         textureFunction.method = TextureFunction::GRAD;
1183         textureFunction.proj   = true;
1184     }
1185     else if (name == "textureProjGradOffset")
1186     {
1187         textureFunction.method = TextureFunction::GRAD;
1188         textureFunction.proj   = true;
1189         textureFunction.offset = true;
1190     }
1191     else
1192         UNREACHABLE();
1193 
1194     if (textureFunction.method ==
1195         TextureFunction::IMPLICIT)  // Could require lod 0 or have a bias argument
1196     {
1197         size_t mandatoryArgumentCount = 2;  // All functions have sampler and coordinate arguments
1198 
1199         if (textureFunction.offset)
1200         {
1201             mandatoryArgumentCount++;
1202         }
1203 
1204         bool bias = (argumentCount > mandatoryArgumentCount);  // Bias argument is optional
1205 
1206         if (lod0 || shaderType == GL_VERTEX_SHADER)
1207         {
1208             if (bias)
1209             {
1210                 textureFunction.method = TextureFunction::LOD0BIAS;
1211             }
1212             else
1213             {
1214                 textureFunction.method = TextureFunction::LOD0;
1215             }
1216         }
1217         else if (bias)
1218         {
1219             textureFunction.method = TextureFunction::BIAS;
1220         }
1221     }
1222 
1223     mUsesTexture.insert(textureFunction);
1224     return textureFunction.name();
1225 }
1226 
textureFunctionHeader(TInfoSinkBase & out,const ShShaderOutput outputType,bool getDimensionsIgnoresBaseLevel)1227 void TextureFunctionHLSL::textureFunctionHeader(TInfoSinkBase &out,
1228                                                 const ShShaderOutput outputType,
1229                                                 bool getDimensionsIgnoresBaseLevel)
1230 {
1231     for (const TextureFunction &textureFunction : mUsesTexture)
1232     {
1233         // Function header
1234         out << textureFunction.getReturnType() << " " << textureFunction.name() << "(";
1235 
1236         OutputTextureFunctionArgumentList(out, textureFunction, outputType);
1237 
1238         out << ")\n"
1239                "{\n";
1240 
1241         // In some cases we use a variable to store the texture/sampler objects, but to work around
1242         // a D3D11 compiler bug related to discard inside a loop that is conditional on texture
1243         // sampling we need to call the function directly on references to the texture and sampler
1244         // arrays. The bug was found using dEQP-GLES3.functional.shaders.discard*loop_texture*
1245         // tests.
1246         TString textureReference;
1247         TString samplerReference;
1248         GetTextureReference(out, textureFunction, outputType, &textureReference, &samplerReference);
1249 
1250         if (textureFunction.method == TextureFunction::SIZE)
1251         {
1252             OutputTextureSizeFunctionBody(out, textureFunction, textureReference,
1253                                           getDimensionsIgnoresBaseLevel);
1254         }
1255         else
1256         {
1257             TString texCoordX("t.x");
1258             TString texCoordY("t.y");
1259             TString texCoordZ("t.z");
1260             ProjectTextureCoordinates(textureFunction, &texCoordX, &texCoordY, &texCoordZ);
1261             OutputIntegerTextureSampleFunctionComputations(out, textureFunction, outputType,
1262                                                            textureReference, &texCoordX, &texCoordY,
1263                                                            &texCoordZ);
1264             OutputTextureSampleFunctionReturnStatement(out, textureFunction, outputType,
1265                                                        textureReference, samplerReference,
1266                                                        texCoordX, texCoordY, texCoordZ);
1267         }
1268 
1269         out << "}\n"
1270                "\n";
1271     }
1272 }
1273 
1274 }  // namespace sh
1275