mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Feat] (MG_Backend/DirectVulkan): Implement DV::TmpImpl and use.
This commit is contained in:
@@ -208,6 +208,7 @@ set(SOURCE_FILES
|
|||||||
MobileGL/MG_Backend/DirectGLES/Managers.cpp
|
MobileGL/MG_Backend/DirectGLES/Managers.cpp
|
||||||
|
|
||||||
MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp
|
MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp
|
||||||
|
MobileGL/MG_Backend/DirectVulkan/TmpImpl.cpp
|
||||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanContext.cpp
|
MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanContext.cpp
|
||||||
MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainManager.cpp
|
MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainManager.cpp
|
||||||
MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineManager.cpp
|
MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineManager.cpp
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include <expected>
|
#include <expected>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <xxhash.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|||||||
@@ -7,13 +7,24 @@
|
|||||||
// End of Source File Header
|
// End of Source File Header
|
||||||
|
|
||||||
#include "DirectVulkan.h"
|
#include "DirectVulkan.h"
|
||||||
|
#include "TmpImpl.h"
|
||||||
|
constexpr bool USE_TMP_IMPL = true;
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
UniquePtr<VulkanRenderer> pVulkanRenderer = nullptr;
|
UniquePtr<VulkanRenderer> pVulkanRenderer = nullptr;
|
||||||
|
|
||||||
void Clear(GLbitfield mask) {}
|
void Clear(GLbitfield mask) {
|
||||||
|
if (USE_TMP_IMPL) {
|
||||||
|
MobileGL::Backend::DirectVulkan::TmpImpl::Clear(mask);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) {
|
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) {
|
||||||
|
if (USE_TMP_IMPL) {
|
||||||
|
MobileGL::Backend::DirectVulkan::TmpImpl::DrawElements(mode, count, type, indices);
|
||||||
|
return;
|
||||||
|
}
|
||||||
pVulkanRenderer->RenderFrame();
|
pVulkanRenderer->RenderFrame();
|
||||||
}
|
}
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
@@ -7,128 +7,95 @@
|
|||||||
// End of Source File Header
|
// End of Source File Header
|
||||||
|
|
||||||
#include "ProgramManager.h"
|
#include "ProgramManager.h"
|
||||||
#include "MG_Backend/DirectVulkan/Renderer/VulkanContext.h"
|
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
|
||||||
ProgramManager::ProgramManager(VulkanContext &ctx): m_ctx(ctx) {}
|
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
||||||
ProgramManager::~ProgramManager() {
|
ProgramManager::~ProgramManager() {
|
||||||
Cleanup();
|
for (auto& [_, stages] : m_cache) {
|
||||||
}
|
DestroyStages(stages);
|
||||||
|
|
||||||
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();
|
m_cache.clear();
|
||||||
XXH64_freeState(m_hashState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<VkPipelineShaderStageCreateInfo>& ProgramManager::CreatePipelineShaderStages(ProgramObject* programObject) {
|
ProgramManager::HashType ProgramManager::ComputeSpvHash(MG_State::GLState::ProgramObject* program) const {
|
||||||
auto hash = GetHash(programObject);
|
if (!program) return 0;
|
||||||
MOBILEGL_ASSERT(m_shaderStageCreateInfo.find(hash) == m_shaderStageCreateInfo.end(),
|
XXH64_state_t* state = XXH64_createState();
|
||||||
"A program with the same hash has already been created");
|
XXH64_reset(state, 0xC0FFEEu);
|
||||||
Vector<VkPipelineShaderStageCreateInfo> info;
|
auto& spirvs = program->GetGeneratedSpirv();
|
||||||
auto& shaders = programObject->GetAttachedShaders();
|
for (const auto& spv : spirvs) {
|
||||||
auto& spirvBinaries = programObject->GetGeneratedSpirv();
|
if (spv.empty()) continue;
|
||||||
MOBILEGL_ASSERT(shaders.size() == spirvBinaries.size(),
|
XXH64_update(state, spv.data(), spv.size() * sizeof(Uint));
|
||||||
"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);
|
|
||||||
}
|
}
|
||||||
|
HashType hash = XXH64_digest(state);
|
||||||
auto [it, inserted] = m_shaderStageCreateInfo.emplace(hash, Move(info));
|
XXH64_freeState(state);
|
||||||
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;
|
return hash;
|
||||||
}
|
}
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
|
||||||
|
VkShaderStageFlagBits ProgramManager::ToVkStage(ShaderStage stage) const {
|
||||||
|
switch (stage) {
|
||||||
|
case ShaderStage::Vertex:
|
||||||
|
return VK_SHADER_STAGE_VERTEX_BIT;
|
||||||
|
case ShaderStage::Fragment:
|
||||||
|
return VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
case ShaderStage::Geometry:
|
||||||
|
return VK_SHADER_STAGE_GEOMETRY_BIT;
|
||||||
|
case ShaderStage::TessControl:
|
||||||
|
return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
|
||||||
|
case ShaderStage::TessEval:
|
||||||
|
return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
|
||||||
|
case ShaderStage::Compute:
|
||||||
|
return VK_SHADER_STAGE_COMPUTE_BIT;
|
||||||
|
default:
|
||||||
|
return VK_SHADER_STAGE_ALL_GRAPHICS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramManager::DestroyStages(ProgramStages& stages) {
|
||||||
|
for (auto module : stages.modules) {
|
||||||
|
if (module != VK_NULL_HANDLE) {
|
||||||
|
vkDestroyShaderModule(m_ctx.GetDevice(), module, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stages.modules.clear();
|
||||||
|
stages.stages.clear();
|
||||||
|
stages.hash = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<VkPipelineShaderStageCreateInfo>& ProgramManager::CreatePipelineShaderStages(
|
||||||
|
MG_State::GLState::ProgramObject* program) {
|
||||||
|
auto& entry = m_cache[program];
|
||||||
|
HashType newHash = ComputeSpvHash(program);
|
||||||
|
if (!entry.stages.empty() && entry.hash == newHash) return entry.stages;
|
||||||
|
|
||||||
|
DestroyStages(entry);
|
||||||
|
entry.hash = newHash;
|
||||||
|
|
||||||
|
if (!program) return entry.stages;
|
||||||
|
|
||||||
|
auto& spirvs = program->GetGeneratedSpirv();
|
||||||
|
auto& shaders = program->GetAttachedShaders();
|
||||||
|
|
||||||
|
for (SizeT i = 0; i < spirvs.size(); ++i) {
|
||||||
|
auto& spv = spirvs[i];
|
||||||
|
if (spv.empty()) continue;
|
||||||
|
|
||||||
|
VkShaderModuleCreateInfo smci{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
|
||||||
|
smci.codeSize = spv.size() * sizeof(Uint);
|
||||||
|
smci.pCode = spv.data();
|
||||||
|
|
||||||
|
VkShaderModule module = VK_NULL_HANDLE;
|
||||||
|
VK_VERIFY(vkCreateShaderModule(m_ctx.GetDevice(), &smci, nullptr, &module), "vkCreateShaderModule");
|
||||||
|
|
||||||
|
VkPipelineShaderStageCreateInfo stage{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
|
||||||
|
ShaderStage shaderStage = ShaderStage::Unknown;
|
||||||
|
if (i < shaders.size()) shaderStage = shaders[i]->GetShaderStage();
|
||||||
|
stage.stage = ToVkStage(shaderStage);
|
||||||
|
stage.module = module;
|
||||||
|
stage.pName = "main";
|
||||||
|
|
||||||
|
entry.modules.push_back(module);
|
||||||
|
entry.stages.push_back(stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.stages;
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkan::VkManager
|
||||||
|
|||||||
@@ -7,30 +7,37 @@
|
|||||||
// End of Source File Header
|
// End of Source File Header
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Includes.h"
|
#include <Includes.h>
|
||||||
|
#include "../Renderer/VulkanContext.h"
|
||||||
|
#include "../Renderer/VkCommon.h"
|
||||||
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
||||||
#include "Config.h"
|
|
||||||
#include "xxhash.h"
|
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
|
||||||
class VulkanContext;
|
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
||||||
class ProgramManager {
|
class ProgramManager {
|
||||||
public:
|
public:
|
||||||
using ProgramObject = MobileGL::MG_State::GLState::ProgramObject;
|
using HashType = Uint64;
|
||||||
using HashType = XXH64_hash_t;
|
|
||||||
ProgramManager(VulkanContext& ctx);
|
explicit ProgramManager(VulkanContext& ctx) : m_ctx(ctx) {}
|
||||||
~ProgramManager();
|
~ProgramManager();
|
||||||
|
|
||||||
Vector<VkPipelineShaderStageCreateInfo>& CreatePipelineShaderStages(ProgramObject* programObject);
|
ProgramManager(const ProgramManager&) = delete;
|
||||||
Vector<VkPipelineShaderStageCreateInfo>* GetPipelineShaderStages(HashType hash);
|
ProgramManager& operator=(const ProgramManager&) = delete;
|
||||||
Vector<VkPipelineShaderStageCreateInfo>* GetPipelineShaderStages(ProgramObject* programObject);
|
|
||||||
|
Vector<VkPipelineShaderStageCreateInfo>& CreatePipelineShaderStages(
|
||||||
|
MG_State::GLState::ProgramObject* program);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct ProgramStages {
|
||||||
|
HashType hash = 0;
|
||||||
|
Vector<VkPipelineShaderStageCreateInfo> stages;
|
||||||
|
Vector<VkShaderModule> modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
void DestroyStages(ProgramStages& stages);
|
||||||
|
HashType ComputeSpvHash(MG_State::GLState::ProgramObject* program) const;
|
||||||
|
VkShaderStageFlagBits ToVkStage(ShaderStage stage) const;
|
||||||
|
|
||||||
void Cleanup();
|
|
||||||
HashType GetHash(ProgramObject* programObject);
|
|
||||||
VulkanContext& m_ctx;
|
VulkanContext& m_ctx;
|
||||||
UnorderedMap<HashType, Vector<VkPipelineShaderStageCreateInfo>> m_shaderStageCreateInfo;
|
UnorderedMap<const MG_State::GLState::ProgramObject*, ProgramStages> m_cache;
|
||||||
XXH64_state_t* const m_hashState = XXH64_createState();
|
|
||||||
};
|
};
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan::VkManager
|
||||||
|
|||||||
@@ -7,46 +7,43 @@
|
|||||||
// End of Source File Header
|
// End of Source File Header
|
||||||
|
|
||||||
#include "FrameContext.h"
|
#include "FrameContext.h"
|
||||||
#include "VulkanContext.h"
|
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
|
||||||
FrameContext::~FrameContext() = default;
|
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
||||||
void FrameContext::Initialize(VulkanContext& ctx, VkCommandPool pool) {
|
void FrameContext::Initialize(VulkanContext& ctx, VkCommandPool pool) {
|
||||||
// allocate cmd
|
CommandPool = pool;
|
||||||
VkCommandBufferAllocateInfo abci{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
|
VkCommandBufferAllocateInfo abci{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
|
||||||
abci.commandPool = pool;
|
abci.commandPool = pool;
|
||||||
abci.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
abci.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||||
abci.commandBufferCount = 1;
|
abci.commandBufferCount = 1;
|
||||||
VK_VERIFY(vkAllocateCommandBuffers(ctx.GetDevice(), &abci, &CommandBuffer), "vkAllocateCommandBuffers");
|
VK_VERIFY(vkAllocateCommandBuffers(ctx.GetDevice(), &abci, &CommandBuffer), "vkAllocateCommandBuffers");
|
||||||
|
|
||||||
// semaphores
|
|
||||||
VkSemaphoreCreateInfo sci{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
|
VkSemaphoreCreateInfo sci{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
|
||||||
VK_VERIFY(vkCreateSemaphore(ctx.GetDevice(), &sci, nullptr, &ImageAvailable),
|
VK_VERIFY(vkCreateSemaphore(ctx.GetDevice(), &sci, nullptr, &ImageAvailable), "vkCreateSemaphore");
|
||||||
"vkCreateSemaphore ImageAvailable");
|
VK_VERIFY(vkCreateSemaphore(ctx.GetDevice(), &sci, nullptr, &RenderFinished), "vkCreateSemaphore");
|
||||||
VK_VERIFY(vkCreateSemaphore(ctx.GetDevice(), &sci, nullptr, &RenderFinished),
|
|
||||||
"vkCreateSemaphore RenderFinished");
|
|
||||||
|
|
||||||
// fence (start signaled)
|
|
||||||
VkFenceCreateInfo fci{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
|
VkFenceCreateInfo fci{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
|
||||||
fci.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
fci.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||||
VK_VERIFY(vkCreateFence(ctx.GetDevice(), &fci, nullptr, &InFlightFence), "vkCreateFence");
|
VK_VERIFY(vkCreateFence(ctx.GetDevice(), &fci, nullptr, &InFlightFence), "vkCreateFence");
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameContext::Cleanup(VulkanContext& ctx) {
|
void FrameContext::Cleanup(VulkanContext& ctx) {
|
||||||
|
auto device = ctx.GetDevice();
|
||||||
|
if (InFlightFence != VK_NULL_HANDLE) {
|
||||||
|
vkDestroyFence(device, InFlightFence, nullptr);
|
||||||
|
InFlightFence = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
if (ImageAvailable != VK_NULL_HANDLE) {
|
if (ImageAvailable != VK_NULL_HANDLE) {
|
||||||
vkDestroySemaphore(ctx.GetDevice(), ImageAvailable, nullptr);
|
vkDestroySemaphore(device, ImageAvailable, nullptr);
|
||||||
ImageAvailable = VK_NULL_HANDLE;
|
ImageAvailable = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
if (RenderFinished != VK_NULL_HANDLE) {
|
if (RenderFinished != VK_NULL_HANDLE) {
|
||||||
vkDestroySemaphore(ctx.GetDevice(), RenderFinished, nullptr);
|
vkDestroySemaphore(device, RenderFinished, nullptr);
|
||||||
RenderFinished = VK_NULL_HANDLE;
|
RenderFinished = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
if (InFlightFence != VK_NULL_HANDLE) {
|
if (CommandBuffer != VK_NULL_HANDLE) {
|
||||||
vkDestroyFence(ctx.GetDevice(), InFlightFence, nullptr);
|
vkFreeCommandBuffers(device, CommandPool, 1, &CommandBuffer);
|
||||||
InFlightFence = VK_NULL_HANDLE;
|
CommandBuffer = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
CommandPool = VK_NULL_HANDLE;
|
||||||
CommandBuffer = VK_NULL_HANDLE;
|
|
||||||
}
|
}
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan::VkManager
|
||||||
|
|||||||
@@ -8,21 +8,35 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <Includes.h>
|
#include <Includes.h>
|
||||||
|
#include "VulkanContext.h"
|
||||||
|
#include "VkCommon.h"
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
class VulkanContext;
|
struct TrashBuffer {
|
||||||
|
VkBuffer buffer = VK_NULL_HANDLE;
|
||||||
struct FrameContext {
|
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||||
FrameContext() = default;
|
Bool mapped = false;
|
||||||
~FrameContext();
|
|
||||||
|
|
||||||
void Initialize(VulkanContext& ctx, VkCommandPool pool);
|
|
||||||
void Cleanup(VulkanContext& ctx);
|
|
||||||
|
|
||||||
VkCommandBuffer CommandBuffer = VK_NULL_HANDLE;
|
|
||||||
VkSemaphore ImageAvailable = VK_NULL_HANDLE;
|
|
||||||
VkSemaphore RenderFinished = VK_NULL_HANDLE;
|
|
||||||
VkFence InFlightFence = VK_NULL_HANDLE;
|
|
||||||
Uint32 CurrentImageIndex = 0;
|
|
||||||
};
|
};
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
struct TrashImage {
|
||||||
|
VkImage image = VK_NULL_HANDLE;
|
||||||
|
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||||
|
VkImageView view = VK_NULL_HANDLE;
|
||||||
|
Vector<VkImageView> mipViews;
|
||||||
|
};
|
||||||
|
namespace VkManager {
|
||||||
|
struct FrameContext {
|
||||||
|
VkCommandBuffer CommandBuffer = VK_NULL_HANDLE;
|
||||||
|
VkSemaphore ImageAvailable = VK_NULL_HANDLE;
|
||||||
|
VkSemaphore RenderFinished = VK_NULL_HANDLE;
|
||||||
|
VkFence InFlightFence = VK_NULL_HANDLE;
|
||||||
|
Uint32 CurrentImageIndex = 0;
|
||||||
|
VkCommandPool CommandPool = VK_NULL_HANDLE;
|
||||||
|
Vector<TrashBuffer> TrashBuffers;
|
||||||
|
Vector<TrashImage> TrashImages;
|
||||||
|
// VkCommandPool CommandPool = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
void Initialize(VulkanContext& ctx, VkCommandPool pool);
|
||||||
|
void Cleanup(VulkanContext& ctx);
|
||||||
|
};
|
||||||
|
} // namespace VkManager
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
|
|||||||
@@ -6,136 +6,6 @@
|
|||||||
// SPDX-License-Identifier: LGPL-3.0-only
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
// End of Source File Header
|
// End of Source File Header
|
||||||
|
|
||||||
#include "PipelineManager.h"
|
#include <Includes.h>
|
||||||
#include "VulkanContext.h"
|
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan::VkManager {}
|
||||||
PipelineManager::PipelineManager(VulkanContext& ctx) : Ctx(ctx) {}
|
|
||||||
PipelineManager::~PipelineManager() {
|
|
||||||
Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PipelineManager::EnsurePipelineLayout() {
|
|
||||||
if (PipelineLayout != VK_NULL_HANDLE) return;
|
|
||||||
VkPipelineLayoutCreateInfo plci{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
|
|
||||||
VK_VERIFY(vkCreatePipelineLayout(Ctx.GetDevice(), &plci, nullptr, &PipelineLayout), "vkCreatePipelineLayout");
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPipeline PipelineManager::CreateGraphicsPipelineFromSpv(const std::string& key,
|
|
||||||
const std::vector<uint32_t>& vsSpv,
|
|
||||||
const std::vector<uint32_t>& fsSpv,
|
|
||||||
VkRenderPass renderPass, VkExtent2D extent) {
|
|
||||||
if (Pipelines.find(key) != Pipelines.end()) {
|
|
||||||
MGLOG_W("Pipeline key '%s' already exists - returning existing", key.c_str());
|
|
||||||
return Pipelines[key];
|
|
||||||
}
|
|
||||||
EnsurePipelineLayout();
|
|
||||||
|
|
||||||
VkShaderModuleCreateInfo smci{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
|
|
||||||
smci.codeSize = vsSpv.size() * sizeof(uint32_t);
|
|
||||||
smci.pCode = vsSpv.data();
|
|
||||||
VkShaderModule vs;
|
|
||||||
VK_VERIFY(vkCreateShaderModule(Ctx.GetDevice(), &smci, nullptr, &vs), "vkCreateShaderModule VS");
|
|
||||||
|
|
||||||
smci.codeSize = fsSpv.size() * sizeof(uint32_t);
|
|
||||||
smci.pCode = fsSpv.data();
|
|
||||||
VkShaderModule fs;
|
|
||||||
VK_VERIFY(vkCreateShaderModule(Ctx.GetDevice(), &smci, nullptr, &fs), "vkCreateShaderModule FS");
|
|
||||||
|
|
||||||
VkPipelineShaderStageCreateInfo stages[2]{};
|
|
||||||
stages[0] = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
|
|
||||||
stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
|
||||||
stages[0].module = vs;
|
|
||||||
stages[0].pName = "main";
|
|
||||||
stages[1] = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
|
|
||||||
stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
||||||
stages[1].module = fs;
|
|
||||||
stages[1].pName = "main";
|
|
||||||
|
|
||||||
VkPipelineVertexInputStateCreateInfo vertexInput{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
|
|
||||||
vertexInput.vertexBindingDescriptionCount = 0;
|
|
||||||
vertexInput.vertexAttributeDescriptionCount = 0;
|
|
||||||
|
|
||||||
VkPipelineInputAssemblyStateCreateInfo ia{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
|
|
||||||
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
|
||||||
|
|
||||||
VkViewport vp{};
|
|
||||||
vp.x = 0;
|
|
||||||
vp.y = 0;
|
|
||||||
vp.width = (float)extent.width;
|
|
||||||
vp.height = (float)extent.height;
|
|
||||||
vp.minDepth = 0;
|
|
||||||
vp.maxDepth = 1;
|
|
||||||
VkRect2D scissor{{0, 0}, extent};
|
|
||||||
VkPipelineViewportStateCreateInfo vpci{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
|
|
||||||
vpci.viewportCount = 1;
|
|
||||||
vpci.pViewports = &vp;
|
|
||||||
vpci.scissorCount = 1;
|
|
||||||
vpci.pScissors = &scissor;
|
|
||||||
|
|
||||||
VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
|
|
||||||
raster.polygonMode = VK_POLYGON_MODE_FILL;
|
|
||||||
raster.cullMode = VK_CULL_MODE_NONE;
|
|
||||||
raster.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
|
||||||
raster.lineWidth = 1.0f;
|
|
||||||
|
|
||||||
VkPipelineMultisampleStateCreateInfo ms{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
|
|
||||||
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
|
||||||
|
|
||||||
VkPipelineColorBlendAttachmentState colorAttach{};
|
|
||||||
colorAttach.colorWriteMask =
|
|
||||||
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
|
||||||
colorAttach.blendEnable = VK_FALSE;
|
|
||||||
VkPipelineColorBlendStateCreateInfo blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
|
|
||||||
blend.attachmentCount = 1;
|
|
||||||
blend.pAttachments = &colorAttach;
|
|
||||||
|
|
||||||
VkGraphicsPipelineCreateInfo gpi{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
|
|
||||||
gpi.stageCount = 2;
|
|
||||||
gpi.pStages = stages;
|
|
||||||
gpi.pVertexInputState = &vertexInput;
|
|
||||||
gpi.pInputAssemblyState = &ia;
|
|
||||||
gpi.pViewportState = &vpci;
|
|
||||||
gpi.pRasterizationState = &raster;
|
|
||||||
gpi.pMultisampleState = &ms;
|
|
||||||
gpi.pColorBlendState = &blend;
|
|
||||||
gpi.layout = PipelineLayout;
|
|
||||||
gpi.renderPass = renderPass;
|
|
||||||
gpi.subpass = 0;
|
|
||||||
|
|
||||||
VkPipeline pipeline;
|
|
||||||
VK_VERIFY(vkCreateGraphicsPipelines(Ctx.GetDevice(), VK_NULL_HANDLE, 1, &gpi, nullptr, &pipeline),
|
|
||||||
"vkCreateGraphicsPipelines");
|
|
||||||
|
|
||||||
vkDestroyShaderModule(Ctx.GetDevice(), vs, nullptr);
|
|
||||||
vkDestroyShaderModule(Ctx.GetDevice(), fs, nullptr);
|
|
||||||
|
|
||||||
Pipelines.emplace(key, pipeline);
|
|
||||||
MGLOG_D("Pipeline '%s' created", key.c_str());
|
|
||||||
return pipeline;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPipeline PipelineManager::GetPipeline(const std::string& key) const {
|
|
||||||
auto it = Pipelines.find(key);
|
|
||||||
if (it == Pipelines.end()) return VK_NULL_HANDLE;
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PipelineManager::DestroyPipeline(const std::string& key) {
|
|
||||||
auto it = Pipelines.find(key);
|
|
||||||
if (it != Pipelines.end()) {
|
|
||||||
vkDestroyPipeline(Ctx.GetDevice(), it->second, nullptr);
|
|
||||||
Pipelines.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PipelineManager::Cleanup() {
|
|
||||||
for (auto& kv : Pipelines)
|
|
||||||
vkDestroyPipeline(Ctx.GetDevice(), kv.second, nullptr);
|
|
||||||
Pipelines.clear();
|
|
||||||
if (PipelineLayout != VK_NULL_HANDLE) {
|
|
||||||
vkDestroyPipelineLayout(Ctx.GetDevice(), PipelineLayout, nullptr);
|
|
||||||
PipelineLayout = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineManager.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 VulkanContext;
|
|
||||||
|
|
||||||
class PipelineManager {
|
|
||||||
public:
|
|
||||||
PipelineManager(VulkanContext& ctx);
|
|
||||||
~PipelineManager();
|
|
||||||
|
|
||||||
VkPipeline CreateGraphicsPipelineFromSpv(const std::string& key, const std::vector<uint32_t>& vsSpv,
|
|
||||||
const std::vector<uint32_t>& fsSpv, VkRenderPass renderPass,
|
|
||||||
VkExtent2D extent);
|
|
||||||
|
|
||||||
VkPipeline GetPipeline(const std::string& key) const;
|
|
||||||
void DestroyPipeline(const std::string& key);
|
|
||||||
void Cleanup();
|
|
||||||
|
|
||||||
private:
|
|
||||||
VulkanContext& Ctx;
|
|
||||||
VkPipelineLayout PipelineLayout = VK_NULL_HANDLE; // TODO: per-pipeline layouts?
|
|
||||||
std::unordered_map<std::string, VkPipeline> Pipelines;
|
|
||||||
|
|
||||||
void EnsurePipelineLayout();
|
|
||||||
};
|
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
|
||||||
@@ -7,127 +7,148 @@
|
|||||||
// End of Source File Header
|
// End of Source File Header
|
||||||
|
|
||||||
#include "SwapchainManager.h"
|
#include "SwapchainManager.h"
|
||||||
#include "VulkanContext.h"
|
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
||||||
SwapchainManager::SwapchainManager(VulkanContext& ctx) : Ctx(ctx) {}
|
namespace {
|
||||||
SwapchainManager::~SwapchainManager() {
|
VkSurfaceFormatKHR ChooseSurfaceFormat(const Vector<VkSurfaceFormatKHR>& formats) {
|
||||||
Cleanup();
|
for (const auto& f : formats) {
|
||||||
}
|
if ((f.format == VK_FORMAT_R8G8B8A8_UNORM || f.format == VK_FORMAT_B8G8R8A8_UNORM) &&
|
||||||
|
f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formats.empty() ? VkSurfaceFormatKHR{VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}
|
||||||
|
: formats[0];
|
||||||
|
}
|
||||||
|
|
||||||
void SwapchainManager::Initialize() {
|
VkPresentModeKHR ChoosePresentMode(const Vector<VkPresentModeKHR>& modes) {
|
||||||
CreateSwapchainInternal();
|
for (const auto& m : modes) {
|
||||||
CreateImageViews();
|
if (m == VK_PRESENT_MODE_MAILBOX_KHR) return m;
|
||||||
ImagesInFlight.resize(Images.size(), VK_NULL_HANDLE);
|
}
|
||||||
}
|
return VK_PRESENT_MODE_FIFO_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkExtent2D ChooseExtent(const VkSurfaceCapabilitiesKHR& caps, ANativeWindow* window) {
|
||||||
|
if (caps.currentExtent.width != UINT32_MAX) return caps.currentExtent;
|
||||||
|
VkExtent2D extent{640, 480};
|
||||||
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
if (window) {
|
||||||
|
extent.width = static_cast<Uint32>(ANativeWindow_getWidth(window));
|
||||||
|
extent.height = static_cast<Uint32>(ANativeWindow_getHeight(window));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)window;
|
||||||
|
#endif
|
||||||
|
extent.width = std::max(caps.minImageExtent.width, std::min(caps.maxImageExtent.width, extent.width));
|
||||||
|
extent.height = std::max(caps.minImageExtent.height, std::min(caps.maxImageExtent.height, extent.height));
|
||||||
|
return extent;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
SwapchainManager::~SwapchainManager() { DestroySwapchain(); }
|
||||||
|
|
||||||
|
void SwapchainManager::Initialize() { CreateSwapchain(VK_NULL_HANDLE); }
|
||||||
|
|
||||||
void SwapchainManager::Recreate() {
|
void SwapchainManager::Recreate() {
|
||||||
DestroyImageViews();
|
DestroySwapchain();
|
||||||
if (Swapchain != VK_NULL_HANDLE) {
|
CreateSwapchain(VK_NULL_HANDLE);
|
||||||
vkDestroySwapchainKHR(Ctx.GetDevice(), Swapchain, nullptr);
|
}
|
||||||
Swapchain = VK_NULL_HANDLE;
|
|
||||||
|
void SwapchainManager::SetFramebuffers(Vector<VkFramebuffer>&& framebuffers) { m_framebuffers = Move(framebuffers); }
|
||||||
|
|
||||||
|
void SwapchainManager::DestroySwapchain() {
|
||||||
|
auto device = m_ctx.GetDevice();
|
||||||
|
if (device == VK_NULL_HANDLE) return;
|
||||||
|
for (auto fb : m_framebuffers) {
|
||||||
|
if (fb != VK_NULL_HANDLE) vkDestroyFramebuffer(device, fb, nullptr);
|
||||||
}
|
}
|
||||||
CreateSwapchainInternal();
|
m_framebuffers.clear();
|
||||||
CreateImageViews();
|
|
||||||
ImagesInFlight.resize(Images.size(), VK_NULL_HANDLE);
|
|
||||||
|
|
||||||
Framebuffers.clear();
|
for (auto view : m_imageViews) {
|
||||||
MGLOG_D("Swapchain recreated");
|
if (view != VK_NULL_HANDLE) vkDestroyImageView(device, view, nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
void SwapchainManager::Cleanup() {
|
|
||||||
for (auto fb : Framebuffers)
|
|
||||||
if (fb != VK_NULL_HANDLE) vkDestroyFramebuffer(Ctx.GetDevice(), fb, nullptr);
|
|
||||||
Framebuffers.clear();
|
|
||||||
DestroyImageViews();
|
|
||||||
if (Swapchain != VK_NULL_HANDLE) {
|
|
||||||
vkDestroySwapchainKHR(Ctx.GetDevice(), Swapchain, nullptr);
|
|
||||||
Swapchain = VK_NULL_HANDLE;
|
|
||||||
}
|
}
|
||||||
}
|
m_imageViews.clear();
|
||||||
|
m_images.clear();
|
||||||
|
m_imagesInFlight.clear();
|
||||||
|
|
||||||
void SwapchainManager::SetFramebuffers(std::vector<VkFramebuffer>&& fbs) {
|
if (m_swapchain != VK_NULL_HANDLE) {
|
||||||
Framebuffers = std::move(fbs);
|
vkDestroySwapchainKHR(device, m_swapchain, nullptr);
|
||||||
}
|
m_swapchain = VK_NULL_HANDLE;
|
||||||
|
|
||||||
VkPresentModeKHR SwapchainManager::QueryPossiblePresentMode() {
|
|
||||||
const auto& surface = Ctx.GetSurface();
|
|
||||||
const auto& phys = Ctx.GetPhysicalDevice();
|
|
||||||
Uint32 modeCount;
|
|
||||||
vkGetPhysicalDeviceSurfacePresentModesKHR(phys, surface, &modeCount, NULL);
|
|
||||||
|
|
||||||
Vector<VkPresentModeKHR> modes(modeCount);
|
|
||||||
vkGetPhysicalDeviceSurfacePresentModesKHR(phys, surface, &modeCount, modes.data());
|
|
||||||
VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
|
|
||||||
|
|
||||||
for (auto m : modes) {
|
|
||||||
if (m == VK_PRESENT_MODE_MAILBOX_KHR) {
|
|
||||||
presentMode = m;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (m == VK_PRESENT_MODE_IMMEDIATE_KHR) {
|
|
||||||
presentMode = m;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
m_format = VK_FORMAT_UNDEFINED;
|
||||||
return presentMode;
|
m_extent = {0, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwapchainManager::CreateSwapchainInternal() {
|
void SwapchainManager::CreateSwapchain(VkSwapchainKHR oldSwapchain) {
|
||||||
VkSurfaceCapabilitiesKHR caps;
|
VkSurfaceCapabilitiesKHR caps{};
|
||||||
VK_VERIFY(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(Ctx.GetPhysicalDevice(), Ctx.GetSurface(), &caps),
|
VK_VERIFY(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_ctx.GetPhysicalDevice(), m_ctx.GetSurface(), &caps),
|
||||||
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
|
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
|
||||||
|
|
||||||
Extent = caps.currentExtent;
|
Uint32 fmtCount = 0;
|
||||||
ImageFormat = VK_FORMAT_R8G8B8A8_UNORM;
|
vkGetPhysicalDeviceSurfaceFormatsKHR(m_ctx.GetPhysicalDevice(), m_ctx.GetSurface(), &fmtCount, nullptr);
|
||||||
|
Vector<VkSurfaceFormatKHR> formats(fmtCount);
|
||||||
|
if (fmtCount > 0) {
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(m_ctx.GetPhysicalDevice(), m_ctx.GetSurface(), &fmtCount,
|
||||||
|
formats.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint32 modeCount = 0;
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(m_ctx.GetPhysicalDevice(), m_ctx.GetSurface(), &modeCount, nullptr);
|
||||||
|
Vector<VkPresentModeKHR> modes(modeCount);
|
||||||
|
if (modeCount > 0) {
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(m_ctx.GetPhysicalDevice(), m_ctx.GetSurface(), &modeCount,
|
||||||
|
modes.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSurfaceFormatKHR surfaceFormat = ChooseSurfaceFormat(formats);
|
||||||
|
VkPresentModeKHR presentMode = ChoosePresentMode(modes);
|
||||||
|
VkExtent2D extent = ChooseExtent(caps, m_ctx.GetWindow());
|
||||||
|
|
||||||
|
Uint32 imageCount = caps.minImageCount + 1;
|
||||||
|
if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) imageCount = caps.maxImageCount;
|
||||||
|
|
||||||
VkSwapchainCreateInfoKHR sci{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};
|
VkSwapchainCreateInfoKHR sci{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};
|
||||||
sci.surface = Ctx.GetSurface();
|
sci.surface = m_ctx.GetSurface();
|
||||||
sci.minImageCount = std::max<uint32_t>(2, caps.minImageCount);
|
sci.minImageCount = imageCount;
|
||||||
sci.imageFormat = ImageFormat;
|
sci.imageFormat = surfaceFormat.format;
|
||||||
sci.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
sci.imageColorSpace = surfaceFormat.colorSpace;
|
||||||
sci.imageExtent = Extent;
|
sci.imageExtent = extent;
|
||||||
sci.imageArrayLayers = 1;
|
sci.imageArrayLayers = 1;
|
||||||
sci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
sci.imageUsage =
|
||||||
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||||
sci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
sci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
sci.preTransform = caps.currentTransform;
|
sci.preTransform = caps.currentTransform;
|
||||||
sci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
sci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
sci.presentMode = QueryPossiblePresentMode();
|
sci.presentMode = presentMode;
|
||||||
|
sci.clipped = VK_TRUE;
|
||||||
|
sci.oldSwapchain = oldSwapchain;
|
||||||
|
|
||||||
VK_VERIFY(vkCreateSwapchainKHR(Ctx.GetDevice(), &sci, nullptr, &Swapchain), "vkCreateSwapchainKHR");
|
VK_VERIFY(vkCreateSwapchainKHR(m_ctx.GetDevice(), &sci, nullptr, &m_swapchain), "vkCreateSwapchainKHR");
|
||||||
|
|
||||||
uint32_t count = 0;
|
Uint32 actualCount = 0;
|
||||||
VK_VERIFY(vkGetSwapchainImagesKHR(Ctx.GetDevice(), Swapchain, &count, nullptr),
|
vkGetSwapchainImagesKHR(m_ctx.GetDevice(), m_swapchain, &actualCount, nullptr);
|
||||||
"vkGetSwapchainImagesKHR count");
|
m_images.resize(actualCount);
|
||||||
Images.resize(count);
|
vkGetSwapchainImagesKHR(m_ctx.GetDevice(), m_swapchain, &actualCount, m_images.data());
|
||||||
VK_VERIFY(vkGetSwapchainImagesKHR(Ctx.GetDevice(), Swapchain, &count, Images.data()),
|
|
||||||
"vkGetSwapchainImagesKHR images");
|
|
||||||
MGLOG_D("Swapchain created (%u images)", count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwapchainManager::CreateImageViews() {
|
m_imageViews.clear();
|
||||||
DestroyImageViews();
|
m_imageViews.reserve(actualCount);
|
||||||
ImageViews.resize(Images.size());
|
for (auto image : m_images) {
|
||||||
for (size_t i = 0; i < Images.size(); ++i) {
|
|
||||||
VkImageViewCreateInfo ivci{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
|
VkImageViewCreateInfo ivci{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
|
||||||
ivci.image = Images[i];
|
ivci.image = image;
|
||||||
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
ivci.format = ImageFormat;
|
ivci.format = surfaceFormat.format;
|
||||||
ivci.components = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B,
|
|
||||||
VK_COMPONENT_SWIZZLE_A};
|
|
||||||
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
ivci.subresourceRange.baseMipLevel = 0;
|
ivci.subresourceRange.baseMipLevel = 0;
|
||||||
ivci.subresourceRange.levelCount = 1;
|
ivci.subresourceRange.levelCount = 1;
|
||||||
ivci.subresourceRange.baseArrayLayer = 0;
|
ivci.subresourceRange.baseArrayLayer = 0;
|
||||||
ivci.subresourceRange.layerCount = 1;
|
ivci.subresourceRange.layerCount = 1;
|
||||||
VK_VERIFY(vkCreateImageView(Ctx.GetDevice(), &ivci, nullptr, &ImageViews[i]), "vkCreateImageView");
|
VkImageView view = VK_NULL_HANDLE;
|
||||||
|
VK_VERIFY(vkCreateImageView(m_ctx.GetDevice(), &ivci, nullptr, &view), "vkCreateImageView swapchain");
|
||||||
|
m_imageViews.push_back(view);
|
||||||
}
|
}
|
||||||
MGLOG_D("ImageViews created");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwapchainManager::DestroyImageViews() {
|
m_imagesInFlight.assign(actualCount, VK_NULL_HANDLE);
|
||||||
for (auto iv : ImageViews)
|
m_format = surfaceFormat.format;
|
||||||
if (iv != VK_NULL_HANDLE) vkDestroyImageView(Ctx.GetDevice(), iv, nullptr);
|
m_extent = extent;
|
||||||
ImageViews.clear();
|
|
||||||
}
|
}
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan::VkManager
|
||||||
|
|||||||
@@ -8,42 +8,41 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <Includes.h>
|
#include <Includes.h>
|
||||||
|
#include "VulkanContext.h"
|
||||||
|
#include "VkCommon.h"
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
||||||
class VulkanContext;
|
|
||||||
|
|
||||||
class SwapchainManager {
|
class SwapchainManager {
|
||||||
public:
|
public:
|
||||||
SwapchainManager(VulkanContext& ctx);
|
explicit SwapchainManager(VulkanContext& ctx) : m_ctx(ctx) {}
|
||||||
~SwapchainManager();
|
~SwapchainManager();
|
||||||
|
|
||||||
|
SwapchainManager(const SwapchainManager&) = delete;
|
||||||
|
SwapchainManager& operator=(const SwapchainManager&) = delete;
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
void Recreate();
|
void Recreate();
|
||||||
void Cleanup();
|
|
||||||
|
|
||||||
VkSwapchainKHR GetSwapchain() const { return Swapchain; }
|
VkSwapchainKHR GetSwapchain() const { return m_swapchain; }
|
||||||
VkFormat GetFormat() const { return ImageFormat; }
|
VkFormat GetFormat() const { return m_format; }
|
||||||
VkExtent2D GetExtent() const { return Extent; }
|
VkExtent2D GetExtent() const { return m_extent; }
|
||||||
const std::vector<VkImageView>& GetImageViews() const { return ImageViews; }
|
const Vector<VkImage>& GetImages() const { return m_images; }
|
||||||
const std::vector<VkFramebuffer>& GetFramebuffers() const { return Framebuffers; }
|
const Vector<VkImageView>& GetImageViews() const { return m_imageViews; }
|
||||||
const std::vector<VkImage>& GetImages() const { return Images; }
|
const Vector<VkFramebuffer>& GetFramebuffers() const { return m_framebuffers; }
|
||||||
std::vector<VkFence>& GetImagesInFlight() { return ImagesInFlight; }
|
void SetFramebuffers(Vector<VkFramebuffer>&& framebuffers);
|
||||||
|
Vector<VkFence>& GetImagesInFlight() { return m_imagesInFlight; }
|
||||||
void SetFramebuffers(std::vector<VkFramebuffer>&& fbs);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VulkanContext& Ctx;
|
void CreateSwapchain(VkSwapchainKHR oldSwapchain);
|
||||||
VkSwapchainKHR Swapchain = VK_NULL_HANDLE;
|
void DestroySwapchain();
|
||||||
VkFormat ImageFormat = VK_FORMAT_UNDEFINED;
|
|
||||||
VkExtent2D Extent{0, 0};
|
|
||||||
std::vector<VkImage> Images;
|
|
||||||
std::vector<VkImageView> ImageViews;
|
|
||||||
std::vector<VkFence> ImagesInFlight;
|
|
||||||
std::vector<VkFramebuffer> Framebuffers;
|
|
||||||
|
|
||||||
void CreateSwapchainInternal();
|
VulkanContext& m_ctx;
|
||||||
void CreateImageViews();
|
VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
|
||||||
void DestroyImageViews();
|
VkFormat m_format = VK_FORMAT_UNDEFINED;
|
||||||
VkPresentModeKHR QueryPossiblePresentMode();
|
VkExtent2D m_extent{0, 0};
|
||||||
|
Vector<VkImage> m_images;
|
||||||
|
Vector<VkImageView> m_imageViews;
|
||||||
|
Vector<VkFramebuffer> m_framebuffers;
|
||||||
|
Vector<VkFence> m_imagesInFlight;
|
||||||
};
|
};
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan::VkManager
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VkCommon.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::VkManager {
|
||||||
|
inline void VkCheck(VkResult result, const char* msg) {
|
||||||
|
if (result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) return;
|
||||||
|
MGLOG_E("Vulkan error %d at %s", static_cast<Int>(result), msg ? msg : "(unknown)");
|
||||||
|
throw RuntimeError(msg ? msg : "Vulkan error");
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkan::VkManager
|
||||||
|
|
||||||
|
#ifndef VK_VERIFY
|
||||||
|
#define VK_VERIFY(res, msg) ::MobileGL::MG_Backend::DirectVulkan::VkManager::VkCheck((res), (msg))
|
||||||
|
#endif
|
||||||
@@ -7,120 +7,159 @@
|
|||||||
// End of Source File Header
|
// End of Source File Header
|
||||||
|
|
||||||
#include "VulkanContext.h"
|
#include "VulkanContext.h"
|
||||||
#include "MG_Util/Types.h"
|
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
||||||
|
namespace {
|
||||||
|
Bool CheckDeviceExtensionSupport(VkPhysicalDevice device, const Vector<const char*>& required) {
|
||||||
|
Uint32 count = 0;
|
||||||
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &count, nullptr);
|
||||||
|
Vector<VkExtensionProperties> props(count);
|
||||||
|
if (count > 0) vkEnumerateDeviceExtensionProperties(device, nullptr, &count, props.data());
|
||||||
|
|
||||||
|
for (auto* ext : required) {
|
||||||
|
Bool found = false;
|
||||||
|
for (const auto& p : props) {
|
||||||
|
if (std::strcmp(p.extensionName, ext) == 0) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool FindGraphicsQueueFamily(VkPhysicalDevice device, VkSurfaceKHR surface, Uint32& outFamily) {
|
||||||
|
Uint32 count = 0;
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &count, nullptr);
|
||||||
|
if (count == 0) return false;
|
||||||
|
Vector<VkQueueFamilyProperties> props(count);
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &count, props.data());
|
||||||
|
|
||||||
|
for (Uint32 i = 0; i < count; ++i) {
|
||||||
|
if (!(props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) continue;
|
||||||
|
if (surface != VK_NULL_HANDLE) {
|
||||||
|
VkBool32 presentSupport = VK_FALSE;
|
||||||
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
|
||||||
|
if (!presentSupport) continue;
|
||||||
|
}
|
||||||
|
outFamily = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
VulkanContext::~VulkanContext() {
|
VulkanContext::~VulkanContext() {
|
||||||
Shutdown();
|
Cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanContext::Initialize(NativeWindowType window, const std::string& appName) {
|
void VulkanContext::Initialize(ANativeWindow* window, const char* appName) {
|
||||||
if (Initialized) return;
|
if (m_instance != VK_NULL_HANDLE) return;
|
||||||
CreateInstance(appName);
|
m_window = window;
|
||||||
|
CreateInstance(appName ? appName : "MobileGL-Vulkan");
|
||||||
CreateSurface(window);
|
CreateSurface(window);
|
||||||
PickPhysicalDevice();
|
PickPhysicalDevice();
|
||||||
CreateLogicalDevice();
|
CreateDevice();
|
||||||
Initialized = true;
|
|
||||||
MGLOG_D("VulkanContext initialized");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanContext::Shutdown() {
|
void VulkanContext::Cleanup() {
|
||||||
if (!Initialized && Instance == VK_NULL_HANDLE) return;
|
if (m_device != VK_NULL_HANDLE) {
|
||||||
if (Device != VK_NULL_HANDLE) {
|
vkDeviceWaitIdle(m_device);
|
||||||
vkDeviceWaitIdle(Device);
|
vkDestroyDevice(m_device, nullptr);
|
||||||
vkDestroyDevice(Device, nullptr);
|
m_device = VK_NULL_HANDLE;
|
||||||
Device = VK_NULL_HANDLE;
|
|
||||||
}
|
}
|
||||||
if (Surface != VK_NULL_HANDLE) {
|
if (m_surface != VK_NULL_HANDLE) {
|
||||||
vkDestroySurfaceKHR(Instance, Surface, nullptr);
|
vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
|
||||||
Surface = VK_NULL_HANDLE;
|
m_surface = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
if (Instance != VK_NULL_HANDLE) {
|
if (m_instance != VK_NULL_HANDLE) {
|
||||||
vkDestroyInstance(Instance, nullptr);
|
vkDestroyInstance(m_instance, nullptr);
|
||||||
Instance = VK_NULL_HANDLE;
|
m_instance = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
Initialized = false;
|
m_physicalDevice = VK_NULL_HANDLE;
|
||||||
MGLOG_D("VulkanContext shutdown");
|
m_graphicsQueue = VK_NULL_HANDLE;
|
||||||
|
m_graphicsQueueFamily = ~0u;
|
||||||
|
m_window = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanContext::CreateInstance(const std::string& appName) {
|
void VulkanContext::CreateInstance(const char* appName) {
|
||||||
VkApplicationInfo appInfo{VK_STRUCTURE_TYPE_APPLICATION_INFO};
|
VkApplicationInfo app{VK_STRUCTURE_TYPE_APPLICATION_INFO};
|
||||||
appInfo.pApplicationName = appName.c_str();
|
app.pApplicationName = appName;
|
||||||
appInfo.apiVersion = VK_API_VERSION_1_1;
|
app.applicationVersion = VK_MAKE_VERSION(1, 1, 0);
|
||||||
|
app.pEngineName = "MobileGL";
|
||||||
|
app.engineVersion = VK_MAKE_VERSION(1, 1, 0);
|
||||||
|
app.apiVersion = VK_API_VERSION_1_1;
|
||||||
|
|
||||||
const char* exts[] = {VK_KHR_SURFACE_EXTENSION_NAME,
|
Vector<const char*> extensions;
|
||||||
#if __ANDROID__
|
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||||
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
||||||
#endif
|
#endif
|
||||||
}; // TODO: support more platforms
|
|
||||||
|
|
||||||
VkInstanceCreateInfo ci{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
|
VkInstanceCreateInfo ici{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
|
||||||
ci.pApplicationInfo = &appInfo;
|
ici.pApplicationInfo = &app;
|
||||||
ci.enabledExtensionCount = 2;
|
ici.enabledExtensionCount = static_cast<Uint32>(extensions.size());
|
||||||
ci.ppEnabledExtensionNames = exts;
|
ici.ppEnabledExtensionNames = extensions.data();
|
||||||
|
|
||||||
VK_VERIFY(vkCreateInstance(&ci, nullptr, &Instance), "vkCreateInstance failed");
|
VK_VERIFY(vkCreateInstance(&ici, nullptr, &m_instance), "vkCreateInstance");
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanContext::CreateSurface(NativeWindowType window) {
|
void VulkanContext::CreateSurface(ANativeWindow* window) {
|
||||||
#if __ANDROID__
|
if (!window) return;
|
||||||
if (!Instance) throw RuntimeError("Instance not created");
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
|
||||||
auto* nativeWindow = static_cast<ANativeWindow*>(window);
|
|
||||||
if (!nativeWindow) throw RuntimeError("ANativeWindowType is null");
|
|
||||||
|
|
||||||
VkAndroidSurfaceCreateInfoKHR sci{VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR};
|
VkAndroidSurfaceCreateInfoKHR sci{VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR};
|
||||||
sci.window = nativeWindow;
|
sci.window = window;
|
||||||
VK_VERIFY(vkCreateAndroidSurfaceKHR(Instance, &sci, nullptr, &Surface), "vkCreateAndroidSurfaceKHR failed");
|
VK_VERIFY(vkCreateAndroidSurfaceKHR(m_instance, &sci, nullptr, &m_surface), "vkCreateAndroidSurfaceKHR");
|
||||||
#else
|
#else
|
||||||
MGLOG_W("VulkanRenderer::Initialize called on a platform which is not supported yet"); // TODO: support more
|
(void)window;
|
||||||
// platforms
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanContext::PickPhysicalDevice() {
|
void VulkanContext::PickPhysicalDevice() {
|
||||||
uint32_t count = 0;
|
Uint32 count = 0;
|
||||||
VK_VERIFY(vkEnumeratePhysicalDevices(Instance, &count, nullptr), "vkEnumeratePhysicalDevices count");
|
VK_VERIFY(vkEnumeratePhysicalDevices(m_instance, &count, nullptr), "vkEnumeratePhysicalDevices");
|
||||||
if (count == 0) throw RuntimeError("No physical devices");
|
if (count == 0) throw RuntimeError("No Vulkan physical devices found");
|
||||||
std::vector<VkPhysicalDevice> devs(count);
|
|
||||||
VK_VERIFY(vkEnumeratePhysicalDevices(Instance, &count, devs.data()), "vkEnumeratePhysicalDevices");
|
|
||||||
|
|
||||||
for (auto d : devs) {
|
Vector<VkPhysicalDevice> devices(count);
|
||||||
uint32_t qcount = 0;
|
VK_VERIFY(vkEnumeratePhysicalDevices(m_instance, &count, devices.data()), "vkEnumeratePhysicalDevices list");
|
||||||
vkGetPhysicalDeviceQueueFamilyProperties(d, &qcount, nullptr);
|
|
||||||
std::vector<VkQueueFamilyProperties> qprops(qcount);
|
Vector<const char*> requiredExts = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
||||||
vkGetPhysicalDeviceQueueFamilyProperties(d, &qcount, qprops.data());
|
|
||||||
for (uint32_t i = 0; i < qcount; ++i) {
|
for (auto device : devices) {
|
||||||
VkBool32 present = VK_FALSE;
|
Uint32 family = ~0u;
|
||||||
vkGetPhysicalDeviceSurfaceSupportKHR(d, i, Surface, &present);
|
if (!FindGraphicsQueueFamily(device, m_surface, family)) continue;
|
||||||
if ((qprops[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && present) {
|
if (!CheckDeviceExtensionSupport(device, requiredExts)) continue;
|
||||||
PhysicalDevice = d;
|
|
||||||
GraphicsQueueFamily = i;
|
m_physicalDevice = device;
|
||||||
MGLOG_D("Picked physical device, queue family %u", i);
|
m_graphicsQueueFamily = family;
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (m_physicalDevice == VK_NULL_HANDLE) {
|
||||||
|
throw RuntimeError("No suitable Vulkan physical device found");
|
||||||
}
|
}
|
||||||
throw RuntimeError("No suitable physical device");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanContext::CreateLogicalDevice() {
|
void VulkanContext::CreateDevice() {
|
||||||
float prio = 1.0f;
|
float priority = 1.0f;
|
||||||
VkDeviceQueueCreateInfo dqci{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO};
|
VkDeviceQueueCreateInfo qci{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO};
|
||||||
dqci.queueFamilyIndex = GraphicsQueueFamily;
|
qci.queueFamilyIndex = m_graphicsQueueFamily;
|
||||||
dqci.queueCount = 1;
|
qci.queueCount = 1;
|
||||||
dqci.pQueuePriorities = &prio;
|
qci.pQueuePriorities = &priority;
|
||||||
|
|
||||||
const char* devExts[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
Vector<const char*> deviceExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
||||||
|
|
||||||
|
VkPhysicalDeviceFeatures features{};
|
||||||
|
|
||||||
VkDeviceCreateInfo dci{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
|
VkDeviceCreateInfo dci{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
|
||||||
dci.queueCreateInfoCount = 1;
|
dci.queueCreateInfoCount = 1;
|
||||||
dci.pQueueCreateInfos = &dqci;
|
dci.pQueueCreateInfos = &qci;
|
||||||
dci.enabledExtensionCount = 1;
|
dci.enabledExtensionCount = static_cast<Uint32>(deviceExtensions.size());
|
||||||
dci.ppEnabledExtensionNames = devExts;
|
dci.ppEnabledExtensionNames = deviceExtensions.data();
|
||||||
|
dci.pEnabledFeatures = &features;
|
||||||
|
|
||||||
VK_VERIFY(vkCreateDevice(PhysicalDevice, &dci, nullptr, &Device), "vkCreateDevice failed");
|
VK_VERIFY(vkCreateDevice(m_physicalDevice, &dci, nullptr, &m_device), "vkCreateDevice");
|
||||||
vkGetDeviceQueue(Device, GraphicsQueueFamily, 0, &GraphicsQueue);
|
vkGetDeviceQueue(m_device, m_graphicsQueueFamily, 0, &m_graphicsQueue);
|
||||||
MGLOG_D("Logical device created");
|
|
||||||
}
|
}
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan::VkManager
|
||||||
|
|||||||
@@ -8,43 +8,40 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <Includes.h>
|
#include <Includes.h>
|
||||||
|
#include "VkCommon.h"
|
||||||
|
|
||||||
#define VK_VERIFY(expr, ...) \
|
namespace MobileGL::MG_Backend::DirectVulkan::VkManager {
|
||||||
do { \
|
|
||||||
VkResult _vk_verify_result = (expr); \
|
|
||||||
if (_vk_verify_result != VK_SUCCESS) { \
|
|
||||||
MGLOG_E("Vulkan error %d at %s:%d" __VA_OPT__(" - ") __VA_ARGS__, _vk_verify_result, __FILE__, __LINE__); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
|
||||||
class VulkanContext {
|
class VulkanContext {
|
||||||
public:
|
public:
|
||||||
VulkanContext() = default;
|
VulkanContext() = default;
|
||||||
~VulkanContext();
|
~VulkanContext();
|
||||||
|
|
||||||
void Initialize(NativeWindowType window, const std::string& appName = "MobileGL-VulkanRenderer");
|
VulkanContext(const VulkanContext&) = delete;
|
||||||
void Shutdown();
|
VulkanContext& operator=(const VulkanContext&) = delete;
|
||||||
|
|
||||||
VkInstance GetInstance() const { return Instance; }
|
void Initialize(ANativeWindow* window, const char* appName);
|
||||||
VkPhysicalDevice GetPhysicalDevice() const { return PhysicalDevice; }
|
void Cleanup();
|
||||||
VkDevice GetDevice() const { return Device; }
|
|
||||||
VkQueue GetGraphicsQueue() const { return GraphicsQueue; }
|
VkInstance GetInstance() const { return m_instance; }
|
||||||
uint32_t GetGraphicsQueueFamily() const { return GraphicsQueueFamily; }
|
VkPhysicalDevice GetPhysicalDevice() const { return m_physicalDevice; }
|
||||||
VkSurfaceKHR GetSurface() const { return Surface; }
|
VkDevice GetDevice() const { return m_device; }
|
||||||
|
VkQueue GetGraphicsQueue() const { return m_graphicsQueue; }
|
||||||
|
Uint32 GetGraphicsQueueFamily() const { return m_graphicsQueueFamily; }
|
||||||
|
VkSurfaceKHR GetSurface() const { return m_surface; }
|
||||||
|
ANativeWindow* GetWindow() const { return m_window; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateInstance(const std::string& appName);
|
void CreateInstance(const char* appName);
|
||||||
void CreateSurface(NativeWindowType window);
|
void CreateSurface(ANativeWindow* window);
|
||||||
void PickPhysicalDevice();
|
void PickPhysicalDevice();
|
||||||
void CreateLogicalDevice();
|
void CreateDevice();
|
||||||
|
|
||||||
VkInstance Instance = VK_NULL_HANDLE;
|
VkInstance m_instance = VK_NULL_HANDLE;
|
||||||
VkPhysicalDevice PhysicalDevice = VK_NULL_HANDLE;
|
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
|
||||||
VkDevice Device = VK_NULL_HANDLE;
|
VkDevice m_device = VK_NULL_HANDLE;
|
||||||
VkQueue GraphicsQueue = VK_NULL_HANDLE;
|
VkQueue m_graphicsQueue = VK_NULL_HANDLE;
|
||||||
uint32_t GraphicsQueueFamily = UINT32_MAX;
|
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
||||||
VkSurfaceKHR Surface = VK_NULL_HANDLE;
|
Uint32 m_graphicsQueueFamily = ~0u;
|
||||||
bool Initialized = false;
|
ANativeWindow* m_window = nullptr;
|
||||||
};
|
};
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan::VkManager
|
||||||
|
|||||||
@@ -7,64 +7,68 @@
|
|||||||
// End of Source File Header
|
// End of Source File Header
|
||||||
|
|
||||||
#include "VulkanRenderer.h"
|
#include "VulkanRenderer.h"
|
||||||
#include "VulkanContext.h"
|
|
||||||
#include "SwapchainManager.h"
|
|
||||||
#include "PipelineManager.h"
|
|
||||||
#include "FrameContext.h"
|
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
VulkanRenderer::VulkanRenderer(NativeWindowType window, const RendererConfig& cfg) : Window(window), Config(cfg) {
|
namespace {
|
||||||
Ctx = std::make_unique<VulkanContext>();
|
VkShaderModule CreateShaderModule(VkDevice device, const Vector<Uint>& spv) {
|
||||||
}
|
VkShaderModuleCreateInfo smci{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
|
||||||
|
smci.codeSize = spv.size() * sizeof(Uint);
|
||||||
|
smci.pCode = spv.data();
|
||||||
|
VkShaderModule module = VK_NULL_HANDLE;
|
||||||
|
VK_VERIFY(vkCreateShaderModule(device, &smci, nullptr, &module), "vkCreateShaderModule");
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
VulkanRenderer::VulkanRenderer(ANativeWindow* window) : m_window(window) {}
|
||||||
|
|
||||||
VulkanRenderer::~VulkanRenderer() {
|
VulkanRenderer::~VulkanRenderer() {
|
||||||
Shutdown();
|
if (!m_initialized) return;
|
||||||
|
vkDeviceWaitIdle(m_ctx.GetDevice());
|
||||||
|
DestroyPipelines();
|
||||||
|
DestroyFrameResources();
|
||||||
|
DestroyRenderPass();
|
||||||
|
if (m_commandPool != VK_NULL_HANDLE) {
|
||||||
|
vkDestroyCommandPool(m_ctx.GetDevice(), m_commandPool, nullptr);
|
||||||
|
m_commandPool = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
if (m_swapchain) {
|
||||||
|
m_swapchain.reset();
|
||||||
|
}
|
||||||
|
m_ctx.Cleanup();
|
||||||
|
m_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::Initialize() {
|
void VulkanRenderer::Initialize() {
|
||||||
Ctx->Initialize(Window, Config.AppName);
|
if (m_initialized) return;
|
||||||
|
m_ctx.Initialize(m_window, "MobileGL-VulkanRenderer");
|
||||||
Swapchain = std::make_unique<SwapchainManager>(*Ctx);
|
m_swapchain = MakeUnique<VkManager::SwapchainManager>(m_ctx);
|
||||||
Swapchain->Initialize();
|
m_swapchain->Initialize();
|
||||||
|
|
||||||
CreateRenderPass();
|
|
||||||
|
|
||||||
PipelineMgr = std::make_unique<PipelineManager>(*Ctx);
|
|
||||||
|
|
||||||
CreateCommandPool();
|
CreateCommandPool();
|
||||||
|
CreateRenderPass();
|
||||||
|
CreateFramebuffers();
|
||||||
CreateFrameResources();
|
CreateFrameResources();
|
||||||
|
m_initialized = true;
|
||||||
FrameBegin();
|
|
||||||
MGLOG_D("VulkanRenderer initialized");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::Shutdown() {
|
void VulkanRenderer::EnsureInitialized() {
|
||||||
if (!Ctx) return;
|
if (!m_initialized) {
|
||||||
|
throw RuntimeError("VulkanRenderer not initialized");
|
||||||
vkDeviceWaitIdle(Ctx->GetDevice());
|
|
||||||
|
|
||||||
DestroyFrameResources();
|
|
||||||
DestroyCommandPool();
|
|
||||||
|
|
||||||
if (PipelineMgr) {
|
|
||||||
PipelineMgr->Cleanup();
|
|
||||||
PipelineMgr.reset();
|
|
||||||
}
|
}
|
||||||
DestroyRenderPass();
|
}
|
||||||
if (Swapchain) {
|
|
||||||
Swapchain->Cleanup();
|
void VulkanRenderer::CreateCommandPool() {
|
||||||
Swapchain.reset();
|
if (m_commandPool != VK_NULL_HANDLE) return;
|
||||||
}
|
VkCommandPoolCreateInfo cpci{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
|
||||||
if (Ctx) {
|
cpci.queueFamilyIndex = m_ctx.GetGraphicsQueueFamily();
|
||||||
Ctx->Shutdown();
|
cpci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||||
Ctx.reset();
|
VK_VERIFY(vkCreateCommandPool(m_ctx.GetDevice(), &cpci, nullptr, &m_commandPool), "vkCreateCommandPool");
|
||||||
}
|
|
||||||
MGLOG_D("VulkanRenderer shutdown");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::CreateRenderPass() {
|
void VulkanRenderer::CreateRenderPass() {
|
||||||
|
DestroyRenderPass();
|
||||||
VkAttachmentDescription color{};
|
VkAttachmentDescription color{};
|
||||||
color.format = Swapchain->GetFormat();
|
color.format = m_swapchain->GetFormat();
|
||||||
color.samples = VK_SAMPLE_COUNT_1_BIT;
|
color.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||||
color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||||
@@ -72,220 +76,282 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
color.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
color.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||||
|
|
||||||
VkAttachmentReference colorRef{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
VkAttachmentReference colorRef{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
||||||
|
|
||||||
VkSubpassDescription sub{};
|
VkSubpassDescription sub{};
|
||||||
sub.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
sub.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||||
sub.colorAttachmentCount = 1;
|
sub.colorAttachmentCount = 1;
|
||||||
sub.pColorAttachments = &colorRef;
|
sub.pColorAttachments = &colorRef;
|
||||||
|
|
||||||
|
VkSubpassDependency dep{};
|
||||||
|
dep.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||||
|
dep.dstSubpass = 0;
|
||||||
|
dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||||
|
dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||||
|
dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||||
|
|
||||||
VkRenderPassCreateInfo rpci{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};
|
VkRenderPassCreateInfo rpci{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};
|
||||||
rpci.attachmentCount = 1;
|
rpci.attachmentCount = 1;
|
||||||
rpci.pAttachments = &color;
|
rpci.pAttachments = &color;
|
||||||
rpci.subpassCount = 1;
|
rpci.subpassCount = 1;
|
||||||
rpci.pSubpasses = ⊂
|
rpci.pSubpasses = ⊂
|
||||||
|
rpci.dependencyCount = 1;
|
||||||
|
rpci.pDependencies = &dep;
|
||||||
|
|
||||||
VK_VERIFY(vkCreateRenderPass(Ctx->GetDevice(), &rpci, nullptr, &RenderPass), "vkCreateRenderPass");
|
VK_VERIFY(vkCreateRenderPass(m_ctx.GetDevice(), &rpci, nullptr, &m_renderPass), "vkCreateRenderPass");
|
||||||
|
|
||||||
// Create framebuffers now (use swapchain imageviews)
|
|
||||||
const auto& imageViews = Swapchain->GetImageViews();
|
|
||||||
std::vector<VkFramebuffer> fbs;
|
|
||||||
fbs.reserve(imageViews.size());
|
|
||||||
for (auto iv : imageViews) {
|
|
||||||
VkImageView attachments[] = {iv};
|
|
||||||
VkFramebufferCreateInfo fbci{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
|
|
||||||
fbci.renderPass = RenderPass;
|
|
||||||
fbci.attachmentCount = 1;
|
|
||||||
fbci.pAttachments = attachments;
|
|
||||||
fbci.width = Swapchain->GetExtent().width;
|
|
||||||
fbci.height = Swapchain->GetExtent().height;
|
|
||||||
fbci.layers = 1;
|
|
||||||
VkFramebuffer fb;
|
|
||||||
VK_VERIFY(vkCreateFramebuffer(Ctx->GetDevice(), &fbci, nullptr, &fb), "vkCreateFramebuffer");
|
|
||||||
fbs.push_back(fb);
|
|
||||||
}
|
|
||||||
Swapchain->SetFramebuffers(std::move(fbs));
|
|
||||||
MGLOG_D("RenderPass created and framebuffers set");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::DestroyRenderPass() {
|
void VulkanRenderer::DestroyRenderPass() {
|
||||||
if (RenderPass != VK_NULL_HANDLE) {
|
if (m_renderPass != VK_NULL_HANDLE) {
|
||||||
vkDestroyRenderPass(Ctx->GetDevice(), RenderPass, nullptr);
|
vkDestroyRenderPass(m_ctx.GetDevice(), m_renderPass, nullptr);
|
||||||
RenderPass = VK_NULL_HANDLE;
|
m_renderPass = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::CreateCommandPool() {
|
void VulkanRenderer::CreateFramebuffers() {
|
||||||
VkCommandPoolCreateInfo cpci{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
|
const auto& views = m_swapchain->GetImageViews();
|
||||||
cpci.queueFamilyIndex = Ctx->GetGraphicsQueueFamily();
|
Vector<VkFramebuffer> fbs;
|
||||||
cpci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
fbs.reserve(views.size());
|
||||||
VK_VERIFY(vkCreateCommandPool(Ctx->GetDevice(), &cpci, nullptr, &CommandPool), "vkCreateCommandPool");
|
for (auto view : views) {
|
||||||
}
|
VkFramebufferCreateInfo fbci{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
|
||||||
|
fbci.renderPass = m_renderPass;
|
||||||
void VulkanRenderer::DestroyCommandPool() {
|
fbci.attachmentCount = 1;
|
||||||
if (CommandPool != VK_NULL_HANDLE) {
|
fbci.pAttachments = &view;
|
||||||
vkDestroyCommandPool(Ctx->GetDevice(), CommandPool, nullptr);
|
fbci.width = m_swapchain->GetExtent().width;
|
||||||
CommandPool = VK_NULL_HANDLE;
|
fbci.height = m_swapchain->GetExtent().height;
|
||||||
|
fbci.layers = 1;
|
||||||
|
VkFramebuffer fb = VK_NULL_HANDLE;
|
||||||
|
VK_VERIFY(vkCreateFramebuffer(m_ctx.GetDevice(), &fbci, nullptr, &fb), "vkCreateFramebuffer");
|
||||||
|
fbs.push_back(fb);
|
||||||
}
|
}
|
||||||
|
m_swapchain->SetFramebuffers(Move(fbs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::CreateFrameResources() {
|
void VulkanRenderer::CreateFrameResources() {
|
||||||
uint32_t imageCount = static_cast<uint32_t>(Swapchain->GetImageViews().size());
|
DestroyFrameResources();
|
||||||
|
Uint32 imageCount = static_cast<Uint32>(m_swapchain->GetImageViews().size());
|
||||||
if (imageCount == 0) throw RuntimeError("Swapchain has zero images");
|
if (imageCount == 0) throw RuntimeError("Swapchain has zero images");
|
||||||
uint32_t frames = std::min<uint32_t>(Config.MaxFramesInFlight, imageCount);
|
Uint32 frames = std::min<Uint32>(2, imageCount);
|
||||||
Frames.clear();
|
for (Uint32 i = 0; i < frames; ++i) {
|
||||||
for (uint32_t i = 0; i < frames; ++i) {
|
auto frame = MakeUnique<VkManager::FrameContext>();
|
||||||
auto fr = std::make_unique<FrameContext>();
|
frame->Initialize(m_ctx, m_commandPool);
|
||||||
fr->Initialize(*Ctx, CommandPool);
|
m_frames.push_back(Move(frame));
|
||||||
Frames.push_back(std::move(fr));
|
|
||||||
}
|
}
|
||||||
CurrentFrame = 0;
|
m_currentFrame = 0;
|
||||||
MGLOG_D("FrameResources created: %u", (uint32_t)Frames.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::DestroyFrameResources() {
|
void VulkanRenderer::DestroyFrameResources() {
|
||||||
for (auto& f : Frames) {
|
for (auto& frame : m_frames) {
|
||||||
if (f) f->Cleanup(*Ctx);
|
if (frame) frame->Cleanup(m_ctx);
|
||||||
}
|
}
|
||||||
Frames.clear();
|
m_frames.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::RecordFrameCommandBuffer(FrameContext& frame, uint32_t imageIndex) {
|
void VulkanRenderer::DestroyPipelines() {
|
||||||
// Begin
|
auto device = m_ctx.GetDevice();
|
||||||
|
for (auto& [_, info] : m_pipelines) {
|
||||||
|
if (info.pipeline != VK_NULL_HANDLE) vkDestroyPipeline(device, info.pipeline, nullptr);
|
||||||
|
if (info.layout != VK_NULL_HANDLE) vkDestroyPipelineLayout(device, info.layout, nullptr);
|
||||||
|
}
|
||||||
|
m_pipelines.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPipeline VulkanRenderer::CreateGraphicsPipelineFromSpv(const String& name, const Vector<Uint>& vertexSpv,
|
||||||
|
const Vector<Uint>& fragmentSpv) {
|
||||||
|
EnsureInitialized();
|
||||||
|
auto it = m_pipelines.find(name);
|
||||||
|
if (it != m_pipelines.end()) return it->second.pipeline;
|
||||||
|
|
||||||
|
VkShaderModule vs = CreateShaderModule(m_ctx.GetDevice(), vertexSpv);
|
||||||
|
VkShaderModule fs = CreateShaderModule(m_ctx.GetDevice(), fragmentSpv);
|
||||||
|
|
||||||
|
VkPipelineShaderStageCreateInfo stages[2] = {};
|
||||||
|
stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||||
|
stages[0].module = vs;
|
||||||
|
stages[0].pName = "main";
|
||||||
|
stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
stages[1].module = fs;
|
||||||
|
stages[1].pName = "main";
|
||||||
|
|
||||||
|
VkPipelineVertexInputStateCreateInfo vertexInput{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
|
||||||
|
|
||||||
|
VkPipelineInputAssemblyStateCreateInfo ia{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
|
||||||
|
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||||
|
ia.primitiveRestartEnable = VK_FALSE;
|
||||||
|
|
||||||
|
VkPipelineViewportStateCreateInfo vpci{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
|
||||||
|
vpci.viewportCount = 1;
|
||||||
|
vpci.scissorCount = 1;
|
||||||
|
|
||||||
|
VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
|
||||||
|
raster.polygonMode = VK_POLYGON_MODE_FILL;
|
||||||
|
raster.cullMode = VK_CULL_MODE_NONE;
|
||||||
|
raster.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||||
|
raster.lineWidth = 1.0f;
|
||||||
|
|
||||||
|
VkPipelineMultisampleStateCreateInfo ms{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
|
||||||
|
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
|
||||||
|
VkPipelineColorBlendAttachmentState colorAttach{};
|
||||||
|
colorAttach.colorWriteMask =
|
||||||
|
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||||
|
colorAttach.blendEnable = VK_FALSE;
|
||||||
|
|
||||||
|
VkPipelineColorBlendStateCreateInfo blend{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
|
||||||
|
blend.attachmentCount = 1;
|
||||||
|
blend.pAttachments = &colorAttach;
|
||||||
|
|
||||||
|
VkDynamicState dynamics[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
|
||||||
|
VkPipelineDynamicStateCreateInfo dyn{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};
|
||||||
|
dyn.dynamicStateCount = 2;
|
||||||
|
dyn.pDynamicStates = dynamics;
|
||||||
|
|
||||||
|
VkPipelineLayoutCreateInfo plci{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
|
||||||
|
VkPipelineLayout layout = VK_NULL_HANDLE;
|
||||||
|
VK_VERIFY(vkCreatePipelineLayout(m_ctx.GetDevice(), &plci, nullptr, &layout), "vkCreatePipelineLayout");
|
||||||
|
|
||||||
|
VkGraphicsPipelineCreateInfo gpi{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
|
||||||
|
gpi.stageCount = 2;
|
||||||
|
gpi.pStages = stages;
|
||||||
|
gpi.pVertexInputState = &vertexInput;
|
||||||
|
gpi.pInputAssemblyState = &ia;
|
||||||
|
gpi.pViewportState = &vpci;
|
||||||
|
gpi.pRasterizationState = &raster;
|
||||||
|
gpi.pMultisampleState = &ms;
|
||||||
|
gpi.pColorBlendState = &blend;
|
||||||
|
gpi.pDynamicState = &dyn;
|
||||||
|
gpi.layout = layout;
|
||||||
|
gpi.renderPass = m_renderPass;
|
||||||
|
gpi.subpass = 0;
|
||||||
|
|
||||||
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||||
|
VK_VERIFY(vkCreateGraphicsPipelines(m_ctx.GetDevice(), VK_NULL_HANDLE, 1, &gpi, nullptr, &pipeline),
|
||||||
|
"vkCreateGraphicsPipelines");
|
||||||
|
|
||||||
|
vkDestroyShaderModule(m_ctx.GetDevice(), vs, nullptr);
|
||||||
|
vkDestroyShaderModule(m_ctx.GetDevice(), fs, nullptr);
|
||||||
|
|
||||||
|
m_pipelines[name] = {pipeline, layout};
|
||||||
|
return pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanRenderer::RegisterRenderCallback(const String& name, RenderCallback callback) {
|
||||||
|
m_callbacks[name] = Move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanRenderer::RecordAndSubmit(Uint32 imageIndex) {
|
||||||
|
auto& frame = *m_frames[m_currentFrame];
|
||||||
|
VK_VERIFY(vkWaitForFences(m_ctx.GetDevice(), 1, &frame.InFlightFence, VK_TRUE, UINT64_MAX),
|
||||||
|
"vkWaitForFences");
|
||||||
|
VK_VERIFY(vkResetFences(m_ctx.GetDevice(), 1, &frame.InFlightFence), "vkResetFences");
|
||||||
|
VK_VERIFY(vkResetCommandBuffer(frame.CommandBuffer, 0), "vkResetCommandBuffer");
|
||||||
|
|
||||||
VkCommandBufferBeginInfo bi{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
|
VkCommandBufferBeginInfo bi{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
|
||||||
VK_VERIFY(vkBeginCommandBuffer(frame.CommandBuffer, &bi), "vkBeginCommandBuffer");
|
VK_VERIFY(vkBeginCommandBuffer(frame.CommandBuffer, &bi), "vkBeginCommandBuffer");
|
||||||
|
|
||||||
VkClearValue clear{};
|
VkClearValue clear{};
|
||||||
clear.color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
clear.color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||||
|
|
||||||
VkRenderPassBeginInfo rpbi{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
|
VkRenderPassBeginInfo rpbi{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
|
||||||
rpbi.renderPass = RenderPass;
|
rpbi.renderPass = m_renderPass;
|
||||||
rpbi.framebuffer = Swapchain->GetFramebuffers()[imageIndex];
|
rpbi.framebuffer = m_swapchain->GetFramebuffers()[imageIndex];
|
||||||
rpbi.renderArea.offset = {0, 0};
|
rpbi.renderArea.offset = {0, 0};
|
||||||
rpbi.renderArea.extent = Swapchain->GetExtent();
|
rpbi.renderArea.extent = m_swapchain->GetExtent();
|
||||||
rpbi.clearValueCount = 1;
|
rpbi.clearValueCount = 1;
|
||||||
rpbi.pClearValues = &clear;
|
rpbi.pClearValues = &clear;
|
||||||
|
|
||||||
vkCmdBeginRenderPass(frame.CommandBuffer, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
|
vkCmdBeginRenderPass(frame.CommandBuffer, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
|
|
||||||
for (auto& kv : RenderCallbacks) {
|
VkViewport vp{};
|
||||||
if (kv.second) {
|
vp.x = 0.0f;
|
||||||
kv.second(frame.CommandBuffer, imageIndex, Swapchain->GetExtent());
|
vp.y = 0.0f;
|
||||||
}
|
vp.width = static_cast<float>(m_swapchain->GetExtent().width);
|
||||||
|
vp.height = static_cast<float>(m_swapchain->GetExtent().height);
|
||||||
|
vp.minDepth = 0.0f;
|
||||||
|
vp.maxDepth = 1.0f;
|
||||||
|
vkCmdSetViewport(frame.CommandBuffer, 0, 1, &vp);
|
||||||
|
|
||||||
|
VkRect2D scissor{{0, 0}, m_swapchain->GetExtent()};
|
||||||
|
vkCmdSetScissor(frame.CommandBuffer, 0, 1, &scissor);
|
||||||
|
|
||||||
|
for (auto& [_, cb] : m_callbacks) {
|
||||||
|
cb(frame.CommandBuffer, imageIndex, m_swapchain->GetExtent());
|
||||||
}
|
}
|
||||||
|
|
||||||
vkCmdEndRenderPass(frame.CommandBuffer);
|
vkCmdEndRenderPass(frame.CommandBuffer);
|
||||||
VK_VERIFY(vkEndCommandBuffer(frame.CommandBuffer), "vkEndCommandBuffer");
|
VK_VERIFY(vkEndCommandBuffer(frame.CommandBuffer), "vkEndCommandBuffer");
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanRenderer::RecreateSwapchainIfNeeded() {
|
|
||||||
vkDeviceWaitIdle(Ctx->GetDevice());
|
|
||||||
DestroyFrameResources();
|
|
||||||
DestroyRenderPass();
|
|
||||||
Swapchain->Recreate();
|
|
||||||
CreateRenderPass();
|
|
||||||
CreateFrameResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait fence & Acquire image & Record commands & Submit
|
|
||||||
void VulkanRenderer::RenderFrame() {
|
|
||||||
if (!Ctx) throw RuntimeError("Renderer not initialized");
|
|
||||||
FrameContext& frame = *Frames[CurrentFrame];
|
|
||||||
|
|
||||||
// Wait fence and reset
|
|
||||||
VK_VERIFY(vkWaitForFences(Ctx->GetDevice(), 1, &frame.InFlightFence, VK_TRUE, UINT64_MAX), "vkWaitForFences");
|
|
||||||
VK_VERIFY(vkResetFences(Ctx->GetDevice(), 1, &frame.InFlightFence), "vkResetFences");
|
|
||||||
|
|
||||||
// Record commands
|
|
||||||
VK_VERIFY(vkResetCommandBuffer(frame.CommandBuffer, 0), "vkResetCommandBuffer");
|
|
||||||
RecordFrameCommandBuffer(frame, frame.CurrentImageIndex);
|
|
||||||
|
|
||||||
// Submit
|
|
||||||
VkSubmitInfo si{VK_STRUCTURE_TYPE_SUBMIT_INFO};
|
|
||||||
VkSemaphore waitSemaphores[] = {frame.ImageAvailable};
|
|
||||||
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
||||||
|
VkSemaphore waitSemaphores[] = {frame.ImageAvailable};
|
||||||
|
VkSemaphore signalSemaphores[] = {frame.RenderFinished};
|
||||||
|
|
||||||
|
VkSubmitInfo si{VK_STRUCTURE_TYPE_SUBMIT_INFO};
|
||||||
si.waitSemaphoreCount = 1;
|
si.waitSemaphoreCount = 1;
|
||||||
si.pWaitSemaphores = waitSemaphores;
|
si.pWaitSemaphores = waitSemaphores;
|
||||||
si.pWaitDstStageMask = waitStages;
|
si.pWaitDstStageMask = waitStages;
|
||||||
si.commandBufferCount = 1;
|
si.commandBufferCount = 1;
|
||||||
si.pCommandBuffers = &frame.CommandBuffer;
|
si.pCommandBuffers = &frame.CommandBuffer;
|
||||||
VkSemaphore signalSemaphores[] = {frame.RenderFinished};
|
|
||||||
si.signalSemaphoreCount = 1;
|
si.signalSemaphoreCount = 1;
|
||||||
si.pSignalSemaphores = signalSemaphores;
|
si.pSignalSemaphores = signalSemaphores;
|
||||||
|
|
||||||
VK_VERIFY(vkQueueSubmit(Ctx->GetGraphicsQueue(), 1, &si, frame.InFlightFence), "vkQueueSubmit");
|
VK_VERIFY(vkQueueSubmit(m_ctx.GetGraphicsQueue(), 1, &si, frame.InFlightFence), "vkQueueSubmit");
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::FrameBegin() {
|
void VulkanRenderer::DrawAndPresent() {
|
||||||
FrameContext& frame = *Frames[CurrentFrame];
|
EnsureInitialized();
|
||||||
|
auto& frame = *m_frames[m_currentFrame];
|
||||||
|
auto& imagesInFlight = m_swapchain->GetImagesInFlight();
|
||||||
|
|
||||||
// Acquire image
|
|
||||||
auto& imagesInFlight = Swapchain->GetImagesInFlight();
|
|
||||||
Uint32 imageIndex = 0;
|
Uint32 imageIndex = 0;
|
||||||
VkResult res = vkAcquireNextImageKHR(Ctx->GetDevice(), Swapchain->GetSwapchain(), UINT64_MAX,
|
VkResult res = vkAcquireNextImageKHR(m_ctx.GetDevice(), m_swapchain->GetSwapchain(), UINT64_MAX,
|
||||||
frame.ImageAvailable, VK_NULL_HANDLE, &imageIndex);
|
frame.ImageAvailable, VK_NULL_HANDLE, &imageIndex);
|
||||||
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
|
|
||||||
vkWaitForFences(Ctx->GetDevice(), 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
|
|
||||||
}
|
|
||||||
imagesInFlight[imageIndex] = frame.InFlightFence;
|
|
||||||
frame.CurrentImageIndex = imageIndex;
|
|
||||||
if (res == VK_ERROR_OUT_OF_DATE_KHR) {
|
if (res == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||||
MGLOG_D("vkAcquireNextImageKHR: OUT_OF_DATE -> recreate");
|
RecreateSwapchain();
|
||||||
RecreateSwapchainIfNeeded();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
VK_VERIFY(res, "vkAcquireNextImageKHR");
|
VK_VERIFY(res, "vkAcquireNextImageKHR");
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanRenderer::Present() {
|
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
|
||||||
if (!Ctx) throw RuntimeError("Renderer not initialized");
|
vkWaitForFences(m_ctx.GetDevice(), 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
|
||||||
FrameContext& frame = *Frames[CurrentFrame];
|
}
|
||||||
|
imagesInFlight[imageIndex] = frame.InFlightFence;
|
||||||
|
|
||||||
|
RecordAndSubmit(imageIndex);
|
||||||
|
|
||||||
// Present
|
|
||||||
VkPresentInfoKHR pi{VK_STRUCTURE_TYPE_PRESENT_INFO_KHR};
|
VkPresentInfoKHR pi{VK_STRUCTURE_TYPE_PRESENT_INFO_KHR};
|
||||||
VkSemaphore signalSemaphores[] = {frame.RenderFinished};
|
VkSemaphore waitSemaphores[] = {frame.RenderFinished};
|
||||||
pi.waitSemaphoreCount = 1;
|
pi.waitSemaphoreCount = 1;
|
||||||
pi.pWaitSemaphores = signalSemaphores;
|
pi.pWaitSemaphores = waitSemaphores;
|
||||||
VkSwapchainKHR scs[] = {Swapchain->GetSwapchain()};
|
VkSwapchainKHR swapchains[] = {m_swapchain->GetSwapchain()};
|
||||||
pi.swapchainCount = 1;
|
pi.swapchainCount = 1;
|
||||||
pi.pSwapchains = scs;
|
pi.pSwapchains = swapchains;
|
||||||
pi.pImageIndices = &frame.CurrentImageIndex;
|
pi.pImageIndices = &imageIndex;
|
||||||
VkResult pres = vkQueuePresentKHR(Ctx->GetGraphicsQueue(), &pi);
|
|
||||||
|
VkResult pres = vkQueuePresentKHR(m_ctx.GetGraphicsQueue(), &pi);
|
||||||
if (pres == VK_ERROR_OUT_OF_DATE_KHR || pres == VK_SUBOPTIMAL_KHR) {
|
if (pres == VK_ERROR_OUT_OF_DATE_KHR || pres == VK_SUBOPTIMAL_KHR) {
|
||||||
MGLOG_D("vkQueuePresentKHR: out_of_date/suboptimal -> recreate");
|
RecreateSwapchain();
|
||||||
RecreateSwapchainIfNeeded();
|
|
||||||
} else {
|
} else {
|
||||||
VK_VERIFY(pres, "vkQueuePresentKHR");
|
VK_VERIFY(pres, "vkQueuePresentKHR");
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentFrame = (CurrentFrame + 1) % Frames.size();
|
m_currentFrame = (m_currentFrame + 1) % m_frames.size();
|
||||||
|
|
||||||
FrameBegin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::RegisterRenderCallback(const std::string& name, RenderCallback cb) {
|
void VulkanRenderer::RecreateSwapchain() {
|
||||||
auto it = std::find_if(RenderCallbacks.begin(), RenderCallbacks.end(),
|
vkDeviceWaitIdle(m_ctx.GetDevice());
|
||||||
[&](const auto& kv) { return kv.first == name; });
|
DestroyFrameResources();
|
||||||
if (it != RenderCallbacks.end()) {
|
DestroyRenderPass();
|
||||||
MGLOG_W("Render callback '%s' already registered", name.c_str());
|
|
||||||
return;
|
m_swapchain->Recreate();
|
||||||
}
|
CreateRenderPass();
|
||||||
RenderCallbacks.emplace_back(name, std::move(cb));
|
CreateFramebuffers();
|
||||||
|
CreateFrameResources();
|
||||||
|
|
||||||
|
DestroyPipelines();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::UnregisterRenderCallback(const std::string& name) {
|
void VulkanRenderer::RenderFrame() { DrawAndPresent(); }
|
||||||
RenderCallbacks.erase(std::remove_if(RenderCallbacks.begin(), RenderCallbacks.end(),
|
|
||||||
[&](const auto& kv) { return kv.first == name; }),
|
|
||||||
RenderCallbacks.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPipeline VulkanRenderer::CreateGraphicsPipelineFromSpv(const std::string& key, const std::vector<uint32_t>& vsSpv,
|
void VulkanRenderer::Present() { DrawAndPresent(); }
|
||||||
const std::vector<uint32_t>& fsSpv) {
|
|
||||||
return PipelineMgr->CreateGraphicsPipelineFromSpv(key, vsSpv, fsSpv, RenderPass, Swapchain->GetExtent());
|
|
||||||
}
|
|
||||||
|
|
||||||
VkExtent2D VulkanRenderer::GetExtent() const {
|
|
||||||
return Swapchain ? Swapchain->GetExtent() : VkExtent2D{0, 0};
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanRenderer::WaitIdle() {
|
|
||||||
if (Ctx && Ctx->GetDevice() != VK_NULL_HANDLE) vkDeviceWaitIdle(Ctx->GetDevice());
|
|
||||||
}
|
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
|
|||||||
@@ -8,67 +8,58 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <Includes.h>
|
#include <Includes.h>
|
||||||
|
#include "VulkanContext.h"
|
||||||
|
#include "SwapchainManager.h"
|
||||||
|
#include "FrameContext.h"
|
||||||
|
#include "VkCommon.h"
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
class VulkanContext;
|
|
||||||
class SwapchainManager;
|
|
||||||
class PipelineManager;
|
|
||||||
struct FrameContext;
|
|
||||||
|
|
||||||
using RenderCallback = std::function<void(VkCommandBuffer cmdBuf, uint32_t imageIndex, VkExtent2D extent)>;
|
|
||||||
|
|
||||||
struct RendererConfig {
|
|
||||||
Uint32 MaxFramesInFlight = 2;
|
|
||||||
String AppName = "MobileGL-VulkanRenderer";
|
|
||||||
};
|
|
||||||
|
|
||||||
class VulkanRenderer {
|
class VulkanRenderer {
|
||||||
public:
|
public:
|
||||||
VulkanRenderer(NativeWindowType window, const RendererConfig& cfg = {});
|
using RenderCallback = std::function<void(VkCommandBuffer, Uint32, VkExtent2D)>;
|
||||||
|
|
||||||
|
explicit VulkanRenderer(ANativeWindow* window);
|
||||||
~VulkanRenderer();
|
~VulkanRenderer();
|
||||||
|
|
||||||
void Initialize();
|
VulkanRenderer(const VulkanRenderer&) = delete;
|
||||||
void Shutdown();
|
VulkanRenderer& operator=(const VulkanRenderer&) = delete;
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
void RenderFrame();
|
void RenderFrame();
|
||||||
void Present();
|
void Present();
|
||||||
|
|
||||||
void RegisterRenderCallback(const std::string& name, RenderCallback cb);
|
VkPipeline CreateGraphicsPipelineFromSpv(const String& name, const Vector<Uint>& vertexSpv,
|
||||||
void UnregisterRenderCallback(const std::string& name);
|
const Vector<Uint>& fragmentSpv);
|
||||||
|
void RegisterRenderCallback(const String& name, RenderCallback callback);
|
||||||
VkPipeline CreateGraphicsPipelineFromSpv(const std::string& key, const std::vector<uint32_t>& vsSpv,
|
|
||||||
const std::vector<uint32_t>& fsSpv);
|
|
||||||
|
|
||||||
VkExtent2D GetExtent() const;
|
|
||||||
|
|
||||||
void WaitIdle();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NativeWindowType Window = 0;
|
void EnsureInitialized();
|
||||||
RendererConfig Config;
|
void CreateCommandPool();
|
||||||
|
|
||||||
std::unique_ptr<VulkanContext> Ctx;
|
|
||||||
std::unique_ptr<SwapchainManager> Swapchain;
|
|
||||||
std::unique_ptr<PipelineManager> PipelineMgr;
|
|
||||||
|
|
||||||
VkRenderPass RenderPass = VK_NULL_HANDLE;
|
|
||||||
VkCommandPool CommandPool = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<FrameContext>> Frames;
|
|
||||||
Uint32 CurrentFrame = 0;
|
|
||||||
|
|
||||||
// Render callbacks map
|
|
||||||
std::vector<std::pair<std::string, RenderCallback>> RenderCallbacks;
|
|
||||||
|
|
||||||
// Internals
|
|
||||||
void CreateRenderPass();
|
void CreateRenderPass();
|
||||||
void DestroyRenderPass();
|
void DestroyRenderPass();
|
||||||
void CreateCommandPool();
|
void CreateFramebuffers();
|
||||||
void DestroyCommandPool();
|
|
||||||
void CreateFrameResources();
|
void CreateFrameResources();
|
||||||
void DestroyFrameResources();
|
void DestroyFrameResources();
|
||||||
void RecordFrameCommandBuffer(FrameContext& frame, uint32_t imageIndex);
|
void RecreateSwapchain();
|
||||||
void RecreateSwapchainIfNeeded();
|
void RecordAndSubmit(Uint32 imageIndex);
|
||||||
void FrameBegin();
|
void DrawAndPresent();
|
||||||
|
void DestroyPipelines();
|
||||||
|
|
||||||
|
struct PipelineInfo {
|
||||||
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||||
|
VkPipelineLayout layout = VK_NULL_HANDLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
ANativeWindow* m_window = nullptr;
|
||||||
|
VkManager::VulkanContext m_ctx;
|
||||||
|
UniquePtr<VkManager::SwapchainManager> m_swapchain;
|
||||||
|
VkRenderPass m_renderPass = VK_NULL_HANDLE;
|
||||||
|
VkCommandPool m_commandPool = VK_NULL_HANDLE;
|
||||||
|
Vector<UniquePtr<VkManager::FrameContext>> m_frames;
|
||||||
|
Uint32 m_currentFrame = 0;
|
||||||
|
Bool m_initialized = false;
|
||||||
|
|
||||||
|
UnorderedMap<String, RenderCallback> m_callbacks;
|
||||||
|
UnorderedMap<String, PipelineInfo> m_pipelines;
|
||||||
};
|
};
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkan/TmpImpl.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::Backend::DirectVulkan::TmpImpl {
|
||||||
|
void Present();
|
||||||
|
void FrameBegin();
|
||||||
|
void InitVulkan(ANativeWindow* window);
|
||||||
|
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
||||||
|
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
||||||
|
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value);
|
||||||
|
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
|
||||||
|
void Clear(GLbitfield mask);
|
||||||
|
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
||||||
|
void DrawArrays(GLenum mode, GLint first, GLsizei count);
|
||||||
|
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex);
|
||||||
|
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
|
GLsizei drawcount);
|
||||||
|
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
|
GLsizei drawcount, const GLint* basevertex);
|
||||||
|
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride);
|
||||||
|
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride);
|
||||||
|
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
|
||||||
|
const void* indices, GLint basevertex);
|
||||||
|
void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices);
|
||||||
|
void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
|
GLsizei instancecount, GLint basevertex, GLuint baseinstance);
|
||||||
|
void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
|
GLsizei instancecount, GLint basevertex);
|
||||||
|
void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
|
GLsizei instancecount, GLuint baseinstance);
|
||||||
|
void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount);
|
||||||
|
void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect);
|
||||||
|
void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
|
||||||
|
GLuint baseinstance);
|
||||||
|
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
|
||||||
|
void DrawArraysIndirect(GLenum mode, const void* indirect);
|
||||||
|
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
|
||||||
|
GLint dstY1, GLbitfield mask, GLenum filter);
|
||||||
|
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
|
||||||
|
GLsizei height, GLint border);
|
||||||
|
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
|
||||||
|
GLsizei height);
|
||||||
|
void GenerateMipmap(GLenum target);
|
||||||
|
void Present();
|
||||||
|
} // namespace MobileGL::Backend::DirectVulkan::TmpImpl
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "EGLForVulkan.h"
|
#include "EGLForVulkan.h"
|
||||||
#include "MG_Backend/DirectVulkan/DirectVulkan.h"
|
#include "MG_Backend/DirectVulkan/DirectVulkan.h"
|
||||||
|
#include "MG_Backend/DirectVulkan/TmpImpl.h"
|
||||||
#include <Config.h>
|
#include <Config.h>
|
||||||
#include <MG_State/GLState/ProgramState/ProgramObject.h>
|
#include <MG_State/GLState/ProgramState/ProgramObject.h>
|
||||||
|
|
||||||
@@ -88,6 +89,8 @@ namespace MobileGL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CreateWindowSurfaceForVulkan(NativeWindowType window) {
|
void CreateWindowSurfaceForVulkan(NativeWindowType window) {
|
||||||
|
MobileGL::Backend::DirectVulkan::TmpImpl::InitVulkan(window);
|
||||||
|
return;
|
||||||
MG_Backend::DirectVulkan::pVulkanRenderer = MakeUnique<MG_Backend::DirectVulkan::VulkanRenderer>(window);
|
MG_Backend::DirectVulkan::pVulkanRenderer = MakeUnique<MG_Backend::DirectVulkan::VulkanRenderer>(window);
|
||||||
MG_Backend::DirectVulkan::pVulkanRenderer->Initialize();
|
MG_Backend::DirectVulkan::pVulkanRenderer->Initialize();
|
||||||
|
|
||||||
@@ -102,6 +105,8 @@ namespace MobileGL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EGLBoolean SwapBuffers(EGLDisplay dpy, EGLSurface draw) {
|
EGLBoolean SwapBuffers(EGLDisplay dpy, EGLSurface draw) {
|
||||||
|
MobileGL::Backend::DirectVulkan::TmpImpl::Present();
|
||||||
|
return EGL_TRUE;
|
||||||
if (!MG_Backend::DirectVulkan::pVulkanRenderer) {
|
if (!MG_Backend::DirectVulkan::pVulkanRenderer) {
|
||||||
MGLOG_E("EGLForVulkan::SwapBuffers called but VulkanRenderer is null");
|
MGLOG_E("EGLForVulkan::SwapBuffers called but VulkanRenderer is null");
|
||||||
return EGL_FALSE;
|
return EGL_FALSE;
|
||||||
|
|||||||
@@ -113,7 +113,8 @@ namespace MobileGL {
|
|||||||
Vector<Vector<unsigned>>& GetGeneratedSpirv() { return m_generatedSpirv; }
|
Vector<Vector<unsigned>>& GetGeneratedSpirv() { return m_generatedSpirv; }
|
||||||
|
|
||||||
Int GetShaderIndexByStage(ShaderStage stage) const {
|
Int GetShaderIndexByStage(ShaderStage stage) const {
|
||||||
auto it = std::find_if(m_shaders.begin(), m_shaders.end(),
|
auto it = std::find_if(
|
||||||
|
m_shaders.begin(), m_shaders.end(),
|
||||||
[stage](const SharedPtr<ShaderObject>& shader) { return shader->GetShaderStage() == stage; });
|
[stage](const SharedPtr<ShaderObject>& shader) { return shader->GetShaderStage() == stage; });
|
||||||
return it == m_shaders.end() ? -1 : std::distance(m_shaders.begin(), it);
|
return it == m_shaders.end() ? -1 : std::distance(m_shaders.begin(), it);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user