diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index e5ce16a66..69391a86d 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -234,10 +234,6 @@ if(ENABLE_VULKAN)
vulkan/shader_cache.h
vulkan/shader_compiler.cpp
vulkan/shader_compiler.h
- vulkan/staging_buffer.cpp
- vulkan/staging_buffer.h
- vulkan/staging_texture.cpp
- vulkan/staging_texture.h
vulkan/stream_buffer.cpp
vulkan/stream_buffer.h
vulkan/swap_chain.cpp
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
index aa05b0426..573b921fb 100644
--- a/src/common/common.vcxproj
+++ b/src/common/common.vcxproj
@@ -92,12 +92,6 @@
true
-
- true
-
-
- true
-
true
@@ -180,12 +174,6 @@
true
-
- true
-
-
- true
-
true
@@ -221,4 +209,4 @@
-
+
\ No newline at end of file
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters
index 15eefe891..2f24c65d4 100644
--- a/src/common/common.vcxproj.filters
+++ b/src/common/common.vcxproj.filters
@@ -55,9 +55,6 @@
vulkan
-
- vulkan
-
vulkan
@@ -70,9 +67,6 @@
vulkan
-
- vulkan
-
vulkan
@@ -188,9 +182,6 @@
vulkan
-
- vulkan
-
vulkan
@@ -203,9 +194,6 @@
vulkan
-
- vulkan
-
vulkan
@@ -277,4 +265,4 @@
vulkan
-
+
\ No newline at end of file
diff --git a/src/common/vulkan/context.cpp b/src/common/vulkan/context.cpp
index 60f39e6f6..0f981a87a 100644
--- a/src/common/vulkan/context.cpp
+++ b/src/common/vulkan/context.cpp
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#include "context.h"
#include "../assert.h"
#include "../log.h"
@@ -21,7 +16,7 @@ namespace Vulkan {
enum : u32
{
- TEXTURE_BUFFER_SIZE = 16 * 1024 * 1024,
+ TEXTURE_BUFFER_SIZE = 32 * 1024 * 1024,
};
Context::Context(VkInstance instance, VkPhysicalDevice physical_device, bool owns_device)
@@ -351,8 +346,8 @@ bool Context::Create(std::string_view gpu_name, const WindowInfo* wi, std::uniqu
// Attempt to create the device.
if (!g_vulkan_context->CreateDevice(surface, enable_validation_layer, nullptr, 0, nullptr, 0, nullptr) ||
- !g_vulkan_context->CreateGlobalDescriptorPool() || !g_vulkan_context->CreateCommandBuffers() ||
- !g_vulkan_context->CreateTextureStreamBuffer() ||
+ !g_vulkan_context->CreateAllocator() || !g_vulkan_context->CreateGlobalDescriptorPool() ||
+ !g_vulkan_context->CreateCommandBuffers() || !g_vulkan_context->CreateTextureStreamBuffer() ||
(enable_surface && (*out_swap_chain = SwapChain::Create(wi_copy, surface, true)) == nullptr))
{
// Since we are destroying the instance, we're also responsible for destroying the surface.
@@ -410,6 +405,7 @@ void Context::Destroy()
g_vulkan_context->DestroyRenderPassCache();
g_vulkan_context->DestroyGlobalDescriptorPool();
g_vulkan_context->DestroyCommandBuffers();
+ g_vulkan_context->DestroyAllocator();
if (g_vulkan_context->m_device != VK_NULL_HANDLE)
vkDestroyDevice(g_vulkan_context->m_device, nullptr);
@@ -474,6 +470,9 @@ bool Context::SelectDeviceExtensions(ExtensionList* extension_list, bool enable_
if (enable_surface && !SupportsExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true))
return false;
+ m_optional_extensions.vk_ext_memory_budget = SupportsExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, false);
+ m_optional_extensions.vk_khr_driver_properties = SupportsExtension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, false);
+
return true;
}
@@ -643,6 +642,37 @@ bool Context::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer, c
return true;
}
+bool Context::CreateAllocator()
+{
+ VmaAllocatorCreateInfo ci = {};
+ ci.vulkanApiVersion = VK_API_VERSION_1_1;
+ ci.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
+ ci.physicalDevice = m_physical_device;
+ ci.device = m_device;
+ ci.instance = m_instance;
+
+ if (m_optional_extensions.vk_ext_memory_budget)
+ ci.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
+
+ VkResult res = vmaCreateAllocator(&ci, &m_allocator);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vmaCreateAllocator failed: ");
+ return false;
+ }
+
+ return true;
+}
+
+void Context::DestroyAllocator()
+{
+ if (m_allocator == VK_NULL_HANDLE)
+ return;
+
+ vmaDestroyAllocator(m_allocator);
+ m_allocator = VK_NULL_HANDLE;
+}
+
bool Context::CreateCommandBuffers()
{
VkResult res;
@@ -1143,6 +1173,9 @@ void Context::ActivateCommandBuffer(u32 index)
m_current_frame = index;
m_current_command_buffer = resources.command_buffer;
+
+ // using the lower 32 bits of the fence index should be sufficient here, I hope...
+ vmaSetCurrentFrameIndex(m_allocator, static_cast(m_next_fence_counter));
}
void Context::ExecuteCommandBuffer(bool wait_for_completion)
@@ -1169,6 +1202,13 @@ void Context::DeferBufferDestruction(VkBuffer object)
resources.cleanup_resources.push_back([this, object]() { vkDestroyBuffer(m_device, object, nullptr); });
}
+void Context::DeferBufferDestruction(VkBuffer object, VmaAllocation allocation)
+{
+ FrameResources& resources = m_frame_resources[m_current_frame];
+ resources.cleanup_resources.push_back(
+ [this, object, allocation]() { vmaDestroyBuffer(m_allocator, object, allocation); });
+}
+
void Context::DeferBufferViewDestruction(VkBufferView object)
{
FrameResources& resources = m_frame_resources[m_current_frame];
@@ -1193,6 +1233,13 @@ void Context::DeferImageDestruction(VkImage object)
resources.cleanup_resources.push_back([this, object]() { vkDestroyImage(m_device, object, nullptr); });
}
+void Context::DeferImageDestruction(VkImage object, VmaAllocation allocation)
+{
+ FrameResources& resources = m_frame_resources[m_current_frame];
+ resources.cleanup_resources.push_back(
+ [this, object, allocation]() { vmaDestroyImage(m_allocator, object, allocation); });
+}
+
void Context::DeferImageViewDestruction(VkImageView object)
{
FrameResources& resources = m_frame_resources[m_current_frame];
@@ -1267,97 +1314,6 @@ void Context::DisableDebugUtils()
}
}
-bool Context::GetMemoryType(u32 bits, VkMemoryPropertyFlags properties, u32* out_type_index)
-{
- for (u32 i = 0; i < VK_MAX_MEMORY_TYPES; i++)
- {
- if ((bits & (1 << i)) != 0)
- {
- u32 supported = m_device_memory_properties.memoryTypes[i].propertyFlags & properties;
- if (supported == properties)
- {
- *out_type_index = i;
- return true;
- }
- }
- }
-
- return false;
-}
-
-u32 Context::GetMemoryType(u32 bits, VkMemoryPropertyFlags properties)
-{
- u32 type_index = VK_MAX_MEMORY_TYPES;
- if (!GetMemoryType(bits, properties, &type_index))
- {
- Log_ErrorPrintf("Unable to find memory type for %x:%x", bits, properties);
- Panic("Unable to find memory type");
- }
-
- return type_index;
-}
-
-u32 Context::GetUploadMemoryType(u32 bits, bool* is_coherent)
-{
- // Try for coherent memory first.
- VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
-
- u32 type_index;
- if (!GetMemoryType(bits, flags, &type_index))
- {
- Log_WarningPrintf("Vulkan: Failed to find a coherent memory type for uploads, this will affect performance.");
-
- // Try non-coherent memory.
- flags &= ~VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
- if (!GetMemoryType(bits, flags, &type_index))
- {
- // We shouldn't have any memory types that aren't host-visible.
- Panic("Unable to get memory type for upload.");
- type_index = 0;
- }
- }
-
- if (is_coherent)
- *is_coherent = ((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0);
-
- return type_index;
-}
-
-u32 Context::GetReadbackMemoryType(u32 bits, bool* is_coherent, bool* is_cached)
-{
- // Try for cached and coherent memory first.
- VkMemoryPropertyFlags flags =
- VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
-
- u32 type_index;
- if (!GetMemoryType(bits, flags, &type_index))
- {
- // For readbacks, caching is more important than coherency.
- flags &= ~VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
- if (!GetMemoryType(bits, flags, &type_index))
- {
- Log_WarningPrintf("Vulkan: Failed to find a cached memory type for readbacks, this will affect "
- "performance.");
-
- // Remove the cached bit as well.
- flags &= ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
- if (!GetMemoryType(bits, flags, &type_index))
- {
- // We shouldn't have any memory types that aren't host-visible.
- Panic("Unable to get memory type for upload.");
- type_index = 0;
- }
- }
- }
-
- if (is_coherent)
- *is_coherent = ((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0);
- if (is_cached)
- *is_cached = ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0);
-
- return type_index;
-}
-
VkRenderPass Context::GetRenderPass(VkFormat color_format, VkFormat depth_format, VkSampleCountFlagBits samples,
VkAttachmentLoadOp load_op)
{
diff --git a/src/common/vulkan/context.h b/src/common/vulkan/context.h
index 037e3c30c..d8e0e82ad 100644
--- a/src/common/vulkan/context.h
+++ b/src/common/vulkan/context.h
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#pragma once
#include "../types.h"
@@ -33,6 +28,12 @@ public:
NUM_COMMAND_BUFFERS = 2
};
+ struct OptionalExtensions
+ {
+ bool vk_ext_memory_budget : 1;
+ bool vk_khr_driver_properties : 1;
+ };
+
~Context();
// Determines if the Vulkan validation layer is available on the system.
@@ -71,6 +72,7 @@ public:
ALWAYS_INLINE VkInstance GetVulkanInstance() const { return m_instance; }
ALWAYS_INLINE VkPhysicalDevice GetPhysicalDevice() const { return m_physical_device; }
ALWAYS_INLINE VkDevice GetDevice() const { return m_device; }
+ ALWAYS_INLINE VmaAllocator GetAllocator() const { return m_allocator; }
ALWAYS_INLINE VkQueue GetGraphicsQueue() const { return m_graphics_queue; }
ALWAYS_INLINE u32 GetGraphicsQueueFamilyIndex() const { return m_graphics_queue_family_index; }
ALWAYS_INLINE VkQueue GetPresentQueue() const { return m_present_queue; }
@@ -118,15 +120,6 @@ public:
}
ALWAYS_INLINE u32 GetMaxImageDimension2D() const { return m_device_properties.limits.maxImageDimension2D; }
- // Finds a memory type index for the specified memory properties and the bits returned by
- // vkGetImageMemoryRequirements
- bool GetMemoryType(u32 bits, VkMemoryPropertyFlags properties, u32* out_type_index);
- u32 GetMemoryType(u32 bits, VkMemoryPropertyFlags properties);
-
- // Finds a memory type for upload or readback buffers.
- u32 GetUploadMemoryType(u32 bits, bool* is_coherent = nullptr);
- u32 GetReadbackMemoryType(u32 bits, bool* is_coherent = nullptr, bool* is_cached = nullptr);
-
// Creates a simple render pass.
VkRenderPass GetRenderPass(VkFormat color_format, VkFormat depth_format, VkSampleCountFlagBits samples,
VkAttachmentLoadOp load_op);
@@ -178,10 +171,12 @@ public:
// Schedule a vulkan resource for destruction later on. This will occur when the command buffer
// is next re-used, and the GPU has finished working with the specified resource.
void DeferBufferDestruction(VkBuffer object);
+ void DeferBufferDestruction(VkBuffer object, VmaAllocation allocation);
void DeferBufferViewDestruction(VkBufferView object);
void DeferDeviceMemoryDestruction(VkDeviceMemory object);
void DeferFramebufferDestruction(VkFramebuffer object);
void DeferImageDestruction(VkImage object);
+ void DeferImageDestruction(VkImage object, VmaAllocation allocation);
void DeferImageViewDestruction(VkImageView object);
void DeferPipelineDestruction(VkPipeline pipeline);
@@ -205,6 +200,8 @@ private:
u32 num_required_device_extensions, const char** required_device_layers,
u32 num_required_device_layers, const VkPhysicalDeviceFeatures* required_features);
+ bool CreateAllocator();
+ void DestroyAllocator();
bool CreateCommandBuffers();
void DestroyCommandBuffers();
bool CreateGlobalDescriptorPool();
@@ -239,6 +236,7 @@ private:
VkInstance m_instance = VK_NULL_HANDLE;
VkPhysicalDevice m_physical_device = VK_NULL_HANDLE;
VkDevice m_device = VK_NULL_HANDLE;
+ VmaAllocator m_allocator = VK_NULL_HANDLE;
VkCommandBuffer m_current_command_buffer = VK_NULL_HANDLE;
@@ -290,6 +288,8 @@ private:
VkPhysicalDeviceFeatures m_device_features = {};
VkPhysicalDeviceProperties m_device_properties = {};
VkPhysicalDeviceMemoryProperties m_device_memory_properties = {};
+ VkPhysicalDeviceDriverPropertiesKHR m_device_driver_properties = {};
+ OptionalExtensions m_optional_extensions = {};
};
} // namespace Vulkan
diff --git a/src/common/vulkan/entry_points.h b/src/common/vulkan/entry_points.h
index 6f584dbb2..5bf62eb69 100644
--- a/src/common/vulkan/entry_points.h
+++ b/src/common/vulkan/entry_points.h
@@ -1,5 +1,9 @@
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
// We abuse the preprocessor here to only need to specify function names once.
// Function names are prefixed so to not conflict with system symbols at runtime.
#define VULKAN_MODULE_ENTRY_POINT(name, required) extern PFN_##name ds_##name;
@@ -12,6 +16,10 @@
#undef VULKAN_INSTANCE_ENTRY_POINT
#undef VULKAN_MODULE_ENTRY_POINT
+#ifdef __cplusplus
+}
+#endif
+
#define vkCreateInstance ds_vkCreateInstance
#define vkGetInstanceProcAddr ds_vkGetInstanceProcAddr
#define vkEnumerateInstanceExtensionProperties ds_vkEnumerateInstanceExtensionProperties
@@ -58,7 +66,6 @@
#define vkSetDebugUtilsObjectTagEXT ds_vkSetDebugUtilsObjectTagEXT
#define vkSubmitDebugUtilsMessageEXT ds_vkSubmitDebugUtilsMessageEXT
-#define vkGetPhysicalDeviceProperties2 ds_vkGetPhysicalDeviceProperties2
#define vkGetPhysicalDeviceSurfaceCapabilities2KHR ds_vkGetPhysicalDeviceSurfaceCapabilities2KHR
#define vkGetPhysicalDeviceDisplayPropertiesKHR ds_vkGetPhysicalDeviceDisplayPropertiesKHR
#define vkGetPhysicalDeviceDisplayPlanePropertiesKHR ds_vkGetPhysicalDeviceDisplayPlanePropertiesKHR
@@ -68,6 +75,11 @@
#define vkGetDisplayPlaneCapabilitiesKHR ds_vkGetDisplayPlaneCapabilitiesKHR
#define vkCreateDisplayPlaneSurfaceKHR ds_vkCreateDisplayPlaneSurfaceKHR
+// Vulkan 1.1 functions.
+#define vkGetPhysicalDeviceFeatures2 ds_vkGetPhysicalDeviceFeatures2
+#define vkGetPhysicalDeviceProperties2 ds_vkGetPhysicalDeviceProperties2
+#define vkGetPhysicalDeviceMemoryProperties2 ds_vkGetPhysicalDeviceMemoryProperties2
+
#define vkDestroyDevice ds_vkDestroyDevice
#define vkGetDeviceQueue ds_vkGetDeviceQueue
#define vkQueueSubmit ds_vkQueueSubmit
@@ -194,7 +206,13 @@
#define vkAcquireNextImageKHR ds_vkAcquireNextImageKHR
#define vkQueuePresentKHR ds_vkQueuePresentKHR
+// Vulkan 1.1 functions.
+#define vkGetBufferMemoryRequirements2 ds_vkGetBufferMemoryRequirements2
+#define vkGetImageMemoryRequirements2 ds_vkGetImageMemoryRequirements2
+#define vkBindBufferMemory2 ds_vkBindBufferMemory2
+#define vkBindImageMemory2 ds_vkBindImageMemory2
+
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
#define vkAcquireFullScreenExclusiveModeEXT ds_vkAcquireFullScreenExclusiveModeEXT
#define vkReleaseFullScreenExclusiveModeEXT ds_vkReleaseFullScreenExclusiveModeEXT
-#endif
+#endif
\ No newline at end of file
diff --git a/src/common/vulkan/entry_points.inl b/src/common/vulkan/entry_points.inl
index 01a57a19b..10abd353c 100644
--- a/src/common/vulkan/entry_points.inl
+++ b/src/common/vulkan/entry_points.inl
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
// Expands the VULKAN_ENTRY_POINT macro for each function when this file is included.
// Parameters: Function name, is required
// VULKAN_MODULE_ENTRY_POINT is for functions in vulkan-1.dll
@@ -16,13 +11,13 @@ VULKAN_MODULE_ENTRY_POINT(vkGetInstanceProcAddr, true)
VULKAN_MODULE_ENTRY_POINT(vkEnumerateInstanceExtensionProperties, true)
VULKAN_MODULE_ENTRY_POINT(vkEnumerateInstanceLayerProperties, true)
VULKAN_MODULE_ENTRY_POINT(vkEnumerateInstanceVersion, false)
+VULKAN_MODULE_ENTRY_POINT(vkDestroyInstance, true)
-#endif // VULKAN_MODULE_ENTRY_POINT
+#endif // VULKAN_MODULE_ENTRY_POINT
#ifdef VULKAN_INSTANCE_ENTRY_POINT
VULKAN_INSTANCE_ENTRY_POINT(vkGetDeviceProcAddr, true)
-VULKAN_INSTANCE_ENTRY_POINT(vkDestroyInstance, true)
VULKAN_INSTANCE_ENTRY_POINT(vkEnumeratePhysicalDevices, true)
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceFeatures, true)
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceFormatProperties, true)
@@ -79,7 +74,6 @@ VULKAN_INSTANCE_ENTRY_POINT(vkSetDebugUtilsObjectNameEXT, false)
VULKAN_INSTANCE_ENTRY_POINT(vkSetDebugUtilsObjectTagEXT, false)
VULKAN_INSTANCE_ENTRY_POINT(vkSubmitDebugUtilsMessageEXT, false)
-VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceProperties2, false)
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceSurfaceCapabilities2KHR, false)
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceDisplayPropertiesKHR, false)
@@ -90,7 +84,12 @@ VULKAN_INSTANCE_ENTRY_POINT(vkCreateDisplayModeKHR, false)
VULKAN_INSTANCE_ENTRY_POINT(vkGetDisplayPlaneCapabilitiesKHR, false)
VULKAN_INSTANCE_ENTRY_POINT(vkCreateDisplayPlaneSurfaceKHR, false)
-#endif // VULKAN_INSTANCE_ENTRY_POINT
+// Vulkan 1.1 functions.
+VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceFeatures2, true)
+VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceProperties2, true)
+VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceMemoryProperties2, true)
+
+#endif // VULKAN_INSTANCE_ENTRY_POINT
#ifdef VULKAN_DEVICE_ENTRY_POINT
@@ -220,9 +219,15 @@ VULKAN_DEVICE_ENTRY_POINT(vkGetSwapchainImagesKHR, false)
VULKAN_DEVICE_ENTRY_POINT(vkAcquireNextImageKHR, false)
VULKAN_DEVICE_ENTRY_POINT(vkQueuePresentKHR, false)
+// Vulkan 1.1 functions.
+VULKAN_DEVICE_ENTRY_POINT(vkGetBufferMemoryRequirements2, true)
+VULKAN_DEVICE_ENTRY_POINT(vkGetImageMemoryRequirements2, true)
+VULKAN_DEVICE_ENTRY_POINT(vkBindBufferMemory2, true)
+VULKAN_DEVICE_ENTRY_POINT(vkBindImageMemory2, true)
+
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
VULKAN_DEVICE_ENTRY_POINT(vkAcquireFullScreenExclusiveModeEXT, false)
VULKAN_DEVICE_ENTRY_POINT(vkReleaseFullScreenExclusiveModeEXT, false)
#endif
-#endif // VULKAN_DEVICE_ENTRY_POINT
+#endif // VULKAN_DEVICE_ENTRY_POINT
diff --git a/src/common/vulkan/loader.cpp b/src/common/vulkan/loader.cpp
index 8ca56a90f..54967c9d7 100644
--- a/src/common/vulkan/loader.cpp
+++ b/src/common/vulkan/loader.cpp
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#include
#include
#include
@@ -10,6 +5,8 @@
#include
#include
+#define VMA_IMPLEMENTATION 1
+
#include "loader.h"
#ifndef _WIN32
@@ -20,6 +17,8 @@
#include
#endif
+extern "C" {
+
#define VULKAN_MODULE_ENTRY_POINT(name, required) PFN_##name ds_##name;
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) PFN_##name ds_##name;
#define VULKAN_DEVICE_ENTRY_POINT(name, required) PFN_##name ds_##name;
@@ -27,9 +26,9 @@
#undef VULKAN_DEVICE_ENTRY_POINT
#undef VULKAN_INSTANCE_ENTRY_POINT
#undef VULKAN_MODULE_ENTRY_POINT
+}
namespace Vulkan {
-
void ResetVulkanLibraryFunctionPointers()
{
#define VULKAN_MODULE_ENTRY_POINT(name, required) ds_##name = nullptr;
@@ -55,11 +54,7 @@ bool LoadVulkanLibrary()
return true;
}
-#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
vulkan_module = LoadLibraryA("vulkan-1.dll");
-#else
- vulkan_module = NULL;
-#endif
if (!vulkan_module)
{
std::fprintf(stderr, "Failed to load vulkan-1.dll\n");
@@ -118,14 +113,9 @@ bool LoadVulkanLibrary()
#if defined(__APPLE__)
// Check if a path to a specific Vulkan library has been specified.
- // Otherwise, try for a system-wide libvulkan.
char* libvulkan_env = getenv("LIBVULKAN_PATH");
if (libvulkan_env)
vulkan_module = dlopen(libvulkan_env, RTLD_NOW);
- else
- vulkan_module = dlopen("libvulkan.dylib", RTLD_NOW);
-
- // Fall back to the packaged MoltenVK.
if (!vulkan_module)
{
unsigned path_size = 0;
@@ -140,11 +130,23 @@ bool LoadVulkanLibrary()
if (pos != std::string::npos)
{
path.erase(pos);
- path += "/../Frameworks/libMoltenVK.dylib";
+ path += "/../Frameworks/libvulkan.dylib";
vulkan_module = dlopen(path.c_str(), RTLD_NOW);
+ if (!vulkan_module)
+ {
+ path.erase(pos);
+ path += "/../Frameworks/libMoltenVK.dylib";
+ vulkan_module = dlopen(path.c_str(), RTLD_NOW);
+ }
}
}
}
+ if (!vulkan_module)
+ {
+ vulkan_module = dlopen("libvulkan.dylib", RTLD_NOW);
+ if (!vulkan_module)
+ vulkan_module = dlopen("libMoltenVK.dylib", RTLD_NOW);
+ }
#else
// Names of libraries to search. Desktop should use libvulkan.so.1 or libvulkan.so.
static const char* search_lib_names[] = {"libvulkan.so.1", "libvulkan.so"};
@@ -240,4 +242,4 @@ bool LoadVulkanDeviceFunctions(VkDevice device)
return !required_functions_missing;
}
-} // namespace Vulkan
+} // namespace Vulkan
\ No newline at end of file
diff --git a/src/common/vulkan/loader.h b/src/common/vulkan/loader.h
index 3b2848647..8b437231f 100644
--- a/src/common/vulkan/loader.h
+++ b/src/common/vulkan/loader.h
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#pragma once
#define VK_NO_PROTOTYPES
@@ -81,6 +76,25 @@
#include "entry_points.h"
+// We include vk_mem_alloc globally, so we don't accidentally include it before the vulkan header somewhere.
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnullability-completeness"
+#elif defined(_MSC_VER)
+#pragma warning(push, 0)
+#endif
+
+#define VMA_STATIC_VULKAN_FUNCTIONS 1
+#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0
+#define VMA_STATS_STRING_ENABLED 0
+#include "vulkan/vk_mem_alloc.h"
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
namespace Vulkan {
bool LoadVulkanLibrary();
diff --git a/src/common/vulkan/shader_compiler.cpp b/src/common/vulkan/shader_compiler.cpp
index 45833e5d5..4efa431ef 100644
--- a/src/common/vulkan/shader_compiler.cpp
+++ b/src/common/vulkan/shader_compiler.cpp
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#include "shader_compiler.h"
#include "../assert.h"
#include "../log.h"
diff --git a/src/common/vulkan/shader_compiler.h b/src/common/vulkan/shader_compiler.h
index 8d37bdba5..08ffd0e29 100644
--- a/src/common/vulkan/shader_compiler.h
+++ b/src/common/vulkan/shader_compiler.h
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#pragma once
#include "../types.h"
diff --git a/src/common/vulkan/staging_buffer.cpp b/src/common/vulkan/staging_buffer.cpp
deleted file mode 100644
index 45b329110..000000000
--- a/src/common/vulkan/staging_buffer.cpp
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
-#include "staging_buffer.h"
-#include "../assert.h"
-#include "context.h"
-#include "util.h"
-
-namespace Vulkan {
-StagingBuffer::StagingBuffer() = default;
-
-StagingBuffer::StagingBuffer(StagingBuffer&& move)
- : m_type(move.m_type), m_buffer(move.m_buffer), m_memory(move.m_memory), m_size(move.m_size),
- m_coherent(move.m_coherent), m_map_pointer(move.m_map_pointer), m_map_offset(move.m_map_offset),
- m_map_size(move.m_map_size)
-{
- move.m_type = Type::Upload;
- move.m_buffer = VK_NULL_HANDLE;
- move.m_memory = VK_NULL_HANDLE;
- move.m_size = 0;
- move.m_coherent = false;
- move.m_map_pointer = nullptr;
- move.m_map_offset = 0;
- move.m_map_size = 0;
-}
-
-StagingBuffer::~StagingBuffer()
-{
- if (IsValid())
- Destroy(true);
-}
-
-StagingBuffer& StagingBuffer::operator=(StagingBuffer&& move)
-{
- if (IsValid())
- Destroy(true);
-
- std::swap(m_type, move.m_type);
- std::swap(m_buffer, move.m_buffer);
- std::swap(m_memory, move.m_memory);
- std::swap(m_size, move.m_size);
- std::swap(m_coherent, move.m_coherent);
- std::swap(m_map_pointer, move.m_map_pointer);
- std::swap(m_map_offset, move.m_map_offset);
- std::swap(m_map_size, move.m_map_size);
- return *this;
-}
-
-bool StagingBuffer::Map(VkDeviceSize offset, VkDeviceSize size)
-{
- m_map_offset = offset;
- if (size == VK_WHOLE_SIZE)
- m_map_size = m_size - offset;
- else
- m_map_size = size;
-
- Assert(!m_map_pointer);
- Assert(m_map_offset + m_map_size <= m_size);
-
- void* map_pointer;
- VkResult res = vkMapMemory(g_vulkan_context->GetDevice(), m_memory, m_map_offset, m_map_size, 0, &map_pointer);
- if (res != VK_SUCCESS)
- {
- LOG_VULKAN_ERROR(res, "vkMapMemory failed: ");
- return false;
- }
-
- m_map_pointer = reinterpret_cast(map_pointer);
- return true;
-}
-
-void StagingBuffer::Unmap()
-{
- Assert(m_map_pointer);
-
- vkUnmapMemory(g_vulkan_context->GetDevice(), m_memory);
- m_map_pointer = nullptr;
- m_map_offset = 0;
- m_map_size = 0;
-}
-
-void StagingBuffer::FlushCPUCache(VkDeviceSize offset, VkDeviceSize size)
-{
- Assert(offset >= m_map_offset);
- if (m_coherent || !IsMapped())
- return;
-
- VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, offset - m_map_offset, size};
- vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range);
-}
-
-void StagingBuffer::InvalidateGPUCache(VkCommandBuffer command_buffer, VkAccessFlagBits dest_access_flags,
- VkPipelineStageFlagBits dest_pipeline_stage, VkDeviceSize offset,
- VkDeviceSize size)
-{
- if (m_coherent)
- return;
-
- Assert((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE));
- Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_HOST_WRITE_BIT, dest_access_flags, offset, size,
- VK_PIPELINE_STAGE_HOST_BIT, dest_pipeline_stage);
-}
-
-void StagingBuffer::PrepareForGPUWrite(VkCommandBuffer command_buffer, VkAccessFlagBits dst_access_flags,
- VkPipelineStageFlagBits dst_pipeline_stage, VkDeviceSize offset,
- VkDeviceSize size)
-{
- if (m_coherent)
- return;
-
- Assert((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE));
- Util::BufferMemoryBarrier(command_buffer, m_buffer, 0, dst_access_flags, offset, size,
- VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, dst_pipeline_stage);
-}
-
-void StagingBuffer::FlushGPUCache(VkCommandBuffer command_buffer, VkAccessFlagBits src_access_flags,
- VkPipelineStageFlagBits src_pipeline_stage, VkDeviceSize offset, VkDeviceSize size)
-{
- if (m_coherent)
- return;
-
- Assert((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE));
- Util::BufferMemoryBarrier(command_buffer, m_buffer, src_access_flags, VK_ACCESS_HOST_READ_BIT, offset, size,
- src_pipeline_stage, VK_PIPELINE_STAGE_HOST_BIT);
-}
-
-void StagingBuffer::InvalidateCPUCache(VkDeviceSize offset, VkDeviceSize size)
-{
- Assert(offset >= m_map_offset);
- if (m_coherent || !IsMapped())
- return;
-
- VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, offset - m_map_offset, size};
- vkInvalidateMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range);
-}
-
-void StagingBuffer::Read(VkDeviceSize offset, void* data, size_t size, bool invalidate_caches)
-{
- Assert((offset + size) <= m_size);
- Assert(offset >= m_map_offset && size <= (m_map_size + (offset - m_map_offset)));
- if (invalidate_caches)
- InvalidateCPUCache(offset, size);
-
- memcpy(data, m_map_pointer + (offset - m_map_offset), size);
-}
-
-void StagingBuffer::Write(VkDeviceSize offset, const void* data, size_t size, bool invalidate_caches)
-{
- Assert((offset + size) <= m_size);
- Assert(offset >= m_map_offset && size <= (m_map_size + (offset - m_map_offset)));
-
- memcpy(m_map_pointer + (offset - m_map_offset), data, size);
- if (invalidate_caches)
- FlushCPUCache(offset, size);
-}
-
-bool StagingBuffer::AllocateBuffer(Type type, VkDeviceSize size, VkBufferUsageFlags usage, VkBuffer* out_buffer,
- VkDeviceMemory* out_memory, bool* out_coherent)
-{
- VkBufferCreateInfo buffer_create_info = {
- VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType
- nullptr, // const void* pNext
- 0, // VkBufferCreateFlags flags
- size, // VkDeviceSize size
- usage, // VkBufferUsageFlags usage
- VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode
- 0, // uint32_t queueFamilyIndexCount
- nullptr // const uint32_t* pQueueFamilyIndices
- };
- VkResult res = vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_create_info, nullptr, out_buffer);
- if (res != VK_SUCCESS)
- {
- LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: ");
- return false;
- }
-
- VkMemoryRequirements requirements;
- vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), *out_buffer, &requirements);
-
- u32 type_index;
- if (type == Type::Upload)
- type_index = g_vulkan_context->GetUploadMemoryType(requirements.memoryTypeBits, out_coherent);
- else
- type_index = g_vulkan_context->GetReadbackMemoryType(requirements.memoryTypeBits, out_coherent);
-
- VkMemoryAllocateInfo memory_allocate_info = {
- VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType
- nullptr, // const void* pNext
- requirements.size, // VkDeviceSize allocationSize
- type_index // uint32_t memoryTypeIndex
- };
- res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, out_memory);
- if (res != VK_SUCCESS)
- {
- LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: ");
- vkDestroyBuffer(g_vulkan_context->GetDevice(), *out_buffer, nullptr);
- return false;
- }
-
- res = vkBindBufferMemory(g_vulkan_context->GetDevice(), *out_buffer, *out_memory, 0);
- if (res != VK_SUCCESS)
- {
- LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: ");
- vkDestroyBuffer(g_vulkan_context->GetDevice(), *out_buffer, nullptr);
- vkFreeMemory(g_vulkan_context->GetDevice(), *out_memory, nullptr);
- return false;
- }
-
- return true;
-}
-
-bool StagingBuffer::Create(Type type, VkDeviceSize size, VkBufferUsageFlags usage)
-{
- if (!AllocateBuffer(type, size, usage, &m_buffer, &m_memory, &m_coherent))
- return false;
-
- m_type = type;
- m_size = size;
- return true;
-}
-
-void StagingBuffer::Destroy(bool defer /* = true */)
-{
- if (!IsValid())
- return;
-
- // Unmap before destroying
- if (m_map_pointer)
- Unmap();
-
- if (defer)
- g_vulkan_context->DeferBufferDestruction(m_buffer);
- else
- vkDestroyBuffer(g_vulkan_context->GetDevice(), m_buffer, nullptr);
-
- if (defer)
- g_vulkan_context->DeferDeviceMemoryDestruction(m_memory);
- else
- vkFreeMemory(g_vulkan_context->GetDevice(), m_memory, nullptr);
-
- m_type = Type::Upload;
- m_buffer = VK_NULL_HANDLE;
- m_memory = VK_NULL_HANDLE;
- m_size = 0;
- m_coherent = false;
- m_map_pointer = nullptr;
- m_map_offset = 0;
- m_map_size = 0;
-}
-
-} // namespace Vulkan
diff --git a/src/common/vulkan/staging_buffer.h b/src/common/vulkan/staging_buffer.h
deleted file mode 100644
index f93446047..000000000
--- a/src/common/vulkan/staging_buffer.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
-#pragma once
-#include "../types.h"
-#include "loader.h"
-#include
-
-namespace Vulkan {
-
-class StagingBuffer
-{
-public:
- enum class Type
- {
- Upload,
- Readback,
- Mutable
- };
-
- StagingBuffer();
- StagingBuffer(StagingBuffer&& move);
- StagingBuffer(const StagingBuffer&) = delete;
- virtual ~StagingBuffer();
-
- StagingBuffer& operator=(StagingBuffer&& move);
- StagingBuffer& operator=(const StagingBuffer&) = delete;
-
- ALWAYS_INLINE Type GetType() const { return m_type; }
- ALWAYS_INLINE VkDeviceSize GetSize() const { return m_size; }
- ALWAYS_INLINE VkBuffer GetBuffer() const { return m_buffer; }
- ALWAYS_INLINE bool IsMapped() const { return m_map_pointer != nullptr; }
- ALWAYS_INLINE const char* GetMapPointer() const { return m_map_pointer; }
- ALWAYS_INLINE char* GetMapPointer() { return m_map_pointer; }
- ALWAYS_INLINE VkDeviceSize GetMapOffset() const { return m_map_offset; }
- ALWAYS_INLINE VkDeviceSize GetMapSize() const { return m_map_size; }
- ALWAYS_INLINE bool IsValid() const { return (m_buffer != VK_NULL_HANDLE); }
- ALWAYS_INLINE bool IsCoherent() const { return m_coherent; }
-
- bool Map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE);
- void Unmap();
-
- // Upload part 1: Prepare from device read from the CPU side
- void FlushCPUCache(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE);
-
- // Upload part 2: Prepare for device read from the GPU side
- // Implicit when submitting the command buffer, so rarely needed.
- void InvalidateGPUCache(VkCommandBuffer command_buffer, VkAccessFlagBits dst_access_flags,
- VkPipelineStageFlagBits dst_pipeline_stage, VkDeviceSize offset = 0,
- VkDeviceSize size = VK_WHOLE_SIZE);
-
- // Readback part 0: Prepare for GPU usage (if necessary)
- void PrepareForGPUWrite(VkCommandBuffer command_buffer, VkAccessFlagBits dst_access_flags,
- VkPipelineStageFlagBits dst_pipeline_stage, VkDeviceSize offset = 0,
- VkDeviceSize size = VK_WHOLE_SIZE);
-
- // Readback part 1: Prepare for host readback from the GPU side
- void FlushGPUCache(VkCommandBuffer command_buffer, VkAccessFlagBits src_access_flags,
- VkPipelineStageFlagBits src_pipeline_stage, VkDeviceSize offset = 0,
- VkDeviceSize size = VK_WHOLE_SIZE);
-
- // Readback part 2: Prepare for host readback from the CPU side
- void InvalidateCPUCache(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE);
-
- // offset is from the start of the buffer, not from the map offset
- void Read(VkDeviceSize offset, void* data, size_t size, bool invalidate_caches = true);
- void Write(VkDeviceSize offset, const void* data, size_t size, bool invalidate_caches = true);
-
- // Creates the optimal format of image copy.
- bool Create(Type type, VkDeviceSize size, VkBufferUsageFlags usage);
-
- void Destroy(bool defer = true);
-
- // Allocates the resources needed to create a staging buffer.
- static bool AllocateBuffer(Type type, VkDeviceSize size, VkBufferUsageFlags usage, VkBuffer* out_buffer,
- VkDeviceMemory* out_memory, bool* out_coherent);
-
-protected:
- Type m_type = Type::Upload;
- VkBuffer m_buffer = VK_NULL_HANDLE;
- VkDeviceMemory m_memory = VK_NULL_HANDLE;
- VkDeviceSize m_size = 0;
- bool m_coherent = false;
-
- char* m_map_pointer = nullptr;
- VkDeviceSize m_map_offset = 0;
- VkDeviceSize m_map_size = 0;
-};
-} // namespace Vulkan
diff --git a/src/common/vulkan/staging_texture.cpp b/src/common/vulkan/staging_texture.cpp
deleted file mode 100644
index 559f640b7..000000000
--- a/src/common/vulkan/staging_texture.cpp
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
-#include "staging_texture.h"
-#include "../assert.h"
-#include "context.h"
-#include "util.h"
-
-namespace Vulkan {
-
-StagingTexture::StagingTexture() = default;
-
-StagingTexture::StagingTexture(StagingTexture&& move)
- : m_staging_buffer(std::move(move.m_staging_buffer)), m_flush_fence_counter(move.m_flush_fence_counter),
- m_width(move.m_width), m_height(move.m_height), m_texel_size(move.m_texel_size), m_map_stride(move.m_map_stride)
-{
- move.m_flush_fence_counter = 0;
- move.m_width = 0;
- move.m_height = 0;
- move.m_texel_size = 0;
- move.m_map_stride = 0;
-}
-
-StagingTexture& StagingTexture::operator=(StagingTexture&& move)
-{
- if (IsValid())
- Destroy(true);
-
- std::swap(m_staging_buffer, move.m_staging_buffer);
- std::swap(m_flush_fence_counter, move.m_flush_fence_counter);
- std::swap(m_width, move.m_width);
- std::swap(m_height, move.m_height);
- std::swap(m_texel_size, move.m_texel_size);
- std::swap(m_map_stride, move.m_map_stride);
- return *this;
-}
-
-StagingTexture::~StagingTexture()
-{
- if (IsValid())
- Destroy(true);
-}
-
-bool StagingTexture::Create(StagingBuffer::Type type, VkFormat format, u32 width, u32 height)
-{
- const u32 texel_size = Util::GetTexelSize(format);
- const u32 map_stride = texel_size * width;
- const u32 buffer_size = map_stride * height;
-
- VkBufferUsageFlags usage_flags;
- switch (type)
- {
- case StagingBuffer::Type::Readback:
- usage_flags = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
- break;
- case StagingBuffer::Type::Upload:
- usage_flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
- break;
- case StagingBuffer::Type::Mutable:
- default:
- usage_flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
- break;
- }
-
- StagingBuffer new_buffer;
- if (!new_buffer.Create(type, buffer_size, usage_flags) || !new_buffer.Map())
- return false;
-
- if (IsValid())
- Destroy(true);
-
- m_staging_buffer = std::move(new_buffer);
- m_width = width;
- m_height = height;
- m_texel_size = texel_size;
- m_map_stride = map_stride;
- return true;
-}
-
-void StagingTexture::Destroy(bool defer /* = true */)
-{
- if (!IsValid())
- return;
-
- m_staging_buffer.Destroy(defer);
- m_flush_fence_counter = 0;
- m_width = 0;
- m_height = 0;
- m_texel_size = 0;
- m_map_stride = 0;
-}
-
-void StagingTexture::CopyFromTexture(VkCommandBuffer command_buffer, Texture& src_texture, u32 src_x, u32 src_y,
- u32 src_layer, u32 src_level, u32 dst_x, u32 dst_y, u32 width, u32 height)
-{
- Assert(m_staging_buffer.GetType() == StagingBuffer::Type::Readback ||
- m_staging_buffer.GetType() == StagingBuffer::Type::Mutable);
- Assert((src_x + width) <= src_texture.GetWidth() && (src_y + height) <= src_texture.GetHeight());
- Assert((dst_x + width) <= m_width && (dst_y + height) <= m_height);
-
- const Vulkan::Util::DebugScope debugScope(command_buffer,
- "StagingTexture::CopyFromTexture: {%u,%u} Lyr:%u Lvl:%u {%u,%u} %ux%u",
- src_x, src_y, src_layer, src_level, dst_x, dst_y, width, height);
-
- VkImageLayout old_layout = src_texture.GetLayout();
- src_texture.TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
-
- // Issue the image->buffer copy, but delay it for now.
- VkBufferImageCopy image_copy = {};
- const VkImageAspectFlags aspect =
- Util ::IsDepthFormat(src_texture.GetFormat()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
- image_copy.bufferOffset = static_cast(dst_y * m_map_stride + dst_x * m_texel_size);
- image_copy.bufferRowLength = m_width;
- image_copy.bufferImageHeight = 0;
- image_copy.imageSubresource = {aspect, src_level, src_layer, 1};
- image_copy.imageOffset = {static_cast(src_x), static_cast(src_y), 0};
- image_copy.imageExtent = {width, height, 1u};
- vkCmdCopyImageToBuffer(command_buffer, src_texture.GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
- m_staging_buffer.GetBuffer(), 1, &image_copy);
-
- // Restore old source texture layout.
- src_texture.TransitionToLayout(command_buffer, old_layout);
-}
-
-void StagingTexture::CopyFromTexture(Texture& src_texture, u32 src_x, u32 src_y, u32 src_layer, u32 src_level,
- u32 dst_x, u32 dst_y, u32 width, u32 height)
-{
- const Vulkan::Util::DebugScope debugScope(g_vulkan_context->GetCurrentCommandBuffer(),
- "StagingTexture::CopyFromTexture: {%u,%u} Lyr:%u Lvl:%u {%u,%u} %ux%u",
- src_x, src_y, src_layer, src_level, dst_x, dst_y, width, height);
- CopyFromTexture(g_vulkan_context->GetCurrentCommandBuffer(), src_texture, src_x, src_y, src_layer, src_level, dst_x,
- dst_y, width, height);
-
- m_needs_flush = true;
- m_flush_fence_counter = g_vulkan_context->GetCurrentFenceCounter();
-}
-
-void StagingTexture::CopyToTexture(VkCommandBuffer command_buffer, u32 src_x, u32 src_y, Texture& dst_texture,
- u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, u32 width, u32 height)
-{
- Assert(m_staging_buffer.GetType() == StagingBuffer::Type::Upload ||
- m_staging_buffer.GetType() == StagingBuffer::Type::Mutable);
- Assert((dst_x + width) <= dst_texture.GetWidth() && (dst_y + height) <= dst_texture.GetHeight());
- Assert((src_x + width) <= m_width && (src_y + height) <= m_height);
-
- // Flush caches before copying.
- m_staging_buffer.FlushCPUCache();
-
- VkImageLayout old_layout = dst_texture.GetLayout();
- dst_texture.TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
-
- // Issue the image->buffer copy, but delay it for now.
- VkBufferImageCopy image_copy = {};
- image_copy.bufferOffset = static_cast(src_y * m_map_stride + src_x * m_texel_size);
- image_copy.bufferRowLength = m_width;
- image_copy.bufferImageHeight = 0;
- image_copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, dst_level, dst_layer, 1};
- image_copy.imageOffset = {static_cast(dst_x), static_cast(dst_y), 0};
- image_copy.imageExtent = {width, height, 1u};
- vkCmdCopyBufferToImage(command_buffer, m_staging_buffer.GetBuffer(), dst_texture.GetImage(),
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
-
- // Restore old source texture layout.
- dst_texture.TransitionToLayout(command_buffer, old_layout);
-}
-
-void StagingTexture::CopyToTexture(u32 src_x, u32 src_y, Texture& dst_texture, u32 dst_x, u32 dst_y, u32 dst_layer,
- u32 dst_level, u32 width, u32 height)
-{
- const Vulkan::Util::DebugScope debugScope(g_vulkan_context->GetCurrentCommandBuffer(),
- "StagingTexture::CopyToTexture: {%u,%u} | {%u,%u} Lyr:%u Lvl:%u %ux%u",
- src_x, src_y, dst_x, dst_y, dst_layer, dst_level, width, height);
- CopyToTexture(g_vulkan_context->GetCurrentCommandBuffer(), src_x, src_y, dst_texture, dst_x, dst_y, dst_layer,
- dst_level, width, height);
-
- m_needs_flush = true;
- m_flush_fence_counter = g_vulkan_context->GetCurrentFenceCounter();
-}
-
-void StagingTexture::Flush()
-{
- if (!m_needs_flush)
- return;
-
- // Is this copy in the current command buffer?
- if (g_vulkan_context->GetCurrentFenceCounter() == m_flush_fence_counter)
- {
- // Execute the command buffer and wait for it to finish.
- g_vulkan_context->ExecuteCommandBuffer(true);
- }
- else
- {
- // Wait for the GPU to finish with it.
- g_vulkan_context->WaitForFenceCounter(m_flush_fence_counter);
- }
-
- // For readback textures, invalidate the CPU cache as there is new data there.
- if (m_staging_buffer.GetType() == StagingBuffer::Type::Readback ||
- m_staging_buffer.GetType() == StagingBuffer::Type::Mutable)
- {
- m_staging_buffer.InvalidateCPUCache();
- }
-
- m_needs_flush = false;
-}
-
-void StagingTexture::ReadTexels(u32 src_x, u32 src_y, u32 width, u32 height, void* out_ptr, u32 out_stride)
-{
- Assert(m_staging_buffer.GetType() != StagingBuffer::Type::Upload);
- Assert((src_x + width) <= m_width && (src_y + height) <= m_height);
- PrepareForAccess();
-
- // Offset pointer to point to start of region being copied out.
- const char* current_ptr = m_staging_buffer.GetMapPointer();
- current_ptr += src_y * m_map_stride;
- current_ptr += src_x * m_texel_size;
-
- // Optimal path: same dimensions, same stride.
- if (src_x == 0 && width == m_width && m_map_stride == out_stride)
- {
- std::memcpy(out_ptr, current_ptr, m_map_stride * height);
- return;
- }
-
- size_t copy_size = std::min(width * m_texel_size, m_map_stride);
- char* dst_ptr = reinterpret_cast(out_ptr);
- for (u32 row = 0; row < height; row++)
- {
- std::memcpy(dst_ptr, current_ptr, copy_size);
- current_ptr += m_map_stride;
- dst_ptr += out_stride;
- }
-}
-
-void StagingTexture::ReadTexel(u32 x, u32 y, void* out_ptr)
-{
- Assert(m_staging_buffer.GetType() != StagingBuffer::Type::Upload);
- Assert(x < m_width && y < m_height);
- PrepareForAccess();
-
- const char* src_ptr = GetMappedPointer() + y * GetMappedStride() + x * m_texel_size;
- std::memcpy(out_ptr, src_ptr, m_texel_size);
-}
-
-void StagingTexture::WriteTexels(u32 dst_x, u32 dst_y, u32 width, u32 height, const void* in_ptr, u32 in_stride)
-{
- Assert(m_staging_buffer.GetType() != StagingBuffer::Type::Readback);
- Assert((dst_x + width) <= m_width && (dst_y + height) <= m_height);
- PrepareForAccess();
-
- // Offset pointer to point to start of region being copied to.
- char* current_ptr = GetMappedPointer();
- current_ptr += dst_y * m_map_stride;
- current_ptr += dst_x * m_texel_size;
-
- // Optimal path: same dimensions, same stride.
- if (dst_x == 0 && width == m_width && m_map_stride == in_stride)
- {
- std::memcpy(current_ptr, in_ptr, m_map_stride * height);
- return;
- }
-
- size_t copy_size = std::min(width * m_texel_size, m_map_stride);
- const char* src_ptr = reinterpret_cast(in_ptr);
- for (u32 row = 0; row < height; row++)
- {
- std::memcpy(current_ptr, src_ptr, copy_size);
- current_ptr += m_map_stride;
- src_ptr += in_stride;
- }
-}
-
-void StagingTexture::WriteTexel(u32 x, u32 y, const void* in_ptr)
-{
- Assert(x < m_width && y < m_height);
- PrepareForAccess();
-
- char* dest_ptr = GetMappedPointer() + y * m_map_stride + x * m_texel_size;
- std::memcpy(dest_ptr, in_ptr, m_texel_size);
-}
-
-void StagingTexture::PrepareForAccess()
-{
- Assert(IsMapped());
- if (m_needs_flush)
- Flush();
-}
-
-} // namespace Vulkan
\ No newline at end of file
diff --git a/src/common/vulkan/staging_texture.h b/src/common/vulkan/staging_texture.h
deleted file mode 100644
index f2a7b6943..000000000
--- a/src/common/vulkan/staging_texture.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
-#pragma once
-#include "staging_buffer.h"
-#include "texture.h"
-
-namespace Vulkan {
-
-class StagingTexture final
-{
-public:
- StagingTexture();
- StagingTexture(StagingTexture&& move);
- StagingTexture(const StagingTexture&) = delete;
- ~StagingTexture();
-
- StagingTexture& operator=(StagingTexture&& move);
- StagingTexture& operator=(const StagingTexture&) = delete;
-
- ALWAYS_INLINE bool IsValid() const { return m_staging_buffer.IsValid(); }
- ALWAYS_INLINE bool IsMapped() const { return m_staging_buffer.IsMapped(); }
- ALWAYS_INLINE const char* GetMappedPointer() const { return m_staging_buffer.GetMapPointer(); }
- ALWAYS_INLINE char* GetMappedPointer() { return m_staging_buffer.GetMapPointer(); }
- ALWAYS_INLINE u32 GetMappedStride() const { return m_map_stride; }
- ALWAYS_INLINE u32 GetWidth() const { return m_width; }
- ALWAYS_INLINE u32 GetHeight() const { return m_height; }
-
- bool Create(StagingBuffer::Type type, VkFormat format, u32 width, u32 height);
- void Destroy(bool defer = true);
-
- // Copies from the GPU texture object to the staging texture, which can be mapped/read by the CPU.
- // Both src_rect and dst_rect must be with within the bounds of the the specified textures.
- void CopyFromTexture(VkCommandBuffer command_buffer, Texture& src_texture, u32 src_x, u32 src_y, u32 src_layer,
- u32 src_level, u32 dst_x, u32 dst_y, u32 width, u32 height);
- void CopyFromTexture(Texture& src_texture, u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 dst_x, u32 dst_y,
- u32 width, u32 height);
-
- // Wrapper for copying a whole layer of a texture to a readback texture.
- // Assumes that the level of src texture and this texture have the same dimensions.
- void CopyToTexture(VkCommandBuffer command_buffer, u32 src_x, u32 src_y, Texture& dst_texture, u32 dst_x, u32 dst_y,
- u32 dst_layer, u32 dst_level, u32 width, u32 height);
- void CopyToTexture(u32 src_x, u32 src_y, Texture& dst_texture, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level,
- u32 width, u32 height);
-
- // Flushes pending writes from the CPU to the GPU, and reads from the GPU to the CPU.
- // This may cause a command buffer flush depending on if one has occurred between the last
- // call to CopyFromTexture()/CopyToTexture() and the Flush() call.
- void Flush();
-
- // Reads the specified rectangle from the staging texture to out_ptr, with the specified stride
- // (length in bytes of each row). CopyFromTexture must be called first. The contents of any
- // texels outside of the rectangle used for CopyFromTexture is undefined.
- void ReadTexels(u32 src_x, u32 src_y, u32 width, u32 height, void* out_ptr, u32 out_stride);
- void ReadTexel(u32 x, u32 y, void* out_ptr);
-
- // Copies the texels from in_ptr to the staging texture, which can be read by the GPU, with the
- // specified stride (length in bytes of each row). After updating the staging texture with all
- // changes, call CopyToTexture() to update the GPU copy.
- void WriteTexels(u32 dst_x, u32 dst_y, u32 width, u32 height, const void* in_ptr, u32 in_stride);
- void WriteTexel(u32 x, u32 y, const void* in_ptr);
-
-private:
- void PrepareForAccess();
-
- StagingBuffer m_staging_buffer;
- u64 m_flush_fence_counter = 0;
- u32 m_width = 0;
- u32 m_height = 0;
- u32 m_texel_size = 0;
- u32 m_map_stride = 0;
- bool m_needs_flush = false;
-};
-
-} // namespace Vulkan
\ No newline at end of file
diff --git a/src/common/vulkan/stream_buffer.cpp b/src/common/vulkan/stream_buffer.cpp
index 16d8b4f77..ca2815477 100644
--- a/src/common/vulkan/stream_buffer.cpp
+++ b/src/common/vulkan/stream_buffer.cpp
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#include "stream_buffer.h"
#include "../align.h"
#include "../assert.h"
@@ -12,15 +7,20 @@
Log_SetChannel(Vulkan::StreamBuffer);
namespace Vulkan {
-
StreamBuffer::StreamBuffer() = default;
StreamBuffer::StreamBuffer(StreamBuffer&& move)
- : m_usage(move.m_usage), m_size(move.m_size), m_current_offset(move.m_current_offset),
- m_current_space(move.m_current_space), m_current_gpu_position(move.m_current_gpu_position), m_buffer(move.m_buffer),
- m_memory(move.m_memory), m_host_pointer(move.m_host_pointer), m_tracked_fences(std::move(move.m_tracked_fences)),
- m_coherent_mapping(move.m_coherent_mapping)
+ : m_size(move.m_size), m_current_offset(move.m_current_offset), m_current_space(move.m_current_space),
+ m_current_gpu_position(move.m_current_gpu_position), m_allocation(move.m_allocation), m_buffer(move.m_buffer),
+ m_host_pointer(move.m_host_pointer), m_tracked_fences(std::move(move.m_tracked_fences))
{
+ move.m_size = 0;
+ move.m_current_offset = 0;
+ move.m_current_space = 0;
+ move.m_current_gpu_position = 0;
+ move.m_allocation = VK_NULL_HANDLE;
+ move.m_buffer = VK_NULL_HANDLE;
+ move.m_host_pointer = nullptr;
}
StreamBuffer::~StreamBuffer()
@@ -34,130 +34,74 @@ StreamBuffer& StreamBuffer::operator=(StreamBuffer&& move)
if (IsValid())
Destroy(true);
- std::swap(m_usage, move.m_usage);
std::swap(m_size, move.m_size);
std::swap(m_current_offset, move.m_current_offset);
std::swap(m_current_space, move.m_current_space);
std::swap(m_current_gpu_position, move.m_current_gpu_position);
std::swap(m_buffer, move.m_buffer);
- std::swap(m_memory, move.m_memory);
std::swap(m_host_pointer, move.m_host_pointer);
std::swap(m_tracked_fences, move.m_tracked_fences);
- std::swap(m_coherent_mapping, move.m_coherent_mapping);
return *this;
}
bool StreamBuffer::Create(VkBufferUsageFlags usage, u32 size)
{
- // Create the buffer descriptor
- VkBufferCreateInfo buffer_create_info = {
- VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType
- nullptr, // const void* pNext
- 0, // VkBufferCreateFlags flags
- static_cast(size), // VkDeviceSize size
- usage, // VkBufferUsageFlags usage
- VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode
- 0, // uint32_t queueFamilyIndexCount
- nullptr // const uint32_t* pQueueFamilyIndices
- };
+ const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ nullptr,
+ 0,
+ static_cast(size),
+ usage,
+ VK_SHARING_MODE_EXCLUSIVE,
+ 0,
+ nullptr};
- VkBuffer buffer = VK_NULL_HANDLE;
- VkResult res = vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_create_info, nullptr, &buffer);
+ VmaAllocationCreateInfo aci = {};
+ aci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
+ aci.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
+ aci.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+
+ VmaAllocationInfo ai = {};
+ VkBuffer new_buffer = VK_NULL_HANDLE;
+ VmaAllocation new_allocation = VK_NULL_HANDLE;
+ VkResult res = vmaCreateBuffer(g_vulkan_context->GetAllocator(), &bci, &aci, &new_buffer, &new_allocation, &ai);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: ");
return false;
}
- // Get memory requirements (types etc) for this buffer
- VkMemoryRequirements memory_requirements;
- vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), buffer, &memory_requirements);
-
- // Aim for a coherent mapping if possible.
- u32 memory_type_index =
- g_vulkan_context->GetUploadMemoryType(memory_requirements.memoryTypeBits, &m_coherent_mapping);
-
- // Allocate memory for backing this buffer
- VkMemoryAllocateInfo memory_allocate_info = {
- VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType
- nullptr, // const void* pNext
- memory_requirements.size, // VkDeviceSize allocationSize
- memory_type_index // uint32_t memoryTypeIndex
- };
- VkDeviceMemory memory = VK_NULL_HANDLE;
- res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory);
- if (res != VK_SUCCESS)
- {
- LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: ");
- vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr);
- return false;
- }
-
- // Bind memory to buffer
- res = vkBindBufferMemory(g_vulkan_context->GetDevice(), buffer, memory, 0);
- if (res != VK_SUCCESS)
- {
- LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: ");
- vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr);
- vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr);
- return false;
- }
-
- // Map this buffer into user-space
- void* mapped_ptr = nullptr;
- res = vkMapMemory(g_vulkan_context->GetDevice(), memory, 0, size, 0, &mapped_ptr);
- if (res != VK_SUCCESS)
- {
- LOG_VULKAN_ERROR(res, "vkMapMemory failed: ");
- vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr);
- vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr);
- return false;
- }
-
- // Unmap current host pointer (if there was a previous buffer)
- if (m_host_pointer)
- vkUnmapMemory(g_vulkan_context->GetDevice(), m_memory);
-
if (IsValid())
Destroy(true);
// Replace with the new buffer
- m_usage = usage;
m_size = size;
- m_buffer = buffer;
- m_memory = memory;
- m_host_pointer = reinterpret_cast(mapped_ptr);
m_current_offset = 0;
m_current_gpu_position = 0;
m_tracked_fences.clear();
+ m_allocation = new_allocation;
+ m_buffer = new_buffer;
+ m_host_pointer = static_cast(ai.pMappedData);
return true;
}
void StreamBuffer::Destroy(bool defer)
{
- if (m_host_pointer)
- {
- vkUnmapMemory(g_vulkan_context->GetDevice(), m_memory);
- m_host_pointer = nullptr;
- }
-
if (m_buffer != VK_NULL_HANDLE)
{
if (defer)
- g_vulkan_context->DeferBufferDestruction(m_buffer);
+ g_vulkan_context->DeferBufferDestruction(m_buffer, m_allocation);
else
- vkDestroyBuffer(g_vulkan_context->GetDevice(), m_buffer, nullptr);
- m_buffer = VK_NULL_HANDLE;
- }
- if (m_memory != VK_NULL_HANDLE)
- {
- if (defer)
- g_vulkan_context->DeferDeviceMemoryDestruction(m_memory);
- else
- vkFreeMemory(g_vulkan_context->GetDevice(), m_memory, nullptr);
- m_memory = VK_NULL_HANDLE;
+ vmaDestroyBuffer(g_vulkan_context->GetAllocator(), m_buffer, m_allocation);
}
+
+ m_size = 0;
+ m_current_offset = 0;
+ m_current_gpu_position = 0;
+ m_tracked_fences.clear();
+ m_buffer = VK_NULL_HANDLE;
+ m_allocation = VK_NULL_HANDLE;
+ m_host_pointer = nullptr;
}
bool StreamBuffer::ReserveMemory(u32 num_bytes, u32 alignment)
@@ -231,16 +175,11 @@ bool StreamBuffer::ReserveMemory(u32 num_bytes, u32 alignment)
void StreamBuffer::CommitMemory(u32 final_num_bytes)
{
- Assert((m_current_offset + final_num_bytes) <= m_size);
- Assert(final_num_bytes <= m_current_space);
+ DebugAssert((m_current_offset + final_num_bytes) <= m_size);
+ DebugAssert(final_num_bytes <= m_current_space);
// For non-coherent mappings, flush the memory range
- if (!m_coherent_mapping)
- {
- VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, m_current_offset,
- final_num_bytes};
- vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range);
- }
+ vmaFlushAllocation(g_vulkan_context->GetAllocator(), m_allocation, m_current_offset, final_num_bytes);
m_current_offset += final_num_bytes;
m_current_space -= final_num_bytes;
@@ -366,4 +305,4 @@ bool StreamBuffer::WaitForClearSpace(u32 num_bytes)
return true;
}
-} // namespace Vulkan
+} // namespace Vulkan
\ No newline at end of file
diff --git a/src/common/vulkan/stream_buffer.h b/src/common/vulkan/stream_buffer.h
index bc5935bd0..c9f259cea 100644
--- a/src/common/vulkan/stream_buffer.h
+++ b/src/common/vulkan/stream_buffer.h
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#pragma once
#include "../types.h"
@@ -26,9 +21,9 @@ public:
ALWAYS_INLINE bool IsValid() const { return (m_buffer != VK_NULL_HANDLE); }
ALWAYS_INLINE VkBuffer GetBuffer() const { return m_buffer; }
ALWAYS_INLINE const VkBuffer* GetBufferPointer() const { return &m_buffer; }
- ALWAYS_INLINE VkDeviceMemory GetDeviceMemory() const { return m_memory; }
- ALWAYS_INLINE void* GetHostPointer() const { return m_host_pointer; }
- ALWAYS_INLINE void* GetCurrentHostPointer() const { return m_host_pointer + m_current_offset; }
+ ALWAYS_INLINE VmaAllocation GetAllocation() const { return m_allocation; }
+ ALWAYS_INLINE u8* GetHostPointer() const { return m_host_pointer; }
+ ALWAYS_INLINE u8* GetCurrentHostPointer() const { return m_host_pointer + m_current_offset; }
ALWAYS_INLINE u32 GetCurrentSize() const { return m_size; }
ALWAYS_INLINE u32 GetCurrentSpace() const { return m_current_space; }
ALWAYS_INLINE u32 GetCurrentOffset() const { return m_current_offset; }
@@ -47,20 +42,17 @@ private:
// Waits for as many fences as needed to allocate num_bytes bytes from the buffer.
bool WaitForClearSpace(u32 num_bytes);
- VkBufferUsageFlags m_usage = 0;
u32 m_size = 0;
u32 m_current_offset = 0;
u32 m_current_space = 0;
u32 m_current_gpu_position = 0;
+ VmaAllocation m_allocation = VK_NULL_HANDLE;
VkBuffer m_buffer = VK_NULL_HANDLE;
- VkDeviceMemory m_memory = VK_NULL_HANDLE;
u8* m_host_pointer = nullptr;
// List of fences and the corresponding positions in the buffer
std::deque> m_tracked_fences;
-
- bool m_coherent_mapping = false;
};
} // namespace Vulkan
diff --git a/src/common/vulkan/swap_chain.cpp b/src/common/vulkan/swap_chain.cpp
index 251211bd8..0cdb7c033 100644
--- a/src/common/vulkan/swap_chain.cpp
+++ b/src/common/vulkan/swap_chain.cpp
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#include "swap_chain.h"
#include "../assert.h"
#include "../log.h"
diff --git a/src/common/vulkan/swap_chain.h b/src/common/vulkan/swap_chain.h
index 0c8341495..2297539b1 100644
--- a/src/common/vulkan/swap_chain.h
+++ b/src/common/vulkan/swap_chain.h
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#pragma once
#include "../types.h"
diff --git a/src/common/vulkan/texture.cpp b/src/common/vulkan/texture.cpp
index e63c5d268..5cfcb7047 100644
--- a/src/common/vulkan/texture.cpp
+++ b/src/common/vulkan/texture.cpp
@@ -1,21 +1,22 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#include "texture.h"
+#include "../align.h"
#include "../assert.h"
+#include "../log.h"
+#include "../string_util.h"
#include "context.h"
#include "util.h"
#include
+Log_SetChannel(Texture);
-namespace Vulkan {
-Texture::Texture() = default;
+static constexpr VkComponentMapping s_identity_swizzle{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
+ VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
-Texture::Texture(Texture&& move)
+Vulkan::Texture::Texture() = default;
+
+Vulkan::Texture::Texture(Texture&& move)
: m_width(move.m_width), m_height(move.m_height), m_levels(move.m_levels), m_layers(move.m_layers),
m_format(move.m_format), m_samples(move.m_samples), m_view_type(move.m_view_type), m_layout(move.m_layout),
- m_image(move.m_image), m_device_memory(move.m_device_memory), m_view(move.m_view)
+ m_image(move.m_image), m_allocation(move.m_allocation), m_view(move.m_view)
{
move.m_width = 0;
move.m_height = 0;
@@ -26,17 +27,17 @@ Texture::Texture(Texture&& move)
move.m_view_type = VK_IMAGE_VIEW_TYPE_2D;
move.m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
move.m_image = VK_NULL_HANDLE;
- move.m_device_memory = VK_NULL_HANDLE;
+ move.m_allocation = VK_NULL_HANDLE;
move.m_view = VK_NULL_HANDLE;
}
-Texture::~Texture()
+Vulkan::Texture::~Texture()
{
if (IsValid())
Destroy(true);
}
-Vulkan::Texture& Texture::operator=(Texture&& move)
+Vulkan::Texture& Vulkan::Texture::operator=(Texture&& move)
{
if (IsValid())
Destroy(true);
@@ -50,85 +51,78 @@ Vulkan::Texture& Texture::operator=(Texture&& move)
std::swap(m_view_type, move.m_view_type);
std::swap(m_layout, move.m_layout);
std::swap(m_image, move.m_image);
- std::swap(m_device_memory, move.m_device_memory);
+ std::swap(m_allocation, move.m_allocation);
std::swap(m_view, move.m_view);
return *this;
}
-bool Texture::Create(u32 width, u32 height, u32 levels, u32 layers, VkFormat format, VkSampleCountFlagBits samples,
- VkImageViewType view_type, VkImageTiling tiling, VkImageUsageFlags usage)
+bool Vulkan::Texture::Create(u32 width, u32 height, u32 levels, u32 layers, VkFormat format,
+ VkSampleCountFlagBits samples, VkImageViewType view_type, VkImageTiling tiling,
+ VkImageUsageFlags usage, bool dedicated_memory /* = false */,
+ const VkComponentMapping* swizzle /* = nullptr */)
{
- VkImageCreateInfo image_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
- nullptr,
- 0,
- VK_IMAGE_TYPE_2D,
- format,
- {width, height, 1},
- levels,
- layers,
- samples,
- tiling,
- usage,
- VK_SHARING_MODE_EXCLUSIVE,
- 0,
- nullptr,
- VK_IMAGE_LAYOUT_UNDEFINED};
+ const VkImageCreateInfo image_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ nullptr,
+ 0,
+ VK_IMAGE_TYPE_2D,
+ format,
+ {width, height, 1},
+ levels,
+ layers,
+ samples,
+ tiling,
+ usage,
+ VK_SHARING_MODE_EXCLUSIVE,
+ 0,
+ nullptr,
+ VK_IMAGE_LAYOUT_UNDEFINED};
+
+ VmaAllocationCreateInfo aci = {};
+ aci.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+ aci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT;
+ aci.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+ if (dedicated_memory)
+ aci.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
VkImage image = VK_NULL_HANDLE;
- VkResult res = vkCreateImage(g_vulkan_context->GetDevice(), &image_info, nullptr, &image);
- if (res != VK_SUCCESS)
+ VmaAllocation allocation = VK_NULL_HANDLE;
+ VkResult res = vmaCreateImage(g_vulkan_context->GetAllocator(), &image_info, &aci, &image, &allocation, nullptr);
+ if (res != VK_SUCCESS && dedicated_memory)
{
- LOG_VULKAN_ERROR(res, "vkCreateImage failed: ");
+ // try without dedicated memory
+ aci.flags &= ~VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
+ res = vmaCreateImage(g_vulkan_context->GetAllocator(), &image_info, &aci, &image, &allocation, nullptr);
+ }
+ if (res == VK_ERROR_OUT_OF_DEVICE_MEMORY)
+ {
+ Log_WarningPrintf("Failed to allocate device memory for %ux%u texture", width, height);
+ return false;
+ }
+ else if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vmaCreateImage failed: ");
return false;
}
- // Allocate memory to back this texture, we want device local memory in this case
- VkMemoryRequirements memory_requirements;
- vkGetImageMemoryRequirements(g_vulkan_context->GetDevice(), image, &memory_requirements);
-
- VkMemoryAllocateInfo memory_info = {
- VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, nullptr, memory_requirements.size,
- g_vulkan_context->GetMemoryType(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)};
-
- VkDeviceMemory device_memory;
- res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_info, nullptr, &device_memory);
- if (res != VK_SUCCESS)
- {
- LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: ");
- vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr);
- return false;
- }
-
- res = vkBindImageMemory(g_vulkan_context->GetDevice(), image, device_memory, 0);
- if (res != VK_SUCCESS)
- {
- LOG_VULKAN_ERROR(res, "vkBindImageMemory failed: ");
- vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr);
- vkFreeMemory(g_vulkan_context->GetDevice(), device_memory, nullptr);
- return false;
- }
-
- VkImageViewCreateInfo view_info = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
- nullptr,
- 0,
- image,
- view_type,
- format,
- {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
- VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY},
- {Util::IsDepthFormat(format) ?
- static_cast(VK_IMAGE_ASPECT_DEPTH_BIT) :
- static_cast(VK_IMAGE_ASPECT_COLOR_BIT),
- 0, levels, 0, layers}};
+ const VkImageViewCreateInfo view_info = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ nullptr,
+ 0,
+ image,
+ view_type,
+ format,
+ swizzle ? *swizzle : s_identity_swizzle,
+ {Util::IsDepthFormat(format) ?
+ static_cast(VK_IMAGE_ASPECT_DEPTH_BIT) :
+ static_cast(VK_IMAGE_ASPECT_COLOR_BIT),
+ 0, levels, 0, layers}};
VkImageView view = VK_NULL_HANDLE;
res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateImageView failed: ");
- vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr);
- vkFreeMemory(g_vulkan_context->GetDevice(), device_memory, nullptr);
+ vmaDestroyImage(g_vulkan_context->GetAllocator(), image, allocation);
return false;
}
@@ -143,27 +137,27 @@ bool Texture::Create(u32 width, u32 height, u32 levels, u32 layers, VkFormat for
m_samples = samples;
m_view_type = view_type;
m_image = image;
- m_device_memory = device_memory;
+ m_allocation = allocation;
m_view = view;
return true;
}
-bool Texture::Adopt(VkImage existing_image, VkImageViewType view_type, u32 width, u32 height, u32 levels, u32 layers,
- VkFormat format, VkSampleCountFlagBits samples)
+bool Vulkan::Texture::Adopt(VkImage existing_image, VkImageViewType view_type, u32 width, u32 height, u32 levels,
+ u32 layers, VkFormat format, VkSampleCountFlagBits samples,
+ const VkComponentMapping* swizzle /* = nullptr */)
{
// Only need to create the image view, this is mainly for swap chains.
- VkImageViewCreateInfo view_info = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
- nullptr,
- 0,
- existing_image,
- view_type,
- format,
- {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
- VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY},
- {Util::IsDepthFormat(format) ?
- static_cast(VK_IMAGE_ASPECT_DEPTH_BIT) :
- static_cast(VK_IMAGE_ASPECT_COLOR_BIT),
- 0, levels, 0, layers}};
+ const VkImageViewCreateInfo view_info = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ nullptr,
+ 0,
+ existing_image,
+ view_type,
+ format,
+ swizzle ? *swizzle : s_identity_swizzle,
+ {Util::IsDepthFormat(format) ?
+ static_cast(VK_IMAGE_ASPECT_DEPTH_BIT) :
+ static_cast(VK_IMAGE_ASPECT_COLOR_BIT),
+ 0, levels, 0, layers}};
// Memory is managed by the owner of the image.
VkImageView view = VK_NULL_HANDLE;
@@ -189,7 +183,7 @@ bool Texture::Adopt(VkImage existing_image, VkImageViewType view_type, u32 width
return true;
}
-void Texture::Destroy(bool defer /* = true */)
+void Vulkan::Texture::Destroy(bool defer /* = true */)
{
if (m_view != VK_NULL_HANDLE)
{
@@ -201,20 +195,15 @@ void Texture::Destroy(bool defer /* = true */)
}
// If we don't have device memory allocated, the image is not owned by us (e.g. swapchain)
- if (m_device_memory != VK_NULL_HANDLE)
+ if (m_allocation != VK_NULL_HANDLE)
{
- DebugAssert(m_image != VK_NULL_HANDLE);
+ Assert(m_image != VK_NULL_HANDLE);
if (defer)
- g_vulkan_context->DeferImageDestruction(m_image);
+ g_vulkan_context->DeferImageDestruction(m_image, m_allocation);
else
- vkDestroyImage(g_vulkan_context->GetDevice(), m_image, nullptr);
+ vmaDestroyImage(g_vulkan_context->GetAllocator(), m_image, m_allocation);
m_image = VK_NULL_HANDLE;
-
- if (defer)
- g_vulkan_context->DeferDeviceMemoryDestruction(m_device_memory);
- else
- vkFreeMemory(g_vulkan_context->GetDevice(), m_device_memory, nullptr);
- m_device_memory = VK_NULL_HANDLE;
+ m_allocation = VK_NULL_HANDLE;
}
m_width = 0;
@@ -225,17 +214,14 @@ void Texture::Destroy(bool defer /* = true */)
m_samples = VK_SAMPLE_COUNT_1_BIT;
m_view_type = VK_IMAGE_VIEW_TYPE_2D;
m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
- m_image = VK_NULL_HANDLE;
- m_device_memory = VK_NULL_HANDLE;
- m_view = VK_NULL_HANDLE;
}
-void Texture::OverrideImageLayout(VkImageLayout new_layout)
+void Vulkan::Texture::OverrideImageLayout(VkImageLayout new_layout)
{
m_layout = new_layout;
}
-void Texture::TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout new_layout)
+void Vulkan::Texture::TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout new_layout)
{
if (m_layout == new_layout)
return;
@@ -247,9 +233,9 @@ void Texture::TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout n
m_layout = new_layout;
}
-void Texture::TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, u32 start_level, u32 num_levels,
- u32 start_layer, u32 num_layers, VkImageLayout old_layout,
- VkImageLayout new_layout)
+void Vulkan::Texture::TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, u32 start_level, u32 num_levels,
+ u32 start_layer, u32 num_layers, VkImageLayout old_layout,
+ VkImageLayout new_layout)
{
const Vulkan::Util::DebugScope debugScope(
command_buffer, "Texture::TransitionSubresourcesToLayout: Lvl:[%u,%u) Lyr:[%u,%u) %s -> %s", start_level,
@@ -369,7 +355,7 @@ void Texture::TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, u32
vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier);
}
-VkFramebuffer Texture::CreateFramebuffer(VkRenderPass render_pass)
+VkFramebuffer Vulkan::Texture::CreateFramebuffer(VkRenderPass render_pass)
{
const VkFramebufferCreateInfo ci = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0u, render_pass, 1, &m_view, m_width, m_height, m_layers};
@@ -384,8 +370,8 @@ VkFramebuffer Texture::CreateFramebuffer(VkRenderPass render_pass)
return fb;
}
-void Texture::UpdateFromBuffer(VkCommandBuffer cmdbuf, u32 level, u32 layer, u32 x, u32 y, u32 width, u32 height,
- VkBuffer buffer, u32 buffer_offset, u32 row_length)
+void Vulkan::Texture::UpdateFromBuffer(VkCommandBuffer cmdbuf, u32 level, u32 layer, u32 x, u32 y, u32 width,
+ u32 height, VkBuffer buffer, u32 buffer_offset, u32 row_length)
{
const VkImageLayout old_layout = m_layout;
const Vulkan::Util::DebugScope debugScope(cmdbuf, "Texture::UpdateFromBuffer: Lvl:%u Lyr:%u {%u,%u} %ux%u", level,
@@ -404,4 +390,117 @@ void Texture::UpdateFromBuffer(VkCommandBuffer cmdbuf, u32 level, u32 layer, u32
TransitionToLayout(cmdbuf, old_layout);
}
-} // namespace Vulkan
+u32 Vulkan::Texture::CalcUpdatePitch(u32 width) const
+{
+ return Common::AlignUp(width * Vulkan::Util::GetTexelSize(m_format),
+ g_vulkan_context->GetBufferCopyRowPitchAlignment());
+}
+
+u32 Vulkan::Texture::CalcUpdateRowLength(u32 pitch) const
+{
+ return pitch / Vulkan::Util::GetTexelSize(m_format);
+}
+
+bool Vulkan::Texture::BeginUpdate(u32 width, u32 height, void** out_buffer, u32* out_pitch)
+{
+ const u32 pitch = CalcUpdatePitch(width);
+ const u32 required_size = pitch * height;
+ StreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
+ if (required_size > buffer.GetCurrentSize())
+ return false;
+
+ // TODO: allocate temporary buffer if this fails...
+ if (!buffer.ReserveMemory(required_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
+ {
+ g_vulkan_context->ExecuteCommandBuffer(false);
+ if (!buffer.ReserveMemory(required_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
+ return false;
+ }
+
+ *out_buffer = buffer.GetCurrentHostPointer();
+ *out_pitch = pitch;
+ return true;
+}
+
+void Vulkan::Texture::EndUpdate(u32 x, u32 y, u32 width, u32 height)
+{
+ const u32 pitch = CalcUpdatePitch(width);
+ const u32 required_size = pitch * height;
+
+ StreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
+ const u32 buffer_offset = buffer.GetCurrentOffset();
+ buffer.CommitMemory(required_size);
+
+ UpdateFromBuffer(g_vulkan_context->GetCurrentCommandBuffer(), 0, 0, x, y, width, height, buffer.GetBuffer(),
+ buffer_offset, CalcUpdateRowLength(pitch));
+}
+
+bool Vulkan::Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_pitch)
+{
+ const u32 pitch = CalcUpdatePitch(width);
+ const u32 row_length = CalcUpdateRowLength(pitch);
+ const u32 required_size = pitch * height;
+ StreamBuffer& sbuffer = g_vulkan_context->GetTextureUploadBuffer();
+
+ // If the texture is larger than half our streaming buffer size, use a separate buffer.
+ // Otherwise allocation will either fail, or require lots of cmdbuffer submissions.
+ if (required_size > (g_vulkan_context->GetTextureUploadBuffer().GetCurrentSize() / 2))
+ {
+ const u32 size = data_pitch * height;
+ const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ nullptr,
+ 0,
+ static_cast(size),
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ VK_SHARING_MODE_EXCLUSIVE,
+ 0,
+ nullptr};
+
+ // Don't worry about setting the coherent bit for this upload, the main reason we had
+ // that set in StreamBuffer was for MoltenVK, which would upload the whole buffer on
+ // smaller uploads, but we're writing to the whole thing anyway.
+ VmaAllocationCreateInfo aci = {};
+ aci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
+ aci.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
+
+ VmaAllocationInfo ai;
+ VkBuffer buffer;
+ VmaAllocation allocation;
+ VkResult res = vmaCreateBuffer(g_vulkan_context->GetAllocator(), &bci, &aci, &buffer, &allocation, &ai);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vmaCreateBuffer() failed: ");
+ return VK_NULL_HANDLE;
+ }
+
+ // Immediately queue it for freeing after the command buffer finishes, since it's only needed for the copy.
+ g_vulkan_context->DeferBufferDestruction(buffer, allocation);
+
+ StringUtil::StrideMemCpy(ai.pMappedData, pitch, data, data_pitch, std::min(data_pitch, pitch), height);
+ vmaFlushAllocation(g_vulkan_context->GetAllocator(), allocation, 0, size);
+
+ UpdateFromBuffer(g_vulkan_context->GetCurrentCommandBuffer(), 0, 0, x, y, width, height, buffer, 0, row_length);
+ return true;
+ }
+ else
+ {
+ if (!sbuffer.ReserveMemory(required_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
+ {
+ g_vulkan_context->ExecuteCommandBuffer(false);
+ if (!sbuffer.ReserveMemory(required_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
+ {
+ Log_ErrorPrintf("Failed to reserve texture upload memory (%u bytes).", required_size);
+ return false;
+ }
+ }
+
+ const u32 buffer_offset = sbuffer.GetCurrentOffset();
+ StringUtil::StrideMemCpy(sbuffer.GetCurrentHostPointer(), pitch, data, data_pitch, std::min(data_pitch, pitch),
+ height);
+ sbuffer.CommitMemory(required_size);
+
+ UpdateFromBuffer(g_vulkan_context->GetCurrentCommandBuffer(), 0, 0, x, y, width, height, sbuffer.GetBuffer(),
+ buffer_offset, row_length);
+ return true;
+ }
+}
diff --git a/src/common/vulkan/texture.h b/src/common/vulkan/texture.h
index 86e1da7a3..fdcab5f8e 100644
--- a/src/common/vulkan/texture.h
+++ b/src/common/vulkan/texture.h
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#pragma once
#include "../types.h"
#include "loader.h"
@@ -24,7 +19,7 @@ public:
ALWAYS_INLINE bool IsValid() const { return (m_image != VK_NULL_HANDLE); }
/// An image is considered owned/managed if we control the memory.
- ALWAYS_INLINE bool IsOwned() const { return (m_device_memory != VK_NULL_HANDLE); }
+ ALWAYS_INLINE bool IsOwned() const { return (m_allocation != VK_NULL_HANDLE); }
ALWAYS_INLINE u32 GetWidth() const { return m_width; }
ALWAYS_INLINE u32 GetHeight() const { return m_height; }
@@ -37,14 +32,15 @@ public:
ALWAYS_INLINE VkImageLayout GetLayout() const { return m_layout; }
ALWAYS_INLINE VkImageViewType GetViewType() const { return m_view_type; }
ALWAYS_INLINE VkImage GetImage() const { return m_image; }
- ALWAYS_INLINE VkDeviceMemory GetDeviceMemory() const { return m_device_memory; }
+ ALWAYS_INLINE VmaAllocation GetAllocation() const { return m_allocation; }
ALWAYS_INLINE VkImageView GetView() const { return m_view; }
bool Create(u32 width, u32 height, u32 levels, u32 layers, VkFormat format, VkSampleCountFlagBits samples,
- VkImageViewType view_type, VkImageTiling tiling, VkImageUsageFlags usage);
+ VkImageViewType view_type, VkImageTiling tiling, VkImageUsageFlags usage, bool dedicated_memory = false,
+ const VkComponentMapping* swizzle = nullptr);
bool Adopt(VkImage existing_image, VkImageViewType view_type, u32 width, u32 height, u32 levels, u32 layers,
- VkFormat format, VkSampleCountFlagBits samples);
+ VkFormat format, VkSampleCountFlagBits samples, const VkComponentMapping* swizzle = nullptr);
void Destroy(bool defer = true);
@@ -62,6 +58,12 @@ public:
void UpdateFromBuffer(VkCommandBuffer cmdbuf, u32 level, u32 layer, u32 x, u32 y, u32 width, u32 height,
VkBuffer buffer, u32 buffer_offset, u32 row_length);
+ u32 CalcUpdatePitch(u32 width) const;
+ u32 CalcUpdateRowLength(u32 pitch) const;
+ bool BeginUpdate(u32 width, u32 height, void** out_buffer, u32* out_pitch);
+ void EndUpdate(u32 x, u32 y, u32 width, u32 height);
+ bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_pitch);
+
private:
u32 m_width = 0;
u32 m_height = 0;
@@ -73,7 +75,7 @@ private:
VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImage m_image = VK_NULL_HANDLE;
- VkDeviceMemory m_device_memory = VK_NULL_HANDLE;
+ VmaAllocation m_allocation = VK_NULL_HANDLE;
VkImageView m_view = VK_NULL_HANDLE;
};
diff --git a/src/common/vulkan/util.cpp b/src/common/vulkan/util.cpp
index b8b1e3f28..ee7c7d73a 100644
--- a/src/common/vulkan/util.cpp
+++ b/src/common/vulkan/util.cpp
@@ -1,8 +1,3 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#include "util.h"
#include "../assert.h"
#include "../log.h"
@@ -552,8 +547,9 @@ DebugScope::DebugScope(VkQueue context, const char* format, ...) : queu
va_end(ap);
const float depth_phase = depth / static_cast(max_depth);
- BeginDebugScope(queue, str,
- Palette(depth_phase, {0.5f, 0.5f, 0.5f}, {0.5f, 0.5f, 0.5f}, {2.0f, 1.0f, 0.0f}, {0.5f, 0.20f, 0.25f}));
+ BeginDebugScope(
+ queue, str,
+ Palette(depth_phase, {0.5f, 0.5f, 0.5f}, {0.5f, 0.5f, 0.5f}, {2.0f, 1.0f, 0.0f}, {0.5f, 0.20f, 0.25f}));
++depth;
}
}
diff --git a/src/common/vulkan/util.h b/src/common/vulkan/util.h
index 969a645c4..cb54237af 100644
--- a/src/common/vulkan/util.h
+++ b/src/common/vulkan/util.h
@@ -1,12 +1,8 @@
-// Copyright 2016 Dolphin Emulator Project
-// Copyright 2020 DuckStation Emulator Project
-// Licensed under GPLv2+
-// Refer to the LICENSE file included.
-
#pragma once
#include "../string.h"
#include "../types.h"
+#include "context.h"
#include "loader.h"
#include
#include
@@ -163,6 +159,20 @@ inline void SetObjectName(VkDevice device, T object_handle, const char* format,
#endif
}
+template<>
+inline void SetObjectName(VkDevice device, VmaAllocation object_handle, const char* format, ...)
+{
+#ifdef ENABLE_VULKAN_DEBUG_OBJECTS
+ std::va_list ap;
+ SmallString str;
+ va_start(ap, format);
+ str.FormatVA(format, ap);
+ va_end(ap);
+
+ vmaSetAllocationName(g_vulkan_context->GetAllocator(), object_handle, str);
+#endif
+}
+
// Command buffer debug utils
inline void BeginDebugScope(VkCommandBuffer command_buffer, const char* scope_name,
const std::array& scope_color = {0.5, 0.5, 0.5, 1.0})
diff --git a/src/core/gpu_hw_vulkan.cpp b/src/core/gpu_hw_vulkan.cpp
index a7cfd9621..ea8f5405e 100644
--- a/src/core/gpu_hw_vulkan.cpp
+++ b/src/core/gpu_hw_vulkan.cpp
@@ -254,7 +254,7 @@ void GPU_HW_Vulkan::MapBatchVertexPointer(u32 required_vertices)
Panic("Failed to reserve vertex stream buffer memory");
}
- m_batch_start_vertex_ptr = static_cast(m_vertex_stream_buffer.GetCurrentHostPointer());
+ m_batch_start_vertex_ptr = reinterpret_cast(m_vertex_stream_buffer.GetCurrentHostPointer());
m_batch_current_vertex_ptr = m_batch_start_vertex_ptr;
m_batch_end_vertex_ptr = m_batch_start_vertex_ptr + (m_vertex_stream_buffer.GetCurrentSpace() / sizeof(BatchVertex));
m_batch_base_vertex = m_vertex_stream_buffer.GetCurrentOffset() / sizeof(BatchVertex);
@@ -559,52 +559,50 @@ bool GPU_HW_Vulkan::CreateFramebuffer()
if (!m_vram_texture.Create(texture_width, texture_height, 1, 1, texture_format, samples, VK_IMAGE_VIEW_TYPE_2D,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
- VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT) ||
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, true) ||
!m_vram_depth_texture.Create(texture_width, texture_height, 1, 1, depth_format, samples, VK_IMAGE_VIEW_TYPE_2D,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT) ||
!m_vram_read_texture.Create(texture_width, texture_height, 1, 1, texture_format, VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
- VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT) ||
+ VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, true) ||
!m_display_texture.Create(
((m_downsample_mode == GPUDownsampleMode::Adaptive) ? VRAM_WIDTH : GPU_MAX_DISPLAY_WIDTH) * m_resolution_scale,
GPU_MAX_DISPLAY_HEIGHT * m_resolution_scale, 1, 1, texture_format, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
- VK_IMAGE_USAGE_TRANSFER_DST_BIT) ||
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT, true) ||
!m_vram_readback_texture.Create(VRAM_WIDTH, VRAM_HEIGHT, 1, 1, texture_format, VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
- VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT) ||
- !m_vram_readback_staging_texture.Create(Vulkan::StagingBuffer::Type::Readback, texture_format, VRAM_WIDTH / 2,
- VRAM_HEIGHT))
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, true))
{
return false;
}
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_texture.GetImage(), "VRAM Texture");
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_texture.GetView(), "VRAM Texture View");
- Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_texture.GetDeviceMemory(), "VRAM Texture Memory");
+ Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_texture.GetAllocation(), "VRAM Texture Memory");
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_depth_texture.GetImage(), "VRAM Depth Texture");
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_depth_texture.GetView(), "VRAM Depth Texture View");
- Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_depth_texture.GetDeviceMemory(),
+ Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_depth_texture.GetAllocation(),
"VRAM Depth Texture Memory");
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_read_texture.GetImage(), "VRAM Read Texture");
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_read_texture.GetView(), "VRAM Read Texture View");
- Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_read_texture.GetDeviceMemory(),
+ Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_read_texture.GetAllocation(),
"VRAM Read Texture Memory");
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_display_texture.GetImage(), "Display Texture");
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_display_texture.GetView(), "Display Texture View");
- Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_display_texture.GetDeviceMemory(),
+ Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_display_texture.GetAllocation(),
"Display Texture Memory");
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_readback_texture.GetImage(),
"VRAM Readback Texture");
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_readback_texture.GetView(),
"VRAM Readback Texture View");
- Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_readback_texture.GetDeviceMemory(),
+ Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_readback_texture.GetAllocation(),
"VRAM Readback Texture Memory");
m_vram_render_pass =
@@ -842,7 +840,6 @@ void GPU_HW_Vulkan::DestroyFramebuffer()
m_vram_texture.Destroy(false);
m_vram_readback_texture.Destroy(false);
m_display_texture.Destroy(false);
- m_vram_readback_staging_texture.Destroy(false);
}
bool GPU_HW_Vulkan::CreateVertexBuffer()
@@ -852,7 +849,7 @@ bool GPU_HW_Vulkan::CreateVertexBuffer()
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vertex_stream_buffer.GetBuffer(),
"Vertex Stream Buffer");
- Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vertex_stream_buffer.GetDeviceMemory(),
+ Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vertex_stream_buffer.GetAllocation(),
"Vertex Stream Buffer Memory");
return true;
}
@@ -864,7 +861,7 @@ bool GPU_HW_Vulkan::CreateUniformBuffer()
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_uniform_stream_buffer.GetBuffer(),
"Uniform Stream Buffer");
- Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_uniform_stream_buffer.GetDeviceMemory(),
+ Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_uniform_stream_buffer.GetAllocation(),
"Uniform Stream Buffer Memory");
return true;
}
@@ -909,7 +906,7 @@ bool GPU_HW_Vulkan::CreateTextureBuffer()
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_texture_stream_buffer.GetBuffer(),
"Texture Stream Buffer");
- Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_texture_stream_buffer.GetDeviceMemory(),
+ Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_texture_stream_buffer.GetAllocation(),
"Texture Stream Buffer Memory");
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_vram_write_descriptor_set, "VRAM Write Descriptor Set");
@@ -1577,15 +1574,10 @@ void GPU_HW_Vulkan::ReadVRAM(u32 x, u32 y, u32 width, u32 height)
m_vram_readback_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
m_vram_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
- // Stage the readback.
- m_vram_readback_staging_texture.CopyFromTexture(m_vram_readback_texture, 0, 0, 0, 0, 0, 0, encoded_width,
- encoded_height);
-
- // And copy it into our shadow buffer (will execute command buffer and stall).
- ExecuteCommandBuffer(true, true);
- m_vram_readback_staging_texture.ReadTexels(0, 0, encoded_width, encoded_height,
- &m_vram_shadow[copy_rect.top * VRAM_WIDTH + copy_rect.left],
- VRAM_WIDTH * sizeof(u16));
+ // Stage the readback and copy it into our shadow buffer (will execute command buffer and stall).
+ g_host_display->DownloadTexture(&m_vram_readback_texture, HostDisplayPixelFormat::RGBA8, 0, 0, encoded_width,
+ encoded_height, &m_vram_shadow[copy_rect.top * VRAM_WIDTH + copy_rect.left],
+ VRAM_WIDTH * sizeof(u16));
}
void GPU_HW_Vulkan::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color)
@@ -1831,7 +1823,7 @@ bool GPU_HW_Vulkan::CreateTextureReplacementStreamBuffer()
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_texture_replacment_stream_buffer.GetBuffer(),
"Texture Replacement Stream Buffer");
- Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_texture_replacment_stream_buffer.GetDeviceMemory(),
+ Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_texture_replacment_stream_buffer.GetAllocation(),
"Texture Replacement Stream Buffer Memory");
return true;
diff --git a/src/core/gpu_hw_vulkan.h b/src/core/gpu_hw_vulkan.h
index ab07a0553..640446533 100644
--- a/src/core/gpu_hw_vulkan.h
+++ b/src/core/gpu_hw_vulkan.h
@@ -1,6 +1,5 @@
#pragma once
#include "common/dimensional_array.h"
-#include "common/vulkan/staging_texture.h"
#include "common/vulkan/stream_buffer.h"
#include "common/vulkan/texture.h"
#include "gpu_hw.h"
@@ -100,7 +99,6 @@ private:
Vulkan::Texture m_vram_depth_texture;
Vulkan::Texture m_vram_read_texture;
Vulkan::Texture m_vram_readback_texture;
- Vulkan::StagingTexture m_vram_readback_staging_texture;
Vulkan::Texture m_display_texture;
bool m_use_ssbos_for_vram_writes = false;
diff --git a/src/duckstation-qt/displaysettingswidget.cpp b/src/duckstation-qt/displaysettingswidget.cpp
index 2636015e1..344113fd7 100644
--- a/src/duckstation-qt/displaysettingswidget.cpp
+++ b/src/duckstation-qt/displaysettingswidget.cpp
@@ -213,7 +213,7 @@ void DisplaySettingsWidget::populateGPUAdaptersAndResolutions()
#endif
#ifdef WITH_VULKAN
case GPURenderer::HardwareVulkan:
- aml = FrontendCommon::VulkanHostDisplay::StaticGetAdapterAndModeList(nullptr);
+ aml = VulkanHostDisplay::StaticGetAdapterAndModeList(nullptr);
threaded_presentation_supported = true;
break;
#endif
diff --git a/src/frontend-common/common_host.cpp b/src/frontend-common/common_host.cpp
index 9299c4bf3..ba2d1c3a6 100644
--- a/src/frontend-common/common_host.cpp
+++ b/src/frontend-common/common_host.cpp
@@ -136,7 +136,7 @@ std::unique_ptr Host::CreateDisplayForAPI(RenderAPI api)
{
#ifdef WITH_VULKAN
case RenderAPI::Vulkan:
- return std::make_unique();
+ return std::make_unique();
#endif
#ifdef WITH_OPENGL
diff --git a/src/frontend-common/imgui_impl_vulkan.cpp b/src/frontend-common/imgui_impl_vulkan.cpp
index 81a46b705..2f89493d6 100644
--- a/src/frontend-common/imgui_impl_vulkan.cpp
+++ b/src/frontend-common/imgui_impl_vulkan.cpp
@@ -67,7 +67,6 @@
#include "common/vulkan/context.h"
#include "common/vulkan/texture.h"
#include "common/vulkan/stream_buffer.h"
-#include "common/vulkan/staging_texture.h"
#include "common/vulkan/util.h"
#include
@@ -412,41 +411,8 @@ bool ImGui_ImplVulkan_CreateFontsTexture()
}
}
-#if 0
- const size_t upload_size = width * height * 4 * sizeof(unsigned char);
- const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0,
- static_cast(upload_size), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, nullptr};
- VmaAllocationCreateInfo aci = {};
- aci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
- aci.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
-
- VmaAllocationInfo ai;
- VkBuffer buffer;
- VmaAllocation allocation;
- VkResult res = vmaCreateBuffer(g_vulkan_context->GetAllocator(), &bci, &aci, &buffer, &allocation, &ai);
- if (res != VK_SUCCESS)
- return false;
-
- std::memcpy(ai.pMappedData, pixels, upload_size);
- vmaFlushAllocation(g_vulkan_context->GetAllocator(), allocation, 0, upload_size);
- bd->FontTexture.TransitionToLayout(g_vulkan_context->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
- bd->FontTexture.UpdateFromBuffer(g_vulkan_context->GetCurrentInitCommandBuffer(), 0, 0, 0, 0, width, height, width, buffer, 0);
- bd->FontTexture.TransitionToLayout(g_vulkan_context->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
-
- // Immediately queue it for freeing after the command buffer finishes, since it's only needed for the copy.
- g_vulkan_context->DeferBufferDestruction(buffer, allocation);
-#else
- Vulkan::StagingTexture stex;
- if (!stex.Create(Vulkan::StagingBuffer::Type::Upload, VK_FORMAT_R8G8B8A8_UNORM, static_cast(width), static_cast(height)))
- return false;
-
- const u32 stride = static_cast(width) * static_cast(sizeof(u32));
- stex.WriteTexels(0, 0, static_cast(width), static_cast(height), pixels, stride);
- stex.CopyToTexture(g_vulkan_context->GetCurrentCommandBuffer(), 0, 0, bd->FontTexture, 0, 0, 0, 0, width, height);
- stex.Destroy(true);
-#endif
-
// Store our identifier
+ bd->FontTexture.Update(0, 0, width, height, pixels, sizeof(u32) * width);
io.Fonts->SetTexID((ImTextureID)&bd->FontTexture);
return true;
}
diff --git a/src/frontend-common/vulkan_host_display.cpp b/src/frontend-common/vulkan_host_display.cpp
index 83e36b478..fa383b4a9 100644
--- a/src/frontend-common/vulkan_host_display.cpp
+++ b/src/frontend-common/vulkan_host_display.cpp
@@ -7,7 +7,6 @@
#include "common/vulkan/builders.h"
#include "common/vulkan/context.h"
#include "common/vulkan/shader_cache.h"
-#include "common/vulkan/staging_texture.h"
#include "common/vulkan/stream_buffer.h"
#include "common/vulkan/swap_chain.h"
#include "common/vulkan/util.h"
@@ -19,8 +18,6 @@
#include
Log_SetChannel(VulkanHostDisplay);
-namespace FrontendCommon {
-
class VulkanHostDisplayTexture : public HostDisplayTexture
{
public:
@@ -38,45 +35,16 @@ public:
u32 GetSamples() const override { return m_texture.GetSamples(); }
HostDisplayPixelFormat GetFormat() const override { return m_format; }
- u32 CalcUpdatePitch(u32 width) const
+ bool BeginUpdate(u32 width, u32 height, void** out_buffer, u32* out_pitch) override
{
- return Common::AlignUp(width * HostDisplay::GetDisplayPixelFormatSize(m_format),
- g_vulkan_context->GetBufferCopyRowPitchAlignment());
+ return m_texture.BeginUpdate(width, height, out_buffer, out_pitch);
}
- bool BeginUpdate(u32 width, u32 height, void** out_buffer, u32* out_pitch)
+ void EndUpdate(u32 x, u32 y, u32 width, u32 height) override { m_texture.EndUpdate(x, y, width, height); }
+
+ bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch) override
{
- const u32 pitch = CalcUpdatePitch(width);
- const u32 required_size = pitch * height;
- Vulkan::StreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
- if (required_size > buffer.GetCurrentSize())
- return false;
-
- // TODO: allocate temporary buffer if this fails...
- if (!buffer.ReserveMemory(required_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
- {
- g_vulkan_context->ExecuteCommandBuffer(false);
- if (!buffer.ReserveMemory(required_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
- return false;
- }
-
- *out_buffer = buffer.GetCurrentHostPointer();
- *out_pitch = pitch;
- return true;
- }
-
- void EndUpdate(u32 x, u32 y, u32 width, u32 height)
- {
- const u32 pitch = CalcUpdatePitch(width);
- const u32 required_size = pitch * height;
-
- Vulkan::StreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
- const u32 buffer_offset = buffer.GetCurrentOffset();
- buffer.CommitMemory(required_size);
-
- m_texture.UpdateFromBuffer(g_vulkan_context->GetCurrentCommandBuffer(), 0, 0, x, y, width, height,
- buffer.GetBuffer(), buffer_offset,
- HostDisplay::GetDisplayPixelFormatSize(m_format) / width);
+ return m_texture.Update(x, y, width, height, data, pitch);
}
const Vulkan::Texture& GetTexture() const { return m_texture; }
@@ -91,6 +59,18 @@ VulkanHostDisplay::VulkanHostDisplay() = default;
VulkanHostDisplay::~VulkanHostDisplay()
{
+ if (!g_vulkan_context)
+ return;
+
+ g_vulkan_context->WaitForGPUIdle();
+
+ DestroyStagingBuffer();
+ DestroyResources();
+
+ Vulkan::ShaderCache::Destroy();
+ m_swap_chain.reset();
+ Vulkan::Context::Destroy();
+
AssertMsg(!g_vulkan_context, "Context should have been destroyed by now");
AssertMsg(!m_swap_chain, "Swap chain should have been destroyed by now");
}
@@ -223,39 +203,7 @@ std::unique_ptr VulkanHostDisplay::CreateTexture(u32 width,
if (data)
{
- const u32 row_size = width * GetDisplayPixelFormatSize(format);
- const u32 data_upload_pitch = Common::AlignUp(row_size, g_vulkan_context->GetBufferCopyRowPitchAlignment());
- const u32 data_size = data_upload_pitch * height;
- Vulkan::StreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
-
- if (data_size < buffer.GetCurrentSize())
- {
- if (!buffer.ReserveMemory(data_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
- {
- g_vulkan_context->ExecuteCommandBuffer(false);
- if (!buffer.ReserveMemory(data_size, g_vulkan_context->GetBufferCopyOffsetAlignment()))
- goto use_staging;
- }
-
- StringUtil::StrideMemCpy(buffer.GetCurrentHostPointer(), data_upload_pitch, data, data_stride, row_size, height);
- const u32 buffer_offset = buffer.GetCurrentOffset();
- buffer.CommitMemory(data_size);
- texture.UpdateFromBuffer(g_vulkan_context->GetCurrentCommandBuffer(), 0, 0, 0, 0, width, height,
- buffer.GetBuffer(), buffer_offset,
- data_upload_pitch / GetDisplayPixelFormatSize(format));
- }
- else
- {
- use_staging:
- // TODO: Drop this thing completely. It's not using the buffer copy row pitch alignment.
- Vulkan::StagingTexture staging_texture;
- if (!staging_texture.Create(Vulkan::StagingBuffer::Type::Upload, vk_format, width, height))
- return {};
-
- staging_texture.WriteTexels(0, 0, width, height, data, data_stride);
- staging_texture.CopyToTexture(g_vulkan_context->GetCurrentCommandBuffer(), 0, 0, texture, 0, 0, 0, 0, width,
- height);
- }
+ texture.Update(0, 0, width, height, data, data_stride);
}
else
{
@@ -271,22 +219,6 @@ std::unique_ptr VulkanHostDisplay::CreateTexture(u32 width,
return std::make_unique(std::move(texture), format);
}
-bool VulkanHostDisplay::DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y,
- u32 width, u32 height, void* out_data, u32 out_data_stride)
-{
- Vulkan::Texture* texture = static_cast(const_cast(texture_handle));
-
- if ((m_readback_staging_texture.GetWidth() < width || m_readback_staging_texture.GetHeight() < height) &&
- !m_readback_staging_texture.Create(Vulkan::StagingBuffer::Type::Readback, texture->GetFormat(), width, height))
- {
- return false;
- }
-
- m_readback_staging_texture.CopyFromTexture(*texture, x, y, 0, 0, 0, 0, width, height);
- m_readback_staging_texture.ReadTexels(0, 0, width, height, out_data, out_data_stride);
- return true;
-}
-
bool VulkanHostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const
{
const VkFormat vk_format = s_display_pixel_format_mapping[static_cast(format)];
@@ -360,6 +292,121 @@ VkRenderPass VulkanHostDisplay::GetRenderPassForDisplay() const
}
}
+bool VulkanHostDisplay::CheckStagingBufferSize(u32 required_size)
+{
+ if (m_readback_staging_buffer_size >= required_size)
+ return true;
+
+ DestroyStagingBuffer();
+
+ const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ nullptr,
+ 0u,
+ required_size,
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+ VK_SHARING_MODE_EXCLUSIVE,
+ 0u,
+ nullptr};
+
+ VmaAllocationCreateInfo aci = {};
+ aci.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
+ aci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
+ aci.preferredFlags = VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
+
+ VmaAllocationInfo ai = {};
+ VkResult res = vmaCreateBuffer(g_vulkan_context->GetAllocator(), &bci, &aci, &m_readback_staging_buffer,
+ &m_readback_staging_allocation, &ai);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vmaCreateBuffer() failed: ");
+ return false;
+ }
+
+ m_readback_staging_buffer_map = static_cast(ai.pMappedData);
+ return true;
+}
+
+void VulkanHostDisplay::DestroyStagingBuffer()
+{
+ // unmapped as part of the buffer destroy
+ m_readback_staging_buffer_map = nullptr;
+ m_readback_staging_buffer_size = 0;
+
+ if (m_readback_staging_buffer != VK_NULL_HANDLE)
+ {
+ vmaDestroyBuffer(g_vulkan_context->GetAllocator(), m_readback_staging_buffer, m_readback_staging_allocation);
+ m_readback_staging_buffer = VK_NULL_HANDLE;
+ m_readback_staging_allocation = VK_NULL_HANDLE;
+ m_readback_staging_buffer_size = 0;
+ }
+}
+
+bool VulkanHostDisplay::DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y,
+ u32 width, u32 height, void* out_data, u32 out_data_stride)
+{
+ Vulkan::Texture* texture = static_cast(const_cast(texture_handle));
+
+ const u32 pitch = texture->CalcUpdatePitch(width);
+ const u32 size = pitch * height;
+ const u32 level = 0;
+ if (!CheckStagingBufferSize(size))
+ {
+ Log_ErrorPrintf("Can't read back %ux%u", width, height);
+ return false;
+ }
+
+ {
+ const VkCommandBuffer cmdbuf = g_vulkan_context->GetCurrentCommandBuffer();
+ const Vulkan::Util::DebugScope debugScope(cmdbuf, "VulkanHostDisplay::DownloadTexture(%u,%u)", width, height);
+
+ VkImageLayout old_layout = texture->GetLayout();
+ if (old_layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
+ texture->TransitionSubresourcesToLayout(cmdbuf, level, 1, 0, 1, old_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+
+ VkBufferImageCopy image_copy = {};
+ const VkImageAspectFlags aspect = Vulkan::Util::IsDepthFormat(static_cast(texture->GetFormat())) ?
+ VK_IMAGE_ASPECT_DEPTH_BIT :
+ VK_IMAGE_ASPECT_COLOR_BIT;
+ image_copy.bufferOffset = 0;
+ image_copy.bufferRowLength = texture->CalcUpdateRowLength(pitch);
+ image_copy.bufferImageHeight = 0;
+ image_copy.imageSubresource = {aspect, level, 0u, 1u};
+ image_copy.imageOffset = {static_cast(x), static_cast(y), 0};
+ image_copy.imageExtent = {width, height, 1u};
+
+ // invalidate gpu cache
+ // TODO: Needed?
+ Vulkan::Util::BufferMemoryBarrier(cmdbuf, m_readback_staging_buffer, 0, VK_ACCESS_TRANSFER_WRITE_BIT, 0, size,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ // do the copy
+ vkCmdCopyImageToBuffer(cmdbuf, texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_readback_staging_buffer,
+ 1, &image_copy);
+
+ // flush gpu cache
+ Vulkan::Util::BufferMemoryBarrier(cmdbuf, m_readback_staging_buffer, VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_ACCESS_HOST_READ_BIT, 0, size, VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_PIPELINE_STAGE_HOST_BIT);
+
+ if (old_layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
+ {
+ texture->TransitionSubresourcesToLayout(cmdbuf, level, 1, 0, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, old_layout);
+ }
+ }
+
+ g_vulkan_context->ExecuteCommandBuffer(true);
+
+ // invalidate cpu cache before reading
+ VkResult res = vmaInvalidateAllocation(g_vulkan_context->GetAllocator(), m_readback_staging_allocation, 0, size);
+ if (res != VK_SUCCESS)
+ LOG_VULKAN_ERROR(res, "vmaInvalidateAllocation() failed, readback may be incorrect: ");
+
+ StringUtil::StrideMemCpy(out_data, out_data_stride, m_readback_staging_buffer_map, pitch,
+ std::min(pitch, out_data_stride), height);
+
+ return true;
+}
+
bool VulkanHostDisplay::CreateResources()
{
static constexpr char fullscreen_quad_vertex_shader[] = R"(
@@ -431,7 +478,7 @@ void main()
plbuilder.AddDescriptorSet(m_post_process_descriptor_set_layout);
plbuilder.AddPushConstants(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0,
- PostProcessingShader::PUSH_CONSTANT_SIZE_THRESHOLD);
+ FrontendCommon::PostProcessingShader::PUSH_CONSTANT_SIZE_THRESHOLD);
m_post_process_pipeline_layout = plbuilder.Create(device);
if (m_post_process_pipeline_layout == VK_NULL_HANDLE)
return false;
@@ -510,8 +557,6 @@ void VulkanHostDisplay::DestroyResources()
m_post_processing_ubo.Destroy(true);
m_post_processing_chain.ClearStages();
- m_readback_staging_texture.Destroy(false);
-
Vulkan::Util::SafeDestroyPipeline(m_display_pipeline);
Vulkan::Util::SafeDestroyPipeline(m_cursor_pipeline);
Vulkan::Util::SafeDestroyPipelineLayout(m_pipeline_layout);
@@ -538,19 +583,7 @@ bool VulkanHostDisplay::UpdateImGuiFontTexture()
return ImGui_ImplVulkan_CreateFontsTexture();
}
-void VulkanHostDisplay::DestroyRenderDevice()
-{
- if (!g_vulkan_context)
- return;
-
- g_vulkan_context->WaitForGPUIdle();
-
- DestroyResources();
-
- Vulkan::ShaderCache::Destroy();
- DestroyRenderSurface();
- Vulkan::Context::Destroy();
-}
+void VulkanHostDisplay::DestroyRenderDevice() {}
bool VulkanHostDisplay::MakeRenderContextCurrent()
{
@@ -687,10 +720,8 @@ bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector
}
Vulkan::Texture tex;
- Vulkan::StagingTexture staging_tex;
if (!tex.Create(width, height, 1, 1, format, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
- VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT) ||
- !staging_tex.Create(Vulkan::StagingBuffer::Type::Readback, format, width, height))
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT))
{
return false;
}
@@ -728,13 +759,11 @@ bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector
vkCmdEndRenderPass(g_vulkan_context->GetCurrentCommandBuffer());
Vulkan::Util::EndDebugScope(g_vulkan_context->GetCurrentCommandBuffer());
tex.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
- staging_tex.CopyFromTexture(tex, 0, 0, 0, 0, 0, 0, width, height);
- staging_tex.ReadTexels(0, 0, width, height, out_pixels->data(), *out_stride);
+ DownloadTexture(&tex, *out_format, 0, 0, width, height, out_pixels->data(), *out_stride);
// destroying these immediately should be safe since nothing's going to access them, and it's not part of the command
// stream
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), fb, nullptr);
- staging_tex.Destroy(false);
tex.Destroy(false);
return true;
}
@@ -960,7 +989,7 @@ bool VulkanHostDisplay::SetPostProcessingChain(const std::string_view& config)
for (u32 i = 0; i < m_post_processing_chain.GetStageCount(); i++)
{
- const PostProcessingShader& shader = m_post_processing_chain.GetShaderStage(i);
+ const FrontendCommon::PostProcessingShader& shader = m_post_processing_chain.GetShaderStage(i);
const std::string vs = shadergen.GeneratePostProcessingVertexShader(shader);
const std::string ps = shadergen.GeneratePostProcessingFragmentShader(shader);
const bool use_push_constants = shader.UsePushConstants();
@@ -1023,8 +1052,6 @@ bool VulkanHostDisplay::SetPostProcessingChain(const std::string_view& config)
}
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_post_processing_ubo.GetBuffer(),
"Post Processing Uniform Buffer");
- Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_post_processing_ubo.GetDeviceMemory(),
- "Post Processing Uniform Buffer Memory");
return true;
}
@@ -1053,7 +1080,7 @@ bool VulkanHostDisplay::CheckPostProcessingRenderTargets(u32 target_width, u32 t
"Post Processing Input Texture");
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_post_processing_input_texture.GetView(),
"Post Processing Input Texture View");
- Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_post_processing_input_texture.GetDeviceMemory(),
+ Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_post_processing_input_texture.GetAllocation(),
"Post Processing Input Texture Memory");
}
@@ -1078,7 +1105,7 @@ bool VulkanHostDisplay::CheckPostProcessingRenderTargets(u32 target_width, u32 t
}
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), pps.output_texture.GetImage(),
"Post Processing Output Texture %u", i);
- Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), pps.output_texture.GetDeviceMemory(),
+ Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), pps.output_texture.GetAllocation(),
"Post Processing Output Texture Memory %u", i);
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), pps.output_texture.GetView(),
"Post Processing Output Texture View %u", i);
@@ -1154,7 +1181,7 @@ void VulkanHostDisplay::ApplyPostProcessingChain(VkFramebuffer target_fb, s32 fi
if (use_push_constants)
{
- u8 buffer[PostProcessingShader::PUSH_CONSTANT_SIZE_THRESHOLD];
+ u8 buffer[FrontendCommon::PostProcessingShader::PUSH_CONSTANT_SIZE_THRESHOLD];
Assert(pps.uniforms_size <= sizeof(buffer));
m_post_processing_chain.GetShaderStage(i).FillUniformBuffer(
buffer, texture_width, texture_height, texture_view_x, texture_view_y, texture_view_width, texture_view_height,
@@ -1201,5 +1228,3 @@ void VulkanHostDisplay::ApplyPostProcessingChain(VkFramebuffer target_fb, s32 fi
}
}
}
-
-} // namespace FrontendCommon
diff --git a/src/frontend-common/vulkan_host_display.h b/src/frontend-common/vulkan_host_display.h
index 531381955..7a6afeb1a 100644
--- a/src/frontend-common/vulkan_host_display.h
+++ b/src/frontend-common/vulkan_host_display.h
@@ -1,6 +1,5 @@
#pragma once
#include "common/vulkan/loader.h"
-#include "common/vulkan/staging_texture.h"
#include "common/vulkan/stream_buffer.h"
#include "common/vulkan/swap_chain.h"
#include "common/window_info.h"
@@ -14,8 +13,6 @@ class StreamBuffer;
class SwapChain;
} // namespace Vulkan
-namespace FrontendCommon {
-
class VulkanHostDisplay final : public HostDisplay
{
public:
@@ -93,9 +90,11 @@ protected:
s32 texture_view_x, s32 texture_view_y, s32 texture_view_width, s32 texture_view_height,
u32 target_width, u32 target_height);
- // Can be overridden by frontends.
VkRenderPass GetRenderPassForDisplay() const;
+ bool CheckStagingBufferSize(u32 required_size);
+ void DestroyStagingBuffer();
+
bool CreateResources() override;
void DestroyResources() override;
@@ -122,18 +121,19 @@ protected:
VkSampler m_point_sampler = VK_NULL_HANDLE;
VkSampler m_linear_sampler = VK_NULL_HANDLE;
- Vulkan::StagingTexture m_readback_staging_texture;
+ VmaAllocation m_readback_staging_allocation = VK_NULL_HANDLE;
+ VkBuffer m_readback_staging_buffer = VK_NULL_HANDLE;
+ u8* m_readback_staging_buffer_map = nullptr;
+ u32 m_readback_staging_buffer_size = 0;
VkDescriptorSetLayout m_post_process_descriptor_set_layout = VK_NULL_HANDLE;
VkDescriptorSetLayout m_post_process_ubo_descriptor_set_layout = VK_NULL_HANDLE;
VkPipelineLayout m_post_process_pipeline_layout = VK_NULL_HANDLE;
VkPipelineLayout m_post_process_ubo_pipeline_layout = VK_NULL_HANDLE;
- PostProcessingChain m_post_processing_chain;
+ FrontendCommon::PostProcessingChain m_post_processing_chain;
Vulkan::Texture m_post_processing_input_texture;
VkFramebuffer m_post_processing_input_framebuffer = VK_NULL_HANDLE;
Vulkan::StreamBuffer m_post_processing_ubo;
std::vector m_post_processing_stages;
};
-
-} // namespace FrontendCommon
\ No newline at end of file