mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Feat] (MG_Backend/DirectVulkan): vertex input state factory
This commit is contained in:
@@ -215,6 +215,7 @@ set(SOURCE_FILES
|
|||||||
MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp
|
MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp
|
||||||
MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp
|
MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp
|
||||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.cpp
|
MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.cpp
|
||||||
|
MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp
|
||||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp
|
MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp
|
||||||
|
|
||||||
MobileGL/MG_State/GLState/Core.cpp
|
MobileGL/MG_State/GLState/Core.cpp
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
payload.count = count;
|
payload.count = count;
|
||||||
|
|
||||||
const auto vao = MG_State::pGLContext->GetBoundVertexArray();
|
const auto vao = MG_State::pGLContext->GetBoundVertexArray();
|
||||||
|
payload.vertexArray = vao ? vao.get() : nullptr;
|
||||||
if (vao) {
|
if (vao) {
|
||||||
const auto& attr0 = vao->GetAttribute(0);
|
const auto& attr0 = vao->GetAttribute(0);
|
||||||
if (attr0.Enabled && attr0.Buffer) {
|
if (attr0.Enabled && attr0.Buffer) {
|
||||||
|
|||||||
@@ -53,4 +53,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_state.pVertexAttributeDescriptions = m_attributes.empty() ? nullptr : m_attributes.data();
|
m_state.pVertexAttributeDescriptions = m_attributes.empty() ? nullptr : m_attributes.data();
|
||||||
return m_state;
|
return m_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Vector<VkVertexInputBindingDescription>& VertexInputStateBuilder::GetBindings() const {
|
||||||
|
return m_bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Vector<VkVertexInputAttributeDescription>& VertexInputStateBuilder::GetAttributes() const {
|
||||||
|
return m_attributes;
|
||||||
|
}
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
VertexInputStateBuilder& AddAttribute(Uint32 location, Uint32 binding, VkFormat format, Uint32 offset);
|
VertexInputStateBuilder& AddAttribute(Uint32 location, Uint32 binding, VkFormat format, Uint32 offset);
|
||||||
|
|
||||||
const VkPipelineVertexInputStateCreateInfo& Build();
|
const VkPipelineVertexInputStateCreateInfo& Build();
|
||||||
|
const Vector<VkVertexInputBindingDescription>& GetBindings() const;
|
||||||
|
const Vector<VkVertexInputAttributeDescription>& GetAttributes() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VkPipelineVertexInputStateCreateInfo m_state{};
|
VkPipelineVertexInputStateCreateInfo m_state{};
|
||||||
|
|||||||
@@ -0,0 +1,176 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.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 "VertexInputStateFactory.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
|
VertexInputStateFactory::HashType VertexInputStateFactory::ComputeHash(
|
||||||
|
const MG_State::GLState::VertexArrayObject& vao) const {
|
||||||
|
XXHASH_VERIFY(XXH64_reset(m_hashState, m_config.CacheVersion));
|
||||||
|
|
||||||
|
for (Int i = 0; i < MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS; ++i) {
|
||||||
|
const auto& attr = vao.GetAttribute(i);
|
||||||
|
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &attr.Enabled, sizeof(attr.Enabled)));
|
||||||
|
if (!attr.Enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &attr.Size, sizeof(attr.Size)));
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &attr.Type, sizeof(attr.Type)));
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &attr.Normalized, sizeof(attr.Normalized)));
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &attr.Stride, sizeof(attr.Stride)));
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &attr.Offset, sizeof(attr.Offset)));
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &attr.IsInteger, sizeof(attr.IsInteger)));
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &attr.Divisor, sizeof(attr.Divisor)));
|
||||||
|
|
||||||
|
const Uint bufferIndex = attr.Buffer ? attr.Buffer->GetExternalIndex() : 0;
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &bufferIndex, sizeof(bufferIndex)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return XXH64_digest(m_hashState);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VertexInputStateFactory::BackendVertexInputState& VertexInputStateFactory::GetOrCreateVertexInputState(
|
||||||
|
const MG_State::GLState::VertexArrayObject& vao) {
|
||||||
|
const HashType hash = ComputeHash(vao);
|
||||||
|
auto it = m_cache.find(hash);
|
||||||
|
if (it != m_cache.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
VertexInputStateBuilder builder;
|
||||||
|
for (Uint32 location = 0; location < MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS; ++location) {
|
||||||
|
const auto& attr = vao.GetAttribute(location);
|
||||||
|
if (!attr.Enabled || !attr.Buffer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto vkFormat = ToVkVertexFormat(attr.Type, attr.Size, attr.Normalized, attr.IsInteger);
|
||||||
|
if (vkFormat != VK_FORMAT_UNDEFINED) {
|
||||||
|
MGLOG_W("Skipping unsupported vertex attribute layout (location=%u, type=%d, size=%d)",
|
||||||
|
location, static_cast<Int>(attr.Type), attr.Size);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SizeT componentSize = GetComponentSize(attr.Type);
|
||||||
|
if (componentSize == 0) {
|
||||||
|
MGLOG_W("Skipping vertex attribute with unknown component size (location=%u, type=%d)",
|
||||||
|
location, static_cast<Int>(attr.Type));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Uint32 stride = attr.Stride > 0
|
||||||
|
? static_cast<Uint32>(attr.Stride)
|
||||||
|
: static_cast<Uint32>(componentSize * static_cast<SizeT>(attr.Size));
|
||||||
|
const VkVertexInputRate inputRate =
|
||||||
|
(attr.Divisor == 0) ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
|
||||||
|
|
||||||
|
builder
|
||||||
|
.AddBinding(location, stride, inputRate)
|
||||||
|
.AddAttribute(location, location, vkFormat, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& state = builder.Build();
|
||||||
|
|
||||||
|
auto& entry = m_cache[hash];
|
||||||
|
entry.hash = hash;
|
||||||
|
entry.bindings = builder.GetBindings();
|
||||||
|
entry.attributes = builder.GetAttributes();
|
||||||
|
entry.state = state;
|
||||||
|
entry.state.pVertexBindingDescriptions = entry.bindings.empty() ? nullptr : entry.bindings.data();
|
||||||
|
entry.state.pVertexAttributeDescriptions = entry.attributes.empty() ? nullptr : entry.attributes.data();
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkFormat VertexInputStateFactory::ToVkVertexFormat(DataType type, Int size, Bool normalized, Bool isInteger) {
|
||||||
|
switch (type) {
|
||||||
|
case DataType::Float32:
|
||||||
|
switch (size) {
|
||||||
|
case 1: return VK_FORMAT_R32_SFLOAT;
|
||||||
|
case 2: return VK_FORMAT_R32G32_SFLOAT;
|
||||||
|
case 3: return VK_FORMAT_R32G32B32_SFLOAT;
|
||||||
|
case 4: return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||||
|
default: return VK_FORMAT_UNDEFINED;
|
||||||
|
}
|
||||||
|
case DataType::Int32:
|
||||||
|
if (!isInteger || normalized) return VK_FORMAT_UNDEFINED;
|
||||||
|
switch (size) {
|
||||||
|
case 1: return VK_FORMAT_R32_SINT;
|
||||||
|
case 2: return VK_FORMAT_R32G32_SINT;
|
||||||
|
case 3: return VK_FORMAT_R32G32B32_SINT;
|
||||||
|
case 4: return VK_FORMAT_R32G32B32A32_SINT;
|
||||||
|
default: return VK_FORMAT_UNDEFINED;
|
||||||
|
}
|
||||||
|
case DataType::Uint32:
|
||||||
|
if (!isInteger || normalized) return VK_FORMAT_UNDEFINED;
|
||||||
|
switch (size) {
|
||||||
|
case 1: return VK_FORMAT_R32_UINT;
|
||||||
|
case 2: return VK_FORMAT_R32G32_UINT;
|
||||||
|
case 3: return VK_FORMAT_R32G32B32_UINT;
|
||||||
|
case 4: return VK_FORMAT_R32G32B32A32_UINT;
|
||||||
|
default: return VK_FORMAT_UNDEFINED;
|
||||||
|
}
|
||||||
|
case DataType::Int16:
|
||||||
|
switch (size) {
|
||||||
|
case 1: return normalized ? VK_FORMAT_R16_SNORM : (isInteger ? VK_FORMAT_R16_SINT : VK_FORMAT_UNDEFINED);
|
||||||
|
case 2: return normalized ? VK_FORMAT_R16G16_SNORM : (isInteger ? VK_FORMAT_R16G16_SINT : VK_FORMAT_UNDEFINED);
|
||||||
|
case 3: return VK_FORMAT_UNDEFINED;
|
||||||
|
case 4: return normalized ? VK_FORMAT_R16G16B16A16_SNORM : (isInteger ? VK_FORMAT_R16G16B16A16_SINT : VK_FORMAT_UNDEFINED);
|
||||||
|
default: return VK_FORMAT_UNDEFINED;
|
||||||
|
}
|
||||||
|
case DataType::Uint16:
|
||||||
|
switch (size) {
|
||||||
|
case 1: return normalized ? VK_FORMAT_R16_UNORM : (isInteger ? VK_FORMAT_R16_UINT : VK_FORMAT_UNDEFINED);
|
||||||
|
case 2: return normalized ? VK_FORMAT_R16G16_UNORM : (isInteger ? VK_FORMAT_R16G16_UINT : VK_FORMAT_UNDEFINED);
|
||||||
|
case 3: return VK_FORMAT_UNDEFINED;
|
||||||
|
case 4: return normalized ? VK_FORMAT_R16G16B16A16_UNORM : (isInteger ? VK_FORMAT_R16G16B16A16_UINT : VK_FORMAT_UNDEFINED);
|
||||||
|
default: return VK_FORMAT_UNDEFINED;
|
||||||
|
}
|
||||||
|
case DataType::Int8:
|
||||||
|
switch (size) {
|
||||||
|
case 1: return normalized ? VK_FORMAT_R8_SNORM : (isInteger ? VK_FORMAT_R8_SINT : VK_FORMAT_UNDEFINED);
|
||||||
|
case 2: return normalized ? VK_FORMAT_R8G8_SNORM : (isInteger ? VK_FORMAT_R8G8_SINT : VK_FORMAT_UNDEFINED);
|
||||||
|
case 3: return VK_FORMAT_UNDEFINED;
|
||||||
|
case 4: return normalized ? VK_FORMAT_R8G8B8A8_SNORM : (isInteger ? VK_FORMAT_R8G8B8A8_SINT : VK_FORMAT_UNDEFINED);
|
||||||
|
default: return VK_FORMAT_UNDEFINED;
|
||||||
|
}
|
||||||
|
case DataType::Uint8:
|
||||||
|
switch (size) {
|
||||||
|
case 1: return normalized ? VK_FORMAT_R8_UNORM : (isInteger ? VK_FORMAT_R8_UINT : VK_FORMAT_UNDEFINED);
|
||||||
|
case 2: return normalized ? VK_FORMAT_R8G8_UNORM : (isInteger ? VK_FORMAT_R8G8_UINT : VK_FORMAT_UNDEFINED);
|
||||||
|
case 3: return VK_FORMAT_UNDEFINED;
|
||||||
|
case 4: return normalized ? VK_FORMAT_R8G8B8A8_UNORM : (isInteger ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_UNDEFINED);
|
||||||
|
default: return VK_FORMAT_UNDEFINED;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return VK_FORMAT_UNDEFINED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SizeT VertexInputStateFactory::GetComponentSize(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 MobileGL::MG_Backend::DirectVulkan
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.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 "Config.h"
|
||||||
|
#include "VertexInputStateBuilder.h"
|
||||||
|
#include "MG_State/GLState/VertexArrayState/VertexArrayObject.h"
|
||||||
|
#include <Includes.h>
|
||||||
|
#include "../VkIncludes.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
|
class VertexInputStateFactory {
|
||||||
|
public:
|
||||||
|
using HashType = Uint64;
|
||||||
|
|
||||||
|
struct BackendVertexInputState {
|
||||||
|
HashType hash = 0;
|
||||||
|
Vector<VkVertexInputBindingDescription> bindings;
|
||||||
|
Vector<VkVertexInputAttributeDescription> attributes;
|
||||||
|
VkPipelineVertexInputStateCreateInfo state{
|
||||||
|
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit VertexInputStateFactory(const VulkanRendererConfig& config):
|
||||||
|
m_config(config) {}
|
||||||
|
~VertexInputStateFactory() = default;
|
||||||
|
VertexInputStateFactory(const VertexInputStateFactory&) = delete;
|
||||||
|
|
||||||
|
HashType ComputeHash(const MG_State::GLState::VertexArrayObject& vao) const;
|
||||||
|
const BackendVertexInputState& GetOrCreateVertexInputState(const MG_State::GLState::VertexArrayObject& vao);
|
||||||
|
|
||||||
|
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;
|
||||||
|
static inline XXH64_state_t* m_hashState = XXH64_createState();
|
||||||
|
};
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
// End of Source File Header
|
// End of Source File Header
|
||||||
|
|
||||||
#include "VulkanRenderer.h"
|
#include "VulkanRenderer.h"
|
||||||
|
#include "VertexInputStateFactory.h"
|
||||||
#include "VertexInputStateBuilder.h"
|
#include "VertexInputStateBuilder.h"
|
||||||
|
|
||||||
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
||||||
@@ -201,6 +202,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
RecreateSwapchain();
|
RecreateSwapchain();
|
||||||
|
|
||||||
m_programFactory = MakeUnique<ProgramFactory>(m_device, m_config);
|
m_programFactory = MakeUnique<ProgramFactory>(m_device, m_config);
|
||||||
|
m_vertexInputStateFactory = MakeUnique<VertexInputStateFactory>(m_config);
|
||||||
|
|
||||||
PrepareDemoPipeline();
|
PrepareDemoPipeline();
|
||||||
CreateFrameContexts();
|
CreateFrameContexts();
|
||||||
@@ -216,6 +218,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
VK_VERIFY(vkDeviceWaitIdle(m_device));
|
VK_VERIFY(vkDeviceWaitIdle(m_device));
|
||||||
|
|
||||||
m_programFactory.reset();
|
m_programFactory.reset();
|
||||||
|
m_vertexInputStateFactory.reset();
|
||||||
m_indexBuffer.Destroy();
|
m_indexBuffer.Destroy();
|
||||||
|
|
||||||
m_frameContext.Destroy(m_device, m_commandPool);
|
m_frameContext.Destroy(m_device, m_commandPool);
|
||||||
@@ -450,6 +453,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (payload.vertexArray && m_vertexInputStateFactory) {
|
||||||
|
(void)m_vertexInputStateFactory->GetOrCreateVertexInputState(*payload.vertexArray);
|
||||||
|
}
|
||||||
|
|
||||||
EnsureFrameRecordingStarted();
|
EnsureFrameRecordingStarted();
|
||||||
auto& frame = m_frameContext.GetCurrent();
|
auto& frame = m_frameContext.GetCurrent();
|
||||||
if (!frame.isCommandRecording || !m_isMainRenderPassActive) {
|
if (!frame.isCommandRecording || !m_isMainRenderPassActive) {
|
||||||
|
|||||||
@@ -18,11 +18,18 @@
|
|||||||
|
|
||||||
#include "../VkIncludes.h"
|
#include "../VkIncludes.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_State::GLState {
|
||||||
|
class VertexArrayObject;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
|
class VertexInputStateFactory;
|
||||||
|
|
||||||
struct DrawArrayPayload {
|
struct DrawArrayPayload {
|
||||||
GLenum mode = GL_TRIANGLES;
|
GLenum mode = GL_TRIANGLES;
|
||||||
GLint first = 0;
|
GLint first = 0;
|
||||||
GLsizei count = 0;
|
GLsizei count = 0;
|
||||||
|
const MG_State::GLState::VertexArrayObject* vertexArray = nullptr;
|
||||||
Bool hasPositionStream = false;
|
Bool hasPositionStream = false;
|
||||||
const void* positionData = nullptr;
|
const void* positionData = nullptr;
|
||||||
SizeT positionDataSizeBytes = 0;
|
SizeT positionDataSizeBytes = 0;
|
||||||
@@ -114,6 +121,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Bool m_isMainRenderPassActive = false;
|
Bool m_isMainRenderPassActive = false;
|
||||||
|
|
||||||
UniquePtr<ProgramFactory> m_programFactory;
|
UniquePtr<ProgramFactory> m_programFactory;
|
||||||
|
UniquePtr<VertexInputStateFactory> m_vertexInputStateFactory;
|
||||||
|
|
||||||
void CreateInstance();
|
void CreateInstance();
|
||||||
VkResult SetupDebugMessenger();
|
VkResult SetupDebugMessenger();
|
||||||
|
|||||||
Reference in New Issue
Block a user