From 4de104569388f9c1f7fb9708e58a423c7644793c Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 8 Sep 2024 23:33:05 +1000 Subject: [PATCH] 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. --- src/common/small_string.cpp | 12 ++++ src/common/small_string.h | 4 ++ src/core/host.h | 2 +- src/core/settings.h | 2 +- src/util/d3d11_device.cpp | 17 ++--- src/util/d3d11_device.h | 2 - src/util/d3d11_pipeline.cpp | 2 +- src/util/d3d12_device.cpp | 47 +++++++++---- src/util/d3d12_device.h | 14 ++-- src/util/d3d12_pipeline.cpp | 2 +- src/util/d3d_common.cpp | 99 ++++++++++++++------------- src/util/d3d_common.h | 8 +-- src/util/gpu_device.cpp | 86 ++++++++--------------- src/util/gpu_device.h | 8 ++- src/util/gpu_shader_cache.cpp | 23 +++++-- src/util/gpu_shader_cache.h | 3 +- src/util/metal_device.h | 2 - src/util/metal_device.mm | 7 +- src/util/opengl_device.cpp | 12 ++-- src/util/opengl_device.h | 2 - src/util/postprocessing_shader_fx.cpp | 52 +++++++------- src/util/vulkan_device.cpp | 9 ++- src/util/vulkan_device.h | 2 - 23 files changed, 222 insertions(+), 195 deletions(-) diff --git a/src/common/small_string.cpp b/src/common/small_string.cpp index 4d4dcc312..f227345df 100644 --- a/src/common/small_string.cpp +++ b/src/common/small_string.cpp @@ -111,6 +111,18 @@ void SmallStringBase::shrink_to_fit() m_buffer_size = buffer_size; } +void SmallStringBase::convert_to_lower_case() +{ + for (u32 i = 0; i < m_length; i++) + m_buffer[i] = static_cast(std::tolower(m_buffer[i])); +} + +void SmallStringBase::convert_to_upper_case() +{ + for (u32 i = 0; i < m_length; i++) + m_buffer[i] = static_cast(std::toupper(m_buffer[i])); +} + std::string_view SmallStringBase::view() const { return (m_length == 0) ? std::string_view() : std::string_view(m_buffer, m_length); diff --git a/src/common/small_string.h b/src/common/small_string.h index 3656129bf..0fe4c828a 100644 --- a/src/common/small_string.h +++ b/src/common/small_string.h @@ -188,6 +188,10 @@ public: ALWAYS_INLINE void push_back(value_type val) { append(val); } 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 std::string_view view() const; diff --git a/src/core/host.h b/src/core/host.h index c1af516fa..db6d672d0 100644 --- a/src/core/host.h +++ b/src/core/host.h @@ -21,7 +21,7 @@ struct WindowInfo; enum class AudioBackend : u8; enum class AudioExpansionMode : u8; enum class AudioStretchMode : u8; -enum class RenderAPI : u32; +enum class RenderAPI : u8; class AudioStream; class CDImage; diff --git a/src/core/settings.h b/src/core/settings.h index 7db451afb..d55dfcf7e 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -18,7 +18,7 @@ #include #include -enum class RenderAPI : u32; +enum class RenderAPI : u8; enum class MediaCaptureBackend : u8; struct SettingInfo diff --git a/src/util/d3d11_device.cpp b/src/util/d3d11_device.cpp index de0d74200..1da0abad5 100644 --- a/src/util/d3d11_device.cpp +++ b/src/util/d3d11_device.cpp @@ -53,11 +53,6 @@ D3D11Device::~D3D11Device() Assert(!m_device); } -RenderAPI D3D11Device::GetRenderAPI() const -{ - return RenderAPI::D3D11; -} - bool D3D11Device::HasSurface() const { return static_cast(m_swap_chain); @@ -127,7 +122,8 @@ bool D3D11Device::CreateDevice(std::string_view adapter, std::optional exc INFO_LOG("D3D Adapter: {}", D3DCommon::GetAdapterName(dxgi_adapter.Get())); else 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; 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(); + 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_multisamples = 1; for (u32 multisamples = 2; multisamples < D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; multisamples++) @@ -443,12 +441,11 @@ bool D3D11Device::SupportsExclusiveFullscreen() const std::string D3D11Device::GetDriverInfo() const { - const D3D_FEATURE_LEVEL fl = m_device->GetFeatureLevel(); - std::string ret = - fmt::format("{} ({})\n", D3DCommon::GetFeatureLevelString(fl), D3DCommon::GetFeatureLevelShaderModelString(fl)); + std::string ret = fmt::format("{} (Shader Model {})\n", D3DCommon::GetFeatureLevelString(m_render_api_version), + D3DCommon::GetShaderModelForFeatureLevelNumber(m_render_api_version)); ComPtr dxgi_dev; - if (m_device.As(&dxgi_dev)) + if (SUCCEEDED(m_device.As(&dxgi_dev))) { ComPtr dxgi_adapter; if (SUCCEEDED(dxgi_dev->GetAdapter(dxgi_adapter.GetAddressOf()))) diff --git a/src/util/d3d11_device.h b/src/util/d3d11_device.h index eaf11e03e..f029bb992 100644 --- a/src/util/d3d11_device.h +++ b/src/util/d3d11_device.h @@ -36,8 +36,6 @@ public: ALWAYS_INLINE static ID3D11DeviceContext1* GetD3DContext() { return GetInstance().m_context.Get(); } ALWAYS_INLINE static D3D_FEATURE_LEVEL GetMaxFeatureLevel() { return GetInstance().m_max_feature_level; } - RenderAPI GetRenderAPI() const override; - bool HasSurface() const override; bool UpdateWindow() override; diff --git a/src/util/d3d11_pipeline.cpp b/src/util/d3d11_pipeline.cpp index 1ebc9300a..5fe9ebbaf 100644 --- a/src/util/d3d11_pipeline.cpp +++ b/src/util/d3d11_pipeline.cpp @@ -100,7 +100,7 @@ std::unique_ptr D3D11Device::CreateShaderFromSource(GPUShaderStage st std::string_view source, const char* entry_point, DynamicHeapArray* 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) { return TranspileAndCreateShaderFromSource(stage, language, source, entry_point, GPUShaderLanguage::HLSL, diff --git a/src/util/d3d12_device.cpp b/src/util/d3d12_device.cpp index d7b2c107f..959c5e7c4 100644 --- a/src/util/d3d12_device.cpp +++ b/src/util/d3d12_device.cpp @@ -147,12 +147,13 @@ bool D3D12Device::CreateDevice(std::string_view adapter, std::optional exc } // 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}) { hr = D3D12CreateDevice(m_adapter.Get(), try_feature_level, IID_PPV_ARGS(&m_device)); if (SUCCEEDED(hr)) { - m_feature_level = try_feature_level; + feature_level = try_feature_level; break; } } @@ -232,7 +233,7 @@ bool D3D12Device::CreateDevice(std::string_view adapter, std::optional exc return false; } - SetFeatures(disabled_features); + SetFeatures(feature_level, disabled_features); if (!CreateCommandLists(error) || !CreateDescriptorHeaps(error)) return false; @@ -282,10 +283,28 @@ void D3D12Device::DestroyDevice() 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> 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 = - 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())); if (SUCCEEDED(hr)) { @@ -328,8 +347,13 @@ bool D3D12Device::GetPipelineCacheData(DynamicHeapArray* data) return true; } - data->resize(size); - const HRESULT hr = m_pipeline_library->Serialize(data->data(), data->size()); + PIPELINE_CACHE_HEADER header; + 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)) { ERROR_LOG("Serialize() failed with HRESULT {:08X}", static_cast(hr)); @@ -771,11 +795,6 @@ void D3D12Device::DestroyDeferredObjects(u64 fence_value) } } -RenderAPI D3D12Device::GetRenderAPI() const -{ - return RenderAPI::D3D12; -} - bool D3D12Device::HasSurface() const { return static_cast(m_swap_chain); @@ -1070,8 +1089,8 @@ bool D3D12Device::SupportsTextureFormat(GPUTexture::Format format) const std::string D3D12Device::GetDriverInfo() const { - std::string ret = fmt::format("{} ({})\n", D3DCommon::GetFeatureLevelString(m_feature_level), - D3DCommon::GetFeatureLevelShaderModelString(m_feature_level)); + std::string ret = fmt::format("{} (Shader Model {})\n", D3DCommon::GetFeatureLevelString(m_render_api_version), + D3DCommon::GetShaderModelForFeatureLevelNumber(m_render_api_version)); DXGI_ADAPTER_DESC desc; if (m_adapter && SUCCEEDED(m_adapter->GetDesc(&desc))) @@ -1234,8 +1253,10 @@ void D3D12Device::InsertDebugMessage(const char* msg) #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_multisamples = 1; for (u32 multisamples = 2; multisamples < D3D12_MAX_MULTISAMPLE_SAMPLE_COUNT; multisamples++) diff --git a/src/util/d3d12_device.h b/src/util/d3d12_device.h index 7a1010bd2..74dc675f7 100644 --- a/src/util/d3d12_device.h +++ b/src/util/d3d12_device.h @@ -58,8 +58,6 @@ public: D3D12Device(); ~D3D12Device() override; - RenderAPI GetRenderAPI() const override; - bool HasSurface() const override; bool UpdateWindow() override; @@ -220,9 +218,18 @@ private: 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; - void SetFeatures(FeatureMask disabled_features); + void GetPipelineCacheHeader(PIPELINE_CACHE_HEADER* hdr); + void SetFeatures(D3D_FEATURE_LEVEL feature_level, FeatureMask disabled_features); u32 GetSwapChainBufferCount() const; bool CreateSwapChain(Error* error); @@ -291,7 +298,6 @@ private: std::array m_command_lists; u32 m_current_command_list = NUM_COMMAND_LISTS - 1; - D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0; ComPtr m_dxgi_factory; ComPtr m_swap_chain; diff --git a/src/util/d3d12_pipeline.cpp b/src/util/d3d12_pipeline.cpp index 3bc3c0a56..5acea7a4a 100644 --- a/src/util/d3d12_pipeline.cpp +++ b/src/util/d3d12_pipeline.cpp @@ -38,7 +38,7 @@ std::unique_ptr D3D12Device::CreateShaderFromSource(GPUShaderStage st std::string_view source, const char* entry_point, DynamicHeapArray* 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) { return TranspileAndCreateShaderFromSource(stage, language, source, entry_point, GPUShaderLanguage::HLSL, diff --git a/src/util/d3d_common.cpp b/src/util/d3d_common.cpp index 318a7a8fa..5b96274f4 100644 --- a/src/util/d3d_common.cpp +++ b/src/util/d3d_common.cpp @@ -19,47 +19,65 @@ Log_SetChannel(D3DCommon); -const char* D3DCommon::GetFeatureLevelString(D3D_FEATURE_LEVEL feature_level) +namespace D3DCommon { +namespace { +struct FeatureLevelTableEntry { - static constexpr std::array, 11> feature_level_names = {{ - {D3D_FEATURE_LEVEL_1_0_CORE, "D3D_FEATURE_LEVEL_1_0_CORE"}, - {D3D_FEATURE_LEVEL_9_1, "D3D_FEATURE_LEVEL_9_1"}, - {D3D_FEATURE_LEVEL_9_2, "D3D_FEATURE_LEVEL_9_2"}, - {D3D_FEATURE_LEVEL_9_3, "D3D_FEATURE_LEVEL_9_3"}, - {D3D_FEATURE_LEVEL_10_0, "D3D_FEATURE_LEVEL_10_0"}, - {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"}, - }}; + D3D_FEATURE_LEVEL d3d_feature_level; + u16 render_api_version; + u16 shader_model_number; + const char* feature_level_str; +}; +} // namespace - for (const auto& [fl, name] : feature_level_names) - { - if (fl == feature_level) - return name; - } +static constexpr std::array s_feature_levels = {{ + {D3D_FEATURE_LEVEL_1_0_CORE, 100, 40, "D3D_FEATURE_LEVEL_1_0_CORE"}, + {D3D_FEATURE_LEVEL_9_1, 910, 40, "D3D_FEATURE_LEVEL_9_1"}, + {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, 4> feature_level_names = {{ - {D3D_FEATURE_LEVEL_10_0, "sm40"}, - {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) + const FeatureLevelTableEntry* highest_entry = nullptr; + for (const FeatureLevelTableEntry& entry : s_feature_levels) { - if (fl == feature_level) - return name; + if (feature_level >= entry.d3d_feature_level) + 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) @@ -379,23 +397,6 @@ std::string D3DCommon::GetDriverVersionFromLUID(const LUID& luid) 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> D3DCommon::CompileShader(u32 shader_model, bool debug_device, GPUShaderStage stage, std::string_view source, const char* entry_point, Error* error) diff --git a/src/util/d3d_common.h b/src/util/d3d_common.h index e017427ee..21cef99ab 100644 --- a/src/util/d3d_common.h +++ b/src/util/d3d_common.h @@ -25,8 +25,10 @@ struct DXGI_MODE_DESC; namespace D3DCommon { // returns string representation of feature level -const char* GetFeatureLevelString(D3D_FEATURE_LEVEL feature_level); -const char* GetFeatureLevelShaderModelString(D3D_FEATURE_LEVEL feature_level); +const char* GetFeatureLevelString(u32 render_api_version); +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 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 std::string GetDriverVersionFromLUID(const LUID& luid); -u32 GetShaderModelForFeatureLevel(D3D_FEATURE_LEVEL feature_level); - std::optional> CompileShader(u32 shader_model, bool debug_device, GPUShaderStage stage, std::string_view source, const char* entry_point, Error* error); diff --git a/src/util/gpu_device.cpp b/src/util/gpu_device.cpp index 770c440a8..8285d8ce4 100644 --- a/src/util/gpu_device.cpp +++ b/src/util/gpu_device.cpp @@ -367,6 +367,7 @@ bool GPUDevice::Create(std::string_view adapter, std::string_view shader_cache_p return false; } + INFO_LOG("Render API: {} Version {}", RenderAPIToString(m_render_api), m_render_api_version); INFO_LOG("Graphics Driver Info:\n{}", GetDriverInfo()); 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 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."); if (!m_shader_cache.Create()) @@ -423,7 +424,7 @@ void GPUDevice::OpenShaderCache(std::string_view base_path, u32 version) else { // 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 = {}; @@ -473,74 +474,47 @@ std::string GPUDevice::GetShaderCacheBaseName(std::string_view type) const { const std::string_view debug_suffix = m_debug_device ? "_debug" : ""; - std::string ret; - switch (GetRenderAPI()) - { -#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; - } + TinyString lower_api_name(RenderAPIToString(m_render_api)); + lower_api_name.convert_to_lower_case(); - return ret; + return fmt::format("{}_{}{}", lower_api_name, type, debug_suffix); } bool GPUDevice::OpenPipelineCache(const std::string& filename) { - Error error; - CompressHelpers::OptionalByteBuffer data = - CompressHelpers::DecompressFile(CompressHelpers::CompressType::Zstandard, filename.c_str(), std::nullopt, &error); + s_pipeline_cache_size = 0; + s_pipeline_cache_hash = {}; - if (data.has_value()) + Error error; + CompressHelpers::OptionalByteBuffer data; + if (FileSystem::FileExists(filename.c_str())) { - s_pipeline_cache_size = data->size(); - s_pipeline_cache_hash = SHA1Digest::GetDigest(data->cspan()); + data = + CompressHelpers::DecompressFile(CompressHelpers::CompressType::Zstandard, filename.c_str(), std::nullopt, &error); + if (data.has_value()) + { + s_pipeline_cache_size = data->size(); + s_pipeline_cache_hash = SHA1Digest::GetDigest(data->cspan()); + } + else + { + ERROR_LOG("Failed to load pipeline cache from '{}': {}", Path::GetFileName(filename), error.GetDescription()); + } + } + + 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 - { - 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))) { s_pipeline_cache_size = 0; s_pipeline_cache_hash = {}; 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> data) diff --git a/src/util/gpu_device.h b/src/util/gpu_device.h index 2d46c333f..c051ad740 100644 --- a/src/util/gpu_device.h +++ b/src/util/gpu_device.h @@ -25,7 +25,7 @@ class Error; -enum class RenderAPI : u32 +enum class RenderAPI : u8 { None, D3D11, @@ -594,6 +594,8 @@ public: } 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 GetMaxMultisamples() const { return m_max_multisamples; } @@ -608,8 +610,6 @@ public: 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, GPUVSyncMode vsync, bool allow_present_throttle, std::optional exclusive_fullscreen_control, FeatureMask disabled_features, Error* error); @@ -776,6 +776,8 @@ protected: static std::optional> OptimizeVulkanSpv(const std::span spirv, Error* error); Features m_features = {}; + RenderAPI m_render_api = RenderAPI::None; + u32 m_render_api_version = 0; u32 m_max_texture_size = 0; u32 m_max_multisamples = 0; diff --git a/src/util/gpu_shader_cache.cpp b/src/util/gpu_shader_cache.cpp index f8865f8b6..0964249d6 100644 --- a/src/util/gpu_shader_cache.cpp +++ b/src/util/gpu_shader_cache.cpp @@ -18,6 +18,12 @@ Log_SetChannel(GPUShaderCache); #pragma pack(push, 1) +struct CacheFileHeader +{ + u32 signature; + u32 render_api_version; + u32 cache_version; +}; struct CacheIndexEntry { u8 shader_type; @@ -34,6 +40,8 @@ struct CacheIndexEntry }; #pragma pack(pop) +static constexpr u32 EXPECTED_SIGNATURE = 0x434B5544; // DUKC + GPUShaderCache::GPUShaderCache() = default; GPUShaderCache::~GPUShaderCache() @@ -59,10 +67,11 @@ std::size_t GPUShaderCache::CacheIndexEntryHash::operator()(const CacheIndexKey& 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_version = version; + m_render_api_version = render_api_version; + m_version = cache_version; if (base_filename.empty()) return true; @@ -127,7 +136,9 @@ bool GPUShaderCache::CreateNew(const std::string& index_filename, const std::str 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)); std::fclose(m_index_file); @@ -165,8 +176,10 @@ bool GPUShaderCache::ReadExisting(const std::string& index_filename, const std:: return false; } - u32 file_version = 0; - if (std::fread(&file_version, sizeof(file_version), 1, m_index_file) != 1 || file_version != m_version) [[unlikely]] + CacheFileHeader file_header; + 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)); std::fclose(m_index_file); diff --git a/src/util/gpu_shader_cache.h b/src/util/gpu_shader_cache.h index 1b6fef49c..922c608e2 100644 --- a/src/util/gpu_shader_cache.h +++ b/src/util/gpu_shader_cache.h @@ -50,7 +50,7 @@ public: 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(); void Close(); @@ -77,6 +77,7 @@ private: CacheIndex m_index; std::string m_base_filename; + u32 m_render_api_version = 0; u32 m_version = 0; std::FILE* m_index_file = nullptr; diff --git a/src/util/metal_device.h b/src/util/metal_device.h index acbecfdf2..21a7e35c7 100644 --- a/src/util/metal_device.h +++ b/src/util/metal_device.h @@ -198,8 +198,6 @@ public: MetalDevice(); ~MetalDevice(); - RenderAPI GetRenderAPI() const override; - bool HasSurface() const override; bool UpdateWindow() override; diff --git a/src/util/metal_device.mm b/src/util/metal_device.mm index 06fc83817..d50882c06 100644 --- a/src/util/metal_device.mm +++ b/src/util/metal_device.mm @@ -137,11 +137,6 @@ MetalDevice::~MetalDevice() Assert(m_device == nil); } -RenderAPI MetalDevice::GetRenderAPI() const -{ - return RenderAPI::Metal; -} - bool MetalDevice::HasSurface() const { return (m_layer != nil); @@ -234,6 +229,8 @@ bool MetalDevice::CreateDevice(std::string_view adapter, std::optional exc 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_multisamples = GetMetalMaxMultisamples(m_device); diff --git a/src/util/opengl_device.cpp b/src/util/opengl_device.cpp index 56454569c..7b03c40f7 100644 --- a/src/util/opengl_device.cpp +++ b/src/util/opengl_device.cpp @@ -55,11 +55,6 @@ void OpenGLDevice::SetErrorObject(Error* errptr, std::string_view prefix, GLenum Error::SetStringFmt(errptr, "{}GL Error 0x{:04X}", prefix, static_cast(glerr)); } -RenderAPI OpenGLDevice::GetRenderAPI() const -{ - return m_gl_context->IsGLES() ? RenderAPI::OpenGLES : RenderAPI::OpenGL; -} - std::unique_ptr OpenGLDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, GPUTexture::Type type, GPUTexture::Format format, const void* data, u32 data_stride) @@ -347,6 +342,13 @@ bool OpenGLDevice::CheckFeatures(FeatureMask disabled_features) { 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(major_version) * 100u) + (static_cast(minor_version) * 10u); + bool vendor_id_amd = false; // bool vendor_id_nvidia = false; bool vendor_id_intel = false; diff --git a/src/util/opengl_device.h b/src/util/opengl_device.h index ac425da78..a54140553 100644 --- a/src/util/opengl_device.h +++ b/src/util/opengl_device.h @@ -40,8 +40,6 @@ public: static bool ShouldUsePBOsForDownloads(); static void SetErrorObject(Error* errptr, std::string_view prefix, GLenum glerr); - RenderAPI GetRenderAPI() const override; - bool HasSurface() const override; void DestroySurface() override; diff --git a/src/util/postprocessing_shader_fx.cpp b/src/util/postprocessing_shader_fx.cpp index 219b398e6..a26d10b32 100644 --- a/src/util/postprocessing_shader_fx.cpp +++ b/src/util/postprocessing_shader_fx.cpp @@ -30,6 +30,7 @@ #include #include #include +#include Log_SetChannel(ReShadeFXShader); @@ -38,7 +39,12 @@ static constexpr s32 DEFAULT_BUFFER_HEIGHT = 2160; 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) @@ -63,37 +69,44 @@ static bool PreprocessorReadFileCallback(const std::string& path, std::string& d return true; } -static std::unique_ptr CreateRFXCodegen() +static std::tuple, GPUShaderLanguage> CreateRFXCodegen() { const bool debug_info = g_gpu_device ? g_gpu_device->IsDebugDevice() : false; const bool uniforms_to_spec_constants = false; const RenderAPI rapi = GetRenderAPI(); + [[maybe_unused]] const u32 rapi_version = g_gpu_device ? g_gpu_device->GetRenderAPIVersion() : 0; switch (rapi) { - case RenderAPI::None: +#ifdef _WIN32 case RenderAPI::D3D11: case RenderAPI::D3D12: { - return std::unique_ptr( - reshadefx::create_codegen_hlsl(50, debug_info, uniforms_to_spec_constants)); + return std::make_tuple(std::unique_ptr(reshadefx::create_codegen_hlsl( + (rapi_version < 1100) ? 40 : 50, debug_info, uniforms_to_spec_constants)), + GPUShaderLanguage::HLSL); } + break; +#endif case RenderAPI::Vulkan: case RenderAPI::Metal: { - return std::unique_ptr(reshadefx::create_codegen_spirv( - true, debug_info, uniforms_to_spec_constants, false, (rapi == RenderAPI::Vulkan))); + return std::make_tuple(std::unique_ptr(reshadefx::create_codegen_spirv( + true, debug_info, uniforms_to_spec_constants, false, (rapi == RenderAPI::Vulkan))), + GPUShaderLanguage::SPV); } case RenderAPI::OpenGL: case RenderAPI::OpenGLES: default: { - return std::unique_ptr( - reshadefx::create_codegen_glsl(ShaderGen::GetGLSLVersion(rapi), (rapi == RenderAPI::OpenGLES), false, - debug_info, uniforms_to_spec_constants, false, true)); + return std::make_tuple(std::unique_ptr(reshadefx::create_codegen_glsl( + g_gpu_device ? ShaderGen::GetGLSLVersion(rapi) : 460, (rapi == RenderAPI::OpenGLES), + 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'); // TODO: This could use spv, it's probably fastest. - std::unique_ptr cg = CreateRFXCodegen(); - if (!cg) - return false; + const auto& [cg, cg_language] = CreateRFXCodegen(); 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), @@ -1319,9 +1330,7 @@ bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format, if (fxcode.empty() || fxcode.back() != '\n') fxcode.push_back('\n'); - std::unique_ptr cg = CreateRFXCodegen(); - if (!cg) - return false; + const auto& [cg, cg_language] = CreateRFXCodegen(); Error error; if (!CreateModule(width, height, cg.get(), std::move(fxcode), &error)) @@ -1339,16 +1348,13 @@ bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format, return false; } - const RenderAPI api = g_gpu_device->GetRenderAPI(); - auto get_shader = [api, &cg](const std::string& name, const std::span samplers, GPUShaderStage stage) { + auto get_shader = [cg_language, &cg](const std::string& name, const std::span samplers, + GPUShaderStage stage) { const std::string real_code = cg->finalize_code_for_entry_point(name); - const GPUShaderLanguage lang = (api == RenderAPI::Vulkan || api == RenderAPI::Metal) ? - GPUShaderLanguage::SPV : - ShaderGen::GetShaderLanguageForAPI(api); - const char* entry_point = (lang == GPUShaderLanguage::HLSL) ? name.c_str() : "main"; + const char* entry_point = (cg_language == GPUShaderLanguage::HLSL) ? name.c_str() : "main"; Error error; - std::unique_ptr sshader = g_gpu_device->CreateShader(stage, lang, real_code, &error, entry_point); + std::unique_ptr sshader = g_gpu_device->CreateShader(stage, cg_language, real_code, &error, entry_point); if (!sshader) ERROR_LOG("Failed to compile function '{}': {}", name, error.GetDescription()); diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index 5c6e99c39..517417b97 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -1889,11 +1889,6 @@ bool VulkanDevice::IsSuitableDefaultRenderer() #endif } -RenderAPI VulkanDevice::GetRenderAPI() const -{ - return RenderAPI::Vulkan; -} - bool VulkanDevice::HasSurface() const { return static_cast(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) { + 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 = std::min(m_device_properties.limits.maxImageDimension2D, m_device_properties.limits.maxFramebufferWidth); m_max_multisamples = GetMaxMultisamples(m_physical_device, m_device_properties); diff --git a/src/util/vulkan_device.h b/src/util/vulkan_device.h index 2d3b25001..48b620f0a 100644 --- a/src/util/vulkan_device.h +++ b/src/util/vulkan_device.h @@ -75,8 +75,6 @@ public: static GPUList EnumerateGPUs(); static AdapterInfoList GetAdapterList(); - RenderAPI GetRenderAPI() const override; - bool HasSurface() const override; bool UpdateWindow() override;