diff options
Diffstat (limited to '')
-rw-r--r-- | dom/canvas/WebGLContextBuffers.cpp | 279 |
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 |