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