From d79635772f025632bf5848987bca813a04d36631 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Wed, 13 Apr 2022 22:06:36 +0100 Subject: [PATCH] Implement support for GPU-side constant buffer updating Previously constant buffer updates would be handled on the CPU and only the end result would be synced to the GPU before execute. This caused issues as if the constant buffer contents was changed between each draw in a renderpass (e.g. text rendering) the draws themselves would only see the final resulting constant buffer. Fix this by updating cbufs on the GPU/CPU seperately, only ever syncing them back at the start or after a guest side CPU write, at the moment only a single word is updated at a time however this can be optimised in the future to batch all consecutive updates into one large one. --- app/src/main/cpp/skyline/gpu/buffer.cpp | 8 ++++---- app/src/main/cpp/skyline/gpu/buffer.h | 6 ++++-- .../cpp/skyline/gpu/interconnect/graphics_context.h | 12 +++++++++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/src/main/cpp/skyline/gpu/buffer.cpp b/app/src/main/cpp/skyline/gpu/buffer.cpp index 84668b0e..d017a886 100644 --- a/app/src/main/cpp/skyline/gpu/buffer.cpp +++ b/app/src/main/cpp/skyline/gpu/buffer.cpp @@ -145,10 +145,10 @@ namespace skyline::gpu { std::memcpy(data.data(), backing.data() + offset, data.size()); } - void Buffer::Write(span data, vk::DeviceSize offset) { + void Buffer::Write(span data, vk::DeviceSize offset, bool skipCleanHostWrite) { if (dirtyState == DirtyState::CpuDirty || dirtyState == DirtyState::Clean) std::memcpy(mirror.data() + offset, data.data(), data.size()); - if (dirtyState == DirtyState::GpuDirty || dirtyState == DirtyState::Clean) + if ((!skipCleanHostWrite && dirtyState == DirtyState::Clean) || dirtyState == DirtyState::GpuDirty) std::memcpy(backing.data() + offset, data.data(), data.size()); } @@ -234,7 +234,7 @@ namespace skyline::gpu { bufferDelegate->buffer->Read(data, offset + bufferDelegate->view->offset); } - void BufferView::Write(span data, vk::DeviceSize offset) const { - bufferDelegate->buffer->Write(data, offset + bufferDelegate->view->offset); + void BufferView::Write(span data, vk::DeviceSize offset, bool skipCleanHostWrite) const { + bufferDelegate->buffer->Write(data, offset + bufferDelegate->view->offset, skipCleanHostWrite); } } diff --git a/app/src/main/cpp/skyline/gpu/buffer.h b/app/src/main/cpp/skyline/gpu/buffer.h index b9a7b9d4..c1ba6632 100644 --- a/app/src/main/cpp/skyline/gpu/buffer.h +++ b/app/src/main/cpp/skyline/gpu/buffer.h @@ -171,8 +171,9 @@ namespace skyline::gpu { /** * @brief Writes data at the specified offset in the buffer + * @param skipCleanHostWrite Skip writing to the host buffer if it's clean, assumes the buffer data will be synchronised externally */ - void Write(span data, vk::DeviceSize offset); + void Write(span data, vk::DeviceSize offset, bool skipCleanHostWrite = false); /** * @return A cached or newly created view into this buffer with the supplied attributes @@ -250,7 +251,8 @@ namespace skyline::gpu { /** * @brief Writes data at the specified offset in the view * @note The view **must** be locked prior to calling this + * @param skipCleanHostWrite Skip writing to the host buffer if it's clean, assumes the buffer data will be synchronised externally */ - void Write(span data, vk::DeviceSize offset) const; + void Write(span data, vk::DeviceSize offset, bool skipCleanHostWrite = false) const; }; } diff --git a/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h b/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h index 7bf62e13..e3043a12 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h @@ -629,7 +629,7 @@ namespace skyline::gpu::interconnect { template void Write(T &object, size_t offset) { std::scoped_lock lock{view}; - view.Write(span(object).template cast(), offset); + view.Write(span(object).template cast(), offset, true); } }; ConstantBuffer constantBufferSelector; //!< The constant buffer selector is used to bind a constant buffer to a stage or update data in it @@ -698,6 +698,11 @@ namespace skyline::gpu::interconnect { if (!view) { auto mappings{channelCtx.asCtx->gmmu.TranslateRange(constantBufferSelector.iova, constantBufferSelector.size)}; view = gpu.buffer.FindOrCreate(mappings.front(), executor.cycle); + { + std::scoped_lock lock{*view}; + view->bufferDelegate->buffer->SynchronizeHost(false); + } + constantBufferCache.Insert(constantBufferSelector.size, constantBufferSelector.iova, *view); } @@ -708,6 +713,11 @@ namespace skyline::gpu::interconnect { void ConstantBufferUpdate(u32 data, u32 offset) { auto constantBuffer{GetConstantBufferSelector().value()}; constantBuffer.Write(data, offset); + + executor.AddNonGraphicsPass([view = constantBuffer.view, data, offset](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr &cycle, GPU &) { + std::scoped_lock lock{view}; + commandBuffer.updateBuffer(view.bufferDelegate->buffer->GetBacking(), offset, vk::ArrayProxy(1, &data)); + }); } /* Shader Program */