WebGLContextDraw.cpp (43334B)
1 /* -*- Mode: C++; tab-width: 4; 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 <algorithm> 7 8 #include "GLContext.h" 9 #include "MozFramebuffer.h" 10 #include "WebGLBuffer.h" 11 #include "WebGLContext.h" 12 #include "WebGLContextUtils.h" 13 #include "WebGLFormats.h" 14 #include "WebGLFramebuffer.h" 15 #include "WebGLProgram.h" 16 #include "WebGLRenderbuffer.h" 17 #include "WebGLShader.h" 18 #include "WebGLTexture.h" 19 #include "WebGLTransformFeedback.h" 20 #include "WebGLVertexArray.h" 21 #include "mozilla/CheckedInt.h" 22 #include "mozilla/ProfilerLabels.h" 23 #include "mozilla/ScopeExit.h" 24 #include "mozilla/StaticPrefs_webgl.h" 25 #include "nsPrintfCString.h" 26 27 namespace mozilla { 28 29 // For a Tegra workaround. 30 static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100; 31 32 //////////////////////////////////////// 33 34 class ScopedResolveTexturesForDraw { 35 struct TexRebindRequest { 36 uint32_t texUnit; 37 WebGLTexture* tex; 38 }; 39 40 WebGLContext* const mWebGL; 41 std::vector<TexRebindRequest> mRebindRequests; 42 43 public: 44 ScopedResolveTexturesForDraw(WebGLContext* webgl, bool* const out_error); 45 ~ScopedResolveTexturesForDraw(); 46 }; 47 48 static bool ValidateNoSamplingFeedback(const WebGLTexture& tex, 49 const uint32_t sampledLevels, 50 const WebGLFramebuffer* const fb, 51 const uint32_t texUnit) { 52 if (!fb) return true; 53 54 const auto& texAttachments = fb->GetCompletenessInfo()->texAttachments; 55 for (const auto& attach : texAttachments) { 56 if (attach->Texture() != &tex) continue; 57 58 const auto& srcBase = tex.Es3_level_base(); 59 const auto srcLast = srcBase + sampledLevels - 1; 60 const auto& dstLevel = attach->MipLevel(); 61 if (MOZ_UNLIKELY(srcBase <= dstLevel && dstLevel <= srcLast)) { 62 const auto& webgl = tex.mContext; 63 const auto& texTargetStr = EnumString(tex.Target().get()); 64 const auto& attachStr = EnumString(attach->mAttachmentPoint); 65 webgl->ErrorInvalidOperation( 66 "Texture level %u would be read by %s unit %u," 67 " but written by framebuffer attachment %s," 68 " which would be illegal feedback.", 69 dstLevel, texTargetStr.c_str(), texUnit, attachStr.c_str()); 70 return false; 71 } 72 } 73 return true; 74 } 75 76 ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw( 77 WebGLContext* webgl, bool* const out_error) 78 : mWebGL(webgl) { 79 const auto& fb = mWebGL->mBoundDrawFramebuffer; 80 81 struct SamplerByTexUnit { 82 uint8_t texUnit; 83 const webgl::SamplerUniformInfo* sampler; 84 }; 85 Vector<SamplerByTexUnit, 8> samplerByTexUnit; 86 87 MOZ_ASSERT(mWebGL->mActiveProgramLinkInfo); 88 const auto& samplerUniforms = mWebGL->mActiveProgramLinkInfo->samplerUniforms; 89 for (const auto& pUniform : samplerUniforms) { 90 const auto& uniform = *pUniform; 91 const auto& texList = uniform.texListForType; 92 93 const auto& uniformBaseType = uniform.texBaseType; 94 for (const auto& texUnit : uniform.texUnits) { 95 MOZ_ASSERT(texUnit < texList.Length()); 96 97 { 98 decltype(SamplerByTexUnit::sampler) prevSamplerForTexUnit = nullptr; 99 for (const auto& cur : samplerByTexUnit) { 100 if (cur.texUnit == texUnit) { 101 prevSamplerForTexUnit = cur.sampler; 102 } 103 } 104 if (!prevSamplerForTexUnit) { 105 prevSamplerForTexUnit = &uniform; 106 MOZ_RELEASE_ASSERT(samplerByTexUnit.append( 107 SamplerByTexUnit{texUnit, prevSamplerForTexUnit})); 108 } 109 110 if (MOZ_UNLIKELY(&uniform.texListForType != 111 &prevSamplerForTexUnit->texListForType)) { 112 // Pointing to different tex lists means different types! 113 const auto linkInfo = mWebGL->mActiveProgramLinkInfo; 114 const auto LocInfoBySampler = [&](const webgl::SamplerUniformInfo* p) 115 -> const webgl::LocationInfo* { 116 for (const auto& pair : linkInfo->locationMap) { 117 const auto& locInfo = pair.second; 118 if (locInfo.samplerInfo == p) { 119 return &locInfo; 120 } 121 } 122 MOZ_CRASH("Can't find sampler location."); 123 }; 124 const auto& cur = *LocInfoBySampler(&uniform); 125 const auto& prev = *LocInfoBySampler(prevSamplerForTexUnit); 126 mWebGL->ErrorInvalidOperation( 127 "Tex unit %u referenced by samplers of different types:" 128 " %s (via %s) and %s (via %s).", 129 texUnit, EnumString(cur.info.info.elemType).c_str(), 130 cur.PrettyName().c_str(), 131 EnumString(prev.info.info.elemType).c_str(), 132 prev.PrettyName().c_str()); 133 *out_error = true; 134 return; 135 } 136 } 137 138 const auto& tex = texList[texUnit]; 139 if (!tex) continue; 140 141 const auto& sampler = mWebGL->mBoundSamplers[texUnit]; 142 const auto& samplingInfo = tex->GetSampleableInfo(sampler.get()); 143 if (MOZ_UNLIKELY(!samplingInfo)) { // There was an error. 144 *out_error = true; 145 return; 146 } 147 if (MOZ_UNLIKELY(!samplingInfo->IsComplete())) { 148 if (samplingInfo->incompleteReason) { 149 const auto& targetName = GetEnumName(tex->Target().get()); 150 mWebGL->GenerateWarning("%s at unit %u is incomplete: %s", targetName, 151 texUnit, samplingInfo->incompleteReason); 152 } 153 mRebindRequests.push_back({texUnit, tex}); 154 continue; 155 } 156 157 // We have more validation to do if we're otherwise complete: 158 const auto& texBaseType = samplingInfo->usage->format->baseType; 159 if (MOZ_UNLIKELY(texBaseType != uniformBaseType)) { 160 const auto& targetName = GetEnumName(tex->Target().get()); 161 const auto& srcType = ToString(texBaseType); 162 const auto& dstType = ToString(uniformBaseType); 163 mWebGL->ErrorInvalidOperation( 164 "%s at unit %u is of type %s, but" 165 " the shader samples as %s.", 166 targetName, texUnit, srcType, dstType); 167 *out_error = true; 168 return; 169 } 170 171 if (MOZ_UNLIKELY(uniform.isShadowSampler != 172 samplingInfo->isDepthTexCompare)) { 173 const auto& targetName = GetEnumName(tex->Target().get()); 174 mWebGL->ErrorInvalidOperation( 175 "%s at unit %u is%s a depth texture" 176 " with TEXTURE_COMPARE_MODE, but" 177 " the shader sampler is%s a shadow" 178 " sampler.", 179 targetName, texUnit, samplingInfo->isDepthTexCompare ? "" : " not", 180 uniform.isShadowSampler ? "" : " not"); 181 *out_error = true; 182 return; 183 } 184 185 if (MOZ_UNLIKELY(!ValidateNoSamplingFeedback(*tex, samplingInfo->levels, 186 fb.get(), texUnit))) { 187 *out_error = true; 188 return; 189 } 190 } 191 } 192 193 const auto& gl = mWebGL->gl; 194 for (const auto& itr : mRebindRequests) { 195 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + itr.texUnit); 196 GLuint incompleteTex = 0; // Tex 0 is always incomplete. 197 const auto& overrideTex = webgl->mIncompleteTexOverride; 198 if (overrideTex) { 199 // In all but the simplest cases, this will be incomplete anyway, since 200 // e.g. int-samplers need int-textures. This is useful for e.g. 201 // dom-to-texture failures, though. 202 incompleteTex = overrideTex->name; 203 } 204 gl->fBindTexture(itr.tex->Target().get(), incompleteTex); 205 } 206 } 207 208 ScopedResolveTexturesForDraw::~ScopedResolveTexturesForDraw() { 209 if (mRebindRequests.empty()) return; 210 211 gl::GLContext* gl = mWebGL->gl; 212 213 for (const auto& itr : mRebindRequests) { 214 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + itr.texUnit); 215 gl->fBindTexture(itr.tex->Target().get(), itr.tex->mGLName); 216 } 217 218 gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mWebGL->mActiveTexture); 219 } 220 221 //////////////////////////////////////// 222 223 bool WebGLContext::ValidateStencilParamsForDrawCall() const { 224 const auto stencilBits = [&]() -> uint8_t { 225 if (!mStencilTestEnabled) return 0; 226 227 if (!mBoundDrawFramebuffer) return mOptions.stencil ? 8 : 0; 228 229 if (mBoundDrawFramebuffer->StencilAttachment().HasAttachment()) return 8; 230 231 if (mBoundDrawFramebuffer->DepthStencilAttachment().HasAttachment()) 232 return 8; 233 234 return 0; 235 }(); 236 const uint32_t stencilMax = (1 << stencilBits) - 1; 237 238 const auto fnMask = [&](const uint32_t x) { return x & stencilMax; }; 239 const auto fnClamp = [&](const int32_t x) { 240 return std::clamp(x, 0, (int32_t)stencilMax); 241 }; 242 243 bool ok = true; 244 ok &= (fnMask(mStencilWriteMaskFront) == fnMask(mStencilWriteMaskBack)); 245 ok &= (fnMask(mStencilValueMaskFront) == fnMask(mStencilValueMaskBack)); 246 ok &= (fnClamp(mStencilRefFront) == fnClamp(mStencilRefBack)); 247 248 if (!ok) { 249 ErrorInvalidOperation( 250 "Stencil front/back state must effectively match." 251 " (before front/back comparison, WRITEMASK and VALUE_MASK" 252 " are masked with (2^s)-1, and REF is clamped to" 253 " [0, (2^s)-1], where `s` is the number of enabled stencil" 254 " bits in the draw framebuffer)"); 255 } 256 return ok; 257 } 258 259 // - 260 261 void WebGLContext::GenErrorIllegalUse(const GLenum useTarget, 262 const uint32_t useId, 263 const GLenum boundTarget, 264 const uint32_t boundId) const { 265 const auto fnName = [&](const GLenum target, const uint32_t id) { 266 auto name = nsCString(EnumString(target).c_str()); 267 if (id != static_cast<uint32_t>(-1)) { 268 name += nsPrintfCString("[%u]", id); 269 } 270 return name; 271 }; 272 const auto& useName = fnName(useTarget, useId); 273 const auto& boundName = fnName(boundTarget, boundId); 274 GenerateError(LOCAL_GL_INVALID_OPERATION, 275 "Illegal use of buffer at %s" 276 " while also bound to %s.", 277 useName.BeginReading(), boundName.BeginReading()); 278 } 279 280 bool WebGLContext::ValidateBufferForNonTf(const WebGLBuffer& nonTfBuffer, 281 const GLenum nonTfTarget, 282 const uint32_t nonTfId) const { 283 bool dupe = false; 284 const auto& tfAttribs = mBoundTransformFeedback->mIndexedBindings; 285 for (const auto& cur : tfAttribs) { 286 dupe |= (&nonTfBuffer == cur.mBufferBinding.get()); 287 } 288 if (MOZ_LIKELY(!dupe)) return true; 289 290 dupe = false; 291 for (const auto tfId : IntegerRange(tfAttribs.size())) { 292 const auto& tfBuffer = tfAttribs[tfId].mBufferBinding; 293 if (&nonTfBuffer == tfBuffer) { 294 dupe = true; 295 GenErrorIllegalUse(nonTfTarget, nonTfId, 296 LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER, tfId); 297 } 298 } 299 MOZ_ASSERT(dupe); 300 return false; 301 } 302 303 bool WebGLContext::ValidateBuffersForTf( 304 const WebGLTransformFeedback& tfo, 305 const webgl::LinkedProgramInfo& linkInfo) const { 306 size_t numUsed; 307 switch (linkInfo.transformFeedbackBufferMode) { 308 case LOCAL_GL_INTERLEAVED_ATTRIBS: 309 numUsed = 1; 310 break; 311 312 case LOCAL_GL_SEPARATE_ATTRIBS: 313 numUsed = linkInfo.active.activeTfVaryings.size(); 314 break; 315 316 default: 317 MOZ_CRASH(); 318 } 319 320 std::vector<webgl::BufferAndIndex> tfBuffers; 321 tfBuffers.reserve(numUsed); 322 for (const auto i : IntegerRange(numUsed)) { 323 tfBuffers.push_back({tfo.mIndexedBindings[i].mBufferBinding.get(), 324 static_cast<uint32_t>(i)}); 325 } 326 327 return ValidateBuffersForTf(tfBuffers); 328 } 329 330 bool WebGLContext::ValidateBuffersForTf( 331 const std::vector<webgl::BufferAndIndex>& tfBuffers) const { 332 bool dupe = false; 333 const auto fnCheck = [&](const WebGLBuffer* const nonTf, 334 const GLenum nonTfTarget, const uint32_t nonTfId) { 335 for (const auto& tf : tfBuffers) { 336 dupe |= (nonTf && tf.buffer == nonTf); 337 } 338 339 if (MOZ_LIKELY(!dupe)) return false; 340 341 for (const auto& tf : tfBuffers) { 342 if (nonTf && tf.buffer == nonTf) { 343 dupe = true; 344 GenErrorIllegalUse(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER, tf.id, 345 nonTfTarget, nonTfId); 346 } 347 } 348 return true; 349 }; 350 351 fnCheck(mBoundArrayBuffer.get(), LOCAL_GL_ARRAY_BUFFER, -1); 352 fnCheck(mBoundCopyReadBuffer.get(), LOCAL_GL_COPY_READ_BUFFER, -1); 353 fnCheck(mBoundCopyWriteBuffer.get(), LOCAL_GL_COPY_WRITE_BUFFER, -1); 354 fnCheck(mBoundPixelPackBuffer.get(), LOCAL_GL_PIXEL_PACK_BUFFER, -1); 355 fnCheck(mBoundPixelUnpackBuffer.get(), LOCAL_GL_PIXEL_UNPACK_BUFFER, -1); 356 // fnCheck(mBoundTransformFeedbackBuffer.get(), 357 // LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER, -1); 358 fnCheck(mBoundUniformBuffer.get(), LOCAL_GL_UNIFORM_BUFFER, -1); 359 360 for (const auto i : IntegerRange(mIndexedUniformBufferBindings.size())) { 361 const auto& cur = mIndexedUniformBufferBindings[i]; 362 fnCheck(cur.mBufferBinding.get(), LOCAL_GL_UNIFORM_BUFFER, i); 363 } 364 365 fnCheck(mBoundVertexArray->mElementArrayBuffer.get(), 366 LOCAL_GL_ELEMENT_ARRAY_BUFFER, -1); 367 for (const auto i : IntegerRange(MaxVertexAttribs())) { 368 const auto& binding = mBoundVertexArray->AttribBinding(i); 369 fnCheck(binding.buffer.get(), LOCAL_GL_ARRAY_BUFFER, i); 370 } 371 372 return !dupe; 373 } 374 375 //////////////////////////////////////// 376 377 template <typename T> 378 static bool DoSetsIntersect(const std::set<T>& a, const std::set<T>& b) { 379 std::vector<T> intersection; 380 std::set_intersection(a.begin(), a.end(), b.begin(), b.end(), 381 std::back_inserter(intersection)); 382 return !intersection.empty(); 383 } 384 385 template <size_t N> 386 static size_t FindFirstOne(const std::bitset<N>& bs) { 387 MOZ_ASSERT(bs.any()); 388 // We don't need this to be fast, so don't bother with CLZ intrinsics. 389 for (const auto i : IntegerRange(N)) { 390 if (bs[i]) return i; 391 } 392 return -1; 393 } 394 395 const webgl::CachedDrawFetchLimits* ValidateDraw(WebGLContext* const webgl, 396 const GLenum mode, 397 const uint32_t instanceCount) { 398 if (!webgl->BindCurFBForDraw()) return nullptr; 399 400 const auto& fb = webgl->mBoundDrawFramebuffer; 401 if (fb) { 402 const auto& info = *fb->GetCompletenessInfo(); 403 const auto isF32WithBlending = info.isAttachmentF32 & webgl->mBlendEnabled; 404 if (isF32WithBlending.any()) { 405 if (!webgl->IsExtensionEnabled(WebGLExtensionID::EXT_float_blend)) { 406 const auto first = FindFirstOne(isF32WithBlending); 407 webgl->ErrorInvalidOperation( 408 "Attachment %u is float32 with blending enabled, which requires " 409 "EXT_float_blend.", 410 uint32_t(first)); 411 return nullptr; 412 } 413 webgl->WarnIfImplicit(WebGLExtensionID::EXT_float_blend); 414 } 415 } 416 417 switch (mode) { 418 case LOCAL_GL_TRIANGLES: 419 case LOCAL_GL_TRIANGLE_STRIP: 420 case LOCAL_GL_TRIANGLE_FAN: 421 case LOCAL_GL_POINTS: 422 case LOCAL_GL_LINE_STRIP: 423 case LOCAL_GL_LINE_LOOP: 424 case LOCAL_GL_LINES: 425 break; 426 default: 427 webgl->ErrorInvalidEnumInfo("mode", mode); 428 return nullptr; 429 } 430 431 if (!webgl->ValidateStencilParamsForDrawCall()) return nullptr; 432 433 if (!webgl->mActiveProgramLinkInfo) { 434 webgl->ErrorInvalidOperation("The current program is not linked."); 435 return nullptr; 436 } 437 const auto& linkInfo = webgl->mActiveProgramLinkInfo; 438 439 // - 440 // Check UBO sizes. 441 442 for (const auto i : IntegerRange(linkInfo->uniformBlocks.size())) { 443 const auto& cur = linkInfo->uniformBlocks[i]; 444 const auto& dataSize = cur.info.dataSize; 445 const auto& binding = cur.binding; 446 if (!binding) { 447 webgl->ErrorInvalidOperation("Buffer for uniform block is null."); 448 return nullptr; 449 } 450 451 const auto availByteCount = binding->ByteCount(); 452 if (dataSize > availByteCount) { 453 webgl->ErrorInvalidOperation( 454 "Buffer for uniform block is smaller" 455 " than UNIFORM_BLOCK_DATA_SIZE."); 456 return nullptr; 457 } 458 459 if (!webgl->ValidateBufferForNonTf(binding->mBufferBinding, 460 LOCAL_GL_UNIFORM_BUFFER, i)) 461 return nullptr; 462 } 463 464 // - 465 466 const auto& tfo = webgl->mBoundTransformFeedback; 467 if (tfo && tfo->IsActiveAndNotPaused()) { 468 if (fb) { 469 const auto& info = *fb->GetCompletenessInfo(); 470 if (info.isMultiview) { 471 webgl->ErrorInvalidOperation( 472 "Cannot render to multiview with transform feedback."); 473 return nullptr; 474 } 475 } 476 477 if (!webgl->ValidateBuffersForTf(*tfo, *linkInfo)) return nullptr; 478 } 479 480 // - 481 482 const auto& fragOutputs = linkInfo->fragOutputs; 483 const auto fnValidateFragOutputType = 484 [&](const uint8_t loc, const webgl::TextureBaseType dstBaseType) { 485 const auto itr = fragOutputs.find(loc); 486 MOZ_DIAGNOSTIC_ASSERT(itr != fragOutputs.end()); 487 488 const auto& info = itr->second; 489 const auto& srcBaseType = info.baseType; 490 if (MOZ_UNLIKELY(dstBaseType != srcBaseType)) { 491 const auto& srcStr = ToString(srcBaseType); 492 const auto& dstStr = ToString(dstBaseType); 493 webgl->ErrorInvalidOperation( 494 "Program frag output at location %u is type %s," 495 " but destination draw buffer is type %s.", 496 uint32_t(loc), srcStr, dstStr); 497 return false; 498 } 499 return true; 500 }; 501 502 if (!webgl->mRasterizerDiscardEnabled) { 503 uint8_t fbZLayerCount = 1; 504 auto hasAttachment = std::bitset<webgl::kMaxDrawBuffers>(1); 505 auto drawBufferEnabled = std::bitset<webgl::kMaxDrawBuffers>(); 506 if (fb) { 507 drawBufferEnabled = fb->DrawBufferEnabled(); 508 const auto& info = *fb->GetCompletenessInfo(); 509 fbZLayerCount = info.zLayerCount; 510 hasAttachment = info.hasAttachment; 511 } else { 512 drawBufferEnabled[0] = (webgl->mDefaultFB_DrawBuffer0 == LOCAL_GL_BACK); 513 } 514 515 if (fbZLayerCount != linkInfo->zLayerCount) { 516 webgl->ErrorInvalidOperation( 517 "Multiview count mismatch: shader: %u, framebuffer: %u", 518 uint32_t{linkInfo->zLayerCount}, uint32_t{fbZLayerCount}); 519 return nullptr; 520 } 521 522 const auto writable = 523 hasAttachment & drawBufferEnabled & webgl->mColorWriteMaskNonzero; 524 if (writable.any()) { 525 // Do we have any undefined outputs with real attachments that 526 // aren't masked-out by color write mask or drawBuffers? 527 const auto wouldWriteUndefined = ~linkInfo->hasOutput & writable; 528 if (wouldWriteUndefined.any()) { 529 const auto first = FindFirstOne(wouldWriteUndefined); 530 webgl->ErrorInvalidOperation( 531 "Program has no frag output at location %u, the" 532 " destination draw buffer has an attached" 533 " image, and its color write mask is not all false," 534 " and DRAW_BUFFER%u is not NONE.", 535 uint32_t(first), uint32_t(first)); 536 return nullptr; 537 } 538 539 const auto outputWrites = linkInfo->hasOutput & writable; 540 541 if (fb) { 542 for (const auto& attach : fb->ColorDrawBuffers()) { 543 const auto i = 544 uint8_t(attach->mAttachmentPoint - LOCAL_GL_COLOR_ATTACHMENT0); 545 if (!outputWrites[i]) continue; 546 const auto& imageInfo = attach->GetImageInfo(); 547 if (!imageInfo) continue; 548 const auto& dstBaseType = imageInfo->mFormat->format->baseType; 549 if (!fnValidateFragOutputType(i, dstBaseType)) return nullptr; 550 } 551 } else { 552 if (outputWrites[0]) { 553 if (!fnValidateFragOutputType(0, webgl::TextureBaseType::Float)) 554 return nullptr; 555 } 556 } 557 } 558 } 559 560 // - 561 562 const auto fetchLimits = linkInfo->GetDrawFetchLimits(); 563 if (!fetchLimits) return nullptr; 564 565 if (instanceCount > fetchLimits->maxInstances) { 566 webgl->ErrorInvalidOperation( 567 "Instance fetch requires %u, but attribs only" 568 " supply %u.", 569 instanceCount, uint32_t(fetchLimits->maxInstances)); 570 return nullptr; 571 } 572 573 if (tfo) { 574 for (const auto& used : fetchLimits->usedBuffers) { 575 MOZ_ASSERT(used.buffer); 576 if (!webgl->ValidateBufferForNonTf(*used.buffer, LOCAL_GL_ARRAY_BUFFER, 577 used.id)) 578 return nullptr; 579 } 580 } 581 582 // - 583 584 webgl->RunContextLossTimer(); 585 586 return fetchLimits; 587 } 588 589 //////////////////////////////////////// 590 591 static uint32_t UsedVertsForTFDraw(GLenum mode, uint32_t vertCount) { 592 uint8_t vertsPerPrim; 593 594 switch (mode) { 595 case LOCAL_GL_POINTS: 596 vertsPerPrim = 1; 597 break; 598 case LOCAL_GL_LINES: 599 vertsPerPrim = 2; 600 break; 601 case LOCAL_GL_TRIANGLES: 602 vertsPerPrim = 3; 603 break; 604 default: 605 MOZ_CRASH("`mode`"); 606 } 607 608 return vertCount / vertsPerPrim * vertsPerPrim; 609 } 610 611 class ScopedDrawWithTransformFeedback final { 612 WebGLContext* const mWebGL; 613 WebGLTransformFeedback* const mTFO; 614 const bool mWithTF; 615 uint32_t mUsedVerts; 616 617 public: 618 ScopedDrawWithTransformFeedback(WebGLContext* webgl, GLenum mode, 619 uint32_t vertCount, uint32_t instanceCount, 620 bool* const out_error) 621 : mWebGL(webgl), 622 mTFO(mWebGL->mBoundTransformFeedback), 623 mWithTF(mTFO && mTFO->mIsActive && !mTFO->mIsPaused), 624 mUsedVerts(0) { 625 *out_error = false; 626 if (!mWithTF) return; 627 628 if (mode != mTFO->mActive_PrimMode) { 629 mWebGL->ErrorInvalidOperation( 630 "Drawing with transform feedback requires" 631 " `mode` to match BeginTransformFeedback's" 632 " `primitiveMode`."); 633 *out_error = true; 634 return; 635 } 636 637 const auto usedVertsPerInstance = UsedVertsForTFDraw(mode, vertCount); 638 const auto usedVerts = 639 CheckedInt<uint32_t>(usedVertsPerInstance) * instanceCount; 640 641 const auto remainingCapacity = 642 mTFO->mActive_VertCapacity - mTFO->mActive_VertPosition; 643 if (!usedVerts.isValid() || usedVerts.value() > remainingCapacity) { 644 mWebGL->ErrorInvalidOperation( 645 "Insufficient buffer capacity remaining for" 646 " transform feedback."); 647 *out_error = true; 648 return; 649 } 650 651 mUsedVerts = usedVerts.value(); 652 } 653 654 void Advance() const { 655 if (!mWithTF) return; 656 657 mTFO->mActive_VertPosition += mUsedVerts; 658 659 for (const auto& cur : mTFO->mIndexedBindings) { 660 const auto& buffer = cur.mBufferBinding; 661 if (buffer) { 662 buffer->ResetLastUpdateFenceId(); 663 } 664 } 665 } 666 }; 667 668 static bool HasInstancedDrawing(const WebGLContext& webgl) { 669 return webgl.IsWebGL2() || 670 webgl.IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays); 671 } 672 673 //////////////////////////////////////// 674 675 void WebGLContext::DrawArraysInstanced(const GLenum mode, const GLint first, 676 const GLsizei iVertCount, 677 const GLsizei instanceCount) { 678 const FuncScope funcScope(*this, "drawArraysInstanced"); 679 // AUTO_PROFILER_LABEL("WebGLContext::DrawArraysInstanced", GRAPHICS); 680 if (IsContextLost()) return; 681 const gl::GLContext::TlsScope inTls(gl); 682 683 // - 684 685 if (!ValidateNonNegative("first", first) || 686 !ValidateNonNegative("vertCount", iVertCount) || 687 !ValidateNonNegative("instanceCount", instanceCount)) { 688 return; 689 } 690 const auto vertCount = AssertedCast<uint32_t>(iVertCount); 691 692 if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { 693 MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); 694 if (mPrimRestartTypeBytes != 0) { 695 mPrimRestartTypeBytes = 0; 696 697 // OSX appears to have severe perf issues with leaving this enabled. 698 gl->fDisable(LOCAL_GL_PRIMITIVE_RESTART); 699 } 700 } 701 702 // - 703 704 const auto fetchLimits = ValidateDraw(this, mode, instanceCount); 705 if (!fetchLimits) return; 706 707 // - 708 709 const auto totalVertCount_safe = CheckedInt<uint32_t>(first) + vertCount; 710 if (!totalVertCount_safe.isValid()) { 711 ErrorOutOfMemory("`first+vertCount` out of range."); 712 return; 713 } 714 auto totalVertCount = totalVertCount_safe.value(); 715 716 if (vertCount && instanceCount && totalVertCount > fetchLimits->maxVerts) { 717 ErrorInvalidOperation( 718 "Vertex fetch requires %u, but attribs only supply %u.", totalVertCount, 719 uint32_t(fetchLimits->maxVerts)); 720 return; 721 } 722 723 if (vertCount > mMaxVertIdsPerDraw) { 724 ErrorOutOfMemory( 725 "Context's max vertCount is %u, but %u requested. " 726 "[webgl.max-vert-ids-per-draw]", 727 mMaxVertIdsPerDraw, vertCount); 728 return; 729 } 730 731 // - 732 733 bool error = false; 734 735 // - 736 737 const ScopedResolveTexturesForDraw scopedResolve(this, &error); 738 if (error) return; 739 740 const ScopedDrawWithTransformFeedback scopedTF(this, mode, vertCount, 741 instanceCount, &error); 742 if (error) return; 743 744 // On MacOS (Intel?), `first` in glDrawArrays also increases where instanced 745 // attribs are fetched from. There are two ways to fix this: 746 // 1. DrawElements with a [0,1,2,...] index buffer, converting `first` to 747 // `byteOffset` 748 // 2. OR offset all non-instanced vertex attrib pointers back, and call 749 // DrawArrays with first:0. 750 // * But now gl_VertexID will be wrong! So we inject a uniform to offset it 751 // back correctly. 752 // #1 ought to be the lowest overhead for any first>0, 753 // but DrawElements can't be used with transform-feedback, 754 // so we need #2 to also work. 755 // For now, only implement #2. 756 757 const auto& activeAttribs = mActiveProgramLinkInfo->active.activeAttribs; 758 759 auto driverFirst = first; 760 761 if (first && mBug_DrawArraysInstancedUserAttribFetchAffectedByFirst) { 762 // This is not particularly optimized, but we can if we need to. 763 bool hasInstancedUserAttrib = false; 764 bool hasVertexAttrib = false; 765 for (const auto& a : activeAttribs) { 766 if (a.location == -1) { 767 if (a.name == "gl_VertexID") { 768 hasVertexAttrib = true; 769 } 770 continue; 771 } 772 const auto& binding = mBoundVertexArray->AttribBinding(a.location); 773 if (binding.layout.divisor) { 774 hasInstancedUserAttrib = true; 775 } else { 776 hasVertexAttrib = true; 777 } 778 } 779 if (hasInstancedUserAttrib && hasVertexAttrib) { 780 driverFirst = 0; 781 } 782 } 783 const bool needsFix_InstancedUserAttribFetch = (driverFirst != first); 784 785 if (needsFix_InstancedUserAttribFetch) { 786 for (const auto& a : activeAttribs) { 787 if (a.location == -1) continue; 788 const auto& binding = mBoundVertexArray->AttribBinding(a.location); 789 if (binding.layout.divisor) continue; 790 791 mBoundVertexArray->DoVertexAttrib(a.location, first); 792 } 793 794 gl->fUniform1i(mActiveProgramLinkInfo->webgl_gl_VertexID_Offset, first); 795 } 796 const auto undoFix_InstancedUserAttribFetch = MakeScopeExit([&]() { 797 if (needsFix_InstancedUserAttribFetch) { 798 gl->fUniform1i(mActiveProgramLinkInfo->webgl_gl_VertexID_Offset, 0); 799 800 for (const auto& a : activeAttribs) { 801 if (a.location == -1) continue; 802 const auto& binding = mBoundVertexArray->AttribBinding(a.location); 803 if (binding.layout.divisor) continue; 804 805 mBoundVertexArray->DoVertexAttrib(a.location, 0); 806 } 807 } 808 }); 809 810 // - 811 812 { 813 const auto whatDoesAttrib0Need = WhatDoesVertexAttrib0Need(); 814 auto fakeVertCount = uint64_t(driverFirst) + vertCount; 815 if (whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default) { 816 fakeVertCount = 0; 817 } 818 if (!(vertCount && instanceCount)) { 819 fakeVertCount = 0; 820 } 821 822 const bool needsFix_FakeVertexAttrib0 = bool(fakeVertCount); 823 const auto undoFix_FakeVertexAttrib0 = MakeScopeExit([&]() { 824 if (needsFix_FakeVertexAttrib0) { 825 mBoundVertexArray->DoVertexAttrib(0); 826 } 827 }); 828 if (needsFix_FakeVertexAttrib0) { 829 // fmt::println(FMT_STRING("DoFakeVertexAttrib0(fakeVertCount: {}, 830 // whatDoesAttrib0Need: {})"), fakeVertCount, (int)whatDoesAttrib0Need); 831 if (!DoFakeVertexAttrib0(fakeVertCount, whatDoesAttrib0Need)) return; 832 } 833 834 ScopedDrawCallWrapper wrapper(*this); 835 836 if (vertCount && instanceCount) { 837 if (HasInstancedDrawing(*this)) { 838 gl->fDrawArraysInstanced(mode, driverFirst, vertCount, instanceCount); 839 } else { 840 MOZ_ASSERT(instanceCount == 1); 841 gl->fDrawArrays(mode, driverFirst, vertCount); 842 } 843 } 844 } 845 846 Draw_cleanup(); 847 scopedTF.Advance(); 848 } 849 850 //////////////////////////////////////// 851 852 WebGLBuffer* WebGLContext::DrawElements_check(const GLsizei rawIndexCount, 853 const GLenum type, 854 const WebGLintptr byteOffset, 855 const GLsizei instanceCount) { 856 if (mBoundTransformFeedback && mBoundTransformFeedback->mIsActive && 857 !mBoundTransformFeedback->mIsPaused) { 858 ErrorInvalidOperation( 859 "DrawElements* functions are incompatible with" 860 " transform feedback."); 861 return nullptr; 862 } 863 864 if (!ValidateNonNegative("vertCount", rawIndexCount) || 865 !ValidateNonNegative("byteOffset", byteOffset) || 866 !ValidateNonNegative("instanceCount", instanceCount)) { 867 return nullptr; 868 } 869 const auto indexCount = uint32_t(rawIndexCount); 870 871 uint8_t bytesPerIndex = 0; 872 switch (type) { 873 case LOCAL_GL_UNSIGNED_BYTE: 874 bytesPerIndex = 1; 875 break; 876 877 case LOCAL_GL_UNSIGNED_SHORT: 878 bytesPerIndex = 2; 879 break; 880 881 case LOCAL_GL_UNSIGNED_INT: 882 if (IsWebGL2() || 883 IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) { 884 bytesPerIndex = 4; 885 } 886 break; 887 } 888 if (!bytesPerIndex) { 889 ErrorInvalidEnumInfo("type", type); 890 return nullptr; 891 } 892 if (byteOffset % bytesPerIndex != 0) { 893 ErrorInvalidOperation( 894 "`byteOffset` must be a multiple of the size of `type`"); 895 return nullptr; 896 } 897 898 //// 899 900 if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { 901 MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); 902 if (mPrimRestartTypeBytes != bytesPerIndex) { 903 mPrimRestartTypeBytes = bytesPerIndex; 904 905 const uint32_t ones = UINT32_MAX >> (32 - 8 * mPrimRestartTypeBytes); 906 gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART); 907 gl->fPrimitiveRestartIndex(ones); 908 } 909 } 910 911 //// 912 // Index fetching 913 914 const auto& indexBuffer = mBoundVertexArray->mElementArrayBuffer; 915 if (!indexBuffer) { 916 ErrorInvalidOperation("Index buffer not bound."); 917 return nullptr; 918 } 919 920 const size_t availBytes = indexBuffer->ByteLength(); 921 const auto availIndices = 922 AvailGroups(availBytes, byteOffset, bytesPerIndex, bytesPerIndex); 923 if (instanceCount && indexCount > availIndices) { 924 ErrorInvalidOperation("Index buffer too small."); 925 return nullptr; 926 } 927 928 return indexBuffer.get(); 929 } 930 931 static void HandleDrawElementsErrors( 932 WebGLContext* webgl, gl::GLContext::LocalErrorScope& errorScope) { 933 const auto err = errorScope.GetError(); 934 if (err == LOCAL_GL_INVALID_OPERATION) { 935 webgl->ErrorInvalidOperation( 936 "Driver rejected indexed draw call, possibly" 937 " due to out-of-bounds indices."); 938 return; 939 } 940 941 MOZ_ASSERT(!err); 942 if (err) { 943 webgl->ErrorImplementationBug( 944 "Unexpected driver error during indexed draw" 945 " call. Please file a bug."); 946 return; 947 } 948 } 949 950 void WebGLContext::DrawElementsInstanced(const GLenum mode, 951 const GLsizei iIndexCount, 952 const GLenum type, 953 const WebGLintptr byteOffset, 954 const GLsizei instanceCount) { 955 const FuncScope funcScope(*this, "drawElementsInstanced"); 956 // AUTO_PROFILER_LABEL("WebGLContext::DrawElementsInstanced", GRAPHICS); 957 if (IsContextLost()) return; 958 959 const gl::GLContext::TlsScope inTls(gl); 960 961 const auto indexBuffer = 962 DrawElements_check(iIndexCount, type, byteOffset, instanceCount); 963 if (!indexBuffer) return; 964 const auto indexCount = AssertedCast<uint32_t>(iIndexCount); 965 966 // - 967 968 const auto fetchLimits = ValidateDraw(this, mode, instanceCount); 969 if (!fetchLimits) return; 970 971 const auto whatDoesAttrib0Need = WhatDoesVertexAttrib0Need(); 972 973 uint64_t fakeVertCount = 0; 974 if (whatDoesAttrib0Need != WebGLVertexAttrib0Status::Default) { 975 fakeVertCount = fetchLimits->maxVerts; 976 } 977 if (!indexCount || !instanceCount) { 978 fakeVertCount = 0; 979 } 980 if (fakeVertCount == UINT64_MAX) { // Ok well that's too many! 981 const auto exactMaxVertId = 982 indexBuffer->GetIndexedFetchMaxVert(type, byteOffset, indexCount); 983 MOZ_RELEASE_ASSERT(exactMaxVertId); 984 fakeVertCount = uint32_t{*exactMaxVertId}; 985 fakeVertCount += 1; 986 } 987 988 // - 989 990 { 991 uint64_t indexCapacity = indexBuffer->ByteLength(); 992 switch (type) { 993 case LOCAL_GL_UNSIGNED_BYTE: 994 break; 995 case LOCAL_GL_UNSIGNED_SHORT: 996 indexCapacity /= 2; 997 break; 998 case LOCAL_GL_UNSIGNED_INT: 999 indexCapacity /= 4; 1000 break; 1001 } 1002 1003 uint32_t maxVertId = 0; 1004 const auto isFetchValid = [&]() { 1005 if (!indexCount || !instanceCount) return true; 1006 1007 const auto globalMaxVertId = 1008 indexBuffer->GetIndexedFetchMaxVert(type, 0, indexCapacity); 1009 if (!globalMaxVertId) return true; 1010 if (globalMaxVertId.value() < fetchLimits->maxVerts) return true; 1011 1012 const auto exactMaxVertId = 1013 indexBuffer->GetIndexedFetchMaxVert(type, byteOffset, indexCount); 1014 maxVertId = exactMaxVertId.value(); 1015 return maxVertId < fetchLimits->maxVerts; 1016 }(); 1017 if (!isFetchValid) { 1018 ErrorInvalidOperation( 1019 "Indexed vertex fetch requires %u vertices, but" 1020 " attribs only supply %u.", 1021 maxVertId + 1, uint32_t(fetchLimits->maxVerts)); 1022 return; 1023 } 1024 } 1025 1026 if (indexCount > mMaxVertIdsPerDraw) { 1027 ErrorOutOfMemory( 1028 "Context's max indexCount is %u, but %u requested. " 1029 "[webgl.max-vert-ids-per-draw]", 1030 mMaxVertIdsPerDraw, indexCount); 1031 return; 1032 } 1033 1034 // - 1035 1036 const bool needsFix_FakeVertexAttrib0 = bool(fakeVertCount); 1037 const auto undoFix_FakeVertexAttrib0 = MakeScopeExit([&]() { 1038 if (needsFix_FakeVertexAttrib0) { 1039 mBoundVertexArray->DoVertexAttrib(0); 1040 } 1041 }); 1042 if (needsFix_FakeVertexAttrib0) { 1043 if (!DoFakeVertexAttrib0(fakeVertCount, whatDoesAttrib0Need)) return; 1044 } 1045 1046 // - 1047 1048 bool error = false; 1049 const ScopedResolveTexturesForDraw scopedResolve(this, &error); 1050 if (error) return; 1051 1052 { 1053 ScopedDrawCallWrapper wrapper(*this); 1054 { 1055 std::unique_ptr<gl::GLContext::LocalErrorScope> errorScope; 1056 if (MOZ_UNLIKELY(gl->IsANGLE() && 1057 gl->mDebugFlags & 1058 gl::GLContext::DebugFlagAbortOnError)) { 1059 // ANGLE does range validation even when it doesn't need to. 1060 // With MOZ_GL_ABORT_ON_ERROR, we need to catch it or hit assertions. 1061 errorScope.reset(new gl::GLContext::LocalErrorScope(*gl)); 1062 } 1063 1064 if (indexCount && instanceCount) { 1065 if (HasInstancedDrawing(*this)) { 1066 gl->fDrawElementsInstanced(mode, indexCount, type, 1067 reinterpret_cast<GLvoid*>(byteOffset), 1068 instanceCount); 1069 } else { 1070 MOZ_ASSERT(instanceCount == 1); 1071 gl->fDrawElements(mode, indexCount, type, 1072 reinterpret_cast<GLvoid*>(byteOffset)); 1073 } 1074 } 1075 1076 if (errorScope) { 1077 HandleDrawElementsErrors(this, *errorScope); 1078 } 1079 } 1080 } 1081 1082 Draw_cleanup(); 1083 } 1084 1085 //////////////////////////////////////// 1086 1087 void WebGLContext::Draw_cleanup() { 1088 if (gl->WorkAroundDriverBugs()) { 1089 if (gl->Renderer() == gl::GLRenderer::Tegra) { 1090 mDrawCallsSinceLastFlush++; 1091 1092 if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) { 1093 gl->fFlush(); 1094 mDrawCallsSinceLastFlush = 0; 1095 } 1096 } 1097 } 1098 1099 // Let's check for a really common error: Viewport is larger than the actual 1100 // destination framebuffer. 1101 uint32_t destWidth; 1102 uint32_t destHeight; 1103 if (mBoundDrawFramebuffer) { 1104 const auto& info = mBoundDrawFramebuffer->GetCompletenessInfo(); 1105 destWidth = info->width; 1106 destHeight = info->height; 1107 } else { 1108 destWidth = mDefaultFB->mSize.width; 1109 destHeight = mDefaultFB->mSize.height; 1110 } 1111 1112 if (mViewportWidth > int32_t(destWidth) || 1113 mViewportHeight > int32_t(destHeight)) { 1114 if (!mAlreadyWarnedAboutViewportLargerThanDest) { 1115 GenerateWarning( 1116 "Drawing to a destination rect smaller than the viewport" 1117 " rect. (This warning will only be given once)"); 1118 mAlreadyWarnedAboutViewportLargerThanDest = true; 1119 } 1120 } 1121 } 1122 1123 WebGLVertexAttrib0Status WebGLContext::WhatDoesVertexAttrib0Need() const { 1124 MOZ_ASSERT(mCurrentProgram); 1125 MOZ_ASSERT(mActiveProgramLinkInfo); 1126 1127 bool legacyAttrib0 = mNeedsLegacyVertexAttrib0Handling; 1128 if (gl->WorkAroundDriverBugs() && kIsMacOS) { 1129 // Also programs with no attribs: 1130 // conformance/attribs/gl-vertex-attrib-unconsumed-out-of-bounds.html 1131 const auto& activeAttribs = mActiveProgramLinkInfo->active.activeAttribs; 1132 bool hasNonInstancedUserAttrib = false; 1133 for (const auto& a : activeAttribs) { 1134 if (a.location == -1) continue; 1135 const auto& layout = mBoundVertexArray->AttribBinding(a.location).layout; 1136 if (layout.divisor == 0) { 1137 hasNonInstancedUserAttrib = true; 1138 } 1139 } 1140 legacyAttrib0 |= !hasNonInstancedUserAttrib; 1141 } 1142 1143 if (!legacyAttrib0) return WebGLVertexAttrib0Status::Default; 1144 MOZ_RELEASE_ASSERT(mMaybeNeedsLegacyVertexAttrib0Handling, 1145 "Invariant need because this turns on index buffer " 1146 "validation, needed for fake-attrib0."); 1147 1148 if (!mActiveProgramLinkInfo->attrib0Active) { 1149 // Attrib0 unused, so just ensure that the legacy code has enough buffer. 1150 return WebGLVertexAttrib0Status::EmulatedUninitializedArray; 1151 } 1152 1153 const auto& isAttribArray0Enabled = 1154 mBoundVertexArray->AttribBinding(0).layout.isArray; 1155 return isAttribArray0Enabled 1156 ? WebGLVertexAttrib0Status::Default 1157 : WebGLVertexAttrib0Status::EmulatedInitializedArray; 1158 } 1159 1160 bool WebGLContext::DoFakeVertexAttrib0( 1161 const uint64_t fakeVertexCount, 1162 const WebGLVertexAttrib0Status whatDoesAttrib0Need) { 1163 MOZ_ASSERT(fakeVertexCount); 1164 MOZ_RELEASE_ASSERT(whatDoesAttrib0Need != WebGLVertexAttrib0Status::Default); 1165 1166 if (gl->WorkAroundDriverBugs() && gl->IsMesa()) { 1167 // Padded/strided to vec4, so 4x4bytes. 1168 const auto effectiveVertAttribBytes = 1169 CheckedInt<int32_t>(fakeVertexCount) * 4 * 4; 1170 if (!effectiveVertAttribBytes.isValid()) { 1171 ErrorOutOfMemory("`offset + count` too large for Mesa."); 1172 return false; 1173 } 1174 } 1175 1176 if (!mAlreadyWarnedAboutFakeVertexAttrib0) { 1177 GenerateWarning( 1178 "Drawing without vertex attrib 0 array enabled forces the browser " 1179 "to do expensive emulation work when running on desktop OpenGL " 1180 "platforms, for example on Mac. It is preferable to always draw " 1181 "with vertex attrib 0 array enabled, by using bindAttribLocation " 1182 "to bind some always-used attribute to location 0."); 1183 mAlreadyWarnedAboutFakeVertexAttrib0 = true; 1184 } 1185 1186 if (!mFakeVertexAttrib0BufferObject) { 1187 gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject); 1188 mFakeVertexAttrib0BufferAllocSize = 0; 1189 mFakeVertexAttrib0BufferInitializedSize = 0; 1190 } 1191 1192 //// 1193 1194 const auto maxFakeVerts = StaticPrefs::webgl_fake_verts_max(); 1195 if (fakeVertexCount > maxFakeVerts) { 1196 ErrorOutOfMemory( 1197 "Draw requires faking a vertex attrib 0 array, but required vert count" 1198 " (%" PRIu64 ") is more than webgl.fake-verts.max (%u).", 1199 fakeVertexCount, maxFakeVerts); 1200 return false; 1201 } 1202 1203 const auto bytesPerVert = sizeof(mFakeVertexAttrib0Data); 1204 const auto checked_dataSize = 1205 CheckedInt<intptr_t>(fakeVertexCount) * bytesPerVert; 1206 if (!checked_dataSize.isValid()) { 1207 ErrorOutOfMemory( 1208 "Integer overflow trying to construct a fake vertex attrib 0" 1209 " array for a draw-operation with %" PRIu64 1210 " vertices. Try" 1211 " reducing the number of vertices.", 1212 fakeVertexCount); 1213 return false; 1214 } 1215 const auto dataSize = checked_dataSize.value(); 1216 1217 if (mFakeVertexAttrib0BufferAllocSize < dataSize) { 1218 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject); 1219 gl::GLContext::LocalErrorScope errorScope(*gl); 1220 1221 gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, 1222 LOCAL_GL_STATIC_DRAW); 1223 1224 const auto err = errorScope.GetError(); 1225 if (err) { 1226 ErrorOutOfMemory( 1227 "Failed to allocate fake vertex attrib 0 buffer: %zi bytes", 1228 dataSize); 1229 return false; 1230 } 1231 1232 mFakeVertexAttrib0BufferAllocSize = dataSize; 1233 mFakeVertexAttrib0BufferInitializedSize = 0; 1234 } 1235 1236 //// 1237 1238 const auto FillWithGenericVertexAttrib0Data = [&]() { 1239 if (dataSize <= mFakeVertexAttrib0BufferInitializedSize && 1240 memcmp(mFakeVertexAttrib0Data, mGenericVertexAttrib0Data, 1241 bytesPerVert) == 0) { 1242 return true; 1243 } 1244 memcpy(mFakeVertexAttrib0Data, mGenericVertexAttrib0Data, bytesPerVert); 1245 mFakeVertexAttrib0BufferInitializedSize = 0; 1246 1247 using DataPerVertT = decltype(mFakeVertexAttrib0Data); 1248 static_assert(sizeof(DataPerVertT) == 4 * sizeof(float)); 1249 MOZ_RELEASE_ASSERT(dataSize % sizeof(DataPerVertT) == 0); 1250 const size_t vertCount = dataSize / sizeof(DataPerVertT); 1251 const auto uploadData = std::unique_ptr<DataPerVertT[]>{ 1252 new (std::nothrow) DataPerVertT[vertCount]}; 1253 if (!uploadData) { 1254 ErrorOutOfMemory("Failed to allocate fake vertex attrib 0 upload data."); 1255 return false; 1256 } 1257 const auto uploadMutSpan = Span{uploadData.get(), vertCount}; 1258 for (auto& vert : uploadMutSpan) { 1259 memcpy(&vert, mFakeVertexAttrib0Data, bytesPerVert); 1260 } 1261 1262 { 1263 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject); 1264 gl::GLContext::LocalErrorScope errorScope(*gl); 1265 1266 const auto uploadBytes = AsBytes(uploadMutSpan); 1267 MOZ_ASSERT(uploadBytes.size() == (size_t)dataSize); 1268 gl->fBufferSubData(LOCAL_GL_ARRAY_BUFFER, 0, uploadBytes.size(), 1269 uploadBytes.data()); 1270 1271 const auto err = errorScope.GetError(); 1272 if (err) { 1273 ErrorOutOfMemory("Failed to upload fake vertex attrib 0 data."); 1274 return false; 1275 } 1276 } 1277 1278 mFakeVertexAttrib0BufferInitializedSize = dataSize; 1279 return true; 1280 }; 1281 1282 if (whatDoesAttrib0Need == 1283 WebGLVertexAttrib0Status::EmulatedInitializedArray) { 1284 if (!FillWithGenericVertexAttrib0Data()) return false; 1285 } 1286 1287 //// 1288 1289 const auto& attrib0 = mBoundVertexArray->AttribBinding(0); 1290 if (attrib0.layout.divisor) { 1291 gl->fVertexAttribDivisor(0, 0); 1292 } 1293 1294 gl->fEnableVertexAttribArray(0); 1295 1296 gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject); 1297 switch (mGenericVertexAttribTypes[0]) { 1298 case webgl::AttribBaseType::Boolean: 1299 case webgl::AttribBaseType::Float: 1300 gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, false, 0, 0); 1301 break; 1302 1303 case webgl::AttribBaseType::Int: 1304 gl->fVertexAttribIPointer(0, 4, LOCAL_GL_INT, 0, 0); 1305 break; 1306 1307 case webgl::AttribBaseType::Uint: 1308 gl->fVertexAttribIPointer(0, 4, LOCAL_GL_UNSIGNED_INT, 0, 0); 1309 break; 1310 } 1311 1312 //// 1313 1314 return true; 1315 } 1316 1317 } // namespace mozilla