[GPU] Display swap output in the trace viewer
Resolve output is unreliable because resolving may be done to a subregion of a texture and even to 3D textures, and to any color format
This commit is contained in:
parent
28670d8ec2
commit
e37e3ef382
|
@ -683,6 +683,7 @@ EmulatorWindow::GetGuestOutputPaintEffectForCvarValue(
|
||||||
ui::Presenter::GuestOutputPaintConfig
|
ui::Presenter::GuestOutputPaintConfig
|
||||||
EmulatorWindow::GetGuestOutputPaintConfigForCvars() {
|
EmulatorWindow::GetGuestOutputPaintConfigForCvars() {
|
||||||
ui::Presenter::GuestOutputPaintConfig paint_config;
|
ui::Presenter::GuestOutputPaintConfig paint_config;
|
||||||
|
paint_config.SetAllowOverscanCutoff(true);
|
||||||
paint_config.SetEffect(GetGuestOutputPaintEffectForCvarValue(
|
paint_config.SetEffect(GetGuestOutputPaintEffectForCvarValue(
|
||||||
cvars::postprocess_scaling_and_sharpening));
|
cvars::postprocess_scaling_and_sharpening));
|
||||||
paint_config.SetCasAdditionalSharpness(
|
paint_config.SetCasAdditionalSharpness(
|
||||||
|
|
|
@ -916,9 +916,7 @@ bool CommandProcessor::ExecutePacketType3_XE_SWAP(RingBuffer* reader,
|
||||||
uint32_t frontbuffer_height = reader->ReadAndSwap<uint32_t>();
|
uint32_t frontbuffer_height = reader->ReadAndSwap<uint32_t>();
|
||||||
reader->AdvanceRead((count - 4) * sizeof(uint32_t));
|
reader->AdvanceRead((count - 4) * sizeof(uint32_t));
|
||||||
|
|
||||||
if (!ignore_swap_) {
|
IssueSwap(frontbuffer_ptr, frontbuffer_width, frontbuffer_height);
|
||||||
IssueSwap(frontbuffer_ptr, frontbuffer_width, frontbuffer_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
++counter_;
|
++counter_;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -90,7 +90,6 @@ class CommandProcessor {
|
||||||
|
|
||||||
virtual void ClearCaches();
|
virtual void ClearCaches();
|
||||||
|
|
||||||
void SetIgnoreSwap(bool ignore_swap) { ignore_swap_ = ignore_swap; }
|
|
||||||
// "Desired" is for the external thread managing the post-processing effect.
|
// "Desired" is for the external thread managing the post-processing effect.
|
||||||
SwapPostEffect GetDesiredSwapPostEffect() const {
|
SwapPostEffect GetDesiredSwapPostEffect() const {
|
||||||
return swap_post_effect_desired_;
|
return swap_post_effect_desired_;
|
||||||
|
@ -265,8 +264,6 @@ class CommandProcessor {
|
||||||
std::atomic<bool> worker_running_;
|
std::atomic<bool> worker_running_;
|
||||||
kernel::object_ref<kernel::XHostThread> worker_thread_;
|
kernel::object_ref<kernel::XHostThread> worker_thread_;
|
||||||
|
|
||||||
bool ignore_swap_ = false;
|
|
||||||
|
|
||||||
std::queue<std::function<void()>> pending_fns_;
|
std::queue<std::function<void()>> pending_fns_;
|
||||||
|
|
||||||
// MicroEngine binary from PM4_ME_INIT
|
// MicroEngine binary from PM4_ME_INIT
|
||||||
|
|
|
@ -1718,10 +1718,6 @@ void D3D12CommandProcessor::OnGammaRampPWLValueWritten() {
|
||||||
void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
|
void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
|
||||||
uint32_t frontbuffer_width,
|
uint32_t frontbuffer_width,
|
||||||
uint32_t frontbuffer_height) {
|
uint32_t frontbuffer_height) {
|
||||||
// FIXME(Triang3l): frontbuffer_ptr is currently unreliable, in the trace
|
|
||||||
// player it's set to 0, but it's not needed anyway since the fetch constant
|
|
||||||
// contains the address.
|
|
||||||
|
|
||||||
SCOPE_profile_cpu_f("gpu");
|
SCOPE_profile_cpu_f("gpu");
|
||||||
|
|
||||||
ui::Presenter* presenter = graphics_system_->presenter();
|
ui::Presenter* presenter = graphics_system_->presenter();
|
||||||
|
|
|
@ -89,19 +89,15 @@ void TracePlayer::PlayTrace(const uint8_t* trace_data, size_t trace_size,
|
||||||
TracePlaybackMode playback_mode,
|
TracePlaybackMode playback_mode,
|
||||||
bool clear_caches) {
|
bool clear_caches) {
|
||||||
playing_trace_ = true;
|
playing_trace_ = true;
|
||||||
// Pass a copy of present_last_copy_ to the thread so it's not accessible by
|
|
||||||
// multiple threads at once.
|
|
||||||
bool present_last_copy = present_last_copy_;
|
|
||||||
graphics_system_->command_processor()->CallInThread([=]() {
|
graphics_system_->command_processor()->CallInThread([=]() {
|
||||||
PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches,
|
PlayTraceOnThread(trace_data, trace_size, playback_mode, clear_caches);
|
||||||
present_last_copy);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
|
void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
|
||||||
size_t trace_size,
|
size_t trace_size,
|
||||||
TracePlaybackMode playback_mode,
|
TracePlaybackMode playback_mode,
|
||||||
bool clear_caches, bool present_last_copy) {
|
bool clear_caches) {
|
||||||
auto memory = graphics_system_->memory();
|
auto memory = graphics_system_->memory();
|
||||||
auto command_processor = graphics_system_->command_processor();
|
auto command_processor = graphics_system_->command_processor();
|
||||||
|
|
||||||
|
@ -109,10 +105,6 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
|
||||||
command_processor->ClearCaches();
|
command_processor->ClearCaches();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (present_last_copy) {
|
|
||||||
command_processor->SetIgnoreSwap(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
playback_percent_ = 0;
|
playback_percent_ = 0;
|
||||||
auto trace_end = trace_data + trace_size;
|
auto trace_end = trace_data + trace_size;
|
||||||
|
|
||||||
|
@ -252,11 +244,6 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
|
||||||
|
|
||||||
playing_trace_ = false;
|
playing_trace_ = false;
|
||||||
|
|
||||||
if (present_last_copy) {
|
|
||||||
command_processor->SetIgnoreSwap(false);
|
|
||||||
command_processor->IssueSwap(0, 1280, 720);
|
|
||||||
}
|
|
||||||
|
|
||||||
playback_event_->Set();
|
playback_event_->Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,6 @@ class TracePlayer : public TraceReader {
|
||||||
TracePlayer(GraphicsSystem* graphics_system);
|
TracePlayer(GraphicsSystem* graphics_system);
|
||||||
|
|
||||||
GraphicsSystem* graphics_system() const { return graphics_system_; }
|
GraphicsSystem* graphics_system() const { return graphics_system_; }
|
||||||
void SetPresentLastCopy(bool present_last_copy) {
|
|
||||||
present_last_copy_ = present_last_copy;
|
|
||||||
}
|
|
||||||
int current_frame_index() const { return current_frame_index_; }
|
int current_frame_index() const { return current_frame_index_; }
|
||||||
int current_command_index() const { return current_command_index_; }
|
int current_command_index() const { return current_command_index_; }
|
||||||
bool is_playing_trace() const { return playing_trace_; }
|
bool is_playing_trace() const { return playing_trace_; }
|
||||||
|
@ -53,13 +50,9 @@ class TracePlayer : public TraceReader {
|
||||||
void PlayTrace(const uint8_t* trace_data, size_t trace_size,
|
void PlayTrace(const uint8_t* trace_data, size_t trace_size,
|
||||||
TracePlaybackMode playback_mode, bool clear_caches);
|
TracePlaybackMode playback_mode, bool clear_caches);
|
||||||
void PlayTraceOnThread(const uint8_t* trace_data, size_t trace_size,
|
void PlayTraceOnThread(const uint8_t* trace_data, size_t trace_size,
|
||||||
TracePlaybackMode playback_mode, bool clear_caches,
|
TracePlaybackMode playback_mode, bool clear_caches);
|
||||||
bool present_last_copy);
|
|
||||||
|
|
||||||
GraphicsSystem* graphics_system_;
|
GraphicsSystem* graphics_system_;
|
||||||
// Whether to present the results of the latest resolve instead of displaying
|
|
||||||
// the front buffer from the trace.
|
|
||||||
bool present_last_copy_ = false;
|
|
||||||
int current_frame_index_;
|
int current_frame_index_;
|
||||||
int current_command_index_;
|
int current_command_index_;
|
||||||
bool playing_trace_ = false;
|
bool playing_trace_ = false;
|
||||||
|
|
|
@ -114,7 +114,7 @@ bool TraceViewer::Setup() {
|
||||||
// Main display window.
|
// Main display window.
|
||||||
assert_true(app_context().IsInUIThread());
|
assert_true(app_context().IsInUIThread());
|
||||||
window_ = xe::ui::Window::Create(app_context(), "xenia-gpu-trace-viewer",
|
window_ = xe::ui::Window::Create(app_context(), "xenia-gpu-trace-viewer",
|
||||||
1920, 1200);
|
1920, 1080);
|
||||||
window_->AddListener(&window_listener_);
|
window_->AddListener(&window_listener_);
|
||||||
window_->AddInputListener(&window_listener_, kZOrderTraceViewerInput);
|
window_->AddInputListener(&window_listener_, kZOrderTraceViewerInput);
|
||||||
if (!window_->Open()) {
|
if (!window_->Open()) {
|
||||||
|
@ -135,28 +135,27 @@ bool TraceViewer::Setup() {
|
||||||
graphics_system_ = emulator_->graphics_system();
|
graphics_system_ = emulator_->graphics_system();
|
||||||
|
|
||||||
player_ = std::make_unique<TracePlayer>(graphics_system_);
|
player_ = std::make_unique<TracePlayer>(graphics_system_);
|
||||||
player_->SetPresentLastCopy(true);
|
|
||||||
|
|
||||||
// Setup drawing to the window.
|
// Setup drawing to the window.
|
||||||
xe::ui::GraphicsProvider& graphics_provider = *graphics_system_->provider();
|
ui::Presenter* presenter = graphics_system_->presenter();
|
||||||
presenter_ = graphics_provider.CreatePresenter();
|
if (!presenter) {
|
||||||
if (!presenter_) {
|
|
||||||
XELOGE("Failed to initialize the presenter");
|
XELOGE("Failed to initialize the presenter");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
xe::ui::GraphicsProvider& graphics_provider = *graphics_system_->provider();
|
||||||
immediate_drawer_ = graphics_provider.CreateImmediateDrawer();
|
immediate_drawer_ = graphics_provider.CreateImmediateDrawer();
|
||||||
if (!immediate_drawer_) {
|
if (!immediate_drawer_) {
|
||||||
XELOGE("Failed to initialize the immediate drawer");
|
XELOGE("Failed to initialize the immediate drawer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
immediate_drawer_->SetPresenter(presenter_.get());
|
immediate_drawer_->SetPresenter(presenter);
|
||||||
imgui_drawer_ =
|
imgui_drawer_ =
|
||||||
std::make_unique<xe::ui::ImGuiDrawer>(window_.get(), kZOrderImGui);
|
std::make_unique<xe::ui::ImGuiDrawer>(window_.get(), kZOrderImGui);
|
||||||
imgui_drawer_->SetPresenterAndImmediateDrawer(presenter_.get(),
|
imgui_drawer_->SetPresenterAndImmediateDrawer(presenter,
|
||||||
immediate_drawer_.get());
|
immediate_drawer_.get());
|
||||||
trace_viewer_dialog_ = std::unique_ptr<TraceViewerDialog>(
|
trace_viewer_dialog_ = std::unique_ptr<TraceViewerDialog>(
|
||||||
new TraceViewerDialog(imgui_drawer_.get(), *this));
|
new TraceViewerDialog(imgui_drawer_.get(), *this));
|
||||||
window_->SetPresenter(presenter_.get());
|
window_->SetPresenter(presenter);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "xenia/ui/imgui_dialog.h"
|
#include "xenia/ui/imgui_dialog.h"
|
||||||
#include "xenia/ui/imgui_drawer.h"
|
#include "xenia/ui/imgui_drawer.h"
|
||||||
#include "xenia/ui/immediate_drawer.h"
|
#include "xenia/ui/immediate_drawer.h"
|
||||||
#include "xenia/ui/presenter.h"
|
|
||||||
#include "xenia/ui/window.h"
|
#include "xenia/ui/window.h"
|
||||||
#include "xenia/ui/window_listener.h"
|
#include "xenia/ui/window_listener.h"
|
||||||
#include "xenia/ui/windowed_app.h"
|
#include "xenia/ui/windowed_app.h"
|
||||||
|
@ -129,7 +128,6 @@ class TraceViewer : public xe::ui::WindowedApp {
|
||||||
GraphicsSystem* graphics_system_ = nullptr;
|
GraphicsSystem* graphics_system_ = nullptr;
|
||||||
std::unique_ptr<TracePlayer> player_;
|
std::unique_ptr<TracePlayer> player_;
|
||||||
|
|
||||||
std::unique_ptr<xe::ui::Presenter> presenter_;
|
|
||||||
std::unique_ptr<xe::ui::ImmediateDrawer> immediate_drawer_;
|
std::unique_ptr<xe::ui::ImmediateDrawer> immediate_drawer_;
|
||||||
std::unique_ptr<xe::ui::ImGuiDrawer> imgui_drawer_;
|
std::unique_ptr<xe::ui::ImGuiDrawer> imgui_drawer_;
|
||||||
std::unique_ptr<TraceViewerDialog> trace_viewer_dialog_;
|
std::unique_ptr<TraceViewerDialog> trace_viewer_dialog_;
|
||||||
|
|
|
@ -269,11 +269,6 @@ void VulkanCommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!frontbuffer_ptr) {
|
|
||||||
// Trace viewer does this.
|
|
||||||
frontbuffer_ptr = last_copy_base_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<VkCommandBuffer> submit_buffers;
|
std::vector<VkCommandBuffer> submit_buffers;
|
||||||
if (frame_open_) {
|
if (frame_open_) {
|
||||||
// TODO(DrChat): If the setup buffer is empty, don't bother queueing it up.
|
// TODO(DrChat): If the setup buffer is empty, don't bother queueing it up.
|
||||||
|
@ -1108,9 +1103,6 @@ bool VulkanCommandProcessor::IssueCopy() {
|
||||||
|
|
||||||
texture->in_flight_fence = current_batch_fence_;
|
texture->in_flight_fence = current_batch_fence_;
|
||||||
|
|
||||||
// For debugging purposes only (trace viewer)
|
|
||||||
last_copy_base_ = texture->texture_info.memory.base_address;
|
|
||||||
|
|
||||||
if (!frame_open_) {
|
if (!frame_open_) {
|
||||||
BeginFrame();
|
BeginFrame();
|
||||||
} else if (current_render_state_) {
|
} else if (current_render_state_) {
|
||||||
|
|
|
@ -106,9 +106,6 @@ class VulkanCommandProcessor : public CommandProcessor {
|
||||||
uint32_t coher_base_vc_ = 0;
|
uint32_t coher_base_vc_ = 0;
|
||||||
uint32_t coher_size_vc_ = 0;
|
uint32_t coher_size_vc_ = 0;
|
||||||
|
|
||||||
// Last copy base address, for debugging only.
|
|
||||||
uint32_t last_copy_base_ = 0;
|
|
||||||
|
|
||||||
bool capturing_ = false;
|
bool capturing_ = false;
|
||||||
bool trace_requested_ = false;
|
bool trace_requested_ = false;
|
||||||
bool cache_clear_requested_ = false;
|
bool cache_clear_requested_ = false;
|
||||||
|
|
|
@ -714,7 +714,8 @@ Presenter::GuestOutputPaintFlow Presenter::GetGuestOutputPaintFlow(
|
||||||
// ratio while stretching throughout the entire surface's width, then limit
|
// ratio while stretching throughout the entire surface's width, then limit
|
||||||
// the Y cropping via letterboxing or stretching along X.
|
// the Y cropping via letterboxing or stretching along X.
|
||||||
uint32_t present_safe_area;
|
uint32_t present_safe_area;
|
||||||
if (cvars::present_safe_area_y > 0 && cvars::present_safe_area_y < 100) {
|
if (config.GetAllowOverscanCutoff() && cvars::present_safe_area_y > 0 &&
|
||||||
|
cvars::present_safe_area_y < 100) {
|
||||||
present_safe_area = uint32_t(cvars::present_safe_area_y);
|
present_safe_area = uint32_t(cvars::present_safe_area_y);
|
||||||
} else {
|
} else {
|
||||||
present_safe_area = 100;
|
present_safe_area = 100;
|
||||||
|
@ -756,7 +757,8 @@ Presenter::GuestOutputPaintFlow Presenter::GetGuestOutputPaintFlow(
|
||||||
// aspect ratio while stretching throughout the entire surface's height,
|
// aspect ratio while stretching throughout the entire surface's height,
|
||||||
// then limit the X cropping via letterboxing or stretching along Y.
|
// then limit the X cropping via letterboxing or stretching along Y.
|
||||||
uint32_t present_safe_area;
|
uint32_t present_safe_area;
|
||||||
if (cvars::present_safe_area_x > 0 && cvars::present_safe_area_x < 100) {
|
if (config.GetAllowOverscanCutoff() && cvars::present_safe_area_x > 0 &&
|
||||||
|
cvars::present_safe_area_x < 100) {
|
||||||
present_safe_area = uint32_t(cvars::present_safe_area_x);
|
present_safe_area = uint32_t(cvars::present_safe_area_x);
|
||||||
} else {
|
} else {
|
||||||
present_safe_area = 100;
|
present_safe_area = 100;
|
||||||
|
|
|
@ -228,6 +228,11 @@ class Presenter {
|
||||||
// In the sharpness setters, min / max with a constant as the first argument
|
// In the sharpness setters, min / max with a constant as the first argument
|
||||||
// also drops NaNs.
|
// also drops NaNs.
|
||||||
|
|
||||||
|
bool GetAllowOverscanCutoff() const { return allow_overscan_cutoff_; }
|
||||||
|
void SetAllowOverscanCutoff(bool new_allow_overscan_cutoff) {
|
||||||
|
allow_overscan_cutoff_ = new_allow_overscan_cutoff;
|
||||||
|
}
|
||||||
|
|
||||||
Effect GetEffect() const { return effect_; }
|
Effect GetEffect() const { return effect_; }
|
||||||
void SetEffect(Effect new_effect) { effect_ = new_effect; }
|
void SetEffect(Effect new_effect) { effect_ = new_effect; }
|
||||||
|
|
||||||
|
@ -265,8 +270,10 @@ class Presenter {
|
||||||
void SetDither(bool new_dither) { dither_ = new_dither; }
|
void SetDither(bool new_dither) { dither_ = new_dither; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Tools, rather than the emulator itself, must use kBilinear as the image
|
// Tools, rather than the emulator itself, must not allow overscan cutoff
|
||||||
// must be as close to the original front buffer as possible.
|
// and must use the kBilinear effect as the image must be as close to the
|
||||||
|
// original front buffer as possible.
|
||||||
|
bool allow_overscan_cutoff_ = false;
|
||||||
Effect effect_ = Effect::kBilinear;
|
Effect effect_ = Effect::kBilinear;
|
||||||
float cas_additional_sharpness_ = kCasAdditionalSharpnessDefault;
|
float cas_additional_sharpness_ = kCasAdditionalSharpnessDefault;
|
||||||
uint32_t fsr_max_upsampling_passes_ = kFsrMaxUpscalingPassesMax;
|
uint32_t fsr_max_upsampling_passes_ = kFsrMaxUpscalingPassesMax;
|
||||||
|
|
Loading…
Reference in New Issue