diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 0cd1a6ea..a00409b8 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -180,6 +180,11 @@ add_library(skyline SHARED ${source_DIR}/skyline/gpu/cache/renderpass_cache.cpp ${source_DIR}/skyline/gpu/cache/framebuffer_cache.cpp ${source_DIR}/skyline/gpu/interconnect/fermi_2d.cpp + ${source_DIR}/skyline/gpu/interconnect/maxwell_3d/common.cpp + ${source_DIR}/skyline/gpu/interconnect/maxwell_3d/active_state.cpp + ${source_DIR}/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp + ${source_DIR}/skyline/gpu/interconnect/maxwell_3d/constant_buffers.cpp + ${source_DIR}/skyline/gpu/interconnect/maxwell_3d/maxwell_3d.cpp ${source_DIR}/skyline/gpu/interconnect/command_executor.cpp ${source_DIR}/skyline/gpu/interconnect/command_nodes.cpp ${source_DIR}/skyline/gpu/interconnect/conversion/quads.cpp diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/constant_buffers.cpp b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/constant_buffers.cpp new file mode 100644 index 00000000..9f207f82 --- /dev/null +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/constant_buffers.cpp @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include +#include +#include "constant_buffers.h" +#include "gpu/buffer.h" + +namespace skyline::gpu::interconnect::maxwell3d { + void ConstantBufferSelectorState::EngineRegisters::DirtyBind(DirtyManager &manager, dirty::Handle handle) const { + manager.Bind(handle, constantBufferSelector); + } + + ConstantBufferSelectorState::ConstantBufferSelectorState(dirty::Handle dirtyHandle, DirtyManager &manager, const EngineRegisters &engine) : engine{manager, dirtyHandle, engine} {} + + void ConstantBufferSelectorState::Flush(InterconnectContext &ctx) { + const auto &selector{engine->constantBufferSelector}; + // Constant buffer selector size is often left at the default value of 0x10000 which can end up being larger than the underlying mapping so avoid warning for split mappings here + view.Update(ctx, selector.address, selector.size, false); + } + + void ConstantBufferSelectorState::PurgeCaches() { + view.PurgeCaches(); + } + + ConstantBuffers::ConstantBuffers(DirtyManager &manager, const ConstantBufferSelectorState::EngineRegisters &constantBufferSelectorRegisters) : selectorState{manager, constantBufferSelectorRegisters} {} + + void ConstantBuffers::MarkAllDirty() { + selectorState.MarkDirty(true); + } + + static void FlushHostCallback() { + // TODO: here we should trigger `Execute()`, however that doesn't currently work due to Read being called mid-draw and attached objects not handling this case + Logger::Warn("GPU dirty buffer reads for attached buffers are unimplemented"); + } + + void ConstantBuffers::Load(InterconnectContext &ctx, span data, u32 offset) { + auto &view{*selectorState.UpdateGet(ctx).view}; + auto srcCpuBuf{data.cast()}; + + ContextLock lock{ctx.executor.tag, view}; + + // First attempt the write without setting up the gpu copy callback as a fast path + if (view.Write(lock.IsFirstUsage(), ctx.executor.cycle, FlushHostCallback, srcCpuBuf, offset)) [[unlikely]] { + // Store callback data in a stack allocated struct to avoid heap allocation for the gpu copy callback lambda + struct GpuCopyCallbackData { + InterconnectContext &ctx; + span srcCpuBuf; + u32 offset; + ContextLock &lock; + BufferView &view; + } callbackData{ctx, srcCpuBuf, offset, lock, view}; + + view.Write(lock.IsFirstUsage(), ctx.executor.cycle, FlushHostCallback, srcCpuBuf, offset, [&callbackData]() { + callbackData.ctx.executor.AttachLockedBufferView(callbackData.view, std::move(callbackData.lock)); + // This will prevent any CPU accesses to backing for the duration of the usage + callbackData.view.GetBuffer()->BlockAllCpuBackingWrites(); + + auto srcGpuAllocation{callbackData.ctx.executor.AcquireMegaBufferAllocator().Push(callbackData.ctx.executor.cycle, callbackData.srcCpuBuf)}; + callbackData.ctx.executor.AddOutsideRpCommand([=, srcCpuBuf = callbackData.srcCpuBuf, view = callbackData.view, offset = callbackData.offset](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr &, GPU &) { + vk::BufferCopy copyRegion{ + .size = srcCpuBuf.size_bytes(), + .srcOffset = srcGpuAllocation.offset, + .dstOffset = view.GetOffset() + offset + }; + commandBuffer.copyBuffer(srcGpuAllocation.buffer, view.GetBuffer()->GetBacking(), copyRegion); + }); + }); + } + } + + void ConstantBuffers::Bind(InterconnectContext &ctx, engine::PipelineStage stage, size_t index) { + auto &view{*selectorState.UpdateGet(ctx).view}; + boundConstantBuffers[static_cast(stage)][index] = {view}; + } + + void ConstantBuffers::Unbind(engine::PipelineStage stage, size_t index) { + boundConstantBuffers[static_cast(stage)][index] = {}; + } +} \ No newline at end of file diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/constant_buffers.h b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/constant_buffers.h new file mode 100644 index 00000000..bb61bc73 --- /dev/null +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/constant_buffers.h @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include "common.h" + +namespace skyline::gpu::interconnect::maxwell3d { + class ConstantBufferSelectorState : dirty::CachedManualDirty { + public: + struct EngineRegisters { + const engine::ConstantBufferSelector &constantBufferSelector; + + void DirtyBind(DirtyManager &manager, dirty::Handle handle) const; + }; + + private: + dirty::BoundSubresource engine; + + public: + ConstantBufferSelectorState(dirty::Handle dirtyHandle, DirtyManager &manager, const EngineRegisters &engine); + + CachedMappedBufferView view; + + void Flush(InterconnectContext &ctx); + + void PurgeCaches(); + }; + + struct ConstantBuffer { + BufferView view; + + void Read(CommandExecutor &executor, span dstBuffer, size_t srcOffset) const; + + template + T Read(CommandExecutor &executor, size_t srcOffset) const { + T object; + Read(executor, span{object}.template cast(), srcOffset); + return object; + } + }; + + /** + * @brief Holds the state of all bound constant buffers and the selector, abstracting out operations on them + */ + class ConstantBuffers { + private: + dirty::ManualDirtyState selectorState; + + public: + std::array, engine::PipelineStageCount> boundConstantBuffers; + + ConstantBuffers(DirtyManager &manager, const ConstantBufferSelectorState::EngineRegisters &constantBufferSelectorRegisters); + + void MarkAllDirty(); + + void Load(InterconnectContext &ctx, span data, u32 offset); + + void Bind(InterconnectContext &ctx, engine::PipelineStage stage, size_t index); + + void Unbind(engine::PipelineStage stage, size_t index); + }; +}