From c81b43ba0c263b6c3be0a159dbb238e71096d74d Mon Sep 17 00:00:00 2001 From: libretroadmin Date: Sat, 7 Nov 2026 12:59:23 +0100 Subject: [PATCH] Vulkan through the frontend alone: no surfaces, swapchain or instance setup Under libretro the Vulkan instance, device, surface and swapchain are the frontend's: the core is handed a device and queue (the shared-device constructor) and draws into the image the frontend gives it. Everything the standalone emulator used to make its own was still compiled in and never ran: the default constructor with its instance creation and device selection, GetDevices, the X11, XCB, Wayland, Win32 and Metal surface creation (CocoaSurface.mm), SwapchainInfoVk, swapchain acquire, recreate and present, the swapchain blit pipeline and descriptor layout, and the Breath of the Wild workaround that forked a child process to probe RADV before creating an instance. All of it is removed (SwapchainInfoVk and CocoaSurface are listed in .upstream-excluded). What touched the swapchain on the way keeps its libretro behaviour: BeginFrame reports no frame, ClearColorbuffer and ImguiInit do nothing, ImguiBegin returns false after the base class's setup, IsPadWindowActive follows the DRC layout, and a screenshot is always a single one. With no VK_USE_PLATFORM_* defined, vulkan.h includes no window-system header, and Common/platform.h no longer includes Xlib, Xrender or Xutil: the Linux build needs no headers beyond the compiler's, so the CI jobs and the README install none. tools/check_no_wsi.sh, run in both CIs, fails on an X11, XCB, Wayland, GL or EGL include, a VK_USE_PLATFORM_* define or a platform surface or context entry point in the core. --- .github/workflows/libretro.yml | 17 +- .gitlab-ci.yml | 21 +- .upstream-excluded | 4 + CHANGELOG.md | 1 + Makefile | 2 +- Makefile.common | 15 +- README.md | 8 +- .../HW/Latte/Renderer/Vulkan/CocoaSurface.h | 9 - .../HW/Latte/Renderer/Vulkan/CocoaSurface.mm | 31 - .../Latte/Renderer/Vulkan/SwapchainInfoVk.cpp | 402 ----- .../Latte/Renderer/Vulkan/SwapchainInfoVk.h | 95 -- src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h | 29 +- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 1437 +---------------- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 54 +- src/Common/platform.h | 6 - tools/check_no_wsi.sh | 25 + 16 files changed, 79 insertions(+), 2077 deletions(-) delete mode 100644 src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.h delete mode 100644 src/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.mm delete mode 100644 src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp delete mode 100644 src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h create mode 100755 tools/check_no_wsi.sh 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"