mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
[Chore] (MG_Test/Backend/DirectVulkan): encapsulate SwapchainObject
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.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 "SwapchainObject.h"
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
SwapchainObject::SwapchainCapabilities SwapchainObject::GetSwapchainCapabilities(VkPhysicalDevice physicalDevice,
|
||||
VkSurfaceKHR surface) {
|
||||
SwapchainCapabilities swapchainCapabilities{};
|
||||
|
||||
VK_VERIFY(
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &swapchainCapabilities.capabilities));
|
||||
|
||||
Uint32 formatCount = 0;
|
||||
VK_VERIFY(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr));
|
||||
if (formatCount != 0) {
|
||||
swapchainCapabilities.surfaceFormats.resize(formatCount);
|
||||
VK_VERIFY(vkGetPhysicalDeviceSurfaceFormatsKHR(
|
||||
physicalDevice, surface, &formatCount, swapchainCapabilities.surfaceFormats.data()));
|
||||
}
|
||||
|
||||
Uint32 presentModeCount = 0;
|
||||
VK_VERIFY(
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, nullptr));
|
||||
if (presentModeCount != 0) {
|
||||
swapchainCapabilities.presentModes.resize(presentModeCount);
|
||||
VK_VERIFY(vkGetPhysicalDeviceSurfacePresentModesKHR(
|
||||
physicalDevice, surface, &presentModeCount, swapchainCapabilities.presentModes.data()));
|
||||
}
|
||||
|
||||
return swapchainCapabilities;
|
||||
}
|
||||
|
||||
VkSurfaceFormatKHR SwapchainObject::ChooseSwapchainSurfaceFormat(
|
||||
const Vector<VkSurfaceFormatKHR>& availableFormats) {
|
||||
for (const auto& availableFormat : availableFormats) {
|
||||
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB &&
|
||||
availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||
return availableFormat;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Properly rank other formats
|
||||
return availableFormats[0];
|
||||
}
|
||||
|
||||
VkPresentModeKHR SwapchainObject::ChooseSwapchainPresentMode(
|
||||
const Vector<VkPresentModeKHR>& availablePresentModes) {
|
||||
for (auto desiredPresentMode : s_desiredPresentModes) {
|
||||
for (const auto& presentMode : availablePresentModes) {
|
||||
if (presentMode == desiredPresentMode) {
|
||||
return presentMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Properly rank other modes
|
||||
return availablePresentModes[0];
|
||||
}
|
||||
|
||||
void SwapchainObject::Create(VkDevice device, const VkSwapchainCreateInfoKHR& createInfo) {
|
||||
m_surfaceFormat = {createInfo.imageFormat, createInfo.imageColorSpace};
|
||||
m_extent = createInfo.imageExtent;
|
||||
|
||||
VK_VERIFY(vkCreateSwapchainKHR(device, &createInfo, nullptr, &m_swapchain));
|
||||
|
||||
Uint32 imageCount = 0;
|
||||
VK_VERIFY(vkGetSwapchainImagesKHR(device, m_swapchain, &imageCount, nullptr));
|
||||
|
||||
m_images.resize(imageCount, VK_NULL_HANDLE);
|
||||
VK_VERIFY(vkGetSwapchainImagesKHR(device, m_swapchain, &imageCount, m_images.data()));
|
||||
m_imageLayouts.assign(imageCount, VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
|
||||
CreateImageViews(device);
|
||||
|
||||
MGLOG_I("Swapchain created, extent = %dx%d, swapchain imageCount = %d", m_extent.width, m_extent.height,
|
||||
imageCount);
|
||||
}
|
||||
|
||||
void SwapchainObject::Shutdown(VkDevice device) {
|
||||
for (auto imageView : m_imageViews) {
|
||||
vkDestroyImageView(device, imageView, nullptr);
|
||||
}
|
||||
m_imageViews.clear();
|
||||
|
||||
if (m_swapchain != VK_NULL_HANDLE) {
|
||||
vkDestroySwapchainKHR(device, m_swapchain, nullptr);
|
||||
m_swapchain = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
m_images.clear();
|
||||
m_imageLayouts.clear();
|
||||
}
|
||||
|
||||
VkImage SwapchainObject::GetImage(Uint32 index) const {
|
||||
MOBILEGL_ASSERT(index < m_images.size(), "Swapchain image index out of range");
|
||||
return m_images[index];
|
||||
}
|
||||
|
||||
VkImageLayout SwapchainObject::GetImageLayout(Uint32 index) const {
|
||||
MOBILEGL_ASSERT(index < m_imageLayouts.size(), "Swapchain image layout index out of range");
|
||||
return m_imageLayouts[index];
|
||||
}
|
||||
|
||||
void SwapchainObject::SetImageLayout(Uint32 index, VkImageLayout layout) {
|
||||
MOBILEGL_ASSERT(index < m_imageLayouts.size(), "Swapchain image layout index out of range");
|
||||
m_imageLayouts[index] = layout;
|
||||
}
|
||||
|
||||
void SwapchainObject::CreateImageViews(VkDevice device) {
|
||||
m_imageViews.resize(m_images.size(), VK_NULL_HANDLE);
|
||||
for (SizeT i = 0; i < m_imageViews.size(); i++) {
|
||||
VkImageViewCreateInfo createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
createInfo.image = m_images[i];
|
||||
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
createInfo.format = m_surfaceFormat.format;
|
||||
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
createInfo.subresourceRange.baseMipLevel = 0;
|
||||
createInfo.subresourceRange.levelCount = 1;
|
||||
createInfo.subresourceRange.baseArrayLayer = 0;
|
||||
createInfo.subresourceRange.layerCount = 1;
|
||||
VK_VERIFY(vkCreateImageView(device, &createInfo, nullptr, &m_imageViews[i]));
|
||||
}
|
||||
MGLOG_I("Swapchain image views created");
|
||||
}
|
||||
|
||||
#undef VK_VERIFY
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
@@ -0,0 +1,60 @@
|
||||
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.h
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Includes.h>
|
||||
#include "../VkIncludes.h"
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
class SwapchainObject {
|
||||
public:
|
||||
struct SwapchainCapabilities {
|
||||
VkSurfaceCapabilitiesKHR capabilities;
|
||||
Vector<VkSurfaceFormatKHR> surfaceFormats;
|
||||
Vector<VkPresentModeKHR> presentModes;
|
||||
|
||||
Bool IsComplete() const {
|
||||
return !surfaceFormats.empty() && !presentModes.empty();
|
||||
}
|
||||
};
|
||||
|
||||
static SwapchainCapabilities GetSwapchainCapabilities(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface);
|
||||
static VkSurfaceFormatKHR ChooseSwapchainSurfaceFormat(const Vector<VkSurfaceFormatKHR>& availableFormats);
|
||||
static VkPresentModeKHR ChooseSwapchainPresentMode(const Vector<VkPresentModeKHR>& availablePresentModes);
|
||||
|
||||
void Create(VkDevice device, const VkSwapchainCreateInfoKHR& createInfo);
|
||||
void Shutdown(VkDevice device);
|
||||
|
||||
VkSwapchainKHR GetHandle() const { return m_swapchain; }
|
||||
const VkSurfaceFormatKHR& GetSurfaceFormat() const { return m_surfaceFormat; }
|
||||
VkExtent2D GetExtent() const { return m_extent; }
|
||||
const Vector<VkImage>& GetImages() const { return m_images; }
|
||||
const Vector<VkImageView>& GetImageViews() const { return m_imageViews; }
|
||||
VkImage GetImage(Uint32 index) const;
|
||||
VkImageLayout GetImageLayout(Uint32 index) const;
|
||||
void SetImageLayout(Uint32 index, VkImageLayout layout);
|
||||
SizeT GetImageCount() const { return m_images.size(); }
|
||||
|
||||
private:
|
||||
void CreateImageViews(VkDevice device);
|
||||
static constexpr VkPresentModeKHR s_desiredPresentModes[] {
|
||||
VK_PRESENT_MODE_MAILBOX_KHR,
|
||||
VK_PRESENT_MODE_IMMEDIATE_KHR,
|
||||
VK_PRESENT_MODE_FIFO_RELAXED_KHR,
|
||||
VK_PRESENT_MODE_FIFO_KHR
|
||||
};
|
||||
|
||||
VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
|
||||
VkSurfaceFormatKHR m_surfaceFormat{};
|
||||
VkExtent2D m_extent{};
|
||||
Vector<VkImage> m_images;
|
||||
Vector<VkImageView> m_imageViews;
|
||||
Vector<VkImageLayout> m_imageLayouts;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
@@ -168,11 +168,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkViewport vp{};
|
||||
vp.x = 0;
|
||||
vp.y = 0;
|
||||
vp.width = (float)m_swapchainExtent.width;
|
||||
vp.height = (float)m_swapchainExtent.height;
|
||||
const auto swapchainExtent = m_swapchainObject.GetExtent();
|
||||
vp.width = (float)swapchainExtent.width;
|
||||
vp.height = (float)swapchainExtent.height;
|
||||
vp.minDepth = 0;
|
||||
vp.maxDepth = 1;
|
||||
VkRect2D scissor{{0, 0}, m_swapchainExtent};
|
||||
VkRect2D scissor{{0, 0}, swapchainExtent};
|
||||
VkPipelineViewportStateCreateInfo vpci{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
|
||||
vpci.viewportCount = 1;
|
||||
vpci.pViewports = &vp;
|
||||
@@ -256,7 +257,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// Prime the first frame so Render() always targets an acquired swapchain image.
|
||||
VK_VERIFY(vkWaitForFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex], VK_TRUE, UINT64_MAX));
|
||||
VK_VERIFY(vkResetFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex]));
|
||||
VK_VERIFY(vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX,
|
||||
VK_VERIFY(vkAcquireNextImageKHR(m_device, m_swapchainObject.GetHandle(), UINT64_MAX,
|
||||
m_imageAvailableSemaphores[m_currentFrameIndex], VK_NULL_HANDLE,
|
||||
&m_imageIndexAcquired));
|
||||
|
||||
@@ -335,7 +336,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
renderPassInfo.renderPass = m_renderPass;
|
||||
renderPassInfo.framebuffer = m_framebuffers[m_imageIndexAcquired];
|
||||
renderPassInfo.renderArea.offset = {0, 0};
|
||||
renderPassInfo.renderArea.extent = m_swapchainExtent;
|
||||
const auto swapchainExtent = m_swapchainObject.GetExtent();
|
||||
renderPassInfo.renderArea.extent = swapchainExtent;
|
||||
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
||||
renderPassInfo.clearValueCount = 1;
|
||||
renderPassInfo.pClearValues = &clearColor;
|
||||
@@ -347,15 +349,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkViewport viewport{};
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = static_cast<float>(m_swapchainExtent.width);
|
||||
viewport.height = static_cast<float>(m_swapchainExtent.height);
|
||||
viewport.width = static_cast<float>(swapchainExtent.width);
|
||||
viewport.height = static_cast<float>(swapchainExtent.height);
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
vkCmdSetViewport(m_commandBuffers[m_currentFrameIndex], 0, 1, &viewport);
|
||||
|
||||
VkRect2D scissor{};
|
||||
scissor.offset = {0, 0};
|
||||
scissor.extent = m_swapchainExtent;
|
||||
scissor.extent = swapchainExtent;
|
||||
vkCmdSetScissor(m_commandBuffers[m_currentFrameIndex], 0, 1, &scissor);
|
||||
|
||||
vkCmdDraw(m_commandBuffers[m_currentFrameIndex], 3, 1, 0, 0);
|
||||
@@ -369,12 +371,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
void VulkanRenderer::Present() {
|
||||
MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainImages.size(), "Present, acquired image index out of range");
|
||||
MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(),
|
||||
"Present, acquired image index out of range");
|
||||
const Bool hasRecordedWork = m_hasCommandBufferRecorded[m_currentFrameIndex];
|
||||
Bool needsLayoutTransitionForPresent = false;
|
||||
|
||||
if (!hasRecordedWork) {
|
||||
auto& acquiredImageLayout = m_swapchainImageLayouts[m_imageIndexAcquired];
|
||||
const auto acquiredImageLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
|
||||
if (acquiredImageLayout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR &&
|
||||
acquiredImageLayout != VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) {
|
||||
needsLayoutTransitionForPresent = true;
|
||||
@@ -395,7 +398,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
presentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
presentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
presentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
presentBarrier.image = m_swapchainImages[m_imageIndexAcquired];
|
||||
presentBarrier.image = m_swapchainObject.GetImage(m_imageIndexAcquired);
|
||||
presentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
presentBarrier.subresourceRange.baseMipLevel = 0;
|
||||
presentBarrier.subresourceRange.levelCount = 1;
|
||||
@@ -427,14 +430,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
submitInfo.pSignalSemaphores = signalSemaphores;
|
||||
VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, m_imageInFlightFences[m_currentFrameIndex]));
|
||||
m_hasCommandBufferRecorded[m_currentFrameIndex] = false;
|
||||
m_swapchainImageLayouts[m_imageIndexAcquired] = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
|
||||
// 2) Present current frame.
|
||||
VkPresentInfoKHR presentInfo{};
|
||||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
presentInfo.waitSemaphoreCount = 1;
|
||||
presentInfo.pWaitSemaphores = signalSemaphores;
|
||||
VkSwapchainKHR swapChains[] = {m_swapchain};
|
||||
VkSwapchainKHR swapChains[] = {m_swapchainObject.GetHandle()};
|
||||
presentInfo.swapchainCount = 1;
|
||||
presentInfo.pSwapchains = swapChains;
|
||||
presentInfo.pImageIndices = &m_imageIndexAcquired;
|
||||
@@ -455,8 +458,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
"Present, vkWaitForFences");
|
||||
VK_VERIFY(vkResetFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex]), "Present, vkResetFences");
|
||||
result =
|
||||
vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, m_imageAvailableSemaphores[m_currentFrameIndex],
|
||||
VK_NULL_HANDLE, &m_imageIndexAcquired);
|
||||
vkAcquireNextImageKHR(m_device, m_swapchainObject.GetHandle(), UINT64_MAX,
|
||||
m_imageAvailableSemaphores[m_currentFrameIndex], VK_NULL_HANDLE,
|
||||
&m_imageIndexAcquired);
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||||
MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result);
|
||||
RecreateSwapchain();
|
||||
@@ -644,8 +648,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
// Check swapchain capabilities
|
||||
newDevice.swapchainCapabilities = GetSwapchainCapabilities(newVkDevice, surface);
|
||||
if (!newDevice.swapchainCapabilities.IsComplete()) {
|
||||
auto swapchainCapabilities = SwapchainObject::GetSwapchainCapabilities(newVkDevice, surface);
|
||||
if (!swapchainCapabilities.IsComplete()) {
|
||||
outBetterDevice = otherDevice;
|
||||
MGLOG_I(" Ignored physical device. (Reason: Swapchain capabilities not met)");
|
||||
return false;
|
||||
@@ -695,32 +699,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return false;
|
||||
}
|
||||
|
||||
VkSurfaceFormatKHR VulkanRenderer::ChooseSwapchainSurfaceFormat(
|
||||
const Vector<VkSurfaceFormatKHR>& availableFormats) {
|
||||
for (const auto& availableFormat : availableFormats) {
|
||||
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB &&
|
||||
availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||
return availableFormat;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Properly rank other formats
|
||||
return availableFormats[0];
|
||||
}
|
||||
|
||||
VkPresentModeKHR VulkanRenderer::ChooseSwapchainPresentMode(const Vector<VkPresentModeKHR>& availablePresentModes) {
|
||||
for (auto desiredPresentMode : s_desiredPresentModes) {
|
||||
for (const auto& presentMode : availablePresentModes) {
|
||||
if (presentMode == desiredPresentMode) {
|
||||
return presentMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Properly rank other modes
|
||||
return availablePresentModes[0];
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::IsNecessaryDeviceExtensionSupported(VkPhysicalDevice device) {
|
||||
Uint32 extensionCount = 0;
|
||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
|
||||
@@ -751,32 +729,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
|
||||
VulkanRenderer::SwapchainCapabilities VulkanRenderer::GetSwapchainCapabilities(VkPhysicalDevice device,
|
||||
VkSurfaceKHR surface) {
|
||||
SwapchainCapabilities swapchainCapabilities;
|
||||
|
||||
VK_VERIFY(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &swapchainCapabilities.capabilities));
|
||||
Uint32 formatCount;
|
||||
VK_VERIFY(vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr));
|
||||
|
||||
if (formatCount != 0) {
|
||||
swapchainCapabilities.surfaceFormats.resize(formatCount);
|
||||
VK_VERIFY(vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount,
|
||||
swapchainCapabilities.surfaceFormats.data()));
|
||||
}
|
||||
|
||||
Uint32 presentModeCount;
|
||||
VK_VERIFY(vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr));
|
||||
|
||||
if (presentModeCount != 0) {
|
||||
swapchainCapabilities.presentModes.resize(presentModeCount);
|
||||
VK_VERIFY(vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount,
|
||||
swapchainCapabilities.presentModes.data()));
|
||||
}
|
||||
|
||||
return swapchainCapabilities;
|
||||
}
|
||||
|
||||
void VulkanRenderer::CreateLogicalDeviceAndQueues() {
|
||||
Float queuePriority = 1.0f;
|
||||
|
||||
@@ -1154,35 +1106,35 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
};
|
||||
|
||||
m_physicalDevice.swapchainCapabilities = GetSwapchainCapabilities(m_physicalDevice.handle, m_surface);
|
||||
const auto swapchainCapabilities = SwapchainObject::GetSwapchainCapabilities(m_physicalDevice.handle, m_surface);
|
||||
|
||||
auto& supportedSurfaceFormats = m_physicalDevice.swapchainCapabilities.surfaceFormats;
|
||||
auto& supportedSurfaceFormats = swapchainCapabilities.surfaceFormats;
|
||||
MGLOG_I("Got %d surface formats: ", supportedSurfaceFormats.size());
|
||||
for (auto& surfaceFormat : supportedSurfaceFormats) {
|
||||
MGLOG_I(" [%s, %s]", surfaceFormatToStr(surfaceFormat.format),
|
||||
colorSpaceToStr(surfaceFormat.colorSpace));
|
||||
}
|
||||
m_swapchainSurfaceFormat = ChooseSwapchainSurfaceFormat(supportedSurfaceFormats);
|
||||
MGLOG_I("Picked surface format: [%s, %s]", surfaceFormatToStr(m_swapchainSurfaceFormat.format),
|
||||
colorSpaceToStr(m_swapchainSurfaceFormat.colorSpace));
|
||||
const auto pickedSurfaceFormat = SwapchainObject::ChooseSwapchainSurfaceFormat(supportedSurfaceFormats);
|
||||
MGLOG_I("Picked surface format: [%s, %s]", surfaceFormatToStr(pickedSurfaceFormat.format),
|
||||
colorSpaceToStr(pickedSurfaceFormat.colorSpace));
|
||||
|
||||
auto& supportedPresentModes = m_physicalDevice.swapchainCapabilities.presentModes;
|
||||
auto& supportedPresentModes = swapchainCapabilities.presentModes;
|
||||
MGLOG_I("Got %d present mode: ", supportedPresentModes.size());
|
||||
for (auto& presentMode : supportedPresentModes) {
|
||||
MGLOG_I(" %s", presentModeToStr(presentMode));
|
||||
}
|
||||
auto presentMode = ChooseSwapchainPresentMode(supportedPresentModes);
|
||||
auto presentMode = SwapchainObject::ChooseSwapchainPresentMode(supportedPresentModes);
|
||||
MGLOG_I("Picked present mode: %s", presentModeToStr(presentMode));
|
||||
|
||||
const auto& swapchainCaps = m_physicalDevice.swapchainCapabilities.capabilities;
|
||||
const auto& swapchainCaps = swapchainCapabilities.capabilities;
|
||||
auto imageCount = std::max<uint32_t>(m_config.MaxFramesInFlight, swapchainCaps.minImageCount);
|
||||
MGLOG_I("Set minImageCount = %u", imageCount);
|
||||
|
||||
VkSwapchainCreateInfoKHR sci{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};
|
||||
sci.surface = m_surface;
|
||||
sci.minImageCount = imageCount;
|
||||
sci.imageFormat = m_swapchainSurfaceFormat.format;
|
||||
sci.imageColorSpace = m_swapchainSurfaceFormat.colorSpace;
|
||||
sci.imageFormat = pickedSurfaceFormat.format;
|
||||
sci.imageColorSpace = pickedSurfaceFormat.colorSpace;
|
||||
sci.imageExtent = swapchainCaps.currentExtent;
|
||||
sci.imageArrayLayers = 1;
|
||||
sci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
@@ -1204,39 +1156,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
sci.presentMode = presentMode;
|
||||
sci.clipped = VK_TRUE;
|
||||
sci.oldSwapchain = VK_NULL_HANDLE;
|
||||
VK_VERIFY(vkCreateSwapchainKHR(m_device, &sci, nullptr, &m_swapchain));
|
||||
|
||||
Uint32 gotImageCount = 0;
|
||||
VK_VERIFY(vkGetSwapchainImagesKHR(m_device, m_swapchain, &gotImageCount, nullptr));
|
||||
m_swapchainImages.resize(gotImageCount, VK_NULL_HANDLE);
|
||||
VK_VERIFY(vkGetSwapchainImagesKHR(m_device, m_swapchain, &gotImageCount, m_swapchainImages.data()));
|
||||
m_swapchainImageLayouts.assign(gotImageCount, VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
m_swapchainExtent = swapchainCaps.currentExtent;
|
||||
|
||||
MGLOG_I("Swapchain created, extent = %dx%d, swapchain imageCount = %d", m_swapchainExtent.width,
|
||||
m_swapchainExtent.height, gotImageCount);
|
||||
}
|
||||
|
||||
void VulkanRenderer::CreateSwapchainImageViews() {
|
||||
m_swapchainImageViews.resize(m_swapchainImages.size(), VK_NULL_HANDLE);
|
||||
for (size_t i = 0; i < m_swapchainImageViews.size(); i++) {
|
||||
VkImageViewCreateInfo createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
createInfo.image = m_swapchainImages[i];
|
||||
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
createInfo.format = m_swapchainSurfaceFormat.format;
|
||||
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
createInfo.subresourceRange.baseMipLevel = 0;
|
||||
createInfo.subresourceRange.levelCount = 1;
|
||||
createInfo.subresourceRange.baseArrayLayer = 0;
|
||||
createInfo.subresourceRange.layerCount = 1;
|
||||
VK_VERIFY(vkCreateImageView(m_device, &createInfo, nullptr, &m_swapchainImageViews[i]));
|
||||
}
|
||||
MGLOG_I("Swapchain image views created");
|
||||
m_swapchainObject.Create(m_device, sci);
|
||||
}
|
||||
|
||||
void VulkanRenderer::CreateCommandPool() {
|
||||
@@ -1260,7 +1180,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
void VulkanRenderer::CreateDefaultRenderPass() {
|
||||
VkAttachmentDescription color{};
|
||||
color.format = m_swapchainSurfaceFormat.format;
|
||||
color.format = m_swapchainObject.GetSurfaceFormat().format;
|
||||
color.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
@@ -1286,7 +1206,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
void VulkanRenderer::CreateDefaultFramebuffers() {
|
||||
// Create framebuffers now (use swapchain imageviews)
|
||||
const auto& imageViews = m_swapchainImageViews;
|
||||
const auto& imageViews = m_swapchainObject.GetImageViews();
|
||||
const auto swapchainExtent = m_swapchainObject.GetExtent();
|
||||
Vector<VkFramebuffer>& fbs = m_framebuffers;
|
||||
fbs.reserve(imageViews.size());
|
||||
for (auto iv : imageViews) {
|
||||
@@ -1295,8 +1216,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
fbci.renderPass = m_renderPass;
|
||||
fbci.attachmentCount = 1;
|
||||
fbci.pAttachments = attachments;
|
||||
fbci.width = m_swapchainExtent.width;
|
||||
fbci.height = m_swapchainExtent.height;
|
||||
fbci.width = swapchainExtent.width;
|
||||
fbci.height = swapchainExtent.height;
|
||||
fbci.layers = 1;
|
||||
VkFramebuffer fb;
|
||||
VK_VERIFY(vkCreateFramebuffer(m_device, &fbci, nullptr, &fb), "vkCreateFramebuffer");
|
||||
@@ -1406,24 +1327,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
m_renderPass = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
for (auto imageView : m_swapchainImageViews) {
|
||||
vkDestroyImageView(m_device, imageView, nullptr);
|
||||
}
|
||||
m_swapchainImageViews.clear();
|
||||
|
||||
if (m_swapchain != VK_NULL_HANDLE) {
|
||||
vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
|
||||
m_swapchain = VK_NULL_HANDLE;
|
||||
}
|
||||
m_swapchainImages.clear();
|
||||
m_swapchainImageLayouts.clear();
|
||||
m_swapchainObject.Shutdown(m_device);
|
||||
}
|
||||
|
||||
void VulkanRenderer::RecreateSwapchain() {
|
||||
// Handle cases like minimize on Windows, where swapchain could return a 0x0 extent
|
||||
m_physicalDevice.swapchainCapabilities = GetSwapchainCapabilities(m_physicalDevice.handle, m_surface);
|
||||
if (m_physicalDevice.swapchainCapabilities.capabilities.currentExtent.width == 0 ||
|
||||
m_physicalDevice.swapchainCapabilities.capabilities.currentExtent.height == 0) {
|
||||
const auto swapchainCapabilities = SwapchainObject::GetSwapchainCapabilities(m_physicalDevice.handle, m_surface);
|
||||
if (swapchainCapabilities.capabilities.currentExtent.width == 0 ||
|
||||
swapchainCapabilities.capabilities.currentExtent.height == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1432,7 +1343,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
ShutdownSwapchain();
|
||||
|
||||
CreateSwapchain();
|
||||
CreateSwapchainImageViews();
|
||||
CreateDefaultRenderPass();
|
||||
CreateDefaultFramebuffers();
|
||||
std::fill(m_hasCommandBufferRecorded.begin(), m_hasCommandBufferRecorded.end(), false);
|
||||
|
||||
@@ -8,15 +8,10 @@
|
||||
|
||||
#pragma once
|
||||
#include "Config.h"
|
||||
#include "SwapchainObject.h"
|
||||
#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)
|
||||
|
||||
#define ENUM_STR_CASE(c) case c: return #c;
|
||||
#include "../VkIncludes.h"
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
using RenderCallback = std::function<void(VkCommandBuffer cmdBuf, uint32_t imageIndex, VkExtent2D extent)>;
|
||||
@@ -48,20 +43,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Int32 presentFamily = -1;
|
||||
};
|
||||
|
||||
struct SwapchainCapabilities {
|
||||
VkSurfaceCapabilitiesKHR capabilities;
|
||||
Vector<VkSurfaceFormatKHR> surfaceFormats;
|
||||
Vector<VkPresentModeKHR> presentModes;
|
||||
|
||||
Bool IsComplete() const {
|
||||
return !surfaceFormats.empty() && !presentModes.empty();
|
||||
}
|
||||
};
|
||||
|
||||
struct PhysicalDevice {
|
||||
QueueFamilyIndices queueFamilies;
|
||||
VkPhysicalDeviceProperties properties;
|
||||
SwapchainCapabilities swapchainCapabilities;
|
||||
VkPhysicalDevice handle = VK_NULL_HANDLE;
|
||||
|
||||
Bool IsComplete() const {
|
||||
@@ -83,12 +67,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
||||
VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
|
||||
VkSurfaceFormatKHR m_swapchainSurfaceFormat;
|
||||
Vector<VkImage> m_swapchainImages;
|
||||
Vector<VkImageLayout> m_swapchainImageLayouts;
|
||||
VkExtent2D m_swapchainExtent;
|
||||
Vector<VkImageView> m_swapchainImageViews;
|
||||
SwapchainObject m_swapchainObject;
|
||||
|
||||
// Vector<VkQueueFamilyProperties> m_queueFamilies;
|
||||
// QueueFamilyIndices m_queueFamilyIndices;
|
||||
@@ -120,7 +99,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void PickPhysicalDevice();
|
||||
void CreateLogicalDeviceAndQueues();
|
||||
void CreateSwapchain();
|
||||
void CreateSwapchainImageViews();
|
||||
void CreateCommandPool();
|
||||
void CreateCommandBuffers();
|
||||
void CreateDefaultRenderPass();
|
||||
@@ -137,20 +115,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
static Int GetQueueFamilyIndex(const Vector<VkQueueFamilyProperties>& queueFamilies, VkQueueFlagBits flag);
|
||||
static Vector<VkExtensionProperties> EnumerateInstanceExtensions();
|
||||
static Bool IsNecessaryDeviceExtensionSupported(VkPhysicalDevice device);
|
||||
static SwapchainCapabilities GetSwapchainCapabilities(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface);
|
||||
static Bool GetMoreCapablePhysicalDevice(VkPhysicalDevice newVkDevice, VkSurfaceKHR surface, const PhysicalDevice& compareWithDevice, PhysicalDevice& outBetterDevice);
|
||||
static VkSurfaceFormatKHR ChooseSwapchainSurfaceFormat(const Vector<VkSurfaceFormatKHR>& availableFormats);
|
||||
static VkPresentModeKHR ChooseSwapchainPresentMode(const Vector<VkPresentModeKHR>& availablePresentModes);
|
||||
static constexpr VkDynamicState s_dynamicStates[] = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR
|
||||
};
|
||||
static constexpr VkPresentModeKHR s_desiredPresentModes[] {
|
||||
VK_PRESENT_MODE_MAILBOX_KHR,
|
||||
VK_PRESENT_MODE_IMMEDIATE_KHR,
|
||||
VK_PRESENT_MODE_FIFO_RELAXED_KHR,
|
||||
VK_PRESENT_MODE_FIFO_KHR
|
||||
};
|
||||
static constexpr const char* s_validationLayerNames[] = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
};
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// MobileGL - MobileGL/MG_Backend/DirectVulkan/VkIncludes.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
|
||||
|
||||
#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)
|
||||
|
||||
#define ENUM_STR_CASE(c) case c: return #c;
|
||||
Reference in New Issue
Block a user