зеркало из https://github.com/mozilla/gecko-dev.git
229 строки
7.7 KiB
C++
229 строки
7.7 KiB
C++
/* -*- 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 "WebGL2Context.h"
|
|
|
|
#include "GLContext.h"
|
|
#include "WebGLBuffer.h"
|
|
#include "WebGLTransformFeedback.h"
|
|
|
|
namespace mozilla {
|
|
|
|
bool
|
|
WebGL2Context::ValidateBufferTarget(GLenum target, const char* info)
|
|
{
|
|
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(info, 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
|
|
|
|
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::GetBufferSubData(GLenum target, GLintptr offset,
|
|
const dom::Nullable<dom::ArrayBuffer>& maybeData)
|
|
{
|
|
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");
|
|
|
|
// If returnedData is null then an INVALID_VALUE error is
|
|
// generated.
|
|
if (maybeData.IsNull())
|
|
return ErrorInvalidValue("getBufferSubData: returnedData is null");
|
|
|
|
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.
|
|
const dom::ArrayBuffer& data = maybeData.Value();
|
|
data.ComputeLengthAndData();
|
|
|
|
CheckedInt<WebGLsizeiptr> neededByteLength = CheckedInt<WebGLsizeiptr>(offset) + data.Length();
|
|
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.Length(), LOCAL_GL_MAP_READ_BIT);
|
|
memcpy(data.Data(), ptr, data.Length());
|
|
gl->fUnmapBuffer(target);
|
|
|
|
if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER && currentTF) {
|
|
BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, currentTF);
|
|
}
|
|
}
|
|
|
|
} // namespace mozilla
|