18bf80f4bSopenharmony_ci/*
28bf80f4bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
38bf80f4bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
48bf80f4bSopenharmony_ci * you may not use this file except in compliance with the License.
58bf80f4bSopenharmony_ci * You may obtain a copy of the License at
68bf80f4bSopenharmony_ci *
78bf80f4bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
88bf80f4bSopenharmony_ci *
98bf80f4bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
108bf80f4bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
118bf80f4bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128bf80f4bSopenharmony_ci * See the License for the specific language governing permissions and
138bf80f4bSopenharmony_ci * limitations under the License.
148bf80f4bSopenharmony_ci */
158bf80f4bSopenharmony_ci
168bf80f4bSopenharmony_ci#include "swapchain_vk.h"
178bf80f4bSopenharmony_ci
188bf80f4bSopenharmony_ci#include <algorithm>
198bf80f4bSopenharmony_ci#include <cstddef>
208bf80f4bSopenharmony_ci#include <cstdint>
218bf80f4bSopenharmony_ci#include <vulkan/vulkan_core.h>
228bf80f4bSopenharmony_ci
238bf80f4bSopenharmony_ci#include <base/containers/vector.h>
248bf80f4bSopenharmony_ci#include <base/math/mathf.h>
258bf80f4bSopenharmony_ci#include <base/util/formats.h>
268bf80f4bSopenharmony_ci#include <core/intf_engine.h>
278bf80f4bSopenharmony_ci#include <render/device/gpu_resource_desc.h>
288bf80f4bSopenharmony_ci#include <render/namespace.h>
298bf80f4bSopenharmony_ci#include <render/vulkan/intf_device_vk.h>
308bf80f4bSopenharmony_ci
318bf80f4bSopenharmony_ci#include "util/log.h"
328bf80f4bSopenharmony_ci#include "vulkan/create_functions_vk.h"
338bf80f4bSopenharmony_ci#include "vulkan/device_vk.h"
348bf80f4bSopenharmony_ci#include "vulkan/validate_vk.h"
358bf80f4bSopenharmony_ci
368bf80f4bSopenharmony_ciusing namespace BASE_NS;
378bf80f4bSopenharmony_ci
388bf80f4bSopenharmony_ciRENDER_BEGIN_NAMESPACE()
398bf80f4bSopenharmony_cinamespace {
408bf80f4bSopenharmony_ciFormat GetValidDepthFormat(const DeviceVk& deviceVk)
418bf80f4bSopenharmony_ci{
428bf80f4bSopenharmony_ci    constexpr uint32_t PREFERRED_FORMAT_COUNT { 3 };
438bf80f4bSopenharmony_ci    constexpr Format preferredFormats[PREFERRED_FORMAT_COUNT] = { BASE_FORMAT_D24_UNORM_S8_UINT, BASE_FORMAT_D32_SFLOAT,
448bf80f4bSopenharmony_ci        BASE_FORMAT_D16_UNORM };
458bf80f4bSopenharmony_ci#ifndef NDEBUG
468bf80f4bSopenharmony_ci    constexpr string_view PREFERRED_FORMAT_NAMES[PREFERRED_FORMAT_COUNT] = { "BASE_FORMAT_D24_UNORM_S8_UINT",
478bf80f4bSopenharmony_ci        "BASE_FORMAT_D32_SFLOAT", "BASE_FORMAT_D16_UNORM" };
488bf80f4bSopenharmony_ci#endif
498bf80f4bSopenharmony_ci    Format finalFormat = BASE_FORMAT_UNDEFINED;
508bf80f4bSopenharmony_ci    const auto& devPlat = deviceVk.GetPlatformInternalDataVk();
518bf80f4bSopenharmony_ci    for (uint32_t idx = 0; idx < PREFERRED_FORMAT_COUNT; ++idx) {
528bf80f4bSopenharmony_ci        finalFormat = preferredFormats[idx];
538bf80f4bSopenharmony_ci        for (const auto& supportedDepthFormat : devPlat.supportedDepthFormats) {
548bf80f4bSopenharmony_ci            if (finalFormat == supportedDepthFormat) {
558bf80f4bSopenharmony_ci#ifndef NDEBUG
568bf80f4bSopenharmony_ci                PLUGIN_LOG_D(
578bf80f4bSopenharmony_ci                    "selected CORE_DEFAULT_BACKBUFFER format: %s", string(PREFERRED_FORMAT_NAMES[idx]).c_str());
588bf80f4bSopenharmony_ci#endif
598bf80f4bSopenharmony_ci                idx = PREFERRED_FORMAT_COUNT;
608bf80f4bSopenharmony_ci                break;
618bf80f4bSopenharmony_ci            }
628bf80f4bSopenharmony_ci        }
638bf80f4bSopenharmony_ci    }
648bf80f4bSopenharmony_ci    return finalFormat;
658bf80f4bSopenharmony_ci}
668bf80f4bSopenharmony_ci
678bf80f4bSopenharmony_cistruct ColorInfo {
688bf80f4bSopenharmony_ci    VkFormat format { VK_FORMAT_UNDEFINED };
698bf80f4bSopenharmony_ci    VkColorSpaceKHR colorSpace { VK_COLOR_SPACE_MAX_ENUM_KHR };
708bf80f4bSopenharmony_ci};
718bf80f4bSopenharmony_ci
728bf80f4bSopenharmony_ciVkFormat GetColorFormat(const uint32_t flags, const vector<VkSurfaceFormatKHR>& surfaceFormats)
738bf80f4bSopenharmony_ci{
748bf80f4bSopenharmony_ci    constexpr uint32_t preferredFormatCount { 4u };
758bf80f4bSopenharmony_ci    constexpr VkFormat srgbFormats[preferredFormatCount] = { VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_B8G8R8A8_SRGB,
768bf80f4bSopenharmony_ci        VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM };
778bf80f4bSopenharmony_ci    constexpr VkFormat nonSrgbFormats[preferredFormatCount] = { VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM,
788bf80f4bSopenharmony_ci        VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_B8G8R8A8_SRGB };
798bf80f4bSopenharmony_ci
808bf80f4bSopenharmony_ci    const bool preferSrgbFormat = (flags & SwapchainFlagBits::CORE_SWAPCHAIN_SRGB_BIT);
818bf80f4bSopenharmony_ci    const array_view<const VkFormat> formats =
828bf80f4bSopenharmony_ci        (preferSrgbFormat) ? array_view<const VkFormat> { srgbFormats, preferredFormatCount }
838bf80f4bSopenharmony_ci                           : array_view<const VkFormat> { nonSrgbFormats, preferredFormatCount };
848bf80f4bSopenharmony_ci
858bf80f4bSopenharmony_ci    // If pSurfaceFormats includes just one entry, whose value for format is VK_FORMAT_UNDEFINED,
868bf80f4bSopenharmony_ci    // surface has no preferred format. In this case, the application can use any valid VkFormat value.
878bf80f4bSopenharmony_ci    if (surfaceFormats[0].format == VK_FORMAT_UNDEFINED && surfaceFormats.size() == 1) {
888bf80f4bSopenharmony_ci        return VK_FORMAT_R8G8B8A8_SRGB;
898bf80f4bSopenharmony_ci    }
908bf80f4bSopenharmony_ci
918bf80f4bSopenharmony_ci    for (size_t idx = 0; idx < formats.size(); ++idx) {
928bf80f4bSopenharmony_ci        for (size_t jdx = 0; jdx < surfaceFormats.size(); ++jdx) {
938bf80f4bSopenharmony_ci            if (formats[idx] == surfaceFormats[jdx].format) {
948bf80f4bSopenharmony_ci                return surfaceFormats[jdx].format;
958bf80f4bSopenharmony_ci            }
968bf80f4bSopenharmony_ci        }
978bf80f4bSopenharmony_ci    }
988bf80f4bSopenharmony_ci    return VK_FORMAT_UNDEFINED;
998bf80f4bSopenharmony_ci}
1008bf80f4bSopenharmony_ci
1018bf80f4bSopenharmony_ciColorInfo GetColorInfo(const VkPhysicalDevice physicalDevice, const VkSurfaceKHR surface, const uint32_t flags)
1028bf80f4bSopenharmony_ci{
1038bf80f4bSopenharmony_ci    // Pick a color format for the swapchain.
1048bf80f4bSopenharmony_ci    uint32_t surfaceFormatsCount = 0;
1058bf80f4bSopenharmony_ci    VALIDATE_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatsCount, nullptr));
1068bf80f4bSopenharmony_ci
1078bf80f4bSopenharmony_ci    vector<VkSurfaceFormatKHR> surfaceFormats(surfaceFormatsCount);
1088bf80f4bSopenharmony_ci    VALIDATE_VK_RESULT(
1098bf80f4bSopenharmony_ci        vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatsCount, surfaceFormats.data()));
1108bf80f4bSopenharmony_ci
1118bf80f4bSopenharmony_ci    ColorInfo ci;
1128bf80f4bSopenharmony_ci    ci.format = GetColorFormat(flags, surfaceFormats);
1138bf80f4bSopenharmony_ci    ci.colorSpace = VK_COLOR_SPACE_MAX_ENUM_KHR;
1148bf80f4bSopenharmony_ci    for (size_t idx = 0; idx < surfaceFormats.size(); ++idx) {
1158bf80f4bSopenharmony_ci        if (surfaceFormats[idx].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
1168bf80f4bSopenharmony_ci            ci.colorSpace = surfaceFormats[idx].colorSpace;
1178bf80f4bSopenharmony_ci            break;
1188bf80f4bSopenharmony_ci        }
1198bf80f4bSopenharmony_ci    }
1208bf80f4bSopenharmony_ci    PLUGIN_ASSERT_MSG(ci.colorSpace != VK_COLOR_SPACE_MAX_ENUM_KHR, "colorspace not correct");
1218bf80f4bSopenharmony_ci
1228bf80f4bSopenharmony_ci    PLUGIN_ASSERT_MSG(ci.format != VK_FORMAT_UNDEFINED, "colorformat not correct");
1238bf80f4bSopenharmony_ci    PLUGIN_LOG_D("swapchainColorFormat: %u swapchainColorSpace %u", ci.format, ci.colorSpace);
1248bf80f4bSopenharmony_ci
1258bf80f4bSopenharmony_ci    return ci;
1268bf80f4bSopenharmony_ci}
1278bf80f4bSopenharmony_ci
1288bf80f4bSopenharmony_ciVkPresentModeKHR GetPresentMode(const VkPhysicalDevice physicalDevice, const VkSurfaceKHR surface, const uint32_t flags)
1298bf80f4bSopenharmony_ci{
1308bf80f4bSopenharmony_ci    // Pick a present mode for the swapchain.
1318bf80f4bSopenharmony_ci    uint32_t presentModeCount;
1328bf80f4bSopenharmony_ci    VALIDATE_VK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, nullptr));
1338bf80f4bSopenharmony_ci
1348bf80f4bSopenharmony_ci    vector<VkPresentModeKHR> presentModes(presentModeCount);
1358bf80f4bSopenharmony_ci    VALIDATE_VK_RESULT(
1368bf80f4bSopenharmony_ci        vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data()));
1378bf80f4bSopenharmony_ci
1388bf80f4bSopenharmony_ci    VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; // FIFO must be supported by the specification.
1398bf80f4bSopenharmony_ci    if ((flags & SwapchainFlagBits::CORE_SWAPCHAIN_VSYNC_BIT) != SwapchainFlagBits::CORE_SWAPCHAIN_VSYNC_BIT) {
1408bf80f4bSopenharmony_ci        // immediate is really without vsync, but it might not be supported, so we also check for mailbox.
1418bf80f4bSopenharmony_ci        if (std::any_of(presentModes.cbegin(), presentModes.cend(),
1428bf80f4bSopenharmony_ci                [](const VkPresentModeKHR& supported) { return supported == VK_PRESENT_MODE_IMMEDIATE_KHR; })) {
1438bf80f4bSopenharmony_ci            swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
1448bf80f4bSopenharmony_ci        } else if (std::any_of(presentModes.cbegin(), presentModes.cend(),
1458bf80f4bSopenharmony_ci                       [](const VkPresentModeKHR& supported) { return supported == VK_PRESENT_MODE_MAILBOX_KHR; })) {
1468bf80f4bSopenharmony_ci            swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
1478bf80f4bSopenharmony_ci        }
1488bf80f4bSopenharmony_ci    }
1498bf80f4bSopenharmony_ci
1508bf80f4bSopenharmony_ci#if (RENDER_DEV_ENABLED == 1)
1518bf80f4bSopenharmony_ci    constexpr uint32_t strArraySize { 4 };
1528bf80f4bSopenharmony_ci    constexpr string_view presentModeStrings[strArraySize] = {
1538bf80f4bSopenharmony_ci        "VK_PRESENT_MODE_IMMEDIATE_KHR",
1548bf80f4bSopenharmony_ci        "VK_PRESENT_MODE_MAILBOX_KHR",
1558bf80f4bSopenharmony_ci        "VK_PRESENT_MODE_FIFO_KHR",
1568bf80f4bSopenharmony_ci        "VK_PRESENT_MODE_FIFO_RELAXED_KHR",
1578bf80f4bSopenharmony_ci    };
1588bf80f4bSopenharmony_ci
1598bf80f4bSopenharmony_ci    PLUGIN_LOG_I("Available swapchain present modes:");
1608bf80f4bSopenharmony_ci    for (auto const presentMode : presentModes) {
1618bf80f4bSopenharmony_ci        if ((uint32_t)presentMode < strArraySize) {
1628bf80f4bSopenharmony_ci            PLUGIN_LOG_I("  %s", presentModeStrings[presentMode].data());
1638bf80f4bSopenharmony_ci        }
1648bf80f4bSopenharmony_ci    }
1658bf80f4bSopenharmony_ci    PLUGIN_LOG_I("Selected swapchain present modes:");
1668bf80f4bSopenharmony_ci    if ((uint32_t)swapchainPresentMode < strArraySize) {
1678bf80f4bSopenharmony_ci        PLUGIN_LOG_I("  %s", presentModeStrings[swapchainPresentMode].data());
1688bf80f4bSopenharmony_ci    }
1698bf80f4bSopenharmony_ci#else
1708bf80f4bSopenharmony_ci    PLUGIN_LOG_D("swapchainPresentMode: %x", swapchainPresentMode);
1718bf80f4bSopenharmony_ci#endif
1728bf80f4bSopenharmony_ci
1738bf80f4bSopenharmony_ci    return swapchainPresentMode;
1748bf80f4bSopenharmony_ci}
1758bf80f4bSopenharmony_ci
1768bf80f4bSopenharmony_civoid ClampSwapchainExtent(const VkSurfaceCapabilitiesKHR& surfaceCapabilities, VkExtent2D& extent)
1778bf80f4bSopenharmony_ci{
1788bf80f4bSopenharmony_ci    extent.width =
1798bf80f4bSopenharmony_ci        std::clamp(extent.width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width);
1808bf80f4bSopenharmony_ci    extent.height =
1818bf80f4bSopenharmony_ci        std::clamp(extent.height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height);
1828bf80f4bSopenharmony_ci
1838bf80f4bSopenharmony_ci    PLUGIN_LOG_D("swapchainExtent: %u x %u", extent.width, extent.height);
1848bf80f4bSopenharmony_ci    if ((extent.width == 0) || (extent.height == 0)) {
1858bf80f4bSopenharmony_ci        PLUGIN_LOG_E(
1868bf80f4bSopenharmony_ci            "zero sized swapchain cannot be created in vulkan (width: %u, height: %u)", extent.width, extent.height);
1878bf80f4bSopenharmony_ci        PLUGIN_LOG_E("using 1x1 swapchain");
1888bf80f4bSopenharmony_ci        PLUGIN_ASSERT(false);
1898bf80f4bSopenharmony_ci        extent.width = 1;
1908bf80f4bSopenharmony_ci        extent.height = 1;
1918bf80f4bSopenharmony_ci    }
1928bf80f4bSopenharmony_ci}
1938bf80f4bSopenharmony_ci
1948bf80f4bSopenharmony_ciconstexpr GpuImageDesc GetColorDesc(
1958bf80f4bSopenharmony_ci    const uint32_t width, const uint32_t height, const Format format, const ImageUsageFlags imageUsageFlags)
1968bf80f4bSopenharmony_ci{
1978bf80f4bSopenharmony_ci    return {
1988bf80f4bSopenharmony_ci        ImageType::CORE_IMAGE_TYPE_2D,                                 // imageType
1998bf80f4bSopenharmony_ci        ImageViewType::CORE_IMAGE_VIEW_TYPE_2D,                        // imageViewType
2008bf80f4bSopenharmony_ci        format,                                                        // format
2018bf80f4bSopenharmony_ci        ImageTiling::CORE_IMAGE_TILING_OPTIMAL,                        // imageTiling
2028bf80f4bSopenharmony_ci        imageUsageFlags,                                               // usageFlags
2038bf80f4bSopenharmony_ci        MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, // memoryPropertyFlags
2048bf80f4bSopenharmony_ci        0,                                                             // createFlags
2058bf80f4bSopenharmony_ci        EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS |
2068bf80f4bSopenharmony_ci            EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_RESET_STATE_ON_FRAME_BORDERS, // engineCreationFlags
2078bf80f4bSopenharmony_ci        width,                                                                                    // width
2088bf80f4bSopenharmony_ci        height,                                                                                   // height
2098bf80f4bSopenharmony_ci        1,                                                                                        // depth
2108bf80f4bSopenharmony_ci        1,                                                                                        // mipCount
2118bf80f4bSopenharmony_ci        1,                                                                                        // layerCount
2128bf80f4bSopenharmony_ci        SampleCountFlagBits::CORE_SAMPLE_COUNT_1_BIT,                                             // sampleCountFlags
2138bf80f4bSopenharmony_ci        {},                                                                                       // componentMapping
2148bf80f4bSopenharmony_ci    };
2158bf80f4bSopenharmony_ci}
2168bf80f4bSopenharmony_ci
2178bf80f4bSopenharmony_ciconstexpr GpuImageDesc GetDepthDesc(const uint32_t width, const uint32_t height, const Format format)
2188bf80f4bSopenharmony_ci{
2198bf80f4bSopenharmony_ci    return {
2208bf80f4bSopenharmony_ci        ImageType::CORE_IMAGE_TYPE_2D,          // imageType
2218bf80f4bSopenharmony_ci        ImageViewType::CORE_IMAGE_VIEW_TYPE_2D, // imageViewType
2228bf80f4bSopenharmony_ci        format,                                 // format
2238bf80f4bSopenharmony_ci        ImageTiling::CORE_IMAGE_TILING_OPTIMAL, // imageTiling
2248bf80f4bSopenharmony_ci        ImageUsageFlagBits::CORE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
2258bf80f4bSopenharmony_ci            ImageUsageFlagBits::CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
2268bf80f4bSopenharmony_ci            ImageUsageFlagBits::CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, // usageFlags
2278bf80f4bSopenharmony_ci        MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
2288bf80f4bSopenharmony_ci            MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT,    // memoryPropertyFlags
2298bf80f4bSopenharmony_ci        0,                                                                        // createFlags
2308bf80f4bSopenharmony_ci        EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS, // engineCreationFlags
2318bf80f4bSopenharmony_ci        width,                                                                    // width
2328bf80f4bSopenharmony_ci        height,                                                                   // height
2338bf80f4bSopenharmony_ci        1,                                                                        // depth
2348bf80f4bSopenharmony_ci        1,                                                                        // mipCount
2358bf80f4bSopenharmony_ci        1,                                                                        // layerCount
2368bf80f4bSopenharmony_ci        SampleCountFlagBits::CORE_SAMPLE_COUNT_1_BIT,                             // sampleCountFlags
2378bf80f4bSopenharmony_ci        {},                                                                       // componentMapping
2388bf80f4bSopenharmony_ci    };
2398bf80f4bSopenharmony_ci}
2408bf80f4bSopenharmony_ci} // namespace
2418bf80f4bSopenharmony_ci
2428bf80f4bSopenharmony_ciSwapchainVk::SwapchainVk(Device& device, const SwapchainCreateInfo& swapchainCreateInfo)
2438bf80f4bSopenharmony_ci    : device_(device), flags_(swapchainCreateInfo.swapchainFlags)
2448bf80f4bSopenharmony_ci{
2458bf80f4bSopenharmony_ci    const auto& devicePlatformData = (const DevicePlatformDataVk&)device_.GetPlatformData();
2468bf80f4bSopenharmony_ci    auto const physicalDevice = devicePlatformData.physicalDevice;
2478bf80f4bSopenharmony_ci    // check for surface creation automatically
2488bf80f4bSopenharmony_ci    if ((swapchainCreateInfo.surfaceHandle == 0) && swapchainCreateInfo.window.window) {
2498bf80f4bSopenharmony_ci        CreateFunctionsVk::Window win;
2508bf80f4bSopenharmony_ci        win.instance = swapchainCreateInfo.window.instance;
2518bf80f4bSopenharmony_ci        win.window = swapchainCreateInfo.window.window;
2528bf80f4bSopenharmony_ci        surface_ = CreateFunctionsVk::CreateSurface(devicePlatformData.instance, win);
2538bf80f4bSopenharmony_ci        ownsSurface_ = true;
2548bf80f4bSopenharmony_ci    } else {
2558bf80f4bSopenharmony_ci        surface_ = VulkanHandleCast<VkSurfaceKHR>(swapchainCreateInfo.surfaceHandle);
2568bf80f4bSopenharmony_ci    }
2578bf80f4bSopenharmony_ci
2588bf80f4bSopenharmony_ci    if (surface_ != VK_NULL_HANDLE) {
2598bf80f4bSopenharmony_ci        auto const vkDevice = devicePlatformData.device;
2608bf80f4bSopenharmony_ci
2618bf80f4bSopenharmony_ci        // Sanity check that the device can use the surface.
2628bf80f4bSopenharmony_ci        // NOTE: queuFamilyIndex hardcoded, should come via devicePlatformData?
2638bf80f4bSopenharmony_ci        VkBool32 surfaceSupported = VK_FALSE;
2648bf80f4bSopenharmony_ci        VALIDATE_VK_RESULT(vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, 0, surface_, &surfaceSupported));
2658bf80f4bSopenharmony_ci        PLUGIN_ASSERT_MSG(surfaceSupported != VK_FALSE, "physicalDevice doesn't support given surface");
2668bf80f4bSopenharmony_ci
2678bf80f4bSopenharmony_ci        const ColorInfo ci = GetColorInfo(physicalDevice, surface_, flags_);
2688bf80f4bSopenharmony_ci
2698bf80f4bSopenharmony_ci        const VkPresentModeKHR swapchainPresentMode = GetPresentMode(physicalDevice, surface_, flags_);
2708bf80f4bSopenharmony_ci        // Pick an extent, image count, and transform for the swapchain.
2718bf80f4bSopenharmony_ci        VkSurfaceCapabilitiesKHR surfaceCapabilities;
2728bf80f4bSopenharmony_ci        VALIDATE_VK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface_, &surfaceCapabilities));
2738bf80f4bSopenharmony_ci
2748bf80f4bSopenharmony_ci        // NOTE: how do we handle the special case of 0xffffffffff which means the extent should be defined by the
2758bf80f4bSopenharmony_ci        // swapchain?
2768bf80f4bSopenharmony_ci        VkExtent2D swapchainExtent = surfaceCapabilities.currentExtent;
2778bf80f4bSopenharmony_ci        ClampSwapchainExtent(surfaceCapabilities, swapchainExtent);
2788bf80f4bSopenharmony_ci        plat_.swapchainImages.width = swapchainExtent.width;
2798bf80f4bSopenharmony_ci        plat_.swapchainImages.height = swapchainExtent.height;
2808bf80f4bSopenharmony_ci
2818bf80f4bSopenharmony_ci        const DeviceConfiguration deviceConfig = device_.GetDeviceConfiguration();
2828bf80f4bSopenharmony_ci        // surfaceCapabilities.maxImageCount of zero means that there is no limit
2838bf80f4bSopenharmony_ci        const uint32_t imageCount =
2848bf80f4bSopenharmony_ci            (surfaceCapabilities.maxImageCount == 0)
2858bf80f4bSopenharmony_ci                ? (Math::max(surfaceCapabilities.minImageCount, deviceConfig.swapchainImageCount))
2868bf80f4bSopenharmony_ci                : (Math::min(surfaceCapabilities.maxImageCount,
2878bf80f4bSopenharmony_ci                    Math::max(surfaceCapabilities.minImageCount, deviceConfig.swapchainImageCount)));
2888bf80f4bSopenharmony_ci        PLUGIN_LOG_D("swapchainImageCount: %u", imageCount);
2898bf80f4bSopenharmony_ci
2908bf80f4bSopenharmony_ci        const VkSurfaceTransformFlagsKHR swapchainTransform =
2918bf80f4bSopenharmony_ci            (surfaceCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
2928bf80f4bSopenharmony_ci                ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
2938bf80f4bSopenharmony_ci                : surfaceCapabilities.currentTransform;
2948bf80f4bSopenharmony_ci
2958bf80f4bSopenharmony_ci        const VkImageUsageFlags desiredUsageFlags = static_cast<VkImageUsageFlags>(swapchainCreateInfo.imageUsageFlags);
2968bf80f4bSopenharmony_ci        const VkImageUsageFlags imageUsageFlags = desiredUsageFlags & surfaceCapabilities.supportedUsageFlags;
2978bf80f4bSopenharmony_ci        PLUGIN_LOG_D("swapchain usage flags, selected: %u, desired: %u, capabilities: %u", imageUsageFlags,
2988bf80f4bSopenharmony_ci            desiredUsageFlags, surfaceCapabilities.supportedUsageFlags);
2998bf80f4bSopenharmony_ci
3008bf80f4bSopenharmony_ci        VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
3018bf80f4bSopenharmony_ci        if (surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) {
3028bf80f4bSopenharmony_ci            compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
3038bf80f4bSopenharmony_ci        }
3048bf80f4bSopenharmony_ci
3058bf80f4bSopenharmony_ci        VkSwapchainCreateInfoKHR const vkSwapchainCreateInfo {
3068bf80f4bSopenharmony_ci            VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,       // sType
3078bf80f4bSopenharmony_ci            nullptr,                                           // pNext
3088bf80f4bSopenharmony_ci            0,                                                 // flags
3098bf80f4bSopenharmony_ci            surface_,                                          // surface
3108bf80f4bSopenharmony_ci            imageCount,                                        // minImageCount
3118bf80f4bSopenharmony_ci            ci.format,                                         // imageFormat
3128bf80f4bSopenharmony_ci            ci.colorSpace,                                     // imageColorSpace
3138bf80f4bSopenharmony_ci            swapchainExtent,                                   // imageExtent
3148bf80f4bSopenharmony_ci            1,                                                 // imageArrayLayers
3158bf80f4bSopenharmony_ci            imageUsageFlags,                                   // imageUsage
3168bf80f4bSopenharmony_ci            VK_SHARING_MODE_EXCLUSIVE,                         // imageSharingMode
3178bf80f4bSopenharmony_ci            0,                                                 // queueFamilyIndexCount
3188bf80f4bSopenharmony_ci            nullptr,                                           // pQueueFamilyIndices
3198bf80f4bSopenharmony_ci            (VkSurfaceTransformFlagBitsKHR)swapchainTransform, // preTransform
3208bf80f4bSopenharmony_ci            compositeAlpha,                                    // compositeAlpha
3218bf80f4bSopenharmony_ci            swapchainPresentMode,                              // presentMode
3228bf80f4bSopenharmony_ci            VK_TRUE,                                           // clipped
3238bf80f4bSopenharmony_ci            VK_NULL_HANDLE,                                    // oldSwapchain
3248bf80f4bSopenharmony_ci        };
3258bf80f4bSopenharmony_ci
3268bf80f4bSopenharmony_ci        VALIDATE_VK_RESULT(vkCreateSwapchainKHR(vkDevice, &vkSwapchainCreateInfo, nullptr, &plat_.swapchain));
3278bf80f4bSopenharmony_ci
3288bf80f4bSopenharmony_ci        {
3298bf80f4bSopenharmony_ci            uint32_t realImageCount = 0;
3308bf80f4bSopenharmony_ci            VALIDATE_VK_RESULT(vkGetSwapchainImagesKHR(vkDevice, // device
3318bf80f4bSopenharmony_ci                plat_.swapchain,                                 // swapchain
3328bf80f4bSopenharmony_ci                &realImageCount,                                 // pSwapchainImageCount
3338bf80f4bSopenharmony_ci                nullptr));                                       // pSwapchainImages
3348bf80f4bSopenharmony_ci
3358bf80f4bSopenharmony_ci            PLUGIN_LOG_D("swapchain realImageCount: %u", realImageCount);
3368bf80f4bSopenharmony_ci
3378bf80f4bSopenharmony_ci            plat_.swapchainImages.images.resize(realImageCount);
3388bf80f4bSopenharmony_ci            plat_.swapchainImages.imageViews.resize(realImageCount);
3398bf80f4bSopenharmony_ci            plat_.swapchainImages.semaphores.resize(realImageCount);
3408bf80f4bSopenharmony_ci
3418bf80f4bSopenharmony_ci            VALIDATE_VK_RESULT(vkGetSwapchainImagesKHR(vkDevice, // device
3428bf80f4bSopenharmony_ci                plat_.swapchain,                                 // swapchain
3438bf80f4bSopenharmony_ci                &realImageCount,                                 // pSwapchainImageCount
3448bf80f4bSopenharmony_ci                plat_.swapchainImages.images.data()));           // pSwapchainImages
3458bf80f4bSopenharmony_ci
3468bf80f4bSopenharmony_ci            constexpr VkComponentMapping componentMapping {
3478bf80f4bSopenharmony_ci                VK_COMPONENT_SWIZZLE_IDENTITY, // r
3488bf80f4bSopenharmony_ci                VK_COMPONENT_SWIZZLE_IDENTITY, // g
3498bf80f4bSopenharmony_ci                VK_COMPONENT_SWIZZLE_IDENTITY, // b
3508bf80f4bSopenharmony_ci                VK_COMPONENT_SWIZZLE_IDENTITY, // a
3518bf80f4bSopenharmony_ci            };
3528bf80f4bSopenharmony_ci            constexpr VkImageSubresourceRange imageSubresourceRange {
3538bf80f4bSopenharmony_ci                VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
3548bf80f4bSopenharmony_ci                0,                         // baseMipLevel
3558bf80f4bSopenharmony_ci                1,                         // levelCount
3568bf80f4bSopenharmony_ci                0,                         // baseArrayLayer
3578bf80f4bSopenharmony_ci                1,                         // layerCount
3588bf80f4bSopenharmony_ci            };
3598bf80f4bSopenharmony_ci
3608bf80f4bSopenharmony_ci            constexpr VkSemaphoreCreateFlags semaphoreCreateFlags { 0 };
3618bf80f4bSopenharmony_ci            const VkSemaphoreCreateInfo semaphoreCreateInfo {
3628bf80f4bSopenharmony_ci                VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, // sType
3638bf80f4bSopenharmony_ci                nullptr,                                 // pNext
3648bf80f4bSopenharmony_ci                semaphoreCreateFlags,                    // flags
3658bf80f4bSopenharmony_ci            };
3668bf80f4bSopenharmony_ci            for (uint32_t idx = 0; idx < plat_.swapchainImages.imageViews.size(); ++idx) {
3678bf80f4bSopenharmony_ci                const VkImageViewCreateInfo imageViewCreateInfo {
3688bf80f4bSopenharmony_ci                    VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // sType
3698bf80f4bSopenharmony_ci                    nullptr,                                  // pNext
3708bf80f4bSopenharmony_ci                    0,                                        // flags
3718bf80f4bSopenharmony_ci                    plat_.swapchainImages.images[idx],        // image
3728bf80f4bSopenharmony_ci                    VK_IMAGE_VIEW_TYPE_2D,                    // viewType
3738bf80f4bSopenharmony_ci                    ci.format,                                // format
3748bf80f4bSopenharmony_ci                    componentMapping,                         // components
3758bf80f4bSopenharmony_ci                    imageSubresourceRange                     // subresourceRange;
3768bf80f4bSopenharmony_ci                };
3778bf80f4bSopenharmony_ci                VALIDATE_VK_RESULT(vkCreateImageView(vkDevice, // device
3788bf80f4bSopenharmony_ci                    &imageViewCreateInfo,                      // pCreateInfo
3798bf80f4bSopenharmony_ci                    nullptr,                                   // pAllocator
3808bf80f4bSopenharmony_ci                    &plat_.swapchainImages.imageViews[idx]));  // pView
3818bf80f4bSopenharmony_ci                VALIDATE_VK_RESULT(vkCreateSemaphore(vkDevice, // device
3828bf80f4bSopenharmony_ci                    &semaphoreCreateInfo,                      // pCreateInfo
3838bf80f4bSopenharmony_ci                    nullptr,                                   // pAllocator
3848bf80f4bSopenharmony_ci                    &plat_.swapchainImages.semaphores[idx]));  // pSemaphore
3858bf80f4bSopenharmony_ci            }
3868bf80f4bSopenharmony_ci        }
3878bf80f4bSopenharmony_ci
3888bf80f4bSopenharmony_ci        desc_ = GetColorDesc(plat_.swapchainImages.width, plat_.swapchainImages.height, (Format)ci.format,
3898bf80f4bSopenharmony_ci            (ImageUsageFlags)imageUsageFlags);
3908bf80f4bSopenharmony_ci
3918bf80f4bSopenharmony_ci        if (flags_ & 0x2) {
3928bf80f4bSopenharmony_ci            const Format depthFormat = GetValidDepthFormat((const DeviceVk&)device_);
3938bf80f4bSopenharmony_ci            descDepthBuffer_ = GetDepthDesc(plat_.swapchainImages.width, plat_.swapchainImages.height, depthFormat);
3948bf80f4bSopenharmony_ci        }
3958bf80f4bSopenharmony_ci    } else {
3968bf80f4bSopenharmony_ci        PLUGIN_LOG_E("Invalid surface in swapchain creation");
3978bf80f4bSopenharmony_ci    }
3988bf80f4bSopenharmony_ci}
3998bf80f4bSopenharmony_ci
4008bf80f4bSopenharmony_ciSwapchainVk::~SwapchainVk()
4018bf80f4bSopenharmony_ci{
4028bf80f4bSopenharmony_ci    const auto& devicePlatformData = (const DevicePlatformDataVk&)device_.GetPlatformData();
4038bf80f4bSopenharmony_ci    const VkDevice device = devicePlatformData.device;
4048bf80f4bSopenharmony_ci    for (auto const imageView : plat_.swapchainImages.imageViews) {
4058bf80f4bSopenharmony_ci        if (imageView) {
4068bf80f4bSopenharmony_ci            vkDestroyImageView(device, // device
4078bf80f4bSopenharmony_ci                imageView,             // imageView
4088bf80f4bSopenharmony_ci                nullptr);              // pAllocator
4098bf80f4bSopenharmony_ci        }
4108bf80f4bSopenharmony_ci    }
4118bf80f4bSopenharmony_ci    for (const auto semaphore : plat_.swapchainImages.semaphores) {
4128bf80f4bSopenharmony_ci        if (semaphore) {
4138bf80f4bSopenharmony_ci            vkDestroySemaphore(device, // device
4148bf80f4bSopenharmony_ci                semaphore,             // semaphore
4158bf80f4bSopenharmony_ci                nullptr);              // pAllocator
4168bf80f4bSopenharmony_ci        }
4178bf80f4bSopenharmony_ci    }
4188bf80f4bSopenharmony_ci
4198bf80f4bSopenharmony_ci    CreateFunctionsVk::DestroySwapchain(device, plat_.swapchain);
4208bf80f4bSopenharmony_ci    if (ownsSurface_ && surface_) {
4218bf80f4bSopenharmony_ci        CreateFunctionsVk::DestroySurface(devicePlatformData.instance, surface_);
4228bf80f4bSopenharmony_ci    }
4238bf80f4bSopenharmony_ci}
4248bf80f4bSopenharmony_ci
4258bf80f4bSopenharmony_ciconst SwapchainPlatformDataVk& SwapchainVk::GetPlatformData() const
4268bf80f4bSopenharmony_ci{
4278bf80f4bSopenharmony_ci    return plat_;
4288bf80f4bSopenharmony_ci}
4298bf80f4bSopenharmony_ci
4308bf80f4bSopenharmony_ciconst GpuImageDesc& SwapchainVk::GetDesc() const
4318bf80f4bSopenharmony_ci{
4328bf80f4bSopenharmony_ci    return desc_;
4338bf80f4bSopenharmony_ci}
4348bf80f4bSopenharmony_ci
4358bf80f4bSopenharmony_ciconst GpuImageDesc& SwapchainVk::GetDescDepthBuffer() const
4368bf80f4bSopenharmony_ci{
4378bf80f4bSopenharmony_ci    return descDepthBuffer_;
4388bf80f4bSopenharmony_ci}
4398bf80f4bSopenharmony_ci
4408bf80f4bSopenharmony_ciuint32_t SwapchainVk::GetFlags() const
4418bf80f4bSopenharmony_ci{
4428bf80f4bSopenharmony_ci    return flags_;
4438bf80f4bSopenharmony_ci}
4448bf80f4bSopenharmony_ci
4458bf80f4bSopenharmony_ciSurfaceTransformFlags SwapchainVk::GetSurfaceTransformFlags() const
4468bf80f4bSopenharmony_ci{
4478bf80f4bSopenharmony_ci    return surfaceTransformFlags_;
4488bf80f4bSopenharmony_ci}
4498bf80f4bSopenharmony_ci
4508bf80f4bSopenharmony_ciuint64_t SwapchainVk::GetSurfaceHandle() const
4518bf80f4bSopenharmony_ci{
4528bf80f4bSopenharmony_ci    return VulkanHandleCast<uint64_t>(surface_);
4538bf80f4bSopenharmony_ci}
4548bf80f4bSopenharmony_ci
4558bf80f4bSopenharmony_ciuint32_t SwapchainVk::GetNextAcquireSwapchainSemaphoreIndex() const
4568bf80f4bSopenharmony_ci{
4578bf80f4bSopenharmony_ci    currSemaphoreIdx_ = (currSemaphoreIdx_ + 1) % plat_.swapchainImages.semaphores.size();
4588bf80f4bSopenharmony_ci    return currSemaphoreIdx_;
4598bf80f4bSopenharmony_ci}
4608bf80f4bSopenharmony_ciRENDER_END_NAMESPACE()
461