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