Compare commits

...
Author SHA1 Message Date
BZLZHH 30c6f55c2d [Fix] (MG_Backend/DirectVulkan): Add namespace qualifier to Version type. 2026-05-09 08:28:46 +08:00
BZLZHH b373535d6e [Fix] (MG_Impl/Texture): Fix proxy texture object handling. 2026-04-06 02:35:04 +08:00
swung0x48 4a3a44924a [Refactor] (MG_Backend/DirectVulkan/ProgramFactory): refactor uniform reflection 2026-04-03 16:45:11 +08:00
swung0x48 1b2a7989af [Fix] (MG_Util/ShaderTranspiler/SpvcSession): use proper usage bit to select the right code path. Fix bugs along the way 2026-04-01 14:57:00 +08:00
swung0x48 83d1ce177e [Optimization] (MG_Util/ShaderTranspiler/SpvcSession): use SPIRV-Reflect to avoid full AST parse, speeding up reflection 2026-04-01 10:28:37 +08:00
swung0x48 4321c4a827 [Submodule] (3rdparty/SPIRV-Reflect): add SPIRV-Reflect as dependency 2026-03-29 00:03:57 +08:00
swung0x48 a039ce0987 [Chore] (MG_State/ProgramState): some renames 2026-03-26 09:49:05 +08:00
swung0x48 accfaab720 [Refactor] (MG_Backend/DirectVulkan): get rid of junk, and rename some symbols 2026-03-24 14:20:21 +08:00
swung0x48 35658eb998 [Refactor] (MG_Backend/DirectVulkan): move uniform reflection from UniformDescriptorBinder to ProgramFactory 2026-03-23 16:36:17 +08:00
swung0x48 ccbde0196e [Fix] (MG_Test/ProgramTest): fix wrong decomp source output 2026-03-21 23:07:27 +08:00
swung0x48 0209c5461f [Feat] (MG_Backend/DirectVulkan): implement naive transient/resident buffer, and heuristics to downgrade resident buffer to transient 2026-03-20 17:39:10 +08:00
swung0x48 810b4886c7 [Feat] (MG_Backend/DirectVulkan): supports uint8 index buffer by VK_KHR_index_type_uint8 / VK_EXT_index_type_uint8 2026-03-20 15:27:57 +08:00
swung0x48 7dfc2149d8 [Feat] (MG_Backend/DirectVulkan): unified transient buffer arena 2026-03-20 13:56:12 +08:00
swung0x48 9d1e8bd7cd [Feat] (MG_Backend/DirectVulkan): VkBufferManager transient upload now includes uniform 2026-03-20 13:31:51 +08:00
swung0x48 bfa4049ac1 [Chore] (MG_Backend/DirectVulkan): unwrap frame context initialization 2026-03-20 09:55:22 +08:00
swung0x48 87c56ce3d5 [Fix] (MG_Backend/DirectVulkan): Fix VkBufferManager initialization assertion failure 2026-03-20 09:36:37 +08:00
swung0x48 f2ab50b84e [Feat] (MG_Backend/DirectVulkan): VkBufferManager 2026-03-19 17:44:35 +08:00
swung0x48 b6ac6c182e [Feat] (MG_Backend/DirectVulkan): Buffer arena, buffer slice 2026-03-19 17:23:52 +08:00
swung0x48 5e9f0f0c70 [Chore] (MG_Backend/DirectVulkan): remove some unused stuff 2026-03-19 08:59:34 +08:00
swung0x48 e4455aed9a [Feat] (MG_Backend/DirectVulkan): add draw cmd MultiDrawIndexedCmd 2026-03-18 16:08:00 +08:00
swung0x48 bc018c9513 [Chore] (MG_Backend/DirectVulkan): add indexBufferView field for draw cmds 2026-03-18 13:25:10 +08:00
swung0x48 2ecba4d70c [Fix] (MG_Test/VertexArrayTest): fix VertexArrayTest compilation error 2026-03-18 13:07:57 +08:00
swung0x48 1e11a5950e [Fix] (MG_Test/BufferTest): fix BufferTest compilation error 2026-03-18 11:07:56 +08:00
swung0x48 b324363db0 [Chore] (MG_Backend/DirectVulkan): hard assert SetupDraw failure 2026-03-18 11:01:00 +08:00
swung0x48 a654b15190 [Feat] (MG_Backend/DirectVulkan): naively implement DrawElementsBaseVertex and MultiDrawElementsBaseVertex 2026-03-18 10:43:30 +08:00
swung0x48 75ea7f8c0c [Chore] (MG_Backend/DirectVulkan): include index buffer upload into SetupDraw 2026-03-17 16:09:02 +08:00
swung0x48 09aeee4c03 [Chore] (MG_State/VertexArrayState): add const getter to index buffer binding slot 2026-03-17 15:55:16 +08:00
swung0x48 87122973be [Chore] (MG_Backend/DirectVulkan): refactor draw cmds 2026-03-17 15:10:17 +08:00
swung0x48 8b9adc5121 [Fix] (MG_Backend/DirectVulkan/VkRenderPassManager): avoid reusing active render pass when draw FBO attachments still have pending clears 2026-03-17 14:34:50 +08:00
swung0x48 9e42e40705 [Chore] (MG_Backend/DirectVulkan): notes for MaterializePendingClearForTexture 2026-03-09 17:13:56 +08:00
swung0x48 8a91225eb1 [Chore] (MG_Backend/DirectVulkan): debug log for clear manager 2026-03-09 16:22:51 +08:00
swung0x48 e82815802e [Feat] (MG_Backend/DirectVulkan): implement texture mipmap 2026-03-09 15:09:05 +08:00
swung0x48 67069d4a12 [Fix]: compilation error after merge 2026-03-08 11:06:27 +08:00
swung0x48 4a38e7224e Merge branch 'dev' into Feat/Backend-Direct-Vulkan 2026-03-08 10:31:44 +08:00
swung0x48 645f2f748f [Fix] (MG_Backend/DirectVulkan): fix missing clear when (texture-FBO attach -> clear -> detach) occurs 2026-03-08 00:24:30 +08:00
swung0x48 5652f9ae5f [Chore] (MG_Backend/DirectVulkan): implements BlitFramebuffer using backend manager APIs 2026-03-07 19:29:33 +08:00
swung0x48 f3b0b242dd [Feat] (MG_Backend/DirectVulkan): implements BlitFramebuffer 2026-03-07 18:22:38 +08:00
swung0x48 876cb7bcd3 [Chore] (MG_Backend/DirectVulkan): asserts fbo existence 2026-03-07 09:33:05 +08:00
swung0x48 ee9a121c6f [Fix] (MG_Impl/Init): bind default fbo to draw/read slot at init 2026-03-07 09:31:42 +08:00
swung0x48 00439c5c4a [Fix] (MG_Backend/DirectVulkan): Fix sampled-texture/render-pass layout hazard 2026-03-06 21:53:50 +08:00
swung0x48 d7286a5832 [Fix] (MG_Backend/DirectVulkan): some transition fixes 2026-03-06 18:25:33 +08:00
swung0x48 972804fdc7 [Fix] (MG_Backend/DirectVulkan): fixing some layout mismatch 2026-03-06 17:16:55 +08:00
swung0x48 bca7328421 [Fix] (MG_Backend/DirectVulkan): use hash to save active render pass 2026-03-06 16:17:56 +08:00
swung0x48 8097c0d7b1 [Fix] (MG_Backend/DirectVulkan): add untreated aspect member in TextureResource move ctor 2026-03-06 15:45:19 +08:00
swung0x48 641f54e1b7 [Feat] (MG_Backend/DirectVulkan): Transition texture for sampling 2026-03-06 15:37:14 +08:00
swung0x48 c36bfe0843 [Fix] (MG_Backend/DirectVulkan): track image layout naively 2026-03-06 14:53:43 +08:00
swung0x48 8ae66521e3 [Fix] (MG_Backend/DirectVulkan/VkRenderPassManager): early return when there's an active render pass that is compatible 2026-03-06 14:46:51 +08:00
swung0x48 6e21fd9a35 [Chore] (MG_Backend/DirectVulkan): a lotta assertions & transition uploaded texture directly to VK_ACCESS_SHADER_READ_BIT 2026-03-05 23:17:27 +08:00
swung0x48 a2e8faafe5 [Fix] (MG_Backend/DirectVulkan): upload buffer using fence, without vkQueueWaitIdle 2026-03-05 18:02:28 +08:00
swung0x48 668fa3033e [Optimize] (MG_Backend/DirectVulkan): store textureResources in permanent scratch space rather than temp memory 2026-03-05 09:44:03 +08:00
swung0x48 139f3f978d [Chore] (MG_Backend/DirectVulkan): get rid of unnecessary checks 2026-03-04 16:19:35 +08:00
swung0x48 09f96e5ba5 [Fix] (MG_Backend/DirectVulkan): Properly handle front face state. save VkDevice as static member 2026-03-04 15:08:23 +08:00
swung0x48 592a69b1c4 [Feat] (MG_Backend/DirectVulkan): integrating cullmode and frontface render state into vk pipeline 2026-03-03 17:01:02 +08:00
swung0x48 9211341ae7 [Chore] (MG_Backend/DirectVulkan): remove unused functions 2026-03-03 15:16:27 +08:00
swung0x48 5f9d354adb [Fix] (CMakeLists): add absent source file 2026-03-03 15:05:33 +08:00
swung0x48 0ae36b71f5 [Chore] (MG_Backend/DirectVulkan): move ClearAttachmentsOnActiveRenderPass to VulkanRenderer 2026-03-03 14:59:03 +08:00
swung0x48 20e2768f27 [Chore] (MG_Backend/DirectVulkan): move enum conversion to MG_Utils 2026-03-03 14:17:28 +08:00
swung0x48 00c8b5ae3a [Chore] (MG_Backend/DirectVulkan): get rid of unused functions 2026-03-03 13:38:06 +08:00
swung0x48 7687c19b33 [Fix] (MG_Backend/DirectVulkan): hook up TextureManager to TextureEnumConverter 2026-03-03 13:30:33 +08:00
swung0x48 fc3a7cd5f1 [Chore] (Config): Bump version to 26.03 2026-03-03 10:50:24 +08:00
swung0x48 0b7f906533 [Fix] (MG_Backend/DirectVulkan): use proper scissor state to fix menu 2026-03-03 09:43:25 +08:00
swung0x48 c78a94ed09 [Chore] (MG_Backend/DirectVulkan): use vkCmdClearAttachments to implement clear semantics when there's one compatible render pass in flight (instead of interrupting it) 2026-03-02 17:16:18 +08:00
swung0x48 debcb171ce [Chore] (MG_Backend/DirectVulkan): relaxing byteSize checks for textures in VkTextureManager 2026-03-02 14:55:55 +08:00
swung0x48 282468c9d5 [Fix] (MG_Backend/DirectVulkan): query clear color/state inside of RenderPassManager 2026-03-02 10:13:59 +08:00
swung0x48 34b06da83b [Fix] (MG_Backend/DirectVulkan): get ImageView from swapchain when creating render pass for default FBO 2026-03-02 09:49:32 +08:00
swung0x48 3d55f38dab [Fix] (ConfigLoader): still defaults to DirectGLES backend 2026-03-02 08:58:15 +08:00
swung0x48 16426db244 [Chore] (MG_Backend/DirectVulkan): manage active renderpass inside RenderPassManager static functions 2026-03-01 23:02:54 +08:00
swung0x48 6f8c76f06c [Fix] (MG_Backend/DirectVulkan): fixing Vulkan backend initialization 2026-03-01 22:39:30 +08:00
swung0x48 43f2043478 [Fix] (MG_Backend/DirectVulkan): Vulkan backend default framebuffer initialization 2026-03-01 21:21:32 +08:00
swung0x48 f61f068e28 [Fix] (MG_Backend/DirectVulkan): refactor VkRenderPassManager and hook it up (this commit only compiles and does not work) 2026-03-01 18:17:34 +08:00
swung0x48 e234d471fe [Fix] (MG_Backend/DirectVulkan): uninitialized vars in VkRenderPassManager 2026-02-28 18:00:31 +08:00
swung0x48 6ff97a9bb1 [Feat] (MG_Backend/DirectVulkan): new VkRenderPassManager and a bunch of changes accordingly 2026-02-28 17:37:45 +08:00
swung0x48 8adf1d3b8a [Chore] (MG_Backend/DirectVulkan): get rid of junk 2026-02-27 17:36:57 +08:00
swung0x48 2935279603 [Chore] (MG_Backend/DirectVulkan): remove "transition" stuff in VkRenderTargetManager 2026-02-27 16:34:09 +08:00
swung0x48 918b39bb63 [Fix] (MG_Backend/DirectVulkan): remove weird texture binding fallback 2026-02-27 16:22:19 +08:00
swung0x48 963e88943c [Chore] (MG_Backend/DirectVulkan): manage default depth buffer in SwapchainObject 2026-02-27 15:33:01 +08:00
swung0x48 1a75bcba5e [Fix] (MG_Backend/DirectVulkan): prevent operation in assert getting compiled out in release 2026-02-27 15:05:10 +08:00
swung0x48 b64309edfc [Chore] (MG_Backend/DirectVulkan): get rid of junk 2026-02-27 14:14:11 +08:00
swung0x48 e3f1db0a9a [Chore] (MG_Backend/DirectVulkan): use VkTextureManager::TransitionImageLayout more 2026-02-27 13:59:39 +08:00
swung0x48 2c848ab44b [Chore] (MG_Backend/DirectVulkan): move TransitionImageLayout to VkTextureManager 2026-02-27 09:44:23 +08:00
swung0x48 9b23594415 [Fix] (MG_Backend/DirectVulkan): misc fixes 2026-02-25 16:49:58 +08:00
swung0x48 e2e52965df [Feat] (MG_Backend/DirectVulkan): VkClearManager 2026-02-25 16:12:33 +08:00
swung0x48 6e7907f183 [Chore] (MG_Backend/DirectVulkan): get rid of more junk 2026-02-25 12:46:47 +08:00
swung0x48 b4be1292b8 [Feat] (MG_Backend/DirectVulkan): TextureManager mipmap completeness check 2026-02-25 11:04:28 +08:00
swung0x48 db58f4c214 [Feat] (MG_Backend/DirectVulkan): TextureManager supports mipmap 2026-02-25 10:52:54 +08:00
swung0x48 59bad77176 [Chore] (MG_Backend/DirectVulkan): rename functions in VkTextureManager 2026-02-25 09:52:15 +08:00
BZLZHH 3504fca8da Merge branch 'dev' into Perf/Improvement1 2026-02-24 22:48:23 +08:00
BZLZHH e61817ff6a [Fix] (MG_Backend/DirectGLES): Improve iteration safety in CollectGarbage. 2026-02-24 22:47:18 +08:00
BZLZHH b9df28838c Merge pull request #5 from Perf/Improvement1
Merge Perf/improvement1 to dev
2026-02-24 22:09:38 +08:00
BZLZHH fff00e65c4 [Perf] (Texture|State): Replace dynamic_cast hot paths with static_cast. 2026-02-24 15:40:06 +08:00
BZLZHH 8320b4f456 [Feat|Fix] (MG_Backend/DirectGLES): Unify state-backend registries with weak-ref GC. 2026-02-24 15:27:28 +08:00
swung0x48 a757669df5 [Improvement] (MG_Backend/DirectVulkan): split TextureSamplerManager into TextureManager and SamplerManager 2026-02-24 15:19:34 +08:00
swung0x48 e16c0645c0 [Improvement] (MG_Backend/DirectVulkan): use VMA to allocate texture images 2026-02-24 14:21:24 +08:00
swung0x48 8d37763fd3 [Improvement] (MG_Backend/DirectVulkan): use VMA to allocate RT images 2026-02-24 13:09:26 +08:00
swung0x48 e0d0551e7b [Chore] (MG_Test/Backend/DirectVulkan/TestExec): FramebufferManager -> RenderTargetManager 2026-02-24 13:07:50 +08:00
swung0x48 caa814e8f9 [Chore] (MG_Test/Backend/DirectVulkan/TestExec): get rid of now useless default renderpass 2026-02-24 12:25:04 +08:00
swung0x48 2502d32a2e [Chore] (MG_Test/Backend/DirectVulkan/TestExec): get rid of necessary check 2026-02-24 12:10:44 +08:00
swung0x48 e57dd3529e [Chore] (MG_Test/Backend/DirectVulkan/TestExec): get rid of junk 2026-02-24 10:19:21 +08:00
swung0x48 4b8d809237 [Fix] (MG_Test/Backend/DirectVulkan/TestExec): initialize MobileGL first then EGL stuff 2026-02-24 09:26:46 +08:00
swung0x48 d42182befc [Fix] (MG_Backend/DirectVulkan): swap swapchain width/height on 90/270 rotation 2026-02-23 23:12:22 +08:00
swung0x48 5efdb1016e [Fix] (MG_Backend/DirectVulkan): make vk_enum_string_helper.h available on Android NDK 2026-02-23 22:11:30 +08:00
BZLZHH 15e24cda78 [Perf|Improvement] (All): Improve performance & optimize code. 2026-02-23 16:00:38 +08:00
swung0x48 5013108a7f [Chore] (MG_Backend/DirectVulkan): get rid of unnecessary checks 2026-02-22 13:17:22 +08:00
swung0x48 359ba1c572 [Fix] (MG_Backend/DirectVulkan): properly compute hash for samplers 2026-02-22 12:51:00 +08:00
swung0x48 733523011b [Chore] (MG_Backend/DirectVulkan): more hard assertions 2026-02-22 12:31:18 +08:00
swung0x48 14fc13c34d [Chore] (MG_Backend/DirectVulkan): get rid of fallback image/sampler/imageview 2026-02-22 12:18:49 +08:00
swung0x48 e9fee1358d [Chore] (MG_Backend/DirectVulkan): hard assert instead of silently fallback 2026-02-22 12:08:31 +08:00
swung0x48 dae3dd9996 [Chore] (MG_Backend/DirectVulkan): unifying framebuffer manager Transition* functiosn 2026-02-22 11:08:43 +08:00
swung0x48 556db8b02e [Chore] (MG_Backend/DirectVulkan): get rid of redundant guardrails (cont.) 2026-02-22 10:35:10 +08:00
swung0x48 96e993eecf [Chore] (MG_Backend/DirectVulkan): get rid of redundant guardrails 2026-02-22 10:19:44 +08:00
swung0x48 fb3e2123cc [Fix] (MG_Backend/DirectVulkan): dynamically retrieve Vulkan functions in loader, fixing compilation error on lower NDK versions 2026-02-22 10:11:14 +08:00
135 changed files with 13571 additions and 13151 deletions
-2
View File
@@ -14,7 +14,6 @@ bugprone-forwarding-reference-overload,
bugprone-inaccurate-erase, bugprone-inaccurate-erase,
bugprone-incorrect-roundings, bugprone-incorrect-roundings,
bugprone-integer-division, bugprone-integer-division,
bugprone-lambda-function-name,
bugprone-macro-parentheses, bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects, bugprone-macro-repeated-side-effects,
bugprone-misplaced-operator-in-strlen-in-alloc, bugprone-misplaced-operator-in-strlen-in-alloc,
@@ -63,7 +62,6 @@ cert-str34-c,
cppcoreguidelines-interfaces-global-init, cppcoreguidelines-interfaces-global-init,
cppcoreguidelines-narrowing-conversions, cppcoreguidelines-narrowing-conversions,
cppcoreguidelines-pro-type-member-init, cppcoreguidelines-pro-type-member-init,
cppcoreguidelines-pro-type-static-cast-downcast,
cppcoreguidelines-slicing, cppcoreguidelines-slicing,
google-default-arguments, google-default-arguments,
google-runtime-operator, google-runtime-operator,
+9
View File
@@ -19,3 +19,12 @@
[submodule "3rdparty/VulkanMemoryAllocator"] [submodule "3rdparty/VulkanMemoryAllocator"]
path = 3rdparty/VulkanMemoryAllocator path = 3rdparty/VulkanMemoryAllocator
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
[submodule "3rdparty/Vulkan-Utility-Libraries"]
path = 3rdparty/Vulkan-Utility-Libraries
url = https://github.com/KhronosGroup/Vulkan-Utility-Libraries.git
[submodule "3rdparty/Vulkan-Headers"]
path = 3rdparty/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
[submodule "3rdparty/SPIRV-Reflect"]
path = 3rdparty/SPIRV-Reflect
url = https://github.com/KhronosGroup/SPIRV-Reflect.git
Vendored Submodule
+1
Submodule 3rdparty/SPIRV-Reflect added at 10b4f09a24
Vendored Submodule
+1
Submodule 3rdparty/Vulkan-Headers added at ad9ce1235e
+20 -3
View File
@@ -104,10 +104,20 @@ set(SPIRV_CROSS_ENABLE_CPP OFF CACHE BOOL "Disable C++ API target" FORCE)
set(SPIRV_CROSS_CLI OFF CACHE BOOL "Disable CLI binary" FORCE) set(SPIRV_CROSS_CLI OFF CACHE BOOL "Disable CLI binary" FORCE)
set(SPIRV_CROSS_STATIC ON CACHE BOOL "Prefer static libs" FORCE) set(SPIRV_CROSS_STATIC ON CACHE BOOL "Prefer static libs" FORCE)
set(SPIRV_REFLECT_EXECUTABLE OFF CACHE BOOL "Build spirv-reflect executable" FORCE)
set(SPIRV_REFLECT_STATIC_LIB ON CACHE BOOL "Build a SPIRV-Reflect static library" FORCE)
set(SPIRV_REFLECT_BUILD_TESTS OFF CACHE BOOL "Build the SPIRV-Reflect test suite" FORCE)
set(SPIRV_REFLECT_ENABLE_ASSERTS OFF CACHE BOOL "Enable asserts for debugging" FORCE)
set(SPIRV_REFLECT_ENABLE_ASAN OFF CACHE BOOL "Use address sanitization" FORCE)
set(SPIRV_REFLECT_INSTALL OFF CACHE BOOL "Whether to install" FORCE)
# add_subdirectory(3rdparty/DiligentCore) # add_subdirectory(3rdparty/DiligentCore)
add_subdirectory(3rdparty/glslang) add_subdirectory(3rdparty/glslang)
add_subdirectory(3rdparty/SPIRV-Cross) add_subdirectory(3rdparty/SPIRV-Cross)
add_subdirectory(3rdparty/VulkanMemoryAllocator) add_subdirectory(3rdparty/VulkanMemoryAllocator)
add_subdirectory(3rdparty/Vulkan-Headers)
add_subdirectory(3rdparty/Vulkan-Utility-Libraries)
add_subdirectory(3rdparty/SPIRV-Reflect)
set(XXHASH_BUILD_XXHSUM OFF) set(XXHASH_BUILD_XXHSUM OFF)
option(BUILD_SHARED_LIBS OFF) option(BUILD_SHARED_LIBS OFF)
@@ -160,6 +170,8 @@ set(SOURCE_FILES
MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp
MobileGL/MG_Util/Converters/GLToMG/ProgramEnumConverter.cpp MobileGL/MG_Util/Converters/GLToMG/ProgramEnumConverter.cpp
MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp MobileGL/MG_Util/Converters/MGToMG/TextureEnumConverter.cpp
MobileGL/MG_Util/Converters/MGToVk/RenderStateEnumConverter.cpp
MobileGL/MG_Util/Converters/MGToVk/TextureEnumConverter.cpp
MobileGL/MG_Util/Classifiers/TextureEnumClassifier.cpp MobileGL/MG_Util/Classifiers/TextureEnumClassifier.cpp
@@ -219,13 +231,16 @@ set(SOURCE_FILES
MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/BufferArena.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VkFramebufferManager.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.cpp
MobileGL/MG_State/GLState/Core.cpp MobileGL/MG_State/GLState/Core.cpp
MobileGL/MG_State/EGLState/Core.cpp MobileGL/MG_State/EGLState/Core.cpp
@@ -262,6 +277,8 @@ set(MOBILEGL_LINK_LIBRARIES
SPIRV-Tools SPIRV-Tools
xxHash::xxhash xxHash::xxhash
GPUOpen::VulkanMemoryAllocator GPUOpen::VulkanMemoryAllocator
Vulkan::UtilityHeaders
spirv-reflect-static
) )
set(MOBILEGL_COMPILE_DEF set(MOBILEGL_COMPILE_DEF
+1 -1
View File
@@ -14,7 +14,7 @@ namespace MobileGL::MG_Config {
inline const String ProjectName = "MobileGL"; inline const String ProjectName = "MobileGL";
inline const String CoreName = "MobileGL Core"; inline const String CoreName = "MobileGL Core";
inline const String CoreVendor = "MobileGL-Dev (BZLZHH, Swung0x48, Tungsten)"; inline const String CoreVendor = "MobileGL-Dev (BZLZHH, Swung0x48, Tungsten)";
inline const Version CoreVersion = {26, 2, 0, "-dev", VersionType::Development}; inline const Version CoreVersion = {26, 3, 0, "-dev", VersionType::Development};
inline const VersionStringFormatAttrib DefaultVersionStringFormatAttrib = {2, 2, 0, true, true}; inline const VersionStringFormatAttrib DefaultVersionStringFormatAttrib = {2, 2, 0, true, true};
inline const Uint64 CacheVersion = 0; inline const Uint64 CacheVersion = 0;
+1 -1
View File
@@ -34,7 +34,7 @@
#define MOBILEGL_EGL_API MOBILEGL_API #define MOBILEGL_EGL_API MOBILEGL_API
// ====================== MobileGL configurations ======================= // // ====================== MobileGL configurations ======================= //
#define MOBILEGL_LOG_ACTIVE_LEVEL MOBILEGL_LOG_LEVEL_INFO #define MOBILEGL_LOG_ACTIVE_LEVEL MOBILEGL_LOG_LEVEL_DEBUG
#define MOBILEGL_LOG_ENABLE_CONSOLE 0 #define MOBILEGL_LOG_ENABLE_CONSOLE 0
#define MOBILEGL_LOG_ENABLE_FILE 1 #define MOBILEGL_LOG_ENABLE_FILE 1
+4 -4
View File
@@ -34,10 +34,10 @@ namespace MobileGL {
void Destroy() { void Destroy() {
MGLOG_I("MobileGL closing..."); MGLOG_I("MobileGL closing...");
glslang::FinalizeProcess(); glslang::FinalizeProcess();
delete MG_State::pGLContext; MG_State::pGLContext.reset();
delete MG_State::pEGLContext; MG_State::pEGLContext.reset();
delete MG_Impl::GLImpl::TextureImpl::pProxyTextureManager; MG_Impl::GLImpl::TextureImpl::pProxyTextureManager.reset();
delete MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo; MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo.reset();
MG_Util::Debug::Close(); MG_Util::Debug::Close();
// TODO: add and use Destroy functions for other subsystems // TODO: add and use Destroy functions for other subsystems
+146 -145
View File
@@ -8,6 +8,7 @@
#include "DirectGLES.h" #include "DirectGLES.h"
#include "EGL/egl.h" #include "EGL/egl.h"
#include "MG_Util/Types.h"
#include "Utils.h" #include "Utils.h"
#include "Managers.h" #include "Managers.h"
#include <MG_Util/Converters/GLToMG/TextureEnumConverter.h> #include <MG_Util/Converters/GLToMG/TextureEnumConverter.h>
@@ -45,7 +46,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
namespace DebugImpl { namespace DebugImpl {
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
void ErrorLopper::Loop(std::function<void(GLenum)> func) { void ErrorLopper::Loop(const std::function<void(GLenum)>& func) {
GLenum err = g_GLESFuncs.glGetError(); GLenum err = g_GLESFuncs.glGetError();
while (err != GL_NO_ERROR) { while (err != GL_NO_ERROR) {
func(err); func(err);
@@ -68,14 +69,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
Clear(); Clear();
} }
#else #else
void ErrorLopper::Loop(std::function<void(GLenum)> func) {} void ErrorLopper::Loop(const std::function<void(GLenum)>& func) {}
void ErrorLopper::Clear() {} void ErrorLopper::Clear() {}
ErrorLopper::ErrorLopper() {} ErrorLopper::ErrorLopper() = default;
ErrorLopper::~ErrorLopper() {} ErrorLopper::~ErrorLopper() = default;
#endif #endif
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
OpenGLScopeMarker::OpenGLScopeMarker(String scopeName) { OpenGLScopeMarker::OpenGLScopeMarker(const String& scopeName) {
g_GLESFuncs.glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, scopeName.c_str()); g_GLESFuncs.glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, scopeName.c_str());
} }
@@ -83,7 +84,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glPopDebugGroup(); g_GLESFuncs.glPopDebugGroup();
} }
#else #else
OpenGLScopeMarker::OpenGLScopeMarker(String scopeName) {} OpenGLScopeMarker::OpenGLScopeMarker(const String& scopeName) {}
OpenGLScopeMarker::~OpenGLScopeMarker() {} OpenGLScopeMarker::~OpenGLScopeMarker() {}
#endif #endif
@@ -92,31 +93,28 @@ namespace MobileGL::MG_Backend::DirectGLES {
// TODO: deletion for deleted objects // TODO: deletion for deleted objects
namespace BufferImpl { namespace BufferImpl {
void CreateAndSyncBufferObject(SharedPtr<MG_State::GLState::BufferObject>& bufferObject) { void CreateAndSyncBufferObject(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject) {
if (!(bufferObject->GetChangeBits() & BufferChangeBits::DirtyBit)) return; if (!(bufferObject->GetChangeBits() & BufferChangeBits::DirtyBit)) return;
const auto& backendBufferIt = g_backendBufferObjects.find(bufferObject); const auto& backendBufferIt = g_backendBufferObjects.find(bufferObject.get());
SharedPtr<BackendBufferObject> backendBufferObject; Bool exist = (backendBufferIt != g_backendBufferObjects.end());
if (backendBufferIt == g_backendBufferObjects.end()) { auto& backendObj = exist ? backendBufferIt->second : g_backendBufferObjects.GetOrCreate(bufferObject);
backendBufferObject = MakeShared<BackendBufferObject>(); if (!exist) {
g_backendBufferObjects[bufferObject] = backendBufferObject; backendObj = MakeShared<BackendBufferObject>();
} else {
backendBufferObject = backendBufferIt->second;
} }
backendBufferObject->SyncToBackend(bufferObject); backendObj->SyncToBackend(bufferObject);
} }
void SyncNeccessaryBuffers(Bool includeIBO = false, Bool includeIndirectBuffer = false) { void SyncNeccessaryBuffers(Bool includeIBO = false, Bool includeIndirectBuffer = false) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
g_backendBufferObjects.CollectGarbageIfNeeded();
// All buffers we need are: // All buffers we need are:
// 1.VBO 2.IBO (if needed) 3.UBO 4.IndirectBuffer (if needed) 5.SSBO (TODO) // 1.VBO 2.IBO (if needed) 3.UBO 4.IndirectBuffer (if needed) 5.SSBO (TODO)
// PBO is not needed since it should be handled in frontend // PBO is not needed since it should be handled in frontend
// static Vector<SharedPtr<MG_State::GLState::BufferObject>> buffersToSync;
// buffersToSync.clear();
const auto& currentVAOObject = MG_State::pGLContext->GetBoundVertexArray(); const auto& currentVAOObject = MG_State::pGLContext->GetBoundVertexArray();
if (!currentVAOObject) { if (!currentVAOObject) {
MGLOG_E("No VAO is currently bound, cannot sync necessary buffers."); MGLOG_E("No VAO is currently bound, cannot sync necessary buffers.");
@@ -126,7 +124,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// VBO // VBO
for (const auto& attrib : currentVAOObject->GetAllAttributes()) { for (const auto& attrib : currentVAOObject->GetAllAttributes()) {
if (!attrib.Enabled) continue; if (!attrib.Enabled) continue;
auto bufferObject = attrib.Buffer; auto& bufferObject = attrib.Buffer;
if (bufferObject) { if (bufferObject) {
CreateAndSyncBufferObject(bufferObject); CreateAndSyncBufferObject(bufferObject);
} }
@@ -134,7 +132,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// IBO // IBO
if (includeIBO) { if (includeIBO) {
auto possibleIBO = currentVAOObject->GetIndexBufferBindingSlot().GetBoundObject(); auto& possibleIBO = currentVAOObject->GetIndexBufferBindingSlot().GetBoundObject();
if (possibleIBO) { if (possibleIBO) {
CreateAndSyncBufferObject(possibleIBO); CreateAndSyncBufferObject(possibleIBO);
} }
@@ -142,7 +140,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Indirect Buffer Object // Indirect Buffer Object
if (includeIndirectBuffer) { if (includeIndirectBuffer) {
auto possibleIndirectBuffer = auto& possibleIndirectBuffer =
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject(); MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
if (possibleIndirectBuffer) { if (possibleIndirectBuffer) {
CreateAndSyncBufferObject(possibleIndirectBuffer); CreateAndSyncBufferObject(possibleIndirectBuffer);
@@ -153,7 +151,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto uboBindingPointCnt = MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::Uniform); auto uboBindingPointCnt = MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::Uniform);
for (SizeT i = 0; i < uboBindingPointCnt; ++i) { for (SizeT i = 0; i < uboBindingPointCnt; ++i) {
auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, i); auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, i);
auto obj = point.GetBoundObject(); auto& obj = point.GetBoundObject();
if (obj) { if (obj) {
CreateAndSyncBufferObject(obj); CreateAndSyncBufferObject(obj);
} }
@@ -166,48 +164,49 @@ namespace MobileGL::MG_Backend::DirectGLES {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
auto currentVAOObject = MG_State::pGLContext->GetBoundVertexArray(); g_backendVertexArrayObjects.CollectGarbageIfNeeded();
auto& currentVAOObject = MG_State::pGLContext->GetBoundVertexArray();
if (!currentVAOObject) { if (!currentVAOObject) {
MGLOG_E("No VAO is currently bound, cannot sync current VAO."); MGLOG_E("No VAO is currently bound, cannot sync current VAO.");
return; return;
} }
const auto& backendVAOIt = g_backendVertexArrayObjects.find(currentVAOObject); const auto& backendVAOIt = g_backendVertexArrayObjects.find(currentVAOObject.get());
SharedPtr<VertexArrayImpl::BackendVertexArrayObject> backendVAOObject; Bool exist = (backendVAOIt != g_backendVertexArrayObjects.end());
if (backendVAOIt == g_backendVertexArrayObjects.end()) { auto& backendObj = exist ? backendVAOIt->second : g_backendVertexArrayObjects.GetOrCreate(currentVAOObject);
backendVAOObject = MakeShared<VertexArrayImpl::BackendVertexArrayObject>(); if (!exist) {
g_backendVertexArrayObjects[currentVAOObject] = backendVAOObject; backendObj = MakeShared<VertexArrayImpl::BackendVertexArrayObject>();
} else {
backendVAOObject = backendVAOIt->second;
} }
backendVAOObject->SyncToBackend(currentVAOObject); backendObj->SyncToBackend(currentVAOObject);
} }
} // namespace VertexArrayImpl } // namespace VertexArrayImpl
namespace TextureImpl { namespace TextureImpl {
SharedPtr<BackendTextureObject> SyncTextureObjectToBackend( SharedPtr<BackendTextureObject>& SyncTextureObjectToBackend(
SharedPtr<MG_State::GLState::ITextureObject>& textureObject) { const SharedPtr<MG_State::GLState::ITextureObject>& textureObject) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
const auto& backendTextureIt = g_backendTextureObjects.find(textureObject); const auto& backendTextureIt = g_backendTextureObjects.find(textureObject.get());
SharedPtr<BackendTextureObject> backendTextureObject; Bool exist = (backendTextureIt != g_backendTextureObjects.end());
if (backendTextureIt == g_backendTextureObjects.end()) { auto& backendObj = exist ? backendTextureIt->second : g_backendTextureObjects.GetOrCreate(textureObject);
backendTextureObject = MakeShared<BackendTextureObject>(); if (!exist) {
g_backendTextureObjects[textureObject] = backendTextureObject; backendObj = MakeShared<BackendTextureObject>();
} else {
backendTextureObject = backendTextureIt->second;
} }
backendTextureObject->SyncTextureParamsToBackend(textureObject); backendObj->SyncTextureParamsToBackend(textureObject);
backendTextureObject->SyncBuiltinSamplerToBackend(textureObject); backendObj->SyncBuiltinSamplerToBackend(textureObject);
backendTextureObject->SyncMipmapsToBackend(textureObject); backendObj->SyncMipmapsToBackend(textureObject);
return backendTextureObject;
return backendObj;
} }
void SyncNeccessaryTextures() { void SyncNeccessaryTextures() {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
g_backendTextureObjects.CollectGarbageIfNeeded();
// All textures we need are: // All textures we need are:
// 1. textures bound to texture units (TODO: only sync ones that are used in current program) // 1. textures bound to texture units (TODO: only sync ones that are used in current program)
// 2. textures used in current FBO // 2. textures used in current FBO
@@ -216,7 +215,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
for (int index = 0; index < MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; ++index) { for (int index = 0; index < MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; ++index) {
auto& unit = MG_State::pGLContext->GetTextureUnitObject(index); auto& unit = MG_State::pGLContext->GetTextureUnitObject(index);
for (const auto& bindingSlot : unit.GetAllBindingSlots()) { for (const auto& bindingSlot : unit.GetAllBindingSlots()) {
auto textureObject = bindingSlot.GetBoundObject(); auto& textureObject = bindingSlot.GetBoundObject();
if (textureObject) { if (textureObject) {
SyncTextureObjectToBackend(textureObject); SyncTextureObjectToBackend(textureObject);
} }
@@ -228,7 +227,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (currentFBO) { if (currentFBO) {
for (const auto& attachment : currentFBO->GetAllAttachmentObjects()) { for (const auto& attachment : currentFBO->GetAllAttachmentObjects()) {
if (!attachment.IsTexture()) continue; if (!attachment.IsTexture()) continue;
auto textureObject = attachment.GetTexture(); auto& textureObject = attachment.GetTexture();
if (textureObject) { if (textureObject) {
SyncTextureObjectToBackend(textureObject); SyncTextureObjectToBackend(textureObject);
} }
@@ -242,16 +241,20 @@ namespace MobileGL::MG_Backend::DirectGLES {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
g_backendFramebufferObjects.CollectGarbageIfNeeded();
TextureImpl::g_backendTextureObjects.CollectGarbageIfNeeded();
RenderbufferImpl::g_backendRenderbufferObjects.CollectGarbageIfNeeded();
const FramebufferTarget fboTargets[] = {FramebufferTarget::Draw, FramebufferTarget::Read}; const FramebufferTarget fboTargets[] = {FramebufferTarget::Draw, FramebufferTarget::Read};
MG_State::GLState::FramebufferObject* lastUpdatedFBO = nullptr; MG_State::GLState::FramebufferObject* lastUpdatedFBO = nullptr;
for (auto target : fboTargets) { for (auto& target : fboTargets) {
auto slot = MG_State::pGLContext->GetFramebufferBindingSlot(target); auto& slot = MG_State::pGLContext->GetFramebufferBindingSlot(target);
auto version = slot.GetVersion(); auto version = slot.GetVersion();
if (version == g_fboBindVersions[SizeT(target)]) continue; if (version == g_fboBindVersions[SizeT(target)]) continue;
auto currentFBO = slot.GetBoundObject(); auto& currentFBO = slot.GetBoundObject();
if (!currentFBO) { if (!currentFBO) {
MGLOG_E("No FBO is currently bound, cannot sync current FBO."); MGLOG_E("No FBO is currently bound, cannot sync current FBO.");
@@ -263,21 +266,19 @@ namespace MobileGL::MG_Backend::DirectGLES {
continue; continue;
} }
const auto& backendFBOIt = g_backendFramebufferObjects.find(currentFBO);
SharedPtr<BackendFramebufferObject> backendFBOObject;
if (backendFBOIt == g_backendFramebufferObjects.end()) {
backendFBOObject = MakeShared<BackendFramebufferObject>();
g_backendFramebufferObjects[currentFBO] = backendFBOObject;
} else {
backendFBOObject = backendFBOIt->second;
}
if (currentFBO.get() == lastUpdatedFBO) { if (currentFBO.get() == lastUpdatedFBO) {
MGLOG_D("Draw FBO and read FBO are the same, skipping sync."); MGLOG_D("Draw FBO and read FBO are the same, skipping sync.");
} else { continue;
backendFBOObject->SyncToBackend(currentFBO, target);
} }
const auto& backendFBOIt = g_backendFramebufferObjects.find(currentFBO.get());
Bool exist = (backendFBOIt != g_backendFramebufferObjects.end());
auto& backendObj = exist ? backendFBOIt->second : g_backendFramebufferObjects.GetOrCreate(currentFBO);
if (!exist) {
backendObj = MakeShared<BackendFramebufferObject>();
}
backendObj->SyncToBackend(currentFBO, target);
lastUpdatedFBO = currentFBO.get(); lastUpdatedFBO = currentFBO.get();
} }
} }
@@ -466,21 +467,23 @@ namespace MobileGL::MG_Backend::DirectGLES {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
auto currentProgram = MG_State::pGLContext->GetCurrentProgram(); g_backendProgramObjects.CollectGarbageIfNeeded();
SamplerImpl::g_backendSamplerObjects.CollectGarbageIfNeeded();
auto& currentProgram = MG_State::pGLContext->GetCurrentProgram();
if (!currentProgram || !currentProgram->GetLinkStatus()) { if (!currentProgram || !currentProgram->GetLinkStatus()) {
g_GLESFuncs.glUseProgram(0); g_GLESFuncs.glUseProgram(0);
return; return;
} }
const auto& backendProgramIt = g_backendProgramObjects.find(currentProgram); const auto& backendProgramIt = g_backendProgramObjects.find(currentProgram.get());
SharedPtr<BackendProgramObjectImpl> backendProgram; Bool exist = (backendProgramIt != g_backendProgramObjects.end());
if (backendProgramIt == g_backendProgramObjects.end()) { auto& backendObj = exist ? backendProgramIt->second : g_backendProgramObjects.GetOrCreate(currentProgram);
backendProgram = MakeShared<BackendProgramObjectImpl>(); if (!exist) {
g_backendProgramObjects[currentProgram] = backendProgram; backendObj = MakeShared<BackendProgramObjectImpl>();
backendProgram->SyncToBackend(currentProgram); backendObj->SyncToBackend(currentProgram);
} else { } else {
backendProgram = backendProgramIt->second; if (!backendObj->GetBackendProgramId()) {
if (!backendProgram->GetBackendProgramId()) { backendObj->SyncToBackend(currentProgram);
backendProgram->SyncToBackend(currentProgram);
} }
} }
} }
@@ -495,7 +498,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const auto& currentFBO = slot.GetBoundObject(); const auto& currentFBO = slot.GetBoundObject();
if (currentFBO && currentFBO != MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) { if (currentFBO && currentFBO != MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) {
const auto& backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find(currentFBO); const auto& backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find(currentFBO.get());
if (backendFBOIt != FramebufferImpl::g_backendFramebufferObjects.end()) { if (backendFBOIt != FramebufferImpl::g_backendFramebufferObjects.end()) {
backendFBOIt->second->Bind(target); backendFBOIt->second->Bind(target);
} else { } else {
@@ -528,7 +531,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
#endif #endif
const auto& currentVAO = MG_State::pGLContext->GetBoundVertexArray(); const auto& currentVAO = MG_State::pGLContext->GetBoundVertexArray();
if (currentVAO) { if (currentVAO) {
const auto& backendVAOIt = VertexArrayImpl::g_backendVertexArrayObjects.find(currentVAO); const auto& backendVAOIt = VertexArrayImpl::g_backendVertexArrayObjects.find(currentVAO.get());
if (backendVAOIt != VertexArrayImpl::g_backendVertexArrayObjects.end()) { if (backendVAOIt != VertexArrayImpl::g_backendVertexArrayObjects.end()) {
backendVAOIt->second->Bind(); backendVAOIt->second->Bind();
} }
@@ -556,7 +559,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_Util::ConvertTextureTargetToString(target).c_str()); MG_Util::ConvertTextureTargetToString(target).c_str());
continue; continue;
} }
const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject.get());
if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) continue; if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) continue;
GLenum targetGL = MG_Util::ConvertTextureTargetToGLEnum(target); GLenum targetGL = MG_Util::ConvertTextureTargetToGLEnum(target);
@@ -566,7 +569,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Bind sampler object if necessary // Bind sampler object if necessary
const auto& samplerObject = textureUnit.GetSamplerObject(); const auto& samplerObject = textureUnit.GetSamplerObject();
if (samplerObject) { if (samplerObject) {
const auto& backendSamplerIt = SamplerImpl::g_backendSamplerObjects.find(samplerObject); const auto& backendSamplerIt = SamplerImpl::g_backendSamplerObjects.find(samplerObject.get());
if (backendSamplerIt != SamplerImpl::g_backendSamplerObjects.end()) { if (backendSamplerIt != SamplerImpl::g_backendSamplerObjects.end()) {
backendSamplerIt->second->Bind(unit); backendSamplerIt->second->Bind(unit);
} }
@@ -581,7 +584,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedNC("BindCurrentProgram", TRACY_ZONECOLOR_BACKEND); ZoneScopedNC("BindCurrentProgram", TRACY_ZONECOLOR_BACKEND);
#endif #endif
const auto& backendProgramIt = PrgramImpl::g_backendProgramObjects.find(currentProgram); const auto& backendProgramIt = PrgramImpl::g_backendProgramObjects.find(currentProgram.get());
if (backendProgramIt != PrgramImpl::g_backendProgramObjects.end()) { if (backendProgramIt != PrgramImpl::g_backendProgramObjects.end()) {
backendProgramIt->second->Use(); backendProgramIt->second->Use();
auto backendProgramId = backendProgramIt->second->GetBackendProgramId(); auto backendProgramId = backendProgramIt->second->GetBackendProgramId();
@@ -623,11 +626,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Connect buffer to backend binding point // Connect buffer to backend binding point
auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, binding); auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, binding);
auto bufferObj = point.GetBoundObject(); auto& bufferObj = point.GetBoundObject();
auto range = point.GetRange(); auto range = point.GetRange();
if (bufferObj) { if (bufferObj) {
const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(bufferObj); const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(bufferObj.get());
if (backendBufferIt != BufferImpl::g_backendBufferObjects.end()) { if (backendBufferIt != BufferImpl::g_backendBufferObjects.end()) {
const auto& backendBufferObject = backendBufferIt->second; const auto& backendBufferObject = backendBufferIt->second;
backendBufferObject->Bind(GL_UNIFORM_BUFFER); backendBufferObject->Bind(GL_UNIFORM_BUFFER);
@@ -635,9 +638,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glBindBufferBase(GL_UNIFORM_BUFFER, lastUBOBinding, g_GLESFuncs.glBindBufferBase(GL_UNIFORM_BUFFER, lastUBOBinding,
backendBufferObject->GetBackendBufferId()); backendBufferObject->GetBackendBufferId());
} else { } else {
g_GLESFuncs.glBindBufferRange(GL_UNIFORM_BUFFER, lastUBOBinding, g_GLESFuncs.glBindBufferRange(
backendBufferObject->GetBackendBufferId(), GL_UNIFORM_BUFFER, lastUBOBinding, backendBufferObject->GetBackendBufferId(),
range.start, range.end - range.start); (GLintptr)range.start, (GLintptr)(range.end - range.start));
} }
} else { } else {
MGLOG_E("No backend buffer found for UBO binding, cannot bind UBO."); MGLOG_E("No backend buffer found for UBO binding, cannot bind UBO.");
@@ -660,18 +663,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
backendProgramIt->second->GetBackendProgramId(), name.c_str()); backendProgramIt->second->GetBackendProgramId(), name.c_str());
g_GLESFuncs.glUniform1i(locAtBackend, unit); g_GLESFuncs.glUniform1i(locAtBackend, unit);
auto samplerObject = MG_State::pGLContext->GetTextureUnitObject(unit).GetSamplerObject(); auto& samplerObject = MG_State::pGLContext->GetTextureUnitObject(unit).GetSamplerObject();
if (samplerObject) { if (samplerObject) {
const auto& backendSamplerIt = SamplerImpl::g_backendSamplerObjects.find(samplerObject); const auto& backendSamplerIt =
SharedPtr<SamplerImpl::BackendSamplerObject> backendSamplerObject; SamplerImpl::g_backendSamplerObjects.find(samplerObject.get());
if (backendSamplerIt == SamplerImpl::g_backendSamplerObjects.end()) { Bool exist = (backendSamplerIt != SamplerImpl::g_backendSamplerObjects.end());
backendSamplerObject = MakeShared<SamplerImpl::BackendSamplerObject>(); auto& backendObj = exist ? backendSamplerIt->second
SamplerImpl::g_backendSamplerObjects[samplerObject] = backendSamplerObject; : SamplerImpl::g_backendSamplerObjects.GetOrCreate(samplerObject);
} else { if (!exist) {
backendSamplerObject = backendSamplerIt->second; backendObj = MakeShared<SamplerImpl::BackendSamplerObject>();
} }
backendSamplerObject->SyncToBackend(samplerObject); backendObj->SyncToBackend(samplerObject);
} else { } else {
SamplerImpl::UnbindSampler(unit); SamplerImpl::UnbindSampler(unit);
} }
@@ -845,30 +848,29 @@ namespace MobileGL::MG_Backend::DirectGLES {
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
DebugImpl::OpenGLScopeMarker marker(__func__); DebugImpl::OpenGLScopeMarker marker(__func__);
#endif #endif
DebugImpl::ErrorLopper errorLopper;
TextureImpl::SyncNeccessaryTextures(); TextureImpl::SyncNeccessaryTextures();
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
FramebufferImpl::SyncCurrentFBO(); FramebufferImpl::SyncCurrentFBO();
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
RenderStateImpl::SyncRenderState(); RenderStateImpl::SyncRenderState();
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
BindCurrentFBO(FramebufferTarget::Draw); BindCurrentFBO(FramebufferTarget::Draw);
BindCurrentFBO(FramebufferTarget::Read); BindCurrentFBO(FramebufferTarget::Read);
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
MGLOG_D("ES %s(%d, %d, %d, %d, %d, %d, %d, %d, 0x%x, %s)", __func__, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, MGLOG_D("ES %s(%d, %d, %d, %d, %d, %d, %d, %d, 0x%x, %s)", __func__, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0,
dstX1, dstY1, mask, MG_Util::ConvertGLEnumToString(filter).c_str()); dstX1, dstY1, mask, MG_Util::ConvertGLEnumToString(filter).c_str());
g_GLESFuncs.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); g_GLESFuncs.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
} }
@@ -895,15 +897,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
} }
const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject.get());
SharedPtr<TextureImpl::BackendTextureObject> backendTextureObject; Bool exist = (backendTextureIt != TextureImpl::g_backendTextureObjects.end());
if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { auto& backendObj =
backendTextureObject = MakeShared<TextureImpl::BackendTextureObject>(); exist ? backendTextureIt->second : TextureImpl::g_backendTextureObjects.GetOrCreate(textureObject);
TextureImpl::g_backendTextureObjects[textureObject] = backendTextureObject; if (!exist) {
} else { backendObj = MakeShared<TextureImpl::BackendTextureObject>();
backendTextureObject = backendTextureIt->second;
} }
backendTextureObject->Bind(target, unit); backendObj->Bind(target, unit);
} }
return true; return true;
} }
@@ -953,15 +954,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
DebugImpl::ErrorLopper errorLopper; DebugImpl::ErrorLopper errorLopper;
MGLOG_D("%s: Backend", __func__); MGLOG_D("%s: Backend", __func__);
TextureImpl::SyncNeccessaryTextures(); TextureImpl::SyncNeccessaryTextures();
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
FramebufferImpl::SyncCurrentFBO(); FramebufferImpl::SyncCurrentFBO();
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
RenderStateImpl::SyncRenderState(); RenderStateImpl::SyncRenderState();
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
if (!UpdateTextureBindingAtTarget(target)) return; if (!UpdateTextureBindingAtTarget(target)) return;
@@ -969,10 +970,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Bind necessary FBO and texture // Bind necessary FBO and texture
BindCurrentFBO(FramebufferTarget::Read); BindCurrentFBO(FramebufferTarget::Read);
Uint activeTextureUnit = MG_State::pGLContext->GetActiveTextureUnit(); Uint activeTextureUnit = MG_State::pGLContext->GetActiveTextureUnit();
const auto& textureObject = MG_State::pGLContext->GetTextureUnitObject(activeTextureUnit) const auto& textureObject = MG_State::pGLContext->GetTextureUnitObject((Int)activeTextureUnit)
.GetBindingSlot(MG_Util::ConvertGLEnumToTextureTarget(target)) .GetBindingSlot(MG_Util::ConvertGLEnumToTextureTarget(target))
.GetBoundObject(); .GetBoundObject();
const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject.get());
if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) {
MGLOG_E("CopyTexSubImage2D: No backend texture found for texture %u.", MGLOG_E("CopyTexSubImage2D: No backend texture found for texture %u.",
textureObject ? textureObject->GetExternalIndex() : 0); textureObject ? textureObject->GetExternalIndex() : 0);
@@ -998,19 +999,19 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (!isDepthFormat) { if (!isDepthFormat) {
g_GLESFuncs.glCopyTexImage2D(target, level, internalformat, x, y, width, height, border); g_GLESFuncs.glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
} else { } else {
MGLOG_D("%s: Backend depth", __func__); MGLOG_D("%s: Backend depth", __func__);
g_GLESFuncs.glTexImage2D(target, level, (GLint)internalformat, width, height, border, format, type, g_GLESFuncs.glTexImage2D(target, level, (GLint)internalformat, width, height, border, format, type,
nullptr); nullptr);
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
GLint currentTex = backendTextureIt->second->GetBackendTextureId(); auto currentTex = (GLint)backendTextureIt->second->GetBackendTextureId();
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
@@ -1026,7 +1027,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glBlitFramebuffer(x, y, x + width, y + height, 0, 0, width, height, g_GLESFuncs.glBlitFramebuffer(x, y, x + width, y + height, 0, 0, width, height,
GL_DEPTH_BUFFER_BIT | (isStencilFormat ? GL_STENCIL_BUFFER_BIT : 0), GL_DEPTH_BUFFER_BIT | (isStencilFormat ? GL_STENCIL_BUFFER_BIT : 0),
GL_NEAREST); GL_NEAREST);
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
} }
@@ -1041,15 +1042,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("%s: Backend", __func__); MGLOG_D("%s: Backend", __func__);
TextureImpl::SyncNeccessaryTextures(); TextureImpl::SyncNeccessaryTextures();
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
FramebufferImpl::SyncCurrentFBO(); FramebufferImpl::SyncCurrentFBO();
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
RenderStateImpl::SyncRenderState(); RenderStateImpl::SyncRenderState();
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
@@ -1057,11 +1058,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Bind necessary FBO and texture // Bind necessary FBO and texture
BindCurrentFBO(FramebufferTarget::Read); BindCurrentFBO(FramebufferTarget::Read);
Uint activeTextureUnit = MG_State::pGLContext->GetActiveTextureUnit(); auto activeTextureUnit = MG_State::pGLContext->GetActiveTextureUnit();
const auto& textureObject = MG_State::pGLContext->GetTextureUnitObject(activeTextureUnit) const auto& textureObject = MG_State::pGLContext->GetTextureUnitObject(activeTextureUnit)
.GetBindingSlot(MG_Util::ConvertGLEnumToTextureTarget(target)) .GetBindingSlot(MG_Util::ConvertGLEnumToTextureTarget(target))
.GetBoundObject(); .GetBoundObject();
const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject.get());
if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) {
MGLOG_E("CopyTexSubImage2D: No backend texture found for texture %u.", MGLOG_E("CopyTexSubImage2D: No backend texture found for texture %u.",
textureObject ? textureObject->GetExternalIndex() : 0); textureObject ? textureObject->GetExternalIndex() : 0);
@@ -1069,12 +1070,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
backendTextureIt->second->Bind(target, activeTextureUnit); backendTextureIt->second->Bind(target, activeTextureUnit);
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
GLenum internalFormat; GLenum internalFormat;
g_GLESFuncs.glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, (GLint*)&internalFormat); g_GLESFuncs.glGetTexLevelParameteriv(target, level, GL_TEXTURE_INTERNAL_FORMAT, (GLint*)&internalFormat);
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
auto mgInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalFormat); auto mgInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalFormat);
@@ -1084,19 +1085,19 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (!isDepthFormat) { if (!isDepthFormat) {
g_GLESFuncs.glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); g_GLESFuncs.glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
} else { } else {
MGLOG_D("%s: Backend depth", __func__); MGLOG_D("%s: Backend depth", __func__);
GLint currentTex = backendTextureIt->second->GetBackendTextureId(); auto currentTex = backendTextureIt->second->GetBackendTextureId();
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
GLenum attachment = isStencilFormat ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT; GLenum attachment = isStencilFormat ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
TempFBOBinder tempFBOBinder(false); TempFBOBinder tempFBOBinder(false);
g_GLESFuncs.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target, currentTex, level); g_GLESFuncs.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target, currentTex, level);
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
if (g_GLESFuncs.glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { if (g_GLESFuncs.glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
@@ -1107,7 +1108,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glBlitFramebuffer( g_GLESFuncs.glBlitFramebuffer(
x, y, x + width, y + height, xoffset, yoffset, xoffset + width, yoffset + height, x, y, x + width, y + height, xoffset, yoffset, xoffset + width, yoffset + height,
GL_DEPTH_BUFFER_BIT | (isStencilFormat ? GL_STENCIL_BUFFER_BIT : 0), GL_NEAREST); GL_DEPTH_BUFFER_BIT | (isStencilFormat ? GL_STENCIL_BUFFER_BIT : 0), GL_NEAREST);
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
} }
@@ -1120,8 +1121,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto unitIndex = MG_State::pGLContext->GetActiveTextureUnit(); auto unitIndex = MG_State::pGLContext->GetActiveTextureUnit();
auto& unit = MG_State::pGLContext->GetTextureUnitObject(unitIndex); auto& unit = MG_State::pGLContext->GetTextureUnitObject(unitIndex);
auto& slot = unit.GetBindingSlot(MG_Util::ConvertGLEnumToTextureTarget(target)); auto& slot = unit.GetBindingSlot(MG_Util::ConvertGLEnumToTextureTarget(target));
auto texture = slot.GetBoundObject(); auto& texture = slot.GetBoundObject();
auto backendTexture = TextureImpl::SyncTextureObjectToBackend(texture); auto& backendTexture = TextureImpl::SyncTextureObjectToBackend(texture);
backendTexture->Bind(target, unitIndex); backendTexture->Bind(target, unitIndex);
g_GLESFuncs.glGenerateMipmap(target); g_GLESFuncs.glGenerateMipmap(target);
@@ -1184,7 +1185,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
PixelStoreParameters m_prevParams; PixelStoreParameters m_prevParams;
PixelStoreParameters QueryCurrentGLPixelStoreParams(Bool isUnpack) { static PixelStoreParameters QueryCurrentGLPixelStoreParams(Bool isUnpack) {
PixelStoreParameters p; PixelStoreParameters p;
if (!isUnpack) { if (!isUnpack) {
g_GLESFuncs.glGetIntegerv(GL_PACK_ALIGNMENT, (GLint*)&p.Alignment); g_GLESFuncs.glGetIntegerv(GL_PACK_ALIGNMENT, (GLint*)&p.Alignment);
@@ -1214,7 +1215,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
return p; return p;
} }
void Sync(Bool isUnpack, const PixelStoreParameters& params) { static void Sync(Bool isUnpack, const PixelStoreParameters& params) {
if (!isUnpack) { if (!isUnpack) {
g_GLESFuncs.glPixelStorei(GL_PACK_ALIGNMENT, params.Alignment); g_GLESFuncs.glPixelStorei(GL_PACK_ALIGNMENT, params.Alignment);
g_GLESFuncs.glPixelStorei(GL_PACK_ROW_LENGTH, params.RowLength); g_GLESFuncs.glPixelStorei(GL_PACK_ROW_LENGTH, params.RowLength);
@@ -1271,7 +1272,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
// Handle PBO // Handle PBO
auto pixelPackBufferObject = auto& pixelPackBufferObject =
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
Bool usePBO; Bool usePBO;
GLuint prevPixelPackBuffer = 0; GLuint prevPixelPackBuffer = 0;
@@ -1279,7 +1280,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
BufferImpl::CreateAndSyncBufferObject(pixelPackBufferObject); BufferImpl::CreateAndSyncBufferObject(pixelPackBufferObject);
MGLOG_D("ReadPixels: Using PBO %u", pixelPackBufferObject->GetExternalIndex()); MGLOG_D("ReadPixels: Using PBO %u", pixelPackBufferObject->GetExternalIndex());
usePBO = true; usePBO = true;
const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(pixelPackBufferObject); const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(pixelPackBufferObject.get());
if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) { if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) {
MGLOG_E("ReadPixels: No backend buffer found for PBO %u.", MGLOG_E("ReadPixels: No backend buffer found for PBO %u.",
@@ -1299,8 +1300,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (usePBO) { if (usePBO) {
// pull back to client memory if PBO is used // pull back to client memory if PBO is used
MGLOG_D("ReadPixels: PBO used, mapping buffer to client memory"); MGLOG_D("ReadPixels: PBO used, mapping buffer to client memory");
GLvoid* pboMappedPtr = g_GLESFuncs.glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, GLvoid* pboMappedPtr = g_GLESFuncs.glMapBufferRange(
pixelPackBufferObject->GetSize(), GL_MAP_READ_BIT); GL_PIXEL_PACK_BUFFER, 0, (GLsizeiptr)pixelPackBufferObject->GetSize(), GL_MAP_READ_BIT);
if (pboMappedPtr) { if (pboMappedPtr) {
MGLOG_D("ReadPixels: Copying data from PBO to client memory"); MGLOG_D("ReadPixels: Copying data from PBO to client memory");
SizeT size = pixelPackBufferObject->GetSize(); SizeT size = pixelPackBufferObject->GetSize();
@@ -1345,7 +1346,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("GetTexImage: SyncCurrentFBO()"); MGLOG_D("GetTexImage: SyncCurrentFBO()");
FramebufferImpl::SyncCurrentFBO(); FramebufferImpl::SyncCurrentFBO();
Uint activeTextureUnit = MG_State::pGLContext->GetActiveTextureUnit(); auto activeTextureUnit = MG_State::pGLContext->GetActiveTextureUnit();
MGLOG_D("GetTexImage: active texture unit = %u", activeTextureUnit); MGLOG_D("GetTexImage: active texture unit = %u", activeTextureUnit);
const auto& textureObject = MG_State::pGLContext->GetTextureUnitObject(activeTextureUnit) const auto& textureObject = MG_State::pGLContext->GetTextureUnitObject(activeTextureUnit)
@@ -1355,7 +1356,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("GetTexImage: bound texture object = %p (name=%u)", textureObject.get(), MGLOG_D("GetTexImage: bound texture object = %p (name=%u)", textureObject.get(),
textureObject ? textureObject->GetExternalIndex() : 0); textureObject ? textureObject->GetExternalIndex() : 0);
const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject.get());
if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) {
MGLOG_E("GetTexImage: No backend texture found for texture %u.", MGLOG_E("GetTexImage: No backend texture found for texture %u.",
@@ -1383,14 +1384,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
return; return;
} }
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
MGLOG_D("GetTexImage: Applying TempPixelStoreParameterSync (PACK)"); MGLOG_D("GetTexImage: Applying TempPixelStoreParameterSync (PACK)");
TempPixelStoreParameterSync tempPackParamsSync(false); TempPixelStoreParameterSync tempPackParamsSync(false);
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
@@ -1404,7 +1405,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto* textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get()); auto* textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
auto levelRange = textureMipmapObject->GetLevelRange(); auto& levelRange = textureMipmapObject->GetLevelRange();
MGLOG_D("GetTexImage: mipmap level range = [%d, %d)", levelRange.x(), levelRange.y()); MGLOG_D("GetTexImage: mipmap level range = [%d, %d)", levelRange.x(), levelRange.y());
if (level < levelRange.x() || level >= levelRange.y()) { if (level < levelRange.x() || level >= levelRange.y()) {
@@ -1421,7 +1422,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("GetTexImage: mip level %d size = %dx%d", level, size.x(), size.y()); MGLOG_D("GetTexImage: mip level %d size = %dx%d", level, size.x(), size.y());
// Handle PBO // Handle PBO
auto pixelPackBufferObject = auto& pixelPackBufferObject =
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject(); MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
Bool usePBO; Bool usePBO;
GLuint prevPixelPackBuffer = 0; GLuint prevPixelPackBuffer = 0;
@@ -1429,7 +1430,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
BufferImpl::CreateAndSyncBufferObject(pixelPackBufferObject); BufferImpl::CreateAndSyncBufferObject(pixelPackBufferObject);
MGLOG_D("GetTexImage: Using PBO %u", pixelPackBufferObject->GetExternalIndex()); MGLOG_D("GetTexImage: Using PBO %u", pixelPackBufferObject->GetExternalIndex());
usePBO = true; usePBO = true;
const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(pixelPackBufferObject); const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(pixelPackBufferObject.get());
if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) { if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) {
MGLOG_E("GetTexImage: No backend buffer found for PBO %u.", MGLOG_E("GetTexImage: No backend buffer found for PBO %u.",
pixelPackBufferObject ? pixelPackBufferObject->GetExternalIndex() : 0); pixelPackBufferObject ? pixelPackBufferObject->GetExternalIndex() : 0);
@@ -1443,7 +1444,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("GetTexImage: Not using PBO"); MGLOG_D("GetTexImage: Not using PBO");
} }
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
MGLOG_D("GetTexImage: glReadPixels(0, 0, %d, %d, %s, %s, %p)", size.x(), size.y(), MGLOG_D("GetTexImage: glReadPixels(0, 0, %d, %d, %s, %s, %p)", size.x(), size.y(),
@@ -1451,14 +1452,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
pixels); pixels);
g_GLESFuncs.glReadPixels(0, 0, size.x(), size.y(), esFormat, esType, pixels); g_GLESFuncs.glReadPixels(0, 0, size.x(), size.y(), esFormat, esType, pixels);
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
if (usePBO) { if (usePBO) {
// pull back to client memory if PBO is used // pull back to client memory if PBO is used
MGLOG_D("ReadPixels: PBO used, mapping buffer to client memory"); MGLOG_D("ReadPixels: PBO used, mapping buffer to client memory");
GLvoid* pboMappedPtr = g_GLESFuncs.glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, GLvoid* pboMappedPtr = g_GLESFuncs.glMapBufferRange(
pixelPackBufferObject->GetSize(), GL_MAP_READ_BIT); GL_PIXEL_PACK_BUFFER, 0, (GLsizeiptr)pixelPackBufferObject->GetSize(), GL_MAP_READ_BIT);
if (pboMappedPtr) { if (pboMappedPtr) {
MGLOG_D("ReadPixels: Copying data from PBO to client memory"); MGLOG_D("ReadPixels: Copying data from PBO to client memory");
SizeT size = pixelPackBufferObject->GetSize(); SizeT size = pixelPackBufferObject->GetSize();
@@ -1479,7 +1480,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
} }
errorLopper.Loop([file = __FILE__, line = __LINE__](auto err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
MGLOG_D("GetTexImage: finished"); MGLOG_D("GetTexImage: finished");
+112 -106
View File
@@ -7,10 +7,6 @@
// End of Source File Header // End of Source File Header
#include "Managers.h" #include "Managers.h"
#include "MG_State/GLState/TextureState/TextureEnum.h"
#include "MG_State/GLState/TextureState/TextureObject.h"
#include "MG_State/GLState/TextureState/TextureState.h"
#include "MG_Util/Converters/GLToMG/TextureEnumConverter.h"
#include "Utils.h" #include "Utils.h"
#include "DirectGLES.h" #include "DirectGLES.h"
@@ -18,6 +14,7 @@
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h> #include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
#include <MG_Util/Converters/MGToGL/DataTypeConverter.h> #include <MG_Util/Converters/MGToGL/DataTypeConverter.h>
#include <MG_Util/Converters/MGToGL/BufferEnumConverter.h> #include <MG_Util/Converters/MGToGL/BufferEnumConverter.h>
#include <MG_Util/Converters/GLToMG/TextureEnumConverter.h>
#include <MG_Util/Converters/MGToGL/ProgramEnumConverter.h> #include <MG_Util/Converters/MGToGL/ProgramEnumConverter.h>
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h> #include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
#include <MG_Util/Converters/MGToStr/TextureEnumConverter.h> #include <MG_Util/Converters/MGToStr/TextureEnumConverter.h>
@@ -44,7 +41,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
} }
void BackendBufferObject::SyncToBackend(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) { void BackendBufferObject::SyncToBackend(const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
@@ -105,7 +102,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
void BackendBufferObject::SyncToBackend_glBufferData( void BackendBufferObject::SyncToBackend_glBufferData(
SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) { const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
@@ -117,11 +114,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
GLenum usage = MG_Util::ConvertBufferUsageToGLEnum(stateBufferObject->GetUsage()); GLenum usage = MG_Util::ConvertBufferUsageToGLEnum(stateBufferObject->GetUsage());
Bind(); Bind();
g_GLESFuncs.glBufferData(TempBufferTarget, size, data, usage); g_GLESFuncs.glBufferData(TempBufferTarget, (GLsizeiptr)size, data, usage);
} }
void BackendBufferObject::SyncToBackend_glBufferSubData( void BackendBufferObject::SyncToBackend_glBufferSubData(
SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) { const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
@@ -130,7 +127,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const void* data = stateBufferObject->GetDataReadOnly()->data(); const void* data = stateBufferObject->GetDataReadOnly()->data();
// dirty range: [range.start, range.end) // dirty range: [range.start, range.end)
auto ranges = stateBufferObject->GetDirtyRanges(); auto& ranges = stateBufferObject->GetDirtyRanges();
if (ranges.empty()) { if (ranges.empty()) {
MGLOG_D("No dirty range to sync for buffer with ID: %u", m_backendBufferId); MGLOG_D("No dirty range to sync for buffer with ID: %u", m_backendBufferId);
return; return;
@@ -138,20 +135,21 @@ namespace MobileGL::MG_Backend::DirectGLES {
for (const auto& range : ranges) { for (const auto& range : ranges) {
Bind(); Bind();
g_GLESFuncs.glBufferSubData(TempBufferTarget, range.start, range.end - range.start, g_GLESFuncs.glBufferSubData(TempBufferTarget, (GLintptr)range.start,
(GLintptr)(range.end - range.start),
reinterpret_cast<const char*>(data) + range.start); reinterpret_cast<const char*>(data) + range.start);
} }
} }
void BackendBufferObject::SyncToBackend_glMapBufferRange( void BackendBufferObject::SyncToBackend_glMapBufferRange(
SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject, Bool invalidate, Bool unsynchronized) { const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject, Bool invalidate, Bool unsynchronized) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
MGLOG_D("Syncing buffer map (glMapBuffer) for object with ID : %u", m_backendBufferId); MGLOG_D("Syncing buffer map (glMapBuffer) for object with ID : %u", m_backendBufferId);
MGLOG_D("Mapping buffer with ID: %u", m_backendBufferId); MGLOG_D("Mapping buffer with ID: %u", m_backendBufferId);
auto ranges = stateBufferObject->GetDirtyRanges(); auto& ranges = stateBufferObject->GetDirtyRanges();
if (ranges.empty()) { if (ranges.empty()) {
MGLOG_D("No dirty range to sync for buffer with ID: %u", m_backendBufferId); MGLOG_D("No dirty range to sync for buffer with ID: %u", m_backendBufferId);
return; return;
@@ -159,18 +157,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
SizeT minStart = ranges.GetOverallMinStart(); SizeT minStart = ranges.GetOverallMinStart();
SizeT maxEnd = ranges.GetOverallMaxEnd(); SizeT maxEnd = ranges.GetOverallMaxEnd();
Bind(); Bind();
void* mappedData = g_GLESFuncs.glMapBufferRange(TempBufferTarget, minStart, maxEnd - minStart, void* mappedData = g_GLESFuncs.glMapBufferRange(
(invalidate ? GL_MAP_INVALIDATE_RANGE_BIT : 0) | TempBufferTarget, (GLintptr)minStart, (GLintptr)(maxEnd - minStart),
(unsynchronized ? GL_MAP_UNSYNCHRONIZED_BIT : 0) | (invalidate ? GL_MAP_INVALIDATE_RANGE_BIT : 0) | (unsynchronized ? GL_MAP_UNSYNCHRONIZED_BIT : 0) |
GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
const void* data = stateBufferObject->GetDataReadOnly()->data(); const void* data = stateBufferObject->GetDataReadOnly()->data();
if (mappedData) { if (mappedData) {
MGLOG_D("Mapped buffer data successfully for object with ID: %u", m_backendBufferId); MGLOG_D("Mapped buffer data successfully for object with ID: %u", m_backendBufferId);
Memcpy(mappedData, reinterpret_cast<const char*>(data) + minStart, maxEnd - minStart); Memcpy(mappedData, reinterpret_cast<const char*>(data) + minStart, maxEnd - minStart);
// Explicitly flush the dirty ranges // Explicitly flush the dirty ranges
for (const auto& range : ranges) { for (const auto& range : ranges) {
g_GLESFuncs.glFlushMappedBufferRange(TempBufferTarget, range.start - minStart, g_GLESFuncs.glFlushMappedBufferRange(TempBufferTarget, (GLintptr)(range.start - minStart),
range.end - range.start); (GLintptr)(range.end - range.start));
} }
g_GLESFuncs.glUnmapBuffer(TempBufferTarget); g_GLESFuncs.glUnmapBuffer(TempBufferTarget);
} else { } else {
@@ -191,7 +189,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glBindBuffer(target, m_backendBufferId); g_GLESFuncs.glBindBuffer(target, m_backendBufferId);
} }
UnorderedMap<SharedPtr<MG_State::GLState::BufferObject>, SharedPtr<BackendBufferObject>> g_backendBufferObjects; StateBackendObjectRegistry<MG_State::GLState::BufferObject, BackendBufferObject> g_backendBufferObjects;
BackendBufferObject* g_boundVertexBufferObject = nullptr; BackendBufferObject* g_boundVertexBufferObject = nullptr;
} // namespace BufferImpl } // namespace BufferImpl
@@ -209,22 +207,21 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
} }
void BackendVertexArrayObject::Bind() { void BackendVertexArrayObject::Bind() const {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
g_GLESFuncs.glBindVertexArray(m_backendVAOId); g_GLESFuncs.glBindVertexArray(m_backendVAOId);
} }
void BackendVertexArrayObject::BindAttributeBuffer(Uint index, inline void BindAttributeBuffer(const MG_State::GLState::VertexAttribute& attrib) {
const MG_State::GLState::VertexAttribute& attrib) {
const auto& bufferObject = attrib.Buffer; const auto& bufferObject = attrib.Buffer;
if (!bufferObject) { if (!bufferObject) {
MGLOG_W("Attribute has no bound buffer, skipping."); MGLOG_W("Attribute has no bound buffer, skipping.");
return; return;
} }
const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(bufferObject); const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(bufferObject.get());
if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) { if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) {
MGLOG_E("No backend buffer found for attribute's buffer, cannot bind attribute."); MGLOG_E("No backend buffer found for attribute's buffer, cannot bind attribute.");
return; return;
@@ -234,7 +231,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
backendBufferObject->Bind(GL_ARRAY_BUFFER); backendBufferObject->Bind(GL_ARRAY_BUFFER);
} }
void BackendVertexArrayObject::SyncToBackend(SharedPtr<MG_State::GLState::VertexArrayObject>& stateVAOObject) { void BackendVertexArrayObject::SyncToBackend(
const SharedPtr<MG_State::GLState::VertexArrayObject>& stateVAOObject) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
@@ -268,7 +266,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_syncedAttributeVersions[attribIndex].BufferVersion; m_syncedAttributeVersions[attribIndex].BufferVersion;
if (!needsSyncFormat && !needsSyncBuffer) continue; if (!needsSyncFormat && !needsSyncBuffer) continue;
BindAttributeBuffer(attribIndex, attrib); BindAttributeBuffer(attrib);
if (!attrib.IsInteger) { if (!attrib.IsInteger) {
g_GLESFuncs.glVertexAttribPointer( g_GLESFuncs.glVertexAttribPointer(
@@ -289,7 +287,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (currentIndexBufferVersion != m_syncedIndexBufferVersion) { if (currentIndexBufferVersion != m_syncedIndexBufferVersion) {
const auto& indexBufferBinding = stateVAOObject->GetIndexBufferBindingSlot().GetBoundObject(); const auto& indexBufferBinding = stateVAOObject->GetIndexBufferBindingSlot().GetBoundObject();
if (indexBufferBinding) { if (indexBufferBinding) {
const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(indexBufferBinding); const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(indexBufferBinding.get());
if (backendBufferIt != BufferImpl::g_backendBufferObjects.end()) { if (backendBufferIt != BufferImpl::g_backendBufferObjects.end()) {
const auto& backendBufferObject = backendBufferIt->second; const auto& backendBufferObject = backendBufferIt->second;
backendBufferObject->Bind(GL_ELEMENT_ARRAY_BUFFER); backendBufferObject->Bind(GL_ELEMENT_ARRAY_BUFFER);
@@ -303,7 +301,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_syncedAttributeVersions = allAttributeVersions; m_syncedAttributeVersions = allAttributeVersions;
} }
UnorderedMap<SharedPtr<MG_State::GLState::VertexArrayObject>, SharedPtr<BackendVertexArrayObject>> StateBackendObjectRegistry<MG_State::GLState::VertexArrayObject, BackendVertexArrayObject>
g_backendVertexArrayObjects; g_backendVertexArrayObjects;
} // namespace VertexArrayImpl } // namespace VertexArrayImpl
@@ -336,7 +334,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_boundTexturesCache[unit][targetN] = this; g_boundTexturesCache[unit][targetN] = this;
} }
Uint BackendTextureObject::GetBackendTextureId() { Uint BackendTextureObject::GetBackendTextureId() const {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
@@ -344,7 +342,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
void BackendTextureObject::SyncMipmapsToBackend( void BackendTextureObject::SyncMipmapsToBackend(
SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) { const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
if (!stateTextureObject) { if (!stateTextureObject) {
MGLOG_E("State texture object is null, cannot sync to backend."); MGLOG_E("State texture object is null, cannot sync to backend.");
return; return;
@@ -353,7 +351,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
DebugImpl::ErrorLopper errorLopper;
MGLOG_D("Syncing texture mipmaps with backend ID %u to backend for state ID %u", m_backendTextureId, MGLOG_D("Syncing texture mipmaps with backend ID %u to backend for state ID %u", m_backendTextureId,
stateTextureObject->GetExternalIndex()); stateTextureObject->GetExternalIndex());
@@ -381,7 +378,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
Bind(target); Bind(target);
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
const auto baseSize = stateTextureObject->GetBaseSize(); const auto baseSize = stateTextureObject->GetBaseSize();
@@ -414,7 +411,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
&glFormat, &glType); &glFormat, &glType);
const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); const auto& uploadTargets = textureMipmapObject->GetUploadTargets();
for (auto uploadTarget : uploadTargets) { for (auto& uploadTarget : uploadTargets) {
for (SizeT level = 0; level < mipmapCount; ++level) { for (SizeT level = 0; level < mipmapCount; ++level) {
auto levelTexelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); auto levelTexelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level);
auto levelByteSize = textureMipmapObject->GetMipmapByteSize(uploadTarget, level); auto levelByteSize = textureMipmapObject->GetMipmapByteSize(uploadTarget, level);
@@ -429,22 +426,22 @@ namespace MobileGL::MG_Backend::DirectGLES {
levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), levelByteSize, pData, levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), levelByteSize, pData,
levelDirty ? "true" : "false"); levelDirty ? "true" : "false");
errorLopper.Clear(); DebugImpl::ErrorLopper::Clear();
g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
auto textureTarget = stateTextureObject->GetTarget(); auto textureTarget = stateTextureObject->GetTarget();
// TODO: handle more texture types // TODO: handle more texture types
switch (textureTarget) { switch (textureTarget) {
case TextureTarget::Texture2D: case TextureTarget::Texture2D:
case TextureTarget::TextureCubeMap: { case TextureTarget::TextureCubeMap: {
g_GLESFuncs.glTexImage2D(glUploadTarget, static_cast<GLint>(level), glInternalFormat, g_GLESFuncs.glTexImage2D(
static_cast<GLsizei>(levelTexelSize.x()), glUploadTarget, static_cast<GLint>(level), (GLint)glInternalFormat,
static_cast<GLsizei>(levelTexelSize.y()), 0, glFormat, glType, static_cast<GLsizei>(levelTexelSize.x()), static_cast<GLsizei>(levelTexelSize.y()),
pData); 0, glFormat, glType, pData);
break; break;
} }
case TextureTarget::Texture3D: { case TextureTarget::Texture3D: {
g_GLESFuncs.glTexImage3D( g_GLESFuncs.glTexImage3D(
glUploadTarget, static_cast<GLint>(level), glInternalFormat, glUploadTarget, static_cast<GLint>(level), (GLint)glInternalFormat,
static_cast<GLsizei>(levelTexelSize.x()), static_cast<GLsizei>(levelTexelSize.y()), static_cast<GLsizei>(levelTexelSize.x()), static_cast<GLsizei>(levelTexelSize.y()),
static_cast<GLsizei>(levelTexelSize.z()), 0, glFormat, glType, pData); static_cast<GLsizei>(levelTexelSize.z()), 0, glFormat, glType, pData);
break; break;
@@ -454,8 +451,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_Util::ConvertTextureTargetToString(textureTarget).c_str()); MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
} }
} }
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__, glUploadTarget, DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__,
glInternalFormat, glFormat, glType, pData](GLenum err) { glUploadTarget, glInternalFormat, glFormat, glType,
pData](GLenum err) {
MGLOG_D("%s(%s:%d) ES error: %s. glTexImage*: target=%s, internalformat=%s, format=%s, " MGLOG_D("%s(%s:%d) ES error: %s. glTexImage*: target=%s, internalformat=%s, format=%s, "
"type=%s, pixels=%p", "type=%s, pixels=%p",
func, file, line, MG_Util::ConvertGLEnumToString(err).c_str(), func, file, line, MG_Util::ConvertGLEnumToString(err).c_str(),
@@ -478,7 +476,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat, TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat,
&glFormat, &glType); &glFormat, &glType);
const auto& uploadTargets = textureMipmapObject->GetUploadTargets(); const auto& uploadTargets = textureMipmapObject->GetUploadTargets();
for (auto uploadTarget : uploadTargets) { for (auto& uploadTarget : uploadTargets) {
for (SizeT level = 0; level < mipmapCount; ++level) { for (SizeT level = 0; level < mipmapCount; ++level) {
if (!textureMipmapObject->IsStorageDirty(uploadTarget, level)) { if (!textureMipmapObject->IsStorageDirty(uploadTarget, level)) {
continue; continue;
@@ -499,10 +497,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto glUploadTarget = MG_Util::ConvertTextureUploadTargetToGLEnum(uploadTarget); auto glUploadTarget = MG_Util::ConvertTextureUploadTargetToGLEnum(uploadTarget);
g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { DebugImpl::ErrorLopper::Loop(
MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, [file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("%s(%s:%d) ES error: %s", func, file, line,
}); MG_Util::ConvertGLEnumToString(err).c_str());
});
auto texelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level); auto texelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level);
g_GLESFuncs.glTexSubImage2D(glUploadTarget, static_cast<GLint>(level), 0, 0, g_GLESFuncs.glTexSubImage2D(glUploadTarget, static_cast<GLint>(level), 0, 0,
static_cast<GLsizei>(texelSize.x()), static_cast<GLsizei>(texelSize.x()),
@@ -518,7 +517,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto* textureBufferObject = auto* textureBufferObject =
static_cast<MG_State::GLState::TextureObjectBuffer*>(stateTextureObject.get()); static_cast<MG_State::GLState::TextureObjectBuffer*>(stateTextureObject.get());
auto& slot = textureBufferObject->GetBufferBindingSlot(); auto& slot = textureBufferObject->GetBufferBindingSlot();
auto buffer = slot.GetBoundObject(); auto& buffer = slot.GetBoundObject();
auto bufferIndex = buffer->GetExternalIndex(); auto bufferIndex = buffer->GetExternalIndex();
currentTextureInfo.bufferExternalIndex = bufferIndex; currentTextureInfo.bufferExternalIndex = bufferIndex;
@@ -530,10 +529,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Need to sync texture buffer if not synced yet // Need to sync texture buffer if not synced yet
auto& backendBuffers = BufferImpl::g_backendBufferObjects; auto& backendBuffers = BufferImpl::g_backendBufferObjects;
SharedPtr<BufferImpl::BackendBufferObject> backendBufferObject; SharedPtr<BufferImpl::BackendBufferObject> backendBufferObject;
const auto& backendBufferIt = backendBuffers.find(buffer); const auto& backendBufferIt = backendBuffers.find(buffer.get());
if (backendBufferIt == backendBuffers.end()) { if (backendBufferIt == backendBuffers.end()) {
backendBufferObject = MakeShared<BufferImpl::BackendBufferObject>(); auto& backendBufferSlot = backendBuffers.GetOrCreate(buffer);
backendBuffers[buffer] = backendBufferObject; if (!backendBufferSlot) {
backendBufferSlot = MakeShared<BufferImpl::BackendBufferObject>();
}
backendBufferObject = backendBufferSlot;
} else { } else {
backendBufferObject = backendBufferIt->second; backendBufferObject = backendBufferIt->second;
} }
@@ -553,7 +555,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
THROW_UNIMPL_EXCEPTION; THROW_UNIMPL_EXCEPTION;
} }
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
@@ -561,11 +563,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
void BackendTextureObject::SyncBuiltinSamplerToBackend( void BackendTextureObject::SyncBuiltinSamplerToBackend(
SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) { const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
DebugImpl::ErrorLopper errorLopper;
if (!stateTextureObject) { if (!stateTextureObject) {
MGLOG_E("State texture object is null, cannot sync to backend."); MGLOG_E("State texture object is null, cannot sync to backend.");
return; return;
@@ -594,7 +596,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
Bind(target); Bind(target);
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
@@ -607,28 +609,29 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glTexParameteri(target, glName, \ g_GLESFuncs.glTexParameteri(target, glName, \
MG_Util::ConvertSampler##type##ToGLEnum(samplerParams.internalName)); \ MG_Util::ConvertSampler##type##ToGLEnum(samplerParams.internalName)); \
m_cacheSamplerParameters.internalName = samplerParams.internalName; \ m_cacheSamplerParameters.internalName = samplerParams.internalName; \
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__, \ DebugImpl::ErrorLopper::Loop( \
t = MG_Util::ConvertSampler##type##ToGLEnum(samplerParams.internalName)](GLenum err) { \ [file = __FILE__, line = __LINE__, func = __func__, \
MGLOG_D("%s(%s:%d) ES error %s, GL_TEXTURE_MIN_FILTER = %s", func, file, line, \ t = MG_Util::ConvertSampler##type##ToGLEnum(samplerParams.internalName)](GLenum err) { \
MG_Util::ConvertGLEnumToString(err).c_str(), MG_Util::ConvertGLEnumToString(t).c_str()); \ MGLOG_D("%s(%s:%d) ES error %s, GL_TEXTURE_MIN_FILTER = %s", func, file, line, \
}); \ MG_Util::ConvertGLEnumToString(err).c_str(), MG_Util::ConvertGLEnumToString(t).c_str()); \
}); \
} }
if (m_cacheSamplerParameters.minFilter != samplerParams.minFilter || if (m_cacheSamplerParameters.minFilter != samplerParams.minFilter ||
m_cacheSamplerParameters.mipmapMode != samplerParams.mipmapMode) { m_cacheSamplerParameters.mipmapMode != samplerParams.mipmapMode) {
g_GLESFuncs.glTexParameteri( g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_MIN_FILTER,
target, GL_TEXTURE_MIN_FILTER, (GLint)MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.minFilter,
MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.minFilter, samplerParams.mipmapMode)); samplerParams.mipmapMode));
m_cacheSamplerParameters.minFilter = samplerParams.minFilter; m_cacheSamplerParameters.minFilter = samplerParams.minFilter;
m_cacheSamplerParameters.mipmapMode = samplerParams.mipmapMode; m_cacheSamplerParameters.mipmapMode = samplerParams.mipmapMode;
} }
if (m_cacheSamplerParameters.magFilter != samplerParams.magFilter) { if (m_cacheSamplerParameters.magFilter != samplerParams.magFilter) {
g_GLESFuncs.glTexParameteri( g_GLESFuncs.glTexParameteri(
target, GL_TEXTURE_MAG_FILTER, target, GL_TEXTURE_MAG_FILTER,
MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.magFilter, SamplerMipmapMode::None)); (GLint)MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.magFilter, SamplerMipmapMode::None));
m_cacheSamplerParameters.magFilter = samplerParams.magFilter; m_cacheSamplerParameters.magFilter = samplerParams.magFilter;
} }
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
@@ -645,18 +648,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glTexParameterf(target, GL_TEXTURE_MAX_LOD, samplerParams.maxLod); g_GLESFuncs.glTexParameterf(target, GL_TEXTURE_MAX_LOD, samplerParams.maxLod);
m_cacheSamplerParameters.maxLod = samplerParams.maxLod; m_cacheSamplerParameters.maxLod = samplerParams.maxLod;
} }
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
#undef SYNC_TEX_SAMPLER_PARAM_IF_CHANGED #undef SYNC_TEX_SAMPLER_PARAM_IF_CHANGED
} }
void BackendTextureObject::SyncTextureParamsToBackend( void BackendTextureObject::SyncTextureParamsToBackend(
SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) { const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
DebugImpl::ErrorLopper errorLopper;
if (!stateTextureObject) { if (!stateTextureObject) {
MGLOG_E("State texture object is null, cannot sync to backend."); MGLOG_E("State texture object is null, cannot sync to backend.");
return; return;
@@ -683,7 +686,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
Bind(target); Bind(target);
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("%s(%s:%d) ES error: %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
@@ -696,14 +699,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, static_cast<GLint>(levelRange.x())); g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, static_cast<GLint>(levelRange.x()));
m_cacheLodRange.x() = levelRange.x(); m_cacheLodRange.x() = levelRange.x();
} }
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
if (m_cacheLodRange.y() != levelRange.y()) { if (m_cacheLodRange.y() != levelRange.y()) {
g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(levelRange.y())); g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(levelRange.y()));
m_cacheLodRange.y() = levelRange.y(); m_cacheLodRange.y() = levelRange.y();
} }
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
@@ -720,7 +723,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED(a(), GL_TEXTURE_SWIZZLE_A); SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED(a(), GL_TEXTURE_SWIZZLE_A);
#undef SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED #undef SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED
m_cacheSwizzleParams = swizzleParams; m_cacheSwizzleParams = swizzleParams;
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
} }
@@ -730,7 +733,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
GLfloat borderColorArray[4] = {borderColor.x(), borderColor.y(), borderColor.z(), borderColor.w()}; GLfloat borderColorArray[4] = {borderColor.x(), borderColor.y(), borderColor.z(), borderColor.w()};
g_GLESFuncs.glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, borderColorArray); g_GLESFuncs.glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, borderColorArray);
m_cacheBorderColor = borderColor; m_cacheBorderColor = borderColor;
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) { DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__](GLenum err) {
MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str()); MGLOG_D("%s(%s:%d) ES error %s", func, file, line, MG_Util::ConvertGLEnumToString(err).c_str());
}); });
} }
@@ -760,8 +763,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Array<Array<BackendTextureObject*, (SizeT)TextureTarget::TextureTargetCount>, Array<Array<BackendTextureObject*, (SizeT)TextureTarget::TextureTargetCount>,
MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS> MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS>
g_boundTexturesCache; g_boundTexturesCache;
UnorderedMap<SharedPtr<MG_State::GLState::ITextureObject>, SharedPtr<BackendTextureObject>> StateBackendObjectRegistry<MG_State::GLState::ITextureObject, BackendTextureObject> g_backendTextureObjects;
g_backendTextureObjects;
} // namespace TextureImpl } // namespace TextureImpl
namespace FramebufferImpl { namespace FramebufferImpl {
@@ -778,7 +780,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
} }
void BackendFramebufferObject::Bind(FramebufferTarget target) { void BackendFramebufferObject::Bind(FramebufferTarget target) const {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
@@ -788,12 +790,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_backendFBOId); g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_backendFBOId);
} }
Bool BackendFramebufferObject::SyncAttachmentObject( static Bool SyncAttachmentObject(GLenum glFBOTarget,
GLenum glFBOTarget, const MG_State::GLState::FramebufferAttachmentObject& attachmentObject, const MG_State::GLState::FramebufferAttachmentObject& attachmentObject,
GLenum glBackendAttachment) { GLenum glBackendAttachment) {
if (attachmentObject.IsTexture()) { if (attachmentObject.IsTexture()) {
const auto& textureObject = attachmentObject.GetTexture(); const auto& textureObject = attachmentObject.GetTexture();
const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject.get());
if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) { if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) {
MGLOG_E("%s: No backend texture found for FBO attachment, cannot bind texture.", __func__); MGLOG_E("%s: No backend texture found for FBO attachment, cannot bind texture.", __func__);
return false; return false;
@@ -807,11 +809,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
} else if (attachmentObject.IsRenderbuffer()) { } else if (attachmentObject.IsRenderbuffer()) {
const auto& renderbufferObject = attachmentObject.GetRenderbuffer(); const auto& renderbufferObject = attachmentObject.GetRenderbuffer();
const auto& backendRenderbufferIt = const auto& backendRenderbufferIt =
RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject); RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject.get());
SharedPtr<RenderbufferImpl::BackendRenderbufferObject> backendRenderbufferObject; SharedPtr<RenderbufferImpl::BackendRenderbufferObject> backendRenderbufferObject;
if (backendRenderbufferIt == RenderbufferImpl::g_backendRenderbufferObjects.end()) { if (backendRenderbufferIt == RenderbufferImpl::g_backendRenderbufferObjects.end()) {
backendRenderbufferObject = MakeShared<RenderbufferImpl::BackendRenderbufferObject>(); auto& backendRenderbufferSlot =
RenderbufferImpl::g_backendRenderbufferObjects[renderbufferObject] = backendRenderbufferObject; RenderbufferImpl::g_backendRenderbufferObjects.GetOrCreate(renderbufferObject);
if (!backendRenderbufferSlot) {
backendRenderbufferSlot = MakeShared<RenderbufferImpl::BackendRenderbufferObject>();
}
backendRenderbufferObject = backendRenderbufferSlot;
} else { } else {
backendRenderbufferObject = backendRenderbufferIt->second; backendRenderbufferObject = backendRenderbufferIt->second;
} }
@@ -824,8 +830,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
return true; return true;
} }
void BackendFramebufferObject::SyncToBackend(SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject, void BackendFramebufferObject::SyncToBackend(
FramebufferTarget asTarget) { const SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject, FramebufferTarget asTarget) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
@@ -853,7 +859,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
std::fill(m_backendDrawBuffers, m_backendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, GL_NONE); std::fill(m_backendDrawBuffers, m_backendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, GL_NONE);
int nEffectiveBuffers = 0; int nEffectiveBuffers = 0;
for (GLint i = 0; i < FramebufferObject::MAX_DRAW_BUFFERS; ++i) { for (GLint i = 0; i < FramebufferObject::MAX_DRAW_BUFFERS; ++i) {
auto frontendBuf = stateDrawBuffers[i]; auto& frontendBuf = stateDrawBuffers[i];
if (frontendBuf == FramebufferAttachmentType::None) { if (frontendBuf == FramebufferAttachmentType::None) {
m_backendDrawBuffers[i] = GL_NONE; m_backendDrawBuffers[i] = GL_NONE;
continue; continue;
@@ -892,7 +898,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const auto& attachmentVersions = stateFBOObject->GetAllFramebufferAttachmentVersions(); const auto& attachmentVersions = stateFBOObject->GetAllFramebufferAttachmentVersions();
for (SizeT i = 0; i < attachments.size(); ++i) { for (SizeT i = 0; i < attachments.size(); ++i) {
const auto& attachmentObject = attachments[i]; const auto& attachmentObject = attachments[i];
FramebufferAttachmentType frontendType = static_cast<FramebufferAttachmentType>(i); auto frontendType = static_cast<FramebufferAttachmentType>(i);
GLenum glBackendAttachment = GL_NONE; GLenum glBackendAttachment = GL_NONE;
if (frontendType >= FramebufferAttachmentType::Color0 && if (frontendType >= FramebufferAttachmentType::Color0 &&
frontendType <= FramebufferAttachmentType::Color31) frontendType <= FramebufferAttachmentType::Color31)
@@ -928,7 +934,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Verify that the backend object's name and parameters match the frontend attachment state // Verify that the backend object's name and parameters match the frontend attachment state
if (attachmentObject.IsTexture()) { if (attachmentObject.IsTexture()) {
const auto& textureObject = attachmentObject.GetTexture(); const auto& textureObject = attachmentObject.GetTexture();
auto backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject); auto backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject.get());
MOBILEGL_ASSERT(backendTextureIt != TextureImpl::g_backendTextureObjects.end(), MOBILEGL_ASSERT(backendTextureIt != TextureImpl::g_backendTextureObjects.end(),
"No backend texture found while framebuffer reports texture attachment."); "No backend texture found while framebuffer reports texture attachment.");
GLuint backendTexId = backendTextureIt->second->GetBackendTextureId(); GLuint backendTexId = backendTextureIt->second->GetBackendTextureId();
@@ -944,7 +950,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
"Attachment texture level mismatch between GLES and state object."); "Attachment texture level mismatch between GLES and state object.");
} else if (attachmentObject.IsRenderbuffer()) { } else if (attachmentObject.IsRenderbuffer()) {
const auto& renderbufferObject = attachmentObject.GetRenderbuffer(); const auto& renderbufferObject = attachmentObject.GetRenderbuffer();
auto backendRboIt = RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject); auto backendRboIt =
RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject.get());
MOBILEGL_ASSERT( MOBILEGL_ASSERT(
backendRboIt != RenderbufferImpl::g_backendRenderbufferObjects.end(), backendRboIt != RenderbufferImpl::g_backendRenderbufferObjects.end(),
"No backend renderbuffer found while framebuffer reports renderbuffer attachment."); "No backend renderbuffer found while framebuffer reports renderbuffer attachment.");
@@ -975,14 +982,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
return glBackendReadBuffer; return glBackendReadBuffer;
} }
UnorderedMap<SharedPtr<MG_State::GLState::FramebufferObject>, SharedPtr<BackendFramebufferObject>> StateBackendObjectRegistry<MG_State::GLState::FramebufferObject, BackendFramebufferObject>
g_backendFramebufferObjects; g_backendFramebufferObjects;
Array<Uint16, SizeT(FramebufferTarget::FramebufferTargetCount)> g_fboBindVersions = {0}; Array<Uint16, SizeT(FramebufferTarget::FramebufferTargetCount)> g_fboBindVersions = {0};
} // namespace FramebufferImpl } // namespace FramebufferImpl
namespace PrgramImpl { namespace PrgramImpl {
UnorderedMap<SharedPtr<MG_State::GLState::ProgramObject>, SharedPtr<BackendProgramObjectImpl>> StateBackendObjectRegistry<MG_State::GLState::ProgramObject, BackendProgramObjectImpl> g_backendProgramObjects;
g_backendProgramObjects;
BackendProgramObjectImpl::BackendProgramObjectImpl() { BackendProgramObjectImpl::BackendProgramObjectImpl() {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
@@ -1008,7 +1014,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
} }
void BackendProgramObjectImpl::SyncToBackend(SharedPtr<MG_State::GLState::ProgramObject>& stateProgramObject) { void BackendProgramObjectImpl::SyncToBackend(
const SharedPtr<MG_State::GLState::ProgramObject>& stateProgramObject) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
@@ -1068,7 +1075,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
String source; String source;
auto& spirvCode = shaderSpirvs[index]; auto& spirvCode = shaderSpirvs[index];
MG_Util::ShaderTranspiler::SpvcSession spvcSession(spirvCode); MG_Util::ShaderTranspiler::SpvcSession spvcSession(spirvCode,
MG_Util::ShaderTranspiler::SessionUsageBit::Transpile);
spvc_compiler_options options; spvc_compiler_options options;
spvcSession.CreateOptions(&options); spvcSession.CreateOptions(&options);
@@ -1161,7 +1169,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("Program sync completed. backend ID %u", m_backendProgramId); MGLOG_D("Program sync completed. backend ID %u", m_backendProgramId);
} }
void BackendProgramObjectImpl::Use() { void BackendProgramObjectImpl::Use() const {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
@@ -1184,7 +1192,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
} }
void BackendSamplerObject::SyncToBackend(SharedPtr<MG_State::GLState::SamplerObject>& stateSamplerObject) { void BackendSamplerObject::SyncToBackend(
const SharedPtr<MG_State::GLState::SamplerObject>& stateSamplerObject) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
@@ -1210,22 +1219,22 @@ namespace MobileGL::MG_Backend::DirectGLES {
#define SYNC_SAMPLER_PARAM_IF_CHANGED(internalName, glName, type) \ #define SYNC_SAMPLER_PARAM_IF_CHANGED(internalName, glName, type) \
if (m_cacheSamplerParameters.internalName != samplerParams.internalName) { \ if (m_cacheSamplerParameters.internalName != samplerParams.internalName) { \
g_GLESFuncs.glSamplerParameteri(m_backendSamplerId, glName, \ g_GLESFuncs.glSamplerParameteri(m_backendSamplerId, glName, \
MG_Util::ConvertSampler##type##ToGLEnum(samplerParams.internalName)); \ (GLint)MG_Util::ConvertSampler##type##ToGLEnum(samplerParams.internalName)); \
m_cacheSamplerParameters.internalName = samplerParams.internalName; \ m_cacheSamplerParameters.internalName = samplerParams.internalName; \
} }
if (m_cacheSamplerParameters.minFilter != samplerParams.minFilter || if (m_cacheSamplerParameters.minFilter != samplerParams.minFilter ||
m_cacheSamplerParameters.mipmapMode != samplerParams.mipmapMode) { m_cacheSamplerParameters.mipmapMode != samplerParams.mipmapMode) {
g_GLESFuncs.glSamplerParameteri( g_GLESFuncs.glSamplerParameteri(m_backendSamplerId, GL_TEXTURE_MIN_FILTER,
m_backendSamplerId, GL_TEXTURE_MIN_FILTER, (GLint)MG_Util::ConvertSamplerFilterModeToGLEnum(
MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.minFilter, samplerParams.mipmapMode)); samplerParams.minFilter, samplerParams.mipmapMode));
m_cacheSamplerParameters.minFilter = samplerParams.minFilter; m_cacheSamplerParameters.minFilter = samplerParams.minFilter;
m_cacheSamplerParameters.mipmapMode = samplerParams.mipmapMode; m_cacheSamplerParameters.mipmapMode = samplerParams.mipmapMode;
} }
if (m_cacheSamplerParameters.magFilter != samplerParams.magFilter) { if (m_cacheSamplerParameters.magFilter != samplerParams.magFilter) {
g_GLESFuncs.glSamplerParameteri( g_GLESFuncs.glSamplerParameteri(
m_backendSamplerId, GL_TEXTURE_MAG_FILTER, m_backendSamplerId, GL_TEXTURE_MAG_FILTER,
MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.magFilter, SamplerMipmapMode::None)); (GLint)MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.magFilter, SamplerMipmapMode::None));
m_cacheSamplerParameters.magFilter = samplerParams.magFilter; m_cacheSamplerParameters.magFilter = samplerParams.magFilter;
} }
@@ -1256,7 +1265,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_boundSamplersCache[unit] = this; g_boundSamplersCache[unit] = this;
} }
Uint BackendSamplerObject::GetBackendSamplerId() { Uint BackendSamplerObject::GetBackendSamplerId() const {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
@@ -1271,8 +1280,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
Array<BackendSamplerObject*, MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS> g_boundSamplersCache; Array<BackendSamplerObject*, MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS> g_boundSamplersCache;
UnorderedMap<SharedPtr<MG_State::GLState::SamplerObject>, SharedPtr<BackendSamplerObject>> StateBackendObjectRegistry<MG_State::GLState::SamplerObject, BackendSamplerObject> g_backendSamplerObjects;
g_backendSamplerObjects;
} // namespace SamplerImpl } // namespace SamplerImpl
namespace RenderbufferImpl { namespace RenderbufferImpl {
@@ -1287,7 +1295,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
} }
void BackendRenderbufferObject::Bind() { void BackendRenderbufferObject::Bind() const {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND); ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif #endif
@@ -1334,9 +1342,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("RBO %u sync completed. backend ID %u", stateRBOObject->GetExternalIndex(), m_backendRBOId); MGLOG_D("RBO %u sync completed. backend ID %u", stateRBOObject->GetExternalIndex(), m_backendRBOId);
} }
UnorderedMap<SharedPtr<MG_State::GLState::RenderbufferObject>, SharedPtr<BackendRenderbufferObject>> StateBackendObjectRegistry<MG_State::GLState::RenderbufferObject, BackendRenderbufferObject>
g_backendRenderbufferObjects; g_backendRenderbufferObjects;
} // namespace RenderbufferImpl } // namespace RenderbufferImpl
namespace Utils {} // namespace Utils
} // namespace MobileGL::MG_Backend::DirectGLES } // namespace MobileGL::MG_Backend::DirectGLES
+122 -34
View File
@@ -15,19 +15,113 @@
#include <MG_State/GLState/Core.h> #include <MG_State/GLState/Core.h>
namespace MobileGL::MG_Backend::DirectGLES { namespace MobileGL::MG_Backend::DirectGLES {
template <typename StateObject, typename BackendObject>
class StateBackendObjectRegistry {
public:
using StatePtr = SharedPtr<StateObject>;
using StateWeakPtr = std::weak_ptr<StateObject>;
using BackendPtr = SharedPtr<BackendObject>;
using BackendMap = UnorderedMap<StateObject*, BackendPtr>;
using StateRefMap = UnorderedMap<StateObject*, StateWeakPtr>;
using iterator = typename BackendMap::iterator;
using const_iterator = typename BackendMap::const_iterator;
BackendPtr& GetOrCreate(const StatePtr& stateObj) {
MOBILEGL_ASSERT(stateObj != nullptr, "State object must not be null");
auto* key = stateObj.get();
auto trackedStateIt = m_stateRefs.find(key);
if (trackedStateIt != m_stateRefs.end() && trackedStateIt->second.expired()) {
EraseByKey(key);
}
m_stateRefs[key] = stateObj;
return m_backendObjects[key];
}
iterator find(StateObject* stateObj) {
if (!IsAlive(stateObj)) {
EraseByKey(stateObj);
return m_backendObjects.end();
}
return m_backendObjects.find(stateObj);
}
const_iterator find(StateObject* stateObj) const {
return const_cast<StateBackendObjectRegistry*>(this)->find(stateObj);
}
iterator end() { return m_backendObjects.end(); }
const_iterator end() const { return m_backendObjects.end(); }
void CollectGarbageIfNeeded() {
++m_gcTick;
if (m_gcTick < kGCInterval) {
return;
}
CollectGarbage();
m_gcTick = 0;
}
void CollectGarbageNow() { CollectGarbage(); }
private:
bool IsAlive(StateObject* stateObj) const {
const auto trackedStateIt = m_stateRefs.find(stateObj);
if (trackedStateIt == m_stateRefs.end()) {
return false;
}
return !trackedStateIt->second.expired();
}
void EraseByKey(StateObject* stateObj) {
m_stateRefs.erase(stateObj);
m_backendObjects.erase(stateObj);
}
void CollectGarbage() {
if (m_isCollecting) {
return;
}
m_isCollecting = true;
Vector<StateObject*> staleKeys;
staleKeys.reserve(m_stateRefs.size());
for (const auto& [stateKey, stateWeakRef] : m_stateRefs) {
if (stateWeakRef.expired()) {
staleKeys.push_back(stateKey);
}
}
for (auto* stateKey : staleKeys) {
m_stateRefs.erase(stateKey);
m_backendObjects.erase(stateKey);
}
m_isCollecting = false;
}
private:
static constexpr Uint32 kGCInterval = 1024;
StateRefMap m_stateRefs;
BackendMap m_backendObjects;
Uint32 m_gcTick = 0;
Bool m_isCollecting = false;
};
namespace BufferImpl { namespace BufferImpl {
const GLenum TempBufferTarget = GL_ARRAY_BUFFER; const GLenum TempBufferTarget = GL_ARRAY_BUFFER;
class BackendBufferObject { class BackendBufferObject {
public: public:
BackendBufferObject(); BackendBufferObject();
void SyncToBackend(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject); void SyncToBackend(const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject);
Uint GetBackendBufferId() { return m_backendBufferId; } Uint GetBackendBufferId() const { return m_backendBufferId; }
void Bind(GLenum target = TempBufferTarget); void Bind(GLenum target = TempBufferTarget);
private: private:
void SyncToBackend_glBufferData(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject); void SyncToBackend_glBufferData(const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject);
void SyncToBackend_glBufferSubData(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject); void SyncToBackend_glBufferSubData(const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject);
void SyncToBackend_glMapBufferRange(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject, void SyncToBackend_glMapBufferRange(const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject,
Bool invalidate = true, Bool unsynchronized = true); Bool invalidate = true, Bool unsynchronized = true);
Uint m_backendBufferId = 0; Uint m_backendBufferId = 0;
@@ -36,21 +130,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
}; };
extern BackendBufferObject* g_boundVertexBufferObject; extern BackendBufferObject* g_boundVertexBufferObject;
extern UnorderedMap<SharedPtr<MG_State::GLState::BufferObject>, SharedPtr<BackendBufferObject>> extern StateBackendObjectRegistry<MG_State::GLState::BufferObject, BackendBufferObject> g_backendBufferObjects;
g_backendBufferObjects;
} // namespace BufferImpl } // namespace BufferImpl
namespace VertexArrayImpl { namespace VertexArrayImpl {
class BackendVertexArrayObject { class BackendVertexArrayObject {
public: public:
BackendVertexArrayObject(); BackendVertexArrayObject();
void SyncToBackend(SharedPtr<MG_State::GLState::VertexArrayObject>& stateVAOObject); void SyncToBackend(const SharedPtr<MG_State::GLState::VertexArrayObject>& stateVAOObject);
Uint GetBackendVertexArrayId() { return m_backendVAOId; } Uint GetBackendVertexArrayId() const { return m_backendVAOId; }
void Bind(); void Bind() const;
private: private:
void BindAttributeBuffer(Uint index, const MG_State::GLState::VertexAttribute& attrib);
Uint m_backendVAOId = 0; Uint m_backendVAOId = 0;
Bool m_isInitialized = false; Bool m_isInitialized = false;
Uint16 m_syncedIndexBufferVersion = 0; Uint16 m_syncedIndexBufferVersion = 0;
@@ -58,7 +149,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_syncedAttributeVersions; m_syncedAttributeVersions;
}; };
extern UnorderedMap<SharedPtr<MG_State::GLState::VertexArrayObject>, SharedPtr<BackendVertexArrayObject>> extern StateBackendObjectRegistry<MG_State::GLState::VertexArrayObject, BackendVertexArrayObject>
g_backendVertexArrayObjects; g_backendVertexArrayObjects;
} // namespace VertexArrayImpl } // namespace VertexArrayImpl
@@ -92,11 +183,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
class BackendTextureObject { class BackendTextureObject {
public: public:
BackendTextureObject(); BackendTextureObject();
void SyncMipmapsToBackend(SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject); void SyncMipmapsToBackend(const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
void SyncBuiltinSamplerToBackend(SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject); void SyncBuiltinSamplerToBackend(const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
void SyncTextureParamsToBackend(SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject); void SyncTextureParamsToBackend(const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
void Bind(GLenum target, Uint unit = TempTextureUnit); void Bind(GLenum target, Uint unit = TempTextureUnit);
Uint GetBackendTextureId(); Uint GetBackendTextureId() const;
private: private:
Uint m_backendTextureId = 0; Uint m_backendTextureId = 0;
@@ -113,7 +204,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
void ActivateTextureUnit(Uint unit); void ActivateTextureUnit(Uint unit);
void UnbindTexture(Uint unit, GLenum target); void UnbindTexture(Uint unit, GLenum target);
extern UnorderedMap<SharedPtr<MG_State::GLState::ITextureObject>, SharedPtr<BackendTextureObject>> extern StateBackendObjectRegistry<MG_State::GLState::ITextureObject, BackendTextureObject>
g_backendTextureObjects; g_backendTextureObjects;
extern Array<Array<BackendTextureObject*, (SizeT)TextureTarget::TextureTargetCount>, extern Array<Array<BackendTextureObject*, (SizeT)TextureTarget::TextureTargetCount>,
MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS> MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS>
@@ -125,13 +216,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
class BackendFramebufferObject { class BackendFramebufferObject {
public: public:
BackendFramebufferObject(); BackendFramebufferObject();
void SyncToBackend(SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject, void SyncToBackend(const SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject,
FramebufferTarget asTarget); FramebufferTarget asTarget);
Uint GetBackendFramebufferId() { return m_backendFBOId; } Uint GetBackendFramebufferId() const { return m_backendFBOId; }
void Bind(FramebufferTarget target); void Bind(FramebufferTarget target) const;
bool SyncAttachmentObject(GLenum glFBOTarget,
const MG_State::GLState::FramebufferAttachmentObject& attachmentObject,
GLenum glBackendAttachment);
// FramebufferAttachmentType GetCompactedAttachmentTypeAtDrawBufferIndex(Int index); // FramebufferAttachmentType GetCompactedAttachmentTypeAtDrawBufferIndex(Int index);
GLenum GetBackendAttachmentType(FramebufferAttachmentType frontendAtt) const; GLenum GetBackendAttachmentType(FramebufferAttachmentType frontendAtt) const;
@@ -159,7 +247,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
FramebufferObject::FramebufferAttachmentVersionArray m_syncedFrontendAttachmentVersions = {0}; FramebufferObject::FramebufferAttachmentVersionArray m_syncedFrontendAttachmentVersions = {0};
}; };
extern UnorderedMap<SharedPtr<MG_State::GLState::FramebufferObject>, SharedPtr<BackendFramebufferObject>> extern StateBackendObjectRegistry<MG_State::GLState::FramebufferObject, BackendFramebufferObject>
g_backendFramebufferObjects; g_backendFramebufferObjects;
extern Array<Uint16, SizeT(FramebufferTarget::FramebufferTargetCount)> g_fboBindVersions; extern Array<Uint16, SizeT(FramebufferTarget::FramebufferTargetCount)> g_fboBindVersions;
} // namespace FramebufferImpl } // namespace FramebufferImpl
@@ -169,8 +257,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
public: public:
BackendProgramObjectImpl(); BackendProgramObjectImpl();
~BackendProgramObjectImpl(); ~BackendProgramObjectImpl();
void SyncToBackend(SharedPtr<MG_State::GLState::ProgramObject>& stateProgramObject); void SyncToBackend(const SharedPtr<MG_State::GLState::ProgramObject>& stateProgramObject);
void Use(); void Use() const;
Uint GetBackendProgramId() const { return m_backendProgramId; } Uint GetBackendProgramId() const { return m_backendProgramId; }
Uint GetBackendGlobalUBOId() const { return m_backendGlobalUBOId; } Uint GetBackendGlobalUBOId() const { return m_backendGlobalUBOId; }
@@ -180,7 +268,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Bool m_isInitialized = false; Bool m_isInitialized = false;
}; };
extern UnorderedMap<SharedPtr<MG_State::GLState::ProgramObject>, SharedPtr<BackendProgramObjectImpl>> extern StateBackendObjectRegistry<MG_State::GLState::ProgramObject, BackendProgramObjectImpl>
g_backendProgramObjects; g_backendProgramObjects;
} // namespace PrgramImpl } // namespace PrgramImpl
@@ -188,9 +276,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
class BackendSamplerObject { class BackendSamplerObject {
public: public:
BackendSamplerObject(); BackendSamplerObject();
void SyncToBackend(SharedPtr<MG_State::GLState::SamplerObject>& stateSamplerObject); void SyncToBackend(const SharedPtr<MG_State::GLState::SamplerObject>& stateSamplerObject);
void Bind(Uint unit); void Bind(Uint unit);
Uint GetBackendSamplerId(); Uint GetBackendSamplerId() const;
private: private:
Uint m_backendSamplerId = 0; Uint m_backendSamplerId = 0;
@@ -203,7 +291,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
extern Array<BackendSamplerObject*, MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS> extern Array<BackendSamplerObject*, MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS>
g_boundSamplersCache; g_boundSamplersCache;
extern UnorderedMap<SharedPtr<MG_State::GLState::SamplerObject>, SharedPtr<BackendSamplerObject>> extern StateBackendObjectRegistry<MG_State::GLState::SamplerObject, BackendSamplerObject>
g_backendSamplerObjects; g_backendSamplerObjects;
} // namespace SamplerImpl } // namespace SamplerImpl
@@ -212,8 +300,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
public: public:
BackendRenderbufferObject(); BackendRenderbufferObject();
void SyncToBackend(const SharedPtr<MG_State::GLState::RenderbufferObject>& stateRBOObject); void SyncToBackend(const SharedPtr<MG_State::GLState::RenderbufferObject>& stateRBOObject);
Uint GetBackendRenderbufferId() { return m_backendRBOId; } Uint GetBackendRenderbufferId() const { return m_backendRBOId; }
void Bind(); void Bind() const;
private: private:
Uint m_backendRBOId = 0; Uint m_backendRBOId = 0;
@@ -223,7 +311,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Int m_cacheHeight = 0; Int m_cacheHeight = 0;
}; };
extern UnorderedMap<SharedPtr<MG_State::GLState::RenderbufferObject>, SharedPtr<BackendRenderbufferObject>> extern StateBackendObjectRegistry<MG_State::GLState::RenderbufferObject, BackendRenderbufferObject>
g_backendRenderbufferObjects; g_backendRenderbufferObjects;
} // namespace RenderbufferImpl } // namespace RenderbufferImpl
} // namespace MobileGL::MG_Backend::DirectGLES } // namespace MobileGL::MG_Backend::DirectGLES
-9
View File
@@ -19,10 +19,6 @@
#include <MG_Util/Converters/MGToGL/FramebufferEnumConverter.h> #include <MG_Util/Converters/MGToGL/FramebufferEnumConverter.h>
namespace MobileGL::MG_Backend::DirectGLES { namespace MobileGL::MG_Backend::DirectGLES {
namespace BufferImpl {} // namespace BufferImpl
namespace VertexArrayImpl {} // namespace VertexArrayImpl
namespace TextureImpl { namespace TextureImpl {
void GenerateTextureFormatInfo(TextureInternalFormat internalFormat, GLenum* outInternalFormat, void GenerateTextureFormatInfo(TextureInternalFormat internalFormat, GLenum* outInternalFormat,
GLenum* outFormat, GLenum* outType) { GLenum* outFormat, GLenum* outType) {
@@ -36,9 +32,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
outInternalFormat, outFormat, outType); outInternalFormat, outFormat, outType);
} }
} // namespace TextureImpl } // namespace TextureImpl
namespace FramebufferImpl {} // namespace FramebufferImpl
namespace PrgramImpl { namespace PrgramImpl {
String ProcessOutColorLocations(const String& glslCode) { String ProcessOutColorLocations(const String& glslCode) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
@@ -166,7 +159,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
return GL_UNIFORM_BUFFER_BINDING; return GL_UNIFORM_BUFFER_BINDING;
case GL_FRAMEBUFFER: case GL_FRAMEBUFFER:
return GL_FRAMEBUFFER_BINDING;
case GL_DRAW_FRAMEBUFFER: case GL_DRAW_FRAMEBUFFER:
return GL_DRAW_FRAMEBUFFER_BINDING; return GL_DRAW_FRAMEBUFFER_BINDING;
case GL_READ_FRAMEBUFFER: case GL_READ_FRAMEBUFFER:
@@ -176,7 +168,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
return GL_RENDERBUFFER_BINDING; return GL_RENDERBUFFER_BINDING;
case GL_VERTEX_ARRAY: case GL_VERTEX_ARRAY:
return GL_VERTEX_ARRAY_BINDING;
case GL_VERTEX_ARRAY_BINDING: case GL_VERTEX_ARRAY_BINDING:
return GL_VERTEX_ARRAY_BINDING; return GL_VERTEX_ARRAY_BINDING;
+3 -3
View File
@@ -14,15 +14,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
namespace DebugImpl { namespace DebugImpl {
class ErrorLopper { class ErrorLopper {
public: public:
void Loop(std::function<void(GLenum)>); static void Loop(const std::function<void(GLenum)>&);
void Clear(); static void Clear();
ErrorLopper(); ErrorLopper();
~ErrorLopper(); ~ErrorLopper();
}; };
class OpenGLScopeMarker { class OpenGLScopeMarker {
public: public:
explicit OpenGLScopeMarker(String scopeName); explicit OpenGLScopeMarker(const String& scopeName);
~OpenGLScopeMarker(); ~OpenGLScopeMarker();
}; };
} // namespace DebugImpl } // namespace DebugImpl
@@ -28,6 +28,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
auto nativeWindow = reinterpret_cast<NativeWindowType>(m_windowHandle.Handle); auto nativeWindow = reinterpret_cast<NativeWindowType>(m_windowHandle.Handle);
pVulkanRenderer = MakeUnique<MG_Backend::DirectVulkan::VulkanRenderer>(nativeWindow); pVulkanRenderer = MakeUnique<MG_Backend::DirectVulkan::VulkanRenderer>(nativeWindow);
MOBILEGL_ASSERT(pVulkanRenderer != nullptr, "InitWindowSurface: VulkanRenderer creation failed");
pVulkanRenderer->Initialize(); pVulkanRenderer->Initialize();
return true; return true;
} }
+101 -390
View File
@@ -17,165 +17,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {} void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {}
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {} void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {}
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {} void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {}
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElementsBaseVertex called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawElementsBaseVertex called with null GL context");
if (count < 0) {
MGLOG_W("DrawElementsBaseVertex skipped: count (%d) must be non-negative", count);
return;
}
if (count == 0) {
return;
}
if (mode != GL_TRIANGLES) {
MGLOG_W("DrawElementsBaseVertex skipped: primitive mode %u is not supported yet", mode);
return;
}
SizeT indexSize = 0;
switch (type) {
case GL_UNSIGNED_SHORT:
indexSize = sizeof(Uint16);
break;
case GL_UNSIGNED_INT:
indexSize = sizeof(Uint32);
break;
default:
MGLOG_W("DrawElementsBaseVertex skipped: index type %u is not supported yet", type);
return;
}
const auto vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MGLOG_W("DrawElementsBaseVertex skipped: no bound VAO");
return;
}
const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject();
if (!indexBuffer) {
MGLOG_W("DrawElementsBaseVertex skipped: no bound ELEMENT_ARRAY_BUFFER");
return;
}
const auto indexData = indexBuffer->GetDataReadOnly();
if (!indexData || indexData->empty()) {
MGLOG_W("DrawElementsBaseVertex skipped: ELEMENT_ARRAY_BUFFER has no data");
return;
}
const SizeT byteOffset = reinterpret_cast<SizeT>(indices);
const SizeT requiredBytes = static_cast<SizeT>(count) * indexSize;
if (byteOffset + requiredBytes > indexBuffer->GetSize()) {
MGLOG_W("DrawElementsBaseVertex skipped: index range out of bounds (offset=%zu, size=%zu, buffer=%zu)",
byteOffset, requiredBytes, indexBuffer->GetSize());
return;
}
DrawElementPayload payload{};
payload.drawArray.mode = mode;
payload.drawArray.first = 0;
payload.drawArray.count = count;
const auto currentProgram = MG_State::pGLContext->GetCurrentProgram();
payload.drawArray.program = currentProgram ? currentProgram.get() : nullptr;
payload.drawArray.vertexArray = vao.get();
payload.indexType = type;
payload.indexByteOffset = byteOffset;
payload.baseVertex = basevertex;
pVulkanRenderer->DrawElements(payload);
}
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount, const GLint* basevertex) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElementsBaseVertex called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElementsBaseVertex called with null GL context");
if (drawcount < 0) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: drawcount (%d) must be non-negative", drawcount);
return;
}
if (drawcount == 0) {
return;
}
if (!count || !indices || !basevertex) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: count/indices/basevertex pointer is null");
return;
}
if (mode != GL_TRIANGLES) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: primitive mode %u is not supported yet", mode);
return;
}
SizeT indexSize = 0;
switch (type) {
case GL_UNSIGNED_SHORT:
indexSize = sizeof(Uint16);
break;
case GL_UNSIGNED_INT:
indexSize = sizeof(Uint32);
break;
default:
MGLOG_W("MultiDrawElementsBaseVertex skipped: index type %u is not supported yet", type);
return;
}
const auto vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: no bound VAO");
return;
}
const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject();
if (!indexBuffer) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: no bound ELEMENT_ARRAY_BUFFER");
return;
}
const auto indexData = indexBuffer->GetDataReadOnly();
if (!indexData || indexData->empty()) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: ELEMENT_ARRAY_BUFFER has no data");
return;
}
const auto currentProgram = MG_State::pGLContext->GetCurrentProgram();
Vector<DrawElementPayload> payloads;
payloads.reserve(static_cast<SizeT>(drawcount));
for (GLsizei i = 0; i < drawcount; ++i) {
if (count[i] < 0) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: count[%d] (%d) must be non-negative", i, count[i]);
return;
}
if (count[i] == 0) {
continue;
}
const SizeT byteOffset = reinterpret_cast<SizeT>(indices[i]);
const SizeT requiredBytes = static_cast<SizeT>(count[i]) * indexSize;
if (byteOffset + requiredBytes > indexBuffer->GetSize()) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: draw[%d] index range out of bounds (offset=%zu, "
"size=%zu, buffer=%zu)",
i, byteOffset, requiredBytes, indexBuffer->GetSize());
return;
}
DrawElementPayload payload{};
payload.drawArray.mode = mode;
payload.drawArray.first = 0;
payload.drawArray.count = count[i];
payload.drawArray.program = currentProgram ? currentProgram.get() : nullptr;
payload.drawArray.vertexArray = vao.get();
payload.indexType = type;
payload.indexByteOffset = byteOffset;
payload.baseVertex = basevertex[i];
payloads.push_back(payload);
}
if (payloads.empty()) {
return;
}
pVulkanRenderer->MultiDrawElements(payloads);
}
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {} 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 MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {}
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
@@ -201,256 +43,125 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {} 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 GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {}
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElements called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElements called with null GL context");
if (drawcount < 0) {
MGLOG_W("MultiDrawElements skipped: drawcount (%d) must be non-negative", drawcount);
return;
}
if (drawcount == 0) {
return;
}
if (!count || !indices) {
MGLOG_W("MultiDrawElements skipped: count/indices pointer is null");
return;
}
if (mode != GL_TRIANGLES) {
MGLOG_W("MultiDrawElements skipped: primitive mode %u is not supported yet", mode);
return;
}
SizeT indexSize = 0;
switch (type) {
case GL_UNSIGNED_SHORT:
indexSize = sizeof(Uint16);
break;
case GL_UNSIGNED_INT:
indexSize = sizeof(Uint32);
break;
default:
MGLOG_W("MultiDrawElements skipped: index type %u is not supported yet", type);
return;
}
const auto vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MGLOG_W("MultiDrawElements skipped: no bound VAO");
return;
}
const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject();
if (!indexBuffer) {
MGLOG_W("MultiDrawElements skipped: no bound ELEMENT_ARRAY_BUFFER");
return;
}
const auto indexData = indexBuffer->GetDataReadOnly();
if (!indexData || indexData->empty()) {
MGLOG_W("MultiDrawElements skipped: ELEMENT_ARRAY_BUFFER has no data");
return;
}
const auto currentProgram = MG_State::pGLContext->GetCurrentProgram();
Vector<DrawElementPayload> payloads;
payloads.reserve(static_cast<SizeT>(drawcount));
for (GLsizei i = 0; i < drawcount; ++i) {
if (count[i] < 0) {
MGLOG_W("MultiDrawElements skipped: count[%d] (%d) must be non-negative", i, count[i]);
return;
}
if (count[i] == 0) {
continue;
}
const auto byteOffset = reinterpret_cast<SizeT>(indices[i]);
const SizeT requiredBytes = static_cast<SizeT>(count[i]) * indexSize;
if (byteOffset + requiredBytes > indexBuffer->GetSize()) {
MGLOG_W("MultiDrawElements skipped: draw[%d] index range out of bounds (offset=%zu, size=%zu, "
"buffer=%zu)",
i, byteOffset, requiredBytes, indexBuffer->GetSize());
return;
}
DrawElementPayload payload{};
payload.drawArray.mode = mode;
payload.drawArray.first = 0;
payload.drawArray.count = count[i];
payload.drawArray.program = currentProgram ? currentProgram.get() : nullptr;
payload.drawArray.vertexArray = vao.get();
payload.indexType = type;
payload.indexByteOffset = byteOffset;
payloads.push_back(payload);
}
if (payloads.empty()) {
return;
}
pVulkanRenderer->MultiDrawElements(payloads);
}
void Clear(GLbitfield mask) { void Clear(GLbitfield mask) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::Clear called with null VulkanRenderer"); MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::Clear called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::Clear called with null GL context"); MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::Clear called with null GL context");
pVulkanRenderer->Clear(mask);
const auto& drawFboSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw);
const auto drawFbo = drawFboSlot.GetBoundObject();
const auto defaultFboInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo;
const auto defaultFbo = defaultFboInfo ? defaultFboInfo->defaultFBO : nullptr;
const Bool isDefaultFboTarget = (drawFbo == defaultFbo) || (drawFbo == nullptr && defaultFbo != nullptr);
Uint drawFboExternalIndex = 0;
if (drawFbo) {
drawFboExternalIndex = drawFbo->GetExternalIndex();
} else if (defaultFbo) {
drawFboExternalIndex = defaultFbo->GetExternalIndex();
}
const auto& clearColor = MG_State::pGLContext->GetClearColor();
const auto clearDepth = MG_State::pGLContext->GetClearDepth();
const auto clearStencil = static_cast<Uint32>(MG_State::pGLContext->GetClearStencil());
pVulkanRenderer->RequestClear(mask, clearColor, clearDepth, clearStencil, drawFboExternalIndex,
isDefaultFboTarget);
}
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElements called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawElements called with null GL context");
if (count < 0) {
MGLOG_W("DrawElements skipped: count (%d) must be non-negative", count);
return;
}
if (count == 0) {
return;
}
if (mode != GL_TRIANGLES) {
MGLOG_W("DrawElements skipped: primitive mode %u is not supported yet", mode);
return;
}
SizeT indexSize = 0;
switch (type) {
case GL_UNSIGNED_SHORT:
indexSize = sizeof(Uint16);
break;
case GL_UNSIGNED_INT:
indexSize = sizeof(Uint32);
break;
default:
MGLOG_W("DrawElements skipped: index type %u is not supported yet", type);
return;
}
const auto vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MGLOG_W("DrawElements skipped: no bound VAO");
return;
}
const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject();
if (!indexBuffer) {
MGLOG_W("DrawElements skipped: no bound ELEMENT_ARRAY_BUFFER");
return;
}
const auto indexData = indexBuffer->GetDataReadOnly();
if (!indexData || indexData->empty()) {
MGLOG_W("DrawElements skipped: ELEMENT_ARRAY_BUFFER has no data");
return;
}
const auto byteOffset = reinterpret_cast<SizeT>(indices);
const SizeT requiredBytes = static_cast<SizeT>(count) * indexSize;
if (byteOffset + requiredBytes > indexBuffer->GetSize()) {
MGLOG_W("DrawElements skipped: index range out of bounds (offset=%zu, size=%zu, buffer=%zu)", byteOffset,
requiredBytes, indexBuffer->GetSize());
return;
}
DrawElementPayload payload{};
payload.drawArray.mode = mode;
payload.drawArray.first = 0;
payload.drawArray.count = count;
const auto currentProgram = MG_State::pGLContext->GetCurrentProgram();
payload.drawArray.program = currentProgram ? currentProgram.get() : nullptr;
payload.drawArray.vertexArray = vao.get();
payload.indexType = type;
payload.indexByteOffset = byteOffset;
pVulkanRenderer->DrawElements(payload);
} }
void DrawArrays(GLenum mode, GLint first, GLsizei count) { void DrawArrays(GLenum mode, GLint first, GLsizei count) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArrays called with null VulkanRenderer"); MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArrays called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawArrays called with null GL context"); MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawArrays called with null GL context");
if (first < 0) { DrawCmd payload{};
MGLOG_W("DrawArrays skipped: first (%d) must be non-negative", first);
return;
}
if (count < 0) {
MGLOG_W("DrawArrays skipped: count (%d) must be non-negative", count);
return;
}
if (count == 0) {
return;
}
if (mode != GL_TRIANGLES) {
MGLOG_W("DrawArrays skipped: primitive mode %u is not supported yet", mode);
return;
}
DrawArrayPayload payload{};
payload.mode = mode; payload.mode = mode;
payload.first = first; payload.params.firstVertex = first;
payload.count = count; payload.params.vertexCount = count;
const auto currentProgram = MG_State::pGLContext->GetCurrentProgram();
payload.program = currentProgram ? currentProgram.get() : nullptr;
const auto vao = MG_State::pGLContext->GetBoundVertexArray();
payload.vertexArray = vao ? vao.get() : nullptr;
pVulkanRenderer->DrawArrays(payload); pVulkanRenderer->DrawArrays(payload);
} }
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElements called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawElements called with null GL context");
DrawIndexedCmd payload{};
payload.mode = mode;
payload.indexBufferView.indexType = type;
payload.indexBufferView.indexByteOffset = reinterpret_cast<SizeT>(indices);
payload.indexBufferView.indexByteSize = count * MG_Util::GetGLTypeSize(type);
payload.params.indexCount = count;
payload.params.instanceCount = 1;
pVulkanRenderer->DrawElements(payload);
}
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElements called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElements called with null GL context");
// Vector<DrawElementCmd> cmds;
// cmds.reserve(static_cast<SizeT>(drawcount));
// for (GLsizei i = 0; i < drawcount; ++i) {
// if (count[i] == 0) {
// continue;
// }
//
// DrawElementCmd payload{};
// payload.mode = mode;
// payload.firstVertex = 0;
// payload.indexCount = count[i];
// payload.indexType = type;
// payload.indexByteOffset = reinterpret_cast<SizeT>(indices[i]);
// cmds.push_back(payload);
// }
//
// if (cmds.empty()) {
// return;
// }
// pVulkanRenderer->MultiDrawElements(cmds);
}
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElementsBaseVertex called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawElementsBaseVertex called with null GL context");
DrawIndexedCmd payload{};
payload.mode = mode;
payload.indexBufferView.indexType = type;
payload.indexBufferView.indexByteOffset = reinterpret_cast<SizeT>(indices);
payload.indexBufferView.indexByteSize = count * MG_Util::GetGLTypeSize(type);
payload.params.indexCount = count;
payload.params.instanceCount = 1;
payload.params.firstIndex = 0;
payload.params.vertexOffset = basevertex;
payload.params.firstInstance = 0;
pVulkanRenderer->DrawElements(payload);
}
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount, const GLint* basevertex) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElements called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElements called with null GL context");
MultiDrawIndexedCmd payload{};
payload.mode = mode;
payload.indexBufferView.indexType = type;
// TODO: allocate draw cmd buf elsewhere
static Vector<DrawIndexedCmdParam> params;
params.clear();
params.resize(drawcount);
for (GLsizei i = 0; i < drawcount; ++i) {
if (count[i] == 0) {
continue;
}
// TODO: this index view needs a redesign, now there's a lotta redundant uploads
payload.indexBufferView.indexByteOffset = 0;
payload.indexBufferView.indexByteSize =
std::max(reinterpret_cast<SizeT>(indices[i]) + count[i] * MG_Util::GetGLTypeSize(type),
payload.indexBufferView.indexByteSize);
auto& param = params[i];
param.indexCount = count[i];
param.instanceCount = 1;
param.firstIndex = reinterpret_cast<SizeT>(indices[i]) / MG_Util::GetGLTypeSize(type);
param.vertexOffset = basevertex[i];
param.firstInstance = 0;
}
payload.drawCount = drawcount;
payload.pParams = params.data();
pVulkanRenderer->MultiDrawElements(payload);
}
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
GLint dstY1, GLbitfield mask, GLenum filter) { GLint dstY1, GLbitfield mask, GLenum filter) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::BlitFramebuffer called with null VulkanRenderer"); MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::BlitFramebuffer called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::BlitFramebuffer called with null GL context"); MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::BlitFramebuffer called with null GL context");
pVulkanRenderer->BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
const auto& readFboSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read);
const auto& drawFboSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw);
const auto readFbo = readFboSlot.GetBoundObject();
const auto drawFbo = drawFboSlot.GetBoundObject();
const auto defaultFboInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo;
const auto defaultFbo = defaultFboInfo ? defaultFboInfo->defaultFBO : nullptr;
const Bool readIsDefault = (readFbo == defaultFbo) || (readFbo == nullptr && defaultFbo != nullptr);
const Bool drawIsDefault = (drawFbo == defaultFbo) || (drawFbo == nullptr && defaultFbo != nullptr);
Uint readFboExternalIndex = 0;
if (readFbo) {
readFboExternalIndex = readFbo->GetExternalIndex();
} else if (defaultFbo) {
readFboExternalIndex = defaultFbo->GetExternalIndex();
}
Uint drawFboExternalIndex = 0;
if (drawFbo) {
drawFboExternalIndex = drawFbo->GetExternalIndex();
} else if (defaultFbo) {
drawFboExternalIndex = defaultFbo->GetExternalIndex();
}
pVulkanRenderer->BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter,
readFboExternalIndex, drawFboExternalIndex, readIsDefault, drawIsDefault);
} }
void Present() { void Present() {
@@ -0,0 +1,138 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/BufferArena.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 "BufferArena.h"
namespace MobileGL::MG_Backend::DirectVulkan {
Bool BufferArena::Initialize(const BufferArenaDesc& desc) {
Shutdown();
MOBILEGL_ASSERT(desc.allocator != nullptr, "BufferArena::Initialize requires valid allocator");
MOBILEGL_ASSERT(desc.frameCount > 0, "BufferArena::Initialize requires non-zero frame count");
MOBILEGL_ASSERT(desc.usage != 0, "BufferArena::Initialize requires non-zero buffer usage");
m_desc = desc;
m_frames.clear();
m_frames.resize(desc.frameCount);
m_deferredReleases.resize(desc.frameCount);
return true;
}
void BufferArena::Shutdown() {
for (auto& frame : m_frames) {
frame.buffer.Destroy();
frame.writeCursor = 0;
}
m_frames.clear();
m_deferredReleases.clear();
m_desc = {};
}
void BufferArena::BeginFrame(Uint32 frameIndex) {
CollectDeferredReleases(frameIndex);
ResetFrame(frameIndex);
}
void BufferArena::ResetFrame(Uint32 frameIndex) {
AssertValidFrameIndex(frameIndex);
m_frames[frameIndex].writeCursor = 0;
}
void BufferArena::CollectDeferredReleases(Uint32 frameIndex) {
AssertValidFrameIndex(frameIndex);
m_deferredReleases[frameIndex].clear();
}
Bool BufferArena::Allocate(Uint32 frameIndex, VkDeviceSize size, VkDeviceSize alignment, BufferSlice& outSlice) {
AssertValidFrameIndex(frameIndex);
MOBILEGL_ASSERT(size > 0, "BufferArena::Allocate requires non-zero size");
auto& frame = m_frames[frameIndex];
const VkDeviceSize resolvedAlignment = alignment > 0 ? alignment : 1;
const VkDeviceSize offset = (frame.writeCursor + resolvedAlignment - 1) & ~(resolvedAlignment - 1);
const VkDeviceSize endOffset = offset + size;
if (!EnsureCapacity(frameIndex, endOffset)) {
return false;
}
frame.writeCursor = endOffset;
outSlice = frame.buffer.GetSlice(offset, size);
return outSlice.IsValid();
}
Bool BufferArena::Upload(Uint32 frameIndex, const void* data, VkDeviceSize size, VkDeviceSize alignment,
BufferSlice& outSlice) {
MOBILEGL_ASSERT(data != nullptr || size == 0, "BufferArena::Upload data pointer is null");
if (!Allocate(frameIndex, size, alignment, outSlice)) {
return false;
}
if (outSlice.mapped != nullptr) {
Memcpy(outSlice.mapped, data, static_cast<SizeT>(size));
return true;
}
return m_frames[frameIndex].buffer.Upload(data, size, outSlice.offset);
}
VkDeviceSize BufferArena::GetWriteCursor(Uint32 frameIndex) const {
AssertValidFrameIndex(frameIndex);
return m_frames[frameIndex].writeCursor;
}
Uint32 BufferArena::GetFrameCount() const {
return static_cast<Uint32>(m_frames.size());
}
Bool BufferArena::EnsureCapacity(Uint32 frameIndex, VkDeviceSize requiredEndOffset) {
AssertValidFrameIndex(frameIndex);
auto& frame = m_frames[frameIndex];
auto& buffer = frame.buffer;
if (buffer.IsValid() && buffer.GetSize() >= requiredEndOffset) {
return true;
}
VkDeviceSize newCapacity = buffer.IsValid() ? buffer.GetSize() : 0;
if (newCapacity < m_desc.minBufferSize) {
newCapacity = m_desc.minBufferSize;
}
if (newCapacity == 0) {
newCapacity = requiredEndOffset;
}
while (newCapacity < requiredEndOffset) {
newCapacity *= 2;
}
if (buffer.IsValid()) {
m_deferredReleases[frameIndex].push_back(std::move(buffer));
}
VkBufferObjectDesc bufferDesc{};
bufferDesc.allocator = m_desc.allocator;
bufferDesc.size = newCapacity;
bufferDesc.usage = m_desc.usage;
bufferDesc.memoryUsage = m_desc.memoryUsage;
bufferDesc.allocationFlags = m_desc.allocationFlags;
if (!buffer.Create(bufferDesc)) {
return false;
}
if (m_desc.persistentlyMapped && buffer.Map() == nullptr) {
buffer.Destroy();
return false;
}
frame.writeCursor = 0;
return true;
}
void BufferArena::AssertValidFrameIndex(Uint32 frameIndex) const {
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "BufferArena frame index out of range");
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,56 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/BufferArena.h
// 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
#pragma once
#include "BufferSlice.h"
#include "VkBufferObject.h"
#include "../VkIncludes.h"
#include <Includes.h>
#include <vk_mem_alloc.h>
namespace MobileGL::MG_Backend::DirectVulkan {
struct BufferArenaDesc {
VmaAllocator allocator = nullptr;
Uint32 frameCount = 0;
VkBufferUsageFlags usage = 0;
VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
VmaAllocationCreateFlags allocationFlags = 0;
VkDeviceSize minBufferSize = 0;
Bool persistentlyMapped = false;
};
class BufferArena {
public:
Bool Initialize(const BufferArenaDesc& desc);
void Shutdown();
void BeginFrame(Uint32 frameIndex);
void ResetFrame(Uint32 frameIndex);
void CollectDeferredReleases(Uint32 frameIndex);
Bool Allocate(Uint32 frameIndex, VkDeviceSize size, VkDeviceSize alignment, BufferSlice& outSlice);
Bool Upload(Uint32 frameIndex, const void* data, VkDeviceSize size, VkDeviceSize alignment, BufferSlice& outSlice);
VkDeviceSize GetWriteCursor(Uint32 frameIndex) const;
Uint32 GetFrameCount() const;
private:
struct FrameResources {
VkBufferObject buffer;
VkDeviceSize writeCursor = 0;
};
Bool EnsureCapacity(Uint32 frameIndex, VkDeviceSize requiredEndOffset);
void AssertValidFrameIndex(Uint32 frameIndex) const;
BufferArenaDesc m_desc{};
Vector<FrameResources> m_frames;
Vector<Vector<VkBufferObject>> m_deferredReleases;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,23 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/BufferSlice.h
// 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
#pragma once
#include "../VkIncludes.h"
#include <Includes.h>
namespace MobileGL::MG_Backend::DirectVulkan {
struct BufferSlice {
VkBuffer buffer = VK_NULL_HANDLE;
VkDeviceSize offset = 0;
VkDeviceSize size = 0;
void* mapped = nullptr;
Bool IsValid() const { return buffer != VK_NULL_HANDLE; }
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -21,6 +21,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.renderPass, sizeof(payload.renderPass))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.renderPass, sizeof(payload.renderPass)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.subpass, sizeof(payload.subpass))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.subpass, sizeof(payload.subpass)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.topology, sizeof(payload.topology))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.topology, sizeof(payload.topology)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.cullMode, sizeof(payload.cullMode)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.frontFace, sizeof(payload.frontFace)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.depthTestEnable, sizeof(payload.depthTestEnable))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.depthTestEnable, sizeof(payload.depthTestEnable)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.depthWriteEnable, sizeof(payload.depthWriteEnable))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.depthWriteEnable, sizeof(payload.depthWriteEnable)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.depthCompareOp, sizeof(payload.depthCompareOp))); XXHASH_VERIFY(XXH64_update(m_hashState, &payload.depthCompareOp, sizeof(payload.depthCompareOp)));
@@ -79,8 +81,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO}; VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
raster.polygonMode = VK_POLYGON_MODE_FILL; raster.polygonMode = VK_POLYGON_MODE_FILL;
raster.cullMode = VK_CULL_MODE_NONE; raster.cullMode = payload.cullMode;
raster.frontFace = VK_FRONT_FACE_CLOCKWISE; raster.frontFace = payload.frontFace;
raster.lineWidth = 1.0f; raster.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo ms{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; VkPipelineMultisampleStateCreateInfo ms{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
@@ -24,6 +24,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkRenderPass renderPass = VK_NULL_HANDLE; VkRenderPass renderPass = VK_NULL_HANDLE;
Uint32 subpass = 0; Uint32 subpass = 0;
VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT;
VkFrontFace frontFace = VK_FRONT_FACE_CLOCKWISE;
Bool depthTestEnable = false; Bool depthTestEnable = false;
Bool depthWriteEnable = false; Bool depthWriteEnable = false;
VkCompareOp depthCompareOp = VK_COMPARE_OP_ALWAYS; VkCompareOp depthCompareOp = VK_COMPARE_OP_ALWAYS;
@@ -8,6 +8,9 @@
#include "ProgramFactory.h" #include "ProgramFactory.h"
#include "MG_Util/ShaderTranspiler/SpvcSession.h"
#include "MG_Util/ShaderTranspiler/Types.h"
#include <cstring>
#include <spirv-tools/libspirv.h> #include <spirv-tools/libspirv.h>
#include <spirv-tools/optimizer.hpp> #include <spirv-tools/optimizer.hpp>
#include <source/opt/constants.h> #include <source/opt/constants.h>
@@ -21,6 +24,8 @@
namespace MobileGL::MG_Backend::DirectVulkan { namespace MobileGL::MG_Backend::DirectVulkan {
namespace { namespace {
using ShaderObject = MG_State::GLState::ShaderObject; using ShaderObject = MG_State::GLState::ShaderObject;
using SpvcSession = MG_Util::ShaderTranspiler::SpvcSession;
using SessionUsageBit = MG_Util::ShaderTranspiler::SessionUsageBit;
struct PositionTargetInfo { struct PositionTargetInfo {
Uint32 variableId = 0; Uint32 variableId = 0;
@@ -321,8 +326,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
} // namespace } // namespace
ProgramFactory::~ProgramFactory() = default;
VkShaderStageFlagBits ProgramFactory::ToVkStage(ShaderStage stage) { VkShaderStageFlagBits ProgramFactory::ToVkStage(ShaderStage stage) {
switch (stage) { switch (stage) {
case ShaderStage::Vertex: case ShaderStage::Vertex:
@@ -351,20 +354,184 @@ namespace MobileGL::MG_Backend::DirectVulkan {
XXHASH_VERIFY(XXH64_update(m_hashState, spv.data(), spv.size() * sizeof(Uint))); XXHASH_VERIFY(XXH64_update(m_hashState, spv.data(), spv.size() * sizeof(Uint)));
} }
XXHASH_VERIFY(XXH64_update(m_hashState, &flags, sizeof(CompileOptionFlags))); XXHASH_VERIFY(XXH64_update(m_hashState, &flags, sizeof(CompileOptionFlags)));
// Include UBO block bindings in hash so different binding configurations produce different entries
const Uint32 blockCount = static_cast<Uint32>(program.GetActiveUniformBlocksCount());
XXHASH_VERIFY(XXH64_update(m_hashState, &blockCount, sizeof(blockCount)));
for (Uint32 i = 0; i < blockCount; ++i) {
const Uint32 binding = program.GetUniformBlockBinding(i);
XXHASH_VERIFY(XXH64_update(m_hashState, &binding, sizeof(binding)));
}
HashType hash = XXH64_digest(m_hashState); HashType hash = XXH64_digest(m_hashState);
return hash; return hash;
} }
Vector<VkPipelineShaderStageCreateInfo>& ProgramFactory::GetOrCreatePipelineShaderStages( TextureTarget ProgramFactory::UniformTypeToTextureTarget(GLenum glType) {
switch (glType) {
case GL_SAMPLER_1D:
case GL_INT_SAMPLER_1D:
case GL_UNSIGNED_INT_SAMPLER_1D:
return TextureTarget::Texture1D;
case GL_SAMPLER_3D:
case GL_INT_SAMPLER_3D:
case GL_UNSIGNED_INT_SAMPLER_3D:
return TextureTarget::Texture3D;
case GL_SAMPLER_CUBE:
case GL_SAMPLER_CUBE_SHADOW:
case GL_INT_SAMPLER_CUBE:
case GL_UNSIGNED_INT_SAMPLER_CUBE:
return TextureTarget::TextureCubeMap;
case GL_SAMPLER_2D_MULTISAMPLE:
case GL_INT_SAMPLER_2D_MULTISAMPLE:
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
return TextureTarget::Texture2DMultisample;
case GL_SAMPLER_BUFFER:
case GL_INT_SAMPLER_BUFFER:
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
return TextureTarget::TextureBuffer;
case GL_SAMPLER_1D_ARRAY:
case GL_SAMPLER_1D_ARRAY_SHADOW:
case GL_INT_SAMPLER_1D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
return TextureTarget::Texture1DArray;
case GL_SAMPLER_2D_ARRAY:
case GL_SAMPLER_2D_ARRAY_SHADOW:
case GL_INT_SAMPLER_2D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
return TextureTarget::Texture2DArray;
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
return TextureTarget::Texture2DMultisampleArray;
case GL_SAMPLER_2D_RECT:
case GL_SAMPLER_2D_RECT_SHADOW:
case GL_INT_SAMPLER_2D_RECT:
case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
return TextureTarget::TextureRectangle;
case GL_SAMPLER_2D:
case GL_SAMPLER_2D_SHADOW:
case GL_INT_SAMPLER_2D:
case GL_UNSIGNED_INT_SAMPLER_2D:
default:
return TextureTarget::Texture2D;
}
}
void ProgramFactory::ReflectLayout(const MG_State::GLState::ProgramObject& program,
VkProgramObject& entry) const {
// Initialize layout vectors
entry.bindingKinds.assign(m_maxBindings, DescriptorBindingKind::None);
entry.samplerUniformLocationByBinding.assign(m_maxBindings, -1);
entry.samplerTextureTargetByBinding.assign(m_maxBindings, TextureTarget::Texture2D);
entry.globalUboBinding = -1;
entry.dynamicBindings.clear();
// Use SpvcSession (Reflection mode) to reflect all SPIR-V modules in a single pass per module
const auto& spirv = program.GetGeneratedSpirv();
for (const auto& module : spirv) {
if (module.empty()) {
continue;
}
SpvcSession session(module, SessionUsageBit::Reflection);
// Reflect uniform buffers
auto ubos = session.GetShaderInterface(SPVC_RESOURCE_TYPE_UNIFORM_BUFFER);
for (const auto& ubo : ubos) {
const Uint32 binding = ubo.location; // GetShaderInterface stores binding in location field
if (binding >= m_maxBindings) {
continue;
}
if (entry.bindingKinds[binding] == DescriptorBindingKind::None) {
entry.bindingKinds[binding] = DescriptorBindingKind::UniformBufferDynamic;
}
// Check for global UBO
if (entry.globalUboBinding < 0 &&
std::strstr(ubo.name.c_str(), MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME) != nullptr) {
entry.globalUboBinding = static_cast<Int>(binding);
}
}
// Reflect sampled images
auto samplers = session.GetShaderInterface(SPVC_RESOURCE_TYPE_SAMPLED_IMAGE);
for (const auto& sampler : samplers) {
const Uint32 binding = sampler.location; // GetShaderInterface stores binding in location field
if (binding >= m_maxBindings) {
continue;
}
// Sampler always wins over UBO for a binding slot
entry.bindingKinds[binding] = DescriptorBindingKind::CombinedImageSampler;
// Resolve uniform location for this sampler
String uniformName = sampler.name;
Int location = program.GetUniformLocation(uniformName);
if (location < 0) {
const auto arraySuffix = uniformName.find("[0]");
if (arraySuffix != String::npos) {
uniformName = uniformName.substr(0, arraySuffix);
location = program.GetUniformLocation(uniformName);
}
}
if (location < 0) {
continue;
}
entry.samplerUniformLocationByBinding[binding] = location;
entry.samplerTextureTargetByBinding[binding] =
UniformTypeToTextureTarget(program.GetUniformType(static_cast<Uint>(location)));
}
}
// Build Vulkan descriptor set layout and pipeline layout from reflected binding kinds
Vector<VkDescriptorSetLayoutBinding> bindings;
bindings.reserve(m_maxBindings);
for (Uint32 binding = 0; binding < m_maxBindings; ++binding) {
const auto kind = entry.bindingKinds[binding];
if (kind == DescriptorBindingKind::None) {
continue;
}
VkDescriptorSetLayoutBinding layoutBinding{};
layoutBinding.binding = binding;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
layoutBinding.pImmutableSamplers = nullptr;
if (kind == DescriptorBindingKind::UniformBufferDynamic) {
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
entry.dynamicBindings.push_back(binding);
} else {
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
}
bindings.push_back(layoutBinding);
}
VkDescriptorSetLayoutCreateInfo setLayoutInfo{};
setLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
setLayoutInfo.bindingCount = static_cast<Uint32>(bindings.size());
setLayoutInfo.pBindings = bindings.data();
VK_VERIFY(vkCreateDescriptorSetLayout(m_device, &setLayoutInfo, nullptr, &entry.descriptorSetLayout),
"ProgramFactory::ReflectLayout, vkCreateDescriptorSetLayout");
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &entry.descriptorSetLayout;
VK_VERIFY(vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &entry.pipelineLayout),
"ProgramFactory::ReflectLayout, vkCreatePipelineLayout");
}
const ProgramFactory::VkProgramObject& ProgramFactory::GetOrCreateProgram(
const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags) { const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags) {
auto hash = ComputeHash(program, flags); auto hash = ComputeHash(program, flags);
auto it = m_cache.find(hash); auto it = m_cache.find(hash);
if (it != m_cache.end()) { if (it != m_cache.end()) {
return it->second.stages; return it->second;
} }
auto& entry = m_cache[hash]; auto& entry = m_cache[hash];
entry.device = m_device;
entry.hash = hash; entry.hash = hash;
auto& shaders = program.GetAttachedShaders(); auto& shaders = program.GetAttachedShaders();
auto& spirv = program.GetGeneratedSpirv(); auto& spirv = program.GetGeneratedSpirv();
@@ -401,6 +568,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
entry.stages.push_back(stage); entry.stages.push_back(stage);
} }
return entry.stages; // Reflect and create layout as part of the program object
ReflectLayout(program, entry);
return entry;
} }
} // namespace MobileGL::MG_Backend::DirectVulkan } // namespace MobileGL::MG_Backend::DirectVulkan
@@ -11,11 +11,19 @@
#include "../VkIncludes.h" #include "../VkIncludes.h"
#include "MG_State/GLState/ProgramState/ProgramObject.h" #include "MG_State/GLState/ProgramState/ProgramObject.h"
#include "MG_State/GLState/ProgramState/ShaderObject.h" #include "MG_State/GLState/ProgramState/ShaderObject.h"
#include "MG_State/GLState/TextureState/TextureEnum.h"
#include <Includes.h> #include <Includes.h>
namespace MobileGL::MG_Backend::DirectVulkan { namespace MobileGL::MG_Backend::DirectVulkan {
class ProgramFactory { class ProgramFactory {
public: public:
enum class DescriptorBindingKind : Uint8 {
None = 0,
UniformBufferDynamic,
CombinedImageSampler
};
enum class CompileOptionBit : Uint { enum class CompileOptionBit : Uint {
None = 0, None = 0,
PositionYFlip = 1 << 0, PositionYFlip = 1 << 0,
@@ -26,68 +34,110 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}; };
using CompileOptionFlags = Flags<CompileOptionBit>; using CompileOptionFlags = Flags<CompileOptionBit>;
using HashType = Uint64; using HashType = Uint64;
struct BackendProgramObject {
struct VkProgramObject {
HashType hash = 0; HashType hash = 0;
VkDevice device = VK_NULL_HANDLE;
Vector<VkPipelineShaderStageCreateInfo> stages; Vector<VkPipelineShaderStageCreateInfo> stages;
Vector<VkShaderModule> modules; Vector<VkShaderModule> modules;
BackendProgramObject() = default; // Layout data (previously in separate VkProgramLayout)
BackendProgramObject(const BackendProgramObject&) = delete; VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
BackendProgramObject& operator=(const BackendProgramObject&) = delete; VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
BackendProgramObject(BackendProgramObject&& other) noexcept { Vector<DescriptorBindingKind> bindingKinds;
Vector<Uint32> dynamicBindings;
Vector<Int> samplerUniformLocationByBinding;
Vector<TextureTarget> samplerTextureTargetByBinding;
Int globalUboBinding = -1;
static inline VkDevice s_device = VK_NULL_HANDLE;
VkProgramObject() = default;
VkProgramObject(const VkProgramObject&) = delete;
VkProgramObject& operator=(const VkProgramObject&) = delete;
VkProgramObject(VkProgramObject&& other) noexcept {
hash = other.hash; hash = other.hash;
device = other.device;
stages = std::move(other.stages); stages = std::move(other.stages);
modules = std::move(other.modules); modules = std::move(other.modules);
descriptorSetLayout = other.descriptorSetLayout;
pipelineLayout = other.pipelineLayout;
bindingKinds = std::move(other.bindingKinds);
dynamicBindings = std::move(other.dynamicBindings);
samplerUniformLocationByBinding = std::move(other.samplerUniformLocationByBinding);
samplerTextureTargetByBinding = std::move(other.samplerTextureTargetByBinding);
globalUboBinding = other.globalUboBinding;
other.hash = 0; other.hash = 0;
other.device = VK_NULL_HANDLE; other.descriptorSetLayout = VK_NULL_HANDLE;
other.pipelineLayout = VK_NULL_HANDLE;
other.globalUboBinding = -1;
} }
BackendProgramObject& operator=(BackendProgramObject&& other) noexcept { VkProgramObject& operator=(VkProgramObject&& other) noexcept {
if (this == &other) { if (this == &other) {
return *this; return *this;
} }
DestroyModules(); Destroy();
stages.clear();
hash = other.hash; hash = other.hash;
device = other.device;
stages = std::move(other.stages); stages = std::move(other.stages);
modules = std::move(other.modules); modules = std::move(other.modules);
descriptorSetLayout = other.descriptorSetLayout;
pipelineLayout = other.pipelineLayout;
bindingKinds = std::move(other.bindingKinds);
dynamicBindings = std::move(other.dynamicBindings);
samplerUniformLocationByBinding = std::move(other.samplerUniformLocationByBinding);
samplerTextureTargetByBinding = std::move(other.samplerTextureTargetByBinding);
globalUboBinding = other.globalUboBinding;
other.hash = 0; other.hash = 0;
other.device = VK_NULL_HANDLE; other.descriptorSetLayout = VK_NULL_HANDLE;
other.pipelineLayout = VK_NULL_HANDLE;
other.globalUboBinding = -1;
return *this; return *this;
} }
~BackendProgramObject() { ~VkProgramObject() {
DestroyModules(); Destroy();
stages.clear();
} }
private: private:
void DestroyModules() { void Destroy() {
for (auto module : modules) { if (s_device != VK_NULL_HANDLE) {
if (module != VK_NULL_HANDLE && device != VK_NULL_HANDLE) { if (pipelineLayout != VK_NULL_HANDLE) {
vkDestroyShaderModule(device, module, nullptr); vkDestroyPipelineLayout(s_device, pipelineLayout, nullptr);
pipelineLayout = VK_NULL_HANDLE;
}
if (descriptorSetLayout != VK_NULL_HANDLE) {
vkDestroyDescriptorSetLayout(s_device, descriptorSetLayout, nullptr);
descriptorSetLayout = VK_NULL_HANDLE;
}
for (auto module : modules) {
if (module != VK_NULL_HANDLE) {
vkDestroyShaderModule(s_device, module, nullptr);
}
} }
} }
modules.clear(); modules.clear();
stages.clear();
} }
}; };
explicit ProgramFactory(VkDevice device, const VulkanRendererConfig& config) explicit ProgramFactory(VkDevice device, const VulkanRendererConfig& config, Uint32 maxBindings = 16)
: m_device(device), m_config(config) {} : m_device(device), m_config(config), m_maxBindings(maxBindings) {
~ProgramFactory(); VkProgramObject::s_device = device;
}
~ProgramFactory() = default;
ProgramFactory(const ProgramFactory&) = delete; ProgramFactory(const ProgramFactory&) = delete;
HashType ComputeHash(const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags) const; HashType ComputeHash(const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags) const;
Vector<VkPipelineShaderStageCreateInfo>& GetOrCreatePipelineShaderStages( const VkProgramObject& GetOrCreateProgram(
const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags); const MG_State::GLState::ProgramObject& program, CompileOptionFlags flags);
static VkShaderStageFlagBits ToVkStage(ShaderStage stage); static VkShaderStageFlagBits ToVkStage(ShaderStage stage);
private: private:
static TextureTarget UniformTypeToTextureTarget(GLenum glType);
void ReflectLayout(const MG_State::GLState::ProgramObject& program, VkProgramObject& entry) const;
VkDevice m_device = VK_NULL_HANDLE; VkDevice m_device = VK_NULL_HANDLE;
UnorderedMap<HashType, BackendProgramObject> m_cache; Uint32 m_maxBindings = 0;
UnorderedMap<HashType, VkProgramObject> m_cache;
const VulkanRendererConfig& m_config; const VulkanRendererConfig& m_config;
static inline XXH64_state_t* m_hashState = XXH64_createState(); static inline XXH64_state_t* m_hashState = XXH64_createState();
}; };
@@ -8,6 +8,9 @@
#include "SwapchainObject.h" #include "SwapchainObject.h"
#include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h"
#include "MG_State/GLState/TextureState/TextureObject2D.h"
#if defined(__has_include) #if defined(__has_include)
#if __has_include(<vulkan/vk_enum_string_helper.h>) #if __has_include(<vulkan/vk_enum_string_helper.h>)
#include <vulkan/vk_enum_string_helper.h> #include <vulkan/vk_enum_string_helper.h>
@@ -38,6 +41,40 @@ static const char* string_VkSurfaceTransformFlagBitsKHR(VkSurfaceTransformFlagBi
#endif #endif
namespace MobileGL::MG_Backend::DirectVulkan { namespace MobileGL::MG_Backend::DirectVulkan {
namespace {
Bool HasStencilComponent(VkFormat format) {
return format == VK_FORMAT_D24_UNORM_S8_UINT || format == VK_FORMAT_D32_SFLOAT_S8_UINT;
}
VkFormat FindSupportedDepthStencilFormat(VkPhysicalDevice physicalDevice) {
const VkFormat candidates[] = {VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D32_SFLOAT};
for (VkFormat format : candidates) {
VkFormatProperties props{};
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);
if ((props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) {
return format;
}
}
return VK_FORMAT_UNDEFINED;
}
Uint32 FindMemoryType(VkPhysicalDevice physicalDevice, Uint32 typeFilter, VkMemoryPropertyFlags properties) {
VkPhysicalDeviceMemoryProperties memProperties{};
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
for (Uint32 i = 0; i < memProperties.memoryTypeCount; i++) {
if ((typeFilter & (1 << i)) &&
(memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
return i;
}
}
MOBILEGL_ASSERT(false, "Failed to find suitable memory type.");
return 0;
}
} // namespace
SwapchainObject::SwapchainCapabilities SwapchainObject::GetSwapchainCapabilities(VkPhysicalDevice physicalDevice, SwapchainObject::SwapchainCapabilities SwapchainObject::GetSwapchainCapabilities(VkPhysicalDevice physicalDevice,
VkSurfaceKHR surface) { VkSurfaceKHR surface) {
SwapchainCapabilities swapchainCapabilities{}; SwapchainCapabilities swapchainCapabilities{};
@@ -127,6 +164,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
createInfo.imageFormat = pickedSurfaceFormat.format; createInfo.imageFormat = pickedSurfaceFormat.format;
createInfo.imageColorSpace = pickedSurfaceFormat.colorSpace; createInfo.imageColorSpace = pickedSurfaceFormat.colorSpace;
createInfo.imageExtent = swapchainCaps.currentExtent; createInfo.imageExtent = swapchainCaps.currentExtent;
if (swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
std::swap(createInfo.imageExtent.width, createInfo.imageExtent.height);
}
createInfo.imageArrayLayers = 1; createInfo.imageArrayLayers = 1;
const VkImageUsageFlags requiredImageUsage = const VkImageUsageFlags requiredImageUsage =
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
@@ -185,12 +227,133 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_imageLayouts.assign(imageCount, VK_IMAGE_LAYOUT_UNDEFINED); m_imageLayouts.assign(imageCount, VK_IMAGE_LAYOUT_UNDEFINED);
CreateImageViews(device); CreateImageViews(device);
CreateDepthStencilResources(device, physicalDevice);
MGLOG_I("Swapchain created, extent = %dx%d, swapchain imageCount = %d", m_extent.width, m_extent.height, MGLOG_I("Swapchain created, extent = %dx%d, swapchain imageCount = %d", m_extent.width, m_extent.height,
imageCount); imageCount);
// Properly initialize Default FBO here
auto& defaultFBOInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo;
auto* colorTex = static_cast<MG_State::GLState::TextureObject2D*>(defaultFBOInfo->colorAttachment.get());
colorTex->AllocateStorage(
TextureUploadTarget::Texture2D, 0, {
{(Int)createInfo.imageExtent.width, (Int)createInfo.imageExtent.height, 1},
createInfo.imageExtent.width * (Int)createInfo.imageExtent.height * 4}); // TODO: 4 is format size
TextureInternalFormat depthFormat = TextureInternalFormat::Depth24Stencil8;
switch (m_depthStencilFormat) {
case VK_FORMAT_D24_UNORM_S8_UINT:
depthFormat = TextureInternalFormat::Depth24Stencil8;
break;
case VK_FORMAT_D32_SFLOAT_S8_UINT:
depthFormat = TextureInternalFormat::Depth32FStencil8;
break;
case VK_FORMAT_D32_SFLOAT:
depthFormat = TextureInternalFormat::DepthComponent32F;
break;
default:
depthFormat = TextureInternalFormat::Depth24Stencil8;
break;
}
auto* depthTex = static_cast<MG_State::GLState::TextureObject2D*>(defaultFBOInfo->depthAttachment.get());
depthTex->SetInternalFormat(depthFormat);
depthTex->AllocateStorage(TextureUploadTarget::Texture2D, 0, {
{(Int)createInfo.imageExtent.width, (Int)createInfo.imageExtent.height, 1},
createInfo.imageExtent.width * createInfo.imageExtent.width * 4}); // TODO: 4 is format size
}
void SwapchainObject::CreateDepthStencilResources(VkDevice device, VkPhysicalDevice physicalDevice) {
DestroyDepthStencilResources(device);
const auto imageCount = static_cast<Uint32>(m_images.size());
if (imageCount == 0) {
return;
}
m_depthStencilFormat = FindSupportedDepthStencilFormat(physicalDevice);
MOBILEGL_ASSERT(m_depthStencilFormat != VK_FORMAT_UNDEFINED, "No supported depth/stencil format found.");
m_depthStencilImages.assign(imageCount, VK_NULL_HANDLE);
m_depthStencilImageMemories.assign(imageCount, VK_NULL_HANDLE);
m_depthStencilImageViews.assign(imageCount, VK_NULL_HANDLE);
m_depthStencilImageLayouts.assign(imageCount, VK_IMAGE_LAYOUT_UNDEFINED);
for (Uint32 i = 0; i < imageCount; ++i) {
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = m_extent.width;
imageInfo.extent.height = m_extent.height;
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = m_depthStencilFormat;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_VERIFY(vkCreateImage(device, &imageInfo, nullptr, &m_depthStencilImages[i]), "vkCreateImage(depth)");
VkMemoryRequirements memRequirements{};
vkGetImageMemoryRequirements(device, m_depthStencilImages[i], &memRequirements);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex =
FindMemoryType(physicalDevice, memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_VERIFY(vkAllocateMemory(device, &allocInfo, nullptr, &m_depthStencilImageMemories[i]),
"vkAllocateMemory(depth)");
VK_VERIFY(vkBindImageMemory(device, m_depthStencilImages[i], m_depthStencilImageMemories[i], 0),
"vkBindImageMemory(depth)");
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = m_depthStencilImages[i];
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = m_depthStencilFormat;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
if (HasStencilComponent(m_depthStencilFormat)) {
viewInfo.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VK_VERIFY(vkCreateImageView(device, &viewInfo, nullptr, &m_depthStencilImageViews[i]),
"vkCreateImageView(depth)");
}
}
void SwapchainObject::DestroyDepthStencilResources(VkDevice device) {
for (auto view : m_depthStencilImageViews) {
if (view != VK_NULL_HANDLE) {
vkDestroyImageView(device, view, nullptr);
}
}
m_depthStencilImageViews.clear();
for (auto image : m_depthStencilImages) {
if (image != VK_NULL_HANDLE) {
vkDestroyImage(device, image, nullptr);
}
}
m_depthStencilImages.clear();
for (auto memory : m_depthStencilImageMemories) {
if (memory != VK_NULL_HANDLE) {
vkFreeMemory(device, memory, nullptr);
}
}
m_depthStencilImageMemories.clear();
m_depthStencilImageLayouts.clear();
m_depthStencilFormat = VK_FORMAT_UNDEFINED;
} }
void SwapchainObject::Shutdown(VkDevice device) { void SwapchainObject::Shutdown(VkDevice device) {
DestroyDepthStencilResources(device);
for (auto imageView : m_imageViews) { for (auto imageView : m_imageViews) {
vkDestroyImageView(device, imageView, nullptr); vkDestroyImageView(device, imageView, nullptr);
} }
@@ -221,6 +384,29 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_imageLayouts[index] = layout; m_imageLayouts[index] = layout;
} }
VkImage SwapchainObject::GetDepthStencilImage(Uint32 index) const {
MOBILEGL_ASSERT(index < m_depthStencilImages.size(), "Swapchain depth/stencil image index out of range");
return m_depthStencilImages[index];
}
VkImageView SwapchainObject::GetDepthStencilImageView(Uint32 index) const {
MOBILEGL_ASSERT(index < m_depthStencilImageViews.size(),
"Swapchain depth/stencil image view index out of range");
return m_depthStencilImageViews[index];
}
VkImageLayout SwapchainObject::GetDepthStencilImageLayout(Uint32 index) const {
MOBILEGL_ASSERT(index < m_depthStencilImageLayouts.size(),
"Swapchain depth/stencil image layout index out of range");
return m_depthStencilImageLayouts[index];
}
void SwapchainObject::SetDepthStencilImageLayout(Uint32 index, VkImageLayout layout) {
MOBILEGL_ASSERT(index < m_depthStencilImageLayouts.size(),
"Swapchain depth/stencil image layout index out of range");
m_depthStencilImageLayouts[index] = layout;
}
void SwapchainObject::CreateImageViews(VkDevice device) { void SwapchainObject::CreateImageViews(VkDevice device) {
m_imageViews.resize(m_images.size(), VK_NULL_HANDLE); m_imageViews.resize(m_images.size(), VK_NULL_HANDLE);
for (SizeT i = 0; i < m_imageViews.size(); i++) { for (SizeT i = 0; i < m_imageViews.size(); i++) {
@@ -38,6 +38,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkSurfaceTransformFlagBitsKHR GetPreTransform() const { return m_preTransform; } VkSurfaceTransformFlagBitsKHR GetPreTransform() const { return m_preTransform; }
const Vector<VkImage>& GetImages() const { return m_images; } const Vector<VkImage>& GetImages() const { return m_images; }
const Vector<VkImageView>& GetImageViews() const { return m_imageViews; } const Vector<VkImageView>& GetImageViews() const { return m_imageViews; }
VkFormat GetDepthStencilFormat() const { return m_depthStencilFormat; }
const Vector<VkImageView>& GetDepthStencilImageViews() const { return m_depthStencilImageViews; }
VkImage GetDepthStencilImage(Uint32 index) const;
VkImageView GetDepthStencilImageView(Uint32 index) const;
VkImageLayout GetDepthStencilImageLayout(Uint32 index) const;
void SetDepthStencilImageLayout(Uint32 index, VkImageLayout layout);
VkImage GetImage(Uint32 index) const; VkImage GetImage(Uint32 index) const;
VkImageLayout GetImageLayout(Uint32 index) const; VkImageLayout GetImageLayout(Uint32 index) const;
void SetImageLayout(Uint32 index, VkImageLayout layout); void SetImageLayout(Uint32 index, VkImageLayout layout);
@@ -45,6 +51,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
private: private:
void CreateImageViews(VkDevice device); void CreateImageViews(VkDevice device);
void CreateDepthStencilResources(VkDevice device, VkPhysicalDevice physicalDevice);
void DestroyDepthStencilResources(VkDevice device);
static constexpr VkPresentModeKHR s_desiredPresentModes[] { static constexpr VkPresentModeKHR s_desiredPresentModes[] {
VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_MAILBOX_KHR,
VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR,
@@ -59,5 +67,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Vector<VkImage> m_images; Vector<VkImage> m_images;
Vector<VkImageView> m_imageViews; Vector<VkImageView> m_imageViews;
Vector<VkImageLayout> m_imageLayouts; Vector<VkImageLayout> m_imageLayouts;
VkFormat m_depthStencilFormat = VK_FORMAT_UNDEFINED;
Vector<VkImage> m_depthStencilImages;
Vector<VkDeviceMemory> m_depthStencilImageMemories;
Vector<VkImageView> m_depthStencilImageViews;
Vector<VkImageLayout> m_depthStencilImageLayouts;
}; };
} // namespace MobileGL::MG_Backend::DirectVulkan } // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,875 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.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 "UniformDescriptorBinder.h"
#include "VkFramebufferManager.h"
#include "MG_State/GLState/Core.h"
#include "MG_State/GLState/ProgramState/ProgramObject.h"
#include "MG_Util/ShaderTranspiler/Types.h"
#include <limits>
namespace MobileGL::MG_Backend::DirectVulkan {
VkDeviceSize UniformDescriptorBinder::AlignUp(VkDeviceSize value, VkDeviceSize alignment) {
if (alignment == 0) {
return value;
}
return (value + alignment - 1) / alignment * alignment;
}
Uint64 UniformDescriptorBinder::ComputeProgramHash(const MG_State::GLState::ProgramObject& program) {
XXH64_state_t* state = XXH64_createState();
XXHASH_VERIFY(XXH64_reset(state, 0xC0D3A11ULL));
const auto& spirv = program.GetGeneratedSpirv();
for (const auto& module : spirv) {
XXHASH_VERIFY(XXH64_update(state, module.data(), module.size() * sizeof(Uint)));
}
const Uint32 blockCount = static_cast<Uint32>(program.GetActiveUniformBlocksCount());
XXHASH_VERIFY(XXH64_update(state, &blockCount, sizeof(blockCount)));
for (Uint32 i = 0; i < blockCount; ++i) {
const Uint32 binding = program.GetUniformBlockBinding(i);
XXHASH_VERIFY(XXH64_update(state, &binding, sizeof(binding)));
}
const Uint64 hash = XXH64_digest(state);
XXH64_freeState(state);
return hash;
}
Bool UniformDescriptorBinder::IsSamplerUniformType(GLenum glType) {
switch (glType) {
case GL_SAMPLER_1D:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
case GL_SAMPLER_CUBE:
case GL_SAMPLER_1D_SHADOW:
case GL_SAMPLER_2D_SHADOW:
case GL_SAMPLER_1D_ARRAY:
case GL_SAMPLER_2D_ARRAY:
case GL_SAMPLER_1D_ARRAY_SHADOW:
case GL_SAMPLER_2D_ARRAY_SHADOW:
case GL_SAMPLER_2D_MULTISAMPLE:
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_SAMPLER_CUBE_SHADOW:
case GL_SAMPLER_BUFFER:
case GL_SAMPLER_2D_RECT:
case GL_SAMPLER_2D_RECT_SHADOW:
case GL_INT_SAMPLER_1D:
case GL_INT_SAMPLER_2D:
case GL_INT_SAMPLER_3D:
case GL_INT_SAMPLER_CUBE:
case GL_INT_SAMPLER_1D_ARRAY:
case GL_INT_SAMPLER_2D_ARRAY:
case GL_INT_SAMPLER_2D_MULTISAMPLE:
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_INT_SAMPLER_BUFFER:
case GL_INT_SAMPLER_2D_RECT:
case GL_UNSIGNED_INT_SAMPLER_1D:
case GL_UNSIGNED_INT_SAMPLER_2D:
case GL_UNSIGNED_INT_SAMPLER_3D:
case GL_UNSIGNED_INT_SAMPLER_CUBE:
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
return true;
default:
return false;
}
}
TextureTarget UniformDescriptorBinder::UniformTypeToTextureTarget(GLenum glType) {
switch (glType) {
case GL_SAMPLER_1D:
case GL_INT_SAMPLER_1D:
case GL_UNSIGNED_INT_SAMPLER_1D:
return TextureTarget::Texture1D;
case GL_SAMPLER_3D:
case GL_INT_SAMPLER_3D:
case GL_UNSIGNED_INT_SAMPLER_3D:
return TextureTarget::Texture3D;
case GL_SAMPLER_CUBE:
case GL_SAMPLER_CUBE_SHADOW:
case GL_INT_SAMPLER_CUBE:
case GL_UNSIGNED_INT_SAMPLER_CUBE:
return TextureTarget::TextureCubeMap;
case GL_SAMPLER_2D_MULTISAMPLE:
case GL_INT_SAMPLER_2D_MULTISAMPLE:
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
return TextureTarget::Texture2DMultisample;
case GL_SAMPLER_BUFFER:
case GL_INT_SAMPLER_BUFFER:
case GL_UNSIGNED_INT_SAMPLER_BUFFER:
return TextureTarget::TextureBuffer;
case GL_SAMPLER_1D_ARRAY:
case GL_SAMPLER_1D_ARRAY_SHADOW:
case GL_INT_SAMPLER_1D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
return TextureTarget::Texture1DArray;
case GL_SAMPLER_2D_ARRAY:
case GL_SAMPLER_2D_ARRAY_SHADOW:
case GL_INT_SAMPLER_2D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
return TextureTarget::Texture2DArray;
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
return TextureTarget::Texture2DMultisampleArray;
case GL_SAMPLER_2D_RECT:
case GL_SAMPLER_2D_RECT_SHADOW:
case GL_INT_SAMPLER_2D_RECT:
case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
return TextureTarget::TextureRectangle;
case GL_SAMPLER_2D:
case GL_SAMPLER_2D_SHADOW:
case GL_INT_SAMPLER_2D:
case GL_UNSIGNED_INT_SAMPLER_2D:
default:
return TextureTarget::Texture2D;
}
}
Bool UniformDescriptorBinder::Initialize(VkDevice device, VmaAllocator allocator,
VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
Uint32 maxBindings, Uint32 setsPerFrame, VkDeviceSize perFrameUploadBytes,
VkTextureSamplerManager* textureSamplerManager,
VkFramebufferManager* framebufferManager) {
Shutdown();
MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "UniformDescriptorBinder::Initialize requires valid VkDevice");
MOBILEGL_ASSERT(allocator != nullptr, "UniformDescriptorBinder::Initialize requires valid VMA allocator");
MOBILEGL_ASSERT(frameCount > 0, "UniformDescriptorBinder::Initialize requires frameCount > 0");
MOBILEGL_ASSERT(maxBindings > 0, "UniformDescriptorBinder::Initialize requires maxBindings > 0");
MOBILEGL_ASSERT(setsPerFrame > 0, "UniformDescriptorBinder::Initialize requires setsPerFrame > 0");
m_device = device;
m_allocator = allocator;
m_minDynamicOffsetAlignment = std::max<VkDeviceSize>(1, minUniformBufferOffsetAlignment);
m_perFrameUploadBytes = perFrameUploadBytes;
m_frameCount = frameCount;
m_maxBindings = maxBindings;
m_setsPerFrame = setsPerFrame;
m_peakDescriptorSetsObserved = 0;
m_textureSamplerManager = textureSamplerManager;
m_framebufferManager = framebufferManager;
m_frames.resize(m_frameCount);
for (Uint32 frameIndex = 0; frameIndex < m_frameCount; ++frameIndex) {
auto& frame = m_frames[frameIndex];
frame.writeCursor = 0;
frame.activeDescriptorPoolIndex = 0;
frame.allocatedSetsThisFrame = 0;
frame.peakAllocatedSetsThisFrame = 0;
frame.descriptorPools.clear();
VkDescriptorPool initialPool = VK_NULL_HANDLE;
if (!CreateDescriptorPool(m_setsPerFrame, initialPool)) {
MGLOG_E("UniformDescriptorBinder::Initialize failed: cannot create frame descriptor pool %u",
frameIndex);
Shutdown();
return false;
}
frame.descriptorPools.push_back({initialPool, m_setsPerFrame, 0});
MGLOG_D("UniformDescriptorBinder: frame %u descriptor pool created (maxSets=%u)", frameIndex, m_setsPerFrame);
const Bool created = frame.uploadBuffer.Create(
m_allocator, m_perFrameUploadBytes, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO,
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT);
if (!created) {
MGLOG_E("UniformDescriptorBinder::Initialize failed: cannot create frame upload buffer %u", frameIndex);
Shutdown();
return false;
}
}
return true;
}
void UniformDescriptorBinder::Shutdown() {
for (auto& frame : m_frames) {
frame.uploadBuffer.Destroy();
if (m_device != VK_NULL_HANDLE) {
for (auto& bucket : frame.descriptorPools) {
if (bucket.handle != VK_NULL_HANDLE) {
vkDestroyDescriptorPool(m_device, bucket.handle, nullptr);
bucket.handle = VK_NULL_HANDLE;
}
}
}
frame.descriptorPools.clear();
frame.activeDescriptorPoolIndex = 0;
frame.allocatedSetsThisFrame = 0;
frame.peakAllocatedSetsThisFrame = 0;
frame.writeCursor = 0;
}
m_frames.clear();
DestroyProgramLayouts();
m_allocator = nullptr;
m_device = VK_NULL_HANDLE;
m_minDynamicOffsetAlignment = 1;
m_perFrameUploadBytes = 0;
m_frameCount = 0;
m_maxBindings = 0;
m_setsPerFrame = 0;
m_peakDescriptorSetsObserved = 0;
m_textureSamplerManager = nullptr;
m_framebufferManager = nullptr;
}
void UniformDescriptorBinder::BeginFrame(Uint32 frameIndex) {
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "UniformDescriptorBinder::BeginFrame invalid frame index");
auto& frame = m_frames[frameIndex];
if (frame.peakAllocatedSetsThisFrame > m_peakDescriptorSetsObserved) {
m_peakDescriptorSetsObserved = frame.peakAllocatedSetsThisFrame;
MGLOG_D(
"UniformDescriptorBinder: new descriptor set peak observed=%u (base setsPerFrame=%u, frame=%u, pools=%zu)",
m_peakDescriptorSetsObserved, m_setsPerFrame, frameIndex, frame.descriptorPools.size());
}
frame.writeCursor = 0;
frame.activeDescriptorPoolIndex = 0;
frame.allocatedSetsThisFrame = 0;
frame.peakAllocatedSetsThisFrame = 0;
for (auto& bucket : frame.descriptorPools) {
bucket.allocatedSets = 0;
if (bucket.handle == VK_NULL_HANDLE) {
continue;
}
VK_VERIFY(vkResetDescriptorPool(m_device, bucket.handle, 0),
"UniformDescriptorBinder::BeginFrame, vkResetDescriptorPool");
}
}
Bool UniformDescriptorBinder::ReflectBindingKinds(const MG_State::GLState::ProgramObject& program,
Vector<BindingKind>& outKinds) const {
outKinds.assign(m_maxBindings, BindingKind::None);
const auto& spirv = program.GetGeneratedSpirv();
for (const auto& module : spirv) {
if (module.empty()) {
continue;
}
spvc_context context = nullptr;
spvc_parsed_ir ir = nullptr;
spvc_compiler compiler = nullptr;
spvc_resources resources = nullptr;
if (spvc_context_create(&context) != SPVC_SUCCESS) {
return false;
}
const spvc_result parseResult = spvc_context_parse_spirv(context, module.data(), module.size(), &ir);
if (parseResult != SPVC_SUCCESS) {
spvc_context_destroy(context);
continue;
}
const spvc_result compilerResult =
spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler);
if (compilerResult != SPVC_SUCCESS) {
spvc_context_destroy(context);
continue;
}
if (spvc_compiler_create_shader_resources(compiler, &resources) != SPVC_SUCCESS) {
spvc_context_destroy(context);
continue;
}
const auto applyBindings = [&](spvc_resource_type resourceType, BindingKind kind) {
const spvc_reflected_resource* list = nullptr;
size_t count = 0;
if (spvc_resources_get_resource_list_for_type(resources, resourceType, &list, &count) != SPVC_SUCCESS) {
return;
}
for (size_t i = 0; i < count; ++i) {
const Uint32 binding =
spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationBinding);
if (binding >= m_maxBindings) {
continue;
}
if (kind == BindingKind::CombinedImageSampler) {
outKinds[binding] = BindingKind::CombinedImageSampler;
} else if (outKinds[binding] == BindingKind::None) {
outKinds[binding] = kind;
}
}
};
applyBindings(SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, BindingKind::UniformBufferDynamic);
applyBindings(SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, BindingKind::CombinedImageSampler);
spvc_context_destroy(context);
}
return true;
}
Bool UniformDescriptorBinder::ReflectSamplerBindings(const MG_State::GLState::ProgramObject& program,
ProgramLayout& layout) const {
layout.samplerUniformLocationByBinding.assign(m_maxBindings, -1);
layout.samplerTextureTargetByBinding.assign(m_maxBindings, TextureTarget::Texture2D);
const auto& spirv = program.GetGeneratedSpirv();
for (const auto& module : spirv) {
if (module.empty()) {
continue;
}
spvc_context context = nullptr;
spvc_parsed_ir ir = nullptr;
spvc_compiler compiler = nullptr;
spvc_resources resources = nullptr;
if (spvc_context_create(&context) != SPVC_SUCCESS) {
return false;
}
if (spvc_context_parse_spirv(context, module.data(), module.size(), &ir) != SPVC_SUCCESS) {
spvc_context_destroy(context);
continue;
}
if (spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP,
&compiler) != SPVC_SUCCESS) {
spvc_context_destroy(context);
continue;
}
if (spvc_compiler_create_shader_resources(compiler, &resources) != SPVC_SUCCESS) {
spvc_context_destroy(context);
continue;
}
const spvc_reflected_resource* list = nullptr;
size_t count = 0;
if (spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, &list, &count) ==
SPVC_SUCCESS) {
for (size_t i = 0; i < count; ++i) {
const Uint32 binding =
spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationBinding);
if (binding >= m_maxBindings) {
continue;
}
String uniformName = list[i].name ? list[i].name : "";
Int location = program.GetUniformLocation(uniformName);
if (location < 0) {
const auto arraySuffix = uniformName.find("[0]");
if (arraySuffix != String::npos) {
uniformName = uniformName.substr(0, arraySuffix);
location = program.GetUniformLocation(uniformName);
}
}
if (location < 0) {
continue;
}
layout.samplerUniformLocationByBinding[binding] = location;
layout.samplerTextureTargetByBinding[binding] =
UniformTypeToTextureTarget(program.GetUniformType(static_cast<Uint>(location)));
}
}
spvc_context_destroy(context);
}
return true;
}
Bool UniformDescriptorBinder::ReflectGlobalUboBinding(const MG_State::GLState::ProgramObject& program,
ProgramLayout& layout) const {
layout.globalUboBinding = -1;
const auto& spirv = program.GetGeneratedSpirv();
for (const auto& module : spirv) {
if (module.empty()) {
continue;
}
spvc_context context = nullptr;
spvc_parsed_ir ir = nullptr;
spvc_compiler compiler = nullptr;
spvc_resources resources = nullptr;
if (spvc_context_create(&context) != SPVC_SUCCESS) {
return false;
}
if (spvc_context_parse_spirv(context, module.data(), module.size(), &ir) != SPVC_SUCCESS) {
spvc_context_destroy(context);
continue;
}
if (spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP,
&compiler) != SPVC_SUCCESS) {
spvc_context_destroy(context);
continue;
}
if (spvc_compiler_create_shader_resources(compiler, &resources) != SPVC_SUCCESS) {
spvc_context_destroy(context);
continue;
}
const spvc_reflected_resource* list = nullptr;
size_t count = 0;
if (spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, &list, &count) ==
SPVC_SUCCESS) {
for (size_t i = 0; i < count; ++i) {
const char* name = list[i].name ? list[i].name : "";
if (std::strstr(name, MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME) == nullptr) {
continue;
}
const Uint32 binding =
spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationBinding);
if (binding < m_maxBindings) {
layout.globalUboBinding = static_cast<Int>(binding);
}
break;
}
}
spvc_context_destroy(context);
if (layout.globalUboBinding >= 0) {
break;
}
}
return true;
}
Bool UniformDescriptorBinder::ResolveSamplerDescriptor(VkCommandBuffer commandBuffer,
const MG_State::GLState::ProgramObject& program,
const ProgramLayout& layout, Uint32 binding,
VkDescriptorImageInfo& outImageInfo) const {
if (!m_textureSamplerManager || !MG_State::pGLContext || binding >= layout.samplerUniformLocationByBinding.size()) {
return false;
}
const Int location = layout.samplerUniformLocationByBinding[binding];
if (location < 0) {
return false;
}
const Int unit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
if (unit < 0) {
return false;
}
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);
const auto samplerOverride = textureUnit.GetSamplerObject();
const TextureTarget preferredTarget = layout.samplerTextureTargetByBinding[binding];
auto texture = textureUnit.GetBindingSlot(preferredTarget).GetBoundObject();
if (!texture) {
auto& slots = textureUnit.GetAllBindingSlots();
for (auto& slot : slots) {
texture = slot.GetBoundObject();
if (texture) {
break;
}
}
}
if (!texture) {
return false;
}
if (m_framebufferManager &&
m_framebufferManager->TransitionOffscreenColorTextureToShaderRead(commandBuffer, texture->GetExternalIndex())) {
VkImageView offscreenView = VK_NULL_HANDLE;
if (m_framebufferManager->GetOffscreenColorViewByTexture(texture->GetExternalIndex(), offscreenView) &&
offscreenView != VK_NULL_HANDLE) {
VkDescriptorImageInfo sampledInfo{};
if (!m_textureSamplerManager->SyncTextureAndGetDescriptor(*texture, samplerOverride.get(), sampledInfo)) {
return false;
}
sampledInfo.imageView = offscreenView;
sampledInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
outImageInfo = sampledInfo;
return true;
}
}
return m_textureSamplerManager->SyncTextureAndGetDescriptor(*texture, samplerOverride.get(), outImageInfo);
}
UniformDescriptorBinder::ProgramLayout* UniformDescriptorBinder::GetOrCreateProgramLayout(
const MG_State::GLState::ProgramObject& program) {
const Uint64 hash = ComputeProgramHash(program);
auto it = m_programLayouts.find(hash);
if (it != m_programLayouts.end()) {
return &it->second;
}
ProgramLayout layout{};
layout.hash = hash;
if (!ReflectBindingKinds(program, layout.bindingKinds)) {
MGLOG_E("UniformDescriptorBinder::GetOrCreateProgramLayout failed: reflection failed");
return nullptr;
}
if (!ReflectSamplerBindings(program, layout)) {
MGLOG_E("UniformDescriptorBinder::GetOrCreateProgramLayout failed: sampler reflection failed");
return nullptr;
}
if (!ReflectGlobalUboBinding(program, layout)) {
MGLOG_E("UniformDescriptorBinder::GetOrCreateProgramLayout failed: global UBO reflection failed");
return nullptr;
}
Vector<VkDescriptorSetLayoutBinding> bindings;
bindings.reserve(m_maxBindings);
for (Uint32 binding = 0; binding < m_maxBindings; ++binding) {
const auto kind = layout.bindingKinds[binding];
if (kind == BindingKind::None) {
continue;
}
VkDescriptorSetLayoutBinding layoutBinding{};
layoutBinding.binding = binding;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
layoutBinding.pImmutableSamplers = nullptr;
if (kind == BindingKind::UniformBufferDynamic) {
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
layout.dynamicBindings.push_back(binding);
} else {
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
}
bindings.push_back(layoutBinding);
}
VkDescriptorSetLayoutCreateInfo setLayoutInfo{};
setLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
setLayoutInfo.bindingCount = static_cast<Uint32>(bindings.size());
setLayoutInfo.pBindings = bindings.data();
VK_VERIFY(vkCreateDescriptorSetLayout(m_device, &setLayoutInfo, nullptr, &layout.descriptorSetLayout),
"UniformDescriptorBinder::GetOrCreateProgramLayout, vkCreateDescriptorSetLayout");
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &layout.descriptorSetLayout;
VK_VERIFY(vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &layout.pipelineLayout),
"UniformDescriptorBinder::GetOrCreateProgramLayout, vkCreatePipelineLayout");
auto [insertIt, _] = m_programLayouts.emplace(hash, std::move(layout));
return &insertIt->second;
}
VkPipelineLayout UniformDescriptorBinder::GetOrCreatePipelineLayout(const MG_State::GLState::ProgramObject& program) {
auto* layout = GetOrCreateProgramLayout(program);
return layout ? layout->pipelineLayout : VK_NULL_HANDLE;
}
Bool UniformDescriptorBinder::AllocateUploadRegion(FrameResources& frame, VkDeviceSize size, VkDeviceSize& outOffset) {
const VkDeviceSize alignedOffset = AlignUp(frame.writeCursor, m_minDynamicOffsetAlignment);
if (alignedOffset + size > m_perFrameUploadBytes) {
return false;
}
outOffset = alignedOffset;
frame.writeCursor = alignedOffset + size;
return true;
}
Bool UniformDescriptorBinder::GatherBindingPayloads(const MG_State::GLState::ProgramObject& program,
Vector<const void*>& outData,
Vector<VkDeviceSize>& outSizes) const {
outData.assign(m_maxBindings, nullptr);
outSizes.assign(m_maxBindings, 0);
if (MG_State::pGLContext == nullptr) {
return false;
}
const Uint32 activeUniformBlockCount = static_cast<Uint32>(program.GetActiveUniformBlocksCount());
const Uint32 uniformBindingPointCount =
static_cast<Uint32>(MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::Uniform));
for (Uint32 blockIndex = 0; blockIndex < activeUniformBlockCount; ++blockIndex) {
const Uint32 binding = program.GetUniformBlockBinding(blockIndex);
if (binding >= m_maxBindings) {
continue;
}
VkDeviceSize blockSize = static_cast<VkDeviceSize>(program.GetUBOSizeAt(blockIndex));
if (blockSize == 0) {
continue;
}
if (binding >= uniformBindingPointCount) {
continue;
}
auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, binding);
const auto bufferObject = bindingPoint.GetBoundObject();
if (!bufferObject) {
continue;
}
const auto bufferData = bufferObject->GetDataReadOnly();
if (!bufferData || bufferData->empty()) {
continue;
}
const auto range = bindingPoint.GetRange();
const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(bufferObject->GetSize());
VkDeviceSize rangeStart = static_cast<VkDeviceSize>(range.start);
VkDeviceSize rangeEnd = static_cast<VkDeviceSize>(range.end);
if (rangeStart >= bufferSize) {
continue;
}
if (rangeEnd <= rangeStart || rangeEnd > bufferSize) {
rangeEnd = bufferSize;
}
VkDeviceSize available = rangeEnd - rangeStart;
if (available == 0) {
continue;
}
outData[binding] = bufferData->data() + static_cast<SizeT>(rangeStart);
outSizes[binding] = std::min(blockSize, available);
}
return true;
}
Bool UniformDescriptorBinder::CreateDescriptorPool(Uint32 maxSets, VkDescriptorPool& outPool) const {
outPool = VK_NULL_HANDLE;
if (m_device == VK_NULL_HANDLE || maxSets == 0 || m_maxBindings == 0) {
return false;
}
const Uint64 descriptorCount64 = static_cast<Uint64>(maxSets) * static_cast<Uint64>(m_maxBindings);
if (descriptorCount64 > static_cast<Uint64>(std::numeric_limits<Uint32>::max())) {
MGLOG_E("UniformDescriptorBinder::CreateDescriptorPool failed: descriptorCount overflow");
return false;
}
const Uint32 descriptorCount = static_cast<Uint32>(descriptorCount64);
VkDescriptorPoolSize poolSizes[2]{};
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
poolSizes[0].descriptorCount = descriptorCount;
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[1].descriptorCount = descriptorCount;
VkDescriptorPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.maxSets = maxSets;
poolInfo.poolSizeCount = static_cast<Uint32>(std::size(poolSizes));
poolInfo.pPoolSizes = poolSizes;
const VkResult result = vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &outPool);
if (result != VK_SUCCESS) {
MGLOG_E("UniformDescriptorBinder::CreateDescriptorPool failed: vkCreateDescriptorPool returned %d", result);
return false;
}
return true;
}
Bool UniformDescriptorBinder::GrowFrameDescriptorPool(FrameResources& frame, Uint32 frameIndex) {
if (frame.descriptorPools.empty()) {
return false;
}
const auto& currentBucket = frame.descriptorPools[frame.activeDescriptorPoolIndex];
const Uint32 currentMaxSets = std::max<Uint32>(1, currentBucket.maxSets);
const Uint32 grownMaxSets = currentMaxSets <= (std::numeric_limits<Uint32>::max() / 2) ? (currentMaxSets * 2)
: currentMaxSets;
VkDescriptorPool grownPool = VK_NULL_HANDLE;
if (!CreateDescriptorPool(grownMaxSets, grownPool)) {
MGLOG_E("UniformDescriptorBinder::GrowFrameDescriptorPool failed: cannot create grown pool (%u -> %u sets)",
currentMaxSets, grownMaxSets);
return false;
}
frame.descriptorPools.push_back({grownPool, grownMaxSets, 0});
frame.activeDescriptorPoolIndex = static_cast<Uint32>(frame.descriptorPools.size() - 1);
MGLOG_D(
"UniformDescriptorBinder: frame %u descriptor pool exhausted, grew pool (%u -> %u sets), poolCount=%zu",
frameIndex, currentMaxSets, grownMaxSets, frame.descriptorPools.size());
return true;
}
Bool UniformDescriptorBinder::BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout,
const MG_State::GLState::ProgramObject& program,
Uint32 frameIndex) {
if (m_frames.empty()) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: binder is not initialized");
return false;
}
if (frameIndex >= m_frames.size()) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: invalid frame index %u", frameIndex);
return false;
}
ProgramLayout* layout = GetOrCreateProgramLayout(program);
if (!layout || layout->pipelineLayout == VK_NULL_HANDLE || layout->descriptorSetLayout == VK_NULL_HANDLE) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: cannot get program layout");
return false;
}
if (layout->pipelineLayout != pipelineLayout) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: pipelineLayout mismatch");
return false;
}
auto& frame = m_frames[frameIndex];
if (frame.descriptorPools.empty()) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame descriptor pools are invalid");
return false;
}
if (frame.activeDescriptorPoolIndex >= frame.descriptorPools.size()) {
frame.activeDescriptorPoolIndex = 0;
}
VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &layout->descriptorSetLayout;
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
auto allocateFromActivePool = [&](VkResult& outResult) {
auto& bucket = frame.descriptorPools[frame.activeDescriptorPoolIndex];
allocInfo.descriptorPool = bucket.handle;
outResult = vkAllocateDescriptorSets(m_device, &allocInfo, &descriptorSet);
if (outResult == VK_SUCCESS) {
++bucket.allocatedSets;
++frame.allocatedSetsThisFrame;
frame.peakAllocatedSetsThisFrame = std::max(frame.peakAllocatedSetsThisFrame, frame.allocatedSetsThisFrame);
}
};
VkResult allocResult = VK_SUCCESS;
allocateFromActivePool(allocResult);
if (allocResult == VK_ERROR_OUT_OF_POOL_MEMORY || allocResult == VK_ERROR_FRAGMENTED_POOL) {
if (!GrowFrameDescriptorPool(frame, frameIndex)) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: descriptor pool growth failed");
return false;
}
allocateFromActivePool(allocResult);
}
if (allocResult != VK_SUCCESS || descriptorSet == VK_NULL_HANDLE) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: vkAllocateDescriptorSets returned %d",
allocResult);
return false;
}
Vector<const void*> bindingData;
Vector<VkDeviceSize> bindingSizes;
if (!GatherBindingPayloads(program, bindingData, bindingSizes)) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: cannot gather UBO payloads");
return false;
}
static const Uint8 kFallbackData[16] = {};
VkDescriptorImageInfo fallbackImageInfo{};
const Bool hasFallbackImage = m_textureSamplerManager && m_textureSamplerManager->GetFallbackDescriptor(fallbackImageInfo);
Vector<VkWriteDescriptorSet> writes;
writes.reserve(m_maxBindings);
Vector<VkDescriptorBufferInfo> bufferInfos;
Vector<VkDescriptorImageInfo> imageInfos;
Vector<Uint32> dynamicOffsets;
bufferInfos.reserve(m_maxBindings);
imageInfos.reserve(m_maxBindings);
dynamicOffsets.reserve(layout->dynamicBindings.size());
for (Uint32 binding = 0; binding < m_maxBindings; ++binding) {
const auto kind = layout->bindingKinds[binding];
if (kind == BindingKind::None) {
continue;
}
VkWriteDescriptorSet write{};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = descriptorSet;
write.dstBinding = binding;
write.dstArrayElement = 0;
write.descriptorCount = 1;
if (kind == BindingKind::UniformBufferDynamic) {
const void* payload = bindingData[binding];
VkDeviceSize payloadSize = bindingSizes[binding];
if (payload == nullptr || payloadSize == 0) {
if (layout->globalUboBinding == static_cast<Int>(binding)) {
const void* globalUboData = program.GetUBOData();
const VkDeviceSize globalUboSize = static_cast<VkDeviceSize>(program.GetUBOSize());
if (globalUboData != nullptr && globalUboSize > 0) {
payload = globalUboData;
payloadSize = globalUboSize;
}
}
if (payload == nullptr || payloadSize == 0) {
payload = kFallbackData;
payloadSize = sizeof(kFallbackData);
}
}
VkDeviceSize payloadOffset = 0;
if (!AllocateUploadRegion(frame, payloadSize, payloadOffset)) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame upload buffer exhausted");
return false;
}
if (!frame.uploadBuffer.Upload(payload, payloadSize, payloadOffset)) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: UBO upload failed on binding %u",
binding);
return false;
}
VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = frame.uploadBuffer.GetHandle();
bufferInfo.offset = 0;
bufferInfo.range = payloadSize;
bufferInfos.push_back(bufferInfo);
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
write.pBufferInfo = &bufferInfos.back();
writes.push_back(write);
dynamicOffsets.push_back(static_cast<Uint32>(payloadOffset));
} else {
VkDescriptorImageInfo imageInfo{};
Bool hasImage = ResolveSamplerDescriptor(commandBuffer, program, *layout, binding, imageInfo);
if (!hasImage) {
if (!hasFallbackImage) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: fallback sampler/texture is unavailable");
return false;
}
imageInfo = fallbackImageInfo;
}
if (imageInfo.sampler == VK_NULL_HANDLE || imageInfo.imageView == VK_NULL_HANDLE) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: fallback sampler/texture is unavailable");
return false;
}
imageInfos.push_back(imageInfo);
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write.pImageInfo = &imageInfos.back();
writes.push_back(write);
}
}
if (!writes.empty()) {
vkUpdateDescriptorSets(m_device, static_cast<Uint32>(writes.size()), writes.data(), 0, nullptr);
}
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet,
static_cast<Uint32>(dynamicOffsets.size()), dynamicOffsets.data());
return true;
}
void UniformDescriptorBinder::DestroyProgramLayouts() {
for (auto& [_, layout] : m_programLayouts) {
if (layout.pipelineLayout != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(m_device, layout.pipelineLayout, nullptr);
layout.pipelineLayout = VK_NULL_HANDLE;
}
if (layout.descriptorSetLayout != VK_NULL_HANDLE) {
vkDestroyDescriptorSetLayout(m_device, layout.descriptorSetLayout, nullptr);
layout.descriptorSetLayout = VK_NULL_HANDLE;
}
}
m_programLayouts.clear();
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,104 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h
// 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
#pragma once
#include "VkBufferObject.h"
#include "VkTextureSamplerManager.h"
#include "../VkIncludes.h"
#include <Includes.h>
#include <vk_mem_alloc.h>
namespace MobileGL::MG_State::GLState {
class ProgramObject;
}
namespace MobileGL::MG_Backend::DirectVulkan {
class VkFramebufferManager;
class UniformDescriptorBinder {
public:
enum class BindingKind : Uint8 {
None = 0,
UniformBufferDynamic,
CombinedImageSampler
};
Bool Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment,
Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64,
VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024,
VkTextureSamplerManager* textureSamplerManager = nullptr,
VkFramebufferManager* framebufferManager = nullptr);
void Shutdown();
void BeginFrame(Uint32 frameIndex);
VkPipelineLayout GetOrCreatePipelineLayout(const MG_State::GLState::ProgramObject& program);
Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout,
const MG_State::GLState::ProgramObject& program, Uint32 frameIndex);
private:
struct DescriptorPoolBucket {
VkDescriptorPool handle = VK_NULL_HANDLE;
Uint32 maxSets = 0;
Uint32 allocatedSets = 0;
};
struct FrameResources {
VkBufferObject uploadBuffer;
Vector<DescriptorPoolBucket> descriptorPools;
Uint32 activeDescriptorPoolIndex = 0;
Uint32 allocatedSetsThisFrame = 0;
Uint32 peakAllocatedSetsThisFrame = 0;
VkDeviceSize writeCursor = 0;
};
struct ProgramLayout {
Uint64 hash = 0;
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
Vector<BindingKind> bindingKinds;
Vector<Uint32> dynamicBindings;
Vector<Int> samplerUniformLocationByBinding;
Vector<TextureTarget> samplerTextureTargetByBinding;
Int globalUboBinding = -1;
};
static VkDeviceSize AlignUp(VkDeviceSize value, VkDeviceSize alignment);
static Uint64 ComputeProgramHash(const MG_State::GLState::ProgramObject& program);
static Bool IsSamplerUniformType(GLenum glType);
static TextureTarget UniformTypeToTextureTarget(GLenum glType);
Bool ReflectSamplerBindings(const MG_State::GLState::ProgramObject& program, ProgramLayout& layout) const;
Bool ReflectGlobalUboBinding(const MG_State::GLState::ProgramObject& program, ProgramLayout& layout) const;
Bool ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program,
const ProgramLayout& layout, Uint32 binding,
VkDescriptorImageInfo& outImageInfo) const;
Bool ReflectBindingKinds(const MG_State::GLState::ProgramObject& program, Vector<BindingKind>& outKinds) const;
ProgramLayout* GetOrCreateProgramLayout(const MG_State::GLState::ProgramObject& program);
Bool AllocateUploadRegion(FrameResources& frame, VkDeviceSize size, VkDeviceSize& outOffset);
Bool GatherBindingPayloads(const MG_State::GLState::ProgramObject& program, Vector<const void*>& outData,
Vector<VkDeviceSize>& outSizes) const;
Bool CreateDescriptorPool(Uint32 maxSets, VkDescriptorPool& outPool) const;
Bool GrowFrameDescriptorPool(FrameResources& frame, Uint32 frameIndex);
void DestroyProgramLayouts();
VkDevice m_device = VK_NULL_HANDLE;
VmaAllocator m_allocator = nullptr;
Vector<FrameResources> m_frames;
UnorderedMap<Uint64, ProgramLayout> m_programLayouts;
VkDeviceSize m_minDynamicOffsetAlignment = 1;
VkDeviceSize m_perFrameUploadBytes = 0;
Uint32 m_frameCount = 0;
Uint32 m_maxBindings = 0;
Uint32 m_setsPerFrame = 0;
Uint32 m_peakDescriptorSetsObserved = 0;
VkTextureSamplerManager* m_textureSamplerManager = nullptr;
VkFramebufferManager* m_framebufferManager = nullptr;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,556 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.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 "UniformManager.h"
#include "MG_State/GLState/Core.h"
#include "MG_State/GLState/ProgramState/ProgramObject.h"
#include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h"
#include <limits>
namespace MobileGL::MG_Backend::DirectVulkan {
static Bool FindFramebufferAttachmentForTexture(const MG_State::GLState::FramebufferObject& framebuffer,
const MG_State::GLState::ITextureObject& texture,
FramebufferAttachmentType& outAttachment, Int& outLevel) {
const auto& attachments = framebuffer.GetAllAttachmentObjects();
for (SizeT i = 0; i < attachments.size(); ++i) {
const auto attachmentType = static_cast<FramebufferAttachmentType>(i);
if (attachmentType == FramebufferAttachmentType::None) {
continue;
}
const auto& attachment = attachments[i];
if (!attachment.IsTexture()) {
continue;
}
auto attachedTexture = attachment.GetTexture();
if (attachedTexture && attachedTexture.get() == &texture) {
outAttachment = attachmentType;
outLevel = attachment.GetTextureLevel();
return true;
}
}
return false;
}
static Bool IsValidSampledImageLayout(VkImageLayout layout) {
switch (layout) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_GENERAL:
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:
return true;
default:
return false;
}
}
Bool UniformManager::Initialize(VkDevice device, VkBufferManager* bufferManager,
ProgramFactory* programFactory,
VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
Uint32 maxBindings, Uint32 setsPerFrame,
VkTextureManager* textureManager, VkSamplerManager* samplerManager) {
Shutdown();
MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "UniformDescriptorBinder::Initialize requires valid VkDevice");
MOBILEGL_ASSERT(bufferManager != nullptr, "UniformDescriptorBinder::Initialize requires valid buffer manager");
MOBILEGL_ASSERT(programFactory != nullptr,
"UniformDescriptorBinder::Initialize requires valid program factory");
MOBILEGL_ASSERT(frameCount > 0, "UniformDescriptorBinder::Initialize requires frameCount > 0");
MOBILEGL_ASSERT(maxBindings > 0, "UniformDescriptorBinder::Initialize requires maxBindings > 0");
MOBILEGL_ASSERT(setsPerFrame > 0, "UniformDescriptorBinder::Initialize requires setsPerFrame > 0");
MOBILEGL_ASSERT(textureManager != nullptr,
"UniformDescriptorBinder::Initialize requires valid texture manager");
MOBILEGL_ASSERT(samplerManager != nullptr,
"UniformDescriptorBinder::Initialize requires valid sampler manager");
m_device = device;
m_bufferManager = bufferManager;
m_programFactory = programFactory;
m_minDynamicOffsetAlignment = std::max<VkDeviceSize>(1, minUniformBufferOffsetAlignment);
m_frameCount = frameCount;
m_maxBindings = maxBindings;
m_setsPerFrame = setsPerFrame;
m_peakDescriptorSetsObserved = 0;
m_textureManager = textureManager;
m_samplerManager = samplerManager;
m_frames.resize(m_frameCount);
for (Uint32 frameIndex = 0; frameIndex < m_frameCount; ++frameIndex) {
auto& frame = m_frames[frameIndex];
frame.activeDescriptorPoolIndex = 0;
frame.allocatedSetsThisFrame = 0;
frame.peakAllocatedSetsThisFrame = 0;
frame.descriptorPools.clear();
VkDescriptorPool initialPool = VK_NULL_HANDLE;
if (!CreateDescriptorPool(m_setsPerFrame, initialPool)) {
MGLOG_E("UniformDescriptorBinder::Initialize failed: cannot create frame descriptor pool %u",
frameIndex);
Shutdown();
return false;
}
frame.descriptorPools.push_back({initialPool, m_setsPerFrame, 0});
MGLOG_D("UniformDescriptorBinder: frame %u descriptor pool created (maxSets=%u)", frameIndex,
m_setsPerFrame);
}
return true;
}
void UniformManager::Shutdown() {
for (auto& frame : m_frames) {
if (m_device != VK_NULL_HANDLE) {
for (auto& bucket : frame.descriptorPools) {
if (bucket.handle != VK_NULL_HANDLE) {
vkDestroyDescriptorPool(m_device, bucket.handle, nullptr);
bucket.handle = VK_NULL_HANDLE;
}
}
}
frame.descriptorPools.clear();
frame.activeDescriptorPoolIndex = 0;
frame.allocatedSetsThisFrame = 0;
frame.peakAllocatedSetsThisFrame = 0;
}
m_frames.clear();
m_bufferManager = nullptr;
m_programFactory = nullptr;
m_device = VK_NULL_HANDLE;
m_minDynamicOffsetAlignment = 1;
m_frameCount = 0;
m_maxBindings = 0;
m_setsPerFrame = 0;
m_peakDescriptorSetsObserved = 0;
m_textureManager = nullptr;
m_samplerManager = nullptr;
}
void UniformManager::BeginFrame(Uint32 frameIndex) {
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "UniformDescriptorBinder::BeginFrame invalid frame index");
auto& frame = m_frames[frameIndex];
if (frame.peakAllocatedSetsThisFrame > m_peakDescriptorSetsObserved) {
m_peakDescriptorSetsObserved = frame.peakAllocatedSetsThisFrame;
MGLOG_D(
"UniformDescriptorBinder: new descriptor set peak observed=%u (base setsPerFrame=%u, frame=%u, pools=%zu)",
m_peakDescriptorSetsObserved, m_setsPerFrame, frameIndex, frame.descriptorPools.size());
}
frame.activeDescriptorPoolIndex = 0;
frame.allocatedSetsThisFrame = 0;
frame.peakAllocatedSetsThisFrame = 0;
for (auto& bucket : frame.descriptorPools) {
bucket.allocatedSets = 0;
if (bucket.handle == VK_NULL_HANDLE) {
continue;
}
VK_VERIFY(vkResetDescriptorPool(m_device, bucket.handle, 0),
"UniformDescriptorBinder::BeginFrame, vkResetDescriptorPool");
}
}
Bool UniformManager::ResolveSamplerDescriptor(VkCommandBuffer commandBuffer,
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");
SharedPtr<MG_State::GLState::ITextureObject> texture;
if (!ResolveSamplerTexture(program, programObj, binding, texture)) {
return false;
}
const Int location = programObj.samplerUniformLocationByBinding[binding];
const Int unit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);
const auto samplerOverride = textureUnit.GetSamplerObject();
if (!texture) {
return false;
}
const MG_State::GLState::SamplerObject* samplerToUse =
samplerOverride ? samplerOverride.get() : texture->GetSamplerObject().get();
if (!samplerToUse) {
return false;
}
VkTextureManager::TextureResource* resource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
if (resource == nullptr) {
return false;
}
if (!IsValidSampledImageLayout(resource->layout)) {
auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
FramebufferAttachmentType attachmentType = FramebufferAttachmentType::None;
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<Int>(resource->layout));
}
MOBILEGL_ASSERT(false,
"ResolveSamplerDescriptor: invalid sampled image layout=%d for textureId=%d, binding=%u",
static_cast<Int>(resource->layout), texture->GetExternalIndex(), binding);
return false;
}
outImageInfo = {
.sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse),
.imageView = resource->fullView,
.imageLayout = resource->layout,
};
return outImageInfo.sampler != VK_NULL_HANDLE;
}
Bool UniformManager::ResolveSamplerDescriptorOverride(
const SamplerBindingOverride& samplerBindingOverride, VkDescriptorImageInfo& outImageInfo) const {
MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveSamplerDescriptorOverride: texture manager is null");
MOBILEGL_ASSERT(m_samplerManager != nullptr, "ResolveSamplerDescriptorOverride: sampler manager is null");
if (samplerBindingOverride.texture == nullptr || samplerBindingOverride.sampler == nullptr) {
return false;
}
auto* resource = m_textureManager->SyncTextureAndGetDescriptor(*samplerBindingOverride.texture);
if (resource == nullptr || !IsValidSampledImageLayout(resource->layout)) {
return false;
}
outImageInfo = {
.sampler = m_samplerManager->GetOrCreateSampler(*samplerBindingOverride.sampler),
.imageView = resource->fullView,
.imageLayout = resource->layout,
};
return outImageInfo.sampler != VK_NULL_HANDLE;
}
Bool UniformManager::ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
SharedPtr<MG_State::GLState::ITextureObject>& outTexture) const {
outTexture.reset();
if (!MG_State::pGLContext || binding >= programObj.samplerUniformLocationByBinding.size()) {
return false;
}
const Int location = programObj.samplerUniformLocationByBinding[binding];
if (location < 0) {
return false;
}
const Int unit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
if (unit < 0) {
return false;
}
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);
const TextureTarget preferredTarget = programObj.samplerTextureTargetByBinding[binding];
outTexture = textureUnit.GetBindingSlot(preferredTarget).GetBoundObject();
return outTexture != nullptr;
}
Bool UniformManager::CollectSampledTextures(const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj,
Vector<MG_State::GLState::ITextureObject*>& outTextures) {
outTextures.clear();
const Uint32 bindingCount =
std::min<Uint32>(m_maxBindings, static_cast<Uint32>(programObj.bindingKinds.size()));
for (Uint32 binding = 0; binding < bindingCount; ++binding) {
if (programObj.bindingKinds[binding] != ProgramFactory::DescriptorBindingKind::CombinedImageSampler) {
continue;
}
SharedPtr<MG_State::GLState::ITextureObject> texture;
if (!ResolveSamplerTexture(program, programObj, binding, texture) || !texture) {
continue;
}
auto found = std::find(outTextures.begin(), outTextures.end(), texture.get());
if (found == outTextures.end()) {
outTextures.push_back(texture.get());
}
}
return true;
}
Bool UniformManager::GatherBindingPayloads(const MG_State::GLState::ProgramObject& program,
Vector<const void*>& outData,
Vector<VkDeviceSize>& outSizes) const {
outData.assign(m_maxBindings, nullptr);
outSizes.assign(m_maxBindings, 0);
const Uint32 activeUniformBlockCount = static_cast<Uint32>(program.GetActiveUniformBlocksCount());
const Uint32 uniformBindingPointCount =
static_cast<Uint32>(MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::Uniform));
for (Uint32 blockIndex = 0; blockIndex < activeUniformBlockCount; ++blockIndex) {
const Uint32 binding = program.GetUniformBlockBinding(blockIndex);
if (binding >= m_maxBindings) {
continue;
}
VkDeviceSize blockSize = static_cast<VkDeviceSize>(program.GetUBOSizeAt(blockIndex));
if (blockSize == 0) {
continue;
}
if (binding >= uniformBindingPointCount) {
continue;
}
auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, binding);
const auto bufferObject = bindingPoint.GetBoundObject();
if (!bufferObject) {
continue;
}
const auto bufferData = bufferObject->GetDataReadOnly();
if (!bufferData || bufferData->empty()) {
continue;
}
const auto range = bindingPoint.GetRange();
const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(bufferObject->GetSize());
VkDeviceSize rangeStart = static_cast<VkDeviceSize>(range.start);
VkDeviceSize rangeEnd = static_cast<VkDeviceSize>(range.end);
if (rangeStart >= bufferSize) {
continue;
}
if (rangeEnd <= rangeStart || rangeEnd > bufferSize) {
rangeEnd = bufferSize;
}
VkDeviceSize available = rangeEnd - rangeStart;
if (available == 0) {
continue;
}
outData[binding] = bufferData->data() + static_cast<SizeT>(rangeStart);
outSizes[binding] = std::min(blockSize, available);
}
return true;
}
Bool UniformManager::CreateDescriptorPool(Uint32 maxSets, VkDescriptorPool& outPool) const {
outPool = VK_NULL_HANDLE;
if (m_device == VK_NULL_HANDLE || maxSets == 0 || m_maxBindings == 0) {
return false;
}
const Uint64 descriptorCount64 = static_cast<Uint64>(maxSets) * static_cast<Uint64>(m_maxBindings);
if (descriptorCount64 > static_cast<Uint64>(std::numeric_limits<Uint32>::max())) {
MGLOG_E("UniformDescriptorBinder::CreateDescriptorPool failed: descriptorCount overflow");
return false;
}
const Uint32 descriptorCount = static_cast<Uint32>(descriptorCount64);
VkDescriptorPoolSize poolSizes[2]{};
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
poolSizes[0].descriptorCount = descriptorCount;
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[1].descriptorCount = descriptorCount;
VkDescriptorPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.maxSets = maxSets;
poolInfo.poolSizeCount = static_cast<Uint32>(std::size(poolSizes));
poolInfo.pPoolSizes = poolSizes;
const VkResult result = vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &outPool);
if (result != VK_SUCCESS) {
MGLOG_E("UniformDescriptorBinder::CreateDescriptorPool failed: vkCreateDescriptorPool returned %d",
result);
return false;
}
return true;
}
Bool UniformManager::GrowFrameDescriptorPool(FrameResources& frame, Uint32 frameIndex) {
if (frame.descriptorPools.empty()) {
return false;
}
const auto& currentBucket = frame.descriptorPools[frame.activeDescriptorPoolIndex];
const Uint32 currentMaxSets = std::max<Uint32>(1, currentBucket.maxSets);
const Uint32 grownMaxSets = currentMaxSets <= (std::numeric_limits<Uint32>::max() / 2) ? (currentMaxSets * 2)
: currentMaxSets;
VkDescriptorPool grownPool = VK_NULL_HANDLE;
if (!CreateDescriptorPool(grownMaxSets, grownPool)) {
MGLOG_E("UniformDescriptorBinder::GrowFrameDescriptorPool failed: cannot create grown pool (%u -> %u sets)",
currentMaxSets, grownMaxSets);
return false;
}
frame.descriptorPools.push_back({grownPool, grownMaxSets, 0});
frame.activeDescriptorPoolIndex = static_cast<Uint32>(frame.descriptorPools.size() - 1);
MGLOG_D(
"UniformDescriptorBinder: frame %u descriptor pool exhausted, grew pool (%u -> %u sets), poolCount=%zu",
frameIndex, currentMaxSets, grownMaxSets, frame.descriptorPools.size());
return true;
}
VkResult UniformManager::AllocateDescriptorSetsFromActivePool(Uint32 frameIndex, const ProgramFactory::VkProgramObject& programObj, VkDescriptorSet& outDescriptorSet) {
auto& frame = m_frames[frameIndex];
auto& bucket = frame.descriptorPools[frame.activeDescriptorPoolIndex];
VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &programObj.descriptorSetLayout;
allocInfo.descriptorPool = bucket.handle;
VkResult result = vkAllocateDescriptorSets(m_device, &allocInfo, &outDescriptorSet);
if (result == VK_SUCCESS) {
++bucket.allocatedSets;
++frame.allocatedSetsThisFrame;
frame.peakAllocatedSetsThisFrame =
std::max(frame.peakAllocatedSetsThisFrame, frame.allocatedSetsThisFrame);
}
return result;
}
Bool UniformManager::BindProgramUniformBuffers(VkCommandBuffer commandBuffer,
const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj,
Uint32 frameIndex,
const SamplerBindingOverride* samplerBindingOverride) {
auto& frame = m_frames[frameIndex];
if (frame.descriptorPools.empty()) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame descriptor pools are invalid");
return false;
}
if (frame.activeDescriptorPoolIndex >= frame.descriptorPools.size()) {
frame.activeDescriptorPoolIndex = 0;
}
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
VkResult allocResult = AllocateDescriptorSetsFromActivePool(frameIndex, programObj, descriptorSet);
if (allocResult == VK_ERROR_OUT_OF_POOL_MEMORY || allocResult == VK_ERROR_FRAGMENTED_POOL) {
if (!GrowFrameDescriptorPool(frame, frameIndex)) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: descriptor pool growth failed");
return false;
}
allocResult = AllocateDescriptorSetsFromActivePool(frameIndex, programObj, descriptorSet);
}
if (allocResult != VK_SUCCESS || descriptorSet == VK_NULL_HANDLE) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: vkAllocateDescriptorSets returned %d",
allocResult);
return false;
}
Vector<const void*> bindingData;
Vector<VkDeviceSize> bindingSizes;
if (!GatherBindingPayloads(program, bindingData, bindingSizes)) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: cannot gather UBO payloads");
return false;
}
MOBILEGL_ASSERT(m_textureManager != nullptr, "BindProgramUniformBuffers: texture manager is null");
MOBILEGL_ASSERT(m_samplerManager != nullptr, "BindProgramUniformBuffers: sampler manager is null");
MOBILEGL_ASSERT(m_bufferManager != nullptr, "BindProgramUniformBuffers: buffer manager is null");
Vector<VkWriteDescriptorSet> writes;
writes.reserve(m_maxBindings);
Vector<VkDescriptorBufferInfo> bufferInfos;
Vector<VkDescriptorImageInfo> imageInfos;
Vector<Uint32> dynamicOffsets;
bufferInfos.reserve(m_maxBindings);
imageInfos.reserve(m_maxBindings);
dynamicOffsets.reserve(programObj.dynamicBindings.size());
const Uint32 bindingCount =
std::min<Uint32>(m_maxBindings, static_cast<Uint32>(programObj.bindingKinds.size()));
for (Uint32 binding = 0; binding < bindingCount; ++binding) {
const auto kind = programObj.bindingKinds[binding];
if (kind == ProgramFactory::DescriptorBindingKind::None) {
continue;
}
VkWriteDescriptorSet write{};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = descriptorSet;
write.dstBinding = binding;
write.dstArrayElement = 0;
write.descriptorCount = 1;
if (kind == ProgramFactory::DescriptorBindingKind::UniformBufferDynamic) {
const void* payload = bindingData[binding];
VkDeviceSize payloadSize = bindingSizes[binding];
if (payload == nullptr || payloadSize == 0) {
if (programObj.globalUboBinding == static_cast<Int>(binding)) {
const void* globalUboData = program.GetUBOData();
const VkDeviceSize globalUboSize = static_cast<VkDeviceSize>(program.GetUBOSize());
if (globalUboData != nullptr && globalUboSize > 0) {
payload = globalUboData;
payloadSize = globalUboSize;
}
}
}
BufferSlice slice{};
if (!m_bufferManager->UploadTransient(BufferKind::Uniform, frameIndex, payload, payloadSize,
m_minDynamicOffsetAlignment, slice)) {
MOBILEGL_ASSERT(false, "UniformDescriptorBinder::BindProgramUniformBuffers failed: UBO upload failed on binding %u",
binding);
return false;
}
VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = slice.buffer;
bufferInfo.offset = 0;
bufferInfo.range = payloadSize;
bufferInfos.push_back(bufferInfo);
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
write.pBufferInfo = &bufferInfos.back();
writes.push_back(write);
dynamicOffsets.push_back(static_cast<Uint32>(slice.offset));
} else {
VkDescriptorImageInfo imageInfo{};
Bool hasImage = false;
if (samplerBindingOverride != nullptr &&
samplerBindingOverride->binding == binding &&
samplerBindingOverride->texture != nullptr &&
samplerBindingOverride->sampler != nullptr) {
hasImage = ResolveSamplerDescriptorOverride(*samplerBindingOverride, imageInfo);
} else {
hasImage = ResolveSamplerDescriptor(commandBuffer, program, programObj, binding, imageInfo);
}
if (!hasImage) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: sampler binding %u has no valid texture descriptor",
binding);
return false;
}
if (imageInfo.sampler == VK_NULL_HANDLE || imageInfo.imageView == VK_NULL_HANDLE) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: sampler binding %u has null sampler or imageView",
binding);
return false;
}
imageInfos.push_back(imageInfo);
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write.pImageInfo = &imageInfos.back();
writes.push_back(write);
}
}
if (!writes.empty()) {
vkUpdateDescriptorSets(m_device, static_cast<Uint32>(writes.size()), writes.data(), 0, nullptr);
}
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, programObj.pipelineLayout, 0, 1,
&descriptorSet, static_cast<Uint32>(dynamicOffsets.size()), dynamicOffsets.data());
return true;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,93 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.h
// 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
#pragma once
#include "ProgramFactory.h"
#include "VkBufferManager.h"
#include "VkSamplerManager.h"
#include "VkTextureManager.h"
#include "../VkIncludes.h"
#include <Includes.h>
namespace MobileGL::MG_State::GLState {
class ITextureObject;
class ProgramObject;
class SamplerObject;
}
namespace MobileGL::MG_Backend::DirectVulkan {
class UniformManager {
public:
struct SamplerBindingOverride {
Uint32 binding = 0;
MG_State::GLState::ITextureObject* texture = nullptr;
const MG_State::GLState::SamplerObject* sampler = nullptr;
};
Bool Initialize(VkDevice device, VkBufferManager* bufferManager,
ProgramFactory* programFactory,
VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
Uint32 maxBindings = 16, Uint32 setsPerFrame = 64,
VkTextureManager* textureManager = nullptr, VkSamplerManager* samplerManager = nullptr);
void Shutdown();
void BeginFrame(Uint32 frameIndex);
Bool CollectSampledTextures(const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj,
Vector<MG_State::GLState::ITextureObject*>& outTextures);
Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer,
const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj,
Uint32 frameIndex,
const SamplerBindingOverride* samplerBindingOverride = nullptr);
private:
struct DescriptorPoolBucket {
VkDescriptorPool handle = VK_NULL_HANDLE;
Uint32 maxSets = 0;
Uint32 allocatedSets = 0;
};
struct FrameResources {
Vector<DescriptorPoolBucket> descriptorPools;
Uint32 activeDescriptorPoolIndex = 0;
Uint32 allocatedSetsThisFrame = 0;
Uint32 peakAllocatedSetsThisFrame = 0;
};
Bool ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
SharedPtr<MG_State::GLState::ITextureObject>& outTexture) const;
Bool ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
VkDescriptorImageInfo& outImageInfo) const;
Bool ResolveSamplerDescriptorOverride(const SamplerBindingOverride& samplerBindingOverride,
VkDescriptorImageInfo& outImageInfo) const;
Bool GatherBindingPayloads(const MG_State::GLState::ProgramObject& program, Vector<const void*>& outData,
Vector<VkDeviceSize>& outSizes) const;
Bool CreateDescriptorPool(Uint32 maxSets, VkDescriptorPool& outPool) const;
Bool GrowFrameDescriptorPool(FrameResources& frame, Uint32 frameIndex);
VkResult AllocateDescriptorSetsFromActivePool(
Uint32 frameIndex, const ProgramFactory::VkProgramObject& programObj, VkDescriptorSet& outDescriptorSet);
VkDevice m_device = VK_NULL_HANDLE;
VkBufferManager* m_bufferManager = nullptr;
ProgramFactory* m_programFactory = nullptr;
Vector<FrameResources> m_frames;
VkDeviceSize m_minDynamicOffsetAlignment = 1;
Uint32 m_frameCount = 0;
Uint32 m_maxBindings = 0;
Uint32 m_setsPerFrame = 0;
Uint32 m_peakDescriptorSetsObserved = 0;
VkTextureManager* m_textureManager = nullptr;
VkSamplerManager* m_samplerManager = nullptr;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,246 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.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 "VkBufferManager.h"
namespace MobileGL::MG_Backend::DirectVulkan {
namespace {
constexpr Uint32 kResidentBufferGCInterval = 60;
constexpr VmaAllocationCreateFlags kResidentBufferAllocationFlags =
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
} // namespace
Bool VkBufferManager::Initialize(const VkBufferManagerInitInfo& initInfo) {
Shutdown();
MOBILEGL_ASSERT(initInfo.allocator != nullptr, "VkBufferManager::Initialize requires valid allocator");
MOBILEGL_ASSERT(initInfo.frameCount > 0, "VkBufferManager::Initialize requires non-zero frame count");
m_initInfo = initInfo;
m_deferredResidentReleases.resize(initInfo.frameCount);
m_currentFrameIndex = 0;
return InitializeTransientArenas();
}
void VkBufferManager::Shutdown() {
m_transientUploadArena.Shutdown();
DestroyResidentBuffers();
DestroyDeferredResidentReleases();
m_initInfo = {};
m_currentFrameIndex = 0;
m_residentGcTick = 0;
}
Bool VkBufferManager::RecreateTransientArenas(Uint32 frameCount) {
MOBILEGL_ASSERT(m_initInfo.allocator != nullptr, "VkBufferManager::RecreateTransientArenas requires initialized manager");
MOBILEGL_ASSERT(frameCount > 0, "VkBufferManager::RecreateTransientArenas requires non-zero frame count");
m_transientUploadArena.Shutdown();
m_initInfo.frameCount = frameCount;
DestroyDeferredResidentReleases();
m_deferredResidentReleases.resize(frameCount);
m_currentFrameIndex = 0;
return InitializeTransientArenas();
}
void VkBufferManager::BeginFrame(Uint32 frameIndex) {
MOBILEGL_ASSERT(frameIndex < m_deferredResidentReleases.size(),
"VkBufferManager::BeginFrame frame index out of range");
m_currentFrameIndex = frameIndex;
CollectDeferredResidentReleases(frameIndex);
m_transientUploadArena.BeginFrame(frameIndex);
}
Bool VkBufferManager::UploadTransient(BufferKind kind, Uint32 frameIndex, const void* data,
VkDeviceSize size, VkDeviceSize alignment, BufferSlice& outSlice) {
(void)kind;
return m_transientUploadArena.Upload(frameIndex, data, size, alignment, outSlice);
}
Bool VkBufferManager::InitializeTransientArenas() {
return m_transientUploadArena.Initialize({
.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,
.memoryUsage = m_initInfo.transientMemoryUsage,
.allocationFlags = m_initInfo.transientAllocationFlags,
.minBufferSize = m_initInfo.minUploadBytes,
.persistentlyMapped = m_initInfo.transientPersistentMapping,
});
}
Bool VkBufferManager::SyncResidentBuffer(BufferKind kind,
const SharedPtr<MG_State::GLState::BufferObject>& bufferObject,
BufferSlice& outSlice) {
const VkBufferUsageFlags requiredUsage = GetVkBufferUsage(kind);
MOBILEGL_ASSERT(requiredUsage != 0,
"VkBufferManager::SyncResidentBuffer only supports resident vertex/index buffers");
MOBILEGL_ASSERT(bufferObject != nullptr, "VkBufferManager::SyncResidentBuffer requires valid buffer object");
CollectResidentGarbageIfNeeded();
const auto* bufferData = bufferObject->GetDataReadOnly().get();
MOBILEGL_ASSERT(bufferData != nullptr, "VkBufferManager::SyncResidentBuffer requires frontend buffer data");
const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(bufferObject->GetSize());
if (bufferSize == 0) {
MGLOG_E("VkBufferManager::SyncResidentBuffer failed: buffer size is zero");
return false;
}
auto& entry = m_residentBuffers[bufferObject.get()];
entry.aliveRef = bufferObject;
const auto changeBits = bufferObject->GetChangeBits();
const Bool needsRecreate = !entry.buffer.IsValid() || entry.size != bufferSize ||
((entry.usage & requiredUsage) != requiredUsage) ||
(changeBits & BufferChangeBits::PreferReallocationBit);
if (needsRecreate) {
const VkBufferUsageFlags recreatedUsage = entry.usage | requiredUsage;
DeferResidentRelease(std::move(entry.buffer));
const Bool created = entry.buffer.Create({
.allocator = m_initInfo.allocator,
.size = bufferSize,
.usage = recreatedUsage,
.memoryUsage = VMA_MEMORY_USAGE_AUTO,
.allocationFlags = kResidentBufferAllocationFlags,
});
if (!created || entry.buffer.Map() == nullptr) {
MGLOG_E("VkBufferManager::SyncResidentBuffer failed: unable to create resident buffer");
entry.buffer.Destroy();
entry.size = 0;
entry.usage = 0;
return false;
}
if (!entry.buffer.Upload(bufferData->data(), bufferSize, 0)) {
MGLOG_E("VkBufferManager::SyncResidentBuffer failed: initial upload failed");
entry.buffer.Destroy();
entry.size = 0;
entry.usage = 0;
return false;
}
entry.size = bufferSize;
entry.usage = recreatedUsage;
bufferObject->ClearDirty();
outSlice = entry.buffer.GetSlice(0, bufferSize);
return true;
}
if (changeBits & BufferChangeBits::DirtyBit) {
const auto& dirtyRanges = bufferObject->GetDirtyRanges();
for (const auto& range : dirtyRanges) {
const VkDeviceSize rangeOffset = static_cast<VkDeviceSize>(range.start);
const VkDeviceSize rangeSize = static_cast<VkDeviceSize>(range.end - range.start);
if (rangeSize == 0) {
continue;
}
if (!entry.buffer.Upload(bufferData->data() + range.start, rangeSize, rangeOffset)) {
MGLOG_E("VkBufferManager::SyncResidentBuffer failed: dirty range upload failed");
return false;
}
}
bufferObject->ClearDirty();
}
outSlice = entry.buffer.GetSlice(0, bufferSize);
return true;
}
void VkBufferManager::DowngradeResidentBufferToTransient(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject) {
if (bufferObject == nullptr) {
return;
}
auto it = m_residentBuffers.find(bufferObject.get());
if (it == m_residentBuffers.end()) {
return;
}
DeferResidentRelease(std::move(it->second.buffer));
m_residentBuffers.erase(it);
}
void VkBufferManager::DeferResidentRelease(VkBufferObject&& buffer) {
if (!buffer.IsValid()) {
return;
}
if (m_deferredResidentReleases.empty()) {
buffer.Destroy();
return;
}
MOBILEGL_ASSERT(m_currentFrameIndex < m_deferredResidentReleases.size(),
"VkBufferManager::DeferResidentRelease current frame index out of range");
m_deferredResidentReleases[m_currentFrameIndex].push_back(std::move(buffer));
}
void VkBufferManager::CollectDeferredResidentReleases(Uint32 frameIndex) {
MOBILEGL_ASSERT(frameIndex < m_deferredResidentReleases.size(),
"VkBufferManager::CollectDeferredResidentReleases frame index out of range");
m_deferredResidentReleases[frameIndex].clear();
}
VkBufferUsageFlags VkBufferManager::GetVkBufferUsage(BufferKind kind) {
switch (kind) {
case BufferKind::Vertex:
case BufferKind::Index:
// A GL buffer can be rebound between ARRAY_BUFFER and ELEMENT_ARRAY_BUFFER,
// and may even be used as both within the same draw setup. Keep resident
// vertex/index buffers compatible with both roles from the start so we
// never need to recreate a buffer after it has already been bound.
return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
case BufferKind::Uniform:
default:
return 0;
}
}
void VkBufferManager::CollectResidentGarbageIfNeeded() {
++m_residentGcTick;
if (m_residentGcTick < kResidentBufferGCInterval) {
return;
}
CollectResidentGarbageNow();
m_residentGcTick = 0;
}
void VkBufferManager::CollectResidentGarbageNow() {
Vector<MG_State::GLState::BufferObject*> staleBuffers;
staleBuffers.reserve(m_residentBuffers.size());
for (const auto& [rawBuffer, entry] : m_residentBuffers) {
if (entry.aliveRef.expired()) {
staleBuffers.push_back(rawBuffer);
}
}
for (const auto* rawBuffer : staleBuffers) {
auto it = m_residentBuffers.find(const_cast<MG_State::GLState::BufferObject*>(rawBuffer));
if (it == m_residentBuffers.end()) {
continue;
}
DeferResidentRelease(std::move(it->second.buffer));
m_residentBuffers.erase(it);
}
}
void VkBufferManager::DestroyDeferredResidentReleases() {
for (auto& deferredReleases : m_deferredResidentReleases) {
deferredReleases.clear();
}
m_deferredResidentReleases.clear();
}
void VkBufferManager::DestroyResidentBuffers() {
for (auto& [_, entry] : m_residentBuffers) {
entry.buffer.Destroy();
}
m_residentBuffers.clear();
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,72 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h
// 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
#pragma once
#include "BufferArena.h"
#include "MG_State/GLState/BufferState/BufferObject.h"
#include "../VkIncludes.h"
#include <Includes.h>
#include <vk_mem_alloc.h>
namespace MobileGL::MG_Backend::DirectVulkan {
enum class BufferKind : Uint8 {
Vertex,
Index,
Uniform,
};
struct VkBufferManagerInitInfo {
VmaAllocator allocator = nullptr;
Uint32 frameCount = 0;
VkDeviceSize minUploadBytes = 4 * 1024 * 1024;
VmaMemoryUsage transientMemoryUsage = VMA_MEMORY_USAGE_AUTO;
VmaAllocationCreateFlags transientAllocationFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
Bool transientPersistentMapping = false;
};
class VkBufferManager {
public:
Bool Initialize(const VkBufferManagerInitInfo& initInfo);
void Shutdown();
// Recreate all per-frame transient arenas
Bool RecreateTransientArenas(Uint32 frameCount);
void BeginFrame(Uint32 frameIndex);
Bool UploadTransient(BufferKind kind, Uint32 frameIndex, const void* data, VkDeviceSize size,
VkDeviceSize alignment, BufferSlice& outSlice);
Bool SyncResidentBuffer(BufferKind kind, const SharedPtr<MG_State::GLState::BufferObject>& bufferObject,
BufferSlice& outSlice);
void DowngradeResidentBufferToTransient(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject);
private:
struct ResidentBufferEntry {
WeakPtr<MG_State::GLState::BufferObject> aliveRef;
VkBufferObject buffer;
VkDeviceSize size = 0;
VkBufferUsageFlags usage = 0;
};
Bool InitializeTransientArenas();
static VkBufferUsageFlags GetVkBufferUsage(BufferKind kind);
void DeferResidentRelease(VkBufferObject&& buffer);
void CollectDeferredResidentReleases(Uint32 frameIndex);
void CollectResidentGarbageIfNeeded();
void CollectResidentGarbageNow();
void DestroyDeferredResidentReleases();
void DestroyResidentBuffers();
VkBufferManagerInitInfo m_initInfo{};
BufferArena m_transientUploadArena;
UnorderedMap<MG_State::GLState::BufferObject*, ResidentBufferEntry> m_residentBuffers;
Vector<Vector<VkBufferObject>> m_deferredResidentReleases;
Uint32 m_currentFrameIndex = 0;
Uint32 m_residentGcTick = 0;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -13,11 +13,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_allocator = other.m_allocator; m_allocator = other.m_allocator;
m_buffer = other.m_buffer; m_buffer = other.m_buffer;
m_allocation = other.m_allocation; m_allocation = other.m_allocation;
m_mappedData = other.m_mappedData;
m_size = other.m_size; m_size = other.m_size;
other.m_allocator = nullptr; other.m_allocator = nullptr;
other.m_buffer = VK_NULL_HANDLE; other.m_buffer = VK_NULL_HANDLE;
other.m_allocation = nullptr; other.m_allocation = nullptr;
other.m_mappedData = nullptr;
other.m_size = 0; other.m_size = 0;
} }
@@ -31,11 +33,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_allocator = other.m_allocator; m_allocator = other.m_allocator;
m_buffer = other.m_buffer; m_buffer = other.m_buffer;
m_allocation = other.m_allocation; m_allocation = other.m_allocation;
m_mappedData = other.m_mappedData;
m_size = other.m_size; m_size = other.m_size;
other.m_allocator = nullptr; other.m_allocator = nullptr;
other.m_buffer = VK_NULL_HANDLE; other.m_buffer = VK_NULL_HANDLE;
other.m_allocation = nullptr; other.m_allocation = nullptr;
other.m_mappedData = nullptr;
other.m_size = 0; other.m_size = 0;
return *this; return *this;
} }
@@ -44,6 +48,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Destroy(); Destroy();
} }
Bool VkBufferObject::Create(const VkBufferObjectDesc& desc) {
return Create(desc.allocator, desc.size, desc.usage, desc.memoryUsage, desc.allocationFlags);
}
Bool VkBufferObject::Create(VmaAllocator allocator, VkDeviceSize size, VkBufferUsageFlags usage, Bool VkBufferObject::Create(VmaAllocator allocator, VkDeviceSize size, VkBufferUsageFlags usage,
VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocationFlags) { VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocationFlags) {
MOBILEGL_ASSERT(allocator != nullptr, "VkBufferObject::Create requires valid VMA allocator"); MOBILEGL_ASSERT(allocator != nullptr, "VkBufferObject::Create requires valid VMA allocator");
@@ -78,6 +86,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
void VkBufferObject::Destroy() { void VkBufferObject::Destroy() {
Unmap();
if (m_allocator != nullptr && m_buffer != VK_NULL_HANDLE && m_allocation != nullptr) { if (m_allocator != nullptr && m_buffer != VK_NULL_HANDLE && m_allocation != nullptr) {
vmaDestroyBuffer(m_allocator, m_buffer, m_allocation); vmaDestroyBuffer(m_allocator, m_buffer, m_allocation);
} }
@@ -87,6 +96,33 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_size = 0; m_size = 0;
} }
void* VkBufferObject::Map() {
MOBILEGL_ASSERT(IsValid(), "VkBufferObject::Map called on invalid buffer");
if (m_mappedData != nullptr) {
return m_mappedData;
}
const VkResult mapResult = vmaMapMemory(m_allocator, m_allocation, &m_mappedData);
if (mapResult != VK_SUCCESS || m_mappedData == nullptr) {
MGLOG_E("VkBufferObject::Map failed: vmaMapMemory returned %d", mapResult);
m_mappedData = nullptr;
return nullptr;
}
return m_mappedData;
}
void VkBufferObject::Unmap() {
if (!IsValid() || m_mappedData == nullptr) {
m_mappedData = nullptr;
return;
}
vmaUnmapMemory(m_allocator, m_allocation);
m_mappedData = nullptr;
}
Bool VkBufferObject::Upload(const void* data, VkDeviceSize size, VkDeviceSize offset) { Bool VkBufferObject::Upload(const void* data, VkDeviceSize size, VkDeviceSize offset) {
MOBILEGL_ASSERT(IsValid(), "VkBufferObject::Upload called on invalid buffer"); MOBILEGL_ASSERT(IsValid(), "VkBufferObject::Upload called on invalid buffer");
MOBILEGL_ASSERT(data != nullptr || size == 0, "VkBufferObject::Upload data pointer is null"); MOBILEGL_ASSERT(data != nullptr || size == 0, "VkBufferObject::Upload data pointer is null");
@@ -96,15 +132,30 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return true; return true;
} }
void* mapped = nullptr; const Bool wasMapped = IsMapped();
const VkResult mapResult = vmaMapMemory(m_allocator, m_allocation, &mapped); void* mapped = wasMapped ? m_mappedData : Map();
if (mapResult != VK_SUCCESS || mapped == nullptr) { if (mapped == nullptr) {
MGLOG_E("VkBufferObject::Upload failed: vmaMapMemory returned %d", mapResult); MGLOG_E("VkBufferObject::Upload failed: unable to map buffer");
return false; return false;
} }
Memcpy(static_cast<Uint8*>(mapped) + offset, data, static_cast<SizeT>(size)); Memcpy(static_cast<Uint8*>(mapped) + offset, data, static_cast<SizeT>(size));
vmaUnmapMemory(m_allocator, m_allocation); if (!wasMapped) {
Unmap();
}
return true; return true;
} }
BufferSlice VkBufferObject::GetSlice(VkDeviceSize offset, VkDeviceSize size) const {
MOBILEGL_ASSERT(offset <= m_size, "VkBufferObject::GetSlice offset out of range");
const VkDeviceSize resolvedSize = (size == VK_WHOLE_SIZE) ? (m_size - offset) : size;
MOBILEGL_ASSERT(offset + resolvedSize <= m_size, "VkBufferObject::GetSlice range out of bounds");
BufferSlice slice{};
slice.buffer = m_buffer;
slice.offset = offset;
slice.size = resolvedSize;
slice.mapped = (m_mappedData != nullptr) ? static_cast<Uint8*>(m_mappedData) + offset : nullptr;
return slice;
}
} // namespace MobileGL::MG_Backend::DirectVulkan } // namespace MobileGL::MG_Backend::DirectVulkan
@@ -8,11 +8,20 @@
#pragma once #pragma once
#include "BufferSlice.h"
#include "../VkIncludes.h" #include "../VkIncludes.h"
#include <Includes.h> #include <Includes.h>
#include <vk_mem_alloc.h> #include <vk_mem_alloc.h>
namespace MobileGL::MG_Backend::DirectVulkan { namespace MobileGL::MG_Backend::DirectVulkan {
struct VkBufferObjectDesc {
VmaAllocator allocator = nullptr;
VkDeviceSize size = 0;
VkBufferUsageFlags usage = 0;
VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
VmaAllocationCreateFlags allocationFlags = 0;
};
class VkBufferObject { class VkBufferObject {
public: public:
VkBufferObject() = default; VkBufferObject() = default;
@@ -23,20 +32,27 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkBufferObject(VkBufferObject&& other) noexcept; VkBufferObject(VkBufferObject&& other) noexcept;
VkBufferObject& operator=(VkBufferObject&& other) noexcept; VkBufferObject& operator=(VkBufferObject&& other) noexcept;
Bool Create(const VkBufferObjectDesc& desc);
Bool Create(VmaAllocator allocator, VkDeviceSize size, VkBufferUsageFlags usage, Bool Create(VmaAllocator allocator, VkDeviceSize size, VkBufferUsageFlags usage,
VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocationFlags = 0); VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocationFlags = 0);
void Destroy(); void Destroy();
void* Map();
void Unmap();
Bool Upload(const void* data, VkDeviceSize size, VkDeviceSize offset = 0); Bool Upload(const void* data, VkDeviceSize size, VkDeviceSize offset = 0);
VkBuffer GetHandle() const { return m_buffer; } VkBuffer GetHandle() const { return m_buffer; }
VkDeviceSize GetSize() const { return m_size; } VkDeviceSize GetSize() const { return m_size; }
BufferSlice GetSlice(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) const;
void* GetMappedData() const { return m_mappedData; }
Bool IsMapped() const { return m_mappedData != nullptr; }
Bool IsValid() const { return m_allocator != nullptr && m_buffer != VK_NULL_HANDLE && m_allocation != nullptr; } Bool IsValid() const { return m_allocator != nullptr && m_buffer != VK_NULL_HANDLE && m_allocation != nullptr; }
private: private:
VmaAllocator m_allocator = nullptr; VmaAllocator m_allocator = nullptr;
VkBuffer m_buffer = VK_NULL_HANDLE; VkBuffer m_buffer = VK_NULL_HANDLE;
VmaAllocation m_allocation = nullptr; VmaAllocation m_allocation = nullptr;
void* m_mappedData = nullptr;
VkDeviceSize m_size = 0; VkDeviceSize m_size = 0;
}; };
} // namespace MobileGL::MG_Backend::DirectVulkan } // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,118 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.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 "VkClearManager.h"
#include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h"
#include "MG_Util/Converters/MGToStr/TextureEnumConverter.h"
namespace MobileGL::MG_Backend::DirectVulkan {
Bool VkClearManager::Initialize() {
return true;
}
void VkClearManager::Shutdown() {
}
void VkClearManager::QueueClear(GLbitfield mask, const ClearFramebufferPayload& clearPayload,
const MG_State::GLState::FramebufferObject& drawFbo) {
if (mask & GL_COLOR_BUFFER_BIT) {
auto& drawbufs = drawFbo.GetDrawBuffers();
// This should automatically work on default & offscreen FBO
for (auto drawbuf: drawbufs) {
if (drawbuf == FramebufferAttachmentType::None ||
drawFbo.GetAttachment(drawbuf).IsRenderbuffer())
continue;
QueueClear({
.color = clearPayload.color,
.attachmentType = drawbuf
}, drawFbo.GetAttachment(drawbuf).GetTexture());
MGLOG_D("%s: %s (texture %d) - color = (%.2f, %.2f, %.2f, %.2f)", __func__,
MG_Util::ConvertFramebufferAttachmentTypeToString(drawbuf).c_str(),
drawFbo.GetAttachment(drawbuf).GetTexture()->GetExternalIndex(),
clearPayload.color[0], clearPayload.color[1], clearPayload.color[2], clearPayload.color[3]);
}
}
if (mask & GL_DEPTH_BUFFER_BIT &&
!drawFbo.GetAttachment(FramebufferAttachmentType::Depth).IsRenderbuffer()) {
QueueClear({
.depth = clearPayload.depth,
.attachmentType = FramebufferAttachmentType::Depth,
}, drawFbo.GetAttachment(FramebufferAttachmentType::Depth).GetTexture());
MGLOG_D("%s: Depth (texture %d) - depth = (%.2f)", __func__,
drawFbo.GetAttachment(FramebufferAttachmentType::Depth).GetTexture()->GetExternalIndex(), clearPayload.depth);
}
if (mask & GL_STENCIL_BUFFER_BIT &&
!drawFbo.GetAttachment(FramebufferAttachmentType::Stencil).IsRenderbuffer()) {
QueueClear({
.stencil = clearPayload.stencil,
.attachmentType = FramebufferAttachmentType::Stencil,
}, drawFbo.GetAttachment(FramebufferAttachmentType::Stencil).GetTexture());
MGLOG_D("%s: Stencil (texture %d) - stencil = (%u)", __func__,
drawFbo.GetAttachment(FramebufferAttachmentType::Stencil).GetTexture()->GetExternalIndex(), clearPayload.stencil);
}
}
void VkClearManager::QueueClear(const ClearAttachmentPayload& clearPayload,
const SharedPtr<MG_State::GLState::ITextureObject>& texture) {
WeakPtr<MG_State::GLState::ITextureObject> weakTexturePtr = texture;
if (weakTexturePtr.expired())
return;
auto* pTexture = weakTexturePtr.lock().get();
m_aliveObjects[pTexture] = weakTexturePtr;
m_pendingClears[pTexture] = clearPayload;
}
Bool VkClearManager::HasPendingClear(MG_State::GLState::ITextureObject* texture) {
return m_pendingClears.find(texture) != m_pendingClears.end();
}
Bool VkClearManager::GetPendingClear(MG_State::GLState::ITextureObject* texture, ClearAttachmentPayload& outPayload) {
if (m_aliveObjects.find(texture) == m_aliveObjects.end() ||
m_pendingClears.find(texture) == m_pendingClears.end()) {
MGLOG_D("%s: Failed getting pending clear for texture %d", __func__, texture->GetExternalIndex());
return false;
}
outPayload = m_pendingClears[texture];
MGLOG_D("%s: Got pending clear for texture %d (%s), clear value: color = (%.2f, %.2f, %.2f, %.2f), depth = (%.2f), stencil = (%u)", __func__,
texture->GetExternalIndex(),
MG_Util::ConvertTextureInternalFormatToString(texture->GetFormat()).c_str(),
outPayload.color[0], outPayload.color[1], outPayload.color[2], outPayload.color[3],
outPayload.depth,
outPayload.stencil);
return true;
}
void VkClearManager::PopPendingClear(MG_State::GLState::ITextureObject* texture) {
MGLOG_D("%s: Pop pending clear for texture %d", __func__, texture->GetExternalIndex());
m_aliveObjects.erase(texture);
m_pendingClears.erase(texture);
}
SizeT VkClearManager::CollectGarbage() {
m_gcCounter++;
if (m_gcCounter != 0) {
return 0;
}
SizeT count = 0;
for (const auto& [raw, weak]: m_aliveObjects) {
if (weak.expired()) {
count++;
m_pendingClears.erase(raw);
m_aliveObjects.erase(raw);
}
}
return count;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,52 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h
// 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
#pragma once
#include "../VkIncludes.h"
#include "../VulkanRendererConfig.h"
#include "MG_State/GLState/FramebufferState/FramebufferObject.h"
#include "MG_Util/Math/VectorTypes.h"
#include <Includes.h>
namespace MobileGL::MG_Backend::DirectVulkan {
struct ClearFramebufferPayload {
FloatVec4 color;
Float depth{};
Uint32 stencil{};
};
struct ClearAttachmentPayload {
union {
FloatVec4 color;
Float depth{};
Uint32 stencil;
};
FramebufferAttachmentType attachmentType = FramebufferAttachmentType::Color0;
};
class VkClearManager {
public:
Bool Initialize();
void Shutdown();
void QueueClear(GLbitfield mask, const ClearFramebufferPayload& clearPayload, const MG_State::GLState::FramebufferObject& drawFbo);
void QueueClear(
const ClearAttachmentPayload& clearPayload,
const SharedPtr<MG_State::GLState::ITextureObject>& texture);
Bool HasPendingClear(MG_State::GLState::ITextureObject* texture);
Bool GetPendingClear(MG_State::GLState::ITextureObject* texture, ClearAttachmentPayload& outPayload);
void PopPendingClear(MG_State::GLState::ITextureObject* texture);
SizeT CollectGarbage();
private:
Uint8 m_gcCounter = 0;
UnorderedMap<MG_State::GLState::ITextureObject*, ClearAttachmentPayload> m_pendingClears;
UnorderedMap<MG_State::GLState::ITextureObject*, WeakPtr<MG_State::GLState::ITextureObject>> m_aliveObjects;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,547 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkFramebufferManager.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 "VkFramebufferManager.h"
#include <MG_State/GLState/RenderbufferState/RenderbufferObject.h>
#include <MG_State/GLState/TextureState/TextureEnum.h>
namespace MobileGL::MG_Backend::DirectVulkan {
Bool VkFramebufferManager::Initialize(const InitInfo& initInfo) {
m_device = initInfo.device;
m_physicalDevice = initInfo.physicalDevice;
return m_device != VK_NULL_HANDLE && m_physicalDevice != VK_NULL_HANDLE;
}
void VkFramebufferManager::Shutdown() {
for (auto& [_, target] : m_offscreenColorTargets) {
DestroyOffscreenColorTarget(target);
}
m_offscreenColorTargets.clear();
m_device = VK_NULL_HANDLE;
m_physicalDevice = VK_NULL_HANDLE;
}
Bool VkFramebufferManager::EnsureOffscreenColorTarget(Uint glFboExternalIndex,
const MG_State::GLState::FramebufferObject& glFbo) {
const auto& colorAttachment = glFbo.GetAttachment(FramebufferAttachmentType::Color0);
if (!colorAttachment.IsValid() || colorAttachment.IsEmpty()) {
MGLOG_W("VkFramebufferManager: FBO %u has no valid COLOR0 attachment", glFboExternalIndex);
return false;
}
const auto objectVersion = glFbo.GetObjectVersion();
auto& target = m_offscreenColorTargets[glFboExternalIndex];
if (target.image != VK_NULL_HANDLE && target.glObjectVersion == objectVersion) {
return true;
}
return RecreateOffscreenColorTarget(target, glFbo, colorAttachment, objectVersion);
}
Bool VkFramebufferManager::TransitionOffscreenColorToAttachment(VkCommandBuffer commandBuffer,
Uint glFboExternalIndex) {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::TransitionOffscreenColorToAttachment skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
if (!TransitionImageLayout(commandBuffer, target.image, target.layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_IMAGE_ASPECT_COLOR_BIT)) {
return false;
}
if (target.depthStencilImage == VK_NULL_HANDLE) {
return true;
}
VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
if (target.depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT ||
target.depthStencilFormat == VK_FORMAT_D32_SFLOAT_S8_UINT) {
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
return TransitionImageLayout(
commandBuffer, target.depthStencilImage, target.depthStencilLayout,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, 0,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, aspectMask);
}
Bool VkFramebufferManager::TransitionOffscreenColorToTransferSrc(VkCommandBuffer commandBuffer,
Uint glFboExternalIndex) {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::TransitionOffscreenColorToTransferSrc skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
return TransitionImageLayout(commandBuffer, target.image, target.layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT);
}
Bool VkFramebufferManager::TransitionOffscreenColorToTransferDst(VkCommandBuffer commandBuffer,
Uint glFboExternalIndex) {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::TransitionOffscreenColorToTransferDst skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
return TransitionImageLayout(commandBuffer, target.image, target.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT);
}
Bool VkFramebufferManager::TransitionOffscreenColorToGeneral(VkCommandBuffer commandBuffer,
Uint glFboExternalIndex) {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::TransitionOffscreenColorToGeneral skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
return TransitionImageLayout(commandBuffer, target.image, target.layout, VK_IMAGE_LAYOUT_GENERAL,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_ASPECT_COLOR_BIT);
}
Bool VkFramebufferManager::TransitionOffscreenDepthStencilToTransferSrc(VkCommandBuffer commandBuffer,
Uint glFboExternalIndex) {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::TransitionOffscreenDepthStencilToTransferSrc skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
if (target.depthStencilImage == VK_NULL_HANDLE) {
return false;
}
VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
if (target.depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT ||
target.depthStencilFormat == VK_FORMAT_D32_SFLOAT_S8_UINT) {
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
return TransitionImageLayout(commandBuffer, target.depthStencilImage, target.depthStencilLayout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, VK_ACCESS_TRANSFER_READ_BIT, aspectMask);
}
Bool VkFramebufferManager::TransitionOffscreenDepthStencilToTransferDst(VkCommandBuffer commandBuffer,
Uint glFboExternalIndex) {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::TransitionOffscreenDepthStencilToTransferDst skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
if (target.depthStencilImage == VK_NULL_HANDLE) {
return false;
}
VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
if (target.depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT ||
target.depthStencilFormat == VK_FORMAT_D32_SFLOAT_S8_UINT) {
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
return TransitionImageLayout(commandBuffer, target.depthStencilImage, target.depthStencilLayout,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT, aspectMask);
}
Bool VkFramebufferManager::TransitionOffscreenDepthStencilToGeneral(VkCommandBuffer commandBuffer,
Uint glFboExternalIndex) {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::TransitionOffscreenDepthStencilToGeneral skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
if (target.depthStencilImage == VK_NULL_HANDLE) {
return false;
}
VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
if (target.depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT ||
target.depthStencilFormat == VK_FORMAT_D32_SFLOAT_S8_UINT) {
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
return TransitionImageLayout(commandBuffer, target.depthStencilImage, target.depthStencilLayout,
VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, aspectMask);
}
Bool VkFramebufferManager::TransitionOffscreenColorTextureToShaderRead(VkCommandBuffer commandBuffer,
Uint textureExternalIndex) {
for (auto& [_, target] : m_offscreenColorTargets) {
if (target.colorTextureExternalIndex != textureExternalIndex || target.image == VK_NULL_HANDLE) {
continue;
}
const Bool fromUndefined = (target.layout == VK_IMAGE_LAYOUT_UNDEFINED);
return TransitionImageLayout(
commandBuffer, target.image, target.layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
fromUndefined ? VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
: (VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT),
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
fromUndefined ? 0 : (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT),
VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT);
}
return false;
}
Bool VkFramebufferManager::GetOffscreenColorImage(Uint glFboExternalIndex, VkImage& outImage,
VkExtent2D& outExtent) const {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end() || it->second.image == VK_NULL_HANDLE) {
return false;
}
outImage = it->second.image;
outExtent = it->second.extent;
return true;
}
Bool VkFramebufferManager::GetOffscreenDepthStencilImage(Uint glFboExternalIndex, VkImage& outImage,
VkExtent2D& outExtent, VkFormat& outFormat) const {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end() || it->second.depthStencilImage == VK_NULL_HANDLE) {
return false;
}
outImage = it->second.depthStencilImage;
outExtent = it->second.extent;
outFormat = it->second.depthStencilFormat;
return true;
}
Bool VkFramebufferManager::GetOffscreenColorViewByTexture(Uint textureExternalIndex,
VkImageView& outImageView) const {
for (const auto& [_, target] : m_offscreenColorTargets) {
if (target.colorTextureExternalIndex != textureExternalIndex || target.imageView == VK_NULL_HANDLE) {
continue;
}
outImageView = target.imageView;
return true;
}
return false;
}
Bool VkFramebufferManager::GetOffscreenRenderSurface(Uint glFboExternalIndex, VkImageView& outColorView,
VkFormat& outColorFormat, VkImageView& outDepthStencilView,
VkFormat& outDepthStencilFormat, VkExtent2D& outExtent) const {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end() || it->second.imageView == VK_NULL_HANDLE) {
return false;
}
outColorView = it->second.imageView;
outColorFormat = it->second.format;
outDepthStencilView = it->second.depthStencilImageView;
outExtent = it->second.extent;
outDepthStencilFormat = it->second.depthStencilFormat;
return true;
}
Bool VkFramebufferManager::RecreateOffscreenColorTarget(
OffscreenColorTarget& target, const MG_State::GLState::FramebufferObject& glFbo,
const MG_State::GLState::FramebufferAttachmentObject& colorAttachment, Uint16 glObjectVersion) {
DestroyOffscreenColorTarget(target);
const auto size = colorAttachment.GetSize();
if (size.x() <= 0 || size.y() <= 0) {
MGLOG_W("VkFramebufferManager: COLOR0 attachment size is invalid (%d, %d)", size.x(), size.y());
return false;
}
const VkFormat format = ResolveColorFormat(colorAttachment);
if (format == VK_FORMAT_UNDEFINED) {
MGLOG_W("VkFramebufferManager: COLOR0 attachment format is unsupported for Vulkan clear");
return false;
}
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = static_cast<Uint32>(size.x());
imageInfo.extent.height = static_cast<Uint32>(size.y());
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = format;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_VERIFY(vkCreateImage(m_device, &imageInfo, nullptr, &target.image), "vkCreateImage(offscreen color)");
VkMemoryRequirements memoryRequirements{};
vkGetImageMemoryRequirements(m_device, target.image, &memoryRequirements);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memoryRequirements.size;
allocInfo.memoryTypeIndex =
FindMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_VERIFY(vkAllocateMemory(m_device, &allocInfo, nullptr, &target.memory), "vkAllocateMemory(offscreen color)");
VK_VERIFY(vkBindImageMemory(m_device, target.image, target.memory, 0), "vkBindImageMemory(offscreen color)");
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = target.image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = format;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &target.imageView),
"vkCreateImageView(offscreen color)");
const auto& depthAttachment = glFbo.GetAttachment(FramebufferAttachmentType::Depth);
const auto& stencilAttachment = glFbo.GetAttachment(FramebufferAttachmentType::Stencil);
const Bool requestedDepthStencil = (depthAttachment.IsValid() && !depthAttachment.IsEmpty()) ||
(stencilAttachment.IsValid() && !stencilAttachment.IsEmpty());
VkFormat depthStencilFormat = ResolveDepthStencilFormat(depthAttachment, stencilAttachment);
if (depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT) {
depthStencilFormat =
FindSupportedDepthStencilFormat({VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT});
} else if (depthStencilFormat == VK_FORMAT_D32_SFLOAT) {
depthStencilFormat = FindSupportedDepthStencilFormat({VK_FORMAT_D32_SFLOAT, VK_FORMAT_D16_UNORM});
}
const Bool hasDepthStencil = (depthStencilFormat != VK_FORMAT_UNDEFINED);
if (requestedDepthStencil && !hasDepthStencil) {
MGLOG_W("VkFramebufferManager: FBO %u depth/stencil attachment exists but format is unsupported",
glFbo.GetExternalIndex());
}
if (hasDepthStencil) {
VkImageCreateInfo depthImageInfo{};
depthImageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
depthImageInfo.imageType = VK_IMAGE_TYPE_2D;
depthImageInfo.extent.width = static_cast<Uint32>(size.x());
depthImageInfo.extent.height = static_cast<Uint32>(size.y());
depthImageInfo.extent.depth = 1;
depthImageInfo.mipLevels = 1;
depthImageInfo.arrayLayers = 1;
depthImageInfo.format = depthStencilFormat;
depthImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
depthImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
depthImageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
depthImageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
depthImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_VERIFY(vkCreateImage(m_device, &depthImageInfo, nullptr, &target.depthStencilImage),
"vkCreateImage(offscreen depth/stencil)");
VkMemoryRequirements depthMemoryRequirements{};
vkGetImageMemoryRequirements(m_device, target.depthStencilImage, &depthMemoryRequirements);
VkMemoryAllocateInfo depthAllocInfo{};
depthAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
depthAllocInfo.allocationSize = depthMemoryRequirements.size;
depthAllocInfo.memoryTypeIndex =
FindMemoryType(depthMemoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_VERIFY(vkAllocateMemory(m_device, &depthAllocInfo, nullptr, &target.depthStencilMemory),
"vkAllocateMemory(offscreen depth/stencil)");
VK_VERIFY(vkBindImageMemory(m_device, target.depthStencilImage, target.depthStencilMemory, 0),
"vkBindImageMemory(offscreen depth/stencil)");
VkImageAspectFlags depthAspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
if (depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT ||
depthStencilFormat == VK_FORMAT_D32_SFLOAT_S8_UINT) {
depthAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
VkImageViewCreateInfo depthViewInfo{};
depthViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
depthViewInfo.image = target.depthStencilImage;
depthViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
depthViewInfo.format = depthStencilFormat;
depthViewInfo.subresourceRange.aspectMask = depthAspectMask;
depthViewInfo.subresourceRange.baseMipLevel = 0;
depthViewInfo.subresourceRange.levelCount = 1;
depthViewInfo.subresourceRange.baseArrayLayer = 0;
depthViewInfo.subresourceRange.layerCount = 1;
VK_VERIFY(vkCreateImageView(m_device, &depthViewInfo, nullptr, &target.depthStencilImageView),
"vkCreateImageView(offscreen depth/stencil)");
}
target.layout = VK_IMAGE_LAYOUT_UNDEFINED;
target.extent = {static_cast<Uint32>(size.x()), static_cast<Uint32>(size.y())};
target.format = format;
target.depthStencilLayout = VK_IMAGE_LAYOUT_UNDEFINED;
target.depthStencilFormat = depthStencilFormat;
target.glObjectVersion = glObjectVersion;
target.colorTextureExternalIndex = (colorAttachment.IsTexture() && colorAttachment.GetTexture())
? colorAttachment.GetTexture()->GetExternalIndex()
: 0;
return true;
}
void VkFramebufferManager::DestroyOffscreenColorTarget(OffscreenColorTarget& target) {
if (target.imageView != VK_NULL_HANDLE) {
vkDestroyImageView(m_device, target.imageView, nullptr);
target.imageView = VK_NULL_HANDLE;
}
if (target.depthStencilImageView != VK_NULL_HANDLE) {
vkDestroyImageView(m_device, target.depthStencilImageView, nullptr);
target.depthStencilImageView = VK_NULL_HANDLE;
}
if (target.image != VK_NULL_HANDLE) {
vkDestroyImage(m_device, target.image, nullptr);
target.image = VK_NULL_HANDLE;
}
if (target.depthStencilImage != VK_NULL_HANDLE) {
vkDestroyImage(m_device, target.depthStencilImage, nullptr);
target.depthStencilImage = VK_NULL_HANDLE;
}
if (target.memory != VK_NULL_HANDLE) {
vkFreeMemory(m_device, target.memory, nullptr);
target.memory = VK_NULL_HANDLE;
}
if (target.depthStencilMemory != VK_NULL_HANDLE) {
vkFreeMemory(m_device, target.depthStencilMemory, nullptr);
target.depthStencilMemory = VK_NULL_HANDLE;
}
target.layout = VK_IMAGE_LAYOUT_UNDEFINED;
target.depthStencilLayout = VK_IMAGE_LAYOUT_UNDEFINED;
target.extent = {0, 0};
target.format = VK_FORMAT_UNDEFINED;
target.depthStencilFormat = VK_FORMAT_UNDEFINED;
target.glObjectVersion = 0;
target.colorTextureExternalIndex = 0;
}
Bool VkFramebufferManager::TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image,
VkImageLayout& trackedLayout, VkImageLayout newLayout,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask,
VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask) {
if (image == VK_NULL_HANDLE) {
return false;
}
if (trackedLayout == newLayout) {
return true;
}
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = srcAccessMask;
barrier.dstAccessMask = dstAccessMask;
barrier.oldLayout = trackedLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = aspectMask;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier);
trackedLayout = newLayout;
return true;
}
Uint32 VkFramebufferManager::FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const {
VkPhysicalDeviceMemoryProperties memoryProperties{};
vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memoryProperties);
for (Uint32 i = 0; i < memoryProperties.memoryTypeCount; ++i) {
if ((typeFilter & (1U << i)) &&
(memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) {
return i;
}
}
MOBILEGL_ASSERT(false, "VkFramebufferManager::FindMemoryType failed");
return 0;
}
VkFormat VkFramebufferManager::ResolveColorFormat(
const MG_State::GLState::FramebufferAttachmentObject& colorAttachment) {
TextureInternalFormat internalFormat = TextureInternalFormat::Unknown;
if (colorAttachment.IsTexture()) {
const auto texture = colorAttachment.GetTexture();
internalFormat = texture ? texture->GetFormat() : TextureInternalFormat::Unknown;
} else if (colorAttachment.IsRenderbuffer()) {
const auto renderbuffer = colorAttachment.GetRenderbuffer();
internalFormat = renderbuffer ? renderbuffer->GetInternalFormat() : TextureInternalFormat::Unknown;
}
switch (internalFormat) {
case TextureInternalFormat::RGBA:
case TextureInternalFormat::RGBA8:
case TextureInternalFormat::SRGB8Alpha8:
return VK_FORMAT_R8G8B8A8_UNORM;
default:
return VK_FORMAT_UNDEFINED;
}
}
VkFormat VkFramebufferManager::ResolveDepthStencilFormat(
const MG_State::GLState::FramebufferAttachmentObject& depthAttachment,
const MG_State::GLState::FramebufferAttachmentObject& stencilAttachment) {
const auto resolveAttachmentFormat = [](const MG_State::GLState::FramebufferAttachmentObject& attachment) {
TextureInternalFormat internalFormat = TextureInternalFormat::Unknown;
if (attachment.IsTexture()) {
const auto texture = attachment.GetTexture();
internalFormat = texture ? texture->GetFormat() : TextureInternalFormat::Unknown;
} else if (attachment.IsRenderbuffer()) {
const auto renderbuffer = attachment.GetRenderbuffer();
internalFormat = renderbuffer ? renderbuffer->GetInternalFormat() : TextureInternalFormat::Unknown;
}
return internalFormat;
};
const auto depthFormat = resolveAttachmentFormat(depthAttachment);
const auto stencilFormat = resolveAttachmentFormat(stencilAttachment);
switch (depthFormat) {
case TextureInternalFormat::Depth24Stencil8:
case TextureInternalFormat::Depth32FStencil8:
case TextureInternalFormat::DepthStencil:
return VK_FORMAT_D24_UNORM_S8_UINT;
case TextureInternalFormat::DepthComponent16:
return VK_FORMAT_D16_UNORM;
case TextureInternalFormat::DepthComponent24:
case TextureInternalFormat::DepthComponent32:
case TextureInternalFormat::DepthComponent32F:
case TextureInternalFormat::DepthComponent:
return VK_FORMAT_D32_SFLOAT;
default:
break;
}
switch (stencilFormat) {
case TextureInternalFormat::Depth24Stencil8:
case TextureInternalFormat::Depth32FStencil8:
case TextureInternalFormat::DepthStencil:
return VK_FORMAT_D24_UNORM_S8_UINT;
default:
return VK_FORMAT_UNDEFINED;
}
}
VkFormat VkFramebufferManager::FindSupportedDepthStencilFormat(const Vector<VkFormat>& candidates) const {
for (auto format : candidates) {
VkFormatProperties properties{};
vkGetPhysicalDeviceFormatProperties(m_physicalDevice, format, &properties);
if ((properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) {
return format;
}
}
return VK_FORMAT_UNDEFINED;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,83 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkFramebufferManager.h
// 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
#pragma once
#include "../VkIncludes.h"
#include <Includes.h>
#include <MG_State/GLState/FramebufferState/FramebufferObject.h>
namespace MobileGL::MG_Backend::DirectVulkan {
class VkFramebufferManager {
public:
struct InitInfo {
VkDevice device = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
};
VkFramebufferManager() = default;
~VkFramebufferManager() = default;
Bool Initialize(const InitInfo& initInfo);
void Shutdown();
Bool EnsureOffscreenColorTarget(Uint glFboExternalIndex, const MG_State::GLState::FramebufferObject& glFbo);
Bool TransitionOffscreenColorToAttachment(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenColorToTransferSrc(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenColorToTransferDst(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenColorToGeneral(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenDepthStencilToTransferSrc(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenDepthStencilToTransferDst(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenDepthStencilToGeneral(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenColorTextureToShaderRead(VkCommandBuffer commandBuffer, Uint textureExternalIndex);
Bool GetOffscreenColorImage(Uint glFboExternalIndex, VkImage& outImage, VkExtent2D& outExtent) const;
Bool GetOffscreenDepthStencilImage(Uint glFboExternalIndex, VkImage& outImage, VkExtent2D& outExtent,
VkFormat& outFormat) const;
Bool GetOffscreenColorViewByTexture(Uint textureExternalIndex, VkImageView& outImageView) const;
Bool GetOffscreenRenderSurface(Uint glFboExternalIndex, VkImageView& outColorView, VkFormat& outColorFormat,
VkImageView& outDepthStencilView, VkFormat& outDepthStencilFormat,
VkExtent2D& outExtent) const;
private:
struct OffscreenColorTarget {
VkImage image = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
VkImageView imageView = VK_NULL_HANDLE;
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkExtent2D extent = {0, 0};
VkFormat format = VK_FORMAT_UNDEFINED;
VkImage depthStencilImage = VK_NULL_HANDLE;
VkDeviceMemory depthStencilMemory = VK_NULL_HANDLE;
VkImageView depthStencilImageView = VK_NULL_HANDLE;
VkImageLayout depthStencilLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkFormat depthStencilFormat = VK_FORMAT_UNDEFINED;
Uint16 glObjectVersion = 0;
Uint colorTextureExternalIndex = 0;
};
Bool RecreateOffscreenColorTarget(OffscreenColorTarget& target,
const MG_State::GLState::FramebufferObject& glFbo,
const MG_State::GLState::FramebufferAttachmentObject& colorAttachment,
Uint16 glObjectVersion);
void DestroyOffscreenColorTarget(OffscreenColorTarget& target);
Bool TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout& trackedLayout,
VkImageLayout newLayout, VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask,
VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask);
Uint32 FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const;
static VkFormat ResolveColorFormat(const MG_State::GLState::FramebufferAttachmentObject& colorAttachment);
static VkFormat ResolveDepthStencilFormat(
const MG_State::GLState::FramebufferAttachmentObject& depthAttachment,
const MG_State::GLState::FramebufferAttachmentObject& stencilAttachment);
VkFormat FindSupportedDepthStencilFormat(const Vector<VkFormat>& candidates) const;
VkDevice m_device = VK_NULL_HANDLE;
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
UnorderedMap<Uint, OffscreenColorTarget> m_offscreenColorTargets;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -8,340 +8,500 @@
#include "VkRenderPassManager.h" #include "VkRenderPassManager.h"
#include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h"
#include "MG_State/GLState/TextureState/TextureObject2D.h"
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
namespace MobileGL::MG_Backend::DirectVulkan { namespace MobileGL::MG_Backend::DirectVulkan {
Bool VkRenderPassManager::Initialize(const InitInfo& initInfo) { VkRenderPassManager::VkRenderPassManager(VkDevice device,
Shutdown(); const VulkanRendererConfig& config, VkClearManager& clearManager, VkTextureManager& textureManager,
SwapchainObject& swapchainObject):
m_device(device), m_config(config), m_clearManager(clearManager), m_textureManager(textureManager),
m_swapchainObject(swapchainObject) {
RenderPassEntry::s_device = m_device;
s_clearManager = &m_clearManager;
s_textureManager = &m_textureManager;
s_swapchainObject = &m_swapchainObject;
}
if (initInfo.device == VK_NULL_HANDLE || initInfo.colorFormat == VK_FORMAT_UNDEFINED || VkRenderPassManager::~VkRenderPassManager() {}
initInfo.depthStencilFormat == VK_FORMAT_UNDEFINED) {
return false;
}
m_device = initInfo.device; Bool VkRenderPassManager::Initialize() {
m_colorFormat = initInfo.colorFormat; return true;
m_depthStencilFormat = initInfo.depthStencilFormat;
m_renderPassLoad = CreateDefaultRenderPass(VK_ATTACHMENT_LOAD_OP_LOAD);
m_renderPassClear = CreateDefaultRenderPass(VK_ATTACHMENT_LOAD_OP_CLEAR);
MGLOG_D("VkRenderPassManager: RenderPasses created (LOAD/CLEAR).");
return m_renderPassLoad != VK_NULL_HANDLE && m_renderPassClear != VK_NULL_HANDLE;
} }
void VkRenderPassManager::Shutdown() { void VkRenderPassManager::Shutdown() {
DestroyDefaultFramebuffers(); }
for (auto& [_, target] : m_offscreenRenderTargets) {
DestroyOffscreenRenderTarget(target);
}
m_offscreenRenderTargets.clear();
if (m_device != VK_NULL_HANDLE) { VkRenderPassManager::HashType VkRenderPassManager::ComputeHash(
if (m_renderPassClear != VK_NULL_HANDLE) { const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex, Bool includePendingClear) const {
vkDestroyRenderPass(m_device, m_renderPassClear, nullptr); XXHASH_VERIFY(XXH64_reset(m_hashState, m_config.CacheVersion));
const Bool isDefaultFbo = (&fbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get());
if (isDefaultFbo) {
XXHASH_VERIFY(XXH64_update(m_hashState, &swapchainImageIndex, sizeof(swapchainImageIndex)));
}
auto& drawBuffers = fbo.GetDrawBuffers();
XXHASH_VERIFY(XXH64_update(m_hashState, drawBuffers.data(), drawBuffers.size() * sizeof(drawBuffers[0])));
auto readBuffer = fbo.GetReadBuffer();
XXHASH_VERIFY(XXH64_update(m_hashState, &readBuffer, sizeof(FramebufferAttachmentType)));
Int validDrawBufCount = 0;
for (Int i = 0; i < drawBuffers.size(); ++i) {
auto drawbuf = drawBuffers[i];
if (drawbuf != FramebufferAttachmentType::None)
validDrawBufCount = std::max(validDrawBufCount, i + 1);
}
XXHASH_VERIFY(XXH64_update(m_hashState, &validDrawBufCount, sizeof(validDrawBufCount)));
auto combineFramebufferAttachmentObjHash = [&](FramebufferAttachmentType attachment) {
auto& att = fbo.GetAttachment(attachment);
Int type = 0;
if (att.IsEmpty()) type = 0;
else if (att.IsTexture()) type = 1;
else if (att.IsRenderbuffer()) type = 2;
XXHASH_VERIFY(XXH64_update(m_hashState, &type, sizeof(type)));
void* contentPtr = nullptr;
if (att.IsTexture())
contentPtr = att.GetTexture().get();
else if (att.IsRenderbuffer())
contentPtr = att.GetRenderbuffer().get();
XXHASH_VERIFY(XXH64_update(m_hashState, &contentPtr, sizeof(contentPtr)));
if (att.IsTexture()) {
const Int textureLevel = att.GetTextureLevel();
XXHASH_VERIFY(XXH64_update(m_hashState, &textureLevel, sizeof(textureLevel)));
} }
if (m_renderPassLoad != VK_NULL_HANDLE) {
vkDestroyRenderPass(m_device, m_renderPassLoad, nullptr); if (includePendingClear && att.IsTexture()) {
auto* texture = att.GetTexture().get();
auto hasClear = m_clearManager.HasPendingClear(texture);
XXHASH_VERIFY(XXH64_update(m_hashState, &hasClear, sizeof(hasClear)));
if (hasClear) {
ClearAttachmentPayload clearPayload{};
Bool hasPayload = m_clearManager.GetPendingClear(texture, clearPayload);
XXHASH_VERIFY(XXH64_update(m_hashState, &hasPayload, sizeof(hasPayload)));
if (hasPayload) {
XXHASH_VERIFY(XXH64_update(
m_hashState, &clearPayload.attachmentType, sizeof(clearPayload.attachmentType)));
}
}
VkImageLayout currentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
if (isDefaultFbo) {
if (attachment >= FramebufferAttachmentType::Color0 &&
attachment <= FramebufferAttachmentType::Color31) {
currentLayout = m_swapchainObject.GetImageLayout(swapchainImageIndex);
} else if (attachment == FramebufferAttachmentType::Depth ||
attachment == FramebufferAttachmentType::Stencil) {
currentLayout = m_swapchainObject.GetDepthStencilImageLayout(swapchainImageIndex);
}
} else {
auto* resource = m_textureManager.SyncTextureAndGetDescriptor(*texture);
if (resource != nullptr) {
currentLayout = resource->layout;
}
}
XXHASH_VERIFY(XXH64_update(m_hashState, &currentLayout, sizeof(currentLayout)));
} }
};
for (Int i = 0; i < validDrawBufCount; ++i) {
auto drawbuf = drawBuffers[i];
combineFramebufferAttachmentObjHash(drawbuf);
} }
m_renderPassClear = VK_NULL_HANDLE; combineFramebufferAttachmentObjHash(FramebufferAttachmentType::Depth);
m_renderPassLoad = VK_NULL_HANDLE; combineFramebufferAttachmentObjHash(FramebufferAttachmentType::Stencil);
m_colorFormat = VK_FORMAT_UNDEFINED;
m_depthStencilFormat = VK_FORMAT_UNDEFINED; return XXH64_digest(m_hashState);
m_defaultExtent = {0, 0};
m_device = VK_NULL_HANDLE;
} }
Bool VkRenderPassManager::RecreateDefaultFramebuffers(const Vector<VkImageView>& colorViews, RenderPassEntry& VkRenderPassManager::GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo,
const Vector<VkImageView>& depthStencilViews, Uint32 swapchainImageIndex) {
VkExtent2D extent) { auto hasPendingClearOnFramebuffer = [&]() -> Bool {
DestroyDefaultFramebuffers(); const auto& drawBuffers = fbo.GetDrawBuffers();
for (auto attachment : drawBuffers) {
if (attachment == FramebufferAttachmentType::None) {
continue;
}
if (m_device == VK_NULL_HANDLE || m_renderPassLoad == VK_NULL_HANDLE || extent.width == 0 || const auto& att = fbo.GetAttachment(attachment);
extent.height == 0 || colorViews.empty() || colorViews.size() != depthStencilViews.size()) { if (att.IsTexture() && m_clearManager.HasPendingClear(att.GetTexture().get())) {
return false; return true;
} }
m_defaultFramebuffers.reserve(colorViews.size());
for (SizeT i = 0; i < colorViews.size(); ++i) {
if (colorViews[i] == VK_NULL_HANDLE || depthStencilViews[i] == VK_NULL_HANDLE) {
DestroyDefaultFramebuffers();
return false;
} }
VkImageView attachments[] = {colorViews[i], depthStencilViews[i]};
VkFramebufferCreateInfo createInfo{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; const auto& depthAtt = fbo.GetAttachment(FramebufferAttachmentType::Depth);
createInfo.renderPass = m_renderPassLoad; if (depthAtt.IsTexture() && m_clearManager.HasPendingClear(depthAtt.GetTexture().get())) {
createInfo.attachmentCount = static_cast<Uint32>(std::size(attachments)); return true;
createInfo.pAttachments = attachments; }
createInfo.width = extent.width;
createInfo.height = extent.height; const auto& stencilAtt = fbo.GetAttachment(FramebufferAttachmentType::Stencil);
createInfo.layers = 1; if (stencilAtt.IsTexture() && m_clearManager.HasPendingClear(stencilAtt.GetTexture().get())) {
VkFramebuffer framebuffer = VK_NULL_HANDLE; return true;
if (vkCreateFramebuffer(m_device, &createInfo, nullptr, &framebuffer) != VK_SUCCESS) {
DestroyDefaultFramebuffers();
return false;
} }
m_defaultFramebuffers.push_back(framebuffer);
}
m_defaultExtent = extent;
return true;
}
Bool VkRenderPassManager::GetDefaultRenderTarget(Uint32 imageIndex, VkRenderPass& outRenderPass,
VkFramebuffer& outFramebuffer, VkExtent2D& outExtent,
VkFormat& outDepthStencilFormat) const {
if (imageIndex >= m_defaultFramebuffers.size() || m_renderPassLoad == VK_NULL_HANDLE) {
return false; return false;
} };
outRenderPass = m_renderPassLoad;
outFramebuffer = m_defaultFramebuffers[imageIndex];
outExtent = m_defaultExtent;
outDepthStencilFormat = m_depthStencilFormat;
return outFramebuffer != VK_NULL_HANDLE;
}
Bool VkRenderPassManager::EnsureOffscreenRenderTarget(const OffscreenRenderTargetInfo& targetInfo) { // retrieve from cache first
if (m_device == VK_NULL_HANDLE || targetInfo.colorView == VK_NULL_HANDLE || auto* activeRenderPass = GetActiveRenderPass();
targetInfo.colorFormat == VK_FORMAT_UNDEFINED || targetInfo.extent.width == 0 || auto compatibilityHash = ComputeHash(fbo, swapchainImageIndex, false);
targetInfo.extent.height == 0) { if (activeRenderPass != nullptr &&
return false; activeRenderPass->CompatibleWith(compatibilityHash) &&
!hasPendingClearOnFramebuffer()) {
auto activeIt = m_renderPasses.find(activeRenderPass->hash);
MOBILEGL_ASSERT(activeIt != m_renderPasses.end(),
"GetOrCreateRenderPass: active render pass hash=0x%llx is missing from cache",
static_cast<unsigned long long>(activeRenderPass->hash));
return activeIt->second;
}
auto hash = ComputeHash(fbo, swapchainImageIndex, true);
auto it = m_renderPasses.find(hash);
if (it != m_renderPasses.end())
return it->second;
Bool isDefaultFbo = (&fbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO.get());
// Color attachment
auto& drawbufs = fbo.GetDrawBuffers();
Int validDrawBufCount = 0;
for (Int i = 0; i < drawbufs.size(); ++i) {
auto drawbuf = drawbufs[i];
if (drawbuf != FramebufferAttachmentType::None)
validDrawBufCount = std::max(validDrawBufCount, i + 1);
} }
const Bool hasDepthStencil = Int width = 0;
targetInfo.depthStencilView != VK_NULL_HANDLE && targetInfo.depthStencilFormat != VK_FORMAT_UNDEFINED; Int height = 0;
Vector<VkAttachmentDescription> attachmentDescriptions(validDrawBufCount);
Vector<VkAttachmentReference> colorAttachmentRefs(validDrawBufCount);
Vector<PendingClearAttachmentInfo> pendingClearAttachments;
Vector<TrackedAttachmentLayoutInfo> trackedAttachmentLayouts;
auto& textureResources = RenderPassEntry::s_textureResourcesScratch;
textureResources.clear();
textureResources.resize(validDrawBufCount, nullptr);
Vector<VkImageView> attachmentViews(validDrawBufCount, VK_NULL_HANDLE);
// This should automatically work on default & offscreen FBO
// assuming default FBO has the right param
for (Int i = 0; i < validDrawBufCount; ++i) {
auto drawbuf = drawbufs[i];
if (drawbuf == FramebufferAttachmentType::None ||
fbo.GetAttachment(drawbuf).IsRenderbuffer())
continue;
auto& target = m_offscreenRenderTargets[targetInfo.targetExternalIndex]; auto& att = fbo.GetAttachment(drawbuf);
if (target.framebuffer != VK_NULL_HANDLE && target.renderPassLoad != VK_NULL_HANDLE && auto* texture = att.GetTexture().get();
target.targetVersion == targetInfo.targetVersion && target.colorView == targetInfo.colorView && const Uint32 attachmentMipLevel = static_cast<Uint32>(std::max(att.GetTextureLevel(), 0));
target.colorFormat == targetInfo.colorFormat && target.depthStencilView == targetInfo.depthStencilView && const auto textureTarget = texture->GetTarget();
target.depthStencilFormat == targetInfo.depthStencilFormat &&
target.extent.width == targetInfo.extent.width && target.extent.height == targetInfo.extent.height) { // Color attachment description
return true; VkAttachmentDescription& desc = attachmentDescriptions[i];
switch (textureTarget) {
case TextureTarget::Texture2D: {
auto* texture2d =
static_cast<MG_State::GLState::TextureObject2D*>(texture);
desc.flags = 0;
desc.format =
MG_Util::ConvertTextureInternalFormatToVkEnum(
texture2d->GetFormat());
desc.samples = VK_SAMPLE_COUNT_1_BIT;
ClearAttachmentPayload clearPayload{};
Bool hasClear = m_clearManager.GetPendingClear(texture, clearPayload);
VkImageLayout trackedColorLayout = VK_IMAGE_LAYOUT_UNDEFINED;
desc.loadOp = hasClear ?
VK_ATTACHMENT_LOAD_OP_CLEAR :
VK_ATTACHMENT_LOAD_OP_LOAD;
desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
desc.finalLayout = isDefaultFbo ?
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR :
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
if (hasClear) {
pendingClearAttachments.emplace_back(PendingClearAttachmentInfo {
.attachmentIndex = static_cast<Uint32>(i),
.texture = texture
});
}
if (width == 0)
width = att.GetSize().x();
if (height == 0)
height = att.GetSize().y();
if (isDefaultFbo) {
const auto& swapchainViews = m_swapchainObject.GetImageViews();
MOBILEGL_ASSERT(swapchainImageIndex < swapchainViews.size(),
"GetOrCreateRenderPass: swapchain image index out of range");
trackedColorLayout = m_swapchainObject.GetImageLayout(swapchainImageIndex);
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
.target = TrackedAttachmentTarget::SwapchainColor,
.swapchainImageIndex = swapchainImageIndex,
.finalLayout = desc.finalLayout,
});
attachmentViews[i] = swapchainViews[swapchainImageIndex];
} else {
textureResources[i] = m_textureManager.SyncTextureAndGetDescriptor(*texture);
MOBILEGL_ASSERT(textureResources[i],
"GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at color attachment %d", i);
trackedColorLayout = textureResources[i]->layout;
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
.target = TrackedAttachmentTarget::Texture,
.texture = texture,
.finalLayout = desc.finalLayout,
});
attachmentViews[i] = m_textureManager.GetOrCreateViewAtMipLevel(*texture, attachmentMipLevel);
MOBILEGL_ASSERT(attachmentViews[i] != VK_NULL_HANDLE,
"GetOrCreateRenderPass: GetOrCreateAttachmentView failed at color attachment %d", i);
}
if (!hasClear && trackedColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
MGLOG_W("GetOrCreateRenderPass: color attachment textureId=%d starts with undefined layout and no clear; "
"using LOAD_OP_DONT_CARE",
texture->GetExternalIndex());
desc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
}
desc.initialLayout = (hasClear || trackedColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) ?
VK_IMAGE_LAYOUT_UNDEFINED :
trackedColorLayout;
break;
}
default:
MOBILEGL_ASSERT(false, "Unsupported texture target");
}
// Attachment reference
VkAttachmentReference& attachmentRef = colorAttachmentRefs[i];
attachmentRef.attachment = i;
attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
} }
DestroyOffscreenRenderTarget(target); // Depth attachment description
auto& depthAtt = fbo.GetAttachment(FramebufferAttachmentType::Depth);
const VkFormat depthStencilFormat = hasDepthStencil ? targetInfo.depthStencilFormat : VK_FORMAT_UNDEFINED; VkAttachmentDescription depthAttachmentDescription;
target.renderPassLoad = CreateRenderPass(targetInfo.colorFormat, depthStencilFormat, VK_ATTACHMENT_LOAD_OP_LOAD, VkAttachmentReference depthAttachmentRef;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); depthAttachmentRef.attachment = VK_ATTACHMENT_UNUSED;
if (target.renderPassLoad == VK_NULL_HANDLE) { depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
return false; VkTextureManager::TextureResource* depthTextureResource = nullptr;
if (depthAtt.IsComplete() && depthAtt.IsTexture()) {
auto& texture = *depthAtt.GetTexture();
const Uint32 attachmentMipLevel = static_cast<Uint32>(std::max(depthAtt.GetTextureLevel(), 0));
const Uint32 depthAttachmentIndex = static_cast<Uint32>(attachmentDescriptions.size());
ClearAttachmentPayload clearPayload{};
Bool hasClear = m_clearManager.GetPendingClear(&texture, clearPayload);
Bool clearDepth = hasClear && clearPayload.attachmentType == FramebufferAttachmentType::Depth;
Bool clearStencil = hasClear && clearPayload.attachmentType == FramebufferAttachmentType::Stencil;
VkImageLayout trackedDepthLayout = isDefaultFbo ?
m_swapchainObject.GetDepthStencilImageLayout(swapchainImageIndex) :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
if (!isDefaultFbo) {
depthTextureResource = m_textureManager.SyncTextureAndGetDescriptor(texture);
MOBILEGL_ASSERT(depthTextureResource,
"GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at depth attachment");
trackedDepthLayout = depthTextureResource->layout;
}
depthAttachmentDescription.flags = 0;
depthAttachmentDescription.format =
MG_Util::ConvertTextureInternalFormatToVkEnum(texture.GetFormat());
depthAttachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT;
depthAttachmentDescription.loadOp = clearDepth ?
VK_ATTACHMENT_LOAD_OP_CLEAR :
VK_ATTACHMENT_LOAD_OP_LOAD;
depthAttachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depthAttachmentDescription.stencilLoadOp = clearStencil ?
VK_ATTACHMENT_LOAD_OP_CLEAR :
VK_ATTACHMENT_LOAD_OP_LOAD;
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 =
(clearDepth && clearStencil) || trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED ?
VK_IMAGE_LAYOUT_UNDEFINED :
trackedDepthLayout;
if (hasClear) {
pendingClearAttachments.emplace_back(PendingClearAttachmentInfo {
.attachmentIndex = depthAttachmentIndex,
.texture = &texture
});
}
if (isDefaultFbo) {
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
.target = TrackedAttachmentTarget::SwapchainDepthStencil,
.swapchainImageIndex = swapchainImageIndex,
.finalLayout = depthAttachmentDescription.finalLayout,
});
attachmentViews.emplace_back(m_swapchainObject.GetDepthStencilImageView(swapchainImageIndex));
} else {
MOBILEGL_ASSERT(clearDepth || clearStencil || depthTextureResource->layout != VK_IMAGE_LAYOUT_UNDEFINED,
"GetOrCreateRenderPass: depth attachment textureId=%d has undefined tracked layout with LOAD_OP_LOAD",
texture.GetExternalIndex());
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
.target = TrackedAttachmentTarget::Texture,
.texture = &texture,
.finalLayout = depthAttachmentDescription.finalLayout,
});
textureResources.emplace_back(depthTextureResource);
attachmentViews.emplace_back(m_textureManager.GetOrCreateViewAtMipLevel(texture, attachmentMipLevel));
MOBILEGL_ASSERT(attachmentViews.back() != VK_NULL_HANDLE,
"GetOrCreateRenderPass: GetOrCreateAttachmentView failed at depth attachment");
if (width == 0 || height == 0) {
width = depthAtt.GetSize().x();
height = depthAtt.GetSize().y();
}
}
attachmentDescriptions.emplace_back(depthAttachmentDescription);
depthAttachmentRef.attachment = depthAttachmentIndex;
} }
Array<VkImageView, 2> attachments{}; // Subpass
attachments[0] = targetInfo.colorView; VkSubpassDescription subpassDesc;
Uint32 attachmentCount = 1; subpassDesc.flags = 0;
if (hasDepthStencil) { subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
attachments[1] = targetInfo.depthStencilView; subpassDesc.inputAttachmentCount = 0;
attachmentCount = 2; subpassDesc.pInputAttachments = nullptr;
} subpassDesc.colorAttachmentCount = colorAttachmentRefs.size();
subpassDesc.pColorAttachments = colorAttachmentRefs.data();
subpassDesc.pResolveAttachments = nullptr;
subpassDesc.pDepthStencilAttachment = depthAtt.IsComplete() ? &depthAttachmentRef : VK_NULL_HANDLE;
subpassDesc.preserveAttachmentCount = 0;
subpassDesc.pPreserveAttachments = nullptr;
VkFramebufferCreateInfo framebufferInfo{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; // Render Pass
framebufferInfo.renderPass = target.renderPassLoad; VkRenderPassCreateInfo renderPassCreateInfo;
framebufferInfo.attachmentCount = attachmentCount; renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
framebufferInfo.pAttachments = attachments.data(); renderPassCreateInfo.pNext = VK_NULL_HANDLE;
framebufferInfo.width = targetInfo.extent.width; renderPassCreateInfo.flags = 0;
framebufferInfo.height = targetInfo.extent.height; renderPassCreateInfo.attachmentCount = attachmentDescriptions.size();
framebufferInfo.layers = 1; renderPassCreateInfo.pAttachments = attachmentDescriptions.data();
VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferInfo, nullptr, &target.framebuffer), renderPassCreateInfo.subpassCount = 1;
"vkCreateFramebuffer(offscreen)"); renderPassCreateInfo.pSubpasses = &subpassDesc;
renderPassCreateInfo.dependencyCount = 0;
target.targetVersion = targetInfo.targetVersion; renderPassCreateInfo.pDependencies = nullptr;
target.colorView = targetInfo.colorView;
target.colorFormat = targetInfo.colorFormat;
target.depthStencilView = targetInfo.depthStencilView;
target.depthStencilFormat = targetInfo.depthStencilFormat;
target.extent = targetInfo.extent;
return true;
}
Bool VkRenderPassManager::GetOffscreenRenderTarget(Uint targetExternalIndex, VkRenderPass& outRenderPass,
VkFramebuffer& outFramebuffer, VkExtent2D& outExtent,
VkFormat& outDepthStencilFormat) const {
auto it = m_offscreenRenderTargets.find(targetExternalIndex);
if (it == m_offscreenRenderTargets.end() || it->second.renderPassLoad == VK_NULL_HANDLE ||
it->second.framebuffer == VK_NULL_HANDLE) {
return false;
}
outRenderPass = it->second.renderPassLoad;
outFramebuffer = it->second.framebuffer;
outExtent = it->second.extent;
outDepthStencilFormat = it->second.depthStencilFormat;
return true;
}
void VkRenderPassManager::RemoveOffscreenRenderTarget(Uint targetExternalIndex) {
auto it = m_offscreenRenderTargets.find(targetExternalIndex);
if (it == m_offscreenRenderTargets.end()) {
return;
}
DestroyOffscreenRenderTarget(it->second);
m_offscreenRenderTargets.erase(it);
}
void VkRenderPassManager::BeginRenderPass(VkCommandBuffer commandBuffer, VkRenderPass renderPass,
VkFramebuffer framebuffer, VkExtent2D extent) const {
VkRenderPassBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
beginInfo.renderPass = renderPass;
beginInfo.framebuffer = framebuffer;
beginInfo.renderArea.offset = {0, 0};
beginInfo.renderArea.extent = extent;
beginInfo.clearValueCount = 0;
beginInfo.pClearValues = nullptr;
vkCmdBeginRenderPass(commandBuffer, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);
}
void VkRenderPassManager::EndRenderPass(VkCommandBuffer commandBuffer) const {
vkCmdEndRenderPass(commandBuffer);
}
void VkRenderPassManager::RecordColorClear(VkCommandBuffer commandBuffer, VkExtent2D extent,
const VkClearColorValue& clearColor) const {
VkClearAttachment clearAttachment{};
clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
clearAttachment.colorAttachment = 0;
clearAttachment.clearValue.color = clearColor;
VkClearRect clearRect{};
clearRect.rect.offset = {0, 0};
clearRect.rect.extent = extent;
clearRect.baseArrayLayer = 0;
clearRect.layerCount = 1;
vkCmdClearAttachments(commandBuffer, 1, &clearAttachment, 1, &clearRect);
}
void VkRenderPassManager::RecordDepthStencilClear(VkCommandBuffer commandBuffer, VkExtent2D extent, GLbitfield mask,
Float depth, Uint32 stencil, VkFormat depthStencilFormat) const {
if (depthStencilFormat == VK_FORMAT_UNDEFINED) {
return;
}
VkImageAspectFlags aspectMask = 0;
if ((mask & GL_DEPTH_BUFFER_BIT) != 0) {
aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if ((mask & GL_STENCIL_BUFFER_BIT) != 0 && HasStencilComponent(depthStencilFormat)) {
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
if (aspectMask == 0) {
return;
}
VkClearAttachment clearAttachment{};
clearAttachment.aspectMask = aspectMask;
clearAttachment.clearValue.depthStencil.depth = depth;
clearAttachment.clearValue.depthStencil.stencil = stencil;
VkClearRect clearRect{};
clearRect.rect.offset = {0, 0};
clearRect.rect.extent = extent;
clearRect.baseArrayLayer = 0;
clearRect.layerCount = 1;
vkCmdClearAttachments(commandBuffer, 1, &clearAttachment, 1, &clearRect);
}
VkRenderPass VkRenderPassManager::GetLoadRenderPass() const {
return m_renderPassLoad;
}
VkRenderPass VkRenderPassManager::GetClearRenderPass() const {
return m_renderPassClear;
}
VkRenderPass VkRenderPassManager::CreateDefaultRenderPass(VkAttachmentLoadOp colorLoadOp) const {
MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE, "VkRenderPassManager: device is null");
MOBILEGL_ASSERT(m_colorFormat != VK_FORMAT_UNDEFINED, "VkRenderPassManager: color format is undefined");
MOBILEGL_ASSERT(m_depthStencilFormat != VK_FORMAT_UNDEFINED,
"VkRenderPassManager: depth/stencil format is undefined");
return CreateRenderPass(m_colorFormat, m_depthStencilFormat, colorLoadOp, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
}
VkRenderPass VkRenderPassManager::CreateRenderPass(VkFormat colorFormat, VkFormat depthStencilFormat,
VkAttachmentLoadOp colorLoadOp,
VkImageLayout colorFinalLayout) const {
VkAttachmentDescription color{};
color.format = colorFormat;
color.samples = VK_SAMPLE_COUNT_1_BIT;
color.loadOp = colorLoadOp;
color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
color.finalLayout = colorFinalLayout;
color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
const Bool hasDepthStencil = depthStencilFormat != VK_FORMAT_UNDEFINED;
VkAttachmentDescription depthStencil{};
if (hasDepthStencil) {
depthStencil.format = depthStencilFormat;
depthStencil.samples = VK_SAMPLE_COUNT_1_BIT;
depthStencil.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
depthStencil.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthStencil.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
depthStencil.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthStencil.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthStencil.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
VkAttachmentReference colorRef{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
VkAttachmentReference depthStencilRef{1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL};
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorRef;
if (hasDepthStencil) {
subpass.pDepthStencilAttachment = &depthStencilRef;
}
Array<VkAttachmentDescription, 2> attachments{};
attachments[0] = color;
Uint32 attachmentCount = 1;
if (hasDepthStencil) {
attachments[1] = depthStencil;
attachmentCount = 2;
}
VkRenderPassCreateInfo createInfo{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};
createInfo.attachmentCount = attachmentCount;
createInfo.pAttachments = attachments.data();
createInfo.subpassCount = 1;
createInfo.pSubpasses = &subpass;
VkRenderPass renderPass = VK_NULL_HANDLE; VkRenderPass renderPass = VK_NULL_HANDLE;
VK_VERIFY(vkCreateRenderPass(m_device, &createInfo, nullptr, &renderPass), "vkCreateRenderPass"); VK_VERIFY(vkCreateRenderPass(m_device, &renderPassCreateInfo, nullptr, &renderPass));
return renderPass;
// Framebuffer
VkFramebufferCreateInfo framebufferCreateInfo;
framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferCreateInfo.pNext = nullptr;
framebufferCreateInfo.flags = 0;
framebufferCreateInfo.renderPass = renderPass;
framebufferCreateInfo.attachmentCount = attachmentViews.size();
framebufferCreateInfo.pAttachments = attachmentViews.data();
framebufferCreateInfo.width = width;
framebufferCreateInfo.height = height;
framebufferCreateInfo.layers = 1;
VkFramebuffer framebuffer = VK_NULL_HANDLE;
VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferCreateInfo, nullptr, &framebuffer));
IntVec2 extent = {width, height};
RenderPassEntry renderPassEntry {
hash,
renderPass,
framebuffer,
compatibilityHash,
Move(pendingClearAttachments),
Move(trackedAttachmentLayouts),
static_cast<Uint32>(attachmentViews.size()),
extent,
1 };
auto [insertedIt, _] = m_renderPasses.emplace(hash, Move(renderPassEntry));
return insertedIt->second;
} }
void VkRenderPassManager::DestroyDefaultFramebuffers() { Bool VkRenderPassManager::BeginRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry) {
for (auto framebuffer : m_defaultFramebuffers) { // TODO: Transition all the attachments into proper layout before starting the render pass
if (framebuffer != VK_NULL_HANDLE) { VkRenderPassBeginInfo renderPassBeginInfo;
vkDestroyFramebuffer(m_device, framebuffer, nullptr); renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassBeginInfo.pNext = nullptr;
renderPassBeginInfo.renderPass = renderPassEntry.renderPass;
renderPassBeginInfo.framebuffer = renderPassEntry.framebuffer;
renderPassBeginInfo.renderArea.offset = { 0, 0 };
renderPassBeginInfo.renderArea.extent = {
(Uint32)renderPassEntry.extent.x(), (Uint32)renderPassEntry.extent.y() };
Vector<VkClearValue> clearValues(renderPassEntry.attachmentCount);
for (auto& clearValue: clearValues) {
clearValue.color = {0.0f, 0.0f, 0.0f, 1.0f};
clearValue.depthStencil = {1.0f, 0};
}
for (const auto& pending: renderPassEntry.pendingClearAttachments) {
if (!pending.texture || pending.attachmentIndex >= clearValues.size()) {
continue;
}
ClearAttachmentPayload clearPayload{};
if (!s_clearManager->GetPendingClear(pending.texture, clearPayload)) {
continue;
}
if (clearPayload.attachmentType >= FramebufferAttachmentType::Color0 &&
clearPayload.attachmentType <= FramebufferAttachmentType::Color31) {
clearValues[pending.attachmentIndex].color = {
clearPayload.color.x(),
clearPayload.color.y(),
clearPayload.color.z(),
clearPayload.color.w()
};
} else if (clearPayload.attachmentType == FramebufferAttachmentType::Depth) {
clearValues[pending.attachmentIndex].depthStencil.depth = clearPayload.depth;
} else if (clearPayload.attachmentType == FramebufferAttachmentType::Stencil) {
clearValues[pending.attachmentIndex].depthStencil.stencil = clearPayload.stencil;
} }
} }
m_defaultFramebuffers.clear();
m_defaultExtent = {0, 0}; renderPassBeginInfo.clearValueCount = static_cast<Uint32>(clearValues.size());
renderPassBeginInfo.pClearValues = clearValues.data();
vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
for (const auto& pending: renderPassEntry.pendingClearAttachments) {
s_clearManager->PopPendingClear(pending.texture);
}
s_activeRenderPass.hash = renderPassEntry.hash;
s_activeRenderPass.compatibilityHash = renderPassEntry.compatibilityHash;
s_activeRenderPass.trackedAttachmentLayouts = renderPassEntry.trackedAttachmentLayouts;
s_activeRenderPass.extent = renderPassEntry.extent;
s_hasActiveRenderPass = true;
return true;
} }
void VkRenderPassManager::DestroyOffscreenRenderTarget(OffscreenRenderTarget& target) { Bool VkRenderPassManager::EndRenderPass(VkCommandBuffer commandBuffer) {
if (target.framebuffer != VK_NULL_HANDLE) { auto* activeRenderPass = GetActiveRenderPass();
vkDestroyFramebuffer(m_device, target.framebuffer, nullptr); vkCmdEndRenderPass(commandBuffer);
target.framebuffer = VK_NULL_HANDLE; if (activeRenderPass != nullptr && !activeRenderPass->trackedAttachmentLayouts.empty()) {
for (const auto& trackedAttachment : activeRenderPass->trackedAttachmentLayouts) {
switch (trackedAttachment.target) {
case TrackedAttachmentTarget::Texture:
MOBILEGL_ASSERT(s_textureManager != nullptr, "EndRenderPass: texture manager is null");
s_textureManager->UpdateTrackedImageLayout(trackedAttachment.texture, trackedAttachment.finalLayout);
break;
case TrackedAttachmentTarget::SwapchainColor:
MOBILEGL_ASSERT(s_swapchainObject != nullptr, "EndRenderPass: swapchain object is null");
s_swapchainObject->SetImageLayout(trackedAttachment.swapchainImageIndex, trackedAttachment.finalLayout);
break;
case TrackedAttachmentTarget::SwapchainDepthStencil:
MOBILEGL_ASSERT(s_swapchainObject != nullptr, "EndRenderPass: swapchain object is null");
s_swapchainObject->SetDepthStencilImageLayout(trackedAttachment.swapchainImageIndex,
trackedAttachment.finalLayout);
break;
default:
MOBILEGL_ASSERT(false, "EndRenderPass: unsupported tracked attachment target=%d",
static_cast<Int>(trackedAttachment.target));
break;
}
}
} }
if (target.renderPassLoad != VK_NULL_HANDLE) { s_activeRenderPass = {};
vkDestroyRenderPass(m_device, target.renderPassLoad, nullptr); s_hasActiveRenderPass = false;
target.renderPassLoad = VK_NULL_HANDLE; return true;
}
target.targetVersion = 0;
target.colorView = VK_NULL_HANDLE;
target.colorFormat = VK_FORMAT_UNDEFINED;
target.depthStencilView = VK_NULL_HANDLE;
target.depthStencilFormat = VK_FORMAT_UNDEFINED;
target.extent = {0, 0};
} }
Bool VkRenderPassManager::HasStencilComponent(VkFormat format) { ActiveRenderPassInfo* VkRenderPassManager::GetActiveRenderPass() {
return format == VK_FORMAT_D24_UNORM_S8_UINT || format == VK_FORMAT_D32_SFLOAT_S8_UINT; return s_hasActiveRenderPass ? &s_activeRenderPass : nullptr;
} }
} // namespace MobileGL::MG_Backend::DirectVulkan } // namespace MobileGL::MG_Backend::DirectVulkan
@@ -8,79 +8,144 @@
#pragma once #pragma once
#include "SwapchainObject.h"
#include "VkClearManager.h"
#include "VkTextureManager.h"
#include "../VkIncludes.h" #include "../VkIncludes.h"
#include "../VulkanRendererConfig.h"
#include "MG_State/GLState/FramebufferState/FramebufferObject.h"
#include <Includes.h> #include <Includes.h>
namespace MobileGL::MG_Backend::DirectVulkan { namespace MobileGL::MG_Backend::DirectVulkan {
enum class TrackedAttachmentTarget : Uint8 {
Texture,
SwapchainColor,
SwapchainDepthStencil
};
struct PendingClearAttachmentInfo {
Uint32 attachmentIndex = 0;
MG_State::GLState::ITextureObject* texture = nullptr;
};
struct TrackedAttachmentLayoutInfo {
TrackedAttachmentTarget target = TrackedAttachmentTarget::Texture;
MG_State::GLState::ITextureObject* texture = nullptr;
Uint32 swapchainImageIndex = 0;
VkImageLayout finalLayout = VK_IMAGE_LAYOUT_UNDEFINED;
};
struct RenderPassEntry {
static inline VkDevice s_device;
static inline Vector<VkTextureManager::TextureResource*> s_textureResourcesScratch;
Uint64 hash = 0;
VkRenderPass renderPass = VK_NULL_HANDLE;
VkFramebuffer framebuffer = VK_NULL_HANDLE;
Uint64 compatibilityHash = 0;
Vector<PendingClearAttachmentInfo> pendingClearAttachments;
Vector<TrackedAttachmentLayoutInfo> trackedAttachmentLayouts;
Uint32 attachmentCount = 0;
IntVec2 extent = {0, 0};
Uint32 subpass = 0;
RenderPassEntry() = default;
RenderPassEntry(const RenderPassEntry&) = delete;
RenderPassEntry(RenderPassEntry&& that) noexcept {
std::swap(hash, that.hash);
std::swap(renderPass, that.renderPass);
std::swap(framebuffer, that.framebuffer);
std::swap(compatibilityHash, that.compatibilityHash);
std::swap(pendingClearAttachments, that.pendingClearAttachments);
std::swap(trackedAttachmentLayouts, that.trackedAttachmentLayouts);
std::swap(attachmentCount, that.attachmentCount);
std::swap(extent, that.extent);
std::swap(subpass, that.subpass);
}
RenderPassEntry(
Uint64 hash,
VkRenderPass renderpass,
VkFramebuffer framebuffer,
Uint64 compatibilityHash,
const Vector<PendingClearAttachmentInfo>& pendingClearAttachments,
const Vector<TrackedAttachmentLayoutInfo>& trackedAttachmentLayouts,
Uint32 attachmentCount,
IntVec2 extent, int subpass):
hash(hash),
renderPass(renderpass),
framebuffer(framebuffer),
compatibilityHash(compatibilityHash),
pendingClearAttachments(Move(pendingClearAttachments)),
trackedAttachmentLayouts(Move(trackedAttachmentLayouts)),
attachmentCount(attachmentCount),
extent(extent),
subpass(subpass)
{}
~RenderPassEntry() {
if (renderPass != VK_NULL_HANDLE) {
vkDestroyRenderPass(s_device, renderPass, nullptr);
}
if (framebuffer != VK_NULL_HANDLE) {
vkDestroyFramebuffer(s_device, framebuffer, nullptr);
}
}
Bool CompatibleWith(const RenderPassEntry& that) const {
return this->compatibilityHash == that.compatibilityHash;
}
Bool CompatibleWith(Uint64 compatibilityHash) const {
return this->compatibilityHash == compatibilityHash;
}
};
struct ActiveRenderPassInfo {
Uint64 hash = 0;
Uint64 compatibilityHash = 0;
Vector<TrackedAttachmentLayoutInfo> trackedAttachmentLayouts;
IntVec2 extent = {0, 0};
Bool CompatibleWith(const RenderPassEntry& that) const {
return compatibilityHash == that.compatibilityHash;
}
Bool CompatibleWith(Uint64 thatCompatibilityHash) const {
return compatibilityHash == thatCompatibilityHash;
}
};
class VkRenderPassManager { class VkRenderPassManager {
public: public:
struct InitInfo { using HashType = Uint64;
VkDevice device = VK_NULL_HANDLE; VkRenderPassManager(VkDevice device,
VkFormat colorFormat = VK_FORMAT_UNDEFINED; const VulkanRendererConfig& config, VkClearManager& clearManager, VkTextureManager& textureManager,
VkFormat depthStencilFormat = VK_FORMAT_UNDEFINED; SwapchainObject& swapchainObject);
}; ~VkRenderPassManager();
struct OffscreenRenderTargetInfo { Bool Initialize();
Uint targetExternalIndex = 0;
Uint16 targetVersion = 0;
VkImageView colorView = VK_NULL_HANDLE;
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
VkImageView depthStencilView = VK_NULL_HANDLE;
VkFormat depthStencilFormat = VK_FORMAT_UNDEFINED;
VkExtent2D extent = {0, 0};
};
Bool Initialize(const InitInfo& initInfo);
void Shutdown(); void Shutdown();
Bool RecreateDefaultFramebuffers(const Vector<VkImageView>& colorViews, HashType ComputeHash(
const Vector<VkImageView>& depthStencilViews, VkExtent2D extent); const MG_State::GLState::FramebufferObject& fbo,
Bool GetDefaultRenderTarget(Uint32 imageIndex, VkRenderPass& outRenderPass, VkFramebuffer& outFramebuffer, Uint32 swapchainImageIndex,
VkExtent2D& outExtent, VkFormat& outDepthStencilFormat) const; Bool includePendingClear = true) const;
RenderPassEntry& GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex);
Bool EnsureOffscreenRenderTarget(const OffscreenRenderTargetInfo& targetInfo); static Bool BeginRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry);
Bool GetOffscreenRenderTarget(Uint targetExternalIndex, VkRenderPass& outRenderPass, static Bool EndRenderPass(VkCommandBuffer commandBuffer);
VkFramebuffer& outFramebuffer, VkExtent2D& outExtent, static ActiveRenderPassInfo* GetActiveRenderPass();
VkFormat& outDepthStencilFormat) const;
void RemoveOffscreenRenderTarget(Uint targetExternalIndex);
void BeginRenderPass(VkCommandBuffer commandBuffer, VkRenderPass renderPass, VkFramebuffer framebuffer,
VkExtent2D extent) const;
void EndRenderPass(VkCommandBuffer commandBuffer) const;
void RecordColorClear(VkCommandBuffer commandBuffer, VkExtent2D extent,
const VkClearColorValue& clearColor) const;
void RecordDepthStencilClear(VkCommandBuffer commandBuffer, VkExtent2D extent, GLbitfield mask, Float depth,
Uint32 stencil, VkFormat depthStencilFormat) const;
VkRenderPass GetLoadRenderPass() const;
VkRenderPass GetClearRenderPass() const;
private: private:
struct OffscreenRenderTarget {
Uint16 targetVersion = 0;
VkImageView colorView = VK_NULL_HANDLE;
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
VkImageView depthStencilView = VK_NULL_HANDLE;
VkFormat depthStencilFormat = VK_FORMAT_UNDEFINED;
VkExtent2D extent = {0, 0};
VkRenderPass renderPassLoad = VK_NULL_HANDLE;
VkFramebuffer framebuffer = VK_NULL_HANDLE;
};
VkRenderPass CreateDefaultRenderPass(VkAttachmentLoadOp colorLoadOp) const;
VkRenderPass CreateRenderPass(VkFormat colorFormat, VkFormat depthStencilFormat, VkAttachmentLoadOp colorLoadOp,
VkImageLayout colorFinalLayout) const;
void DestroyDefaultFramebuffers();
void DestroyOffscreenRenderTarget(OffscreenRenderTarget& target);
static Bool HasStencilComponent(VkFormat format);
VkDevice m_device = VK_NULL_HANDLE; VkDevice m_device = VK_NULL_HANDLE;
VkFormat m_colorFormat = VK_FORMAT_UNDEFINED; const VulkanRendererConfig& m_config;
VkFormat m_depthStencilFormat = VK_FORMAT_UNDEFINED; VkClearManager& m_clearManager;
VkRenderPass m_renderPassLoad = VK_NULL_HANDLE; VkTextureManager& m_textureManager;
VkRenderPass m_renderPassClear = VK_NULL_HANDLE; SwapchainObject& m_swapchainObject;
Vector<VkFramebuffer> m_defaultFramebuffers; UnorderedMap<Uint64, RenderPassEntry> m_renderPasses;
VkExtent2D m_defaultExtent = {0, 0}; static inline XXH64_state_t* m_hashState = XXH64_createState();
UnorderedMap<Uint, OffscreenRenderTarget> m_offscreenRenderTargets; static inline ActiveRenderPassInfo s_activeRenderPass{};
static inline Bool s_hasActiveRenderPass = false;
static inline VkClearManager* s_clearManager = nullptr;
static inline VkTextureManager* s_textureManager = nullptr;
static inline SwapchainObject* s_swapchainObject = nullptr;
}; };
} // namespace MobileGL::MG_Backend::DirectVulkan } // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,156 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.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 "VkSamplerManager.h"
#include "MG_State/GLState/Core.h"
namespace MobileGL::MG_Backend::DirectVulkan {
Bool VkSamplerManager::Initialize(const InitInfo& initInfo) {
Shutdown();
m_device = initInfo.device;
m_config = initInfo.config;
MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE && m_config != nullptr,
"VkSamplerManager::Initialize failed: invalid initialization info");
return true;
}
void VkSamplerManager::Shutdown() {
for (auto& [_, sampler] : m_samplers) {
if (m_device != VK_NULL_HANDLE && sampler.handle != VK_NULL_HANDLE) {
vkDestroySampler(m_device, sampler.handle, nullptr);
}
sampler.handle = VK_NULL_HANDLE;
}
m_samplers.clear();
m_device = VK_NULL_HANDLE;
m_config = nullptr;
}
Uint64 VkSamplerManager::BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler) const {
MOBILEGL_ASSERT(m_config != nullptr, "VkSamplerManager::BuildSamplerKey: m_config is null");
XXHASH_VERIFY(XXH64_reset(m_hashState, m_config->CacheVersion));
const auto minFilter = sampler.GetMinFilter();
XXHASH_VERIFY(XXH64_update(m_hashState, &minFilter, sizeof(minFilter)));
const auto magFilter = sampler.GetMagFilter();
XXHASH_VERIFY(XXH64_update(m_hashState, &magFilter, sizeof(magFilter)));
const auto mipmapMode = sampler.GetMipmapMode();
XXHASH_VERIFY(XXH64_update(m_hashState, &mipmapMode, sizeof(mipmapMode)));
const auto wrapS = sampler.GetWrapS();
XXHASH_VERIFY(XXH64_update(m_hashState, &wrapS, sizeof(wrapS)));
const auto wrapT = sampler.GetWrapT();
XXHASH_VERIFY(XXH64_update(m_hashState, &wrapT, sizeof(wrapT)));
const auto wrapR = sampler.GetWrapR();
XXHASH_VERIFY(XXH64_update(m_hashState, &wrapR, sizeof(wrapR)));
const auto minLod = sampler.GetMinLod();
XXHASH_VERIFY(XXH64_update(m_hashState, &minLod, sizeof(minLod)));
const auto maxLod = sampler.GetMaxLod();
XXHASH_VERIFY(XXH64_update(m_hashState, &maxLod, sizeof(maxLod)));
const auto lodBias = sampler.GetLodBias();
XXHASH_VERIFY(XXH64_update(m_hashState, &lodBias, sizeof(lodBias)));
const auto compareMode = sampler.GetCompareMode();
XXHASH_VERIFY(XXH64_update(m_hashState, &compareMode, sizeof(compareMode)));
const auto compareFunc = sampler.GetSamplerCompareFunc();
XXHASH_VERIFY(XXH64_update(m_hashState, &compareFunc, sizeof(compareFunc)));
return XXH64_digest(m_hashState);
}
VkSampler VkSamplerManager::GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler) {
const Uint64 key = BuildSamplerKey(sampler);
auto it = m_samplers.find(key);
if (it != m_samplers.end()) {
return it->second.handle;
}
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = ToVkFilter(sampler.GetMagFilter());
samplerInfo.minFilter = ToVkFilter(sampler.GetMinFilter());
samplerInfo.mipmapMode = ToVkMipmapMode(sampler.GetMipmapMode());
samplerInfo.addressModeU = ToVkAddressMode(sampler.GetWrapS());
samplerInfo.addressModeV = ToVkAddressMode(sampler.GetWrapT());
samplerInfo.addressModeW = ToVkAddressMode(sampler.GetWrapR());
samplerInfo.mipLodBias = sampler.GetLodBias();
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.compareEnable = sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture ? VK_TRUE : VK_FALSE;
samplerInfo.compareOp = ToVkCompareOp(sampler.GetSamplerCompareFunc());
samplerInfo.minLod = sampler.GetMinLod();
samplerInfo.maxLod = sampler.GetMaxLod();
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
VkSampler vkSampler = VK_NULL_HANDLE;
VK_VERIFY(vkCreateSampler(m_device, &samplerInfo, nullptr, &vkSampler), "vkCreateSampler(texture)");
SamplerCacheEntry entry{};
entry.handle = vkSampler;
entry.externalIndex = sampler.GetExternalIndex();
entry.version = sampler.GetVersion();
m_samplers[key] = entry;
return vkSampler;
}
VkFilter VkSamplerManager::ToVkFilter(SamplerFilterMode mode) {
return mode == SamplerFilterMode::Nearest ? VK_FILTER_NEAREST : VK_FILTER_LINEAR;
}
VkSamplerMipmapMode VkSamplerManager::ToVkMipmapMode(SamplerMipmapMode mode) {
switch (mode) {
case SamplerMipmapMode::Nearest:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMipmapMode::Linear:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
case SamplerMipmapMode::None:
default:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
}
}
VkSamplerAddressMode VkSamplerManager::ToVkAddressMode(SamplerWrapMode mode) {
switch (mode) {
case SamplerWrapMode::ClampToEdge:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case SamplerWrapMode::MirroredRepeat:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
case SamplerWrapMode::Repeat:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case SamplerWrapMode::ClampToBorder:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
case SamplerWrapMode::MirrorClampToEdge:
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
default:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
}
}
VkCompareOp VkSamplerManager::ToVkCompareOp(SamplerCompareFunc func) {
switch (func) {
case SamplerCompareFunc::Never:
return VK_COMPARE_OP_NEVER;
case SamplerCompareFunc::Less:
return VK_COMPARE_OP_LESS;
case SamplerCompareFunc::Equal:
return VK_COMPARE_OP_EQUAL;
case SamplerCompareFunc::LessEqual:
return VK_COMPARE_OP_LESS_OR_EQUAL;
case SamplerCompareFunc::Greater:
return VK_COMPARE_OP_GREATER;
case SamplerCompareFunc::NotEqual:
return VK_COMPARE_OP_NOT_EQUAL;
case SamplerCompareFunc::GreaterEqual:
return VK_COMPARE_OP_GREATER_OR_EQUAL;
case SamplerCompareFunc::Always:
default:
return VK_COMPARE_OP_ALWAYS;
}
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,51 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkSamplerManager.h
// 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
#pragma once
#include "../VkIncludes.h"
#include "../VulkanRendererConfig.h"
#include <Includes.h>
#include <MG_State/GLState/SamplerState/SamplerObject.h>
namespace MobileGL::MG_State::GLState {
class SamplerObject;
}
namespace MobileGL::MG_Backend::DirectVulkan {
class VkSamplerManager {
public:
struct InitInfo {
VkDevice device = VK_NULL_HANDLE;
const VulkanRendererConfig* config = nullptr;
};
Bool Initialize(const InitInfo& initInfo);
void Shutdown();
VkSampler GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler);
private:
struct SamplerCacheEntry {
VkSampler handle = VK_NULL_HANDLE;
Uint externalIndex = 0;
Uint16 version = 0;
};
Uint64 BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler) const;
static VkFilter ToVkFilter(SamplerFilterMode mode);
static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode);
static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode);
static VkCompareOp ToVkCompareOp(SamplerCompareFunc func);
VkDevice m_device = VK_NULL_HANDLE;
const VulkanRendererConfig* m_config = nullptr;
UnorderedMap<Uint64, SamplerCacheEntry> m_samplers;
static inline XXH64_state_t* m_hashState = XXH64_createState();
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,617 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.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 "VkTextureManager.h"
#include "MG_State/GLState/Core.h"
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
namespace MobileGL::MG_Backend::DirectVulkan {
static constexpr VkPipelineStageFlags kGraphicsSampledReadStages = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
static Bool IsValidSampledImageLayout(VkImageLayout layout) {
switch (layout) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_GENERAL:
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:
return true;
default:
return false;
}
}
Bool VkTextureManager::Initialize(const InitInfo& initInfo) {
Shutdown();
m_device = initInfo.device;
m_physicalDevice = initInfo.physicalDevice;
m_allocator = initInfo.allocator;
m_commandPool = initInfo.commandPool;
m_graphicsQueue = initInfo.graphicsQueue;
MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE && m_physicalDevice != VK_NULL_HANDLE && m_allocator != nullptr &&
m_commandPool != VK_NULL_HANDLE && m_graphicsQueue != VK_NULL_HANDLE,
"VkTextureManager::Initialize failed: invalid initialization info");
TextureResource::s_device = m_device;
TextureResource::s_allocator = m_allocator;
return true;
}
void VkTextureManager::Shutdown() {
m_textureResources.clear();
m_device = VK_NULL_HANDLE;
m_physicalDevice = VK_NULL_HANDLE;
m_allocator = nullptr;
m_commandPool = VK_NULL_HANDLE;
m_graphicsQueue = VK_NULL_HANDLE;
}
VkTextureManager::TextureResource* VkTextureManager::SyncTextureAndGetDescriptor(MG_State::GLState::ITextureObject& texture) {
MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE, "SyncTextureAndGetDescriptor: m_device == VK_NULL_HANDLE");
auto it = m_textureResources.find(&texture);
if (it == m_textureResources.end()) {
TextureResource initial{};
auto [insertIt, _] = m_textureResources.emplace(&texture, Move(initial));
it = insertIt;
}
if (!SyncTexture(texture, it->second)) {
MGLOG_D("%s: Syncing texture %d failed", __func__, texture.GetExternalIndex());
return nullptr;
}
return &(it->second);
}
VkImageView VkTextureManager::GetOrCreateViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel) {
TextureResource* resource = SyncTextureAndGetDescriptor(texture);
if (resource == nullptr || resource->image == VK_NULL_HANDLE || mipLevel >= resource->mipLevels) {
return VK_NULL_HANDLE;
}
if (resource->perMipViews.size() != resource->mipLevels) {
resource->perMipViews.resize(resource->mipLevels, VK_NULL_HANDLE);
}
VkImageView& perMipView = resource->perMipViews[mipLevel];
if (perMipView != VK_NULL_HANDLE) {
return perMipView;
}
perMipView = CreateImageView(resource->image, resource->format, resource->aspect, mipLevel, 1);
if (perMipView == VK_NULL_HANDLE) {
MGLOG_D("%s: CreateImageView failed for textureId=%d mipLevel=%u", __func__, texture.GetExternalIndex(), mipLevel);
return VK_NULL_HANDLE;
}
return perMipView;
}
void VkTextureManager::UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout) {
MOBILEGL_ASSERT(texture != nullptr, "UpdateTrackedImageLayout: texture is null");
auto it = m_textureResources.find(texture);
MOBILEGL_ASSERT(it != m_textureResources.end(),
"UpdateTrackedImageLayout: textureId=%d has no tracked resource", texture->GetExternalIndex());
MOBILEGL_ASSERT(it->second.image != VK_NULL_HANDLE,
"UpdateTrackedImageLayout: textureId=%d has null image", texture->GetExternalIndex());
it->second.layout = newLayout;
}
Bool VkTextureManager::TransitionTextureForSampling(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture) {
TextureResource* resource = SyncTextureAndGetDescriptor(texture);
if (resource == nullptr) {
return false;
}
if (IsValidSampledImageLayout(resource->layout)) {
return true;
}
if (resource->layout == VK_IMAGE_LAYOUT_UNDEFINED) {
MGLOG_W("TransitionTextureForSampling: textureId=%d is still in VK_IMAGE_LAYOUT_UNDEFINED before sampling",
texture.GetExternalIndex());
}
VkImageLayout targetLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags srcAccessMask = 0;
if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) {
MOBILEGL_ASSERT(resource->layout == VK_IMAGE_LAYOUT_UNDEFINED ||
resource->layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
"TransitionTextureForSampling: unsupported color layout=%d for textureId=%d",
static_cast<Int>(resource->layout), texture.GetExternalIndex());
if (resource->layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
}
targetLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
} else if ((resource->aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
MOBILEGL_ASSERT(resource->layout == VK_IMAGE_LAYOUT_UNDEFINED ||
resource->layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
"TransitionTextureForSampling: unsupported depth/stencil layout=%d for textureId=%d",
static_cast<Int>(resource->layout), texture.GetExternalIndex());
if (resource->layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
}
targetLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
} else {
MOBILEGL_ASSERT(false, "TransitionTextureForSampling: unsupported aspect mask=0x%x for textureId=%d",
static_cast<Uint32>(resource->aspect), texture.GetExternalIndex());
}
const Bool ok = TransitionImageLayout(commandBuffer, resource->image, resource->layout, targetLayout, srcStageMask,
kGraphicsSampledReadStages, srcAccessMask,
VK_ACCESS_SHADER_READ_BIT, resource->aspect, 0, resource->mipLevels);
MOBILEGL_ASSERT(ok, "TransitionTextureForSampling: transition failed for textureId=%d", texture.GetExternalIndex());
return ok;
}
Bool VkTextureManager::TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image,
VkImageLayout& trackedLayout, VkImageLayout newLayout,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask,
VkImageAspectFlags aspectMask, Uint32 baseMipLevel, Uint32 levelCount) {
MOBILEGL_ASSERT(image != VK_NULL_HANDLE, "TransitionImageLayout: m_image == VK_NULL_HANDLE");
MOBILEGL_ASSERT(!((dstAccessMask & VK_ACCESS_TRANSFER_READ_BIT) != 0 &&
(dstStageMask & VK_PIPELINE_STAGE_TRANSFER_BIT) == 0),
"TransitionImageLayout: invalid dstAccess/dstStage pair (dstAccess=0x%x, dstStage=0x%x, oldLayout=%d, newLayout=%d)",
static_cast<Uint32>(dstAccessMask), static_cast<Uint32>(dstStageMask), static_cast<Int>(trackedLayout),
static_cast<Int>(newLayout));
if (trackedLayout == newLayout) {
return true;
}
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = srcAccessMask;
barrier.dstAccessMask = dstAccessMask;
barrier.oldLayout = trackedLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = aspectMask;
barrier.subresourceRange.baseMipLevel = baseMipLevel;
barrier.subresourceRange.levelCount = levelCount;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier);
trackedLayout = newLayout;
return true;
}
SizeT VkTextureManager::CollectGarbage() {
m_gcCounter++;
if (m_gcCounter != 0) {
return 0;
}
SizeT count = 0;
for (const auto& [raw, weak]: m_aliveObjects) {
if (weak.expired()) {
count++;
m_textureResources.erase(raw);
m_aliveObjects.erase(raw);
}
}
return count;
}
Bool VkTextureManager::SyncTexture(MG_State::GLState::ITextureObject &texture,
TextureResource &outResource) {
TextureUploadTarget uploadTarget = TextureUploadTarget::Unknown;
IntVec3 texelSize{0, 0, 0};
SizeT byteSize = 0;
Uint32 mipLevelCount = 0;
if (!CheckMipmapCompleteness(texture, uploadTarget, texelSize, byteSize, mipLevelCount)) {
MGLOG_D("%s: mipmap not complete", __func__);
return false;
}
auto* mipTexture = dynamic_cast<MG_State::GLState::TextureObjectMipmap*>(&texture);
if (!mipTexture) {
MGLOG_D("%s: not TextureObjectMipmap", __func__);
return false;
}
if (!SyncTextureResource(texture, uploadTarget, texelSize, byteSize, mipLevelCount, outResource)) {
MGLOG_D("%s: SyncTextureResource failed", __func__);
return false;
}
if (!SyncTextureViews(texture, outResource)) {
MGLOG_D("%s: SyncTextureViews failed", __func__);
return false;
}
Bool hasDirtyMipLevel = false;
for (Uint32 level = 0; level < mipLevelCount; ++level) {
if (mipTexture->IsStorageDirty(uploadTarget, level)) {
hasDirtyMipLevel = true;
break;
}
}
if (!hasDirtyMipLevel) {
return true;
}
if (!UploadDirtyMipLevels(*mipTexture, uploadTarget, outResource)) {
MGLOG_D("%s: UploadDirtyMipLevels failed", __func__);
return false;
}
return true;
}
Bool VkTextureManager::SyncTextureResource(const MG_State::GLState::ITextureObject &texture,
TextureUploadTarget uploadTarget,
const IntVec3 &texelSize, SizeT byteSize, Uint32 mipLevels,
TextureResource &resource) {
const VkFormat format = MG_Util::ConvertTextureInternalFormatToVkEnum(texture.GetFormat());
if (format == VK_FORMAT_UNDEFINED) {
MGLOG_D("%s: format == VK_FORMAT_UNDEFINED", __func__);
return false;
}
if (texelSize.x() <= 0 || texelSize.y() <= 0 /*|| byteSize == 0*/) {
MGLOG_D("%s: texelSize or byteSize is zero", __func__);
return false;
}
if (mipLevels == 0) {
MGLOG_D("%s: no mip levels", __func__);
return false;
}
if (uploadTarget != TextureUploadTarget::Texture2D) {
MGLOG_D("%s: not Texture2D, unsupported", __func__);
return false;
}
const Bool compatible = resource.image != VK_NULL_HANDLE && resource.format == format &&
resource.extent.width == static_cast<Uint32>(texelSize.x()) &&
resource.extent.height == static_cast<Uint32>(texelSize.y()) &&
resource.mipLevels == mipLevels;
if (compatible) {
if (resource.perMipViews.size() != mipLevels) {
resource.perMipViews.resize(mipLevels, VK_NULL_HANDLE);
}
return true;
}
resource.Reset();
auto aspect = GetAspectMaskForFormat(format);
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = static_cast<Uint32>(texelSize.x());
imageInfo.extent.height = static_cast<Uint32>(texelSize.y());
imageInfo.extent.depth = 1;
imageInfo.mipLevels = mipLevels;
imageInfo.arrayLayers = 1;
imageInfo.format = format;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage =
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
((aspect & VK_IMAGE_ASPECT_COLOR_BIT) ?
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : 0) |
((aspect & VK_IMAGE_ASPECT_DEPTH_BIT || aspect & VK_IMAGE_ASPECT_STENCIL_BIT) ?
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : 0);
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo allocationInfo{};
allocationInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
allocationInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
VK_VERIFY(vmaCreateImage(m_allocator, &imageInfo, &allocationInfo, &resource.image, &resource.allocation, nullptr),
"vmaCreateImage(texture)");
resource.layout = VK_IMAGE_LAYOUT_UNDEFINED;
resource.extent = {static_cast<Uint32>(texelSize.x()), static_cast<Uint32>(texelSize.y())};
resource.mipLevels = mipLevels;
resource.perMipViews.assign(mipLevels, VK_NULL_HANDLE);
resource.sampledBaseMipLevel = 0;
resource.sampledLevelCount = mipLevels;
resource.format = format;
resource.aspect = aspect;
resource.syncedTextureParamsVersion = 0;
return true;
}
Bool VkTextureManager::SyncTextureViews(const MG_State::GLState::ITextureObject& texture, TextureResource& resource) {
MOBILEGL_ASSERT(resource.image != VK_NULL_HANDLE, "SyncTextureViews: image == VK_NULL_HANDLE");
Uint32 baseMipLevel = 0;
Uint32 levelCount = 1;
ResolveViewMipRange(texture, resource.mipLevels, baseMipLevel, levelCount);
const Bool needsRecreate =
resource.fullView == VK_NULL_HANDLE ||
resource.sampledBaseMipLevel != baseMipLevel ||
resource.sampledLevelCount != levelCount ||
resource.syncedTextureParamsVersion != texture.GetTextureParamsVersion();
if (!needsRecreate) {
return true;
}
if (resource.fullView != VK_NULL_HANDLE) {
vkDestroyImageView(m_device, resource.fullView, nullptr);
resource.fullView = VK_NULL_HANDLE;
}
resource.fullView = CreateImageView(resource.image, resource.format, resource.aspect, baseMipLevel, levelCount);
if (resource.fullView == VK_NULL_HANDLE) {
return false;
}
resource.sampledBaseMipLevel = baseMipLevel;
resource.sampledLevelCount = levelCount;
resource.syncedTextureParamsVersion = texture.GetTextureParamsVersion();
return true;
}
VkImageView VkTextureManager::CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspect,
Uint32 baseMipLevel, Uint32 levelCount) const {
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = format;
viewInfo.subresourceRange.aspectMask = aspect;
viewInfo.subresourceRange.baseMipLevel = baseMipLevel;
viewInfo.subresourceRange.levelCount = levelCount;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VkImageView view = VK_NULL_HANDLE;
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &view), "vkCreateImageView(texture)");
return view;
}
Bool VkTextureManager::UploadDirtyMipLevels(MG_State::GLState::TextureObjectMipmap &mipmapTexture,
TextureUploadTarget uploadTarget,
TextureResource &outResource) {
struct UploadItem {
Uint32 level = 0;
SizeT byteSize = 0;
IntVec3 texelSize = {0, 0, 0};
const void* source = nullptr;
VkDeviceSize offset = 0;
};
Vector<UploadItem> uploadItems;
uploadItems.reserve(outResource.mipLevels);
VkDeviceSize stagingSize = 0;
for (Uint32 level = 0; level < outResource.mipLevels; ++level) {
if (!mipmapTexture.IsStorageDirty(uploadTarget, level)) {
continue;
}
const auto texelSize = mipmapTexture.GetMipmapTexelSize(uploadTarget, level);
const auto byteSize = mipmapTexture.GetMipmapByteSize(uploadTarget, level);
if (texelSize.x() <= 0 || texelSize.y() <= 0 || byteSize == 0) {
mipmapTexture.MarkStorageDirty(uploadTarget, level, false);
continue;
}
const void* source = mipmapTexture.MapMipmapData(uploadTarget, level);
if (source == nullptr) {
MGLOG_D("%s: MapmipmapData failed at level %d", __func__, level);
return false;
}
uploadItems.push_back({level, byteSize, texelSize, source, stagingSize});
stagingSize += static_cast<VkDeviceSize>(byteSize);
}
if (uploadItems.empty()) {
return true;
}
VkBuffer stagingBuffer = VK_NULL_HANDLE;
VmaAllocation stagingAllocation = nullptr;
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = stagingSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo stagingAllocationInfo{};
stagingAllocationInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
stagingAllocationInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
stagingAllocationInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
VK_VERIFY(vmaCreateBuffer(m_allocator, &bufferInfo, &stagingAllocationInfo, &stagingBuffer, &stagingAllocation, nullptr),
"vmaCreateBuffer(staging texture)");
void* mapped = nullptr;
VK_VERIFY(vmaMapMemory(m_allocator, stagingAllocation, &mapped), "vmaMapMemory(staging texture)");
for (const auto& item : uploadItems) {
std::memcpy(static_cast<Uint8*>(mapped) + item.offset, item.source, item.byteSize);
}
vmaUnmapMemory(m_allocator, stagingAllocation);
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = m_commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
VK_VERIFY(vkAllocateCommandBuffers(m_device, &allocInfo, &commandBuffer), "vkAllocateCommandBuffers(texture)");
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo), "vkBeginCommandBuffer(texture)");
const VkImageAspectFlags aspectMask = GetAspectMaskForFormat(outResource.format);
Bool ok = TransitionImageLayout(commandBuffer, outResource.image,
outResource.layout,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
outResource.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ?
kGraphicsSampledReadStages :
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
outResource.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ? VK_ACCESS_SHADER_READ_BIT : 0,
VK_ACCESS_TRANSFER_WRITE_BIT,
aspectMask, 0, outResource.mipLevels);
MOBILEGL_ASSERT(ok, "TransitionImageLayout to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL failed");
for (const auto& item : uploadItems) {
VkBufferImageCopy copy{};
copy.bufferOffset = item.offset;
copy.bufferRowLength = 0;
copy.bufferImageHeight = 0;
copy.imageSubresource.aspectMask = aspectMask;
copy.imageSubresource.mipLevel = item.level;
copy.imageSubresource.baseArrayLayer = 0;
copy.imageSubresource.layerCount = 1;
copy.imageOffset = {0, 0, 0};
copy.imageExtent = {static_cast<Uint32>(item.texelSize.x()), static_cast<Uint32>(item.texelSize.y()), 1};
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, outResource.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &copy);
}
ok = TransitionImageLayout(commandBuffer, outResource.image,
outResource.layout,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
kGraphicsSampledReadStages,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT,
aspectMask, 0, outResource.mipLevels);
MOBILEGL_ASSERT(ok, "TransitionImageLayout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL failed");
outResource.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VK_VERIFY(vkEndCommandBuffer(commandBuffer), "vkEndCommandBuffer(texture)");
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
VkFenceCreateInfo fenceInfo{};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
VkFence uploadFence = VK_NULL_HANDLE;
VK_VERIFY(vkCreateFence(m_device, &fenceInfo, nullptr, &uploadFence), "vkCreateFence(texture upload)");
VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, uploadFence), "vkQueueSubmit(texture)");
VK_VERIFY(vkWaitForFences(m_device, 1, &uploadFence, VK_TRUE, UINT64_MAX), "vkWaitForFences(texture upload)");
vkDestroyFence(m_device, uploadFence, nullptr);
vkFreeCommandBuffers(m_device, m_commandPool, 1, &commandBuffer);
vmaDestroyBuffer(m_allocator, stagingBuffer, stagingAllocation);
if (!ok) {
MGLOG_D("%s: texture upload cmd failed", __func__);
return false;
}
for (const auto& item : uploadItems) {
mipmapTexture.MarkStorageDirty(uploadTarget, item.level, false);
}
outResource.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
return true;
}
Bool VkTextureManager::CheckMipmapCompleteness(const MG_State::GLState::ITextureObject& texture,
TextureUploadTarget& outTarget,
IntVec3& outTexelSize,
SizeT& outByteSize,
Uint32& outMipLevelCount) {
const auto* mipTexture = dynamic_cast<const MG_State::GLState::TextureObjectMipmap*>(&texture);
if (!mipTexture) {
MGLOG_D("%s: not TextureObjectMipmap", __func__);
return false;
}
const auto& targets = texture.GetUploadTargets();
if (targets.empty()) {
MGLOG_D("%s: upload target empty", __func__);
return false;
}
for (const auto target : targets) {
const Uint32 mipLevelCount = GetUploadMipLevelCount(*mipTexture, target);
if (mipLevelCount == 0) {
MGLOG_D("%s: mipLevelCount == 0", __func__);
continue;
}
// Backing VkImage allocation still uses storage mip 0 as the physical image extent.
// GL_TEXTURE_BASE_LEVEL / MAX_LEVEL are applied later when building the sampled view.
const auto storageBaseTexelSize = mipTexture->GetMipmapTexelSize(target, 0);
const auto storageBaseByteSize = mipTexture->GetMipmapByteSize(target, 0);
if (storageBaseTexelSize.x() <= 0 || storageBaseTexelSize.y() <= 0 /*|| storageBaseByteSize == 0*/) {
continue;
}
outTarget = target;
outTexelSize = storageBaseTexelSize;
outByteSize = storageBaseByteSize;
outMipLevelCount = mipLevelCount;
return true;
}
MGLOG_D("%s: no valid target or mipmap", __func__);
return false;
}
Uint32 VkTextureManager::GetUploadMipLevelCount(const MG_State::GLState::TextureObjectMipmap& texture,
TextureUploadTarget target) {
const Uint totalLevelCount = texture.GetMipmapLevelCount();
if (totalLevelCount == 0) {
return 0;
}
Uint32 validLevelCount = 0;
for (Uint level = 0; level < totalLevelCount; ++level) {
const auto size = texture.GetMipmapTexelSize(target, level);
const auto byteSize = texture.GetMipmapByteSize(target, level);
if (size.x() <= 0 || size.y() <= 0 /*|| byteSize == 0*/) {
break;
}
++validLevelCount;
}
return validLevelCount;
}
void VkTextureManager::ResolveViewMipRange(const MG_State::GLState::ITextureObject& texture, Uint32 mipLevels,
Uint32& outBaseMipLevel, Uint32& outLevelCount) {
MOBILEGL_ASSERT(mipLevels > 0, "ResolveViewMipRange: mipLevels must be > 0");
const auto& levelRange = texture.GetLevelRange();
const Uint32 maxAvailableMipLevel = mipLevels - 1;
const Uint32 requestedBaseMipLevel = std::min(static_cast<Uint32>(levelRange.x()), maxAvailableMipLevel);
Uint32 requestedMaxMipLevel = std::min(static_cast<Uint32>(levelRange.y()), maxAvailableMipLevel);
if (requestedMaxMipLevel < requestedBaseMipLevel) {
requestedMaxMipLevel = requestedBaseMipLevel;
}
outBaseMipLevel = requestedBaseMipLevel;
outLevelCount = requestedMaxMipLevel - requestedBaseMipLevel + 1;
}
VkImageAspectFlags VkTextureManager::GetAspectMaskForFormat(VkFormat format) {
switch (format) {
case VK_FORMAT_D16_UNORM:
case VK_FORMAT_X8_D24_UNORM_PACK32:
case VK_FORMAT_D32_SFLOAT:
return VK_IMAGE_ASPECT_DEPTH_BIT;
case VK_FORMAT_S8_UINT:
return VK_IMAGE_ASPECT_STENCIL_BIT;
case VK_FORMAT_D16_UNORM_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
default:
return VK_IMAGE_ASPECT_COLOR_BIT;
}
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,146 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h
// 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
#pragma once
#include "../VkIncludes.h"
#include <Includes.h>
#include <MG_State/GLState/TextureState/TextureObject.h>
#include <vk_mem_alloc.h>
namespace MobileGL::MG_State::GLState {
class ITextureObject;
}
namespace MobileGL::MG_Backend::DirectVulkan {
class VkTextureManager {
public:
struct InitInfo {
VkDevice device = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VmaAllocator allocator = nullptr;
VkCommandPool commandPool = VK_NULL_HANDLE;
VkQueue graphicsQueue = VK_NULL_HANDLE;
};
struct TextureResource {
VkImage image = VK_NULL_HANDLE;
VmaAllocation allocation = nullptr;
VkImageView fullView = VK_NULL_HANDLE;
Vector<VkImageView> perMipViews;
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkExtent2D extent = {0, 0};
Uint32 mipLevels = 1;
Uint32 sampledBaseMipLevel = 0;
Uint32 sampledLevelCount = 1;
VkFormat format = VK_FORMAT_UNDEFINED;
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_NONE;
Uint16 syncedTextureParamsVersion = 0;
TextureResource() = default;
TextureResource(const TextureResource&) = delete;
TextureResource(TextureResource&& that) noexcept {
std::swap(this->image, that.image);
std::swap(this->allocation, that.allocation);
std::swap(this->fullView, that.fullView);
std::swap(this->perMipViews, that.perMipViews);
std::swap(this->layout, that.layout);
std::swap(this->extent, that.extent);
std::swap(this->mipLevels, that.mipLevels);
std::swap(this->sampledBaseMipLevel, that.sampledBaseMipLevel);
std::swap(this->sampledLevelCount, that.sampledLevelCount);
std::swap(this->format, that.format);
std::swap(this->aspect, that.aspect);
std::swap(this->syncedTextureParamsVersion, that.syncedTextureParamsVersion);
}
void Reset() {
if (fullView != VK_NULL_HANDLE) {
vkDestroyImageView(s_device, fullView, nullptr);
}
for (const auto attachmentView : perMipViews) {
if (attachmentView != VK_NULL_HANDLE) {
vkDestroyImageView(s_device, attachmentView, nullptr);
}
}
if (image != VK_NULL_HANDLE && allocation != nullptr) {
vmaDestroyImage(s_allocator, image, allocation);
}
fullView = VK_NULL_HANDLE;
perMipViews.clear();
image = VK_NULL_HANDLE;
allocation = nullptr;
layout = VK_IMAGE_LAYOUT_UNDEFINED;
extent = {0, 0};
mipLevels = 1;
sampledBaseMipLevel = 0;
sampledLevelCount = 1;
format = VK_FORMAT_UNDEFINED;
aspect = VK_IMAGE_ASPECT_NONE;
syncedTextureParamsVersion = 0;
}
~TextureResource() {
Reset();
}
static inline VkDevice s_device = VK_NULL_HANDLE;
static inline VmaAllocator s_allocator = VK_NULL_HANDLE;
};
Bool Initialize(const InitInfo& initInfo);
void Shutdown();
TextureResource* SyncTextureAndGetDescriptor(
MG_State::GLState::ITextureObject& texture);
VkImageView GetOrCreateViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel);
void UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout);
Bool TransitionTextureForSampling(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture);
static Bool TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout& trackedLayout,
VkImageLayout newLayout, VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask,
VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask,
Uint32 baseMipLevel = 0, Uint32 levelCount = 1);
SizeT CollectGarbage();
private:
Bool SyncTexture(MG_State::GLState::ITextureObject &texture,
TextureResource &outResource);
Bool SyncTextureResource(const MG_State::GLState::ITextureObject &texture,
TextureUploadTarget uploadTarget,
const IntVec3 &texelSize, SizeT byteSize, Uint32 mipLevels,
TextureResource &resource);
Bool SyncTextureViews(const MG_State::GLState::ITextureObject& texture, TextureResource& resource);
VkImageView CreateImageView(VkImage image, VkFormat format, VkImageAspectFlags aspect,
Uint32 baseMipLevel, Uint32 levelCount) const;
Bool UploadDirtyMipLevels(MG_State::GLState::TextureObjectMipmap &mipmapTexture,
TextureUploadTarget uploadTarget,
TextureResource &outResource);
static Bool CheckMipmapCompleteness(const MG_State::GLState::ITextureObject& texture,
TextureUploadTarget& outTarget,
IntVec3& outTexelSize,
SizeT& outByteSize,
Uint32& outMipLevelCount);
static Uint32 GetUploadMipLevelCount(const MG_State::GLState::TextureObjectMipmap& texture, TextureUploadTarget target);
static void ResolveViewMipRange(const MG_State::GLState::ITextureObject& texture, Uint32 mipLevels,
Uint32& outBaseMipLevel, Uint32& outLevelCount);
static VkImageAspectFlags GetAspectMaskForFormat(VkFormat format);
VkDevice m_device = VK_NULL_HANDLE;
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
VmaAllocator m_allocator = nullptr;
VkCommandPool m_commandPool = VK_NULL_HANDLE;
VkQueue m_graphicsQueue = VK_NULL_HANDLE;
Uint8 m_gcCounter = 0;
UnorderedMap<MG_State::GLState::ITextureObject*, WeakPtr<MG_State::GLState::ITextureObject>> m_aliveObjects;
UnorderedMap<MG_State::GLState::ITextureObject*, TextureResource> m_textureResources;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,644 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.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 "VkTextureSamplerManager.h"
#include "MG_State/GLState/Core.h"
namespace MobileGL::MG_Backend::DirectVulkan {
namespace {
constexpr Uint64 BuildSamplerKey(Uint externalIndex, Uint16 version) {
return (static_cast<Uint64>(externalIndex) << 16) | static_cast<Uint64>(version);
}
} // namespace
Bool VkTextureSamplerManager::Initialize(const InitInfo& initInfo) {
Shutdown();
m_device = initInfo.device;
m_physicalDevice = initInfo.physicalDevice;
m_commandPool = initInfo.commandPool;
m_graphicsQueue = initInfo.graphicsQueue;
if (m_device == VK_NULL_HANDLE || m_physicalDevice == VK_NULL_HANDLE || m_commandPool == VK_NULL_HANDLE ||
m_graphicsQueue == VK_NULL_HANDLE) {
MGLOG_E("VkTextureSamplerManager::Initialize failed: invalid Vulkan handles");
Shutdown();
return false;
}
if (!UploadFallbackTexture()) {
MGLOG_E("VkTextureSamplerManager::Initialize failed: fallback texture creation failed");
Shutdown();
return false;
}
return true;
}
void VkTextureSamplerManager::Shutdown() {
for (auto& [_, resource] : m_textureResources) {
DestroyTextureResource(resource);
}
m_textureResources.clear();
for (auto& [_, sampler] : m_samplers) {
if (m_device != VK_NULL_HANDLE && sampler.handle != VK_NULL_HANDLE) {
vkDestroySampler(m_device, sampler.handle, nullptr);
}
sampler.handle = VK_NULL_HANDLE;
}
m_samplers.clear();
if (m_device != VK_NULL_HANDLE && m_fallbackSampler != VK_NULL_HANDLE) {
vkDestroySampler(m_device, m_fallbackSampler, nullptr);
}
if (m_device != VK_NULL_HANDLE && m_fallbackImageView != VK_NULL_HANDLE) {
vkDestroyImageView(m_device, m_fallbackImageView, nullptr);
}
if (m_device != VK_NULL_HANDLE && m_fallbackImage != VK_NULL_HANDLE) {
vkDestroyImage(m_device, m_fallbackImage, nullptr);
}
if (m_device != VK_NULL_HANDLE && m_fallbackImageMemory != VK_NULL_HANDLE) {
vkFreeMemory(m_device, m_fallbackImageMemory, nullptr);
}
m_fallbackSampler = VK_NULL_HANDLE;
m_fallbackImageView = VK_NULL_HANDLE;
m_fallbackImage = VK_NULL_HANDLE;
m_fallbackImageMemory = VK_NULL_HANDLE;
m_device = VK_NULL_HANDLE;
m_physicalDevice = VK_NULL_HANDLE;
m_commandPool = VK_NULL_HANDLE;
m_graphicsQueue = VK_NULL_HANDLE;
}
Bool VkTextureSamplerManager::GetFallbackDescriptor(VkDescriptorImageInfo& outImageInfo) const {
if (m_fallbackSampler == VK_NULL_HANDLE || m_fallbackImageView == VK_NULL_HANDLE) {
return false;
}
outImageInfo.sampler = m_fallbackSampler;
outImageInfo.imageView = m_fallbackImageView;
outImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
return true;
}
Bool VkTextureSamplerManager::SyncTextureAndGetDescriptor(const MG_State::GLState::ITextureObject& texture,
const MG_State::GLState::SamplerObject* samplerOverride,
VkDescriptorImageInfo& outImageInfo) {
if (m_device == VK_NULL_HANDLE) {
return GetFallbackDescriptor(outImageInfo);
}
auto it = m_textureResources.find(texture.GetExternalIndex());
if (it == m_textureResources.end()) {
TextureResource initial{};
initial.textureExternalIndex = texture.GetExternalIndex();
auto [insertIt, _] = m_textureResources.emplace(texture.GetExternalIndex(), initial);
it = insertIt;
}
if (!EnsureTextureSynced(it->second, texture)) {
return GetFallbackDescriptor(outImageInfo);
}
const MG_State::GLState::SamplerObject* samplerToUse = samplerOverride;
if (!samplerToUse) {
auto textureSampler = texture.GetSamplerObject();
if (textureSampler) {
samplerToUse = textureSampler.get();
}
}
VkSampler sampler = m_fallbackSampler;
if (samplerToUse) {
sampler = GetOrCreateSampler(*samplerToUse);
}
if (sampler == VK_NULL_HANDLE) {
sampler = m_fallbackSampler;
}
if (it->second.view == VK_NULL_HANDLE || sampler == VK_NULL_HANDLE) {
return GetFallbackDescriptor(outImageInfo);
}
outImageInfo.sampler = sampler;
outImageInfo.imageView = it->second.view;
outImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
return true;
}
Bool VkTextureSamplerManager::EnsureTextureSynced(TextureResource& resource,
const MG_State::GLState::ITextureObject& texture) {
TextureUploadTarget level0Target = TextureUploadTarget::Unknown;
IntVec3 texelSize{0, 0, 0};
SizeT byteSize = 0;
if (!ResolveLevel0(texture, level0Target, texelSize, byteSize)) {
return false;
}
if (!EnsureTextureResource(resource, texture, level0Target, texelSize, byteSize)) {
return false;
}
const auto* mipTexture = dynamic_cast<const MG_State::GLState::TextureObjectMipmap*>(&texture);
if (!mipTexture) {
return false;
}
if (!mipTexture->IsStorageDirty(level0Target, 0)) {
return true;
}
if (!UploadLevel0(resource, *mipTexture, level0Target, byteSize)) {
return false;
}
auto& mutableTexture = const_cast<MG_State::GLState::TextureObjectMipmap&>(*mipTexture);
mutableTexture.MarkStorageDirty(level0Target, 0, false);
return true;
}
Bool VkTextureSamplerManager::EnsureTextureResource(TextureResource& resource,
const MG_State::GLState::ITextureObject& texture,
TextureUploadTarget level0Target, const IntVec3& texelSize,
SizeT byteSize) {
const VkFormat format = ResolveTextureFormat(texture.GetFormat());
if (format == VK_FORMAT_UNDEFINED) {
return false;
}
if (texelSize.x() <= 0 || texelSize.y() <= 0 || byteSize == 0) {
return false;
}
if (level0Target != TextureUploadTarget::Texture2D) {
return false;
}
const Bool compatible = resource.image != VK_NULL_HANDLE && resource.format == format &&
resource.extent.width == static_cast<Uint32>(texelSize.x()) &&
resource.extent.height == static_cast<Uint32>(texelSize.y());
if (compatible) {
return true;
}
DestroyTextureResource(resource);
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = static_cast<Uint32>(texelSize.x());
imageInfo.extent.height = static_cast<Uint32>(texelSize.y());
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = format;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_VERIFY(vkCreateImage(m_device, &imageInfo, nullptr, &resource.image), "vkCreateImage(texture)");
VkMemoryRequirements requirements{};
vkGetImageMemoryRequirements(m_device, resource.image, &requirements);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = requirements.size;
allocInfo.memoryTypeIndex = FindMemoryType(requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_VERIFY(vkAllocateMemory(m_device, &allocInfo, nullptr, &resource.memory), "vkAllocateMemory(texture)");
VK_VERIFY(vkBindImageMemory(m_device, resource.image, resource.memory, 0), "vkBindImageMemory(texture)");
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = resource.image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = format;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &resource.view), "vkCreateImageView(texture)");
resource.layout = VK_IMAGE_LAYOUT_UNDEFINED;
resource.extent = {static_cast<Uint32>(texelSize.x()), static_cast<Uint32>(texelSize.y())};
resource.format = format;
resource.textureExternalIndex = texture.GetExternalIndex();
return true;
}
Bool VkTextureSamplerManager::UploadLevel0(TextureResource& resource,
const MG_State::GLState::TextureObjectMipmap& mipmapTexture,
TextureUploadTarget level0Target, SizeT byteSize) {
auto& mutableTexture = const_cast<MG_State::GLState::TextureObjectMipmap&>(mipmapTexture);
const void* source = mutableTexture.MapMipmapData(level0Target, 0);
if (source == nullptr || byteSize == 0) {
return false;
}
VkBuffer stagingBuffer = VK_NULL_HANDLE;
VkDeviceMemory stagingMemory = VK_NULL_HANDLE;
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = byteSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_VERIFY(vkCreateBuffer(m_device, &bufferInfo, nullptr, &stagingBuffer), "vkCreateBuffer(staging texture)");
VkMemoryRequirements requirements{};
vkGetBufferMemoryRequirements(m_device, stagingBuffer, &requirements);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = requirements.size;
allocInfo.memoryTypeIndex =
FindMemoryType(requirements.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
VK_VERIFY(vkAllocateMemory(m_device, &allocInfo, nullptr, &stagingMemory), "vkAllocateMemory(staging texture)");
VK_VERIFY(vkBindBufferMemory(m_device, stagingBuffer, stagingMemory, 0), "vkBindBufferMemory(staging texture)");
void* mapped = nullptr;
VK_VERIFY(vkMapMemory(m_device, stagingMemory, 0, byteSize, 0, &mapped), "vkMapMemory(staging texture)");
std::memcpy(mapped, source, byteSize);
vkUnmapMemory(m_device, stagingMemory);
const Bool ok = ExecuteImmediate([&](VkCommandBuffer commandBuffer) {
VkImageMemoryBarrier toTransferDst{};
toTransferDst.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
toTransferDst.srcAccessMask = 0;
toTransferDst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
toTransferDst.oldLayout = resource.layout;
toTransferDst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
toTransferDst.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toTransferDst.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toTransferDst.image = resource.image;
toTransferDst.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
toTransferDst.subresourceRange.baseMipLevel = 0;
toTransferDst.subresourceRange.levelCount = 1;
toTransferDst.subresourceRange.baseArrayLayer = 0;
toTransferDst.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0,
nullptr, 0, nullptr, 1, &toTransferDst);
VkBufferImageCopy copy{};
copy.bufferOffset = 0;
copy.bufferRowLength = 0;
copy.bufferImageHeight = 0;
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy.imageSubresource.mipLevel = 0;
copy.imageSubresource.baseArrayLayer = 0;
copy.imageSubresource.layerCount = 1;
copy.imageOffset = {0, 0, 0};
copy.imageExtent = {resource.extent.width, resource.extent.height, 1};
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, resource.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
&copy);
VkImageMemoryBarrier toSampled{};
toSampled.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
toSampled.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
toSampled.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
toSampled.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
toSampled.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
toSampled.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toSampled.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toSampled.image = resource.image;
toSampled.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
toSampled.subresourceRange.baseMipLevel = 0;
toSampled.subresourceRange.levelCount = 1;
toSampled.subresourceRange.baseArrayLayer = 0;
toSampled.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
0, nullptr, 0, nullptr, 1, &toSampled);
});
vkDestroyBuffer(m_device, stagingBuffer, nullptr);
vkFreeMemory(m_device, stagingMemory, nullptr);
if (!ok) {
return false;
}
resource.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
return true;
}
Bool VkTextureSamplerManager::ExecuteImmediate(const std::function<void(VkCommandBuffer)>& recorder) const {
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = m_commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
VK_VERIFY(vkAllocateCommandBuffers(m_device, &allocInfo, &commandBuffer), "vkAllocateCommandBuffers(texture)");
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo), "vkBeginCommandBuffer(texture)");
recorder(commandBuffer);
VK_VERIFY(vkEndCommandBuffer(commandBuffer), "vkEndCommandBuffer(texture)");
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE), "vkQueueSubmit(texture)");
VK_VERIFY(vkQueueWaitIdle(m_graphicsQueue), "vkQueueWaitIdle(texture)");
vkFreeCommandBuffers(m_device, m_commandPool, 1, &commandBuffer);
return true;
}
void VkTextureSamplerManager::DestroyTextureResource(TextureResource& resource) const {
if (m_device != VK_NULL_HANDLE && resource.view != VK_NULL_HANDLE) {
vkDestroyImageView(m_device, resource.view, nullptr);
}
if (m_device != VK_NULL_HANDLE && resource.image != VK_NULL_HANDLE) {
vkDestroyImage(m_device, resource.image, nullptr);
}
if (m_device != VK_NULL_HANDLE && resource.memory != VK_NULL_HANDLE) {
vkFreeMemory(m_device, resource.memory, nullptr);
}
resource.view = VK_NULL_HANDLE;
resource.image = VK_NULL_HANDLE;
resource.memory = VK_NULL_HANDLE;
resource.layout = VK_IMAGE_LAYOUT_UNDEFINED;
resource.extent = {0, 0};
resource.format = VK_FORMAT_UNDEFINED;
}
Bool VkTextureSamplerManager::ResolveLevel0(const MG_State::GLState::ITextureObject& texture,
TextureUploadTarget& outTarget, IntVec3& outTexelSize,
SizeT& outByteSize) {
const auto* mipTexture = dynamic_cast<const MG_State::GLState::TextureObjectMipmap*>(&texture);
if (!mipTexture) {
return false;
}
const auto& targets = texture.GetUploadTargets();
if (targets.empty()) {
return false;
}
outTarget = targets.front();
outTexelSize = mipTexture->GetMipmapTexelSize(outTarget, 0);
outByteSize = mipTexture->GetMipmapByteSize(outTarget, 0);
return outTexelSize.x() > 0 && outTexelSize.y() > 0 && outByteSize > 0;
}
VkFormat VkTextureSamplerManager::ResolveTextureFormat(TextureInternalFormat format) {
switch (format) {
case TextureInternalFormat::RGBA:
case TextureInternalFormat::RGBA8:
return VK_FORMAT_R8G8B8A8_UNORM;
case TextureInternalFormat::SRGB8Alpha8:
return VK_FORMAT_R8G8B8A8_SRGB;
default:
return VK_FORMAT_UNDEFINED;
}
}
Uint32 VkTextureSamplerManager::FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const {
VkPhysicalDeviceMemoryProperties memProperties{};
vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties);
for (Uint32 i = 0; i < memProperties.memoryTypeCount; ++i) {
if ((typeFilter & (1u << i)) != 0 &&
(memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
return i;
}
}
MOBILEGL_ASSERT(false, "VkTextureSamplerManager::FindMemoryType failed");
return 0;
}
VkSampler VkTextureSamplerManager::GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler) {
const Uint64 key = BuildSamplerKey(sampler.GetExternalIndex(), sampler.GetVersion());
auto it = m_samplers.find(key);
if (it != m_samplers.end()) {
return it->second.handle;
}
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = ToVkFilter(sampler.GetMagFilter());
samplerInfo.minFilter = ToVkFilter(sampler.GetMinFilter());
samplerInfo.mipmapMode = ToVkMipmapMode(sampler.GetMipmapMode());
samplerInfo.addressModeU = ToVkAddressMode(sampler.GetWrapS());
samplerInfo.addressModeV = ToVkAddressMode(sampler.GetWrapT());
samplerInfo.addressModeW = ToVkAddressMode(sampler.GetWrapR());
samplerInfo.mipLodBias = sampler.GetLodBias();
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.compareEnable = sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture ? VK_TRUE : VK_FALSE;
samplerInfo.compareOp = ToVkCompareOp(sampler.GetSamplerCompareFunc());
samplerInfo.minLod = sampler.GetMinLod();
samplerInfo.maxLod = sampler.GetMaxLod();
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
VkSampler vkSampler = VK_NULL_HANDLE;
VK_VERIFY(vkCreateSampler(m_device, &samplerInfo, nullptr, &vkSampler), "vkCreateSampler(texture)");
SamplerCacheEntry entry{};
entry.handle = vkSampler;
entry.externalIndex = sampler.GetExternalIndex();
entry.version = sampler.GetVersion();
m_samplers[key] = entry;
return vkSampler;
}
VkFilter VkTextureSamplerManager::ToVkFilter(SamplerFilterMode mode) {
return mode == SamplerFilterMode::Nearest ? VK_FILTER_NEAREST : VK_FILTER_LINEAR;
}
VkSamplerMipmapMode VkTextureSamplerManager::ToVkMipmapMode(SamplerMipmapMode mode) {
switch (mode) {
case SamplerMipmapMode::Nearest:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMipmapMode::Linear:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
case SamplerMipmapMode::None:
default:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
}
}
VkSamplerAddressMode VkTextureSamplerManager::ToVkAddressMode(SamplerWrapMode mode) {
switch (mode) {
case SamplerWrapMode::ClampToEdge:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case SamplerWrapMode::MirroredRepeat:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
case SamplerWrapMode::Repeat:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case SamplerWrapMode::ClampToBorder:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
case SamplerWrapMode::MirrorClampToEdge:
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
default:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
}
}
VkCompareOp VkTextureSamplerManager::ToVkCompareOp(SamplerCompareFunc func) {
switch (func) {
case SamplerCompareFunc::Never:
return VK_COMPARE_OP_NEVER;
case SamplerCompareFunc::Less:
return VK_COMPARE_OP_LESS;
case SamplerCompareFunc::Equal:
return VK_COMPARE_OP_EQUAL;
case SamplerCompareFunc::LessEqual:
return VK_COMPARE_OP_LESS_OR_EQUAL;
case SamplerCompareFunc::Greater:
return VK_COMPARE_OP_GREATER;
case SamplerCompareFunc::NotEqual:
return VK_COMPARE_OP_NOT_EQUAL;
case SamplerCompareFunc::GreaterEqual:
return VK_COMPARE_OP_GREATER_OR_EQUAL;
case SamplerCompareFunc::Always:
default:
return VK_COMPARE_OP_ALWAYS;
}
}
Bool VkTextureSamplerManager::UploadFallbackTexture() {
const Uint32 rgba = 0xFFFFFFFFu;
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent = {1, 1, 1};
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_VERIFY(vkCreateImage(m_device, &imageInfo, nullptr, &m_fallbackImage), "vkCreateImage(fallback)");
VkMemoryRequirements imageMemReq{};
vkGetImageMemoryRequirements(m_device, m_fallbackImage, &imageMemReq);
VkMemoryAllocateInfo imageAllocInfo{};
imageAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
imageAllocInfo.allocationSize = imageMemReq.size;
imageAllocInfo.memoryTypeIndex = FindMemoryType(imageMemReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_VERIFY(vkAllocateMemory(m_device, &imageAllocInfo, nullptr, &m_fallbackImageMemory),
"vkAllocateMemory(fallback)");
VK_VERIFY(vkBindImageMemory(m_device, m_fallbackImage, m_fallbackImageMemory, 0), "vkBindImageMemory(fallback)");
VkBuffer stagingBuffer = VK_NULL_HANDLE;
VkDeviceMemory stagingMemory = VK_NULL_HANDLE;
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = sizeof(rgba);
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_VERIFY(vkCreateBuffer(m_device, &bufferInfo, nullptr, &stagingBuffer), "vkCreateBuffer(fallback)");
VkMemoryRequirements stagingMemReq{};
vkGetBufferMemoryRequirements(m_device, stagingBuffer, &stagingMemReq);
VkMemoryAllocateInfo stagingAllocInfo{};
stagingAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
stagingAllocInfo.allocationSize = stagingMemReq.size;
stagingAllocInfo.memoryTypeIndex =
FindMemoryType(stagingMemReq.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
VK_VERIFY(vkAllocateMemory(m_device, &stagingAllocInfo, nullptr, &stagingMemory), "vkAllocateMemory(fallback)");
VK_VERIFY(vkBindBufferMemory(m_device, stagingBuffer, stagingMemory, 0), "vkBindBufferMemory(fallback)");
void* mapped = nullptr;
VK_VERIFY(vkMapMemory(m_device, stagingMemory, 0, sizeof(rgba), 0, &mapped), "vkMapMemory(fallback)");
std::memcpy(mapped, &rgba, sizeof(rgba));
vkUnmapMemory(m_device, stagingMemory);
const Bool uploadOk = ExecuteImmediate([&](VkCommandBuffer commandBuffer) {
VkImageMemoryBarrier toTransferDst{};
toTransferDst.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
toTransferDst.srcAccessMask = 0;
toTransferDst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
toTransferDst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
toTransferDst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
toTransferDst.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toTransferDst.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toTransferDst.image = m_fallbackImage;
toTransferDst.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
toTransferDst.subresourceRange.baseMipLevel = 0;
toTransferDst.subresourceRange.levelCount = 1;
toTransferDst.subresourceRange.baseArrayLayer = 0;
toTransferDst.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0,
nullptr, 0, nullptr, 1, &toTransferDst);
VkBufferImageCopy copy{};
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy.imageSubresource.mipLevel = 0;
copy.imageSubresource.baseArrayLayer = 0;
copy.imageSubresource.layerCount = 1;
copy.imageExtent = {1, 1, 1};
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, m_fallbackImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
&copy);
VkImageMemoryBarrier toSampled{};
toSampled.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
toSampled.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
toSampled.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
toSampled.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
toSampled.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
toSampled.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toSampled.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toSampled.image = m_fallbackImage;
toSampled.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
toSampled.subresourceRange.baseMipLevel = 0;
toSampled.subresourceRange.levelCount = 1;
toSampled.subresourceRange.baseArrayLayer = 0;
toSampled.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
0, nullptr, 0, nullptr, 1, &toSampled);
});
vkDestroyBuffer(m_device, stagingBuffer, nullptr);
vkFreeMemory(m_device, stagingMemory, nullptr);
if (!uploadOk) {
return false;
}
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = m_fallbackImage;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &m_fallbackImageView), "vkCreateImageView(fallback)");
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_NEAREST;
samplerInfo.minFilter = VK_FILTER_NEAREST;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
samplerInfo.maxAnisotropy = 1.0f;
VK_VERIFY(vkCreateSampler(m_device, &samplerInfo, nullptr, &m_fallbackSampler), "vkCreateSampler(fallback)");
return true;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,88 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureSamplerManager.h
// 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
#pragma once
#include "../VkIncludes.h"
#include <Includes.h>
#include <MG_State/GLState/SamplerState/SamplerObject.h>
#include <MG_State/GLState/TextureState/TextureObject.h>
namespace MobileGL::MG_State::GLState {
class ITextureObject;
class SamplerObject;
}
namespace MobileGL::MG_Backend::DirectVulkan {
class VkTextureSamplerManager {
public:
struct InitInfo {
VkDevice device = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkCommandPool commandPool = VK_NULL_HANDLE;
VkQueue graphicsQueue = VK_NULL_HANDLE;
};
Bool Initialize(const InitInfo& initInfo);
void Shutdown();
Bool GetFallbackDescriptor(VkDescriptorImageInfo& outImageInfo) const;
Bool SyncTextureAndGetDescriptor(const MG_State::GLState::ITextureObject& texture,
const MG_State::GLState::SamplerObject* samplerOverride,
VkDescriptorImageInfo& outImageInfo);
private:
struct TextureResource {
VkImage image = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
VkImageView view = VK_NULL_HANDLE;
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkExtent2D extent = {0, 0};
VkFormat format = VK_FORMAT_UNDEFINED;
Uint textureExternalIndex = 0;
};
struct SamplerCacheEntry {
VkSampler handle = VK_NULL_HANDLE;
Uint externalIndex = 0;
Uint16 version = 0;
};
Bool EnsureTextureSynced(TextureResource& resource, const MG_State::GLState::ITextureObject& texture);
Bool EnsureTextureResource(TextureResource& resource, const MG_State::GLState::ITextureObject& texture,
TextureUploadTarget level0Target, const IntVec3& texelSize, SizeT byteSize);
Bool UploadLevel0(TextureResource& resource, const MG_State::GLState::TextureObjectMipmap& mipmapTexture,
TextureUploadTarget level0Target, SizeT byteSize);
Bool ExecuteImmediate(const std::function<void(VkCommandBuffer)>& recorder) const;
void DestroyTextureResource(TextureResource& resource) const;
static Bool ResolveLevel0(const MG_State::GLState::ITextureObject& texture, TextureUploadTarget& outTarget,
IntVec3& outTexelSize, SizeT& outByteSize);
static VkFormat ResolveTextureFormat(TextureInternalFormat format);
Uint32 FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const;
VkSampler GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler);
static VkFilter ToVkFilter(SamplerFilterMode mode);
static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode);
static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode);
static VkCompareOp ToVkCompareOp(SamplerCompareFunc func);
Bool UploadFallbackTexture();
VkDevice m_device = VK_NULL_HANDLE;
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
VkCommandPool m_commandPool = VK_NULL_HANDLE;
VkQueue m_graphicsQueue = VK_NULL_HANDLE;
UnorderedMap<Uint, TextureResource> m_textureResources;
UnorderedMap<Uint64, SamplerCacheEntry> m_samplers;
VkImage m_fallbackImage = VK_NULL_HANDLE;
VkDeviceMemory m_fallbackImageMemory = VK_NULL_HANDLE;
VkImageView m_fallbackImageView = VK_NULL_HANDLE;
VkSampler m_fallbackSampler = VK_NULL_HANDLE;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
File diff suppressed because it is too large Load Diff
@@ -12,12 +12,14 @@
#include "PipelineFactory.h" #include "PipelineFactory.h"
#include "ProgramFactory.h" #include "ProgramFactory.h"
#include "SwapchainObject.h" #include "SwapchainObject.h"
#include "UniformDescriptorBinder.h" #include "UniformManager.h"
#include "VertexInputStateFactory.h" #include "VertexInputStateFactory.h"
#include "VkBufferObject.h" #include "VkBufferObject.h"
#include "VkFramebufferManager.h" #include "VkBufferManager.h"
#include "VkClearManager.h"
#include "VkRenderPassManager.h" #include "VkRenderPassManager.h"
#include "VkTextureSamplerManager.h" #include "VkSamplerManager.h"
#include "VkTextureManager.h"
#include "MG_Util/Math/VectorTypes.h" #include "MG_Util/Math/VectorTypes.h"
#include <Includes.h> #include <Includes.h>
#include <vk_mem_alloc.h> #include <vk_mem_alloc.h>
@@ -27,23 +29,61 @@
namespace MobileGL::MG_State::GLState { namespace MobileGL::MG_State::GLState {
class FramebufferObject; class FramebufferObject;
class ProgramObject; class ProgramObject;
class SamplerObject;
class VertexArrayObject; class VertexArrayObject;
} // namespace MobileGL::MG_State::GLState } // namespace MobileGL::MG_State::GLState
namespace MobileGL::MG_Backend::DirectVulkan { namespace MobileGL::MG_Backend::DirectVulkan {
struct DrawArrayPayload { enum class DrawSetupAspect: Uint8 {
GLenum mode = GL_TRIANGLES; FramebufferObject = 1 << 0,
GLint first = 0; VertexArrayObject = 1 << 1,
GLsizei count = 0; UniformBuffer = 1 << 2,
const MG_State::GLState::ProgramObject* program = nullptr; VertexBuffer = 1 << 3,
const MG_State::GLState::VertexArrayObject* vertexArray = nullptr; IndexBuffer = 1 << 4,
IndirectDrawBuffer = 1 << 5,
Viewport = 1 << 6,
Scissor = 1 << 7,
}; };
struct DrawElementPayload { struct DrawCmdParam {
DrawArrayPayload drawArray; Uint32 vertexCount = 0;
Uint32 instanceCount = 1;
Uint32 firstVertex = 0;
Uint32 firstInstance = 0;
};
struct DrawIndexedCmdParam {
Uint32 indexCount = 0;
Uint32 instanceCount = 1;
Uint32 firstIndex = 0;
Int32 vertexOffset = 0;
Int32 firstInstance = 0;
};
struct DrawCmd {
GLenum mode = GL_TRIANGLES;
DrawCmdParam params;
};
struct IndexBufferView {
GLenum indexType = GL_UNSIGNED_SHORT; GLenum indexType = GL_UNSIGNED_SHORT;
SizeT indexByteOffset = 0; SizeT indexByteOffset = 0;
GLint baseVertex = 0; SizeT indexByteSize = 0;
};
struct DrawIndexedCmd {
GLenum mode = GL_TRIANGLES;
IndexBufferView indexBufferView;
DrawIndexedCmdParam params;
};
struct MultiDrawIndexedCmd {
GLenum mode = GL_TRIANGLES;
IndexBufferView indexBufferView;
Uint32 drawCount = 0;
DrawIndexedCmdParam* pParams = nullptr;
}; };
struct QueueFamilyIndices { struct QueueFamilyIndices {
@@ -69,17 +109,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void Initialize(); void Initialize();
void Shutdown(); void Shutdown();
void RequestClear(GLbitfield mask, const FloatVec4& color, Float depth, Uint32 stencil, Bool SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags<DrawSetupAspect> aspects,
Uint drawFboExternalIndex, Bool isDefaultFramebufferTarget); const IndexBufferView* pIndexBufferView = nullptr);
Bool ConsumePendingColorClear(VkClearColorValue& outClearColor); void ClearAttachmentsOnActiveRenderPass(VkCommandBuffer commandBuffer,
void EnsureFrameRecordingStarted(); const RenderPassEntry& compatibleRenderPassEntry);
void DrawArrays(const DrawArrayPayload& payload);
void DrawElements(const DrawElementPayload& payload); void Clear(GLbitfield mask);
void MultiDrawElements(const Vector<DrawElementPayload>& payloads); void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
Bool BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLint dstY1, GLbitfield mask, GLenum filter, Uint readFboExternalIndex, GLbitfield mask, GLenum filter);
Uint drawFboExternalIndex, Bool readIsDefaultFramebuffer, Bool drawIsDefaultFramebuffer); void DrawArrays(const DrawCmd& payload);
void Render(); void DrawElements(const DrawIndexedCmd& payload);
void MultiDrawElements(const MultiDrawIndexedCmd& payloads);
void Present(); void Present();
const PhysicalDevice& GetPhysicalDevice() const; const PhysicalDevice& GetPhysicalDevice() const;
@@ -88,13 +129,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void RecreateSwapchain(); void RecreateSwapchain();
private: private:
struct PendingClearState { struct BlitUniformData {
GLbitfield mask = 0; float srcRect[4] = {0.f, 0.f, 1.f, 1.f};
VkClearColorValue color = {{0.0f, 0.0f, 0.0f, 1.0f}}; float dstRect[4] = {0.f, 0.f, 1.f, 1.f};
Float depth = 1.0f; Int surfaceTransform = 0;
Uint32 stencil = 0; Int padding[3] = {0, 0, 0};
Uint drawFboExternalIndex = 0; };
Bool targetsDefaultFramebuffer = true;
struct BlitResources {
SharedPtr<MG_State::GLState::ProgramObject> program;
SharedPtr<MG_State::GLState::SamplerObject> nearestSampler;
SharedPtr<MG_State::GLState::SamplerObject> linearSampler;
Int srcRectLocation = -1;
Int dstRectLocation = -1;
Int surfaceTransformLocation = -1;
Uint32 samplerBinding = 0;
}; };
NativeWindowType m_window = 0; NativeWindowType m_window = 0;
@@ -106,56 +155,38 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkInstance m_instance = VK_NULL_HANDLE; VkInstance m_instance = VK_NULL_HANDLE;
VkDebugUtilsMessengerEXT m_debugMessenger = VK_NULL_HANDLE; VkDebugUtilsMessengerEXT m_debugMessenger = VK_NULL_HANDLE;
PhysicalDevice m_physicalDevice; PhysicalDevice m_physicalDevice;
// VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
VkDevice m_device = VK_NULL_HANDLE; VkDevice m_device = VK_NULL_HANDLE;
VmaAllocator m_allocator = nullptr; VmaAllocator m_allocator = nullptr;
VkSurfaceKHR m_surface = VK_NULL_HANDLE; VkSurfaceKHR m_surface = VK_NULL_HANDLE;
SwapchainObject m_swapchainObject; SwapchainObject m_swapchainObject;
// Vector<VkQueueFamilyProperties> m_queueFamilies;
// QueueFamilyIndices m_queueFamilyIndices;
VkQueue m_graphicsQueue = VK_NULL_HANDLE; VkQueue m_graphicsQueue = VK_NULL_HANDLE;
VkQueue m_presentQueue = VK_NULL_HANDLE; VkQueue m_presentQueue = VK_NULL_HANDLE;
Bool m_drawIndirectCountExtensionEnabled = false; Bool m_drawIndirectCountExtensionEnabled = false;
Bool m_indexTypeUint8ExtensionEnabled = false;
using PFNDrawIndexedIndirectCountFunc = void(VKAPI_PTR*)(VkCommandBuffer commandBuffer, VkBuffer buffer, using PFNDrawIndexedIndirectCountFunc = void(VKAPI_PTR*)(VkCommandBuffer commandBuffer, VkBuffer buffer,
VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize offset, VkBuffer countBuffer,
VkDeviceSize countBufferOffset, Uint32 maxDrawCount, VkDeviceSize countBufferOffset, Uint32 maxDrawCount,
Uint32 stride); Uint32 stride);
PFNDrawIndexedIndirectCountFunc m_cmdDrawIndexedIndirectCount = nullptr; static inline PFNDrawIndexedIndirectCountFunc s_vkCmdDrawIndexedIndirectCount = nullptr;
VkCommandPool m_commandPool = VK_NULL_HANDLE; VkCommandPool m_commandPool = VK_NULL_HANDLE;
VkFormat m_depthStencilFormat = VK_FORMAT_UNDEFINED; VkBufferManager m_bufferManager;
Vector<VkImage> m_depthStencilImages; Vector<const MG_State::GLState::BufferObject*> m_transientVertexIndexBuffersThisFrame;
Vector<VkDeviceMemory> m_depthStencilImageMemories;
Vector<VkImageView> m_depthStencilImageViews;
Vector<VkImageLayout> m_depthStencilImageLayouts;
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
Vector<VkBufferObject> m_frameVertexUploadBuffers;
Vector<VkDeviceSize> m_frameVertexUploadHeads;
Vector<VkBufferObject> m_frameIndexUploadBuffers;
Vector<VkDeviceSize> m_frameIndexUploadHeads;
Vector<Vector<VkBufferObject>> m_deferredBufferReleases;
Uint m_imageIndexAcquired = 0; Uint m_imageIndexAcquired = 0;
FrameContext m_frameContext; FrameContext m_frameContext;
UnorderedMap<Uint64, PendingClearState> m_pendingClears;
Bool m_isMainRenderPassActive = false;
VkRenderPass m_activeRenderPass = VK_NULL_HANDLE;
VkExtent2D m_activeRenderExtent = {0, 0};
VkFormat m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED;
Bool m_activeRenderTargetIsDefault = true;
Uint m_activeDrawFboExternalIndex = 0;
UniquePtr<PipelineFactory> m_pipelineFactory; UniquePtr<PipelineFactory> m_pipelineFactory;
UniquePtr<ProgramFactory> m_programFactory; UniquePtr<ProgramFactory> m_programFactory;
UniquePtr<UniformDescriptorBinder> m_uniformDescriptorBinder; UniquePtr<UniformManager> m_uniformManager;
UniquePtr<VertexInputStateFactory> m_vertexInputStateFactory; UniquePtr<VertexInputStateFactory> m_vertexInputStateFactory;
UniquePtr<VkFramebufferManager> m_framebufferManager; UniquePtr<VkClearManager> m_clearManager;
UniquePtr<VkRenderPassManager> m_renderPassManager; UniquePtr<VkRenderPassManager> m_renderPassManager;
UniquePtr<VkTextureSamplerManager> m_textureSamplerManager; UniquePtr<VkTextureManager> m_textureManager;
UniquePtr<VkSamplerManager> m_samplerManager;
BlitResources m_blitResources;
void CreateInstance(); void CreateInstance();
VkResult SetupDebugMessenger(); VkResult SetupDebugMessenger();
@@ -168,31 +199,32 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void DestroyAllocator(); void DestroyAllocator();
void CreateSwapchain(); void CreateSwapchain();
void CreateCommandPool(); void CreateCommandPool();
void CreateFrameContexts();
void CreateDepthStencilResources(); VkPipeline GetOrCreatePipeline(
void DestroyDepthStencilResources(); GLenum mode,
VkRenderPass GetDefaultLoadRenderPass() const; const MG_State::GLState::ProgramObject& program,
Bool GetDefaultRenderTargetForCurrentImage(VkRenderPass& outRenderPass, VkFramebuffer& outFramebuffer, const MG_State::GLState::VertexArrayObject& vao,
VkExtent2D& outExtent, VkFormat& outDepthStencilFormat) const; const RenderPassEntry& renderPassEntry);
Bool EnsureOffscreenRenderTarget(Uint glFboExternalIndex, const MG_State::GLState::FramebufferObject& glFbo,
VkRenderPass& outRenderPass, VkFramebuffer& outFramebuffer, Bool UploadAndBindVertexBuffers(VkCommandBuffer commandBuffer, const MG_State::GLState::VertexArrayObject& vao);
VkExtent2D& outExtent, VkFormat& outDepthStencilFormat); Bool UploadAndBindIndexBuffer(FrameContext::FrameData& frame,
void PrepareDemoPipeline(); const MG_State::GLState::VertexArrayObject& vao,
VkPipeline GetOrCreatePipeline(const MG_State::GLState::ProgramObject& program, VkPipelineLayout pipelineLayout, const IndexBufferView* pIndexBufferView = nullptr);
Uint64 vertexInputHash, Bool InitializeBlitResources();
const VkPipelineVertexInputStateCreateInfo& vertexInputState); void ShutdownBlitResources();
void TransitionSwapchainImageToColorAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex); Bool TryBlitToDefaultFramebufferWithShader(FrameContext::FrameData& frame,
void TransitionDepthStencilImageToAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex); MG_State::GLState::FramebufferObject& readFbo,
void EndFrameRecordingIfNeeded(); MG_State::GLState::FramebufferObject& drawFbo,
void DeferDestroyBuffer(VkBufferObject& buffer); GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
void CollectDeferredBufferReleases(Uint32 frameIndex); GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
Bool EnsureFrameUploadBufferCapacity(Uint32 frameIndex, Bool isIndexBuffer, VkDeviceSize requiredEndOffset, GLenum filter);
VkDeviceSize minCapacity, VkBufferUsageFlags usage); Bool MaterializePendingClearForTexture(VkCommandBuffer commandBuffer,
Bool UploadAndBindVertexStreams(const VertexInputStateFactory::BackendVertexInputState& vertexInputState, MG_State::GLState::ITextureObject& texture);
const DrawArrayPayload& payload, VkCommandBuffer commandBuffer); VkPipeline GetOrCreateBlitPipeline(const RenderPassEntry& renderPassEntry);
void ShutdownSwapchain(); void ShutdownSwapchain();
// Static functions
static Int GetPresentQueueFamilyIndex(const PhysicalDevice& physicalDevice, VkSurfaceKHR surface, static Int GetPresentQueueFamilyIndex(const PhysicalDevice& physicalDevice, VkSurfaceKHR surface,
const Vector<VkQueueFamilyProperties>& queueFamilies, const Vector<VkQueueFamilyProperties>& queueFamilies,
Int preferredFamilyIndex = -1); Int preferredFamilyIndex = -1);
@@ -212,11 +244,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
static Bool GetMoreCapablePhysicalDevice(VkPhysicalDevice newVkDevice, VkSurfaceKHR surface, static Bool GetMoreCapablePhysicalDevice(VkPhysicalDevice newVkDevice, VkSurfaceKHR surface,
const PhysicalDevice& compareWithDevice, const PhysicalDevice& compareWithDevice,
PhysicalDevice& outBetterDevice); PhysicalDevice& outBetterDevice);
Uint32 FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const;
static Uint64 BuildPendingClearKey(Uint drawFboExternalIndex, Bool targetsDefaultFramebuffer);
static Bool HasStencilComponent(VkFormat format);
static VkFormat FindSupportedDepthStencilFormat(VkPhysicalDevice physicalDevice);
static constexpr VkDynamicState s_dynamicStates[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
static constexpr const char* s_validationLayerNames[] = {"VK_LAYER_KHRONOS_validation"}; static constexpr const char* s_validationLayerNames[] = {"VK_LAYER_KHRONOS_validation"};
static constexpr const char* s_deviceExtensionNames[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; static constexpr const char* s_deviceExtensionNames[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
static Bool CheckValidationLayerSupport(); static Bool CheckValidationLayerSupport();
@@ -13,8 +13,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
struct VulkanRendererConfig { struct VulkanRendererConfig {
Uint32 MaxFramesInFlight = 2; Uint32 MaxFramesInFlight = 2;
String AppName = "MobileGL-VulkanRenderer"; String AppName = "MobileGL-VulkanRenderer";
Version Version = MG_Config::CoreVersion; MobileGL::Version Version = MG_Config::CoreVersion;
Uint64 CacheVersion = MG_Config::CacheVersion; Uint64 CacheVersion = MG_Config::CacheVersion;
Bool EnableValidationLayers = true; Bool EnableValidationLayers = true;
}; };
} // namespace MobileGL::MG_Backend::DirectVulkan } // namespace MobileGL::MG_Backend::DirectVulkan
+1 -1
View File
@@ -20,7 +20,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
if (!MG_State::pEGLContext) { if (!MG_State::pEGLContext) {
MGLOG_E("pEGLContext is null. MG_State may not be initialized."); MGLOG_E("pEGLContext is null. MG_State may not be initialized.");
} }
return MG_State::pEGLContext; return MG_State::pEGLContext.get();
} }
MG_Backend::BackendObject* GetBackendObject(EGLStateContext* state) { MG_Backend::BackendObject* GetBackendObject(EGLStateContext* state) {
File diff suppressed because it is too large Load Diff
+18 -20
View File
@@ -9,24 +9,22 @@
#pragma once #pragma once
#include <Includes.h> #include <Includes.h>
namespace MobileGL { namespace MobileGL::MG_Impl::GLImpl {
namespace MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */ void GetBufferParameteriv(GLenum target, GLenum pname, GLint* params);
void GetBufferParameteriv(GLenum target, GLenum pname, GLint* params); GLboolean IsBuffer(GLuint buffer);
GLboolean IsBuffer(GLuint buffer); void DeleteBuffers(GLsizei n, const GLuint* buffers);
void DeleteBuffers(GLsizei n, const GLuint* buffers); void FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length);
void FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length); GLboolean UnmapBuffer(GLenum target);
GLboolean UnmapBuffer(GLenum target); void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); void* MapBuffer(GLenum target, GLenum access);
void* MapBuffer(GLenum target, GLenum access); void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset,
void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
GLsizeiptr size); void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data); void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage); void BindBuffer(GLenum target, GLuint buffer);
void BindBuffer(GLenum target, GLuint buffer); void GenBuffers(GLsizei n, GLuint* buffers);
void GenBuffers(GLsizei n, GLuint* buffers); void BindBufferBase(GLenum target, GLuint index, GLuint buffer);
void BindBufferBase(GLenum target, GLuint index, GLuint buffer); void BindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
void BindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
} // namespace MG_Impl::GLImpl } // namespace MobileGL::MG_Impl::GLImpl
} // namespace MobileGL
+87 -92
View File
@@ -13,105 +13,100 @@
#include <MG_Util/Converters/MGToGL/BufferEnumConverter.h> #include <MG_Util/Converters/MGToGL/BufferEnumConverter.h>
#include <MG_Util/Converters/MGToStr/BufferEnumConverter.h> #include <MG_Util/Converters/MGToStr/BufferEnumConverter.h>
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl::BufferImpl {
namespace BufferImpl { Bool ValidateBufferTarget(BufferTarget target) {
Bool ValidateBufferTarget(BufferTarget target) { if (target == BufferTarget::Unknown) {
if (target == BufferTarget::Unknown) { using namespace MG_Util;
using namespace MG_Util; String bufferTargetStr = ConvertBufferTargetToString(target);
String bufferTargetStr = ConvertBufferTargetToString(target); String glTargetStr = ConvertGLEnumToString(ConvertBufferTargetToGLEnum(target));
String glTargetStr = ConvertGLEnumToString(ConvertBufferTargetToGLEnum(target));
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>(
"MG_Impl/GLImpl/BufferImpl", "ValidateBufferTarget",
std::format("Target {} ({}) is not valid.", bufferTargetStr, glTargetStr)));
return false;
}
if (target == BufferTarget::Index && MG_State::pGLContext->GetBoundVertexArray() == nullptr) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl",
"ValidateBufferTarget",
"No vertex array object is bound."));
return false;
}
return true;
}
Bool ValidateBufferBindingPointTarget(BufferTarget target) {
if (target != BufferTarget::Uniform && target != BufferTarget::AtomicCounter &&
target != BufferTarget::TransformFeedback && target != BufferTarget::ShaderStorage) {
using namespace MG_Util;
String bufferTargetStr = ConvertBufferTargetToString(target);
String glTargetStr = ConvertGLEnumToString(ConvertBufferTargetToGLEnum(target));
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>(
"MG_Impl/GLImpl/BufferImpl", "ValidateBufferTarget",
std::format("Target {} ({}) is not valid.", bufferTargetStr, glTargetStr)));
return false;
}
return true;
}
Bool ValidateBufferName(Uint index, Bool allowZero) {
if (index == 0) {
if (allowZero) return true;
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl",
"ValidateBufferName",
"Buffer name 0 is not valid."));
return false;
}
Bool isValid = MG_State::pGLContext->ValidateBufferName(index);
if (isValid) return true;
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>(
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferName", "MG_Impl/GLImpl/BufferImpl", "ValidateBufferTarget",
std::format("Buffer name {} is not valid.", index))); std::format("Target {} ({}) is not valid.", bufferTargetStr, glTargetStr)));
return false; return false;
} }
Bool ValidateBufferUsage(BufferUsage usage) { if (target == BufferTarget::Index && MG_State::pGLContext->GetBoundVertexArray() == nullptr) {
if (usage != BufferUsage::Unknown) { MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
return true; MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl",
} "ValidateBufferTarget",
"No vertex array object is bound."));
return false;
}
return true;
}
Bool ValidateBufferBindingPointTarget(BufferTarget target) {
if (target != BufferTarget::Uniform && target != BufferTarget::AtomicCounter &&
target != BufferTarget::TransformFeedback && target != BufferTarget::ShaderStorage) {
using namespace MG_Util; using namespace MG_Util;
String bufferUsageStr = ConvertBufferUsageToString(usage); String bufferTargetStr = ConvertBufferTargetToString(target);
String glUsageStr = ConvertGLEnumToString(ConvertBufferUsageToGLEnum(usage)); String glTargetStr = ConvertGLEnumToString(ConvertBufferTargetToGLEnum(target));
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl/BufferImpl", "ValidateBufferTarget",
std::format("Target {} ({}) is not valid.", bufferTargetStr, glTargetStr)));
return false;
}
return true;
}
Bool ValidateBufferName(Uint index, Bool allowZero) {
if (index == 0) {
if (allowZero) return true;
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferName",
"Buffer name 0 is not valid."));
return false;
}
Bool isValid = MG_State::pGLContext->ValidateBufferName(index);
if (isValid) return true;
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferName",
std::format("Buffer name {} is not valid.", index)));
return false;
}
Bool ValidateBufferUsage(BufferUsage usage) {
if (usage != BufferUsage::Unknown) {
return true;
}
using namespace MG_Util;
String bufferUsageStr = ConvertBufferUsageToString(usage);
String glUsageStr = ConvertGLEnumToString(ConvertBufferUsageToGLEnum(usage));
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl/BufferImpl", "ValidateBufferUsage",
std::format("Usage {} ({}) is not one of the allowable values.", bufferUsageStr, glUsageStr)));
return false;
}
Bool ValidateBufferMappingAccess(Flags<BufferMappingAccessBit> accessBits) {
if (accessBits == BufferMappingAccessBit::Null) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl",
"ValidateBufferMappingAccess",
"Access bits cannot be null."));
return false;
}
const auto validBits = BufferMappingAccessBit::Read | BufferMappingAccessBit::Write |
BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer |
BufferMappingAccessBit::FlushExplicit | BufferMappingAccessBit::Unsynchronized |
BufferMappingAccessBit::Persistent | BufferMappingAccessBit::Coherent;
if ((accessBits & validBits) != accessBits) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>( MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferMappingAccess",
"MG_Impl/GLImpl/BufferImpl", "ValidateBufferUsage", "Access bits cannot contain invalid flags."));
std::format("Usage {} ({}) is not one of the allowable values.", bufferUsageStr, glUsageStr)));
return false; return false;
} }
Bool ValidateBufferMappingAccess(Flags<BufferMappingAccessBit> accessBits) { return true;
if (accessBits == BufferMappingAccessBit::Null) { }
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum, } // namespace MobileGL::MG_Impl::GLImpl::BufferImpl
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl",
"ValidateBufferMappingAccess",
"Access bits cannot be null."));
return false;
}
const auto validBits = BufferMappingAccessBit::Read | BufferMappingAccessBit::Write |
BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer |
BufferMappingAccessBit::FlushExplicit | BufferMappingAccessBit::Unsynchronized |
BufferMappingAccessBit::Persistent | BufferMappingAccessBit::Coherent;
if ((accessBits & validBits) != accessBits) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferMappingAccess",
"Access bits cannot contain invalid flags."));
return false;
}
return true;
}
} // namespace BufferImpl
} // namespace MobileGL::MG_Impl::GLImpl
+7 -9
View File
@@ -10,12 +10,10 @@
#include <Includes.h> #include <Includes.h>
#include <MG_State/GLState/BufferState/BufferObject.h> #include <MG_State/GLState/BufferState/BufferObject.h>
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl::BufferImpl {
namespace BufferImpl { Bool ValidateBufferTarget(BufferTarget target);
Bool ValidateBufferTarget(BufferTarget target); Bool ValidateBufferName(Uint index, Bool allowZero = false);
Bool ValidateBufferName(Uint index, Bool allowZero = false); Bool ValidateBufferUsage(BufferUsage usage);
Bool ValidateBufferUsage(BufferUsage usage); Bool ValidateBufferMappingAccess(Flags<BufferMappingAccessBit> accessBits);
Bool ValidateBufferMappingAccess(Flags<BufferMappingAccessBit> accessBits); Bool ValidateBufferBindingPointTarget(BufferTarget target);
Bool ValidateBufferBindingPointTarget(BufferTarget target); } // namespace MobileGL::MG_Impl::GLImpl::BufferImpl
} // namespace BufferImpl
} // namespace MobileGL::MG_Impl::GLImpl
+28 -31
View File
@@ -9,34 +9,31 @@
#pragma once #pragma once
#include <Includes.h> #include <Includes.h>
namespace MobileGL { namespace MobileGL::MG_Impl::GLImpl {
namespace MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */ void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride);
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride);
GLsizei stride); void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride); const void* indices, GLint basevertex);
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices);
const void* indices, GLint basevertex); void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices); GLsizei instancecount, GLint basevertex, GLuint baseinstance);
void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex, GLuint baseinstance); GLsizei instancecount, GLint basevertex);
void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex); GLsizei instancecount, GLuint baseinstance);
void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices, void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount);
GLsizei instancecount, GLuint baseinstance); void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect);
void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount); void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect); GLuint baseinstance);
void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
GLuint baseinstance); void DrawArraysIndirect(GLenum mode, const void* indirect);
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex);
void DrawArraysIndirect(GLenum mode, const void* indirect); void DrawArrays(GLenum mode, GLint first, GLsizei count);
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex); void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
void DrawArrays(GLenum mode, GLint first, GLsizei count); GLsizei drawcount);
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
GLsizei drawcount); GLsizei drawcount, const GLint* basevertex);
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, void Clear(GLbitfield mask);
GLsizei drawcount, const GLint* basevertex); void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
void Clear(GLbitfield mask); } // namespace MobileGL::MG_Impl::GLImpl
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
@@ -38,11 +38,11 @@ namespace MobileGL::MG_Impl::GLImpl {
RenderbufferTarget rbTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target); RenderbufferTarget rbTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target);
if (!FramebufferImpl::ValidateRenderbufferTarget(rbTarget)) return; if (!FramebufferImpl::ValidateRenderbufferTarget(rbTarget)) return;
auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(rbTarget); auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(rbTarget);
auto renderbufferObject = bindingSlot.GetBoundObject(); auto& renderbufferObject = bindingSlot.GetBoundObject();
if (!renderbufferObject) { if (!renderbufferObject) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "RenderbufferStorage_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "RenderbufferStorage_State",
"Renderbuffer target is bound to no renderbuffer object.")); "Renderbuffer target is bound to no renderbuffer object."));
return; return;
} }
@@ -50,7 +50,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!TextureImpl::ValidateTextureInternalFormat(format)) return; if (!TextureImpl::ValidateTextureInternalFormat(format)) return;
if (width < 0 || height < 0) { if (width < 0 || height < 0) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "RenderbufferStorage_State", ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "RenderbufferStorage_State",
"Width and height must be non-negative.")); "Width and height must be non-negative."));
return; return;
} }
@@ -74,10 +74,11 @@ namespace MobileGL::MG_Impl::GLImpl {
if (n < 0) { if (n < 0) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GenRenderbuffers_State", "n must be non-negative")); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GenRenderbuffers_State", "n must be non-negative"));
return; return;
} }
auto renderbufferNames = MG_State::pGLContext->GenRenderbufferNames(n); static thread_local Vector<GLuint> renderbufferNames;
MG_State::pGLContext->GenRenderbufferNames(n, renderbufferNames);
Memcpy(renderbuffers, renderbufferNames.data(), sizeof(GLuint) * static_cast<SizeT>(n)); Memcpy(renderbuffers, renderbufferNames.data(), sizeof(GLuint) * static_cast<SizeT>(n));
} }
@@ -85,10 +86,11 @@ namespace MobileGL::MG_Impl::GLImpl {
if (n < 0) { if (n < 0) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GenFramebuffers_State", "n must be non-negative")); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GenFramebuffers_State", "n must be non-negative"));
return; return;
} }
auto framebuffersNames = MG_State::pGLContext->GenFramebufferNames(n); static thread_local Vector<GLuint> framebuffersNames;
MG_State::pGLContext->GenFramebufferNames(n, framebuffersNames);
Memcpy(framebuffers, framebuffersNames.data(), sizeof(GLuint) * static_cast<SizeT>(n)); Memcpy(framebuffers, framebuffersNames.data(), sizeof(GLuint) * static_cast<SizeT>(n));
} }
@@ -119,11 +121,11 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!TextureImpl::ValidateTextureName(texture, true)) return; if (!TextureImpl::ValidateTextureName(texture, true)) return;
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget); auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget);
auto framebufferObject = bindingSlot.GetBoundObject(); auto& framebufferObject = bindingSlot.GetBoundObject();
if (!framebufferObject) { if (!framebufferObject) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FramebufferTexture2D_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "FramebufferTexture2D_State",
"Framebuffer target is bound to no framebuffer object.")); "Framebuffer target is bound to no framebuffer object."));
return; return;
} }
@@ -133,11 +135,11 @@ namespace MobileGL::MG_Impl::GLImpl {
return; return;
} }
auto textureObject = MG_State::pGLContext->GetTextureObject(texture); auto& textureObject = MG_State::pGLContext->GetTextureObject(texture);
if (!textureObject) { if (!textureObject) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FramebufferTexture2D_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "FramebufferTexture2D_State",
std::format("Texture object {} is not valid.", texture))); std::format("Texture object {} is not valid.", texture)));
return; return;
} }
@@ -166,11 +168,11 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return; if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return;
if (!FramebufferImpl::ValidateRenderbufferName(renderbuffer)) return; if (!FramebufferImpl::ValidateRenderbufferName(renderbuffer)) return;
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget); auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget);
auto framebufferObject = bindingSlot.GetBoundObject(); auto& framebufferObject = bindingSlot.GetBoundObject();
if (!framebufferObject) { if (!framebufferObject) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FramebufferRenderbuffer_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "FramebufferRenderbuffer_State",
"Framebuffer target is bound to no framebuffer object.")); "Framebuffer target is bound to no framebuffer object."));
return; return;
} }
@@ -180,11 +182,11 @@ namespace MobileGL::MG_Impl::GLImpl {
return; return;
} }
auto renderbufferObject = MG_State::pGLContext->GetRenderbufferObject(renderbuffer); auto& renderbufferObject = MG_State::pGLContext->GetRenderbufferObject(renderbuffer);
if (!renderbufferObject) { if (!renderbufferObject) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FramebufferRenderbuffer_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "FramebufferRenderbuffer_State",
std::format("Renderbuffer object {} is not valid.", renderbuffer))); std::format("Renderbuffer object {} is not valid.", renderbuffer)));
return; return;
} }
@@ -196,18 +198,18 @@ namespace MobileGL::MG_Impl::GLImpl {
if (n < 0) { if (n < 0) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "`n` is less than 0.")); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "`n` is less than 0."));
return; return;
} else if (n > MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { } else if (n > MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "`n` is greater than `GL_MAX_DRAW_BUFFERS`.")); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "`n` is greater than `GL_MAX_DRAW_BUFFERS`."));
return; return;
} }
// Get bound framebuffer // Get bound framebuffer
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw); auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw);
auto fbo = bindingSlot.GetBoundObject(); auto& fbo = bindingSlot.GetBoundObject();
bool isDefaultFBO = (fbo == FramebufferImpl::pDefaultFramebufferInfo->defaultFBO); bool isDefaultFBO = (fbo == FramebufferImpl::pDefaultFramebufferInfo->defaultFBO);
static int existenceMap[(SizeT)FramebufferAttachmentType::FramebufferAttachmentTypeCount] = {-1}; static int existenceMap[(SizeT)FramebufferAttachmentType::FramebufferAttachmentTypeCount] = {-1};
@@ -220,7 +222,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (attType == FramebufferAttachmentType::Unknown) { if (attType == FramebufferAttachmentType::Unknown) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", __func__, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
std::format("bufs[{}] = {} is not an accepted value.", i, std::format("bufs[{}] = {} is not an accepted value.", i,
MG_Util::ConvertGLEnumToString(bufs[i])))); MG_Util::ConvertGLEnumToString(bufs[i]))));
return; return;
@@ -230,7 +232,7 @@ namespace MobileGL::MG_Impl::GLImpl {
attType <= FramebufferAttachmentType::Color31) { attType <= FramebufferAttachmentType::Color31) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>( MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", __func__, "MG_Impl/GLImpl", __func__,
std::format( std::format(
"FBO is default FBO, but bufs[{}] = {} is one of the `GL_COLOR_ATTACHMENTn` tokens.", i, "FBO is default FBO, but bufs[{}] = {} is one of the `GL_COLOR_ATTACHMENTn` tokens.", i,
@@ -242,7 +244,7 @@ namespace MobileGL::MG_Impl::GLImpl {
attType <= FramebufferAttachmentType::BackRight) { attType <= FramebufferAttachmentType::BackRight) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>( MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", __func__, "MG_Impl/GLImpl", __func__,
std::format("FBO is not default FBO, but bufs[{}] = {} is anything other than `GL_NONE` or " std::format("FBO is not default FBO, but bufs[{}] = {} is anything other than `GL_NONE` or "
"one of the `GL_COLOR_ATTACHMENTn` tokens.", "one of the `GL_COLOR_ATTACHMENTn` tokens.",
@@ -253,7 +255,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (attType != FramebufferAttachmentType::None && existenceMap[(SizeT)attType] >= 0) { if (attType != FramebufferAttachmentType::None && existenceMap[(SizeT)attType] >= 0) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", __func__, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
std::format("a symbolic constant other than `GL_NONE` appears " std::format("a symbolic constant other than `GL_NONE` appears "
"more than once in bufs. bufs[{}] == bufs[{}] == {}.", "more than once in bufs. bufs[{}] == bufs[{}] == {}.",
i, existenceMap[(SizeT)attType], i, existenceMap[(SizeT)attType],
@@ -267,7 +269,7 @@ namespace MobileGL::MG_Impl::GLImpl {
(SizeT)FramebufferAttachmentType::Color0 + MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { (SizeT)FramebufferAttachmentType::Color0 + MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", __func__, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
std::format("bufs[{}] == {} indicates a color buffer that does " std::format("bufs[{}] == {} indicates a color buffer that does "
"not exist in the current GL context.", "not exist in the current GL context.",
i, MG_Util::ConvertGLEnumToString(bufs[i])))); i, MG_Util::ConvertGLEnumToString(bufs[i]))));
@@ -297,7 +299,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (attType == FramebufferAttachmentType::Unknown) { if (attType == FramebufferAttachmentType::Unknown) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>( MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", __func__, "MG_Impl/GLImpl", __func__,
std::format("`mode` = {} is not an accepted value.", MG_Util::ConvertGLEnumToString(mode)))); std::format("`mode` = {} is not an accepted value.", MG_Util::ConvertGLEnumToString(mode))));
return; return;
@@ -305,7 +307,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// Get bound framebuffer // Get bound framebuffer
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read); auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read);
auto fbo = bindingSlot.GetBoundObject(); auto& fbo = bindingSlot.GetBoundObject();
fbo->SetReadBuffer(attType); fbo->SetReadBuffer(attType);
} }
@@ -313,13 +315,13 @@ namespace MobileGL::MG_Impl::GLImpl {
if (n < 0) { if (n < 0) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteRenderbuffers_State", "n must be non-negative.")); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteRenderbuffers_State", "n must be non-negative."));
return; return;
} }
if (!renderbuffers) { if (!renderbuffers) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteRenderbuffers_State", ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteRenderbuffers_State",
"Renderbuffer names array cannot be null.")); "Renderbuffer names array cannot be null."));
return; return;
} }
@@ -336,13 +338,13 @@ namespace MobileGL::MG_Impl::GLImpl {
if (n < 0) { if (n < 0) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteFramebuffers_State", "n must be non-negative.")); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteFramebuffers_State", "n must be non-negative."));
return; return;
} }
if (!framebuffers) { if (!framebuffers) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue, MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteFramebuffers_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteFramebuffers_State",
"Framebuffer names array cannot be null.")); "Framebuffer names array cannot be null."));
return; return;
} }
@@ -360,11 +362,11 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return GL_FRAMEBUFFER_UNDEFINED; if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return GL_FRAMEBUFFER_UNDEFINED;
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget); auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget);
auto framebufferObject = bindingSlot.GetBoundObject(); auto& framebufferObject = bindingSlot.GetBoundObject();
if (!framebufferObject) { if (!framebufferObject) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "CheckFramebufferStatus_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "CheckFramebufferStatus_State",
"Framebuffer target is bound to no framebuffer object.")); "Framebuffer target is bound to no framebuffer object."));
return GL_FRAMEBUFFER_UNDEFINED; return GL_FRAMEBUFFER_UNDEFINED;
} }
@@ -382,11 +384,11 @@ namespace MobileGL::MG_Impl::GLImpl {
RenderbufferTarget renderbufferTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target); RenderbufferTarget renderbufferTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target);
if (!FramebufferImpl::ValidateRenderbufferTarget(renderbufferTarget)) return; if (!FramebufferImpl::ValidateRenderbufferTarget(renderbufferTarget)) return;
auto renderbufferObject = MG_State::pGLContext->GetRenderbufferObject(renderbuffer); Bool doesRenderbufferCreated = MG_State::pGLContext->ValidateRenderbufferObject(renderbuffer);
if (!renderbufferObject) { if (!doesRenderbufferCreated) {
MG_State::pGLContext->CreateRenderbufferObject(renderbuffer); MG_State::pGLContext->CreateRenderbufferObject(renderbuffer);
renderbufferObject = MG_State::pGLContext->GetRenderbufferObject(renderbuffer);
} }
auto& renderbufferObject = MG_State::pGLContext->GetRenderbufferObject(renderbuffer);
auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(renderbufferTarget); auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(renderbufferTarget);
bindingSlot.Bind(renderbufferObject); bindingSlot.Bind(renderbufferObject);
@@ -403,11 +405,11 @@ namespace MobileGL::MG_Impl::GLImpl {
FramebufferTarget framebufferTarget = MG_Util::ConvertGLEnumToFramebufferTarget(target); FramebufferTarget framebufferTarget = MG_Util::ConvertGLEnumToFramebufferTarget(target);
if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return; if (!FramebufferImpl::ValidateFramebufferTarget(framebufferTarget)) return;
auto framebufferObject = MG_State::pGLContext->GetFramebufferObject(framebuffer); Bool doesFramebufferCreated = MG_State::pGLContext->ValidateFramebufferObject(framebuffer);
if (!framebufferObject) { if (!doesFramebufferCreated) {
MG_State::pGLContext->CreateFramebufferObject(framebuffer); MG_State::pGLContext->CreateFramebufferObject(framebuffer);
framebufferObject = MG_State::pGLContext->GetFramebufferObject(framebuffer);
} }
auto& framebufferObject = MG_State::pGLContext->GetFramebufferObject(framebuffer);
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget); auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(framebufferTarget);
bindingSlot.Bind(framebufferObject); bindingSlot.Bind(framebufferObject);
@@ -419,11 +421,11 @@ namespace MobileGL::MG_Impl::GLImpl {
RenderbufferTarget renderbufferTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target); RenderbufferTarget renderbufferTarget = MG_Util::ConvertGLEnumToRenderbufferTarget(target);
if (!FramebufferImpl::ValidateRenderbufferTarget(renderbufferTarget)) return; if (!FramebufferImpl::ValidateRenderbufferTarget(renderbufferTarget)) return;
auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(renderbufferTarget); auto& bindingSlot = MG_State::pGLContext->GetRenderbufferBindingSlot(renderbufferTarget);
auto renderbufferObject = bindingSlot.GetBoundObject(); auto& renderbufferObject = bindingSlot.GetBoundObject();
if (!renderbufferObject) { if (!renderbufferObject) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GetRenderbufferParameteriv_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetRenderbufferParameteriv_State",
"Renderbuffer target is bound to no renderbuffer object.")); "Renderbuffer target is bound to no renderbuffer object."));
return; return;
} }
@@ -462,7 +464,7 @@ namespace MobileGL::MG_Impl::GLImpl {
default: default:
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>( MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", "GetRenderbufferParameteriv_State", "MG_Impl/GLImpl", "GetRenderbufferParameteriv_State",
std::format("pname {} is not an accepted value.", MG_Util::ConvertGLEnumToString(pname)))); std::format("pname {} is not an accepted value.", MG_Util::ConvertGLEnumToString(pname))));
return; return;
@@ -492,7 +494,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// Check width/height // Check width/height
if (width < 0 || height < 0) { if (width < 0 || height < 0) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue, MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"Width and height must be non-negative")); "Width and height must be non-negative"));
return; return;
} }
@@ -501,7 +503,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) { if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", "Invalid format")); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", "Invalid format"));
return; return;
} }
@@ -509,17 +511,17 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) { if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", "Invalid pixel data type")); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", "Invalid pixel data type"));
return; return;
} }
// Get bound framebuffer // Get bound framebuffer
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read); auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read);
auto framebufferObject = bindingSlot.GetBoundObject(); auto& framebufferObject = bindingSlot.GetBoundObject();
if (!framebufferObject) { if (!framebufferObject) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation, MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"No framebuffer bound to read target")); "No framebuffer bound to read target"));
return; return;
} }
@@ -528,7 +530,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!framebufferObject->CheckCompleteness()) { if (!framebufferObject->CheckCompleteness()) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidFramebufferOperation, ErrorCode::InvalidFramebufferOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", "Framebuffer is incomplete")); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", "Framebuffer is incomplete"));
return; return;
} }
@@ -537,7 +539,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!framebufferObject->GetAttachment(FramebufferAttachmentType::Stencil).IsValid()) { if (!framebufferObject->GetAttachment(FramebufferAttachmentType::Stencil).IsValid()) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"No stencil buffer for stencil index format")); "No stencil buffer for stencil index format"));
return; return;
} }
@@ -545,7 +547,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!framebufferObject->GetAttachment(FramebufferAttachmentType::Depth).IsValid()) { if (!framebufferObject->GetAttachment(FramebufferAttachmentType::Depth).IsValid()) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"No depth buffer for depth component format")); "No depth buffer for depth component format"));
return; return;
} }
@@ -554,7 +556,7 @@ namespace MobileGL::MG_Impl::GLImpl {
!framebufferObject->GetAttachment(FramebufferAttachmentType::Stencil).IsValid()) { !framebufferObject->GetAttachment(FramebufferAttachmentType::Stencil).IsValid()) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"No depth/stencil buffer for depth-stencil format")); "No depth/stencil buffer for depth-stencil format"));
return; return;
} }
@@ -563,7 +565,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (texturePixelDataType != TexturePixelDataType::UnsignedInt248 && if (texturePixelDataType != TexturePixelDataType::UnsignedInt248 &&
texturePixelDataType != TexturePixelDataType::Float32UnsignedInt248Rev) { texturePixelDataType != TexturePixelDataType::Float32UnsignedInt248Rev) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"Invalid type for depth-stencil format")); "Invalid type for depth-stencil format"));
return; return;
} }
@@ -577,7 +579,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// Check if PBO is mapped // Check if PBO is mapped
if (pixelPackBufferObject->IsMapped()) { if (pixelPackBufferObject->IsMapped()) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", ErrorCode::InvalidOperation, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"Pixel pack buffer is currently mapped")); "Pixel pack buffer is currently mapped"));
return; return;
} }
@@ -587,7 +589,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (reinterpret_cast<uintptr_t>(pixels) % typeSize != 0) { if (reinterpret_cast<uintptr_t>(pixels) % typeSize != 0) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"Pixel data not aligned for pixel pack buffer")); "Pixel data not aligned for pixel pack buffer"));
return; return;
} }
@@ -595,11 +597,11 @@ namespace MobileGL::MG_Impl::GLImpl {
// Check multisampling // Check multisampling
if (framebufferObject->GetAttachment(FramebufferAttachmentType::Color0).IsRenderbuffer()) { if (framebufferObject->GetAttachment(FramebufferAttachmentType::Color0).IsRenderbuffer()) {
auto rbo = framebufferObject->GetAttachment(FramebufferAttachmentType::Color0).GetRenderbuffer(); auto& rbo = framebufferObject->GetAttachment(FramebufferAttachmentType::Color0).GetRenderbuffer();
if (rbo && rbo->GetSamples() > 1) { if (rbo && rbo->GetSamples() > 1) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ReadPixels_State",
"ReadPixels not supported for multisampled framebuffers")); "ReadPixels not supported for multisampled framebuffers"));
return; return;
} }
@@ -732,6 +734,6 @@ namespace MobileGL::MG_Impl::GLImpl {
} }
namespace FramebufferImpl { namespace FramebufferImpl {
DefaultFramebufferInfo* pDefaultFramebufferInfo; UniquePtr<DefaultFramebufferInfo> pDefaultFramebufferInfo;
} // namespace FramebufferImpl } // namespace FramebufferImpl
} // namespace MobileGL::MG_Impl::GLImpl } // namespace MobileGL::MG_Impl::GLImpl
@@ -11,51 +11,49 @@
#include <Includes.h> #include <Includes.h>
#include <MG_State/GLState/Core.h> #include <MG_State/GLState/Core.h>
namespace MobileGL { namespace MobileGL::MG_Impl::GLImpl {
namespace MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */ void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels);
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value);
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value); void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value);
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value); void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value); void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width,
void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
GLsizei height); void RenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
void RenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); GLboolean IsRenderbuffer(GLuint renderbuffer);
GLboolean IsRenderbuffer(GLuint renderbuffer); void GetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params);
void GetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params); void GenRenderbuffers(GLsizei n, GLuint* renderbuffers);
void GenRenderbuffers(GLsizei n, GLuint* renderbuffers); void FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
void FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers);
void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers); void BindRenderbuffer(GLenum target, GLuint renderbuffer);
void BindRenderbuffer(GLenum target, GLuint renderbuffer); void SampleMaski(GLuint maskNumber, GLbitfield mask);
void SampleMaski(GLuint maskNumber, GLbitfield mask); GLboolean IsFramebuffer(GLuint framebuffer);
GLboolean IsFramebuffer(GLuint framebuffer); void GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params);
void GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params); void GenFramebuffers(GLsizei n, GLuint* framebuffers);
void GenFramebuffers(GLsizei n, GLuint* framebuffers); void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); void FramebufferTexture3D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level,
void FramebufferTexture3D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);
GLint zoffset); void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
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 FramebufferTexture1D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); void FramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level);
void FramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level); void DrawBuffer(GLenum buf);
void DrawBuffer(GLenum buf); void DrawBuffers(GLsizei n, const GLenum* bufs);
void DrawBuffers(GLsizei n, const GLenum* bufs); void ReadBuffer(GLenum src);
void ReadBuffer(GLenum src); void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers);
void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers); GLenum CheckFramebufferStatus(GLenum target);
GLenum CheckFramebufferStatus(GLenum target); void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
GLint dstY1, GLbitfield mask, GLenum filter); void BindFramebuffer(GLenum target, GLuint framebuffer);
void BindFramebuffer(GLenum target, GLuint framebuffer);
namespace FramebufferImpl { namespace FramebufferImpl {
struct DefaultFramebufferInfo { struct DefaultFramebufferInfo {
SharedPtr<MG_State::GLState::FramebufferObject> defaultFBO; SharedPtr<MG_State::GLState::FramebufferObject> defaultFBO;
SharedPtr<MG_State::GLState::ITextureObject> colorAttachment; SharedPtr<MG_State::GLState::ITextureObject> colorAttachment;
SharedPtr<MG_State::GLState::ITextureObject> depthAttachment; SharedPtr<MG_State::GLState::ITextureObject> depthAttachment;
SharedPtr<MG_State::GLState::ITextureObject> stencilAttachment; SharedPtr<MG_State::GLState::ITextureObject> stencilAttachment;
}; };
extern DefaultFramebufferInfo* pDefaultFramebufferInfo; extern UniquePtr<DefaultFramebufferInfo> pDefaultFramebufferInfo;
} // namespace FramebufferImpl } // namespace FramebufferImpl
} // namespace MG_Impl::GLImpl } // namespace MobileGL::MG_Impl::GLImpl
} // namespace MobileGL
@@ -13,85 +13,82 @@
#include <MG_Util/Converters/MGToGL/FramebufferEnumConverter.h> #include <MG_Util/Converters/MGToGL/FramebufferEnumConverter.h>
#include <MG_Util/Converters/MGToStr/FramebufferEnumConverter.h> #include <MG_Util/Converters/MGToStr/FramebufferEnumConverter.h>
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl {
namespace FramebufferImpl { Bool ValidateFramebufferTarget(FramebufferTarget target) {
Bool ValidateFramebufferTarget(FramebufferTarget target) { if (target == FramebufferTarget::Unknown) {
if (target == FramebufferTarget::Unknown) { using namespace MG_Util;
using namespace MG_Util; String bufferTargetStr = ConvertFramebufferTargetToString(target);
String bufferTargetStr = ConvertFramebufferTargetToString(target); String glTargetStr = ConvertGLEnumToString(ConvertFramebufferTargetToGLEnum(target));
String glTargetStr = ConvertGLEnumToString(ConvertFramebufferTargetToGLEnum(target));
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>(
"MG_Impl/GLImpl/FramebufferImpl", "ValidateFramebufferTarget",
std::format("Target {} ({}) is not valid.", bufferTargetStr, glTargetStr)));
return false;
}
return true;
}
Bool ValidateFramebufferName(Uint index, Bool allowZero) {
if (index == 0 && !allowZero) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", "ValidateFramebufferName",
"Framebuffer name 0 is not valid in this situation."));
return false;
}
Bool isValid = MG_State::pGLContext->ValidateFramebufferName(index);
if (isValid) return true;
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>(
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", "ValidateFramebufferName", "MG_Impl/GLImpl/FramebufferImpl", "ValidateFramebufferTarget",
std::format("Framebuffer name {} is not valid.", index))); std::format("Target {} ({}) is not valid.", bufferTargetStr, glTargetStr)));
return false; return false;
} }
return true;
}
Bool ValidateFramebufferAttachmentType(FramebufferAttachmentType attachment) { Bool ValidateFramebufferName(Uint index, Bool allowZero) {
if (attachment == FramebufferAttachmentType::Unknown) { if (index == 0 && !allowZero) {
using namespace MG_Util;
String attachmentStr = ConvertFramebufferAttachmentTypeToString(attachment);
String glAttachmentStr = ConvertGLEnumToString(ConvertFramebufferAttachmentTypeToGLEnum(attachment));
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>(
"MG_Impl/GLImpl/FramebufferImpl", "ValidateFramebufferAttachmentType",
std::format("Attachment type {} ({}) is not valid.", attachmentStr, glAttachmentStr)));
return false;
}
return true;
}
Bool ValidateRenderbufferTarget(RenderbufferTarget target) {
if (target == RenderbufferTarget::Unknown) {
using namespace MG_Util;
String renderbufferTargetStr = ConvertRenderbufferTargetToString(target);
String glTargetStr = ConvertGLEnumToString(ConvertRenderbufferTargetToGLEnum(target));
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>(
"MG_Impl/GLImpl/FramebufferImpl", "ValidateRenderbufferTarget",
std::format("Target {} ({}) is not valid.", renderbufferTargetStr, glTargetStr)));
return false;
}
return true;
}
Bool ValidateRenderbufferName(Uint index, Bool allowZero) {
if (index == 0 && !allowZero) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", "ValidateRenderbufferName",
"Renderbuffer name 0 is not valid in this situation."));
return false;
}
Bool isValid = MG_State::pGLContext->ValidateRenderbufferName(index);
if (isValid) return true;
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", "ValidateRenderbufferName", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", "ValidateFramebufferName",
std::format("Renderbuffer name {} is not valid.", index))); "Framebuffer name 0 is not valid in this situation."));
return false; return false;
} }
} // namespace FramebufferImpl Bool isValid = MG_State::pGLContext->ValidateFramebufferName(index);
} // namespace MobileGL::MG_Impl::GLImpl if (isValid) return true;
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", "ValidateFramebufferName",
std::format("Framebuffer name {} is not valid.", index)));
return false;
}
Bool ValidateFramebufferAttachmentType(FramebufferAttachmentType attachment) {
if (attachment == FramebufferAttachmentType::Unknown) {
using namespace MG_Util;
String attachmentStr = ConvertFramebufferAttachmentTypeToString(attachment);
String glAttachmentStr = ConvertGLEnumToString(ConvertFramebufferAttachmentTypeToGLEnum(attachment));
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl/FramebufferImpl", "ValidateFramebufferAttachmentType",
std::format("Attachment type {} ({}) is not valid.", attachmentStr, glAttachmentStr)));
return false;
}
return true;
}
Bool ValidateRenderbufferTarget(RenderbufferTarget target) {
if (target == RenderbufferTarget::Unknown) {
using namespace MG_Util;
String renderbufferTargetStr = ConvertRenderbufferTargetToString(target);
String glTargetStr = ConvertGLEnumToString(ConvertRenderbufferTargetToGLEnum(target));
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl/FramebufferImpl", "ValidateRenderbufferTarget",
std::format("Target {} ({}) is not valid.", renderbufferTargetStr, glTargetStr)));
return false;
}
return true;
}
Bool ValidateRenderbufferName(Uint index, Bool allowZero) {
if (index == 0 && !allowZero) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", "ValidateRenderbufferName",
"Renderbuffer name 0 is not valid in this situation."));
return false;
}
Bool isValid = MG_State::pGLContext->ValidateRenderbufferName(index);
if (isValid) return true;
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", "ValidateRenderbufferName",
std::format("Renderbuffer name {} is not valid.", index)));
return false;
}
} // namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl
@@ -10,12 +10,10 @@
#include <Includes.h> #include <Includes.h>
#include <MG_State/GLState/FramebufferState/FramebufferObject.h> #include <MG_State/GLState/FramebufferState/FramebufferObject.h>
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl {
namespace FramebufferImpl { Bool ValidateFramebufferTarget(FramebufferTarget target);
Bool ValidateFramebufferTarget(FramebufferTarget target); Bool ValidateFramebufferName(Uint index, Bool allowZero = true);
Bool ValidateFramebufferName(Uint index, Bool allowZero = true); Bool ValidateFramebufferAttachmentType(FramebufferAttachmentType attachment);
Bool ValidateFramebufferAttachmentType(FramebufferAttachmentType attachment); Bool ValidateRenderbufferTarget(RenderbufferTarget target);
Bool ValidateRenderbufferTarget(RenderbufferTarget target); Bool ValidateRenderbufferName(Uint index, Bool allowZero = true);
Bool ValidateRenderbufferName(Uint index, Bool allowZero = true); } // namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl
} // namespace FramebufferImpl
} // namespace MobileGL::MG_Impl::GLImpl
+15 -14
View File
@@ -7,8 +7,6 @@
// End of Source File Header // End of Source File Header
#include "GL_Getter.h" #include "GL_Getter.h"
#include "GL/gl.h"
#include "MG_Util/Debug/Log.h"
#include <Config.h> #include <Config.h>
#include <MGGitHash.h> #include <MGGitHash.h>
#include <MG_State/GLState/Core.h> #include <MG_State/GLState/Core.h>
@@ -127,7 +125,7 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!params) { if (!params) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GetIntegerv", "params pointer cannot be null")); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetIntegerv", "params pointer cannot be null"));
return; return;
} }
@@ -146,9 +144,9 @@ namespace MobileGL::MG_Impl::GLImpl {
*params = 0; // TODO *params = 0; // TODO
break; break;
case GL_ARRAY_BUFFER_BINDING: { case GL_ARRAY_BUFFER_BINDING: {
auto obj = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Vertex).GetBoundObject(); auto& obj = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Vertex).GetBoundObject();
if (obj) if (obj)
*params = obj->GetExternalIndex(); *params = (GLint)obj->GetExternalIndex();
else else
*params = 0; *params = 0;
break; break;
@@ -256,14 +254,14 @@ namespace MobileGL::MG_Impl::GLImpl {
break; break;
case GL_CURRENT_PROGRAM: { case GL_CURRENT_PROGRAM: {
const auto& currentProgram = MG_State::pGLContext->GetCurrentProgram(); const auto& currentProgram = MG_State::pGLContext->GetCurrentProgram();
*params = currentProgram ? currentProgram->GetExternalIndex() : 0; *params = currentProgram ? (GLint)currentProgram->GetExternalIndex() : 0;
break; break;
} }
case GL_DEPTH_CLEAR_VALUE: case GL_DEPTH_CLEAR_VALUE:
*params = MG_State::pGLContext->GetClearDepth(); *params = (GLint)MG_State::pGLContext->GetClearDepth();
break; break;
case GL_DEPTH_FUNC: case GL_DEPTH_FUNC:
*params = MG_Util::ConvertDepthTestFuncToGLEnum(MG_State::pGLContext->GetDepthFunc()); *params = (GLint)MG_Util::ConvertDepthTestFuncToGLEnum(MG_State::pGLContext->GetDepthFunc());
break; break;
case GL_DEPTH_RANGE: case GL_DEPTH_RANGE:
*params = 0; // TODO *params = 0; // TODO
@@ -288,12 +286,12 @@ namespace MobileGL::MG_Impl::GLImpl {
break; break;
case GL_DRAW_FRAMEBUFFER_BINDING: { case GL_DRAW_FRAMEBUFFER_BINDING: {
const auto& FBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); const auto& FBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
*params = FBO ? FBO->GetExternalIndex() : 0; *params = FBO ? (GLint)FBO->GetExternalIndex() : 0;
break; break;
} }
case GL_READ_FRAMEBUFFER_BINDING: { case GL_READ_FRAMEBUFFER_BINDING: {
const auto& FBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); const auto& FBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
*params = FBO ? FBO->GetExternalIndex() : 0; *params = FBO ? (GLint)FBO->GetExternalIndex() : 0;
break; break;
} }
case GL_ELEMENT_ARRAY_BUFFER_BINDING: { case GL_ELEMENT_ARRAY_BUFFER_BINDING: {
@@ -302,7 +300,7 @@ namespace MobileGL::MG_Impl::GLImpl {
break; break;
} }
const auto& bufferObject = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Index).GetBoundObject(); const auto& bufferObject = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Index).GetBoundObject();
*params = bufferObject ? bufferObject->GetExternalIndex() : 0; *params = bufferObject ? (GLint)bufferObject->GetExternalIndex() : 0;
break; break;
} }
case GL_FRAGMENT_SHADER_DERIVATIVE_HINT: case GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
@@ -886,7 +884,7 @@ namespace MobileGL::MG_Impl::GLImpl {
default: default:
MGLOG_E("glGetIntegerv: Invalid enum %s (0x%X)", MG_Util::ConvertGLEnumToString(pname).c_str(), pname); MGLOG_E("glGetIntegerv: Invalid enum %s (0x%X)", MG_Util::ConvertGLEnumToString(pname).c_str(), pname);
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum, MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GetIntegerv", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetIntegerv",
std::format("Invalid enum: 0x{:X}", pname))); std::format("Invalid enum: 0x{:X}", pname)));
break; break;
@@ -894,7 +892,10 @@ namespace MobileGL::MG_Impl::GLImpl {
} }
GLenum GetError() { GLenum GetError() {
ErrorCode errorCode = MG_State::pGLContext->PopGLError().value_or(Error{ErrorCode::NoError, nullptr}).code; auto error = MG_State::pGLContext->PopGLError();
return MG_Util::ConvertErrorCodeToGLEnum(errorCode); if (!error || !error->get()) {
return GL_NO_ERROR;
}
return MG_Util::ConvertErrorCodeToGLEnum(error->get()->code);
} }
} // namespace MobileGL::MG_Impl::GLImpl } // namespace MobileGL::MG_Impl::GLImpl
File diff suppressed because it is too large Load Diff
+56 -58
View File
@@ -9,61 +9,59 @@
#pragma once #pragma once
#include <Includes.h> #include <Includes.h>
namespace MobileGL { namespace MobileGL::MG_Impl::GLImpl {
namespace MG_Impl::GLImpl { void AttachShader(GLuint program, GLuint shader);
void AttachShader(GLuint program, GLuint shader); void BindAttribLocation(GLuint program, GLuint index, const GLchar* name);
void BindAttribLocation(GLuint program, GLuint index, const GLchar* name); void CompileShader(GLuint shader);
void CompileShader(GLuint shader); GLuint CreateProgram(void);
GLuint CreateProgram(void); GLuint CreateShader(GLenum type);
GLuint CreateShader(GLenum type); void DeleteProgram(GLuint program);
void DeleteProgram(GLuint program); void DeleteShader(GLuint shader);
void DeleteShader(GLuint shader); void DetachShader(GLuint program, GLuint shader);
void DetachShader(GLuint program, GLuint shader); void GetActiveAttrib(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLint* size, GLenum* type,
void GetActiveAttrib(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLint* size, GLenum* type, GLchar* name);
GLchar* name); void GetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLint* size, GLenum* type,
void GetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLint* size, GLenum* type, GLchar* name);
GLchar* name); void GetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders);
void GetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders); GLint GetAttribLocation(GLuint program, const GLchar* name);
GLint GetAttribLocation(GLuint program, const GLchar* name); void GetProgramiv(GLuint program, GLenum pname, GLint* params);
void GetProgramiv(GLuint program, GLenum pname, GLint* params); void GetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
void GetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog); void GetShaderiv(GLuint shader, GLenum pname, GLint* params);
void GetShaderiv(GLuint shader, GLenum pname, GLint* params); void GetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
void GetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog); void GetShaderSource(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* source);
void GetShaderSource(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* source); GLint GetUniformLocation(GLuint program, const GLchar* name);
GLint GetUniformLocation(GLuint program, const GLchar* name); void GetUniformfv(GLuint program, GLint location, GLfloat* params);
void GetUniformfv(GLuint program, GLint location, GLfloat* params); void GetUniformiv(GLuint program, GLint location, GLint* params);
void GetUniformiv(GLuint program, GLint location, GLint* params); GLboolean IsProgram(GLuint program);
GLboolean IsProgram(GLuint program); GLboolean IsShader(GLuint shader);
GLboolean IsShader(GLuint shader); void LinkProgram(GLuint program);
void LinkProgram(GLuint program); void ShaderSource(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length);
void ShaderSource(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length); void UseProgram(GLuint program);
void UseProgram(GLuint program); void Uniform1f(GLint location, GLfloat v0);
void Uniform1f(GLint location, GLfloat v0); void Uniform2f(GLint location, GLfloat v0, GLfloat v1);
void Uniform2f(GLint location, GLfloat v0, GLfloat v1); void Uniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
void Uniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); void Uniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
void Uniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); void Uniform1i(GLint location, GLint v0);
void Uniform1i(GLint location, GLint v0); void Uniform2i(GLint location, GLint v0, GLint v1);
void Uniform2i(GLint location, GLint v0, GLint v1); void Uniform3i(GLint location, GLint v0, GLint v1, GLint v2);
void Uniform3i(GLint location, GLint v0, GLint v1, GLint v2); void Uniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
void Uniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); void Uniform1fv(GLint location, GLsizei count, const GLfloat* value);
void Uniform1fv(GLint location, GLsizei count, const GLfloat* value); void Uniform2fv(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);
void Uniform3fv(GLint location, GLsizei count, const GLfloat* value); void Uniform4fv(GLint location, GLsizei count, const GLfloat* value);
void Uniform4fv(GLint location, GLsizei count, const GLfloat* value); void Uniform1iv(GLint location, GLsizei count, const GLint* value);
void Uniform1iv(GLint location, GLsizei count, const GLint* value); void Uniform2iv(GLint location, GLsizei count, const GLint* value);
void Uniform2iv(GLint location, GLsizei count, const GLint* value); void Uniform3iv(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 Uniform4iv(GLint location, GLsizei count, const GLint* value); void UniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
void UniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); void UniformMatrix3fv(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);
void UniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); GLuint GetUniformBlockIndex(GLuint program, const GLchar* uniformBlockName);
GLuint GetUniformBlockIndex(GLuint program, const GLchar* uniformBlockName); void UniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
void UniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); void GetActiveUniformBlockiv(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params);
void GetActiveUniformBlockiv(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params); void GetActiveUniformBlockName(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length,
void GetActiveUniformBlockName(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformBlockName);
GLchar* uniformBlockName); void BindFragDataLocation(GLuint program, GLuint colorNumber, const char* name);
void BindFragDataLocation(GLuint program, GLuint colorNumber, const char* name); GLint GetFragDataLocation(GLuint program, const char* name);
GLint GetFragDataLocation(GLuint program, const char* name); void ValidateProgram(GLuint program);
void ValidateProgram(GLuint program); } // namespace MobileGL::MG_Impl::GLImpl
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
@@ -7,474 +7,467 @@
// End of Source File Header // End of Source File Header
#include "GL_RenderState.h" #include "GL_RenderState.h"
#include "MG_State/GLState/RenderState/RenderState.h"
#include "MG_Util/Converters/GLToStr/GLEnumConverter.h"
#include <MG_State/GLState/Core.h> #include <MG_State/GLState/Core.h>
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
#include <MG_Util/Converters/GLToMG/RenderStateEnumConverter.h> #include <MG_Util/Converters/GLToMG/RenderStateEnumConverter.h>
#include <MG_Util/Converters/MGToGL/RenderStateEnumConverter.h> #include <MG_Util/Converters/MGToGL/RenderStateEnumConverter.h>
#include <MG_Util/Converters/MGToStr/RenderStateEnumConverter.h> #include <MG_Util/Converters/MGToStr/RenderStateEnumConverter.h>
namespace MobileGL { namespace MobileGL::MG_Impl::GLImpl {
namespace MG_Impl::GLImpl { void Viewport_State(GLint x, GLint y, GLsizei width, GLsizei height) {
void Viewport_State(GLint x, GLint y, GLsizei width, GLsizei height) { if (width < 0 || height < 0) {
if (width < 0 || height < 0) { MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MG_State::pGLContext->RecordError( MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "Viewport_State",
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "Viewport_State", "Width abd height must be non-negative."));
"Width abd height must be non-negative.")); return;
return; }
}
MG_State::pGLContext->SetViewport(IntVec4(x, y, width, height));
MG_State::pGLContext->SetViewport(IntVec4(x, y, width, height)); }
}
void StencilOpSeparate_State(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) {
void StencilOpSeparate_State(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) { // TODO: implement
// TODO: implement }
}
void StencilOp_State(GLenum fail, GLenum zfail, GLenum zpass) {
void StencilOp_State(GLenum fail, GLenum zfail, GLenum zpass) { // TODO: implement
// TODO: implement }
}
void StencilMaskSeparate_State(GLenum face, GLuint mask) {
void StencilMaskSeparate_State(GLenum face, GLuint mask) { // TODO: implement
// TODO: implement }
}
void StencilMask_State(GLuint mask) {
void StencilMask_State(GLuint mask) { // TODO: implement
// TODO: implement }
}
void StencilFuncSeparate_State(GLenum face, GLenum func, GLint ref, GLuint mask) {
void StencilFuncSeparate_State(GLenum face, GLenum func, GLint ref, GLuint mask) { // TODO: implement
// TODO: implement }
}
void StencilFunc_State(GLenum func, GLint ref, GLuint mask) {
void StencilFunc_State(GLenum func, GLint ref, GLuint mask) { // TODO: implement
// TODO: implement }
}
void Scissor_State(GLint x, GLint y, GLsizei width, GLsizei height) {
void Scissor_State(GLint x, GLint y, GLsizei width, GLsizei height) { if (width < 0 || height < 0) {
if (width < 0 || height < 0) { MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MG_State::pGLContext->RecordError( MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "Scissor_State",
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "Scissor_State", "Width abd height must be non-negative."));
"Width abd height must be non-negative.")); return;
return; }
}
MG_State::pGLContext->SetScissorBox(IntVec4(x, y, width, height));
MG_State::pGLContext->SetScissorBox(IntVec4(x, y, width, height)); }
}
void SampleCoverage_State(GLfloat value, GLboolean invert) {
void SampleCoverage_State(GLfloat value, GLboolean invert) { // TODO: implement
// TODO: implement }
}
void PolygonOffset_State(GLfloat factor, GLfloat units) {
void PolygonOffset_State(GLfloat factor, GLfloat units) { // TODO: implement
// TODO: implement }
}
void PolygonMode_State(GLenum face, GLenum mode) {
void PolygonMode_State(GLenum face, GLenum mode) { // TODO: implement
// TODO: implement }
}
void PointSize_State(GLfloat size) {
void PointSize_State(GLfloat size) { // TODO: implement
// TODO: implement }
}
void PointParameterf_State(GLenum pname, GLfloat param) {
void PointParameterf_State(GLenum pname, GLfloat param) { // TODO: implement
// TODO: implement }
}
void PointParameteri_State(GLenum pname, GLint param) {
void PointParameteri_State(GLenum pname, GLint param) { // TODO: implement
// TODO: implement }
}
void PixelStorei_State(GLenum pname, GLint param) {
void PixelStorei_State(GLenum pname, GLint param) { MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), param);
MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), param); PixelStoreParam pixelStoreParam = MG_Util::ConvertGLEnumToPixelStoreParam(pname);
PixelStoreParam pixelStoreParam = MG_Util::ConvertGLEnumToPixelStoreParam(pname); if (pixelStoreParam == PixelStoreParam::Unknown) {
if (pixelStoreParam == PixelStoreParam::Unknown) { MG_State::pGLContext->RecordError(
MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum,
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "PixelStorei_State",
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "PixelStorei_State", "Pixel store param enum " +
"Pixel store param enum " + MG_Util::ConvertPixelStoreParamToString(pixelStoreParam) + "(" +
MG_Util::ConvertPixelStoreParamToString(pixelStoreParam) + "(" + MG_Util::ConvertGLEnumToString(pname) + ") is not supported."));
MG_Util::ConvertGLEnumToString(pname) + ") is not supported.")); return;
return; }
}
MG_State::pGLContext->SetPixelStoreParam(pixelStoreParam, param);
MG_State::pGLContext->SetPixelStoreParam(pixelStoreParam, param); }
}
void LogicOp_State(GLenum opcode) {
void LogicOp_State(GLenum opcode) { // TODO: implement
// TODO: implement }
}
void LineWidth_State(GLfloat width) {
void LineWidth_State(GLfloat width) { // TODO: implement
// TODO: implement }
}
GLboolean IsEnabledi_State(GLenum target, GLuint index) {
GLboolean IsEnabledi_State(GLenum target, GLuint index) { CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(target);
CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(target); if (capInput == CapabilityInput::Unknown) {
if (capInput == CapabilityInput::Unknown) { MG_State::pGLContext->RecordError(
MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum,
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "IsEnabledi_State",
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "IsEnabledi_State", "Capability enum " + MG_Util::ConvertCapabilityInputToString(capInput) +
"Capability enum " + "(" + MG_Util::ConvertGLEnumToString(target) + ") is not supported."));
MG_Util::ConvertCapabilityInputToString(capInput) + "(" + return GL_FALSE;
MG_Util::ConvertGLEnumToString(target) + ") is not supported.")); }
return GL_FALSE;
} return MG_State::pGLContext->IsCapabilityEnabledIndexed(capInput, index) ? GL_TRUE : GL_FALSE;
}
return MG_State::pGLContext->IsCapabilityEnabledIndexed(capInput, index) ? GL_TRUE : GL_FALSE;
} GLboolean IsEnabled_State(GLenum cap) {
CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(cap);
GLboolean IsEnabled_State(GLenum cap) { if (capInput == CapabilityInput::Unknown) {
CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(cap); MG_State::pGLContext->RecordError(
if (capInput == CapabilityInput::Unknown) { ErrorCode::InvalidEnum,
MG_State::pGLContext->RecordError( MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "IsEnabled_State",
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>( "Capability enum " + MG_Util::ConvertCapabilityInputToString(capInput) +
"MG_Impl/GLImpl", "IsEnabled_State", "(" + MG_Util::ConvertGLEnumToString(cap) + ") is not supported."));
"Capability enum " + MG_Util::ConvertCapabilityInputToString(capInput) + return GL_FALSE;
"(" + MG_Util::ConvertGLEnumToString(cap) + ") is not supported.")); }
return GL_FALSE;
} return MG_State::pGLContext->IsCapabilityEnabled(capInput) ? GL_TRUE : GL_FALSE;
}
return MG_State::pGLContext->IsCapabilityEnabled(capInput) ? GL_TRUE : GL_FALSE;
} void Hint_State(GLenum target, GLenum mode) {
// TODO: implement
void Hint_State(GLenum target, GLenum mode) { }
// TODO: implement
} void FrontFace_State(GLenum mode) {
// TODO: implement
void FrontFace_State(GLenum mode) { }
// TODO: implement
} void Enable_State(GLenum cap) {
CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(cap);
void Enable_State(GLenum cap) { if (capInput == CapabilityInput::Unknown) {
CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(cap); MG_State::pGLContext->RecordError(
if (capInput == CapabilityInput::Unknown) { ErrorCode::InvalidEnum,
MG_State::pGLContext->RecordError( MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "Enable_State",
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>( "Capability enum " + MG_Util::ConvertCapabilityInputToString(capInput) +
"MG_Impl/GLImpl", "Enable_State", "(" + MG_Util::ConvertGLEnumToString(cap) + ") is not supported."));
"Capability enum " + MG_Util::ConvertCapabilityInputToString(capInput) + return;
"(" + MG_Util::ConvertGLEnumToString(cap) + ") is not supported.")); }
return;
} MG_State::pGLContext->SetCapability(capInput, true);
}
MG_State::pGLContext->SetCapability(capInput, true);
} void Disable_State(GLenum cap) {
CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(cap);
void Disable_State(GLenum cap) { if (capInput == CapabilityInput::Unknown) {
CapabilityInput capInput = MG_Util::ConvertGLEnumToCapabilityInput(cap); MG_State::pGLContext->RecordError(
if (capInput == CapabilityInput::Unknown) { ErrorCode::InvalidEnum,
MG_State::pGLContext->RecordError( MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "Disable_State",
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>( "Capability enum " + MG_Util::ConvertCapabilityInputToString(capInput) +
"MG_Impl/GLImpl", "Disable_State", "(" + MG_Util::ConvertGLEnumToString(cap) + ") is not supported."));
"Capability enum " + MG_Util::ConvertCapabilityInputToString(capInput) + return;
"(" + MG_Util::ConvertGLEnumToString(cap) + ") is not supported.")); }
return;
} MG_State::pGLContext->SetCapability(capInput, false);
}
MG_State::pGLContext->SetCapability(capInput, false);
} void DepthRange_State(GLclampd near_val, GLclampd far_val) {
// TODO: implement
void DepthRange_State(GLclampd near_val, GLclampd far_val) { }
// TODO: implement
} void DepthMask_State(GLboolean flag) {
MG_State::pGLContext->SetDepthMask(flag == GL_TRUE);
void DepthMask_State(GLboolean flag) { }
MG_State::pGLContext->SetDepthMask(flag == GL_TRUE);
} void DepthFunc_State(GLenum func) {
DepthTestFunc depthFunc = MG_Util::ConvertGLEnumToDepthTestFunc(func);
void DepthFunc_State(GLenum func) { if (depthFunc == DepthTestFunc::Unknown) {
DepthTestFunc depthFunc = MG_Util::ConvertGLEnumToDepthTestFunc(func); MG_State::pGLContext->RecordError(
if (depthFunc == DepthTestFunc::Unknown) { ErrorCode::InvalidEnum,
MG_State::pGLContext->RecordError( MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "DepthFunc_State",
ErrorCode::InvalidEnum, "Depth function enum " + MG_Util::ConvertDepthTestFuncToString(depthFunc) +
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DepthFunc_State", "(" + MG_Util::ConvertGLEnumToString(func) + ") is not supported."));
"Depth function enum " + return;
MG_Util::ConvertDepthTestFuncToString(depthFunc) + "(" + }
MG_Util::ConvertGLEnumToString(func) + ") is not supported."));
return; MG_State::pGLContext->SetDepthFunc(depthFunc);
} }
MG_State::pGLContext->SetDepthFunc(depthFunc); void CullFace_State(GLenum mode) {
} CullFaceMode cullFaceMode = MG_Util::ConvertGLEnumToCullFaceMode(mode);
if (cullFaceMode == CullFaceMode::Unknown) {
void CullFace_State(GLenum mode) { MG_State::pGLContext->RecordError(
CullFaceMode cullFaceMode = MG_Util::ConvertGLEnumToCullFaceMode(mode); ErrorCode::InvalidEnum,
if (cullFaceMode == CullFaceMode::Unknown) { MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "CullFace_State",
MG_State::pGLContext->RecordError( "Cull face mode enum " +
ErrorCode::InvalidEnum, MG_Util::ConvertCullFaceModeToString(cullFaceMode) + "(" +
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "CullFace_State", MG_Util::ConvertGLEnumToString(mode) + ") is not supported."));
"Cull face mode enum " + return;
MG_Util::ConvertCullFaceModeToString(cullFaceMode) + "(" + }
MG_Util::ConvertGLEnumToString(mode) + ") is not supported."));
return; MG_State::pGLContext->SetCullFaceMode(cullFaceMode);
} }
MG_State::pGLContext->SetCullFaceMode(cullFaceMode); void ColorMask_State(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {
} MG_State::pGLContext->SetColorMask(
BoolVec4(red == GL_TRUE, green == GL_TRUE, blue == GL_TRUE, alpha == GL_TRUE));
void ColorMask_State(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) { }
MG_State::pGLContext->SetColorMask(
BoolVec4(red == GL_TRUE, green == GL_TRUE, blue == GL_TRUE, alpha == GL_TRUE)); void ClampColor_State(GLenum target, GLenum clamp) {
} // TODO: implement
}
void ClampColor_State(GLenum target, GLenum clamp) {
// TODO: implement void BlendFuncSeparate_State(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) {
} BlendFactor srcRGB = MG_Util::ConvertGLEnumToBlendFactor(sfactorRGB);
BlendFactor dstRGB = MG_Util::ConvertGLEnumToBlendFactor(dfactorRGB);
void BlendFuncSeparate_State(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) { BlendFactor srcAlpha = MG_Util::ConvertGLEnumToBlendFactor(sfactorAlpha);
BlendFactor srcRGB = MG_Util::ConvertGLEnumToBlendFactor(sfactorRGB); BlendFactor dstAlpha = MG_Util::ConvertGLEnumToBlendFactor(dfactorAlpha);
BlendFactor dstRGB = MG_Util::ConvertGLEnumToBlendFactor(dfactorRGB);
BlendFactor srcAlpha = MG_Util::ConvertGLEnumToBlendFactor(sfactorAlpha); if (srcRGB == BlendFactor::Unknown || dstRGB == BlendFactor::Unknown || srcAlpha == BlendFactor::Unknown ||
BlendFactor dstAlpha = MG_Util::ConvertGLEnumToBlendFactor(dfactorAlpha); dstAlpha == BlendFactor::Unknown) {
MG_State::pGLContext->RecordError(
if (srcRGB == BlendFactor::Unknown || dstRGB == BlendFactor::Unknown || srcAlpha == BlendFactor::Unknown || ErrorCode::InvalidEnum,
dstAlpha == BlendFactor::Unknown) { MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "BlendFuncSeparate_State",
MG_State::pGLContext->RecordError( "One of the blend factor enums is not supported: srcRGB " +
ErrorCode::InvalidEnum, MG_Util::ConvertBlendFactorToString(srcRGB) + "(" +
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "BlendFuncSeparate_State", MG_Util::ConvertGLEnumToString(sfactorRGB) + "), dstRGB " +
"One of the blend factor enums is not supported: srcRGB " + MG_Util::ConvertBlendFactorToString(dstRGB) + "(" +
MG_Util::ConvertBlendFactorToString(srcRGB) + "(" + MG_Util::ConvertGLEnumToString(dfactorRGB) + "), srcAlpha " +
MG_Util::ConvertGLEnumToString(sfactorRGB) + "), dstRGB " + MG_Util::ConvertBlendFactorToString(srcAlpha) + "(" +
MG_Util::ConvertBlendFactorToString(dstRGB) + "(" + MG_Util::ConvertGLEnumToString(sfactorAlpha) + "), dstAlpha " +
MG_Util::ConvertGLEnumToString(dfactorRGB) + "), srcAlpha " + MG_Util::ConvertBlendFactorToString(dstAlpha) + "(" +
MG_Util::ConvertBlendFactorToString(srcAlpha) + "(" + MG_Util::ConvertGLEnumToString(dfactorAlpha) + ")."));
MG_Util::ConvertGLEnumToString(sfactorAlpha) + "), dstAlpha " + return;
MG_Util::ConvertBlendFactorToString(dstAlpha) + "(" + }
MG_Util::ConvertGLEnumToString(dfactorAlpha) + ")."));
return; MG_State::pGLContext->SetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha);
} }
MG_State::pGLContext->SetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha); void BlendFunc_State(GLenum sfactor, GLenum dfactor) {
} BlendFuncSeparate_State(sfactor, dfactor, sfactor, dfactor);
}
void BlendFunc_State(GLenum sfactor, GLenum dfactor) {
BlendFuncSeparate_State(sfactor, dfactor, sfactor, dfactor); void BlendEquation_State(GLenum mode) {
} // TODO: implement
}
void BlendEquation_State(GLenum mode) {
// TODO: implement void BlendColor_State(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
} // TODO: implement
}
void BlendColor_State(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
// TODO: implement void ClearStencil_State(GLint s) {
} MG_State::pGLContext->SetClearStencil(static_cast<Int>(s));
}
void ClearStencil_State(GLint s) {
MG_State::pGLContext->SetClearStencil(static_cast<Int>(s)); void ClearDepth_State(GLclampd depth) {
} MG_State::pGLContext->SetClearDepth(static_cast<Float>(depth));
}
void ClearDepth_State(GLclampd depth) {
MG_State::pGLContext->SetClearDepth(static_cast<Float>(depth)); void ClearColor_State(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
} MG_State::pGLContext->SetClearColor(FloatVec4(red, green, blue, alpha));
}
void BlendFuncSeparatei_State(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {
if (buf >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", "BlendFuncSeparatei_State",
"Buffer index " + std::to_string(buf) + " is out of range. Max supported is " +
std::to_string(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS - 1) + "."));
return;
}
BlendFactor srcRGBM = MG_Util::ConvertGLEnumToBlendFactor(srcRGB);
BlendFactor dstRGBM = MG_Util::ConvertGLEnumToBlendFactor(dstRGB);
BlendFactor srcAlphaM = MG_Util::ConvertGLEnumToBlendFactor(srcAlpha);
BlendFactor dstAlphaM = MG_Util::ConvertGLEnumToBlendFactor(dstAlpha);
MG_State::pGLContext->SetBlendFuncIndexed(buf, srcRGBM, dstRGBM, srcAlphaM, dstAlphaM);
}
void Disablei_State(GLenum target, GLuint index) {
auto capInput = MG_Util::ConvertGLEnumToCapabilityInput(target);
if (capInput == CapabilityInput::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "Disablei_State",
"Capability enum " + MG_Util::ConvertCapabilityInputToString(capInput) +
"(" + MG_Util::ConvertGLEnumToString(target) + ") is not supported."));
return;
}
MG_State::pGLContext->SetCapabilityIndexed(capInput, index, false);
}
void Enablei_State(GLenum target, GLuint index) {
auto capInput = MG_Util::ConvertGLEnumToCapabilityInput(target);
if (capInput == CapabilityInput::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "Enablei_State",
"Capability enum " + MG_Util::ConvertCapabilityInputToString(capInput) +
"(" + MG_Util::ConvertGLEnumToString(target) + ") is not supported."));
return;
}
MG_State::pGLContext->SetCapabilityIndexed(capInput, index, true);
}
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
void BlendFuncSeparatei(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {
BlendFuncSeparatei_State(buf, srcRGB, dstRGB, srcAlpha, dstAlpha);
}
void Disablei(GLenum target, GLuint index) {
Disablei_State(target, index);
}
void Enablei(GLenum target, GLuint index) {
Enablei_State(target, index);
}
void BlendFunc(GLenum sfactor, GLenum dfactor) {
BlendFunc_State(sfactor, dfactor);
}
void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) {
Viewport_State(x, y, width, height);
}
void StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) {
StencilOpSeparate_State(face, sfail, dpfail, dppass);
}
void StencilOp(GLenum fail, GLenum zfail, GLenum zpass) {
StencilOp_State(fail, zfail, zpass);
}
void StencilMaskSeparate(GLenum face, GLuint mask) {
StencilMaskSeparate_State(face, mask);
}
void StencilMask(GLuint mask) {
StencilMask_State(mask);
}
void StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) {
StencilFuncSeparate_State(face, func, ref, mask);
}
void StencilFunc(GLenum func, GLint ref, GLuint mask) {
StencilFunc_State(func, ref, mask);
}
void Scissor(GLint x, GLint y, GLsizei width, GLsizei height) {
Scissor_State(x, y, width, height);
}
void SampleCoverage(GLfloat value, GLboolean invert) {
SampleCoverage_State(value, invert);
}
void PolygonOffset(GLfloat factor, GLfloat units) {
PolygonOffset_State(factor, units);
}
void PolygonMode(GLenum face, GLenum mode) {
PolygonMode_State(face, mode);
}
void PointSize(GLfloat size) {
PointSize_State(size);
}
void PointParameterf(GLenum pname, GLfloat param) {
PointParameterf_State(pname, param);
}
void PointParameteri(GLenum pname, GLint param) {
PointParameteri_State(pname, param);
}
void PixelStorei(GLenum pname, GLint param) {
PixelStorei_State(pname, param);
}
void LogicOp(GLenum opcode) {
LogicOp_State(opcode);
}
void ClearColor_State(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { void LineWidth(GLfloat width) {
MG_State::pGLContext->SetClearColor(FloatVec4(red, green, blue, alpha)); LineWidth_State(width);
} }
void BlendFuncSeparatei_State(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { GLboolean IsEnabledi(GLenum target, GLuint index) {
if (buf >= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS) { return IsEnabledi_State(target, index);
MG_State::pGLContext->RecordError( }
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>( GLboolean IsEnabled(GLenum cap) {
"MG_Impl/GLImpl", "BlendFuncSeparatei_State", return IsEnabled_State(cap);
"Buffer index " + std::to_string(buf) + " is out of range. Max supported is " + }
std::to_string(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS - 1) + "."));
return;
}
BlendFactor srcRGBM = MG_Util::ConvertGLEnumToBlendFactor(srcRGB);
BlendFactor dstRGBM = MG_Util::ConvertGLEnumToBlendFactor(dstRGB);
BlendFactor srcAlphaM = MG_Util::ConvertGLEnumToBlendFactor(srcAlpha);
BlendFactor dstAlphaM = MG_Util::ConvertGLEnumToBlendFactor(dstAlpha);
MG_State::pGLContext->SetBlendFuncIndexed(buf, srcRGBM, dstRGBM, srcAlphaM, dstAlphaM);
}
void Disablei_State(GLenum target, GLuint index) {
auto capInput = MG_Util::ConvertGLEnumToCapabilityInput(target);
if (capInput == CapabilityInput::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "Disablei_State",
"Capability enum " +
MG_Util::ConvertCapabilityInputToString(capInput) + "(" +
MG_Util::ConvertGLEnumToString(target) + ") is not supported."));
return;
}
MG_State::pGLContext->SetCapabilityIndexed(capInput, index, false);
}
void Enablei_State(GLenum target, GLuint index) {
auto capInput = MG_Util::ConvertGLEnumToCapabilityInput(target);
if (capInput == CapabilityInput::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "Enablei_State",
"Capability enum " +
MG_Util::ConvertCapabilityInputToString(capInput) + "(" +
MG_Util::ConvertGLEnumToString(target) + ") is not supported."));
return;
}
MG_State::pGLContext->SetCapabilityIndexed(capInput, index, true);
}
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
void BlendFuncSeparatei(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {
BlendFuncSeparatei_State(buf, srcRGB, dstRGB, srcAlpha, dstAlpha);
}
void Disablei(GLenum target, GLuint index) {
Disablei_State(target, index);
}
void Enablei(GLenum target, GLuint index) {
Enablei_State(target, index);
}
void BlendFunc(GLenum sfactor, GLenum dfactor) {
BlendFunc_State(sfactor, dfactor);
}
void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) {
Viewport_State(x, y, width, height);
}
void StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) {
StencilOpSeparate_State(face, sfail, dpfail, dppass);
}
void StencilOp(GLenum fail, GLenum zfail, GLenum zpass) {
StencilOp_State(fail, zfail, zpass);
}
void StencilMaskSeparate(GLenum face, GLuint mask) {
StencilMaskSeparate_State(face, mask);
}
void StencilMask(GLuint mask) {
StencilMask_State(mask);
}
void StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) {
StencilFuncSeparate_State(face, func, ref, mask);
}
void StencilFunc(GLenum func, GLint ref, GLuint mask) {
StencilFunc_State(func, ref, mask);
}
void Scissor(GLint x, GLint y, GLsizei width, GLsizei height) {
Scissor_State(x, y, width, height);
}
void SampleCoverage(GLfloat value, GLboolean invert) {
SampleCoverage_State(value, invert);
}
void PolygonOffset(GLfloat factor, GLfloat units) {
PolygonOffset_State(factor, units);
}
void PolygonMode(GLenum face, GLenum mode) { void Hint(GLenum target, GLenum mode) {
PolygonMode_State(face, mode); Hint_State(target, mode);
} }
void PointSize(GLfloat size) { void FrontFace(GLenum mode) {
PointSize_State(size); FrontFace_State(mode);
} }
void Enable(GLenum cap) {
Enable_State(cap);
}
void PointParameterf(GLenum pname, GLfloat param) { void Disable(GLenum cap) {
PointParameterf_State(pname, param); Disable_State(cap);
} }
void PointParameteri(GLenum pname, GLint param) { void DepthRange(GLclampd near_val, GLclampd far_val) {
PointParameteri_State(pname, param); DepthRange_State(near_val, far_val);
} }
void PixelStorei(GLenum pname, GLint param) { void DepthMask(GLboolean flag) {
PixelStorei_State(pname, param); DepthMask_State(flag);
} }
void LogicOp(GLenum opcode) {
LogicOp_State(opcode);
}
void LineWidth(GLfloat width) {
LineWidth_State(width);
}
GLboolean IsEnabledi(GLenum target, GLuint index) { void DepthFunc(GLenum func) {
return IsEnabledi_State(target, index); DepthFunc_State(func);
} }
GLboolean IsEnabled(GLenum cap) { void CullFace(GLenum mode) {
return IsEnabled_State(cap); CullFace_State(mode);
} }
void Hint(GLenum target, GLenum mode) { void ColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {
Hint_State(target, mode); ColorMask_State(red, green, blue, alpha);
} }
void FrontFace(GLenum mode) { void ClampColor(GLenum target, GLenum clamp) {
FrontFace_State(mode); ClampColor_State(target, clamp);
} }
void Enable(GLenum cap) { void BlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) {
Enable_State(cap); BlendFuncSeparate_State(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
} }
void Disable(GLenum cap) { void BlendEquation(GLenum mode) {
Disable_State(cap); BlendEquation_State(mode);
} }
void DepthRange(GLclampd near_val, GLclampd far_val) { void BlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
DepthRange_State(near_val, far_val); BlendColor_State(red, green, blue, alpha);
} }
void DepthMask(GLboolean flag) { void ClearStencil(GLint s) {
DepthMask_State(flag); ClearStencil_State(s);
} }
void DepthFunc(GLenum func) { void ClearDepth(GLclampd depth) {
DepthFunc_State(func); ClearDepth_State(depth);
} }
void CullFace(GLenum mode) { void ClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
CullFace_State(mode); ClearColor_State(red, green, blue, alpha);
} }
} // namespace MobileGL::MG_Impl::GLImpl
void ColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {
ColorMask_State(red, green, blue, alpha);
}
void ClampColor(GLenum target, GLenum clamp) {
ClampColor_State(target, clamp);
}
void BlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) {
BlendFuncSeparate_State(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
}
void BlendEquation(GLenum mode) {
BlendEquation_State(mode);
}
void BlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
BlendColor_State(red, green, blue, alpha);
}
void ClearStencil(GLint s) {
ClearStencil_State(s);
}
void ClearDepth(GLclampd depth) {
ClearDepth_State(depth);
}
void ClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
ClearColor_State(red, green, blue, alpha);
}
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
@@ -9,47 +9,45 @@
#pragma once #pragma once
#include <Includes.h> #include <Includes.h>
namespace MobileGL { namespace MobileGL::MG_Impl::GLImpl {
namespace MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */ void BlendFuncSeparatei(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
void BlendFuncSeparatei(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); void Disablei(GLenum target, GLuint index);
void Disablei(GLenum target, GLuint index); void Enablei(GLenum target, GLuint index);
void Enablei(GLenum target, GLuint index); void BlendFunc(GLenum sfactor, GLenum dfactor);
void BlendFunc(GLenum sfactor, GLenum dfactor); void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);
void Viewport(GLint x, GLint y, GLsizei width, GLsizei height); void StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
void StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); void StencilOp(GLenum fail, GLenum zfail, GLenum zpass);
void StencilOp(GLenum fail, GLenum zfail, GLenum zpass); void StencilMaskSeparate(GLenum face, GLuint mask);
void StencilMaskSeparate(GLenum face, GLuint mask); void StencilMask(GLuint mask);
void StencilMask(GLuint mask); void StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
void StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask); void StencilFunc(GLenum func, GLint ref, GLuint mask);
void StencilFunc(GLenum func, GLint ref, GLuint mask); void Scissor(GLint x, GLint y, GLsizei width, GLsizei height);
void Scissor(GLint x, GLint y, GLsizei width, GLsizei height); void SampleCoverage(GLfloat value, GLboolean invert);
void SampleCoverage(GLfloat value, GLboolean invert); void PolygonOffset(GLfloat factor, GLfloat units);
void PolygonOffset(GLfloat factor, GLfloat units); void PolygonMode(GLenum face, GLenum mode);
void PolygonMode(GLenum face, GLenum mode); void PointSize(GLfloat size);
void PointSize(GLfloat size); void PointParameterf(GLenum pname, GLfloat param);
void PointParameterf(GLenum pname, GLfloat param); void PointParameteri(GLenum pname, GLint param);
void PointParameteri(GLenum pname, GLint param); void PixelStorei(GLenum pname, GLint param);
void PixelStorei(GLenum pname, GLint param); void LogicOp(GLenum opcode);
void LogicOp(GLenum opcode); void LineWidth(GLfloat width);
void LineWidth(GLfloat width); GLboolean IsEnabledi(GLenum target, GLuint index);
GLboolean IsEnabledi(GLenum target, GLuint index); GLboolean IsEnabled(GLenum cap);
GLboolean IsEnabled(GLenum cap); void Hint(GLenum target, GLenum mode);
void Hint(GLenum target, GLenum mode); void FrontFace(GLenum mode);
void FrontFace(GLenum mode); void Enable(GLenum cap);
void Enable(GLenum cap); void Disable(GLenum cap);
void Disable(GLenum cap); void DepthRange(GLclampd near_val, GLclampd far_val);
void DepthRange(GLclampd near_val, GLclampd far_val); void DepthMask(GLboolean flag);
void DepthMask(GLboolean flag); void DepthFunc(GLenum func);
void DepthFunc(GLenum func); void CullFace(GLenum mode);
void CullFace(GLenum mode); void ColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
void ColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); void ClampColor(GLenum target, GLenum clamp);
void ClampColor(GLenum target, GLenum clamp); void BlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
void BlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); void BlendEquation(GLenum mode);
void BlendEquation(GLenum mode); void BlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
void BlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); void ClearStencil(GLint s);
void ClearStencil(GLint s); void ClearDepth(GLclampd depth);
void ClearDepth(GLclampd depth); void ClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
void ClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); } // namespace MobileGL::MG_Impl::GLImpl
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
+233 -229
View File
@@ -12,254 +12,258 @@
#include <MG_Util/Converters/GLToMG/TextureEnumConverter.h> #include <MG_Util/Converters/GLToMG/TextureEnumConverter.h>
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h> #include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
namespace MobileGL { namespace MobileGL::MG_Impl::GLImpl {
namespace MG_Impl::GLImpl { void SetSamplerParam_State(GLuint sampler, GLenum pname, const void* param, bool isFloat, bool isInteger) {
void SetSamplerParam_State(GLuint sampler, GLenum pname, const void* param, bool isFloat, bool isInteger) { if (!SamplerImpl::ValidateSamplerName(sampler)) return;
Bool doesSamplerObjectCreated = MG_State::pGLContext->ValidateSamplerObject(sampler);
if (!doesSamplerObjectCreated) {
// Create one for compatibility
MG_State::pGLContext->CreateSamplerObject(sampler);
}
auto& samplerObj = MG_State::pGLContext->GetSamplerObject(sampler);
if (!SamplerImpl::ValidateSamplerObject(sampler)) return;
using namespace MG_Util;
switch (pname) {
case GL_TEXTURE_WRAP_S:
samplerObj->SetWrapS(MG_Util::ConvertGLEnumToSamplerWrapMode(*(const GLint*)param));
break;
case GL_TEXTURE_WRAP_T:
samplerObj->SetWrapT(MG_Util::ConvertGLEnumToSamplerWrapMode(*(const GLint*)param));
break;
case GL_TEXTURE_WRAP_R:
samplerObj->SetWrapR(MG_Util::ConvertGLEnumToSamplerWrapMode(*(const GLint*)param));
break;
case GL_TEXTURE_MIN_FILTER:
samplerObj->SetMinFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(*(const GLint*)param));
samplerObj->SetMipmapMode(MG_Util::ConvertGLEnumToSamplerMipmapMode(*(const GLint*)param));
break;
case GL_TEXTURE_MAG_FILTER:
samplerObj->SetMagFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(*(const GLint*)param));
break;
case GL_TEXTURE_MIN_LOD:
samplerObj->SetLodRange(*(const GLfloat*)param, samplerObj->GetMaxLod());
break;
case GL_TEXTURE_MAX_LOD:
samplerObj->SetLodRange(samplerObj->GetMinLod(), *(const GLfloat*)param);
break;
case GL_TEXTURE_LOD_BIAS:
samplerObj->SetLodBias(*(const GLfloat*)param);
break;
case GL_TEXTURE_COMPARE_MODE:
samplerObj->SetCompareMode(MG_Util::ConvertGLEnumToSamplerCompareMode(*(const GLint*)param));
break;
case GL_TEXTURE_COMPARE_FUNC:
samplerObj->SetSamplerCompareFunc(MG_Util::ConvertGLEnumToSamplerCompareFunc(*(const GLint*)param));
break;
default:
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "SetSamplerParam_State",
"Invalid pname for sampler parameter"));
}
}
void GetSamplerParam_State(GLuint sampler, GLenum pname, void* params, bool isFloat, bool isInteger) {
if (!SamplerImpl::ValidateSamplerName(sampler)) return;
Bool doesSamplerObjectCreated = MG_State::pGLContext->ValidateSamplerObject(sampler);
if (!doesSamplerObjectCreated) {
// Create one for compatibility
MG_State::pGLContext->CreateSamplerObject(sampler);
}
auto& samplerObj = MG_State::pGLContext->GetSamplerObject(sampler);
if (!SamplerImpl::ValidateSamplerObject(sampler)) return;
using namespace MG_Util;
switch (pname) {
case GL_TEXTURE_WRAP_S:
*(GLuint*)params = MG_Util::ConvertSamplerWrapModeToGLEnum(samplerObj->GetWrapS());
break;
case GL_TEXTURE_WRAP_T:
*(GLuint*)params = MG_Util::ConvertSamplerWrapModeToGLEnum(samplerObj->GetWrapT());
break;
case GL_TEXTURE_WRAP_R:
*(GLuint*)params = MG_Util::ConvertSamplerWrapModeToGLEnum(samplerObj->GetWrapR());
break;
case GL_TEXTURE_MIN_FILTER:
*(GLuint*)params =
MG_Util::ConvertSamplerFilterModeToGLEnum(samplerObj->GetMinFilter(), samplerObj->GetMipmapMode());
break;
case GL_TEXTURE_MAG_FILTER:
*(GLuint*)params =
MG_Util::ConvertSamplerFilterModeToGLEnum(samplerObj->GetMagFilter(), SamplerMipmapMode::None);
break;
case GL_TEXTURE_MIN_LOD:
*(GLfloat*)params = samplerObj->GetMinLod();
break;
case GL_TEXTURE_MAX_LOD:
*(GLfloat*)params = samplerObj->GetMaxLod();
break;
case GL_TEXTURE_LOD_BIAS:
*(GLfloat*)params = samplerObj->GetLodBias();
break;
case GL_TEXTURE_COMPARE_MODE:
*(GLuint*)params = MG_Util::ConvertSamplerCompareModeToGLEnum(samplerObj->GetCompareMode());
break;
case GL_TEXTURE_COMPARE_FUNC:
*(GLuint*)params = MG_Util::ConvertSamplerCompareFuncToGLEnum(samplerObj->GetSamplerCompareFunc());
break;
default:
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetSamplerParam_State",
"Invalid pname for sampler parameter"));
}
}
GLboolean IsSampler_State(GLuint sampler) {
return MG_State::pGLContext->ValidateSamplerObject(sampler) ? GL_TRUE : GL_FALSE;
}
// migrate below functions without "_State" into this section
void GenSamplers_State(GLsizei count, GLuint* samplers) {
if (count < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GenSamplers", "count must be non-negative"));
return;
}
static thread_local Vector<GLuint> names;
MG_State::pGLContext->GenSamplerNames(count, names);
Memcpy(samplers, names.data(), count * sizeof(GLuint));
}
void DeleteSamplers_State(GLsizei count, const GLuint* samplers) {
if (count < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteSamplers", "count must be non-negative"));
return;
}
for (GLsizei i = 0; i < count; ++i) {
if (samplers[i] != 0) {
MG_State::pGLContext->MarkSamplerObjectForDeletion(samplers[i]);
}
}
}
void CreateSamplers_State(GLsizei n, GLuint* samplers) {
if (n < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GenSamplers", "count must be non-negative"));
return;
}
static thread_local Vector<GLuint> names;
MG_State::pGLContext->GenSamplerNames(n, names);
Memcpy(samplers, names.data(), n * sizeof(GLuint));
for (GLsizei i = 0; i < n; ++i) {
samplers[i] = names[i];
MG_State::pGLContext->CreateSamplerObject(names[i]);
}
}
void BindSampler_State(GLuint unit, GLuint sampler) {
if (unit >= MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "BindSampler", "texture unit out of range"));
return;
}
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject((Int)unit);
if (sampler == 0) {
textureUnit.SetSamplerObject(nullptr);
} else {
if (!SamplerImpl::ValidateSamplerName(sampler)) return; if (!SamplerImpl::ValidateSamplerName(sampler)) return;
Bool doesSamplerObjectCreated = MG_State::pGLContext->ValidateSamplerObject(sampler);
auto samplerObj = MG_State::pGLContext->GetSamplerObject(sampler); if (!doesSamplerObjectCreated) {
if (!samplerObj) { MG_State::pGLContext->CreateSamplerObject(sampler);
samplerObj = MG_State::pGLContext->CreateSamplerObject(sampler); // for compatibility
} }
if (!SamplerImpl::ValidateSamplerObject(sampler)) return; auto& samplerObject = MG_State::pGLContext->GetSamplerObject(sampler);
using namespace MG_Util; textureUnit.SetSamplerObject(MG_State::pGLContext->GetSamplerObject(sampler));
switch (pname) { }
case GL_TEXTURE_WRAP_S: }
samplerObj->SetWrapS(MG_Util::ConvertGLEnumToSamplerWrapMode(*(const GLint*)param));
break; void BindSamplers_State(GLuint first, GLsizei count, const GLuint* samplers) {
case GL_TEXTURE_WRAP_T: if (count < 0) {
samplerObj->SetWrapT(MG_Util::ConvertGLEnumToSamplerWrapMode(*(const GLint*)param)); MG_State::pGLContext->RecordError(
break; ErrorCode::InvalidValue,
case GL_TEXTURE_WRAP_R: MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "BindSamplers", "count must be non-negative"));
samplerObj->SetWrapR(MG_Util::ConvertGLEnumToSamplerWrapMode(*(const GLint*)param)); return;
break;
case GL_TEXTURE_MIN_FILTER:
samplerObj->SetMinFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(*(const GLint*)param));
samplerObj->SetMipmapMode(MG_Util::ConvertGLEnumToSamplerMipmapMode(*(const GLint*)param));
break;
case GL_TEXTURE_MAG_FILTER:
samplerObj->SetMagFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(*(const GLint*)param));
break;
case GL_TEXTURE_MIN_LOD:
samplerObj->SetLodRange(*(const GLfloat*)param, samplerObj->GetMaxLod());
break;
case GL_TEXTURE_MAX_LOD:
samplerObj->SetLodRange(samplerObj->GetMinLod(), *(const GLfloat*)param);
break;
case GL_TEXTURE_LOD_BIAS:
samplerObj->SetLodBias(*(const GLfloat*)param);
break;
case GL_TEXTURE_COMPARE_MODE:
samplerObj->SetCompareMode(MG_Util::ConvertGLEnumToSamplerCompareMode(*(const GLint*)param));
break;
case GL_TEXTURE_COMPARE_FUNC:
samplerObj->SetSamplerCompareFunc(MG_Util::ConvertGLEnumToSamplerCompareFunc(*(const GLint*)param));
break;
default:
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "SetSamplerParam_State",
"Invalid pname for sampler parameter"));
}
} }
void GetSamplerParam_State(GLuint sampler, GLenum pname, void* params, bool isFloat, bool isInteger) { for (GLsizei i = 0; i < count; ++i) {
if (!SamplerImpl::ValidateSamplerName(sampler)) return; BindSampler_State(first + i, samplers ? samplers[i] : 0);
auto samplerObj = MG_State::pGLContext->GetSamplerObject(sampler);
if (!samplerObj) {
samplerObj = MG_State::pGLContext->CreateSamplerObject(sampler); // for compatibility
}
if (!SamplerImpl::ValidateSamplerObject(sampler)) return;
using namespace MG_Util;
switch (pname) {
case GL_TEXTURE_WRAP_S:
*(GLint*)params = MG_Util::ConvertSamplerWrapModeToGLEnum(samplerObj->GetWrapS());
break;
case GL_TEXTURE_WRAP_T:
*(GLint*)params = MG_Util::ConvertSamplerWrapModeToGLEnum(samplerObj->GetWrapT());
break;
case GL_TEXTURE_WRAP_R:
*(GLint*)params = MG_Util::ConvertSamplerWrapModeToGLEnum(samplerObj->GetWrapR());
break;
case GL_TEXTURE_MIN_FILTER:
*(GLint*)params =
MG_Util::ConvertSamplerFilterModeToGLEnum(samplerObj->GetMinFilter(), samplerObj->GetMipmapMode());
break;
case GL_TEXTURE_MAG_FILTER:
*(GLint*)params =
MG_Util::ConvertSamplerFilterModeToGLEnum(samplerObj->GetMagFilter(), SamplerMipmapMode::None);
break;
case GL_TEXTURE_MIN_LOD:
*(GLfloat*)params = samplerObj->GetMinLod();
break;
case GL_TEXTURE_MAX_LOD:
*(GLfloat*)params = samplerObj->GetMaxLod();
break;
case GL_TEXTURE_LOD_BIAS:
*(GLfloat*)params = samplerObj->GetLodBias();
break;
case GL_TEXTURE_COMPARE_MODE:
*(GLint*)params = MG_Util::ConvertSamplerCompareModeToGLEnum(samplerObj->GetCompareMode());
break;
case GL_TEXTURE_COMPARE_FUNC:
*(GLint*)params = MG_Util::ConvertSamplerCompareFuncToGLEnum(samplerObj->GetSamplerCompareFunc());
break;
default:
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GetSamplerParam_State",
"Invalid pname for sampler parameter"));
}
} }
}
GLboolean IsSampler_State(GLuint sampler) { /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
return MG_State::pGLContext->ValidateSamplerObject(sampler) ? GL_TRUE : GL_FALSE; void GetSamplerParameteriv(GLuint sampler, GLenum pname, GLint* params) {
} GetSamplerParam_State(sampler, pname, params, false, false);
}
// migrate below functions without "_State" into this section void SamplerParameterIuiv(GLuint sampler, GLenum pname, const GLuint* param) {
void GenSamplers_State(GLsizei count, GLuint* samplers) { SetSamplerParam_State(sampler, pname, param, false, true);
if (count < 0) { }
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GenSamplers", "count must be non-negative"));
return;
}
auto names = MG_State::pGLContext->GenSamplerNames(count); void SamplerParameterIiv(GLuint sampler, GLenum pname, const GLint* param) {
for (GLsizei i = 0; i < count; ++i) { SetSamplerParam_State(sampler, pname, param, false, true);
samplers[i] = names[i]; }
}
}
void DeleteSamplers_State(GLsizei count, const GLuint* samplers) { void SamplerParameteriv(GLuint sampler, GLenum pname, const GLint* param) {
if (count < 0) { SetSamplerParam_State(sampler, pname, param, false, false);
MG_State::pGLContext->RecordError( }
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteSamplers", "count must be non-negative"));
return;
}
for (GLsizei i = 0; i < count; ++i) { void SamplerParameterfv(GLuint sampler, GLenum pname, const GLfloat* param) {
if (samplers[i] != 0) { SetSamplerParam_State(sampler, pname, param, true, false);
MG_State::pGLContext->MarkSamplerObjectForDeletion(samplers[i]); }
}
}
}
void CreateSamplers_State(GLsizei n, GLuint* samplers) { void SamplerParameteri(GLuint sampler, GLenum pname, GLint param) {
if (n < 0) { SamplerParameteriv(sampler, pname, &param);
MG_State::pGLContext->RecordError( }
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GenSamplers", "count must be non-negative"));
return;
}
auto names = MG_State::pGLContext->GenSamplerNames(n); void SamplerParameterf(GLuint sampler, GLenum pname, GLfloat param) {
for (GLsizei i = 0; i < n; ++i) { SamplerParameterfv(sampler, pname, &param);
samplers[i] = names[i]; }
MG_State::pGLContext->CreateSamplerObject(names[i]);
}
}
void BindSampler_State(GLuint unit, GLuint sampler) { GLboolean IsSampler(GLuint sampler) {
if (unit >= MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS) { return IsSampler_State(sampler);
MG_State::pGLContext->RecordError( }
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "BindSampler", "texture unit out of range"));
return;
}
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit); void GetSamplerParameterIuiv(GLuint sampler, GLenum pname, GLuint* params) {
if (sampler == 0) { GetSamplerParam_State(sampler, pname, params, false, true);
textureUnit.SetSamplerObject(nullptr); }
} else {
if (!SamplerImpl::ValidateSamplerName(sampler)) return;
auto samplerObject = MG_State::pGLContext->GetSamplerObject(sampler);
if (!samplerObject) {
samplerObject = MG_State::pGLContext->CreateSamplerObject(sampler);
}
textureUnit.SetSamplerObject(MG_State::pGLContext->GetSamplerObject(sampler)); void GetSamplerParameterIiv(GLuint sampler, GLenum pname, GLint* params) {
} GetSamplerParam_State(sampler, pname, params, false, true);
} }
void BindSamplers_State(GLuint first, GLsizei count, const GLuint* samplers) { void GetSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat* params) {
if (count < 0) { GetSamplerParam_State(sampler, pname, params, true, false);
MG_State::pGLContext->RecordError( }
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "BindSamplers", "count must be non-negative"));
return;
}
for (GLsizei i = 0; i < count; ++i) { void GenSamplers(GLsizei count, GLuint* samplers) {
BindSampler_State(first + i, samplers ? samplers[i] : 0); GenSamplers_State(count, samplers);
} }
}
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ void DeleteSamplers(GLsizei count, const GLuint* samplers) {
void GetSamplerParameteriv(GLuint sampler, GLenum pname, GLint* params) { DeleteSamplers_State(count, samplers);
GetSamplerParam_State(sampler, pname, params, false, false); }
}
void SamplerParameterIuiv(GLuint sampler, GLenum pname, const GLuint* param) { void CreateSamplers(GLsizei n, GLuint* samplers) {
SetSamplerParam_State(sampler, pname, param, false, true); CreateSamplers_State(n, samplers);
} }
void SamplerParameterIiv(GLuint sampler, GLenum pname, const GLint* param) { void BindSamplers(GLuint first, GLsizei count, const GLuint* samplers) {
SetSamplerParam_State(sampler, pname, param, false, true); BindSamplers_State(first, count, samplers);
} }
void SamplerParameteriv(GLuint sampler, GLenum pname, const GLint* param) { void BindSampler(GLuint unit, GLuint sampler) {
SetSamplerParam_State(sampler, pname, param, false, false); BindSampler_State(unit, sampler);
} }
} // namespace MobileGL::MG_Impl::GLImpl
void SamplerParameterfv(GLuint sampler, GLenum pname, const GLfloat* param) {
SetSamplerParam_State(sampler, pname, param, true, false);
}
void SamplerParameteri(GLuint sampler, GLenum pname, GLint param) {
SamplerParameteriv(sampler, pname, &param);
}
void SamplerParameterf(GLuint sampler, GLenum pname, GLfloat param) {
SamplerParameterfv(sampler, pname, &param);
}
GLboolean IsSampler(GLuint sampler) {
return IsSampler_State(sampler);
}
void GetSamplerParameterIuiv(GLuint sampler, GLenum pname, GLuint* params) {
GetSamplerParam_State(sampler, pname, params, false, true);
}
void GetSamplerParameterIiv(GLuint sampler, GLenum pname, GLint* params) {
GetSamplerParam_State(sampler, pname, params, false, true);
}
void GetSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat* params) {
GetSamplerParam_State(sampler, pname, params, true, false);
}
void GenSamplers(GLsizei count, GLuint* samplers) {
GenSamplers_State(count, samplers);
}
void DeleteSamplers(GLsizei count, const GLuint* samplers) {
DeleteSamplers_State(count, samplers);
}
void CreateSamplers(GLsizei n, GLuint* samplers) {
CreateSamplers_State(n, samplers);
}
void BindSamplers(GLuint first, GLsizei count, const GLuint* samplers) {
BindSamplers_State(first, count, samplers);
}
void BindSampler(GLuint unit, GLuint sampler) {
BindSampler_State(unit, sampler);
}
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
+19 -21
View File
@@ -9,24 +9,22 @@
#pragma once #pragma once
#include <Includes.h> #include <Includes.h>
namespace MobileGL { namespace MobileGL::MG_Impl::GLImpl {
namespace MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */ void GetSamplerParameteriv(GLuint sampler, GLenum pname, GLint* params);
void GetSamplerParameteriv(GLuint sampler, GLenum pname, GLint* params); void SamplerParameterIuiv(GLuint sampler, GLenum pname, const GLuint* param);
void SamplerParameterIuiv(GLuint sampler, GLenum pname, const GLuint* param); void SamplerParameterIiv(GLuint sampler, GLenum pname, const GLint* param);
void SamplerParameterIiv(GLuint sampler, GLenum pname, const GLint* param); void SamplerParameteriv(GLuint sampler, GLenum pname, const GLint* param);
void SamplerParameteriv(GLuint sampler, GLenum pname, const GLint* param); void SamplerParameterfv(GLuint sampler, GLenum pname, const GLfloat* param);
void SamplerParameterfv(GLuint sampler, GLenum pname, const GLfloat* param); void SamplerParameteri(GLuint sampler, GLenum pname, GLint param);
void SamplerParameteri(GLuint sampler, GLenum pname, GLint param); void SamplerParameterf(GLuint sampler, GLenum pname, GLfloat param);
void SamplerParameterf(GLuint sampler, GLenum pname, GLfloat param); GLboolean IsSampler(GLuint sampler);
GLboolean IsSampler(GLuint sampler); void GetSamplerParameterIuiv(GLuint sampler, GLenum pname, GLuint* params);
void GetSamplerParameterIuiv(GLuint sampler, GLenum pname, GLuint* params); void GetSamplerParameterIiv(GLuint sampler, GLenum pname, GLint* params);
void GetSamplerParameterIiv(GLuint sampler, GLenum pname, GLint* params); void GetSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat* params);
void GetSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat* params); void GenSamplers(GLsizei count, GLuint* samplers);
void GenSamplers(GLsizei count, GLuint* samplers); void DeleteSamplers(GLsizei count, const GLuint* samplers);
void DeleteSamplers(GLsizei count, const GLuint* samplers); void CreateSamplers(GLsizei n, GLuint* samplers);
void CreateSamplers(GLsizei n, GLuint* samplers); void BindSamplers(GLuint first, GLsizei count, const GLuint* samplers);
void BindSamplers(GLuint first, GLsizei count, const GLuint* samplers); void BindSampler(GLuint unit, GLuint sampler);
void BindSampler(GLuint unit, GLuint sampler); } // namespace MobileGL::MG_Impl::GLImpl
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
+113 -117
View File
@@ -11,126 +11,122 @@
#include <MG_State/GLState/ErrorState/Error.h> #include <MG_State/GLState/ErrorState/Error.h>
#include <MG_Util/Converters/GLToMG/TextureEnumConverter.h> #include <MG_Util/Converters/GLToMG/TextureEnumConverter.h>
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl::SamplerImpl {
namespace SamplerImpl { Bool ValidateSamplerName(GLuint sampler) {
Bool ValidateSamplerName(GLuint sampler) { if (!MG_State::pGLContext->ValidateSamplerName(sampler)) {
if (!MG_State::pGLContext->ValidateSamplerName(sampler)) { MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerName",
std::format("Invalid sampler name {}", sampler)));
return false;
}
return true;
}
Bool ValidateSamplerObject(GLuint sampler) {
if (!MG_State::pGLContext->ValidateSamplerObject(sampler)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerObject",
std::format("Sampler object {} does not exist", sampler)));
return false;
}
return true;
}
Bool ValidateSamplerParam(GLenum pname, GLenum param) {
using namespace MG_Util;
switch (pname) {
case GL_TEXTURE_WRAP_S:
case GL_TEXTURE_WRAP_T:
case GL_TEXTURE_WRAP_R:
if (MG_Util::ConvertGLEnumToSamplerWrapMode(param) == SamplerWrapMode::Unknown) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerParam",
"Invalid wrap mode parameter"));
return false;
}
break;
case GL_TEXTURE_MIN_FILTER:
if (MG_Util::ConvertGLEnumToSamplerFilterMode(param) == SamplerFilterMode::Unknown) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerParam",
"Invalid min filter parameter"));
return false;
}
break;
case GL_TEXTURE_MAG_FILTER:
if (param != GL_NEAREST && param != GL_LINEAR) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerParam",
"Invalid mag filter parameter"));
return false;
}
break;
case GL_TEXTURE_COMPARE_MODE:
if (param != GL_NONE && param != GL_COMPARE_REF_TO_TEXTURE) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerParam",
"Invalid compare mode parameter"));
return false;
}
break;
case GL_TEXTURE_COMPARE_FUNC:
if (param < GL_LEQUAL || param > GL_ALWAYS) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerParam",
"Invalid compare function parameter"));
return false;
}
break;
default:
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerParam",
"Invalid pname for sampler parameter"));
return false;
}
return true;
}
Bool ValidateSamplerFloatParam(GLenum pname, GLfloat param) {
switch (pname) {
case GL_TEXTURE_MIN_LOD:
case GL_TEXTURE_MAX_LOD:
case GL_TEXTURE_LOD_BIAS:
return true;
case GL_TEXTURE_BORDER_COLOR:
if (param < 0.0f || param > 1.0f) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerFloatParam",
"Border color component out of [0,1] range"));
return false;
}
return true;
default:
return ValidateSamplerParam(pname, static_cast<GLenum>(param));
}
}
Bool ValidateSamplerIntParam(GLenum pname, GLint param) {
switch (pname) {
case GL_TEXTURE_BORDER_COLOR:
if (param < 0 || param > 255) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerName", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerIntParam",
std::format("Invalid sampler name {}", sampler))); "Border color component out of [0,255] range"));
return false; return false;
} }
return true; return true;
default:
return ValidateSamplerParam(pname, static_cast<GLenum>(param));
} }
}
Bool ValidateSamplerObject(GLuint sampler) { } // namespace MobileGL::MG_Impl::GLImpl::SamplerImpl
if (!MG_State::pGLContext->ValidateSamplerObject(sampler)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerObject",
std::format("Sampler object {} does not exist", sampler)));
return false;
}
return true;
}
Bool ValidateSamplerParam(GLenum pname, GLenum param) {
using namespace MG_Util;
switch (pname) {
case GL_TEXTURE_WRAP_S:
case GL_TEXTURE_WRAP_T:
case GL_TEXTURE_WRAP_R:
if (MG_Util::ConvertGLEnumToSamplerWrapMode(param) == SamplerWrapMode::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerParam",
"Invalid wrap mode parameter"));
return false;
}
break;
case GL_TEXTURE_MIN_FILTER:
if (MG_Util::ConvertGLEnumToSamplerFilterMode(param) == SamplerFilterMode::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerParam",
"Invalid min filter parameter"));
return false;
}
break;
case GL_TEXTURE_MAG_FILTER:
if (param != GL_NEAREST && param != GL_LINEAR) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerParam",
"Invalid mag filter parameter"));
return false;
}
break;
case GL_TEXTURE_COMPARE_MODE:
if (param != GL_NONE && param != GL_COMPARE_REF_TO_TEXTURE) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerParam",
"Invalid compare mode parameter"));
return false;
}
break;
case GL_TEXTURE_COMPARE_FUNC:
if (param < GL_LEQUAL || param > GL_ALWAYS) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerParam",
"Invalid compare function parameter"));
return false;
}
break;
default:
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerParam",
"Invalid pname for sampler parameter"));
return false;
}
return true;
}
Bool ValidateSamplerFloatParam(GLenum pname, GLfloat param) {
switch (pname) {
case GL_TEXTURE_MIN_LOD:
case GL_TEXTURE_MAX_LOD:
case GL_TEXTURE_LOD_BIAS:
return true;
case GL_TEXTURE_BORDER_COLOR:
if (param < 0.0f || param > 1.0f) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerFloatParam",
"Border color component out of [0,1] range"));
return false;
}
return true;
default:
return ValidateSamplerParam(pname, static_cast<GLenum>(param));
}
}
Bool ValidateSamplerIntParam(GLenum pname, GLint param) {
switch (pname) {
case GL_TEXTURE_BORDER_COLOR:
if (param < 0 || param > 255) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerIntParam",
"Border color component out of [0,255] range"));
return false;
}
return true;
default:
return ValidateSamplerParam(pname, static_cast<GLenum>(param));
}
}
} // namespace SamplerImpl
} // namespace MobileGL::MG_Impl::GLImpl
+7 -9
View File
@@ -10,12 +10,10 @@
#include <Includes.h> #include <Includes.h>
#include <MG_State/GLState/SamplerState/SamplerObject.h> #include <MG_State/GLState/SamplerState/SamplerObject.h>
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl::SamplerImpl {
namespace SamplerImpl { Bool ValidateSamplerName(GLuint sampler);
Bool ValidateSamplerName(GLuint sampler); Bool ValidateSamplerObject(GLuint sampler);
Bool ValidateSamplerObject(GLuint sampler); Bool ValidateSamplerParam(GLenum pname, GLenum param);
Bool ValidateSamplerParam(GLenum pname, GLenum param); Bool ValidateSamplerFloatParam(GLenum pname, GLfloat param);
Bool ValidateSamplerFloatParam(GLenum pname, GLfloat param); Bool ValidateSamplerIntParam(GLenum pname, GLint param);
Bool ValidateSamplerIntParam(GLenum pname, GLint param); } // namespace MobileGL::MG_Impl::GLImpl::SamplerImpl
} // namespace SamplerImpl
} // namespace MobileGL::MG_Impl::GLImpl
+23 -25
View File
@@ -10,36 +10,34 @@
#include "MG_State/GLState/Core.h" #include "MG_State/GLState/Core.h"
namespace MobileGL { namespace MobileGL::MG_Impl::GLImpl {
namespace MG_Impl::GLImpl { GLsync FenceSync_Backend(GLenum condition, GLbitfield flags) {
GLsync FenceSync_Backend(GLenum condition, GLbitfield flags) { return 0;
return 0; }
}
GLenum ClientWaitSync_Backend(GLsync sync, GLbitfield flags, GLuint64 timeout) { GLenum ClientWaitSync_Backend(GLsync sync, GLbitfield flags, GLuint64 timeout) {
return 0; return 0;
} }
void DeleteSync_Backend(GLsync sync) {} void DeleteSync_Backend(GLsync sync) {}
GLsync FenceSync_State(GLenum condition, GLbitfield flags) { GLsync FenceSync_State(GLenum condition, GLbitfield flags) {
return 0; return 0;
} }
GLenum ClientWaitSync_State(GLsync sync, GLbitfield flags, GLuint64 timeout) { GLenum ClientWaitSync_State(GLsync sync, GLbitfield flags, GLuint64 timeout) {
return 0; return 0;
} }
void DeleteSync_State(GLsync sync) {} void DeleteSync_State(GLsync sync) {}
GLsync FenceSync(GLenum condition, GLbitfield flags) { GLsync FenceSync(GLenum condition, GLbitfield flags) {
return 0; return 0;
} }
GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) { GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
return 0; return 0;
} }
void DeleteSync(GLsync sync) {} void DeleteSync(GLsync sync) {}
} // namespace MG_Impl::GLImpl } // namespace MobileGL::MG_Impl::GLImpl
} // namespace MobileGL
+5 -7
View File
@@ -9,10 +9,8 @@
#pragma once #pragma once
#include <Includes.h> #include <Includes.h>
namespace MobileGL { namespace MobileGL::MG_Impl::GLImpl {
namespace MG_Impl::GLImpl { GLsync FenceSync(GLenum condition, GLbitfield flags);
GLsync FenceSync(GLenum condition, GLbitfield flags); GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout);
GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout); void DeleteSync(GLsync sync);
void DeleteSync(GLsync sync); } // namespace MobileGL::MG_Impl::GLImpl
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
File diff suppressed because it is too large Load Diff
@@ -7,48 +7,47 @@
// End of Source File Header // End of Source File Header
#include "ProxyTexture.h" #include "ProxyTexture.h"
#include "MG_State/GLState/TextureState/TextureObject2D.h" #include <MG_State/GLState/TextureState/TextureObject2D.h>
#include "MG_Util/Types.h"
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
namespace TextureImpl { UniquePtr<ProxyTextureManager> pProxyTextureManager;
ProxyTextureManager* pProxyTextureManager;
Bool IsProxyTextureTarget(TextureUploadTarget target) { Bool IsProxyTextureTarget(TextureUploadTarget target) {
switch (target) { switch (target) {
case TextureUploadTarget::ProxyCubeMap: case TextureUploadTarget::ProxyCubeMap:
case TextureUploadTarget::ProxyTexture1DArray: case TextureUploadTarget::ProxyTexture1DArray:
case TextureUploadTarget::ProxyTexture2DArray: case TextureUploadTarget::ProxyTexture2DArray:
case TextureUploadTarget::ProxyTexture1D: case TextureUploadTarget::ProxyTexture1D:
case TextureUploadTarget::ProxyTexture2D: case TextureUploadTarget::ProxyTexture2D:
case TextureUploadTarget::ProxyTexture3D: case TextureUploadTarget::ProxyTexture3D:
case TextureUploadTarget::ProxyTexture2DMultisample: case TextureUploadTarget::ProxyTexture2DMultisample:
case TextureUploadTarget::ProxyTexture2DMultisampleArray: case TextureUploadTarget::ProxyTexture2DMultisampleArray:
case TextureUploadTarget::ProxyTextureRectangle: case TextureUploadTarget::ProxyTextureRectangle:
case TextureUploadTarget::ProxyCubeMapArray: case TextureUploadTarget::ProxyCubeMapArray:
return true; return true;
default: default:
return false; return false;
}
} }
}
SharedPtr<MG_State::GLState::ITextureObject> ProxyTextureManager::CreateOrReplaceProxyTextureObject( const SharedPtr<MG_State::GLState::ITextureObject>& ProxyTextureManager::CreateOrReplaceProxyTextureObject(
TextureUploadTarget target) { TextureUploadTarget target) {
auto it = m_proxyTexturesMap.find(target); auto it = m_proxyTexturesMap.find(target);
if (it != m_proxyTexturesMap.end()) { if (it != m_proxyTexturesMap.end()) {
m_proxyTexturesMap.erase(it); m_proxyTexturesMap.erase(it);
}
m_proxyTexturesMap[target] = MakeShared<MG_State::GLState::TextureObject2D>(0);
return m_proxyTexturesMap[target];
} }
auto& obj = m_proxyTexturesMap[target];
obj = MakeShared<MG_State::GLState::TextureObject2D>(0);
return obj;
}
SharedPtr<MG_State::GLState::ITextureObject> ProxyTextureManager::GetProxyTextureObject( const SharedPtr<MG_State::GLState::ITextureObject>& ProxyTextureManager::GetProxyTextureObject(
TextureUploadTarget target) { TextureUploadTarget target) {
auto it = m_proxyTexturesMap.find(target); auto it = m_proxyTexturesMap.find(target);
if (it != m_proxyTexturesMap.end()) { if (it != m_proxyTexturesMap.end()) {
return it->second; return it->second;
}
return nullptr;
} }
} // namespace TextureImpl static SharedPtr<MG_State::GLState::ITextureObject> nullTextureObject = nullptr;
} // namespace MobileGL::MG_Impl::GLImpl return nullTextureObject;
}
} // namespace MobileGL::MG_Impl::GLImpl::TextureImpl
+12 -13
View File
@@ -10,19 +10,18 @@
#include <Includes.h> #include <Includes.h>
#include <MG_State/GLState/Core.h> #include <MG_State/GLState/Core.h>
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
namespace TextureImpl { Bool IsProxyTextureTarget(TextureUploadTarget target);
Bool IsProxyTextureTarget(TextureUploadTarget target);
class ProxyTextureManager { class ProxyTextureManager {
public: public:
SharedPtr<MG_State::GLState::ITextureObject> CreateOrReplaceProxyTextureObject(TextureUploadTarget target); const SharedPtr<MG_State::GLState::ITextureObject>& CreateOrReplaceProxyTextureObject(
SharedPtr<MG_State::GLState::ITextureObject> GetProxyTextureObject(TextureUploadTarget target); TextureUploadTarget target);
const SharedPtr<MG_State::GLState::ITextureObject>& GetProxyTextureObject(TextureUploadTarget target);
private: private:
UnorderedMap<TextureUploadTarget, SharedPtr<MG_State::GLState::ITextureObject>> m_proxyTexturesMap; UnorderedMap<TextureUploadTarget, SharedPtr<MG_State::GLState::ITextureObject>> m_proxyTexturesMap;
}; };
extern ProxyTextureManager* pProxyTextureManager; extern UniquePtr<ProxyTextureManager> pProxyTextureManager;
} // namespace TextureImpl } // namespace MobileGL::MG_Impl::GLImpl::TextureImpl
} // namespace MobileGL::MG_Impl::GLImpl
+240 -254
View File
@@ -15,309 +15,295 @@
#include <MG_Util/Converters/MGToMG/TextureEnumConverter.h> #include <MG_Util/Converters/MGToMG/TextureEnumConverter.h>
#include <MG_Util/Converters/MGToStr/TextureEnumConverter.h> #include <MG_Util/Converters/MGToStr/TextureEnumConverter.h>
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
namespace TextureImpl { Bool ValidateTextureTarget(TextureTarget target) {
Bool ValidateTextureTarget(TextureTarget target) { if (target == TextureTarget::Unknown) {
if (target == TextureTarget::Unknown) { MG_State::pGLContext->RecordError(
MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum,
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureTarget", "Invalid texture target"));
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureTarget", "Invalid texture target")); return false;
return false; }
} return true;
return true; }
Bool ValidateTextureUploadTarget(TextureUploadTarget textureUploadTarget) {
if (textureUploadTarget == TextureUploadTarget::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureUploadTarget",
"Invalid texture upload target"));
return false;
}
return true;
}
Bool ValidateTextureName(Uint texture, Bool allowZero) {
if (texture == 0) {
if (allowZero) return true;
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureName", "Texture name cannot be zero"));
return false;
} }
Bool ValidateTextureUploadTarget(TextureUploadTarget textureUploadTarget) { if (!MG_State::pGLContext->ValidateTextureName(texture)) {
if (textureUploadTarget == TextureUploadTarget::Unknown) { MG_State::pGLContext->RecordError(
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureName", "Invalid texture name"));
"ValidateTextureUploadTarget", return false;
"Invalid texture upload target")); }
return false; return true;
} }
return true;
Bool ValidateTextureInputFormat(TextureInputFormat format) {
if (format == TextureInputFormat::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInputFormat",
"Invalid texture input format"));
return false;
}
return true;
}
Bool ValidateTexturePixelDataType(TexturePixelDataType texturePixelDataType) {
if (texturePixelDataType == TexturePixelDataType::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTexturePixelDataType",
"Invalid texture pixel data type"));
return false;
}
return true;
}
Bool ValidateTextureLevelNumber(GLint level) {
if (level < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureLevelNumber",
"Texture level must be non-negative"));
return false;
} }
Bool ValidateTextureName(Uint texture, Bool allowZero) { // TODO: GL_INVALID_VALUE may be generated if level is greater than log2(max), where max is the returned
if (texture == 0) { // value of GL_MAX_TEXTURE_SIZE.
if (allowZero) return true;
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue, return true;
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureName", }
"Texture name cannot be zero"));
return false;
}
if (!MG_State::pGLContext->ValidateTextureName(texture)) { Bool ValidateTextureSizeWithTextureUploadTarget(TextureUploadTarget target, GLsizei width, GLsizei height) {
if (target == TextureUploadTarget::CubeMapPositiveX || target == TextureUploadTarget::CubeMapNegativeX ||
target == TextureUploadTarget::CubeMapPositiveY || target == TextureUploadTarget::CubeMapNegativeY ||
target == TextureUploadTarget::CubeMapPositiveZ || target == TextureUploadTarget::CubeMapNegativeZ) {
if (width != height) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureName", "Invalid texture name")); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSizeWithTarget",
"Width and height must be equal for cube map textures"));
return false; return false;
} }
return true;
} }
Bool ValidateTextureInputFormat(TextureInputFormat format) { if (!(target == TextureUploadTarget::Texture1DArray || target == TextureUploadTarget::ProxyTexture1DArray)) {
if (format == TextureInputFormat::Unknown) { if (height < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInputFormat",
"Invalid texture input format"));
return false;
}
return true;
}
Bool ValidateTexturePixelDataType(TexturePixelDataType texturePixelDataType) {
if (texturePixelDataType == TexturePixelDataType::Unknown) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"ValidateTexturePixelDataType",
"Invalid texture pixel data type"));
return false;
}
return true;
}
Bool ValidateTextureLevelNumber(GLint level) {
if (level < 0) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"ValidateTextureLevelNumber",
"Texture level must be non-negative"));
return false;
}
// TODO: GL_INVALID_VALUE may be generated if level is greater than log2(max), where max is the returned
// value of GL_MAX_TEXTURE_SIZE.
return true;
}
Bool ValidateTextureSizeWithTextureUploadTarget(TextureUploadTarget target, GLsizei width, GLsizei height) {
if (target == TextureUploadTarget::CubeMapPositiveX || target == TextureUploadTarget::CubeMapNegativeX ||
target == TextureUploadTarget::CubeMapPositiveY || target == TextureUploadTarget::CubeMapNegativeY ||
target == TextureUploadTarget::CubeMapPositiveZ || target == TextureUploadTarget::CubeMapNegativeZ) {
if (width != height) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSizeWithTarget",
"Width and height must be equal for cube map textures"));
return false;
}
}
if (!(target == TextureUploadTarget::Texture1DArray ||
target == TextureUploadTarget::ProxyTexture1DArray)) {
if (height < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSizeWithTarget",
"Height must be greater than or equal to zero"));
return false;
}
// TODO: GL_INVALID_VALUE is generated if target is not GL_TEXTURE_1D_ARRAY or GL_PROXY_TEXTURE_1D_ARRAY
// and height is greater than GL_MAX_TEXTURE_SIZE.
}
if (target == TextureUploadTarget::Texture1DArray || target == TextureUploadTarget::ProxyTexture1DArray) {
if (height < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSizeWithTarget",
"Height must be greater than or equal to zero"));
return false;
}
// TODO: GL_INVALID_VALUE is generated if target is GL_TEXTURE_1D_ARRAY or GL_PROXY_TEXTURE_1D_ARRAY and
// height is greater than GL_MAX_ARRAY_TEXTURE_LAYERS.
}
return true;
}
Bool ValidateTextureSizeRange(SizeT width, SizeT height, SizeT depth) {
if (width < 0 || height < 0 || depth < 0) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSizeRange", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSizeWithTarget",
"Width and height must be greater than zero")); "Height must be greater than or equal to zero"));
return false; return false;
} }
// TODO: GL_INVALID_VALUE is generated if target is not GL_TEXTURE_1D_ARRAY or GL_PROXY_TEXTURE_1D_ARRAY
// TODO: GL_INVALID_VALUE is generated if width is greater than GL_MAX_TEXTURE_SIZE. // and height is greater than GL_MAX_TEXTURE_SIZE.
return true;
} }
Bool ValidateTextureInternalFormat(TextureInternalFormat format) { if (target == TextureUploadTarget::Texture1DArray || target == TextureUploadTarget::ProxyTexture1DArray) {
if (format == TextureInternalFormat::Unknown) { if (height < 0) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormat", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSizeWithTarget",
"Invalid texture sized internal format")); "Height must be greater than or equal to zero"));
return false; return false;
} }
return true; // TODO: GL_INVALID_VALUE is generated if target is GL_TEXTURE_1D_ARRAY or GL_PROXY_TEXTURE_1D_ARRAY and
// height is greater than GL_MAX_ARRAY_TEXTURE_LAYERS.
} }
Bool ValidateTextureBorderNumber(Int border) { return true;
if (border != 0) { }
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", Bool ValidateTextureSizeRange(SizeT width, SizeT height, SizeT depth) {
"ValidateTextureBorderNumber", if (width < 0 || height < 0 || depth < 0) {
"Border must be zero")); MG_State::pGLContext->RecordError(
return false; ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSizeRange",
} "Width and height must be greater than zero"));
return true; return false;
} }
Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format, // TODO: GL_INVALID_VALUE is generated if width is greater than GL_MAX_TEXTURE_SIZE.
TextureInternalFormat internalFormat,
TexturePixelDataType type) {
if (type == TexturePixelDataType::UnsignedByte332 || type == TexturePixelDataType::UnsignedByte233Rev ||
type == TexturePixelDataType::UnsignedShort565 || type == TexturePixelDataType::UnsignedShort565Rev ||
type == TexturePixelDataType::UnsignedInt101111Rev) {
if (format != TextureInputFormat::RGB) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"ValidateTextureInternalFormatCompatibleWithInput",
"Invalid format for the given type"));
return false;
}
}
if (type == TexturePixelDataType::UnsignedShort4444 || type == TexturePixelDataType::UnsignedShort4444Rev || return true;
type == TexturePixelDataType::UnsignedShort5551 || type == TexturePixelDataType::UnsignedShort1555Rev || }
type == TexturePixelDataType::UnsignedInt8888 || type == TexturePixelDataType::UnsignedInt8888Rev ||
type == TexturePixelDataType::UnsignedInt1010102 ||
type == TexturePixelDataType::UnsignedInt2101010Rev ||
type == TexturePixelDataType::UnsignedInt5999Rev) {
if (format != TextureInputFormat::RGBA && format != TextureInputFormat::BGRA) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"ValidateTextureInternalFormatCompatibleWithInput",
"Invalid format for the given type"));
return false;
}
}
if (internalFormat == TextureInternalFormat::DepthComponent || Bool ValidateTextureInternalFormat(TextureInternalFormat format) {
internalFormat == TextureInternalFormat::DepthComponent16 || if (format == TextureInternalFormat::Unknown) {
internalFormat == TextureInternalFormat::DepthComponent24 || MG_State::pGLContext->RecordError(
internalFormat == TextureInternalFormat::DepthComponent32F) { ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormat",
if (format != TextureInputFormat::DepthComponent) { "Invalid texture sized internal format"));
MG_State::pGLContext->RecordError( return false;
ErrorCode::InvalidOperation, }
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", return true;
"ValidateTextureInternalFormatCompatibleWithInput", }
"Invalid format for depth component internal format"));
return false;
}
}
if (format == TextureInputFormat::DepthComponent && Bool ValidateTextureBorderNumber(Int border) {
(internalFormat != TextureInternalFormat::DepthComponent && if (border != 0) {
internalFormat != TextureInternalFormat::DepthComponent16 && MG_State::pGLContext->RecordError(
internalFormat != TextureInternalFormat::DepthComponent24 && ErrorCode::InvalidValue,
internalFormat != TextureInternalFormat::DepthComponent32F && MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureBorderNumber", "Border must be zero"));
internalFormat != TextureInternalFormat::DepthComponent32 // workaround for Minecraft 1.21.5+ return false;
)) { }
return true;
}
Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format,
TextureInternalFormat internalFormat,
TexturePixelDataType type) {
if (type == TexturePixelDataType::UnsignedByte332 || type == TexturePixelDataType::UnsignedByte233Rev ||
type == TexturePixelDataType::UnsignedShort565 || type == TexturePixelDataType::UnsignedShort565Rev ||
type == TexturePixelDataType::UnsignedInt101111Rev) {
if (format != TextureInputFormat::RGB) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput",
"Invalid internal format for depth component format")); "Invalid format for the given type"));
return false; return false;
} }
return true;
} }
Bool ValidateTextureLevelWithUploadTarget(TextureUploadTarget target, Int level) { if (type == TexturePixelDataType::UnsignedShort4444 || type == TexturePixelDataType::UnsignedShort4444Rev ||
if (target == TextureUploadTarget::TextureRectangle || type == TexturePixelDataType::UnsignedShort5551 || type == TexturePixelDataType::UnsignedShort1555Rev ||
target == TextureUploadTarget::ProxyTextureRectangle) { type == TexturePixelDataType::UnsignedInt8888 || type == TexturePixelDataType::UnsignedInt8888Rev ||
if (level != 0) { type == TexturePixelDataType::UnsignedInt1010102 || type == TexturePixelDataType::UnsignedInt2101010Rev ||
MG_State::pGLContext->RecordError( type == TexturePixelDataType::UnsignedInt5999Rev) {
ErrorCode::InvalidValue, if (format != TextureInputFormat::RGBA && format != TextureInputFormat::BGRA) {
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureLevelWithUploadTarget",
"Level must be zero for rectangle textures"));
return false;
}
}
return true;
}
Bool ValidateTextureObject(SharedPtr<MG_State::GLState::ITextureObject> textureObject) {
if (!textureObject) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureObject", "Texture object is null")); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput",
"Invalid format for the given type"));
return false; return false;
} }
return true;
} }
Bool ValidateTextureTargetUniformity(SharedPtr<MG_State::GLState::ITextureObject> textureObject, if (internalFormat == TextureInternalFormat::DepthComponent ||
TextureTarget target) { internalFormat == TextureInternalFormat::DepthComponent16 ||
if (!textureObject) return true; // should be created later internalFormat == TextureInternalFormat::DepthComponent24 ||
TextureTarget prevTarget = textureObject->GetTarget(); internalFormat == TextureInternalFormat::DepthComponent32F) {
if (prevTarget != target) { if (format != TextureInputFormat::DepthComponent) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureTargetUniformity", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput",
"Texture target does not match the previously created texture")); "Invalid format for depth component internal format"));
return false; return false;
} }
return true;
} }
Bool ValidateTextureSubImageOffsets(SharedPtr<MG_State::GLState::ITextureObject> textureObject, Int xoffset, if (format == TextureInputFormat::DepthComponent &&
Int width, Int yoffset, Int height, Int zoffset, Int depth) { (internalFormat != TextureInternalFormat::DepthComponent &&
auto baseSize = textureObject->GetBaseSize(); internalFormat != TextureInternalFormat::DepthComponent16 &&
if (xoffset < 0 || (xoffset + width) > baseSize.x()) { internalFormat != TextureInternalFormat::DepthComponent24 &&
MG_State::pGLContext->RecordError( internalFormat != TextureInternalFormat::DepthComponent32F &&
ErrorCode::InvalidValue, internalFormat != TextureInternalFormat::DepthComponent32 // workaround for Minecraft 1.21.5+
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSubImageOffsets", )) {
"xoffset must be non-negative and (xoffset + width) must not exceed " MG_State::pGLContext->RecordError(
"the texture width.")); ErrorCode::InvalidOperation,
return false; MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput",
} "Invalid internal format for depth component format"));
if (baseSize.y() == 0) return true; return false;
if (yoffset < 0 || (yoffset + height) > baseSize.y()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSubImageOffsets",
"yoffset must be non-negative and (yoffset + height) must not exceed "
"the texture height."));
return false;
}
if (baseSize.z() == 0) return true;
if (zoffset < 0 || (zoffset + depth) > baseSize.z()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSubImageOffsets",
"zoffset must be non-negative and (zoffset + depth) must not exceed "
"the texture depth."));
return false;
}
return true;
} }
return true;
}
Bool ValidateBaseInternalFormatMatch(TextureInternalFormat format1, TextureInternalFormat format2) { Bool ValidateTextureLevelWithUploadTarget(TextureUploadTarget target, Int level) {
auto unsizedFormat1 = MG_Util::ConvertInternalFormatToUnsized(format1); if (target == TextureUploadTarget::TextureRectangle || target == TextureUploadTarget::ProxyTextureRectangle) {
auto unsizedFormat2 = MG_Util::ConvertInternalFormatToUnsized(format2); if (level != 0) {
if (unsizedFormat1 != unsizedFormat2) {
MG_State::pGLContext->RecordError( MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>( MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureLevelWithUploadTarget",
std::format("MG_Impl/GLImpl", "ValidateBaseInternalFormatMatch", "Level must be zero for rectangle textures"));
"The base internal format of the two formats do not match ({} vs. {})",
MG_Util::ConvertTextureInternalFormatToString(unsizedFormat1).c_str(),
MG_Util::ConvertTextureInternalFormatToString(unsizedFormat2).c_str())));
return false; return false;
} }
return true; }
} // namespace TextureImpl return true;
}
Bool ValidateTextureObject(SharedPtr<MG_State::GLState::ITextureObject> textureObject) {
if (!textureObject) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureObject", "Texture object is null"));
return false;
}
return true;
}
Bool ValidateTextureTargetUniformity(SharedPtr<MG_State::GLState::ITextureObject> textureObject,
TextureTarget target) {
if (!textureObject) return true; // should be created later
TextureTarget prevTarget = textureObject->GetTarget();
if (prevTarget != target) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureTargetUniformity",
"Texture target does not match the previously created texture"));
return false;
}
return true;
}
Bool ValidateTextureSubImageOffsets(SharedPtr<MG_State::GLState::ITextureObject> textureObject, Int xoffset,
Int width, Int yoffset, Int height, Int zoffset, Int depth) {
auto baseSize = textureObject->GetBaseSize();
if (xoffset < 0 || (xoffset + width) > baseSize.x()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSubImageOffsets",
"xoffset must be non-negative and (xoffset + width) must not exceed "
"the texture width."));
return false;
}
if (baseSize.y() == 0) return true;
if (yoffset < 0 || (yoffset + height) > baseSize.y()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSubImageOffsets",
"yoffset must be non-negative and (yoffset + height) must not exceed "
"the texture height."));
return false;
}
if (baseSize.z() == 0) return true;
if (zoffset < 0 || (zoffset + depth) > baseSize.z()) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSubImageOffsets",
"zoffset must be non-negative and (zoffset + depth) must not exceed "
"the texture depth."));
return false;
}
return true;
}
Bool ValidateBaseInternalFormatMatch(TextureInternalFormat format1, TextureInternalFormat format2) {
auto unsizedFormat1 = MG_Util::ConvertInternalFormatToUnsized(format1);
auto unsizedFormat2 = MG_Util::ConvertInternalFormatToUnsized(format2);
if (unsizedFormat1 != unsizedFormat2) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>(
std::format("MG_Impl/GLImpl", "ValidateBaseInternalFormatMatch",
"The base internal format of the two formats do not match ({} vs. {})",
MG_Util::ConvertTextureInternalFormatToString(unsizedFormat1).c_str(),
MG_Util::ConvertTextureInternalFormatToString(unsizedFormat2).c_str())));
return false;
}
return true;
} // namespace TextureImpl } // namespace TextureImpl
} // namespace MobileGL::MG_Impl::GLImpl } // namespace MobileGL::MG_Impl::GLImpl::TextureImpl
+22 -24
View File
@@ -12,27 +12,25 @@
#include <Includes.h> #include <Includes.h>
#include <MG_State/GLState/TextureState/TextureObject.h> #include <MG_State/GLState/TextureState/TextureObject.h>
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
namespace TextureImpl { Bool ValidateTextureTarget(TextureTarget target);
Bool ValidateTextureTarget(TextureTarget target); Bool ValidateTextureUploadTarget(TextureUploadTarget textureUploadTarget);
Bool ValidateTextureUploadTarget(TextureUploadTarget textureUploadTarget); Bool ValidateTextureName(Uint texture, Bool allowZero = false);
Bool ValidateTextureName(Uint texture, Bool allowZero = false); Bool ValidateTextureInputFormat(TextureInputFormat format);
Bool ValidateTextureInputFormat(TextureInputFormat format); Bool ValidateTexturePixelDataType(TexturePixelDataType texturePixelDataType);
Bool ValidateTexturePixelDataType(TexturePixelDataType texturePixelDataType); Bool ValidateTextureLevelNumber(Int level);
Bool ValidateTextureLevelNumber(Int level); Bool ValidateTextureSizeWithTextureUploadTarget(TextureUploadTarget target, GLsizei width, GLsizei height);
Bool ValidateTextureSizeWithTextureUploadTarget(TextureUploadTarget target, GLsizei width, GLsizei height); Bool ValidateTextureSizeRange(SizeT width, SizeT height, SizeT depth);
Bool ValidateTextureSizeRange(SizeT width, SizeT height, SizeT depth); Bool ValidateTextureInternalFormat(TextureInternalFormat format);
Bool ValidateTextureInternalFormat(TextureInternalFormat format); Bool ValidateTextureBorderNumber(Int border);
Bool ValidateTextureBorderNumber(Int border); Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format,
Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format, TextureInternalFormat internalFormat,
TextureInternalFormat internalFormat, TexturePixelDataType type);
TexturePixelDataType type); Bool ValidateTextureLevelWithUploadTarget(TextureUploadTarget target, Int level);
Bool ValidateTextureLevelWithUploadTarget(TextureUploadTarget target, Int level); Bool ValidateTextureObject(SharedPtr<MG_State::GLState::ITextureObject> textureObject);
Bool ValidateTextureObject(SharedPtr<MG_State::GLState::ITextureObject> textureObject); Bool ValidateTextureTargetUniformity(SharedPtr<MG_State::GLState::ITextureObject> textureObject,
Bool ValidateTextureTargetUniformity(SharedPtr<MG_State::GLState::ITextureObject> textureObject, TextureTarget target);
TextureTarget target); Bool ValidateTextureSubImageOffsets(SharedPtr<MG_State::GLState::ITextureObject> textureObject, Int xoffset,
Bool ValidateTextureSubImageOffsets(SharedPtr<MG_State::GLState::ITextureObject> textureObject, Int xoffset, Int width, Int yoffset = 0, Int height = 0, Int zoffset = 0, Int depth = 0);
Int width, Int yoffset = 0, Int height = 0, Int zoffset = 0, Int depth = 0); Bool ValidateBaseInternalFormatMatch(TextureInternalFormat format1, TextureInternalFormat format2);
Bool ValidateBaseInternalFormatMatch(TextureInternalFormat format1, TextureInternalFormat format2); } // namespace MobileGL::MG_Impl::GLImpl::TextureImpl
} // namespace TextureImpl
} // namespace MobileGL::MG_Impl::GLImpl
@@ -12,222 +12,216 @@
#include <MG_State/GLState/ErrorState/Error.h> #include <MG_State/GLState/ErrorState/Error.h>
#include <MG_Util/Converters/GLToMG/DataTypeConverter.h> #include <MG_Util/Converters/GLToMG/DataTypeConverter.h>
namespace MobileGL { namespace MobileGL::MG_Impl::GLImpl {
namespace MG_Impl::GLImpl { void DisableVertexAttribArray_State(GLuint index) {
void DisableVertexAttribArray_State(GLuint index) { if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
auto vao = MG_State::pGLContext->GetBoundVertexArray(); auto& vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) { if (!vao) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation, MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl",
"EnableVertexAttribArray_State", "EnableVertexAttribArray_State",
"No vertex array object is bound.")); "No vertex array object is bound."));
return; return;
}
vao->DisableAttribute(index);
} }
void EnableVertexAttribArray_State(GLuint index) { vao->DisableAttribute(index);
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return; }
auto vao = MG_State::pGLContext->GetBoundVertexArray(); void EnableVertexAttribArray_State(GLuint index) {
if (!vao) { if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"EnableVertexAttribArray_State",
"No vertex array object is bound."));
return;
}
vao->EnableAttribute(index); auto& vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl",
"EnableVertexAttribArray_State",
"No vertex array object is bound."));
return;
} }
// TODO: implement GL_BGRA support vao->EnableAttribute(index);
void VertexAttribIPointer_State(GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer) { }
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
DataType dataType = MG_Util::ConvertGLEnumToDataType(type); // TODO: implement GL_BGRA support
if (!VertexArrayImpl::ValidateVertexAttribPointerParams(index, size, dataType, stride)) return; void VertexAttribIPointer_State(GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
auto vao = MG_State::pGLContext->GetBoundVertexArray(); DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
if (!vao) { if (!VertexArrayImpl::ValidateVertexAttribPointerParams(index, size, dataType, stride)) return;
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"VertexAttribPointer_State",
"No vertex array object is bound."));
return;
}
auto& vboSlot = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Vertex); auto& vao = MG_State::pGLContext->GetBoundVertexArray();
auto vbo = vboSlot.GetBoundObject(); if (!vao) {
if (!vbo) { MG_State::pGLContext->RecordError(
MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "VertexAttribPointer_State",
ErrorCode::InvalidOperation, "No vertex array object is bound."));
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "VertexAttribPointer_State", return;
"No buffer is bound to GL_ARRAY_BUFFER."));
return;
}
SizeT offset = reinterpret_cast<SizeT>(pointer);
vao->SetAttributeFormat(index, size, dataType, false, stride, offset, true);
vao->BindAttributeBuffer(index, vbo);
} }
// TODO: implement GL_BGRA support auto& vboSlot = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Vertex);
void VertexAttribPointer_State(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, auto& vbo = vboSlot.GetBoundObject();
const void* pointer) { if (!vbo) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return; MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "VertexAttribPointer_State",
DataType dataType = MG_Util::ConvertGLEnumToDataType(type); "No buffer is bound to GL_ARRAY_BUFFER."));
if (!VertexArrayImpl::ValidateVertexAttribPointerParams(index, size, dataType, stride)) return; return;
auto vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"VertexAttribPointer_State",
"No vertex array object is bound."));
return;
}
auto& vboSlot = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Vertex);
auto vbo = vboSlot.GetBoundObject();
if (!vbo) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "VertexAttribPointer_State",
"No buffer is bound to GL_ARRAY_BUFFER."));
return;
}
SizeT offset = reinterpret_cast<SizeT>(pointer);
vao->SetAttributeFormat(index, size, dataType, normalized, stride, offset, false);
vao->BindAttributeBuffer(index, vbo);
} }
void BindVertexArray_State(GLuint array) { auto offset = reinterpret_cast<SizeT>(pointer);
if (array == 0) {
vao->SetAttributeFormat(index, size, dataType, false, stride, offset, true);
vao->BindAttributeBuffer(index, vbo);
}
// TODO: implement GL_BGRA support
void VertexAttribPointer_State(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
const void* pointer) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
if (!VertexArrayImpl::ValidateVertexAttribPointerParams(index, size, dataType, stride)) return;
auto& vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "VertexAttribPointer_State",
"No vertex array object is bound."));
return;
}
auto& vboSlot = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Vertex);
auto& vbo = vboSlot.GetBoundObject();
if (!vbo) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "VertexAttribPointer_State",
"No buffer is bound to GL_ARRAY_BUFFER."));
return;
}
SizeT offset = reinterpret_cast<SizeT>(pointer);
vao->SetAttributeFormat(index, size, dataType, normalized, stride, offset, false);
vao->BindAttributeBuffer(index, vbo);
}
void BindVertexArray_State(GLuint array) {
if (array == 0) {
MG_State::pGLContext->BindVertexArray(0);
return;
}
if (!VertexArrayImpl::ValidateVertexArrayName(array)) return;
if (!MG_State::pGLContext->ValidateVertexArrayObject(array)) {
MG_State::pGLContext->CreateVertexArrayObject(array);
}
MG_State::pGLContext->BindVertexArray(array);
}
void DeleteVertexArrays_State(GLsizei n, const GLuint* arrays) {
if (n < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteVertexArrays_State", "n must be non-negative."));
return;
}
for (GLsizei i = 0; i < n; ++i) {
GLuint vao = arrays[i];
if (vao == 0) continue;
if (!VertexArrayImpl::ValidateVertexArrayName(vao)) continue;
if (MG_State::pGLContext->GetBoundVertexArray() &&
MG_State::pGLContext->GetBoundVertexArray() == MG_State::pGLContext->GetVertexArrayObject(vao)) {
MG_State::pGLContext->BindVertexArray(0); MG_State::pGLContext->BindVertexArray(0);
return;
} }
if (!VertexArrayImpl::ValidateVertexArrayName(array)) return; MG_State::pGLContext->MarkVertexArrayForDeletion(vao);
}
}
if (!MG_State::pGLContext->ValidateVertexArrayObject(array)) { void GenVertexArrays_State(GLsizei n, GLuint* arrays) {
MG_State::pGLContext->CreateVertexArrayObject(array); if (n < 0) {
} MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MG_State::pGLContext->BindVertexArray(array); MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GenVertexArrays_State", "n must be non-negative."));
return;
} }
void DeleteVertexArrays_State(GLsizei n, const GLuint* arrays) { static thread_local Vector<Uint> vaos;
if (n < 0) { MG_State::pGLContext->GenVertexArrayNames(n, vaos);
MG_State::pGLContext->RecordError( Memcpy(arrays, vaos.data(), n * sizeof(GLuint));
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteVertexArrays_State", }
"n must be non-negative."));
return;
}
for (GLsizei i = 0; i < n; ++i) { GLboolean IsVertexArray_State(GLuint array) {
GLuint vao = arrays[i]; if (array == 0) {
if (vao == 0) continue; MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "IsVertexArray_State",
"Vertex array name 0 is not supported."));
return GL_FALSE;
}
if (!VertexArrayImpl::ValidateVertexArrayName(array)) return GL_FALSE;
if (!MG_State::pGLContext->ValidateVertexArrayObject(array)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "IsVertexArray_State",
std::format("Vertex array object {} does not exist.", array)));
return GL_FALSE;
}
return GL_TRUE;
}
if (!VertexArrayImpl::ValidateVertexArrayName(vao)) continue; void VertexAttribDivisor_State(GLuint index, GLuint divisor) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
if (MG_State::pGLContext->GetBoundVertexArray() && auto& vao = MG_State::pGLContext->GetBoundVertexArray();
MG_State::pGLContext->GetBoundVertexArray() == MG_State::pGLContext->GetVertexArrayObject(vao)) { if (!vao) {
MG_State::pGLContext->BindVertexArray(0); MG_State::pGLContext->RecordError(
} ErrorCode::InvalidOperation, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "VertexAttribDivisor_State",
"No vertex array object is bound."));
MG_State::pGLContext->MarkVertexArrayForDeletion(vao); return;
}
} }
void GenVertexArrays_State(GLsizei n, GLuint* arrays) { vao->SetAttributeDivisor(index, divisor);
if (n < 0) { }
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GenVertexArrays_State", "n must be non-negative."));
return;
}
auto vaoNames = MG_State::pGLContext->GenVertexArrayNames(n); /* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
Copy(vaoNames.data(), arrays, vaoNames.size()); void VertexAttribDivisor(GLuint index, GLuint divisor) {
} VertexAttribDivisor_State(index, divisor);
}
GLboolean IsVertexArray_State(GLuint array) { GLboolean IsVertexArray(GLuint array) {
if (array == 0) { return IsVertexArray_State(array);
MG_State::pGLContext->RecordError( }
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "IsVertexArray_State",
"Vertex array name 0 is not supported."));
return GL_FALSE;
}
if (!VertexArrayImpl::ValidateVertexArrayName(array)) return GL_FALSE;
if (!MG_State::pGLContext->ValidateVertexArrayObject(array)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "IsVertexArray_State",
std::format("Vertex array object {} does not exist.", array)));
return GL_FALSE;
}
return GL_TRUE;
}
void VertexAttribDivisor_State(GLuint index, GLuint divisor) { void DisableVertexAttribArray(GLuint index) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return; DisableVertexAttribArray_State(index);
}
auto vao = MG_State::pGLContext->GetBoundVertexArray(); void EnableVertexAttribArray(GLuint index) {
if (!vao) { EnableVertexAttribArray_State(index);
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation, }
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"VertexAttribDivisor_State",
"No vertex array object is bound."));
return;
}
vao->SetAttributeDivisor(index, divisor); void VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer) {
} VertexAttribIPointer_State(index, size, type, stride, pointer);
}
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */ void VertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
void VertexAttribDivisor(GLuint index, GLuint divisor) { const void* pointer) {
VertexAttribDivisor_State(index, divisor); VertexAttribPointer_State(index, size, type, normalized, stride, pointer);
} }
GLboolean IsVertexArray(GLuint array) { void BindVertexArray(GLuint array) {
return IsVertexArray_State(array); BindVertexArray_State(array);
} }
void DisableVertexAttribArray(GLuint index) { void DeleteVertexArrays(GLsizei n, const GLuint* arrays) {
DisableVertexAttribArray_State(index); DeleteVertexArrays_State(n, arrays);
} }
void EnableVertexAttribArray(GLuint index) { void GenVertexArrays(GLsizei n, GLuint* arrays) {
EnableVertexAttribArray_State(index); GenVertexArrays_State(n, arrays);
} }
} // namespace MobileGL::MG_Impl::GLImpl
void VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer) {
VertexAttribIPointer_State(index, size, type, stride, pointer);
}
void VertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
const void* pointer) {
VertexAttribPointer_State(index, size, type, normalized, stride, pointer);
}
void BindVertexArray(GLuint array) {
BindVertexArray_State(array);
}
void DeleteVertexArrays(GLsizei n, const GLuint* arrays) {
DeleteVertexArrays_State(n, arrays);
}
void GenVertexArrays(GLsizei n, GLuint* arrays) {
GenVertexArrays_State(n, arrays);
}
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
@@ -9,18 +9,16 @@
#pragma once #pragma once
#include <Includes.h> #include <Includes.h>
namespace MobileGL { namespace MobileGL::MG_Impl::GLImpl {
namespace MG_Impl::GLImpl { /* @INSERTION_POINT:FUNCTION_DECLARATION@ */
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */ void VertexAttribDivisor(GLuint index, GLuint divisor);
void VertexAttribDivisor(GLuint index, GLuint divisor); GLboolean IsVertexArray(GLuint array);
GLboolean IsVertexArray(GLuint array); void DisableVertexAttribArray(GLuint index);
void DisableVertexAttribArray(GLuint index); void EnableVertexAttribArray(GLuint index);
void EnableVertexAttribArray(GLuint index); void VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer);
void VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer); void VertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
void VertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
const void* pointer); void BindVertexArray(GLuint array);
void BindVertexArray(GLuint array); void DeleteVertexArrays(GLsizei n, const GLuint* arrays);
void DeleteVertexArrays(GLsizei n, const GLuint* arrays); void GenVertexArrays(GLsizei n, GLuint* arrays);
void GenVertexArrays(GLsizei n, GLuint* arrays); } // namespace MobileGL::MG_Impl::GLImpl
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
@@ -12,74 +12,71 @@
#include <MG_Util/Converters/MGToGL/DataTypeConverter.h> #include <MG_Util/Converters/MGToGL/DataTypeConverter.h>
#include <MG_Util/Converters/MGToStr/DataTypeConverter.h> #include <MG_Util/Converters/MGToStr/DataTypeConverter.h>
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl {
namespace VertexArrayImpl { Bool ValidateVertexArrayName(Uint index) {
Bool isValid = MG_State::pGLContext->ValidateVertexArrayName(index);
if (!isValid) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateVertexArrayName",
std::format("Vertex array name {} is not valid.", index)));
return false;
}
return true;
}
Bool ValidateVertexArrayName(Uint index) { Bool ValidateVertexArrayObject(Uint index) {
Bool isValid = MG_State::pGLContext->ValidateVertexArrayName(index); if (!MG_State::pGLContext->ValidateVertexArrayObject(index)) {
if (!isValid) { MG_State::pGLContext->RecordError(
MG_State::pGLContext->RecordError( ErrorCode::InvalidOperation,
ErrorCode::InvalidOperation, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateVertexArrayObject",
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateVertexArrayName", std::format("Vertex array object {} does not exist.", index)));
std::format("Vertex array name {} is not valid.", index))); return false;
return false; }
} return true;
return true; }
Bool ValidateVertexAttributeIndex(Uint index) {
if (index >= MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", "ValidateVertexAttributeIndex",
std::format("Attribute index {} exceeds maximum of {}.", index,
MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS - 1)));
return false;
}
return true;
}
Bool ValidateVertexAttribPointerParams(Uint index, SizeT size, DataType type, Int stride) {
if (size < 1 || size > 4) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", "ValidateVertexAttribPointerParams",
std::format("Invalid size {} for attribute {}. Must be 1-4.", size, index)));
return false;
} }
Bool ValidateVertexArrayObject(Uint index) { if (type == DataType::Unknown) {
if (!MG_State::pGLContext->ValidateVertexArrayObject(index)) { MG_State::pGLContext->RecordError(
MG_State::pGLContext->RecordError( ErrorCode::InvalidEnum,
ErrorCode::InvalidOperation, MakeUnique<GenericErrorInfo>(
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateVertexArrayObject", "MG_Impl/GLImpl", "ValidateVertexAttribPointerParams",
std::format("Vertex array object {} does not exist.", index))); std::format("Invalid type {} for attribute {}.", MG_Util::ConvertDataTypeToString(type), index)));
return false; return false;
}
return true;
} }
Bool ValidateVertexAttributeIndex(Uint index) { if (stride < 0) {
if (index >= MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS) { MG_State::pGLContext->RecordError(
MG_State::pGLContext->RecordError( ErrorCode::InvalidValue,
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>(
MakeShared<GenericErrorInfo>( "MG_Impl/GLImpl", "ValidateVertexAttribPointerParams",
"MG_Impl/GLImpl", "ValidateVertexAttributeIndex", std::format("Negative stride {} is not allowed for attribute {}.", stride, index)));
std::format("Attribute index {} exceeds maximum of {}.", index, return false;
MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS - 1)));
return false;
}
return true;
} }
Bool ValidateVertexAttribPointerParams(Uint index, SizeT size, DataType type, Int stride) { return true;
if (size < 1 || size > 4) { }
MG_State::pGLContext->RecordError( } // namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>(
"MG_Impl/GLImpl", "ValidateVertexAttribPointerParams",
std::format("Invalid size {} for attribute {}. Must be 1-4.", size, index)));
return false;
}
if (type == DataType::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateVertexAttribPointerParams",
std::format("Invalid type {} for attribute {}.",
MG_Util::ConvertDataTypeToString(type).c_str(), index)));
return false;
}
if (stride < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>(
"MG_Impl/GLImpl", "ValidateVertexAttribPointerParams",
std::format("Negative stride {} is not allowed for attribute {}.", stride, index)));
return false;
}
return true;
}
} // namespace VertexArrayImpl
} // namespace MobileGL::MG_Impl::GLImpl
@@ -10,11 +10,9 @@
#include <Includes.h> #include <Includes.h>
#include <MG_State/GLState/VertexArrayState/VertexArrayObject.h> #include <MG_State/GLState/VertexArrayState/VertexArrayObject.h>
namespace MobileGL::MG_Impl::GLImpl { namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl {
namespace VertexArrayImpl { Bool ValidateVertexArrayName(Uint index);
Bool ValidateVertexArrayName(Uint index); Bool ValidateVertexArrayObject(Uint index);
Bool ValidateVertexArrayObject(Uint index); Bool ValidateVertexAttributeIndex(Uint index);
Bool ValidateVertexAttributeIndex(Uint index); Bool ValidateVertexAttribPointerParams(Uint index, SizeT size, DataType type, Int stride);
Bool ValidateVertexAttribPointerParams(Uint index, SizeT size, DataType type, Int stride); } // namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl
} // namespace VertexArrayImpl
} // namespace MobileGL::MG_Impl::GLImpl
+4 -6
View File
@@ -10,9 +10,7 @@
#include <Includes.h> #include <Includes.h>
#include "MG_Impl/GetProcAddress.h" #include "MG_Impl/GetProcAddress.h"
namespace MG_Impl { namespace MG_Impl::GLXImpl {
namespace GLXImpl { void* GetProcAddress(const char* name);
void* GetProcAddress(const char* name); void* GetProcAddressARB(const char* name);
void* GetProcAddressARB(const char* name); } // namespace MG_Impl::GLXImpl
} // namespace GLXImpl
} // namespace MG_Impl
File diff suppressed because it is too large Load Diff
+3 -5
View File
@@ -9,8 +9,6 @@
#pragma once #pragma once
#include <Includes.h> #include <Includes.h>
namespace MobileGL { namespace MobileGL::MG_Impl {
namespace MG_Impl { void* GetProcAddress(const char* name);
void* GetProcAddress(const char* name); } // namespace MobileGL::MG_Impl
} // namespace MG_Impl
} // namespace MobileGL
+5 -3
View File
@@ -18,10 +18,10 @@
namespace MobileGL::MG_Impl { namespace MobileGL::MG_Impl {
void Init() { void Init() {
MGLOG_D("Initializing MobileGL Implementation..."); MGLOG_D("Initializing MobileGL Implementation...");
GLImpl::TextureImpl::pProxyTextureManager = new GLImpl::TextureImpl::ProxyTextureManager(); GLImpl::TextureImpl::pProxyTextureManager = MakeUnique<GLImpl::TextureImpl::ProxyTextureManager>();
// TODO: get real info in EGL // TODO: get real info in EGL
auto fbo0 = MG_State::pGLContext->CreateFramebufferObject(0); auto& fbo0 = MG_State::pGLContext->CreateFramebufferObject(0);
auto colorTex = MakeShared<MG_State::GLState::TextureObject2D>(0); auto colorTex = MakeShared<MG_State::GLState::TextureObject2D>(0);
colorTex->SetInternalFormat(TextureInternalFormat::RGBA8); colorTex->SetInternalFormat(TextureInternalFormat::RGBA8);
colorTex->AllocateStorage(TextureUploadTarget::Texture2D, 0, {{512, 512, 1}, 0}); colorTex->AllocateStorage(TextureUploadTarget::Texture2D, 0, {{512, 512, 1}, 0});
@@ -38,6 +38,8 @@ namespace MobileGL::MG_Impl {
fbo0->AttachTexture(FramebufferAttachmentType::Depth, depthTex); fbo0->AttachTexture(FramebufferAttachmentType::Depth, depthTex);
fbo0->AttachTexture(FramebufferAttachmentType::Stencil, stencilTex); fbo0->AttachTexture(FramebufferAttachmentType::Stencil, stencilTex);
GLImpl::FramebufferImpl::pDefaultFramebufferInfo = GLImpl::FramebufferImpl::pDefaultFramebufferInfo =
new GLImpl::FramebufferImpl::DefaultFramebufferInfo(fbo0, colorTex, depthTex, stencilTex); MakeUnique<GLImpl::FramebufferImpl::DefaultFramebufferInfo>(fbo0, colorTex, depthTex, stencilTex);
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).Bind(fbo0);
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).Bind(fbo0);
} }
} // namespace MobileGL::MG_Impl } // namespace MobileGL::MG_Impl
+7 -5
View File
@@ -16,6 +16,10 @@ namespace MobileGL {
constexpr EGLint EGL_DISPLAY_MINOR_VERSION = 5; constexpr EGLint EGL_DISPLAY_MINOR_VERSION = 5;
} // namespace } // namespace
Bool EGLContext::DisplayLookupKey::operator==(const DisplayLookupKey& rhs) const {
return this->NativeDisplayKey == rhs.NativeDisplayKey && this->Platform == rhs.Platform;
}
SizeT EGLContext::DisplayLookupHasher::operator()(const DisplayLookupKey& key) const { SizeT EGLContext::DisplayLookupHasher::operator()(const DisplayLookupKey& key) const {
const auto nativeHash = std::hash<Uint64>{}(key.NativeDisplayKey); const auto nativeHash = std::hash<Uint64>{}(key.NativeDisplayKey);
const auto platformHash = std::hash<Uint64>{}(static_cast<Uint64>(key.Platform)); const auto platformHash = std::hash<Uint64>{}(static_cast<Uint64>(key.Platform));
@@ -388,8 +392,6 @@ namespace MobileGL {
*value = cfg->SurfaceType; *value = cfg->SurfaceType;
return true; return true;
case EGL_RENDERABLE_TYPE: case EGL_RENDERABLE_TYPE:
*value = cfg->RenderableType;
return true;
case EGL_CONFORMANT: case EGL_CONFORMANT:
*value = cfg->RenderableType; *value = cfg->RenderableType;
return true; return true;
@@ -878,8 +880,8 @@ namespace MobileGL {
Bool EGLContext::MakeCurrent(EGLDisplayHandle display, EGLSurfaceHandle draw, EGLSurfaceHandle read, Bool EGLContext::MakeCurrent(EGLDisplayHandle display, EGLSurfaceHandle draw, EGLSurfaceHandle read,
EGLContextHandle context) { EGLContextHandle context) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex); const std::lock_guard<std::recursive_mutex> lock(m_mutex);
const Bool releaseCurrentRequest = display == EGL_NO_DISPLAY && draw == EGL_NO_SURFACE && const Bool releaseCurrentRequest =
read == EGL_NO_SURFACE && context == nullptr; display == EGL_NO_DISPLAY && draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && context == nullptr;
const auto threadKey = CurrentThreadKey(); const auto threadKey = CurrentThreadKey();
if (releaseCurrentRequest) { if (releaseCurrentRequest) {
ReleaseThreadUnlocked(threadKey); ReleaseThreadUnlocked(threadKey);
@@ -1130,6 +1132,6 @@ namespace MobileGL {
} }
} // namespace EGLState } // namespace EGLState
EGLState::EGLContext* pEGLContext; UniquePtr<EGLState::EGLContext> pEGLContext;
} // namespace MG_State } // namespace MG_State
} // namespace MobileGL } // namespace MobileGL
+2 -2
View File
@@ -115,7 +115,7 @@ namespace MobileGL {
Uint64 NativeDisplayKey = 0; Uint64 NativeDisplayKey = 0;
EGLenum Platform = EGL_NONE; EGLenum Platform = EGL_NONE;
Bool operator==(const DisplayLookupKey& rhs) const = default; Bool operator==(const DisplayLookupKey& rhs) const;
}; };
struct DisplayLookupHasher { struct DisplayLookupHasher {
@@ -253,6 +253,6 @@ namespace MobileGL {
}; };
} // namespace EGLState } // namespace EGLState
extern EGLState::EGLContext* pEGLContext; extern UniquePtr<EGLState::EGLContext> pEGLContext;
} // namespace MG_State } // namespace MG_State
} // namespace MobileGL } // namespace MobileGL
@@ -7,210 +7,203 @@
// End of Source File Header // End of Source File Header
#include "BufferObject.h" #include "BufferObject.h"
#include "MG_Util/Types.h"
namespace MobileGL { namespace MobileGL::MG_State::GLState {
namespace MG_State { BufferObject::BufferObject(Uint externalIndex)
namespace GLState { : m_externalIndex(externalIndex), m_size(0), m_usage(BufferUsage::StaticDraw), m_isMapped(false),
BufferObject::BufferObject(Uint externalIndex) m_mappingAccess(BufferMappingAccessBit::Null),
: m_externalIndex(externalIndex), m_size(0), m_usage(BufferUsage::StaticDraw), m_isMapped(false), m_change(BufferChangeBits::DirtyBit | BufferChangeBits::PreferReallocationBit), m_mappedRange({0, 0}),
m_mappingAccess(BufferMappingAccessBit::Null), m_dataPtr(MakeShared<Data>()), m_ownsStagingData{} {
m_change(BufferChangeBits::DirtyBit | BufferChangeBits::PreferReallocationBit), m_mappedRange({0, 0}), m_change.DirtyRanges.reserve(BufferChange::DEFAULT_RESERVED_DIRTY_RANGES_COUNT);
m_dataPtr(MakeShared<Data>()) { }
m_change.DirtyRanges.reserve(BufferChange::DEFAULT_RESERVED_DIRTY_RANGES_COUNT);
}
void BufferObject::Resize(SizeT size) { void BufferObject::Resize(SizeT size) {
m_size = size; m_size = size;
m_dataPtr->reserve(std::bit_ceil(size)); // power-of-2 reserve m_dataPtr->reserve(std::bit_ceil(size)); // power-of-2 reserve
m_dataPtr->resize(size); m_dataPtr->resize(size);
m_change.Bits |= BufferChangeBits::DirtyBit;
m_change.Bits |= BufferChangeBits::PreferReallocationBit;
}
void BufferObject::UploadData(DataPtr data, SizeT atOffset) {
MOBILEGL_ASSERT(atOffset + data.size <= m_size,
"UploadData out of bounds: atOffset (%zu) + data.size (%zu) > m_size (%zu)", atOffset,
data.size, m_size);
MOBILEGL_ASSERT(!m_isMapped, "Cannot upload data while buffer is mapped.");
Memcpy(m_dataPtr->data() + atOffset, data.data, data.size);
m_change.DirtyRanges.Add({atOffset, atOffset + data.size});
m_change.Bits |= BufferChangeBits::DirtyBit;
m_change.Bits |= BufferChangeBits::ForbidInvalidationBit;
m_change.Bits |= BufferChangeBits::ForbidUnsynchronizationBit;
// This function may be called by `glBufferData`, but we still set the forbid bits above,
// because when `PreferReallocationBit` is set, those bits are ignored anyway.
// The bits can fit the `glBufferSubData` semantics
// (though `glBufferSubData` calls `UploadSubData` instead).
}
void BufferObject::SetUsage(BufferUsage usage) {
m_usage = usage;
}
void BufferObject::ReleaseMemory() {
if (!m_isMapped) return;
if (m_mappingAccess & BufferMappingAccessBit::Write) { // if we wrote to the buffer
if (!(m_mappingAccess & BufferMappingAccessBit::FlushExplicit)) { // if we didn't flush explicitly
Memcpy(m_dataPtr->data() + m_mappedRange.start, m_stagingData.data(),
m_mappedRange.end - m_mappedRange.start);
m_change.DirtyRanges.Add({m_mappedRange.start, m_mappedRange.end});
m_change.Bits |= BufferChangeBits::DirtyBit; m_change.Bits |= BufferChangeBits::DirtyBit;
m_change.Bits |= BufferChangeBits::PreferReallocationBit;
} }
void BufferObject::UploadData(DataPtr data, SizeT atOffset) { m_stagingData.clear();
MOBILEGL_ASSERT(atOffset + data.size <= m_size, }
"UploadData out of bounds: atOffset (%zu) + data.size (%zu) > m_size (%zu)", atOffset,
data.size, m_size);
MOBILEGL_ASSERT(!m_isMapped, "Cannot upload data while buffer is mapped.");
Memcpy(m_dataPtr->data() + atOffset, data.data, data.size);
m_change.DirtyRanges.Add({atOffset, atOffset + data.size});
m_change.Bits |= BufferChangeBits::DirtyBit;
m_change.Bits |= BufferChangeBits::ForbidInvalidationBit;
m_change.Bits |= BufferChangeBits::ForbidUnsynchronizationBit;
// This function may be called by `glBufferData`, but we still set the forbid bits above,
// because when `PreferReallocationBit` is set, those bits are ignored anyway.
// The bits can fit the `glBufferSubData` semantics
// (though `glBufferSubData` calls `UploadSubData` instead).
}
void BufferObject::SetUsage(BufferUsage usage) { m_isMapped = false;
m_usage = usage; m_mappingAccess = BufferMappingAccessBit::Null;
} m_mappedRange = {0, 0};
m_ownsStagingData = false;
}
void BufferObject::ReleaseMemory() { void BufferObject::FlushMemoryRange(SizeT offset, SizeT length) {
if (!m_isMapped) return; MOBILEGL_ASSERT(m_isMapped, "Buffer must be mapped to flush memory range.");
MOBILEGL_ASSERT((m_mappingAccess & BufferMappingAccessBit::FlushExplicit),
"Buffer must be mapped with FlushExplicit access to flush memory range.");
MOBILEGL_ASSERT((m_mappingAccess & BufferMappingAccessBit::Write),
"Buffer must be mapped with Write access to flush memory range.");
if (m_mappingAccess & BufferMappingAccessBit::Write) { // if we wrote to the buffer SizeT start = m_mappedRange.start + offset;
if (!(m_mappingAccess & BufferMappingAccessBit::FlushExplicit)) { // if we didn't flush explicitly SizeT end = start + length;
Memcpy(m_dataPtr->data() + m_mappedRange.start, m_stagingData.data(), MOBILEGL_ASSERT(end <= m_mappedRange.end, "Flush range out of bounds: mappedRange.end (%zu) < end (%zu)",
m_mappedRange.end - m_mappedRange.start); m_mappedRange.end, end);
m_change.DirtyRanges.Add({m_mappedRange.start, m_mappedRange.end});
m_change.Bits |= BufferChangeBits::DirtyBit;
}
m_stagingData.clear(); Memcpy(m_dataPtr->data() + start, m_stagingData.data() + offset, length);
m_change.DirtyRanges.Add({start, end});
m_change.Bits |= BufferChangeBits::DirtyBit;
}
void BufferObject::UploadSubData(DataPtr data, SizeT atOffset) {
MOBILEGL_ASSERT(!m_isMapped, "Cannot upload sub data while buffer is mapped.");
MOBILEGL_ASSERT(atOffset + data.size <= m_size,
"UploadSubData out of bounds: atOffset (%zu) + data.size (%zu) > m_size (%zu)", atOffset,
data.size, m_size);
Memcpy(m_dataPtr->data() + atOffset, data.data, data.size);
m_change.DirtyRanges.Add({atOffset, atOffset + data.size});
m_change.Bits |= BufferChangeBits::DirtyBit;
m_change.Bits |= BufferChangeBits::ForbidInvalidationBit;
m_change.Bits |= BufferChangeBits::ForbidUnsynchronizationBit;
}
void BufferObject::CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset, SizeT size) {
MOBILEGL_ASSERT(!m_isMapped, "Cannot copy data while buffer is mapped.");
MOBILEGL_ASSERT(!src->IsMapped(), "Cannot copy data from a buffer that is mapped.");
MOBILEGL_ASSERT(srcOffset + size <= src->GetSize(),
"Source buffer copy out of bounds: srcOffset (%zu) + size (%zu) > src->GetSize() (%zu)",
srcOffset, size, src->GetSize());
MOBILEGL_ASSERT(dstOffset + size <= m_size,
"Destination buffer copy out of bounds: dstOffset (%zu) + size (%zu) > m_size (%zu)", dstOffset,
size, m_size);
const Uint8* srcData = src->m_dataPtr->data() + srcOffset;
Memcpy(m_dataPtr->data() + dstOffset, srcData, size);
m_change.DirtyRanges.Add({dstOffset, dstOffset + size});
m_change.Bits |= BufferChangeBits::DirtyBit;
}
void* BufferObject::AcquireMemory(Bool markMapped, Bool read, Bool write) {
if (markMapped) {
m_isMapped = true;
auto a = BufferMappingAccessBit::Coherent | BufferMappingAccessBit::Read;
m_mappingAccess = (read ? BufferMappingAccessBit::Read : BufferMappingAccessBit::Null) |
(write ? BufferMappingAccessBit::Write : BufferMappingAccessBit::Null);
m_mappedRange = {0, m_size};
if (m_mappingAccess & BufferMappingAccessBit::Write) {
m_stagingData.resize(m_size);
m_ownsStagingData = true;
if (!(m_mappingAccess &
(BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer))) {
Memcpy(m_stagingData.data(), m_dataPtr->data(), m_size);
} }
m_isMapped = false; return m_stagingData.data();
m_mappingAccess = BufferMappingAccessBit::Null; }
m_mappedRange = {0, 0}; }
m_ownsStagingData = false;
return m_dataPtr->data();
}
void* BufferObject::AcquireMemoryRange(Range1D range, Flags<BufferMappingAccessBit> access) {
MOBILEGL_ASSERT(range.end <= m_size && range.start <= range.end,
"AcquireMemoryRange out of bounds: range (%zu, %zu) exceeds m_size (%zu)", range.start,
range.end, m_size);
m_isMapped = true;
m_mappingAccess = access;
m_mappedRange = range;
if (access & BufferMappingAccessBit::Write) {
m_stagingData.resize(range.end - range.start);
m_ownsStagingData = true;
if (!(access & (BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer))) {
Memcpy(m_stagingData.data(), m_dataPtr->data() + range.start, m_stagingData.size());
} }
void BufferObject::FlushMemoryRange(SizeT offset, SizeT length) { return m_stagingData.data();
MOBILEGL_ASSERT(m_isMapped, "Buffer must be mapped to flush memory range."); } else {
MOBILEGL_ASSERT((m_mappingAccess & BufferMappingAccessBit::FlushExplicit), m_ownsStagingData = false;
"Buffer must be mapped with FlushExplicit access to flush memory range."); return m_dataPtr->data() + range.start;
MOBILEGL_ASSERT((m_mappingAccess & BufferMappingAccessBit::Write), }
"Buffer must be mapped with Write access to flush memory range.");
SizeT start = m_mappedRange.start + offset; m_change.Bits |=
SizeT end = start + length; !(access & BufferMappingAccessBit::InvalidateBuffer || access & BufferMappingAccessBit::InvalidateRange)
MOBILEGL_ASSERT(end <= m_mappedRange.end, ? BufferChangeBits::ForbidInvalidationBit
"Flush range out of bounds: mappedRange.end (%zu) < end (%zu)", m_mappedRange.end, end); : BufferChangeBits::None;
m_change.Bits |= !(access & BufferMappingAccessBit::Unsynchronized)
? BufferChangeBits::ForbidUnsynchronizationBit
: BufferChangeBits::None;
}
Memcpy(m_dataPtr->data() + start, m_stagingData.data() + offset, length); const SharedPtr<Data>& BufferObject::GetDataReadOnly() const {
m_change.DirtyRanges.Add({start, end}); return m_dataPtr;
m_change.Bits |= BufferChangeBits::DirtyBit; }
}
void BufferObject::UploadSubData(DataPtr data, SizeT atOffset) { void BufferObject::ClearDirty() {
MOBILEGL_ASSERT(!m_isMapped, "Cannot upload sub data while buffer is mapped."); m_change.DirtyRanges.clear();
MOBILEGL_ASSERT(atOffset + data.size <= m_size, m_change.Bits = BufferChangeBits::None;
"UploadSubData out of bounds: atOffset (%zu) + data.size (%zu) > m_size (%zu)", }
atOffset, data.size, m_size);
Memcpy(m_dataPtr->data() + atOffset, data.data, data.size); SizeT BufferObject::GetSize() const {
m_change.DirtyRanges.Add({atOffset, atOffset + data.size}); return m_size;
m_change.Bits |= BufferChangeBits::DirtyBit; }
m_change.Bits |= BufferChangeBits::ForbidInvalidationBit;
m_change.Bits |= BufferChangeBits::ForbidUnsynchronizationBit;
}
void BufferObject::CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset, BufferUsage BufferObject::GetUsage() const {
SizeT size) { return m_usage;
MOBILEGL_ASSERT(!m_isMapped, "Cannot copy data while buffer is mapped."); }
MOBILEGL_ASSERT(!src->IsMapped(), "Cannot copy data from a buffer that is mapped.");
MOBILEGL_ASSERT(srcOffset + size <= src->GetSize(),
"Source buffer copy out of bounds: srcOffset (%zu) + size (%zu) > src->GetSize() (%zu)",
srcOffset, size, src->GetSize());
MOBILEGL_ASSERT(dstOffset + size <= m_size,
"Destination buffer copy out of bounds: dstOffset (%zu) + size (%zu) > m_size (%zu)",
dstOffset, size, m_size);
const Uint8* srcData = src->m_dataPtr->data() + srcOffset; const VecRange1D& BufferObject::GetDirtyRanges() const {
Memcpy(m_dataPtr->data() + dstOffset, srcData, size); return m_change.DirtyRanges;
m_change.DirtyRanges.Add({dstOffset, dstOffset + size}); }
m_change.Bits |= BufferChangeBits::DirtyBit;
}
void* BufferObject::AcquireMemory(Bool markMapped, Bool read, Bool write) { Flags<BufferChangeBits> BufferObject::GetChangeBits() const {
if (markMapped) { return m_change.Bits;
m_isMapped = true; }
auto a = BufferMappingAccessBit::Coherent | BufferMappingAccessBit::Read;
m_mappingAccess = (read ? BufferMappingAccessBit::Read : BufferMappingAccessBit::Null) |
(write ? BufferMappingAccessBit::Write : BufferMappingAccessBit::Null);
m_mappedRange = {0, m_size};
if (m_mappingAccess & BufferMappingAccessBit::Write) { Bool BufferObject::IsMapped() const {
m_stagingData.resize(m_size); return m_isMapped;
m_ownsStagingData = true; }
if (!(m_mappingAccess & Range1D BufferObject::GetMappedRange() const {
(BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer))) { return m_isMapped ? m_mappedRange : Range1D{0, 0};
Memcpy(m_stagingData.data(), m_dataPtr->data(), m_size); }
}
return m_stagingData.data(); Flags<BufferMappingAccessBit> BufferObject::GetMappingAccess() const {
} return m_isMapped ? m_mappingAccess : BufferMappingAccessBit::Null;
} }
return m_dataPtr->data(); Uint BufferObject::GetExternalIndex() const {
} return m_externalIndex;
}
void* BufferObject::AcquireMemoryRange(Range1D range, Flags<BufferMappingAccessBit> access) { } // namespace MobileGL::MG_State::GLState
MOBILEGL_ASSERT(range.end <= m_size && range.start <= range.end,
"AcquireMemoryRange out of bounds: range (%zu, %zu) exceeds m_size (%zu)", range.start,
range.end, m_size);
m_isMapped = true;
m_mappingAccess = access;
m_mappedRange = range;
if (access & BufferMappingAccessBit::Write) {
m_stagingData.resize(range.end - range.start);
m_ownsStagingData = true;
if (!(access &
(BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer))) {
Memcpy(m_stagingData.data(), m_dataPtr->data() + range.start, m_stagingData.size());
}
return m_stagingData.data();
} else {
m_ownsStagingData = false;
return m_dataPtr->data() + range.start;
}
m_change.Bits |= !(access & BufferMappingAccessBit::InvalidateBuffer ||
access & BufferMappingAccessBit::InvalidateRange)
? BufferChangeBits::ForbidInvalidationBit
: BufferChangeBits::None;
m_change.Bits |= !(access & BufferMappingAccessBit::Unsynchronized)
? BufferChangeBits::ForbidUnsynchronizationBit
: BufferChangeBits::None;
}
const SharedPtr<Data> BufferObject::GetDataReadOnly() const {
return m_dataPtr;
}
void BufferObject::ClearDirty() {
m_change.DirtyRanges.clear();
m_change.Bits = BufferChangeBits::None;
}
SizeT BufferObject::GetSize() const {
return m_size;
}
BufferUsage BufferObject::GetUsage() const {
return m_usage;
}
const VecRange1D& BufferObject::GetDirtyRanges() const {
return m_change.DirtyRanges;
}
Flags<BufferChangeBits> BufferObject::GetChangeBits() const {
return m_change.Bits;
}
Bool BufferObject::IsMapped() const {
return m_isMapped;
}
Range1D BufferObject::GetMappedRange() const {
return m_isMapped ? m_mappedRange : Range1D{0, 0};
}
Flags<BufferMappingAccessBit> BufferObject::GetMappingAccess() const {
return m_isMapped ? m_mappingAccess : BufferMappingAccessBit::Null;
}
Uint BufferObject::GetExternalIndex() const {
return m_externalIndex;
}
} // namespace GLState
} // namespace MG_State
} // namespace MobileGL
@@ -7,7 +7,6 @@
// End of Source File Header // End of Source File Header
#pragma once #pragma once
#include "MG_Util/Types.h"
#include <Includes.h> #include <Includes.h>
#include <MG_Util/Math/VectorTypes.h> #include <MG_Util/Math/VectorTypes.h>
@@ -73,47 +72,45 @@ namespace MobileGL {
VecRange1D DirtyRanges; VecRange1D DirtyRanges;
}; };
namespace MG_State { namespace MG_State::GLState {
namespace GLState { class BufferObject {
class BufferObject { public:
public: using TargetEnum = BufferTarget;
using TargetEnum = BufferTarget;
BufferObject(Uint externalIndex); BufferObject(Uint externalIndex);
void Resize(SizeT size); void Resize(SizeT size);
void UploadData(DataPtr data, SizeT atOffset); void UploadData(DataPtr data, SizeT atOffset);
void SetUsage(BufferUsage usage); void SetUsage(BufferUsage usage);
void* AcquireMemory(Bool markMapped, Bool read, Bool write); void* AcquireMemory(Bool markMapped, Bool read, Bool write);
void* AcquireMemoryRange(Range1D range, Flags<BufferMappingAccessBit> access); void* AcquireMemoryRange(Range1D range, Flags<BufferMappingAccessBit> access);
void ReleaseMemory(); void ReleaseMemory();
void FlushMemoryRange(SizeT offset, SizeT length); void FlushMemoryRange(SizeT offset, SizeT length);
void UploadSubData(DataPtr data, SizeT atOffset); void UploadSubData(DataPtr data, SizeT atOffset);
void CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset, SizeT size); void CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset, SizeT size);
void ClearDirty(); void ClearDirty();
Bool IsMapped() const; Bool IsMapped() const;
SizeT GetSize() const; SizeT GetSize() const;
BufferUsage GetUsage() const; BufferUsage GetUsage() const;
Range1D GetMappedRange() const; Range1D GetMappedRange() const;
const SharedPtr<Data> GetDataReadOnly() const; const SharedPtr<Data>& GetDataReadOnly() const;
Flags<BufferMappingAccessBit> GetMappingAccess() const; Flags<BufferMappingAccessBit> GetMappingAccess() const;
Uint GetExternalIndex() const; Uint GetExternalIndex() const;
const VecRange1D& GetDirtyRanges() const; const VecRange1D& GetDirtyRanges() const;
Flags<BufferChangeBits> GetChangeBits() const; Flags<BufferChangeBits> GetChangeBits() const;
private: private:
const Uint m_externalIndex = 0; const Uint m_externalIndex = 0;
SizeT m_size = 0; SizeT m_size = 0;
BufferUsage m_usage = BufferUsage::StaticDraw; BufferUsage m_usage = BufferUsage::StaticDraw;
SharedPtr<Data> m_dataPtr; SharedPtr<Data> m_dataPtr;
Bool m_isMapped; Bool m_isMapped;
Flags<BufferMappingAccessBit> m_mappingAccess; Flags<BufferMappingAccessBit> m_mappingAccess;
BufferChange m_change; BufferChange m_change;
Range1D m_mappedRange; Range1D m_mappedRange;
Vector<Uint8> m_stagingData; Vector<Uint8> m_stagingData;
Bool m_ownsStagingData; Bool m_ownsStagingData;
}; };
} // namespace GLState } // namespace MG_State::GLState
} // namespace MG_State
} // namespace MobileGL } // namespace MobileGL
@@ -8,78 +8,75 @@
#include "BufferState.h" #include "BufferState.h"
namespace MobileGL { namespace MobileGL::MG_State::GLState {
namespace MG_State { BufferState::BufferState() : m_indexGenerator(1024, 1) {
namespace GLState { for (SizeT i = 0; i < m_bindingSlots.size(); ++i) {
BufferState::BufferState() : m_indexGenerator(1024, 1) { m_bindingSlots[i] = BindingSlot<BufferObject>(GlobalBufferTargets[i]);
for (SizeT i = 0; i < m_bindingSlots.size(); ++i) { }
m_bindingSlots[i] = BindingSlot<BufferObject>(GlobalBufferTargets[i]); }
}
}
SharedPtr<BufferObject> BufferState::GetBufferObject(Uint index) { const SharedPtr<BufferObject>& BufferState::GetBufferObject(Uint index) {
auto it = m_bufferObjects.find(index); auto it = m_bufferObjects.find(index);
if (it != m_bufferObjects.end()) { if (it != m_bufferObjects.end()) {
return it->second; return it->second;
} }
return nullptr; static SharedPtr<BufferObject> nullBufferObject = nullptr;
} return nullBufferObject;
}
Vector<Uint> BufferState::GenerateNames(Uint number) { void BufferState::GenerateNames(Uint number, Vector<Uint>& buffers) {
Vector<Uint> buffers(number); buffers.resize(number);
m_indexGenerator.Generate(number, buffers.data()); m_indexGenerator.Generate(number, buffers.data());
return buffers; }
}
SharedPtr<BufferObject> BufferState::CreateBufferObject(Uint index) { const SharedPtr<BufferObject>& BufferState::CreateBufferObject(Uint index) {
auto bufferObject = MakeShared<BufferObject>(index); auto& bufferObj = m_bufferObjects[index];
m_bufferObjects[index] = bufferObject; if (!bufferObj) {
return bufferObject; bufferObj = MakeShared<BufferObject>(index);
} }
return bufferObj;
}
BindingSlot<BufferObject>& BufferState::GetBindingSlot(BufferTarget target) { BindingSlot<BufferObject>& BufferState::GetBindingSlot(BufferTarget target) {
for (SizeT i = 0; i < m_bindingSlots.size(); ++i) { for (auto& bindingSlot : m_bindingSlots) {
if (m_bindingSlots[i].GetTarget() == target) { if (bindingSlot.GetTarget() == target) {
return m_bindingSlots[i]; return bindingSlot;
}
}
MOBILEGL_ASSERT(false, "Invalid BufferTarget enum value: %d", static_cast<int>(target));
return m_bindingSlots[0];
}
void BufferState::MarkBufferObjectForDeletion(Uint index) {
if (m_indexGenerator.IsValid(index)) {
auto it = m_bufferObjects.find(index);
if (it != m_bufferObjects.end()) {
for (auto& bindingSlot : m_bindingSlots) {
if (bindingSlot.GetBoundObject() == it->second) {
bindingSlot.Bind(nullptr);
} }
} }
MOBILEGL_ASSERT(false, "Invalid BufferTarget enum value: %d", static_cast<int>(target)); m_bufferObjects.erase(it);
return m_bindingSlots[0];
} }
m_indexGenerator.Delete(index);
}
}
void BufferState::MarkBufferObjectForDeletion(Uint index) { Bool BufferState::ValidateName(Uint index) const {
if (m_indexGenerator.IsValid(index)) { return m_indexGenerator.IsValid(index);
auto it = m_bufferObjects.find(index); }
if (it != m_bufferObjects.end()) {
for (SizeT i = 0; i < m_bindingSlots.size(); ++i) {
if (m_bindingSlots[i].GetBoundObject() == it->second) {
m_bindingSlots[i].Bind(nullptr);
}
}
m_bufferObjects.erase(it);
}
m_indexGenerator.Delete(index);
}
}
Bool BufferState::ValidateName(Uint index) const { Bool BufferState::ValidateBufferObject(Uint index) const {
return m_indexGenerator.IsValid(index); return m_bufferObjects.find(index) != m_bufferObjects.end();
} }
Bool BufferState::ValidateBufferObject(Uint index) const { BindingSlotRange1D<BufferObject>& BufferState::GetBindingPoint(BufferTarget target, Uint index) {
return m_bufferObjects.find(index) != m_bufferObjects.end(); for (SizeT i = 0; i < BufferBindPointTargets.size(); ++i) {
if (BufferBindPointTargets[i] == target) {
return m_bufferBindPointTargets[i][index];
} }
}
BindingSlotRange1D<BufferObject>& BufferState::GetBindingPoint(BufferTarget target, Uint index) { MOBILEGL_ASSERT(false, "Invalid BufferTarget enum value for binding point: %d", static_cast<int>(target));
for (SizeT i = 0; i < BufferBindPointTargets.size(); ++i) { return m_bufferBindPointTargets[0][index];
if (BufferBindPointTargets[i] == target) { }
return m_bufferBindPointTargets[i][index]; } // namespace MobileGL::MG_State::GLState
}
}
MOBILEGL_ASSERT(false, "Invalid BufferTarget enum value for binding point: %d",
static_cast<int>(target));
return m_bufferBindPointTargets[0][index];
}
} // namespace GLState
} // namespace MG_State
} // namespace MobileGL
@@ -11,46 +11,40 @@
#include <MG_Util/Miscellany/IndexGenerator.h> #include <MG_Util/Miscellany/IndexGenerator.h>
#include "BufferObject.h" #include "BufferObject.h"
namespace MobileGL { namespace MobileGL::MG_State::GLState {
namespace MG_State { constexpr const auto GlobalBufferTargets =
namespace GLState { ToArray(BufferTarget::Vertex, BufferTarget::Uniform, BufferTarget::CopyRead, BufferTarget::CopyWrite,
constexpr const auto GlobalBufferTargets = BufferTarget::PixelPack, BufferTarget::PixelUnpack, BufferTarget::Query, BufferTarget::Texture,
ToArray(BufferTarget::Vertex, BufferTarget::Uniform, BufferTarget::CopyRead, BufferTarget::CopyWrite, BufferTarget::TransformFeedback, BufferTarget::AtomicCounter, BufferTarget::DispatchIndirect,
BufferTarget::PixelPack, BufferTarget::PixelUnpack, BufferTarget::Query, BufferTarget::Texture, BufferTarget::DrawIndirect, BufferTarget::ShaderStorage);
BufferTarget::TransformFeedback, BufferTarget::AtomicCounter, BufferTarget::DispatchIndirect, constexpr const auto BufferBindPointTargets = ToArray(BufferTarget::Uniform, BufferTarget::TransformFeedback,
BufferTarget::DrawIndirect, BufferTarget::ShaderStorage); BufferTarget::AtomicCounter, BufferTarget::ShaderStorage);
constexpr const auto BufferBindPointTargets =
ToArray(BufferTarget::Uniform, BufferTarget::TransformFeedback, BufferTarget::AtomicCounter,
BufferTarget::ShaderStorage);
class BufferState { class BufferState {
public: public:
BufferState(); BufferState();
SharedPtr<BufferObject> GetBufferObject(Uint index); const SharedPtr<BufferObject>& GetBufferObject(Uint index);
Vector<Uint> GenerateNames(Uint number); void GenerateNames(Uint number, Vector<Uint>& buffers);
SharedPtr<BufferObject> CreateBufferObject(Uint index); const SharedPtr<BufferObject>& CreateBufferObject(Uint index);
BindingSlot<BufferObject>& GetBindingSlot(BufferTarget target); BindingSlot<BufferObject>& GetBindingSlot(BufferTarget target);
// For glBindBufferBase / glBindBufferRange // For glBindBufferBase / glBindBufferRange
BindingSlotRange1D<BufferObject>& GetBindingPoint(BufferTarget target, Uint index); BindingSlotRange1D<BufferObject>& GetBindingPoint(BufferTarget target, Uint index);
constexpr SizeT GetBindingPointCount(const BufferTarget target) const { constexpr SizeT GetBindingPointCount(const BufferTarget target) const {
auto it = std::find(BufferBindPointTargets.begin(), BufferBindPointTargets.end(), target); auto it = std::find(BufferBindPointTargets.begin(), BufferBindPointTargets.end(), target);
auto index = std::distance(BufferBindPointTargets.begin(), it); auto index = std::distance(BufferBindPointTargets.begin(), it);
return m_bufferBindPointTargets[index].size(); return m_bufferBindPointTargets[index].size();
} }
void MarkBufferObjectForDeletion(Uint index); void MarkBufferObjectForDeletion(Uint index);
Bool ValidateName(Uint index) const; Bool ValidateName(Uint index) const;
Bool ValidateBufferObject(Uint index) const; Bool ValidateBufferObject(Uint index) const;
private: private:
UnorderedMap<Uint, SharedPtr<BufferObject>> m_bufferObjects; UnorderedMap<Uint, SharedPtr<BufferObject>> m_bufferObjects;
IndexGenerator<Uint> m_indexGenerator; IndexGenerator<Uint> m_indexGenerator;
Array<BindingSlot<BufferObject>, GlobalBufferTargets.size()> m_bindingSlots; Array<BindingSlot<BufferObject>, GlobalBufferTargets.size()> m_bindingSlots;
// TODO: query the count somewhere globally? // TODO: query the count somewhere globally?
// For glBindBufferBase / glBindBufferRange // For glBindBufferBase / glBindBufferRange
Array<Array<BindingSlotRange1D<BufferObject>, 16>, BufferBindPointTargets.size()> Array<Array<BindingSlotRange1D<BufferObject>, 16>, BufferBindPointTargets.size()> m_bufferBindPointTargets;
m_bufferBindPointTargets; };
}; } // namespace MobileGL::MG_State::GLState
} // namespace GLState
} // namespace MG_State
} // namespace MobileGL
+336 -336
View File
@@ -10,436 +10,436 @@
#include "MG_State/GLState/RenderbufferState/RenderbufferObject.h" #include "MG_State/GLState/RenderbufferState/RenderbufferObject.h"
#include "MG_State/EGLState/Core.h" #include "MG_State/EGLState/Core.h"
namespace MobileGL { namespace MobileGL::MG_State {
namespace MG_State { void Init() {
void Init() { MGLOG_D("Initializing MobileGL State...");
MGLOG_D("Initializing MobileGL State..."); pGLContext = MakeUnique<GLState::GLContext>();
pGLContext = new MG_State::GLState::GLContext(); pEGLContext = MakeUnique<EGLState::EGLContext>();
pEGLContext = new MG_State::EGLState::EGLContext(); }
namespace GLState {
// Error
void GLContext::RecordError(ErrorCode code, UniquePtr<ErrorInfo> info) {
m_errorState.RecordError(code, Move(info));
} }
namespace GLState { Bool GLContext::HasGLError() const {
// Error return m_errorState.HasGLError();
void GLContext::RecordError(ErrorCode code, SharedPtr<ErrorInfo> info) { }
m_errorState.RecordError(code, info);
Optional<const Error*> GLContext::PeekGLError() const {
return m_errorState.PeekGLError();
}
Optional<UniquePtr<Error>> GLContext::PopGLError() {
return Move(m_errorState.PopGLError());
}
Bool GLContext::HasNonGLError() const {
return m_errorState.HasNonGLError();
}
Optional<const Error*> GLContext::PeekNonGLError() const {
return m_errorState.PeekNonGLError();
}
Optional<UniquePtr<Error>> GLContext::PopNonGLError() {
return Move(m_errorState.PopNonGLError());
}
void GLContext::ClearErrors() {
m_errorState.Clear();
}
// Buffer
void GLContext::GenBufferNames(Uint number, Vector<Uint>& buffers) {
m_bufferState.GenerateNames(number, buffers);
}
const SharedPtr<BufferObject>& GLContext::GetBufferObject(Uint index) {
return m_bufferState.GetBufferObject(index);
}
BindingSlot<BufferObject>& GLContext::GetBufferBindingSlot(BufferTarget target) {
if (target == BufferTarget::Index) {
const auto& vao = m_vertexArrayState.GetBoundVertexArray();
MOBILEGL_ASSERT(vao != nullptr, "No VAO is currently bound when accessing index buffer binding slot.");
return vao->GetIndexBufferBindingSlot();
} }
Bool GLContext::HasGLError() const { return m_bufferState.GetBindingSlot(target);
return m_errorState.HasGLError(); }
}
Optional<const Error> GLContext::PeekGLError() const { BindingSlotRange1D<BufferObject>& GLContext::GetBufferBindingPoint(BufferTarget target, Uint index) {
return m_errorState.PeekGLError(); return m_bufferState.GetBindingPoint(target, index);
} }
Optional<Error> GLContext::PopGLError() { const SharedPtr<BufferObject>& GLContext::CreateBufferObject(Uint index) {
return m_errorState.PopGLError(); return m_bufferState.CreateBufferObject(index);
} }
Bool GLContext::HasNonGLError() const { void GLContext::MarkBufferObjectForDeletion(Uint index) {
return m_errorState.HasNonGLError(); if (ValidateBufferObject(index)) {
} auto bufferObject = m_bufferState.GetBufferObject(index);
auto& vaos = m_vertexArrayState.GetAllVertexArrays();
for (auto& vao : vaos) {
if (vao == nullptr) continue;
Optional<const Error> GLContext::PeekNonGLError() const { if (vao->GetIndexBufferBindingSlot().GetBoundObject() == bufferObject) {
return m_errorState.PeekNonGLError(); vao->GetIndexBufferBindingSlot().Bind(nullptr);
} }
for (SizeT j = 0; j < VertexArrayObject::MAX_VERTEX_ATTRIBS; ++j) {
Optional<Error> GLContext::PopNonGLError() { if (vao->GetAttribute(j).Buffer == bufferObject) {
return m_errorState.PopNonGLError(); vao->BindAttributeBuffer(j, nullptr);
}
void GLContext::ClearErrors() {
m_errorState.Clear();
}
// Buffer
Vector<Uint> GLContext::GenBufferNames(Uint number) {
return m_bufferState.GenerateNames(number);
}
SharedPtr<BufferObject> GLContext::GetBufferObject(Uint index) {
return m_bufferState.GetBufferObject(index);
}
BindingSlot<BufferObject>& GLContext::GetBufferBindingSlot(BufferTarget target) {
if (target == BufferTarget::Index) {
const auto& vao = m_vertexArrayState.GetBoundVertexArray();
MOBILEGL_ASSERT(vao != nullptr,
"No VAO is currently bound when accessing index buffer binding slot.");
return vao->GetIndexBufferBindingSlot();
}
return m_bufferState.GetBindingSlot(target);
}
BindingSlotRange1D<BufferObject>& GLContext::GetBufferBindingPoint(BufferTarget target, Uint index) {
return m_bufferState.GetBindingPoint(target, index);
}
SharedPtr<BufferObject> GLContext::CreateBufferObject(Uint index) {
return m_bufferState.CreateBufferObject(index);
}
void GLContext::MarkBufferObjectForDeletion(Uint index) {
if (ValidateBufferObject(index)) {
auto bufferObject = m_bufferState.GetBufferObject(index);
auto& vaos = m_vertexArrayState.GetAllVertexArrays();
for (SizeT i = 0; i < vaos.size(); ++i) {
auto vao = vaos[i];
if (vao == nullptr) continue;
if (vao->GetIndexBufferBindingSlot().GetBoundObject() == bufferObject) {
vao->GetIndexBufferBindingSlot().Bind(nullptr);
}
for (SizeT j = 0; j < VertexArrayObject::MAX_VERTEX_ATTRIBS; ++j) {
if (vao->GetAttribute(j).Buffer == bufferObject) {
vao->BindAttributeBuffer(j, nullptr);
}
} }
} }
} }
m_bufferState.MarkBufferObjectForDeletion(index);
} }
Bool GLContext::ValidateBufferName(Uint index) const { m_bufferState.MarkBufferObjectForDeletion(index);
return m_bufferState.ValidateName(index); }
}
Bool GLContext::ValidateBufferObject(Uint index) const { Bool GLContext::ValidateBufferName(Uint index) const {
return m_bufferState.ValidateBufferObject(index); return m_bufferState.ValidateName(index);
} }
// VertexArray Bool GLContext::ValidateBufferObject(Uint index) const {
Vector<Uint> GLContext::GenVertexArrayNames(Uint number) { return m_bufferState.ValidateBufferObject(index);
return m_vertexArrayState.GenerateNames(number); }
}
SharedPtr<VertexArrayObject> GLContext::GetVertexArrayObject(Uint index) { // VertexArray
return m_vertexArrayState.GetVertexArrayObject(index); void GLContext::GenVertexArrayNames(Uint number, Vector<Uint>& vertexArrays) {
} m_vertexArrayState.GenerateNames(number, vertexArrays);
}
void GLContext::BindVertexArray(Uint index) { const SharedPtr<VertexArrayObject>& GLContext::GetVertexArrayObject(Uint index) {
m_vertexArrayState.Bind(index); return m_vertexArrayState.GetVertexArrayObject(index);
} }
SharedPtr<VertexArrayObject> GLContext::CreateVertexArrayObject(Uint index) { void GLContext::BindVertexArray(Uint index) {
return m_vertexArrayState.CreateVertexArrayObject(index); m_vertexArrayState.Bind(index);
} }
void GLContext::MarkVertexArrayForDeletion(Uint index) { const SharedPtr<VertexArrayObject>& GLContext::CreateVertexArrayObject(Uint index) {
m_vertexArrayState.MarkVertexArrayForDeletion(index); return m_vertexArrayState.CreateVertexArrayObject(index);
} }
Bool GLContext::ValidateVertexArrayName(Uint index) const { void GLContext::MarkVertexArrayForDeletion(Uint index) {
return m_vertexArrayState.ValidateName(index); m_vertexArrayState.MarkVertexArrayForDeletion(index);
} }
Bool GLContext::ValidateVertexArrayObject(Uint index) const { Bool GLContext::ValidateVertexArrayName(Uint index) const {
return m_vertexArrayState.ValidateVertexArrayObject(index); return m_vertexArrayState.ValidateName(index);
} }
SharedPtr<VertexArrayObject> GLContext::GetBoundVertexArray() { Bool GLContext::ValidateVertexArrayObject(Uint index) const {
return m_vertexArrayState.GetBoundVertexArray(); return m_vertexArrayState.ValidateVertexArrayObject(index);
} }
// Texture const SharedPtr<VertexArrayObject>& GLContext::GetBoundVertexArray() {
Vector<Uint> GLContext::GenTextureNames(Uint number) { return m_vertexArrayState.GetBoundVertexArray();
return m_textureState.GenerateNames(number); }
}
SharedPtr<ITextureObject> GLContext::GetTextureObject(Uint index) { // Texture
return m_textureState.GetTextureObject(index); void GLContext::GenTextureNames(Uint number, Vector<Uint>& textures) {
} m_textureState.GenerateNames(number, textures);
}
SharedPtr<ITextureObject> GLContext::CreateTextureObject(Uint index, TextureTarget target) { const SharedPtr<ITextureObject>& GLContext::GetTextureObject(Uint index) {
return m_textureState.CreateTextureObject(index, target); return m_textureState.GetTextureObject(index);
} }
void GLContext::MarkTextureObjectForDeletion(Uint index) { const SharedPtr<ITextureObject>& GLContext::CreateTextureObject(Uint index, TextureTarget target) {
m_textureState.MarkTextureObjectForDeletion(index); return m_textureState.CreateTextureObject(index, target);
} }
TextureUnit& GLContext::GetTextureUnitObject(Int unit) { void GLContext::MarkTextureObjectForDeletion(Uint index) {
return m_textureState.GetUnitObject(unit); m_textureState.MarkTextureObjectForDeletion(index);
} }
Bool GLContext::ValidateTextureName(Uint index) const { TextureUnit& GLContext::GetTextureUnitObject(Int unit) {
return m_textureState.ValidateName(index); return m_textureState.GetUnitObject(unit);
} }
Bool GLContext::ValidateTextureObject(Uint index) const { Bool GLContext::ValidateTextureName(Uint index) const {
return m_textureState.ValidateTextureObject(index); return m_textureState.ValidateName(index);
} }
Int GLContext::GetActiveTextureUnit() const { Bool GLContext::ValidateTextureObject(Uint index) const {
return m_textureState.GetActiveTextureUnit(); return m_textureState.ValidateTextureObject(index);
} }
void GLContext::SetActiveTextureUnit(Int unit) { Int GLContext::GetActiveTextureUnit() const {
m_textureState.SetActiveTextureUnit(unit); return m_textureState.GetActiveTextureUnit();
} }
// Program void GLContext::SetActiveTextureUnit(Int unit) {
Uint GLContext::CreateProgram() { m_textureState.SetActiveTextureUnit(unit);
return m_programState.CreateProgram(); }
}
Uint GLContext::CreateShader(const ShaderStage stage) { // Program
return m_programState.CreateShader(stage); Uint GLContext::CreateProgram() {
} return m_programState.CreateProgram();
}
void GLContext::MarkProgramForDeletion(const Uint index) { Uint GLContext::CreateShader(const ShaderStage stage) {
return m_programState.MarkProgramObjectForDeletion(index); return m_programState.CreateShader(stage);
} }
void GLContext::MarkShaderForDeletion(const Uint index) { void GLContext::MarkProgramForDeletion(const Uint index) {
return m_programState.MarkShaderObjectForDeletion(index); return m_programState.MarkProgramObjectForDeletion(index);
} }
Bool GLContext::ValidateProgramName(const Uint index) const { void GLContext::MarkShaderForDeletion(const Uint index) {
return m_programState.ValidateProgramObject(index); return m_programState.MarkShaderObjectForDeletion(index);
} }
Bool GLContext::ValidateShaderName(const Uint index) const { Bool GLContext::ValidateProgramName(const Uint index) const {
return m_programState.ValidateShaderObject(index); return m_programState.ValidateProgramObject(index);
} }
SharedPtr<ProgramObject> GLContext::GetProgramObject(const Uint index) { Bool GLContext::ValidateShaderName(const Uint index) const {
return m_programState.GetProgramObject(index); return m_programState.ValidateShaderObject(index);
} }
SharedPtr<ShaderObject> GLContext::GetShaderObject(const Uint index) { const SharedPtr<ProgramObject>& GLContext::GetProgramObject(const Uint index) {
return m_programState.GetShaderObject(index); return m_programState.GetProgramObject(index);
} }
void GLContext::UseProgram(Uint program) { const SharedPtr<ShaderObject>& GLContext::GetShaderObject(const Uint index) {
return m_programState.UseProgram(program); return m_programState.GetShaderObject(index);
} }
SharedPtr<ProgramObject> GLContext::GetCurrentProgram() { void GLContext::UseProgram(Uint program) {
return m_programState.GetCurrentProgram(); return m_programState.UseProgram(program);
} }
// RenderState const SharedPtr<ProgramObject>& GLContext::GetCurrentProgram() {
Uint GLContext::GetRenderStateParametersVersion() const { return m_programState.GetCurrentProgram();
return m_renderState.GetVersion(); }
}
const RenderStateParameters& GLContext::GetRenderStateParameters() const { // RenderState
return m_renderState.GetAllParameters(); Uint GLContext::GetRenderStateParametersVersion() const {
} return m_renderState.GetVersion();
}
void GLContext::SetViewport(IntVec4 viewport) { const RenderStateParameters& GLContext::GetRenderStateParameters() const {
m_renderState.SetViewport(viewport); return m_renderState.GetAllParameters();
} }
const IntVec4& GLContext::GetViewport() const { void GLContext::SetViewport(IntVec4 viewport) {
return m_renderState.GetViewport(); m_renderState.SetViewport(viewport);
} }
void GLContext::SetCapability(CapabilityInput cap, Bool enabled) { const IntVec4& GLContext::GetViewport() const {
m_renderState.SetCapability(cap, enabled); return m_renderState.GetViewport();
} }
Bool GLContext::IsCapabilityEnabled(CapabilityInput cap) const { void GLContext::SetCapability(CapabilityInput cap, Bool enabled) {
return m_renderState.IsCapabilityEnabled(cap); m_renderState.SetCapability(cap, enabled);
} }
void GLContext::SetCapabilityIndexed(CapabilityInput cap, Uint index, Bool enabled) { Bool GLContext::IsCapabilityEnabled(CapabilityInput cap) const {
m_renderState.SetCapabilityIndexed(cap, index, enabled); return m_renderState.IsCapabilityEnabled(cap);
} }
Bool GLContext::IsCapabilityEnabledIndexed(CapabilityInput cap, Uint index) const { void GLContext::SetCapabilityIndexed(CapabilityInput cap, Uint index, Bool enabled) {
return m_renderState.IsCapabilityEnabledIndexed(cap, index); m_renderState.SetCapabilityIndexed(cap, index, enabled);
} }
void GLContext::SetBlendFunc(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, Bool GLContext::IsCapabilityEnabledIndexed(CapabilityInput cap, Uint index) const {
BlendFactor dstAlpha) { return m_renderState.IsCapabilityEnabledIndexed(cap, index);
m_renderState.SetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha); }
}
void GLContext::GetBlendFunc(BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha, void GLContext::SetBlendFunc(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha,
BlendFactor& dstAlpha) const { BlendFactor dstAlpha) {
m_renderState.GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha); m_renderState.SetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha);
} }
void GLContext::SetBlendFuncIndexed(Uint index, BlendFactor srcRGB, BlendFactor dstRGB, void GLContext::GetBlendFunc(BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha,
BlendFactor srcAlpha, BlendFactor dstAlpha) { BlendFactor& dstAlpha) const {
m_renderState.SetBlendFuncIndexed(index, srcRGB, dstRGB, srcAlpha, dstAlpha); m_renderState.GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha);
} }
void GLContext::GetBlendFuncIndexed(Uint index, BlendFactor& srcRGB, BlendFactor& dstRGB, void GLContext::SetBlendFuncIndexed(Uint index, BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha,
BlendFactor& srcAlpha, BlendFactor& dstAlpha) const { BlendFactor dstAlpha) {
m_renderState.GetBlendFuncIndexed(index, srcRGB, dstRGB, srcAlpha, dstAlpha); m_renderState.SetBlendFuncIndexed(index, srcRGB, dstRGB, srcAlpha, dstAlpha);
} }
void GLContext::SetDepthFunc(DepthTestFunc func) { void GLContext::GetBlendFuncIndexed(Uint index, BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha,
m_renderState.SetDepthFunc(func); BlendFactor& dstAlpha) const {
} m_renderState.GetBlendFuncIndexed(index, srcRGB, dstRGB, srcAlpha, dstAlpha);
}
DepthTestFunc GLContext::GetDepthFunc() const { void GLContext::SetDepthFunc(DepthTestFunc func) {
return m_renderState.GetDepthFunc(); m_renderState.SetDepthFunc(func);
} }
void GLContext::SetDepthMask(Bool flag) { DepthTestFunc GLContext::GetDepthFunc() const {
m_renderState.SetDepthMask(flag); return m_renderState.GetDepthFunc();
} }
Bool GLContext::GetDepthMask() const { void GLContext::SetDepthMask(Bool flag) {
return m_renderState.GetDepthMask(); m_renderState.SetDepthMask(flag);
} }
void GLContext::SetColorMask(BoolVec4 mask) { Bool GLContext::GetDepthMask() const {
m_renderState.SetColorMask(mask); return m_renderState.GetDepthMask();
} }
const BoolVec4 GLContext::GetColorMask() const { void GLContext::SetColorMask(BoolVec4 mask) {
return m_renderState.GetColorMask(); m_renderState.SetColorMask(mask);
} }
void GLContext::SetClearColor(FloatVec4 color) { BoolVec4 GLContext::GetColorMask() const {
m_renderState.SetClearColor(color); return m_renderState.GetColorMask();
} }
const FloatVec4& GLContext::GetClearColor() const { void GLContext::SetClearColor(FloatVec4 color) {
return m_renderState.GetClearColor(); m_renderState.SetClearColor(color);
} }
void GLContext::SetClearDepth(Float depth) { const FloatVec4& GLContext::GetClearColor() const {
m_renderState.SetClearDepth(depth); return m_renderState.GetClearColor();
} }
Float GLContext::GetClearDepth() const { void GLContext::SetClearDepth(Float depth) {
return m_renderState.GetClearDepth(); m_renderState.SetClearDepth(depth);
} }
void GLContext::SetClearStencil(Int stencil) { Float GLContext::GetClearDepth() const {
m_renderState.SetClearStencil(stencil); return m_renderState.GetClearDepth();
} }
Int GLContext::GetClearStencil() const { void GLContext::SetClearStencil(Int stencil) {
m_renderState.SetClearStencil(stencil);
}
Uint32 GLContext::GetClearStencil() const {
return m_renderState.GetClearStencil(); return m_renderState.GetClearStencil();
} }
void GLContext::SetPixelStoreParam(PixelStoreParam param, Int value) { void GLContext::SetPixelStoreParam(PixelStoreParam param, Int value) {
m_renderState.SetPixelStoreParam(param, value); m_renderState.SetPixelStoreParam(param, value);
} }
Int GLContext::GetPixelStoreParam(PixelStoreParam param) const { Int GLContext::GetPixelStoreParam(PixelStoreParam param) const {
return m_renderState.GetPixelStoreParam(param); return m_renderState.GetPixelStoreParam(param);
} }
PixelStoreParameters GLContext::GetPixelStoreParameters(Bool isUnpack) const { PixelStoreParameters GLContext::GetPixelStoreParameters(Bool isUnpack) const {
return m_renderState.GetPixelStoreParameters(isUnpack); return m_renderState.GetPixelStoreParameters(isUnpack);
} }
void GLContext::SetCullFaceMode(CullFaceMode mode) { void GLContext::SetCullFaceMode(CullFaceMode mode) {
m_renderState.SetCullFaceMode(mode); m_renderState.SetCullFaceMode(mode);
} }
CullFaceMode GLContext::GetCullFaceMode() const { CullFaceMode GLContext::GetCullFaceMode() const {
return m_renderState.GetCullFaceMode(); return m_renderState.GetCullFaceMode();
} }
void GLContext::SetScissorBox(IntVec4 box) { void GLContext::SetScissorBox(IntVec4 box) {
m_renderState.SetScissorBox(box); m_renderState.SetScissorBox(box);
} }
const IntVec4& GLContext::GetScissorBox() const { const IntVec4& GLContext::GetScissorBox() const {
return m_renderState.GetScissorBox(); return m_renderState.GetScissorBox();
} }
// Framebuffer // Framebuffer
Vector<Uint> GLContext::GenFramebufferNames(Uint number) { void GLContext::GenFramebufferNames(Uint number, Vector<Uint>& framebuffers) {
return m_framebufferState.GenerateNames(number); m_framebufferState.GenerateNames(number, framebuffers);
} }
SharedPtr<FramebufferObject> GLContext::GetFramebufferObject(Uint index) { const SharedPtr<FramebufferObject>& GLContext::GetFramebufferObject(Uint index) {
return m_framebufferState.GetFramebufferObject(index); return m_framebufferState.GetFramebufferObject(index);
} }
BindingSlot<FramebufferObject>& GLContext::GetFramebufferBindingSlot(FramebufferTarget target) { BindingSlot<FramebufferObject>& GLContext::GetFramebufferBindingSlot(FramebufferTarget target) {
return m_framebufferState.GetBindingSlot(target); return m_framebufferState.GetBindingSlot(target);
} }
SharedPtr<FramebufferObject> GLContext::CreateFramebufferObject(Uint index) { const SharedPtr<FramebufferObject>& GLContext::CreateFramebufferObject(Uint index) {
return m_framebufferState.CreateFramebufferObject(index); return m_framebufferState.CreateFramebufferObject(index);
} }
void GLContext::MarkFramebufferObjectForDeletion(Uint index) { void GLContext::MarkFramebufferObjectForDeletion(Uint index) {
m_framebufferState.MarkFramebufferObjectForDeletion(index); m_framebufferState.MarkFramebufferObjectForDeletion(index);
} }
Bool GLContext::ValidateFramebufferName(Uint index) const { Bool GLContext::ValidateFramebufferName(Uint index) const {
return m_framebufferState.ValidateName(index); return m_framebufferState.ValidateName(index);
} }
Bool GLContext::ValidateFramebufferObject(Uint index) const { Bool GLContext::ValidateFramebufferObject(Uint index) const {
return m_framebufferState.ValidateFramebufferObject(index); return m_framebufferState.ValidateFramebufferObject(index);
} }
// Sampler // Sampler
Vector<Uint> GLContext::GenSamplerNames(Uint number) { void GLContext::GenSamplerNames(Uint number, Vector<Uint>& samplers) {
return m_samplerState.GenerateNames(number); m_samplerState.GenerateNames(number, samplers);
} }
SharedPtr<SamplerObject> GLContext::GetSamplerObject(Uint index) { const SharedPtr<SamplerObject>& GLContext::GetSamplerObject(Uint index) {
return m_samplerState.GetSamplerObject(index); return m_samplerState.GetSamplerObject(index);
} }
SharedPtr<SamplerObject> GLContext::CreateSamplerObject(Uint index) { const SharedPtr<SamplerObject>& GLContext::CreateSamplerObject(Uint index) {
return m_samplerState.CreateSamplerObject(index); return m_samplerState.CreateSamplerObject(index);
} }
void GLContext::MarkSamplerObjectForDeletion(Uint index) { void GLContext::MarkSamplerObjectForDeletion(Uint index) {
// Unbind the sampler from all texture units // Unbind the sampler from all texture units
if (ValidateSamplerObject(index)) { if (ValidateSamplerObject(index)) {
auto sampler = m_samplerState.GetSamplerObject(index); auto sampler = m_samplerState.GetSamplerObject(index);
for (Int unit = 0; unit < TextureState::MAX_TEXTURE_IMAGE_UNITS; ++unit) { for (Int unit = 0; unit < TextureState::MAX_TEXTURE_IMAGE_UNITS; ++unit) {
auto& textureUnit = m_textureState.GetUnitObject(unit); auto& textureUnit = m_textureState.GetUnitObject(unit);
if (textureUnit.GetSamplerObject() == sampler) { if (textureUnit.GetSamplerObject() == sampler) {
textureUnit.SetSamplerObject(nullptr); textureUnit.SetSamplerObject(nullptr);
}
} }
} }
m_samplerState.MarkSamplerObjectForDeletion(index);
} }
m_samplerState.MarkSamplerObjectForDeletion(index);
}
Bool GLContext::ValidateSamplerName(Uint index) const { Bool GLContext::ValidateSamplerName(Uint index) const {
return m_samplerState.ValidateName(index); return m_samplerState.ValidateName(index);
} }
Bool GLContext::ValidateSamplerObject(Uint index) const { Bool GLContext::ValidateSamplerObject(Uint index) const {
return m_samplerState.ValidateSamplerObject(index); return m_samplerState.ValidateSamplerObject(index);
} }
// Renderbuffer // Renderbuffer
Vector<Uint> GLContext::GenRenderbufferNames(Uint number) { void GLContext::GenRenderbufferNames(Uint number, Vector<Uint>& renderbuffers) {
return m_renderbufferState.GenerateNames(number); m_renderbufferState.GenerateNames(number, renderbuffers);
} }
SharedPtr<RenderbufferObject> GLContext::GetRenderbufferObject(Uint index) { const SharedPtr<RenderbufferObject>& GLContext::GetRenderbufferObject(Uint index) {
return m_renderbufferState.GetRenderbufferObject(index); return m_renderbufferState.GetRenderbufferObject(index);
} }
BindingSlot<RenderbufferObject>& GLContext::GetRenderbufferBindingSlot(RenderbufferTarget target) { BindingSlot<RenderbufferObject>& GLContext::GetRenderbufferBindingSlot(RenderbufferTarget target) {
return m_renderbufferState.GetBindingSlot(target); return m_renderbufferState.GetBindingSlot(target);
} }
SharedPtr<RenderbufferObject> GLContext::CreateRenderbufferObject(Uint index) { const SharedPtr<RenderbufferObject>& GLContext::CreateRenderbufferObject(Uint index) {
return m_renderbufferState.CreateRenderbufferObject(index); return m_renderbufferState.CreateRenderbufferObject(index);
} }
void GLContext::MarkRenderbufferObjectForDeletion(Uint index) { void GLContext::MarkRenderbufferObjectForDeletion(Uint index) {
m_renderbufferState.MarkRenderbufferObjectForDeletion(index); m_renderbufferState.MarkRenderbufferObjectForDeletion(index);
} }
Bool GLContext::ValidateRenderbufferName(Uint index) const { Bool GLContext::ValidateRenderbufferName(Uint index) const {
return m_renderbufferState.ValidateName(index); return m_renderbufferState.ValidateName(index);
} }
} // namespace GLState
GLState::GLContext* pGLContext; Bool GLContext::ValidateRenderbufferObject(Uint index) const {
} // namespace MG_State return m_renderbufferState.ValidateRenderbufferObject(index);
} // namespace MobileGL }
} // namespace GLState
UniquePtr<GLState::GLContext> pGLContext;
} // namespace MobileGL::MG_State
+30 -30
View File
@@ -30,42 +30,42 @@ namespace MobileGL {
GLContext() = default; GLContext() = default;
// Error // Error
void RecordError(ErrorCode code, SharedPtr<ErrorInfo> info = nullptr); void RecordError(ErrorCode code, UniquePtr<ErrorInfo> info);
Bool HasGLError() const; Bool HasGLError() const;
Optional<const Error> PeekGLError() const; Optional<const Error*> PeekNonGLError() const;
Optional<Error> PopGLError(); Optional<UniquePtr<Error>> PopNonGLError();
Bool HasNonGLError() const; Bool HasNonGLError() const;
Optional<const Error> PeekNonGLError() const; Optional<const Error*> PeekGLError() const;
Optional<Error> PopNonGLError(); Optional<UniquePtr<Error>> PopGLError();
void ClearErrors(); void ClearErrors();
// Buffer // Buffer
Vector<Uint> GenBufferNames(Uint number); void GenBufferNames(Uint number, Vector<Uint>& buffers);
SharedPtr<BufferObject> GetBufferObject(Uint index); const SharedPtr<BufferObject>& GetBufferObject(Uint index);
BindingSlot<BufferObject>& GetBufferBindingSlot(BufferTarget target); BindingSlot<BufferObject>& GetBufferBindingSlot(BufferTarget target);
BindingSlotRange1D<BufferObject>& GetBufferBindingPoint(BufferTarget target, Uint index); BindingSlotRange1D<BufferObject>& GetBufferBindingPoint(BufferTarget target, Uint index);
constexpr SizeT GetBufferBindingPointCount(BufferTarget target) const { constexpr SizeT GetBufferBindingPointCount(BufferTarget target) const {
return m_bufferState.GetBindingPointCount(target); return m_bufferState.GetBindingPointCount(target);
} }
SharedPtr<BufferObject> CreateBufferObject(Uint index); const SharedPtr<BufferObject>& CreateBufferObject(Uint index);
void MarkBufferObjectForDeletion(Uint index); void MarkBufferObjectForDeletion(Uint index);
Bool ValidateBufferName(Uint index) const; Bool ValidateBufferName(Uint index) const;
Bool ValidateBufferObject(Uint index) const; Bool ValidateBufferObject(Uint index) const;
// VertexArray // VertexArray
Vector<Uint> GenVertexArrayNames(Uint number); void GenVertexArrayNames(Uint number, Vector<Uint>& vertexArrays);
SharedPtr<VertexArrayObject> GetVertexArrayObject(Uint index); const SharedPtr<VertexArrayObject>& GetVertexArrayObject(Uint index);
void BindVertexArray(Uint index); void BindVertexArray(Uint index);
SharedPtr<VertexArrayObject> CreateVertexArrayObject(Uint index); const SharedPtr<VertexArrayObject>& CreateVertexArrayObject(Uint index);
void MarkVertexArrayForDeletion(Uint index); void MarkVertexArrayForDeletion(Uint index);
Bool ValidateVertexArrayName(Uint index) const; Bool ValidateVertexArrayName(Uint index) const;
Bool ValidateVertexArrayObject(Uint index) const; Bool ValidateVertexArrayObject(Uint index) const;
SharedPtr<VertexArrayObject> GetBoundVertexArray(); const SharedPtr<VertexArrayObject>& GetBoundVertexArray();
// Texture // Texture
Vector<Uint> GenTextureNames(Uint number); void GenTextureNames(Uint number, Vector<Uint>& textures);
SharedPtr<ITextureObject> GetTextureObject(Uint index); const SharedPtr<ITextureObject>& GetTextureObject(Uint index);
SharedPtr<ITextureObject> CreateTextureObject(Uint index, TextureTarget target); const SharedPtr<ITextureObject>& CreateTextureObject(Uint index, TextureTarget target);
void MarkTextureObjectForDeletion(Uint index); void MarkTextureObjectForDeletion(Uint index);
TextureUnit& GetTextureUnitObject(Int unit); TextureUnit& GetTextureUnitObject(Int unit);
Bool ValidateTextureName(Uint index) const; Bool ValidateTextureName(Uint index) const;
@@ -80,10 +80,10 @@ namespace MobileGL {
void MarkShaderForDeletion(Uint index); void MarkShaderForDeletion(Uint index);
Bool ValidateProgramName(Uint index) const; Bool ValidateProgramName(Uint index) const;
Bool ValidateShaderName(Uint index) const; Bool ValidateShaderName(Uint index) const;
SharedPtr<ProgramObject> GetProgramObject(Uint index); const SharedPtr<ProgramObject>& GetProgramObject(Uint index);
SharedPtr<ShaderObject> GetShaderObject(Uint index); const SharedPtr<ShaderObject>& GetShaderObject(Uint index);
void UseProgram(Uint program); void UseProgram(Uint program);
SharedPtr<ProgramObject> GetCurrentProgram(); const SharedPtr<ProgramObject>& GetCurrentProgram();
// RenderState // RenderState
Uint GetRenderStateParametersVersion() const; Uint GetRenderStateParametersVersion() const;
@@ -106,13 +106,13 @@ namespace MobileGL {
void SetDepthMask(Bool flag); void SetDepthMask(Bool flag);
Bool GetDepthMask() const; Bool GetDepthMask() const;
void SetColorMask(BoolVec4 mask); void SetColorMask(BoolVec4 mask);
const BoolVec4 GetColorMask() const; BoolVec4 GetColorMask() const;
void SetClearColor(FloatVec4 color); void SetClearColor(FloatVec4 color);
const FloatVec4& GetClearColor() const; const FloatVec4& GetClearColor() const;
void SetClearDepth(Float depth); void SetClearDepth(Float depth);
Float GetClearDepth() const; Float GetClearDepth() const;
void SetClearStencil(Int stencil); void SetClearStencil(Int stencil);
Int GetClearStencil() const; Uint32 GetClearStencil() const;
void SetPixelStoreParam(PixelStoreParam param, Int value); void SetPixelStoreParam(PixelStoreParam param, Int value);
Int GetPixelStoreParam(PixelStoreParam param) const; Int GetPixelStoreParam(PixelStoreParam param) const;
PixelStoreParameters GetPixelStoreParameters(Bool isUnpack) const; PixelStoreParameters GetPixelStoreParameters(Bool isUnpack) const;
@@ -122,27 +122,27 @@ namespace MobileGL {
const IntVec4& GetScissorBox() const; // x, y, width, height const IntVec4& GetScissorBox() const; // x, y, width, height
// Framebuffer // Framebuffer
Vector<Uint> GenFramebufferNames(Uint number); void GenFramebufferNames(Uint number, Vector<Uint>& framebuffers);
SharedPtr<FramebufferObject> GetFramebufferObject(Uint index); const SharedPtr<FramebufferObject>& GetFramebufferObject(Uint index);
BindingSlot<FramebufferObject>& GetFramebufferBindingSlot(FramebufferTarget target); BindingSlot<FramebufferObject>& GetFramebufferBindingSlot(FramebufferTarget target);
SharedPtr<FramebufferObject> CreateFramebufferObject(Uint index); const SharedPtr<FramebufferObject>& CreateFramebufferObject(Uint index);
void MarkFramebufferObjectForDeletion(Uint index); void MarkFramebufferObjectForDeletion(Uint index);
Bool ValidateFramebufferName(Uint index) const; Bool ValidateFramebufferName(Uint index) const;
Bool ValidateFramebufferObject(Uint index) const; Bool ValidateFramebufferObject(Uint index) const;
// Sampler // Sampler
Vector<Uint> GenSamplerNames(Uint number); void GenSamplerNames(Uint number, Vector<Uint>& samplers);
SharedPtr<SamplerObject> GetSamplerObject(Uint index); const SharedPtr<SamplerObject>& GetSamplerObject(Uint index);
SharedPtr<SamplerObject> CreateSamplerObject(Uint index); const SharedPtr<SamplerObject>& CreateSamplerObject(Uint index);
void MarkSamplerObjectForDeletion(Uint index); void MarkSamplerObjectForDeletion(Uint index);
Bool ValidateSamplerName(Uint index) const; Bool ValidateSamplerName(Uint index) const;
Bool ValidateSamplerObject(Uint index) const; Bool ValidateSamplerObject(Uint index) const;
// Renderbuffer // Renderbuffer
Vector<Uint> GenRenderbufferNames(Uint number); void GenRenderbufferNames(Uint number, Vector<Uint>& renderbuffers);
SharedPtr<RenderbufferObject> GetRenderbufferObject(Uint index); const SharedPtr<RenderbufferObject>& GetRenderbufferObject(Uint index);
BindingSlot<RenderbufferObject>& GetRenderbufferBindingSlot(RenderbufferTarget target); BindingSlot<RenderbufferObject>& GetRenderbufferBindingSlot(RenderbufferTarget target);
SharedPtr<RenderbufferObject> CreateRenderbufferObject(Uint index); const SharedPtr<RenderbufferObject>& CreateRenderbufferObject(Uint index);
void MarkRenderbufferObjectForDeletion(Uint index); void MarkRenderbufferObjectForDeletion(Uint index);
Bool ValidateRenderbufferName(Uint index) const; Bool ValidateRenderbufferName(Uint index) const;
Bool ValidateRenderbufferObject(Uint index) const; Bool ValidateRenderbufferObject(Uint index) const;
@@ -161,6 +161,6 @@ namespace MobileGL {
}; };
} // namespace GLState } // namespace GLState
extern GLState::GLContext* pGLContext; extern UniquePtr<GLState::GLContext> pGLContext;
} // namespace MG_State } // namespace MG_State
} // namespace MobileGL } // namespace MobileGL
+43 -47
View File
@@ -10,57 +10,53 @@
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h> #include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
#include <MG_Util/Converters/MGToGL/ErrorCodeConverter.h> #include <MG_Util/Converters/MGToGL/ErrorCodeConverter.h>
namespace MobileGL { namespace MobileGL::MG_State::GLState {
namespace MG_State { void ErrorState::RecordError(ErrorCode code, UniquePtr<ErrorInfo> info) {
namespace GLState { if (code == ErrorCode::NoError) {
void ErrorState::RecordError(ErrorCode code, SharedPtr<ErrorInfo> info) { MGLOG_E("Recording Non-OpenGL error:\n%s", info->toString().c_str());
if (code == ErrorCode::NoError) { m_nonGLErrors.push_back(MakeUnique<Error>(code, Move(info)));
MGLOG_E("Recording Non-OpenGL error:\n%s", info->ToString().c_str()); } else {
m_nonGLErrors.push_back(Error{code, info}); MGLOG_E("Recording OpenGL error (%s):\n%s",
} else { MG_Util::ConvertGLEnumToString(MG_Util::ConvertErrorCodeToGLEnum(code)).c_str(),
MGLOG_E("Recording OpenGL error (%s):\n%s", info->toString().c_str());
MG_Util::ConvertGLEnumToString(MG_Util::ConvertErrorCodeToGLEnum(code)).c_str(), m_errors.push_back(MakeUnique<Error>(code, Move(info)));
info->ToString().c_str()); }
m_errors.push_back(Error{code, info}); }
}
}
Bool ErrorState::HasNonGLError() const { Bool ErrorState::HasNonGLError() const {
return !m_nonGLErrors.empty(); return !m_nonGLErrors.empty();
} }
Optional<const Error> ErrorState::PeekNonGLError() const { Optional<const Error*> ErrorState::PeekNonGLError() const {
if (m_nonGLErrors.empty()) return Optional<const Error>{}; if (m_nonGLErrors.empty()) return Nullopt;
return Optional<const Error>{m_nonGLErrors.front()}; return Optional<const Error*>{m_nonGLErrors.front().get()};
} }
Optional<Error> ErrorState::PopNonGLError() { Optional<UniquePtr<Error>> ErrorState::PopNonGLError() {
if (m_nonGLErrors.empty()) return Optional<const Error>{}; if (m_nonGLErrors.empty()) return Nullopt;
auto error = Move(m_nonGLErrors.front()); auto error = Move(m_nonGLErrors.front());
m_nonGLErrors.erase(m_nonGLErrors.begin()); m_nonGLErrors.erase(m_nonGLErrors.begin());
return Optional<const Error>{error}; return Move(error);
} }
Bool ErrorState::HasGLError() const { Bool ErrorState::HasGLError() const {
return !m_errors.empty(); return !m_errors.empty();
} }
Optional<const Error> ErrorState::PeekGLError() const { Optional<const Error*> ErrorState::PeekGLError() const {
if (m_errors.empty()) return Optional<const Error>{}; if (m_errors.empty()) return Optional<const Error*>{};
return Optional<const Error>{m_errors.front()}; return Optional<const Error*>{m_errors.front().get()};
} }
Optional<Error> ErrorState::PopGLError() { Optional<UniquePtr<Error>> ErrorState::PopGLError() {
if (m_errors.empty()) return Optional<const Error>{}; if (m_errors.empty()) return Nullopt;
auto error = Move(m_errors.front()); auto error = Move(m_errors.front());
m_errors.erase(m_errors.begin()); m_errors.erase(m_errors.begin());
return Optional<const Error>{error}; return Move(error);
} }
void ErrorState::Clear() { void ErrorState::Clear() {
m_errors.clear(); m_errors.clear();
m_nonGLErrors.clear(); m_nonGLErrors.clear();
} }
} // namespace GLState } // namespace MobileGL::MG_State::GLState
} // namespace MG_State
} // namespace MobileGL
+18 -20
View File
@@ -14,26 +14,24 @@
namespace MobileGL { namespace MobileGL {
struct Error { struct Error {
ErrorCode code; ErrorCode code;
SharedPtr<ErrorInfo> info; UniquePtr<ErrorInfo> info;
}; };
namespace MG_State { namespace MG_State::GLState {
namespace GLState { class ErrorState {
class ErrorState { public:
public: void RecordError(ErrorCode code, UniquePtr<ErrorInfo> info);
void RecordError(ErrorCode code, SharedPtr<ErrorInfo> info = nullptr); Bool HasNonGLError() const;
Bool HasNonGLError() const; Optional<const Error*> PeekNonGLError() const;
Optional<const Error> PeekNonGLError() const; Optional<UniquePtr<Error>> PopNonGLError();
Optional<Error> PopNonGLError(); Bool HasGLError() const;
Bool HasGLError() const; Optional<const Error*> PeekGLError() const;
Optional<const Error> PeekGLError() const; Optional<UniquePtr<Error>> PopGLError();
Optional<Error> PopGLError(); void Clear();
void Clear();
private: private:
Vector<Error> m_errors; Vector<UniquePtr<Error>> m_errors;
Vector<Error> m_nonGLErrors; Vector<UniquePtr<Error>> m_nonGLErrors;
}; };
} // namespace GLState } // namespace MG_State::GLState
} // namespace MG_State } // namespace MobileGL
} // namespace MobileGL
@@ -13,7 +13,7 @@ namespace MobileGL {
class ErrorInfo { class ErrorInfo {
public: public:
virtual ~ErrorInfo() = default; virtual ~ErrorInfo() = default;
virtual String ToString() const = 0; virtual String toString() const = 0;
}; };
class GenericErrorInfo : public ErrorInfo { class GenericErrorInfo : public ErrorInfo {
@@ -24,7 +24,7 @@ namespace MobileGL {
explicit GenericErrorInfo(String m_prefix, String m_prefix_2, String message) explicit GenericErrorInfo(String m_prefix, String m_prefix_2, String message)
: m_message(Move(message)), m_prefix(Move(m_prefix)), m_prefix_2(Move(m_prefix_2)) {} : m_message(Move(message)), m_prefix(Move(m_prefix)), m_prefix_2(Move(m_prefix_2)) {}
String ToString() const override { String toString() const override {
StringStream ss; StringStream ss;
if (m_prefix.has_value()) { if (m_prefix.has_value()) {
ss << "[" << m_prefix.value() << "] "; ss << "[" << m_prefix.value() << "] ";
@@ -9,157 +9,153 @@
#include "FramebufferObject.h" #include "FramebufferObject.h"
#include "MG_Util/Types.h" #include "MG_Util/Types.h"
namespace MobileGL { namespace MobileGL::MG_State::GLState {
namespace MG_State { // FramebufferAttachmentObject
namespace GLState { FramebufferAttachmentObject::FramebufferAttachmentObject(
// FramebufferAttachmentObject const SharedPtr<MG_State::GLState::ITextureObject>& texture, Int level)
FramebufferAttachmentObject::FramebufferAttachmentObject( : m_texture(texture), m_textureLevel(level) {}
SharedPtr<MG_State::GLState::ITextureObject> texture, Int level) FramebufferAttachmentObject::FramebufferAttachmentObject(const SharedPtr<RenderbufferObject>& renderbuffer)
: m_texture(texture), m_textureLevel(level) {} : m_renderbuffer(renderbuffer) {}
FramebufferAttachmentObject::FramebufferAttachmentObject(SharedPtr<RenderbufferObject> renderbuffer) FramebufferAttachmentObject::FramebufferAttachmentObject(Bool IsValid)
: m_renderbuffer(renderbuffer) {} : m_texture(nullptr), m_renderbuffer(nullptr) {
FramebufferAttachmentObject::FramebufferAttachmentObject(Bool IsValid) m_isValid = IsValid;
: m_texture(nullptr), m_renderbuffer(nullptr) { }
m_isValid = IsValid;
}
Bool FramebufferAttachmentObject::IsTexture() const { Bool FramebufferAttachmentObject::IsTexture() const {
return m_texture != nullptr; return m_texture != nullptr;
} }
Bool FramebufferAttachmentObject::IsRenderbuffer() const { Bool FramebufferAttachmentObject::IsRenderbuffer() const {
return m_renderbuffer != nullptr; return m_renderbuffer != nullptr;
} }
Bool FramebufferAttachmentObject::IsEmpty() const { Bool FramebufferAttachmentObject::IsEmpty() const {
return m_texture == nullptr && m_renderbuffer == nullptr; return m_texture == nullptr && m_renderbuffer == nullptr;
} }
SharedPtr<MG_State::GLState::ITextureObject> FramebufferAttachmentObject::GetTexture() const { const SharedPtr<MG_State::GLState::ITextureObject>& FramebufferAttachmentObject::GetTexture() const {
return m_texture; return m_texture;
} }
SharedPtr<RenderbufferObject> FramebufferAttachmentObject::GetRenderbuffer() const { const SharedPtr<RenderbufferObject>& FramebufferAttachmentObject::GetRenderbuffer() const {
return m_renderbuffer; return m_renderbuffer;
} }
Int FramebufferAttachmentObject::GetTextureLevel() const { Int FramebufferAttachmentObject::GetTextureLevel() const {
return m_textureLevel; return m_textureLevel;
} }
Bool FramebufferAttachmentObject::IsComplete() const { Bool FramebufferAttachmentObject::IsComplete() const {
if (IsTexture()) { if (IsTexture()) {
Bool complete = m_texture->IsComplete(); Bool complete = m_texture->IsComplete();
return complete; return complete;
} else if (IsRenderbuffer()) { } else if (IsRenderbuffer()) {
Bool complete = m_renderbuffer->IsAllocated(); Bool complete = m_renderbuffer->IsAllocated();
return complete; return complete;
} }
return false;
}
IntVec3 FramebufferAttachmentObject::GetSize() const {
if (IsTexture()) {
// TODO: get correct upload target
MOBILEGL_ASSERT(nullptr != static_cast<MG_State::GLState::TextureObjectMipmap*>(m_texture.get()),
"Texture object here should always be an object with mipmap");
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(m_texture.get());
return textureMipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture2D, m_textureLevel);
} else if (IsRenderbuffer()) {
return {m_renderbuffer->GetWidth(), m_renderbuffer->GetHeight(), 1};
}
return {0, 0, 0};
}
Bool FramebufferAttachmentObject::IsValid() const {
return m_isValid;
}
// FramebufferObject
FramebufferObject::FramebufferObject(Uint externalIndex)
: m_externalIndex(externalIndex), m_attachmentVersions{}, m_drawBuffers{} {
m_attachmentObjects.fill(FramebufferAttachmentObject(false));
m_drawBuffers.fill(FramebufferAttachmentType::None);
m_drawBuffers[0] = FramebufferAttachmentType::Color0;
m_attachmentVersions.fill(0);
}
void FramebufferObject::AttachTexture(FramebufferAttachmentType type, const SharedPtr<ITextureObject>& texture,
int level) {
m_attachmentObjects[static_cast<SizeT>(type)] = FramebufferAttachmentObject(texture, level);
BumpAttachmentVersion(type);
}
void FramebufferObject::AttachRenderbuffer(FramebufferAttachmentType type,
const SharedPtr<RenderbufferObject>& renderbuffer) {
m_attachmentObjects[static_cast<SizeT>(type)] = FramebufferAttachmentObject(renderbuffer);
BumpAttachmentVersion(type);
}
void FramebufferObject::Detach(FramebufferAttachmentType type) {
m_attachmentObjects[static_cast<SizeT>(type)] = FramebufferAttachmentObject(false);
BumpAttachmentVersion(type);
}
const FramebufferAttachmentObject& FramebufferObject::GetAttachment(FramebufferAttachmentType type) const {
return m_attachmentObjects[static_cast<SizeT>(type)];
}
const FramebufferObject::FramebufferAttachmentObjectArray& FramebufferObject::GetAllAttachmentObjects() const {
return m_attachmentObjects;
}
Bool FramebufferObject::CheckCompleteness() const {
if (m_attachmentObjects.empty()) {
return false;
}
Int width = -1, height = -1;
Int validAttachmentCount = 0;
for (const auto& attachmentObject : m_attachmentObjects) {
if (!attachmentObject.IsValid()) continue;
++validAttachmentCount;
const auto& attachment = attachmentObject;
auto attachmentSize = attachment.GetSize();
Int w = attachmentSize.x();
Int h = attachmentSize.y();
if (width == -1) {
width = w;
height = h;
} else if (width != w || height != h) {
return false; return false;
} }
IntVec3 FramebufferAttachmentObject::GetSize() const { if (!attachment.IsComplete()) {
if (IsTexture()) { return false;
// TODO: get correct upload target
MOBILEGL_ASSERT(nullptr != dynamic_cast<MG_State::GLState::TextureObjectMipmap*>(m_texture.get()),
"Texture object here should always be an object with mipmap");
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(m_texture.get());
return textureMipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture2D, m_textureLevel);
} else if (IsRenderbuffer()) {
return IntVec3(m_renderbuffer->GetWidth(), m_renderbuffer->GetHeight(), 1);
}
return {0, 0, 0};
} }
}
Bool FramebufferAttachmentObject::IsValid() const { if (validAttachmentCount == 0) return false;
return m_isValid; return true;
} }
// FramebufferObject void FramebufferObject::SetDrawBuffer(Uint index, FramebufferAttachmentType buffer) {
FramebufferObject::FramebufferObject(Uint externalIndex) : m_externalIndex(externalIndex) { if (m_drawBuffers[index] == buffer) return;
m_attachmentObjects.fill(FramebufferAttachmentObject(false)); m_drawBuffers[index] = buffer;
m_drawBuffers.fill(FramebufferAttachmentType::None); BumpAttachmentVersion(buffer);
m_drawBuffers[0] = FramebufferAttachmentType::Color0; }
m_attachmentVersions.fill(0);
}
void FramebufferObject::AttachTexture(FramebufferAttachmentType type, SharedPtr<ITextureObject> texture, const FramebufferObject::FramebufferAttachmentArray& FramebufferObject::GetDrawBuffers() const {
int level) { return m_drawBuffers;
m_attachmentObjects[static_cast<SizeT>(type)] = FramebufferAttachmentObject(std::move(texture), level); }
BumpAttachmentVersion(type);
}
void FramebufferObject::AttachRenderbuffer(FramebufferAttachmentType type, Uint FramebufferObject::GetExternalIndex() const {
std::shared_ptr<RenderbufferObject> renderbuffer) { return m_externalIndex;
m_attachmentObjects[static_cast<SizeT>(type)] = FramebufferAttachmentObject(renderbuffer); }
BumpAttachmentVersion(type);
}
void FramebufferObject::Detach(FramebufferAttachmentType type) { void FramebufferObject::BumpAttachmentVersion(FramebufferAttachmentType type) {
m_attachmentObjects[static_cast<SizeT>(type)] = FramebufferAttachmentObject(false); ++m_attachmentVersions[static_cast<SizeT>(type)];
BumpAttachmentVersion(type); ++m_objectVersion;
} }
} // namespace MobileGL::MG_State::GLState
const FramebufferAttachmentObject& FramebufferObject::GetAttachment(FramebufferAttachmentType type) const {
return m_attachmentObjects[static_cast<SizeT>(type)];
}
const FramebufferObject::FramebufferAttachmentObjectArray& FramebufferObject::GetAllAttachmentObjects()
const {
return m_attachmentObjects;
}
Bool FramebufferObject::CheckCompleteness() const {
if (m_attachmentObjects.empty()) {
return false;
}
Int width = -1, height = -1;
Int validAttachmentCount = 0;
for (SizeT i = 0; i < m_attachmentObjects.size(); ++i) {
if (!m_attachmentObjects[i].IsValid()) continue;
++validAttachmentCount;
const auto& attachment = m_attachmentObjects[i];
auto attachmentSize = attachment.GetSize();
Int w = attachmentSize.x();
Int h = attachmentSize.y();
if (width == -1) {
width = w;
height = h;
} else if (width != w || height != h) {
return false;
}
if (!attachment.IsComplete()) {
return false;
}
}
if (validAttachmentCount == 0) return false;
return true;
}
void FramebufferObject::SetDrawBuffer(Uint index, FramebufferAttachmentType buffer) {
if (m_drawBuffers[index] == buffer) return;
m_drawBuffers[index] = buffer;
BumpAttachmentVersion(buffer);
}
const FramebufferObject::FramebufferAttachmentArray& FramebufferObject::GetDrawBuffers() const {
return m_drawBuffers;
}
Uint FramebufferObject::GetExternalIndex() const {
return m_externalIndex;
}
void FramebufferObject::BumpAttachmentVersion(FramebufferAttachmentType type) {
++m_attachmentVersions[static_cast<SizeT>(type)];
++m_objectVersion;
}
} // namespace GLState
} // namespace MG_State
} // namespace MobileGL
@@ -7,6 +7,7 @@
// End of Source File Header // End of Source File Header
#pragma once #pragma once
#include "MG_Util/Types.h"
#include <Includes.h> #include <Includes.h>
#include <MG_State/GLState/TextureState/TextureObject.h> #include <MG_State/GLState/TextureState/TextureObject.h>
#include <MG_State/GLState/RenderbufferState/RenderbufferObject.h> #include <MG_State/GLState/RenderbufferState/RenderbufferObject.h>
@@ -62,86 +63,84 @@ namespace MobileGL {
Color29, Color29,
Color30, Color30,
Color31, Color31,
ColorMax = Color31,
FramebufferAttachmentTypeCount, FramebufferAttachmentTypeCount,
Unknown = -1 Unknown = -1
}; };
namespace MG_State { namespace MG_State::GLState {
namespace GLState { class FramebufferAttachmentObject {
class FramebufferAttachmentObject { public:
public: explicit FramebufferAttachmentObject(const SharedPtr<MG_State::GLState::ITextureObject>& texture,
explicit FramebufferAttachmentObject(SharedPtr<MG_State::GLState::ITextureObject> texture, Int level = 0);
Int level = 0); explicit FramebufferAttachmentObject(const SharedPtr<RenderbufferObject>& renderbuffer);
explicit FramebufferAttachmentObject(SharedPtr<RenderbufferObject> renderbuffer); explicit FramebufferAttachmentObject(Bool IsValid = true);
explicit FramebufferAttachmentObject(Bool IsValid = true);
Bool IsTexture() const; Bool IsTexture() const;
Bool IsRenderbuffer() const; Bool IsRenderbuffer() const;
Bool IsEmpty() const; Bool IsEmpty() const;
SharedPtr<MG_State::GLState::ITextureObject> GetTexture() const; const SharedPtr<MG_State::GLState::ITextureObject>& GetTexture() const;
SharedPtr<RenderbufferObject> GetRenderbuffer() const; const SharedPtr<RenderbufferObject>& GetRenderbuffer() const;
Int GetTextureLevel() const; Int GetTextureLevel() const;
Bool IsComplete() const; Bool IsComplete() const;
IntVec3 GetSize() const; IntVec3 GetSize() const;
Bool IsValid() const; Bool IsValid() const;
private: private:
SharedPtr<MG_State::GLState::ITextureObject> m_texture = nullptr; SharedPtr<MG_State::GLState::ITextureObject> m_texture = nullptr;
SharedPtr<RenderbufferObject> m_renderbuffer = nullptr; SharedPtr<RenderbufferObject> m_renderbuffer = nullptr;
Int m_textureLevel = 0; Int m_textureLevel = 0;
Bool m_isValid = true; Bool m_isValid = true;
}; };
class FramebufferObject { class FramebufferObject {
public: public:
static constexpr Uint MAX_DRAW_BUFFERS = 8; static constexpr Uint MAX_DRAW_BUFFERS = 8;
using TargetEnum = FramebufferTarget; using TargetEnum = FramebufferTarget;
using FramebufferAttachmentObjectArray = using FramebufferAttachmentObjectArray =
Array<FramebufferAttachmentObject, Array<FramebufferAttachmentObject,
static_cast<SizeT>(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>; static_cast<SizeT>(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>;
using FramebufferAttachmentArray = Array<FramebufferAttachmentType, MAX_DRAW_BUFFERS>; using FramebufferAttachmentArray = Array<FramebufferAttachmentType, MAX_DRAW_BUFFERS>;
using FramebufferAttachmentVersionArray = using FramebufferAttachmentVersionArray =
Array<Uint16, static_cast<SizeT>(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>; Array<Uint16, static_cast<SizeT>(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>;
FramebufferObject(Uint externalIndex); FramebufferObject(Uint externalIndex);
void AttachTexture(FramebufferAttachmentType type, SharedPtr<ITextureObject> texture, int level = 0); void AttachTexture(FramebufferAttachmentType type, const SharedPtr<ITextureObject>& texture, int level = 0);
void AttachRenderbuffer(FramebufferAttachmentType type, void AttachRenderbuffer(FramebufferAttachmentType type, const SharedPtr<RenderbufferObject>& renderbuffer);
std::shared_ptr<RenderbufferObject> renderbuffer); void Detach(FramebufferAttachmentType type);
void Detach(FramebufferAttachmentType type); const FramebufferAttachmentObject& GetAttachment(FramebufferAttachmentType type) const;
const FramebufferAttachmentObject& GetAttachment(FramebufferAttachmentType type) const; const FramebufferAttachmentObjectArray& GetAllAttachmentObjects() const;
const FramebufferAttachmentObjectArray& GetAllAttachmentObjects() const; Bool CheckCompleteness() const;
Bool CheckCompleteness() const; // aka. `buffer` as in glDrawBuffers/glReadBuffers
// aka. `buffer` as in glDrawBuffers/glReadBuffers void SetDrawBuffer(Uint index, FramebufferAttachmentType buffer);
void SetDrawBuffer(Uint index, FramebufferAttachmentType buffer); const FramebufferAttachmentArray& GetDrawBuffers() const;
const FramebufferAttachmentArray& GetDrawBuffers() const; void SetReadBuffer(FramebufferAttachmentType buf) { m_readBuffer = buf; }
void SetReadBuffer(FramebufferAttachmentType buf) { m_readBuffer = buf; } FramebufferAttachmentType GetReadBuffer() const { return m_readBuffer; }
FramebufferAttachmentType GetReadBuffer() const { return m_readBuffer; }
const FramebufferAttachmentVersionArray GetAllFramebufferAttachmentVersions() const { FramebufferAttachmentVersionArray GetAllFramebufferAttachmentVersions() const {
return m_attachmentVersions; return m_attachmentVersions;
} }
Uint16 GetObjectVersion() const { return m_objectVersion; } Uint16 GetObjectVersion() const { return m_objectVersion; }
Uint GetExternalIndex() const; Uint GetExternalIndex() const;
private: private:
void BumpAttachmentVersion(FramebufferAttachmentType type); void BumpAttachmentVersion(FramebufferAttachmentType type);
const Uint m_externalIndex = 0; const Uint m_externalIndex = 0;
FramebufferAttachmentObjectArray m_attachmentObjects; FramebufferAttachmentObjectArray m_attachmentObjects;
FramebufferAttachmentVersionArray m_attachmentVersions; FramebufferAttachmentVersionArray m_attachmentVersions;
FramebufferAttachmentArray m_drawBuffers; // Probably no versioning needed for this, just check equality FramebufferAttachmentArray m_drawBuffers; // Probably no versioning needed for this, just check equality
FramebufferAttachmentType m_readBuffer = FramebufferAttachmentType::Color0; // ditto FramebufferAttachmentType m_readBuffer = FramebufferAttachmentType::Color0; // ditto
// This version will bump when draw/read buffer changes (by `glDrawBuffer(s)`/`glReadBuffer`) // This version will bump when draw/read buffer changes (by `glDrawBuffer(s)`/`glReadBuffer`)
Uint16 m_objectVersion = 0; Uint16 m_objectVersion = 0;
}; };
} // namespace GLState } // namespace MG_State::GLState
} // namespace MG_State
} // namespace MobileGL } // namespace MobileGL
@@ -9,74 +9,73 @@
#include "FramebufferState.h" #include "FramebufferState.h"
#include "MG_State/GLState/FramebufferState/FramebufferObject.h" #include "MG_State/GLState/FramebufferState/FramebufferObject.h"
namespace MobileGL { namespace MobileGL::MG_State::GLState {
namespace MG_State { FramebufferState::FramebufferState() {
namespace GLState { for (SizeT i = 0; i < m_bindingSlots.size(); ++i) {
FramebufferState::FramebufferState() { m_bindingSlots[i] = BindingSlot<FramebufferObject>(static_cast<FramebufferTarget>(i));
for (SizeT i = 0; i < m_bindingSlots.size(); ++i) { }
m_bindingSlots[i] = BindingSlot<FramebufferObject>(static_cast<FramebufferTarget>(i)); }
}
}
SharedPtr<FramebufferObject> FramebufferState::GetFramebufferObject(Uint index) { const SharedPtr<FramebufferObject>& FramebufferState::GetFramebufferObject(Uint index) {
auto it = m_framebufferObjects.find(index); auto it = m_framebufferObjects.find(index);
if (it != m_framebufferObjects.end()) { if (it != m_framebufferObjects.end()) {
return it->second; return it->second;
} }
return nullptr; static SharedPtr<FramebufferObject> nullFramebufferObject = nullptr;
} return nullFramebufferObject;
}
Vector<Uint> FramebufferState::GenerateNames(Uint number) { void FramebufferState::GenerateNames(Uint number, Vector<Uint>& buffers) {
Vector<Uint> buffers(number); buffers.resize(number);
m_indexGenerator.Generate(number, buffers.data()); m_indexGenerator.Generate(number, buffers.data());
return buffers; }
}
SharedPtr<FramebufferObject> FramebufferState::CreateFramebufferObject(Uint index) { const SharedPtr<FramebufferObject>& FramebufferState::CreateFramebufferObject(Uint index) {
if (index == 0) { if (index == 0) {
if (!m_indexGenerator.IsValid(0)) { if (!m_indexGenerator.IsValid(0)) {
m_indexGenerator.Insert(0); m_indexGenerator.Insert(0);
} else { } else {
return nullptr; static SharedPtr<FramebufferObject> nullFramebufferObject = nullptr;
return nullFramebufferObject;
}
}
auto& framebufferObject = m_framebufferObjects[index];
if (!framebufferObject) {
framebufferObject = MakeShared<FramebufferObject>(index);
}
return framebufferObject;
}
BindingSlot<FramebufferObject>& FramebufferState::GetBindingSlot(FramebufferTarget target) {
for (auto& bindingSlot : m_bindingSlots) {
if (bindingSlot.GetTarget() == target) {
return bindingSlot;
}
}
MOBILEGL_ASSERT(false, "Invalid FramebufferTarget enum value: %d", static_cast<int>(target));
return m_bindingSlots[0];
}
void FramebufferState::MarkFramebufferObjectForDeletion(Uint index) {
if (m_indexGenerator.IsValid(index)) {
auto it = m_framebufferObjects.find(index);
if (it != m_framebufferObjects.end()) {
for (auto& bindingSlot : m_bindingSlots) {
if (bindingSlot.GetBoundObject() == it->second) {
bindingSlot.Bind(nullptr);
} }
} }
auto bufferObject = MakeShared<FramebufferObject>(index); m_framebufferObjects.erase(it);
m_framebufferObjects[index] = bufferObject;
return bufferObject;
} }
m_indexGenerator.Delete(index);
}
}
BindingSlot<FramebufferObject>& FramebufferState::GetBindingSlot(FramebufferTarget target) { Bool FramebufferState::ValidateName(Uint index) const {
for (SizeT i = 0; i < m_bindingSlots.size(); ++i) { return m_indexGenerator.IsValid(index);
if (m_bindingSlots[i].GetTarget() == target) { }
return m_bindingSlots[i];
}
}
MOBILEGL_ASSERT(false, "Invalid FramebufferTarget enum value: %d", static_cast<int>(target));
return m_bindingSlots[0];
}
void FramebufferState::MarkFramebufferObjectForDeletion(Uint index) { Bool FramebufferState::ValidateFramebufferObject(Uint index) const {
if (m_indexGenerator.IsValid(index)) { return m_framebufferObjects.find(index) != m_framebufferObjects.end();
auto it = m_framebufferObjects.find(index); }
if (it != m_framebufferObjects.end()) { } // namespace MobileGL::MG_State::GLState
for (SizeT i = 0; i < m_bindingSlots.size(); ++i) {
if (m_bindingSlots[i].GetBoundObject() == it->second) {
m_bindingSlots[i].Bind(nullptr);
}
}
m_framebufferObjects.erase(it);
}
m_indexGenerator.Delete(index);
}
}
Bool FramebufferState::ValidateName(Uint index) const {
return m_indexGenerator.IsValid(index);
}
Bool FramebufferState::ValidateFramebufferObject(Uint index) const {
return m_framebufferObjects.find(index) != m_framebufferObjects.end();
}
} // namespace GLState
} // namespace MG_State
} // namespace MobileGL
@@ -11,28 +11,24 @@
#include <MG_Util/Miscellany/IndexGenerator.h> #include <MG_Util/Miscellany/IndexGenerator.h>
#include "FramebufferObject.h" #include "FramebufferObject.h"
namespace MobileGL { namespace MobileGL::MG_State::GLState {
namespace MG_State { class FramebufferState {
namespace GLState { public:
class FramebufferState { FramebufferState();
public:
FramebufferState();
// FBO 0 should be created by MG_Backend when initializing the context // FBO 0 should be created by MG_Backend when initializing the context
SharedPtr<FramebufferObject> GetFramebufferObject(Uint index); const SharedPtr<FramebufferObject>& GetFramebufferObject(Uint index);
Vector<Uint> GenerateNames(Uint number); void GenerateNames(Uint number, Vector<Uint>& framebuffers);
SharedPtr<FramebufferObject> CreateFramebufferObject(Uint index); const SharedPtr<FramebufferObject>& CreateFramebufferObject(Uint index);
BindingSlot<FramebufferObject>& GetBindingSlot(FramebufferTarget target); BindingSlot<FramebufferObject>& GetBindingSlot(FramebufferTarget target);
void MarkFramebufferObjectForDeletion(Uint index); void MarkFramebufferObjectForDeletion(Uint index);
Bool ValidateName(Uint index) const; Bool ValidateName(Uint index) const;
Bool ValidateFramebufferObject(Uint index) const; Bool ValidateFramebufferObject(Uint index) const;
private: private:
UnorderedMap<Uint, SharedPtr<FramebufferObject>> m_framebufferObjects; UnorderedMap<Uint, SharedPtr<FramebufferObject>> m_framebufferObjects;
IndexGenerator<Uint> m_indexGenerator; IndexGenerator<Uint> m_indexGenerator;
Array<BindingSlot<FramebufferObject>, static_cast<SizeT>(FramebufferTarget::FramebufferTargetCount)> Array<BindingSlot<FramebufferObject>, static_cast<SizeT>(FramebufferTarget::FramebufferTargetCount)>
m_bindingSlots; m_bindingSlots;
}; };
} // namespace GLState } // namespace MobileGL::MG_State::GLState
} // namespace MG_State
} // namespace MobileGL
File diff suppressed because it is too large Load Diff
@@ -9,180 +9,173 @@
#pragma once #pragma once
#include <Includes.h> #include <Includes.h>
#include "ShaderObject.h" #include "ShaderObject.h"
#include "MG_Util/Metrics/BufferMetrics.h"
#include "MG_Util/ShaderTranspiler/SpvcSession.h"
namespace MobileGL { #include <MG_Util/Metrics/BufferMetrics.h>
namespace MG_State { #include <MG_Util/ShaderTranspiler/SpvcSession.h>
namespace GLState {
class ProgramObject {
public:
ProgramObject(Uint externalIndex) : m_externalIndex(externalIndex) {}
bool ShaderIsAttached(SharedPtr<ShaderObject> shader);
bool AttachShader(SharedPtr<ShaderObject> shader);
SizeT DetachShader(SharedPtr<ShaderObject> shader);
SizeT RemoveShader(SharedPtr<ShaderObject> shader);
void Link(Bool addDefaultFSIfMissingForRenderingPipelineProgram = false);
void MarkAsDeleted();
void SetExplicitVertexInLocation(Uint index, const char* name); namespace MobileGL::MG_State::GLState {
void SetExplicitFragmentOutLocation(Uint index, const char* name); class ProgramObject {
Int GetFragmentDataLocation(const char* name); public:
ProgramObject(Uint externalIndex) : m_externalIndex(externalIndex) {}
bool ShaderIsAttached(const SharedPtr<ShaderObject>& shader);
bool AttachShader(const SharedPtr<ShaderObject>& shader);
SizeT DetachShader(const SharedPtr<ShaderObject>& shader);
SizeT RemoveShader(const SharedPtr<ShaderObject>& shader);
void Link(Bool addDefaultFSIfMissingForRenderingPipelineProgram = false);
void MarkAsDeleted();
Vector<SharedPtr<ShaderObject>>& GetAttachedShaders(); void SetExplicitVertexInLocation(Uint index, const char* name);
const Vector<SharedPtr<ShaderObject>>& GetAttachedShaders() const; void SetExplicitFragmentOutLocation(Uint index, const char* name);
const String& GetInfoLog() const { return m_infoLog; } Int GetFragmentDataLocation(const char* name);
Int GetUniformMaxLength() const { return m_uniformNameMaxLength; }
Uint GetUniformCount() { return m_activeUniformCount; }
Uint GetMaxUniformLocation() const { return m_maxUniformLocation; }
Int GetUniformLocation(const String& name) const {
const auto it = m_uniformLocations.find(name);
if (it == m_uniformLocations.end()) return -1;
return (Int)it->second;
}
GLenum GetUniformType(Uint location) const { Vector<SharedPtr<ShaderObject>>& GetAttachedShaders();
auto& uniform = m_program->getUniform(m_uniformIndexInTProgram[location]); const Vector<SharedPtr<ShaderObject>>& GetAttachedShaders() const;
return uniform.glDefineType; const String& GetInfoLog() const { return m_infoLog; }
} Int GetUniformMaxLength() const { return m_uniformNameMaxLength; }
Uint GetUniformCount() const { return m_activeUniformCount; }
Uint GetMaxUniformLocation() const { return m_maxUniformLocation; }
Int GetUniformLocation(const String& name) const {
const auto it = m_uniformLocations.find(name);
if (it == m_uniformLocations.end()) return -1;
return (Int)it->second;
}
const glslang::TType* GetUniformTType(Uint location) const { GLenum GetUniformType(Uint location) const {
auto& uniform = m_program->getUniform(m_uniformIndexInTProgram[location]); auto& uniform = m_program->getUniform(m_uniformIndexInTProgram[location]);
return uniform.getType(); return uniform.glDefineType;
} }
Bool IsUniformOpaqueAtLocation(Uint location) const { return GetUniformTType(location)->isOpaque(); } const glslang::TType* GetUniformTType(Uint location) const {
auto& uniform = m_program->getUniform(m_uniformIndexInTProgram[location]);
return uniform.getType();
}
const String& GetUniformName(Uint location) const { Bool IsUniformOpaqueAtLocation(Uint location) const { return GetUniformTType(location)->isOpaque(); }
auto& uniform = m_program->getUniform(m_uniformIndexInTProgram[location]);
return uniform.name;
}
Uint GetUniformOffset(Uint location) const { return m_uniformOffsets[location]; }
Uint GetUniformSizesInBytes(Uint location) const {
return MG_Util::GetGLTypeSize(GetUniformType(location));
}
Int GetAttributeLocation(const String& name) { const String& GetUniformName(Uint location) const {
const auto it = std::find(m_attribs.begin(), m_attribs.end(), name); auto& uniform = m_program->getUniform(m_uniformIndexInTProgram[location]);
return (it == m_attribs.end()) ? -1 : std::distance(m_attribs.begin(), it); return uniform.name;
} }
GLenum GetAttribType(Uint index) const { return m_attribTypes[index]; } Uint GetUniformOffset(Uint location) const { return m_uniformOffsets[location]; }
const String& GetAttribName(Uint index) const { return m_attribs[index]; } Uint GetUniformSizesInBytes(Uint location) const { return MG_Util::GetGLTypeSize(GetUniformType(location)); }
void* MapUBO() { return m_uboScratch.data(); }
const void* GetUBOData() const { return m_uboScratch.data(); }
Uint GetUBOSize() const { return static_cast<Uint>(m_uboScratch.size()); }
void SetUniformSamplerOrImageUnitIndex(Uint location, Int unit) { Int GetAttributeLocation(const String& name) {
m_uniformSamplerOrImageUnitIndex[location] = unit; const auto it = std::find(m_attribs.begin(), m_attribs.end(), name);
} return (it == m_attribs.end()) ? -1 : (Int)std::distance(m_attribs.begin(), it);
}
GLenum GetAttribType(Uint index) const { return m_attribTypes[index]; }
const String& GetAttribName(Uint index) const { return m_attribs[index]; }
void* MapUBO() { return m_globalUboScratch.data(); }
const void* GetUBOData() const { return m_globalUboScratch.data(); }
Uint GetUBOSize() const { return static_cast<Uint>(m_globalUboScratch.size()); }
Int GetUniformSamplerOrImageUnitIndex(Uint location) const { void SetUniformSamplerOrImageUnitIndex(Uint location, Int unit) {
return m_uniformSamplerOrImageUnitIndex[location]; m_uniformSamplerOrImageUnitIndex[location] = unit;
} }
Bool GetDeleteStatus() const { return m_deleteStatus; } Int GetUniformSamplerOrImageUnitIndex(Uint location) const {
Bool GetLinkStatus() const { return m_linkStatus; } return m_uniformSamplerOrImageUnitIndex[location];
Bool GetValidateStatus() const { return m_validateStatus; } }
Int GetActiveAtomicCounterCount() const { return m_program->getNumAtomicCounters(); }
Int GetActiveAttributesCount() const { return m_program->getNumPipeInputs(); }
Int GetActiveUniformBlocksCount() const { return m_program->getNumUniformBlocks(); }
Int GetActiveAttributesMaxLength() const { return m_attribInNameMaxLength; }
Int GetActiveUniformBlocksMaxNameLength() const { return m_uniformBlockNameMaxLength; }
Uint GetUniformBlockIndex(const char* name) const {
auto it = m_uniformBlockIndexByName.find(name);
if (it != m_uniformBlockIndexByName.end()) return it->second;
return 0xFFFFFFFFu; // GL_INVALID_INDEX
}
Bool IsActiveUniformBlock(Uint index) const {
if (index >= GetActiveUniformBlocksCount()) return false;
return true;
}
Uint GetUBOSizeAt(Uint index) const {
if (!IsActiveUniformBlock(index)) return 0;
return m_program->getUniformBlock(index).size;
}
const String& GetUniformBlockName(Uint index) const { Bool GetDeleteStatus() const { return m_deleteStatus; }
auto& ubo = m_program->getUniformBlock(index); Bool GetLinkStatus() const { return m_linkStatus; }
return ubo.name; Bool GetValidateStatus() const { return m_validateStatus; }
} Int GetActiveAtomicCounterCount() const { return m_program->getNumAtomicCounters(); }
Int GetActiveAttributesCount() const { return m_program->getNumPipeInputs(); }
Int GetActiveUniformBlocksCount() const { return m_program->getNumUniformBlocks(); }
Int GetActiveAttributesMaxLength() const { return m_attribInNameMaxLength; }
Int GetActiveUniformBlocksMaxNameLength() const { return m_uniformBlockNameMaxLength; }
Uint GetUniformBlockIndex(const char* name) const {
auto it = m_uniformBlockIndexByName.find(name);
if (it != m_uniformBlockIndexByName.end()) return it->second;
return 0xFFFFFFFFu; // GL_INVALID_INDEX
}
Bool IsActiveUniformBlock(Uint index) const {
if (index >= GetActiveUniformBlocksCount()) return false;
return true;
}
Uint GetUBOSizeAt(Uint index) const {
if (!IsActiveUniformBlock(index)) return 0;
return m_program->getUniformBlock((Int)index).size;
}
// Set by glUniformBlockBinding const String& GetUniformBlockName(Uint index) const {
void SetUniformBlockBinding(Uint index, Uint binding) { m_uniformBlockBinding[index] = binding; } auto& ubo = m_program->getUniformBlock((Int)index);
return ubo.name;
}
Uint GetUniformBlockBinding(Uint index) const { return m_uniformBlockBinding[index]; } // Set by glUniformBlockBinding
void SetUniformBlockBinding(Uint index, Uint binding) { m_uniformBlockBinding[index] = (Int)binding; }
Vector<Vector<unsigned>>& GetGeneratedSpirv() { return m_generatedSpirv; } Uint GetUniformBlockBinding(Uint index) const { return m_uniformBlockBinding[index]; }
const Vector<Vector<unsigned>>& GetGeneratedSpirv() const { return m_generatedSpirv; }
Int GetShaderIndexByStage(ShaderStage stage) const { Vector<Vector<unsigned>>& GetGeneratedSpirv() { return m_generatedSpirv; }
auto it = std::find_if(m_shaders.begin(), m_shaders.end(), const Vector<Vector<unsigned>>& GetGeneratedSpirv() const { return m_generatedSpirv; }
[stage](const SharedPtr<ShaderObject>& shader) { return shader->GetShaderStage() == stage; });
return it == m_shaders.end() ? -1 : std::distance(m_shaders.begin(), it);
}
Uint GetExternalIndex() const { return m_externalIndex; } Int GetShaderIndexByStage(ShaderStage stage) const {
auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [stage](const SharedPtr<ShaderObject>& shader) {
return shader->GetShaderStage() == stage;
});
return it == m_shaders.end() ? -1 : (Int)std::distance(m_shaders.begin(), it);
}
// const UnorderedMap<String, Uint>& GetAttribLocationMap() const { return Uint GetExternalIndex() const { return m_externalIndex; }
// m_attribLocation; }
private: private:
void DoReflection(); void DoReflection();
void GenerateBinary(); void GenerateBinary();
void WaitUntilGenerationCompleted(); void WaitUntilGenerationCompleted() const;
void AddDefaultFragmentShaderIfMissing(); void AddDefaultFragmentShaderIfMissing();
const Uint m_externalIndex = 0; const Uint m_externalIndex = 0;
Vector<SharedPtr<ShaderObject>> m_shaders; Vector<SharedPtr<ShaderObject>> m_shaders;
Vector<SharedPtr<ShaderObject>> m_detachedShaders; // Store detached shaders and remove on next link Vector<SharedPtr<ShaderObject>> m_detachedShaders; // Store detached shaders and remove on next link
SharedPtr<glslang::TProgram> m_program; SharedPtr<glslang::TProgram> m_program;
Vector<Vector<unsigned>> m_generatedSpirv; Vector<Vector<unsigned>> m_generatedSpirv;
// Attributes (Vertex in) // Attributes (Vertex in)
UnorderedMap<String, Uint> m_explicitAttribLocations; UnorderedMap<String, Uint> m_explicitAttribLocations;
Vector<String> m_attribs; Vector<String> m_attribs;
Vector<GLenum> m_attribTypes; Vector<GLenum> m_attribTypes;
// For SpvcSession::SetVertexAttribLocation()
// UnorderedMap<String, Uint> m_attribLocation;
// FragData (Frag out) // FragData (Frag out)
UnorderedMap<String, Uint> m_explicitFragDataLocation; UnorderedMap<String, Uint> m_explicitFragDataLocation;
// Uniforms // Uniforms
UnorderedMap<String, Uint> m_uniformLocations; UnorderedMap<String, Uint> m_uniformLocations;
// Ordered by location, // Ordered by location,
// aka. m_uniformIndexInTProgram[loc] == "uniform index of TProgram at location `loc`" // aka. m_uniformIndexInTProgram[loc] == "uniform index of TProgram at location `loc`"
Vector<Int> m_uniformIndexInTProgram; Vector<Int> m_uniformIndexInTProgram;
// ditto. Will be set at glUniform1i // ditto. Will be set at glUniform1i
Vector<Int> m_uniformSamplerOrImageUnitIndex; Vector<Int> m_uniformSamplerOrImageUnitIndex;
// Ordered by uniform block index // Ordered by uniform block index
// index is DIFFERENT from binding!!! // index is DIFFERENT from binding!!!
// //
// Let's define UniformBlockIndex == the order at glslang getUniformBlock() // Let's define UniformBlockIndex == the order at glslang getUniformBlock()
// aka `i = glGetUniformBlockIndex(prog, "BlockName")` implies: // aka `i = glGetUniformBlockIndex(prog, "BlockName")` implies:
// `prog->getUniformBlock(i) == "BlockName"` // `prog->getUniformBlock(i) == "BlockName"`
UnorderedMap<String, Uint> m_uniformBlockIndexByName; // These stuff are present for GL semantics, not for backend inspection
Vector<Int> m_uniformBlockBinding; // These may change after-link (because GL spec decided to have `glUniformBlockBinding`)
UnorderedMap<String, Uint> m_uniformBlockIndexByName;
Vector<Int> m_uniformBlockBinding;
// Need to be reflected after linking of SPIR-V binary // Need to be reflected after linking of SPIR-V binary
Vector<Uint> m_uniformOffsets; Vector<Uint> m_uniformOffsets;
Vector<Uint> m_uniformSizesInBytes; Vector<Uint> m_uniformSizesInBytes;
Vector<Uint8> m_uboScratch; Vector<Uint8> m_globalUboScratch;
Uint m_activeUniformCount = 0; Uint m_activeUniformCount = 0;
Uint m_maxUniformLocation = 0; Uint m_maxUniformLocation = 0;
Int m_uniformNameMaxLength = 0; Int m_uniformNameMaxLength = 0;
Int m_attribInNameMaxLength = 0; Int m_attribInNameMaxLength = 0;
Int m_uniformBlockNameMaxLength = 0; Int m_uniformBlockNameMaxLength = 0;
String m_infoLog; String m_infoLog;
Bool m_deleteStatus = false; Bool m_deleteStatus = false;
Bool m_linkStatus = false; Bool m_linkStatus = false;
Bool m_validateStatus = true; Bool m_validateStatus = true;
}; };
} // namespace GLState } // namespace MobileGL::MG_State::GLState
} // namespace MG_State
} // namespace MobileGL
@@ -8,73 +8,71 @@
#include "ProgramState.h" #include "ProgramState.h"
namespace MobileGL { namespace MobileGL::MG_State::GLState {
namespace MG_State { Uint ProgramState::CreateProgram() {
namespace GLState { Uint programId = 0;
Uint ProgramState::CreateProgram() { m_programIndexGenerator.Generate(1, &programId);
Uint programId = 0; EnsureIndexAvail(programId, m_programObjects);
m_programIndexGenerator.Generate(1, &programId); auto programObject = MakeShared<ProgramObject>(programId);
EnsureIndexAvail(programId, m_programObjects); if (programObject == nullptr) return 0;
auto programObject = MakeShared<ProgramObject>(programId); m_programObjects[programId] = programObject;
if (programObject == nullptr) return 0; return programId;
m_programObjects[programId] = programObject; }
return programId;
}
SharedPtr<ProgramObject> ProgramState::GetProgramObject(const Uint id) { const SharedPtr<ProgramObject>& ProgramState::GetProgramObject(const Uint id) {
if (!CheckIndexAvail(id, m_programObjects)) return nullptr; // FIXME: add error reporting here static SharedPtr<ProgramObject> nullProgramObject = nullptr;
return m_programObjects[id]; if (!CheckIndexAvail(id, m_programObjects)) return nullProgramObject; // FIXME: add error reporting here
} return m_programObjects[id];
}
void ProgramState::MarkProgramObjectForDeletion(const Uint program) { void ProgramState::MarkProgramObjectForDeletion(const Uint program) {
if (!CheckIndexAvail(program, m_programObjects)) return; // FIXME: add error reporting here if (!CheckIndexAvail(program, m_programObjects)) return; // FIXME: add error reporting here
auto& programObject = m_programObjects[program]; auto& programObject = m_programObjects[program];
if (programObject != nullptr) { if (programObject != nullptr) {
programObject->MarkAsDeleted(); programObject->MarkAsDeleted();
programObject.reset(); programObject.reset();
m_programIndexGenerator.Delete(program); m_programIndexGenerator.Delete(program);
} }
} }
Bool ProgramState::ValidateProgramObject(const Uint program) const { Bool ProgramState::ValidateProgramObject(const Uint program) const {
return CheckIndexAvail(program, m_programObjects) && m_programObjects[program] != nullptr; return CheckIndexAvail(program, m_programObjects) && m_programObjects[program] != nullptr;
} }
void ProgramState::UseProgram(Uint program) { void ProgramState::UseProgram(Uint program) {
if (program == 0) m_currentProgram.reset(); if (program == 0) m_currentProgram.reset();
if (!CheckIndexAvail(program, m_programObjects)) return; if (!CheckIndexAvail(program, m_programObjects)) return;
m_currentProgram = m_programObjects[program]; m_currentProgram = m_programObjects[program];
} }
Uint ProgramState::CreateShader(ShaderStage stage) { Uint ProgramState::CreateShader(ShaderStage stage) {
Uint shaderId = 0; Uint shaderId = 0;
m_shaderIndexGenerator.Generate(1, &shaderId); m_shaderIndexGenerator.Generate(1, &shaderId);
EnsureIndexAvail(shaderId, m_shaderObjects); EnsureIndexAvail(shaderId, m_shaderObjects);
auto shaderObject = MakeShared<ShaderObject>(stage, shaderId); auto shaderObject = MakeShared<ShaderObject>(stage, shaderId);
if (shaderObject == nullptr) return 0; if (shaderObject == nullptr) return 0;
m_shaderObjects[shaderId] = shaderObject; m_shaderObjects[shaderId] = shaderObject;
return shaderId; return shaderId;
} }
SharedPtr<ShaderObject> ProgramState::GetShaderObject(const Uint shader) { const SharedPtr<ShaderObject>& ProgramState::GetShaderObject(const Uint shader) {
if (!CheckIndexAvail(shader, m_shaderObjects)) return nullptr; static SharedPtr<ShaderObject> nullShaderObject = nullptr;
return m_shaderObjects[shader]; if (!CheckIndexAvail(shader, m_shaderObjects)) return nullShaderObject;
} return m_shaderObjects[shader];
}
void ProgramState::MarkShaderObjectForDeletion(Uint shader) { void ProgramState::MarkShaderObjectForDeletion(Uint shader) {
if (!CheckIndexAvail(shader, m_shaderObjects)) return; if (!CheckIndexAvail(shader, m_shaderObjects)) return;
auto& shaderObject = m_shaderObjects[shader]; auto& shaderObject = m_shaderObjects[shader];
if (shaderObject != nullptr) { if (shaderObject != nullptr) {
m_shaderObjects[shader]->MarkAsDeleted(); m_shaderObjects[shader]->MarkAsDeleted();
m_shaderObjects[shader].reset(); m_shaderObjects[shader].reset();
m_shaderIndexGenerator.Delete(shader); m_shaderIndexGenerator.Delete(shader);
} }
} }
Bool ProgramState::ValidateShaderObject(Uint shader) const { Bool ProgramState::ValidateShaderObject(Uint shader) const {
return CheckIndexAvail(shader, m_shaderObjects) && m_shaderObjects[shader] != nullptr; return CheckIndexAvail(shader, m_shaderObjects) && m_shaderObjects[shader] != nullptr;
} }
} // namespace GLState } // namespace MobileGL::MG_State::GLState
} // namespace MG_State
} // namespace MobileGL

Some files were not shown because too many files have changed in this diff Show More