diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index 6d1391513..1f780845e 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -104,6 +104,8 @@ #endif #endif +#include "xenia/apu/audio_system.h" +#include "xenia/cpu/backend/backend.h" #include "xenia/cpu/processor.h" #include "xenia/emulator.h" #include "xenia/gpu/command_processor.h" @@ -1960,16 +1962,44 @@ void EmulatorWindow::UpdateTitle() { } } - // Graphics system name, if available + std::string graphics_name; + std::string graphics_suffix; auto graphics_system = emulator()->graphics_system(); if (graphics_system) { - auto graphics_name = graphics_system->name(); - if (!graphics_name.empty()) { - sb.Append(" <"); - sb.Append(graphics_name); - sb.Append(">"); + graphics_name = graphics_system->name(); + auto command_processor = graphics_system->command_processor(); + if (command_processor) { + graphics_suffix = command_processor->GetTitleStateSuffix(); } } + std::string audio_name; + auto audio_system = emulator()->audio_system(); + if (audio_system) { + audio_name = audio_system->name(); + } + std::string cpu_name; + auto processor = emulator()->processor(); + if (processor && processor->backend()) { + cpu_name = processor->backend()->name(); + } + if (!graphics_name.empty() || !cpu_name.empty()) { + sb.Append(" <"); + if (!graphics_name.empty()) { + sb.Append(graphics_name); + sb.Append(graphics_suffix); + if (!audio_name.empty()) { + sb.Append(" - "); + sb.Append(audio_name); + } + } + if (!cpu_name.empty()) { + if (!graphics_name.empty()) { + sb.Append(" - "); + } + sb.Append(cpu_name); + } + sb.Append(">"); + } if (Clock::guest_time_scalar() != 1.0) { sb.AppendFormat(" (@{:.2f}x)", Clock::guest_time_scalar()); diff --git a/src/xenia/cpu/backend/a64/a64_backend.h b/src/xenia/cpu/backend/a64/a64_backend.h index 3e57430f5..763839fd9 100644 --- a/src/xenia/cpu/backend/a64/a64_backend.h +++ b/src/xenia/cpu/backend/a64/a64_backend.h @@ -101,6 +101,8 @@ class A64Backend : public Backend { A64CodeCache* code_cache() const { return code_cache_.get(); } uintptr_t emitter_data() const { return emitter_data_; } + std::string name() const override { return "a64"; } + HostToGuestThunk host_to_guest_thunk() const { return host_to_guest_thunk_; } GuestToHostThunk guest_to_host_thunk() const { return guest_to_host_thunk_; } ResolveFunctionThunk resolve_function_thunk() const { diff --git a/src/xenia/cpu/backend/backend.h b/src/xenia/cpu/backend/backend.h index 8e552ab56..dd0716762 100644 --- a/src/xenia/cpu/backend/backend.h +++ b/src/xenia/cpu/backend/backend.h @@ -11,6 +11,7 @@ #define XENIA_CPU_BACKEND_BACKEND_H_ #include +#include #include "xenia/cpu/backend/machine_info.h" #include "xenia/cpu/thread_debug_info.h" @@ -50,6 +51,8 @@ class Backend { const MachineInfo* machine_info() const { return &machine_info_; } CodeCache* code_cache() const { return code_cache_; } + virtual std::string name() const { return "unknown"; } + virtual bool Initialize(Processor* processor); virtual void* AllocThreadData(); diff --git a/src/xenia/cpu/backend/null_backend.h b/src/xenia/cpu/backend/null_backend.h index 957132269..e2a9d22a8 100644 --- a/src/xenia/cpu/backend/null_backend.h +++ b/src/xenia/cpu/backend/null_backend.h @@ -18,6 +18,8 @@ namespace backend { class NullBackend : public Backend { public: + std::string name() const override { return "null"; } + void CommitExecutableRange(uint32_t guest_low, uint32_t guest_high) override; std::unique_ptr CreateAssembler() override; diff --git a/src/xenia/cpu/backend/x64/x64_backend.h b/src/xenia/cpu/backend/x64/x64_backend.h index d8c18f7a0..1a3b0775f 100644 --- a/src/xenia/cpu/backend/x64/x64_backend.h +++ b/src/xenia/cpu/backend/x64/x64_backend.h @@ -128,6 +128,8 @@ class X64Backend : public Backend { X64CodeCache* code_cache() const { return code_cache_.get(); } uintptr_t emitter_data() const { return emitter_data_; } + std::string name() const override { return "x64"; } + // Call a generated function, saving all stack parameters. HostToGuestThunk host_to_guest_thunk() const { return host_to_guest_thunk_; } // Function that guest code can call to transition into host code. diff --git a/src/xenia/gpu/command_processor.h b/src/xenia/gpu/command_processor.h index 90c944049..12e47c70a 100644 --- a/src/xenia/gpu/command_processor.h +++ b/src/xenia/gpu/command_processor.h @@ -144,6 +144,8 @@ class CommandProcessor { virtual bool Initialize(); virtual void Shutdown(); + virtual std::string GetTitleStateSuffix() const { return {}; } + void CallInThread(std::function fn); virtual void ClearCaches(); diff --git a/src/xenia/gpu/d3d12/d3d12_command_processor.cc b/src/xenia/gpu/d3d12/d3d12_command_processor.cc index 02c9f14c4..338b7332d 100644 --- a/src/xenia/gpu/d3d12/d3d12_command_processor.cc +++ b/src/xenia/gpu/d3d12/d3d12_command_processor.cc @@ -12,14 +12,12 @@ #include #include -#include "xenia/apu/audio_system.h" #include "xenia/base/assert.h" #include "xenia/base/byte_order.h" #include "xenia/base/cvar.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" #include "xenia/base/profiling.h" -#include "xenia/emulator.h" #include "xenia/gpu/d3d12/d3d12_graphics_system.h" #include "xenia/gpu/d3d12/d3d12_shader.h" #include "xenia/gpu/d3d12/d3d12_zpd_query_pool.h" @@ -798,40 +796,36 @@ void D3D12CommandProcessor::SetPrimitiveTopology( } } -std::string D3D12CommandProcessor::GetWindowTitleText() const { - std::ostringstream title; - title << "Direct3D 12"; - if (render_target_cache_) { - // Rasterizer-ordered views are a feature very rarely used as of 2020 and - // that faces adoption complications (outside of Direct3D - on Vulkan - at - // least), but crucial to Xenia - raise awareness of its usage. - // https://github.com/KhronosGroup/Vulkan-Ecosystem/issues/27#issuecomment-455712319 - // "In Xenia's title bar "D3D12 ROV" can be seen, which was a surprise, as I - // wasn't aware that Xenia D3D12 backend was using Raster Order Views - // feature" - oscarbg in that issue. - switch (render_target_cache_->GetPath()) { - case RenderTargetCache::Path::kHostRenderTargets: - title << " - RTV/DSV"; - break; - case RenderTargetCache::Path::kPixelShaderInterlock: - title << " - ROV"; - break; - default: - break; - } - uint32_t draw_resolution_scale_x = - texture_cache_ ? texture_cache_->draw_resolution_scale_x() : 1; - uint32_t draw_resolution_scale_y = - texture_cache_ ? texture_cache_->draw_resolution_scale_y() : 1; - if (draw_resolution_scale_x > 1 || draw_resolution_scale_y > 1) { - title << ' ' << draw_resolution_scale_x << 'x' << draw_resolution_scale_y; - } +std::string D3D12CommandProcessor::GetTitleStateSuffix() const { + if (!render_target_cache_) { + return {}; } - auto* audio_system = kernel_state_->emulator()->audio_system(); - if (audio_system) { - title << " - " << audio_system->name(); + std::ostringstream suffix; + // Rasterizer-ordered views are a feature very rarely used as of 2020 and + // that faces adoption complications (outside of Direct3D - on Vulkan - at + // least), but crucial to Xenia - raise awareness of its usage. + // https://github.com/KhronosGroup/Vulkan-Ecosystem/issues/27#issuecomment-455712319 + // "In Xenia's title bar "D3D12 ROV" can be seen, which was a surprise, as I + // wasn't aware that Xenia D3D12 backend was using Raster Order Views + // feature" - oscarbg in that issue. + switch (render_target_cache_->GetPath()) { + case RenderTargetCache::Path::kHostRenderTargets: + suffix << " - RTV/DSV"; + break; + case RenderTargetCache::Path::kPixelShaderInterlock: + suffix << " - ROV"; + break; + default: + break; } - return title.str(); + uint32_t draw_resolution_scale_x = + texture_cache_ ? texture_cache_->draw_resolution_scale_x() : 1; + uint32_t draw_resolution_scale_y = + texture_cache_ ? texture_cache_->draw_resolution_scale_y() : 1; + if (draw_resolution_scale_x > 1 || draw_resolution_scale_y > 1) { + suffix << ' ' << draw_resolution_scale_x << 'x' << draw_resolution_scale_y; + } + return suffix.str(); } bool D3D12CommandProcessor::SetupContext() { diff --git a/src/xenia/gpu/d3d12/d3d12_command_processor.h b/src/xenia/gpu/d3d12/d3d12_command_processor.h index d4cd99a87..c4760f578 100644 --- a/src/xenia/gpu/d3d12/d3d12_command_processor.h +++ b/src/xenia/gpu/d3d12/d3d12_command_processor.h @@ -229,8 +229,7 @@ class D3D12CommandProcessor final : public CommandProcessor { void SetStencilReference(uint32_t stencil_ref); void SetPrimitiveTopology(D3D12_PRIMITIVE_TOPOLOGY primitive_topology); - // Returns the text to display in the GPU backend name in the window title. - std::string GetWindowTitleText() const; + std::string GetTitleStateSuffix() const override; protected: bool SetupContext() override; diff --git a/src/xenia/gpu/d3d12/d3d12_graphics_system.cc b/src/xenia/gpu/d3d12/d3d12_graphics_system.cc index 3e851ef26..98c9f5c80 100644 --- a/src/xenia/gpu/d3d12/d3d12_graphics_system.cc +++ b/src/xenia/gpu/d3d12/d3d12_graphics_system.cc @@ -28,14 +28,7 @@ bool D3D12GraphicsSystem::IsAvailable() { return xe::ui::d3d12::D3D12Provider::IsD3D12APIAvailable(); } -std::string D3D12GraphicsSystem::name() const { - auto d3d12_command_processor = - static_cast(command_processor()); - if (d3d12_command_processor != nullptr) { - return d3d12_command_processor->GetWindowTitleText(); - } - return "Direct3D 12"; -} +std::string D3D12GraphicsSystem::name() const { return "D3D12"; } X_STATUS D3D12GraphicsSystem::Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, diff --git a/src/xenia/gpu/metal/metal_command_processor.cc b/src/xenia/gpu/metal/metal_command_processor.cc index 4ba72c609..982f35f36 100644 --- a/src/xenia/gpu/metal/metal_command_processor.cc +++ b/src/xenia/gpu/metal/metal_command_processor.cc @@ -471,6 +471,22 @@ MetalCommandProcessor::MetalCommandProcessor( MetalGraphicsSystem* graphics_system, kernel::KernelState* kernel_state) : CommandProcessor(graphics_system, kernel_state) {} +std::string MetalCommandProcessor::GetTitleStateSuffix() const { + if (!render_target_cache_) { + return {}; + } + std::ostringstream suffix; + suffix << " - SPIRV-Cross"; + uint32_t draw_resolution_scale_x = + texture_cache_ ? texture_cache_->draw_resolution_scale_x() : 1; + uint32_t draw_resolution_scale_y = + texture_cache_ ? texture_cache_->draw_resolution_scale_y() : 1; + if (draw_resolution_scale_x > 1 || draw_resolution_scale_y > 1) { + suffix << ' ' << draw_resolution_scale_x << 'x' << draw_resolution_scale_y; + } + return suffix.str(); +} + MetalCommandProcessor::~MetalCommandProcessor() { // End any active render encoder before releasing // Note: Only call endEncoding if the encoder is still active diff --git a/src/xenia/gpu/metal/metal_command_processor.h b/src/xenia/gpu/metal/metal_command_processor.h index 2827f0042..a359863ca 100644 --- a/src/xenia/gpu/metal/metal_command_processor.h +++ b/src/xenia/gpu/metal/metal_command_processor.h @@ -67,6 +67,8 @@ class MetalCommandProcessor : public CommandProcessor { void InvalidateGpuMemory() override; void ClearReadbackBuffers() override; + std::string GetTitleStateSuffix() const override; + // Track memory regions written by IssueCopy (resolve) so trace playback // can skip overwriting them with stale data from the trace file. void MarkResolvedMemory(uint32_t base_ptr, uint32_t length); diff --git a/src/xenia/gpu/metal/metal_graphics_system.cc b/src/xenia/gpu/metal/metal_graphics_system.cc index 9defb35d0..7ca1d2499 100644 --- a/src/xenia/gpu/metal/metal_graphics_system.cc +++ b/src/xenia/gpu/metal/metal_graphics_system.cc @@ -26,7 +26,7 @@ bool MetalGraphicsSystem::IsAvailable() { return xe::ui::metal::MetalProvider::IsMetalAPIAvailable(); } -std::string MetalGraphicsSystem::name() const { return "MetalGraphicsSystem"; } +std::string MetalGraphicsSystem::name() const { return "Metal"; } X_STATUS MetalGraphicsSystem::Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc index da9348552..a09ae62d2 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -13,13 +13,11 @@ #include #include -#include "xenia/apu/audio_system.h" #include "xenia/base/assert.h" #include "xenia/base/byte_order.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" #include "xenia/base/profiling.h" -#include "xenia/emulator.h" #include "xenia/gpu/draw_util.h" #include "xenia/gpu/gpu_flags.h" #include "xenia/gpu/packet_disassembler.h" @@ -190,33 +188,29 @@ void VulkanCommandProcessor::ReturnFromWait() { CommandProcessor::ReturnFromWait(); } -std::string VulkanCommandProcessor::GetWindowTitleText() const { - std::ostringstream title; - title << "Vulkan"; - if (render_target_cache_) { - switch (render_target_cache_->GetPath()) { - case RenderTargetCache::Path::kHostRenderTargets: - title << " - FBO"; - break; - case RenderTargetCache::Path::kPixelShaderInterlock: - title << " - FSI"; - break; - default: - break; - } - uint32_t draw_resolution_scale_x = - texture_cache_ ? texture_cache_->draw_resolution_scale_x() : 1; - uint32_t draw_resolution_scale_y = - texture_cache_ ? texture_cache_->draw_resolution_scale_y() : 1; - if (draw_resolution_scale_x > 1 || draw_resolution_scale_y > 1) { - title << ' ' << draw_resolution_scale_x << 'x' << draw_resolution_scale_y; - } +std::string VulkanCommandProcessor::GetTitleStateSuffix() const { + if (!render_target_cache_) { + return {}; } - auto* audio_system = kernel_state_->emulator()->audio_system(); - if (audio_system) { - title << " - " << audio_system->name(); + std::ostringstream suffix; + switch (render_target_cache_->GetPath()) { + case RenderTargetCache::Path::kHostRenderTargets: + suffix << " - FBO"; + break; + case RenderTargetCache::Path::kPixelShaderInterlock: + suffix << " - FSI"; + break; + default: + break; } - return title.str(); + uint32_t draw_resolution_scale_x = + texture_cache_ ? texture_cache_->draw_resolution_scale_x() : 1; + uint32_t draw_resolution_scale_y = + texture_cache_ ? texture_cache_->draw_resolution_scale_y() : 1; + if (draw_resolution_scale_x > 1 || draw_resolution_scale_y > 1) { + suffix << ' ' << draw_resolution_scale_x << 'x' << draw_resolution_scale_y; + } + return suffix.str(); } bool VulkanCommandProcessor::SetupContext() { diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.h b/src/xenia/gpu/vulkan/vulkan_command_processor.h index 185295a50..395f3593d 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.h +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.h @@ -271,8 +271,7 @@ class VulkanCommandProcessor final : public CommandProcessor { void SetViewport(const VkViewport& viewport); void SetScissor(const VkRect2D& scissor); - // Returns the text to display in the GPU backend name in the window title. - std::string GetWindowTitleText() const; + std::string GetTitleStateSuffix() const override; // Debug marker methods - public so subsystems can annotate their operations. void PushDebugMarker(const char* format, ...); diff --git a/src/xenia/gpu/vulkan/vulkan_graphics_system.cc b/src/xenia/gpu/vulkan/vulkan_graphics_system.cc index 3c80d766a..d5be87315 100644 --- a/src/xenia/gpu/vulkan/vulkan_graphics_system.cc +++ b/src/xenia/gpu/vulkan/vulkan_graphics_system.cc @@ -21,14 +21,7 @@ VulkanGraphicsSystem::VulkanGraphicsSystem() {} VulkanGraphicsSystem::~VulkanGraphicsSystem() {} -std::string VulkanGraphicsSystem::name() const { - auto vulkan_command_processor = - static_cast(command_processor()); - if (vulkan_command_processor != nullptr) { - return vulkan_command_processor->GetWindowTitleText(); - } - return "Vulkan"; -} +std::string VulkanGraphicsSystem::name() const { return "Vulkan"; } X_STATUS VulkanGraphicsSystem::Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,