TextureFunctionHLSL.cpp (61161B)
1 // 2 // Copyright 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/ImmutableStringBuilder.h" 15 #include "compiler/translator/UtilsHLSL.h" 16 17 namespace sh 18 { 19 20 namespace 21 { 22 23 void OutputIntTexCoordWrap(TInfoSinkBase &out, 24 const char *wrapMode, 25 const char *size, 26 const ImmutableString &texCoord, 27 const char *texCoordOffset, 28 const char *texCoordOutName) 29 { 30 // GLES 3.0.4 table 3.22 specifies how the wrap modes work. We don't use the formulas verbatim 31 // but rather use equivalent formulas that map better to HLSL. 32 out << "int " << texCoordOutName << ";\n"; 33 out << "float " << texCoordOutName << "Offset = " << texCoord << " + float(" << texCoordOffset 34 << ") / " << size << ";\n"; 35 out << "bool " << texCoordOutName << "UseBorderColor = false;\n"; 36 37 // CLAMP_TO_EDGE 38 out << "if (" << wrapMode << " == 0)\n"; 39 out << "{\n"; 40 out << " " << texCoordOutName << " = clamp(int(floor(" << size << " * " << texCoordOutName 41 << "Offset)), 0, int(" << size << ") - 1);\n"; 42 out << "}\n"; 43 44 // CLAMP_TO_BORDER 45 out << "else if (" << wrapMode << " == 3)\n"; 46 out << "{\n"; 47 out << " int texCoordInt = int(floor(" << size << " * " << texCoordOutName << "Offset));\n"; 48 out << " " << texCoordOutName << " = clamp(texCoordInt, 0, int(" << size << ") - 1);\n"; 49 out << " " << texCoordOutName << "UseBorderColor = (texCoordInt != " << texCoordOutName 50 << ");\n"; 51 out << "}\n"; 52 53 // MIRRORED_REPEAT 54 out << "else if (" << wrapMode << " == 2)\n"; 55 out << "{\n"; 56 out << " float coordWrapped = 1.0 - abs(frac(abs(" << texCoordOutName 57 << "Offset) * 0.5) * 2.0 - 1.0);\n"; 58 out << " " << texCoordOutName << " = int(floor(" << size << " * coordWrapped));\n"; 59 out << "}\n"; 60 61 // REPEAT 62 out << "else\n"; 63 out << "{\n"; 64 out << " " << texCoordOutName << " = int(floor(" << size << " * frac(" << texCoordOutName 65 << "Offset)));\n"; 66 out << "}\n"; 67 } 68 69 void OutputIntTexCoordWraps(TInfoSinkBase &out, 70 const TextureFunctionHLSL::TextureFunction &textureFunction, 71 ImmutableString *texCoordX, 72 ImmutableString *texCoordY, 73 ImmutableString *texCoordZ) 74 { 75 // Convert from normalized floating-point to integer 76 out << "int wrapS = samplerMetadata[samplerIndex].wrapModes & 0x3;\n"; 77 if (textureFunction.offset) 78 { 79 OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "offset.x", "tix"); 80 } 81 else 82 { 83 OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "0", "tix"); 84 } 85 *texCoordX = ImmutableString("tix"); 86 out << "int wrapT = (samplerMetadata[samplerIndex].wrapModes >> 2) & 0x3;\n"; 87 if (textureFunction.offset) 88 { 89 OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "offset.y", "tiy"); 90 } 91 else 92 { 93 OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "0", "tiy"); 94 } 95 *texCoordY = ImmutableString("tiy"); 96 97 bool tizAvailable = false; 98 99 if (IsSamplerArray(textureFunction.sampler)) 100 { 101 *texCoordZ = ImmutableString("int(max(0, min(layers - 1, floor(0.5 + t.z))))"); 102 } 103 else if (!IsSamplerCube(textureFunction.sampler) && !IsSampler2D(textureFunction.sampler)) 104 { 105 out << "int wrapR = (samplerMetadata[samplerIndex].wrapModes >> 4) & 0x3;\n"; 106 if (textureFunction.offset) 107 { 108 OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "offset.z", "tiz"); 109 } 110 else 111 { 112 OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "0", "tiz"); 113 } 114 *texCoordZ = ImmutableString("tiz"); 115 tizAvailable = true; 116 } 117 118 out << "bool useBorderColor = tixUseBorderColor || tiyUseBorderColor" 119 << (tizAvailable ? " || tizUseBorderColor" : "") << ";\n"; 120 } 121 122 void OutputHLSL4SampleFunctionPrefix(TInfoSinkBase &out, 123 const TextureFunctionHLSL::TextureFunction &textureFunction, 124 const ImmutableString &textureReference, 125 const ImmutableString &samplerReference) 126 { 127 out << textureReference; 128 if (IsIntegerSampler(textureFunction.sampler) || 129 textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH) 130 { 131 out << ".Load("; 132 return; 133 } 134 135 if (IsShadowSampler(textureFunction.sampler)) 136 { 137 switch (textureFunction.method) 138 { 139 case TextureFunctionHLSL::TextureFunction::IMPLICIT: 140 case TextureFunctionHLSL::TextureFunction::BIAS: 141 case TextureFunctionHLSL::TextureFunction::LOD: 142 out << ".SampleCmp("; 143 break; 144 case TextureFunctionHLSL::TextureFunction::LOD0: 145 case TextureFunctionHLSL::TextureFunction::LOD0BIAS: 146 case TextureFunctionHLSL::TextureFunction::GRAD: 147 out << ".SampleCmpLevelZero("; 148 break; 149 default: 150 UNREACHABLE(); 151 } 152 } 153 else 154 { 155 switch (textureFunction.method) 156 { 157 case TextureFunctionHLSL::TextureFunction::IMPLICIT: 158 out << ".Sample("; 159 break; 160 case TextureFunctionHLSL::TextureFunction::BIAS: 161 out << ".SampleBias("; 162 break; 163 case TextureFunctionHLSL::TextureFunction::LOD: 164 case TextureFunctionHLSL::TextureFunction::LOD0: 165 case TextureFunctionHLSL::TextureFunction::LOD0BIAS: 166 out << ".SampleLevel("; 167 break; 168 case TextureFunctionHLSL::TextureFunction::GRAD: 169 out << ".SampleGrad("; 170 break; 171 default: 172 UNREACHABLE(); 173 } 174 } 175 out << samplerReference << ", "; 176 } 177 178 const char *GetSamplerCoordinateTypeString( 179 const TextureFunctionHLSL::TextureFunction &textureFunction, 180 int hlslCoords) 181 { 182 // Gather[Red|Green|Blue|Alpha] accepts float texture coordinates on textures in integer or 183 // unsigned integer formats. 184 // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-to-gather 185 if ((IsIntegerSampler(textureFunction.sampler) && 186 textureFunction.method != TextureFunctionHLSL::TextureFunction::GATHER) || 187 textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH) 188 { 189 switch (hlslCoords) 190 { 191 case 1: 192 return "int"; 193 case 2: 194 if (IsSampler2DMS(textureFunction.sampler)) 195 { 196 return "int2"; 197 } 198 else 199 { 200 return "int3"; 201 } 202 case 3: 203 if (IsSampler2DMSArray(textureFunction.sampler)) 204 { 205 return "int3"; 206 } 207 else 208 { 209 return "int4"; 210 } 211 default: 212 UNREACHABLE(); 213 } 214 } 215 else 216 { 217 switch (hlslCoords) 218 { 219 case 1: 220 return "float"; 221 case 2: 222 return "float2"; 223 case 3: 224 return "float3"; 225 case 4: 226 return "float4"; 227 default: 228 UNREACHABLE(); 229 } 230 } 231 return ""; 232 } 233 234 int GetHLSLCoordCount(const TextureFunctionHLSL::TextureFunction &textureFunction, 235 ShShaderOutput outputType) 236 { 237 if (outputType == SH_HLSL_3_0_OUTPUT) 238 { 239 int hlslCoords = 2; 240 switch (textureFunction.sampler) 241 { 242 case EbtSamplerBuffer: 243 hlslCoords = 1; 244 break; 245 case EbtSampler2D: 246 case EbtSamplerExternalOES: 247 case EbtSampler2DMS: 248 case EbtSamplerVideoWEBGL: 249 hlslCoords = 2; 250 break; 251 case EbtSamplerCube: 252 hlslCoords = 3; 253 break; 254 default: 255 UNREACHABLE(); 256 } 257 258 switch (textureFunction.method) 259 { 260 case TextureFunctionHLSL::TextureFunction::IMPLICIT: 261 case TextureFunctionHLSL::TextureFunction::GRAD: 262 return hlslCoords; 263 case TextureFunctionHLSL::TextureFunction::BIAS: 264 case TextureFunctionHLSL::TextureFunction::LOD: 265 case TextureFunctionHLSL::TextureFunction::LOD0: 266 case TextureFunctionHLSL::TextureFunction::LOD0BIAS: 267 return 4; 268 default: 269 UNREACHABLE(); 270 } 271 } 272 else 273 { 274 if (IsSamplerBuffer(textureFunction.sampler)) 275 { 276 return 1; 277 } 278 else if (IsSampler3D(textureFunction.sampler) || IsSamplerArray(textureFunction.sampler) || 279 IsSamplerCube(textureFunction.sampler)) 280 { 281 return 3; 282 } 283 ASSERT(IsSampler2D(textureFunction.sampler)); 284 return 2; 285 } 286 return 0; 287 } 288 289 void OutputTextureFunctionArgumentList(TInfoSinkBase &out, 290 const TextureFunctionHLSL::TextureFunction &textureFunction, 291 const ShShaderOutput outputType) 292 { 293 if (outputType == SH_HLSL_3_0_OUTPUT) 294 { 295 switch (textureFunction.sampler) 296 { 297 case EbtSampler2D: 298 case EbtSamplerVideoWEBGL: 299 case EbtSamplerExternalOES: 300 out << "sampler2D s"; 301 break; 302 case EbtSamplerCube: 303 out << "samplerCUBE s"; 304 break; 305 default: 306 UNREACHABLE(); 307 } 308 } 309 else 310 { 311 if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT) 312 { 313 out << TextureString(textureFunction.sampler) << " x, " 314 << SamplerString(textureFunction.sampler) << " s"; 315 } 316 else 317 { 318 ASSERT(outputType == SH_HLSL_4_1_OUTPUT); 319 // A bug in the D3D compiler causes some nested sampling operations to fail. 320 // See http://anglebug.com/1923 321 // TODO(jmadill): Reinstate the const keyword when possible. 322 out << /*"const"*/ "uint samplerIndex"; 323 } 324 } 325 326 if (textureFunction.method == 327 TextureFunctionHLSL::TextureFunction::FETCH) // Integer coordinates 328 { 329 switch (textureFunction.coords) 330 { 331 case 1: 332 out << ", int t"; 333 break; 334 case 2: 335 out << ", int2 t"; 336 break; 337 case 3: 338 out << ", int3 t"; 339 break; 340 default: 341 UNREACHABLE(); 342 } 343 } 344 else // Floating-point coordinates (except textureSize) 345 { 346 switch (textureFunction.coords) 347 { 348 case 0: 349 break; // textureSize(gSampler2DMS sampler) 350 case 1: 351 out << ", int lod"; 352 break; // textureSize() 353 case 2: 354 out << ", float2 t"; 355 break; 356 case 3: 357 out << ", float3 t"; 358 break; 359 case 4: 360 out << ", float4 t"; 361 break; 362 default: 363 UNREACHABLE(); 364 } 365 } 366 367 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) 368 { 369 switch (textureFunction.sampler) 370 { 371 case EbtSampler2D: 372 case EbtISampler2D: 373 case EbtUSampler2D: 374 case EbtSampler2DArray: 375 case EbtISampler2DArray: 376 case EbtUSampler2DArray: 377 case EbtSampler2DShadow: 378 case EbtSampler2DArrayShadow: 379 case EbtSamplerExternalOES: 380 case EbtSamplerVideoWEBGL: 381 out << ", float2 ddx, float2 ddy"; 382 break; 383 case EbtSampler3D: 384 case EbtISampler3D: 385 case EbtUSampler3D: 386 case EbtSamplerCube: 387 case EbtISamplerCube: 388 case EbtUSamplerCube: 389 case EbtSamplerCubeShadow: 390 out << ", float3 ddx, float3 ddy"; 391 break; 392 default: 393 UNREACHABLE(); 394 } 395 } 396 397 switch (textureFunction.method) 398 { 399 case TextureFunctionHLSL::TextureFunction::IMPLICIT: 400 break; 401 case TextureFunctionHLSL::TextureFunction::BIAS: 402 break; // Comes after the offset parameter 403 case TextureFunctionHLSL::TextureFunction::LOD: 404 out << ", float lod"; 405 break; 406 case TextureFunctionHLSL::TextureFunction::LOD0: 407 break; 408 case TextureFunctionHLSL::TextureFunction::LOD0BIAS: 409 break; // Comes after the offset parameter 410 case TextureFunctionHLSL::TextureFunction::SIZE: 411 break; 412 case TextureFunctionHLSL::TextureFunction::FETCH: 413 if (IsSampler2DMS(textureFunction.sampler) || 414 IsSampler2DMSArray(textureFunction.sampler)) 415 out << ", int index"; 416 else if (!IsSamplerBuffer(textureFunction.sampler)) 417 out << ", int mip"; 418 break; 419 case TextureFunctionHLSL::TextureFunction::GRAD: 420 break; 421 case TextureFunctionHLSL::TextureFunction::GATHER: 422 break; 423 default: 424 UNREACHABLE(); 425 } 426 427 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GATHER && 428 IsShadowSampler(textureFunction.sampler)) 429 { 430 out << ", float refZ"; 431 } 432 433 if (textureFunction.offset) 434 { 435 switch (textureFunction.sampler) 436 { 437 case EbtSampler3D: 438 case EbtISampler3D: 439 case EbtUSampler3D: 440 out << ", int3 offset"; 441 break; 442 case EbtSampler2D: 443 case EbtSampler2DArray: 444 case EbtISampler2D: 445 case EbtISampler2DArray: 446 case EbtUSampler2D: 447 case EbtUSampler2DArray: 448 case EbtSampler2DShadow: 449 case EbtSampler2DArrayShadow: 450 case EbtSamplerExternalOES: 451 case EbtSamplerVideoWEBGL: 452 out << ", int2 offset"; 453 break; 454 default: 455 // Offset is not supported for multisampled textures. 456 UNREACHABLE(); 457 } 458 } 459 460 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS || 461 textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS) 462 { 463 out << ", float bias"; 464 } 465 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GATHER && 466 !IsShadowSampler(textureFunction.sampler)) 467 { 468 out << ", int comp = 0"; 469 } 470 } 471 472 void GetTextureReference(TInfoSinkBase &out, 473 const TextureFunctionHLSL::TextureFunction &textureFunction, 474 const ShShaderOutput outputType, 475 ImmutableString *textureReference, 476 ImmutableString *samplerReference) 477 { 478 if (outputType == SH_HLSL_4_1_OUTPUT) 479 { 480 static const ImmutableString kTexturesStr("textures"); 481 static const ImmutableString kSamplersStr("samplers"); 482 static const ImmutableString kSamplerIndexStr("[samplerIndex]"); 483 static const ImmutableString kTextureIndexStr("[textureIndex]"); 484 static const ImmutableString kSamplerArrayIndexStr("[samplerArrayIndex]"); 485 ImmutableString suffix(TextureGroupSuffix(textureFunction.sampler)); 486 487 if (TextureGroup(textureFunction.sampler) == HLSL_TEXTURE_2D) 488 { 489 ImmutableStringBuilder textureRefBuilder(kTexturesStr.length() + suffix.length() + 490 kSamplerIndexStr.length()); 491 textureRefBuilder << kTexturesStr << suffix << kSamplerIndexStr; 492 *textureReference = textureRefBuilder; 493 ImmutableStringBuilder samplerRefBuilder(kSamplersStr.length() + suffix.length() + 494 kSamplerIndexStr.length()); 495 samplerRefBuilder << kSamplersStr << suffix << kSamplerIndexStr; 496 *samplerReference = samplerRefBuilder; 497 } 498 else 499 { 500 out << " const uint textureIndex = samplerIndex - textureIndexOffset" 501 << suffix.data() << ";\n"; 502 ImmutableStringBuilder textureRefBuilder(kTexturesStr.length() + suffix.length() + 503 kTextureIndexStr.length()); 504 textureRefBuilder << kTexturesStr << suffix << kTextureIndexStr; 505 *textureReference = textureRefBuilder; 506 507 out << " const uint samplerArrayIndex = samplerIndex - samplerIndexOffset" 508 << suffix.data() << ";\n"; 509 ImmutableStringBuilder samplerRefBuilder(kSamplersStr.length() + suffix.length() + 510 kSamplerArrayIndexStr.length()); 511 samplerRefBuilder << kSamplersStr << suffix << kSamplerArrayIndexStr; 512 *samplerReference = samplerRefBuilder; 513 } 514 } 515 else 516 { 517 *textureReference = ImmutableString("x"); 518 *samplerReference = ImmutableString("s"); 519 } 520 } 521 522 void OutputTextureSizeFunctionBody(TInfoSinkBase &out, 523 const TextureFunctionHLSL::TextureFunction &textureFunction, 524 const ImmutableString &textureReference, 525 bool getDimensionsIgnoresBaseLevel) 526 { 527 if (IsSampler2DMS(textureFunction.sampler)) 528 { 529 out << " uint width; uint height; uint samples;\n" 530 << " " << textureReference << ".GetDimensions(width, height, samples);\n"; 531 } 532 else if (IsSampler2DMSArray(textureFunction.sampler)) 533 { 534 out << " uint width; uint height; uint depth; uint samples;\n" 535 << " " << textureReference << ".GetDimensions(width, height, depth, samples);\n"; 536 } 537 else if (IsSamplerBuffer(textureFunction.sampler)) 538 { 539 out << " uint width;\n" 540 << " " << textureReference << ".GetDimensions(width);\n"; 541 } 542 else 543 { 544 if (getDimensionsIgnoresBaseLevel) 545 { 546 out << " int baseLevel = samplerMetadata[samplerIndex].baseLevel;\n"; 547 } 548 else 549 { 550 out << " int baseLevel = 0;\n"; 551 } 552 553 if (IsSampler3D(textureFunction.sampler) || IsSamplerArray(textureFunction.sampler) || 554 (IsIntegerSampler(textureFunction.sampler) && IsSamplerCube(textureFunction.sampler))) 555 { 556 // "depth" stores either the number of layers in an array texture or 3D depth 557 out << " uint width; uint height; uint depth; uint numberOfLevels;\n" 558 << " " << textureReference 559 << ".GetDimensions(baseLevel, width, height, depth, numberOfLevels);\n" 560 << " width = max(width >> lod, 1);\n" 561 << " height = max(height >> lod, 1);\n"; 562 563 if (!IsSamplerArray(textureFunction.sampler)) 564 { 565 out << " depth = max(depth >> lod, 1);\n"; 566 } 567 } 568 else if (IsSampler2D(textureFunction.sampler) || IsSamplerCube(textureFunction.sampler)) 569 { 570 out << " uint width; uint height; uint numberOfLevels;\n" 571 << " " << textureReference 572 << ".GetDimensions(baseLevel, width, height, numberOfLevels);\n" 573 << " width = max(width >> lod, 1);\n" 574 << " height = max(height >> lod, 1);\n"; 575 } 576 else 577 UNREACHABLE(); 578 } 579 580 const char *returnType = textureFunction.getReturnType(); 581 if (strcmp(returnType, "int3") == 0) 582 { 583 out << " return int3(width, height, depth);\n"; 584 } 585 else if (strcmp(returnType, "int2") == 0) 586 { 587 out << " return int2(width, height);\n"; 588 } 589 else 590 { 591 out << " return int(width);\n"; 592 } 593 } 594 595 void ProjectTextureCoordinates(const TextureFunctionHLSL::TextureFunction &textureFunction, 596 ImmutableString *texCoordX, 597 ImmutableString *texCoordY, 598 ImmutableString *texCoordZ) 599 { 600 if (textureFunction.proj) 601 { 602 ImmutableString proj(""); 603 switch (textureFunction.coords) 604 { 605 case 3: 606 proj = ImmutableString(" / t.z"); 607 break; 608 case 4: 609 proj = ImmutableString(" / t.w"); 610 break; 611 default: 612 UNREACHABLE(); 613 } 614 ImmutableStringBuilder texCoordXBuilder(texCoordX->length() + proj.length() + 2u); 615 texCoordXBuilder << '(' << *texCoordX << proj << ')'; 616 *texCoordX = texCoordXBuilder; 617 ImmutableStringBuilder texCoordYBuilder(texCoordY->length() + proj.length() + 2u); 618 texCoordYBuilder << '(' << *texCoordY << proj << ')'; 619 *texCoordY = texCoordYBuilder; 620 ImmutableStringBuilder texCoordZBuilder(texCoordZ->length() + proj.length() + 2u); 621 texCoordZBuilder << '(' << *texCoordZ << proj << ')'; 622 *texCoordZ = texCoordZBuilder; 623 } 624 } 625 626 void OutputIntegerTextureSampleFunctionComputations( 627 TInfoSinkBase &out, 628 const TextureFunctionHLSL::TextureFunction &textureFunction, 629 const ShShaderOutput outputType, 630 const ImmutableString &textureReference, 631 ImmutableString *texCoordX, 632 ImmutableString *texCoordY, 633 ImmutableString *texCoordZ, 634 bool getDimensionsIgnoresBaseLevel) 635 { 636 if (!IsIntegerSampler(textureFunction.sampler)) 637 { 638 return; 639 } 640 641 if (getDimensionsIgnoresBaseLevel) 642 { 643 out << " int baseLevel = samplerMetadata[samplerIndex].baseLevel;\n"; 644 } 645 else 646 { 647 out << " int baseLevel = 0;\n"; 648 } 649 650 if (IsSamplerCube(textureFunction.sampler)) 651 { 652 out << " float width; float height; float layers; float levels;\n"; 653 654 out << " uint mip = 0;\n"; 655 656 out << " " << textureReference 657 << ".GetDimensions(baseLevel + mip, width, height, layers, levels);\n"; 658 659 out << " bool xMajor = abs(t.x) >= abs(t.y) && abs(t.x) >= abs(t.z);\n"; 660 out << " bool yMajor = abs(t.y) >= abs(t.z) && abs(t.y) > abs(t.x);\n"; 661 out << " bool zMajor = abs(t.z) > abs(t.x) && abs(t.z) > abs(t.y);\n"; 662 out << " bool negative = (xMajor && t.x < 0.0f) || (yMajor && t.y < 0.0f) || " 663 "(zMajor && t.z < 0.0f);\n"; 664 665 // FACE_POSITIVE_X = 000b 666 // FACE_NEGATIVE_X = 001b 667 // FACE_POSITIVE_Y = 010b 668 // FACE_NEGATIVE_Y = 011b 669 // FACE_POSITIVE_Z = 100b 670 // FACE_NEGATIVE_Z = 101b 671 out << " int face = (int)negative + (int)yMajor * 2 + (int)zMajor * 4;\n"; 672 673 out << " float u = xMajor ? -t.z : (yMajor && t.y < 0.0f ? -t.x : t.x);\n"; 674 out << " float v = yMajor ? t.z : (negative ? t.y : -t.y);\n"; 675 out << " float m = xMajor ? t.x : (yMajor ? t.y : t.z);\n"; 676 677 out << " float3 r = any(t) ? t : float3(1, 0, 0);\n"; 678 out << " t.x = (u * 0.5f / m) + 0.5f;\n"; 679 out << " t.y = (v * 0.5f / m) + 0.5f;\n"; 680 681 // Mip level computation. 682 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT || 683 textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD || 684 textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) 685 { 686 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT) 687 { 688 // We would like to calculate tha maximum of how many texels we move in the major 689 // face's texture as we move across the screen in any direction. Namely, we want the 690 // length of the directional derivative of the function p (defined below), maximized 691 // over screen space directions. (For short: we want the norm of Dp.) For 692 // simplicity, assume that z-axis is the major axis. By symmetry, we can assume that 693 // the positive z direction is major. (The calculated value will be the same even if 694 // this is false.) Let r denote the function from screen position to cube texture 695 // coordinates. Then p can be written as p = s . P . r, where P(r) = (r.x, r.y)/r.z 696 // is the projection onto the major cube face, and s = diag(width, height)/2. (s 697 // linearly maps from the cube face into texture space, so that p(r) is in units of 698 // texels.) The derivative is 699 // Dp(r) = s |1 0 -r.x/r.z| 700 // |0 1 -r.y/r.z| |ddx(r) ddy(r)| / r.z 701 // = |dot(a, ddx(r)) dot(a, ddy(r))| 702 // |dot(b, ddx(r)) dot(b, ddy(r))| / (2 r.z) 703 // where a = w * vec3(1, 0, -r.x/r.z) 704 // b = h * vec3(0, 1, -r.y/r.z) 705 // We would like to know max(L(x)) over unit vectors x, where L(x) = |Dp(r) x|^2. 706 // Since ddx(r) and ddy(r) are unknown, the best we can do is to sample L in some 707 // directions and take the maximum across the samples. 708 // 709 // Some implementations use max(L(n1), L(n2)) where n1 = vec2(1,0) and n2 = 710 // vec2(0,1). 711 // 712 // Some implementations use max(L(n1), L(n2), L(n3), L(n4)), 713 // where n3 = (n1 + n2) / |n1 + n2| = (n1 + n2)/sqrt(2) 714 // n4 = (n1 - n2) / |n1 - n2| = (n1 - n2)/sqrt(2). 715 // In other words, two samples along the diagonal screen space directions have been 716 // added, giving a strictly better estimate of the true maximum. 717 // 718 // It turns out we can get twice the sample count very cheaply. 719 // We can use the linearity of Dp(r) to get these extra samples of L cheaply in 720 // terms of the already taken samples, L(n1) and L(n2): 721 // Denoting 722 // dpx = Dp(r)n1 723 // dpy = Dp(r)n2 724 // dpxx = dot(dpx, dpx) 725 // dpyy = dot(dpy, dpy) 726 // dpxy = dot(dpx, dpy) 727 // we obtain 728 // L(n3) = |Dp(r)n1 + Dp(r)n2|^2/2 = (dpxx + dpyy)/2 + dpxy 729 // L(n4) = |Dp(r)n1 - Dp(r)n2|^2/2 = (dpxx + dpyy)/2 - dpxy 730 // max(L(n1), L(n2), L(n3), L(n4)) 731 // = max(max(L(n1), L(n2)), max(L(n3), L(n4))) 732 // = max(max(dpxx, dpyy), (dpxx + dpyy)/2 + abs(dpxy)) 733 // So the extra cost is: one dot, one abs, one add, one multiply-add and one max. 734 // (All scalar.) 735 // 736 // In section 3.8.10.1, the OpenGL ES 3 specification defines the "scale factor", 737 // rho. In our terminology, this definition works out to taking sqrt(max(L(n1), 738 // L(n2))). Some implementations will use this estimate, here we use the strictly 739 // better sqrt(max(L(n1), L(n2), L(n3), L(n4))), since it's not much more expensive 740 // to calculate. 741 742 // Swap coordinates such that we can assume that the positive z-axis is major, in 743 // what follows. 744 out << " float3 ddxr = xMajor ? ddx(r).yzx : yMajor ? ddx(r).zxy : ddx(r).xyz;\n" 745 " float3 ddyr = xMajor ? ddy(r).yzx : yMajor ? ddy(r).zxy : ddy(r).xyz;\n" 746 " r = xMajor ? r.yzx : yMajor ? r.zxy : r.xyz;\n"; 747 748 out << " float2 s = 0.5*float2(width, height);\n" 749 " float2 dpx = s * (ddxr.xy - ddxr.z*r.xy/r.z)/r.z;\n" 750 " float2 dpy = s * (ddyr.xy - ddyr.z*r.xy/r.z)/r.z;\n" 751 " float dpxx = dot(dpx, dpx);\n;" 752 " float dpyy = dot(dpy, dpy);\n;" 753 " float dpxy = dot(dpx, dpy);\n" 754 " float ma = max(dpxx, dpyy);\n" 755 " float mb = 0.5 * (dpxx + dpyy) + abs(dpxy);\n" 756 " float mab = max(ma, mb);\n" 757 " float lod = 0.5f * log2(mab);\n"; 758 } 759 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) 760 { 761 // ESSL 3.00.6 spec section 8.8: "For the cube version, the partial 762 // derivatives of P are assumed to be in the coordinate system used before 763 // texture coordinates are projected onto the appropriate cube face." 764 // ddx[0] and ddy[0] are the derivatives of t.x passed into the function 765 // ddx[1] and ddy[1] are the derivatives of t.y passed into the function 766 // ddx[2] and ddy[2] are the derivatives of t.z passed into the function 767 // Determine the derivatives of u, v and m 768 out << " float dudx = xMajor ? ddx[2] : (yMajor && t.y < 0.0f ? -ddx[0] " 769 ": ddx[0]);\n" 770 " float dudy = xMajor ? ddy[2] : (yMajor && t.y < 0.0f ? -ddy[0] " 771 ": ddy[0]);\n" 772 " float dvdx = yMajor ? ddx[2] : (negative ? ddx[1] : -ddx[1]);\n" 773 " float dvdy = yMajor ? ddy[2] : (negative ? ddy[1] : -ddy[1]);\n" 774 " float dmdx = xMajor ? ddx[0] : (yMajor ? ddx[1] : ddx[2]);\n" 775 " float dmdy = xMajor ? ddy[0] : (yMajor ? ddy[1] : ddy[2]);\n"; 776 // Now determine the derivatives of the face coordinates, using the 777 // derivatives calculated above. 778 // d / dx (u(x) * 0.5 / m(x) + 0.5) 779 // = 0.5 * (m(x) * u'(x) - u(x) * m'(x)) / m(x)^2 780 out << " float dfacexdx = 0.5f * (m * dudx - u * dmdx) / (m * m);\n" 781 " float dfaceydx = 0.5f * (m * dvdx - v * dmdx) / (m * m);\n" 782 " float dfacexdy = 0.5f * (m * dudy - u * dmdy) / (m * m);\n" 783 " float dfaceydy = 0.5f * (m * dvdy - v * dmdy) / (m * m);\n" 784 " float2 sizeVec = float2(width, height);\n" 785 " float2 faceddx = float2(dfacexdx, dfaceydx) * sizeVec;\n" 786 " float2 faceddy = float2(dfacexdy, dfaceydy) * sizeVec;\n"; 787 // Optimization: instead of: log2(max(length(faceddx), length(faceddy))) 788 // we compute: log2(max(length(faceddx)^2, length(faceddy)^2)) / 2 789 out << " float lengthfaceddx2 = dot(faceddx, faceddx);\n" 790 " float lengthfaceddy2 = dot(faceddy, faceddy);\n" 791 " float lod = log2(max(lengthfaceddx2, lengthfaceddy2)) * 0.5f;\n"; 792 } 793 out << " mip = uint(min(max(round(lod), 0), levels - 1));\n" 794 << " " << textureReference 795 << ".GetDimensions(baseLevel + mip, width, height, layers, levels);\n"; 796 } 797 798 // Convert from normalized floating-point to integer 799 static const ImmutableString kXPrefix("int(floor(width * frac("); 800 static const ImmutableString kYPrefix("int(floor(height * frac("); 801 static const ImmutableString kSuffix(")))"); 802 ImmutableStringBuilder texCoordXBuilder(kXPrefix.length() + texCoordX->length() + 803 kSuffix.length()); 804 texCoordXBuilder << kXPrefix << *texCoordX << kSuffix; 805 *texCoordX = texCoordXBuilder; 806 ImmutableStringBuilder texCoordYBuilder(kYPrefix.length() + texCoordX->length() + 807 kSuffix.length()); 808 texCoordYBuilder << kYPrefix << *texCoordY << kSuffix; 809 *texCoordY = texCoordYBuilder; 810 *texCoordZ = ImmutableString("face"); 811 } 812 else if (textureFunction.method != TextureFunctionHLSL::TextureFunction::FETCH) 813 { 814 if (IsSamplerArray(textureFunction.sampler)) 815 { 816 out << " float width; float height; float layers; float levels;\n"; 817 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0) 818 { 819 out << " uint mip = 0;\n"; 820 } 821 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS) 822 { 823 out << " uint mip = bias;\n"; 824 } 825 else 826 { 827 828 out << " " << textureReference 829 << ".GetDimensions(baseLevel, width, height, layers, levels);\n"; 830 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT || 831 textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS) 832 { 833 out << " float2 tSized = float2(t.x * width, t.y * height);\n" 834 " float dx = length(ddx(tSized));\n" 835 " float dy = length(ddy(tSized));\n" 836 " float lod = log2(max(dx, dy));\n"; 837 838 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS) 839 { 840 out << " lod += bias;\n"; 841 } 842 } 843 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) 844 { 845 out << " float2 sizeVec = float2(width, height);\n" 846 " float2 sizeDdx = ddx * sizeVec;\n" 847 " float2 sizeDdy = ddy * sizeVec;\n" 848 " float lod = log2(max(dot(sizeDdx, sizeDdx), " 849 "dot(sizeDdy, sizeDdy))) * 0.5f;\n"; 850 } 851 852 out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; 853 } 854 855 out << " " << textureReference 856 << ".GetDimensions(baseLevel + mip, width, height, layers, levels);\n"; 857 } 858 else if (IsSampler2D(textureFunction.sampler)) 859 { 860 out << " float width; float height; float levels;\n"; 861 862 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0) 863 { 864 out << " uint mip = 0;\n"; 865 } 866 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS) 867 { 868 out << " uint mip = bias;\n"; 869 } 870 else 871 { 872 out << " " << textureReference 873 << ".GetDimensions(baseLevel, width, height, levels);\n"; 874 875 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT || 876 textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS) 877 { 878 out << " float2 tSized = float2(t.x * width, t.y * height);\n" 879 " float dx = length(ddx(tSized));\n" 880 " float dy = length(ddy(tSized));\n" 881 " float lod = log2(max(dx, dy));\n"; 882 883 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS) 884 { 885 out << " lod += bias;\n"; 886 } 887 } 888 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) 889 { 890 out << " float2 sizeVec = float2(width, height);\n" 891 " float2 sizeDdx = ddx * sizeVec;\n" 892 " float2 sizeDdy = ddy * sizeVec;\n" 893 " float lod = log2(max(dot(sizeDdx, sizeDdx), " 894 "dot(sizeDdy, sizeDdy))) * 0.5f;\n"; 895 } 896 897 out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; 898 } 899 900 out << " " << textureReference 901 << ".GetDimensions(baseLevel + mip, width, height, levels);\n"; 902 } 903 else if (IsSampler3D(textureFunction.sampler)) 904 { 905 out << " float width; float height; float depth; float levels;\n"; 906 907 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0) 908 { 909 out << " uint mip = 0;\n"; 910 } 911 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS) 912 { 913 out << " uint mip = bias;\n"; 914 } 915 else 916 { 917 out << " " << textureReference 918 << ".GetDimensions(baseLevel, width, height, depth, levels);\n"; 919 920 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT || 921 textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS) 922 { 923 out << " float3 tSized = float3(t.x * width, t.y * height, t.z * depth);\n" 924 " float dx = length(ddx(tSized));\n" 925 " float dy = length(ddy(tSized));\n" 926 " float lod = log2(max(dx, dy));\n"; 927 928 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS) 929 { 930 out << " lod += bias;\n"; 931 } 932 } 933 else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) 934 { 935 out << " float3 sizeVec = float3(width, height, depth);\n" 936 " float3 sizeDdx = ddx * sizeVec;\n" 937 " float3 sizeDdy = ddy * sizeVec;\n" 938 " float lod = log2(max(dot(sizeDdx, sizeDdx), dot(sizeDdy, " 939 "sizeDdy))) * 0.5f;\n"; 940 } 941 942 out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; 943 } 944 945 out << " " << textureReference 946 << ".GetDimensions(baseLevel + mip, width, height, depth, levels);\n"; 947 } 948 else 949 UNREACHABLE(); 950 951 OutputIntTexCoordWraps(out, textureFunction, texCoordX, texCoordY, texCoordZ); 952 } 953 } 954 955 void OutputTextureGatherFunctionBody(TInfoSinkBase &out, 956 const TextureFunctionHLSL::TextureFunction &textureFunction, 957 ShShaderOutput outputType, 958 const ImmutableString &textureReference, 959 const ImmutableString &samplerReference, 960 const ImmutableString &texCoordX, 961 const ImmutableString &texCoordY, 962 const ImmutableString &texCoordZ) 963 { 964 const int hlslCoords = GetHLSLCoordCount(textureFunction, outputType); 965 ImmutableString samplerCoordTypeString( 966 GetSamplerCoordinateTypeString(textureFunction, hlslCoords)); 967 ImmutableStringBuilder samplerCoordBuilder( 968 samplerCoordTypeString.length() + strlen("(") + texCoordX.length() + strlen(", ") + 969 texCoordY.length() + strlen(", ") + texCoordZ.length() + strlen(")")); 970 971 samplerCoordBuilder << samplerCoordTypeString << "(" << texCoordX << ", " << texCoordY; 972 if (hlslCoords >= 3) 973 { 974 if (textureFunction.coords < 3) 975 { 976 samplerCoordBuilder << ", 0"; 977 } 978 else 979 { 980 samplerCoordBuilder << ", " << texCoordZ; 981 } 982 } 983 samplerCoordBuilder << ")"; 984 985 ImmutableString samplerCoordString(samplerCoordBuilder); 986 987 if (IsShadowSampler(textureFunction.sampler)) 988 { 989 out << "return " << textureReference << ".GatherCmp(" << samplerReference << ", " 990 << samplerCoordString << ", refZ"; 991 if (textureFunction.offset) 992 { 993 out << ", offset"; 994 } 995 out << ");\n"; 996 return; 997 } 998 999 constexpr std::array<const char *, 4> kHLSLGatherFunctions = { 1000 {"GatherRed", "GatherGreen", "GatherBlue", "GatherAlpha"}}; 1001 1002 out << " switch(comp)\n" 1003 " {\n"; 1004 for (size_t component = 0; component < kHLSLGatherFunctions.size(); ++component) 1005 { 1006 out << " case " << component << ":\n" 1007 << " return " << textureReference << "." << kHLSLGatherFunctions[component] 1008 << "(" << samplerReference << ", " << samplerCoordString; 1009 if (textureFunction.offset) 1010 { 1011 out << ", offset"; 1012 } 1013 out << ");\n"; 1014 } 1015 1016 out << " default:\n" 1017 " return float4(0.0, 0.0, 0.0, 1.0);\n" 1018 " }\n"; 1019 } 1020 1021 void OutputTextureSampleFunctionReturnStatement( 1022 TInfoSinkBase &out, 1023 const TextureFunctionHLSL::TextureFunction &textureFunction, 1024 const ShShaderOutput outputType, 1025 const ImmutableString &textureReference, 1026 const ImmutableString &samplerReference, 1027 const ImmutableString &texCoordX, 1028 const ImmutableString &texCoordY, 1029 const ImmutableString &texCoordZ) 1030 { 1031 out << " return "; 1032 1033 if (IsIntegerSampler(textureFunction.sampler) && !IsSamplerCube(textureFunction.sampler) && 1034 textureFunction.method != TextureFunctionHLSL::TextureFunction::FETCH) 1035 { 1036 out << " useBorderColor ? "; 1037 if (IsIntegerSamplerUnsigned(textureFunction.sampler)) 1038 { 1039 out << "asuint"; 1040 } 1041 out << "(samplerMetadata[samplerIndex].intBorderColor) : "; 1042 } 1043 1044 // HLSL intrinsic 1045 if (outputType == SH_HLSL_3_0_OUTPUT) 1046 { 1047 switch (textureFunction.sampler) 1048 { 1049 case EbtSampler2D: 1050 case EbtSamplerVideoWEBGL: 1051 case EbtSamplerExternalOES: 1052 out << "tex2D"; 1053 break; 1054 case EbtSamplerCube: 1055 out << "texCUBE"; 1056 break; 1057 default: 1058 UNREACHABLE(); 1059 } 1060 1061 switch (textureFunction.method) 1062 { 1063 case TextureFunctionHLSL::TextureFunction::IMPLICIT: 1064 out << "(" << samplerReference << ", "; 1065 break; 1066 case TextureFunctionHLSL::TextureFunction::BIAS: 1067 out << "bias(" << samplerReference << ", "; 1068 break; 1069 case TextureFunctionHLSL::TextureFunction::LOD: 1070 out << "lod(" << samplerReference << ", "; 1071 break; 1072 case TextureFunctionHLSL::TextureFunction::LOD0: 1073 out << "lod(" << samplerReference << ", "; 1074 break; 1075 case TextureFunctionHLSL::TextureFunction::LOD0BIAS: 1076 out << "lod(" << samplerReference << ", "; 1077 break; 1078 case TextureFunctionHLSL::TextureFunction::GRAD: 1079 out << "grad(" << samplerReference << ", "; 1080 break; 1081 default: 1082 UNREACHABLE(); 1083 } 1084 } 1085 else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT) 1086 { 1087 OutputHLSL4SampleFunctionPrefix(out, textureFunction, textureReference, samplerReference); 1088 } 1089 else 1090 UNREACHABLE(); 1091 1092 const int hlslCoords = GetHLSLCoordCount(textureFunction, outputType); 1093 out << GetSamplerCoordinateTypeString(textureFunction, hlslCoords); 1094 1095 if (hlslCoords >= 2) 1096 { 1097 out << "(" << texCoordX << ", " << texCoordY; 1098 } 1099 else if (hlslCoords == 1) 1100 { 1101 std::string varName(texCoordX.data()); 1102 if (size_t pos = varName.find_last_of('.') != std::string::npos) 1103 { 1104 varName = varName.substr(0, pos); 1105 } 1106 out << "(" << varName; 1107 } 1108 else 1109 { 1110 out << "("; 1111 } 1112 1113 if (outputType == SH_HLSL_3_0_OUTPUT) 1114 { 1115 if (hlslCoords >= 3) 1116 { 1117 if (textureFunction.coords < 3) 1118 { 1119 out << ", 0"; 1120 } 1121 else 1122 { 1123 out << ", " << texCoordZ; 1124 } 1125 } 1126 1127 if (hlslCoords == 4) 1128 { 1129 switch (textureFunction.method) 1130 { 1131 case TextureFunctionHLSL::TextureFunction::BIAS: 1132 out << ", bias"; 1133 break; 1134 case TextureFunctionHLSL::TextureFunction::LOD: 1135 out << ", lod"; 1136 break; 1137 case TextureFunctionHLSL::TextureFunction::LOD0: 1138 out << ", 0"; 1139 break; 1140 case TextureFunctionHLSL::TextureFunction::LOD0BIAS: 1141 out << ", bias"; 1142 break; 1143 default: 1144 UNREACHABLE(); 1145 } 1146 } 1147 1148 out << ")"; 1149 } 1150 else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT) 1151 { 1152 if (hlslCoords >= 3) 1153 { 1154 ASSERT(!IsIntegerSampler(textureFunction.sampler) || 1155 !IsSamplerCube(textureFunction.sampler) || texCoordZ == "face"); 1156 out << ", " << texCoordZ; 1157 } 1158 1159 if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD) 1160 { 1161 if (IsIntegerSampler(textureFunction.sampler)) 1162 { 1163 out << ", mip)"; 1164 } 1165 else if (IsShadowSampler(textureFunction.sampler)) 1166 { 1167 // Compare value 1168 if (textureFunction.proj) 1169 { 1170 // According to ESSL 3.00.4 sec 8.8 p95 on textureProj: 1171 // The resulting third component of P' in the shadow forms is used as 1172 // Dref 1173 out << "), " << texCoordZ; 1174 } 1175 else 1176 { 1177 switch (textureFunction.coords) 1178 { 1179 case 3: 1180 out << "), t.z"; 1181 break; 1182 case 4: 1183 out << "), t.w"; 1184 break; 1185 default: 1186 UNREACHABLE(); 1187 } 1188 } 1189 } 1190 else 1191 { 1192 out << "), ddx, ddy"; 1193 } 1194 } 1195 else if (IsIntegerSampler(textureFunction.sampler) || 1196 textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH) 1197 { 1198 if (IsSampler2DMS(textureFunction.sampler) || 1199 IsSampler2DMSArray(textureFunction.sampler)) 1200 out << "), index"; 1201 else if (IsSamplerBuffer(textureFunction.sampler)) 1202 out << ")"; 1203 else 1204 out << ", mip)"; 1205 } 1206 else if (IsShadowSampler(textureFunction.sampler)) 1207 { 1208 // Compare value 1209 if (textureFunction.proj) 1210 { 1211 // According to ESSL 3.00.4 sec 8.8 p95 on textureProj: 1212 // The resulting third component of P' in the shadow forms is used as Dref 1213 out << "), " << texCoordZ; 1214 } 1215 else 1216 { 1217 switch (textureFunction.coords) 1218 { 1219 case 3: 1220 out << "), t.z"; 1221 break; 1222 case 4: 1223 out << "), t.w"; 1224 break; 1225 default: 1226 UNREACHABLE(); 1227 } 1228 } 1229 } 1230 else 1231 { 1232 switch (textureFunction.method) 1233 { 1234 case TextureFunctionHLSL::TextureFunction::IMPLICIT: 1235 out << ")"; 1236 break; 1237 case TextureFunctionHLSL::TextureFunction::BIAS: 1238 out << "), bias"; 1239 break; 1240 case TextureFunctionHLSL::TextureFunction::LOD: 1241 out << "), lod"; 1242 break; 1243 case TextureFunctionHLSL::TextureFunction::LOD0: 1244 out << "), 0"; 1245 break; 1246 case TextureFunctionHLSL::TextureFunction::LOD0BIAS: 1247 out << "), bias"; 1248 break; 1249 default: 1250 UNREACHABLE(); 1251 } 1252 } 1253 1254 if (textureFunction.offset && 1255 (!IsIntegerSampler(textureFunction.sampler) || 1256 textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)) 1257 { 1258 out << ", offset"; 1259 } 1260 } 1261 else 1262 UNREACHABLE(); 1263 1264 out << ");\n"; // Close the sample function call and return statement 1265 } 1266 1267 } // Anonymous namespace 1268 1269 ImmutableString TextureFunctionHLSL::TextureFunction::name() const 1270 { 1271 static const ImmutableString kGlTextureName("gl_texture"); 1272 1273 ImmutableString suffix(TextureTypeSuffix(this->sampler)); 1274 1275 ImmutableStringBuilder name(kGlTextureName.length() + suffix.length() + 4u + 6u + 5u); 1276 1277 name << kGlTextureName; 1278 1279 // We need to include full the sampler type in the function name to make the signature unique 1280 // on D3D11, where samplers are passed to texture functions as indices. 1281 name << suffix; 1282 1283 if (proj) 1284 { 1285 name << "Proj"; 1286 } 1287 1288 if (offset) 1289 { 1290 name << "Offset"; 1291 } 1292 1293 switch (method) 1294 { 1295 case IMPLICIT: 1296 break; 1297 case BIAS: 1298 break; // Extra parameter makes the signature unique 1299 case LOD: 1300 name << "Lod"; 1301 break; 1302 case LOD0: 1303 name << "Lod0"; 1304 break; 1305 case LOD0BIAS: 1306 name << "Lod0"; 1307 break; // Extra parameter makes the signature unique 1308 case SIZE: 1309 name << "Size"; 1310 break; 1311 case FETCH: 1312 name << "Fetch"; 1313 break; 1314 case GRAD: 1315 name << "Grad"; 1316 break; 1317 case GATHER: 1318 name << "Gather"; 1319 break; 1320 default: 1321 UNREACHABLE(); 1322 } 1323 1324 return name; 1325 } 1326 1327 const char *TextureFunctionHLSL::TextureFunction::getReturnType() const 1328 { 1329 if (method == TextureFunction::SIZE) 1330 { 1331 switch (sampler) 1332 { 1333 case EbtSampler2D: 1334 case EbtISampler2D: 1335 case EbtUSampler2D: 1336 case EbtSampler2DShadow: 1337 case EbtSamplerCube: 1338 case EbtISamplerCube: 1339 case EbtUSamplerCube: 1340 case EbtSamplerCubeShadow: 1341 case EbtSamplerExternalOES: 1342 case EbtSampler2DMS: 1343 case EbtISampler2DMS: 1344 case EbtUSampler2DMS: 1345 case EbtSamplerVideoWEBGL: 1346 return "int2"; 1347 case EbtSampler3D: 1348 case EbtISampler3D: 1349 case EbtUSampler3D: 1350 case EbtSampler2DArray: 1351 case EbtISampler2DArray: 1352 case EbtUSampler2DArray: 1353 case EbtSampler2DMSArray: 1354 case EbtISampler2DMSArray: 1355 case EbtUSampler2DMSArray: 1356 case EbtSampler2DArrayShadow: 1357 return "int3"; 1358 case EbtISamplerBuffer: 1359 case EbtUSamplerBuffer: 1360 case EbtSamplerBuffer: 1361 return "int"; 1362 default: 1363 UNREACHABLE(); 1364 } 1365 } 1366 else // Sampling function 1367 { 1368 switch (sampler) 1369 { 1370 case EbtSampler2D: 1371 case EbtSampler2DMS: 1372 case EbtSampler2DMSArray: 1373 case EbtSampler3D: 1374 case EbtSamplerCube: 1375 case EbtSampler2DArray: 1376 case EbtSamplerExternalOES: 1377 case EbtSamplerVideoWEBGL: 1378 case EbtSamplerBuffer: 1379 return "float4"; 1380 case EbtISampler2D: 1381 case EbtISampler2DMS: 1382 case EbtISampler2DMSArray: 1383 case EbtISampler3D: 1384 case EbtISamplerCube: 1385 case EbtISampler2DArray: 1386 case EbtISamplerBuffer: 1387 return "int4"; 1388 case EbtUSampler2D: 1389 case EbtUSampler2DMS: 1390 case EbtUSampler2DMSArray: 1391 case EbtUSampler3D: 1392 case EbtUSamplerCube: 1393 case EbtUSampler2DArray: 1394 case EbtUSamplerBuffer: 1395 return "uint4"; 1396 case EbtSampler2DShadow: 1397 case EbtSamplerCubeShadow: 1398 case EbtSampler2DArrayShadow: 1399 if (method == TextureFunctionHLSL::TextureFunction::GATHER) 1400 { 1401 return "float4"; 1402 } 1403 else 1404 { 1405 return "float"; 1406 } 1407 default: 1408 UNREACHABLE(); 1409 } 1410 } 1411 return ""; 1412 } 1413 1414 bool TextureFunctionHLSL::TextureFunction::operator<(const TextureFunction &rhs) const 1415 { 1416 return std::tie(sampler, coords, proj, offset, method) < 1417 std::tie(rhs.sampler, rhs.coords, rhs.proj, rhs.offset, rhs.method); 1418 } 1419 1420 ImmutableString TextureFunctionHLSL::useTextureFunction(const ImmutableString &name, 1421 TBasicType samplerType, 1422 int coords, 1423 size_t argumentCount, 1424 bool lod0, 1425 sh::GLenum shaderType) 1426 { 1427 TextureFunction textureFunction; 1428 textureFunction.sampler = samplerType; 1429 textureFunction.coords = coords; 1430 textureFunction.method = TextureFunction::IMPLICIT; 1431 textureFunction.proj = false; 1432 textureFunction.offset = false; 1433 1434 if (name == "texture2D" || name == "textureCube" || name == "texture") 1435 { 1436 textureFunction.method = TextureFunction::IMPLICIT; 1437 } 1438 else if (name == "texture2DProj" || name == "textureProj") 1439 { 1440 textureFunction.method = TextureFunction::IMPLICIT; 1441 textureFunction.proj = true; 1442 } 1443 else if (name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod" || 1444 name == "texture2DLodEXT" || name == "textureCubeLodEXT") 1445 { 1446 textureFunction.method = TextureFunction::LOD; 1447 } 1448 else if (name == "texture2DProjLod" || name == "textureProjLod" || 1449 name == "texture2DProjLodEXT") 1450 { 1451 textureFunction.method = TextureFunction::LOD; 1452 textureFunction.proj = true; 1453 } 1454 else if (name == "textureSize") 1455 { 1456 textureFunction.method = TextureFunction::SIZE; 1457 } 1458 else if (name == "textureOffset") 1459 { 1460 textureFunction.method = TextureFunction::IMPLICIT; 1461 textureFunction.offset = true; 1462 } 1463 else if (name == "textureProjOffset") 1464 { 1465 textureFunction.method = TextureFunction::IMPLICIT; 1466 textureFunction.offset = true; 1467 textureFunction.proj = true; 1468 } 1469 else if (name == "textureLodOffset") 1470 { 1471 textureFunction.method = TextureFunction::LOD; 1472 textureFunction.offset = true; 1473 } 1474 else if (name == "textureProjLodOffset") 1475 { 1476 textureFunction.method = TextureFunction::LOD; 1477 textureFunction.proj = true; 1478 textureFunction.offset = true; 1479 } 1480 else if (name == "texelFetch") 1481 { 1482 textureFunction.method = TextureFunction::FETCH; 1483 } 1484 else if (name == "texelFetchOffset") 1485 { 1486 textureFunction.method = TextureFunction::FETCH; 1487 textureFunction.offset = true; 1488 } 1489 else if (name == "textureGrad" || name == "texture2DGradEXT") 1490 { 1491 textureFunction.method = TextureFunction::GRAD; 1492 } 1493 else if (name == "textureGradOffset") 1494 { 1495 textureFunction.method = TextureFunction::GRAD; 1496 textureFunction.offset = true; 1497 } 1498 else if (name == "textureProjGrad" || name == "texture2DProjGradEXT" || 1499 name == "textureCubeGradEXT") 1500 { 1501 textureFunction.method = TextureFunction::GRAD; 1502 textureFunction.proj = true; 1503 } 1504 else if (name == "textureProjGradOffset") 1505 { 1506 textureFunction.method = TextureFunction::GRAD; 1507 textureFunction.proj = true; 1508 textureFunction.offset = true; 1509 } 1510 else if (name == "textureGather") 1511 { 1512 textureFunction.method = TextureFunction::GATHER; 1513 } 1514 else if (name == "textureGatherOffset") 1515 { 1516 textureFunction.method = TextureFunction::GATHER; 1517 textureFunction.offset = true; 1518 } 1519 else if (name == "textureVideoWEBGL") 1520 { 1521 textureFunction.method = TextureFunction::IMPLICIT; 1522 } 1523 else 1524 UNREACHABLE(); 1525 1526 if (textureFunction.method == 1527 TextureFunction::IMPLICIT) // Could require lod 0 or have a bias argument 1528 { 1529 size_t mandatoryArgumentCount = 2; // All functions have sampler and coordinate arguments 1530 1531 if (textureFunction.offset) 1532 { 1533 mandatoryArgumentCount++; 1534 } 1535 1536 bool bias = (argumentCount > mandatoryArgumentCount); // Bias argument is optional 1537 1538 if (lod0 || shaderType == GL_VERTEX_SHADER || shaderType == GL_COMPUTE_SHADER) 1539 { 1540 if (bias) 1541 { 1542 textureFunction.method = TextureFunction::LOD0BIAS; 1543 } 1544 else 1545 { 1546 textureFunction.method = TextureFunction::LOD0; 1547 } 1548 } 1549 else if (bias) 1550 { 1551 textureFunction.method = TextureFunction::BIAS; 1552 } 1553 } 1554 1555 mUsesTexture.insert(textureFunction); 1556 return textureFunction.name(); 1557 } 1558 1559 void TextureFunctionHLSL::textureFunctionHeader(TInfoSinkBase &out, 1560 const ShShaderOutput outputType, 1561 bool getDimensionsIgnoresBaseLevel) 1562 { 1563 for (const TextureFunction &textureFunction : mUsesTexture) 1564 { 1565 // Function header 1566 out << textureFunction.getReturnType() << " " << textureFunction.name() << "("; 1567 1568 OutputTextureFunctionArgumentList(out, textureFunction, outputType); 1569 1570 out << ")\n" 1571 "{\n"; 1572 1573 // In some cases we use a variable to store the texture/sampler objects, but to work around 1574 // a D3D11 compiler bug related to discard inside a loop that is conditional on texture 1575 // sampling we need to call the function directly on references to the texture and sampler 1576 // arrays. The bug was found using dEQP-GLES3.functional.shaders.discard*loop_texture* 1577 // tests. 1578 ImmutableString textureReference(""); 1579 ImmutableString samplerReference(""); 1580 GetTextureReference(out, textureFunction, outputType, &textureReference, &samplerReference); 1581 1582 if (textureFunction.method == TextureFunction::SIZE) 1583 { 1584 OutputTextureSizeFunctionBody(out, textureFunction, textureReference, 1585 getDimensionsIgnoresBaseLevel); 1586 } 1587 else 1588 { 1589 ImmutableString texCoordX("t.x"); 1590 ImmutableString texCoordY("t.y"); 1591 ImmutableString texCoordZ("t.z"); 1592 if (textureFunction.method == TextureFunction::GATHER) 1593 { 1594 OutputTextureGatherFunctionBody(out, textureFunction, outputType, textureReference, 1595 samplerReference, texCoordX, texCoordY, texCoordZ); 1596 } 1597 else 1598 { 1599 ProjectTextureCoordinates(textureFunction, &texCoordX, &texCoordY, &texCoordZ); 1600 OutputIntegerTextureSampleFunctionComputations( 1601 out, textureFunction, outputType, textureReference, &texCoordX, &texCoordY, 1602 &texCoordZ, getDimensionsIgnoresBaseLevel); 1603 OutputTextureSampleFunctionReturnStatement(out, textureFunction, outputType, 1604 textureReference, samplerReference, 1605 texCoordX, texCoordY, texCoordZ); 1606 } 1607 } 1608 1609 out << "}\n" 1610 "\n"; 1611 } 1612 } 1613 1614 } // namespace sh