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