From 0c13b239fd456b2b2ffeb7c13df958ea949d5013 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sat, 22 Aug 2020 23:15:50 +0300 Subject: [PATCH] [D3D12] DXILConv disassembly dumping option --- .gitmodules | 3 + src/xenia/gpu/d3d12/d3d12_shader.cc | 78 +++++++++++++++++++++----- src/xenia/gpu/d3d12/d3d12_shader.h | 6 +- src/xenia/gpu/d3d12/pipeline_cache.cc | 79 ++++++++++++++++++++++++--- src/xenia/gpu/d3d12/pipeline_cache.h | 10 ++++ src/xenia/ui/d3d12/d3d12_api.h | 3 + src/xenia/ui/d3d12/d3d12_provider.cc | 77 ++++++++++++++++++++++---- src/xenia/ui/d3d12/d3d12_provider.h | 35 ++++++++++-- third_party/DirectXShaderCompiler | 1 + 9 files changed, 254 insertions(+), 38 deletions(-) create mode 160000 third_party/DirectXShaderCompiler diff --git a/.gitmodules b/.gitmodules index ec398defa..6c3ca7278 100644 --- a/.gitmodules +++ b/.gitmodules @@ -61,3 +61,6 @@ [submodule "third_party/disruptorplus"] path = third_party/disruptorplus url = https://github.com/xenia-project/disruptorplus.git +[submodule "third_party/DirectXShaderCompiler"] + path = third_party/DirectXShaderCompiler + url = https://github.com/microsoft/DirectXShaderCompiler.git diff --git a/src/xenia/gpu/d3d12/d3d12_shader.cc b/src/xenia/gpu/d3d12/d3d12_shader.cc index cdef02cbf..0b5296a4f 100644 --- a/src/xenia/gpu/d3d12/d3d12_shader.cc +++ b/src/xenia/gpu/d3d12/d3d12_shader.cc @@ -62,21 +62,73 @@ void D3D12Shader::SetTexturesAndSamplers( } } -bool D3D12Shader::DisassembleDxbc(const ui::d3d12::D3D12Provider* provider) { - if (!host_disassembly_.empty()) { - return true; +void D3D12Shader::DisassembleDxbc(const ui::d3d12::D3D12Provider& provider, + bool disassemble_dxbc, + IDxbcConverter* dxbc_converter, + IDxcUtils* dxc_utils, + IDxcCompiler* dxc_compiler) { + bool is_first_disassembly = true; + if (disassemble_dxbc) { + ID3DBlob* dxbc_disassembly; + if (SUCCEEDED(provider.Disassemble(translated_binary().data(), + translated_binary().size(), + D3D_DISASM_ENABLE_INSTRUCTION_NUMBERING | + D3D_DISASM_ENABLE_INSTRUCTION_OFFSET, + nullptr, &dxbc_disassembly))) { + assert_true(is_first_disassembly); + is_first_disassembly = false; + host_disassembly_.append( + reinterpret_cast(dxbc_disassembly->GetBufferPointer())); + dxbc_disassembly->Release(); + } else { + XELOGE("Failed to disassemble DXBC shader {:016X}", ucode_data_hash()); + } } - ID3DBlob* blob; - if (FAILED(provider->Disassemble(translated_binary().data(), - translated_binary().size(), - D3D_DISASM_ENABLE_INSTRUCTION_NUMBERING | - D3D_DISASM_ENABLE_INSTRUCTION_OFFSET, - nullptr, &blob))) { - return false; + if (dxbc_converter && dxc_utils && dxc_compiler) { + void* dxil; + UINT32 dxil_size; + if (SUCCEEDED(dxbc_converter->Convert( + translated_binary().data(), UINT32(translated_binary().size()), + nullptr, &dxil, &dxil_size, nullptr)) && + dxil != nullptr) { + IDxcBlobEncoding* dxil_blob; + if (SUCCEEDED(dxc_utils->CreateBlobFromPinned(dxil, dxil_size, DXC_CP_ACP, + &dxil_blob))) { + IDxcBlobEncoding* dxil_disassembly; + bool dxil_disassembled = + SUCCEEDED(dxc_compiler->Disassemble(dxil_blob, &dxil_disassembly)); + dxil_blob->Release(); + CoTaskMemFree(dxil); + if (dxil_disassembled) { + IDxcBlobUtf8* dxil_disassembly_utf8; + bool dxil_disassembly_got_utf8 = SUCCEEDED(dxc_utils->GetBlobAsUtf8( + dxil_disassembly, &dxil_disassembly_utf8)); + dxil_disassembly->Release(); + if (dxil_disassembly_got_utf8) { + if (!is_first_disassembly) { + host_disassembly_.append("\n\n"); + } + is_first_disassembly = false; + host_disassembly_.append(reinterpret_cast( + dxil_disassembly_utf8->GetStringPointer())); + dxil_disassembly_utf8->Release(); + } else { + XELOGE("Failed to get DXIL shader {:016X} disassembly as UTF-8", + ucode_data_hash()); + } + } else { + XELOGE("Failed to disassemble DXIL shader {:016X}", + ucode_data_hash()); + } + } else { + XELOGE("Failed to create a blob with DXIL shader {:016X}", + ucode_data_hash()); + CoTaskMemFree(dxil); + } + } else { + XELOGE("Failed to convert shader {:016X} to DXIL", ucode_data_hash()); + } } - host_disassembly_ = reinterpret_cast(blob->GetBufferPointer()); - blob->Release(); - return true; } } // namespace d3d12 diff --git a/src/xenia/gpu/d3d12/d3d12_shader.h b/src/xenia/gpu/d3d12/d3d12_shader.h index 5b7564ea0..7eb4ac6e0 100644 --- a/src/xenia/gpu/d3d12/d3d12_shader.h +++ b/src/xenia/gpu/d3d12/d3d12_shader.h @@ -43,7 +43,11 @@ class D3D12Shader : public Shader { return forced_early_z_shader_; } - bool DisassembleDxbc(const ui::d3d12::D3D12Provider* provider); + void DisassembleDxbc(const ui::d3d12::D3D12Provider& provider, + bool disassemble_dxbc, + IDxbcConverter* dxbc_converter = nullptr, + IDxcUtils* dxc_utils = nullptr, + IDxcCompiler* dxc_compiler = nullptr); static constexpr uint32_t kMaxTextureBindingIndexBits = DxbcShaderTranslator::kMaxTextureBindingIndexBits; diff --git a/src/xenia/gpu/d3d12/pipeline_cache.cc b/src/xenia/gpu/d3d12/pipeline_cache.cc index bd0578e9b..84640c030 100644 --- a/src/xenia/gpu/d3d12/pipeline_cache.cc +++ b/src/xenia/gpu/d3d12/pipeline_cache.cc @@ -31,9 +31,16 @@ #include "xenia/base/string.h" #include "xenia/gpu/d3d12/d3d12_command_processor.h" #include "xenia/gpu/gpu_flags.h" +#include "xenia/ui/d3d12/d3d12_util.h" DEFINE_bool(d3d12_dxbc_disasm, false, "Disassemble DXBC shaders after generation.", "D3D12"); +DEFINE_bool( + d3d12_dxbc_disasm_dxilconv, false, + "Disassemble DXBC shaders after conversion to DXIL, if DXIL shaders are " + "supported by the OS, and DirectX Shader Compiler DLLs available at " + "https://github.com/microsoft/DirectXShaderCompiler/releases are present.", + "D3D12"); DEFINE_int32( d3d12_pipeline_creation_threads, -1, "Number of threads used for graphics pipeline state object creation. -1 to " @@ -85,6 +92,30 @@ PipelineCache::PipelineCache(D3D12CommandProcessor* command_processor, PipelineCache::~PipelineCache() { Shutdown(); } bool PipelineCache::Initialize() { + auto provider = command_processor_->GetD3D12Context()->GetD3D12Provider(); + + // Initialize the command processor thread DXIL objects. + if (cvars::d3d12_dxbc_disasm_dxilconv) { + if (FAILED(provider->DxbcConverterCreateInstance( + CLSID_DxbcConverter, IID_PPV_ARGS(&dxbc_converter_)))) { + XELOGE( + "Failed to create DxbcConverter, converted DXIL disassembly for " + "debugging will be unavailable"); + } + if (FAILED(provider->DxcCreateInstance(CLSID_DxcUtils, + IID_PPV_ARGS(&dxc_utils_)))) { + XELOGE( + "Failed to create DxcUtils, converted DXIL disassembly for debugging " + "will be unavailable"); + } + if (FAILED(provider->DxcCreateInstance(CLSID_DxcCompiler, + IID_PPV_ARGS(&dxc_compiler_)))) { + XELOGE( + "Failed to create DxcCompiler, converted DXIL disassembly for " + "debugging will be unavailable"); + } + } + uint32_t logical_processor_count = xe::threading::logical_processor_count(); if (!logical_processor_count) { // Pick some reasonable amount if couldn't determine the number of cores. @@ -134,6 +165,10 @@ void PipelineCache::Shutdown() { creation_threads_.clear(); } creation_completion_event_.reset(); + + ui::d3d12::util::ReleaseAndNull(dxc_compiler_); + ui::d3d12::util::ReleaseAndNull(dxc_utils_); + ui::d3d12::util::ReleaseAndNull(dxbc_converter_); } void PipelineCache::ClearCache(bool shutting_down) { @@ -273,6 +308,19 @@ void PipelineCache::InitializeShaderStorage( DxbcShaderTranslator translator( provider->GetAdapterVendorID(), bindless_resources_used_, edram_rov_used_, provider->GetGraphicsAnalysis() != nullptr); + // If needed and possible, create objects needed for DXIL conversion and + // disassembly on this thread. + IDxbcConverter* dxbc_converter = nullptr; + IDxcUtils* dxc_utils = nullptr; + IDxcCompiler* dxc_compiler = nullptr; + if (cvars::d3d12_dxbc_disasm_dxilconv && dxbc_converter_ && dxc_utils_ && + dxc_compiler_) { + provider->DxbcConverterCreateInstance(CLSID_DxbcConverter, + IID_PPV_ARGS(&dxbc_converter)); + provider->DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(&dxc_utils)); + provider->DxcCreateInstance(CLSID_DxcCompiler, + IID_PPV_ARGS(&dxc_compiler)); + } for (;;) { std::pair shader_to_translate; for (;;) { @@ -292,7 +340,8 @@ void PipelineCache::InitializeShaderStorage( assert_not_null(shader_to_translate.second); if (!TranslateShader( translator, shader_to_translate.second, - shader_to_translate.first.sq_program_cntl, + shader_to_translate.first.sq_program_cntl, dxbc_converter, + dxc_utils, dxc_compiler, shader_to_translate.first.host_vertex_shader_type)) { std::lock_guard lock(shaders_failed_to_translate_mutex); shaders_failed_to_translate.push_back(shader_to_translate.second); @@ -302,6 +351,15 @@ void PipelineCache::InitializeShaderStorage( --shader_translation_threads_busy; } } + if (dxc_compiler) { + dxc_compiler->Release(); + } + if (dxc_utils) { + dxc_utils->Release(); + } + if (dxbc_converter) { + dxbc_converter->Release(); + } }; std::vector> shader_translation_threads; @@ -825,6 +883,7 @@ bool PipelineCache::EnsureShadersTranslated( if (!vertex_shader->is_translated()) { if (!TranslateShader(*shader_translator_, vertex_shader, sq_program_cntl, + dxbc_converter_, dxc_utils_, dxc_compiler_, host_vertex_shader_type)) { XELOGE("Failed to translate the vertex shader!"); return false; @@ -842,7 +901,8 @@ bool PipelineCache::EnsureShadersTranslated( } if (pixel_shader != nullptr && !pixel_shader->is_translated()) { - if (!TranslateShader(*shader_translator_, pixel_shader, sq_program_cntl)) { + if (!TranslateShader(*shader_translator_, pixel_shader, sq_program_cntl, + dxbc_converter_, dxc_utils_, dxc_compiler_)) { XELOGE("Failed to translate the pixel shader!"); return false; } @@ -953,7 +1013,8 @@ bool PipelineCache::ConfigurePipeline( bool PipelineCache::TranslateShader( DxbcShaderTranslator& translator, D3D12Shader* shader, - reg::SQ_PROGRAM_CNTL cntl, + reg::SQ_PROGRAM_CNTL cntl, IDxbcConverter* dxbc_converter, + IDxcUtils* dxc_utils, IDxcCompiler* dxc_compiler, Shader::HostVertexShaderType host_vertex_shader_type) { // Perform translation. // If this fails the shader will be marked as invalid and ignored later. @@ -1134,12 +1195,12 @@ bool PipelineCache::TranslateShader( } // Disassemble the shader for dumping. - if (cvars::d3d12_dxbc_disasm) { - auto provider = command_processor_->GetD3D12Context()->GetD3D12Provider(); - if (!shader->DisassembleDxbc(provider)) { - XELOGE("Failed to disassemble DXBC shader {:016X}", - shader->ucode_data_hash()); - } + auto provider = command_processor_->GetD3D12Context()->GetD3D12Provider(); + if (cvars::d3d12_dxbc_disasm_dxilconv) { + shader->DisassembleDxbc(*provider, cvars::d3d12_dxbc_disasm, dxbc_converter, + dxc_utils, dxc_compiler); + } else { + shader->DisassembleDxbc(*provider, cvars::d3d12_dxbc_disasm); } // Dump shader files if desired. diff --git a/src/xenia/gpu/d3d12/pipeline_cache.h b/src/xenia/gpu/d3d12/pipeline_cache.h index 11a54f15d..0511d43aa 100644 --- a/src/xenia/gpu/d3d12/pipeline_cache.h +++ b/src/xenia/gpu/d3d12/pipeline_cache.h @@ -224,6 +224,9 @@ class PipelineCache { // Can be called from multiple threads. bool TranslateShader(DxbcShaderTranslator& translator, D3D12Shader* shader, reg::SQ_PROGRAM_CNTL cntl, + IDxbcConverter* dxbc_converter = nullptr, + IDxcUtils* dxc_utils = nullptr, + IDxcCompiler* dxc_compiler = nullptr, Shader::HostVertexShaderType host_vertex_shader_type = Shader::HostVertexShaderType::kVertex); @@ -245,6 +248,13 @@ class PipelineCache { // Reusable shader translator. std::unique_ptr shader_translator_ = nullptr; + + // Command processor thread DXIL conversion/disassembly interfaces, if DXIL + // disassembly is enabled. + IDxbcConverter* dxbc_converter_ = nullptr; + IDxcUtils* dxc_utils_ = nullptr; + IDxcCompiler* dxc_compiler_ = nullptr; + // All loaded shaders mapped by their guest hash key. std::unordered_map> shader_map_; diff --git a/src/xenia/ui/d3d12/d3d12_api.h b/src/xenia/ui/d3d12/d3d12_api.h index 3d6b693eb..8f29317d9 100644 --- a/src/xenia/ui/d3d12/d3d12_api.h +++ b/src/xenia/ui/d3d12/d3d12_api.h @@ -20,6 +20,9 @@ #include #include +#include "third_party/DirectXShaderCompiler/include/dxc/dxcapi.h" +#include "third_party/DirectXShaderCompiler/projects/dxilconv/include/DxbcConverter.h" + #define XELOGD3D XELOGI #endif // XENIA_UI_D3D12_D3D12_API_H_ diff --git a/src/xenia/ui/d3d12/d3d12_provider.cc b/src/xenia/ui/d3d12/d3d12_provider.cc index 413c6d421..77e4e70aa 100644 --- a/src/xenia/ui/d3d12/d3d12_provider.cc +++ b/src/xenia/ui/d3d12/d3d12_provider.cc @@ -78,6 +78,12 @@ D3D12Provider::~D3D12Provider() { dxgi_factory_->Release(); } + if (library_dxcompiler_ != nullptr) { + FreeLibrary(library_dxcompiler_); + } + if (library_dxilconv_ != nullptr) { + FreeLibrary(library_dxilconv_); + } if (library_d3dcompiler_ != nullptr) { FreeLibrary(library_d3dcompiler_); } @@ -109,13 +115,11 @@ bool D3D12Provider::EnableIncreaseBasePriorityPrivilege() { } bool D3D12Provider::Initialize() { - // Load the libraries. + // Load the core libraries. library_dxgi_ = LoadLibraryW(L"dxgi.dll"); library_d3d12_ = LoadLibraryW(L"D3D12.dll"); - library_d3dcompiler_ = LoadLibraryW(L"D3DCompiler_47.dll"); - if (library_dxgi_ == nullptr || library_d3d12_ == nullptr || - library_d3dcompiler_ == nullptr) { - XELOGE("Failed to load dxgi.dll, D3D12.dll or D3DCompiler_47.dll."); + if (library_dxgi_ == nullptr || library_d3d12_ == nullptr) { + XELOGE("Failed to load dxgi.dll or D3D12.dll"); return false; } bool libraries_loaded = true; @@ -136,12 +140,65 @@ bool D3D12Provider::Initialize() { (pfn_d3d12_serialize_root_signature_ = PFN_D3D12_SERIALIZE_ROOT_SIGNATURE( GetProcAddress(library_d3d12_, "D3D12SerializeRootSignature"))) != nullptr; - libraries_loaded &= (pfn_d3d_disassemble_ = pD3DDisassemble(GetProcAddress( - library_d3dcompiler_, "D3DDisassemble"))) != nullptr; if (!libraries_loaded) { + XELOGE("Failed to get DXGI or Direct3D 12 functions"); return false; } + // Load optional D3DCompiler_47.dll. + pfn_d3d_disassemble_ = nullptr; + library_d3dcompiler_ = LoadLibraryW(L"D3DCompiler_47.dll"); + if (library_d3dcompiler_) { + pfn_d3d_disassemble_ = + pD3DDisassemble(GetProcAddress(library_d3dcompiler_, "D3DDisassemble")); + if (pfn_d3d_disassemble_ == nullptr) { + XELOGW( + "Failed to get D3DDisassemble from D3DCompiler_47.dll, DXBC " + "disassembly for debugging will be unavailable"); + } + } else { + XELOGW( + "Failed to load D3DCompiler_47.dll, DXBC disassembly for debugging " + "will be unavailable"); + } + + // Load optional dxilconv.dll. + pfn_dxilconv_dxc_create_instance_ = nullptr; + library_dxilconv_ = LoadLibraryW(L"dxilconv.dll"); + if (library_dxilconv_) { + pfn_dxilconv_dxc_create_instance_ = DxcCreateInstanceProc( + GetProcAddress(library_dxilconv_, "DxcCreateInstance")); + if (pfn_dxilconv_dxc_create_instance_ == nullptr) { + XELOGW( + "Failed to get DxcCreateInstance from dxilconv.dll, converted DXIL " + "disassembly for debugging will be unavailable"); + } + } else { + XELOGW( + "Failed to load dxilconv.dll, converted DXIL disassembly for debugging " + "will be unavailable - DXIL may be unsupported by your OS version"); + } + + // Load optional dxcompiler.dll. + pfn_dxcompiler_dxc_create_instance_ = nullptr; + library_dxcompiler_ = LoadLibraryW(L"dxcompiler.dll"); + if (library_dxcompiler_) { + pfn_dxcompiler_dxc_create_instance_ = DxcCreateInstanceProc( + GetProcAddress(library_dxcompiler_, "DxcCreateInstance")); + if (pfn_dxcompiler_dxc_create_instance_ == nullptr) { + XELOGW( + "Failed to get DxcCreateInstance from dxcompiler.dll, converted DXIL " + "disassembly for debugging will be unavailable"); + } + } else { + XELOGW( + "Failed to load dxcompiler.dll, converted DXIL disassembly for " + "debugging will be unavailable - if needed, download the DirectX " + "Shader Compiler from " + "https://github.com/microsoft/DirectXShaderCompiler/releases and place " + "the DLL in the Xenia directory"); + } + // Configure the DXGI debug info queue. if (cvars::d3d12_break_on_error) { IDXGIInfoQueue* dxgi_info_queue; @@ -205,13 +262,13 @@ bool D3D12Provider::Initialize() { ++adapter_index; } if (adapter == nullptr) { - XELOGE("Failed to get an adapter supporting Direct3D feature level 11_0."); + XELOGE("Failed to get an adapter supporting Direct3D feature level 11_0"); dxgi_factory->Release(); return false; } DXGI_ADAPTER_DESC adapter_desc; if (FAILED(adapter->GetDesc(&adapter_desc))) { - XELOGE("Failed to get the DXGI adapter description."); + XELOGE("Failed to get the DXGI adapter description"); adapter->Release(); dxgi_factory->Release(); return false; @@ -234,7 +291,7 @@ bool D3D12Provider::Initialize() { ID3D12Device* device; if (FAILED(pfn_d3d12_create_device_(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device)))) { - XELOGE("Failed to create a Direct3D 12 feature level 11_0 device."); + XELOGE("Failed to create a Direct3D 12 feature level 11_0 device"); adapter->Release(); dxgi_factory->Release(); return false; diff --git a/src/xenia/ui/d3d12/d3d12_provider.h b/src/xenia/ui/d3d12/d3d12_provider.h index 0e79f56bb..122f16e2f 100644 --- a/src/xenia/ui/d3d12/d3d12_provider.h +++ b/src/xenia/ui/d3d12/d3d12_provider.h @@ -96,9 +96,27 @@ class D3D12Provider : public GraphicsProvider { inline HRESULT Disassemble(const void* src_data, size_t src_data_size, UINT flags, const char* comments, ID3DBlob** disassembly_out) const { + if (!pfn_d3d_disassemble_) { + return E_NOINTERFACE; + } return pfn_d3d_disassemble_(src_data, src_data_size, flags, comments, disassembly_out); } + inline HRESULT DxbcConverterCreateInstance(const CLSID& rclsid, + const IID& riid, + void** ppv) const { + if (!pfn_dxilconv_dxc_create_instance_) { + return E_NOINTERFACE; + } + return pfn_dxilconv_dxc_create_instance_(rclsid, riid, ppv); + } + inline HRESULT DxcCreateInstance(const CLSID& rclsid, const IID& riid, + void** ppv) const { + if (!pfn_dxcompiler_dxc_create_instance_) { + return E_NOINTERFACE; + } + return pfn_dxcompiler_dxc_create_instance_(rclsid, riid, ppv); + } private: explicit D3D12Provider(Window* main_window); @@ -106,21 +124,28 @@ class D3D12Provider : public GraphicsProvider { static bool EnableIncreaseBasePriorityPrivilege(); bool Initialize(); - HMODULE library_dxgi_ = nullptr; - HMODULE library_d3d12_ = nullptr; - HMODULE library_d3dcompiler_ = nullptr; - typedef HRESULT(WINAPI* PFNCreateDXGIFactory2)(UINT Flags, REFIID riid, _COM_Outptr_ void** ppFactory); typedef HRESULT(WINAPI* PFNDXGIGetDebugInterface1)( UINT Flags, REFIID riid, _COM_Outptr_ void** pDebug); + HMODULE library_dxgi_ = nullptr; PFNCreateDXGIFactory2 pfn_create_dxgi_factory2_; PFNDXGIGetDebugInterface1 pfn_dxgi_get_debug_interface1_; + + HMODULE library_d3d12_ = nullptr; PFN_D3D12_GET_DEBUG_INTERFACE pfn_d3d12_get_debug_interface_; PFN_D3D12_CREATE_DEVICE pfn_d3d12_create_device_; PFN_D3D12_SERIALIZE_ROOT_SIGNATURE pfn_d3d12_serialize_root_signature_; - pD3DDisassemble pfn_d3d_disassemble_; + + HMODULE library_d3dcompiler_ = nullptr; + pD3DDisassemble pfn_d3d_disassemble_ = nullptr; + + HMODULE library_dxilconv_ = nullptr; + DxcCreateInstanceProc pfn_dxilconv_dxc_create_instance_ = nullptr; + + HMODULE library_dxcompiler_ = nullptr; + DxcCreateInstanceProc pfn_dxcompiler_dxc_create_instance_ = nullptr; IDXGIFactory2* dxgi_factory_ = nullptr; IDXGraphicsAnalysis* graphics_analysis_ = nullptr; diff --git a/third_party/DirectXShaderCompiler b/third_party/DirectXShaderCompiler new file mode 160000 index 000000000..6b6f40200 --- /dev/null +++ b/third_party/DirectXShaderCompiler @@ -0,0 +1 @@ +Subproject commit 6b6f40200bea5ed99367513f53f6a28e52fd3d0e