renderer_vulkan: Implement DXGI swapchain

This commit is contained in:
GPUCode 2023-06-01 02:32:08 +03:00
parent 05e38ee149
commit 3cca4b421b
13 changed files with 390 additions and 51 deletions

View file

@ -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);

View file

@ -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"};

View file

@ -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")

View file

@ -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,

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -194,6 +194,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkGetMemoryFdKHR);
#ifdef _WIN32
X(vkGetMemoryWin32HandleKHR);
X(vkGetMemoryWin32HandlePropertiesKHR);
#endif
X(vkGetQueryPoolResults);
X(vkGetPipelineExecutablePropertiesKHR);

View file

@ -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 {

View file

@ -695,6 +695,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);
@ -1323,6 +1324,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()),

View file

@ -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);

View file

@ -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;

View file

@ -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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Presents frames with a D3D12 swapchain when using Vulkan.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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">