mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
534 lines
25 KiB
C++
534 lines
25 KiB
C++
// 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"
|
|
|
|
#include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h"
|
|
#include "MG_State/GLState/TextureState/TextureObject2D.h"
|
|
|
|
#if defined(__has_include)
|
|
#if __has_include(<vulkan/vk_enum_string_helper.h>)
|
|
#include <vulkan/vk_enum_string_helper.h>
|
|
#define MOBILEGL_HAS_VK_ENUM_STRING_HELPER 1
|
|
#else
|
|
#define MOBILEGL_HAS_VK_ENUM_STRING_HELPER 0
|
|
#endif
|
|
#else
|
|
#define MOBILEGL_HAS_VK_ENUM_STRING_HELPER 0
|
|
#endif
|
|
|
|
#if !MOBILEGL_HAS_VK_ENUM_STRING_HELPER
|
|
static const char* string_VkFormat(VkFormat) {
|
|
return "VkFormat(unknown)";
|
|
}
|
|
|
|
static const char* string_VkColorSpaceKHR(VkColorSpaceKHR) {
|
|
return "VkColorSpaceKHR(unknown)";
|
|
}
|
|
|
|
static const char* string_VkPresentModeKHR(VkPresentModeKHR presentMode) {
|
|
switch (presentMode) {
|
|
case VK_PRESENT_MODE_IMMEDIATE_KHR:
|
|
return "VK_PRESENT_MODE_IMMEDIATE_KHR";
|
|
case VK_PRESENT_MODE_MAILBOX_KHR:
|
|
return "VK_PRESENT_MODE_MAILBOX_KHR";
|
|
case VK_PRESENT_MODE_FIFO_KHR:
|
|
return "VK_PRESENT_MODE_FIFO_KHR";
|
|
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
|
|
return "VK_PRESENT_MODE_FIFO_RELAXED_KHR";
|
|
default:
|
|
return "VkPresentModeKHR(unknown)";
|
|
}
|
|
}
|
|
|
|
static const char* string_VkSurfaceTransformFlagBitsKHR(VkSurfaceTransformFlagBitsKHR) {
|
|
return "VkSurfaceTransformFlagBitsKHR(unknown)";
|
|
}
|
|
#endif
|
|
|
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
|
namespace {
|
|
Bool HasStencilComponent(VkFormat format) {
|
|
return format == VK_FORMAT_D24_UNORM_S8_UINT || format == VK_FORMAT_D32_SFLOAT_S8_UINT;
|
|
}
|
|
|
|
VkFormat FindSupportedDepthStencilFormat(VkPhysicalDevice physicalDevice) {
|
|
const VkFormat candidates[] = {VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT,
|
|
VK_FORMAT_D32_SFLOAT};
|
|
for (VkFormat format : candidates) {
|
|
VkFormatProperties props{};
|
|
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);
|
|
if ((props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) {
|
|
return format;
|
|
}
|
|
}
|
|
return VK_FORMAT_UNDEFINED;
|
|
}
|
|
|
|
Uint32 FindMemoryType(VkPhysicalDevice physicalDevice, Uint32 typeFilter, VkMemoryPropertyFlags properties) {
|
|
VkPhysicalDeviceMemoryProperties memProperties{};
|
|
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
|
|
|
|
for (Uint32 i = 0; i < memProperties.memoryTypeCount; i++) {
|
|
if ((typeFilter & (1 << i)) &&
|
|
(memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
MOBILEGL_ASSERT(false, "Failed to find suitable memory type.");
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
|
|
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_UNORM ||
|
|
availableFormat.format == VK_FORMAT_R8G8B8A8_UNORM) &&
|
|
availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
|
return availableFormat;
|
|
}
|
|
}
|
|
for (const auto& availableFormat : availableFormats) {
|
|
if ((availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB ||
|
|
availableFormat.format == VK_FORMAT_R8G8B8A8_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, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
|
|
Uint32 graphicsQueueFamily, Uint32 presentQueueFamily, Uint32 minImageCountHint,
|
|
VkExtent2D desiredExtent) {
|
|
const auto swapchainCapabilities = GetSwapchainCapabilities(physicalDevice, surface);
|
|
MOBILEGL_ASSERT(swapchainCapabilities.IsComplete(),
|
|
"SwapchainObject::Create failed: incomplete swapchain capabilities");
|
|
|
|
MGLOG_I("Got %d surface formats:", swapchainCapabilities.surfaceFormats.size());
|
|
for (const auto& sf : swapchainCapabilities.surfaceFormats) {
|
|
MGLOG_D(" [%s, %s]", string_VkFormat(sf.format), string_VkColorSpaceKHR(sf.colorSpace));
|
|
}
|
|
|
|
const auto pickedSurfaceFormat = ChooseSwapchainSurfaceFormat(swapchainCapabilities.surfaceFormats);
|
|
MGLOG_I("Picked surface format: [%s, %s]", string_VkFormat(pickedSurfaceFormat.format),
|
|
string_VkColorSpaceKHR(pickedSurfaceFormat.colorSpace));
|
|
|
|
MGLOG_I("Got %d present modes:", swapchainCapabilities.presentModes.size());
|
|
for (const auto& pm : swapchainCapabilities.presentModes) {
|
|
MGLOG_D(" %s", string_VkPresentModeKHR(pm));
|
|
}
|
|
|
|
const auto presentMode = ChooseSwapchainPresentMode(swapchainCapabilities.presentModes);
|
|
MGLOG_I("Picked present mode: %s", string_VkPresentModeKHR(presentMode));
|
|
|
|
const auto& swapchainCaps = swapchainCapabilities.capabilities;
|
|
Uint32 targetImageCount = std::max<Uint32>(minImageCountHint, swapchainCaps.minImageCount);
|
|
if (swapchainCaps.maxImageCount != 0) {
|
|
targetImageCount = std::min(targetImageCount, swapchainCaps.maxImageCount);
|
|
}
|
|
MGLOG_I("Set minImageCount = %u", targetImageCount);
|
|
MGLOG_I("Swapchain currentTransform = %s",
|
|
string_VkSurfaceTransformFlagBitsKHR(swapchainCaps.currentTransform));
|
|
|
|
VkSwapchainCreateInfoKHR createInfo{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};
|
|
createInfo.surface = surface;
|
|
createInfo.minImageCount = targetImageCount;
|
|
createInfo.imageFormat = pickedSurfaceFormat.format;
|
|
createInfo.imageColorSpace = pickedSurfaceFormat.colorSpace;
|
|
createInfo.imageExtent = swapchainCaps.currentExtent;
|
|
if (createInfo.imageExtent.width == UINT32_MAX || createInfo.imageExtent.height == UINT32_MAX) {
|
|
createInfo.imageExtent.width = std::clamp(desiredExtent.width,
|
|
swapchainCaps.minImageExtent.width,
|
|
swapchainCaps.maxImageExtent.width);
|
|
createInfo.imageExtent.height = std::clamp(desiredExtent.height,
|
|
swapchainCaps.minImageExtent.height,
|
|
swapchainCaps.maxImageExtent.height);
|
|
}
|
|
const VkExtent2D defaultFramebufferExtent = createInfo.imageExtent;
|
|
if (swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
|
|
swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
|
|
std::swap(createInfo.imageExtent.width, createInfo.imageExtent.height);
|
|
}
|
|
|
|
createInfo.imageArrayLayers = 1;
|
|
const VkImageUsageFlags requiredImageUsage =
|
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
MOBILEGL_ASSERT((swapchainCaps.supportedUsageFlags & requiredImageUsage) == requiredImageUsage,
|
|
"Swapchain does not support required usage flags (COLOR_ATTACHMENT | TRANSFER_DST). "
|
|
"supportedUsageFlags=0x%x",
|
|
static_cast<Uint32>(swapchainCaps.supportedUsageFlags));
|
|
|
|
VkImageUsageFlags imageUsage = requiredImageUsage;
|
|
if ((swapchainCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) != 0) {
|
|
imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
}
|
|
createInfo.imageUsage = imageUsage;
|
|
MGLOG_I("Swapchain imageUsage = 0x%x (supportedUsageFlags = 0x%x)", static_cast<Uint32>(createInfo.imageUsage),
|
|
static_cast<Uint32>(swapchainCaps.supportedUsageFlags));
|
|
Uint32 queueFamilyIndices[] = {graphicsQueueFamily, presentQueueFamily};
|
|
if (graphicsQueueFamily != presentQueueFamily) {
|
|
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
|
createInfo.queueFamilyIndexCount = 2;
|
|
createInfo.pQueueFamilyIndices = queueFamilyIndices;
|
|
} else {
|
|
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
createInfo.queueFamilyIndexCount = 0;
|
|
createInfo.pQueueFamilyIndices = nullptr;
|
|
}
|
|
createInfo.preTransform = swapchainCaps.currentTransform;
|
|
MGLOG_I("Set swapchain preTransform = %s", string_VkSurfaceTransformFlagBitsKHR(createInfo.preTransform));
|
|
const VkCompositeAlphaFlagBitsKHR compositeAlphaCandidates[] = {
|
|
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
|
VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
|
|
VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
|
|
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
|
|
};
|
|
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
|
for (auto candidate : compositeAlphaCandidates) {
|
|
if ((swapchainCaps.supportedCompositeAlpha & candidate) != 0) {
|
|
createInfo.compositeAlpha = candidate;
|
|
break;
|
|
}
|
|
}
|
|
createInfo.presentMode = presentMode;
|
|
createInfo.clipped = VK_TRUE;
|
|
createInfo.oldSwapchain = VK_NULL_HANDLE;
|
|
|
|
m_surfaceFormat = {createInfo.imageFormat, createInfo.imageColorSpace};
|
|
m_extent = createInfo.imageExtent;
|
|
// The surface-space extent this swapchain was built from, i.e. before the
|
|
// quarter-turn swap above. Out-of-date checks must compare in THIS space: comparing a
|
|
// freshly queried currentExtent against the swapped m_extent flips axes every rotation
|
|
// and makes the comparison alternate forever.
|
|
m_surfaceExtent = defaultFramebufferExtent;
|
|
m_preTransform = createInfo.preTransform;
|
|
|
|
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);
|
|
// Fresh swapchain images hold garbage until a render pass stores into them.
|
|
m_imageContentDefined.assign(imageCount, false);
|
|
m_depthStencilContentDefined.assign(imageCount, false);
|
|
|
|
CreateImageViews(device);
|
|
CreateDepthStencilResources(device, physicalDevice);
|
|
|
|
MGLOG_I("Swapchain created, extent = %dx%d, swapchain imageCount = %d", m_extent.width, m_extent.height,
|
|
imageCount);
|
|
|
|
// Properly initialize Default FBO here
|
|
auto& defaultFBOInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo;
|
|
const Int extentWidth = static_cast<Int>(defaultFramebufferExtent.width);
|
|
const Int extentHeight = static_cast<Int>(defaultFramebufferExtent.height);
|
|
const SizeT defaultAttachmentByteSize =
|
|
static_cast<SizeT>(defaultFramebufferExtent.width) *
|
|
static_cast<SizeT>(defaultFramebufferExtent.height) * 4;
|
|
|
|
auto* colorTex = static_cast<MG_State::GLState::TextureObject2D*>(defaultFBOInfo->colorAttachment.get());
|
|
colorTex->AllocateStorage(
|
|
TextureUploadTarget::Texture2D, 0, {
|
|
{extentWidth, extentHeight, 1},
|
|
defaultAttachmentByteSize}); // TODO: 4 is format size
|
|
TextureInternalFormat depthFormat = TextureInternalFormat::Depth24Stencil8;
|
|
switch (m_depthStencilFormat) {
|
|
case VK_FORMAT_D24_UNORM_S8_UINT:
|
|
depthFormat = TextureInternalFormat::Depth24Stencil8;
|
|
break;
|
|
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
|
depthFormat = TextureInternalFormat::Depth32FStencil8;
|
|
break;
|
|
case VK_FORMAT_D32_SFLOAT:
|
|
depthFormat = TextureInternalFormat::DepthComponent32F;
|
|
break;
|
|
default:
|
|
depthFormat = TextureInternalFormat::Depth24Stencil8;
|
|
break;
|
|
}
|
|
auto* depthTex = static_cast<MG_State::GLState::TextureObject2D*>(defaultFBOInfo->depthAttachment.get());
|
|
depthTex->SetInternalFormat(depthFormat);
|
|
depthTex->AllocateStorage(TextureUploadTarget::Texture2D, 0, {
|
|
{extentWidth, extentHeight, 1},
|
|
defaultAttachmentByteSize}); // TODO: 4 is format size
|
|
|
|
// The default FBO's stencil attachment must track the swapchain extent:
|
|
// FramebufferObject::CheckCompleteness requires every valid attachment
|
|
// to share the same dimensions, and Init.cpp leaves a 512x512 placeholder.
|
|
// Without this the retrace-layer glReadPixels snapshot fails with
|
|
// GL_INVALID_FRAMEBUFFER_OPERATION on DirectVulkan.
|
|
TextureInternalFormat stencilFormat = TextureInternalFormat::Depth24Stencil8;
|
|
switch (m_depthStencilFormat) {
|
|
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
|
stencilFormat = TextureInternalFormat::Depth32FStencil8;
|
|
break;
|
|
case VK_FORMAT_D24_UNORM_S8_UINT:
|
|
stencilFormat = TextureInternalFormat::Depth24Stencil8;
|
|
break;
|
|
default:
|
|
// No stencil plane; mirror the depth format for consistency.
|
|
stencilFormat = depthFormat;
|
|
break;
|
|
}
|
|
auto* stencilTex = static_cast<MG_State::GLState::TextureObject2D*>(defaultFBOInfo->stencilAttachment.get());
|
|
stencilTex->SetInternalFormat(stencilFormat);
|
|
stencilTex->AllocateStorage(TextureUploadTarget::Texture2D, 0, {
|
|
{extentWidth, extentHeight, 1},
|
|
defaultAttachmentByteSize}); // TODO: 4 is format size
|
|
|
|
}
|
|
|
|
void SwapchainObject::CreateDepthStencilResources(VkDevice device, VkPhysicalDevice physicalDevice) {
|
|
DestroyDepthStencilResources(device);
|
|
|
|
const auto imageCount = static_cast<Uint32>(m_images.size());
|
|
if (imageCount == 0) {
|
|
return;
|
|
}
|
|
|
|
m_depthStencilFormat = FindSupportedDepthStencilFormat(physicalDevice);
|
|
MOBILEGL_ASSERT(m_depthStencilFormat != VK_FORMAT_UNDEFINED, "No supported depth/stencil format found.");
|
|
|
|
m_depthStencilImages.assign(imageCount, VK_NULL_HANDLE);
|
|
m_depthStencilImageMemories.assign(imageCount, VK_NULL_HANDLE);
|
|
m_depthStencilImageViews.assign(imageCount, VK_NULL_HANDLE);
|
|
m_depthStencilImageLayouts.assign(imageCount, VK_IMAGE_LAYOUT_UNDEFINED);
|
|
|
|
for (Uint32 i = 0; i < imageCount; ++i) {
|
|
VkImageCreateInfo imageInfo{};
|
|
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
|
imageInfo.extent.width = m_extent.width;
|
|
imageInfo.extent.height = m_extent.height;
|
|
imageInfo.extent.depth = 1;
|
|
imageInfo.mipLevels = 1;
|
|
imageInfo.arrayLayers = 1;
|
|
imageInfo.format = m_depthStencilFormat;
|
|
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
VK_VERIFY(vkCreateImage(device, &imageInfo, nullptr, &m_depthStencilImages[i]), "vkCreateImage(depth)");
|
|
|
|
VkMemoryRequirements memRequirements{};
|
|
vkGetImageMemoryRequirements(device, m_depthStencilImages[i], &memRequirements);
|
|
|
|
VkMemoryAllocateInfo allocInfo{};
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
allocInfo.allocationSize = memRequirements.size;
|
|
allocInfo.memoryTypeIndex =
|
|
FindMemoryType(physicalDevice, memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
|
VK_VERIFY(vkAllocateMemory(device, &allocInfo, nullptr, &m_depthStencilImageMemories[i]),
|
|
"vkAllocateMemory(depth)");
|
|
VK_VERIFY(vkBindImageMemory(device, m_depthStencilImages[i], m_depthStencilImageMemories[i], 0),
|
|
"vkBindImageMemory(depth)");
|
|
|
|
VkImageViewCreateInfo viewInfo{};
|
|
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
viewInfo.image = m_depthStencilImages[i];
|
|
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
viewInfo.format = m_depthStencilFormat;
|
|
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
if (HasStencilComponent(m_depthStencilFormat)) {
|
|
viewInfo.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
}
|
|
viewInfo.subresourceRange.baseMipLevel = 0;
|
|
viewInfo.subresourceRange.levelCount = 1;
|
|
viewInfo.subresourceRange.baseArrayLayer = 0;
|
|
viewInfo.subresourceRange.layerCount = 1;
|
|
VK_VERIFY(vkCreateImageView(device, &viewInfo, nullptr, &m_depthStencilImageViews[i]),
|
|
"vkCreateImageView(depth)");
|
|
}
|
|
}
|
|
|
|
void SwapchainObject::DestroyDepthStencilResources(VkDevice device) {
|
|
for (auto view : m_depthStencilImageViews) {
|
|
if (view != VK_NULL_HANDLE) {
|
|
vkDestroyImageView(device, view, nullptr);
|
|
}
|
|
}
|
|
m_depthStencilImageViews.clear();
|
|
|
|
for (auto image : m_depthStencilImages) {
|
|
if (image != VK_NULL_HANDLE) {
|
|
vkDestroyImage(device, image, nullptr);
|
|
}
|
|
}
|
|
m_depthStencilImages.clear();
|
|
|
|
for (auto memory : m_depthStencilImageMemories) {
|
|
if (memory != VK_NULL_HANDLE) {
|
|
vkFreeMemory(device, memory, nullptr);
|
|
}
|
|
}
|
|
m_depthStencilImageMemories.clear();
|
|
m_depthStencilImageLayouts.clear();
|
|
m_depthStencilFormat = VK_FORMAT_UNDEFINED;
|
|
}
|
|
|
|
void SwapchainObject::Shutdown(VkDevice device) {
|
|
DestroyDepthStencilResources(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();
|
|
m_imageContentDefined.clear();
|
|
m_depthStencilContentDefined.clear();
|
|
m_preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
|
}
|
|
|
|
Bool SwapchainObject::IsImageContentDefined(Uint32 index) const {
|
|
MOBILEGL_ASSERT(index < m_imageContentDefined.size(), "Swapchain image content index out of range");
|
|
return m_imageContentDefined[index];
|
|
}
|
|
|
|
void SwapchainObject::SetImageContentDefined(Uint32 index, Bool defined) {
|
|
MOBILEGL_ASSERT(index < m_imageContentDefined.size(), "Swapchain image content index out of range");
|
|
m_imageContentDefined[index] = defined;
|
|
}
|
|
|
|
Bool SwapchainObject::IsDepthStencilContentDefined(Uint32 index) const {
|
|
MOBILEGL_ASSERT(index < m_depthStencilContentDefined.size(),
|
|
"Swapchain depth/stencil content index out of range");
|
|
return m_depthStencilContentDefined[index];
|
|
}
|
|
|
|
void SwapchainObject::SetDepthStencilContentDefined(Uint32 index, Bool defined) {
|
|
MOBILEGL_ASSERT(index < m_depthStencilContentDefined.size(),
|
|
"Swapchain depth/stencil content index out of range");
|
|
m_depthStencilContentDefined[index] = defined;
|
|
}
|
|
|
|
void SwapchainObject::SetAllDepthStencilContentUndefined() {
|
|
for (SizeT i = 0; i < m_depthStencilContentDefined.size(); ++i) {
|
|
m_depthStencilContentDefined[i] = false;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
VkImage SwapchainObject::GetDepthStencilImage(Uint32 index) const {
|
|
MOBILEGL_ASSERT(index < m_depthStencilImages.size(), "Swapchain depth/stencil image index out of range");
|
|
return m_depthStencilImages[index];
|
|
}
|
|
|
|
VkImageView SwapchainObject::GetDepthStencilImageView(Uint32 index) const {
|
|
MOBILEGL_ASSERT(index < m_depthStencilImageViews.size(),
|
|
"Swapchain depth/stencil image view index out of range");
|
|
return m_depthStencilImageViews[index];
|
|
}
|
|
|
|
VkImageLayout SwapchainObject::GetDepthStencilImageLayout(Uint32 index) const {
|
|
MOBILEGL_ASSERT(index < m_depthStencilImageLayouts.size(),
|
|
"Swapchain depth/stencil image layout index out of range");
|
|
return m_depthStencilImageLayouts[index];
|
|
}
|
|
|
|
void SwapchainObject::SetDepthStencilImageLayout(Uint32 index, VkImageLayout layout) {
|
|
MOBILEGL_ASSERT(index < m_depthStencilImageLayouts.size(),
|
|
"Swapchain depth/stencil image layout index out of range");
|
|
m_depthStencilImageLayouts[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
|