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