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