[Feat] (MG_Backend/DirectVulkan): vertex input state builder

This commit is contained in:
2026-02-17 10:11:24 +08:00
parent 53989d1711
commit 206d8ab36a
6 changed files with 145 additions and 7 deletions
+1
View File
@@ -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
@@ -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<SizeT>(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
@@ -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<Uint32>(m_bindings.size());
m_state.pVertexBindingDescriptions = m_bindings.empty() ? nullptr : m_bindings.data();
m_state.vertexAttributeDescriptionCount = static_cast<Uint32>(m_attributes.size());
m_state.pVertexAttributeDescriptions = m_attributes.empty() ? nullptr : m_attributes.data();
return m_state;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -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 <Includes.h>
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<VkVertexInputBindingDescription> m_bindings;
Vector<VkVertexInputAttributeDescription> m_attributes;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -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<uint32_t>(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<Uint32>(count), 1, static_cast<Uint32>(first), 0);
vkCmdDraw(commandBuffer, static_cast<Uint32>(payload.count), 1, static_cast<Uint32>(payload.first), 0);
}
void VulkanRenderer::DrawElements(GLenum type, GLsizei count, const void* indexData, SizeT indexDataSizeBytes) {
@@ -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();