WebGLProgram.cpp (37505B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "WebGLProgram.h" 7 8 #include "GLContext.h" 9 #include "WebGLBuffer.h" 10 #include "WebGLContext.h" 11 #include "WebGLFormats.h" 12 #include "WebGLShader.h" 13 #include "WebGLShaderValidator.h" 14 #include "WebGLTransformFeedback.h" 15 #include "WebGLValidateStrings.h" 16 #include "WebGLVertexArray.h" 17 #include "mozilla/CheckedInt.h" 18 #include "mozilla/RefPtr.h" 19 #include "mozilla/dom/WebGL2RenderingContextBinding.h" 20 #include "mozilla/dom/WebGLRenderingContextBinding.h" 21 #include "mozilla/gfx/Logging.h" 22 #include "nsPrintfCString.h" 23 24 namespace mozilla { 25 26 static bool IsShadowSampler(const GLenum elemType) { 27 switch (elemType) { 28 case LOCAL_GL_SAMPLER_2D_SHADOW: 29 case LOCAL_GL_SAMPLER_CUBE_SHADOW: 30 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: 31 return true; 32 default: 33 return false; 34 } 35 } 36 37 static Maybe<webgl::TextureBaseType> SamplerBaseType(const GLenum elemType) { 38 switch (elemType) { 39 case LOCAL_GL_SAMPLER_2D: 40 case LOCAL_GL_SAMPLER_3D: 41 case LOCAL_GL_SAMPLER_CUBE: 42 case LOCAL_GL_SAMPLER_2D_ARRAY: 43 case LOCAL_GL_SAMPLER_2D_SHADOW: 44 case LOCAL_GL_SAMPLER_CUBE_SHADOW: 45 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: 46 return Some(webgl::TextureBaseType::Float); 47 48 case LOCAL_GL_INT_SAMPLER_2D: 49 case LOCAL_GL_INT_SAMPLER_3D: 50 case LOCAL_GL_INT_SAMPLER_CUBE: 51 case LOCAL_GL_INT_SAMPLER_2D_ARRAY: 52 return Some(webgl::TextureBaseType::Int); 53 54 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: 55 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: 56 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: 57 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: 58 return Some(webgl::TextureBaseType::UInt); 59 60 default: 61 return {}; 62 } 63 } 64 65 ////////// 66 67 static webgl::TextureBaseType FragOutputBaseType(const GLenum type) { 68 switch (type) { 69 case LOCAL_GL_FLOAT: 70 case LOCAL_GL_FLOAT_VEC2: 71 case LOCAL_GL_FLOAT_VEC3: 72 case LOCAL_GL_FLOAT_VEC4: 73 return webgl::TextureBaseType::Float; 74 75 case LOCAL_GL_INT: 76 case LOCAL_GL_INT_VEC2: 77 case LOCAL_GL_INT_VEC3: 78 case LOCAL_GL_INT_VEC4: 79 return webgl::TextureBaseType::Int; 80 81 case LOCAL_GL_UNSIGNED_INT: 82 case LOCAL_GL_UNSIGNED_INT_VEC2: 83 case LOCAL_GL_UNSIGNED_INT_VEC3: 84 case LOCAL_GL_UNSIGNED_INT_VEC4: 85 return webgl::TextureBaseType::UInt; 86 87 default: 88 break; 89 } 90 91 const auto& str = EnumString(type); 92 gfxCriticalError() << "Unhandled enum for FragOutputBaseType: " 93 << str.c_str(); 94 return webgl::TextureBaseType::Float; 95 } 96 97 // ----------------------------------------- 98 99 namespace webgl { 100 101 void UniformAs1fv(gl::GLContext& gl, GLint location, GLsizei count, 102 bool transpose, const void* any) { 103 gl.fUniform1fv(location, count, static_cast<const float*>(any)); 104 } 105 void UniformAs2fv(gl::GLContext& gl, GLint location, GLsizei count, 106 bool transpose, const void* any) { 107 gl.fUniform2fv(location, count, static_cast<const float*>(any)); 108 } 109 void UniformAs3fv(gl::GLContext& gl, GLint location, GLsizei count, 110 bool transpose, const void* any) { 111 gl.fUniform3fv(location, count, static_cast<const float*>(any)); 112 } 113 void UniformAs4fv(gl::GLContext& gl, GLint location, GLsizei count, 114 bool transpose, const void* any) { 115 gl.fUniform4fv(location, count, static_cast<const float*>(any)); 116 } 117 118 void UniformAs1iv(gl::GLContext& gl, GLint location, GLsizei count, 119 bool transpose, const void* any) { 120 gl.fUniform1iv(location, count, static_cast<const int32_t*>(any)); 121 } 122 void UniformAs2iv(gl::GLContext& gl, GLint location, GLsizei count, 123 bool transpose, const void* any) { 124 gl.fUniform2iv(location, count, static_cast<const int32_t*>(any)); 125 } 126 void UniformAs3iv(gl::GLContext& gl, GLint location, GLsizei count, 127 bool transpose, const void* any) { 128 gl.fUniform3iv(location, count, static_cast<const int32_t*>(any)); 129 } 130 void UniformAs4iv(gl::GLContext& gl, GLint location, GLsizei count, 131 bool transpose, const void* any) { 132 gl.fUniform4iv(location, count, static_cast<const int32_t*>(any)); 133 } 134 135 void UniformAs1uiv(gl::GLContext& gl, GLint location, GLsizei count, 136 bool transpose, const void* any) { 137 gl.fUniform1uiv(location, count, static_cast<const uint32_t*>(any)); 138 } 139 void UniformAs2uiv(gl::GLContext& gl, GLint location, GLsizei count, 140 bool transpose, const void* any) { 141 gl.fUniform2uiv(location, count, static_cast<const uint32_t*>(any)); 142 } 143 void UniformAs3uiv(gl::GLContext& gl, GLint location, GLsizei count, 144 bool transpose, const void* any) { 145 gl.fUniform3uiv(location, count, static_cast<const uint32_t*>(any)); 146 } 147 void UniformAs4uiv(gl::GLContext& gl, GLint location, GLsizei count, 148 bool transpose, const void* any) { 149 gl.fUniform4uiv(location, count, static_cast<const uint32_t*>(any)); 150 } 151 152 void UniformAsMatrix2x2fv(gl::GLContext& gl, GLint location, GLsizei count, 153 bool transpose, const void* any) { 154 gl.fUniformMatrix2fv(location, count, transpose, 155 static_cast<const float*>(any)); 156 } 157 void UniformAsMatrix2x3fv(gl::GLContext& gl, GLint location, GLsizei count, 158 bool transpose, const void* any) { 159 gl.fUniformMatrix2x3fv(location, count, transpose, 160 static_cast<const float*>(any)); 161 } 162 void UniformAsMatrix2x4fv(gl::GLContext& gl, GLint location, GLsizei count, 163 bool transpose, const void* any) { 164 gl.fUniformMatrix2x4fv(location, count, transpose, 165 static_cast<const float*>(any)); 166 } 167 168 void UniformAsMatrix3x2fv(gl::GLContext& gl, GLint location, GLsizei count, 169 bool transpose, const void* any) { 170 gl.fUniformMatrix3x2fv(location, count, transpose, 171 static_cast<const float*>(any)); 172 } 173 void UniformAsMatrix3x3fv(gl::GLContext& gl, GLint location, GLsizei count, 174 bool transpose, const void* any) { 175 gl.fUniformMatrix3fv(location, count, transpose, 176 static_cast<const float*>(any)); 177 } 178 void UniformAsMatrix3x4fv(gl::GLContext& gl, GLint location, GLsizei count, 179 bool transpose, const void* any) { 180 gl.fUniformMatrix3x4fv(location, count, transpose, 181 static_cast<const float*>(any)); 182 } 183 184 void UniformAsMatrix4x2fv(gl::GLContext& gl, GLint location, GLsizei count, 185 bool transpose, const void* any) { 186 gl.fUniformMatrix4x2fv(location, count, transpose, 187 static_cast<const float*>(any)); 188 } 189 void UniformAsMatrix4x3fv(gl::GLContext& gl, GLint location, GLsizei count, 190 bool transpose, const void* any) { 191 gl.fUniformMatrix4x3fv(location, count, transpose, 192 static_cast<const float*>(any)); 193 } 194 void UniformAsMatrix4x4fv(gl::GLContext& gl, GLint location, GLsizei count, 195 bool transpose, const void* any) { 196 gl.fUniformMatrix4fv(location, count, transpose, 197 static_cast<const float*>(any)); 198 } 199 200 // - 201 202 static bool EndsWith(const std::string& str, const std::string& needle) { 203 if (str.length() < needle.length()) return false; 204 return str.compare(str.length() - needle.length(), needle.length(), needle) == 205 0; 206 } 207 208 webgl::ActiveUniformValidationInfo webgl::ActiveUniformValidationInfo::Make( 209 const webgl::ActiveUniformInfo& info) { 210 auto ret = webgl::ActiveUniformValidationInfo{info}; 211 ret.isArray = EndsWith(info.name, "[0]"); 212 213 switch (info.elemType) { 214 case LOCAL_GL_FLOAT: 215 ret.channelsPerElem = 1; 216 ret.pfn = &UniformAs1fv; 217 break; 218 case LOCAL_GL_FLOAT_VEC2: 219 ret.channelsPerElem = 2; 220 ret.pfn = &UniformAs2fv; 221 break; 222 case LOCAL_GL_FLOAT_VEC3: 223 ret.channelsPerElem = 3; 224 ret.pfn = &UniformAs3fv; 225 break; 226 case LOCAL_GL_FLOAT_VEC4: 227 ret.channelsPerElem = 4; 228 ret.pfn = &UniformAs4fv; 229 break; 230 231 case LOCAL_GL_SAMPLER_2D: 232 case LOCAL_GL_SAMPLER_3D: 233 case LOCAL_GL_SAMPLER_CUBE: 234 case LOCAL_GL_SAMPLER_2D_SHADOW: 235 case LOCAL_GL_SAMPLER_2D_ARRAY: 236 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: 237 case LOCAL_GL_SAMPLER_CUBE_SHADOW: 238 case LOCAL_GL_INT_SAMPLER_2D: 239 case LOCAL_GL_INT_SAMPLER_3D: 240 case LOCAL_GL_INT_SAMPLER_CUBE: 241 case LOCAL_GL_INT_SAMPLER_2D_ARRAY: 242 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: 243 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: 244 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: 245 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: 246 case LOCAL_GL_BOOL: 247 case LOCAL_GL_INT: 248 ret.channelsPerElem = 1; 249 ret.pfn = &UniformAs1iv; 250 break; 251 case LOCAL_GL_BOOL_VEC2: 252 case LOCAL_GL_INT_VEC2: 253 ret.channelsPerElem = 2; 254 ret.pfn = &UniformAs2iv; 255 break; 256 case LOCAL_GL_BOOL_VEC3: 257 case LOCAL_GL_INT_VEC3: 258 ret.channelsPerElem = 3; 259 ret.pfn = &UniformAs3iv; 260 break; 261 case LOCAL_GL_BOOL_VEC4: 262 case LOCAL_GL_INT_VEC4: 263 ret.channelsPerElem = 4; 264 ret.pfn = &UniformAs4iv; 265 break; 266 267 case LOCAL_GL_UNSIGNED_INT: 268 ret.channelsPerElem = 1; 269 ret.pfn = &UniformAs1uiv; 270 break; 271 case LOCAL_GL_UNSIGNED_INT_VEC2: 272 ret.channelsPerElem = 2; 273 ret.pfn = &UniformAs2uiv; 274 break; 275 case LOCAL_GL_UNSIGNED_INT_VEC3: 276 ret.channelsPerElem = 3; 277 ret.pfn = &UniformAs3uiv; 278 break; 279 case LOCAL_GL_UNSIGNED_INT_VEC4: 280 ret.channelsPerElem = 4; 281 ret.pfn = &UniformAs4uiv; 282 break; 283 284 // - 285 286 case LOCAL_GL_FLOAT_MAT2: 287 ret.channelsPerElem = 2 * 2; 288 ret.pfn = &UniformAsMatrix2x2fv; 289 break; 290 case LOCAL_GL_FLOAT_MAT2x3: 291 ret.channelsPerElem = 2 * 3; 292 ret.pfn = &UniformAsMatrix2x3fv; 293 break; 294 case LOCAL_GL_FLOAT_MAT2x4: 295 ret.channelsPerElem = 2 * 4; 296 ret.pfn = &UniformAsMatrix2x4fv; 297 break; 298 299 case LOCAL_GL_FLOAT_MAT3x2: 300 ret.channelsPerElem = 3 * 2; 301 ret.pfn = &UniformAsMatrix3x2fv; 302 break; 303 case LOCAL_GL_FLOAT_MAT3: 304 ret.channelsPerElem = 3 * 3; 305 ret.pfn = &UniformAsMatrix3x3fv; 306 break; 307 case LOCAL_GL_FLOAT_MAT3x4: 308 ret.channelsPerElem = 3 * 4; 309 ret.pfn = &UniformAsMatrix3x4fv; 310 break; 311 312 case LOCAL_GL_FLOAT_MAT4x2: 313 ret.channelsPerElem = 4 * 2; 314 ret.pfn = &UniformAsMatrix4x2fv; 315 break; 316 case LOCAL_GL_FLOAT_MAT4x3: 317 ret.channelsPerElem = 4 * 3; 318 ret.pfn = &UniformAsMatrix4x3fv; 319 break; 320 case LOCAL_GL_FLOAT_MAT4: 321 ret.channelsPerElem = 4 * 4; 322 ret.pfn = &UniformAsMatrix4x4fv; 323 break; 324 325 default: 326 gfxCriticalError() << "Bad `elemType`: " << EnumString(info.elemType); 327 MOZ_CRASH("`elemType`"); 328 } 329 return ret; 330 } 331 332 } // namespace webgl 333 334 // ------------------------- 335 336 // #define DUMP_SHADERVAR_MAPPINGS 337 338 RefPtr<const webgl::LinkedProgramInfo> QueryProgramInfo(WebGLProgram* prog, 339 gl::GLContext* gl) { 340 WebGLContext* const webgl = prog->mContext; 341 342 RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog)); 343 344 // Frag outputs 345 346 { 347 const auto& fragShader = prog->FragShader(); 348 const auto& compileResults = fragShader->CompileResults(); 349 const auto version = compileResults->mShaderVersion; 350 351 const auto fnAddInfo = [&](const webgl::FragOutputInfo& x) { 352 info->hasOutput[x.loc] = true; 353 info->fragOutputs.insert({x.loc, x}); 354 }; 355 356 if (version == 300) { 357 for (const auto& cur : compileResults->mOutputVariables) { 358 auto loc = cur.location; 359 if (loc == -1) loc = 0; 360 361 const auto info = 362 webgl::FragOutputInfo{uint8_t(loc), cur.name, cur.mappedName, 363 FragOutputBaseType(cur.type)}; 364 if (!cur.isArray()) { 365 fnAddInfo(info); 366 continue; 367 } 368 MOZ_ASSERT(cur.arraySizes.size() == 1); 369 for (uint32_t i = 0; i < cur.arraySizes[0]; ++i) { 370 const auto indexStr = std::string("[") + std::to_string(i) + "]"; 371 372 const auto userName = info.userName + indexStr; 373 const auto mappedName = info.mappedName + indexStr; 374 375 const auto indexedInfo = webgl::FragOutputInfo{ 376 uint8_t(info.loc + i), userName, mappedName, info.baseType}; 377 fnAddInfo(indexedInfo); 378 } 379 } 380 } else { 381 // ANGLE's translator doesn't tell us about non-user frag outputs. :( 382 383 const auto& translatedSource = compileResults->mObjectCode; 384 uint32_t drawBuffers = 1; 385 if (translatedSource.find("(gl_FragData[1]") != std::string::npos || 386 translatedSource.find("(webgl_FragData[1]") != std::string::npos) { 387 // The matching with the leading '(' prevents cleverly-named user vars 388 // breaking this. Since ANGLE initializes all outputs, if this is an MRT 389 // shader, FragData[1] will be present. FragData[0] is valid for non-MRT 390 // shaders. 391 drawBuffers = webgl->GLMaxDrawBuffers(); 392 } else if (translatedSource.find("(gl_FragColor") == std::string::npos && 393 translatedSource.find("(webgl_FragColor") == 394 std::string::npos && 395 translatedSource.find("(gl_FragData") == std::string::npos && 396 translatedSource.find("(webgl_FragData") == 397 std::string::npos) { 398 // We have to support no-color-output shaders? 399 drawBuffers = 0; 400 } 401 402 for (uint32_t i = 0; i < drawBuffers; ++i) { 403 const auto name = std::string("gl_FragData[") + std::to_string(i) + "]"; 404 const auto info = webgl::FragOutputInfo{uint8_t(i), name, name, 405 webgl::TextureBaseType::Float}; 406 fnAddInfo(info); 407 } 408 } 409 } 410 411 const auto& vertShader = prog->VertShader(); 412 const auto& vertCompileResults = vertShader->CompileResults(); 413 const auto numViews = vertCompileResults->mVertexShaderNumViews; 414 if (numViews != -1) { 415 info->zLayerCount = AssertedCast<uint8_t>(numViews); 416 } 417 418 // - 419 420 auto& nameMap = info->nameMap; 421 422 const auto fnAccum = [&](WebGLShader& shader) { 423 const auto& compRes = shader.CompileResults(); 424 for (const auto& pair : compRes->mNameMap) { 425 nameMap.insert(pair); 426 } 427 }; 428 fnAccum(*prog->VertShader()); 429 fnAccum(*prog->FragShader()); 430 431 // - 432 433 std::unordered_map<std::string, std::string> nameUnmap; 434 for (const auto& pair : nameMap) { 435 nameUnmap.insert({pair.second, pair.first}); 436 } 437 438 info->active = 439 GetLinkActiveInfo(*gl, prog->mGLName, webgl->IsWebGL2(), nameUnmap); 440 441 // - 442 443 for (const auto& attrib : info->active.activeAttribs) { 444 if (attrib.location == 0) { 445 info->attrib0Active = true; 446 break; 447 } 448 } 449 450 info->webgl_gl_VertexID_Offset = 451 gl->fGetUniformLocation(prog->mGLName, "webgl_gl_VertexID_Offset"); 452 453 // - 454 455 for (const auto& uniform : info->active.activeUniforms) { 456 const auto& elemType = uniform.elemType; 457 webgl::SamplerUniformInfo* samplerInfo = nullptr; 458 const auto baseType = SamplerBaseType(elemType); 459 if (baseType) { 460 const bool isShadowSampler = IsShadowSampler(elemType); 461 462 auto* texList = &webgl->mBound2DTextures; 463 464 switch (elemType) { 465 case LOCAL_GL_SAMPLER_2D: 466 case LOCAL_GL_SAMPLER_2D_SHADOW: 467 case LOCAL_GL_INT_SAMPLER_2D: 468 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: 469 break; 470 471 case LOCAL_GL_SAMPLER_CUBE: 472 case LOCAL_GL_SAMPLER_CUBE_SHADOW: 473 case LOCAL_GL_INT_SAMPLER_CUBE: 474 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: 475 texList = &webgl->mBoundCubeMapTextures; 476 break; 477 478 case LOCAL_GL_SAMPLER_3D: 479 case LOCAL_GL_INT_SAMPLER_3D: 480 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: 481 texList = &webgl->mBound3DTextures; 482 break; 483 484 case LOCAL_GL_SAMPLER_2D_ARRAY: 485 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: 486 case LOCAL_GL_INT_SAMPLER_2D_ARRAY: 487 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: 488 texList = &webgl->mBound2DArrayTextures; 489 break; 490 } 491 492 auto curInfo = std::unique_ptr<webgl::SamplerUniformInfo>( 493 new webgl::SamplerUniformInfo{*texList, *baseType, isShadowSampler}); 494 MOZ_RELEASE_ASSERT(curInfo->texUnits.resize(uniform.elemCount)); 495 samplerInfo = curInfo.get(); 496 info->samplerUniforms.push_back(std::move(curInfo)); 497 } 498 499 const auto valInfo = webgl::ActiveUniformValidationInfo::Make(uniform); 500 501 for (const auto& pair : uniform.locByIndex) { 502 info->locationMap.insert( 503 {pair.second, {valInfo, pair.first, samplerInfo}}); 504 } 505 } 506 507 // - 508 509 { 510 const auto& activeBlocks = info->active.activeUniformBlocks; 511 info->uniformBlocks.reserve(activeBlocks.size()); 512 for (const auto& cur : activeBlocks) { 513 const auto curInfo = webgl::UniformBlockInfo{ 514 cur, &webgl->mIndexedUniformBufferBindings[0]}; 515 info->uniformBlocks.push_back(curInfo); 516 } 517 } 518 519 return info; 520 } 521 522 //////////////////////////////////////////////////////////////////////////////// 523 524 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog) 525 : prog(prog), 526 transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode) { 527 } 528 529 webgl::LinkedProgramInfo::~LinkedProgramInfo() = default; 530 531 webgl::AttribBaseType webgl::ToAttribBaseType(const GLenum elemType) { 532 switch (elemType) { 533 case LOCAL_GL_BOOL: 534 case LOCAL_GL_BOOL_VEC2: 535 case LOCAL_GL_BOOL_VEC3: 536 case LOCAL_GL_BOOL_VEC4: 537 return webgl::AttribBaseType::Boolean; 538 539 case LOCAL_GL_FLOAT: 540 case LOCAL_GL_FLOAT_VEC2: 541 case LOCAL_GL_FLOAT_VEC3: 542 case LOCAL_GL_FLOAT_VEC4: 543 case LOCAL_GL_FLOAT_MAT2: 544 case LOCAL_GL_FLOAT_MAT2x3: 545 case LOCAL_GL_FLOAT_MAT3x2: 546 case LOCAL_GL_FLOAT_MAT2x4: 547 case LOCAL_GL_FLOAT_MAT4x2: 548 case LOCAL_GL_FLOAT_MAT3: 549 case LOCAL_GL_FLOAT_MAT3x4: 550 case LOCAL_GL_FLOAT_MAT4x3: 551 case LOCAL_GL_FLOAT_MAT4: 552 return webgl::AttribBaseType::Float; 553 554 case LOCAL_GL_INT: 555 case LOCAL_GL_INT_VEC2: 556 case LOCAL_GL_INT_VEC3: 557 case LOCAL_GL_INT_VEC4: 558 case LOCAL_GL_SAMPLER_2D: 559 case LOCAL_GL_SAMPLER_3D: 560 case LOCAL_GL_SAMPLER_CUBE: 561 case LOCAL_GL_SAMPLER_2D_SHADOW: 562 case LOCAL_GL_SAMPLER_2D_ARRAY: 563 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: 564 case LOCAL_GL_SAMPLER_CUBE_SHADOW: 565 case LOCAL_GL_INT_SAMPLER_2D: 566 case LOCAL_GL_INT_SAMPLER_3D: 567 case LOCAL_GL_INT_SAMPLER_CUBE: 568 case LOCAL_GL_INT_SAMPLER_2D_ARRAY: 569 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: 570 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: 571 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: 572 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: 573 return webgl::AttribBaseType::Int; 574 575 case LOCAL_GL_UNSIGNED_INT: 576 case LOCAL_GL_UNSIGNED_INT_VEC2: 577 case LOCAL_GL_UNSIGNED_INT_VEC3: 578 case LOCAL_GL_UNSIGNED_INT_VEC4: 579 return webgl::AttribBaseType::Uint; 580 581 default: 582 gfxCriticalError() << "Bad `elemType`: " << EnumString(elemType); 583 MOZ_CRASH("`elemType`"); 584 } 585 } 586 587 const char* webgl::ToString(const webgl::AttribBaseType x) { 588 switch (x) { 589 case webgl::AttribBaseType::Float: 590 return "FLOAT"; 591 case webgl::AttribBaseType::Int: 592 return "INT"; 593 case webgl::AttribBaseType::Uint: 594 return "UINT"; 595 case webgl::AttribBaseType::Boolean: 596 return "BOOL"; 597 } 598 MOZ_CRASH("pacify gcc6 warning"); 599 } 600 601 const char* webgl::ToString(const webgl::UniformBaseType x) { 602 switch (x) { 603 case webgl::UniformBaseType::Float: 604 return "FLOAT"; 605 case webgl::UniformBaseType::Int: 606 return "INT"; 607 case webgl::UniformBaseType::Uint: 608 return "UINT"; 609 } 610 MOZ_CRASH("pacify gcc6 warning"); 611 } 612 613 const webgl::CachedDrawFetchLimits* 614 webgl::LinkedProgramInfo::GetDrawFetchLimits() const { 615 const auto& webgl = prog->mContext; 616 const auto& vao = webgl->mBoundVertexArray; 617 618 { 619 // We have to ensure that every enabled attrib array (not just the active 620 // ones) has a non-null buffer. 621 const auto badIndex = vao->GetAttribIsArrayWithNullBuffer(); 622 if (badIndex) { 623 webgl->ErrorInvalidOperation( 624 "Vertex attrib array %u is enabled but" 625 " has no buffer bound.", 626 *badIndex); 627 return nullptr; 628 } 629 } 630 631 const auto& activeAttribs = active.activeAttribs; 632 633 webgl::CachedDrawFetchLimits fetchLimits; 634 fetchLimits.usedBuffers = 635 std::move(mScratchFetchLimits.usedBuffers); // Avoid realloc. 636 fetchLimits.usedBuffers.clear(); 637 fetchLimits.usedBuffers.reserve(activeAttribs.size()); 638 639 bool hasActiveAttrib = false; 640 bool hasActiveDivisor0 = false; 641 642 for (const auto& progAttrib : activeAttribs) { 643 const auto& loc = progAttrib.location; 644 if (loc == -1) continue; 645 hasActiveAttrib |= true; 646 647 const auto& binding = vao->AttribBinding(loc); 648 const auto& buffer = binding.buffer; 649 const auto& layout = binding.layout; 650 hasActiveDivisor0 |= (layout.divisor == 0); 651 652 webgl::AttribBaseType attribDataBaseType; 653 if (layout.isArray) { 654 MOZ_ASSERT(buffer); 655 fetchLimits.usedBuffers.push_back( 656 {buffer.get(), static_cast<uint32_t>(loc)}); 657 658 attribDataBaseType = layout.baseType; 659 660 const auto availBytes = buffer->ByteLength(); 661 const auto availElems = AvailGroups(availBytes, layout.byteOffset, 662 layout.byteSize, layout.byteStride); 663 if (layout.divisor) { 664 const auto availInstances = 665 CheckedInt<uint64_t>(availElems) * layout.divisor; 666 if (availInstances.isValid()) { 667 fetchLimits.maxInstances = 668 std::min(fetchLimits.maxInstances, availInstances.value()); 669 } // If not valid, it overflowed too large, so we're super safe. 670 } else { 671 fetchLimits.maxVerts = std::min(fetchLimits.maxVerts, availElems); 672 } 673 } else { 674 attribDataBaseType = webgl->mGenericVertexAttribTypes[loc]; 675 } 676 677 const auto& progBaseType = progAttrib.baseType; 678 if ((attribDataBaseType != progBaseType) && 679 (progBaseType != webgl::AttribBaseType::Boolean)) { 680 const auto& dataType = ToString(attribDataBaseType); 681 const auto& progType = ToString(progBaseType); 682 webgl->ErrorInvalidOperation( 683 "Vertex attrib %u requires data of type %s," 684 " but is being supplied with type %s.", 685 loc, progType, dataType); 686 return nullptr; 687 } 688 } 689 690 if (!webgl->IsWebGL2() && hasActiveAttrib && !hasActiveDivisor0) { 691 webgl->ErrorInvalidOperation( 692 "One active vertex attrib (if any are active)" 693 " must have a divisor of 0."); 694 return nullptr; 695 } 696 697 // - 698 699 mScratchFetchLimits = std::move(fetchLimits); 700 return &mScratchFetchLimits; 701 } 702 703 //////////////////////////////////////////////////////////////////////////////// 704 // WebGLProgram 705 706 WebGLProgram::WebGLProgram(WebGLContext* webgl) 707 : WebGLContextBoundObject(webgl), 708 mGLName(webgl->gl->fCreateProgram()), 709 mNumActiveTFOs(0), 710 mNextLink_TransformFeedbackBufferMode(LOCAL_GL_INTERLEAVED_ATTRIBS) {} 711 712 WebGLProgram::~WebGLProgram() { 713 mVertShader = nullptr; 714 mFragShader = nullptr; 715 716 mMostRecentLinkInfo = nullptr; 717 718 if (!mContext) return; 719 mContext->gl->fDeleteProgram(mGLName); 720 } 721 722 //////////////////////////////////////////////////////////////////////////////// 723 // GL funcs 724 725 void WebGLProgram::AttachShader(WebGLShader& shader) { 726 RefPtr<WebGLShader>* shaderSlot = nullptr; 727 switch (shader.mType) { 728 case LOCAL_GL_VERTEX_SHADER: 729 shaderSlot = &mVertShader; 730 break; 731 case LOCAL_GL_FRAGMENT_SHADER: 732 shaderSlot = &mFragShader; 733 break; 734 } 735 MOZ_ASSERT(shaderSlot); 736 737 *shaderSlot = &shader; 738 739 mContext->gl->fAttachShader(mGLName, shader.mGLName); 740 } 741 742 void WebGLProgram::BindAttribLocation(GLuint loc, const std::string& name) { 743 const auto err = CheckGLSLVariableName(mContext->IsWebGL2(), name); 744 if (err) { 745 mContext->GenerateError(err->type, "%s", err->info.c_str()); 746 return; 747 } 748 749 if (loc >= mContext->MaxVertexAttribs()) { 750 mContext->ErrorInvalidValue( 751 "`location` must be less than" 752 " MAX_VERTEX_ATTRIBS."); 753 return; 754 } 755 756 if (name.find("gl_") == 0) { 757 mContext->ErrorInvalidOperation( 758 "Can't set the location of a" 759 " name that starts with 'gl_'."); 760 return; 761 } 762 763 auto res = mNextLink_BoundAttribLocs.insert({name, loc}); 764 765 const auto& wasInserted = res.second; 766 if (!wasInserted) { 767 const auto& itr = res.first; 768 itr->second = loc; 769 } 770 } 771 772 void WebGLProgram::DetachShader(const WebGLShader& shader) { 773 RefPtr<WebGLShader>* shaderSlot = nullptr; 774 switch (shader.mType) { 775 case LOCAL_GL_VERTEX_SHADER: 776 shaderSlot = &mVertShader; 777 break; 778 case LOCAL_GL_FRAGMENT_SHADER: 779 shaderSlot = &mFragShader; 780 break; 781 } 782 MOZ_ASSERT(shaderSlot); 783 784 if (*shaderSlot != &shader) return; 785 786 *shaderSlot = nullptr; 787 788 mContext->gl->fDetachShader(mGLName, shader.mGLName); 789 } 790 791 void WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex, 792 GLuint uniformBlockBinding) const { 793 if (!IsLinked()) { 794 mContext->ErrorInvalidOperation("`program` must be linked."); 795 return; 796 } 797 798 auto& uniformBlocks = LinkInfo()->uniformBlocks; 799 if (uniformBlockIndex >= uniformBlocks.size()) { 800 mContext->ErrorInvalidValue("Index %u invalid.", uniformBlockIndex); 801 return; 802 } 803 auto& uniformBlock = uniformBlocks[uniformBlockIndex]; 804 805 const auto& indexedBindings = mContext->mIndexedUniformBufferBindings; 806 if (uniformBlockBinding >= indexedBindings.size()) { 807 mContext->ErrorInvalidValue("Binding %u invalid.", uniformBlockBinding); 808 return; 809 } 810 const auto& indexedBinding = indexedBindings[uniformBlockBinding]; 811 812 //// 813 814 gl::GLContext* gl = mContext->GL(); 815 gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding); 816 817 //// 818 819 uniformBlock.binding = &indexedBinding; 820 } 821 822 bool WebGLProgram::ValidateForLink() { 823 const auto AppendCompileLog = [&](const WebGLShader* const shader) { 824 if (!shader) { 825 mLinkLog += " Missing shader."; 826 return; 827 } 828 mLinkLog += "\nSHADER_INFO_LOG:\n"; 829 mLinkLog += shader->CompileLog(); 830 }; 831 832 if (!mVertShader || !mVertShader->IsCompiled()) { 833 mLinkLog = "Must have a compiled vertex shader attached:"; 834 AppendCompileLog(mVertShader); 835 return false; 836 } 837 const auto& vertInfo = *mVertShader->CompileResults(); 838 839 if (!mFragShader || !mFragShader->IsCompiled()) { 840 mLinkLog = "Must have a compiled fragment shader attached:"; 841 AppendCompileLog(mFragShader); 842 return false; 843 } 844 const auto& fragInfo = *mFragShader->CompileResults(); 845 846 nsCString errInfo; 847 if (!fragInfo.CanLinkTo(vertInfo, &errInfo)) { 848 mLinkLog = errInfo.BeginReading(); 849 return false; 850 } 851 852 const auto& gl = mContext->gl; 853 854 if (gl->WorkAroundDriverBugs() && mContext->mIsMesa) { 855 // Bug 1203135: Mesa crashes internally if we exceed the reported maximum 856 // attribute count. 857 if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) { 858 mLinkLog = 859 "Number of attributes exceeds Mesa's reported max" 860 " attribute count."; 861 return false; 862 } 863 } 864 865 return true; 866 } 867 868 void WebGLProgram::LinkProgram() { 869 if (mNumActiveTFOs) { 870 mContext->ErrorInvalidOperation( 871 "Program is in-use by one or more active" 872 " transform feedback objects."); 873 return; 874 } 875 876 // as some of the validation changes program state 877 878 mLinkLog = {}; 879 mMostRecentLinkInfo = nullptr; 880 881 if (!ValidateForLink()) { 882 mContext->GenerateWarning("%s", mLinkLog.c_str()); 883 return; 884 } 885 886 // Bind the attrib locations. 887 // This can't be done trivially, because we have to deal with mapped attrib 888 // names. 889 for (const auto& pair : mNextLink_BoundAttribLocs) { 890 const auto& name = pair.first; 891 const auto& index = pair.second; 892 893 mVertShader->BindAttribLocation(mGLName, name, index); 894 } 895 896 // Storage for transform feedback varyings before link. 897 // (Work around for bug seen on nVidia drivers.) 898 std::vector<std::string> scopedMappedTFVaryings; 899 900 if (mContext->IsWebGL2()) { 901 mVertShader->MapTransformFeedbackVaryings( 902 mNextLink_TransformFeedbackVaryings, &scopedMappedTFVaryings); 903 904 std::vector<const char*> driverVaryings; 905 driverVaryings.reserve(scopedMappedTFVaryings.size()); 906 for (const auto& cur : scopedMappedTFVaryings) { 907 driverVaryings.push_back(cur.c_str()); 908 } 909 910 mContext->gl->fTransformFeedbackVaryings( 911 mGLName, driverVaryings.size(), driverVaryings.data(), 912 mNextLink_TransformFeedbackBufferMode); 913 } 914 915 LinkAndUpdate(); 916 917 if (mMostRecentLinkInfo) { 918 std::string postLinkLog; 919 if (ValidateAfterTentativeLink(&postLinkLog)) return; 920 921 mMostRecentLinkInfo = nullptr; 922 mLinkLog = std::move(postLinkLog); 923 } 924 925 // Failed link. 926 if (mContext->ShouldGenerateWarnings()) { 927 // report shader/program infoLogs as warnings. 928 // note that shader compilation errors can be deferred to linkProgram, 929 // which is why we can't do anything in compileShader. In practice we could 930 // report in compileShader the translation errors generated by ANGLE, 931 // but it seems saner to keep a single way of obtaining shader infologs. 932 if (!mLinkLog.empty()) { 933 mContext->GenerateWarning( 934 "Failed to link, leaving the following" 935 " log:\n%s\n", 936 mLinkLog.c_str()); 937 } 938 } 939 } 940 941 static uint8_t NumUsedLocationsByElemType(GLenum elemType) { 942 // GLES 3.0.4 p55 943 944 switch (elemType) { 945 case LOCAL_GL_FLOAT_MAT2: 946 case LOCAL_GL_FLOAT_MAT2x3: 947 case LOCAL_GL_FLOAT_MAT2x4: 948 return 2; 949 950 case LOCAL_GL_FLOAT_MAT3x2: 951 case LOCAL_GL_FLOAT_MAT3: 952 case LOCAL_GL_FLOAT_MAT3x4: 953 return 3; 954 955 case LOCAL_GL_FLOAT_MAT4x2: 956 case LOCAL_GL_FLOAT_MAT4x3: 957 case LOCAL_GL_FLOAT_MAT4: 958 return 4; 959 960 default: 961 return 1; 962 } 963 } 964 965 uint8_t ElemTypeComponents(const GLenum elemType) { 966 switch (elemType) { 967 case LOCAL_GL_BOOL: 968 case LOCAL_GL_FLOAT: 969 case LOCAL_GL_INT: 970 case LOCAL_GL_UNSIGNED_INT: 971 case LOCAL_GL_SAMPLER_2D: 972 case LOCAL_GL_SAMPLER_3D: 973 case LOCAL_GL_SAMPLER_CUBE: 974 case LOCAL_GL_SAMPLER_2D_SHADOW: 975 case LOCAL_GL_SAMPLER_2D_ARRAY: 976 case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW: 977 case LOCAL_GL_SAMPLER_CUBE_SHADOW: 978 case LOCAL_GL_INT_SAMPLER_2D: 979 case LOCAL_GL_INT_SAMPLER_3D: 980 case LOCAL_GL_INT_SAMPLER_CUBE: 981 case LOCAL_GL_INT_SAMPLER_2D_ARRAY: 982 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D: 983 case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D: 984 case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE: 985 case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: 986 return 1; 987 988 case LOCAL_GL_BOOL_VEC2: 989 case LOCAL_GL_FLOAT_VEC2: 990 case LOCAL_GL_INT_VEC2: 991 case LOCAL_GL_UNSIGNED_INT_VEC2: 992 return 2; 993 994 case LOCAL_GL_BOOL_VEC3: 995 case LOCAL_GL_FLOAT_VEC3: 996 case LOCAL_GL_INT_VEC3: 997 case LOCAL_GL_UNSIGNED_INT_VEC3: 998 return 3; 999 1000 case LOCAL_GL_BOOL_VEC4: 1001 case LOCAL_GL_FLOAT_VEC4: 1002 case LOCAL_GL_INT_VEC4: 1003 case LOCAL_GL_UNSIGNED_INT_VEC4: 1004 case LOCAL_GL_FLOAT_MAT2: 1005 return 4; 1006 1007 case LOCAL_GL_FLOAT_MAT2x3: 1008 case LOCAL_GL_FLOAT_MAT3x2: 1009 return 2 * 3; 1010 1011 case LOCAL_GL_FLOAT_MAT2x4: 1012 case LOCAL_GL_FLOAT_MAT4x2: 1013 return 2 * 4; 1014 1015 case LOCAL_GL_FLOAT_MAT3: 1016 return 3 * 3; 1017 1018 case LOCAL_GL_FLOAT_MAT3x4: 1019 case LOCAL_GL_FLOAT_MAT4x3: 1020 return 3 * 4; 1021 1022 case LOCAL_GL_FLOAT_MAT4: 1023 return 4 * 4; 1024 1025 default: 1026 return 0; 1027 } 1028 } 1029 1030 bool WebGLProgram::ValidateAfterTentativeLink( 1031 std::string* const out_linkLog) const { 1032 const auto& linkInfo = mMostRecentLinkInfo; 1033 const auto& gl = mContext->gl; 1034 1035 // Check for overlapping attrib locations. 1036 { 1037 std::unordered_map<uint32_t, const std::string&> nameByLoc; 1038 for (const auto& attrib : linkInfo->active.activeAttribs) { 1039 if (attrib.location == -1) continue; 1040 1041 const auto& elemType = attrib.elemType; 1042 const auto numUsedLocs = NumUsedLocationsByElemType(elemType); 1043 for (uint32_t i = 0; i < numUsedLocs; i++) { 1044 const uint32_t usedLoc = attrib.location + i; 1045 1046 const auto res = nameByLoc.insert({usedLoc, attrib.name}); 1047 const bool& didInsert = res.second; 1048 if (!didInsert) { 1049 const auto& aliasingName = attrib.name; 1050 const auto& itrExisting = res.first; 1051 const auto& existingName = itrExisting->second; 1052 *out_linkLog = nsPrintfCString( 1053 "Attrib \"%s\" aliases locations used by" 1054 " attrib \"%s\".", 1055 aliasingName.c_str(), existingName.c_str()) 1056 .BeginReading(); 1057 return false; 1058 } 1059 } 1060 } 1061 } 1062 1063 // Forbid too many components for specified buffer mode 1064 const auto& activeTfVaryings = linkInfo->active.activeTfVaryings; 1065 MOZ_ASSERT(mNextLink_TransformFeedbackVaryings.size() == 1066 activeTfVaryings.size()); 1067 if (!activeTfVaryings.empty()) { 1068 GLuint maxComponentsPerIndex = 0; 1069 switch (linkInfo->transformFeedbackBufferMode) { 1070 case LOCAL_GL_INTERLEAVED_ATTRIBS: 1071 gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, 1072 &maxComponentsPerIndex); 1073 break; 1074 1075 case LOCAL_GL_SEPARATE_ATTRIBS: 1076 gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, 1077 &maxComponentsPerIndex); 1078 break; 1079 1080 default: 1081 MOZ_CRASH("`bufferMode`"); 1082 } 1083 1084 std::vector<size_t> componentsPerVert; 1085 for (const auto& cur : activeTfVaryings) { 1086 if (componentsPerVert.empty() || 1087 linkInfo->transformFeedbackBufferMode == LOCAL_GL_SEPARATE_ATTRIBS) { 1088 componentsPerVert.push_back(0); 1089 } 1090 1091 size_t varyingComponents = ElemTypeComponents(cur.elemType); 1092 MOZ_ASSERT(varyingComponents); 1093 varyingComponents *= cur.elemCount; 1094 1095 auto& totalComponentsForIndex = *(componentsPerVert.rbegin()); 1096 totalComponentsForIndex += varyingComponents; 1097 1098 if (totalComponentsForIndex > maxComponentsPerIndex) { 1099 *out_linkLog = nsPrintfCString( 1100 "Transform feedback varying \"%s\"" 1101 " pushed `componentsForIndex` over the" 1102 " limit of %u.", 1103 cur.name.c_str(), maxComponentsPerIndex) 1104 .BeginReading(); 1105 return false; 1106 } 1107 } 1108 1109 linkInfo->componentsPerTFVert = std::move(componentsPerVert); 1110 } 1111 1112 return true; 1113 } 1114 1115 bool WebGLProgram::UseProgram() const { 1116 if (!mMostRecentLinkInfo) { 1117 mContext->ErrorInvalidOperation( 1118 "Program has not been successfully linked."); 1119 return false; 1120 } 1121 1122 if (mContext->mBoundTransformFeedback && 1123 mContext->mBoundTransformFeedback->mIsActive && 1124 !mContext->mBoundTransformFeedback->mIsPaused) { 1125 mContext->ErrorInvalidOperation( 1126 "Transform feedback active and not paused."); 1127 return false; 1128 } 1129 1130 mContext->gl->fUseProgram(mGLName); 1131 return true; 1132 } 1133 1134 bool WebGLProgram::ValidateProgram() const { 1135 gl::GLContext* gl = mContext->gl; 1136 1137 #ifdef XP_MACOSX 1138 // See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed 1139 // with Mac OS 10.6.7. 1140 if (gl->WorkAroundDriverBugs()) { 1141 mContext->GenerateWarning( 1142 "Implemented as a no-op on" 1143 " Mac to work around crashes."); 1144 return true; 1145 } 1146 #endif 1147 1148 gl->fValidateProgram(mGLName); 1149 GLint ok = 0; 1150 gl->fGetProgramiv(mGLName, LOCAL_GL_VALIDATE_STATUS, &ok); 1151 return bool(ok); 1152 } 1153 1154 //////////////////////////////////////////////////////////////////////////////// 1155 1156 void WebGLProgram::LinkAndUpdate() { 1157 mMostRecentLinkInfo = nullptr; 1158 1159 gl::GLContext* gl = mContext->gl; 1160 gl->fLinkProgram(mGLName); 1161 1162 // Grab the program log. 1163 { 1164 GLuint logLenWithNull = 0; 1165 gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH, 1166 (GLint*)&logLenWithNull); 1167 if (logLenWithNull > 1) { 1168 std::vector<char> buffer(logLenWithNull); 1169 gl->fGetProgramInfoLog(mGLName, buffer.size(), nullptr, buffer.data()); 1170 mLinkLog = buffer.data(); 1171 } else { 1172 mLinkLog.clear(); 1173 } 1174 } 1175 1176 GLint ok = 0; 1177 gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok); 1178 if (!ok) return; 1179 1180 mMostRecentLinkInfo = 1181 QueryProgramInfo(this, gl); // Fallible after context loss. 1182 } 1183 1184 void WebGLProgram::TransformFeedbackVaryings( 1185 const std::vector<std::string>& varyings, GLenum bufferMode) { 1186 const auto& gl = mContext->gl; 1187 1188 switch (bufferMode) { 1189 case LOCAL_GL_INTERLEAVED_ATTRIBS: 1190 break; 1191 1192 case LOCAL_GL_SEPARATE_ATTRIBS: { 1193 GLuint maxAttribs = 0; 1194 gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, 1195 &maxAttribs); 1196 if (varyings.size() > maxAttribs) { 1197 mContext->ErrorInvalidValue("Length of `varyings` exceeds %s.", 1198 "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS"); 1199 return; 1200 } 1201 } break; 1202 1203 default: 1204 mContext->ErrorInvalidEnumInfo("bufferMode", bufferMode); 1205 return; 1206 } 1207 1208 //// 1209 1210 mNextLink_TransformFeedbackVaryings = varyings; 1211 mNextLink_TransformFeedbackBufferMode = bufferMode; 1212 } 1213 1214 } // namespace mozilla