mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 00:49:02 -06:00 
			
		
		
		
	Merge pull request #6741 from ReinUsesLisp/stream-remove
vk_stream_buffer: Remove unused stream buffer
This commit is contained in:
		@@ -1,168 +0,0 @@
 | 
				
			|||||||
// Copyright 2019 yuzu Emulator Project
 | 
					 | 
				
			||||||
// Licensed under GPLv2 or any later version
 | 
					 | 
				
			||||||
// Refer to the license.txt file included.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <algorithm>
 | 
					 | 
				
			||||||
#include <limits>
 | 
					 | 
				
			||||||
#include <optional>
 | 
					 | 
				
			||||||
#include <tuple>
 | 
					 | 
				
			||||||
#include <vector>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "common/alignment.h"
 | 
					 | 
				
			||||||
#include "common/assert.h"
 | 
					 | 
				
			||||||
#include "common/literals.h"
 | 
					 | 
				
			||||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
 | 
					 | 
				
			||||||
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
 | 
					 | 
				
			||||||
#include "video_core/vulkan_common/vulkan_device.h"
 | 
					 | 
				
			||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Vulkan {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using namespace Common::Literals;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
constexpr VkBufferUsageFlags BUFFER_USAGE =
 | 
					 | 
				
			||||||
    VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
 | 
					 | 
				
			||||||
    VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000;
 | 
					 | 
				
			||||||
constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
constexpr u64 PREFERRED_STREAM_BUFFER_SIZE = 256_MiB;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Find a memory type with the passed requirements
 | 
					 | 
				
			||||||
std::optional<u32> FindMemoryType(const VkPhysicalDeviceMemoryProperties& properties,
 | 
					 | 
				
			||||||
                                  VkMemoryPropertyFlags wanted,
 | 
					 | 
				
			||||||
                                  u32 filter = std::numeric_limits<u32>::max()) {
 | 
					 | 
				
			||||||
    for (u32 i = 0; i < properties.memoryTypeCount; ++i) {
 | 
					 | 
				
			||||||
        const auto flags = properties.memoryTypes[i].propertyFlags;
 | 
					 | 
				
			||||||
        if ((flags & wanted) == wanted && (filter & (1U << i)) != 0) {
 | 
					 | 
				
			||||||
            return i;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return std::nullopt;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Get the preferred host visible memory type.
 | 
					 | 
				
			||||||
u32 GetMemoryType(const VkPhysicalDeviceMemoryProperties& properties,
 | 
					 | 
				
			||||||
                  u32 filter = std::numeric_limits<u32>::max()) {
 | 
					 | 
				
			||||||
    // Prefer device local host visible allocations. Both AMD and Nvidia now provide one.
 | 
					 | 
				
			||||||
    // Otherwise search for a host visible allocation.
 | 
					 | 
				
			||||||
    static constexpr auto HOST_MEMORY =
 | 
					 | 
				
			||||||
        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
 | 
					 | 
				
			||||||
    static constexpr auto DYNAMIC_MEMORY = HOST_MEMORY | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::optional preferred_type = FindMemoryType(properties, DYNAMIC_MEMORY);
 | 
					 | 
				
			||||||
    if (!preferred_type) {
 | 
					 | 
				
			||||||
        preferred_type = FindMemoryType(properties, HOST_MEMORY);
 | 
					 | 
				
			||||||
        ASSERT_MSG(preferred_type, "No host visible and coherent memory type found");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return preferred_type.value_or(0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // Anonymous namespace
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VKStreamBuffer::VKStreamBuffer(const Device& device_, VKScheduler& scheduler_)
 | 
					 | 
				
			||||||
    : device{device_}, scheduler{scheduler_} {
 | 
					 | 
				
			||||||
    CreateBuffers();
 | 
					 | 
				
			||||||
    ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE);
 | 
					 | 
				
			||||||
    ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VKStreamBuffer::~VKStreamBuffer() = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::pair<u8*, u64> VKStreamBuffer::Map(u64 size, u64 alignment) {
 | 
					 | 
				
			||||||
    ASSERT(size <= stream_buffer_size);
 | 
					 | 
				
			||||||
    mapped_size = size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (alignment > 0) {
 | 
					 | 
				
			||||||
        offset = Common::AlignUp(offset, alignment);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    WaitPendingOperations(offset);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (offset + size > stream_buffer_size) {
 | 
					 | 
				
			||||||
        // The buffer would overflow, save the amount of used watches and reset the state.
 | 
					 | 
				
			||||||
        invalidation_mark = current_watch_cursor;
 | 
					 | 
				
			||||||
        current_watch_cursor = 0;
 | 
					 | 
				
			||||||
        offset = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Swap watches and reset waiting cursors.
 | 
					 | 
				
			||||||
        std::swap(previous_watches, current_watches);
 | 
					 | 
				
			||||||
        wait_cursor = 0;
 | 
					 | 
				
			||||||
        wait_bound = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Ensure that we don't wait for uncommitted fences.
 | 
					 | 
				
			||||||
        scheduler.Flush();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return std::make_pair(memory.Map(offset, size), offset);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void VKStreamBuffer::Unmap(u64 size) {
 | 
					 | 
				
			||||||
    ASSERT_MSG(size <= mapped_size, "Reserved size is too small");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    memory.Unmap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    offset += size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (current_watch_cursor + 1 >= current_watches.size()) {
 | 
					 | 
				
			||||||
        // Ensure that there are enough watches.
 | 
					 | 
				
			||||||
        ReserveWatches(current_watches, WATCHES_RESERVE_CHUNK);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    auto& watch = current_watches[current_watch_cursor++];
 | 
					 | 
				
			||||||
    watch.upper_bound = offset;
 | 
					 | 
				
			||||||
    watch.tick = scheduler.CurrentTick();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void VKStreamBuffer::CreateBuffers() {
 | 
					 | 
				
			||||||
    const auto memory_properties = device.GetPhysical().GetMemoryProperties();
 | 
					 | 
				
			||||||
    const u32 preferred_type = GetMemoryType(memory_properties);
 | 
					 | 
				
			||||||
    const u32 preferred_heap = memory_properties.memoryTypes[preferred_type].heapIndex;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Substract from the preferred heap size some bytes to avoid getting out of memory.
 | 
					 | 
				
			||||||
    const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size;
 | 
					 | 
				
			||||||
    // As per DXVK's example, using `heap_size / 2`
 | 
					 | 
				
			||||||
    const VkDeviceSize allocable_size = heap_size / 2;
 | 
					 | 
				
			||||||
    buffer = device.GetLogical().CreateBuffer({
 | 
					 | 
				
			||||||
        .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
 | 
					 | 
				
			||||||
        .pNext = nullptr,
 | 
					 | 
				
			||||||
        .flags = 0,
 | 
					 | 
				
			||||||
        .size = std::min(PREFERRED_STREAM_BUFFER_SIZE, allocable_size),
 | 
					 | 
				
			||||||
        .usage = BUFFER_USAGE,
 | 
					 | 
				
			||||||
        .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
 | 
					 | 
				
			||||||
        .queueFamilyIndexCount = 0,
 | 
					 | 
				
			||||||
        .pQueueFamilyIndices = nullptr,
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const auto requirements = device.GetLogical().GetBufferMemoryRequirements(*buffer);
 | 
					 | 
				
			||||||
    const u32 required_flags = requirements.memoryTypeBits;
 | 
					 | 
				
			||||||
    stream_buffer_size = static_cast<u64>(requirements.size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    memory = device.GetLogical().AllocateMemory({
 | 
					 | 
				
			||||||
        .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
 | 
					 | 
				
			||||||
        .pNext = nullptr,
 | 
					 | 
				
			||||||
        .allocationSize = requirements.size,
 | 
					 | 
				
			||||||
        .memoryTypeIndex = GetMemoryType(memory_properties, required_flags),
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    buffer.BindMemory(*memory, 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void VKStreamBuffer::ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size) {
 | 
					 | 
				
			||||||
    watches.resize(watches.size() + grow_size);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void VKStreamBuffer::WaitPendingOperations(u64 requested_upper_bound) {
 | 
					 | 
				
			||||||
    if (!invalidation_mark) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    while (requested_upper_bound < wait_bound && wait_cursor < *invalidation_mark) {
 | 
					 | 
				
			||||||
        auto& watch = previous_watches[wait_cursor];
 | 
					 | 
				
			||||||
        wait_bound = watch.upper_bound;
 | 
					 | 
				
			||||||
        scheduler.Wait(watch.tick);
 | 
					 | 
				
			||||||
        ++wait_cursor;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace Vulkan
 | 
					 | 
				
			||||||
@@ -1,76 +0,0 @@
 | 
				
			|||||||
// Copyright 2019 yuzu Emulator Project
 | 
					 | 
				
			||||||
// Licensed under GPLv2 or any later version
 | 
					 | 
				
			||||||
// Refer to the license.txt file included.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <optional>
 | 
					 | 
				
			||||||
#include <utility>
 | 
					 | 
				
			||||||
#include <vector>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "common/common_types.h"
 | 
					 | 
				
			||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Vulkan {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Device;
 | 
					 | 
				
			||||||
class VKFenceWatch;
 | 
					 | 
				
			||||||
class VKScheduler;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class VKStreamBuffer final {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
    explicit VKStreamBuffer(const Device& device, VKScheduler& scheduler);
 | 
					 | 
				
			||||||
    ~VKStreamBuffer();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Reserves a region of memory from the stream buffer.
 | 
					 | 
				
			||||||
     * @param size Size to reserve.
 | 
					 | 
				
			||||||
     * @returns A pair of a raw memory pointer (with offset added), and the buffer offset
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    std::pair<u8*, u64> Map(u64 size, u64 alignment);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy.
 | 
					 | 
				
			||||||
    void Unmap(u64 size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    VkBuffer Handle() const noexcept {
 | 
					 | 
				
			||||||
        return *buffer;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    u64 Address() const noexcept {
 | 
					 | 
				
			||||||
        return 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
    struct Watch {
 | 
					 | 
				
			||||||
        u64 tick{};
 | 
					 | 
				
			||||||
        u64 upper_bound{};
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Creates Vulkan buffer handles committing the required the required memory.
 | 
					 | 
				
			||||||
    void CreateBuffers();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Increases the amount of watches available.
 | 
					 | 
				
			||||||
    void ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void WaitPendingOperations(u64 requested_upper_bound);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const Device& device;   ///< Vulkan device manager.
 | 
					 | 
				
			||||||
    VKScheduler& scheduler; ///< Command scheduler.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    vk::Buffer buffer;        ///< Mapped buffer.
 | 
					 | 
				
			||||||
    vk::DeviceMemory memory;  ///< Memory allocation.
 | 
					 | 
				
			||||||
    u64 stream_buffer_size{}; ///< Stream buffer size.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    u64 offset{};      ///< Buffer iterator.
 | 
					 | 
				
			||||||
    u64 mapped_size{}; ///< Size reserved for the current copy.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::vector<Watch> current_watches;           ///< Watches recorded in the current iteration.
 | 
					 | 
				
			||||||
    std::size_t current_watch_cursor{};           ///< Count of watches, reset on invalidation.
 | 
					 | 
				
			||||||
    std::optional<std::size_t> invalidation_mark; ///< Number of watches used in the previous cycle.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::vector<Watch> previous_watches; ///< Watches used in the previous iteration.
 | 
					 | 
				
			||||||
    std::size_t wait_cursor{};           ///< Last watch being waited for completion.
 | 
					 | 
				
			||||||
    u64 wait_bound{};                    ///< Highest offset being watched for completion.
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace Vulkan
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user