[UI] Image post-processing and full presentation/window rework

[GPU] Add FXAA post-processing
[UI] Add FidelityFX FSR and CAS post-processing
[UI] Add blue noise dithering from 10bpc to 8bpc
[GPU] Apply the DC PWL gamma ramp closer to the spec, supporting fully white color
[UI] Allow the GPU CP thread to present on the host directly, bypassing the UI thread OS paint event
[UI] Allow variable refresh rate (or tearing)
[UI] Present the newest frame (restart) on DXGI
[UI] Replace GraphicsContext with a far more advanced Presenter with more coherent surface connection and UI overlay state management
[UI] Connect presentation to windows via the Surface class, not native window handles
[Vulkan] Switch to simpler Vulkan setup with no instance/device separation due to interdependencies and to pass fewer objects around
[Vulkan] Lower the minimum required Vulkan version to 1.0
[UI/GPU] Various cleanup, mainly ComPtr usage
[UI] Support per-monitor DPI awareness v2 on Windows
[UI] DPI-scale Dear ImGui
[UI] Replace the remaining non-detachable window delegates with unified window event and input listeners
[UI] Allow listeners to safely destroy or close the window, and to register/unregister listeners without use-after-free and the ABA problem
[UI] Explicit Z ordering of input listeners and UI overlays, top-down for input, bottom-up for drawing
[UI] Add explicit window lifecycle phases
[UI] Replace Window virtual functions with explicit desired state, its application, actual state, its feedback
[UI] GTK: Apply the initial size to the drawing area
[UI] Limit internal UI frame rate to that of the monitor
[UI] Hide the cursor using a timer instead of polling due to no repeated UI thread paints with GPU CP thread presentation, and only within the window
This commit is contained in:
Triang3l
2022-01-29 13:22:03 +03:00
parent 372bdd3ec9
commit fe3f0f26e4
428 changed files with 75942 additions and 18360 deletions

View File

@@ -0,0 +1,199 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/vulkan/vulkan_upload_buffer_pool.h"
#include <algorithm>
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/ui/vulkan/vulkan_util.h"
namespace xe {
namespace ui {
namespace vulkan {
// Host-visible memory sizes are likely to be internally rounded to
// nonCoherentAtomSize (it's the memory mapping granularity, though as the map
// or flush range must be clamped to the actual allocation size as a special
// case, but it's still unlikely that the allocation won't be aligned to it), so
// try not to waste that padding.
VulkanUploadBufferPool::VulkanUploadBufferPool(const VulkanProvider& provider,
VkBufferUsageFlags usage,
size_t page_size)
: GraphicsUploadBufferPool(size_t(
util::GetMappableMemorySize(provider, VkDeviceSize(page_size)))),
provider_(provider),
usage_(usage) {}
uint8_t* VulkanUploadBufferPool::Request(uint64_t submission_index, size_t size,
size_t alignment, VkBuffer& buffer_out,
VkDeviceSize& offset_out) {
size_t offset;
const VulkanPage* page =
static_cast<const VulkanPage*>(GraphicsUploadBufferPool::Request(
submission_index, size, alignment, offset));
if (!page) {
return nullptr;
}
buffer_out = page->buffer_;
offset_out = VkDeviceSize(offset);
return reinterpret_cast<uint8_t*>(page->mapping_) + offset;
}
uint8_t* VulkanUploadBufferPool::RequestPartial(uint64_t submission_index,
size_t size, size_t alignment,
VkBuffer& buffer_out,
VkDeviceSize& offset_out,
VkDeviceSize& size_out) {
size_t offset, size_obtained;
const VulkanPage* page =
static_cast<const VulkanPage*>(GraphicsUploadBufferPool::RequestPartial(
submission_index, size, alignment, offset, size_obtained));
if (!page) {
return nullptr;
}
buffer_out = page->buffer_;
offset_out = VkDeviceSize(offset);
size_out = VkDeviceSize(size_obtained);
return reinterpret_cast<uint8_t*>(page->mapping_) + offset;
}
GraphicsUploadBufferPool::Page*
VulkanUploadBufferPool::CreatePageImplementation() {
if (memory_type_ == kMemoryTypeUnavailable) {
// Don't try to create everything again and again if totally broken.
return nullptr;
}
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
VkDevice device = provider_.device();
VkBufferCreateInfo buffer_create_info;
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_create_info.pNext = nullptr;
buffer_create_info.flags = 0;
buffer_create_info.size = VkDeviceSize(page_size_);
buffer_create_info.usage = usage_;
buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buffer_create_info.queueFamilyIndexCount = 0;
buffer_create_info.pQueueFamilyIndices = nullptr;
VkBuffer buffer;
if (dfn.vkCreateBuffer(device, &buffer_create_info, nullptr, &buffer) !=
VK_SUCCESS) {
XELOGE("Failed to create a Vulkan upload buffer with {} bytes", page_size_);
return nullptr;
}
if (memory_type_ == kMemoryTypeUnknown) {
VkMemoryRequirements memory_requirements;
dfn.vkGetBufferMemoryRequirements(device, buffer, &memory_requirements);
memory_type_ = util::ChooseHostMemoryType(
provider_, memory_requirements.memoryTypeBits, false);
if (memory_type_ == UINT32_MAX) {
XELOGE(
"No host-visible memory types can store an Vulkan upload buffer with "
"{} bytes",
page_size_);
memory_type_ = kMemoryTypeUnavailable;
dfn.vkDestroyBuffer(device, buffer, nullptr);
return nullptr;
}
allocation_size_ = memory_requirements.size;
if (allocation_size_ > page_size_) {
// Try to occupy the allocation padding. If that's going to require even
// more memory for some reason, don't.
buffer_create_info.size = allocation_size_;
VkBuffer buffer_expanded;
if (dfn.vkCreateBuffer(device, &buffer_create_info, nullptr,
&buffer_expanded) == VK_SUCCESS) {
VkMemoryRequirements memory_requirements_expanded;
dfn.vkGetBufferMemoryRequirements(device, buffer_expanded,
&memory_requirements_expanded);
uint32_t memory_type_expanded = util::ChooseHostMemoryType(
provider_, memory_requirements.memoryTypeBits, false);
if (memory_requirements_expanded.size <= allocation_size_ &&
memory_type_expanded != UINT32_MAX) {
page_size_ = size_t(allocation_size_);
allocation_size_ = memory_requirements_expanded.size;
memory_type_ = memory_type_expanded;
dfn.vkDestroyBuffer(device, buffer, nullptr);
buffer = buffer_expanded;
} else {
dfn.vkDestroyBuffer(device, buffer_expanded, nullptr);
}
}
}
}
VkMemoryAllocateInfo memory_allocate_info;
VkMemoryAllocateInfo* memory_allocate_info_last = &memory_allocate_info;
memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memory_allocate_info.pNext = nullptr;
memory_allocate_info.allocationSize = allocation_size_;
memory_allocate_info.memoryTypeIndex = memory_type_;
VkMemoryDedicatedAllocateInfoKHR memory_dedicated_allocate_info;
if (provider_.device_extensions().khr_dedicated_allocation) {
memory_allocate_info_last->pNext = &memory_dedicated_allocate_info;
memory_allocate_info_last = reinterpret_cast<VkMemoryAllocateInfo*>(
&memory_dedicated_allocate_info);
memory_dedicated_allocate_info.sType =
VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
memory_dedicated_allocate_info.pNext = nullptr;
memory_dedicated_allocate_info.image = VK_NULL_HANDLE;
memory_dedicated_allocate_info.buffer = buffer;
}
VkDeviceMemory memory;
if (dfn.vkAllocateMemory(device, &memory_allocate_info, nullptr, &memory) !=
VK_SUCCESS) {
XELOGE("Failed to allocate {} bytes of Vulkan upload buffer memory",
allocation_size_);
dfn.vkDestroyBuffer(device, buffer, nullptr);
return nullptr;
}
if (dfn.vkBindBufferMemory(device, buffer, memory, 0) != VK_SUCCESS) {
XELOGE("Failed to bind memory to a Vulkan upload buffer with {} bytes",
page_size_);
dfn.vkDestroyBuffer(device, buffer, nullptr);
dfn.vkFreeMemory(device, memory, nullptr);
return nullptr;
}
void* mapping;
if (dfn.vkMapMemory(device, memory, 0, VK_WHOLE_SIZE, 0, &mapping) !=
VK_SUCCESS) {
XELOGE("Failed to map {} bytes of Vulkan upload buffer memory",
allocation_size_);
dfn.vkDestroyBuffer(device, buffer, nullptr);
dfn.vkFreeMemory(device, memory, nullptr);
return nullptr;
}
return new VulkanPage(provider_, buffer, memory, mapping);
}
void VulkanUploadBufferPool::FlushPageWrites(Page* page, size_t offset,
size_t size) {
util::FlushMappedMemoryRange(
provider_, static_cast<const VulkanPage*>(page)->memory_, memory_type_,
VkDeviceSize(offset), allocation_size_, VkDeviceSize(size));
}
VulkanUploadBufferPool::VulkanPage::~VulkanPage() {
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
VkDevice device = provider_.device();
dfn.vkDestroyBuffer(device, buffer_, nullptr);
// Unmapping is done implicitly when the memory is freed.
dfn.vkFreeMemory(device, memory_, nullptr);
}
} // namespace vulkan
} // namespace ui
} // namespace xe