mirror of
https://github.com/libretro/parallel-gx2.git
synced 2026-11-07 04:25:58 -10:00
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.
This commit is contained in:
1 parent
5de6d7d261
commit
c81b43ba0c
16 files changed
+79
-2077
No files matched your search
@@ -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: |
|
||||
|
||||
+13
-8
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+3
-12
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#if BOOST_OS_MACOS
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
VkSurfaceKHR CreateCocoaSurface(VkInstance instance, void* handle);
|
||||
|
||||
#endif
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<VkSurfaceFormatKHR>& 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<uint32>::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<VkPresentModeKHR>& 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;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/math/vector2.h"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
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<VkSurfaceFormatKHR> formats;
|
||||
std::vector<VkPresentModeKHR> 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<VkPresentModeKHR>& modes);
|
||||
VkSurfaceFormatKHR ChooseSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& 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<VkImage> m_swapchainImages;
|
||||
std::vector<VkImageView> m_swapchainImageViews;
|
||||
std::vector<VkFramebuffer> m_swapchainFramebuffers;
|
||||
std::vector<VkSemaphore> m_presentSemaphores; // indexed by swapchainImageIndex
|
||||
|
||||
VkRenderPass m_swapchainRenderPass = nullptr;
|
||||
|
||||
private:
|
||||
uint32 m_acquireIndex = 0;
|
||||
std::vector<VkSemaphore> m_acquireSemaphores; // indexed by m_acquireIndex
|
||||
VkFence m_imageAvailableFence{};
|
||||
VkFence m_awaitableFence = VK_NULL_HANDLE;
|
||||
VkSemaphore m_currentSemaphore = VK_NULL_HANDLE;
|
||||
|
||||
std::array<uint32, 2> m_swapchainQueueFamilyIndices;
|
||||
};
|
||||
@@ -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 <vulkan/vulkan.h>
|
||||
|
||||
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);
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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<uint8, VK_UUID_SIZE> uuid)
|
||||
: name(name)
|
||||
{
|
||||
std::copy(uuid.begin(), uuid.end(), this->uuid.begin());
|
||||
}
|
||||
|
||||
std::string name;
|
||||
std::array<uint8, VK_UUID_SIZE> uuid;
|
||||
};
|
||||
|
||||
static std::vector<DeviceInfo> 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<SwapchainInfoVk>& 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<uint64, robin_hood::unordered_flat_map<uint64, PipelineInfo*> > 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<SwapchainInfoVk> 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<const char*> 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<VkDeviceQueueCreateInfo> CreateQueueCreateInfos(const std::set<int>& uniqueQueueFamilies) const;
|
||||
VkDeviceCreateInfo CreateDeviceCreateInfo(const std::vector<VkDeviceQueueCreateInfo>& queueCreateInfos, const VkPhysicalDeviceFeatures& deviceFeatures, const void* deviceExtensionStructs, std::vector<const char*>& 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<uint64, VkPipeline> m_backbufferBlitPipelineCache;
|
||||
std::unordered_map<uint64, VkDescriptorSet> m_backbufferBlitDescriptorSetCache;
|
||||
VkPipelineLayout m_pipelineLayout{nullptr};
|
||||
VkCommandPool m_commandPool{ nullptr };
|
||||
|
||||
@@ -11,12 +11,6 @@
|
||||
#elif BOOST_OS_BSD
|
||||
#include <endian.h>
|
||||
#endif
|
||||
#if !defined(__ANDROID__) && !defined(WEBOS)
|
||||
// Android and webOS are BOOST_OS_LINUX as well, and have no X11.
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xrender.h>
|
||||
#include <X11/Xutil.h>
|
||||
#endif
|
||||
#include "Common/unix/platform.h"
|
||||
#elif defined(__APPLE__)
|
||||
// Every Apple platform, not only the desktop one. BOOST_OS_MACOS is macOS
|
||||
|
||||
Executable
+25
@@ -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"
|
||||
Reference in new issue
Block a user