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