mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 12:41:50 +01:00
Introduce Buffer
and BufferView
Implements infrastructure for using guest buffers on the host for rendering, a `BufferManager` is still missing which'd handle mapping from guest buffers to host buffers and will be subsequently committed. It should be noted that `BufferView` is also disconnected from `Buffer` and shared for every instance with the same properties like `TextureView` is now.
This commit is contained in:
parent
6eda1777c5
commit
bde61d72cc
@ -159,6 +159,7 @@ add_library(skyline SHARED
|
||||
${source_DIR}/skyline/gpu/texture_manager.cpp
|
||||
${source_DIR}/skyline/gpu/command_scheduler.cpp
|
||||
${source_DIR}/skyline/gpu/texture/texture.cpp
|
||||
${source_DIR}/skyline/gpu/buffer.cpp
|
||||
${source_DIR}/skyline/gpu/presentation_engine.cpp
|
||||
${source_DIR}/skyline/gpu/shader_manager.cpp
|
||||
${source_DIR}/skyline/gpu/interconnect/command_executor.cpp
|
||||
|
138
app/src/main/cpp/skyline/gpu/buffer.cpp
Normal file
138
app/src/main/cpp/skyline/gpu/buffer.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <gpu.h>
|
||||
#include <common/trace.h>
|
||||
#include "buffer.h"
|
||||
|
||||
namespace skyline::gpu {
|
||||
vk::DeviceSize GuestBuffer::BufferSize() const {
|
||||
vk::DeviceSize size{};
|
||||
for (const auto &buffer : mappings)
|
||||
size += buffer.size_bytes();
|
||||
return size;
|
||||
}
|
||||
|
||||
Buffer::Buffer(GPU &gpu, GuestBuffer guest) : size(guest.BufferSize()), backing(gpu.memory.AllocateBuffer(size)), guest(std::move(guest)) {
|
||||
SynchronizeHost();
|
||||
}
|
||||
|
||||
void Buffer::WaitOnFence() {
|
||||
TRACE_EVENT("gpu", "Buffer::WaitOnFence");
|
||||
|
||||
auto lCycle{cycle.lock()};
|
||||
if (lCycle) {
|
||||
lCycle->Wait();
|
||||
cycle.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::SynchronizeHost() {
|
||||
WaitOnFence();
|
||||
|
||||
TRACE_EVENT("gpu", "Buffer::SynchronizeHost");
|
||||
|
||||
auto host{backing.data()};
|
||||
for (auto &mapping : guest.mappings) {
|
||||
auto mappingSize{mapping.size_bytes()};
|
||||
std::memcpy(host, mapping.data(), mappingSize);
|
||||
host += mappingSize;
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::SynchronizeHostWithCycle(const std::shared_ptr<FenceCycle> &pCycle) {
|
||||
if (pCycle != cycle.lock())
|
||||
WaitOnFence();
|
||||
|
||||
TRACE_EVENT("gpu", "Buffer::SynchronizeHostWithCycle");
|
||||
|
||||
auto host{backing.data()};
|
||||
for (auto &mapping : guest.mappings) {
|
||||
auto mappingSize{mapping.size_bytes()};
|
||||
std::memcpy(host, mapping.data(), mappingSize);
|
||||
host += mappingSize;
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::SynchronizeGuest() {
|
||||
WaitOnFence();
|
||||
|
||||
TRACE_EVENT("gpu", "Buffer::SynchronizeGuest");
|
||||
|
||||
auto host{backing.data()};
|
||||
for (auto &mapping : guest.mappings) {
|
||||
auto mappingSize{mapping.size_bytes()};
|
||||
std::memcpy(mapping.data(), host, mappingSize);
|
||||
host += mappingSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A FenceCycleDependency that synchronizes the contents of a host buffer with the guest buffer
|
||||
*/
|
||||
struct BufferGuestSync : public FenceCycleDependency {
|
||||
std::shared_ptr<Buffer> buffer;
|
||||
|
||||
explicit BufferGuestSync(std::shared_ptr<Buffer> buffer) : buffer(std::move(buffer)) {}
|
||||
|
||||
~BufferGuestSync() {
|
||||
TRACE_EVENT("gpu", "Buffer::BufferGuestSync");
|
||||
buffer->SynchronizeGuest();
|
||||
}
|
||||
};
|
||||
|
||||
void Buffer::SynchronizeGuestWithCycle(const std::shared_ptr<FenceCycle> &pCycle) {
|
||||
if (pCycle != cycle.lock())
|
||||
WaitOnFence();
|
||||
|
||||
pCycle->AttachObject(std::make_shared<BufferGuestSync>(shared_from_this()));
|
||||
cycle = pCycle;
|
||||
}
|
||||
|
||||
std::shared_ptr<BufferView> Buffer::GetView(vk::DeviceSize offset, vk::DeviceSize range, vk::Format format) {
|
||||
for (const auto &viewWeak : views) {
|
||||
auto view{viewWeak.lock()};
|
||||
if (view && view->offset == offset && view->range == range && view->format == format)
|
||||
return view;
|
||||
}
|
||||
|
||||
auto view{std::make_shared<BufferView>(shared_from_this(), offset, range, format)};
|
||||
views.push_back(view);
|
||||
return view;
|
||||
}
|
||||
|
||||
BufferView::BufferView(std::shared_ptr<Buffer> backing, vk::DeviceSize offset, vk::DeviceSize range, vk::Format format) : buffer(std::move(backing)), offset(offset), range(range), format(format) {}
|
||||
|
||||
void BufferView::lock() {
|
||||
auto currentBacking{std::atomic_load(&buffer)};
|
||||
while (true) {
|
||||
currentBacking->lock();
|
||||
|
||||
auto newBacking{std::atomic_load(&buffer)};
|
||||
if (currentBacking == newBacking)
|
||||
return;
|
||||
|
||||
currentBacking->unlock();
|
||||
currentBacking = newBacking;
|
||||
}
|
||||
}
|
||||
|
||||
void BufferView::unlock() {
|
||||
buffer->unlock();
|
||||
}
|
||||
|
||||
bool BufferView::try_lock() {
|
||||
auto currentBacking{std::atomic_load(&buffer)};
|
||||
while (true) {
|
||||
bool success{currentBacking->try_lock()};
|
||||
|
||||
auto newBacking{std::atomic_load(&buffer)};
|
||||
if (currentBacking == newBacking)
|
||||
return success;
|
||||
|
||||
if (success)
|
||||
currentBacking->unlock();
|
||||
currentBacking = newBacking;
|
||||
}
|
||||
}
|
||||
}
|
145
app/src/main/cpp/skyline/gpu/buffer.h
Normal file
145
app/src/main/cpp/skyline/gpu/buffer.h
Normal file
@ -0,0 +1,145 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "memory_manager.h"
|
||||
|
||||
namespace skyline::gpu {
|
||||
/**
|
||||
* @brief A descriptor for a GPU buffer on the guest
|
||||
*/
|
||||
struct GuestBuffer {
|
||||
using Mappings = boost::container::small_vector<span<u8>, 3>;
|
||||
Mappings mappings; //!< Spans to CPU memory for the underlying data backing this buffer
|
||||
vk::Format format;
|
||||
|
||||
/**
|
||||
* @return The total size of the buffer by adding up the size of all mappings
|
||||
*/
|
||||
vk::DeviceSize BufferSize() const;
|
||||
};
|
||||
|
||||
struct BufferView;
|
||||
|
||||
/**
|
||||
* @brief A buffer which is backed by host constructs while being synchronized with the underlying guest buffer
|
||||
* @note This class conforms to the Lockable and BasicLockable C++ named requirements
|
||||
*/
|
||||
class Buffer : public std::enable_shared_from_this<Buffer>, public FenceCycleDependency {
|
||||
private:
|
||||
std::mutex mutex; //!< Synchronizes any mutations to the buffer or its backing
|
||||
vk::DeviceSize size;
|
||||
memory::Buffer backing;
|
||||
GuestBuffer guest;
|
||||
|
||||
std::vector<std::weak_ptr<BufferView>> views; //!< BufferView(s) that are backed by this Buffer, used for repointing to a new Buffer on deletion
|
||||
|
||||
friend BufferView;
|
||||
|
||||
public:
|
||||
std::weak_ptr<FenceCycle> cycle; //!< A fence cycle for when any host operation mutating the buffer has completed, it must be waited on prior to any mutations to the backing
|
||||
|
||||
constexpr vk::Buffer GetBacking() {
|
||||
return backing.vkBuffer;
|
||||
}
|
||||
|
||||
Buffer(GPU &gpu, GuestBuffer guest);
|
||||
|
||||
/**
|
||||
* @brief Acquires an exclusive lock on the texture for the calling thread
|
||||
* @note Naming is in accordance to the BasicLockable named requirement
|
||||
*/
|
||||
void lock() {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Relinquishes an existing lock on the texture by the calling thread
|
||||
* @note Naming is in accordance to the BasicLockable named requirement
|
||||
*/
|
||||
void unlock() {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Attempts to acquire an exclusive lock but returns immediately if it's captured by another thread
|
||||
* @note Naming is in accordance to the Lockable named requirement
|
||||
*/
|
||||
bool try_lock() {
|
||||
return mutex.try_lock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Waits on a fence cycle if it exists till it's signalled and resets it after
|
||||
* @note The buffer **must** be locked prior to calling this
|
||||
*/
|
||||
void WaitOnFence();
|
||||
|
||||
/**
|
||||
* @brief Synchronizes the host buffer with the guest
|
||||
* @note The buffer **must** be locked prior to calling this
|
||||
*/
|
||||
void SynchronizeHost();
|
||||
|
||||
/**
|
||||
* @brief Synchronizes the host buffer with the guest
|
||||
* @param cycle A FenceCycle that is checked against the held one to skip waiting on it when equal
|
||||
* @note The buffer **must** be locked prior to calling this
|
||||
*/
|
||||
void SynchronizeHostWithCycle(const std::shared_ptr<FenceCycle> &cycle);
|
||||
|
||||
/**
|
||||
* @brief Synchronizes the guest buffer with the host buffer
|
||||
* @note The buffer **must** be locked prior to calling this
|
||||
*/
|
||||
void SynchronizeGuest();
|
||||
|
||||
/**
|
||||
* @brief Synchronizes the guest buffer with the host buffer when the FenceCycle is signalled
|
||||
* @note The buffer **must** be locked prior to calling this
|
||||
* @note The guest buffer should not be null prior to calling this
|
||||
*/
|
||||
void SynchronizeGuestWithCycle(const std::shared_ptr<FenceCycle> &cycle);
|
||||
|
||||
/**
|
||||
* @return A cached or newly created view into this buffer with the supplied attributes
|
||||
*/
|
||||
std::shared_ptr<BufferView> GetView(vk::DeviceSize offset, vk::DeviceSize range, vk::Format format);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A contiguous view into a Vulkan Buffer that represents a single guest buffer (as opposed to Buffer objects which contain multiple)
|
||||
* @note The object **must** be locked prior to accessing any members as values will be mutated
|
||||
* @note This class conforms to the Lockable and BasicLockable C++ named requirements
|
||||
*/
|
||||
struct BufferView {
|
||||
std::shared_ptr<Buffer> buffer;
|
||||
vk::DeviceSize offset;
|
||||
vk::DeviceSize range;
|
||||
vk::Format format;
|
||||
|
||||
/**
|
||||
* @note A view must **NOT** be constructed directly, it should always be retrieved using Texture::GetView
|
||||
*/
|
||||
BufferView(std::shared_ptr<Buffer> backing, vk::DeviceSize offset, vk::DeviceSize range, vk::Format format);
|
||||
|
||||
/**
|
||||
* @brief Acquires an exclusive lock on the buffer for the calling thread
|
||||
* @note Naming is in accordance to the BasicLockable named requirement
|
||||
*/
|
||||
void lock();
|
||||
|
||||
/**
|
||||
* @brief Relinquishes an existing lock on the buffer by the calling thread
|
||||
* @note Naming is in accordance to the BasicLockable named requirement
|
||||
*/
|
||||
void unlock();
|
||||
|
||||
/**
|
||||
* @brief Attempts to acquire an exclusive lock but returns immediately if it's captured by another thread
|
||||
* @note Naming is in accordance to the Lockable named requirement
|
||||
*/
|
||||
bool try_lock();
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user