--- a/dom/canvas/WebGL2ContextBuffers.cpp
+++ b/dom/canvas/WebGL2ContextBuffers.cpp
@@ -26,25 +26,16 @@ WebGL2Context::CopyBufferSubData(GLenum
const auto& readBuffer = ValidateBufferSelection(funcName, readTarget);
if (!readBuffer)
return;
const auto& writeBuffer = ValidateBufferSelection(funcName, writeTarget);
if (!writeBuffer)
return;
- if (readBuffer->mNumActiveTFOs ||
- writeBuffer->mNumActiveTFOs)
- {
- ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
- " object.",
- funcName);
- return;
- }
-
if (!ValidateNonNegative(funcName, "readOffset", readOffset) ||
!ValidateNonNegative(funcName, "writeOffset", writeOffset) ||
!ValidateNonNegative(funcName, "size", size))
{
return;
}
const auto fnValidateOffsetSize = [&](const char* info, GLintptr offset,
@@ -124,31 +115,16 @@ WebGL2Context::GetBufferSubData(GLenum t
if (!buffer)
return;
if (!buffer->ValidateRange(funcName, srcByteOffset, byteLen))
return;
////
- if (buffer->mNumActiveTFOs) {
- ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
- " object.",
- funcName);
- return;
- }
-
- if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
- mBoundTransformFeedback->mIsActive)
- {
- ErrorInvalidOperation("%s: Currently bound transform feedback is active.",
- funcName);
- return;
- }
-
if (!CheckedInt<GLsizeiptr>(byteLen).isValid()) {
ErrorOutOfMemory("%s: Size too large.", funcName);
return;
}
const GLsizeiptr glByteLen(byteLen);
////
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -13,25 +13,22 @@
namespace mozilla {
WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
: WebGLContextBoundObject(webgl)
, mGLName(buf)
, mContent(Kind::Undefined)
, mUsage(LOCAL_GL_STATIC_DRAW)
, mByteLength(0)
- , mNumActiveTFOs(0)
- , mBoundForTF(false)
{
mContext->mBuffers.insertBack(this);
}
WebGLBuffer::~WebGLBuffer()
{
- MOZ_ASSERT(!mNumActiveTFOs);
DeleteOnce();
}
void
WebGLBuffer::SetContentAfterBind(GLenum target)
{
if (mContent != Kind::Undefined)
return;
@@ -106,23 +103,16 @@ WebGLBuffer::BufferData(GLenum target, s
// Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
// is like intptr_t.
if (!CheckedInt<GLsizeiptr>(size).isValid())
return mContext->ErrorOutOfMemory("%s: bad size", funcName);
if (!ValidateBufferUsageEnum(mContext, funcName, usage))
return;
- if (mNumActiveTFOs) {
- mContext->ErrorInvalidOperation("%s: Buffer is bound to an active transform"
- " feedback object.",
- funcName);
- return;
- }
-
const auto& gl = mContext->gl;
gl->MakeCurrent();
const ScopedLazyBind lazyBind(gl, target, this);
mContext->InvalidateBufferFetching();
#ifdef XP_MACOSX
// bug 790879
if (gl->WorkAroundDriverBugs() &&
@@ -215,25 +205,16 @@ bool
WebGLBuffer::IsElementArrayUsedWithMultipleTypes() const
{
return mCache->BeenUsedWithMultipleTypes();
}
bool
WebGLBuffer::ValidateCanBindToTarget(const char* funcName, GLenum target)
{
- const bool wouldBeTF = (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER);
- if (mWebGLRefCnt && wouldBeTF != mBoundForTF) {
- mContext->ErrorInvalidOperation("%s: Buffers cannot be simultaneously bound to "
- " transform feedback and bound elsewhere.",
- funcName);
- return false;
- }
- mBoundForTF = wouldBeTF;
-
/* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1
*
* In the WebGL 2 API, buffers have their WebGL buffer type
* initially set to undefined. Calling bindBuffer, bindBufferRange
* or bindBufferBase with the target argument set to any buffer
* binding point except COPY_READ_BUFFER or COPY_WRITE_BUFFER will
* then set the WebGL buffer type of the buffer being bound
* according to the table above.
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -74,15 +74,13 @@ public:
protected:
~WebGLBuffer();
Kind mContent;
GLenum mUsage;
size_t mByteLength;
UniquePtr<WebGLElementArrayCache> mCache;
- size_t mNumActiveTFOs;
- bool mBoundForTF;
};
} // namespace mozilla
#endif // WEBGL_BUFFER_H_
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -115,16 +115,17 @@ WebGLContextOptions::WebGLContextOptions
WebGLContext::WebGLContext()
: WebGLContextUnchecked(nullptr)
, mBufferFetchingIsVerified(false)
, mBufferFetchingHasPerVertex(false)
, mMaxFetchedVertices(0)
, mMaxFetchedInstances(0)
, mLayerIsMirror(false)
, mBypassShaderValidation(false)
+ , mBuffersForUB_Dirty(true)
, mContextLossHandler(this)
, mNeedsFakeNoAlpha(false)
, mNeedsFakeNoDepth(false)
, mNeedsFakeNoStencil(false)
, mNeedsEmulatedLoneDepthStencil(false)
{
mGeneration = 0;
mInvalidated = false;
@@ -257,16 +258,17 @@ WebGLContext::DestroyResourcesAndContext
mBoundTransformFeedback = nullptr;
mDefaultTransformFeedback = nullptr;
mQuerySlot_SamplesPassed = nullptr;
mQuerySlot_TFPrimsWritten = nullptr;
mQuerySlot_TimeElapsed = nullptr;
mIndexedUniformBufferBindings.clear();
+ OnUBIndexedBindingsChanged();
//////
ClearLinkedList(mBuffers);
ClearLinkedList(mFramebuffers);
ClearLinkedList(mPrograms);
ClearLinkedList(mQueries);
ClearLinkedList(mRenderbuffers);
@@ -2565,16 +2567,52 @@ WebGLContext::ValidateArrayBufferView(co
elemCount = elemCountOverride;
}
*out_bytes = bytes + (elemOffset * elemSize);
*out_byteLen = elemCount * elemSize;
return true;
}
+////
+
+const decltype(WebGLContext::mBuffersForUB)&
+WebGLContext::BuffersForUB() const
+{
+ if (mBuffersForUB_Dirty) {
+ mBuffersForUB.clear();
+ for (const auto& cur : mIndexedUniformBufferBindings) {
+ if (cur.mBufferBinding) {
+ mBuffersForUB.insert(cur.mBufferBinding.get());
+ }
+ }
+ mBuffersForUB_Dirty = false;
+ }
+ return mBuffersForUB;
+}
+
+////
+
+bool
+WebGLContext::ValidateForNonTransformFeedback(const char* funcName, WebGLBuffer* buffer)
+{
+ if (!mBoundTransformFeedback)
+ return true;
+
+ const auto& buffersForTF = mBoundTransformFeedback->BuffersForTF();
+ if (buffersForTF.count(buffer)) {
+ ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for transform"
+ " feedback.",
+ funcName);
+ return false;
+ }
+
+ return true;
+}
+
////////////////////////////////////////////////////////////////////////////////
// XPCOM goop
void
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
const std::vector<IndexedBufferBinding>& field,
const char* name, uint32_t flags)
{
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1580,32 +1580,36 @@ protected:
uint32_t* const out_width, uint32_t* const out_height);
bool HasDrawBuffers() const {
return IsWebGL2() ||
IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers);
}
WebGLRefPtr<WebGLBuffer>* ValidateBufferSlot(const char* funcName, GLenum target);
+public:
WebGLBuffer* ValidateBufferSelection(const char* funcName, GLenum target);
+protected:
IndexedBufferBinding* ValidateIndexedBufferSlot(const char* funcName, GLenum target,
GLuint index);
bool ValidateIndexedBufferBinding(const char* funcName, GLenum target, GLuint index,
WebGLRefPtr<WebGLBuffer>** const out_genericBinding,
IndexedBufferBinding** const out_indexedBinding);
bool ValidateNonNegative(const char* funcName, const char* argName, int64_t val) {
if (MOZ_UNLIKELY(val < 0)) {
ErrorInvalidValue("%s: `%s` must be non-negative.", funcName, argName);
return false;
}
return true;
}
+ bool ValidateForNonTransformFeedback(const char* funcName, WebGLBuffer* buffer);
+
public:
bool ValidateArrayBufferView(const char* funcName, const dom::ArrayBufferView& view,
GLuint elemOffset, GLuint elemCountOverride,
uint8_t** const out_bytes, size_t* const out_byteLen);
protected:
////
@@ -1741,16 +1745,27 @@ protected:
UniquePtr<FakeBlackTexture> mFakeBlack_3D_0001;
UniquePtr<FakeBlackTexture> mFakeBlack_2D_Array_0000;
UniquePtr<FakeBlackTexture> mFakeBlack_2D_Array_0001;
bool BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fakeBlack);
////////////////////////////////////
+private:
+ mutable bool mBuffersForUB_Dirty;
+ mutable std::set<const WebGLBuffer*> mBuffersForUB;
+
+public:
+ void OnUBIndexedBindingsChanged() const { mBuffersForUB_Dirty = true; }
+ const decltype(mBuffersForUB)& BuffersForUB() const;
+
+ ////////////////////////////////////
+
+protected:
// Generic Vertex Attributes
UniquePtr<GLenum[]> mVertexAttribType;
GLfloat mVertexAttrib0Vector[4];
GLfloat mFakeVertexAttrib0BufferObjectVector[4];
size_t mFakeVertexAttrib0BufferObjectSize;
GLuint mFakeVertexAttrib0BufferObject;
WebGLVertexAttrib0Status mFakeVertexAttrib0BufferStatus;
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -70,16 +70,19 @@ WebGLContext::ValidateBufferSelection(co
return nullptr;
const auto& buffer = *slot;
if (!buffer) {
ErrorInvalidOperation("%s: Buffer for `target` is null.", funcName);
return nullptr;
}
+ if (!ValidateForNonTransformFeedback(funcName, buffer.get()))
+ return nullptr;
+
return buffer.get();
}
IndexedBufferBinding*
WebGLContext::ValidateIndexedBufferSlot(const char* funcName, GLenum target, GLuint index)
{
decltype(mIndexedUniformBufferBindings)* bindings;
const char* maxIndexEnum;
@@ -207,16 +210,25 @@ WebGLContext::BindBufferBase(GLenum targ
*genericBinding = buffer;
indexedBinding->mBufferBinding = buffer;
indexedBinding->mRangeStart = 0;
indexedBinding->mRangeSize = 0;
if (buffer) {
buffer->SetContentAfterBind(target);
}
+
+ switch (target) {
+ case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
+ mBoundTransformFeedback->OnIndexedBindingsChanged();
+ break;
+ case LOCAL_GL_UNIFORM:
+ OnUBIndexedBindingsChanged();
+ break;
+ }
}
void
WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
WebGLintptr offset, WebGLsizeiptr size)
{
const char funcName[] = "bindBufferRange";
if (IsContextLost())
@@ -291,16 +303,25 @@ WebGLContext::BindBufferRange(GLenum tar
*genericBinding = buffer;
indexedBinding->mBufferBinding = buffer;
indexedBinding->mRangeStart = offset;
indexedBinding->mRangeSize = size;
if (buffer) {
buffer->SetContentAfterBind(target);
}
+
+ switch (target) {
+ case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
+ mBoundTransformFeedback->OnIndexedBindingsChanged();
+ break;
+ case LOCAL_GL_UNIFORM:
+ OnUBIndexedBindingsChanged();
+ break;
+ }
}
////////////////////////////////////////
void
WebGLContext::BufferDataImpl(GLenum target, size_t dataLen, const uint8_t* data,
GLenum usage)
{
@@ -388,23 +409,16 @@ WebGLContext::BufferSubDataImpl(GLenum t
if (!ValidateNonNegative(funcName, "byteOffset", dstByteOffset))
return;
const auto& buffer = ValidateBufferSelection(funcName, target);
if (!buffer)
return;
- if (buffer->mNumActiveTFOs) {
- ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
- " object.",
- "bufferSubData");
- return;
- }
-
if (!buffer->ValidateRange(funcName, dstByteOffset, dataLen))
return;
if (!CheckedInt<GLintptr>(dataLen).isValid()) {
ErrorOutOfMemory("%s: Size too large.", funcName);
return;
}
const GLintptr glDataLen(dataLen);
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -14,16 +14,18 @@
#include "WebGLFramebuffer.h"
#include "WebGLProgram.h"
#include "WebGLRenderbuffer.h"
#include "WebGLShader.h"
#include "WebGLTexture.h"
#include "WebGLVertexArray.h"
#include "WebGLVertexAttribData.h"
+#include <algorithm>
+
namespace mozilla {
// For a Tegra workaround.
static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100;
////////////////////////////////////////
class ScopedResolveTexturesForDraw
@@ -295,16 +297,26 @@ WebGLContext::DrawArrays_check(const cha
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)
@@ -338,17 +350,17 @@ public:
*out_error = true;
return;
}
mDidFake = true;
////
// Check UBO sizes.
- const auto& linkInfo = webgl->mActiveProgramLinkInfo;
+ const auto& linkInfo = mWebGL->mActiveProgramLinkInfo;
for (const auto& cur : linkInfo->uniformBlocks) {
const auto& dataSize = cur->mDataSize;
const auto& binding = cur->mBinding;
if (!binding) {
mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is null.",
funcName);
*out_error = true;
return;
@@ -361,16 +373,32 @@ public:
funcName);
*out_error = true;
return;
}
}
////
+ const auto& tfo = mWebGL->mBoundTransformFeedback;
+ if (tfo) {
+ const auto& buffersForTF = tfo->BuffersForTF();
+ const auto& buffersForUB = mWebGL->BuffersForUB();
+ if (DoSetsIntersect(buffersForTF, buffersForUB)) {
+ mWebGL->ErrorInvalidOperation("%s: At least one WebGLBuffer is bound for"
+ " both transform feedback and as a uniform"
+ " buffer.",
+ funcName);
+ *out_error = true;
+ return;
+ }
+ }
+
+ ////
+
mWebGL->RunContextLossTimer();
}
~ScopedDrawHelper() {
if (mDidFake) {
mWebGL->UndoFakeVertexAttrib0();
}
}
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1464,60 +1464,52 @@ WebGLContext::ReadPixels(GLint x, GLint
ReadPixelsImpl(x, y, width, height, format, type, bytes, byteLen);
}
void
WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
GLenum type, WebGLsizeiptr offset, ErrorResult& out_error)
{
+ const char funcName[] = "readPixels";
if (!ReadPixels_SharedPrecheck(&out_error))
return;
- if (!mBoundPixelPackBuffer) {
- ErrorInvalidOperation("readPixels: PIXEL_PACK_BUFFER must not be null.");
+ const auto& buffer = ValidateBufferSelection(funcName, LOCAL_GL_PIXEL_PACK_BUFFER);
+ if (!buffer)
return;
- }
-
- if (mBoundPixelPackBuffer->mNumActiveTFOs) {
- ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
- " object.",
- "readPixels");
- return;
- }
//////
- if (offset < 0) {
- ErrorInvalidValue("readPixels: offset must not be negative.");
+ if (!ValidateNonNegative(funcName, "offset", offset))
return;
- }
{
const auto bytesPerType = webgl::BytesPerPixel({LOCAL_GL_RED, type});
if (offset % bytesPerType != 0) {
- ErrorInvalidOperation("readPixels: `offset` must be divisible by the size"
- " a `type` in bytes.");
+ ErrorInvalidOperation("%s: `offset` must be divisible by the size of `type`"
+ " in bytes.",
+ funcName);
return;
}
}
//////
- const auto bytesAvailable = mBoundPixelPackBuffer->ByteLength();
+ const auto bytesAvailable = buffer->ByteLength();
const auto checkedBytesAfterOffset = CheckedUint32(bytesAvailable) - offset;
uint32_t bytesAfterOffset = 0;
if (checkedBytesAfterOffset.isValid()) {
bytesAfterOffset = checkedBytesAfterOffset.value();
}
gl->MakeCurrent();
- const ScopedLazyBind lazyBind(gl, LOCAL_GL_PIXEL_PACK_BUFFER, mBoundPixelPackBuffer);
+ const ScopedLazyBind lazyBind(gl, LOCAL_GL_PIXEL_PACK_BUFFER, buffer);
ReadPixelsImpl(x, y, width, height, format, type, (void*)offset, bytesAfterOffset);
}
static bool
ValidateReadPixelsFormatAndType(const webgl::FormatInfo* srcFormat,
const webgl::PackingInfo& pi, gl::GLContext* gl,
WebGLContext* webgl)
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -1439,17 +1439,17 @@ WebGLProgram::TransformFeedbackVaryings(
case LOCAL_GL_INTERLEAVED_ATTRIBS:
break;
case LOCAL_GL_SEPARATE_ATTRIBS:
{
GLuint maxAttribs = 0;
gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
&maxAttribs);
- if (varyings.Length() >= maxAttribs) {
+ if (varyings.Length() > maxAttribs) {
mContext->ErrorInvalidValue("%s: Length of `varyings` exceeds %s.",
funcName,
"TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
return;
}
}
break;
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -180,24 +180,29 @@ FromView(WebGLContext* webgl, const char
}
}
return MakeUnique<webgl::TexUnpackBytes>(webgl, target, width, height, depth,
isClientData, bytes, availByteCount);
}
static UniquePtr<webgl::TexUnpackBytes>
FromPboOffset(WebGLContext* webgl, const char* funcName, TexImageTarget target,
- uint32_t width, uint32_t height, uint32_t depth, WebGLsizeiptr pboOffset,
- size_t availBufferBytes)
+ uint32_t width, uint32_t height, uint32_t depth, WebGLsizeiptr pboOffset)
{
if (pboOffset < 0) {
webgl->ErrorInvalidValue("%s: offset cannot be negative.", funcName);
return nullptr;
}
+ const auto& buffer = webgl->ValidateBufferSelection(funcName,
+ LOCAL_GL_PIXEL_UNPACK_BUFFER);
+ if (!buffer)
+ return nullptr;
+
+ size_t availBufferBytes = buffer->ByteLength();
if (size_t(pboOffset) > availBufferBytes) {
webgl->ErrorInvalidOperation("%s: Offset is passed end of buffer.", funcName);
return nullptr;
}
availBufferBytes -= pboOffset;
const bool isClientData = false;
const auto ptr = (const uint8_t*)pboOffset;
@@ -361,35 +366,22 @@ WebGLContext::From(const char* funcName,
uint32_t width, height, depth;
if (!ValidateExtents(this, funcName, rawWidth, rawHeight, rawDepth, border, &width,
&height, &depth))
{
return nullptr;
}
if (src.mPboOffset) {
- if (!mBoundPixelUnpackBuffer) {
- ErrorInvalidOperation("%s: PACK_BUFFER must be non-null.", funcName);
- return nullptr;
- }
-
- if (mBoundPixelUnpackBuffer->mNumActiveTFOs) {
- ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
- " object.",
- funcName);
- return nullptr;
- }
-
- const auto& availBytes = mBoundPixelUnpackBuffer->ByteLength();
return FromPboOffset(this, funcName, target, width, height, depth,
- *(src.mPboOffset), availBytes);
+ *(src.mPboOffset));
}
if (mBoundPixelUnpackBuffer) {
- ErrorInvalidOperation("%s: PACK_BUFFER must be null.", funcName);
+ ErrorInvalidOperation("%s: PIXEL_UNPACK_BUFFER must be null.", funcName);
return nullptr;
}
if (src.mImageData) {
return FromImageData(this, funcName, target, width, height, depth,
*(src.mImageData), scopedArr);
}
@@ -1365,35 +1357,22 @@ WebGLContext::FromCompressed(const char*
uint32_t width, height, depth;
if (!ValidateExtents(this, funcName, rawWidth, rawHeight, rawDepth, border, &width,
&height, &depth))
{
return nullptr;
}
if (src.mPboOffset) {
- if (!mBoundPixelUnpackBuffer) {
- ErrorInvalidOperation("%s: PACK_BUFFER must be non-null.", funcName);
- return nullptr;
- }
-
- if (mBoundPixelUnpackBuffer->mNumActiveTFOs) {
- ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
- " object.",
- funcName);
- return nullptr;
- }
-
- const auto& availBytes = mBoundPixelUnpackBuffer->ByteLength();
return FromPboOffset(this, funcName, target, width, height, depth,
- *(src.mPboOffset), availBytes);
+ *(src.mPboOffset));
}
if (mBoundPixelUnpackBuffer) {
- ErrorInvalidOperation("%s: PACK_BUFFER must be null.", funcName);
+ ErrorInvalidOperation("%s: PIXEL_UNPACK_BUFFER must be null.", funcName);
return nullptr;
}
return FromView(this, funcName, target, width, height, depth, src.mView,
src.mViewElemOffset, src.mViewElemLengthOverride);
}
void
--- a/dom/canvas/WebGLTransformFeedback.cpp
+++ b/dom/canvas/WebGLTransformFeedback.cpp
@@ -12,16 +12,17 @@
namespace mozilla {
WebGLTransformFeedback::WebGLTransformFeedback(WebGLContext* webgl, GLuint tf)
: WebGLContextBoundObject(webgl)
, mGLName(tf)
, mIndexedBindings(webgl->mGLMaxTransformFeedbackSeparateAttribs)
, mIsPaused(false)
, mIsActive(false)
+ , mBuffersForTF_Dirty(true)
{
mContext->mTransformFeedbacks.insertBack(this);
}
WebGLTransformFeedback::~WebGLTransformFeedback()
{
DeleteOnce();
}
@@ -31,16 +32,38 @@ WebGLTransformFeedback::Delete()
{
if (mGLName) {
mContext->MakeContextCurrent();
mContext->gl->fDeleteTransformFeedbacks(1, &mGLName);
}
removeFrom(mContext->mTransformFeedbacks);
}
+////
+
+const decltype(WebGLTransformFeedback::mBuffersForTF)&
+WebGLTransformFeedback::BuffersForTF() const
+{
+ // The generic bind point cannot incur undefined read/writes because otherwise it
+ // would be impossible to read back from this. The spec implies that readback from
+ // the TRANSFORM_FEEDBACK target is possible, just not simultaneously with being
+ // "bound or in use for transform feedback".
+ // Therefore, only the indexed bindings of the TFO count.
+ if (mBuffersForTF_Dirty) {
+ mBuffersForTF.clear();
+ for (const auto& cur : mIndexedBindings) {
+ if (cur.mBufferBinding) {
+ mBuffersForTF.insert(cur.mBufferBinding.get());
+ }
+ }
+ mBuffersForTF_Dirty = false;
+ }
+ return mBuffersForTF;
+}
+
////////////////////////////////////////
void
WebGLTransformFeedback::BeginTransformFeedback(GLenum primMode)
{
const char funcName[] = "beginTransformFeedback";
if (mIsActive)
@@ -102,23 +125,16 @@ WebGLTransformFeedback::BeginTransformFe
mActive_Program = prog;
mActive_PrimMode = primMode;
mActive_VertPosition = 0;
mActive_VertCapacity = minVertCapacity;
////
- for (const auto& cur : mIndexedBindings) {
- const auto& buffer = cur.mBufferBinding;
- if (buffer) {
- buffer->mNumActiveTFOs++;
- }
- }
-
mActive_Program->mNumActiveTFOs++;
}
void
WebGLTransformFeedback::EndTransformFeedback()
{
const char funcName[] = "endTransformFeedback";
@@ -134,23 +150,16 @@ WebGLTransformFeedback::EndTransformFeed
////
mIsActive = false;
mIsPaused = false;
////
- for (const auto& cur : mIndexedBindings) {
- const auto& buffer = cur.mBufferBinding;
- if (buffer) {
- buffer->mNumActiveTFOs--;
- }
- }
-
mActive_Program->mNumActiveTFOs--;
}
void
WebGLTransformFeedback::PauseTransformFeedback()
{
const char funcName[] = "pauseTransformFeedback";
--- a/dom/canvas/WebGLTransformFeedback.h
+++ b/dom/canvas/WebGLTransformFeedback.h
@@ -32,29 +32,37 @@ private:
bool mIsPaused;
bool mIsActive;
// Not in state tables:
WebGLRefPtr<WebGLProgram> mActive_Program;
MOZ_INIT_OUTSIDE_CTOR GLenum mActive_PrimMode;
MOZ_INIT_OUTSIDE_CTOR size_t mActive_VertPosition;
MOZ_INIT_OUTSIDE_CTOR size_t mActive_VertCapacity;
+ mutable bool mBuffersForTF_Dirty;
+ mutable std::set<const WebGLBuffer*> mBuffersForTF;
+
public:
WebGLTransformFeedback(WebGLContext* webgl, GLuint tf);
private:
~WebGLTransformFeedback();
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTransformFeedback)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTransformFeedback)
void Delete();
WebGLContext* GetParentObject() const { return mContext; }
virtual JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
+ ////
+
+ void OnIndexedBindingsChanged() const { mBuffersForTF_Dirty = true; }
+ const decltype(mBuffersForTF)& BuffersForTF() const;
+
// GL Funcs
void BeginTransformFeedback(GLenum primMode);
void EndTransformFeedback();
void PauseTransformFeedback();
void ResumeTransformFeedback();
};
} // namespace mozilla