diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_stream_buffer.cpp | 168 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_stream_buffer.h | 76 |
2 files changed, 0 insertions, 244 deletions
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp deleted file mode 100644 index 7b4875d0e..000000000 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp +++ /dev/null | |||
| @@ -1,168 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <limits> | ||
| 7 | #include <optional> | ||
| 8 | #include <tuple> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/alignment.h" | ||
| 12 | #include "common/assert.h" | ||
| 13 | #include "common/literals.h" | ||
| 14 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 15 | #include "video_core/renderer_vulkan/vk_stream_buffer.h" | ||
| 16 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 17 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 18 | |||
| 19 | namespace Vulkan { | ||
| 20 | |||
| 21 | namespace { | ||
| 22 | |||
| 23 | using namespace Common::Literals; | ||
| 24 | |||
| 25 | constexpr VkBufferUsageFlags BUFFER_USAGE = | ||
| 26 | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | | ||
| 27 | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; | ||
| 28 | |||
| 29 | constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000; | ||
| 30 | constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000; | ||
| 31 | |||
| 32 | constexpr u64 PREFERRED_STREAM_BUFFER_SIZE = 256_MiB; | ||
| 33 | |||
| 34 | /// Find a memory type with the passed requirements | ||
| 35 | std::optional<u32> FindMemoryType(const VkPhysicalDeviceMemoryProperties& properties, | ||
| 36 | VkMemoryPropertyFlags wanted, | ||
| 37 | u32 filter = std::numeric_limits<u32>::max()) { | ||
| 38 | for (u32 i = 0; i < properties.memoryTypeCount; ++i) { | ||
| 39 | const auto flags = properties.memoryTypes[i].propertyFlags; | ||
| 40 | if ((flags & wanted) == wanted && (filter & (1U << i)) != 0) { | ||
| 41 | return i; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | return std::nullopt; | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Get the preferred host visible memory type. | ||
| 48 | u32 GetMemoryType(const VkPhysicalDeviceMemoryProperties& properties, | ||
| 49 | u32 filter = std::numeric_limits<u32>::max()) { | ||
| 50 | // Prefer device local host visible allocations. Both AMD and Nvidia now provide one. | ||
| 51 | // Otherwise search for a host visible allocation. | ||
| 52 | static constexpr auto HOST_MEMORY = | ||
| 53 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; | ||
| 54 | static constexpr auto DYNAMIC_MEMORY = HOST_MEMORY | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; | ||
| 55 | |||
| 56 | std::optional preferred_type = FindMemoryType(properties, DYNAMIC_MEMORY); | ||
| 57 | if (!preferred_type) { | ||
| 58 | preferred_type = FindMemoryType(properties, HOST_MEMORY); | ||
| 59 | ASSERT_MSG(preferred_type, "No host visible and coherent memory type found"); | ||
| 60 | } | ||
| 61 | return preferred_type.value_or(0); | ||
| 62 | } | ||
| 63 | |||
| 64 | } // Anonymous namespace | ||
| 65 | |||
| 66 | VKStreamBuffer::VKStreamBuffer(const Device& device_, VKScheduler& scheduler_) | ||
| 67 | : device{device_}, scheduler{scheduler_} { | ||
| 68 | CreateBuffers(); | ||
| 69 | ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE); | ||
| 70 | ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE); | ||
| 71 | } | ||
| 72 | |||
| 73 | VKStreamBuffer::~VKStreamBuffer() = default; | ||
| 74 | |||
| 75 | std::pair<u8*, u64> VKStreamBuffer::Map(u64 size, u64 alignment) { | ||
| 76 | ASSERT(size <= stream_buffer_size); | ||
| 77 | mapped_size = size; | ||
| 78 | |||
| 79 | if (alignment > 0) { | ||
| 80 | offset = Common::AlignUp(offset, alignment); | ||
| 81 | } | ||
| 82 | |||
| 83 | WaitPendingOperations(offset); | ||
| 84 | |||
| 85 | if (offset + size > stream_buffer_size) { | ||
| 86 | // The buffer would overflow, save the amount of used watches and reset the state. | ||
| 87 | invalidation_mark = current_watch_cursor; | ||
| 88 | current_watch_cursor = 0; | ||
| 89 | offset = 0; | ||
| 90 | |||
| 91 | // Swap watches and reset waiting cursors. | ||
| 92 | std::swap(previous_watches, current_watches); | ||
| 93 | wait_cursor = 0; | ||
| 94 | wait_bound = 0; | ||
| 95 | |||
| 96 | // Ensure that we don't wait for uncommitted fences. | ||
| 97 | scheduler.Flush(); | ||
| 98 | } | ||
| 99 | |||
| 100 | return std::make_pair(memory.Map(offset, size), offset); | ||
| 101 | } | ||
| 102 | |||
| 103 | void VKStreamBuffer::Unmap(u64 size) { | ||
| 104 | ASSERT_MSG(size <= mapped_size, "Reserved size is too small"); | ||
| 105 | |||
| 106 | memory.Unmap(); | ||
| 107 | |||
| 108 | offset += size; | ||
| 109 | |||
| 110 | if (current_watch_cursor + 1 >= current_watches.size()) { | ||
| 111 | // Ensure that there are enough watches. | ||
| 112 | ReserveWatches(current_watches, WATCHES_RESERVE_CHUNK); | ||
| 113 | } | ||
| 114 | auto& watch = current_watches[current_watch_cursor++]; | ||
| 115 | watch.upper_bound = offset; | ||
| 116 | watch.tick = scheduler.CurrentTick(); | ||
| 117 | } | ||
| 118 | |||
| 119 | void VKStreamBuffer::CreateBuffers() { | ||
| 120 | const auto memory_properties = device.GetPhysical().GetMemoryProperties(); | ||
| 121 | const u32 preferred_type = GetMemoryType(memory_properties); | ||
| 122 | const u32 preferred_heap = memory_properties.memoryTypes[preferred_type].heapIndex; | ||
| 123 | |||
| 124 | // Substract from the preferred heap size some bytes to avoid getting out of memory. | ||
| 125 | const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size; | ||
| 126 | // As per DXVK's example, using `heap_size / 2` | ||
| 127 | const VkDeviceSize allocable_size = heap_size / 2; | ||
| 128 | buffer = device.GetLogical().CreateBuffer({ | ||
| 129 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | ||
| 130 | .pNext = nullptr, | ||
| 131 | .flags = 0, | ||
| 132 | .size = std::min(PREFERRED_STREAM_BUFFER_SIZE, allocable_size), | ||
| 133 | .usage = BUFFER_USAGE, | ||
| 134 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||
| 135 | .queueFamilyIndexCount = 0, | ||
| 136 | .pQueueFamilyIndices = nullptr, | ||
| 137 | }); | ||
| 138 | |||
| 139 | const auto requirements = device.GetLogical().GetBufferMemoryRequirements(*buffer); | ||
| 140 | const u32 required_flags = requirements.memoryTypeBits; | ||
| 141 | stream_buffer_size = static_cast<u64>(requirements.size); | ||
| 142 | |||
| 143 | memory = device.GetLogical().AllocateMemory({ | ||
| 144 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | ||
| 145 | .pNext = nullptr, | ||
| 146 | .allocationSize = requirements.size, | ||
| 147 | .memoryTypeIndex = GetMemoryType(memory_properties, required_flags), | ||
| 148 | }); | ||
| 149 | buffer.BindMemory(*memory, 0); | ||
| 150 | } | ||
| 151 | |||
| 152 | void VKStreamBuffer::ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size) { | ||
| 153 | watches.resize(watches.size() + grow_size); | ||
| 154 | } | ||
| 155 | |||
| 156 | void VKStreamBuffer::WaitPendingOperations(u64 requested_upper_bound) { | ||
| 157 | if (!invalidation_mark) { | ||
| 158 | return; | ||
| 159 | } | ||
| 160 | while (requested_upper_bound < wait_bound && wait_cursor < *invalidation_mark) { | ||
| 161 | auto& watch = previous_watches[wait_cursor]; | ||
| 162 | wait_bound = watch.upper_bound; | ||
| 163 | scheduler.Wait(watch.tick); | ||
| 164 | ++wait_cursor; | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | } // namespace Vulkan | ||
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h deleted file mode 100644 index 2e9c8cb46..000000000 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.h +++ /dev/null | |||
| @@ -1,76 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <optional> | ||
| 8 | #include <utility> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 13 | |||
| 14 | namespace Vulkan { | ||
| 15 | |||
| 16 | class Device; | ||
| 17 | class VKFenceWatch; | ||
| 18 | class VKScheduler; | ||
| 19 | |||
| 20 | class VKStreamBuffer final { | ||
| 21 | public: | ||
| 22 | explicit VKStreamBuffer(const Device& device, VKScheduler& scheduler); | ||
| 23 | ~VKStreamBuffer(); | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Reserves a region of memory from the stream buffer. | ||
| 27 | * @param size Size to reserve. | ||
| 28 | * @returns A pair of a raw memory pointer (with offset added), and the buffer offset | ||
| 29 | */ | ||
| 30 | std::pair<u8*, u64> Map(u64 size, u64 alignment); | ||
| 31 | |||
| 32 | /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy. | ||
| 33 | void Unmap(u64 size); | ||
| 34 | |||
| 35 | VkBuffer Handle() const noexcept { | ||
| 36 | return *buffer; | ||
| 37 | } | ||
| 38 | |||
| 39 | u64 Address() const noexcept { | ||
| 40 | return 0; | ||
| 41 | } | ||
| 42 | |||
| 43 | private: | ||
| 44 | struct Watch { | ||
| 45 | u64 tick{}; | ||
| 46 | u64 upper_bound{}; | ||
| 47 | }; | ||
| 48 | |||
| 49 | /// Creates Vulkan buffer handles committing the required the required memory. | ||
| 50 | void CreateBuffers(); | ||
| 51 | |||
| 52 | /// Increases the amount of watches available. | ||
| 53 | void ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size); | ||
| 54 | |||
| 55 | void WaitPendingOperations(u64 requested_upper_bound); | ||
| 56 | |||
| 57 | const Device& device; ///< Vulkan device manager. | ||
| 58 | VKScheduler& scheduler; ///< Command scheduler. | ||
| 59 | |||
| 60 | vk::Buffer buffer; ///< Mapped buffer. | ||
| 61 | vk::DeviceMemory memory; ///< Memory allocation. | ||
| 62 | u64 stream_buffer_size{}; ///< Stream buffer size. | ||
| 63 | |||
| 64 | u64 offset{}; ///< Buffer iterator. | ||
| 65 | u64 mapped_size{}; ///< Size reserved for the current copy. | ||
| 66 | |||
| 67 | std::vector<Watch> current_watches; ///< Watches recorded in the current iteration. | ||
| 68 | std::size_t current_watch_cursor{}; ///< Count of watches, reset on invalidation. | ||
| 69 | std::optional<std::size_t> invalidation_mark; ///< Number of watches used in the previous cycle. | ||
| 70 | |||
| 71 | std::vector<Watch> previous_watches; ///< Watches used in the previous iteration. | ||
| 72 | std::size_t wait_cursor{}; ///< Last watch being waited for completion. | ||
| 73 | u64 wait_bound{}; ///< Highest offset being watched for completion. | ||
| 74 | }; | ||
| 75 | |||
| 76 | } // namespace Vulkan | ||