From cabf9d626164c53566358770dee9d61bc3472954 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Wed, 10 Jun 2015 19:19:55 -0700 Subject: [PATCH] Adding some GPU docs and creating output directories automatically. --- docs/gpu.md | 52 +++++++++++++++++++++++++++++ libxenia.vcxproj | 1 + libxenia.vcxproj.filters | 3 ++ src/xenia/base/string.cc | 16 +++++++++ src/xenia/base/string.h | 1 + src/xenia/debug/debugger.cc | 7 +++- src/xenia/gpu/gl4/gl4_shader.cc | 12 +++++-- src/xenia/gpu/tracing.cc | 49 +++++++++++++++++++++++++++ src/xenia/gpu/tracing.h | 26 +++------------ src/xenia/kernel/content_manager.cc | 1 + 10 files changed, 144 insertions(+), 24 deletions(-) create mode 100644 src/xenia/gpu/tracing.cc diff --git a/docs/gpu.md b/docs/gpu.md index 00fa701c7..97a0fa8fe 100644 --- a/docs/gpu.md +++ b/docs/gpu.md @@ -1,5 +1,57 @@ # GPU Documentation +## Options + +### General + +See the top of [src/xenia/gpu/gpu.cc](https://github.com/benvanik/xenia/blob/master/src/xenia/gpu/gpu.cc). + +`--vsync=false` will attempt to render the game as fast as possible instead of +waiting for a fixed 60hz timer. + +### OpenGL + +See the top of [src/xenia/gpu/gl4/gl4_gpu.cc](https://github.com/benvanik/xenia/blob/master/src/xenia/gpu/gl4/gl4_gpu.cc). + +Buggy GL implementations can benefit from `--thread_safe_gl`. + +## Tools + +### Shader Dumps + +Adding `--dump_shaders=path/` will write all translated shaders to the given +path with names based on input hash (so they'll be stable across runs). + +### xe-gpu-trace-viewer + +To quickly iterate on graphical issues, xenia can dump frames (or sequences of +frames) while running that can be opened and inspected in a separate app. + +The basic workflow is: + +1. Capture the frame in game (using F4) or a stream of frames. +2. Add the file path to the xe-gpu-trace-viewer Debugging command line in +Visual Studio. +3. Launch xe-gpu-trace-viewer. +4. Poke around, find issues, etc. +5. Modify code. +6. Build and relaunch. +7. Goto 4. + +#### Capturing Frames + +First, specify a path to capture traces to with +`--trace_gpu_prefix=path/file_prefix_`. All files will have a randomish name +based on that. + +When running xenia.exe you can hit F4 at any time to capture the next frame the +game tries to draw (up until a VdSwap call). The file can be used immediately. + +#### Capturing Sequences + +Passing `--trace_gpu_stream` will write all frames rendered to a file, allowing +you to seek through them in the trace viewer. These files will get large. + ## References ### Command Buffer/Registers diff --git a/libxenia.vcxproj b/libxenia.vcxproj index a2d327666..1b3de68d2 100644 --- a/libxenia.vcxproj +++ b/libxenia.vcxproj @@ -113,6 +113,7 @@ + diff --git a/libxenia.vcxproj.filters b/libxenia.vcxproj.filters index 958a1cfc1..d02907f53 100644 --- a/libxenia.vcxproj.filters +++ b/libxenia.vcxproj.filters @@ -724,6 +724,9 @@ src\xenia\kernel + + src\xenia\gpu + diff --git a/src/xenia/base/string.cc b/src/xenia/base/string.cc index acd00ce76..3a9eeb04a 100644 --- a/src/xenia/base/string.cc +++ b/src/xenia/base/string.cc @@ -196,4 +196,20 @@ std::string find_base_path(const std::string& path) { } } +std::wstring find_base_path(const std::wstring& path) { + auto last_slash = path.find_last_of('\\'); + if (last_slash == std::wstring::npos) { + return path; + } else if (last_slash == path.length() - 1) { + auto prev_slash = path.find_last_of('\\', last_slash - 1); + if (prev_slash == std::wstring::npos) { + return L""; + } else { + return path.substr(0, prev_slash + 1); + } + } else { + return path.substr(0, last_slash + 1); + } +} + } // namespace xe diff --git a/src/xenia/base/string.h b/src/xenia/base/string.h index 79f159e35..8fb9b4b84 100644 --- a/src/xenia/base/string.h +++ b/src/xenia/base/string.h @@ -50,6 +50,7 @@ std::wstring find_name_from_path(const std::wstring& path); // Get parent path of the given directory or filename. std::string find_base_path(const std::string& path); +std::wstring find_base_path(const std::wstring& path); } // namespace xe diff --git a/src/xenia/debug/debugger.cc b/src/xenia/debug/debugger.cc index 3f2ca3dda..37229cf4f 100644 --- a/src/xenia/debug/debugger.cc +++ b/src/xenia/debug/debugger.cc @@ -16,6 +16,7 @@ #include +#include "xenia/base/fs.h" #include "xenia/base/logging.h" #include "xenia/base/string.h" #include "xenia/base/threading.h" @@ -67,7 +68,11 @@ Debugger::~Debugger() { } bool Debugger::StartSession() { - std::wstring session_path = xe::to_wstring(FLAGS_debug_session_path); + auto session_path = xe::to_wstring(FLAGS_debug_session_path); + if (!session_path.empty()) { + session_path = xe::to_absolute_path(session_path); + xe::fs::CreateFolder(session_path); + } functions_path_ = xe::join_paths(session_path, L"functions"); functions_file_ = diff --git a/src/xenia/gpu/gl4/gl4_shader.cc b/src/xenia/gpu/gl4/gl4_shader.cc index 2e4dd8ef6..c2c251abb 100644 --- a/src/xenia/gpu/gl4/gl4_shader.cc +++ b/src/xenia/gpu/gl4/gl4_shader.cc @@ -9,6 +9,7 @@ #include "xenia/gpu/gl4/gl4_shader.h" +#include "xenia/base/fs.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" #include "xenia/gpu/gl4/gl4_gpu-private.h" @@ -357,7 +358,14 @@ bool GL4Shader::CompileProgram(std::string source) { snprintf(file_name, xe::countof(file_name), "%s/gl4_gen_%.16llX.%s", base_path, data_hash_, shader_type_ == ShaderType::kVertex ? "vert" : "frag"); - if (FLAGS_dump_shaders.size()) { + if (!FLAGS_dump_shaders.empty()) { + // Ensure shader dump path exists. + auto dump_shaders_path = xe::to_wstring(FLAGS_dump_shaders); + if (!dump_shaders_path.empty()) { + dump_shaders_path = xe::to_absolute_path(dump_shaders_path); + xe::fs::CreateFolder(dump_shaders_path); + } + // Note that we put the translated source first so we get good line numbers. FILE* f = fopen(file_name, "w"); fprintf(f, translated_disassembly_.c_str()); @@ -430,7 +438,7 @@ bool GL4Shader::CompileProgram(std::string source) { } // Append to shader dump. - if (FLAGS_dump_shaders.size()) { + if (!FLAGS_dump_shaders.empty()) { if (disasm_start) { FILE* f = fopen(file_name, "a"); fprintf(f, "\n\n/*\n"); diff --git a/src/xenia/gpu/tracing.cc b/src/xenia/gpu/tracing.cc new file mode 100644 index 000000000..ea3af529a --- /dev/null +++ b/src/xenia/gpu/tracing.cc @@ -0,0 +1,49 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/gpu/tracing.h" + +#include "xenia/base/fs.h" +#include "xenia/base/string.h" + +namespace xe { +namespace gpu { + +TraceWriter::TraceWriter(uint8_t* membase) + : membase_(membase), file_(nullptr) {} + +TraceWriter::~TraceWriter() = default; + +bool TraceWriter::Open(const std::wstring& path) { + Close(); + + auto canonical_path = xe::to_absolute_path(path); + auto base_path = xe::find_base_path(canonical_path); + xe::fs::CreateFolder(base_path); + + file_ = _wfopen(canonical_path.c_str(), L"wb"); + return file_ != nullptr; +} + +void TraceWriter::Flush() { + if (file_) { + fflush(file_); + } +} + +void TraceWriter::Close() { + if (file_) { + fflush(file_); + fclose(file_); + file_ = nullptr; + } +} + +} // namespace gpu +} // namespace xe diff --git a/src/xenia/gpu/tracing.h b/src/xenia/gpu/tracing.h index cd7c53f68..b1f9622ea 100644 --- a/src/xenia/gpu/tracing.h +++ b/src/xenia/gpu/tracing.h @@ -82,30 +82,14 @@ struct EventCommand { class TraceWriter { public: - TraceWriter(uint8_t* membase) : membase_(membase), file_(nullptr) {} - ~TraceWriter() = default; + TraceWriter(uint8_t* membase); + ~TraceWriter(); bool is_open() const { return file_ != nullptr; } - bool Open(const std::wstring& path) { - Close(); - file_ = _wfopen(path.c_str(), L"wb"); - return file_ != nullptr; - } - - void Flush() { - if (file_) { - fflush(file_); - } - } - - void Close() { - if (file_) { - fflush(file_); - fclose(file_); - file_ = nullptr; - } - } + bool Open(const std::wstring& path); + void Flush(); + void Close(); void WritePrimaryBufferStart(uint32_t base_ptr, uint32_t count) { if (!file_) { diff --git a/src/xenia/kernel/content_manager.cc b/src/xenia/kernel/content_manager.cc index f422652b3..d5fef014f 100644 --- a/src/xenia/kernel/content_manager.cc +++ b/src/xenia/kernel/content_manager.cc @@ -221,6 +221,7 @@ X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data, std::vector buffer) { std::lock_guard lock(content_mutex_); auto package_path = ResolvePackagePath(data); + xe::fs::CreateFolder(package_path); if (xe::fs::PathExists(package_path)) { auto thumb_path = xe::join_paths(package_path, kThumbnailFileName); auto file = _wfopen(thumb_path.c_str(), L"wb");