diff --git a/src/d3d_hooks.cpp b/src/d3d_hooks.cpp index 6d02663e..a0655be8 100644 --- a/src/d3d_hooks.cpp +++ b/src/d3d_hooks.cpp @@ -17,6 +17,14 @@ ac6::d3d::ShadowState g_shadow{}; ac6::d3d::DrawStats g_live_stats{}; ac6::d3d::DrawStatsSnapshot g_snapshot{}; +// Double-buffered draw log: write_log accumulates during the frame, +// read_log holds the previous frame's captures for consumers. +ac6::d3d::FrameDrawLog g_draw_log[2]{}; +uint32_t g_draw_log_write{0}; // Index into g_draw_log for current frame + +ac6::d3d::FrameDrawLog& WriteLog() { return g_draw_log[g_draw_log_write]; } +const ac6::d3d::FrameDrawLog& ReadLog() { return g_draw_log[g_draw_log_write ^ 1]; } + } // namespace PPC_EXTERN_FUNC(__imp__rex_sub_821DEF18); // DrawIndexedVertices @@ -40,38 +48,58 @@ PPC_EXTERN_FUNC(__imp__rex_sub_821E10C8); // SetTextureFetchConstant PPC_EXTERN_FUNC(__imp__rex_sub_821E2BB8); // Resolve // D3DDevice_DrawIndexedVertices (0x821DEF18) +// r3=pDevice, r4=PrimitiveType, r5=StartIndex, r6=IndexCount PPC_FUNC_IMPL(rex_sub_821DEF18) { PPC_FUNC_PROLOGUE(); + uint32_t prim_type = ctx.r4.u32; + uint32_t start_index = ctx.r5.u32; uint32_t index_count = ctx.r6.u32; g_live_stats.draw_calls.fetch_add(1, std::memory_order_relaxed); g_live_stats.draw_calls_indexed.fetch_add(1, std::memory_order_relaxed); g_live_stats.total_indices.fetch_add(index_count, std::memory_order_relaxed); + { + auto& cap = WriteLog().Append(ac6::d3d::DrawType::Indexed, g_shadow); + cap.primitive_type = prim_type; + cap.start_index = start_index; + cap.count = index_count; + } + if (REXCVAR_GET(ac6_d3d_trace)) { REXLOG_CAT_TRACE(kLogGPU, "DrawIndexedVertices: prim={} start={} count={}", - ctx.r4.u32, ctx.r5.u32, index_count); + prim_type, start_index, index_count); } __imp__rex_sub_821DEF18(ctx, base); } // D3DDevice_DrawIndexedVertices_Shared (0x821DF300) +// r3=pDevice, r4=PrimitiveType, r5=Flags, r6=StartIndex, r7=IndexCount PPC_FUNC_IMPL(rex_sub_821DF300) { PPC_FUNC_PROLOGUE(); + uint32_t prim_type = ctx.r4.u32; + uint32_t start_index = ctx.r6.u32; uint32_t index_count = ctx.r7.u32; g_live_stats.draw_calls.fetch_add(1, std::memory_order_relaxed); g_live_stats.draw_calls_indexed_shared.fetch_add(1, std::memory_order_relaxed); g_live_stats.total_indices.fetch_add(index_count, std::memory_order_relaxed); + { + auto& cap = WriteLog().Append(ac6::d3d::DrawType::IndexedShared, g_shadow); + cap.primitive_type = prim_type; + cap.start_index = start_index; + cap.count = index_count; + } + if (REXCVAR_GET(ac6_d3d_trace)) { REXLOG_CAT_TRACE(kLogGPU, "DrawIndexedVertices_Shared: prim={} flags={} start={} count={}", - ctx.r4.u32, ctx.r5.u32, ctx.r6.u32, index_count); + prim_type, ctx.r5.u32, start_index, index_count); } __imp__rex_sub_821DF300(ctx, base); @@ -212,6 +240,12 @@ PPC_FUNC_IMPL(rex_sub_821DEA48) { g_live_stats.draw_calls_primitive.fetch_add(1, std::memory_order_relaxed); g_live_stats.total_vertices.fetch_add(vertex_count, std::memory_order_relaxed); + { + auto& cap = WriteLog().Append(ac6::d3d::DrawType::Primitive, g_shadow); + cap.primitive_type = prim_type; + cap.count = vertex_count; + } + if (REXCVAR_GET(ac6_d3d_trace)) { REXLOG_CAT_TRACE(kLogGPU, "DrawPrimitive: prim={} count={}", @@ -433,6 +467,10 @@ void OnFrameBoundary() { g_snapshot.resolve_calls = g_live_stats.resolve_calls.load(std::memory_order_relaxed); g_live_stats.Reset(); + + // Swap draw log buffers: current write becomes read, old read becomes new write + g_draw_log_write ^= 1; + WriteLog().Reset(); } const DrawStatsSnapshot& GetDrawStats() { @@ -443,4 +481,8 @@ const ShadowState& GetShadowState() { return g_shadow; } +const FrameDrawLog& GetFrameDrawLog() { + return ReadLog(); +} + } // namespace ac6::d3d diff --git a/src/d3d_hooks.h b/src/d3d_hooks.h index ecd352c8..f2f4383b 100644 --- a/src/d3d_hooks.h +++ b/src/d3d_hooks.h @@ -18,4 +18,6 @@ const DrawStatsSnapshot& GetDrawStats(); const ShadowState& GetShadowState(); +const FrameDrawLog& GetFrameDrawLog(); + } // namespace ac6::d3d diff --git a/src/d3d_state.h b/src/d3d_state.h index 6678d852..bf07def0 100644 --- a/src/d3d_state.h +++ b/src/d3d_state.h @@ -3,6 +3,7 @@ #include #include #include +#include namespace ac6::d3d { @@ -107,4 +108,72 @@ struct ShadowState { } viewport; }; +enum class DrawType : uint8_t { + Indexed, + IndexedShared, + Primitive, +}; + +struct DrawCallCapture { + uint32_t draw_id{0}; // Monotonic per-frame draw index + DrawType type{DrawType::Indexed}; + uint32_t primitive_type{0}; + uint32_t start_index{0}; // Start index (indexed) or unused (primitive) + uint32_t count{0}; // Index count or vertex count + + // Full binding snapshot (copied from ShadowState at draw time) + std::array render_targets{}; + uint32_t depth_stencil{0}; + std::array textures{}; + uint32_t vertex_declaration{0}; + uint32_t index_buffer{0}; + std::array streams{}; + std::array samplers{}; + std::array texture_fetch_ptrs{}; + uint32_t shader_gpr_alloc{0}; + + struct { + uint32_t x{0}; + uint32_t y{0}; + uint32_t width{0}; + uint32_t height{0}; + } viewport; +}; + +inline constexpr uint32_t kMaxDrawCapturesPerFrame = 4096; + +struct FrameDrawLog { + std::vector captures; + uint32_t next_draw_id{0}; + + void Reserve() { captures.reserve(kMaxDrawCapturesPerFrame); } + + void Reset() { + captures.clear(); + next_draw_id = 0; + } + + DrawCallCapture& Append(DrawType type, const ShadowState& state) { + auto& c = captures.emplace_back(); + c.draw_id = next_draw_id++; + c.type = type; + + c.render_targets = state.render_targets; + c.depth_stencil = state.depth_stencil; + c.textures = state.textures; + c.vertex_declaration = state.vertex_declaration; + c.index_buffer = state.index_buffer; + c.streams = state.streams; + c.samplers = state.samplers; + c.texture_fetch_ptrs = state.texture_fetch_ptrs; + c.shader_gpr_alloc = state.shader_gpr_alloc; + c.viewport.x = state.viewport.x; + c.viewport.y = state.viewport.y; + c.viewport.width = state.viewport.width; + c.viewport.height = state.viewport.height; + + return c; + } +}; + } // namespace ac6::d3d