Vulkan: Use Vulkan Memory Allocator
This commit is contained in:
parent
56293e4d8f
commit
9d27f7095f
|
@ -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
|
||||
|
|
|
@ -92,12 +92,6 @@
|
|||
<ClInclude Include="vulkan\shader_compiler.h">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vulkan\staging_buffer.h">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vulkan\staging_texture.h">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vulkan\stream_buffer.h">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
|
@ -180,12 +174,6 @@
|
|||
<ClCompile Include="vulkan\shader_compiler.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vulkan\staging_buffer.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vulkan\staging_texture.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vulkan\stream_buffer.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'=='ARM64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
@ -221,4 +209,4 @@
|
|||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="..\..\dep\msvc\vsprops\Targets.props" />
|
||||
</Project>
|
||||
</Project>
|
|
@ -55,9 +55,6 @@
|
|||
<ClInclude Include="vulkan\texture.h">
|
||||
<Filter>vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vulkan\staging_buffer.h">
|
||||
<Filter>vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vulkan\stream_buffer.h">
|
||||
<Filter>vulkan</Filter>
|
||||
</ClInclude>
|
||||
|
@ -70,9 +67,6 @@
|
|||
<ClInclude Include="vulkan\swap_chain.h">
|
||||
<Filter>vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vulkan\staging_texture.h">
|
||||
<Filter>vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dimensional_array.h" />
|
||||
<ClInclude Include="vulkan\context.h">
|
||||
<Filter>vulkan</Filter>
|
||||
|
@ -188,9 +182,6 @@
|
|||
<ClCompile Include="vulkan\context.cpp">
|
||||
<Filter>vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vulkan\staging_buffer.cpp">
|
||||
<Filter>vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vulkan\stream_buffer.cpp">
|
||||
<Filter>vulkan</Filter>
|
||||
</ClCompile>
|
||||
|
@ -203,9 +194,6 @@
|
|||
<ClCompile Include="vulkan\swap_chain.cpp">
|
||||
<Filter>vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vulkan\staging_texture.cpp">
|
||||
<Filter>vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vulkan\builders.cpp">
|
||||
<Filter>vulkan</Filter>
|
||||
</ClCompile>
|
||||
|
@ -277,4 +265,4 @@
|
|||
<Filter>vulkan</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -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<u32>(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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Copyright 2020 DuckStation Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the LICENSE file included.
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
|
@ -10,6 +5,8 @@
|
|||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#define VMA_IMPLEMENTATION 1
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
|
@ -20,6 +17,8 @@
|
|||
#include <mach-o/dyld.h>
|
||||
#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
|
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<char*>(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
|
|
@ -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 <memory>
|
||||
|
||||
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
|
|
@ -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<VkDeviceSize>(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<int32_t>(src_x), static_cast<int32_t>(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<VkDeviceSize>(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<int32_t>(dst_x), static_cast<int32_t>(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<u32>(width * m_texel_size, m_map_stride);
|
||||
char* dst_ptr = reinterpret_cast<char*>(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<u32>(width * m_texel_size, m_map_stride);
|
||||
const char* src_ptr = reinterpret_cast<const char*>(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
|
|
@ -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
|
|
@ -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<VkDeviceSize>(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<VkDeviceSize>(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<u8*>(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<u8*>(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
|
|
@ -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<std::pair<u64, u32>> m_tracked_fences;
|
||||
|
||||
bool m_coherent_mapping = false;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 <algorithm>
|
||||
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<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) :
|
||||
static_cast<VkImageAspectFlags>(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<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) :
|
||||
static_cast<VkImageAspectFlags>(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<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) :
|
||||
static_cast<VkImageAspectFlags>(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<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) :
|
||||
static_cast<VkImageAspectFlags>(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<VkDeviceSize>(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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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<VkQueue>::DebugScope(VkQueue context, const char* format, ...) : queu
|
|||
va_end(ap);
|
||||
|
||||
const float depth_phase = depth / static_cast<float>(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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <algorithm>
|
||||
#include <array>
|
||||
|
@ -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<float, 4>& scope_color = {0.5, 0.5, 0.5, 1.0})
|
||||
|
|
|
@ -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<BatchVertex*>(m_vertex_stream_buffer.GetCurrentHostPointer());
|
||||
m_batch_start_vertex_ptr = reinterpret_cast<BatchVertex*>(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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -136,7 +136,7 @@ std::unique_ptr<HostDisplay> Host::CreateDisplayForAPI(RenderAPI api)
|
|||
{
|
||||
#ifdef WITH_VULKAN
|
||||
case RenderAPI::Vulkan:
|
||||
return std::make_unique<FrontendCommon::VulkanHostDisplay>();
|
||||
return std::make_unique<VulkanHostDisplay>();
|
||||
#endif
|
||||
|
||||
#ifdef WITH_OPENGL
|
||||
|
|
|
@ -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 <cstdio>
|
||||
|
@ -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<VkDeviceSize>(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<u32>(width), static_cast<u32>(height)))
|
||||
return false;
|
||||
|
||||
const u32 stride = static_cast<u32>(width) * static_cast<u32>(sizeof(u32));
|
||||
stex.WriteTexels(0, 0, static_cast<u32>(width), static_cast<u32>(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;
|
||||
}
|
||||
|
|
|
@ -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 <array>
|
||||
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<HostDisplayTexture> 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<HostDisplayTexture> VulkanHostDisplay::CreateTexture(u32 width,
|
|||
return std::make_unique<VulkanHostDisplayTexture>(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<Vulkan::Texture*>(const_cast<void*>(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<u32>(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<u8*>(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<Vulkan::Texture*>(const_cast<void*>(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<VkFormat>(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<s32>(x), static_cast<s32>(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<u32>
|
|||
}
|
||||
|
||||
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<u32>
|
|||
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
|
||||
|
|
|
@ -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<PostProcessingStage> m_post_processing_stages;
|
||||
};
|
||||
|
||||
} // namespace FrontendCommon
|
Loading…
Reference in New Issue