From d553e363a7668ef091d95bc2b1ecd481b71ed601 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 8 Jun 2026 14:08:35 +0800 Subject: [PATCH 1/5] [Feat] (MG_Impl/GL_Buffer, MG_State/BufferState, MG_Backend): implement persistent mapping --- .../DirectGLES/BackendObject_DirectGLES.cpp | 2 +- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 1 + MobileGL/MG_Backend/DirectGLES/Managers.cpp | 9 +- MobileGL/MG_Backend/DirectGLES/Managers.h | 4 + .../BackendObject_DirectVulkan.cpp | 2 +- .../DirectVulkan/Renderer/UniformManager.cpp | 1 + .../DirectVulkan/Renderer/VkBufferManager.cpp | 1 + .../DirectVulkan/Renderer/VkBufferObject.cpp | 8 + .../DirectVulkan/Renderer/VulkanRenderer.cpp | 4 +- MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp | 736 ++++++++++++++++-- MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h | 13 + .../MG_Impl/GLImpl/Exporting/Definitions.cpp | 57 +- MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp | 125 ++- MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.h | 3 + .../GLState/BufferState/BufferObject.cpp | 95 ++- .../GLState/BufferState/BufferObject.h | 7 + MobileGL/MG_Test/Buffer/BufferTest.cpp | 151 ++++ MobileGL/MG_Test/Buffer/CMakeLists.txt | 7 + 18 files changed, 1098 insertions(+), 128 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index 119ba42b..3bb3f6bf 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -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 diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 05447c79..11438aaa 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -106,6 +106,7 @@ namespace MobileGL::MG_Backend::DirectGLES { namespace BufferImpl { void CreateAndSyncBufferObject(const SharedPtr& bufferObject) { + bufferObject->MarkPersistentMappedRangeDirty(); if (!(bufferObject->GetChangeBits() & BufferChangeBits::DirtyBit)) return; const auto& backendBufferIt = g_backendBufferObjects.find(bufferObject.get()); diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 144aca17..c8d77b6b 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -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); diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index fccdc41a..17876da6 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -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; diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index 21e1279d..e4b9d1b9 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -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 diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp index 0a39abdc..5fb54fb1 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp @@ -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(blockIndex)).c_str()); + bufferObject->MarkPersistentMappedRangeDirty(); const auto bufferData = bufferObject->GetDataReadOnly(); MOBILEGL_ASSERT(bufferData != nullptr && !bufferData->empty(), diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp index b60f1aa8..541e009d 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp @@ -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(bufferObject->GetSize()); if (bufferSize == 0) { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp index 07818111..bfd3a1a8 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp @@ -140,6 +140,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { } Memcpy(static_cast(mapped) + offset, data, static_cast(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(); } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 29455b7c..3433f90b 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -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(); @@ -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(); diff --git a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp index 7f4fd073..329b747f 100644 --- a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp @@ -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("MG_Impl/GLImpl", "GetBufferParameteriv_State", - "Params pointer cannot be null.")); - return; + SharedPtr 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("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("MG_Impl/GLImpl", "GetBufferParameteriv_State", - "Buffer target is bound to no buffer object.")); - return; + SharedPtr 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("MG_Impl/GLImpl", GetBufferOpName(op), + std::format("Buffer object {} does not exist.", buffer))); + } + return bufferObject; } - switch (pname) { - case GL_BUFFER_SIZE: - *params = static_cast(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("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("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("MG_Impl/GLImpl", GetBufferOpName(op), + "GL_MAP_COHERENT_BIT requires GL_MAP_PERSISTENT_BIT.")); + return false; + } + return true; + } + + Bool ValidateImmutableMapAccess(const SharedPtr& bufferObject, + Flags accessBits, BufferOp op) { + if (!bufferObject->IsImmutableStorage()) { + if (accessBits & (BufferMappingAccessBit::Persistent | BufferMappingAccessBit::Coherent)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique( + "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("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("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("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("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& bufferObject, GLenum pname, + GLint* params, BufferOp op) { + if (!params) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, MakeUnique("MG_Impl/GLImpl", GetBufferOpName(op), + "Params pointer cannot be null.")); + return; + } + + switch (pname) { + case GL_BUFFER_SIZE: + *params = static_cast(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(bufferObject->GetStorageFlags()); + break; + case GL_BUFFER_MAP_OFFSET: + *params = static_cast(bufferObject->GetMappedRange().start); + break; + case GL_BUFFER_MAP_LENGTH: + *params = static_cast(bufferObject->GetMappedRange().end - bufferObject->GetMappedRange().start); + break; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, MakeUnique("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("MG_Impl/GLImpl", "GetBufferParameteriv_State", - std::format("Invalid pname enum: 0x{:X}", pname))); - break; } + + void GetBufferParameteri64v_Object(const SharedPtr& bufferObject, GLenum pname, + GLint64* params, BufferOp op) { + if (!params) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, MakeUnique("MG_Impl/GLImpl", GetBufferOpName(op), + "Params pointer cannot be null.")); + return; + } + + switch (pname) { + case GL_BUFFER_SIZE: + *params = static_cast(bufferObject->GetSize()); + break; + case GL_BUFFER_MAP_OFFSET: + *params = static_cast(bufferObject->GetMappedRange().start); + break; + case GL_BUFFER_MAP_LENGTH: + *params = static_cast(bufferObject->GetMappedRange().end - bufferObject->GetMappedRange().start); + break; + default: { + GLint value = 0; + GetBufferParameteriv_Object(bufferObject, pname, &value, op); + *params = static_cast(value); + break; + } + } + } + + void GetBufferPointerv_Object(const SharedPtr& bufferObject, GLenum pname, + void** params, BufferOp op) { + if (!params) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, MakeUnique("MG_Impl/GLImpl", GetBufferOpName(op), + "Params pointer cannot be null.")); + return; + } + if (pname != GL_BUFFER_MAP_POINTER) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, MakeUnique("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(offset) + static_cast(length) > mappedRange.end - mappedRange.start) { MG_State::pGLContext->RecordError( ErrorCode::InvalidValue, MakeUnique("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("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("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 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( - "MG_Impl/GLImpl", "BufferSubData_State", - "Offset and size must not overlap with the mapped range of the buffer object.")); + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", "BufferSubData_State", + "Immutable buffer storage was not created with GL_DYNAMIC_STORAGE_BIT.")); return; } + SizeT bufferSize = bufferObject->GetSize(); + if (static_cast(offset) + static_cast(size) > bufferSize) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("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( + "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("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("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("MG_Impl/GLImpl", "BufferStorage_State", + "Buffer already has immutable storage.")); + return; + } + bufferObject->AllocateImmutableStorage(static_cast(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("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("MG_Impl/GLImpl", "NamedBufferStorage_State", + "Buffer already has immutable storage.")); + return; + } + bufferObject->AllocateImmutableStorage(static_cast(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("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("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("MG_Impl/GLImpl", "NamedBufferSubData_State", + "Data pointer cannot be null.")); + return; + } + if (size < 0 || offset < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("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("MG_Impl/GLImpl", "NamedBufferSubData_State", + "Immutable buffer storage was not created with GL_DYNAMIC_STORAGE_BIT.")); + return; + } + if (static_cast(offset) + static_cast(size) > bufferObject->GetSize()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("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("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("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("MG_Impl/GLImpl", "MapNamedBuffer_State", + "Cannot map a buffer object that is already mapped.")); + return nullptr; + } + + Flags 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("MG_Impl/GLImpl", "MapNamedBufferRange_State", + "Offset and length must be non-negative.")); + return nullptr; + } + if (length == 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", "MapNamedBufferRange_State", + "Length must be greater than zero.")); + return nullptr; + } + if (static_cast(offset) + static_cast(length) > bufferObject->GetSize()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("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("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("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("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("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("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("MG_Impl/GLImpl", "MapNamedBufferRange_State", + "Cannot map a buffer object that is already mapped.")); + return nullptr; + } + } + + return bufferObject->AcquireMemoryRange({static_cast(offset), static_cast(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("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("MG_Impl/GLImpl", "FlushMappedNamedBufferRange_State", + "Offset and length must be non-negative.")); + return; + } + if (!bufferObject->IsMapped()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", "FlushMappedNamedBufferRange_State", + "Cannot flush a buffer object that is not mapped.")); + return; + } + const auto mappedRange = bufferObject->GetMappedRange(); + if (static_cast(offset) + static_cast(length) > mappedRange.end - mappedRange.start) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "FlushMappedNamedBufferRange_State", + "Offset and length exceed mapped range.")); + return; + } + if (!(bufferObject->GetMappingAccess() & BufferMappingAccessBit::FlushExplicit)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("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(offset), static_cast(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) { diff --git a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h index c24f9eea..48f48fbe 100644 --- a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h +++ b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h @@ -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); diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index 76a896a6..fd332bb6 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -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) { diff --git a/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp b/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp index 4bff1afc..39c091c9 100644 --- a/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp +++ b/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.cpp @@ -8,36 +8,121 @@ #include "GL_Sync.h" -#include "MG_State/GLState/Core.h" +#include +#include 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> g_syncObjects; - void DeleteSync_Backend(GLsync sync) {} + void RecordSyncError(const char* funcName, ErrorCode code, String message) { + MG_State::pGLContext->RecordError(code, + MakeUnique("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->Condition = condition; + syncObject->Flags = flags; + GLsync handle = reinterpret_cast(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(syncObject->Condition); + break; + case GL_SYNC_FLAGS: + value = static_cast(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 diff --git a/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.h b/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.h index 356967da..d9a0dc87 100644 --- a/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.h +++ b/MobileGL/MG_Impl/GLImpl/Sync/GL_Sync.h @@ -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 diff --git a/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp b/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp index e625803b..9fc9b250 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp +++ b/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp @@ -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& 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& 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(m_dataPtr->data()) + m_mappedRange.start; + } + if (m_ownsStagingData) { + return const_cast(m_stagingData.data()); + } + return const_cast(m_dataPtr->data()) + m_mappedRange.start; + } + Flags BufferObject::GetMappingAccess() const { return m_isMapped ? m_mappingAccess : BufferMappingAccessBit::Null; } + GLbitfield BufferObject::GetStorageFlags() const { + return m_storageFlags; + } + Uint BufferObject::GetExternalIndex() const { return m_externalIndex; } diff --git a/MobileGL/MG_State/GLState/BufferState/BufferObject.h b/MobileGL/MG_State/GLState/BufferState/BufferObject.h index e6a01383..f13ed348 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferObject.h +++ b/MobileGL/MG_State/GLState/BufferState/BufferObject.h @@ -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 access); void ReleaseMemory(); void FlushMemoryRange(SizeT offset, SizeT length); + void MarkPersistentMappedRangeDirty(); void UploadSubData(DataPtr data, SizeT atOffset); void CopyDataFrom(const SharedPtr& 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& GetDataReadOnly() const; Flags GetMappingAccess() const; + GLbitfield GetStorageFlags() const; Uint GetExternalIndex() const; const VecRange1D& GetDirtyRanges() const; Flags GetChangeBits() const; @@ -108,6 +113,8 @@ namespace MobileGL { SharedPtr m_dataPtr; Bool m_isMapped; Flags m_mappingAccess; + Bool m_isImmutableStorage = false; + GLbitfield m_storageFlags = 0; BufferChange m_change; Uint64 m_changeSerial = 0; Range1D m_mappedRange; diff --git a/MobileGL/MG_Test/Buffer/BufferTest.cpp b/MobileGL/MG_Test/Buffer/BufferTest.cpp index 9d724fcd..1f589e68 100644 --- a/MobileGL/MG_Test/Buffer/BufferTest.cpp +++ b/MobileGL/MG_Test/Buffer/BufferTest.cpp @@ -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(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( + 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(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( + 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( + 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); diff --git a/MobileGL/MG_Test/Buffer/CMakeLists.txt b/MobileGL/MG_Test/Buffer/CMakeLists.txt index c8f5787a..cf72e015 100644 --- a/MobileGL/MG_Test/Buffer/CMakeLists.txt +++ b/MobileGL/MG_Test/Buffer/CMakeLists.txt @@ -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) From ab9db435990db78139ba57e95f28a16b1e9ec890 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 8 Jun 2026 14:11:20 +0800 Subject: [PATCH 2/5] [Chore]: bump version to 26.06 --- MobileGL/Config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MobileGL/Config.h b/MobileGL/Config.h index f6b9c340..bbb83faf 100644 --- a/MobileGL/Config.h +++ b/MobileGL/Config.h @@ -14,7 +14,7 @@ namespace MobileGL::MG_Config { inline const String ProjectName = "MobileGL"; inline const String CoreName = "MobileGL Core"; inline const String CoreVendor = "MobileGL-Dev (BZLZHH, Swung0x48, Tungsten)"; - inline const Version CoreVersion = {26, 5, 0, "-dev", VersionType::Development}; + inline const Version CoreVersion = {26, 6, 0, "-dev", VersionType::Development}; inline const VersionStringFormatAttrib DefaultVersionStringFormatAttrib = {2, 2, 0, true, true}; inline const Uint64 CacheVersion = 0; From cf165c0db57dcaadf82e561bec30ab2f74de77ad Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 9 Jun 2026 00:37:37 +0800 Subject: [PATCH 3/5] [Fix] (MG_Backend/DirectVulkan): support Voxy rendering Implemented: - Advertise Voxy-required DirectVulkan extensions without raising the reported OpenGL version. - Add DirectVulkan compute, indirect draw count, DSA, readback, and buffer state paths needed by Voxy. Fixed: - Enable Vulkan shaderInt64 and drawIndirectFirstInstance so Voxy baseInstance-driven LOD draws address the correct section data. - Fix DirectVulkan synchronization, framebuffer, texture readback, and shader interface handling used by Voxy and Minecraft screenshots. Tests: - Add MG_Test coverage for DirectVulkan extension advertising, DSA buffer/texture/framebuffer/vertex-array behavior, persistent mapped readback, and shader/program paths. --- CMakeLists.txt | 2 +- MobileGL/MG_Backend/BackendObject.h | 23 + .../BackendObject_DirectVulkan.cpp | 19 +- .../MG_Backend/DirectVulkan/DirectVulkan.cpp | 67 +- .../MG_Backend/DirectVulkan/DirectVulkan.h | 15 + .../DirectVulkan/Renderer/FrameContext.cpp | 9 +- .../DirectVulkan/Renderer/FrameContext.h | 1 + .../DirectVulkan/Renderer/ProgramFactory.cpp | 15 +- .../DirectVulkan/Renderer/UniformManager.cpp | 31 +- .../DirectVulkan/Renderer/VkBufferManager.cpp | 6 +- .../DirectVulkan/Renderer/VkBufferManager.h | 1 + .../Renderer/VkRenderPassManager.cpp | 50 +- .../Renderer/VkRenderPassManager.h | 9 + .../Renderer/VkTextureManager.cpp | 30 +- .../DirectVulkan/Renderer/VkTextureManager.h | 8 + .../DirectVulkan/Renderer/VulkanRenderer.cpp | 655 +++++++++++++++++- .../DirectVulkan/Renderer/VulkanRenderer.h | 21 + MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp | 217 +++++- MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h | 6 + .../MG_Impl/GLImpl/Drawing/GL_Drawing.cpp | 44 ++ MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.h | 4 + .../MG_Impl/GLImpl/Exporting/Definitions.cpp | 108 +-- .../GLImpl/Framebuffer/GL_Framebuffer.cpp | 535 +++++++++++++- .../GLImpl/Framebuffer/GL_Framebuffer.h | 17 + MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp | 37 +- MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h | 1 + .../MG_Impl/GLImpl/Program/GL_Program.cpp | 12 + MobileGL/MG_Impl/GLImpl/Program/GL_Program.h | 2 + .../GLImpl/RenderState/GL_RenderState.cpp | 19 + .../GLImpl/RenderState/GL_RenderState.h | 1 + .../MG_Impl/GLImpl/Texture/GL_Texture.cpp | 646 +++++++++++++++-- MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h | 13 + .../GLImpl/VertexArray/GL_VertexArray.cpp | 139 +++- .../GLImpl/VertexArray/GL_VertexArray.h | 8 + MobileGL/MG_Impl/GetProcAddress.cpp | 17 + .../GLState/BufferState/BufferObject.h | 1 + .../GLState/BufferState/BufferState.h | 2 +- MobileGL/MG_State/GLState/Core.cpp | 8 + MobileGL/MG_State/GLState/Core.h | 2 + .../GLState/ProgramState/ProgramObject.cpp | 21 + .../GLState/RenderState/RenderState.cpp | 11 + .../GLState/RenderState/RenderState.h | 10 + MobileGL/MG_Test/Buffer/BufferTest.cpp | 121 ++++ MobileGL/MG_Test/CMakeLists.txt | 2 + MobileGL/MG_Test/Framebuffer/CMakeLists.txt | 20 + .../MG_Test/Framebuffer/FramebufferTest.cpp | 333 +++++++++ MobileGL/MG_Test/Program/ProgramTest.cpp | 132 ++++ MobileGL/MG_Test/Program/ProgramUtilTest.cpp | 22 + MobileGL/MG_Test/SanityTest.cpp | 113 ++- MobileGL/MG_Test/Texture/CMakeLists.txt | 20 + MobileGL/MG_Test/Texture/TextureTest.cpp | 189 +++++ .../MG_Test/VertexArray/VertexArrayTest.cpp | 96 +++ .../MG_Util/BackendLoaders/Vulkan/Loader.cpp | 2 + .../MG_Util/BackendLoaders/Vulkan/Loader.h | 1 + .../Converters/GLToMG/BufferEnumConverter.cpp | 4 +- .../GLToMG/RenderStateEnumConverter.cpp | 11 + .../GLToMG/RenderStateEnumConverter.h | 1 + .../Converters/MGToGL/BufferEnumConverter.cpp | 4 +- .../MGToGL/RenderStateEnumConverter.cpp | 11 + .../MGToGL/RenderStateEnumConverter.h | 1 + .../MGToStr/BufferEnumConverter.cpp | 4 +- .../MGToStr/RenderStateEnumConverter.cpp | 11 + .../MGToStr/RenderStateEnumConverter.h | 1 + .../ShaderSourceProcessor.cpp | 13 + .../Texture/TextureFormatProcessor.cpp | 8 +- 65 files changed, 3724 insertions(+), 239 deletions(-) create mode 100644 MobileGL/MG_Test/Framebuffer/CMakeLists.txt create mode 100644 MobileGL/MG_Test/Framebuffer/FramebufferTest.cpp create mode 100644 MobileGL/MG_Test/Texture/CMakeLists.txt create mode 100644 MobileGL/MG_Test/Texture/TextureTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3edc054b..ccf75d3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project("MobileGL") option(MOBILEGL_BUILD_TEST "Build MobileGL tests" ON ) option(MOBILEGL_BUILD_BENCHMARK "Build MobileGL benchmarks" ON ) -option(MOBILEGL_FORCE_RELEASE_OPT "Enable Release optimization flags in Debug build" OFF) +option(MOBILEGL_FORCE_RELEASE_OPT "Enable Release optimization flags in Debug build" ON ) option(MOBILEGL_ENABLE_TRACY "Enable tracy for profiling" OFF) if (ANDROID) diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index 10f80e2c..75841ef3 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -8,8 +8,14 @@ #pragma once #include +#include "MG_State/GLState/TextureState/TextureEnum.h" namespace MobileGL { + namespace MG_State::GLState { + class FramebufferObject; + class ITextureObject; + } + enum class BackendType { DirectGLES, DirectVulkan, @@ -31,6 +37,10 @@ namespace MobileGL { void (*MultiDrawElementsIndirect)(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride); void (*MultiDrawArraysIndirect)(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride); + void (*MultiDrawElementsIndirectCount)(GLenum mode, GLenum type, const void* indirect, + GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); + void (*MultiDrawArraysIndirectCount)(GLenum mode, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride); void (*DrawRangeElementsBaseVertex)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices, GLint basevertex); void (*DrawRangeElements)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, @@ -54,8 +64,17 @@ namespace MobileGL { void (*ClearBufferfv)(GLenum buffer, GLint drawbuffer, const GLfloat* value); void (*ClearBufferuiv)(GLenum buffer, GLint drawbuffer, const GLuint* value); void (*ClearBufferiv)(GLenum buffer, GLint drawbuffer, const GLint* value); + void (*ClearNamedFramebufferfv)(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, const GLfloat* value); + void (*ClearNamedFramebufferfi)(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); void (*BlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + void (*BlitNamedFramebuffer)(const SharedPtr& readFramebuffer, + const SharedPtr& drawFramebuffer, + GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); void (*CopyTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); void (*CopyTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, @@ -64,6 +83,9 @@ namespace MobileGL { void (*ReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); void (*GetTexImage)(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels); + void (*GetTextureImage)(const SharedPtr& texture, + TextureUploadTarget uploadTarget, GLint level, GLenum format, GLenum type, + GLsizei bufSize, GLvoid* pixels); void (*DispatchCompute)(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ); void (*DispatchComputeIndirect)(GLintptr indirect); void (*MemoryBarrier)(GLbitfield barriers); @@ -90,6 +112,7 @@ namespace MobileGL { struct DynamicBackendParameters { SizeT UniformBufferOffsetAlignment = 256; + SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024; }; enum class WindowBackend { diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index e4b9d1b9..aad30621 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -125,7 +125,10 @@ 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_ARB_buffer_storage}, + 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_texture_storage, E_GL_ARB_direct_state_access, + E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader_int64}, .IsCompatibilityProfile = false // Is Compatibility Profile }, .StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability @@ -160,6 +163,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { funcsTable.GL.MultiDrawElementsBaseVertex = MultiDrawElementsBaseVertex; funcsTable.GL.MultiDrawElementsIndirect = MultiDrawElementsIndirect; funcsTable.GL.MultiDrawArraysIndirect = MultiDrawArraysIndirect; + funcsTable.GL.MultiDrawElementsIndirectCount = MultiDrawElementsIndirectCount; + funcsTable.GL.MultiDrawArraysIndirectCount = MultiDrawArraysIndirectCount; funcsTable.GL.DrawRangeElementsBaseVertex = DrawRangeElementsBaseVertex; funcsTable.GL.DrawRangeElements = DrawRangeElements; funcsTable.GL.DrawElementsInstancedBaseVertexBaseInstance = DrawElementsInstancedBaseVertexBaseInstance; @@ -175,12 +180,16 @@ namespace MobileGL::MG_Backend::DirectVulkan { funcsTable.GL.ClearBufferfv = ClearBufferfv; funcsTable.GL.ClearBufferuiv = ClearBufferuiv; funcsTable.GL.ClearBufferiv = ClearBufferiv; + funcsTable.GL.ClearNamedFramebufferfv = ClearNamedFramebufferfv; + funcsTable.GL.ClearNamedFramebufferfi = ClearNamedFramebufferfi; funcsTable.GL.BlitFramebuffer = BlitFramebuffer; + funcsTable.GL.BlitNamedFramebuffer = BlitNamedFramebuffer; funcsTable.GL.CopyTexImage2D = CopyTexImage2D; funcsTable.GL.CopyTexSubImage2D = CopyTexSubImage2D; funcsTable.GL.GenerateMipmap = GenerateMipmap; funcsTable.GL.ReadPixels = ReadPixels; funcsTable.GL.GetTexImage = GetTexImage; + funcsTable.GL.GetTextureImage = GetTextureImage; funcsTable.GL.DispatchCompute = DispatchCompute; funcsTable.GL.DispatchComputeIndirect = DispatchComputeIndirect; funcsTable.GL.MemoryBarrier = MemoryBarrier; @@ -206,6 +215,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void BackendObject_DirectVulkan::UpdateDynamicBackendParameters() { + static constexpr SizeT kMaxAdvertisedShaderStorageBlockSize = 512ull * 1024ull * 1024ull; m_dynamicParameters.UniformBufferOffsetAlignment = m_vulkanCaps.UniformBufferOffsetAlignment; + m_dynamicParameters.MaxShaderStorageBlockSize = + std::min(m_vulkanCaps.MaxShaderStorageBlockSize, kMaxAdvertisedShaderStorageBlockSize); + if (m_dynamicParameters.MaxShaderStorageBlockSize != m_vulkanCaps.MaxShaderStorageBlockSize) { + MGLOG_I("DirectVulkan: clamped GL_MAX_SHADER_STORAGE_BLOCK_SIZE from %zu to %zu", + m_vulkanCaps.MaxShaderStorageBlockSize, + m_dynamicParameters.MaxShaderStorageBlockSize); + } } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp index fde6983c..d6ad277d 100644 --- a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp @@ -157,6 +157,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { StorageBlockResource block{}; block.name = blockName; + block.binding = binding->binding; block.dataSize = static_cast(binding->block.size); const GLuint blockIndex = static_cast(cache.storageBlocks.size()); AddBufferVariablesRecursive(binding->block, blockName, blockIndex, cache.bufferVariables, @@ -235,8 +236,38 @@ namespace MobileGL::MG_Backend::DirectVulkan { pVulkanRenderer->ClearBufferiv(buffer, drawbuffer, value); } - void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {} - void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {} + void ClearNamedFramebufferfv(const SharedPtr& framebuffer, GLenum buffer, + GLint drawbuffer, const GLfloat* value) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearNamedFramebufferfv called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::ClearNamedFramebufferfv called with null GL context"); + pVulkanRenderer->ClearNamedFramebufferfv(framebuffer, buffer, drawbuffer, value); + } + + void ClearNamedFramebufferfi(const SharedPtr& framebuffer, GLenum buffer, + GLint drawbuffer, GLfloat depth, GLint stencil) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearNamedFramebufferfi called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::ClearNamedFramebufferfi called with null GL context"); + pVulkanRenderer->ClearNamedFramebufferfi(framebuffer, buffer, drawbuffer, depth, stencil); + } + + void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElementsIndirect called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElementsIndirect called with null GL context"); + pVulkanRenderer->MultiDrawElementsIndirectCount(mode, type, indirect, 0, drawcount, stride); + } + void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) { + MGLOG_W("DirectVulkan::MultiDrawArraysIndirect is not implemented yet (drawcount=%d)", drawcount); + } + void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElementsIndirectCount called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElementsIndirectCount called with null GL context"); + pVulkanRenderer->MultiDrawElementsIndirectCount(mode, type, indirect, drawcount, maxdrawcount, stride); + } + void MultiDrawArraysIndirectCount(GLenum mode, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride) { + MGLOG_W("DirectVulkan::MultiDrawArraysIndirectCount is not implemented yet (maxdrawcount=%d)", maxdrawcount); + } void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices, GLint basevertex) {} void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices) {} @@ -277,8 +308,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void DispatchComputeIndirect(GLintptr indirect) { - MGLOG_W("DirectVulkan::DispatchComputeIndirect is not implemented yet (offset=%zu)", - static_cast(indirect)); + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DispatchComputeIndirect called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DispatchComputeIndirect called with null GL context"); + pVulkanRenderer->DispatchComputeIndirect(indirect); } void MemoryBarrier(GLbitfield barriers) { @@ -589,8 +621,22 @@ namespace MobileGL::MG_Backend::DirectVulkan { } cache.storageBlocks[storageBlockIndex].binding = storageBlockBinding; } - void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {} - void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {} + void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ReadPixels called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::ReadPixels called with null GL context"); + pVulkanRenderer->ReadPixels(x, y, width, height, format, type, pixels); + } + void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::GetTexImage called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::GetTexImage called with null GL context"); + pVulkanRenderer->GetTexImage(target, level, format, type, pixels); + } + void GetTextureImage(const SharedPtr& texture, TextureUploadTarget uploadTarget, + GLint level, GLenum format, GLenum type, GLsizei bufSize, GLvoid* pixels) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::GetTextureImage called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::GetTextureImage called with null GL context"); + pVulkanRenderer->GetTextureImage(texture, uploadTarget, level, format, type, bufSize, pixels); + } void Clear(GLbitfield mask) { MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::Clear called with null VulkanRenderer"); @@ -713,6 +759,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { pVulkanRenderer->BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } + void BlitNamedFramebuffer(const SharedPtr& readFramebuffer, + const SharedPtr& drawFramebuffer, + GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, + GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::BlitNamedFramebuffer called with null VulkanRenderer"); + pVulkanRenderer->BlitNamedFramebuffer(readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, + dstY0, dstX1, dstY1, mask, filter); + } + void Present() { MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::Present called with null VulkanRenderer"); pVulkanRenderer->Present(); diff --git a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.h b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.h index 92e761c9..55d1edf5 100644 --- a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.h +++ b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.h @@ -17,6 +17,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value); void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value); void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value); + void ClearNamedFramebufferfv(const SharedPtr& framebuffer, GLenum buffer, + GLint drawbuffer, const GLfloat* value); + void ClearNamedFramebufferfi(const SharedPtr& framebuffer, GLenum buffer, + GLint drawbuffer, GLfloat depth, GLint stencil); void Clear(GLbitfield mask); void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices); void DrawArrays(GLenum mode, GLint first, GLsizei count); @@ -27,6 +31,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { GLsizei drawcount, const GLint* basevertex); void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride); void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride); + void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride); + void MultiDrawArraysIndirectCount(GLenum mode, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride); void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices, GLint basevertex); void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices); @@ -44,6 +52,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { void DrawArraysIndirect(GLenum mode, const void* indirect); void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + void BlitNamedFramebuffer(const SharedPtr& readFramebuffer, + const SharedPtr& drawFramebuffer, + GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, @@ -69,5 +82,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void ShaderStorageBlockBinding(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels); + void GetTextureImage(const SharedPtr& texture, TextureUploadTarget uploadTarget, + GLint level, GLenum format, GLenum type, GLsizei bufSize, GLvoid* pixels); void Present(); } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp index 0a7c9211..855554b9 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp @@ -178,9 +178,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { packet.signalSemaphore = m_swapchainImageRenderFinishedSemaphores[swapchainImageIndex]; packet.commandBuffer = frame.commandBuffer; - packet.submitInfo.waitSemaphoreCount = 1; - packet.submitInfo.pWaitSemaphores = &packet.waitSemaphore; - packet.submitInfo.pWaitDstStageMask = &packet.waitDstStageMask; + packet.submitInfo.waitSemaphoreCount = frame.imageAvailableSemaphoreConsumed ? 0U : 1U; + packet.submitInfo.pWaitSemaphores = frame.imageAvailableSemaphoreConsumed ? nullptr : &packet.waitSemaphore; + packet.submitInfo.pWaitDstStageMask = frame.imageAvailableSemaphoreConsumed ? nullptr : &packet.waitDstStageMask; packet.submitInfo.commandBufferCount = shouldSubmitCommandBuffer ? 1U : 0U; packet.submitInfo.pCommandBuffers = shouldSubmitCommandBuffer ? &packet.commandBuffer : nullptr; packet.submitInfo.signalSemaphoreCount = 1; @@ -217,6 +217,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { return result; } + frame.imageAvailableSemaphoreConsumed = false; return vkAcquireNextImageKHR(device, swapchain, timeout, frame.imageAvailableSemaphore, acquireFence, &outImageIndex); } @@ -260,6 +261,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { frame.hasCommandBufferRecorded = false; frame.isCommandRecording = false; + frame.imageAvailableSemaphoreConsumed = false; return VK_SUCCESS; } @@ -277,5 +279,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { frame.imageAvailableSemaphore = VK_NULL_HANDLE; frame.isCommandRecording = false; frame.hasCommandBufferRecorded = false; + frame.imageAvailableSemaphoreConsumed = false; } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h index 83815445..bb66bb1b 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.h @@ -35,6 +35,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkFence imageInFlightFence = VK_NULL_HANDLE; Bool isCommandRecording = false; Bool hasCommandBufferRecorded = false; + Bool imageAvailableSemaphoreConsumed = false; }; VkResult Initialize(VkDevice device, VkCommandPool commandPool, Uint32 frameCount); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp index 564c9aee..1021223a 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp @@ -994,16 +994,19 @@ namespace MobileGL::MG_Backend::DirectVulkan { String NormalizeDescriptorName(const SpvReflectDescriptorBinding& binding, ProgramFactory::DescriptorBindingKind kind) { const char* rawName = binding.name; - if (kind == ProgramFactory::DescriptorBindingKind::UniformBufferDynamic && + if ((kind == ProgramFactory::DescriptorBindingKind::UniformBufferDynamic || + kind == ProgramFactory::DescriptorBindingKind::StorageBuffer) && binding.type_description != nullptr && binding.type_description->type_name != nullptr) { rawName = binding.type_description->type_name; } - MOBILEGL_ASSERT(rawName != nullptr && rawName[0] != '\0', - "ProgramFactory: descriptor has empty name (spirvId=%u type=%d)", binding.spirv_id, - static_cast(binding.descriptor_type)); - - String name = rawName; + String name = (rawName != nullptr) ? rawName : ""; + if (name.empty()) { + name = std::format("__mg_unnamed_descriptor_set{}_binding{}_id{}", binding.set, binding.binding, + binding.spirv_id); + MGLOG_W("ProgramFactory: descriptor has empty name; using generated name '%s' (type=%d)", + name.c_str(), static_cast(binding.descriptor_type)); + } if (kind == ProgramFactory::DescriptorBindingKind::CombinedImageSampler || kind == ProgramFactory::DescriptorBindingKind::UniformTexelBuffer || kind == ProgramFactory::DescriptorBindingKind::StorageImage) { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp index 5fb54fb1..5067122d 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp @@ -202,7 +202,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { const MG_State::GLState::ProgramObject& program, const ProgramFactory::VkProgramObject& programObj, Uint32 binding, VkDescriptorImageInfo& outImageInfo) const { - (void)commandBuffer; MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveSamplerDescriptor: texture manager is null"); MOBILEGL_ASSERT(m_samplerManager != nullptr, "ResolveSamplerDescriptor: sampler manager is null"); MOBILEGL_ASSERT(binding < programObj.samplerNameByBinding.size(), @@ -254,23 +253,31 @@ namespace MobileGL::MG_Backend::DirectVulkan { Int attachmentLevel = 0; if (drawFbo && FindFramebufferAttachmentForTexture(*drawFbo, *texture, attachmentType, attachmentLevel)) { - MOBILEGL_ASSERT(false, - "ResolveSamplerDescriptor: framebuffer feedback loop detected: textureId=%d is bound " - "for sampling at binding=%u, but is also attached to drawFbo=%u as %s (level=%d, " - "trackedLayout=%d)", - texture->GetExternalIndex(), binding, drawFbo->GetExternalIndex(), - MG_Util::ConvertFramebufferAttachmentTypeToString(attachmentType).c_str(), - attachmentLevel, static_cast(resource->layout)); + MGLOG_W("ResolveSamplerDescriptor: framebuffer feedback loop detected: textureId=%d is bound " + "for sampling at binding=%u, but is also attached to drawFbo=%u as %s (level=%d, " + "trackedLayout=%d)", + texture->GetExternalIndex(), binding, drawFbo->GetExternalIndex(), + MG_Util::ConvertFramebufferAttachmentTypeToString(attachmentType).c_str(), + attachmentLevel, static_cast(resource->layout)); } - MOBILEGL_ASSERT(false, + const Bool readyForSampling = m_textureManager->TransitionTextureForSampling(commandBuffer, *texture); + if (!readyForSampling) { + MGLOG_E("ResolveSamplerDescriptor: failed to transition textureId=%d for sampler binding=%u", + texture->GetExternalIndex(), binding); + return false; + } + resource = m_textureManager->SyncTextureAndGetDescriptor(*texture); + MOBILEGL_ASSERT(resource != nullptr, + "ResolveSamplerDescriptor: failed to resync textureId=%d after sampling transition", + texture->GetExternalIndex()); + MOBILEGL_ASSERT(IsValidSampledImageLayout(resource->layout), "ResolveSamplerDescriptor: invalid sampled image layout=%d for textureId=%d, binding=%u", static_cast(resource->layout), texture->GetExternalIndex(), binding); - return false; } outImageInfo = { .sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse, *texture), - .imageView = resource->fullView, + .imageView = resource->sampledView != VK_NULL_HANDLE ? resource->sampledView : resource->fullView, .imageLayout = resource->layout, }; if (ShouldDumpDescriptorStats()) { @@ -316,7 +323,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { *samplerBindingOverride.texture), .imageView = samplerBindingOverride.imageView != VK_NULL_HANDLE ? samplerBindingOverride.imageView : - resource->fullView, + (resource->sampledView != VK_NULL_HANDLE ? resource->sampledView : resource->fullView), .imageLayout = resource->layout, }; return outImageInfo.sampler != VK_NULL_HANDLE; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp index 541e009d..99e8a5df 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp @@ -67,7 +67,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { .allocator = m_initInfo.allocator, .frameCount = m_initInfo.frameCount, .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, .memoryUsage = m_initInfo.transientMemoryUsage, .allocationFlags = m_initInfo.transientAllocationFlags, .minBufferSize = m_initInfo.minUploadBytes, @@ -201,7 +201,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { case BufferKind::TextureBuffer: return VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; case BufferKind::ShaderStorage: - return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; + case BufferKind::Indirect: + return VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; default: return 0; } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h index 83c14d2b..127d3296 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h @@ -21,6 +21,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uniform, TextureBuffer, ShaderStorage, + Indirect, }; struct VkBufferManagerInitInfo { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp index 6773ff60..bda3a202 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp @@ -61,6 +61,23 @@ namespace MobileGL::MG_Backend::DirectVulkan { return texture; } + DepthStencilAttachmentLoadInfo ResolveDepthStencilAttachmentLoadInfo( + VkImageLayout trackedLayout, Bool clearDepth, Bool clearStencil) { + DepthStencilAttachmentLoadInfo info{}; + info.depthLoadOp = clearDepth + ? VK_ATTACHMENT_LOAD_OP_CLEAR + : (trackedLayout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE + : VK_ATTACHMENT_LOAD_OP_LOAD); + info.stencilLoadOp = clearStencil + ? VK_ATTACHMENT_LOAD_OP_CLEAR + : (trackedLayout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE + : VK_ATTACHMENT_LOAD_OP_LOAD); + info.initialLayout = + (trackedLayout == VK_IMAGE_LAYOUT_UNDEFINED || (clearDepth && clearStencil)) ? VK_IMAGE_LAYOUT_UNDEFINED + : trackedLayout; + return info; + } + VkRenderPassManager::VkRenderPassManager(VkDevice device, const VulkanRendererConfig& config, VkClearManager& clearManager, VkTextureManager& textureManager, SwapchainObject& swapchainObject): @@ -372,27 +389,19 @@ namespace MobileGL::MG_Backend::DirectVulkan { depthAttachmentDescription.format = depthTextureResource->format; } depthAttachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT; - depthAttachmentDescription.loadOp = clearDepth ? - VK_ATTACHMENT_LOAD_OP_CLEAR : - VK_ATTACHMENT_LOAD_OP_LOAD; + const auto loadInfo = + ResolveDepthStencilAttachmentLoadInfo(trackedDepthLayout, clearDepth, clearStencil); + depthAttachmentDescription.loadOp = loadInfo.depthLoadOp; depthAttachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - depthAttachmentDescription.stencilLoadOp = clearStencil ? - VK_ATTACHMENT_LOAD_OP_CLEAR : - VK_ATTACHMENT_LOAD_OP_LOAD; + depthAttachmentDescription.stencilLoadOp = loadInfo.stencilLoadOp; depthAttachmentDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; depthAttachmentDescription.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - if (trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED) { - if (!clearDepth) { - depthAttachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } - if (!clearStencil) { - depthAttachmentDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } + depthAttachmentDescription.initialLayout = loadInfo.initialLayout; + if (trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED && (!clearDepth || !clearStencil)) { + MGLOG_W("GetOrCreateRenderPass: depth/stencil attachment textureId=%d starts with undefined layout " + "and partial/no clear; using DONT_CARE for uncleared aspects", + texture.GetExternalIndex()); } - depthAttachmentDescription.initialLayout = - (clearDepth && clearStencil) || trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED ? - VK_IMAGE_LAYOUT_UNDEFINED : - trackedDepthLayout; if (hasClear) { pendingClearAttachments.emplace_back(PendingClearAttachmentInfo { .attachmentIndex = depthAttachmentIndex, @@ -407,9 +416,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { }); attachmentViews.emplace_back(m_swapchainObject.GetDepthStencilImageView(swapchainImageIndex)); } else { - MOBILEGL_ASSERT(clearDepth || clearStencil || depthTextureResource->layout != VK_IMAGE_LAYOUT_UNDEFINED, + MOBILEGL_ASSERT(depthTextureResource->layout != VK_IMAGE_LAYOUT_UNDEFINED || + depthAttachmentDescription.loadOp != VK_ATTACHMENT_LOAD_OP_LOAD, "GetOrCreateRenderPass: depth attachment textureId=%d has undefined tracked layout with LOAD_OP_LOAD", texture.GetExternalIndex()); + MOBILEGL_ASSERT(depthTextureResource->layout != VK_IMAGE_LAYOUT_UNDEFINED || + depthAttachmentDescription.stencilLoadOp != VK_ATTACHMENT_LOAD_OP_LOAD, + "GetOrCreateRenderPass: stencil attachment textureId=%d has undefined tracked layout with LOAD_OP_LOAD", + texture.GetExternalIndex()); trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo { .target = TrackedAttachmentTarget::Texture, .texture = &texture, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h index 190bf60a..fc3b95d0 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h @@ -37,6 +37,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkImageLayout finalLayout = VK_IMAGE_LAYOUT_UNDEFINED; }; + struct DepthStencilAttachmentLoadInfo { + VkAttachmentLoadOp depthLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + VkAttachmentLoadOp stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + VkImageLayout initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + }; + + DepthStencilAttachmentLoadInfo ResolveDepthStencilAttachmentLoadInfo( + VkImageLayout trackedLayout, Bool clearDepth, Bool clearStencil); + struct RenderPassEntry { static inline VkDevice s_device; static inline Vector s_textureResourcesScratch; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp index d48ba0fe..82c6835c 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp @@ -621,7 +621,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { const TextureFormatInfo formatInfo = ResolveTextureFormatInfo(texture.GetFormat()); const VkComponentMapping sampledComponents = ResolveSampledViewComponents(texture, formatInfo); - perMipSampledView = CreateImageView(resource->image, resource->format, resource->aspect, resource->viewType, + const VkImageAspectFlags sampledAspect = ResolveSampledImageViewAspectMask(resource->aspect); + perMipSampledView = CreateImageView(resource->image, resource->format, sampledAspect, resource->viewType, mipLevel, 1, resource->arrayLayers, &sampledComponents); if (perMipSampledView == VK_NULL_HANDLE) { MGLOG_D("%s: CreateImageView failed for textureId=%d mipLevel=%u", __func__, texture.GetExternalIndex(), @@ -1014,6 +1015,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void VkTextureManager::DeferResourceRelease(TextureResource&& resource) { if (resource.image == VK_NULL_HANDLE && resource.fullView == VK_NULL_HANDLE && + resource.sampledView == VK_NULL_HANDLE && resource.perMipViews.empty() && resource.perMipSampledViews.empty()) { return; } @@ -1088,6 +1090,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { const Bool needsRecreate = resource.fullView == VK_NULL_HANDLE || + resource.sampledView == VK_NULL_HANDLE || resource.sampledBaseMipLevel != baseMipLevel || resource.sampledLevelCount != levelCount || resource.syncedTextureParamsVersion != texture.GetTextureParamsVersion(); @@ -1099,6 +1102,10 @@ namespace MobileGL::MG_Backend::DirectVulkan { DeferViewRelease(resource.fullView); resource.fullView = VK_NULL_HANDLE; } + if (resource.sampledView != VK_NULL_HANDLE) { + DeferViewRelease(resource.sampledView); + resource.sampledView = VK_NULL_HANDLE; + } for (auto& sampledView : resource.perMipSampledViews) { if (sampledView != VK_NULL_HANDLE) { DeferViewRelease(sampledView); @@ -1109,10 +1116,16 @@ namespace MobileGL::MG_Backend::DirectVulkan { const TextureFormatInfo formatInfo = ResolveTextureFormatInfo(texture.GetFormat()); const VkComponentMapping sampledComponents = ResolveSampledViewComponents(texture, formatInfo); resource.fullView = CreateImageView(resource.image, resource.format, resource.aspect, resource.viewType, - baseMipLevel, levelCount, resource.arrayLayers, &sampledComponents); + baseMipLevel, levelCount, resource.arrayLayers); if (resource.fullView == VK_NULL_HANDLE) { return false; } + const VkImageAspectFlags sampledAspect = ResolveSampledImageViewAspectMask(resource.aspect); + resource.sampledView = CreateImageView(resource.image, resource.format, sampledAspect, resource.viewType, + baseMipLevel, levelCount, resource.arrayLayers, &sampledComponents); + if (resource.sampledView == VK_NULL_HANDLE) { + return false; + } resource.sampledBaseMipLevel = baseMipLevel; resource.sampledLevelCount = levelCount; @@ -1441,4 +1454,17 @@ namespace MobileGL::MG_Backend::DirectVulkan { return VK_IMAGE_ASPECT_COLOR_BIT; } } + + VkImageAspectFlags VkTextureManager::ResolveSampledImageViewAspectMask(VkImageAspectFlags imageAspect) { + if ((imageAspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { + return VK_IMAGE_ASPECT_COLOR_BIT; + } + if ((imageAspect & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) { + return VK_IMAGE_ASPECT_DEPTH_BIT; + } + if ((imageAspect & VK_IMAGE_ASPECT_STENCIL_BIT) != 0) { + return VK_IMAGE_ASPECT_STENCIL_BIT; + } + return imageAspect; + } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h index 64bd6d89..debe1658 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h @@ -33,6 +33,7 @@ public: VkImage image = VK_NULL_HANDLE; VmaAllocation allocation = nullptr; VkImageView fullView = VK_NULL_HANDLE; + VkImageView sampledView = VK_NULL_HANDLE; Vector perMipViews; Vector perMipSampledViews; VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -53,6 +54,7 @@ public: std::swap(this->image, that.image); std::swap(this->allocation, that.allocation); std::swap(this->fullView, that.fullView); + std::swap(this->sampledView, that.sampledView); std::swap(this->perMipViews, that.perMipViews); std::swap(this->perMipSampledViews, that.perMipSampledViews); std::swap(this->layout, that.layout); @@ -72,6 +74,9 @@ public: if (fullView != VK_NULL_HANDLE) { vkDestroyImageView(s_device, fullView, nullptr); } + if (sampledView != VK_NULL_HANDLE) { + vkDestroyImageView(s_device, sampledView, nullptr); + } for (const auto attachmentView : perMipViews) { if (attachmentView != VK_NULL_HANDLE) { vkDestroyImageView(s_device, attachmentView, nullptr); @@ -86,6 +91,7 @@ public: vmaDestroyImage(s_allocator, image, allocation); } fullView = VK_NULL_HANDLE; + sampledView = VK_NULL_HANDLE; perMipViews.clear(); perMipSampledViews.clear(); image = VK_NULL_HANDLE; @@ -127,6 +133,8 @@ public: Bool TransitionTextureForSampling(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture); Bool TransitionTextureForStorageImage(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture); + static VkImageAspectFlags ResolveSampledImageViewAspectMask(VkImageAspectFlags imageAspect); + static Bool TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout& trackedLayout, VkImageLayout newLayout, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 3433f90b..a4614dd7 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -1226,6 +1226,127 @@ void main() { const char* value = std::getenv("MOBILEGL_PRESENT_STATS"); return value != nullptr && value[0] == '1' && value[1] == '\0'; } + + static Bool IsBgraVkFormat(VkFormat format) { + switch (format) { + case VK_FORMAT_B8G8R8A8_UNORM: + case VK_FORMAT_B8G8R8A8_SNORM: + case VK_FORMAT_B8G8R8A8_SRGB: + return true; + default: + return false; + } + } + + static SizeT AlignPixelRow(SizeT rowBytes, Int alignment) { + const SizeT resolvedAlignment = static_cast(std::max(alignment, 1)); + return (rowBytes + resolvedAlignment - 1) & ~(resolvedAlignment - 1); + } + + static Int GetReadbackChannelCount(GLenum format) { + switch (format) { + case GL_RGB: + case GL_BGR: + return 3; + case GL_RGBA: + case GL_BGRA: + return 4; + default: + return 0; + } + } + + static void StoreReadbackPixel(const Uint8* src, Bool srcIsBgra, GLenum dstFormat, Uint8* dst) { + const Uint8 r = srcIsBgra ? src[2] : src[0]; + const Uint8 g = src[1]; + const Uint8 b = srcIsBgra ? src[0] : src[2]; + const Uint8 a = src[3]; + + switch (dstFormat) { + case GL_RGB: + dst[0] = r; + dst[1] = g; + dst[2] = b; + break; + case GL_BGR: + dst[0] = b; + dst[1] = g; + dst[2] = r; + break; + case GL_RGBA: + dst[0] = r; + dst[1] = g; + dst[2] = b; + dst[3] = a; + break; + case GL_BGRA: + dst[0] = b; + dst[1] = g; + dst[2] = r; + dst[3] = a; + break; + default: + break; + } + } + + static Bool PackReadbackToClientOrPbo(const Uint8* srcPixels, VkFormat srcFormat, GLsizei width, + GLsizei height, GLenum format, GLenum type, void* pixels) { + if (width <= 0 || height <= 0) { + return true; + } + if (type != GL_UNSIGNED_BYTE) { + MGLOG_E("DirectVulkan readback skipped: unsupported type=0x%x", type); + return false; + } + + const Int dstChannels = GetReadbackChannelCount(format); + if (dstChannels == 0) { + MGLOG_E("DirectVulkan readback skipped: unsupported format=0x%x", format); + return false; + } + + const auto packParams = MG_State::pGLContext->GetPixelStoreParameters(false); + const SizeT rowPixels = static_cast(packParams.RowLength > 0 ? packParams.RowLength : width); + const SizeT dstRowStride = AlignPixelRow(rowPixels * static_cast(dstChannels), + packParams.Alignment); + const SizeT dstOffset = static_cast(std::max(packParams.SkipRows, 0)) * dstRowStride + + static_cast(std::max(packParams.SkipPixels, 0)) * + static_cast(dstChannels); + const SizeT packedSize = dstOffset + + (static_cast(height - 1) * dstRowStride) + + (static_cast(width) * static_cast(dstChannels)); + Vector packed(packedSize, 0); + + const Bool srcIsBgra = IsBgraVkFormat(srcFormat); + for (GLsizei row = 0; row < height; ++row) { + const Uint8* srcRow = srcPixels + static_cast(row) * static_cast(width) * 4; + Uint8* dstRow = packed.data() + dstOffset + static_cast(row) * dstRowStride; + for (GLsizei col = 0; col < width; ++col) { + StoreReadbackPixel(srcRow + static_cast(col) * 4, + srcIsBgra, + format, + dstRow + static_cast(col) * static_cast(dstChannels)); + } + } + + const auto& pixelPackBufferObject = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); + if (pixelPackBufferObject) { + const SizeT pboOffset = reinterpret_cast(pixels); + if (pboOffset + packed.size() > pixelPackBufferObject->GetSize()) { + MGLOG_E("DirectVulkan readback skipped: pixel pack buffer is too small"); + return false; + } + pixelPackBufferObject->UploadSubData({packed.data(), packed.size()}, pboOffset); + return true; + } + + if (pixels != nullptr && !packed.empty()) { + Memcpy(pixels, packed.data(), packed.size()); + } + return true; + } } // namespace VkBool32 VulkanRenderer::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, @@ -1607,7 +1728,10 @@ void main() { ++syntheticBinding; } - vkCmdBindVertexBuffers(commandBuffer, 0, static_cast(bindingCount), vkBuffers.data(), vkOffsets.data()); + if (bindingCount > 0) { + vkCmdBindVertexBuffers(commandBuffer, 0, static_cast(bindingCount), vkBuffers.data(), + vkOffsets.data()); + } return true; } @@ -2747,6 +2871,55 @@ void main() { vkCmdDispatch(frame.commandBuffer, numGroupsX, numGroupsY, numGroupsZ); } + void VulkanRenderer::DispatchComputeIndirect(GLintptr indirect) { + m_textureManager->CollectGarbage(); + auto& frame = m_frameContext.GetCurrent(); + const auto& program = *MG_State::pGLContext->GetCurrentProgram(); + ProgramFactory::CompileOptionFlags transformFlags = 0; + const auto& programObj = m_programFactory->GetOrCreateProgram(program, transformFlags); + + if (!frame.isCommandRecording) { + m_frameContext.BeginCommandRecording(); + m_uniformManager->BeginFrame(m_frameContext.GetCurrentFrameIndex()); + } + + if (VkRenderPassManager::GetActiveRenderPass() != nullptr) { + VkRenderPassManager::EndRenderPass(frame.commandBuffer); + } + + const VkPipeline pipeline = GetOrCreateComputePipeline(programObj); + if (pipeline == VK_NULL_HANDLE) { + MGLOG_E("DispatchComputeIndirect skipped: compute pipeline creation failed for program=%u", + program.GetExternalIndex()); + return; + } + + vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); + const Bool boundUniforms = m_uniformManager->BindProgramUniformBuffers( + frame.commandBuffer, program, programObj, m_frameContext.GetCurrentFrameIndex(), + VK_PIPELINE_BIND_POINT_COMPUTE); + if (!boundUniforms) { + MGLOG_E("DispatchComputeIndirect skipped: BindProgramUniformBuffers failed"); + return; + } + + auto indirectBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DispatchIndirect).GetBoundObject(); + if (!indirectBuffer) { + MGLOG_E("DispatchComputeIndirect skipped: GL_DISPATCH_INDIRECT_BUFFER is not bound"); + return; + } + indirectBuffer->MarkPersistentMappedRangeDirty(); + + BufferSlice slice{}; + if (!m_bufferManager.SyncResidentBuffer(BufferKind::Indirect, indirectBuffer, slice)) { + MGLOG_E("DispatchComputeIndirect skipped: failed to sync indirect dispatch buffer"); + return; + } + + MGLOG_D("DirectVulkan: glDispatchComputeIndirect(offset=%zu)", static_cast(indirect)); + vkCmdDispatchIndirect(frame.commandBuffer, slice.buffer, slice.offset + static_cast(indirect)); + } + void VulkanRenderer::MemoryBarrier(GLbitfield barriers) { auto& frame = m_frameContext.GetCurrent(); if (!frame.isCommandRecording) { @@ -2791,17 +2964,16 @@ void main() { m_clearManager->QueueClear(mask, payload, *fbo); } - void VulkanRenderer::QueueClearBufferPayload(GLenum buffer, GLint drawbuffer, - const ClearAttachmentPayload& clearPayload) { + void VulkanRenderer::QueueClearBufferPayloadForFramebuffer( + const MG_State::GLState::FramebufferObject& framebuffer, GLenum buffer, GLint drawbuffer, + const ClearAttachmentPayload& clearPayload) { m_clearManager->CollectGarbage(); - auto* fbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject().get(); - MOBILEGL_ASSERT(fbo, "VulkanRenderer::QueueClearBufferPayload: draw framebuffer not found"); auto queueAttachmentClear = [&](FramebufferAttachmentType attachmentType) { if (attachmentType == FramebufferAttachmentType::None) { return; } - const auto& attachment = fbo->GetAttachment(attachmentType); + const auto& attachment = framebuffer.GetAttachment(attachmentType); if (!attachment.IsTexture() || attachment.IsRenderbuffer()) { return; } @@ -2819,7 +2991,7 @@ void main() { RecordClearBufferError(__func__, ErrorCode::InvalidValue, "color drawbuffer index is out of range"); return; } - queueAttachmentClear(fbo->GetDrawBuffers()[drawbuffer]); + queueAttachmentClear(framebuffer.GetDrawBuffers()[drawbuffer]); return; } case GL_DEPTH: @@ -2850,6 +3022,13 @@ void main() { } } + void VulkanRenderer::QueueClearBufferPayload(GLenum buffer, GLint drawbuffer, + const ClearAttachmentPayload& clearPayload) { + auto* fbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject().get(); + MOBILEGL_ASSERT(fbo, "VulkanRenderer::QueueClearBufferPayload: draw framebuffer not found"); + QueueClearBufferPayloadForFramebuffer(*fbo, buffer, drawbuffer, clearPayload); + } + void VulkanRenderer::ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { ClearAttachmentPayload payload{}; payload.mask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; @@ -2878,6 +3057,41 @@ void main() { QueueClearBufferPayload(buffer, drawbuffer, payload); } + void VulkanRenderer::ClearNamedFramebufferfv( + const SharedPtr& framebuffer, GLenum buffer, GLint drawbuffer, + const GLfloat* value) { + if (!framebuffer || value == nullptr) { + return; + } + ClearAttachmentPayload payload{}; + switch (buffer) { + case GL_COLOR: + payload.mask = GL_COLOR_BUFFER_BIT; + payload.color = FloatVec4(value[0], value[1], value[2], value[3]); + break; + case GL_DEPTH: + payload.mask = GL_DEPTH_BUFFER_BIT; + payload.depth = value[0]; + break; + default: + break; + } + QueueClearBufferPayloadForFramebuffer(*framebuffer, buffer, drawbuffer, payload); + } + + void VulkanRenderer::ClearNamedFramebufferfi( + const SharedPtr& framebuffer, GLenum buffer, GLint drawbuffer, + GLfloat depth, GLint stencil) { + if (!framebuffer) { + return; + } + ClearAttachmentPayload payload{}; + payload.mask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + payload.depth = depth; + payload.stencil = static_cast(stencil); + QueueClearBufferPayloadForFramebuffer(*framebuffer, buffer, drawbuffer, payload); + } + void VulkanRenderer::ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) { if (value == nullptr) { return; @@ -3137,6 +3351,16 @@ void main() { void VulkanRenderer::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { + auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + BlitNamedFramebuffer(readFbo, drawFbo, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + } + + void VulkanRenderer::BlitNamedFramebuffer(const SharedPtr& readFbo, + const SharedPtr& drawFbo, + GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) { static constexpr GLbitfield kSupportedBlitMask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT; if ((mask & ~kSupportedBlitMask) != 0) { MGLOG_E("BlitFramebuffer skipped: unsupported mask bits=0x%x", static_cast(mask)); @@ -3161,8 +3385,6 @@ void main() { return; } - auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); - auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); MOBILEGL_ASSERT(readFbo != nullptr, "VulkanRenderer::BlitFramebuffer: read framebuffer is null"); MOBILEGL_ASSERT(drawFbo != nullptr, "VulkanRenderer::BlitFramebuffer: draw framebuffer is null"); @@ -3461,10 +3683,18 @@ void main() { MOBILEGL_ASSERT(ok, "%s: failed to restore source image layout", __func__); } - if (!drawIsDefaultFbo) { - VkPipelineStageFlags dstRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - VkAccessFlags dstRestoreAccessMask = 0; - GetImageTransitionDestinationState(dstRestoreLayout, dstRestoreStageMask, dstRestoreAccessMask); + VkPipelineStageFlags dstRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkAccessFlags dstRestoreAccessMask = 0; + GetImageTransitionDestinationState(dstRestoreLayout, dstRestoreStageMask, dstRestoreAccessMask); + if (drawIsDefaultFbo) { + VkImageLayout dstTrackedLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired); + Bool ok = VkTextureManager::TransitionImageLayout( + frame.commandBuffer, dstBinding.image, dstTrackedLayout, dstRestoreLayout, + VK_PIPELINE_STAGE_TRANSFER_BIT, dstRestoreStageMask, + VK_ACCESS_TRANSFER_WRITE_BIT, dstRestoreAccessMask, dstBinding.aspectMask); + MOBILEGL_ASSERT(ok, "%s: failed to restore swapchain destination image layout", __func__); + m_swapchainObject.SetImageLayout(m_imageIndexAcquired, dstTrackedLayout); + } else { Bool ok = VkTextureManager::TransitionImageLayout( frame.commandBuffer, dstBinding.image, *dstBinding.trackedLayout, dstRestoreLayout, VK_PIPELINE_STAGE_TRANSFER_BIT, dstRestoreStageMask, @@ -3655,6 +3885,287 @@ void main() { MOBILEGL_ASSERT(dstRestored, "%s: failed to restore destination image layout", __func__); } + Bool VulkanRenderer::SubmitReadbackCommandsAndWait(FrameContext::FrameData& frame) { + if (frame.isCommandRecording) { + m_frameContext.EndCommandRecording(); + } + if (!frame.hasCommandBufferRecorded) { + return true; + } + + VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + VkSubmitInfo submitInfo{VK_STRUCTURE_TYPE_SUBMIT_INFO}; + VkSemaphore waitSemaphore = frame.imageAvailableSemaphore; + if (!frame.imageAvailableSemaphoreConsumed) { + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = &waitSemaphore; + submitInfo.pWaitDstStageMask = &waitDstStageMask; + } + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &frame.commandBuffer; + + VkResult result = vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, frame.imageInFlightFence); + if (result != VK_SUCCESS) { + MGLOG_E("DirectVulkan readback: vkQueueSubmit returned %d", result); + return false; + } + frame.imageAvailableSemaphoreConsumed = true; + + result = vkWaitForFences(m_device, 1, &frame.imageInFlightFence, VK_TRUE, UINT64_MAX); + if (result != VK_SUCCESS) { + MGLOG_E("DirectVulkan readback: vkWaitForFences returned %d", result); + return false; + } + result = vkResetFences(m_device, 1, &frame.imageInFlightFence); + if (result != VK_SUCCESS) { + MGLOG_E("DirectVulkan readback: vkResetFences returned %d", result); + return false; + } + + frame.hasCommandBufferRecorded = false; + frame.isCommandRecording = false; + return true; + } + + void VulkanRenderer::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, + void* pixels) { + if (width <= 0 || height <= 0) { + return; + } + + auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + if (readFbo == nullptr) { + MGLOG_E("DirectVulkan::ReadPixels skipped: no read framebuffer is bound"); + return; + } + + auto& frame = m_frameContext.GetCurrent(); + if (!frame.isCommandRecording) { + m_frameContext.BeginCommandRecording(); + m_uniformManager->BeginFrame(m_frameContext.GetCurrentFrameIndex()); + } + if (VkRenderPassManager::GetActiveRenderPass() != nullptr) { + VkRenderPassManager::EndRenderPass(frame.commandBuffer); + } + + const Bool readIsDefaultFbo = + (readFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO); + BlitImageBinding srcBinding{}; + if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, + srcBinding)) { + return; + } + if (!readIsDefaultFbo) { + const auto& sourceAttachment = readFbo->GetAttachment(readFbo->GetReadBuffer()); + auto sourceTexture = sourceAttachment.GetTexture(); + if (sourceTexture != nullptr) { + const Bool clearReady = MaterializePendingClearForTexture(frame.commandBuffer, *sourceTexture); + MOBILEGL_ASSERT(clearReady, + "ReadPixels: failed to materialize pending clear for source textureId=%d", + sourceTexture->GetExternalIndex()); + } + } + + const VkImageLayout srcOriginalLayout = readIsDefaultFbo + ? m_swapchainObject.GetImageLayout(m_imageIndexAcquired) + : *srcBinding.trackedLayout; + if (srcOriginalLayout == VK_IMAGE_LAYOUT_UNDEFINED) { + MGLOG_E("DirectVulkan::ReadPixels skipped: source image layout is undefined"); + return; + } + + const VkDeviceSize readbackSize = static_cast(width) * static_cast(height) * 4; + VkBufferObject readback; + if (!readback.Create({ + .allocator = m_allocator, + .size = readbackSize, + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .memoryUsage = VMA_MEMORY_USAGE_AUTO, + .allocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT, + })) { + MGLOG_E("DirectVulkan::ReadPixels skipped: failed to create readback buffer"); + return; + } + + VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkAccessFlags srcAccessMask = 0; + GetImageTransitionSourceState(srcOriginalLayout, srcStageMask, srcAccessMask); + if (readIsDefaultFbo) { + VkImageLayout trackedLayout = srcOriginalLayout; + Bool ok = VkTextureManager::TransitionImageLayout( + frame.commandBuffer, srcBinding.image, trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, + srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcBinding.aspectMask); + MOBILEGL_ASSERT(ok, "%s: failed to transition swapchain source image", __func__); + m_swapchainObject.SetImageLayout(m_imageIndexAcquired, trackedLayout); + } else { + Bool ok = VkTextureManager::TransitionImageLayout( + frame.commandBuffer, srcBinding.image, *srcBinding.trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, + srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcBinding.aspectMask, + srcBinding.mipLevel, 1); + MOBILEGL_ASSERT(ok, "%s: failed to transition source image", __func__); + } + + VkBufferImageCopy copyRegion{}; + copyRegion.imageSubresource.aspectMask = srcBinding.aspectMask; + copyRegion.imageSubresource.mipLevel = srcBinding.mipLevel; + copyRegion.imageSubresource.baseArrayLayer = 0; + copyRegion.imageSubresource.layerCount = 1; + copyRegion.imageOffset = {x, y, 0}; + copyRegion.imageExtent = {static_cast(width), static_cast(height), 1}; + vkCmdCopyImageToBuffer(frame.commandBuffer, srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + readback.GetHandle(), 1, ©Region); + + VkPipelineStageFlags restoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkAccessFlags restoreAccessMask = 0; + GetImageTransitionDestinationState(srcOriginalLayout, restoreStageMask, restoreAccessMask); + if (readIsDefaultFbo) { + VkImageLayout trackedLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired); + Bool ok = VkTextureManager::TransitionImageLayout( + frame.commandBuffer, srcBinding.image, trackedLayout, srcOriginalLayout, + VK_PIPELINE_STAGE_TRANSFER_BIT, restoreStageMask, + VK_ACCESS_TRANSFER_READ_BIT, restoreAccessMask, srcBinding.aspectMask); + MOBILEGL_ASSERT(ok, "%s: failed to restore swapchain source image layout", __func__); + m_swapchainObject.SetImageLayout(m_imageIndexAcquired, trackedLayout); + } else { + Bool ok = VkTextureManager::TransitionImageLayout( + frame.commandBuffer, srcBinding.image, *srcBinding.trackedLayout, srcOriginalLayout, + VK_PIPELINE_STAGE_TRANSFER_BIT, restoreStageMask, + VK_ACCESS_TRANSFER_READ_BIT, restoreAccessMask, srcBinding.aspectMask, + srcBinding.mipLevel, 1); + MOBILEGL_ASSERT(ok, "%s: failed to restore source image layout", __func__); + } + + if (!SubmitReadbackCommandsAndWait(frame)) { + return; + } + const auto* mapped = static_cast(readback.Map()); + if (mapped == nullptr) { + MGLOG_E("DirectVulkan::ReadPixels skipped: failed to map readback buffer"); + return; + } + const VkFormat srcFormat = readIsDefaultFbo ? m_swapchainObject.GetSurfaceFormat().format : VK_FORMAT_R8G8B8A8_UNORM; + PackReadbackToClientOrPbo(mapped, srcFormat, width, height, format, type, pixels); + } + + void VulkanRenderer::GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { + const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + auto textureObject = activeUnit.GetBindingSlot(textureTarget).GetBoundObject(); + GetTextureImage(textureObject, textureUploadTarget, level, format, type, -1, pixels); + } + + void VulkanRenderer::GetTextureImage(const SharedPtr& textureObject, + TextureUploadTarget textureUploadTarget, GLint level, GLenum format, + GLenum type, GLsizei bufSize, GLvoid* pixels) { + if (textureObject == nullptr || textureObject->GetStorageType() != TextureStorageType::Mipmap) { + return; + } + + auto* textureMipmapObject = static_cast(textureObject.get()); + if (level < 0 || static_cast(level) >= textureMipmapObject->GetMipmapLevelCount()) { + MGLOG_E("DirectVulkan::GetTexImage skipped: level %d is out of range", level); + return; + } + + auto* resource = m_textureManager->SyncTextureAndGetDescriptor(*textureObject); + if (resource == nullptr || resource->image == VK_NULL_HANDLE) { + MGLOG_E("DirectVulkan::GetTexImage skipped: failed to sync textureId=%u", + textureObject->GetExternalIndex()); + return; + } + if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) == 0) { + MGLOG_E("DirectVulkan::GetTexImage skipped: only color textures are supported right now"); + return; + } + + auto& frame = m_frameContext.GetCurrent(); + if (!frame.isCommandRecording) { + m_frameContext.BeginCommandRecording(); + m_uniformManager->BeginFrame(m_frameContext.GetCurrentFrameIndex()); + } + if (VkRenderPassManager::GetActiveRenderPass() != nullptr) { + VkRenderPassManager::EndRenderPass(frame.commandBuffer); + } + const Bool clearReady = MaterializePendingClearForTexture(frame.commandBuffer, *textureObject); + MOBILEGL_ASSERT(clearReady, + "GetTexImage: failed to materialize pending clear for textureId=%d", + textureObject->GetExternalIndex()); + + const auto texelSize = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, static_cast(level)); + const GLsizei width = texelSize.x(); + const GLsizei height = texelSize.y(); + if (width <= 0 || height <= 0) { + return; + } + if (bufSize >= 0) { + const Int dstChannels = GetReadbackChannelCount(format); + if (type == GL_UNSIGNED_BYTE && dstChannels > 0) { + const SizeT minSize = static_cast(width) * static_cast(height) * + static_cast(dstChannels); + if (static_cast(bufSize) < minSize) { + MGLOG_E("DirectVulkan::GetTextureImage skipped: destination buffer is too small"); + return; + } + } + } + + const VkDeviceSize readbackSize = static_cast(width) * static_cast(height) * 4; + VkBufferObject readback; + if (!readback.Create({ + .allocator = m_allocator, + .size = readbackSize, + .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .memoryUsage = VMA_MEMORY_USAGE_AUTO, + .allocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT, + })) { + MGLOG_E("DirectVulkan::GetTexImage skipped: failed to create readback buffer"); + return; + } + + const VkImageLayout originalLayout = resource->layout; + VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkAccessFlags srcAccessMask = 0; + GetImageTransitionSourceState(originalLayout, srcStageMask, srcAccessMask); + Bool ok = VkTextureManager::TransitionImageLayout( + frame.commandBuffer, resource->image, resource->layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, + srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, resource->aspect, + static_cast(level), 1); + MOBILEGL_ASSERT(ok, "%s: failed to transition texture image", __func__); + + VkBufferImageCopy copyRegion{}; + copyRegion.imageSubresource.aspectMask = resource->aspect; + copyRegion.imageSubresource.mipLevel = static_cast(level); + copyRegion.imageSubresource.baseArrayLayer = 0; + copyRegion.imageSubresource.layerCount = 1; + copyRegion.imageExtent = {static_cast(width), static_cast(height), 1}; + vkCmdCopyImageToBuffer(frame.commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + readback.GetHandle(), 1, ©Region); + + VkPipelineStageFlags restoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkAccessFlags restoreAccessMask = 0; + GetImageTransitionDestinationState(originalLayout, restoreStageMask, restoreAccessMask); + ok = VkTextureManager::TransitionImageLayout( + frame.commandBuffer, resource->image, resource->layout, originalLayout, + VK_PIPELINE_STAGE_TRANSFER_BIT, restoreStageMask, + VK_ACCESS_TRANSFER_READ_BIT, restoreAccessMask, resource->aspect, + static_cast(level), 1); + MOBILEGL_ASSERT(ok, "%s: failed to restore texture image layout", __func__); + + if (!SubmitReadbackCommandsAndWait(frame)) { + return; + } + const auto* mapped = static_cast(readback.Map()); + if (mapped == nullptr) { + MGLOG_E("DirectVulkan::GetTextureImage skipped: failed to map readback buffer"); + return; + } + PackReadbackToClientOrPbo(mapped, resource->format, width, height, format, type, pixels); + } + void VulkanRenderer::GenerateMipmap(GLenum target) { const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); const auto uploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); @@ -3927,6 +4438,108 @@ void main() { } } + void VulkanRenderer::MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, + GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) { + auto& frame = m_frameContext.GetCurrent(); + + if (maxdrawcount <= 0) { + return; + } + if (stride == 0) { + stride = sizeof(DrawIndexedCmdParam); + } + if (stride < static_cast(sizeof(DrawIndexedCmdParam))) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: stride %d is smaller than command size %zu", + stride, sizeof(DrawIndexedCmdParam)); + return; + } + + const SizeT indexSize = MG_Util::GetGLTypeSize(type); + if (indexSize == 0) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: unsupported index type 0x%x", type); + return; + } + + const auto& vao = *MG_State::pGLContext->GetBoundVertexArray(); + const auto* indexBuffer = vao.GetIndexBufferBindingSlot().GetBoundObject().get(); + if (!indexBuffer) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: no element array buffer is bound"); + return; + } + + const SizeT commandOffset = reinterpret_cast(indirect); + const SizeT commandBytes = commandOffset + + static_cast(stride) * static_cast(maxdrawcount - 1) + sizeof(DrawIndexedCmdParam); + auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject(); + if (!drawBuffer || commandBytes > drawBuffer->GetSize()) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range"); + return; + } + + auto parameterBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject(); + if (!parameterBuffer || static_cast(drawcount) + sizeof(Uint32) > parameterBuffer->GetSize()) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range"); + return; + } + + DrawCmdParam vertexRange{}; + vertexRange.vertexCount = static_cast(indexBuffer->GetSize() / indexSize); + vertexRange.instanceCount = 1; + + IndexBufferView indexBufferView{}; + indexBufferView.indexType = type; + indexBufferView.indexByteOffset = 0; + indexBufferView.indexByteSize = indexBuffer->GetSize(); + + if (!SetupDraw(frame, mode, DrawSetupAspect::IndexBuffer | DrawSetupAspect::IndirectDrawBuffer, + vertexRange, &indexBufferView)) { + return; + } + + drawBuffer->MarkPersistentMappedRangeDirty(); + parameterBuffer->MarkPersistentMappedRangeDirty(); + + BufferSlice drawSlice{}; + if (!m_bufferManager.SyncResidentBuffer(BufferKind::Indirect, drawBuffer, drawSlice)) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: failed to sync draw indirect buffer"); + return; + } + BufferSlice parameterSlice{}; + if (!m_bufferManager.SyncResidentBuffer(BufferKind::Indirect, parameterBuffer, parameterSlice)) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: failed to sync parameter buffer"); + return; + } + + MOBILEGL_ASSERT(frame.isCommandRecording, "%s: frame recording was not started", __func__); + if (m_drawIndirectCountExtensionEnabled && s_vkCmdDrawIndexedIndirectCount) { + MGLOG_D("DirectVulkan: glMultiDrawElementsIndirectCountARB(max=%d stride=%d)", maxdrawcount, stride); + s_vkCmdDrawIndexedIndirectCount(frame.commandBuffer, + drawSlice.buffer, + drawSlice.offset + static_cast(commandOffset), + parameterSlice.buffer, + parameterSlice.offset + static_cast(drawcount), + static_cast(maxdrawcount), + static_cast(stride)); + return; + } + + const auto parameterData = parameterBuffer->GetDataReadOnly(); + const auto drawData = drawBuffer->GetDataReadOnly(); + if (!parameterData || !drawData) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: CPU fallback cannot read buffers"); + return; + } + Uint32 actualDrawCount = 0; + std::memcpy(&actualDrawCount, parameterData->data() + drawcount, sizeof(actualDrawCount)); + actualDrawCount = std::min(actualDrawCount, static_cast(maxdrawcount)); + for (Uint32 idraw = 0; idraw < actualDrawCount; ++idraw) { + DrawIndexedCmdParam cmd{}; + std::memcpy(&cmd, drawData->data() + commandOffset + static_cast(idraw) * stride, sizeof(cmd)); + vkCmdDrawIndexed(frame.commandBuffer, cmd.indexCount, cmd.instanceCount, cmd.firstIndex, + cmd.vertexOffset, cmd.firstInstance); + } + } + void VulkanRenderer::Present() { MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(), "Present, acquired image index out of range"); @@ -4354,6 +4967,8 @@ void main() { deviceFeatures.independentBlend = supportedDeviceFeatures.independentBlend; deviceFeatures.shaderClipDistance = supportedDeviceFeatures.shaderClipDistance; deviceFeatures.shaderCullDistance = supportedDeviceFeatures.shaderCullDistance; + deviceFeatures.shaderInt64 = supportedDeviceFeatures.shaderInt64; + deviceFeatures.drawIndirectFirstInstance = supportedDeviceFeatures.drawIndirectFirstInstance; VkDeviceCreateInfo deviceCreateInfo{}; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; @@ -4418,16 +5033,20 @@ void main() { deviceCreateInfo.enabledExtensionCount = static_cast(enabledDeviceExtensions.size()); deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data(); - MGLOG_I("Device feature support: geometryShader=%s independentBlend=%s shaderClipDistance=%s shaderCullDistance=%s", + MGLOG_I("Device feature support: geometryShader=%s independentBlend=%s shaderClipDistance=%s shaderCullDistance=%s shaderInt64=%s drawIndirectFirstInstance=%s", supportedDeviceFeatures.geometryShader ? "true" : "false", supportedDeviceFeatures.independentBlend ? "true" : "false", supportedDeviceFeatures.shaderClipDistance ? "true" : "false", - supportedDeviceFeatures.shaderCullDistance ? "true" : "false"); - MGLOG_I("Device feature enabled: geometryShader=%s independentBlend=%s shaderClipDistance=%s shaderCullDistance=%s", + supportedDeviceFeatures.shaderCullDistance ? "true" : "false", + supportedDeviceFeatures.shaderInt64 ? "true" : "false", + supportedDeviceFeatures.drawIndirectFirstInstance ? "true" : "false"); + MGLOG_I("Device feature enabled: geometryShader=%s independentBlend=%s shaderClipDistance=%s shaderCullDistance=%s shaderInt64=%s drawIndirectFirstInstance=%s", deviceFeatures.geometryShader ? "true" : "false", deviceFeatures.independentBlend ? "true" : "false", deviceFeatures.shaderClipDistance ? "true" : "false", - deviceFeatures.shaderCullDistance ? "true" : "false"); + deviceFeatures.shaderCullDistance ? "true" : "false", + deviceFeatures.shaderInt64 ? "true" : "false", + deviceFeatures.drawIndirectFirstInstance ? "true" : "false"); VK_VERIFY(vkCreateDevice(m_physicalDevice.handle, &deviceCreateInfo, nullptr, &m_device), "vkCreateDevice"); s_vkCmdDrawIndexedIndirectCount = reinterpret_cast( @@ -4634,6 +5253,8 @@ void main() { Vector& inOutEnabledExtensions) { m_drawIndirectCountExtensionEnabled = EnableOptionalDeviceExtension(availableExtensions, inOutEnabledExtensions, VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME); + EnableOptionalDeviceExtension(availableExtensions, inOutEnabledExtensions, + VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME); #ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME EnableOptionalDeviceExtension(availableExtensions, inOutEnabledExtensions, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 39627a3b..d50e0dab 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -120,17 +120,34 @@ namespace MobileGL::MG_Backend::DirectVulkan { void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value); void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value); void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value); + void ClearNamedFramebufferfv(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, const GLfloat* value); + void ClearNamedFramebufferfi(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + void BlitNamedFramebuffer(const SharedPtr& readFbo, + const SharedPtr& drawFbo, + GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); void GenerateMipmap(GLenum target); + void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); + void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels); + void GetTextureImage(const SharedPtr& texture, + TextureUploadTarget uploadTarget, GLint level, GLenum format, GLenum type, + GLsizei bufSize, GLvoid* pixels); void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ); + void DispatchComputeIndirect(GLintptr indirect); void MemoryBarrier(GLbitfield barriers); void DrawArrays(const DrawCmd& payload); void DrawElements(const DrawIndexedCmd& payload); void MultiDrawElements(const MultiDrawIndexedCmd& payloads); + void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride); void Present(); const PhysicalDevice& GetPhysicalDevice() const; @@ -173,6 +190,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { }; void QueueClearBufferPayload(GLenum buffer, GLint drawbuffer, const ClearAttachmentPayload& clearPayload); + void QueueClearBufferPayloadForFramebuffer(const MG_State::GLState::FramebufferObject& framebuffer, + GLenum buffer, GLint drawbuffer, + const ClearAttachmentPayload& clearPayload); NativeWindowType m_window = 0; void* m_platformDisplay = nullptr; @@ -279,6 +299,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { const IntVec3& storageBaseTexelSize, VkImageLayout originalLayout, VkImageLayout finalLayout); + Bool SubmitReadbackCommandsAndWait(FrameContext::FrameData& frame); void ShutdownSwapchain(); diff --git a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp index 329b747f..ac029a6f 100644 --- a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp @@ -21,9 +21,13 @@ namespace MobileGL::MG_Impl::GLImpl { GetBufferParameteri64v, GetBufferPointerv, BufferStorage, + CreateBuffers, NamedBufferStorage, NamedBufferData, NamedBufferSubData, + CopyNamedBufferSubData, + ClearNamedBufferData, + ClearNamedBufferSubData, MapBufferRange, MapBuffer, MapNamedBuffer, @@ -45,12 +49,20 @@ namespace MobileGL::MG_Impl::GLImpl { return "GetBufferPointerv"; case BufferOp::BufferStorage: return "BufferStorage"; + case BufferOp::CreateBuffers: + return "CreateBuffers"; case BufferOp::NamedBufferStorage: return "NamedBufferStorage"; case BufferOp::NamedBufferData: return "NamedBufferData"; case BufferOp::NamedBufferSubData: return "NamedBufferSubData"; + case BufferOp::CopyNamedBufferSubData: + return "CopyNamedBufferSubData"; + case BufferOp::ClearNamedBufferData: + return "ClearNamedBufferData"; + case BufferOp::ClearNamedBufferSubData: + return "ClearNamedBufferSubData"; case BufferOp::MapBufferRange: return "MapBufferRange"; case BufferOp::MapBuffer: @@ -74,6 +86,90 @@ namespace MobileGL::MG_Impl::GLImpl { } } + SharedPtr GetNamedBufferObject(GLuint buffer, BufferOp op); + + SizeT GetClearPatternSize(GLenum internalformat, GLenum format, GLenum type, BufferOp op) { + if (format != GL_RED_INTEGER) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", GetBufferOpName(op), + "Only GL_RED_INTEGER buffer clears are currently supported.")); + return 0; + } + + if (internalformat == GL_R8UI && type == GL_UNSIGNED_BYTE) return sizeof(GLubyte); + if (internalformat == GL_R32UI && type == GL_UNSIGNED_INT) return sizeof(GLuint); + + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", GetBufferOpName(op), + std::format("Unsupported clear format tuple: internalformat=0x{:X}, " + "format=0x{:X}, type=0x{:X}", + internalformat, format, type))); + return 0; + } + + Bool ValidateBufferClearRange(const SharedPtr& bufferObject, GLintptr offset, + GLsizeiptr size, SizeT patternSize, BufferOp op) { + if (offset < 0 || size < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", GetBufferOpName(op), + "Offset and size must be non-negative.")); + return false; + } + + if (patternSize == 0 || (static_cast(offset) % patternSize) != 0 || + (static_cast(size) % patternSize) != 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", GetBufferOpName(op), + "Offset and size must be aligned to the clear element size.")); + return false; + } + + if (static_cast(offset) + static_cast(size) > bufferObject->GetSize()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", GetBufferOpName(op), + "Offset and size exceed buffer size.")); + return false; + } + + if (bufferObject->IsMapped() && !(bufferObject->GetMappingAccess() & BufferMappingAccessBit::Persistent)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", GetBufferOpName(op), + "Cannot clear a non-persistently mapped buffer object.")); + return false; + } + + return true; + } + + void ClearNamedBufferRange_State(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, + GLenum format, GLenum type, const void* data, BufferOp op) { + const SizeT patternSize = GetClearPatternSize(internalformat, format, type, op); + if (patternSize == 0) return; + + auto bufferObject = GetNamedBufferObject(buffer, op); + if (!bufferObject) return; + if (!ValidateBufferClearRange(bufferObject, offset, size, patternSize, op)) return; + if (size == 0) return; + + Vector clearData(static_cast(size)); + if (data) { + const auto* pattern = static_cast(data); + for (SizeT at = 0; at < clearData.size(); at += patternSize) { + Memcpy(clearData.data() + at, pattern, patternSize); + } + } else { + Memset(clearData.data(), 0, clearData.size()); + } + + bufferObject->UploadSubData({clearData.data(), clearData.size()}, static_cast(offset)); + } + auto& GetBufferBindingSlot(BufferTarget target) { if (target == BufferTarget::Index) { return MG_State::pGLContext->GetBoundVertexArray()->GetIndexBufferBindingSlot(); @@ -765,6 +861,29 @@ namespace MobileGL::MG_Impl::GLImpl { bufferObject->AllocateImmutableStorage(static_cast(size), data, flags); } + void CreateBuffers_State(GLsizei n, GLuint* buffers) { + if (n < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "CreateBuffers_State", "Count must be non-negative.")); + return; + } + if (n > 0 && !buffers) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "CreateBuffers_State", + "Buffer output pointer cannot be null.")); + return; + } + + Vector bufferNames; + MG_State::pGLContext->GenBufferNames(static_cast(n), bufferNames); + for (GLsizei i = 0; i < n; ++i) { + buffers[i] = bufferNames[i]; + MG_State::pGLContext->CreateBufferObject(bufferNames[i]); + } + } + void NamedBufferStorage_State(GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) { if (size <= 0) { MG_State::pGLContext->RecordError( @@ -862,6 +981,68 @@ namespace MobileGL::MG_Impl::GLImpl { bufferObject->UploadSubData({(void*)data, (SizeT)size}, offset); } + void CopyNamedBufferSubData_State(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, + GLsizeiptr size) { + if (size < 0 || readOffset < 0 || writeOffset < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "CopyNamedBufferSubData_State", + "Offset and size must be non-negative.")); + return; + } + + auto readBufferObject = GetNamedBufferObject(readBuffer, BufferOp::CopyNamedBufferSubData); + auto writeBufferObject = GetNamedBufferObject(writeBuffer, BufferOp::CopyNamedBufferSubData); + if (!readBufferObject || !writeBufferObject) return; + + if (static_cast(readOffset) + static_cast(size) > readBufferObject->GetSize() || + static_cast(writeOffset) + static_cast(size) > writeBufferObject->GetSize()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "CopyNamedBufferSubData_State", + "Offset and size must be within the bounds of the buffer objects.")); + return; + } + + if (readBufferObject == writeBufferObject) { + if ((readOffset <= writeOffset && readOffset + size > writeOffset) || + (writeOffset <= readOffset && writeOffset + size > readOffset)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "CopyNamedBufferSubData_State", + "Source and destination ranges overlap.")); + return; + } + } + + auto isIllegallyMapped = [](const SharedPtr& buffer) { + return buffer->IsMapped() && !(buffer->GetMappingAccess() & BufferMappingAccessBit::Persistent); + }; + if (isIllegallyMapped(readBufferObject) || isIllegallyMapped(writeBufferObject)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", "CopyNamedBufferSubData_State", + "Cannot copy data from/to a non-persistently mapped buffer object.")); + return; + } + + writeBufferObject->CopyDataFrom(readBufferObject, static_cast(readOffset), + static_cast(writeOffset), static_cast(size)); + } + + void ClearNamedBufferData_State(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) { + auto bufferObject = GetNamedBufferObject(buffer, BufferOp::ClearNamedBufferData); + if (!bufferObject) return; + ClearNamedBufferRange_State(buffer, internalformat, 0, static_cast(bufferObject->GetSize()), format, + type, data, BufferOp::ClearNamedBufferData); + } + + void ClearNamedBufferSubData_State(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, + GLenum format, GLenum type, const void* data) { + ClearNamedBufferRange_State(buffer, internalformat, offset, size, format, type, data, + BufferOp::ClearNamedBufferSubData); + } + 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; @@ -1070,7 +1251,7 @@ namespace MobileGL::MG_Impl::GLImpl { MakeUnique("MG_Impl/GLImpl", "GenBuffers_State", "n must be non-negative")); return; } - static thread_local Vector bufferNames; + Vector bufferNames; MG_State::pGLContext->GenBufferNames(n, bufferNames); Memcpy(buffers, bufferNames.data(), n * sizeof(GLuint)); } @@ -1086,13 +1267,19 @@ namespace MobileGL::MG_Impl::GLImpl { BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target); if (!BufferImpl::ValidateBufferBindingPointTarget(bufferTarget)) return; + auto& point = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, pointIndex); + if (buffer == 0) { + point.Bind(nullptr); + point.SetRange(Range1D(0, 0)); + return; + } + Bool doesBufferObjectCreated = MG_State::pGLContext->ValidateBufferObject(buffer); if (!doesBufferObjectCreated) { MG_State::pGLContext->CreateBufferObject(buffer); } auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer); - auto& point = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, pointIndex); point.Bind(bufferObject); point.SetRange(Range1D(0, bufferObject->GetSize())); MGLOG_D("%s: set range (0, %d)", __func__, bufferObject->GetSize()); @@ -1104,13 +1291,19 @@ namespace MobileGL::MG_Impl::GLImpl { BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target); if (!BufferImpl::ValidateBufferBindingPointTarget(bufferTarget)) return; + auto& point = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, index); + if (buffer == 0) { + point.Bind(nullptr); + point.SetRange(Range1D(0, 0)); + return; + } + Bool doesBufferObjectCreated = MG_State::pGLContext->ValidateBufferObject(buffer); if (!doesBufferObjectCreated) { MG_State::pGLContext->CreateBufferObject(buffer); } auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer); - auto& point = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, index); point.Bind(bufferObject); point.SetRange(Range1D(offset, offset + size)); } @@ -1160,6 +1353,10 @@ namespace MobileGL::MG_Impl::GLImpl { NamedBufferStorage_State(buffer, size, data, flags); } + void CreateBuffers(GLsizei n, GLuint* buffers) { + CreateBuffers_State(n, buffers); + } + void NamedBufferData(GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) { NamedBufferData_State(buffer, size, data, usage); } @@ -1168,6 +1365,20 @@ namespace MobileGL::MG_Impl::GLImpl { NamedBufferSubData_State(buffer, offset, size, data); } + void CopyNamedBufferSubData(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, + GLsizeiptr size) { + CopyNamedBufferSubData_State(readBuffer, writeBuffer, readOffset, writeOffset, size); + } + + void ClearNamedBufferData(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) { + ClearNamedBufferData_State(buffer, internalformat, format, type, data); + } + + void ClearNamedBufferSubData(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, + GLenum type, const void* data) { + ClearNamedBufferSubData_State(buffer, internalformat, offset, size, format, type, data); + } + void* MapNamedBuffer(GLuint buffer, GLenum access) { return MapNamedBuffer_State(buffer, access); } diff --git a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h index 48f48fbe..24115cf4 100644 --- a/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h +++ b/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.h @@ -21,9 +21,15 @@ namespace MobileGL::MG_Impl::GLImpl { 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 CreateBuffers(GLsizei n, GLuint* buffers); 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 CopyNamedBufferSubData(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, + GLsizeiptr size); + 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, + GLenum type, const void* data); void* MapNamedBuffer(GLuint buffer, GLenum access); void* MapNamedBufferRange(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); GLboolean UnmapNamedBuffer(GLuint buffer); diff --git a/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp b/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp index 155a8e03..406e7fc1 100644 --- a/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp +++ b/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.cpp @@ -73,6 +73,24 @@ namespace MobileGL::MG_Impl::GLImpl { MG_Backend::gBackendFunctionsTable.GL.MultiDrawArraysIndirect(mode, indirect, drawcount, stride); } + void MultiDrawElementsIndirectCount_Backend(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride) { +#ifdef TRACY_ENABLE + ZoneScopedC(TRACY_ZONECOLOR_BACKEND); +#endif + MG_Backend::gBackendFunctionsTable.GL.MultiDrawElementsIndirectCount(mode, type, indirect, drawcount, + maxdrawcount, stride); + } + + void MultiDrawArraysIndirectCount_Backend(GLenum mode, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride) { +#ifdef TRACY_ENABLE + ZoneScopedC(TRACY_ZONECOLOR_BACKEND); +#endif + MG_Backend::gBackendFunctionsTable.GL.MultiDrawArraysIndirectCount(mode, indirect, drawcount, maxdrawcount, + stride); + } + void DrawRangeElementsBaseVertex_Backend(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices, GLint basevertex) { #ifdef TRACY_ENABLE @@ -210,6 +228,32 @@ namespace MobileGL::MG_Impl::GLImpl { MultiDrawArraysIndirect_Backend(mode, indirect, drawcount, stride); } + void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride) { + auto multiDrawElementsIndirectCount = MG_Backend::gBackendFunctionsTable.GL.MultiDrawElementsIndirectCount; + if (!multiDrawElementsIndirectCount) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "Backend does not support indirect-parameter indexed draws.")); + return; + } + MultiDrawElementsIndirectCount_Backend(mode, type, indirect, drawcount, maxdrawcount, stride); + } + + void MultiDrawArraysIndirectCount(GLenum mode, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride) { + auto multiDrawArraysIndirectCount = MG_Backend::gBackendFunctionsTable.GL.MultiDrawArraysIndirectCount; + if (!multiDrawArraysIndirectCount) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "Backend does not support indirect-parameter array draws.")); + return; + } + MultiDrawArraysIndirectCount_Backend(mode, indirect, drawcount, maxdrawcount, stride); + } + void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices, GLint basevertex) { DrawRangeElementsBaseVertex_Backend(mode, start, end, count, type, indices, basevertex); diff --git a/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.h b/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.h index 64aa08bc..e6273147 100644 --- a/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.h +++ b/MobileGL/MG_Impl/GLImpl/Drawing/GL_Drawing.h @@ -17,6 +17,10 @@ namespace MobileGL::MG_Impl::GLImpl { void MemoryBarrierByRegion(GLbitfield barriers); void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride); void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride); + void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride); + void MultiDrawArraysIndirectCount(GLenum mode, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride); void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices, GLint basevertex); void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices); diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index fd332bb6..5bea76ac 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -103,8 +103,8 @@ DECLARE_GL_FUNCTION_HEAD(void, DrawArrays, GLenum mode, GLint first, GLsizei cou DECLARE_GL_FUNCTION_HEAD(void, DrawElements, GLenum mode, GLsizei count, GLenum type, const void* indices) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawElements, mode, count, type, indices) DECLARE_GL_FUNCTION_HEAD(void, Enable, GLenum cap) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Enable, cap) DECLARE_GL_FUNCTION_HEAD(void, EnableVertexAttribArray, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, EnableVertexAttribArray, index) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Finish) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Finish) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Flush) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Flush) +MOBILEGL_GL_API void glFinish() { MGLOG_D("Implementing function: %s(...)", __FUNCTION__); } +MOBILEGL_GL_API void glFlush() { MGLOG_D("Implementing function: %s(...)", __FUNCTION__); } DECLARE_GL_FUNCTION_HEAD(void, FramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FramebufferRenderbuffer, target, attachment, renderbuffertarget, renderbuffer) DECLARE_GL_FUNCTION_HEAD(void, FramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FramebufferTexture2D, target, attachment, textarget, texture, level) DECLARE_GL_FUNCTION_HEAD(void, FrontFace, GLenum mode) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FrontFace, mode) @@ -248,11 +248,11 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribI4iv, GLuint index, const GLint* DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribI4uiv, GLuint index, const GLuint* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribI4uiv, index, v) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetUniformuiv, GLuint program, GLint location, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetUniformuiv, program, location, params) DECLARE_GL_FUNCTION_HEAD(GLint, GetFragDataLocation, GLuint program, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetFragDataLocation, program, name) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform1ui, GLint location, GLuint v0) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform1ui, location, v0) +DECLARE_GL_FUNCTION_HEAD(void, Uniform1ui, GLint location, GLuint v0) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Uniform1ui, location, v0) DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform2ui, GLint location, GLuint v0, GLuint v1) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform2ui, location, v0, v1) DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform3ui, GLint location, GLuint v0, GLuint v1, GLuint v2) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform3ui, location, v0, v1, v2) DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform4ui, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform4ui, location, v0, v1, v2, v3) -DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform1uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform1uiv, location, count, value) +DECLARE_GL_FUNCTION_HEAD(void, Uniform1uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Uniform1uiv, location, count, value) DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform2uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform2uiv, location, count, value) DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform3uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform3uiv, location, count, value) DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform4uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform4uiv, location, count, value) @@ -274,7 +274,7 @@ DECLARE_GL_FUNCTION_HEAD(GLboolean, IsSync, GLsync sync) DECLARE_GL_FUNCTION_END 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_HEAD(void, GetInteger64v, GLenum pname, GLint64* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetInteger64v, pname, data) 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_HEAD(void, GetBufferParameteri64v, GLenum target, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBufferParameteri64v, target, pname, params) @@ -846,7 +846,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4usv, GLuint index, const GLusho DECLARE_GL_FUNCTION_STUB_HEAD(void, PrimitiveRestartIndex, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PrimitiveRestartIndex, index) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetActiveUniformName, GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformName) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetActiveUniformName, program, uniformIndex, bufSize, length, uniformName) DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsBaseVertex, GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount, const GLint* basevertex) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElementsBaseVertex, mode, count, type, indices, drawcount, basevertex) -DECLARE_GL_FUNCTION_STUB_HEAD(void, ProvokingVertex, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProvokingVertex, mode) +DECLARE_GL_FUNCTION_HEAD(void, ProvokingVertex, GLenum mode) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProvokingVertex, mode) DECLARE_GL_FUNCTION_HEAD(void, TexImage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexImage2DMultisample, target, samples, internalformat, width, height, fixedsamplelocations) DECLARE_GL_FUNCTION_HEAD(void, TexImage3DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexImage3DMultisample, target, samples, internalformat, width, height, depth, fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_HEAD(void, BindFragDataLocationIndexed, GLuint program, GLuint colorNumber, GLuint index, const GLchar* name) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindFragDataLocationIndexed, program, colorNumber, index, name) @@ -996,51 +996,51 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, TransformFeedbackBufferRange, GLuint xfb, GL DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTransformFeedbackiv, GLuint xfb, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTransformFeedbackiv, xfb, pname, param) 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_HEAD(void, CreateBuffers, GLsizei n, GLuint* buffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateBuffers, n, buffers) 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_HEAD(void, CopyNamedBufferSubData, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyNamedBufferSubData, readBuffer, writeBuffer, readOffset, writeOffset, size) +DECLARE_GL_FUNCTION_HEAD(void, ClearNamedBufferData, GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedBufferData, buffer, internalformat, format, type, data) +DECLARE_GL_FUNCTION_HEAD(void, ClearNamedBufferSubData, GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedBufferSubData, buffer, internalformat, offset, size, format, type, data) 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) +DECLARE_GL_FUNCTION_HEAD(void, CreateFramebuffers, GLsizei n, GLuint* framebuffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateFramebuffers, n, framebuffers) +DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferRenderbuffer, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferRenderbuffer, framebuffer, attachment, renderbuffertarget, renderbuffer) DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferParameteri, GLuint framebuffer, GLenum pname, GLint param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferParameteri, framebuffer, pname, param) -DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTexture, GLuint framebuffer, GLenum attachment, GLuint texture, GLint level) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferTexture, framebuffer, attachment, texture, level) +DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferTexture, GLuint framebuffer, GLenum attachment, GLuint texture, GLint level) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferTexture, framebuffer, attachment, texture, level) DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTextureLayer, GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferTextureLayer, framebuffer, attachment, texture, level, layer) -DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferDrawBuffer, GLuint framebuffer, GLenum buf) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferDrawBuffer, framebuffer, buf) -DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferDrawBuffers, GLuint framebuffer, GLsizei n, const GLenum* bufs) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferDrawBuffers, framebuffer, n, bufs) -DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferReadBuffer, GLuint framebuffer, GLenum src) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferReadBuffer, framebuffer, src) +DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferDrawBuffer, GLuint framebuffer, GLenum buf) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferDrawBuffer, framebuffer, buf) +DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferDrawBuffers, GLuint framebuffer, GLsizei n, const GLenum* bufs) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferDrawBuffers, framebuffer, n, bufs) +DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferReadBuffer, GLuint framebuffer, GLenum src) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferReadBuffer, framebuffer, src) DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateNamedFramebufferData, GLuint framebuffer, GLsizei numAttachments, const GLenum* attachments) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateNamedFramebufferData, framebuffer, numAttachments, attachments) DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateNamedFramebufferSubData, GLuint framebuffer, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateNamedFramebufferSubData, framebuffer, numAttachments, attachments, x, y, width, height) DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedFramebufferiv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedFramebufferiv, framebuffer, buffer, drawbuffer, value) DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedFramebufferuiv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedFramebufferuiv, framebuffer, buffer, drawbuffer, value) -DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedFramebufferfv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedFramebufferfv, framebuffer, buffer, drawbuffer, value) -DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedFramebufferfi, GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedFramebufferfi, framebuffer, buffer, drawbuffer, depth, stencil) -DECLARE_GL_FUNCTION_STUB_HEAD(void, BlitNamedFramebuffer, GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlitNamedFramebuffer, readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) -DECLARE_GL_FUNCTION_STUB_HEAD(GLenum, CheckNamedFramebufferStatus, GLuint framebuffer, GLenum target) DECLARE_GL_FUNCTION_STUB_END(GLenum, CheckNamedFramebufferStatus, framebuffer, target) +DECLARE_GL_FUNCTION_HEAD(void, ClearNamedFramebufferfv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedFramebufferfv, framebuffer, buffer, drawbuffer, value) +DECLARE_GL_FUNCTION_HEAD(void, ClearNamedFramebufferfi, GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedFramebufferfi, framebuffer, buffer, drawbuffer, depth, stencil) +DECLARE_GL_FUNCTION_HEAD(void, BlitNamedFramebuffer, GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlitNamedFramebuffer, readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) +DECLARE_GL_FUNCTION_HEAD(GLenum, CheckNamedFramebufferStatus, GLuint framebuffer, GLenum target) DECLARE_GL_FUNCTION_END(GLenum, CheckNamedFramebufferStatus, framebuffer, target) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedFramebufferParameteriv, GLuint framebuffer, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedFramebufferParameteriv, framebuffer, pname, param) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedFramebufferAttachmentParameteriv, GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedFramebufferAttachmentParameteriv, framebuffer, attachment, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateRenderbuffers, GLsizei n, GLuint* renderbuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateRenderbuffers, n, renderbuffers) -DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorage, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorage, renderbuffer, internalformat, width, height) +DECLARE_GL_FUNCTION_HEAD(void, GetNamedFramebufferAttachmentParameteriv, GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedFramebufferAttachmentParameteriv, framebuffer, attachment, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, CreateRenderbuffers, GLsizei n, GLuint* renderbuffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateRenderbuffers, n, renderbuffers) +DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorage, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedRenderbufferStorage, renderbuffer, internalformat, width, height) DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorageMultisample, GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorageMultisample, renderbuffer, samples, internalformat, width, height) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedRenderbufferParameteriv, GLuint renderbuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedRenderbufferParameteriv, renderbuffer, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateTextures, GLenum target, GLsizei n, GLuint* textures) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateTextures, target, n, textures) +DECLARE_GL_FUNCTION_HEAD(void, GetNamedRenderbufferParameteriv, GLuint renderbuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedRenderbufferParameteriv, renderbuffer, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, CreateTextures, GLenum target, GLsizei n, GLuint* textures) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateTextures, target, n, textures) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBuffer, GLuint texture, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBuffer, texture, internalformat, buffer) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBufferRange, GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBufferRange, texture, internalformat, buffer, offset, size) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage1D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage1D, texture, levels, internalformat, width) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage2D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage2D, texture, levels, internalformat, width, height) +DECLARE_GL_FUNCTION_HEAD(void, TextureStorage2D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage2D, texture, levels, internalformat, width, height) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage3D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage3D, texture, levels, internalformat, width, height, depth) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage2DMultisample, GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage2DMultisample, texture, samples, internalformat, width, height, fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage3DMultisample, GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage3DMultisample, texture, samples, internalformat, width, height, depth, fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureSubImage1D, texture, level, xoffset, width, format, type, pixels) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, type, pixels) +DECLARE_GL_FUNCTION_HEAD(void, TextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, type, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureSubImage3D, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage1D, texture, level, xoffset, width, format, imageSize, data) DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, imageSize, data) @@ -1048,31 +1048,31 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage3D, GLuint texture, DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage1D, texture, level, xoffset, x, y, width) DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage2D, texture, level, xoffset, yoffset, x, y, width, height) DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage3D, texture, level, xoffset, yoffset, zoffset, x, y, width, height) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterf, GLuint texture, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterf, texture, pname, param) +DECLARE_GL_FUNCTION_HEAD(void, TextureParameterf, GLuint texture, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterf, texture, pname, param) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterfv, GLuint texture, GLenum pname, const GLfloat* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterfv, texture, pname, param) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameteri, GLuint texture, GLenum pname, GLint param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameteri, texture, pname, param) +DECLARE_GL_FUNCTION_HEAD(void, TextureParameteri, GLuint texture, GLenum pname, GLint param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameteri, texture, pname, param) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterIiv, GLuint texture, GLenum pname, const GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterIiv, texture, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterIuiv, GLuint texture, GLenum pname, const GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterIuiv, texture, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameteriv, GLuint texture, GLenum pname, const GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameteriv, texture, pname, param) +DECLARE_GL_FUNCTION_HEAD(void, TextureParameteriv, GLuint texture, GLenum pname, const GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameteriv, texture, pname, param) DECLARE_GL_FUNCTION_STUB_HEAD(void, GenerateTextureMipmap, GLuint texture) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenerateTextureMipmap, texture) -DECLARE_GL_FUNCTION_STUB_HEAD(void, BindTextureUnit, GLuint unit, GLuint texture) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindTextureUnit, unit, texture) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureImage, GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureImage, texture, level, format, type, bufSize, pixels) +DECLARE_GL_FUNCTION_HEAD(void, BindTextureUnit, GLuint unit, GLuint texture) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindTextureUnit, unit, texture) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureImage, GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureImage, texture, level, format, type, bufSize, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetCompressedTextureImage, GLuint texture, GLint level, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetCompressedTextureImage, texture, level, bufSize, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureLevelParameterfv, GLuint texture, GLint level, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureLevelParameterfv, texture, level, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureLevelParameteriv, GLuint texture, GLint level, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureLevelParameteriv, texture, level, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureLevelParameteriv, GLuint texture, GLint level, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureLevelParameteriv, texture, level, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterfv, GLuint texture, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterfv, texture, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterIiv, GLuint texture, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterIiv, texture, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterIuiv, GLuint texture, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterIuiv, texture, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameteriv, GLuint texture, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameteriv, texture, pname, params) -DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateVertexArrays, GLsizei n, GLuint* arrays) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateVertexArrays, n, arrays) -DECLARE_GL_FUNCTION_STUB_HEAD(void, DisableVertexArrayAttrib, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DisableVertexArrayAttrib, vaobj, index) -DECLARE_GL_FUNCTION_STUB_HEAD(void, EnableVertexArrayAttrib, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, EnableVertexArrayAttrib, vaobj, index) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayElementBuffer, GLuint vaobj, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayElementBuffer, vaobj, buffer) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayVertexBuffer, GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayVertexBuffer, vaobj, bindingindex, buffer, offset, stride) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureParameteriv, GLuint texture, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureParameteriv, texture, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, CreateVertexArrays, GLsizei n, GLuint* arrays) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateVertexArrays, n, arrays) +DECLARE_GL_FUNCTION_HEAD(void, DisableVertexArrayAttrib, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DisableVertexArrayAttrib, vaobj, index) +DECLARE_GL_FUNCTION_HEAD(void, EnableVertexArrayAttrib, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, EnableVertexArrayAttrib, vaobj, index) +DECLARE_GL_FUNCTION_HEAD(void, VertexArrayElementBuffer, GLuint vaobj, GLuint buffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayElementBuffer, vaobj, buffer) +DECLARE_GL_FUNCTION_HEAD(void, VertexArrayVertexBuffer, GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayVertexBuffer, vaobj, bindingindex, buffer, offset, stride) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayVertexBuffers, GLuint vaobj, GLuint first, GLsizei count, const GLuint* buffers, const GLintptr* offsets, const GLsizei* strides) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayVertexBuffers, vaobj, first, count, buffers, offsets, strides) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayAttribBinding, GLuint vaobj, GLuint attribindex, GLuint bindingindex) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayAttribBinding, vaobj, attribindex, bindingindex) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayAttribFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayAttribFormat, vaobj, attribindex, size, type, normalized, relativeoffset) -DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayAttribIFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayAttribIFormat, vaobj, attribindex, size, type, relativeoffset) +DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribFormat, vaobj, attribindex, size, type, normalized, relativeoffset) +DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribIFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribIFormat, vaobj, attribindex, size, type, relativeoffset) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayAttribLFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayAttribLFormat, vaobj, attribindex, size, type, relativeoffset) DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayBindingDivisor, GLuint vaobj, GLuint bindingindex, GLuint divisor) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayBindingDivisor, vaobj, bindingindex, divisor) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayiv, GLuint vaobj, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayiv, vaobj, pname, param) @@ -1085,7 +1085,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryBufferObjecti64v, GLuint id, GLuint DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryBufferObjectiv, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryBufferObjectiv, id, buffer, pname, offset) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryBufferObjectui64v, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryBufferObjectui64v, id, buffer, pname, offset) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryBufferObjectuiv, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryBufferObjectuiv, id, buffer, pname, offset) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, bufSize, pixels) +DECLARE_GL_FUNCTION_HEAD(void, GetTextureSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, bufSize, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetCompressedTextureSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetCompressedTextureSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, bufSize, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnCompressedTexImage, GLenum target, GLint lod, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnCompressedTexImage, target, lod, bufSize, pixels) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnTexImage, GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnTexImage, target, level, format, type, bufSize, pixels) @@ -1104,8 +1104,8 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnHistogram, GLenum target, GLboolean rese DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnMinmax, GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void* values) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnMinmax, target, reset, format, type, bufSize, values) DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBarrier, void) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBarrier, ) DECLARE_GL_FUNCTION_STUB_HEAD(void, SpecializeShader, GLuint shader, const GLchar* pEntryPoint, GLuint numSpecializationConstants, const GLuint* pConstantIndex, const GLuint* pConstantValue) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, SpecializeShader, shader, pEntryPoint, numSpecializationConstants, pConstantIndex, pConstantValue) -DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiDrawArraysIndirectCount, GLenum mode, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiDrawArraysIndirectCount, mode, indirect, drawcount, maxdrawcount, stride) -DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiDrawElementsIndirectCount, GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiDrawElementsIndirectCount, mode, type, indirect, drawcount, maxdrawcount, stride) +DECLARE_GL_FUNCTION_HEAD(void, MultiDrawArraysIndirectCount, GLenum mode, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawArraysIndirectCount, mode, indirect, drawcount, maxdrawcount, stride) +DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsIndirectCount, GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElementsIndirectCount, mode, type, indirect, drawcount, maxdrawcount, stride) DECLARE_GL_FUNCTION_STUB_HEAD(void, PolygonOffsetClamp, GLfloat factor, GLfloat units, GLfloat clamp) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PolygonOffsetClamp, factor, units, clamp) DECLARE_GL_FUNCTION_STUB_HEAD(void, PrimitiveBoundingBoxARB, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) DECLARE_GL_FUNCTION_STUB_END(void, PrimitiveBoundingBoxARB, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW) DECLARE_GL_FUNCTION_STUB_HEAD(GLuint64, GetTextureHandleARB, GLuint texture) DECLARE_GL_FUNCTION_STUB_END(GLuint64, GetTextureHandleARB, texture) @@ -1214,8 +1214,8 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, Histogram, GLenum target, GLsizei width, GLe DECLARE_GL_FUNCTION_STUB_HEAD(void, Minmax, GLenum target, GLenum internalformat, GLboolean sink) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Minmax, target, internalformat, sink) DECLARE_GL_FUNCTION_STUB_HEAD(void, ResetHistogram, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ResetHistogram, target) DECLARE_GL_FUNCTION_STUB_HEAD(void, ResetMinmax, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ResetMinmax, target) -DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiDrawArraysIndirectCountARB, GLenum mode, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiDrawArraysIndirectCountARB, mode, indirect, drawcount, maxdrawcount, stride) -DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiDrawElementsIndirectCountARB, GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiDrawElementsIndirectCountARB, mode, type, indirect, drawcount, maxdrawcount, stride) +DECLARE_GL_FUNCTION_HEAD(void, MultiDrawArraysIndirectCountARB, GLenum mode, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawArraysIndirectCount, mode, indirect, drawcount, maxdrawcount, stride) +DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsIndirectCountARB, GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElementsIndirectCount, mode, type, indirect, drawcount, maxdrawcount, stride) DECLARE_GL_FUNCTION_STUB_HEAD(void, CurrentPaletteMatrixARB, GLint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CurrentPaletteMatrixARB, index) DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixIndexubvARB, GLint size, const GLubyte* indices) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MatrixIndexubvARB, size, indices) DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixIndexusvARB, GLint size, const GLushort* indices) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MatrixIndexusvARB, size, indices) @@ -1884,15 +1884,15 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedProgramLocalParameterdvEXT, GLuint p DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedProgramLocalParameterfvEXT, GLuint program, GLenum target, GLuint index, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedProgramLocalParameterfvEXT, program, target, index, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedProgramivEXT, GLuint program, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedProgramivEXT, program, target, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedProgramStringEXT, GLuint program, GLenum target, GLenum pname, void* string) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedProgramStringEXT, program, target, pname, string) -DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorageEXT, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorageEXT, renderbuffer, internalformat, width, height) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedRenderbufferParameterivEXT, GLuint renderbuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedRenderbufferParameterivEXT, renderbuffer, pname, params) +DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorageEXT, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedRenderbufferStorage, renderbuffer, internalformat, width, height) +DECLARE_GL_FUNCTION_HEAD(void, GetNamedRenderbufferParameterivEXT, GLuint renderbuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedRenderbufferParameteriv, renderbuffer, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorageMultisampleEXT, GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorageMultisampleEXT, renderbuffer, samples, internalformat, width, height) DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorageMultisampleCoverageEXT, GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorageMultisampleCoverageEXT, renderbuffer, coverageSamples, colorSamples, internalformat, width, height) DECLARE_GL_FUNCTION_STUB_HEAD(GLenum, CheckNamedFramebufferStatusEXT, GLuint framebuffer, GLenum target) DECLARE_GL_FUNCTION_STUB_END(GLenum, CheckNamedFramebufferStatusEXT, framebuffer, target) DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTexture1DEXT, GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferTexture1DEXT, framebuffer, attachment, textarget, texture, level) DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTexture2DEXT, GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferTexture2DEXT, framebuffer, attachment, textarget, texture, level) DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTexture3DEXT, GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferTexture3DEXT, framebuffer, attachment, textarget, texture, level, zoffset) -DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferRenderbufferEXT, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferRenderbufferEXT, framebuffer, attachment, renderbuffertarget, renderbuffer) +DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferRenderbufferEXT, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferRenderbuffer, framebuffer, attachment, renderbuffertarget, renderbuffer) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedFramebufferAttachmentParameterivEXT, GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedFramebufferAttachmentParameterivEXT, framebuffer, attachment, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, GenerateTextureMipmapEXT, GLuint texture, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenerateTextureMipmapEXT, texture, target) DECLARE_GL_FUNCTION_STUB_HEAD(void, GenerateMultiTexMipmapEXT, GLenum texunit, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenerateMultiTexMipmapEXT, texunit, target) @@ -1918,16 +1918,16 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayVertexAttribOffsetEXT, GLuint vao DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayVertexAttribIOffsetEXT, GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayVertexAttribIOffsetEXT, vaobj, buffer, index, size, type, stride, offset) DECLARE_GL_FUNCTION_STUB_HEAD(void, EnableVertexArrayEXT, GLuint vaobj, GLenum array) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, EnableVertexArrayEXT, vaobj, array) DECLARE_GL_FUNCTION_STUB_HEAD(void, DisableVertexArrayEXT, GLuint vaobj, GLenum array) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DisableVertexArrayEXT, vaobj, array) -DECLARE_GL_FUNCTION_STUB_HEAD(void, EnableVertexArrayAttribEXT, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, EnableVertexArrayAttribEXT, vaobj, index) -DECLARE_GL_FUNCTION_STUB_HEAD(void, DisableVertexArrayAttribEXT, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DisableVertexArrayAttribEXT, vaobj, index) +DECLARE_GL_FUNCTION_HEAD(void, EnableVertexArrayAttribEXT, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, EnableVertexArrayAttrib, vaobj, index) +DECLARE_GL_FUNCTION_HEAD(void, DisableVertexArrayAttribEXT, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DisableVertexArrayAttrib, vaobj, index) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayIntegervEXT, GLuint vaobj, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayIntegervEXT, vaobj, pname, param) 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_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_HEAD(void, ClearNamedBufferDataEXT, GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedBufferData, buffer, internalformat, format, type, data) +DECLARE_GL_FUNCTION_HEAD(void, ClearNamedBufferSubDataEXT, GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedBufferSubData, 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) DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedFramebufferParameterivEXT, GLuint framebuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedFramebufferParameterivEXT, framebuffer, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform1dEXT, GLuint program, GLint location, GLdouble x) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform1dEXT, program, location, x) @@ -2045,7 +2045,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetPixelTransformParameterfvEXT, GLenum targ DECLARE_GL_FUNCTION_STUB_HEAD(void, PointParameterfEXT, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PointParameterfEXT, pname, param) DECLARE_GL_FUNCTION_STUB_HEAD(void, PointParameterfvEXT, GLenum pname, const GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PointParameterfvEXT, pname, params) DECLARE_GL_FUNCTION_STUB_HEAD(void, PolygonOffsetClampEXT, GLfloat factor, GLfloat units, GLfloat clamp) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PolygonOffsetClampEXT, factor, units, clamp) -DECLARE_GL_FUNCTION_STUB_HEAD(void, ProvokingVertexEXT, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProvokingVertexEXT, mode) +DECLARE_GL_FUNCTION_HEAD(void, ProvokingVertexEXT, GLenum mode) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProvokingVertex, mode) DECLARE_GL_FUNCTION_STUB_HEAD(void, RasterSamplesEXT, GLuint samples, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, RasterSamplesEXT, samples, fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_HEAD(void, SecondaryColor3bEXT, GLbyte red, GLbyte green, GLbyte blue) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, SecondaryColor3bEXT, red, green, blue) DECLARE_GL_FUNCTION_STUB_HEAD(void, SecondaryColor3bvEXT, const GLbyte* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, SecondaryColor3bvEXT, v) diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp index 73941e5a..33e3d1cf 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp @@ -25,6 +25,39 @@ namespace MobileGL::MG_Impl::GLImpl { mask, filter); } + void BlitNamedFramebuffer_Backend(const SharedPtr& readFramebuffer, + const SharedPtr& drawFramebuffer, + GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, + GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { + auto blitNamedFramebuffer = MG_Backend::gBackendFunctionsTable.GL.BlitNamedFramebuffer; + if (!blitNamedFramebuffer) { + MGLOG_E("glBlitNamedFramebuffer skipped: backend does not implement explicit framebuffer blit."); + return; + } + blitNamedFramebuffer(readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, + dstY1, mask, filter); + } + + void ClearNamedFramebufferfv_Backend(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, const GLfloat* value) { + auto clearNamedFramebufferfv = MG_Backend::gBackendFunctionsTable.GL.ClearNamedFramebufferfv; + if (!clearNamedFramebufferfv) { + MGLOG_E("glClearNamedFramebufferfv skipped: backend does not implement explicit framebuffer clear."); + return; + } + clearNamedFramebufferfv(framebuffer, buffer, drawbuffer, value); + } + + void ClearNamedFramebufferfi_Backend(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { + auto clearNamedFramebufferfi = MG_Backend::gBackendFunctionsTable.GL.ClearNamedFramebufferfi; + if (!clearNamedFramebufferfi) { + MGLOG_E("glClearNamedFramebufferfi skipped: backend does not implement explicit framebuffer clear."); + return; + } + clearNamedFramebufferfi(framebuffer, buffer, drawbuffer, depth, stencil); + } + void SampleMaski_State(GLuint maskNumber, GLbitfield mask) { // TODO: implement } @@ -34,23 +67,19 @@ namespace MobileGL::MG_Impl::GLImpl { // TODO: implement } - void RenderbufferStorage_State(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { - RenderbufferTarget rbTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target); - if (!FramebufferImpl::ValidateRenderbufferTarget(rbTarget)) return; - auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(rbTarget); - auto& renderbufferObject = bindingSlot.GetBoundObject(); + void AllocateRenderbufferStorage_State(const SharedPtr& renderbufferObject, + GLenum internalformat, GLsizei width, GLsizei height, const char* caller) { if (!renderbufferObject) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", "RenderbufferStorage_State", - "Renderbuffer target is bound to no renderbuffer object.")); + MakeUnique("MG_Impl/GLImpl", caller, "No renderbuffer object is available.")); return; } TextureInternalFormat format = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); if (!TextureImpl::ValidateTextureInternalFormat(format)) return; if (width < 0 || height < 0) { MG_State::pGLContext->RecordError( - ErrorCode::InvalidValue, MakeUnique("MG_Impl/GLImpl", "RenderbufferStorage_State", + ErrorCode::InvalidValue, MakeUnique("MG_Impl/GLImpl", caller, "Width and height must be non-negative.")); return; } @@ -58,6 +87,14 @@ namespace MobileGL::MG_Impl::GLImpl { renderbufferObject->SetInternalFormat(format); } + void RenderbufferStorage_State(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { + RenderbufferTarget rbTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target); + if (!FramebufferImpl::ValidateRenderbufferTarget(rbTarget)) return; + auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(rbTarget); + auto& renderbufferObject = bindingSlot.GetBoundObject(); + AllocateRenderbufferStorage_State(renderbufferObject, internalformat, width, height, "RenderbufferStorage_State"); + } + GLboolean IsRenderbuffer_State(GLuint renderbuffer) { return MG_State::pGLContext->ValidateRenderbufferName(renderbuffer); } @@ -156,11 +193,55 @@ namespace MobileGL::MG_Impl::GLImpl { MakeUnique("MG_Impl/GLImpl", "GenRenderbuffers_State", "n must be non-negative")); return; } - static thread_local Vector renderbufferNames; + Vector renderbufferNames; MG_State::pGLContext->GenRenderbufferNames(n, renderbufferNames); Memcpy(renderbuffers, renderbufferNames.data(), sizeof(GLuint) * static_cast(n)); } + void CreateRenderbuffers_State(GLsizei n, GLuint* renderbuffers) { + if (n < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "CreateRenderbuffers_State", "n must be non-negative")); + return; + } + if (n > 0 && !renderbuffers) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "CreateRenderbuffers_State", + "Renderbuffer output pointer cannot be null.")); + return; + } + + Vector renderbufferNames; + MG_State::pGLContext->GenRenderbufferNames(static_cast(n), renderbufferNames); + for (GLsizei i = 0; i < n; ++i) { + renderbuffers[i] = renderbufferNames[i]; + MG_State::pGLContext->CreateRenderbufferObject(renderbufferNames[i]); + } + } + + SharedPtr GetNamedRenderbufferObject_State(GLuint renderbuffer, + const char* caller) { + if (!FramebufferImpl::ValidateRenderbufferName(renderbuffer, false)) return nullptr; + + auto& renderbufferObject = MG_State::pGLContext->GetRenderbufferObject(renderbuffer); + if (!renderbufferObject) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", caller, + std::format("Renderbuffer object {} does not exist.", renderbuffer))); + return nullptr; + } + return renderbufferObject; + } + + void NamedRenderbufferStorage_State(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) { + auto renderbufferObject = GetNamedRenderbufferObject_State(renderbuffer, "NamedRenderbufferStorage_State"); + AllocateRenderbufferStorage_State(renderbufferObject, internalformat, width, height, + "NamedRenderbufferStorage_State"); + } + void GenFramebuffers_State(GLsizei n, GLuint* framebuffers) { if (n < 0) { MG_State::pGLContext->RecordError( @@ -168,11 +249,49 @@ namespace MobileGL::MG_Impl::GLImpl { MakeUnique("MG_Impl/GLImpl", "GenFramebuffers_State", "n must be non-negative")); return; } - static thread_local Vector framebuffersNames; + Vector framebuffersNames; MG_State::pGLContext->GenFramebufferNames(n, framebuffersNames); Memcpy(framebuffers, framebuffersNames.data(), sizeof(GLuint) * static_cast(n)); } + void CreateFramebuffers_State(GLsizei n, GLuint* framebuffers) { + if (n < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "CreateFramebuffers_State", "n must be non-negative")); + return; + } + if (n > 0 && !framebuffers) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "CreateFramebuffers_State", + "Framebuffer output pointer cannot be null.")); + return; + } + + Vector framebufferNames; + MG_State::pGLContext->GenFramebufferNames(static_cast(n), framebufferNames); + for (GLsizei i = 0; i < n; ++i) { + framebuffers[i] = framebufferNames[i]; + MG_State::pGLContext->CreateFramebufferObject(framebufferNames[i]); + } + } + + SharedPtr GetNamedFramebufferObject_State(GLuint framebuffer, + const char* caller) { + if (!FramebufferImpl::ValidateFramebufferName(framebuffer, false)) return nullptr; + + auto& framebufferObject = MG_State::pGLContext->GetFramebufferObject(framebuffer); + if (!framebufferObject) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", caller, + std::format("Framebuffer object {} does not exist.", framebuffer))); + return nullptr; + } + return framebufferObject; + } + void FramebufferTextureLayer_State(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) { // TODO: implement } @@ -232,7 +351,38 @@ namespace MobileGL::MG_Impl::GLImpl { } void FramebufferTexture_State(GLenum target, GLenum attachment, GLuint texture, GLint level) { - // TODO: implement + FramebufferTexture2D_State(target, attachment, GL_TEXTURE_2D, texture, level); + } + + void NamedFramebufferTexture_State(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level) { + if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { + NamedFramebufferTexture_State(framebuffer, GL_DEPTH_ATTACHMENT, texture, level); + NamedFramebufferTexture_State(framebuffer, GL_STENCIL_ATTACHMENT, texture, level); + return; + } + + auto framebufferObject = GetNamedFramebufferObject_State(framebuffer, "NamedFramebufferTexture_State"); + if (!framebufferObject) return; + + FramebufferAttachmentType attachmentType = MG_Util::ConvertGLEnumToFramebufferAttachmentType(attachment); + if (!FramebufferImpl::ValidateFramebufferAttachmentType(attachmentType)) return; + if (!TextureImpl::ValidateTextureName(texture, true)) return; + + if (texture == 0) { + framebufferObject->Detach(attachmentType); + return; + } + + auto& textureObject = MG_State::pGLContext->GetTextureObject(texture); + if (!textureObject) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", "NamedFramebufferTexture_State", + std::format("Texture object {} is not valid.", texture))); + return; + } + + framebufferObject->AttachTexture(attachmentType, textureObject, level); } void FramebufferRenderbuffer_State(GLenum target, GLenum attachment, GLenum renderbuffertarget, @@ -274,7 +424,37 @@ namespace MobileGL::MG_Impl::GLImpl { framebufferObject->AttachRenderbuffer(attachmentType, renderbufferObject); } - void DrawBuffers_State(GLsizei n, const GLenum* bufs) { + void NamedFramebufferRenderbuffer_State(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, + GLuint renderbuffer) { + if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { + NamedFramebufferRenderbuffer_State(framebuffer, GL_DEPTH_ATTACHMENT, renderbuffertarget, renderbuffer); + NamedFramebufferRenderbuffer_State(framebuffer, GL_STENCIL_ATTACHMENT, renderbuffertarget, renderbuffer); + return; + } + + auto framebufferObject = GetNamedFramebufferObject_State(framebuffer, "NamedFramebufferRenderbuffer_State"); + if (!framebufferObject) return; + + FramebufferAttachmentType attachmentType = MG_Util::ConvertGLEnumToFramebufferAttachmentType(attachment); + RenderbufferTarget rbTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(renderbuffertarget); + if (!FramebufferImpl::ValidateFramebufferAttachmentType(attachmentType)) return; + if (!FramebufferImpl::ValidateRenderbufferTarget(rbTarget)) return; + if (!FramebufferImpl::ValidateRenderbufferName(renderbuffer)) return; + + if (renderbuffer == 0) { + framebufferObject->Detach(attachmentType); + return; + } + + auto renderbufferObject = + GetNamedRenderbufferObject_State(renderbuffer, "NamedFramebufferRenderbuffer_State"); + if (!renderbufferObject) return; + + framebufferObject->AttachRenderbuffer(attachmentType, renderbufferObject); + } + + void DrawBuffersForFramebuffer_State(const SharedPtr& fbo, Bool isDefaultFBO, + GLsizei n, const GLenum* bufs) { if (n < 0) { MG_State::pGLContext->RecordError( ErrorCode::InvalidValue, @@ -286,11 +466,12 @@ namespace MobileGL::MG_Impl::GLImpl { MakeUnique("MG_Impl/GLImpl", __func__, "`n` is greater than `GL_MAX_DRAW_BUFFERS`.")); return; } - - // Get bound framebuffer - auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw); - auto& fbo = bindingSlot.GetBoundObject(); - bool isDefaultFBO = (fbo == FramebufferImpl::pDefaultFramebufferInfo->defaultFBO); + if (!fbo) { + MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "Framebuffer object is null.")); + return; + } static int existenceMap[(SizeT)FramebufferAttachmentType::FramebufferAttachmentTypeCount] = {-1}; std::fill(existenceMap, existenceMap + (SizeT)FramebufferAttachmentType::FramebufferAttachmentTypeCount, -1); @@ -363,6 +544,13 @@ namespace MobileGL::MG_Impl::GLImpl { } } + void DrawBuffers_State(GLsizei n, const GLenum* bufs) { + auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw); + auto& fbo = bindingSlot.GetBoundObject(); + const bool isDefaultFBO = (fbo == FramebufferImpl::pDefaultFramebufferInfo->defaultFBO); + DrawBuffersForFramebuffer_State(fbo, isDefaultFBO, n, bufs); + } + void DrawBuffer_State(GLenum buf) { if (buf == GL_NONE) { DrawBuffers_State(0, nullptr); @@ -391,6 +579,114 @@ namespace MobileGL::MG_Impl::GLImpl { fbo->SetReadBuffer(attType); } + void NamedFramebufferDrawBuffers_State(GLuint framebuffer, GLsizei n, const GLenum* bufs) { + auto framebufferObject = GetNamedFramebufferObject_State(framebuffer, "NamedFramebufferDrawBuffers_State"); + if (!framebufferObject) return; + DrawBuffersForFramebuffer_State(framebufferObject, false, n, bufs); + } + + void NamedFramebufferDrawBuffer_State(GLuint framebuffer, GLenum buf) { + if (buf == GL_NONE) { + NamedFramebufferDrawBuffers_State(framebuffer, 0, nullptr); + } else { + GLenum bufs[] = {buf}; + NamedFramebufferDrawBuffers_State(framebuffer, 1, bufs); + } + } + + void NamedFramebufferReadBuffer_State(GLuint framebuffer, GLenum src) { + auto framebufferObject = GetNamedFramebufferObject_State(framebuffer, "NamedFramebufferReadBuffer_State"); + if (!framebufferObject) return; + + auto attType = MG_Util::ConvertGLEnumToFramebufferAttachmentType(src); + if (attType == FramebufferAttachmentType::Unknown) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique( + "MG_Impl/GLImpl", "NamedFramebufferReadBuffer_State", + std::format("`src` = {} is not an accepted value.", MG_Util::ConvertGLEnumToString(src)))); + return; + } + framebufferObject->SetReadBuffer(attType); + } + + SharedPtr GetFramebufferObjectForNamedClear(GLuint framebuffer, + const char* caller) { + return framebuffer == 0 ? FramebufferImpl::pDefaultFramebufferInfo->defaultFBO + : GetNamedFramebufferObject_State(framebuffer, caller); + } + + Bool ValidateNamedClearfv_State(GLenum buffer, GLint drawbuffer, const GLfloat* value, const char* caller) { + if (!value) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", caller, "value pointer cannot be null.")); + return false; + } + + switch (buffer) { + case GL_COLOR: + if (drawbuffer < 0 || + drawbuffer >= static_cast(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", caller, "color drawbuffer index is out of range.")); + return false; + } + return true; + case GL_DEPTH: + if (drawbuffer != 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", caller, "depth clear requires drawbuffer 0.")); + return false; + } + return true; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique( + "MG_Impl/GLImpl", caller, + std::format("buffer {} is not accepted for glClearNamedFramebufferfv.", + MG_Util::ConvertGLEnumToString(buffer)))); + return false; + } + } + + Bool ValidateNamedClearfi_State(GLenum buffer, GLint drawbuffer, const char* caller) { + if (buffer != GL_DEPTH_STENCIL) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique( + "MG_Impl/GLImpl", caller, + std::format("buffer {} is not accepted for glClearNamedFramebufferfi.", + MG_Util::ConvertGLEnumToString(buffer)))); + return false; + } + if (drawbuffer != 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", caller, "depth/stencil clear requires drawbuffer 0.")); + return false; + } + return true; + } + + void ClearNamedFramebufferfv_State(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat* value) { + auto framebufferObject = GetFramebufferObjectForNamedClear(framebuffer, "ClearNamedFramebufferfv_State"); + if (!framebufferObject) return; + if (!ValidateNamedClearfv_State(buffer, drawbuffer, value, "ClearNamedFramebufferfv_State")) return; + ClearNamedFramebufferfv_Backend(framebufferObject, buffer, drawbuffer, value); + } + + void ClearNamedFramebufferfi_State(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, + GLint stencil) { + auto framebufferObject = GetFramebufferObjectForNamedClear(framebuffer, "ClearNamedFramebufferfi_State"); + if (!framebufferObject) return; + if (!ValidateNamedClearfi_State(buffer, drawbuffer, "ClearNamedFramebufferfi_State")) return; + ClearNamedFramebufferfi_Backend(framebufferObject, buffer, drawbuffer, depth, stencil); + } + void DeleteRenderbuffers_State(GLsizei n, const GLuint* renderbuffers) { if (n < 0) { MG_State::pGLContext->RecordError( @@ -456,7 +752,117 @@ namespace MobileGL::MG_Impl::GLImpl { // GL_FRAMEBUFFER_UNSUPPORTED, GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE, // GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS return framebufferObject->CheckCompleteness() ? GL_FRAMEBUFFER_COMPLETE - : GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + : GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + } + + GLenum CheckNamedFramebufferStatus_State(GLuint framebuffer, GLenum target) { + if (target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER && target != GL_READ_FRAMEBUFFER) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", "CheckNamedFramebufferStatus_State", + std::format("target {} is not accepted.", + MG_Util::ConvertGLEnumToString(target)))); + return GL_FRAMEBUFFER_UNDEFINED; + } + + auto framebufferObject = GetNamedFramebufferObject_State(framebuffer, "CheckNamedFramebufferStatus_State"); + if (!framebufferObject) return GL_FRAMEBUFFER_UNDEFINED; + + return framebufferObject->CheckCompleteness() ? GL_FRAMEBUFFER_COMPLETE + : GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + } + + void GetFramebufferAttachmentParameteriv_Object( + const SharedPtr& framebufferObject, GLenum attachment, GLenum pname, + GLint* params, const char* caller) { + if (params == nullptr) return; + + const Bool depthStencilAlias = attachment == GL_DEPTH_STENCIL_ATTACHMENT; + FramebufferAttachmentType attachmentType = depthStencilAlias + ? FramebufferAttachmentType::Depth + : MG_Util::ConvertGLEnumToFramebufferAttachmentType(attachment); + if (!FramebufferImpl::ValidateFramebufferAttachmentType(attachmentType)) return; + + const auto* attachmentObject = [&]() -> const MG_State::GLState::FramebufferAttachmentObject* { + if (!depthStencilAlias) { + return &framebufferObject->GetAttachment(attachmentType); + } + + const auto& depthAttachment = framebufferObject->GetAttachment(FramebufferAttachmentType::Depth); + if (depthAttachment.IsValid() && !depthAttachment.IsEmpty()) return &depthAttachment; + + const auto& stencilAttachment = framebufferObject->GetAttachment(FramebufferAttachmentType::Stencil); + if (stencilAttachment.IsValid() && !stencilAttachment.IsEmpty()) return &stencilAttachment; + + return nullptr; + }(); + + switch (pname) { + case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: + if (attachmentObject == nullptr || attachmentObject->IsEmpty() || !attachmentObject->IsValid()) { + *params = GL_NONE; + } else if (attachmentObject->IsTexture()) { + *params = GL_TEXTURE; + } else if (attachmentObject->IsRenderbuffer()) { + *params = GL_RENDERBUFFER; + } else { + *params = GL_NONE; + } + break; + case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: + if (attachmentObject == nullptr || attachmentObject->IsEmpty() || !attachmentObject->IsValid()) { + *params = 0; + } else if (attachmentObject->IsTexture()) { + const auto& textureObject = attachmentObject->GetTexture(); + *params = textureObject ? static_cast(textureObject->GetExternalIndex()) : 0; + } else if (attachmentObject->IsRenderbuffer()) { + const auto& renderbufferObject = attachmentObject->GetRenderbuffer(); + *params = renderbufferObject ? static_cast(renderbufferObject->GetExternalIndex()) : 0; + } else { + *params = 0; + } + break; + case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: + *params = (attachmentObject != nullptr && attachmentObject->IsTexture() && attachmentObject->IsValid()) + ? static_cast(attachmentObject->GetTextureLevel()) + : 0; + break; + case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: + case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: + *params = 0; + break; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique( + "MG_Impl/GLImpl", caller, + std::format("pname {} is not an accepted value.", MG_Util::ConvertGLEnumToString(pname)))); + return; + } + } + + void GetNamedFramebufferAttachmentParameteriv_State(GLuint framebuffer, GLenum attachment, GLenum pname, + GLint* params) { + auto framebufferObject = + GetNamedFramebufferObject_State(framebuffer, "GetNamedFramebufferAttachmentParameteriv_State"); + if (!framebufferObject) return; + GetFramebufferAttachmentParameteriv_Object(framebufferObject, attachment, pname, params, + "GetNamedFramebufferAttachmentParameteriv_State"); + } + + void BlitNamedFramebuffer_State(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, + GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) { + auto readObject = readFramebuffer == 0 + ? FramebufferImpl::pDefaultFramebufferInfo->defaultFBO + : GetNamedFramebufferObject_State(readFramebuffer, "BlitNamedFramebuffer_State"); + auto drawObject = drawFramebuffer == 0 + ? FramebufferImpl::pDefaultFramebufferInfo->defaultFBO + : GetNamedFramebufferObject_State(drawFramebuffer, "BlitNamedFramebuffer_State"); + if (!readObject || !drawObject) return; + + BlitNamedFramebuffer_Backend(readObject, drawObject, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, + mask, filter); } void BindRenderbuffer_State(GLenum target, GLuint renderbuffer) { @@ -495,18 +901,15 @@ namespace MobileGL::MG_Impl::GLImpl { bindingSlot.Bind(framebufferObject); } - void GetRenderbufferParameteriv_State(GLenum target, GLenum pname, GLint* params) { + void GetRenderbufferParameterivForObject_State(const SharedPtr& + renderbufferObject, + GLenum pname, GLint* params, const char* caller) { if (!params) return; - RenderbufferTarget renderbufferTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target); - if (!FramebufferImpl::ValidateRenderbufferTarget(renderbufferTarget)) return; - auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(renderbufferTarget); - auto& renderbufferObject = bindingSlot.GetBoundObject(); if (!renderbufferObject) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", "GetRenderbufferParameteriv_State", - "Renderbuffer target is bound to no renderbuffer object.")); + MakeUnique("MG_Impl/GLImpl", caller, "No renderbuffer object is available.")); return; } @@ -545,12 +948,27 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum, MakeUnique( - "MG_Impl/GLImpl", "GetRenderbufferParameteriv_State", + "MG_Impl/GLImpl", caller, std::format("pname {} is not an accepted value.", MG_Util::ConvertGLEnumToString(pname)))); return; } } + void GetRenderbufferParameteriv_State(GLenum target, GLenum pname, GLint* params) { + RenderbufferTarget renderbufferTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target); + if (!FramebufferImpl::ValidateRenderbufferTarget(renderbufferTarget)) return; + auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(renderbufferTarget); + auto& renderbufferObject = bindingSlot.GetBoundObject(); + GetRenderbufferParameterivForObject_State(renderbufferObject, pname, params, "GetRenderbufferParameteriv_State"); + } + + void GetNamedRenderbufferParameteriv_State(GLuint renderbuffer, GLenum pname, GLint* params) { + auto renderbufferObject = + GetNamedRenderbufferObject_State(renderbuffer, "GetNamedRenderbufferParameteriv_State"); + GetRenderbufferParameterivForObject_State(renderbufferObject, pname, params, + "GetNamedRenderbufferParameteriv_State"); + } + void ClearBufferfi_Backend(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { MG_Backend::gBackendFunctionsTable.GL.ClearBufferfi(buffer, drawbuffer, depth, stencil); } @@ -656,8 +1074,9 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); if (pixelPackBufferObject) { - // Check if PBO is mapped - if (pixelPackBufferObject->IsMapped()) { + // Persistent mappings remain legal GPU transfer destinations. + if (pixelPackBufferObject->IsMapped() && + !(pixelPackBufferObject->GetMappingAccess() & BufferMappingAccessBit::Persistent)) { MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, MakeUnique("MG_Impl/GLImpl", "ReadPixels_State", "Pixel pack buffer is currently mapped")); @@ -743,10 +1162,26 @@ namespace MobileGL::MG_Impl::GLImpl { GenRenderbuffers_State(n, renderbuffers); } + void CreateRenderbuffers(GLsizei n, GLuint* renderbuffers) { + CreateRenderbuffers_State(n, renderbuffers); + } + + void NamedRenderbufferStorage(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) { + NamedRenderbufferStorage_State(renderbuffer, internalformat, width, height); + } + + void GetNamedRenderbufferParameteriv(GLuint renderbuffer, GLenum pname, GLint* params) { + GetNamedRenderbufferParameteriv_State(renderbuffer, pname, params); + } + void GenFramebuffers(GLsizei n, GLuint* framebuffers) { GenFramebuffers_State(n, framebuffers); } + void CreateFramebuffers(GLsizei n, GLuint* framebuffers) { + CreateFramebuffers_State(n, framebuffers); + } + void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) { FramebufferTextureLayer_State(target, attachment, texture, level, layer); } @@ -768,10 +1203,39 @@ namespace MobileGL::MG_Impl::GLImpl { FramebufferTexture_State(target, attachment, texture, level); } + void NamedFramebufferTexture(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level) { + NamedFramebufferTexture_State(framebuffer, attachment, texture, level); + } + + void NamedFramebufferDrawBuffer(GLuint framebuffer, GLenum buf) { + NamedFramebufferDrawBuffer_State(framebuffer, buf); + } + + void NamedFramebufferDrawBuffers(GLuint framebuffer, GLsizei n, const GLenum* bufs) { + NamedFramebufferDrawBuffers_State(framebuffer, n, bufs); + } + + void NamedFramebufferReadBuffer(GLuint framebuffer, GLenum src) { + NamedFramebufferReadBuffer_State(framebuffer, src); + } + + void ClearNamedFramebufferfv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat* value) { + ClearNamedFramebufferfv_State(framebuffer, buffer, drawbuffer, value); + } + + void ClearNamedFramebufferfi(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { + ClearNamedFramebufferfi_State(framebuffer, buffer, drawbuffer, depth, stencil); + } + void FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) { FramebufferRenderbuffer_State(target, attachment, renderbuffertarget, renderbuffer); } + void NamedFramebufferRenderbuffer(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, + GLuint renderbuffer) { + NamedFramebufferRenderbuffer_State(framebuffer, attachment, renderbuffertarget, renderbuffer); + } + void DrawBuffer(GLenum buf) { DrawBuffer_State(buf); } @@ -796,6 +1260,21 @@ namespace MobileGL::MG_Impl::GLImpl { return CheckFramebufferStatus_State(target); } + GLenum CheckNamedFramebufferStatus(GLuint framebuffer, GLenum target) { + return CheckNamedFramebufferStatus_State(framebuffer, target); + } + + void GetNamedFramebufferAttachmentParameteriv(GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params) { + GetNamedFramebufferAttachmentParameteriv_State(framebuffer, attachment, pname, params); + } + + void BlitNamedFramebuffer(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, + GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, + GLenum filter) { + BlitNamedFramebuffer_State(readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, + dstY1, mask, filter); + } + void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { BlitFramebuffer_Backend(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h index abf1b7a0..6e1e551a 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h @@ -24,19 +24,36 @@ namespace MobileGL::MG_Impl::GLImpl { GLboolean IsRenderbuffer(GLuint renderbuffer); void GetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params); void GenRenderbuffers(GLsizei n, GLuint* renderbuffers); + void CreateRenderbuffers(GLsizei n, GLuint* renderbuffers); + void NamedRenderbufferStorage(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); + void GetNamedRenderbufferParameteriv(GLuint renderbuffer, GLenum pname, GLint* params); void FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + void NamedFramebufferRenderbuffer(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, + GLuint renderbuffer); void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers); void BindRenderbuffer(GLenum target, GLuint renderbuffer); void SampleMaski(GLuint maskNumber, GLbitfield mask); GLboolean IsFramebuffer(GLuint framebuffer); void GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params); void GenFramebuffers(GLsizei n, GLuint* framebuffers); + void CreateFramebuffers(GLsizei n, GLuint* framebuffers); void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); void FramebufferTexture3D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); void FramebufferTexture1D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); void FramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level); + void NamedFramebufferTexture(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); + void NamedFramebufferDrawBuffer(GLuint framebuffer, GLenum buf); + void NamedFramebufferDrawBuffers(GLuint framebuffer, GLsizei n, const GLenum* bufs); + void NamedFramebufferReadBuffer(GLuint framebuffer, GLenum src); + void ClearNamedFramebufferfv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat* value); + void ClearNamedFramebufferfi(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); + GLenum CheckNamedFramebufferStatus(GLuint framebuffer, GLenum target); + void GetNamedFramebufferAttachmentParameteriv(GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params); + void BlitNamedFramebuffer(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, + GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, + GLenum filter); void DrawBuffer(GLenum buf); void DrawBuffers(GLsizei n, const GLenum* bufs); void ReadBuffer(GLenum src); diff --git a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp index ebb96907..e23af76d 100644 --- a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp +++ b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp @@ -156,6 +156,35 @@ namespace MobileGL::MG_Impl::GLImpl { getInteger64i(target, index, data); } + void GetInteger64v(GLenum pname, GLint64* data) { + MGLOG_D("glGetInteger64v, pname: %s", MG_Util::ConvertGLEnumToString(pname).c_str()); + if (!data) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "data pointer cannot be null")); + return; + } + + switch (pname) { + case GL_MAX_SHADER_STORAGE_BLOCK_SIZE: + if (MG_Backend::pActiveBackendObject) { + *data = static_cast( + MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxShaderStorageBlockSize); + } else { + *data = static_cast(MG_Backend::DynamicBackendParameters{}.MaxShaderStorageBlockSize); + } + return; + default: + *data = 0; + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, + std::format("Unsupported integer64 pname {}.", + MG_Util::ConvertGLEnumToString(pname)))); + return; + } + } + void GetIntegerv(GLenum pname, GLint* params) { MGLOG_D("glGetIntegerv, pname: %s", MG_Util::ConvertGLEnumToString(pname).c_str()); if (!params) { @@ -643,6 +672,11 @@ namespace MobileGL::MG_Impl::GLImpl { case GL_PIXEL_UNPACK_BUFFER_BINDING: *params = 0; // TODO break; + case GL_PARAMETER_BUFFER_BINDING_ARB: { + auto& obj = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject(); + *params = obj ? static_cast(obj->GetExternalIndex()) : 0; + break; + } case GL_POINT_FADE_THRESHOLD_SIZE: *params = 0; // TODO break; @@ -659,7 +693,8 @@ namespace MobileGL::MG_Impl::GLImpl { *params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ProgramPointSize) ? GL_TRUE : GL_FALSE; break; case GL_PROVOKING_VERTEX: - *params = 0; // TODO + *params = static_cast( + MG_Util::ConvertProvokingVertexModeToGLEnum(MG_State::pGLContext->GetProvokingVertexMode())); break; case GL_POINT_SIZE: *params = 0; // TODO diff --git a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h index 4e5b8d97..450089a0 100644 --- a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h +++ b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.h @@ -14,6 +14,7 @@ namespace MobileGL::MG_Impl::GLImpl { const GLubyte* GetString(GLenum name); const GLubyte* GetStringi(GLenum name, GLuint index); void GetIntegerv(GLenum pname, GLint* params); + void GetInteger64v(GLenum pname, GLint64* data); void GetIntegeri_v(GLenum target, GLuint index, GLint* data); void GetInteger64i_v(GLenum target, GLuint index, GLint64* data); GLenum GetError(); diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index 6458f8fa..646943b5 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -731,6 +731,10 @@ namespace MobileGL::MG_Impl::GLImpl { Uniformv_State<4>(location, count, value); } + void Uniform1uiv_State(GLint location, GLsizei count, const GLuint* value) { + Uniformv_State<1>(location, count, value); + } + void UniformMatrix2fv_State(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { // For 2x2 matrices, we have 4 elements per matrix // If transpose is GL_TRUE, we need to transpose the matrix data @@ -1263,6 +1267,10 @@ namespace MobileGL::MG_Impl::GLImpl { Uniform4iv(location, 1, v); } + void Uniform1ui(GLint location, GLuint v0) { + Uniform1uiv(location, 1, &v0); + } + void Uniform1fv(GLint location, GLsizei count, const GLfloat* value) { Uniform1fv_State(location, count, value); } @@ -1295,6 +1303,10 @@ namespace MobileGL::MG_Impl::GLImpl { Uniform4iv_State(location, count, value); } + void Uniform1uiv(GLint location, GLsizei count, const GLuint* value) { + Uniform1uiv_State(location, count, value); + } + void UniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { UniformMatrix2fv_State(location, count, transpose, value); } diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h index d58e643f..e4b1358f 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h @@ -45,6 +45,7 @@ namespace MobileGL::MG_Impl::GLImpl { void Uniform2i(GLint location, GLint v0, GLint v1); void Uniform3i(GLint location, GLint v0, GLint v1, GLint v2); void Uniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); + void Uniform1ui(GLint location, GLuint v0); void Uniform1fv(GLint location, GLsizei count, const GLfloat* value); void Uniform2fv(GLint location, GLsizei count, const GLfloat* value); void Uniform3fv(GLint location, GLsizei count, const GLfloat* value); @@ -53,6 +54,7 @@ namespace MobileGL::MG_Impl::GLImpl { void Uniform2iv(GLint location, GLsizei count, const GLint* value); void Uniform3iv(GLint location, GLsizei count, const GLint* value); void Uniform4iv(GLint location, GLsizei count, const GLint* value); + void Uniform1uiv(GLint location, GLsizei count, const GLuint* value); void UniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); void UniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); void UniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); diff --git a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp index a96b8bff..0ef19709 100644 --- a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp +++ b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.cpp @@ -168,6 +168,21 @@ namespace MobileGL::MG_Impl::GLImpl { MG_State::pGLContext->SetFrontFaceMode(frontFaceMode); } + void ProvokingVertex_State(GLenum mode) { + ProvokingVertexMode provokingVertexMode = MG_Util::ConvertGLEnumToProvokingVertexMode(mode); + if (provokingVertexMode == ProvokingVertexMode::Unknown) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", "ProvokingVertex_State", + "Provoking vertex mode enum " + + MG_Util::ConvertProvokingVertexModeToString(provokingVertexMode) + + "(" + MG_Util::ConvertGLEnumToString(mode) + ") is not supported.")); + return; + } + + MG_State::pGLContext->SetProvokingVertexMode(provokingVertexMode); + } + void Enable_State(GLenum cap) { CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(cap); if (capInput == CapabilityInput::Unknown) { @@ -499,6 +514,10 @@ namespace MobileGL::MG_Impl::GLImpl { FrontFace_State(mode); } + void ProvokingVertex(GLenum mode) { + ProvokingVertex_State(mode); + } + void Enable(GLenum cap) { Enable_State(cap); } diff --git a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h index 400909dc..0b32a50c 100644 --- a/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h +++ b/MobileGL/MG_Impl/GLImpl/RenderState/GL_RenderState.h @@ -39,6 +39,7 @@ namespace MobileGL::MG_Impl::GLImpl { GLboolean IsEnabled(GLenum cap); void Hint(GLenum target, GLenum mode); void FrontFace(GLenum mode); + void ProvokingVertex(GLenum mode); void Enable(GLenum cap); void Disable(GLenum cap); void DepthRange(GLclampd near_val, GLclampd far_val); diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 1970a1f2..9fbd2c63 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -31,6 +31,214 @@ namespace MobileGL::MG_Impl::GLImpl { static SharedPtr nullTextureObject; static UnorderedMap g_autoGenerateMipmapByTextureId; + const SharedPtr& GetTextureObjectByName(GLuint texture, const char* caller) { + if (texture == 0 || !TextureImpl::ValidateTextureName(texture, true)) return nullTextureObject; + + auto& textureObject = MG_State::pGLContext->GetTextureObject(texture); + if (!TextureImpl::ValidateTextureObject(textureObject)) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", caller, + std::format("Texture object {} does not exist.", texture))); + return nullTextureObject; + } + return textureObject; + } + + TextureUploadTarget GetPrimaryUploadTarget(const SharedPtr& textureObject) { + if (!textureObject) return TextureUploadTarget::Unknown; + const auto& uploadTargets = textureObject->GetUploadTargets(); + return uploadTargets.empty() ? TextureUploadTarget::Unknown : uploadTargets[0]; + } + + void TextureParameterObject_State(const SharedPtr& textureObject, GLenum pname, + GLint param, const char* caller) { + if (!textureObject) return; + + switch (pname) { + case GL_TEXTURE_MAG_FILTER: + textureObject->GetSamplerObject()->SetMagFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(param)); + break; + case GL_TEXTURE_MIN_FILTER: + textureObject->GetSamplerObject()->SetMinFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(param)); + textureObject->GetSamplerObject()->SetMipmapMode(MG_Util::ConvertGLEnumToSamplerMipmapMode(param)); + break; + case GL_TEXTURE_MIN_LOD: { + Float maxLod = textureObject->GetSamplerObject()->GetMaxLod(); + textureObject->GetSamplerObject()->SetLodRange(param, maxLod); + break; + } + case GL_TEXTURE_MAX_LOD: { + Float minLod = textureObject->GetSamplerObject()->GetMinLod(); + textureObject->GetSamplerObject()->SetLodRange(minLod, param); + break; + } + case GL_TEXTURE_BASE_LEVEL: + textureObject->SetBaseLevel(param); + break; + case GL_TEXTURE_MAX_LEVEL: + textureObject->SetMaxLevel(param); + break; + case GL_TEXTURE_SWIZZLE_R: + case GL_TEXTURE_SWIZZLE_G: + case GL_TEXTURE_SWIZZLE_B: + case GL_TEXTURE_SWIZZLE_A: { + auto swizzleParam = MG_Util::ConvertGLEnumPnameToTextureSwizzleParam(pname); + auto swizzleValue = MG_Util::ConvertGLEnumToTextureSwizzleParam(param); + textureObject->SetSwizzleParam(swizzleParam, swizzleValue); + break; + } + case GL_TEXTURE_WRAP_S: + textureObject->GetSamplerObject()->SetWrapS(MG_Util::ConvertGLEnumToSamplerWrapMode(param)); + break; + case GL_TEXTURE_WRAP_T: + textureObject->GetSamplerObject()->SetWrapT(MG_Util::ConvertGLEnumToSamplerWrapMode(param)); + break; + case GL_TEXTURE_WRAP_R: + textureObject->GetSamplerObject()->SetWrapR(MG_Util::ConvertGLEnumToSamplerWrapMode(param)); + break; + case GL_TEXTURE_COMPARE_MODE: + textureObject->GetSamplerObject()->SetCompareMode(MG_Util::ConvertGLEnumToSamplerCompareMode(param)); + break; + case GL_TEXTURE_COMPARE_FUNC: + textureObject->GetSamplerObject()->SetSamplerCompareFunc(MG_Util::ConvertGLEnumToSamplerCompareFunc(param)); + break; + case GL_TEXTURE_LOD_BIAS: + textureObject->GetSamplerObject()->SetLodBias((GLfloat)param); + break; + case GL_GENERATE_MIPMAP: + g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != GL_FALSE); + break; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique( + "MG_Impl/GLImpl", caller, + std::format("pname {} is not a valid texture parameter.", MG_Util::ConvertGLEnumToString(pname)))); + return; + } + } + + void TextureParameterObjectf_State(const SharedPtr& textureObject, GLenum pname, + GLfloat param, const char* caller) { + if (!textureObject) return; + + switch (pname) { + case GL_TEXTURE_MAG_FILTER: + textureObject->GetSamplerObject()->SetMagFilter(MG_Util::ConvertGLEnumToSamplerFilterMode((GLenum)param)); + break; + case GL_TEXTURE_MIN_FILTER: + textureObject->GetSamplerObject()->SetMinFilter(MG_Util::ConvertGLEnumToSamplerFilterMode((GLenum)param)); + textureObject->GetSamplerObject()->SetMipmapMode(MG_Util::ConvertGLEnumToSamplerMipmapMode((GLenum)param)); + break; + case GL_TEXTURE_MIN_LOD: { + Float maxLod = textureObject->GetSamplerObject()->GetMaxLod(); + textureObject->GetSamplerObject()->SetLodRange(param, maxLod); + break; + } + case GL_TEXTURE_MAX_LOD: { + Float minLod = textureObject->GetSamplerObject()->GetMinLod(); + textureObject->GetSamplerObject()->SetLodRange(minLod, param); + break; + } + case GL_TEXTURE_BASE_LEVEL: + textureObject->SetBaseLevel((Uint)param); + break; + case GL_TEXTURE_MAX_LEVEL: + textureObject->SetMaxLevel((Uint)param); + break; + case GL_TEXTURE_WRAP_S: + textureObject->GetSamplerObject()->SetWrapS(MG_Util::ConvertGLEnumToSamplerWrapMode((GLenum)param)); + break; + case GL_TEXTURE_WRAP_T: + textureObject->GetSamplerObject()->SetWrapT(MG_Util::ConvertGLEnumToSamplerWrapMode((GLenum)param)); + break; + case GL_TEXTURE_WRAP_R: + textureObject->GetSamplerObject()->SetWrapR(MG_Util::ConvertGLEnumToSamplerWrapMode((GLenum)param)); + break; + case GL_TEXTURE_COMPARE_MODE: + textureObject->GetSamplerObject()->SetCompareMode( + MG_Util::ConvertGLEnumToSamplerCompareMode((GLenum)param)); + break; + case GL_TEXTURE_COMPARE_FUNC: + textureObject->GetSamplerObject()->SetSamplerCompareFunc( + MG_Util::ConvertGLEnumToSamplerCompareFunc((GLenum)param)); + break; + case GL_TEXTURE_LOD_BIAS: + textureObject->GetSamplerObject()->SetLodBias(param); + break; + case GL_GENERATE_MIPMAP: + g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != 0.0f); + break; + case GL_DEPTH_STENCIL_TEXTURE_MODE: + if (param != GL_DEPTH_COMPONENT && param != GL_STENCIL_INDEX) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", caller, + "Invalid GL_DEPTH_STENCIL_TEXTURE_MODE value.")); + } + break; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique( + "MG_Impl/GLImpl", caller, + std::format("pname {} is not a valid texture parameter.", MG_Util::ConvertGLEnumToString(pname)))); + return; + } + } + + void GetTextureParameterObjectiv_State(const SharedPtr& textureObject, + GLenum pname, GLint* params, const char* caller) { + if (!textureObject || !params) return; + + switch (pname) { + case GL_TEXTURE_MAG_FILTER: + *params = (GLint)MG_Util::ConvertSamplerFilterModeToGLEnum(textureObject->GetSamplerObject()->GetMagFilter(), + SamplerMipmapMode::None); + break; + case GL_TEXTURE_MIN_FILTER: + *params = (GLint)MG_Util::ConvertSamplerFilterModeToGLEnum( + textureObject->GetSamplerObject()->GetMinFilter(), textureObject->GetSamplerObject()->GetMipmapMode()); + break; + case GL_TEXTURE_MIN_LOD: + *params = static_cast(textureObject->GetSamplerObject()->GetMinLod()); + break; + case GL_TEXTURE_MAX_LOD: + *params = static_cast(textureObject->GetSamplerObject()->GetMaxLod()); + break; + case GL_TEXTURE_BASE_LEVEL: + *params = static_cast(textureObject->GetLevelRange().x()); + break; + case GL_TEXTURE_MAX_LEVEL: + *params = static_cast(textureObject->GetLevelRange().y()); + break; + case GL_TEXTURE_WRAP_S: + *params = (GLint)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapS()); + break; + case GL_TEXTURE_WRAP_T: + *params = (GLint)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapT()); + break; + case GL_TEXTURE_WRAP_R: + *params = (GLint)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapR()); + break; + case GL_TEXTURE_COMPARE_MODE: + *params = + (GLint)MG_Util::ConvertSamplerCompareModeToGLEnum(textureObject->GetSamplerObject()->GetCompareMode()); + break; + case GL_TEXTURE_COMPARE_FUNC: + *params = (GLint)MG_Util::ConvertSamplerCompareFuncToGLEnum( + textureObject->GetSamplerObject()->GetSamplerCompareFunc()); + break; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", caller, + "pname is not a valid texture parameter.")); + return; + } + } + const SharedPtr& GetTextureObjectByTarget( TextureUploadTarget textureUploadTarget, TextureTarget textureTarget) { if (TextureImpl::IsProxyTextureTarget(textureUploadTarget)) { @@ -280,72 +488,7 @@ namespace MobileGL::MG_Impl::GLImpl { auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget); if (!textureObject) return; - switch (pname) { - case GL_TEXTURE_MAG_FILTER: - textureObject->GetSamplerObject()->SetMagFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(param)); - break; - case GL_TEXTURE_MIN_FILTER: - textureObject->GetSamplerObject()->SetMinFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(param)); - textureObject->GetSamplerObject()->SetMipmapMode(MG_Util::ConvertGLEnumToSamplerMipmapMode(param)); - break; - case GL_TEXTURE_MIN_LOD: { - Float maxLod = textureObject->GetSamplerObject()->GetMaxLod(); - textureObject->GetSamplerObject()->SetLodRange(param, maxLod); - break; - } - case GL_TEXTURE_MAX_LOD: { - Float minLod = textureObject->GetSamplerObject()->GetMinLod(); - textureObject->GetSamplerObject()->SetLodRange(minLod, param); - break; - } - case GL_TEXTURE_BASE_LEVEL: - textureObject->SetBaseLevel(param); - break; - case GL_TEXTURE_MAX_LEVEL: - textureObject->SetMaxLevel(param); - break; - case GL_TEXTURE_SWIZZLE_R: - case GL_TEXTURE_SWIZZLE_G: - case GL_TEXTURE_SWIZZLE_B: - case GL_TEXTURE_SWIZZLE_A: { - auto swizzleParam = MG_Util::ConvertGLEnumPnameToTextureSwizzleParam(pname); - auto swizzleValue = MG_Util::ConvertGLEnumToTextureSwizzleParam(param); - textureObject->SetSwizzleParam(swizzleParam, swizzleValue); - break; - } - case GL_TEXTURE_WRAP_S: - textureObject->GetSamplerObject()->SetWrapS(MG_Util::ConvertGLEnumToSamplerWrapMode(param)); - break; - case GL_TEXTURE_WRAP_T: - textureObject->GetSamplerObject()->SetWrapT(MG_Util::ConvertGLEnumToSamplerWrapMode(param)); - break; - case GL_TEXTURE_WRAP_R: - textureObject->GetSamplerObject()->SetWrapR(MG_Util::ConvertGLEnumToSamplerWrapMode(param)); - break; - case GL_TEXTURE_COMPARE_MODE: - textureObject->GetSamplerObject()->SetCompareMode(MG_Util::ConvertGLEnumToSamplerCompareMode(param)); - break; - case GL_TEXTURE_COMPARE_FUNC: - textureObject->GetSamplerObject()->SetSamplerCompareFunc(MG_Util::ConvertGLEnumToSamplerCompareFunc(param)); - break; - case GL_TEXTURE_LOD_BIAS: - textureObject->GetSamplerObject()->SetLodBias((GLfloat)param); - break; - case GL_GENERATE_MIPMAP: - g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != GL_FALSE); - break; - case GL_TEXTURE_SWIZZLE_RGBA: - // Not supported in this function - case GL_TEXTURE_BORDER_COLOR: - // Not supported in this function - default: - MG_State::pGLContext->RecordError( - ErrorCode::InvalidEnum, - MakeUnique( - "MG_Impl/GLImpl", __func__, - std::format("pname {} is not a valid texture parameter.", MG_Util::ConvertGLEnumToString(pname)))); - return; - } + TextureParameterObject_State(textureObject, pname, param, __func__); } // Quick and dirty TexParameter*v implementation to make NeoForge happy. @@ -1173,7 +1316,7 @@ namespace MobileGL::MG_Impl::GLImpl { } // ======================= Processing ================================ - static thread_local Vector textureNames; + Vector textureNames; MG_State::pGLContext->GenTextureNames(n, textureNames); Memcpy(textures, textureNames.data(), n * sizeof(GLuint)); } @@ -1486,20 +1629,364 @@ namespace MobileGL::MG_Impl::GLImpl { } } - // Check for multisampling - if (textureObject->GetStorageType() == TextureStorageType::Mipmap) { - auto mipmapObject = static_cast(textureObject.get()); - if (mipmapObject->GetMipmapLevelCount() > 1) { + if (textureObject->GetStorageType() != TextureStorageType::Mipmap) return; + } + + void CopyTextureImageToClientOrPBO_State(const SharedPtr& textureObject, + TextureUploadTarget textureUploadTarget, GLint level, GLenum format, + GLenum type, GLsizei bufSize, void* pixels, const char* caller) { + if (!textureObject) return; + + TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format); + TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type); + if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return; + if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return; + if (!TextureImpl::ValidateTextureLevelNumber(level)) return; + if (textureObject->GetStorageType() != TextureStorageType::Mipmap) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", caller, "Texture storage is not mipmap-backed.")); + return; + } + + auto* textureMipmapObject = static_cast(textureObject.get()); + if (static_cast(level) >= textureMipmapObject->GetMipmapLevelCount()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", caller, "Texture level is out of range.")); + return; + } + + const auto texelSize = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level); + const void* src = textureMipmapObject->MapMipmapData(textureUploadTarget, level); + if (!src) return; + + SizeT packedSize = 0; + void* packedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataPack( + src, MG_State::pGLContext->GetPixelStoreParameters(false), textureObject->GetFormat(), texturePixelDataType, + textureInputFormat, texturePixelDataType, texelSize, false, packedSize); + if (!packedPixels || packedSize == 0) { + if (packedPixels) free(packedPixels); + return; + } + + if (bufSize >= 0 && static_cast(bufSize) < packedSize) { + free(packedPixels); + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", caller, "Destination buffer is too small.")); + return; + } + + const auto& pixelPackBufferObject = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); + if (pixelPackBufferObject) { + const SizeT offset = reinterpret_cast(pixels); + if (offset + packedSize > pixelPackBufferObject->GetSize()) { + free(packedPixels); MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, - MakeUnique("MG_Impl/GLImpl", "GetTexImage_State", - "Multisampled textures not supported for GetTexImage")); + MakeUnique("MG_Impl/GLImpl", caller, "Pixel pack buffer is too small.")); return; } + pixelPackBufferObject->UploadSubData({packedPixels, packedSize}, offset); + } else if (pixels) { + Memcpy(pixels, packedPixels, packedSize); } + + free(packedPixels); } /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ + void CreateTextures(GLenum target, GLsizei n, GLuint* textures) { + if (n < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "n must be non-negative.")); + return; + } + if (n > 0 && !textures) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "Texture output pointer cannot be null.")); + return; + } + + TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + if (!TextureImpl::ValidateTextureTarget(textureTarget)) return; + + Vector textureNames; + MG_State::pGLContext->GenTextureNames(n, textureNames); + for (GLsizei i = 0; i < n; ++i) { + textures[i] = textureNames[i]; + MG_State::pGLContext->CreateTextureObject(textureNames[i], textureTarget); + } + } + + void TextureStorage2D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { + auto textureObject = GetTextureObjectByName(texture, __func__); + if (!textureObject) return; + if (levels < 1) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "levels must be positive.")); + return; + } + if (!TextureImpl::ValidateTextureSizeRange(width, height, 1)) return; + + TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat); + textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, TextureInputFormat::RGBA, + TexturePixelDataType::UnsignedByte); + if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return; + if (textureObject->GetStorageType() != TextureStorageType::Mipmap) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed.")); + return; + } + + auto textureUploadTarget = GetPrimaryUploadTarget(textureObject); + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + auto* textureMipmapObject = static_cast(textureObject.get()); + + GLenum realInternalFormat = internalformat; + GLenum realFormat = GL_RGBA; + GLenum realType = GL_UNSIGNED_BYTE; + MG_Util::TextureFormatProcessor::NormalizePixelFormat( + MG_Util::ConvertTextureInternalFormatToGLEnum(textureInternalFormat), PixelFormatNormalizeOptionBit::None, + &realInternalFormat, &realFormat, &realType); + const SizeT bytesPerPixel = MG_Util::GetInternalBytesPerPixel( + textureInternalFormat, MG_Util::ConvertGLEnumToTexturePixelDataType(realType)); + + textureObject->SetInternalFormat(textureInternalFormat); + for (GLsizei level = 0; level < levels; ++level) { + const GLsizei levelWidth = std::max(1, width >> level); + const GLsizei levelHeight = std::max(1, height >> level); + const SizeT byteSize = static_cast(levelWidth) * static_cast(levelHeight) * bytesPerPixel; + textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{levelWidth, levelHeight, 1}, byteSize}); + textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false); + } + } + + void TextureSubImage2D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLenum type, const void* pixels) { + auto textureObject = GetTextureObjectByName(texture, __func__); + if (!textureObject) return; + + TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format); + TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type); + if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return; + if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return; + if (!TextureImpl::ValidateTextureLevelNumber(level)) return; + if (!TextureImpl::ValidateTextureSizeRange(width, height, 1)) return; + if (textureObject->GetStorageType() != TextureStorageType::Mipmap) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed.")); + return; + } + + auto textureUploadTarget = GetPrimaryUploadTarget(textureObject); + if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return; + if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput(textureInputFormat, textureObject->GetFormat(), + texturePixelDataType)) + return; + + auto* textureMipmapObject = static_cast(textureObject.get()); + if (static_cast(level) >= textureMipmapObject->GetMipmapLevelCount()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "Texture level is out of range.")); + return; + } + if (!TextureImpl::ValidateTextureSubImageOffsets(textureObject, xoffset, width, yoffset, height)) return; + + const void* originalPixels = pixels; + const auto& pixelUnpackBufferObject = + MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject(); + if (pixelUnpackBufferObject) { + originalPixels = reinterpret_cast(pixelUnpackBufferObject->GetDataReadOnly()->data()) + + reinterpret_cast(pixels); + } + if (!originalPixels) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "No data supplied from pixels parameter and no PBO bound.")); + return; + } + + SizeT inputSize = 0; + void* processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack( + originalPixels, MG_State::pGLContext->GetPixelStoreParameters(true), textureObject->GetFormat(), + textureInputFormat, texturePixelDataType, {width, height, 1}, false, inputSize); + if (!processedPixels || inputSize == 0) { + if (processedPixels) free(processedPixels); + return; + } + + const auto texelSize = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level); + const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureObject->GetFormat(), texturePixelDataType); + const SizeT srcRowSize = static_cast(width) * internalBpp; + const SizeT destRowSize = static_cast(texelSize.x()) * internalBpp; + + const auto* srcData = static_cast(processedPixels); + Uint8* destData = static_cast(textureMipmapObject->MapMipmapData(textureUploadTarget, level)); + if (destData) { + for (GLsizei y = 0; y < height; ++y) { + const SizeT destRowOffset = static_cast(yoffset + y) * destRowSize + + static_cast(xoffset) * internalBpp; + const SizeT srcRowOffset = static_cast(y) * srcRowSize; + Memcpy(destData + destRowOffset, srcData + srcRowOffset, srcRowSize); + } + textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true); + } + free(processedPixels); + } + + void TextureParameteri(GLuint texture, GLenum pname, GLint param) { + auto textureObject = GetTextureObjectByName(texture, __func__); + TextureParameterObject_State(textureObject, pname, param, __func__); + } + + void TextureParameterf(GLuint texture, GLenum pname, GLfloat param) { + auto textureObject = GetTextureObjectByName(texture, __func__); + TextureParameterObjectf_State(textureObject, pname, param, __func__); + } + + void TextureParameteriv(GLuint texture, GLenum pname, const GLint* params) { + if (!params) return; + if (pname == GL_TEXTURE_SWIZZLE_RGBA) { + auto textureObject = GetTextureObjectByName(texture, __func__); + if (!textureObject) return; + Vec4 swizzleParams; + for (int i = 0; i < 4; i++) { + swizzleParams[i] = MG_Util::ConvertGLEnumToTextureSwizzleParam(static_cast(params[i])); + if (TextureSwizzleParam::Unknown == swizzleParams[i]) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, "`params` is not valid.")); + return; + } + } + textureObject->SetSwizzleParamRGBA(swizzleParams); + return; + } + TextureParameteri(texture, pname, *params); + } + + void BindTextureUnit(GLuint unit, GLuint texture) { + if (unit >= MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "Texture unit is out of range.")); + return; + } + + auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(static_cast(unit)); + if (texture == 0) { + for (auto& slot : textureUnit.GetAllBindingSlots()) { + slot.Bind(nullptr); + } + return; + } + + auto textureObject = GetTextureObjectByName(texture, __func__); + if (!textureObject) return; + textureUnit.GetBindingSlot(textureObject->GetTarget()).Bind(textureObject); + } + + void GetTextureImage(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels) { + auto textureObject = GetTextureObjectByName(texture, __func__); + if (!textureObject) return; + const auto uploadTarget = GetPrimaryUploadTarget(textureObject); + if (MG_Backend::pActiveBackendObject != nullptr && + MG_Backend::pActiveBackendObject->GetBackendType() == BackendType::DirectVulkan && + MG_Backend::gBackendFunctionsTable.GL.GetTextureImage != nullptr) { + MG_Backend::gBackendFunctionsTable.GL.GetTextureImage(textureObject, uploadTarget, level, format, type, + bufSize, pixels); + return; + } + CopyTextureImageToClientOrPBO_State(textureObject, uploadTarget, level, format, type, bufSize, pixels, + __func__); + } + + void GetTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, + GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels) { + auto textureObject = GetTextureObjectByName(texture, __func__); + if (!textureObject) return; + if (level < 0 || xoffset < 0 || yoffset < 0 || zoffset < 0 || width < 0 || height < 0 || depth < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "Texture sub-image range is invalid.")); + return; + } + if (textureObject->GetStorageType() != TextureStorageType::Mipmap) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed.")); + return; + } + + const auto uploadTarget = GetPrimaryUploadTarget(textureObject); + auto* textureMipmapObject = static_cast(textureObject.get()); + if (static_cast(level) >= textureMipmapObject->GetMipmapLevelCount()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "Texture level is out of range.")); + return; + } + + const auto texelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, static_cast(level)); + const Bool isFullLevelRead = xoffset == 0 && yoffset == 0 && zoffset == 0 && + width == texelSize.x() && height == texelSize.y() && + depth == texelSize.z(); + if (!isFullLevelRead) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + "Partial texture sub-image readback is not implemented yet.")); + return; + } + + GetTextureImage(texture, level, format, type, bufSize, pixels); + } + + void GetTextureParameteriv(GLuint texture, GLenum pname, GLint* params) { + auto textureObject = GetTextureObjectByName(texture, __func__); + GetTextureParameterObjectiv_State(textureObject, pname, params, __func__); + } + + void GetTextureLevelParameteriv(GLuint texture, GLint level, GLenum pname, GLint* params) { + auto textureObject = GetTextureObjectByName(texture, __func__); + if (!textureObject || !params) return; + if (!TextureImpl::ValidateTextureLevelNumber(level)) return; + if (textureObject->GetStorageType() != TextureStorageType::Mipmap) return; + + auto textureUploadTarget = GetPrimaryUploadTarget(textureObject); + auto* textureMipmapObject = static_cast(textureObject.get()); + switch (pname) { + case GL_TEXTURE_WIDTH: + *params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).x(); + break; + case GL_TEXTURE_HEIGHT: + *params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).y(); + break; + case GL_TEXTURE_DEPTH: + *params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).z(); + break; + case GL_TEXTURE_INTERNAL_FORMAT: + *params = (GLint)MG_Util::ConvertTextureInternalFormatToGLEnum(textureObject->GetFormat()); + break; + default: + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, + "pname is not a valid texture level parameter.")); + return; + } + } + void BindImageTexture(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) { if (unit >= MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS) { @@ -1558,7 +2045,18 @@ namespace MobileGL::MG_Impl::GLImpl { void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { GetTexImage_State(target, level, format, type, pixels); - GetTexImage_Backend(target, level, format, type, pixels); + if (MG_Backend::pActiveBackendObject != nullptr && + MG_Backend::pActiveBackendObject->GetBackendType() == BackendType::DirectVulkan && + MG_Backend::gBackendFunctionsTable.GL.GetTexImage != nullptr) { + GetTexImage_Backend(target, level, format, type, pixels); + return; + } + TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target); + TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target); + auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + const auto& textureObject = activeUnit.GetBindingSlot(textureTarget).GetBoundObject(); + CopyTextureImageToClientOrPBO_State(textureObject, textureUploadTarget, level, format, type, -1, pixels, + __func__); } void TexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h index 36729e2d..fbfead3b 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.h @@ -15,6 +15,19 @@ namespace MobileGL::MG_Impl::GLImpl { GLenum format); void GenerateMipmap(GLenum target); void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels); + void CreateTextures(GLenum target, GLsizei n, GLuint* textures); + void TextureStorage2D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + void TextureSubImage2D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLenum type, const void* pixels); + void TextureParameterf(GLuint texture, GLenum pname, GLfloat param); + void TextureParameteri(GLuint texture, GLenum pname, GLint param); + void TextureParameteriv(GLuint texture, GLenum pname, const GLint* params); + void BindTextureUnit(GLuint unit, GLuint texture); + void GetTextureImage(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels); + void GetTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, + GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels); + void GetTextureParameteriv(GLuint texture, GLenum pname, GLint* params); + void GetTextureLevelParameteriv(GLuint texture, GLint level, GLenum pname, GLint* params); void TexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels); void TexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, diff --git a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp index 1218237a..e03d9e15 100644 --- a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp +++ b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.cpp @@ -8,11 +8,34 @@ #include "GL_VertexArray.h" #include "Validators.h" +#include #include #include #include namespace MobileGL::MG_Impl::GLImpl { + SharedPtr GetNamedVertexArrayObject_State(GLuint vaobj, + const char* caller) { + if (!VertexArrayImpl::ValidateVertexArrayName(vaobj)) return nullptr; + if (!VertexArrayImpl::ValidateVertexArrayObject(vaobj)) return nullptr; + return MG_State::pGLContext->GetVertexArrayObject(vaobj); + } + + SharedPtr GetVertexArrayBufferObject_State(GLuint buffer, const char* caller) { + if (!BufferImpl::ValidateBufferName(buffer, true)) return nullptr; + if (buffer == 0) return nullptr; + + auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer); + if (!bufferObject) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", caller, + std::format("Buffer object {} does not exist.", buffer))); + return nullptr; + } + return bufferObject; + } + void DisableVertexAttribArray_State(GLuint index) { if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return; @@ -138,11 +161,96 @@ namespace MobileGL::MG_Impl::GLImpl { return; } - static thread_local Vector vaos; + Vector vaos; MG_State::pGLContext->GenVertexArrayNames(n, vaos); Memcpy(arrays, vaos.data(), n * sizeof(GLuint)); } + void CreateVertexArrays_State(GLsizei n, GLuint* arrays) { + if (n < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "CreateVertexArrays_State", "n must be non-negative.")); + return; + } + + Vector vaos; + MG_State::pGLContext->GenVertexArrayNames(n, vaos); + for (GLsizei i = 0; i < n; ++i) { + MG_State::pGLContext->CreateVertexArrayObject(vaos[i]); + arrays[i] = vaos[i]; + } + } + + void DisableVertexArrayAttrib_State(GLuint vaobj, GLuint index) { + auto vao = GetNamedVertexArrayObject_State(vaobj, "DisableVertexArrayAttrib_State"); + if (!vao) return; + if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return; + vao->DisableAttribute(index); + } + + void EnableVertexArrayAttrib_State(GLuint vaobj, GLuint index) { + auto vao = GetNamedVertexArrayObject_State(vaobj, "EnableVertexArrayAttrib_State"); + if (!vao) return; + if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return; + vao->EnableAttribute(index); + } + + void VertexArrayElementBuffer_State(GLuint vaobj, GLuint buffer) { + auto vao = GetNamedVertexArrayObject_State(vaobj, "VertexArrayElementBuffer_State"); + if (!vao) return; + auto bufferObject = GetVertexArrayBufferObject_State(buffer, "VertexArrayElementBuffer_State"); + if (buffer != 0 && !bufferObject) return; + vao->GetIndexBufferBindingSlot().Bind(bufferObject); + } + + void VertexArrayVertexBuffer_State(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, + GLsizei stride) { + auto vao = GetNamedVertexArrayObject_State(vaobj, "VertexArrayVertexBuffer_State"); + if (!vao) return; + if (!VertexArrayImpl::ValidateVertexAttributeIndex(bindingindex)) return; + if (offset < 0 || stride < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", "VertexArrayVertexBuffer_State", + "offset and stride must be non-negative.")); + return; + } + auto bufferObject = GetVertexArrayBufferObject_State(buffer, "VertexArrayVertexBuffer_State"); + if (buffer != 0 && !bufferObject) return; + + const auto& attr = vao->GetAttribute(bindingindex); + vao->SetAttributeFormat(bindingindex, attr.Size, attr.Type, attr.Normalized, stride, static_cast(offset), + attr.IsInteger); + vao->BindAttributeBuffer(bindingindex, bufferObject); + } + + void VertexArrayAttribFormat_State(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, + GLboolean normalized, GLuint relativeoffset) { + auto vao = GetNamedVertexArrayObject_State(vaobj, "VertexArrayAttribFormat_State"); + if (!vao) return; + if (!VertexArrayImpl::ValidateVertexAttributeIndex(attribindex)) return; + + DataType dataType = MG_Util::ConvertGLEnumToDataType(type); + const auto& attr = vao->GetAttribute(attribindex); + if (!VertexArrayImpl::ValidateVertexAttribPointerParams(attribindex, size, dataType, attr.Stride)) return; + + vao->SetAttributeFormat(attribindex, size, dataType, normalized, attr.Stride, relativeoffset, false); + } + + void VertexArrayAttribIFormat_State(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, + GLuint relativeoffset) { + auto vao = GetNamedVertexArrayObject_State(vaobj, "VertexArrayAttribIFormat_State"); + if (!vao) return; + if (!VertexArrayImpl::ValidateVertexAttributeIndex(attribindex)) return; + + DataType dataType = MG_Util::ConvertGLEnumToDataType(type); + const auto& attr = vao->GetAttribute(attribindex); + if (!VertexArrayImpl::ValidateVertexAttribPointerParams(attribindex, size, dataType, attr.Stride)) return; + + vao->SetAttributeFormat(attribindex, size, dataType, false, attr.Stride, relativeoffset, true); + } + GLboolean IsVertexArray_State(GLuint array) { if (array == 0) return GL_FALSE; return MG_State::pGLContext->ValidateVertexArrayObject(array) ? GL_TRUE : GL_FALSE; @@ -163,6 +271,35 @@ namespace MobileGL::MG_Impl::GLImpl { } /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ + void CreateVertexArrays(GLsizei n, GLuint* arrays) { + CreateVertexArrays_State(n, arrays); + } + + void DisableVertexArrayAttrib(GLuint vaobj, GLuint index) { + DisableVertexArrayAttrib_State(vaobj, index); + } + + void EnableVertexArrayAttrib(GLuint vaobj, GLuint index) { + EnableVertexArrayAttrib_State(vaobj, index); + } + + void VertexArrayElementBuffer(GLuint vaobj, GLuint buffer) { + VertexArrayElementBuffer_State(vaobj, buffer); + } + + void VertexArrayVertexBuffer(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) { + VertexArrayVertexBuffer_State(vaobj, bindingindex, buffer, offset, stride); + } + + void VertexArrayAttribFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, + GLuint relativeoffset) { + VertexArrayAttribFormat_State(vaobj, attribindex, size, type, normalized, relativeoffset); + } + + void VertexArrayAttribIFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) { + VertexArrayAttribIFormat_State(vaobj, attribindex, size, type, relativeoffset); + } + void VertexAttribDivisor(GLuint index, GLuint divisor) { VertexAttribDivisor_State(index, divisor); } diff --git a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h index 2dafde79..10a07729 100644 --- a/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h +++ b/MobileGL/MG_Impl/GLImpl/VertexArray/GL_VertexArray.h @@ -11,6 +11,14 @@ namespace MobileGL::MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */ + void CreateVertexArrays(GLsizei n, GLuint* arrays); + void DisableVertexArrayAttrib(GLuint vaobj, GLuint index); + void EnableVertexArrayAttrib(GLuint vaobj, GLuint index); + void VertexArrayElementBuffer(GLuint vaobj, GLuint buffer); + void VertexArrayVertexBuffer(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); + void VertexArrayAttribFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, + GLuint relativeoffset); + void VertexArrayAttribIFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); void VertexAttribDivisor(GLuint index, GLuint divisor); GLboolean IsVertexArray(GLuint array); void DisableVertexAttribArray(GLuint index); diff --git a/MobileGL/MG_Impl/GetProcAddress.cpp b/MobileGL/MG_Impl/GetProcAddress.cpp index 2304c6e4..b3b96e88 100644 --- a/MobileGL/MG_Impl/GetProcAddress.cpp +++ b/MobileGL/MG_Impl/GetProcAddress.cpp @@ -321,22 +321,39 @@ namespace MobileGL::MG_Impl { GETPROC(glClearBufferfi, name); GETPROC(glGetStringi, name); GETPROC(glIsRenderbuffer, name); + GETPROC(glIsRenderbufferEXT, name); GETPROC(glBindRenderbuffer, name); + GETPROC(glBindRenderbufferEXT, name); GETPROC(glDeleteRenderbuffers, name); + GETPROC(glDeleteRenderbuffersEXT, name); GETPROC(glGenRenderbuffers, name); + GETPROC(glGenRenderbuffersEXT, name); GETPROC(glRenderbufferStorage, name); + GETPROC(glRenderbufferStorageEXT, name); GETPROC(glGetRenderbufferParameteriv, name); + GETPROC(glGetRenderbufferParameterivEXT, name); GETPROC(glIsFramebuffer, name); + GETPROC(glIsFramebufferEXT, name); GETPROC(glBindFramebuffer, name); + GETPROC(glBindFramebufferEXT, name); GETPROC(glDeleteFramebuffers, name); + GETPROC(glDeleteFramebuffersEXT, name); GETPROC(glGenFramebuffers, name); + GETPROC(glGenFramebuffersEXT, name); GETPROC(glCheckFramebufferStatus, name); + GETPROC(glCheckFramebufferStatusEXT, name); GETPROC(glFramebufferTexture1D, name); + GETPROC(glFramebufferTexture1DEXT, name); GETPROC(glFramebufferTexture2D, name); + GETPROC(glFramebufferTexture2DEXT, name); GETPROC(glFramebufferTexture3D, name); + GETPROC(glFramebufferTexture3DEXT, name); GETPROC(glFramebufferRenderbuffer, name); + GETPROC(glFramebufferRenderbufferEXT, name); GETPROC(glGetFramebufferAttachmentParameteriv, name); + GETPROC(glGetFramebufferAttachmentParameterivEXT, name); GETPROC(glGenerateMipmap, name); + GETPROC(glGenerateMipmapEXT, name); GETPROC(glBlitFramebuffer, name); GETPROC(glRenderbufferStorageMultisample, name); GETPROC(glFramebufferTextureLayer, name); diff --git a/MobileGL/MG_State/GLState/BufferState/BufferObject.h b/MobileGL/MG_State/GLState/BufferState/BufferObject.h index f13ed348..e1d31f2f 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferObject.h +++ b/MobileGL/MG_State/GLState/BufferState/BufferObject.h @@ -25,6 +25,7 @@ namespace MobileGL { AtomicCounter, DispatchIndirect, DrawIndirect, + Parameter, ShaderStorage, BufferTargetCount, Unknown = -1 diff --git a/MobileGL/MG_State/GLState/BufferState/BufferState.h b/MobileGL/MG_State/GLState/BufferState/BufferState.h index fc2f447f..a515a783 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferState.h +++ b/MobileGL/MG_State/GLState/BufferState/BufferState.h @@ -16,7 +16,7 @@ namespace MobileGL::MG_State::GLState { ToArray(BufferTarget::Vertex, BufferTarget::Uniform, BufferTarget::CopyRead, BufferTarget::CopyWrite, BufferTarget::PixelPack, BufferTarget::PixelUnpack, BufferTarget::Query, BufferTarget::Texture, BufferTarget::TransformFeedback, BufferTarget::AtomicCounter, BufferTarget::DispatchIndirect, - BufferTarget::DrawIndirect, BufferTarget::ShaderStorage); + BufferTarget::DrawIndirect, BufferTarget::Parameter, BufferTarget::ShaderStorage); constexpr const auto BufferBindPointTargets = ToArray(BufferTarget::Uniform, BufferTarget::TransformFeedback, BufferTarget::AtomicCounter, BufferTarget::ShaderStorage); diff --git a/MobileGL/MG_State/GLState/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index a6be59e1..98512322 100644 --- a/MobileGL/MG_State/GLState/Core.cpp +++ b/MobileGL/MG_State/GLState/Core.cpp @@ -413,6 +413,14 @@ namespace MobileGL::MG_State { return m_renderState.GetFrontFaceMode(); } + void GLContext::SetProvokingVertexMode(ProvokingVertexMode mode) { + m_renderState.SetProvokingVertexMode(mode); + } + + ProvokingVertexMode GLContext::GetProvokingVertexMode() const { + return m_renderState.GetProvokingVertexMode(); + } + void GLContext::SetScissorBox(IntVec4 box) { m_renderState.SetScissorBox(box); } diff --git a/MobileGL/MG_State/GLState/Core.h b/MobileGL/MG_State/GLState/Core.h index f074d9ee..1787fbfe 100644 --- a/MobileGL/MG_State/GLState/Core.h +++ b/MobileGL/MG_State/GLState/Core.h @@ -136,6 +136,8 @@ namespace MobileGL { CullFaceMode GetCullFaceMode() const; void SetFrontFaceMode(FrontFaceMode mode); FrontFaceMode GetFrontFaceMode() const; + void SetProvokingVertexMode(ProvokingVertexMode mode); + ProvokingVertexMode GetProvokingVertexMode() const; void SetScissorBox(IntVec4 box); // x, y, width, height const IntVec4& GetScissorBox() const; // x, y, width, height diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp index e75fb9e1..5f28eee6 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp @@ -299,6 +299,27 @@ namespace MobileGL::MG_State::GLState { } } + for (int i = 0; i < m_activeUniformCount; i++) { + auto& uniform = m_program->getUniform(i); + const auto locationIt = m_uniformLocations.find(uniform.name); + if (locationIt == m_uniformLocations.end()) { + continue; + } + + const Uint location = locationIt->second; + if (location >= m_uniformSamplerOrImageUnitIndex.size() || uniform.getType() == nullptr || + !uniform.getType()->isOpaque() || (!uniform.getType()->isTexture() && !uniform.getType()->isImage())) { + continue; + } + + const int binding = uniform.getBinding(); + if (binding >= 0 && binding != static_cast(glslang::TQualifier::layoutBindingEnd)) { + m_uniformSamplerOrImageUnitIndex[location] = binding; + MGLOG_D("ProgramObject %u: Reflection - opaque uniform '%s' location=%u initialUnit=%d", + m_externalIndex, uniform.name.c_str(), location, binding); + } + } + // ------------ attributes (vertex in) --------------- Int inCount = m_program->getNumPipeInputs(); MGLOG_D("ProgramObject %u: Reflection - pipe input count (attributes) = %d", m_externalIndex, inCount); diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp index 1df25bc8..06a0530e 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp @@ -360,6 +360,17 @@ namespace MobileGL { return m_parameters.FrontFaceModeSetting; } + void RenderState::SetProvokingVertexMode(ProvokingVertexMode mode) { + if (m_parameters.ProvokingVertexModeSetting == mode) return; + + m_parameters.ProvokingVertexModeSetting = mode; + ++m_version; + } + + ProvokingVertexMode RenderState::GetProvokingVertexMode() const { + return m_parameters.ProvokingVertexModeSetting; + } + // --------------------- Scissor --------------------- void RenderState::SetScissorBox(IntVec4 box) { if (m_parameters.ScissorBox == box) return; diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.h b/MobileGL/MG_State/GLState/RenderState/RenderState.h index c6101112..32ea5e1e 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.h +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.h @@ -94,6 +94,13 @@ namespace MobileGL { Unknown = -1 }; + enum class ProvokingVertexMode { + FirstVertex, + LastVertex, + ProvokingVertexModeCount, + Unknown = -1 + }; + enum class CapabilityInput { Blend, ClipDistance0, @@ -179,6 +186,7 @@ namespace MobileGL { Bool CullFaceEnabled = false; CullFaceMode CullFaceModeSetting = CullFaceMode::Back; FrontFaceMode FrontFaceModeSetting = FrontFaceMode::CounterClockwise; + ProvokingVertexMode ProvokingVertexModeSetting = ProvokingVertexMode::LastVertex; // Scissor Bool ScissorTestEnabled = false; @@ -245,6 +253,8 @@ namespace MobileGL { CullFaceMode GetCullFaceMode() const; void SetFrontFaceMode(FrontFaceMode mode); FrontFaceMode GetFrontFaceMode() const; + void SetProvokingVertexMode(ProvokingVertexMode mode); + ProvokingVertexMode GetProvokingVertexMode() const; // Scissor void SetScissorBox(IntVec4 box); // x, y, width, height diff --git a/MobileGL/MG_Test/Buffer/BufferTest.cpp b/MobileGL/MG_Test/Buffer/BufferTest.cpp index 1f589e68..61edc8ba 100644 --- a/MobileGL/MG_Test/Buffer/BufferTest.cpp +++ b/MobileGL/MG_Test/Buffer/BufferTest.cpp @@ -330,6 +330,127 @@ TEST_F(BufferTest, DeleteBufferObject) { ASSERT_FALSE(MobileGL::MG_State::pGLContext->GetBufferObject(bufferNames[0])); } +TEST_F(BufferTest, ParameterBufferBindingAndQuery) { + Vector bufferNames; + MobileGL::MG_State::pGLContext->GenBufferNames(1, bufferNames); + auto bufObj = MobileGL::MG_State::pGLContext->CreateBufferObject(bufferNames[0]); + + auto& slot = MobileGL::MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter); + slot.Bind(bufObj); + + GLint binding = 0; + MobileGL::MG_Impl::GLImpl::GetIntegerv(GL_PARAMETER_BUFFER_BINDING_ARB, &binding); + EXPECT_EQ(binding, static_cast(bufferNames[0])); + + slot.Bind(nullptr); + MobileGL::MG_Impl::GLImpl::GetIntegerv(GL_PARAMETER_BUFFER_BINDING_ARB, &binding); + EXPECT_EQ(binding, 0); + EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(BufferTest, BindBufferBaseZeroUnbindsBindingPoint) { + GLuint buffer = 0; + MobileGL::MG_Impl::GLImpl::GenBuffers(1, &buffer); + MobileGL::MG_Impl::GLImpl::BindBuffer(GL_SHADER_STORAGE_BUFFER, buffer); + MobileGL::MG_Impl::GLImpl::BufferData(GL_SHADER_STORAGE_BUFFER, 16, nullptr, GL_DYNAMIC_DRAW); + + MobileGL::MG_Impl::GLImpl::BindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffer); + auto& point = MobileGL::MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, 2); + ASSERT_NE(point.GetBoundObject(), nullptr); + EXPECT_EQ(point.GetBoundObject()->GetExternalIndex(), buffer); + + MobileGL::MG_Impl::GLImpl::BindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, 0); + EXPECT_EQ(point.GetBoundObject(), nullptr); + EXPECT_FALSE(MobileGL::MG_State::pGLContext->ValidateBufferObject(0)); + EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(BufferTest, BindBufferRangeZeroUnbindsBindingPoint) { + GLuint buffer = 0; + MobileGL::MG_Impl::GLImpl::GenBuffers(1, &buffer); + MobileGL::MG_Impl::GLImpl::BindBuffer(GL_SHADER_STORAGE_BUFFER, buffer); + MobileGL::MG_Impl::GLImpl::BufferData(GL_SHADER_STORAGE_BUFFER, 16, nullptr, GL_DYNAMIC_DRAW); + + MobileGL::MG_Impl::GLImpl::BindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, buffer, 4, 8); + auto& point = MobileGL::MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, 3); + ASSERT_NE(point.GetBoundObject(), nullptr); + EXPECT_EQ(point.GetRange().start, 4); + EXPECT_EQ(point.GetRange().end, 12); + + MobileGL::MG_Impl::GLImpl::BindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, 0, 0, 0); + EXPECT_EQ(point.GetBoundObject(), nullptr); + EXPECT_FALSE(MobileGL::MG_State::pGLContext->ValidateBufferObject(0)); + EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(BufferTest, GetInteger64vMaxShaderStorageBlockSize) { + GLint64 maxSsboBlockSize = 0; + MobileGL::MG_Impl::GLImpl::GetInteger64v(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &maxSsboBlockSize); + + EXPECT_GT(maxSsboBlockSize, 0); + EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(BufferTest, CreateBuffersCreatesObjectsImmediately) { + GLuint buffers[2] = {}; + MobileGL::MG_Impl::GLImpl::CreateBuffers(2, buffers); + + EXPECT_NE(buffers[0], 0u); + EXPECT_NE(buffers[1], 0u); + EXPECT_TRUE(MobileGL::MG_State::pGLContext->ValidateBufferObject(buffers[0])); + EXPECT_TRUE(MobileGL::MG_State::pGLContext->ValidateBufferObject(buffers[1])); + EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(BufferTest, CopyNamedBufferSubDataCopiesBetweenDSABuffers) { + GLuint buffers[2] = {}; + MobileGL::MG_Impl::GLImpl::CreateBuffers(2, buffers); + + Vector src{1, 2, 3, 4, 5, 6}; + Vector dst(src.size(), 0); + MobileGL::MG_Impl::GLImpl::NamedBufferData(buffers[0], src.size(), src.data(), GL_STATIC_DRAW); + MobileGL::MG_Impl::GLImpl::NamedBufferData(buffers[1], dst.size(), dst.data(), GL_STATIC_DRAW); + MobileGL::MG_Impl::GLImpl::CopyNamedBufferSubData(buffers[0], buffers[1], 1, 2, 3); + + Vector actual(dst.size()); + auto dstObject = MobileGL::MG_State::pGLContext->GetBufferObject(buffers[1]); + Memcpy(actual.data(), dstObject->AcquireMemory(false, true, false), actual.size()); + EXPECT_EQ(actual, (Vector{0, 0, 2, 3, 4, 0})); + EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(BufferTest, ClearNamedBufferDataZeroesStorage) { + GLuint buffer = 0; + MobileGL::MG_Impl::GLImpl::CreateBuffers(1, &buffer); + + Vector initial(8, 0x7F); + MobileGL::MG_Impl::GLImpl::NamedBufferData(buffer, initial.size(), initial.data(), GL_STATIC_DRAW); + MobileGL::MG_Impl::GLImpl::ClearNamedBufferData(buffer, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, nullptr); + + Vector actual(initial.size(), 0xFF); + auto bufferObject = MobileGL::MG_State::pGLContext->GetBufferObject(buffer); + Memcpy(actual.data(), bufferObject->AcquireMemory(false, true, false), actual.size()); + EXPECT_EQ(actual, Vector(initial.size(), 0)); + EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(BufferTest, ClearNamedBufferSubDataRepeatsPattern) { + GLuint buffer = 0; + MobileGL::MG_Impl::GLImpl::CreateBuffers(1, &buffer); + + Vector initial{0, 0, 0, 0, 0}; + MobileGL::MG_Impl::GLImpl::NamedBufferData(buffer, initial.size() * sizeof(Uint32), initial.data(), GL_STATIC_DRAW); + const Uint32 pattern = 0xAABBCCDDu; + MobileGL::MG_Impl::GLImpl::ClearNamedBufferSubData(buffer, GL_R32UI, sizeof(Uint32), sizeof(Uint32) * 3, + GL_RED_INTEGER, GL_UNSIGNED_INT, &pattern); + + Vector actual(initial.size(), 0); + auto bufferObject = MobileGL::MG_State::pGLContext->GetBufferObject(buffer); + Memcpy(actual.data(), bufferObject->AcquireMemory(false, true, false), actual.size() * sizeof(Uint32)); + EXPECT_EQ(actual, (Vector{0, pattern, pattern, pattern, 0})); + EXPECT_EQ(MobileGL::MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + using namespace MobileGL::MG_Impl::GLImpl; class GeneralBufferTest : public ::testing::Test { diff --git a/MobileGL/MG_Test/CMakeLists.txt b/MobileGL/MG_Test/CMakeLists.txt index 1d0212fd..814482df 100644 --- a/MobileGL/MG_Test/CMakeLists.txt +++ b/MobileGL/MG_Test/CMakeLists.txt @@ -65,6 +65,8 @@ include(GoogleTest) gtest_discover_tests(SanityTest) add_subdirectory(Buffer) +add_subdirectory(Framebuffer) +add_subdirectory(Texture) add_subdirectory(VertexArray) add_subdirectory(Program) if (ENABLE_INTEGRATION_TESTS) diff --git a/MobileGL/MG_Test/Framebuffer/CMakeLists.txt b/MobileGL/MG_Test/Framebuffer/CMakeLists.txt new file mode 100644 index 00000000..191b1f5e --- /dev/null +++ b/MobileGL/MG_Test/Framebuffer/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.14) + +add_executable( + FramebufferTest + FramebufferTest.cpp +) + +target_include_directories(FramebufferTest PRIVATE + ${MGL_ROOT}/include + ${MGL_ROOT}/MobileGL +) + +target_link_libraries( + FramebufferTest PRIVATE + GTest::gtest_main + ${LINK_LIBRARIES} +) + +include(GoogleTest) +gtest_discover_tests(FramebufferTest) diff --git a/MobileGL/MG_Test/Framebuffer/FramebufferTest.cpp b/MobileGL/MG_Test/Framebuffer/FramebufferTest.cpp new file mode 100644 index 00000000..030302cc --- /dev/null +++ b/MobileGL/MG_Test/Framebuffer/FramebufferTest.cpp @@ -0,0 +1,333 @@ +// MobileGL - MobileGL/MG_Test/Framebuffer/FramebufferTest.cpp +// Copyright (c) 2025-2026 MobileGL-Dev +// Licensed under the GNU Lesser General Public License v3.0: +// https://www.gnu.org/licenses/gpl-3.0.txt +// https://www.gnu.org/licenses/lgpl-3.0.txt +// SPDX-License-Identifier: LGPL-3.0-only +// End of Source File Header + +#include + +#include "Includes.h" +#include "Init.h" +#include +#include +#include +#include +#include +#include + +using namespace MobileGL; + +namespace { + SharedPtr g_lastBlitReadFramebuffer; + SharedPtr g_lastBlitDrawFramebuffer; + Int g_blitNamedFramebufferCallCount = 0; + SharedPtr g_lastClearFramebuffer; + GLenum g_lastClearBuffer = GL_NONE; + GLint g_lastClearDrawbuffer = -1; + GLfloat g_lastClearDepth = -1.0f; + GLint g_lastClearStencil = -1; + FloatVec4 g_lastClearColor = {}; + Int g_clearNamedFramebufferfvCallCount = 0; + Int g_clearNamedFramebufferfiCallCount = 0; + Int g_readPixelsCallCount = 0; + + void RecordBlitNamedFramebuffer(const SharedPtr& readFramebuffer, + const SharedPtr& drawFramebuffer, + GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum) { + g_lastBlitReadFramebuffer = readFramebuffer; + g_lastBlitDrawFramebuffer = drawFramebuffer; + ++g_blitNamedFramebufferCallCount; + } + + void RecordClearNamedFramebufferfv(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, const GLfloat* value) { + g_lastClearFramebuffer = framebuffer; + g_lastClearBuffer = buffer; + g_lastClearDrawbuffer = drawbuffer; + if (value) { + if (buffer == GL_COLOR) { + g_lastClearColor = FloatVec4(value[0], value[1], value[2], value[3]); + } else if (buffer == GL_DEPTH) { + g_lastClearDepth = value[0]; + } + } + ++g_clearNamedFramebufferfvCallCount; + } + + void RecordClearNamedFramebufferfi(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { + g_lastClearFramebuffer = framebuffer; + g_lastClearBuffer = buffer; + g_lastClearDrawbuffer = drawbuffer; + g_lastClearDepth = depth; + g_lastClearStencil = stencil; + ++g_clearNamedFramebufferfiCallCount; + } + + void RecordReadPixels(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, void*) { + ++g_readPixelsCallCount; + } +} // namespace + +class FramebufferTest : public ::testing::Test { +protected: + void SetUp() override { + MobileGL::Initialize(); + g_lastBlitReadFramebuffer = nullptr; + g_lastBlitDrawFramebuffer = nullptr; + g_blitNamedFramebufferCallCount = 0; + g_lastClearFramebuffer = nullptr; + g_lastClearBuffer = GL_NONE; + g_lastClearDrawbuffer = -1; + g_lastClearDepth = -1.0f; + g_lastClearStencil = -1; + g_lastClearColor = {}; + g_clearNamedFramebufferfvCallCount = 0; + g_clearNamedFramebufferfiCallCount = 0; + g_readPixelsCallCount = 0; + MG_Backend::gBackendFunctionsTable.GL.BlitNamedFramebuffer = nullptr; + MG_Backend::gBackendFunctionsTable.GL.ClearNamedFramebufferfv = nullptr; + MG_Backend::gBackendFunctionsTable.GL.ClearNamedFramebufferfi = nullptr; + MG_Backend::gBackendFunctionsTable.GL.ReadPixels = nullptr; + } +}; + +TEST_F(FramebufferTest, CreateFramebuffersCreatesObjectsImmediately) { + GLuint framebuffers[2] = {}; + MG_Impl::GLImpl::CreateFramebuffers(2, framebuffers); + + EXPECT_NE(framebuffers[0], 0u); + EXPECT_NE(framebuffers[1], 0u); + EXPECT_TRUE(MG_State::pGLContext->ValidateFramebufferObject(framebuffers[0])); + EXPECT_TRUE(MG_State::pGLContext->ValidateFramebufferObject(framebuffers[1])); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(FramebufferTest, NamedFramebufferTextureAttachesWithoutChangingBindings) { + GLuint framebuffer = 0; + MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); + + Vector textureNames; + MG_State::pGLContext->GenTextureNames(1, textureNames); + MG_State::pGLContext->CreateTextureObject(textureNames[0], TextureTarget::Texture2D); + + const auto originalDraw = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + const auto originalRead = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + + MG_Impl::GLImpl::NamedFramebufferTexture(framebuffer, GL_COLOR_ATTACHMENT0, textureNames[0], 3); + + const auto framebufferObject = MG_State::pGLContext->GetFramebufferObject(framebuffer); + const auto& attachment = framebufferObject->GetAttachment(FramebufferAttachmentType::Color0); + EXPECT_TRUE(attachment.IsTexture()); + EXPECT_EQ(attachment.GetTexture()->GetExternalIndex(), textureNames[0]); + EXPECT_EQ(attachment.GetTextureLevel(), 3); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(), originalDraw); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(), originalRead); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(FramebufferTest, NamedDepthFramebufferTextureStorageIsCompleteWithoutBinding) { + GLuint framebuffer = 0; + GLuint texture = 0; + MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); + + const auto originalDraw = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + const auto originalRead = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + + MG_Impl::GLImpl::TextureStorage2D(texture, 1, GL_DEPTH_COMPONENT24, 64, 32); + MG_Impl::GLImpl::NamedFramebufferTexture(framebuffer, GL_DEPTH_ATTACHMENT, texture, 0); + + EXPECT_EQ(MG_Impl::GLImpl::CheckNamedFramebufferStatus(framebuffer, GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(), originalDraw); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(), originalRead); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(FramebufferTest, ReadPixelsAllowsPersistentMappedPixelPackBuffer) { + GLuint framebuffer = 0; + GLuint texture = 0; + GLuint pixelPackBuffer = 0; + MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); + MG_Impl::GLImpl::CreateBuffers(1, &pixelPackBuffer); + + MG_Impl::GLImpl::TextureStorage2D(texture, 1, GL_RGBA8, 4, 4); + MG_Impl::GLImpl::NamedFramebufferTexture(framebuffer, GL_COLOR_ATTACHMENT0, texture, 0); + MG_Impl::GLImpl::BindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer); + + MG_Impl::GLImpl::BindBuffer(GL_PIXEL_PACK_BUFFER, pixelPackBuffer); + MG_Impl::GLImpl::BufferStorage(GL_PIXEL_PACK_BUFFER, 4 * 4 * 4, nullptr, + GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT); + ASSERT_NE(MG_Impl::GLImpl::MapBufferRange(GL_PIXEL_PACK_BUFFER, 0, 4 * 4 * 4, + GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT), + nullptr); + + MG_Backend::gBackendFunctionsTable.GL.ReadPixels = RecordReadPixels; + MG_Impl::GLImpl::ReadPixels(0, 0, 4, 4, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + + EXPECT_EQ(g_readPixelsCallCount, 1); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(FramebufferTest, NamedRenderbufferStorageAndFramebufferAttachDoNotChangeBindings) { + GLuint framebuffer = 0; + GLuint renderbuffer = 0; + MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); + MG_Impl::GLImpl::CreateRenderbuffers(1, &renderbuffer); + + const auto originalDraw = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + const auto originalRead = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + const auto originalRenderbuffer = + MG_State::pGLContext->GetRenderbufferBindingSlot(RenderbufferTarget::Renderbuffer).GetBoundObject(); + + MG_Impl::GLImpl::NamedRenderbufferStorage(renderbuffer, GL_RGBA8, 64, 32); + + GLint width = 0; + GLint height = 0; + GLint format = 0; + MG_Impl::GLImpl::GetNamedRenderbufferParameteriv(renderbuffer, GL_RENDERBUFFER_WIDTH, &width); + MG_Impl::GLImpl::GetNamedRenderbufferParameteriv(renderbuffer, GL_RENDERBUFFER_HEIGHT, &height); + MG_Impl::GLImpl::GetNamedRenderbufferParameteriv(renderbuffer, GL_RENDERBUFFER_INTERNAL_FORMAT, &format); + + EXPECT_EQ(width, 64); + EXPECT_EQ(height, 32); + EXPECT_EQ(format, GL_RGBA8); + + MG_Impl::GLImpl::NamedFramebufferRenderbuffer(framebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); + + const auto framebufferObject = MG_State::pGLContext->GetFramebufferObject(framebuffer); + const auto& attachment = framebufferObject->GetAttachment(FramebufferAttachmentType::Color0); + EXPECT_TRUE(attachment.IsRenderbuffer()); + EXPECT_EQ(attachment.GetRenderbuffer()->GetExternalIndex(), renderbuffer); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(), originalDraw); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(), originalRead); + EXPECT_EQ(MG_State::pGLContext->GetRenderbufferBindingSlot(RenderbufferTarget::Renderbuffer).GetBoundObject(), + originalRenderbuffer); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(FramebufferTest, NamedFramebufferDrawBuffersDoNotModifyDefaultFramebuffer) { + GLuint framebuffer = 0; + MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); + + const auto defaultDraw = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + const auto defaultRead = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + + GLenum bufs[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; + MG_Impl::GLImpl::NamedFramebufferDrawBuffers(framebuffer, 2, bufs); + MG_Impl::GLImpl::NamedFramebufferReadBuffer(framebuffer, GL_COLOR_ATTACHMENT1); + + const auto framebufferObject = MG_State::pGLContext->GetFramebufferObject(framebuffer); + EXPECT_EQ(framebufferObject->GetDrawBuffers()[0], FramebufferAttachmentType::Color0); + EXPECT_EQ(framebufferObject->GetDrawBuffers()[1], FramebufferAttachmentType::Color1); + EXPECT_EQ(framebufferObject->GetReadBuffer(), FramebufferAttachmentType::Color1); + EXPECT_EQ(defaultDraw->GetDrawBuffers()[0], FramebufferAttachmentType::Color0); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(), defaultDraw); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(), defaultRead); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(FramebufferTest, ClearNamedFramebufferfvUsesNamedObjectWithoutChangingBindings) { + GLuint framebuffer = 0; + MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); + + const auto defaultDraw = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + const auto defaultRead = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + + const GLfloat depth[] = {0.25f}; + MG_Backend::gBackendFunctionsTable.GL.ClearNamedFramebufferfv = RecordClearNamedFramebufferfv; + MG_Impl::GLImpl::ClearNamedFramebufferfv(framebuffer, GL_DEPTH, 0, depth); + + const auto framebufferObject = MG_State::pGLContext->GetFramebufferObject(framebuffer); + EXPECT_EQ(g_clearNamedFramebufferfvCallCount, 1); + EXPECT_EQ(g_lastClearFramebuffer, framebufferObject); + EXPECT_EQ(g_lastClearBuffer, GL_DEPTH); + EXPECT_EQ(g_lastClearDrawbuffer, 0); + EXPECT_EQ(g_lastClearDepth, 0.25f); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(), defaultDraw); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(), defaultRead); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(FramebufferTest, ClearNamedFramebufferfiAllowsDefaultFramebufferZero) { + GLuint framebuffer = 0; + MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); + + const auto defaultDraw = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + const auto defaultRead = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + + MG_Backend::gBackendFunctionsTable.GL.ClearNamedFramebufferfi = RecordClearNamedFramebufferfi; + MG_Impl::GLImpl::ClearNamedFramebufferfi(0, GL_DEPTH_STENCIL, 0, 0.5f, 7); + + EXPECT_EQ(g_clearNamedFramebufferfiCallCount, 1); + EXPECT_EQ(g_lastClearFramebuffer, defaultDraw); + EXPECT_EQ(g_lastClearBuffer, GL_DEPTH_STENCIL); + EXPECT_EQ(g_lastClearDrawbuffer, 0); + EXPECT_EQ(g_lastClearDepth, 0.5f); + EXPECT_EQ(g_lastClearStencil, 7); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(), defaultDraw); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(), defaultRead); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(FramebufferTest, GetNamedFramebufferAttachmentParameterivReadsTargetObjectDirectly) { + GLuint framebuffer = 0; + MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); + + Vector textureNames; + MG_State::pGLContext->GenTextureNames(1, textureNames); + MG_State::pGLContext->CreateTextureObject(textureNames[0], TextureTarget::Texture2D); + MG_Impl::GLImpl::NamedFramebufferTexture(framebuffer, GL_COLOR_ATTACHMENT0, textureNames[0], 2); + + GLint objectType = 0; + GLint objectName = 0; + GLint textureLevel = 0; + MG_Impl::GLImpl::GetNamedFramebufferAttachmentParameteriv( + framebuffer, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &objectType); + MG_Impl::GLImpl::GetNamedFramebufferAttachmentParameteriv( + framebuffer, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &objectName); + MG_Impl::GLImpl::GetNamedFramebufferAttachmentParameteriv( + framebuffer, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, &textureLevel); + + EXPECT_EQ(objectType, GL_TEXTURE); + EXPECT_EQ(objectName, static_cast(textureNames[0])); + EXPECT_EQ(textureLevel, 2); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(FramebufferTest, BlitNamedFramebufferAllowsDefaultFramebufferZero) { + GLuint framebuffer = 0; + MG_Impl::GLImpl::CreateFramebuffers(1, &framebuffer); + + const auto defaultDraw = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); + const auto defaultRead = + MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); + + MG_Backend::gBackendFunctionsTable.GL.BlitNamedFramebuffer = RecordBlitNamedFramebuffer; + MG_Impl::GLImpl::BlitNamedFramebuffer(framebuffer, 0, 0, 0, 16, 16, 0, 0, 16, 16, GL_COLOR_BUFFER_BIT, + GL_NEAREST); + + const auto framebufferObject = MG_State::pGLContext->GetFramebufferObject(framebuffer); + EXPECT_EQ(g_blitNamedFramebufferCallCount, 1); + EXPECT_EQ(g_lastBlitReadFramebuffer, framebufferObject); + EXPECT_EQ(g_lastBlitDrawFramebuffer, defaultDraw); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(), defaultDraw); + EXPECT_EQ(MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(), defaultRead); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} diff --git a/MobileGL/MG_Test/Program/ProgramTest.cpp b/MobileGL/MG_Test/Program/ProgramTest.cpp index 314d138e..c074c7e3 100644 --- a/MobileGL/MG_Test/Program/ProgramTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramTest.cpp @@ -12,6 +12,8 @@ #include "Includes.h" #include "Init.h" +#include "MG_Backend/DirectVulkan/DirectVulkanResourceState.h" +#include "MG_Impl/GLImpl/Getter/GL_Getter.h" #include "MG_Impl/GLImpl/Program/GL_Program.h" #include "MG_State/GLState/Core.h" #include "MG_Util/ShaderTranspiler/ShaderCompiler.h" @@ -203,6 +205,79 @@ void main() { ASSERT_EQ(linkStatus, GL_TRUE) << infoLog; } +TEST_F(ProgramTest, ImageUniformLayoutBindingInitializesImageUnit) { + char infoLog[1024] = ""; + const char* csSrc = R"(#version 460 core +layout(local_size_x = 1) in; +layout(binding = 4, rgba8) uniform writeonly image2D colourTexOut; + +void main() { + imageStore(colourTexOut, ivec2(0), vec4(1.0)); +} +)"; + + GLuint cs = CreateShader(GL_COMPUTE_SHADER); + ShaderSource(cs, 1, &csSrc, nullptr); + CompileShader(cs); + GLint csStatus = GL_FALSE; + GetShaderiv(cs, GL_COMPILE_STATUS, &csStatus); + GetShaderInfoLog(cs, sizeof(infoLog), nullptr, infoLog); + ASSERT_EQ(csStatus, GL_TRUE) << infoLog; + + GLuint program = CreateProgram(); + AttachShader(program, cs); + LinkProgram(program); + GLint linkStatus = GL_FALSE; + GetProgramiv(program, GL_LINK_STATUS, &linkStatus); + GetProgramInfoLog(program, sizeof(infoLog), nullptr, infoLog); + ASSERT_EQ(linkStatus, GL_TRUE) << infoLog; + + const GLint location = GetUniformLocation(program, "colourTexOut"); + ASSERT_GE(location, 0); + auto programObject = MobileGL::MG_State::pGLContext->GetProgramObject(program); + ASSERT_NE(programObject, nullptr); + EXPECT_EQ(programObject->GetUniformSamplerOrImageUnitIndex(static_cast(location)), 4); + EXPECT_EQ(GetError(), GL_NO_ERROR); +} + +TEST_F(ProgramTest, DirectVulkanStorageBlockUsesShaderLayoutBinding) { + char infoLog[1024] = ""; + const char* csSrc = R"(#version 460 core +layout(local_size_x = 1) in; +layout(std430, binding = 2) buffer requestQueueStruct { + uint value; +} requestQueue; + +void main() { + requestQueue.value = 1u; +} +)"; + + GLuint cs = CreateShader(GL_COMPUTE_SHADER); + ShaderSource(cs, 1, &csSrc, nullptr); + CompileShader(cs); + GLint csStatus = GL_FALSE; + GetShaderiv(cs, GL_COMPILE_STATUS, &csStatus); + GetShaderInfoLog(cs, sizeof(infoLog), nullptr, infoLog); + ASSERT_EQ(csStatus, GL_TRUE) << infoLog; + + GLuint program = CreateProgram(); + AttachShader(program, cs); + LinkProgram(program); + GLint linkStatus = GL_FALSE; + GetProgramiv(program, GL_LINK_STATUS, &linkStatus); + GetProgramInfoLog(program, sizeof(infoLog), nullptr, infoLog); + ASSERT_EQ(linkStatus, GL_TRUE) << infoLog; + + auto programObject = MobileGL::MG_State::pGLContext->GetProgramObject(program); + ASSERT_NE(programObject, nullptr); + const GLuint blockIndex = + MG_Backend::DirectVulkan::GetShaderStorageBlockIndex(*programObject, "requestQueueStruct"); + ASSERT_NE(blockIndex, GL_INVALID_INDEX); + EXPECT_EQ(MG_Backend::DirectVulkan::GetShaderStorageBlockBinding(*programObject, blockIndex), 2u); + EXPECT_EQ(GetError(), GL_NO_ERROR); +} + TEST_F(ProgramTest, CompileAndLink) { char infoLog[1024] = ""; @@ -306,6 +381,63 @@ TEST_F(ProgramTest, CompileAndLink) { } } +TEST_F(ProgramTest, Uniform1uiStoresUnsignedValue) { + char infoLog[1024] = ""; + + const char* simpleVs = R"(#version 460 +layout(location = 0) in vec4 Position; + +void main() { + gl_Position = Position; +} +)"; + + const char* uintFs = R"(#version 460 +uniform uint NodeQueueIndex; + +out vec4 fragColor; + +void main() { + fragColor = vec4(float(NodeQueueIndex & 255u)); +} +)"; + + GLuint vs = CreateShader(GL_VERTEX_SHADER); + ShaderSource(vs, 1, &simpleVs, NULL); + CompileShader(vs); + GLint vsStatus = GL_FALSE; + GetShaderiv(vs, GL_COMPILE_STATUS, &vsStatus); + GetShaderInfoLog(vs, 1024, nullptr, infoLog); + ASSERT_EQ(vsStatus, GL_TRUE) << infoLog; + + GLuint fs = CreateShader(GL_FRAGMENT_SHADER); + ShaderSource(fs, 1, &uintFs, NULL); + CompileShader(fs); + GLint fsStatus = GL_FALSE; + GetShaderiv(fs, GL_COMPILE_STATUS, &fsStatus); + GetShaderInfoLog(fs, 1024, nullptr, infoLog); + ASSERT_EQ(fsStatus, GL_TRUE) << infoLog; + + GLuint program = CreateProgram(); + AttachShader(program, vs); + AttachShader(program, fs); + LinkProgram(program); + GLint linkStatus = GL_FALSE; + GetProgramiv(program, GL_LINK_STATUS, &linkStatus); + ASSERT_EQ(linkStatus, GL_TRUE); + + UseProgram(program); + GLint loc = GetUniformLocation(program, "NodeQueueIndex"); + ASSERT_GE(loc, 0); + + const GLuint expected = 0xF1234567u; + Uniform1ui(loc, expected); + + GLint actual = 0; + GetUniformiv(program, loc, &actual); + EXPECT_EQ(static_cast(actual), expected); +} + TEST_F(ProgramTest, UniformMatrixFunctions) { char infoLog[1024] = ""; diff --git a/MobileGL/MG_Test/Program/ProgramUtilTest.cpp b/MobileGL/MG_Test/Program/ProgramUtilTest.cpp index bfc619a1..de85927a 100644 --- a/MobileGL/MG_Test/Program/ProgramUtilTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramUtilTest.cpp @@ -81,6 +81,28 @@ void main() { EXPECT_EQ(source.find("#define"), String::npos); } +TEST_F(ProgramUtilTest, PreprocessFragmentShaderInjectsDepthRangeShim) { + using namespace MG_Util::ShaderTranspiler; + + String source = R"(#version 460 core +out float depth; + +void main() { + depth = gl_DepthRange.diff * 0.5 + gl_DepthRange.near; +})"; + + PreprocessShaderSource(ShaderStage::Fragment, source); + + EXPECT_NE(source.find("struct mg_DepthRangeParameters"), String::npos); + EXPECT_NE(source.find("#define gl_DepthRange mg_DepthRange"), String::npos); + + ShaderAttrib attrib{.shaderType = GL_FRAGMENT_SHADER, .sourceStr = source}; + auto res = ShaderCompiler::CompileShader(attrib); + if (!res) { + FAIL() << "errc: " << res.error().errc << "\nlog: " << res.error().log << "\nsource:\n" << source; + } +} + const char* vs = R"(#version 150 in vec4 Position; diff --git a/MobileGL/MG_Test/SanityTest.cpp b/MobileGL/MG_Test/SanityTest.cpp index fe0e8e3b..88a5c5d1 100644 --- a/MobileGL/MG_Test/SanityTest.cpp +++ b/MobileGL/MG_Test/SanityTest.cpp @@ -14,6 +14,13 @@ #include #include +#include +#include +#include +#include +#include +#include +#include #include namespace { @@ -48,6 +55,102 @@ TEST(DirectGLESSanity, AdvertisesDepthTextureForGlmarkShadowScenes) { EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_depth_texture), extensions.end()); } +TEST(DirectVulkanSanity, AdvertisesTextureStorageForDirectStateAccess) { + MobileGL::MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend; + const auto& extensions = backend.GetRendererInfo().RendererGLInfo.Extensions; + + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_direct_state_access), + extensions.end()); + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_texture_storage), extensions.end()); +} + +TEST(DirectVulkanSanity, AdvertisesVoxyRequiredRenderingExtensionsWithoutRaisingGLVersion) { + MobileGL::MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend; + const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo; + const auto& extensions = rendererInfo.Extensions; + + EXPECT_EQ(rendererInfo.TargetGLVersion.Major, 3); + EXPECT_EQ(rendererInfo.TargetGLVersion.Minor, 3); + EXPECT_EQ(rendererInfo.TargetGLVersion.Patch, 0); + + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_compute_shader), + extensions.end()); + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_shader_storage_buffer_object), + extensions.end()); + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_multi_draw_indirect), + extensions.end()); + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_indirect_parameters), + extensions.end()); + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_shader_draw_parameters), + extensions.end()); + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_gpu_shader_int64), + extensions.end()); +} + +TEST(DirectVulkanSanity, UndefinedDepthStencilLayoutUsesDontCareForUnclearedAspects) { + using namespace MobileGL; + using namespace MobileGL::MG_Backend::DirectVulkan; + + auto noClear = ResolveDepthStencilAttachmentLoadInfo(VK_IMAGE_LAYOUT_UNDEFINED, false, false); + EXPECT_EQ(noClear.depthLoadOp, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + EXPECT_EQ(noClear.stencilLoadOp, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + EXPECT_EQ(noClear.initialLayout, VK_IMAGE_LAYOUT_UNDEFINED); + + auto depthOnlyClear = ResolveDepthStencilAttachmentLoadInfo(VK_IMAGE_LAYOUT_UNDEFINED, true, false); + EXPECT_EQ(depthOnlyClear.depthLoadOp, VK_ATTACHMENT_LOAD_OP_CLEAR); + EXPECT_EQ(depthOnlyClear.stencilLoadOp, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + EXPECT_EQ(depthOnlyClear.initialLayout, VK_IMAGE_LAYOUT_UNDEFINED); + + auto knownLayout = ResolveDepthStencilAttachmentLoadInfo( + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, false); + EXPECT_EQ(knownLayout.depthLoadOp, VK_ATTACHMENT_LOAD_OP_LOAD); + EXPECT_EQ(knownLayout.stencilLoadOp, VK_ATTACHMENT_LOAD_OP_LOAD); + EXPECT_EQ(knownLayout.initialLayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); +} + +TEST(DirectVulkanSanity, SampledDepthStencilViewUsesSingleDepthAspect) { + using namespace MobileGL::MG_Backend::DirectVulkan; + + EXPECT_EQ(VkTextureManager::ResolveSampledImageViewAspectMask(VK_IMAGE_ASPECT_COLOR_BIT), + VK_IMAGE_ASPECT_COLOR_BIT); + EXPECT_EQ(VkTextureManager::ResolveSampledImageViewAspectMask(VK_IMAGE_ASPECT_DEPTH_BIT), + VK_IMAGE_ASPECT_DEPTH_BIT); + EXPECT_EQ(VkTextureManager::ResolveSampledImageViewAspectMask(VK_IMAGE_ASPECT_STENCIL_BIT), + VK_IMAGE_ASPECT_STENCIL_BIT); + EXPECT_EQ(VkTextureManager::ResolveSampledImageViewAspectMask( + VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT), + VK_IMAGE_ASPECT_DEPTH_BIT); +} + +TEST(RenderStateSanity, ProvokingVertexUpdatesStateAndValidatesEnum) { + using namespace MobileGL; + + MG_State::pGLContext = MakeUnique(); + MG_Backend::pActiveBackendObject = MakeUnique(); + + GLint mode = 0; + MG_Impl::GLImpl::GetIntegerv(GL_PROVOKING_VERTEX, &mode); + EXPECT_EQ(mode, GL_LAST_VERTEX_CONVENTION); + + const Uint initialVersion = MG_State::pGLContext->GetRenderStateParametersVersion(); + MG_Impl::GLImpl::ProvokingVertex(GL_FIRST_VERTEX_CONVENTION); + MG_Impl::GLImpl::GetIntegerv(GL_PROVOKING_VERTEX, &mode); + EXPECT_EQ(mode, GL_FIRST_VERTEX_CONVENTION); + EXPECT_GT(MG_State::pGLContext->GetRenderStateParametersVersion(), initialVersion); + + const Uint updatedVersion = MG_State::pGLContext->GetRenderStateParametersVersion(); + MG_Impl::GLImpl::ProvokingVertex(GL_FIRST_VERTEX_CONVENTION); + EXPECT_EQ(MG_State::pGLContext->GetRenderStateParametersVersion(), updatedVersion); + + MG_Impl::GLImpl::ProvokingVertex(GL_TRIANGLES); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_ENUM); + MG_Impl::GLImpl::GetIntegerv(GL_PROVOKING_VERTEX, &mode); + EXPECT_EQ(mode, GL_FIRST_VERTEX_CONVENTION); + + MG_Backend::pActiveBackendObject.reset(); + MG_State::pGLContext.reset(); +} + TEST(LogSanity, UsesEnvOverrideForFilePath) { namespace fs = std::filesystem; @@ -62,11 +165,13 @@ TEST(LogSanity, UsesEnvOverrideForFilePath) { MobileGL::MG_Util::Debug::Close(); UnsetEnvVar("MOBILEGL_LOG_FILE_PATH"); - std::ifstream logFile(logPath); - ASSERT_TRUE(logFile.good()); + { + std::ifstream logFile(logPath); + ASSERT_TRUE(logFile.good()); - const std::string contents((std::istreambuf_iterator(logFile)), std::istreambuf_iterator()); - EXPECT_NE(contents.find(message), std::string::npos); + const std::string contents((std::istreambuf_iterator(logFile)), std::istreambuf_iterator()); + EXPECT_NE(contents.find(message), std::string::npos); + } fs::remove(logPath); } diff --git a/MobileGL/MG_Test/Texture/CMakeLists.txt b/MobileGL/MG_Test/Texture/CMakeLists.txt new file mode 100644 index 00000000..b6860ba7 --- /dev/null +++ b/MobileGL/MG_Test/Texture/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.14) + +add_executable( + TextureTest + TextureTest.cpp +) + +target_include_directories(TextureTest PRIVATE + ${MGL_ROOT}/include + ${MGL_ROOT}/MobileGL +) + +target_link_libraries( + TextureTest PRIVATE + GTest::gtest_main + ${LINK_LIBRARIES} +) + +include(GoogleTest) +gtest_discover_tests(TextureTest) diff --git a/MobileGL/MG_Test/Texture/TextureTest.cpp b/MobileGL/MG_Test/Texture/TextureTest.cpp new file mode 100644 index 00000000..ccb8d493 --- /dev/null +++ b/MobileGL/MG_Test/Texture/TextureTest.cpp @@ -0,0 +1,189 @@ +// MobileGL - MobileGL/MG_Test/Texture/TextureTest.cpp +// Copyright (c) 2025-2026 MobileGL-Dev +// Licensed under the GNU Lesser General Public License v3.0: +// https://www.gnu.org/licenses/gpl-3.0.txt +// https://www.gnu.org/licenses/lgpl-3.0.txt +// SPDX-License-Identifier: LGPL-3.0-only +// End of Source File Header + +#include + +#include "Includes.h" +#include "Init.h" +#include +#include +#include +#include +#include + +using namespace MobileGL; + +class TextureTest : public ::testing::Test { +protected: + void SetUp() override { MobileGL::Initialize(); } +}; + +TEST_F(TextureTest, CreateTexturesCreatesObjectsWithoutBinding) { + GLuint texture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); + + EXPECT_NE(texture, 0u); + EXPECT_TRUE(MG_State::pGLContext->ValidateTextureObject(texture)); + + auto& unit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit()); + EXPECT_EQ(unit.GetBindingSlot(TextureTarget::Texture2D).GetBoundObject(), nullptr); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(TextureTest, TextureStorageAndSubImageModifyNamedObjectOnly) { + GLuint namedTexture = 0; + GLuint boundTexture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &namedTexture); + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &boundTexture); + MG_Impl::GLImpl::BindTextureUnit(0, boundTexture); + + const auto boundObjectBefore = + MG_State::pGLContext->GetTextureUnitObject(0).GetBindingSlot(TextureTarget::Texture2D).GetBoundObject(); + + MG_Impl::GLImpl::TextureStorage2D(namedTexture, 2, GL_RGBA8, 2, 2); + const Uint8 pixels[] = { + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16, + }; + MG_Impl::GLImpl::TextureSubImage2D(namedTexture, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + const auto namedObject = MG_State::pGLContext->GetTextureObject(namedTexture); + auto* mipmapObject = static_cast(namedObject.get()); + EXPECT_EQ(mipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture2D, 0), IntVec3(2, 2, 1)); + EXPECT_EQ(mipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture2D, 1), IntVec3(1, 1, 1)); + EXPECT_TRUE(mipmapObject->IsStorageDirty(TextureUploadTarget::Texture2D, 0)); + EXPECT_FALSE(mipmapObject->IsStorageDirty(TextureUploadTarget::Texture2D, 1)); + + EXPECT_EQ(MG_State::pGLContext->GetTextureUnitObject(0).GetBindingSlot(TextureTarget::Texture2D).GetBoundObject(), + boundObjectBefore); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(TextureTest, GetTextureImageReadsNamedObjectWithoutBinding) { + GLuint texture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); + MG_Impl::GLImpl::TextureStorage2D(texture, 1, GL_RGBA8, 2, 1); + + const Uint8 pixels[] = { + 21, 22, 23, 24, + 31, 32, 33, 34, + }; + MG_Impl::GLImpl::TextureSubImage2D(texture, 0, 0, 0, 2, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + Uint8 output[sizeof(pixels)] = {}; + MG_Impl::GLImpl::GetTextureImage(texture, 0, GL_RGBA, GL_UNSIGNED_BYTE, sizeof(output), output); + + EXPECT_EQ(std::memcmp(output, pixels, sizeof(pixels)), 0); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(TextureTest, GetTextureSubImageReadsFullNamedLevelWithoutBinding) { + GLuint texture = 0; + GLuint boundTexture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &boundTexture); + MG_Impl::GLImpl::BindTextureUnit(0, boundTexture); + + const auto boundObjectBefore = + MG_State::pGLContext->GetTextureUnitObject(0).GetBindingSlot(TextureTarget::Texture2D).GetBoundObject(); + + MG_Impl::GLImpl::TextureStorage2D(texture, 1, GL_RGBA8, 2, 1); + const Uint8 pixels[] = { + 41, 42, 43, 44, + 51, 52, 53, 54, + }; + MG_Impl::GLImpl::TextureSubImage2D(texture, 0, 0, 0, 2, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + Uint8 output[sizeof(pixels)] = {}; + MG_Impl::GLImpl::GetTextureSubImage(texture, 0, 0, 0, 0, 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + sizeof(output), output); + + EXPECT_EQ(std::memcmp(output, pixels, sizeof(pixels)), 0); + EXPECT_EQ(MG_State::pGLContext->GetTextureUnitObject(0).GetBindingSlot(TextureTarget::Texture2D).GetBoundObject(), + boundObjectBefore); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(TextureTest, GetTextureSubImageRejectsPartialReadbackForNow) { + GLuint texture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); + MG_Impl::GLImpl::TextureStorage2D(texture, 1, GL_RGBA8, 2, 2); + + Uint8 output[4] = {}; + MG_Impl::GLImpl::GetTextureSubImage(texture, 0, 0, 0, 0, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + sizeof(output), output); + + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_OPERATION); +} + +TEST_F(TextureTest, TextureParameteriAndBindTextureUnitAreDirectStateAccess) { + GLuint texture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); + + MG_Impl::GLImpl::TextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + GLint minFilter = 0; + MG_Impl::GLImpl::GetTextureParameteriv(texture, GL_TEXTURE_MIN_FILTER, &minFilter); + EXPECT_EQ(minFilter, GL_NEAREST); + + MG_Impl::GLImpl::BindTextureUnit(3, texture); + EXPECT_EQ(MG_State::pGLContext->GetActiveTextureUnit(), 0); + EXPECT_EQ(MG_State::pGLContext->GetTextureUnitObject(3) + .GetBindingSlot(TextureTarget::Texture2D) + .GetBoundObject() + ->GetExternalIndex(), + texture); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(TextureTest, TextureParameterfModifiesNamedObjectWithoutBinding) { + GLuint namedTexture = 0; + GLuint boundTexture = 0; + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &namedTexture); + MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &boundTexture); + MG_Impl::GLImpl::BindTextureUnit(0, boundTexture); + + const auto boundObjectBefore = + MG_State::pGLContext->GetTextureUnitObject(0).GetBindingSlot(TextureTarget::Texture2D).GetBoundObject(); + + MG_Impl::GLImpl::TextureParameterf(namedTexture, GL_TEXTURE_MIN_FILTER, static_cast(GL_LINEAR)); + MG_Impl::GLImpl::TextureParameterf(namedTexture, GL_TEXTURE_MAG_FILTER, static_cast(GL_NEAREST)); + MG_Impl::GLImpl::TextureParameterf(namedTexture, GL_DEPTH_STENCIL_TEXTURE_MODE, + static_cast(GL_DEPTH_COMPONENT)); + + GLint namedMinFilter = 0; + GLint namedMagFilter = 0; + GLint boundMinFilter = 0; + GLint boundMagFilter = 0; + MG_Impl::GLImpl::GetTextureParameteriv(namedTexture, GL_TEXTURE_MIN_FILTER, &namedMinFilter); + MG_Impl::GLImpl::GetTextureParameteriv(namedTexture, GL_TEXTURE_MAG_FILTER, &namedMagFilter); + MG_Impl::GLImpl::GetTextureParameteriv(boundTexture, GL_TEXTURE_MIN_FILTER, &boundMinFilter); + MG_Impl::GLImpl::GetTextureParameteriv(boundTexture, GL_TEXTURE_MAG_FILTER, &boundMagFilter); + + EXPECT_EQ(namedMinFilter, GL_LINEAR); + EXPECT_EQ(namedMagFilter, GL_NEAREST); + EXPECT_EQ(boundMinFilter, GL_NEAREST_MIPMAP_LINEAR); + EXPECT_EQ(boundMagFilter, GL_LINEAR); + EXPECT_EQ(MG_State::pGLContext->GetTextureUnitObject(0).GetBindingSlot(TextureTarget::Texture2D).GetBoundObject(), + boundObjectBefore); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + +TEST_F(TextureTest, NormalizeDepth24Stencil8UsesPackedDepthStencilType) { + GLenum internalFormat = 0; + GLenum format = 0; + GLenum type = 0; + MG_Util::TextureFormatProcessor::NormalizePixelFormat(GL_DEPTH24_STENCIL8, + PixelFormatNormalizeOptionBit::None, + &internalFormat, &format, &type); + + EXPECT_EQ(internalFormat, GL_DEPTH24_STENCIL8); + EXPECT_EQ(format, GL_DEPTH_STENCIL); + EXPECT_EQ(type, GL_UNSIGNED_INT_24_8); +} diff --git a/MobileGL/MG_Test/VertexArray/VertexArrayTest.cpp b/MobileGL/MG_Test/VertexArray/VertexArrayTest.cpp index db062d6a..4196f6d3 100644 --- a/MobileGL/MG_Test/VertexArray/VertexArrayTest.cpp +++ b/MobileGL/MG_Test/VertexArray/VertexArrayTest.cpp @@ -272,6 +272,102 @@ TEST_F(GeneralVertexArrayTest, General_VAOLifecycle) { EXPECT_EQ(GetError(), GL_NO_ERROR); } +TEST_F(GeneralVertexArrayTest, General_CreateVertexArraysCreatesObjectsWithoutChangingBinding) { + GLuint bound = CreateVAO(); + auto boundObj = MG_State::pGLContext->GetBoundVertexArray(); + ASSERT_NE(boundObj, nullptr); + + GLuint vaos[2] = {}; + CreateVertexArrays(2, vaos); + + EXPECT_NE(vaos[0], 0u); + EXPECT_NE(vaos[1], 0u); + EXPECT_NE(vaos[0], vaos[1]); + EXPECT_EQ(IsVertexArray(vaos[0]), GL_TRUE); + EXPECT_EQ(IsVertexArray(vaos[1]), GL_TRUE); + EXPECT_EQ(MG_State::pGLContext->GetBoundVertexArray(), boundObj); + + DeleteVertexArrays(2, vaos); + DeleteVertexArrays(1, &bound); + EXPECT_EQ(GetError(), GL_NO_ERROR); +} + +TEST_F(GeneralVertexArrayTest, General_CreateVertexArraysRejectsNegativeCount) { + GLuint vao = 0; + CreateVertexArrays(-1, &vao); + EXPECT_EQ(GetError(), GL_INVALID_VALUE); +} + +TEST_F(GeneralVertexArrayTest, General_DirectStateAccessConfiguresNamedVAOWithoutChangingBinding) { + GLuint bound = CreateVAO(); + auto boundObj = MG_State::pGLContext->GetBoundVertexArray(); + ASSERT_NE(boundObj, nullptr); + + GLuint vao = 0; + CreateVertexArrays(1, &vao); + + GLuint vertexBuffer = 0; + GLuint indexBuffer = 0; + CreateBuffers(1, &vertexBuffer); + CreateBuffers(1, &indexBuffer); + NamedBufferData(vertexBuffer, 256, nullptr, GL_STATIC_DRAW); + NamedBufferData(indexBuffer, 128, nullptr, GL_STATIC_DRAW); + + VertexArrayVertexBuffer(vao, 2, vertexBuffer, 16, 24); + VertexArrayAttribFormat(vao, 2, 3, GL_FLOAT, GL_TRUE, 12); + EnableVertexArrayAttrib(vao, 2); + VertexArrayElementBuffer(vao, indexBuffer); + + auto vaoObj = MG_State::pGLContext->GetVertexArrayObject(vao); + ASSERT_NE(vaoObj, nullptr); + + const auto& attr = vaoObj->GetAttribute(2); + EXPECT_TRUE(attr.Enabled); + EXPECT_EQ(attr.Size, 3); + EXPECT_EQ(attr.Type, DataType::Float32); + EXPECT_TRUE(attr.Normalized); + EXPECT_FALSE(attr.IsInteger); + EXPECT_EQ(attr.Stride, 24); + EXPECT_EQ(attr.Offset, 12); + EXPECT_EQ(attr.Buffer, MG_State::pGLContext->GetBufferObject(vertexBuffer)); + EXPECT_EQ(vaoObj->GetIndexBufferBindingSlot().GetBoundObject(), MG_State::pGLContext->GetBufferObject(indexBuffer)); + EXPECT_EQ(MG_State::pGLContext->GetBoundVertexArray(), boundObj); + + DeleteVertexArrays(1, &vao); + DeleteVertexArrays(1, &bound); + GLuint buffers[] = {vertexBuffer, indexBuffer}; + DeleteBuffers(2, buffers); + EXPECT_EQ(GetError(), GL_NO_ERROR); +} + +TEST_F(GeneralVertexArrayTest, General_DirectStateAccessIntegerAttribAndUnbindElementBuffer) { + GLuint vao = 0; + GLuint indexBuffer = 0; + CreateVertexArrays(1, &vao); + CreateBuffers(1, &indexBuffer); + + VertexArrayAttribIFormat(vao, 1, 4, GL_UNSIGNED_INT, 8); + EnableVertexArrayAttrib(vao, 1); + VertexArrayElementBuffer(vao, indexBuffer); + VertexArrayElementBuffer(vao, 0); + + auto vaoObj = MG_State::pGLContext->GetVertexArrayObject(vao); + ASSERT_NE(vaoObj, nullptr); + + const auto& attr = vaoObj->GetAttribute(1); + EXPECT_TRUE(attr.Enabled); + EXPECT_EQ(attr.Size, 4); + EXPECT_EQ(attr.Type, DataType::Uint32); + EXPECT_TRUE(attr.IsInteger); + EXPECT_FALSE(attr.Normalized); + EXPECT_EQ(attr.Offset, 8); + EXPECT_EQ(vaoObj->GetIndexBufferBindingSlot().GetBoundObject(), nullptr); + + DeleteVertexArrays(1, &vao); + DeleteBuffers(1, &indexBuffer); + EXPECT_EQ(GetError(), GL_NO_ERROR); +} + TEST_F(GeneralVertexArrayTest, General_VertexAttributeConfiguration) { GLuint vao = CreateVAO(); GLuint vbo = CreateVBO(GL_ARRAY_BUFFER, 128); diff --git a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp index e1df0d2b..a5271759 100644 --- a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp @@ -83,6 +83,7 @@ namespace MobileGL::MG_Util::BackendLoader { caps.DeviceName = p.deviceName; caps.DriverVersionString = DecodeDriverVersion(p.driverVersion); caps.UniformBufferOffsetAlignment = static_cast(p.limits.minUniformBufferOffsetAlignment); + caps.MaxShaderStorageBlockSize = static_cast(p.limits.maxStorageBufferRange); return true; } @@ -93,5 +94,6 @@ namespace MobileGL::MG_Util::BackendLoader { caps.DeviceName = properties.deviceName; caps.DriverVersionString = DecodeDriverVersion(properties.driverVersion); caps.UniformBufferOffsetAlignment = static_cast(properties.limits.minUniformBufferOffsetAlignment); + caps.MaxShaderStorageBlockSize = static_cast(properties.limits.maxStorageBufferRange); } } // namespace MobileGL::MG_Util::BackendLoader diff --git a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h index efd85e1d..54c360a7 100644 --- a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h @@ -16,6 +16,7 @@ namespace MobileGL { String DeviceName; String DriverVersionString; Int UniformBufferOffsetAlignment = 256; + SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024; }; } // namespace MG_External namespace MG_Util::BackendLoader { diff --git a/MobileGL/MG_Util/Converters/GLToMG/BufferEnumConverter.cpp b/MobileGL/MG_Util/Converters/GLToMG/BufferEnumConverter.cpp index 7c8e305c..44d279f3 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/BufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/GLToMG/BufferEnumConverter.cpp @@ -38,6 +38,8 @@ namespace MobileGL { return BufferTarget::DispatchIndirect; case GL_DRAW_INDIRECT_BUFFER: return BufferTarget::DrawIndirect; + case GL_PARAMETER_BUFFER_ARB: + return BufferTarget::Parameter; case GL_SHADER_STORAGE_BUFFER: return BufferTarget::ShaderStorage; case GL_UNKNOWN_MGL: @@ -84,4 +86,4 @@ namespace MobileGL { return result; } } // namespace MG_Util -} // namespace MobileGL \ No newline at end of file +} // namespace MobileGL diff --git a/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp b/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp index 46c104ab..ea5b82cc 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp @@ -148,6 +148,17 @@ namespace MobileGL { } } + ProvokingVertexMode ConvertGLEnumToProvokingVertexMode(GLenum v) { + switch (v) { + case GL_FIRST_VERTEX_CONVENTION: + return ProvokingVertexMode::FirstVertex; + case GL_LAST_VERTEX_CONVENTION: + return ProvokingVertexMode::LastVertex; + default: + return ProvokingVertexMode::Unknown; + } + } + CapabilityInput ConvertGLEnumToCapabilityInput(GLenum v) { switch (v) { case GL_BLEND: diff --git a/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.h b/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.h index 21794a22..1fa7ce33 100644 --- a/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.h +++ b/MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.h @@ -18,6 +18,7 @@ namespace MobileGL { PixelStoreParam ConvertGLEnumToPixelStoreParam(GLenum value); CullFaceMode ConvertGLEnumToCullFaceMode(GLenum value); FrontFaceMode ConvertGLEnumToFrontFaceMode(GLenum value); + ProvokingVertexMode ConvertGLEnumToProvokingVertexMode(GLenum value); CapabilityInput ConvertGLEnumToCapabilityInput(GLenum value); } // namespace MG_Util } // namespace MobileGL diff --git a/MobileGL/MG_Util/Converters/MGToGL/BufferEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToGL/BufferEnumConverter.cpp index a86278b1..14c87c88 100644 --- a/MobileGL/MG_Util/Converters/MGToGL/BufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToGL/BufferEnumConverter.cpp @@ -38,6 +38,8 @@ namespace MobileGL { return GL_DISPATCH_INDIRECT_BUFFER; case BufferTarget::DrawIndirect: return GL_DRAW_INDIRECT_BUFFER; + case BufferTarget::Parameter: + return GL_PARAMETER_BUFFER_ARB; case BufferTarget::ShaderStorage: return GL_SHADER_STORAGE_BUFFER; case BufferTarget::Unknown: @@ -84,4 +86,4 @@ namespace MobileGL { return result; } } // namespace MG_Util -} // namespace MobileGL \ No newline at end of file +} // namespace MobileGL diff --git a/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp index 19fa100f..6a0f1f28 100644 --- a/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.cpp @@ -149,6 +149,17 @@ namespace MobileGL { } } + GLenum ConvertProvokingVertexModeToGLEnum(ProvokingVertexMode v) { + switch (v) { + case ProvokingVertexMode::FirstVertex: + return GL_FIRST_VERTEX_CONVENTION; + case ProvokingVertexMode::LastVertex: + return GL_LAST_VERTEX_CONVENTION; + default: + return GL_UNKNOWN_MGL; + } + } + GLenum ConvertCapabilityInputToGLEnum(CapabilityInput v) { switch (v) { case CapabilityInput::Blend: diff --git a/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.h b/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.h index a857d438..7273871d 100644 --- a/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.h +++ b/MobileGL/MG_Util/Converters/MGToGL/RenderStateEnumConverter.h @@ -18,6 +18,7 @@ namespace MobileGL { GLenum ConvertPixelStoreParamToGLEnum(PixelStoreParam value); GLenum ConvertCullFaceModeToGLEnum(CullFaceMode value); GLenum ConvertFrontFaceModeToGLEnum(FrontFaceMode value); + GLenum ConvertProvokingVertexModeToGLEnum(ProvokingVertexMode value); GLenum ConvertCapabilityInputToGLEnum(CapabilityInput value); } // namespace MG_Util } // namespace MobileGL diff --git a/MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp index 04314e9e..ddeb797d 100644 --- a/MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp @@ -38,6 +38,8 @@ namespace MobileGL { return "DispatchIndirect"; case BufferTarget::DrawIndirect: return "DrawIndirect"; + case BufferTarget::Parameter: + return "Parameter"; case BufferTarget::ShaderStorage: return "ShaderStorage"; case BufferTarget::Unknown: @@ -87,4 +89,4 @@ namespace MobileGL { return result.empty() ? "[]" : result; } } // namespace MG_Util -} // namespace MobileGL \ No newline at end of file +} // namespace MobileGL diff --git a/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp b/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp index d2b3c3e4..f83511e0 100644 --- a/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp +++ b/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.cpp @@ -131,6 +131,17 @@ namespace MobileGL { } } + String ConvertProvokingVertexModeToString(ProvokingVertexMode v) { + switch (v) { + case ProvokingVertexMode::FirstVertex: + return "FirstVertex"; + case ProvokingVertexMode::LastVertex: + return "LastVertex"; + default: + return "Unknown"; + } + } + String ConvertCapabilityInputToString(CapabilityInput v) { switch (v) { case CapabilityInput::Blend: diff --git a/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.h b/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.h index 1831d86b..5d66b73a 100644 --- a/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.h +++ b/MobileGL/MG_Util/Converters/MGToStr/RenderStateEnumConverter.h @@ -17,6 +17,7 @@ namespace MobileGL { String ConvertPixelStoreParamToString(PixelStoreParam value); String ConvertCullFaceModeToString(CullFaceMode value); String ConvertFrontFaceModeToString(FrontFaceMode value); + String ConvertProvokingVertexModeToString(ProvokingVertexMode value); String ConvertCapabilityInputToString(CapabilityInput value); } // namespace MG_Util } // namespace MobileGL diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp b/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp index e0893638..4e13031b 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp @@ -190,6 +190,18 @@ namespace { } } } + + void InjectDepthRangeBuiltinShim(MobileGL::ShaderStage stage, MobileGL::String& source) { + if (stage != MobileGL::ShaderStage::Fragment) return; + if (source.find("gl_DepthRange") == MobileGL::String::npos) return; + if (source.find("mg_DepthRangeParameters") != MobileGL::String::npos) return; + + constexpr const char* shim = + "struct mg_DepthRangeParameters { float near; float far; float diff; };\n" + "const mg_DepthRangeParameters mg_DepthRange = mg_DepthRangeParameters(0.0, 1.0, 1.0);\n" + "#define gl_DepthRange mg_DepthRange\n"; + source.insert(FindAfterVersionDirective(source), shim); + } } // namespace namespace MobileGL { @@ -272,6 +284,7 @@ namespace MobileGL { RenameBuiltinShadowingFunction(source, "tanh", "mg_tanh"); RenameBuiltinShadowingFunction(source, "fma", "mg_fma"); ModernizeLegacyGLSL(stage, source); + InjectDepthRangeBuiltinShim(stage, source); } } // namespace ShaderTranspiler diff --git a/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp b/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp index 4d8eea60..16fa920f 100644 --- a/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp +++ b/MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp @@ -178,6 +178,7 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { break; // Depth Stencil + case GL_DEPTH24_STENCIL8: case GL_DEPTH32F_STENCIL8: case GL_DEPTH_STENCIL: *outFormat = GL_DEPTH_STENCIL; @@ -332,9 +333,12 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { // Depth Stencil case GL_DEPTH32F_STENCIL8: - case GL_DEPTH_STENCIL: *outType = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; break; + case GL_DEPTH24_STENCIL8: + case GL_DEPTH_STENCIL: + *outType = GL_UNSIGNED_INT_24_8; + break; default: MGLOG_E("NormalizePixelFormat: outType: unhandled internalFormat: %s", @@ -345,4 +349,4 @@ namespace MobileGL::MG_Util::TextureFormatProcessor { } } } -} // namespace MobileGL::MG_Util::TextureFormatProcessor \ No newline at end of file +} // namespace MobileGL::MG_Util::TextureFormatProcessor From dd52f0381a0d6f974b987d494cdd44faf6e2a7fb Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 9 Jun 2026 04:11:03 +0800 Subject: [PATCH 4/5] [Fix] (MG_Backend/DirectVulkan): fix Voxy subgroup and indirect draw sync - Implement Vulkan subgroup capability querying and expose KHR subgroup getter values. - Fix DirectVulkan memory barriers so GL_COMMAND_BARRIER_BIT makes generated indirect draw commands visible. - Keep Voxy on the DirectVulkan gpu_shader_int64 quad decode path while filtering unsupported optional int64 usage on backends that do not advertise it. - Add MG_Test coverage for subgroup getters, Voxy subgroup/int64 shader probes, command barrier mapping, and indirect draw command layout. - Check for whether driver supports shader subgroup operation, disable on demand, and provide env var `MOBILEGL_DISABLE_SUBGROUP` to explicitly disable subgroup features --- MobileGL/MG_Backend/BackendObject.h | 4 + .../BackendObject_DirectVulkan.cpp | 121 ++++++++++--- .../DirectVulkan/BackendObject_DirectVulkan.h | 4 + .../DirectVulkan/Renderer/VulkanRenderer.cpp | 33 ++-- .../DirectVulkan/Renderer/VulkanRenderer.h | 2 + MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp | 41 +++-- .../MG_Impl/GLImpl/Texture/GL_Texture.cpp | 2 +- MobileGL/MG_Test/Program/ProgramTest.cpp | 76 ++++++++ MobileGL/MG_Test/SanityTest.cpp | 166 ++++++++++++++++++ MobileGL/MG_Test/Texture/TextureTest.cpp | 33 ++++ .../MG_Util/BackendLoaders/Vulkan/Loader.cpp | 48 +++++ .../MG_Util/BackendLoaders/Vulkan/Loader.h | 5 + .../ShaderSourceProcessor.cpp | 104 +++++++++++ 13 files changed, 595 insertions(+), 44 deletions(-) diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index 75841ef3..265cafcf 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -113,6 +113,10 @@ namespace MobileGL { struct DynamicBackendParameters { SizeT UniformBufferOffsetAlignment = 256; SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024; + Uint32 SubgroupSize = 0; + Uint32 SubgroupSupportedStages = 0; + Uint32 SubgroupSupportedFeatures = 0; + Bool SubgroupQuadOperationsInAllStages = false; }; enum class WindowBackend { diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index aad30621..ff59df3a 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -19,6 +19,27 @@ namespace MobileGL::MG_Backend::DirectVulkan { BackendObject_DirectVulkan::~BackendObject_DirectVulkan() = default; + BackendObject_DirectVulkan::BackendObject_DirectVulkan(): + m_rendererInfo{ + .RendererName = "Magma", + .BackendName = "Direct (Vulkan)", + .ExtraVendor = Nullopt, + .RendererGLInfo = + { + .TargetGLVersion = {3, 3, 0}, + .TargetGLSLVersion = {4, 6, 0}, + .Extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32, + 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_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_texture_storage, E_GL_ARB_direct_state_access, + E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader_int64}, + .IsCompatibilityProfile = false + }, + .StaticBackendCapability = {.AllowVSOnlyPrograms = false}} {} + Bool BackendObject_DirectVulkan::InitWindowSurface() { if (!m_windowHandle.Handle) { MGLOG_E("Cannot initialize DirectVulkan window surface: native window handle is null"); @@ -47,8 +68,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { return false; } - MG_Util::BackendLoader::FillInVulkanCapabilities(m_vulkanCaps, pVulkanRenderer->GetPhysicalDevice().properties); + const auto& physicalDevice = pVulkanRenderer->GetPhysicalDevice(); + if (!MG_Util::BackendLoader::QueryVulkanCapabilities(m_vulkanCaps, pVulkanRenderer->GetInstance(), + physicalDevice.handle)) { + MGLOG_W("DirectVulkan: failed to query extended Vulkan capabilities, using basic properties"); + MG_Util::BackendLoader::FillInVulkanCapabilities(m_vulkanCaps, physicalDevice.properties); + } UpdateDynamicBackendParameters(); + UpdateAdvertisedExtensions(); return true; } @@ -113,27 +140,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { } const RendererInfo& BackendObject_DirectVulkan::GetRendererInfo() const { - static RendererInfo RendererInfo = { - .RendererName = "Magma", // Renderer Name - .BackendName = "Direct (Vulkan)", // Backend Name - .ExtraVendor = Nullopt, // Extra vendor - .RendererGLInfo = - { - .TargetGLVersion = {3, 3, 0}, // Target OpenGL Version - .TargetGLSLVersion = {4, 6, 0}, // Target Shading Language Version - .Extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32, // OpenGL Extensions - 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_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_texture_storage, E_GL_ARB_direct_state_access, - E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader_int64}, - .IsCompatibilityProfile = false // Is Compatibility Profile - }, - .StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability - }; - return RendererInfo; + return m_rendererInfo; } String BackendObject_DirectVulkan::GetBackendAPIVersionString() const { @@ -214,11 +221,81 @@ namespace MobileGL::MG_Backend::DirectVulkan { return m_dynamicParameters; } + void BackendObject_DirectVulkan::ApplyVulkanCapabilitiesForTesting( + const MG_External::VulkanCapabilities& capabilities) { + m_vulkanCaps = capabilities; + UpdateDynamicBackendParameters(); + UpdateAdvertisedExtensions(); + } + + void BackendObject_DirectVulkan::UpdateAdvertisedExtensions() { + auto& extensions = m_rendererInfo.RendererGLInfo.Extensions; + extensions.erase(std::remove(extensions.begin(), extensions.end(), E_GL_KHR_shader_subgroup), + extensions.end()); + + if (m_vulkanCaps.SupportsShaderSubgroup) { + extensions.push_back(E_GL_KHR_shader_subgroup); + } + } + void BackendObject_DirectVulkan::UpdateDynamicBackendParameters() { + const auto mapShaderStages = [](Uint32 vkStages) { + Uint32 glStages = 0; + if ((vkStages & VK_SHADER_STAGE_VERTEX_BIT) != 0) glStages |= GL_VERTEX_SHADER_BIT; + if ((vkStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) != 0) glStages |= GL_TESS_CONTROL_SHADER_BIT; + if ((vkStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) != 0) { + glStages |= GL_TESS_EVALUATION_SHADER_BIT; + } + if ((vkStages & VK_SHADER_STAGE_GEOMETRY_BIT) != 0) glStages |= GL_GEOMETRY_SHADER_BIT; + if ((vkStages & VK_SHADER_STAGE_FRAGMENT_BIT) != 0) glStages |= GL_FRAGMENT_SHADER_BIT; + if ((vkStages & VK_SHADER_STAGE_COMPUTE_BIT) != 0) glStages |= GL_COMPUTE_SHADER_BIT; + return glStages; + }; + + const auto mapSubgroupFeatures = [](Uint32 vkFeatures) { + Uint32 glFeatures = 0; + if ((vkFeatures & VK_SUBGROUP_FEATURE_BASIC_BIT) != 0) { + glFeatures |= GL_SUBGROUP_FEATURE_BASIC_BIT_KHR; + } + if ((vkFeatures & VK_SUBGROUP_FEATURE_VOTE_BIT) != 0) { + glFeatures |= GL_SUBGROUP_FEATURE_VOTE_BIT_KHR; + } + if ((vkFeatures & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) != 0) { + glFeatures |= GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR; + } + if ((vkFeatures & VK_SUBGROUP_FEATURE_BALLOT_BIT) != 0) { + glFeatures |= GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR; + } + if ((vkFeatures & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) != 0) { + glFeatures |= GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR; + } + if ((vkFeatures & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) != 0) { + glFeatures |= GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR; + } + if ((vkFeatures & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) != 0) { + glFeatures |= GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR; + } + if ((vkFeatures & VK_SUBGROUP_FEATURE_QUAD_BIT) != 0) { + glFeatures |= GL_SUBGROUP_FEATURE_QUAD_BIT_KHR; + } + return glFeatures; + }; + static constexpr SizeT kMaxAdvertisedShaderStorageBlockSize = 512ull * 1024ull * 1024ull; m_dynamicParameters.UniformBufferOffsetAlignment = m_vulkanCaps.UniformBufferOffsetAlignment; m_dynamicParameters.MaxShaderStorageBlockSize = std::min(m_vulkanCaps.MaxShaderStorageBlockSize, kMaxAdvertisedShaderStorageBlockSize); + if (m_vulkanCaps.SupportsShaderSubgroup) { + m_dynamicParameters.SubgroupSize = m_vulkanCaps.SubgroupSize; + m_dynamicParameters.SubgroupSupportedStages = mapShaderStages(m_vulkanCaps.SubgroupSupportedStages); + m_dynamicParameters.SubgroupSupportedFeatures = mapSubgroupFeatures(m_vulkanCaps.SubgroupSupportedOperations); + m_dynamicParameters.SubgroupQuadOperationsInAllStages = m_vulkanCaps.SubgroupQuadOperationsInAllStages; + } else { + m_dynamicParameters.SubgroupSize = 0; + m_dynamicParameters.SubgroupSupportedStages = 0; + m_dynamicParameters.SubgroupSupportedFeatures = 0; + m_dynamicParameters.SubgroupQuadOperationsInAllStages = false; + } if (m_dynamicParameters.MaxShaderStorageBlockSize != m_vulkanCaps.MaxShaderStorageBlockSize) { MGLOG_I("DirectVulkan: clamped GL_MAX_SHADER_STORAGE_BLOCK_SIZE from %zu to %zu", m_vulkanCaps.MaxShaderStorageBlockSize, diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h index 74e045fa..7058cb1a 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h @@ -14,6 +14,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { class BackendObject_DirectVulkan : public BackendObject { public: + BackendObject_DirectVulkan(); ~BackendObject_DirectVulkan() override; void Initialize() override; @@ -30,12 +31,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { const GlobalBackendFunctionsTable& GetBackendFunctions() const override; const DynamicBackendParameters& GetDynamicParameters() const override; BackendType GetBackendType() const override; + void ApplyVulkanCapabilitiesForTesting(const MG_External::VulkanCapabilities& capabilities); private: + void UpdateAdvertisedExtensions(); void UpdateDynamicBackendParameters(); Bool m_initialized = false; DynamicBackendParameters m_dynamicParameters; MG_External::VulkanCapabilities m_vulkanCaps; + RendererInfo m_rendererInfo; }; } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index a4614dd7..276730b8 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -2920,16 +2920,7 @@ void main() { vkCmdDispatchIndirect(frame.commandBuffer, slice.buffer, slice.offset + static_cast(indirect)); } - void VulkanRenderer::MemoryBarrier(GLbitfield barriers) { - auto& frame = m_frameContext.GetCurrent(); - if (!frame.isCommandRecording) { - m_frameContext.BeginCommandRecording(); - m_uniformManager->BeginFrame(m_frameContext.GetCurrentFrameIndex()); - } - if (VkRenderPassManager::GetActiveRenderPass() != nullptr) { - VkRenderPassManager::EndRenderPass(frame.commandBuffer); - } - + VkMemoryBarrier VulkanRenderer::BuildMemoryBarrierForGlBarriers(GLbitfield barriers) { VkMemoryBarrier memoryBarrier{}; memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; memoryBarrier.srcAccessMask = @@ -2945,6 +2936,24 @@ void main() { VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_UNIFORM_READ_BIT | VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; + if ((barriers & GL_COMMAND_BARRIER_BIT) != 0) { + memoryBarrier.dstAccessMask |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + } + return memoryBarrier; + } + + void VulkanRenderer::MemoryBarrier(GLbitfield barriers) { + auto& frame = m_frameContext.GetCurrent(); + if (!frame.isCommandRecording) { + m_frameContext.BeginCommandRecording(); + m_uniformManager->BeginFrame(m_frameContext.GetCurrentFrameIndex()); + } + if (VkRenderPassManager::GetActiveRenderPass() != nullptr) { + VkRenderPassManager::EndRenderPass(frame.commandBuffer); + } + + VkMemoryBarrier memoryBarrier = BuildMemoryBarrierForGlBarriers(barriers); + MGLOG_D("DirectVulkan: glMemoryBarrier(0x%x)", static_cast(barriers)); vkCmdPipelineBarrier(frame.commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, @@ -5331,6 +5340,10 @@ void main() { return m_physicalDevice; } + VkInstance VulkanRenderer::GetInstance() const { + return m_instance; + } + Bool VulkanRenderer::IsDrawIndirectCountExtensionEnabled() const { return m_drawIndirectCountExtensionEnabled; } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index d50e0dab..4b80756e 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -143,6 +143,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ); void DispatchComputeIndirect(GLintptr indirect); void MemoryBarrier(GLbitfield barriers); + static VkMemoryBarrier BuildMemoryBarrierForGlBarriers(GLbitfield barriers); void DrawArrays(const DrawCmd& payload); void DrawElements(const DrawIndexedCmd& payload); void MultiDrawElements(const MultiDrawIndexedCmd& payloads); @@ -151,6 +152,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void Present(); const PhysicalDevice& GetPhysicalDevice() const; + VkInstance GetInstance() const; Bool IsDrawIndirectCountExtensionEnabled() const; void RecreateSwapchain(); diff --git a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp index e23af76d..37d1c753 100644 --- a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp +++ b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp @@ -76,12 +76,12 @@ namespace MobileGL::MG_Impl::GLImpl { MGLOG_D("shadingLanguageVersion: %s", shadingLanguageVersion.c_str()); return (const GLubyte*)shadingLanguageVersion.c_str(); case GL_EXTENSIONS: - if (extensionsString.empty()) { - for (auto& ext : rendererInfo.RendererGLInfo.Extensions) { - extensionsString += MG_Util::ConvertGLExtToString(ext); + extensionsString.clear(); + for (auto& ext : rendererInfo.RendererGLInfo.Extensions) { + if (!extensionsString.empty()) { extensionsString += " "; } - extensionsString.pop_back(); + extensionsString += MG_Util::ConvertGLExtToString(ext); } return (const GLubyte*)extensionsString.c_str(); default: @@ -108,13 +108,10 @@ namespace MobileGL::MG_Impl::GLImpl { } static Vector extStrings; - static Bool initialized = false; - if (!initialized) { - extStrings.reserve(exts.size()); - for (const auto& ext : exts) { - extStrings.emplace_back(MG_Util::ConvertGLExtToString(ext)); - } - initialized = true; + extStrings.clear(); + extStrings.reserve(exts.size()); + for (const auto& ext : exts) { + extStrings.emplace_back(MG_Util::ConvertGLExtToString(ext)); } return (const GLubyte*)extStrings[index].c_str(); @@ -174,6 +171,15 @@ namespace MobileGL::MG_Impl::GLImpl { *data = static_cast(MG_Backend::DynamicBackendParameters{}.MaxShaderStorageBlockSize); } return; + case GL_SUBGROUP_SIZE_KHR: + case GL_SUBGROUP_SUPPORTED_STAGES_KHR: + case GL_SUBGROUP_SUPPORTED_FEATURES_KHR: + case GL_SUBGROUP_QUAD_ALL_STAGES_KHR: { + GLint params = 0; + GetIntegerv(pname, ¶ms); + *data = static_cast(params); + return; + } default: *data = 0; MG_State::pGLContext->RecordError( @@ -200,6 +206,7 @@ namespace MobileGL::MG_Impl::GLImpl { return; } const auto& rendererInfo = activeBackendObject->GetRendererInfo(); + const auto& dynamicParameters = activeBackendObject->GetDynamicParameters(); switch (pname) { case GL_ACTIVE_TEXTURE: @@ -277,6 +284,18 @@ namespace MobileGL::MG_Impl::GLImpl { case GL_COMPRESSED_TEXTURE_FORMATS: *params = 0; // TODO break; + case GL_SUBGROUP_SIZE_KHR: + *params = static_cast(dynamicParameters.SubgroupSize); + break; + case GL_SUBGROUP_SUPPORTED_STAGES_KHR: + *params = static_cast(dynamicParameters.SubgroupSupportedStages); + break; + case GL_SUBGROUP_SUPPORTED_FEATURES_KHR: + *params = static_cast(dynamicParameters.SubgroupSupportedFeatures); + break; + case GL_SUBGROUP_QUAD_ALL_STAGES_KHR: + *params = dynamicParameters.SubgroupQuadOperationsInAllStages ? GL_TRUE : GL_FALSE; + break; case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS: *params = 16; // TODO: use backend value break; diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 9fbd2c63..831a942f 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -365,7 +365,7 @@ namespace MobileGL::MG_Impl::GLImpl { const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureInternalFormat, texturePixelDataType); const SizeT srcRowSize = static_cast(width) * internalBpp; - const SizeT srcStride = (srcRowSize + unpackParams.Alignment - 1) & ~(unpackParams.Alignment - 1); + const SizeT srcStride = srcRowSize; const SizeT destRowSize = static_cast(texelSize.x()) * internalBpp; if (xoffset + width > static_cast(texelSize.x()) || diff --git a/MobileGL/MG_Test/Program/ProgramTest.cpp b/MobileGL/MG_Test/Program/ProgramTest.cpp index c074c7e3..37c24df6 100644 --- a/MobileGL/MG_Test/Program/ProgramTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramTest.cpp @@ -13,6 +13,8 @@ #include "Includes.h" #include "Init.h" #include "MG_Backend/DirectVulkan/DirectVulkanResourceState.h" +#include "MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h" +#include "MG_Backend/BackendObjects.h" #include "MG_Impl/GLImpl/Getter/GL_Getter.h" #include "MG_Impl/GLImpl/Program/GL_Program.h" #include "MG_State/GLState/Core.h" @@ -118,6 +120,80 @@ TEST_F(ProgramTest, CompileFragment) { CompileShader(fs); } +TEST_F(ProgramTest, CompileVoxySubgroupProbeShader) { + char infoLog[1024] = ""; + const char* csSrc = R"(#version 430 +#extension GL_KHR_shader_subgroup_basic : require +#extension GL_KHR_shader_subgroup_arithmetic : require +layout(local_size_x=32) in; +void main() { + uint a = subgroupExclusiveAdd(gl_LocalInvocationIndex); +} +)"; + + GLuint cs = CreateShader(GL_COMPUTE_SHADER); + ShaderSource(cs, 1, &csSrc, nullptr); + CompileShader(cs); + + GLint compileStatus = GL_FALSE; + GetShaderiv(cs, GL_COMPILE_STATUS, &compileStatus); + GetShaderInfoLog(cs, sizeof(infoLog), nullptr, infoLog); + EXPECT_EQ(compileStatus, GL_TRUE) << infoLog; +} + +TEST_F(ProgramTest, CompileVoxyGpuShaderInt64QuadDecode) { + auto previousBackend = Move(MG_Backend::pActiveBackendObject); + MG_Backend::pActiveBackendObject = MakeUnique(); + + char infoLog[2048] = ""; + const char* vsSrc = R"(#version 460 core +#extension GL_ARB_gpu_shader_int64 : enable + +#ifdef GL_ARB_gpu_shader_int64 +#define Quad uint64_t +#define Eu32(data, amountBits, shift) (uint((data)>>(shift))&((1u<<(amountBits))-1)) + +vec3 extractPos(uint64_t quad) { + return vec3(Eu32(quad, 5, 21), Eu32(quad, 5, 16), Eu32(quad, 5, 11)); +} + +uint extractStateId(uint64_t quad) { + return Eu32(quad, 16, 26); +} + +uint extractBiomeId(uint64_t quad) { + return Eu32(quad, 9, 46); +} +#else +#error GL_ARB_gpu_shader_int64 should select Voxy native quad decode path +#endif + +layout(std430, binding = 1) readonly buffer QuadBuffer { + Quad quadData[]; +}; + +layout(location = 0) flat out uvec4 interData; + +void main() { + uint64_t quad = quadData[uint(gl_VertexID) >> 2]; + vec3 pos = extractPos(quad); + interData = uvec4(extractStateId(quad), extractBiomeId(quad), uint(pos.x), uint(pos.y)); + gl_Position = vec4(pos * (1.0 / 32.0), 1.0); +} +)"; + + GLuint vs = CreateShader(GL_VERTEX_SHADER); + ShaderSource(vs, 1, &vsSrc, nullptr); + CompileShader(vs); + + GLint compileStatus = GL_FALSE; + GetShaderiv(vs, GL_COMPILE_STATUS, &compileStatus); + GetShaderInfoLog(vs, sizeof(infoLog), nullptr, infoLog); + EXPECT_EQ(compileStatus, GL_TRUE) << infoLog; + + MG_Backend::pActiveBackendObject = Move(previousBackend); +} + TEST_F(ProgramTest, ShaderSourceKeepsOriginalTextAfterCompile) { const char* part0 = R"(#define HIGHP_OR_DEFAULT highp attribute vec4 Position; diff --git a/MobileGL/MG_Test/SanityTest.cpp b/MobileGL/MG_Test/SanityTest.cpp index 88a5c5d1..5825444a 100644 --- a/MobileGL/MG_Test/SanityTest.cpp +++ b/MobileGL/MG_Test/SanityTest.cpp @@ -21,9 +21,44 @@ #include #include #include +#include +#include +#include #include namespace { + class DynamicParameterBackend final : public MobileGL::MG_Backend::BackendObject { + public: + explicit DynamicParameterBackend(MobileGL::MG_Backend::DynamicBackendParameters params): + m_params(params) {} + + void Initialize() override {} + MobileGL::Bool InitCapabilities() override { return true; } + MobileGL::Bool InitWindowSurface() override { return true; } + const MobileGL::RendererInfo& GetRendererInfo() const override { return m_info; } + MobileGL::String GetBackendAPIVersionString() const override { return "test"; } + const MobileGL::MG_Backend::GlobalBackendFunctionsTable& GetBackendFunctions() const override { + return m_functions; + } + const MobileGL::MG_Backend::DynamicBackendParameters& GetDynamicParameters() const override { + return m_params; + } + MobileGL::BackendType GetBackendType() const override { return MobileGL::BackendType::Unknown; } + + private: + MobileGL::MG_Backend::DynamicBackendParameters m_params; + MobileGL::MG_Backend::GlobalBackendFunctionsTable m_functions{}; + MobileGL::RendererInfo m_info{ + .RendererName = "Test", + .BackendName = "DynamicParameterBackend", + .ExtraVendor = MobileGL::Nullopt, + .RendererGLInfo = {.TargetGLVersion = {3, 3, 0}, + .TargetGLSLVersion = {4, 6, 0}, + .Extensions = {}, + .IsCompatibilityProfile = false}, + .StaticBackendCapability = {.AllowVSOnlyPrograms = false}}; + }; + void SetEnvVar(const char* name, const char* value) { #if defined(_WIN32) _putenv_s(name, value); @@ -87,6 +122,137 @@ TEST(DirectVulkanSanity, AdvertisesVoxyRequiredRenderingExtensionsWithoutRaising extensions.end()); } +TEST(DirectVulkanSanity, AdvertisesSubgroupOnlyWhenVulkanReportsUsableSupport) { + using namespace MobileGL; + + MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend; + + MG_External::VulkanCapabilities unsupportedCaps; + unsupportedCaps.SupportsShaderSubgroup = false; + unsupportedCaps.SubgroupSize = 32; + unsupportedCaps.SubgroupSupportedStages = VK_SHADER_STAGE_COMPUTE_BIT; + unsupportedCaps.SubgroupSupportedOperations = VK_SUBGROUP_FEATURE_BASIC_BIT; + backend.ApplyVulkanCapabilitiesForTesting(unsupportedCaps); + + const auto& unsupportedExtensions = backend.GetRendererInfo().RendererGLInfo.Extensions; + EXPECT_EQ(std::find(unsupportedExtensions.begin(), unsupportedExtensions.end(), E_GL_KHR_shader_subgroup), + unsupportedExtensions.end()); + EXPECT_EQ(backend.GetDynamicParameters().SubgroupSize, 0u); + EXPECT_EQ(backend.GetDynamicParameters().SubgroupSupportedStages, 0u); + EXPECT_EQ(backend.GetDynamicParameters().SubgroupSupportedFeatures, 0u); + + MG_External::VulkanCapabilities supportedCaps; + supportedCaps.SupportsShaderSubgroup = true; + supportedCaps.SubgroupSize = 32; + supportedCaps.SubgroupSupportedStages = VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_COMPUTE_BIT; + supportedCaps.SubgroupSupportedOperations = + VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_ARITHMETIC_BIT | VK_SUBGROUP_FEATURE_QUAD_BIT; + supportedCaps.SubgroupQuadOperationsInAllStages = true; + backend.ApplyVulkanCapabilitiesForTesting(supportedCaps); + + const auto& supportedExtensions = backend.GetRendererInfo().RendererGLInfo.Extensions; + EXPECT_NE(std::find(supportedExtensions.begin(), supportedExtensions.end(), E_GL_KHR_shader_subgroup), + supportedExtensions.end()); + EXPECT_EQ(backend.GetDynamicParameters().SubgroupSize, 32u); + EXPECT_EQ(backend.GetDynamicParameters().SubgroupSupportedStages, + static_cast(GL_FRAGMENT_SHADER_BIT | GL_COMPUTE_SHADER_BIT)); + EXPECT_EQ(backend.GetDynamicParameters().SubgroupSupportedFeatures, + static_cast(GL_SUBGROUP_FEATURE_BASIC_BIT_KHR | + GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR | + GL_SUBGROUP_FEATURE_QUAD_BIT_KHR)); + EXPECT_TRUE(backend.GetDynamicParameters().SubgroupQuadOperationsInAllStages); +} + +TEST(DirectVulkanSanity, KeepsOptionalGpuShaderInt64BranchForVoxyQuadDecode) { + using namespace MobileGL; + + MG_Backend::pActiveBackendObject = MakeUnique(); + String source = R"(#version 460 core +#extension GL_ARB_gpu_shader_int64 : enable +#ifdef GL_ARB_gpu_shader_int64 +uint getLowBits(uint64_t v) { + return uint(v & uint64_t(0xffu)); +} +#else +#error int64 branch should be enabled for DirectVulkan +#endif +void main() { + gl_Position = vec4(float(getLowBits(uint64_t(0x2au)))); +} +)"; + + MG_Util::ShaderTranspiler::PreprocessShaderSource(ShaderStage::Vertex, source); + EXPECT_NE(source.find("#extension GL_ARB_gpu_shader_int64"), String::npos); + EXPECT_NE(source.find("GL_ARB_gpu_shader_int64"), String::npos); + + auto shaderResult = MG_Util::ShaderTranspiler::ShaderCompiler::CompileShader({ + .shaderType = GL_VERTEX_SHADER, + .sourceStr = source, + .flags = MG_Util::ShaderTranspiler::ShaderCompileBits::CompileForOpenGL, + }); + EXPECT_TRUE(shaderResult) << (shaderResult ? "" : shaderResult.error().log); + + MG_Backend::pActiveBackendObject.reset(); +} + +TEST(GetterSanity, ReportsKhrSubgroupDynamicParameters) { + using namespace MobileGL; + + MG_State::pGLContext = MakeUnique(); + + MG_Backend::DynamicBackendParameters params; + params.SubgroupSize = 32; + params.SubgroupSupportedStages = GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT | GL_COMPUTE_SHADER_BIT; + params.SubgroupSupportedFeatures = GL_SUBGROUP_FEATURE_BASIC_BIT_KHR | + GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR | + GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR | + GL_SUBGROUP_FEATURE_QUAD_BIT_KHR; + params.SubgroupQuadOperationsInAllStages = true; + MG_Backend::pActiveBackendObject = MakeUnique(params); + + GLint intValue = 0; + MG_Impl::GLImpl::GetIntegerv(GL_SUBGROUP_SIZE_KHR, &intValue); + EXPECT_EQ(intValue, 32); + MG_Impl::GLImpl::GetIntegerv(GL_SUBGROUP_SUPPORTED_STAGES_KHR, &intValue); + EXPECT_EQ(intValue, static_cast(params.SubgroupSupportedStages)); + MG_Impl::GLImpl::GetIntegerv(GL_SUBGROUP_SUPPORTED_FEATURES_KHR, &intValue); + EXPECT_EQ(intValue, static_cast(params.SubgroupSupportedFeatures)); + MG_Impl::GLImpl::GetIntegerv(GL_SUBGROUP_QUAD_ALL_STAGES_KHR, &intValue); + EXPECT_EQ(intValue, GL_TRUE); + + GLint64 int64Value = 0; + MG_Impl::GLImpl::GetInteger64v(GL_SUBGROUP_SUPPORTED_FEATURES_KHR, &int64Value); + EXPECT_EQ(int64Value, static_cast(params.SubgroupSupportedFeatures)); + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); + + MG_Backend::pActiveBackendObject.reset(); + MG_State::pGLContext.reset(); +} + +TEST(DirectVulkanSanity, CommandMemoryBarrierMakesIndirectDrawCommandsVisible) { + using namespace MobileGL; + using namespace MobileGL::MG_Backend::DirectVulkan; + + const VkMemoryBarrier commandBarrier = + VulkanRenderer::BuildMemoryBarrierForGlBarriers(GL_COMMAND_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT); + EXPECT_NE(commandBarrier.dstAccessMask & VK_ACCESS_INDIRECT_COMMAND_READ_BIT, 0u); + + const VkMemoryBarrier storageOnlyBarrier = + VulkanRenderer::BuildMemoryBarrierForGlBarriers(GL_SHADER_STORAGE_BARRIER_BIT); + EXPECT_EQ(storageOnlyBarrier.dstAccessMask & VK_ACCESS_INDIRECT_COMMAND_READ_BIT, 0u); +} + +TEST(DirectVulkanSanity, DrawIndexedIndirectCommandMatchesGlAndVulkanLayout) { + using namespace MobileGL::MG_Backend::DirectVulkan; + + EXPECT_EQ(sizeof(DrawIndexedCmdParam), 20u); + EXPECT_EQ(offsetof(DrawIndexedCmdParam, indexCount), 0u); + EXPECT_EQ(offsetof(DrawIndexedCmdParam, instanceCount), 4u); + EXPECT_EQ(offsetof(DrawIndexedCmdParam, firstIndex), 8u); + EXPECT_EQ(offsetof(DrawIndexedCmdParam, vertexOffset), 12u); + EXPECT_EQ(offsetof(DrawIndexedCmdParam, firstInstance), 16u); +} + TEST(DirectVulkanSanity, UndefinedDepthStencilLayoutUsesDontCareForUnclearedAspects) { using namespace MobileGL; using namespace MobileGL::MG_Backend::DirectVulkan; diff --git a/MobileGL/MG_Test/Texture/TextureTest.cpp b/MobileGL/MG_Test/Texture/TextureTest.cpp index ccb8d493..61c16cce 100644 --- a/MobileGL/MG_Test/Texture/TextureTest.cpp +++ b/MobileGL/MG_Test/Texture/TextureTest.cpp @@ -11,6 +11,7 @@ #include "Includes.h" #include "Init.h" #include +#include #include #include #include @@ -66,6 +67,38 @@ TEST_F(TextureTest, TextureStorageAndSubImageModifyNamedObjectOnly) { EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); } +TEST_F(TextureTest, BoundTexSubImage2DUsesCompactRowsAfterUnpackProcessing) { + GLuint texture = 0; + MG_Impl::GLImpl::GenTextures(1, &texture); + MG_Impl::GLImpl::BindTexture(GL_TEXTURE_2D, texture); + + const Uint8 initialPixels[2 * 16] = {}; + MG_Impl::GLImpl::TexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 5, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, initialPixels); + + const Uint8 subImageWithGuard[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, + 101, 102, 103, + 10, 11, 12, 13, 14, 15, 16, 17, 18, + 201, 202, 203, 204, 205, 206, + }; + MG_Impl::GLImpl::PixelStorei(GL_UNPACK_ALIGNMENT, 4); + MG_Impl::GLImpl::TexSubImage2D(GL_TEXTURE_2D, 0, 1, 0, 3, 2, GL_RGB, GL_UNSIGNED_BYTE, subImageWithGuard); + + const auto textureObject = MG_State::pGLContext->GetTextureObject(texture); + auto* mipmapObject = static_cast(textureObject.get()); + const auto* stored = static_cast( + mipmapObject->MapMipmapData(TextureUploadTarget::Texture2D, 0)); + + const Uint8 expected[] = { + 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, + 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 0, 0, 0, + }; + for (SizeT i = 0; i < sizeof(expected); ++i) { + EXPECT_EQ(stored[i], expected[i]) << "byte " << i; + } + EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR); +} + TEST_F(TextureTest, GetTextureImageReadsNamedObjectWithoutBinding) { GLuint texture = 0; MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture); diff --git a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp index a5271759..68941de2 100644 --- a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp @@ -8,6 +8,9 @@ #include "Loader.h" +#include +#include + namespace MobileGL::MG_Util::BackendLoader { namespace { struct VulkanDynamicFunctions { @@ -33,6 +36,14 @@ namespace MobileGL::MG_Util::BackendLoader { return loaded; } + + Bool IsShaderSubgroupForcedDisabled() { + const char* value = std::getenv("MOBILEGL_DISABLE_SUBGROUP"); + if (!value) { + return false; + } + return std::strcmp(value, "true") == 0 || std::strcmp(value, "TRUE") == 0; + } } // namespace inline Version DecodeApiVersion(uint32_t version) { @@ -46,6 +57,12 @@ namespace MobileGL::MG_Util::BackendLoader { return oss.str(); } + inline Bool HasUsableShaderSubgroupSupport(const VkPhysicalDeviceSubgroupProperties& subgroupProps) { + return subgroupProps.subgroupSize > 0 && + (subgroupProps.supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) != 0 && + (subgroupProps.supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) != 0; + } + Bool QueryVulkanCapabilities(MobileGL::MG_External::VulkanCapabilities& caps, VkInstance instance, VkPhysicalDevice physicalDevice) { if (!physicalDevice) { @@ -69,8 +86,12 @@ namespace MobileGL::MG_Util::BackendLoader { return false; } + VkPhysicalDeviceSubgroupProperties subgroupProps{}; + subgroupProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; + VkPhysicalDeviceProperties2 props2{}; props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + props2.pNext = &subgroupProps; if (vk.vkGetPhysicalDeviceProperties2) { vk.vkGetPhysicalDeviceProperties2(physicalDevice, &props2); } else { @@ -84,6 +105,28 @@ namespace MobileGL::MG_Util::BackendLoader { caps.DriverVersionString = DecodeDriverVersion(p.driverVersion); caps.UniformBufferOffsetAlignment = static_cast(p.limits.minUniformBufferOffsetAlignment); caps.MaxShaderStorageBlockSize = static_cast(p.limits.maxStorageBufferRange); + const Bool supportsShaderSubgroup = vk.vkGetPhysicalDeviceProperties2 && + HasUsableShaderSubgroupSupport(subgroupProps); + const Bool forceDisableShaderSubgroup = IsShaderSubgroupForcedDisabled(); + caps.SupportsShaderSubgroup = supportsShaderSubgroup && !forceDisableShaderSubgroup; + if (caps.SupportsShaderSubgroup) { + caps.SubgroupSize = subgroupProps.subgroupSize; + caps.SubgroupSupportedStages = subgroupProps.supportedStages; + caps.SubgroupSupportedOperations = subgroupProps.supportedOperations; + caps.SubgroupQuadOperationsInAllStages = subgroupProps.quadOperationsInAllStages == VK_TRUE; + } else { + caps.SubgroupSize = 0; + caps.SubgroupSupportedStages = 0; + caps.SubgroupSupportedOperations = 0; + caps.SubgroupQuadOperationsInAllStages = false; + } + + MGLOG_I("Vulkan shader subgroup support: detected=%s advertised=%s size=%u stages=0x%x operations=0x%x", + supportsShaderSubgroup ? "true" : "false", caps.SupportsShaderSubgroup ? "true" : "false", + subgroupProps.subgroupSize, subgroupProps.supportedStages, subgroupProps.supportedOperations); + if (supportsShaderSubgroup && forceDisableShaderSubgroup) { + MGLOG_W("Vulkan shader subgroup support forced off by MOBILEGL_DISABLE_SUBGROUP"); + } return true; } @@ -95,5 +138,10 @@ namespace MobileGL::MG_Util::BackendLoader { caps.DriverVersionString = DecodeDriverVersion(properties.driverVersion); caps.UniformBufferOffsetAlignment = static_cast(properties.limits.minUniformBufferOffsetAlignment); caps.MaxShaderStorageBlockSize = static_cast(properties.limits.maxStorageBufferRange); + caps.SupportsShaderSubgroup = false; + caps.SubgroupSize = 0; + caps.SubgroupSupportedStages = 0; + caps.SubgroupSupportedOperations = 0; + caps.SubgroupQuadOperationsInAllStages = false; } } // namespace MobileGL::MG_Util::BackendLoader diff --git a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h index 54c360a7..dae1620d 100644 --- a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h @@ -17,6 +17,11 @@ namespace MobileGL { String DriverVersionString; Int UniformBufferOffsetAlignment = 256; SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024; + Bool SupportsShaderSubgroup = false; + Uint32 SubgroupSize = 0; + Uint32 SubgroupSupportedStages = 0; + Uint32 SubgroupSupportedOperations = 0; + Bool SubgroupQuadOperationsInAllStages = false; }; } // namespace MG_External namespace MG_Util::BackendLoader { diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp b/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp index 4e13031b..e6c88619 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp @@ -8,7 +8,9 @@ #include "ShaderSourceProcessor.h" +#include #include +#include namespace { using MobileGL::SizeT; @@ -159,6 +161,106 @@ namespace { return lineEnd == MobileGL::String::npos ? source.size() : lineEnd + 1; } + bool IsExtensionAdvertised(MobileGL::GLExtension extension) { + const auto& activeBackendObject = MobileGL::MG_Backend::pActiveBackendObject; + if (!activeBackendObject) { + return true; + } + + const auto& extensions = activeBackendObject->GetRendererInfo().RendererGLInfo.Extensions; + return std::find(extensions.begin(), extensions.end(), extension) != extensions.end(); + } + + MobileGL::String TrimDirectiveToken(const MobileGL::String& token) { + SizeT start = 0; + while (start < token.size() && std::isspace(static_cast(token[start]))) { + start++; + } + + SizeT end = token.size(); + while (end > start && std::isspace(static_cast(token[end - 1]))) { + end--; + } + return token.substr(start, end - start); + } + + void FilterUnsupportedGpuShaderInt64(MobileGL::String& source) { + if (IsExtensionAdvertised(MobileGL::E_GL_ARB_gpu_shader_int64)) { + return; + } + + SizeT lineStart = 0; + while (lineStart < source.size()) { + SizeT lineEnd = source.find('\n', lineStart); + const bool hasLineBreak = lineEnd != MobileGL::String::npos; + if (!hasLineBreak) { + lineEnd = source.size(); + } + + const MobileGL::String line = source.substr(lineStart, lineEnd - lineStart); + SizeT probe = 0; + while (probe < line.size() && std::isspace(static_cast(line[probe]))) { + probe++; + } + + if (probe < line.size() && line[probe] == '#') { + probe++; + while (probe < line.size() && std::isspace(static_cast(line[probe]))) { + probe++; + } + + constexpr const char* extensionToken = "extension"; + constexpr SizeT extensionLen = 9; + const bool hasExtensionDirective = + probe + extensionLen <= line.size() && + line.compare(probe, extensionLen, extensionToken) == 0 && + (probe + extensionLen == line.size() || !IsIdentifierChar(line[probe + extensionLen])); + if (hasExtensionDirective) { + probe += extensionLen; + while (probe < line.size() && std::isspace(static_cast(line[probe]))) { + probe++; + } + + constexpr const char* int64Extension = "GL_ARB_gpu_shader_int64"; + constexpr SizeT int64ExtensionLen = 23; + const bool hasInt64Extension = + probe + int64ExtensionLen <= line.size() && + line.compare(probe, int64ExtensionLen, int64Extension) == 0 && + (probe + int64ExtensionLen == line.size() || + !IsIdentifierChar(line[probe + int64ExtensionLen])); + if (hasInt64Extension) { + probe += int64ExtensionLen; + while (probe < line.size() && std::isspace(static_cast(line[probe]))) { + probe++; + } + + if (probe < line.size() && line[probe] == ':') { + probe++; + const MobileGL::String behavior = TrimDirectiveToken(line.substr(probe)); + const SizeT replaceLen = lineEnd - lineStart + (hasLineBreak ? 1 : 0); + if (behavior == "require") { + const MobileGL::String replacement = + "#error GL_ARB_gpu_shader_int64 is not advertised by MobileGL\n"; + source.replace(lineStart, replaceLen, replacement); + lineStart += replacement.size(); + } else if (behavior == "enable" || behavior == "warn") { + source.replace(lineStart, replaceLen, "\n"); + lineStart++; + } else { + lineStart = lineEnd + (hasLineBreak ? 1 : 0); + } + continue; + } + } + } + } + + lineStart = lineEnd + (hasLineBreak ? 1 : 0); + } + + ReplaceIdentifier(source, "GL_ARB_gpu_shader_int64", "MG_DISABLED_GL_ARB_gpu_shader_int64"); + } + void ModernizeLegacyGLSL(MobileGL::ShaderStage stage, MobileGL::String& source) { RemoveDefineForIdentifier(source, "HIGHP_OR_DEFAULT"); RemoveDefineForIdentifier(source, "MEDIUMP_OR_DEFAULT"); @@ -278,6 +380,8 @@ namespace MobileGL { } } + FilterUnsupportedGpuShaderInt64(source); + // Some shader packs define helpers with built-in GLSL names such as round(), tanh(), or fma(). // These may pass OpenGL-style validation but fail when recompiled for Vulkan/SPIR-V generation. RenameBuiltinShadowingFunction(source, "round", "mg_round"); From 85bd0613ca07f9c3d1ffaa110ebcd06a23248370 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 9 Jun 2026 17:20:23 +0800 Subject: [PATCH 5/5] [Fix] (MG_Backend/DirectGLES): support Voxy rendering Implemented: - Advertise Voxy-required DirectGLES extensions without raising the reported OpenGL version. - Add DirectGLES multi draw indirect count emulation and preserve GL draw indirect baseInstance semantics on GLES. - Add DirectGLES DSA framebuffer clear/blit paths used by Minecraft and Voxy presentation. Fixed: - Rewrite gl_BaseInstance in DirectGLES vertex shaders and provide a backend uniform for indirect draw emulation. - Materialize framebuffer attachment textures during DirectGLES FBO sync so named framebuffer operations do not desync backend attachment state. - Avoid redundant texture buffer rebinding and handle texture buffers without bound storage during backend sync. Tests: - Add MG_Test coverage for DirectGLES Voxy extension advertising, baseInstance shader rewriting, and DSA named framebuffer clear/blit backend wiring. --- .../DirectGLES/BackendObject_DirectGLES.cpp | 9 +- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 237 +++++++++++++++++- MobileGL/MG_Backend/DirectGLES/DirectGLES.h | 12 + MobileGL/MG_Backend/DirectGLES/Managers.cpp | 102 +++++++- MobileGL/MG_Backend/DirectGLES/Managers.h | 5 + MobileGL/MG_Test/SanityTest.cpp | 66 +++++ 6 files changed, 418 insertions(+), 13 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index 3bb3f6bf..5d7a764f 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -132,7 +132,10 @@ 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_ARB_buffer_storage}, + E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage, + E_GL_ARB_texture_storage, E_GL_ARB_direct_state_access, + E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters, + E_GL_ARB_shader_draw_parameters}, .IsCompatibilityProfile = false // Is Compatibility Profile }, .StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability @@ -166,6 +169,7 @@ namespace MobileGL::MG_Backend::DirectGLES { funcsTable.GL.MultiDrawElements = MultiDrawElements; funcsTable.GL.MultiDrawElementsBaseVertex = MultiDrawElementsBaseVertex; funcsTable.GL.MultiDrawElementsIndirect = MultiDrawElementsIndirect; + funcsTable.GL.MultiDrawElementsIndirectCount = MultiDrawElementsIndirectCount; funcsTable.GL.MultiDrawArraysIndirect = MultiDrawArraysIndirect; funcsTable.GL.DrawRangeElementsBaseVertex = DrawRangeElementsBaseVertex; funcsTable.GL.DrawRangeElements = DrawRangeElements; @@ -197,7 +201,10 @@ namespace MobileGL::MG_Backend::DirectGLES { funcsTable.GL.ClearBufferfv = ClearBufferfv; funcsTable.GL.ClearBufferuiv = ClearBufferuiv; funcsTable.GL.ClearBufferiv = ClearBufferiv; + funcsTable.GL.ClearNamedFramebufferfv = ClearNamedFramebufferfv; + funcsTable.GL.ClearNamedFramebufferfi = ClearNamedFramebufferfi; funcsTable.GL.BlitFramebuffer = BlitFramebuffer; + funcsTable.GL.BlitNamedFramebuffer = BlitNamedFramebuffer; funcsTable.GL.CopyTexImage2D = CopyTexImage2D; funcsTable.GL.CopyTexSubImage2D = CopyTexSubImage2D; funcsTable.GL.GenerateMipmap = GenerateMipmap; diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 11438aaa..91a6cea1 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,14 @@ namespace MobileGL::MG_Backend::DirectGLES { return a; } + struct DrawElementsIndirectCommand { + Uint32 count = 0; + Uint32 instanceCount = 0; + Uint32 firstIndex = 0; + Int32 baseVertex = 0; + Uint32 baseInstance = 0; + }; + namespace DebugImpl { #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG void ErrorLopper::Loop(const std::function& func) { @@ -623,6 +632,41 @@ namespace MobileGL::MG_Backend::DirectGLES { } } + void SyncAndBindFramebufferObject(const SharedPtr& framebuffer, + FramebufferTarget target, Bool forceSync = false) { +#ifdef TRACY_ENABLE + ZoneScopedC(TRACY_ZONECOLOR_BACKEND); +#endif + if (!framebuffer || framebuffer == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) { + g_GLESFuncs.glBindFramebuffer(target == FramebufferTarget::Draw ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER, + 0); + return; + } + + auto& registry = FramebufferImpl::g_backendFramebufferObjects; + const auto& backendFBOIt = registry.find(framebuffer.get()); + const Bool exists = backendFBOIt != registry.end(); + auto& backendObj = exists ? backendFBOIt->second : registry.GetOrCreate(framebuffer); + if (!exists) { + backendObj = MakeShared(); + } + if (forceSync) { + backendObj->InvalidateSyncedState(); + } + + backendObj->SyncToBackend(framebuffer, target); + backendObj->Bind(target); + } + + void ForceBindCurrentFBO(FramebufferTarget target) { +#ifdef TRACY_ENABLE + ZoneScopedC(TRACY_ZONECOLOR_BACKEND); +#endif + auto& slot = MG_State::pGLContext->GetFramebufferBindingSlot(target); + SyncAndBindFramebufferObject(slot.GetBoundObject(), target); + FramebufferImpl::g_fboBindVersions[(SizeT)target] = slot.GetVersion(); + } + void PrepareForDraw(DrawSyncBit syncBit) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); @@ -804,6 +848,17 @@ namespace MobileGL::MG_Backend::DirectGLES { } } + void SetCurrentBaseInstance(Uint32 baseInstance) { + const auto& currentProgram = MG_State::pGLContext->GetCurrentProgram(); + if (!currentProgram || !currentProgram->GetLinkStatus()) { + return; + } + const auto& backendProgramIt = PrgramImpl::g_backendProgramObjects.find(currentProgram.get()); + if (backendProgramIt != PrgramImpl::g_backendProgramObjects.end()) { + backendProgramIt->second->SetBaseInstance(baseInstance); + } + } + void PrepareForCompute(Bool includeDispatchIndirectBuffer) { #ifdef TRACY_ENABLE ZoneScopedC(TRACY_ZONECOLOR_BACKEND); @@ -930,14 +985,127 @@ namespace MobileGL::MG_Backend::DirectGLES { #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER DebugImpl::OpenGLScopeMarker marker(__func__); #endif - DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::IndirectBuffer; + if (drawcount <= 0) { + return; + } + if (stride == 0) { + stride = sizeof(DrawElementsIndirectCommand); + } + if (stride < static_cast(sizeof(DrawElementsIndirectCommand))) { + MGLOG_E("MultiDrawElementsIndirect skipped: stride %d is smaller than command size %zu", + stride, sizeof(DrawElementsIndirectCommand)); + return; + } + + DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::IndirectBuffer | DrawSyncBit::Instancing; PrepareForDraw(syncBit); - for (GLsizei i = 0; i < drawcount; ++i) { - const GLvoid* cmd = reinterpret_cast(reinterpret_cast(indirect) + - i * (stride ? stride : sizeof(GLsizei) * 4)); - g_GLESFuncs.glDrawElementsIndirect(mode, type, cmd); + const SizeT indexSize = MG_Util::GetGLTypeSize(type); + if (indexSize == 0) { + MGLOG_E("MultiDrawElementsIndirect skipped: unsupported index type 0x%x", type); + return; } + + auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject(); + if (!drawBuffer) { + MGLOG_E("MultiDrawElementsIndirect skipped: no GL_DRAW_INDIRECT_BUFFER is bound"); + return; + } + drawBuffer->MarkPersistentMappedRangeDirty(); + const auto drawData = drawBuffer->GetDataReadOnly(); + const SizeT commandOffset = reinterpret_cast(indirect); + const SizeT commandBytes = commandOffset + static_cast(stride) * static_cast(drawcount - 1) + + sizeof(DrawElementsIndirectCommand); + if (!drawData || commandBytes > drawData->size()) { + MGLOG_E("MultiDrawElementsIndirect skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range"); + return; + } + + for (GLsizei i = 0; i < drawcount; ++i) { + DrawElementsIndirectCommand cmd{}; + std::memcpy(&cmd, drawData->data() + commandOffset + static_cast(i) * stride, sizeof(cmd)); + if (cmd.count == 0 || cmd.instanceCount == 0) { + continue; + } + SetCurrentBaseInstance(cmd.baseInstance); + const auto indexByteOffset = static_cast(cmd.firstIndex) * indexSize; + g_GLESFuncs.glDrawElementsInstancedBaseVertex( + mode, static_cast(cmd.count), type, reinterpret_cast(indexByteOffset), + static_cast(cmd.instanceCount), cmd.baseVertex); + } + SetCurrentBaseInstance(0); + } + + void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride) { +#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER + DebugImpl::OpenGLScopeMarker marker(__func__); +#endif + if (maxdrawcount <= 0) { + return; + } + if (stride == 0) { + stride = sizeof(DrawElementsIndirectCommand); + } + if (stride < static_cast(sizeof(DrawElementsIndirectCommand))) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: stride %d is smaller than command size %zu", + stride, sizeof(DrawElementsIndirectCommand)); + return; + } + + DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::IndirectBuffer | DrawSyncBit::Instancing; + PrepareForDraw(syncBit); + + const SizeT indexSize = MG_Util::GetGLTypeSize(type); + if (indexSize == 0) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: unsupported index type 0x%x", type); + return; + } + + auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject(); + auto parameterBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject(); + if (!drawBuffer) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: no GL_DRAW_INDIRECT_BUFFER is bound"); + return; + } + if (!parameterBuffer) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: no GL_PARAMETER_BUFFER is bound"); + return; + } + + drawBuffer->MarkPersistentMappedRangeDirty(); + parameterBuffer->MarkPersistentMappedRangeDirty(); + const auto drawData = drawBuffer->GetDataReadOnly(); + const auto parameterData = parameterBuffer->GetDataReadOnly(); + + const SizeT commandOffset = reinterpret_cast(indirect); + const SizeT commandBytes = commandOffset + static_cast(stride) * static_cast(maxdrawcount - 1) + + sizeof(DrawElementsIndirectCommand); + if (!drawData || commandBytes > drawData->size()) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range"); + return; + } + if (!parameterData || drawcount < 0 || static_cast(drawcount) + sizeof(Uint32) > parameterData->size()) { + MGLOG_E("MultiDrawElementsIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range"); + return; + } + + Uint32 actualDrawCount = 0; + std::memcpy(&actualDrawCount, parameterData->data() + drawcount, sizeof(actualDrawCount)); + actualDrawCount = std::min(actualDrawCount, static_cast(maxdrawcount)); + for (Uint32 i = 0; i < actualDrawCount; ++i) { + DrawElementsIndirectCommand cmd{}; + std::memcpy(&cmd, drawData->data() + commandOffset + static_cast(i) * stride, sizeof(cmd)); + if (cmd.count == 0 || cmd.instanceCount == 0) { + continue; + } + SetCurrentBaseInstance(cmd.baseInstance); + const auto indexByteOffset = static_cast(cmd.firstIndex) * indexSize; + g_GLESFuncs.glDrawElementsInstancedBaseVertex( + mode, static_cast(cmd.count), type, reinterpret_cast(indexByteOffset), + static_cast(cmd.instanceCount), cmd.baseVertex); + } + SetCurrentBaseInstance(0); } void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) { @@ -1048,6 +1216,31 @@ namespace MobileGL::MG_Backend::DirectGLES { }); } + void BlitNamedFramebuffer(const SharedPtr& readFramebuffer, + const SharedPtr& drawFramebuffer, + GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) { +#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER + DebugImpl::OpenGLScopeMarker marker(__func__); +#endif + TextureImpl::SyncNeccessaryTextures(); + RenderStateImpl::SyncRenderState(); + + SyncAndBindFramebufferObject(readFramebuffer, FramebufferTarget::Read, true); + SyncAndBindFramebufferObject(drawFramebuffer, FramebufferTarget::Draw, true); + + MGLOG_D("ES %s(%d, %d, %d, %d, %d, %d, %d, %d, 0x%x, %s)", __func__, srcX0, srcY0, srcX1, srcY1, + dstX0, dstY0, dstX1, dstY1, mask, MG_Util::ConvertGLEnumToString(filter).c_str()); + g_GLESFuncs.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) { + MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + + ForceBindCurrentFBO(FramebufferTarget::Read); + ForceBindCurrentFBO(FramebufferTarget::Draw); + } + Bool UpdateTextureBindingAtTarget(GLenum target) { #ifdef TRACY_ENABLE ZoneScopedNC(__func__, TRACY_ZONECOLOR_BACKEND); @@ -1558,6 +1751,40 @@ namespace MobileGL::MG_Backend::DirectGLES { g_GLESFuncs.glClearBufferuiv(buffer, drawbuffer, value); } + void ClearNamedFramebufferfv(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, const GLfloat* value) { +#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER + DebugImpl::OpenGLScopeMarker marker(__func__); +#endif + TextureImpl::SyncNeccessaryTextures(); + RenderStateImpl::SyncRenderState(); + + SyncAndBindFramebufferObject(framebuffer, FramebufferTarget::Draw, true); + g_GLESFuncs.glClearBufferfv(buffer, drawbuffer, value); + DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) { + MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + + ForceBindCurrentFBO(FramebufferTarget::Draw); + } + + void ClearNamedFramebufferfi(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { +#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER + DebugImpl::OpenGLScopeMarker marker(__func__); +#endif + TextureImpl::SyncNeccessaryTextures(); + RenderStateImpl::SyncRenderState(); + + SyncAndBindFramebufferObject(framebuffer, FramebufferTarget::Draw, true); + g_GLESFuncs.glClearBufferfi(buffer, drawbuffer, depth, stencil); + DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) { + MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + + ForceBindCurrentFBO(FramebufferTarget::Draw); + } + class TempPixelStoreParameterSync { public: TempPixelStoreParameterSync(Bool isUnpack) : m_isUnpack(isUnpack) { diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.h b/MobileGL/MG_Backend/DirectGLES/DirectGLES.h index 271689d6..1ad94814 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.h +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -30,6 +31,8 @@ namespace MobileGL::MG_Backend::DirectGLES { void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices, GLsizei drawcount, const GLint* basevertex); void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride); + void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, + GLsizei maxdrawcount, GLsizei stride); void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride); void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices, GLint basevertex); @@ -46,8 +49,17 @@ namespace MobileGL::MG_Backend::DirectGLES { GLuint baseinstance); void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); void DrawArraysIndirect(GLenum mode, const void* indirect); + void ClearNamedFramebufferfv(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, const GLfloat* value); + void ClearNamedFramebufferfi(const SharedPtr& framebuffer, + GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); + void BlitNamedFramebuffer(const SharedPtr& readFramebuffer, + const SharedPtr& drawFramebuffer, + GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index c8d77b6b..e36d239d 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -23,9 +23,11 @@ #include #include #include +#include namespace MobileGL::MG_Backend::DirectGLES { constexpr Bool PREFER_MAP_BUFFER_RANGE_FOR_BUFFER_SYNC = true; + constexpr const char* BASE_INSTANCE_UNIFORM_NAME = "mg_BaseInstance"; static Uint ResolveBackendEsslVersion() { const auto& version = g_GLESCapabilities.GLESVersion; @@ -38,6 +40,47 @@ namespace MobileGL::MG_Backend::DirectGLES { return 300; } + String ReplaceIdentifier(String source, const String& from, const String& to) { + SizeT pos = 0; + while ((pos = source.find(from, pos)) != String::npos) { + const Bool leftIsIdent = pos > 0 && + (std::isalnum(static_cast(source[pos - 1])) || source[pos - 1] == '_'); + const SizeT end = pos + from.size(); + const Bool rightIsIdent = end < source.size() && + (std::isalnum(static_cast(source[end])) || source[end] == '_'); + if (!leftIsIdent && !rightIsIdent) { + source.replace(pos, from.size(), to); + pos += to.size(); + } else { + pos = end; + } + } + return source; + } + + String InjectUniformAfterVersion(String source, const String& declaration) { + const SizeT versionPos = source.find("#version"); + if (versionPos == String::npos) { + return declaration + "\n" + source; + } + + const SizeT lineEnd = source.find('\n', versionPos); + if (lineEnd == String::npos) { + return source + "\n" + declaration + "\n"; + } + source.insert(lineEnd + 1, declaration + "\n"); + return source; + } + + String EmulateBaseInstanceInVertexShader(String source, GLenum shaderType) { + if (shaderType != GL_VERTEX_SHADER || source.find("gl_BaseInstance") == String::npos) { + return source; + } + source = ReplaceIdentifier(std::move(source), "gl_BaseInstance", BASE_INSTANCE_UNIFORM_NAME); + return InjectUniformAfterVersion(std::move(source), + String("uniform highp int ") + BASE_INSTANCE_UNIFORM_NAME + ";"); + } + namespace BufferImpl { BackendBufferObject::BackendBufferObject() { #ifdef TRACY_ENABLE @@ -629,13 +672,15 @@ namespace MobileGL::MG_Backend::DirectGLES { static_cast(stateTextureObject.get()); auto& slot = textureBufferObject->GetBufferBindingSlot(); auto& buffer = slot.GetBoundObject(); + if (!buffer) { + MGLOG_D("Texture buffer object with ID: %u has no bound buffer, skipping sync.", + stateTextureObject->GetExternalIndex()); + return; + } auto bufferIndex = buffer->GetExternalIndex(); currentTextureInfo.bufferExternalIndex = bufferIndex; Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo); - MGLOG_D("Texture state changed significantly or not initialized, regenerating texture (tex buffer) " - "with ID: %u", - m_backendTextureId); // Need to sync texture buffer if not synced yet auto& backendBuffers = BufferImpl::g_backendBufferObjects; @@ -659,7 +704,19 @@ namespace MobileGL::MG_Backend::DirectGLES { TextureImpl::GenerateTextureFormatInfo(textureBufferObject->GetFormat(), &glInternalFormat, &glFormat, &glType); - g_GLESFuncs.glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId); + if (needsRegeneration) { + MGLOG_D("Texture state changed significantly or not initialized, regenerating texture buffer with " + "ID: %u, buffer ID: %u, buffer size: %zu, format: %s", + m_backendTextureId, backendId, buffer->GetSize(), + MG_Util::ConvertGLEnumToString(glInternalFormat).c_str()); + g_GLESFuncs.glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId); + DebugImpl::ErrorLopper::Loop( + [file = __FILE__, line = __LINE__, func = __func__, glInternalFormat, backendId](GLenum err) { + MGLOG_D("%s(%s:%d) glTexBuffer(format=%s, buffer=%u) ES error: %s", + func, file, line, MG_Util::ConvertGLEnumToString(glInternalFormat).c_str(), + backendId, MG_Util::ConvertGLEnumToString(err).c_str()); + }); + } break; } default: @@ -905,17 +962,37 @@ namespace MobileGL::MG_Backend::DirectGLES { g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_backendFBOId); } + void BackendFramebufferObject::InvalidateSyncedState() { + std::fill(std::begin(m_frontendDrawBuffers), std::end(m_frontendDrawBuffers), + FramebufferAttachmentType::Unknown); + std::fill(std::begin(m_backendDrawBuffers), std::end(m_backendDrawBuffers), GL_NONE); + m_frontendReadBuffer = FramebufferAttachmentType::Unknown; + m_backendReadBuffer = GL_NONE; + std::fill(m_syncedFrontendAttachmentVersions.begin(), m_syncedFrontendAttachmentVersions.end(), + static_cast(~0u)); + } + static Bool SyncAttachmentObject(GLenum glFBOTarget, const MG_State::GLState::FramebufferAttachmentObject& attachmentObject, GLenum glBackendAttachment) { if (attachmentObject.IsTexture()) { const auto& textureObject = attachmentObject.GetTexture(); + SharedPtr backendTextureObject; const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject.get()); if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { + auto& backendTextureSlot = TextureImpl::g_backendTextureObjects.GetOrCreate(textureObject); + if (!backendTextureSlot) { + backendTextureSlot = MakeShared(); + } + backendTextureObject = backendTextureSlot; + } else { + backendTextureObject = backendTextureIt->second; + } + if (!backendTextureObject) { MGLOG_E("%s: No backend texture found for FBO attachment, cannot bind texture.", __func__); return false; } - const auto& backendTextureObject = backendTextureIt->second; + backendTextureObject->SyncMipmapsToBackend(textureObject); auto glTextureTarget = MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget()); backendTextureObject->Bind(glTextureTarget); g_GLESFuncs.glFramebufferTexture2D(glFBOTarget, glBackendAttachment, glTextureTarget, @@ -1023,8 +1100,9 @@ namespace MobileGL::MG_Backend::DirectGLES { // relevant FRONTEND!!! version should be checked and updated if (m_syncedFrontendAttachmentVersions[i] != attachmentVersions[i]) { - SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment); - m_syncedFrontendAttachmentVersions[i] = attachmentVersions[i]; + if (SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment)) { + m_syncedFrontendAttachmentVersions[i] = attachmentVersions[i]; + } } #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG else { @@ -1223,6 +1301,7 @@ namespace MobileGL::MG_Backend::DirectGLES { source = RemoveLayoutBinding(source); source = ProcessOutColorLocations(source); source = ForceFlatIntegerVaryings(source, glShaderType); + source = EmulateBaseInstanceInVertexShader(std::move(source), glShaderType); source = ForceSupporterOutput(source); // Patch for Photon compiler precision issue @@ -1273,6 +1352,8 @@ namespace MobileGL::MG_Backend::DirectGLES { } else { MGLOG_D("Program linked successfully. ID: %u", m_backendProgramId); } + m_baseInstanceUniformLocation = g_GLESFuncs.glGetUniformLocation(m_backendProgramId, + BASE_INSTANCE_UNIFORM_NAME); // Create global UBO if (stateProgramObject->GetUBOSize() > 0) { @@ -1295,6 +1376,13 @@ namespace MobileGL::MG_Backend::DirectGLES { MGLOG_D("Using program %u", m_backendProgramId); g_GLESFuncs.glUseProgram(m_backendProgramId); } + + void BackendProgramObjectImpl::SetBaseInstance(Uint32 baseInstance) const { + if (m_baseInstanceUniformLocation < 0) { + return; + } + g_GLESFuncs.glUniform1i(m_baseInstanceUniformLocation, static_cast(baseInstance)); + } } // namespace PrgramImpl namespace SamplerImpl { diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 17876da6..455bf88a 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -15,6 +15,8 @@ #include namespace MobileGL::MG_Backend::DirectGLES { + String EmulateBaseInstanceInVertexShader(String source, GLenum shaderType); + template class StateBackendObjectRegistry { public: @@ -226,6 +228,7 @@ namespace MobileGL::MG_Backend::DirectGLES { BackendFramebufferObject(); void SyncToBackend(const SharedPtr& stateFBOObject, FramebufferTarget asTarget); + void InvalidateSyncedState(); Uint GetBackendFramebufferId() const { return m_backendFBOId; } void Bind(FramebufferTarget target) const; // FramebufferAttachmentType GetCompactedAttachmentTypeAtDrawBufferIndex(Int index); @@ -267,12 +270,14 @@ namespace MobileGL::MG_Backend::DirectGLES { ~BackendProgramObjectImpl(); void SyncToBackend(const SharedPtr& stateProgramObject); void Use() const; + void SetBaseInstance(Uint32 baseInstance) const; Uint GetBackendProgramId() const { return m_backendProgramId; } Uint GetBackendGlobalUBOId() const { return m_backendGlobalUBOId; } private: Uint m_backendProgramId = 0; Uint m_backendGlobalUBOId = 0; + Int m_baseInstanceUniformLocation = -1; Bool m_isInitialized = false; }; diff --git a/MobileGL/MG_Test/SanityTest.cpp b/MobileGL/MG_Test/SanityTest.cpp index 5825444a..4b506476 100644 --- a/MobileGL/MG_Test/SanityTest.cpp +++ b/MobileGL/MG_Test/SanityTest.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -90,6 +91,71 @@ TEST(DirectGLESSanity, AdvertisesDepthTextureForGlmarkShadowScenes) { EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_depth_texture), extensions.end()); } +TEST(DirectGLESSanity, AdvertisesVoxyRequiredRenderingExtensionsWithoutRaisingGLVersion) { + MobileGL::MG_Backend::DirectGLES::BackendObject_DirectGLES backend; + const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo; + const auto& extensions = rendererInfo.Extensions; + + EXPECT_EQ(rendererInfo.TargetGLVersion.Major, 3); + EXPECT_EQ(rendererInfo.TargetGLVersion.Minor, 3); + EXPECT_EQ(rendererInfo.TargetGLVersion.Patch, 0); + + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_compute_shader), + extensions.end()); + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_shader_storage_buffer_object), + extensions.end()); + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_texture_storage), + extensions.end()); + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_direct_state_access), + extensions.end()); + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_multi_draw_indirect), + extensions.end()); + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_indirect_parameters), + extensions.end()); + EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_shader_draw_parameters), + extensions.end()); + EXPECT_EQ(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_gpu_shader_int64), + extensions.end()); + EXPECT_EQ(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_KHR_shader_subgroup), + extensions.end()); +} + +TEST(DirectGLESSanity, ProvidesNamedFramebufferBlitForDirectStateAccess) { + MobileGL::MG_Backend::DirectGLES::BackendObject_DirectGLES backend; + const auto& funcs = backend.GetBackendFunctions().GL; + + EXPECT_NE(funcs.ClearNamedFramebufferfv, nullptr); + EXPECT_NE(funcs.ClearNamedFramebufferfi, nullptr); + EXPECT_NE(funcs.BlitFramebuffer, nullptr); + EXPECT_NE(funcs.BlitNamedFramebuffer, nullptr); +} + +TEST(DirectGLESSanity, RewritesBaseInstanceBuiltinForEsslVertexShaders) { + const MobileGL::String source = R"(#version 320 es +void main() { + uint drawId = gl_BaseInstance; + uint untouched = my_gl_BaseInstance_value; +} +)"; + + const auto rewritten = MobileGL::MG_Backend::DirectGLES::EmulateBaseInstanceInVertexShader( + source, GL_VERTEX_SHADER); + + EXPECT_NE(rewritten.find("uniform highp int mg_BaseInstance;"), MobileGL::String::npos); + EXPECT_NE(rewritten.find("uint drawId = mg_BaseInstance;"), MobileGL::String::npos); + EXPECT_NE(rewritten.find("my_gl_BaseInstance_value"), MobileGL::String::npos); + EXPECT_EQ(rewritten.find("uint drawId = gl_BaseInstance;"), MobileGL::String::npos); +} + +TEST(DirectGLESSanity, LeavesBaseInstanceBuiltinAloneOutsideVertexShaders) { + const MobileGL::String source = "#version 320 es\nuint value = gl_BaseInstance;\n"; + + const auto rewritten = MobileGL::MG_Backend::DirectGLES::EmulateBaseInstanceInVertexShader( + source, GL_FRAGMENT_SHADER); + + EXPECT_EQ(rewritten, source); +} + TEST(DirectVulkanSanity, AdvertisesTextureStorageForDirectStateAccess) { MobileGL::MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend; const auto& extensions = backend.GetRendererInfo().RendererGLInfo.Extensions;