summaryrefslogtreecommitdiffstats
path: root/dom/canvas/WebGLContextBuffers.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/canvas/WebGLContextBuffers.cpp279
1 files changed, 279 insertions, 0 deletions
diff --git a/dom/canvas/WebGLContextBuffers.cpp b/dom/canvas/WebGLContextBuffers.cpp
new file mode 100644
index 0000000000..0fa27a2f1e
--- /dev/null
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "ClientWebGLContext.h"
+
+#include <limits>
+
+#include "GLContext.h"
+#include "WebGLBuffer.h"
+#include "WebGLTransformFeedback.h"
+#include "WebGLVertexArray.h"
+
+namespace mozilla {
+
+RefPtr<WebGLBuffer>* WebGLContext::ValidateBufferSlot(GLenum target) {
+ RefPtr<WebGLBuffer>* slot = nullptr;
+
+ switch (target) {
+ case LOCAL_GL_ARRAY_BUFFER:
+ slot = &mBoundArrayBuffer;
+ break;
+
+ case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
+ slot = &(mBoundVertexArray->mElementArrayBuffer);
+ break;
+ }
+
+ if (IsWebGL2()) {
+ switch (target) {
+ case LOCAL_GL_COPY_READ_BUFFER:
+ slot = &mBoundCopyReadBuffer;
+ break;
+
+ case LOCAL_GL_COPY_WRITE_BUFFER:
+ slot = &mBoundCopyWriteBuffer;
+ break;
+
+ case LOCAL_GL_PIXEL_PACK_BUFFER:
+ slot = &mBoundPixelPackBuffer;
+ break;
+
+ case LOCAL_GL_PIXEL_UNPACK_BUFFER:
+ slot = &mBoundPixelUnpackBuffer;
+ break;
+
+ case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
+ slot = &mBoundTransformFeedbackBuffer;
+ break;
+
+ case LOCAL_GL_UNIFORM_BUFFER:
+ slot = &mBoundUniformBuffer;
+ break;
+ }
+ }
+
+ if (!slot) {
+ ErrorInvalidEnumInfo("target", target);
+ return nullptr;
+ }
+
+ return slot;
+}
+
+WebGLBuffer* WebGLContext::ValidateBufferSelection(GLenum target) const {
+ const auto& slot =
+ const_cast<WebGLContext*>(this)->ValidateBufferSlot(target);
+ if (!slot) return nullptr;
+ const auto& buffer = *slot;
+
+ if (!buffer) {
+ ErrorInvalidOperation("Buffer for `target` is null.");
+ return nullptr;
+ }
+
+ if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) {
+ if (mBoundTransformFeedback->IsActiveAndNotPaused()) {
+ ErrorInvalidOperation(
+ "Cannot select TRANSFORM_FEEDBACK_BUFFER when"
+ " transform feedback is active and unpaused.");
+ return nullptr;
+ }
+ const auto tfBuffers = std::vector<webgl::BufferAndIndex>{{
+ {buffer},
+ }};
+
+ if (!ValidateBuffersForTf(tfBuffers)) return nullptr;
+ } else {
+ if (mBoundTransformFeedback && !ValidateBufferForNonTf(buffer, target))
+ return nullptr;
+ }
+
+ return buffer.get();
+}
+
+IndexedBufferBinding* WebGLContext::ValidateIndexedBufferSlot(GLenum target,
+ GLuint index) {
+ decltype(mIndexedUniformBufferBindings)* bindings;
+ const char* maxIndexEnum;
+ switch (target) {
+ case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
+ if (mBoundTransformFeedback->mIsActive &&
+ !mBoundTransformFeedback->mIsPaused) {
+ ErrorInvalidOperation("Transform feedback active and not paused.");
+ return nullptr;
+ }
+ bindings = &(mBoundTransformFeedback->mIndexedBindings);
+ maxIndexEnum = "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS";
+ break;
+
+ case LOCAL_GL_UNIFORM_BUFFER:
+ bindings = &mIndexedUniformBufferBindings;
+ maxIndexEnum = "MAX_UNIFORM_BUFFER_BINDINGS";
+ break;
+
+ default:
+ ErrorInvalidEnumInfo("target", target);
+ return nullptr;
+ }
+
+ if (index >= bindings->size()) {
+ ErrorInvalidValue("`index` >= %s.", maxIndexEnum);
+ return nullptr;
+ }
+
+ return &(*bindings)[index];
+}
+
+////////////////////////////////////////
+
+void WebGLContext::BindBuffer(GLenum target, WebGLBuffer* buffer) {
+ FuncScope funcScope(*this, "bindBuffer");
+ if (IsContextLost()) return;
+ funcScope.mBindFailureGuard = true;
+
+ if (buffer && !ValidateObject("buffer", *buffer)) return;
+
+ const auto& slot = ValidateBufferSlot(target);
+ if (!slot) return;
+
+ if (buffer && !buffer->ValidateCanBindToTarget(target)) return;
+
+ if (!IsBufferTargetLazilyBound(target)) {
+ gl->fBindBuffer(target, buffer ? buffer->mGLName : 0);
+ }
+
+ *slot = buffer;
+ if (buffer) {
+ buffer->SetContentAfterBind(target);
+ }
+
+ funcScope.mBindFailureGuard = false;
+}
+
+////////////////////////////////////////
+
+bool WebGLContext::ValidateIndexedBufferBinding(
+ GLenum target, GLuint index, RefPtr<WebGLBuffer>** const out_genericBinding,
+ IndexedBufferBinding** const out_indexedBinding) {
+ *out_genericBinding = ValidateBufferSlot(target);
+ if (!*out_genericBinding) return false;
+
+ *out_indexedBinding = ValidateIndexedBufferSlot(target, index);
+ if (!*out_indexedBinding) return false;
+
+ if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
+ mBoundTransformFeedback->mIsActive) {
+ ErrorInvalidOperation(
+ "Cannot update indexed buffer bindings on active"
+ " transform feedback objects.");
+ return false;
+ }
+
+ return true;
+}
+
+void WebGLContext::BindBufferRange(GLenum target, GLuint index,
+ WebGLBuffer* buffer, uint64_t offset,
+ uint64_t size) {
+ FuncScope funcScope(*this, "bindBufferBase/Range");
+ if (buffer && !ValidateObject("buffer", *buffer)) return;
+ funcScope.mBindFailureGuard = true;
+
+ RefPtr<WebGLBuffer>* genericBinding;
+ IndexedBufferBinding* indexedBinding;
+ if (!ValidateIndexedBufferBinding(target, index, &genericBinding,
+ &indexedBinding)) {
+ return;
+ }
+
+ if (buffer && !buffer->ValidateCanBindToTarget(target)) return;
+
+ const auto& limits = Limits();
+ auto err =
+ CheckBindBufferRange(target, index, bool(buffer), offset, size, limits);
+ if (err) return;
+
+ ////
+
+ bool needsPrebind = false;
+ needsPrebind |= gl->IsANGLE();
+#ifdef XP_MACOSX
+ needsPrebind = true;
+#endif
+
+ if (gl->WorkAroundDriverBugs() && buffer && needsPrebind) {
+ // BindBufferBase/Range will fail (on some drivers) if the buffer name has
+ // never been bound. (GenBuffers makes a name, but BindBuffer initializes
+ // that name as a real buffer object)
+ gl->fBindBuffer(target, buffer->mGLName);
+ }
+
+ if (size) {
+ gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset,
+ size);
+ } else {
+ gl->fBindBufferBase(target, index, buffer ? buffer->mGLName : 0);
+ }
+
+ if (buffer) {
+ gl->fBindBuffer(target, 0); // Reset generic.
+ }
+
+ ////
+
+ *genericBinding = buffer;
+ indexedBinding->mBufferBinding = buffer;
+ indexedBinding->mRangeStart = offset;
+ indexedBinding->mRangeSize = size;
+
+ if (buffer) {
+ buffer->SetContentAfterBind(target);
+ }
+
+ funcScope.mBindFailureGuard = false;
+}
+
+////////////////////////////////////////
+
+void WebGLContext::BufferData(GLenum target, uint64_t dataLen,
+ const uint8_t* data, GLenum usage) const {
+ // `data` may be null.
+ const FuncScope funcScope(*this, "bufferData");
+ if (IsContextLost()) return;
+
+ const auto& buffer = ValidateBufferSelection(target);
+ if (!buffer) return;
+
+ buffer->BufferData(target, dataLen, data, usage);
+}
+
+////////////////////////////////////////
+
+void WebGLContext::BufferSubData(GLenum target, uint64_t dstByteOffset,
+ uint64_t dataLen, const uint8_t* data,
+ bool unsynchronized) const {
+ MOZ_ASSERT(data || !dataLen);
+ const FuncScope funcScope(*this, "bufferSubData");
+ if (IsContextLost()) return;
+
+ const auto& buffer = ValidateBufferSelection(target);
+ if (!buffer) return;
+ buffer->BufferSubData(target, dstByteOffset, dataLen, data, unsynchronized);
+}
+
+////////////////////////////////////////
+
+RefPtr<WebGLBuffer> WebGLContext::CreateBuffer() {
+ const FuncScope funcScope(*this, "createBuffer");
+ if (IsContextLost()) return nullptr;
+
+ GLuint buf = 0;
+ gl->fGenBuffers(1, &buf);
+
+ return new WebGLBuffer(this, buf);
+}
+
+} // namespace mozilla