renderer_vulkan: Implement DXGI swapchain
This commit is contained in:
parent
05e38ee149
commit
3cca4b421b
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_UseReactiveFlushing", values.use_reactive_flushing.GetValue());
|
||||||
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
|
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
|
||||||
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.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("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
|
||||||
log_setting("Audio_OutputEngine", values.sink_id.GetValue());
|
log_setting("Audio_OutputEngine", values.sink_id.GetValue());
|
||||||
log_setting("Audio_OutputDevice", values.audio_output_device_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.fsr_sharpening_slider.SetGlobal(true);
|
||||||
values.renderer_backend.SetGlobal(true);
|
values.renderer_backend.SetGlobal(true);
|
||||||
values.async_presentation.SetGlobal(true);
|
values.async_presentation.SetGlobal(true);
|
||||||
|
values.use_dxgi_swapchain.SetGlobal(true);
|
||||||
values.renderer_force_max_clock.SetGlobal(true);
|
values.renderer_force_max_clock.SetGlobal(true);
|
||||||
values.vulkan_device.SetGlobal(true);
|
values.vulkan_device.SetGlobal(true);
|
||||||
values.fullscreen_mode.SetGlobal(true);
|
values.fullscreen_mode.SetGlobal(true);
|
||||||
|
|
|
@ -437,6 +437,7 @@ struct Values {
|
||||||
SwitchableSetting<RendererBackend, true> renderer_backend{
|
SwitchableSetting<RendererBackend, true> renderer_backend{
|
||||||
RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"};
|
RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"};
|
||||||
SwitchableSetting<bool> async_presentation{false, "async_presentation"};
|
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"};
|
SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"};
|
||||||
Setting<bool> renderer_debug{false, "debug"};
|
Setting<bool> renderer_debug{false, "debug"};
|
||||||
Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
|
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_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
|
||||||
target_link_libraries(video_core PRIVATE sirit Vulkan::Headers)
|
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 (ENABLE_NSIGHT_AFTERMATH)
|
||||||
if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})
|
if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK})
|
||||||
message(FATAL_ERROR "Environment variable NSIGHT_AFTERMATH_SDK has to be provided")
|
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())),
|
surface(CreateSurface(instance, render_window.GetWindowInfo())),
|
||||||
device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
|
device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
|
||||||
state_tracker(), scheduler(device, state_tracker),
|
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),
|
render_window.GetFramebufferLayout().height, false),
|
||||||
present_manager(render_window, device, memory_allocator, scheduler, swapchain),
|
present_manager(render_window, device, memory_allocator, scheduler, swapchain),
|
||||||
blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, present_manager,
|
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 present_semaphore = swapchain.CurrentPresentSemaphore();
|
||||||
const VkSemaphore render_semaphore = swapchain.CurrentRenderSemaphore();
|
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{
|
static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
|
||||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
|
@ -425,7 +425,7 @@ void PresentManager::CopyToSwapchain(Frame* frame) {
|
||||||
const VkSubmitInfo submit_info{
|
const VkSubmitInfo submit_info{
|
||||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.waitSemaphoreCount = 2U,
|
.waitSemaphoreCount = swapchain.IsDXGI() ? 1U : 2U,
|
||||||
.pWaitSemaphores = wait_semaphores.data(),
|
.pWaitSemaphores = wait_semaphores.data(),
|
||||||
.pWaitDstStageMask = wait_stage_masks.data(),
|
.pWaitDstStageMask = wait_stage_masks.data(),
|
||||||
.commandBufferCount = 1,
|
.commandBufferCount = 1,
|
||||||
|
|
|
@ -5,11 +5,13 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <fmt/xchar.h>
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/polyfill_ranges.h"
|
#include "common/polyfill_ranges.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.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_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||||
#include "video_core/vulkan_common/vulkan_device.h"
|
#include "video_core/vulkan_common/vulkan_device.h"
|
||||||
|
@ -104,10 +106,17 @@ VkCompositeAlphaFlagBitsKHR ChooseAlphaFlags(const VkSurfaceCapabilitiesKHR& cap
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_,
|
Swapchain::Swapchain(VkSurfaceKHR surface_, const Core::Frontend::EmuWindow& emu_window_,
|
||||||
u32 width_, u32 height_, bool srgb)
|
const Device& device_, Scheduler& scheduler_, u32 width_, u32 height_,
|
||||||
: surface{surface_}, device{device_}, scheduler{scheduler_} {
|
bool srgb)
|
||||||
|
: surface{surface_}, emu_window{emu_window_}, device{device_}, scheduler{scheduler_},
|
||||||
|
use_dxgi{Settings::values.use_dxgi_swapchain.GetValue()} {
|
||||||
Create(width_, height_, srgb);
|
Create(width_, height_, srgb);
|
||||||
|
#ifdef WIN32
|
||||||
|
if (use_dxgi) {
|
||||||
|
CreateDXGIFactory();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Swapchain::~Swapchain() = default;
|
Swapchain::~Swapchain() = default;
|
||||||
|
@ -124,16 +133,36 @@ void Swapchain::Create(u32 width_, u32 height_, bool srgb) {
|
||||||
return;
|
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();
|
Destroy();
|
||||||
|
|
||||||
CreateSwapchain(capabilities, srgb);
|
CreateSwapchain(capabilities, srgb);
|
||||||
CreateSemaphores();
|
CreateSemaphores();
|
||||||
|
#ifdef WIN32
|
||||||
|
if (dxgi_factory && use_dxgi) {
|
||||||
|
ImportDXGIImages();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
resource_ticks.clear();
|
resource_ticks.clear();
|
||||||
resource_ticks.resize(image_count);
|
resource_ticks.resize(image_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Swapchain::AcquireNextImage() {
|
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(
|
const VkResult result = device.GetLogical().AcquireNextImageKHR(
|
||||||
*swapchain, std::numeric_limits<u64>::max(), *present_semaphores[frame_index],
|
*swapchain, std::numeric_limits<u64>::max(), *present_semaphores[frame_index],
|
||||||
VK_NULL_HANDLE, &image_index);
|
VK_NULL_HANDLE, &image_index);
|
||||||
|
@ -158,6 +187,11 @@ bool Swapchain::AcquireNextImage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Swapchain::Present(VkSemaphore render_semaphore) {
|
void Swapchain::Present(VkSemaphore render_semaphore) {
|
||||||
|
#ifdef WIN32
|
||||||
|
if (use_dxgi) {
|
||||||
|
return PresentDXGI(render_semaphore);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
const auto present_queue{device.GetPresentQueue()};
|
const auto present_queue{device.GetPresentQueue()};
|
||||||
const VkPresentInfoKHR present_info{
|
const VkPresentInfoKHR present_info{
|
||||||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
.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();
|
VK_PRESENT_MODE_IMMEDIATE_KHR) != present_modes.end();
|
||||||
has_fifo_relaxed = std::find(present_modes.begin(), present_modes.end(),
|
has_fifo_relaxed = std::find(present_modes.begin(), present_modes.end(),
|
||||||
VK_PRESENT_MODE_FIFO_RELAXED_KHR) != 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)};
|
const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)};
|
||||||
surface_format = ChooseSwapSurfaceFormat(formats);
|
surface_format = ChooseSwapSurfaceFormat(formats);
|
||||||
|
@ -262,11 +298,8 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
|
||||||
swapchain = device.GetLogical().CreateSwapchainKHR(swapchain_ci);
|
swapchain = device.GetLogical().CreateSwapchainKHR(swapchain_ci);
|
||||||
|
|
||||||
extent = swapchain_ci.imageExtent;
|
extent = swapchain_ci.imageExtent;
|
||||||
current_srgb = srgb;
|
|
||||||
|
|
||||||
images = swapchain.GetImages();
|
images = swapchain.GetImages();
|
||||||
image_count = static_cast<u32>(images.size());
|
image_count = static_cast<u32>(images.size());
|
||||||
image_view_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Swapchain::CreateSemaphores() {
|
void Swapchain::CreateSemaphores() {
|
||||||
|
@ -280,8 +313,19 @@ void Swapchain::CreateSemaphores() {
|
||||||
|
|
||||||
void Swapchain::Destroy() {
|
void Swapchain::Destroy() {
|
||||||
frame_index = 0;
|
frame_index = 0;
|
||||||
present_semaphores.clear();
|
|
||||||
swapchain.reset();
|
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 {
|
bool Swapchain::NeedsPresentModeUpdate() const {
|
||||||
|
@ -289,4 +333,215 @@ bool Swapchain::NeedsPresentModeUpdate() const {
|
||||||
return present_mode != requested_mode;
|
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
|
} // namespace Vulkan
|
||||||
|
|
|
@ -5,6 +5,14 @@
|
||||||
|
|
||||||
#include <vector>
|
#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 "common/common_types.h"
|
||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||||
|
|
||||||
|
@ -12,6 +20,10 @@ namespace Layout {
|
||||||
struct FramebufferLayout;
|
struct FramebufferLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Core::Frontend {
|
||||||
|
class EmuWindow;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
class Device;
|
class Device;
|
||||||
|
@ -19,8 +31,9 @@ class Scheduler;
|
||||||
|
|
||||||
class Swapchain {
|
class Swapchain {
|
||||||
public:
|
public:
|
||||||
explicit Swapchain(VkSurfaceKHR surface, const Device& device, Scheduler& scheduler, u32 width,
|
explicit Swapchain(VkSurfaceKHR surface, const Core::Frontend::EmuWindow& emu_window,
|
||||||
u32 height, bool srgb);
|
const Device& device, Scheduler& scheduler, u32 width, u32 height,
|
||||||
|
bool srgb);
|
||||||
~Swapchain();
|
~Swapchain();
|
||||||
|
|
||||||
/// Creates (or recreates) the swapchain with a given size.
|
/// Creates (or recreates) the swapchain with a given size.
|
||||||
|
@ -57,6 +70,11 @@ public:
|
||||||
return current_srgb;
|
return current_srgb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true when images are presented to DXGI swapchain.
|
||||||
|
bool IsDXGI() const {
|
||||||
|
return use_dxgi;
|
||||||
|
}
|
||||||
|
|
||||||
VkExtent2D GetSize() const {
|
VkExtent2D GetSize() const {
|
||||||
return extent;
|
return extent;
|
||||||
}
|
}
|
||||||
|
@ -118,7 +136,14 @@ private:
|
||||||
|
|
||||||
bool NeedsPresentModeUpdate() const;
|
bool NeedsPresentModeUpdate() const;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
void CreateDXGIFactory();
|
||||||
|
void ImportDXGIImages();
|
||||||
|
void PresentDXGI(VkSemaphore render_semaphore);
|
||||||
|
#endif
|
||||||
|
|
||||||
const VkSurfaceKHR surface;
|
const VkSurfaceKHR surface;
|
||||||
|
const Core::Frontend::EmuWindow& emu_window;
|
||||||
const Device& device;
|
const Device& device;
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
|
|
||||||
|
@ -130,6 +155,19 @@ private:
|
||||||
std::vector<vk::Semaphore> present_semaphores;
|
std::vector<vk::Semaphore> present_semaphores;
|
||||||
std::vector<vk::Semaphore> render_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 width;
|
||||||
u32 height;
|
u32 height;
|
||||||
|
|
||||||
|
@ -147,6 +185,7 @@ private:
|
||||||
bool current_srgb{};
|
bool current_srgb{};
|
||||||
bool is_outdated{};
|
bool is_outdated{};
|
||||||
bool is_suboptimal{};
|
bool is_suboptimal{};
|
||||||
|
bool use_dxgi{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -194,6 +194,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||||
X(vkGetMemoryFdKHR);
|
X(vkGetMemoryFdKHR);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
X(vkGetMemoryWin32HandleKHR);
|
X(vkGetMemoryWin32HandleKHR);
|
||||||
|
X(vkGetMemoryWin32HandlePropertiesKHR);
|
||||||
#endif
|
#endif
|
||||||
X(vkGetQueryPoolResults);
|
X(vkGetQueryPoolResults);
|
||||||
X(vkGetPipelineExecutablePropertiesKHR);
|
X(vkGetPipelineExecutablePropertiesKHR);
|
||||||
|
|
|
@ -312,6 +312,7 @@ struct DeviceDispatch : InstanceDispatch {
|
||||||
PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{};
|
PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{};
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
|
PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
|
||||||
|
PFN_vkGetMemoryWin32HandlePropertiesKHR vkGetMemoryWin32HandlePropertiesKHR{};
|
||||||
#endif
|
#endif
|
||||||
PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{};
|
PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{};
|
||||||
PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{};
|
PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{};
|
||||||
|
@ -930,6 +931,15 @@ public:
|
||||||
return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
|
return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
|
||||||
flags);
|
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 {
|
class PhysicalDevice {
|
||||||
|
|
|
@ -695,6 +695,7 @@ void Config::ReadRendererValues() {
|
||||||
|
|
||||||
ReadGlobalSetting(Settings::values.renderer_backend);
|
ReadGlobalSetting(Settings::values.renderer_backend);
|
||||||
ReadGlobalSetting(Settings::values.async_presentation);
|
ReadGlobalSetting(Settings::values.async_presentation);
|
||||||
|
ReadGlobalSetting(Settings::values.use_dxgi_swapchain);
|
||||||
ReadGlobalSetting(Settings::values.renderer_force_max_clock);
|
ReadGlobalSetting(Settings::values.renderer_force_max_clock);
|
||||||
ReadGlobalSetting(Settings::values.vulkan_device);
|
ReadGlobalSetting(Settings::values.vulkan_device);
|
||||||
ReadGlobalSetting(Settings::values.fullscreen_mode);
|
ReadGlobalSetting(Settings::values.fullscreen_mode);
|
||||||
|
@ -1323,6 +1324,7 @@ void Config::SaveRendererValues() {
|
||||||
static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
|
static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
|
||||||
Settings::values.renderer_backend.UsingGlobal());
|
Settings::values.renderer_backend.UsingGlobal());
|
||||||
WriteGlobalSetting(Settings::values.async_presentation);
|
WriteGlobalSetting(Settings::values.async_presentation);
|
||||||
|
WriteGlobalSetting(Settings::values.use_dxgi_swapchain);
|
||||||
WriteGlobalSetting(Settings::values.renderer_force_max_clock);
|
WriteGlobalSetting(Settings::values.renderer_force_max_clock);
|
||||||
WriteGlobalSetting(Settings::values.vulkan_device);
|
WriteGlobalSetting(Settings::values.vulkan_device);
|
||||||
WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()),
|
WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()),
|
||||||
|
|
|
@ -14,6 +14,13 @@ ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(const Core::System& system_
|
||||||
|
|
||||||
SetupPerGameUI();
|
SetupPerGameUI();
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
static constexpr bool is_win32 = true;
|
||||||
|
#else
|
||||||
|
static constexpr bool is_win32 = false;
|
||||||
|
#endif
|
||||||
|
ui->dxgi_swapchain->setVisible(is_win32);
|
||||||
|
|
||||||
SetConfiguration();
|
SetConfiguration();
|
||||||
|
|
||||||
ui->enable_compute_pipelines_checkbox->setVisible(false);
|
ui->enable_compute_pipelines_checkbox->setVisible(false);
|
||||||
|
@ -33,6 +40,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
|
||||||
ui->enable_compute_pipelines_checkbox->setEnabled(runtime_lock);
|
ui->enable_compute_pipelines_checkbox->setEnabled(runtime_lock);
|
||||||
|
|
||||||
ui->async_present->setChecked(Settings::values.async_presentation.GetValue());
|
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->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue());
|
||||||
ui->use_reactive_flushing->setChecked(Settings::values.use_reactive_flushing.GetValue());
|
ui->use_reactive_flushing->setChecked(Settings::values.use_reactive_flushing.GetValue());
|
||||||
ui->async_astc->setChecked(Settings::values.async_astc.GetValue());
|
ui->async_astc->setChecked(Settings::values.async_astc.GetValue());
|
||||||
|
@ -91,6 +99,8 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
|
||||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines,
|
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines,
|
||||||
ui->enable_compute_pipelines_checkbox,
|
ui->enable_compute_pipelines_checkbox,
|
||||||
enable_compute_pipelines);
|
enable_compute_pipelines);
|
||||||
|
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_dxgi_swapchain,
|
||||||
|
ui->dxgi_swapchain, dxgi_swapchain);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
|
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
|
||||||
|
@ -110,6 +120,7 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
|
||||||
if (Settings::IsConfiguringGlobal()) {
|
if (Settings::IsConfiguringGlobal()) {
|
||||||
ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
|
ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
|
||||||
ui->async_present->setEnabled(Settings::values.async_presentation.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(
|
ui->renderer_force_max_clock->setEnabled(
|
||||||
Settings::values.renderer_force_max_clock.UsingGlobal());
|
Settings::values.renderer_force_max_clock.UsingGlobal());
|
||||||
ui->use_reactive_flushing->setEnabled(Settings::values.use_reactive_flushing.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,
|
ConfigurationShared::SetColoredTristate(ui->async_present, Settings::values.async_presentation,
|
||||||
async_present);
|
async_present);
|
||||||
|
ConfigurationShared::SetColoredTristate(ui->dxgi_swapchain, Settings::values.use_dxgi_swapchain,
|
||||||
|
dxgi_swapchain);
|
||||||
ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock,
|
ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock,
|
||||||
Settings::values.renderer_force_max_clock,
|
Settings::values.renderer_force_max_clock,
|
||||||
renderer_force_max_clock);
|
renderer_force_max_clock);
|
||||||
|
|
|
@ -39,6 +39,7 @@ private:
|
||||||
std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
|
std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
|
||||||
|
|
||||||
ConfigurationShared::CheckState async_present;
|
ConfigurationShared::CheckState async_present;
|
||||||
|
ConfigurationShared::CheckState dxgi_swapchain;
|
||||||
ConfigurationShared::CheckState renderer_force_max_clock;
|
ConfigurationShared::CheckState renderer_force_max_clock;
|
||||||
ConfigurationShared::CheckState use_vsync;
|
ConfigurationShared::CheckState use_vsync;
|
||||||
ConfigurationShared::CheckState async_astc;
|
ConfigurationShared::CheckState async_astc;
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>404</width>
|
<width>418</width>
|
||||||
<height>376</height>
|
<height>444</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -72,44 +72,44 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QWidget" name="astc_recompression_layout" native="true">
|
<widget class="QWidget" name="astc_recompression_layout" native="true">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="topMargin">
|
<property name="topMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_astc_recompression">
|
<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">
|
<property name="text">
|
||||||
<string>ASTC recompression:</string>
|
<string>Uncompressed (Best quality)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</item>
|
||||||
</item>
|
<item>
|
||||||
<item>
|
<property name="text">
|
||||||
<widget class="QComboBox" name="astc_recompression_combobox">
|
<string>BC1 (Low quality)</string>
|
||||||
<item>
|
</property>
|
||||||
<property name="text">
|
</item>
|
||||||
<string>Uncompressed (Best quality)</string>
|
<item>
|
||||||
</property>
|
<property name="text">
|
||||||
</item>
|
<string>BC3 (Medium quality)</string>
|
||||||
<item>
|
</property>
|
||||||
<property name="text">
|
</item>
|
||||||
<string>BC1 (Low quality)</string>
|
</widget>
|
||||||
</property>
|
</item>
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>BC3 (Medium quality)</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -191,6 +191,16 @@ Compute pipelines are always enabled on all other drivers.</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<widget class="QWidget" name="af_layout" native="true">
|
<widget class="QWidget" name="af_layout" native="true">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_1">
|
<layout class="QHBoxLayout" name="horizontalLayout_1">
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue