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) // the 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 WebGLBuffer *boundBuffer = nullptr; if (target == LOCAL_GL_ARRAY_BUFFER) { boundBuffer = mBoundArrayBuffer; } else if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER) { boundBuffer = mBoundVertexArray->mBoundElementArrayBuffer; } NS_ABORT_IF_FALSE(boundBuffer != nullptr, "no buffer bound for this target"); bool sizeChanges = uint32_t(size) != boundBuffer->ByteLength(); if (sizeChanges) { UpdateWebGLErrorAndClearGLError(); gl->fBufferData(target, size, data, usage); GLenum error = LOCAL_GL_NO_ERROR; UpdateWebGLErrorAndClearGLError(&error); return error; } else { gl->fBufferData(target, size, data, usage); return LOCAL_GL_NO_ERROR; } }
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; } }
void WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset, const ArrayBufferView& data) { if (IsContextLost()) return; WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferSubData"); if (!bufferSlot) { return; } if (byteOffset < 0) return ErrorInvalidValue("bufferSubData: negative offset"); WebGLBuffer* boundBuffer = bufferSlot->get(); if (!boundBuffer) return ErrorInvalidOperation("bufferSubData: no buffer bound!"); CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length(); if (!checked_neededByteLength.isValid()) return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length"); if (checked_neededByteLength.value() > boundBuffer->ByteLength()) return ErrorInvalidValue("bufferSubData: not enough data -- operation requires %d bytes, but buffer only has %d bytes", checked_neededByteLength.value(), boundBuffer->ByteLength()); boundBuffer->ElementArrayCacheBufferSubData(byteOffset, data.Data(), data.Length()); MakeContextCurrent(); gl->fBufferSubData(target, byteOffset, data.Length(), data.Data()); }
void WebGLContext::AssertCachedBindings() { #ifdef DEBUG MakeContextCurrent(); GetAndFlushUnderlyingGLErrors(); if (IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) { GLuint bound = mBoundVertexArray ? mBoundVertexArray->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, bound); } // Bound object state if (IsWebGL2()) { GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, bound); bound = mBoundReadFramebuffer ? mBoundReadFramebuffer->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_READ_FRAMEBUFFER_BINDING, bound); } else { MOZ_ASSERT(mBoundDrawFramebuffer == mBoundReadFramebuffer); GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_FRAMEBUFFER_BINDING, bound); } GLuint bound = mCurrentProgram ? mCurrentProgram->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_CURRENT_PROGRAM, bound); // Textures GLenum activeTexture = mActiveTexture + LOCAL_GL_TEXTURE0; AssertUintParamCorrect(gl, LOCAL_GL_ACTIVE_TEXTURE, activeTexture); WebGLTexture* curTex = ActiveBoundTextureForTarget(LOCAL_GL_TEXTURE_2D); bound = curTex ? curTex->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_TEXTURE_BINDING_2D, bound); curTex = ActiveBoundTextureForTarget(LOCAL_GL_TEXTURE_CUBE_MAP); bound = curTex ? curTex->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_TEXTURE_BINDING_CUBE_MAP, bound); // Buffers bound = mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_ARRAY_BUFFER_BINDING, bound); MOZ_ASSERT(mBoundVertexArray); WebGLBuffer* curBuff = mBoundVertexArray->mElementArrayBuffer; bound = curBuff ? curBuff->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING, bound); MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors()); #endif }
void WebGLContext::BufferData(GLenum target, const Nullable<ArrayBuffer> &maybeData, GLenum usage) { if (IsContextLost()) return; if (maybeData.IsNull()) { // see http://www.khronos.org/bugzilla/show_bug.cgi?id=386 return ErrorInvalidValue("bufferData: null object passed"); } WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferData"); if (!bufferSlot) { return; } const ArrayBuffer& data = maybeData.Value(); data.ComputeLengthAndData(); // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr // is like intptr_t. if (!CheckedInt<GLsizeiptr>(data.Length()).isValid()) return ErrorOutOfMemory("bufferData: bad size"); if (!ValidateBufferUsageEnum(usage, "bufferData: usage")) return; WebGLBuffer* boundBuffer = bufferSlot->get(); if (!boundBuffer) return ErrorInvalidOperation("bufferData: no buffer bound!"); MakeContextCurrent(); InvalidateBufferFetching(); GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage); if (error) { GenerateWarning("bufferData generated error %s", ErrorName(error)); return; } boundBuffer->SetByteLength(data.Length()); if (!boundBuffer->ElementArrayCacheBufferData(data.Data(), data.Length())) { return ErrorOutOfMemory("bufferData: out of memory"); } }
void WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage) { if (IsContextLost()) return; WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferData"); if (!bufferSlot) { return; } if (size < 0) return ErrorInvalidValue("bufferData: negative size"); if (!ValidateBufferUsageEnum(usage, "bufferData: usage")) return; // careful: WebGLsizeiptr is always 64-bit, but GLsizeiptr is like intptr_t. if (!CheckedInt<GLsizeiptr>(size).isValid()) return ErrorOutOfMemory("bufferData: bad size"); WebGLBuffer* boundBuffer = bufferSlot->get(); if (!boundBuffer) return ErrorInvalidOperation("bufferData: no buffer bound!"); void* zeroBuffer = calloc(size, 1); if (!zeroBuffer) return ErrorOutOfMemory("bufferData: out of memory"); MakeContextCurrent(); InvalidateBufferFetching(); GLenum error = CheckedBufferData(target, size, zeroBuffer, usage); free(zeroBuffer); if (error) { GenerateWarning("bufferData generated error %s", ErrorName(error)); return; } boundBuffer->SetByteLength(size); if (!boundBuffer->ElementArrayCacheBufferData(nullptr, size)) { return ErrorOutOfMemory("bufferData: out of memory"); } }
void WebGLContext::BufferSubDataT(GLenum target, WebGLsizeiptr byteOffset, const BufferT& data) { if (IsContextLost()) return; if (!ValidateBufferTarget(target, "bufferSubData")) return; WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target); if (byteOffset < 0) return ErrorInvalidValue("bufferSubData: negative offset"); WebGLBuffer* boundBuffer = bufferSlot.get(); if (!boundBuffer) return ErrorInvalidOperation("bufferData: no buffer bound!"); data.ComputeLengthAndData(); CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length(); if (!checked_neededByteLength.isValid()) { ErrorInvalidValue("bufferSubData: Integer overflow computing the needed" " byte length."); return; } if (checked_neededByteLength.value() > boundBuffer->ByteLength()) { ErrorInvalidValue("bufferSubData: Not enough data. Operation requires" " %d bytes, but buffer only has %d bytes.", checked_neededByteLength.value(), boundBuffer->ByteLength()); return; } boundBuffer->ElementArrayCacheBufferSubData(byteOffset, data.Data(), data.Length()); MakeContextCurrent(); gl->fBufferSubData(target, byteOffset, data.Length(), data.Data()); }
void WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset, const Nullable<ArrayBuffer> &maybeData) { if (IsContextLost()) return; if (maybeData.IsNull()) { // see http://www.khronos.org/bugzilla/show_bug.cgi?id=386 return; } WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target, "bufferSubData"); if (!bufferSlot) { return; } if (byteOffset < 0) return ErrorInvalidValue("bufferSubData: negative offset"); WebGLBuffer* boundBuffer = bufferSlot->get(); if (!boundBuffer) return ErrorInvalidOperation("bufferData: no buffer bound!"); const ArrayBuffer& data = maybeData.Value(); data.ComputeLengthAndData(); CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length(); if (!checked_neededByteLength.isValid()) return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length"); if (checked_neededByteLength.value() > boundBuffer->ByteLength()) return ErrorInvalidValue("bufferSubData: not enough data - operation requires %d bytes, but buffer only has %d bytes", checked_neededByteLength.value(), boundBuffer->ByteLength()); MakeContextCurrent(); boundBuffer->ElementArrayCacheBufferSubData(byteOffset, data.Data(), data.Length()); gl->fBufferSubData(target, byteOffset, data.Length(), data.Data()); }
void WebGLContext::BufferDataT(GLenum target, const BufferT& data, GLenum usage) { 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 // is like intptr_t. if (!CheckedInt<GLsizeiptr>(data.Length()).isValid()) return ErrorOutOfMemory("bufferData: bad size"); if (!ValidateBufferUsageEnum(usage, "bufferData: usage")) return; WebGLBuffer* boundBuffer = bufferSlot.get(); if (!boundBuffer) return ErrorInvalidOperation("bufferData: no buffer bound!"); MakeContextCurrent(); InvalidateBufferFetching(); GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage); if (error) { GenerateWarning("bufferData generated error %s", ErrorName(error)); return; } boundBuffer->SetByteLength(data.Length()); if (!boundBuffer->ElementArrayCacheBufferData(data.Data(), data.Length())) return ErrorOutOfMemory("bufferData: out of memory"); }
void WebGL2Context::CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { if (IsContextLost()) return; if (!ValidateBufferTarget(readTarget, "copyBufferSubData") || !ValidateBufferTarget(writeTarget, "copyBufferSubData")) { return; } const WebGLRefPtr<WebGLBuffer>& readBufferSlot = GetBufferSlotByTarget(readTarget); const WebGLRefPtr<WebGLBuffer>& writeBufferSlot = GetBufferSlotByTarget(writeTarget); if (!readBufferSlot || !writeBufferSlot) return; const WebGLBuffer* readBuffer = readBufferSlot.get(); if (!readBuffer) return ErrorInvalidOperation("copyBufferSubData: No buffer bound to readTarget"); WebGLBuffer* writeBuffer = writeBufferSlot.get(); if (!writeBuffer) return ErrorInvalidOperation("copyBufferSubData: No buffer bound to writeTarget"); if (!ValidateDataOffsetSize(readOffset, size, readBuffer->ByteLength(), "copyBufferSubData")) { return; } if (!ValidateDataOffsetSize(writeOffset, size, writeBuffer->ByteLength(), "copyBufferSubData")) { return; } if (readTarget == writeTarget && !ValidateDataRanges(readOffset, writeOffset, size, "copyBufferSubData")) { 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 WebGL2Context::GetBufferSubDataT(GLenum target, GLintptr offset, const BufferT& data) { 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, "getBufferSubData")) return; // If offset is less than zero, an INVALID_VALUE error is // generated. if (offset < 0) return ErrorInvalidValue("getBufferSubData: negative offset"); WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target); WebGLBuffer* boundBuffer = bufferSlot.get(); if (!boundBuffer) return ErrorInvalidOperation("getBufferSubData: no buffer bound"); // 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(); if (!neededByteLength.isValid()) { ErrorInvalidValue("getBufferSubData: Integer overflow computing the needed" " byte length."); return; } if (neededByteLength.value() > boundBuffer->ByteLength()) { ErrorInvalidValue("getBufferSubData: Not enough data. Operation requires" " %d bytes, but buffer only has %d bytes.", neededByteLength.value(), boundBuffer->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) return ErrorInvalidOperation("getBufferSubData: Currently bound transform" " feedback is active"); // 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 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 && currentTF) { BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, currentTF); } }