Bug 1300946 - Implement transform feedback. - r=jrmuizel

MozReview-Commit-ID: 1xrEX4Srij1
This commit is contained in:
Jeff Gilbert 2016-09-09 21:02:54 -07:00 коммит произвёл Jeff Gilbert (:jgilbert)
Родитель 9f5c1ddc1d
Коммит 6b423ece44
32 изменённых файлов: 1509 добавлений и 1128 удалений

Просмотреть файл

@ -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',