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