Compare commits

..
Author SHA1 Message Date
swung0x48 85bd0613ca [Fix] (MG_Backend/DirectGLES): support Voxy rendering
Implemented:

- Advertise Voxy-required DirectGLES extensions without raising the reported OpenGL version.

- Add DirectGLES multi draw indirect count emulation and preserve GL draw indirect baseInstance semantics on GLES.

- Add DirectGLES DSA framebuffer clear/blit paths used by Minecraft and Voxy presentation.

Fixed:

- Rewrite gl_BaseInstance in DirectGLES vertex shaders and provide a backend uniform for indirect draw emulation.

- Materialize framebuffer attachment textures during DirectGLES FBO sync so named framebuffer operations do not desync backend attachment state.

- Avoid redundant texture buffer rebinding and handle texture buffers without bound storage during backend sync.

Tests:

- Add MG_Test coverage for DirectGLES Voxy extension advertising, baseInstance shader rewriting, and DSA named framebuffer clear/blit backend wiring.
2026-06-09 17:20:23 +08:00
swung0x48 dd52f0381a [Fix] (MG_Backend/DirectVulkan): fix Voxy subgroup and indirect draw sync
- Implement Vulkan subgroup capability querying and expose KHR subgroup getter values.

- Fix DirectVulkan memory barriers so GL_COMMAND_BARRIER_BIT makes generated indirect draw commands visible.

- Keep Voxy on the DirectVulkan gpu_shader_int64 quad decode path while filtering unsupported optional int64 usage on backends that do not advertise it.

- Add MG_Test coverage for subgroup getters, Voxy subgroup/int64 shader probes, command barrier mapping, and indirect draw command layout.

- Check for whether driver supports shader subgroup operation, disable on demand, and provide env var `MOBILEGL_DISABLE_SUBGROUP` to explicitly disable subgroup features
2026-06-09 09:46:07 +08:00
swung0x48 cf165c0db5 [Fix] (MG_Backend/DirectVulkan): support Voxy rendering
Implemented:

- Advertise Voxy-required DirectVulkan extensions without raising the reported OpenGL version.

- Add DirectVulkan compute, indirect draw count, DSA, readback, and buffer state paths needed by Voxy.

Fixed:

- Enable Vulkan shaderInt64 and drawIndirectFirstInstance so Voxy baseInstance-driven LOD draws address the correct section data.

- Fix DirectVulkan synchronization, framebuffer, texture readback, and shader interface handling used by Voxy and Minecraft screenshots.

Tests:

- Add MG_Test coverage for DirectVulkan extension advertising, DSA buffer/texture/framebuffer/vertex-array behavior, persistent mapped readback, and shader/program paths.
2026-06-09 00:37:37 +08:00
swung0x48 ab9db43599 [Chore]: bump version to 26.06 2026-06-08 14:11:20 +08:00
swung0x48 d553e363a7 [Feat] (MG_Impl/GL_Buffer, MG_State/BufferState, MG_Backend): implement persistent mapping 2026-06-08 14:08:35 +08:00
swung0x48 be3c3eb9bb [Fix] (MG_Test/VertexArray): lossen too strict error test 2026-06-08 09:43:24 +08:00
swung0x48 357807666d [Fix] (MG_Backend/DirectVulkan, MG_Backend/DirectGLES): fix vulkan program
cache, get EGLSurfaceSize on viewport = 0
2026-06-08 05:41:38 +08:00
BZLZHH d1a5a4e39c [Fix] (MG_Util/BackendLoader): Add /usr/lib64 and /lib64 to library search paths for Fedora/RHEL compatibility. 2026-06-07 20:28:19 +08:00
swung0x48 a701c896f0 [Feat] (MG_Backend/DirectVulkan): wire up client-side buffer 2026-06-07 18:05:48 +08:00
swung0x48 b0de886f8e [Fix] (MG_Backend/DirectVulkan): make glmark2 work on Magma 2026-06-07 16:24:30 +08:00
swung0x48 ad1ca4ea92 [Feat] (MG_Impl/VertexArray): implement client-side buffer 2026-06-07 13:02:58 +08:00
swung0x48 fbaf5261e2 [Fix]: properly open X11 display for rendering 2026-06-07 12:26:35 +08:00
swung0x48 19ada4b8f9 [Fix]: fix glmark2 crash
- deal with legacy GLSL syntax (attribute/varying/gl_FragColor/texture2D/etc.)
- implement glGet GL_SHADER_SOURCE_LENGTH, and make sure returns
  original shader source
- expose proper extensions (GL_ARB_depth_texture)
- support env var MOBILEGL_LOG_FILE_PATH
- unit tests to test against those changes
2026-06-07 11:08:38 +08:00
swung0x48 ff41e59282 [Feat] (EGL): support Linux X11 + EGL 2026-06-07 08:21:20 +08:00
swung0x48 a15a13ca46 [Fix]: fix compilation on Linux 2026-06-06 23:18:27 +08:00
swung0x48 2937043e77 [Fix] (MG_Backend/DirectGLES): mark integer varyings flat 2026-06-06 18:58:27 +08:00
swung0x48 24efb0bb17 [Fix] (MG_Impl/Program): implement direct state uniform updates 2026-06-06 17:27:09 +08:00
swung0x48 dd2bd267c1 [Fix] (MG_State/RenderState): track front face mode 2026-06-06 08:46:43 +08:00
swung0x48 5cfe9c8998 [Feat] (MG_Backend/DirectVulkan): support compute shaders 2026-06-05 11:52:10 +08:00
swung0x48 3bd8a62aa8 [Feat] (MG_Backend/DirectGLES): support compute shaders 2026-06-05 10:41:23 +08:00
swung0x48 dcd37f3c38 [Fix] (MG_Util/ShaderTranspiler): preserve reflected uniform backing 2026-06-05 10:39:53 +08:00
swung0x48 a579fd2342 [Feat] (MG_State/ProgramState): attempting to do shader LTO (WIP) 2026-06-01 22:26:16 +08:00
swung0x48 f93ff00642 [Chore] (MG_State/ProgramState): add more remarks to GenerateBinary() 2026-06-01 21:35:28 +08:00
swung0x48 8a628631fb [Feat] (Backend/DirectVulkan): implement min/max lod
- This fixes water waves in Derivative d24.4.14
2026-06-01 05:38:28 +08:00
swung0x48 59733a2a26 [Fix] (MG_Backend/DirectVulkan): clear alpha as 1.0f when using RGB format on GL side. Transition to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL for clearing 2026-05-24 19:23:36 +08:00
swung0x48 c7d385c03d [Fix] (CI): scan rather than hardcode apk file name 2026-05-24 18:45:24 +08:00
swung0x48 27a8aa057c [Feat] (MG_Impl/RenderState): implement some blend related states 2026-05-24 17:38:44 +08:00
swung0x48 e63bfbabf0 [Chore] (MG_Backend/DirectVulkan): enable vulkan validation layer only on DEBUG log level 2026-05-24 11:11:12 +08:00
swung0x48 c107d9edcf [Fix] (MG_Backend/DirectVulkan): use RGBA format as RGB format
- some drivers (Adreno as I tested) lacks
  VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT on 24-bit RGB formats,
  use 32-bit ones as a fallback.
2026-05-24 10:07:28 +08:00
swung0x48 49e69c1c7d [CI] (android-plugin): build two (Espryt/Magma) variants 2026-05-24 08:57:02 +08:00
swung0x48 482b6d7bbf [CI] (android-plugin): specify MOBILEGL_LOG_ACTIVE_LEVEL=MOBILEGL_LOG_LEVEL_INFO define 2026-05-24 08:45:02 +08:00
swung0x48 dcfe91bdfc [Optimization] (MG_Backend/DirectVulkan): properly use vkCmdBlitImage() on rotations that
applicable, rather than shader-based blit
2026-05-24 00:14:05 +08:00
swung0x48 6072b5b703 [Fix] (MG_Backend/DirectVulkan): use shader-based default framebuffer blit to fix wrong orientation 2026-05-24 00:07:16 +08:00
swung0x48 9c50ac8a69 [CI]: use DirectVulkan as default backend 2026-05-23 23:01:48 +08:00
swung0x48 8b7acad257 [CI]: add android build key + signing process 2026-05-23 21:54:40 +08:00
swung0x48 cb8cb48a60 [CI]: build MobileGL android renderer plugin 2026-05-23 20:58:03 +08:00
swung0x48 8c00ae1d38 [Fix] (MG_Impl/GL_Texture): don't actually allocate large proxy texture to avoid large allocation makes some devices to crash 2026-05-12 16:33:30 +08:00
swung0x48 633b3d3b0a [Feat] (MG_State/RenderState, MG_Backend/DirectVulkan): make Distant
Horizon work
- Implement BlendEquation/CullFaceMode/PointSize/PolygonMode
- Implement GetFramebufferAttachmentParameter*
- Downgrade some color attachment resolve failure
- Downgrade some overly-strict shader stage linkage check (don't check
  on unused input var)
2026-05-12 12:38:19 +08:00
swung0x48 60199c0323 [Optimization] (MG_Backend/DirectVulkan): optimize CreatePipeline hot path with VkPipelineCache 2026-05-12 00:27:52 +08:00
swung0x48 9e0e03d4c0 [Optimaization] (MG_Backend/DirectVulkan): optimize perf
- Properly reuse descriptor sets rather than always allocating
- Add program lookup cache
2026-05-11 23:20:55 +08:00
swung0x48 4972ebf914 [Fix] (MG_Backend/DirectVulkan): fix 1.21.6+ intermittent crashing
- Try coerce vertex input format to please Vulkan driver
- Skip inactive UBO instead of hard fast-fail
2026-05-10 11:53:26 +08:00
swung0x48 4a36d63c1b [Fix] (MG_Backend/DirectVulkan): fix 26.2 texture bug.
- Fix texture object / buffer lifecycle issues
2026-05-10 10:03:03 +08:00
swung0x48 d7e768096e [Fix] (MG_Backend/DirectVulkan): fix Minecraft 26.2 startup crashes
- Support cube map face uploads with cube-compatible images and per-face array layers
- Add uniform texel buffer descriptor support for samplerBuffer bindings
- Cache transient vertex/index buffer uploads per frame to avoid VMA allocation failures
2026-05-10 08:50:55 +08:00
swung0x48 e13b5d0618 [Chore] (MG_Backend/DirectVulkan): make Photon fix a SPIR-V patch rather than source-level 2026-05-09 17:46:16 +08:00
swung0x48 c5bf0dc07d [Chore] (MG_Backend/DirectVulkan): exclude some validation logic from release build 2026-05-09 13:45:19 +08:00
swung0x48 9fbd83d602 Merge branch 'Feat/Backend-Direct-Vulkan' of https://github.com/MobileGL-Dev/MobileGL into Feat/Backend-Direct-Vulkan 2026-05-09 13:08:12 +08:00
swung0x48 39c5b28dfb [Fix] (MG_Backend/DirectVulkan): fixing Photon v1.1
- Flatten DailyWeatherVariation interface varyings
- Correct internal-format component counts
- Preserve GL draw-buffer slot semantics in render pass creation
- relax GL_NONE / vec4-to-RGB pipeline checks
2026-05-09 09:56:45 +08:00
BZLZHH be533994e5 Merge branch 'dev' into Feat/Backend-Direct-Vulkan 2026-05-09 08:32:33 +08:00
BZLZHH 368c089172 Merge branch 'Perf/Improvement1' into dev 2026-05-09 08:31:36 +08:00
BZLZHH 30c6f55c2d [Fix] (MG_Backend/DirectVulkan): Add namespace qualifier to Version type. 2026-05-09 08:28:46 +08:00
swung0x48 407d4344c4 [Feat] (MG_Backend/DirectVulkan): generate mipmap, add fallback texture, implement shader-based depth mipmap blit, enumerate more vk physical device features
- Supporting iterationT
2026-05-08 09:25:41 +08:00
swung0x48 76e3950028 [Feat] (MG_Backend/DirectVulkan): GenerateMipmap WIP 2026-05-07 15:14:25 +08:00
swung0x48 7337b00ca8 [Feat] (MG_Backend/DirectVulkan): implement 3D texture handling 2026-05-07 12:28:14 +08:00
swung0x48 22582dea3e [Chore] (MG_Backend/DirectVulkan): get rid of 1x1 texture fallback path for unbinded sampler 2026-05-07 09:30:10 +08:00
swung0x48 f600a07404 [Fix] (MG_Backend/DirectVulkan): use fallback texture in case some
shaderpack failes to properly bind texture
2026-05-06 17:57:59 +08:00
swung0x48 ff790e1ff1 [Feat] (MG_Backend/DirectVulkan): implement vulkan backend for glCopyTex(Sub)Image2D 2026-05-06 15:39:59 +08:00
swung0x48 a345369269 [Fix] (MG_Backend/DirectVulkan): fix null dereference crash in VkClearManager 2026-05-06 13:15:08 +08:00
swung0x48 8e4a4359b4 [Chore] (MG_Backend/DirectVulkan): eliminate vague texture binding fallbacks 2026-05-05 18:34:44 +08:00
swung0x48 0a000d1628 [Feat] (MG_Backend/DirectVulkan): more built-in function renames 2026-05-05 18:33:20 +08:00
swung0x48 5bd8e369c4 [Feat] (MG_Backend/DirectVulkan): advertise GL_ARB_draw_buffers_blend for DirectVulkan backend 2026-05-05 18:02:20 +08:00
swung0x48 777d756d1b [Fix] (MG_Impl/GL_Texture): properly unbind texture when texture name == 0 2026-05-05 17:23:57 +08:00
swung0x48 7716a8e05d [Fix] (MG_Backend/DirectVulkan): more flexible ResolveSamplerDescriptor 2026-05-05 17:13:35 +08:00
swung0x48 94e93b171c [Feat] (MG_Backend/DirectVulkan): get real maxProgramBindings from VkDevice 2026-05-05 13:19:45 +08:00
swung0x48 1bf12be278 [Fix] (MG_Util/ShaderTranspiler): add preprocessing to remove name-collided glsl functions 2026-05-05 12:58:36 +08:00
swung0x48 99b753be9a [Feat] (MG_Backend/DirectVulkan): implement backend func for glCopyTex(Sub)Image2D 2026-05-05 12:02:33 +08:00
swung0x48 bc2f2d896b [Chore]: bump version to 26.05 2026-05-05 09:12:01 +08:00
swung0x48 659fbf26da [Fix] (MG_Backend/DirectVulkan): fix depth sampler state, fixing BSL
shadow
2026-05-05 00:40:17 +08:00
swung0x48 f61a7bb9c0 [Fix] (MG_Backend/DirectVulkan): fix some uniform/sampler binding 2026-05-04 22:48:50 +08:00
swung0x48 212309083a [Fix] (MG_State/EGLState): fix compilation error on 32-bit arch where EGLAttrib & EGLint are the same type and collides 2026-05-03 19:52:29 +08:00
swung0x48 c1ebd01a70 Merge pull request #8 from MobileGL-Dev/Feat/Backend-Direct-Vulkan
Feat/backend direct vulkan
2026-04-26 21:55:28 +08:00
swung0x48 af58e2c7b4 [Fix] (MG_Backend/DirectVulkan/VertexInputStateFactory): don't use SSCALED formats 2026-04-26 19:43:27 +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
188 changed files with 29520 additions and 13596 deletions
-2
View File
@@ -14,7 +14,6 @@ bugprone-forwarding-reference-overload,
bugprone-inaccurate-erase,
bugprone-incorrect-roundings,
bugprone-integer-division,
bugprone-lambda-function-name,
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-misplaced-operator-in-strlen-in-alloc,
@@ -63,7 +62,6 @@ cert-str34-c,
cppcoreguidelines-interfaces-global-init,
cppcoreguidelines-narrowing-conversions,
cppcoreguidelines-pro-type-member-init,
cppcoreguidelines-pro-type-static-cast-downcast,
cppcoreguidelines-slicing,
google-default-arguments,
google-runtime-operator,
+80
View File
@@ -0,0 +1,80 @@
name: MobileGL Plugin APK
on:
push:
branches:
- dev
- Feat/Backend-Direct-GLES
- Feat/Backend-Direct-Vulkan
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set artifact metadata
run: |
echo "date_today=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV"
echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_ENV"
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: zulu
java-version: '17'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
gradle-version: 8.10.2
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Install Android NDK
run: |
sdkmanager "ndk;27.3.13750724"
echo "ndk.dir=$ANDROID_HOME/ndk/27.3.13750724" >> android-plugin/local.properties
- name: Update glslang external sources
working-directory: 3rdparty/glslang
run: python update_glslang_sources.py
- name: Build plugin APK
run: gradle --no-daemon -p android-plugin :app:assembleRelease
env:
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
- name: Verify signed APK
run: |
APKSIGNER="$(find "$ANDROID_HOME/build-tools" -name apksigner -type f | sort -V | tail -n 1)"
mapfile -t APKS < <(find android-plugin/app/build/outputs/apk -type f -path '*/release/*.apk' | sort)
if [ "${#APKS[@]}" -eq 0 ]; then
echo "::error::No release APKs found under android-plugin/app/build/outputs/apk"
find android-plugin/app/build/outputs/apk -type f -print || true
exit 1
fi
for APK in "${APKS[@]}"; do
if [[ "$APK" == *-unsigned.apk ]]; then
echo "::error::Unsigned release APK produced: $APK"
exit 1
fi
"$APKSIGNER" verify --verbose "$APK"
done
- name: Upload plugin APK
uses: actions/upload-artifact@v4
with:
name: MobileGL-plugin-${{ env.date_today }}-${{ env.short_sha }}
path: |
android-plugin/app/build/outputs/apk/release/*.apk
android-plugin/app/build/outputs/apk/**/release/*.apk
+4 -1
View File
@@ -18,4 +18,7 @@ MobileGL/MG_Test/build
/cmake-build*
.idea
MobileGL/MG*/build*
MobileGL/MG*/cmake-build*
MobileGL/MG*/cmake-build*
/android-plugin/.gradle
/android-plugin/build
/android-plugin/app/build
+9
View File
@@ -19,3 +19,12 @@
[submodule "3rdparty/VulkanMemoryAllocator"]
path = 3rdparty/VulkanMemoryAllocator
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
+21 -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_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/glslang)
add_subdirectory(3rdparty/SPIRV-Cross)
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)
option(BUILD_SHARED_LIBS OFF)
@@ -160,6 +170,8 @@ set(SOURCE_FILES
MobileGL/MG_Util/Converters/GLToMG/RenderStateEnumConverter.cpp
MobileGL/MG_Util/Converters/GLToMG/ProgramEnumConverter.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
@@ -167,6 +179,7 @@ set(SOURCE_FILES
MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp
MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp
MobileGL/MG_Util/ShaderTranspiler/glslang/TMglGlslIoResolver.cpp
MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/FlattenInterfaceStructPass.cpp
MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp
MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp
@@ -219,13 +232,16 @@ set(SOURCE_FILES
MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.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/VertexInputStateFactory.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/VkTextureSamplerManager.cpp
MobileGL/MG_State/GLState/Core.cpp
MobileGL/MG_State/EGLState/Core.cpp
@@ -262,6 +278,8 @@ set(MOBILEGL_LINK_LIBRARIES
SPIRV-Tools
xxHash::xxhash
GPUOpen::VulkanMemoryAllocator
Vulkan::UtilityHeaders
spirv-reflect-static
)
set(MOBILEGL_COMPILE_DEF
+1 -1
View File
@@ -14,7 +14,7 @@ namespace MobileGL::MG_Config {
inline const String ProjectName = "MobileGL";
inline const String CoreName = "MobileGL Core";
inline const String CoreVendor = "MobileGL-Dev (BZLZHH, Swung0x48, Tungsten)";
inline const Version CoreVersion = {26, 2, 0, "-dev", VersionType::Development};
inline const Version CoreVersion = {26, 6, 0, "-dev", VersionType::Development};
inline const VersionStringFormatAttrib DefaultVersionStringFormatAttrib = {2, 2, 0, true, true};
inline const Uint64 CacheVersion = 0;
+4
View File
@@ -8,6 +8,10 @@
#include "Config.h"
#ifndef _WIN32
extern char** environ;
#endif
namespace MobileGL::MG_ConfigLoader {
static UniquePtr<UnorderedMap<String, String>> acceptedEnvVariablesMap;
+15 -9
View File
@@ -34,7 +34,9 @@
#define MOBILEGL_EGL_API MOBILEGL_API
// ====================== MobileGL configurations ======================= //
#define MOBILEGL_LOG_ACTIVE_LEVEL MOBILEGL_LOG_LEVEL_INFO
#ifndef MOBILEGL_LOG_ACTIVE_LEVEL
#define MOBILEGL_LOG_ACTIVE_LEVEL MOBILEGL_LOG_LEVEL_DEBUG
#endif
#define MOBILEGL_LOG_ENABLE_CONSOLE 0
#define MOBILEGL_LOG_ENABLE_FILE 1
@@ -63,11 +65,15 @@
#endif
// =============================== Utils ================================ //
#define MOBILEGL_ASSERT(condition, ...) \
do { \
if (!(condition)) { \
MGLOG_F("Assertion failed" __VA_OPT__(": ") __VA_ARGS__); \
MGLOG_F(" at %s:%d (%s)", __FILE__, __LINE__, __func__); \
TRAP; \
} \
} while (0)
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
#define MOBILEGL_ASSERT(condition, ...) \
do { \
if (!(condition)) { \
MGLOG_F("Assertion failed" __VA_OPT__(": ") __VA_ARGS__); \
MGLOG_F(" at %s:%d (%s)", __FILE__, __LINE__, __func__); \
TRAP; \
} \
} while (0)
#else
#define MOBILEGL_ASSERT(condition, ...)
#endif
+24
View File
@@ -32,6 +32,8 @@
#include <thread>
#include <vector>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <cstdarg>
#include <cstring>
#include <numeric>
@@ -108,10 +110,32 @@
#define VK_USE_PLATFORM_WIN32_KHR
#elif defined(__APPLE__)
#define VK_USE_PLATFORM_METAL_EXT
#elif defined(__linux__)
#define VK_USE_PLATFORM_XLIB_KHR
typedef struct _XDisplay Display;
typedef unsigned long XID;
typedef XID Window;
typedef unsigned long VisualID;
#else
#warning "VK_USE_PLATFORM_*_KHR not defined for this platform!"
#endif
#if defined(VK_USE_PLATFORM_XLIB_KHR)
#pragma push_macro("Bool")
#pragma push_macro("None")
#pragma push_macro("Always")
#pragma push_macro("Status")
#pragma push_macro("LSBFirst")
#pragma push_macro("DestroyAll")
#endif
#include <vulkan/vulkan.h>
#if defined(VK_USE_PLATFORM_XLIB_KHR)
#pragma pop_macro("DestroyAll")
#pragma pop_macro("LSBFirst")
#pragma pop_macro("Status")
#pragma pop_macro("Always")
#pragma pop_macro("None")
#pragma pop_macro("Bool")
#endif
#ifdef TRACY_ENABLE
#include <tracy/Tracy.hpp>
+4 -4
View File
@@ -34,10 +34,10 @@ namespace MobileGL {
void Destroy() {
MGLOG_I("MobileGL closing...");
glslang::FinalizeProcess();
delete MG_State::pGLContext;
delete MG_State::pEGLContext;
delete MG_Impl::GLImpl::TextureImpl::pProxyTextureManager;
delete MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo;
MG_State::pGLContext.reset();
MG_State::pEGLContext.reset();
MG_Impl::GLImpl::TextureImpl::pProxyTextureManager.reset();
MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo.reset();
MG_Util::Debug::Close();
// TODO: add and use Destroy functions for other subsystems
+50 -6
View File
@@ -53,7 +53,8 @@ namespace MobileGL::MG_Backend {
return false;
}
if (m_eglWindowSurfaceInitialized && m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle) {
if (m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Window &&
m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle) {
return true;
}
@@ -63,7 +64,35 @@ namespace MobileGL::MG_Backend {
return false;
}
m_eglWindowSurfaceInitialized = true;
m_eglSurfaceInitialized = true;
m_eglSurfaceKind = SurfaceKind::Window;
m_eglCurrentThreads.clear();
m_backendCapabilitiesInitialized = false;
return true;
}
Bool BackendObject::CreateEGLPbufferSurface(EGLint width, EGLint height) {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
if (!m_eglDisplayInitialized) {
MGLOG_E("CreateEGLPbufferSurface failed: EGL display is not initialized");
return false;
}
if (width <= 0 || height <= 0) {
MGLOG_E("CreateEGLPbufferSurface failed: invalid size %dx%d", width, height);
return false;
}
if (m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Pbuffer) {
return true;
}
if (!InitPbufferSurface(width, height)) {
MGLOG_E("CreateEGLPbufferSurface failed: backend InitPbufferSurface failed");
return false;
}
m_eglSurfaceInitialized = true;
m_eglSurfaceKind = SurfaceKind::Pbuffer;
m_eglCurrentThreads.clear();
m_backendCapabilitiesInitialized = false;
return true;
@@ -81,8 +110,8 @@ namespace MobileGL::MG_Backend {
MGLOG_E("MakeEGLCurrent failed: EGL display mismatch or not initialized");
return false;
}
if (!m_eglWindowSurfaceInitialized) {
MGLOG_E("MakeEGLCurrent failed: EGL window surface is not initialized");
if (!m_eglSurfaceInitialized) {
MGLOG_E("MakeEGLCurrent failed: EGL surface is not initialized");
return false;
}
if (draw == EGL_NO_SURFACE || read == EGL_NO_SURFACE || ctx == EGL_NO_CONTEXT) {
@@ -104,8 +133,9 @@ namespace MobileGL::MG_Backend {
void BackendObject::ResetEGLRuntimeState() {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
m_eglWindowSurfaceInitialized = false;
m_eglSurfaceInitialized = false;
m_backendCapabilitiesInitialized = false;
m_eglSurfaceKind = SurfaceKind::None;
m_eglCurrentThreads.clear();
}
@@ -119,7 +149,7 @@ namespace MobileGL::MG_Backend {
MGLOG_E("SwapEGLBuffers failed: no current context attached");
return false;
}
if (!m_eglWindowSurfaceInitialized || draw == EGL_NO_SURFACE) {
if (!m_eglSurfaceInitialized || draw == EGL_NO_SURFACE) {
MGLOG_E("SwapEGLBuffers failed: invalid draw surface");
return false;
}
@@ -134,8 +164,22 @@ namespace MobileGL::MG_Backend {
return true;
}
void BackendObject::ReleaseEGLResources() {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
ResetEGLRuntimeState();
m_eglDisplay = EGL_NO_DISPLAY;
m_eglDisplayInitialized = false;
m_windowHandle = {};
}
void BackendObject::SetWindowHandle(const WindowHandle& handle) {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
m_windowHandle = handle;
}
Bool BackendObject::InitPbufferSurface(EGLint width, EGLint height) {
(void)width;
(void)height;
return false;
}
} // namespace MobileGL::MG_Backend
+57 -1
View File
@@ -8,8 +8,14 @@
#pragma once
#include <Includes.h>
#include "MG_State/GLState/TextureState/TextureEnum.h"
namespace MobileGL {
namespace MG_State::GLState {
class FramebufferObject;
class ITextureObject;
}
enum class BackendType {
DirectGLES,
DirectVulkan,
@@ -31,6 +37,10 @@ namespace MobileGL {
void (*MultiDrawElementsIndirect)(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount,
GLsizei stride);
void (*MultiDrawArraysIndirect)(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride);
void (*MultiDrawElementsIndirectCount)(GLenum mode, GLenum type, const void* indirect,
GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
void (*MultiDrawArraysIndirectCount)(GLenum mode, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride);
void (*DrawRangeElementsBaseVertex)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices, GLint basevertex);
void (*DrawRangeElements)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
@@ -54,8 +64,17 @@ namespace MobileGL {
void (*ClearBufferfv)(GLenum buffer, GLint drawbuffer, const GLfloat* value);
void (*ClearBufferuiv)(GLenum buffer, GLint drawbuffer, const GLuint* value);
void (*ClearBufferiv)(GLenum buffer, GLint drawbuffer, const GLint* value);
void (*ClearNamedFramebufferfv)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, const GLfloat* value);
void (*ClearNamedFramebufferfi)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
void (*BlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
void (*BlitNamedFramebuffer)(const SharedPtr<MG_State::GLState::FramebufferObject>& readFramebuffer,
const SharedPtr<MG_State::GLState::FramebufferObject>& drawFramebuffer,
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter);
void (*CopyTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
GLsizei height, GLint border);
void (*CopyTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y,
@@ -64,6 +83,27 @@ namespace MobileGL {
void (*ReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
void* pixels);
void (*GetTexImage)(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels);
void (*GetTextureImage)(const SharedPtr<MG_State::GLState::ITextureObject>& texture,
TextureUploadTarget uploadTarget, GLint level, GLenum format, GLenum type,
GLsizei bufSize, GLvoid* pixels);
void (*DispatchCompute)(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
void (*DispatchComputeIndirect)(GLintptr indirect);
void (*MemoryBarrier)(GLbitfield barriers);
void (*MemoryBarrierByRegion)(GLbitfield barriers);
void (*BindImageTexture)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer,
GLenum access, GLenum format);
void (*GetIntegeri_v)(GLenum target, GLuint index, GLint* data);
void (*GetInteger64i_v)(GLenum target, GLuint index, GLint64* data);
void (*GetProgramiv)(GLuint program, GLenum pname, GLint* params);
void (*GetProgramInterfaceiv)(GLuint program, GLenum programInterface, GLenum pname, GLint* params);
GLuint (*GetProgramResourceIndex)(GLuint program, GLenum programInterface, const GLchar* name);
void (*GetProgramResourceName)(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize,
GLsizei* length, GLchar* name);
void (*GetProgramResourceiv)(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount,
const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params);
GLint (*GetProgramResourceLocation)(GLuint program, GLenum programInterface, const GLchar* name);
GLint (*GetProgramResourceLocationIndex)(GLuint program, GLenum programInterface, const GLchar* name);
void (*ShaderStorageBlockBinding)(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding);
};
struct GlobalBackendFunctionsTable {
GLFunctionsTable GL;
@@ -72,10 +112,16 @@ namespace MobileGL {
struct DynamicBackendParameters {
SizeT UniformBufferOffsetAlignment = 256;
SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024;
Uint32 SubgroupSize = 0;
Uint32 SubgroupSupportedStages = 0;
Uint32 SubgroupSupportedFeatures = 0;
Bool SubgroupQuadOperationsInAllStages = false;
};
enum class WindowBackend {
Android,
X11,
// TODO: X11, Wayland, Windows, macOS, etc.
WindowBackendCount,
Unknown = -1
@@ -96,8 +142,10 @@ namespace MobileGL {
virtual Bool InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor);
virtual Bool CreateEGLWindowSurface(const WindowHandle& handle);
virtual Bool CreateEGLPbufferSurface(EGLint width, EGLint height);
virtual Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
virtual Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw);
virtual void ReleaseEGLResources();
void SetWindowHandle(const WindowHandle& handle);
@@ -108,14 +156,22 @@ namespace MobileGL {
virtual BackendType GetBackendType() const = 0;
protected:
enum class SurfaceKind {
None,
Window,
Pbuffer
};
void ResetEGLRuntimeState();
virtual Bool InitPbufferSurface(EGLint width, EGLint height);
mutable std::recursive_mutex m_eglStateMutex;
WindowHandle m_windowHandle;
EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
Bool m_eglDisplayInitialized = false;
Bool m_eglWindowSurfaceInitialized = false;
Bool m_eglSurfaceInitialized = false;
Bool m_backendCapabilitiesInitialized = false;
SurfaceKind m_eglSurfaceKind = SurfaceKind::None;
UnorderedMap<std::thread::id, Bool> m_eglCurrentThreads;
};
} // namespace MG_Backend
@@ -78,18 +78,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
return false;
}
if (handle.Backend != WindowBackend::Android || !handle.Handle) {
MGLOG_E("DirectGLES backend only supports Android native windows");
if ((handle.Backend != WindowBackend::Android && handle.Backend != WindowBackend::X11) || !handle.Handle) {
MGLOG_E("DirectGLES backend only supports Android and X11 native windows");
return false;
}
const Bool sameHandle =
m_eglWindowSurfaceInitialized && m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle;
const Bool sameHandle = m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Window &&
m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle;
if (sameHandle) {
return true;
}
if (m_eglWindowSurfaceInitialized) {
if (m_eglSurfaceInitialized) {
DestroyEGLContext();
ResetEGLRuntimeState();
}
@@ -97,6 +97,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
return BackendObject::CreateEGLWindowSurface(handle);
}
Bool BackendObject_DirectGLES::InitPbufferSurface(EGLint width, EGLint height) {
return DirectGLES::InitPbufferSurface(width, height);
}
Bool BackendObject_DirectGLES::MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
if (IsReleaseCurrentRequest(dpy, draw, read, ctx)) {
return BackendObject::MakeEGLCurrent(dpy, draw, read, ctx);
@@ -109,6 +113,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
return BackendObject::SwapEGLBuffers(dpy, draw);
}
void BackendObject_DirectGLES::ReleaseEGLResources() {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
DestroyEGLContext();
BackendObject::ReleaseEGLResources();
}
const RendererInfo& BackendObject_DirectGLES::GetRendererInfo() const {
static RendererInfo RendererInfo = {
.RendererName = "Espryt", // Renderer Name
@@ -119,7 +129,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
.TargetGLVersion = {3, 3, 0}, // Target OpenGL Version
.TargetGLSLVersion = {4, 6, 0}, // Target Shading Language Version
.Extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32, // OpenGL Extensions
V_OpenGL33, E_GL_ARB_draw_buffers_blend},
V_OpenGL33, E_GL_ARB_draw_buffers_blend, E_GL_ARB_compute_shader,
E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object,
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
E_GL_ARB_texture_storage, E_GL_ARB_direct_state_access,
E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters,
E_GL_ARB_shader_draw_parameters},
.IsCompatibilityProfile = false // Is Compatibility Profile
},
.StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability
@@ -153,6 +169,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
funcsTable.GL.MultiDrawElements = MultiDrawElements;
funcsTable.GL.MultiDrawElementsBaseVertex = MultiDrawElementsBaseVertex;
funcsTable.GL.MultiDrawElementsIndirect = MultiDrawElementsIndirect;
funcsTable.GL.MultiDrawElementsIndirectCount = MultiDrawElementsIndirectCount;
funcsTable.GL.MultiDrawArraysIndirect = MultiDrawArraysIndirect;
funcsTable.GL.DrawRangeElementsBaseVertex = DrawRangeElementsBaseVertex;
funcsTable.GL.DrawRangeElements = DrawRangeElements;
@@ -164,12 +181,30 @@ namespace MobileGL::MG_Backend::DirectGLES {
funcsTable.GL.DrawArraysInstanced = DrawArraysInstanced;
funcsTable.GL.DrawElementsIndirect = DrawElementsIndirect;
funcsTable.GL.DrawArraysIndirect = DrawArraysIndirect;
funcsTable.GL.DispatchCompute = DispatchCompute;
funcsTable.GL.DispatchComputeIndirect = DispatchComputeIndirect;
funcsTable.GL.MemoryBarrier = MemoryBarrier;
funcsTable.GL.MemoryBarrierByRegion = MemoryBarrierByRegion;
funcsTable.GL.BindImageTexture = BindImageTexture;
funcsTable.GL.GetIntegeri_v = GetIntegeri_v;
funcsTable.GL.GetInteger64i_v = GetInteger64i_v;
funcsTable.GL.GetProgramiv = GetProgramiv;
funcsTable.GL.GetProgramInterfaceiv = GetProgramInterfaceiv;
funcsTable.GL.GetProgramResourceIndex = GetProgramResourceIndex;
funcsTable.GL.GetProgramResourceName = GetProgramResourceName;
funcsTable.GL.GetProgramResourceiv = GetProgramResourceiv;
funcsTable.GL.GetProgramResourceLocation = GetProgramResourceLocation;
funcsTable.GL.GetProgramResourceLocationIndex = GetProgramResourceLocationIndex;
funcsTable.GL.ShaderStorageBlockBinding = ShaderStorageBlockBinding;
funcsTable.GL.Clear = Clear;
funcsTable.GL.ClearBufferfi = ClearBufferfi;
funcsTable.GL.ClearBufferfv = ClearBufferfv;
funcsTable.GL.ClearBufferuiv = ClearBufferuiv;
funcsTable.GL.ClearBufferiv = ClearBufferiv;
funcsTable.GL.ClearNamedFramebufferfv = ClearNamedFramebufferfv;
funcsTable.GL.ClearNamedFramebufferfi = ClearNamedFramebufferfi;
funcsTable.GL.BlitFramebuffer = BlitFramebuffer;
funcsTable.GL.BlitNamedFramebuffer = BlitNamedFramebuffer;
funcsTable.GL.CopyTexImage2D = CopyTexImage2D;
funcsTable.GL.CopyTexSubImage2D = CopyTexSubImage2D;
funcsTable.GL.GenerateMipmap = GenerateMipmap;
@@ -23,6 +23,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Bool CreateEGLWindowSurface(const WindowHandle& handle) override;
Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) override;
Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) override;
void ReleaseEGLResources() override;
const RendererInfo& GetRendererInfo() const override;
String GetBackendAPIVersionString() const override;
@@ -35,6 +36,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
private:
void UpdateDynamicBackendParameters();
Bool InitPbufferSurface(EGLint width, EGLint height) override;
Bool m_initialized = false;
MG_External::EGLFunctionsTable m_EGLFunctions;
File diff suppressed because it is too large Load Diff
@@ -8,6 +8,7 @@
#pragma once
#include <Includes.h>
#include <MG_State/GLState/FramebufferState/FramebufferObject.h>
#include <MG_State/GLState/TextureState/TextureState.h>
#include <MG_State/GLState/SamplerState/SamplerObject.h>
#include <MG_Util/BackendLoaders/OpenGL/Loader.h>
@@ -30,6 +31,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount, const GLint* basevertex);
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride);
void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride);
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride);
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices, GLint basevertex);
@@ -46,8 +49,17 @@ namespace MobileGL::MG_Backend::DirectGLES {
GLuint baseinstance);
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
void DrawArraysIndirect(GLenum mode, const void* indirect);
void ClearNamedFramebufferfv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, const GLfloat* value);
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
GLint dstY1, GLbitfield mask, GLenum filter);
void BlitNamedFramebuffer(const SharedPtr<MG_State::GLState::FramebufferObject>& readFramebuffer,
const SharedPtr<MG_State::GLState::FramebufferObject>& drawFramebuffer,
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter);
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
GLsizei height, GLint border);
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
@@ -56,7 +68,26 @@ namespace MobileGL::MG_Backend::DirectGLES {
const GLubyte* GetString(GLenum name);
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 DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
void DispatchComputeIndirect(GLintptr indirect);
void MemoryBarrier(GLbitfield barriers);
void MemoryBarrierByRegion(GLbitfield barriers);
void BindImageTexture(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access,
GLenum format);
void GetIntegeri_v(GLenum target, GLuint index, GLint* data);
void GetInteger64i_v(GLenum target, GLuint index, GLint64* data);
void GetProgramiv(GLuint program, GLenum pname, GLint* params);
void GetProgramInterfaceiv(GLuint program, GLenum programInterface, GLenum pname, GLint* params);
GLuint GetProgramResourceIndex(GLuint program, GLenum programInterface, const GLchar* name);
void GetProgramResourceName(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei* length,
GLchar* name);
void GetProgramResourceiv(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount,
const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params);
GLint GetProgramResourceLocation(GLuint program, GLenum programInterface, const GLchar* name);
GLint GetProgramResourceLocationIndex(GLuint program, GLenum programInterface, const GLchar* name);
void ShaderStorageBlockBinding(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding);
Bool InitWindowSurface(NativeWindowType window);
Bool InitPbufferSurface(EGLint width, EGLint height);
void Present();
void SetEGLFuncsTable(const MG_External::EGLFunctionsTable& eglFuncs);
void SetGLESFuncsTable(const MG_External::GLESFunctionsTable& glesFuncs);
+332 -119
View File
@@ -7,10 +7,6 @@
// End of Source File Header
#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 "DirectGLES.h"
@@ -18,6 +14,7 @@
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
#include <MG_Util/Converters/MGToGL/DataTypeConverter.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/TextureEnumConverter.h>
#include <MG_Util/Converters/MGToStr/TextureEnumConverter.h>
@@ -26,9 +23,63 @@
#include <MG_Util/Converters/GLToMG/FramebufferEnumConverter.h>
#include <MG_Util/Converters/MGToGL/FramebufferEnumConverter.h>
#include <MG_State/GLState/FramebufferState/FramebufferObject.h>
#include <cctype>
namespace MobileGL::MG_Backend::DirectGLES {
constexpr Bool PREFER_MAP_BUFFER_RANGE_FOR_BUFFER_SYNC = true;
constexpr const char* BASE_INSTANCE_UNIFORM_NAME = "mg_BaseInstance";
static Uint ResolveBackendEsslVersion() {
const auto& version = g_GLESCapabilities.GLESVersion;
if (version.Major > 3 || (version.Major == 3 && version.Minor >= 2)) {
return 320;
}
if (version.Major == 3 && version.Minor >= 1) {
return 310;
}
return 300;
}
String ReplaceIdentifier(String source, const String& from, const String& to) {
SizeT pos = 0;
while ((pos = source.find(from, pos)) != String::npos) {
const Bool leftIsIdent = pos > 0 &&
(std::isalnum(static_cast<unsigned char>(source[pos - 1])) || source[pos - 1] == '_');
const SizeT end = pos + from.size();
const Bool rightIsIdent = end < source.size() &&
(std::isalnum(static_cast<unsigned char>(source[end])) || source[end] == '_');
if (!leftIsIdent && !rightIsIdent) {
source.replace(pos, from.size(), to);
pos += to.size();
} else {
pos = end;
}
}
return source;
}
String InjectUniformAfterVersion(String source, const String& declaration) {
const SizeT versionPos = source.find("#version");
if (versionPos == String::npos) {
return declaration + "\n" + source;
}
const SizeT lineEnd = source.find('\n', versionPos);
if (lineEnd == String::npos) {
return source + "\n" + declaration + "\n";
}
source.insert(lineEnd + 1, declaration + "\n");
return source;
}
String EmulateBaseInstanceInVertexShader(String source, GLenum shaderType) {
if (shaderType != GL_VERTEX_SHADER || source.find("gl_BaseInstance") == String::npos) {
return source;
}
source = ReplaceIdentifier(std::move(source), "gl_BaseInstance", BASE_INSTANCE_UNIFORM_NAME);
return InjectUniformAfterVersion(std::move(source),
String("uniform highp int ") + BASE_INSTANCE_UNIFORM_NAME + ";");
}
namespace BufferImpl {
BackendBufferObject::BackendBufferObject() {
@@ -44,7 +95,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
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
@@ -105,7 +156,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
void BackendBufferObject::SyncToBackend_glBufferData(
SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) {
const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
@@ -117,11 +168,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
GLenum usage = MG_Util::ConvertBufferUsageToGLEnum(stateBufferObject->GetUsage());
Bind();
g_GLESFuncs.glBufferData(TempBufferTarget, size, data, usage);
g_GLESFuncs.glBufferData(TempBufferTarget, (GLsizeiptr)size, data, usage);
}
void BackendBufferObject::SyncToBackend_glBufferSubData(
SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) {
const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
@@ -130,7 +181,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const void* data = stateBufferObject->GetDataReadOnly()->data();
// dirty range: [range.start, range.end)
auto ranges = stateBufferObject->GetDirtyRanges();
auto& ranges = stateBufferObject->GetDirtyRanges();
if (ranges.empty()) {
MGLOG_D("No dirty range to sync for buffer with ID: %u", m_backendBufferId);
return;
@@ -138,20 +189,21 @@ namespace MobileGL::MG_Backend::DirectGLES {
for (const auto& range : ranges) {
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);
}
}
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
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
MGLOG_D("Syncing buffer map (glMapBuffer) for object 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()) {
MGLOG_D("No dirty range to sync for buffer with ID: %u", m_backendBufferId);
return;
@@ -159,18 +211,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
SizeT minStart = ranges.GetOverallMinStart();
SizeT maxEnd = ranges.GetOverallMaxEnd();
Bind();
void* mappedData = g_GLESFuncs.glMapBufferRange(TempBufferTarget, minStart, maxEnd - minStart,
(invalidate ? GL_MAP_INVALIDATE_RANGE_BIT : 0) |
(unsynchronized ? GL_MAP_UNSYNCHRONIZED_BIT : 0) |
GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
void* mappedData = g_GLESFuncs.glMapBufferRange(
TempBufferTarget, (GLintptr)minStart, (GLintptr)(maxEnd - minStart),
(invalidate ? GL_MAP_INVALIDATE_RANGE_BIT : 0) | (unsynchronized ? GL_MAP_UNSYNCHRONIZED_BIT : 0) |
GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
const void* data = stateBufferObject->GetDataReadOnly()->data();
if (mappedData) {
MGLOG_D("Mapped buffer data successfully for object with ID: %u", m_backendBufferId);
Memcpy(mappedData, reinterpret_cast<const char*>(data) + minStart, maxEnd - minStart);
// Explicitly flush the dirty ranges
for (const auto& range : ranges) {
g_GLESFuncs.glFlushMappedBufferRange(TempBufferTarget, range.start - minStart,
range.end - range.start);
g_GLESFuncs.glFlushMappedBufferRange(TempBufferTarget, (GLintptr)(range.start - minStart),
(GLintptr)(range.end - range.start));
}
g_GLESFuncs.glUnmapBuffer(TempBufferTarget);
} else {
@@ -191,15 +243,39 @@ namespace MobileGL::MG_Backend::DirectGLES {
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;
} // namespace BufferImpl
namespace VertexArrayImpl {
namespace {
SizeT GetDataTypeSize(DataType type) {
switch (type) {
case DataType::Int8:
case DataType::Uint8:
return 1;
case DataType::Int16:
case DataType::Uint16:
case DataType::Float16:
return 2;
case DataType::Int32:
case DataType::Uint32:
case DataType::Float32:
case DataType::Fixed32:
return 4;
case DataType::Float64:
return 8;
default:
return 0;
}
}
} // namespace
BackendVertexArrayObject::BackendVertexArrayObject() {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
m_clientAttributeBufferIds.fill(0);
g_GLESFuncs.glGenVertexArrays(1, &m_backendVAOId);
if (m_backendVAOId == 0) {
MGLOG_E("Failed to generate vertex array object.");
@@ -209,32 +285,46 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
}
void BackendVertexArrayObject::Bind() {
BackendVertexArrayObject::~BackendVertexArrayObject() {
if (m_backendVAOId != 0) {
g_GLESFuncs.glDeleteVertexArrays(1, &m_backendVAOId);
m_backendVAOId = 0;
}
for (auto& bufferId : m_clientAttributeBufferIds) {
if (bufferId != 0) {
g_GLESFuncs.glDeleteBuffers(1, &bufferId);
bufferId = 0;
}
}
}
void BackendVertexArrayObject::Bind() const {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
g_GLESFuncs.glBindVertexArray(m_backendVAOId);
}
void BackendVertexArrayObject::BindAttributeBuffer(Uint index,
const MG_State::GLState::VertexAttribute& attrib) {
inline Bool BindAttributeBuffer(const MG_State::GLState::VertexAttribute& attrib) {
const auto& bufferObject = attrib.Buffer;
if (!bufferObject) {
MGLOG_W("Attribute has no bound buffer, skipping.");
return;
return false;
}
const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(bufferObject);
const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(bufferObject.get());
if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) {
MGLOG_E("No backend buffer found for attribute's buffer, cannot bind attribute.");
return;
return false;
}
const auto& backendBufferObject = backendBufferIt->second;
backendBufferObject->Bind(GL_ARRAY_BUFFER);
return true;
}
void BackendVertexArrayObject::SyncToBackend(SharedPtr<MG_State::GLState::VertexArrayObject>& stateVAOObject) {
void BackendVertexArrayObject::SyncToBackend(
const SharedPtr<MG_State::GLState::VertexArrayObject>& stateVAOObject) {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
@@ -268,7 +358,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_syncedAttributeVersions[attribIndex].BufferVersion;
if (!needsSyncFormat && !needsSyncBuffer) continue;
BindAttributeBuffer(attribIndex, attrib);
if (!BindAttributeBuffer(attrib)) {
continue;
}
if (!attrib.IsInteger) {
g_GLESFuncs.glVertexAttribPointer(
@@ -288,22 +380,82 @@ namespace MobileGL::MG_Backend::DirectGLES {
Uint16 currentIndexBufferVersion = stateVAOObject->GetIndexBufferBindingSlot().GetVersion();
if (currentIndexBufferVersion != m_syncedIndexBufferVersion) {
const auto& indexBufferBinding = stateVAOObject->GetIndexBufferBindingSlot().GetBoundObject();
Bool indexBufferSynced = false;
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()) {
const auto& backendBufferObject = backendBufferIt->second;
backendBufferObject->Bind(GL_ELEMENT_ARRAY_BUFFER);
indexBufferSynced = true;
} else {
MGLOG_W("No backend buffer found for index buffer binding, cannot bind index buffer.");
}
} else {
g_GLESFuncs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
indexBufferSynced = true;
}
if (indexBufferSynced) {
m_syncedIndexBufferVersion = currentIndexBufferVersion;
}
m_syncedIndexBufferVersion = currentIndexBufferVersion;
}
m_syncedAttributeVersions = allAttributeVersions;
}
UnorderedMap<SharedPtr<MG_State::GLState::VertexArrayObject>, SharedPtr<BackendVertexArrayObject>>
void BackendVertexArrayObject::SyncClientSideAttributesForDrawArrays(
const SharedPtr<MG_State::GLState::VertexArrayObject>& stateVAOObject, GLint first, GLsizei count) {
if (!stateVAOObject || count <= 0 || first < 0) {
return;
}
Bind();
const auto& allAttributes = stateVAOObject->GetAllAttributes();
for (Uint attribIndex = 0; attribIndex < allAttributes.size(); ++attribIndex) {
const auto& attrib = allAttributes[attribIndex];
if (!attrib.Enabled || attrib.Buffer) {
continue;
}
const auto* clientData = reinterpret_cast<const Uint8*>(attrib.Offset);
const SizeT componentSize = GetDataTypeSize(attrib.Type);
if (!clientData || componentSize == 0 || attrib.Size <= 0) {
continue;
}
const SizeT elementSize = componentSize * static_cast<SizeT>(attrib.Size);
const SizeT stride = attrib.Stride > 0 ? static_cast<SizeT>(attrib.Stride) : elementSize;
const SizeT uploadSize = static_cast<SizeT>(first + count - 1) * stride + elementSize;
auto& bufferId = m_clientAttributeBufferIds[attribIndex];
if (bufferId == 0) {
g_GLESFuncs.glGenBuffers(1, &bufferId);
if (bufferId == 0) {
MGLOG_E("Failed to create client-side vertex attribute upload buffer.");
continue;
}
}
g_GLESFuncs.glBindBuffer(GL_ARRAY_BUFFER, bufferId);
g_GLESFuncs.glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(uploadSize), clientData,
GL_STREAM_DRAW);
if (!attrib.IsInteger) {
g_GLESFuncs.glVertexAttribPointer(
attribIndex, attrib.Size, MG_Util::ConvertDataTypeToGLEnum(attrib.Type),
attrib.Normalized ? GL_TRUE : GL_FALSE, static_cast<GLsizei>(stride), nullptr);
} else {
g_GLESFuncs.glVertexAttribIPointer(attribIndex, attrib.Size,
MG_Util::ConvertDataTypeToGLEnum(attrib.Type),
static_cast<GLsizei>(stride), nullptr);
}
}
BufferImpl::g_boundVertexBufferObject = nullptr;
}
StateBackendObjectRegistry<MG_State::GLState::VertexArrayObject, BackendVertexArrayObject>
g_backendVertexArrayObjects;
} // namespace VertexArrayImpl
@@ -336,7 +488,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_boundTexturesCache[unit][targetN] = this;
}
Uint BackendTextureObject::GetBackendTextureId() {
Uint BackendTextureObject::GetBackendTextureId() const {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
@@ -344,7 +496,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
void BackendTextureObject::SyncMipmapsToBackend(
SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
if (!stateTextureObject) {
MGLOG_E("State texture object is null, cannot sync to backend.");
return;
@@ -353,7 +505,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
DebugImpl::ErrorLopper errorLopper;
MGLOG_D("Syncing texture mipmaps with backend ID %u to backend for state ID %u", m_backendTextureId,
stateTextureObject->GetExternalIndex());
@@ -381,7 +532,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
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());
});
const auto baseSize = stateTextureObject->GetBaseSize();
@@ -414,7 +565,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
&glFormat, &glType);
const auto& uploadTargets = textureMipmapObject->GetUploadTargets();
for (auto uploadTarget : uploadTargets) {
for (auto& uploadTarget : uploadTargets) {
for (SizeT level = 0; level < mipmapCount; ++level) {
auto levelTexelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level);
auto levelByteSize = textureMipmapObject->GetMipmapByteSize(uploadTarget, level);
@@ -429,22 +580,22 @@ namespace MobileGL::MG_Backend::DirectGLES {
levelTexelSize.x(), levelTexelSize.y(), levelTexelSize.z(), levelByteSize, pData,
levelDirty ? "true" : "false");
errorLopper.Clear();
DebugImpl::ErrorLopper::Clear();
g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
auto textureTarget = stateTextureObject->GetTarget();
// TODO: handle more texture types
switch (textureTarget) {
case TextureTarget::Texture2D:
case TextureTarget::TextureCubeMap: {
g_GLESFuncs.glTexImage2D(glUploadTarget, static_cast<GLint>(level), glInternalFormat,
static_cast<GLsizei>(levelTexelSize.x()),
static_cast<GLsizei>(levelTexelSize.y()), 0, glFormat, glType,
pData);
g_GLESFuncs.glTexImage2D(
glUploadTarget, static_cast<GLint>(level), (GLint)glInternalFormat,
static_cast<GLsizei>(levelTexelSize.x()), static_cast<GLsizei>(levelTexelSize.y()),
0, glFormat, glType, pData);
break;
}
case TextureTarget::Texture3D: {
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.z()), 0, glFormat, glType, pData);
break;
@@ -454,8 +605,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
}
}
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__, glUploadTarget,
glInternalFormat, glFormat, glType, pData](GLenum err) {
DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__, func = __func__,
glUploadTarget, glInternalFormat, glFormat, glType,
pData](GLenum err) {
MGLOG_D("%s(%s:%d) ES error: %s. glTexImage*: target=%s, internalformat=%s, format=%s, "
"type=%s, pixels=%p",
func, file, line, MG_Util::ConvertGLEnumToString(err).c_str(),
@@ -478,7 +630,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
TextureImpl::GenerateTextureFormatInfo(textureMipmapObject->GetFormat(), &glInternalFormat,
&glFormat, &glType);
const auto& uploadTargets = textureMipmapObject->GetUploadTargets();
for (auto uploadTarget : uploadTargets) {
for (auto& uploadTarget : uploadTargets) {
for (SizeT level = 0; level < mipmapCount; ++level) {
if (!textureMipmapObject->IsStorageDirty(uploadTarget, level)) {
continue;
@@ -499,10 +651,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto glUploadTarget = MG_Util::ConvertTextureUploadTargetToGLEnum(uploadTarget);
g_GLESFuncs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
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());
});
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());
});
auto texelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, level);
g_GLESFuncs.glTexSubImage2D(glUploadTarget, static_cast<GLint>(level), 0, 0,
static_cast<GLsizei>(texelSize.x()),
@@ -518,22 +671,27 @@ namespace MobileGL::MG_Backend::DirectGLES {
auto* textureBufferObject =
static_cast<MG_State::GLState::TextureObjectBuffer*>(stateTextureObject.get());
auto& slot = textureBufferObject->GetBufferBindingSlot();
auto buffer = slot.GetBoundObject();
auto& buffer = slot.GetBoundObject();
if (!buffer) {
MGLOG_D("Texture buffer object with ID: %u has no bound buffer, skipping sync.",
stateTextureObject->GetExternalIndex());
return;
}
auto bufferIndex = buffer->GetExternalIndex();
currentTextureInfo.bufferExternalIndex = bufferIndex;
Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo);
MGLOG_D("Texture state changed significantly or not initialized, regenerating texture (tex buffer) "
"with ID: %u",
m_backendTextureId);
// Need to sync texture buffer if not synced yet
auto& backendBuffers = BufferImpl::g_backendBufferObjects;
SharedPtr<BufferImpl::BackendBufferObject> backendBufferObject;
const auto& backendBufferIt = backendBuffers.find(buffer);
const auto& backendBufferIt = backendBuffers.find(buffer.get());
if (backendBufferIt == backendBuffers.end()) {
backendBufferObject = MakeShared<BufferImpl::BackendBufferObject>();
backendBuffers[buffer] = backendBufferObject;
auto& backendBufferSlot = backendBuffers.GetOrCreate(buffer);
if (!backendBufferSlot) {
backendBufferSlot = MakeShared<BufferImpl::BackendBufferObject>();
}
backendBufferObject = backendBufferSlot;
} else {
backendBufferObject = backendBufferIt->second;
}
@@ -546,14 +704,26 @@ namespace MobileGL::MG_Backend::DirectGLES {
TextureImpl::GenerateTextureFormatInfo(textureBufferObject->GetFormat(), &glInternalFormat, &glFormat,
&glType);
g_GLESFuncs.glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId);
if (needsRegeneration) {
MGLOG_D("Texture state changed significantly or not initialized, regenerating texture buffer with "
"ID: %u, buffer ID: %u, buffer size: %zu, format: %s",
m_backendTextureId, backendId, buffer->GetSize(),
MG_Util::ConvertGLEnumToString(glInternalFormat).c_str());
g_GLESFuncs.glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId);
DebugImpl::ErrorLopper::Loop(
[file = __FILE__, line = __LINE__, func = __func__, glInternalFormat, backendId](GLenum err) {
MGLOG_D("%s(%s:%d) glTexBuffer(format=%s, buffer=%u) ES error: %s",
func, file, line, MG_Util::ConvertGLEnumToString(glInternalFormat).c_str(),
backendId, MG_Util::ConvertGLEnumToString(err).c_str());
});
}
break;
}
default:
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());
});
@@ -561,11 +731,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
void BackendTextureObject::SyncBuiltinSamplerToBackend(
SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
DebugImpl::ErrorLopper errorLopper;
if (!stateTextureObject) {
MGLOG_E("State texture object is null, cannot sync to backend.");
return;
@@ -594,7 +764,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
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());
});
@@ -607,34 +777,39 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glTexParameteri(target, glName, \
MG_Util::ConvertSampler##type##ToGLEnum(samplerParams.internalName)); \
m_cacheSamplerParameters.internalName = samplerParams.internalName; \
errorLopper.Loop([file = __FILE__, line = __LINE__, func = __func__, \
t = MG_Util::ConvertSampler##type##ToGLEnum(samplerParams.internalName)](GLenum err) { \
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()); \
}); \
DebugImpl::ErrorLopper::Loop( \
[file = __FILE__, line = __LINE__, func = __func__, \
t = MG_Util::ConvertSampler##type##ToGLEnum(samplerParams.internalName)](GLenum err) { \
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 ||
m_cacheSamplerParameters.mipmapMode != samplerParams.mipmapMode) {
g_GLESFuncs.glTexParameteri(
target, GL_TEXTURE_MIN_FILTER,
MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.minFilter, samplerParams.mipmapMode));
g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_MIN_FILTER,
(GLint)MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.minFilter,
samplerParams.mipmapMode));
m_cacheSamplerParameters.minFilter = samplerParams.minFilter;
m_cacheSamplerParameters.mipmapMode = samplerParams.mipmapMode;
}
if (m_cacheSamplerParameters.magFilter != samplerParams.magFilter) {
g_GLESFuncs.glTexParameteri(
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;
}
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());
});
SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapS, GL_TEXTURE_WRAP_S, WrapMode)
SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapT, GL_TEXTURE_WRAP_T, WrapMode)
SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapR, GL_TEXTURE_WRAP_R, WrapMode)
if (SupportsWrapR(targetInternal)) {
SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(wrapR, GL_TEXTURE_WRAP_R, WrapMode)
} else {
m_cacheSamplerParameters.wrapR = samplerParams.wrapR;
}
SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(compareFunc, GL_TEXTURE_COMPARE_FUNC, CompareFunc)
SYNC_TEX_SAMPLER_PARAM_IF_CHANGED(compareMode, GL_TEXTURE_COMPARE_MODE, CompareMode)
if (m_cacheSamplerParameters.minLod != samplerParams.minLod) {
@@ -645,18 +820,18 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glTexParameterf(target, GL_TEXTURE_MAX_LOD, 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());
});
#undef SYNC_TEX_SAMPLER_PARAM_IF_CHANGED
}
void BackendTextureObject::SyncTextureParamsToBackend(
SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
DebugImpl::ErrorLopper errorLopper;
if (!stateTextureObject) {
MGLOG_E("State texture object is null, cannot sync to backend.");
return;
@@ -683,7 +858,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
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());
});
@@ -696,14 +871,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, static_cast<GLint>(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());
});
if (m_cacheLodRange.y() != levelRange.y()) {
g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(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());
});
@@ -720,7 +895,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED(a(), GL_TEXTURE_SWIZZLE_A);
#undef SYNC_TEX_SWIZZLE_PARAM_IF_CHANGED
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());
});
}
@@ -730,7 +905,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
GLfloat borderColorArray[4] = {borderColor.x(), borderColor.y(), borderColor.z(), borderColor.w()};
g_GLESFuncs.glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, borderColorArray);
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());
});
}
@@ -760,8 +935,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Array<Array<BackendTextureObject*, (SizeT)TextureTarget::TextureTargetCount>,
MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS>
g_boundTexturesCache;
UnorderedMap<SharedPtr<MG_State::GLState::ITextureObject>, SharedPtr<BackendTextureObject>>
g_backendTextureObjects;
StateBackendObjectRegistry<MG_State::GLState::ITextureObject, BackendTextureObject> g_backendTextureObjects;
} // namespace TextureImpl
namespace FramebufferImpl {
@@ -778,7 +952,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
}
void BackendFramebufferObject::Bind(FramebufferTarget target) {
void BackendFramebufferObject::Bind(FramebufferTarget target) const {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
@@ -788,17 +962,37 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_backendFBOId);
}
Bool BackendFramebufferObject::SyncAttachmentObject(
GLenum glFBOTarget, const MG_State::GLState::FramebufferAttachmentObject& attachmentObject,
GLenum glBackendAttachment) {
void BackendFramebufferObject::InvalidateSyncedState() {
std::fill(std::begin(m_frontendDrawBuffers), std::end(m_frontendDrawBuffers),
FramebufferAttachmentType::Unknown);
std::fill(std::begin(m_backendDrawBuffers), std::end(m_backendDrawBuffers), GL_NONE);
m_frontendReadBuffer = FramebufferAttachmentType::Unknown;
m_backendReadBuffer = GL_NONE;
std::fill(m_syncedFrontendAttachmentVersions.begin(), m_syncedFrontendAttachmentVersions.end(),
static_cast<Uint16>(~0u));
}
static Bool SyncAttachmentObject(GLenum glFBOTarget,
const MG_State::GLState::FramebufferAttachmentObject& attachmentObject,
GLenum glBackendAttachment) {
if (attachmentObject.IsTexture()) {
const auto& textureObject = attachmentObject.GetTexture();
const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject);
SharedPtr<TextureImpl::BackendTextureObject> backendTextureObject;
const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject.get());
if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) {
auto& backendTextureSlot = TextureImpl::g_backendTextureObjects.GetOrCreate(textureObject);
if (!backendTextureSlot) {
backendTextureSlot = MakeShared<TextureImpl::BackendTextureObject>();
}
backendTextureObject = backendTextureSlot;
} else {
backendTextureObject = backendTextureIt->second;
}
if (!backendTextureObject) {
MGLOG_E("%s: No backend texture found for FBO attachment, cannot bind texture.", __func__);
return false;
}
const auto& backendTextureObject = backendTextureIt->second;
backendTextureObject->SyncMipmapsToBackend(textureObject);
auto glTextureTarget = MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget());
backendTextureObject->Bind(glTextureTarget);
g_GLESFuncs.glFramebufferTexture2D(glFBOTarget, glBackendAttachment, glTextureTarget,
@@ -807,11 +1001,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
} else if (attachmentObject.IsRenderbuffer()) {
const auto& renderbufferObject = attachmentObject.GetRenderbuffer();
const auto& backendRenderbufferIt =
RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject);
RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject.get());
SharedPtr<RenderbufferImpl::BackendRenderbufferObject> backendRenderbufferObject;
if (backendRenderbufferIt == RenderbufferImpl::g_backendRenderbufferObjects.end()) {
backendRenderbufferObject = MakeShared<RenderbufferImpl::BackendRenderbufferObject>();
RenderbufferImpl::g_backendRenderbufferObjects[renderbufferObject] = backendRenderbufferObject;
auto& backendRenderbufferSlot =
RenderbufferImpl::g_backendRenderbufferObjects.GetOrCreate(renderbufferObject);
if (!backendRenderbufferSlot) {
backendRenderbufferSlot = MakeShared<RenderbufferImpl::BackendRenderbufferObject>();
}
backendRenderbufferObject = backendRenderbufferSlot;
} else {
backendRenderbufferObject = backendRenderbufferIt->second;
}
@@ -824,8 +1022,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
return true;
}
void BackendFramebufferObject::SyncToBackend(SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject,
FramebufferTarget asTarget) {
void BackendFramebufferObject::SyncToBackend(
const SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject, FramebufferTarget asTarget) {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
@@ -853,7 +1051,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
std::fill(m_backendDrawBuffers, m_backendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, GL_NONE);
int nEffectiveBuffers = 0;
for (GLint i = 0; i < FramebufferObject::MAX_DRAW_BUFFERS; ++i) {
auto frontendBuf = stateDrawBuffers[i];
auto& frontendBuf = stateDrawBuffers[i];
if (frontendBuf == FramebufferAttachmentType::None) {
m_backendDrawBuffers[i] = GL_NONE;
continue;
@@ -892,7 +1090,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
const auto& attachmentVersions = stateFBOObject->GetAllFramebufferAttachmentVersions();
for (SizeT i = 0; i < attachments.size(); ++i) {
const auto& attachmentObject = attachments[i];
FramebufferAttachmentType frontendType = static_cast<FramebufferAttachmentType>(i);
auto frontendType = static_cast<FramebufferAttachmentType>(i);
GLenum glBackendAttachment = GL_NONE;
if (frontendType >= FramebufferAttachmentType::Color0 &&
frontendType <= FramebufferAttachmentType::Color31)
@@ -902,8 +1100,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
// relevant FRONTEND!!! version should be checked and updated
if (m_syncedFrontendAttachmentVersions[i] != attachmentVersions[i]) {
SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment);
m_syncedFrontendAttachmentVersions[i] = attachmentVersions[i];
if (SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment)) {
m_syncedFrontendAttachmentVersions[i] = attachmentVersions[i];
}
}
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
else {
@@ -915,6 +1114,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_Util::ConvertFramebufferAttachmentTypeToString(frontendType).c_str(),
MG_Util::ConvertGLEnumToString(glBackendAttachment).c_str(),
m_syncedFrontendAttachmentVersions[i]);
if (!attachmentObject.IsTexture() && !attachmentObject.IsRenderbuffer()) {
continue;
}
GLint objectType = GL_NONE;
g_GLESFuncs.glGetFramebufferAttachmentParameteriv(
glFBOTarget, glBackendAttachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &objectType);
@@ -928,7 +1130,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
// Verify that the backend object's name and parameters match the frontend attachment state
if (attachmentObject.IsTexture()) {
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(),
"No backend texture found while framebuffer reports texture attachment.");
GLuint backendTexId = backendTextureIt->second->GetBackendTextureId();
@@ -944,7 +1146,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
"Attachment texture level mismatch between GLES and state object.");
} else if (attachmentObject.IsRenderbuffer()) {
const auto& renderbufferObject = attachmentObject.GetRenderbuffer();
auto backendRboIt = RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject);
auto backendRboIt =
RenderbufferImpl::g_backendRenderbufferObjects.find(renderbufferObject.get());
MOBILEGL_ASSERT(
backendRboIt != RenderbufferImpl::g_backendRenderbufferObjects.end(),
"No backend renderbuffer found while framebuffer reports renderbuffer attachment.");
@@ -975,14 +1178,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
return glBackendReadBuffer;
}
UnorderedMap<SharedPtr<MG_State::GLState::FramebufferObject>, SharedPtr<BackendFramebufferObject>>
StateBackendObjectRegistry<MG_State::GLState::FramebufferObject, BackendFramebufferObject>
g_backendFramebufferObjects;
Array<Uint16, SizeT(FramebufferTarget::FramebufferTargetCount)> g_fboBindVersions = {0};
} // namespace FramebufferImpl
namespace PrgramImpl {
UnorderedMap<SharedPtr<MG_State::GLState::ProgramObject>, SharedPtr<BackendProgramObjectImpl>>
g_backendProgramObjects;
StateBackendObjectRegistry<MG_State::GLState::ProgramObject, BackendProgramObjectImpl> g_backendProgramObjects;
BackendProgramObjectImpl::BackendProgramObjectImpl() {
#ifdef TRACY_ENABLE
@@ -1008,7 +1210,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
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
@@ -1068,13 +1271,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
String source;
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;
spvcSession.CreateOptions(&options);
// TODO: check ESSL version supported by backend driver
spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, 320);
spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION,
ResolveBackendEsslVersion());
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_TRUE);
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, SPVC_FALSE);
@@ -1096,6 +1300,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
source = RemoveLayoutBinding(source);
source = ProcessOutColorLocations(source);
source = ForceFlatIntegerVaryings(source, glShaderType);
source = EmulateBaseInstanceInVertexShader(std::move(source), glShaderType);
source = ForceSupporterOutput(source);
// Patch for Photon compiler precision issue
@@ -1146,6 +1352,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
} else {
MGLOG_D("Program linked successfully. ID: %u", m_backendProgramId);
}
m_baseInstanceUniformLocation = g_GLESFuncs.glGetUniformLocation(m_backendProgramId,
BASE_INSTANCE_UNIFORM_NAME);
// Create global UBO
if (stateProgramObject->GetUBOSize() > 0) {
@@ -1161,13 +1369,20 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("Program sync completed. backend ID %u", m_backendProgramId);
}
void BackendProgramObjectImpl::Use() {
void BackendProgramObjectImpl::Use() const {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
MGLOG_D("Using program %u", m_backendProgramId);
g_GLESFuncs.glUseProgram(m_backendProgramId);
}
void BackendProgramObjectImpl::SetBaseInstance(Uint32 baseInstance) const {
if (m_baseInstanceUniformLocation < 0) {
return;
}
g_GLESFuncs.glUniform1i(m_baseInstanceUniformLocation, static_cast<GLint>(baseInstance));
}
} // namespace PrgramImpl
namespace SamplerImpl {
@@ -1184,7 +1399,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
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
@@ -1210,22 +1426,22 @@ namespace MobileGL::MG_Backend::DirectGLES {
#define SYNC_SAMPLER_PARAM_IF_CHANGED(internalName, glName, type) \
if (m_cacheSamplerParameters.internalName != samplerParams.internalName) { \
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; \
}
if (m_cacheSamplerParameters.minFilter != samplerParams.minFilter ||
m_cacheSamplerParameters.mipmapMode != samplerParams.mipmapMode) {
g_GLESFuncs.glSamplerParameteri(
m_backendSamplerId, GL_TEXTURE_MIN_FILTER,
MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.minFilter, samplerParams.mipmapMode));
g_GLESFuncs.glSamplerParameteri(m_backendSamplerId, GL_TEXTURE_MIN_FILTER,
(GLint)MG_Util::ConvertSamplerFilterModeToGLEnum(
samplerParams.minFilter, samplerParams.mipmapMode));
m_cacheSamplerParameters.minFilter = samplerParams.minFilter;
m_cacheSamplerParameters.mipmapMode = samplerParams.mipmapMode;
}
if (m_cacheSamplerParameters.magFilter != samplerParams.magFilter) {
g_GLESFuncs.glSamplerParameteri(
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;
}
@@ -1256,7 +1472,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_boundSamplersCache[unit] = this;
}
Uint BackendSamplerObject::GetBackendSamplerId() {
Uint BackendSamplerObject::GetBackendSamplerId() const {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
@@ -1271,8 +1487,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
Array<BackendSamplerObject*, MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS> g_boundSamplersCache;
UnorderedMap<SharedPtr<MG_State::GLState::SamplerObject>, SharedPtr<BackendSamplerObject>>
g_backendSamplerObjects;
StateBackendObjectRegistry<MG_State::GLState::SamplerObject, BackendSamplerObject> g_backendSamplerObjects;
} // namespace SamplerImpl
namespace RenderbufferImpl {
@@ -1287,7 +1502,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
}
void BackendRenderbufferObject::Bind() {
void BackendRenderbufferObject::Bind() const {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
@@ -1334,9 +1549,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
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;
} // namespace RenderbufferImpl
namespace Utils {} // namespace Utils
} // namespace MobileGL::MG_Backend::DirectGLES
+135 -34
View File
@@ -15,19 +15,115 @@
#include <MG_State/GLState/Core.h>
namespace MobileGL::MG_Backend::DirectGLES {
String EmulateBaseInstanceInVertexShader(String source, GLenum shaderType);
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 {
const GLenum TempBufferTarget = GL_ARRAY_BUFFER;
class BackendBufferObject {
public:
BackendBufferObject();
void SyncToBackend(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject);
Uint GetBackendBufferId() { return m_backendBufferId; }
void SyncToBackend(const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject);
Uint GetBackendBufferId() const { return m_backendBufferId; }
void Bind(GLenum target = TempBufferTarget);
private:
void SyncToBackend_glBufferData(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject);
void SyncToBackend_glBufferSubData(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject);
void SyncToBackend_glMapBufferRange(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject,
void SyncToBackend_glBufferData(const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject);
void SyncToBackend_glBufferSubData(const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject);
void SyncToBackend_glMapBufferRange(const SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject,
Bool invalidate = true, Bool unsynchronized = true);
Uint m_backendBufferId = 0;
@@ -36,29 +132,30 @@ namespace MobileGL::MG_Backend::DirectGLES {
};
extern BackendBufferObject* g_boundVertexBufferObject;
extern UnorderedMap<SharedPtr<MG_State::GLState::BufferObject>, SharedPtr<BackendBufferObject>>
g_backendBufferObjects;
extern StateBackendObjectRegistry<MG_State::GLState::BufferObject, BackendBufferObject> g_backendBufferObjects;
} // namespace BufferImpl
namespace VertexArrayImpl {
class BackendVertexArrayObject {
public:
BackendVertexArrayObject();
void SyncToBackend(SharedPtr<MG_State::GLState::VertexArrayObject>& stateVAOObject);
Uint GetBackendVertexArrayId() { return m_backendVAOId; }
void Bind();
~BackendVertexArrayObject();
void SyncToBackend(const SharedPtr<MG_State::GLState::VertexArrayObject>& stateVAOObject);
void SyncClientSideAttributesForDrawArrays(
const SharedPtr<MG_State::GLState::VertexArrayObject>& stateVAOObject, GLint first, GLsizei count);
Uint GetBackendVertexArrayId() const { return m_backendVAOId; }
void Bind() const;
private:
void BindAttributeBuffer(Uint index, const MG_State::GLState::VertexAttribute& attrib);
Uint m_backendVAOId = 0;
Array<Uint, MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS> m_clientAttributeBufferIds;
Bool m_isInitialized = false;
Uint16 m_syncedIndexBufferVersion = 0;
Array<MG_State::GLState::VertexAttributeVersion, MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS>
m_syncedAttributeVersions;
};
extern UnorderedMap<SharedPtr<MG_State::GLState::VertexArrayObject>, SharedPtr<BackendVertexArrayObject>>
extern StateBackendObjectRegistry<MG_State::GLState::VertexArrayObject, BackendVertexArrayObject>
g_backendVertexArrayObjects;
} // namespace VertexArrayImpl
@@ -71,6 +168,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
return true;
}
inline Bool SupportsWrapR(TextureTarget target) {
return target == TextureTarget::Texture3D || target == TextureTarget::TextureCubeMap;
}
struct StateTextureBasicInfo { // Used for tracking texture state changes
TextureInternalFormat internalFormat = TextureInternalFormat::Unknown;
SizeT width = 0;
@@ -92,11 +193,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
class BackendTextureObject {
public:
BackendTextureObject();
void SyncMipmapsToBackend(SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
void SyncBuiltinSamplerToBackend(SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
void SyncTextureParamsToBackend(SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
void SyncMipmapsToBackend(const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
void SyncBuiltinSamplerToBackend(const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
void SyncTextureParamsToBackend(const SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
void Bind(GLenum target, Uint unit = TempTextureUnit);
Uint GetBackendTextureId();
Uint GetBackendTextureId() const;
private:
Uint m_backendTextureId = 0;
@@ -113,7 +214,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
void ActivateTextureUnit(Uint unit);
void UnbindTexture(Uint unit, GLenum target);
extern UnorderedMap<SharedPtr<MG_State::GLState::ITextureObject>, SharedPtr<BackendTextureObject>>
extern StateBackendObjectRegistry<MG_State::GLState::ITextureObject, BackendTextureObject>
g_backendTextureObjects;
extern Array<Array<BackendTextureObject*, (SizeT)TextureTarget::TextureTargetCount>,
MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS>
@@ -125,13 +226,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
class BackendFramebufferObject {
public:
BackendFramebufferObject();
void SyncToBackend(SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject,
void SyncToBackend(const SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject,
FramebufferTarget asTarget);
Uint GetBackendFramebufferId() { return m_backendFBOId; }
void Bind(FramebufferTarget target);
bool SyncAttachmentObject(GLenum glFBOTarget,
const MG_State::GLState::FramebufferAttachmentObject& attachmentObject,
GLenum glBackendAttachment);
void InvalidateSyncedState();
Uint GetBackendFramebufferId() const { return m_backendFBOId; }
void Bind(FramebufferTarget target) const;
// FramebufferAttachmentType GetCompactedAttachmentTypeAtDrawBufferIndex(Int index);
GLenum GetBackendAttachmentType(FramebufferAttachmentType frontendAtt) const;
@@ -159,7 +258,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
FramebufferObject::FramebufferAttachmentVersionArray m_syncedFrontendAttachmentVersions = {0};
};
extern UnorderedMap<SharedPtr<MG_State::GLState::FramebufferObject>, SharedPtr<BackendFramebufferObject>>
extern StateBackendObjectRegistry<MG_State::GLState::FramebufferObject, BackendFramebufferObject>
g_backendFramebufferObjects;
extern Array<Uint16, SizeT(FramebufferTarget::FramebufferTargetCount)> g_fboBindVersions;
} // namespace FramebufferImpl
@@ -169,18 +268,20 @@ namespace MobileGL::MG_Backend::DirectGLES {
public:
BackendProgramObjectImpl();
~BackendProgramObjectImpl();
void SyncToBackend(SharedPtr<MG_State::GLState::ProgramObject>& stateProgramObject);
void Use();
void SyncToBackend(const SharedPtr<MG_State::GLState::ProgramObject>& stateProgramObject);
void Use() const;
void SetBaseInstance(Uint32 baseInstance) const;
Uint GetBackendProgramId() const { return m_backendProgramId; }
Uint GetBackendGlobalUBOId() const { return m_backendGlobalUBOId; }
private:
Uint m_backendProgramId = 0;
Uint m_backendGlobalUBOId = 0;
Int m_baseInstanceUniformLocation = -1;
Bool m_isInitialized = false;
};
extern UnorderedMap<SharedPtr<MG_State::GLState::ProgramObject>, SharedPtr<BackendProgramObjectImpl>>
extern StateBackendObjectRegistry<MG_State::GLState::ProgramObject, BackendProgramObjectImpl>
g_backendProgramObjects;
} // namespace PrgramImpl
@@ -188,9 +289,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
class BackendSamplerObject {
public:
BackendSamplerObject();
void SyncToBackend(SharedPtr<MG_State::GLState::SamplerObject>& stateSamplerObject);
void SyncToBackend(const SharedPtr<MG_State::GLState::SamplerObject>& stateSamplerObject);
void Bind(Uint unit);
Uint GetBackendSamplerId();
Uint GetBackendSamplerId() const;
private:
Uint m_backendSamplerId = 0;
@@ -203,7 +304,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
extern Array<BackendSamplerObject*, MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS>
g_boundSamplersCache;
extern UnorderedMap<SharedPtr<MG_State::GLState::SamplerObject>, SharedPtr<BackendSamplerObject>>
extern StateBackendObjectRegistry<MG_State::GLState::SamplerObject, BackendSamplerObject>
g_backendSamplerObjects;
} // namespace SamplerImpl
@@ -212,8 +313,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
public:
BackendRenderbufferObject();
void SyncToBackend(const SharedPtr<MG_State::GLState::RenderbufferObject>& stateRBOObject);
Uint GetBackendRenderbufferId() { return m_backendRBOId; }
void Bind();
Uint GetBackendRenderbufferId() const { return m_backendRBOId; }
void Bind() const;
private:
Uint m_backendRBOId = 0;
@@ -223,7 +324,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Int m_cacheHeight = 0;
};
extern UnorderedMap<SharedPtr<MG_State::GLState::RenderbufferObject>, SharedPtr<BackendRenderbufferObject>>
extern StateBackendObjectRegistry<MG_State::GLState::RenderbufferObject, BackendRenderbufferObject>
g_backendRenderbufferObjects;
} // namespace RenderbufferImpl
} // namespace MobileGL::MG_Backend::DirectGLES
+31 -9
View File
@@ -19,10 +19,6 @@
#include <MG_Util/Converters/MGToGL/FramebufferEnumConverter.h>
namespace MobileGL::MG_Backend::DirectGLES {
namespace BufferImpl {} // namespace BufferImpl
namespace VertexArrayImpl {} // namespace VertexArrayImpl
namespace TextureImpl {
void GenerateTextureFormatInfo(TextureInternalFormat internalFormat, GLenum* outInternalFormat,
GLenum* outFormat, GLenum* outType) {
@@ -36,9 +32,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
outInternalFormat, outFormat, outType);
}
} // namespace TextureImpl
namespace FramebufferImpl {} // namespace FramebufferImpl
namespace PrgramImpl {
String ProcessOutColorLocations(const String& glslCode) {
#ifdef TRACY_ENABLE
@@ -108,6 +101,37 @@ namespace MobileGL::MG_Backend::DirectGLES {
return result;
}
String ForceFlatIntegerVaryings(const String& glslCode, GLenum shaderType) {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
String result = glslCode;
const String integerType = R"((?:(?:lowp|mediump|highp)\s+)?(?:u?int|[iu]vec[234])\b)";
auto addFlatQualifier = [&result, &integerType](const String& qualifier) {
const std::regex pattern("(layout\\s*\\([^)]*\\)\\s*)(?!(?:flat|smooth|noperspective)\\s)(" +
qualifier + "\\s+" + integerType + ")");
result = std::regex_replace(result, pattern, "$1flat $2");
};
switch (shaderType) {
case GL_VERTEX_SHADER:
addFlatQualifier("out");
break;
case GL_GEOMETRY_SHADER:
addFlatQualifier("in");
addFlatQualifier("out");
break;
case GL_FRAGMENT_SHADER:
addFlatQualifier("in");
break;
default:
break;
}
return result;
}
String RemoveLayoutBinding(const String& glslCode) {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
@@ -166,7 +190,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
return GL_UNIFORM_BUFFER_BINDING;
case GL_FRAMEBUFFER:
return GL_FRAMEBUFFER_BINDING;
case GL_DRAW_FRAMEBUFFER:
return GL_DRAW_FRAMEBUFFER_BINDING;
case GL_READ_FRAMEBUFFER:
@@ -176,7 +199,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
return GL_RENDERBUFFER_BINDING;
case GL_VERTEX_ARRAY:
return GL_VERTEX_ARRAY_BINDING;
case GL_VERTEX_ARRAY_BINDING:
return GL_VERTEX_ARRAY_BINDING;
+4 -3
View File
@@ -14,15 +14,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
namespace DebugImpl {
class ErrorLopper {
public:
void Loop(std::function<void(GLenum)>);
void Clear();
static void Loop(const std::function<void(GLenum)>&);
static void Clear();
ErrorLopper();
~ErrorLopper();
};
class OpenGLScopeMarker {
public:
explicit OpenGLScopeMarker(String scopeName);
explicit OpenGLScopeMarker(const String& scopeName);
~OpenGLScopeMarker();
};
} // namespace DebugImpl
@@ -43,6 +43,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
namespace PrgramImpl {
String ProcessOutColorLocations(const String& glslCode);
String ForceSupporterOutput(const String& glslCode);
String ForceFlatIntegerVaryings(const String& glslCode, GLenum shaderType);
String RemoveLayoutBinding(const String& glslCode);
} // namespace PrgramImpl
@@ -19,6 +19,27 @@ namespace MobileGL::MG_Backend::DirectVulkan {
BackendObject_DirectVulkan::~BackendObject_DirectVulkan() = default;
BackendObject_DirectVulkan::BackendObject_DirectVulkan():
m_rendererInfo{
.RendererName = "Magma",
.BackendName = "Direct (Vulkan)",
.ExtraVendor = Nullopt,
.RendererGLInfo =
{
.TargetGLVersion = {3, 3, 0},
.TargetGLSLVersion = {4, 6, 0},
.Extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32,
V_OpenGL33, E_GL_ARB_draw_buffers_blend, E_GL_ARB_compute_shader,
E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object,
E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters,
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
E_GL_ARB_texture_storage, E_GL_ARB_direct_state_access,
E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader_int64},
.IsCompatibilityProfile = false
},
.StaticBackendCapability = {.AllowVSOnlyPrograms = false}} {}
Bool BackendObject_DirectVulkan::InitWindowSurface() {
if (!m_windowHandle.Handle) {
MGLOG_E("Cannot initialize DirectVulkan window surface: native window handle is null");
@@ -28,6 +49,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
auto nativeWindow = reinterpret_cast<NativeWindowType>(m_windowHandle.Handle);
pVulkanRenderer = MakeUnique<MG_Backend::DirectVulkan::VulkanRenderer>(nativeWindow);
MOBILEGL_ASSERT(pVulkanRenderer != nullptr, "InitWindowSurface: VulkanRenderer creation failed");
pVulkanRenderer->Initialize();
return true;
}
@@ -46,8 +68,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return false;
}
MG_Util::BackendLoader::FillInVulkanCapabilities(m_vulkanCaps, pVulkanRenderer->GetPhysicalDevice().properties);
const auto& physicalDevice = pVulkanRenderer->GetPhysicalDevice();
if (!MG_Util::BackendLoader::QueryVulkanCapabilities(m_vulkanCaps, pVulkanRenderer->GetInstance(),
physicalDevice.handle)) {
MGLOG_W("DirectVulkan: failed to query extended Vulkan capabilities, using basic properties");
MG_Util::BackendLoader::FillInVulkanCapabilities(m_vulkanCaps, physicalDevice.properties);
}
UpdateDynamicBackendParameters();
UpdateAdvertisedExtensions();
return true;
}
@@ -65,18 +93,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MGLOG_E("DirectVulkan backend not initialized");
return false;
}
if (handle.Backend != WindowBackend::Android || !handle.Handle) {
MGLOG_E("DirectVulkan backend only supports Android native windows");
if (!handle.Handle || (handle.Backend != WindowBackend::Android && handle.Backend != WindowBackend::X11)) {
MGLOG_E("DirectVulkan backend only supports Android and X11 native windows");
return false;
}
const Bool sameHandle =
m_eglWindowSurfaceInitialized && m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle;
const Bool sameHandle = m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Window &&
m_windowHandle.Backend == handle.Backend && m_windowHandle.Handle == handle.Handle;
if (sameHandle) {
return true;
}
if (m_eglWindowSurfaceInitialized || pVulkanRenderer) {
if (m_eglSurfaceInitialized || pVulkanRenderer) {
pVulkanRenderer.reset();
ResetEGLRuntimeState();
}
@@ -105,22 +133,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return BackendObject::SwapEGLBuffers(dpy, draw);
}
void BackendObject_DirectVulkan::ReleaseEGLResources() {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
pVulkanRenderer.reset();
BackendObject::ReleaseEGLResources();
}
const RendererInfo& BackendObject_DirectVulkan::GetRendererInfo() const {
static RendererInfo RendererInfo = {
.RendererName = "Magma", // Renderer Name
.BackendName = "Direct (Vulkan)", // Backend Name
.ExtraVendor = Nullopt, // Extra vendor
.RendererGLInfo =
{
.TargetGLVersion = {3, 3, 0}, // Target OpenGL Version
.TargetGLSLVersion = {4, 6, 0}, // Target Shading Language Version
.Extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32, // OpenGL Extensions
V_OpenGL33},
.IsCompatibilityProfile = false // Is Compatibility Profile
},
.StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability
};
return RendererInfo;
return m_rendererInfo;
}
String BackendObject_DirectVulkan::GetBackendAPIVersionString() const {
@@ -150,6 +170,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
funcsTable.GL.MultiDrawElementsBaseVertex = MultiDrawElementsBaseVertex;
funcsTable.GL.MultiDrawElementsIndirect = MultiDrawElementsIndirect;
funcsTable.GL.MultiDrawArraysIndirect = MultiDrawArraysIndirect;
funcsTable.GL.MultiDrawElementsIndirectCount = MultiDrawElementsIndirectCount;
funcsTable.GL.MultiDrawArraysIndirectCount = MultiDrawArraysIndirectCount;
funcsTable.GL.DrawRangeElementsBaseVertex = DrawRangeElementsBaseVertex;
funcsTable.GL.DrawRangeElements = DrawRangeElements;
funcsTable.GL.DrawElementsInstancedBaseVertexBaseInstance = DrawElementsInstancedBaseVertexBaseInstance;
@@ -165,12 +187,31 @@ namespace MobileGL::MG_Backend::DirectVulkan {
funcsTable.GL.ClearBufferfv = ClearBufferfv;
funcsTable.GL.ClearBufferuiv = ClearBufferuiv;
funcsTable.GL.ClearBufferiv = ClearBufferiv;
funcsTable.GL.ClearNamedFramebufferfv = ClearNamedFramebufferfv;
funcsTable.GL.ClearNamedFramebufferfi = ClearNamedFramebufferfi;
funcsTable.GL.BlitFramebuffer = BlitFramebuffer;
funcsTable.GL.BlitNamedFramebuffer = BlitNamedFramebuffer;
funcsTable.GL.CopyTexImage2D = CopyTexImage2D;
funcsTable.GL.CopyTexSubImage2D = CopyTexSubImage2D;
funcsTable.GL.GenerateMipmap = GenerateMipmap;
funcsTable.GL.ReadPixels = ReadPixels;
funcsTable.GL.GetTexImage = GetTexImage;
funcsTable.GL.GetTextureImage = GetTextureImage;
funcsTable.GL.DispatchCompute = DispatchCompute;
funcsTable.GL.DispatchComputeIndirect = DispatchComputeIndirect;
funcsTable.GL.MemoryBarrier = MemoryBarrier;
funcsTable.GL.MemoryBarrierByRegion = MemoryBarrierByRegion;
funcsTable.GL.BindImageTexture = BindImageTexture;
funcsTable.GL.GetIntegeri_v = GetIntegeri_v;
funcsTable.GL.GetInteger64i_v = GetInteger64i_v;
funcsTable.GL.GetProgramiv = GetProgramiv;
funcsTable.GL.GetProgramInterfaceiv = GetProgramInterfaceiv;
funcsTable.GL.GetProgramResourceIndex = GetProgramResourceIndex;
funcsTable.GL.GetProgramResourceName = GetProgramResourceName;
funcsTable.GL.GetProgramResourceiv = GetProgramResourceiv;
funcsTable.GL.GetProgramResourceLocation = GetProgramResourceLocation;
funcsTable.GL.GetProgramResourceLocationIndex = GetProgramResourceLocationIndex;
funcsTable.GL.ShaderStorageBlockBinding = ShaderStorageBlockBinding;
funcsTableInitialized = true;
}
return funcsTable;
@@ -180,7 +221,85 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return m_dynamicParameters;
}
void BackendObject_DirectVulkan::ApplyVulkanCapabilitiesForTesting(
const MG_External::VulkanCapabilities& capabilities) {
m_vulkanCaps = capabilities;
UpdateDynamicBackendParameters();
UpdateAdvertisedExtensions();
}
void BackendObject_DirectVulkan::UpdateAdvertisedExtensions() {
auto& extensions = m_rendererInfo.RendererGLInfo.Extensions;
extensions.erase(std::remove(extensions.begin(), extensions.end(), E_GL_KHR_shader_subgroup),
extensions.end());
if (m_vulkanCaps.SupportsShaderSubgroup) {
extensions.push_back(E_GL_KHR_shader_subgroup);
}
}
void BackendObject_DirectVulkan::UpdateDynamicBackendParameters() {
const auto mapShaderStages = [](Uint32 vkStages) {
Uint32 glStages = 0;
if ((vkStages & VK_SHADER_STAGE_VERTEX_BIT) != 0) glStages |= GL_VERTEX_SHADER_BIT;
if ((vkStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) != 0) glStages |= GL_TESS_CONTROL_SHADER_BIT;
if ((vkStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) != 0) {
glStages |= GL_TESS_EVALUATION_SHADER_BIT;
}
if ((vkStages & VK_SHADER_STAGE_GEOMETRY_BIT) != 0) glStages |= GL_GEOMETRY_SHADER_BIT;
if ((vkStages & VK_SHADER_STAGE_FRAGMENT_BIT) != 0) glStages |= GL_FRAGMENT_SHADER_BIT;
if ((vkStages & VK_SHADER_STAGE_COMPUTE_BIT) != 0) glStages |= GL_COMPUTE_SHADER_BIT;
return glStages;
};
const auto mapSubgroupFeatures = [](Uint32 vkFeatures) {
Uint32 glFeatures = 0;
if ((vkFeatures & VK_SUBGROUP_FEATURE_BASIC_BIT) != 0) {
glFeatures |= GL_SUBGROUP_FEATURE_BASIC_BIT_KHR;
}
if ((vkFeatures & VK_SUBGROUP_FEATURE_VOTE_BIT) != 0) {
glFeatures |= GL_SUBGROUP_FEATURE_VOTE_BIT_KHR;
}
if ((vkFeatures & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) != 0) {
glFeatures |= GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR;
}
if ((vkFeatures & VK_SUBGROUP_FEATURE_BALLOT_BIT) != 0) {
glFeatures |= GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR;
}
if ((vkFeatures & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) != 0) {
glFeatures |= GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR;
}
if ((vkFeatures & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) != 0) {
glFeatures |= GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR;
}
if ((vkFeatures & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) != 0) {
glFeatures |= GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR;
}
if ((vkFeatures & VK_SUBGROUP_FEATURE_QUAD_BIT) != 0) {
glFeatures |= GL_SUBGROUP_FEATURE_QUAD_BIT_KHR;
}
return glFeatures;
};
static constexpr SizeT kMaxAdvertisedShaderStorageBlockSize = 512ull * 1024ull * 1024ull;
m_dynamicParameters.UniformBufferOffsetAlignment = m_vulkanCaps.UniformBufferOffsetAlignment;
m_dynamicParameters.MaxShaderStorageBlockSize =
std::min(m_vulkanCaps.MaxShaderStorageBlockSize, kMaxAdvertisedShaderStorageBlockSize);
if (m_vulkanCaps.SupportsShaderSubgroup) {
m_dynamicParameters.SubgroupSize = m_vulkanCaps.SubgroupSize;
m_dynamicParameters.SubgroupSupportedStages = mapShaderStages(m_vulkanCaps.SubgroupSupportedStages);
m_dynamicParameters.SubgroupSupportedFeatures = mapSubgroupFeatures(m_vulkanCaps.SubgroupSupportedOperations);
m_dynamicParameters.SubgroupQuadOperationsInAllStages = m_vulkanCaps.SubgroupQuadOperationsInAllStages;
} else {
m_dynamicParameters.SubgroupSize = 0;
m_dynamicParameters.SubgroupSupportedStages = 0;
m_dynamicParameters.SubgroupSupportedFeatures = 0;
m_dynamicParameters.SubgroupQuadOperationsInAllStages = false;
}
if (m_dynamicParameters.MaxShaderStorageBlockSize != m_vulkanCaps.MaxShaderStorageBlockSize) {
MGLOG_I("DirectVulkan: clamped GL_MAX_SHADER_STORAGE_BLOCK_SIZE from %zu to %zu",
m_vulkanCaps.MaxShaderStorageBlockSize,
m_dynamicParameters.MaxShaderStorageBlockSize);
}
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -14,6 +14,7 @@
namespace MobileGL::MG_Backend::DirectVulkan {
class BackendObject_DirectVulkan : public BackendObject {
public:
BackendObject_DirectVulkan();
~BackendObject_DirectVulkan() override;
void Initialize() override;
@@ -23,18 +24,22 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool CreateEGLWindowSurface(const WindowHandle& handle) override;
Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) override;
Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) override;
void ReleaseEGLResources() override;
const RendererInfo& GetRendererInfo() const override;
String GetBackendAPIVersionString() const override;
const GlobalBackendFunctionsTable& GetBackendFunctions() const override;
const DynamicBackendParameters& GetDynamicParameters() const override;
BackendType GetBackendType() const override;
void ApplyVulkanCapabilitiesForTesting(const MG_External::VulkanCapabilities& capabilities);
private:
void UpdateAdvertisedExtensions();
void UpdateDynamicBackendParameters();
Bool m_initialized = false;
DynamicBackendParameters m_dynamicParameters;
MG_External::VulkanCapabilities m_vulkanCaps;
RendererInfo m_rendererInfo;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
File diff suppressed because it is too large Load Diff
@@ -17,6 +17,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value);
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value);
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
void ClearNamedFramebufferfv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer,
GLint drawbuffer, const GLfloat* value);
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer,
GLint drawbuffer, GLfloat depth, GLint stencil);
void Clear(GLbitfield mask);
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
void DrawArrays(GLenum mode, GLint first, GLsizei count);
@@ -27,6 +31,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
GLsizei drawcount, const GLint* basevertex);
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride);
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride);
void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride);
void MultiDrawArraysIndirectCount(GLenum mode, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride);
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices, GLint basevertex);
void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices);
@@ -44,12 +52,37 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void DrawArraysIndirect(GLenum mode, const void* indirect);
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
GLint dstY1, GLbitfield mask, GLenum filter);
void BlitNamedFramebuffer(const SharedPtr<MG_State::GLState::FramebufferObject>& readFramebuffer,
const SharedPtr<MG_State::GLState::FramebufferObject>& drawFramebuffer,
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter);
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
GLsizei height, GLint border);
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
GLsizei height);
void GenerateMipmap(GLenum target);
void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
void DispatchComputeIndirect(GLintptr indirect);
void MemoryBarrier(GLbitfield barriers);
void MemoryBarrierByRegion(GLbitfield barriers);
void BindImageTexture(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access,
GLenum format);
void GetIntegeri_v(GLenum target, GLuint index, GLint* data);
void GetInteger64i_v(GLenum target, GLuint index, GLint64* data);
void GetProgramiv(GLuint program, GLenum pname, GLint* params);
void GetProgramInterfaceiv(GLuint program, GLenum programInterface, GLenum pname, GLint* params);
GLuint GetProgramResourceIndex(GLuint program, GLenum programInterface, const GLchar* name);
void GetProgramResourceName(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize,
GLsizei* length, GLchar* name);
void GetProgramResourceiv(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount,
const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params);
GLint GetProgramResourceLocation(GLuint program, GLenum programInterface, const GLchar* name);
GLint GetProgramResourceLocationIndex(GLuint program, GLenum programInterface, const GLchar* name);
void ShaderStorageBlockBinding(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding);
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels);
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels);
void GetTextureImage(const SharedPtr<MG_State::GLState::ITextureObject>& texture, TextureUploadTarget uploadTarget,
GLint level, GLenum format, GLenum type, GLsizei bufSize, GLvoid* pixels);
void Present();
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,20 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/DirectVulkanResourceState.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 <Includes.h>
namespace MobileGL::MG_State::GLState {
class ProgramObject;
}
namespace MobileGL::MG_Backend::DirectVulkan {
GLuint GetShaderStorageBlockIndex(const MG_State::GLState::ProgramObject& program, const String& name);
GLuint GetShaderStorageBlockBinding(const MG_State::GLState::ProgramObject& program, GLuint blockIndex);
}
@@ -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
@@ -178,9 +178,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
packet.signalSemaphore = m_swapchainImageRenderFinishedSemaphores[swapchainImageIndex];
packet.commandBuffer = frame.commandBuffer;
packet.submitInfo.waitSemaphoreCount = 1;
packet.submitInfo.pWaitSemaphores = &packet.waitSemaphore;
packet.submitInfo.pWaitDstStageMask = &packet.waitDstStageMask;
packet.submitInfo.waitSemaphoreCount = frame.imageAvailableSemaphoreConsumed ? 0U : 1U;
packet.submitInfo.pWaitSemaphores = frame.imageAvailableSemaphoreConsumed ? nullptr : &packet.waitSemaphore;
packet.submitInfo.pWaitDstStageMask = frame.imageAvailableSemaphoreConsumed ? nullptr : &packet.waitDstStageMask;
packet.submitInfo.commandBufferCount = shouldSubmitCommandBuffer ? 1U : 0U;
packet.submitInfo.pCommandBuffers = shouldSubmitCommandBuffer ? &packet.commandBuffer : nullptr;
packet.submitInfo.signalSemaphoreCount = 1;
@@ -217,6 +217,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return result;
}
frame.imageAvailableSemaphoreConsumed = false;
return vkAcquireNextImageKHR(device, swapchain, timeout, frame.imageAvailableSemaphore, acquireFence,
&outImageIndex);
}
@@ -260,6 +261,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
frame.hasCommandBufferRecorded = false;
frame.isCommandRecording = false;
frame.imageAvailableSemaphoreConsumed = false;
return VK_SUCCESS;
}
@@ -277,5 +279,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
frame.imageAvailableSemaphore = VK_NULL_HANDLE;
frame.isCommandRecording = false;
frame.hasCommandBufferRecorded = false;
frame.imageAvailableSemaphoreConsumed = false;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -35,6 +35,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkFence imageInFlightFence = VK_NULL_HANDLE;
Bool isCommandRecording = false;
Bool hasCommandBufferRecorded = false;
Bool imageAvailableSemaphoreConsumed = false;
};
VkResult Initialize(VkDevice device, VkCommandPool commandPool, Uint32 frameCount);
@@ -9,8 +9,21 @@
#include "PipelineFactory.h"
namespace MobileGL::MG_Backend::DirectVulkan {
PipelineFactory::PipelineFactory(VkDevice device, const VulkanRendererConfig& config):
m_device(device), m_config(config) {
MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE, "PipelineFactory: device is null");
VkPipelineCacheCreateInfo pipelineCacheInfo{VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO};
VK_VERIFY(vkCreatePipelineCache(m_device, &pipelineCacheInfo, nullptr, &m_pipelineCache),
"vkCreatePipelineCache");
}
PipelineFactory::~PipelineFactory() {
DestroyAll();
if (m_pipelineCache != VK_NULL_HANDLE) {
vkDestroyPipelineCache(m_device, m_pipelineCache, nullptr);
m_pipelineCache = VK_NULL_HANDLE;
}
}
PipelineFactory::HashType PipelineFactory::ComputeHash(const PipelineCreatePayload& payload) const {
@@ -19,17 +32,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.vertexInputHash, sizeof(payload.vertexInputHash)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.pipelineLayout, sizeof(payload.pipelineLayout)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.renderPass, sizeof(payload.renderPass)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.colorAttachmentCount, sizeof(payload.colorAttachmentCount)));
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.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.depthWriteEnable, sizeof(payload.depthWriteEnable)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.depthCompareOp, sizeof(payload.depthCompareOp)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.blendEnable, sizeof(payload.blendEnable)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.srcColorBlendFactor, sizeof(payload.srcColorBlendFactor)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.dstColorBlendFactor, sizeof(payload.dstColorBlendFactor)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.srcAlphaBlendFactor, sizeof(payload.srcAlphaBlendFactor)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.dstAlphaBlendFactor, sizeof(payload.dstAlphaBlendFactor)));
XXHASH_VERIFY(XXH64_update(m_hashState, &payload.colorWriteMask, sizeof(payload.colorWriteMask)));
if (payload.colorAttachmentCount > 0) {
XXHASH_VERIFY(XXH64_update(
m_hashState,
payload.colorBlendAttachments.data(),
sizeof(payload.colorBlendAttachments[0]) * payload.colorAttachmentCount));
}
return XXH64_digest(m_hashState);
}
@@ -59,6 +75,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MOBILEGL_ASSERT(payload.vertexInputState != nullptr, "PipelineFactory: vertexInputState is null");
MOBILEGL_ASSERT(payload.pipelineLayout != VK_NULL_HANDLE, "PipelineFactory: pipelineLayout is null");
MOBILEGL_ASSERT(payload.renderPass != VK_NULL_HANDLE, "PipelineFactory: renderPass is null");
MOBILEGL_ASSERT(payload.colorAttachmentCount <= PipelineCreatePayload::kMaxColorAttachments,
"PipelineFactory: colorAttachmentCount=%u is unexpectedly large",
payload.colorAttachmentCount);
MGLOG_D("PipelineFactory::CreatePipeline: programHash=0x%llx vertexInputHash=0x%llx colorAttachmentCount=%u subpass=%u",
static_cast<unsigned long long>(payload.programHash),
static_cast<unsigned long long>(payload.vertexInputHash),
payload.colorAttachmentCount,
payload.subpass);
static constexpr VkDynamicState kDynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
@@ -79,8 +103,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
raster.polygonMode = VK_POLYGON_MODE_FILL;
raster.cullMode = VK_CULL_MODE_NONE;
raster.frontFace = VK_FRONT_FACE_CLOCKWISE;
raster.cullMode = payload.cullMode;
raster.frontFace = payload.frontFace;
raster.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo ms{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
@@ -93,18 +117,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
depthStencil.depthBoundsTestEnable = VK_FALSE;
depthStencil.stencilTestEnable = VK_FALSE;
VkPipelineColorBlendAttachmentState colorAttach{};
colorAttach.colorWriteMask = payload.colorWriteMask;
colorAttach.blendEnable = payload.blendEnable ? VK_TRUE : VK_FALSE;
colorAttach.srcColorBlendFactor = payload.srcColorBlendFactor;
colorAttach.dstColorBlendFactor = payload.dstColorBlendFactor;
colorAttach.colorBlendOp = VK_BLEND_OP_ADD;
colorAttach.srcAlphaBlendFactor = payload.srcAlphaBlendFactor;
colorAttach.dstAlphaBlendFactor = payload.dstAlphaBlendFactor;
colorAttach.alphaBlendOp = VK_BLEND_OP_ADD;
Vector<VkPipelineColorBlendAttachmentState> colorAttachments(payload.colorAttachmentCount);
for (Uint32 i = 0; i < payload.colorAttachmentCount; ++i) {
colorAttachments[i] = payload.colorBlendAttachments[i];
}
VkPipelineColorBlendStateCreateInfo blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
blend.attachmentCount = 1;
blend.pAttachments = &colorAttach;
blend.attachmentCount = payload.colorAttachmentCount;
blend.pAttachments = colorAttachments.empty() ? nullptr : colorAttachments.data();
VkGraphicsPipelineCreateInfo gpi{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
gpi.stageCount = static_cast<Uint32>(payload.stages->size());
@@ -122,7 +141,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
gpi.subpass = payload.subpass;
VkPipeline pipeline = VK_NULL_HANDLE;
VK_VERIFY(vkCreateGraphicsPipelines(m_device, VK_NULL_HANDLE, 1, &gpi, nullptr, &pipeline),
VK_VERIFY(vkCreateGraphicsPipelines(m_device, m_pipelineCache, 1, &gpi, nullptr, &pipeline),
"vkCreateGraphicsPipelines");
return pipeline;
}
@@ -10,6 +10,7 @@
#include "Config.h"
#include "../VkIncludes.h"
#include "MG_State/GLState/FramebufferState/FramebufferObject.h"
#include <Includes.h>
namespace MobileGL::MG_Backend::DirectVulkan {
@@ -18,29 +19,26 @@ namespace MobileGL::MG_Backend::DirectVulkan {
using HashType = Uint64;
struct PipelineCreatePayload {
static constexpr Uint32 kMaxColorAttachments = MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS;
HashType programHash = 0;
HashType vertexInputHash = 0;
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
VkRenderPass renderPass = VK_NULL_HANDLE;
Uint32 colorAttachmentCount = 1;
Uint32 subpass = 0;
VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT;
VkFrontFace frontFace = VK_FRONT_FACE_CLOCKWISE;
Bool depthTestEnable = false;
Bool depthWriteEnable = false;
VkCompareOp depthCompareOp = VK_COMPARE_OP_ALWAYS;
Bool blendEnable = false;
VkBlendFactor srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
VkBlendFactor dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
VkBlendFactor srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
VkBlendFactor dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
VkColorComponentFlags colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
Array<VkPipelineColorBlendAttachmentState, kMaxColorAttachments> colorBlendAttachments{};
const Vector<VkPipelineShaderStageCreateInfo>* stages = nullptr;
const VkPipelineVertexInputStateCreateInfo* vertexInputState = nullptr;
};
explicit PipelineFactory(VkDevice device, const VulkanRendererConfig& config):
m_device(device), m_config(config) {}
explicit PipelineFactory(VkDevice device, const VulkanRendererConfig& config);
~PipelineFactory();
PipelineFactory(const PipelineFactory&) = delete;
@@ -53,6 +51,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkDevice m_device = VK_NULL_HANDLE;
const VulkanRendererConfig& m_config;
VkPipelineCache m_pipelineCache = VK_NULL_HANDLE;
UnorderedMap<HashType, VkPipeline> m_cache;
static inline XXH64_state_t* m_hashState = XXH64_createState();
};
File diff suppressed because it is too large Load Diff
@@ -11,11 +11,22 @@
#include "../VkIncludes.h"
#include "MG_State/GLState/ProgramState/ProgramObject.h"
#include "MG_State/GLState/ProgramState/ShaderObject.h"
#include "MG_State/GLState/TextureState/TextureEnum.h"
#include <Includes.h>
namespace MobileGL::MG_Backend::DirectVulkan {
class ProgramFactory {
public:
enum class DescriptorBindingKind : Uint8 {
None = 0,
UniformBufferDynamic,
CombinedImageSampler,
UniformTexelBuffer,
StorageBuffer,
StorageImage
};
enum class CompileOptionBit : Uint {
None = 0,
PositionYFlip = 1 << 0,
@@ -26,69 +37,171 @@ namespace MobileGL::MG_Backend::DirectVulkan {
};
using CompileOptionFlags = Flags<CompileOptionBit>;
using HashType = Uint64;
struct BackendProgramObject {
struct VkProgramObject {
static constexpr Uint32 kMaxVertexInputLocations = 32;
HashType hash = 0;
VkDevice device = VK_NULL_HANDLE;
Vector<VkPipelineShaderStageCreateInfo> stages;
Vector<VkShaderModule> modules;
BackendProgramObject() = default;
BackendProgramObject(const BackendProgramObject&) = delete;
BackendProgramObject& operator=(const BackendProgramObject&) = delete;
BackendProgramObject(BackendProgramObject&& other) noexcept {
// Layout data (previously in separate VkProgramLayout)
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
Vector<DescriptorBindingKind> bindingKinds;
Vector<Uint32> dynamicBindings;
Vector<Int> uniformBlockIndexByBinding;
Vector<String> samplerNameByBinding;
Vector<Int> samplerUniformLocationByBinding;
Vector<TextureTarget> samplerTextureTargetByBinding;
Vector<String> storageBlockNameByBinding;
Vector<Int> storageBlockIndexByBinding;
Int globalUboBinding = -1;
Uint32 activeVertexInputLocationMask = 0;
Array<GLenum, kMaxVertexInputLocations> vertexInputTypes{};
Uint32 activeFragmentOutputLocationMask = 0;
Array<GLenum, kMaxVertexInputLocations> fragmentOutputTypes{};
ShaderStage rasterizationProducerStage = ShaderStage::Unknown;
Uint32 producerOutputComponentCount = 0;
Uint32 fragmentInputComponentCount = 0;
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;
device = other.device;
stages = std::move(other.stages);
modules = std::move(other.modules);
descriptorSetLayout = other.descriptorSetLayout;
pipelineLayout = other.pipelineLayout;
bindingKinds = std::move(other.bindingKinds);
dynamicBindings = std::move(other.dynamicBindings);
uniformBlockIndexByBinding = std::move(other.uniformBlockIndexByBinding);
samplerNameByBinding = std::move(other.samplerNameByBinding);
samplerUniformLocationByBinding = std::move(other.samplerUniformLocationByBinding);
samplerTextureTargetByBinding = std::move(other.samplerTextureTargetByBinding);
storageBlockNameByBinding = std::move(other.storageBlockNameByBinding);
storageBlockIndexByBinding = std::move(other.storageBlockIndexByBinding);
globalUboBinding = other.globalUboBinding;
activeVertexInputLocationMask = other.activeVertexInputLocationMask;
vertexInputTypes = other.vertexInputTypes;
activeFragmentOutputLocationMask = other.activeFragmentOutputLocationMask;
fragmentOutputTypes = other.fragmentOutputTypes;
rasterizationProducerStage = other.rasterizationProducerStage;
producerOutputComponentCount = other.producerOutputComponentCount;
fragmentInputComponentCount = other.fragmentInputComponentCount;
other.hash = 0;
other.device = VK_NULL_HANDLE;
other.descriptorSetLayout = VK_NULL_HANDLE;
other.pipelineLayout = VK_NULL_HANDLE;
other.globalUboBinding = -1;
other.activeVertexInputLocationMask = 0;
other.activeFragmentOutputLocationMask = 0;
other.rasterizationProducerStage = ShaderStage::Unknown;
other.producerOutputComponentCount = 0;
other.fragmentInputComponentCount = 0;
}
BackendProgramObject& operator=(BackendProgramObject&& other) noexcept {
VkProgramObject& operator=(VkProgramObject&& other) noexcept {
if (this == &other) {
return *this;
}
DestroyModules();
stages.clear();
Destroy();
hash = other.hash;
device = other.device;
stages = std::move(other.stages);
modules = std::move(other.modules);
descriptorSetLayout = other.descriptorSetLayout;
pipelineLayout = other.pipelineLayout;
bindingKinds = std::move(other.bindingKinds);
dynamicBindings = std::move(other.dynamicBindings);
uniformBlockIndexByBinding = std::move(other.uniformBlockIndexByBinding);
samplerNameByBinding = std::move(other.samplerNameByBinding);
samplerUniformLocationByBinding = std::move(other.samplerUniformLocationByBinding);
samplerTextureTargetByBinding = std::move(other.samplerTextureTargetByBinding);
storageBlockNameByBinding = std::move(other.storageBlockNameByBinding);
storageBlockIndexByBinding = std::move(other.storageBlockIndexByBinding);
globalUboBinding = other.globalUboBinding;
activeVertexInputLocationMask = other.activeVertexInputLocationMask;
vertexInputTypes = other.vertexInputTypes;
activeFragmentOutputLocationMask = other.activeFragmentOutputLocationMask;
fragmentOutputTypes = other.fragmentOutputTypes;
rasterizationProducerStage = other.rasterizationProducerStage;
producerOutputComponentCount = other.producerOutputComponentCount;
fragmentInputComponentCount = other.fragmentInputComponentCount;
other.hash = 0;
other.device = VK_NULL_HANDLE;
other.descriptorSetLayout = VK_NULL_HANDLE;
other.pipelineLayout = VK_NULL_HANDLE;
other.globalUboBinding = -1;
other.activeVertexInputLocationMask = 0;
other.activeFragmentOutputLocationMask = 0;
other.rasterizationProducerStage = ShaderStage::Unknown;
other.producerOutputComponentCount = 0;
other.fragmentInputComponentCount = 0;
return *this;
}
~BackendProgramObject() {
DestroyModules();
stages.clear();
~VkProgramObject() {
Destroy();
}
private:
void DestroyModules() {
for (auto module : modules) {
if (module != VK_NULL_HANDLE && device != VK_NULL_HANDLE) {
vkDestroyShaderModule(device, module, nullptr);
void Destroy() {
if (s_device != VK_NULL_HANDLE) {
if (pipelineLayout != VK_NULL_HANDLE) {
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();
stages.clear();
}
};
explicit ProgramFactory(VkDevice device, const VulkanRendererConfig& config)
: m_device(device), m_config(config) {}
~ProgramFactory();
explicit ProgramFactory(VkDevice device, const VulkanRendererConfig& config, Uint32 maxBindings = 16)
: m_device(device), m_config(config), m_maxBindings(maxBindings) {
VkProgramObject::s_device = device;
}
~ProgramFactory() = default;
ProgramFactory(const ProgramFactory&) = delete;
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);
static VkShaderStageFlagBits ToVkStage(ShaderStage stage);
private:
struct ProgramLookupCache {
const MG_State::GLState::ProgramObject* program = nullptr;
Uint32 backendStateVersion = 0;
CompileOptionFlags flags{};
HashType hash = 0;
};
static TextureTarget UniformTypeToTextureTarget(GLenum glType);
void ReflectVertexInputs(const Vector<SharedPtr<MG_State::GLState::ShaderObject>>& shaders,
const Vector<Vector<Uint>>& spirv,
VkProgramObject& entry) const;
void ReflectFragmentOutputs(const Vector<SharedPtr<MG_State::GLState::ShaderObject>>& shaders,
const Vector<Vector<Uint>>& spirv,
VkProgramObject& entry) const;
void ReflectLayout(const MG_State::GLState::ProgramObject& program, const Vector<Vector<Uint>>& spirv,
VkProgramObject& entry) const;
VkDevice m_device = VK_NULL_HANDLE;
UnorderedMap<HashType, BackendProgramObject> m_cache;
Uint32 m_maxBindings = 0;
UnorderedMap<HashType, VkProgramObject> m_cache;
const VulkanRendererConfig& m_config;
mutable ProgramLookupCache m_lastLookup;
static inline XXH64_state_t* m_hashState = XXH64_createState();
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -8,6 +8,9 @@
#include "SwapchainObject.h"
#include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h"
#include "MG_State/GLState/TextureState/TextureObject2D.h"
#if defined(__has_include)
#if __has_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
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,
VkSurfaceKHR surface) {
SwapchainCapabilities swapchainCapabilities{};
@@ -127,6 +164,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
createInfo.imageFormat = pickedSurfaceFormat.format;
createInfo.imageColorSpace = pickedSurfaceFormat.colorSpace;
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;
const VkImageUsageFlags requiredImageUsage =
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);
CreateImageViews(device);
CreateDepthStencilResources(device, physicalDevice);
MGLOG_I("Swapchain created, extent = %dx%d, swapchain imageCount = %d", m_extent.width, m_extent.height,
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 | VK_IMAGE_USAGE_TRANSFER_SRC_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) {
DestroyDepthStencilResources(device);
for (auto imageView : m_imageViews) {
vkDestroyImageView(device, imageView, nullptr);
}
@@ -221,6 +384,29 @@ namespace MobileGL::MG_Backend::DirectVulkan {
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) {
m_imageViews.resize(m_images.size(), VK_NULL_HANDLE);
for (SizeT i = 0; i < m_imageViews.size(); i++) {
@@ -38,6 +38,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkSurfaceTransformFlagBitsKHR GetPreTransform() const { return m_preTransform; }
const Vector<VkImage>& GetImages() const { return m_images; }
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;
VkImageLayout GetImageLayout(Uint32 index) const;
void SetImageLayout(Uint32 index, VkImageLayout layout);
@@ -45,6 +51,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
private:
void CreateImageViews(VkDevice device);
void CreateDepthStencilResources(VkDevice device, VkPhysicalDevice physicalDevice);
void DestroyDepthStencilResources(VkDevice device);
static constexpr VkPresentModeKHR s_desiredPresentModes[] {
VK_PRESENT_MODE_MAILBOX_KHR,
VK_PRESENT_MODE_IMMEDIATE_KHR,
@@ -59,5 +67,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Vector<VkImage> m_images;
Vector<VkImageView> m_imageViews;
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
@@ -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,953 @@
// 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_Backend/DirectVulkan/DirectVulkanResourceState.h"
#include "MG_State/GLState/Core.h"
#include "MG_State/GLState/ProgramState/ProgramObject.h"
#include "MG_State/GLState/TextureState/TextureObject2D.h"
#include "MG_State/GLState/TextureState/TextureObjectBuffer.h"
#include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h"
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
#include "MG_Util/Metrics/TextureMetrics.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
namespace MobileGL::MG_Backend::DirectVulkan {
namespace {
constexpr Uint kFallbackTexture2DExternalIndex = 0xFFFFFF00u;
}
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;
}
}
static Int ResolveSamplerUnitIndex(const MG_State::GLState::ProgramObject& program, Int location, Uint32 binding) {
MOBILEGL_ASSERT(location >= -1, "ResolveSamplerUnitIndex: invalid sampler location for binding %u", binding);
if (location < 0) {
return 0;
}
const Int uniformUnit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
MOBILEGL_ASSERT(uniformUnit >= -1,
"ResolveSamplerUnitIndex: invalid texture unit for binding %u location %d (unit=%d)", binding,
location, uniformUnit);
return uniformUnit >= 0 ? uniformUnit : 0;
}
static Bool ShouldDumpDescriptorStats() {
static const Bool enabled = [] {
const char* value = std::getenv("MOBILEGL_DESCRIPTOR_STATS");
return value != nullptr && value[0] != '\0' && std::strcmp(value, "0") != 0;
}();
return enabled;
}
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& view : frame.texelBufferViews) {
if (view != VK_NULL_HANDLE) {
vkDestroyBufferView(m_device, view, nullptr);
}
}
frame.texelBufferViews.clear();
frame.descriptorSetCacheByLayout.clear();
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;
m_fallbackTexture2D.reset();
}
void UniformManager::BeginFrame(Uint32 frameIndex) {
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "UniformDescriptorBinder::BeginFrame invalid frame index");
auto& frame = m_frames[frameIndex];
for (auto& view : frame.texelBufferViews) {
if (view != VK_NULL_HANDLE) {
vkDestroyBufferView(m_device, view, nullptr);
}
}
frame.texelBufferViews.clear();
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& cacheEntryPair : frame.descriptorSetCacheByLayout) {
cacheEntryPair.second.cursor = 0;
}
}
Bool UniformManager::ResolveSamplerDescriptor(VkCommandBuffer commandBuffer,
const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj,
Uint32 binding, VkDescriptorImageInfo& outImageInfo) const {
MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveSamplerDescriptor: texture manager is null");
MOBILEGL_ASSERT(m_samplerManager != nullptr, "ResolveSamplerDescriptor: sampler manager is null");
MOBILEGL_ASSERT(binding < programObj.samplerNameByBinding.size(),
"ResolveSamplerDescriptor: sampler binding %u name lookup out of range", binding);
SharedPtr<MG_State::GLState::ITextureObject> texture;
const Bool resolvedTexture = ResolveSamplerTexture(program, programObj, binding, texture);
if (!resolvedTexture) {
MGLOG_E("ResolveSamplerDescriptor: failed to resolve sampler texture for binding %u ('%s')", binding,
programObj.samplerNameByBinding[binding].c_str());
return false;
}
const Int location = programObj.samplerUniformLocationByBinding[binding];
const Int unit = ResolveSamplerUnitIndex(program, location, binding);
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);
const auto samplerOverride = textureUnit.GetSamplerObject();
const auto preferredTarget = programObj.samplerTextureTargetByBinding[binding];
if (texture == nullptr) {
texture = GetFallbackTexture(preferredTarget);
MOBILEGL_ASSERT(texture != nullptr,
"ResolveSamplerDescriptor: no fallback texture available for binding=%u location=%d unit=%d target=%d",
binding, location, unit, static_cast<Int>(preferredTarget));
MGLOG_W(
"ResolveSamplerDescriptor: using fallback texture for unbound sampler binding=%u ('%s') location=%d unit=%d target=%d",
binding, programObj.samplerNameByBinding[binding].c_str(), location, unit,
static_cast<Int>(preferredTarget));
}
const MG_State::GLState::SamplerObject* samplerToUse =
samplerOverride ? samplerOverride.get() : texture->GetSamplerObject().get();
if (samplerToUse == nullptr) {
MGLOG_E(
"ResolveSamplerDescriptor: sampler binding %u ('%s') has no sampler object (textureId=%d location=%d unit=%d)",
binding, programObj.samplerNameByBinding[binding].c_str(), texture->GetExternalIndex(), location,
unit);
return false;
}
VkTextureManager::TextureResource* resource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
if (resource == nullptr) {
MGLOG_E(
"ResolveSamplerDescriptor: sampler binding %u ('%s') failed to create/sync texture resource (textureId=%d target=%d location=%d unit=%d)",
binding, programObj.samplerNameByBinding[binding].c_str(), texture->GetExternalIndex(),
static_cast<Int>(texture->GetTarget()), location, unit);
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)) {
MGLOG_W("ResolveSamplerDescriptor: framebuffer feedback loop detected: textureId=%d is bound "
"for sampling at binding=%u, but is also attached to drawFbo=%u as %s (level=%d, "
"trackedLayout=%d)",
texture->GetExternalIndex(), binding, drawFbo->GetExternalIndex(),
MG_Util::ConvertFramebufferAttachmentTypeToString(attachmentType).c_str(),
attachmentLevel, static_cast<Int>(resource->layout));
}
const Bool readyForSampling = m_textureManager->TransitionTextureForSampling(commandBuffer, *texture);
if (!readyForSampling) {
MGLOG_E("ResolveSamplerDescriptor: failed to transition textureId=%d for sampler binding=%u",
texture->GetExternalIndex(), binding);
return false;
}
resource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
MOBILEGL_ASSERT(resource != nullptr,
"ResolveSamplerDescriptor: failed to resync textureId=%d after sampling transition",
texture->GetExternalIndex());
MOBILEGL_ASSERT(IsValidSampledImageLayout(resource->layout),
"ResolveSamplerDescriptor: invalid sampled image layout=%d for textureId=%d, binding=%u",
static_cast<Int>(resource->layout), texture->GetExternalIndex(), binding);
}
outImageInfo = {
.sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse, *texture),
.imageView = resource->sampledView != VK_NULL_HANDLE ? resource->sampledView : resource->fullView,
.imageLayout = resource->layout,
};
if (ShouldDumpDescriptorStats()) {
std::fprintf(stderr,
"MOBILEGL_DESCRIPTOR_STATS program=%u binding=%u name=%s location=%d unit=%d "
"texture=%u target=%d sampler=%p imageView=%p layout=%d\n",
program.GetExternalIndex(),
binding,
programObj.samplerNameByBinding[binding].c_str(),
location,
unit,
texture->GetExternalIndex(),
static_cast<Int>(texture->GetTarget()),
reinterpret_cast<void*>(outImageInfo.sampler),
reinterpret_cast<void*>(outImageInfo.imageView),
static_cast<Int>(outImageInfo.imageLayout));
}
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");
MOBILEGL_ASSERT(samplerBindingOverride.texture != nullptr,
"ResolveSamplerDescriptorOverride: override texture is null for binding %u",
samplerBindingOverride.binding);
MOBILEGL_ASSERT(samplerBindingOverride.sampler != nullptr,
"ResolveSamplerDescriptorOverride: override sampler is null for binding %u",
samplerBindingOverride.binding);
auto* resource = m_textureManager->SyncTextureAndGetDescriptor(*samplerBindingOverride.texture);
MOBILEGL_ASSERT(resource != nullptr,
"ResolveSamplerDescriptorOverride: failed to sync override texture resource for binding %u textureId=%d",
samplerBindingOverride.binding, samplerBindingOverride.texture->GetExternalIndex());
MOBILEGL_ASSERT(IsValidSampledImageLayout(resource->layout),
"ResolveSamplerDescriptorOverride: invalid layout %d for binding %u textureId=%d",
static_cast<Int>(resource->layout), samplerBindingOverride.binding,
samplerBindingOverride.texture->GetExternalIndex());
outImageInfo = {
.sampler = m_samplerManager->GetOrCreateSampler(*samplerBindingOverride.sampler,
*samplerBindingOverride.texture),
.imageView = samplerBindingOverride.imageView != VK_NULL_HANDLE ?
samplerBindingOverride.imageView :
(resource->sampledView != VK_NULL_HANDLE ? resource->sampledView : 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) {
outTexture.reset();
MOBILEGL_ASSERT(MG_State::pGLContext != nullptr, "ResolveSamplerTexture: GL context is null");
MOBILEGL_ASSERT(binding < programObj.samplerUniformLocationByBinding.size(),
"ResolveSamplerTexture: sampler location binding %u out of range", binding);
MOBILEGL_ASSERT(binding < programObj.samplerTextureTargetByBinding.size(),
"ResolveSamplerTexture: sampler target binding %u out of range", binding);
const Int location = programObj.samplerUniformLocationByBinding[binding];
const Int unit = ResolveSamplerUnitIndex(program, location, binding);
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);
const TextureTarget preferredTarget = programObj.samplerTextureTargetByBinding[binding];
outTexture = textureUnit.GetBindingSlot(preferredTarget).GetBoundObject();
return true;
}
Bool UniformManager::ResolveTexelBufferDescriptor(const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj,
Uint32 binding, Uint32 frameIndex,
VkBufferView& outBufferView) {
outBufferView = VK_NULL_HANDLE;
MOBILEGL_ASSERT(m_bufferManager != nullptr, "ResolveTexelBufferDescriptor: buffer manager is null");
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "ResolveTexelBufferDescriptor: frame index out of range");
SharedPtr<MG_State::GLState::ITextureObject> texture;
if (!ResolveSamplerTexture(program, programObj, binding, texture) || texture == nullptr) {
MGLOG_E("ResolveTexelBufferDescriptor: texture buffer binding %u ('%s') is unbound", binding,
programObj.samplerNameByBinding[binding].c_str());
return false;
}
if (texture->GetStorageType() != TextureStorageType::Buffer ||
texture->GetTarget() != TextureTarget::TextureBuffer) {
MGLOG_E(
"ResolveTexelBufferDescriptor: binding %u ('%s') expected texture buffer, got textureId=%u target=%d storage=%d",
binding, programObj.samplerNameByBinding[binding].c_str(), texture->GetExternalIndex(),
static_cast<Int>(texture->GetTarget()), static_cast<Int>(texture->GetStorageType()));
return false;
}
auto* textureBuffer = static_cast<MG_State::GLState::TextureObjectBuffer*>(texture.get());
const auto bufferObject = textureBuffer->GetBufferBindingSlot().GetBoundObject();
if (bufferObject == nullptr) {
MGLOG_E("ResolveTexelBufferDescriptor: texture buffer binding %u ('%s') has no GL buffer bound",
binding, programObj.samplerNameByBinding[binding].c_str());
return false;
}
BufferSlice slice{};
if (!m_bufferManager->SyncResidentBuffer(BufferKind::TextureBuffer, bufferObject, slice) || !slice.IsValid()) {
MGLOG_E("ResolveTexelBufferDescriptor: failed to sync GL buffer %u for texture buffer %u",
bufferObject->GetExternalIndex(), texture->GetExternalIndex());
return false;
}
const auto internalFormat = textureBuffer->GetFormat();
const VkFormat vkFormat = MG_Util::ConvertTextureInternalFormatToVkEnum(internalFormat);
if (vkFormat == VK_FORMAT_UNDEFINED) {
MGLOG_E("ResolveTexelBufferDescriptor: unsupported texture buffer internal format %d",
static_cast<Int>(internalFormat));
return false;
}
const VkDeviceSize texelSize =
static_cast<VkDeviceSize>(MG_Util::GetSizedInternalFormatSizeInBytes(internalFormat));
VkDeviceSize viewRange = slice.size;
if (texelSize > 0) {
viewRange = (viewRange / texelSize) * texelSize;
}
if (viewRange == 0) {
MGLOG_E("ResolveTexelBufferDescriptor: texture buffer %u has empty view range", texture->GetExternalIndex());
return false;
}
VkBufferViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
viewInfo.buffer = slice.buffer;
viewInfo.format = vkFormat;
viewInfo.offset = slice.offset;
viewInfo.range = viewRange;
VkBufferView bufferView = VK_NULL_HANDLE;
const VkResult result = vkCreateBufferView(m_device, &viewInfo, nullptr, &bufferView);
if (result != VK_SUCCESS || bufferView == VK_NULL_HANDLE) {
MGLOG_E("ResolveTexelBufferDescriptor: vkCreateBufferView failed result=%d format=%d range=%zu",
result, static_cast<Int>(vkFormat), static_cast<SizeT>(viewRange));
return false;
}
m_frames[frameIndex].texelBufferViews.push_back(bufferView);
outBufferView = bufferView;
return true;
}
Bool UniformManager::ResolveStorageBufferDescriptor(const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj,
Uint32 binding,
VkDescriptorBufferInfo& outBufferInfo) const {
outBufferInfo = {};
MOBILEGL_ASSERT(m_bufferManager != nullptr, "ResolveStorageBufferDescriptor: buffer manager is null");
MOBILEGL_ASSERT(MG_State::pGLContext != nullptr, "ResolveStorageBufferDescriptor: GL context is null");
MOBILEGL_ASSERT(binding < programObj.storageBlockIndexByBinding.size(),
"ResolveStorageBufferDescriptor: binding %u out of range", binding);
const Int blockIndex = programObj.storageBlockIndexByBinding[binding];
MOBILEGL_ASSERT(blockIndex >= 0, "ResolveStorageBufferDescriptor: no SSBO block mapped to binding %u",
binding);
const GLuint frontendBinding =
GetShaderStorageBlockBinding(program, static_cast<GLuint>(blockIndex));
const Uint32 bindingPointCount =
static_cast<Uint32>(MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::ShaderStorage));
MOBILEGL_ASSERT(frontendBinding < bindingPointCount,
"ResolveStorageBufferDescriptor: frontend SSBO binding %u out of range for block '%s'",
frontendBinding, programObj.storageBlockNameByBinding[binding].c_str());
auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, frontendBinding);
const auto bufferObject = bindingPoint.GetBoundObject();
if (bufferObject == nullptr) {
MGLOG_E("ResolveStorageBufferDescriptor: no SSBO bound at frontend binding %u for block '%s'",
frontendBinding, programObj.storageBlockNameByBinding[binding].c_str());
return false;
}
BufferSlice slice{};
if (!m_bufferManager->SyncResidentBuffer(BufferKind::ShaderStorage, bufferObject, slice) || !slice.IsValid()) {
MGLOG_E("ResolveStorageBufferDescriptor: failed to sync GL buffer %u for block '%s'",
bufferObject->GetExternalIndex(), programObj.storageBlockNameByBinding[binding].c_str());
return false;
}
const auto range = bindingPoint.GetRange();
const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(bufferObject->GetSize());
VkDeviceSize rangeStart = static_cast<VkDeviceSize>(std::min(range.start, bufferObject->GetSize()));
VkDeviceSize rangeEnd = static_cast<VkDeviceSize>(std::min(range.end, bufferObject->GetSize()));
if (rangeEnd <= rangeStart) {
rangeStart = 0;
rangeEnd = bufferSize;
}
if (rangeEnd <= rangeStart) {
MGLOG_E("ResolveStorageBufferDescriptor: empty SSBO range for block '%s'",
programObj.storageBlockNameByBinding[binding].c_str());
return false;
}
outBufferInfo.buffer = slice.buffer;
outBufferInfo.offset = slice.offset + rangeStart;
outBufferInfo.range = rangeEnd - rangeStart;
return true;
}
Bool UniformManager::ResolveStorageImageDescriptor(VkCommandBuffer commandBuffer,
const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj,
Uint32 binding,
VkDescriptorImageInfo& outImageInfo) const {
outImageInfo = {};
MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveStorageImageDescriptor: texture manager is null");
MOBILEGL_ASSERT(MG_State::pGLContext != nullptr, "ResolveStorageImageDescriptor: GL context is null");
MOBILEGL_ASSERT(binding < programObj.samplerUniformLocationByBinding.size(),
"ResolveStorageImageDescriptor: binding %u out of range", binding);
const Int location = programObj.samplerUniformLocationByBinding[binding];
if (location < 0) {
MGLOG_E("ResolveStorageImageDescriptor: storage image binding %u has no uniform location", binding);
return false;
}
const Int imageUnit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
if (imageUnit < 0 || imageUnit >= MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS) {
MGLOG_E("ResolveStorageImageDescriptor: image unit %d out of range for binding %u",
imageUnit, binding);
return false;
}
auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(imageUnit);
if (imageBinding.Texture == nullptr) {
MGLOG_E("ResolveStorageImageDescriptor: image unit %d is unbound for binding %u", imageUnit, binding);
return false;
}
const Bool ready = m_textureManager->TransitionTextureForStorageImage(commandBuffer, *imageBinding.Texture);
if (!ready) {
MGLOG_E("ResolveStorageImageDescriptor: failed to transition textureId=%d for image unit %d",
imageBinding.Texture->GetExternalIndex(), imageUnit);
return false;
}
auto* resource = m_textureManager->SyncTextureAndGetDescriptor(*imageBinding.Texture);
if (resource == nullptr) {
return false;
}
const Uint32 mipLevel = static_cast<Uint32>(std::max<GLint>(0, imageBinding.Level));
VkImageView view = m_textureManager->GetOrCreateViewAtMipLevel(*imageBinding.Texture, mipLevel);
if (view == VK_NULL_HANDLE) {
view = resource->fullView;
}
outImageInfo.sampler = VK_NULL_HANDLE;
outImageInfo.imageView = view;
outImageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
return outImageInfo.imageView != VK_NULL_HANDLE;
}
SharedPtr<MG_State::GLState::ITextureObject> UniformManager::GetFallbackTexture(TextureTarget target) const {
MOBILEGL_ASSERT(target == TextureTarget::Texture2D || target == TextureTarget::TextureRectangle,
"UniformManager::GetFallbackTexture: unsupported fallback target=%d",
static_cast<Int>(target));
if (m_fallbackTexture2D == nullptr) {
auto fallbackTexture = MakeShared<MG_State::GLState::TextureObject2D>(kFallbackTexture2DExternalIndex);
fallbackTexture->SetInternalFormat(TextureInternalFormat::RGBA8);
fallbackTexture->AllocateStorage(TextureUploadTarget::Texture2D, 0,
{.texelSize = {1, 1, 1}, .byteSize = 4});
fallbackTexture->MarkStorageDirty(TextureUploadTarget::Texture2D, 0, true);
m_fallbackTexture2D = fallbackTexture;
}
return m_fallbackTexture2D;
}
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::ResolveUniformBufferPayload(const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
const void*& outData, VkDeviceSize& outSize) const {
outData = nullptr;
outSize = 0;
MOBILEGL_ASSERT(MG_State::pGLContext != nullptr, "ResolveUniformBufferPayload: GL context is null");
MOBILEGL_ASSERT(binding < programObj.bindingKinds.size(),
"ResolveUniformBufferPayload: binding %u out of range", binding);
MOBILEGL_ASSERT(programObj.bindingKinds[binding] == ProgramFactory::DescriptorBindingKind::UniformBufferDynamic,
"ResolveUniformBufferPayload: binding %u is not a uniform buffer descriptor", binding);
if (programObj.globalUboBinding == static_cast<Int>(binding)) {
outData = program.GetUBOData();
outSize = static_cast<VkDeviceSize>(program.GetUBOSize());
static const Array<Uint8, 16> emptyGlobalUbo{};
if (outData == nullptr || outSize == 0) {
outData = emptyGlobalUbo.data();
outSize = static_cast<VkDeviceSize>(emptyGlobalUbo.size());
}
return outData != nullptr && outSize > 0;
}
MOBILEGL_ASSERT(binding < programObj.uniformBlockIndexByBinding.size(),
"ResolveUniformBufferPayload: UBO mapping binding %u out of range", binding);
const Int blockIndex = programObj.uniformBlockIndexByBinding[binding];
MOBILEGL_ASSERT(blockIndex >= 0,
"ResolveUniformBufferPayload: no uniform block mapped to descriptor binding %u", binding);
const Uint32 activeUniformBlockCount = static_cast<Uint32>(program.GetActiveUniformBlocksCount());
MOBILEGL_ASSERT(static_cast<Uint32>(blockIndex) < activeUniformBlockCount,
"ResolveUniformBufferPayload: uniform block index %d out of range (count=%u)", blockIndex,
activeUniformBlockCount);
const Uint32 frontendBinding = program.GetUniformBlockBinding(static_cast<Uint32>(blockIndex));
const Uint32 uniformBindingPointCount =
static_cast<Uint32>(MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::Uniform));
MOBILEGL_ASSERT(frontendBinding < uniformBindingPointCount,
"ResolveUniformBufferPayload: frontend UBO binding %u out of range for block '%s'",
frontendBinding, program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, frontendBinding);
const auto bufferObject = bindingPoint.GetBoundObject();
MOBILEGL_ASSERT(bufferObject != nullptr,
"ResolveUniformBufferPayload: no UBO bound at frontend binding %u for block '%s'",
frontendBinding, program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
bufferObject->MarkPersistentMappedRangeDirty();
const auto bufferData = bufferObject->GetDataReadOnly();
MOBILEGL_ASSERT(bufferData != nullptr && !bufferData->empty(),
"ResolveUniformBufferPayload: bound UBO data is empty for block '%s'",
program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
const auto range = bindingPoint.GetRange();
const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(bufferObject->GetSize());
const VkDeviceSize rangeStart = static_cast<VkDeviceSize>(range.start);
MOBILEGL_ASSERT(rangeStart < bufferSize,
"ResolveUniformBufferPayload: UBO range start %zu exceeds buffer size %zu for block '%s'",
static_cast<SizeT>(rangeStart), static_cast<SizeT>(bufferSize),
program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
VkDeviceSize rangeEnd = static_cast<VkDeviceSize>(range.end);
if (rangeEnd > bufferSize) {
rangeEnd = bufferSize;
}
MOBILEGL_ASSERT(rangeEnd > rangeStart,
"ResolveUniformBufferPayload: invalid UBO range [%zu, %zu) for block '%s'",
static_cast<SizeT>(rangeStart), static_cast<SizeT>(rangeEnd),
program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
const VkDeviceSize blockSize = static_cast<VkDeviceSize>(program.GetUBOSizeAt(static_cast<Uint32>(blockIndex)));
MOBILEGL_ASSERT(blockSize > 0,
"ResolveUniformBufferPayload: reflected UBO size is zero for block '%s'",
program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
const VkDeviceSize available = rangeEnd - rangeStart;
MOBILEGL_ASSERT(available >= blockSize,
"ResolveUniformBufferPayload: bound range %zu is smaller than UBO size %zu for block '%s'",
static_cast<SizeT>(available), static_cast<SizeT>(blockSize),
program.GetUniformBlockName(static_cast<Uint32>(blockIndex)).c_str());
outData = bufferData->data() + static_cast<SizeT>(rangeStart);
outSize = blockSize;
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[5]{};
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;
poolSizes[2].type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
poolSizes[2].descriptorCount = descriptorCount;
poolSizes[3].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
poolSizes[3].descriptorCount = descriptorCount;
poolSizes[4].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
poolSizes[4].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];
if (frame.activeDescriptorPoolIndex >= frame.descriptorPools.size()) {
frame.activeDescriptorPoolIndex = 0;
}
if (frame.descriptorPools[frame.activeDescriptorPoolIndex].allocatedSets >=
frame.descriptorPools[frame.activeDescriptorPoolIndex].maxSets) {
const auto availableBucket = std::find_if(
frame.descriptorPools.begin(), frame.descriptorPools.end(),
[](const DescriptorPoolBucket& candidate) { return candidate.allocatedSets < candidate.maxSets; });
if (availableBucket == frame.descriptorPools.end()) {
outDescriptorSet = VK_NULL_HANDLE;
return VK_ERROR_OUT_OF_POOL_MEMORY;
}
frame.activeDescriptorPoolIndex =
static_cast<Uint32>(std::distance(frame.descriptorPools.begin(), availableBucket));
}
auto& bucket = frame.descriptorPools[frame.activeDescriptorPoolIndex];
if (bucket.allocatedSets >= bucket.maxSets) {
outDescriptorSet = VK_NULL_HANDLE;
return VK_ERROR_OUT_OF_POOL_MEMORY;
}
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;
}
return result;
}
VkResult UniformManager::AcquireDescriptorSet(Uint32 frameIndex,
const ProgramFactory::VkProgramObject& programObj,
VkDescriptorSet& outDescriptorSet) {
auto& frame = m_frames[frameIndex];
auto& cache = frame.descriptorSetCacheByLayout[programObj.descriptorSetLayout];
if (cache.cursor < cache.sets.size()) {
outDescriptorSet = cache.sets[cache.cursor++];
} else {
VkResult allocResult = AllocateDescriptorSetsFromActivePool(frameIndex, programObj, outDescriptorSet);
if (allocResult == VK_ERROR_OUT_OF_POOL_MEMORY || allocResult == VK_ERROR_FRAGMENTED_POOL) {
if (!GrowFrameDescriptorPool(frame, frameIndex)) {
MGLOG_E("UniformDescriptorBinder::AcquireDescriptorSet failed: descriptor pool growth failed");
return allocResult;
}
allocResult = AllocateDescriptorSetsFromActivePool(frameIndex, programObj, outDescriptorSet);
}
if (allocResult != VK_SUCCESS || outDescriptorSet == VK_NULL_HANDLE) {
return allocResult;
}
cache.sets.push_back(outDescriptorSet);
++cache.cursor;
MGLOG_D("UniformDescriptorBinder: cached descriptor set count for frame=%u grew to %zu", frameIndex,
cache.sets.size());
}
++frame.allocatedSetsThisFrame;
frame.peakAllocatedSetsThisFrame =
std::max(frame.peakAllocatedSetsThisFrame, frame.allocatedSetsThisFrame);
return VK_SUCCESS;
}
Bool UniformManager::BindProgramUniformBuffers(VkCommandBuffer commandBuffer,
const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj,
Uint32 frameIndex,
VkPipelineBindPoint bindPoint,
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 = AcquireDescriptorSet(frameIndex, programObj, descriptorSet);
if (allocResult != VK_SUCCESS || descriptorSet == VK_NULL_HANDLE) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: descriptor set acquire returned %d",
allocResult);
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<VkBufferView> texelBufferViews;
Vector<Uint32> dynamicOffsets;
bufferInfos.reserve(m_maxBindings);
imageInfos.reserve(m_maxBindings);
texelBufferViews.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 = nullptr;
VkDeviceSize payloadSize = 0;
const Bool hasPayload = ResolveUniformBufferPayload(program, programObj, binding, payload, payloadSize);
MOBILEGL_ASSERT(hasPayload && payload != nullptr && payloadSize > 0,
"UniformDescriptorBinder::BindProgramUniformBuffers failed: missing UBO payload on binding %u",
binding);
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 if (kind == ProgramFactory::DescriptorBindingKind::UniformTexelBuffer) {
VkBufferView bufferView = VK_NULL_HANDLE;
if (!ResolveTexelBufferDescriptor(program, programObj, binding, frameIndex, bufferView) ||
bufferView == VK_NULL_HANDLE) {
MGLOG_E(
"UniformDescriptorBinder::BindProgramUniformBuffers failed: texture buffer binding %u has no valid descriptor",
binding);
return false;
}
texelBufferViews.push_back(bufferView);
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
write.pTexelBufferView = &texelBufferViews.back();
writes.push_back(write);
} else if (kind == ProgramFactory::DescriptorBindingKind::StorageBuffer) {
VkDescriptorBufferInfo bufferInfo{};
if (!ResolveStorageBufferDescriptor(program, programObj, binding, bufferInfo)) {
MGLOG_E(
"UniformDescriptorBinder::BindProgramUniformBuffers failed: storage buffer binding %u has no valid descriptor",
binding);
return false;
}
bufferInfos.push_back(bufferInfo);
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
write.pBufferInfo = &bufferInfos.back();
writes.push_back(write);
} else if (kind == ProgramFactory::DescriptorBindingKind::StorageImage) {
VkDescriptorImageInfo imageInfo{};
if (!ResolveStorageImageDescriptor(commandBuffer, program, programObj, binding, imageInfo)) {
MGLOG_E(
"UniformDescriptorBinder::BindProgramUniformBuffers failed: storage image binding %u has no valid descriptor",
binding);
return false;
}
imageInfos.push_back(imageInfo);
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
write.pImageInfo = &imageInfos.back();
writes.push_back(write);
} 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, bindPoint, programObj.pipelineLayout, 0, 1,
&descriptorSet, static_cast<Uint32>(dynamicOffsets.size()), dynamicOffsets.data());
return true;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,118 @@
// 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;
VkImageView imageView = VK_NULL_HANDLE;
};
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,
VkPipelineBindPoint bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
const SamplerBindingOverride* samplerBindingOverride = nullptr);
private:
struct DescriptorPoolBucket {
VkDescriptorPool handle = VK_NULL_HANDLE;
Uint32 maxSets = 0;
Uint32 allocatedSets = 0;
};
struct DescriptorSetCacheEntry {
Vector<VkDescriptorSet> sets;
Uint32 cursor = 0;
};
struct FrameResources {
Vector<DescriptorPoolBucket> descriptorPools;
UnorderedMap<VkDescriptorSetLayout, DescriptorSetCacheEntry> descriptorSetCacheByLayout;
Vector<VkBufferView> texelBufferViews;
Uint32 activeDescriptorPoolIndex = 0;
Uint32 allocatedSetsThisFrame = 0;
Uint32 peakAllocatedSetsThisFrame = 0;
};
static Bool ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
SharedPtr<MG_State::GLState::ITextureObject>& outTexture);
SharedPtr<MG_State::GLState::ITextureObject> GetFallbackTexture(TextureTarget target) 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 ResolveTexelBufferDescriptor(const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
Uint32 frameIndex, VkBufferView& outBufferView);
Bool ResolveStorageBufferDescriptor(const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
VkDescriptorBufferInfo& outBufferInfo) const;
Bool ResolveStorageImageDescriptor(VkCommandBuffer commandBuffer,
const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
VkDescriptorImageInfo& outImageInfo) const;
Bool ResolveUniformBufferPayload(const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj, Uint32 binding,
const void*& outData, VkDeviceSize& outSize) const;
Bool CreateDescriptorPool(Uint32 maxSets, VkDescriptorPool& outPool) const;
Bool GrowFrameDescriptorPool(FrameResources& frame, Uint32 frameIndex);
VkResult AllocateDescriptorSetsFromActivePool(
Uint32 frameIndex, const ProgramFactory::VkProgramObject& programObj, VkDescriptorSet& outDescriptorSet);
VkResult AcquireDescriptorSet(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;
mutable SharedPtr<MG_State::GLState::ITextureObject> m_fallbackTexture2D;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -41,20 +41,25 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const VertexInputStateFactory::BackendVertexInputState& VertexInputStateFactory::GetOrCreateVertexInputState(
const MG_State::GLState::VertexArrayObject& vao) {
const HashType hash = ComputeHash(vao);
return GetOrCreateVertexInputState(vao, hash);
}
const VertexInputStateFactory::BackendVertexInputState& VertexInputStateFactory::GetOrCreateVertexInputState(
const MG_State::GLState::VertexArrayObject& vao, HashType hash) {
auto it = m_cache.find(hash);
if (it != m_cache.end()) {
return it->second;
}
VertexInputStateBuilder builder;
UnorderedMap<SizeT, Uint32> bindingByBufferKey;
UnorderedMap<SizeT, Uint32> strideByBufferKey;
UnorderedMap<SizeT, VkVertexInputRate> inputRateByBufferKey;
Vector<SizeT> bindingBufferKeys;
Vector<SizeT> bindingBaseOffsets;
Vector<Uint32> bindingAttributeLocations;
Vector<Bool> bindingUsesClientMemory;
for (Uint32 location = 0; location < MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS; ++location) {
const auto& attr = vao.GetAttribute(location);
if (!attr.Enabled || !attr.Buffer) {
if (!attr.Enabled) {
continue;
}
@@ -79,29 +84,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
(attr.Divisor == 0) ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
const SizeT bufferKey = reinterpret_cast<SizeT>(attr.Buffer.get());
Uint32 binding = 0;
auto itBinding = bindingByBufferKey.find(bufferKey);
if (itBinding == bindingByBufferKey.end()) {
binding = static_cast<Uint32>(bindingByBufferKey.size());
bindingByBufferKey.emplace(bufferKey, binding);
strideByBufferKey.emplace(bufferKey, stride);
inputRateByBufferKey.emplace(bufferKey, inputRate);
bindingBufferKeys.push_back(bufferKey);
builder.AddBinding(binding, stride, inputRate);
} else {
binding = itBinding->second;
if (strideByBufferKey[bufferKey] != stride) {
MGLOG_D("Skipping vertex attribute at location %u: stride mismatch (%u vs %u) on same buffer",
location, stride, strideByBufferKey[bufferKey]);
continue;
}
if (inputRateByBufferKey[bufferKey] != inputRate) {
MGLOG_D("Skipping vertex attribute at location %u: input-rate mismatch on same buffer", location);
continue;
}
}
builder.AddAttribute(location, binding, vkFormat, static_cast<Uint32>(attr.Offset));
const Uint32 binding = static_cast<Uint32>(bindingBufferKeys.size());
bindingBufferKeys.push_back(bufferKey);
bindingBaseOffsets.push_back(attr.Buffer ? attr.Offset : 0);
bindingAttributeLocations.push_back(location);
bindingUsesClientMemory.push_back(attr.Buffer == nullptr);
builder.AddBinding(binding, stride, inputRate);
builder.AddAttribute(location, binding, vkFormat, 0);
}
const auto& state = builder.Build();
@@ -111,6 +100,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
entry.bindings = builder.GetBindings();
entry.attributes = builder.GetAttributes();
entry.bindingBufferKeys = std::move(bindingBufferKeys);
entry.bindingBaseOffsets = std::move(bindingBaseOffsets);
entry.bindingAttributeLocations = std::move(bindingAttributeLocations);
entry.bindingUsesClientMemory = std::move(bindingUsesClientMemory);
entry.state = state;
entry.state.pVertexBindingDescriptions = entry.bindings.empty() ? nullptr : entry.bindings.data();
entry.state.pVertexAttributeDescriptions = entry.attributes.empty() ? nullptr : entry.attributes.data();
@@ -148,8 +140,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
case DataType::Int16:
switch (size) {
case 1:
return isInteger ? VK_FORMAT_R16_SINT
: (normalized ? VK_FORMAT_R16_SNORM : VK_FORMAT_R16_SSCALED);
return isInteger ? VK_FORMAT_R16_SINT : (normalized ? VK_FORMAT_R16_SNORM : VK_FORMAT_R16_SSCALED);
case 2:
return isInteger ? VK_FORMAT_R16G16_SINT
: (normalized ? VK_FORMAT_R16G16_SNORM : VK_FORMAT_R16G16_SSCALED);
@@ -164,8 +155,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
case DataType::Uint16:
switch (size) {
case 1:
return isInteger ? VK_FORMAT_R16_UINT
: (normalized ? VK_FORMAT_R16_UNORM : VK_FORMAT_R16_USCALED);
return isInteger ? VK_FORMAT_R16_UINT : (normalized ? VK_FORMAT_R16_UNORM : VK_FORMAT_R16_USCALED);
case 2:
return isInteger ? VK_FORMAT_R16G16_UINT
: (normalized ? VK_FORMAT_R16G16_UNORM : VK_FORMAT_R16G16_USCALED);
@@ -180,8 +170,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
case DataType::Int8:
switch (size) {
case 1:
return isInteger ? VK_FORMAT_R8_SINT
: (normalized ? VK_FORMAT_R8_SNORM : VK_FORMAT_R8_SSCALED);
return isInteger ? VK_FORMAT_R8_SINT : (normalized ? VK_FORMAT_R8_SNORM : VK_FORMAT_R8_SSCALED);
case 2:
return isInteger ? VK_FORMAT_R8G8_SINT
: (normalized ? VK_FORMAT_R8G8_SNORM : VK_FORMAT_R8G8_SSCALED);
@@ -196,8 +185,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
case DataType::Uint8:
switch (size) {
case 1:
return isInteger ? VK_FORMAT_R8_UINT
: (normalized ? VK_FORMAT_R8_UNORM : VK_FORMAT_R8_USCALED);
return isInteger ? VK_FORMAT_R8_UINT : (normalized ? VK_FORMAT_R8_UNORM : VK_FORMAT_R8_USCALED);
case 2:
return isInteger ? VK_FORMAT_R8G8_UINT
: (normalized ? VK_FORMAT_R8G8_UNORM : VK_FORMAT_R8G8_USCALED);
@@ -24,6 +24,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Vector<VkVertexInputBindingDescription> bindings;
Vector<VkVertexInputAttributeDescription> attributes;
Vector<SizeT> bindingBufferKeys;
Vector<SizeT> bindingBaseOffsets;
Vector<Uint32> bindingAttributeLocations;
Vector<Bool> bindingUsesClientMemory;
VkPipelineVertexInputStateCreateInfo state{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
};
@@ -35,11 +38,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VertexInputStateFactory(const VertexInputStateFactory&) = delete;
HashType ComputeHash(const MG_State::GLState::VertexArrayObject& vao) const;
const BackendVertexInputState& GetOrCreateVertexInputState(
const MG_State::GLState::VertexArrayObject& vao, HashType hash);
const BackendVertexInputState& GetOrCreateVertexInputState(const MG_State::GLState::VertexArrayObject& vao);
static SizeT GetComponentSize(DataType type);
private:
static VkFormat ToVkVertexFormat(DataType type, Int size, Bool normalized, Bool isInteger);
static SizeT GetComponentSize(DataType type);
const VulkanRendererConfig& m_config;
UnorderedMap<HashType, BackendVertexInputState> m_cache;
@@ -0,0 +1,254 @@
// 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 | VK_BUFFER_USAGE_INDIRECT_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 unsupported resident buffer kind");
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");
bufferObject->MarkPersistentMappedRangeDirty();
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:
return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
case BufferKind::TextureBuffer:
return VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
case BufferKind::ShaderStorage:
return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
case BufferKind::Indirect:
return VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
default:
return 0;
}
}
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,75 @@
// 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,
TextureBuffer,
ShaderStorage,
Indirect,
};
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_buffer = other.m_buffer;
m_allocation = other.m_allocation;
m_mappedData = other.m_mappedData;
m_size = other.m_size;
other.m_allocator = nullptr;
other.m_buffer = VK_NULL_HANDLE;
other.m_allocation = nullptr;
other.m_mappedData = nullptr;
other.m_size = 0;
}
@@ -31,11 +33,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_allocator = other.m_allocator;
m_buffer = other.m_buffer;
m_allocation = other.m_allocation;
m_mappedData = other.m_mappedData;
m_size = other.m_size;
other.m_allocator = nullptr;
other.m_buffer = VK_NULL_HANDLE;
other.m_allocation = nullptr;
other.m_mappedData = nullptr;
other.m_size = 0;
return *this;
}
@@ -44,6 +48,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
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,
VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocationFlags) {
MOBILEGL_ASSERT(allocator != nullptr, "VkBufferObject::Create requires valid VMA allocator");
@@ -78,6 +86,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
void VkBufferObject::Destroy() {
Unmap();
if (m_allocator != nullptr && m_buffer != VK_NULL_HANDLE && m_allocation != nullptr) {
vmaDestroyBuffer(m_allocator, m_buffer, m_allocation);
}
@@ -87,6 +96,33 @@ namespace MobileGL::MG_Backend::DirectVulkan {
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) {
MOBILEGL_ASSERT(IsValid(), "VkBufferObject::Upload called on invalid buffer");
MOBILEGL_ASSERT(data != nullptr || size == 0, "VkBufferObject::Upload data pointer is null");
@@ -96,15 +132,38 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return true;
}
void* mapped = nullptr;
const VkResult mapResult = vmaMapMemory(m_allocator, m_allocation, &mapped);
if (mapResult != VK_SUCCESS || mapped == nullptr) {
MGLOG_E("VkBufferObject::Upload failed: vmaMapMemory returned %d", mapResult);
const Bool wasMapped = IsMapped();
void* mapped = wasMapped ? m_mappedData : Map();
if (mapped == nullptr) {
MGLOG_E("VkBufferObject::Upload failed: unable to map buffer");
return false;
}
Memcpy(static_cast<Uint8*>(mapped) + offset, data, static_cast<SizeT>(size));
vmaUnmapMemory(m_allocator, m_allocation);
const VkResult flushResult = vmaFlushAllocation(m_allocator, m_allocation, offset, size);
if (flushResult != VK_SUCCESS) {
MGLOG_E("VkBufferObject::Upload failed: vmaFlushAllocation returned %d", flushResult);
if (!wasMapped) {
Unmap();
}
return false;
}
if (!wasMapped) {
Unmap();
}
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
@@ -8,11 +8,20 @@
#pragma once
#include "BufferSlice.h"
#include "../VkIncludes.h"
#include <Includes.h>
#include <vk_mem_alloc.h>
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 {
public:
VkBufferObject() = default;
@@ -23,20 +32,27 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkBufferObject(VkBufferObject&& other) noexcept;
VkBufferObject& operator=(VkBufferObject&& other) noexcept;
Bool Create(const VkBufferObjectDesc& desc);
Bool Create(VmaAllocator allocator, VkDeviceSize size, VkBufferUsageFlags usage,
VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocationFlags = 0);
void Destroy();
void* Map();
void Unmap();
Bool Upload(const void* data, VkDeviceSize size, VkDeviceSize offset = 0);
VkBuffer GetHandle() const { return m_buffer; }
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; }
private:
VmaAllocator m_allocator = nullptr;
VkBuffer m_buffer = VK_NULL_HANDLE;
VmaAllocation m_allocation = nullptr;
void* m_mappedData = nullptr;
VkDeviceSize m_size = 0;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,155 @@
// 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 {
static SharedPtr<MG_State::GLState::ITextureObject> GetClearableAttachmentTexture(
const MG_State::GLState::FramebufferObject& drawFbo, FramebufferAttachmentType attachmentType) {
if (attachmentType == FramebufferAttachmentType::None) {
return nullptr;
}
const auto& attachment = drawFbo.GetAttachment(attachmentType);
if (!attachment.IsTexture() || attachment.IsRenderbuffer()) {
return nullptr;
}
return attachment.GetTexture();
}
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) {
auto texture = GetClearableAttachmentTexture(drawFbo, drawbuf);
if (!texture) {
continue;
}
QueueClear({
.mask = GL_COLOR_BUFFER_BIT,
.color = clearPayload.color
}, texture);
MGLOG_D("%s: %s (texture %d) - color = (%.2f, %.2f, %.2f, %.2f)", __func__,
MG_Util::ConvertFramebufferAttachmentTypeToString(drawbuf).c_str(),
texture->GetExternalIndex(),
clearPayload.color[0], clearPayload.color[1], clearPayload.color[2], clearPayload.color[3]);
}
}
if (mask & GL_DEPTH_BUFFER_BIT) {
auto texture = GetClearableAttachmentTexture(drawFbo, FramebufferAttachmentType::Depth);
if (texture) {
QueueClear({
.mask = GL_DEPTH_BUFFER_BIT,
.depth = clearPayload.depth,
}, texture);
MGLOG_D("%s: Depth (texture %d) - depth = (%.2f)", __func__,
texture->GetExternalIndex(), clearPayload.depth);
}
}
if (mask & GL_STENCIL_BUFFER_BIT) {
auto texture = GetClearableAttachmentTexture(drawFbo, FramebufferAttachmentType::Stencil);
if (texture) {
QueueClear({
.mask = GL_STENCIL_BUFFER_BIT,
.stencil = clearPayload.stencil,
}, texture);
MGLOG_D("%s: Stencil (texture %d) - stencil = (%u)", __func__,
texture->GetExternalIndex(), clearPayload.stencil);
}
}
}
void VkClearManager::QueueClear(const ClearAttachmentPayload& clearPayload,
const SharedPtr<MG_State::GLState::ITextureObject>& texture) {
if (clearPayload.mask == 0) {
return;
}
WeakPtr<MG_State::GLState::ITextureObject> weakTexturePtr = texture;
if (weakTexturePtr.expired())
return;
auto* pTexture = weakTexturePtr.lock().get();
m_aliveObjects[pTexture] = weakTexturePtr;
auto& pending = m_pendingClears[pTexture];
pending.mask |= clearPayload.mask;
if (clearPayload.mask & GL_COLOR_BUFFER_BIT) {
pending.color = clearPayload.color;
}
if (clearPayload.mask & GL_DEPTH_BUFFER_BIT) {
pending.depth = clearPayload.depth;
}
if (clearPayload.mask & GL_STENCIL_BUFFER_BIT) {
pending.stencil = clearPayload.stencil;
}
}
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), mask=0x%x clear value: color = (%.2f, %.2f, %.2f, %.2f), depth = (%.2f), stencil = (%u)", __func__,
texture->GetExternalIndex(),
MG_Util::ConvertTextureInternalFormatToString(texture->GetFormat()).c_str(),
static_cast<Uint32>(outPayload.mask),
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 (auto it = m_aliveObjects.begin(); it != m_aliveObjects.end();) {
auto current = it++;
if (current->second.expired()) {
m_pendingClears.erase(current->first);
m_aliveObjects.erase(current);
++count;
}
}
return count;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,50 @@
// 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 {
GLbitfield mask = 0;
FloatVec4 color = FloatVec4(0.0f, 0.0f, 0.0f, 0.0f);
Float depth = 1.0f;
Uint32 stencil = 0;
};
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,599 @@
#include "VkRenderPassManager.h"
namespace MobileGL::MG_Backend::DirectVulkan {
Bool VkRenderPassManager::Initialize(const InitInfo& initInfo) {
Shutdown();
#include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h"
#include "MG_State/GLState/TextureState/TextureObject2D.h"
#include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h"
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
#include "MG_Util/Metrics/TextureMetrics.h"
if (initInfo.device == VK_NULL_HANDLE || initInfo.colorFormat == VK_FORMAT_UNDEFINED ||
initInfo.depthStencilFormat == VK_FORMAT_UNDEFINED) {
return false;
namespace MobileGL::MG_Backend::DirectVulkan {
static Float ResolveColorClearAlpha(const MG_State::GLState::ITextureObject* texture, Float requestedAlpha) {
if (texture != nullptr && MG_Util::GetBaseInternalFormatComponentCount(texture->GetFormat()) == 3) {
return 1.0f;
}
return requestedAlpha;
}
static MG_State::GLState::ITextureObject* ResolveCompleteColorAttachmentTexture(
const MG_State::GLState::FramebufferObject& fbo,
FramebufferAttachmentType attachmentType,
Uint32 drawBufferIndex) {
if (attachmentType == FramebufferAttachmentType::None) {
return nullptr;
}
m_device = initInfo.device;
m_colorFormat = initInfo.colorFormat;
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;
const auto& attachment = fbo.GetAttachment(attachmentType);
if (!attachment.IsTexture()) {
if (attachment.IsEmpty()) {
MGLOG_D("GetOrCreateRenderPass: draw buffer slot %u (%s) on FBO %u has no bound color attachment; using VK_ATTACHMENT_UNUSED",
drawBufferIndex,
MG_Util::ConvertFramebufferAttachmentTypeToString(attachmentType).c_str(),
fbo.GetExternalIndex());
}
return nullptr;
}
if (!attachment.IsComplete()) {
MGLOG_W("GetOrCreateRenderPass: draw buffer slot %u (%s) on FBO %u has an incomplete texture attachment; using VK_ATTACHMENT_UNUSED",
drawBufferIndex,
MG_Util::ConvertFramebufferAttachmentTypeToString(attachmentType).c_str(),
fbo.GetExternalIndex());
return nullptr;
}
auto* texture = attachment.GetTexture().get();
if (texture == nullptr) {
MGLOG_W("GetOrCreateRenderPass: draw buffer slot %u (%s) on FBO %u resolved to a null texture; using VK_ATTACHMENT_UNUSED",
drawBufferIndex,
MG_Util::ConvertFramebufferAttachmentTypeToString(attachmentType).c_str(),
fbo.GetExternalIndex());
return nullptr;
}
return texture;
}
DepthStencilAttachmentLoadInfo ResolveDepthStencilAttachmentLoadInfo(
VkImageLayout trackedLayout, Bool clearDepth, Bool clearStencil) {
DepthStencilAttachmentLoadInfo info{};
info.depthLoadOp = clearDepth
? VK_ATTACHMENT_LOAD_OP_CLEAR
: (trackedLayout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE
: VK_ATTACHMENT_LOAD_OP_LOAD);
info.stencilLoadOp = clearStencil
? VK_ATTACHMENT_LOAD_OP_CLEAR
: (trackedLayout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE
: VK_ATTACHMENT_LOAD_OP_LOAD);
info.initialLayout =
(trackedLayout == VK_IMAGE_LAYOUT_UNDEFINED || (clearDepth && clearStencil)) ? VK_IMAGE_LAYOUT_UNDEFINED
: trackedLayout;
return info;
}
VkRenderPassManager::VkRenderPassManager(VkDevice device,
const VulkanRendererConfig& config, VkClearManager& clearManager, VkTextureManager& textureManager,
SwapchainObject& swapchainObject):
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;
}
VkRenderPassManager::~VkRenderPassManager() {}
Bool VkRenderPassManager::Initialize() {
return true;
}
void VkRenderPassManager::Shutdown() {
DestroyDefaultFramebuffers();
for (auto& [_, target] : m_offscreenRenderTargets) {
DestroyOffscreenRenderTarget(target);
}
m_offscreenRenderTargets.clear();
}
if (m_device != VK_NULL_HANDLE) {
if (m_renderPassClear != VK_NULL_HANDLE) {
vkDestroyRenderPass(m_device, m_renderPassClear, nullptr);
VkRenderPassManager::HashType VkRenderPassManager::ComputeHash(
const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex, Bool includePendingClear) const {
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)));
Uint64 imageIdentity = 0;
auto* texture = att.GetTexture().get();
auto* resource = m_textureManager.SyncTextureAndGetDescriptor(*texture);
if (resource != nullptr) {
imageIdentity = reinterpret_cast<Uint64>(resource->image);
}
XXHASH_VERIFY(XXH64_update(m_hashState, &imageIdentity, sizeof(imageIdentity)));
}
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.mask, sizeof(clearPayload.mask)));
}
}
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* textureResource = m_textureManager.SyncTextureAndGetDescriptor(*texture);
if (textureResource != nullptr) {
currentLayout = textureResource->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;
m_renderPassLoad = VK_NULL_HANDLE;
m_colorFormat = VK_FORMAT_UNDEFINED;
m_depthStencilFormat = VK_FORMAT_UNDEFINED;
m_defaultExtent = {0, 0};
m_device = VK_NULL_HANDLE;
combineFramebufferAttachmentObjHash(FramebufferAttachmentType::Depth);
combineFramebufferAttachmentObjHash(FramebufferAttachmentType::Stencil);
return XXH64_digest(m_hashState);
}
Bool VkRenderPassManager::RecreateDefaultFramebuffers(const Vector<VkImageView>& colorViews,
const Vector<VkImageView>& depthStencilViews,
VkExtent2D extent) {
DestroyDefaultFramebuffers();
RenderPassEntry& VkRenderPassManager::GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo,
Uint32 swapchainImageIndex) {
auto hasPendingClearOnFramebuffer = [&]() -> Bool {
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 ||
extent.height == 0 || colorViews.empty() || colorViews.size() != depthStencilViews.size()) {
return false;
}
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;
const auto& att = fbo.GetAttachment(attachment);
if (att.IsTexture() && m_clearManager.HasPendingClear(att.GetTexture().get())) {
return true;
}
}
VkImageView attachments[] = {colorViews[i], depthStencilViews[i]};
VkFramebufferCreateInfo createInfo{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
createInfo.renderPass = m_renderPassLoad;
createInfo.attachmentCount = static_cast<Uint32>(std::size(attachments));
createInfo.pAttachments = attachments;
createInfo.width = extent.width;
createInfo.height = extent.height;
createInfo.layers = 1;
VkFramebuffer framebuffer = VK_NULL_HANDLE;
if (vkCreateFramebuffer(m_device, &createInfo, nullptr, &framebuffer) != VK_SUCCESS) {
DestroyDefaultFramebuffers();
return false;
const auto& depthAtt = fbo.GetAttachment(FramebufferAttachmentType::Depth);
if (depthAtt.IsTexture() && m_clearManager.HasPendingClear(depthAtt.GetTexture().get())) {
return true;
}
const auto& stencilAtt = fbo.GetAttachment(FramebufferAttachmentType::Stencil);
if (stencilAtt.IsTexture() && m_clearManager.HasPendingClear(stencilAtt.GetTexture().get())) {
return true;
}
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;
}
outRenderPass = m_renderPassLoad;
outFramebuffer = m_defaultFramebuffers[imageIndex];
outExtent = m_defaultExtent;
outDepthStencilFormat = m_depthStencilFormat;
return outFramebuffer != VK_NULL_HANDLE;
}
};
Bool VkRenderPassManager::EnsureOffscreenRenderTarget(const OffscreenRenderTargetInfo& targetInfo) {
if (m_device == VK_NULL_HANDLE || targetInfo.colorView == VK_NULL_HANDLE ||
targetInfo.colorFormat == VK_FORMAT_UNDEFINED || targetInfo.extent.width == 0 ||
targetInfo.extent.height == 0) {
return false;
// retrieve from cache first
auto* activeRenderPass = GetActiveRenderPass();
auto compatibilityHash = ComputeHash(fbo, swapchainImageIndex, false);
if (activeRenderPass != nullptr &&
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();
const Uint32 colorAttachmentSlotCount = static_cast<Uint32>(drawbufs.size());
Int width = 0;
Int height = 0;
Vector<VkAttachmentDescription> attachmentDescriptions;
attachmentDescriptions.reserve(colorAttachmentSlotCount + 1);
// Keep the full GL draw buffer slot span so fragment outputs targeting GL_NONE map to VK_ATTACHMENT_UNUSED.
Vector<VkAttachmentReference> colorAttachmentRefs(colorAttachmentSlotCount);
for (auto& attachmentRef : colorAttachmentRefs) {
attachmentRef.attachment = VK_ATTACHMENT_UNUSED;
attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
Vector<PendingClearAttachmentInfo> pendingClearAttachments;
pendingClearAttachments.reserve(colorAttachmentSlotCount + 1);
Vector<TrackedAttachmentLayoutInfo> trackedAttachmentLayouts;
trackedAttachmentLayouts.reserve(colorAttachmentSlotCount + 1);
auto& textureResources = RenderPassEntry::s_textureResourcesScratch;
textureResources.clear();
textureResources.reserve(colorAttachmentSlotCount + 1);
Vector<VkImageView> attachmentViews;
attachmentViews.reserve(colorAttachmentSlotCount + 1);
// This should automatically work on default & offscreen FBO
// assuming default FBO has the right param
for (Uint32 i = 0; i < colorAttachmentSlotCount; ++i) {
auto drawbuf = drawbufs[i];
auto* texture = ResolveCompleteColorAttachmentTexture(fbo, drawbuf, i);
if (texture == nullptr)
continue;
auto& att = fbo.GetAttachment(drawbuf);
const Uint32 attachmentMipLevel = static_cast<Uint32>(std::max(att.GetTextureLevel(), 0));
const auto textureTarget = texture->GetTarget();
const Uint32 attachmentIndex = static_cast<Uint32>(attachmentDescriptions.size());
attachmentDescriptions.emplace_back();
// Color attachment description
VkAttachmentDescription& desc = attachmentDescriptions.back();
switch (textureTarget) {
case TextureTarget::Texture2D: {
auto* texture2d =
static_cast<MG_State::GLState::TextureObject2D*>(texture);
desc.flags = 0;
desc.format = isDefaultFbo ?
m_swapchainObject.GetSurfaceFormat().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 = attachmentIndex,
.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,
});
textureResources.emplace_back(nullptr);
attachmentViews.emplace_back(swapchainViews[swapchainImageIndex]);
} else {
auto* textureResource = m_textureManager.SyncTextureAndGetDescriptor(*texture);
MOBILEGL_ASSERT(textureResource,
"GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at color attachment %d", i);
textureResources.emplace_back(textureResource);
desc.format = textureResource->format;
trackedColorLayout = textureResource->layout;
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
.target = TrackedAttachmentTarget::Texture,
.texture = texture,
.textureMipLevel = attachmentMipLevel,
.finalLayout = desc.finalLayout,
});
attachmentViews.emplace_back(m_textureManager.GetOrCreateViewAtMipLevel(*texture, attachmentMipLevel));
MOBILEGL_ASSERT(attachmentViews.back() != 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 = attachmentIndex;
attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
const Bool hasDepthStencil =
targetInfo.depthStencilView != VK_NULL_HANDLE && targetInfo.depthStencilFormat != VK_FORMAT_UNDEFINED;
auto& target = m_offscreenRenderTargets[targetInfo.targetExternalIndex];
if (target.framebuffer != VK_NULL_HANDLE && target.renderPassLoad != VK_NULL_HANDLE &&
target.targetVersion == targetInfo.targetVersion && target.colorView == targetInfo.colorView &&
target.colorFormat == targetInfo.colorFormat && target.depthStencilView == targetInfo.depthStencilView &&
target.depthStencilFormat == targetInfo.depthStencilFormat &&
target.extent.width == targetInfo.extent.width && target.extent.height == targetInfo.extent.height) {
return true;
// Depth attachment description
auto& depthAtt = fbo.GetAttachment(FramebufferAttachmentType::Depth);
VkAttachmentDescription depthAttachmentDescription;
VkAttachmentReference depthAttachmentRef;
depthAttachmentRef.attachment = VK_ATTACHMENT_UNUSED;
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
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.mask & GL_DEPTH_BUFFER_BIT) != 0;
Bool clearStencil = hasClear && (clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0;
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 = isDefaultFbo ?
m_swapchainObject.GetDepthStencilFormat() :
MG_Util::ConvertTextureInternalFormatToVkEnum(texture.GetFormat());
if (!isDefaultFbo) {
depthAttachmentDescription.format = depthTextureResource->format;
}
depthAttachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT;
const auto loadInfo =
ResolveDepthStencilAttachmentLoadInfo(trackedDepthLayout, clearDepth, clearStencil);
depthAttachmentDescription.loadOp = loadInfo.depthLoadOp;
depthAttachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depthAttachmentDescription.stencilLoadOp = loadInfo.stencilLoadOp;
depthAttachmentDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
depthAttachmentDescription.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthAttachmentDescription.initialLayout = loadInfo.initialLayout;
if (trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED && (!clearDepth || !clearStencil)) {
MGLOG_W("GetOrCreateRenderPass: depth/stencil attachment textureId=%d starts with undefined layout "
"and partial/no clear; using DONT_CARE for uncleared aspects",
texture.GetExternalIndex());
}
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(depthTextureResource->layout != VK_IMAGE_LAYOUT_UNDEFINED ||
depthAttachmentDescription.loadOp != VK_ATTACHMENT_LOAD_OP_LOAD,
"GetOrCreateRenderPass: depth attachment textureId=%d has undefined tracked layout with LOAD_OP_LOAD",
texture.GetExternalIndex());
MOBILEGL_ASSERT(depthTextureResource->layout != VK_IMAGE_LAYOUT_UNDEFINED ||
depthAttachmentDescription.stencilLoadOp != VK_ATTACHMENT_LOAD_OP_LOAD,
"GetOrCreateRenderPass: stencil attachment textureId=%d has undefined tracked layout with LOAD_OP_LOAD",
texture.GetExternalIndex());
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
.target = TrackedAttachmentTarget::Texture,
.texture = &texture,
.textureMipLevel = attachmentMipLevel,
.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;
}
const Bool hasDepthStencilAttachment = depthAttachmentRef.attachment != VK_ATTACHMENT_UNUSED;
DestroyOffscreenRenderTarget(target);
// Subpass
VkSubpassDescription subpassDesc;
subpassDesc.flags = 0;
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc.inputAttachmentCount = 0;
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;
const VkFormat depthStencilFormat = hasDepthStencil ? targetInfo.depthStencilFormat : VK_FORMAT_UNDEFINED;
target.renderPassLoad = CreateRenderPass(targetInfo.colorFormat, depthStencilFormat, VK_ATTACHMENT_LOAD_OP_LOAD,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
if (target.renderPassLoad == VK_NULL_HANDLE) {
return false;
}
Array<VkImageView, 2> attachments{};
attachments[0] = targetInfo.colorView;
Uint32 attachmentCount = 1;
if (hasDepthStencil) {
attachments[1] = targetInfo.depthStencilView;
attachmentCount = 2;
}
VkFramebufferCreateInfo framebufferInfo{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
framebufferInfo.renderPass = target.renderPassLoad;
framebufferInfo.attachmentCount = attachmentCount;
framebufferInfo.pAttachments = attachments.data();
framebufferInfo.width = targetInfo.extent.width;
framebufferInfo.height = targetInfo.extent.height;
framebufferInfo.layers = 1;
VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferInfo, nullptr, &target.framebuffer),
"vkCreateFramebuffer(offscreen)");
target.targetVersion = targetInfo.targetVersion;
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;
// Render Pass
VkRenderPassCreateInfo renderPassCreateInfo;
renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassCreateInfo.pNext = VK_NULL_HANDLE;
renderPassCreateInfo.flags = 0;
renderPassCreateInfo.attachmentCount = attachmentDescriptions.size();
renderPassCreateInfo.pAttachments = attachmentDescriptions.data();
renderPassCreateInfo.subpassCount = 1;
renderPassCreateInfo.pSubpasses = &subpassDesc;
renderPassCreateInfo.dependencyCount = 0;
renderPassCreateInfo.pDependencies = nullptr;
VkRenderPass renderPass = VK_NULL_HANDLE;
VK_VERIFY(vkCreateRenderPass(m_device, &createInfo, nullptr, &renderPass), "vkCreateRenderPass");
return renderPass;
VK_VERIFY(vkCreateRenderPass(m_device, &renderPassCreateInfo, nullptr, &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()),
static_cast<Uint32>(colorAttachmentRefs.size()),
hasDepthStencilAttachment,
extent,
1 };
MGLOG_D("VkRenderPassManager::GetOrCreateRenderPass: hash=0x%llx compatibilityHash=0x%llx attachmentCount=%u colorAttachmentCount=%u extent=%dx%d",
static_cast<unsigned long long>(hash),
static_cast<unsigned long long>(compatibilityHash),
renderPassEntry.attachmentCount,
renderPassEntry.colorAttachmentCount,
extent.x(),
extent.y());
auto [insertedIt, _] = m_renderPasses.emplace(hash, Move(renderPassEntry));
return insertedIt->second;
}
void VkRenderPassManager::DestroyDefaultFramebuffers() {
for (auto framebuffer : m_defaultFramebuffers) {
if (framebuffer != VK_NULL_HANDLE) {
vkDestroyFramebuffer(m_device, framebuffer, nullptr);
Bool VkRenderPassManager::BeginRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry) {
// TODO: Transition all the attachments into proper layout before starting the render pass
VkRenderPassBeginInfo renderPassBeginInfo;
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.mask & GL_COLOR_BUFFER_BIT) != 0) {
clearValues[pending.attachmentIndex].color = {
clearPayload.color.x(),
clearPayload.color.y(),
clearPayload.color.z(),
ResolveColorClearAlpha(pending.texture, clearPayload.color.w())
};
}
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
clearValues[pending.attachmentIndex].depthStencil.depth = clearPayload.depth;
}
if ((clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0) {
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) {
if (target.framebuffer != VK_NULL_HANDLE) {
vkDestroyFramebuffer(m_device, target.framebuffer, nullptr);
target.framebuffer = VK_NULL_HANDLE;
Bool VkRenderPassManager::EndRenderPass(VkCommandBuffer commandBuffer) {
auto* activeRenderPass = GetActiveRenderPass();
vkCmdEndRenderPass(commandBuffer);
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->UpdateTrackedImageLayoutAfterAttachmentWrite(
commandBuffer,
trackedAttachment.texture,
trackedAttachment.textureMipLevel,
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) {
vkDestroyRenderPass(m_device, target.renderPassLoad, nullptr);
target.renderPassLoad = VK_NULL_HANDLE;
}
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};
s_activeRenderPass = {};
s_hasActiveRenderPass = false;
return true;
}
Bool VkRenderPassManager::HasStencilComponent(VkFormat format) {
return format == VK_FORMAT_D24_UNORM_S8_UINT || format == VK_FORMAT_D32_SFLOAT_S8_UINT;
ActiveRenderPassInfo* VkRenderPassManager::GetActiveRenderPass() {
return s_hasActiveRenderPass ? &s_activeRenderPass : nullptr;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -8,79 +8,162 @@
#pragma once
#include "SwapchainObject.h"
#include "VkClearManager.h"
#include "VkTextureManager.h"
#include "../VkIncludes.h"
#include "../VulkanRendererConfig.h"
#include "MG_State/GLState/FramebufferState/FramebufferObject.h"
#include <Includes.h>
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 textureMipLevel = 0;
Uint32 swapchainImageIndex = 0;
VkImageLayout finalLayout = VK_IMAGE_LAYOUT_UNDEFINED;
};
struct DepthStencilAttachmentLoadInfo {
VkAttachmentLoadOp depthLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
VkAttachmentLoadOp stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
VkImageLayout initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
};
DepthStencilAttachmentLoadInfo ResolveDepthStencilAttachmentLoadInfo(
VkImageLayout trackedLayout, Bool clearDepth, Bool clearStencil);
struct RenderPassEntry {
static inline VkDevice s_device;
static inline Vector<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;
Uint32 colorAttachmentCount = 0;
Bool hasDepthStencilAttachment = false;
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(colorAttachmentCount, that.colorAttachmentCount);
std::swap(hasDepthStencilAttachment, that.hasDepthStencilAttachment);
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,
Uint32 colorAttachmentCount,
Bool hasDepthStencilAttachment,
IntVec2 extent, int subpass):
hash(hash),
renderPass(renderpass),
framebuffer(framebuffer),
compatibilityHash(compatibilityHash),
pendingClearAttachments(Move(pendingClearAttachments)),
trackedAttachmentLayouts(Move(trackedAttachmentLayouts)),
attachmentCount(attachmentCount),
colorAttachmentCount(colorAttachmentCount),
hasDepthStencilAttachment(hasDepthStencilAttachment),
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 {
public:
struct InitInfo {
VkDevice device = VK_NULL_HANDLE;
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
VkFormat depthStencilFormat = VK_FORMAT_UNDEFINED;
};
using HashType = Uint64;
VkRenderPassManager(VkDevice device,
const VulkanRendererConfig& config, VkClearManager& clearManager, VkTextureManager& textureManager,
SwapchainObject& swapchainObject);
~VkRenderPassManager();
struct OffscreenRenderTargetInfo {
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);
Bool Initialize();
void Shutdown();
Bool RecreateDefaultFramebuffers(const Vector<VkImageView>& colorViews,
const Vector<VkImageView>& depthStencilViews, VkExtent2D extent);
Bool GetDefaultRenderTarget(Uint32 imageIndex, VkRenderPass& outRenderPass, VkFramebuffer& outFramebuffer,
VkExtent2D& outExtent, VkFormat& outDepthStencilFormat) const;
Bool EnsureOffscreenRenderTarget(const OffscreenRenderTargetInfo& targetInfo);
Bool GetOffscreenRenderTarget(Uint targetExternalIndex, VkRenderPass& outRenderPass,
VkFramebuffer& outFramebuffer, VkExtent2D& outExtent,
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;
HashType ComputeHash(
const MG_State::GLState::FramebufferObject& fbo,
Uint32 swapchainImageIndex,
Bool includePendingClear = true) const;
RenderPassEntry& GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex);
static Bool BeginRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry);
static Bool EndRenderPass(VkCommandBuffer commandBuffer);
static ActiveRenderPassInfo* GetActiveRenderPass();
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;
VkFormat m_colorFormat = VK_FORMAT_UNDEFINED;
VkFormat m_depthStencilFormat = VK_FORMAT_UNDEFINED;
VkRenderPass m_renderPassLoad = VK_NULL_HANDLE;
VkRenderPass m_renderPassClear = VK_NULL_HANDLE;
Vector<VkFramebuffer> m_defaultFramebuffers;
VkExtent2D m_defaultExtent = {0, 0};
UnorderedMap<Uint, OffscreenRenderTarget> m_offscreenRenderTargets;
const VulkanRendererConfig& m_config;
VkClearManager& m_clearManager;
VkTextureManager& m_textureManager;
SwapchainObject& m_swapchainObject;
UnorderedMap<Uint64, RenderPassEntry> m_renderPasses;
static inline XXH64_state_t* m_hashState = XXH64_createState();
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
@@ -0,0 +1,247 @@
// 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"
#include <algorithm>
#include <cmath>
namespace MobileGL::MG_Backend::DirectVulkan {
namespace {
Bool UsesBorderColor(const MG_State::GLState::SamplerObject& sampler) {
return sampler.GetWrapS() == SamplerWrapMode::ClampToBorder ||
sampler.GetWrapT() == SamplerWrapMode::ClampToBorder ||
sampler.GetWrapR() == SamplerWrapMode::ClampToBorder;
}
Bool IsDepthTextureFormat(TextureInternalFormat format) {
switch (format) {
case TextureInternalFormat::DepthComponent:
case TextureInternalFormat::DepthComponent16:
case TextureInternalFormat::DepthComponent24:
case TextureInternalFormat::DepthComponent32:
case TextureInternalFormat::DepthComponent32F:
case TextureInternalFormat::Depth24Stencil8:
case TextureInternalFormat::Depth32FStencil8:
case TextureInternalFormat::DepthStencil:
return true;
default:
return false;
}
}
Bool NearlyEqual(Float lhs, Float rhs) {
return std::fabs(lhs - rhs) <= 1e-6f;
}
Float ResolveEffectiveMaxLod(const MG_State::GLState::SamplerObject& sampler) {
if (sampler.GetMipmapMode() == SamplerMipmapMode::None) {
return 0.0f;
}
return sampler.GetMaxLod();
}
Float ResolveEffectiveMinLod(const MG_State::GLState::SamplerObject& sampler, Float effectiveMaxLod) {
return std::min(sampler.GetMinLod(), effectiveMaxLod);
}
} // namespace
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 MG_State::GLState::ITextureObject& texture) 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 maxLod = ResolveEffectiveMaxLod(sampler);
const auto minLod = ResolveEffectiveMinLod(sampler, maxLod);
XXHASH_VERIFY(XXH64_update(m_hashState, &minLod, sizeof(minLod)));
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 = ResolveCompareFunc(sampler, texture);
XXHASH_VERIFY(XXH64_update(m_hashState, &compareFunc, sizeof(compareFunc)));
const auto borderColor = ResolveVkBorderColor(sampler, texture);
XXHASH_VERIFY(XXH64_update(m_hashState, &borderColor, sizeof(borderColor)));
return XXH64_digest(m_hashState);
}
VkSampler VkSamplerManager::GetOrCreateSampler(const MG_State::GLState::SamplerObject& sampler,
const MG_State::GLState::ITextureObject& texture) {
const Uint64 key = BuildSamplerKey(sampler, texture);
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(ResolveCompareFunc(sampler, texture));
samplerInfo.maxLod = ResolveEffectiveMaxLod(sampler);
samplerInfo.minLod = ResolveEffectiveMinLod(sampler, samplerInfo.maxLod);
samplerInfo.borderColor = ResolveVkBorderColor(sampler, texture);
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;
}
}
SamplerCompareFunc VkSamplerManager::ResolveCompareFunc(const MG_State::GLState::SamplerObject& sampler,
const MG_State::GLState::ITextureObject& texture) {
const auto compareFunc = sampler.GetSamplerCompareFunc();
if (sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture &&
IsDepthTextureFormat(texture.GetFormat()) && compareFunc == SamplerCompareFunc::Always) {
return SamplerCompareFunc::LessEqual;
}
return compareFunc;
}
VkBorderColor VkSamplerManager::ResolveVkBorderColor(const MG_State::GLState::SamplerObject& sampler,
const MG_State::GLState::ITextureObject& texture) {
if (!UsesBorderColor(sampler)) {
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
}
const auto& borderColor = texture.GetBorderColor();
const Bool isDepthTexture = IsDepthTextureFormat(texture.GetFormat());
if (isDepthTexture) {
if (NearlyEqual(borderColor.x(), 1.0f)) {
return VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
}
if (NearlyEqual(borderColor.x(), 0.0f)) {
return VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
}
}
const Bool rgbZero = NearlyEqual(borderColor.x(), 0.0f) && NearlyEqual(borderColor.y(), 0.0f) &&
NearlyEqual(borderColor.z(), 0.0f);
if (rgbZero && NearlyEqual(borderColor.w(), 0.0f)) {
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
}
if (rgbZero && NearlyEqual(borderColor.w(), 1.0f)) {
return VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
}
if (NearlyEqual(borderColor.x(), 1.0f) && NearlyEqual(borderColor.y(), 1.0f) &&
NearlyEqual(borderColor.z(), 1.0f) && NearlyEqual(borderColor.w(), 1.0f)) {
return VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
}
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,58 @@
// 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;
class ITextureObject;
}
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,
const MG_State::GLState::ITextureObject& texture);
private:
struct SamplerCacheEntry {
VkSampler handle = VK_NULL_HANDLE;
Uint externalIndex = 0;
Uint16 version = 0;
};
Uint64 BuildSamplerKey(const MG_State::GLState::SamplerObject& sampler,
const MG_State::GLState::ITextureObject& texture) const;
static VkFilter ToVkFilter(SamplerFilterMode mode);
static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode);
static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode);
static VkCompareOp ToVkCompareOp(SamplerCompareFunc func);
static SamplerCompareFunc ResolveCompareFunc(const MG_State::GLState::SamplerObject& sampler,
const MG_State::GLState::ITextureObject& texture);
static VkBorderColor ResolveVkBorderColor(const MG_State::GLState::SamplerObject& sampler,
const MG_State::GLState::ITextureObject& texture);
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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,189 @@
// 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;
Uint32 frameCount = 0;
};
struct TextureResource {
VkImage image = VK_NULL_HANDLE;
VmaAllocation allocation = nullptr;
VkImageView fullView = VK_NULL_HANDLE;
VkImageView sampledView = VK_NULL_HANDLE;
Vector<VkImageView> perMipViews;
Vector<VkImageView> perMipSampledViews;
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkExtent2D extent = {0, 0};
Uint32 depth = 1;
Uint32 arrayLayers = 1;
Uint32 mipLevels = 1;
Uint32 sampledBaseMipLevel = 0;
Uint32 sampledLevelCount = 1;
VkFormat format = VK_FORMAT_UNDEFINED;
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_NONE;
VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_2D;
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->sampledView, that.sampledView);
std::swap(this->perMipViews, that.perMipViews);
std::swap(this->perMipSampledViews, that.perMipSampledViews);
std::swap(this->layout, that.layout);
std::swap(this->extent, that.extent);
std::swap(this->depth, that.depth);
std::swap(this->arrayLayers, that.arrayLayers);
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->viewType, that.viewType);
std::swap(this->syncedTextureParamsVersion, that.syncedTextureParamsVersion);
}
void Reset() {
if (fullView != VK_NULL_HANDLE) {
vkDestroyImageView(s_device, fullView, nullptr);
}
if (sampledView != VK_NULL_HANDLE) {
vkDestroyImageView(s_device, sampledView, nullptr);
}
for (const auto attachmentView : perMipViews) {
if (attachmentView != VK_NULL_HANDLE) {
vkDestroyImageView(s_device, attachmentView, nullptr);
}
}
for (const auto sampledView : perMipSampledViews) {
if (sampledView != VK_NULL_HANDLE) {
vkDestroyImageView(s_device, sampledView, nullptr);
}
}
if (image != VK_NULL_HANDLE && allocation != nullptr) {
vmaDestroyImage(s_allocator, image, allocation);
}
fullView = VK_NULL_HANDLE;
sampledView = VK_NULL_HANDLE;
perMipViews.clear();
perMipSampledViews.clear();
image = VK_NULL_HANDLE;
allocation = nullptr;
layout = VK_IMAGE_LAYOUT_UNDEFINED;
extent = {0, 0};
depth = 1;
arrayLayers = 1;
mipLevels = 1;
sampledBaseMipLevel = 0;
sampledLevelCount = 1;
format = VK_FORMAT_UNDEFINED;
aspect = VK_IMAGE_ASPECT_NONE;
viewType = VK_IMAGE_VIEW_TYPE_2D;
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();
void BeginFrame(Uint32 frameIndex);
TextureResource* SyncTextureAndGetDescriptor(
MG_State::GLState::ITextureObject& texture);
VkImageView GetOrCreateViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel);
VkImageView GetOrCreateSampledViewAtMipLevel(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel);
void UpdateTrackedImageLayout(MG_State::GLState::ITextureObject* texture, VkImageLayout newLayout);
void UpdateTrackedImageLayoutAfterAttachmentWrite(VkCommandBuffer commandBuffer,
MG_State::GLState::ITextureObject* texture,
Uint32 writtenMipLevel,
VkImageLayout newLayout);
Bool TransitionTextureForSampling(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture);
Bool TransitionTextureForStorageImage(VkCommandBuffer commandBuffer, MG_State::GLState::ITextureObject& texture);
static VkImageAspectFlags ResolveSampledImageViewAspectMask(VkImageAspectFlags imageAspect);
static Bool TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout& trackedLayout,
VkImageLayout newLayout, VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask,
VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask,
Uint32 baseMipLevel = 0, Uint32 levelCount = 1,
Uint32 layerCount = 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,
VkImageViewType viewType, Uint32 baseMipLevel, Uint32 levelCount,
Uint32 layerCount,
const VkComponentMapping* components = nullptr) 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);
void DeferResourceRelease(TextureResource&& resource);
void DeferViewRelease(VkImageView view);
void CollectDeferredReleases(Uint32 frameIndex);
void DestroyDeferredReleases();
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;
Uint32 m_currentFrameIndex = 0;
Uint8 m_gcCounter = 0;
UnorderedMap<MG_State::GLState::ITextureObject*, WeakPtr<MG_State::GLState::ITextureObject>> m_aliveObjects;
UnorderedMap<MG_State::GLState::ITextureObject*, TextureResource> m_textureResources;
Vector<Vector<TextureResource>> m_deferredReleases;
Vector<Vector<VkImageView>> m_deferredViewReleases;
};
} // 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 "ProgramFactory.h"
#include "SwapchainObject.h"
#include "UniformDescriptorBinder.h"
#include "UniformManager.h"
#include "VertexInputStateFactory.h"
#include "VkBufferObject.h"
#include "VkFramebufferManager.h"
#include "VkBufferManager.h"
#include "VkClearManager.h"
#include "VkRenderPassManager.h"
#include "VkTextureSamplerManager.h"
#include "VkSamplerManager.h"
#include "VkTextureManager.h"
#include "MG_Util/Math/VectorTypes.h"
#include <Includes.h>
#include <vk_mem_alloc.h>
@@ -27,23 +29,61 @@
namespace MobileGL::MG_State::GLState {
class FramebufferObject;
class ProgramObject;
class SamplerObject;
class VertexArrayObject;
} // namespace MobileGL::MG_State::GLState
namespace MobileGL::MG_Backend::DirectVulkan {
struct DrawArrayPayload {
GLenum mode = GL_TRIANGLES;
GLint first = 0;
GLsizei count = 0;
const MG_State::GLState::ProgramObject* program = nullptr;
const MG_State::GLState::VertexArrayObject* vertexArray = nullptr;
enum class DrawSetupAspect: Uint8 {
FramebufferObject = 1 << 0,
VertexArrayObject = 1 << 1,
UniformBuffer = 1 << 2,
VertexBuffer = 1 << 3,
IndexBuffer = 1 << 4,
IndirectDrawBuffer = 1 << 5,
Viewport = 1 << 6,
Scissor = 1 << 7,
};
struct DrawElementPayload {
DrawArrayPayload drawArray;
struct DrawCmdParam {
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;
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 {
@@ -69,35 +109,97 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void Initialize();
void Shutdown();
void RequestClear(GLbitfield mask, const FloatVec4& color, Float depth, Uint32 stencil,
Uint drawFboExternalIndex, Bool isDefaultFramebufferTarget);
Bool ConsumePendingColorClear(VkClearColorValue& outClearColor);
void EnsureFrameRecordingStarted();
void DrawArrays(const DrawArrayPayload& payload);
void DrawElements(const DrawElementPayload& payload);
void MultiDrawElements(const Vector<DrawElementPayload>& payloads);
Bool BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
GLint dstY1, GLbitfield mask, GLenum filter, Uint readFboExternalIndex,
Uint drawFboExternalIndex, Bool readIsDefaultFramebuffer, Bool drawIsDefaultFramebuffer);
void Render();
Bool SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags<DrawSetupAspect> aspects,
const DrawCmdParam& drawParams,
const IndexBufferView* pIndexBufferView = nullptr);
void ClearAttachmentsOnActiveRenderPass(VkCommandBuffer commandBuffer,
const RenderPassEntry& compatibleRenderPassEntry);
void Clear(GLbitfield mask);
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value);
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value);
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
void ClearNamedFramebufferfv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, const GLfloat* value);
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter);
void BlitNamedFramebuffer(const SharedPtr<MG_State::GLState::FramebufferObject>& readFbo,
const SharedPtr<MG_State::GLState::FramebufferObject>& drawFbo,
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter);
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
GLint x, GLint y, GLsizei width, GLsizei height);
void GenerateMipmap(GLenum target);
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels);
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels);
void GetTextureImage(const SharedPtr<MG_State::GLState::ITextureObject>& texture,
TextureUploadTarget uploadTarget, GLint level, GLenum format, GLenum type,
GLsizei bufSize, GLvoid* pixels);
void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
void DispatchComputeIndirect(GLintptr indirect);
void MemoryBarrier(GLbitfield barriers);
static VkMemoryBarrier BuildMemoryBarrierForGlBarriers(GLbitfield barriers);
void DrawArrays(const DrawCmd& payload);
void DrawElements(const DrawIndexedCmd& payload);
void MultiDrawElements(const MultiDrawIndexedCmd& payloads);
void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride);
void Present();
const PhysicalDevice& GetPhysicalDevice() const;
VkInstance GetInstance() const;
Bool IsDrawIndirectCountExtensionEnabled() const;
void RecreateSwapchain();
private:
struct PendingClearState {
GLbitfield mask = 0;
VkClearColorValue color = {{0.0f, 0.0f, 0.0f, 1.0f}};
Float depth = 1.0f;
Uint32 stencil = 0;
Uint drawFboExternalIndex = 0;
Bool targetsDefaultFramebuffer = true;
struct BlitUniformData {
float srcRect[4] = {0.f, 0.f, 1.f, 1.f};
float dstRect[4] = {0.f, 0.f, 1.f, 1.f};
Int surfaceTransform = 0;
Int padding[3] = {0, 0, 0};
};
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;
};
struct DepthMipmapResources {
SharedPtr<MG_State::GLState::ProgramObject> program;
Int srcRectLocation = -1;
Int dstRectLocation = -1;
Int surfaceTransformLocation = -1;
Int srcTexelSizeLocation = -1;
Uint32 samplerBinding = 0;
};
struct DeferredDepthMipmapCleanup {
Vector<VkImageView> imageViews;
Vector<VkFramebuffer> framebuffers;
Vector<VkRenderPass> renderPasses;
Vector<VkPipeline> pipelines;
};
void QueueClearBufferPayload(GLenum buffer, GLint drawbuffer, const ClearAttachmentPayload& clearPayload);
void QueueClearBufferPayloadForFramebuffer(const MG_State::GLState::FramebufferObject& framebuffer,
GLenum buffer, GLint drawbuffer,
const ClearAttachmentPayload& clearPayload);
NativeWindowType m_window = 0;
void* m_platformDisplay = nullptr;
void* m_platformLibrary = nullptr;
void* m_platformCloseDisplay = nullptr;
VulkanRendererConfig m_config;
// Vulkan objects
@@ -106,56 +208,48 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkInstance m_instance = VK_NULL_HANDLE;
VkDebugUtilsMessengerEXT m_debugMessenger = VK_NULL_HANDLE;
PhysicalDevice m_physicalDevice;
// VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
VkDevice m_device = VK_NULL_HANDLE;
VmaAllocator m_allocator = nullptr;
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
SwapchainObject m_swapchainObject;
// Vector<VkQueueFamilyProperties> m_queueFamilies;
// QueueFamilyIndices m_queueFamilyIndices;
VkQueue m_graphicsQueue = VK_NULL_HANDLE;
VkQueue m_presentQueue = VK_NULL_HANDLE;
Bool m_drawIndirectCountExtensionEnabled = false;
Bool m_indexTypeUint8ExtensionEnabled = false;
using PFNDrawIndexedIndirectCountFunc = void(VKAPI_PTR*)(VkCommandBuffer commandBuffer, VkBuffer buffer,
VkDeviceSize offset, VkBuffer countBuffer,
VkDeviceSize countBufferOffset, Uint32 maxDrawCount,
Uint32 stride);
PFNDrawIndexedIndirectCountFunc m_cmdDrawIndexedIndirectCount = nullptr;
static inline PFNDrawIndexedIndirectCountFunc s_vkCmdDrawIndexedIndirectCount = nullptr;
VkCommandPool m_commandPool = VK_NULL_HANDLE;
VkFormat m_depthStencilFormat = VK_FORMAT_UNDEFINED;
Vector<VkImage> m_depthStencilImages;
Vector<VkDeviceMemory> m_depthStencilImageMemories;
Vector<VkImageView> m_depthStencilImageViews;
Vector<VkImageLayout> m_depthStencilImageLayouts;
struct TransientBufferSliceCacheEntry {
BufferSlice slice;
Uint64 changeSerial = 0;
SizeT size = 0;
};
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;
VkBufferManager m_bufferManager;
UnorderedMap<const MG_State::GLState::BufferObject*, TransientBufferSliceCacheEntry>
m_transientVertexIndexBufferSlicesThisFrame;
Uint m_imageIndexAcquired = 0;
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;
UnorderedMap<ProgramFactory::HashType, VkPipeline> m_computePipelines;
UniquePtr<ProgramFactory> m_programFactory;
UniquePtr<UniformDescriptorBinder> m_uniformDescriptorBinder;
UniquePtr<UniformManager> m_uniformManager;
UniquePtr<VertexInputStateFactory> m_vertexInputStateFactory;
UniquePtr<VkFramebufferManager> m_framebufferManager;
UniquePtr<VkClearManager> m_clearManager;
UniquePtr<VkRenderPassManager> m_renderPassManager;
UniquePtr<VkTextureSamplerManager> m_textureSamplerManager;
UniquePtr<VkTextureManager> m_textureManager;
UniquePtr<VkSamplerManager> m_samplerManager;
BlitResources m_blitResources;
DepthMipmapResources m_depthMipmapResources;
Vector<DeferredDepthMipmapCleanup> m_deferredDepthMipmapCleanup;
void CreateInstance();
VkResult SetupDebugMessenger();
@@ -168,31 +262,50 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void DestroyAllocator();
void CreateSwapchain();
void CreateCommandPool();
void CreateFrameContexts();
void CreateDepthStencilResources();
void DestroyDepthStencilResources();
VkRenderPass GetDefaultLoadRenderPass() const;
Bool GetDefaultRenderTargetForCurrentImage(VkRenderPass& outRenderPass, VkFramebuffer& outFramebuffer,
VkExtent2D& outExtent, VkFormat& outDepthStencilFormat) const;
Bool EnsureOffscreenRenderTarget(Uint glFboExternalIndex, const MG_State::GLState::FramebufferObject& glFbo,
VkRenderPass& outRenderPass, VkFramebuffer& outFramebuffer,
VkExtent2D& outExtent, VkFormat& outDepthStencilFormat);
void PrepareDemoPipeline();
VkPipeline GetOrCreatePipeline(const MG_State::GLState::ProgramObject& program, VkPipelineLayout pipelineLayout,
Uint64 vertexInputHash,
const VkPipelineVertexInputStateCreateInfo& vertexInputState);
void TransitionSwapchainImageToColorAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex);
void TransitionDepthStencilImageToAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex);
void EndFrameRecordingIfNeeded();
void DeferDestroyBuffer(VkBufferObject& buffer);
void CollectDeferredBufferReleases(Uint32 frameIndex);
Bool EnsureFrameUploadBufferCapacity(Uint32 frameIndex, Bool isIndexBuffer, VkDeviceSize requiredEndOffset,
VkDeviceSize minCapacity, VkBufferUsageFlags usage);
Bool UploadAndBindVertexStreams(const VertexInputStateFactory::BackendVertexInputState& vertexInputState,
const DrawArrayPayload& payload, VkCommandBuffer commandBuffer);
VkPipeline GetOrCreatePipeline(
GLenum mode,
const MG_State::GLState::ProgramObject& program,
const ProgramFactory::VkProgramObject& programObj,
ProgramFactory::CompileOptionFlags transformFlags,
const MG_State::GLState::VertexArrayObject& vao,
const RenderPassEntry& renderPassEntry);
VkPipeline GetOrCreateComputePipeline(const ProgramFactory::VkProgramObject& programObj);
void DestroyComputePipelines();
Bool UploadAndBindVertexBuffers(VkCommandBuffer commandBuffer, const MG_State::GLState::VertexArrayObject& vao,
const DrawCmdParam& drawParams);
Bool UploadAndBindIndexBuffer(FrameContext::FrameData& frame,
const MG_State::GLState::VertexArrayObject& vao,
const IndexBufferView* pIndexBufferView = nullptr);
Bool InitializeBlitResources();
Bool InitializeDepthMipmapResources();
void ShutdownBlitResources();
void ShutdownDepthMipmapResources();
void CollectDeferredDepthMipmapCleanup(Uint32 frameIndex);
void DestroyDeferredDepthMipmapCleanup();
Bool TryBlitToDefaultFramebufferWithShader(FrameContext::FrameData& frame,
MG_State::GLState::FramebufferObject& readFbo,
MG_State::GLState::FramebufferObject& drawFbo,
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLenum filter);
Bool MaterializePendingClearForTexture(VkCommandBuffer commandBuffer,
MG_State::GLState::ITextureObject& texture);
VkPipeline GetOrCreateBlitPipeline(const RenderPassEntry& renderPassEntry);
Bool GenerateDepthMipmapWithShader(FrameContext::FrameData& frame,
MG_State::GLState::ITextureObject& texture,
VkTextureManager::TextureResource& resource,
Uint32 baseMipLevel,
Uint32 generateMipLevelCount,
const IntVec3& storageBaseTexelSize,
VkImageLayout originalLayout,
VkImageLayout finalLayout);
Bool SubmitReadbackCommandsAndWait(FrameContext::FrameData& frame);
void ShutdownSwapchain();
// Static functions
static Int GetPresentQueueFamilyIndex(const PhysicalDevice& physicalDevice, VkSurfaceKHR surface,
const Vector<VkQueueFamilyProperties>& queueFamilies,
Int preferredFamilyIndex = -1);
@@ -212,11 +325,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
static Bool GetMoreCapablePhysicalDevice(VkPhysicalDevice newVkDevice, VkSurfaceKHR surface,
const PhysicalDevice& compareWithDevice,
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_deviceExtensionNames[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
static Bool CheckValidationLayerSupport();
@@ -13,8 +13,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
struct VulkanRendererConfig {
Uint32 MaxFramesInFlight = 2;
String AppName = "MobileGL-VulkanRenderer";
Version Version = MG_Config::CoreVersion;
MobileGL::Version Version = MG_Config::CoreVersion;
Uint64 CacheVersion = MG_Config::CacheVersion;
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
Bool EnableValidationLayers = true;
#else
Bool EnableValidationLayers = false;
#endif
};
} // namespace MobileGL::MG_Backend::DirectVulkan
} // namespace MobileGL::MG_Backend::DirectVulkan
+40 -3
View File
@@ -20,7 +20,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
if (!MG_State::pEGLContext) {
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) {
@@ -34,11 +34,25 @@ namespace MobileGL::MG_Impl::EGLImpl {
MG_Backend::WindowBackend DetectWindowBackend() {
#if defined(ANDROID) || defined(__ANDROID__)
return MG_Backend::WindowBackend::Android;
#elif defined(__linux__)
return MG_Backend::WindowBackend::X11;
#else
return MG_Backend::WindowBackend::Unknown;
#endif
}
EGLint GetAttribValue(const EGLint* attribList, EGLint attrib, EGLint defaultValue) {
if (!attribList) {
return defaultValue;
}
for (SizeT i = 0; attribList[i] != EGL_NONE; i += 2) {
if (attribList[i] == attrib) {
return attribList[i + 1];
}
}
return defaultValue;
}
template <typename NativeType>
Bool IsNullNativeHandle(NativeType nativeHandle) {
if constexpr (std::is_pointer_v<NativeType>) {
@@ -230,7 +244,13 @@ namespace MobileGL::MG_Impl::EGLImpl {
if (!state) {
return EGL_FALSE;
}
return state->TerminateDisplay(dpy) ? EGL_TRUE : EGL_FALSE;
if (!state->TerminateDisplay(dpy)) {
return EGL_FALSE;
}
if (auto* backendObject = MG_Backend::pActiveBackendObject.get()) {
backendObject->ReleaseEGLResources();
}
return EGL_TRUE;
}
EGLBoolean ReleaseThread() {
@@ -330,7 +350,24 @@ namespace MobileGL::MG_Impl::EGLImpl {
if (!state) {
return EGL_NO_SURFACE;
}
return state->CreatePbufferSurface(dpy, config, attrib_list);
const EGLint width = GetAttribValue(attrib_list, EGL_WIDTH, 1);
const EGLint height = GetAttribValue(attrib_list, EGL_HEIGHT, 1);
EGLSurface surface = state->CreatePbufferSurface(dpy, config, attrib_list);
if (surface == EGL_NO_SURFACE) {
return EGL_NO_SURFACE;
}
auto* backendObject = GetBackendObject(state);
if (!backendObject) {
return EGL_NO_SURFACE;
}
if (!backendObject->CreateEGLPbufferSurface(width, height)) {
state->DestroySurface(dpy, surface);
state->SetError(EGL_BAD_ALLOC);
return EGL_NO_SURFACE;
}
return surface;
}
EGLBoolean BindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
File diff suppressed because it is too large Load Diff
+37 -20
View File
@@ -9,24 +9,41 @@
#pragma once
#include <Includes.h>
namespace MobileGL {
namespace MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
void GetBufferParameteriv(GLenum target, GLenum pname, GLint* params);
GLboolean IsBuffer(GLuint buffer);
void DeleteBuffers(GLsizei n, const GLuint* buffers);
void FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length);
GLboolean UnmapBuffer(GLenum target);
void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
void* MapBuffer(GLenum target, GLenum access);
void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset,
GLsizeiptr size);
void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
void BindBuffer(GLenum target, GLuint buffer);
void GenBuffers(GLsizei n, GLuint* buffers);
void BindBufferBase(GLenum target, GLuint index, GLuint buffer);
void BindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
namespace MobileGL::MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
void GetBufferParameteriv(GLenum target, GLenum pname, GLint* params);
void GetBufferParameteri64v(GLenum target, GLenum pname, GLint64* params);
void GetBufferPointerv(GLenum target, GLenum pname, void** params);
GLboolean IsBuffer(GLuint buffer);
void DeleteBuffers(GLsizei n, const GLuint* buffers);
void FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length);
GLboolean UnmapBuffer(GLenum target);
void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
void* MapBuffer(GLenum target, GLenum access);
void BufferStorage(GLenum target, GLsizeiptr size, const void* data, GLbitfield flags);
void CreateBuffers(GLsizei n, GLuint* buffers);
void NamedBufferStorage(GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags);
void NamedBufferData(GLuint buffer, GLsizeiptr size, const void* data, GLenum usage);
void NamedBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data);
void CopyNamedBufferSubData(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset,
GLsizeiptr size);
void ClearNamedBufferData(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data);
void ClearNamedBufferSubData(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format,
GLenum type, const void* data);
void* MapNamedBuffer(GLuint buffer, GLenum access);
void* MapNamedBufferRange(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access);
GLboolean UnmapNamedBuffer(GLuint buffer);
void FlushMappedNamedBufferRange(GLuint buffer, GLintptr offset, GLsizeiptr length);
void GetNamedBufferParameteriv(GLuint buffer, GLenum pname, GLint* params);
void GetNamedBufferParameteri64v(GLuint buffer, GLenum pname, GLint64* params);
void GetNamedBufferPointerv(GLuint buffer, GLenum pname, void** params);
void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset,
GLsizeiptr size);
void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
void BindBuffer(GLenum target, GLuint buffer);
void GenBuffers(GLsizei n, GLuint* buffers);
void BindBufferBase(GLenum target, GLuint index, GLuint buffer);
void BindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
} // namespace MobileGL::MG_Impl::GLImpl
+87 -92
View File
@@ -13,105 +13,100 @@
#include <MG_Util/Converters/MGToGL/BufferEnumConverter.h>
#include <MG_Util/Converters/MGToStr/BufferEnumConverter.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace BufferImpl {
Bool ValidateBufferTarget(BufferTarget target) {
if (target == BufferTarget::Unknown) {
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;
}
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;
namespace MobileGL::MG_Impl::GLImpl::BufferImpl {
Bool ValidateBufferTarget(BufferTarget target) {
if (target == BufferTarget::Unknown) {
using namespace MG_Util;
String bufferTargetStr = ConvertBufferTargetToString(target);
String glTargetStr = ConvertGLEnumToString(ConvertBufferTargetToGLEnum(target));
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferName",
std::format("Buffer name {} is not valid.", index)));
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl/BufferImpl", "ValidateBufferTarget",
std::format("Target {} ({}) is not valid.", bufferTargetStr, glTargetStr)));
return false;
}
Bool ValidateBufferUsage(BufferUsage usage) {
if (usage != BufferUsage::Unknown) {
return true;
}
if (target == BufferTarget::Index && MG_State::pGLContext->GetBoundVertexArray() == nullptr) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
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;
String bufferUsageStr = ConvertBufferUsageToString(usage);
String glUsageStr = ConvertGLEnumToString(ConvertBufferUsageToGLEnum(usage));
String bufferTargetStr = ConvertBufferTargetToString(target);
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(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>(
"MG_Impl/GLImpl/BufferImpl", "ValidateBufferUsage",
std::format("Usage {} ({}) is not one of the allowable values.", bufferUsageStr, glUsageStr)));
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferMappingAccess",
"Access bits cannot contain invalid flags."));
return false;
}
Bool ValidateBufferMappingAccess(Flags<BufferMappingAccessBit> accessBits) {
if (accessBits == BufferMappingAccessBit::Null) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
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
return true;
}
} // namespace MobileGL::MG_Impl::GLImpl::BufferImpl
+7 -9
View File
@@ -10,12 +10,10 @@
#include <Includes.h>
#include <MG_State/GLState/BufferState/BufferObject.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace BufferImpl {
Bool ValidateBufferTarget(BufferTarget target);
Bool ValidateBufferName(Uint index, Bool allowZero = false);
Bool ValidateBufferUsage(BufferUsage usage);
Bool ValidateBufferMappingAccess(Flags<BufferMappingAccessBit> accessBits);
Bool ValidateBufferBindingPointTarget(BufferTarget target);
} // namespace BufferImpl
} // namespace MobileGL::MG_Impl::GLImpl
namespace MobileGL::MG_Impl::GLImpl::BufferImpl {
Bool ValidateBufferTarget(BufferTarget target);
Bool ValidateBufferName(Uint index, Bool allowZero = false);
Bool ValidateBufferUsage(BufferUsage usage);
Bool ValidateBufferMappingAccess(Flags<BufferMappingAccessBit> accessBits);
Bool ValidateBufferBindingPointTarget(BufferTarget target);
} // namespace MobileGL::MG_Impl::GLImpl::BufferImpl
@@ -73,6 +73,24 @@ namespace MobileGL::MG_Impl::GLImpl {
MG_Backend::gBackendFunctionsTable.GL.MultiDrawArraysIndirect(mode, indirect, drawcount, stride);
}
void MultiDrawElementsIndirectCount_Backend(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride) {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
MG_Backend::gBackendFunctionsTable.GL.MultiDrawElementsIndirectCount(mode, type, indirect, drawcount,
maxdrawcount, stride);
}
void MultiDrawArraysIndirectCount_Backend(GLenum mode, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride) {
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
MG_Backend::gBackendFunctionsTable.GL.MultiDrawArraysIndirectCount(mode, indirect, drawcount, maxdrawcount,
stride);
}
void DrawRangeElementsBaseVertex_Backend(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices, GLint basevertex) {
#ifdef TRACY_ENABLE
@@ -156,6 +174,52 @@ namespace MobileGL::MG_Impl::GLImpl {
}
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ) {
auto dispatchCompute = MG_Backend::gBackendFunctionsTable.GL.DispatchCompute;
if (!dispatchCompute) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Backend does not support compute dispatch."));
return;
}
dispatchCompute(numGroupsX, numGroupsY, numGroupsZ);
}
void DispatchComputeIndirect(GLintptr indirect) {
auto dispatchComputeIndirect = MG_Backend::gBackendFunctionsTable.GL.DispatchComputeIndirect;
if (!dispatchComputeIndirect) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
"Backend does not support indirect compute dispatch."));
return;
}
dispatchComputeIndirect(indirect);
}
void MemoryBarrier(GLbitfield barriers) {
auto memoryBarrier = MG_Backend::gBackendFunctionsTable.GL.MemoryBarrier;
if (!memoryBarrier) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Backend does not support memory barriers."));
return;
}
memoryBarrier(barriers);
}
void MemoryBarrierByRegion(GLbitfield barriers) {
auto memoryBarrierByRegion = MG_Backend::gBackendFunctionsTable.GL.MemoryBarrierByRegion;
if (!memoryBarrierByRegion) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
"Backend does not support regional memory barriers."));
return;
}
memoryBarrierByRegion(barriers);
}
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {
MultiDrawElementsIndirect_Backend(mode, type, indirect, drawcount, stride);
}
@@ -164,6 +228,32 @@ namespace MobileGL::MG_Impl::GLImpl {
MultiDrawArraysIndirect_Backend(mode, indirect, drawcount, stride);
}
void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride) {
auto multiDrawElementsIndirectCount = MG_Backend::gBackendFunctionsTable.GL.MultiDrawElementsIndirectCount;
if (!multiDrawElementsIndirectCount) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
"Backend does not support indirect-parameter indexed draws."));
return;
}
MultiDrawElementsIndirectCount_Backend(mode, type, indirect, drawcount, maxdrawcount, stride);
}
void MultiDrawArraysIndirectCount(GLenum mode, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride) {
auto multiDrawArraysIndirectCount = MG_Backend::gBackendFunctionsTable.GL.MultiDrawArraysIndirectCount;
if (!multiDrawArraysIndirectCount) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
"Backend does not support indirect-parameter array draws."));
return;
}
MultiDrawArraysIndirectCount_Backend(mode, indirect, drawcount, maxdrawcount, stride);
}
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices, GLint basevertex) {
DrawRangeElementsBaseVertex_Backend(mode, start, end, count, type, indices, basevertex);
+36 -31
View File
@@ -9,34 +9,39 @@
#pragma once
#include <Includes.h>
namespace MobileGL {
namespace MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
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 DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices, GLint basevertex);
void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices);
void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex, GLuint baseinstance);
void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex);
void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLuint baseinstance);
void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount);
void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect);
void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
GLuint baseinstance);
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
void DrawArraysIndirect(GLenum mode, const void* indirect);
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex);
void DrawArrays(GLenum mode, GLint first, GLsizei count);
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
GLsizei drawcount);
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
GLsizei drawcount, const GLint* basevertex);
void Clear(GLbitfield mask);
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
namespace MobileGL::MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ);
void DispatchComputeIndirect(GLintptr indirect);
void MemoryBarrier(GLbitfield barriers);
void MemoryBarrierByRegion(GLbitfield barriers);
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride);
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride);
void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride);
void MultiDrawArraysIndirectCount(GLenum mode, const void* indirect, GLintptr drawcount,
GLsizei maxdrawcount, GLsizei stride);
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices, GLint basevertex);
void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices);
void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex, GLuint baseinstance);
void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex);
void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLuint baseinstance);
void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount);
void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect);
void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
GLuint baseinstance);
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
void DrawArraysIndirect(GLenum mode, const void* indirect);
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex);
void DrawArrays(GLenum mode, GLint first, GLsizei count);
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
GLsizei drawcount);
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
GLsizei drawcount, const GLint* basevertex);
void Clear(GLbitfield mask);
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
} // namespace MobileGL::MG_Impl::GLImpl
+236 -174
View File
@@ -10,6 +10,7 @@
#include "../Buffer/GL_Buffer.h"
#include "../Getter/GL_Getter.h"
#include "../Sampler/GL_Sampler.h"
#include "../Sync/GL_Sync.h"
#include "../Texture/GL_Texture.h"
#include "../Drawing/GL_Drawing.h"
#include "../Program/GL_Program.h"
@@ -17,42 +18,40 @@
#include "../Framebuffer/GL_Framebuffer.h"
#include "../VertexArray/GL_VertexArray.h"
#define DECLARE_GL_FUNCTION_STUB_HEAD(type,name,...) \
MOBILEGL_GL_API type gl##name(__VA_ARGS__) {
#define DECLARE_GL_FUNCTION_STUB_HEAD(type, name, ...) MOBILEGL_GL_API type gl##name(__VA_ARGS__) {
#define DECLARE_GL_FUNCTION_STUB_END(type,name,...) \
MGLOG_W("Stub function: %s(...)", __FUNCTION__); \
return (type)1; \
}
#define DECLARE_GL_FUNCTION_STUB_END(type, name, ...) \
MGLOG_W("Stub function: %s(...)", __FUNCTION__); \
return (type)1; \
}
#define DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(type,name,...) \
MGLOG_W("Stub function: %s(...)", __FUNCTION__); \
}
#define DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(type, name, ...) \
MGLOG_W("Stub function: %s(...)", __FUNCTION__); \
}
#define DECLARE_GL_FUNCTION_HEAD(type,name,...) \
MOBILEGL_GL_API type gl##name(__VA_ARGS__) {
#define DECLARE_GL_FUNCTION_HEAD(type, name, ...) MOBILEGL_GL_API type gl##name(__VA_ARGS__) {
#ifdef TRACY_ENABLE
#define DECLARE_GL_FUNCTION_END(type,name,...) \
ZoneScopedC(TRACY_ZONECOLOR_ENTRY); \
MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \
return MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \
#define DECLARE_GL_FUNCTION_END(type, name, ...) \
ZoneScopedC(TRACY_ZONECOLOR_ENTRY); \
MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \
return MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \
}
#define DECLARE_GL_FUNCTION_END_NO_RETURN(type,name,...) \
ZoneScopedC(TRACY_ZONECOLOR_ENTRY); \
MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \
MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \
#define DECLARE_GL_FUNCTION_END_NO_RETURN(type, name, ...) \
ZoneScopedC(TRACY_ZONECOLOR_ENTRY); \
MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \
MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \
}
#else
#define DECLARE_GL_FUNCTION_END(type,name,...) \
MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \
return MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \
#define DECLARE_GL_FUNCTION_END(type, name, ...) \
MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \
return MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \
}
#define DECLARE_GL_FUNCTION_END_NO_RETURN(type,name,...) \
MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \
MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \
#define DECLARE_GL_FUNCTION_END_NO_RETURN(type, name, ...) \
MGLOG_D("Implementing function: %s(...)", __FUNCTION__); \
MobileGL::MG_Impl::GLImpl::name(__VA_ARGS__); \
}
#endif
@@ -69,7 +68,7 @@ DECLARE_GL_FUNCTION_HEAD(void, BindRenderbuffer, GLenum target, GLuint renderbuf
DECLARE_GL_FUNCTION_HEAD(void, BindTexture, GLenum target, GLuint texture) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindTexture, target, texture)
DECLARE_GL_FUNCTION_HEAD(void, BlendColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendColor, red, green, blue, alpha)
DECLARE_GL_FUNCTION_HEAD(void, BlendEquation, GLenum mode) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendEquation, mode)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquationSeparate, GLenum modeRGB, GLenum modeAlpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendEquationSeparate, modeRGB, modeAlpha)
DECLARE_GL_FUNCTION_HEAD(void, BlendEquationSeparate, GLenum modeRGB, GLenum modeAlpha) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendEquationSeparate, modeRGB, modeAlpha)
DECLARE_GL_FUNCTION_HEAD(void, BlendFunc, GLenum sfactor, GLenum dfactor) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendFunc, sfactor, dfactor)
DECLARE_GL_FUNCTION_HEAD(void, BlendFuncSeparate, GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendFuncSeparate, sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha)
DECLARE_GL_FUNCTION_HEAD(void, BufferData, GLenum target, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BufferData, target, size, data, usage)
@@ -104,8 +103,8 @@ DECLARE_GL_FUNCTION_HEAD(void, DrawArrays, GLenum mode, GLint first, GLsizei cou
DECLARE_GL_FUNCTION_HEAD(void, DrawElements, GLenum mode, GLsizei count, GLenum type, const void* indices) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawElements, mode, count, type, indices)
DECLARE_GL_FUNCTION_HEAD(void, Enable, GLenum cap) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Enable, cap)
DECLARE_GL_FUNCTION_HEAD(void, EnableVertexAttribArray, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, EnableVertexAttribArray, index)
DECLARE_GL_FUNCTION_STUB_HEAD(void, Finish) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Finish)
DECLARE_GL_FUNCTION_STUB_HEAD(void, Flush) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Flush)
MOBILEGL_GL_API void glFinish() { MGLOG_D("Implementing function: %s(...)", __FUNCTION__); }
MOBILEGL_GL_API void glFlush() { MGLOG_D("Implementing function: %s(...)", __FUNCTION__); }
DECLARE_GL_FUNCTION_HEAD(void, FramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FramebufferRenderbuffer, target, attachment, renderbuffertarget, renderbuffer)
DECLARE_GL_FUNCTION_HEAD(void, FramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FramebufferTexture2D, target, attachment, textarget, texture, level)
DECLARE_GL_FUNCTION_HEAD(void, FrontFace, GLenum mode) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FrontFace, mode)
@@ -216,7 +215,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, EndQuery, GLenum target) DECLARE_GL_FUNCTION
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryiv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryiv, target, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryObjectuiv, GLuint id, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryObjectuiv, id, pname, params)
DECLARE_GL_FUNCTION_HEAD(GLboolean, UnmapBuffer, GLenum target) DECLARE_GL_FUNCTION_END(GLboolean, UnmapBuffer, target)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBufferPointerv, GLenum target, GLenum pname, void** params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBufferPointerv, target, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, GetBufferPointerv, GLenum target, GLenum pname, void** params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBufferPointerv, target, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, DrawBuffers, GLsizei n, const GLenum* bufs) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawBuffers, n, bufs)
DECLARE_GL_FUNCTION_STUB_HEAD(void, UniformMatrix2x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, UniformMatrix2x3fv, location, count, transpose, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, UniformMatrix3x2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, UniformMatrix3x2fv, location, count, transpose, value)
@@ -233,7 +232,7 @@ DECLARE_GL_FUNCTION_HEAD(void, BindVertexArray, GLuint array) DECLARE_GL_FUNCTIO
DECLARE_GL_FUNCTION_HEAD(void, DeleteVertexArrays, GLsizei n, const GLuint* arrays) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteVertexArrays, n, arrays)
DECLARE_GL_FUNCTION_HEAD(void, GenVertexArrays, GLsizei n, GLuint* arrays) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenVertexArrays, n, arrays)
DECLARE_GL_FUNCTION_HEAD(GLboolean, IsVertexArray, GLuint array) DECLARE_GL_FUNCTION_END(GLboolean, IsVertexArray, array)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetIntegeri_v, GLenum target, GLuint index, GLint* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetIntegeri_v, target, index, data)
DECLARE_GL_FUNCTION_HEAD(void, GetIntegeri_v, GLenum target, GLuint index, GLint* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetIntegeri_v, target, index, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BeginTransformFeedback, GLenum primitiveMode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BeginTransformFeedback, primitiveMode)
DECLARE_GL_FUNCTION_STUB_HEAD(void, EndTransformFeedback) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, EndTransformFeedback)
DECLARE_GL_FUNCTION_HEAD(void, BindBufferRange, GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindBufferRange, target, index, buffer, offset, size)
@@ -249,11 +248,11 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribI4iv, GLuint index, const GLint*
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribI4uiv, GLuint index, const GLuint* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribI4uiv, index, v)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetUniformuiv, GLuint program, GLint location, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetUniformuiv, program, location, params)
DECLARE_GL_FUNCTION_HEAD(GLint, GetFragDataLocation, GLuint program, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetFragDataLocation, program, name)
DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform1ui, GLint location, GLuint v0) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform1ui, location, v0)
DECLARE_GL_FUNCTION_HEAD(void, Uniform1ui, GLint location, GLuint v0) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Uniform1ui, location, v0)
DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform2ui, GLint location, GLuint v0, GLuint v1) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform2ui, location, v0, v1)
DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform3ui, GLint location, GLuint v0, GLuint v1, GLuint v2) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform3ui, location, v0, v1, v2)
DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform4ui, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform4ui, location, v0, v1, v2, v3)
DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform1uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform1uiv, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, Uniform1uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Uniform1uiv, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform2uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform2uiv, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform3uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform3uiv, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, Uniform4uiv, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Uniform4uiv, location, count, value)
@@ -270,15 +269,15 @@ DECLARE_GL_FUNCTION_HEAD(void, GetActiveUniformBlockName, GLuint program, GLuint
DECLARE_GL_FUNCTION_HEAD(void, UniformBlockBinding, GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) DECLARE_GL_FUNCTION_END_NO_RETURN(void, UniformBlockBinding, program, uniformBlockIndex, uniformBlockBinding)
DECLARE_GL_FUNCTION_HEAD(void, DrawArraysInstanced, GLenum mode, GLint first, GLsizei count, GLsizei instancecount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawArraysInstanced, mode, first, count, instancecount)
DECLARE_GL_FUNCTION_HEAD(void, DrawElementsInstanced, GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawElementsInstanced, mode, count, type, indices, instancecount)
DECLARE_GL_FUNCTION_STUB_HEAD(GLsync, FenceSync, GLenum condition, GLbitfield flags) DECLARE_GL_FUNCTION_STUB_END(GLsync, FenceSync, condition, flags)
DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, IsSync, GLsync sync) DECLARE_GL_FUNCTION_STUB_END(GLboolean, IsSync, sync)
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteSync, GLsync sync) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteSync, sync)
DECLARE_GL_FUNCTION_STUB_HEAD(GLenum, ClientWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) DECLARE_GL_FUNCTION_STUB_END(GLenum, ClientWaitSync, sync, flags, timeout)
DECLARE_GL_FUNCTION_STUB_HEAD(void, WaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, WaitSync, sync, flags, timeout)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetInteger64v, GLenum pname, GLint64* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetInteger64v, pname, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetSynciv, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetSynciv, sync, pname, bufSize, length, values)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetInteger64i_v, GLenum target, GLuint index, GLint64* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetInteger64i_v, target, index, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBufferParameteri64v, GLenum target, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBufferParameteri64v, target, pname, params)
DECLARE_GL_FUNCTION_HEAD(GLsync, FenceSync, GLenum condition, GLbitfield flags) DECLARE_GL_FUNCTION_END(GLsync, FenceSync, condition, flags)
DECLARE_GL_FUNCTION_HEAD(GLboolean, IsSync, GLsync sync) DECLARE_GL_FUNCTION_END(GLboolean, IsSync, sync)
DECLARE_GL_FUNCTION_HEAD(void, DeleteSync, GLsync sync) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteSync, sync)
DECLARE_GL_FUNCTION_HEAD(GLenum, ClientWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) DECLARE_GL_FUNCTION_END(GLenum, ClientWaitSync, sync, flags, timeout)
DECLARE_GL_FUNCTION_HEAD(void, WaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout) DECLARE_GL_FUNCTION_END_NO_RETURN(void, WaitSync, sync, flags, timeout)
DECLARE_GL_FUNCTION_HEAD(void, GetInteger64v, GLenum pname, GLint64* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetInteger64v, pname, data)
DECLARE_GL_FUNCTION_HEAD(void, GetSynciv, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetSynciv, sync, pname, bufSize, length, values)
DECLARE_GL_FUNCTION_HEAD(void, GetInteger64i_v, GLenum target, GLuint index, GLint64* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetInteger64i_v, target, index, data)
DECLARE_GL_FUNCTION_HEAD(void, GetBufferParameteri64v, GLenum target, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBufferParameteri64v, target, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, GenSamplers, GLsizei count, GLuint* samplers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenSamplers, count, samplers)
DECLARE_GL_FUNCTION_HEAD(void, DeleteSamplers, GLsizei count, const GLuint* samplers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteSamplers, count, samplers)
DECLARE_GL_FUNCTION_HEAD(GLboolean, IsSampler, GLuint sampler) DECLARE_GL_FUNCTION_END(GLboolean, IsSampler, sampler)
@@ -304,17 +303,17 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateSubFramebuffer, GLenum target, GLs
DECLARE_GL_FUNCTION_STUB_HEAD(void, TexStorage2D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexStorage2D, target, levels, internalformat, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TexStorage3D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexStorage3D, target, levels, internalformat, width, height, depth)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetInternalformativ, GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetInternalformativ, target, internalformat, pname, bufSize, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, DispatchCompute, GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DispatchCompute, num_groups_x, num_groups_y, num_groups_z)
DECLARE_GL_FUNCTION_STUB_HEAD(void, DispatchComputeIndirect, GLintptr indirect) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DispatchComputeIndirect, indirect)
DECLARE_GL_FUNCTION_HEAD(void, DispatchCompute, GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DispatchCompute, num_groups_x, num_groups_y, num_groups_z)
DECLARE_GL_FUNCTION_HEAD(void, DispatchComputeIndirect, GLintptr indirect) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DispatchComputeIndirect, indirect)
DECLARE_GL_FUNCTION_HEAD(void, DrawArraysIndirect, GLenum mode, const void* indirect) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawArraysIndirect, mode, indirect)
DECLARE_GL_FUNCTION_HEAD(void, DrawElementsIndirect, GLenum mode, GLenum type, const void* indirect) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawElementsIndirect, mode, type, indirect)
DECLARE_GL_FUNCTION_STUB_HEAD(void, FramebufferParameteri, GLenum target, GLenum pname, GLint param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FramebufferParameteri, target, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetFramebufferParameteriv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetFramebufferParameteriv, target, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramInterfaceiv, GLuint program, GLenum programInterface, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramInterfaceiv, program, programInterface, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(GLuint, GetProgramResourceIndex, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_STUB_END(GLuint, GetProgramResourceIndex, program, programInterface, name)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramResourceName, GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei* length, GLchar* name) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramResourceName, program, programInterface, index, bufSize, length, name)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramResourceiv, GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramResourceiv, program, programInterface, index, propCount, props, bufSize, length, params)
DECLARE_GL_FUNCTION_STUB_HEAD(GLint, GetProgramResourceLocation, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_STUB_END(GLint, GetProgramResourceLocation, program, programInterface, name)
DECLARE_GL_FUNCTION_HEAD(void, GetProgramInterfaceiv, GLuint program, GLenum programInterface, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramInterfaceiv, program, programInterface, pname, params)
DECLARE_GL_FUNCTION_HEAD(GLuint, GetProgramResourceIndex, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_END(GLuint, GetProgramResourceIndex, program, programInterface, name)
DECLARE_GL_FUNCTION_HEAD(void, GetProgramResourceName, GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei* length, GLchar* name) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramResourceName, program, programInterface, index, bufSize, length, name)
DECLARE_GL_FUNCTION_HEAD(void, GetProgramResourceiv, GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramResourceiv, program, programInterface, index, propCount, props, bufSize, length, params)
DECLARE_GL_FUNCTION_HEAD(GLint, GetProgramResourceLocation, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetProgramResourceLocation, program, programInterface, name)
DECLARE_GL_FUNCTION_STUB_HEAD(void, UseProgramStages, GLuint pipeline, GLbitfield stages, GLuint program) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, UseProgramStages, pipeline, stages, program)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ActiveShaderProgram, GLuint pipeline, GLuint program) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ActiveShaderProgram, pipeline, program)
DECLARE_GL_FUNCTION_STUB_HEAD(GLuint, CreateShaderProgramv, GLenum type, GLsizei count, const GLchar* const* strings) DECLARE_GL_FUNCTION_STUB_END(GLuint, CreateShaderProgramv, type, count, strings)
@@ -323,33 +322,33 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteProgramPipelines, GLsizei n, const GLu
DECLARE_GL_FUNCTION_STUB_HEAD(void, GenProgramPipelines, GLsizei n, GLuint* pipelines) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenProgramPipelines, n, pipelines)
DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, IsProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_STUB_END(GLboolean, IsProgramPipeline, pipeline)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramPipelineiv, GLuint pipeline, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramPipelineiv, pipeline, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform1i, GLuint program, GLint location, GLint v0) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform1i, program, location, v0)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform2i, GLuint program, GLint location, GLint v0, GLint v1) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform2i, program, location, v0, v1)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform3i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform3i, program, location, v0, v1, v2)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform4i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform4i, program, location, v0, v1, v2, v3)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform1ui, GLuint program, GLint location, GLuint v0) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform1ui, program, location, v0)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform2ui, GLuint program, GLint location, GLuint v0, GLuint v1) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform2ui, program, location, v0, v1)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform3ui, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform3ui, program, location, v0, v1, v2)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform4ui, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform4ui, program, location, v0, v1, v2, v3)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform1f, GLuint program, GLint location, GLfloat v0) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform1f, program, location, v0)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform2f, GLuint program, GLint location, GLfloat v0, GLfloat v1) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform2f, program, location, v0, v1)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform3f, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform3f, program, location, v0, v1, v2)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform4f, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform4f, program, location, v0, v1, v2, v3)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform1iv, GLuint program, GLint location, GLsizei count, const GLint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform1iv, program, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform2iv, GLuint program, GLint location, GLsizei count, const GLint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform2iv, program, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform3iv, GLuint program, GLint location, GLsizei count, const GLint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform3iv, program, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform4iv, GLuint program, GLint location, GLsizei count, const GLint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform4iv, program, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform1uiv, GLuint program, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform1uiv, program, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform2uiv, GLuint program, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform2uiv, program, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform3uiv, GLuint program, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform3uiv, program, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform4uiv, GLuint program, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform4uiv, program, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform1fv, GLuint program, GLint location, GLsizei count, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform1fv, program, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform2fv, GLuint program, GLint location, GLsizei count, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform2fv, program, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform3fv, GLuint program, GLint location, GLsizei count, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform3fv, program, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform4fv, GLuint program, GLint location, GLsizei count, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform4fv, program, location, count, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniformMatrix2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniformMatrix2fv, program, location, count, transpose, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniformMatrix3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniformMatrix3fv, program, location, count, transpose, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniformMatrix4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniformMatrix4fv, program, location, count, transpose, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform1i, GLuint program, GLint location, GLint v0) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform1i, program, location, v0)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform2i, GLuint program, GLint location, GLint v0, GLint v1) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform2i, program, location, v0, v1)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform3i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform3i, program, location, v0, v1, v2)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform4i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform4i, program, location, v0, v1, v2, v3)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform1ui, GLuint program, GLint location, GLuint v0) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform1ui, program, location, v0)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform2ui, GLuint program, GLint location, GLuint v0, GLuint v1) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform2ui, program, location, v0, v1)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform3ui, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform3ui, program, location, v0, v1, v2)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform4ui, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform4ui, program, location, v0, v1, v2, v3)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform1f, GLuint program, GLint location, GLfloat v0) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform1f, program, location, v0)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform2f, GLuint program, GLint location, GLfloat v0, GLfloat v1) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform2f, program, location, v0, v1)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform3f, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform3f, program, location, v0, v1, v2)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform4f, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform4f, program, location, v0, v1, v2, v3)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform1iv, GLuint program, GLint location, GLsizei count, const GLint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform1iv, program, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform2iv, GLuint program, GLint location, GLsizei count, const GLint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform2iv, program, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform3iv, GLuint program, GLint location, GLsizei count, const GLint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform3iv, program, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform4iv, GLuint program, GLint location, GLsizei count, const GLint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform4iv, program, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform1uiv, GLuint program, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform1uiv, program, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform2uiv, GLuint program, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform2uiv, program, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform3uiv, GLuint program, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform3uiv, program, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform4uiv, GLuint program, GLint location, GLsizei count, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform4uiv, program, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform1fv, GLuint program, GLint location, GLsizei count, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform1fv, program, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform2fv, GLuint program, GLint location, GLsizei count, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform2fv, program, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform3fv, GLuint program, GLint location, GLsizei count, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform3fv, program, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform4fv, GLuint program, GLint location, GLsizei count, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform4fv, program, location, count, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniformMatrix2fv, program, location, count, transpose, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniformMatrix3fv, program, location, count, transpose, value)
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniformMatrix4fv, program, location, count, transpose, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniformMatrix2x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniformMatrix2x3fv, program, location, count, transpose, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniformMatrix3x2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniformMatrix3x2fv, program, location, count, transpose, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniformMatrix2x4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniformMatrix2x4fv, program, location, count, transpose, value)
@@ -358,10 +357,10 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniformMatrix3x4fv, GLuint program, G
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniformMatrix4x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniformMatrix4x3fv, program, location, count, transpose, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ValidateProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ValidateProgramPipeline, pipeline)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramPipelineInfoLog, GLuint pipeline, GLsizei bufSize, GLsizei* length, GLchar* infoLog) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramPipelineInfoLog, pipeline, bufSize, length, infoLog)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindImageTexture, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindImageTexture, unit, texture, level, layered, layer, access, format)
DECLARE_GL_FUNCTION_HEAD(void, BindImageTexture, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindImageTexture, unit, texture, level, layered, layer, access, format)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBooleani_v, GLenum target, GLuint index, GLboolean* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBooleani_v, target, index, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, MemoryBarrier, GLbitfield barriers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MemoryBarrier, barriers)
DECLARE_GL_FUNCTION_STUB_HEAD(void, MemoryBarrierByRegion, GLbitfield barriers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MemoryBarrierByRegion, barriers)
DECLARE_GL_FUNCTION_HEAD(void, MemoryBarrier, GLbitfield barriers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MemoryBarrier, barriers)
DECLARE_GL_FUNCTION_HEAD(void, MemoryBarrierByRegion, GLbitfield barriers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MemoryBarrierByRegion, barriers)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TexStorage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TexStorage2DMultisample, target, samples, internalformat, width, height, fixedsamplelocations)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetMultisamplefv, GLenum pname, GLuint index, GLfloat* val) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetMultisamplefv, pname, index, val)
DECLARE_GL_FUNCTION_HEAD(void, SampleMaski, GLuint maskNumber, GLbitfield mask) DECLARE_GL_FUNCTION_END_NO_RETURN(void, SampleMaski, maskNumber, mask)
@@ -387,14 +386,14 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetObjectPtrLabel, const void* ptr, GLsizei
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetPointerv, GLenum pname, void** params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetPointerv, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, Enablei, GLenum target, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Enablei, target, index)
DECLARE_GL_FUNCTION_HEAD(void, Disablei, GLenum target, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, Disablei, target, index)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquationi, GLuint buf, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendEquationi, buf, mode)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquationiARB, GLuint buf, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendEquationi, buf, mode)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquationSeparatei, GLuint buf, GLenum modeRGB, GLenum modeAlpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendEquationSeparatei, buf, modeRGB, modeAlpha)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquationSeparateiARB, GLuint buf, GLenum modeRGB, GLenum modeAlpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendEquationSeparatei, buf, modeRGB, modeAlpha)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendFunci, GLuint buf, GLenum src, GLenum dst) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendFunci, buf, src, dst)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendFunciARB, GLuint buf, GLenum src, GLenum dst) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendFunci, buf, src, dst)
DECLARE_GL_FUNCTION_HEAD(void, BlendEquationi, GLuint buf, GLenum mode) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendEquationi, buf, mode)
DECLARE_GL_FUNCTION_HEAD(void, BlendEquationiARB, GLuint buf, GLenum mode) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendEquationi, buf, mode)
DECLARE_GL_FUNCTION_HEAD(void, BlendEquationSeparatei, GLuint buf, GLenum modeRGB, GLenum modeAlpha) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendEquationSeparatei, buf, modeRGB, modeAlpha)
DECLARE_GL_FUNCTION_HEAD(void, BlendEquationSeparateiARB, GLuint buf, GLenum modeRGB, GLenum modeAlpha) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendEquationSeparatei, buf, modeRGB, modeAlpha)
DECLARE_GL_FUNCTION_HEAD(void, BlendFunci, GLuint buf, GLenum src, GLenum dst) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendFunci, buf, src, dst)
DECLARE_GL_FUNCTION_HEAD(void, BlendFunciARB, GLuint buf, GLenum src, GLenum dst) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendFunci, buf, src, dst)
DECLARE_GL_FUNCTION_HEAD(void, BlendFuncSeparatei, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendFuncSeparatei, buf, srcRGB, dstRGB, srcAlpha, dstAlpha)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendFuncSeparateiARB, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendFuncSeparatei, buf, srcRGB, dstRGB, srcAlpha, dstAlpha)
DECLARE_GL_FUNCTION_HEAD(void, BlendFuncSeparateiARB, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlendFuncSeparatei, buf, srcRGB, dstRGB, srcAlpha, dstAlpha)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ColorMaski, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ColorMaski, index, r, g, b, a)
DECLARE_GL_FUNCTION_HEAD(GLboolean, IsEnabledi, GLenum target, GLuint index) DECLARE_GL_FUNCTION_END(GLboolean, IsEnabledi, target, index)
DECLARE_GL_FUNCTION_HEAD(void, DrawElementsBaseVertex, GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawElementsBaseVertex, mode, count, type, indices, basevertex)
@@ -847,7 +846,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4usv, GLuint index, const GLusho
DECLARE_GL_FUNCTION_STUB_HEAD(void, PrimitiveRestartIndex, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PrimitiveRestartIndex, index)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetActiveUniformName, GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformName) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetActiveUniformName, program, uniformIndex, bufSize, length, uniformName)
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsBaseVertex, GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount, const GLint* basevertex) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElementsBaseVertex, mode, count, type, indices, drawcount, basevertex)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProvokingVertex, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProvokingVertex, mode)
DECLARE_GL_FUNCTION_HEAD(void, ProvokingVertex, GLenum mode) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProvokingVertex, mode)
DECLARE_GL_FUNCTION_HEAD(void, TexImage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexImage2DMultisample, target, samples, internalformat, width, height, fixedsamplelocations)
DECLARE_GL_FUNCTION_HEAD(void, TexImage3DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexImage3DMultisample, target, samples, internalformat, width, height, depth, fixedsamplelocations)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindFragDataLocationIndexed, GLuint program, GLuint colorNumber, GLuint index, const GLchar* name) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindFragDataLocationIndexed, program, colorNumber, index, name)
@@ -977,11 +976,11 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateBufferSubData, GLuint buffer, GLin
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateBufferData, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateBufferData, buffer)
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawArraysIndirect, GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawArraysIndirect, mode, indirect, drawcount, stride)
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsIndirect, GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElementsIndirect, mode, type, indirect, drawcount, stride)
DECLARE_GL_FUNCTION_STUB_HEAD(GLint, GetProgramResourceLocationIndex, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_STUB_END(GLint, GetProgramResourceLocationIndex, program, programInterface, name)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ShaderStorageBlockBinding, GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ShaderStorageBlockBinding, program, storageBlockIndex, storageBlockBinding)
DECLARE_GL_FUNCTION_HEAD(GLint, GetProgramResourceLocationIndex, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetProgramResourceLocationIndex, program, programInterface, name)
DECLARE_GL_FUNCTION_HEAD(void, ShaderStorageBlockBinding, GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ShaderStorageBlockBinding, program, storageBlockIndex, storageBlockBinding)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureView, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureView, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribLFormat, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribLFormat, attribindex, size, type, relativeoffset)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BufferStorage, GLenum target, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BufferStorage, target, size, data, flags)
DECLARE_GL_FUNCTION_HEAD(void, BufferStorage, GLenum target, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BufferStorage, target, size, data, flags)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearTexImage, GLuint texture, GLint level, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearTexImage, texture, level, format, type, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearTexSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearTexSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindBuffersBase, GLenum target, GLuint first, GLsizei count, const GLuint* buffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindBuffersBase, target, first, count, buffers)
@@ -997,51 +996,51 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, TransformFeedbackBufferRange, GLuint xfb, GL
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTransformFeedbackiv, GLuint xfb, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTransformFeedbackiv, xfb, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTransformFeedbacki_v, GLuint xfb, GLenum pname, GLuint index, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTransformFeedbacki_v, xfb, pname, index, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTransformFeedbacki64_v, GLuint xfb, GLenum pname, GLuint index, GLint64* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTransformFeedbacki64_v, xfb, pname, index, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateBuffers, GLsizei n, GLuint* buffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateBuffers, n, buffers)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedBufferStorage, GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedBufferStorage, buffer, size, data, flags)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedBufferData, GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedBufferData, buffer, size, data, usage)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedBufferSubData, GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedBufferSubData, buffer, offset, size, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyNamedBufferSubData, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyNamedBufferSubData, readBuffer, writeBuffer, readOffset, writeOffset, size)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedBufferData, GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedBufferData, buffer, internalformat, format, type, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedBufferSubData, GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedBufferSubData, buffer, internalformat, offset, size, format, type, data)
DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, UnmapNamedBuffer, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END(GLboolean, UnmapNamedBuffer, buffer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, FlushMappedNamedBufferRange, GLuint buffer, GLintptr offset, GLsizeiptr length) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FlushMappedNamedBufferRange, buffer, offset, length)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferParameteriv, GLuint buffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferParameteriv, buffer, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferParameteri64v, GLuint buffer, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferParameteri64v, buffer, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferPointerv, GLuint buffer, GLenum pname, void** params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferPointerv, buffer, pname, *params)
DECLARE_GL_FUNCTION_HEAD(void, CreateBuffers, GLsizei n, GLuint* buffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateBuffers, n, buffers)
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferStorage, GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferStorage, buffer, size, data, flags)
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferData, GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferData, buffer, size, data, usage)
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferSubData, GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferSubData, buffer, offset, size, data)
DECLARE_GL_FUNCTION_HEAD(void, CopyNamedBufferSubData, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyNamedBufferSubData, readBuffer, writeBuffer, readOffset, writeOffset, size)
DECLARE_GL_FUNCTION_HEAD(void, ClearNamedBufferData, GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedBufferData, buffer, internalformat, format, type, data)
DECLARE_GL_FUNCTION_HEAD(void, ClearNamedBufferSubData, GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedBufferSubData, buffer, internalformat, offset, size, format, type, data)
DECLARE_GL_FUNCTION_HEAD(GLboolean, UnmapNamedBuffer, GLuint buffer) DECLARE_GL_FUNCTION_END(GLboolean, UnmapNamedBuffer, buffer)
DECLARE_GL_FUNCTION_HEAD(void, FlushMappedNamedBufferRange, GLuint buffer, GLintptr offset, GLsizeiptr length) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FlushMappedNamedBufferRange, buffer, offset, length)
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferParameteriv, GLuint buffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferParameteriv, buffer, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferParameteri64v, GLuint buffer, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferParameteri64v, buffer, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferPointerv, GLuint buffer, GLenum pname, void** params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferPointerv, buffer, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferSubData, GLuint buffer, GLintptr offset, GLsizeiptr size, void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferSubData, buffer, offset, size, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateFramebuffers, GLsizei n, GLuint* framebuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateFramebuffers, n, framebuffers)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferRenderbuffer, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferRenderbuffer, framebuffer, attachment, renderbuffertarget, renderbuffer)
DECLARE_GL_FUNCTION_HEAD(void, CreateFramebuffers, GLsizei n, GLuint* framebuffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateFramebuffers, n, framebuffers)
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferRenderbuffer, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferRenderbuffer, framebuffer, attachment, renderbuffertarget, renderbuffer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferParameteri, GLuint framebuffer, GLenum pname, GLint param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferParameteri, framebuffer, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTexture, GLuint framebuffer, GLenum attachment, GLuint texture, GLint level) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferTexture, framebuffer, attachment, texture, level)
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferTexture, GLuint framebuffer, GLenum attachment, GLuint texture, GLint level) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferTexture, framebuffer, attachment, texture, level)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTextureLayer, GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferTextureLayer, framebuffer, attachment, texture, level, layer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferDrawBuffer, GLuint framebuffer, GLenum buf) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferDrawBuffer, framebuffer, buf)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferDrawBuffers, GLuint framebuffer, GLsizei n, const GLenum* bufs) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferDrawBuffers, framebuffer, n, bufs)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferReadBuffer, GLuint framebuffer, GLenum src) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferReadBuffer, framebuffer, src)
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferDrawBuffer, GLuint framebuffer, GLenum buf) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferDrawBuffer, framebuffer, buf)
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferDrawBuffers, GLuint framebuffer, GLsizei n, const GLenum* bufs) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferDrawBuffers, framebuffer, n, bufs)
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferReadBuffer, GLuint framebuffer, GLenum src) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferReadBuffer, framebuffer, src)
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateNamedFramebufferData, GLuint framebuffer, GLsizei numAttachments, const GLenum* attachments) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateNamedFramebufferData, framebuffer, numAttachments, attachments)
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateNamedFramebufferSubData, GLuint framebuffer, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateNamedFramebufferSubData, framebuffer, numAttachments, attachments, x, y, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedFramebufferiv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedFramebufferiv, framebuffer, buffer, drawbuffer, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedFramebufferuiv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedFramebufferuiv, framebuffer, buffer, drawbuffer, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedFramebufferfv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedFramebufferfv, framebuffer, buffer, drawbuffer, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedFramebufferfi, GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedFramebufferfi, framebuffer, buffer, drawbuffer, depth, stencil)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BlitNamedFramebuffer, GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlitNamedFramebuffer, readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter)
DECLARE_GL_FUNCTION_STUB_HEAD(GLenum, CheckNamedFramebufferStatus, GLuint framebuffer, GLenum target) DECLARE_GL_FUNCTION_STUB_END(GLenum, CheckNamedFramebufferStatus, framebuffer, target)
DECLARE_GL_FUNCTION_HEAD(void, ClearNamedFramebufferfv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedFramebufferfv, framebuffer, buffer, drawbuffer, value)
DECLARE_GL_FUNCTION_HEAD(void, ClearNamedFramebufferfi, GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedFramebufferfi, framebuffer, buffer, drawbuffer, depth, stencil)
DECLARE_GL_FUNCTION_HEAD(void, BlitNamedFramebuffer, GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlitNamedFramebuffer, readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter)
DECLARE_GL_FUNCTION_HEAD(GLenum, CheckNamedFramebufferStatus, GLuint framebuffer, GLenum target) DECLARE_GL_FUNCTION_END(GLenum, CheckNamedFramebufferStatus, framebuffer, target)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedFramebufferParameteriv, GLuint framebuffer, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedFramebufferParameteriv, framebuffer, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedFramebufferAttachmentParameteriv, GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedFramebufferAttachmentParameteriv, framebuffer, attachment, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateRenderbuffers, GLsizei n, GLuint* renderbuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateRenderbuffers, n, renderbuffers)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorage, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorage, renderbuffer, internalformat, width, height)
DECLARE_GL_FUNCTION_HEAD(void, GetNamedFramebufferAttachmentParameteriv, GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedFramebufferAttachmentParameteriv, framebuffer, attachment, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, CreateRenderbuffers, GLsizei n, GLuint* renderbuffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateRenderbuffers, n, renderbuffers)
DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorage, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedRenderbufferStorage, renderbuffer, internalformat, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorageMultisample, GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorageMultisample, renderbuffer, samples, internalformat, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedRenderbufferParameteriv, GLuint renderbuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedRenderbufferParameteriv, renderbuffer, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateTextures, GLenum target, GLsizei n, GLuint* textures) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateTextures, target, n, textures)
DECLARE_GL_FUNCTION_HEAD(void, GetNamedRenderbufferParameteriv, GLuint renderbuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedRenderbufferParameteriv, renderbuffer, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, CreateTextures, GLenum target, GLsizei n, GLuint* textures) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateTextures, target, n, textures)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBuffer, GLuint texture, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBuffer, texture, internalformat, buffer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBufferRange, GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBufferRange, texture, internalformat, buffer, offset, size)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage1D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage1D, texture, levels, internalformat, width)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage2D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage2D, texture, levels, internalformat, width, height)
DECLARE_GL_FUNCTION_HEAD(void, TextureStorage2D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureStorage2D, texture, levels, internalformat, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage3D, GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage3D, texture, levels, internalformat, width, height, depth)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage2DMultisample, GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage2DMultisample, texture, samples, internalformat, width, height, fixedsamplelocations)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureStorage3DMultisample, GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureStorage3DMultisample, texture, samples, internalformat, width, height, depth, fixedsamplelocations)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureSubImage1D, texture, level, xoffset, width, format, type, pixels)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, type, pixels)
DECLARE_GL_FUNCTION_HEAD(void, TextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, type, pixels)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureSubImage3D, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage1D, texture, level, xoffset, width, format, imageSize, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, imageSize, data)
@@ -1049,31 +1048,31 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage3D, GLuint texture,
DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage1D, texture, level, xoffset, x, y, width)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage2D, texture, level, xoffset, yoffset, x, y, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage3D, texture, level, xoffset, yoffset, zoffset, x, y, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterf, GLuint texture, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterf, texture, pname, param)
DECLARE_GL_FUNCTION_HEAD(void, TextureParameterf, GLuint texture, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterf, texture, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterfv, GLuint texture, GLenum pname, const GLfloat* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterfv, texture, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameteri, GLuint texture, GLenum pname, GLint param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameteri, texture, pname, param)
DECLARE_GL_FUNCTION_HEAD(void, TextureParameteri, GLuint texture, GLenum pname, GLint param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameteri, texture, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterIiv, GLuint texture, GLenum pname, const GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterIiv, texture, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameterIuiv, GLuint texture, GLenum pname, const GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameterIuiv, texture, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureParameteriv, GLuint texture, GLenum pname, const GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureParameteriv, texture, pname, param)
DECLARE_GL_FUNCTION_HEAD(void, TextureParameteriv, GLuint texture, GLenum pname, const GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameteriv, texture, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GenerateTextureMipmap, GLuint texture) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenerateTextureMipmap, texture)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindTextureUnit, GLuint unit, GLuint texture) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindTextureUnit, unit, texture)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureImage, GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureImage, texture, level, format, type, bufSize, pixels)
DECLARE_GL_FUNCTION_HEAD(void, BindTextureUnit, GLuint unit, GLuint texture) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindTextureUnit, unit, texture)
DECLARE_GL_FUNCTION_HEAD(void, GetTextureImage, GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureImage, texture, level, format, type, bufSize, pixels)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetCompressedTextureImage, GLuint texture, GLint level, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetCompressedTextureImage, texture, level, bufSize, pixels)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureLevelParameterfv, GLuint texture, GLint level, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureLevelParameterfv, texture, level, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureLevelParameteriv, GLuint texture, GLint level, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureLevelParameteriv, texture, level, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, GetTextureLevelParameteriv, GLuint texture, GLint level, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureLevelParameteriv, texture, level, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterfv, GLuint texture, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterfv, texture, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterIiv, GLuint texture, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterIiv, texture, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameterIuiv, GLuint texture, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameterIuiv, texture, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureParameteriv, GLuint texture, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureParameteriv, texture, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateVertexArrays, GLsizei n, GLuint* arrays) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateVertexArrays, n, arrays)
DECLARE_GL_FUNCTION_STUB_HEAD(void, DisableVertexArrayAttrib, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DisableVertexArrayAttrib, vaobj, index)
DECLARE_GL_FUNCTION_STUB_HEAD(void, EnableVertexArrayAttrib, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, EnableVertexArrayAttrib, vaobj, index)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayElementBuffer, GLuint vaobj, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayElementBuffer, vaobj, buffer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayVertexBuffer, GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayVertexBuffer, vaobj, bindingindex, buffer, offset, stride)
DECLARE_GL_FUNCTION_HEAD(void, GetTextureParameteriv, GLuint texture, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureParameteriv, texture, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, CreateVertexArrays, GLsizei n, GLuint* arrays) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateVertexArrays, n, arrays)
DECLARE_GL_FUNCTION_HEAD(void, DisableVertexArrayAttrib, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DisableVertexArrayAttrib, vaobj, index)
DECLARE_GL_FUNCTION_HEAD(void, EnableVertexArrayAttrib, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, EnableVertexArrayAttrib, vaobj, index)
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayElementBuffer, GLuint vaobj, GLuint buffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayElementBuffer, vaobj, buffer)
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayVertexBuffer, GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayVertexBuffer, vaobj, bindingindex, buffer, offset, stride)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayVertexBuffers, GLuint vaobj, GLuint first, GLsizei count, const GLuint* buffers, const GLintptr* offsets, const GLsizei* strides) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayVertexBuffers, vaobj, first, count, buffers, offsets, strides)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayAttribBinding, GLuint vaobj, GLuint attribindex, GLuint bindingindex) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayAttribBinding, vaobj, attribindex, bindingindex)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayAttribFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayAttribFormat, vaobj, attribindex, size, type, normalized, relativeoffset)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayAttribIFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayAttribIFormat, vaobj, attribindex, size, type, relativeoffset)
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribFormat, vaobj, attribindex, size, type, normalized, relativeoffset)
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribIFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribIFormat, vaobj, attribindex, size, type, relativeoffset)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayAttribLFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayAttribLFormat, vaobj, attribindex, size, type, relativeoffset)
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayBindingDivisor, GLuint vaobj, GLuint bindingindex, GLuint divisor) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayBindingDivisor, vaobj, bindingindex, divisor)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayiv, GLuint vaobj, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayiv, vaobj, pname, param)
@@ -1086,7 +1085,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryBufferObjecti64v, GLuint id, GLuint
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryBufferObjectiv, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryBufferObjectiv, id, buffer, pname, offset)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryBufferObjectui64v, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryBufferObjectui64v, id, buffer, pname, offset)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryBufferObjectuiv, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryBufferObjectuiv, id, buffer, pname, offset)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTextureSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTextureSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, bufSize, pixels)
DECLARE_GL_FUNCTION_HEAD(void, GetTextureSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, bufSize, pixels)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetCompressedTextureSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetCompressedTextureSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, bufSize, pixels)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnCompressedTexImage, GLenum target, GLint lod, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnCompressedTexImage, target, lod, bufSize, pixels)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnTexImage, GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnTexImage, target, level, format, type, bufSize, pixels)
@@ -1105,8 +1104,8 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnHistogram, GLenum target, GLboolean rese
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnMinmax, GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void* values) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnMinmax, target, reset, format, type, bufSize, values)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBarrier, void) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBarrier, )
DECLARE_GL_FUNCTION_STUB_HEAD(void, SpecializeShader, GLuint shader, const GLchar* pEntryPoint, GLuint numSpecializationConstants, const GLuint* pConstantIndex, const GLuint* pConstantValue) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, SpecializeShader, shader, pEntryPoint, numSpecializationConstants, pConstantIndex, pConstantValue)
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiDrawArraysIndirectCount, GLenum mode, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiDrawArraysIndirectCount, mode, indirect, drawcount, maxdrawcount, stride)
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiDrawElementsIndirectCount, GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiDrawElementsIndirectCount, mode, type, indirect, drawcount, maxdrawcount, stride)
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawArraysIndirectCount, GLenum mode, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawArraysIndirectCount, mode, indirect, drawcount, maxdrawcount, stride)
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsIndirectCount, GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElementsIndirectCount, mode, type, indirect, drawcount, maxdrawcount, stride)
DECLARE_GL_FUNCTION_STUB_HEAD(void, PolygonOffsetClamp, GLfloat factor, GLfloat units, GLfloat clamp) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PolygonOffsetClamp, factor, units, clamp)
DECLARE_GL_FUNCTION_STUB_HEAD(void, PrimitiveBoundingBoxARB, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) DECLARE_GL_FUNCTION_STUB_END(void, PrimitiveBoundingBoxARB, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW)
DECLARE_GL_FUNCTION_STUB_HEAD(GLuint64, GetTextureHandleARB, GLuint texture) DECLARE_GL_FUNCTION_STUB_END(GLuint64, GetTextureHandleARB, texture)
@@ -1215,8 +1214,8 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, Histogram, GLenum target, GLsizei width, GLe
DECLARE_GL_FUNCTION_STUB_HEAD(void, Minmax, GLenum target, GLenum internalformat, GLboolean sink) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Minmax, target, internalformat, sink)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ResetHistogram, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ResetHistogram, target)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ResetMinmax, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ResetMinmax, target)
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiDrawArraysIndirectCountARB, GLenum mode, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiDrawArraysIndirectCountARB, mode, indirect, drawcount, maxdrawcount, stride)
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiDrawElementsIndirectCountARB, GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiDrawElementsIndirectCountARB, mode, type, indirect, drawcount, maxdrawcount, stride)
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawArraysIndirectCountARB, GLenum mode, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawArraysIndirectCount, mode, indirect, drawcount, maxdrawcount, stride)
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsIndirectCountARB, GLenum mode, GLenum type, const void* indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElementsIndirectCount, mode, type, indirect, drawcount, maxdrawcount, stride)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CurrentPaletteMatrixARB, GLint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CurrentPaletteMatrixARB, index)
DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixIndexubvARB, GLint size, const GLubyte* indices) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MatrixIndexubvARB, size, indices)
DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixIndexusvARB, GLint size, const GLushort* indices) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MatrixIndexusvARB, size, indices)
@@ -1846,11 +1845,11 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixLoadTransposefEXT, GLenum mode, const
DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixLoadTransposedEXT, GLenum mode, const GLdouble* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MatrixLoadTransposedEXT, mode, m)
DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixMultTransposefEXT, GLenum mode, const GLfloat* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MatrixMultTransposefEXT, mode, m)
DECLARE_GL_FUNCTION_STUB_HEAD(void, MatrixMultTransposedEXT, GLenum mode, const GLdouble* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MatrixMultTransposedEXT, mode, m)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedBufferDataEXT, GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedBufferDataEXT, buffer, size, data, usage)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedBufferSubDataEXT, GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedBufferSubDataEXT, buffer, offset, size, data)
DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, UnmapNamedBufferEXT, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END(GLboolean, UnmapNamedBufferEXT, buffer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferParameterivEXT, GLuint buffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferParameterivEXT, buffer, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferPointervEXT, GLuint buffer, GLenum pname, void** params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferPointervEXT, buffer, pname, *params)
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferDataEXT, GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferData, buffer, size, data, usage)
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferSubDataEXT, GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferSubData, buffer, offset, size, data)
DECLARE_GL_FUNCTION_HEAD(GLboolean, UnmapNamedBufferEXT, GLuint buffer) DECLARE_GL_FUNCTION_END(GLboolean, UnmapNamedBuffer, buffer)
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferParameterivEXT, GLuint buffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferParameteriv, buffer, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferPointervEXT, GLuint buffer, GLenum pname, void** params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferPointerv, buffer, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferSubDataEXT, GLuint buffer, GLintptr offset, GLsizeiptr size, void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferSubDataEXT, buffer, offset, size, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureBufferEXT, GLuint texture, GLenum target, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureBufferEXT, texture, target, internalformat, buffer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiTexBufferEXT, GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiTexBufferEXT, texunit, target, internalformat, buffer)
@@ -1885,15 +1884,15 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedProgramLocalParameterdvEXT, GLuint p
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedProgramLocalParameterfvEXT, GLuint program, GLenum target, GLuint index, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedProgramLocalParameterfvEXT, program, target, index, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedProgramivEXT, GLuint program, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedProgramivEXT, program, target, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedProgramStringEXT, GLuint program, GLenum target, GLenum pname, void* string) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedProgramStringEXT, program, target, pname, string)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorageEXT, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorageEXT, renderbuffer, internalformat, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedRenderbufferParameterivEXT, GLuint renderbuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedRenderbufferParameterivEXT, renderbuffer, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorageEXT, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedRenderbufferStorage, renderbuffer, internalformat, width, height)
DECLARE_GL_FUNCTION_HEAD(void, GetNamedRenderbufferParameterivEXT, GLuint renderbuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedRenderbufferParameteriv, renderbuffer, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorageMultisampleEXT, GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorageMultisampleEXT, renderbuffer, samples, internalformat, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedRenderbufferStorageMultisampleCoverageEXT, GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedRenderbufferStorageMultisampleCoverageEXT, renderbuffer, coverageSamples, colorSamples, internalformat, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(GLenum, CheckNamedFramebufferStatusEXT, GLuint framebuffer, GLenum target) DECLARE_GL_FUNCTION_STUB_END(GLenum, CheckNamedFramebufferStatusEXT, framebuffer, target)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTexture1DEXT, GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferTexture1DEXT, framebuffer, attachment, textarget, texture, level)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTexture2DEXT, GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferTexture2DEXT, framebuffer, attachment, textarget, texture, level)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferTexture3DEXT, GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferTexture3DEXT, framebuffer, attachment, textarget, texture, level, zoffset)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferRenderbufferEXT, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferRenderbufferEXT, framebuffer, attachment, renderbuffertarget, renderbuffer)
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferRenderbufferEXT, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferRenderbuffer, framebuffer, attachment, renderbuffertarget, renderbuffer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedFramebufferAttachmentParameterivEXT, GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedFramebufferAttachmentParameterivEXT, framebuffer, attachment, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GenerateTextureMipmapEXT, GLuint texture, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenerateTextureMipmapEXT, texture, target)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GenerateMultiTexMipmapEXT, GLenum texunit, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenerateMultiTexMipmapEXT, texunit, target)
@@ -1919,16 +1918,16 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayVertexAttribOffsetEXT, GLuint vao
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayVertexAttribIOffsetEXT, GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayVertexAttribIOffsetEXT, vaobj, buffer, index, size, type, stride, offset)
DECLARE_GL_FUNCTION_STUB_HEAD(void, EnableVertexArrayEXT, GLuint vaobj, GLenum array) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, EnableVertexArrayEXT, vaobj, array)
DECLARE_GL_FUNCTION_STUB_HEAD(void, DisableVertexArrayEXT, GLuint vaobj, GLenum array) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DisableVertexArrayEXT, vaobj, array)
DECLARE_GL_FUNCTION_STUB_HEAD(void, EnableVertexArrayAttribEXT, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, EnableVertexArrayAttribEXT, vaobj, index)
DECLARE_GL_FUNCTION_STUB_HEAD(void, DisableVertexArrayAttribEXT, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DisableVertexArrayAttribEXT, vaobj, index)
DECLARE_GL_FUNCTION_HEAD(void, EnableVertexArrayAttribEXT, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, EnableVertexArrayAttrib, vaobj, index)
DECLARE_GL_FUNCTION_HEAD(void, DisableVertexArrayAttribEXT, GLuint vaobj, GLuint index) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DisableVertexArrayAttrib, vaobj, index)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayIntegervEXT, GLuint vaobj, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayIntegervEXT, vaobj, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayPointervEXT, GLuint vaobj, GLenum pname, void** param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayPointervEXT, vaobj, pname, *param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayIntegeri_vEXT, GLuint vaobj, GLuint index, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayIntegeri_vEXT, vaobj, index, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetVertexArrayPointeri_vEXT, GLuint vaobj, GLuint index, GLenum pname, void** param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetVertexArrayPointeri_vEXT, vaobj, index, pname, *param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, FlushMappedNamedBufferRangeEXT, GLuint buffer, GLintptr offset, GLsizeiptr length) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FlushMappedNamedBufferRangeEXT, buffer, offset, length)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedBufferStorageEXT, GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedBufferStorageEXT, buffer, size, data, flags)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedBufferDataEXT, GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedBufferDataEXT, buffer, internalformat, format, type, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedBufferSubDataEXT, GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedBufferSubDataEXT, buffer, internalformat, offset, size, format, type, data)
DECLARE_GL_FUNCTION_HEAD(void, FlushMappedNamedBufferRangeEXT, GLuint buffer, GLintptr offset, GLsizeiptr length) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FlushMappedNamedBufferRange, buffer, offset, length)
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferStorageEXT, GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferStorage, buffer, size, data, flags)
DECLARE_GL_FUNCTION_HEAD(void, ClearNamedBufferDataEXT, GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedBufferData, buffer, internalformat, format, type, data)
DECLARE_GL_FUNCTION_HEAD(void, ClearNamedBufferSubDataEXT, GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedBufferSubData, buffer, internalformat, offset, size, format, type, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferParameteriEXT, GLuint framebuffer, GLenum pname, GLint param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferParameteriEXT, framebuffer, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedFramebufferParameterivEXT, GLuint framebuffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedFramebufferParameterivEXT, framebuffer, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramUniform1dEXT, GLuint program, GLint location, GLdouble x) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramUniform1dEXT, program, location, x)
@@ -2046,7 +2045,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetPixelTransformParameterfvEXT, GLenum targ
DECLARE_GL_FUNCTION_STUB_HEAD(void, PointParameterfEXT, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PointParameterfEXT, pname, param)
DECLARE_GL_FUNCTION_STUB_HEAD(void, PointParameterfvEXT, GLenum pname, const GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PointParameterfvEXT, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, PolygonOffsetClampEXT, GLfloat factor, GLfloat units, GLfloat clamp) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PolygonOffsetClampEXT, factor, units, clamp)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProvokingVertexEXT, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProvokingVertexEXT, mode)
DECLARE_GL_FUNCTION_HEAD(void, ProvokingVertexEXT, GLenum mode) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProvokingVertex, mode)
DECLARE_GL_FUNCTION_STUB_HEAD(void, RasterSamplesEXT, GLuint samples, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, RasterSamplesEXT, samples, fixedsamplelocations)
DECLARE_GL_FUNCTION_STUB_HEAD(void, SecondaryColor3bEXT, GLbyte red, GLbyte green, GLbyte blue) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, SecondaryColor3bEXT, red, green, blue)
DECLARE_GL_FUNCTION_STUB_HEAD(void, SecondaryColor3bvEXT, const GLbyte* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, SecondaryColor3bvEXT, v)
@@ -2880,6 +2879,69 @@ MOBILEGL_GL_API void glGetFramebufferParameterivEXT(GLenum target, GLenum pname,
glGetFramebufferParameteriv(target, pname, params);
}
MOBILEGL_GL_API void glBindFramebufferEXT(GLenum target, GLuint framebuffer) {
glBindFramebuffer(target, framebuffer);
}
MOBILEGL_GL_API void glBindRenderbufferEXT(GLenum target, GLuint renderbuffer) {
glBindRenderbuffer(target, renderbuffer);
}
MOBILEGL_GL_API GLenum glCheckFramebufferStatusEXT(GLenum target) {
return glCheckFramebufferStatus(target);
}
MOBILEGL_GL_API void glDeleteFramebuffersEXT(GLsizei n, const GLuint* framebuffers) {
glDeleteFramebuffers(n, framebuffers);
}
MOBILEGL_GL_API void glDeleteRenderbuffersEXT(GLsizei n, const GLuint* renderbuffers) {
glDeleteRenderbuffers(n, renderbuffers);
}
MOBILEGL_GL_API void glFramebufferRenderbufferEXT(GLenum target, GLenum attachment, GLenum renderbuffertarget,
GLuint renderbuffer) {
glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer);
}
MOBILEGL_GL_API void glFramebufferTexture2DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture,
GLint level) {
glFramebufferTexture2D(target, attachment, textarget, texture, level);
}
MOBILEGL_GL_API void glGenFramebuffersEXT(GLsizei n, GLuint* framebuffers) {
glGenFramebuffers(n, framebuffers);
}
MOBILEGL_GL_API void glGenRenderbuffersEXT(GLsizei n, GLuint* renderbuffers) {
glGenRenderbuffers(n, renderbuffers);
}
MOBILEGL_GL_API void glGenerateMipmapEXT(GLenum target) {
glGenerateMipmap(target);
}
MOBILEGL_GL_API void glGetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, GLenum pname,
GLint* params) {
glGetFramebufferAttachmentParameteriv(target, attachment, pname, params);
}
MOBILEGL_GL_API void glGetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint* params) {
glGetRenderbufferParameteriv(target, pname, params);
}
MOBILEGL_GL_API GLboolean glIsFramebufferEXT(GLuint framebuffer) {
return glIsFramebuffer(framebuffer);
}
MOBILEGL_GL_API GLboolean glIsRenderbufferEXT(GLuint renderbuffer) {
return glIsRenderbuffer(renderbuffer);
}
MOBILEGL_GL_API void glRenderbufferStorageEXT(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
glRenderbufferStorage(target, internalformat, width, height);
}
MOBILEGL_GL_API GLenum glGetGraphicsResetStatusARB(void) {
return glGetGraphicsResetStatus();
}
@@ -2902,8 +2964,8 @@ MOBILEGL_GL_API void glGetObjectLabelEXT(GLenum identifier, GLuint name, GLsizei
}
MOBILEGL_GL_API void* glMapNamedBuffer(GLuint buffer, GLenum access) {
MGLOG_W("Stub function: %s(...)", __FUNCTION__);
return nullptr;
MGLOG_D("Implementing function: %s(...)", __FUNCTION__);
return MobileGL::MG_Impl::GLImpl::MapNamedBuffer(buffer, access);
}
MOBILEGL_GL_API void* glMapNamedBufferEXT(GLuint buffer, GLenum access) {
@@ -2911,8 +2973,8 @@ MOBILEGL_GL_API void* glMapNamedBufferEXT(GLuint buffer, GLenum access) {
}
MOBILEGL_GL_API void* glMapNamedBufferRange(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access) {
MGLOG_W("Stub function: %s(...)", __FUNCTION__);
return nullptr;
MGLOG_D("Implementing function: %s(...)", __FUNCTION__);
return MobileGL::MG_Impl::GLImpl::MapNamedBufferRange(buffer, offset, length, access);
}
MOBILEGL_GL_API void* glMapNamedBufferRangeEXT(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access) {
@@ -3025,48 +3087,48 @@ MOBILEGL_GL_API void glProgramUniform4uivEXT(GLuint program, GLint location, GLs
glProgramUniform4uiv(program, location, count, value);
}
MOBILEGL_GL_API void glProgramUniformMatrix2fvEXT(GLuint program, GLint location, GLsizei count,
GLboolean transpose, const GLfloat* value) {
MOBILEGL_GL_API void glProgramUniformMatrix2fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose,
const GLfloat* value) {
glProgramUniformMatrix2fv(program, location, count, transpose, value);
}
MOBILEGL_GL_API void glProgramUniformMatrix2x3fvEXT(GLuint program, GLint location, GLsizei count,
GLboolean transpose, const GLfloat* value) {
MOBILEGL_GL_API void glProgramUniformMatrix2x3fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose,
const GLfloat* value) {
glProgramUniformMatrix2x3fv(program, location, count, transpose, value);
}
MOBILEGL_GL_API void glProgramUniformMatrix2x4fvEXT(GLuint program, GLint location, GLsizei count,
GLboolean transpose, const GLfloat* value) {
MOBILEGL_GL_API void glProgramUniformMatrix2x4fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose,
const GLfloat* value) {
glProgramUniformMatrix2x4fv(program, location, count, transpose, value);
}
MOBILEGL_GL_API void glProgramUniformMatrix3fvEXT(GLuint program, GLint location, GLsizei count,
GLboolean transpose, const GLfloat* value) {
MOBILEGL_GL_API void glProgramUniformMatrix3fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose,
const GLfloat* value) {
glProgramUniformMatrix3fv(program, location, count, transpose, value);
}
MOBILEGL_GL_API void glProgramUniformMatrix3x2fvEXT(GLuint program, GLint location, GLsizei count,
GLboolean transpose, const GLfloat* value) {
MOBILEGL_GL_API void glProgramUniformMatrix3x2fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose,
const GLfloat* value) {
glProgramUniformMatrix3x2fv(program, location, count, transpose, value);
}
MOBILEGL_GL_API void glProgramUniformMatrix3x4fvEXT(GLuint program, GLint location, GLsizei count,
GLboolean transpose, const GLfloat* value) {
MOBILEGL_GL_API void glProgramUniformMatrix3x4fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose,
const GLfloat* value) {
glProgramUniformMatrix3x4fv(program, location, count, transpose, value);
}
MOBILEGL_GL_API void glProgramUniformMatrix4fvEXT(GLuint program, GLint location, GLsizei count,
GLboolean transpose, const GLfloat* value) {
MOBILEGL_GL_API void glProgramUniformMatrix4fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose,
const GLfloat* value) {
glProgramUniformMatrix4fv(program, location, count, transpose, value);
}
MOBILEGL_GL_API void glProgramUniformMatrix4x2fvEXT(GLuint program, GLint location, GLsizei count,
GLboolean transpose, const GLfloat* value) {
MOBILEGL_GL_API void glProgramUniformMatrix4x2fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose,
const GLfloat* value) {
glProgramUniformMatrix4x2fv(program, location, count, transpose, value);
}
MOBILEGL_GL_API void glProgramUniformMatrix4x3fvEXT(GLuint program, GLint location, GLsizei count,
GLboolean transpose, const GLfloat* value) {
MOBILEGL_GL_API void glProgramUniformMatrix4x3fvEXT(GLuint program, GLint location, GLsizei count, GLboolean transpose,
const GLfloat* value) {
glProgramUniformMatrix4x3fv(program, location, count, transpose, value);
}
File diff suppressed because it is too large Load Diff
@@ -11,51 +11,66 @@
#include <Includes.h>
#include <MG_State/GLState/Core.h>
namespace MobileGL {
namespace MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
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 ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value);
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value);
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width,
GLsizei height);
void RenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
GLboolean IsRenderbuffer(GLuint renderbuffer);
void GetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params);
void GenRenderbuffers(GLsizei n, GLuint* renderbuffers);
void FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers);
void BindRenderbuffer(GLenum target, GLuint renderbuffer);
void SampleMaski(GLuint maskNumber, GLbitfield mask);
GLboolean IsFramebuffer(GLuint framebuffer);
void GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params);
void GenFramebuffers(GLsizei n, GLuint* framebuffers);
void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
void FramebufferTexture3D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level,
GLint zoffset);
void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
void FramebufferTexture1D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
void FramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level);
void DrawBuffer(GLenum buf);
void DrawBuffers(GLsizei n, const GLenum* bufs);
void ReadBuffer(GLenum src);
void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers);
GLenum CheckFramebufferStatus(GLenum target);
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
GLint dstY1, GLbitfield mask, GLenum filter);
void BindFramebuffer(GLenum target, GLuint framebuffer);
namespace MobileGL::MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
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 ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value);
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value);
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width,
GLsizei height);
void RenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
GLboolean IsRenderbuffer(GLuint renderbuffer);
void GetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params);
void GenRenderbuffers(GLsizei n, GLuint* renderbuffers);
void CreateRenderbuffers(GLsizei n, GLuint* renderbuffers);
void NamedRenderbufferStorage(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height);
void GetNamedRenderbufferParameteriv(GLuint renderbuffer, GLenum pname, GLint* params);
void FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
void NamedFramebufferRenderbuffer(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget,
GLuint renderbuffer);
void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers);
void BindRenderbuffer(GLenum target, GLuint renderbuffer);
void SampleMaski(GLuint maskNumber, GLbitfield mask);
GLboolean IsFramebuffer(GLuint framebuffer);
void GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params);
void GenFramebuffers(GLsizei n, GLuint* framebuffers);
void CreateFramebuffers(GLsizei n, GLuint* framebuffers);
void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
void FramebufferTexture3D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level,
GLint zoffset);
void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
void FramebufferTexture1D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
void FramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level);
void NamedFramebufferTexture(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level);
void NamedFramebufferDrawBuffer(GLuint framebuffer, GLenum buf);
void NamedFramebufferDrawBuffers(GLuint framebuffer, GLsizei n, const GLenum* bufs);
void NamedFramebufferReadBuffer(GLuint framebuffer, GLenum src);
void ClearNamedFramebufferfv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat* value);
void ClearNamedFramebufferfi(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
GLenum CheckNamedFramebufferStatus(GLuint framebuffer, GLenum target);
void GetNamedFramebufferAttachmentParameteriv(GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params);
void BlitNamedFramebuffer(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1,
GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask,
GLenum filter);
void DrawBuffer(GLenum buf);
void DrawBuffers(GLsizei n, const GLenum* bufs);
void ReadBuffer(GLenum src);
void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers);
GLenum CheckFramebufferStatus(GLenum target);
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
GLint dstY1, GLbitfield mask, GLenum filter);
void BindFramebuffer(GLenum target, GLuint framebuffer);
namespace FramebufferImpl {
struct DefaultFramebufferInfo {
SharedPtr<MG_State::GLState::FramebufferObject> defaultFBO;
SharedPtr<MG_State::GLState::ITextureObject> colorAttachment;
SharedPtr<MG_State::GLState::ITextureObject> depthAttachment;
SharedPtr<MG_State::GLState::ITextureObject> stencilAttachment;
};
namespace FramebufferImpl {
struct DefaultFramebufferInfo {
SharedPtr<MG_State::GLState::FramebufferObject> defaultFBO;
SharedPtr<MG_State::GLState::ITextureObject> colorAttachment;
SharedPtr<MG_State::GLState::ITextureObject> depthAttachment;
SharedPtr<MG_State::GLState::ITextureObject> stencilAttachment;
};
extern DefaultFramebufferInfo* pDefaultFramebufferInfo;
} // namespace FramebufferImpl
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
extern UniquePtr<DefaultFramebufferInfo> pDefaultFramebufferInfo;
} // namespace FramebufferImpl
} // namespace MobileGL::MG_Impl::GLImpl
@@ -13,85 +13,82 @@
#include <MG_Util/Converters/MGToGL/FramebufferEnumConverter.h>
#include <MG_Util/Converters/MGToStr/FramebufferEnumConverter.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace FramebufferImpl {
Bool ValidateFramebufferTarget(FramebufferTarget target) {
if (target == FramebufferTarget::Unknown) {
using namespace MG_Util;
String bufferTargetStr = ConvertFramebufferTargetToString(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;
namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl {
Bool ValidateFramebufferTarget(FramebufferTarget target) {
if (target == FramebufferTarget::Unknown) {
using namespace MG_Util;
String bufferTargetStr = ConvertFramebufferTargetToString(target);
String glTargetStr = ConvertGLEnumToString(ConvertFramebufferTargetToGLEnum(target));
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", "ValidateFramebufferName",
std::format("Framebuffer name {} is not valid.", index)));
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl/FramebufferImpl", "ValidateFramebufferTarget",
std::format("Target {} ({}) is not valid.", bufferTargetStr, glTargetStr)));
return false;
}
return true;
}
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,
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;
Bool ValidateFramebufferName(Uint index, Bool allowZero) {
if (index == 0 && !allowZero) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", "ValidateRenderbufferName",
std::format("Renderbuffer name {} is not valid.", index)));
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", "ValidateFramebufferName",
"Framebuffer name 0 is not valid in this situation."));
return false;
}
} // namespace FramebufferImpl
} // namespace MobileGL::MG_Impl::GLImpl
Bool isValid = MG_State::pGLContext->ValidateFramebufferName(index);
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 <MG_State/GLState/FramebufferState/FramebufferObject.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace FramebufferImpl {
Bool ValidateFramebufferTarget(FramebufferTarget target);
Bool ValidateFramebufferName(Uint index, Bool allowZero = true);
Bool ValidateFramebufferAttachmentType(FramebufferAttachmentType attachment);
Bool ValidateRenderbufferTarget(RenderbufferTarget target);
Bool ValidateRenderbufferName(Uint index, Bool allowZero = true);
} // namespace FramebufferImpl
} // namespace MobileGL::MG_Impl::GLImpl
namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl {
Bool ValidateFramebufferTarget(FramebufferTarget target);
Bool ValidateFramebufferName(Uint index, Bool allowZero = true);
Bool ValidateFramebufferAttachmentType(FramebufferAttachmentType attachment);
Bool ValidateRenderbufferTarget(RenderbufferTarget target);
Bool ValidateRenderbufferName(Uint index, Bool allowZero = true);
} // namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl
+183 -50
View File
@@ -7,8 +7,6 @@
// End of Source File Header
#include "GL_Getter.h"
#include "GL/gl.h"
#include "MG_Util/Debug/Log.h"
#include <Config.h>
#include <MGGitHash.h>
#include <MG_State/GLState/Core.h>
@@ -78,12 +76,12 @@ namespace MobileGL::MG_Impl::GLImpl {
MGLOG_D("shadingLanguageVersion: %s", shadingLanguageVersion.c_str());
return (const GLubyte*)shadingLanguageVersion.c_str();
case GL_EXTENSIONS:
if (extensionsString.empty()) {
for (auto& ext : rendererInfo.RendererGLInfo.Extensions) {
extensionsString += MG_Util::ConvertGLExtToString(ext);
extensionsString.clear();
for (auto& ext : rendererInfo.RendererGLInfo.Extensions) {
if (!extensionsString.empty()) {
extensionsString += " ";
}
extensionsString.pop_back();
extensionsString += MG_Util::ConvertGLExtToString(ext);
}
return (const GLubyte*)extensionsString.c_str();
default:
@@ -110,24 +108,95 @@ namespace MobileGL::MG_Impl::GLImpl {
}
static Vector<String> extStrings;
static Bool initialized = false;
if (!initialized) {
extStrings.reserve(exts.size());
for (const auto& ext : exts) {
extStrings.emplace_back(MG_Util::ConvertGLExtToString(ext));
}
initialized = true;
extStrings.clear();
extStrings.reserve(exts.size());
for (const auto& ext : exts) {
extStrings.emplace_back(MG_Util::ConvertGLExtToString(ext));
}
return (const GLubyte*)extStrings[index].c_str();
}
void GetIntegeri_v(GLenum target, GLuint index, GLint* data) {
if (!data) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "data pointer cannot be null"));
return;
}
auto getIntegeri = MG_Backend::gBackendFunctionsTable.GL.GetIntegeri_v;
if (!getIntegeri) {
*data = 0;
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Backend does not support indexed integer queries."));
return;
}
getIntegeri(target, index, data);
}
void GetInteger64i_v(GLenum target, GLuint index, GLint64* data) {
if (!data) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "data pointer cannot be null"));
return;
}
auto getInteger64i = MG_Backend::gBackendFunctionsTable.GL.GetInteger64i_v;
if (!getInteger64i) {
*data = 0;
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Backend does not support indexed integer queries."));
return;
}
getInteger64i(target, index, data);
}
void GetInteger64v(GLenum pname, GLint64* data) {
MGLOG_D("glGetInteger64v, pname: %s", MG_Util::ConvertGLEnumToString(pname).c_str());
if (!data) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "data pointer cannot be null"));
return;
}
switch (pname) {
case GL_MAX_SHADER_STORAGE_BLOCK_SIZE:
if (MG_Backend::pActiveBackendObject) {
*data = static_cast<GLint64>(
MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxShaderStorageBlockSize);
} else {
*data = static_cast<GLint64>(MG_Backend::DynamicBackendParameters{}.MaxShaderStorageBlockSize);
}
return;
case GL_SUBGROUP_SIZE_KHR:
case GL_SUBGROUP_SUPPORTED_STAGES_KHR:
case GL_SUBGROUP_SUPPORTED_FEATURES_KHR:
case GL_SUBGROUP_QUAD_ALL_STAGES_KHR: {
GLint params = 0;
GetIntegerv(pname, &params);
*data = static_cast<GLint64>(params);
return;
}
default:
*data = 0;
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
std::format("Unsupported integer64 pname {}.",
MG_Util::ConvertGLEnumToString(pname))));
return;
}
}
void GetIntegerv(GLenum pname, GLint* params) {
MGLOG_D("glGetIntegerv, pname: %s", MG_Util::ConvertGLEnumToString(pname).c_str());
if (!params) {
MG_State::pGLContext->RecordError(
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;
}
@@ -137,6 +206,7 @@ namespace MobileGL::MG_Impl::GLImpl {
return;
}
const auto& rendererInfo = activeBackendObject->GetRendererInfo();
const auto& dynamicParameters = activeBackendObject->GetDynamicParameters();
switch (pname) {
case GL_ACTIVE_TEXTURE:
@@ -146,9 +216,9 @@ namespace MobileGL::MG_Impl::GLImpl {
*params = 0; // TODO
break;
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)
*params = obj->GetExternalIndex();
*params = (GLint)obj->GetExternalIndex();
else
*params = 0;
break;
@@ -162,31 +232,39 @@ namespace MobileGL::MG_Impl::GLImpl {
case GL_BLEND_DST_ALPHA: {
BlendFactor srcRGB, dstRGB, srcAlpha, dstAlpha;
MG_State::pGLContext->GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha);
*params = static_cast<GLint>(dstAlpha);
*params = static_cast<GLint>(MG_Util::ConvertBlendFactorToGLEnum(dstAlpha));
break;
}
case GL_BLEND_DST_RGB: {
BlendFactor srcRGB, dstRGB, srcAlpha, dstAlpha;
MG_State::pGLContext->GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha);
*params = static_cast<GLint>(dstRGB);
*params = static_cast<GLint>(MG_Util::ConvertBlendFactorToGLEnum(dstRGB));
break;
}
case GL_BLEND_EQUATION_RGB:
*params = 0; // TODO
case GL_BLEND_EQUATION_RGB: {
BlendEquation colorEquation = BlendEquation::Add;
BlendEquation alphaEquation = BlendEquation::Add;
MG_State::pGLContext->GetBlendEquation(colorEquation, alphaEquation);
*params = static_cast<GLint>(MG_Util::ConvertBlendEquationToGLEnum(colorEquation));
break;
case GL_BLEND_EQUATION_ALPHA:
*params = 0; // TODO
}
case GL_BLEND_EQUATION_ALPHA: {
BlendEquation colorEquation = BlendEquation::Add;
BlendEquation alphaEquation = BlendEquation::Add;
MG_State::pGLContext->GetBlendEquation(colorEquation, alphaEquation);
*params = static_cast<GLint>(MG_Util::ConvertBlendEquationToGLEnum(alphaEquation));
break;
}
case GL_BLEND_SRC_ALPHA: {
BlendFactor srcRGB, dstRGB, srcAlpha, dstAlpha;
MG_State::pGLContext->GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha);
*params = static_cast<GLint>(srcAlpha);
*params = static_cast<GLint>(MG_Util::ConvertBlendFactorToGLEnum(srcAlpha));
break;
}
case GL_BLEND_SRC_RGB: {
BlendFactor srcRGB, dstRGB, srcAlpha, dstAlpha;
MG_State::pGLContext->GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha);
*params = static_cast<GLint>(srcRGB);
*params = static_cast<GLint>(MG_Util::ConvertBlendFactorToGLEnum(srcRGB));
break;
}
case GL_COLOR_CLEAR_VALUE:
@@ -206,42 +284,60 @@ namespace MobileGL::MG_Impl::GLImpl {
case GL_COMPRESSED_TEXTURE_FORMATS:
*params = 0; // TODO
break;
case GL_SUBGROUP_SIZE_KHR:
*params = static_cast<GLint>(dynamicParameters.SubgroupSize);
break;
case GL_SUBGROUP_SUPPORTED_STAGES_KHR:
*params = static_cast<GLint>(dynamicParameters.SubgroupSupportedStages);
break;
case GL_SUBGROUP_SUPPORTED_FEATURES_KHR:
*params = static_cast<GLint>(dynamicParameters.SubgroupSupportedFeatures);
break;
case GL_SUBGROUP_QUAD_ALL_STAGES_KHR:
*params = dynamicParameters.SubgroupQuadOperationsInAllStages ? GL_TRUE : GL_FALSE;
break;
case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS:
*params = 0; // TODO
*params = 16; // TODO: use backend value
break;
case GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS:
*params = 0; // TODO
*params = 96; // TODO: use backend value
break;
case GL_MAX_COMPUTE_UNIFORM_BLOCKS:
*params = 0; // TODO
*params = 14; // TODO: use backend value
break;
case GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS:
*params = 0; // TODO
*params = MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; // TODO: use backend value
break;
case GL_MAX_COMPUTE_UNIFORM_COMPONENTS:
*params = 0; // TODO
*params = 1024 * 4; // TODO: use backend value
break;
case GL_MAX_COMPUTE_ATOMIC_COUNTERS:
*params = 0; // TODO
*params = 8; // TODO: use backend value
break;
case GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS:
*params = 0; // TODO
*params = 8; // TODO: use backend value
break;
case GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS:
*params = 0; // TODO
*params = 1024 * 228; // TODO: use backend value
break;
case GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS:
*params = 0; // TODO
*params = 1024; // TODO: use backend value
break;
case GL_MAX_COMPUTE_WORK_GROUP_COUNT:
*params = 0; // TODO
GetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &params[0]);
GetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &params[1]);
GetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &params[2]);
break;
case GL_MAX_COMPUTE_WORK_GROUP_SIZE:
*params = 0; // TODO
GetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &params[0]);
GetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &params[1]);
GetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &params[2]);
break;
case GL_DISPATCH_INDIRECT_BUFFER_BINDING:
*params = 0; // TODO
case GL_DISPATCH_INDIRECT_BUFFER_BINDING: {
auto& obj = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DispatchIndirect).GetBoundObject();
*params = obj ? static_cast<GLint>(obj->GetExternalIndex()) : 0;
break;
}
case GL_MAX_DEBUG_GROUP_STACK_DEPTH:
*params = 0; // TODO
break;
@@ -254,16 +350,22 @@ namespace MobileGL::MG_Impl::GLImpl {
case GL_CULL_FACE:
*params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::CullFace) ? GL_TRUE : GL_FALSE;
break;
case GL_CULL_FACE_MODE:
*params = static_cast<GLint>(MG_Util::ConvertCullFaceModeToGLEnum(MG_State::pGLContext->GetCullFaceMode()));
break;
case GL_FRONT_FACE:
*params = static_cast<GLint>(MG_Util::ConvertFrontFaceModeToGLEnum(MG_State::pGLContext->GetFrontFaceMode()));
break;
case GL_CURRENT_PROGRAM: {
const auto& currentProgram = MG_State::pGLContext->GetCurrentProgram();
*params = currentProgram ? currentProgram->GetExternalIndex() : 0;
*params = currentProgram ? (GLint)currentProgram->GetExternalIndex() : 0;
break;
}
case GL_DEPTH_CLEAR_VALUE:
*params = MG_State::pGLContext->GetClearDepth();
*params = (GLint)MG_State::pGLContext->GetClearDepth();
break;
case GL_DEPTH_FUNC:
*params = MG_Util::ConvertDepthTestFuncToGLEnum(MG_State::pGLContext->GetDepthFunc());
*params = (GLint)MG_Util::ConvertDepthTestFuncToGLEnum(MG_State::pGLContext->GetDepthFunc());
break;
case GL_DEPTH_RANGE:
*params = 0; // TODO
@@ -288,12 +390,12 @@ namespace MobileGL::MG_Impl::GLImpl {
break;
case GL_DRAW_FRAMEBUFFER_BINDING: {
const auto& FBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
*params = FBO ? FBO->GetExternalIndex() : 0;
*params = FBO ? (GLint)FBO->GetExternalIndex() : 0;
break;
}
case GL_READ_FRAMEBUFFER_BINDING: {
const auto& FBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
*params = FBO ? FBO->GetExternalIndex() : 0;
*params = FBO ? (GLint)FBO->GetExternalIndex() : 0;
break;
}
case GL_ELEMENT_ARRAY_BUFFER_BINDING: {
@@ -302,7 +404,7 @@ namespace MobileGL::MG_Impl::GLImpl {
break;
}
const auto& bufferObject = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Index).GetBoundObject();
*params = bufferObject ? bufferObject->GetExternalIndex() : 0;
*params = bufferObject ? (GLint)bufferObject->GetExternalIndex() : 0;
break;
}
case GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
@@ -428,6 +530,22 @@ namespace MobileGL::MG_Impl::GLImpl {
case GL_MAX_GEOMETRY_UNIFORM_COMPONENTS:
*params = 1024 * 4; // TODO
break;
case GL_MAX_IMAGE_UNITS:
*params = MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; // TODO: use backend value
break;
case GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS:
*params = MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS +
MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS;
break;
case GL_MAX_COMBINED_IMAGE_UNIFORMS:
*params = MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; // TODO: use backend value
break;
case GL_MAX_IMAGE_SAMPLES:
*params = 0; // multisampled image load/store is not exposed by the DirectGLES frontend
break;
case GL_MAX_COMPUTE_IMAGE_UNIFORMS:
*params = MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS; // TODO: use backend value
break;
case GL_MAX_INTEGER_SAMPLES:
*params = 16; // TODO
break;
@@ -573,6 +691,11 @@ namespace MobileGL::MG_Impl::GLImpl {
case GL_PIXEL_UNPACK_BUFFER_BINDING:
*params = 0; // TODO
break;
case GL_PARAMETER_BUFFER_BINDING_ARB: {
auto& obj = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();
*params = obj ? static_cast<GLint>(obj->GetExternalIndex()) : 0;
break;
}
case GL_POINT_FADE_THRESHOLD_SIZE:
*params = 0; // TODO
break;
@@ -589,11 +712,16 @@ namespace MobileGL::MG_Impl::GLImpl {
*params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ProgramPointSize) ? GL_TRUE : GL_FALSE;
break;
case GL_PROVOKING_VERTEX:
*params = 0; // TODO
*params = static_cast<GLint>(
MG_Util::ConvertProvokingVertexModeToGLEnum(MG_State::pGLContext->GetProvokingVertexMode()));
break;
case GL_POINT_SIZE:
*params = 0; // TODO
break;
case GL_POLYGON_MODE:
params[0] = GL_FILL;
params[1] = GL_FILL;
break;
case GL_POINT_SIZE_GRANULARITY:
*params = 0; // TODO
break;
@@ -661,11 +789,13 @@ namespace MobileGL::MG_Impl::GLImpl {
case GL_SHADER_COMPILER:
*params = 0; // TODO
break;
case GL_SHADER_STORAGE_BUFFER_BINDING:
*params = 0; // TODO
case GL_SHADER_STORAGE_BUFFER_BINDING: {
auto& obj = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::ShaderStorage).GetBoundObject();
*params = obj ? static_cast<GLint>(obj->GetExternalIndex()) : 0;
break;
}
case GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT:
*params = 0; // TODO
*params = 256; // TODO: use backend value
break;
case GL_SHADER_STORAGE_BUFFER_START:
*params = 0; // TODO
@@ -886,7 +1016,7 @@ namespace MobileGL::MG_Impl::GLImpl {
default:
MGLOG_E("glGetIntegerv: Invalid enum %s (0x%X)", MG_Util::ConvertGLEnumToString(pname).c_str(), pname);
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)));
break;
@@ -894,7 +1024,10 @@ namespace MobileGL::MG_Impl::GLImpl {
}
GLenum GetError() {
ErrorCode errorCode = MG_State::pGLContext->PopGLError().value_or(Error{ErrorCode::NoError, nullptr}).code;
return MG_Util::ConvertErrorCodeToGLEnum(errorCode);
auto error = MG_State::pGLContext->PopGLError();
if (!error || !error->get()) {
return GL_NO_ERROR;
}
return MG_Util::ConvertErrorCodeToGLEnum(error->get()->code);
}
} // namespace MobileGL::MG_Impl::GLImpl
@@ -14,5 +14,8 @@ namespace MobileGL::MG_Impl::GLImpl {
const GLubyte* GetString(GLenum name);
const GLubyte* GetStringi(GLenum name, GLuint index);
void GetIntegerv(GLenum pname, GLint* params);
void GetInteger64v(GLenum pname, GLint64* data);
void GetIntegeri_v(GLenum target, GLuint index, GLint* data);
void GetInteger64i_v(GLenum target, GLuint index, GLint64* data);
GLenum GetError();
} // namespace MobileGL::MG_Impl::GLImpl
File diff suppressed because it is too large Load Diff
+97 -58
View File
@@ -9,61 +9,100 @@
#pragma once
#include <Includes.h>
namespace MobileGL {
namespace MG_Impl::GLImpl {
void AttachShader(GLuint program, GLuint shader);
void BindAttribLocation(GLuint program, GLuint index, const GLchar* name);
void CompileShader(GLuint shader);
GLuint CreateProgram(void);
GLuint CreateShader(GLenum type);
void DeleteProgram(GLuint program);
void DeleteShader(GLuint shader);
void DetachShader(GLuint program, GLuint shader);
void GetActiveAttrib(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLint* size, GLenum* type,
GLchar* name);
void GetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLint* size, GLenum* type,
GLchar* name);
void GetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders);
GLint GetAttribLocation(GLuint program, const GLchar* name);
void GetProgramiv(GLuint program, GLenum pname, GLint* params);
void GetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
void GetShaderiv(GLuint shader, GLenum pname, GLint* params);
void GetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
void GetShaderSource(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* source);
GLint GetUniformLocation(GLuint program, const GLchar* name);
void GetUniformfv(GLuint program, GLint location, GLfloat* params);
void GetUniformiv(GLuint program, GLint location, GLint* params);
GLboolean IsProgram(GLuint program);
GLboolean IsShader(GLuint shader);
void LinkProgram(GLuint program);
void ShaderSource(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length);
void UseProgram(GLuint program);
void Uniform1f(GLint location, GLfloat v0);
void Uniform2f(GLint location, GLfloat v0, GLfloat v1);
void Uniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
void Uniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
void Uniform1i(GLint location, GLint v0);
void Uniform2i(GLint location, GLint v0, GLint v1);
void Uniform3i(GLint location, GLint v0, GLint v1, GLint v2);
void Uniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
void Uniform1fv(GLint location, GLsizei count, const GLfloat* value);
void Uniform2fv(GLint location, GLsizei count, const GLfloat* value);
void Uniform3fv(GLint location, GLsizei count, const GLfloat* value);
void Uniform4fv(GLint location, GLsizei count, const GLfloat* value);
void Uniform1iv(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 Uniform4iv(GLint location, GLsizei count, const GLint* value);
void UniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
void UniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
void UniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
GLuint GetUniformBlockIndex(GLuint program, const GLchar* uniformBlockName);
void UniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
void GetActiveUniformBlockiv(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params);
void GetActiveUniformBlockName(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length,
GLchar* uniformBlockName);
void BindFragDataLocation(GLuint program, GLuint colorNumber, const char* name);
GLint GetFragDataLocation(GLuint program, const char* name);
void ValidateProgram(GLuint program);
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
namespace MobileGL::MG_Impl::GLImpl {
void AttachShader(GLuint program, GLuint shader);
void BindAttribLocation(GLuint program, GLuint index, const GLchar* name);
void CompileShader(GLuint shader);
GLuint CreateProgram(void);
GLuint CreateShader(GLenum type);
void DeleteProgram(GLuint program);
void DeleteShader(GLuint shader);
void DetachShader(GLuint program, GLuint shader);
void GetActiveAttrib(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLint* size, GLenum* type,
GLchar* name);
void GetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLint* size, GLenum* type,
GLchar* name);
void GetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders);
GLint GetAttribLocation(GLuint program, const GLchar* name);
void GetProgramiv(GLuint program, GLenum pname, GLint* params);
void GetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
void GetShaderiv(GLuint shader, GLenum pname, GLint* params);
void GetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
void GetShaderSource(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* source);
GLint GetUniformLocation(GLuint program, const GLchar* name);
void GetUniformfv(GLuint program, GLint location, GLfloat* params);
void GetUniformiv(GLuint program, GLint location, GLint* params);
GLboolean IsProgram(GLuint program);
GLboolean IsShader(GLuint shader);
void LinkProgram(GLuint program);
void ShaderSource(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length);
void UseProgram(GLuint program);
void Uniform1f(GLint location, GLfloat v0);
void Uniform2f(GLint location, GLfloat v0, GLfloat v1);
void Uniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
void Uniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
void Uniform1i(GLint location, GLint v0);
void Uniform2i(GLint location, GLint v0, GLint v1);
void Uniform3i(GLint location, GLint v0, GLint v1, GLint v2);
void Uniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
void Uniform1ui(GLint location, GLuint v0);
void Uniform1fv(GLint location, GLsizei count, const GLfloat* value);
void Uniform2fv(GLint location, GLsizei count, const GLfloat* value);
void Uniform3fv(GLint location, GLsizei count, const GLfloat* value);
void Uniform4fv(GLint location, GLsizei count, const GLfloat* value);
void Uniform1iv(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 Uniform4iv(GLint location, GLsizei count, const GLint* value);
void Uniform1uiv(GLint location, GLsizei count, const GLuint* value);
void UniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
void UniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
void UniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
void ProgramUniform1f(GLuint program, GLint location, GLfloat v0);
void ProgramUniform2f(GLuint program, GLint location, GLfloat v0, GLfloat v1);
void ProgramUniform3f(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
void ProgramUniform4f(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
void ProgramUniform1i(GLuint program, GLint location, GLint v0);
void ProgramUniform2i(GLuint program, GLint location, GLint v0, GLint v1);
void ProgramUniform3i(GLuint program, GLint location, GLint v0, GLint v1, GLint v2);
void ProgramUniform4i(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
void ProgramUniform1ui(GLuint program, GLint location, GLuint v0);
void ProgramUniform2ui(GLuint program, GLint location, GLuint v0, GLuint v1);
void ProgramUniform3ui(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2);
void ProgramUniform4ui(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);
void ProgramUniform1fv(GLuint program, GLint location, GLsizei count, const GLfloat* value);
void ProgramUniform2fv(GLuint program, GLint location, GLsizei count, const GLfloat* value);
void ProgramUniform3fv(GLuint program, GLint location, GLsizei count, const GLfloat* value);
void ProgramUniform4fv(GLuint program, GLint location, GLsizei count, const GLfloat* value);
void ProgramUniform1iv(GLuint program, GLint location, GLsizei count, const GLint* value);
void ProgramUniform2iv(GLuint program, GLint location, GLsizei count, const GLint* value);
void ProgramUniform3iv(GLuint program, GLint location, GLsizei count, const GLint* value);
void ProgramUniform4iv(GLuint program, GLint location, GLsizei count, const GLint* value);
void ProgramUniform1uiv(GLuint program, GLint location, GLsizei count, const GLuint* value);
void ProgramUniform2uiv(GLuint program, GLint location, GLsizei count, const GLuint* value);
void ProgramUniform3uiv(GLuint program, GLint location, GLsizei count, const GLuint* value);
void ProgramUniform4uiv(GLuint program, GLint location, GLsizei count, const GLuint* value);
void ProgramUniformMatrix2fv(GLuint program, GLint location, GLsizei count, GLboolean transpose,
const GLfloat* value);
void ProgramUniformMatrix3fv(GLuint program, GLint location, GLsizei count, GLboolean transpose,
const GLfloat* value);
void ProgramUniformMatrix4fv(GLuint program, GLint location, GLsizei count, GLboolean transpose,
const GLfloat* value);
GLuint GetUniformBlockIndex(GLuint program, const GLchar* uniformBlockName);
void UniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
void GetActiveUniformBlockiv(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params);
void GetActiveUniformBlockName(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length,
GLchar* uniformBlockName);
void BindFragDataLocation(GLuint program, GLuint colorNumber, const char* name);
GLint GetFragDataLocation(GLuint program, const char* name);
void GetProgramInterfaceiv(GLuint program, GLenum programInterface, GLenum pname, GLint* params);
GLuint GetProgramResourceIndex(GLuint program, GLenum programInterface, const GLchar* name);
void GetProgramResourceName(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize,
GLsizei* length, GLchar* name);
void GetProgramResourceiv(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount,
const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params);
GLint GetProgramResourceLocation(GLuint program, GLenum programInterface, const GLchar* name);
GLint GetProgramResourceLocationIndex(GLuint program, GLenum programInterface, const GLchar* name);
void ShaderStorageBlockBinding(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding);
void ValidateProgram(GLuint program);
} // namespace MobileGL::MG_Impl::GLImpl
File diff suppressed because it is too large Load Diff
@@ -9,47 +9,50 @@
#pragma once
#include <Includes.h>
namespace MobileGL {
namespace MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
void BlendFuncSeparatei(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
void Disablei(GLenum target, GLuint index);
void Enablei(GLenum target, GLuint index);
void BlendFunc(GLenum sfactor, GLenum dfactor);
void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);
void StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
void StencilOp(GLenum fail, GLenum zfail, GLenum zpass);
void StencilMaskSeparate(GLenum face, GLuint mask);
void StencilMask(GLuint mask);
void StencilFuncSeparate(GLenum face, 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 SampleCoverage(GLfloat value, GLboolean invert);
void PolygonOffset(GLfloat factor, GLfloat units);
void PolygonMode(GLenum face, GLenum mode);
void PointSize(GLfloat size);
void PointParameterf(GLenum pname, GLfloat param);
void PointParameteri(GLenum pname, GLint param);
void PixelStorei(GLenum pname, GLint param);
void LogicOp(GLenum opcode);
void LineWidth(GLfloat width);
GLboolean IsEnabledi(GLenum target, GLuint index);
GLboolean IsEnabled(GLenum cap);
void Hint(GLenum target, GLenum mode);
void FrontFace(GLenum mode);
void Enable(GLenum cap);
void Disable(GLenum cap);
void DepthRange(GLclampd near_val, GLclampd far_val);
void DepthMask(GLboolean flag);
void DepthFunc(GLenum func);
void CullFace(GLenum mode);
void ColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
void ClampColor(GLenum target, GLenum clamp);
void BlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
void BlendEquation(GLenum mode);
void BlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
void ClearStencil(GLint s);
void ClearDepth(GLclampd depth);
void ClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
namespace MobileGL::MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
void BlendEquationi(GLuint buf, GLenum mode);
void BlendEquationSeparatei(GLuint buf, GLenum modeRGB, GLenum modeAlpha);
void BlendFunci(GLuint buf, GLenum src, GLenum dst);
void BlendFuncSeparatei(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
void Disablei(GLenum target, GLuint index);
void Enablei(GLenum target, GLuint index);
void BlendFunc(GLenum sfactor, GLenum dfactor);
void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);
void StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
void StencilOp(GLenum fail, GLenum zfail, GLenum zpass);
void StencilMaskSeparate(GLenum face, GLuint mask);
void StencilMask(GLuint mask);
void StencilFuncSeparate(GLenum face, 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 SampleCoverage(GLfloat value, GLboolean invert);
void PolygonOffset(GLfloat factor, GLfloat units);
void PolygonMode(GLenum face, GLenum mode);
void PointSize(GLfloat size);
void PointParameterf(GLenum pname, GLfloat param);
void PointParameteri(GLenum pname, GLint param);
void PixelStorei(GLenum pname, GLint param);
void LogicOp(GLenum opcode);
void LineWidth(GLfloat width);
GLboolean IsEnabledi(GLenum target, GLuint index);
GLboolean IsEnabled(GLenum cap);
void Hint(GLenum target, GLenum mode);
void FrontFace(GLenum mode);
void ProvokingVertex(GLenum mode);
void Enable(GLenum cap);
void Disable(GLenum cap);
void DepthRange(GLclampd near_val, GLclampd far_val);
void DepthMask(GLboolean flag);
void DepthFunc(GLenum func);
void CullFace(GLenum mode);
void ColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
void ClampColor(GLenum target, GLenum clamp);
void BlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
void BlendEquation(GLenum mode);
void BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha);
void BlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
void ClearStencil(GLint s);
void ClearDepth(GLclampd depth);
void ClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
} // namespace MobileGL::MG_Impl::GLImpl
+234 -229
View File
@@ -12,254 +12,259 @@
#include <MG_Util/Converters/GLToMG/TextureEnumConverter.h>
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
namespace MobileGL {
namespace MG_Impl::GLImpl {
void SetSamplerParam_State(GLuint sampler, GLenum pname, const void* param, bool isFloat, bool isInteger) {
namespace MobileGL::MG_Impl::GLImpl {
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) {
MGLOG_D("BindSampler_State: unit = %u, sampler = %u", unit, 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;
auto samplerObj = MG_State::pGLContext->GetSamplerObject(sampler);
if (!samplerObj) {
samplerObj = MG_State::pGLContext->CreateSamplerObject(sampler); // for compatibility
Bool doesSamplerObjectCreated = MG_State::pGLContext->ValidateSamplerObject(sampler);
if (!doesSamplerObjectCreated) {
MG_State::pGLContext->CreateSamplerObject(sampler);
}
if (!SamplerImpl::ValidateSamplerObject(sampler)) return;
auto& samplerObject = MG_State::pGLContext->GetSamplerObject(sampler);
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, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "SetSamplerParam_State",
"Invalid pname for sampler parameter"));
}
textureUnit.SetSamplerObject(MG_State::pGLContext->GetSamplerObject(sampler));
}
}
void BindSamplers_State(GLuint first, GLsizei count, const GLuint* samplers) {
if (count < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "BindSamplers", "count must be non-negative"));
return;
}
void GetSamplerParam_State(GLuint sampler, GLenum pname, void* params, bool isFloat, bool isInteger) {
if (!SamplerImpl::ValidateSamplerName(sampler)) return;
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"));
}
for (GLsizei i = 0; i < count; ++i) {
BindSampler_State(first + i, samplers ? samplers[i] : 0);
}
}
GLboolean IsSampler_State(GLuint sampler) {
return MG_State::pGLContext->ValidateSamplerObject(sampler) ? GL_TRUE : GL_FALSE;
}
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
void GetSamplerParameteriv(GLuint sampler, GLenum pname, GLint* params) {
GetSamplerParam_State(sampler, pname, params, false, 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,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GenSamplers", "count must be non-negative"));
return;
}
void SamplerParameterIuiv(GLuint sampler, GLenum pname, const GLuint* param) {
SetSamplerParam_State(sampler, pname, param, false, true);
}
auto names = MG_State::pGLContext->GenSamplerNames(count);
for (GLsizei i = 0; i < count; ++i) {
samplers[i] = names[i];
}
}
void SamplerParameterIiv(GLuint sampler, GLenum pname, const GLint* param) {
SetSamplerParam_State(sampler, pname, param, false, true);
}
void DeleteSamplers_State(GLsizei count, const GLuint* samplers) {
if (count < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteSamplers", "count must be non-negative"));
return;
}
void SamplerParameteriv(GLuint sampler, GLenum pname, const GLint* param) {
SetSamplerParam_State(sampler, pname, param, false, false);
}
for (GLsizei i = 0; i < count; ++i) {
if (samplers[i] != 0) {
MG_State::pGLContext->MarkSamplerObjectForDeletion(samplers[i]);
}
}
}
void SamplerParameterfv(GLuint sampler, GLenum pname, const GLfloat* param) {
SetSamplerParam_State(sampler, pname, param, true, false);
}
void CreateSamplers_State(GLsizei n, GLuint* samplers) {
if (n < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GenSamplers", "count must be non-negative"));
return;
}
void SamplerParameteri(GLuint sampler, GLenum pname, GLint param) {
SamplerParameteriv(sampler, pname, &param);
}
auto names = MG_State::pGLContext->GenSamplerNames(n);
for (GLsizei i = 0; i < n; ++i) {
samplers[i] = names[i];
MG_State::pGLContext->CreateSamplerObject(names[i]);
}
}
void SamplerParameterf(GLuint sampler, GLenum pname, GLfloat param) {
SamplerParameterfv(sampler, pname, &param);
}
void BindSampler_State(GLuint unit, GLuint sampler) {
if (unit >= MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "BindSampler", "texture unit out of range"));
return;
}
GLboolean IsSampler(GLuint sampler) {
return IsSampler_State(sampler);
}
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);
if (sampler == 0) {
textureUnit.SetSamplerObject(nullptr);
} else {
if (!SamplerImpl::ValidateSamplerName(sampler)) return;
auto samplerObject = MG_State::pGLContext->GetSamplerObject(sampler);
if (!samplerObject) {
samplerObject = MG_State::pGLContext->CreateSamplerObject(sampler);
}
void GetSamplerParameterIuiv(GLuint sampler, GLenum pname, GLuint* params) {
GetSamplerParam_State(sampler, pname, params, false, true);
}
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) {
if (count < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "BindSamplers", "count must be non-negative"));
return;
}
void GetSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat* params) {
GetSamplerParam_State(sampler, pname, params, true, false);
}
for (GLsizei i = 0; i < count; ++i) {
BindSampler_State(first + i, samplers ? samplers[i] : 0);
}
}
void GenSamplers(GLsizei count, GLuint* samplers) {
GenSamplers_State(count, samplers);
}
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
void GetSamplerParameteriv(GLuint sampler, GLenum pname, GLint* params) {
GetSamplerParam_State(sampler, pname, params, false, false);
}
void DeleteSamplers(GLsizei count, const GLuint* samplers) {
DeleteSamplers_State(count, samplers);
}
void SamplerParameterIuiv(GLuint sampler, GLenum pname, const GLuint* param) {
SetSamplerParam_State(sampler, pname, param, false, true);
}
void CreateSamplers(GLsizei n, GLuint* samplers) {
CreateSamplers_State(n, samplers);
}
void SamplerParameterIiv(GLuint sampler, GLenum pname, const GLint* param) {
SetSamplerParam_State(sampler, pname, param, false, true);
}
void BindSamplers(GLuint first, GLsizei count, const GLuint* samplers) {
BindSamplers_State(first, count, samplers);
}
void SamplerParameteriv(GLuint sampler, GLenum pname, const GLint* param) {
SetSamplerParam_State(sampler, pname, param, false, false);
}
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
void BindSampler(GLuint unit, GLuint sampler) {
BindSampler_State(unit, sampler);
}
} // namespace MobileGL::MG_Impl::GLImpl
+19 -21
View File
@@ -9,24 +9,22 @@
#pragma once
#include <Includes.h>
namespace MobileGL {
namespace MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
void GetSamplerParameteriv(GLuint sampler, GLenum pname, GLint* params);
void SamplerParameterIuiv(GLuint sampler, GLenum pname, const GLuint* param);
void SamplerParameterIiv(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 SamplerParameteri(GLuint sampler, GLenum pname, GLint param);
void SamplerParameterf(GLuint sampler, GLenum pname, GLfloat param);
GLboolean IsSampler(GLuint sampler);
void GetSamplerParameterIuiv(GLuint sampler, GLenum pname, GLuint* params);
void GetSamplerParameterIiv(GLuint sampler, GLenum pname, GLint* params);
void GetSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat* params);
void GenSamplers(GLsizei count, GLuint* samplers);
void DeleteSamplers(GLsizei count, const GLuint* samplers);
void CreateSamplers(GLsizei n, GLuint* samplers);
void BindSamplers(GLuint first, GLsizei count, const GLuint* samplers);
void BindSampler(GLuint unit, GLuint sampler);
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
namespace MobileGL::MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
void GetSamplerParameteriv(GLuint sampler, GLenum pname, GLint* params);
void SamplerParameterIuiv(GLuint sampler, GLenum pname, const GLuint* param);
void SamplerParameterIiv(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 SamplerParameteri(GLuint sampler, GLenum pname, GLint param);
void SamplerParameterf(GLuint sampler, GLenum pname, GLfloat param);
GLboolean IsSampler(GLuint sampler);
void GetSamplerParameterIuiv(GLuint sampler, GLenum pname, GLuint* params);
void GetSamplerParameterIiv(GLuint sampler, GLenum pname, GLint* params);
void GetSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat* params);
void GenSamplers(GLsizei count, GLuint* samplers);
void DeleteSamplers(GLsizei count, const GLuint* samplers);
void CreateSamplers(GLsizei n, GLuint* samplers);
void BindSamplers(GLuint first, GLsizei count, const GLuint* samplers);
void BindSampler(GLuint unit, GLuint sampler);
} // namespace MobileGL::MG_Impl::GLImpl
+113 -117
View File
@@ -11,126 +11,122 @@
#include <MG_State/GLState/ErrorState/Error.h>
#include <MG_Util/Converters/GLToMG/TextureEnumConverter.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace SamplerImpl {
Bool ValidateSamplerName(GLuint sampler) {
if (!MG_State::pGLContext->ValidateSamplerName(sampler)) {
namespace MobileGL::MG_Impl::GLImpl::SamplerImpl {
Bool ValidateSamplerName(GLuint 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(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateSamplerName",
std::format("Invalid sampler name {}", sampler)));
MakeUnique<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));
}
Bool ValidateSamplerObject(GLuint sampler) {
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
}
} // namespace MobileGL::MG_Impl::GLImpl::SamplerImpl
+7 -9
View File
@@ -10,12 +10,10 @@
#include <Includes.h>
#include <MG_State/GLState/SamplerState/SamplerObject.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace SamplerImpl {
Bool ValidateSamplerName(GLuint sampler);
Bool ValidateSamplerObject(GLuint sampler);
Bool ValidateSamplerParam(GLenum pname, GLenum param);
Bool ValidateSamplerFloatParam(GLenum pname, GLfloat param);
Bool ValidateSamplerIntParam(GLenum pname, GLint param);
} // namespace SamplerImpl
} // namespace MobileGL::MG_Impl::GLImpl
namespace MobileGL::MG_Impl::GLImpl::SamplerImpl {
Bool ValidateSamplerName(GLuint sampler);
Bool ValidateSamplerObject(GLuint sampler);
Bool ValidateSamplerParam(GLenum pname, GLenum param);
Bool ValidateSamplerFloatParam(GLenum pname, GLfloat param);
Bool ValidateSamplerIntParam(GLenum pname, GLint param);
} // namespace MobileGL::MG_Impl::GLImpl::SamplerImpl
+106 -23
View File
@@ -8,38 +8,121 @@
#include "GL_Sync.h"
#include "MG_State/GLState/Core.h"
#include <MG_State/GLState/Core.h>
#include <MG_State/GLState/ErrorState/Error.h>
namespace MobileGL {
namespace MG_Impl::GLImpl {
GLsync FenceSync_Backend(GLenum condition, GLbitfield flags) {
return 0;
namespace MobileGL::MG_Impl::GLImpl {
namespace {
struct SyncObject {
GLenum Condition = GL_SYNC_GPU_COMMANDS_COMPLETE;
GLbitfield Flags = 0;
};
UnorderedMap<GLsync, UniquePtr<SyncObject>> g_syncObjects;
void RecordSyncError(const char* funcName, ErrorCode code, String message) {
MG_State::pGLContext->RecordError(code,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", funcName, Move(message)));
}
GLenum ClientWaitSync_Backend(GLsync sync, GLbitfield flags, GLuint64 timeout) {
return 0;
SyncObject* GetSyncObject(GLsync sync, const char* funcName) {
auto it = g_syncObjects.find(sync);
if (sync == nullptr || it == g_syncObjects.end()) {
RecordSyncError(funcName, ErrorCode::InvalidValue, "Sync object is not valid.");
return nullptr;
}
return it->second.get();
}
} // namespace
GLsync FenceSync(GLenum condition, GLbitfield flags) {
if (condition != GL_SYNC_GPU_COMMANDS_COMPLETE) {
RecordSyncError("FenceSync", ErrorCode::InvalidEnum, "Condition must be GL_SYNC_GPU_COMMANDS_COMPLETE.");
return nullptr;
}
if (flags != 0) {
RecordSyncError("FenceSync", ErrorCode::InvalidValue, "Flags must be zero.");
return nullptr;
}
void DeleteSync_Backend(GLsync sync) {}
auto syncObject = MakeUnique<SyncObject>();
syncObject->Condition = condition;
syncObject->Flags = flags;
GLsync handle = reinterpret_cast<GLsync>(syncObject.get());
g_syncObjects[handle] = Move(syncObject);
return handle;
}
GLsync FenceSync_State(GLenum condition, GLbitfield flags) {
return 0;
GLboolean IsSync(GLsync sync) {
return g_syncObjects.find(sync) != g_syncObjects.end() ? GL_TRUE : GL_FALSE;
}
GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
(void)timeout;
if ((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) != 0) {
RecordSyncError("ClientWaitSync", ErrorCode::InvalidValue,
"Flags can only contain GL_SYNC_FLUSH_COMMANDS_BIT.");
return GL_WAIT_FAILED;
}
if (!GetSyncObject(sync, "ClientWaitSync")) return GL_WAIT_FAILED;
return GL_ALREADY_SIGNALED;
}
void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
if (flags != 0) {
RecordSyncError("WaitSync", ErrorCode::InvalidValue, "Flags must be zero.");
return;
}
if (timeout != GL_TIMEOUT_IGNORED) {
RecordSyncError("WaitSync", ErrorCode::InvalidValue, "Timeout must be GL_TIMEOUT_IGNORED.");
return;
}
(void)GetSyncObject(sync, "WaitSync");
}
void DeleteSync(GLsync sync) {
if (sync == nullptr) return;
auto it = g_syncObjects.find(sync);
if (it == g_syncObjects.end()) {
RecordSyncError("DeleteSync", ErrorCode::InvalidValue, "Sync object is not valid.");
return;
}
g_syncObjects.erase(it);
}
void GetSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values) {
if (bufSize < 0) {
RecordSyncError("GetSynciv", ErrorCode::InvalidValue, "bufSize must be non-negative.");
return;
}
GLenum ClientWaitSync_State(GLsync sync, GLbitfield flags, GLuint64 timeout) {
return 0;
auto* syncObject = GetSyncObject(sync, "GetSynciv");
if (!syncObject) return;
GLint value = 0;
switch (pname) {
case GL_OBJECT_TYPE:
value = GL_SYNC_FENCE;
break;
case GL_SYNC_STATUS:
value = GL_SIGNALED;
break;
case GL_SYNC_CONDITION:
value = static_cast<GLint>(syncObject->Condition);
break;
case GL_SYNC_FLAGS:
value = static_cast<GLint>(syncObject->Flags);
break;
default:
RecordSyncError("GetSynciv", ErrorCode::InvalidEnum, std::format("Invalid pname enum: 0x{:X}", pname));
return;
}
void DeleteSync_State(GLsync sync) {}
GLsync FenceSync(GLenum condition, GLbitfield flags) {
return 0;
if (length) {
*length = bufSize > 0 && values ? 1 : 0;
}
GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
return 0;
if (bufSize > 0 && values) {
values[0] = value;
}
void DeleteSync(GLsync sync) {}
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
}
} // namespace MobileGL::MG_Impl::GLImpl
+8 -7
View File
@@ -9,10 +9,11 @@
#pragma once
#include <Includes.h>
namespace MobileGL {
namespace MG_Impl::GLImpl {
GLsync FenceSync(GLenum condition, GLbitfield flags);
GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout);
void DeleteSync(GLsync sync);
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
namespace MobileGL::MG_Impl::GLImpl {
GLsync FenceSync(GLenum condition, GLbitfield flags);
GLboolean IsSync(GLsync sync);
GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout);
void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout);
void DeleteSync(GLsync sync);
void GetSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values);
} // namespace MobileGL::MG_Impl::GLImpl
File diff suppressed because it is too large Load Diff
@@ -11,8 +11,23 @@
namespace MobileGL::MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
void BindImageTexture(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access,
GLenum format);
void GenerateMipmap(GLenum target);
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels);
void CreateTextures(GLenum target, GLsizei n, GLuint* textures);
void TextureStorage2D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
void TextureSubImage2D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
GLenum format, GLenum type, const void* pixels);
void TextureParameterf(GLuint texture, GLenum pname, GLfloat param);
void TextureParameteri(GLuint texture, GLenum pname, GLint param);
void TextureParameteriv(GLuint texture, GLenum pname, const GLint* params);
void BindTextureUnit(GLuint unit, GLuint texture);
void GetTextureImage(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels);
void GetTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels);
void GetTextureParameteriv(GLuint texture, GLenum pname, GLint* params);
void GetTextureLevelParameteriv(GLuint texture, GLint level, GLenum pname, GLint* params);
void TexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels);
void TexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
@@ -7,48 +7,47 @@
// End of Source File Header
#include "ProxyTexture.h"
#include "MG_State/GLState/TextureState/TextureObject2D.h"
#include "MG_Util/Types.h"
#include <MG_State/GLState/TextureState/TextureObject2D.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace TextureImpl {
ProxyTextureManager* pProxyTextureManager;
namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
UniquePtr<ProxyTextureManager> pProxyTextureManager;
Bool IsProxyTextureTarget(TextureUploadTarget target) {
switch (target) {
case TextureUploadTarget::ProxyCubeMap:
case TextureUploadTarget::ProxyTexture1DArray:
case TextureUploadTarget::ProxyTexture2DArray:
case TextureUploadTarget::ProxyTexture1D:
case TextureUploadTarget::ProxyTexture2D:
case TextureUploadTarget::ProxyTexture3D:
case TextureUploadTarget::ProxyTexture2DMultisample:
case TextureUploadTarget::ProxyTexture2DMultisampleArray:
case TextureUploadTarget::ProxyTextureRectangle:
case TextureUploadTarget::ProxyCubeMapArray:
return true;
default:
return false;
}
Bool IsProxyTextureTarget(TextureUploadTarget target) {
switch (target) {
case TextureUploadTarget::ProxyCubeMap:
case TextureUploadTarget::ProxyTexture1DArray:
case TextureUploadTarget::ProxyTexture2DArray:
case TextureUploadTarget::ProxyTexture1D:
case TextureUploadTarget::ProxyTexture2D:
case TextureUploadTarget::ProxyTexture3D:
case TextureUploadTarget::ProxyTexture2DMultisample:
case TextureUploadTarget::ProxyTexture2DMultisampleArray:
case TextureUploadTarget::ProxyTextureRectangle:
case TextureUploadTarget::ProxyCubeMapArray:
return true;
default:
return false;
}
}
SharedPtr<MG_State::GLState::ITextureObject> ProxyTextureManager::CreateOrReplaceProxyTextureObject(
TextureUploadTarget target) {
auto it = m_proxyTexturesMap.find(target);
if (it != m_proxyTexturesMap.end()) {
m_proxyTexturesMap.erase(it);
}
m_proxyTexturesMap[target] = MakeShared<MG_State::GLState::TextureObject2D>(0);
return m_proxyTexturesMap[target];
const SharedPtr<MG_State::GLState::ITextureObject>& ProxyTextureManager::CreateOrReplaceProxyTextureObject(
TextureUploadTarget target) {
auto it = m_proxyTexturesMap.find(target);
if (it != m_proxyTexturesMap.end()) {
m_proxyTexturesMap.erase(it);
}
auto& obj = m_proxyTexturesMap[target];
obj = MakeShared<MG_State::GLState::TextureObject2D>(0);
return obj;
}
SharedPtr<MG_State::GLState::ITextureObject> ProxyTextureManager::GetProxyTextureObject(
TextureUploadTarget target) {
auto it = m_proxyTexturesMap.find(target);
if (it != m_proxyTexturesMap.end()) {
return it->second;
}
return nullptr;
const SharedPtr<MG_State::GLState::ITextureObject>& ProxyTextureManager::GetProxyTextureObject(
TextureUploadTarget target) {
auto it = m_proxyTexturesMap.find(target);
if (it != m_proxyTexturesMap.end()) {
return it->second;
}
} // namespace TextureImpl
} // namespace MobileGL::MG_Impl::GLImpl
static SharedPtr<MG_State::GLState::ITextureObject> nullTextureObject = nullptr;
return nullTextureObject;
}
} // namespace MobileGL::MG_Impl::GLImpl::TextureImpl
+12 -13
View File
@@ -10,19 +10,18 @@
#include <Includes.h>
#include <MG_State/GLState/Core.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace TextureImpl {
Bool IsProxyTextureTarget(TextureUploadTarget target);
namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
Bool IsProxyTextureTarget(TextureUploadTarget target);
class ProxyTextureManager {
public:
SharedPtr<MG_State::GLState::ITextureObject> CreateOrReplaceProxyTextureObject(TextureUploadTarget target);
SharedPtr<MG_State::GLState::ITextureObject> GetProxyTextureObject(TextureUploadTarget target);
class ProxyTextureManager {
public:
const SharedPtr<MG_State::GLState::ITextureObject>& CreateOrReplaceProxyTextureObject(
TextureUploadTarget target);
const SharedPtr<MG_State::GLState::ITextureObject>& GetProxyTextureObject(TextureUploadTarget target);
private:
UnorderedMap<TextureUploadTarget, SharedPtr<MG_State::GLState::ITextureObject>> m_proxyTexturesMap;
};
private:
UnorderedMap<TextureUploadTarget, SharedPtr<MG_State::GLState::ITextureObject>> m_proxyTexturesMap;
};
extern ProxyTextureManager* pProxyTextureManager;
} // namespace TextureImpl
} // namespace MobileGL::MG_Impl::GLImpl
extern UniquePtr<ProxyTextureManager> pProxyTextureManager;
} // namespace MobileGL::MG_Impl::GLImpl::TextureImpl
+240 -254
View File
@@ -15,309 +15,295 @@
#include <MG_Util/Converters/MGToMG/TextureEnumConverter.h>
#include <MG_Util/Converters/MGToStr/TextureEnumConverter.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace TextureImpl {
Bool ValidateTextureTarget(TextureTarget target) {
if (target == TextureTarget::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureTarget", "Invalid texture target"));
return false;
}
return true;
namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
Bool ValidateTextureTarget(TextureTarget target) {
if (target == TextureTarget::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureTarget", "Invalid texture target"));
return false;
}
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 (textureUploadTarget == TextureUploadTarget::Unknown) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"ValidateTextureUploadTarget",
"Invalid texture upload target"));
return false;
}
return true;
if (!MG_State::pGLContext->ValidateTextureName(texture)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureName", "Invalid texture name"));
return false;
}
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) {
if (texture == 0) {
if (allowZero) return true;
// 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.
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureName",
"Texture name cannot be zero"));
return false;
}
return true;
}
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(
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 true;
}
Bool ValidateTextureInputFormat(TextureInputFormat format) {
if (format == TextureInputFormat::Unknown) {
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) {
if (!(target == TextureUploadTarget::Texture1DArray || target == TextureUploadTarget::ProxyTexture1DArray)) {
if (height < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSizeRange",
"Width and height must be greater than zero"));
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSizeWithTarget",
"Height must be greater than or equal to zero"));
return false;
}
// TODO: GL_INVALID_VALUE is generated if width is greater than GL_MAX_TEXTURE_SIZE.
return true;
// 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.
}
Bool ValidateTextureInternalFormat(TextureInternalFormat format) {
if (format == TextureInternalFormat::Unknown) {
if (target == TextureUploadTarget::Texture1DArray || target == TextureUploadTarget::ProxyTexture1DArray) {
if (height < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormat",
"Invalid texture sized internal format"));
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSizeWithTarget",
"Height must be greater than or equal to zero"));
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) {
if (border != 0) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"ValidateTextureBorderNumber",
"Border must be zero"));
return false;
}
return true;
return true;
}
Bool ValidateTextureSizeRange(SizeT width, SizeT height, SizeT depth) {
if (width < 0 || height < 0 || depth < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureSizeRange",
"Width and height must be greater than zero"));
return false;
}
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(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"ValidateTextureInternalFormatCompatibleWithInput",
"Invalid format for the given type"));
return false;
}
}
// TODO: GL_INVALID_VALUE is generated if width is greater than GL_MAX_TEXTURE_SIZE.
if (type == TexturePixelDataType::UnsignedShort4444 || type == TexturePixelDataType::UnsignedShort4444Rev ||
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;
}
}
return true;
}
if (internalFormat == TextureInternalFormat::DepthComponent ||
internalFormat == TextureInternalFormat::DepthComponent16 ||
internalFormat == TextureInternalFormat::DepthComponent24 ||
internalFormat == TextureInternalFormat::DepthComponent32F) {
if (format != TextureInputFormat::DepthComponent) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"ValidateTextureInternalFormatCompatibleWithInput",
"Invalid format for depth component internal format"));
return false;
}
}
Bool ValidateTextureInternalFormat(TextureInternalFormat format) {
if (format == TextureInternalFormat::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormat",
"Invalid texture sized internal format"));
return false;
}
return true;
}
if (format == TextureInputFormat::DepthComponent &&
(internalFormat != TextureInternalFormat::DepthComponent &&
internalFormat != TextureInternalFormat::DepthComponent16 &&
internalFormat != TextureInternalFormat::DepthComponent24 &&
internalFormat != TextureInternalFormat::DepthComponent32F &&
internalFormat != TextureInternalFormat::DepthComponent32 // workaround for Minecraft 1.21.5+
)) {
Bool ValidateTextureBorderNumber(Int border) {
if (border != 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureBorderNumber", "Border must be zero"));
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(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput",
"Invalid internal format for depth component format"));
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput",
"Invalid format for the given type"));
return false;
}
return true;
}
Bool ValidateTextureLevelWithUploadTarget(TextureUploadTarget target, Int level) {
if (target == TextureUploadTarget::TextureRectangle ||
target == TextureUploadTarget::ProxyTextureRectangle) {
if (level != 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
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) {
if (type == TexturePixelDataType::UnsignedShort4444 || type == TexturePixelDataType::UnsignedShort4444Rev ||
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", "ValidateTextureObject", "Texture object is null"));
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput",
"Invalid format for the given type"));
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) {
if (internalFormat == TextureInternalFormat::DepthComponent ||
internalFormat == TextureInternalFormat::DepthComponent16 ||
internalFormat == TextureInternalFormat::DepthComponent24 ||
internalFormat == TextureInternalFormat::DepthComponent32F) {
if (format != TextureInputFormat::DepthComponent) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureTargetUniformity",
"Texture target does not match the previously created texture"));
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput",
"Invalid format for depth component internal format"));
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,
MakeShared<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,
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;
if (format == TextureInputFormat::DepthComponent &&
(internalFormat != TextureInternalFormat::DepthComponent &&
internalFormat != TextureInternalFormat::DepthComponent16 &&
internalFormat != TextureInternalFormat::DepthComponent24 &&
internalFormat != TextureInternalFormat::DepthComponent32F &&
internalFormat != TextureInternalFormat::DepthComponent32 // workaround for Minecraft 1.21.5+
)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureInternalFormatCompatibleWithInput",
"Invalid internal format for depth component format"));
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) {
Bool ValidateTextureLevelWithUploadTarget(TextureUploadTarget target, Int level) {
if (target == TextureUploadTarget::TextureRectangle || target == TextureUploadTarget::ProxyTextureRectangle) {
if (level != 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<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())));
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateTextureLevelWithUploadTarget",
"Level must be zero for rectangle textures"));
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 MobileGL::MG_Impl::GLImpl
} // namespace MobileGL::MG_Impl::GLImpl::TextureImpl
+22 -24
View File
@@ -12,27 +12,25 @@
#include <Includes.h>
#include <MG_State/GLState/TextureState/TextureObject.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace TextureImpl {
Bool ValidateTextureTarget(TextureTarget target);
Bool ValidateTextureUploadTarget(TextureUploadTarget textureUploadTarget);
Bool ValidateTextureName(Uint texture, Bool allowZero = false);
Bool ValidateTextureInputFormat(TextureInputFormat format);
Bool ValidateTexturePixelDataType(TexturePixelDataType texturePixelDataType);
Bool ValidateTextureLevelNumber(Int level);
Bool ValidateTextureSizeWithTextureUploadTarget(TextureUploadTarget target, GLsizei width, GLsizei height);
Bool ValidateTextureSizeRange(SizeT width, SizeT height, SizeT depth);
Bool ValidateTextureInternalFormat(TextureInternalFormat format);
Bool ValidateTextureBorderNumber(Int border);
Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format,
TextureInternalFormat internalFormat,
TexturePixelDataType type);
Bool ValidateTextureLevelWithUploadTarget(TextureUploadTarget target, Int level);
Bool ValidateTextureObject(SharedPtr<MG_State::GLState::ITextureObject> textureObject);
Bool ValidateTextureTargetUniformity(SharedPtr<MG_State::GLState::ITextureObject> textureObject,
TextureTarget target);
Bool ValidateTextureSubImageOffsets(SharedPtr<MG_State::GLState::ITextureObject> textureObject, Int xoffset,
Int width, Int yoffset = 0, Int height = 0, Int zoffset = 0, Int depth = 0);
Bool ValidateBaseInternalFormatMatch(TextureInternalFormat format1, TextureInternalFormat format2);
} // namespace TextureImpl
} // namespace MobileGL::MG_Impl::GLImpl
namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
Bool ValidateTextureTarget(TextureTarget target);
Bool ValidateTextureUploadTarget(TextureUploadTarget textureUploadTarget);
Bool ValidateTextureName(Uint texture, Bool allowZero = false);
Bool ValidateTextureInputFormat(TextureInputFormat format);
Bool ValidateTexturePixelDataType(TexturePixelDataType texturePixelDataType);
Bool ValidateTextureLevelNumber(Int level);
Bool ValidateTextureSizeWithTextureUploadTarget(TextureUploadTarget target, GLsizei width, GLsizei height);
Bool ValidateTextureSizeRange(SizeT width, SizeT height, SizeT depth);
Bool ValidateTextureInternalFormat(TextureInternalFormat format);
Bool ValidateTextureBorderNumber(Int border);
Bool ValidateTextureInternalFormatCompatibleWithInput(TextureInputFormat format,
TextureInternalFormat internalFormat,
TexturePixelDataType type);
Bool ValidateTextureLevelWithUploadTarget(TextureUploadTarget target, Int level);
Bool ValidateTextureObject(SharedPtr<MG_State::GLState::ITextureObject> textureObject);
Bool ValidateTextureTargetUniformity(SharedPtr<MG_State::GLState::ITextureObject> textureObject,
TextureTarget target);
Bool ValidateTextureSubImageOffsets(SharedPtr<MG_State::GLState::ITextureObject> textureObject, Int xoffset,
Int width, Int yoffset = 0, Int height = 0, Int zoffset = 0, Int depth = 0);
Bool ValidateBaseInternalFormatMatch(TextureInternalFormat format1, TextureInternalFormat format2);
} // namespace MobileGL::MG_Impl::GLImpl::TextureImpl
@@ -8,226 +8,332 @@
#include "GL_VertexArray.h"
#include "Validators.h"
#include <MG_Impl/GLImpl/Buffer/Validators.h>
#include <MG_State/GLState/Core.h>
#include <MG_State/GLState/ErrorState/Error.h>
#include <MG_Util/Converters/GLToMG/DataTypeConverter.h>
namespace MobileGL {
namespace MG_Impl::GLImpl {
void DisableVertexAttribArray_State(GLuint index) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
namespace MobileGL::MG_Impl::GLImpl {
SharedPtr<MG_State::GLState::VertexArrayObject> GetNamedVertexArrayObject_State(GLuint vaobj,
const char* caller) {
if (!VertexArrayImpl::ValidateVertexArrayName(vaobj)) return nullptr;
if (!VertexArrayImpl::ValidateVertexArrayObject(vaobj)) return nullptr;
return MG_State::pGLContext->GetVertexArrayObject(vaobj);
}
auto vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"EnableVertexAttribArray_State",
"No vertex array object is bound."));
return;
}
SharedPtr<MG_State::GLState::BufferObject> GetVertexArrayBufferObject_State(GLuint buffer, const char* caller) {
if (!BufferImpl::ValidateBufferName(buffer, true)) return nullptr;
if (buffer == 0) return nullptr;
vao->DisableAttribute(index);
auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
if (!bufferObject) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
std::format("Buffer object {} does not exist.", buffer)));
return nullptr;
}
return bufferObject;
}
void DisableVertexAttribArray_State(GLuint index) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
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;
}
void EnableVertexAttribArray_State(GLuint index) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
vao->DisableAttribute(index);
}
auto vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"EnableVertexAttribArray_State",
"No vertex array object is bound."));
return;
}
void EnableVertexAttribArray_State(GLuint index) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) 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
void VertexAttribIPointer_State(GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
vao->EnableAttribute(index);
}
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
if (!VertexArrayImpl::ValidateVertexAttribPointerParams(index, size, dataType, stride)) return;
// TODO: implement GL_BGRA support
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();
if (!vao) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"VertexAttribPointer_State",
"No vertex array object is bound."));
return;
}
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
if (!VertexArrayImpl::ValidateVertexAttribPointerParams(index, size, dataType, stride)) 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, false, stride, offset, true);
vao->BindAttributeBuffer(index, vbo);
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;
}
// 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;
auto& vboSlot = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Vertex);
auto& vbo = vboSlot.GetBoundObject();
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
if (!VertexArrayImpl::ValidateVertexAttribPointerParams(index, size, dataType, stride)) return;
auto offset = reinterpret_cast<SizeT>(pointer);
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;
}
vao->SetAttributeFormat(index, size, dataType, false, stride, offset, true);
vao->BindAttributeBuffer(index, vbo);
}
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;
}
// 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;
SizeT offset = reinterpret_cast<SizeT>(pointer);
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
if (!VertexArrayImpl::ValidateVertexAttribPointerParams(index, size, dataType, stride)) return;
vao->SetAttributeFormat(index, size, dataType, normalized, stride, offset, false);
vao->BindAttributeBuffer(index, vbo);
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;
}
void BindVertexArray_State(GLuint array) {
if (array == 0) {
auto& vboSlot = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Vertex);
auto& vbo = vboSlot.GetBoundObject();
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);
return;
}
if (!VertexArrayImpl::ValidateVertexArrayName(array)) return;
MG_State::pGLContext->MarkVertexArrayForDeletion(vao);
}
}
if (!MG_State::pGLContext->ValidateVertexArrayObject(array)) {
MG_State::pGLContext->CreateVertexArrayObject(array);
}
MG_State::pGLContext->BindVertexArray(array);
void GenVertexArrays_State(GLsizei n, GLuint* arrays) {
if (n < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GenVertexArrays_State", "n must be non-negative."));
return;
}
void DeleteVertexArrays_State(GLsizei n, const GLuint* arrays) {
if (n < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue, MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteVertexArrays_State",
"n must be non-negative."));
return;
}
Vector<Uint> vaos;
MG_State::pGLContext->GenVertexArrayNames(n, vaos);
Memcpy(arrays, vaos.data(), n * sizeof(GLuint));
}
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->MarkVertexArrayForDeletion(vao);
}
void CreateVertexArrays_State(GLsizei n, GLuint* arrays) {
if (n < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "CreateVertexArrays_State", "n must be non-negative."));
return;
}
void GenVertexArrays_State(GLsizei n, GLuint* arrays) {
if (n < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GenVertexArrays_State", "n must be non-negative."));
return;
}
Vector<Uint> vaos;
MG_State::pGLContext->GenVertexArrayNames(n, vaos);
for (GLsizei i = 0; i < n; ++i) {
MG_State::pGLContext->CreateVertexArrayObject(vaos[i]);
arrays[i] = vaos[i];
}
}
auto vaoNames = MG_State::pGLContext->GenVertexArrayNames(n);
Copy(vaoNames.data(), arrays, vaoNames.size());
void DisableVertexArrayAttrib_State(GLuint vaobj, GLuint index) {
auto vao = GetNamedVertexArrayObject_State(vaobj, "DisableVertexArrayAttrib_State");
if (!vao) return;
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
vao->DisableAttribute(index);
}
void EnableVertexArrayAttrib_State(GLuint vaobj, GLuint index) {
auto vao = GetNamedVertexArrayObject_State(vaobj, "EnableVertexArrayAttrib_State");
if (!vao) return;
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
vao->EnableAttribute(index);
}
void VertexArrayElementBuffer_State(GLuint vaobj, GLuint buffer) {
auto vao = GetNamedVertexArrayObject_State(vaobj, "VertexArrayElementBuffer_State");
if (!vao) return;
auto bufferObject = GetVertexArrayBufferObject_State(buffer, "VertexArrayElementBuffer_State");
if (buffer != 0 && !bufferObject) return;
vao->GetIndexBufferBindingSlot().Bind(bufferObject);
}
void VertexArrayVertexBuffer_State(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset,
GLsizei stride) {
auto vao = GetNamedVertexArrayObject_State(vaobj, "VertexArrayVertexBuffer_State");
if (!vao) return;
if (!VertexArrayImpl::ValidateVertexAttributeIndex(bindingindex)) return;
if (offset < 0 || stride < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "VertexArrayVertexBuffer_State",
"offset and stride must be non-negative."));
return;
}
auto bufferObject = GetVertexArrayBufferObject_State(buffer, "VertexArrayVertexBuffer_State");
if (buffer != 0 && !bufferObject) return;
const auto& attr = vao->GetAttribute(bindingindex);
vao->SetAttributeFormat(bindingindex, attr.Size, attr.Type, attr.Normalized, stride, static_cast<SizeT>(offset),
attr.IsInteger);
vao->BindAttributeBuffer(bindingindex, bufferObject);
}
void VertexArrayAttribFormat_State(GLuint vaobj, GLuint attribindex, GLint size, GLenum type,
GLboolean normalized, GLuint relativeoffset) {
auto vao = GetNamedVertexArrayObject_State(vaobj, "VertexArrayAttribFormat_State");
if (!vao) return;
if (!VertexArrayImpl::ValidateVertexAttributeIndex(attribindex)) return;
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
const auto& attr = vao->GetAttribute(attribindex);
if (!VertexArrayImpl::ValidateVertexAttribPointerParams(attribindex, size, dataType, attr.Stride)) return;
vao->SetAttributeFormat(attribindex, size, dataType, normalized, attr.Stride, relativeoffset, false);
}
void VertexArrayAttribIFormat_State(GLuint vaobj, GLuint attribindex, GLint size, GLenum type,
GLuint relativeoffset) {
auto vao = GetNamedVertexArrayObject_State(vaobj, "VertexArrayAttribIFormat_State");
if (!vao) return;
if (!VertexArrayImpl::ValidateVertexAttributeIndex(attribindex)) return;
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
const auto& attr = vao->GetAttribute(attribindex);
if (!VertexArrayImpl::ValidateVertexAttribPointerParams(attribindex, size, dataType, attr.Stride)) return;
vao->SetAttributeFormat(attribindex, size, dataType, false, attr.Stride, relativeoffset, true);
}
GLboolean IsVertexArray_State(GLuint array) {
if (array == 0) return GL_FALSE;
return MG_State::pGLContext->ValidateVertexArrayObject(array) ? GL_TRUE : GL_FALSE;
}
void VertexAttribDivisor_State(GLuint index, GLuint divisor) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
auto& vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "VertexAttribDivisor_State",
"No vertex array object is bound."));
return;
}
GLboolean IsVertexArray_State(GLuint array) {
if (array == 0) {
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;
}
vao->SetAttributeDivisor(index, divisor);
}
void VertexAttribDivisor_State(GLuint index, GLuint divisor) {
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
void CreateVertexArrays(GLsizei n, GLuint* arrays) {
CreateVertexArrays_State(n, arrays);
}
auto vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl",
"VertexAttribDivisor_State",
"No vertex array object is bound."));
return;
}
void DisableVertexArrayAttrib(GLuint vaobj, GLuint index) {
DisableVertexArrayAttrib_State(vaobj, index);
}
vao->SetAttributeDivisor(index, divisor);
}
void EnableVertexArrayAttrib(GLuint vaobj, GLuint index) {
EnableVertexArrayAttrib_State(vaobj, index);
}
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
void VertexAttribDivisor(GLuint index, GLuint divisor) {
VertexAttribDivisor_State(index, divisor);
}
void VertexArrayElementBuffer(GLuint vaobj, GLuint buffer) {
VertexArrayElementBuffer_State(vaobj, buffer);
}
GLboolean IsVertexArray(GLuint array) {
return IsVertexArray_State(array);
}
void VertexArrayVertexBuffer(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) {
VertexArrayVertexBuffer_State(vaobj, bindingindex, buffer, offset, stride);
}
void DisableVertexAttribArray(GLuint index) {
DisableVertexAttribArray_State(index);
}
void VertexArrayAttribFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized,
GLuint relativeoffset) {
VertexArrayAttribFormat_State(vaobj, attribindex, size, type, normalized, relativeoffset);
}
void EnableVertexAttribArray(GLuint index) {
EnableVertexAttribArray_State(index);
}
void VertexArrayAttribIFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) {
VertexArrayAttribIFormat_State(vaobj, attribindex, size, type, relativeoffset);
}
void VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer) {
VertexAttribIPointer_State(index, size, type, stride, pointer);
}
void VertexAttribDivisor(GLuint index, GLuint divisor) {
VertexAttribDivisor_State(index, divisor);
}
void VertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
const void* pointer) {
VertexAttribPointer_State(index, size, type, normalized, stride, pointer);
}
GLboolean IsVertexArray(GLuint array) {
return IsVertexArray_State(array);
}
void BindVertexArray(GLuint array) {
BindVertexArray_State(array);
}
void DisableVertexAttribArray(GLuint index) {
DisableVertexAttribArray_State(index);
}
void DeleteVertexArrays(GLsizei n, const GLuint* arrays) {
DeleteVertexArrays_State(n, arrays);
}
void EnableVertexAttribArray(GLuint index) {
EnableVertexAttribArray_State(index);
}
void GenVertexArrays(GLsizei n, GLuint* arrays) {
GenVertexArrays_State(n, arrays);
}
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
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 MobileGL::MG_Impl::GLImpl
@@ -9,18 +9,24 @@
#pragma once
#include <Includes.h>
namespace MobileGL {
namespace MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
void VertexAttribDivisor(GLuint index, GLuint divisor);
GLboolean IsVertexArray(GLuint array);
void DisableVertexAttribArray(GLuint index);
void EnableVertexAttribArray(GLuint index);
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,
const void* pointer);
void BindVertexArray(GLuint array);
void DeleteVertexArrays(GLsizei n, const GLuint* arrays);
void GenVertexArrays(GLsizei n, GLuint* arrays);
} // namespace MG_Impl::GLImpl
} // namespace MobileGL
namespace MobileGL::MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
void CreateVertexArrays(GLsizei n, GLuint* arrays);
void DisableVertexArrayAttrib(GLuint vaobj, GLuint index);
void EnableVertexArrayAttrib(GLuint vaobj, GLuint index);
void VertexArrayElementBuffer(GLuint vaobj, GLuint buffer);
void VertexArrayVertexBuffer(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride);
void VertexArrayAttribFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized,
GLuint relativeoffset);
void VertexArrayAttribIFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
void VertexAttribDivisor(GLuint index, GLuint divisor);
GLboolean IsVertexArray(GLuint array);
void DisableVertexAttribArray(GLuint index);
void EnableVertexAttribArray(GLuint index);
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,
const void* pointer);
void BindVertexArray(GLuint array);
void DeleteVertexArrays(GLsizei n, const GLuint* arrays);
void GenVertexArrays(GLsizei n, GLuint* arrays);
} // namespace MobileGL::MG_Impl::GLImpl
@@ -12,74 +12,71 @@
#include <MG_Util/Converters/MGToGL/DataTypeConverter.h>
#include <MG_Util/Converters/MGToStr/DataTypeConverter.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace VertexArrayImpl {
namespace MobileGL::MG_Impl::GLImpl::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 isValid = MG_State::pGLContext->ValidateVertexArrayName(index);
if (!isValid) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateVertexArrayName",
std::format("Vertex array name {} is not valid.", index)));
return false;
}
return true;
Bool ValidateVertexArrayObject(Uint index) {
if (!MG_State::pGLContext->ValidateVertexArrayObject(index)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateVertexArrayObject",
std::format("Vertex array object {} does not exist.", index)));
return false;
}
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 (!MG_State::pGLContext->ValidateVertexArrayObject(index)) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "ValidateVertexArrayObject",
std::format("Vertex array object {} does not exist.", index)));
return false;
}
return true;
if (type == DataType::Unknown) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidEnum,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", "ValidateVertexAttribPointerParams",
std::format("Invalid type {} for attribute {}.", MG_Util::ConvertDataTypeToString(type), index)));
return false;
}
Bool ValidateVertexAttributeIndex(Uint index) {
if (index >= MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeShared<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;
if (stride < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>(
"MG_Impl/GLImpl", "ValidateVertexAttribPointerParams",
std::format("Negative stride {} is not allowed for attribute {}.", stride, index)));
return false;
}
Bool ValidateVertexAttribPointerParams(Uint index, SizeT size, DataType type, Int stride) {
if (size < 1 || size > 4) {
MG_State::pGLContext->RecordError(
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
return true;
}
} // namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl
@@ -10,11 +10,9 @@
#include <Includes.h>
#include <MG_State/GLState/VertexArrayState/VertexArrayObject.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace VertexArrayImpl {
Bool ValidateVertexArrayName(Uint index);
Bool ValidateVertexArrayObject(Uint index);
Bool ValidateVertexAttributeIndex(Uint index);
Bool ValidateVertexAttribPointerParams(Uint index, SizeT size, DataType type, Int stride);
} // namespace VertexArrayImpl
} // namespace MobileGL::MG_Impl::GLImpl
namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl {
Bool ValidateVertexArrayName(Uint index);
Bool ValidateVertexArrayObject(Uint index);
Bool ValidateVertexAttributeIndex(Uint index);
Bool ValidateVertexAttribPointerParams(Uint index, SizeT size, DataType type, Int stride);
} // namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl
+4 -6
View File
@@ -10,9 +10,7 @@
#include <Includes.h>
#include "MG_Impl/GetProcAddress.h"
namespace MG_Impl {
namespace GLXImpl {
void* GetProcAddress(const char* name);
void* GetProcAddressARB(const char* name);
} // namespace GLXImpl
} // namespace MG_Impl
namespace MG_Impl::GLXImpl {
void* GetProcAddress(const char* name);
void* GetProcAddressARB(const char* name);
} // namespace MG_Impl::GLXImpl
File diff suppressed because it is too large Load Diff

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