bool WebGLContext::DrawArrays_check(const char* funcName, GLenum mode, GLint first, GLsizei vertCount, GLsizei instanceCount) { if (!ValidateDrawModeEnum(mode, funcName)) return false; if (!ValidateNonNegative(funcName, "first", first) || !ValidateNonNegative(funcName, "vertCount", vertCount) || !ValidateNonNegative(funcName, "instanceCount", instanceCount)) { return false; } if (!ValidateStencilParamsForDrawCall()) return false; if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); if (mPrimRestartTypeBytes != 0) { mPrimRestartTypeBytes = 0; // OSX appears to have severe perf issues with leaving this enabled. gl->fDisable(LOCAL_GL_PRIMITIVE_RESTART); } } if (!vertCount || !instanceCount) return false; // No error, just early out. if (!ValidateBufferFetching(funcName)) return false; const auto checked_firstPlusCount = CheckedInt<GLsizei>(first) + vertCount; if (!checked_firstPlusCount.isValid()) { 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.", funcName); return false; } return true; }
void WebGLContext::BufferSubDataImpl(GLenum target, WebGLsizeiptr dstByteOffset, size_t dataLen, const uint8_t* data) { const char funcName[] = "bufferSubData"; if (!ValidateNonNegative(funcName, "byteOffset", dstByteOffset)) return; const auto& buffer = ValidateBufferSelection(funcName, target); if (!buffer) return; if (!buffer->ValidateRange(funcName, dstByteOffset, dataLen)) return; if (!CheckedInt<GLintptr>(dataLen).isValid()) { ErrorOutOfMemory("%s: Size too large.", funcName); return; } const GLintptr glDataLen(dataLen); //// MakeContextCurrent(); const ScopedLazyBind lazyBind(gl, target, buffer); // Warning: Possibly shared memory. See bug 1225033. gl->fBufferSubData(target, dstByteOffset, glDataLen, data); // Warning: Possibly shared memory. See bug 1225033. buffer->ElementArrayCacheBufferSubData(dstByteOffset, data, size_t(glDataLen)); }
void WebGLContext::BufferSubDataImpl(GLenum target, WebGLsizeiptr dstByteOffset, uint64_t dataLen, const uint8_t* data) { const FuncScope funcScope(*this, "bufferSubData"); if (!ValidateNonNegative("byteOffset", dstByteOffset)) return; const auto& buffer = ValidateBufferSelection(target); if (!buffer) return; buffer->BufferSubData(target, uint64_t(dstByteOffset), dataLen, data); }
void WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage) { const char funcName[] = "bufferData"; if (IsContextLost()) return; if (!ValidateNonNegative(funcName, "size", size)) return; //// const UniqueBuffer zeroBuffer(calloc(size, 1)); if (!zeroBuffer) return ErrorOutOfMemory("%s: Failed to allocate zeros.", funcName); BufferDataImpl(target, size_t(size), (const uint8_t*)zeroBuffer.get(), usage); }
void WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage) { const FuncScope funcScope(*this, "bufferData"); if (IsContextLost()) return; if (!ValidateNonNegative("size", size)) return; //// const auto checkedSize = CheckedInt<size_t>(size); if (!checkedSize.isValid()) return ErrorOutOfMemory("size too large for platform."); const UniqueBuffer zeroBuffer(calloc(checkedSize.value(), 1u)); if (!zeroBuffer) return ErrorOutOfMemory("Failed to allocate zeros."); BufferDataImpl(target, uint64_t{checkedSize.value()}, (const uint8_t*)zeroBuffer.get(), usage); }
bool WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vertCount, GLenum type, WebGLintptr byteOffset, GLsizei instanceCount) { 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 (!ValidateNonNegative(funcName, "vertCount", vertCount) || !ValidateNonNegative(funcName, "byteOffset", byteOffset) || !ValidateNonNegative(funcName, "instanceCount", instanceCount)) { return false; } if (!ValidateStencilParamsForDrawCall()) return false; if (!vertCount || !instanceCount) return false; // No error, just early out. uint8_t bytesPerElem = 0; switch (type) { case LOCAL_GL_UNSIGNED_BYTE: bytesPerElem = 1; break; case LOCAL_GL_UNSIGNED_SHORT: bytesPerElem = 2; break; case LOCAL_GL_UNSIGNED_INT: if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) { bytesPerElem = 4; } break; } if (!bytesPerElem) { 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`", funcName); return false; } //// if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); if (mPrimRestartTypeBytes != bytesPerElem) { mPrimRestartTypeBytes = bytesPerElem; const uint32_t ones = UINT32_MAX >> (32 - 8*mPrimRestartTypeBytes); gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART); gl->fPrimitiveRestartIndex(ones); }
void WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer, WebGLintptr offset, WebGLsizeiptr size) { const char funcName[] = "bindBufferRange"; if (IsContextLost()) return; if (!ValidateObjectAllowDeletedOrNull(funcName, buffer)) return; if (buffer && buffer->IsDeleted()) return ErrorInvalidOperation("%s: Cannot bind a deleted object.", funcName); if (!ValidateNonNegative(funcName, "offset", offset) || !ValidateNonNegative(funcName, "size", size)) { return; } WebGLRefPtr<WebGLBuffer>* genericBinding; IndexedBufferBinding* indexedBinding; if (!ValidateIndexedBufferBinding(funcName, target, index, &genericBinding, &indexedBinding)) { return; } if (buffer && !buffer->ValidateCanBindToTarget(funcName, target)) return; //// gl->MakeCurrent(); switch (target) { case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: 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: { 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; } //// #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); //// *genericBinding = buffer; indexedBinding->mBufferBinding = buffer; indexedBinding->mRangeStart = offset; indexedBinding->mRangeSize = size; if (buffer) { buffer->SetContentAfterBind(target); } switch (target) { case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: mBoundTransformFeedback->OnIndexedBindingsChanged(); break; case LOCAL_GL_UNIFORM: OnUBIndexedBindingsChanged(); break; } }
void WebGL2Context::GetBufferSubData(GLenum target, GLintptr offset, const dom::ArrayBufferView& data) { const char funcName[] = "getBufferSubData"; if (IsContextLost()) return; if (!ValidateNonNegative(funcName, "offset", offset)) return; const auto& buffer = ValidateBufferSelection(funcName, target); if (!buffer) return; //// // If offset + returnedData.byteLength would extend beyond the end // of the buffer an INVALID_VALUE error is generated. data.ComputeLengthAndData(); 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() > buffer->ByteLength()) { ErrorInvalidValue("%s: Not enough data. Operation requires %d bytes, but buffer" " only has %d bytes.", funcName, neededByteLength.value(), buffer->ByteLength()); return; } //// if (buffer->mNumActiveTFOs) { ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback" " object.", funcName); return; } if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER && mBoundTransformFeedback->mIsActive) { ErrorInvalidOperation("%s: Currently bound transform feedback is active.", funcName); return; } //// 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); }
void WebGL2Context::CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { const char funcName[] = "copyBufferSubData"; if (IsContextLost()) return; 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 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; }; if (!fnValidateOffsetSize("read", readOffset, readBuffer) || !fnValidateOffsetSize("write", writeOffset, writeBuffer)) { return; } if (readBuffer == writeBuffer && !ValidateDataRanges(readOffset, writeOffset, size, funcName)) { return; } 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" : "element", (writeType == WebGLBuffer::Kind::OtherData) ? "other" : "element"); return; } gl->MakeCurrent(); gl->fCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); }
void WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer, WebGLintptr offset, WebGLsizeiptr size) { const FuncScope funcScope(*this, "bindBufferRange"); if (IsContextLost()) return; if (buffer && !ValidateObject("buffer", *buffer)) return; if (!ValidateNonNegative("offset", offset) || !ValidateNonNegative("size", size)) { return; } WebGLRefPtr<WebGLBuffer>* genericBinding; IndexedBufferBinding* indexedBinding; if (!ValidateIndexedBufferBinding(target, index, &genericBinding, &indexedBinding)) { return; } if (buffer && !buffer->ValidateCanBindToTarget(target)) return; if (buffer && !size) { ErrorInvalidValue("Size must be non-zero for non-null buffer."); return; } //// switch (target) { case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: if (offset % 4 != 0 || size % 4 != 0) { ErrorInvalidValue("For %s, `offset` and `size` must be multiples of 4.", "TRANSFORM_FEEDBACK_BUFFER"); return; } break; case LOCAL_GL_UNIFORM_BUFFER: { GLuint offsetAlignment = 0; gl->GetUIntegerv(LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &offsetAlignment); if (offset % offsetAlignment != 0) { ErrorInvalidValue("For %s, `offset` must be a multiple of %s.", "UNIFORM_BUFFER", "UNIFORM_BUFFER_OFFSET_ALIGNMENT"); return; } } break; } //// #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); //// WebGLBuffer::SetSlot(target, buffer, genericBinding); WebGLBuffer::SetSlot(target, buffer, &indexedBinding->mBufferBinding); indexedBinding->mRangeStart = offset; indexedBinding->mRangeSize = size; if (buffer) { buffer->SetContentAfterBind(target); } }