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