Frontend: Add VulkanHostDisplay

This commit is contained in:
Connor McLaughlin 2021-10-21 18:45:14 +10:00 committed by refractionpcsx2
parent a27b6a113a
commit c4ab6280c6
17 changed files with 1843 additions and 10 deletions

View File

@ -885,6 +885,17 @@ set(pcsx2FrontendHeaders
Frontend/OpenGLHostDisplay.h
)
if(USE_VULKAN)
list(APPEND pcsx2FrontendSources
Frontend/VulkanHostDisplay.cpp
Frontend/imgui_impl_vulkan.cpp
)
list(APPEND pcsx2FrontendHeaders
Frontend/imgui_impl_vulkan.h
Frontend/VulkanHostDisplay.h
)
endif()
if(WIN32)
list(APPEND pcsx2FrontendSources
Frontend/D3D11HostDisplay.cpp

View File

@ -403,6 +403,7 @@ struct Pcsx2Config
UseDebugDevice : 1,
UseBlitSwapChain : 1,
DisableShaderCache : 1,
ThreadedPresentation : 1,
OsdShowMessages : 1,
OsdShowSpeed : 1,
OsdShowFPS : 1,

View File

@ -217,7 +217,7 @@ void D3D11HostDisplay::SetVSync(VsyncMode mode)
m_vsync = mode;
}
bool D3D11HostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device)
bool D3D11HostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device)
{
UINT create_flags = 0;
if (debug_device)

View File

@ -42,7 +42,7 @@ public:
bool HasRenderDevice() const override;
bool HasRenderSurface() const override;
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) override;
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device) override;
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
void DestroyRenderDevice() override;

View File

@ -196,7 +196,7 @@ bool OpenGLHostDisplay::HasRenderSurface() const
return m_window_info.type != WindowInfo::Type::Surfaceless;
}
bool OpenGLHostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device)
bool OpenGLHostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device)
{
m_gl_context = GL::Context::Create(wi);
if (!m_gl_context)

View File

@ -36,7 +36,7 @@ public:
bool HasRenderDevice() const override;
bool HasRenderSurface() const override;
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) override;
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device) override;
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
void DestroyRenderDevice() override;

View File

@ -0,0 +1,401 @@
#include "PrecompiledHeader.h"
#include "VulkanHostDisplay.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/ScopedGuard.h"
#include "common/Vulkan/Builders.h"
#include "common/Vulkan/Context.h"
#include "common/Vulkan/ShaderCache.h"
#include "common/Vulkan/StreamBuffer.h"
#include "common/Vulkan/SwapChain.h"
#include "common/Vulkan/Util.h"
#include "imgui.h"
#include "imgui_impl_vulkan.h"
#include <array>
static constexpr u32 SHADER_CACHE_VERSION = 1;
class VulkanHostDisplayTexture : public HostDisplayTexture
{
public:
explicit VulkanHostDisplayTexture(Vulkan::Texture texture)
: m_texture(std::move(texture))
{
}
~VulkanHostDisplayTexture() override = default;
void* GetHandle() const override { return const_cast<Vulkan::Texture*>(&m_texture); }
u32 GetWidth() const override { return m_texture.GetWidth(); }
u32 GetHeight() const override { return m_texture.GetHeight(); }
u32 GetLayers() const override { return m_texture.GetLayers(); }
u32 GetLevels() const override { return m_texture.GetLevels(); }
const Vulkan::Texture& GetTexture() const { return m_texture; }
Vulkan::Texture& GetTexture() { return m_texture; }
private:
Vulkan::Texture m_texture;
};
VulkanHostDisplay::VulkanHostDisplay() = default;
VulkanHostDisplay::~VulkanHostDisplay()
{
pxAssertRel(!g_vulkan_context, "Context should have been destroyed by now");
pxAssertRel(!m_swap_chain, "Swap chain should have been destroyed by now");
}
HostDisplay::RenderAPI VulkanHostDisplay::GetRenderAPI() const { return HostDisplay::RenderAPI::Vulkan; }
void* VulkanHostDisplay::GetRenderDevice() const { return nullptr; }
void* VulkanHostDisplay::GetRenderContext() const { return nullptr; }
void* VulkanHostDisplay::GetRenderSurface() const { return m_swap_chain.get(); }
bool VulkanHostDisplay::ChangeRenderWindow(const WindowInfo& new_wi)
{
g_vulkan_context->WaitForGPUIdle();
if (new_wi.type == WindowInfo::Type::Surfaceless)
{
g_vulkan_context->ExecuteCommandBuffer(true);
m_swap_chain.reset();
m_window_info = new_wi;
return true;
}
// recreate surface in existing swap chain if it already exists
if (m_swap_chain)
{
if (m_swap_chain->RecreateSurface(new_wi))
{
m_window_info = m_swap_chain->GetWindowInfo();
return true;
}
m_swap_chain.reset();
}
WindowInfo wi_copy(new_wi);
VkSurfaceKHR surface = Vulkan::SwapChain::CreateVulkanSurface(
g_vulkan_context->GetVulkanInstance(), g_vulkan_context->GetPhysicalDevice(), &wi_copy);
if (surface == VK_NULL_HANDLE)
{
Console.Error("Failed to create new surface for swap chain");
return false;
}
m_swap_chain = Vulkan::SwapChain::Create(wi_copy, surface, false);
if (!m_swap_chain)
{
Console.Error("Failed to create swap chain");
Vulkan::SwapChain::DestroyVulkanSurface(g_vulkan_context->GetVulkanInstance(), &wi_copy, surface);
return false;
}
m_window_info = m_swap_chain->GetWindowInfo();
return true;
}
void VulkanHostDisplay::ResizeRenderWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
g_vulkan_context->WaitForGPUIdle();
if (!m_swap_chain->ResizeSwapChain(new_window_width, new_window_height))
pxFailRel("Failed to resize swap chain");
m_window_info = m_swap_chain->GetWindowInfo();
}
bool VulkanHostDisplay::SupportsFullscreen() const { return false; }
bool VulkanHostDisplay::IsFullscreen() { return false; }
bool VulkanHostDisplay::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) { return false; }
HostDisplay::AdapterAndModeList VulkanHostDisplay::GetAdapterAndModeList()
{
return StaticGetAdapterAndModeList(m_window_info.type != WindowInfo::Type::Surfaceless ? &m_window_info : nullptr);
}
void VulkanHostDisplay::DestroyRenderSurface()
{
m_window_info = {};
g_vulkan_context->WaitForGPUIdle();
m_swap_chain.reset();
}
static bool UploadBufferToTexture(Vulkan::Texture* texture, u32 width, u32 height, const void* data, u32 data_stride)
{
const u32 tight_stride = Vulkan::Util::GetTexelSize(texture->GetFormat()) * width;
const u32 tight_size = tight_stride * height;
Vulkan::StreamBuffer& buf = g_vulkan_context->GetTextureUploadBuffer();
if (!buf.ReserveMemory(tight_size, g_vulkan_context->GetBufferImageGranularity()))
{
Console.WriteLn("Executing command buffer for UploadBufferToTexture()");
g_vulkan_context->ExecuteCommandBuffer(false);
if (!buf.ReserveMemory(tight_size, g_vulkan_context->GetBufferImageGranularity()))
{
Console.WriteLn("Failed to allocate %u bytes in stream buffer for UploadBufferToTexture()", tight_size);
return false;
}
}
const u32 buf_offset = buf.GetCurrentOffset();
StringUtil::StrideMemCpy(buf.GetCurrentHostPointer(), tight_stride, data, data_stride, tight_stride, height);
buf.CommitMemory(tight_size);
texture->UpdateFromBuffer(
g_vulkan_context->GetCurrentCommandBuffer(), 0, 0, 0, 0, width, height, width, buf.GetBuffer(), buf_offset);
return true;
}
std::unique_ptr<HostDisplayTexture> VulkanHostDisplay::CreateTexture(
u32 width, u32 height, u32 layers, u32 levels, const void* data, u32 data_stride, bool dynamic /* = false */)
{
static constexpr VkFormat vk_format = VK_FORMAT_R8G8B8A8_UNORM;
static constexpr VkImageUsageFlags usage =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
Vulkan::Texture texture;
if (!texture.Create(width, height, levels, layers, vk_format, VK_SAMPLE_COUNT_1_BIT,
(layers > 1) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, usage))
{
return {};
}
texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
if (data)
{
if (!UploadBufferToTexture(&texture, width, height, data, data_stride))
return {};
}
else
{
// clear it instead so we don't read uninitialized data (and keep the validation layer happy!)
static constexpr VkClearColorValue ccv = {};
static constexpr VkImageSubresourceRange isr = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};
vkCmdClearColorImage(
g_vulkan_context->GetCurrentCommandBuffer(), texture.GetImage(), texture.GetLayout(), &ccv, 1u, &isr);
}
texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
return std::make_unique<VulkanHostDisplayTexture>(std::move(texture));
}
void VulkanHostDisplay::UpdateTexture(
HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride)
{
UploadBufferToTexture(
&static_cast<VulkanHostDisplayTexture*>(texture)->GetTexture(), width, height, data, data_stride);
}
void VulkanHostDisplay::SetVSync(VsyncMode mode)
{
if (!m_swap_chain)
return;
// This swap chain should not be used by the current buffer, thus safe to destroy.
g_vulkan_context->WaitForGPUIdle();
m_swap_chain->SetVSync(mode != VsyncMode::Off);
}
bool VulkanHostDisplay::CreateRenderDevice(
const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device)
{
// debug_device = true;
WindowInfo local_wi(wi);
if (!Vulkan::Context::Create(
adapter_name, &local_wi, &m_swap_chain, threaded_presentation, debug_device, debug_device))
{
Console.Error("Failed to create Vulkan context");
m_window_info = {};
return false;
}
m_window_info = m_swap_chain ? m_swap_chain->GetWindowInfo() : local_wi;
return true;
}
bool VulkanHostDisplay::InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device)
{
Vulkan::ShaderCache::Create(shader_cache_directory, SHADER_CACHE_VERSION, debug_device);
return true;
}
bool VulkanHostDisplay::HasRenderDevice() const { return static_cast<bool>(g_vulkan_context); }
bool VulkanHostDisplay::HasRenderSurface() const { return static_cast<bool>(m_swap_chain); }
bool VulkanHostDisplay::CreateImGuiContext()
{
ImGui_ImplVulkan_InitInfo vii = {};
vii.Instance = g_vulkan_context->GetVulkanInstance();
vii.PhysicalDevice = g_vulkan_context->GetPhysicalDevice();
vii.Device = g_vulkan_context->GetDevice();
vii.QueueFamily = g_vulkan_context->GetGraphicsQueueFamilyIndex();
vii.Queue = g_vulkan_context->GetGraphicsQueue();
vii.PipelineCache = g_vulkan_shader_cache->GetPipelineCache();
vii.MinImageCount = m_swap_chain->GetImageCount();
vii.ImageCount = m_swap_chain->GetImageCount();
vii.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
return ImGui_ImplVulkan_Init(&vii, m_swap_chain->GetClearRenderPass());
}
void VulkanHostDisplay::DestroyImGuiContext()
{
g_vulkan_context->WaitForGPUIdle();
ImGui_ImplVulkan_Shutdown();
}
bool VulkanHostDisplay::UpdateImGuiFontTexture()
{
// Just in case we were drawing something.
g_vulkan_context->ExecuteCommandBuffer(true);
ImGui_ImplVulkan_DestroyFontUploadObjects();
return ImGui_ImplVulkan_CreateFontsTexture(g_vulkan_context->GetCurrentCommandBuffer());
}
void VulkanHostDisplay::DestroyRenderDevice()
{
if (!g_vulkan_context)
return;
g_vulkan_context->WaitForGPUIdle();
Vulkan::ShaderCache::Destroy();
DestroyRenderSurface();
Vulkan::Context::Destroy();
}
bool VulkanHostDisplay::MakeRenderContextCurrent() { return true; }
bool VulkanHostDisplay::DoneRenderContextCurrent() { return true; }
bool VulkanHostDisplay::BeginPresent(bool frame_skip)
{
if (frame_skip || !m_swap_chain)
{
ImGui::EndFrame();
return false;
}
// Previous frame needs to be presented before we can acquire the swap chain.
g_vulkan_context->WaitForPresentComplete();
VkResult res = m_swap_chain->AcquireNextImage();
if (res != VK_SUCCESS)
{
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
{
ResizeRenderWindow(0, 0, m_window_info.surface_scale);
res = m_swap_chain->AcquireNextImage();
}
else if (res == VK_ERROR_SURFACE_LOST_KHR)
{
Console.Warning("Surface lost, attempting to recreate");
if (!m_swap_chain->RecreateSurface(m_window_info))
{
Console.Error("Failed to recreate surface after loss");
g_vulkan_context->ExecuteCommandBuffer(false);
m_swap_chain.reset();
return false;
}
res = m_swap_chain->AcquireNextImage();
}
// This can happen when multiple resize events happen in quick succession.
// In this case, just wait until the next frame to try again.
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
{
// Still submit the command buffer, otherwise we'll end up with several frames waiting.
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
g_vulkan_context->ExecuteCommandBuffer(false);
return false;
}
}
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
// Swap chain images start in undefined
Vulkan::Texture& swap_chain_texture = m_swap_chain->GetCurrentTexture();
swap_chain_texture.OverrideImageLayout(VK_IMAGE_LAYOUT_UNDEFINED);
swap_chain_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
const VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr,
m_swap_chain->GetClearRenderPass(), m_swap_chain->GetCurrentFramebuffer(),
{{0, 0}, {swap_chain_texture.GetWidth(), swap_chain_texture.GetHeight()}}, 1u, &clear_value};
vkCmdBeginRenderPass(g_vulkan_context->GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE);
const VkViewport vp{0.0f, 0.0f, static_cast<float>(swap_chain_texture.GetWidth()),
static_cast<float>(swap_chain_texture.GetHeight()), 0.0f, 1.0f};
const VkRect2D scissor{
{0, 0}, {static_cast<u32>(swap_chain_texture.GetWidth()), static_cast<u32>(swap_chain_texture.GetHeight())}};
vkCmdSetViewport(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &vp);
vkCmdSetScissor(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &scissor);
return true;
}
void VulkanHostDisplay::EndPresent()
{
ImGui::Render();
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), g_vulkan_context->GetCurrentCommandBuffer());
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
vkCmdEndRenderPass(g_vulkan_context->GetCurrentCommandBuffer());
m_swap_chain->GetCurrentTexture().TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
g_vulkan_context->SubmitCommandBuffer(m_swap_chain->GetImageAvailableSemaphore(),
m_swap_chain->GetRenderingFinishedSemaphore(), m_swap_chain->GetSwapChain(),
m_swap_chain->GetCurrentImageIndex(), !m_swap_chain->IsVSyncEnabled());
g_vulkan_context->MoveToNextCommandBuffer();
}
HostDisplay::AdapterAndModeList VulkanHostDisplay::StaticGetAdapterAndModeList(const WindowInfo* wi)
{
AdapterAndModeList ret;
std::vector<Vulkan::SwapChain::FullscreenModeInfo> fsmodes;
if (g_vulkan_context)
{
ret.adapter_names = Vulkan::Context::EnumerateGPUNames(g_vulkan_context->GetVulkanInstance());
if (wi)
{
fsmodes = Vulkan::SwapChain::GetSurfaceFullscreenModes(
g_vulkan_context->GetVulkanInstance(), g_vulkan_context->GetPhysicalDevice(), *wi);
}
}
else if (Vulkan::LoadVulkanLibrary())
{
ScopedGuard lib_guard([]() { Vulkan::UnloadVulkanLibrary(); });
VkInstance instance = Vulkan::Context::CreateVulkanInstance(nullptr, false, false);
if (instance != VK_NULL_HANDLE)
{
ScopedGuard instance_guard([&instance]() { vkDestroyInstance(instance, nullptr); });
if (Vulkan::LoadVulkanInstanceFunctions(instance))
ret.adapter_names = Vulkan::Context::EnumerateGPUNames(instance);
}
}
if (!fsmodes.empty())
{
ret.fullscreen_modes.reserve(fsmodes.size());
for (const Vulkan::SwapChain::FullscreenModeInfo& fmi : fsmodes)
{
ret.fullscreen_modes.push_back(GetFullscreenModeString(fmi.width, fmi.height, fmi.refresh_rate));
}
}
return ret;
}

View File

@ -0,0 +1,63 @@
#pragma once
#include "common/Vulkan/Loader.h"
#include "common/Vulkan/StreamBuffer.h"
#include "common/Vulkan/SwapChain.h"
#include "common/WindowInfo.h"
#include "pcsx2/HostDisplay.h"
#include <memory>
#include <string_view>
namespace Vulkan
{
class StreamBuffer;
class SwapChain;
} // namespace Vulkan
class VulkanHostDisplay final : public HostDisplay
{
public:
VulkanHostDisplay();
~VulkanHostDisplay();
RenderAPI GetRenderAPI() const override;
void* GetRenderDevice() const override;
void* GetRenderContext() const override;
void* GetRenderSurface() const override;
bool HasRenderDevice() const override;
bool HasRenderSurface() const override;
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device) override;
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
void DestroyRenderDevice() override;
bool MakeRenderContextCurrent() override;
bool DoneRenderContextCurrent() override;
bool ChangeRenderWindow(const WindowInfo& new_wi) override;
void ResizeRenderWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsFullscreen() const override;
bool IsFullscreen() override;
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
AdapterAndModeList GetAdapterAndModeList() override;
void DestroyRenderSurface() override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels,
const void* data, u32 data_stride, bool dynamic = false) override;
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data,
u32 texture_data_stride) override;
void SetVSync(VsyncMode mode) override;
bool BeginPresent(bool frame_skip) override;
void EndPresent() override;
static AdapterAndModeList StaticGetAdapterAndModeList(const WindowInfo* wi);
protected:
bool CreateImGuiContext() override;
void DestroyImGuiContext() override;
bool UpdateImGuiFontTexture() override;
std::unique_ptr<Vulkan::SwapChain> m_swap_chain;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
// dear imgui: Renderer for Vulkan
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// Missing features:
// [ ] Renderer: User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
// You will use those if you want to use this rendering back-end in your engine/app.
// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
// the back-end itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
// Read comments in imgui_impl_vulkan.h.
#pragma once
#include "imgui.h"
#include "common/Vulkan/Loader.h"
// Initialization data, for ImGui_ImplVulkan_Init()
// [Please zero-clear before use!]
struct ImGui_ImplVulkan_InitInfo
{
VkInstance Instance;
VkPhysicalDevice PhysicalDevice;
VkDevice Device;
uint32_t QueueFamily;
VkQueue Queue;
VkPipelineCache PipelineCache;
uint32_t MinImageCount; // >= 2
uint32_t ImageCount; // >= MinImageCount
VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT
const VkAllocationCallbacks* Allocator;
void (*CheckVkResultFn)(VkResult err);
};
// Called by user code
IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass);
IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown();
IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer);
IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer);
IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontUploadObjects();
IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated)
//-------------------------------------------------------------------------
// Internal / Miscellaneous Vulkan Helpers
// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.)
//-------------------------------------------------------------------------
// You probably do NOT need to use or care about those functions.
// Those functions only exist because:
// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files.
// 2) the upcoming multi-viewport feature will need them internally.
// Generally we avoid exposing any kind of superfluous high-level helpers in the bindings,
// but it is too much code to duplicate everywhere so we exceptionally expose them.
//
// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.).
// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work.
// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions)
//-------------------------------------------------------------------------
struct ImGui_ImplVulkanH_Frame;
struct ImGui_ImplVulkanH_Window;
// Helpers
IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wnd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count);
IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wnd, const VkAllocationCallbacks* allocator);
IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space);
IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count);
IMGUI_IMPL_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode);
// Helper structure to hold the data needed by one rendering frame
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
// [Please zero-clear before use!]
struct ImGui_ImplVulkanH_Frame
{
VkCommandPool CommandPool;
VkCommandBuffer CommandBuffer;
VkFence Fence;
VkImage Backbuffer;
VkImageView BackbufferView;
VkFramebuffer Framebuffer;
};
struct ImGui_ImplVulkanH_FrameSemaphores
{
VkSemaphore ImageAcquiredSemaphore;
VkSemaphore RenderCompleteSemaphore;
};
// Helper structure to hold the data needed by one rendering context into one OS window
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
struct ImGui_ImplVulkanH_Window
{
int Width;
int Height;
VkSwapchainKHR Swapchain;
VkSurfaceKHR Surface;
VkSurfaceFormatKHR SurfaceFormat;
VkPresentModeKHR PresentMode;
VkRenderPass RenderPass;
bool ClearEnable;
VkClearValue ClearValue;
uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount)
uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count)
uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data)
ImGui_ImplVulkanH_Frame* Frames;
ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores;
ImGui_ImplVulkanH_Window()
{
memset(this, 0, sizeof(*this));
PresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR;
ClearEnable = true;
}
};

View File

@ -709,7 +709,8 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
GSConfig.Adapter != old_config.Adapter ||
GSConfig.UseDebugDevice != old_config.UseDebugDevice ||
GSConfig.UseBlitSwapChain != old_config.UseBlitSwapChain ||
GSConfig.DisableShaderCache != old_config.DisableShaderCache
GSConfig.DisableShaderCache != old_config.DisableShaderCache ||
GSConfig.ThreadedPresentation != old_config.ThreadedPresentation
);
GSreopen(do_full_restart);
return;
@ -1300,7 +1301,7 @@ void GSApp::Init()
m_default_configuration["shaderfx_conf"] = "shaders/GS_FX_Settings.ini";
m_default_configuration["shaderfx_glsl"] = "shaders/GS.fx";
m_default_configuration["skip_duplicate_frames"] = "0";
m_default_configuration["threaded_presentation"] = "0";
m_default_configuration["ThreadedPresentation"] = "0";
m_default_configuration["throttle_present_rate"] = "0";
m_default_configuration["TVShader"] = "0";
m_default_configuration["upscale_multiplier"] = "1";

View File

@ -106,6 +106,10 @@ std::string HostDisplay::GetFullscreenModeString(u32 width, u32 height, float re
#include "Frontend/OpenGLHostDisplay.h"
#ifdef ENABLE_VULKAN
#include "Frontend/VulkanHostDisplay.h"
#endif
#ifdef _WIN32
#include "Frontend/D3D11HostDisplay.h"
#endif
@ -123,6 +127,11 @@ std::unique_ptr<HostDisplay> HostDisplay::CreateDisplayForAPI(RenderAPI api)
case HostDisplay::RenderAPI::OpenGLES:
return std::make_unique<OpenGLHostDisplay>();
#ifdef ENABLE_VULKAN
case RenderAPI::Vulkan:
return std::make_unique<VulkanHostDisplay>();
#endif
default:
Console.Error("Unknown render API %u", static_cast<unsigned>(api));
return {};

View File

@ -97,7 +97,7 @@ public:
virtual bool HasRenderDevice() const = 0;
virtual bool HasRenderSurface() const = 0;
virtual bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool debug_device) = 0;
virtual bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, bool threaded_presentation, bool debug_device) = 0;
virtual bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) = 0;
virtual bool MakeRenderContextCurrent() = 0;
virtual bool DoneRenderContextCurrent() = 0;

View File

@ -281,6 +281,7 @@ Pcsx2Config::GSOptions::GSOptions()
UseDebugDevice = false;
UseBlitSwapChain = false;
DisableShaderCache = false;
ThreadedPresentation = false;
OsdShowMessages = true;
OsdShowSpeed = false;
OsdShowFPS = false;
@ -390,7 +391,8 @@ bool Pcsx2Config::GSOptions::RestartOptionsAreEqual(const GSOptions& right) cons
OpEqu(Adapter) &&
OpEqu(UseDebugDevice) &&
OpEqu(UseBlitSwapChain) &&
OpEqu(DisableShaderCache);
OpEqu(DisableShaderCache) &&
OpEqu(ThreadedPresentation);
}
void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
@ -417,6 +419,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
// These are loaded from GSWindow in wx.
SettingsWrapEnumEx(AspectRatio, "AspectRatio", AspectRatioNames);
SettingsWrapEnumEx(FMVAspectRatioSwitch, "FMVAspectRatioSwitch", FMVAspectRatioSwitchNames);
SettingsWrapEntry(Zoom);
SettingsWrapEntry(StretchY);
SettingsWrapEntry(OffsetX);
@ -467,6 +470,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
GSSettingBool(UseDebugDevice);
GSSettingBool(UseBlitSwapChain);
GSSettingBoolEx(DisableShaderCache, "disable_shader_cache");
GSSettingBool(ThreadedPresentation);
GSSettingBool(OsdShowMessages);
GSSettingBool(OsdShowSpeed);
GSSettingBool(OsdShowFPS);

View File

@ -120,7 +120,7 @@ HostDisplay* Host::AcquireHostDisplay(HostDisplay::RenderAPI api)
if (!s_host_display)
return nullptr;
if (!s_host_display->CreateRenderDevice(g_gs_window_info, GSConfig.Adapter, GSConfig.UseDebugDevice) ||
if (!s_host_display->CreateRenderDevice(g_gs_window_info, GSConfig.Adapter, GSConfig.ThreadedPresentation, GSConfig.UseDebugDevice) ||
!s_host_display->InitializeRenderDevice(StringUtil::wxStringToUTF8String(EmuFolders::Cache.ToString()), GSConfig.UseDebugDevice) ||
!ImGuiManager::Initialize())
{

View File

@ -48,7 +48,7 @@
<ForcedIncludeFiles>PrecompiledHeader.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
<AdditionalOptions>/Zc:externConstexpr %(AdditionalOptions)</AdditionalOptions>
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;LZMA_API_STATIC;BUILD_DX=1;SPU2X_CUBEB;DIRECTINPUT_VERSION=0x0800;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;LZMA_API_STATIC;BUILD_DX=1;ENABLE_VULKAN;SPU2X_CUBEB;DIRECTINPUT_VERSION=0x0800;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="$(Configuration.Contains(Debug))">PCSX2_DEBUG;PCSX2_DEVBUILD;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="$(Configuration.Contains(Devel))">PCSX2_DEVEL;PCSX2_DEVBUILD;NDEBUG;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="$(Configuration.Contains(Release))">NDEBUG;_SECURE_SCL_=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -305,7 +305,9 @@
<ClCompile Include="DEV9\Win32\tap-win32.cpp" />
<ClCompile Include="Frontend\D3D11HostDisplay.cpp" />
<ClCompile Include="Frontend\ImGuiManager.cpp" />
<ClCompile Include="Frontend\imgui_impl_vulkan.cpp" />
<ClCompile Include="Frontend\OpenGLHostDisplay.cpp" />
<ClCompile Include="Frontend\VulkanHostDisplay.cpp" />
<ClCompile Include="GameDatabase.cpp" />
<ClCompile Include="Gif_Logger.cpp" />
<ClCompile Include="Gif_Unit.cpp" />
@ -748,6 +750,7 @@
<ClInclude Include="Frontend\D3D11HostDisplay.h" />
<ClInclude Include="Frontend\ImGuiManager.h" />
<ClInclude Include="Frontend\OpenGLHostDisplay.h" />
<ClInclude Include="Frontend\VulkanHostDisplay.h" />
<ClInclude Include="GameDatabase.h" />
<ClInclude Include="Gif_Unit.h" />
<ClInclude Include="GS\Renderers\DX11\D3D.h" />

View File

@ -1664,6 +1664,12 @@
<ClCompile Include="Host.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="Frontend\VulkanHostDisplay.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="Frontend\imgui_impl_vulkan.cpp">
<Filter>Host</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Patch.h">
@ -2779,6 +2785,9 @@
<ClInclude Include="Frontend\ImGuiManager.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="Frontend\VulkanHostDisplay.h">
<Filter>Host</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="windows\wxResources.rc">