diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index b2847f0bd0..3d6293faa4 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -145,6 +145,7 @@ set(agcDriverSources ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Dispatch/IndirectDispatch.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Dispatch/VariantValidation.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Draw/Draw.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Draw/DrawProgramTrace.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Draw/DrawCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Draw/DrawCacheStatistics.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Draw/DrawCapture.cpp @@ -290,6 +291,21 @@ if(APS5_ENABLE_TRACY) target_link_libraries(libSceAgcDriver PUBLIC TracyClient) endif() +if(BUILD_TESTING) + add_test_executable(agc_driver_draw_program_trace_tests + tests/DrawProgramTrace.cpp + Execution/src/Driver/Draw/DrawProgramTrace.cpp + ) + target_include_directories(agc_driver_draw_program_trace_tests PRIVATE + ${LIBS_INCLUDE_DIR} + ${CMAKE_SOURCE_DIR}/core/shader/recompiler + ) + set_target_properties(agc_driver_draw_program_trace_tests PROPERTIES CXX_EXTENSIONS OFF) + configure_windows_unwind(agc_driver_draw_program_trace_tests) + add_test(NAME agc_driver_draw_program_trace COMMAND agc_driver_draw_program_trace_tests) + set_tests_properties(agc_driver_draw_program_trace PROPERTIES TIMEOUT 20) +endif() + set_target_properties(libSceAgcDriver PROPERTIES CXX_EXTENSIONS OFF) configure_windows_unwind(libSceAgcDriver) diff --git a/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawProgramTrace.hpp b/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawProgramTrace.hpp new file mode 100644 index 0000000000..b5f44eb347 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawProgramTrace.hpp @@ -0,0 +1,22 @@ +#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_DRIVER_DRAW_DRAWPROGRAMTRACE_HPP +#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_DRIVER_DRAW_DRAWPROGRAMTRACE_HPP + +#include "Recompiler.hpp" +#include +#include +#include + +namespace AgcDriver::DriverDetail { + +struct DrawProgramTraceEntry { + ShaderRecompiler::ShaderStage stage; + ShaderRecompiler::ProgramRole role; + std::uint64_t codeAddress; + std::uint64_t entryAddress; +}; + +std::string FormatDrawProgramTrace(std::uint64_t submission, std::uint32_t queue, std::span programs); + +} + +#endif diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp index 0bb0c88c09..a6a05f1155 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp @@ -1,11 +1,13 @@ #include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp" #include "prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp" +#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawProgramTrace.hpp" #include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp" #include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp" #include "prx/libSceAgcDriver/Execution/include/Pm4.hpp" #include "Optimization/ResourceProgram.hpp" #include #include +#include #include namespace AgcDriver::DriverDetail { @@ -147,6 +149,16 @@ DrawVerdict Driver::draw(QueueState& queue, std::span packe if (!drawParameters.indirect) setMeshIndexBuffer(drawParameters); else if (graphics.stages.mesh) setMeshIndexBuffer(Pm4::DrawParameters{drawParameters.indexAddress, std::max(drawParameters.indexCount, 1u), drawParameters.indexSize, 1, 0, drawParameters.indexed}); const std::vector& roles = decode->roles; + static const bool tracePrograms = std::getenv("APS5_TRACE_DRAW_PROGRAMS") != nullptr; + if (tracePrograms) { + std::vector identities; + identities.reserve(programs.size()); + for (std::size_t i = 0; i < programs.size(); ++i) { + identities.push_back({programs[i].binary.stage, roles[i], programs[i].snapshot->codeAddress, programs[i].binary.codeAddress}); + } + const auto line = FormatDrawProgramTrace(submission.serial, submission.queue, identities); + std::fprintf(stderr, "%s\n", line.c_str()); + } phaseTiming.Phase(DrawRowDecode); const auto locate = [&](std::uint32_t location) -> std::optional> { diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawProgramTrace.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawProgramTrace.cpp new file mode 100644 index 0000000000..7123e8727d --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawProgramTrace.cpp @@ -0,0 +1,55 @@ +#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawProgramTrace.hpp" +#include +#include + +namespace AgcDriver::DriverDetail { +namespace { + +const char* StageName(ShaderRecompiler::ShaderStage stage) { + using ShaderRecompiler::ShaderStage; + switch (stage) { + case ShaderStage::Compute: return "compute"; + case ShaderStage::Vertex: return "vertex"; + case ShaderStage::TessellationControl: return "tess-control"; + case ShaderStage::TessellationEvaluation: return "tess-evaluation"; + case ShaderStage::Geometry: return "geometry"; + case ShaderStage::Fragment: return "fragment"; + case ShaderStage::Local: return "local"; + case ShaderStage::Mesh: return "mesh"; + } + throw std::invalid_argument("unknown shader stage in draw trace"); +} + +const char* RoleName(ShaderRecompiler::ProgramRole role) { + using ShaderRecompiler::ProgramRole; + switch (role) { + case ProgramRole::Main: return "main"; + case ProgramRole::GeometryBack: return "geometry-back"; + case ProgramRole::Local: return "local"; + case ProgramRole::Hull: return "hull"; + case ProgramRole::Domain: return "domain"; + case ProgramRole::Fragment: return "fragment"; + } + throw std::invalid_argument("unknown program role in draw trace"); +} + +} + +std::string FormatDrawProgramTrace(std::uint64_t submission, std::uint32_t queue, std::span programs) { + char prefix[128]; + const auto size = std::snprintf(prefix, sizeof(prefix), "[draw-programs] submission=%llu queue=0x%x count=%zu", + static_cast(submission), queue, programs.size()); + if (size < 0 || static_cast(size) >= sizeof(prefix)) throw std::runtime_error("draw program trace prefix overflow"); + std::string result(prefix, static_cast(size)); + char item[160]; + for (const auto& program : programs) { + const auto itemSize = std::snprintf(item, sizeof(item), " %s/%s=0x%llx(entry=0x%llx)", + StageName(program.stage), RoleName(program.role), + static_cast(program.codeAddress), static_cast(program.entryAddress)); + if (itemSize < 0 || static_cast(itemSize) >= sizeof(item)) throw std::runtime_error("draw program trace entry overflow"); + result.append(item, static_cast(itemSize)); + } + return result; +} + +} diff --git a/core/libs/prx/libSceAgcDriver/tests/DrawProgramTrace.cpp b/core/libs/prx/libSceAgcDriver/tests/DrawProgramTrace.cpp new file mode 100644 index 0000000000..bedff70e8c --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/DrawProgramTrace.cpp @@ -0,0 +1,41 @@ +#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawProgramTrace.hpp" +#include +#include +#include + +namespace { + +void Check(bool condition, const char* message) { + if (!condition) throw std::runtime_error(message); +} + +void EmptyTrace() { + const auto trace = AgcDriver::DriverDetail::FormatDrawProgramTrace(12, 3, {}); + Check(trace == "[draw-programs] submission=12 queue=0x3 count=0", "empty draw trace changed shape"); +} + +void ProgramAddresses() { + using namespace AgcDriver::DriverDetail; + using ShaderRecompiler::ProgramRole; + using ShaderRecompiler::ShaderStage; + const DrawProgramTraceEntry entries[]{ + {ShaderStage::Vertex, ProgramRole::Main, 0x100000, 0x100040}, + {ShaderStage::Fragment, ProgramRole::Fragment, 0x200000, 0x200020}, + }; + const auto trace = FormatDrawProgramTrace(42, 0x9, entries); + Check(trace == "[draw-programs] submission=42 queue=0x9 count=2 vertex/main=0x100000(entry=0x100040) fragment/fragment=0x200000(entry=0x200020)", + "draw trace omitted the selected shader code or entry address"); +} + +} + +int main() { + try { + EmptyTrace(); + ProgramAddresses(); + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + return 1; + } + return 0; +} diff --git a/core/shader/recompiler/CMakeLists.txt b/core/shader/recompiler/CMakeLists.txt index 810c99d92d..78b512d569 100644 --- a/core/shader/recompiler/CMakeLists.txt +++ b/core/shader/recompiler/CMakeLists.txt @@ -252,11 +252,13 @@ function(add_shader_recompiler target runtimeTarget) SampledTextureNumericClass SetDebugProbeActive ShaderCacheDirectory + ShaderStage SrgbDecodeBit SrgbUnormFormat FindUserDataTail LinkUserDataTail ThreadOwned + ProgramRole UnresolvableSwappcTarget ValidateAndOptimizeSpirv FindUserDataCalls