void TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName, WebGLTexture* tex, TexImageTarget target, GLint level, const webgl::DriverUnpackInfo* dui, GLint xOffset, GLint yOffset, GLint zOffset, GLenum* const out_glError) { *out_glError = 0; WebGLContext* webgl = tex->mContext; // MakeCurrent is a big mess in here, because mapping (and presumably unmapping) on // OSX can lose our MakeCurrent. Therefore it's easiest to MakeCurrent just before we // call into GL, instead of trying to keep MakeCurrent-ed. RefPtr<gfx::DataSourceSurface> dataSurf = mSurf->GetDataSurface(); if (!dataSurf) { // Since GetDataSurface didn't return error code, assume system // is out of memory *out_glError = LOCAL_GL_OUT_OF_MEMORY; return; } GLenum error; if (UploadDataSurface(isSubImage, webgl, target, level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, dataSurf, mIsAlphaPremult, &error)) { return; } if (error == LOCAL_GL_OUT_OF_MEMORY) { *out_glError = LOCAL_GL_OUT_OF_MEMORY; return; } // CPU conversion. (++numCopies) UniqueBuffer convertedBuffer; uint8_t convertedAlignment; bool outOfMemory; if (!ConvertSurface(webgl, dui, dataSurf, mIsAlphaPremult, &convertedBuffer, &convertedAlignment, &outOfMemory)) { if (outOfMemory) { *out_glError = LOCAL_GL_OUT_OF_MEMORY; } else { NS_ERROR("Failed to convert surface."); *out_glError = LOCAL_GL_OUT_OF_MEMORY; } return; } MOZ_ALWAYS_TRUE( webgl->gl->MakeCurrent() ); ScopedUnpackReset scopedReset(webgl); webgl->gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, convertedAlignment); error = DoTexOrSubImage(isSubImage, webgl->gl, target.get(), level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth, convertedBuffer.get()); *out_glError = error; }
WebGLContext::FakeBlackTexture::FakeBlackTexture(gl::GLContext* gl, TexTarget target, FakeBlackType type) : mGL(gl) , mGLName(CreateGLTexture(gl)) { GLenum texFormat; switch (type) { case FakeBlackType::RGBA0000: texFormat = LOCAL_GL_RGBA; break; case FakeBlackType::RGBA0001: texFormat = LOCAL_GL_RGB; break; default: MOZ_CRASH("bad type"); } gl::ScopedBindTexture scopedBind(mGL, mGLName, target.get()); mGL->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST); mGL->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST); // We allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) to // minimize the risk of running into a driver bug in texImage2D, as it is a bit // unusual maybe to create 1x1 textures, and the stack may not have the alignment that // TexImage2D expects. const webgl::DriverUnpackInfo dui = {texFormat, texFormat, LOCAL_GL_UNSIGNED_BYTE}; UniqueBuffer zeros = moz_xcalloc(1, 16); // Infallible allocation. if (target == LOCAL_GL_TEXTURE_CUBE_MAP) { for (int i = 0; i < 6; ++i) { const TexImageTarget curTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; const GLenum error = DoTexImage(mGL, curTarget.get(), 0, &dui, 1, 1, 1, zeros.get()); if (error) MOZ_CRASH("Unexpected error during FakeBlack creation."); } } else { const GLenum error = DoTexImage(mGL, target.get(), 0, &dui, 1, 1, 1, zeros.get()); if (error) MOZ_CRASH("Unexpected error during FakeBlack creation."); } }
void WebGLShader::ShaderSource(const nsAString& source) { const char funcName[] = "shaderSource"; nsString sourceWithoutComments; if (!TruncateComments(source, &sourceWithoutComments)) { mContext->ErrorOutOfMemory("%s: Failed to alloc for empting comment contents.", funcName); return; } if (!ValidateGLSLPreprocString(mContext, funcName, sourceWithoutComments)) return; // We checked that the source stripped of comments is in the // 7-bit ASCII range, so we can skip the NS_IsAscii() check. const NS_LossyConvertUTF16toASCII cleanSource(sourceWithoutComments); if (PR_GetEnv("MOZ_WEBGL_DUMP_SHADERS")) { printf_stderr("////////////////////////////////////////\n"); printf_stderr("// MOZ_WEBGL_DUMP_SHADERS:\n"); // Wow - Roll Your Own Foreach-Lines because printf_stderr has a hard-coded // internal size, so long strings are truncated. const size_t maxChunkSize = 1024-1; // -1 for null-term. const UniqueBuffer buf(moz_xmalloc(maxChunkSize+1)); // +1 for null-term const auto bufBegin = (char*)buf.get(); size_t chunkStart = 0; while (chunkStart != cleanSource.Length()) { const auto chunkEnd = std::min(chunkStart + maxChunkSize, size_t(cleanSource.Length())); const auto chunkSize = chunkEnd - chunkStart; memcpy(bufBegin, cleanSource.BeginReading() + chunkStart, chunkSize); bufBegin[chunkSize + 1] = '\0'; printf_stderr("%s", bufBegin); chunkStart += chunkSize; } printf_stderr("////////////////////////////////////////\n"); } mSource = source; mCleanSource = cleanSource; }
void WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage) { const char funcName[] = "bufferData"; if (IsContextLost()) return; if (!ValidateNonNegative(funcName, "size", size)) return; //// const UniqueBuffer zeroBuffer(calloc(size, 1)); if (!zeroBuffer) return ErrorOutOfMemory("%s: Failed to allocate zeros.", funcName); BufferDataImpl(target, size_t(size), (const uint8_t*)zeroBuffer.get(), usage); }
void WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage) { const FuncScope funcScope(*this, "bufferData"); if (IsContextLost()) return; if (!ValidateNonNegative("size", size)) return; //// const auto checkedSize = CheckedInt<size_t>(size); if (!checkedSize.isValid()) return ErrorOutOfMemory("size too large for platform."); const UniqueBuffer zeroBuffer(calloc(checkedSize.value(), 1u)); if (!zeroBuffer) return ErrorOutOfMemory("Failed to allocate zeros."); BufferDataImpl(target, uint64_t{checkedSize.value()}, (const uint8_t*)zeroBuffer.get(), usage); }
bool TexUnpackBlob::ConvertIfNeeded(WebGLContext* webgl, const char* funcName, const uint32_t rowLength, const uint32_t rowCount, WebGLTexelFormat srcFormat, const uint8_t* const srcBegin, const ptrdiff_t srcStride, WebGLTexelFormat dstFormat, const ptrdiff_t dstStride, const uint8_t** const out_begin, UniqueBuffer* const out_anchoredBuffer) const { MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion); MOZ_ASSERT(dstFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion); *out_begin = srcBegin; if (!rowLength || !rowCount) return true; const auto srcIsPremult = (mSrcAlphaType == gfxAlphaType::Premult); const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha; const auto fnHasPremultMismatch = [&]() { if (mSrcAlphaType == gfxAlphaType::Opaque) return false; if (!HasColorAndAlpha(srcFormat)) return false; return srcIsPremult != dstIsPremult; }; const auto srcOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft : gl::OriginPos::BottomLeft); const auto dstOrigin = gl::OriginPos::BottomLeft; if (srcFormat != dstFormat) { webgl->GeneratePerfWarning("%s: Conversion requires pixel reformatting. (%u->%u)", funcName, uint32_t(srcFormat), uint32_t(dstFormat)); } else if (fnHasPremultMismatch()) { webgl->GeneratePerfWarning("%s: Conversion requires change in" " alpha-premultiplication.", funcName); } else if (srcOrigin != dstOrigin) { webgl->GeneratePerfWarning("%s: Conversion requires y-flip.", funcName); } else if (srcStride != dstStride) { webgl->GeneratePerfWarning("%s: Conversion requires change in stride. (%u->%u)", funcName, uint32_t(srcStride), uint32_t(dstStride)); } else { return true; } //// const auto dstTotalBytes = CheckedUint32(rowCount) * dstStride; if (!dstTotalBytes.isValid()) { webgl->ErrorOutOfMemory("%s: Calculation failed.", funcName); return false; } UniqueBuffer dstBuffer = calloc(1, dstTotalBytes.value()); if (!dstBuffer.get()) { webgl->ErrorOutOfMemory("%s: Failed to allocate dest buffer.", funcName); return false; } const auto dstBegin = static_cast<uint8_t*>(dstBuffer.get()); //// // And go!: bool wasTrivial; if (!ConvertImage(rowLength, rowCount, srcBegin, srcStride, srcOrigin, srcFormat, srcIsPremult, dstBegin, dstStride, dstOrigin, dstFormat, dstIsPremult, &wasTrivial)) { webgl->ErrorImplementationBug("%s: ConvertImage failed.", funcName); return false; } *out_begin = dstBegin; *out_anchoredBuffer = Move(dstBuffer); return true; }
static bool ZeroTextureData(WebGLContext* webgl, const char* funcName, GLuint tex, TexImageTarget target, uint32_t level, const webgl::FormatUsageInfo* usage, uint32_t width, uint32_t height, uint32_t depth) { // This has two usecases: // 1. Lazy zeroing of uninitialized textures: // a. Before draw, when FakeBlack isn't viable. (TexStorage + Draw*) // b. Before partial upload. (TexStorage + TexSubImage) // 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image) // We have no sympathy for any of these cases. // "Doctor, it hurts when I do this!" "Well don't do that!" webgl->GenerateWarning("%s: This operation requires zeroing texture data. This is" " slow.", funcName); gl::GLContext* gl = webgl->GL(); gl->MakeCurrent(); GLenum scopeBindTarget; switch (target.get()) { case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: scopeBindTarget = LOCAL_GL_TEXTURE_CUBE_MAP; break; default: scopeBindTarget = target.get(); break; } const gl::ScopedBindTexture scopeBindTexture(gl, tex, scopeBindTarget); auto compression = usage->format->compression; if (compression) { auto sizedFormat = usage->format->sizedFormat; MOZ_RELEASE_ASSERT(sizedFormat, "GFX: texture sized format not set"); const auto fnSizeInBlocks = [](CheckedUint32 pixels, uint8_t pixelsPerBlock) { return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock; }; const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth); const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight); CheckedUint32 checkedByteCount = compression->bytesPerBlock; checkedByteCount *= widthBlocks; checkedByteCount *= heightBlocks; checkedByteCount *= depth; if (!checkedByteCount.isValid()) return false; const size_t byteCount = checkedByteCount.value(); UniqueBuffer zeros = calloc(1, byteCount); if (!zeros) return false; ScopedUnpackReset scopedReset(webgl); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it // well. const auto error = DoCompressedTexSubImage(gl, target.get(), level, 0, 0, 0, width, height, depth, sizedFormat, byteCount, zeros.get()); return !error; } const auto driverUnpackInfo = usage->idealUnpack; MOZ_RELEASE_ASSERT(driverUnpackInfo, "GFX: ideal unpack info not set."); if (webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) && gl->IsANGLE() && usage->format->d) { // ANGLE_depth_texture does not allow uploads, so we have to clear. // (Restriction because of D3D9) MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D); MOZ_ASSERT(level == 0); ZeroANGLEDepthTexture(webgl, tex, usage, width, height); return true; } const webgl::PackingInfo packing = driverUnpackInfo->ToPacking(); const auto bytesPerPixel = webgl::BytesPerPixel(packing); CheckedUint32 checkedByteCount = bytesPerPixel; checkedByteCount *= width; checkedByteCount *= height; checkedByteCount *= depth; if (!checkedByteCount.isValid()) return false; const size_t byteCount = checkedByteCount.value(); UniqueBuffer zeros = calloc(1, byteCount); if (!zeros) return false; ScopedUnpackReset scopedReset(webgl); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well. const auto error = DoTexSubImage(gl, target, level, 0, 0, 0, width, height, depth, packing, zeros.get()); return !error; }
/*static*/ bool TexUnpackSurface::ConvertSurface(WebGLContext* webgl, const webgl::DriverUnpackInfo* dui, gfx::DataSourceSurface* surf, bool isSurfAlphaPremult, UniqueBuffer* const out_convertedBuffer, uint8_t* const out_convertedAlignment, bool* const out_outOfMemory) { *out_outOfMemory = false; const size_t width = surf->GetSize().width; const size_t height = surf->GetSize().height; // Source args: // After we call this, on OSX, our GLContext will no longer be current. gfx::DataSourceSurface::ScopedMap srcMap(surf, gfx::DataSourceSurface::MapType::READ); if (!srcMap.IsMapped()) return false; const void* const srcBegin = srcMap.GetData(); const size_t srcStride = srcMap.GetStride(); WebGLTexelFormat srcFormat; if (!GetFormatForSurf(surf, &srcFormat)) return false; const bool srcPremultiplied = isSurfAlphaPremult; // Dest args: WebGLTexelFormat dstFormat; if (!GetFormatForPackingTuple(dui->unpackFormat, dui->unpackType, &dstFormat)) return false; const auto bytesPerPixel = webgl::BytesPerPixel({dui->unpackFormat, dui->unpackType}); const size_t dstRowBytes = bytesPerPixel * width; const size_t dstAlignment = 8; // Just use the max! const size_t dstStride = RoundUpToMultipleOf(dstRowBytes, dstAlignment); CheckedUint32 checkedDstSize = dstStride; checkedDstSize *= height; if (!checkedDstSize.isValid()) { *out_outOfMemory = true; return false; } const size_t dstSize = checkedDstSize.value(); UniqueBuffer dstBuffer = malloc(dstSize); if (!dstBuffer) { *out_outOfMemory = true; return false; } void* const dstBegin = dstBuffer.get(); gl::OriginPos srcOrigin, dstOrigin; OriginsForDOM(webgl, &srcOrigin, &dstOrigin); const bool dstPremultiplied = webgl->mPixelStore_PremultiplyAlpha; // And go!: if (!ConvertImage(width, height, srcBegin, srcStride, srcOrigin, srcFormat, srcPremultiplied, dstBegin, dstStride, dstOrigin, dstFormat, dstPremultiplied)) { MOZ_ASSERT(false, "ConvertImage failed unexpectedly."); NS_ERROR("ConvertImage failed unexpectedly."); *out_outOfMemory = true; return false; } *out_convertedBuffer = Move(dstBuffer); *out_convertedAlignment = dstAlignment; return true; }
void TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName, WebGLTexture* tex, TexImageTarget target, GLint level, const webgl::DriverUnpackInfo* dui, GLint xOffset, GLint yOffset, GLint zOffset, GLenum* const out_glError) { WebGLContext* webgl = tex->mContext; gl::GLContext* gl = webgl->gl; const void* uploadBytes = mBytes; UniqueBuffer tempBuffer; do { if (!webgl->mPixelStore_FlipY && !webgl->mPixelStore_PremultiplyAlpha) break; if (!mBytes || !mWidth || !mHeight || !mDepth) break; if (webgl->IsWebGL2()) break; MOZ_ASSERT(mDepth == 1); // This is literally the worst. webgl->GenerateWarning("%s: Uploading ArrayBuffers with FLIP_Y or" " PREMULTIPLY_ALPHA is slow.", funcName); tempBuffer = malloc(mByteCount); if (!tempBuffer) { *out_glError = LOCAL_GL_OUT_OF_MEMORY; return; } const webgl::PackingInfo pi = { dui->unpackFormat, dui->unpackType }; const auto bytesPerPixel = webgl::BytesPerPixel(pi); const auto rowByteAlignment = webgl->mPixelStore_UnpackAlignment; const size_t bytesPerRow = bytesPerPixel * mWidth; const size_t rowStride = RoundUpToMultipleOf(bytesPerRow, rowByteAlignment); const bool needsYFlip = webgl->mPixelStore_FlipY; bool needsAlphaPremult = webgl->mPixelStore_PremultiplyAlpha; if (!UnpackFormatHasAlpha(pi.format)) needsAlphaPremult = false; if (!needsAlphaPremult) { if (!webgl->mPixelStore_FlipY) break; const uint8_t* src = (const uint8_t*)mBytes; const uint8_t* const srcEnd = src + rowStride * mHeight; uint8_t* dst = (uint8_t*)tempBuffer.get() + rowStride * (mHeight - 1); while (src != srcEnd) { memcpy(dst, src, bytesPerRow); src += rowStride; dst -= rowStride; } uploadBytes = tempBuffer.get(); break; } const auto texelFormat = FormatFromPacking(pi); if (texelFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion) { MOZ_ASSERT(false, "Bad texelFormat from pi."); *out_glError = LOCAL_GL_OUT_OF_MEMORY; return; } const auto srcOrigin = gl::OriginPos::BottomLeft; const auto dstOrigin = (needsYFlip ? gl::OriginPos::TopLeft : gl::OriginPos::BottomLeft); const bool srcPremultiplied = false; const bool dstPremultiplied = needsAlphaPremult; // Always true here. // And go!: if (!ConvertImage(mWidth, mHeight, mBytes, rowStride, srcOrigin, texelFormat, srcPremultiplied, tempBuffer.get(), rowStride, dstOrigin, texelFormat, dstPremultiplied)) { MOZ_ASSERT(false, "ConvertImage failed unexpectedly."); *out_glError = LOCAL_GL_OUT_OF_MEMORY; return; } uploadBytes = tempBuffer.get(); } while (false); GLenum error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth, uploadBytes); *out_glError = error; }
static bool ZeroTextureData(const WebGLContext* webgl, GLuint tex, TexImageTarget target, uint32_t level, const webgl::FormatUsageInfo* usage, uint32_t width, uint32_t height, uint32_t depth) { // This has two usecases: // 1. Lazy zeroing of uninitialized textures: // a. Before draw. // b. Before partial upload. (TexStorage + TexSubImage) // 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image) // We have no sympathy for any of these cases. // "Doctor, it hurts when I do this!" "Well don't do that!" const auto targetStr = EnumString(target.get()); webgl->GeneratePerfWarning( "Tex image %s level %u is incurring lazy initialization.", targetStr.c_str(), level); gl::GLContext* gl = webgl->GL(); GLenum scopeBindTarget; switch (target.get()) { case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: scopeBindTarget = LOCAL_GL_TEXTURE_CUBE_MAP; break; default: scopeBindTarget = target.get(); break; } const gl::ScopedBindTexture scopeBindTexture(gl, tex, scopeBindTarget); const auto& compression = usage->format->compression; if (compression) { auto sizedFormat = usage->format->sizedFormat; MOZ_RELEASE_ASSERT(sizedFormat, "GFX: texture sized format not set"); const auto fnSizeInBlocks = [](CheckedUint32 pixels, uint8_t pixelsPerBlock) { return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock; }; const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth); const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight); CheckedUint32 checkedByteCount = compression->bytesPerBlock; checkedByteCount *= widthBlocks; checkedByteCount *= heightBlocks; checkedByteCount *= depth; if (!checkedByteCount.isValid()) return false; const size_t byteCount = checkedByteCount.value(); UniqueBuffer zeros = calloc(1, byteCount); if (!zeros) return false; ScopedUnpackReset scopedReset(webgl); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with // striding it well. const auto error = DoCompressedTexSubImage(gl, target.get(), level, 0, 0, 0, width, height, depth, sizedFormat, byteCount, zeros.get()); return !error; } const auto driverUnpackInfo = usage->idealUnpack; MOZ_RELEASE_ASSERT(driverUnpackInfo, "GFX: ideal unpack info not set."); if (usage->format->d) { // ANGLE_depth_texture does not allow uploads, so we have to clear. // (Restriction because of D3D9) // Also, depth resources are cleared to 1.0f and are always renderable, so // just use FB clears. return ClearDepthTexture(*webgl, tex, target, level, usage, depth); } const webgl::PackingInfo packing = driverUnpackInfo->ToPacking(); const auto bytesPerPixel = webgl::BytesPerPixel(packing); CheckedUint32 checkedByteCount = bytesPerPixel; checkedByteCount *= width; checkedByteCount *= height; checkedByteCount *= depth; if (!checkedByteCount.isValid()) return false; const size_t byteCount = checkedByteCount.value(); UniqueBuffer zeros = calloc(1, byteCount); if (!zeros) return false; ScopedUnpackReset scopedReset(webgl); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well. const auto error = DoTexSubImage(gl, target, level, 0, 0, 0, width, height, depth, packing, zeros.get()); return !error; }
void WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usage) { // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr // is like intptr_t. if (!CheckedInt<GLsizeiptr>(size).isValid()) return mContext->ErrorOutOfMemory("bad size"); if (!ValidateBufferUsageEnum(mContext, usage)) return; #ifdef XP_MACOSX // bug 790879 if (mContext->gl->WorkAroundDriverBugs() && size > INT32_MAX) { mContext->ErrorOutOfMemory("Allocation size too large."); return; } #endif const void* uploadData = data; UniqueBuffer newIndexCache; if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER && mContext->mNeedsIndexValidation) { newIndexCache = malloc(size); if (!newIndexCache) { mContext->ErrorOutOfMemory("Failed to alloc index cache."); return; } memcpy(newIndexCache.get(), data, size); uploadData = newIndexCache.get(); } const auto& gl = mContext->gl; const ScopedLazyBind lazyBind(gl, target, this); const bool sizeChanges = (size != ByteLength()); if (sizeChanges) { gl::GLContext::LocalErrorScope errorScope(*gl); gl->fBufferData(target, size, uploadData, usage); const auto error = errorScope.GetError(); if (error) { MOZ_ASSERT(error == LOCAL_GL_OUT_OF_MEMORY); mContext->ErrorOutOfMemory("Error from driver: 0x%04x", error); return; } } else { gl->fBufferData(target, size, uploadData, usage); } mContext->OnDataAllocCall(); mUsage = usage; mByteLength = size; mFetchInvalidator.InvalidateCaches(); mIndexCache = std::move(newIndexCache); if (mIndexCache) { if (!mIndexRanges.empty()) { mContext->GeneratePerfWarning("[%p] Invalidating %u ranges.", this, uint32_t(mIndexRanges.size())); mIndexRanges.clear(); } } ResetLastUpdateFenceId(); }