From 0c6fa22c6bf17626805cb60c5419b0dbfdc595da Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Sat, 10 Sep 2022 20:55:55 +0100 Subject: [PATCH] Introduce pipeline shader stage state Simple state that generates a hash that can be used in the packed state and spans over the binary for pipeline creation. --- .../maxwell_3d/pipeline_state.cpp | 47 ++++++++++++++++++- .../interconnect/maxwell_3d/pipeline_state.h | 33 ++++++++++++- .../skyline/soc/gm20b/engines/maxwell_3d.cpp | 2 +- 3 files changed, 78 insertions(+), 4 deletions(-) diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp index ea33a2b9..bb60525f 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp @@ -211,6 +211,44 @@ namespace skyline::gpu::interconnect::maxwell3d { view = ctx.executor.AcquireTextureManager().FindOrCreate(guest, ctx.executor.tag); } + /* Pipeline Stages */ + void PipelineStageState::EngineRegisters::DirtyBind(DirtyManager &manager, dirty::Handle handle) const { + manager.Bind(handle, pipeline, programRegion); + } + + PipelineStageState::PipelineStageState(dirty::Handle dirtyHandle, DirtyManager &manager, const EngineRegisters &engine, u8 shaderType) + : engine{manager, dirtyHandle, engine}, + shaderType{static_cast(shaderType)} {} + + void PipelineStageState::Flush(InterconnectContext &ctx) { + if (engine->pipeline.shader.type != shaderType) + throw exception("Shader type mismatch: {} != {}!", engine->pipeline.shader.type, static_cast(shaderType)); + + if (!engine->pipeline.shader.enable && shaderType != engine::Pipeline::Shader::Type::Vertex) { + hash = 0; + return; + } + + binary.binary = ctx.channelCtx.asCtx->gmmu.ReadTill(shaderBacking, engine->programRegion + engine->pipeline.programOffset, [](span data) -> std::optional { + // We attempt to find the shader size by looking for "BRA $" (Infinite Loop) which is used as padding at the end of the shader + // UAM Shader Compiler Reference: https://github.com/devkitPro/uam/blob/5a5afc2bae8b55409ab36ba45be63fcb73f68993/source/compiler_iface.cpp#L319-L351 + constexpr u64 BraSelf1{0xE2400FFFFF87000F}, BraSelf2{0xE2400FFFFF07000F}; + + span shaderInstructions{data.cast()}; + for (auto it{shaderInstructions.begin()}; it != shaderInstructions.end(); it++) { + auto instruction{*it}; + if (instruction == BraSelf1 || instruction == BraSelf2) [[unlikely]] + // It is far more likely that the instruction doesn't match so this is an unlikely case + return static_cast(std::distance(shaderInstructions.begin(), it)) * sizeof(u64); + } + return std::nullopt; + }); + + binary.baseOffset = engine->pipeline.programOffset; + + hash = XXH64(binary.binary.data(), binary.binary.size_bytes(), 0); + } + /* Vertex Input State */ // TODO: check if better individually void VertexInputState::EngineRegisters::DirtyBind(DirtyManager &manager, dirty::Handle handle) const { @@ -502,7 +540,7 @@ namespace skyline::gpu::interconnect::maxwell3d { PipelineState::PipelineState(dirty::Handle dirtyHandle, DirtyManager &manager, const EngineRegisters &engine) : engine{manager, dirtyHandle, engine}, - shaders{util::MergeInto, engine::PipelineCount>(manager, engine.shadersRegisters, util::IncrementingT{})}, + pipelineStages{util::MergeInto, engine::PipelineCount>(manager, engine.pipelineStageRegisters, util::IncrementingT{})}, colorRenderTargets{util::MergeInto, engine::ColorTargetCount>(manager, engine.colorRenderTargetsRegisters, util::IncrementingT{})}, depthRenderTarget{manager, engine.depthRenderTargetRegisters}, vertexInput{manager, engine.vertexInputRegisters}, @@ -514,6 +552,13 @@ namespace skyline::gpu::interconnect::maxwell3d { globalShaderConfig{engine.globalShaderConfigRegisters} {} void PipelineState::Flush(InterconnectContext &ctx, StateUpdateBuilder &builder) { + std::array shaderBinaries; + for (size_t i{}; i < engine::PipelineCount; i++) { + const auto &stage{pipelineStages[i].UpdateGet(ctx)}; + packedState.shaderHashes[i] = stage.hash; + shaderBinaries[i] = stage.binary; + } + boost::container::static_vector colorAttachments; for (auto &colorRenderTarget : colorRenderTargets) if (auto view{colorRenderTarget.UpdateGet(ctx, packedState).view}; view) diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.h b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.h index c861f98b..1c9f574d 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.h @@ -56,6 +56,32 @@ namespace skyline::gpu::interconnect::maxwell3d { void Flush(InterconnectContext &ctx, PackedPipelineState &packedState); }; + class PipelineStageState : dirty::ManualDirty { + public: + struct EngineRegisters { + const engine::Pipeline &pipeline; + const soc::gm20b::engine::Address &programRegion; + + void DirtyBind(DirtyManager &manager, dirty::Handle handle) const; + }; + + private: + dirty::BoundSubresource engine; + engine::Pipeline::Shader::Type shaderType; + + constexpr static size_t MaxShaderBytecodeSize{1 * 1024 * 1024}; //!< The largest shader binary that we support (1 MiB) + + std::array shaderBacking; + + public: + ShaderBinary binary; + u64 hash; + + PipelineStageState(dirty::Handle dirtyHandle, DirtyManager &manager, const EngineRegisters &engine, u8 shaderType); + + void Flush(InterconnectContext &ctx); + }; + class VertexInputState : dirty::ManualDirty { public: struct EngineRegisters { @@ -223,7 +249,7 @@ namespace skyline::gpu::interconnect::maxwell3d { class PipelineState : dirty::ManualDirty { public: struct EngineRegisters { - std::array shadersRegisters; + std::array pipelineStageRegisters; std::array colorRenderTargetsRegisters; DepthRenderTargetState::EngineRegisters depthRenderTargetRegisters; VertexInputState::EngineRegisters vertexInputRegisters; @@ -238,11 +264,14 @@ namespace skyline::gpu::interconnect::maxwell3d { }; private: + PipelineManager pipelineManager{}; + Pipeline *pipeline{}; + PackedPipelineState packedState{}; dirty::BoundSubresource engine; - std::array, engine::PipelineCount> shaders; + std::array, engine::PipelineCount> pipelineStages; std::array, engine::ColorTargetCount> colorRenderTargets; dirty::ManualDirtyState depthRenderTarget; dirty::ManualDirtyState vertexInput; diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp index e3c1a2a6..43a56a53 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp +++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp @@ -14,7 +14,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d { static gpu::interconnect::maxwell3d::PipelineState::EngineRegisters MakePipelineStateRegisters(const Maxwell3D::Registers ®isters) { return { - .shadersRegisters = util::MergeInto(*registers.pipelines, *registers.programRegion), + .pipelineStageRegisters = util::MergeInto(*registers.pipelines, *registers.programRegion), .colorRenderTargetsRegisters = util::MergeInto(*registers.colorTargets), .depthRenderTargetRegisters = {*registers.ztSize, *registers.ztOffset, *registers.ztFormat, *registers.ztBlockSize, *registers.ztArrayPitch, *registers.ztSelect, *registers.ztLayer}, .vertexInputRegisters = {*registers.vertexStreams, *registers.vertexStreamInstance, *registers.vertexAttributes},