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