GPUDevice: Add API version field

Also tie shader caches to API version and device LUID. That way we don't
have tons of cache files, and they're regenerated if the GPU/driver
changes.
This commit is contained in:
Stenzek 2024-09-08 23:33:05 +10:00
parent 4ec5ca4319
commit 4de1045693
No known key found for this signature in database
23 changed files with 222 additions and 195 deletions

View file

@ -111,6 +111,18 @@ void SmallStringBase::shrink_to_fit()
m_buffer_size = buffer_size; m_buffer_size = buffer_size;
} }
void SmallStringBase::convert_to_lower_case()
{
for (u32 i = 0; i < m_length; i++)
m_buffer[i] = static_cast<char>(std::tolower(m_buffer[i]));
}
void SmallStringBase::convert_to_upper_case()
{
for (u32 i = 0; i < m_length; i++)
m_buffer[i] = static_cast<char>(std::toupper(m_buffer[i]));
}
std::string_view SmallStringBase::view() const std::string_view SmallStringBase::view() const
{ {
return (m_length == 0) ? std::string_view() : std::string_view(m_buffer, m_length); return (m_length == 0) ? std::string_view() : std::string_view(m_buffer, m_length);

View file

@ -188,6 +188,10 @@ public:
ALWAYS_INLINE void push_back(value_type val) { append(val); } ALWAYS_INLINE void push_back(value_type val) { append(val); }
ALWAYS_INLINE void pop_back() { erase(-1); } ALWAYS_INLINE void pop_back() { erase(-1); }
// case conversion
void convert_to_lower_case();
void convert_to_upper_case();
// returns a string view for this string // returns a string view for this string
std::string_view view() const; std::string_view view() const;

View file

@ -21,7 +21,7 @@ struct WindowInfo;
enum class AudioBackend : u8; enum class AudioBackend : u8;
enum class AudioExpansionMode : u8; enum class AudioExpansionMode : u8;
enum class AudioStretchMode : u8; enum class AudioStretchMode : u8;
enum class RenderAPI : u32; enum class RenderAPI : u8;
class AudioStream; class AudioStream;
class CDImage; class CDImage;

View file

@ -18,7 +18,7 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
enum class RenderAPI : u32; enum class RenderAPI : u8;
enum class MediaCaptureBackend : u8; enum class MediaCaptureBackend : u8;
struct SettingInfo struct SettingInfo

View file

@ -53,11 +53,6 @@ D3D11Device::~D3D11Device()
Assert(!m_device); Assert(!m_device);
} }
RenderAPI D3D11Device::GetRenderAPI() const
{
return RenderAPI::D3D11;
}
bool D3D11Device::HasSurface() const bool D3D11Device::HasSurface() const
{ {
return static_cast<bool>(m_swap_chain); return static_cast<bool>(m_swap_chain);
@ -127,7 +122,8 @@ bool D3D11Device::CreateDevice(std::string_view adapter, std::optional<bool> exc
INFO_LOG("D3D Adapter: {}", D3DCommon::GetAdapterName(dxgi_adapter.Get())); INFO_LOG("D3D Adapter: {}", D3DCommon::GetAdapterName(dxgi_adapter.Get()));
else else
ERROR_LOG("Failed to obtain D3D adapter name."); ERROR_LOG("Failed to obtain D3D adapter name.");
INFO_LOG("Max device feature level: {}", D3DCommon::GetFeatureLevelString(m_max_feature_level)); INFO_LOG("Max device feature level: {}",
D3DCommon::GetFeatureLevelString(D3DCommon::GetRenderAPIVersionForFeatureLevel(m_max_feature_level)));
BOOL allow_tearing_supported = false; BOOL allow_tearing_supported = false;
hr = m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, hr = m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported,
@ -164,6 +160,8 @@ void D3D11Device::SetFeatures(FeatureMask disabled_features)
{ {
const D3D_FEATURE_LEVEL feature_level = m_device->GetFeatureLevel(); const D3D_FEATURE_LEVEL feature_level = m_device->GetFeatureLevel();
m_render_api = RenderAPI::D3D11;
m_render_api_version = D3DCommon::GetRenderAPIVersionForFeatureLevel(feature_level);
m_max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; m_max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
m_max_multisamples = 1; m_max_multisamples = 1;
for (u32 multisamples = 2; multisamples < D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; multisamples++) for (u32 multisamples = 2; multisamples < D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; multisamples++)
@ -443,12 +441,11 @@ bool D3D11Device::SupportsExclusiveFullscreen() const
std::string D3D11Device::GetDriverInfo() const std::string D3D11Device::GetDriverInfo() const
{ {
const D3D_FEATURE_LEVEL fl = m_device->GetFeatureLevel(); std::string ret = fmt::format("{} (Shader Model {})\n", D3DCommon::GetFeatureLevelString(m_render_api_version),
std::string ret = D3DCommon::GetShaderModelForFeatureLevelNumber(m_render_api_version));
fmt::format("{} ({})\n", D3DCommon::GetFeatureLevelString(fl), D3DCommon::GetFeatureLevelShaderModelString(fl));
ComPtr<IDXGIDevice> dxgi_dev; ComPtr<IDXGIDevice> dxgi_dev;
if (m_device.As(&dxgi_dev)) if (SUCCEEDED(m_device.As(&dxgi_dev)))
{ {
ComPtr<IDXGIAdapter> dxgi_adapter; ComPtr<IDXGIAdapter> dxgi_adapter;
if (SUCCEEDED(dxgi_dev->GetAdapter(dxgi_adapter.GetAddressOf()))) if (SUCCEEDED(dxgi_dev->GetAdapter(dxgi_adapter.GetAddressOf())))

View file

@ -36,8 +36,6 @@ public:
ALWAYS_INLINE static ID3D11DeviceContext1* GetD3DContext() { return GetInstance().m_context.Get(); } ALWAYS_INLINE static ID3D11DeviceContext1* GetD3DContext() { return GetInstance().m_context.Get(); }
ALWAYS_INLINE static D3D_FEATURE_LEVEL GetMaxFeatureLevel() { return GetInstance().m_max_feature_level; } ALWAYS_INLINE static D3D_FEATURE_LEVEL GetMaxFeatureLevel() { return GetInstance().m_max_feature_level; }
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override; bool HasSurface() const override;
bool UpdateWindow() override; bool UpdateWindow() override;

View file

@ -100,7 +100,7 @@ std::unique_ptr<GPUShader> D3D11Device::CreateShaderFromSource(GPUShaderStage st
std::string_view source, const char* entry_point, std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error) DynamicHeapArray<u8>* out_binary, Error* error)
{ {
const u32 shader_model = D3DCommon::GetShaderModelForFeatureLevel(m_device->GetFeatureLevel()); const u32 shader_model = D3DCommon::GetShaderModelForFeatureLevelNumber(m_render_api_version);
if (language != GPUShaderLanguage::HLSL) if (language != GPUShaderLanguage::HLSL)
{ {
return TranspileAndCreateShaderFromSource(stage, language, source, entry_point, GPUShaderLanguage::HLSL, return TranspileAndCreateShaderFromSource(stage, language, source, entry_point, GPUShaderLanguage::HLSL,

View file

@ -147,12 +147,13 @@ bool D3D12Device::CreateDevice(std::string_view adapter, std::optional<bool> exc
} }
// Create the actual device. // Create the actual device.
D3D_FEATURE_LEVEL feature_level = D3D_FEATURE_LEVEL_1_0_CORE;
for (D3D_FEATURE_LEVEL try_feature_level : {D3D_FEATURE_LEVEL_11_0}) for (D3D_FEATURE_LEVEL try_feature_level : {D3D_FEATURE_LEVEL_11_0})
{ {
hr = D3D12CreateDevice(m_adapter.Get(), try_feature_level, IID_PPV_ARGS(&m_device)); hr = D3D12CreateDevice(m_adapter.Get(), try_feature_level, IID_PPV_ARGS(&m_device));
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
m_feature_level = try_feature_level; feature_level = try_feature_level;
break; break;
} }
} }
@ -232,7 +233,7 @@ bool D3D12Device::CreateDevice(std::string_view adapter, std::optional<bool> exc
return false; return false;
} }
SetFeatures(disabled_features); SetFeatures(feature_level, disabled_features);
if (!CreateCommandLists(error) || !CreateDescriptorHeaps(error)) if (!CreateCommandLists(error) || !CreateDescriptorHeaps(error))
return false; return false;
@ -282,10 +283,28 @@ void D3D12Device::DestroyDevice()
m_dxgi_factory.Reset(); m_dxgi_factory.Reset();
} }
void D3D12Device::GetPipelineCacheHeader(PIPELINE_CACHE_HEADER* hdr)
{
const LUID adapter_luid = m_device->GetAdapterLuid();
std::memcpy(&hdr->adapter_luid, &adapter_luid, sizeof(hdr->adapter_luid));
hdr->render_api_version = m_render_api_version;
hdr->unused = 0;
}
bool D3D12Device::ReadPipelineCache(std::optional<DynamicHeapArray<u8>> data) bool D3D12Device::ReadPipelineCache(std::optional<DynamicHeapArray<u8>> data)
{ {
PIPELINE_CACHE_HEADER expected_header;
GetPipelineCacheHeader(&expected_header);
if (data.has_value() && (data->size() < sizeof(PIPELINE_CACHE_HEADER) ||
std::memcmp(data->data(), &expected_header, sizeof(PIPELINE_CACHE_HEADER)) != 0))
{
WARNING_LOG("Pipeline cache header does not match current device, ignoring.");
data.reset();
}
HRESULT hr = HRESULT hr =
m_device->CreatePipelineLibrary(data.has_value() ? data->data() : nullptr, data.has_value() ? data->size() : 0, m_device->CreatePipelineLibrary(data.has_value() ? (data->data() + sizeof(PIPELINE_CACHE_HEADER)) : nullptr,
data.has_value() ? (data->size() - sizeof(PIPELINE_CACHE_HEADER)) : 0,
IID_PPV_ARGS(m_pipeline_library.ReleaseAndGetAddressOf())); IID_PPV_ARGS(m_pipeline_library.ReleaseAndGetAddressOf()));
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
@ -328,8 +347,13 @@ bool D3D12Device::GetPipelineCacheData(DynamicHeapArray<u8>* data)
return true; return true;
} }
data->resize(size); PIPELINE_CACHE_HEADER header;
const HRESULT hr = m_pipeline_library->Serialize(data->data(), data->size()); GetPipelineCacheHeader(&header);
data->resize(sizeof(PIPELINE_CACHE_HEADER) + size);
std::memcpy(data->data(), &header, sizeof(PIPELINE_CACHE_HEADER));
const HRESULT hr = m_pipeline_library->Serialize(data->data() + sizeof(PIPELINE_CACHE_HEADER), size);
if (FAILED(hr)) if (FAILED(hr))
{ {
ERROR_LOG("Serialize() failed with HRESULT {:08X}", static_cast<unsigned>(hr)); ERROR_LOG("Serialize() failed with HRESULT {:08X}", static_cast<unsigned>(hr));
@ -771,11 +795,6 @@ void D3D12Device::DestroyDeferredObjects(u64 fence_value)
} }
} }
RenderAPI D3D12Device::GetRenderAPI() const
{
return RenderAPI::D3D12;
}
bool D3D12Device::HasSurface() const bool D3D12Device::HasSurface() const
{ {
return static_cast<bool>(m_swap_chain); return static_cast<bool>(m_swap_chain);
@ -1070,8 +1089,8 @@ bool D3D12Device::SupportsTextureFormat(GPUTexture::Format format) const
std::string D3D12Device::GetDriverInfo() const std::string D3D12Device::GetDriverInfo() const
{ {
std::string ret = fmt::format("{} ({})\n", D3DCommon::GetFeatureLevelString(m_feature_level), std::string ret = fmt::format("{} (Shader Model {})\n", D3DCommon::GetFeatureLevelString(m_render_api_version),
D3DCommon::GetFeatureLevelShaderModelString(m_feature_level)); D3DCommon::GetShaderModelForFeatureLevelNumber(m_render_api_version));
DXGI_ADAPTER_DESC desc; DXGI_ADAPTER_DESC desc;
if (m_adapter && SUCCEEDED(m_adapter->GetDesc(&desc))) if (m_adapter && SUCCEEDED(m_adapter->GetDesc(&desc)))
@ -1234,8 +1253,10 @@ void D3D12Device::InsertDebugMessage(const char* msg)
#endif #endif
} }
void D3D12Device::SetFeatures(FeatureMask disabled_features) void D3D12Device::SetFeatures(D3D_FEATURE_LEVEL feature_level, FeatureMask disabled_features)
{ {
m_render_api = RenderAPI::D3D12;
m_render_api_version = D3DCommon::GetRenderAPIVersionForFeatureLevel(feature_level);
m_max_texture_size = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION; m_max_texture_size = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION;
m_max_multisamples = 1; m_max_multisamples = 1;
for (u32 multisamples = 2; multisamples < D3D12_MAX_MULTISAMPLE_SAMPLE_COUNT; multisamples++) for (u32 multisamples = 2; multisamples < D3D12_MAX_MULTISAMPLE_SAMPLE_COUNT; multisamples++)

View file

@ -58,8 +58,6 @@ public:
D3D12Device(); D3D12Device();
~D3D12Device() override; ~D3D12Device() override;
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override; bool HasSurface() const override;
bool UpdateWindow() override; bool UpdateWindow() override;
@ -220,9 +218,18 @@ private:
bool has_timestamp_query = false; bool has_timestamp_query = false;
}; };
struct PIPELINE_CACHE_HEADER
{
u64 adapter_luid;
u32 render_api_version;
u32 unused;
};
static_assert(sizeof(PIPELINE_CACHE_HEADER) == 16);
using SamplerMap = std::unordered_map<u64, D3D12DescriptorHandle>; using SamplerMap = std::unordered_map<u64, D3D12DescriptorHandle>;
void SetFeatures(FeatureMask disabled_features); void GetPipelineCacheHeader(PIPELINE_CACHE_HEADER* hdr);
void SetFeatures(D3D_FEATURE_LEVEL feature_level, FeatureMask disabled_features);
u32 GetSwapChainBufferCount() const; u32 GetSwapChainBufferCount() const;
bool CreateSwapChain(Error* error); bool CreateSwapChain(Error* error);
@ -291,7 +298,6 @@ private:
std::array<CommandList, NUM_COMMAND_LISTS> m_command_lists; std::array<CommandList, NUM_COMMAND_LISTS> m_command_lists;
u32 m_current_command_list = NUM_COMMAND_LISTS - 1; u32 m_current_command_list = NUM_COMMAND_LISTS - 1;
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
ComPtr<IDXGIFactory5> m_dxgi_factory; ComPtr<IDXGIFactory5> m_dxgi_factory;
ComPtr<IDXGISwapChain1> m_swap_chain; ComPtr<IDXGISwapChain1> m_swap_chain;

View file

@ -38,7 +38,7 @@ std::unique_ptr<GPUShader> D3D12Device::CreateShaderFromSource(GPUShaderStage st
std::string_view source, const char* entry_point, std::string_view source, const char* entry_point,
DynamicHeapArray<u8>* out_binary, Error* error) DynamicHeapArray<u8>* out_binary, Error* error)
{ {
const u32 shader_model = D3DCommon::GetShaderModelForFeatureLevel(m_feature_level); const u32 shader_model = D3DCommon::GetShaderModelForFeatureLevelNumber(m_render_api_version);
if (language != GPUShaderLanguage::HLSL) if (language != GPUShaderLanguage::HLSL)
{ {
return TranspileAndCreateShaderFromSource(stage, language, source, entry_point, GPUShaderLanguage::HLSL, return TranspileAndCreateShaderFromSource(stage, language, source, entry_point, GPUShaderLanguage::HLSL,

View file

@ -19,47 +19,65 @@
Log_SetChannel(D3DCommon); Log_SetChannel(D3DCommon);
const char* D3DCommon::GetFeatureLevelString(D3D_FEATURE_LEVEL feature_level) namespace D3DCommon {
namespace {
struct FeatureLevelTableEntry
{ {
static constexpr std::array<std::tuple<D3D_FEATURE_LEVEL, const char*>, 11> feature_level_names = {{ D3D_FEATURE_LEVEL d3d_feature_level;
{D3D_FEATURE_LEVEL_1_0_CORE, "D3D_FEATURE_LEVEL_1_0_CORE"}, u16 render_api_version;
{D3D_FEATURE_LEVEL_9_1, "D3D_FEATURE_LEVEL_9_1"}, u16 shader_model_number;
{D3D_FEATURE_LEVEL_9_2, "D3D_FEATURE_LEVEL_9_2"}, const char* feature_level_str;
{D3D_FEATURE_LEVEL_9_3, "D3D_FEATURE_LEVEL_9_3"}, };
{D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_0"}, } // namespace
{D3D_FEATURE_LEVEL_10_1, "D3D_FEATURE_LEVEL_10_1"},
{D3D_FEATURE_LEVEL_11_0, "D3D_FEATURE_LEVEL_11_0"},
{D3D_FEATURE_LEVEL_11_1, "D3D_FEATURE_LEVEL_11_1"},
{D3D_FEATURE_LEVEL_12_0, "D3D_FEATURE_LEVEL_12_0"},
{D3D_FEATURE_LEVEL_12_1, "D3D_FEATURE_LEVEL_12_1"},
{D3D_FEATURE_LEVEL_12_2, "D3D_FEATURE_LEVEL_12_2"},
}};
for (const auto& [fl, name] : feature_level_names) static constexpr std::array<FeatureLevelTableEntry, 11> s_feature_levels = {{
{ {D3D_FEATURE_LEVEL_1_0_CORE, 100, 40, "D3D_FEATURE_LEVEL_1_0_CORE"},
if (fl == feature_level) {D3D_FEATURE_LEVEL_9_1, 910, 40, "D3D_FEATURE_LEVEL_9_1"},
return name; {D3D_FEATURE_LEVEL_9_2, 920, 40, "D3D_FEATURE_LEVEL_9_2"},
} {D3D_FEATURE_LEVEL_9_3, 930, 40, "D3D_FEATURE_LEVEL_9_3"},
{D3D_FEATURE_LEVEL_10_0, 1000, 40, "D3D_FEATURE_LEVEL_10_0"},
{D3D_FEATURE_LEVEL_10_1, 1010, 41, "D3D_FEATURE_LEVEL_10_1"},
{D3D_FEATURE_LEVEL_11_0, 1100, 50, "D3D_FEATURE_LEVEL_11_0"},
{D3D_FEATURE_LEVEL_11_1, 1110, 50, "D3D_FEATURE_LEVEL_11_1"},
{D3D_FEATURE_LEVEL_12_0, 1200, 50, "D3D_FEATURE_LEVEL_12_0"},
{D3D_FEATURE_LEVEL_12_1, 1210, 50, "D3D_FEATURE_LEVEL_12_1"},
{D3D_FEATURE_LEVEL_12_2, 1220, 50, "D3D_FEATURE_LEVEL_12_2"},
}};
} // namespace D3DCommon
return "D3D_FEATURE_LEVEL_UNKNOWN"; const char* D3DCommon::GetFeatureLevelString(u32 render_api_version)
{
const auto iter =
std::find_if(s_feature_levels.begin(), s_feature_levels.end(),
[&render_api_version](const auto& it) { return it.render_api_version == render_api_version; });
return (iter != s_feature_levels.end()) ? iter->feature_level_str : "D3D_FEATURE_LEVEL_UNKNOWN";
} }
const char* D3DCommon::GetFeatureLevelShaderModelString(D3D_FEATURE_LEVEL feature_level) u32 D3DCommon::GetRenderAPIVersionForFeatureLevel(D3D_FEATURE_LEVEL feature_level)
{ {
static constexpr std::array<std::tuple<D3D_FEATURE_LEVEL, const char*>, 4> feature_level_names = {{ const FeatureLevelTableEntry* highest_entry = nullptr;
{D3D_FEATURE_LEVEL_10_0, "sm40"}, for (const FeatureLevelTableEntry& entry : s_feature_levels)
{D3D_FEATURE_LEVEL_10_1, "sm41"},
{D3D_FEATURE_LEVEL_11_0, "sm50"},
{D3D_FEATURE_LEVEL_11_1, "sm50"},
}};
for (const auto& [fl, name] : feature_level_names)
{ {
if (fl == feature_level) if (feature_level >= entry.d3d_feature_level)
return name; highest_entry = &entry;
} }
return highest_entry ? highest_entry->render_api_version : 0;
}
return "unk"; D3D_FEATURE_LEVEL D3DCommon::GetFeatureLevelForNumber(u32 render_api_version)
{
const auto iter =
std::find_if(s_feature_levels.begin(), s_feature_levels.end(),
[&render_api_version](const auto& it) { return it.render_api_version == render_api_version; });
return (iter != s_feature_levels.end()) ? iter->d3d_feature_level : D3D_FEATURE_LEVEL_1_0_CORE;
}
u32 D3DCommon::GetShaderModelForFeatureLevelNumber(u32 render_api_version)
{
const auto iter =
std::find_if(s_feature_levels.begin(), s_feature_levels.end(),
[&render_api_version](const auto& it) { return it.render_api_version == render_api_version; });
return (iter != s_feature_levels.end()) ? iter->shader_model_number : 40;
} }
D3D_FEATURE_LEVEL D3DCommon::GetDeviceMaxFeatureLevel(IDXGIAdapter1* adapter) D3D_FEATURE_LEVEL D3DCommon::GetDeviceMaxFeatureLevel(IDXGIAdapter1* adapter)
@ -379,23 +397,6 @@ std::string D3DCommon::GetDriverVersionFromLUID(const LUID& luid)
return ret; return ret;
} }
u32 D3DCommon::GetShaderModelForFeatureLevel(D3D_FEATURE_LEVEL feature_level)
{
switch (feature_level)
{
case D3D_FEATURE_LEVEL_10_0:
return 40;
case D3D_FEATURE_LEVEL_10_1:
return 41;
case D3D_FEATURE_LEVEL_11_0:
case D3D_FEATURE_LEVEL_11_1:
default:
return 50;
}
}
std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShader(u32 shader_model, bool debug_device, GPUShaderStage stage, std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShader(u32 shader_model, bool debug_device, GPUShaderStage stage,
std::string_view source, const char* entry_point, std::string_view source, const char* entry_point,
Error* error) Error* error)

View file

@ -25,8 +25,10 @@ struct DXGI_MODE_DESC;
namespace D3DCommon { namespace D3DCommon {
// returns string representation of feature level // returns string representation of feature level
const char* GetFeatureLevelString(D3D_FEATURE_LEVEL feature_level); const char* GetFeatureLevelString(u32 render_api_version);
const char* GetFeatureLevelShaderModelString(D3D_FEATURE_LEVEL feature_level); u32 GetRenderAPIVersionForFeatureLevel(D3D_FEATURE_LEVEL feature_level);
D3D_FEATURE_LEVEL GetFeatureLevelForNumber(u32 render_api_version);
u32 GetShaderModelForFeatureLevelNumber(u32 render_api_version);
// returns max feature level of a device // returns max feature level of a device
D3D_FEATURE_LEVEL GetDeviceMaxFeatureLevel(IDXGIAdapter1* adapter); D3D_FEATURE_LEVEL GetDeviceMaxFeatureLevel(IDXGIAdapter1* adapter);
@ -57,8 +59,6 @@ std::string GetAdapterName(IDXGIAdapter1* adapter);
// returns the driver version from the registry as a string // returns the driver version from the registry as a string
std::string GetDriverVersionFromLUID(const LUID& luid); std::string GetDriverVersionFromLUID(const LUID& luid);
u32 GetShaderModelForFeatureLevel(D3D_FEATURE_LEVEL feature_level);
std::optional<DynamicHeapArray<u8>> CompileShader(u32 shader_model, bool debug_device, GPUShaderStage stage, std::optional<DynamicHeapArray<u8>> CompileShader(u32 shader_model, bool debug_device, GPUShaderStage stage,
std::string_view source, const char* entry_point, Error* error); std::string_view source, const char* entry_point, Error* error);

View file

@ -367,6 +367,7 @@ bool GPUDevice::Create(std::string_view adapter, std::string_view shader_cache_p
return false; return false;
} }
INFO_LOG("Render API: {} Version {}", RenderAPIToString(m_render_api), m_render_api_version);
INFO_LOG("Graphics Driver Info:\n{}", GetDriverInfo()); INFO_LOG("Graphics Driver Info:\n{}", GetDriverInfo());
OpenShaderCache(shader_cache_path, shader_cache_version); OpenShaderCache(shader_cache_path, shader_cache_version);
@ -401,7 +402,7 @@ void GPUDevice::OpenShaderCache(std::string_view base_path, u32 version)
{ {
const std::string basename = GetShaderCacheBaseName("shaders"); const std::string basename = GetShaderCacheBaseName("shaders");
const std::string filename = Path::Combine(base_path, basename); const std::string filename = Path::Combine(base_path, basename);
if (!m_shader_cache.Open(filename.c_str(), version)) if (!m_shader_cache.Open(filename.c_str(), m_render_api_version, version))
{ {
WARNING_LOG("Failed to open shader cache. Creating new cache."); WARNING_LOG("Failed to open shader cache. Creating new cache.");
if (!m_shader_cache.Create()) if (!m_shader_cache.Create())
@ -423,7 +424,7 @@ void GPUDevice::OpenShaderCache(std::string_view base_path, u32 version)
else else
{ {
// Still need to set the version - GL needs it. // Still need to set the version - GL needs it.
m_shader_cache.Open(std::string_view(), version); m_shader_cache.Open(std::string_view(), m_render_api_version, version);
} }
s_pipeline_cache_path = {}; s_pipeline_cache_path = {};
@ -473,52 +474,23 @@ std::string GPUDevice::GetShaderCacheBaseName(std::string_view type) const
{ {
const std::string_view debug_suffix = m_debug_device ? "_debug" : ""; const std::string_view debug_suffix = m_debug_device ? "_debug" : "";
std::string ret; TinyString lower_api_name(RenderAPIToString(m_render_api));
switch (GetRenderAPI()) lower_api_name.convert_to_lower_case();
{
#ifdef _WIN32
case RenderAPI::D3D11:
ret = fmt::format(
"d3d11_{}_{}{}", type,
D3DCommon::GetFeatureLevelShaderModelString(D3D11Device::GetInstance().GetD3DDevice()->GetFeatureLevel()),
debug_suffix);
break;
case RenderAPI::D3D12:
ret = fmt::format("d3d12_{}{}", type, debug_suffix);
break;
#endif
#ifdef ENABLE_VULKAN
case RenderAPI::Vulkan:
ret = fmt::format("vulkan_{}{}", type, debug_suffix);
break;
#endif
#ifdef ENABLE_OPENGL
case RenderAPI::OpenGL:
ret = fmt::format("opengl_{}{}", type, debug_suffix);
break;
case RenderAPI::OpenGLES:
ret = fmt::format("opengles_{}{}", type, debug_suffix);
break;
#endif
#ifdef __APPLE__
case RenderAPI::Metal:
ret = fmt::format("metal_{}{}", type, debug_suffix);
break;
#endif
default:
UnreachableCode();
break;
}
return ret; return fmt::format("{}_{}{}", lower_api_name, type, debug_suffix);
} }
bool GPUDevice::OpenPipelineCache(const std::string& filename) bool GPUDevice::OpenPipelineCache(const std::string& filename)
{ {
Error error; s_pipeline_cache_size = 0;
CompressHelpers::OptionalByteBuffer data = s_pipeline_cache_hash = {};
CompressHelpers::DecompressFile(CompressHelpers::CompressType::Zstandard, filename.c_str(), std::nullopt, &error);
Error error;
CompressHelpers::OptionalByteBuffer data;
if (FileSystem::FileExists(filename.c_str()))
{
data =
CompressHelpers::DecompressFile(CompressHelpers::CompressType::Zstandard, filename.c_str(), std::nullopt, &error);
if (data.has_value()) if (data.has_value())
{ {
s_pipeline_cache_size = data->size(); s_pipeline_cache_size = data->size();
@ -527,20 +499,22 @@ bool GPUDevice::OpenPipelineCache(const std::string& filename)
else else
{ {
ERROR_LOG("Failed to load pipeline cache from '{}': {}", Path::GetFileName(filename), error.GetDescription()); ERROR_LOG("Failed to load pipeline cache from '{}': {}", Path::GetFileName(filename), error.GetDescription());
s_pipeline_cache_size = 0; }
s_pipeline_cache_hash = {};
} }
if (!ReadPipelineCache(std::move(data))) INFO_LOG("Loading {} byte pipeline cache with hash {}", s_pipeline_cache_size,
SHA1Digest::DigestToString(s_pipeline_cache_hash));
if (ReadPipelineCache(std::move(data)))
{
return true;
}
else
{ {
s_pipeline_cache_size = 0; s_pipeline_cache_size = 0;
s_pipeline_cache_hash = {}; s_pipeline_cache_hash = {};
return false; return false;
} }
INFO_LOG("Loaded pipeline cache: {} bytes with hash: {}", s_pipeline_cache_size,
SHA1Digest::DigestToString(s_pipeline_cache_hash));
return true;
} }
bool GPUDevice::ReadPipelineCache(std::optional<DynamicHeapArray<u8>> data) bool GPUDevice::ReadPipelineCache(std::optional<DynamicHeapArray<u8>> data)

View file

@ -25,7 +25,7 @@
class Error; class Error;
enum class RenderAPI : u32 enum class RenderAPI : u8
{ {
None, None,
D3D11, D3D11,
@ -594,6 +594,8 @@ public:
} }
ALWAYS_INLINE const Features& GetFeatures() const { return m_features; } ALWAYS_INLINE const Features& GetFeatures() const { return m_features; }
ALWAYS_INLINE RenderAPI GetRenderAPI() const { return m_render_api; }
ALWAYS_INLINE u32 GetRenderAPIVersion() const { return m_render_api_version; }
ALWAYS_INLINE u32 GetMaxTextureSize() const { return m_max_texture_size; } ALWAYS_INLINE u32 GetMaxTextureSize() const { return m_max_texture_size; }
ALWAYS_INLINE u32 GetMaxMultisamples() const { return m_max_multisamples; } ALWAYS_INLINE u32 GetMaxMultisamples() const { return m_max_multisamples; }
@ -608,8 +610,6 @@ public:
ALWAYS_INLINE bool IsGPUTimingEnabled() const { return m_gpu_timing_enabled; } ALWAYS_INLINE bool IsGPUTimingEnabled() const { return m_gpu_timing_enabled; }
virtual RenderAPI GetRenderAPI() const = 0;
bool Create(std::string_view adapter, std::string_view shader_cache_path, u32 shader_cache_version, bool debug_device, bool Create(std::string_view adapter, std::string_view shader_cache_path, u32 shader_cache_version, bool debug_device,
GPUVSyncMode vsync, bool allow_present_throttle, std::optional<bool> exclusive_fullscreen_control, GPUVSyncMode vsync, bool allow_present_throttle, std::optional<bool> exclusive_fullscreen_control,
FeatureMask disabled_features, Error* error); FeatureMask disabled_features, Error* error);
@ -776,6 +776,8 @@ protected:
static std::optional<DynamicHeapArray<u8>> OptimizeVulkanSpv(const std::span<const u8> spirv, Error* error); static std::optional<DynamicHeapArray<u8>> OptimizeVulkanSpv(const std::span<const u8> spirv, Error* error);
Features m_features = {}; Features m_features = {};
RenderAPI m_render_api = RenderAPI::None;
u32 m_render_api_version = 0;
u32 m_max_texture_size = 0; u32 m_max_texture_size = 0;
u32 m_max_multisamples = 0; u32 m_max_multisamples = 0;

View file

@ -18,6 +18,12 @@
Log_SetChannel(GPUShaderCache); Log_SetChannel(GPUShaderCache);
#pragma pack(push, 1) #pragma pack(push, 1)
struct CacheFileHeader
{
u32 signature;
u32 render_api_version;
u32 cache_version;
};
struct CacheIndexEntry struct CacheIndexEntry
{ {
u8 shader_type; u8 shader_type;
@ -34,6 +40,8 @@ struct CacheIndexEntry
}; };
#pragma pack(pop) #pragma pack(pop)
static constexpr u32 EXPECTED_SIGNATURE = 0x434B5544; // DUKC
GPUShaderCache::GPUShaderCache() = default; GPUShaderCache::GPUShaderCache() = default;
GPUShaderCache::~GPUShaderCache() GPUShaderCache::~GPUShaderCache()
@ -59,10 +67,11 @@ std::size_t GPUShaderCache::CacheIndexEntryHash::operator()(const CacheIndexKey&
return h; return h;
} }
bool GPUShaderCache::Open(std::string_view base_filename, u32 version) bool GPUShaderCache::Open(std::string_view base_filename, u32 render_api_version, u32 cache_version)
{ {
m_base_filename = base_filename; m_base_filename = base_filename;
m_version = version; m_render_api_version = render_api_version;
m_version = cache_version;
if (base_filename.empty()) if (base_filename.empty())
return true; return true;
@ -127,7 +136,9 @@ bool GPUShaderCache::CreateNew(const std::string& index_filename, const std::str
return false; return false;
} }
if (std::fwrite(&m_version, sizeof(m_version), 1, m_index_file) != 1) [[unlikely]] const CacheFileHeader file_header = {
.signature = EXPECTED_SIGNATURE, .render_api_version = m_render_api_version, .cache_version = m_version};
if (std::fwrite(&file_header, sizeof(file_header), 1, m_index_file) != 1) [[unlikely]]
{ {
ERROR_LOG("Failed to write version to index file '{}'", Path::GetFileName(index_filename)); ERROR_LOG("Failed to write version to index file '{}'", Path::GetFileName(index_filename));
std::fclose(m_index_file); std::fclose(m_index_file);
@ -165,8 +176,10 @@ bool GPUShaderCache::ReadExisting(const std::string& index_filename, const std::
return false; return false;
} }
u32 file_version = 0; CacheFileHeader file_header;
if (std::fread(&file_version, sizeof(file_version), 1, m_index_file) != 1 || file_version != m_version) [[unlikely]] if (std::fread(&file_header, sizeof(file_header), 1, m_index_file) != 1 ||
file_header.signature != EXPECTED_SIGNATURE || file_header.render_api_version != m_render_api_version ||
file_header.cache_version != m_version) [[unlikely]]
{ {
ERROR_LOG("Bad file/data version in '{}'", Path::GetFileName(index_filename)); ERROR_LOG("Bad file/data version in '{}'", Path::GetFileName(index_filename));
std::fclose(m_index_file); std::fclose(m_index_file);

View file

@ -50,7 +50,7 @@ public:
bool IsOpen() const { return (m_index_file != nullptr); } bool IsOpen() const { return (m_index_file != nullptr); }
bool Open(std::string_view base_filename, u32 version); bool Open(std::string_view base_filename, u32 render_api_version, u32 cache_version);
bool Create(); bool Create();
void Close(); void Close();
@ -77,6 +77,7 @@ private:
CacheIndex m_index; CacheIndex m_index;
std::string m_base_filename; std::string m_base_filename;
u32 m_render_api_version = 0;
u32 m_version = 0; u32 m_version = 0;
std::FILE* m_index_file = nullptr; std::FILE* m_index_file = nullptr;

View file

@ -198,8 +198,6 @@ public:
MetalDevice(); MetalDevice();
~MetalDevice(); ~MetalDevice();
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override; bool HasSurface() const override;
bool UpdateWindow() override; bool UpdateWindow() override;

View file

@ -137,11 +137,6 @@ MetalDevice::~MetalDevice()
Assert(m_device == nil); Assert(m_device == nil);
} }
RenderAPI MetalDevice::GetRenderAPI() const
{
return RenderAPI::Metal;
}
bool MetalDevice::HasSurface() const bool MetalDevice::HasSurface() const
{ {
return (m_layer != nil); return (m_layer != nil);
@ -234,6 +229,8 @@ bool MetalDevice::CreateDevice(std::string_view adapter, std::optional<bool> exc
void MetalDevice::SetFeatures(FeatureMask disabled_features) void MetalDevice::SetFeatures(FeatureMask disabled_features)
{ {
m_render_api = RenderAPI::Metal;
m_render_api_version = 100; // TODO: Make this more meaningful.
m_max_texture_size = GetMetalMaxTextureSize(m_device); m_max_texture_size = GetMetalMaxTextureSize(m_device);
m_max_multisamples = GetMetalMaxMultisamples(m_device); m_max_multisamples = GetMetalMaxMultisamples(m_device);

View file

@ -55,11 +55,6 @@ void OpenGLDevice::SetErrorObject(Error* errptr, std::string_view prefix, GLenum
Error::SetStringFmt(errptr, "{}GL Error 0x{:04X}", prefix, static_cast<unsigned>(glerr)); Error::SetStringFmt(errptr, "{}GL Error 0x{:04X}", prefix, static_cast<unsigned>(glerr));
} }
RenderAPI OpenGLDevice::GetRenderAPI() const
{
return m_gl_context->IsGLES() ? RenderAPI::OpenGLES : RenderAPI::OpenGL;
}
std::unique_ptr<GPUTexture> OpenGLDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> OpenGLDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data, u32 data_stride) const void* data, u32 data_stride)
@ -347,6 +342,13 @@ bool OpenGLDevice::CheckFeatures(FeatureMask disabled_features)
{ {
const bool is_gles = m_gl_context->IsGLES(); const bool is_gles = m_gl_context->IsGLES();
m_render_api = is_gles ? RenderAPI::OpenGLES : RenderAPI::OpenGL;
GLint major_version = 0, minor_version = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major_version);
glGetIntegerv(GL_MINOR_VERSION, &minor_version);
m_render_api_version = (static_cast<u32>(major_version) * 100u) + (static_cast<u32>(minor_version) * 10u);
bool vendor_id_amd = false; bool vendor_id_amd = false;
// bool vendor_id_nvidia = false; // bool vendor_id_nvidia = false;
bool vendor_id_intel = false; bool vendor_id_intel = false;

View file

@ -40,8 +40,6 @@ public:
static bool ShouldUsePBOsForDownloads(); static bool ShouldUsePBOsForDownloads();
static void SetErrorObject(Error* errptr, std::string_view prefix, GLenum glerr); static void SetErrorObject(Error* errptr, std::string_view prefix, GLenum glerr);
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override; bool HasSurface() const override;
void DestroySurface() override; void DestroySurface() override;

View file

@ -30,6 +30,7 @@
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <sstream> #include <sstream>
#include <tuple>
Log_SetChannel(ReShadeFXShader); Log_SetChannel(ReShadeFXShader);
@ -38,7 +39,12 @@ static constexpr s32 DEFAULT_BUFFER_HEIGHT = 2160;
static RenderAPI GetRenderAPI() static RenderAPI GetRenderAPI()
{ {
return g_gpu_device ? g_gpu_device->GetRenderAPI() : RenderAPI::D3D11; #ifdef _WIN32
static constexpr RenderAPI DEFAULT_RENDER_API = RenderAPI::D3D11;
#else
static constexpr RenderAPI DEFAULT_RENDER_API = RenderAPI::D3D12;
#endif
return g_gpu_device ? g_gpu_device->GetRenderAPI() : DEFAULT_RENDER_API;
} }
static bool PreprocessorFileExistsCallback(const std::string& path) static bool PreprocessorFileExistsCallback(const std::string& path)
@ -63,37 +69,44 @@ static bool PreprocessorReadFileCallback(const std::string& path, std::string& d
return true; return true;
} }
static std::unique_ptr<reshadefx::codegen> CreateRFXCodegen() static std::tuple<std::unique_ptr<reshadefx::codegen>, GPUShaderLanguage> CreateRFXCodegen()
{ {
const bool debug_info = g_gpu_device ? g_gpu_device->IsDebugDevice() : false; const bool debug_info = g_gpu_device ? g_gpu_device->IsDebugDevice() : false;
const bool uniforms_to_spec_constants = false; const bool uniforms_to_spec_constants = false;
const RenderAPI rapi = GetRenderAPI(); const RenderAPI rapi = GetRenderAPI();
[[maybe_unused]] const u32 rapi_version = g_gpu_device ? g_gpu_device->GetRenderAPIVersion() : 0;
switch (rapi) switch (rapi)
{ {
case RenderAPI::None: #ifdef _WIN32
case RenderAPI::D3D11: case RenderAPI::D3D11:
case RenderAPI::D3D12: case RenderAPI::D3D12:
{ {
return std::unique_ptr<reshadefx::codegen>( return std::make_tuple(std::unique_ptr<reshadefx::codegen>(reshadefx::create_codegen_hlsl(
reshadefx::create_codegen_hlsl(50, debug_info, uniforms_to_spec_constants)); (rapi_version < 1100) ? 40 : 50, debug_info, uniforms_to_spec_constants)),
GPUShaderLanguage::HLSL);
} }
break;
#endif
case RenderAPI::Vulkan: case RenderAPI::Vulkan:
case RenderAPI::Metal: case RenderAPI::Metal:
{ {
return std::unique_ptr<reshadefx::codegen>(reshadefx::create_codegen_spirv( return std::make_tuple(std::unique_ptr<reshadefx::codegen>(reshadefx::create_codegen_spirv(
true, debug_info, uniforms_to_spec_constants, false, (rapi == RenderAPI::Vulkan))); true, debug_info, uniforms_to_spec_constants, false, (rapi == RenderAPI::Vulkan))),
GPUShaderLanguage::SPV);
} }
case RenderAPI::OpenGL: case RenderAPI::OpenGL:
case RenderAPI::OpenGLES: case RenderAPI::OpenGLES:
default: default:
{ {
return std::unique_ptr<reshadefx::codegen>( return std::make_tuple(std::unique_ptr<reshadefx::codegen>(reshadefx::create_codegen_glsl(
reshadefx::create_codegen_glsl(ShaderGen::GetGLSLVersion(rapi), (rapi == RenderAPI::OpenGLES), false, g_gpu_device ? ShaderGen::GetGLSLVersion(rapi) : 460, (rapi == RenderAPI::OpenGLES),
debug_info, uniforms_to_spec_constants, false, true)); false, debug_info, uniforms_to_spec_constants, false, true)),
(rapi == RenderAPI::OpenGLES) ? GPUShaderLanguage::GLSLES : GPUShaderLanguage::GLSL);
} }
break;
} }
} }
@ -308,9 +321,7 @@ bool PostProcessing::ReShadeFXShader::LoadFromString(std::string name, std::stri
code.push_back('\n'); code.push_back('\n');
// TODO: This could use spv, it's probably fastest. // TODO: This could use spv, it's probably fastest.
std::unique_ptr<reshadefx::codegen> cg = CreateRFXCodegen(); const auto& [cg, cg_language] = CreateRFXCodegen();
if (!cg)
return false;
if (!CreateModule(only_config ? DEFAULT_BUFFER_WIDTH : g_gpu_device->GetWindowWidth(), if (!CreateModule(only_config ? DEFAULT_BUFFER_WIDTH : g_gpu_device->GetWindowWidth(),
only_config ? DEFAULT_BUFFER_HEIGHT : g_gpu_device->GetWindowHeight(), cg.get(), std::move(code), only_config ? DEFAULT_BUFFER_HEIGHT : g_gpu_device->GetWindowHeight(), cg.get(), std::move(code),
@ -1319,9 +1330,7 @@ bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format,
if (fxcode.empty() || fxcode.back() != '\n') if (fxcode.empty() || fxcode.back() != '\n')
fxcode.push_back('\n'); fxcode.push_back('\n');
std::unique_ptr<reshadefx::codegen> cg = CreateRFXCodegen(); const auto& [cg, cg_language] = CreateRFXCodegen();
if (!cg)
return false;
Error error; Error error;
if (!CreateModule(width, height, cg.get(), std::move(fxcode), &error)) if (!CreateModule(width, height, cg.get(), std::move(fxcode), &error))
@ -1339,16 +1348,13 @@ bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format,
return false; return false;
} }
const RenderAPI api = g_gpu_device->GetRenderAPI(); auto get_shader = [cg_language, &cg](const std::string& name, const std::span<Sampler> samplers,
auto get_shader = [api, &cg](const std::string& name, const std::span<Sampler> samplers, GPUShaderStage stage) { GPUShaderStage stage) {
const std::string real_code = cg->finalize_code_for_entry_point(name); const std::string real_code = cg->finalize_code_for_entry_point(name);
const GPUShaderLanguage lang = (api == RenderAPI::Vulkan || api == RenderAPI::Metal) ? const char* entry_point = (cg_language == GPUShaderLanguage::HLSL) ? name.c_str() : "main";
GPUShaderLanguage::SPV :
ShaderGen::GetShaderLanguageForAPI(api);
const char* entry_point = (lang == GPUShaderLanguage::HLSL) ? name.c_str() : "main";
Error error; Error error;
std::unique_ptr<GPUShader> sshader = g_gpu_device->CreateShader(stage, lang, real_code, &error, entry_point); std::unique_ptr<GPUShader> sshader = g_gpu_device->CreateShader(stage, cg_language, real_code, &error, entry_point);
if (!sshader) if (!sshader)
ERROR_LOG("Failed to compile function '{}': {}", name, error.GetDescription()); ERROR_LOG("Failed to compile function '{}': {}", name, error.GetDescription());

View file

@ -1889,11 +1889,6 @@ bool VulkanDevice::IsSuitableDefaultRenderer()
#endif #endif
} }
RenderAPI VulkanDevice::GetRenderAPI() const
{
return RenderAPI::Vulkan;
}
bool VulkanDevice::HasSurface() const bool VulkanDevice::HasSurface() const
{ {
return static_cast<bool>(m_swap_chain); return static_cast<bool>(m_swap_chain);
@ -2507,6 +2502,10 @@ u32 VulkanDevice::GetMaxMultisamples(VkPhysicalDevice physical_device, const VkP
void VulkanDevice::SetFeatures(FeatureMask disabled_features, const VkPhysicalDeviceFeatures& vk_features) void VulkanDevice::SetFeatures(FeatureMask disabled_features, const VkPhysicalDeviceFeatures& vk_features)
{ {
const u32 store_api_version = std::min(m_device_properties.apiVersion, VK_API_VERSION_1_1);
m_render_api = RenderAPI::Vulkan;
m_render_api_version = (VK_API_VERSION_MAJOR(store_api_version) * 100u) +
(VK_API_VERSION_MINOR(store_api_version) * 10u) + (VK_API_VERSION_PATCH(store_api_version));
m_max_texture_size = m_max_texture_size =
std::min(m_device_properties.limits.maxImageDimension2D, m_device_properties.limits.maxFramebufferWidth); std::min(m_device_properties.limits.maxImageDimension2D, m_device_properties.limits.maxFramebufferWidth);
m_max_multisamples = GetMaxMultisamples(m_physical_device, m_device_properties); m_max_multisamples = GetMaxMultisamples(m_physical_device, m_device_properties);

View file

@ -75,8 +75,6 @@ public:
static GPUList EnumerateGPUs(); static GPUList EnumerateGPUs();
static AdapterInfoList GetAdapterList(); static AdapterInfoList GetAdapterList();
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override; bool HasSurface() const override;
bool UpdateWindow() override; bool UpdateWindow() override;