diff --git a/.github/workflows/libretro.yml b/.github/workflows/libretro.yml index 4d451a93..49c3b1a8 100644 --- a/.github/workflows/libretro.yml +++ b/.github/workflows/libretro.yml @@ -3,9 +3,10 @@ name: Build libretro core # Builds parallel_gx2_libretro with the Makefile for every target the core # currently supports and uploads each result as an artifact, so a release # can be assembled from them. The dependencies are submodules built with -# the core, so a job installs no libraries beyond the system's X11 headers -# on Linux (Vulkan's surface declarations). Also runs the network layer's -# and the OpenGL GPU cothread's tests. +# the core, so a job installs no libraries at all - not even GL or X11 +# headers, as the context, surface and swapchain are the frontend's. Also +# runs the network layer's, the OpenGL GPU cothread's and the thread +# handoffs' tests. on: push: @@ -46,11 +47,10 @@ jobs: fetch-depth: 0 - name: "Install dependencies" - # the headers Vulkan's X11 surfaces are declared with + # the compiler, and nothing else run: | sudo apt update -qq - sudo apt install -y --no-install-recommends g++-12 \ - libx11-dev libxrender-dev libx11-xcb-dev + sudo apt install -y --no-install-recommends g++-12 - name: "Build" run: make -j $(nproc) CC=gcc-12 CXX=g++-12 @@ -74,6 +74,11 @@ jobs: - name: "Checkout repo" uses: actions/checkout@v6 + - name: "No window-system code" + # no X11/XCB/Wayland/GL/EGL header, VK_USE_PLATFORM_* or surface entry + # point in the core: the frontend owns the context and the surface + run: ./tools/check_no_wsi.sh + - name: "curl_compat against local servers" # plain, then under AddressSanitizer + UBSan, then ThreadSanitizer run: | diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 66a8312d..d3c4abc9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -62,20 +62,14 @@ stages: ############################################################################## # ################################### DESKTOPS ################################# -# Linux wants the X11 and XCB headers Vulkan's surfaces are declared with -# (OpenGL needs none: the context is the frontend's); a job defining -# before_script replaces the template's, so its one line is repeated here. +# Linux needs no system headers beyond the compiler's: the GL context and the +# Vulkan surface and swapchain are the frontend's. .core-defs-linux: extends: .core-defs variables: # Cemu is C++20 CC: gcc-12 CXX: g++-12 - before_script: - - export NUMPROC=$(($(nproc)/5)) - - apt-get update -qq - - apt-get install -y --no-install-recommends - libx11-dev libxrender-dev libx11-xcb-dev # Windows 64-bit libretro-build-windows-x64: @@ -204,3 +198,14 @@ test-handoff: - SANITIZER=thread ./test/handoff/run_tests.sh - make -C test/handoff clean - SANITIZER=address,undefined ./test/handoff/run_tests.sh + +# No X11/XCB/Wayland/GL/EGL header, VK_USE_PLATFORM_* or platform surface +# entry point in the core: the frontend owns the context and the surface. +test-no-wsi: + stage: test + image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-amd64-ubuntu:backports + needs: [] + variables: + GIT_SUBMODULE_STRATEGY: none + script: + - ./tools/check_no_wsi.sh diff --git a/.upstream-excluded b/.upstream-excluded index 211c38ed..0957cc06 100644 --- a/.upstream-excluded +++ b/.upstream-excluded @@ -50,3 +50,7 @@ src/util/helpers/ZipArchive.h src/Common/GLInclude/egl.h src/Common/GLInclude/glxext.h src/Common/GLInclude/wglext.h +src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp +src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h +src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.mm +src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h diff --git a/CHANGELOG.md b/CHANGELOG.md index afd8a4c0..4f1d5803 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ What changed in the libretro core of this fork, newest first. Changes that come ## Unreleased +- Vulkan goes through the frontend alone too: the core's own instance and device setup, its X11, XCB, Wayland, Win32 and Metal surfaces, its swapchain and the Breath of the Wild child-process workaround that came with them are gone - none of it ever ran under libretro, where the device, surface and swapchain are RetroArch's. Linux builds no longer need X11 headers, or any headers beyond the compiler's. - Boot no longer waits up to three seconds for the CPU timer's calibration: a first estimate is ready 25 ms after the core loads and is refined in the background. - The recompiler starts on a newly run function at once instead of at its next 10 ms look, so hot code spends less time interpreted, and its work queue no longer allocates. - Vulkan: loading the pipeline cache no longer pauses 10 ms whenever 50 pipelines are queued, which held a warm cache to 5,000 pipelines a second; closing content no longer waits up to 250 ms for the pipeline cache's save thread. diff --git a/Makefile b/Makefile index 4909df1e..42b4b9a4 100644 --- a/Makefile +++ b/Makefile @@ -187,7 +187,7 @@ else ifneq (,$(filter android-arm64 android-x86_64,$(platform))) CC = $(ANDROID_NDK_LLVM)/bin/clang --target=$(ANDROID_TRIPLE) CXX = $(ANDROID_NDK_LLVM)/bin/clang++ --target=$(ANDROID_TRIPLE) AR = $(ANDROID_NDK_LLVM)/bin/llvm-ar - FLAGS += -DANDROID -DVK_USE_PLATFORM_ANDROID_KHR -funwind-tables + FLAGS += -DANDROID -funwind-tables LDFLAGS += -static-libstdc++ LIBS += -landroid -llog -lm -ldl diff --git a/Makefile.common b/Makefile.common index 3835fc27..1360d014 100644 --- a/Makefile.common +++ b/Makefile.common @@ -447,7 +447,6 @@ ifeq ($(HAVE_VULKAN),1) $(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureViewVk.cpp \ $(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp \ $(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp \ - $(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp \ $(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp \ $(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/VKRMemoryManager.cpp \ $(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp \ @@ -479,8 +478,7 @@ ifeq ($(HAVE_METAL),1) $(SRC_DIR)/imgui/imgui_impl_metal.mm ifeq ($(OS_MACOS),1) CORE_SOURCES_OBJCXX += \ - $(SRC_DIR)/Cafe/HW/Latte/Renderer/MetalView.mm \ - $(SRC_DIR)/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.mm + $(SRC_DIR)/Cafe/HW/Latte/Renderer/MetalView.mm endif endif ifeq ($(HAVE_LIBUSB),1) @@ -516,15 +514,8 @@ endif ifeq ($(HAVE_METAL),1) CORE_FLAGS += -DENABLE_METAL -DIMGUI_IMPL_METAL_CPP -I$(DEP_DIR)/metal-cpp endif -ifeq ($(OS_APPLE),1) - CORE_FLAGS += -DVK_USE_PLATFORM_MACOS_MVK -DVK_USE_PLATFORM_METAL_EXT -endif -# desktop Linux: the X11 surfaces (Xlib and XCB headers); webOS has none -ifeq ($(OS_LINUX),1) -ifneq ($(platform),webos) - CORE_FLAGS += -DVK_USE_PLATFORM_XLIB_KHR -DVK_USE_PLATFORM_XCB_KHR -endif -endif +# No VK_USE_PLATFORM_*: the surface and the swapchain are the frontend's, so +# no window-system header is ever included ifeq ($(HAVE_LIBUSB),1) CORE_FLAGS += -DHAS_LIBUSB endif diff --git a/README.md b/README.md index 3630fe45..f348603c 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,12 @@ Build it yourself for anything else, or to test a change. A static Makefile builds the core and everything it uses: the libraries are submodules built with it, and the network layer is libretro-common's (HTTP and TLS, with no libcurl, OpenSSL or mbedTLS), so nothing but a C++20 -compiler and, on Linux, the system's X11 headers (for Vulkan's surface -declarations) is needed. +compiler is needed - no GL, X11 or other window-system headers either, as the +graphics context, the surface and the swapchain are all the frontend's. ```bash -# Ubuntu/Debian: the headers Vulkan's X11 surfaces are declared with -sudo apt install -y g++ make libx11-dev libxrender-dev libx11-xcb-dev +# Ubuntu/Debian +sudo apt install -y g++ make # Clone with submodules - the libraries and the community graphic packs live # in them. On an existing clone: git submodule update --init --recursive diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h b/src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h deleted file mode 100644 index b2c0db13..00000000 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#if BOOST_OS_MACOS - -#include - -VkSurfaceKHR CreateCocoaSurface(VkInstance instance, void* handle); - -#endif diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.mm b/src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.mm deleted file mode 100644 index a68174c9..00000000 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.mm +++ /dev/null @@ -1,31 +0,0 @@ -#include "Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h" -#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" - -#include "Cafe/HW/Latte/Renderer/MetalView.h" - -VkSurfaceKHR CreateCocoaSurface(VkInstance instance, void* handle) -{ - NSView* view = (NSView*)handle; - - MetalView* childView = [[MetalView alloc] initWithFrame:view.bounds]; - childView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; - childView.wantsLayer = YES; - - [view addSubview:childView]; - - VkMetalSurfaceCreateInfoEXT surface; - surface.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; - surface.pNext = NULL; - surface.flags = 0; - surface.pLayer = (CAMetalLayer*)childView.layer; - - VkSurfaceKHR result; - VkResult err; - if ((err = vkCreateMetalSurfaceEXT(instance, &surface, nullptr, &result)) != VK_SUCCESS) - { - cemuLog_log(LogType::Force, "Cannot create a Metal Vulkan surface: {}", (sint32)err); - throw std::runtime_error(fmt::format("Cannot create a Metal Vulkan surface: {}", err)); - } - - return result; -} diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp deleted file mode 100644 index b27aae0e..00000000 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp +++ /dev/null @@ -1,402 +0,0 @@ -#include "SwapchainInfoVk.h" - -#include "config/CemuConfig.h" -#include "WindowSystem.h" -#include "Cafe/HW/Latte/Core/Latte.h" -#include "Cafe/HW/Latte/Core/LatteTiming.h" -#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" -#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h" - -SwapchainInfoVk::SwapchainInfoVk(bool mainWindow, Vector2i size) : mainWindow(mainWindow), m_desiredExtent(size) -{ - auto& windowHandleInfo = mainWindow ? WindowSystem::GetWindowInfo().canvas_main : WindowSystem::GetWindowInfo().canvas_pad; - auto renderer = VulkanRenderer::GetInstance(); - m_instance = renderer->GetVkInstance(); - m_logicalDevice = renderer->GetLogicalDevice(); - m_physicalDevice = renderer->GetPhysicalDevice(); - - m_surface = renderer->CreateFramebufferSurface(m_instance, windowHandleInfo); -} - - -SwapchainInfoVk::~SwapchainInfoVk() -{ - Cleanup(); - if(m_surface != VK_NULL_HANDLE) - vkDestroySurfaceKHR(m_instance, m_surface, nullptr); -} - -void SwapchainInfoVk::Create() -{ - const auto details = QuerySwapchainSupport(m_surface, m_physicalDevice); - m_surfaceFormat = ChooseSurfaceFormat(details.formats); - m_actualExtent = ChooseSwapExtent(details.capabilities); - - // use at least two swapchain images. fewer than that causes problems on some drivers - uint32_t image_count = std::max(2u, details.capabilities.minImageCount); - if(details.capabilities.maxImageCount > 0) - image_count = std::min(image_count, details.capabilities.maxImageCount); - if(image_count < 2) - cemuLog_log(LogType::Force, "Vulkan: Swapchain image count less than 2 may cause problems"); - - VkSwapchainCreateInfoKHR create_info = CreateSwapchainCreateInfo(m_surface, details, m_surfaceFormat, image_count, m_actualExtent); - create_info.oldSwapchain = nullptr; - create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - - VkResult result = vkCreateSwapchainKHR(m_logicalDevice, &create_info, nullptr, &m_swapchain); - if (result != VK_SUCCESS) - UnrecoverableError("Error attempting to create a swapchain"); - - result = vkGetSwapchainImagesKHR(m_logicalDevice, m_swapchain, &image_count, nullptr); - if (result != VK_SUCCESS) - UnrecoverableError("Error attempting to retrieve the count of swapchain images"); - - - m_swapchainImages.resize(image_count); - result = vkGetSwapchainImagesKHR(m_logicalDevice, m_swapchain, &image_count, m_swapchainImages.data()); - if (result != VK_SUCCESS) - UnrecoverableError("Error attempting to retrieve swapchain images"); - // create default renderpass - VkAttachmentDescription colorAttachment = {}; - colorAttachment.format = m_surfaceFormat.format; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentReference colorAttachmentRef = {}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - - VkRenderPassCreateInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = 1; - renderPassInfo.pAttachments = &colorAttachment; - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - result = vkCreateRenderPass(m_logicalDevice, &renderPassInfo, nullptr, &m_swapchainRenderPass); - if (result != VK_SUCCESS) - UnrecoverableError("Failed to create renderpass for swapchain"); - - // create swapchain image views - m_swapchainImageViews.resize(m_swapchainImages.size()); - for (sint32 i = 0; i < m_swapchainImages.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_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; - result = vkCreateImageView(m_logicalDevice, &createInfo, nullptr, &m_swapchainImageViews[i]); - if (result != VK_SUCCESS) - UnrecoverableError("Failed to create imageviews for swapchain"); - } - - // create swapchain framebuffers - m_swapchainFramebuffers.resize(m_swapchainImages.size()); - for (size_t i = 0; i < m_swapchainImages.size(); i++) - { - VkImageView attachments[1]; - attachments[0] = m_swapchainImageViews[i]; - // create framebuffer - VkFramebufferCreateInfo framebufferInfo = {}; - framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.renderPass = m_swapchainRenderPass; - framebufferInfo.attachmentCount = 1; - framebufferInfo.pAttachments = attachments; - framebufferInfo.width = m_actualExtent.width; - framebufferInfo.height = m_actualExtent.height; - framebufferInfo.layers = 1; - result = vkCreateFramebuffer(m_logicalDevice, &framebufferInfo, nullptr, &m_swapchainFramebuffers[i]); - if (result != VK_SUCCESS) - UnrecoverableError("Failed to create framebuffer for swapchain"); - } - - m_presentSemaphores.resize(m_swapchainImages.size()); - // create present semaphores - VkSemaphoreCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - for (auto& semaphore : m_presentSemaphores){ - if (vkCreateSemaphore(m_logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS) - UnrecoverableError("Failed to create semaphore for swapchain present"); - } - - m_acquireSemaphores.resize(m_swapchainImages.size()); - // create acquire semaphores - info = {}; - info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - for (auto& semaphore : m_acquireSemaphores){ - if (vkCreateSemaphore(m_logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS) - UnrecoverableError("Failed to create semaphore for swapchain acquire"); - } - - VkFenceCreateInfo fenceInfo = {}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - result = vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &m_imageAvailableFence); - if (result != VK_SUCCESS) - UnrecoverableError("Failed to create fence for swapchain"); - - m_acquireIndex = 0; - hasDefinedSwapchainImage = false; - - m_queueDepth = 0; -} - -void SwapchainInfoVk::Cleanup() -{ - m_swapchainImages.clear(); - - for (auto& sem: m_acquireSemaphores) - vkDestroySemaphore(m_logicalDevice, sem, nullptr); - m_acquireSemaphores.clear(); - - for (auto& sem: m_presentSemaphores) - vkDestroySemaphore(m_logicalDevice, sem, nullptr); - m_presentSemaphores.clear(); - - if (m_swapchainRenderPass) - { - vkDestroyRenderPass(m_logicalDevice, m_swapchainRenderPass, nullptr); - m_swapchainRenderPass = nullptr; - } - - for (auto& imageView : m_swapchainImageViews) - vkDestroyImageView(m_logicalDevice, imageView, nullptr); - m_swapchainImageViews.clear(); - - for (auto& framebuffer : m_swapchainFramebuffers) - vkDestroyFramebuffer(m_logicalDevice, framebuffer, nullptr); - m_swapchainFramebuffers.clear(); - - - if (m_imageAvailableFence) - { - WaitAvailableFence(); - vkDestroyFence(m_logicalDevice, m_imageAvailableFence, nullptr); - m_imageAvailableFence = nullptr; - } - if (m_swapchain) - { - vkDestroySwapchainKHR(m_logicalDevice, m_swapchain, nullptr); - m_swapchain = VK_NULL_HANDLE; - } -} - -bool SwapchainInfoVk::IsValid() const -{ - return m_swapchain && !m_acquireSemaphores.empty(); -} - -void SwapchainInfoVk::WaitAvailableFence() -{ - if(m_awaitableFence != VK_NULL_HANDLE) - vkWaitForFences(m_logicalDevice, 1, &m_awaitableFence, VK_TRUE, UINT64_MAX); - m_awaitableFence = VK_NULL_HANDLE; -} - -void SwapchainInfoVk::ResetAvailableFence() const -{ - vkResetFences(m_logicalDevice, 1, &m_imageAvailableFence); -} - -VkSemaphore SwapchainInfoVk::ConsumeAcquireSemaphore() -{ - VkSemaphore ret = m_currentSemaphore; - m_currentSemaphore = VK_NULL_HANDLE; - return ret; -} - -bool SwapchainInfoVk::AcquireImage() -{ - ResetAvailableFence(); - - VkSemaphore acquireSemaphore = m_acquireSemaphores[m_acquireIndex]; - VkResult result = vkAcquireNextImageKHR(m_logicalDevice, m_swapchain, 1'000'000'000, acquireSemaphore, m_imageAvailableFence, &swapchainImageIndex); - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) - m_shouldRecreate = true; - if (result == VK_TIMEOUT) - { - swapchainImageIndex = -1; - return false; - } - - if (result < 0) - { - swapchainImageIndex = -1; - if (result != VK_ERROR_OUT_OF_DATE_KHR) - throw std::runtime_error(fmt::format("Failed to acquire next image: {}", result)); - return false; - } - m_currentSemaphore = acquireSemaphore; - m_awaitableFence = m_imageAvailableFence; - m_acquireIndex = (m_acquireIndex + 1) % m_swapchainImages.size(); - - return true; -} - -void SwapchainInfoVk::UnrecoverableError(const char* errMsg) -{ - cemuLog_log(LogType::Force, "Unrecoverable error in Vulkan swapchain"); - cemuLog_log(LogType::Force, "Msg: {}", errMsg); - throw std::runtime_error(errMsg); -} - - -SwapchainInfoVk::SwapchainSupportDetails SwapchainInfoVk::QuerySwapchainSupport(VkSurfaceKHR surface, const VkPhysicalDevice& device) -{ - SwapchainSupportDetails details; - - VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); - if (result != VK_SUCCESS) - { - if (result != VK_ERROR_SURFACE_LOST_KHR) - cemuLog_log(LogType::Force, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed. Error {}", (sint32)result); - throw std::runtime_error(fmt::format("Unable to retrieve physical device surface capabilities: {}", result)); - } - - uint32_t formatCount = 0; - result = vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); - if (result != VK_SUCCESS) - { - cemuLog_log(LogType::Force, "vkGetPhysicalDeviceSurfaceFormatsKHR failed. Error {}", (sint32)result); - throw std::runtime_error(fmt::format("Unable to retrieve the number of formats for a surface on a physical device: {}", result)); - } - - if (formatCount != 0) - { - details.formats.resize(formatCount); - result = vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); - if (result != VK_SUCCESS) - { - cemuLog_log(LogType::Force, "vkGetPhysicalDeviceSurfaceFormatsKHR failed. Error {}", (sint32)result); - throw std::runtime_error(fmt::format("Unable to retrieve the formats for a surface on a physical device: {}", result)); - } - } - - uint32_t presentModeCount = 0; - result = vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); - if (result != VK_SUCCESS) - { - cemuLog_log(LogType::Force, "vkGetPhysicalDeviceSurfacePresentModesKHR failed. Error {}", (sint32)result); - throw std::runtime_error(fmt::format("Unable to retrieve the count of present modes for a surface on a physical device: {}", result)); - } - - if (presentModeCount != 0) - { - details.presentModes.resize(presentModeCount); - result = vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); - if (result != VK_SUCCESS) - { - cemuLog_log(LogType::Force, "vkGetPhysicalDeviceSurfacePresentModesKHR failed. Error {}", (sint32)result); - throw std::runtime_error(fmt::format("Unable to retrieve the present modes for a surface on a physical device: {}", result)); - } - } - - return details; -} - -VkSurfaceFormatKHR SwapchainInfoVk::ChooseSurfaceFormat(const std::vector& formats) const -{ - if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED) - return{ VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; - - for (const auto& format : formats) - { - if (format.format == VK_FORMAT_B8G8R8A8_UNORM && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) - return format; - } - - return formats[0]; -} - -VkExtent2D SwapchainInfoVk::ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) const -{ - if (capabilities.currentExtent.width != std::numeric_limits::max()) - return capabilities.currentExtent; - - VkExtent2D actualExtent = { (uint32)m_desiredExtent.x, (uint32)m_desiredExtent.y }; - actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); - actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); - return actualExtent; -} - -VkPresentModeKHR SwapchainInfoVk::ChoosePresentMode(const std::vector& modes) -{ - m_maxQueued = 0; - const auto vsyncState = (VSync)GetConfig().vsync.GetValue(); - if (vsyncState == VSync::MAILBOX) - { - if (std::find(modes.cbegin(), modes.cend(), VK_PRESENT_MODE_MAILBOX_KHR) != modes.cend()) - return VK_PRESENT_MODE_MAILBOX_KHR; - - cemuLog_log(LogType::Force, "Vulkan: Can't find mailbox present mode"); - } - else if (vsyncState == VSync::Immediate) - { - if (std::find(modes.cbegin(), modes.cend(), VK_PRESENT_MODE_IMMEDIATE_KHR) != modes.cend()) - return VK_PRESENT_MODE_IMMEDIATE_KHR; - - cemuLog_log(LogType::Force, "Vulkan: Can't find immediate present mode"); - } - else if (vsyncState == VSync::SYNC_AND_LIMIT) - { - LatteTiming_EnableHostDrivenVSync(); - // use immediate mode if available, other wise fall back to - //if (std::find(modes.cbegin(), modes.cend(), VK_PRESENT_MODE_IMMEDIATE_KHR) != modes.cend()) - // return VK_PRESENT_MODE_IMMEDIATE_KHR; - //else - // cemuLog_log(LogType::Force, "Vulkan: Present mode 'immediate' not available. Vsync might not behave as intended"); - return VK_PRESENT_MODE_FIFO_KHR; - } - - m_maxQueued = 1; - return VK_PRESENT_MODE_FIFO_KHR; -} - -VkSwapchainCreateInfoKHR SwapchainInfoVk::CreateSwapchainCreateInfo(VkSurfaceKHR surface, const SwapchainSupportDetails& swapchainSupport, const VkSurfaceFormatKHR& surfaceFormat, uint32 imageCount, const VkExtent2D& extent) -{ - VkSwapchainCreateInfoKHR createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - createInfo.surface = surface; - createInfo.minImageCount = imageCount; - createInfo.imageFormat = surfaceFormat.format; - createInfo.imageExtent = extent; - createInfo.imageArrayLayers = 1; - createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - - const VulkanRenderer::QueueFamilyIndices indices = VulkanRenderer::GetInstance()->FindQueueFamilies(surface, m_physicalDevice); - m_swapchainQueueFamilyIndices = { (uint32)indices.graphicsFamily, (uint32)indices.presentFamily }; - if (indices.graphicsFamily != indices.presentFamily) - { - createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - createInfo.queueFamilyIndexCount = m_swapchainQueueFamilyIndices.size(); - createInfo.pQueueFamilyIndices = m_swapchainQueueFamilyIndices.data(); - } - else - createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - - createInfo.preTransform = swapchainSupport.capabilities.currentTransform; - createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - createInfo.presentMode = ChoosePresentMode(swapchainSupport.presentModes); - createInfo.clipped = VK_TRUE; - - cemuLog_logDebug(LogType::Force, "vulkan presentation mode: {}", createInfo.presentMode); - return createInfo; -} diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h deleted file mode 100644 index 9f1199e6..00000000 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include "util/math/vector2.h" -#include - -struct SwapchainInfoVk -{ - enum class VSync - { - // values here must match GeneralSettings2::m_vsync - Immediate = 0, - FIFO = 1, - MAILBOX = 2, - SYNC_AND_LIMIT = 3, // synchronize emulated vsync events to monitor vsync. But skip events if rate higher than virtual vsync period - }; - - struct SwapchainSupportDetails - { - VkSurfaceCapabilitiesKHR capabilities; - std::vector formats; - std::vector presentModes; - }; - - void Cleanup(); - void Create(); - - bool IsValid() const; - - void WaitAvailableFence(); - void ResetAvailableFence() const; - - bool AcquireImage(); - // retrieve semaphore of last acquire for submitting a wait operation - // only one wait operation must be submitted per acquire (which submits a single signal operation) - // therefore subsequent calls will return a NULL handle - VkSemaphore ConsumeAcquireSemaphore(); - - static void UnrecoverableError(const char* errMsg); - - static SwapchainSupportDetails QuerySwapchainSupport(VkSurfaceKHR surface, const VkPhysicalDevice& device); - - VkPresentModeKHR ChoosePresentMode(const std::vector& modes); - VkSurfaceFormatKHR ChooseSurfaceFormat(const std::vector& formats) const; - VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) const; - - VkSwapchainCreateInfoKHR CreateSwapchainCreateInfo(VkSurfaceKHR surface, const SwapchainSupportDetails& swapchainSupport, const VkSurfaceFormatKHR& surfaceFormat, uint32 imageCount, const VkExtent2D& extent); - - - VkExtent2D getExtent() const - { - return m_actualExtent; - } - - SwapchainInfoVk(bool mainWindow, Vector2i size); - SwapchainInfoVk(const SwapchainInfoVk&) = delete; - SwapchainInfoVk(SwapchainInfoVk&&) noexcept = default; - ~SwapchainInfoVk(); - - bool mainWindow{}; - - bool m_shouldRecreate = false; - VSync m_vsyncState = VSync::Immediate; - bool hasDefinedSwapchainImage{}; // indicates if the swapchain image is in a defined state - - VkInstance m_instance{}; - VkPhysicalDevice m_physicalDevice{}; - VkDevice m_logicalDevice{}; - VkSurfaceKHR m_surface{}; - VkSurfaceFormatKHR m_surfaceFormat{}; - VkSwapchainKHR m_swapchain{}; - Vector2i m_desiredExtent{}; - VkExtent2D m_actualExtent{}; - uint32 swapchainImageIndex = (uint32)-1; - uint64 m_presentId = 1; - uint64 m_queueDepth = 0; // number of frames with pending presentation requests - uint64 m_maxQueued = 0; // the maximum number of frames with presentation requests. - - - // swapchain image ringbuffer (indexed by swapchainImageIndex) - std::vector m_swapchainImages; - std::vector m_swapchainImageViews; - std::vector m_swapchainFramebuffers; - std::vector m_presentSemaphores; // indexed by swapchainImageIndex - - VkRenderPass m_swapchainRenderPass = nullptr; - -private: - uint32 m_acquireIndex = 0; - std::vector m_acquireSemaphores; // indexed by m_acquireIndex - VkFence m_imageAvailableFence{}; - VkFence m_awaitableFence = VK_NULL_HANDLE; - VkSemaphore m_currentSemaphore = VK_NULL_HANDLE; - - std::array m_swapchainQueueFamilyIndices; -}; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h index 96f6bf30..d65042c8 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h @@ -1,10 +1,6 @@ #ifndef _VULKAN_API_ #define _VULKAN_API_ -#if BOOST_OS_WINDOWS -#define VK_USE_PLATFORM_WIN32_KHR // todo - define in CMakeLists.txt -#endif - #include bool InitializeGlobalVulkan(); @@ -129,29 +125,8 @@ VKFUNC_DEVICE(vkCreateGraphicsPipelines); VKFUNC_DEVICE(vkDestroyPipeline); VKFUNC_DEVICE(vkCmdBindPipeline); -// swapchain -#if defined(WEBOS) -// webOS is BOOST_OS_LINUX too, with no X11, no Wayland and no surface of its -// own: the frontend hands the core one, so no platform entry point is loaded. -#elif defined(__ANDROID__) -// Android is BOOST_OS_LINUX too, but there is no X11 or Wayland here -VKFUNC_INSTANCE(vkCreateAndroidSurfaceKHR); -#elif BOOST_OS_LINUX || BOOST_OS_BSD -VKFUNC_INSTANCE(vkCreateXlibSurfaceKHR); -VKFUNC_INSTANCE(vkCreateXcbSurfaceKHR); -#ifdef HAS_WAYLAND -VKFUNC_INSTANCE(vkCreateWaylandSurfaceKHR); -#endif -#endif - -#if BOOST_OS_WINDOWS -VKFUNC_INSTANCE(vkCreateWin32SurfaceKHR); -#endif - -#if BOOST_OS_MACOS -VKFUNC_INSTANCE(vkCreateMetalSurfaceEXT); -#endif - +// swapchain: no platform surface entry points - the frontend owns the surface +// and the swapchain, and hands the core an image to draw into VKFUNC_INSTANCE(vkDestroySurfaceKHR); VKFUNC_DEVICE(vkCreateSwapchainKHR); VKFUNC_DEVICE(vkDestroySwapchainKHR); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index f05e8015..9f47ecda 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -6,7 +6,6 @@ #include "Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.h" #include "Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanTextureReadback.h" -#include "Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.h" #include "Cafe/HW/Latte/Core/LatteBufferCache.h" @@ -95,101 +94,6 @@ VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback(VkDebugUtilsMessageSeverityFla return VK_FALSE; } -std::vector VulkanRenderer::GetDevices() -{ - if(!vkEnumerateInstanceVersion) - { - cemuLog_log(LogType::Force, "Vulkan cant list devices because Vulkan loader failed"); - return {}; - } - uint32 apiVersion = VK_API_VERSION_1_1; - if (vkEnumerateInstanceVersion(&apiVersion) != VK_SUCCESS) - { - if (VK_API_VERSION_MAJOR(apiVersion) < 1 || VK_API_VERSION_MINOR(apiVersion) < 2) - apiVersion = VK_API_VERSION_1_1; - } - - std::vector result; - - std::vector requiredExtensions; - requiredExtensions.clear(); - requiredExtensions.emplace_back(VK_KHR_SURFACE_EXTENSION_NAME); - #if BOOST_OS_WINDOWS - requiredExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); - #elif defined(__ANDROID__) || defined(WEBOS) - // no window system extension: the libretro core is handed a surface by the - // frontend and never creates one itself - #elif BOOST_OS_LINUX || BOOST_OS_BSD - auto backend = WindowSystem::GetWindowInfo().window_main.backend; - if(backend == WindowSystem::WindowHandleInfo::Backend::X11) - requiredExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); - #ifdef HAS_WAYLAND - else if (backend == WindowSystem::WindowHandleInfo::Backend::Wayland) - requiredExtensions.emplace_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); - #endif - #elif BOOST_OS_MACOS - requiredExtensions.emplace_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); - #endif - - VkApplicationInfo app_info{}; - app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - app_info.pApplicationName = EMULATOR_NAME; - app_info.applicationVersion = VK_MAKE_VERSION(EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH); - app_info.pEngineName = EMULATOR_NAME; - app_info.engineVersion = app_info.applicationVersion; - app_info.apiVersion = apiVersion; - - VkInstanceCreateInfo create_info{}; - create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - create_info.pApplicationInfo = &app_info; - create_info.ppEnabledExtensionNames = requiredExtensions.data(); - create_info.enabledExtensionCount = requiredExtensions.size(); - create_info.ppEnabledLayerNames = nullptr; - create_info.enabledLayerCount = 0; - - VkInstance instance = nullptr; - try - { - VkResult err; - if ((err = vkCreateInstance(&create_info, nullptr, &instance)) != VK_SUCCESS) - throw std::runtime_error(fmt::format("Unable to create a Vulkan instance: {}", err)); - - if (!InitializeInstanceVulkan(instance)) - throw std::runtime_error("can't initialize instanced vulkan functions"); - - uint32_t device_count = 0; - vkEnumeratePhysicalDevices(instance, &device_count, nullptr); - if (device_count == 0) - throw std::runtime_error("Failed to find a GPU with Vulkan support."); - - // create tmp surface to create a logical device - auto surface = CreateFramebufferSurface(instance, WindowSystem::GetWindowInfo().window_main); - std::vector devices(device_count); - vkEnumeratePhysicalDevices(instance, &device_count, devices.data()); - for (const auto& device : devices) - { - if (IsDeviceSuitable(surface, device)) - { - VkPhysicalDeviceIDProperties physDeviceIDProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES }; - VkPhysicalDeviceProperties2 physDeviceProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 }; - physDeviceProps.pNext = &physDeviceIDProps; - vkGetPhysicalDeviceProperties2(device, &physDeviceProps); - - result.emplace_back(physDeviceProps.properties.deviceName, physDeviceIDProps.deviceUUID); - } - } - vkDestroySurfaceKHR(instance, surface, nullptr); - } - catch (...) - { - } - - if (instance) - vkDestroyInstance(instance, nullptr); - - return result; - -} void VulkanRenderer::DetermineVendor() { @@ -368,499 +272,10 @@ void VulkanRenderer::GetDeviceFeatures() m_featureControl.limits.calcUniformBufferAlignmentM1 = std::max(m_featureControl.limits.minUniformBufferOffsetAlignment, m_featureControl.limits.nonCoherentAtomSize) - 1; } -#if BOOST_OS_LINUX -#include -#include "resource/IconsFontAwesome5.h" - -int BreathOfTheWildChildProcessMain() -{ - InitializeGlobalVulkan(); - struct sigaction sa{}; - sa.sa_handler = [](int unused) { _exit(1); }; - - int ret = sigaction(SIGABRT, &sa, nullptr); - - freopen("/dev/null", "w", stderr); - - setenv("RADV_DEBUG", "llvm", 1); - - VkInstanceCreateInfo create_info{}; - create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - VkInstance instance = VK_NULL_HANDLE; - if (vkCreateInstance(&create_info, nullptr, &instance) != VK_SUCCESS) - return 1; - InitializeInstanceVulkan(instance); - - // this function will abort() when LLVM is absent - uint32_t count = 0; - vkEnumeratePhysicalDevices(instance, &count, nullptr); - - vkDestroyInstance(instance, nullptr); - return 0; -} - -static void LinuxBreathOfTheWildWorkaround(VkInstance& instance, const VkInstanceCreateInfo* create_info) -{ - - // if the user specified either shader backend, do nothing. - // should parse the flag list but there are currently no other flags containing llvm or aco as a substring - const char* debugEnvC = getenv("RADV_DEBUG"); - std::string_view debugEnv = debugEnvC != nullptr ? debugEnvC : ""; - if (debugEnv.find("aco") != std::string_view::npos || debugEnv.find("llvm") != std::string_view::npos) - return; - - uint32_t count = 0; - vkEnumeratePhysicalDevices(instance, &count, nullptr); - - std::vector physicalDevices{count}; - vkEnumeratePhysicalDevices(instance, &count, physicalDevices.data()); - - // Find the first AMD device using a RADV driver and store its version - int version = 0; - for (auto& i : physicalDevices) - { - VkPhysicalDeviceDriverProperties driverProps{}; - driverProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; - VkPhysicalDeviceProperties2 prop{}; - prop.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - prop.pNext = &driverProps; - vkGetPhysicalDeviceProperties2(i, &prop); - if (prop.properties.vendorID != 0x1002 || driverProps.driverID != VK_DRIVER_ID_MESA_RADV) - continue; - - version = prop.properties.driverVersion; - break; - } - - if (version == 0) - return; - - - int major = VK_API_VERSION_MAJOR(version); - int minor = VK_API_VERSION_MINOR(version); - int patch = VK_API_VERSION_PATCH(version); - - // If the driver is unaffected skip the workaround. - // affected drivers: - // 25.3.0 - 26.0.4 - if ((major <= 25 && minor < 3) || (major == 26 && (minor > 0 || patch >= 5)) || major > 26) - return; - - // check if running with LLVM would crash because mesa is LLVM-less. - int childID = fork(); - if (childID == 0) // inside this if statement runs in child - { - setenv("CEMU_DETECT_RADV","1", 1); - execl("/proc/self/exe", "/proc/self/exe", nullptr); - _exit(2); // exec failed so err on the safe side and signal failure - } - - int childStatus = 0; - waitpid(childID, &childStatus, 0); - - // if the process didn't exit cleanly or failed to determine LLVM status - if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) == 2) - { - cemuLog_log(LogType::Force, "BOTW/RADV workaround not applied because detecting LLVM presence failed unexpectedly"); - return; - } - - if (WEXITSTATUS(childStatus) == 1) - cemuLog_log(LogType::Force, "BOTW/RADV workaround not applied because mesa was built without LLVM"); - - // only continue if the process exits with code zero, which means it didn't crash - if (WEXITSTATUS(childStatus) != 0) - return; - - cemuLog_log(LogType::Force, "BOTW/RADV workaround active. Adding \"llvm\" to RADV_DEBUG environment variable"); - if (debugEnv.empty()) - { - setenv("RADV_DEBUG", "llvm", 1); - } - else - { - std::string appendedDebugEnv{debugEnv}; - appendedDebugEnv.append(",llvm"); - setenv("RADV_DEBUG", appendedDebugEnv.c_str(), 1); - } - - // recreate the vulkan instance to update debug setting - vkDestroyInstance(instance, nullptr); - VkResult err = vkCreateInstance(create_info, nullptr, &instance); - // re-check for errors just in case. - if (err != VK_SUCCESS) - throw std::runtime_error(fmt::format("Unable to re-create a Vulkan instance after RADV/LLVM workaround: {}", err)); - InitializeInstanceVulkan(instance); - - LatteOverlay_pushNotification(std::string{(const char*)ICON_FA_EXCLAMATION_TRIANGLE} + "RADV_DEBUG=llvm set automatically to avoid crashing due to a driver bug. If possible update mesa to 26.0.5 or newer", 10'000); - -} - -#endif // Defined at global scope by the libretro glue (src/libretro/CemuLibretro.cpp). bool libretro_gpu_context_gone(); -VulkanRenderer::VulkanRenderer() : Renderer(RendererAPI::Vulkan) -{ - glslang::InitializeProcess(); - - cemuLog_log(LogType::Force, "------- Init Vulkan graphics backend -------"); - - const bool useValidationLayer = cemuLog_isLoggingEnabled(LogType::VulkanValidation); - if (useValidationLayer) - cemuLog_log(LogType::Force, "Validation layer is enabled"); - - VkResult err; - - // build list of layers - m_layerNames.clear(); - if (useValidationLayer) - m_layerNames.emplace_back("VK_LAYER_KHRONOS_validation"); - - // check available instance extensions - std::vector enabledInstanceExtensions = CheckInstanceExtensionSupport(m_featureControl); - - uint32 apiVersion = VK_API_VERSION_1_1; - if (vkEnumerateInstanceVersion(&apiVersion) != VK_SUCCESS) - { - if (VK_API_VERSION_MAJOR(apiVersion) < 1 || VK_API_VERSION_MINOR(apiVersion) < 2) - apiVersion = VK_API_VERSION_1_1; - } - - cemuLog_log(LogType::Force, fmt::format("Vulkan instance version: {}.{}", VK_API_VERSION_MAJOR(apiVersion), VK_API_VERSION_MINOR(apiVersion))); - - VkApplicationInfo app_info{}; - app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - app_info.pApplicationName = EMULATOR_NAME; - app_info.applicationVersion = VK_MAKE_VERSION(EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH); - app_info.pEngineName = EMULATOR_NAME; - app_info.engineVersion = app_info.applicationVersion; - app_info.apiVersion = apiVersion; - - VkInstanceCreateInfo create_info{}; - create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - create_info.pApplicationInfo = &app_info; - create_info.ppEnabledExtensionNames = enabledInstanceExtensions.data(); - create_info.enabledExtensionCount = enabledInstanceExtensions.size(); - create_info.ppEnabledLayerNames = m_layerNames.data(); - create_info.enabledLayerCount = m_layerNames.size(); - - err = vkCreateInstance(&create_info, nullptr, &m_instance); - - if (err == VK_ERROR_LAYER_NOT_PRESENT) { - cemuLog_log(LogType::Force, "Failed to enable vulkan validation (VK_LAYER_KHRONOS_validation)"); - create_info.enabledLayerCount = 0; - err = vkCreateInstance(&create_info, nullptr, &m_instance); - } - - if (err != VK_SUCCESS) - throw std::runtime_error(fmt::format("Unable to create a Vulkan instance: {}", err)); - - if (!InitializeInstanceVulkan(m_instance)) - throw std::runtime_error("Unable to load instanced Vulkan functions"); - - // Workaround for BOTW + RADV. Runes like Magnesis and the camera cause GPU crashes. -#if BOOST_OS_LINUX - uint64 currentTitleId = CafeSystem::GetForegroundTitleId(); - if (currentTitleId == 0x00050000101c9500 || currentTitleId == 0x00050000101c9400 || currentTitleId == 0x00050000101c9300) - { - LinuxBreathOfTheWildWorkaround(m_instance, &create_info); - } -#endif - - uint32_t device_count = 0; - vkEnumeratePhysicalDevices(m_instance, &device_count, nullptr); - if (device_count == 0) - throw std::runtime_error("Failed to find a GPU with Vulkan support."); - - // create tmp surface to create a logical device - auto surface = CreateFramebufferSurface(m_instance, WindowSystem::GetWindowInfo().window_main); - - auto& config = GetConfig(); - decltype(config.vk_graphic_device_uuid) zero{}; - const bool has_device_set = config.vk_graphic_device_uuid != zero; - - VkPhysicalDevice fallbackDevice = VK_NULL_HANDLE; - std::string fallbackDeviceName = ""; - - std::vector devices(device_count); - vkEnumeratePhysicalDevices(m_instance, &device_count, devices.data()); - for (const auto& device : devices) - { - if (IsDeviceSuitable(surface, device)) - { - VkPhysicalDeviceIDProperties physDeviceIDProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES }; - VkPhysicalDeviceProperties2 physDeviceProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 }; - physDeviceProps.pNext = &physDeviceIDProps; - vkGetPhysicalDeviceProperties2(device, &physDeviceProps); - - if (fallbackDevice == VK_NULL_HANDLE) - { - fallbackDevice = device; - fallbackDeviceName = physDeviceProps.properties.deviceName; - } - - if (has_device_set) - { - if (memcmp(config.vk_graphic_device_uuid.data(), physDeviceIDProps.deviceUUID, VK_UUID_SIZE) != 0) - continue; - } - - m_physicalDevice = device; - m_selectedDeviceName = physDeviceProps.properties.deviceName; - break; - } - } - - if (m_physicalDevice == VK_NULL_HANDLE && fallbackDevice != VK_NULL_HANDLE) - { - cemuLog_log(LogType::Force, "The selected GPU could not be found or is not suitable. Falling back to first available device instead"); - m_physicalDevice = fallbackDevice; - m_selectedDeviceName = fallbackDeviceName; - config.vk_graphic_device_uuid = {}; // resetting device selection - } - else if (m_physicalDevice == VK_NULL_HANDLE) - { - cemuLog_log(LogType::Force, "No physical GPU could be found with the required extensions and swap chain support."); - throw std::runtime_error("No physical GPU could be found with the required extensions and swap chain support."); - } - - CheckDeviceExtensionSupport(m_physicalDevice, m_featureControl); // todo - merge this with GetDeviceFeatures and separate from IsDeviceSuitable? - - DetermineVendor(); - GetDeviceFeatures(); - - // init memory manager - memoryManager.reset(new VKRMemoryManager(this)); - - try - { - VkPhysicalDeviceIDProperties physDeviceIDProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES }; - VkPhysicalDeviceProperties2 physDeviceProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 }; - physDeviceProps.pNext = &physDeviceIDProps; - vkGetPhysicalDeviceProperties2(m_physicalDevice, &physDeviceProps); - - #if BOOST_OS_WINDOWS - m_dxgi_wrapper = std::make_unique(physDeviceIDProps.deviceLUID); - #endif - } - catch (const std::exception& ex) - { - cemuLog_log(LogType::Force, "can't create dxgi wrapper: {}", ex.what()); - } - - // create logical device - m_indices = FindQueueFamilies(surface, m_physicalDevice); - std::set uniqueQueueFamilies = { m_indices.graphicsFamily, m_indices.presentFamily }; - std::vector queueCreateInfos = CreateQueueCreateInfos(uniqueQueueFamilies); - VkPhysicalDeviceFeatures deviceFeatures = {}; - VkPhysicalDeviceFeatures2 deviceFeatures2 = {}; - deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - vkGetPhysicalDeviceFeatures2(m_physicalDevice, &deviceFeatures2); - - deviceFeatures.independentBlend = VK_TRUE; - deviceFeatures.samplerAnisotropy = VK_TRUE; - deviceFeatures.imageCubeArray = VK_TRUE; - //moltenVK supports logicOp via private api - deviceFeatures.logicOp = deviceFeatures2.features.logicOp; - if (!deviceFeatures.logicOp) { - cemuLog_log(LogType::Force, "LogicOp not supported by the driver, some rendering issues might occur"); -#if BOOST_OS_MACOS - cemuLog_log(LogType::Force, "Install the privateapi variant of MoltenVK to get logicOp support on macOS"); -#endif - } -#if !BOOST_OS_MACOS - deviceFeatures.geometryShader = VK_TRUE; -#endif - deviceFeatures.occlusionQueryPrecise = VK_TRUE; - deviceFeatures.depthClamp = VK_TRUE; - deviceFeatures.depthBiasClamp = VK_TRUE; - - if (m_featureControl.deviceExtensions.pipeline_robustness) - { - deviceFeatures.robustBufferAccess = VK_FALSE; - } - else - { - cemuLog_log(LogType::Force, "VK_EXT_pipeline_robustness not supported. Falling back to robustBufferAccess"); - deviceFeatures.robustBufferAccess = VK_TRUE; - } - - deviceFeatures.vertexPipelineStoresAndAtomics = deviceFeatures2.features.vertexPipelineStoresAndAtomics; - if (!deviceFeatures.vertexPipelineStoresAndAtomics) - cemuLog_log(LogType::Force, "vertexPipelineStoresAndAtomics not supported by the driver. Games which use the streamout feature will not render correctly"); - - void* deviceExtensionFeatures = nullptr; - - // enable VK_EXT_pipeline_creation_cache_control - VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT cacheControlFeature{}; - if (m_featureControl.deviceExtensions.pipeline_creation_cache_control) - { - cacheControlFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES_EXT; - cacheControlFeature.pNext = deviceExtensionFeatures; - deviceExtensionFeatures = &cacheControlFeature; - cacheControlFeature.pipelineCreationCacheControl = VK_TRUE; - } - // enable VK_EXT_custom_border_color - VkPhysicalDeviceCustomBorderColorFeaturesEXT customBorderColorFeature{}; - if (m_featureControl.deviceExtensions.custom_border_color_without_format) - { - customBorderColorFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT; - customBorderColorFeature.pNext = deviceExtensionFeatures; - deviceExtensionFeatures = &customBorderColorFeature; - customBorderColorFeature.customBorderColors = VK_TRUE; - customBorderColorFeature.customBorderColorWithoutFormat = VK_TRUE; - } - // enable VK_KHR_present_id - VkPhysicalDevicePresentIdFeaturesKHR presentIdFeature{}; - if(m_featureControl.deviceExtensions.present_wait) - { - presentIdFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR; - presentIdFeature.pNext = deviceExtensionFeatures; - deviceExtensionFeatures = &presentIdFeature; - presentIdFeature.presentId = VK_TRUE; - } - // enable VK_KHR_present_wait - VkPhysicalDevicePresentWaitFeaturesKHR presentWaitFeature{}; - if(m_featureControl.deviceExtensions.present_wait) - { - presentWaitFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR; - presentWaitFeature.pNext = deviceExtensionFeatures; - deviceExtensionFeatures = &presentWaitFeature; - presentWaitFeature.presentWait = VK_TRUE; - } - // enable VK_EXT_pipeline_robustness - VkPhysicalDevicePipelineRobustnessFeaturesEXT pipelineRobustnessFeature{}; - if (m_featureControl.deviceExtensions.pipeline_robustness) - { - pipelineRobustnessFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES_EXT; - pipelineRobustnessFeature.pNext = deviceExtensionFeatures; - deviceExtensionFeatures = &pipelineRobustnessFeature; - pipelineRobustnessFeature.pipelineRobustness = VK_TRUE; - } - // enable attachment feedback loop layout + dynamic state if both are supported - VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT attachmentFeedbackLoopLayoutFeature{}; - VkPhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT attachmentFeedbackLoopDynamicStateFeature{}; - if (UseAttachmentFeedbackLoop()) - { - attachmentFeedbackLoopLayoutFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT; - attachmentFeedbackLoopLayoutFeature.pNext = deviceExtensionFeatures; - deviceExtensionFeatures = &attachmentFeedbackLoopLayoutFeature; - attachmentFeedbackLoopLayoutFeature.attachmentFeedbackLoopLayout = VK_TRUE; - - attachmentFeedbackLoopDynamicStateFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_FEATURES_EXT; - attachmentFeedbackLoopDynamicStateFeature.pNext = deviceExtensionFeatures; - deviceExtensionFeatures = &attachmentFeedbackLoopDynamicStateFeature; - attachmentFeedbackLoopDynamicStateFeature.attachmentFeedbackLoopDynamicState = VK_TRUE; - } - - std::vector used_extensions; - VkDeviceCreateInfo createInfo = CreateDeviceCreateInfo(queueCreateInfos, deviceFeatures, deviceExtensionFeatures, used_extensions); - - VkResult result = vkCreateDevice(m_physicalDevice, &createInfo, nullptr, &m_logicalDevice); - if (result != VK_SUCCESS) - { - cemuLog_log(LogType::Force, "Vulkan: Unable to create a logical device. Error {}", (sint32)result); - throw std::runtime_error(fmt::format("Unable to create a logical device: {}", result)); - } - - InitializeDeviceVulkan(m_logicalDevice); - - vkGetDeviceQueue(m_logicalDevice, m_indices.graphicsFamily, 0, &m_graphicsQueue); - vkGetDeviceQueue(m_logicalDevice, m_indices.graphicsFamily, 0, &m_presentQueue); - - vkDestroySurfaceKHR(m_instance, surface, nullptr); - - if (useValidationLayer && m_featureControl.instanceExtensions.debug_utils) - { - PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = reinterpret_cast(vkGetInstanceProcAddr(m_instance, "vkCreateDebugUtilsMessengerEXT")); - - VkDebugUtilsMessengerCreateInfoEXT debugCallback{}; - debugCallback.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; - debugCallback.pNext = nullptr; - debugCallback.flags = 0; - debugCallback.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; - debugCallback.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; - debugCallback.pfnUserCallback = &DebugUtilsCallback; - - vkCreateDebugUtilsMessengerEXT(m_instance, &debugCallback, nullptr, &m_debugCallback); - - cemuLog_log(LogType::Force, "Debug: Vulkan validation layer enabled, vkCreateDebugUtilsMessengerEXT will be used to log validation errors"); - } - - if (this->IsTracingToolEnabled()) - cemuLog_log(LogType::Force, "Debug: Tracing tool detected, will recompile all shaders with debug info enabled. This disables the SPIR-V cache."); - if (this->IsDebugMarkersEnabled()) - cemuLog_log(LogType::Force, "Debug: Detected tool capable of using debug markers, will use vkDebugMarkerSetObjectNameEXT to identify Vulkan objects"); - - // set initial viewport and scissor box size - m_state.currentViewport.width = 4; - m_state.currentViewport.height = 4; - m_state.currentScissorRect.extent.width = 4; - m_state.currentScissorRect.extent.height = 4; - - QueryMemoryInfo(); - QueryAvailableFormats(); - CreateCommandPool(); - CreateCommandBuffers(); - CreateDescriptorPool(); - swapchain_createDescriptorSetLayout(); - - // extension info - // cemuLog_log(LogType::Force, "VK_KHR_dynamic_rendering: {}", m_featureControl.deviceExtensions.dynamic_rendering?"supported":"not supported"); - - void* bufferPtr; - // init ringbuffer for uniform vars - m_uniformVarBufferMemoryIsCoherent = false; - if (memoryManager->CreateBuffer(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory)) - m_uniformVarBufferMemoryIsCoherent = true; - else if (memoryManager->CreateBuffer(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory)) - m_uniformVarBufferMemoryIsCoherent = true; // unified memory - else if (memoryManager->CreateBuffer(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory)) - m_uniformVarBufferMemoryIsCoherent = true; - else if (memoryManager->CreateBuffer(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory)) - m_uniformVarBufferMemoryIsCoherent = true; - else - { - memoryManager->CreateBuffer(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory); - } - - if (!m_uniformVarBufferMemoryIsCoherent) - cemuLog_log(LogType::Force, "[Vulkan-Info] Using non-coherent memory for uniform data"); - bufferPtr = nullptr; - vkMapMemory(m_logicalDevice, m_uniformVarBufferMemory, 0, VK_WHOLE_SIZE, 0, &bufferPtr); - m_uniformVarBufferPtr = (uint8*)bufferPtr; - - // texture readback buffer - if (!memoryManager->CreateBuffer(TEXTURE_READBACK_SIZE, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_textureReadbackBuffer, m_textureReadbackBufferMemory)) - { - memoryManager->CreateBuffer(TEXTURE_READBACK_SIZE, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_textureReadbackBuffer, m_textureReadbackBufferMemory); - } - bufferPtr = nullptr; - vkMapMemory(m_logicalDevice, m_textureReadbackBufferMemory, 0, VK_WHOLE_SIZE, 0, &bufferPtr); - m_textureReadbackBufferPtr = (uint8*)bufferPtr; - - // transform feedback ringbuffer - VkBufferUsageFlags xfbRingBufferUsage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - memoryManager->CreateBuffer(LatteStreamout_GetRingBufferSize(), xfbRingBufferUsage, 0, m_xfbRingBuffer, m_xfbRingBufferMemory); - - // occlusion query result buffer - if (!memoryManager->CreateBuffer(OCCLUSION_QUERY_POOL_SIZE * sizeof(uint64), VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_occlusionQueries.bufferQueryResults, m_occlusionQueries.memoryQueryResults)) - { - memoryManager->CreateBuffer(OCCLUSION_QUERY_POOL_SIZE * sizeof(uint64), VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_occlusionQueries.bufferQueryResults, m_occlusionQueries.memoryQueryResults); - } - bufferPtr = nullptr; - vkMapMemory(m_logicalDevice, m_occlusionQueries.memoryQueryResults, 0, VK_WHOLE_SIZE, 0, &bufferPtr); - m_occlusionQueries.ptrQueryResults = (uint64*)bufferPtr; - - for (sint32 i = 0; i < OCCLUSION_QUERY_POOL_SIZE; i++) - m_occlusionQueries.list_availableQueryIndices.emplace_back(i); - - // start compilation threads - RendererShaderVk::Init(); // shaders - PipelineCompiler::CompileThreadPool_Start(); // pipelines -} VulkanRenderer::VulkanRenderer(VkInstance instance, VkPhysicalDevice physDevice, VkDevice device, VkQueue queue, uint32_t queueFamilyIndex) : Renderer(RendererAPI::Vulkan) @@ -964,7 +379,6 @@ VulkanRenderer::VulkanRenderer(VkInstance instance, VkPhysicalDevice physDevice, CreateCommandPool(); CreateCommandBuffers(); CreateDescriptorPool(); - swapchain_createDescriptorSetLayout(); void* bufferPtr; m_uniformVarBufferMemoryIsCoherent = false; @@ -1208,16 +622,9 @@ VulkanRenderer::~VulkanRenderer() vkDestroyDescriptorPool(m_logicalDevice, m_descriptorPool, nullptr); - for(auto& i : m_backbufferBlitPipelineCache) - { - vkDestroyPipeline(m_logicalDevice, i.second, nullptr); - } - m_backbufferBlitPipelineCache = {}; - if(m_occlusionQueries.queryPool != VK_NULL_HANDLE) vkDestroyQueryPool(m_logicalDevice, m_occlusionQueries.queryPool, nullptr); - vkDestroyDescriptorSetLayout(m_logicalDevice, m_swapchainDescriptorSetLayout, nullptr); // shut down imgui ImGui_ImplVulkan_Shutdown(); @@ -1232,8 +639,6 @@ VulkanRenderer::~VulkanRenderer() memoryManager->DeleteBuffer(m_occlusionQueries.bufferQueryResults, m_occlusionQueries.memoryQueryResults); memoryManager->DeleteBuffer(m_bufferCache, m_bufferCacheMemory); - m_padSwapchainInfo = nullptr; - m_mainSwapchainInfo = nullptr; // clean up resources used for surface copy surfaceCopy_cleanup(); @@ -1304,36 +709,6 @@ VulkanRenderer* VulkanRenderer::GetInstance() return static_cast(g_renderer.get()); } -void VulkanRenderer::InitializeSurface(const Vector2i& size, bool mainWindow) -{ - if (mainWindow) - { - m_mainSwapchainInfo = std::make_unique(mainWindow, size); - m_mainSwapchainInfo->Create(); - } - else - { - m_padSwapchainInfo = std::make_unique(mainWindow, size); - // todo: figure out a way to exclusively create swapchain on main LatteThread - m_padSwapchainInfo->Create(); - } -} - -const std::unique_ptr& VulkanRenderer::GetChainInfoPtr(bool mainWindow) const -{ - return mainWindow ? m_mainSwapchainInfo : m_padSwapchainInfo; -} - -SwapchainInfoVk& VulkanRenderer::GetChainInfo(bool mainWindow) const -{ - return *GetChainInfoPtr(mainWindow); -} - -void VulkanRenderer::StopUsingPadAndWait() -{ - m_destroyPadSwapchainNextAcquire.test_and_set(); - m_destroyPadSwapchainNextAcquire.wait(true); -} bool VulkanRenderer::IsPadWindowActive() { @@ -1341,9 +716,7 @@ bool VulkanRenderer::IsPadWindowActive() // shared present image. Report "active" whenever the DRC layout asks for it // so LatteRenderTarget routes DRC scan-out (and the auto-mirror fallback) // through DrawBackbufferQuad. - if (g_libretroScreenLayout != LibretroScreenLayout::Tv) - return true; - return IsSwapchainInfoValid(false); + return g_libretroScreenLayout != LibretroScreenLayout::Tv; } void VulkanRenderer::HandleScreenshotRequest(LatteTextureView* texView, bool padView) @@ -1351,23 +724,8 @@ void VulkanRenderer::HandleScreenshotRequest(LatteTextureView* texView, bool pad if (!m_screenshot_requested && m_screenshot_state == ScreenshotState::None) return; - if (IsSwapchainInfoValid(false)) - { - // we already took a pad view screenshow and want a main window screenshot - if (m_screenshot_state == ScreenshotState::Main && padView) - return; - - if (m_screenshot_state == ScreenshotState::Pad && !padView) - return; - - // remember which screenshot is left to take - if (m_screenshot_state == ScreenshotState::None) - m_screenshot_state = padView ? ScreenshotState::Main : ScreenshotState::Pad; - else - m_screenshot_state = ScreenshotState::None; - } - else - m_screenshot_state = ScreenshotState::None; + // No pad window of its own, so one screenshot covers the request + m_screenshot_state = ScreenshotState::None; auto texViewVk = (LatteTextureViewVk*)texView; auto baseImageTex = texViewVk->GetBaseImage(); @@ -1700,45 +1058,6 @@ RendererShader* VulkanRenderer::shader_create(RendererShader::ShaderType type, u return new RendererShaderVk(type, baseHash, auxHash, isGameShader, isGfxPackShader, source); } -VulkanRenderer::QueueFamilyIndices VulkanRenderer::FindQueueFamilies(VkSurfaceKHR surface, VkPhysicalDevice device) -{ - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); - - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); - - QueueFamilyIndices indices; - for (int i = 0; i < (int)queueFamilies.size(); ++i) - { - const auto& queueFamily = queueFamilies[i]; - if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) - indices.graphicsFamily = i; - - // Skip present support check if no surface (libretro mode) - if (surface != VK_NULL_HANDLE) - { - VkBool32 presentSupport = false; - const VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); - if (result != VK_SUCCESS) - throw std::runtime_error(fmt::format("Error while attempting to check if a surface supports presentation: {}", result)); - - if (queueFamily.queueCount > 0 && presentSupport) - indices.presentFamily = i; - } - else - { - // In libretro mode, use graphics queue for presentation too - if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) - indices.presentFamily = i; - } - - if (indices.IsComplete()) - break; - } - - return indices; -} bool VulkanRenderer::CheckDeviceExtensionSupport(const VkPhysicalDevice device, FeatureControl& info) { @@ -1812,215 +1131,6 @@ bool VulkanRenderer::CheckDeviceExtensionSupport(const VkPhysicalDevice device, return requiredExtensions.empty(); } -std::vector VulkanRenderer::CheckInstanceExtensionSupport(FeatureControl& info) -{ - std::vector availableInstanceExtensions; - std::vector enabledInstanceExtensions; - VkResult err; - - auto isExtensionAvailable = [&availableInstanceExtensions](const char* extensionName) -> bool - { - return std::find_if(availableInstanceExtensions.begin(), availableInstanceExtensions.end(), - [&extensionName](const VkExtensionProperties& prop) -> bool - { - return strcmp(prop.extensionName, extensionName) == 0; - }) != availableInstanceExtensions.cend(); - }; - - // get list of available instance extensions - uint32_t count; - if ((err = vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr)) != VK_SUCCESS) - throw std::runtime_error(fmt::format("Failed to retrieve the instance extension properties : {}", err)); - - availableInstanceExtensions.resize(count); - if ((err = vkEnumerateInstanceExtensionProperties(nullptr, &count, availableInstanceExtensions.data())) != VK_SUCCESS) - throw std::runtime_error(fmt::format("Failed to retrieve the instance extension properties: {}", err)); - - // build list of required extensions - std::vector requiredInstanceExtensions; - requiredInstanceExtensions.emplace_back(VK_KHR_SURFACE_EXTENSION_NAME); - #if BOOST_OS_WINDOWS - requiredInstanceExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); - #elif defined(__ANDROID__) || defined(WEBOS) - // no window system extension: the libretro core is handed a surface by the - // frontend and never creates one itself - #elif BOOST_OS_LINUX || BOOST_OS_BSD - auto backend = WindowSystem::GetWindowInfo().window_main.backend; - if(backend == WindowSystem::WindowHandleInfo::Backend::X11) - requiredInstanceExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); - #if HAS_WAYLAND - else if (backend == WindowSystem::WindowHandleInfo::Backend::Wayland) - requiredInstanceExtensions.emplace_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); - #endif - #elif BOOST_OS_MACOS - requiredInstanceExtensions.emplace_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); - #endif - if (cemuLog_isLoggingEnabled(LogType::VulkanValidation)) - requiredInstanceExtensions.emplace_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); - - // make sure all required extensions are supported - for (const auto& extension : availableInstanceExtensions) - { - for (auto it = requiredInstanceExtensions.begin(); it < requiredInstanceExtensions.end(); ++it) - { - if (strcmp(*it, extension.extensionName) == 0) - { - enabledInstanceExtensions.emplace_back(*it); - requiredInstanceExtensions.erase(it); - break; - } - } - } - if (!requiredInstanceExtensions.empty()) - { - cemuLog_log(LogType::Force, "The following required Vulkan instance extensions are not supported:"); - - std::stringstream ss; - for (const auto& extension : requiredInstanceExtensions) - cemuLog_log(LogType::Force, "{}", extension); - cemuLog_waitForFlush(); - throw std::runtime_error(ss.str()); - } - - // check for optional extensions - info.instanceExtensions.debug_utils = isExtensionAvailable(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - if (info.instanceExtensions.debug_utils) - enabledInstanceExtensions.emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - return enabledInstanceExtensions; -} - -bool VulkanRenderer::IsDeviceSuitable(VkSurfaceKHR surface, const VkPhysicalDevice& device) -{ - if (!FindQueueFamilies(surface, device).IsComplete()) - return false; - - // check API version (using Vulkan 1.0 way of querying properties) - VkPhysicalDeviceProperties properties{}; - vkGetPhysicalDeviceProperties(device, &properties); - uint32 vkVersionMajor = VK_API_VERSION_MAJOR(properties.apiVersion); - uint32 vkVersionMinor = VK_API_VERSION_MINOR(properties.apiVersion); - if (vkVersionMajor < 1 || (vkVersionMajor == 1 && vkVersionMinor < 1)) - return false; // minimum required version is Vulkan 1.1 - - FeatureControl info; - if (!CheckDeviceExtensionSupport(device, info)) - return false; - - // Skip swapchain support check if no surface (libretro mode) - if (surface == VK_NULL_HANDLE) - return true; - - const auto swapchainSupport = SwapchainInfoVk::QuerySwapchainSupport(surface, device); - - return !swapchainSupport.formats.empty() && !swapchainSupport.presentModes.empty(); -} - -#if BOOST_OS_WINDOWS -VkSurfaceKHR VulkanRenderer::CreateWinSurface(VkInstance instance, HWND hwindow) -{ - VkWin32SurfaceCreateInfoKHR sci{}; - sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - sci.hwnd = hwindow; - sci.hinstance = GetModuleHandle(nullptr); - - VkSurfaceKHR result; - VkResult err; - if ((err = vkCreateWin32SurfaceKHR(instance, &sci, nullptr, &result)) != VK_SUCCESS) - { - cemuLog_log(LogType::Force, "Cannot create a Win32 Vulkan surface: {}", (sint32)err); - throw std::runtime_error(fmt::format("Cannot create a Win32 Vulkan surface: {}", err)); - } - - return result; -} -#endif - -#if (BOOST_OS_LINUX || BOOST_OS_BSD) && !defined(__ANDROID__) && !defined(WEBOS) -VkSurfaceKHR VulkanRenderer::CreateXlibSurface(VkInstance instance, Display* dpy, Window window) -{ - VkXlibSurfaceCreateInfoKHR sci{}; - sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - sci.flags = 0; - sci.dpy = dpy; - sci.window = window; - - VkSurfaceKHR result; - VkResult err; - if ((err = vkCreateXlibSurfaceKHR(instance, &sci, nullptr, &result)) != VK_SUCCESS) - { - cemuLog_log(LogType::Force, "Cannot create a X11 Vulkan surface: {}", (sint32)err); - throw std::runtime_error(fmt::format("Cannot create a X11 Vulkan surface: {}", err)); - } - - return result; -} - -VkSurfaceKHR VulkanRenderer::CreateXcbSurface(VkInstance instance, xcb_connection_t* connection, xcb_window_t window) -{ - VkXcbSurfaceCreateInfoKHR sci{}; - sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; - sci.flags = 0; - sci.connection = connection; - sci.window = window; - - VkSurfaceKHR result; - VkResult err; - if ((err = vkCreateXcbSurfaceKHR(instance, &sci, nullptr, &result)) != VK_SUCCESS) - { - cemuLog_log(LogType::Force, "Cannot create a XCB Vulkan surface: {}", (sint32)err); - throw std::runtime_error(fmt::format("Cannot create a XCB Vulkan surface: {}", err)); - } - - return result; -} -#ifdef HAS_WAYLAND -VkSurfaceKHR VulkanRenderer::CreateWaylandSurface(VkInstance instance, wl_display* display, wl_surface* surface) -{ - VkWaylandSurfaceCreateInfoKHR sci{}; - sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; - sci.flags = 0; - sci.display = display; - sci.surface = surface; - - VkSurfaceKHR result; - VkResult err; - if ((err = vkCreateWaylandSurfaceKHR(instance, &sci, nullptr, &result)) != VK_SUCCESS) - { - cemuLog_log(LogType::Force, "Cannot create a Wayland Vulkan surface: {}", (sint32)err); - throw std::runtime_error(fmt::format("Cannot create a Wayland Vulkan surface: {}", err)); - } - - return result; -} -#endif // HAS_WAYLAND -#endif // BOOST_OS_LINUX - -VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, WindowSystem::WindowHandleInfo& windowInfo) -{ - // Return null surface if no window (libretro mode) - if (windowInfo.surface == nullptr) - return VK_NULL_HANDLE; - -#if BOOST_OS_WINDOWS - return CreateWinSurface(instance, static_cast(windowInfo.surface)); -#elif defined(__ANDROID__) || defined(WEBOS) - // WindowHandleInfo knows only about X11 and Wayland; on Android and webOS the - // surface is non-null only if a window backend is ever added there, so until - // then the early return above is the only path reached. - cemu_assert_unimplemented(); - return VK_NULL_HANDLE; -#elif BOOST_OS_LINUX || BOOST_OS_BSD - if(windowInfo.backend == WindowSystem::WindowHandleInfo::Backend::X11) - return CreateXlibSurface(instance, static_cast(windowInfo.display), reinterpret_cast(windowInfo.surface)); - #ifdef HAS_WAYLAND - if(windowInfo.backend == WindowSystem::WindowHandleInfo::Backend::Wayland) - return CreateWaylandSurface(instance, static_cast(windowInfo.display), static_cast(windowInfo.surface)); - #endif - return {}; -#elif BOOST_OS_MACOS - return CreateCocoaSurface(instance, windowInfo.surface); -#endif -} void VulkanRenderer::CreateCommandPool() { @@ -2070,12 +1180,6 @@ void VulkanRenderer::CreateCommandBuffers() } } -bool VulkanRenderer::IsSwapchainInfoValid(bool mainWindow) const -{ - auto& chainInfo = GetChainInfoPtr(mainWindow); - return chainInfo && chainInfo->IsValid(); -} - void VulkanRenderer::CreateNullTexture(NullTexture& nullTex, VkImageType imageType) { @@ -2173,54 +1277,10 @@ void VulkanRenderer::DeleteNullObjects() DeleteNullTexture(nullTexture2D); } +// The overlay draws into the frontend's image or not at all: there is no +// swapchain of the core's own to give imgui a render pass on void VulkanRenderer::ImguiInit() { - return; - VkRenderPass prevRenderPass = m_imguiRenderPass; - - VkAttachmentDescription colorAttachment = {}; - colorAttachment.format = m_mainSwapchainInfo->m_surfaceFormat.format; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentReference colorAttachmentRef = {}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - - VkRenderPassCreateInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = 1; - renderPassInfo.pAttachments = &colorAttachment; - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - const auto result = vkCreateRenderPass(m_logicalDevice, &renderPassInfo, nullptr, &m_imguiRenderPass); - if (result != VK_SUCCESS) - throw VkException(result, "can't create imgui renderpass"); - - ImGui_ImplVulkan_InitInfo info{}; - info.Instance = m_instance; - info.PhysicalDevice = m_physicalDevice; - info.Device = m_logicalDevice; - info.QueueFamily = m_indices.presentFamily; - info.Queue = m_presentQueue; - info.PipelineCache = m_pipeline_cache; - info.DescriptorPool = m_descriptorPool; - info.MinImageCount = m_mainSwapchainInfo->m_swapchainImages.size(); - info.ImageCount = info.MinImageCount; - - ImGui_ImplVulkan_Init(&info, m_imguiRenderPass); - - if (prevRenderPass != VK_NULL_HANDLE) - vkDestroyRenderPass(GetLogicalDevice(), prevRenderPass, nullptr); } void VulkanRenderer::Initialize() @@ -2493,22 +1553,9 @@ void VulkanRenderer::QueryAvailableFormats() bool VulkanRenderer::ImguiBegin(bool mainWindow) { - if (!Renderer::ImguiBegin(mainWindow)) - return false; - - auto& chainInfo = GetChainInfo(mainWindow); - - if (!AcquireNextSwapchainImage(mainWindow)) - return false; - - draw_endRenderPass(); - m_state.currentPipeline = VK_NULL_HANDLE; - - ImGui_ImplVulkan_CreateFontsTexture(m_state.currentCommandBuffer); - ImGui_ImplVulkan_NewFrame(m_state.currentCommandBuffer, chainInfo.m_swapchainFramebuffers[chainInfo.swapchainImageIndex], chainInfo.getExtent()); - ImGui_UpdateWindowInformation(mainWindow); - ImGui::NewFrame(); - return true; + // No swapchain image to draw the overlay into (ImguiInit) + Renderer::ImguiBegin(mainWindow); + return false; } void VulkanRenderer::ImguiEnd() @@ -2559,20 +1606,11 @@ void VulkanRenderer::DeleteFontTextures() } +// Began a frame on the core's own swapchain image. There is none: frames go +// into the frontend's image (DrawBackbufferQuad), so an empty frame is no frame. bool VulkanRenderer::BeginFrame(bool mainWindow) { - if (!AcquireNextSwapchainImage(mainWindow)) - return false; - - auto& chainInfo = GetChainInfo(mainWindow); - - VkClearColorValue clearColor{ 0, 0, 0, 0 }; - ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - - // mark current swapchain image as well defined - chainInfo.hasDefinedSwapchainImage = true; - - return true; + return false; } void VulkanRenderer::DrawEmptyFrame(bool mainWindow) @@ -2933,30 +1971,6 @@ void VulkanRenderer::CreatePipelineCache() m_pipeline_cache_save_thread = std::thread(&VulkanRenderer::PipelineCacheSaveThread, this, cache_size); } -void VulkanRenderer::swapchain_createDescriptorSetLayout() -{ - VkDescriptorSetLayoutBinding bindings[2]{}; - VkDescriptorSetLayoutBinding& samplerLayoutBinding = bindings[0]; - samplerLayoutBinding.binding = 0; - samplerLayoutBinding.descriptorCount = 1; - samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - samplerLayoutBinding.pImmutableSamplers = nullptr; - samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - - VkDescriptorSetLayoutBinding& uniformBufferBinding = bindings[1]; - uniformBufferBinding.binding = 1; - uniformBufferBinding.descriptorCount = 1; - uniformBufferBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - uniformBufferBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - - VkDescriptorSetLayoutCreateInfo layoutInfo = {}; - layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = std::size(bindings); - layoutInfo.pBindings = bindings; - - if (vkCreateDescriptorSetLayout(m_logicalDevice, &layoutInfo, nullptr, &m_swapchainDescriptorSetLayout) != VK_SUCCESS) - UnrecoverableError("failed to create descriptor set layout for swapchain"); -} void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isDepth, Latte::E_DIM dim, sint32 width, sint32 height, FormatInfoVK* formatInfoOut) { @@ -3399,293 +2413,6 @@ VkPipelineShaderStageCreateInfo VulkanRenderer::CreatePipelineShaderStageCreateI return shaderStageInfo; } -VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSetLayout descriptorLayout, bool padView, RendererOutputShader* shader) -{ - auto& chainInfo = GetChainInfo(!padView); - - RendererShaderVk* vertexRendererShader = static_cast(shader->GetVertexShader()); - RendererShaderVk* fragmentRendererShader = static_cast(shader->GetFragmentShader()); - - uint64 hash = 0; - hash += (uint64)vertexRendererShader; - hash += (uint64)fragmentRendererShader; - hash += ((uint64)padView) << 1; - - const auto it = m_backbufferBlitPipelineCache.find(hash); - if (it != m_backbufferBlitPipelineCache.cend()) - return it->second; - - std::vector shaderStages; - if (vertexRendererShader) - shaderStages.emplace_back(CreatePipelineShaderStageCreateInfo(VK_SHADER_STAGE_VERTEX_BIT, vertexRendererShader->GetShaderModule(), "main")); - - if (fragmentRendererShader) - shaderStages.emplace_back(CreatePipelineShaderStageCreateInfo(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentRendererShader->GetShaderModule(), "main")); - - VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; - vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo.vertexBindingDescriptionCount = 0; - vertexInputInfo.vertexAttributeDescriptionCount = 0; - - VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; - inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - inputAssembly.primitiveRestartEnable = VK_FALSE; - - VkPipelineViewportStateCreateInfo viewportState{}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.scissorCount = 1; - - VkDynamicState dynamicStates[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; - - VkPipelineDynamicStateCreateInfo dynamicState = {}; - dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamicState.dynamicStateCount = std::size(dynamicStates); - dynamicState.pDynamicStates = dynamicStates; - - VkPipelineRasterizationStateCreateInfo rasterizer{}; - rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizer.depthClampEnable = VK_FALSE; - rasterizer.rasterizerDiscardEnable = VK_FALSE; - rasterizer.polygonMode = VK_POLYGON_MODE_FILL; - rasterizer.lineWidth = 1.0f; - rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; - rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; - rasterizer.depthBiasEnable = VK_FALSE; - - VkPipelineMultisampleStateCreateInfo multisampling{}; - multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - - VkPipelineColorBlendAttachmentState colorBlendAttachment{}; - colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - - VkPipelineColorBlendStateCreateInfo colorBlending{}; - colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlending.logicOpEnable = VK_FALSE; - colorBlending.logicOp = VK_LOGIC_OP_COPY; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; - colorBlending.blendConstants[0] = 0.0f; - colorBlending.blendConstants[1] = 0.0f; - colorBlending.blendConstants[2] = 0.0f; - colorBlending.blendConstants[3] = 0.0f; - - VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = &descriptorLayout; - - VkResult result; - if (m_pipelineLayout == VK_NULL_HANDLE) - { - result = vkCreatePipelineLayout(m_logicalDevice, &pipelineLayoutInfo, nullptr, &m_pipelineLayout); - if (result != VK_SUCCESS) - throw std::runtime_error(fmt::format("Failed to create pipeline layout: {}", result)); - } - - VkGraphicsPipelineCreateInfo pipelineInfo = {}; - pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineInfo.stageCount = shaderStages.size(); - pipelineInfo.pStages = shaderStages.data(); - pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &inputAssembly; - pipelineInfo.pViewportState = &viewportState; - pipelineInfo.pDynamicState = &dynamicState; - pipelineInfo.pRasterizationState = &rasterizer; - pipelineInfo.pMultisampleState = &multisampling; - pipelineInfo.pColorBlendState = &colorBlending; - pipelineInfo.layout = m_pipelineLayout; - pipelineInfo.renderPass = chainInfo.m_swapchainRenderPass; - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - - VkPipeline pipeline = nullptr; - std::shared_lock lock(m_pipeline_cache_save_mutex); - result = vkCreateGraphicsPipelines(m_logicalDevice, m_pipeline_cache, 1, &pipelineInfo, nullptr, &pipeline); - if (result != VK_SUCCESS) - { - cemuLog_log(LogType::Force, "Failed to create graphics pipeline. Error {}", result); - throw std::runtime_error(fmt::format("Failed to create graphics pipeline: {}", result)); - } - - m_backbufferBlitPipelineCache[hash] = pipeline; - m_pipeline_cache_semaphore.notify(); - - return pipeline; -} - -bool VulkanRenderer::AcquireNextSwapchainImage(bool mainWindow) -{ - if(!IsSwapchainInfoValid(mainWindow)) - return false; - - if(!mainWindow && m_destroyPadSwapchainNextAcquire.test()) - { - RecreateSwapchain(mainWindow, true); - m_destroyPadSwapchainNextAcquire.clear(); - m_destroyPadSwapchainNextAcquire.notify_all(); - return false; - } - - auto& chainInfo = GetChainInfo(mainWindow); - - if (chainInfo.swapchainImageIndex != -1) - return true; // image already reserved - - if (!UpdateSwapchainProperties(mainWindow)) - return false; - - bool result = chainInfo.AcquireImage(); - if (!result) - return false; - - SubmitCommandBuffer(VK_NULL_HANDLE, chainInfo.ConsumeAcquireSemaphore()); - return true; -} - -void VulkanRenderer::RecreateSwapchain(bool mainWindow, bool skipCreate) -{ - SubmitCommandBuffer(); - WaitDeviceIdle(); - auto& chainInfo = GetChainInfo(mainWindow); - - Vector2i size; - if (mainWindow) - { - ImGui_ImplVulkan_Shutdown(); - WindowSystem::GetWindowPhysSize(size.x, size.y); - } - else - { - WindowSystem::GetPadWindowPhysSize(size.x, size.y); - } - - chainInfo.swapchainImageIndex = -1; - chainInfo.Cleanup(); - chainInfo.m_desiredExtent = size; - if(!skipCreate) - { - chainInfo.Create(); - } - - if (mainWindow) - ImguiInit(); -} - -bool VulkanRenderer::UpdateSwapchainProperties(bool mainWindow) -{ - auto& chainInfo = GetChainInfo(mainWindow); - bool stateChanged = chainInfo.m_shouldRecreate; - - const auto configValue = (VSync)GetConfig().vsync.GetValue(); - if(chainInfo.m_vsyncState != configValue) - stateChanged = true; - - int width, height; - if (mainWindow) - WindowSystem::GetWindowPhysSize(width, height); - else - WindowSystem::GetPadWindowPhysSize(width, height); - auto extent = chainInfo.getExtent(); - if (width != extent.width || height != extent.height) - stateChanged = true; - - if(stateChanged) - { - try - { - RecreateSwapchain(mainWindow); - } - catch (std::exception&) - { - cemu_assert_debug(false); - return false; - } - } - - chainInfo.m_shouldRecreate = false; - chainInfo.m_vsyncState = configValue; - return true; -} - -void VulkanRenderer::SwapBuffer(bool mainWindow) -{ - if(!AcquireNextSwapchainImage(mainWindow)) - return; - - auto& chainInfo = GetChainInfo(mainWindow); - - if (!chainInfo.hasDefinedSwapchainImage) - { - // set the swapchain image to a defined state - VkClearColorValue clearColor{ 0, 0, 0, 0 }; - ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - } - - const size_t currentFrameCmdBufferID = GetCurrentCommandBufferId(); - - VkSemaphore presentSemaphore = chainInfo.m_presentSemaphores[chainInfo.swapchainImageIndex]; - SubmitCommandBuffer(presentSemaphore); // submit all command and signal semaphore - - cemu_assert_debug(m_numSubmittedCmdBuffers > 0); - - // wait for the previous frame to finish rendering - WaitCommandBufferFinished(m_commandBufferIDOfPrevFrame); - m_commandBufferIDOfPrevFrame = currentFrameCmdBufferID; - - chainInfo.WaitAvailableFence(); - - VkPresentIdKHR presentId = {}; - - VkPresentInfoKHR presentInfo = {}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &chainInfo.m_swapchain; - presentInfo.pImageIndices = &chainInfo.swapchainImageIndex; - // wait on command buffer semaphore - presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = &presentSemaphore; - - // if present_wait is available and enabled, add frame markers to present requests - // and limit the number of queued present operations - if (m_featureControl.deviceExtensions.present_wait && chainInfo.m_maxQueued > 0) - { - presentId.sType = VK_STRUCTURE_TYPE_PRESENT_ID_KHR; - presentId.swapchainCount = 1; - presentId.pPresentIds = &chainInfo.m_presentId; - - presentInfo.pNext = &presentId; - - if(chainInfo.m_queueDepth >= chainInfo.m_maxQueued) - { - uint64 waitFrameId = chainInfo.m_presentId - chainInfo.m_queueDepth; - vkWaitForPresentKHR(m_logicalDevice, chainInfo.m_swapchain, waitFrameId, 40'000'000); - chainInfo.m_queueDepth--; - } - } - - VkResult result = vkQueuePresentKHR(m_presentQueue, &presentInfo); - if (result < 0 && result != VK_ERROR_OUT_OF_DATE_KHR) - { - throw std::runtime_error(fmt::format("Failed to present image: {}", result)); - } - if(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) - chainInfo.m_shouldRecreate = true; - - if(result >= 0) - { - chainInfo.m_queueDepth++; - chainInfo.m_presentId++; - } - - chainInfo.hasDefinedSwapchainImage = false; - - chainInfo.swapchainImageIndex = -1; -} // One cursor pixel per copy region would be hundreds of regions; a run of // same-coloured pixels in a row is one region, read from a buffer that holds a @@ -3839,17 +2566,10 @@ void VulkanRenderer::SwapBuffers(bool swapTV, bool swapDRC) VulkanBenchmarkPrintResults(); } +// Cleared the core's own swapchain image, of which there is none: the frontend +// clears its own void VulkanRenderer::ClearColorbuffer(bool padView) { - if (!IsSwapchainInfoValid(!padView)) - return; - - auto& chainInfo = GetChainInfo(!padView); - if (chainInfo.swapchainImageIndex == -1) - return; - - VkClearColorValue clearColor{ 0, 0, 0, 0 }; - ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); } void VulkanRenderer::ClearColorImageRaw(VkImage image, uint32 sliceIndex, uint32 mipIndex, const VkClearColorValue& color, VkImageLayout inputLayout, VkImageLayout outputLayout) @@ -4058,80 +2778,6 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu // one never showed - a repeated frame and a dropped one (NNshi, MK8). m_libretroFramePending = true; } - return; - - if(!AcquireNextSwapchainImage(!padView)) - return; - - auto& chainInfo = GetChainInfo(!padView); - LatteTextureViewVk* texViewVk = (LatteTextureViewVk*)texView; - draw_endRenderPass(); - - // barrier for input texture - VkMemoryBarrier memoryBarrier{}; - memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; - VkPipelineStageFlags srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; - VkPipelineStageFlags dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - memoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; - memoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT; - vkCmdPipelineBarrier(m_state.currentCommandBuffer, srcStage, dstStage, 0, 1, &memoryBarrier, 0, nullptr, 0, nullptr); - - auto pipeline = backbufferBlit_createGraphicsPipeline(m_swapchainDescriptorSetLayout, padView, shader); - - VkRenderPassBeginInfo renderPassInfo = {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = chainInfo.m_swapchainRenderPass; - renderPassInfo.framebuffer = chainInfo.m_swapchainFramebuffers[chainInfo.swapchainImageIndex]; - renderPassInfo.renderArea.offset = { 0, 0 }; - renderPassInfo.renderArea.extent = chainInfo.getExtent(); - renderPassInfo.clearValueCount = 0; - - VkViewport viewport{}; - viewport.x = imageX; - viewport.y = imageY; - viewport.width = imageWidth; - viewport.height = imageHeight; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - vkCmdSetViewport(m_state.currentCommandBuffer, 0, 1, &viewport); - - VkRect2D scissor{}; - scissor.extent = chainInfo.getExtent(); - vkCmdSetScissor(m_state.currentCommandBuffer, 0, 1, &scissor); - - auto descriptSet = backbufferBlit_createDescriptorSet(m_swapchainDescriptorSetLayout, texViewVk, useLinearTexFilter); - - vkCmdBeginRenderPass(m_state.currentCommandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - - if (clearBackground) - { - VkClearAttachment clearAttachment{}; - clearAttachment.clearValue = {0,0,0,0}; - clearAttachment.colorAttachment = 0; - clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - VkClearRect clearExtent = {{{0,0},chainInfo.m_actualExtent}, 0, 1}; - vkCmdClearAttachments(m_state.currentCommandBuffer, 1, &clearAttachment, 1, &clearExtent); - } - - vkCmdBindPipeline(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - m_state.currentPipeline = pipeline; - - auto outputUniforms = shader->FillUniformBlockBuffer(*texView, {imageWidth, imageHeight}, padView); - - auto outputUniformOffset = uniformData_uploadUniformDataBufferGetOffset({(uint8*)&outputUniforms, sizeof(decltype(outputUniforms))}); - - vkCmdBindDescriptorSets(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &descriptSet, - 1, &outputUniformOffset); - - vkCmdDraw(m_state.currentCommandBuffer, 6, 1, 0, 0); - - vkCmdEndRenderPass(m_state.currentCommandBuffer); - - // restore viewport - vkCmdSetViewport(m_state.currentCommandBuffer, 0, 1, &m_state.currentViewport); - - // mark current swapchain image as well defined - chainInfo.hasDefinedSwapchainImage = true; } void VulkanRenderer::CreateDescriptorPool() @@ -4157,63 +2803,6 @@ void VulkanRenderer::CreateDescriptorPool() UnrecoverableError("Failed to create descriptor pool!"); } -VkDescriptorSet VulkanRenderer::backbufferBlit_createDescriptorSet(VkDescriptorSetLayout descriptor_set_layout, LatteTextureViewVk* texViewVk, bool useLinearTexFilter) -{ - uint64 hash = 0; - hash += (uint64)texViewVk->GetViewRGBA(); - hash += (uint64)texViewVk->GetDefaultTextureSampler(useLinearTexFilter); - - const auto it = m_backbufferBlitDescriptorSetCache.find(hash); - if (it != m_backbufferBlitDescriptorSetCache.cend()) - return it->second; - - VkDescriptorSetAllocateInfo allocInfo = {}; - allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - allocInfo.descriptorPool = m_descriptorPool; - allocInfo.descriptorSetCount = 1; - allocInfo.pSetLayouts = &descriptor_set_layout; - - VkDescriptorSet result; - if (vkAllocateDescriptorSets(m_logicalDevice, &allocInfo, &result) != VK_SUCCESS) - UnrecoverableError("Failed to allocate descriptor sets for backbuffer blit"); - performanceMonitor.vk.numDescriptorSets.increment(); - - VkDescriptorImageInfo imageInfo = {}; - imageInfo.imageLayout = static_cast(texViewVk->baseTexture)->GetDefaultLayout(); - imageInfo.imageView = texViewVk->GetViewRGBA()->m_textureImageView; - imageInfo.sampler = texViewVk->GetDefaultTextureSampler(useLinearTexFilter); - - VkWriteDescriptorSet descriptorWrites[2]{}; - - VkWriteDescriptorSet& samplerWrite = descriptorWrites[0]; - samplerWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - samplerWrite.dstSet = result; - samplerWrite.dstBinding = 0; - samplerWrite.dstArrayElement = 0; - samplerWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - samplerWrite.descriptorCount = 1; - samplerWrite.pImageInfo = &imageInfo; - - VkWriteDescriptorSet& uniformBufferWrite = descriptorWrites[1]; - uniformBufferWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - uniformBufferWrite.dstSet = result; - uniformBufferWrite.dstBinding = 1; - uniformBufferWrite.descriptorCount = 1; - uniformBufferWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - - VkDescriptorBufferInfo uniformBufferInfo{}; - uniformBufferInfo.buffer = m_uniformVarBuffer; - uniformBufferInfo.offset = 0; - uniformBufferInfo.range = sizeof(RendererOutputShader::OutputUniformVariables); - uniformBufferWrite.pBufferInfo = &uniformBufferInfo; - - - vkUpdateDescriptorSets(m_logicalDevice, std::size(descriptorWrites), descriptorWrites, 0, nullptr); - performanceMonitor.vk.numDescriptorSamplerTextures.increment(); - - m_backbufferBlitDescriptorSetCache[hash] = result; - return result; -} void VulkanRenderer::renderTarget_setViewport(float x, float y, float width, float height, float nearZ, float farZ, bool halfZ) { diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 096ecd01..ad74c69f 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -9,7 +9,6 @@ #include "Cafe/HW/Latte/Renderer/Vulkan/LatteTextureViewVk.h" #include "Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VKRMemoryManager.h" -#include "Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h" #include "util/math/vector2.h" #include "util/helpers/Semaphore.h" #include "util/containers/flat_hash_map.hpp" @@ -159,8 +158,6 @@ class VulkanRenderer : public Renderer friend class LatteTextureReadbackInfoVk; friend class PipelineCompiler; - using VSync = SwapchainInfoVk::VSync; - static const inline int UNIFORMVAR_RINGBUFFER_SIZE = 1024 * 1024 * 16; // 16MB static const inline int TEXTURE_READBACK_SIZE = 32 * 1024 * 1024; // 32 MB @@ -190,20 +187,8 @@ public: sint32 texelCountY; }FormatInfoVK; - struct DeviceInfo - { - DeviceInfo(const std::string name, std::span uuid) - : name(name) - { - std::copy(uuid.begin(), uuid.end(), this->uuid.begin()); - } - - std::string name; - std::array uuid; - }; - - static std::vector GetDevices(); - VulkanRenderer(); + // The device is always the frontend's (libretro's Vulkan HW interface): no + // instance, surface or swapchain of the core's own // Constructor that uses an externally created device (for libretro Vulkan HW render) VulkanRenderer(VkInstance instance, VkPhysicalDevice physDevice, VkDevice device, VkQueue queue, uint32_t queueFamilyIndex); virtual ~VulkanRenderer(); @@ -214,12 +199,6 @@ public: void GetDeviceFeatures(); void DetermineVendor(); - void InitializeSurface(const Vector2i& size, bool mainWindow); - - const std::unique_ptr& GetChainInfoPtr(bool mainWindow) const; - SwapchainInfoVk& GetChainInfo(bool mainWindow) const; - - void StopUsingPadAndWait(); bool IsPadWindowActive() override; void HandleScreenshotRequest(LatteTextureView* texView, bool padView) override; @@ -227,19 +206,6 @@ public: void QueryMemoryInfo(); void QueryAvailableFormats(); -#if BOOST_OS_WINDOWS - static VkSurfaceKHR CreateWinSurface(VkInstance instance, HWND hwindow); -#endif -#if (BOOST_OS_LINUX || BOOST_OS_BSD) && !defined(__ANDROID__) && !defined(WEBOS) - static VkSurfaceKHR CreateXlibSurface(VkInstance instance, Display* dpy, Window window); - static VkSurfaceKHR CreateXcbSurface(VkInstance instance, xcb_connection_t* connection, xcb_window_t window); - #ifdef HAS_WAYLAND - static VkSurfaceKHR CreateWaylandSurface(VkInstance instance, wl_display* display, wl_surface* surface); - #endif -#endif - - static VkSurfaceKHR CreateFramebufferSurface(VkInstance instance, struct WindowSystem::WindowHandleInfo& windowInfo); - void AppendOverlayDebugInfo() override; void ImguiInit(); @@ -294,7 +260,6 @@ public: void DrawBackbufferQuad(LatteTextureView* texView, RendererOutputShader* shader, bool useLinearTexFilter, sint32 imageX, sint32 imageY, sint32 imageWidth, sint32 imageHeight, bool padView, bool clearBackground) override; void CreateDescriptorPool(); - VkDescriptorSet backbufferBlit_createDescriptorSet(VkDescriptorSetLayout descriptor_set_layout, LatteTextureViewVk* texViewVk, bool useLinearTexFilter); robin_hood::unordered_flat_map > m_pipeline_info_cache; // using robin_hood::unordered_flat_map is twice as fast (1-2% overall CPU time reduction) void draw_debugPipelineHashState(); @@ -448,10 +413,6 @@ private: bool drawSequenceSkip; // if true, skip draw_execute() }m_state; - std::unique_ptr m_mainSwapchainInfo{}, m_padSwapchainInfo{}; - std::atomic_flag m_destroyPadSwapchainNextAcquire{}; - bool IsSwapchainInfoValid(bool mainWindow) const; - VkRenderPass m_imguiRenderPass = VK_NULL_HANDLE; VkDescriptorPool m_descriptorPool; @@ -464,7 +425,6 @@ private: bool IsComplete() const { return graphicsFamily >= 0 && presentFamily >= 0; } }; - static QueueFamilyIndices FindQueueFamilies(VkSurfaceKHR surface, VkPhysicalDevice device); private: @@ -517,12 +477,8 @@ private: }m_featureControl{}; static bool CheckDeviceExtensionSupport(const VkPhysicalDevice device, FeatureControl& info); - static std::vector CheckInstanceExtensionSupport(FeatureControl& info); - bool UpdateSwapchainProperties(bool mainWindow); - void SwapBuffer(bool mainWindow); - VkDescriptorSetLayout m_swapchainDescriptorSetLayout; VkQueue m_graphicsQueue, m_presentQueue; @@ -530,12 +486,10 @@ private: std::vector CreateQueueCreateInfos(const std::set& uniqueQueueFamilies) const; VkDeviceCreateInfo CreateDeviceCreateInfo(const std::vector& queueCreateInfos, const VkPhysicalDeviceFeatures& deviceFeatures, const void* deviceExtensionStructs, std::vector& used_extensions) const; - static bool IsDeviceSuitable(VkSurfaceKHR surface, const VkPhysicalDevice& device); void CreateCommandPool(); void CreateCommandBuffers(); - void swapchain_createDescriptorSetLayout(); // shader @@ -594,9 +548,6 @@ private: // misc void CreatePipelineCache(); VkPipelineShaderStageCreateInfo CreatePipelineShaderStageCreateInfo(VkShaderStageFlagBits stage, VkShaderModule& module, const char* entryName) const; - VkPipeline backbufferBlit_createGraphicsPipeline(VkDescriptorSetLayout descriptorLayout, bool padView, RendererOutputShader* shader); - bool AcquireNextSwapchainImage(bool mainWindow); - void RecreateSwapchain(bool mainWindow, bool skipCreate = false); // streamout void streamout_setupXfbBuffer(uint32 bufferIndex, sint32 ringBufferOffset, uint32 rangeAddr, uint32 rangeSize) override; @@ -631,7 +582,6 @@ private: bool m_pipeline_cache_wake_ok = false; bool PipelineCacheSaveWait(std::chrono::milliseconds duration); VkPipelineCache m_pipeline_cache{ nullptr }; - std::unordered_map m_backbufferBlitPipelineCache; std::unordered_map m_backbufferBlitDescriptorSetCache; VkPipelineLayout m_pipelineLayout{nullptr}; VkCommandPool m_commandPool{ nullptr }; diff --git a/src/Common/platform.h b/src/Common/platform.h index fcd23da2..59280ec6 100644 --- a/src/Common/platform.h +++ b/src/Common/platform.h @@ -11,12 +11,6 @@ #elif BOOST_OS_BSD #include #endif -#if !defined(__ANDROID__) && !defined(WEBOS) -// Android and webOS are BOOST_OS_LINUX as well, and have no X11. -#include -#include -#include -#endif #include "Common/unix/platform.h" #elif defined(__APPLE__) // Every Apple platform, not only the desktop one. BOOST_OS_MACOS is macOS diff --git a/tools/check_no_wsi.sh b/tools/check_no_wsi.sh new file mode 100755 index 00000000..ae60e4a4 --- /dev/null +++ b/tools/check_no_wsi.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# Fails if the core's own sources or build files bring in a window system: an +# X11, XCB, Wayland, GL or EGL header, a VK_USE_PLATFORM_* define (which makes +# vulkan.h pull those headers in), or a platform surface entry point. The GL +# context and the Vulkan surface and swapchain are the frontend's under +# libretro; a core that includes these needs headers to build that nothing +# else needs, and has code that can never run. +cd "$(dirname "$0")/.." || exit 2 +pattern='#[[:space:]]*include[[:space:]]*[<"](X11/|xcb/|wayland-|GL/|EGL/|GLES[0-9]*/|KHR/)|VK_USE_PLATFORM_|vkCreate(Xlib|Xcb|Wayland|Win32|Android|Metal|MacOS)Surface|(wgl|glX|egl)(CreateContext|MakeCurrent|SwapBuffers|GetProcAddress)' +found=$(grep -rnE "$pattern" src Makefile Makefile.common \ + --include='*.c' --include='*.cpp' --include='*.h' --include='*.hpp' --include='*.mm' \ + --include='Makefile' --include='Makefile.common' 2>/dev/null \ + | grep -v -e '^src/Cafe/HW/Latte/Renderer/Metal/' \ + -e '^src/imgui/imgui_impl_opengl3.cpp:' \ + -e '^[^:]*:[0-9]*:[[:space:]]*\(\*\|//\|# \)') +# Metal: Apple's renderer, which has no window-system headers of its own to +# leave out. imgui's GL backend: its loader includes are alternatives behind +# IMGUI_IMPL_OPENGL_LOADER_*, and the core selects the custom one (GLInclude). +# Comment lines are not code. +if [ -n "$found" ]; then + echo "window-system code or headers in the core (the frontend owns these):" + printf '%s\n' "$found" + exit 1 +fi +echo "no window-system headers, defines or surface entry points"