[Chore] (MG_Test/Backend/DirectVulkan): get rid of some junk

This commit is contained in:
2026-02-16 10:24:53 +08:00
parent e01ebc8fc7
commit 1a4fde893f
7 changed files with 0 additions and 775 deletions
@@ -1,133 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Managers/ProgramManager.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 "ProgramManager.h"
namespace MobileGL::MG_Backend::DirectVulkan {
ProgramManager::ProgramManager(VulkanContext &ctx): m_ctx(ctx) {}
ProgramManager::~ProgramManager() {
Cleanup();
}
void ProgramManager::Cleanup() {
auto device = m_ctx.GetDevice();
if (device != VK_NULL_HANDLE) {
for (auto& [_, stages] : m_shaderStageCreateInfo) {
for (auto& stageInfo : stages) {
if (stageInfo.module != VK_NULL_HANDLE) {
vkDestroyShaderModule(device, stageInfo.module, nullptr);
stageInfo.module = VK_NULL_HANDLE;
}
}
}
}
m_shaderStageCreateInfo.clear();
XXH64_freeState(m_hashState);
}
Vector<VkPipelineShaderStageCreateInfo>& ProgramManager::CreatePipelineShaderStages(ProgramObject* programObject) {
auto hash = GetHash(programObject);
MOBILEGL_ASSERT(m_shaderStageCreateInfo.find(hash) == m_shaderStageCreateInfo.end(),
"A program with the same hash has already been created");
Vector<VkPipelineShaderStageCreateInfo> info;
auto& shaders = programObject->GetAttachedShaders();
auto& spirvBinaries = programObject->GetGeneratedSpirv();
MOBILEGL_ASSERT(shaders.size() == spirvBinaries.size(),
"Shader and SPIR-V binary count mismatch");
info.reserve(shaders.size());
auto device = m_ctx.GetDevice();
MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "Vulkan device is null");
for (SizeT i = 0; i < shaders.size(); ++i) {
auto shaderStage = shaders[i]->GetShaderStage();
VkShaderStageFlagBits vkStage = VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM;
switch (shaderStage) {
case ShaderStage::Vertex:
vkStage = VK_SHADER_STAGE_VERTEX_BIT;
break;
case ShaderStage::TessControl:
vkStage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
break;
case ShaderStage::TessEval:
vkStage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
break;
case ShaderStage::Geometry:
vkStage = VK_SHADER_STAGE_GEOMETRY_BIT;
break;
case ShaderStage::Fragment:
vkStage = VK_SHADER_STAGE_FRAGMENT_BIT;
break;
case ShaderStage::Compute:
vkStage = VK_SHADER_STAGE_COMPUTE_BIT;
break;
default:
MOBILEGL_ASSERT(false, "Unsupported shader stage");
break;
}
auto& spirv = spirvBinaries[i];
MOBILEGL_ASSERT(!spirv.empty(), "SPIR-V binary is empty");
VkShaderModuleCreateInfo smci{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
smci.codeSize = spirv.size() * sizeof(Uint);
smci.pCode = reinterpret_cast<const uint32_t*>(spirv.data());
VkShaderModule shaderModule = VK_NULL_HANDLE;
VK_VERIFY(vkCreateShaderModule(device, &smci, nullptr, &shaderModule), "vkCreateShaderModule");
MOBILEGL_ASSERT(shaderModule != VK_NULL_HANDLE, "Failed to create shader module");
VkPipelineShaderStageCreateInfo stageInfo{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
stageInfo.stage = vkStage;
stageInfo.module = shaderModule;
stageInfo.pName = "main";
info.emplace_back(stageInfo);
}
auto [it, inserted] = m_shaderStageCreateInfo.emplace(hash, Move(info));
MOBILEGL_ASSERT(inserted, "Failed to cache shader stage create info");
return it->second;
}
Vector<VkPipelineShaderStageCreateInfo>* ProgramManager::GetPipelineShaderStages(HashType hash) {
auto it = m_shaderStageCreateInfo.find(hash);
if (it == m_shaderStageCreateInfo.end()) return nullptr;
return &it->second;
}
Vector<VkPipelineShaderStageCreateInfo>* ProgramManager::GetPipelineShaderStages(ProgramObject* programObject) {
return GetPipelineShaderStages(GetHash(programObject));
}
ProgramManager::HashType ProgramManager::GetHash(ProgramObject *programObject) {
MOBILEGL_ASSERT(programObject && programObject->GetLinkStatus(), "program object is null or not successfully linked!");
MOBILEGL_ASSERT(m_hashState != nullptr, "Hash state should already be initialized");
XXH64_hash_t const seed = MG_Config::CacheVersion;
auto errc = XXH64_reset(m_hashState, seed);
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state reset failed");
auto& shaders = programObject->GetAttachedShaders();
auto& spirvBinaries = programObject->GetGeneratedSpirv();
auto count = shaders.size();
for (auto i = 0; i < count; i++) {
auto& shader = shaders[i];
auto stage = shader->GetShaderStage();
errc = XXH64_update(m_hashState, &stage, sizeof(stage));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, spirvBinaries[i].data(), spirvBinaries[i].size() * sizeof(unsigned));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
}
HashType hash = XXH64_digest(m_hashState);
return hash;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,36 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Managers/ProgramManager.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"
#include "MG_State/GLState/ProgramState/ProgramObject.h"
#include "Config.h"
#include "xxhash.h"
namespace MobileGL::MG_Backend::DirectVulkan {
class VulkanContext;
class ProgramManager {
public:
using ProgramObject = MobileGL::MG_State::GLState::ProgramObject;
using HashType = XXH64_hash_t;
ProgramManager(VulkanContext& ctx);
~ProgramManager();
Vector<VkPipelineShaderStageCreateInfo>& CreatePipelineShaderStages(ProgramObject* programObject);
Vector<VkPipelineShaderStageCreateInfo>* GetPipelineShaderStages(HashType hash);
Vector<VkPipelineShaderStageCreateInfo>* GetPipelineShaderStages(ProgramObject* programObject);
private:
void Cleanup();
HashType GetHash(ProgramObject* programObject);
VulkanContext& m_ctx;
UnorderedMap<HashType, Vector<VkPipelineShaderStageCreateInfo>> m_shaderStageCreateInfo;
XXH64_state_t* const m_hashState = XXH64_createState();
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,269 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Managers/RenderStateManager.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 "RenderStateManager.h"
namespace MobileGL::MG_Backend::DirectVulkan {
RenderStateManager::RenderStateManager() {}
RenderStateManager::~RenderStateManager() {
Cleanup();
}
void RenderStateManager::Cleanup() {
m_renderStateInfo.clear();
XXH64_freeState(m_hashState);
}
void RenderStateManager::PatchCreateInfoPointers(RenderStateInfo& stateInfo) {
stateInfo.ColorBlendStateCreateInfo.attachmentCount =
static_cast<Uint>(stateInfo.ColorBlendAttachmentStates.size());
stateInfo.ColorBlendStateCreateInfo.pAttachments =
stateInfo.ColorBlendAttachmentStates.empty() ? nullptr : stateInfo.ColorBlendAttachmentStates.data();
stateInfo.ViewportStateCreateInfo.viewportCount = 1;
stateInfo.ViewportStateCreateInfo.pViewports = &stateInfo.Viewport;
stateInfo.ViewportStateCreateInfo.scissorCount = 1;
stateInfo.ViewportStateCreateInfo.pScissors = &stateInfo.Scissor;
}
RenderStateManager::RenderStateInfo&
RenderStateManager::CreatePipelineRenderState(const RenderStateParameters& renderState, Uint colorAttachmentCount) {
auto hash = GetHash(renderState, colorAttachmentCount);
MOBILEGL_ASSERT(m_renderStateInfo.find(hash) == m_renderStateInfo.end(),
"A render state with the same hash has already been created");
MOBILEGL_ASSERT(colorAttachmentCount > 0, "colorAttachmentCount must be greater than zero");
MOBILEGL_ASSERT(colorAttachmentCount <= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS,
"colorAttachmentCount exceeds max draw buffers");
RenderStateInfo stateInfo;
MOBILEGL_ASSERT(renderState.Viewport.z() >= 0 && renderState.Viewport.w() >= 0,
"Viewport width/height must be non-negative");
stateInfo.Viewport.x = static_cast<Float>(renderState.Viewport.x());
stateInfo.Viewport.y = static_cast<Float>(renderState.Viewport.y());
stateInfo.Viewport.width = static_cast<Float>(renderState.Viewport.z());
stateInfo.Viewport.height = static_cast<Float>(renderState.Viewport.w());
stateInfo.Viewport.minDepth = 0.0f;
stateInfo.Viewport.maxDepth = 1.0f;
if (renderState.ScissorTestEnabled) {
MOBILEGL_ASSERT(renderState.ScissorBox.z() >= 0 && renderState.ScissorBox.w() >= 0,
"Scissor width/height must be non-negative");
stateInfo.Scissor.offset.x = renderState.ScissorBox.x();
stateInfo.Scissor.offset.y = renderState.ScissorBox.y();
stateInfo.Scissor.extent.width = static_cast<Uint>(renderState.ScissorBox.z());
stateInfo.Scissor.extent.height = static_cast<Uint>(renderState.ScissorBox.w());
} else {
stateInfo.Scissor.offset.x = renderState.Viewport.x();
stateInfo.Scissor.offset.y = renderState.Viewport.y();
stateInfo.Scissor.extent.width = static_cast<Uint>(renderState.Viewport.z());
stateInfo.Scissor.extent.height = static_cast<Uint>(renderState.Viewport.w());
}
stateInfo.RasterizationStateCreateInfo.depthClampEnable = VK_FALSE;
stateInfo.RasterizationStateCreateInfo.rasterizerDiscardEnable = VK_FALSE;
stateInfo.RasterizationStateCreateInfo.polygonMode = VK_POLYGON_MODE_FILL;
stateInfo.RasterizationStateCreateInfo.cullMode =
renderState.CullFaceEnabled ? ResolveCullMode(renderState.CullFaceModeSetting) : VK_CULL_MODE_NONE;
stateInfo.RasterizationStateCreateInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
stateInfo.RasterizationStateCreateInfo.depthBiasEnable = VK_FALSE;
stateInfo.RasterizationStateCreateInfo.lineWidth = 1.0f;
stateInfo.DepthStencilStateCreateInfo.depthTestEnable = renderState.DepthTestEnabled ? VK_TRUE : VK_FALSE;
stateInfo.DepthStencilStateCreateInfo.depthWriteEnable = renderState.DepthMask ? VK_TRUE : VK_FALSE;
stateInfo.DepthStencilStateCreateInfo.depthCompareOp = ResolveDepthCompareOp(renderState.DepthFunc);
stateInfo.DepthStencilStateCreateInfo.depthBoundsTestEnable = VK_FALSE;
stateInfo.DepthStencilStateCreateInfo.stencilTestEnable = VK_FALSE;
stateInfo.DepthStencilStateCreateInfo.minDepthBounds = 0.0f;
stateInfo.DepthStencilStateCreateInfo.maxDepthBounds = 1.0f;
stateInfo.ColorBlendAttachmentStates.reserve(colorAttachmentCount);
for (Uint i = 0; i < colorAttachmentCount; ++i) {
const auto& blendState = renderState.BlendStates[i];
VkPipelineColorBlendAttachmentState attachment{};
attachment.blendEnable = blendState.Enabled ? VK_TRUE : VK_FALSE;
attachment.srcColorBlendFactor = ResolveBlendFactor(blendState.SrcFactorRGB);
attachment.dstColorBlendFactor = ResolveBlendFactor(blendState.DstFactorRGB);
attachment.colorBlendOp = VK_BLEND_OP_ADD;
attachment.srcAlphaBlendFactor = ResolveBlendFactor(blendState.SrcFactorAlpha);
attachment.dstAlphaBlendFactor = ResolveBlendFactor(blendState.DstFactorAlpha);
attachment.alphaBlendOp = VK_BLEND_OP_ADD;
attachment.colorWriteMask = ResolveColorWriteMask(renderState.ColorMask);
stateInfo.ColorBlendAttachmentStates.emplace_back(attachment);
}
stateInfo.ColorBlendStateCreateInfo.logicOpEnable = VK_FALSE;
stateInfo.ColorBlendStateCreateInfo.logicOp = VK_LOGIC_OP_COPY;
stateInfo.ColorBlendStateCreateInfo.blendConstants[0] = 0.0f;
stateInfo.ColorBlendStateCreateInfo.blendConstants[1] = 0.0f;
stateInfo.ColorBlendStateCreateInfo.blendConstants[2] = 0.0f;
stateInfo.ColorBlendStateCreateInfo.blendConstants[3] = 0.0f;
PatchCreateInfoPointers(stateInfo);
auto [it, inserted] = m_renderStateInfo.emplace(hash, Move(stateInfo));
MOBILEGL_ASSERT(inserted, "Failed to cache pipeline render state create info");
PatchCreateInfoPointers(it->second);
return it->second;
}
RenderStateManager::RenderStateInfo* RenderStateManager::GetPipelineRenderState(HashType hash) {
auto it = m_renderStateInfo.find(hash);
if (it == m_renderStateInfo.end()) return nullptr;
return &it->second;
}
RenderStateManager::RenderStateInfo*
RenderStateManager::GetPipelineRenderState(const RenderStateParameters& renderState, Uint colorAttachmentCount) {
return GetPipelineRenderState(GetHash(renderState, colorAttachmentCount));
}
RenderStateManager::HashType RenderStateManager::GetHash(const RenderStateParameters& renderState,
Uint colorAttachmentCount) {
MOBILEGL_ASSERT(m_hashState != nullptr, "Hash state should already be initialized");
MOBILEGL_ASSERT(colorAttachmentCount > 0, "colorAttachmentCount must be greater than zero");
MOBILEGL_ASSERT(colorAttachmentCount <= MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS,
"colorAttachmentCount exceeds max draw buffers");
XXH64_hash_t const seed = MG_Config::CacheVersion;
auto errc = XXH64_reset(m_hashState, seed);
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state reset failed");
const Int viewport[4] = {renderState.Viewport.x(), renderState.Viewport.y(), renderState.Viewport.z(),
renderState.Viewport.w()};
errc = XXH64_update(m_hashState, viewport, sizeof(viewport));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
for (Uint i = 0; i < colorAttachmentCount; ++i) {
const auto& blendState = renderState.BlendStates[i];
errc = XXH64_update(m_hashState, &blendState.Enabled, sizeof(blendState.Enabled));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &blendState.SrcFactorRGB, sizeof(blendState.SrcFactorRGB));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &blendState.DstFactorRGB, sizeof(blendState.DstFactorRGB));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &blendState.SrcFactorAlpha, sizeof(blendState.SrcFactorAlpha));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &blendState.DstFactorAlpha, sizeof(blendState.DstFactorAlpha));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
}
errc = XXH64_update(m_hashState, &renderState.DepthTestEnabled, sizeof(renderState.DepthTestEnabled));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &renderState.DepthFunc, sizeof(renderState.DepthFunc));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &renderState.DepthMask, sizeof(renderState.DepthMask));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
const Bool colorMask[4] = {renderState.ColorMask.x(), renderState.ColorMask.y(), renderState.ColorMask.z(),
renderState.ColorMask.w()};
errc = XXH64_update(m_hashState, colorMask, sizeof(colorMask));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &renderState.CullFaceEnabled, sizeof(renderState.CullFaceEnabled));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &renderState.CullFaceModeSetting, sizeof(renderState.CullFaceModeSetting));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &renderState.ScissorTestEnabled, sizeof(renderState.ScissorTestEnabled));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
const Int scissor[4] = {renderState.ScissorBox.x(), renderState.ScissorBox.y(), renderState.ScissorBox.z(),
renderState.ScissorBox.w()};
errc = XXH64_update(m_hashState, scissor, sizeof(scissor));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &colorAttachmentCount, sizeof(colorAttachmentCount));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
return XXH64_digest(m_hashState);
}
VkBlendFactor RenderStateManager::ResolveBlendFactor(BlendFactor factor) {
switch (factor) {
case BlendFactor::Zero:
return VK_BLEND_FACTOR_ZERO;
case BlendFactor::One:
return VK_BLEND_FACTOR_ONE;
case BlendFactor::SrcColor:
return VK_BLEND_FACTOR_SRC_COLOR;
case BlendFactor::OneMinusSrcColor:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
case BlendFactor::DstColor:
return VK_BLEND_FACTOR_DST_COLOR;
case BlendFactor::OneMinusDstColor:
return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
case BlendFactor::SrcAlpha:
return VK_BLEND_FACTOR_SRC_ALPHA;
case BlendFactor::OneMinusSrcAlpha:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
case BlendFactor::DstAlpha:
return VK_BLEND_FACTOR_DST_ALPHA;
case BlendFactor::OneMinusDstAlpha:
return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
case BlendFactor::ConstantColor:
return VK_BLEND_FACTOR_CONSTANT_COLOR;
case BlendFactor::OneMinusConstantColor:
return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
case BlendFactor::ConstantAlpha:
return VK_BLEND_FACTOR_CONSTANT_ALPHA;
case BlendFactor::OneMinusConstantAlpha:
return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
default:
MOBILEGL_ASSERT(false, "Unsupported blend factor");
return VK_BLEND_FACTOR_ONE;
}
}
VkCompareOp RenderStateManager::ResolveDepthCompareOp(DepthTestFunc func) {
switch (func) {
case DepthTestFunc::Never:
return VK_COMPARE_OP_NEVER;
case DepthTestFunc::Less:
return VK_COMPARE_OP_LESS;
case DepthTestFunc::Equal:
return VK_COMPARE_OP_EQUAL;
case DepthTestFunc::LessEqual:
return VK_COMPARE_OP_LESS_OR_EQUAL;
case DepthTestFunc::Greater:
return VK_COMPARE_OP_GREATER;
case DepthTestFunc::NotEqual:
return VK_COMPARE_OP_NOT_EQUAL;
case DepthTestFunc::GreaterEqual:
return VK_COMPARE_OP_GREATER_OR_EQUAL;
case DepthTestFunc::Always:
return VK_COMPARE_OP_ALWAYS;
default:
MOBILEGL_ASSERT(false, "Unsupported depth compare function");
return VK_COMPARE_OP_LESS;
}
}
VkCullModeFlags RenderStateManager::ResolveCullMode(CullFaceMode mode) {
switch (mode) {
case CullFaceMode::Front:
return VK_CULL_MODE_FRONT_BIT;
case CullFaceMode::Back:
return VK_CULL_MODE_BACK_BIT;
case CullFaceMode::FrontAndBack:
return VK_CULL_MODE_FRONT_AND_BACK;
default:
MOBILEGL_ASSERT(false, "Unsupported cull face mode");
return VK_CULL_MODE_BACK_BIT;
}
}
VkColorComponentFlags RenderStateManager::ResolveColorWriteMask(const BoolVec4& mask) {
VkColorComponentFlags vkMask = 0;
if (mask.r()) vkMask |= VK_COLOR_COMPONENT_R_BIT;
if (mask.g()) vkMask |= VK_COLOR_COMPONENT_G_BIT;
if (mask.b()) vkMask |= VK_COLOR_COMPONENT_B_BIT;
if (mask.a()) vkMask |= VK_COLOR_COMPONENT_A_BIT;
return vkMask;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,54 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Managers/RenderStateManager.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"
#include "MG_State/GLState/RenderState/RenderState.h"
#include "Config.h"
#include "xxhash.h"
namespace MobileGL::MG_Backend::DirectVulkan {
class RenderStateManager {
public:
using RenderStateParameters = MobileGL::RenderStateParameters;
using HashType = XXH64_hash_t;
struct RenderStateInfo {
VkPipelineColorBlendStateCreateInfo ColorBlendStateCreateInfo{
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
Vector<VkPipelineColorBlendAttachmentState> ColorBlendAttachmentStates;
VkPipelineRasterizationStateCreateInfo RasterizationStateCreateInfo{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
VkPipelineViewportStateCreateInfo ViewportStateCreateInfo{
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
VkPipelineDepthStencilStateCreateInfo DepthStencilStateCreateInfo{
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO};
VkViewport Viewport{};
VkRect2D Scissor{};
};
RenderStateManager();
~RenderStateManager();
RenderStateInfo& CreatePipelineRenderState(const RenderStateParameters& renderState, Uint colorAttachmentCount);
RenderStateInfo* GetPipelineRenderState(HashType hash);
RenderStateInfo* GetPipelineRenderState(const RenderStateParameters& renderState, Uint colorAttachmentCount);
private:
void Cleanup();
static void PatchCreateInfoPointers(RenderStateInfo& stateInfo);
HashType GetHash(const RenderStateParameters& renderState, Uint colorAttachmentCount);
static VkBlendFactor ResolveBlendFactor(BlendFactor factor);
static VkCompareOp ResolveDepthCompareOp(DepthTestFunc func);
static VkCullModeFlags ResolveCullMode(CullFaceMode mode);
static VkColorComponentFlags ResolveColorWriteMask(const BoolVec4& mask);
UnorderedMap<HashType, RenderStateInfo> m_renderStateInfo;
XXH64_state_t* const m_hashState = XXH64_createState();
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,227 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Managers/VertexInputStateManager.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 "VertexInputStateManager.h"
namespace MobileGL::MG_Backend::DirectVulkan {
VertexInputStateManager::VertexInputStateManager() {}
VertexInputStateManager::~VertexInputStateManager() {
Cleanup();
}
void VertexInputStateManager::Cleanup() {
m_vertexInputStateInfo.clear();
XXH64_freeState(m_hashState);
}
void VertexInputStateManager::PatchCreateInfoPointers(VertexInputStateInfo& stateInfo) {
stateInfo.CreateInfo.vertexBindingDescriptionCount = static_cast<Uint>(stateInfo.BindingDescriptions.size());
stateInfo.CreateInfo.pVertexBindingDescriptions =
stateInfo.BindingDescriptions.empty() ? nullptr : stateInfo.BindingDescriptions.data();
stateInfo.CreateInfo.vertexAttributeDescriptionCount = static_cast<Uint>(stateInfo.AttributeDescriptions.size());
stateInfo.CreateInfo.pVertexAttributeDescriptions =
stateInfo.AttributeDescriptions.empty() ? nullptr : stateInfo.AttributeDescriptions.data();
}
VertexInputStateManager::VertexInputStateInfo&
VertexInputStateManager::CreatePipelineVertexInputState(VertexArrayObject* vaoObject) {
auto hash = GetHash(vaoObject);
MOBILEGL_ASSERT(m_vertexInputStateInfo.find(hash) == m_vertexInputStateInfo.end(),
"A VAO vertex input state with the same hash has already been created");
VertexInputStateInfo stateInfo;
const auto& allAttributes = vaoObject->GetAllAttributes();
for (Uint attribIndex = 0; attribIndex < allAttributes.size(); ++attribIndex) {
const auto& attrib = allAttributes[attribIndex];
if (!attrib.Enabled) continue;
MOBILEGL_ASSERT(attrib.Size >= 1 && attrib.Size <= 4, "Vertex attribute size out of range");
MOBILEGL_ASSERT(attrib.Divisor <= 1,
"Vulkan core pipeline vertex input only supports divisor 0/1 without extra extensions");
VkVertexInputBindingDescription bindingDesc{};
bindingDesc.binding = attribIndex;
bindingDesc.inputRate = (attrib.Divisor == 0) ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
auto componentByteSize = GetDataTypeByteSize(attrib.Type);
auto computedStride = static_cast<Uint>(componentByteSize * static_cast<Uint>(attrib.Size));
bindingDesc.stride = (attrib.Stride > 0) ? static_cast<Uint>(attrib.Stride) : computedStride;
stateInfo.BindingDescriptions.emplace_back(bindingDesc);
VkVertexInputAttributeDescription attrDesc{};
attrDesc.location = attribIndex;
attrDesc.binding = attribIndex;
attrDesc.format = ResolveVertexFormat(attrib);
attrDesc.offset = static_cast<Uint>(attrib.Offset);
stateInfo.AttributeDescriptions.emplace_back(attrDesc);
}
PatchCreateInfoPointers(stateInfo);
auto [it, inserted] = m_vertexInputStateInfo.emplace(hash, Move(stateInfo));
MOBILEGL_ASSERT(inserted, "Failed to cache pipeline vertex input state create info");
PatchCreateInfoPointers(it->second);
return it->second;
}
VertexInputStateManager::VertexInputStateInfo* VertexInputStateManager::GetPipelineVertexInputState(HashType hash) {
auto it = m_vertexInputStateInfo.find(hash);
if (it == m_vertexInputStateInfo.end()) return nullptr;
return &it->second;
}
VertexInputStateManager::VertexInputStateInfo*
VertexInputStateManager::GetPipelineVertexInputState(VertexArrayObject* vaoObject) {
return GetPipelineVertexInputState(GetHash(vaoObject));
}
VertexInputStateManager::HashType VertexInputStateManager::GetHash(VertexArrayObject* vaoObject) {
MOBILEGL_ASSERT(vaoObject != nullptr, "vao object is null");
MOBILEGL_ASSERT(m_hashState != nullptr, "Hash state should already be initialized");
XXH64_hash_t const seed = MG_Config::CacheVersion;
auto errc = XXH64_reset(m_hashState, seed);
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state reset failed");
const auto& allAttributes = vaoObject->GetAllAttributes();
for (const auto& attrib : allAttributes) {
errc = XXH64_update(m_hashState, &attrib.Enabled, sizeof(attrib.Enabled));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &attrib.Size, sizeof(attrib.Size));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &attrib.Type, sizeof(attrib.Type));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &attrib.Normalized, sizeof(attrib.Normalized));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &attrib.Stride, sizeof(attrib.Stride));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &attrib.Offset, sizeof(attrib.Offset));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &attrib.IsInteger, sizeof(attrib.IsInteger));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
errc = XXH64_update(m_hashState, &attrib.Divisor, sizeof(attrib.Divisor));
MOBILEGL_ASSERT(errc == XXH_OK, "Hash state update failed");
}
return XXH64_digest(m_hashState);
}
Uint VertexInputStateManager::GetDataTypeByteSize(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:
MOBILEGL_ASSERT(false, "Unsupported vertex data type");
return 0;
}
}
VkFormat VertexInputStateManager::ResolveVertexFormat(const MG_State::GLState::VertexAttribute& attrib) {
auto size = attrib.Size;
auto selectFormat = [size](VkFormat c1, VkFormat c2, VkFormat c3, VkFormat c4) {
switch (size) {
case 1:
return c1;
case 2:
return c2;
case 3:
return c3;
case 4:
return c4;
default:
return VK_FORMAT_UNDEFINED;
}
};
if (attrib.IsInteger) {
MOBILEGL_ASSERT(!attrib.Normalized, "Integer attributes cannot be normalized in Vulkan integer pipeline IO");
switch (attrib.Type) {
case DataType::Int8:
return selectFormat(VK_FORMAT_R8_SINT, VK_FORMAT_R8G8_SINT, VK_FORMAT_R8G8B8_SINT,
VK_FORMAT_R8G8B8A8_SINT);
case DataType::Uint8:
return selectFormat(VK_FORMAT_R8_UINT, VK_FORMAT_R8G8_UINT, VK_FORMAT_R8G8B8_UINT,
VK_FORMAT_R8G8B8A8_UINT);
case DataType::Int16:
return selectFormat(VK_FORMAT_R16_SINT, VK_FORMAT_R16G16_SINT, VK_FORMAT_R16G16B16_SINT,
VK_FORMAT_R16G16B16A16_SINT);
case DataType::Uint16:
return selectFormat(VK_FORMAT_R16_UINT, VK_FORMAT_R16G16_UINT, VK_FORMAT_R16G16B16_UINT,
VK_FORMAT_R16G16B16A16_UINT);
case DataType::Int32:
return selectFormat(VK_FORMAT_R32_SINT, VK_FORMAT_R32G32_SINT, VK_FORMAT_R32G32B32_SINT,
VK_FORMAT_R32G32B32A32_SINT);
case DataType::Uint32:
return selectFormat(VK_FORMAT_R32_UINT, VK_FORMAT_R32G32_UINT, VK_FORMAT_R32G32B32_UINT,
VK_FORMAT_R32G32B32A32_UINT);
default:
MOBILEGL_ASSERT(false, "Unsupported integer vertex attribute data type");
return VK_FORMAT_UNDEFINED;
}
}
if (attrib.Normalized) {
switch (attrib.Type) {
case DataType::Int8:
return selectFormat(VK_FORMAT_R8_SNORM, VK_FORMAT_R8G8_SNORM, VK_FORMAT_R8G8B8_SNORM,
VK_FORMAT_R8G8B8A8_SNORM);
case DataType::Uint8:
return selectFormat(VK_FORMAT_R8_UNORM, VK_FORMAT_R8G8_UNORM, VK_FORMAT_R8G8B8_UNORM,
VK_FORMAT_R8G8B8A8_UNORM);
case DataType::Int16:
return selectFormat(VK_FORMAT_R16_SNORM, VK_FORMAT_R16G16_SNORM, VK_FORMAT_R16G16B16_SNORM,
VK_FORMAT_R16G16B16A16_SNORM);
case DataType::Uint16:
return selectFormat(VK_FORMAT_R16_UNORM, VK_FORMAT_R16G16_UNORM, VK_FORMAT_R16G16B16_UNORM,
VK_FORMAT_R16G16B16A16_UNORM);
default:
MOBILEGL_ASSERT(false, "Unsupported normalized vertex attribute data type");
return VK_FORMAT_UNDEFINED;
}
}
switch (attrib.Type) {
case DataType::Float16:
return selectFormat(VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_R16G16B16_SFLOAT,
VK_FORMAT_R16G16B16A16_SFLOAT);
case DataType::Float32:
return selectFormat(VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT,
VK_FORMAT_R32G32B32A32_SFLOAT);
case DataType::Int8:
return selectFormat(VK_FORMAT_R8_SSCALED, VK_FORMAT_R8G8_SSCALED, VK_FORMAT_R8G8B8_SSCALED,
VK_FORMAT_R8G8B8A8_SSCALED);
case DataType::Uint8:
return selectFormat(VK_FORMAT_R8_USCALED, VK_FORMAT_R8G8_USCALED, VK_FORMAT_R8G8B8_USCALED,
VK_FORMAT_R8G8B8A8_USCALED);
case DataType::Int16:
return selectFormat(VK_FORMAT_R16_SSCALED, VK_FORMAT_R16G16_SSCALED, VK_FORMAT_R16G16B16_SSCALED,
VK_FORMAT_R16G16B16A16_SSCALED);
case DataType::Uint16:
return selectFormat(VK_FORMAT_R16_USCALED, VK_FORMAT_R16G16_USCALED, VK_FORMAT_R16G16B16_USCALED,
VK_FORMAT_R16G16B16A16_USCALED);
default:
MOBILEGL_ASSERT(false, "Unsupported floating-point vertex attribute data type");
return VK_FORMAT_UNDEFINED;
}
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,44 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Managers/VertexInputStateManager.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"
#include "MG_State/GLState/VertexArrayState/VertexArrayObject.h"
#include "Config.h"
#include "xxhash.h"
namespace MobileGL::MG_Backend::DirectVulkan {
class VertexInputStateManager {
public:
using VertexArrayObject = MobileGL::MG_State::GLState::VertexArrayObject;
using HashType = XXH64_hash_t;
struct VertexInputStateInfo {
VkPipelineVertexInputStateCreateInfo CreateInfo{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
Vector<VkVertexInputBindingDescription> BindingDescriptions;
Vector<VkVertexInputAttributeDescription> AttributeDescriptions;
};
VertexInputStateManager();
~VertexInputStateManager();
VertexInputStateInfo& CreatePipelineVertexInputState(VertexArrayObject* vaoObject);
VertexInputStateInfo* GetPipelineVertexInputState(HashType hash);
VertexInputStateInfo* GetPipelineVertexInputState(VertexArrayObject* vaoObject);
private:
void Cleanup();
static void PatchCreateInfoPointers(VertexInputStateInfo& stateInfo);
HashType GetHash(VertexArrayObject* vaoObject);
static VkFormat ResolveVertexFormat(const MG_State::GLState::VertexAttribute& attrib);
static Uint GetDataTypeByteSize(DataType type);
UnorderedMap<HashType, VertexInputStateInfo> m_vertexInputStateInfo;
XXH64_state_t* const m_hashState = XXH64_createState();
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -10,18 +10,6 @@
#include "MG_State/GLState/ProgramState/ProgramObject.h"
// #ifndef VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR
// #define VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR 0x00000001
// #endif
//
// #ifndef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME
// #define VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME "VK_KHR_portability_enumeration"
// #endif
//
// #ifndef VK_EXT_METAL_SURFACE_EXTENSION_NAME
// #define VK_EXT_METAL_SURFACE_EXTENSION_NAME "VK_EXT_metal_surface"
// #endif
namespace MobileGL::MG_Backend::DirectVulkan {
VkBool32 VulkanRenderer::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,