From 7d4ebfc305416e30dcd8baa942ca6599101310c8 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 17 Feb 2026 09:47:13 +0800 Subject: [PATCH] [Feat] (MG_Backend/DirectVulkan): implement simple DrawElements path to put buffer to the test --- .../MG_Backend/DirectVulkan/DirectVulkan.cpp | 45 +++++++- .../DirectVulkan/Renderer/VulkanRenderer.cpp | 102 ++++++++++++++++++ .../DirectVulkan/Renderer/VulkanRenderer.h | 7 ++ 3 files changed, 150 insertions(+), 4 deletions(-) diff --git a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp index 840c2755..8db3ef13 100644 --- a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp @@ -24,6 +24,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) { MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElements called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawElements called with null GL context"); if (count < 0) { MGLOG_W("DrawElements skipped: count (%d) must be non-negative", count); @@ -39,11 +40,47 @@ namespace MobileGL::MG_Backend::DirectVulkan { return; } - pVulkanRenderer->EnsureFrameRecordingStarted(); + SizeT indexSize = 0; + switch (type) { + case GL_UNSIGNED_SHORT: + indexSize = sizeof(Uint16); + break; + case GL_UNSIGNED_INT: + indexSize = sizeof(Uint32); + break; + default: + MGLOG_W("DrawElements skipped: index type %u is not supported yet", type); + return; + } - (void)mode; - (void)type; - (void)indices; + const auto vao = MG_State::pGLContext->GetBoundVertexArray(); + if (!vao) { + MGLOG_W("DrawElements skipped: no bound VAO"); + return; + } + + const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject(); + if (!indexBuffer) { + MGLOG_W("DrawElements skipped: no bound ELEMENT_ARRAY_BUFFER"); + return; + } + + const auto indexData = indexBuffer->GetDataReadOnly(); + if (!indexData || indexData->empty()) { + MGLOG_W("DrawElements skipped: ELEMENT_ARRAY_BUFFER has no data"); + return; + } + + const SizeT byteOffset = reinterpret_cast(indices); + const SizeT requiredBytes = static_cast(count) * indexSize; + if (byteOffset + requiredBytes > indexBuffer->GetSize()) { + MGLOG_W("DrawElements skipped: index range out of bounds (offset=%zu, size=%zu, buffer=%zu)", + byteOffset, requiredBytes, indexBuffer->GetSize()); + return; + } + + const Uint8* src = indexData->data() + byteOffset; + pVulkanRenderer->DrawElements(type, count, src, requiredBytes); } void DrawArrays(GLenum mode, GLint first, GLsizei count) { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 28343260..ca4f0f28 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -143,6 +143,13 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkPipelineMultisampleStateCreateInfo ms{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + VkPipelineDepthStencilStateCreateInfo depthStencil{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO}; + depthStencil.depthTestEnable = VK_FALSE; + depthStencil.depthWriteEnable = VK_FALSE; + depthStencil.depthCompareOp = VK_COMPARE_OP_ALWAYS; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.stencilTestEnable = VK_FALSE; + VkPipelineColorBlendAttachmentState colorAttach{}; colorAttach.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; @@ -160,6 +167,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { gpi.pViewportState = &vpci; gpi.pRasterizationState = &raster; gpi.pMultisampleState = &ms; + gpi.pDepthStencilState = &depthStencil; gpi.pColorBlendState = &blend; gpi.pDynamicState = &dynamicState; gpi.layout = m_pipelineLayout; @@ -186,6 +194,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { CreateSurface(); PickPhysicalDevice(); CreateLogicalDeviceAndQueues(); + CreateAllocator(); CreateCommandPool(); @@ -207,6 +216,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { VK_VERIFY(vkDeviceWaitIdle(m_device)); m_programFactory.reset(); + m_indexBuffer.Destroy(); m_frameContext.Destroy(m_device, m_commandPool); @@ -225,6 +235,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_commandPool = VK_NULL_HANDLE; } + DestroyAllocator(); + if (m_device != VK_NULL_HANDLE) { vkDestroyDevice(m_device, nullptr); m_device = VK_NULL_HANDLE; @@ -462,6 +474,71 @@ namespace MobileGL::MG_Backend::DirectVulkan { vkCmdDraw(commandBuffer, static_cast(count), 1, static_cast(first), 0); } + void VulkanRenderer::DrawElements(GLenum type, GLsizei count, const void* indexData, SizeT indexDataSizeBytes) { + EnsureFrameRecordingStarted(); + auto& frame = m_frameContext.GetCurrent(); + if (!frame.isCommandRecording || !m_isMainRenderPassActive) { + MGLOG_W("DrawElements skipped: frame recording was not started"); + return; + } + + VkIndexType vkIndexType = VK_INDEX_TYPE_MAX_ENUM; + switch (type) { + case GL_UNSIGNED_SHORT: + vkIndexType = VK_INDEX_TYPE_UINT16; + break; + case GL_UNSIGNED_INT: + vkIndexType = VK_INDEX_TYPE_UINT32; + break; + default: + MGLOG_W("DrawElements skipped: index type %u is not supported yet", type); + return; + } + + MOBILEGL_ASSERT(indexData != nullptr, "DrawElements requires non-null indexData"); + MOBILEGL_ASSERT(indexDataSizeBytes > 0, "DrawElements requires non-zero index data size"); + + if (!m_indexBuffer.IsValid() || m_indexBuffer.GetSize() < indexDataSizeBytes) { + m_indexBuffer.Destroy(); + const Bool created = m_indexBuffer.Create( + m_allocator, static_cast(indexDataSizeBytes), + VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO, + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT); + if (!created) { + MGLOG_E("DrawElements skipped: failed to create index buffer"); + return; + } + } + + if (!m_indexBuffer.Upload(indexData, static_cast(indexDataSizeBytes), 0)) { + MGLOG_E("DrawElements skipped: failed to upload index data"); + return; + } + + VkCommandBuffer& commandBuffer = frame.commandBuffer; + const auto swapchainExtent = m_swapchainObject.GetExtent(); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline); + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(swapchainExtent.width); + viewport.height = static_cast(swapchainExtent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = swapchainExtent; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + vkCmdBindIndexBuffer(commandBuffer, m_indexBuffer.GetHandle(), 0, vkIndexType); + vkCmdDrawIndexed(commandBuffer, static_cast(count), 1, 0, 0, 0); + } + void VulkanRenderer::Render() { // Route test rendering through the same frame-start logic used by draw calls, // so pending glClear() state can be consumed consistently. @@ -878,6 +955,31 @@ namespace MobileGL::MG_Backend::DirectVulkan { MGLOG_I("Queues got successfully."); } + void VulkanRenderer::CreateAllocator() { + MOBILEGL_ASSERT(m_instance != VK_NULL_HANDLE, "CreateAllocator requires valid VkInstance"); + MOBILEGL_ASSERT(m_physicalDevice.handle != VK_NULL_HANDLE, "CreateAllocator requires valid physical device"); + MOBILEGL_ASSERT(m_device != VK_NULL_HANDLE, "CreateAllocator requires valid VkDevice"); + + if (m_allocator != nullptr) { + return; + } + + VmaAllocatorCreateInfo allocatorInfo{}; + allocatorInfo.instance = m_instance; + allocatorInfo.physicalDevice = m_physicalDevice.handle; + allocatorInfo.device = m_device; + allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_0; + + VK_VERIFY(vmaCreateAllocator(&allocatorInfo, &m_allocator), "vmaCreateAllocator"); + } + + void VulkanRenderer::DestroyAllocator() { + if (m_allocator != nullptr) { + vmaDestroyAllocator(m_allocator); + m_allocator = nullptr; + } + } + void VulkanRenderer::CreateSwapchain() { m_swapchainObject.Create(m_device, m_physicalDevice.handle, m_surface, static_cast(m_physicalDevice.queueFamilies.graphicsFamily), diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 0e77a4f9..aa57a702 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -11,8 +11,10 @@ #include "FrameContext.h" #include "ProgramFactory.h" #include "SwapchainObject.h" +#include "VkBufferObject.h" #include "MG_Util/Math/VectorTypes.h" #include +#include #include "../VkIncludes.h" @@ -29,6 +31,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Bool ConsumePendingColorClear(VkClearColorValue& outClearColor); void EnsureFrameRecordingStarted(); void DrawArrays(GLenum mode, GLint first, GLsizei count); + void DrawElements(GLenum type, GLsizei count, const void* indexData, SizeT indexDataSizeBytes); void Render(); void Present(); @@ -63,6 +66,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { 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; @@ -85,6 +89,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; VkPipeline m_pipeline = VK_NULL_HANDLE; + VkBufferObject m_indexBuffer; Uint m_imageIndexAcquired = 0; FrameContext m_frameContext; @@ -103,6 +108,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { void CreateSurface(); void PickPhysicalDevice(); void CreateLogicalDeviceAndQueues(); + void CreateAllocator(); + void DestroyAllocator(); void CreateSwapchain(); void CreateCommandPool(); void CreateFrameContexts();