static bool ValidateUnpackPixels(WebGLContext* webgl, const char* funcName, uint32_t fullRows, uint32_t tailPixels, webgl::TexUnpackBlob* blob) { if (!blob->mWidth || !blob->mHeight || !blob->mDepth) return true; const auto usedPixelsPerRow = CheckedUint32(blob->mSkipPixels) + blob->mWidth; if (!usedPixelsPerRow.isValid() || usedPixelsPerRow.value() > blob->mRowLength) { webgl->ErrorInvalidOperation("%s: UNPACK_SKIP_PIXELS + width >" " UNPACK_ROW_LENGTH.", funcName); return false; } if (blob->mHeight > blob->mImageHeight) { webgl->ErrorInvalidOperation("%s: height > UNPACK_IMAGE_HEIGHT.", funcName); return false; } ////// // The spec doesn't bound SKIP_ROWS + height <= IMAGE_HEIGHT, unfortunately. auto skipFullRows = CheckedUint32(blob->mSkipImages) * blob->mImageHeight; skipFullRows += blob->mSkipRows; MOZ_ASSERT(blob->mDepth >= 1); MOZ_ASSERT(blob->mHeight >= 1); auto usedFullRows = CheckedUint32(blob->mDepth - 1) * blob->mImageHeight; usedFullRows += blob->mHeight - 1; // Full rows in the final image, excluding the tail. const auto fullRowsNeeded = skipFullRows + usedFullRows; if (!fullRowsNeeded.isValid()) { webgl->ErrorOutOfMemory("%s: Invalid calculation for required row count.", funcName); return false; } if (fullRows > fullRowsNeeded.value()) return true; if (fullRows == fullRowsNeeded.value() && tailPixels >= usedPixelsPerRow.value()) { blob->mNeedsExactUpload = true; return true; } webgl->ErrorInvalidOperation("%s: Desired upload requires more data than is" " available: (%u rows plus %u pixels needed, %u rows" " plus %u pixels available)", funcName, fullRowsNeeded.value(), usedPixelsPerRow.value(), fullRows, tailPixels); return false; }
nsresult nsRawReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime) { NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); MediaResource *resource = mDecoder->GetResource(); NS_ASSERTION(resource, "Decoder has no media resource"); uint32_t frame = mCurrentFrame; if (aTime >= UINT_MAX) return NS_ERROR_FAILURE; mCurrentFrame = aTime * mFrameRate / USECS_PER_S; CheckedUint32 offset = CheckedUint32(mCurrentFrame) * mFrameSize; offset += sizeof(nsRawVideoHeader); NS_ENSURE_TRUE(offset.isValid(), NS_ERROR_FAILURE); nsresult rv = resource->Seek(nsISeekableStream::NS_SEEK_SET, offset.value()); NS_ENSURE_SUCCESS(rv, rv); mVideoQueue.Erase(); while(mVideoQueue.GetSize() == 0) { bool keyframeSkip = false; if (!DecodeVideoFrame(keyframeSkip, 0)) { mCurrentFrame = frame; return NS_ERROR_FAILURE; } { mozilla::ReentrantMonitorAutoEnter autoMonitor(mDecoder->GetReentrantMonitor()); if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) { mCurrentFrame = frame; return NS_ERROR_FAILURE; } } nsAutoPtr<VideoData> video(mVideoQueue.PeekFront()); if (video && video->mEndTime < aTime) { mVideoQueue.PopFront(); video = nullptr; } else { video.forget(); } } return NS_OK; }
void AudioStream::GetTimeStretched(AudioBufferWriter& aWriter) { mMonitor.AssertCurrentThreadOwns(); // We need to call the non-locking version, because we already have the lock. if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) { return; } uint32_t toPopFrames = ceil(aWriter.Available() * mAudioClock.GetPlaybackRate()); while (mTimeStretcher->numSamples() < aWriter.Available()) { UniquePtr<Chunk> c = mDataSource.PopFrames(toPopFrames); if (c->Frames() == 0) { break; } MOZ_ASSERT(c->Frames() <= toPopFrames); if (IsValidAudioFormat(c.get())) { mTimeStretcher->putSamples(c->Data(), c->Frames()); } else { // Write silence if invalid format. AutoTArray<AudioDataValue, 1000> buf; auto size = CheckedUint32(mOutChannels) * c->Frames(); if (!size.isValid()) { // The overflow should not happen in normal case. LOGW("Invalid member data: %d channels, %d frames", mOutChannels, c->Frames()); return; } buf.SetLength(size.value()); size = size * sizeof(AudioDataValue); if (!size.isValid()) { LOGW("The required memory size is too large."); return; } memset(buf.Elements(), 0, size.value()); mTimeStretcher->putSamples(buf.Elements(), c->Frames()); } } auto timeStretcher = mTimeStretcher; aWriter.Write([timeStretcher] (AudioDataValue* aPtr, uint32_t aFrames) { return timeStretcher->receiveSamples(aPtr, aFrames); }, aWriter.Available()); }
static bool ValidateUnpackBytes(WebGLContext* webgl, const char* funcName, const webgl::PackingInfo& pi, size_t availByteCount, webgl::TexUnpackBlob* blob) { if (!blob->mWidth || !blob->mHeight || !blob->mDepth) return true; const auto bytesPerPixel = webgl::BytesPerPixel(pi); const auto bytesPerRow = CheckedUint32(blob->mRowLength) * bytesPerPixel; const auto rowStride = RoundUpToMultipleOf(bytesPerRow, blob->mAlignment); const auto fullRows = availByteCount / rowStride; if (!fullRows.isValid()) { webgl->ErrorOutOfMemory("%s: Unacceptable upload size calculated.", funcName); return false; } const auto bodyBytes = fullRows.value() * rowStride.value(); const auto tailPixels = (availByteCount - bodyBytes) / bytesPerPixel; return ValidateUnpackPixels(webgl, funcName, fullRows.value(), tailPixels, blob); }
bool 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, const webgl::PackingInfo& pi, GLenum* const out_error) const { WebGLContext* webgl = tex->mContext; const auto format = FormatForPackingInfo(pi); const auto bytesPerPixel = webgl::BytesPerPixel(pi); const uint8_t* uploadPtr = mPtr; UniqueBuffer tempBuffer; do { if (!mIsClientData || !mPtr) break; if (!webgl->mPixelStore_FlipY && !webgl->mPixelStore_PremultiplyAlpha) { break; } if (webgl->mPixelStore_UnpackImageHeight || webgl->mPixelStore_UnpackSkipImages || webgl->mPixelStore_UnpackRowLength || webgl->mPixelStore_UnpackSkipRows || webgl->mPixelStore_UnpackSkipPixels) { webgl->ErrorInvalidOperation("%s: Non-DOM-Element uploads with alpha-premult" " or y-flip do not support subrect selection.", funcName); return false; } webgl->GenerateWarning("%s: Alpha-premult and y-flip are deprecated for" " non-DOM-Element uploads.", funcName); const uint32_t rowLength = mWidth; const uint32_t rowCount = mHeight * mDepth; const auto stride = RoundUpToMultipleOf(rowLength * bytesPerPixel, mAlignment); if (!ConvertIfNeeded(webgl, funcName, rowLength, rowCount, format, mPtr, stride, format, stride, &uploadPtr, &tempBuffer)) { return false; } } while (false); ////// const auto& gl = webgl->gl; bool useParanoidHandling = false; if (mNeedsExactUpload && webgl->mBoundPixelUnpackBuffer) { webgl->GenerateWarning("%s: Uploads from a buffer with a final row with a byte" " count smaller than the row stride can incur extra" " overhead.", funcName); if (gl->WorkAroundDriverBugs()) { useParanoidHandling |= (gl->Vendor() == gl::GLVendor::NVIDIA); } } if (!useParanoidHandling) { if (webgl->mBoundPixelUnpackBuffer) { gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, webgl->mBoundPixelUnpackBuffer->mGLName); } *out_error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth, uploadPtr); if (webgl->mBoundPixelUnpackBuffer) { gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0); } return true; } ////// MOZ_ASSERT(webgl->mBoundPixelUnpackBuffer); if (!isSubImage) { // Alloc first to catch OOMs. AssertUintParamCorrect(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER, 0); *out_error = DoTexOrSubImage(false, gl, target, level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth, nullptr); if (*out_error) return true; } const ScopedLazyBind bindPBO(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER, webgl->mBoundPixelUnpackBuffer); ////// // Make our sometimes-implicit values explicit. Also this keeps them constant when we // ask for height=mHeight-1 and such. gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, mRowLength); gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, mImageHeight); if (mDepth > 1) { *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset, zOffset, mWidth, mHeight, mDepth-1, uploadPtr); } // Skip the images we uploaded. gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, mSkipImages + mDepth - 1); if (mHeight > 1) { *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset, zOffset+mDepth-1, mWidth, mHeight-1, 1, uploadPtr); } const auto totalSkipRows = CheckedUint32(mSkipImages) * mImageHeight + mSkipRows; const auto totalFullRows = CheckedUint32(mDepth - 1) * mImageHeight + mHeight - 1; const auto tailOffsetRows = totalSkipRows + totalFullRows; const auto bytesPerRow = CheckedUint32(mRowLength) * bytesPerPixel; const auto rowStride = RoundUpToMultipleOf(bytesPerRow, mAlignment); if (!rowStride.isValid()) { MOZ_CRASH("Should be checked earlier."); } const auto tailOffsetBytes = tailOffsetRows * rowStride; uploadPtr += tailOffsetBytes.value(); ////// gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // No stride padding. gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); // No padding in general. gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, 0); // Don't skip images, gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, 0); // or rows. // Keep skipping pixels though! *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset+mHeight-1, zOffset+mDepth-1, mWidth, 1, 1, uploadPtr); // Reset all our modified state. gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, webgl->mPixelStore_UnpackAlignment); gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, webgl->mPixelStore_UnpackImageHeight); gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, webgl->mPixelStore_UnpackRowLength); gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, webgl->mPixelStore_UnpackSkipImages); gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, webgl->mPixelStore_UnpackSkipRows); return true; }
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; }
bool TexUnpackBlob::ConvertIfNeeded(WebGLContext* webgl, const char* funcName, const uint8_t* srcBytes, uint32_t srcStride, uint8_t srcBPP, WebGLTexelFormat srcFormat, const webgl::DriverUnpackInfo* dstDUI, const uint8_t** const out_bytes, UniqueBuffer* const out_anchoredBuffer) const { *out_bytes = srcBytes; if (!HasData() || !mWidth || !mHeight || !mDepth) return true; ////// const auto totalSkipRows = mSkipRows + CheckedUint32(mSkipImages) * mImageHeight; const auto offset = mSkipPixels * CheckedUint32(srcBPP) + totalSkipRows * srcStride; if (!offset.isValid()) { webgl->ErrorOutOfMemory("%s: Invalid offset calculation during conversion.", funcName); return false; } const uint32_t skipBytes = offset.value(); auto const srcBegin = srcBytes + skipBytes; ////// const auto srcOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft : gl::OriginPos::BottomLeft); const auto dstOrigin = gl::OriginPos::BottomLeft; const bool isDstPremult = webgl->mPixelStore_PremultiplyAlpha; const auto pi = dstDUI->ToPacking(); const auto dstBPP = webgl::BytesPerPixel(pi); const auto dstWidthBytes = CheckedUint32(dstBPP) * mWidth; const auto dstRowLengthBytes = CheckedUint32(dstBPP) * mRowLength; const auto dstAlignment = mAlignment; const auto dstStride = RoundUpToMultipleOf(dstRowLengthBytes, dstAlignment); ////// const auto dstTotalRows = CheckedUint32(mDepth - 1) * mImageHeight + mHeight; const auto dstUsedSizeExceptLastRow = (dstTotalRows - 1) * dstStride; const auto dstSize = skipBytes + dstUsedSizeExceptLastRow + dstWidthBytes; if (!dstSize.isValid()) { webgl->ErrorOutOfMemory("%s: Invalid dstSize calculation during conversion.", funcName); return false; } ////// const auto dstFormat = FormatForPackingInfo(pi); bool premultMatches = (mIsSrcPremult == isDstPremult); if (!UnpackFormatHasColorAndAlpha(dstDUI->unpackFormat)) { premultMatches = true; } const bool needsPixelConversion = (srcFormat != dstFormat || !premultMatches); const bool originsMatch = (srcOrigin == dstOrigin); MOZ_ASSERT_IF(!needsPixelConversion, srcBPP == dstBPP); if (!needsPixelConversion && originsMatch && srcStride == dstStride.value()) { // No conversion needed! return true; } ////// // We need some sort of conversion, so create the dest buffer. *out_anchoredBuffer = calloc(1, dstSize.value()); const auto dstBytes = (uint8_t*)out_anchoredBuffer->get(); if (!dstBytes) { webgl->ErrorOutOfMemory("%s: Unable to allocate buffer during conversion.", funcName); return false; } *out_bytes = dstBytes; const auto dstBegin = dstBytes + skipBytes; ////// // Row conversion if (!needsPixelConversion) { webgl->GenerateWarning("%s: Incurred CPU row conversion, which is slow.", funcName); const uint8_t* srcRow = srcBegin; uint8_t* dstRow = dstBegin; const auto widthBytes = dstWidthBytes.value(); ptrdiff_t dstCopyStride = dstStride.value(); if (!originsMatch) { dstRow += dstUsedSizeExceptLastRow.value(); dstCopyStride = -dstCopyStride; } for (uint32_t i = 0; i < dstTotalRows.value(); i++) { memcpy(dstRow, srcRow, widthBytes); srcRow += srcStride; dstRow += dstCopyStride; } return true; } //////////// // Pixel conversion. MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion); MOZ_ASSERT(dstFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion); webgl->GenerateWarning("%s: Incurred CPU pixel conversion, which is very slow.", funcName); ////// // And go!: bool wasTrivial; if (!ConvertImage(mWidth, dstTotalRows.value(), srcBegin, srcStride, srcOrigin, srcFormat, mIsSrcPremult, dstBegin, dstStride.value(), dstOrigin, dstFormat, isDstPremult, &wasTrivial)) { webgl->ErrorImplementationBug("%s: ConvertImage failed.", funcName); return false; } if (!wasTrivial) { webgl->GenerateWarning("%s: Chosen format/type incurred an expensive reformat:" " 0x%04x/0x%04x", funcName, dstDUI->unpackFormat, dstDUI->unpackType); } return true; }
bool WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount) { WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need(); if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default)) return true; if (!mAlreadyWarnedAboutFakeVertexAttrib0) { GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser " "to do expensive emulation work when running on desktop OpenGL " "platforms, for example on Mac. It is preferable to always draw " "with vertex attrib 0 array enabled, by using bindAttribLocation " "to bind some always-used attribute to location 0."); mAlreadyWarnedAboutFakeVertexAttrib0 = true; } CheckedUint32 checked_dataSize = CheckedUint32(vertexCount) * 4 * sizeof(GLfloat); if (!checked_dataSize.isValid()) { ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation " "with %d vertices. Try reducing the number of vertices.", vertexCount); return false; } GLuint dataSize = checked_dataSize.value(); if (!mFakeVertexAttrib0BufferObject) { gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject); } // if the VBO status is already exactly what we need, or if the only difference is that it's initialized and // we don't need it to be, then consider it OK bool vertexAttrib0BufferStatusOK = mFakeVertexAttrib0BufferStatus == whatDoesAttrib0Need || (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray && whatDoesAttrib0Need == WebGLVertexAttrib0Status::EmulatedUninitializedArray); if (!vertexAttrib0BufferStatusOK || mFakeVertexAttrib0BufferObjectSize < dataSize || mFakeVertexAttrib0BufferObjectVector[0] != mVertexAttrib0Vector[0] || mFakeVertexAttrib0BufferObjectVector[1] != mVertexAttrib0Vector[1] || mFakeVertexAttrib0BufferObjectVector[2] != mVertexAttrib0Vector[2] || mFakeVertexAttrib0BufferObjectVector[3] != mVertexAttrib0Vector[3]) { mFakeVertexAttrib0BufferStatus = whatDoesAttrib0Need; mFakeVertexAttrib0BufferObjectSize = dataSize; mFakeVertexAttrib0BufferObjectVector[0] = mVertexAttrib0Vector[0]; mFakeVertexAttrib0BufferObjectVector[1] = mVertexAttrib0Vector[1]; mFakeVertexAttrib0BufferObjectVector[2] = mVertexAttrib0Vector[2]; mFakeVertexAttrib0BufferObjectVector[3] = mVertexAttrib0Vector[3]; gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject); GetAndFlushUnderlyingGLErrors(); if (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray) { auto array = MakeUniqueFallible<GLfloat[]>(4 * vertexCount); if (!array) { ErrorOutOfMemory("Fake attrib0 array."); return false; } for(size_t i = 0; i < vertexCount; ++i) { array[4 * i + 0] = mVertexAttrib0Vector[0]; array[4 * i + 1] = mVertexAttrib0Vector[1]; array[4 * i + 2] = mVertexAttrib0Vector[2]; array[4 * i + 3] = mVertexAttrib0Vector[3]; } gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, array.get(), LOCAL_GL_DYNAMIC_DRAW); } else { gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW); } GLenum error = GetAndFlushUnderlyingGLErrors(); gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0); // note that we do this error checking and early return AFTER having restored the buffer binding above if (error) { ErrorOutOfMemory("Ran out of memory trying to construct a fake vertex attrib 0 array for a draw-operation " "with %d vertices. Try reducing the number of vertices.", vertexCount); return false; } } gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject); gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0); return true; }
bool WebGLContext::ValidateBufferFetching(const char* info) { MOZ_ASSERT(mCurrentProgram); // Note that mCurrentProgram->IsLinked() is NOT GUARANTEED. MOZ_ASSERT(mActiveProgramLinkInfo); #ifdef DEBUG GLint currentProgram = 0; MakeContextCurrent(); gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, ¤tProgram); MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->mGLName, "WebGL: current program doesn't agree with GL state"); #endif if (mBufferFetchingIsVerified) return true; bool hasPerVertex = false; uint32_t maxVertices = UINT32_MAX; uint32_t maxInstances = UINT32_MAX; uint32_t attribs = mBoundVertexArray->mAttribs.Length(); for (uint32_t i = 0; i < attribs; ++i) { const WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[i]; // If the attrib array isn't enabled, there's nothing to check; // it's a static value. if (!vd.enabled) continue; if (vd.buf == nullptr) { ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i); return false; } // If the attrib is not in use, then we don't have to validate // it, just need to make sure that the binding is non-null. if (!mActiveProgramLinkInfo->HasActiveAttrib(i)) continue; // the base offset CheckedUint32 checked_byteLength = CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset; CheckedUint32 checked_sizeOfLastElement = CheckedUint32(vd.componentSize()) * vd.size; if (!checked_byteLength.isValid() || !checked_sizeOfLastElement.isValid()) { ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i); return false; } if (checked_byteLength.value() < checked_sizeOfLastElement.value()) { maxVertices = 0; maxInstances = 0; break; } CheckedUint32 checked_maxAllowedCount = ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1; if (!checked_maxAllowedCount.isValid()) { ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i); return false; } if (vd.divisor == 0) { maxVertices = std::min(maxVertices, checked_maxAllowedCount.value()); hasPerVertex = true; } else { CheckedUint32 checked_curMaxInstances = checked_maxAllowedCount * vd.divisor; uint32_t curMaxInstances = UINT32_MAX; // If this isn't valid, it's because we overflowed our // uint32 above. Just leave this as UINT32_MAX, since // sizeof(uint32) becomes our limiting factor. if (checked_curMaxInstances.isValid()) { curMaxInstances = checked_curMaxInstances.value(); } maxInstances = std::min(maxInstances, curMaxInstances); } } mBufferFetchingIsVerified = true; mBufferFetchingHasPerVertex = hasPerVertex; mMaxFetchedVertices = maxVertices; mMaxFetchedInstances = maxInstances; return true; }
bool WebGLContext::DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOffset, GLsizei primcount, const char* info, GLuint* out_upperBound) { if (count < 0 || byteOffset < 0) { ErrorInvalidValue("%s: negative count or offset", info); return false; } if (primcount < 0) { ErrorInvalidValue("%s: negative primcount", info); return false; } if (!ValidateStencilParamsForDrawCall()) { return false; } // If count is 0, there's nothing to do. if (count == 0 || primcount == 0) return false; uint8_t bytesPerElem = 0; switch (type) { case LOCAL_GL_UNSIGNED_BYTE: bytesPerElem = 1; break; case LOCAL_GL_UNSIGNED_SHORT: bytesPerElem = 2; break; case LOCAL_GL_UNSIGNED_INT: if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) { bytesPerElem = 4; } break; } if (!bytesPerElem) { ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", info, type); return false; } if (byteOffset % bytesPerElem != 0) { ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`", info); return false; } const GLsizei first = byteOffset / bytesPerElem; const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(count); if (!checked_byteCount.isValid()) { ErrorInvalidValue("%s: overflow in byteCount", info); return false; } // Any checks below this depend on a program being available. if (!mCurrentProgram) { ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info); return false; } if (!mBoundVertexArray->mElementArrayBuffer) { ErrorInvalidOperation("%s: must have element array buffer binding", info); return false; } WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mElementArrayBuffer; if (!elemArrayBuffer.ByteLength()) { ErrorInvalidOperation("%s: bound element array buffer doesn't have any data", info); return false; } CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + byteOffset; if (!checked_neededByteCount.isValid()) { ErrorInvalidOperation("%s: overflow in byteOffset+byteCount", info); return false; } if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) { ErrorInvalidOperation("%s: bound element array buffer is too small for given count and offset", info); return false; } if (!ValidateBufferFetching(info)) return false; if (!mMaxFetchedVertices || !elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, count, out_upperBound)) { ErrorInvalidOperation( "%s: bound vertex attribute buffers do not have sufficient " "size for given indices from the bound element array", info); return false; } if (uint32_t(primcount) > mMaxFetchedInstances) { ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info); return false; } // Bug 1008310 - Check if buffer has been used with a different previous type if (elemArrayBuffer.IsElementArrayUsedWithMultipleTypes()) { GenerateWarning("%s: bound element array buffer previously used with a type other than " "%s, this will affect performance.", info, WebGLContext::EnumName(type)); } MOZ_ASSERT(gl->IsCurrent()); if (mBoundDrawFramebuffer) { if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(info)) return false; } else { ClearBackbufferIfNeeded(); } if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) { return false; } return true; }
bool SkeletonState::DecodeFisbone(ogg_packet* aPacket) { if (aPacket->bytes < static_cast<long>(FISBONE_MSG_FIELDS_OFFSET + 4)) { return false; } uint32_t offsetMsgField = LittleEndian::readUint32(aPacket->packet + FISBONE_MSG_FIELDS_OFFSET); if (aPacket->bytes < static_cast<long>(FISBONE_SERIALNO_OFFSET + 4)) { return false; } uint32_t serialno = LittleEndian::readUint32(aPacket->packet + FISBONE_SERIALNO_OFFSET); CheckedUint32 checked_fields_pos = CheckedUint32(FISBONE_MSG_FIELDS_OFFSET) + offsetMsgField; if (!checked_fields_pos.isValid() || aPacket->bytes < static_cast<int64_t>(checked_fields_pos.value())) { return false; } int64_t msgLength = aPacket->bytes - checked_fields_pos.value(); char* msgProbe = (char*)aPacket->packet + checked_fields_pos.value(); char* msgHead = msgProbe; nsAutoPtr<MessageField> field(new MessageField()); const static FieldPatternType kFieldTypeMaps[] = { {"Content-Type:", eContentType}, {"Role:", eRole}, {"Name:", eName}, {"Language:", eLanguage}, {"Title:", eTitle}, {"Display-hint:", eDisplayHint}, {"Altitude:", eAltitude}, {"TrackOrder:", eTrackOrder}, {"Track dependencies:", eTrackDependencies} }; bool isContentTypeParsed = false; while (msgLength > 1) { if (*msgProbe == '\r' && *(msgProbe+1) == '\n') { nsAutoCString strMsg(msgHead, msgProbe-msgHead); for (size_t i = 0; i < ArrayLength(kFieldTypeMaps); i++) { if (strMsg.Find(kFieldTypeMaps[i].mPatternToRecognize) != -1) { // The content of message header fields follows [RFC2822], and the // mandatory message field must be encoded in US-ASCII, others // must be be encoded in UTF-8. "Content-Type" must come first // for all of message header fields. // See http://svn.annodex.net/standards/draft-pfeiffer-oggskeleton-current.txt. if (i != 0 && !isContentTypeParsed) { return false; } if ((i == 0 && IsASCII(strMsg)) || (i != 0 && IsUTF8(strMsg))) { EMsgHeaderType eHeaderType = kFieldTypeMaps[i].mMsgHeaderType; if (!field->mValuesStore.Contains(eHeaderType)) { uint32_t nameLen = strlen(kFieldTypeMaps[i].mPatternToRecognize); field->mValuesStore.Put(eHeaderType, new nsCString(msgHead+nameLen, msgProbe-msgHead-nameLen)); } isContentTypeParsed = i==0 ? true : isContentTypeParsed; } break; } } msgProbe += 2; msgLength -= 2; msgHead = msgProbe; continue; } msgLength--; msgProbe++; }; if (!mMsgFieldStore.Contains(serialno)) { mMsgFieldStore.Put(serialno, field.forget()); } else { return false; } return true; }
nsresult nsRawReader::ReadMetadata(nsVideoInfo* aInfo, nsHTMLMediaElement::MetadataTags** aTags) { NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); MediaResource* resource = mDecoder->GetResource(); NS_ASSERTION(resource, "Decoder has no media resource"); if (!ReadFromResource(resource, reinterpret_cast<uint8_t*>(&mMetadata), sizeof(mMetadata))) return NS_ERROR_FAILURE; // Validate the header if (!(mMetadata.headerPacketID == 0 /* Packet ID of 0 for the header*/ && mMetadata.codecID == RAW_ID /* "YUV" */ && mMetadata.majorVersion == 0 && mMetadata.minorVersion == 1)) return NS_ERROR_FAILURE; CheckedUint32 dummy = CheckedUint32(static_cast<uint32_t>(mMetadata.frameWidth)) * static_cast<uint32_t>(mMetadata.frameHeight); NS_ENSURE_TRUE(dummy.isValid(), NS_ERROR_FAILURE); if (mMetadata.aspectDenominator == 0 || mMetadata.framerateDenominator == 0) return NS_ERROR_FAILURE; // Invalid data // Determine and verify frame display size. float pixelAspectRatio = static_cast<float>(mMetadata.aspectNumerator) / mMetadata.aspectDenominator; nsIntSize display(mMetadata.frameWidth, mMetadata.frameHeight); ScaleDisplayByAspectRatio(display, pixelAspectRatio); mPicture = nsIntRect(0, 0, mMetadata.frameWidth, mMetadata.frameHeight); nsIntSize frameSize(mMetadata.frameWidth, mMetadata.frameHeight); if (!nsVideoInfo::ValidateVideoRegion(frameSize, mPicture, display)) { // Video track's frame sizes will overflow. Fail. return NS_ERROR_FAILURE; } mInfo.mHasVideo = true; mInfo.mHasAudio = false; mInfo.mDisplay = display; mFrameRate = static_cast<float>(mMetadata.framerateNumerator) / mMetadata.framerateDenominator; // Make some sanity checks if (mFrameRate > 45 || mFrameRate == 0 || pixelAspectRatio == 0 || mMetadata.frameWidth > 2000 || mMetadata.frameHeight > 2000 || mMetadata.chromaChannelBpp != 4 || mMetadata.lumaChannelBpp != 8 || mMetadata.colorspace != 1 /* 4:2:0 */) return NS_ERROR_FAILURE; mFrameSize = mMetadata.frameWidth * mMetadata.frameHeight * (mMetadata.lumaChannelBpp + mMetadata.chromaChannelBpp) / 8.0 + sizeof(nsRawPacketHeader); int64_t length = resource->GetLength(); if (length != -1) { mozilla::ReentrantMonitorAutoEnter autoMonitor(mDecoder->GetReentrantMonitor()); mDecoder->GetStateMachine()->SetDuration(USECS_PER_S * (length - sizeof(nsRawVideoHeader)) / (mFrameSize * mFrameRate)); } *aInfo = mInfo; *aTags = nullptr; return NS_OK; }