diff --git a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h index d300a0d931..d8179d5941 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h +++ b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h @@ -214,4 +214,8 @@ protected: virtual void load_vertex_data(u32 first, u32 count) override; virtual void load_vertex_index_data(u32 first, u32 count) override; + + virtual void copy_render_targets_to_memory(void *buffer, u8 rtt) override; + virtual void copy_depth_buffer_to_memory(void *buffer) override; + virtual void copy_stencil_buffer_to_memory(void *buffer) override; }; diff --git a/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.cpp b/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.cpp index 7088951360..83b5e5c84b 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.cpp +++ b/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.cpp @@ -587,4 +587,128 @@ void D3D12GSRender::copy_render_target_to_dma_location() } } + +void D3D12GSRender::copy_render_targets_to_memory(void *buffer, u8 rtt) +{ + ComPtr readback_buffer = create_readback_buffer_and_download(m_device.Get(), getCurrentResourceStorage().command_list.Get(), m_readbackResources, m_rtts.bound_render_targets[rtt], m_surface.color_format); + + ThrowIfFailed(getCurrentResourceStorage().command_list->Close()); + m_commandQueueGraphic->ExecuteCommandLists(1, (ID3D12CommandList**)getCurrentResourceStorage().command_list.GetAddressOf()); + getCurrentResourceStorage().set_new_command_list(); + + wait_for_command_queue(m_device.Get(), m_commandQueueGraphic.Get()); + m_readbackResources.m_get_pos = m_readbackResources.get_current_put_pos_minus_one(); + + int clip_w = rsx::method_registers[NV4097_SET_SURFACE_CLIP_HORIZONTAL] >> 16; + int clip_h = rsx::method_registers[NV4097_SET_SURFACE_CLIP_VERTICAL] >> 16; + size_t srcPitch, dstPitch; + switch (m_surface.color_format) + { + case CELL_GCM_SURFACE_A8R8G8B8: + srcPitch = align(clip_w * 4, 256); + dstPitch = clip_w * 4; + break; + case CELL_GCM_SURFACE_F_W16Z16Y16X16: + srcPitch = align(clip_w * 8, 256); + dstPitch = clip_w * 8; + break; + } + copy_readback_buffer_to_dest(buffer, readback_buffer.Get(), srcPitch, dstPitch, clip_h); +} + +void D3D12GSRender::copy_depth_buffer_to_memory(void *buffer) +{ + int clip_w = rsx::method_registers[NV4097_SET_SURFACE_CLIP_HORIZONTAL] >> 16; + int clip_h = rsx::method_registers[NV4097_SET_SURFACE_CLIP_VERTICAL] >> 16; + + size_t row_pitch = align(clip_w * 4, 256); + + ComPtr readback_buffer; + size_t buffer_size = row_pitch * clip_h; + assert(m_readbackResources.can_alloc(buffer_size)); + size_t heapOffset = m_readbackResources.alloc(buffer_size); + ThrowIfFailed( + m_device->CreatePlacedResource( + m_readbackResources.m_heap, + heapOffset, + &CD3DX12_RESOURCE_DESC::Buffer(buffer_size), + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_PPV_ARGS(readback_buffer.GetAddressOf()) + ) + ); + + getCurrentResourceStorage().command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_rtts.bound_depth_stencil, D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE)); + + getCurrentResourceStorage().command_list->CopyTextureRegion(&CD3DX12_TEXTURE_COPY_LOCATION(readback_buffer.Get(), { 0,{ DXGI_FORMAT_R32_TYPELESS, (UINT)clip_w, (UINT)clip_h, 1, (UINT)row_pitch } }), 0, 0, 0, + &CD3DX12_TEXTURE_COPY_LOCATION(m_rtts.bound_depth_stencil, 0), nullptr); + getCurrentResourceStorage().command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_rtts.bound_depth_stencil, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE)); + + ThrowIfFailed(getCurrentResourceStorage().command_list->Close()); + m_commandQueueGraphic->ExecuteCommandLists(1, (ID3D12CommandList**)getCurrentResourceStorage().command_list.GetAddressOf()); + getCurrentResourceStorage().set_new_command_list(); + + wait_for_command_queue(m_device.Get(), m_commandQueueGraphic.Get()); + m_readbackResources.m_get_pos = m_readbackResources.get_current_put_pos_minus_one(); + + void *mapped_buffer; + ThrowIfFailed(readback_buffer->Map(0, nullptr, &mapped_buffer)); + for (unsigned row = 0; row < clip_h; row++) + { + u32 *casted_dest = (u32*)((char*)buffer + row * clip_w * 4); + u32 *casted_src = (u32*)((char*)mapped_buffer + row * row_pitch); + for (unsigned col = 0; col < row_pitch / 4; col++) + *casted_dest++ = *casted_src++; + } + readback_buffer->Unmap(0, nullptr); +} + + +void D3D12GSRender::copy_stencil_buffer_to_memory(void *buffer) +{ + int clip_w = rsx::method_registers[NV4097_SET_SURFACE_CLIP_HORIZONTAL] >> 16; + int clip_h = rsx::method_registers[NV4097_SET_SURFACE_CLIP_VERTICAL] >> 16; + + size_t row_pitch = align(clip_w * 4, 256); + + ComPtr readback_buffer; + size_t buffer_size = row_pitch * clip_h; + assert(m_readbackResources.can_alloc(buffer_size)); + size_t heapOffset = m_readbackResources.alloc(buffer_size); + ThrowIfFailed( + m_device->CreatePlacedResource( + m_readbackResources.m_heap, + heapOffset, + &CD3DX12_RESOURCE_DESC::Buffer(buffer_size), + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_PPV_ARGS(readback_buffer.GetAddressOf()) + ) + ); + + getCurrentResourceStorage().command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_rtts.bound_depth_stencil, D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE)); + + getCurrentResourceStorage().command_list->CopyTextureRegion(&CD3DX12_TEXTURE_COPY_LOCATION(readback_buffer.Get(), { 0,{ DXGI_FORMAT_R8_TYPELESS, (UINT)clip_w, (UINT)clip_h, 1, (UINT)row_pitch } }), 0, 0, 0, + &CD3DX12_TEXTURE_COPY_LOCATION(m_rtts.bound_depth_stencil, 1), nullptr); + getCurrentResourceStorage().command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_rtts.bound_depth_stencil, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE)); + + ThrowIfFailed(getCurrentResourceStorage().command_list->Close()); + m_commandQueueGraphic->ExecuteCommandLists(1, (ID3D12CommandList**)getCurrentResourceStorage().command_list.GetAddressOf()); + getCurrentResourceStorage().set_new_command_list(); + + wait_for_command_queue(m_device.Get(), m_commandQueueGraphic.Get()); + m_readbackResources.m_get_pos = m_readbackResources.get_current_put_pos_minus_one(); + + void *mapped_buffer; + ThrowIfFailed(readback_buffer->Map(0, nullptr, &mapped_buffer)); + for (unsigned row = 0; row < clip_h; row++) + { + char *casted_dest = (char*)buffer + row * clip_w; + char *casted_src = (char*)mapped_buffer + row * row_pitch; + for (unsigned col = 0; col < row_pitch; col++) + *casted_dest++ = *casted_src++; + } + readback_buffer->Unmap(0, nullptr); +} + #endif diff --git a/rpcs3/Emu/RSX/GCM.h b/rpcs3/Emu/RSX/GCM.h index c726b8635f..4220ee85c6 100644 --- a/rpcs3/Emu/RSX/GCM.h +++ b/rpcs3/Emu/RSX/GCM.h @@ -1,6 +1,7 @@ #pragma once #include "Utilities/types.h" #include "Emu/Memory/vm.h" +#include enum { @@ -1644,4 +1645,353 @@ namespace rsx return fmt::format("unknown/illegal method [0x%08x]", id); } + + static std::string get_blend_factor(u16 factor) noexcept + { + switch (factor) + { + case CELL_GCM_ZERO: return "0"; + case CELL_GCM_ONE: return "1"; + case CELL_GCM_SRC_COLOR: return "src.rgb"; + case CELL_GCM_ONE_MINUS_SRC_COLOR: return "(1 - src.rgb)"; + case CELL_GCM_SRC_ALPHA: return "src.a"; + case CELL_GCM_ONE_MINUS_SRC_ALPHA: return "(1 - src.a)"; + case CELL_GCM_DST_ALPHA: return "dst.a"; + case CELL_GCM_ONE_MINUS_DST_ALPHA: return "(1 - dst.a)"; + case CELL_GCM_DST_COLOR: return "dst.rgb"; + case CELL_GCM_ONE_MINUS_DST_COLOR: return "(1 - dst.rgb)"; + case CELL_GCM_SRC_ALPHA_SATURATE: return "sat(src.a)"; + case CELL_GCM_CONSTANT_COLOR: return "const.rgb"; + case CELL_GCM_ONE_MINUS_CONSTANT_COLOR: return "(1 - const.rgb)"; + case CELL_GCM_CONSTANT_ALPHA: return "const.a"; + case CELL_GCM_ONE_MINUS_CONSTANT_ALPHA: return "(1 - const.a)"; + } + return "Error"; + } + + static std::string get_blend_op(u16 op) noexcept + { + switch (op) + { + case CELL_GCM_FUNC_ADD: return "Add"; + case CELL_GCM_FUNC_SUBTRACT: return "Substract"; + case CELL_GCM_FUNC_REVERSE_SUBTRACT: return "Reverse_substract"; + case CELL_GCM_MIN: return "Min"; + case CELL_GCM_MAX: return "Max"; + case CELL_GCM_FUNC_ADD_SIGNED: return "Add_signed"; + case CELL_GCM_FUNC_REVERSE_ADD_SIGNED: return "Reverse_add_signed"; + case CELL_GCM_FUNC_REVERSE_SUBTRACT_SIGNED: return "Reverse_substract_signed"; + } + return "Error"; + } + + static std::string get_logic_op(u32 op) noexcept + { + switch (op) + { + case CELL_GCM_CLEAR: return "Clear"; + case CELL_GCM_AND: return "And"; + case CELL_GCM_AND_REVERSE: return "And_reverse"; + case CELL_GCM_COPY: return "Copy"; + case CELL_GCM_AND_INVERTED: return "And_inverted"; + case CELL_GCM_NOOP: return "Noop"; + case CELL_GCM_XOR: return "Xor"; + case CELL_GCM_OR: return "Or"; + case CELL_GCM_NOR: return "Nor"; + case CELL_GCM_EQUIV: return "Equiv"; + case CELL_GCM_INVERT: return "Invert"; + case CELL_GCM_OR_REVERSE: return "Or_reverse"; + case CELL_GCM_COPY_INVERTED: return "Copy_inverted"; + case CELL_GCM_OR_INVERTED: return "Or_inverted"; + case CELL_GCM_NAND: return "Nand"; + } + return "Error"; + } + + static std::string get_compare_func(u32 op) noexcept + { + switch (op) + { + case CELL_GCM_NEVER: return "Never"; + case CELL_GCM_LESS: return "Less"; + case CELL_GCM_EQUAL: return "Equal"; + case CELL_GCM_LEQUAL: return "Less_equal"; + case CELL_GCM_GREATER: return "Greater"; + case CELL_GCM_NOTEQUAL: return "Not_equal"; + case CELL_GCM_GEQUAL: return "Greater_equal"; + case CELL_GCM_ALWAYS: return "Always"; + } + return "Error"; + } + + static std::string get_primitive_mode(u8 draw_mode) noexcept + { + switch (draw_mode) + { + case CELL_GCM_PRIMITIVE_POINTS: return "Points"; + case CELL_GCM_PRIMITIVE_LINES: return "Lines"; + case CELL_GCM_PRIMITIVE_LINE_LOOP: return "Line_loop"; + case CELL_GCM_PRIMITIVE_LINE_STRIP: return "Line_strip"; + case CELL_GCM_PRIMITIVE_TRIANGLES: return "Triangles"; + case CELL_GCM_PRIMITIVE_TRIANGLE_STRIP: return "Triangle_strip"; + case CELL_GCM_PRIMITIVE_TRIANGLE_FAN: return "Triangle_fan"; + case CELL_GCM_PRIMITIVE_QUADS: return "Quads"; + case CELL_GCM_PRIMITIVE_QUAD_STRIP: return "Quad_strip"; + case CELL_GCM_PRIMITIVE_POLYGON: return "Polygon"; + } + return "Error"; + } + + static std::string ptr_to_string(u32 ptr) noexcept + { + std::stringstream ss; + ss << (void*)(u64)ptr; + return ss.str(); + } + + static std::string dma_mode(u32 arg) noexcept + { + switch (arg) + { + case CELL_GCM_LOCATION_LOCAL: + case CELL_GCM_CONTEXT_DMA_MEMORY_FRAME_BUFFER: return "Local memory"; + case CELL_GCM_LOCATION_MAIN: + case CELL_GCM_CONTEXT_DMA_MEMORY_HOST_BUFFER: return "Main memory"; + } + return "Error"; + } + + + static std::string depth_stencil_surface_format(u32 format) noexcept + { + switch (format) + { + case CELL_GCM_SURFACE_Z16: return "CELL_GCM_SURFACE_Z16"; + case CELL_GCM_SURFACE_Z24S8: return "CELL_GCM_SURFACE_Z24S8"; + } + return "Error"; + } + + static std::string color_surface_format(u32 format) noexcept + { + switch (format) + { + case CELL_GCM_SURFACE_X1R5G5B5_Z1R5G5B5: return "CELL_GCM_SURFACE_X1R5G5B5_Z1R5G5B5"; + case CELL_GCM_SURFACE_X1R5G5B5_O1R5G5B5: return "CELL_GCM_SURFACE_X1R5G5B5_O1R5G5B5"; + case CELL_GCM_SURFACE_R5G6B5: return "CELL_GCM_SURFACE_R5G6B5"; + case CELL_GCM_SURFACE_X8R8G8B8_Z8R8G8B8: return "CELL_GCM_SURFACE_X8R8G8B8_Z8R8G8B8"; + case CELL_GCM_SURFACE_X8R8G8B8_O8R8G8B8: return "CELL_GCM_SURFACE_X8R8G8B8_O8R8G8B8"; + case CELL_GCM_SURFACE_A8R8G8B8: return "CELL_GCM_SURFACE_A8R8G8B8"; + case CELL_GCM_SURFACE_B8: return "CELL_GCM_SURFACE_B8"; + case CELL_GCM_SURFACE_G8B8: return "CELL_GCM_SURFACE_G8B8"; + case CELL_GCM_SURFACE_F_W16Z16Y16X16: return "CELL_GCM_SURFACE_F_W16Z16Y16X16"; + case CELL_GCM_SURFACE_F_W32Z32Y32X32: return "CELL_GCM_SURFACE_F_W32Z32Y32X32"; + case CELL_GCM_SURFACE_F_X32: return "CELL_GCM_SURFACE_F_X32"; + case CELL_GCM_SURFACE_X8B8G8R8_Z8B8G8R8: return "CELL_GCM_SURFACE_X8B8G8R8_Z8B8G8R8"; + case CELL_GCM_SURFACE_X8B8G8R8_O8B8G8R8: return "CELL_GCM_SURFACE_X8B8G8R8_O8B8G8R8"; + case CELL_GCM_SURFACE_A8B8G8R8: return "CELL_GCM_SURFACE_A8B8G8R8"; + } + return "Error"; + } + + static std::string surface_target(u32 target) noexcept + { + switch (target) + { + case CELL_GCM_SURFACE_TARGET_0: return "surface A"; + case CELL_GCM_SURFACE_TARGET_1: return "surface B"; + case CELL_GCM_SURFACE_TARGET_MRT1: return "surfaces A and B"; + case CELL_GCM_SURFACE_TARGET_MRT2: return "surfaces A, B and C"; + case CELL_GCM_SURFACE_TARGET_MRT3: return "surfaces A,B, C and D"; + } + return "Error"; + } + + static std::string get_clear_color(u32 clear_color) noexcept + { + u8 clear_a = clear_color >> 24; + u8 clear_r = clear_color >> 16; + u8 clear_g = clear_color >> 8; + u8 clear_b = clear_color; + return "A = " + std::to_string(clear_a / 255.0f) + " R = " + std::to_string(clear_r / 255.0f) + " G = " + std::to_string(clear_g / 255.0f) + " B = " + std::to_string(clear_b / 255.0f); + } + + static std::string get_zstencil_clear(u32 zstencil) noexcept + { + u32 depth = zstencil >> 8; + u32 stencil = zstencil & 0xff; + return "Z = " + std::to_string(depth) + " S = " + std::to_string(stencil); + } + + static std::string get_stencil_op(u32 op) noexcept + { + switch (op) + { + case CELL_GCM_KEEP: return "Keep"; + case CELL_GCM_ZERO: return "Zero"; + case CELL_GCM_REPLACE: return "Replace"; + case CELL_GCM_INCR: return "Incr"; + case CELL_GCM_DECR: return "Decr"; + case CELL_GCM_INCR_WRAP: return "Incr_wrap"; + case CELL_GCM_DECR_WRAP: return "Decr_wrap"; + } + return "Error"; + } + + static std::string get_vertex_attribute_format(u8 type) noexcept + { + switch (type) + { + case CELL_GCM_VERTEX_S1: return "Short"; + case CELL_GCM_VERTEX_F: return "Float"; + case CELL_GCM_VERTEX_SF: return "Half float"; + case CELL_GCM_VERTEX_UB: return "Unsigned byte"; + case CELL_GCM_VERTEX_S32K: return "Signed int"; + case CELL_GCM_VERTEX_CMP: return "CMP"; + case CELL_GCM_VERTEX_UB256: return "UB256"; + } + return "Error"; + } + + static std::string unpack_vertex_format(u32 arg) noexcept + { + u32 frequency = arg >> 16; + u32 stride = (arg >> 8) & 0xff; + u32 size = (arg >> 4) & 0xf; + u32 type = arg & 0xf; + if (size == 0) + return "(disabled)"; + + return "Type = " + get_vertex_attribute_format(type) + " size = " + std::to_string(size) + " stride = " + std::to_string(stride) + " frequency = " + std::to_string(frequency); + } + + static std::string index_type(u16 arg) noexcept + { + switch (arg) + { + case CELL_GCM_DRAW_INDEX_ARRAY_TYPE_16: return "unsigned short"; + case CELL_GCM_DRAW_INDEX_ARRAY_TYPE_32: return "unsigned int"; + } + return "Error"; + } + + static std::function get_pretty_printing_function(const u32 id) + { + static const std::unordered_map > printing_functions = + { + { NV4097_NO_OPERATION , [](u32) { return "(nop)"; } }, + { NV4097_SET_ALPHA_TEST_ENABLE, [](u32 arg) { return (!!arg) ? "Alpha: enable" : "Alpha: disable"; } }, + { NV4097_SET_DEPTH_TEST_ENABLE, [](u32 arg) { return (!!arg) ? "Depth: enable" : "Depth: disable"; } }, + { NV4097_SET_DEPTH_MASK, [](u32 arg) { return (!!arg) ? "Depth: write enabled" : "Depth: write disabled"; } }, + { NV4097_SET_DEPTH_FUNC, [](u32 arg) { return "Depth: " + get_compare_func(arg); } }, + { NV4097_SET_LOGIC_OP_ENABLE, [](u32 arg) { return (!!arg) ? "Logic Op: enable" : "Logic Op: disable"; } }, + { NV4097_SET_LOGIC_OP, [](u32 arg) { return "Logic Op: " + get_logic_op(arg); } }, + { NV4097_SET_BLEND_ENABLE , [](u32 arg) { return (!!arg) ? "Blend: enable" : "Blend: disable"; } }, + { NV4097_SET_BLEND_FUNC_SFACTOR, [](u32 arg) { return "Blend: sfactor.rgb = " + get_blend_factor(arg & 0xFFFF) + " sfactor.a = " + get_blend_factor(arg >> 16); } }, + { NV4097_SET_BLEND_FUNC_DFACTOR, [](u32 arg) { return "Blend: dfactor.rgb = " + get_blend_factor(arg & 0xFFFF) + " dfactor.a = " + get_blend_factor(arg >> 16); } }, + { NV4097_SET_BLEND_EQUATION , [](u32 arg) { return "Blend: op.rgb = " + get_blend_op(arg & 0xFFFF) + " op.a = " + get_blend_op(arg >> 16); } }, + { NV4097_SET_BLEND_ENABLE_MRT, [](u32 arg) { return "Blend: mrt0 = " + std::to_string(!!(arg & 0x2)) + " mrt1 = " + std::to_string(!!(arg & 0x4)) + " mrt2 = " + std::to_string(!!(arg & 0x8)); } }, + { NV4097_SET_COLOR_MASK , [](u32 arg) { return "Color mask: A = " + std::to_string(!!(arg & 0x1000000)) + " R = " + std::to_string(!!(arg & 0x10000)) + " G = " + std::to_string(!!(arg & 0x100)) + " B = " + std::to_string(!!(arg & 0x1)); } }, + { NV4097_SET_VIEWPORT_HORIZONTAL, [](u32 arg) { return "Viewport: x = " + std::to_string(arg & 0xFFFF) + " width = " + std::to_string(arg >> 16); } }, + { NV4097_SET_VIEWPORT_VERTICAL, [](u32 arg) { return "Viewport: y = " + std::to_string(arg & 0xFFFF) + " height = " + std::to_string(arg >> 16); } }, + { NV4097_SET_BEGIN_END, [](u32 arg) { return arg ? "- Begin: " + get_primitive_mode(arg) + " -" : "- End -"; } }, + { NV4097_DRAW_ARRAYS, [](u32 arg) { return "Draw " + std::to_string((arg >> 24) + 1) + " vertex starting from " + std::to_string(arg & 0xFFFFFF); } }, + { NV4097_DRAW_INDEX_ARRAY, [](u32 arg) { return "Draw " + std::to_string((arg >> 24) + 1) + " index starting from " + std::to_string(arg & 0xFFFFFF); } }, + { NV4097_SET_SEMAPHORE_OFFSET, [](u32 arg) { return "Semaphore: @ " + ptr_to_string(arg); } }, + { NV4097_TEXTURE_READ_SEMAPHORE_RELEASE, [](u32 arg) { return "Write semaphore value " + std::to_string(arg); } }, + { NV4097_CLEAR_SURFACE, [](u32 arg) { return "Clear surface " + std::string(arg & 0x1 ? "Depth " : "") + std::string(arg & 0x2 ? "Stencil " : "") + std::string(arg & 0xF0 ? "Color " : ""); } }, + { NV4097_SET_CONTEXT_DMA_COLOR_A, [](u32 arg) { return "Surface A: DMA mode = " + dma_mode(arg);} }, + { NV4097_SET_SURFACE_PITCH_A, [](u32 arg) { return "Surface A: Pitch = " + std::to_string(arg); } }, + { NV4097_SET_SURFACE_COLOR_AOFFSET, [](u32 arg) { return "Surface A: Offset = " + std::to_string(arg); } }, + { NV4097_SET_CONTEXT_DMA_COLOR_B, [](u32 arg) { return "Surface B: DMA mode = " + dma_mode(arg);} }, + { NV4097_SET_SURFACE_PITCH_B, [](u32 arg) { return "Surface B: Pitch = " + std::to_string(arg); } }, + { NV4097_SET_SURFACE_COLOR_BOFFSET, [](u32 arg) { return "Surface B: Offset = " + std::to_string(arg); } }, + { NV4097_SET_CONTEXT_DMA_COLOR_C, [](u32 arg) { return "Surface C: DMA mode = " + dma_mode(arg);} }, + { NV4097_SET_SURFACE_PITCH_C, [](u32 arg) { return "Surface C: Pitch = " + std::to_string(arg); } }, + { NV4097_SET_SURFACE_COLOR_COFFSET, [](u32 arg) { return "Surface C: Offset = " + std::to_string(arg); } }, + { NV4097_SET_SURFACE_PITCH_D, [](u32 arg) { return "Surface D: Pitch = " + std::to_string(arg); } }, + { NV4097_SET_SURFACE_COLOR_DOFFSET, [](u32 arg) { return "Surface D: Offset = " + std::to_string(arg); } }, + { NV4097_SET_CONTEXT_DMA_COLOR_D, [](u32 arg) { return "Surface D: DMA mode = " + dma_mode(arg);} }, + { NV4097_SET_SURFACE_PITCH_Z, [](u32 arg) { return "Surface Zeta: Pitch = " + std::to_string(arg); } }, + { NV4097_SET_SURFACE_ZETA_OFFSET, [](u32 arg) { return "Surface Zeta: Offset = " + std::to_string(arg); } }, + { NV4097_SET_CONTEXT_DMA_ZETA, [](u32 arg) { return "Surface Zeta: DMA mode = " + dma_mode(arg);} }, + { NV4097_SET_SURFACE_FORMAT, [](u32 arg) { return "Surface: Color format = " + color_surface_format(arg & 0x1F) + " DepthStencil format = " + depth_stencil_surface_format((arg >> 5) & 0x7); } }, + { NV4097_SET_SURFACE_CLIP_HORIZONTAL, [](u32 arg) { return "Surface: clip x = " + std::to_string(arg & 0xFFFF) + " width = " + std::to_string(arg >> 16); } }, + { NV4097_SET_SURFACE_CLIP_VERTICAL, [](u32 arg) { return "Surface: clip y = " + std::to_string(arg & 0xFFFF) + " height = " + std::to_string(arg >> 16); } }, + { NV4097_SET_SURFACE_COLOR_TARGET, [](u32 arg) { return "Surface: Targets " + surface_target(arg); } }, + { NV4097_SET_COLOR_CLEAR_VALUE, [](u32 arg) { return "Clear: " + get_clear_color(arg); } }, + { NV4097_SET_ZSTENCIL_CLEAR_VALUE, [](u32 arg) { return "Clear: " + get_zstencil_clear(arg); } }, + { NV4097_SET_CLIP_MIN, [](u32 arg) { return "Depth: Min = " + std::to_string((f32&)arg); } }, + { NV4097_SET_CLIP_MAX, [](u32 arg) { return "Depth: Max = " + std::to_string((f32&)arg); } }, + { NV4097_SET_VIEWPORT_SCALE, [](u32 arg) { return "Viewport: x scale = " + std::to_string((f32&)arg); } }, + { NV4097_SET_VIEWPORT_SCALE + 1, [](u32 arg) { return "Viewport: y scale = " + std::to_string((f32&)arg); } }, + { NV4097_SET_VIEWPORT_SCALE + 2, [](u32 arg) { return "Viewport: z scale = " + std::to_string((f32&)arg); } }, + { NV4097_SET_VIEWPORT_OFFSET, [](u32 arg) { return "Viewport: x offset = " + std::to_string((f32&)arg); } }, + { NV4097_SET_VIEWPORT_OFFSET + 1, [](u32 arg) { return "Viewport: y offset = " + std::to_string((f32&)arg); } }, + { NV4097_SET_VIEWPORT_OFFSET + 2, [](u32 arg) { return "Viewport: z offset = " + std::to_string((f32&)arg); } }, + { NV4097_SET_SCISSOR_HORIZONTAL, [](u32 arg) { return "Scissor: x = " + std::to_string(arg & 0xFFFF) + " width = " + std::to_string(arg >> 16); } }, + { NV4097_SET_SCISSOR_VERTICAL, [](u32 arg) { return "Scissor: y = " + std::to_string(arg & 0xFFFF) + " height = " + std::to_string(arg >> 16); } }, + { NV4097_SET_STENCIL_TEST_ENABLE, [](u32 arg) { return (!!arg) ? "Stencil: Enable" : "Stencil : Disable"; } }, + { NV4097_SET_STENCIL_FUNC, [](u32 arg) { return "Stencil: " + get_compare_func(arg); } }, + { NV4097_SET_BACK_STENCIL_OP_ZPASS, [](u32 arg) { return "Stencil: Back ZPass = " + get_stencil_op(arg); } }, + { NV4097_SET_BACK_STENCIL_OP_ZFAIL, [](u32 arg) { return "Stencil: Back ZFail = " + get_stencil_op(arg); } }, + { NV4097_SET_BACK_STENCIL_OP_FAIL, [](u32 arg) { return "Stencil: Back Fail = " + get_stencil_op(arg); } }, + { NV4097_SET_STENCIL_OP_ZPASS, [](u32 arg) { return "Stencil: ZPass = " + get_stencil_op(arg); } }, + { NV4097_SET_STENCIL_OP_ZFAIL, [](u32 arg) { return "Stencil: ZFail = " + get_stencil_op(arg); } }, + { NV4097_SET_STENCIL_OP_FAIL, [](u32 arg) { return "Stencil: Fail = " + get_stencil_op(arg); } }, + { NV4097_SET_STENCIL_FUNC_MASK, [](u32 arg) { return "Stencil: Func mask = " + ptr_to_string(arg); } }, + { NV4097_SET_STENCIL_MASK, [](u32 arg) { return "Stencil: Mask = " + ptr_to_string(arg); } }, + { NV4097_SET_STENCIL_FUNC_REF, [](u32 arg) { return "Stencil: Ref = " + std::to_string(arg); } }, + { NV4097_INVALIDATE_VERTEX_CACHE_FILE, [](u32) { return "(invalidate vertex cache file)"; } }, + { NV4097_INVALIDATE_VERTEX_FILE, [](u32) { return "(invalidate vertex file)"; } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT, [](u32 arg) { return "Vertex array 0: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 1, [](u32 arg) { return "Vertex array 1: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 2, [](u32 arg) { return "Vertex array 2: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 3, [](u32 arg) { return "Vertex array 3: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 4, [](u32 arg) { return "Vertex array 4: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 5, [](u32 arg) { return "Vertex array 5: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 6, [](u32 arg) { return "Vertex array 6: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 7, [](u32 arg) { return "Vertex array 7: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 8, [](u32 arg) { return "Vertex array 8: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 9, [](u32 arg) { return "Vertex array 9: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 10, [](u32 arg) { return "Vertex array 10: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 11, [](u32 arg) { return "Vertex array 11: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 12, [](u32 arg) { return "Vertex array 12: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 13, [](u32 arg) { return "Vertex array 13: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 14, [](u32 arg) { return "Vertex array 14: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + 15, [](u32 arg) { return "Vertex array 15: " + unpack_vertex_format(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET, [](u32 arg) { return "Vertex array 0: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 1, [](u32 arg) { return "Vertex array 1: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 2, [](u32 arg) { return "Vertex array 2: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 3, [](u32 arg) { return "Vertex array 3: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 4, [](u32 arg) { return "Vertex array 4: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 5, [](u32 arg) { return "Vertex array 5: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 6, [](u32 arg) { return "Vertex array 6: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 7, [](u32 arg) { return "Vertex array 7: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 8, [](u32 arg) { return "Vertex array 8: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 9, [](u32 arg) { return "Vertex array 9: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 10, [](u32 arg) { return "Vertex array 10: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 11, [](u32 arg) { return "Vertex array 11: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 12, [](u32 arg) { return "Vertex array 12: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 13, [](u32 arg) { return "Vertex array 13: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 14, [](u32 arg) { return "Vertex array 14: Offset = " + std::to_string(arg); } }, + { NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + 15, [](u32 arg) { return "Vertex array 15: Offset = " + std::to_string(arg); } }, + { NV4097_SET_INDEX_ARRAY_ADDRESS, [](u32 arg) { return "Index array: Address = " + ptr_to_string(arg); } }, + { NV4097_SET_INDEX_ARRAY_DMA, [](u32 arg) { return "Index array: DMA mode = " + dma_mode(arg & 0xF) + " type = " + index_type(arg >> 4); } }, + }; + + + auto found = printing_functions.find(id); + if (found != printing_functions.end()) + { + return found->second; + } + + return [=](u32 v) + { + std::stringstream ss; + ss << rsx::get_method_name(id) << " : " << (void*)(u64)v; + return ss.str(); + }; + } } \ No newline at end of file diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 906e5e09eb..4335bf19ff 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -21,7 +21,8 @@ extern "C" #define CMD_DEBUG 0 -extern u64 get_system_time(); +bool user_asked_for_frame_capture = false; +frame_capture_data frame_debug; namespace rsx { @@ -552,6 +553,18 @@ namespace rsx void flip_command(thread* rsx, u32 arg) { + if (user_asked_for_frame_capture) + { + rsx->capture_current_frame = true; + user_asked_for_frame_capture = false; + frame_debug.reset(); + } + else if (rsx->capture_current_frame) + { + rsx->capture_current_frame = false; + Emu.Pause(); + } + rsx->gcm_current_buffer = arg; rsx->flip(arg); @@ -625,7 +638,11 @@ namespace rsx { // try process using gpu if (rsx->domethod(id, arg)) + { + if (rsx->capture_current_frame && id == NV4097_CLEAR_SURFACE) + rsx->capture_frame(); return; + } // not handled by renderer // try process using cpu @@ -872,6 +889,53 @@ namespace rsx } } + void thread::capture_frame() + { + frame_capture_data::draw_state draw_state = {}; + + int clip_w = rsx::method_registers[NV4097_SET_SURFACE_CLIP_HORIZONTAL] >> 16; + int clip_h = rsx::method_registers[NV4097_SET_SURFACE_CLIP_VERTICAL] >> 16; + size_t pitch = clip_w * 4; + std::vector color_index_to_record; + switch (method_registers[NV4097_SET_SURFACE_COLOR_TARGET]) + { + case CELL_GCM_SURFACE_TARGET_0: + color_index_to_record = { 0 }; + break; + case CELL_GCM_SURFACE_TARGET_1: + color_index_to_record = { 1 }; + break; + case CELL_GCM_SURFACE_TARGET_MRT1: + color_index_to_record = { 0, 1 }; + break; + case CELL_GCM_SURFACE_TARGET_MRT2: + color_index_to_record = { 0, 1, 2 }; + break; + case CELL_GCM_SURFACE_TARGET_MRT3: + color_index_to_record = { 0, 1, 2, 3 }; + break; + } + for (size_t i : color_index_to_record) + { + draw_state.color_buffer[i].width = clip_w; + draw_state.color_buffer[i].height = clip_h; + draw_state.color_buffer[i].data.resize(pitch * clip_h); + copy_render_targets_to_memory(draw_state.color_buffer[i].data.data(), i); + } + if (get_address(method_registers[NV4097_SET_SURFACE_ZETA_OFFSET], method_registers[NV4097_SET_CONTEXT_DMA_ZETA])) + { + draw_state.depth.width = clip_w; + draw_state.depth.height = clip_h; + draw_state.depth.data.resize(clip_w * clip_h * 4); + copy_depth_buffer_to_memory(draw_state.depth.data.data()); + draw_state.stencil.width = clip_w; + draw_state.stencil.height = clip_h; + draw_state.stencil.data.resize(clip_w * clip_h * 4); + copy_stencil_buffer_to_memory(draw_state.stencil.data.data()); + } + frame_debug.draw_calls.push_back(draw_state); + } + void thread::begin() { draw_mode = method_registers[NV4097_SET_BEGIN_END]; @@ -884,6 +948,9 @@ namespace rsx vertex_array.clear(); transform_constants.clear(); + + if (capture_current_frame) + capture_frame(); } void thread::task() @@ -1006,6 +1073,8 @@ namespace rsx } method_registers[reg] = value; + if (capture_current_frame) + frame_debug.command_queue.push_back(std::make_pair(reg, value)); if (auto method = methods[reg]) method(this, value); diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 4e1d8aa824..e9f1770701 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -10,6 +10,36 @@ #include "Utilities/Timer.h" #include "Utilities/types.h" + +extern u64 get_system_time(); + +struct frame_capture_data +{ + struct buffer + { + std::vector data; + size_t width = 0, height = 0; + }; + + struct draw_state + { + buffer color_buffer[4]; + buffer depth; + buffer stencil; + }; + std::vector > command_queue; + std::vector draw_calls; + + void reset() noexcept + { + command_queue.clear(); + draw_calls.clear(); + } +}; + +extern bool user_asked_for_frame_capture; +extern frame_capture_data frame_debug; + namespace rsx { namespace limits @@ -169,6 +199,8 @@ namespace rsx virtual void load_vertex_data(u32 first, u32 count); virtual void load_vertex_index_data(u32 first, u32 count); + bool capture_current_frame = false; + void capture_frame(); public: u32 ioAddress, ioSize; int flip_status; @@ -233,6 +265,23 @@ namespace rsx */ void fill_vertex_program_constants_data(void *buffer) noexcept; + /** + * Copy rtt values to buffer. + * TODO: It's more efficient to combine multiple call of this function into one. + */ + virtual void copy_render_targets_to_memory(void *buffer, u8 rtt) {}; + + /** + * Copy depth content to buffer. + * TODO: It's more efficient to combine multiple call of this function into one. + */ + virtual void copy_depth_buffer_to_memory(void *buffer) {}; + + /** + * Copy stencil content to buffer. + * TODO: It's more efficient to combine multiple call of this function into one. + */ + virtual void copy_stencil_buffer_to_memory(void *buffer) {}; public: void reset(); void init(const u32 ioAddress, const u32 ioSize, const u32 ctrlAddress, const u32 localAddress); diff --git a/rpcs3/Gui/Debugger.cpp b/rpcs3/Gui/Debugger.cpp index f2c8a16df7..05ab3d1337 100644 --- a/rpcs3/Gui/Debugger.cpp +++ b/rpcs3/Gui/Debugger.cpp @@ -9,12 +9,14 @@ #include "Emu/CPU/CPUThreadManager.h" #include "Emu/CPU/CPUThread.h" +extern bool user_asked_for_frame_capture; class DbgEmuPanel : public wxPanel { wxButton* m_btn_run; wxButton* m_btn_stop; wxButton* m_btn_restart; + wxButton* m_btn_capture_frame; public: DbgEmuPanel(wxWindow* parent) : wxPanel(parent) @@ -28,11 +30,15 @@ public: m_btn_restart = new wxButton(this, wxID_ANY, "Restart"); m_btn_restart->Bind(wxEVT_BUTTON, &DbgEmuPanel::OnRestart, this); + m_btn_capture_frame = new wxButton(this, wxID_ANY, "Capture frame"); + m_btn_capture_frame->Bind(wxEVT_BUTTON, &DbgEmuPanel::OnCaptureFrame, this); + wxBoxSizer* s_b_main = new wxBoxSizer(wxHORIZONTAL); s_b_main->Add(m_btn_run, wxSizerFlags().Border(wxALL, 5)); s_b_main->Add(m_btn_stop, wxSizerFlags().Border(wxALL, 5)); s_b_main->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND); s_b_main->Add(m_btn_restart, wxSizerFlags().Border(wxALL, 5)); + s_b_main->Add(m_btn_capture_frame, wxSizerFlags().Border(wxALL, 5)); SetSizerAndFit(s_b_main); Layout(); @@ -75,6 +81,11 @@ public: Emu.Load(); } + void OnCaptureFrame(wxCommandEvent& event) + { + user_asked_for_frame_capture = true; + } + void HandleCommand(wxCommandEvent& event) { event.Skip(); diff --git a/rpcs3/Gui/RSXDebugger.cpp b/rpcs3/Gui/RSXDebugger.cpp index eca281215d..522dce6360 100644 --- a/rpcs3/Gui/RSXDebugger.cpp +++ b/rpcs3/Gui/RSXDebugger.cpp @@ -82,6 +82,8 @@ RSXDebugger::RSXDebugger(wxWindow* parent) //Tabs wxNotebook* nb_rsx = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxSize(732, 732)); wxPanel* p_commands = new wxPanel(nb_rsx, wxID_ANY); + wxPanel* p_captured_frame = new wxPanel(nb_rsx, wxID_ANY); + wxPanel* p_captured_draw_calls = new wxPanel(nb_rsx, wxID_ANY); wxPanel* p_flags = new wxPanel(nb_rsx, wxID_ANY); wxPanel* p_programs = new wxPanel(nb_rsx, wxID_ANY); wxPanel* p_lightning = new wxPanel(nb_rsx, wxID_ANY); @@ -89,6 +91,8 @@ RSXDebugger::RSXDebugger(wxWindow* parent) wxPanel* p_settings = new wxPanel(nb_rsx, wxID_ANY); nb_rsx->AddPage(p_commands, wxT("RSX Commands")); + nb_rsx->AddPage(p_captured_frame, wxT("Captured Frame")); + nb_rsx->AddPage(p_captured_draw_calls, wxT("Captured Draw Calls")); nb_rsx->AddPage(p_flags, wxT("Flags")); nb_rsx->AddPage(p_programs, wxT("Programs")); nb_rsx->AddPage(p_lightning, wxT("Lightning")); @@ -97,6 +101,8 @@ RSXDebugger::RSXDebugger(wxWindow* parent) //Tabs: Lists m_list_commands = new wxListView(p_commands, wxID_ANY, wxPoint(1,3), wxSize(720, 720)); + m_list_captured_frame = new wxListView(p_captured_frame, wxID_ANY, wxPoint(1, 3), wxSize(720, 720)); + m_list_captured_draw_calls = new wxListView(p_captured_draw_calls, wxID_ANY, wxPoint(1, 3), wxSize(720, 720)); m_list_flags = new wxListView(p_flags, wxID_ANY, wxPoint(1,3), wxSize(720, 720)); m_list_programs = new wxListView(p_programs, wxID_ANY, wxPoint(1,3), wxSize(720, 720)); m_list_lightning = new wxListView(p_lightning, wxID_ANY, wxPoint(1,3), wxSize(720, 720)); @@ -105,6 +111,8 @@ RSXDebugger::RSXDebugger(wxWindow* parent) //Tabs: List Style m_list_commands ->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); + m_list_captured_frame->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); + m_list_captured_draw_calls->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); m_list_flags ->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); m_list_programs ->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); m_list_lightning->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); @@ -116,6 +124,8 @@ RSXDebugger::RSXDebugger(wxWindow* parent) m_list_commands->InsertColumn(1, "Value", 0, 80); m_list_commands->InsertColumn(2, "Command", 0, 500); m_list_commands->InsertColumn(3, "Count", 0, 40); + m_list_captured_frame->InsertColumn(0, "Column", 0, 720); + m_list_captured_draw_calls->InsertColumn(0, "Draw calls", 0, 720); m_list_flags->InsertColumn(0, "Name", 0, 170); m_list_flags->InsertColumn(1, "Value", 0, 270); m_list_programs->InsertColumn(0, "ID", 0, 70); @@ -144,6 +154,8 @@ RSXDebugger::RSXDebugger(wxWindow* parent) { m_list_commands->InsertItem(m_list_commands->GetItemCount(), wxEmptyString); } + for (u32 i = 0; iInsertItem(1, wxEmptyString); //Tools: Tools = Controls + Notebook Tabs s_tools->AddSpacer(10); @@ -228,6 +240,7 @@ RSXDebugger::RSXDebugger(wxWindow* parent) //p_buffer_depth->Bind(wxEVT_BUTTON, &RSXDebugger::OnClickBuffer, this); //p_buffer_stencil->Bind(wxEVT_BUTTON, &RSXDebugger::OnClickBuffer, this); p_buffer_tex->Bind(wxEVT_LEFT_DOWN, &RSXDebugger::OnClickBuffer, this); + m_list_captured_draw_calls->Bind(wxEVT_LEFT_DOWN, &RSXDebugger::OnClickDrawCalls, this); m_list_commands->Bind(wxEVT_MOUSEWHEEL, &RSXDebugger::OnScrollMemory, this); m_list_flags->Bind(wxEVT_LIST_ITEM_ACTIVATED, &RSXDebugger::SetFlags, this); @@ -330,6 +343,107 @@ void RSXDebugger::OnClickBuffer(wxMouseEvent& event) #undef SHOW_BUFFER } +namespace +{ + /** + * Return a new buffer that can be passed to wxImage ctor. + * The pointer seems to be freed by wxImage. + */ + u8* convert_to_wximage_buffer(u8 *orig_buffer, size_t width, size_t height) noexcept + { + unsigned char* buffer = (unsigned char*)malloc(width * height * 3); + for (u32 i = 0; i < width * height; i++) + { + buffer[0 + i * 3] = orig_buffer[3 + i * 4]; + buffer[1 + i * 3] = orig_buffer[2 + i * 4]; + buffer[2 + i * 3] = orig_buffer[1 + i * 4]; + } + return buffer; + } +}; + +void RSXDebugger::OnClickDrawCalls(wxMouseEvent& event) +{ + size_t draw_id = m_list_captured_draw_calls->GetFirstSelected(); + + wxPanel* p_buffers[] = + { + p_buffer_colorA, + p_buffer_colorB, + p_buffer_colorC, + p_buffer_colorD, + }; + + for (size_t i = 0; i < 4; i++) + { + size_t width = frame_debug.draw_calls[draw_id].color_buffer[i].width, height = frame_debug.draw_calls[draw_id].color_buffer[i].height; + if (width && height) + { + unsigned char *orig_buffer = frame_debug.draw_calls[draw_id].color_buffer[i].data.data(); + wxImage img(width, height, convert_to_wximage_buffer(orig_buffer, width, height)); + wxClientDC dc_canvas(p_buffers[i]); + + if (img.IsOk()) + dc_canvas.DrawBitmap(img.Scale(m_panel_width, m_panel_height), 0, 0, false); + } + } + + // Buffer Z + { + size_t width = frame_debug.draw_calls[draw_id].depth.width, height = frame_debug.draw_calls[draw_id].depth.height; + if (width && height) + { + u32 *orig_buffer = (u32*)frame_debug.draw_calls[draw_id].depth.data.data(); + unsigned char *buffer = (unsigned char *)malloc(width * height * 3); + + for (u32 row = 0; row < height; row++) + { + for (u32 col = 0; col < width; col++) + { + u32 depth_val = orig_buffer[row * width + col]; + u8 displayed_depth_val = 255 * depth_val / 0xFFFFFF; + buffer[3 * col + 0 + width * row * 3] = displayed_depth_val; + buffer[3 * col + 1 + width * row * 3] = displayed_depth_val; + buffer[3 * col + 2 + width * row * 3] = displayed_depth_val; + } + } + + wxImage img(width, height, buffer); + wxClientDC dc_canvas(p_buffer_depth); + + if (img.IsOk()) + dc_canvas.DrawBitmap(img.Scale(m_panel_width, m_panel_height), 0, 0, false); + } + } + + // Buffer S + { + size_t width = frame_debug.draw_calls[draw_id].stencil.width, height = frame_debug.draw_calls[draw_id].stencil.height; + if (width && height) + { + u8 *orig_buffer = frame_debug.draw_calls[draw_id].stencil.data.data(); + unsigned char *buffer = (unsigned char *)malloc(width * height * 3); + + for (u32 row = 0; row < height; row++) + { + for (u32 col = 0; col < width; col++) + { + u32 stencil_val = orig_buffer[row * width + col]; + buffer[3 * col + 0 + width * row * 3] = stencil_val; + buffer[3 * col + 1 + width * row * 3] = stencil_val; + buffer[3 * col + 2 + width * row * 3] = stencil_val; + } + } + + wxImage img(width, height, buffer); + wxClientDC dc_canvas(p_buffer_stencil); + + if (img.IsOk()) + dc_canvas.DrawBitmap(img.Scale(m_panel_width, m_panel_height), 0, 0, false); + } + } +} + void RSXDebugger::GoToGet(wxCommandEvent& event) { if (!RSXReady()) return; @@ -401,6 +515,15 @@ void RSXDebugger::GetMemory() m_list_commands->SetItem(i, 1, "????????"); } } + + for (u32 i = 0; i < frame_debug.command_queue.size(); i++) + { + std::string str = rsx::get_pretty_printing_function(frame_debug.command_queue[i].first)(frame_debug.command_queue[i].second); + m_list_captured_frame->SetItem(i, 0, str); + } + + for (u32 i = 0;i < frame_debug.draw_calls.size(); i++) + m_list_captured_draw_calls->InsertItem(0, std::to_string(frame_debug.draw_calls.size() - i - 1)); } void RSXDebugger::GetBuffers() @@ -842,120 +965,10 @@ wxString RSXDebugger::DisAsmCommand(u32 cmd, u32 count, u32 currentAddr, u32 ioA u32 index = 0; switch((cmd & 0x3ffff) >> 2) { - case NV4097_SET_SURFACE_FORMAT: - { - const u32 a0 = (u32)args[0]; - const u32 surface_format = a0 & 0x1f; - const u32 surface_depth_format = (a0 >> 5) & 0x7; - - const char *depth_type_name, *color_type_name; - switch (surface_depth_format) - { - case CELL_GCM_SURFACE_Z16: - depth_type_name = "CELL_GCM_SURFACE_Z16"; - break; - case CELL_GCM_SURFACE_Z24S8: - depth_type_name = "CELL_GCM_SURFACE_Z24S8"; - break; - default: depth_type_name = ""; - break; - } - switch (surface_format) - { - case CELL_GCM_SURFACE_X1R5G5B5_Z1R5G5B5: - color_type_name = "CELL_GCM_SURFACE_X1R5G5B5_Z1R5G5B5"; - break; - case CELL_GCM_SURFACE_X1R5G5B5_O1R5G5B5: - color_type_name = "CELL_GCM_SURFACE_X1R5G5B5_O1R5G5B5"; - break; - case CELL_GCM_SURFACE_R5G6B5: - color_type_name = "CELL_GCM_SURFACE_R5G6B5"; - break; - case CELL_GCM_SURFACE_X8R8G8B8_Z8R8G8B8: - color_type_name = "CELL_GCM_SURFACE_X8R8G8B8_Z8R8G8B8"; - break; - case CELL_GCM_SURFACE_X8R8G8B8_O8R8G8B8: - color_type_name = "CELL_GCM_SURFACE_X8R8G8B8_O8R8G8B8"; - break; - case CELL_GCM_SURFACE_A8R8G8B8: - color_type_name = "CELL_GCM_SURFACE_A8R8G8B8"; - break; - case CELL_GCM_SURFACE_B8: - color_type_name = "CELL_GCM_SURFACE_B8"; - break; - case CELL_GCM_SURFACE_G8B8: - color_type_name = "CELL_GCM_SURFACE_G8B8"; - break; - case CELL_GCM_SURFACE_F_W16Z16Y16X16: - color_type_name = "CELL_GCM_SURFACE_F_W16Z16Y16X16"; - break; - case CELL_GCM_SURFACE_F_W32Z32Y32X32: - color_type_name = "CELL_GCM_SURFACE_F_W32Z32Y32X32"; - break; - case CELL_GCM_SURFACE_F_X32: - color_type_name = "CELL_GCM_SURFACE_F_X32"; - break; - case CELL_GCM_SURFACE_X8B8G8R8_Z8B8G8R8: - color_type_name = "CELL_GCM_SURFACE_X8B8G8R8_Z8B8G8R8"; - break; - case CELL_GCM_SURFACE_X8B8G8R8_O8B8G8R8: - color_type_name = "CELL_GCM_SURFACE_X8B8G8R8_O8B8G8R8"; - break; - case CELL_GCM_SURFACE_A8B8G8R8: - color_type_name = "CELL_GCM_SURFACE_A8B8G8R8"; - break; - default: color_type_name = ""; - break; - } - DISASM("Set surface format : C %s Z %s", color_type_name, depth_type_name); - } - break; - - case NV4097_SET_VIEWPORT_HORIZONTAL: - { - u32 m_viewport_x = (u32)args[0] & 0xffff; - u32 m_viewport_w = (u32)args[0] >> 16; - - if (count == 2) - { - u32 m_viewport_y = (u32)args[1] & 0xffff; - u32 m_viewport_h = (u32)args[1] >> 16; - DISASM("Set viewport horizontal %d %d", m_viewport_w, m_viewport_h); - } - else - DISASM("Set viewport horizontal %d", m_viewport_w); - break; - } - - case NV4097_SET_SURFACE_CLIP_HORIZONTAL: - { - const u32 a0 = (u32)args[0]; - - u32 clip_x = a0; - u32 clip_w = a0 >> 16; - - if (count == 2) - { - const u32 a1 = (u32)args[1]; - u32 clip_y = a1; - u32 clip_h = a1 >> 16; - DISASM("Set surface clip horizontal : %d %d", clip_w, clip_h); - } - else - DISASM("Set surface clip horizontal : %d", clip_w); - break; - } - - break; - case 0x3fead: DISASM("Flip and change current buffer: %d", (u32)args[0]); break; - case NV4097_NO_OPERATION: - DISASM("NOP"); - break; - case_16(NV4097_SET_TEXTURE_OFFSET, 0x20): DISASM("Texture Offset[%d]: %08x", index, (u32)args[0]); switch ((args[1] & 0x3) - 1) @@ -971,29 +984,13 @@ wxString RSXDebugger::DisAsmCommand(u32 cmd, u32 count, u32 currentAddr, u32 ioA ((args[1] >> 16) & 0xffff)); break; - case NV4097_SET_COLOR_MASK: - DISASM(" Color mask: True (A:%c, R:%c, G:%c, B:%c)", - args[0] & 0x1000000 ? '1' : '0', - args[0] & 0x0010000 ? '1' : '0', - args[0] & 0x0000100 ? '1' : '0', - args[0] & 0x0000001 ? '1' : '0'); - break; - - case NV4097_SET_ALPHA_TEST_ENABLE: - DISASM(args[0] ? "Alpha test: Enable" : "Alpha test: Disable"); - break; - - case NV4097_SET_BLEND_ENABLE: - DISASM(args[0] ? "Blend: Enable" : "Blend: Disable"); - break; - case NV4097_SET_DEPTH_BOUNDS_TEST_ENABLE: DISASM(args[0] ? "Depth bounds test: Enable" : "Depth bounds test: Disable"); break; default: { - std::string str = rsx::get_method_name((cmd & 0x3ffff) >> 2); - DISASM("%s : 0x%x", str.c_str(), (u32)args[0]); + std::string str = rsx::get_pretty_printing_function((cmd & 0x3ffff) >> 2)((u32)args[0]); + DISASM("%s", str.c_str()); } } diff --git a/rpcs3/Gui/RSXDebugger.h b/rpcs3/Gui/RSXDebugger.h index 7ba49b8deb..82f22ec05e 100644 --- a/rpcs3/Gui/RSXDebugger.h +++ b/rpcs3/Gui/RSXDebugger.h @@ -15,6 +15,8 @@ class RSXDebugger : public wxFrame u32 m_item_count; wxListView* m_list_commands; + wxListView* m_list_captured_frame; + wxListView* m_list_captured_draw_calls; wxListView* m_list_flags; wxListView* m_list_programs; wxListView* m_list_lightning; @@ -43,6 +45,7 @@ public: virtual void OnChangeToolsAddr(wxCommandEvent& event); virtual void OnScrollMemory(wxMouseEvent& event); virtual void OnClickBuffer(wxMouseEvent& event); + virtual void OnClickDrawCalls(wxMouseEvent &event); virtual void GoToGet(wxCommandEvent& event); virtual void GoToPut(wxCommandEvent& event);