--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -54,17 +54,20 @@ WebGLBuffer::SetContentAfterBind(GLenum
}
}
void
WebGLBuffer::Delete()
{
mContext->MakeContextCurrent();
mContext->gl->fDeleteBuffers(1, &mGLName);
+
mByteLength = 0;
+ mFetchInvalidator.InvalidateCaches();
+
mIndexCache = nullptr;
mIndexRanges.clear();
LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
}
////////////////////////////////////////
static bool
@@ -133,18 +136,16 @@ WebGLBuffer::BufferData(GLenum target, s
}
const auto& gl = mContext->gl;
gl->MakeCurrent();
const ScopedLazyBind lazyBind(gl, target, this);
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();
if (error) {
MOZ_ASSERT(error == LOCAL_GL_OUT_OF_MEMORY);
mContext->ErrorOutOfMemory("%s: Error from driver: 0x%04x", funcName, error);
return;
@@ -152,16 +153,17 @@ WebGLBuffer::BufferData(GLenum target, s
} else {
gl->fBufferData(target, size, uploadData, usage);
}
mContext->OnDataAllocCall();
mUsage = usage;
mByteLength = size;
+ mFetchInvalidator.InvalidateCaches();
mIndexCache = Move(newIndexCache);
if (mIndexCache) {
if (!mIndexRanges.empty()) {
mContext->GeneratePerfWarning("[%p] Invalidating %u ranges.", this,
uint32_t(mIndexRanges.size()));
mIndexRanges.clear();
}
@@ -229,28 +231,28 @@ IndexByteSizeByType(GLenum type)
case LOCAL_GL_UNSIGNED_SHORT: return 2;
case LOCAL_GL_UNSIGNED_INT: return 4;
default:
MOZ_CRASH();
}
}
void
-WebGLBuffer::InvalidateCacheRange(size_t byteOffset, size_t byteLength) const
+WebGLBuffer::InvalidateCacheRange(uint64_t byteOffset, uint64_t byteLength) const
{
MOZ_ASSERT(mIndexCache);
std::vector<IndexRange> 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);
}
if (!invalids.empty()) {
mContext->GeneratePerfWarning("[%p] Invalidating %u/%u ranges.", this,
uint32_t(invalids.size()),
@@ -268,89 +270,96 @@ WebGLBuffer::SizeOfIncludingThis(mozilla
size_t size = mallocSizeOf(this);
if (mIndexCache) {
size += mByteLength;
}
return size;
}
template<typename T>
-static size_t
-MaxForRange(const void* data, size_t first, size_t count, const uint32_t ignoredVal)
+static Maybe<uint32_t>
+MaxForRange(const void* const start, const uint32_t count,
+ const Maybe<uint32_t>& untypedIgnoredVal)
{
- const T ignoredTVal(ignoredVal);
- T ret = 0;
+ const Maybe<T> ignoredVal = (untypedIgnoredVal ? Some(T(untypedIgnoredVal.value()))
+ : Nothing());
+ Maybe<uint32_t> 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<uint32_t>
+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;
const auto& didInsert = res.second;
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<uint32_t> ignoredVal;
+ if (mContext->IsWebGL2()) {
+ ignoredVal = Some(UINT32_MAX);
+ }
switch (type) {
case LOCAL_GL_UNSIGNED_BYTE:
- maxFetchIndex = MaxForRange<uint8_t>(data, first, count, ignoreVal);
+ maxFetchIndex = MaxForRange<uint8_t>(start, indexCount, ignoredVal);
break;
case LOCAL_GL_UNSIGNED_SHORT:
- maxFetchIndex = MaxForRange<uint16_t>(data, first, count, ignoreVal);
+ maxFetchIndex = MaxForRange<uint16_t>(start, indexCount, ignoredVal);
break;
case LOCAL_GL_UNSIGNED_INT:
- maxFetchIndex = MaxForRange<uint32_t>(data, first, count, ignoreVal);
+ maxFetchIndex = MaxForRange<uint32_t>(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;
}
////
bool
WebGLBuffer::ValidateCanBindToTarget(const char* funcName, GLenum target)
{
/* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -3,16 +3,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef WEBGL_BUFFER_H_
#define WEBGL_BUFFER_H_
#include <map>
+#include "CacheMap.h"
#include "GLDefs.h"
#include "mozilla/LinkedList.h"
#include "nsWrapperCache.h"
#include "WebGLObjectModel.h"
#include "WebGLTypes.h"
namespace mozilla {
@@ -39,17 +40,18 @@ public:
void Delete();
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
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<uint32_t> 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 {
return mContext;
}
virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
@@ -62,16 +64,17 @@ public:
static void AddBindCount(GLenum target, WebGLBuffer* buffer, int8_t addVal) {
if (!buffer)
return;
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;
}
}
static void SetSlot(GLenum target, WebGLBuffer* newBuffer,
WebGLRefPtr<WebGLBuffer>* const out_slot)
@@ -90,39 +93,42 @@ public:
const GLenum mGLName;
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBuffer)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLBuffer)
protected:
~WebGLBuffer();
- void InvalidateCacheRange(size_t offset, size_t length) const;
+ void InvalidateCacheRange(uint64_t byteOffset, uint64_t byteLength) const;
Kind mContent;
GLenum mUsage;
size_t mByteLength;
size_t mTFBindCount;
size_t mNonTFBindCount;
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<IndexRange, size_t> mIndexRanges;
+ mutable std::map<IndexRange, Maybe<uint32_t>> mIndexRanges;
+
+public:
+ CacheMapInvalidator mFetchInvalidator;
};
} // namespace mozilla
#endif // WEBGL_BUFFER_H_
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -105,20 +105,16 @@ WebGLContextOptions::WebGLContextOptions
}
WebGLContext::WebGLContext()
: WebGLContextUnchecked(nullptr)
, mMaxPerfWarnings(gfxPrefs::WebGLMaxPerfWarnings())
, mNumPerfWarnings(0)
, mMaxAcceptableFBStatusInvals(gfxPrefs::WebGLMaxAcceptableFBStatusInvals())
, mDataAllocGLCallCount(0)
- , mBufferFetchingIsVerified(false)
- , mBufferFetchingHasPerVertex(false)
- , mMaxFetchedVertices(0)
- , mMaxFetchedInstances(0)
, mBypassShaderValidation(false)
, mEmptyTFO(0)
, mContextLossHandler(this)
, mNeedsFakeNoAlpha(false)
, mNeedsFakeNoDepth(false)
, mNeedsFakeNoStencil(false)
, mNeedsEmulatedLoneDepthStencil(false)
, mAllowFBInvalidation(gfxPrefs::WebGLFBInvalidation())
@@ -177,18 +173,16 @@ WebGLContext::WebGLContext()
mMaxWarnings = gfxPrefs::WebGLMaxWarningsPerContext();
if (mMaxWarnings < -1) {
GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
mMaxWarnings = 0;
}
mLastUseIndex = 0;
- InvalidateBufferFetching();
-
mDisableFragHighP = false;
mDrawCallsSinceLastFlush = 0;
}
WebGLContext::~WebGLContext()
{
RemovePostRefreshObserver();
@@ -2274,16 +2268,37 @@ Intersect(const int32_t srcSize, const i
}
*out_intRead0 = intRead0;
*out_intWrite0 = intWrite0;
*out_intSize = intSize;
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
WebGLContext::GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height,
uint32_t depth, uint8_t bytesPerPixel)
{
if (!width || !height || !depth)
return 0;
@@ -2389,32 +2404,34 @@ WebGLContext::ValidateArrayBufferView(co
elemCount = elemCountOverride;
}
*out_bytes = bytes + (elemOffset * elemSize);
*out_byteLen = elemCount * elemSize;
return true;
}
-////////////////////////////////////////////////////////////////////////////////
-// XPCOM goop
+////
void
WebGLContext::UpdateMaxDrawBuffers()
{
gl->MakeCurrent();
mGLMaxColorAttachments = gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_COLOR_ATTACHMENTS);
mGLMaxDrawBuffers = gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_DRAW_BUFFERS);
// WEBGL_draw_buffers:
// "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or
// equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter."
mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mGLMaxColorAttachments);
}
+////////////////////////////////////////////////////////////////////////////////
+// XPCOM goop
+
void
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
const std::vector<IndexedBufferBinding>& field,
const char* name, uint32_t flags)
{
for (const auto& cur : field) {
ImplCycleCollectionTraverse(callback, cur.mBufferBinding, name, flags);
}
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -29,16 +29,17 @@
#include "ScopedGLHelpers.h"
#include "TexUnpackBlob.h"
#ifdef XP_MACOSX
#include "ForceDiscreteGPUHelperCGL.h"
#endif
// Local
+#include "CacheMap.h"
#include "WebGLContextLossHandler.h"
#include "WebGLContextUnchecked.h"
#include "WebGLFormats.h"
#include "WebGLObjectModel.h"
#include "WebGLStrongTypes.h"
#include "WebGLTexture.h"
// Generated
@@ -288,16 +289,17 @@ class WebGLContext
friend class WebGLExtensionCompressedTextureS3TC;
friend class WebGLExtensionCompressedTextureS3TC_SRGB;
friend class WebGLExtensionDepthTexture;
friend class WebGLExtensionDisjointTimerQuery;
friend class WebGLExtensionDrawBuffers;
friend class WebGLExtensionLoseContext;
friend class WebGLExtensionVertexArray;
friend class WebGLMemoryTracker;
+ friend struct webgl::LinkedProgramInfo;
friend struct webgl::UniformBlockInfo;
enum {
UNPACK_FLIP_Y_WEBGL = 0x9240,
UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
// We throw InvalidOperation in TexImage if we fail to use GPU fast-path
// for texture copy when it is set to true, only for debug purpose.
UNPACK_REQUIRE_FASTPATH = 0x10001,
@@ -1380,59 +1382,41 @@ public:
const bool isFuncInt = false;
VertexAttribAnyPointer(funcName, isFuncInt, index, size, type, normalized, stride,
byteOffset);
}
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<uint32_t>* out_lastVert);
+ bool DrawElements_check(const char* funcName, GLsizei indexCount, GLenum type,
+ WebGLintptr byteOffset, GLsizei instanceCount,
+ Maybe<uint32_t>* out_lastVert);
void Draw_cleanup(const char* funcName);
void VertexAttrib1fv_base(GLuint index, uint32_t arrayLength,
const GLfloat* ptr);
void VertexAttrib2fv_base(GLuint index, uint32_t arrayLength,
const GLfloat* ptr);
void VertexAttrib3fv_base(GLuint index, uint32_t arrayLength,
const GLfloat* ptr);
void VertexAttrib4fv_base(GLuint index, uint32_t arrayLength,
const GLfloat* ptr);
- bool ValidateBufferFetching(const char* info);
bool BindArrayAttribToLocation0(WebGLProgram* prog);
// -----------------------------------------------------------------------------
// PROTECTED
protected:
WebGLVertexAttrib0Status WhatDoesVertexAttrib0Need() const;
bool DoFakeVertexAttrib0(const char* funcName, GLuint vertexCount);
void UndoFakeVertexAttrib0();
- inline void InvalidateBufferFetching()
- {
- mBufferFetchingIsVerified = false;
- mBufferFetchingHasPerVertex = false;
- mMaxFetchedVertices = 0;
- mMaxFetchedInstances = 0;
- }
-
CheckedUint32 mGeneration;
WebGLContextOptions mOptions;
bool mInvalidated;
bool mCapturedFrameInvalidated;
bool mResetLayer;
bool mOptionsFrozen;
@@ -1917,16 +1901,17 @@ protected:
GLuint mEmptyTFO;
// Generic Vertex Attributes
// Though CURRENT_VERTEX_ATTRIB is listed under "Vertex Shader State" in the spec
// state tables, this isn't vertex shader /object/ state. This array is merely state
// useful to vertex shaders, but is global state.
UniquePtr<GLenum[]> mGenericVertexAttribTypes;
uint8_t mGenericVertexAttrib0Data[sizeof(float) * 4];
+ CacheMapInvalidator mGenericVertexAttribTypeInvalidator;
GLuint mFakeVertexAttrib0BufferObject;
size_t mFakeVertexAttrib0BufferObjectSize;
bool mFakeVertexAttrib0DataDefined;
uint8_t mFakeVertexAttrib0Data[sizeof(float) * 4];
JSObject* GetVertexAttribFloat32Array(JSContext* cx, GLuint index);
JSObject* GetVertexAttribInt32Array(JSContext* cx, GLuint index);
@@ -2179,16 +2164,20 @@ private:
};
////
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
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
const std::vector<IndexedBufferBinding>& field,
const char* name, uint32_t flags = 0);
void
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -496,18 +496,16 @@ WebGLContext::DeleteBuffer(WebGLBuffer*
for (auto& binding : mIndexedUniformBufferBindings) {
fnClearIfBuffer(0, binding.mBufferBinding);
}
}
////
buffer->RequestDelete();
-
- InvalidateBufferFetching();
}
bool
WebGLContext::IsBuffer(WebGLBuffer* buffer)
{
if (!ValidateIsObject("isBuffer", buffer))
return false;
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -1,15 +1,16 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WebGLContext.h"
+#include "GeckoProfiler.h"
#include "GLContext.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/UniquePtrExtensions.h"
#include "nsPrintfCString.h"
#include "WebGLBuffer.h"
#include "WebGLContextUtils.h"
#include "WebGLFramebuffer.h"
#include "WebGLProgram.h"
@@ -225,140 +226,62 @@ WebGLContext::BindFakeBlack(uint32_t tex
gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit);
gl->fBindTexture(target.get(), fakeBlackTex->mGLName);
gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
return true;
}
////////////////////////////////////////
-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<GLsizei>(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<typename T>
static bool
DoSetsIntersect(const std::set<T>& a, const std::set<T>& b)
{
std::vector<T> intersection;
std::set_intersection(a.begin(), a.end(), b.begin(), b.end(),
std::back_inserter(intersection));
return bool(intersection.size());
}
class ScopedDrawHelper final
{
WebGLContext* const mWebGL;
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<uint32_t>& 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)) {
*out_error = true;
return;
}
} else {
mWebGL->ClearBackbufferIfNeeded();
}
////
-
- const size_t requiredVerts = firstVertex + vertCount;
- if (!mWebGL->DoFakeVertexAttrib0(funcName, requiredVerts)) {
- *out_error = true;
- return;
- }
- mDidFake = true;
-
- ////
// Check UBO sizes.
const auto& linkInfo = mWebGL->mActiveProgramLinkInfo;
for (const auto& cur : linkInfo->uniformBlocks) {
const auto& dataSize = cur->mDataSize;
const auto& binding = cur->mBinding;
if (!binding) {
@@ -414,49 +337,49 @@ public:
*out_error = true;
return;
}
}
}
////
- for (const auto& progAttrib : linkInfo->attribs) {
- const auto& loc = progAttrib.mLoc;
- if (loc == -1)
- continue;
-
- const auto& attribData = mWebGL->mBoundVertexArray->mAttribs[loc];
-
- GLenum attribDataBaseType;
- if (attribData.mEnabled) {
- attribDataBaseType = attribData.BaseType();
+ const auto& fetchLimits = linkInfo->GetDrawFetchLimits(funcName);
+ if (!fetchLimits) {
+ *out_error = true;
+ return;
+ }
- 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;
}
////
mWebGL->RunContextLossTimer();
}
~ScopedDrawHelper() {
@@ -543,48 +466,87 @@ public:
return;
mTFO->mActive_VertPosition += mUsedVerts;
}
};
////////////////////////////////////////
+bool
+WebGLContext::DrawArrays_check(const char* const funcName, const GLint first,
+ const GLsizei vertCount, const GLsizei instanceCount,
+ Maybe<uint32_t>* 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<uint32_t>(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)
{
AUTO_PROFILER_LABEL("WebGLContext::DrawArrays", GRAPHICS);
const char funcName[] = "drawArrays";
if (IsContextLost())
return;
MakeContextCurrent();
bool error = false;
ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
if (error)
return;
const GLsizei instanceCount = 1;
- if (!DrawArrays_check(funcName, mode, first, vertCount, instanceCount))
+ Maybe<uint32_t> 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;
const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
instanceCount, &error);
if (error)
return;
{
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);
scopedTF.Advance();
}
void
WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount,
@@ -597,164 +559,130 @@ WebGLContext::DrawArraysInstanced(GLenum
MakeContextCurrent();
bool error = false;
ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
if (error)
return;
- if (!DrawArrays_check(funcName, mode, first, vertCount, instanceCount))
+ Maybe<uint32_t> 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;
const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
instanceCount, &error);
if (error)
return;
{
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);
scopedTF.Advance();
}
////////////////////////////////////////
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<uint32_t>* const out_lastVert)
{
- if (!ValidateDrawModeEnum(mode, funcName))
- return false;
-
if (mBoundTransformFeedback &&
mBoundTransformFeedback->mIsActive &&
!mBoundTransformFeedback->mIsPaused)
{
ErrorInvalidOperation("%s: DrawElements* functions are incompatible with"
" transform feedback.",
funcName);
return false;
}
- if (!ValidateNonNegative(funcName, "vertCount", vertCount) ||
+ if (!ValidateNonNegative(funcName, "vertCount", rawIndexCount) ||
!ValidateNonNegative(funcName, "byteOffset", byteOffset) ||
!ValidateNonNegative(funcName, "instanceCount", instanceCount))
{
return false;
}
-
- if (!ValidateStencilParamsForDrawCall())
- return false;
+ const auto indexCount = uint32_t(rawIndexCount);
- 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;
}
////
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);
gl->fPrimitiveRestartIndex(ones);
}
}
////
-
- const GLsizei first = byteOffset / bytesPerElem;
- const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(vertCount);
+ // Index fetching
- if (!checked_byteCount.isValid()) {
- ErrorInvalidValue("%s: Overflow in byteCount.", funcName);
- return false;
+ if (!indexCount || !instanceCount) {
+ *out_lastVert = Nothing();
+ return true;
}
- if (!mBoundVertexArray->mElementArrayBuffer) {
- ErrorInvalidOperation("%s: Must have element array buffer binding.", funcName);
- return false;
- }
+ const auto& indexBuffer = mBoundVertexArray->mElementArrayBuffer;
- WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mElementArrayBuffer;
-
- if (!elemArrayBuffer.ByteLength()) {
- ErrorInvalidOperation("%s: Bound element array buffer doesn't have any data.",
- funcName);
+ 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;
}
- CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + 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);
- return false;
- }
-
+ *out_lastVert = indexBuffer->GetIndexedFetchMaxVert(type, byteOffset, indexCount);
return true;
}
static void
HandleDrawElementsErrors(WebGLContext* webgl, const char* funcName,
gl::GLContext::LocalErrorScope& errorScope)
{
const auto err = errorScope.GetError();
@@ -769,105 +697,113 @@ HandleDrawElementsErrors(WebGLContext* w
webgl->ErrorImplementationBug("%s: Unexpected driver error during indexed draw"
" call. Please file a bug.",
funcName);
return;
}
}
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;
MakeContextCurrent();
bool error = false;
ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
if (error)
return;
const GLsizei instanceCount = 1;
- if (!DrawElements_check(funcName, mode, vertCount, type, byteOffset, instanceCount))
+ Maybe<uint32_t> 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;
{
ScopedDrawCallWrapper wrapper(*this);
{
UniquePtr<gl::GLContext::LocalErrorScope> errorScope;
if (gl->IsANGLE()) {
errorScope.reset(new gl::GLContext::LocalErrorScope(*gl));
}
- AUTO_PROFILER_LABEL("glDrawElements", GRAPHICS);
- gl->fDrawElements(mode, vertCount, type,
- reinterpret_cast<GLvoid*>(byteOffset));
+ if (lastVert) {
+ AUTO_PROFILER_LABEL("glDrawElements", GRAPHICS);
+ gl->fDrawElements(mode, indexCount, type,
+ reinterpret_cast<GLvoid*>(byteOffset));
+ }
if (errorScope) {
HandleDrawElementsErrors(this, funcName, *errorScope);
}
}
}
Draw_cleanup(funcName);
}
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);
const char funcName[] = "drawElementsInstanced";
if (IsContextLost())
return;
MakeContextCurrent();
bool error = false;
ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
if (error)
return;
- if (!DrawElements_check(funcName, mode, vertCount, type, byteOffset, instanceCount))
+ Maybe<uint32_t> 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;
{
ScopedDrawCallWrapper wrapper(*this);
{
UniquePtr<gl::GLContext::LocalErrorScope> errorScope;
if (gl->IsANGLE()) {
errorScope.reset(new gl::GLContext::LocalErrorScope(*gl));
}
- AUTO_PROFILER_LABEL("glDrawElementsInstanced", GRAPHICS);
- gl->fDrawElementsInstanced(mode, vertCount, type,
- reinterpret_cast<GLvoid*>(byteOffset),
- instanceCount);
+ if (lastVert && instanceCount) {
+ AUTO_PROFILER_LABEL("glDrawElementsInstanced", GRAPHICS);
+ gl->fDrawElementsInstanced(mode, indexCount, type,
+ reinterpret_cast<GLvoid*>(byteOffset),
+ instanceCount);
+ }
if (errorScope) {
HandleDrawElementsErrors(this, funcName, *errorScope);
}
}
}
Draw_cleanup(funcName);
@@ -914,156 +850,47 @@ WebGLContext::Draw_cleanup(const char* f
GenerateWarning("%s: Drawing to a destination rect smaller than the viewport"
" rect. (This warning will only be given once)",
funcName);
mAlreadyWarnedAboutViewportLargerThanDest = true;
}
}
}
-/*
- * 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<size_t>(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()) {
// Failures in conformance/attribs/gl-disabled-vertex-attrib.
// Even in Core profiles on NV. Sigh.
legacyAttrib0 |= (gl->Vendor() == gl::GLVendor::NVIDIA);
}
#endif
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;
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 "
@@ -1097,20 +924,22 @@ WebGLContext::DoFakeVertexAttrib0(const
default:
MOZ_CRASH();
}
////
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();
if (mFakeVertexAttrib0BufferObjectSize < dataSize) {
gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
mFakeVertexAttrib0BufferObjectSize = dataSize;
mFakeVertexAttrib0DataDefined = false;
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -748,16 +748,17 @@ WebGLContext::InitAndValidateGL(FailureR
mPixelStore_PackSkipRows = 0;
mPixelStore_PackSkipPixels = 0;
mPixelStore_PackAlignment = 4;
mPrimRestartTypeBytes = 0;
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,
sizeof(mGenericVertexAttrib0Data));
mFakeVertexAttrib0BufferObject = 0;
mNeedsIndexValidation = !gl->IsSupported(gl::GLFeature::robust_buffer_access_behavior);
--- a/dom/canvas/WebGLContextVertexArray.cpp
+++ b/dom/canvas/WebGLContextVertexArray.cpp
@@ -16,18 +16,16 @@ void
WebGLContext::BindVertexArray(WebGLVertexArray* array)
{
if (IsContextLost())
return;
if (array && !ValidateObject("bindVertexArrayObject", *array))
return;
- InvalidateBufferFetching();
-
MakeContextCurrent();
if (mBoundVertexArray) {
mBoundVertexArray->AddBufferBindCounts(-1);
}
if (array == nullptr) {
array = mDefaultVertexArray;
--- a/dom/canvas/WebGLContextVertices.cpp
+++ b/dom/canvas/WebGLContextVertices.cpp
@@ -77,16 +77,17 @@ WebGLContext::VertexAttrib4f(GLuint inde
gl->MakeCurrent();
if (index || !gl->IsCompatibilityProfile()) {
gl->fVertexAttrib4f(index, x, y, z, w);
}
////
mGenericVertexAttribTypes[index] = LOCAL_GL_FLOAT;
+ mGenericVertexAttribTypeInvalidator.InvalidateCaches();
if (!index) {
const float data[4] = { x, y, z, w };
memcpy(mGenericVertexAttrib0Data, data, sizeof(data));
}
}
void
@@ -108,16 +109,17 @@ WebGL2Context::VertexAttribI4i(GLuint in
gl->MakeCurrent();
if (index || !gl->IsCompatibilityProfile()) {
gl->fVertexAttribI4i(index, x, y, z, w);
}
////
mGenericVertexAttribTypes[index] = LOCAL_GL_INT;
+ mGenericVertexAttribTypeInvalidator.InvalidateCaches();
if (!index) {
const int32_t data[4] = { x, y, z, w };
memcpy(mGenericVertexAttrib0Data, data, sizeof(data));
}
}
void
@@ -139,16 +141,17 @@ WebGL2Context::VertexAttribI4ui(GLuint i
gl->MakeCurrent();
if (index || !gl->IsCompatibilityProfile()) {
gl->fVertexAttribI4ui(index, x, y, z, w);
}
////
mGenericVertexAttribTypes[index] = LOCAL_GL_UNSIGNED_INT;
+ mGenericVertexAttribTypeInvalidator.InvalidateCaches();
if (!index) {
const uint32_t data[4] = { x, y, z, w };
memcpy(mGenericVertexAttrib0Data, data, sizeof(data));
}
}
////////////////////////////////////////
@@ -158,42 +161,42 @@ WebGLContext::EnableVertexAttribArray(GL
{
if (IsContextLost())
return;
if (!ValidateAttribIndex(index, "enableVertexAttribArray"))
return;
MakeContextCurrent();
- InvalidateBufferFetching();
gl->fEnableVertexAttribArray(index);
MOZ_ASSERT(mBoundVertexArray);
mBoundVertexArray->mAttribs[index].mEnabled = true;
+ mBoundVertexArray->InvalidateCaches();
}
void
WebGLContext::DisableVertexAttribArray(GLuint index)
{
if (IsContextLost())
return;
if (!ValidateAttribIndex(index, "disableVertexAttribArray"))
return;
MakeContextCurrent();
- InvalidateBufferFetching();
if (index || !gl->IsCompatibilityProfile()) {
gl->fDisableVertexAttribArray(index);
}
MOZ_ASSERT(mBoundVertexArray);
mBoundVertexArray->mAttribs[index].mEnabled = false;
+ mBoundVertexArray->InvalidateCaches();
}
JS::Value
WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
ErrorResult& rv)
{
const char funcName[] = "getVertexAttrib";
if (IsContextLost())
@@ -420,36 +423,32 @@ WebGLContext::VertexAttribAnyPointer(con
reinterpret_cast<void*>(byteOffset));
} else {
gl->fVertexAttribPointer(index, size, type, normalized, stride,
reinterpret_cast<void*>(byteOffset));
}
WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
vd.VertexAttribPointer(isFuncInt, buffer, size, type, normalized, stride, byteOffset);
-
- InvalidateBufferFetching();
+ mBoundVertexArray->InvalidateCaches();
}
////////////////////////////////////////
void
WebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor)
{
if (IsContextLost())
return;
if (!ValidateAttribIndex(index, "vertexAttribDivisor"))
return;
MOZ_ASSERT(mBoundVertexArray);
-
- WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
- vd.mDivisor = divisor;
-
- InvalidateBufferFetching();
+ mBoundVertexArray->mAttribs[index].mDivisor = divisor;
+ mBoundVertexArray->InvalidateCaches();
MakeContextCurrent();
gl->fVertexAttribDivisor(index, divisor);
}
} // namespace mozilla
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -7,21 +7,23 @@
#include "GLContext.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/dom/WebGL2RenderingContextBinding.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#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 {
/* If `name`: "foo[3]"
* Then returns true, with
* `out_baseName`: "foo"
* `out_isArray`: true
* `out_index`: 3
@@ -259,16 +261,20 @@ QueryProgramInfo(WebGLProgram* prog, gl:
const bool isArray = false;
const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
elemType, isArray,
userName,
mappedName);
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)
const bool needsCheckForArrays = gl->WorkAroundDriverBugs();
GLuint numActiveUniforms = 0;
gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORMS,
@@ -438,28 +444,124 @@ QueryProgramInfo(WebGLProgram* prog, gl:
return info.forget();
}
////////////////////////////////////////////////////////////////////////////////
webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
: prog(prog)
, transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode)
+ , attrib0Active(false)
{ }
webgl::LinkedProgramInfo::~LinkedProgramInfo()
{
for (auto& cur : uniforms) {
delete cur;
}
for (auto& cur : uniformBlocks) {
delete cur;
}
}
+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<const CacheMapInvalidator*> 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<uint64_t>(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
static GLuint
CreateProgram(gl::GLContext* gl)
{
gl->MakeCurrent();
return gl->fCreateProgram();
@@ -1064,17 +1166,16 @@ WebGLProgram::LinkProgram()
if (mNumActiveTFOs) {
mContext->ErrorInvalidOperation("%s: Program is in-use by one or more active"
" transform feedback objects.",
funcName);
return;
}
mContext->MakeContextCurrent();
- mContext->InvalidateBufferFetching(); // we do it early in this function
// as some of the validation changes program state
mLinkLog.Truncate();
mMostRecentLinkInfo = nullptr;
if (!ValidateForLink()) {
mContext->GenerateWarning("%s: %s", funcName, mLinkLog.BeginReading());
return;
@@ -1360,18 +1461,16 @@ WebGLProgram::UseProgram() const
{
mContext->ErrorInvalidOperation("%s: Transform feedback active and not paused.",
funcName);
return false;
}
mContext->MakeContextCurrent();
- mContext->InvalidateBufferFetching();
-
mContext->gl->fUseProgram(mGLName);
return true;
}
void
WebGLProgram::ValidateProgram() const
{
mContext->MakeContextCurrent();
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -12,16 +12,17 @@
#include <vector>
#include "mozilla/LinkedList.h"
#include "mozilla/RefPtr.h"
#include "mozilla/WeakPtr.h"
#include "nsString.h"
#include "nsWrapperCache.h"
+#include "CacheMap.h"
#include "WebGLContext.h"
#include "WebGLObjectModel.h"
namespace mozilla {
class ErrorResult;
class WebGLActiveInfo;
class WebGLProgram;
class WebGLShader;
@@ -70,16 +71,21 @@ struct UniformBlockInfo final
const nsACString& mappedName, uint32_t dataSize)
: mUserName(userName)
, mMappedName(mappedName)
, mDataSize(dataSize)
, mBinding(&webgl->mIndexedUniformBufferBindings[0])
{ }
};
+struct CachedDrawFetchLimits final {
+ uint64_t maxVerts;
+ uint64_t maxInstances;
+};
+
struct LinkedProgramInfo final
: public RefCounted<LinkedProgramInfo>
, public SupportsWeakPtr<LinkedProgramInfo>
{
friend class WebGLProgram;
MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo)
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(LinkedProgramInfo)
@@ -94,21 +100,32 @@ struct LinkedProgramInfo final
std::vector<UniformBlockInfo*> uniformBlocks; // Owns its contents.
std::vector<RefPtr<WebGLActiveInfo>> transformFeedbackVaryings;
// Needed for draw call validation.
std::vector<UniformInfo*> uniformSamplers;
mutable std::vector<size_t> componentsPerTFVert;
+ bool attrib0Active;
+
//////
// The maps for the frag data names to the translated names.
std::map<nsCString, const nsCString> fragDataMap;
+ //////
+
+ mutable CacheMap<const WebGLVertexArray*,
+ CachedDrawFetchLimits> mDrawFetchCache;
+
+ const CachedDrawFetchLimits* GetDrawFetchLimits(const char* funcName) const;
+
+ //////
+
explicit LinkedProgramInfo(WebGLProgram* prog);
~LinkedProgramInfo();
bool FindAttrib(const nsCString& userName, const AttribInfo** const out_info) const;
bool FindUniform(const nsCString& userName, nsCString* const out_mappedName,
size_t* const out_arrayIndex, UniformInfo** const out_info) const;
bool MapFragDataName(const nsCString& userName,
nsCString* const out_mappedName) const;
--- a/dom/canvas/WebGLVertexArray.h
+++ b/dom/canvas/WebGLVertexArray.h
@@ -5,28 +5,33 @@
#ifndef WEBGL_VERTEX_ARRAY_H_
#define WEBGL_VERTEX_ARRAY_H_
#include "nsTArray.h"
#include "mozilla/LinkedList.h"
#include "nsWrapperCache.h"
+#include "CacheMap.h"
#include "WebGLObjectModel.h"
#include "WebGLStrongTypes.h"
#include "WebGLVertexAttribData.h"
namespace mozilla {
class WebGLVertexArrayFake;
+namespace webgl {
+struct LinkedProgramInfo;
+}
class WebGLVertexArray
: public nsWrapperCache
, public WebGLRefCountedObject<WebGLVertexArray>
, public LinkedListElement<WebGLVertexArray>
+ , public CacheMapInvalidator
{
public:
static WebGLVertexArray* Create(WebGLContext* webgl);
void BindVertexArray() {
// Bind to dummy value to signal that this vertex array has ever been
// bound.
BindVertexArrayImpl();
@@ -61,13 +66,14 @@ protected:
GLuint mGLName;
nsTArray<WebGLVertexAttribData> mAttribs;
WebGLRefPtr<WebGLBuffer> mElementArrayBuffer;
friend class ScopedDrawHelper;
friend class WebGLContext;
friend class WebGLVertexArrayFake;
friend class WebGL2Context;
+ friend struct webgl::LinkedProgramInfo;
};
} // namespace mozilla
#endif // WEBGL_VERTEX_ARRAY_H_