[Feat] (MG_Backend/DirectVulkan): get rid of stuff that no longer needed

This commit is contained in:
2026-02-13 15:24:18 +08:00
parent 0822cb871d
commit beb9301282
11 changed files with 3 additions and 639 deletions
+3 -7
View File
@@ -208,14 +208,10 @@ set(SOURCE_FILES
MobileGL/MG_Backend/DirectGLES/Managers.cpp
MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanContext.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainManager.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineManager.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp
MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp
MobileGL/MG_Backend/DirectVulkan/Managers/ProgramManager.cpp
MobileGL/MG_Backend/DirectVulkan/Managers/VertexInputStateManager.cpp
MobileGL/MG_Backend/DirectVulkan/Managers/RenderStateManager.cpp
# MobileGL/MG_Backend/DirectVulkan/Managers/ProgramManager.cpp
# MobileGL/MG_Backend/DirectVulkan/Managers/VertexInputStateManager.cpp
# MobileGL/MG_Backend/DirectVulkan/Managers/RenderStateManager.cpp
MobileGL/MG_State/GLState/Core.cpp
MobileGL/MG_State/GLState/ErrorState/Error.cpp
@@ -7,7 +7,6 @@
// End of Source File Header
#include "ProgramManager.h"
#include "MG_Backend/DirectVulkan/Renderer/VulkanContext.h"
namespace MobileGL::MG_Backend::DirectVulkan {
ProgramManager::ProgramManager(VulkanContext &ctx): m_ctx(ctx) {}
@@ -1,52 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#include "FrameContext.h"
#include "VulkanContext.h"
namespace MobileGL::MG_Backend::DirectVulkan {
FrameContext::~FrameContext() = default;
void FrameContext::Initialize(VulkanContext& ctx, VkCommandPool pool) {
// allocate cmd
VkCommandBufferAllocateInfo abci{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
abci.commandPool = pool;
abci.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
abci.commandBufferCount = 1;
VK_VERIFY(vkAllocateCommandBuffers(ctx.GetDevice(), &abci, &CommandBuffer), "vkAllocateCommandBuffers");
// semaphores
VkSemaphoreCreateInfo sci{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
VK_VERIFY(vkCreateSemaphore(ctx.GetDevice(), &sci, nullptr, &ImageAvailable),
"vkCreateSemaphore ImageAvailable");
VK_VERIFY(vkCreateSemaphore(ctx.GetDevice(), &sci, nullptr, &RenderFinished),
"vkCreateSemaphore RenderFinished");
// fence (start signaled)
VkFenceCreateInfo fci{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
fci.flags = VK_FENCE_CREATE_SIGNALED_BIT;
VK_VERIFY(vkCreateFence(ctx.GetDevice(), &fci, nullptr, &InFlightFence), "vkCreateFence");
}
void FrameContext::Cleanup(VulkanContext& ctx) {
if (ImageAvailable != VK_NULL_HANDLE) {
vkDestroySemaphore(ctx.GetDevice(), ImageAvailable, nullptr);
ImageAvailable = VK_NULL_HANDLE;
}
if (RenderFinished != VK_NULL_HANDLE) {
vkDestroySemaphore(ctx.GetDevice(), RenderFinished, nullptr);
RenderFinished = VK_NULL_HANDLE;
}
if (InFlightFence != VK_NULL_HANDLE) {
vkDestroyFence(ctx.GetDevice(), InFlightFence, nullptr);
InFlightFence = VK_NULL_HANDLE;
}
CommandBuffer = VK_NULL_HANDLE;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,28 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.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;
struct FrameContext {
FrameContext() = default;
~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
@@ -1,141 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineManager.cpp
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#include "PipelineManager.h"
#include "VulkanContext.h"
namespace MobileGL::MG_Backend::DirectVulkan {
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
@@ -1,133 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainManager.cpp
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#include "SwapchainManager.h"
#include "VulkanContext.h"
namespace MobileGL::MG_Backend::DirectVulkan {
SwapchainManager::SwapchainManager(VulkanContext& ctx) : Ctx(ctx) {}
SwapchainManager::~SwapchainManager() {
Cleanup();
}
void SwapchainManager::Initialize() {
CreateSwapchainInternal();
CreateImageViews();
ImagesInFlight.resize(Images.size(), VK_NULL_HANDLE);
}
void SwapchainManager::Recreate() {
DestroyImageViews();
if (Swapchain != VK_NULL_HANDLE) {
vkDestroySwapchainKHR(Ctx.GetDevice(), Swapchain, nullptr);
Swapchain = VK_NULL_HANDLE;
}
CreateSwapchainInternal();
CreateImageViews();
ImagesInFlight.resize(Images.size(), VK_NULL_HANDLE);
Framebuffers.clear();
MGLOG_D("Swapchain recreated");
}
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;
}
}
void SwapchainManager::SetFramebuffers(std::vector<VkFramebuffer>&& fbs) {
Framebuffers = std::move(fbs);
}
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;
}
}
return presentMode;
}
void SwapchainManager::CreateSwapchainInternal() {
VkSurfaceCapabilitiesKHR caps;
VK_VERIFY(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(Ctx.GetPhysicalDevice(), Ctx.GetSurface(), &caps),
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
Extent = caps.currentExtent;
ImageFormat = VK_FORMAT_R8G8B8A8_UNORM;
VkSwapchainCreateInfoKHR sci{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};
sci.surface = Ctx.GetSurface();
sci.minImageCount = std::max<uint32_t>(2, caps.minImageCount);
sci.imageFormat = ImageFormat;
sci.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
sci.imageExtent = Extent;
sci.imageArrayLayers = 1;
sci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
sci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
sci.preTransform = caps.currentTransform;
sci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
sci.presentMode = QueryPossiblePresentMode();
VK_VERIFY(vkCreateSwapchainKHR(Ctx.GetDevice(), &sci, nullptr, &Swapchain), "vkCreateSwapchainKHR");
uint32_t count = 0;
VK_VERIFY(vkGetSwapchainImagesKHR(Ctx.GetDevice(), Swapchain, &count, nullptr),
"vkGetSwapchainImagesKHR count");
Images.resize(count);
VK_VERIFY(vkGetSwapchainImagesKHR(Ctx.GetDevice(), Swapchain, &count, Images.data()),
"vkGetSwapchainImagesKHR images");
MGLOG_D("Swapchain created (%u images)", count);
}
void SwapchainManager::CreateImageViews() {
DestroyImageViews();
ImageViews.resize(Images.size());
for (size_t i = 0; i < Images.size(); ++i) {
VkImageViewCreateInfo ivci{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
ivci.image = Images[i];
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
ivci.format = ImageFormat;
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.baseMipLevel = 0;
ivci.subresourceRange.levelCount = 1;
ivci.subresourceRange.baseArrayLayer = 0;
ivci.subresourceRange.layerCount = 1;
VK_VERIFY(vkCreateImageView(Ctx.GetDevice(), &ivci, nullptr, &ImageViews[i]), "vkCreateImageView");
}
MGLOG_D("ImageViews created");
}
void SwapchainManager::DestroyImageViews() {
for (auto iv : ImageViews)
if (iv != VK_NULL_HANDLE) vkDestroyImageView(Ctx.GetDevice(), iv, nullptr);
ImageViews.clear();
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,49 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainManager.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 SwapchainManager {
public:
SwapchainManager(VulkanContext& ctx);
~SwapchainManager();
void Initialize();
void Recreate();
void Cleanup();
VkSwapchainKHR GetSwapchain() const { return Swapchain; }
VkFormat GetFormat() const { return ImageFormat; }
VkExtent2D GetExtent() const { return Extent; }
const std::vector<VkImageView>& GetImageViews() const { return ImageViews; }
const std::vector<VkFramebuffer>& GetFramebuffers() const { return Framebuffers; }
const std::vector<VkImage>& GetImages() const { return Images; }
std::vector<VkFence>& GetImagesInFlight() { return ImagesInFlight; }
void SetFramebuffers(std::vector<VkFramebuffer>&& fbs);
private:
VulkanContext& Ctx;
VkSwapchainKHR Swapchain = VK_NULL_HANDLE;
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();
void CreateImageViews();
void DestroyImageViews();
VkPresentModeKHR QueryPossiblePresentMode();
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,141 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanContext.cpp
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#include "VulkanContext.h"
#include "MG_Util/Types.h"
namespace MobileGL::MG_Backend::DirectVulkan {
VulkanContext::~VulkanContext() {
Shutdown();
}
void VulkanContext::Initialize(NativeWindowType window, const std::string& appName) {
if (Initialized) return;
CreateInstance(appName);
CreateSurface(window);
PickPhysicalDevice();
CreateLogicalDevice();
Initialized = true;
MGLOG_D("VulkanContext initialized");
}
void VulkanContext::Shutdown() {
if (!Initialized && Instance == VK_NULL_HANDLE) return;
if (Device != VK_NULL_HANDLE) {
vkDeviceWaitIdle(Device);
vkDestroyDevice(Device, nullptr);
Device = VK_NULL_HANDLE;
}
if (Surface != VK_NULL_HANDLE) {
vkDestroySurfaceKHR(Instance, Surface, nullptr);
Surface = VK_NULL_HANDLE;
}
if (Instance != VK_NULL_HANDLE) {
vkDestroyInstance(Instance, nullptr);
Instance = VK_NULL_HANDLE;
}
Initialized = false;
MGLOG_D("VulkanContext shutdown");
}
void VulkanContext::CreateInstance(const std::string& appName) {
VkApplicationInfo appInfo{VK_STRUCTURE_TYPE_APPLICATION_INFO};
appInfo.pApplicationName = appName.c_str();
#ifdef VK_USE_PLATFORM_WIN32_KHR
appInfo.apiVersion = VK_API_VERSION_1_3;
#else
appInfo.apiVersion = VK_API_VERSION_1_1;
#endif
const char* exts[] = {VK_KHR_SURFACE_EXTENSION_NAME,
#ifdef VK_USE_PLATFORM_ANDROID_KHR
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME
#elif defined VK_USE_PLATFORM_WIN32_KHR
VK_KHR_WIN32_SURFACE_EXTENSION_NAME
#else
#warning "VulkanContext::CreateInstance: VK_KHR_*_surface extension not defined on this platform"
#endif
}; // TODO: support more platforms
VkInstanceCreateInfo ci{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
ci.pApplicationInfo = &appInfo;
ci.enabledExtensionCount = 2;
ci.ppEnabledExtensionNames = exts;
VK_VERIFY(vkCreateInstance(&ci, nullptr, &Instance), "vkCreateInstance failed");
}
void VulkanContext::CreateSurface(NativeWindowType window) {
if (!Instance) throw RuntimeError("Instance not created");
#if defined 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};
sci.window = nativeWindow;
VK_VERIFY(vkCreateAndroidSurfaceKHR(Instance, &sci, nullptr, &Surface), "vkCreateAndroidSurfaceKHR failed");
#elif defined VK_USE_PLATFORM_WIN32_KHR
auto hwnd = static_cast<HWND>(window);
MOBILEGL_ASSERT(hwnd, "HWND is null");
VkWin32SurfaceCreateInfoKHR sci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR};
sci.hwnd = hwnd;
VK_VERIFY(vkCreateWin32SurfaceKHR(Instance, &sci, nullptr, &Surface), "vkCreateWin32SurfaceKHR failed");
#else
//#warning "VulkanRenderer::Initialize called on a platform which is not supported yet"
MGLOG_W("VulkanRenderer::Initialize called on a platform which is not supported yet"); // TODO: support more
// platforms
#endif
}
void VulkanContext::PickPhysicalDevice() {
uint32_t count = 0;
VK_VERIFY(vkEnumeratePhysicalDevices(Instance, &count, nullptr), "vkEnumeratePhysicalDevices count");
if (count == 0) throw RuntimeError("No physical devices");
std::vector<VkPhysicalDevice> devs(count);
VK_VERIFY(vkEnumeratePhysicalDevices(Instance, &count, devs.data()), "vkEnumeratePhysicalDevices");
for (auto d : devs) {
uint32_t qcount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(d, &qcount, nullptr);
std::vector<VkQueueFamilyProperties> qprops(qcount);
vkGetPhysicalDeviceQueueFamilyProperties(d, &qcount, qprops.data());
for (uint32_t i = 0; i < qcount; ++i) {
VkBool32 present = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(d, i, Surface, &present);
if ((qprops[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && present) {
PhysicalDevice = d;
GraphicsQueueFamily = i;
MGLOG_D("Picked physical device, queue family %u", i);
return;
}
}
}
throw RuntimeError("No suitable physical device");
}
void VulkanContext::CreateLogicalDevice() {
float prio = 1.0f;
VkDeviceQueueCreateInfo dqci{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO};
dqci.queueFamilyIndex = GraphicsQueueFamily;
dqci.queueCount = 1;
dqci.pQueuePriorities = &prio;
const char* devExts[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
VkDeviceCreateInfo dci{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
dci.queueCreateInfoCount = 1;
dci.pQueueCreateInfos = &dqci;
dci.enabledExtensionCount = 1;
dci.ppEnabledExtensionNames = devExts;
VK_VERIFY(vkCreateDevice(PhysicalDevice, &dci, nullptr, &Device), "vkCreateDevice failed");
vkGetDeviceQueue(Device, GraphicsQueueFamily, 0, &GraphicsQueue);
MGLOG_D("Logical device created");
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -1,48 +0,0 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanContext.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>
#define VK_VERIFY(expr, ...) \
do { \
VkResult _vk_verify_result = (expr); \
MOBILEGL_ASSERT(_vk_verify_result == VK_SUCCESS, "Vulkan error %d at %s:%d" __VA_OPT__(" - ") __VA_ARGS__, _vk_verify_result, __FILE__, __LINE__); \
} while (0)
namespace MobileGL::MG_Backend::DirectVulkan {
class VulkanContext {
public:
VulkanContext() = default;
~VulkanContext();
void Initialize(NativeWindowType window, const std::string& appName = "MobileGL-VulkanRenderer");
void Shutdown();
VkInstance GetInstance() const { return Instance; }
VkPhysicalDevice GetPhysicalDevice() const { return PhysicalDevice; }
VkDevice GetDevice() const { return Device; }
VkQueue GetGraphicsQueue() const { return GraphicsQueue; }
uint32_t GetGraphicsQueueFamily() const { return GraphicsQueueFamily; }
VkSurfaceKHR GetSurface() const { return Surface; }
private:
void CreateInstance(const std::string& appName);
void CreateSurface(NativeWindowType window);
void PickPhysicalDevice();
void CreateLogicalDevice();
VkInstance Instance = VK_NULL_HANDLE;
VkPhysicalDevice PhysicalDevice = VK_NULL_HANDLE;
VkDevice Device = VK_NULL_HANDLE;
VkQueue GraphicsQueue = VK_NULL_HANDLE;
uint32_t GraphicsQueueFamily = UINT32_MAX;
VkSurfaceKHR Surface = VK_NULL_HANDLE;
bool Initialized = false;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -7,10 +7,6 @@
// End of Source File Header
#include "VulkanRenderer.h"
#include "VulkanContext.h"
#include "SwapchainManager.h"
#include "PipelineManager.h"
#include "FrameContext.h"
namespace MobileGL::MG_Backend::DirectVulkan {
VkBool32 VulkanRenderer::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,