Merge 3cca4b421b
into db7b106f1d
This commit is contained in:
commit
78d5d8691d
13 changed files with 390 additions and 51 deletions
|
@ -66,6 +66,8 @@ void LogSettings() {
|
|||
log_setting("Renderer_UseReactiveFlushing", values.use_reactive_flushing.GetValue());
|
||||
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
|
||||
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
|
||||
log_setting("Renderer_AsyncPresentation", values.async_presentation.GetValue());
|
||||
log_setting("Renderer_UseDXGISwapchain", values.use_dxgi_swapchain.GetValue());
|
||||
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
|
||||
log_setting("Audio_OutputEngine", values.sink_id.GetValue());
|
||||
log_setting("Audio_OutputDevice", values.audio_output_device_id.GetValue());
|
||||
|
@ -209,6 +211,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
|||
values.fsr_sharpening_slider.SetGlobal(true);
|
||||
values.renderer_backend.SetGlobal(true);
|
||||
values.async_presentation.SetGlobal(true);
|
||||
values.use_dxgi_swapchain.SetGlobal(true);
|
||||
values.renderer_force_max_clock.SetGlobal(true);
|
||||
values.vulkan_device.SetGlobal(true);
|
||||
values.fullscreen_mode.SetGlobal(true);
|
||||
|
|
|
@ -437,6 +437,7 @@ struct Values {
|
|||
SwitchableSetting<RendererBackend, true> renderer_backend{
|
||||
RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"};
|
||||
SwitchableSetting<bool> async_presentation{false, "async_presentation"};
|
||||
SwitchableSetting<bool> use_dxgi_swapchain{false, "use_dxgi_swapchain"};
|
||||
SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"};
|
||||
Setting<bool> renderer_debug{false, "debug"};
|
||||
Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
|
||||
|
|
|
@ -293,6 +293,10 @@ add_dependencies(video_core host_shaders)
|
|||
target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
|
||||
target_link_libraries(video_core PRIVATE sirit Vulkan::Headers)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(video_core PRIVATE DXGI.lib DXGUID.lib D3D12.lib)
|
||||
endif()
|
||||
|
||||
if (ENABLE_NSIGHT_AFTERMATH)
|
||||
if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})
|
||||
message(FATAL_ERROR "Environment variable NSIGHT_AFTERMATH_SDK has to be provided")
|
||||
|
|
|
@ -91,7 +91,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
|
|||
surface(CreateSurface(instance, render_window.GetWindowInfo())),
|
||||
device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
|
||||
state_tracker(), scheduler(device, state_tracker),
|
||||
swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
|
||||
swapchain(*surface, emu_window, device, scheduler, render_window.GetFramebufferLayout().width,
|
||||
render_window.GetFramebufferLayout().height, false),
|
||||
present_manager(render_window, device, memory_allocator, scheduler, swapchain),
|
||||
blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, present_manager,
|
||||
|
|
|
@ -415,7 +415,7 @@ void PresentManager::CopyToSwapchain(Frame* frame) {
|
|||
|
||||
const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore();
|
||||
const VkSemaphore render_semaphore = swapchain.CurrentRenderSemaphore();
|
||||
const std::array wait_semaphores = {present_semaphore, *frame->render_ready};
|
||||
const std::array wait_semaphores = {*frame->render_ready, present_semaphore};
|
||||
|
||||
static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
|
@ -425,7 +425,7 @@ void PresentManager::CopyToSwapchain(Frame* frame) {
|
|||
const VkSubmitInfo submit_info{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.pNext = nullptr,
|
||||
.waitSemaphoreCount = 2U,
|
||||
.waitSemaphoreCount = swapchain.IsDXGI() ? 1U : 2U,
|
||||
.pWaitSemaphores = wait_semaphores.data(),
|
||||
.pWaitDstStageMask = wait_stage_masks.data(),
|
||||
.commandBufferCount = 1,
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
#include <array>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/xchar.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/polyfill_ranges.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
|
@ -104,10 +106,17 @@ VkCompositeAlphaFlagBitsKHR ChooseAlphaFlags(const VkSurfaceCapabilitiesKHR& cap
|
|||
|
||||
} // Anonymous namespace
|
||||
|
||||
Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_,
|
||||
u32 width_, u32 height_, bool srgb)
|
||||
: surface{surface_}, device{device_}, scheduler{scheduler_} {
|
||||
Swapchain::Swapchain(VkSurfaceKHR surface_, const Core::Frontend::EmuWindow& emu_window_,
|
||||
const Device& device_, Scheduler& scheduler_, u32 width_, u32 height_,
|
||||
bool srgb)
|
||||
: surface{surface_}, emu_window{emu_window_}, device{device_}, scheduler{scheduler_},
|
||||
use_dxgi{Settings::values.use_dxgi_swapchain.GetValue()} {
|
||||
Create(width_, height_, srgb);
|
||||
#ifdef WIN32
|
||||
if (use_dxgi) {
|
||||
CreateDXGIFactory();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Swapchain::~Swapchain() = default;
|
||||
|
@ -124,16 +133,36 @@ void Swapchain::Create(u32 width_, u32 height_, bool srgb) {
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
if (dxgi_factory && use_dxgi) {
|
||||
const UINT buffer_count = static_cast<UINT>(image_count);
|
||||
dxgi_swapchain->ResizeBuffers(buffer_count, capabilities.currentExtent.width,
|
||||
capabilities.currentExtent.height, DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
0U);
|
||||
}
|
||||
#endif
|
||||
|
||||
Destroy();
|
||||
|
||||
CreateSwapchain(capabilities, srgb);
|
||||
CreateSemaphores();
|
||||
#ifdef WIN32
|
||||
if (dxgi_factory && use_dxgi) {
|
||||
ImportDXGIImages();
|
||||
}
|
||||
#endif
|
||||
|
||||
resource_ticks.clear();
|
||||
resource_ticks.resize(image_count);
|
||||
}
|
||||
|
||||
bool Swapchain::AcquireNextImage() {
|
||||
#ifdef WIN32
|
||||
if (use_dxgi) {
|
||||
image_index = dxgi_swapchain->GetCurrentBackBufferIndex();
|
||||
return is_suboptimal || is_outdated;
|
||||
}
|
||||
#endif
|
||||
const VkResult result = device.GetLogical().AcquireNextImageKHR(
|
||||
*swapchain, std::numeric_limits<u64>::max(), *present_semaphores[frame_index],
|
||||
VK_NULL_HANDLE, &image_index);
|
||||
|
@ -158,6 +187,11 @@ bool Swapchain::AcquireNextImage() {
|
|||
}
|
||||
|
||||
void Swapchain::Present(VkSemaphore render_semaphore) {
|
||||
#ifdef WIN32
|
||||
if (use_dxgi) {
|
||||
return PresentDXGI(render_semaphore);
|
||||
}
|
||||
#endif
|
||||
const auto present_queue{device.GetPresentQueue()};
|
||||
const VkPresentInfoKHR present_info{
|
||||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
|
@ -199,6 +233,8 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
|
|||
VK_PRESENT_MODE_IMMEDIATE_KHR) != present_modes.end();
|
||||
has_fifo_relaxed = std::find(present_modes.begin(), present_modes.end(),
|
||||
VK_PRESENT_MODE_FIFO_RELAXED_KHR) != present_modes.end();
|
||||
current_srgb = srgb;
|
||||
image_view_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)};
|
||||
surface_format = ChooseSwapSurfaceFormat(formats);
|
||||
|
@ -262,11 +298,8 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
|
|||
swapchain = device.GetLogical().CreateSwapchainKHR(swapchain_ci);
|
||||
|
||||
extent = swapchain_ci.imageExtent;
|
||||
current_srgb = srgb;
|
||||
|
||||
images = swapchain.GetImages();
|
||||
image_count = static_cast<u32>(images.size());
|
||||
image_view_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
|
||||
}
|
||||
|
||||
void Swapchain::CreateSemaphores() {
|
||||
|
@ -280,8 +313,19 @@ void Swapchain::CreateSemaphores() {
|
|||
|
||||
void Swapchain::Destroy() {
|
||||
frame_index = 0;
|
||||
present_semaphores.clear();
|
||||
swapchain.reset();
|
||||
#ifdef WIN32
|
||||
if (!use_dxgi) {
|
||||
return;
|
||||
}
|
||||
for (HANDLE handle : shared_handles) {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
shared_handles.clear();
|
||||
present_semaphores.clear();
|
||||
imported_memories.clear();
|
||||
dx_vk_images.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Swapchain::NeedsPresentModeUpdate() const {
|
||||
|
@ -289,4 +333,215 @@ bool Swapchain::NeedsPresentModeUpdate() const {
|
|||
return present_mode != requested_mode;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
void Swapchain::CreateDXGIFactory() {
|
||||
UINT create_factory_flags = 0;
|
||||
CreateDXGIFactory2(create_factory_flags, IID_PPV_ARGS(&dxgi_factory));
|
||||
|
||||
VkPhysicalDeviceIDProperties device_id_props = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR,
|
||||
.pNext = nullptr,
|
||||
};
|
||||
VkPhysicalDeviceProperties2 device_props_2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
|
||||
.pNext = &device_id_props,
|
||||
};
|
||||
|
||||
device.GetPhysical().GetProperties2(device_props_2);
|
||||
if (!device_id_props.deviceLUIDValid) {
|
||||
LOG_ERROR(Render_Vulkan, "Device LUID not valid, DXGI interop not possible");
|
||||
return;
|
||||
}
|
||||
|
||||
const LUID* pluid = reinterpret_cast<LUID*>(&device_id_props.deviceLUID);
|
||||
dxgi_factory->EnumAdapterByLuid(*pluid, IID_PPV_ARGS(&dxgi_adapter));
|
||||
D3D12CreateDevice(dxgi_adapter.Get(), D3D_FEATURE_LEVEL_11_1, IID_PPV_ARGS(&dx_device));
|
||||
|
||||
const UINT node_count = dx_device->GetNodeCount();
|
||||
const UINT node_mask = node_count <= 1 ? 0 : device_id_props.deviceNodeMask;
|
||||
|
||||
const D3D12_COMMAND_QUEUE_DESC dxQDesc = {D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,
|
||||
D3D12_COMMAND_QUEUE_FLAG_NONE, node_mask};
|
||||
dx_device->CreateCommandQueue(&dxQDesc, IID_PPV_ARGS(&dx_command_queue));
|
||||
|
||||
const UINT buffer_count = static_cast<UINT>(image_count);
|
||||
const DXGI_SWAP_CHAIN_DESC1 swapchain_desc = {
|
||||
.Width = extent.width,
|
||||
.Height = extent.height,
|
||||
.Format = DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
.Stereo = FALSE,
|
||||
.SampleDesc = {1, 0},
|
||||
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
|
||||
.BufferCount = buffer_count,
|
||||
.Scaling = DXGI_SCALING_NONE,
|
||||
.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
|
||||
.AlphaMode = DXGI_ALPHA_MODE_IGNORE,
|
||||
.Flags = 0,
|
||||
};
|
||||
|
||||
const HWND hWnd = static_cast<HWND>(emu_window.GetWindowInfo().render_surface);
|
||||
dxgi_factory->CreateSwapChainForHwnd(dx_command_queue.Get(), hWnd, &swapchain_desc, nullptr,
|
||||
nullptr, &dxgi_swapchain1);
|
||||
dxgi_factory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER);
|
||||
dxgi_swapchain1.As(&dxgi_swapchain);
|
||||
|
||||
present_fence = device.GetLogical().CreateFence({
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
});
|
||||
}
|
||||
|
||||
void Swapchain::ImportDXGIImages() {
|
||||
if (!dxgi_factory) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& dld = device.GetLogical();
|
||||
std::vector<ComPtr<ID3D12Resource>> dx_images(image_count);
|
||||
|
||||
images.clear();
|
||||
images.resize(image_count);
|
||||
imported_memories.resize(image_count);
|
||||
shared_handles.resize(image_count);
|
||||
dx_vk_images.resize(image_count);
|
||||
|
||||
for (UINT i = 0; i < dx_images.size(); i++) {
|
||||
dxgi_swapchain->GetBuffer(i, IID_PPV_ARGS(&dx_images[i]));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < dx_images.size(); i++) {
|
||||
const auto& dxImage = dx_images[i];
|
||||
const auto dxImageDesc = dxImage->GetDesc();
|
||||
|
||||
D3D12_HEAP_PROPERTIES dx_image_heap;
|
||||
D3D12_HEAP_FLAGS dx_image_heap_flags;
|
||||
dxImage->GetHeapProperties(&dx_image_heap, &dx_image_heap_flags);
|
||||
|
||||
const VkExternalMemoryImageCreateInfoKHR eii = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT_KHR};
|
||||
|
||||
const VkImageCreateInfo image_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.pNext = &eii,
|
||||
.flags = 0,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = VK_FORMAT_B8G8R8A8_UNORM,
|
||||
.extent =
|
||||
{
|
||||
.width = static_cast<u32>(dxImageDesc.Width),
|
||||
.height = static_cast<u32>(dxImageDesc.Height),
|
||||
.depth = 1,
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
};
|
||||
|
||||
dx_vk_images[i] = dld.CreateImage(image_ci);
|
||||
images[i] = *dx_vk_images[i];
|
||||
|
||||
const std::wstring handle_name = fmt::format(L"DXImage{}", i);
|
||||
dx_device->CreateSharedHandle(dxImage.Get(), NULL, GENERIC_ALL, handle_name.data(),
|
||||
&shared_handles[i]);
|
||||
|
||||
static constexpr u32 BAD_BITS = 0xcdcdcdcd;
|
||||
|
||||
VkMemoryWin32HandlePropertiesKHR win32_memory_props = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR,
|
||||
.pNext = nullptr,
|
||||
.memoryTypeBits = BAD_BITS,
|
||||
};
|
||||
dld.GetMemoryWin32HandlePropertiesKHR(VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT,
|
||||
shared_handles[i], win32_memory_props);
|
||||
|
||||
const auto requirements = dld.GetImageMemoryRequirements(images[i]);
|
||||
if (win32_memory_props.memoryTypeBits == BAD_BITS) [[unlikely]] {
|
||||
win32_memory_props.memoryTypeBits = requirements.memoryTypeBits;
|
||||
} else {
|
||||
win32_memory_props.memoryTypeBits &= requirements.memoryTypeBits;
|
||||
}
|
||||
|
||||
// Find the memory type the image should be placed in.
|
||||
int mem_type_index = -1;
|
||||
const auto properties = device.GetPhysical().GetMemoryProperties().memoryProperties;
|
||||
for (u32 im = 0; im < properties.memoryTypeCount; ++im) {
|
||||
const u32 current_bit = 0x1 << im;
|
||||
if (win32_memory_props.memoryTypeBits == current_bit) {
|
||||
if (properties.memoryTypes[im].propertyFlags &
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
|
||||
mem_type_index = im;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT_MSG(mem_type_index != -1, "Device local import memory not found!");
|
||||
|
||||
// DX12 resources need to be dedicated per the vulkan spec.
|
||||
const VkMemoryDedicatedAllocateInfoKHR dii = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.image = images[i],
|
||||
.buffer = VK_NULL_HANDLE,
|
||||
};
|
||||
|
||||
const VkImportMemoryWin32HandleInfoKHR imi = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR,
|
||||
.pNext = &dii,
|
||||
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT_KHR,
|
||||
.handle = shared_handles[i],
|
||||
.name = nullptr};
|
||||
|
||||
const VkMemoryAllocateInfo alloc_info = {.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = &imi,
|
||||
.allocationSize = requirements.size,
|
||||
.memoryTypeIndex =
|
||||
static_cast<u32>(mem_type_index)};
|
||||
|
||||
// Bind imported memory to created image
|
||||
imported_memories[i] = dld.AllocateMemory(alloc_info);
|
||||
dx_vk_images[i].BindMemory(*imported_memories[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Swapchain::PresentDXGI(VkSemaphore render_semaphore) {
|
||||
const VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
const VkSubmitInfo submit = {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.pNext = nullptr,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = &render_semaphore,
|
||||
.pWaitDstStageMask = &wait_stage,
|
||||
.commandBufferCount = 0,
|
||||
.pCommandBuffers = nullptr,
|
||||
.signalSemaphoreCount = 0,
|
||||
.pSignalSemaphores = nullptr,
|
||||
};
|
||||
const auto present_queue{device.GetPresentQueue()};
|
||||
present_queue.Submit(submit, *present_fence);
|
||||
|
||||
// Wait for the generated image to be rendered before calling present from D3D.
|
||||
// TODO: Is there any better way to sync? Call vulkan semaphore from D3D?
|
||||
present_fence.Wait();
|
||||
present_fence.Reset();
|
||||
|
||||
const DXGI_PRESENT_PARAMETERS present_params = {};
|
||||
dxgi_swapchain->Present1(1, 0, &present_params);
|
||||
|
||||
++frame_index;
|
||||
if (frame_index >= image_count) {
|
||||
frame_index = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -5,6 +5,14 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include <windows.h>
|
||||
#include <wrl.h>
|
||||
using namespace Microsoft::WRL;
|
||||
#endif
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
|
@ -12,6 +20,10 @@ namespace Layout {
|
|||
struct FramebufferLayout;
|
||||
}
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class Device;
|
||||
|
@ -19,8 +31,9 @@ class Scheduler;
|
|||
|
||||
class Swapchain {
|
||||
public:
|
||||
explicit Swapchain(VkSurfaceKHR surface, const Device& device, Scheduler& scheduler, u32 width,
|
||||
u32 height, bool srgb);
|
||||
explicit Swapchain(VkSurfaceKHR surface, const Core::Frontend::EmuWindow& emu_window,
|
||||
const Device& device, Scheduler& scheduler, u32 width, u32 height,
|
||||
bool srgb);
|
||||
~Swapchain();
|
||||
|
||||
/// Creates (or recreates) the swapchain with a given size.
|
||||
|
@ -57,6 +70,11 @@ public:
|
|||
return current_srgb;
|
||||
}
|
||||
|
||||
/// Returns true when images are presented to DXGI swapchain.
|
||||
bool IsDXGI() const {
|
||||
return use_dxgi;
|
||||
}
|
||||
|
||||
VkExtent2D GetSize() const {
|
||||
return extent;
|
||||
}
|
||||
|
@ -118,7 +136,14 @@ private:
|
|||
|
||||
bool NeedsPresentModeUpdate() const;
|
||||
|
||||
#ifdef WIN32
|
||||
void CreateDXGIFactory();
|
||||
void ImportDXGIImages();
|
||||
void PresentDXGI(VkSemaphore render_semaphore);
|
||||
#endif
|
||||
|
||||
const VkSurfaceKHR surface;
|
||||
const Core::Frontend::EmuWindow& emu_window;
|
||||
const Device& device;
|
||||
Scheduler& scheduler;
|
||||
|
||||
|
@ -130,6 +155,19 @@ private:
|
|||
std::vector<vk::Semaphore> present_semaphores;
|
||||
std::vector<vk::Semaphore> render_semaphores;
|
||||
|
||||
#ifdef WIN32
|
||||
ComPtr<IDXGIFactory7> dxgi_factory;
|
||||
ComPtr<IDXGIAdapter4> dxgi_adapter;
|
||||
ComPtr<ID3D12Device5> dx_device;
|
||||
ComPtr<ID3D12CommandQueue> dx_command_queue;
|
||||
ComPtr<IDXGISwapChain1> dxgi_swapchain1;
|
||||
ComPtr<IDXGISwapChain4> dxgi_swapchain;
|
||||
std::vector<vk::DeviceMemory> imported_memories;
|
||||
std::vector<HANDLE> shared_handles;
|
||||
std::vector<vk::Image> dx_vk_images;
|
||||
vk::Fence present_fence;
|
||||
#endif
|
||||
|
||||
u32 width;
|
||||
u32 height;
|
||||
|
||||
|
@ -147,6 +185,7 @@ private:
|
|||
bool current_srgb{};
|
||||
bool is_outdated{};
|
||||
bool is_suboptimal{};
|
||||
bool use_dxgi{};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -194,6 +194,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
|||
X(vkGetMemoryFdKHR);
|
||||
#ifdef _WIN32
|
||||
X(vkGetMemoryWin32HandleKHR);
|
||||
X(vkGetMemoryWin32HandlePropertiesKHR);
|
||||
#endif
|
||||
X(vkGetQueryPoolResults);
|
||||
X(vkGetPipelineExecutablePropertiesKHR);
|
||||
|
|
|
@ -312,6 +312,7 @@ struct DeviceDispatch : InstanceDispatch {
|
|||
PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{};
|
||||
#ifdef _WIN32
|
||||
PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
|
||||
PFN_vkGetMemoryWin32HandlePropertiesKHR vkGetMemoryWin32HandlePropertiesKHR{};
|
||||
#endif
|
||||
PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{};
|
||||
PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{};
|
||||
|
@ -930,6 +931,15 @@ public:
|
|||
return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
|
||||
flags);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
VkResult GetMemoryWin32HandlePropertiesKHR(
|
||||
VkExternalMemoryHandleTypeFlagBits handle_type, HANDLE w_handle,
|
||||
VkMemoryWin32HandlePropertiesKHR& handle_properties) const {
|
||||
return dld->vkGetMemoryWin32HandlePropertiesKHR(handle, handle_type, w_handle,
|
||||
&handle_properties);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class PhysicalDevice {
|
||||
|
|
|
@ -731,6 +731,7 @@ void Config::ReadRendererValues() {
|
|||
|
||||
ReadGlobalSetting(Settings::values.renderer_backend);
|
||||
ReadGlobalSetting(Settings::values.async_presentation);
|
||||
ReadGlobalSetting(Settings::values.use_dxgi_swapchain);
|
||||
ReadGlobalSetting(Settings::values.renderer_force_max_clock);
|
||||
ReadGlobalSetting(Settings::values.vulkan_device);
|
||||
ReadGlobalSetting(Settings::values.fullscreen_mode);
|
||||
|
@ -1359,6 +1360,7 @@ void Config::SaveRendererValues() {
|
|||
static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
|
||||
Settings::values.renderer_backend.UsingGlobal());
|
||||
WriteGlobalSetting(Settings::values.async_presentation);
|
||||
WriteGlobalSetting(Settings::values.use_dxgi_swapchain);
|
||||
WriteGlobalSetting(Settings::values.renderer_force_max_clock);
|
||||
WriteGlobalSetting(Settings::values.vulkan_device);
|
||||
WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()),
|
||||
|
|
|
@ -14,6 +14,13 @@ ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(const Core::System& system_
|
|||
|
||||
SetupPerGameUI();
|
||||
|
||||
#ifdef WIN32
|
||||
static constexpr bool is_win32 = true;
|
||||
#else
|
||||
static constexpr bool is_win32 = false;
|
||||
#endif
|
||||
ui->dxgi_swapchain->setVisible(is_win32);
|
||||
|
||||
SetConfiguration();
|
||||
|
||||
ui->enable_compute_pipelines_checkbox->setVisible(false);
|
||||
|
@ -33,6 +40,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
|
|||
ui->enable_compute_pipelines_checkbox->setEnabled(runtime_lock);
|
||||
|
||||
ui->async_present->setChecked(Settings::values.async_presentation.GetValue());
|
||||
ui->dxgi_swapchain->setChecked(Settings::values.use_dxgi_swapchain.GetValue());
|
||||
ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue());
|
||||
ui->use_reactive_flushing->setChecked(Settings::values.use_reactive_flushing.GetValue());
|
||||
ui->async_astc->setChecked(Settings::values.async_astc.GetValue());
|
||||
|
@ -91,6 +99,8 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
|
|||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines,
|
||||
ui->enable_compute_pipelines_checkbox,
|
||||
enable_compute_pipelines);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_dxgi_swapchain,
|
||||
ui->dxgi_swapchain, dxgi_swapchain);
|
||||
}
|
||||
|
||||
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
|
||||
|
@ -110,6 +120,7 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
|
|||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
|
||||
ui->async_present->setEnabled(Settings::values.async_presentation.UsingGlobal());
|
||||
ui->dxgi_swapchain->setEnabled(Settings::values.use_dxgi_swapchain.UsingGlobal());
|
||||
ui->renderer_force_max_clock->setEnabled(
|
||||
Settings::values.renderer_force_max_clock.UsingGlobal());
|
||||
ui->use_reactive_flushing->setEnabled(Settings::values.use_reactive_flushing.UsingGlobal());
|
||||
|
@ -131,6 +142,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
|
|||
|
||||
ConfigurationShared::SetColoredTristate(ui->async_present, Settings::values.async_presentation,
|
||||
async_present);
|
||||
ConfigurationShared::SetColoredTristate(ui->dxgi_swapchain, Settings::values.use_dxgi_swapchain,
|
||||
dxgi_swapchain);
|
||||
ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock,
|
||||
Settings::values.renderer_force_max_clock,
|
||||
renderer_force_max_clock);
|
||||
|
|
|
@ -39,6 +39,7 @@ private:
|
|||
std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
|
||||
|
||||
ConfigurationShared::CheckState async_present;
|
||||
ConfigurationShared::CheckState dxgi_swapchain;
|
||||
ConfigurationShared::CheckState renderer_force_max_clock;
|
||||
ConfigurationShared::CheckState use_vsync;
|
||||
ConfigurationShared::CheckState async_astc;
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>404</width>
|
||||
<height>376</height>
|
||||
<width>418</width>
|
||||
<height>444</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -72,44 +72,44 @@
|
|||
<item>
|
||||
<widget class="QWidget" name="astc_recompression_layout" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_astc_recompression">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_astc_recompression">
|
||||
<property name="text">
|
||||
<string>ASTC recompression:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="astc_recompression_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ASTC recompression:</string>
|
||||
<string>Uncompressed (Best quality)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="astc_recompression_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Uncompressed (Best quality)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BC1 (Low quality)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BC3 (Medium quality)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BC1 (Low quality)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BC3 (Medium quality)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -191,6 +191,16 @@ Compute pipelines are always enabled on all other drivers.</string>
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="dxgi_swapchain">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Presents frames with a D3D12 swapchain when using Vulkan.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use DXGI swapchain (Vulkan only)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="af_layout" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_1">
|
||||
|
|
Loading…
Add table
Reference in a new issue