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