Adding some GPU docs and creating output directories automatically.
This commit is contained in:
parent
6be12a61c1
commit
cabf9d6261
52
docs/gpu.md
52
docs/gpu.md
|
@ -1,5 +1,57 @@
|
||||||
# GPU Documentation
|
# 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
|
## References
|
||||||
|
|
||||||
### Command Buffer/Registers
|
### Command Buffer/Registers
|
||||||
|
|
|
@ -113,6 +113,7 @@
|
||||||
<ClCompile Include="src\xenia\gpu\sampler_info.cc" />
|
<ClCompile Include="src\xenia\gpu\sampler_info.cc" />
|
||||||
<ClCompile Include="src\xenia\gpu\shader.cc" />
|
<ClCompile Include="src\xenia\gpu\shader.cc" />
|
||||||
<ClCompile Include="src\xenia\gpu\texture_info.cc" />
|
<ClCompile Include="src\xenia\gpu\texture_info.cc" />
|
||||||
|
<ClCompile Include="src\xenia\gpu\tracing.cc" />
|
||||||
<ClCompile Include="src\xenia\gpu\ucode_disassembler.cc" />
|
<ClCompile Include="src\xenia\gpu\ucode_disassembler.cc" />
|
||||||
<ClCompile Include="src\xenia\hid\hid.cc" />
|
<ClCompile Include="src\xenia\hid\hid.cc" />
|
||||||
<ClCompile Include="src\xenia\hid\input_driver.cc" />
|
<ClCompile Include="src\xenia\hid\input_driver.cc" />
|
||||||
|
|
|
@ -724,6 +724,9 @@
|
||||||
<ClCompile Include="src\xenia\kernel\xam_avatar.cc">
|
<ClCompile Include="src\xenia\kernel\xam_avatar.cc">
|
||||||
<Filter>src\xenia\kernel</Filter>
|
<Filter>src\xenia\kernel</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="src\xenia\gpu\tracing.cc">
|
||||||
|
<Filter>src\xenia\gpu</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src\xenia\emulator.h">
|
<ClInclude Include="src\xenia\emulator.h">
|
||||||
|
|
|
@ -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
|
} // namespace xe
|
||||||
|
|
|
@ -50,6 +50,7 @@ std::wstring find_name_from_path(const std::wstring& path);
|
||||||
|
|
||||||
// Get parent path of the given directory or filename.
|
// Get parent path of the given directory or filename.
|
||||||
std::string find_base_path(const std::string& path);
|
std::string find_base_path(const std::string& path);
|
||||||
|
std::wstring find_base_path(const std::wstring& path);
|
||||||
|
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "xenia/base/fs.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
#include "xenia/base/threading.h"
|
#include "xenia/base/threading.h"
|
||||||
|
@ -67,7 +68,11 @@ Debugger::~Debugger() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Debugger::StartSession() {
|
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_path_ = xe::join_paths(session_path, L"functions");
|
||||||
functions_file_ =
|
functions_file_ =
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "xenia/gpu/gl4/gl4_shader.h"
|
#include "xenia/gpu/gl4/gl4_shader.h"
|
||||||
|
|
||||||
|
#include "xenia/base/fs.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/gpu/gl4/gl4_gpu-private.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",
|
snprintf(file_name, xe::countof(file_name), "%s/gl4_gen_%.16llX.%s",
|
||||||
base_path, data_hash_,
|
base_path, data_hash_,
|
||||||
shader_type_ == ShaderType::kVertex ? "vert" : "frag");
|
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.
|
// Note that we put the translated source first so we get good line numbers.
|
||||||
FILE* f = fopen(file_name, "w");
|
FILE* f = fopen(file_name, "w");
|
||||||
fprintf(f, translated_disassembly_.c_str());
|
fprintf(f, translated_disassembly_.c_str());
|
||||||
|
@ -430,7 +438,7 @@ bool GL4Shader::CompileProgram(std::string source) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append to shader dump.
|
// Append to shader dump.
|
||||||
if (FLAGS_dump_shaders.size()) {
|
if (!FLAGS_dump_shaders.empty()) {
|
||||||
if (disasm_start) {
|
if (disasm_start) {
|
||||||
FILE* f = fopen(file_name, "a");
|
FILE* f = fopen(file_name, "a");
|
||||||
fprintf(f, "\n\n/*\n");
|
fprintf(f, "\n\n/*\n");
|
||||||
|
|
|
@ -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
|
|
@ -82,30 +82,14 @@ struct EventCommand {
|
||||||
|
|
||||||
class TraceWriter {
|
class TraceWriter {
|
||||||
public:
|
public:
|
||||||
TraceWriter(uint8_t* membase) : membase_(membase), file_(nullptr) {}
|
TraceWriter(uint8_t* membase);
|
||||||
~TraceWriter() = default;
|
~TraceWriter();
|
||||||
|
|
||||||
bool is_open() const { return file_ != nullptr; }
|
bool is_open() const { return file_ != nullptr; }
|
||||||
|
|
||||||
bool Open(const std::wstring& path) {
|
bool Open(const std::wstring& path);
|
||||||
Close();
|
void Flush();
|
||||||
file_ = _wfopen(path.c_str(), L"wb");
|
void Close();
|
||||||
return file_ != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Flush() {
|
|
||||||
if (file_) {
|
|
||||||
fflush(file_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Close() {
|
|
||||||
if (file_) {
|
|
||||||
fflush(file_);
|
|
||||||
fclose(file_);
|
|
||||||
file_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WritePrimaryBufferStart(uint32_t base_ptr, uint32_t count) {
|
void WritePrimaryBufferStart(uint32_t base_ptr, uint32_t count) {
|
||||||
if (!file_) {
|
if (!file_) {
|
||||||
|
|
|
@ -221,6 +221,7 @@ X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data,
|
||||||
std::vector<uint8_t> buffer) {
|
std::vector<uint8_t> buffer) {
|
||||||
std::lock_guard<xe::recursive_mutex> lock(content_mutex_);
|
std::lock_guard<xe::recursive_mutex> lock(content_mutex_);
|
||||||
auto package_path = ResolvePackagePath(data);
|
auto package_path = ResolvePackagePath(data);
|
||||||
|
xe::fs::CreateFolder(package_path);
|
||||||
if (xe::fs::PathExists(package_path)) {
|
if (xe::fs::PathExists(package_path)) {
|
||||||
auto thumb_path = xe::join_paths(package_path, kThumbnailFileName);
|
auto thumb_path = xe::join_paths(package_path, kThumbnailFileName);
|
||||||
auto file = _wfopen(thumb_path.c_str(), L"wb");
|
auto file = _wfopen(thumb_path.c_str(), L"wb");
|
||||||
|
|
Loading…
Reference in New Issue