Bug 1167504 - Part 11: Clean up buffer binding constraints. r=jgilbert

Checked against http://www.khronos.org/registry/webgl/sdk/tests/conformance2/buffers/
This commit is contained in:
Dan Glastonbury 2015-05-21 09:59:14 +10:00
parent 9ab81fee0a
commit 5b282e5258
11 changed files with 196 additions and 105 deletions

View File

@ -33,7 +33,6 @@ private:
virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* 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 ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, 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;

View File

@ -35,22 +35,6 @@ WebGL1Context::ValidateBufferIndexedTarget(GLenum target, const char* info)
return false;
}
/** Buffer and Target validation for BindBuffer */
bool
WebGL1Context::ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer,
const char* info)
{
if (!buffer)
return true;
if (buffer->HasEverBeenBound() && target != buffer->Target()) {
ErrorInvalidOperation("%s: buffer already bound to a different target", info);
return false;
}
return true;
}
bool
WebGL1Context::ValidateBufferUsageEnum(GLenum usage, const char* info)
{

View File

@ -370,7 +370,6 @@ private:
virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* 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 ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, 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;

View File

@ -45,36 +45,6 @@ WebGL2Context::ValidateBufferIndexedTarget(GLenum target, const char* info)
}
}
bool
WebGL2Context::ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer,
const char* info)
{
if (!buffer)
return true;
switch (target) {
case LOCAL_GL_COPY_READ_BUFFER:
case LOCAL_GL_COPY_WRITE_BUFFER:
return true;
case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
return !buffer->HasEverBeenBound() ||
buffer->Target() == LOCAL_GL_ELEMENT_ARRAY_BUFFER;
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:
return !buffer->HasEverBeenBound() ||
buffer->Target() != LOCAL_GL_ELEMENT_ARRAY_BUFFER;
}
ErrorInvalidOperation("%s: buffer already bound to a incompatible target %s",
info, EnumName(buffer->Target()));
return false;
}
bool
WebGL2Context::ValidateBufferUsageEnum(GLenum usage, const char* info)
{
@ -123,7 +93,7 @@ WebGL2Context::CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
if (!readBuffer)
return ErrorInvalidOperation("copyBufferSubData: No buffer bound to readTarget");
const WebGLBuffer* writeBuffer = writeBufferSlot.get();
WebGLBuffer* writeBuffer = writeBufferSlot.get();
if (!writeBuffer)
return ErrorInvalidOperation("copyBufferSubData: No buffer bound to writeTarget");
@ -145,8 +115,27 @@ WebGL2Context::CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
return;
}
WebGLBuffer::Kind readType = readBuffer->Content();
WebGLBuffer::Kind writeType = writeBuffer->Content();
if (readType != WebGLBuffer::Kind::Undefined &&
writeType != WebGLBuffer::Kind::Undefined &&
writeType != readType)
{
ErrorInvalidOperation("copyBufferSubData: Can't copy %s data to %s data",
(readType == WebGLBuffer::Kind::OtherData) ? "other" : "element",
(writeType == WebGLBuffer::Kind::OtherData) ? "other" : "element");
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);
}
}
void

View File

@ -15,7 +15,7 @@ namespace mozilla {
WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
: WebGLContextBoundObject(webgl)
, mGLName(buf)
, mTarget(LOCAL_GL_NONE)
, mContent(Kind::Undefined)
, mByteLength(0)
{
mContext->mBuffers.insertBack(this);
@ -26,6 +26,34 @@ WebGLBuffer::~WebGLBuffer()
DeleteOnce();
}
void
WebGLBuffer::BindTo(GLenum target)
{
switch (target) {
case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
mContent = Kind::ElementArray;
if (!mCache)
mCache = new WebGLElementArrayCache;
break;
case LOCAL_GL_ARRAY_BUFFER:
case LOCAL_GL_PIXEL_PACK_BUFFER:
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:
/* Do nothing. Doesn't set the type of the buffer contents. */
break;
default:
MOZ_CRASH();
}
}
void
WebGLBuffer::Delete()
{
@ -36,30 +64,11 @@ WebGLBuffer::Delete()
LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
}
void
WebGLBuffer::BindTo(GLenum target)
{
MOZ_ASSERT(target != LOCAL_GL_NONE, "Can't bind to GL_NONE.");
MOZ_ASSERT(!HasEverBeenBound() || mTarget == target, "Rebinding is illegal.");
bool targetChanged = (target != mTarget);
mTarget = target;
if (targetChanged)
OnTargetChanged();
}
void
WebGLBuffer::OnTargetChanged()
{
if (!mCache && mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
mCache = new WebGLElementArrayCache;
}
bool
WebGLBuffer::ElementArrayCacheBufferData(const void* ptr,
size_t bufferSizeInBytes)
{
if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
if (mContent == Kind::ElementArray)
return mCache->BufferData(ptr, bufferSizeInBytes);
return true;
@ -69,7 +78,7 @@ void
WebGLBuffer::ElementArrayCacheBufferSubData(size_t pos, const void* ptr,
size_t updateSizeInBytes)
{
if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
if (mContent == Kind::ElementArray)
mCache->BufferSubData(pos, ptr, updateSizeInBytes);
}

View File

@ -25,8 +25,18 @@ class WebGLBuffer final
, public WebGLContextBoundObject
{
public:
enum class Kind {
Undefined,
ElementArray,
OtherData
};
explicit WebGLBuffer(WebGLContext* webgl, GLuint buf);
void BindTo(GLenum target);
Kind Content() const { return mContent; }
void Delete();
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
@ -39,10 +49,6 @@ public:
void ElementArrayCacheBufferSubData(size_t pos, const void* ptr,
size_t updateSizeInBytes);
void BindTo(GLenum target);
bool HasEverBeenBound() const { return mTarget != LOCAL_GL_NONE; }
GLenum Target() const { return mTarget; }
bool Validate(GLenum type, uint32_t max_allowed, size_t first, size_t count,
uint32_t* const out_upperBound);
@ -62,10 +68,7 @@ public:
protected:
~WebGLBuffer();
void OnTargetChanged();
GLenum mTarget;
Kind mContent;
WebGLsizeiptr mByteLength;
nsAutoPtr<WebGLElementArrayCache> mCache;
};

View File

@ -939,6 +939,8 @@ protected:
WebGLRefPtr<WebGLBuffer>& GetBufferSlotByTargetIndexed(GLenum target,
GLuint index);
GLenum GetCurrentBinding(WebGLBuffer* buffer) const;
// -----------------------------------------------------------------------------
// Queries (WebGL2ContextQueries.cpp)
protected:
@ -1395,7 +1397,7 @@ private:
virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* 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) = 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;

View File

@ -20,24 +20,7 @@ WebGLContext::UpdateBoundBuffer(GLenum target, WebGLBuffer* buffer)
if (!buffer)
return;
/* 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.
*/
if (target != LOCAL_GL_COPY_READ_BUFFER && target != LOCAL_GL_COPY_WRITE_BUFFER)
buffer->BindTo(target);
buffer->BindTo(target);
}
void
@ -435,9 +418,89 @@ WebGLContext::IsBuffer(WebGLBuffer* buffer)
if (IsContextLost())
return false;
return ValidateObjectAllowDeleted("isBuffer", buffer) &&
!buffer->IsDeleted() &&
buffer->HasEverBeenBound();
if (!ValidateObjectAllowDeleted("isBuffer", buffer))
return false;
if (buffer->IsDeleted())
return false;
MakeContextCurrent();
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.
*/
GLenum boundTo = GetCurrentBinding(buffer);
if (boundTo != LOCAL_GL_NONE) {
if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
boundTo != LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER)
{
ErrorInvalidOperation("Can't bind buffer to TRANSFORM_FEEDBACK_BUFFER as the "
"buffer is already bound to another bind point.");
return false;
}
else if (target != LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
boundTo == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER)
{
ErrorInvalidOperation("Can't bind buffer to bind point as it is currently "
"bound to TRANSFORM_FEEDBACK_BUFFER.");
return false;
}
}
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
@ -510,6 +573,37 @@ WebGLContext::GetBufferSlotByTargetIndexed(GLenum target, GLuint index)
}
}
GLenum
WebGLContext::GetCurrentBinding(WebGLBuffer* buffer) const
{
if (mBoundArrayBuffer == buffer)
return LOCAL_GL_ARRAY_BUFFER;
if (mBoundCopyReadBuffer == buffer)
return LOCAL_GL_COPY_READ_BUFFER;
if (mBoundCopyWriteBuffer == buffer)
return LOCAL_GL_COPY_WRITE_BUFFER;
if (mBoundPixelPackBuffer == buffer)
return LOCAL_GL_PIXEL_PACK_BUFFER;
if (mBoundPixelUnpackBuffer == buffer)
return LOCAL_GL_PIXEL_UNPACK_BUFFER;
if (mBoundTransformFeedbackBuffer == buffer ||
mBoundTransformFeedbackBuffers.Contains(buffer)) {
return LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER;
}
if (mBoundUniformBuffer == buffer ||
mBoundUniformBuffers.Contains(buffer)) {
return LOCAL_GL_UNIFORM_BUFFER;
}
return LOCAL_GL_NONE;
}
GLenum
WebGLContext::CheckedBufferData(GLenum target, GLsizeiptr size,
const GLvoid* data, GLenum usage)

View File

@ -126,8 +126,7 @@ WebGLMemoryTracker::GetBufferCacheMemoryUsed()
buffer;
buffer = buffer->getNext())
{
if (buffer->HasEverBeenBound() &&
buffer->Target() == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
if (buffer->Content() == WebGLBuffer::Kind::ElementArray) {
result += buffer->SizeOfIncludingThis(WebGLBufferMallocSizeOf);
}
}

View File

@ -38,6 +38,17 @@ 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);
}

View File

@ -266,8 +266,10 @@ WebGLContext::ValidateDataRanges(WebGLintptr readOffset, WebGLintptr writeOffset
MOZ_ASSERT((CheckedInt<WebGLsizeiptr>(writeOffset) + size).isValid());
bool separate = (readOffset + size < writeOffset || writeOffset + size < readOffset);
if (!separate)
ErrorInvalidValue("%s: ranges [readOffset, readOffset + size) and [writeOffset, writeOffset + size) overlap");
if (!separate) {
ErrorInvalidValue("%s: ranges [readOffset, readOffset + size) and [writeOffset, "
"writeOffset + size) overlap", info);
}
return separate;
}