From cf165c0db57dcaadf82e561bec30ab2f74de77ad Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 9 Jun 2026 00:37:37 +0800 Subject: [PATCH] [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