mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
[Feat] (MG_Impl/GL_Buffer, MG_State/BufferState, MG_Backend): implement persistent mapping
This commit is contained in:
@@ -132,7 +132,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
V_OpenGL33, 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_program_interface_query, E_GL_ARB_framebuffer_object,
|
||||
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture},
|
||||
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage},
|
||||
.IsCompatibilityProfile = false // Is Compatibility Profile
|
||||
},
|
||||
.StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability
|
||||
|
||||
@@ -106,6 +106,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
namespace BufferImpl {
|
||||
void CreateAndSyncBufferObject(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject) {
|
||||
bufferObject->MarkPersistentMappedRangeDirty();
|
||||
if (!(bufferObject->GetChangeBits() & BufferChangeBits::DirtyBit)) return;
|
||||
|
||||
const auto& backendBufferIt = g_backendBufferObjects.find(bufferObject.get());
|
||||
|
||||
@@ -748,7 +748,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapS, GL_TEXTURE_WRAP_S, WrapMode)
|
||||
SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapT, GL_TEXTURE_WRAP_T, WrapMode)
|
||||
SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapR, GL_TEXTURE_WRAP_R, WrapMode)
|
||||
if (SupportsWrapR(targetInternal)) {
|
||||
SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapR, GL_TEXTURE_WRAP_R, WrapMode)
|
||||
} else {
|
||||
m_cacheSamplerParameters.wrapR = samplerParams.wrapR;
|
||||
}
|
||||
SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(compareFunc, GL_TEXTURE_COMPARE_FUNC, CompareFunc)
|
||||
SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(compareMode, GL_TEXTURE_COMPARE_MODE, CompareMode)
|
||||
if (m_cacheSamplerParameters.minLod != samplerParams.minLod) {
|
||||
@@ -1032,6 +1036,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MG_Util::ConvertFramebufferAttachmentTypeToString(frontendType).c_str(),
|
||||
MG_Util::ConvertGLEnumToString(glBackendAttachment).c_str(),
|
||||
m_syncedFrontendAttachmentVersions[i]);
|
||||
if (!attachmentObject.IsTexture() && !attachmentObject.IsRenderbuffer()) {
|
||||
continue;
|
||||
}
|
||||
GLint objectType = GL_NONE;
|
||||
g_GLESFuncs.glGetFramebufferAttachmentParameteriv(
|
||||
glFBOTarget, glBackendAttachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &objectType);
|
||||
|
||||
@@ -166,6 +166,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return true;
|
||||
}
|
||||
|
||||
inline Bool SupportsWrapR(TextureTarget target) {
|
||||
return target == TextureTarget::Texture3D || target == TextureTarget::TextureCubeMap;
|
||||
}
|
||||
|
||||
struct StateTextureBasicInfo { // Used for tracking texture state changes
|
||||
TextureInternalFormat internalFormat = TextureInternalFormat::Unknown;
|
||||
SizeT width = 0;
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
V_OpenGL33, 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_program_interface_query, E_GL_ARB_framebuffer_object,
|
||||
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture},
|
||||
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage},
|
||||
.IsCompatibilityProfile = false // Is Compatibility Profile
|
||||
},
|
||||
.StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability
|
||||
|
||||
@@ -615,6 +615,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
MOBILEGL_ASSERT(bufferObject != nullptr,
|
||||
"ResolveUniformBufferPayload: no UBO bound at frontend binding %u for block '%s'",
|
||||
frontendBinding, program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
|
||||
bufferObject->MarkPersistentMappedRangeDirty();
|
||||
|
||||
const auto bufferData = bufferObject->GetDataReadOnly();
|
||||
MOBILEGL_ASSERT(bufferData != nullptr && !bufferData->empty(),
|
||||
|
||||
@@ -86,6 +86,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
const auto* bufferData = bufferObject->GetDataReadOnly().get();
|
||||
MOBILEGL_ASSERT(bufferData != nullptr, "VkBufferManager::SyncResidentBuffer requires frontend buffer data");
|
||||
bufferObject->MarkPersistentMappedRangeDirty();
|
||||
|
||||
const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(bufferObject->GetSize());
|
||||
if (bufferSize == 0) {
|
||||
|
||||
@@ -140,6 +140,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
Memcpy(static_cast<Uint8*>(mapped) + offset, data, static_cast<SizeT>(size));
|
||||
const VkResult flushResult = vmaFlushAllocation(m_allocator, m_allocation, offset, size);
|
||||
if (flushResult != VK_SUCCESS) {
|
||||
MGLOG_E("VkBufferObject::Upload failed: vmaFlushAllocation returned %d", flushResult);
|
||||
if (!wasMapped) {
|
||||
Unmap();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!wasMapped) {
|
||||
Unmap();
|
||||
}
|
||||
|
||||
@@ -1316,7 +1316,7 @@ void main() {
|
||||
.minUploadBytes = 4 * 1024 * 1024,
|
||||
.transientMemoryUsage = VMA_MEMORY_USAGE_AUTO,
|
||||
.transientAllocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT,
|
||||
.transientPersistentMapping = false,
|
||||
.transientPersistentMapping = true,
|
||||
});
|
||||
MOBILEGL_ASSERT(succeeded, "VkBufferManager initialization failed.");
|
||||
m_textureManager = MakeUnique<VkTextureManager>();
|
||||
@@ -1537,6 +1537,7 @@ void main() {
|
||||
auto sourceBufferShared = MG_State::pGLContext->GetBufferObject(sourceBuffer->GetExternalIndex());
|
||||
MOBILEGL_ASSERT(sourceBufferShared != nullptr,
|
||||
"UploadAndBindVertexStreams failed to resolve shared source buffer");
|
||||
sourceBufferShared->MarkPersistentMappedRangeDirty();
|
||||
BufferSlice slice{};
|
||||
const Bool isDirty = (sourceBufferShared->GetChangeBits() & BufferChangeBits::DirtyBit);
|
||||
const Uint64 changeSerial = sourceBufferShared->GetChangeSerial();
|
||||
@@ -1641,6 +1642,7 @@ void main() {
|
||||
BufferSlice slice{};
|
||||
auto indexBufferShared = MG_State::pGLContext->GetBufferObject(indexBuffer->GetExternalIndex());
|
||||
MOBILEGL_ASSERT(indexBufferShared != nullptr, "UploadAndBindIndexBuffer failed to resolve shared EBO");
|
||||
indexBufferShared->MarkPersistentMappedRangeDirty();
|
||||
const Bool isDirty = (indexBufferShared->GetChangeBits() & BufferChangeBits::DirtyBit);
|
||||
const Uint64 changeSerial = indexBufferShared->GetChangeSerial();
|
||||
const SizeT indexBufferSize = indexBufferShared->GetSize();
|
||||
|
||||
@@ -16,68 +16,294 @@
|
||||
|
||||
namespace MobileGL::MG_Impl::GLImpl {
|
||||
namespace {
|
||||
enum class BufferOp {
|
||||
GetBufferParameteriv,
|
||||
GetBufferParameteri64v,
|
||||
GetBufferPointerv,
|
||||
BufferStorage,
|
||||
NamedBufferStorage,
|
||||
NamedBufferData,
|
||||
NamedBufferSubData,
|
||||
MapBufferRange,
|
||||
MapBuffer,
|
||||
MapNamedBuffer,
|
||||
MapNamedBufferRange,
|
||||
UnmapNamedBuffer,
|
||||
FlushMappedNamedBufferRange,
|
||||
GetNamedBufferParameteriv,
|
||||
GetNamedBufferParameteri64v,
|
||||
GetNamedBufferPointerv,
|
||||
};
|
||||
|
||||
const char* GetBufferOpName(BufferOp op) {
|
||||
switch (op) {
|
||||
case BufferOp::GetBufferParameteriv:
|
||||
return "GetBufferParameteriv";
|
||||
case BufferOp::GetBufferParameteri64v:
|
||||
return "GetBufferParameteri64v";
|
||||
case BufferOp::GetBufferPointerv:
|
||||
return "GetBufferPointerv";
|
||||
case BufferOp::BufferStorage:
|
||||
return "BufferStorage";
|
||||
case BufferOp::NamedBufferStorage:
|
||||
return "NamedBufferStorage";
|
||||
case BufferOp::NamedBufferData:
|
||||
return "NamedBufferData";
|
||||
case BufferOp::NamedBufferSubData:
|
||||
return "NamedBufferSubData";
|
||||
case BufferOp::MapBufferRange:
|
||||
return "MapBufferRange";
|
||||
case BufferOp::MapBuffer:
|
||||
return "MapBuffer";
|
||||
case BufferOp::MapNamedBuffer:
|
||||
return "MapNamedBuffer";
|
||||
case BufferOp::MapNamedBufferRange:
|
||||
return "MapNamedBufferRange";
|
||||
case BufferOp::UnmapNamedBuffer:
|
||||
return "UnmapNamedBuffer";
|
||||
case BufferOp::FlushMappedNamedBufferRange:
|
||||
return "FlushMappedNamedBufferRange";
|
||||
case BufferOp::GetNamedBufferParameteriv:
|
||||
return "GetNamedBufferParameteriv";
|
||||
case BufferOp::GetNamedBufferParameteri64v:
|
||||
return "GetNamedBufferParameteri64v";
|
||||
case BufferOp::GetNamedBufferPointerv:
|
||||
return "GetNamedBufferPointerv";
|
||||
default:
|
||||
return "Buffer";
|
||||
}
|
||||
}
|
||||
|
||||
auto& GetBufferBindingSlot(BufferTarget target) {
|
||||
if (target == BufferTarget::Index) {
|
||||
return MG_State::pGLContext->GetBoundVertexArray()->GetIndexBufferBindingSlot();
|
||||
}
|
||||
return MG_State::pGLContext->GetBufferBindingSlot(target);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void GetBufferParameteriv_State(GLenum target, GLenum pname, GLint* params) {
|
||||
if (!params) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetBufferParameteriv_State",
|
||||
"Params pointer cannot be null."));
|
||||
return;
|
||||
SharedPtr<MG_State::GLState::BufferObject> GetBoundBufferObject(GLenum target, BufferOp op) {
|
||||
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
|
||||
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return nullptr;
|
||||
|
||||
auto& bindingSlot = GetBufferBindingSlot(bufferTarget);
|
||||
auto& bufferObject = bindingSlot.GetBoundObject();
|
||||
if (!bufferObject) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
"Buffer target is bound to no buffer object."));
|
||||
return nullptr;
|
||||
}
|
||||
return bufferObject;
|
||||
}
|
||||
|
||||
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
|
||||
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return;
|
||||
|
||||
auto& bindingSlot = GetBufferBindingSlot(bufferTarget);
|
||||
|
||||
auto& bufferObject = bindingSlot.GetBoundObject();
|
||||
if (!bufferObject) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetBufferParameteriv_State",
|
||||
"Buffer target is bound to no buffer object."));
|
||||
return;
|
||||
SharedPtr<MG_State::GLState::BufferObject> GetNamedBufferObject(GLuint buffer, BufferOp op) {
|
||||
if (!BufferImpl::ValidateBufferName(buffer, false)) return nullptr;
|
||||
if (!MG_State::pGLContext->ValidateBufferObject(buffer)) {
|
||||
MG_State::pGLContext->CreateBufferObject(buffer);
|
||||
}
|
||||
auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
|
||||
if (!bufferObject) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
std::format("Buffer object {} does not exist.", buffer)));
|
||||
}
|
||||
return bufferObject;
|
||||
}
|
||||
|
||||
switch (pname) {
|
||||
case GL_BUFFER_SIZE:
|
||||
*params = static_cast<GLint>(bufferObject->GetSize());
|
||||
break;
|
||||
case GL_BUFFER_USAGE:
|
||||
*params = (GLint)MG_Util::ConvertBufferUsageToGLEnum(bufferObject->GetUsage());
|
||||
break;
|
||||
case GL_BUFFER_ACCESS:
|
||||
if (bufferObject->IsMapped()) {
|
||||
auto access = bufferObject->GetMappingAccess();
|
||||
if (access & BufferMappingAccessBit::Read && access & BufferMappingAccessBit::Write) {
|
||||
*params = GL_READ_WRITE;
|
||||
} else if (access & BufferMappingAccessBit::Read) {
|
||||
*params = GL_READ_ONLY;
|
||||
} else if (access & BufferMappingAccessBit::Write) {
|
||||
*params = GL_WRITE_ONLY;
|
||||
Bool ValidateStorageFlags(GLbitfield flags, BufferOp op) {
|
||||
constexpr GLbitfield validFlags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT |
|
||||
GL_MAP_COHERENT_BIT | GL_DYNAMIC_STORAGE_BIT | GL_CLIENT_STORAGE_BIT;
|
||||
if ((flags & ~validFlags) != 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
std::format("Invalid buffer storage flags: 0x{:X}", flags)));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & GL_MAP_PERSISTENT_BIT) && !(flags & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT))) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
"GL_MAP_PERSISTENT_BIT requires GL_MAP_READ_BIT or GL_MAP_WRITE_BIT."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & GL_MAP_COHERENT_BIT) && !(flags & GL_MAP_PERSISTENT_BIT)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
"GL_MAP_COHERENT_BIT requires GL_MAP_PERSISTENT_BIT."));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool ValidateImmutableMapAccess(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject,
|
||||
Flags<BufferMappingAccessBit> accessBits, BufferOp op) {
|
||||
if (!bufferObject->IsImmutableStorage()) {
|
||||
if (accessBits & (BufferMappingAccessBit::Persistent | BufferMappingAccessBit::Coherent)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>(
|
||||
"MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
"Persistent or coherent mapping requires immutable buffer storage."));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const GLbitfield storageFlags = bufferObject->GetStorageFlags();
|
||||
if ((accessBits & BufferMappingAccessBit::Read) && !(storageFlags & GL_MAP_READ_BIT)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
"GL_MAP_READ_BIT is not allowed by buffer storage flags."));
|
||||
return false;
|
||||
}
|
||||
if ((accessBits & BufferMappingAccessBit::Write) && !(storageFlags & GL_MAP_WRITE_BIT)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
"GL_MAP_WRITE_BIT is not allowed by buffer storage flags."));
|
||||
return false;
|
||||
}
|
||||
if ((accessBits & BufferMappingAccessBit::Persistent) && !(storageFlags & GL_MAP_PERSISTENT_BIT)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
"GL_MAP_PERSISTENT_BIT is not allowed by buffer storage flags."));
|
||||
return false;
|
||||
}
|
||||
if ((accessBits & BufferMappingAccessBit::Coherent) && !(storageFlags & GL_MAP_COHERENT_BIT)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
"GL_MAP_COHERENT_BIT is not allowed by buffer storage flags."));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetBufferParameteriv_Object(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject, GLenum pname,
|
||||
GLint* params, BufferOp op) {
|
||||
if (!params) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
"Params pointer cannot be null."));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pname) {
|
||||
case GL_BUFFER_SIZE:
|
||||
*params = static_cast<GLint>(bufferObject->GetSize());
|
||||
break;
|
||||
case GL_BUFFER_USAGE:
|
||||
*params = (GLint)MG_Util::ConvertBufferUsageToGLEnum(bufferObject->GetUsage());
|
||||
break;
|
||||
case GL_BUFFER_ACCESS:
|
||||
if (bufferObject->IsMapped()) {
|
||||
auto access = bufferObject->GetMappingAccess();
|
||||
if (access & BufferMappingAccessBit::Read && access & BufferMappingAccessBit::Write) {
|
||||
*params = GL_READ_WRITE;
|
||||
} else if (access & BufferMappingAccessBit::Read) {
|
||||
*params = GL_READ_ONLY;
|
||||
} else if (access & BufferMappingAccessBit::Write) {
|
||||
*params = GL_WRITE_ONLY;
|
||||
} else {
|
||||
*params = 0;
|
||||
}
|
||||
} else {
|
||||
*params = 0;
|
||||
}
|
||||
} else {
|
||||
*params = 0;
|
||||
break;
|
||||
case GL_BUFFER_MAPPED:
|
||||
*params = bufferObject->IsMapped() ? GL_TRUE : GL_FALSE;
|
||||
break;
|
||||
case GL_BUFFER_IMMUTABLE_STORAGE:
|
||||
*params = bufferObject->IsImmutableStorage() ? GL_TRUE : GL_FALSE;
|
||||
break;
|
||||
case GL_BUFFER_STORAGE_FLAGS:
|
||||
*params = static_cast<GLint>(bufferObject->GetStorageFlags());
|
||||
break;
|
||||
case GL_BUFFER_MAP_OFFSET:
|
||||
*params = static_cast<GLint>(bufferObject->GetMappedRange().start);
|
||||
break;
|
||||
case GL_BUFFER_MAP_LENGTH:
|
||||
*params = static_cast<GLint>(bufferObject->GetMappedRange().end - bufferObject->GetMappedRange().start);
|
||||
break;
|
||||
default:
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
std::format("Invalid pname enum: 0x{:X}", pname)));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GL_BUFFER_MAPPED:
|
||||
*params = bufferObject->IsMapped() ? GL_TRUE : GL_FALSE;
|
||||
break;
|
||||
default:
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetBufferParameteriv_State",
|
||||
std::format("Invalid pname enum: 0x{:X}", pname)));
|
||||
break;
|
||||
}
|
||||
|
||||
void GetBufferParameteri64v_Object(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject, GLenum pname,
|
||||
GLint64* params, BufferOp op) {
|
||||
if (!params) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
"Params pointer cannot be null."));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pname) {
|
||||
case GL_BUFFER_SIZE:
|
||||
*params = static_cast<GLint64>(bufferObject->GetSize());
|
||||
break;
|
||||
case GL_BUFFER_MAP_OFFSET:
|
||||
*params = static_cast<GLint64>(bufferObject->GetMappedRange().start);
|
||||
break;
|
||||
case GL_BUFFER_MAP_LENGTH:
|
||||
*params = static_cast<GLint64>(bufferObject->GetMappedRange().end - bufferObject->GetMappedRange().start);
|
||||
break;
|
||||
default: {
|
||||
GLint value = 0;
|
||||
GetBufferParameteriv_Object(bufferObject, pname, &value, op);
|
||||
*params = static_cast<GLint64>(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetBufferPointerv_Object(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject, GLenum pname,
|
||||
void** params, BufferOp op) {
|
||||
if (!params) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
"Params pointer cannot be null."));
|
||||
return;
|
||||
}
|
||||
if (pname != GL_BUFFER_MAP_POINTER) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||
std::format("Invalid pname enum: 0x{:X}", pname)));
|
||||
return;
|
||||
}
|
||||
*params = bufferObject->GetMappedPointer();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void GetBufferParameteriv_State(GLenum target, GLenum pname, GLint* params) {
|
||||
auto bufferObject = GetBoundBufferObject(target, BufferOp::GetBufferParameteriv);
|
||||
if (!bufferObject) return;
|
||||
GetBufferParameteriv_Object(bufferObject, pname, params, BufferOp::GetBufferParameteriv);
|
||||
}
|
||||
|
||||
void GetBufferParameteri64v_State(GLenum target, GLenum pname, GLint64* params) {
|
||||
auto bufferObject = GetBoundBufferObject(target, BufferOp::GetBufferParameteri64v);
|
||||
if (!bufferObject) return;
|
||||
GetBufferParameteri64v_Object(bufferObject, pname, params, BufferOp::GetBufferParameteri64v);
|
||||
}
|
||||
|
||||
void GetBufferPointerv_State(GLenum target, GLenum pname, void** params) {
|
||||
auto bufferObject = GetBoundBufferObject(target, BufferOp::GetBufferPointerv);
|
||||
if (!bufferObject) return;
|
||||
GetBufferPointerv_Object(bufferObject, pname, params, BufferOp::GetBufferPointerv);
|
||||
}
|
||||
|
||||
void DeleteBuffers_State(GLsizei n, const GLuint* buffers) {
|
||||
@@ -133,10 +359,11 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return;
|
||||
}
|
||||
|
||||
if (offset + length > bufferObject->GetSize()) {
|
||||
const auto mappedRange = bufferObject->GetMappedRange();
|
||||
if (static_cast<SizeT>(offset) + static_cast<SizeT>(length) > mappedRange.end - mappedRange.start) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "FlushMappedBufferRange_State",
|
||||
"Offset and length exceed buffer size."));
|
||||
"Offset and length exceed mapped range."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -250,18 +477,24 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
}
|
||||
|
||||
const auto storageFlags = BufferMappingAccessBit::Persistent | BufferMappingAccessBit::Coherent;
|
||||
auto requiredFlags = accessBits & storageFlags;
|
||||
if (requiredFlags) {
|
||||
// TODO: check if the buffer data is created by BufferStorage and its flags after its
|
||||
// implementation
|
||||
if ((accessBits & BufferMappingAccessBit::Persistent) && !(accessBits & (BufferMappingAccessBit::Read | BufferMappingAccessBit::Write))) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapBufferRange_State",
|
||||
"Access flags require matching storage flags in buffer."));
|
||||
"GL_MAP_PERSISTENT_BIT requires GL_MAP_READ_BIT or GL_MAP_WRITE_BIT."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((accessBits & BufferMappingAccessBit::Coherent) && !(accessBits & BufferMappingAccessBit::Persistent)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapBufferRange_State",
|
||||
"GL_MAP_COHERENT_BIT requires GL_MAP_PERSISTENT_BIT."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ValidateImmutableMapAccess(bufferObject, accessBits, BufferOp::MapBufferRange)) return nullptr;
|
||||
|
||||
if (bufferObject->IsMapped()) {
|
||||
const auto invalidateFlags =
|
||||
BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer;
|
||||
@@ -319,6 +552,11 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Flags<BufferMappingAccessBit> accessBits = BufferMappingAccessBit::Null;
|
||||
if (readable) accessBits |= BufferMappingAccessBit::Read;
|
||||
if (writable) accessBits |= BufferMappingAccessBit::Write;
|
||||
if (!ValidateImmutableMapAccess(bufferObject, accessBits, BufferOp::MapBuffer)) return nullptr;
|
||||
|
||||
void* result = bufferObject->AcquireMemory(true, readable, writable);
|
||||
if (!result) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
@@ -421,18 +659,35 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return;
|
||||
}
|
||||
|
||||
SizeT bufferSize = bufferObject->GetSize();
|
||||
Range1D mappedRange = bufferObject->GetMappedRange();
|
||||
if ((offset < mappedRange.end) && (offset + size > mappedRange.start)) {
|
||||
if (bufferObject->IsImmutableStorage() && !(bufferObject->GetStorageFlags() & GL_DYNAMIC_STORAGE_BIT)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>(
|
||||
"MG_Impl/GLImpl", "BufferSubData_State",
|
||||
"Offset and size must not overlap with the mapped range of the buffer object."));
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "BufferSubData_State",
|
||||
"Immutable buffer storage was not created with GL_DYNAMIC_STORAGE_BIT."));
|
||||
return;
|
||||
}
|
||||
|
||||
SizeT bufferSize = bufferObject->GetSize();
|
||||
if (static_cast<SizeT>(offset) + static_cast<SizeT>(size) > bufferSize) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "BufferSubData_State",
|
||||
"Offset and size exceed buffer size."));
|
||||
return;
|
||||
}
|
||||
|
||||
Range1D mappedRange = bufferObject->GetMappedRange();
|
||||
auto mappingAccess = bufferObject->GetMappingAccess();
|
||||
if (bufferObject->IsMapped() && !(mappingAccess & BufferMappingAccessBit::Persistent) &&
|
||||
(offset < mappedRange.end) && (offset + size > mappedRange.start)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>(
|
||||
"MG_Impl/GLImpl", "BufferSubData_State",
|
||||
"Cannot modify a non-persistently mapped buffer object."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bufferObject->IsMapped() && !(mappingAccess & BufferMappingAccessBit::Persistent)) {
|
||||
Range1D mappedRange = bufferObject->GetMappedRange();
|
||||
if (offset + size >= mappedRange.start) {
|
||||
@@ -474,6 +729,14 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bufferObject->IsImmutableStorage()) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "BufferData_State",
|
||||
"Cannot call glBufferData on immutable buffer storage."));
|
||||
return;
|
||||
}
|
||||
|
||||
bufferObject->SetUsage(bufferUsage);
|
||||
bufferObject->Resize(size);
|
||||
if (data) {
|
||||
@@ -481,6 +744,305 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
}
|
||||
}
|
||||
|
||||
void BufferStorage_State(GLenum target, GLsizeiptr size, const void* data, GLbitfield flags) {
|
||||
if (size <= 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "BufferStorage_State", "Size must be positive."));
|
||||
return;
|
||||
}
|
||||
if (!ValidateStorageFlags(flags, BufferOp::BufferStorage)) return;
|
||||
|
||||
auto bufferObject = GetBoundBufferObject(target, BufferOp::BufferStorage);
|
||||
if (!bufferObject) return;
|
||||
if (bufferObject->IsImmutableStorage()) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "BufferStorage_State",
|
||||
"Buffer already has immutable storage."));
|
||||
return;
|
||||
}
|
||||
bufferObject->AllocateImmutableStorage(static_cast<SizeT>(size), data, flags);
|
||||
}
|
||||
|
||||
void NamedBufferStorage_State(GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) {
|
||||
if (size <= 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "NamedBufferStorage_State", "Size must be positive."));
|
||||
return;
|
||||
}
|
||||
if (!ValidateStorageFlags(flags, BufferOp::NamedBufferStorage)) return;
|
||||
|
||||
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::NamedBufferStorage);
|
||||
if (!bufferObject) return;
|
||||
if (bufferObject->IsImmutableStorage()) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "NamedBufferStorage_State",
|
||||
"Buffer already has immutable storage."));
|
||||
return;
|
||||
}
|
||||
bufferObject->AllocateImmutableStorage(static_cast<SizeT>(size), data, flags);
|
||||
}
|
||||
|
||||
void NamedBufferData_State(GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) {
|
||||
if (size < 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "NamedBufferData_State", "Size must be non-negative."));
|
||||
return;
|
||||
}
|
||||
|
||||
BufferUsage bufferUsage = MG_Util::ConvertGLEnumToBufferUsage(usage);
|
||||
if (!BufferImpl::ValidateBufferUsage(bufferUsage)) return;
|
||||
|
||||
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::NamedBufferData);
|
||||
if (!bufferObject) return;
|
||||
|
||||
if (bufferObject->IsImmutableStorage()) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "NamedBufferData_State",
|
||||
"Cannot call glNamedBufferData on immutable buffer storage."));
|
||||
return;
|
||||
}
|
||||
|
||||
bufferObject->SetUsage(bufferUsage);
|
||||
bufferObject->Resize(size);
|
||||
if (data) {
|
||||
bufferObject->UploadData({(void*)data, (SizeT)size}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void NamedBufferSubData_State(GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data) {
|
||||
if (!data) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::NoError,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "NamedBufferSubData_State",
|
||||
"Data pointer cannot be null."));
|
||||
return;
|
||||
}
|
||||
if (size < 0 || offset < 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "NamedBufferSubData_State",
|
||||
"Offset and size must be non-negative."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::NamedBufferSubData);
|
||||
if (!bufferObject) return;
|
||||
|
||||
if (bufferObject->IsImmutableStorage() && !(bufferObject->GetStorageFlags() & GL_DYNAMIC_STORAGE_BIT)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "NamedBufferSubData_State",
|
||||
"Immutable buffer storage was not created with GL_DYNAMIC_STORAGE_BIT."));
|
||||
return;
|
||||
}
|
||||
if (static_cast<SizeT>(offset) + static_cast<SizeT>(size) > bufferObject->GetSize()) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "NamedBufferSubData_State",
|
||||
"Offset and size exceed buffer size."));
|
||||
return;
|
||||
}
|
||||
const auto mappingAccess = bufferObject->GetMappingAccess();
|
||||
const auto mappedRange = bufferObject->GetMappedRange();
|
||||
if (bufferObject->IsMapped() && !(mappingAccess & BufferMappingAccessBit::Persistent) &&
|
||||
(offset < mappedRange.end) && (offset + size > mappedRange.start)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "NamedBufferSubData_State",
|
||||
"Cannot modify a non-persistently mapped buffer object."));
|
||||
return;
|
||||
}
|
||||
|
||||
bufferObject->UploadSubData({(void*)data, (SizeT)size}, offset);
|
||||
}
|
||||
|
||||
void* MapNamedBuffer_State(GLuint buffer, GLenum access) {
|
||||
Bool readable = access == GL_READ_ONLY || access == GL_READ_WRITE;
|
||||
Bool writable = access == GL_WRITE_ONLY || access == GL_READ_WRITE;
|
||||
if (access != GL_READ_ONLY && access != GL_WRITE_ONLY && access != GL_READ_WRITE) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidEnum,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapNamedBuffer_State",
|
||||
"Access must be one of GL_READ_ONLY, GL_WRITE_ONLY, or GL_READ_WRITE."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::MapNamedBuffer);
|
||||
if (!bufferObject) return nullptr;
|
||||
if (bufferObject->IsMapped()) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapNamedBuffer_State",
|
||||
"Cannot map a buffer object that is already mapped."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Flags<BufferMappingAccessBit> accessBits = BufferMappingAccessBit::Null;
|
||||
if (readable) accessBits |= BufferMappingAccessBit::Read;
|
||||
if (writable) accessBits |= BufferMappingAccessBit::Write;
|
||||
if (!ValidateImmutableMapAccess(bufferObject, accessBits, BufferOp::MapNamedBuffer)) return nullptr;
|
||||
|
||||
return bufferObject->AcquireMemory(true, readable, writable);
|
||||
}
|
||||
|
||||
void* MapNamedBufferRange_State(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access) {
|
||||
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::MapNamedBufferRange);
|
||||
if (!bufferObject) return nullptr;
|
||||
|
||||
if (length < 0 || offset < 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapNamedBufferRange_State",
|
||||
"Offset and length must be non-negative."));
|
||||
return nullptr;
|
||||
}
|
||||
if (length == 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapNamedBufferRange_State",
|
||||
"Length must be greater than zero."));
|
||||
return nullptr;
|
||||
}
|
||||
if (static_cast<SizeT>(offset) + static_cast<SizeT>(length) > bufferObject->GetSize()) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapNamedBufferRange_State",
|
||||
"Offset and length exceed buffer size."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto accessBits = MG_Util::ConvertGLEnumToBufferMappingAccess(access);
|
||||
if (!BufferImpl::ValidateBufferMappingAccess(accessBits)) return nullptr;
|
||||
if (!(accessBits & (BufferMappingAccessBit::Read | BufferMappingAccessBit::Write))) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapNamedBufferRange_State",
|
||||
"At least one of GL_MAP_READ_BIT or GL_MAP_WRITE_BIT must be set."));
|
||||
return nullptr;
|
||||
}
|
||||
if (accessBits & BufferMappingAccessBit::Read) {
|
||||
const auto invalidFlags = BufferMappingAccessBit::InvalidateRange |
|
||||
BufferMappingAccessBit::InvalidateBuffer | BufferMappingAccessBit::Unsynchronized;
|
||||
if (accessBits & invalidFlags) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapNamedBufferRange_State",
|
||||
"GL_MAP_READ_BIT cannot be combined with invalidation or unsynchronized flags."));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if ((accessBits & BufferMappingAccessBit::FlushExplicit) && !(accessBits & BufferMappingAccessBit::Write)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapNamedBufferRange_State",
|
||||
"GL_MAP_FLUSH_EXPLICIT_BIT requires GL_MAP_WRITE_BIT."));
|
||||
return nullptr;
|
||||
}
|
||||
if ((accessBits & BufferMappingAccessBit::Persistent) && !(accessBits & (BufferMappingAccessBit::Read | BufferMappingAccessBit::Write))) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapNamedBufferRange_State",
|
||||
"GL_MAP_PERSISTENT_BIT requires GL_MAP_READ_BIT or GL_MAP_WRITE_BIT."));
|
||||
return nullptr;
|
||||
}
|
||||
if ((accessBits & BufferMappingAccessBit::Coherent) && !(accessBits & BufferMappingAccessBit::Persistent)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapNamedBufferRange_State",
|
||||
"GL_MAP_COHERENT_BIT requires GL_MAP_PERSISTENT_BIT."));
|
||||
return nullptr;
|
||||
}
|
||||
if (!ValidateImmutableMapAccess(bufferObject, accessBits, BufferOp::MapNamedBufferRange)) return nullptr;
|
||||
|
||||
if (bufferObject->IsMapped()) {
|
||||
const auto invalidateFlags =
|
||||
BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer;
|
||||
if (!(accessBits & invalidateFlags)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "MapNamedBufferRange_State",
|
||||
"Cannot map a buffer object that is already mapped."));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return bufferObject->AcquireMemoryRange({static_cast<SizeT>(offset), static_cast<SizeT>(offset + length)},
|
||||
accessBits);
|
||||
}
|
||||
|
||||
GLboolean UnmapNamedBuffer_State(GLuint buffer) {
|
||||
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::UnmapNamedBuffer);
|
||||
if (!bufferObject) return GL_FALSE;
|
||||
if (!bufferObject->IsMapped()) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "UnmapNamedBuffer_State",
|
||||
"Cannot unmap a buffer object that is not mapped."));
|
||||
return GL_FALSE;
|
||||
}
|
||||
bufferObject->ReleaseMemory();
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
void FlushMappedNamedBufferRange_State(GLuint buffer, GLintptr offset, GLsizeiptr length) {
|
||||
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::FlushMappedNamedBufferRange);
|
||||
if (!bufferObject) return;
|
||||
if (length < 0 || offset < 0) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "FlushMappedNamedBufferRange_State",
|
||||
"Offset and length must be non-negative."));
|
||||
return;
|
||||
}
|
||||
if (!bufferObject->IsMapped()) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "FlushMappedNamedBufferRange_State",
|
||||
"Cannot flush a buffer object that is not mapped."));
|
||||
return;
|
||||
}
|
||||
const auto mappedRange = bufferObject->GetMappedRange();
|
||||
if (static_cast<SizeT>(offset) + static_cast<SizeT>(length) > mappedRange.end - mappedRange.start) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "FlushMappedNamedBufferRange_State",
|
||||
"Offset and length exceed mapped range."));
|
||||
return;
|
||||
}
|
||||
if (!(bufferObject->GetMappingAccess() & BufferMappingAccessBit::FlushExplicit)) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidOperation,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "FlushMappedNamedBufferRange_State",
|
||||
"Cannot flush a buffer object that is not mapped with GL_MAP_FLUSH_EXPLICIT_BIT."));
|
||||
return;
|
||||
}
|
||||
bufferObject->FlushMemoryRange(static_cast<SizeT>(offset), static_cast<SizeT>(length));
|
||||
}
|
||||
|
||||
void GetNamedBufferParameteriv_State(GLuint buffer, GLenum pname, GLint* params) {
|
||||
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::GetNamedBufferParameteriv);
|
||||
if (!bufferObject) return;
|
||||
GetBufferParameteriv_Object(bufferObject, pname, params, BufferOp::GetNamedBufferParameteriv);
|
||||
}
|
||||
|
||||
void GetNamedBufferParameteri64v_State(GLuint buffer, GLenum pname, GLint64* params) {
|
||||
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::GetNamedBufferParameteri64v);
|
||||
if (!bufferObject) return;
|
||||
GetBufferParameteri64v_Object(bufferObject, pname, params, BufferOp::GetNamedBufferParameteri64v);
|
||||
}
|
||||
|
||||
void GetNamedBufferPointerv_State(GLuint buffer, GLenum pname, void** params) {
|
||||
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::GetNamedBufferPointerv);
|
||||
if (!bufferObject) return;
|
||||
GetBufferPointerv_Object(bufferObject, pname, params, BufferOp::GetNamedBufferPointerv);
|
||||
}
|
||||
|
||||
void BindBuffer_State(GLenum target, GLuint buffer) {
|
||||
if (!BufferImpl::ValidateBufferName(buffer, true)) return;
|
||||
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
|
||||
@@ -558,6 +1120,14 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
GetBufferParameteriv_State(target, pname, params);
|
||||
}
|
||||
|
||||
void GetBufferParameteri64v(GLenum target, GLenum pname, GLint64* params) {
|
||||
GetBufferParameteri64v_State(target, pname, params);
|
||||
}
|
||||
|
||||
void GetBufferPointerv(GLenum target, GLenum pname, void** params) {
|
||||
GetBufferPointerv_State(target, pname, params);
|
||||
}
|
||||
|
||||
GLboolean IsBuffer(GLuint buffer) {
|
||||
return IsBuffer_State(buffer);
|
||||
}
|
||||
@@ -582,6 +1152,50 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return MapBuffer_State(target, access);
|
||||
}
|
||||
|
||||
void BufferStorage(GLenum target, GLsizeiptr size, const void* data, GLbitfield flags) {
|
||||
BufferStorage_State(target, size, data, flags);
|
||||
}
|
||||
|
||||
void NamedBufferStorage(GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) {
|
||||
NamedBufferStorage_State(buffer, size, data, flags);
|
||||
}
|
||||
|
||||
void NamedBufferData(GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) {
|
||||
NamedBufferData_State(buffer, size, data, usage);
|
||||
}
|
||||
|
||||
void NamedBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data) {
|
||||
NamedBufferSubData_State(buffer, offset, size, data);
|
||||
}
|
||||
|
||||
void* MapNamedBuffer(GLuint buffer, GLenum access) {
|
||||
return MapNamedBuffer_State(buffer, access);
|
||||
}
|
||||
|
||||
void* MapNamedBufferRange(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access) {
|
||||
return MapNamedBufferRange_State(buffer, offset, length, access);
|
||||
}
|
||||
|
||||
GLboolean UnmapNamedBuffer(GLuint buffer) {
|
||||
return UnmapNamedBuffer_State(buffer);
|
||||
}
|
||||
|
||||
void FlushMappedNamedBufferRange(GLuint buffer, GLintptr offset, GLsizeiptr length) {
|
||||
FlushMappedNamedBufferRange_State(buffer, offset, length);
|
||||
}
|
||||
|
||||
void GetNamedBufferParameteriv(GLuint buffer, GLenum pname, GLint* params) {
|
||||
GetNamedBufferParameteriv_State(buffer, pname, params);
|
||||
}
|
||||
|
||||
void GetNamedBufferParameteri64v(GLuint buffer, GLenum pname, GLint64* params) {
|
||||
GetNamedBufferParameteri64v_State(buffer, pname, params);
|
||||
}
|
||||
|
||||
void GetNamedBufferPointerv(GLuint buffer, GLenum pname, void** params) {
|
||||
GetNamedBufferPointerv_State(buffer, pname, params);
|
||||
}
|
||||
|
||||
// FIXME: this should be a "backend" function
|
||||
void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset,
|
||||
GLsizeiptr size) {
|
||||
|
||||
@@ -12,12 +12,25 @@
|
||||
namespace MobileGL::MG_Impl::GLImpl {
|
||||
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
|
||||
void GetBufferParameteriv(GLenum target, GLenum pname, GLint* params);
|
||||
void GetBufferParameteri64v(GLenum target, GLenum pname, GLint64* params);
|
||||
void GetBufferPointerv(GLenum target, GLenum pname, void** params);
|
||||
GLboolean IsBuffer(GLuint buffer);
|
||||
void DeleteBuffers(GLsizei n, const GLuint* buffers);
|
||||
void FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length);
|
||||
GLboolean UnmapBuffer(GLenum target);
|
||||
void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
|
||||
void* MapBuffer(GLenum target, GLenum access);
|
||||
void BufferStorage(GLenum target, GLsizeiptr size, const void* data, GLbitfield flags);
|
||||
void NamedBufferStorage(GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags);
|
||||
void NamedBufferData(GLuint buffer, GLsizeiptr size, const void* data, GLenum usage);
|
||||
void NamedBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data);
|
||||
void* MapNamedBuffer(GLuint buffer, GLenum access);
|
||||
void* MapNamedBufferRange(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access);
|
||||
GLboolean UnmapNamedBuffer(GLuint buffer);
|
||||
void FlushMappedNamedBufferRange(GLuint buffer, GLintptr offset, GLsizeiptr length);
|
||||
void GetNamedBufferParameteriv(GLuint buffer, GLenum pname, GLint* params);
|
||||
void GetNamedBufferParameteri64v(GLuint buffer, GLenum pname, GLint64* params);
|
||||
void GetNamedBufferPointerv(GLuint buffer, GLenum pname, void** params);
|
||||
void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset,
|
||||
GLsizeiptr size);
|
||||
void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "../Buffer/GL_Buffer.h"
|
||||
#include "../Getter/GL_Getter.h"
|
||||
#include "../Sampler/GL_Sampler.h"
|
||||
#include "../Sync/GL_Sync.h"
|
||||
#include "../Texture/GL_Texture.h"
|
||||
#include "../Drawing/GL_Drawing.h"
|
||||
#include "../Program/GL_Program.h"
|
||||
@@ -214,7 +215,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, EndQuery, GLenum target) DECLARE_GL_FUNCTION
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryiv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryiv, target, pname, params)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryObjectuiv, GLuint id, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryObjectuiv, id, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(GLboolean, UnmapBuffer, GLenum target) DECLARE_GL_FUNCTION_END(GLboolean, UnmapBuffer, target)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBufferPointerv, GLenum target, GLenum pname, void** params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBufferPointerv, target, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetBufferPointerv, GLenum target, GLenum pname, void** params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBufferPointerv, target, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, DrawBuffers, GLsizei n, const GLenum* bufs) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawBuffers, n, bufs)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, UniformMatrix2x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, UniformMatrix2x3fv, location, count, transpose, value)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, UniformMatrix3x2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, UniformMatrix3x2fv, location, count, transpose, value)
|
||||
@@ -268,15 +269,15 @@ DECLARE_GL_FUNCTION_HEAD(void, GetActiveUniformBlockName, GLuint program, GLuint
|
||||
DECLARE_GL_FUNCTION_HEAD(void, UniformBlockBinding, GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) DECLARE_GL_FUNCTION_END_NO_RETURN(void, UniformBlockBinding, program, uniformBlockIndex, uniformBlockBinding)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, DrawArraysInstanced, GLenum mode, GLint first, GLsizei count, GLsizei instancecount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawArraysInstanced, mode, first, count, instancecount)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, DrawElementsInstanced, GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawElementsInstanced, mode, count, type, indices, instancecount)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLsync, FenceSync, GLenum condition, GLbitfield flags) DECLARE_GL_FUNCTION_STUB_END(GLsync, FenceSync, condition, flags)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, IsSync, GLsync sync) DECLARE_GL_FUNCTION_STUB_END(GLboolean, IsSync, sync)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteSync, GLsync sync) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteSync, sync)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLenum, ClientWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) DECLARE_GL_FUNCTION_STUB_END(GLenum, ClientWaitSync, sync, flags, timeout)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, WaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, WaitSync, sync, flags, timeout)
|
||||
DECLARE_GL_FUNCTION_HEAD(GLsync, FenceSync, GLenum condition, GLbitfield flags) DECLARE_GL_FUNCTION_END(GLsync, FenceSync, condition, flags)
|
||||
DECLARE_GL_FUNCTION_HEAD(GLboolean, IsSync, GLsync sync) DECLARE_GL_FUNCTION_END(GLboolean, IsSync, sync)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, DeleteSync, GLsync sync) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteSync, sync)
|
||||
DECLARE_GL_FUNCTION_HEAD(GLenum, ClientWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) DECLARE_GL_FUNCTION_END(GLenum, ClientWaitSync, sync, flags, timeout)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, WaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) DECLARE_GL_FUNCTION_END_NO_RETURN(void, WaitSync, sync, flags, timeout)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetInteger64v, GLenum pname, GLint64* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetInteger64v, pname, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetSynciv, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetSynciv, sync, pname, bufSize, length, values)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetSynciv, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetSynciv, sync, pname, bufSize, length, values)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetInteger64i_v, GLenum target, GLuint index, GLint64* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetInteger64i_v, target, index, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBufferParameteri64v, GLenum target, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBufferParameteri64v, target, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetBufferParameteri64v, GLenum target, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBufferParameteri64v, target, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GenSamplers, GLsizei count, GLuint* samplers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenSamplers, count, samplers)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, DeleteSamplers, GLsizei count, const GLuint* samplers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteSamplers, count, samplers)
|
||||
DECLARE_GL_FUNCTION_HEAD(GLboolean, IsSampler, GLuint sampler) DECLARE_GL_FUNCTION_END(GLboolean, IsSampler, sampler)
|
||||
@@ -979,7 +980,7 @@ DECLARE_GL_FUNCTION_HEAD(GLint, GetProgramResourceLocationIndex, GLuint program,
|
||||
DECLARE_GL_FUNCTION_HEAD(void, ShaderStorageBlockBinding, GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ShaderStorageBlockBinding, program, storageBlockIndex, storageBlockBinding)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureView, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureView, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribLFormat, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribLFormat, attribindex, size, type, relativeoffset)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, BufferStorage, GLenum target, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BufferStorage, target, size, data, flags)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, BufferStorage, GLenum target, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BufferStorage, target, size, data, flags)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearTexImage, GLuint texture, GLint level, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearTexImage, texture, level, format, type, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearTexSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearTexSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindBuffersBase, GLenum target, GLuint first, GLsizei count, const GLuint* buffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindBuffersBase, target, first, count, buffers)
|
||||
@@ -996,17 +997,17 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTransformFeedbackiv, GLuint xfb, GLenum p
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTransformFeedbacki_v, GLuint xfb, GLenum pname, GLuint index, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTransformFeedbacki_v, xfb, pname, index, param)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTransformFeedbacki64_v, GLuint xfb, GLenum pname, GLuint index, GLint64* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTransformFeedbacki64_v, xfb, pname, index, param)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateBuffers, GLsizei n, GLuint* buffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateBuffers, n, buffers)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedBufferStorage, GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedBufferStorage, buffer, size, data, flags)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedBufferData, GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedBufferData, buffer, size, data, usage)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedBufferSubData, GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedBufferSubData, buffer, offset, size, data)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferStorage, GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferStorage, buffer, size, data, flags)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferData, GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferData, buffer, size, data, usage)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferSubData, GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferSubData, buffer, offset, size, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyNamedBufferSubData, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyNamedBufferSubData, readBuffer, writeBuffer, readOffset, writeOffset, size)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedBufferData, GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedBufferData, buffer, internalformat, format, type, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedBufferSubData, GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedBufferSubData, buffer, internalformat, offset, size, format, type, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, UnmapNamedBuffer, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END(GLboolean, UnmapNamedBuffer, buffer)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, FlushMappedNamedBufferRange, GLuint buffer, GLintptr offset, GLsizeiptr length) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FlushMappedNamedBufferRange, buffer, offset, length)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferParameteriv, GLuint buffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferParameteriv, buffer, pname, params)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferParameteri64v, GLuint buffer, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferParameteri64v, buffer, pname, params)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferPointerv, GLuint buffer, GLenum pname, void** params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferPointerv, buffer, pname, *params)
|
||||
DECLARE_GL_FUNCTION_HEAD(GLboolean, UnmapNamedBuffer, GLuint buffer) DECLARE_GL_FUNCTION_END(GLboolean, UnmapNamedBuffer, buffer)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, FlushMappedNamedBufferRange, GLuint buffer, GLintptr offset, GLsizeiptr length) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FlushMappedNamedBufferRange, buffer, offset, length)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferParameteriv, GLuint buffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferParameteriv, buffer, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferParameteri64v, GLuint buffer, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferParameteri64v, buffer, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferPointerv, GLuint buffer, GLenum pname, void** params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferPointerv, buffer, pname, params)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferSubData, GLuint buffer, GLintptr offset, GLsizeiptr size, void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferSubData, buffer, offset, size, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateFramebuffers, GLsizei n, GLuint* framebuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateFramebuffers, n, framebuffers)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferRenderbuffer, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferRenderbuffer, framebuffer, attachment, renderbuffertarget, renderbuffer)
|
||||
@@ -1844,11 +1845,11 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixLoadTransposefEXT, GLenum mode, const
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixLoadTransposedEXT, GLenum mode, const GLdouble* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MatrixLoadTransposedEXT, mode, m)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixMultTransposefEXT, GLenum mode, const GLfloat* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MatrixMultTransposefEXT, mode, m)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixMultTransposedEXT, GLenum mode, const GLdouble* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MatrixMultTransposedEXT, mode, m)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedBufferDataEXT, GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedBufferDataEXT, buffer, size, data, usage)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedBufferSubDataEXT, GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedBufferSubDataEXT, buffer, offset, size, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, UnmapNamedBufferEXT, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END(GLboolean, UnmapNamedBufferEXT, buffer)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferParameterivEXT, GLuint buffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferParameterivEXT, buffer, pname, params)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferPointervEXT, GLuint buffer, GLenum pname, void** params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferPointervEXT, buffer, pname, *params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferDataEXT, GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferData, buffer, size, data, usage)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferSubDataEXT, GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferSubData, buffer, offset, size, data)
|
||||
DECLARE_GL_FUNCTION_HEAD(GLboolean, UnmapNamedBufferEXT, GLuint buffer) DECLARE_GL_FUNCTION_END(GLboolean, UnmapNamedBuffer, buffer)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferParameterivEXT, GLuint buffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferParameteriv, buffer, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferPointervEXT, GLuint buffer, GLenum pname, void** params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferPointerv, buffer, pname, params)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferSubDataEXT, GLuint buffer, GLintptr offset, GLsizeiptr size, void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferSubDataEXT, buffer, offset, size, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBufferEXT, GLuint texture, GLenum target, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBufferEXT, texture, target, internalformat, buffer)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiTexBufferEXT, GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiTexBufferEXT, texunit, target, internalformat, buffer)
|
||||
@@ -1923,8 +1924,8 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayIntegervEXT, GLuint vaobj, GLe
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayPointervEXT, GLuint vaobj, GLenum pname, void** param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayPointervEXT, vaobj, pname, *param)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayIntegeri_vEXT, GLuint vaobj, GLuint index, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayIntegeri_vEXT, vaobj, index, pname, param)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayPointeri_vEXT, GLuint vaobj, GLuint index, GLenum pname, void** param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayPointeri_vEXT, vaobj, index, pname, *param)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, FlushMappedNamedBufferRangeEXT, GLuint buffer, GLintptr offset, GLsizeiptr length) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FlushMappedNamedBufferRangeEXT, buffer, offset, length)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedBufferStorageEXT, GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedBufferStorageEXT, buffer, size, data, flags)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, FlushMappedNamedBufferRangeEXT, GLuint buffer, GLintptr offset, GLsizeiptr length) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FlushMappedNamedBufferRange, buffer, offset, length)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferStorageEXT, GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferStorage, buffer, size, data, flags)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedBufferDataEXT, GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedBufferDataEXT, buffer, internalformat, format, type, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedBufferSubDataEXT, GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedBufferSubDataEXT, buffer, internalformat, offset, size, format, type, data)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferParameteriEXT, GLuint framebuffer, GLenum pname, GLint param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferParameteriEXT, framebuffer, pname, param)
|
||||
@@ -2963,8 +2964,8 @@ MOBILEGL_GL_API void glGetObjectLabelEXT(GLenum identifier, GLuint name, GLsizei
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void* glMapNamedBuffer(GLuint buffer, GLenum access) {
|
||||
MGLOG_W("Stub function: %s(...)", __FUNCTION__);
|
||||
return nullptr;
|
||||
MGLOG_D("Implementing function: %s(...)", __FUNCTION__);
|
||||
return MobileGL::MG_Impl::GLImpl::MapNamedBuffer(buffer, access);
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void* glMapNamedBufferEXT(GLuint buffer, GLenum access) {
|
||||
@@ -2972,8 +2973,8 @@ MOBILEGL_GL_API void* glMapNamedBufferEXT(GLuint buffer, GLenum access) {
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void* glMapNamedBufferRange(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access) {
|
||||
MGLOG_W("Stub function: %s(...)", __FUNCTION__);
|
||||
return nullptr;
|
||||
MGLOG_D("Implementing function: %s(...)", __FUNCTION__);
|
||||
return MobileGL::MG_Impl::GLImpl::MapNamedBufferRange(buffer, offset, length, access);
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void* glMapNamedBufferRangeEXT(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access) {
|
||||
|
||||
@@ -8,36 +8,121 @@
|
||||
|
||||
#include "GL_Sync.h"
|
||||
|
||||
#include "MG_State/GLState/Core.h"
|
||||
#include <MG_State/GLState/Core.h>
|
||||
#include <MG_State/GLState/ErrorState/Error.h>
|
||||
|
||||
namespace MobileGL::MG_Impl::GLImpl {
|
||||
GLsync FenceSync_Backend(GLenum condition, GLbitfield flags) {
|
||||
return 0;
|
||||
}
|
||||
namespace {
|
||||
struct SyncObject {
|
||||
GLenum Condition = GL_SYNC_GPU_COMMANDS_COMPLETE;
|
||||
GLbitfield Flags = 0;
|
||||
};
|
||||
|
||||
GLenum ClientWaitSync_Backend(GLsync sync, GLbitfield flags, GLuint64 timeout) {
|
||||
return 0;
|
||||
}
|
||||
UnorderedMap<GLsync, UniquePtr<SyncObject>> g_syncObjects;
|
||||
|
||||
void DeleteSync_Backend(GLsync sync) {}
|
||||
void RecordSyncError(const char* funcName, ErrorCode code, String message) {
|
||||
MG_State::pGLContext->RecordError(code,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", funcName, Move(message)));
|
||||
}
|
||||
|
||||
GLsync FenceSync_State(GLenum condition, GLbitfield flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLenum ClientWaitSync_State(GLsync sync, GLbitfield flags, GLuint64 timeout) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DeleteSync_State(GLsync sync) {}
|
||||
SyncObject* GetSyncObject(GLsync sync, const char* funcName) {
|
||||
auto it = g_syncObjects.find(sync);
|
||||
if (sync == nullptr || it == g_syncObjects.end()) {
|
||||
RecordSyncError(funcName, ErrorCode::InvalidValue, "Sync object is not valid.");
|
||||
return nullptr;
|
||||
}
|
||||
return it->second.get();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
GLsync FenceSync(GLenum condition, GLbitfield flags) {
|
||||
return 0;
|
||||
if (condition != GL_SYNC_GPU_COMMANDS_COMPLETE) {
|
||||
RecordSyncError("FenceSync", ErrorCode::InvalidEnum, "Condition must be GL_SYNC_GPU_COMMANDS_COMPLETE.");
|
||||
return nullptr;
|
||||
}
|
||||
if (flags != 0) {
|
||||
RecordSyncError("FenceSync", ErrorCode::InvalidValue, "Flags must be zero.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto syncObject = MakeUnique<SyncObject>();
|
||||
syncObject->Condition = condition;
|
||||
syncObject->Flags = flags;
|
||||
GLsync handle = reinterpret_cast<GLsync>(syncObject.get());
|
||||
g_syncObjects[handle] = Move(syncObject);
|
||||
return handle;
|
||||
}
|
||||
|
||||
GLboolean IsSync(GLsync sync) {
|
||||
return g_syncObjects.find(sync) != g_syncObjects.end() ? GL_TRUE : GL_FALSE;
|
||||
}
|
||||
|
||||
GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
|
||||
return 0;
|
||||
(void)timeout;
|
||||
if ((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) != 0) {
|
||||
RecordSyncError("ClientWaitSync", ErrorCode::InvalidValue,
|
||||
"Flags can only contain GL_SYNC_FLUSH_COMMANDS_BIT.");
|
||||
return GL_WAIT_FAILED;
|
||||
}
|
||||
if (!GetSyncObject(sync, "ClientWaitSync")) return GL_WAIT_FAILED;
|
||||
return GL_ALREADY_SIGNALED;
|
||||
}
|
||||
|
||||
void DeleteSync(GLsync sync) {}
|
||||
void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
|
||||
if (flags != 0) {
|
||||
RecordSyncError("WaitSync", ErrorCode::InvalidValue, "Flags must be zero.");
|
||||
return;
|
||||
}
|
||||
if (timeout != GL_TIMEOUT_IGNORED) {
|
||||
RecordSyncError("WaitSync", ErrorCode::InvalidValue, "Timeout must be GL_TIMEOUT_IGNORED.");
|
||||
return;
|
||||
}
|
||||
(void)GetSyncObject(sync, "WaitSync");
|
||||
}
|
||||
|
||||
void DeleteSync(GLsync sync) {
|
||||
if (sync == nullptr) return;
|
||||
auto it = g_syncObjects.find(sync);
|
||||
if (it == g_syncObjects.end()) {
|
||||
RecordSyncError("DeleteSync", ErrorCode::InvalidValue, "Sync object is not valid.");
|
||||
return;
|
||||
}
|
||||
g_syncObjects.erase(it);
|
||||
}
|
||||
|
||||
void GetSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) {
|
||||
if (bufSize < 0) {
|
||||
RecordSyncError("GetSynciv", ErrorCode::InvalidValue, "bufSize must be non-negative.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto* syncObject = GetSyncObject(sync, "GetSynciv");
|
||||
if (!syncObject) return;
|
||||
|
||||
GLint value = 0;
|
||||
switch (pname) {
|
||||
case GL_OBJECT_TYPE:
|
||||
value = GL_SYNC_FENCE;
|
||||
break;
|
||||
case GL_SYNC_STATUS:
|
||||
value = GL_SIGNALED;
|
||||
break;
|
||||
case GL_SYNC_CONDITION:
|
||||
value = static_cast<GLint>(syncObject->Condition);
|
||||
break;
|
||||
case GL_SYNC_FLAGS:
|
||||
value = static_cast<GLint>(syncObject->Flags);
|
||||
break;
|
||||
default:
|
||||
RecordSyncError("GetSynciv", ErrorCode::InvalidEnum, std::format("Invalid pname enum: 0x{:X}", pname));
|
||||
return;
|
||||
}
|
||||
|
||||
if (length) {
|
||||
*length = bufSize > 0 && values ? 1 : 0;
|
||||
}
|
||||
if (bufSize > 0 && values) {
|
||||
values[0] = value;
|
||||
}
|
||||
}
|
||||
} // namespace MobileGL::MG_Impl::GLImpl
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
namespace MobileGL::MG_Impl::GLImpl {
|
||||
GLsync FenceSync(GLenum condition, GLbitfield flags);
|
||||
GLboolean IsSync(GLsync sync);
|
||||
GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout);
|
||||
void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout);
|
||||
void DeleteSync(GLsync sync);
|
||||
void GetSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values);
|
||||
} // namespace MobileGL::MG_Impl::GLImpl
|
||||
|
||||
@@ -18,19 +18,41 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
|
||||
void BufferObject::Resize(SizeT size) {
|
||||
ReleaseMemory();
|
||||
m_size = size;
|
||||
m_dataPtr->reserve(std::bit_ceil(size)); // power-of-2 reserve
|
||||
m_dataPtr->resize(size);
|
||||
m_isImmutableStorage = false;
|
||||
m_storageFlags = 0;
|
||||
m_change.Bits |= BufferChangeBits::DirtyBit;
|
||||
m_change.Bits |= BufferChangeBits::PreferReallocationBit;
|
||||
++m_changeSerial;
|
||||
}
|
||||
|
||||
void BufferObject::AllocateImmutableStorage(SizeT size, const void* data, GLbitfield storageFlags) {
|
||||
ReleaseMemory();
|
||||
m_size = size;
|
||||
m_dataPtr->reserve(std::bit_ceil(size));
|
||||
m_dataPtr->resize(size);
|
||||
if (data) {
|
||||
Memcpy(m_dataPtr->data(), data, size);
|
||||
} else if (size > 0) {
|
||||
Memset(m_dataPtr->data(), 0, size);
|
||||
}
|
||||
m_isImmutableStorage = true;
|
||||
m_storageFlags = storageFlags;
|
||||
m_change.DirtyRanges.clear();
|
||||
m_change.DirtyRanges.Add({0, size});
|
||||
m_change.Bits = BufferChangeBits::DirtyBit | BufferChangeBits::PreferReallocationBit;
|
||||
++m_changeSerial;
|
||||
}
|
||||
|
||||
void BufferObject::UploadData(DataPtr data, SizeT atOffset) {
|
||||
MOBILEGL_ASSERT(atOffset + data.size <= m_size,
|
||||
"UploadData out of bounds: atOffset (%zu) + data.size (%zu) > m_size (%zu)", atOffset,
|
||||
data.size, m_size);
|
||||
MOBILEGL_ASSERT(!m_isMapped, "Cannot upload data while buffer is mapped.");
|
||||
MOBILEGL_ASSERT(!m_isMapped || (m_mappingAccess & BufferMappingAccessBit::Persistent),
|
||||
"Cannot upload data while buffer is non-persistently mapped.");
|
||||
Memcpy(m_dataPtr->data() + atOffset, data.data, data.size);
|
||||
m_change.DirtyRanges.Add({atOffset, atOffset + data.size});
|
||||
m_change.Bits |= BufferChangeBits::DirtyBit;
|
||||
@@ -52,8 +74,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
|
||||
if (m_mappingAccess & BufferMappingAccessBit::Write) { // if we wrote to the buffer
|
||||
if (!(m_mappingAccess & BufferMappingAccessBit::FlushExplicit)) { // if we didn't flush explicitly
|
||||
Memcpy(m_dataPtr->data() + m_mappedRange.start, m_stagingData.data(),
|
||||
m_mappedRange.end - m_mappedRange.start);
|
||||
if (!(m_mappingAccess & BufferMappingAccessBit::Persistent)) {
|
||||
Memcpy(m_dataPtr->data() + m_mappedRange.start, m_stagingData.data(),
|
||||
m_mappedRange.end - m_mappedRange.start);
|
||||
}
|
||||
m_change.DirtyRanges.Add({m_mappedRange.start, m_mappedRange.end});
|
||||
m_change.Bits |= BufferChangeBits::DirtyBit;
|
||||
++m_changeSerial;
|
||||
@@ -80,14 +104,29 @@ namespace MobileGL::MG_State::GLState {
|
||||
MOBILEGL_ASSERT(end <= m_mappedRange.end, "Flush range out of bounds: mappedRange.end (%zu) < end (%zu)",
|
||||
m_mappedRange.end, end);
|
||||
|
||||
Memcpy(m_dataPtr->data() + start, m_stagingData.data() + offset, length);
|
||||
if (!(m_mappingAccess & BufferMappingAccessBit::Persistent)) {
|
||||
Memcpy(m_dataPtr->data() + start, m_stagingData.data() + offset, length);
|
||||
}
|
||||
m_change.DirtyRanges.Add({start, end});
|
||||
m_change.Bits |= BufferChangeBits::DirtyBit;
|
||||
++m_changeSerial;
|
||||
}
|
||||
|
||||
void BufferObject::MarkPersistentMappedRangeDirty() {
|
||||
if (!m_isMapped) return;
|
||||
if (!(m_mappingAccess & BufferMappingAccessBit::Persistent)) return;
|
||||
if (!(m_mappingAccess & BufferMappingAccessBit::Write)) return;
|
||||
if (m_mappingAccess & BufferMappingAccessBit::FlushExplicit) return;
|
||||
if (m_mappedRange.start >= m_mappedRange.end) return;
|
||||
|
||||
m_change.DirtyRanges.Add(m_mappedRange);
|
||||
m_change.Bits |= BufferChangeBits::DirtyBit;
|
||||
++m_changeSerial;
|
||||
}
|
||||
|
||||
void BufferObject::UploadSubData(DataPtr data, SizeT atOffset) {
|
||||
MOBILEGL_ASSERT(!m_isMapped, "Cannot upload sub data while buffer is mapped.");
|
||||
MOBILEGL_ASSERT(!m_isMapped || (m_mappingAccess & BufferMappingAccessBit::Persistent),
|
||||
"Cannot upload sub data while buffer is non-persistently mapped.");
|
||||
MOBILEGL_ASSERT(atOffset + data.size <= m_size,
|
||||
"UploadSubData out of bounds: atOffset (%zu) + data.size (%zu) > m_size (%zu)", atOffset,
|
||||
data.size, m_size);
|
||||
@@ -101,8 +140,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
|
||||
void BufferObject::CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset, SizeT size) {
|
||||
MOBILEGL_ASSERT(!m_isMapped, "Cannot copy data while buffer is mapped.");
|
||||
MOBILEGL_ASSERT(!src->IsMapped(), "Cannot copy data from a buffer that is mapped.");
|
||||
MOBILEGL_ASSERT(!m_isMapped || (m_mappingAccess & BufferMappingAccessBit::Persistent),
|
||||
"Cannot copy data while destination buffer is non-persistently mapped.");
|
||||
MOBILEGL_ASSERT(!src->IsMapped() || (src->GetMappingAccess() & BufferMappingAccessBit::Persistent),
|
||||
"Cannot copy data from a buffer that is non-persistently mapped.");
|
||||
MOBILEGL_ASSERT(srcOffset + size <= src->GetSize(),
|
||||
"Source buffer copy out of bounds: srcOffset (%zu) + size (%zu) > src->GetSize() (%zu)",
|
||||
srcOffset, size, src->GetSize());
|
||||
@@ -149,6 +190,19 @@ namespace MobileGL::MG_State::GLState {
|
||||
m_mappingAccess = access;
|
||||
m_mappedRange = range;
|
||||
|
||||
m_change.Bits |=
|
||||
!(access & BufferMappingAccessBit::InvalidateBuffer || access & BufferMappingAccessBit::InvalidateRange)
|
||||
? BufferChangeBits::ForbidInvalidationBit
|
||||
: BufferChangeBits::None;
|
||||
m_change.Bits |= !(access & BufferMappingAccessBit::Unsynchronized)
|
||||
? BufferChangeBits::ForbidUnsynchronizationBit
|
||||
: BufferChangeBits::None;
|
||||
|
||||
if (access & BufferMappingAccessBit::Persistent) {
|
||||
m_ownsStagingData = false;
|
||||
return m_dataPtr->data() + range.start;
|
||||
}
|
||||
|
||||
if (access & BufferMappingAccessBit::Write) {
|
||||
m_stagingData.resize(range.end - range.start);
|
||||
m_ownsStagingData = true;
|
||||
@@ -162,14 +216,6 @@ namespace MobileGL::MG_State::GLState {
|
||||
m_ownsStagingData = false;
|
||||
return m_dataPtr->data() + range.start;
|
||||
}
|
||||
|
||||
m_change.Bits |=
|
||||
!(access & BufferMappingAccessBit::InvalidateBuffer || access & BufferMappingAccessBit::InvalidateRange)
|
||||
? BufferChangeBits::ForbidInvalidationBit
|
||||
: BufferChangeBits::None;
|
||||
m_change.Bits |= !(access & BufferMappingAccessBit::Unsynchronized)
|
||||
? BufferChangeBits::ForbidUnsynchronizationBit
|
||||
: BufferChangeBits::None;
|
||||
}
|
||||
|
||||
const SharedPtr<Data>& BufferObject::GetDataReadOnly() const {
|
||||
@@ -185,6 +231,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
Bool BufferObject::IsImmutableStorage() const {
|
||||
return m_isImmutableStorage;
|
||||
}
|
||||
|
||||
BufferUsage BufferObject::GetUsage() const {
|
||||
return m_usage;
|
||||
}
|
||||
@@ -209,10 +259,25 @@ namespace MobileGL::MG_State::GLState {
|
||||
return m_isMapped ? m_mappedRange : Range1D{0, 0};
|
||||
}
|
||||
|
||||
void* BufferObject::GetMappedPointer() const {
|
||||
if (!m_isMapped) return nullptr;
|
||||
if (m_mappingAccess & BufferMappingAccessBit::Persistent) {
|
||||
return const_cast<Uint8*>(m_dataPtr->data()) + m_mappedRange.start;
|
||||
}
|
||||
if (m_ownsStagingData) {
|
||||
return const_cast<Uint8*>(m_stagingData.data());
|
||||
}
|
||||
return const_cast<Uint8*>(m_dataPtr->data()) + m_mappedRange.start;
|
||||
}
|
||||
|
||||
Flags<BufferMappingAccessBit> BufferObject::GetMappingAccess() const {
|
||||
return m_isMapped ? m_mappingAccess : BufferMappingAccessBit::Null;
|
||||
}
|
||||
|
||||
GLbitfield BufferObject::GetStorageFlags() const {
|
||||
return m_storageFlags;
|
||||
}
|
||||
|
||||
Uint BufferObject::GetExternalIndex() const {
|
||||
return m_externalIndex;
|
||||
}
|
||||
|
||||
@@ -80,22 +80,27 @@ namespace MobileGL {
|
||||
BufferObject(Uint externalIndex);
|
||||
|
||||
void Resize(SizeT size);
|
||||
void AllocateImmutableStorage(SizeT size, const void* data, GLbitfield storageFlags);
|
||||
void UploadData(DataPtr data, SizeT atOffset);
|
||||
void SetUsage(BufferUsage usage);
|
||||
void* AcquireMemory(Bool markMapped, Bool read, Bool write);
|
||||
void* AcquireMemoryRange(Range1D range, Flags<BufferMappingAccessBit> access);
|
||||
void ReleaseMemory();
|
||||
void FlushMemoryRange(SizeT offset, SizeT length);
|
||||
void MarkPersistentMappedRangeDirty();
|
||||
void UploadSubData(DataPtr data, SizeT atOffset);
|
||||
void CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset, SizeT size);
|
||||
void ClearDirty();
|
||||
|
||||
Bool IsMapped() const;
|
||||
Bool IsImmutableStorage() const;
|
||||
SizeT GetSize() const;
|
||||
BufferUsage GetUsage() const;
|
||||
Range1D GetMappedRange() const;
|
||||
void* GetMappedPointer() const;
|
||||
const SharedPtr<Data>& GetDataReadOnly() const;
|
||||
Flags<BufferMappingAccessBit> GetMappingAccess() const;
|
||||
GLbitfield GetStorageFlags() const;
|
||||
Uint GetExternalIndex() const;
|
||||
const VecRange1D& GetDirtyRanges() const;
|
||||
Flags<BufferChangeBits> GetChangeBits() const;
|
||||
@@ -108,6 +113,8 @@ namespace MobileGL {
|
||||
SharedPtr<Data> m_dataPtr;
|
||||
Bool m_isMapped;
|
||||
Flags<BufferMappingAccessBit> m_mappingAccess;
|
||||
Bool m_isImmutableStorage = false;
|
||||
GLbitfield m_storageFlags = 0;
|
||||
BufferChange m_change;
|
||||
Uint64 m_changeSerial = 0;
|
||||
Range1D m_mappedRange;
|
||||
|
||||
@@ -477,6 +477,157 @@ TEST_F(GeneralBufferTest, General_MapFlags) {
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(GeneralBufferTest, General_BufferStorageQueriesImmutable) {
|
||||
GLuint buffer = 0;
|
||||
GenBuffers(1, &buffer);
|
||||
BindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
|
||||
const GLint initial[] = {1, 2, 3, 4};
|
||||
constexpr GLbitfield storageFlags = GL_DYNAMIC_STORAGE_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
|
||||
BufferStorage(GL_ARRAY_BUFFER, sizeof(initial), initial, storageFlags);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
|
||||
GLint immutable = GL_FALSE;
|
||||
GLint reportedFlags = 0;
|
||||
GetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_IMMUTABLE_STORAGE, &immutable);
|
||||
GetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_STORAGE_FLAGS, &reportedFlags);
|
||||
EXPECT_EQ(immutable, GL_TRUE);
|
||||
EXPECT_EQ(reportedFlags, static_cast<GLint>(storageFlags));
|
||||
|
||||
const GLint update = 42;
|
||||
BufferSubData(GL_ARRAY_BUFFER, sizeof(GLint), sizeof(update), &update);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
|
||||
BufferData(GL_ARRAY_BUFFER, sizeof(initial), initial, GL_DYNAMIC_DRAW);
|
||||
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
|
||||
}
|
||||
|
||||
TEST_F(GeneralBufferTest, General_PersistentMapRequiresStorageFlags) {
|
||||
GLuint mutableBuffer = CreateBoundBuffer(GL_ARRAY_BUFFER, 64, GL_DYNAMIC_DRAW);
|
||||
(void)mutableBuffer;
|
||||
void* mapped = MapBufferRange(GL_ARRAY_BUFFER, 0, 16, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
EXPECT_EQ(mapped, nullptr);
|
||||
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
|
||||
|
||||
GLuint storageBuffer = 0;
|
||||
GenBuffers(1, &storageBuffer);
|
||||
BindBuffer(GL_ARRAY_BUFFER, storageBuffer);
|
||||
BufferStorage(GL_ARRAY_BUFFER, 64, nullptr, GL_MAP_WRITE_BIT);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
mapped = MapBufferRange(GL_ARRAY_BUFFER, 0, 16, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
EXPECT_EQ(mapped, nullptr);
|
||||
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
|
||||
|
||||
GLuint persistentBuffer = 0;
|
||||
GenBuffers(1, &persistentBuffer);
|
||||
BindBuffer(GL_ARRAY_BUFFER, persistentBuffer);
|
||||
BufferStorage(GL_ARRAY_BUFFER, 64, nullptr, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_CLIENT_STORAGE_BIT);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
mapped = MapBufferRange(GL_ARRAY_BUFFER, 0, 16, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
ASSERT_NE(mapped, nullptr);
|
||||
EXPECT_TRUE(UnmapBuffer(GL_ARRAY_BUFFER));
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(GeneralBufferTest, General_PersistentCoherentWriteDirtyWithoutUnmap) {
|
||||
GLuint buffer = 0;
|
||||
GenBuffers(1, &buffer);
|
||||
BindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
|
||||
GLint initial[] = {10, 20, 30, 40};
|
||||
BufferStorage(GL_ARRAY_BUFFER, sizeof(initial), initial,
|
||||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
|
||||
auto bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
|
||||
ASSERT_NE(bufferObject, nullptr);
|
||||
bufferObject->ClearDirty();
|
||||
|
||||
auto* mapped = static_cast<GLint*>(
|
||||
MapBufferRange(GL_ARRAY_BUFFER, 0, sizeof(initial),
|
||||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT));
|
||||
ASSERT_NE(mapped, nullptr);
|
||||
mapped[2] = 1234;
|
||||
|
||||
bufferObject->MarkPersistentMappedRangeDirty();
|
||||
ASSERT_FALSE(bufferObject->GetDirtyRanges().empty());
|
||||
EXPECT_EQ(bufferObject->GetDirtyRanges()[0].start, 0);
|
||||
EXPECT_EQ(bufferObject->GetDirtyRanges()[0].end, sizeof(initial));
|
||||
|
||||
const auto data = bufferObject->GetDataReadOnly();
|
||||
EXPECT_EQ(reinterpret_cast<const GLint*>(data->data())[2], 1234);
|
||||
EXPECT_TRUE(UnmapBuffer(GL_ARRAY_BUFFER));
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(GeneralBufferTest, General_PersistentExplicitFlushOnlyDirtiesFlushedRange) {
|
||||
GLuint buffer = 0;
|
||||
GenBuffers(1, &buffer);
|
||||
BindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
|
||||
GLint initial[] = {10, 20, 30, 40};
|
||||
BufferStorage(GL_ARRAY_BUFFER, sizeof(initial), initial, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
|
||||
auto bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
|
||||
ASSERT_NE(bufferObject, nullptr);
|
||||
bufferObject->ClearDirty();
|
||||
|
||||
auto* mapped = static_cast<GLint*>(
|
||||
MapBufferRange(GL_ARRAY_BUFFER, 0, sizeof(initial),
|
||||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT));
|
||||
ASSERT_NE(mapped, nullptr);
|
||||
mapped[1] = 200;
|
||||
mapped[3] = 400;
|
||||
|
||||
bufferObject->MarkPersistentMappedRangeDirty();
|
||||
EXPECT_TRUE(bufferObject->GetDirtyRanges().empty());
|
||||
|
||||
FlushMappedBufferRange(GL_ARRAY_BUFFER, sizeof(GLint), sizeof(GLint));
|
||||
ASSERT_FALSE(bufferObject->GetDirtyRanges().empty());
|
||||
EXPECT_EQ(bufferObject->GetDirtyRanges()[0].start, sizeof(GLint));
|
||||
EXPECT_EQ(bufferObject->GetDirtyRanges()[0].end, sizeof(GLint) * 2);
|
||||
|
||||
EXPECT_TRUE(UnmapBuffer(GL_ARRAY_BUFFER));
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(GeneralBufferTest, General_NamedBufferStorageMappingWrappers) {
|
||||
GLuint buffer = 0;
|
||||
GenBuffers(1, &buffer);
|
||||
|
||||
GLint initial[] = {1, 2, 3, 4};
|
||||
NamedBufferStorage(buffer, sizeof(initial), initial,
|
||||
GL_DYNAMIC_STORAGE_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
|
||||
GLint immutable = GL_FALSE;
|
||||
GetNamedBufferParameteriv(buffer, GL_BUFFER_IMMUTABLE_STORAGE, &immutable);
|
||||
EXPECT_EQ(immutable, GL_TRUE);
|
||||
|
||||
auto bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
|
||||
ASSERT_NE(bufferObject, nullptr);
|
||||
bufferObject->ClearDirty();
|
||||
|
||||
auto* mapped = static_cast<GLint*>(
|
||||
MapNamedBufferRange(buffer, 0, sizeof(initial),
|
||||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT));
|
||||
ASSERT_NE(mapped, nullptr);
|
||||
mapped[0] = 99;
|
||||
|
||||
void* mapPointer = nullptr;
|
||||
GetNamedBufferPointerv(buffer, GL_BUFFER_MAP_POINTER, &mapPointer);
|
||||
EXPECT_EQ(mapPointer, mapped);
|
||||
|
||||
FlushMappedNamedBufferRange(buffer, 0, sizeof(GLint));
|
||||
ASSERT_FALSE(bufferObject->GetDirtyRanges().empty());
|
||||
EXPECT_EQ(bufferObject->GetDirtyRanges()[0].start, 0);
|
||||
EXPECT_EQ(bufferObject->GetDirtyRanges()[0].end, sizeof(GLint));
|
||||
|
||||
EXPECT_TRUE(UnmapNamedBuffer(buffer));
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(GeneralBufferTest, General_GeneralTest_1) {
|
||||
GLuint buffers[3];
|
||||
GenBuffers(3, buffers);
|
||||
|
||||
@@ -8,6 +8,9 @@ add_executable(
|
||||
target_include_directories(BufferTest PRIVATE
|
||||
${MGL_ROOT}/include
|
||||
${MGL_ROOT}/MobileGL
|
||||
${MGL_ROOT}/3rdparty/xxHash
|
||||
${MGL_ROOT}/3rdparty/Vulkan-Headers/include
|
||||
${MGL_ROOT}/3rdparty/SPIRV-Reflect
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
@@ -16,5 +19,9 @@ target_link_libraries(
|
||||
${LINK_LIBRARIES}
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(BufferTest PRIVATE /Zc:preprocessor)
|
||||
endif()
|
||||
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(BufferTest)
|
||||
|
||||
Reference in New Issue
Block a user