diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index d527304b23..69a2728f38 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -168,9 +168,13 @@ endif()
if(WIN32)
enable_language(ASM_MASM)
target_sources(common PRIVATE FastJmp.asm)
- target_link_libraries(common PUBLIC pthreads4w Winmm.lib)
+ target_link_libraries(common PUBLIC WIL::WIL pthreads4w Winmm.lib)
target_sources(common PRIVATE
FastJmp.asm
+ D3D11/ShaderCache.cpp
+ D3D11/ShaderCache.h
+ D3D11/ShaderCompiler.cpp
+ D3D11/ShaderCompiler.h
GL/ContextWGL.cpp
GL/ContextWGL.h
)
diff --git a/common/D3D11/ShaderCache.cpp b/common/D3D11/ShaderCache.cpp
new file mode 100644
index 0000000000..dc317a4590
--- /dev/null
+++ b/common/D3D11/ShaderCache.cpp
@@ -0,0 +1,385 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2022 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#include "common/PrecompiledHeader.h"
+#include "common/D3D11/ShaderCache.h"
+#include "common/D3D11/ShaderCompiler.h"
+#include "common/FileSystem.h"
+#include "common/Console.h"
+#include "common/MD5Digest.h"
+#include
+
+#pragma pack(push, 1)
+struct CacheIndexEntry
+{
+ u64 source_hash_low;
+ u64 source_hash_high;
+ u64 macro_hash_low;
+ u64 macro_hash_high;
+ u64 entry_point_low;
+ u64 entry_point_high;
+ u32 source_length;
+ u32 shader_type;
+ u32 file_offset;
+ u32 blob_size;
+};
+#pragma pack(pop)
+
+D3D11::ShaderCache::ShaderCache() = default;
+
+D3D11::ShaderCache::~ShaderCache()
+{
+ if (m_index_file)
+ std::fclose(m_index_file);
+ if (m_blob_file)
+ std::fclose(m_blob_file);
+}
+
+bool D3D11::ShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const
+{
+ return (source_hash_low == key.source_hash_low && source_hash_high == key.source_hash_high &&
+ macro_hash_low == key.macro_hash_low && macro_hash_high == key.macro_hash_high &&
+ entry_point_low == key.entry_point_low && entry_point_high == key.entry_point_high &&
+ shader_type == key.shader_type && source_length == key.source_length);
+}
+
+bool D3D11::ShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const
+{
+ return (source_hash_low != key.source_hash_low || source_hash_high != key.source_hash_high ||
+ macro_hash_low != key.macro_hash_low || macro_hash_high != key.macro_hash_high ||
+ entry_point_low != key.entry_point_low || entry_point_high != key.entry_point_high ||
+ shader_type != key.shader_type || source_length != key.source_length);
+}
+
+bool D3D11::ShaderCache::Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_level, u32 version, bool debug)
+{
+ m_feature_level = feature_level;
+ m_version = version;
+ m_debug = debug;
+
+ if (!base_path.empty())
+ {
+ const std::string base_filename = GetCacheBaseFileName(base_path, feature_level, debug);
+ const std::string index_filename = base_filename + ".idx";
+ const std::string blob_filename = base_filename + ".bin";
+
+ if (!ReadExisting(index_filename, blob_filename))
+ return CreateNew(index_filename, blob_filename);
+ }
+
+ return true;
+}
+
+bool D3D11::ShaderCache::CreateNew(const std::string& index_filename, const std::string& blob_filename)
+{
+ if (FileSystem::FileExists(index_filename.c_str()))
+ {
+ Console.Warning("Removing existing index file '%s'", index_filename.c_str());
+ FileSystem::DeleteFilePath(index_filename.c_str());
+ }
+ if (FileSystem::FileExists(blob_filename.c_str()))
+ {
+ Console.Warning("Removing existing blob file '%s'", blob_filename.c_str());
+ FileSystem::DeleteFilePath(blob_filename.c_str());
+ }
+
+ m_index_file = FileSystem::OpenCFile(index_filename.c_str(), "wb");
+ if (!m_index_file)
+ {
+ Console.Error("Failed to open index file '%s' for writing", index_filename.c_str());
+ return false;
+ }
+
+ const u32 index_version = FILE_VERSION;
+ if (std::fwrite(&index_version, sizeof(index_version), 1, m_index_file) != 1 ||
+ std::fwrite(&m_version, sizeof(m_version), 1, m_index_file) != 1)
+ {
+ Console.Error("Failed to write version to index file '%s'", index_filename.c_str());
+ std::fclose(m_index_file);
+ m_index_file = nullptr;
+ FileSystem::DeleteFilePath(index_filename.c_str());
+ return false;
+ }
+
+ m_blob_file = FileSystem::OpenCFile(blob_filename.c_str(), "w+b");
+ if (!m_blob_file)
+ {
+ Console.Error("Failed to open blob file '%s' for writing", blob_filename.c_str());
+ std::fclose(m_index_file);
+ m_index_file = nullptr;
+ FileSystem::DeleteFilePath(index_filename.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+bool D3D11::ShaderCache::ReadExisting(const std::string& index_filename, const std::string& blob_filename)
+{
+ m_index_file = FileSystem::OpenCFile(index_filename.c_str(), "r+b");
+ if (!m_index_file)
+ return false;
+
+ u32 file_version = 0;
+ u32 data_version = 0;
+ if (std::fread(&file_version, sizeof(file_version), 1, m_index_file) != 1 || file_version != FILE_VERSION ||
+ std::fread(&data_version, sizeof(data_version), 1, m_index_file) != 1 || data_version != m_version)
+ {
+ Console.Error("Bad file/data version in '%s'", index_filename.c_str());
+ std::fclose(m_index_file);
+ m_index_file = nullptr;
+ return false;
+ }
+
+ m_blob_file = FileSystem::OpenCFile(blob_filename.c_str(), "a+b");
+ if (!m_blob_file)
+ {
+ Console.Error("Blob file '%s' is missing", blob_filename.c_str());
+ std::fclose(m_index_file);
+ m_index_file = nullptr;
+ return false;
+ }
+
+ std::fseek(m_blob_file, 0, SEEK_END);
+ const u32 blob_file_size = static_cast(std::ftell(m_blob_file));
+
+ for (;;)
+ {
+ CacheIndexEntry entry;
+ if (std::fread(&entry, sizeof(entry), 1, m_index_file) != 1 ||
+ (entry.file_offset + entry.blob_size) > blob_file_size)
+ {
+ if (std::feof(m_index_file))
+ break;
+
+ Console.Error("Failed to read entry from '%s', corrupt file?", index_filename.c_str());
+ m_index.clear();
+ std::fclose(m_blob_file);
+ m_blob_file = nullptr;
+ std::fclose(m_index_file);
+ m_index_file = nullptr;
+ return false;
+ }
+
+ const CacheIndexKey key{
+ entry.source_hash_low, entry.source_hash_high,
+ entry.macro_hash_low, entry.macro_hash_high,
+ entry.entry_point_low, entry.entry_point_high,
+ entry.source_length, static_cast(entry.shader_type)};
+ const CacheIndexData data{entry.file_offset, entry.blob_size};
+ m_index.emplace(key, data);
+ }
+
+ // ensure we don't write before seeking
+ std::fseek(m_index_file, 0, SEEK_END);
+
+ DevCon.WriteLn("Read %zu entries from '%s'", m_index.size(), index_filename.c_str());
+ return true;
+}
+
+std::string D3D11::ShaderCache::GetCacheBaseFileName(const std::string_view& base_path, D3D_FEATURE_LEVEL feature_level,
+ bool debug)
+{
+ std::string base_filename(base_path);
+ base_filename += FS_OSPATH_SEPARATOR_STR "d3d_shaders_";
+
+ switch (feature_level)
+ {
+ case D3D_FEATURE_LEVEL_10_0:
+ base_filename += "sm40";
+ break;
+ case D3D_FEATURE_LEVEL_10_1:
+ base_filename += "sm41";
+ break;
+ case D3D_FEATURE_LEVEL_11_0:
+ base_filename += "sm50";
+ break;
+ default:
+ base_filename += "unk";
+ break;
+ }
+
+ if (debug)
+ base_filename += "_debug";
+
+ return base_filename;
+}
+
+D3D11::ShaderCache::CacheIndexKey D3D11::ShaderCache::GetCacheKey(ShaderCompiler::Type type, const std::string_view& shader_code,
+ const D3D_SHADER_MACRO* macros, const char* entry_point)
+{
+ union
+ {
+ struct
+ {
+ u64 hash_low;
+ u64 hash_high;
+ };
+ u8 hash[16];
+ };
+
+ CacheIndexKey key = {};
+ key.shader_type = type;
+
+ MD5Digest digest;
+ digest.Update(shader_code.data(), static_cast(shader_code.length()));
+ digest.Final(hash);
+ key.source_hash_low = hash_low;
+ key.source_hash_high = hash_high;
+ key.source_length = static_cast(shader_code.length());
+
+ if (macros)
+ {
+ digest.Reset();
+ for (const D3D_SHADER_MACRO* macro = macros; macro->Name != nullptr; macro++)
+ {
+ digest.Update(macro->Name, std::strlen(macro->Name));
+ digest.Update(macro->Definition, std::strlen(macro->Definition));
+ }
+ digest.Final(hash);
+ key.macro_hash_low = hash_low;
+ key.macro_hash_high = hash_high;
+ }
+
+ digest.Reset();
+ digest.Update(entry_point, static_cast(std::strlen(entry_point)));
+ digest.Final(hash);
+ key.entry_point_low = hash_low;
+ key.entry_point_high = hash_high;
+
+ return key;
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCache::GetShaderBlob(ShaderCompiler::Type type, const std::string_view& shader_code,
+ const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
+{
+ const auto key = GetCacheKey(type, shader_code, macros, entry_point);
+ auto iter = m_index.find(key);
+ if (iter == m_index.end())
+ return CompileAndAddShaderBlob(key, shader_code, macros, entry_point);
+
+ wil::com_ptr_nothrow blob;
+ HRESULT hr = D3DCreateBlob(iter->second.blob_size, blob.put());
+ if (FAILED(hr) || std::fseek(m_blob_file, iter->second.file_offset, SEEK_SET) != 0 ||
+ std::fread(blob->GetBufferPointer(), 1, iter->second.blob_size, m_blob_file) != iter->second.blob_size)
+ {
+ Console.Error("(D3D11::ShaderCache::GetShaderBlob): Read blob from file failed");
+ return {};
+ }
+
+ return blob;
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCache::GetVertexShader(ID3D11Device* device,
+ const std::string_view& shader_code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
+{
+ wil::com_ptr_nothrow blob = GetShaderBlob(ShaderCompiler::Type::Vertex, shader_code, macros, entry_point);
+ if (!blob)
+ return {};
+
+ return D3D11::ShaderCompiler::CreateVertexShader(device, blob.get());
+}
+
+bool D3D11::ShaderCache::GetVertexShaderAndInputLayout(ID3D11Device* device,
+ ID3D11VertexShader** vs, ID3D11InputLayout** il,
+ const D3D11_INPUT_ELEMENT_DESC* layout, size_t layout_size, const std::string_view& shader_code,
+ const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
+{
+ wil::com_ptr_nothrow blob = GetShaderBlob(ShaderCompiler::Type::Vertex, shader_code, macros, entry_point);
+ if (!blob)
+ return false;
+
+ wil::com_ptr_nothrow actual_vs = D3D11::ShaderCompiler::CreateVertexShader(device, blob.get());
+ if (!actual_vs)
+ return false;
+
+ HRESULT hr = device->CreateInputLayout(layout, layout_size, blob->GetBufferPointer(), blob->GetBufferSize(), il);
+ if (FAILED(hr))
+ {
+ Console.Error("(GetVertexShaderAndInputLayout) Failed to create input layout: %08X", hr);
+ return false;
+ }
+
+ *vs = actual_vs.detach();
+ return true;
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCache::GetGeometryShader(ID3D11Device* device,
+ const std::string_view& shader_code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
+{
+ wil::com_ptr_nothrow blob = GetShaderBlob(ShaderCompiler::Type::Geometry, shader_code, macros, entry_point);
+ if (!blob)
+ return {};
+
+ return D3D11::ShaderCompiler::CreateGeometryShader(device, blob.get());
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCache::GetPixelShader(ID3D11Device* device,
+ const std::string_view& shader_code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
+{
+ wil::com_ptr_nothrow blob = GetShaderBlob(ShaderCompiler::Type::Pixel, shader_code, macros, entry_point);
+ if (!blob)
+ return {};
+
+ return D3D11::ShaderCompiler::CreatePixelShader(device, blob.get());
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCache::GetComputeShader(ID3D11Device* device,
+ const std::string_view& shader_code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
+{
+ wil::com_ptr_nothrow blob = GetShaderBlob(ShaderCompiler::Type::Compute, shader_code, macros, entry_point);
+ if (!blob)
+ return {};
+
+ return D3D11::ShaderCompiler::CreateComputeShader(device, blob.get());
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCache::CompileAndAddShaderBlob(const CacheIndexKey& key,
+ const std::string_view& shader_code, const D3D_SHADER_MACRO* macros, const char* entry_point)
+{
+ wil::com_ptr_nothrow blob = ShaderCompiler::CompileShader(key.shader_type, m_feature_level, m_debug, shader_code, macros, entry_point);
+ if (!blob)
+ return {};
+
+ if (!m_blob_file || std::fseek(m_blob_file, 0, SEEK_END) != 0)
+ return blob;
+
+ CacheIndexData data;
+ data.file_offset = static_cast(std::ftell(m_blob_file));
+ data.blob_size = static_cast(blob->GetBufferSize());
+
+ CacheIndexEntry entry = {};
+ entry.source_hash_low = key.source_hash_low;
+ entry.source_hash_high = key.source_hash_high;
+ entry.macro_hash_low = key.macro_hash_low;
+ entry.macro_hash_high = key.macro_hash_high;
+ entry.entry_point_low = key.entry_point_low;
+ entry.entry_point_high = key.entry_point_high;
+ entry.source_length = key.source_length;
+ entry.shader_type = static_cast(key.shader_type);
+ entry.blob_size = data.blob_size;
+ entry.file_offset = data.file_offset;
+
+ if (std::fwrite(blob->GetBufferPointer(), 1, entry.blob_size, m_blob_file) != entry.blob_size ||
+ std::fflush(m_blob_file) != 0 || std::fwrite(&entry, sizeof(entry), 1, m_index_file) != 1 ||
+ std::fflush(m_index_file) != 0)
+ {
+ Console.Error("(D3D11::ShaderCache::CompileAndAddShaderBlob) Failed to write shader blob to file");
+ return blob;
+ }
+
+ m_index.emplace(key, data);
+ return blob;
+}
diff --git a/common/D3D11/ShaderCache.h b/common/D3D11/ShaderCache.h
new file mode 100644
index 0000000000..5c5fc5a5a0
--- /dev/null
+++ b/common/D3D11/ShaderCache.h
@@ -0,0 +1,119 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2022 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#pragma once
+#include "common/Pcsx2Defs.h"
+#include "common/HashCombine.h"
+#include "common/RedtapeWindows.h"
+#include "common/D3D11/ShaderCompiler.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace D3D11
+{
+ class ShaderCache
+ {
+ public:
+ ShaderCache();
+ ~ShaderCache();
+
+ D3D_FEATURE_LEVEL GetFeatureLevel() const { return m_feature_level; }
+ bool UsingDebugShaders() const { return m_debug; }
+
+ bool Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_level, u32 version, bool debug);
+
+ wil::com_ptr_nothrow GetShaderBlob(ShaderCompiler::Type type, const std::string_view& shader_code,
+ const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
+
+ wil::com_ptr_nothrow GetVertexShader(ID3D11Device* device, const std::string_view& shader_code,
+ const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
+
+ bool GetVertexShaderAndInputLayout(ID3D11Device* device,
+ ID3D11VertexShader** vs, ID3D11InputLayout** il,
+ const D3D11_INPUT_ELEMENT_DESC* layout, size_t layout_size,
+ const std::string_view& shader_code, const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
+
+ wil::com_ptr_nothrow GetGeometryShader(ID3D11Device* device, const std::string_view& shader_code,
+ const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
+
+ wil::com_ptr_nothrow GetPixelShader(ID3D11Device* device, const std::string_view& shader_code,
+ const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
+
+ wil::com_ptr_nothrow GetComputeShader(ID3D11Device* device, const std::string_view& shader_code,
+ const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
+
+ private:
+ static constexpr u32 FILE_VERSION = 1;
+
+ struct CacheIndexKey
+ {
+ u64 source_hash_low;
+ u64 source_hash_high;
+ u64 macro_hash_low;
+ u64 macro_hash_high;
+ u64 entry_point_low;
+ u64 entry_point_high;
+ u32 source_length;
+ ShaderCompiler::Type shader_type;
+
+ bool operator==(const CacheIndexKey& key) const;
+ bool operator!=(const CacheIndexKey& key) const;
+ };
+
+ struct CacheIndexEntryHasher
+ {
+ std::size_t operator()(const CacheIndexKey& e) const noexcept
+ {
+ std::size_t h = 0;
+ HashCombine(h, e.entry_point_low, e.entry_point_high, e.macro_hash_low, e.macro_hash_high,
+ e.source_hash_low, e.source_hash_high, e.source_length, e.shader_type);
+ return h;
+ }
+ };
+
+ struct CacheIndexData
+ {
+ u32 file_offset;
+ u32 blob_size;
+ };
+
+ using CacheIndex = std::unordered_map;
+
+ static std::string GetCacheBaseFileName(const std::string_view& base_path, D3D_FEATURE_LEVEL feature_level,
+ bool debug);
+ static CacheIndexKey GetCacheKey(ShaderCompiler::Type type, const std::string_view& shader_code,
+ const D3D_SHADER_MACRO* macros, const char* entry_point);
+
+ bool CreateNew(const std::string& index_filename, const std::string& blob_filename);
+ bool ReadExisting(const std::string& index_filename, const std::string& blob_filename);
+ void Close();
+
+ wil::com_ptr_nothrow CompileAndAddShaderBlob(const CacheIndexKey& key, const std::string_view& shader_code,
+ const D3D_SHADER_MACRO* macros, const char* entry_point);
+
+ std::FILE* m_index_file = nullptr;
+ std::FILE* m_blob_file = nullptr;
+
+ CacheIndex m_index;
+
+ D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
+ u32 m_version = 0;
+ bool m_debug = false;
+ };
+} // namespace D3D11
diff --git a/common/D3D11/ShaderCompiler.cpp b/common/D3D11/ShaderCompiler.cpp
new file mode 100644
index 0000000000..eb2cf0f66f
--- /dev/null
+++ b/common/D3D11/ShaderCompiler.cpp
@@ -0,0 +1,215 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2022 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#include "common/PrecompiledHeader.h"
+#include "common/D3D11/ShaderCompiler.h"
+#include "common/Console.h"
+#include "common/StringUtil.h"
+#include
+#include
+#include
+
+static unsigned s_next_bad_shader_id = 1;
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CompileShader(Type type, D3D_FEATURE_LEVEL feature_level, bool debug,
+ const std::string_view& code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
+{
+ const char* target;
+ switch (feature_level)
+ {
+ case D3D_FEATURE_LEVEL_10_0:
+ {
+ static constexpr std::array targets = {{"vs_4_0", "gs_4_0", "ps_4_0", "cs_4_0"}};
+ target = targets[static_cast(type)];
+ }
+ break;
+
+ case D3D_FEATURE_LEVEL_10_1:
+ {
+ static constexpr std::array targets = {{"vs_4_1", "gs_4_1", "ps_4_1", "cs_4_1"}};
+ target = targets[static_cast(type)];
+ }
+ break;
+
+ case D3D_FEATURE_LEVEL_11_0:
+ {
+ static constexpr std::array targets = {{"vs_5_0", "gs_5_0", "ps_5_0", "cs_5_0"}};
+ target = targets[static_cast(type)];
+ }
+ break;
+
+ case D3D_FEATURE_LEVEL_11_1:
+ default:
+ {
+ static constexpr std::array targets = {{"vs_5_1", "gs_5_1", "ps_5_1", "cs_5_1"}};
+ target = targets[static_cast(type)];
+ }
+ break;
+ }
+
+ static constexpr UINT flags_non_debug = D3DCOMPILE_OPTIMIZATION_LEVEL3;
+ static constexpr UINT flags_debug = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG;
+
+ wil::com_ptr_nothrow blob;
+ wil::com_ptr_nothrow error_blob;
+ const HRESULT hr =
+ D3DCompile(code.data(), code.size(), "0", macros, nullptr, entry_point, target, debug ? flags_debug : flags_non_debug,
+ 0, blob.put(), error_blob.put());
+
+ std::string error_string;
+ if (error_blob)
+ {
+ error_string.append(static_cast(error_blob->GetBufferPointer()), error_blob->GetBufferSize());
+ error_blob.reset();
+ }
+
+ if (FAILED(hr))
+ {
+ Console.WriteLn("Failed to compile '%s':\n%s", target, error_string.c_str());
+
+ std::ofstream ofs(StringUtil::StdStringFromFormat("bad_shader_%u.txt", s_next_bad_shader_id++).c_str(),
+ std::ofstream::out | std::ofstream::binary);
+ if (ofs.is_open())
+ {
+ ofs << code;
+ ofs << "\n\nCompile as " << target << " failed: " << hr << "\n";
+ ofs.write(error_string.c_str(), error_string.size());
+ ofs.close();
+ }
+
+ return {};
+ }
+
+ if (!error_string.empty())
+ Console.Warning("'%s' compiled with warnings:\n%s", target, error_string.c_str());
+
+ return blob;
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CompileAndCreateVertexShader(ID3D11Device* device, bool debug,
+ const std::string_view& code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
+{
+ wil::com_ptr_nothrow blob = CompileShader(Type::Vertex, device->GetFeatureLevel(), debug, code, macros, entry_point);
+ if (!blob)
+ return {};
+
+ return CreateVertexShader(device, blob.get());
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CompileAndCreateGeometryShader(ID3D11Device* device, bool debug,
+ const std::string_view& code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
+{
+ wil::com_ptr_nothrow blob = CompileShader(Type::Geometry, device->GetFeatureLevel(), debug, code, macros, entry_point);
+ if (!blob)
+ return {};
+
+ return CreateGeometryShader(device, blob.get());
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CompileAndCreatePixelShader(ID3D11Device* device, bool debug,
+ const std::string_view& code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
+{
+ wil::com_ptr_nothrow blob = CompileShader(Type::Pixel, device->GetFeatureLevel(), debug, code, macros, entry_point);
+ if (!blob)
+ return {};
+
+ return CreatePixelShader(device, blob.get());
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CompileAndCreateComputeShader(ID3D11Device* device, bool debug,
+ const std::string_view& code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
+{
+ wil::com_ptr_nothrow blob = CompileShader(Type::Compute, device->GetFeatureLevel(), debug, code, macros, entry_point);
+ if (!blob)
+ return {};
+
+ return CreateComputeShader(device, blob.get());
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CreateVertexShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length)
+{
+ wil::com_ptr_nothrow shader;
+ const HRESULT hr = device->CreateVertexShader(bytecode, bytecode_length, nullptr, shader.put());
+ if (FAILED(hr))
+ {
+ Console.Error("Failed to create vertex shader: 0x%08X", hr);
+ return {};
+ }
+
+ return shader;
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CreateVertexShader(ID3D11Device* device, const ID3DBlob* blob)
+{
+ return CreateVertexShader(device, const_cast(blob)->GetBufferPointer(),
+ const_cast(blob)->GetBufferSize());
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CreateGeometryShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length)
+{
+ wil::com_ptr_nothrow shader;
+ const HRESULT hr = device->CreateGeometryShader(bytecode, bytecode_length, nullptr, shader.put());
+ if (FAILED(hr))
+ {
+ Console.Error("Failed to create geometry shader: 0x%08X", hr);
+ return {};
+ }
+
+ return shader;
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CreateGeometryShader(ID3D11Device* device, const ID3DBlob* blob)
+{
+ return CreateGeometryShader(device, const_cast(blob)->GetBufferPointer(),
+ const_cast(blob)->GetBufferSize());
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CreatePixelShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length)
+{
+ wil::com_ptr_nothrow shader;
+ const HRESULT hr = device->CreatePixelShader(bytecode, bytecode_length, nullptr, shader.put());
+ if (FAILED(hr))
+ {
+ Console.Error("Failed to create pixel shader: 0x%08X", hr);
+ return {};
+ }
+
+ return shader;
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CreatePixelShader(ID3D11Device* device, const ID3DBlob* blob)
+{
+ return CreatePixelShader(device, const_cast(blob)->GetBufferPointer(),
+ const_cast(blob)->GetBufferSize());
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CreateComputeShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length)
+{
+ wil::com_ptr_nothrow shader;
+ const HRESULT hr = device->CreateComputeShader(bytecode, bytecode_length, nullptr, shader.put());
+ if (FAILED(hr))
+ {
+ Console.Error("Failed to create compute shader: 0x%08X", hr);
+ return {};
+ }
+
+ return shader;
+}
+
+wil::com_ptr_nothrow D3D11::ShaderCompiler::CreateComputeShader(ID3D11Device* device, const ID3DBlob* blob)
+{
+ return CreateComputeShader(device, const_cast(blob)->GetBufferPointer(),
+ const_cast(blob)->GetBufferSize());
+}
diff --git a/common/D3D11/ShaderCompiler.h b/common/D3D11/ShaderCompiler.h
new file mode 100644
index 0000000000..1c50b88e2e
--- /dev/null
+++ b/common/D3D11/ShaderCompiler.h
@@ -0,0 +1,53 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2022 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#pragma once
+#include "common/RedtapeWindows.h"
+#include
+#include
+#include
+#include
+
+namespace D3D11::ShaderCompiler
+{
+ enum class Type
+ {
+ Vertex,
+ Geometry,
+ Pixel,
+ Compute
+ };
+
+ wil::com_ptr_nothrow CompileShader(Type type, D3D_FEATURE_LEVEL feature_level, bool debug, const std::string_view& code,
+ const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
+
+ wil::com_ptr_nothrow CompileAndCreateVertexShader(ID3D11Device* device, bool debug, const std::string_view& code,
+ const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
+ wil::com_ptr_nothrow CompileAndCreateGeometryShader(ID3D11Device* device, bool debug, const std::string_view& code,
+ const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
+ wil::com_ptr_nothrow CompileAndCreatePixelShader(ID3D11Device* device, bool debug, const std::string_view& code,
+ const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
+ wil::com_ptr_nothrow CompileAndCreateComputeShader(ID3D11Device* device, bool debug, const std::string_view& code,
+ const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
+
+ wil::com_ptr_nothrow CreateVertexShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length);
+ wil::com_ptr_nothrow CreateVertexShader(ID3D11Device* device, const ID3DBlob* blob);
+ wil::com_ptr_nothrow CreateGeometryShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length);
+ wil::com_ptr_nothrow CreateGeometryShader(ID3D11Device* device, const ID3DBlob* blob);
+ wil::com_ptr_nothrow CreatePixelShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length);
+ wil::com_ptr_nothrow CreatePixelShader(ID3D11Device* device, const ID3DBlob* blob);
+ wil::com_ptr_nothrow CreateComputeShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length);
+ wil::com_ptr_nothrow CreateComputeShader(ID3D11Device* device, const ID3DBlob* blob);
+}; // namespace D3D11::ShaderCompiler
diff --git a/common/common.vcxproj b/common/common.vcxproj
index 738da06a8a..1fec737582 100644
--- a/common/common.vcxproj
+++ b/common/common.vcxproj
@@ -45,6 +45,8 @@
+
+
@@ -120,6 +122,8 @@
+
+
diff --git a/common/common.vcxproj.filters b/common/common.vcxproj.filters
index 8ccf0416f3..764ed08887 100644
--- a/common/common.vcxproj.filters
+++ b/common/common.vcxproj.filters
@@ -181,6 +181,12 @@
Source Files\Vulkan
+
+ Source Files\D3D11
+
+
+ Source Files\D3D11
+
@@ -411,6 +417,12 @@
Header Files\GL
+
+ Header Files\D3D11
+
+
+ Header Files\D3D11
+
@@ -431,6 +443,12 @@
{46f36c68-0e0e-4acd-a621-3365e3167c4f}
+
+ {f428aac0-c9c5-4b66-b218-9829dce45d13}
+
+
+ {764ddf71-77a6-41b8-bc65-1eef94b6997b}
+