mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Feat] (MG_Impl, MG_State, MG_Util, DirectGLES, DirectVulkan, MG_Test): implement ARB_clear_buffer_object correctly
This commit is contained in:
@@ -932,7 +932,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
Vector<GLExtension> extensions = {
|
Vector<GLExtension> extensions = {
|
||||||
V_OpenGL30, V_OpenGL31, V_OpenGL32, V_OpenGL33, V_OpenGL40, E_GL_ARB_draw_buffers_blend,
|
V_OpenGL30, V_OpenGL31, V_OpenGL32, V_OpenGL33, V_OpenGL40, E_GL_ARB_draw_buffers_blend,
|
||||||
E_GL_ARB_compute_shader, E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
|
E_GL_ARB_compute_shader, E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
|
||||||
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object, E_GL_EXT_framebuffer_object,
|
E_GL_ARB_clear_buffer_object, E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object, E_GL_EXT_framebuffer_object,
|
||||||
E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage, E_GL_ARB_texture_storage,
|
E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage, E_GL_ARB_texture_storage,
|
||||||
E_GL_ARB_texture_storage_multisample, E_GL_ARB_clear_texture, E_GL_ARB_direct_state_access,
|
E_GL_ARB_texture_storage_multisample, E_GL_ARB_clear_texture, E_GL_ARB_direct_state_access,
|
||||||
E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters, E_GL_ARB_shader_draw_parameters,
|
E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters, E_GL_ARB_shader_draw_parameters,
|
||||||
|
|||||||
@@ -729,8 +729,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
void Ops_ReadbackFromGpu(BufferObject& bufferObject) {
|
void Ops_ReadbackFromGpu(BufferObject& bufferObject) {
|
||||||
auto* resource = ResourceOf(bufferObject);
|
auto* resource = ResourceOf(bufferObject);
|
||||||
if (!resource || resource->id == 0 || !resource->storageInitialized) return;
|
if (!resource || resource->id == 0 || !resource->storageInitialized) return;
|
||||||
if (resource->persistentMapped) return; // shadow already IS the GPU storage
|
|
||||||
if (!CanTouchGLNow() || resource->contextGeneration != g_bufferContextGeneration) return;
|
if (!CanTouchGLNow() || resource->contextGeneration != g_bufferContextGeneration) return;
|
||||||
|
if (resource->persistentMapped) {
|
||||||
|
// Host writes to a persistent map must not race shader writes already queued
|
||||||
|
// on this context. There is no backend copy to read back in this case.
|
||||||
|
if (g_GLESFuncs.glFinish) g_GLESFuncs.glFinish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!g_GLESFuncs.glMapBufferRange || !g_GLESFuncs.glUnmapBuffer) return;
|
if (!g_GLESFuncs.glMapBufferRange || !g_GLESFuncs.glUnmapBuffer) return;
|
||||||
const SizeT size = std::min<SizeT>(bufferObject.GetSize(), resource->storageSize);
|
const SizeT size = std::min<SizeT>(bufferObject.GetSize(), resource->storageSize);
|
||||||
if (size == 0) return;
|
if (size == 0) return;
|
||||||
|
|||||||
@@ -511,7 +511,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Vector<GLExtension> extensions = {
|
Vector<GLExtension> extensions = {
|
||||||
V_OpenGL30, V_OpenGL31, V_OpenGL32, V_OpenGL33, V_OpenGL40, E_GL_ARB_draw_buffers_blend,
|
V_OpenGL30, V_OpenGL31, V_OpenGL32, V_OpenGL33, V_OpenGL40, E_GL_ARB_draw_buffers_blend,
|
||||||
E_GL_ARB_compute_shader, E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
|
E_GL_ARB_compute_shader, E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
|
||||||
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object, E_GL_ARB_draw_indirect,
|
E_GL_ARB_clear_buffer_object, E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object, E_GL_ARB_draw_indirect,
|
||||||
E_GL_ARB_multi_draw_indirect,
|
E_GL_ARB_multi_draw_indirect,
|
||||||
E_GL_ARB_indirect_parameters, E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
|
E_GL_ARB_indirect_parameters, E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
|
||||||
E_GL_ARB_texture_storage, E_GL_ARB_texture_storage_multisample, E_GL_ARB_texture_multisample,
|
E_GL_ARB_texture_storage, E_GL_ARB_texture_storage_multisample, E_GL_ARB_texture_multisample,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
||||||
#include <MG_Util/Converters/GLToMG/BufferEnumConverter.h>
|
#include <MG_Util/Converters/GLToMG/BufferEnumConverter.h>
|
||||||
#include <MG_Util/Converters/MGToGL/BufferEnumConverter.h>
|
#include <MG_Util/Converters/MGToGL/BufferEnumConverter.h>
|
||||||
|
#include <MG_Util/Texture/PixelStoreProcessor.h>
|
||||||
|
|
||||||
namespace MobileGL::MG_Impl::GLImpl {
|
namespace MobileGL::MG_Impl::GLImpl {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -31,6 +32,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
NamedBufferData,
|
NamedBufferData,
|
||||||
NamedBufferSubData,
|
NamedBufferSubData,
|
||||||
CopyNamedBufferSubData,
|
CopyNamedBufferSubData,
|
||||||
|
ClearBufferData,
|
||||||
|
ClearBufferSubData,
|
||||||
ClearNamedBufferData,
|
ClearNamedBufferData,
|
||||||
ClearNamedBufferSubData,
|
ClearNamedBufferSubData,
|
||||||
MapBufferRange,
|
MapBufferRange,
|
||||||
@@ -65,6 +68,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
return "NamedBufferSubData";
|
return "NamedBufferSubData";
|
||||||
case BufferOp::CopyNamedBufferSubData:
|
case BufferOp::CopyNamedBufferSubData:
|
||||||
return "CopyNamedBufferSubData";
|
return "CopyNamedBufferSubData";
|
||||||
|
case BufferOp::ClearBufferData:
|
||||||
|
return "ClearBufferData";
|
||||||
|
case BufferOp::ClearBufferSubData:
|
||||||
|
return "ClearBufferSubData";
|
||||||
case BufferOp::ClearNamedBufferData:
|
case BufferOp::ClearNamedBufferData:
|
||||||
return "ClearNamedBufferData";
|
return "ClearNamedBufferData";
|
||||||
case BufferOp::ClearNamedBufferSubData:
|
case BufferOp::ClearNamedBufferSubData:
|
||||||
@@ -143,16 +150,6 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The pattern is replicated verbatim, which is only the whole story while the client
|
|
||||||
// layout already matches the internal format - the case every entry point in practice
|
|
||||||
// uses, and the only one the conversion machinery here can express. Say so rather than
|
|
||||||
// quietly writing a differently-sized pattern.
|
|
||||||
const SizeT sourceSize = MG_Util::GetInputBytesPerPixel(inputFormat, pixelType);
|
|
||||||
if (sourceSize != elementSize) {
|
|
||||||
MGLOG_W_ONCE("%s: clear pattern is %zu bytes but internalformat 0x%X stores %zu; "
|
|
||||||
"converting between them is not implemented",
|
|
||||||
GetBufferOpName(op), sourceSize, internalformat, elementSize);
|
|
||||||
}
|
|
||||||
return elementSize;
|
return elementSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,27 +191,59 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearNamedBufferRange_State(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size,
|
Bool BuildClearPattern(GLenum internalformat, GLenum format, GLenum type, const void* data,
|
||||||
GLenum format, GLenum type, const void* data, BufferOp op) {
|
SizeT patternSize, BufferOp op, Vector<Uint8>& pattern) {
|
||||||
|
const TextureInternalFormat internal = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
||||||
|
const TextureInputFormat inputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format);
|
||||||
|
const TexturePixelDataType inputType = MG_Util::ConvertGLEnumToTexturePixelDataType(type);
|
||||||
|
|
||||||
|
Vector<Uint8> zeroInput;
|
||||||
|
const void* inputPixel = data;
|
||||||
|
if (inputPixel == nullptr) {
|
||||||
|
const SizeT inputSize = MG_Util::GetInputBytesPerPixel(inputFormat, inputType);
|
||||||
|
if (inputSize == 0) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||||
|
"format and type do not describe a source pixel."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
zeroInput.resize(inputSize);
|
||||||
|
inputPixel = zeroInput.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MG_Util::PixelStoreProcessor::ConvertOnePixelToInternal(
|
||||||
|
internal, inputFormat, inputType, inputPixel, pattern)) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl", GetBufferOpName(op),
|
||||||
|
std::format("Cannot convert one ({}, {}) pixel into internalformat 0x{:X}.",
|
||||||
|
MG_Util::ConvertGLEnumToString(format), MG_Util::ConvertGLEnumToString(type),
|
||||||
|
internalformat)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data == nullptr) {
|
||||||
|
// GL defines a null clear value as all zero bits in the destination store, while
|
||||||
|
// retaining the format/type validation above.
|
||||||
|
pattern.assign(patternSize, 0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearBufferRange_State(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject,
|
||||||
|
GLenum internalformat, GLintptr offset, GLsizeiptr size,
|
||||||
|
GLenum format, GLenum type, const void* data, BufferOp op) {
|
||||||
const SizeT patternSize = GetClearPatternSize(internalformat, format, type, op);
|
const SizeT patternSize = GetClearPatternSize(internalformat, format, type, op);
|
||||||
if (patternSize == 0) return;
|
if (patternSize == 0) return;
|
||||||
|
|
||||||
auto bufferObject = GetNamedBufferObject(buffer, op);
|
|
||||||
if (!bufferObject) return;
|
|
||||||
if (!ValidateBufferClearRange(bufferObject, offset, size, patternSize, op)) return;
|
if (!ValidateBufferClearRange(bufferObject, offset, size, patternSize, op)) return;
|
||||||
if (size == 0) return;
|
if (size == 0) return;
|
||||||
|
|
||||||
Vector<Uint8> clearData(static_cast<SizeT>(size));
|
Vector<Uint8> pattern;
|
||||||
if (data) {
|
if (!BuildClearPattern(internalformat, format, type, data, patternSize, op, pattern)) return;
|
||||||
const auto* pattern = static_cast<const Uint8*>(data);
|
bufferObject->FillSubData({pattern.data(), pattern.size()}, static_cast<SizeT>(offset),
|
||||||
for (SizeT at = 0; at < clearData.size(); at += patternSize) {
|
static_cast<SizeT>(size));
|
||||||
Memcpy(clearData.data() + at, pattern, patternSize);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Memset(clearData.data(), 0, clearData.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
bufferObject->UploadSubData({clearData.data(), clearData.size()}, static_cast<SizeT>(offset));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& GetBufferBindingSlot(BufferTarget target) {
|
auto& GetBufferBindingSlot(BufferTarget target) {
|
||||||
@@ -1197,17 +1226,34 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
static_cast<SizeT>(writeOffset), static_cast<SizeT>(size));
|
static_cast<SizeT>(writeOffset), static_cast<SizeT>(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearBufferData_State(GLenum target, GLenum internalformat, GLenum format, GLenum type, const void* data) {
|
||||||
|
auto bufferObject = GetBoundBufferObject(target, BufferOp::ClearBufferData);
|
||||||
|
if (!bufferObject) return;
|
||||||
|
ClearBufferRange_State(bufferObject, internalformat, 0, static_cast<GLsizeiptr>(bufferObject->GetSize()), format,
|
||||||
|
type, data, BufferOp::ClearBufferData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearBufferSubData_State(GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size,
|
||||||
|
GLenum format, GLenum type, const void* data) {
|
||||||
|
auto bufferObject = GetBoundBufferObject(target, BufferOp::ClearBufferSubData);
|
||||||
|
if (!bufferObject) return;
|
||||||
|
ClearBufferRange_State(bufferObject, internalformat, offset, size, format, type, data,
|
||||||
|
BufferOp::ClearBufferSubData);
|
||||||
|
}
|
||||||
|
|
||||||
void ClearNamedBufferData_State(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) {
|
void ClearNamedBufferData_State(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) {
|
||||||
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::ClearNamedBufferData);
|
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::ClearNamedBufferData);
|
||||||
if (!bufferObject) return;
|
if (!bufferObject) return;
|
||||||
ClearNamedBufferRange_State(buffer, internalformat, 0, static_cast<GLsizeiptr>(bufferObject->GetSize()), format,
|
ClearBufferRange_State(bufferObject, internalformat, 0, static_cast<GLsizeiptr>(bufferObject->GetSize()), format,
|
||||||
type, data, BufferOp::ClearNamedBufferData);
|
type, data, BufferOp::ClearNamedBufferData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearNamedBufferSubData_State(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size,
|
void ClearNamedBufferSubData_State(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size,
|
||||||
GLenum format, GLenum type, const void* data) {
|
GLenum format, GLenum type, const void* data) {
|
||||||
ClearNamedBufferRange_State(buffer, internalformat, offset, size, format, type, data,
|
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::ClearNamedBufferSubData);
|
||||||
BufferOp::ClearNamedBufferSubData);
|
if (!bufferObject) return;
|
||||||
|
ClearBufferRange_State(bufferObject, internalformat, offset, size, format, type, data,
|
||||||
|
BufferOp::ClearNamedBufferSubData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* MapNamedBuffer_State(GLuint buffer, GLenum access) {
|
void* MapNamedBuffer_State(GLuint buffer, GLenum access) {
|
||||||
@@ -1662,6 +1708,15 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
CopyNamedBufferSubData_State(readBuffer, writeBuffer, readOffset, writeOffset, size);
|
CopyNamedBufferSubData_State(readBuffer, writeBuffer, readOffset, writeOffset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearBufferData(GLenum target, GLenum internalformat, GLenum format, GLenum type, const void* data) {
|
||||||
|
ClearBufferData_State(target, internalformat, format, type, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearBufferSubData(GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format,
|
||||||
|
GLenum type, const void* data) {
|
||||||
|
ClearBufferSubData_State(target, internalformat, offset, size, format, type, data);
|
||||||
|
}
|
||||||
|
|
||||||
void ClearNamedBufferData(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) {
|
void ClearNamedBufferData(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) {
|
||||||
ClearNamedBufferData_State(buffer, internalformat, format, type, data);
|
ClearNamedBufferData_State(buffer, internalformat, format, type, data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
void NamedBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data);
|
void NamedBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data);
|
||||||
void CopyNamedBufferSubData(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset,
|
void CopyNamedBufferSubData(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset,
|
||||||
GLsizeiptr size);
|
GLsizeiptr size);
|
||||||
|
void ClearBufferData(GLenum target, GLenum internalformat, GLenum format, GLenum type, const void* data);
|
||||||
|
void ClearBufferSubData(GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format,
|
||||||
|
GLenum type, const void* data);
|
||||||
void ClearNamedBufferData(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data);
|
void ClearNamedBufferData(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data);
|
||||||
void ClearNamedBufferSubData(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format,
|
void ClearNamedBufferSubData(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format,
|
||||||
GLenum type, const void* data);
|
GLenum type, const void* data);
|
||||||
|
|||||||
@@ -985,8 +985,8 @@ DECLARE_GL_FUNCTION_HEAD(void, DrawElementsInstancedBaseVertexBaseInstance, GLen
|
|||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetActiveAtomicCounterBufferiv, GLuint program, GLuint bufferIndex, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetActiveAtomicCounterBufferiv, program, bufferIndex, pname, params)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetActiveAtomicCounterBufferiv, GLuint program, GLuint bufferIndex, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetActiveAtomicCounterBufferiv, program, bufferIndex, pname, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, DrawTransformFeedbackInstanced, GLenum mode, GLuint id, GLsizei instancecount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawTransformFeedbackInstanced, mode, id, instancecount)
|
DECLARE_GL_FUNCTION_HEAD(void, DrawTransformFeedbackInstanced, GLenum mode, GLuint id, GLsizei instancecount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawTransformFeedbackInstanced, mode, id, instancecount)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, DrawTransformFeedbackStreamInstanced, GLenum mode, GLuint id, GLuint stream, GLsizei instancecount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawTransformFeedbackStreamInstanced, mode, id, stream, instancecount)
|
DECLARE_GL_FUNCTION_HEAD(void, DrawTransformFeedbackStreamInstanced, GLenum mode, GLuint id, GLuint stream, GLsizei instancecount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawTransformFeedbackStreamInstanced, mode, id, stream, instancecount)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearBufferData, GLenum target, GLenum internalformat, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearBufferData, target, internalformat, format, type, data)
|
DECLARE_GL_FUNCTION_HEAD(void, ClearBufferData, GLenum target, GLenum internalformat, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearBufferData, target, internalformat, format, type, data)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearBufferSubData, GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearBufferSubData, target, internalformat, offset, size, format, type, data)
|
DECLARE_GL_FUNCTION_HEAD(void, ClearBufferSubData, GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearBufferSubData, target, internalformat, offset, size, format, type, data)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetInternalformati64v, GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetInternalformati64v, target, internalformat, pname, count, params)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetInternalformati64v, GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetInternalformati64v, target, internalformat, pname, count, params)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateTexSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateTexSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateTexSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateTexSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateTexImage, GLuint texture, GLint level) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateTexImage, texture, level)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateTexImage, GLuint texture, GLint level) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateTexImage, texture, level)
|
||||||
|
|||||||
@@ -250,6 +250,34 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
NotifyContentWrite(atOffset, data.size);
|
NotifyContentWrite(atOffset, data.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BufferObject::FillSubData(DataPtr pattern, SizeT atOffset, SizeT size) {
|
||||||
|
MOBILEGL_ASSERT(pattern.data != nullptr && pattern.size > 0,
|
||||||
|
"FillSubData requires a non-empty pattern.");
|
||||||
|
MOBILEGL_ASSERT(size % pattern.size == 0,
|
||||||
|
"FillSubData size (%zu) must be a multiple of pattern size (%zu).", size, pattern.size);
|
||||||
|
MOBILEGL_ASSERT(atOffset <= m_size && size <= m_size - atOffset,
|
||||||
|
"FillSubData out of bounds: atOffset (%zu) + size (%zu) > m_size (%zu)", atOffset, size,
|
||||||
|
m_size);
|
||||||
|
MOBILEGL_ASSERT(!m_isMapped || (m_mappingAccess & BufferMappingAccessBit::Persistent),
|
||||||
|
"Cannot fill data while buffer is non-persistently mapped.");
|
||||||
|
if (size == 0) return;
|
||||||
|
|
||||||
|
// A clear is ordered after all earlier GPU writes. Partial clears additionally need the
|
||||||
|
// retained shadow bytes; whole-store clears need the same synchronization before writing
|
||||||
|
// an adopted persistent mapping that the GPU may still be accessing.
|
||||||
|
SyncGpuWrites();
|
||||||
|
|
||||||
|
Uint8* dst = m_resource.Bytes() + atOffset;
|
||||||
|
if (pattern.size == 1) {
|
||||||
|
Memset(dst, *static_cast<const Uint8*>(pattern.data), size);
|
||||||
|
} else {
|
||||||
|
for (SizeT at = 0; at < size; at += pattern.size) {
|
||||||
|
Memcpy(dst + at, pattern.data, pattern.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NotifyContentWrite(atOffset, size);
|
||||||
|
}
|
||||||
|
|
||||||
void BufferObject::DownloadSubData(void* dst, SizeT atOffset, SizeT size) const {
|
void BufferObject::DownloadSubData(void* dst, SizeT atOffset, SizeT size) const {
|
||||||
MOBILEGL_ASSERT(atOffset + size <= m_size,
|
MOBILEGL_ASSERT(atOffset + size <= m_size,
|
||||||
"DownloadSubData out of bounds: atOffset (%zu) + size (%zu) > m_size (%zu)", atOffset, size,
|
"DownloadSubData out of bounds: atOffset (%zu) + size (%zu) > m_size (%zu)", atOffset, size,
|
||||||
|
|||||||
@@ -132,6 +132,9 @@ namespace MobileGL {
|
|||||||
|
|
||||||
void UploadData(DataPtr data, SizeT atOffset);
|
void UploadData(DataPtr data, SizeT atOffset);
|
||||||
void UploadSubData(DataPtr data, SizeT atOffset);
|
void UploadSubData(DataPtr data, SizeT atOffset);
|
||||||
|
// Repeats one already-converted element through [atOffset, atOffset + size) and
|
||||||
|
// publishes the range as one content mutation.
|
||||||
|
void FillSubData(DataPtr pattern, SizeT atOffset, SizeT size);
|
||||||
// Reads `size` bytes from the CPU shadow at `atOffset` into `dst` (glGetBufferSubData).
|
// Reads `size` bytes from the CPU shadow at `atOffset` into `dst` (glGetBufferSubData).
|
||||||
// The shadow reflects CPU writes (BufferData/SubData/maps) and backend write-backs, but not
|
// The shadow reflects CPU writes (BufferData/SubData/maps) and backend write-backs, but not
|
||||||
// arbitrary GPU-side writes.
|
// arbitrary GPU-side writes.
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <MG_State/GLState/Core.h>
|
#include <MG_State/GLState/Core.h>
|
||||||
|
|
||||||
#include <MG_Impl/GLImpl/Buffer/GL_Buffer.h>
|
#include <MG_Impl/GLImpl/Buffer/GL_Buffer.h>
|
||||||
|
#include <MG_Impl/GetProcAddress.h>
|
||||||
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
|
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
|
||||||
|
|
||||||
using namespace MobileGL;
|
using namespace MobileGL;
|
||||||
@@ -599,6 +600,117 @@ TEST_F(BufferTest, ClearNamedBufferSubDataRepeatsPattern) {
|
|||||||
EXPECT_EQ(actual, (Vector<Uint32>{0, pattern, pattern, pattern, 0}));
|
EXPECT_EQ(actual, (Vector<Uint32>{0, pattern, pattern, pattern, 0}));
|
||||||
EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
}
|
}
|
||||||
|
TEST_F(BufferTest, ClearBufferSubDataInitializesIrisStaticSsboRange) {
|
||||||
|
GLuint buffer = 0;
|
||||||
|
MobileGL::MG_Impl::GLImpl::GenBuffers(1, &buffer);
|
||||||
|
MobileGL::MG_Impl::GLImpl::BindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
|
||||||
|
|
||||||
|
Vector<Uint8> initial(32, 0x7F);
|
||||||
|
MobileGL::MG_Impl::GLImpl::BufferData(
|
||||||
|
GL_SHADER_STORAGE_BUFFER, initial.size(), initial.data(), GL_STATIC_DRAW);
|
||||||
|
const GLbyte zero = 0;
|
||||||
|
const auto clear = reinterpret_cast<PFNGLCLEARBUFFERSUBDATAPROC>(
|
||||||
|
MobileGL::MG_Impl::GetProcAddress("glClearBufferSubData"));
|
||||||
|
ASSERT_NE(clear, nullptr);
|
||||||
|
clear(GL_SHADER_STORAGE_BUFFER, GL_R8, 4, 24, GL_RED, GL_BYTE, &zero);
|
||||||
|
|
||||||
|
Vector<Uint8> actual(initial.size());
|
||||||
|
auto bufferObject = MobileGL::MG_State::pGLContext->GetBufferObject(buffer);
|
||||||
|
ASSERT_NE(bufferObject, nullptr);
|
||||||
|
Memcpy(actual.data(), bufferObject->AcquireMemory(false, true, false), actual.size());
|
||||||
|
EXPECT_EQ(actual, (Vector<Uint8>{0x7F, 0x7F, 0x7F, 0x7F,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0x7F, 0x7F, 0x7F, 0x7F}));
|
||||||
|
EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
|
||||||
|
MobileGL::MG_Impl::GLImpl::BindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||||
|
MobileGL::MG_Impl::GLImpl::DeleteBuffers(1, &buffer);
|
||||||
|
DrainPendingGlErrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BufferTest, ClearBufferSubDataInitializesCompleteIrisStaticSsbo) {
|
||||||
|
constexpr SizeT irisStaticSsboSize = 5'000'192;
|
||||||
|
GLuint buffer = 0;
|
||||||
|
MobileGL::MG_Impl::GLImpl::GenBuffers(1, &buffer);
|
||||||
|
MobileGL::MG_Impl::GLImpl::BindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
|
||||||
|
|
||||||
|
Vector<Uint8> initial(irisStaticSsboSize, 0x7F);
|
||||||
|
MobileGL::MG_Impl::GLImpl::BufferData(
|
||||||
|
GL_SHADER_STORAGE_BUFFER, initial.size(), initial.data(), GL_STATIC_DRAW);
|
||||||
|
const GLbyte zero = 0;
|
||||||
|
MobileGL::MG_Impl::GLImpl::ClearBufferSubData(
|
||||||
|
GL_SHADER_STORAGE_BUFFER, GL_R8, 0, irisStaticSsboSize, GL_RED, GL_BYTE, &zero);
|
||||||
|
|
||||||
|
Vector<Uint8> actual(irisStaticSsboSize);
|
||||||
|
auto bufferObject = MobileGL::MG_State::pGLContext->GetBufferObject(buffer);
|
||||||
|
ASSERT_NE(bufferObject, nullptr);
|
||||||
|
Memcpy(actual.data(), bufferObject->AcquireMemory(false, true, false), actual.size());
|
||||||
|
EXPECT_EQ(actual, Vector<Uint8>(irisStaticSsboSize, 0));
|
||||||
|
EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
|
||||||
|
MobileGL::MG_Impl::GLImpl::BindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||||
|
MobileGL::MG_Impl::GLImpl::DeleteBuffers(1, &buffer);
|
||||||
|
DrainPendingGlErrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BufferTest, ClearBufferDataConvertsOneClientPixelBeforeRepeatingIt) {
|
||||||
|
GLuint buffer = 0;
|
||||||
|
MobileGL::MG_Impl::GLImpl::GenBuffers(1, &buffer);
|
||||||
|
MobileGL::MG_Impl::GLImpl::BindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||||
|
|
||||||
|
Vector<Uint32> initial(4, 0u);
|
||||||
|
MobileGL::MG_Impl::GLImpl::BufferData(GL_ARRAY_BUFFER, initial.size() * sizeof(Uint32), initial.data(),
|
||||||
|
GL_STATIC_DRAW);
|
||||||
|
const Uint8 value = 0xAB;
|
||||||
|
MobileGL::MG_Impl::GLImpl::ClearBufferData(
|
||||||
|
GL_ARRAY_BUFFER, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, &value);
|
||||||
|
|
||||||
|
Vector<Uint32> actual(initial.size());
|
||||||
|
auto bufferObject = MobileGL::MG_State::pGLContext->GetBufferObject(buffer);
|
||||||
|
ASSERT_NE(bufferObject, nullptr);
|
||||||
|
Memcpy(actual.data(), bufferObject->AcquireMemory(false, true, false), actual.size() * sizeof(Uint32));
|
||||||
|
EXPECT_EQ(actual, Vector<Uint32>(initial.size(), value));
|
||||||
|
EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
|
||||||
|
MobileGL::MG_Impl::GLImpl::BindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
MobileGL::MG_Impl::GLImpl::DeleteBuffers(1, &buffer);
|
||||||
|
DrainPendingGlErrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BufferTest, ClearBufferSubDataRejectsUnboundTarget) {
|
||||||
|
MobileGL::MG_Impl::GLImpl::BindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||||
|
const GLbyte zero = 0;
|
||||||
|
MobileGL::MG_Impl::GLImpl::ClearBufferSubData(
|
||||||
|
GL_SHADER_STORAGE_BUFFER, GL_R8, 0, 1, GL_RED, GL_BYTE, &zero);
|
||||||
|
ExpectSingleGlError(GL_INVALID_OPERATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BufferTest, ClearBufferDataRejectsInvalidPixelFormatTypePairs) {
|
||||||
|
GLuint buffer = 0;
|
||||||
|
MobileGL::MG_Impl::GLImpl::GenBuffers(1, &buffer);
|
||||||
|
MobileGL::MG_Impl::GLImpl::BindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||||
|
|
||||||
|
const Vector<Uint8> initial{0x7F, 0x7F};
|
||||||
|
MobileGL::MG_Impl::GLImpl::BufferData(GL_ARRAY_BUFFER, initial.size(), initial.data(), GL_STATIC_DRAW);
|
||||||
|
const Uint16 packed = 0;
|
||||||
|
MobileGL::MG_Impl::GLImpl::ClearBufferData(
|
||||||
|
GL_ARRAY_BUFFER, GL_R16, GL_RED, GL_UNSIGNED_SHORT_5_6_5, &packed);
|
||||||
|
ExpectSingleGlError(GL_INVALID_VALUE);
|
||||||
|
MobileGL::MG_Impl::GLImpl::ClearBufferData(
|
||||||
|
GL_ARRAY_BUFFER, GL_R16, GL_RED, GL_UNSIGNED_SHORT_5_6_5, nullptr);
|
||||||
|
ExpectSingleGlError(GL_INVALID_VALUE);
|
||||||
|
|
||||||
|
Vector<Uint8> actual(initial.size());
|
||||||
|
auto bufferObject = MobileGL::MG_State::pGLContext->GetBufferObject(buffer);
|
||||||
|
ASSERT_NE(bufferObject, nullptr);
|
||||||
|
Memcpy(actual.data(), bufferObject->AcquireMemory(false, true, false), actual.size());
|
||||||
|
EXPECT_EQ(actual, initial);
|
||||||
|
|
||||||
|
MobileGL::MG_Impl::GLImpl::BindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
MobileGL::MG_Impl::GLImpl::DeleteBuffers(1, &buffer);
|
||||||
|
DrainPendingGlErrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// GL 4.6 core 6.5: glBufferSubData fails only when the written range OVERLAPS the mapped range.
|
// GL 4.6 core 6.5: glBufferSubData fails only when the written range OVERLAPS the mapped range.
|
||||||
|
|||||||
@@ -423,6 +423,26 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
|
|||||||
InternalPackedLayout internalPacked;
|
InternalPackedLayout internalPacked;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Bool IsValidUnpackPixelPair(TextureInputFormat format, TexturePixelDataType type) {
|
||||||
|
UnpackChannelMapping mapping{};
|
||||||
|
if (!GetUnpackChannelMapping(format, mapping)) return false;
|
||||||
|
|
||||||
|
PackedTypeLayout packed{};
|
||||||
|
if (GetPackedTypeLayout(type, packed)) {
|
||||||
|
return packed.fieldCount == mapping.channelCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case TexturePixelDataType::UnsignedInt5999Rev:
|
||||||
|
case TexturePixelDataType::UnsignedInt101111Rev:
|
||||||
|
return !mapping.isInteger && mapping.channelCount == 3;
|
||||||
|
default: {
|
||||||
|
ShadowComponent component{};
|
||||||
|
return GetDirectShadowComponentForType(type, mapping.isInteger, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns true when the (format, type) -> internal-format upload needs a per-texel conversion;
|
// Returns true when the (format, type) -> internal-format upload needs a per-texel conversion;
|
||||||
// returns false both for layouts that already match the shadow bytes (memcpy fast path) and for
|
// returns false both for layouts that already match the shadow bytes (memcpy fast path) and for
|
||||||
// combinations the converter does not support (legacy copy behavior).
|
// combinations the converter does not support (legacy copy behavior).
|
||||||
@@ -964,6 +984,32 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
|
|||||||
return outputPixels;
|
return outputPixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool ConvertOnePixelToInternal(TextureInternalFormat targetInternalFormat,
|
||||||
|
TextureInputFormat textureInputFormat,
|
||||||
|
TexturePixelDataType inputDataType,
|
||||||
|
const void* inputPixel,
|
||||||
|
Vector<Uint8>& outputPixel) {
|
||||||
|
outputPixel.clear();
|
||||||
|
if (inputPixel == nullptr || !IsValidUnpackPixelPair(textureInputFormat, inputDataType)) return false;
|
||||||
|
|
||||||
|
PixelStoreParameters params{};
|
||||||
|
params.Alignment = 1;
|
||||||
|
SizeT convertedSize = 0;
|
||||||
|
void* converted = ProcessTexturePixelsDataUnpack(
|
||||||
|
inputPixel, params, targetInternalFormat, textureInputFormat, inputDataType, {1, 1, 1}, false,
|
||||||
|
convertedSize);
|
||||||
|
const SizeT expectedSize = MG_Util::GetSizedInternalFormatSizeInBytes(targetInternalFormat);
|
||||||
|
if (converted == nullptr || convertedSize != expectedSize || expectedSize == 0) {
|
||||||
|
if (converted != nullptr) free(converted);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputPixel.resize(convertedSize);
|
||||||
|
Memcpy(outputPixel.data(), converted, convertedSize);
|
||||||
|
free(converted);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void* ProcessTexturePixelsDataPack(const void* inputPixels, const PixelStoreParameters& params,
|
void* ProcessTexturePixelsDataPack(const void* inputPixels, const PixelStoreParameters& params,
|
||||||
TextureInternalFormat srcInternalFormat, TexturePixelDataType srcDataType,
|
TextureInternalFormat srcInternalFormat, TexturePixelDataType srcDataType,
|
||||||
TextureInputFormat dstInputFormat, TexturePixelDataType dstDataType,
|
TextureInputFormat dstInputFormat, TexturePixelDataType dstDataType,
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ namespace MobileGL::MG_Util::PixelStoreProcessor {
|
|||||||
TextureInternalFormat srcInternalFormat, TexturePixelDataType srcDataType,
|
TextureInternalFormat srcInternalFormat, TexturePixelDataType srcDataType,
|
||||||
TextureInputFormat dstInputFormat, TexturePixelDataType dstDataType,
|
TextureInputFormat dstInputFormat, TexturePixelDataType dstDataType,
|
||||||
IntVec3 dimension, Bool isBitmap, SizeT& outSize);
|
IntVec3 dimension, Bool isBitmap, SizeT& outSize);
|
||||||
|
Bool ConvertOnePixelToInternal(TextureInternalFormat targetInternalFormat,
|
||||||
|
TextureInputFormat textureInputFormat,
|
||||||
|
TexturePixelDataType inputDataType,
|
||||||
|
const void* inputPixel,
|
||||||
|
Vector<Uint8>& outputPixel);
|
||||||
|
|
||||||
void ProcessColorSwizzle(void* data, SizeT pixelCount, const Vector<TextureSwizzleParam>& swizzle);
|
void ProcessColorSwizzle(void* data, SizeT pixelCount, const Vector<TextureSwizzleParam>& swizzle);
|
||||||
|
|
||||||
// True when a packed internal format's 32-bit storage word IS the client (format, type) word,
|
// True when a packed internal format's 32-bit storage word IS the client (format, type) word,
|
||||||
|
|||||||
Reference in New Issue
Block a user