Factoring out a lot of reusable GPU code from gl4/.

This commit is contained in:
Ben Vanik
2015-11-08 11:54:36 -08:00
parent b26f4a5719
commit b5a18b5462
23 changed files with 3074 additions and 2746 deletions

View File

@@ -21,82 +21,31 @@
#include <vector>
#include "xenia/base/threading.h"
#include "xenia/gpu/command_processor.h"
#include "xenia/gpu/gl4/draw_batcher.h"
#include "xenia/gpu/gl4/gl4_shader.h"
#include "xenia/gpu/gl4/gl4_shader_translator.h"
#include "xenia/gpu/gl4/texture_cache.h"
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/tracing.h"
#include "xenia/gpu/xenos.h"
#include "xenia/kernel/xthread.h"
#include "xenia/memory.h"
#include "xenia/ui/gl/circular_buffer.h"
#include "xenia/ui/gl/gl_context.h"
namespace xe {
namespace kernel {
class XHostThread;
} // namespace kernel
} // namespace xe
namespace xe {
namespace gpu {
namespace gl4 {
class GL4GraphicsSystem;
struct SwapState {
// Lock must be held when changing data in this structure.
std::mutex mutex;
// Dimensions of the framebuffer textures. Should match window size.
uint32_t width = 0;
uint32_t height = 0;
// Current front buffer, being drawn to the screen.
GLuint front_buffer_texture = 0;
// Current back buffer, being updated by the CP.
GLuint back_buffer_texture = 0;
// Whether the back buffer is dirty and a swap is pending.
bool pending = false;
};
enum class SwapMode {
kNormal,
kIgnored,
};
class CommandProcessor {
class GL4CommandProcessor : public CommandProcessor {
public:
explicit CommandProcessor(GL4GraphicsSystem* graphics_system);
~CommandProcessor();
GL4CommandProcessor(GL4GraphicsSystem* graphics_system,
kernel::KernelState* kernel_state);
~GL4CommandProcessor() override;
uint32_t counter() const { return counter_; }
void increment_counter() { counter_++; }
bool Initialize(std::unique_ptr<xe::ui::GraphicsContext> context);
void Shutdown();
void CallInThread(std::function<void()> fn);
void ClearCaches();
SwapState& swap_state() { return swap_state_; }
void set_swap_mode(SwapMode swap_mode) { swap_mode_ = swap_mode; }
void IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width,
uint32_t frontbuffer_height);
void set_swap_request_handler(std::function<void()> fn) {
swap_request_handler_ = fn;
}
void RequestFrameTrace(const std::wstring& root_path);
void BeginTracing(const std::wstring& root_path);
void EndTracing();
void InitializeRingBuffer(uint32_t ptr, uint32_t page_count);
void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size);
void UpdateWritePointer(uint32_t value);
void ExecutePacket(uint32_t ptr, uint32_t count);
void ClearCaches() override;
// HACK: for debugging; would be good to have this in a base type.
TextureCache* texture_cache() { return &texture_cache_; }
@@ -111,8 +60,6 @@ class CommandProcessor {
xenos::DepthRenderTargetFormat format);
private:
class RingbufferReader;
enum class UpdateStatus {
kCompatible,
kMismatch,
@@ -153,69 +100,22 @@ class CommandProcessor {
} handles;
};
void WorkerThreadMain();
bool SetupGL();
void ShutdownGL();
bool SetupContext() override;
void ShutdownContext() override;
GLuint CreateGeometryProgram(const std::string& source);
void WriteRegister(uint32_t index, uint32_t value);
void MakeCoherent();
void PrepareForWait();
void ReturnFromWait();
void MakeCoherent() override;
void PrepareForWait() override;
void ReturnFromWait() override;
void ExecutePrimaryBuffer(uint32_t start_index, uint32_t end_index);
void ExecuteIndirectBuffer(uint32_t ptr, uint32_t length);
bool ExecutePacket(RingbufferReader* reader);
bool ExecutePacketType0(RingbufferReader* reader, uint32_t packet);
bool ExecutePacketType1(RingbufferReader* reader, uint32_t packet);
bool ExecutePacketType2(RingbufferReader* reader, uint32_t packet);
bool ExecutePacketType3(RingbufferReader* reader, uint32_t packet);
bool ExecutePacketType3_ME_INIT(RingbufferReader* reader, uint32_t packet,
uint32_t count);
bool ExecutePacketType3_NOP(RingbufferReader* reader, uint32_t packet,
uint32_t count);
bool ExecutePacketType3_INTERRUPT(RingbufferReader* reader, uint32_t packet,
uint32_t count);
bool ExecutePacketType3_XE_SWAP(RingbufferReader* reader, uint32_t packet,
uint32_t count);
bool ExecutePacketType3_INDIRECT_BUFFER(RingbufferReader* reader,
uint32_t packet, uint32_t count);
bool ExecutePacketType3_WAIT_REG_MEM(RingbufferReader* reader,
uint32_t packet, uint32_t count);
bool ExecutePacketType3_REG_RMW(RingbufferReader* reader, uint32_t packet,
uint32_t count);
bool ExecutePacketType3_COND_WRITE(RingbufferReader* reader, uint32_t packet,
uint32_t count);
bool ExecutePacketType3_EVENT_WRITE(RingbufferReader* reader, uint32_t packet,
uint32_t count);
bool ExecutePacketType3_EVENT_WRITE_SHD(RingbufferReader* reader,
uint32_t packet, uint32_t count);
bool ExecutePacketType3_EVENT_WRITE_EXT(RingbufferReader* reader,
uint32_t packet, uint32_t count);
bool ExecutePacketType3_DRAW_INDX(RingbufferReader* reader, uint32_t packet,
uint32_t count);
bool ExecutePacketType3_DRAW_INDX_2(RingbufferReader* reader, uint32_t packet,
uint32_t count);
bool ExecutePacketType3_SET_CONSTANT(RingbufferReader* reader,
uint32_t packet, uint32_t count);
bool ExecutePacketType3_SET_CONSTANT2(RingbufferReader* reader,
uint32_t packet, uint32_t count);
bool ExecutePacketType3_LOAD_ALU_CONSTANT(RingbufferReader* reader,
uint32_t packet, uint32_t count);
bool ExecutePacketType3_SET_SHADER_CONSTANTS(RingbufferReader* reader,
uint32_t packet, uint32_t count);
bool ExecutePacketType3_IM_LOAD(RingbufferReader* reader, uint32_t packet,
uint32_t count);
bool ExecutePacketType3_IM_LOAD_IMMEDIATE(RingbufferReader* reader,
uint32_t packet, uint32_t count);
bool ExecutePacketType3_INVALIDATE_STATE(RingbufferReader* reader,
uint32_t packet, uint32_t count);
void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width,
uint32_t frontbuffer_height) override;
bool LoadShader(ShaderType shader_type, uint32_t guest_address,
const uint32_t* host_address, uint32_t dword_count);
const uint32_t* host_address, uint32_t dword_count) override;
bool IssueDraw();
bool IssueDraw(PrimitiveType prim_type, uint32_t index_count,
IndexBufferInfo* index_buffer_info) override;
UpdateStatus UpdateShaders(PrimitiveType prim_type);
UpdateStatus UpdateRenderTargets();
UpdateStatus UpdateState();
@@ -223,77 +123,32 @@ class CommandProcessor {
UpdateStatus UpdateRasterizerState();
UpdateStatus UpdateBlendState();
UpdateStatus UpdateDepthStencilState();
UpdateStatus PopulateIndexBuffer();
UpdateStatus PopulateIndexBuffer(IndexBufferInfo* index_buffer_info);
UpdateStatus PopulateVertexBuffers();
UpdateStatus PopulateSamplers();
UpdateStatus PopulateSampler(const Shader::SamplerDesc& desc);
bool IssueCopy();
bool IssueCopy() override;
CachedFramebuffer* GetFramebuffer(GLuint color_targets[4],
GLuint depth_target);
Memory* memory_;
GL4GraphicsSystem* graphics_system_;
RegisterFile* register_file_;
TraceWriter trace_writer_;
enum class TraceState {
kDisabled,
kStreaming,
kSingleFrame,
};
TraceState trace_state_;
std::wstring trace_frame_path_;
std::atomic<bool> worker_running_;
kernel::object_ref<kernel::XHostThread> worker_thread_;
std::unique_ptr<xe::ui::GraphicsContext> context_;
SwapMode swap_mode_;
SwapState swap_state_;
std::function<void()> swap_request_handler_;
std::queue<std::function<void()>> pending_fns_;
uint32_t counter_;
uint32_t primary_buffer_ptr_;
uint32_t primary_buffer_size_;
uint32_t read_ptr_index_;
uint32_t read_ptr_update_freq_;
uint32_t read_ptr_writeback_ptr_;
std::unique_ptr<xe::threading::Event> write_ptr_index_event_;
std::atomic<uint32_t> write_ptr_index_;
uint64_t bin_select_;
uint64_t bin_mask_;
GL4ShaderTranslator shader_translator_;
std::vector<std::unique_ptr<GL4Shader>> all_shaders_;
std::unordered_map<uint64_t, GL4Shader*> shader_cache_;
GL4Shader* active_vertex_shader_;
GL4Shader* active_pixel_shader_;
CachedFramebuffer* active_framebuffer_;
GLuint last_framebuffer_texture_;
GL4Shader* active_vertex_shader_ = nullptr;
GL4Shader* active_pixel_shader_ = nullptr;
CachedFramebuffer* active_framebuffer_ = nullptr;
GLuint last_framebuffer_texture_ = 0;
std::vector<CachedFramebuffer> cached_framebuffers_;
std::vector<CachedColorRenderTarget> cached_color_render_targets_;
std::vector<CachedDepthRenderTarget> cached_depth_render_targets_;
std::vector<std::unique_ptr<CachedPipeline>> all_pipelines_;
std::unordered_map<uint64_t, CachedPipeline*> cached_pipelines_;
GLuint point_list_geometry_program_;
GLuint rect_list_geometry_program_;
GLuint quad_list_geometry_program_;
GLuint line_quad_list_geometry_program_;
struct {
xenos::IndexFormat format;
xenos::Endian endianness;
uint32_t count;
uint32_t guest_base;
size_t length;
} index_buffer_info_;
uint32_t draw_index_count_;
GLuint point_list_geometry_program_ = 0;
GLuint rect_list_geometry_program_ = 0;
GLuint quad_list_geometry_program_ = 0;
GLuint line_quad_list_geometry_program_ = 0;
TextureCache texture_cache_;

View File

@@ -12,41 +12,20 @@
#include <algorithm>
#include <cstring>
#include "xenia/base/clock.h"
#include "xenia/base/logging.h"
#include "xenia/base/profiling.h"
#include "xenia/base/threading.h"
#include "xenia/cpu/processor.h"
#include "xenia/emulator.h"
#include "xenia/gpu/gl4/gl4_command_processor.h"
#include "xenia/gpu/gl4/gl4_gpu_flags.h"
#include "xenia/gpu/gpu_flags.h"
#include "xenia/gpu/tracing.h"
#include "xenia/ui/window.h"
namespace xe {
namespace gpu {
namespace gl4 {
void InitializeIfNeeded();
void CleanupOnShutdown();
void InitializeIfNeeded() {
static bool has_initialized = false;
if (has_initialized) {
return;
}
has_initialized = true;
//
atexit(CleanupOnShutdown);
}
void CleanupOnShutdown() {}
std::unique_ptr<GraphicsSystem> Create(Emulator* emulator) {
InitializeIfNeeded();
return std::make_unique<GL4GraphicsSystem>(emulator);
std::unique_ptr<GraphicsSystem> Create() {
return std::make_unique<GL4GraphicsSystem>();
}
std::unique_ptr<ui::GraphicsContext> GL4GraphicsSystem::CreateContext(
@@ -57,15 +36,14 @@ std::unique_ptr<ui::GraphicsContext> GL4GraphicsSystem::CreateContext(
return xe::ui::gl::GLContext::Create(target_window);
}
GL4GraphicsSystem::GL4GraphicsSystem(Emulator* emulator)
: GraphicsSystem(emulator), worker_running_(false) {}
GL4GraphicsSystem::GL4GraphicsSystem() : GraphicsSystem() {}
GL4GraphicsSystem::~GL4GraphicsSystem() = default;
X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
ui::Loop* target_loop,
kernel::KernelState* kernel_state,
ui::Window* target_window) {
auto result = GraphicsSystem::Setup(processor, target_loop, target_window);
auto result = GraphicsSystem::Setup(processor, kernel_state, target_window);
if (result) {
return result;
}
@@ -73,224 +51,14 @@ X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
display_context_ =
reinterpret_cast<xe::ui::gl::GLContext*>(target_window->context());
// Watch for paint requests to do our swap.
target_window->on_painting.AddListener(
[this](xe::ui::UIEvent* e) { Swap(e); });
// Create rendering control.
// This must happen on the UI thread.
std::unique_ptr<xe::ui::GraphicsContext> processor_context;
target_loop_->PostSynchronous([&]() {
// Setup the GL context the command processor will do all its drawing in.
// It's shared with the display context so that we can resolve framebuffers
// from it.
processor_context = display_context_->CreateShared();
processor_context->ClearCurrent();
});
if (!processor_context) {
xe::FatalError(
"Unable to initialize GL context. Xenia requires OpenGL 4.5. Ensure "
"you have the latest drivers for your GPU and that it supports OpenGL "
"4.5. See http://xenia.jp/faq/ for more information.");
return X_STATUS_UNSUCCESSFUL;
}
// Create command processor. This will spin up a thread to process all
// incoming ringbuffer packets.
command_processor_ = std::make_unique<CommandProcessor>(this);
if (!command_processor_->Initialize(std::move(processor_context))) {
XELOGE("Unable to initialize command processor");
return X_STATUS_UNSUCCESSFUL;
}
command_processor_->set_swap_request_handler(
[this]() { target_window_->Invalidate(); });
// Let the processor know we want register access callbacks.
memory_->AddVirtualMappedRange(
0x7FC80000, 0xFFFF0000, 0x0000FFFF, this,
reinterpret_cast<cpu::MMIOReadCallback>(MMIOReadRegisterThunk),
reinterpret_cast<cpu::MMIOWriteCallback>(MMIOWriteRegisterThunk));
// 60hz vsync timer.
worker_running_ = true;
worker_thread_ =
kernel::object_ref<kernel::XHostThread>(new kernel::XHostThread(
emulator()->kernel_state(), 128 * 1024, 0, [this]() {
uint64_t vsync_duration = FLAGS_vsync ? 16 : 1;
uint64_t last_frame_time = Clock::QueryGuestTickCount();
while (worker_running_) {
uint64_t current_time = Clock::QueryGuestTickCount();
uint64_t elapsed = (current_time - last_frame_time) /
(Clock::guest_tick_frequency() / 1000);
if (elapsed >= vsync_duration) {
MarkVblank();
last_frame_time = current_time;
}
xe::threading::Sleep(std::chrono::milliseconds(1));
}
return 0;
}));
// As we run vblank interrupts the debugger must be able to suspend us.
worker_thread_->set_can_debugger_suspend(true);
worker_thread_->set_name("GL4 Vsync");
worker_thread_->Create();
if (FLAGS_trace_gpu_stream) {
BeginTracing();
}
return X_STATUS_SUCCESS;
}
void GL4GraphicsSystem::Shutdown() {
EndTracing();
void GL4GraphicsSystem::Shutdown() { GraphicsSystem::Shutdown(); }
worker_running_ = false;
worker_thread_->Wait(0, 0, 0, nullptr);
worker_thread_.reset();
command_processor_->Shutdown();
// TODO(benvanik): remove mapped range.
command_processor_.reset();
GraphicsSystem::Shutdown();
}
void GL4GraphicsSystem::InitializeRingBuffer(uint32_t ptr,
uint32_t page_count) {
command_processor_->InitializeRingBuffer(ptr, page_count);
}
void GL4GraphicsSystem::EnableReadPointerWriteBack(uint32_t ptr,
uint32_t block_size) {
command_processor_->EnableReadPointerWriteBack(ptr, block_size);
}
void GL4GraphicsSystem::RequestFrameTrace() {
command_processor_->RequestFrameTrace(xe::to_wstring(FLAGS_trace_gpu_prefix));
}
void GL4GraphicsSystem::BeginTracing() {
command_processor_->BeginTracing(xe::to_wstring(FLAGS_trace_gpu_prefix));
}
void GL4GraphicsSystem::EndTracing() { command_processor_->EndTracing(); }
void GL4GraphicsSystem::PlayTrace(const uint8_t* trace_data, size_t trace_size,
TracePlaybackMode playback_mode) {
command_processor_->CallInThread([this, trace_data, trace_size,
playback_mode]() {
command_processor_->set_swap_mode(SwapMode::kIgnored);
auto trace_ptr = trace_data;
bool pending_break = false;
const PacketStartCommand* pending_packet = nullptr;
while (trace_ptr < trace_data + trace_size) {
auto type = static_cast<TraceCommandType>(xe::load<uint32_t>(trace_ptr));
switch (type) {
case TraceCommandType::kPrimaryBufferStart: {
auto cmd =
reinterpret_cast<const PrimaryBufferStartCommand*>(trace_ptr);
//
trace_ptr += sizeof(*cmd) + cmd->count * 4;
break;
}
case TraceCommandType::kPrimaryBufferEnd: {
auto cmd =
reinterpret_cast<const PrimaryBufferEndCommand*>(trace_ptr);
//
trace_ptr += sizeof(*cmd);
break;
}
case TraceCommandType::kIndirectBufferStart: {
auto cmd =
reinterpret_cast<const IndirectBufferStartCommand*>(trace_ptr);
//
trace_ptr += sizeof(*cmd) + cmd->count * 4;
break;
}
case TraceCommandType::kIndirectBufferEnd: {
auto cmd =
reinterpret_cast<const IndirectBufferEndCommand*>(trace_ptr);
//
trace_ptr += sizeof(*cmd);
break;
}
case TraceCommandType::kPacketStart: {
auto cmd = reinterpret_cast<const PacketStartCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd);
std::memcpy(memory()->TranslatePhysical(cmd->base_ptr), trace_ptr,
cmd->count * 4);
trace_ptr += cmd->count * 4;
pending_packet = cmd;
break;
}
case TraceCommandType::kPacketEnd: {
auto cmd = reinterpret_cast<const PacketEndCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd);
if (pending_packet) {
command_processor_->ExecutePacket(pending_packet->base_ptr,
pending_packet->count);
pending_packet = nullptr;
}
if (pending_break) {
return;
}
break;
}
case TraceCommandType::kMemoryRead: {
auto cmd = reinterpret_cast<const MemoryReadCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd);
std::memcpy(memory()->TranslatePhysical(cmd->base_ptr), trace_ptr,
cmd->length);
trace_ptr += cmd->length;
break;
}
case TraceCommandType::kMemoryWrite: {
auto cmd = reinterpret_cast<const MemoryWriteCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd);
// ?
trace_ptr += cmd->length;
break;
}
case TraceCommandType::kEvent: {
auto cmd = reinterpret_cast<const EventCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd);
switch (cmd->event_type) {
case EventType::kSwap: {
if (playback_mode == TracePlaybackMode::kBreakOnSwap) {
pending_break = true;
}
break;
}
}
break;
}
}
}
command_processor_->set_swap_mode(SwapMode::kNormal);
command_processor_->IssueSwap(0, 1280, 720);
});
}
void GL4GraphicsSystem::ClearCaches() {
command_processor_->CallInThread(
[&]() { command_processor_->ClearCaches(); });
}
void GL4GraphicsSystem::MarkVblank() {
SCOPE_profile_cpu_f("gpu");
// Increment vblank counter (so the game sees us making progress).
command_processor_->increment_counter();
// TODO(benvanik): we shouldn't need to do the dispatch here, but there's
// something wrong and the CP will block waiting for code that
// needs to be run in the interrupt.
DispatchInterruptCallback(0, 2);
std::unique_ptr<CommandProcessor> GL4GraphicsSystem::CreateCommandProcessor() {
return std::unique_ptr<CommandProcessor>(
new GL4CommandProcessor(this, kernel_state_));
}
void GL4GraphicsSystem::Swap(xe::ui::UIEvent* e) {
@@ -315,51 +83,12 @@ void GL4GraphicsSystem::Swap(xe::ui::UIEvent* e) {
// Blit the frontbuffer.
display_context_->blitter()->BlitTexture2D(
swap_state.front_buffer_texture,
static_cast<GLuint>(swap_state.front_buffer_texture),
Rect2D(0, 0, swap_state.width, swap_state.height),
Rect2D(0, 0, target_window_->width(), target_window_->height()),
GL_LINEAR);
}
uint32_t GL4GraphicsSystem::ReadRegister(uint32_t addr) {
uint32_t r = addr & 0xFFFF;
switch (r) {
case 0x3C00: // ?
return 0x08100748;
case 0x3C04: // ?
return 0x0000200E;
case 0x6530: // Scanline?
return 0x000002D0;
case 0x6544: // ? vblank pending?
return 1;
case 0x6584: // Screen res - 1280x720
return 0x050002D0;
}
assert_true(r < RegisterFile::kRegisterCount);
return register_file_.values[r].u32;
}
void GL4GraphicsSystem::WriteRegister(uint32_t addr, uint32_t value) {
uint32_t r = addr & 0xFFFF;
switch (r) {
case 0x0714: // CP_RB_WPTR
command_processor_->UpdateWritePointer(value);
break;
case 0x6110: // ? swap related?
XELOGW("Unimplemented GPU register %.4X write: %.8X", r, value);
return;
default:
XELOGW("Unknown GPU register %.4X write: %.8X", r, value);
break;
}
assert_true(r < RegisterFile::kRegisterCount);
register_file_.values[r].u32 = value;
}
} // namespace gl4
} // namespace gpu
} // namespace xe

View File

@@ -12,10 +12,7 @@
#include <memory>
#include "xenia/gpu/gl4/command_processor.h"
#include "xenia/gpu/graphics_system.h"
#include "xenia/gpu/register_file.h"
#include "xenia/kernel/xthread.h"
#include "xenia/ui/gl/gl_context.h"
namespace xe {
@@ -24,53 +21,22 @@ namespace gl4 {
class GL4GraphicsSystem : public GraphicsSystem {
public:
explicit GL4GraphicsSystem(Emulator* emulator);
GL4GraphicsSystem();
~GL4GraphicsSystem() override;
std::unique_ptr<ui::GraphicsContext> CreateContext(
ui::Window* target_window) override;
X_STATUS Setup(cpu::Processor* processor, ui::Loop* target_loop,
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
ui::Window* target_window) override;
void Shutdown() override;
RegisterFile* register_file() { return &register_file_; }
CommandProcessor* command_processor() const {
return command_processor_.get();
}
void InitializeRingBuffer(uint32_t ptr, uint32_t page_count) override;
void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size) override;
void RequestFrameTrace() override;
void BeginTracing() override;
void EndTracing() override;
void PlayTrace(const uint8_t* trace_data, size_t trace_size,
TracePlaybackMode playback_mode) override;
void ClearCaches() override;
private:
void MarkVblank();
void Swap(xe::ui::UIEvent* e);
uint32_t ReadRegister(uint32_t addr);
void WriteRegister(uint32_t addr, uint32_t value);
std::unique_ptr<CommandProcessor> CreateCommandProcessor() override;
static uint32_t MMIOReadRegisterThunk(void* ppc_context,
GL4GraphicsSystem* gs, uint32_t addr) {
return gs->ReadRegister(addr);
}
static void MMIOWriteRegisterThunk(void* ppc_context, GL4GraphicsSystem* gs,
uint32_t addr, uint32_t value) {
gs->WriteRegister(addr, value);
}
RegisterFile register_file_;
std::unique_ptr<CommandProcessor> command_processor_;
void Swap(xe::ui::UIEvent* e) override;
xe::ui::gl::GLContext* display_context_ = nullptr;
std::atomic<bool> worker_running_;
kernel::object_ref<kernel::XHostThread> worker_thread_;
};
} // namespace gl4

View File

@@ -8,6 +8,7 @@
*/
#include <gflags/gflags.h>
#include <cstring>
#include "third_party/imgui/imgui.h"
@@ -19,16 +20,21 @@
#include "xenia/base/platform_win.h"
#include "xenia/base/profiling.h"
#include "xenia/emulator.h"
#include "xenia/gpu/command_processor.h"
#include "xenia/gpu/graphics_system.h"
#include "xenia/gpu/packet_disassembler.h"
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/tracing.h"
#include "xenia/gpu/sampler_info.h"
#include "xenia/gpu/texture_info.h"
#include "xenia/gpu/trace_player.h"
#include "xenia/gpu/trace_protocol.h"
#include "xenia/gpu/xenos.h"
#include "xenia/ui/gl/gl_context.h"
#include "xenia/ui/imgui_drawer.h"
#include "xenia/ui/window.h"
// HACK: until we have another impl, we just use gl4 directly.
#include "xenia/gpu/gl4/command_processor.h"
#include "xenia/gpu/gl4/gl4_command_processor.h"
#include "xenia/gpu/gl4/gl4_graphics_system.h"
#include "xenia/gpu/gl4/gl4_shader.h"
@@ -37,810 +43,7 @@ DEFINE_string(target_trace_file, "", "Specifies the trace file to load.");
namespace xe {
namespace gpu {
enum class PacketCategory {
kGeneric,
kDraw,
kSwap,
};
struct PacketTypeInfo {
PacketCategory category;
const char* name;
};
struct PacketAction {
enum class Type {
kRegisterWrite,
kSetBinMask,
kSetBinSelect,
};
Type type;
union {
struct {
uint32_t index;
RegisterFile::RegisterValue value;
} register_write;
struct {
uint64_t value;
} set_bin_mask;
struct {
uint64_t value;
} set_bin_select;
};
static PacketAction RegisterWrite(uint32_t index, uint32_t value) {
PacketAction action;
action.type = Type::kRegisterWrite;
action.register_write.index = index;
action.register_write.value.u32 = value;
return action;
}
static PacketAction SetBinMask(uint64_t value) {
PacketAction action;
action.type = Type::kSetBinMask;
action.set_bin_mask.value = value;
return action;
}
static PacketAction SetBinSelect(uint64_t value) {
PacketAction action;
action.type = Type::kSetBinSelect;
action.set_bin_select.value = value;
return action;
}
};
struct PacketInfo {
const PacketTypeInfo* type_info;
bool predicated;
uint32_t count;
std::vector<PacketAction> actions;
};
bool DisasmPacketType0(const uint8_t* base_ptr, uint32_t packet,
PacketInfo* out_info) {
static const PacketTypeInfo type_0_info = {PacketCategory::kGeneric,
"PM4_TYPE0"};
out_info->type_info = &type_0_info;
uint32_t count = ((packet >> 16) & 0x3FFF) + 1;
out_info->count = 1 + count;
auto ptr = base_ptr + 4;
uint32_t base_index = (packet & 0x7FFF);
uint32_t write_one_reg = (packet >> 15) & 0x1;
for (uint32_t m = 0; m < count; m++) {
uint32_t reg_data = xe::load_and_swap<uint32_t>(ptr);
uint32_t target_index = write_one_reg ? base_index : base_index + m;
out_info->actions.emplace_back(
PacketAction::RegisterWrite(target_index, reg_data));
ptr += 4;
}
return true;
}
bool DisasmPacketType1(const uint8_t* base_ptr, uint32_t packet,
PacketInfo* out_info) {
static const PacketTypeInfo type_1_info = {PacketCategory::kGeneric,
"PM4_TYPE1"};
out_info->type_info = &type_1_info;
out_info->count = 1 + 2;
auto ptr = base_ptr + 4;
uint32_t reg_index_1 = packet & 0x7FF;
uint32_t reg_index_2 = (packet >> 11) & 0x7FF;
uint32_t reg_data_1 = xe::load_and_swap<uint32_t>(ptr);
uint32_t reg_data_2 = xe::load_and_swap<uint32_t>(ptr + 4);
out_info->actions.emplace_back(
PacketAction::RegisterWrite(reg_index_1, reg_data_1));
out_info->actions.emplace_back(
PacketAction::RegisterWrite(reg_index_2, reg_data_2));
return true;
}
bool DisasmPacketType2(const uint8_t* base_ptr, uint32_t packet,
PacketInfo* out_info) {
static const PacketTypeInfo type_2_info = {PacketCategory::kGeneric,
"PM4_TYPE2"};
out_info->type_info = &type_2_info;
out_info->count = 1;
return true;
}
using namespace xe::gpu::xenos;
bool DisasmPacketType3(const uint8_t* base_ptr, uint32_t packet,
PacketInfo* out_info) {
static const PacketTypeInfo type_3_unknown_info = {PacketCategory::kGeneric,
"PM4_TYPE3_UNKNOWN"};
out_info->type_info = &type_3_unknown_info;
uint32_t opcode = (packet >> 8) & 0x7F;
uint32_t count = ((packet >> 16) & 0x3FFF) + 1;
out_info->count = 1 + count;
auto ptr = base_ptr + 4;
if (packet & 1) {
out_info->predicated = true;
}
bool result = true;
switch (opcode) {
case PM4_ME_INIT: {
// initialize CP's micro-engine
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_ME_INIT"};
out_info->type_info = &op_info;
break;
}
case PM4_NOP: {
// skip N 32-bit words to get to the next packet
// No-op, ignore some data.
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_NOP"};
out_info->type_info = &op_info;
break;
}
case PM4_INTERRUPT: {
// generate interrupt from the command stream
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_INTERRUPT"};
out_info->type_info = &op_info;
uint32_t cpu_mask = xe::load_and_swap<uint32_t>(ptr + 0);
for (int n = 0; n < 6; n++) {
if (cpu_mask & (1 << n)) {
// graphics_system_->DispatchInterruptCallback(1, n);
}
}
break;
}
case PM4_XE_SWAP: {
// Xenia-specific VdSwap hook.
// VdSwap will post this to tell us we need to swap the screen/fire an
// interrupt.
// 63 words here, but only the first has any data.
static const PacketTypeInfo op_info = {PacketCategory::kSwap,
"PM4_XE_SWAP"};
out_info->type_info = &op_info;
uint32_t frontbuffer_ptr = xe::load_and_swap<uint32_t>(ptr + 0);
break;
}
case PM4_INDIRECT_BUFFER: {
// indirect buffer dispatch
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_INDIRECT_BUFFER"};
out_info->type_info = &op_info;
uint32_t list_ptr = xe::load_and_swap<uint32_t>(ptr + 0);
uint32_t list_length = xe::load_and_swap<uint32_t>(ptr + 4);
break;
}
case PM4_WAIT_REG_MEM: {
// wait until a register or memory location is a specific value
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_WAIT_REG_MEM"};
out_info->type_info = &op_info;
uint32_t wait_info = xe::load_and_swap<uint32_t>(ptr + 0);
uint32_t poll_reg_addr = xe::load_and_swap<uint32_t>(ptr + 4);
uint32_t ref = xe::load_and_swap<uint32_t>(ptr + 8);
uint32_t mask = xe::load_and_swap<uint32_t>(ptr + 12);
uint32_t wait = xe::load_and_swap<uint32_t>(ptr + 16);
break;
}
case PM4_REG_RMW: {
// register read/modify/write
// ? (used during shader upload and edram setup)
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_REG_RMW"};
out_info->type_info = &op_info;
uint32_t rmw_info = xe::load_and_swap<uint32_t>(ptr + 0);
uint32_t and_mask = xe::load_and_swap<uint32_t>(ptr + 4);
uint32_t or_mask = xe::load_and_swap<uint32_t>(ptr + 8);
break;
}
case PM4_COND_WRITE: {
// conditional write to memory or register
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_COND_WRITE"};
out_info->type_info = &op_info;
uint32_t wait_info = xe::load_and_swap<uint32_t>(ptr + 0);
uint32_t poll_reg_addr = xe::load_and_swap<uint32_t>(ptr + 4);
uint32_t ref = xe::load_and_swap<uint32_t>(ptr + 8);
uint32_t mask = xe::load_and_swap<uint32_t>(ptr + 12);
uint32_t write_reg_addr = xe::load_and_swap<uint32_t>(ptr + 16);
uint32_t write_data = xe::load_and_swap<uint32_t>(ptr + 20);
break;
}
case PM4_EVENT_WRITE: {
// generate an event that creates a write to memory when completed
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_EVENT_WRITE"};
out_info->type_info = &op_info;
uint32_t initiator = xe::load_and_swap<uint32_t>(ptr + 0);
break;
}
case PM4_EVENT_WRITE_SHD: {
// generate a VS|PS_done event
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_EVENT_WRITE_SHD"};
out_info->type_info = &op_info;
uint32_t initiator = xe::load_and_swap<uint32_t>(ptr + 0);
uint32_t address = xe::load_and_swap<uint32_t>(ptr + 4);
uint32_t value = xe::load_and_swap<uint32_t>(ptr + 8);
break;
}
case PM4_EVENT_WRITE_EXT: {
// generate a screen extent event
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_EVENT_WRITE_EXT"};
out_info->type_info = &op_info;
uint32_t unk0 = xe::load_and_swap<uint32_t>(ptr + 0);
uint32_t unk1 = xe::load_and_swap<uint32_t>(ptr + 4);
break;
}
case PM4_DRAW_INDX: {
// initiate fetch of index buffer and draw
// dword0 = viz query info
static const PacketTypeInfo op_info = {PacketCategory::kDraw,
"PM4_DRAW_INDX"};
out_info->type_info = &op_info;
uint32_t dword0 = xe::load_and_swap<uint32_t>(ptr + 0);
uint32_t dword1 = xe::load_and_swap<uint32_t>(ptr + 4);
uint32_t index_count = dword1 >> 16;
auto prim_type = static_cast<PrimitiveType>(dword1 & 0x3F);
uint32_t src_sel = (dword1 >> 6) & 0x3;
if (src_sel == 0x0) {
// Indexed draw.
uint32_t guest_base = xe::load_and_swap<uint32_t>(ptr + 8);
uint32_t index_size = xe::load_and_swap<uint32_t>(ptr + 12);
auto endianness = static_cast<Endian>(index_size >> 30);
index_size &= 0x00FFFFFF;
bool index_32bit = (dword1 >> 11) & 0x1;
index_size *= index_32bit ? 4 : 2;
} else if (src_sel == 0x2) {
// Auto draw.
} else {
// Unknown source select.
assert_always();
}
break;
}
case PM4_DRAW_INDX_2: {
// draw using supplied indices in packet
static const PacketTypeInfo op_info = {PacketCategory::kDraw,
"PM4_DRAW_INDX_2"};
out_info->type_info = &op_info;
uint32_t dword0 = xe::load_and_swap<uint32_t>(ptr + 0);
uint32_t index_count = dword0 >> 16;
auto prim_type = static_cast<PrimitiveType>(dword0 & 0x3F);
uint32_t src_sel = (dword0 >> 6) & 0x3;
assert_true(src_sel == 0x2); // 'SrcSel=AutoIndex'
bool index_32bit = (dword0 >> 11) & 0x1;
uint32_t indices_size = index_count * (index_32bit ? 4 : 2);
auto index_ptr = ptr + 4;
break;
}
case PM4_SET_CONSTANT: {
// load constant into chip and to memory
// PM4_REG(reg) ((0x4 << 16) | (GSL_HAL_SUBBLOCK_OFFSET(reg)))
// reg - 0x2000
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_SET_CONSTANT"};
out_info->type_info = &op_info;
uint32_t offset_type = xe::load_and_swap<uint32_t>(ptr + 0);
uint32_t index = offset_type & 0x7FF;
uint32_t type = (offset_type >> 16) & 0xFF;
switch (type) {
case 0: // ALU
index += 0x4000;
break;
case 1: // FETCH
index += 0x4800;
break;
case 2: // BOOL
index += 0x4900;
break;
case 3: // LOOP
index += 0x4908;
break;
case 4: // REGISTERS
index += 0x2000;
break;
default:
assert_always();
result = false;
break;
}
for (uint32_t n = 0; n < count - 1; n++, index++) {
uint32_t data = xe::load_and_swap<uint32_t>(ptr + 4 + n * 4);
out_info->actions.emplace_back(
PacketAction::RegisterWrite(index, data));
}
break;
}
case PM4_SET_CONSTANT2: {
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_SET_CONSTANT2"};
out_info->type_info = &op_info;
uint32_t offset_type = xe::load_and_swap<uint32_t>(ptr + 0);
uint32_t index = offset_type & 0xFFFF;
for (uint32_t n = 0; n < count - 1; n++, index++) {
uint32_t data = xe::load_and_swap<uint32_t>(ptr + 4 + n * 4);
out_info->actions.emplace_back(
PacketAction::RegisterWrite(index, data));
}
return true;
break;
}
case PM4_LOAD_ALU_CONSTANT: {
// load constants from memory
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_LOAD_ALU_CONSTANT"};
out_info->type_info = &op_info;
uint32_t address = xe::load_and_swap<uint32_t>(ptr + 0);
address &= 0x3FFFFFFF;
uint32_t offset_type = xe::load_and_swap<uint32_t>(ptr + 4);
uint32_t index = offset_type & 0x7FF;
uint32_t size_dwords = xe::load_and_swap<uint32_t>(ptr + 8);
size_dwords &= 0xFFF;
uint32_t type = (offset_type >> 16) & 0xFF;
switch (type) {
case 0: // ALU
index += 0x4000;
break;
case 1: // FETCH
index += 0x4800;
break;
case 2: // BOOL
index += 0x4900;
break;
case 3: // LOOP
index += 0x4908;
break;
case 4: // REGISTERS
index += 0x2000;
break;
default:
assert_always();
return true;
}
for (uint32_t n = 0; n < size_dwords; n++, index++) {
// Hrm, ?
// xe::load_and_swap<uint32_t>(membase_ + GpuToCpu(address + n * 4));
uint32_t data = 0xDEADBEEF;
out_info->actions.emplace_back(
PacketAction::RegisterWrite(index, data));
}
break;
}
case PM4_SET_SHADER_CONSTANTS: {
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_SET_SHADER_CONSTANTS"};
out_info->type_info = &op_info;
uint32_t offset_type = xe::load_and_swap<uint32_t>(ptr + 0);
uint32_t index = offset_type & 0xFFFF;
for (uint32_t n = 0; n < count - 1; n++, index++) {
uint32_t data = xe::load_and_swap<uint32_t>(ptr + 4 + n * 4);
out_info->actions.emplace_back(
PacketAction::RegisterWrite(index, data));
}
return true;
}
case PM4_IM_LOAD: {
// load sequencer instruction memory (pointer-based)
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_IM_LOAD"};
out_info->type_info = &op_info;
uint32_t addr_type = xe::load_and_swap<uint32_t>(ptr + 0);
auto shader_type = static_cast<ShaderType>(addr_type & 0x3);
uint32_t addr = addr_type & ~0x3;
uint32_t start_size = xe::load_and_swap<uint32_t>(ptr + 4);
uint32_t start = start_size >> 16;
uint32_t size_dwords = start_size & 0xFFFF; // dwords
assert_true(start == 0);
break;
}
case PM4_IM_LOAD_IMMEDIATE: {
// load sequencer instruction memory (code embedded in packet)
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_IM_LOAD_IMMEDIATE"};
out_info->type_info = &op_info;
uint32_t dword0 = xe::load_and_swap<uint32_t>(ptr + 0);
uint32_t dword1 = xe::load_and_swap<uint32_t>(ptr + 4);
auto shader_type = static_cast<ShaderType>(dword0);
uint32_t start_size = dword1;
uint32_t start = start_size >> 16;
uint32_t size_dwords = start_size & 0xFFFF; // dwords
assert_true(start == 0);
break;
}
case PM4_INVALIDATE_STATE: {
// selective invalidation of state pointers
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_INVALIDATE_STATE"};
out_info->type_info = &op_info;
uint32_t mask = xe::load_and_swap<uint32_t>(ptr + 0);
break;
}
case PM4_SET_BIN_MASK_LO: {
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_SET_BIN_MASK_LO"};
out_info->type_info = &op_info;
uint32_t value = xe::load_and_swap<uint32_t>(ptr);
// bin_mask_ = (bin_mask_ & 0xFFFFFFFF00000000ull) | value;
out_info->actions.emplace_back(PacketAction::SetBinMask(value));
break;
}
case PM4_SET_BIN_MASK_HI: {
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_SET_BIN_MASK_HI"};
out_info->type_info = &op_info;
uint32_t value = xe::load_and_swap<uint32_t>(ptr);
// bin_mask_ =
// (bin_mask_ & 0xFFFFFFFFull) | (static_cast<uint64_t>(value) << 32);
break;
}
case PM4_SET_BIN_SELECT_LO: {
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_SET_BIN_SELECT_LO"};
out_info->type_info = &op_info;
uint32_t value = xe::load_and_swap<uint32_t>(ptr);
// bin_select_ = (bin_select_ & 0xFFFFFFFF00000000ull) | value;
out_info->actions.emplace_back(PacketAction::SetBinSelect(value));
break;
}
case PM4_SET_BIN_SELECT_HI: {
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_SET_BIN_SELECT_HI"};
out_info->type_info = &op_info;
uint32_t value = xe::load_and_swap<uint32_t>(ptr);
// bin_select_ =
// (bin_select_ & 0xFFFFFFFFull) | (static_cast<uint64_t>(value) << 32);
break;
}
// Ignored packets - useful if breaking on the default handler below.
case 0x50: { // 0xC0015000 usually 2 words, 0xFFFFFFFF / 0x00000000
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_TYPE3_0x50"};
out_info->type_info = &op_info;
break;
}
case 0x51: { // 0xC0015100 usually 2 words, 0xFFFFFFFF / 0xFFFFFFFF
static const PacketTypeInfo op_info = {PacketCategory::kGeneric,
"PM4_TYPE3_0x51"};
out_info->type_info = &op_info;
break;
}
default: {
result = false;
break;
}
}
return result;
}
bool DisasmPacket(const uint8_t* base_ptr, PacketInfo* out_info) {
const uint32_t packet = xe::load_and_swap<uint32_t>(base_ptr);
const uint32_t packet_type = packet >> 30;
switch (packet_type) {
case 0x00:
return DisasmPacketType0(base_ptr, packet, out_info);
case 0x01:
return DisasmPacketType1(base_ptr, packet, out_info);
case 0x02:
return DisasmPacketType2(base_ptr, packet, out_info);
case 0x03:
return DisasmPacketType3(base_ptr, packet, out_info);
default:
assert_unhandled_case(packet_type);
return false;
}
}
PacketCategory GetPacketCategory(const uint8_t* base_ptr) {
const uint32_t packet = xe::load_and_swap<uint32_t>(base_ptr);
const uint32_t packet_type = packet >> 30;
switch (packet_type) {
case 0x00:
case 0x01:
case 0x02: {
return PacketCategory::kGeneric;
}
case 0x03: {
uint32_t opcode = (packet >> 8) & 0x7F;
switch (opcode) {
case PM4_DRAW_INDX:
case PM4_DRAW_INDX_2:
return PacketCategory::kDraw;
case PM4_XE_SWAP:
return PacketCategory::kSwap;
default:
return PacketCategory::kGeneric;
}
}
default: {
assert_unhandled_case(packet_type);
return PacketCategory::kGeneric;
}
}
}
// TODO(benvanik): move to tracing.h/cc
class TraceReader {
public:
struct Frame {
struct Command {
enum class Type {
kDraw,
kSwap,
};
const uint8_t* head_ptr;
const uint8_t* start_ptr;
const uint8_t* end_ptr;
Type type;
union {
struct {
//
} draw;
struct {
//
} swap;
};
};
const uint8_t* start_ptr;
const uint8_t* end_ptr;
int command_count;
std::vector<Command> commands;
};
TraceReader() : trace_data_(nullptr), trace_size_(0) {}
~TraceReader() = default;
const Frame* frame(int n) const { return &frames_[n]; }
int frame_count() const { return int(frames_.size()); }
bool Open(const std::wstring& path) {
Close();
mmap_ = MappedMemory::Open(path, MappedMemory::Mode::kRead);
if (!mmap_) {
return false;
}
trace_data_ = reinterpret_cast<const uint8_t*>(mmap_->data());
trace_size_ = mmap_->size();
ParseTrace();
return true;
}
void Close() {
mmap_.reset();
trace_data_ = nullptr;
trace_size_ = 0;
}
// void Foo() {
// auto trace_ptr = trace_data;
// while (trace_ptr < trace_data + trace_size) {
// auto cmd_type = *reinterpret_cast<const TraceCommandType*>(trace_ptr);
// switch (cmd_type) {
// case TraceCommandType::kPrimaryBufferStart:
// break;
// case TraceCommandType::kPrimaryBufferEnd:
// break;
// case TraceCommandType::kIndirectBufferStart:
// break;
// case TraceCommandType::kIndirectBufferEnd:
// break;
// case TraceCommandType::kPacketStart:
// break;
// case TraceCommandType::kPacketEnd:
// break;
// case TraceCommandType::kMemoryRead:
// break;
// case TraceCommandType::kMemoryWrite:
// break;
// case TraceCommandType::kEvent:
// break;
// }
// /*trace_ptr = graphics_system->PlayTrace(
// trace_ptr, trace_size - (trace_ptr - trace_data),
// GraphicsSystem::TracePlaybackMode::kBreakOnSwap);*/
// }
//}
protected:
void ParseTrace() {
auto trace_ptr = trace_data_;
Frame current_frame = {
trace_ptr, nullptr, 0,
};
const PacketStartCommand* packet_start = nullptr;
const uint8_t* packet_start_ptr = nullptr;
const uint8_t* last_ptr = trace_ptr;
bool pending_break = false;
while (trace_ptr < trace_data_ + trace_size_) {
++current_frame.command_count;
auto type = static_cast<TraceCommandType>(xe::load<uint32_t>(trace_ptr));
switch (type) {
case TraceCommandType::kPrimaryBufferStart: {
auto cmd =
reinterpret_cast<const PrimaryBufferStartCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd) + cmd->count * 4;
break;
}
case TraceCommandType::kPrimaryBufferEnd: {
auto cmd =
reinterpret_cast<const PrimaryBufferEndCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd);
break;
}
case TraceCommandType::kIndirectBufferStart: {
auto cmd =
reinterpret_cast<const IndirectBufferStartCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd) + cmd->count * 4;
break;
}
case TraceCommandType::kIndirectBufferEnd: {
auto cmd =
reinterpret_cast<const IndirectBufferEndCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd);
break;
}
case TraceCommandType::kPacketStart: {
auto cmd = reinterpret_cast<const PacketStartCommand*>(trace_ptr);
packet_start_ptr = trace_ptr;
packet_start = cmd;
trace_ptr += sizeof(*cmd) + cmd->count * 4;
break;
}
case TraceCommandType::kPacketEnd: {
auto cmd = reinterpret_cast<const PacketEndCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd);
if (!packet_start_ptr) {
continue;
}
auto packet_category =
GetPacketCategory(packet_start_ptr + sizeof(*packet_start));
switch (packet_category) {
case PacketCategory::kDraw: {
Frame::Command command;
command.type = Frame::Command::Type::kDraw;
command.head_ptr = packet_start_ptr;
command.start_ptr = last_ptr;
command.end_ptr = trace_ptr;
current_frame.commands.push_back(std::move(command));
last_ptr = trace_ptr;
break;
}
case PacketCategory::kSwap: {
//
break;
}
}
if (pending_break) {
current_frame.end_ptr = trace_ptr;
frames_.push_back(std::move(current_frame));
current_frame.start_ptr = trace_ptr;
current_frame.end_ptr = nullptr;
current_frame.command_count = 0;
pending_break = false;
}
break;
}
case TraceCommandType::kMemoryRead: {
auto cmd = reinterpret_cast<const MemoryReadCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd) + cmd->length;
break;
}
case TraceCommandType::kMemoryWrite: {
auto cmd = reinterpret_cast<const MemoryWriteCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd) + cmd->length;
break;
}
case TraceCommandType::kEvent: {
auto cmd = reinterpret_cast<const EventCommand*>(trace_ptr);
trace_ptr += sizeof(*cmd);
switch (cmd->event_type) {
case EventType::kSwap: {
pending_break = true;
break;
}
}
break;
}
default:
// Broken trace file?
assert_unhandled_case(type);
break;
}
}
if (pending_break || current_frame.command_count) {
current_frame.end_ptr = trace_ptr;
frames_.push_back(std::move(current_frame));
}
}
std::unique_ptr<MappedMemory> mmap_;
const uint8_t* trace_data_;
size_t trace_size_;
std::vector<Frame> frames_;
};
class TracePlayer : public TraceReader {
public:
TracePlayer(xe::ui::Loop* loop, GraphicsSystem* graphics_system)
: loop_(loop),
graphics_system_(graphics_system),
current_frame_index_(0),
current_command_index_(-1) {
// Need to allocate all of physical memory so that we can write to it
// during playback.
graphics_system_->memory()
->LookupHeapByType(true, 4096)
->AllocFixed(0, 0x1FFFFFFF, 4096,
kMemoryAllocationReserve | kMemoryAllocationCommit,
kMemoryProtectRead | kMemoryProtectWrite);
}
~TracePlayer() = default;
GraphicsSystem* graphics_system() const { return graphics_system_; }
int current_frame_index() const { return current_frame_index_; }
const Frame* current_frame() const {
if (current_frame_index_ > frame_count()) {
return nullptr;
}
return frame(current_frame_index_);
}
void SeekFrame(int target_frame) {
if (current_frame_index_ == target_frame) {
return;
}
current_frame_index_ = target_frame;
auto frame = current_frame();
current_command_index_ = int(frame->commands.size()) - 1;
assert_true(frame->start_ptr <= frame->end_ptr);
graphics_system_->PlayTrace(
frame->start_ptr, frame->end_ptr - frame->start_ptr,
GraphicsSystem::TracePlaybackMode::kBreakOnSwap);
}
int current_command_index() const { return current_command_index_; }
void SeekCommand(int target_command) {
if (current_command_index_ == target_command) {
return;
}
int previous_command_index = current_command_index_;
current_command_index_ = target_command;
if (current_command_index_ == -1) {
return;
}
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) {
// Seek forward.
const auto& previous_command = frame->commands[target_command - 1];
graphics_system_->PlayTrace(
previous_command.end_ptr, command.end_ptr - previous_command.end_ptr,
GraphicsSystem::TracePlaybackMode::kBreakOnSwap);
} else {
// Full playback from frame start.
graphics_system_->PlayTrace(
frame->start_ptr, command.end_ptr - frame->start_ptr,
GraphicsSystem::TracePlaybackMode::kBreakOnSwap);
}
}
private:
xe::ui::Loop* loop_;
GraphicsSystem* graphics_system_;
int current_frame_index_;
int current_command_index_;
};
void DrawControllerUI(xe::ui::Window* window, TracePlayer& player,
Memory* memory) {
@@ -1126,7 +329,7 @@ void DrawFailedTextureInfo(const Shader::SamplerDesc& desc,
}
void DrawTextureInfo(TracePlayer& player, const Shader::SamplerDesc& desc) {
auto gs = static_cast<gl4::GL4GraphicsSystem*>(player.graphics_system());
auto cp = gs->command_processor();
auto cp = static_cast<gl4::GL4CommandProcessor*>(gs->command_processor());
auto& regs = *gs->register_file();
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + desc.fetch_slot * 6;
@@ -1394,7 +597,7 @@ static const char* kEndiannessNames[] = {
void DrawStateUI(xe::ui::Window* window, TracePlayer& player, Memory* memory) {
auto gs = static_cast<gl4::GL4GraphicsSystem*>(player.graphics_system());
auto cp = gs->command_processor();
auto cp = static_cast<gl4::GL4CommandProcessor*>(gs->command_processor());
auto& regs = *gs->register_file();
ImGui::SetNextWindowPos(ImVec2(float(window->width()) - 500 - 5, 30),
@@ -2097,9 +1300,10 @@ void DrawPacketDisassemblerUI(xe::ui::Window* window, TracePlayer& player,
trace_ptr += sizeof(*cmd);
if (pending_packet) {
PacketInfo packet_info = {0};
if (DisasmPacket(reinterpret_cast<const uint8_t*>(pending_packet) +
sizeof(PacketStartCommand),
&packet_info)) {
if (PacketDisassembler::DisasmPacket(
reinterpret_cast<const uint8_t*>(pending_packet) +
sizeof(PacketStartCommand),
&packet_info)) {
if (packet_info.predicated) {
ImGui::PushStyleColor(ImGuiCol_Text, kColorIgnored);
}

View File

@@ -66,7 +66,7 @@ project("xenia-gpu-gl4-trace-viewer")
project_root.."/build_tools/third_party/gflags/src",
})
files({
"trace_viewer_main.cc",
"gl4_trace_viewer_main.cc",
"../../base/main_"..platform_suffix..".cc",
})