зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1300946 - Implement transform feedback. - r=jrmuizel
MozReview-Commit-ID: 1xrEX4Srij1
This commit is contained in:
Родитель
9f5c1ddc1d
Коммит
6b423ece44
|
@ -36,9 +36,6 @@ private:
|
|||
virtual bool ValidateAttribPointerType(bool integerMode, GLenum type,
|
||||
uint32_t* alignment,
|
||||
const char* info) override;
|
||||
virtual bool ValidateBufferTarget(GLenum target, const char* info) override;
|
||||
virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) override;
|
||||
virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) override;
|
||||
virtual bool ValidateQueryTarget(GLenum target, const char* info) override;
|
||||
virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
|
||||
};
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/* -*- 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 "WebGL1Context.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Buffer objects
|
||||
|
||||
/** Target validation for BindBuffer, etc */
|
||||
bool
|
||||
WebGL1Context::ValidateBufferTarget(GLenum target, const char* info)
|
||||
{
|
||||
switch (target) {
|
||||
case LOCAL_GL_ARRAY_BUFFER:
|
||||
case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
|
||||
return true;
|
||||
|
||||
default:
|
||||
ErrorInvalidEnumInfo(info, target);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WebGL1Context::ValidateBufferIndexedTarget(GLenum target, const char* info)
|
||||
{
|
||||
ErrorInvalidEnumInfo(info, target);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGL1Context::ValidateBufferUsageEnum(GLenum usage, const char* info)
|
||||
{
|
||||
switch (usage) {
|
||||
case LOCAL_GL_STREAM_DRAW:
|
||||
case LOCAL_GL_STATIC_DRAW:
|
||||
case LOCAL_GL_DYNAMIC_DRAW:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ErrorInvalidEnumInfo(info, usage);
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -158,8 +158,7 @@ WebGLContext::InitWebGL2(FailureReason* const out_failReason)
|
|||
gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS,
|
||||
&mGLMaxUniformBufferBindings);
|
||||
|
||||
mBoundTransformFeedbackBuffers.SetLength(mGLMaxTransformFeedbackSeparateAttribs);
|
||||
mBoundUniformBuffers.SetLength(mGLMaxUniformBufferBindings);
|
||||
mIndexedUniformBufferBindings.resize(mGLMaxUniformBufferBindings);
|
||||
|
||||
mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0);
|
||||
mBoundTransformFeedback = mDefaultTransformFeedback;
|
||||
|
|
|
@ -411,9 +411,6 @@ private:
|
|||
virtual bool ValidateAttribPointerType(bool integerMode, GLenum type,
|
||||
uint32_t* alignment,
|
||||
const char* info) override;
|
||||
virtual bool ValidateBufferTarget(GLenum target, const char* info) override;
|
||||
virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) override;
|
||||
virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) override;
|
||||
virtual bool ValidateQueryTarget(GLenum target, const char* info) override;
|
||||
virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
|
||||
};
|
||||
|
|
|
@ -11,62 +11,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
bool
|
||||
WebGL2Context::ValidateBufferTarget(GLenum target, const char* funcName)
|
||||
{
|
||||
switch (target) {
|
||||
case LOCAL_GL_ARRAY_BUFFER:
|
||||
case LOCAL_GL_COPY_READ_BUFFER:
|
||||
case LOCAL_GL_COPY_WRITE_BUFFER:
|
||||
case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
|
||||
case LOCAL_GL_PIXEL_PACK_BUFFER:
|
||||
case LOCAL_GL_PIXEL_UNPACK_BUFFER:
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
|
||||
case LOCAL_GL_UNIFORM_BUFFER:
|
||||
return true;
|
||||
|
||||
default:
|
||||
ErrorInvalidEnumInfo(funcName, target);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WebGL2Context::ValidateBufferIndexedTarget(GLenum target, const char* info)
|
||||
{
|
||||
switch (target) {
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
|
||||
case LOCAL_GL_UNIFORM_BUFFER:
|
||||
return true;
|
||||
|
||||
default:
|
||||
ErrorInvalidEnumInfo(info, target);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WebGL2Context::ValidateBufferUsageEnum(GLenum usage, const char* info)
|
||||
{
|
||||
switch (usage) {
|
||||
case LOCAL_GL_DYNAMIC_COPY:
|
||||
case LOCAL_GL_DYNAMIC_DRAW:
|
||||
case LOCAL_GL_DYNAMIC_READ:
|
||||
case LOCAL_GL_STATIC_COPY:
|
||||
case LOCAL_GL_STATIC_DRAW:
|
||||
case LOCAL_GL_STATIC_READ:
|
||||
case LOCAL_GL_STREAM_COPY:
|
||||
case LOCAL_GL_STREAM_DRAW:
|
||||
case LOCAL_GL_STREAM_READ:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ErrorInvalidEnumInfo(info, usage);
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Buffer objects
|
||||
|
||||
|
@ -79,48 +23,58 @@ WebGL2Context::CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateBufferTarget(readTarget, funcName) ||
|
||||
!ValidateBufferTarget(writeTarget, funcName))
|
||||
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 WebGLRefPtr<WebGLBuffer>& readBufferSlot = GetBufferSlotByTarget(readTarget);
|
||||
const WebGLRefPtr<WebGLBuffer>& writeBufferSlot = GetBufferSlotByTarget(writeTarget);
|
||||
if (!readBufferSlot || !writeBufferSlot)
|
||||
return;
|
||||
const auto fnValidateOffsetSize = [&](const char* info, GLintptr offset,
|
||||
const WebGLBuffer* buffer)
|
||||
{
|
||||
const auto neededBytes = CheckedInt<size_t>(offset) + size;
|
||||
if (!neededBytes.isValid() || neededBytes.value() > buffer->ByteLength()) {
|
||||
ErrorInvalidValue("%s: Invalid %s range.", funcName, info);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const WebGLBuffer* readBuffer = readBufferSlot.get();
|
||||
if (!readBuffer) {
|
||||
ErrorInvalidOperation("%s: No buffer bound to readTarget.", funcName);
|
||||
if (!fnValidateOffsetSize("read", readOffset, readBuffer) ||
|
||||
!fnValidateOffsetSize("write", writeOffset, writeBuffer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WebGLBuffer* writeBuffer = writeBufferSlot.get();
|
||||
if (!writeBuffer) {
|
||||
ErrorInvalidOperation("%s: No buffer bound to writeTarget.", funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidateDataOffsetSize(readOffset, size, readBuffer->ByteLength(), funcName))
|
||||
return;
|
||||
|
||||
if (!ValidateDataOffsetSize(writeOffset, size, writeBuffer->ByteLength(), funcName))
|
||||
return;
|
||||
|
||||
if (readTarget == writeTarget &&
|
||||
if (readBuffer == writeBuffer &&
|
||||
!ValidateDataRanges(readOffset, writeOffset, size, funcName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WebGLBuffer::Kind readType = readBuffer->Content();
|
||||
WebGLBuffer::Kind writeType = writeBuffer->Content();
|
||||
|
||||
if (readType != WebGLBuffer::Kind::Undefined &&
|
||||
writeType != WebGLBuffer::Kind::Undefined &&
|
||||
writeType != readType)
|
||||
{
|
||||
const auto& readType = readBuffer->Content();
|
||||
const auto& writeType = writeBuffer->Content();
|
||||
MOZ_ASSERT(readType != WebGLBuffer::Kind::Undefined);
|
||||
MOZ_ASSERT(writeType != WebGLBuffer::Kind::Undefined);
|
||||
if (writeType != readType) {
|
||||
ErrorInvalidOperation("%s: Can't copy %s data to %s data.",
|
||||
funcName,
|
||||
(readType == WebGLBuffer::Kind::OtherData) ? "other"
|
||||
|
@ -130,14 +84,8 @@ WebGL2Context::CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
|
|||
return;
|
||||
}
|
||||
|
||||
WebGLContextUnchecked::CopyBufferSubData(readTarget, writeTarget, readOffset,
|
||||
writeOffset, size);
|
||||
|
||||
if (writeType == WebGLBuffer::Kind::Undefined) {
|
||||
writeBuffer->BindTo(
|
||||
(readType == WebGLBuffer::Kind::OtherData) ? LOCAL_GL_ARRAY_BUFFER
|
||||
: LOCAL_GL_ELEMENT_ARRAY_BUFFER);
|
||||
}
|
||||
gl->MakeCurrent();
|
||||
gl->fCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -148,88 +96,59 @@ WebGL2Context::GetBufferSubData(GLenum target, GLintptr offset,
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
// For the WebGLBuffer bound to the passed target, read
|
||||
// returnedData.byteLength bytes from the buffer starting at byte
|
||||
// offset offset and write them to returnedData.
|
||||
|
||||
// If zero is bound to target, an INVALID_OPERATION error is
|
||||
// generated.
|
||||
if (!ValidateBufferTarget(target, funcName))
|
||||
if (!ValidateNonNegative(funcName, "offset", offset))
|
||||
return;
|
||||
|
||||
// If offset is less than zero, an INVALID_VALUE error is
|
||||
// generated.
|
||||
if (offset < 0) {
|
||||
ErrorInvalidValue("%s: Offset must be non-negative.", funcName);
|
||||
const auto& buffer = ValidateBufferSelection(funcName, target);
|
||||
if (!buffer)
|
||||
return;
|
||||
}
|
||||
|
||||
WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);
|
||||
WebGLBuffer* boundBuffer = bufferSlot.get();
|
||||
if (!boundBuffer) {
|
||||
ErrorInvalidOperation("%s: No buffer bound.", funcName);
|
||||
return;
|
||||
}
|
||||
////
|
||||
|
||||
// If offset + returnedData.byteLength would extend beyond the end
|
||||
// of the buffer an INVALID_VALUE error is generated.
|
||||
data.ComputeLengthAndData();
|
||||
|
||||
CheckedInt<WebGLsizeiptr> neededByteLength = CheckedInt<WebGLsizeiptr>(offset) + data.LengthAllowShared();
|
||||
const auto neededByteLength = CheckedInt<size_t>(offset) + data.LengthAllowShared();
|
||||
if (!neededByteLength.isValid()) {
|
||||
ErrorInvalidValue("%s: Integer overflow computing the needed byte length.",
|
||||
funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (neededByteLength.value() > boundBuffer->ByteLength()) {
|
||||
if (neededByteLength.value() > buffer->ByteLength()) {
|
||||
ErrorInvalidValue("%s: Not enough data. Operation requires %d bytes, but buffer"
|
||||
" only has %d bytes.",
|
||||
funcName, neededByteLength.value(), boundBuffer->ByteLength());
|
||||
funcName, neededByteLength.value(), buffer->ByteLength());
|
||||
return;
|
||||
}
|
||||
|
||||
// If target is TRANSFORM_FEEDBACK_BUFFER, and any transform
|
||||
// feedback object is currently active, an INVALID_OPERATION error
|
||||
// is generated.
|
||||
WebGLTransformFeedback* currentTF = mBoundTransformFeedback;
|
||||
if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER && currentTF) {
|
||||
if (currentTF->mIsActive) {
|
||||
ErrorInvalidOperation("%s: Currently bound transform feedback is active.",
|
||||
funcName);
|
||||
return;
|
||||
}
|
||||
////
|
||||
|
||||
// https://github.com/NVIDIA/WebGL/commit/63aff5e58c1d79825a596f0f4aa46174b9a5f72c
|
||||
// Performing reads and writes on a buffer that is currently
|
||||
// bound for transform feedback causes undefined results in
|
||||
// GLES3.0 and OpenGL 4.5. In practice results of reads and
|
||||
// writes might be consistent as long as transform feedback
|
||||
// objects are not active, but neither GLES3.0 nor OpenGL 4.5
|
||||
// spec guarantees this - just being bound for transform
|
||||
// feedback is sufficient to cause undefined results.
|
||||
|
||||
BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, nullptr);
|
||||
if (buffer->mNumActiveTFOs) {
|
||||
ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
|
||||
" object.",
|
||||
funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the buffer is written and read sequentially by other
|
||||
* operations and getBufferSubData, it is the responsibility of
|
||||
* the WebGL API to ensure that data are access
|
||||
* consistently. This applies even if the buffer is currently
|
||||
* bound to a transform feedback binding point.
|
||||
*/
|
||||
|
||||
void* ptr = gl->fMapBufferRange(target, offset, data.LengthAllowShared(),
|
||||
LOCAL_GL_MAP_READ_BIT);
|
||||
// Warning: Possibly shared memory. See bug 1225033.
|
||||
memcpy(data.DataAllowShared(), ptr, data.LengthAllowShared());
|
||||
gl->fUnmapBuffer(target);
|
||||
if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
|
||||
mBoundTransformFeedback->mIsActive)
|
||||
{
|
||||
ErrorInvalidOperation("%s: Currently bound transform feedback is active.",
|
||||
funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER && currentTF) {
|
||||
BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, currentTF);
|
||||
}
|
||||
gl->MakeCurrent();
|
||||
|
||||
const auto ptr = gl->fMapBufferRange(target, offset, data.LengthAllowShared(),
|
||||
LOCAL_GL_MAP_READ_BIT);
|
||||
// Warning: Possibly shared memory. See bug 1225033.
|
||||
memcpy(data.DataAllowShared(), ptr, data.LengthAllowShared());
|
||||
gl->fUnmapBuffer(target);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -33,14 +33,17 @@ WebGL2Context::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
|
|||
/* GLboolean */
|
||||
case LOCAL_GL_RASTERIZER_DISCARD:
|
||||
case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE:
|
||||
case LOCAL_GL_SAMPLE_COVERAGE:
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_PAUSED:
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_ACTIVE: {
|
||||
case LOCAL_GL_SAMPLE_COVERAGE: {
|
||||
realGLboolean b = 0;
|
||||
gl->fGetBooleanv(pname, &b);
|
||||
return JS::BooleanValue(bool(b));
|
||||
}
|
||||
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_ACTIVE:
|
||||
return JS::BooleanValue(mBoundTransformFeedback->mIsActive);
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_PAUSED:
|
||||
return JS::BooleanValue(mBoundTransformFeedback->mIsPaused);
|
||||
|
||||
/* GLenum */
|
||||
case LOCAL_GL_READ_BUFFER: {
|
||||
if (!mBoundReadFramebuffer)
|
||||
|
@ -149,7 +152,10 @@ WebGL2Context::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
|
|||
return WebGLObjectAsJSValue(cx, mBoundPixelUnpackBuffer.get(), rv);
|
||||
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
|
||||
return WebGLObjectAsJSValue(cx, mBoundTransformFeedbackBuffer.get(), rv);
|
||||
{
|
||||
const auto& tf = mBoundTransformFeedback;
|
||||
return WebGLObjectAsJSValue(cx, tf->mGenericBufferBinding.get(), rv);
|
||||
}
|
||||
|
||||
case LOCAL_GL_UNIFORM_BUFFER_BINDING:
|
||||
return WebGLObjectAsJSValue(cx, mBoundUniformBuffer.get(), rv);
|
||||
|
@ -167,11 +173,14 @@ WebGL2Context::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
|
|||
case LOCAL_GL_TEXTURE_BINDING_3D:
|
||||
return WebGLObjectAsJSValue(cx, mBound3DTextures[mActiveTexture].get(), rv);
|
||||
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BINDING: {
|
||||
WebGLTransformFeedback* tf =
|
||||
(mBoundTransformFeedback != mDefaultTransformFeedback) ? mBoundTransformFeedback.get() : nullptr;
|
||||
return WebGLObjectAsJSValue(cx, tf, rv);
|
||||
}
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BINDING:
|
||||
{
|
||||
const WebGLTransformFeedback* tf = mBoundTransformFeedback;
|
||||
if (tf == mDefaultTransformFeedback) {
|
||||
tf = nullptr;
|
||||
}
|
||||
return WebGLObjectAsJSValue(cx, tf, rv);
|
||||
}
|
||||
|
||||
case LOCAL_GL_VERTEX_ARRAY_BINDING: {
|
||||
WebGLVertexArray* vao =
|
||||
|
|
|
@ -20,28 +20,32 @@ WebGL2Context::CreateTransformFeedback()
|
|||
if (IsContextLost())
|
||||
return nullptr;
|
||||
|
||||
GLuint tf = 0;
|
||||
MakeContextCurrent();
|
||||
GLuint tf = 0;
|
||||
gl->fGenTransformFeedbacks(1, &tf);
|
||||
|
||||
RefPtr<WebGLTransformFeedback> globj = new WebGLTransformFeedback(this, tf);
|
||||
return globj.forget();
|
||||
RefPtr<WebGLTransformFeedback> ret = new WebGLTransformFeedback(this, tf);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WebGL2Context::DeleteTransformFeedback(WebGLTransformFeedback* tf)
|
||||
{
|
||||
const char funcName[] = "deleteTransformFeedback";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateObjectAllowDeletedOrNull("deleteTransformFeedback", tf))
|
||||
if (!ValidateObject(funcName, tf))
|
||||
return;
|
||||
|
||||
if (!tf || tf->IsDeleted())
|
||||
if (tf->mIsActive) {
|
||||
ErrorInvalidOperation("%s: Cannot delete active transform feedbacks.", funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBoundTransformFeedback == tf)
|
||||
if (mBoundTransformFeedback == tf) {
|
||||
BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, nullptr);
|
||||
}
|
||||
|
||||
tf->RequestDelete();
|
||||
}
|
||||
|
@ -65,67 +69,40 @@ WebGL2Context::IsTransformFeedback(WebGLTransformFeedback* tf)
|
|||
void
|
||||
WebGL2Context::BindTransformFeedback(GLenum target, WebGLTransformFeedback* tf)
|
||||
{
|
||||
const char funcName[] = "bindTransformFeedback";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateObjectAllowDeletedOrNull("bindTransformFeedback", tf))
|
||||
if (target != LOCAL_GL_TRANSFORM_FEEDBACK)
|
||||
return ErrorInvalidEnum("%s: `target` must be TRANSFORM_FEEDBACK.", funcName);
|
||||
|
||||
if (!ValidateObjectAllowNull(funcName, tf))
|
||||
return;
|
||||
|
||||
if (target != LOCAL_GL_TRANSFORM_FEEDBACK)
|
||||
return ErrorInvalidEnum("bindTransformFeedback: target must be TRANSFORM_FEEDBACK");
|
||||
|
||||
WebGLRefPtr<WebGLTransformFeedback> currentTF = mBoundTransformFeedback;
|
||||
if (currentTF && currentTF->mIsActive && !currentTF->mIsPaused) {
|
||||
return ErrorInvalidOperation("bindTransformFeedback: Currently bound transform "
|
||||
"feedback is active and not paused");
|
||||
if (mBoundTransformFeedback->mIsActive &&
|
||||
!mBoundTransformFeedback->mIsPaused)
|
||||
{
|
||||
ErrorInvalidOperation("%s: Currently bound transform feedback is active and not"
|
||||
" paused.",
|
||||
funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tf && tf->IsDeleted())
|
||||
return ErrorInvalidOperation("bindTransformFeedback: Attempt to bind deleted id");
|
||||
////
|
||||
|
||||
mBoundTransformFeedback = (tf ? tf : mDefaultTransformFeedback);
|
||||
|
||||
MakeContextCurrent();
|
||||
gl->fBindTransformFeedback(target, tf ? tf->mGLName : 0);
|
||||
if (tf)
|
||||
mBoundTransformFeedback = tf;
|
||||
else
|
||||
mBoundTransformFeedback = mDefaultTransformFeedback;
|
||||
gl->fBindTransformFeedback(target, mBoundTransformFeedback->mGLName);
|
||||
}
|
||||
|
||||
void
|
||||
WebGL2Context::BeginTransformFeedback(GLenum primitiveMode)
|
||||
WebGL2Context::BeginTransformFeedback(GLenum primMode)
|
||||
{
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
WebGLTransformFeedback* tf = mBoundTransformFeedback;
|
||||
MOZ_ASSERT(tf);
|
||||
if (!tf)
|
||||
return;
|
||||
|
||||
if (tf->mIsActive)
|
||||
return ErrorInvalidOperation("beginTransformFeedback: transform feedback is active");
|
||||
|
||||
const GLenum mode = tf->mMode;
|
||||
if (mode != LOCAL_GL_POINTS && mode != LOCAL_GL_LINES && mode != LOCAL_GL_TRIANGLES)
|
||||
return ErrorInvalidEnum("beginTransformFeedback: primitive must be one of POINTS, LINES, or TRIANGLES");
|
||||
|
||||
// TODO:
|
||||
// GL_INVALID_OPERATION is generated by glBeginTransformFeedback
|
||||
// if any binding point used in transform feedback mode does not
|
||||
// have a buffer object bound. In interleaved mode, only the first
|
||||
// buffer object binding point is ever written to.
|
||||
|
||||
// GL_INVALID_OPERATION is generated by glBeginTransformFeedback
|
||||
// if no binding points would be used, either because no program
|
||||
// object is active of because the active program object has
|
||||
// specified no varying variables to record.
|
||||
if (!mCurrentProgram)
|
||||
return ErrorInvalidOperation("beginTransformFeedback: no program is active");
|
||||
|
||||
MakeContextCurrent();
|
||||
gl->fBeginTransformFeedback(primitiveMode);
|
||||
tf->mIsActive = true;
|
||||
tf->mIsPaused = false;
|
||||
mBoundTransformFeedback->BeginTransformFeedback(primMode);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -134,20 +111,7 @@ WebGL2Context::EndTransformFeedback()
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
WebGLTransformFeedback* tf = mBoundTransformFeedback;
|
||||
MOZ_ASSERT(tf);
|
||||
|
||||
if (!tf)
|
||||
return;
|
||||
|
||||
if (!tf->mIsActive)
|
||||
return ErrorInvalidOperation("%s: transform feedback in not active",
|
||||
"endTransformFeedback");
|
||||
|
||||
MakeContextCurrent();
|
||||
gl->fEndTransformFeedback();
|
||||
tf->mIsActive = false;
|
||||
tf->mIsPaused = false;
|
||||
mBoundTransformFeedback->EndTransformFeedback();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -156,19 +120,7 @@ WebGL2Context::PauseTransformFeedback()
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
WebGLTransformFeedback* tf = mBoundTransformFeedback;
|
||||
MOZ_ASSERT(tf);
|
||||
if (!tf)
|
||||
return;
|
||||
|
||||
if (!tf->mIsActive || tf->mIsPaused) {
|
||||
return ErrorInvalidOperation("%s: transform feedback is not active or is paused",
|
||||
"pauseTransformFeedback");
|
||||
}
|
||||
|
||||
MakeContextCurrent();
|
||||
gl->fPauseTransformFeedback();
|
||||
tf->mIsPaused = true;
|
||||
mBoundTransformFeedback->PauseTransformFeedback();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -177,17 +129,7 @@ WebGL2Context::ResumeTransformFeedback()
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
WebGLTransformFeedback* tf = mBoundTransformFeedback;
|
||||
MOZ_ASSERT(tf);
|
||||
if (!tf)
|
||||
return;
|
||||
|
||||
if (!tf->mIsActive || !tf->mIsPaused)
|
||||
return ErrorInvalidOperation("resumeTransformFeedback: transform feedback is not active or is not paused");
|
||||
|
||||
MakeContextCurrent();
|
||||
gl->fResumeTransformFeedback();
|
||||
tf->mIsPaused = false;
|
||||
mBoundTransformFeedback->ResumeTransformFeedback();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -70,57 +70,60 @@ WebGL2Context::Uniform4ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1, GLuin
|
|||
|
||||
// -------------------------------------------------------------------------
|
||||
// Uniform Buffer Objects and Transform Feedback Buffers
|
||||
// TODO(djg): Implemented in WebGLContext
|
||||
/*
|
||||
void BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer);
|
||||
void BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
|
||||
GLintptr offset, GLsizeiptr size);
|
||||
*/
|
||||
|
||||
/* This doesn't belong here. It's part of state querying */
|
||||
void
|
||||
WebGL2Context::GetIndexedParameter(GLenum target, GLuint index,
|
||||
dom::Nullable<dom::OwningWebGLBufferOrLongLong>& retval)
|
||||
{
|
||||
const char funcName[] = "getIndexedParameter";
|
||||
retval.SetNull();
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
GLint64 data = 0;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
const std::vector<IndexedBufferBinding>* bindings;
|
||||
switch (target) {
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
|
||||
if (index >= mGLMaxTransformFeedbackSeparateAttribs)
|
||||
return ErrorInvalidValue("getIndexedParameter: index should be less than "
|
||||
"MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
|
||||
|
||||
if (mBoundTransformFeedbackBuffers[index].get()) {
|
||||
retval.SetValue().SetAsWebGLBuffer() =
|
||||
mBoundTransformFeedbackBuffers[index].get();
|
||||
}
|
||||
return;
|
||||
|
||||
case LOCAL_GL_UNIFORM_BUFFER_BINDING:
|
||||
if (index >= mGLMaxUniformBufferBindings)
|
||||
return ErrorInvalidValue("getIndexedParameter: index should be than "
|
||||
"MAX_UNIFORM_BUFFER_BINDINGS");
|
||||
|
||||
if (mBoundUniformBuffers[index].get())
|
||||
retval.SetValue().SetAsWebGLBuffer() = mBoundUniformBuffers[index].get();
|
||||
return;
|
||||
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_START:
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
|
||||
bindings = &(mBoundTransformFeedback->mIndexedBindings);
|
||||
break;
|
||||
|
||||
case LOCAL_GL_UNIFORM_BUFFER_BINDING:
|
||||
case LOCAL_GL_UNIFORM_BUFFER_START:
|
||||
case LOCAL_GL_UNIFORM_BUFFER_SIZE:
|
||||
gl->fGetInteger64i_v(target, index, &data);
|
||||
retval.SetValue().SetAsLongLong() = data;
|
||||
bindings = &mIndexedUniformBufferBindings;
|
||||
break;
|
||||
|
||||
default:
|
||||
ErrorInvalidEnumInfo("getIndexedParameter: target", target);
|
||||
return;
|
||||
}
|
||||
|
||||
ErrorInvalidEnumInfo("getIndexedParameter: target", target);
|
||||
if (index >= bindings->size()) {
|
||||
ErrorInvalidValue("%s: `index` must be < %s.", funcName,
|
||||
"MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
|
||||
return;
|
||||
}
|
||||
const auto& binding = (*bindings)[index];
|
||||
|
||||
switch (target) {
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
|
||||
case LOCAL_GL_UNIFORM_BUFFER_BINDING:
|
||||
if (binding.mBufferBinding) {
|
||||
retval.SetValue().SetAsWebGLBuffer() = binding.mBufferBinding;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_START:
|
||||
case LOCAL_GL_UNIFORM_BUFFER_START:
|
||||
retval.SetValue().SetAsLongLong() = binding.mRangeStart;
|
||||
break;
|
||||
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
|
||||
case LOCAL_GL_UNIFORM_BUFFER_SIZE:
|
||||
retval.SetValue().SetAsLongLong() = binding.mRangeSize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -17,23 +17,29 @@ WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
|
|||
, mGLName(buf)
|
||||
, mContent(Kind::Undefined)
|
||||
, mByteLength(0)
|
||||
, mNumActiveTFOs(0)
|
||||
{
|
||||
mContext->mBuffers.insertBack(this);
|
||||
}
|
||||
|
||||
WebGLBuffer::~WebGLBuffer()
|
||||
{
|
||||
MOZ_ASSERT(!mNumActiveTFOs);
|
||||
DeleteOnce();
|
||||
}
|
||||
|
||||
void
|
||||
WebGLBuffer::BindTo(GLenum target)
|
||||
WebGLBuffer::SetContentAfterBind(GLenum target)
|
||||
{
|
||||
if (mContent != Kind::Undefined)
|
||||
return;
|
||||
|
||||
switch (target) {
|
||||
case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
|
||||
mContent = Kind::ElementArray;
|
||||
if (!mCache)
|
||||
mCache = new WebGLElementArrayCache;
|
||||
if (!mCache) {
|
||||
mCache.reset(new WebGLElementArrayCache);
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCAL_GL_ARRAY_BUFFER:
|
||||
|
@ -41,14 +47,9 @@ WebGLBuffer::BindTo(GLenum target)
|
|||
case LOCAL_GL_PIXEL_UNPACK_BUFFER:
|
||||
case LOCAL_GL_UNIFORM_BUFFER:
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
|
||||
mContent = Kind::OtherData;
|
||||
break;
|
||||
|
||||
case LOCAL_GL_COPY_READ_BUFFER:
|
||||
case LOCAL_GL_COPY_WRITE_BUFFER:
|
||||
if (mContent == Kind::Undefined) {
|
||||
mContent = Kind::OtherData;
|
||||
}
|
||||
mContent = Kind::OtherData;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -66,6 +67,90 @@ WebGLBuffer::Delete()
|
|||
LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
static bool
|
||||
ValidateBufferUsageEnum(WebGLContext* webgl, const char* funcName, GLenum usage)
|
||||
{
|
||||
switch (usage) {
|
||||
case LOCAL_GL_STREAM_DRAW:
|
||||
case LOCAL_GL_STATIC_DRAW:
|
||||
case LOCAL_GL_DYNAMIC_DRAW:
|
||||
return true;
|
||||
|
||||
case LOCAL_GL_DYNAMIC_COPY:
|
||||
case LOCAL_GL_DYNAMIC_READ:
|
||||
case LOCAL_GL_STATIC_COPY:
|
||||
case LOCAL_GL_STATIC_READ:
|
||||
case LOCAL_GL_STREAM_COPY:
|
||||
case LOCAL_GL_STREAM_READ:
|
||||
if (MOZ_LIKELY(webgl->IsWebGL2()))
|
||||
return true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
webgl->ErrorInvalidEnum("%s: Invalid `usage`: 0x%04x", funcName, usage);
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usage)
|
||||
{
|
||||
const char funcName[] = "bufferData";
|
||||
|
||||
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();
|
||||
mContext->InvalidateBufferFetching();
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// bug 790879
|
||||
if (gl->WorkAroundDriverBugs() &&
|
||||
size > INT32_MAX)
|
||||
{
|
||||
mContext->ErrorOutOfMemory("%s: Allocation size too large.", funcName);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
const bool sizeChanges = (size != ByteLength());
|
||||
if (sizeChanges) {
|
||||
gl::GLContext::LocalErrorScope errorScope(*gl);
|
||||
gl->fBufferData(target, size, data, 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;
|
||||
}
|
||||
} else {
|
||||
gl->fBufferData(target, size, data, usage);
|
||||
}
|
||||
|
||||
mByteLength = size;
|
||||
|
||||
// Warning: Possibly shared memory. See bug 1225033.
|
||||
if (!ElementArrayCacheBufferData(data, size)) {
|
||||
mByteLength = 0;
|
||||
mContext->ErrorOutOfMemory("%s: Failed update index buffer cache.", funcName);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
bool
|
||||
WebGLBuffer::ElementArrayCacheBufferData(const void* ptr,
|
||||
size_t bufferSizeInBytes)
|
||||
|
@ -93,10 +178,9 @@ WebGLBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
|||
}
|
||||
|
||||
bool
|
||||
WebGLBuffer::Validate(GLenum type, uint32_t maxAllowed, size_t first,
|
||||
size_t count, uint32_t* const out_upperBound)
|
||||
WebGLBuffer::Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count) const
|
||||
{
|
||||
return mCache->Validate(type, maxAllowed, first, count, out_upperBound);
|
||||
return mCache->Validate(type, maxAllowed, first, count);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include "GLDefs.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
#include "WebGLObjectModel.h"
|
||||
|
@ -24,8 +24,12 @@ class WebGLBuffer final
|
|||
, public LinkedListElement<WebGLBuffer>
|
||||
, public WebGLContextBoundObject
|
||||
{
|
||||
public:
|
||||
friend class WebGLContext;
|
||||
friend class WebGL2Context;
|
||||
friend class WebGLTexture;
|
||||
friend class WebGLTransformFeedback;
|
||||
|
||||
public:
|
||||
enum class Kind {
|
||||
Undefined,
|
||||
ElementArray,
|
||||
|
@ -34,23 +38,21 @@ public:
|
|||
|
||||
WebGLBuffer(WebGLContext* webgl, GLuint buf);
|
||||
|
||||
void BindTo(GLenum target);
|
||||
void SetContentAfterBind(GLenum target);
|
||||
Kind Content() const { return mContent; }
|
||||
|
||||
void Delete();
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
WebGLsizeiptr ByteLength() const { return mByteLength; }
|
||||
void SetByteLength(WebGLsizeiptr byteLength) { mByteLength = byteLength; }
|
||||
size_t ByteLength() const { return mByteLength; }
|
||||
|
||||
bool ElementArrayCacheBufferData(const void* ptr, size_t bufferSizeInBytes);
|
||||
|
||||
void ElementArrayCacheBufferSubData(size_t pos, const void* ptr,
|
||||
size_t updateSizeInBytes);
|
||||
|
||||
bool Validate(GLenum type, uint32_t max_allowed, size_t first, size_t count,
|
||||
uint32_t* const out_upperBound);
|
||||
bool Validate(GLenum type, uint32_t max_allowed, size_t first, size_t count) const;
|
||||
|
||||
bool IsElementArrayUsedWithMultipleTypes() const;
|
||||
|
||||
|
@ -60,6 +62,8 @@ public:
|
|||
|
||||
virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
|
||||
|
||||
void BufferData(GLenum target, size_t size, const void* data, GLenum usage);
|
||||
|
||||
const GLenum mGLName;
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBuffer)
|
||||
|
@ -69,8 +73,9 @@ protected:
|
|||
~WebGLBuffer();
|
||||
|
||||
Kind mContent;
|
||||
WebGLsizeiptr mByteLength;
|
||||
nsAutoPtr<WebGLElementArrayCache> mCache;
|
||||
size_t mByteLength;
|
||||
UniquePtr<WebGLElementArrayCache> mCache;
|
||||
size_t mNumActiveTFOs;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -247,7 +247,6 @@ WebGLContext::DestroyResourcesAndContext()
|
|||
mBoundCopyWriteBuffer = nullptr;
|
||||
mBoundPixelPackBuffer = nullptr;
|
||||
mBoundPixelUnpackBuffer = nullptr;
|
||||
mBoundTransformFeedbackBuffer = nullptr;
|
||||
mBoundUniformBuffer = nullptr;
|
||||
mCurrentProgram = nullptr;
|
||||
mActiveProgramLinkInfo = nullptr;
|
||||
|
@ -260,8 +259,7 @@ WebGLContext::DestroyResourcesAndContext()
|
|||
mBoundTransformFeedback = nullptr;
|
||||
mDefaultTransformFeedback = nullptr;
|
||||
|
||||
mBoundTransformFeedbackBuffers.Clear();
|
||||
mBoundUniformBuffers.Clear();
|
||||
mIndexedUniformBufferBindings.clear();
|
||||
|
||||
//////
|
||||
|
||||
|
@ -2081,6 +2079,30 @@ WebGLContext::ScopedMaskWorkaround::HasDepthButNoStencil(const WebGLFramebuffer*
|
|||
|
||||
////////////////////////////////////////
|
||||
|
||||
IndexedBufferBinding::IndexedBufferBinding()
|
||||
: mRangeStart(0)
|
||||
, mRangeSize(0)
|
||||
{ }
|
||||
|
||||
uint64_t
|
||||
IndexedBufferBinding::ByteCount() const
|
||||
{
|
||||
if (!mBufferBinding)
|
||||
return 0;
|
||||
|
||||
uint64_t bufferSize = mBufferBinding->ByteLength();
|
||||
if (!mRangeSize) // BindBufferBase
|
||||
return bufferSize;
|
||||
|
||||
if (mRangeStart >= bufferSize)
|
||||
return 0;
|
||||
bufferSize -= mRangeStart;
|
||||
|
||||
return std::min(bufferSize, mRangeSize);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
ScopedUnpackReset::ScopedUnpackReset(WebGLContext* webgl)
|
||||
: ScopedGLWrapper<ScopedUnpackReset>(webgl->gl)
|
||||
, mWebGL(webgl)
|
||||
|
@ -2456,6 +2478,24 @@ WebGLContext::StartVRPresentation()
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImplCycleCollectionUnlink(std::vector<IndexedBufferBinding>& field)
|
||||
{
|
||||
field.clear();
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
|
||||
|
||||
|
@ -2473,7 +2513,7 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
|
|||
mBoundCopyWriteBuffer,
|
||||
mBoundPixelPackBuffer,
|
||||
mBoundPixelUnpackBuffer,
|
||||
mBoundTransformFeedbackBuffer,
|
||||
mBoundTransformFeedback,
|
||||
mBoundUniformBuffer,
|
||||
mCurrentProgram,
|
||||
mBoundDrawFramebuffer,
|
||||
|
|
|
@ -124,6 +124,7 @@ struct LinkedProgramInfo;
|
|||
class ShaderValidator;
|
||||
class TexUnpackBlob;
|
||||
struct UniformInfo;
|
||||
struct UniformBlockInfo;
|
||||
} // namespace webgl
|
||||
|
||||
WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
|
||||
|
@ -181,6 +182,19 @@ public:
|
|||
GLfloat AsFloat() const { return (mType == Float) ? mValue.f : GLfloat(mValue.i); }
|
||||
};
|
||||
|
||||
struct IndexedBufferBinding
|
||||
{
|
||||
WebGLRefPtr<WebGLBuffer> mBufferBinding;
|
||||
uint64_t mRangeStart;
|
||||
uint64_t mRangeSize;
|
||||
|
||||
IndexedBufferBinding();
|
||||
|
||||
uint64_t ByteCount() const;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class WebGLContext
|
||||
: public nsIDOMWebGLRenderingContext
|
||||
, public nsICanvasRenderingContextInternal
|
||||
|
@ -189,6 +203,8 @@ class WebGLContext
|
|||
, public WebGLRectangleObject
|
||||
, public nsWrapperCache
|
||||
{
|
||||
friend class ScopedDrawHelper;
|
||||
friend class ScopedDrawWithTransformFeedback;
|
||||
friend class ScopedFBRebinder;
|
||||
friend class WebGL2Context;
|
||||
friend class WebGLContextUserData;
|
||||
|
@ -203,6 +219,7 @@ class WebGLContext
|
|||
friend class WebGLExtensionLoseContext;
|
||||
friend class WebGLExtensionVertexArray;
|
||||
friend class WebGLMemoryTracker;
|
||||
friend struct webgl::UniformBlockInfo;
|
||||
|
||||
enum {
|
||||
UNPACK_FLIP_Y_WEBGL = 0x9240,
|
||||
|
@ -726,11 +743,6 @@ public:
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Buffer Objects (WebGLContextBuffers.cpp)
|
||||
private:
|
||||
void UpdateBoundBuffer(GLenum target, WebGLBuffer* buffer);
|
||||
void UpdateBoundBufferIndexed(GLenum target, GLuint index, WebGLBuffer* buffer);
|
||||
|
||||
public:
|
||||
void BindBuffer(GLenum target, WebGLBuffer* buffer);
|
||||
void BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buf);
|
||||
void BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buf,
|
||||
|
@ -772,11 +784,9 @@ protected:
|
|||
WebGLRefPtr<WebGLBuffer> mBoundCopyWriteBuffer;
|
||||
WebGLRefPtr<WebGLBuffer> mBoundPixelPackBuffer;
|
||||
WebGLRefPtr<WebGLBuffer> mBoundPixelUnpackBuffer;
|
||||
WebGLRefPtr<WebGLBuffer> mBoundTransformFeedbackBuffer;
|
||||
WebGLRefPtr<WebGLBuffer> mBoundUniformBuffer;
|
||||
|
||||
nsTArray<WebGLRefPtr<WebGLBuffer>> mBoundUniformBuffers;
|
||||
nsTArray<WebGLRefPtr<WebGLBuffer>> mBoundTransformFeedbackBuffers;
|
||||
std::vector<IndexedBufferBinding> mIndexedUniformBufferBindings;
|
||||
|
||||
WebGLRefPtr<WebGLBuffer>& GetBufferSlotByTarget(GLenum target);
|
||||
WebGLRefPtr<WebGLBuffer>& GetBufferSlotByTargetIndexed(GLenum target,
|
||||
|
@ -1044,11 +1054,11 @@ private:
|
|||
uint32_t mMaxFetchedInstances;
|
||||
bool mBufferFetch_IsAttrib0Active;
|
||||
|
||||
bool DrawArrays_check(GLint first, GLsizei count, GLsizei primcount,
|
||||
const char* info);
|
||||
bool DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOffset,
|
||||
GLsizei primcount, const char* info,
|
||||
GLuint* out_upperBound);
|
||||
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);
|
||||
void Draw_cleanup(const char* funcName);
|
||||
|
||||
|
@ -1253,7 +1263,6 @@ protected:
|
|||
bool ValidateBlendFuncSrcEnum(GLenum mode, const char* info);
|
||||
bool ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor,
|
||||
const char* info);
|
||||
bool ValidateDataOffsetSize(WebGLintptr offset, WebGLsizeiptr size, WebGLsizeiptr bufferSize, const char* info);
|
||||
bool ValidateDataRanges(WebGLintptr readOffset, WebGLintptr writeOffset, WebGLsizeiptr size, const char* info);
|
||||
bool ValidateTextureTargetEnum(GLenum target, const char* info);
|
||||
bool ValidateComparisonEnum(GLenum target, const char* info);
|
||||
|
@ -1325,6 +1334,23 @@ protected:
|
|||
IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers);
|
||||
}
|
||||
|
||||
WebGLRefPtr<WebGLBuffer>* ValidateBufferSlot(const char* funcName, GLenum target);
|
||||
WebGLBuffer* ValidateBufferSelection(const char* funcName, GLenum target);
|
||||
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;
|
||||
}
|
||||
|
||||
void Invalidate();
|
||||
void DestroyResourcesAndContext();
|
||||
|
||||
|
@ -1370,20 +1396,9 @@ private:
|
|||
virtual WebGLVertexArray* CreateVertexArrayImpl();
|
||||
|
||||
virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, uint32_t* alignment, const char* info) = 0;
|
||||
virtual bool ValidateBufferTarget(GLenum target, const char* info) = 0;
|
||||
virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) = 0;
|
||||
virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info);
|
||||
virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) = 0;
|
||||
virtual bool ValidateQueryTarget(GLenum usage, const char* info) = 0;
|
||||
virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) = 0;
|
||||
|
||||
protected:
|
||||
/** Like glBufferData, but if the call may change the buffer size, checks
|
||||
* any GL error generated by this glBufferData call and returns it.
|
||||
*/
|
||||
GLenum CheckedBufferData(GLenum target, GLsizeiptr size, const GLvoid* data,
|
||||
GLenum usage);
|
||||
|
||||
public:
|
||||
void ForceLoseContext(bool simulateLoss = false);
|
||||
|
||||
|
@ -1835,6 +1850,16 @@ ZeroTextureData(WebGLContext* webgl, const char* funcName, GLuint tex,
|
|||
const webgl::FormatUsageInfo* usage, uint32_t xOffset, uint32_t yOffset,
|
||||
uint32_t zOffset, uint32_t width, uint32_t height, uint32_t depth);
|
||||
|
||||
////
|
||||
|
||||
void
|
||||
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
|
||||
const std::vector<IndexedBufferBinding>& field,
|
||||
const char* name, uint32_t flags = 0);
|
||||
|
||||
void
|
||||
ImplCycleCollectionUnlink(std::vector<IndexedBufferBinding>& field);
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,177 +11,368 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
WebGLContext::UpdateBoundBuffer(GLenum target, WebGLBuffer* buffer)
|
||||
WebGLRefPtr<WebGLBuffer>*
|
||||
WebGLContext::ValidateBufferSlot(const char* funcName, GLenum target)
|
||||
{
|
||||
WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);
|
||||
bufferSlot = buffer;
|
||||
WebGLRefPtr<WebGLBuffer>* slot = nullptr;
|
||||
|
||||
if (!buffer)
|
||||
return;
|
||||
switch (target) {
|
||||
case LOCAL_GL_ARRAY_BUFFER:
|
||||
slot = &mBoundArrayBuffer;
|
||||
break;
|
||||
|
||||
buffer->BindTo(target);
|
||||
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 = &(mBoundTransformFeedback->mGenericBufferBinding);
|
||||
break;
|
||||
|
||||
case LOCAL_GL_UNIFORM_BUFFER:
|
||||
slot = &mBoundUniformBuffer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!slot) {
|
||||
ErrorInvalidEnum("%s: Bad `target`: 0x%04x", funcName, target);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::UpdateBoundBufferIndexed(GLenum target, GLuint index, WebGLBuffer* buffer)
|
||||
WebGLBuffer*
|
||||
WebGLContext::ValidateBufferSelection(const char* funcName, GLenum target)
|
||||
{
|
||||
UpdateBoundBuffer(target, buffer);
|
||||
const auto& slot = ValidateBufferSlot(funcName, target);
|
||||
if (!slot)
|
||||
return nullptr;
|
||||
const auto& buffer = *slot;
|
||||
|
||||
WebGLRefPtr<WebGLBuffer>& bufferIndexSlot =
|
||||
GetBufferSlotByTargetIndexed(target, index);
|
||||
bufferIndexSlot = buffer;
|
||||
if (!buffer) {
|
||||
ErrorInvalidOperation("%s: Buffer for `target` is null.", funcName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return buffer.get();
|
||||
}
|
||||
|
||||
IndexedBufferBinding*
|
||||
WebGLContext::ValidateIndexedBufferSlot(const char* funcName, GLenum target, GLuint index)
|
||||
{
|
||||
decltype(mIndexedUniformBufferBindings)* bindings;
|
||||
const char* maxIndexEnum;
|
||||
switch (target) {
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
|
||||
bindings = &(mBoundTransformFeedback->mIndexedBindings);
|
||||
maxIndexEnum = "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS";
|
||||
break;
|
||||
|
||||
case LOCAL_GL_UNIFORM_BUFFER:
|
||||
bindings = &mIndexedUniformBufferBindings;
|
||||
maxIndexEnum = "MAX_UNIFORM_BUFFER_BINDINGS";
|
||||
break;
|
||||
|
||||
default:
|
||||
ErrorInvalidEnum("%s: Bad `target`: 0x%04x", funcName, target);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (index >= bindings->size()) {
|
||||
ErrorInvalidOperation("%s: `index` >= %s.", funcName, maxIndexEnum);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &(*bindings)[index];
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
static bool
|
||||
ValidateCanBindToTarget(WebGLContext* webgl, const char* funcName, GLenum target,
|
||||
WebGLBuffer* buffer)
|
||||
{
|
||||
if (!buffer)
|
||||
return true;
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* Any call to one of these functions which attempts to bind a
|
||||
* WebGLBuffer that has the element array WebGL buffer type to a
|
||||
* binding point that falls under other data, or bind a
|
||||
* WebGLBuffer which has the other data WebGL buffer type to
|
||||
* ELEMENT_ARRAY_BUFFER will generate an INVALID_OPERATION error,
|
||||
* and the state of the binding point will remain untouched.
|
||||
*/
|
||||
|
||||
const auto& content = buffer->Content();
|
||||
if (content == WebGLBuffer::Kind::Undefined)
|
||||
return true;
|
||||
|
||||
switch (target) {
|
||||
case LOCAL_GL_COPY_READ_BUFFER:
|
||||
case LOCAL_GL_COPY_WRITE_BUFFER:
|
||||
return true;
|
||||
|
||||
case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
|
||||
if (content == WebGLBuffer::Kind::ElementArray)
|
||||
return true;
|
||||
break;
|
||||
|
||||
case LOCAL_GL_ARRAY_BUFFER:
|
||||
case LOCAL_GL_PIXEL_PACK_BUFFER:
|
||||
case LOCAL_GL_PIXEL_UNPACK_BUFFER:
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
|
||||
case LOCAL_GL_UNIFORM_BUFFER:
|
||||
if (content == WebGLBuffer::Kind::OtherData)
|
||||
return true;
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
webgl->ErrorInvalidOperation("%s: buffer already contains %s data.", funcName,
|
||||
content == WebGLBuffer::Kind::OtherData ? "other"
|
||||
: "element");
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::BindBuffer(GLenum target, WebGLBuffer* buffer)
|
||||
{
|
||||
const char funcName[] = "bindBuffer";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateObjectAllowDeletedOrNull("bindBuffer", buffer))
|
||||
if (!ValidateObjectAllowDeletedOrNull(funcName, buffer))
|
||||
return;
|
||||
|
||||
// silently ignore a deleted buffer
|
||||
if (buffer && buffer->IsDeleted())
|
||||
return;
|
||||
|
||||
if (!ValidateBufferTarget(target, "bindBuffer"))
|
||||
const auto& slot = ValidateBufferSlot(funcName, target);
|
||||
if (!slot)
|
||||
return;
|
||||
|
||||
if (!ValidateBufferForTarget(target, buffer, "bindBuffer"))
|
||||
if (!ValidateCanBindToTarget(this, funcName, target, buffer))
|
||||
return;
|
||||
|
||||
WebGLContextUnchecked::BindBuffer(target, buffer);
|
||||
gl->MakeCurrent();
|
||||
gl->fBindBuffer(target, buffer ? buffer->mGLName : 0);
|
||||
|
||||
UpdateBoundBuffer(target, buffer);
|
||||
*slot = buffer;
|
||||
if (buffer) {
|
||||
buffer->SetContentAfterBind(target);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
bool
|
||||
WebGLContext::ValidateIndexedBufferBinding(const char* funcName, GLenum target,
|
||||
GLuint index,
|
||||
WebGLRefPtr<WebGLBuffer>** const out_genericBinding,
|
||||
IndexedBufferBinding** const out_indexedBinding)
|
||||
{
|
||||
*out_genericBinding = ValidateBufferSlot(funcName, target);
|
||||
if (!*out_genericBinding)
|
||||
return false;
|
||||
|
||||
*out_indexedBinding = ValidateIndexedBufferSlot(funcName, target, index);
|
||||
if (!*out_indexedBinding)
|
||||
return false;
|
||||
|
||||
if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
|
||||
mBoundTransformFeedback->mIsActive)
|
||||
{
|
||||
ErrorInvalidOperation("%s: Cannot update indexed buffer bindings on active"
|
||||
" transform feedback objects.",
|
||||
funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ValidateCanBindToTarget(this, funcName, target, (*out_genericBinding)->get()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
|
||||
{
|
||||
const char funcName[] = "bindBufferBase";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateObjectAllowDeletedOrNull("bindBufferBase", buffer))
|
||||
if (!ValidateObjectAllowDeletedOrNull(funcName, buffer))
|
||||
return;
|
||||
|
||||
// silently ignore a deleted buffer
|
||||
if (buffer && buffer->IsDeleted())
|
||||
return;
|
||||
|
||||
// ValidateBufferTarget
|
||||
switch (target) {
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
|
||||
if (index >= mGLMaxTransformFeedbackSeparateAttribs)
|
||||
return ErrorInvalidValue("bindBufferBase: index should be less than "
|
||||
"MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
|
||||
break;
|
||||
|
||||
case LOCAL_GL_UNIFORM_BUFFER:
|
||||
if (index >= mGLMaxUniformBufferBindings)
|
||||
return ErrorInvalidValue("bindBufferBase: index should be less than "
|
||||
"MAX_UNIFORM_BUFFER_BINDINGS");
|
||||
break;
|
||||
|
||||
default:
|
||||
return ErrorInvalidEnumInfo("bindBufferBase: target", target);
|
||||
WebGLRefPtr<WebGLBuffer>* genericBinding;
|
||||
IndexedBufferBinding* indexedBinding;
|
||||
if (!ValidateIndexedBufferBinding(funcName, target, index, &genericBinding,
|
||||
&indexedBinding))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidateBufferForTarget(target, buffer, "bindBufferBase"))
|
||||
return;
|
||||
////
|
||||
|
||||
WebGLContextUnchecked::BindBufferBase(target, index, buffer);
|
||||
gl->MakeCurrent();
|
||||
gl->fBindBufferBase(target, index, buffer ? buffer->mGLName : 0);
|
||||
|
||||
UpdateBoundBufferIndexed(target, index, buffer);
|
||||
////
|
||||
|
||||
*genericBinding = buffer;
|
||||
indexedBinding->mBufferBinding = buffer;
|
||||
indexedBinding->mRangeStart = 0;
|
||||
indexedBinding->mRangeSize = 0;
|
||||
|
||||
if (buffer) {
|
||||
buffer->SetContentAfterBind(target);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
|
||||
WebGLintptr offset, WebGLsizeiptr size)
|
||||
{
|
||||
const char funcName[] = "bindBufferRange";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateObjectAllowDeletedOrNull("bindBufferRange", buffer))
|
||||
if (!ValidateObjectAllowDeletedOrNull(funcName, buffer))
|
||||
return;
|
||||
|
||||
// silently ignore a deleted buffer
|
||||
if (buffer && buffer->IsDeleted())
|
||||
return;
|
||||
|
||||
// ValidateBufferTarget
|
||||
if (!ValidateNonNegative(funcName, "offset", offset) ||
|
||||
!ValidateNonNegative(funcName, "size", size))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WebGLRefPtr<WebGLBuffer>* genericBinding;
|
||||
IndexedBufferBinding* indexedBinding;
|
||||
if (!ValidateIndexedBufferBinding(funcName, target, index, &genericBinding,
|
||||
&indexedBinding))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gl->MakeCurrent();
|
||||
|
||||
switch (target) {
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
|
||||
if (index >= mGLMaxTransformFeedbackSeparateAttribs)
|
||||
return ErrorInvalidValue("bindBufferRange: index should be less than "
|
||||
"MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
|
||||
if (offset % 4 != 0 || size % 4 != 0) {
|
||||
ErrorInvalidValue("%s: For %s, `offset` and `size` must be multiples of 4.",
|
||||
funcName, "TRANSFORM_FEEDBACK_BUFFER");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCAL_GL_UNIFORM_BUFFER:
|
||||
if (index >= mGLMaxUniformBufferBindings)
|
||||
return ErrorInvalidValue("bindBufferRange: index should be less than "
|
||||
"MAX_UNIFORM_BUFFER_BINDINGS");
|
||||
{
|
||||
GLuint offsetAlignment = 0;
|
||||
gl->GetUIntegerv(LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &offsetAlignment);
|
||||
if (offset % offsetAlignment != 0) {
|
||||
ErrorInvalidValue("%s: For %s, `offset` must be a multiple of %s.",
|
||||
funcName, "UNIFORM_BUFFER",
|
||||
"UNIFORM_BUFFER_OFFSET_ALIGNMENT");
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return ErrorInvalidEnumInfo("bindBufferRange: target", target);
|
||||
}
|
||||
|
||||
if (!ValidateBufferForTarget(target, buffer, "bindBufferRange"))
|
||||
return;
|
||||
////
|
||||
|
||||
WebGLContextUnchecked::BindBufferRange(target, index, buffer, offset, size);
|
||||
#ifdef XP_MACOSX
|
||||
if (buffer && buffer->Content() == WebGLBuffer::Kind::Undefined &&
|
||||
gl->WorkAroundDriverBugs())
|
||||
{
|
||||
// BindBufferRange will fail if the buffer's contents is undefined.
|
||||
// Bind so driver initializes the buffer.
|
||||
gl->fBindBuffer(target, buffer->mGLName);
|
||||
}
|
||||
#endif
|
||||
|
||||
UpdateBoundBufferIndexed(target, index, buffer);
|
||||
gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset, size);
|
||||
|
||||
////
|
||||
|
||||
*genericBinding = buffer;
|
||||
indexedBinding->mBufferBinding = buffer;
|
||||
indexedBinding->mRangeStart = offset;
|
||||
indexedBinding->mRangeSize = size;
|
||||
|
||||
if (buffer) {
|
||||
buffer->SetContentAfterBind(target);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
void
|
||||
WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage)
|
||||
{
|
||||
const char funcName[] = "bufferData";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateBufferTarget(target, "bufferData"))
|
||||
return;
|
||||
|
||||
WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);
|
||||
|
||||
if (size < 0)
|
||||
return ErrorInvalidValue("bufferData: negative size");
|
||||
|
||||
if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
|
||||
if (!ValidateNonNegative(funcName, "size", size))
|
||||
return;
|
||||
|
||||
// careful: WebGLsizeiptr is always 64-bit, but GLsizeiptr is like intptr_t.
|
||||
if (!CheckedInt<GLsizeiptr>(size).isValid())
|
||||
return ErrorOutOfMemory("bufferData: bad size");
|
||||
return ErrorOutOfMemory("%s: bad size", funcName);
|
||||
|
||||
WebGLBuffer* boundBuffer = bufferSlot.get();
|
||||
const auto& buffer = ValidateBufferSelection(funcName, target);
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
if (!boundBuffer)
|
||||
return ErrorInvalidOperation("bufferData: no buffer bound!");
|
||||
////
|
||||
|
||||
UniquePtr<uint8_t> zeroBuffer((uint8_t*)calloc(size, 1));
|
||||
if (!zeroBuffer)
|
||||
return ErrorOutOfMemory("bufferData: out of memory");
|
||||
return ErrorOutOfMemory("%s: Failed to allocate zeros.", funcName);
|
||||
|
||||
MakeContextCurrent();
|
||||
InvalidateBufferFetching();
|
||||
|
||||
GLenum error = CheckedBufferData(target, size, zeroBuffer.get(), usage);
|
||||
|
||||
if (error) {
|
||||
GenerateWarning("bufferData generated error %s", ErrorName(error));
|
||||
return;
|
||||
}
|
||||
|
||||
boundBuffer->SetByteLength(size);
|
||||
|
||||
if (!boundBuffer->ElementArrayCacheBufferData(nullptr, size)) {
|
||||
boundBuffer->SetByteLength(0);
|
||||
return ErrorOutOfMemory("bufferData: out of memory");
|
||||
}
|
||||
buffer->BufferData(target, size_t(size), zeroBuffer.get(), usage);
|
||||
}
|
||||
|
||||
// BufferT may be one of
|
||||
|
@ -194,14 +385,10 @@ WebGLContext::BufferDataT(GLenum target,
|
|||
const BufferT& data,
|
||||
GLenum usage)
|
||||
{
|
||||
const char funcName[] = "bufferData";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateBufferTarget(target, "bufferData"))
|
||||
return;
|
||||
|
||||
const WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);
|
||||
|
||||
data.ComputeLengthAndData();
|
||||
|
||||
// Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
|
||||
|
@ -209,32 +396,12 @@ WebGLContext::BufferDataT(GLenum target,
|
|||
if (!CheckedInt<GLsizeiptr>(data.LengthAllowShared()).isValid())
|
||||
return ErrorOutOfMemory("bufferData: bad size");
|
||||
|
||||
if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
|
||||
const auto& buffer = ValidateBufferSelection(funcName, target);
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
WebGLBuffer* boundBuffer = bufferSlot.get();
|
||||
|
||||
if (!boundBuffer)
|
||||
return ErrorInvalidOperation("bufferData: no buffer bound!");
|
||||
|
||||
MakeContextCurrent();
|
||||
InvalidateBufferFetching();
|
||||
|
||||
// Warning: Possibly shared memory. See bug 1225033.
|
||||
GLenum error = CheckedBufferData(target, data.LengthAllowShared(), data.DataAllowShared(), usage);
|
||||
|
||||
if (error) {
|
||||
GenerateWarning("bufferData generated error %s", ErrorName(error));
|
||||
return;
|
||||
}
|
||||
|
||||
boundBuffer->SetByteLength(data.LengthAllowShared());
|
||||
|
||||
// Warning: Possibly shared memory. See bug 1225033.
|
||||
if (!boundBuffer->ElementArrayCacheBufferData(data.DataAllowShared(), data.LengthAllowShared())) {
|
||||
boundBuffer->SetByteLength(0);
|
||||
return ErrorOutOfMemory("bufferData: out of memory");
|
||||
}
|
||||
buffer->BufferData(target, data.LengthAllowShared(), data.DataAllowShared(), usage);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -264,6 +431,8 @@ WebGLContext::BufferData(GLenum target, const dom::ArrayBufferView& data,
|
|||
BufferDataT(target, data, usage);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
// BufferT may be one of
|
||||
// const dom::ArrayBuffer&
|
||||
// const dom::SharedArrayBuffer&
|
||||
|
@ -274,25 +443,28 @@ WebGLContext::BufferSubDataT(GLenum target,
|
|||
WebGLsizeiptr byteOffset,
|
||||
const BufferT& data)
|
||||
{
|
||||
const char funcName[] = "bufferSubData";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateBufferTarget(target, "bufferSubData"))
|
||||
if (!ValidateNonNegative(funcName, "byteOffset", byteOffset))
|
||||
return;
|
||||
|
||||
WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);
|
||||
const auto& buffer = ValidateBufferSelection(funcName, target);
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
if (byteOffset < 0)
|
||||
return ErrorInvalidValue("bufferSubData: negative offset");
|
||||
|
||||
WebGLBuffer* boundBuffer = bufferSlot.get();
|
||||
if (!boundBuffer)
|
||||
return ErrorInvalidOperation("bufferData: no buffer bound!");
|
||||
if (buffer->mNumActiveTFOs) {
|
||||
ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
|
||||
" object.",
|
||||
"bufferSubData");
|
||||
return;
|
||||
}
|
||||
|
||||
data.ComputeLengthAndData();
|
||||
|
||||
CheckedInt<WebGLsizeiptr> checked_neededByteLength =
|
||||
CheckedInt<WebGLsizeiptr>(byteOffset) + data.LengthAllowShared();
|
||||
const auto checked_neededByteLength =
|
||||
CheckedInt<size_t>(byteOffset) + data.LengthAllowShared();
|
||||
|
||||
if (!checked_neededByteLength.isValid()) {
|
||||
ErrorInvalidValue("bufferSubData: Integer overflow computing the needed"
|
||||
|
@ -300,21 +472,22 @@ WebGLContext::BufferSubDataT(GLenum target,
|
|||
return;
|
||||
}
|
||||
|
||||
if (checked_neededByteLength.value() > boundBuffer->ByteLength()) {
|
||||
if (checked_neededByteLength.value() > buffer->ByteLength()) {
|
||||
ErrorInvalidValue("bufferSubData: Not enough data. Operation requires"
|
||||
" %d bytes, but buffer only has %d bytes.",
|
||||
checked_neededByteLength.value(),
|
||||
boundBuffer->ByteLength());
|
||||
buffer->ByteLength());
|
||||
return;
|
||||
}
|
||||
|
||||
// Warning: Possibly shared memory. See bug 1225033.
|
||||
boundBuffer->ElementArrayCacheBufferSubData(byteOffset, data.DataAllowShared(),
|
||||
data.LengthAllowShared());
|
||||
|
||||
MakeContextCurrent();
|
||||
// Warning: Possibly shared memory. See bug 1225033.
|
||||
gl->fBufferSubData(target, byteOffset, data.LengthAllowShared(), data.DataAllowShared());
|
||||
gl->fBufferSubData(target, byteOffset, data.LengthAllowShared(),
|
||||
data.DataAllowShared());
|
||||
|
||||
// Warning: Possibly shared memory. See bug 1225033.
|
||||
buffer->ElementArrayCacheBufferSubData(byteOffset, data.DataAllowShared(),
|
||||
data.LengthAllowShared());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -342,6 +515,8 @@ WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
|
|||
BufferSubDataT(target, byteOffset, data);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
already_AddRefed<WebGLBuffer>
|
||||
WebGLContext::CreateBuffer()
|
||||
{
|
||||
|
@ -368,60 +543,43 @@ WebGLContext::DeleteBuffer(WebGLBuffer* buffer)
|
|||
if (!buffer || buffer->IsDeleted())
|
||||
return;
|
||||
|
||||
// TODO: Extract this into a helper function?
|
||||
if (mBoundArrayBuffer == buffer) {
|
||||
WebGLContextUnchecked::BindBuffer(LOCAL_GL_ARRAY_BUFFER, nullptr);
|
||||
mBoundArrayBuffer = nullptr;
|
||||
}
|
||||
////
|
||||
|
||||
if (mBoundVertexArray->mElementArrayBuffer == buffer) {
|
||||
WebGLContextUnchecked::BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER, nullptr);
|
||||
mBoundVertexArray->mElementArrayBuffer = nullptr;
|
||||
}
|
||||
const auto fnClearIfBuffer = [&](WebGLRefPtr<WebGLBuffer>& bindPoint) {
|
||||
if (bindPoint == buffer) {
|
||||
bindPoint = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
fnClearIfBuffer(mBoundArrayBuffer);
|
||||
fnClearIfBuffer(mBoundVertexArray->mElementArrayBuffer);
|
||||
|
||||
// WebGL binding points
|
||||
if (IsWebGL2()) {
|
||||
if (mBoundCopyReadBuffer == buffer)
|
||||
mBoundCopyReadBuffer = nullptr;
|
||||
fnClearIfBuffer(mBoundCopyReadBuffer);
|
||||
fnClearIfBuffer(mBoundCopyWriteBuffer);
|
||||
fnClearIfBuffer(mBoundPixelPackBuffer);
|
||||
fnClearIfBuffer(mBoundPixelUnpackBuffer);
|
||||
fnClearIfBuffer(mBoundUniformBuffer);
|
||||
fnClearIfBuffer(mBoundTransformFeedback->mGenericBufferBinding);
|
||||
|
||||
if (mBoundCopyWriteBuffer == buffer)
|
||||
mBoundCopyWriteBuffer = nullptr;
|
||||
|
||||
if (mBoundPixelPackBuffer == buffer)
|
||||
mBoundPixelPackBuffer = nullptr;
|
||||
|
||||
if (mBoundPixelUnpackBuffer == buffer)
|
||||
mBoundPixelUnpackBuffer = nullptr;
|
||||
|
||||
if (mBoundTransformFeedbackBuffer == buffer)
|
||||
mBoundTransformFeedbackBuffer = nullptr;
|
||||
|
||||
if (mBoundUniformBuffer == buffer)
|
||||
mBoundUniformBuffer = nullptr;
|
||||
|
||||
const size_t xfBufferCount = mBoundTransformFeedbackBuffers.Length();
|
||||
for (size_t n = 0; n < xfBufferCount; n++) {
|
||||
if (mBoundTransformFeedbackBuffers[n] == buffer) {
|
||||
mBoundTransformFeedbackBuffers[n] = nullptr;
|
||||
}
|
||||
for (auto& binding : mBoundTransformFeedback->mIndexedBindings) {
|
||||
fnClearIfBuffer(binding.mBufferBinding);
|
||||
}
|
||||
|
||||
const size_t uniformBufferCount = mBoundUniformBuffers.Length();
|
||||
for (size_t n = 0; n < uniformBufferCount; n++) {
|
||||
if (mBoundUniformBuffers[n] == buffer) {
|
||||
mBoundUniformBuffers[n] = nullptr;
|
||||
}
|
||||
for (auto& binding : mIndexedUniformBufferBindings) {
|
||||
fnClearIfBuffer(binding.mBufferBinding);
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < mGLMaxVertexAttribs; i++) {
|
||||
if (mBoundVertexArray->HasAttrib(i) &&
|
||||
mBoundVertexArray->mAttribs[i].buf == buffer)
|
||||
{
|
||||
mBoundVertexArray->mAttribs[i].buf = nullptr;
|
||||
if (mBoundVertexArray->HasAttrib(i)) {
|
||||
fnClearIfBuffer(mBoundVertexArray->mAttribs[i].buf);
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
buffer->RequestDelete();
|
||||
}
|
||||
|
||||
|
@ -441,162 +599,4 @@ WebGLContext::IsBuffer(WebGLBuffer* buffer)
|
|||
return gl->fIsBuffer(buffer->mGLName);
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContext::ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer,
|
||||
const char* info)
|
||||
{
|
||||
if (!buffer)
|
||||
return true;
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* Any call to one of these functions which attempts to bind a
|
||||
* WebGLBuffer that has the element array WebGL buffer type to a
|
||||
* binding point that falls under other data, or bind a
|
||||
* WebGLBuffer which has the other data WebGL buffer type to
|
||||
* ELEMENT_ARRAY_BUFFER will generate an INVALID_OPERATION error,
|
||||
* and the state of the binding point will remain untouched.
|
||||
*/
|
||||
|
||||
WebGLBuffer::Kind content = buffer->Content();
|
||||
if (content == WebGLBuffer::Kind::Undefined)
|
||||
return true;
|
||||
|
||||
switch (target) {
|
||||
case LOCAL_GL_COPY_READ_BUFFER:
|
||||
case LOCAL_GL_COPY_WRITE_BUFFER:
|
||||
return true;
|
||||
|
||||
case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
|
||||
if (content == WebGLBuffer::Kind::ElementArray)
|
||||
return true;
|
||||
break;
|
||||
|
||||
case LOCAL_GL_ARRAY_BUFFER:
|
||||
case LOCAL_GL_PIXEL_PACK_BUFFER:
|
||||
case LOCAL_GL_PIXEL_UNPACK_BUFFER:
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
|
||||
case LOCAL_GL_UNIFORM_BUFFER:
|
||||
if (content == WebGLBuffer::Kind::OtherData)
|
||||
return true;
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
ErrorInvalidOperation("%s: buffer already contains %s data.", info,
|
||||
content == WebGLBuffer::Kind::OtherData ? "other" : "element");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContext::ValidateBufferUsageEnum(GLenum target, const char* info)
|
||||
{
|
||||
switch (target) {
|
||||
case LOCAL_GL_STREAM_DRAW:
|
||||
case LOCAL_GL_STATIC_DRAW:
|
||||
case LOCAL_GL_DYNAMIC_DRAW:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ErrorInvalidEnumInfo(info, target);
|
||||
return false;
|
||||
}
|
||||
|
||||
WebGLRefPtr<WebGLBuffer>&
|
||||
WebGLContext::GetBufferSlotByTarget(GLenum target)
|
||||
{
|
||||
/* This function assumes that target has been validated for either
|
||||
* WebGL1 or WebGL2.
|
||||
*/
|
||||
switch (target) {
|
||||
case LOCAL_GL_ARRAY_BUFFER:
|
||||
return mBoundArrayBuffer;
|
||||
|
||||
case LOCAL_GL_COPY_READ_BUFFER:
|
||||
return mBoundCopyReadBuffer;
|
||||
|
||||
case LOCAL_GL_COPY_WRITE_BUFFER:
|
||||
return mBoundCopyWriteBuffer;
|
||||
|
||||
case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
|
||||
return mBoundVertexArray->mElementArrayBuffer;
|
||||
|
||||
case LOCAL_GL_PIXEL_PACK_BUFFER:
|
||||
return mBoundPixelPackBuffer;
|
||||
|
||||
case LOCAL_GL_PIXEL_UNPACK_BUFFER:
|
||||
return mBoundPixelUnpackBuffer;
|
||||
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
|
||||
return mBoundTransformFeedbackBuffer;
|
||||
|
||||
case LOCAL_GL_UNIFORM_BUFFER:
|
||||
return mBoundUniformBuffer;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("GFX: Should not get here.");
|
||||
}
|
||||
}
|
||||
|
||||
WebGLRefPtr<WebGLBuffer>&
|
||||
WebGLContext::GetBufferSlotByTargetIndexed(GLenum target, GLuint index)
|
||||
{
|
||||
/* This function assumes that target has been validated for either WebGL1 or WebGL. */
|
||||
switch (target) {
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
|
||||
MOZ_ASSERT(index < mGLMaxTransformFeedbackSeparateAttribs);
|
||||
return mBoundTransformFeedbackBuffers[index];
|
||||
|
||||
case LOCAL_GL_UNIFORM_BUFFER:
|
||||
MOZ_ASSERT(index < mGLMaxUniformBufferBindings);
|
||||
return mBoundUniformBuffers[index];
|
||||
|
||||
default:
|
||||
MOZ_CRASH("GFX: Should not get here.");
|
||||
}
|
||||
}
|
||||
|
||||
GLenum
|
||||
WebGLContext::CheckedBufferData(GLenum target, GLsizeiptr size,
|
||||
const GLvoid* data, GLenum usage)
|
||||
{
|
||||
#ifdef XP_MACOSX
|
||||
// bug 790879
|
||||
if (gl->WorkAroundDriverBugs() &&
|
||||
int64_t(size) > INT32_MAX) // cast avoids a potential always-true warning on 32bit
|
||||
{
|
||||
GenerateWarning("Rejecting valid bufferData call with size %lu to avoid"
|
||||
" a Mac bug", size);
|
||||
return LOCAL_GL_INVALID_VALUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);
|
||||
WebGLBuffer* boundBuffer = bufferSlot.get();
|
||||
MOZ_ASSERT(boundBuffer, "No buffer bound for this target.");
|
||||
|
||||
bool sizeChanges = uint32_t(size) != boundBuffer->ByteLength();
|
||||
if (sizeChanges) {
|
||||
GetAndFlushUnderlyingGLErrors();
|
||||
gl->fBufferData(target, size, data, usage);
|
||||
GLenum error = GetAndFlushUnderlyingGLErrors();
|
||||
return error;
|
||||
} else {
|
||||
gl->fBufferData(target, size, data, usage);
|
||||
return LOCAL_GL_NO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -250,22 +250,21 @@ WebGLContext::DrawInstanced_check(const char* info)
|
|||
}
|
||||
|
||||
bool
|
||||
WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcount,
|
||||
const char* info)
|
||||
WebGLContext::DrawArrays_check(const char* funcName, GLenum mode, GLint first,
|
||||
GLsizei vertCount, GLsizei instanceCount)
|
||||
{
|
||||
if (first < 0 || count < 0) {
|
||||
ErrorInvalidValue("%s: negative first or count", info);
|
||||
if (!ValidateDrawModeEnum(mode, funcName))
|
||||
return false;
|
||||
|
||||
if (!ValidateNonNegative(funcName, "first", first) ||
|
||||
!ValidateNonNegative(funcName, "vertCount", vertCount) ||
|
||||
!ValidateNonNegative(funcName, "instanceCount", instanceCount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (primcount < 0) {
|
||||
ErrorInvalidValue("%s: negative primcount", info);
|
||||
if (!ValidateStencilParamsForDrawCall())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ValidateStencilParamsForDrawCall()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
|
||||
MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
|
||||
|
@ -277,124 +276,295 @@ WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcount,
|
|||
}
|
||||
}
|
||||
|
||||
// If count is 0, there's nothing to do.
|
||||
if (count == 0 || primcount == 0) {
|
||||
if (!vertCount || !instanceCount)
|
||||
return false; // No error, just early out.
|
||||
|
||||
if (!ValidateBufferFetching(funcName))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ValidateBufferFetching(info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckedInt<GLsizei> checked_firstPlusCount = CheckedInt<GLsizei>(first) + count;
|
||||
|
||||
const auto checked_firstPlusCount = CheckedInt<GLsizei>(first) + vertCount;
|
||||
if (!checked_firstPlusCount.isValid()) {
|
||||
ErrorInvalidOperation("%s: overflow in first+count", info);
|
||||
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", info);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uint32_t(primcount) > mMaxFetchedInstances) {
|
||||
ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) {
|
||||
ErrorInvalidOperation("%s: Bound vertex attribute buffers do not have sufficient"
|
||||
" size for given first and count.",
|
||||
funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
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)
|
||||
: mWebGL(webgl)
|
||||
, mDidFake(false)
|
||||
{
|
||||
if (instanceCount > mWebGL->mMaxFetchedInstances) {
|
||||
mWebGL->ErrorInvalidOperation("%s: Bound instance attribute buffers do not"
|
||||
" have sufficient size for given"
|
||||
" `instanceCount`.",
|
||||
funcName);
|
||||
*out_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mWebGL->gl->IsCurrent());
|
||||
|
||||
if (mWebGL->mBoundDrawFramebuffer) {
|
||||
if (!mWebGL->mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName)) {
|
||||
*out_error = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mWebGL->ClearBackbufferIfNeeded();
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
const size_t requiredVerts = firstVertex + vertCount;
|
||||
if (!mWebGL->DoFakeVertexAttrib0(requiredVerts)) {
|
||||
*out_error = true;
|
||||
return;
|
||||
}
|
||||
mDidFake = true;
|
||||
|
||||
////
|
||||
// Check UBO sizes.
|
||||
|
||||
const auto& linkInfo = webgl->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;
|
||||
}
|
||||
|
||||
const auto availByteCount = binding->ByteCount();
|
||||
if (dataSize > availByteCount) {
|
||||
mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is smaller"
|
||||
" than UNIFORM_BLOCK_DATA_SIZE.",
|
||||
funcName);
|
||||
*out_error = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
mWebGL->RunContextLossTimer();
|
||||
}
|
||||
|
||||
~ScopedDrawHelper() {
|
||||
if (mDidFake) {
|
||||
mWebGL->UndoFakeVertexAttrib0();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
static uint32_t
|
||||
UsedVertsForTFDraw(GLenum mode, uint32_t vertCount)
|
||||
{
|
||||
uint8_t vertsPerPrim;
|
||||
|
||||
switch (mode) {
|
||||
case LOCAL_GL_POINTS:
|
||||
vertsPerPrim = 1;
|
||||
break;
|
||||
case LOCAL_GL_LINES:
|
||||
vertsPerPrim = 2;
|
||||
break;
|
||||
case LOCAL_GL_TRIANGLES:
|
||||
vertsPerPrim = 3;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("`mode`");
|
||||
}
|
||||
|
||||
return vertCount / vertsPerPrim * vertsPerPrim;
|
||||
}
|
||||
|
||||
class ScopedDrawWithTransformFeedback final
|
||||
{
|
||||
WebGLContext* const mWebGL;
|
||||
WebGLTransformFeedback* const mTFO;
|
||||
const bool mWithTF;
|
||||
uint32_t mUsedVerts;
|
||||
|
||||
public:
|
||||
ScopedDrawWithTransformFeedback(WebGLContext* webgl, const char* funcName,
|
||||
GLenum mode, uint32_t vertCount,
|
||||
uint32_t instanceCount, bool* const out_error)
|
||||
: mWebGL(webgl)
|
||||
, mTFO(mWebGL->mBoundTransformFeedback)
|
||||
, mWithTF(mTFO &&
|
||||
mTFO->mIsActive &&
|
||||
!mTFO->mIsPaused)
|
||||
, mUsedVerts(0)
|
||||
{
|
||||
*out_error = false;
|
||||
if (!mWithTF)
|
||||
return;
|
||||
|
||||
if (mode != mTFO->mActive_PrimMode) {
|
||||
mWebGL->ErrorInvalidOperation("%s: Drawing with transform feedback requires"
|
||||
" `mode` to match BeginTransformFeedback's"
|
||||
" `primitiveMode`.",
|
||||
funcName);
|
||||
*out_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto usedVertsPerInstance = UsedVertsForTFDraw(mode, vertCount);
|
||||
const auto usedVerts = CheckedInt<uint32_t>(usedVertsPerInstance) * instanceCount;
|
||||
|
||||
const auto remainingCapacity = mTFO->mActive_VertCapacity - mTFO->mActive_VertPosition;
|
||||
if (!usedVerts.isValid() ||
|
||||
usedVerts.value() > remainingCapacity)
|
||||
{
|
||||
mWebGL->ErrorInvalidOperation("%s: Insufficient buffer capacity remaining for"
|
||||
" transform feedback.",
|
||||
funcName);
|
||||
*out_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mUsedVerts = usedVerts.value();
|
||||
}
|
||||
|
||||
void Advance() const {
|
||||
if (!mWithTF)
|
||||
return;
|
||||
|
||||
mTFO->mActive_VertPosition += mUsedVerts;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
void
|
||||
WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count)
|
||||
WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei vertCount)
|
||||
{
|
||||
const char funcName[] = "drawArrays";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateDrawModeEnum(mode, funcName))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
bool error;
|
||||
bool error = false;
|
||||
ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
if (!DrawArrays_check(first, count, 1, funcName))
|
||||
const GLsizei instanceCount = 1;
|
||||
if (!DrawArrays_check(funcName, mode, first, vertCount, instanceCount))
|
||||
return;
|
||||
|
||||
RunContextLossTimer();
|
||||
const ScopedDrawHelper scopedHelper(this, funcName, first, vertCount, instanceCount, &error);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
|
||||
instanceCount, &error);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
{
|
||||
ScopedMaskWorkaround autoMask(*this);
|
||||
gl->fDrawArrays(mode, first, count);
|
||||
gl->fDrawArrays(mode, first, vertCount);
|
||||
}
|
||||
|
||||
Draw_cleanup(funcName);
|
||||
scopedTF.Advance();
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
|
||||
WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount,
|
||||
GLsizei instanceCount)
|
||||
{
|
||||
const char funcName[] = "drawArraysInstanced";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateDrawModeEnum(mode, funcName))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
bool error;
|
||||
bool error = false;
|
||||
ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
if (!DrawArrays_check(first, count, primcount, funcName))
|
||||
if (!DrawArrays_check(funcName, mode, first, vertCount, instanceCount))
|
||||
return;
|
||||
|
||||
if (!DrawInstanced_check(funcName))
|
||||
return;
|
||||
|
||||
RunContextLossTimer();
|
||||
const ScopedDrawHelper scopedHelper(this, funcName, first, vertCount, instanceCount, &error);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
|
||||
instanceCount, &error);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
{
|
||||
ScopedMaskWorkaround autoMask(*this);
|
||||
gl->fDrawArraysInstanced(mode, first, count, primcount);
|
||||
gl->fDrawArraysInstanced(mode, first, vertCount, instanceCount);
|
||||
}
|
||||
|
||||
Draw_cleanup(funcName);
|
||||
scopedTF.Advance();
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
bool
|
||||
WebGLContext::DrawElements_check(GLsizei count, GLenum type,
|
||||
WebGLintptr byteOffset, GLsizei primcount,
|
||||
const char* info, GLuint* out_upperBound)
|
||||
WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vertCount,
|
||||
GLenum type, WebGLintptr byteOffset,
|
||||
GLsizei instanceCount)
|
||||
{
|
||||
if (count < 0 || byteOffset < 0) {
|
||||
ErrorInvalidValue("%s: negative count or offset", info);
|
||||
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 (primcount < 0) {
|
||||
ErrorInvalidValue("%s: negative primcount", info);
|
||||
if (!ValidateNonNegative(funcName, "vertCount", vertCount) ||
|
||||
!ValidateNonNegative(funcName, "byteOffset", byteOffset) ||
|
||||
!ValidateNonNegative(funcName, "instanceCount", instanceCount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ValidateStencilParamsForDrawCall()) {
|
||||
if (!ValidateStencilParamsForDrawCall())
|
||||
return false;
|
||||
}
|
||||
|
||||
// If count is 0, there's nothing to do.
|
||||
if (count == 0 || primcount == 0)
|
||||
return false;
|
||||
if (!vertCount || !instanceCount)
|
||||
return false; // No error, just early out.
|
||||
|
||||
uint8_t bytesPerElem = 0;
|
||||
switch (type) {
|
||||
|
@ -414,13 +584,13 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type,
|
|||
}
|
||||
|
||||
if (!bytesPerElem) {
|
||||
ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", info, type);
|
||||
ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", funcName, type);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (byteOffset % bytesPerElem != 0) {
|
||||
ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`",
|
||||
info);
|
||||
funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -439,51 +609,49 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type,
|
|||
////
|
||||
|
||||
const GLsizei first = byteOffset / bytesPerElem;
|
||||
const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(count);
|
||||
const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(vertCount);
|
||||
|
||||
if (!checked_byteCount.isValid()) {
|
||||
ErrorInvalidValue("%s: overflow in byteCount", info);
|
||||
ErrorInvalidValue("%s: Overflow in byteCount.", funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mBoundVertexArray->mElementArrayBuffer) {
|
||||
ErrorInvalidOperation("%s: must have element array buffer binding", info);
|
||||
ErrorInvalidOperation("%s: Must have element array buffer binding.", funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mElementArrayBuffer;
|
||||
|
||||
if (!elemArrayBuffer.ByteLength()) {
|
||||
ErrorInvalidOperation("%s: bound element array buffer doesn't have any data", info);
|
||||
ErrorInvalidOperation("%s: Bound element array buffer doesn't have any data.",
|
||||
funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + byteOffset;
|
||||
|
||||
if (!checked_neededByteCount.isValid()) {
|
||||
ErrorInvalidOperation("%s: overflow in byteOffset+byteCount", info);
|
||||
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", info);
|
||||
ErrorInvalidOperation("%s: Bound element array buffer is too small for given"
|
||||
" count and offset.",
|
||||
funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ValidateBufferFetching(info))
|
||||
if (!ValidateBufferFetching(funcName))
|
||||
return false;
|
||||
|
||||
if (!mMaxFetchedVertices ||
|
||||
!elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, count, out_upperBound))
|
||||
!elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, vertCount))
|
||||
{
|
||||
ErrorInvalidOperation(
|
||||
"%s: bound vertex attribute buffers do not have sufficient "
|
||||
"size for given indices from the bound element array", info);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uint32_t(primcount) > mMaxFetchedInstances) {
|
||||
ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
|
||||
ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient "
|
||||
"size for given indices from the bound element array",
|
||||
funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -491,97 +659,86 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type,
|
|||
if (elemArrayBuffer.IsElementArrayUsedWithMultipleTypes()) {
|
||||
GenerateWarning("%s: bound element array buffer previously used with a type other than "
|
||||
"%s, this will affect performance.",
|
||||
info,
|
||||
WebGLContext::EnumName(type));
|
||||
funcName, WebGLContext::EnumName(type));
|
||||
}
|
||||
|
||||
if (!DoFakeVertexAttrib0(mMaxFetchedVertices))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type,
|
||||
WebGLContext::DrawElements(GLenum mode, GLsizei vertCount, GLenum type,
|
||||
WebGLintptr byteOffset)
|
||||
{
|
||||
const char funcName[] = "drawElements";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateDrawModeEnum(mode, funcName))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
bool error;
|
||||
bool error = false;
|
||||
ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
GLuint upperBound = 0;
|
||||
if (!DrawElements_check(count, type, byteOffset, 1, funcName, &upperBound))
|
||||
const GLsizei instanceCount = 1;
|
||||
if (!DrawElements_check(funcName, mode, vertCount, type, byteOffset, instanceCount))
|
||||
return;
|
||||
|
||||
RunContextLossTimer();
|
||||
const ScopedDrawHelper scopedHelper(this, funcName, 0, mMaxFetchedVertices, instanceCount,
|
||||
&error);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
{
|
||||
ScopedMaskWorkaround autoMask(*this);
|
||||
|
||||
if (gl->IsSupported(gl::GLFeature::draw_range_elements)) {
|
||||
gl->fDrawRangeElements(mode, 0, upperBound, count, type,
|
||||
reinterpret_cast<GLvoid*>(byteOffset));
|
||||
} else {
|
||||
gl->fDrawElements(mode, count, type,
|
||||
reinterpret_cast<GLvoid*>(byteOffset));
|
||||
}
|
||||
gl->fDrawElements(mode, vertCount, type,
|
||||
reinterpret_cast<GLvoid*>(byteOffset));
|
||||
}
|
||||
|
||||
Draw_cleanup(funcName);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
|
||||
WebGLintptr byteOffset, GLsizei primcount)
|
||||
WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei vertCount, GLenum type,
|
||||
WebGLintptr byteOffset, GLsizei instanceCount)
|
||||
{
|
||||
const char funcName[] = "drawElementsInstanced";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateDrawModeEnum(mode, funcName))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
bool error;
|
||||
bool error = false;
|
||||
ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
GLuint upperBound = 0;
|
||||
if (!DrawElements_check(count, type, byteOffset, primcount, funcName, &upperBound))
|
||||
if (!DrawElements_check(funcName, mode, vertCount, type, byteOffset, instanceCount))
|
||||
return;
|
||||
|
||||
if (!DrawInstanced_check(funcName))
|
||||
return;
|
||||
|
||||
RunContextLossTimer();
|
||||
const ScopedDrawHelper scopedHelper(this, funcName, 0, mMaxFetchedVertices, instanceCount,
|
||||
&error);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
{
|
||||
ScopedMaskWorkaround autoMask(*this);
|
||||
gl->fDrawElementsInstanced(mode, count, type,
|
||||
gl->fDrawElementsInstanced(mode, vertCount, type,
|
||||
reinterpret_cast<GLvoid*>(byteOffset),
|
||||
primcount);
|
||||
instanceCount);
|
||||
}
|
||||
|
||||
Draw_cleanup(funcName);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
void
|
||||
WebGLContext::Draw_cleanup(const char* funcName)
|
||||
{
|
||||
UndoFakeVertexAttrib0();
|
||||
|
||||
if (!mBoundDrawFramebuffer) {
|
||||
Invalidate();
|
||||
mShouldPresent = true;
|
||||
|
|
|
@ -626,15 +626,10 @@ WebGLContext::GetBufferParameter(GLenum target, GLenum pname)
|
|||
if (IsContextLost())
|
||||
return JS::NullValue();
|
||||
|
||||
if (!ValidateBufferTarget(target, "getBufferParameter"))
|
||||
const auto& buffer = ValidateBufferSelection("getBufferParameter", target);
|
||||
if (!buffer)
|
||||
return JS::NullValue();
|
||||
|
||||
WebGLRefPtr<WebGLBuffer>& slot = GetBufferSlotByTarget(target);
|
||||
if (!slot) {
|
||||
ErrorInvalidOperation("No buffer bound to `target` (0x%4x).", target);
|
||||
return JS::NullValue();
|
||||
}
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
switch (pname) {
|
||||
|
@ -1499,6 +1494,13 @@ WebGL2Context::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenu
|
|||
return;
|
||||
}
|
||||
|
||||
if (mBoundPixelPackBuffer->mNumActiveTFOs) {
|
||||
ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
|
||||
" object.",
|
||||
"readPixels");
|
||||
return;
|
||||
}
|
||||
|
||||
//////
|
||||
|
||||
if (offset < 0) {
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "WebGLContextUnchecked.h"
|
||||
|
||||
#include "GLContext.h"
|
||||
#include "WebGLBuffer.h"
|
||||
#include "WebGLSampler.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -17,50 +16,6 @@ WebGLContextUnchecked::WebGLContextUnchecked(gl::GLContext* _gl)
|
|||
, gl(mGL_OnlyClearInDestroyResourcesAndContext) // const reference
|
||||
{ }
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Buffer Objects
|
||||
|
||||
void
|
||||
WebGLContextUnchecked::BindBuffer(GLenum target, WebGLBuffer* buffer)
|
||||
{
|
||||
gl->MakeCurrent();
|
||||
gl->fBindBuffer(target, buffer ? buffer->mGLName : 0);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContextUnchecked::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
|
||||
{
|
||||
gl->MakeCurrent();
|
||||
gl->fBindBufferBase(target, index, buffer ? buffer->mGLName : 0);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContextUnchecked::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer, WebGLintptr offset, WebGLsizeiptr size)
|
||||
{
|
||||
gl->MakeCurrent();
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
if (buffer && buffer->Content() == WebGLBuffer::Kind::Undefined &&
|
||||
gl->WorkAroundDriverBugs())
|
||||
{
|
||||
// BindBufferRange will fail if the buffer's contents is undefined.
|
||||
// Bind so driver initializes the buffer.
|
||||
gl->fBindBuffer(target, buffer->mGLName);
|
||||
}
|
||||
#endif
|
||||
|
||||
gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset, size);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContextUnchecked::CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size)
|
||||
{
|
||||
gl->MakeCurrent();
|
||||
gl->fCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Sampler Objects
|
||||
|
||||
|
|
|
@ -20,13 +20,6 @@ class WebGLContextUnchecked
|
|||
public:
|
||||
explicit WebGLContextUnchecked(gl::GLContext* gl);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Buffer Objects
|
||||
void BindBuffer(GLenum target, WebGLBuffer* buffer);
|
||||
void BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer);
|
||||
void BindBufferRange(GLenum taret, GLuint index, WebGLBuffer* buffer, WebGLintptr offset, WebGLsizeiptr size);
|
||||
void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Sampler Objects
|
||||
void BindSampler(GLuint unit, WebGLSampler* sampler);
|
||||
|
|
|
@ -122,36 +122,11 @@ WebGLContext::ValidateBlendFuncEnumsCompatibility(GLenum sfactor,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContext::ValidateDataOffsetSize(WebGLintptr offset, WebGLsizeiptr size, WebGLsizeiptr bufferSize, const char* info)
|
||||
{
|
||||
if (offset < 0) {
|
||||
ErrorInvalidValue("%s: offset must be positive", info);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size < 0) {
|
||||
ErrorInvalidValue("%s: size must be positive", info);
|
||||
return false;
|
||||
}
|
||||
|
||||
// *** Careful *** WebGLsizeiptr is always 64-bits but GLsizeiptr
|
||||
// is like intptr_t. On some platforms it is 32-bits.
|
||||
CheckedInt<GLsizeiptr> neededBytes = CheckedInt<GLsizeiptr>(offset) + size;
|
||||
if (!neededBytes.isValid() || neededBytes.value() > bufferSize) {
|
||||
ErrorInvalidValue("%s: invalid range", info);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check data ranges [readOffset, readOffset + size] and [writeOffset,
|
||||
* writeOffset + size] for overlap.
|
||||
*
|
||||
* It is assumed that offset and size have already been validated with
|
||||
* ValidateDataOffsetSize().
|
||||
* It is assumed that offset and size have already been validated.
|
||||
*/
|
||||
bool
|
||||
WebGLContext::ValidateDataRanges(WebGLintptr readOffset, WebGLintptr writeOffset, WebGLsizeiptr size, const char* info)
|
||||
|
@ -738,7 +713,6 @@ WebGLContext::InitAndValidateGL(FailureReason* const out_failReason)
|
|||
mBoundSamplers.Clear();
|
||||
|
||||
mBoundArrayBuffer = nullptr;
|
||||
mBoundTransformFeedbackBuffer = nullptr;
|
||||
mCurrentProgram = nullptr;
|
||||
|
||||
mBoundDrawFramebuffer = nullptr;
|
||||
|
@ -980,7 +954,6 @@ WebGLContext::InitAndValidateGL(FailureReason* const out_failReason)
|
|||
// vertex array object (the name zero) is also deprecated. [...]"
|
||||
|
||||
if (gl->IsCoreProfile()) {
|
||||
MakeContextCurrent();
|
||||
mDefaultVertexArray->GenVertexArray();
|
||||
mDefaultVertexArray->BindVertexArray();
|
||||
}
|
||||
|
|
|
@ -15,16 +15,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
static void
|
||||
UpdateUpperBound(uint32_t* const out_upperBound, uint32_t newBound)
|
||||
{
|
||||
MOZ_ASSERT(out_upperBound);
|
||||
// Move *out_upperBound to a local variable to work around a false positive
|
||||
// -Wuninitialized gcc warning about std::max() in PGO builds.
|
||||
uint32_t upperBound = *out_upperBound;
|
||||
*out_upperBound = std::max(upperBound, newBound);
|
||||
}
|
||||
|
||||
/* WebGLElementArrayCacheTree contains most of the implementation of
|
||||
* WebGLElementArrayCache, which performs WebGL element array buffer validation
|
||||
* for drawElements.
|
||||
|
@ -245,8 +235,7 @@ public:
|
|||
return ((numElements - 1) | kElementsPerLeafMask) + 1;
|
||||
}
|
||||
|
||||
bool Validate(T maxAllowed, size_t firstLeaf, size_t lastLeaf,
|
||||
uint32_t* const out_upperBound)
|
||||
bool Validate(T maxAllowed, size_t firstLeaf, size_t lastLeaf)
|
||||
{
|
||||
size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf);
|
||||
size_t lastTreeIndex = TreeIndexForLeaf(lastLeaf);
|
||||
|
@ -260,7 +249,6 @@ public:
|
|||
// current tree level:
|
||||
if (lastTreeIndex == firstTreeIndex) {
|
||||
const T& curData = mTreeData[firstTreeIndex];
|
||||
UpdateUpperBound(out_upperBound, curData);
|
||||
return curData <= maxAllowed;
|
||||
}
|
||||
|
||||
|
@ -269,7 +257,6 @@ public:
|
|||
// a left node.
|
||||
if (IsRightNode(firstTreeIndex)) {
|
||||
const T& curData = mTreeData[firstTreeIndex];
|
||||
UpdateUpperBound(out_upperBound, curData);
|
||||
if (curData > maxAllowed)
|
||||
return false;
|
||||
|
||||
|
@ -281,7 +268,6 @@ public:
|
|||
// right node.
|
||||
if (IsLeftNode(lastTreeIndex)) {
|
||||
const T& curData = mTreeData[lastTreeIndex];
|
||||
UpdateUpperBound(out_upperBound, curData);
|
||||
if (curData > maxAllowed)
|
||||
return false;
|
||||
|
||||
|
@ -514,18 +500,13 @@ WebGLElementArrayCache::UpdateTrees(size_t firstByte, size_t lastByte)
|
|||
template<typename T>
|
||||
bool
|
||||
WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
|
||||
size_t countElements,
|
||||
uint32_t* const out_upperBound)
|
||||
size_t countElements)
|
||||
{
|
||||
*out_upperBound = 0;
|
||||
|
||||
// If maxAllowed is >= the max T value, then there is no way that a T index
|
||||
// could be invalid.
|
||||
uint32_t maxTSize = std::numeric_limits<T>::max();
|
||||
if (maxAllowed >= maxTSize) {
|
||||
UpdateUpperBound(out_upperBound, maxTSize);
|
||||
if (maxAllowed >= maxTSize)
|
||||
return true;
|
||||
}
|
||||
|
||||
T maxAllowedT(maxAllowed);
|
||||
|
||||
|
@ -556,10 +537,8 @@ WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
|
|||
// Fast-exit path when the global maximum for the whole element array buffer
|
||||
// falls in the allowed range:
|
||||
T globalMax = tree->GlobalMaximum();
|
||||
if (globalMax <= maxAllowedT) {
|
||||
UpdateUpperBound(out_upperBound, globalMax);
|
||||
if (globalMax <= maxAllowedT)
|
||||
return true;
|
||||
}
|
||||
|
||||
const T* elements = Elements<T>();
|
||||
|
||||
|
@ -570,7 +549,6 @@ WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
|
|||
tree->LastElementUnderSameLeaf(firstElement));
|
||||
while (firstElement <= firstElementAdjustmentEnd) {
|
||||
const T& curData = elements[firstElement];
|
||||
UpdateUpperBound(out_upperBound, curData);
|
||||
if (curData > maxAllowedT)
|
||||
return false;
|
||||
|
||||
|
@ -580,7 +558,6 @@ WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
|
|||
tree->FirstElementUnderSameLeaf(lastElement));
|
||||
while (lastElement >= lastElementAdjustmentEnd) {
|
||||
const T& curData = elements[lastElement];
|
||||
UpdateUpperBound(out_upperBound, curData);
|
||||
if (curData > maxAllowedT)
|
||||
return false;
|
||||
|
||||
|
@ -593,23 +570,19 @@ WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
|
|||
|
||||
// general case
|
||||
return tree->Validate(maxAllowedT, tree->LeafForElement(firstElement),
|
||||
tree->LeafForElement(lastElement), out_upperBound);
|
||||
tree->LeafForElement(lastElement));
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLElementArrayCache::Validate(GLenum type, uint32_t maxAllowed,
|
||||
size_t firstElement, size_t countElements,
|
||||
uint32_t* const out_upperBound)
|
||||
size_t firstElement, size_t countElements)
|
||||
{
|
||||
if (type == LOCAL_GL_UNSIGNED_BYTE)
|
||||
return Validate<uint8_t>(maxAllowed, firstElement, countElements,
|
||||
out_upperBound);
|
||||
return Validate<uint8_t>(maxAllowed, firstElement, countElements);
|
||||
if (type == LOCAL_GL_UNSIGNED_SHORT)
|
||||
return Validate<uint16_t>(maxAllowed, firstElement, countElements,
|
||||
out_upperBound);
|
||||
return Validate<uint16_t>(maxAllowed, firstElement, countElements);
|
||||
if (type == LOCAL_GL_UNSIGNED_INT)
|
||||
return Validate<uint32_t>(maxAllowed, firstElement, countElements,
|
||||
out_upperBound);
|
||||
return Validate<uint32_t>(maxAllowed, firstElement, countElements);
|
||||
|
||||
MOZ_ASSERT(false, "Invalid type.");
|
||||
return false;
|
||||
|
|
|
@ -37,8 +37,7 @@ public:
|
|||
bool BufferData(const void* ptr, size_t byteLength);
|
||||
bool BufferSubData(size_t pos, const void* ptr, size_t updateByteSize);
|
||||
|
||||
bool Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count,
|
||||
uint32_t* const out_upperBound);
|
||||
bool Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count);
|
||||
|
||||
template<typename T>
|
||||
T Element(size_t i) const { return Elements<T>()[i]; }
|
||||
|
@ -74,8 +73,7 @@ private:
|
|||
* than maxAllowed.
|
||||
*/
|
||||
template<typename T>
|
||||
bool Validate(uint32_t maxAllowed, size_t first, size_t count,
|
||||
uint32_t* const out_upperBound);
|
||||
bool Validate(uint32_t maxAllowed, size_t first, size_t count);
|
||||
|
||||
template<typename T>
|
||||
const T* Elements() const {
|
||||
|
|
|
@ -331,7 +331,7 @@ ImplCycleCollectionUnlink(mozilla::WebGLRefPtr<T>& field)
|
|||
template <typename T>
|
||||
inline void
|
||||
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
|
||||
mozilla::WebGLRefPtr<T>& field,
|
||||
const mozilla::WebGLRefPtr<T>& field,
|
||||
const char* name,
|
||||
uint32_t flags = 0)
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "WebGLActiveInfo.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "WebGLShader.h"
|
||||
#include "WebGLTransformFeedback.h"
|
||||
#include "WebGLUniformLocation.h"
|
||||
#include "WebGLValidateStrings.h"
|
||||
|
||||
|
@ -331,6 +332,13 @@ QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl)
|
|||
}
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
GLuint dataSize = 0;
|
||||
gl->fGetActiveUniformBlockiv(prog->mGLName, i,
|
||||
LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE,
|
||||
(GLint*)&dataSize);
|
||||
|
||||
#ifdef DUMP_SHADERVAR_MAPPINGS
|
||||
printf_stderr("[uniform block %i] %s/%i/%s/%s\n", i,
|
||||
mappedName.BeginReading(), (int)isArray,
|
||||
|
@ -339,7 +347,8 @@ QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl)
|
|||
printf_stderr(" isArray: %d\n", (int)isArray);
|
||||
#endif
|
||||
|
||||
const auto* block = new webgl::UniformBlockInfo(baseUserName, baseMappedName);
|
||||
auto* block = new webgl::UniformBlockInfo(webgl, baseUserName, baseMappedName,
|
||||
dataSize);
|
||||
info->uniformBlocks.push_back(block);
|
||||
}
|
||||
}
|
||||
|
@ -436,7 +445,8 @@ CreateProgram(gl::GLContext* gl)
|
|||
WebGLProgram::WebGLProgram(WebGLContext* webgl)
|
||||
: WebGLContextBoundObject(webgl)
|
||||
, mGLName(CreateProgram(webgl->GL()))
|
||||
, mTransformFeedbackBufferMode(LOCAL_GL_NONE)
|
||||
, mNumActiveTFOs(0)
|
||||
, mNextLink_TransformFeedbackBufferMode(LOCAL_GL_SEPARATE_ATTRIBS)
|
||||
{
|
||||
mContext->mPrograms.insertBack(this);
|
||||
}
|
||||
|
@ -517,7 +527,7 @@ WebGLProgram::BindAttribLocation(GLuint loc, const nsAString& name)
|
|||
|
||||
NS_LossyConvertUTF16toASCII asciiName(name);
|
||||
|
||||
auto res = mBoundAttribLocs.insert(std::pair<nsCString, GLuint>(asciiName, loc));
|
||||
auto res = mNextLink_BoundAttribLocs.insert({asciiName, loc});
|
||||
|
||||
const bool wasInserted = res.second;
|
||||
if (!wasInserted) {
|
||||
|
@ -675,11 +685,13 @@ WebGLProgram::GetProgramParameter(GLenum pname) const
|
|||
if (mContext->IsWebGL2()) {
|
||||
switch (pname) {
|
||||
case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
|
||||
return JS::Int32Value(GetProgramiv(gl, mGLName, pname));
|
||||
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS:
|
||||
return JS::Int32Value(mTransformFeedbackVaryings.size());
|
||||
return JS::Int32Value(mNextLink_TransformFeedbackVaryings.size());
|
||||
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
|
||||
return JS::Int32Value(mNextLink_TransformFeedbackBufferMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -936,57 +948,58 @@ WebGLProgram::GetUniformIndices(const dom::Sequence<nsString>& uniformNames,
|
|||
|
||||
|
||||
void
|
||||
WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) const
|
||||
WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex,
|
||||
GLuint uniformBlockBinding) const
|
||||
{
|
||||
const char funcName[] = "getActiveUniformBlockName";
|
||||
if (!IsLinked()) {
|
||||
mContext->ErrorInvalidOperation("getActiveUniformBlockName: `program` must be linked.");
|
||||
mContext->ErrorInvalidOperation("%s: `program` must be linked.", funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
|
||||
if (uniformBlockIndex >= linkInfo->uniformBlocks.size()) {
|
||||
mContext->ErrorInvalidValue("getActiveUniformBlockName: index %u invalid.", uniformBlockIndex);
|
||||
const auto& uniformBlocks = LinkInfo()->uniformBlocks;
|
||||
if (uniformBlockIndex >= uniformBlocks.size()) {
|
||||
mContext->ErrorInvalidValue("%s: Index %u invalid.", funcName, uniformBlockIndex);
|
||||
return;
|
||||
}
|
||||
const auto& uniformBlock = uniformBlocks[uniformBlockIndex];
|
||||
|
||||
if (uniformBlockBinding > mContext->mGLMaxUniformBufferBindings) {
|
||||
mContext->ErrorInvalidEnum("getActiveUniformBlockName: binding %u invalid.", uniformBlockBinding);
|
||||
const auto& indexedBindings = mContext->mIndexedUniformBufferBindings;
|
||||
if (uniformBlockBinding >= indexedBindings.size()) {
|
||||
mContext->ErrorInvalidValue("%s: Binding %u invalid.", funcName,
|
||||
uniformBlockBinding);
|
||||
return;
|
||||
}
|
||||
const auto& indexedBinding = indexedBindings[uniformBlockBinding];
|
||||
|
||||
////
|
||||
|
||||
gl::GLContext* gl = mContext->GL();
|
||||
gl->MakeCurrent();
|
||||
gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding);
|
||||
|
||||
////
|
||||
|
||||
uniformBlock->mBinding = &indexedBinding;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLProgram::LinkProgram()
|
||||
bool
|
||||
WebGLProgram::ValidateForLink()
|
||||
{
|
||||
mContext->InvalidateBufferFetching(); // we do it early in this function
|
||||
// as some of the validation below changes program state
|
||||
|
||||
mLinkLog.Truncate();
|
||||
mMostRecentLinkInfo = nullptr;
|
||||
|
||||
if (!mVertShader || !mVertShader->IsCompiled()) {
|
||||
mLinkLog.AssignLiteral("Must have a compiled vertex shader attached.");
|
||||
mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mFragShader || !mFragShader->IsCompiled()) {
|
||||
mLinkLog.AssignLiteral("Must have an compiled fragment shader attached.");
|
||||
mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mFragShader->CanLinkTo(mVertShader, &mLinkLog)) {
|
||||
mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
|
||||
return;
|
||||
}
|
||||
if (!mFragShader->CanLinkTo(mVertShader, &mLinkLog))
|
||||
return false;
|
||||
|
||||
gl::GLContext* gl = mContext->gl;
|
||||
gl->MakeCurrent();
|
||||
const auto& gl = mContext->gl;
|
||||
|
||||
if (gl->WorkAroundDriverBugs() &&
|
||||
mContext->mIsMesa)
|
||||
|
@ -998,34 +1011,70 @@ WebGLProgram::LinkProgram()
|
|||
if (numSamplerUniforms_upperBound > 16) {
|
||||
mLinkLog.AssignLiteral("Programs with more than 16 samplers are disallowed on"
|
||||
" Mesa drivers to avoid crashing.");
|
||||
mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bug 1203135: Mesa crashes internally if we exceed the reported maximum attribute count.
|
||||
if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) {
|
||||
mLinkLog.AssignLiteral("Number of attributes exceeds Mesa's reported max attribute count.");
|
||||
mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
|
||||
return;
|
||||
mLinkLog.AssignLiteral("Number of attributes exceeds Mesa's reported max"
|
||||
" attribute count.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLProgram::LinkProgram()
|
||||
{
|
||||
const char funcName[] = "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;
|
||||
}
|
||||
|
||||
// Bind the attrib locations.
|
||||
// This can't be done trivially, because we have to deal with mapped attrib names.
|
||||
for (auto itr = mBoundAttribLocs.begin(); itr != mBoundAttribLocs.end(); ++itr) {
|
||||
const nsCString& name = itr->first;
|
||||
GLuint index = itr->second;
|
||||
for (const auto& pair : mNextLink_BoundAttribLocs) {
|
||||
const auto& name = pair.first;
|
||||
const auto& index = pair.second;
|
||||
|
||||
mVertShader->BindAttribLocation(mGLName, name, index);
|
||||
}
|
||||
|
||||
if (!mTransformFeedbackVaryings.empty()) {
|
||||
// Bind the transform feedback varyings.
|
||||
// This can't be done trivially, because we have to deal with mapped names too.
|
||||
mVertShader->ApplyTransformFeedbackVaryings(mGLName,
|
||||
mTransformFeedbackVaryings,
|
||||
mTransformFeedbackBufferMode,
|
||||
&mTempMappedVaryings);
|
||||
// Storage for transform feedback varyings before link.
|
||||
// (Work around for bug seen on nVidia drivers.)
|
||||
std::vector<std::string> scopedMappedTFVaryings;
|
||||
|
||||
if (mContext->IsWebGL2()) {
|
||||
mVertShader->MapTransformFeedbackVaryings(mNextLink_TransformFeedbackVaryings,
|
||||
&scopedMappedTFVaryings);
|
||||
|
||||
std::vector<const char*> driverVaryings;
|
||||
driverVaryings.reserve(scopedMappedTFVaryings.size());
|
||||
for (const auto& cur : scopedMappedTFVaryings) {
|
||||
driverVaryings.push_back(cur.c_str());
|
||||
}
|
||||
|
||||
mContext->gl->fTransformFeedbackVaryings(mGLName, driverVaryings.size(),
|
||||
driverVaryings.data(),
|
||||
mNextLink_TransformFeedbackBufferMode);
|
||||
}
|
||||
|
||||
LinkAndUpdate();
|
||||
|
@ -1080,10 +1129,63 @@ NumUsedLocationsByElemType(GLenum elemType)
|
|||
}
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
NumComponents(GLenum elemType)
|
||||
{
|
||||
switch (elemType) {
|
||||
case LOCAL_GL_FLOAT:
|
||||
case LOCAL_GL_INT:
|
||||
case LOCAL_GL_UNSIGNED_INT:
|
||||
case LOCAL_GL_BOOL:
|
||||
return 1;
|
||||
|
||||
case LOCAL_GL_FLOAT_VEC2:
|
||||
case LOCAL_GL_INT_VEC2:
|
||||
case LOCAL_GL_UNSIGNED_INT_VEC2:
|
||||
case LOCAL_GL_BOOL_VEC2:
|
||||
return 2;
|
||||
|
||||
case LOCAL_GL_FLOAT_VEC3:
|
||||
case LOCAL_GL_INT_VEC3:
|
||||
case LOCAL_GL_UNSIGNED_INT_VEC3:
|
||||
case LOCAL_GL_BOOL_VEC3:
|
||||
return 3;
|
||||
|
||||
case LOCAL_GL_FLOAT_VEC4:
|
||||
case LOCAL_GL_INT_VEC4:
|
||||
case LOCAL_GL_UNSIGNED_INT_VEC4:
|
||||
case LOCAL_GL_BOOL_VEC4:
|
||||
case LOCAL_GL_FLOAT_MAT2:
|
||||
return 4;
|
||||
|
||||
case LOCAL_GL_FLOAT_MAT2x3:
|
||||
case LOCAL_GL_FLOAT_MAT3x2:
|
||||
return 6;
|
||||
|
||||
case LOCAL_GL_FLOAT_MAT2x4:
|
||||
case LOCAL_GL_FLOAT_MAT4x2:
|
||||
return 8;
|
||||
|
||||
case LOCAL_GL_FLOAT_MAT3:
|
||||
return 9;
|
||||
|
||||
case LOCAL_GL_FLOAT_MAT3x4:
|
||||
case LOCAL_GL_FLOAT_MAT4x3:
|
||||
return 12;
|
||||
|
||||
case LOCAL_GL_FLOAT_MAT4:
|
||||
return 16;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("`elemType`");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLProgram::ValidateAfterTentativeLink(nsCString* const out_linkLog) const
|
||||
{
|
||||
const auto& linkInfo = mMostRecentLinkInfo;
|
||||
const auto& gl = mContext->gl;
|
||||
|
||||
// Check if the attrib name conflicting to uniform name
|
||||
for (const auto& attrib : linkInfo->attribs) {
|
||||
|
@ -1123,15 +1225,107 @@ WebGLProgram::ValidateAfterTentativeLink(nsCString* const out_linkLog) const
|
|||
}
|
||||
}
|
||||
|
||||
// Forbid:
|
||||
// * Unrecognized varying name
|
||||
// * Duplicate varying name
|
||||
// * Too many components for specified buffer mode
|
||||
if (mNextLink_TransformFeedbackVaryings.size()) {
|
||||
GLuint maxComponentsPerIndex = 0;
|
||||
switch (mNextLink_TransformFeedbackBufferMode) {
|
||||
case LOCAL_GL_INTERLEAVED_ATTRIBS:
|
||||
gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,
|
||||
&maxComponentsPerIndex);
|
||||
break;
|
||||
|
||||
case LOCAL_GL_SEPARATE_ATTRIBS:
|
||||
gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,
|
||||
&maxComponentsPerIndex);
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("`bufferMode`");
|
||||
}
|
||||
|
||||
std::vector<size_t> componentsPerVert;
|
||||
std::set<const WebGLActiveInfo*> alreadyUsed;
|
||||
for (const auto& wideUserName : mNextLink_TransformFeedbackVaryings) {
|
||||
if (!componentsPerVert.size() ||
|
||||
mNextLink_TransformFeedbackBufferMode == LOCAL_GL_SEPARATE_ATTRIBS)
|
||||
{
|
||||
componentsPerVert.push_back(0);
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
const WebGLActiveInfo* curInfo = nullptr;
|
||||
for (const auto& info : linkInfo->transformFeedbackVaryings) {
|
||||
const NS_ConvertASCIItoUTF16 info_wideUserName(info->mBaseUserName);
|
||||
if (info_wideUserName == wideUserName) {
|
||||
curInfo = info.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!curInfo) {
|
||||
const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
|
||||
*out_linkLog = nsPrintfCString("Transform feedback varying \"%s\" not"
|
||||
" found.",
|
||||
asciiUserName.BeginReading());
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto insertResPair = alreadyUsed.insert(curInfo);
|
||||
const auto& didInsert = insertResPair.second;
|
||||
if (!didInsert) {
|
||||
const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
|
||||
*out_linkLog = nsPrintfCString("Transform feedback varying \"%s\""
|
||||
" specified twice.",
|
||||
asciiUserName.BeginReading());
|
||||
return false;
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
size_t varyingComponents = NumComponents(curInfo->mElemType);
|
||||
varyingComponents *= curInfo->mElemCount;
|
||||
|
||||
auto& totalComponentsForIndex = *(componentsPerVert.rbegin());
|
||||
totalComponentsForIndex += varyingComponents;
|
||||
|
||||
if (totalComponentsForIndex > maxComponentsPerIndex) {
|
||||
const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
|
||||
*out_linkLog = nsPrintfCString("Transform feedback varying \"%s\""
|
||||
" pushed `componentsForIndex` over the"
|
||||
" limit of %u.",
|
||||
asciiUserName.BeginReading(),
|
||||
maxComponentsPerIndex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
linkInfo->componentsPerTFVert.swap(componentsPerVert);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLProgram::UseProgram() const
|
||||
{
|
||||
const char funcName[] = "useProgram";
|
||||
|
||||
if (!mMostRecentLinkInfo) {
|
||||
mContext->ErrorInvalidOperation("useProgram: Program has not been successfully"
|
||||
" linked.");
|
||||
mContext->ErrorInvalidOperation("%s: Program has not been successfully linked.",
|
||||
funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mContext->mBoundTransformFeedback &&
|
||||
mContext->mBoundTransformFeedback->mIsActive &&
|
||||
!mContext->mBoundTransformFeedback->mIsPaused)
|
||||
{
|
||||
mContext->ErrorInvalidOperation("%s: Transform feedback active and not paused.",
|
||||
funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1183,11 +1377,6 @@ WebGLProgram::LinkAndUpdate()
|
|||
mLinkLog.SetLength(0);
|
||||
}
|
||||
|
||||
// Post link, temporary mapped varying names for transform feedback can be discarded.
|
||||
// The memory can only be deleted after log is queried or the link status will fail.
|
||||
std::vector<std::string> empty;
|
||||
empty.swap(mTempMappedVaryings);
|
||||
|
||||
GLint ok = 0;
|
||||
gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
|
||||
if (!ok)
|
||||
|
@ -1237,42 +1426,43 @@ void
|
|||
WebGLProgram::TransformFeedbackVaryings(const dom::Sequence<nsString>& varyings,
|
||||
GLenum bufferMode)
|
||||
{
|
||||
if (bufferMode != LOCAL_GL_INTERLEAVED_ATTRIBS &&
|
||||
bufferMode != LOCAL_GL_SEPARATE_ATTRIBS)
|
||||
{
|
||||
mContext->ErrorInvalidEnum("transformFeedbackVaryings: `bufferMode` %s is "
|
||||
"invalid. Must be one of gl.INTERLEAVED_ATTRIBS or "
|
||||
"gl.SEPARATE_ATTRIBS.",
|
||||
mContext->EnumName(bufferMode));
|
||||
const char funcName[] = "transformFeedbackVaryings";
|
||||
|
||||
const auto& gl = mContext->gl;
|
||||
gl->MakeCurrent();
|
||||
|
||||
switch (bufferMode) {
|
||||
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) {
|
||||
mContext->ErrorInvalidValue("%s: Length of `varyings` exceeds %s.",
|
||||
funcName,
|
||||
"TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
mContext->ErrorInvalidEnum("%s: Bad `bufferMode`: 0x%04x.", funcName, bufferMode);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t varyingsCount = varyings.Length();
|
||||
if (bufferMode == LOCAL_GL_SEPARATE_ATTRIBS &&
|
||||
varyingsCount >= mContext->mGLMaxTransformFeedbackSeparateAttribs)
|
||||
{
|
||||
mContext->ErrorInvalidValue("transformFeedbackVaryings: Number of `varyings` exc"
|
||||
"eeds gl.MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS.");
|
||||
return;
|
||||
}
|
||||
////
|
||||
|
||||
std::vector<nsCString> asciiVaryings;
|
||||
for (size_t i = 0; i < varyingsCount; i++) {
|
||||
if (!ValidateGLSLVariableName(varyings[i], mContext, "transformFeedbackVaryings"))
|
||||
return;
|
||||
|
||||
NS_LossyConvertUTF16toASCII asciiName(varyings[i]);
|
||||
asciiVaryings.push_back(asciiName);
|
||||
}
|
||||
|
||||
// All validated. Translate the strings and store them until
|
||||
// program linking.
|
||||
mTransformFeedbackBufferMode = bufferMode;
|
||||
mTransformFeedbackVaryings.swap(asciiVaryings);
|
||||
mNextLink_TransformFeedbackVaryings.assign(varyings.Elements(),
|
||||
varyings.Elements() + varyings.Length());
|
||||
mNextLink_TransformFeedbackBufferMode = bufferMode;
|
||||
}
|
||||
|
||||
already_AddRefed<WebGLActiveInfo>
|
||||
WebGLProgram::GetTransformFeedbackVarying(GLuint index)
|
||||
WebGLProgram::GetTransformFeedbackVarying(GLuint index) const
|
||||
{
|
||||
// No docs in the WebGL 2 spec for this function. Taking the language for
|
||||
// getActiveAttrib, which states that the function returns null on any error.
|
||||
|
|
|
@ -61,18 +61,25 @@ struct UniformBlockInfo final
|
|||
{
|
||||
const nsCString mBaseUserName;
|
||||
const nsCString mBaseMappedName;
|
||||
const uint32_t mDataSize;
|
||||
|
||||
UniformBlockInfo(const nsACString& baseUserName,
|
||||
const nsACString& baseMappedName)
|
||||
const IndexedBufferBinding* mBinding;
|
||||
|
||||
UniformBlockInfo(WebGLContext* webgl, const nsACString& baseUserName,
|
||||
const nsACString& baseMappedName, uint32_t dataSize)
|
||||
: mBaseUserName(baseUserName)
|
||||
, mBaseMappedName(baseMappedName)
|
||||
{}
|
||||
, mDataSize(dataSize)
|
||||
, mBinding(&webgl->mIndexedUniformBufferBindings[0])
|
||||
{ }
|
||||
};
|
||||
|
||||
struct LinkedProgramInfo final
|
||||
: public RefCounted<LinkedProgramInfo>
|
||||
, public SupportsWeakPtr<LinkedProgramInfo>
|
||||
{
|
||||
friend class WebGLProgram;
|
||||
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo)
|
||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(LinkedProgramInfo)
|
||||
|
||||
|
@ -82,12 +89,14 @@ struct LinkedProgramInfo final
|
|||
|
||||
std::vector<AttribInfo> attribs;
|
||||
std::vector<UniformInfo*> uniforms; // Owns its contents.
|
||||
std::vector<const UniformBlockInfo*> uniformBlocks; // Owns its contents.
|
||||
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;
|
||||
|
||||
//////
|
||||
|
||||
// The maps for the frag data names to the translated names.
|
||||
|
@ -123,6 +132,8 @@ class WebGLProgram final
|
|||
, public LinkedListElement<WebGLProgram>
|
||||
, public WebGLContextBoundObject
|
||||
{
|
||||
friend class WebGLTransformFeedback;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLProgram)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLProgram)
|
||||
|
@ -174,7 +185,7 @@ public:
|
|||
|
||||
void TransformFeedbackVaryings(const dom::Sequence<nsString>& varyings,
|
||||
GLenum bufferMode);
|
||||
already_AddRefed<WebGLActiveInfo> GetTransformFeedbackVarying(GLuint index);
|
||||
already_AddRefed<WebGLActiveInfo> GetTransformFeedbackVarying(GLuint index) const;
|
||||
|
||||
void EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const;
|
||||
|
||||
|
@ -194,6 +205,7 @@ private:
|
|||
~WebGLProgram();
|
||||
|
||||
void LinkAndUpdate();
|
||||
bool ValidateForLink();
|
||||
bool ValidateAfterTentativeLink(nsCString* const out_linkLog) const;
|
||||
|
||||
public:
|
||||
|
@ -202,14 +214,15 @@ public:
|
|||
private:
|
||||
WebGLRefPtr<WebGLShader> mVertShader;
|
||||
WebGLRefPtr<WebGLShader> mFragShader;
|
||||
std::map<nsCString, GLuint> mBoundAttribLocs;
|
||||
std::vector<nsCString> mTransformFeedbackVaryings;
|
||||
GLenum mTransformFeedbackBufferMode;
|
||||
size_t mNumActiveTFOs;
|
||||
|
||||
std::map<nsCString, GLuint> mNextLink_BoundAttribLocs;
|
||||
|
||||
std::vector<nsString> mNextLink_TransformFeedbackVaryings;
|
||||
GLenum mNextLink_TransformFeedbackBufferMode;
|
||||
|
||||
nsCString mLinkLog;
|
||||
RefPtr<const webgl::LinkedProgramInfo> mMostRecentLinkInfo;
|
||||
// Storage for transform feedback varyings before link.
|
||||
// (Work around for bug seen on nVidia drivers.)
|
||||
std::vector<std::string> mTempMappedVaryings;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -408,40 +408,24 @@ WebGLShader::EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_Frag
|
|||
}
|
||||
|
||||
void
|
||||
WebGLShader::ApplyTransformFeedbackVaryings(GLuint prog,
|
||||
const std::vector<nsCString>& varyings,
|
||||
GLenum bufferMode,
|
||||
std::vector<std::string>* out_mappedVaryings) const
|
||||
WebGLShader::MapTransformFeedbackVaryings(const std::vector<nsString>& varyings,
|
||||
std::vector<std::string>* out_mappedVaryings) const
|
||||
{
|
||||
MOZ_ASSERT(mType == LOCAL_GL_VERTEX_SHADER);
|
||||
MOZ_ASSERT(!varyings.empty());
|
||||
MOZ_ASSERT(out_mappedVaryings);
|
||||
|
||||
const size_t varyingsCount = varyings.size();
|
||||
std::vector<std::string> mappedVaryings;
|
||||
out_mappedVaryings->clear();
|
||||
out_mappedVaryings->reserve(varyings.size());
|
||||
|
||||
for (size_t i = 0; i < varyingsCount; i++) {
|
||||
const nsCString& userName = varyings[i];
|
||||
std::string userNameStr(userName.BeginReading());
|
||||
|
||||
const std::string* mappedNameStr = &userNameStr;
|
||||
if (mValidator)
|
||||
mValidator->FindVaryingMappedNameByUserName(userNameStr, &mappedNameStr);
|
||||
|
||||
mappedVaryings.push_back(*mappedNameStr);
|
||||
for (const auto& wideUserName : varyings) {
|
||||
const NS_LossyConvertUTF16toASCII mozUserName(wideUserName); // Don't validate here.
|
||||
const std::string userName(mozUserName.BeginReading(), mozUserName.Length());
|
||||
const std::string* pMappedName = &userName;
|
||||
if (mValidator) {
|
||||
mValidator->FindVaryingMappedNameByUserName(userName, &pMappedName);
|
||||
}
|
||||
out_mappedVaryings->push_back(*pMappedName);
|
||||
}
|
||||
|
||||
// Temporary, tight packed array of string pointers into mappedVaryings.
|
||||
std::vector<const GLchar*> strings;
|
||||
strings.resize(varyingsCount);
|
||||
for (size_t i = 0; i < varyingsCount; i++) {
|
||||
strings[i] = mappedVaryings[i].c_str();
|
||||
}
|
||||
|
||||
mContext->MakeContextCurrent();
|
||||
mContext->gl->fTransformFeedbackVaryings(prog, varyingsCount, &strings[0], bufferMode);
|
||||
|
||||
out_mappedVaryings->swap(mappedVaryings);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -52,7 +52,6 @@ public:
|
|||
bool CanLinkTo(const WebGLShader* prev, nsCString* const out_log) const;
|
||||
size_t CalcNumSamplerUniforms() const;
|
||||
size_t NumAttributes() const;
|
||||
void BindAttribLocation(GLuint prog, const nsCString& userName, GLuint index) const;
|
||||
bool FindAttribUserNameByMappedName(const nsACString& mappedName,
|
||||
nsDependentCString* const out_userName) const;
|
||||
bool FindVaryingByMappedName(const nsACString& mappedName,
|
||||
|
@ -71,11 +70,12 @@ public:
|
|||
return mTranslationSuccessful && mCompilationSuccessful;
|
||||
}
|
||||
|
||||
void ApplyTransformFeedbackVaryings(GLuint prog,
|
||||
const std::vector<nsCString>& varyings,
|
||||
GLenum bufferMode,
|
||||
std::vector<std::string>* out_mappedVaryings) const;
|
||||
private:
|
||||
void BindAttribLocation(GLuint prog, const nsCString& userName, GLuint index) const;
|
||||
void MapTransformFeedbackVaryings(const std::vector<nsString>& varyings,
|
||||
std::vector<std::string>* out_mappedVaryings) const;
|
||||
|
||||
public:
|
||||
// Other funcs
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
void Delete();
|
||||
|
|
|
@ -64,6 +64,10 @@ public:
|
|||
std::string* const out_userName) const;
|
||||
|
||||
void EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const;
|
||||
|
||||
bool ValidateTransformFeedback(const std::vector<nsString>& userNames,
|
||||
uint32_t maxComponents, nsCString* const out_errorText,
|
||||
std::vector<std::string>* const out_mappedNames);
|
||||
};
|
||||
|
||||
} // namespace webgl
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "mozilla/Unused.h"
|
||||
#include "ScopedGLHelpers.h"
|
||||
#include "TexUnpackBlob.h"
|
||||
#include "WebGLBuffer.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "WebGLContextUtils.h"
|
||||
#include "WebGLFramebuffer.h"
|
||||
|
@ -213,6 +214,15 @@ WebGLContext::ValidateUnpackInfo(const char* funcName, bool usePBOs, GLenum form
|
|||
return false;
|
||||
}
|
||||
|
||||
if (mBoundPixelUnpackBuffer &&
|
||||
mBoundPixelUnpackBuffer->mNumActiveTFOs)
|
||||
{
|
||||
ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
|
||||
" object.",
|
||||
funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mFormatUsage->AreUnpackEnumsValid(format, type)) {
|
||||
ErrorInvalidEnum("%s: Invalid unpack format/type: 0x%04x/0x%04x", funcName,
|
||||
format, type);
|
||||
|
@ -315,7 +325,7 @@ WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarge
|
|||
const auto bufferByteCount = packBuffer->ByteLength();
|
||||
|
||||
uint32_t byteCount = 0;
|
||||
if (bufferByteCount >= offset) {
|
||||
if (bufferByteCount >= uint64_t(offset)) {
|
||||
byteCount = bufferByteCount - offset;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,48 +11,206 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
WebGLTransformFeedback::WebGLTransformFeedback(WebGLContext* webgl,
|
||||
GLuint tf)
|
||||
WebGLTransformFeedback::WebGLTransformFeedback(WebGLContext* webgl, GLuint tf)
|
||||
: WebGLContextBoundObject(webgl)
|
||||
, mGLName(tf)
|
||||
, mMode(LOCAL_GL_NONE)
|
||||
, mIsActive(false)
|
||||
, mIndexedBindings(webgl->mGLMaxTransformFeedbackSeparateAttribs)
|
||||
, mIsPaused(false)
|
||||
, mIsActive(false)
|
||||
{
|
||||
mContext->mTransformFeedbacks.insertBack(this);
|
||||
}
|
||||
|
||||
WebGLTransformFeedback::~WebGLTransformFeedback()
|
||||
{
|
||||
mMode = LOCAL_GL_NONE;
|
||||
mIsActive = false;
|
||||
mIsPaused = false;
|
||||
DeleteOnce();
|
||||
}
|
||||
|
||||
void
|
||||
WebGLTransformFeedback::Delete()
|
||||
{
|
||||
mContext->MakeContextCurrent();
|
||||
mContext->gl->fDeleteTransformFeedbacks(1, &mGLName);
|
||||
if (mGLName) {
|
||||
mContext->MakeContextCurrent();
|
||||
mContext->gl->fDeleteTransformFeedbacks(1, &mGLName);
|
||||
}
|
||||
removeFrom(mContext->mTransformFeedbacks);
|
||||
}
|
||||
|
||||
WebGLContext*
|
||||
WebGLTransformFeedback::GetParentObject() const
|
||||
////////////////////////////////////////
|
||||
|
||||
void
|
||||
WebGLTransformFeedback::BeginTransformFeedback(GLenum primMode)
|
||||
{
|
||||
return mContext;
|
||||
const char funcName[] = "beginTransformFeedback";
|
||||
|
||||
if (mIsActive)
|
||||
return mContext->ErrorInvalidOperation("%s: Already active.", funcName);
|
||||
|
||||
switch (primMode) {
|
||||
case LOCAL_GL_POINTS:
|
||||
case LOCAL_GL_LINES:
|
||||
case LOCAL_GL_TRIANGLES:
|
||||
break;
|
||||
default:
|
||||
mContext->ErrorInvalidEnum("%s: `primitiveMode` must be one of POINTS, LINES, or"
|
||||
" TRIANGLES.",
|
||||
funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& prog = mContext->mCurrentProgram;
|
||||
if (!prog ||
|
||||
!prog->IsLinked() ||
|
||||
!prog->LinkInfo()->componentsPerTFVert.size())
|
||||
{
|
||||
mContext->ErrorInvalidOperation("%s: Current program not valid for transform"
|
||||
" feedback.",
|
||||
funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& linkInfo = prog->LinkInfo();
|
||||
const auto& componentsPerTFVert = linkInfo->componentsPerTFVert;
|
||||
|
||||
size_t minVertCapacity = SIZE_MAX;
|
||||
for (size_t i = 0; i < componentsPerTFVert.size(); i++) {
|
||||
const auto& indexedBinding = mIndexedBindings[i];
|
||||
const auto& componentsPerVert = componentsPerTFVert[i];
|
||||
|
||||
const auto& buffer = indexedBinding.mBufferBinding;
|
||||
if (!buffer) {
|
||||
mContext->ErrorInvalidOperation("%s: No buffer attached to required transform"
|
||||
" feedback index %u.",
|
||||
funcName, (uint32_t)i);
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t vertCapacity = buffer->ByteLength() / 4 / componentsPerVert;
|
||||
minVertCapacity = std::min(minVertCapacity, vertCapacity);
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
const auto& gl = mContext->gl;
|
||||
gl->MakeCurrent();
|
||||
gl->fBeginTransformFeedback(primMode);
|
||||
|
||||
////
|
||||
|
||||
mIsActive = true;
|
||||
MOZ_ASSERT(!mIsPaused);
|
||||
|
||||
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";
|
||||
|
||||
if (!mIsActive)
|
||||
return mContext->ErrorInvalidOperation("%s: Not active.", funcName);
|
||||
|
||||
////
|
||||
|
||||
const auto& gl = mContext->gl;
|
||||
gl->MakeCurrent();
|
||||
gl->fEndTransformFeedback();
|
||||
|
||||
////
|
||||
|
||||
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";
|
||||
|
||||
if (!mIsActive ||
|
||||
mIsPaused)
|
||||
{
|
||||
mContext->ErrorInvalidOperation("%s: Not active or is paused.", funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
const auto& gl = mContext->gl;
|
||||
gl->MakeCurrent();
|
||||
gl->fPauseTransformFeedback();
|
||||
|
||||
////
|
||||
|
||||
mIsPaused = true;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLTransformFeedback::ResumeTransformFeedback()
|
||||
{
|
||||
const char funcName[] = "resumeTransformFeedback";
|
||||
|
||||
if (!mIsPaused)
|
||||
return mContext->ErrorInvalidOperation("%s: Not paused.", funcName);
|
||||
|
||||
if (mContext->mCurrentProgram != mActive_Program) {
|
||||
mContext->ErrorInvalidOperation("%s: Active program differs from original.",
|
||||
funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
const auto& gl = mContext->gl;
|
||||
gl->MakeCurrent();
|
||||
gl->fResumeTransformFeedback();
|
||||
|
||||
////
|
||||
|
||||
MOZ_ASSERT(mIsActive);
|
||||
mIsPaused = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
JSObject*
|
||||
WebGLTransformFeedback::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
|
||||
{
|
||||
return dom::WebGLTransformFeedbackBinding::Wrap(cx, this, givenProto);
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTransformFeedback)
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTransformFeedback, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTransformFeedback, Release)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLTransformFeedback,
|
||||
mGenericBufferBinding,
|
||||
mIndexedBindings,
|
||||
mActive_Program)
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -18,27 +18,43 @@ class WebGLTransformFeedback final
|
|||
, public LinkedListElement<WebGLTransformFeedback>
|
||||
, public WebGLContextBoundObject
|
||||
{
|
||||
friend class ScopedDrawWithTransformFeedback;
|
||||
friend class WebGLContext;
|
||||
friend class WebGL2Context;
|
||||
friend class WebGLProgram;
|
||||
|
||||
public:
|
||||
const GLuint mGLName;
|
||||
private:
|
||||
// GLES 3.0.4 p267, Table 6.24 "Transform Feedback State"
|
||||
WebGLRefPtr<WebGLBuffer> mGenericBufferBinding;
|
||||
std::vector<IndexedBufferBinding> mIndexedBindings;
|
||||
bool mIsPaused;
|
||||
bool mIsActive;
|
||||
// Not in state tables:
|
||||
WebGLRefPtr<WebGLProgram> mActive_Program;
|
||||
GLenum mActive_PrimMode;
|
||||
size_t mActive_VertPosition;
|
||||
size_t mActive_VertCapacity;
|
||||
|
||||
public:
|
||||
WebGLTransformFeedback(WebGLContext* webgl, GLuint tf);
|
||||
|
||||
void Delete();
|
||||
WebGLContext* GetParentObject() const;
|
||||
virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
|
||||
|
||||
const GLuint mGLName;
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTransformFeedback)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTransformFeedback)
|
||||
|
||||
private:
|
||||
~WebGLTransformFeedback();
|
||||
|
||||
GLenum mMode;
|
||||
bool mIsActive;
|
||||
bool mIsPaused;
|
||||
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;
|
||||
|
||||
// GL Funcs
|
||||
void BeginTransformFeedback(GLenum primMode);
|
||||
void EndTransformFeedback();
|
||||
void PauseTransformFeedback();
|
||||
void ResumeTransformFeedback();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -41,26 +41,19 @@ struct WebGLVertexAttribData
|
|||
GLuint componentSize() const {
|
||||
switch(type) {
|
||||
case LOCAL_GL_BYTE:
|
||||
return sizeof(GLbyte);
|
||||
|
||||
case LOCAL_GL_UNSIGNED_BYTE:
|
||||
return sizeof(GLubyte);
|
||||
return 1;
|
||||
|
||||
case LOCAL_GL_SHORT:
|
||||
return sizeof(GLshort);
|
||||
|
||||
case LOCAL_GL_UNSIGNED_SHORT:
|
||||
return sizeof(GLushort);
|
||||
case LOCAL_GL_HALF_FLOAT:
|
||||
case LOCAL_GL_HALF_FLOAT_OES:
|
||||
return 2;
|
||||
|
||||
case LOCAL_GL_INT:
|
||||
return sizeof(GLint);
|
||||
|
||||
case LOCAL_GL_UNSIGNED_INT:
|
||||
return sizeof(GLuint);
|
||||
|
||||
// case LOCAL_GL_FIXED:
|
||||
case LOCAL_GL_FLOAT:
|
||||
return sizeof(GLfloat);
|
||||
return 4;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT(false, "Should never get here!");
|
||||
|
|
|
@ -66,14 +66,8 @@ void
|
|||
CheckValidate(bool expectSuccess, mozilla::WebGLElementArrayCache& c, GLenum type,
|
||||
uint32_t maxAllowed, size_t first, size_t count)
|
||||
{
|
||||
uint32_t out_upperBound = 0;
|
||||
const bool success = c.Validate(type, maxAllowed, first, count, &out_upperBound);
|
||||
const bool success = c.Validate(type, maxAllowed, first, count);
|
||||
VERIFY(success == expectSuccess);
|
||||
if (success) {
|
||||
VERIFY(out_upperBound <= maxAllowed);
|
||||
} else {
|
||||
VERIFY(out_upperBound > maxAllowed);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -71,7 +71,6 @@ SOURCES += [
|
|||
UNIFIED_SOURCES += [
|
||||
'TexUnpackBlob.cpp',
|
||||
'WebGL1Context.cpp',
|
||||
'WebGL1ContextBuffers.cpp',
|
||||
'WebGL1ContextUniforms.cpp',
|
||||
'WebGL2Context.cpp',
|
||||
'WebGL2ContextBuffers.cpp',
|
||||
|
|
Загрузка…
Ссылка в новой задаче