From d6af1e81ba1530ec570496ec397613eb37f95c89 Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Fri, 20 Oct 2017 15:40:12 -0700 Subject: [PATCH] Bug 1404196 - Simplify and repair vertex fetch. - r=daoshengmu MozReview-Commit-ID: FL7uibuv4VY --- dom/canvas/WebGLBuffer.cpp | 83 +++-- dom/canvas/WebGLBuffer.h | 22 +- dom/canvas/WebGLContext.cpp | 33 +- dom/canvas/WebGLContext.h | 35 +- dom/canvas/WebGLContextBuffers.cpp | 2 - dom/canvas/WebGLContextDraw.cpp | 495 ++++++++----------------- dom/canvas/WebGLContextValidate.cpp | 1 + dom/canvas/WebGLContextVertexArray.cpp | 2 - dom/canvas/WebGLContextVertices.cpp | 17 +- dom/canvas/WebGLProgram.cpp | 105 +++++- dom/canvas/WebGLProgram.h | 17 + dom/canvas/WebGLVertexArray.h | 6 + 12 files changed, 393 insertions(+), 425 deletions(-) diff --git a/dom/canvas/WebGLBuffer.cpp b/dom/canvas/WebGLBuffer.cpp index c278c2721513..70dcd85b427b 100644 --- a/dom/canvas/WebGLBuffer.cpp +++ b/dom/canvas/WebGLBuffer.cpp @@ -59,7 +59,10 @@ WebGLBuffer::Delete() { mContext->MakeContextCurrent(); mContext->gl->fDeleteBuffers(1, &mGLName); + mByteLength = 0; + mFetchInvalidator.InvalidateCaches(); + mIndexCache = nullptr; mIndexRanges.clear(); LinkedListElement::remove(); // remove from mContext->mBuffers @@ -138,8 +141,6 @@ WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usa const bool sizeChanges = (size != ByteLength()); if (sizeChanges) { - mContext->InvalidateBufferFetching(); - gl::GLContext::LocalErrorScope errorScope(*gl); gl->fBufferData(target, size, uploadData, usage); const auto error = errorScope.GetError(); @@ -157,6 +158,7 @@ WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usa mUsage = usage; mByteLength = size; + mFetchInvalidator.InvalidateCaches(); mIndexCache = Move(newIndexCache); if (mIndexCache) { @@ -234,18 +236,18 @@ IndexByteSizeByType(GLenum type) } void -WebGLBuffer::InvalidateCacheRange(size_t byteOffset, size_t byteLength) const +WebGLBuffer::InvalidateCacheRange(uint64_t byteOffset, uint64_t byteLength) const { MOZ_ASSERT(mIndexCache); std::vector invalids; - const size_t updateBegin = byteOffset; - const size_t updateEnd = updateBegin + byteLength; + const uint64_t updateBegin = byteOffset; + const uint64_t updateEnd = updateBegin + byteLength; for (const auto& cur : mIndexRanges) { const auto& range = cur.first; const auto& indexByteSize = IndexByteSizeByType(range.type); - const size_t rangeBegin = range.first * indexByteSize; - const size_t rangeEnd = rangeBegin + range.count*indexByteSize; + const auto rangeBegin = range.byteOffset * indexByteSize; + const auto rangeEnd = rangeBegin + uint64_t(range.indexCount) * indexByteSize; if (rangeBegin >= updateEnd || rangeEnd <= updateBegin) continue; invalids.push_back(range); @@ -273,48 +275,47 @@ WebGLBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const } template -static size_t -MaxForRange(const void* data, size_t first, size_t count, const uint32_t ignoredVal) +static Maybe +MaxForRange(const void* const start, const uint32_t count, + const Maybe& untypedIgnoredVal) { - const T ignoredTVal(ignoredVal); - T ret = 0; + const Maybe ignoredVal = (untypedIgnoredVal ? Some(T(untypedIgnoredVal.value())) + : Nothing()); + Maybe maxVal; - auto itr = (const T*)data + first; + auto itr = (const T*)start; const auto end = itr + count; for (; itr != end; ++itr) { const auto& val = *itr; - if (val <= ret) + if (ignoredVal && val == ignoredVal.value()) continue; - if (val == ignoredTVal) + if (maxVal && val <= maxVal.value()) continue; - ret = val; + maxVal = Some(val); } - return size_t(ret); + return maxVal; } -const uint32_t kMaxIndexRanges = 256; +static const uint32_t kMaxIndexRanges = 256; -bool -WebGLBuffer::ValidateIndexedFetch(GLenum type, uint32_t numFetchable, size_t first, - size_t count) const +Maybe +WebGLBuffer::GetIndexedFetchMaxVert(const GLenum type, const uint64_t byteOffset, + const uint32_t indexCount) const { if (!mIndexCache) - return true; + return Nothing(); - if (!count) - return true; - - const IndexRange range = { type, first, count }; - auto res = mIndexRanges.insert({ range, size_t(0) }); + const IndexRange range = { type, byteOffset, indexCount }; + auto res = mIndexRanges.insert({ range, Nothing() }); if (mIndexRanges.size() > kMaxIndexRanges) { mContext->GeneratePerfWarning("[%p] Clearing mIndexRanges after exceeding %u.", this, kMaxIndexRanges); mIndexRanges.clear(); - res = mIndexRanges.insert({ range, size_t(0) }); + res = mIndexRanges.insert({ range, Nothing() }); } const auto& itr = res.first; @@ -323,29 +324,37 @@ WebGLBuffer::ValidateIndexedFetch(GLenum type, uint32_t numFetchable, size_t fir auto& maxFetchIndex = itr->second; if (didInsert) { const auto& data = mIndexCache.get(); - const uint32_t ignoreVal = (mContext->IsWebGL2() ? UINT32_MAX : 0); + + const auto start = (const uint8_t*)data + byteOffset; + + Maybe ignoredVal; + if (mContext->IsWebGL2()) { + ignoredVal = Some(UINT32_MAX); + } switch (type) { case LOCAL_GL_UNSIGNED_BYTE: - maxFetchIndex = MaxForRange(data, first, count, ignoreVal); + maxFetchIndex = MaxForRange(start, indexCount, ignoredVal); break; case LOCAL_GL_UNSIGNED_SHORT: - maxFetchIndex = MaxForRange(data, first, count, ignoreVal); + maxFetchIndex = MaxForRange(start, indexCount, ignoredVal); break; case LOCAL_GL_UNSIGNED_INT: - maxFetchIndex = MaxForRange(data, first, count, ignoreVal); + maxFetchIndex = MaxForRange(start, indexCount, ignoredVal); break; default: MOZ_CRASH(); } - - mContext->GeneratePerfWarning("[%p] New range #%u: (0x%04x, %u, %u): %u", this, - uint32_t(mIndexRanges.size()), type, - uint32_t(first), uint32_t(count), - uint32_t(maxFetchIndex)); + const auto displayMaxVertIndex = maxFetchIndex ? int64_t(maxFetchIndex.value()) + : -1; + mContext->GeneratePerfWarning("[%p] New range #%u: (0x%04x, %" PRIu64 ", %u):" + " %" PRIi64, + this, uint32_t(mIndexRanges.size()), range.type, + range.byteOffset, range.indexCount, + displayMaxVertIndex); } - return maxFetchIndex < numFetchable; + return maxFetchIndex; } //// diff --git a/dom/canvas/WebGLBuffer.h b/dom/canvas/WebGLBuffer.h index 025ad78dc645..4c01b186656f 100644 --- a/dom/canvas/WebGLBuffer.h +++ b/dom/canvas/WebGLBuffer.h @@ -8,6 +8,7 @@ #include +#include "CacheMap.h" #include "GLDefs.h" #include "mozilla/LinkedList.h" #include "nsWrapperCache.h" @@ -44,7 +45,8 @@ public: GLenum Usage() const { return mUsage; } size_t ByteLength() const { return mByteLength; } - bool ValidateIndexedFetch(GLenum type, uint32_t max_allowed, size_t first, size_t count) const; + Maybe GetIndexedFetchMaxVert(GLenum type, uint64_t byteOffset, + uint32_t indexCount) const; bool ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const; WebGLContext* GetParentObject() const { @@ -67,6 +69,7 @@ public: if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) { MOZ_ASSERT_IF(addVal < 0, buffer->mTFBindCount >= size_t(-addVal)); buffer->mTFBindCount += addVal; + buffer->mFetchInvalidator.InvalidateCaches(); } else { MOZ_ASSERT_IF(addVal < 0, buffer->mNonTFBindCount >= size_t(-addVal)); buffer->mNonTFBindCount += addVal; @@ -95,7 +98,7 @@ public: protected: ~WebGLBuffer(); - void InvalidateCacheRange(size_t offset, size_t length) const; + void InvalidateCacheRange(uint64_t byteOffset, uint64_t byteLength) const; Kind mContent; GLenum mUsage; @@ -105,22 +108,25 @@ protected: struct IndexRange final { GLenum type; - size_t first; - size_t count; + uint64_t byteOffset; + uint32_t indexCount; bool operator<(const IndexRange& x) const { if (type != x.type) return type < x.type; - if (first != x.first) - return first < x.first; + if (byteOffset != x.byteOffset) + return byteOffset < x.byteOffset; - return count < x.count; + return indexCount < x.indexCount; } }; UniqueBuffer mIndexCache; - mutable std::map mIndexRanges; + mutable std::map> mIndexRanges; + +public: + CacheMapInvalidator mFetchInvalidator; }; } // namespace mozilla diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index 273f3cf72ce7..a9769348ccb1 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -112,10 +112,6 @@ WebGLContext::WebGLContext() , mNumPerfWarnings(0) , mMaxAcceptableFBStatusInvals(gfxPrefs::WebGLMaxAcceptableFBStatusInvals()) , mDataAllocGLCallCount(0) - , mBufferFetchingIsVerified(false) - , mBufferFetchingHasPerVertex(false) - , mMaxFetchedVertices(0) - , mMaxFetchedInstances(0) , mBypassShaderValidation(false) , mEmptyTFO(0) , mContextLossHandler(this) @@ -184,8 +180,6 @@ WebGLContext::WebGLContext() mLastUseIndex = 0; - InvalidateBufferFetching(); - mDisableFragHighP = false; mDrawCallsSinceLastFlush = 0; @@ -2303,6 +2297,27 @@ Intersect(const int32_t srcSize, const int32_t read0, const int32_t readSize, return true; } +// -- + +uint64_t +AvailGroups(const uint64_t totalAvailItems, const uint64_t firstItemOffset, + const uint32_t groupSize, const uint32_t groupStride) +{ + MOZ_ASSERT(groupSize && groupStride); + MOZ_ASSERT(groupSize <= groupStride); + + if (totalAvailItems <= firstItemOffset) + return 0; + const size_t availItems = totalAvailItems - firstItemOffset; + + size_t availGroups = availItems / groupStride; + const size_t tailItems = availItems % groupStride; + if (tailItems >= groupSize) { + availGroups += 1; + } + return availGroups; +} + //////////////////////////////////////////////////////////////////////////////// CheckedUint32 @@ -2418,8 +2433,7 @@ WebGLContext::ValidateArrayBufferView(const char* funcName, return true; } -//////////////////////////////////////////////////////////////////////////////// -// XPCOM goop +//// void WebGLContext::UpdateMaxDrawBuffers() @@ -2434,6 +2448,9 @@ WebGLContext::UpdateMaxDrawBuffers() mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mGLMaxColorAttachments); } +//////////////////////////////////////////////////////////////////////////////// +// XPCOM goop + void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback, const std::vector& field, diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index fabb3467b36a..e4dbf70ea91b 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -34,6 +34,7 @@ #endif // Local +#include "CacheMap.h" #include "WebGLContextLossHandler.h" #include "WebGLContextUnchecked.h" #include "WebGLFormats.h" @@ -293,6 +294,7 @@ class WebGLContext friend class WebGLExtensionLoseContext; friend class WebGLExtensionVertexArray; friend class WebGLMemoryTracker; + friend struct webgl::LinkedProgramInfo; friend struct webgl::UniformBlockInfo; enum { @@ -1390,20 +1392,11 @@ public: void VertexAttribDivisor(GLuint index, GLuint divisor); private: - // Cache the max number of vertices and instances that can be read from - // bound VBOs (result of ValidateBuffers). - bool mBufferFetchingIsVerified; - bool mBufferFetchingHasPerVertex; - uint32_t mMaxFetchedVertices; - uint32_t mMaxFetchedInstances; - bool mBufferFetch_IsAttrib0Active; - - bool DrawArrays_check(const char* funcName, GLenum mode, GLint first, - GLsizei vertCount, GLsizei instanceCount); - bool DrawElements_check(const char* funcName, GLenum mode, GLsizei vertCount, - GLenum type, WebGLintptr byteOffset, - GLsizei instanceCount); - bool DrawInstanced_check(const char* info); + bool DrawArrays_check(const char* funcName, GLint first, GLsizei vertCount, + GLsizei instanceCount, Maybe* out_lastVert); + bool DrawElements_check(const char* funcName, GLsizei indexCount, GLenum type, + WebGLintptr byteOffset, GLsizei instanceCount, + Maybe* out_lastVert); void Draw_cleanup(const char* funcName); void VertexAttrib1fv_base(GLuint index, uint32_t arrayLength, @@ -1415,7 +1408,6 @@ private: void VertexAttrib4fv_base(GLuint index, uint32_t arrayLength, const GLfloat* ptr); - bool ValidateBufferFetching(const char* info); bool BindArrayAttribToLocation0(WebGLProgram* prog); // ----------------------------------------------------------------------------- @@ -1425,14 +1417,6 @@ protected: bool DoFakeVertexAttrib0(const char* funcName, GLuint vertexCount); void UndoFakeVertexAttrib0(); - inline void InvalidateBufferFetching() - { - mBufferFetchingIsVerified = false; - mBufferFetchingHasPerVertex = false; - mMaxFetchedVertices = 0; - mMaxFetchedInstances = 0; - } - CheckedUint32 mGeneration; WebGLContextOptions mOptions; @@ -1927,6 +1911,7 @@ protected: // useful to vertex shaders, but is global state. UniquePtr mGenericVertexAttribTypes; uint8_t mGenericVertexAttrib0Data[sizeof(float) * 4]; + CacheMapInvalidator mGenericVertexAttribTypeInvalidator; GLuint mFakeVertexAttrib0BufferObject; size_t mFakeVertexAttrib0BufferObjectSize; @@ -2189,6 +2174,10 @@ bool Intersect(int32_t srcSize, int32_t read0, int32_t readSize, int32_t* out_intRead0, int32_t* out_intWrite0, int32_t* out_intSize); +uint64_t +AvailGroups(uint64_t totalAvailItems, uint64_t firstItemOffset, uint32_t groupSize, + uint32_t groupStride); + //// void diff --git a/dom/canvas/WebGLContextBuffers.cpp b/dom/canvas/WebGLContextBuffers.cpp index 381884cc0519..776b6c883de2 100644 --- a/dom/canvas/WebGLContextBuffers.cpp +++ b/dom/canvas/WebGLContextBuffers.cpp @@ -501,8 +501,6 @@ WebGLContext::DeleteBuffer(WebGLBuffer* buffer) //// buffer->RequestDelete(); - - InvalidateBufferFetching(); } bool diff --git a/dom/canvas/WebGLContextDraw.cpp b/dom/canvas/WebGLContextDraw.cpp index cd0700c4e435..119e603e1283 100644 --- a/dom/canvas/WebGLContextDraw.cpp +++ b/dom/canvas/WebGLContextDraw.cpp @@ -5,6 +5,7 @@ #include "WebGLContext.h" +#include "GeckoProfiler.h" #include "GLContext.h" #include "mozilla/CheckedInt.h" #include "mozilla/UniquePtrExtensions.h" @@ -230,79 +231,6 @@ WebGLContext::BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fa //////////////////////////////////////// -bool -WebGLContext::DrawInstanced_check(const char* info) -{ - MOZ_ASSERT(IsWebGL2() || - IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays)); - if (!mBufferFetchingHasPerVertex) { - /* http://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_instanced_arrays.txt - * If all of the enabled vertex attribute arrays that are bound to active - * generic attributes in the program have a non-zero divisor, the draw - * call should return INVALID_OPERATION. - * - * NB: This also appears to apply to NV_instanced_arrays, though the - * INVALID_OPERATION emission is not explicitly stated. - * ARB_instanced_arrays does not have this restriction. - */ - ErrorInvalidOperation("%s: at least one vertex attribute divisor should be 0", info); - return false; - } - - return true; -} - -bool -WebGLContext::DrawArrays_check(const char* funcName, GLenum mode, GLint first, - GLsizei vertCount, GLsizei instanceCount) -{ - if (!ValidateDrawModeEnum(mode, funcName)) - return false; - - if (!ValidateNonNegative(funcName, "first", first) || - !ValidateNonNegative(funcName, "vertCount", vertCount) || - !ValidateNonNegative(funcName, "instanceCount", instanceCount)) - { - return false; - } - - if (!ValidateStencilParamsForDrawCall()) - return false; - - if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { - MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); - if (mPrimRestartTypeBytes != 0) { - mPrimRestartTypeBytes = 0; - - // OSX appears to have severe perf issues with leaving this enabled. - gl->fDisable(LOCAL_GL_PRIMITIVE_RESTART); - } - } - - if (!vertCount || !instanceCount) - return false; // No error, just early out. - - if (!ValidateBufferFetching(funcName)) - return false; - - const auto checked_firstPlusCount = CheckedInt(first) + vertCount; - if (!checked_firstPlusCount.isValid()) { - ErrorInvalidOperation("%s: overflow in first+vertCount", funcName); - return false; - } - - if (uint32_t(checked_firstPlusCount.value()) > mMaxFetchedVertices) { - ErrorInvalidOperation("%s: Bound vertex attribute buffers do not have sufficient" - " size for given first and count.", - funcName); - return false; - } - - return true; -} - -//////////////////////////////////////// - template static bool DoSetsIntersect(const std::set& a, const std::set& b) @@ -319,21 +247,25 @@ class ScopedDrawHelper final bool mDidFake; public: - ScopedDrawHelper(WebGLContext* webgl, const char* funcName, uint32_t firstVertex, - uint32_t vertCount, uint32_t instanceCount, bool* const out_error) + ScopedDrawHelper(WebGLContext* const webgl, const char* const funcName, + const GLenum mode, const Maybe& lastRequiredVertex, + const uint32_t instanceCount, bool* const out_error) : mWebGL(webgl) , mDidFake(false) { - if (instanceCount > mWebGL->mMaxFetchedInstances) { - mWebGL->ErrorInvalidOperation("%s: Bound instance attribute buffers do not" - " have sufficient size for given" - " `instanceCount`.", - funcName); + MOZ_ASSERT(mWebGL->gl->IsCurrent()); + + if (!mWebGL->ValidateDrawModeEnum(mode, funcName)) { *out_error = true; return; } - MOZ_ASSERT(mWebGL->gl->IsCurrent()); + if (!mWebGL->ValidateStencilParamsForDrawCall()) { + *out_error = true; + return; + } + + //// if (mWebGL->mBoundDrawFramebuffer) { if (!mWebGL->mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName)) { @@ -344,15 +276,6 @@ public: mWebGL->ClearBackbufferIfNeeded(); } - //// - - const size_t requiredVerts = firstVertex + vertCount; - if (!mWebGL->DoFakeVertexAttrib0(funcName, requiredVerts)) { - *out_error = true; - return; - } - mDidFake = true; - //// // Check UBO sizes. @@ -419,39 +342,39 @@ public: //// - for (const auto& progAttrib : linkInfo->attribs) { - const auto& loc = progAttrib.mLoc; - if (loc == -1) - continue; + const auto& fetchLimits = linkInfo->GetDrawFetchLimits(funcName); + if (!fetchLimits) { + *out_error = true; + return; + } - const auto& attribData = mWebGL->mBoundVertexArray->mAttribs[loc]; - - GLenum attribDataBaseType; - if (attribData.mEnabled) { - attribDataBaseType = attribData.BaseType(); - - if (attribData.mBuf->IsBoundForTF()) { - mWebGL->ErrorInvalidOperation("%s: Vertex attrib %u's buffer is bound" - " or in use for transform feedback.", - funcName, loc); - *out_error = true; - return; - } - } else { - attribDataBaseType = mWebGL->mGenericVertexAttribTypes[loc]; - } - - if (attribDataBaseType != progAttrib.mBaseType) { - nsCString progType, dataType; - WebGLContext::EnumName(progAttrib.mBaseType, &progType); - WebGLContext::EnumName(attribDataBaseType, &dataType); - mWebGL->ErrorInvalidOperation("%s: Vertex attrib %u requires data of type" - " %s, but is being supplied with type %s.", - funcName, loc, progType.BeginReading(), - dataType.BeginReading()); + if (lastRequiredVertex && instanceCount) { + if (lastRequiredVertex.value() >= fetchLimits->maxVerts) { + mWebGL->ErrorInvalidOperation("%s: Vertex fetch requires vertex #%u, but" + " attribs only supply %" PRIu64 ".", + funcName, lastRequiredVertex.value(), + fetchLimits->maxVerts); *out_error = true; return; } + if (instanceCount > fetchLimits->maxInstances) { + mWebGL->ErrorInvalidOperation("%s: Instance fetch requires %u, but" + " attribs only supply %" PRIu64 ".", + funcName, instanceCount, + fetchLimits->maxInstances); + *out_error = true; + return; + } + } + + //// + + if (lastRequiredVertex) { + if (!mWebGL->DoFakeVertexAttrib0(funcName, lastRequiredVertex.value())) { + *out_error = true; + return; + } + mDidFake = true; } //// @@ -548,6 +471,41 @@ public: //////////////////////////////////////// +bool +WebGLContext::DrawArrays_check(const char* const funcName, const GLint first, + const GLsizei vertCount, const GLsizei instanceCount, + Maybe* const out_lastVert) +{ + if (!ValidateNonNegative(funcName, "first", first) || + !ValidateNonNegative(funcName, "vertCount", vertCount) || + !ValidateNonNegative(funcName, "instanceCount", instanceCount)) + { + return false; + } + + if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { + MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); + if (mPrimRestartTypeBytes != 0) { + mPrimRestartTypeBytes = 0; + + // OSX appears to have severe perf issues with leaving this enabled. + gl->fDisable(LOCAL_GL_PRIMITIVE_RESTART); + } + } + + if (!vertCount) { + *out_lastVert = Nothing(); + } else { + const auto lastVert_checked = CheckedInt(first) + vertCount - 1; + if (!lastVert_checked.isValid()) { + ErrorOutOfMemory("%s: `first+vertCount` out of range.", funcName); + return false; + } + *out_lastVert = Some(lastVert_checked.value()); + } + return true; +} + void WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei vertCount) { @@ -564,10 +522,12 @@ WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei vertCount) return; const GLsizei instanceCount = 1; - if (!DrawArrays_check(funcName, mode, first, vertCount, instanceCount)) + Maybe lastVert; + if (!DrawArrays_check(funcName, first, vertCount, instanceCount, &lastVert)) return; - const ScopedDrawHelper scopedHelper(this, funcName, first, vertCount, instanceCount, &error); + const ScopedDrawHelper scopedHelper(this, funcName, mode, lastVert, instanceCount, + &error); if (error) return; @@ -578,8 +538,10 @@ WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei vertCount) { ScopedDrawCallWrapper wrapper(*this); - AUTO_PROFILER_LABEL("glDrawArrays", GRAPHICS); - gl->fDrawArrays(mode, first, vertCount); + if (vertCount) { + AUTO_PROFILER_LABEL("glDrawArrays", GRAPHICS); + gl->fDrawArrays(mode, first, vertCount); + } } Draw_cleanup(funcName); @@ -602,13 +564,12 @@ WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount, if (error) return; - if (!DrawArrays_check(funcName, mode, first, vertCount, instanceCount)) + Maybe lastVert; + if (!DrawArrays_check(funcName, first, vertCount, instanceCount, &lastVert)) return; - if (!DrawInstanced_check(funcName)) - return; - - const ScopedDrawHelper scopedHelper(this, funcName, first, vertCount, instanceCount, &error); + const ScopedDrawHelper scopedHelper(this, funcName, mode, lastVert, instanceCount, + &error); if (error) return; @@ -619,8 +580,10 @@ WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount, { ScopedDrawCallWrapper wrapper(*this); - AUTO_PROFILER_LABEL("glDrawArraysInstanced", GRAPHICS); - gl->fDrawArraysInstanced(mode, first, vertCount, instanceCount); + if (vertCount && instanceCount) { + AUTO_PROFILER_LABEL("glDrawArraysInstanced", GRAPHICS); + gl->fDrawArraysInstanced(mode, first, vertCount, instanceCount); + } } Draw_cleanup(funcName); @@ -630,13 +593,11 @@ WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount, //////////////////////////////////////// bool -WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vertCount, - GLenum type, WebGLintptr byteOffset, - GLsizei instanceCount) +WebGLContext::DrawElements_check(const char* const funcName, const GLsizei rawIndexCount, + const GLenum type, const WebGLintptr byteOffset, + const GLsizei instanceCount, + Maybe* const out_lastVert) { - if (!ValidateDrawModeEnum(mode, funcName)) - return false; - if (mBoundTransformFeedback && mBoundTransformFeedback->mIsActive && !mBoundTransformFeedback->mIsPaused) @@ -647,42 +608,35 @@ WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vert return false; } - if (!ValidateNonNegative(funcName, "vertCount", vertCount) || + if (!ValidateNonNegative(funcName, "vertCount", rawIndexCount) || !ValidateNonNegative(funcName, "byteOffset", byteOffset) || !ValidateNonNegative(funcName, "instanceCount", instanceCount)) { return false; } + const auto indexCount = uint32_t(rawIndexCount); - if (!ValidateStencilParamsForDrawCall()) - return false; - - if (!vertCount || !instanceCount) - return false; // No error, just early out. - - uint8_t bytesPerElem = 0; + uint8_t bytesPerIndex = 0; switch (type) { case LOCAL_GL_UNSIGNED_BYTE: - bytesPerElem = 1; + bytesPerIndex = 1; break; case LOCAL_GL_UNSIGNED_SHORT: - bytesPerElem = 2; + bytesPerIndex = 2; break; case LOCAL_GL_UNSIGNED_INT: if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) { - bytesPerElem = 4; + bytesPerIndex = 4; } break; } - - if (!bytesPerElem) { + if (!bytesPerIndex) { ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", funcName, type); return false; } - - if (byteOffset % bytesPerElem != 0) { + if (byteOffset % bytesPerIndex != 0) { ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`", funcName); return false; @@ -692,8 +646,8 @@ WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vert if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); - if (mPrimRestartTypeBytes != bytesPerElem) { - mPrimRestartTypeBytes = bytesPerElem; + if (mPrimRestartTypeBytes != bytesPerIndex) { + mPrimRestartTypeBytes = bytesPerIndex; const uint32_t ones = UINT32_MAX >> (32 - 8*mPrimRestartTypeBytes); gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART); @@ -702,54 +656,28 @@ WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vert } //// + // Index fetching - const GLsizei first = byteOffset / bytesPerElem; - const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(vertCount); + if (!indexCount || !instanceCount) { + *out_lastVert = Nothing(); + return true; + } - if (!checked_byteCount.isValid()) { - ErrorInvalidValue("%s: Overflow in byteCount.", funcName); - return false; - } - - if (!mBoundVertexArray->mElementArrayBuffer) { - ErrorInvalidOperation("%s: Must have element array buffer binding.", funcName); - return false; - } - - WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mElementArrayBuffer; - - if (!elemArrayBuffer.ByteLength()) { - ErrorInvalidOperation("%s: Bound element array buffer doesn't have any data.", - funcName); - return false; - } - - CheckedInt checked_neededByteCount = checked_byteCount.toChecked() + byteOffset; - - if (!checked_neededByteCount.isValid()) { - ErrorInvalidOperation("%s: Overflow in byteOffset+byteCount.", funcName); - return false; - } - - if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) { - ErrorInvalidOperation("%s: Bound element array buffer is too small for given" - " count and offset.", - funcName); - return false; - } - - if (!ValidateBufferFetching(funcName)) - return false; - - if (!mMaxFetchedVertices || - !elemArrayBuffer.ValidateIndexedFetch(type, mMaxFetchedVertices, first, vertCount)) - { - ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient " - "size for given indices from the bound element array", - funcName); + const auto& indexBuffer = mBoundVertexArray->mElementArrayBuffer; + + size_t availBytes = 0; + if (indexBuffer) { + MOZ_ASSERT(!indexBuffer->IsBoundForTF(), "This should be impossible."); + availBytes = indexBuffer->ByteLength(); + } + const auto availIndices = AvailGroups(availBytes, byteOffset, bytesPerIndex, + bytesPerIndex); + if (indexCount > availIndices) { + ErrorInvalidOperation("%s: Index buffer too small.", funcName); return false; } + *out_lastVert = indexBuffer->GetIndexedFetchMaxVert(type, byteOffset, indexCount); return true; } @@ -774,14 +702,13 @@ HandleDrawElementsErrors(WebGLContext* webgl, const char* funcName, } void -WebGLContext::DrawElements(GLenum mode, GLsizei vertCount, GLenum type, +WebGLContext::DrawElements(GLenum mode, GLsizei indexCount, GLenum type, WebGLintptr byteOffset, const char* funcName) { AUTO_PROFILER_LABEL("WebGLContext::DrawElements", GRAPHICS); if (!funcName) { funcName = "drawElements"; } - if (IsContextLost()) return; @@ -793,10 +720,14 @@ WebGLContext::DrawElements(GLenum mode, GLsizei vertCount, GLenum type, return; const GLsizei instanceCount = 1; - if (!DrawElements_check(funcName, mode, vertCount, type, byteOffset, instanceCount)) + Maybe lastVert; + if (!DrawElements_check(funcName, indexCount, type, byteOffset, instanceCount, + &lastVert)) + { return; + } - const ScopedDrawHelper scopedHelper(this, funcName, 0, mMaxFetchedVertices, instanceCount, + const ScopedDrawHelper scopedHelper(this, funcName, mode, lastVert, instanceCount, &error); if (error) return; @@ -810,9 +741,11 @@ WebGLContext::DrawElements(GLenum mode, GLsizei vertCount, GLenum type, errorScope.reset(new gl::GLContext::LocalErrorScope(*gl)); } - AUTO_PROFILER_LABEL("glDrawElements", GRAPHICS); - gl->fDrawElements(mode, vertCount, type, - reinterpret_cast(byteOffset)); + if (lastVert) { + AUTO_PROFILER_LABEL("glDrawElements", GRAPHICS); + gl->fDrawElements(mode, indexCount, type, + reinterpret_cast(byteOffset)); + } if (errorScope) { HandleDrawElementsErrors(this, funcName, *errorScope); @@ -824,7 +757,7 @@ WebGLContext::DrawElements(GLenum mode, GLsizei vertCount, GLenum type, } void -WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei vertCount, GLenum type, +WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei indexCount, GLenum type, WebGLintptr byteOffset, GLsizei instanceCount) { AUTO_PROFILER_LABEL("WebGLContext::DrawElementsInstanced", GRAPHICS); @@ -839,13 +772,14 @@ WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei vertCount, GLenum type, if (error) return; - if (!DrawElements_check(funcName, mode, vertCount, type, byteOffset, instanceCount)) + Maybe lastVert; + if (!DrawElements_check(funcName, indexCount, type, byteOffset, instanceCount, + &lastVert)) + { return; + } - if (!DrawInstanced_check(funcName)) - return; - - const ScopedDrawHelper scopedHelper(this, funcName, 0, mMaxFetchedVertices, instanceCount, + const ScopedDrawHelper scopedHelper(this, funcName, mode, lastVert, instanceCount, &error); if (error) return; @@ -859,10 +793,12 @@ WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei vertCount, GLenum type, errorScope.reset(new gl::GLContext::LocalErrorScope(*gl)); } - AUTO_PROFILER_LABEL("glDrawElementsInstanced", GRAPHICS); - gl->fDrawElementsInstanced(mode, vertCount, type, - reinterpret_cast(byteOffset), - instanceCount); + if (lastVert && instanceCount) { + AUTO_PROFILER_LABEL("glDrawElementsInstanced", GRAPHICS); + gl->fDrawElementsInstanced(mode, indexCount, type, + reinterpret_cast(byteOffset), + instanceCount); + } if (errorScope) { HandleDrawElementsErrors(this, funcName, *errorScope); @@ -919,117 +855,12 @@ WebGLContext::Draw_cleanup(const char* funcName) } } -/* - * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount) - * that will be legal to be read from bound VBOs. - */ - -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; - const uint32_t attribCount = mBoundVertexArray->mAttribs.Length(); - - uint32_t i = 0; - for (const auto& vd : mBoundVertexArray->mAttribs) { - // If the attrib array isn't enabled, there's nothing to check; - // it's a static value. - if (!vd.mEnabled) - continue; - - if (!vd.mBuf) { - ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %du!", - info, i); - return false; - } - - ++i; - } - - mBufferFetch_IsAttrib0Active = false; - - for (const auto& attrib : mActiveProgramLinkInfo->attribs) { - if (attrib.mLoc == -1) - continue; - - const uint32_t attribLoc(attrib.mLoc); - if (attribLoc >= attribCount) - continue; - - if (attribLoc == 0) { - mBufferFetch_IsAttrib0Active = true; - } - - const auto& vd = mBoundVertexArray->mAttribs[attribLoc]; - if (!vd.mEnabled) - continue; - - const auto& bufByteLen = vd.mBuf->ByteLength(); - if (vd.ByteOffset() > bufByteLen) { - maxVertices = 0; - maxInstances = 0; - break; - } - - size_t availBytes = bufByteLen - vd.ByteOffset(); - if (vd.BytesPerVertex() > availBytes) { - maxVertices = 0; - maxInstances = 0; - break; - } - availBytes -= vd.BytesPerVertex(); // Snip off the tail. - const size_t vertCapacity = availBytes / vd.ExplicitStride() + 1; // Add +1 for the snipped tail. - - if (vd.mDivisor == 0) { - if (vertCapacity < maxVertices) { - maxVertices = vertCapacity; - } - hasPerVertex = true; - } else { - const auto curMaxInstances = CheckedInt(vertCapacity) * vd.mDivisor; - // If this isn't valid, it's because we overflowed, which means we can support - // *too much*. Don't update maxInstances in this case. - if (curMaxInstances.isValid() && - curMaxInstances.value() < maxInstances) - { - maxInstances = curMaxInstances.value(); - } - } - } - - mBufferFetchingIsVerified = true; - mBufferFetchingHasPerVertex = hasPerVertex; - mMaxFetchedVertices = maxVertices; - mMaxFetchedInstances = maxInstances; - - return true; -} - WebGLVertexAttrib0Status WebGLContext::WhatDoesVertexAttrib0Need() const { MOZ_ASSERT(mCurrentProgram); MOZ_ASSERT(mActiveProgramLinkInfo); - const auto& isAttribArray0Enabled = mBoundVertexArray->mAttribs[0].mEnabled; - bool legacyAttrib0 = gl->IsCompatibilityProfile(); #ifdef XP_MACOSX if (gl->WorkAroundDriverBugs()) { @@ -1042,23 +873,19 @@ WebGLContext::WhatDoesVertexAttrib0Need() const if (!legacyAttrib0) return WebGLVertexAttrib0Status::Default; - if (isAttribArray0Enabled && mBufferFetch_IsAttrib0Active) - return WebGLVertexAttrib0Status::Default; + if (!mActiveProgramLinkInfo->attrib0Active) { + // Ensure that the legacy code has enough buffer. + return WebGLVertexAttrib0Status::EmulatedUninitializedArray; + } - if (mBufferFetch_IsAttrib0Active) - return WebGLVertexAttrib0Status::EmulatedInitializedArray; - - // Ensure that the legacy code has enough buffer. - return WebGLVertexAttrib0Status::EmulatedUninitializedArray; + const auto& isAttribArray0Enabled = mBoundVertexArray->mAttribs[0].mEnabled; + return isAttribArray0Enabled ? WebGLVertexAttrib0Status::Default + : WebGLVertexAttrib0Status::EmulatedInitializedArray; } bool -WebGLContext::DoFakeVertexAttrib0(const char* funcName, GLuint vertexCount) +WebGLContext::DoFakeVertexAttrib0(const char* const funcName, const uint32_t lastVert) { - if (!vertexCount) { - vertexCount = 1; - } - const auto whatDoesAttrib0Need = WhatDoesVertexAttrib0Need(); if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default)) return true; @@ -1102,10 +929,12 @@ WebGLContext::DoFakeVertexAttrib0(const char* funcName, GLuint vertexCount) //// const auto bytesPerVert = sizeof(mFakeVertexAttrib0Data); - const auto checked_dataSize = CheckedUint32(vertexCount) * bytesPerVert; + const auto checked_dataSize = (CheckedUint32(lastVert)+1) * bytesPerVert; 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); + ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0" + " array for a draw-operation with %" PRIu64 " vertices. Try" + " reducing the number of vertices.", + uint64_t(lastVert) + 1); return false; } const auto dataSize = checked_dataSize.value(); diff --git a/dom/canvas/WebGLContextValidate.cpp b/dom/canvas/WebGLContextValidate.cpp index 5cc371b8d444..257adfc650aa 100644 --- a/dom/canvas/WebGLContextValidate.cpp +++ b/dom/canvas/WebGLContextValidate.cpp @@ -753,6 +753,7 @@ WebGLContext::InitAndValidateGL(FailureReason* const out_failReason) mGenericVertexAttribTypes.reset(new GLenum[mGLMaxVertexAttribs]); std::fill_n(mGenericVertexAttribTypes.get(), mGLMaxVertexAttribs, LOCAL_GL_FLOAT); + mGenericVertexAttribTypeInvalidator.InvalidateCaches(); static const float kDefaultGenericVertexAttribData[4] = { 0, 0, 0, 1 }; memcpy(mGenericVertexAttrib0Data, kDefaultGenericVertexAttribData, diff --git a/dom/canvas/WebGLContextVertexArray.cpp b/dom/canvas/WebGLContextVertexArray.cpp index 5b7a10f220c7..a46e10ef4306 100644 --- a/dom/canvas/WebGLContextVertexArray.cpp +++ b/dom/canvas/WebGLContextVertexArray.cpp @@ -21,8 +21,6 @@ WebGLContext::BindVertexArray(WebGLVertexArray* array) if (array && !ValidateObject("bindVertexArrayObject", *array)) return; - InvalidateBufferFetching(); - MakeContextCurrent(); if (mBoundVertexArray) { diff --git a/dom/canvas/WebGLContextVertices.cpp b/dom/canvas/WebGLContextVertices.cpp index cd5d0811c609..7e2bf20bb9f9 100644 --- a/dom/canvas/WebGLContextVertices.cpp +++ b/dom/canvas/WebGLContextVertices.cpp @@ -82,6 +82,7 @@ WebGLContext::VertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfl //// mGenericVertexAttribTypes[index] = LOCAL_GL_FLOAT; + mGenericVertexAttribTypeInvalidator.InvalidateCaches(); if (!index) { const float data[4] = { x, y, z, w }; @@ -113,6 +114,7 @@ WebGL2Context::VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w, //// mGenericVertexAttribTypes[index] = LOCAL_GL_INT; + mGenericVertexAttribTypeInvalidator.InvalidateCaches(); if (!index) { const int32_t data[4] = { x, y, z, w }; @@ -144,6 +146,7 @@ WebGL2Context::VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLui //// mGenericVertexAttribTypes[index] = LOCAL_GL_UNSIGNED_INT; + mGenericVertexAttribTypeInvalidator.InvalidateCaches(); if (!index) { const uint32_t data[4] = { x, y, z, w }; @@ -163,12 +166,12 @@ WebGLContext::EnableVertexAttribArray(GLuint index) return; MakeContextCurrent(); - InvalidateBufferFetching(); gl->fEnableVertexAttribArray(index); MOZ_ASSERT(mBoundVertexArray); mBoundVertexArray->mAttribs[index].mEnabled = true; + mBoundVertexArray->InvalidateCaches(); } void @@ -181,7 +184,6 @@ WebGLContext::DisableVertexAttribArray(GLuint index) return; MakeContextCurrent(); - InvalidateBufferFetching(); if (index || !gl->IsCompatibilityProfile()) { gl->fDisableVertexAttribArray(index); @@ -189,6 +191,7 @@ WebGLContext::DisableVertexAttribArray(GLuint index) MOZ_ASSERT(mBoundVertexArray); mBoundVertexArray->mAttribs[index].mEnabled = false; + mBoundVertexArray->InvalidateCaches(); } JS::Value @@ -425,8 +428,7 @@ WebGLContext::VertexAttribAnyPointer(const char* funcName, bool isFuncInt, GLuin WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index]; vd.VertexAttribPointer(isFuncInt, buffer, size, type, normalized, stride, byteOffset); - - InvalidateBufferFetching(); + mBoundVertexArray->InvalidateCaches(); } //////////////////////////////////////// @@ -441,11 +443,8 @@ WebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor) return; MOZ_ASSERT(mBoundVertexArray); - - WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index]; - vd.mDivisor = divisor; - - InvalidateBufferFetching(); + mBoundVertexArray->mAttribs[index].mDivisor = divisor; + mBoundVertexArray->InvalidateCaches(); MakeContextCurrent(); diff --git a/dom/canvas/WebGLProgram.cpp b/dom/canvas/WebGLProgram.cpp index 4f0cfde39d74..1525f96d55f8 100644 --- a/dom/canvas/WebGLProgram.cpp +++ b/dom/canvas/WebGLProgram.cpp @@ -12,11 +12,13 @@ #include "mozilla/RefPtr.h" #include "nsPrintfCString.h" #include "WebGLActiveInfo.h" +#include "WebGLBuffer.h" #include "WebGLContext.h" #include "WebGLShader.h" #include "WebGLTransformFeedback.h" #include "WebGLUniformLocation.h" #include "WebGLValidateStrings.h" +#include "WebGLVertexArray.h" namespace mozilla { @@ -264,6 +266,10 @@ QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl) const GLenum baseType = AttribBaseType(elemType); const webgl::AttribInfo attrib = {activeInfo, loc, baseType}; info->attribs.push_back(attrib); + + if (loc == 0) { + info->attrib0Active = true; + } } // Uniforms (can be basically anything) @@ -443,6 +449,7 @@ QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl) webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog) : prog(prog) , transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode) + , attrib0Active(false) { } webgl::LinkedProgramInfo::~LinkedProgramInfo() @@ -455,6 +462,101 @@ webgl::LinkedProgramInfo::~LinkedProgramInfo() } } +const webgl::CachedDrawFetchLimits* +webgl::LinkedProgramInfo::GetDrawFetchLimits(const char* const funcName) const +{ + const auto& webgl = prog->mContext; + const auto& vao = webgl->mBoundVertexArray; + + const auto found = mDrawFetchCache.Find(vao); + if (found) + return found; + + std::vector cacheDeps; + cacheDeps.push_back(vao.get()); + cacheDeps.push_back(&webgl->mGenericVertexAttribTypeInvalidator); + + { + // We have to ensure that every enabled attrib array (not just the active ones) + // has a non-null buffer. + uint32_t i = 0; + for (const auto& cur : vao->mAttribs) { + if (cur.mEnabled && !cur.mBuf) { + webgl->ErrorInvalidOperation("%s: Vertex attrib array %u is enabled but" + " has no buffer bound.", + funcName, i); + return nullptr; + } + } + } + + bool hasActiveAttrib = false; + bool hasActiveDivisor0 = false; + webgl::CachedDrawFetchLimits fetchLimits = { UINT64_MAX, UINT64_MAX }; + + for (const auto& progAttrib : this->attribs) { + const auto& loc = progAttrib.mLoc; + if (loc == -1) + continue; + hasActiveAttrib |= true; + + const auto& attribData = vao->mAttribs[loc]; + hasActiveDivisor0 |= (attribData.mDivisor == 0); + + GLenum attribDataBaseType; + if (attribData.mEnabled) { + MOZ_ASSERT(attribData.mBuf); + if (attribData.mBuf->IsBoundForTF()) { + webgl->ErrorInvalidOperation("%s: Vertex attrib %u's buffer is bound for" + " transform feedback.", + funcName, loc); + return nullptr; + } + cacheDeps.push_back(&attribData.mBuf->mFetchInvalidator); + + attribDataBaseType = attribData.BaseType(); + + const size_t availBytes = attribData.mBuf->ByteLength(); + const auto availElems = AvailGroups(availBytes, attribData.ByteOffset(), + attribData.BytesPerVertex(), + attribData.ExplicitStride()); + if (attribData.mDivisor) { + const auto availInstances = CheckedInt(availElems) * attribData.mDivisor; + if (availInstances.isValid()) { + fetchLimits.maxInstances = std::min(fetchLimits.maxInstances, + availInstances.value()); + } // If not valid, it overflowed too large, so we're super safe. + } else { + fetchLimits.maxVerts = std::min(fetchLimits.maxVerts, availElems); + } + } else { + attribDataBaseType = webgl->mGenericVertexAttribTypes[loc]; + } + + if (attribDataBaseType != progAttrib.mBaseType) { + nsCString progType, dataType; + WebGLContext::EnumName(progAttrib.mBaseType, &progType); + WebGLContext::EnumName(attribDataBaseType, &dataType); + webgl->ErrorInvalidOperation("%s: Vertex attrib %u requires data of type %s," + " but is being supplied with type %s.", + funcName, loc, progType.BeginReading(), + dataType.BeginReading()); + return nullptr; + } + } + + if (hasActiveAttrib && !hasActiveDivisor0) { + webgl->ErrorInvalidOperation("%s: One active vertex attrib (if any are active)" + " must have a divisor of 0.", + funcName); + return nullptr; + } + + // -- + + return mDrawFetchCache.Insert(vao.get(), Move(fetchLimits), Move(cacheDeps)); +} + //////////////////////////////////////////////////////////////////////////////// // WebGLProgram @@ -1069,7 +1171,6 @@ WebGLProgram::LinkProgram() } mContext->MakeContextCurrent(); - mContext->InvalidateBufferFetching(); // we do it early in this function // as some of the validation changes program state mLinkLog.Truncate(); @@ -1365,8 +1466,6 @@ WebGLProgram::UseProgram() const mContext->MakeContextCurrent(); - mContext->InvalidateBufferFetching(); - mContext->gl->fUseProgram(mGLName); return true; } diff --git a/dom/canvas/WebGLProgram.h b/dom/canvas/WebGLProgram.h index 0f08d3df3eb9..3799efcadf70 100644 --- a/dom/canvas/WebGLProgram.h +++ b/dom/canvas/WebGLProgram.h @@ -17,6 +17,7 @@ #include "nsString.h" #include "nsWrapperCache.h" +#include "CacheMap.h" #include "WebGLContext.h" #include "WebGLObjectModel.h" @@ -75,6 +76,11 @@ struct UniformBlockInfo final { } }; +struct CachedDrawFetchLimits final { + uint64_t maxVerts; + uint64_t maxInstances; +}; + struct LinkedProgramInfo final : public RefCounted , public SupportsWeakPtr @@ -99,11 +105,22 @@ struct LinkedProgramInfo final mutable std::vector componentsPerTFVert; + bool attrib0Active; + ////// // The maps for the frag data names to the translated names. std::map fragDataMap; + ////// + + mutable CacheMap mDrawFetchCache; + + const CachedDrawFetchLimits* GetDrawFetchLimits(const char* funcName) const; + + ////// + explicit LinkedProgramInfo(WebGLProgram* prog); ~LinkedProgramInfo(); diff --git a/dom/canvas/WebGLVertexArray.h b/dom/canvas/WebGLVertexArray.h index 74f714af13b9..666820b859e0 100644 --- a/dom/canvas/WebGLVertexArray.h +++ b/dom/canvas/WebGLVertexArray.h @@ -10,6 +10,7 @@ #include "mozilla/LinkedList.h" #include "nsWrapperCache.h" +#include "CacheMap.h" #include "WebGLObjectModel.h" #include "WebGLStrongTypes.h" #include "WebGLVertexAttribData.h" @@ -17,11 +18,15 @@ namespace mozilla { class WebGLVertexArrayFake; +namespace webgl { +struct LinkedProgramInfo; +} class WebGLVertexArray : public nsWrapperCache , public WebGLRefCountedObject , public LinkedListElement + , public CacheMapInvalidator { public: static WebGLVertexArray* Create(WebGLContext* webgl); @@ -66,6 +71,7 @@ protected: friend class WebGLContext; friend class WebGLVertexArrayFake; friend class WebGL2Context; + friend struct webgl::LinkedProgramInfo; }; } // namespace mozilla