diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b938c9a..0a2a1a34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,6 +214,7 @@ set(SOURCE_FILES MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp + MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.cpp MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp MobileGL/MG_State/GLState/Core.cpp diff --git a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp index 8db3ef13..62957b56 100644 --- a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp @@ -85,6 +85,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void DrawArrays(GLenum mode, GLint first, GLsizei count) { MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArrays called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawArrays called with null GL context"); if (first < 0) { MGLOG_W("DrawArrays skipped: first (%d) must be non-negative", first); @@ -105,7 +106,39 @@ namespace MobileGL::MG_Backend::DirectVulkan { return; } - pVulkanRenderer->DrawArrays(mode, first, count); + DrawArrayPayload payload{}; + payload.mode = mode; + payload.first = first; + payload.count = count; + + const auto vao = MG_State::pGLContext->GetBoundVertexArray(); + if (vao) { + const auto& attr0 = vao->GetAttribute(0); + if (attr0.Enabled && attr0.Buffer) { + const auto positionData = attr0.Buffer->GetDataReadOnly(); + if (positionData && !positionData->empty()) { + payload.hasPositionStream = true; + payload.positionData = positionData->data(); + payload.positionDataSizeBytes = attr0.Buffer->GetSize(); + payload.positionOffsetBytes = attr0.Offset; + payload.positionStrideBytes = attr0.Stride > 0 ? static_cast(attr0.Stride) : 0; + payload.positionSize = attr0.Size; + payload.positionNormalized = attr0.Normalized; + + switch (attr0.Type) { + case DataType::Float32: + payload.positionType = GL_FLOAT; + break; + default: + payload.positionType = GL_FLOAT; + payload.hasPositionStream = false; + break; + } + } + } + } + + pVulkanRenderer->DrawArrays(payload); } } // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.cpp new file mode 100644 index 00000000..67087d42 --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.cpp @@ -0,0 +1,56 @@ +// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.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 "VertexInputStateBuilder.h" + +namespace MobileGL::MG_Backend::DirectVulkan { + VertexInputStateBuilder::VertexInputStateBuilder() { + Reset(); + } + + void VertexInputStateBuilder::Reset() { + m_bindings.clear(); + m_attributes.clear(); + + m_state = {}; + m_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + m_state.vertexBindingDescriptionCount = 0; + m_state.pVertexBindingDescriptions = nullptr; + m_state.vertexAttributeDescriptionCount = 0; + m_state.pVertexAttributeDescriptions = nullptr; + } + + VertexInputStateBuilder& VertexInputStateBuilder::AddBinding( + Uint32 binding, Uint32 stride, VkVertexInputRate inputRate) { + VkVertexInputBindingDescription desc{}; + desc.binding = binding; + desc.stride = stride; + desc.inputRate = inputRate; + m_bindings.push_back(desc); + return *this; + } + + VertexInputStateBuilder& VertexInputStateBuilder::AddAttribute( + Uint32 location, Uint32 binding, VkFormat format, Uint32 offset) { + VkVertexInputAttributeDescription desc{}; + desc.location = location; + desc.binding = binding; + desc.format = format; + desc.offset = offset; + m_attributes.push_back(desc); + return *this; + } + + const VkPipelineVertexInputStateCreateInfo& VertexInputStateBuilder::Build() { + m_state.vertexBindingDescriptionCount = static_cast(m_bindings.size()); + m_state.pVertexBindingDescriptions = m_bindings.empty() ? nullptr : m_bindings.data(); + m_state.vertexAttributeDescriptionCount = static_cast(m_attributes.size()); + m_state.pVertexAttributeDescriptions = m_attributes.empty() ? nullptr : m_attributes.data(); + return m_state; + } +} // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.h new file mode 100644 index 00000000..43417a06 --- /dev/null +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.h @@ -0,0 +1,29 @@ +// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.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 + +namespace MobileGL::MG_Backend::DirectVulkan { + class VertexInputStateBuilder { + public: + VertexInputStateBuilder(); + + void Reset(); + VertexInputStateBuilder& AddBinding( + Uint32 binding, Uint32 stride, VkVertexInputRate inputRate = VK_VERTEX_INPUT_RATE_VERTEX); + VertexInputStateBuilder& AddAttribute(Uint32 location, Uint32 binding, VkFormat format, Uint32 offset); + + const VkPipelineVertexInputStateCreateInfo& Build(); + + private: + VkPipelineVertexInputStateCreateInfo m_state{}; + Vector m_bindings; + Vector m_attributes; + }; +} // namespace MobileGL::MG_Backend::DirectVulkan diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index ca4f0f28..98f76928 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -7,6 +7,7 @@ // End of Source File Header #include "VulkanRenderer.h" +#include "VertexInputStateBuilder.h" #include "MG_State/GLState/ProgramState/ProgramObject.h" @@ -112,9 +113,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { dynamicState.dynamicStateCount = static_cast(std::size(s_dynamicStates)); dynamicState.pDynamicStates = s_dynamicStates; - VkPipelineVertexInputStateCreateInfo vertexInput{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO}; - vertexInput.vertexBindingDescriptionCount = 0; - vertexInput.vertexAttributeDescriptionCount = 0; + VertexInputStateBuilder vertexInputBuilder; + const auto& vertexInput = vertexInputBuilder.Build(); VkPipelineInputAssemblyStateCreateInfo ia{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO}; ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; @@ -444,7 +444,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { m_frameContext.EndCommandRecording(); } - void VulkanRenderer::DrawArrays(GLenum mode, GLint first, GLsizei count) { + void VulkanRenderer::DrawArrays(const DrawArrayPayload& payload) { + if (payload.mode != GL_TRIANGLES) { + MGLOG_W("DrawArrays skipped: primitive mode %u is not supported yet", payload.mode); + return; + } + EnsureFrameRecordingStarted(); auto& frame = m_frameContext.GetCurrent(); if (!frame.isCommandRecording || !m_isMainRenderPassActive) { @@ -471,7 +476,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { scissor.extent = swapchainExtent; vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - vkCmdDraw(commandBuffer, static_cast(count), 1, static_cast(first), 0); + vkCmdDraw(commandBuffer, static_cast(payload.count), 1, static_cast(payload.first), 0); } void VulkanRenderer::DrawElements(GLenum type, GLsizei count, const void* indexData, SizeT indexDataSizeBytes) { diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index aa57a702..6988fd51 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -19,6 +19,20 @@ #include "../VkIncludes.h" namespace MobileGL::MG_Backend::DirectVulkan { + struct DrawArrayPayload { + GLenum mode = GL_TRIANGLES; + GLint first = 0; + GLsizei count = 0; + Bool hasPositionStream = false; + const void* positionData = nullptr; + SizeT positionDataSizeBytes = 0; + SizeT positionStrideBytes = 0; + SizeT positionOffsetBytes = 0; + GLenum positionType = GL_FLOAT; + GLint positionSize = 0; + Bool positionNormalized = false; + }; + class VulkanRenderer { public: VulkanRenderer(NativeWindowType window, const VulkanRendererConfig& cfg = {}); @@ -30,7 +44,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void RequestClear(GLbitfield mask, const FloatVec4& color, Float depth, Uint32 stencil); Bool ConsumePendingColorClear(VkClearColorValue& outClearColor); void EnsureFrameRecordingStarted(); - void DrawArrays(GLenum mode, GLint first, GLsizei count); + void DrawArrays(const DrawArrayPayload& payload); void DrawElements(GLenum type, GLsizei count, const void* indexData, SizeT indexDataSizeBytes); void Render(); void Present();