diff --git a/src/xenia/gpu/trace_player.cc b/src/xenia/gpu/trace_player.cc index 40291453f..3e762f451 100644 --- a/src/xenia/gpu/trace_player.cc +++ b/src/xenia/gpu/trace_player.cc @@ -64,9 +64,9 @@ void TracePlayer::SeekCommand(int target_command) { auto frame = current_frame(); const auto& command = frame->commands[target_command]; assert_true(frame->start_ptr <= command.end_ptr); - if (target_command && previous_command_index == target_command - 1) { + if (previous_command_index != -1 && target_command > previous_command_index) { // Seek forward. - const auto& previous_command = frame->commands[target_command - 1]; + const auto& previous_command = frame->commands[previous_command_index]; PlayTrace(previous_command.end_ptr, command.end_ptr - previous_command.end_ptr, TracePlaybackMode::kBreakOnSwap); @@ -92,11 +92,18 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data, auto command_processor = graphics_system_->command_processor(); command_processor->set_swap_mode(SwapMode::kIgnored); + playback_percent_ = 0; + auto trace_end = trace_data + trace_size; + playing_trace_ = true; auto trace_ptr = trace_data; bool pending_break = false; const PacketStartCommand* pending_packet = nullptr; while (trace_ptr < trace_data + trace_size) { + playback_percent_ = uint32_t( + (float(trace_ptr - trace_data) / float(trace_end - trace_data)) * + 10000); + auto type = static_cast(xe::load(trace_ptr)); switch (type) { case TraceCommandType::kPrimaryBufferStart: { @@ -184,6 +191,7 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data, } } + playing_trace_ = false; command_processor->set_swap_mode(SwapMode::kNormal); command_processor->IssueSwap(0, 1280, 720); } diff --git a/src/xenia/gpu/trace_player.h b/src/xenia/gpu/trace_player.h index 6e78ac3a6..70678ec1c 100644 --- a/src/xenia/gpu/trace_player.h +++ b/src/xenia/gpu/trace_player.h @@ -10,6 +10,7 @@ #ifndef XENIA_GPU_TRACE_PLAYER_H_ #define XENIA_GPU_TRACE_PLAYER_H_ +#include #include #include "xenia/gpu/trace_protocol.h" @@ -34,8 +35,13 @@ class TracePlayer : public TraceReader { GraphicsSystem* graphics_system() const { return graphics_system_; } int current_frame_index() const { return current_frame_index_; } int current_command_index() const { return current_command_index_; } + bool playing_trace() const { return playing_trace_; } const Frame* current_frame() const; + // Only valid if playing_trace is true. + // Scalar from 0-10000 + uint32_t playback_percent() const { return playback_percent_; } + void SeekFrame(int target_frame); void SeekCommand(int target_command); @@ -49,6 +55,8 @@ class TracePlayer : public TraceReader { GraphicsSystem* graphics_system_; int current_frame_index_; int current_command_index_; + bool playing_trace_ = false; + std::atomic playback_percent_ = 0; }; } // namespace gpu diff --git a/src/xenia/gpu/trace_viewer.cc b/src/xenia/gpu/trace_viewer.cc index 53c750428..14513f989 100644 --- a/src/xenia/gpu/trace_viewer.cc +++ b/src/xenia/gpu/trace_viewer.cc @@ -192,9 +192,16 @@ void TraceViewer::DrawControllerUI() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Skip to last frame"); } + if (player_->playing_trace()) { + // Don't allow the user to change the frame index just yet... + // TODO: Find a way to disable the slider below. + target_frame = player_->current_frame_index(); + } + ImGui::SameLine(); ImGui::SliderInt("", &target_frame, 0, player_->frame_count() - 1); - if (target_frame != player_->current_frame_index()) { + if (target_frame != player_->current_frame_index() && + !player_->playing_trace()) { player_->SeekFrame(target_frame); } ImGui::End(); @@ -398,10 +405,18 @@ void TraceViewer::DrawCommandListUI() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Move to the last command"); } + if (player_->playing_trace()) { + // Don't allow the user to change the command index just yet... + // TODO: Find a way to disable the slider below. + target_command = player_->current_command_index(); + } + ImGui::PushItemWidth(float(column_width - 15)); ImGui::SliderInt("", &target_command, -1, command_count - 1); ImGui::PopItemWidth(); - if (target_command != player_->current_command_index()) { + + if (target_command != player_->current_command_index() && + !player_->playing_trace()) { did_seek = true; player_->SeekCommand(target_command); } @@ -583,6 +598,7 @@ void TraceViewer::DrawTextureInfo(const Shader::SamplerDesc& desc) { } ImGui::NextColumn(); ImGui::Text("Fetch Slot: %d", desc.fetch_slot); + ImGui::Text("Guest Address: %.8X", texture_info.guest_address); switch (texture_info.dimension) { case Dimension::k1D: ImGui::Text("1D: %dpx", texture_info.width + 1); @@ -599,6 +615,14 @@ void TraceViewer::DrawTextureInfo(const Shader::SamplerDesc& desc) { ImGui::Text("Cube: ?"); break; } + static const char* swizzle_map[] = {"Red", "Green", "Blue", "Alpha", + "Zero", "One", "UNK6", "UNK7"}; + ImGui::Text("Swizzle: %s %s %s %s", + swizzle_map[(texture_info.swizzle >> 0) & 0x7], + swizzle_map[(texture_info.swizzle >> 3) & 0x7], + swizzle_map[(texture_info.swizzle >> 6) & 0x7], + swizzle_map[(texture_info.swizzle >> 9) & 0x7]); + ImGui::Columns(1); } @@ -820,6 +844,34 @@ static const char* kEndiannessNames[] = { "unspecified endianness", "8-in-16", "8-in-32", "16-in-32", }; +void ProgressBar(float frac, float width, float height = 0, + const ImVec4& color = ImVec4(0, 1, 0, 1), + const ImVec4& border_color = ImVec4(0, 1, 0, 1)) { + if (height == 0) { + height = ImGui::GetTextLineHeightWithSpacing(); + } + frac = xe::saturate(frac); + + const auto fontAtlas = ImGui::GetIO().Fonts; + + auto pos = ImGui::GetCursorScreenPos(); + auto col = ImGui::ColorConvertFloat4ToU32(color); + auto border_col = ImGui::ColorConvertFloat4ToU32(border_color); + + if (frac > 0) { + // Progress bar + ImGui::GetWindowDrawList()->AddRectFilled( + pos, ImVec2(pos.x + width * frac, pos.y + height), col); + } + if (border_color.w > 0.f) { + // Border + ImGui::GetWindowDrawList()->AddRect( + pos, ImVec2(pos.x + width, pos.y + height), border_col); + } + + ImGui::Dummy(ImVec2(width, height)); +} + void TraceViewer::DrawStateUI() { auto command_processor = graphics_system_->command_processor(); auto& regs = *graphics_system_->register_file(); @@ -893,8 +945,18 @@ void TraceViewer::DrawStateUI() { } } + if (player_->playing_trace()) { + ImGui::Text("Playing trace..."); + float width = ImGui::GetWindowWidth() - 20.f; + + ProgressBar(float(player_->playback_percent()) / 10000.f, width); + ImGui::End(); + return; + } + auto enable_mode = static_cast(regs[XE_GPU_REG_RB_MODECONTROL].u32 & 0x7); + const char* mode_name = "Unknown"; switch (enable_mode) { case ModeControl::kIgnore: @@ -915,9 +977,12 @@ void TraceViewer::DrawStateUI() { kPrimNames[int(draw_info.prim_type)], draw_info.index_count); break; } - case ModeControl::kCopy: - ImGui::Text("Copy Command %d", player_->current_command_index()); + case ModeControl::kCopy: { + uint32_t copy_dest_base = regs[XE_GPU_REG_RB_COPY_DEST_BASE].u32; + ImGui::Text("Copy Command %d (to %.8X)", player_->current_command_index(), + copy_dest_base); break; + } } ImGui::Columns(2); @@ -1408,7 +1473,6 @@ void TraceViewer::DrawStateUI() { } if (ImGui::CollapsingHeader("Fetch Constants (raw)")) { ImGui::Columns(2); - ImGui::SetColumnOffset(1, 85.0f); for (int i = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0; i <= XE_GPU_REG_SHADER_CONSTANT_FETCH_31_5; ++i) { ImGui::Text("f%02d_%d", (i - XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0) / 6,