From 1984b37101cb0a7d8b48d6f1085df3f279cc6c64 Mon Sep 17 00:00:00 2001 From: crosire Date: Sat, 23 May 2026 12:40:59 +0200 Subject: [PATCH 01/26] Update utfcpp dependency --- deps/utfcpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/utfcpp b/deps/utfcpp index 63d64de49f..819011bb01 160000 --- a/deps/utfcpp +++ b/deps/utfcpp @@ -1 +1 @@ -Subproject commit 63d64de49fd6b829f7c8694df5ab2ee625cb7134 +Subproject commit 819011bb01628fe1aa2f1da9f2c842a48fd5680b From 697b00d8accf71208ecdfdbcb48c9ce848c6d54f Mon Sep 17 00:00:00 2001 From: crosire Date: Sat, 30 May 2026 04:03:22 +0200 Subject: [PATCH 02/26] Enable thread-safe local static initialization by default again This was disabled in commit 054a4dbe7a1e27787f5618a69fe91dabb40ca01b as an optimization due to most uses being in functions that are not thread-safe to begin with, but it missed the case of e.g. the ws2_32 hooks, which should be thread-safe --- ReShade.vcxproj | 20 ++++++++++++-------- source/d3d12/d3d12_pipeline_library.cpp | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/ReShade.vcxproj b/ReShade.vcxproj index 248bb7f565..7bf8a717f5 100644 --- a/ReShade.vcxproj +++ b/ReShade.vcxproj @@ -266,7 +266,7 @@ Fast false true - /Zc:threadSafeInit- /utf-8 %(AdditionalOptions) + /utf-8 %(AdditionalOptions) /Zc:char8_t- %(AdditionalOptions) @@ -308,7 +308,7 @@ Fast false true - /Zc:threadSafeInit- /utf-8 %(AdditionalOptions) + /utf-8 %(AdditionalOptions) /Zc:char8_t- %(AdditionalOptions) @@ -349,7 +349,7 @@ Fast false true - /Zc:threadSafeInit- /utf-8 %(AdditionalOptions) + /utf-8 %(AdditionalOptions) /Zc:char8_t- %(AdditionalOptions) @@ -389,7 +389,7 @@ Fast false true - /Zc:threadSafeInit- /utf-8 %(AdditionalOptions) + /utf-8 %(AdditionalOptions) /Zc:char8_t- %(AdditionalOptions) @@ -430,7 +430,7 @@ Fast false true - /Zc:threadSafeInit- /utf-8 %(AdditionalOptions) + /utf-8 %(AdditionalOptions) /Zc:char8_t- %(AdditionalOptions) @@ -475,7 +475,7 @@ Fast false true - /Zc:threadSafeInit- /utf-8 %(AdditionalOptions) + /utf-8 %(AdditionalOptions) /Zc:char8_t- %(AdditionalOptions) @@ -634,8 +634,12 @@ _X86_;%(PreprocessorDefinitions) _AMD64_;%(PreprocessorDefinitions) - - + + /Zc:threadSafeInit- %(AdditionalOptions) + + + /Zc:threadSafeInit- %(AdditionalOptions) + diff --git a/source/d3d12/d3d12_pipeline_library.cpp b/source/d3d12/d3d12_pipeline_library.cpp index c48f15088c..83f16fba05 100644 --- a/source/d3d12/d3d12_pipeline_library.cpp +++ b/source/d3d12/d3d12_pipeline_library.cpp @@ -25,7 +25,7 @@ bool D3D12PipelineLibrary::check_and_upgrade_interface(REFIID riid) riid == __uuidof(ID3D12DeviceChild)) return true; - static const IID iid_lookup[] = { + static constexpr IID iid_lookup[] = { __uuidof(ID3D12PipelineLibrary), __uuidof(ID3D12PipelineLibrary1), }; From 99ccc7e923b5177efd6a6c3abe990954d73a7ace Mon Sep 17 00:00:00 2001 From: crosire Date: Sat, 30 May 2026 14:08:15 +0200 Subject: [PATCH 03/26] Clean up code and add documentation note regarding synchronization to some events --- include/reshade_api_pipeline.hpp | 4 ++-- include/reshade_api_resource.hpp | 8 ++++---- include/reshade_events.hpp | 5 +++++ source/d3d11/d3d11_impl_command_list.cpp | 18 +++++++++--------- source/effect_codegen_glsl.cpp | 4 ++-- source/input.cpp | 2 +- 6 files changed, 23 insertions(+), 18 deletions(-) diff --git a/include/reshade_api_pipeline.hpp b/include/reshade_api_pipeline.hpp index dae8808ab7..845ee20708 100644 --- a/include/reshade_api_pipeline.hpp +++ b/include/reshade_api_pipeline.hpp @@ -147,8 +147,8 @@ namespace reshade::api push_descriptors_with_ranges = 3, push_descriptors_with_ranges_and_flags = 5, - descriptor_table_with_static_samplers [[deprecated]] = descriptor_table_with_flags, - push_descriptors_with_static_samplers [[deprecated]] = push_descriptors_with_ranges_and_flags, + descriptor_table_with_static_samplers [[deprecated("use 'pipeline_layout_param_type::descriptor_table_with_flags' instead")]] = descriptor_table_with_flags, + push_descriptors_with_static_samplers [[deprecated("use 'pipeline_layout_param_type::push_descriptors_with_ranges_and_flags' instead")]] = push_descriptors_with_ranges_and_flags, }; /// diff --git a/include/reshade_api_resource.hpp b/include/reshade_api_resource.hpp index 965a2a680d..9e2b37e1f8 100644 --- a/include/reshade_api_resource.hpp +++ b/include/reshade_api_resource.hpp @@ -190,10 +190,10 @@ namespace reshade::api /// gpu_upload = 6, - gpu_only [[deprecated]] = default_, - cpu_to_gpu [[deprecated]] = upload, - gpu_to_cpu [[deprecated]] = readback, - cpu_only [[deprecated]] = scratch, + gpu_only [[deprecated("use 'memory_heap::default_' instead")]] = default_, + cpu_to_gpu [[deprecated("use 'memory_heap::upload' instead")]] = upload, + gpu_to_cpu [[deprecated("use 'memory_heap::readback' instead")]] = readback, + cpu_only [[deprecated("use 'memory_heap::scratch' instead")]] = scratch, }; /// diff --git a/include/reshade_events.hpp b/include/reshade_events.hpp index 70f5c3b52c..4d0a3218d2 100644 --- a/include/reshade_events.hpp +++ b/include/reshade_events.hpp @@ -403,6 +403,9 @@ namespace reshade /// /// Callback function signature: void (api::device *device, api::resource resource) /// + /// + /// This may be called from within other API calls, e.g. , so be careful with synchronization primitives in the callback. + /// destroy_resource, /// @@ -496,6 +499,7 @@ namespace reshade /// /// /// Is not called in D3D12 (since resource views are descriptor handles instead of objects there). + /// This may be called from within other API calls, e.g. , so be careful with synchronization primitives in the callback. /// destroy_resource_view, @@ -753,6 +757,7 @@ namespace reshade /// /// /// Is not called in D3D9. + /// This may be called from within other API calls, e.g. , so be careful with synchronization primitives in the callback. /// destroy_pipeline, diff --git a/source/d3d11/d3d11_impl_command_list.cpp b/source/d3d11/d3d11_impl_command_list.cpp index e5eb42fc10..b0bcfffbf5 100644 --- a/source/d3d11/d3d11_impl_command_list.cpp +++ b/source/d3d11/d3d11_impl_command_list.cpp @@ -813,28 +813,28 @@ void reshade::d3d11::device_context_impl::query_acceleration_structures(uint32_t assert(false); } -void reshade::d3d11::device_context_impl::update_buffer_region(const void *data, api::resource dest, uint64_t dest_offset, uint64_t size) +void reshade::d3d11::device_context_impl::update_buffer_region(const void *data, api::resource dst, uint64_t dst_offset, uint64_t size) { - assert(dest != 0); + assert(dst != 0); if (UINT64_MAX == size) { D3D11_BUFFER_DESC desc; - reinterpret_cast(dest.handle)->GetDesc(&desc); + reinterpret_cast(dst.handle)->GetDesc(&desc); size = desc.ByteWidth; } - assert(dest_offset <= std::numeric_limits::max() && size <= std::numeric_limits::max()); + assert(dst_offset <= std::numeric_limits::max() && size <= std::numeric_limits::max()); - const D3D11_BOX box = { static_cast(dest_offset), 0, 0, static_cast(dest_offset + size), 1, 1 }; + const D3D11_BOX box = { static_cast(dst_offset), 0, 0, static_cast(dst_offset + size), 1, 1 }; - _orig->UpdateSubresource(reinterpret_cast(dest.handle), 0, dest_offset != 0 ? &box : nullptr, data, static_cast(size), 0); + _orig->UpdateSubresource(reinterpret_cast(dst.handle), 0, dst_offset != 0 ? &box : nullptr, data, static_cast(size), 0); } -void reshade::d3d11::device_context_impl::update_texture_region(const api::subresource_data &data, api::resource dest, uint32_t dest_subresource, const api::subresource_box *dest_box) +void reshade::d3d11::device_context_impl::update_texture_region(const api::subresource_data &data, api::resource dst, uint32_t dst_subresource, const api::subresource_box *dst_box) { - assert(dest != 0); + assert(dst != 0); - _orig->UpdateSubresource(reinterpret_cast(dest.handle), dest_subresource, reinterpret_cast(dest_box), data.data, data.row_pitch, data.slice_pitch); + _orig->UpdateSubresource(reinterpret_cast(dst.handle), dst_subresource, reinterpret_cast(dst_box), data.data, data.row_pitch, data.slice_pitch); } void reshade::d3d11::device_context_impl::begin_debug_event(const char *label, const float[4]) diff --git a/source/effect_codegen_glsl.cpp b/source/effect_codegen_glsl.cpp index 4f90fde6aa..fc28b9feb7 100644 --- a/source/effect_codegen_glsl.cpp +++ b/source/effect_codegen_glsl.cpp @@ -700,7 +700,7 @@ class codegen_glsl : public codegen { assert(!name.empty()); - static const std::unordered_set s_reserverd_names = { + static const std::unordered_set s_reserved_names = { "common", "partition", "input", "output", "active", "filter", "superp", "invariant", "attribute", "varying", "buffer", "resource", "coherent", "readonly", "writeonly", "layout", "flat", "smooth", "lowp", "mediump", "highp", "precision", "patch", "subroutine", @@ -727,7 +727,7 @@ class codegen_glsl : public codegen }; // Escape reserved names so that they do not fail to compile - if (name.compare(0, 3, "gl_") == 0 || s_reserverd_names.count(name)) + if (name.compare(0, 3, "gl_") == 0 || s_reserved_names.count(name)) // Append an underscore at start instead of the end, since another one may get added in 'define_name' when there is a suffix // This is guaranteed to not clash with user defined names, since those starting with an underscore are filtered out in 'define_name' name = '_' + name; diff --git a/source/input.cpp b/source/input.cpp index be6c957a38..6600e4c24e 100644 --- a/source/input.cpp +++ b/source/input.cpp @@ -121,7 +121,7 @@ std::string reshade::input::key_name(unsigned int keycode) if (keycode == key_home && is_keyboard_layout_german()) return "Pos1"; - static const char *keyboard_keys[256] = { + static const char *const keyboard_keys[256] = { "", "Left Mouse", "Right Mouse", "Cancel", "Middle Mouse", "X1 Mouse", "X2 Mouse", "", "Backspace", "Tab", "", "", "Clear", "Enter", "", "", "Shift", "Control", "Alt", "Pause", "Caps Lock", "", "", "", "", "", "", "Escape", "", "", "", "", "Space", "Page Up", "Page Down", "End", "Home", "Left Arrow", "Up Arrow", "Right Arrow", "Down Arrow", "Select", "", "", "Print Screen", "Insert", "Delete", "Help", From c6a191b7592908791bfb1feae88b5e3b18bfe53e Mon Sep 17 00:00:00 2001 From: crosire Date: Sat, 30 May 2026 15:21:48 +0200 Subject: [PATCH 04/26] Fix build with Visual Studio 2017 Closes #422 --- ReShade.vcxproj | 2 +- ReShadeFX.vcxproj | 2 +- ReShadeFXC.vcxproj | 2 +- ReShadeInject.vcxproj | 2 +- deps/ImGui.vcxproj | 2 +- deps/MinHook.vcxproj | 2 +- deps/fpng.vcxproj | 2 +- deps/glad.vcxproj | 2 +- deps/jxl_simple_lossless.vcxproj | 6 ++++-- deps/stb.vcxproj | 2 +- source/d3d10/d3d10_device.cpp | 1 + source/d3d11/d3d11_device.cpp | 1 + source/d3d12/d3d12_impl_type_convert.cpp | 22 ++++++++++++++++++++- source/d3d12/d3d12_impl_type_convert.hpp | 20 +------------------ source/effect_codegen_dxil.cpp | 2 +- source/vulkan/vulkan_impl_command_queue.cpp | 2 +- source/vulkan/vulkan_impl_type_convert.cpp | 2 +- 17 files changed, 40 insertions(+), 34 deletions(-) diff --git a/ReShade.vcxproj b/ReShade.vcxproj index 7bf8a717f5..1948e3e867 100644 --- a/ReShade.vcxproj +++ b/ReShade.vcxproj @@ -45,7 +45,7 @@ {0401ADF5-D085-4A3D-95B2-D9B7896BB338} Win32Proj - 10.0.17763.0 + 10.0.19041.0 10.0 diff --git a/ReShadeFX.vcxproj b/ReShadeFX.vcxproj index 88049761a8..33c491b24d 100644 --- a/ReShadeFX.vcxproj +++ b/ReShadeFX.vcxproj @@ -21,7 +21,7 @@ {D1C2099B-BEC7-4993-8947-01D4A1F7EAE2} Win32Proj - 10.0.17763.0 + 10.0.19041.0 10.0 ReShade FX diff --git a/ReShadeFXC.vcxproj b/ReShadeFXC.vcxproj index 068f0eec36..c3336697b2 100644 --- a/ReShadeFXC.vcxproj +++ b/ReShadeFXC.vcxproj @@ -21,7 +21,7 @@ {65640687-0740-4681-B018-17DBF33E061C} Win32Proj - 10.0.17763.0 + 10.0.19041.0 10.0 FXC diff --git a/ReShadeInject.vcxproj b/ReShadeInject.vcxproj index 4629b648bc..69efbbb671 100644 --- a/ReShadeInject.vcxproj +++ b/ReShadeInject.vcxproj @@ -21,7 +21,7 @@ {D388A856-4100-49AB-8FAF-62D63F8AC155} Win32Proj - 10.0.17763.0 + 10.0.19041.0 10.0 Injector diff --git a/deps/ImGui.vcxproj b/deps/ImGui.vcxproj index 33fe23d328..fbba692529 100644 --- a/deps/ImGui.vcxproj +++ b/deps/ImGui.vcxproj @@ -20,7 +20,7 @@ {9A62233B-0B70-4B48-91E8-35AA666BC32E} - 10.0.17763.0 + 10.0.19041.0 10.0 diff --git a/deps/MinHook.vcxproj b/deps/MinHook.vcxproj index 8c9c80e7e9..6376a72945 100644 --- a/deps/MinHook.vcxproj +++ b/deps/MinHook.vcxproj @@ -20,7 +20,7 @@ {783FEDFB-5124-4F8C-87BC-70AA8490266B} - 10.0.17763.0 + 10.0.19041.0 10.0 diff --git a/deps/fpng.vcxproj b/deps/fpng.vcxproj index e164db5096..d80db0839e 100644 --- a/deps/fpng.vcxproj +++ b/deps/fpng.vcxproj @@ -20,7 +20,7 @@ {79F676AF-1A25-49BB-9549-E533D162FB0A} - 10.0.17763.0 + 10.0.19041.0 10.0 diff --git a/deps/glad.vcxproj b/deps/glad.vcxproj index 2a16e5591a..792c3957a1 100644 --- a/deps/glad.vcxproj +++ b/deps/glad.vcxproj @@ -20,7 +20,7 @@ {09C0D610-9B82-40D8-B37E-0D26E3BFF77F} - 10.0.17763.0 + 10.0.19041.0 10.0 diff --git a/deps/jxl_simple_lossless.vcxproj b/deps/jxl_simple_lossless.vcxproj index 96efb3cdfb..c6bf50bb60 100644 --- a/deps/jxl_simple_lossless.vcxproj +++ b/deps/jxl_simple_lossless.vcxproj @@ -20,7 +20,7 @@ {54216c95-7b51-46d3-ab11-4b341e76a774} - 10.0.17763.0 + 10.0.19041.0 10.0 @@ -86,7 +86,9 @@ - + + intrin.h + diff --git a/deps/stb.vcxproj b/deps/stb.vcxproj index 42c3cdbc99..b062cdae65 100644 --- a/deps/stb.vcxproj +++ b/deps/stb.vcxproj @@ -20,7 +20,7 @@ {723BDEF8-4A39-4961-BDAB-54074012FF47} - 10.0.17763.0 + 10.0.19041.0 10.0 diff --git a/source/d3d10/d3d10_device.cpp b/source/d3d10/d3d10_device.cpp index e0b998662d..979d63fd10 100644 --- a/source/d3d10/d3d10_device.cpp +++ b/source/d3d10/d3d10_device.cpp @@ -10,6 +10,7 @@ #include "com_utils.hpp" #include "hook_manager.hpp" #include "addon_manager.hpp" +#include // _ReturnAddress using reshade::d3d10::to_handle; diff --git a/source/d3d11/d3d11_device.cpp b/source/d3d11/d3d11_device.cpp index c623ca48e0..ecacfb95a2 100644 --- a/source/d3d11/d3d11_device.cpp +++ b/source/d3d11/d3d11_device.cpp @@ -12,6 +12,7 @@ #include "com_utils.hpp" #include "hook_manager.hpp" #include "addon_manager.hpp" +#include // _ReturnAddress using reshade::d3d11::to_handle; diff --git a/source/d3d12/d3d12_impl_type_convert.cpp b/source/d3d12/d3d12_impl_type_convert.cpp index 2336c5d75f..169113c5b5 100644 --- a/source/d3d12/d3d12_impl_type_convert.cpp +++ b/source/d3d12/d3d12_impl_type_convert.cpp @@ -5,7 +5,7 @@ #include "d3d12_impl_type_convert.hpp" #include -#include // std::copy_n, std::fill_n +#include // std::copy_n, std::fill_n, std::max auto reshade::d3d12::convert_format(api::format format) -> DXGI_FORMAT { @@ -54,6 +54,26 @@ auto reshade::d3d12::convert_color_space(DXGI_COLOR_SPACE_TYPE type) -> api::col } } +void reshade::d3d12::convert_subresource_box(const reshade::api::subresource_box *box, const D3D12_RESOURCE_DESC &desc, uint32_t subresource, UINT &width, UINT &height, UINT &depth) +{ + if (box != nullptr) + { + width = box->width(); + height = box->height(); + depth = box->depth(); + } + else + { + width = std::max(1u, static_cast(desc.Width) >> (subresource % desc.MipLevels)); + height = std::max(1u, desc.Height >> (subresource % desc.MipLevels)); + + if (desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) + depth = std::max(1u, static_cast(desc.DepthOrArraySize) >> (subresource % desc.MipLevels)); + else + depth = 1; + } +} + auto reshade::d3d12::convert_access_to_usage(D3D12_BARRIER_ACCESS access) -> api::resource_usage { if (access == D3D12_BARRIER_ACCESS_COMMON) diff --git a/source/d3d12/d3d12_impl_type_convert.hpp b/source/d3d12/d3d12_impl_type_convert.hpp index 217894a29a..c9549e0186 100644 --- a/source/d3d12/d3d12_impl_type_convert.hpp +++ b/source/d3d12/d3d12_impl_type_convert.hpp @@ -48,25 +48,7 @@ namespace reshade::d3d12 auto convert_color_space(api::color_space type) -> DXGI_COLOR_SPACE_TYPE; auto convert_color_space(DXGI_COLOR_SPACE_TYPE type) -> api::color_space; - inline void convert_subresource_box(const reshade::api::subresource_box *box, const D3D12_RESOURCE_DESC &desc, uint32_t subresource, UINT &width, UINT &height, UINT &depth) - { - if (box != nullptr) - { - width = box->width(); - height = box->height(); - depth = box->depth(); - } - else - { - width = std::max(1u, static_cast(desc.Width) >> (subresource % desc.MipLevels)); - height = std::max(1u, desc.Height >> (subresource % desc.MipLevels)); - - if (desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) - depth = std::max(1u, static_cast(desc.DepthOrArraySize) >> (subresource % desc.MipLevels)); - else - depth = 1; - } - } + void convert_subresource_box(const reshade::api::subresource_box *box, const D3D12_RESOURCE_DESC &desc, uint32_t subresource, UINT &width, UINT &height, UINT &depth); auto convert_access_to_usage(D3D12_BARRIER_ACCESS access) -> api::resource_usage; auto convert_barrier_layout_to_usage(D3D12_BARRIER_LAYOUT layout) -> api::resource_usage; diff --git a/source/effect_codegen_dxil.cpp b/source/effect_codegen_dxil.cpp index d2d3bb849a..eb6be90a26 100644 --- a/source/effect_codegen_dxil.cpp +++ b/source/effect_codegen_dxil.cpp @@ -67,7 +67,7 @@ class codegen_dxil final : public codegen_hlsl com_ptr result; - HRESULT hr = compiler->Compile(&hlsl_buffer, arguments, std::size(arguments), nullptr, IID_PPV_ARGS(&result)); + HRESULT hr = compiler->Compile(&hlsl_buffer, arguments, static_cast(std::size(arguments)), nullptr, IID_PPV_ARGS(&result)); if (result != nullptr) { result->GetStatus(&hr); diff --git a/source/vulkan/vulkan_impl_command_queue.cpp b/source/vulkan/vulkan_impl_command_queue.cpp index 2cb525e118..a83855364d 100644 --- a/source/vulkan/vulkan_impl_command_queue.cpp +++ b/source/vulkan/vulkan_impl_command_queue.cpp @@ -33,7 +33,7 @@ reshade::vulkan::command_queue_impl::command_queue_impl(device_impl *device, uin } // Always create queue synchronization semaphores, even for compute queues, e.g. for present from compute - for (int i = 0; i < std::size(_signal_semaphores); ++i) + for (unsigned int i = 0; i < std::size(_signal_semaphores); ++i) { VkSemaphoreCreateInfo sem_create_info { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; diff --git a/source/vulkan/vulkan_impl_type_convert.cpp b/source/vulkan/vulkan_impl_type_convert.cpp index df5f7d1036..992a0382a7 100644 --- a/source/vulkan/vulkan_impl_type_convert.cpp +++ b/source/vulkan/vulkan_impl_type_convert.cpp @@ -5,7 +5,7 @@ #include "vulkan_hooks.hpp" #include "vulkan_impl_type_convert.hpp" -#include // std::copy_n, std::fill_n, std::find_if +#include // std::copy_n, std::fill_n, std::find_if, std::max auto reshade::vulkan::convert_format(api::format format, VkComponentMapping *components) -> VkFormat { From 4c6c88a4fbb049ad227e526fe9e12f5136e41b09 Mon Sep 17 00:00:00 2001 From: crosire Date: Sun, 7 Jun 2026 13:22:21 +0200 Subject: [PATCH 05/26] Avoid Vulkan render pass layout transition for more initial layouts --- source/vulkan/vulkan_hooks_command_list.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/source/vulkan/vulkan_hooks_command_list.cpp b/source/vulkan/vulkan_hooks_command_list.cpp index 1f883942f8..747090ebf2 100644 --- a/source/vulkan/vulkan_hooks_command_list.cpp +++ b/source/vulkan/vulkan_hooks_command_list.cpp @@ -79,7 +79,8 @@ static bool invoke_begin_render_pass_event(const reshade::vulkan::device_impl *d std::copy_n(begin_info->pClearValues[a].color.float32, 4, rt.clear_color); } - if (desc.initialLayout != VK_IMAGE_LAYOUT_UNDEFINED && desc.initialLayout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) + if (desc.initialLayout != VK_IMAGE_LAYOUT_UNDEFINED && + desc.initialLayout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { VkImageMemoryBarrier &transition = transitions[num_transitions++]; transition = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; @@ -124,7 +125,10 @@ static bool invoke_begin_render_pass_event(const reshade::vulkan::device_impl *d ds.clear_stencil = static_cast(begin_info->pClearValues[a].depthStencil.stencil); } - if (desc.initialLayout != VK_IMAGE_LAYOUT_UNDEFINED && desc.initialLayout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) + if (desc.initialLayout != VK_IMAGE_LAYOUT_UNDEFINED && + desc.initialLayout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL && + desc.initialLayout != VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL && + desc.initialLayout != VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL) { VkImageMemoryBarrier &transition = transitions[num_transitions++]; transition = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; From 34e108a6b4828073271773a35fdb2f3bfa31af2a Mon Sep 17 00:00:00 2001 From: crosire Date: Sun, 7 Jun 2026 13:34:32 +0200 Subject: [PATCH 06/26] Fix "create_pipeline" event not being invoked in Vulkan when there are custom allocator callbacks --- source/vulkan/vulkan_hooks_device.cpp | 40 ++++++++++++++++++--------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/source/vulkan/vulkan_hooks_device.cpp b/source/vulkan/vulkan_hooks_device.cpp index cf04d5f191..7158ad02d4 100644 --- a/source/vulkan/vulkan_hooks_device.cpp +++ b/source/vulkan/vulkan_hooks_device.cpp @@ -1379,6 +1379,10 @@ VkResult VKAPI_CALL vkCreateGraphicsPipelines(VkDevice device, VkPipelineCache p RESHADE_VULKAN_GET_DEVICE_DISPATCH_PTR(CreateGraphicsPipelines, device_impl); #if RESHADE_ADDON >= 2 + // Disable custom allocator when pipeline may be overriden, so corresponding 'vkDestroyPipeline' is not called with mismatching callbacks + if (reshade::has_addon_event()) + pAllocator = nullptr; + VkResult result = VK_SUCCESS; for (uint32_t i = 0; i < createInfoCount; ++i) { @@ -1602,8 +1606,7 @@ VkResult VKAPI_CALL vkCreateGraphicsPipelines(VkDevice device, VkPipelineCache p subobjects.push_back({ reshade::api::pipeline_subobject_type::sample_count, 1, &sample_count }); } - if (pAllocator == nullptr && // Cannot replace pipeline if custom allocator is used, since corresponding 'vkDestroyPipeline' would be called with mismatching allocator callbacks - reshade::invoke_addon_event(device_impl, reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(subobjects.size()), subobjects.data())) + if (reshade::invoke_addon_event(device_impl, reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(subobjects.size()), subobjects.data())) { static_assert(sizeof(*pPipelines) == sizeof(reshade::api::pipeline)); @@ -1647,6 +1650,10 @@ VkResult VKAPI_CALL vkCreateComputePipelines(VkDevice device, VkPipelineCache pi RESHADE_VULKAN_GET_DEVICE_DISPATCH_PTR(CreateComputePipelines, device_impl); #if RESHADE_ADDON >= 2 + // Disable custom allocator when pipeline may be overriden, so corresponding 'vkDestroyPipeline' is not called with mismatching callbacks + if (reshade::has_addon_event()) + pAllocator = nullptr; + VkResult result = VK_SUCCESS; for (uint32_t i = 0; i < createInfoCount; ++i) { @@ -1687,8 +1694,7 @@ VkResult VKAPI_CALL vkCreateComputePipelines(VkDevice device, VkPipelineCache pi { reshade::api::pipeline_subobject_type::flags, 1, &flags } }; - if (pAllocator == nullptr && // Cannot replace pipeline if custom allocator is used, since corresponding 'vkDestroyPipeline' would be called with mismatching allocator callbacks - reshade::invoke_addon_event(device_impl, reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(std::size(subobjects)), subobjects)) + if (reshade::invoke_addon_event(device_impl, reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(std::size(subobjects)), subobjects)) { result = device_impl->create_pipeline( reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(std::size(subobjects)), subobjects, reinterpret_cast(&pPipelines[i])) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; @@ -1729,6 +1735,10 @@ VkResult VKAPI_CALL vkCreateRayTracingPipelinesKHR(VkDevice device, VkDeferredOp RESHADE_VULKAN_GET_DEVICE_DISPATCH_PTR(CreateRayTracingPipelinesKHR, device_impl); #if RESHADE_ADDON >= 2 + // Disable custom allocator when pipeline may be overriden, so corresponding 'vkDestroyPipeline' is not called with mismatching callbacks + if (reshade::has_addon_event()) + pAllocator = nullptr; + VkResult result = VK_SUCCESS; for (uint32_t i = 0; i < createInfoCount; ++i) { @@ -1874,8 +1884,7 @@ VkResult VKAPI_CALL vkCreateRayTracingPipelinesKHR(VkDevice device, VkDeferredOp subobjects.push_back({ reshade::api::pipeline_subobject_type::max_attribute_size, 1, const_cast(&create_info.pLibraryInterface->maxPipelineRayHitAttributeSize) }); } - if (pAllocator == nullptr && // Cannot replace pipeline if custom allocator is used, since corresponding 'vkDestroyPipeline' would be called with mismatching allocator callbacks - reshade::invoke_addon_event(device_impl, reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(subobjects.size()), subobjects.data())) + if (reshade::invoke_addon_event(device_impl, reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(subobjects.size()), subobjects.data())) { static_assert(sizeof(*pPipelines) == sizeof(reshade::api::pipeline)); @@ -1926,7 +1935,7 @@ void VKAPI_CALL vkDestroyPipeline(VkDevice device, VkPipeline pipeline, cons #if RESHADE_ADDON >= 2 reshade::invoke_addon_event(device_impl, reshade::api::pipeline { (uint64_t)pipeline }); - if (pAllocator == nullptr) + if (reshade::has_addon_event()) { device_impl->destroy_pipeline(reshade::api::pipeline { (uint64_t)pipeline }); return; @@ -1945,7 +1954,11 @@ VkResult VKAPI_CALL vkCreatePipelineLayout(VkDevice device, const VkPipelineLayo VkResult result = VK_SUCCESS; #if RESHADE_ADDON >= 2 - bool created_via_addon_event = false; + // Disable custom allocator when pipeline layout may be overriden, so corresponding 'vkDestroyPipelineLayout' is not called with mismatching callbacks + if (reshade::has_addon_event()) + pAllocator = nullptr; + + bool owns_set_layouts = false; const uint32_t set_desc_count = pCreateInfo->setLayoutCount; uint32_t param_count = set_desc_count + pCreateInfo->pushConstantRangeCount; @@ -2007,15 +2020,14 @@ VkResult VKAPI_CALL vkCreatePipelineLayout(VkDevice device, const VkPipelineLayo reshade::api::pipeline_layout_param *param_data = params.data(); - if (pAllocator == nullptr && // Cannot replace pipeline layout if custom allocator is used, since corresponding 'vkDestroyPipelineLayout' would be called with mismatching allocator callbacks - reshade::invoke_addon_event(device_impl, param_count, param_data)) + if (reshade::invoke_addon_event(device_impl, param_count, param_data)) { static_assert(sizeof(*pPipelineLayout) == sizeof(reshade::api::pipeline_layout)); assert(pCreateInfo->pNext == nullptr); // 'device_impl::create_pipeline_layout' does not support extension structures result = device_impl->create_pipeline_layout(param_count, param_data, reinterpret_cast(pPipelineLayout)) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; - created_via_addon_event = result == VK_SUCCESS; + owns_set_layouts = true; } else #endif @@ -2032,7 +2044,7 @@ VkResult VKAPI_CALL vkCreatePipelineLayout(VkDevice device, const VkPipelineLayo } #if RESHADE_ADDON >= 2 - if (!created_via_addon_event) + if (!owns_set_layouts) { reshade::vulkan::object_data &data = *device_impl->register_object(*pPipelineLayout); data.set_layouts.assign(pCreateInfo->pSetLayouts, pCreateInfo->pSetLayouts + pCreateInfo->setLayoutCount); @@ -2055,13 +2067,15 @@ void VKAPI_CALL vkDestroyPipelineLayout(VkDevice device, VkPipelineLayout pi #if RESHADE_ADDON >= 2 reshade::invoke_addon_event(device_impl, reshade::api::pipeline_layout { (uint64_t)pipelineLayout }); - if (pAllocator == nullptr) + if (reshade::has_addon_event()) { // Clean up any samplers and descriptor set layouts that may have been created when an add-on modified the creation of the pipeline layout device_impl->destroy_pipeline_layout(reshade::api::pipeline_layout { (uint64_t)pipelineLayout }); return; } + assert(!device_impl->get_private_data_for_object(pipelineLayout)->owns_set_layouts); + device_impl->unregister_object(pipelineLayout); #endif From 1623e5776088eeb0372f5536d02f5d000ee1ebf1 Mon Sep 17 00:00:00 2001 From: crosire Date: Wed, 10 Jun 2026 00:43:01 +0200 Subject: [PATCH 07/26] Add header row to global preprocessor definitions list --- res/lang_ar-SA.rc2 | 2 ++ res/lang_bg-BG.rc2 | 2 ++ res/lang_de-DE.rc2 | 2 ++ res/lang_en-US.rc2 | 2 ++ res/lang_es-ES.rc2 | 2 ++ res/lang_fr-FR.rc2 | 2 ++ res/lang_ja-JP.rc2 | 2 ++ res/lang_ko-KR.rc2 | 2 ++ res/lang_pl-PL.rc2 | 2 ++ res/lang_pt-BR.rc2 | 2 ++ res/lang_ru-RU.rc2 | 2 ++ res/lang_sl-SI.rc2 | 2 ++ res/lang_th-TH.rc2 | 2 ++ res/lang_tr-TR.rc2 | 2 ++ res/lang_zh-CN.rc2 | 2 ++ res/lang_zh-HK.rc2 | 2 ++ source/runtime_gui.cpp | 8 +++++++- 17 files changed, 39 insertions(+), 1 deletion(-) diff --git a/res/lang_ar-SA.rc2 b/res/lang_ar-SA.rc2 index 0ad267669c..1d48013531 100644 --- a/res/lang_ar-SA.rc2 +++ b/res/lang_ar-SA.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "ﺔﻣﺎﻌﻟﺍ ﻖﺒﺴﻤﻟﺍ ﺞﻟﺎﻌﻤﻟﺍ ﺕﺎﻔﻳﺮﻌﺗ ﻞﻳﺪﻌﺗ" 2314 "ﻡﺎﻋ" 38344 "ﻲﻟﺎﺤﻟﺍ ﺐﻟﺎﻘﻟﺍ" +19882 "الاسم" +22129 "القيمة" 10646 "؟'%s' ﻖﺒﺴﻤﻟﺍ ﺞﻟﺎﻌﻤﻟﺍ ﻒﻳﺮﻌﺗ ﺔﻟﺍﺯﺇ ًﺎﻘﺣ ﺪﻳﺮﺗ ﻞﻫ" 63179 "ﻖﻴﺒﻄﺗ" 9813 "ﻲﺿﺍﺮﺘﻓﻼﻟ ﻞﻜﻟﺍ ﻂﺒﺿ ﺓﺩﺎﻋﺇ" diff --git a/res/lang_bg-BG.rc2 b/res/lang_bg-BG.rc2 index 4b73b9dfaf..872cf891ed 100644 --- a/res/lang_bg-BG.rc2 +++ b/res/lang_bg-BG.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "Редактирай глобалните дефиниции за предпроцесора" 2314 "Глобален" 38344 "Текущ preset" +19882 "Име" +22129 "Стойност" 10646 "Наистина ли искате да премахнете дефиницията за предпроцесора '%s'?" 63179 "Прилагайте" 9813 "Нулиране на всички към стойностите им по подразбиране" diff --git a/res/lang_de-DE.rc2 b/res/lang_de-DE.rc2 index a5a2e653c8..b3b6778664 100644 --- a/res/lang_de-DE.rc2 +++ b/res/lang_de-DE.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "Globale Präprozessordefinitionen bearbeiten" 2314 "Global" 38344 "Aktuelles Preset" +19882 "Name" +22129 "Wert" 10646 "Möchtest du die Präprozessordefinition '%s' wirklich entfernen?" 63179 "Anwenden" 9813 "Alle zurücksetzen" diff --git a/res/lang_en-US.rc2 b/res/lang_en-US.rc2 index a08e4ee807..f1a1172b2a 100644 --- a/res/lang_en-US.rc2 +++ b/res/lang_en-US.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "Edit global preprocessor definitions" 2314 "Global" 38344 "Current Preset" +19882 "Name" +22129 "Value" 10646 "Do you really want to remove the preprocessor definition '%s'?" 63179 "Apply" 9813 "Reset all to default" diff --git a/res/lang_es-ES.rc2 b/res/lang_es-ES.rc2 index f8bc0f48d5..8fd346cc81 100644 --- a/res/lang_es-ES.rc2 +++ b/res/lang_es-ES.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "Editar definiciones globales del preprocesador" 2314 "Global" 38344 "Preset Actual" +19882 "Nombre" +22129 "Valor" 10646 "¿Remover la definición del preprocesador '%s'?" 63179 "Aplicar" 9813 "Resetear todo" diff --git a/res/lang_fr-FR.rc2 b/res/lang_fr-FR.rc2 index 69feac2bc4..052887a1cc 100644 --- a/res/lang_fr-FR.rc2 +++ b/res/lang_fr-FR.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "Éditer les définitions des pré-processeurs globaux" 2314 "Global" 38344 "Configuration actuelle" +19882 "Nom" +22129 "Valeur" 10646 "Êtes vous certain de vouloir supprimer la définition du pré-processeur '%s'?" 63179 "Appliquer" 9813 "Réinitialiser aux valeurs par défaut" diff --git a/res/lang_ja-JP.rc2 b/res/lang_ja-JP.rc2 index 8f3a2380ad..a49d4a4240 100644 --- a/res/lang_ja-JP.rc2 +++ b/res/lang_ja-JP.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "プリプロセッサの定義を編集" 2314 "共通" 38344 "プリセット" +19882 "名前" +22129 "値" 10646 "プリプロセッサの定義'%s'を削除します。" 63179 "適用する" 9813 "すべてを初期値にリセット" diff --git a/res/lang_ko-KR.rc2 b/res/lang_ko-KR.rc2 index 3f64b04b62..c9a8231e41 100644 --- a/res/lang_ko-KR.rc2 +++ b/res/lang_ko-KR.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "공통 프리프로세서 정의 편집" 2314 "공통" 38344 "현재 사전 설정" +19882 "이름" +22129 "값" 10646 "프리프로세서 정의 '%s'을(를) 제거할까요?" 63179 "신청하기" 9813 "모두 기본값으로 재설정" diff --git a/res/lang_pl-PL.rc2 b/res/lang_pl-PL.rc2 index 16643e3360..dec19a375a 100644 --- a/res/lang_pl-PL.rc2 +++ b/res/lang_pl-PL.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "Edytuj globalne definicje preprocesora" 2314 "Globalne" 38344 "Obecny profil" +19882 "Nazwa" +22129 "Wartość" 10646 "Czy na pewno chcesz usunąć definicję preprocesora '%s'?" 63179 "Zatwierdź" 9813 "Zresetuj wszystko do wartości domyślnych" diff --git a/res/lang_pt-BR.rc2 b/res/lang_pt-BR.rc2 index 421c5b5d57..95a15ec025 100644 --- a/res/lang_pt-BR.rc2 +++ b/res/lang_pt-BR.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "Editar definições globais do pré-processador" 2314 "Global" 38344 "Preset atual" +19882 "Nome" +22129 "Valor" 10646 "Você realmente deseja remover a definição do pré-processador '%s'?" 63179 "Aplicar" 9813 "Resetar tudo para o padrão" diff --git a/res/lang_ru-RU.rc2 b/res/lang_ru-RU.rc2 index 42489eff57..9de80c5983 100644 --- a/res/lang_ru-RU.rc2 +++ b/res/lang_ru-RU.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "Редактирование глобальных определений препроцессора" 2314 "Глобальное" 38344 "Текущий пресет" +19882 "Имя" +22129 "Значение" 10646 "Вы действительно хотите удалить определение препроцессора '%s'?" 63179 "Применить" 9813 "Сброс всего на стандарт" diff --git a/res/lang_sl-SI.rc2 b/res/lang_sl-SI.rc2 index 79bd65544a..82a749a916 100644 --- a/res/lang_sl-SI.rc2 +++ b/res/lang_sl-SI.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "Uredi globalne predprocesorske definicije" 2314 "Globalno" 38344 "Trenutna prednastavitev" +19882 "Ime" +22129 "Vrednost" 10646 "Ali res želite odstraniti predprocesorsko definicijo '%s'?" 63179 "Uporabi" 9813 "Ponastavi vse na privzeto" diff --git a/res/lang_th-TH.rc2 b/res/lang_th-TH.rc2 index fd53977a38..8f4db0be60 100644 --- a/res/lang_th-TH.rc2 +++ b/res/lang_th-TH.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "แก้ไขคำนิยาม preprocessor ทั่วไป" 2314 "ทั่วไป" 38344 "พรีเซ็ตปัจจุบัน" +19882 "ชื่อ" +22129 "ค่า" 10646 "คุณแน่ใจหรือว่าต้องการลบคำนิยาม preprocessor '%s'?" 63179 "ใช้" 9813 "รีเซ็ตทั้งหมดเป็นค่าเริ่มต้น" diff --git a/res/lang_tr-TR.rc2 b/res/lang_tr-TR.rc2 index 9007537f6d..71dfa702d1 100644 --- a/res/lang_tr-TR.rc2 +++ b/res/lang_tr-TR.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "Genel önişlemci tanımlarını düzenle" 2314 "Genel" 38344 "Geçerli Ön Ayar" +19882 "Ad" +22129 "Değer" 10646 "'%s' önişlemci tanımını gerçekten kaldırmak istiyor musunuz?" 63179 "Uygula" 9813 "Tümünü varsayılanlara sıfırla" diff --git a/res/lang_zh-CN.rc2 b/res/lang_zh-CN.rc2 index 35759aa5da..9e2100dac5 100644 --- a/res/lang_zh-CN.rc2 +++ b/res/lang_zh-CN.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "编辑全局/预设预处理器定义" 2314 "全局" 38344 "当前预设" +19882 "名称" +22129 "值" 10646 "真的要移除全局预处理器 '%s' 吗?" 63179 "应用" 9813 "全部重置为默认值" diff --git a/res/lang_zh-HK.rc2 b/res/lang_zh-HK.rc2 index b8c8e03c80..7bff1d0852 100644 --- a/res/lang_zh-HK.rc2 +++ b/res/lang_zh-HK.rc2 @@ -170,6 +170,8 @@ BEGIN 17289 "編輯全局/預設預處理器定義" 2314 "全局" 38344 "當前預設" +19882 "名稱" +22129 "值" 10646 "真的要移除全域預處理器 '%s' 嗎?" 63179 "應用" 9813 "全部重置爲默認值" diff --git a/source/runtime_gui.cpp b/source/runtime_gui.cpp index f6572152f4..3217ae06ea 100644 --- a/source/runtime_gui.cpp +++ b/source/runtime_gui.cpp @@ -3427,7 +3427,7 @@ void reshade::runtime::draw_variable_editor() ImGui::SetWindowPos(popup_pos); bool global_modified = false, preset_modified = false; - float popup_height = (std::max(_global_preprocessor_definitions.size(), _preset_preprocessor_definitions[{}].size()) + 2) * ImGui::GetFrameHeightWithSpacing(); + float popup_height = (std::max(_global_preprocessor_definitions.size(), _preset_preprocessor_definitions[{}].size()) + 3) * ImGui::GetFrameHeightWithSpacing(); popup_height = std::min(popup_height, ImGui::GetWindowViewport()->Size.y - popup_pos.y - 20.0f); popup_height = std::max(popup_height, 42.0f); // Ensure window always has a minimum height const float button_size = ImGui::GetFrameHeight(); @@ -3453,6 +3453,12 @@ void reshade::runtime::draw_variable_editor() { if (ImGui::BeginTabItem(type.name.c_str())) { + ImGui::Dummy(ImVec2()); + ImGui::SameLine(0, button_spacing); + ImGui::TextUnformatted(_("Name")); + ImGui::SameLine(content_region_width * 0.66666666f, button_spacing); + ImGui::TextUnformatted(_("Value")); + if (&type.modified == &preset_modified) ImGui::BeginDisabled(!_auto_save_preset); From dcfe874d5ce3f36d0d91cb98034c79f332855be7 Mon Sep 17 00:00:00 2001 From: Ritsu <19637006+MohannedElfatih@users.noreply.github.com> Date: Sat, 13 Jun 2026 16:52:59 +0400 Subject: [PATCH 08/26] Preserve untouched Vulkan pipeline and layout create info when modified by an add-on (#425) --- source/vulkan/vulkan_hooks_device.cpp | 8 +- source/vulkan/vulkan_impl_device.cpp | 884 +++++++++++++++------ source/vulkan/vulkan_impl_device.hpp | 5 +- source/vulkan/vulkan_impl_type_convert.hpp | 1 + 4 files changed, 659 insertions(+), 239 deletions(-) diff --git a/source/vulkan/vulkan_hooks_device.cpp b/source/vulkan/vulkan_hooks_device.cpp index 7158ad02d4..5d6989b6e4 100644 --- a/source/vulkan/vulkan_hooks_device.cpp +++ b/source/vulkan/vulkan_hooks_device.cpp @@ -1610,10 +1610,8 @@ VkResult VKAPI_CALL vkCreateGraphicsPipelines(VkDevice device, VkPipelineCache p { static_assert(sizeof(*pPipelines) == sizeof(reshade::api::pipeline)); - assert(create_info.pNext == nullptr); // 'device_impl::create_pipeline' does not support extension structures apart from dynamic rendering - result = device_impl->create_pipeline( - reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(subobjects.size()), subobjects.data(), reinterpret_cast(&pPipelines[i])) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; + reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(subobjects.size()), subobjects.data(), reinterpret_cast(&pPipelines[i]), &create_info) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; } else { @@ -1697,7 +1695,7 @@ VkResult VKAPI_CALL vkCreateComputePipelines(VkDevice device, VkPipelineCache pi if (reshade::invoke_addon_event(device_impl, reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(std::size(subobjects)), subobjects)) { result = device_impl->create_pipeline( - reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(std::size(subobjects)), subobjects, reinterpret_cast(&pPipelines[i])) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; + reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(std::size(subobjects)), subobjects, reinterpret_cast(&pPipelines[i]), &create_info) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; } else { @@ -2026,7 +2024,7 @@ VkResult VKAPI_CALL vkCreatePipelineLayout(VkDevice device, const VkPipelineLayo assert(pCreateInfo->pNext == nullptr); // 'device_impl::create_pipeline_layout' does not support extension structures - result = device_impl->create_pipeline_layout(param_count, param_data, reinterpret_cast(pPipelineLayout)) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; + result = device_impl->create_pipeline_layout(param_count, param_data, reinterpret_cast(pPipelineLayout), pCreateInfo) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; owns_set_layouts = true; } else diff --git a/source/vulkan/vulkan_impl_device.cpp b/source/vulkan/vulkan_impl_device.cpp index 88e1e0d238..0e97d62989 100644 --- a/source/vulkan/vulkan_impl_device.cpp +++ b/source/vulkan/vulkan_impl_device.cpp @@ -4,6 +4,7 @@ */ #include "vulkan_impl_device.hpp" +#include "vulkan_hooks.hpp" #include "vulkan_impl_command_queue.hpp" #include "vulkan_impl_type_convert.hpp" #include "dll_log.hpp" @@ -1238,34 +1239,122 @@ void reshade::vulkan::device_impl::update_texture_region(const api::subresource_ destroy_resource(intermediate); } -bool reshade::vulkan::device_impl::create_shader_module(VkShaderStageFlagBits stage, const api::shader_desc &desc, VkPipelineShaderStageCreateInfo &stage_info, VkSpecializationInfo &spec_info, std::vector &spec_map) +struct pnext_link_restore { - spec_map.reserve(desc.spec_constants); - for (uint32_t i = 0; i < desc.spec_constants; ++i) - spec_map.push_back(VkSpecializationMapEntry { desc.spec_constant_ids[i], i * 4, sizeof(uint32_t) }); + VkBaseOutStructure *structure; + VkBaseOutStructure *pNext; +}; - spec_info.mapEntryCount = desc.spec_constants; - spec_info.pMapEntries = spec_map.data(); - spec_info.dataSize = desc.spec_constants * sizeof(uint32_t); - spec_info.pData = desc.spec_constant_values; +template +static void append_to_structure_chain(const void *&chain, T *node, std::vector *restore_links = nullptr) +{ + node->pNext = nullptr; + + if (chain == nullptr) + { + chain = node; + return; + } + + auto *tail = const_cast(reinterpret_cast(chain)); + while (tail->pNext != nullptr) + tail = tail->pNext; + + if (restore_links != nullptr) + restore_links->push_back({ tail, tail->pNext }); + + tail->pNext = reinterpret_cast(node); +} + +template +static bool replace_in_structure_chain(const void *&chain, const T *original_node, T *replacement_node, std::vector *restore_links = nullptr) +{ + replacement_node->pNext = original_node->pNext; + + if (chain == original_node) + { + chain = replacement_node; + return true; + } + + auto *current = const_cast(reinterpret_cast(chain)); + while (current != nullptr && current->pNext != nullptr) + { + if (current->pNext == reinterpret_cast(const_cast(original_node))) + { + if (restore_links != nullptr) + restore_links->push_back({ current, current->pNext }); + + current->pNext = reinterpret_cast(replacement_node); + return true; + } + + current = current->pNext; + } + + return false; +} - stage_info = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; +static void restore_pnext_links(const std::vector &restore_links) +{ + for (const pnext_link_restore &restore : restore_links) + restore.structure->pNext = restore.pNext; +} + +template +static T &replace_or_append_structure_chain_node(const void *&chain, VkStructureType type, T &node, std::vector &restore_links) +{ + if (const auto original_node = find_in_structure_chain(chain, type)) + { + node = *original_node; + replace_in_structure_chain(chain, original_node, &node, &restore_links); + } + else + { + node = { type }; + append_to_structure_chain(chain, &node, &restore_links); + } + + return node; +} + +static bool setup_shader_stage_inline(VkShaderStageFlagBits stage, const reshade::api::shader_desc &desc, const VkPipelineShaderStageCreateInfo *orig_stage_info, VkPipelineShaderStageCreateInfo &stage_info, VkShaderModuleCreateInfo &module_info, VkSpecializationInfo &spec_info, std::vector &spec_map, std::vector &restore_links) +{ + if (desc.code_size == 0 || desc.code == nullptr) + return false; + + stage_info = orig_stage_info != nullptr ? *orig_stage_info : VkPipelineShaderStageCreateInfo { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; stage_info.stage = stage; - stage_info.pName = desc.entry_point != nullptr ? desc.entry_point : "main"; - stage_info.pSpecializationInfo = &spec_info; + stage_info.module = VK_NULL_HANDLE; + + if (desc.entry_point != nullptr) + stage_info.pName = desc.entry_point; + else if (stage_info.pName == nullptr) + stage_info.pName = "main"; + + if (desc.spec_constants != 0) + { + spec_map.reserve(desc.spec_constants); + for (uint32_t i = 0; i < desc.spec_constants; ++i) + spec_map.push_back(VkSpecializationMapEntry { desc.spec_constant_ids[i], i * 4, sizeof(uint32_t) }); + + spec_info.mapEntryCount = desc.spec_constants; + spec_info.pMapEntries = spec_map.data(); + spec_info.dataSize = desc.spec_constants * sizeof(uint32_t); + spec_info.pData = desc.spec_constant_values; - VkShaderModuleCreateInfo create_info { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; - create_info.codeSize = desc.code_size; - create_info.pCode = static_cast(desc.code); + stage_info.pSpecializationInfo = &spec_info; + } + + replace_or_append_structure_chain_node(stage_info.pNext, VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, module_info, restore_links); + module_info.codeSize = desc.code_size; + module_info.pCode = static_cast(desc.code); - return vk.CreateShaderModule(_orig, &create_info, nullptr, &stage_info.module) == VK_SUCCESS; + return true; } bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline *out_pipeline) { - VkRenderPass render_pass = VK_NULL_HANDLE; - std::vector shaders; - api::shader_desc vs_desc = {}; api::shader_desc hs_desc = {}; api::shader_desc ds_desc = {}; @@ -1451,47 +1540,48 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, const size_t max_shader_stage_count = raygen_desc.size() + any_hit_desc.size() + closest_hit_desc.size() + miss_desc.size() + intersection_desc.size() + callable_desc.size(); std::vector shader_stage_info; + std::vector shader_module_info; std::vector spec_info; std::vector> spec_map; + std::vector pnext_restore_links; shader_stage_info.reserve(max_shader_stage_count); + shader_module_info.reserve(max_shader_stage_count); spec_info.reserve(max_shader_stage_count); spec_map.reserve(max_shader_stage_count); + const auto append_stage = [&](VkShaderStageFlagBits stage, const api::shader_desc &shader_desc) { + return setup_shader_stage_inline(stage, shader_desc, nullptr, shader_stage_info.emplace_back(), shader_module_info.emplace_back(), spec_info.emplace_back(), spec_map.emplace_back(), pnext_restore_links); + }; + for (const api::shader_desc &shader_desc : raygen_desc) { - if (!create_shader_module(VK_SHADER_STAGE_RAYGEN_BIT_KHR, shader_desc, shader_stage_info.emplace_back(), spec_info.emplace_back(), spec_map.emplace_back())) + if (!append_stage(VK_SHADER_STAGE_RAYGEN_BIT_KHR, shader_desc)) goto exit_failure; - shaders.push_back(shader_stage_info.back().module); } for (const api::shader_desc &shader_desc : any_hit_desc) { - if (!create_shader_module(VK_SHADER_STAGE_ANY_HIT_BIT_KHR, shader_desc, shader_stage_info.emplace_back(), spec_info.emplace_back(), spec_map.emplace_back())) + if (!append_stage(VK_SHADER_STAGE_ANY_HIT_BIT_KHR, shader_desc)) goto exit_failure; - shaders.push_back(shader_stage_info.back().module); } for (const api::shader_desc &shader_desc : closest_hit_desc) { - if (!create_shader_module(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, shader_desc, shader_stage_info.emplace_back(), spec_info.emplace_back(), spec_map.emplace_back())) + if (!append_stage(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, shader_desc)) goto exit_failure; - shaders.push_back(shader_stage_info.back().module); } for (const api::shader_desc &shader_desc : miss_desc) { - if (!create_shader_module(VK_SHADER_STAGE_MISS_BIT_KHR, shader_desc, shader_stage_info.emplace_back(), spec_info.emplace_back(), spec_map.emplace_back())) + if (!append_stage(VK_SHADER_STAGE_MISS_BIT_KHR, shader_desc)) goto exit_failure; - shaders.push_back(shader_stage_info.back().module); } for (const api::shader_desc &shader_desc : intersection_desc) { - if (!create_shader_module(VK_SHADER_STAGE_INTERSECTION_BIT_KHR, shader_desc, shader_stage_info.emplace_back(), spec_info.emplace_back(), spec_map.emplace_back())) + if (!append_stage(VK_SHADER_STAGE_INTERSECTION_BIT_KHR, shader_desc)) goto exit_failure; - shaders.push_back(shader_stage_info.back().module); } for (const api::shader_desc &shader_desc : callable_desc) { - if (!create_shader_module(VK_SHADER_STAGE_CALLABLE_BIT_KHR, shader_desc, shader_stage_info.emplace_back(), spec_info.emplace_back(), spec_map.emplace_back())) + if (!append_stage(VK_SHADER_STAGE_CALLABLE_BIT_KHR, shader_desc)) goto exit_failure; - shaders.push_back(shader_stage_info.back().module); } create_info.stageCount = static_cast(shader_stage_info.size()); @@ -1617,9 +1707,6 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, if (VkPipeline object = VK_NULL_HANDLE; vk.CreateRayTracingPipelinesKHR(_orig, VK_NULL_HANDLE, VK_NULL_HANDLE, 1, &create_info, nullptr, &object) == VK_SUCCESS) { - for (const VkShaderModule shader : shaders) - vk.DestroyShaderModule(_orig, shader, nullptr); - *out_pipeline = { (uint64_t)object }; return true; } @@ -1627,83 +1714,318 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, } else if (cs_desc.code_size != 0) { - VkComputePipelineCreateInfo create_info { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; - create_info.flags = convert_pipeline_flags(flags); + return create_pipeline(layout, subobject_count, subobjects, out_pipeline, static_cast(nullptr)); + } + else + { + return create_pipeline(layout, subobject_count, subobjects, out_pipeline, static_cast(nullptr)); + } + +exit_failure: + *out_pipeline = { 0 }; + return false; +} +bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline *out_pipeline, const VkComputePipelineCreateInfo *orig_create_info) +{ + api::shader_desc cs_desc = {}; + api::pipeline_flags flags = api::pipeline_flags::none; + std::vector pnext_restore_links; + + for (uint32_t i = 0; i < subobject_count; ++i) + { + if (subobjects[i].count == 0) + continue; + + switch (subobjects[i].type) + { + case api::pipeline_subobject_type::compute_shader: + assert(subobjects[i].count == 1); + cs_desc = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::flags: + assert(subobjects[i].count == 1); + flags = *static_cast(subobjects[i].data); + break; + default: + assert(false); + goto exit_failure; + } + } + + { + VkComputePipelineCreateInfo create_info = orig_create_info != nullptr ? *orig_create_info : VkComputePipelineCreateInfo { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; create_info.layout = (VkPipelineLayout)layout.handle; + if (find_in_structure_chain(create_info.pNext, VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO) == nullptr) + create_info.flags = convert_pipeline_flags(flags); + VkSpecializationInfo spec_info; std::vector spec_map; + VkShaderModuleCreateInfo shader_module_info; if (cs_desc.code_size != 0) { - if (!create_shader_module(VK_SHADER_STAGE_COMPUTE_BIT, cs_desc, create_info.stage, spec_info, spec_map)) + if (!setup_shader_stage_inline(VK_SHADER_STAGE_COMPUTE_BIT, cs_desc, orig_create_info != nullptr ? &orig_create_info->stage : nullptr, create_info.stage, shader_module_info, spec_info, spec_map, pnext_restore_links)) goto exit_failure; - shaders.push_back(create_info.stage.module); } if (VkPipeline object = VK_NULL_HANDLE; vk.CreateComputePipelines(_orig, VK_NULL_HANDLE, 1, &create_info, nullptr, &object) == VK_SUCCESS) { - vk.DestroyShaderModule(_orig, create_info.stage.module, nullptr); - + restore_pnext_links(pnext_restore_links); *out_pipeline = { (uint64_t)object }; return true; } + } - else + +exit_failure: + restore_pnext_links(pnext_restore_links); + *out_pipeline = { 0 }; + return false; +} +bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline *out_pipeline, const VkGraphicsPipelineCreateInfo *orig_create_info) +{ + VkRenderPass render_pass = VK_NULL_HANDLE; + + api::shader_desc vs_desc = {}; + api::shader_desc hs_desc = {}; + api::shader_desc ds_desc = {}; + api::shader_desc gs_desc = {}; + api::shader_desc ps_desc = {}; + api::shader_desc as_desc = {}; + api::shader_desc ms_desc = {}; + api::pipeline_subobject input_layout_desc = {}; + api::stream_output_desc stream_output_desc = {}; + api::blend_desc blend_desc = {}; + api::rasterizer_desc rasterizer_desc = {}; + api::depth_stencil_desc depth_stencil_desc = {}; + api::primitive_topology topology = api::primitive_topology::undefined; + api::format depth_stencil_format = api::format::unknown; + api::pipeline_subobject render_target_formats = {}; + api::pipeline_subobject dynamic_states_subobject = {}; + uint32_t sample_mask = UINT32_MAX; + uint32_t sample_count = 1; + uint32_t viewport_count = 1; + std::vector libraries; + api::pipeline_flags flags = api::pipeline_flags::none; + std::vector pnext_restore_links; + + for (uint32_t i = 0; i < subobject_count; ++i) { - VkGraphicsPipelineCreateInfo create_info { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; - create_info.flags = convert_pipeline_flags(flags); + if (subobjects[i].count == 0) + continue; + + switch (subobjects[i].type) + { + case api::pipeline_subobject_type::vertex_shader: + assert(subobjects[i].count == 1); + vs_desc = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::hull_shader: + assert(subobjects[i].count == 1); + hs_desc = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::domain_shader: + assert(subobjects[i].count == 1); + ds_desc = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::geometry_shader: + assert(subobjects[i].count == 1); + gs_desc = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::pixel_shader: + assert(subobjects[i].count == 1); + ps_desc = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::input_layout: + input_layout_desc = subobjects[i]; + break; + case api::pipeline_subobject_type::stream_output_state: + assert(subobjects[i].count == 1); + stream_output_desc = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::blend_state: + assert(subobjects[i].count == 1); + blend_desc = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::rasterizer_state: + assert(subobjects[i].count == 1); + rasterizer_desc = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::depth_stencil_state: + assert(subobjects[i].count == 1); + depth_stencil_desc = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::primitive_topology: + assert(subobjects[i].count == 1); + topology = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::depth_stencil_format: + assert(subobjects[i].count == 1); + depth_stencil_format = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::render_target_formats: + assert(subobjects[i].count <= 8); + render_target_formats = subobjects[i]; + break; + case api::pipeline_subobject_type::sample_mask: + assert(subobjects[i].count == 1); + sample_mask = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::sample_count: + assert(subobjects[i].count == 1); + sample_count = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::viewport_count: + assert(subobjects[i].count == 1); + viewport_count = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::dynamic_pipeline_states: + dynamic_states_subobject = subobjects[i]; + break; + case api::pipeline_subobject_type::amplification_shader: + assert(subobjects[i].count == 1); + as_desc = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::mesh_shader: + assert(subobjects[i].count == 1); + ms_desc = *static_cast(subobjects[i].data); + break; + case api::pipeline_subobject_type::libraries: + for (uint32_t k = 0; k < subobjects[i].count; ++k) + libraries.push_back(static_cast(subobjects[i].data)[k]); + break; + case api::pipeline_subobject_type::flags: + assert(subobjects[i].count == 1); + flags = *static_cast(subobjects[i].data); + break; + default: + assert(false); + goto exit_failure; + } + } + + { + VkGraphicsPipelineCreateInfo create_info = orig_create_info != nullptr ? *orig_create_info : VkGraphicsPipelineCreateInfo { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; create_info.layout = (VkPipelineLayout)layout.handle; + if (find_in_structure_chain(create_info.pNext, VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO) == nullptr) + create_info.flags = convert_pipeline_flags(flags); + VkPipelineShaderStageCreateInfo shader_stage_info[6]; + create_info.stageCount = 0; create_info.pStages = shader_stage_info; VkSpecializationInfo spec_info[6]; std::vector spec_map[6]; - if (vs_desc.code_size != 0) - { - if (!create_shader_module(VK_SHADER_STAGE_VERTEX_BIT, vs_desc, shader_stage_info[create_info.stageCount], spec_info[create_info.stageCount], spec_map[create_info.stageCount])) - goto exit_failure; - shaders.push_back(shader_stage_info[create_info.stageCount++].module); - } - if (hs_desc.code_size != 0) - { - if (!create_shader_module(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, hs_desc, shader_stage_info[create_info.stageCount], spec_info[create_info.stageCount], spec_map[create_info.stageCount])) - goto exit_failure; - shaders.push_back(shader_stage_info[create_info.stageCount++].module); - } - if (ds_desc.code_size != 0) - { - if (!create_shader_module(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, ds_desc, shader_stage_info[create_info.stageCount], spec_info[create_info.stageCount], spec_map[create_info.stageCount])) - goto exit_failure; - shaders.push_back(shader_stage_info[create_info.stageCount++].module); - } - if (gs_desc.code_size != 0) - { - if (!create_shader_module(VK_SHADER_STAGE_GEOMETRY_BIT, gs_desc, shader_stage_info[create_info.stageCount], spec_info[create_info.stageCount], spec_map[create_info.stageCount])) - goto exit_failure; - shaders.push_back(shader_stage_info[create_info.stageCount++].module); - } - if (ps_desc.code_size != 0) - { - if (!create_shader_module(VK_SHADER_STAGE_FRAGMENT_BIT, ps_desc, shader_stage_info[create_info.stageCount], spec_info[create_info.stageCount], spec_map[create_info.stageCount])) - goto exit_failure; - shaders.push_back(shader_stage_info[create_info.stageCount++].module); - } + VkShaderModuleCreateInfo shader_module_info[6]; + + const auto append_stage = [&](VkShaderStageFlagBits stage, const api::shader_desc &shader_desc, const VkPipelineShaderStageCreateInfo *original_stage_info) { + if (shader_desc.code_size != 0) + { + if (!setup_shader_stage_inline(stage, shader_desc, original_stage_info, shader_stage_info[create_info.stageCount], shader_module_info[create_info.stageCount], spec_info[create_info.stageCount], spec_map[create_info.stageCount], pnext_restore_links)) + return false; + ++create_info.stageCount; + return true; + } + + if (original_stage_info != nullptr) + shader_stage_info[create_info.stageCount++] = *original_stage_info; + + return true; + }; + + const bool stage_setup_success = [&]() { + if (orig_create_info != nullptr) + { + const auto get_shader_desc = [&](VkShaderStageFlagBits stage) -> const api::shader_desc *{ + switch (stage) + { + case VK_SHADER_STAGE_VERTEX_BIT: + return &vs_desc; + case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: + return &hs_desc; + case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: + return &ds_desc; + case VK_SHADER_STAGE_GEOMETRY_BIT: + return &gs_desc; + case VK_SHADER_STAGE_FRAGMENT_BIT: + return &ps_desc; #if VK_EXT_mesh_shader - if (as_desc.code_size != 0) - { - if (!create_shader_module(VK_SHADER_STAGE_TASK_BIT_EXT, as_desc, shader_stage_info[create_info.stageCount], spec_info[create_info.stageCount], spec_map[create_info.stageCount])) - goto exit_failure; - shaders.push_back(shader_stage_info[create_info.stageCount++].module); - } - if (ms_desc.code_size != 0) + case VK_SHADER_STAGE_TASK_BIT_EXT: + return &as_desc; + case VK_SHADER_STAGE_MESH_BIT_EXT: + return &ms_desc; +#endif + default: + return nullptr; + } + }; + + VkShaderStageFlags present_stages = 0; + + for (uint32_t i = 0; i < orig_create_info->stageCount; ++i) + { + const VkPipelineShaderStageCreateInfo &original_stage_info = orig_create_info->pStages[i]; + const api::shader_desc *shader_desc = get_shader_desc(original_stage_info.stage); + + if (shader_desc == nullptr) + { + shader_stage_info[create_info.stageCount++] = original_stage_info; + continue; + } + + present_stages |= original_stage_info.stage; + + if (!append_stage(original_stage_info.stage, *shader_desc, &original_stage_info)) + return false; + } + + // Add new added stages from addon + if ((present_stages & VK_SHADER_STAGE_VERTEX_BIT) == 0 && vs_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_VERTEX_BIT, vs_desc, nullptr)) + return false; + if ((present_stages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) == 0 && hs_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, hs_desc, nullptr)) + return false; + if ((present_stages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) == 0 && ds_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, ds_desc, nullptr)) + return false; + if ((present_stages & VK_SHADER_STAGE_GEOMETRY_BIT) == 0 && gs_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_GEOMETRY_BIT, gs_desc, nullptr)) + return false; + if ((present_stages & VK_SHADER_STAGE_FRAGMENT_BIT) == 0 && ps_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_FRAGMENT_BIT, ps_desc, nullptr)) + return false; +#if VK_EXT_mesh_shader + if ((present_stages & VK_SHADER_STAGE_TASK_BIT_EXT) == 0 && as_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_TASK_BIT_EXT, as_desc, nullptr)) + return false; + if ((present_stages & VK_SHADER_STAGE_MESH_BIT_EXT) == 0 && ms_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_MESH_BIT_EXT, ms_desc, nullptr)) + return false; +#endif + } + else + { + if (!append_stage(VK_SHADER_STAGE_VERTEX_BIT, vs_desc, nullptr)) + return false; + if (!append_stage(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, hs_desc, nullptr)) + return false; + if (!append_stage(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, ds_desc, nullptr)) + return false; + if (!append_stage(VK_SHADER_STAGE_GEOMETRY_BIT, gs_desc, nullptr)) + return false; + if (!append_stage(VK_SHADER_STAGE_FRAGMENT_BIT, ps_desc, nullptr)) + return false; +#if VK_EXT_mesh_shader + if (!append_stage(VK_SHADER_STAGE_TASK_BIT_EXT, as_desc, nullptr)) + return false; + if (!append_stage(VK_SHADER_STAGE_MESH_BIT_EXT, ms_desc, nullptr)) + return false; +#endif + } + + return true; + }(); + + if (!stage_setup_success) { - if (!create_shader_module(VK_SHADER_STAGE_MESH_BIT_EXT, ms_desc, shader_stage_info[create_info.stageCount], spec_info[create_info.stageCount], spec_map[create_info.stageCount])) - goto exit_failure; - shaders.push_back(shader_stage_info[create_info.stageCount++].module); + goto exit_failure; } -#endif std::vector dynamic_states; // Always make scissor rectangles and viewports dynamic @@ -1711,7 +2033,7 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); convert_dynamic_states(dynamic_states_subobject.count, static_cast(dynamic_states_subobject.data), dynamic_states); - VkPipelineDynamicStateCreateInfo dynamic_state_info { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; + VkPipelineDynamicStateCreateInfo dynamic_state_info = create_info.pDynamicState != nullptr ? *create_info.pDynamicState : VkPipelineDynamicStateCreateInfo { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; create_info.pDynamicState = &dynamic_state_info; dynamic_state_info.dynamicStateCount = static_cast(dynamic_states.size()); dynamic_state_info.pDynamicStates = dynamic_states.data(); @@ -1719,50 +2041,53 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, std::vector vertex_bindings; std::vector vertex_attributes; std::vector vertex_binding_divisors; + convert_input_layout_desc(input_layout_desc.count, static_cast(input_layout_desc.data), vertex_bindings, vertex_attributes, vertex_binding_divisors); - VkPipelineVertexInputDivisorStateCreateInfo vertex_input_divisor_state_info { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO }; - vertex_input_divisor_state_info.vertexBindingDivisorCount = static_cast(vertex_binding_divisors.size()); - vertex_input_divisor_state_info.pVertexBindingDivisors = vertex_binding_divisors.data(); + VkPipelineVertexInputDivisorStateCreateInfo vertex_input_divisor_state_info; - VkPipelineVertexInputStateCreateInfo vertex_input_state_info { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; + VkPipelineVertexInputStateCreateInfo vertex_input_state_info = create_info.pVertexInputState != nullptr ? *create_info.pVertexInputState : VkPipelineVertexInputStateCreateInfo { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; create_info.pVertexInputState = &vertex_input_state_info; if (!vertex_binding_divisors.empty()) - vertex_input_state_info.pNext = &vertex_input_divisor_state_info; + { + replace_or_append_structure_chain_node(vertex_input_state_info.pNext, VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO, vertex_input_divisor_state_info, pnext_restore_links); + vertex_input_divisor_state_info.vertexBindingDivisorCount = static_cast(vertex_binding_divisors.size()); + vertex_input_divisor_state_info.pVertexBindingDivisors = vertex_binding_divisors.data(); + } vertex_input_state_info.vertexBindingDescriptionCount = static_cast(vertex_bindings.size()); vertex_input_state_info.pVertexBindingDescriptions = vertex_bindings.data(); vertex_input_state_info.vertexAttributeDescriptionCount = static_cast(vertex_attributes.size()); vertex_input_state_info.pVertexAttributeDescriptions = vertex_attributes.data(); - VkPipelineInputAssemblyStateCreateInfo input_assembly_state_info { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; + VkPipelineInputAssemblyStateCreateInfo input_assembly_state_info = create_info.pInputAssemblyState != nullptr ? *create_info.pInputAssemblyState : VkPipelineInputAssemblyStateCreateInfo { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; create_info.pInputAssemblyState = &input_assembly_state_info; input_assembly_state_info.primitiveRestartEnable = VK_FALSE; input_assembly_state_info.topology = convert_primitive_topology(topology); - VkPipelineTessellationStateCreateInfo tessellation_state_info { VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO }; + VkPipelineTessellationStateCreateInfo tessellation_state_info = create_info.pTessellationState != nullptr ? *create_info.pTessellationState : VkPipelineTessellationStateCreateInfo { VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO }; create_info.pTessellationState = &tessellation_state_info; if (input_assembly_state_info.topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) tessellation_state_info.patchControlPoints = static_cast(topology) - static_cast(api::primitive_topology::patch_list_01_cp) + 1; - VkPipelineViewportStateCreateInfo viewport_state_info { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; + VkPipelineViewportStateCreateInfo viewport_state_info = create_info.pViewportState != nullptr ? *create_info.pViewportState : VkPipelineViewportStateCreateInfo { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; create_info.pViewportState = &viewport_state_info; viewport_state_info.scissorCount = viewport_count; viewport_state_info.viewportCount = viewport_count; - VkPipelineRasterizationStateCreateInfo rasterization_state_info { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; - create_info.pRasterizationState = &rasterization_state_info; + VkPipelineRasterizationStateCreateInfo rasterization_state_info; + { + rasterization_state_info = create_info.pRasterizationState != nullptr ? *create_info.pRasterizationState : VkPipelineRasterizationStateCreateInfo { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; + create_info.pRasterizationState = &rasterization_state_info; + } #if VK_EXT_conservative_rasterization VkPipelineRasterizationConservativeStateCreateInfoEXT conservative_rasterization_info; - if (rasterizer_desc.conservative_rasterization != 0) + if (rasterizer_desc.conservative_rasterization != 0 || find_in_structure_chain(rasterization_state_info.pNext, VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT) != nullptr) { - if (!vk.EXT_conservative_rasterization) + if (rasterizer_desc.conservative_rasterization != 0 && !vk.EXT_conservative_rasterization) goto exit_failure; - conservative_rasterization_info = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT }; - conservative_rasterization_info.pNext = rasterization_state_info.pNext; - - rasterization_state_info.pNext = &conservative_rasterization_info; + replace_or_append_structure_chain_node(rasterization_state_info.pNext, VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT, conservative_rasterization_info, pnext_restore_links); } #endif @@ -1774,16 +2099,13 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, VkPipelineRasterizationStateStreamCreateInfoEXT stream_rasterization_info; if (stream_output_desc.rasterized_stream != 0) { - stream_rasterization_info = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT }; - stream_rasterization_info.pNext = rasterization_state_info.pNext; - - rasterization_state_info.pNext = &stream_rasterization_info; + replace_or_append_structure_chain_node(rasterization_state_info.pNext, VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT, stream_rasterization_info, pnext_restore_links); convert_stream_output_desc(stream_output_desc, rasterization_state_info); } #endif - VkPipelineMultisampleStateCreateInfo multisample_state_info { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; + VkPipelineMultisampleStateCreateInfo multisample_state_info = create_info.pMultisampleState != nullptr ? *create_info.pMultisampleState : VkPipelineMultisampleStateCreateInfo { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; create_info.pMultisampleState = &multisample_state_info; multisample_state_info.rasterizationSamples = sample_count != 0 ? static_cast(sample_count) : VK_SAMPLE_COUNT_1_BIT; multisample_state_info.sampleShadingEnable = VK_FALSE; @@ -1791,14 +2113,14 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, multisample_state_info.alphaToOneEnable = VK_FALSE; multisample_state_info.pSampleMask = &sample_mask; - VkPipelineDepthStencilStateCreateInfo depth_stencil_state_info { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; + VkPipelineDepthStencilStateCreateInfo depth_stencil_state_info = create_info.pDepthStencilState != nullptr ? *create_info.pDepthStencilState : VkPipelineDepthStencilStateCreateInfo { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; create_info.pDepthStencilState = &depth_stencil_state_info; convert_depth_stencil_desc(depth_stencil_desc, depth_stencil_state_info); depth_stencil_state_info.depthBoundsTestEnable = VK_FALSE; depth_stencil_state_info.minDepthBounds = 0.0f; depth_stencil_state_info.maxDepthBounds = 1.0f; - VkPipelineColorBlendStateCreateInfo color_blend_state_info { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; + VkPipelineColorBlendStateCreateInfo color_blend_state_info = create_info.pColorBlendState != nullptr ? *create_info.pColorBlendState : VkPipelineColorBlendStateCreateInfo { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; create_info.pColorBlendState = &color_blend_state_info; color_blend_state_info.attachmentCount = 8; temp_mem attachment_info(color_blend_state_info.attachmentCount); @@ -1808,30 +2130,29 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, temp_mem attachment_formats(render_target_formats.count); color_blend_state_info.attachmentCount = 0; for (uint32_t i = 0; i < render_target_formats.count; ++i, ++color_blend_state_info.attachmentCount) - { attachment_formats[i] = convert_format(static_cast(render_target_formats.data)[i]); - assert(attachment_formats[i] != VK_FORMAT_UNDEFINED); - } #if VK_KHR_dynamic_rendering VkPipelineRenderingCreateInfo dynamic_rendering_info; - if (vk.KHR_dynamic_rendering) + if (create_info.renderPass == VK_NULL_HANDLE && vk.KHR_dynamic_rendering) { - dynamic_rendering_info = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO }; - dynamic_rendering_info.pNext = create_info.pNext; + replace_or_append_structure_chain_node(create_info.pNext, VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, dynamic_rendering_info, pnext_restore_links); + dynamic_rendering_info.colorAttachmentCount = color_blend_state_info.attachmentCount; dynamic_rendering_info.pColorAttachmentFormats = attachment_formats.p; const VkFormat depth_stencil_format_vk = convert_format(depth_stencil_format); + dynamic_rendering_info.depthAttachmentFormat = VK_FORMAT_UNDEFINED; + dynamic_rendering_info.stencilAttachmentFormat = VK_FORMAT_UNDEFINED; if (aspect_flags_from_format(depth_stencil_format_vk) & VK_IMAGE_ASPECT_DEPTH_BIT) dynamic_rendering_info.depthAttachmentFormat = depth_stencil_format_vk; if (aspect_flags_from_format(depth_stencil_format_vk) & VK_IMAGE_ASPECT_STENCIL_BIT) dynamic_rendering_info.stencilAttachmentFormat = depth_stencil_format_vk; - create_info.pNext = &dynamic_rendering_info; } else #endif + if (create_info.renderPass == VK_NULL_HANDLE) { const uint32_t max_attachments = color_blend_state_info.attachmentCount + 1; @@ -1900,42 +2221,39 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, goto exit_failure; create_info.renderPass = render_pass; + create_info.subpass = 0; } #if VK_KHR_pipeline_library VkPipelineLibraryCreateInfoKHR library_info; if (!libraries.empty()) { - library_info = { VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR }; - library_info.pNext = create_info.pNext; + replace_or_append_structure_chain_node(create_info.pNext, VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR, library_info, pnext_restore_links); + library_info.libraryCount = static_cast(libraries.size()); library_info.pLibraries = reinterpret_cast(libraries.data()); - - create_info.pNext = &library_info; } #endif if (VkPipeline object = VK_NULL_HANDLE; vk.CreateGraphicsPipelines(_orig, VK_NULL_HANDLE, 1, &create_info, nullptr, &object) == VK_SUCCESS) { + restore_pnext_links(pnext_restore_links); + if (render_pass != VK_NULL_HANDLE) vk.DestroyRenderPass(_orig, render_pass, nullptr); - for (const VkShaderModule shader : shaders) - vk.DestroyShaderModule(_orig, shader, nullptr); - *out_pipeline = { (uint64_t)object }; return true; } + } exit_failure: + restore_pnext_links(pnext_restore_links); if (render_pass != VK_NULL_HANDLE) vk.DestroyRenderPass(_orig, render_pass, nullptr); - for (const VkShaderModule shader : shaders) - vk.DestroyShaderModule(_orig, shader, nullptr); - *out_pipeline = { 0 }; return false; } @@ -1944,156 +2262,255 @@ void reshade::vulkan::device_impl::destroy_pipeline(api::pipeline pipeline) vk.DestroyPipeline(_orig, (VkPipeline)pipeline.handle, nullptr); } -bool reshade::vulkan::device_impl::create_pipeline_layout(uint32_t param_count, const api::pipeline_layout_param *params, api::pipeline_layout *out_layout) +bool reshade::vulkan::device_impl::create_descriptor_set_layout(const api::pipeline_layout_param ¶m, VkDescriptorSetLayout *out_set_layout, std::vector &embedded_samplers) { - std::vector push_constant_ranges; - std::vector set_layouts; - std::vector embedded_samplers; - set_layouts.reserve(param_count); - push_constant_ranges.reserve(param_count); + assert(out_set_layout != nullptr); + assert(param.type != api::pipeline_layout_param_type::push_constants); + *out_set_layout = VK_NULL_HANDLE; + + bool push_descriptors = (param.type == api::pipeline_layout_param_type::push_descriptors); + bool update_after_bind_pool = false; + const bool with_flags = (param.type == api::pipeline_layout_param_type::descriptor_table_with_flags || param.type == api::pipeline_layout_param_type::push_descriptors_with_ranges_and_flags); + const uint32_t range_count = push_descriptors ? 1 : with_flags ? param.descriptor_table_with_flags.count : param.descriptor_table.count; + const api::descriptor_range *range = push_descriptors ? ¶m.push_descriptors : with_flags ? param.descriptor_table_with_flags.ranges : param.descriptor_table.ranges; + push_descriptors |= (param.type == api::pipeline_layout_param_type::push_descriptors_with_ranges || param.type == api::pipeline_layout_param_type::push_descriptors_with_ranges_and_flags); + + object_data data; + data.num_descriptors = 0; + data.push_descriptors = push_descriptors; + data.ranges.reserve(range_count); + if (with_flags) + { + data.ranges_with_flags.reserve(range_count); + data.static_samplers.resize(range_count); + } + data.binding_to_offset.reserve(range_count); + + std::vector internal_bindings; + std::vector internal_binding_flags; + std::vector> internal_samplers; + internal_bindings.reserve(range_count); + internal_binding_flags.reserve(range_count); + internal_samplers.reserve(range_count); - for (uint32_t i = 0; i < param_count; ++i) + for (uint32_t k = 0, offset = 0; k < range_count; ++k, range = (with_flags ? static_cast(range) + 1 : range + 1)) { - if (params[i].type != api::pipeline_layout_param_type::push_constants) + if (with_flags) { - bool push_descriptors = (params[i].type == api::pipeline_layout_param_type::push_descriptors); - bool update_after_bind_pool = false; - const bool with_flags = (params[i].type == api::pipeline_layout_param_type::descriptor_table_with_flags || params[i].type == api::pipeline_layout_param_type::push_descriptors_with_ranges_and_flags); - const uint32_t range_count = push_descriptors ? 1 : with_flags ? params[i].descriptor_table_with_flags.count : params[i].descriptor_table.count; - const api::descriptor_range *range = push_descriptors ? ¶ms[i].push_descriptors : with_flags ? params[i].descriptor_table_with_flags.ranges : params[i].descriptor_table.ranges; - push_descriptors |= (params[i].type == api::pipeline_layout_param_type::push_descriptors_with_ranges || params[i].type == api::pipeline_layout_param_type::push_descriptors_with_ranges_and_flags); + data.ranges_with_flags.push_back(*static_cast(range)); + data.ranges_with_flags.back().static_samplers = nullptr; + } + else + data.ranges.push_back(*range); - object_data data; - data.ranges.reserve(range_count); - data.binding_to_offset.reserve(range_count); + if (range->count == 0) + continue; - std::vector internal_bindings; - std::vector internal_binding_flags; - std::vector> internal_samplers; - internal_bindings.reserve(range_count); - internal_binding_flags.reserve(range_count); - internal_samplers.reserve(range_count); + const uint32_t max_binding = (range->count == UINT32_MAX) ? range->binding : range->binding + (range->count - range->array_size); + if (max_binding >= data.binding_to_offset.size()) + data.binding_to_offset.resize(max_binding + 1); + data.binding_to_offset[range->binding] = offset; - for (uint32_t k = 0, offset = 0; k < range_count; ++k, range = (with_flags ? static_cast(range) + 1 : range + 1)) - { - data.ranges.push_back(*static_cast(range)); + VkDescriptorSetLayoutBinding &internal_binding = internal_bindings.emplace_back(); + internal_binding.binding = range->binding; + internal_binding.descriptorType = convert_descriptor_type(range->type); + internal_binding.descriptorCount = range->array_size; + internal_binding.stageFlags = static_cast(range->visibility); - if (range->count == 0) - continue; + offset += internal_binding.descriptorCount; - const uint32_t max_binding = (range->count == UINT32_MAX) ? range->binding : range->binding + (range->count - range->array_size); - if (max_binding >= data.binding_to_offset.size()) - data.binding_to_offset.resize(max_binding + 1); - data.binding_to_offset[range->binding] = offset; + if (with_flags && (range->type == api::descriptor_type::sampler || range->type == api::descriptor_type::sampler_with_resource_view) && static_cast(range)->static_samplers != nullptr) + { + if (range->array_size != 1 || range->count == UINT32_MAX) + return false; - VkDescriptorSetLayoutBinding &internal_binding = internal_bindings.emplace_back(); - internal_binding.binding = range->binding; - internal_binding.descriptorType = convert_descriptor_type(range->type); - internal_binding.descriptorCount = range->array_size; - internal_binding.stageFlags = static_cast(range->visibility); + data.static_samplers[k].assign(static_cast(range)->static_samplers, static_cast(range)->static_samplers + range->count); + data.ranges_with_flags.back().static_samplers = data.static_samplers[k].data(); - offset += internal_binding.descriptorCount; + std::vector &internal_binding_samplers = internal_samplers.emplace_back(); + internal_binding_samplers.resize(range->count); - if (with_flags && (range->type == api::descriptor_type::sampler || range->type == api::descriptor_type::sampler_with_resource_view) && static_cast(range)->static_samplers != nullptr) - { - if (range->array_size != 1 || range->count == UINT32_MAX) - goto exit_failure; - - std::vector &internal_binding_samplers = internal_samplers.emplace_back(); - internal_binding_samplers.resize(range->count); + for (uint32_t j = 0; j < range->count; ++j) + { + VkSamplerCreateInfo create_info { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + // Cannot have custom border color in immutable samplers + convert_sampler_desc(static_cast(range)->static_samplers[j], create_info); - for (uint32_t j = 0; j < range->count; ++j) - { - VkSamplerCreateInfo create_info { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; - // Cannot have custom border color in immutable samplers - convert_sampler_desc(static_cast(range)->static_samplers[j], create_info); + if (vk.CreateSampler(_orig, &create_info, nullptr, &embedded_samplers.emplace_back()) != VK_SUCCESS) + return false; - if (vk.CreateSampler(_orig, &create_info, nullptr, &embedded_samplers.emplace_back()) != VK_SUCCESS) - goto exit_failure; + internal_binding_samplers[j] = embedded_samplers.back(); + } - internal_binding_samplers[j] = embedded_samplers.back(); - } + internal_binding.pImmutableSamplers = internal_binding_samplers.data(); + } - internal_binding.pImmutableSamplers = internal_binding_samplers.data(); - } + data.num_descriptors += internal_binding.descriptorCount; - VkDescriptorBindingFlags binding_flags = 0; + VkDescriptorBindingFlags binding_flags = 0; #if VK_EXT_descriptor_indexing - if (with_flags && vk.EXT_descriptor_indexing) - { - binding_flags = convert_descriptor_range_flags(static_cast(range)->flags); + if (with_flags && vk.EXT_descriptor_indexing) + { + binding_flags = convert_descriptor_range_flags(static_cast(range)->flags); - if ((binding_flags & VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT) != 0) - update_after_bind_pool = true; - } + if ((binding_flags & VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT) != 0) + update_after_bind_pool = true; + } #endif - if (range->count == UINT32_MAX) - { - internal_binding_flags.push_back(binding_flags | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT); - break; // Unbounded range must be the last binding - } - else - { - internal_binding_flags.push_back(binding_flags); - } + if (range->count == UINT32_MAX) + { + internal_binding_flags.push_back(binding_flags | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT); + break; // Unbounded range must be the last binding + } + else + { + internal_binding_flags.push_back(binding_flags); + } - // Add additional bindings if the total descriptor count exceeds the array size of the binding - for (uint32_t j = 0; j < (range->count - range->array_size); ++j) - { - data.binding_to_offset[range->binding + 1 + j] = offset; + // Add additional bindings if the total descriptor count exceeds the array size of the binding + for (uint32_t j = 0; j < (range->count - range->array_size); ++j) + { + data.binding_to_offset[range->binding + 1 + j] = offset; - VkDescriptorSetLayoutBinding &additional_binding = internal_bindings.emplace_back(); - additional_binding.binding = range->binding + 1 + j; - additional_binding.descriptorType = convert_descriptor_type(range->type); - additional_binding.descriptorCount = 1; - additional_binding.stageFlags = static_cast(range->visibility); + VkDescriptorSetLayoutBinding &additional_binding = internal_bindings.emplace_back(); + additional_binding.binding = range->binding + 1 + j; + additional_binding.descriptorType = convert_descriptor_type(range->type); + additional_binding.descriptorCount = 1; + additional_binding.stageFlags = static_cast(range->visibility); - offset += additional_binding.descriptorCount; + offset += additional_binding.descriptorCount; + ++data.num_descriptors; - internal_binding_flags.push_back(binding_flags); - } - } + internal_binding_flags.push_back(binding_flags); + } + } - VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags_info { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO }; - binding_flags_info.bindingCount = static_cast(internal_binding_flags.size()); - binding_flags_info.pBindingFlags = internal_binding_flags.data(); + VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags_info { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO }; + binding_flags_info.bindingCount = static_cast(internal_binding_flags.size()); + binding_flags_info.pBindingFlags = internal_binding_flags.data(); - VkDescriptorSetLayoutCreateInfo create_info { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, &binding_flags_info }; - create_info.bindingCount = static_cast(internal_bindings.size()); - create_info.pBindings = internal_bindings.data(); + VkDescriptorSetLayoutCreateInfo create_info { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, &binding_flags_info }; + create_info.bindingCount = static_cast(internal_bindings.size()); + create_info.pBindings = internal_bindings.data(); #if VK_KHR_push_descriptor - if (push_descriptors && vk.KHR_push_descriptor) - create_info.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT; + if (push_descriptors && vk.KHR_push_descriptor) + create_info.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT; #endif #if VK_EXT_descriptor_indexing - if (update_after_bind_pool && vk.EXT_descriptor_indexing) - create_info.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT; + if (update_after_bind_pool && vk.EXT_descriptor_indexing) + create_info.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT; #endif - if (vk.CreateDescriptorSetLayout(_orig, &create_info, nullptr, &set_layouts.emplace_back()) == VK_SUCCESS) - { - register_object(set_layouts.back(), std::move(data)); - } - else - { - goto exit_failure; - } - } - else + if (vk.CreateDescriptorSetLayout(_orig, &create_info, nullptr, out_set_layout) == VK_SUCCESS) + { + register_object(*out_set_layout, std::move(data)); + return true; + } + + return false; +} + +bool reshade::vulkan::device_impl::create_pipeline_layout(uint32_t param_count, const api::pipeline_layout_param *params, api::pipeline_layout *out_layout) +{ + return create_pipeline_layout(param_count, params, out_layout, nullptr); +} +bool reshade::vulkan::device_impl::create_pipeline_layout(uint32_t param_count, const api::pipeline_layout_param *params, api::pipeline_layout *out_layout, const VkPipelineLayoutCreateInfo *orig_create_info) +{ + std::vector push_constant_ranges; + std::vector set_layouts; + std::vector owned_set_layouts; + std::vector embedded_samplers; + push_constant_ranges.reserve(param_count); + set_layouts.reserve(param_count); + owned_set_layouts.reserve(param_count); + + for (uint32_t i = 0, set_index = 0; i < param_count; ++i) + { + if (params[i].type == api::pipeline_layout_param_type::push_constants) { VkPushConstantRange &push_constant_range = push_constant_ranges.emplace_back(); push_constant_range.stageFlags = static_cast(params[i].push_constants.visibility); push_constant_range.offset = params[i].push_constants.binding * 4; push_constant_range.size = params[i].push_constants.count * 4; + continue; + } + + bool reuse_original_set_layout = false; + VkDescriptorSetLayout set_layout = VK_NULL_HANDLE; + const VkDescriptorSetLayout original_set_layout = orig_create_info != nullptr && set_index < orig_create_info->setLayoutCount ? orig_create_info->pSetLayouts[set_index] : VK_NULL_HANDLE; + + if (original_set_layout != VK_NULL_HANDLE) + { + const auto original_set_layout_data = get_private_data_for_object(original_set_layout); + + switch (params[i].type) + { + case api::pipeline_layout_param_type::descriptor_table: + if (!original_set_layout_data->push_descriptors && + params[i].descriptor_table.count == original_set_layout_data->ranges.size() && + params[i].descriptor_table.ranges == original_set_layout_data->ranges.data()) + { + set_layout = original_set_layout; + reuse_original_set_layout = true; + } + break; + case api::pipeline_layout_param_type::descriptor_table_with_flags: + if (!original_set_layout_data->push_descriptors && + params[i].descriptor_table_with_flags.count == original_set_layout_data->ranges_with_flags.size() && + params[i].descriptor_table_with_flags.ranges == original_set_layout_data->ranges_with_flags.data()) + { + set_layout = original_set_layout; + reuse_original_set_layout = true; + } + break; + case api::pipeline_layout_param_type::push_descriptors_with_ranges: + if (original_set_layout_data->push_descriptors && + params[i].descriptor_table.count == original_set_layout_data->ranges.size() && + params[i].descriptor_table.ranges == original_set_layout_data->ranges.data()) + { + set_layout = original_set_layout; + reuse_original_set_layout = true; + } + break; + case api::pipeline_layout_param_type::push_descriptors_with_ranges_and_flags: + if (original_set_layout_data->push_descriptors && + params[i].descriptor_table_with_flags.count == original_set_layout_data->ranges_with_flags.size() && + params[i].descriptor_table_with_flags.ranges == original_set_layout_data->ranges_with_flags.data()) + { + set_layout = original_set_layout; + reuse_original_set_layout = true; + } + break; + default: + break; + } } + else if (orig_create_info != nullptr && + set_index < orig_create_info->setLayoutCount && + params[i].type == api::pipeline_layout_param_type::push_descriptors && + params[i].push_descriptors.count == 0) + { + set_layout = VK_NULL_HANDLE; + reuse_original_set_layout = true; + } + + if (!reuse_original_set_layout && !create_descriptor_set_layout(params[i], &set_layout, embedded_samplers)) + goto exit_failure; + + if (!reuse_original_set_layout) + owned_set_layouts.push_back(set_layout); + + set_layouts.push_back(set_layout); + ++set_index; } { - VkPipelineLayoutCreateInfo create_info { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + VkPipelineLayoutCreateInfo create_info { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, orig_create_info != nullptr ? orig_create_info->pNext : nullptr }; + create_info.flags = orig_create_info != nullptr ? orig_create_info->flags : 0; create_info.setLayoutCount = static_cast(set_layouts.size()); - create_info.pSetLayouts = set_layouts.data(); + create_info.pSetLayouts = set_layouts.data(); create_info.pushConstantRangeCount = static_cast(push_constant_ranges.size()); create_info.pPushConstantRanges = push_constant_ranges.data(); @@ -2101,6 +2518,7 @@ bool reshade::vulkan::device_impl::create_pipeline_layout(uint32_t param_count, vk.CreatePipelineLayout(_orig, &create_info, nullptr, &object) == VK_SUCCESS) { object_data data; + data.owned_set_layouts = std::move(owned_set_layouts); data.embedded_samplers = std::move(embedded_samplers); data.set_layouts = std::move(set_layouts); data.owns_set_layouts = true; @@ -2118,7 +2536,7 @@ bool reshade::vulkan::device_impl::create_pipeline_layout(uint32_t param_count, vk.DestroySampler(_orig, sampler, nullptr); } - for (const VkDescriptorSetLayout set_layout : set_layouts) + for (const VkDescriptorSetLayout set_layout : owned_set_layouts) { unregister_object(set_layout); @@ -2142,7 +2560,7 @@ void reshade::vulkan::device_impl::destroy_pipeline_layout(api::pipeline_layout if (layout_data->owns_set_layouts) { - for (const VkDescriptorSetLayout set_layout : layout_data->set_layouts) + for (const VkDescriptorSetLayout set_layout : layout_data->owned_set_layouts) { unregister_object(set_layout); diff --git a/source/vulkan/vulkan_impl_device.hpp b/source/vulkan/vulkan_impl_device.hpp index d3d273bfc5..8395bd3aff 100644 --- a/source/vulkan/vulkan_impl_device.hpp +++ b/source/vulkan/vulkan_impl_device.hpp @@ -73,9 +73,12 @@ namespace reshade::vulkan void update_texture_region(const api::subresource_data &data, api::resource dest, uint32_t dest_subresource, const api::subresource_box *dest_box) final; bool create_pipeline(api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline *out_pipeline) final; + bool create_pipeline(api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline *out_pipeline, const VkComputePipelineCreateInfo *orig_create_info); + bool create_pipeline(api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline *out_pipeline, const VkGraphicsPipelineCreateInfo *orig_create_info); void destroy_pipeline(api::pipeline pipeline) final; bool create_pipeline_layout(uint32_t param_count, const api::pipeline_layout_param *params, api::pipeline_layout *out_layout) final; + bool create_pipeline_layout(uint32_t param_count, const api::pipeline_layout_param *params, api::pipeline_layout *out_layout, const VkPipelineLayoutCreateInfo *orig_create_info); void destroy_pipeline_layout(api::pipeline_layout layout) final; bool allocate_descriptor_tables(uint32_t count, api::pipeline_layout layout, uint32_t layout_param, api::descriptor_table *out_tables) final; @@ -157,7 +160,7 @@ namespace reshade::vulkan const VkPhysicalDeviceFeatures _enabled_features; private: - bool create_shader_module(VkShaderStageFlagBits stage, const api::shader_desc &desc, VkPipelineShaderStageCreateInfo &stage_info, VkSpecializationInfo &spec_info, std::vector &spec_map); + bool create_descriptor_set_layout(const api::pipeline_layout_param ¶m, VkDescriptorSetLayout *out_set_layout, std::vector &embedded_samplers); VmaAllocator _alloc = nullptr; VkDescriptorPool _descriptor_pool = VK_NULL_HANDLE; diff --git a/source/vulkan/vulkan_impl_type_convert.hpp b/source/vulkan/vulkan_impl_type_convert.hpp index 272dfad90e..37ab24487e 100644 --- a/source/vulkan/vulkan_impl_type_convert.hpp +++ b/source/vulkan/vulkan_impl_type_convert.hpp @@ -121,6 +121,7 @@ namespace reshade::vulkan std::vector embedded_samplers; std::vector set_layouts; + std::vector owned_set_layouts; bool owns_set_layouts; }; From 06008caa5f30eeae9fb79dd20bd746b3d106c81f Mon Sep 17 00:00:00 2001 From: LordNyriox Date: Sat, 13 Jun 2026 05:58:37 -0700 Subject: [PATCH 09/26] Update GitHub Actions modules (#424) --- .github/workflows/build.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 31559bba65..56740e3785 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,24 +8,30 @@ on: branches: - main +permissions: {} + jobs: build: runs-on: windows-latest + permissions: + contents: read + steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 + persist-credentials: false submodules: true - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.x' - name: Set up MSBuild - uses: microsoft/setup-msbuild@v2 + uses: microsoft/setup-msbuild@v3 - name: Build ReShade (32-bit) run: | @@ -40,19 +46,19 @@ jobs: msbuild ReShade.sln /p:Configuration="Release Setup" - name: Upload ReShade (32-bit) - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ReShade (32-bit) path: './bin/Win32/Release/ReShade32.dll' - name: Upload ReShade (64-bit) - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ReShade (64-bit) path: './bin/x64/Release/ReShade64.dll' - name: Upload ReShade Setup - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ReShade Setup path: './bin/AnyCPU/Release/ReShade Setup.exe' From 6ba99ab1d6bdc75011e40b302efae451fac95e10 Mon Sep 17 00:00:00 2001 From: Ritsu <19637006+MohannedElfatih@users.noreply.github.com> Date: Tue, 16 Jun 2026 00:43:36 +0400 Subject: [PATCH 10/26] Remove obsolete assert in Vulkan pipeline layout creation (#426) --- source/vulkan/vulkan_hooks_device.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/vulkan/vulkan_hooks_device.cpp b/source/vulkan/vulkan_hooks_device.cpp index 5d6989b6e4..d732d369d9 100644 --- a/source/vulkan/vulkan_hooks_device.cpp +++ b/source/vulkan/vulkan_hooks_device.cpp @@ -2022,8 +2022,6 @@ VkResult VKAPI_CALL vkCreatePipelineLayout(VkDevice device, const VkPipelineLayo { static_assert(sizeof(*pPipelineLayout) == sizeof(reshade::api::pipeline_layout)); - assert(pCreateInfo->pNext == nullptr); // 'device_impl::create_pipeline_layout' does not support extension structures - result = device_impl->create_pipeline_layout(param_count, param_data, reinterpret_cast(pPipelineLayout), pCreateInfo) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; owns_set_layouts = true; } From 2117f62a3d191e78a2ac2d5e62cadea04fbdf5c2 Mon Sep 17 00:00:00 2001 From: crosire Date: Sat, 13 Jun 2026 20:59:09 +0200 Subject: [PATCH 11/26] Enable "VK_KHR_maintenance5" extension required for in-place shader module description during pipeline creation --- source/vulkan/vulkan_hooks_device.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/source/vulkan/vulkan_hooks_device.cpp b/source/vulkan/vulkan_hooks_device.cpp index d732d369d9..9edfb19b30 100644 --- a/source/vulkan/vulkan_hooks_device.cpp +++ b/source/vulkan/vulkan_hooks_device.cpp @@ -248,6 +248,8 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi if (instance.api_version < VK_API_VERSION_1_4) { + add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME, true); + #if VK_KHR_push_descriptor push_descriptor_ext = add_extension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, false); #endif @@ -537,6 +539,20 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi create_info.pNext = &acceleration_structure_features; } #endif + + VkPhysicalDeviceMaintenance5Features maintenance5_features; + if (const auto existing_maintenance5_features = find_in_structure_chain( + pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES)) + { + const_cast(existing_maintenance5_features)->maintenance5 = VK_TRUE; + } + else + { + maintenance5_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES, const_cast(create_info.pNext) }; + maintenance5_features.maintenance5 = true; + + create_info.pNext = &maintenance5_features; + } #pragma endregion // Continue calling down the chain From bc6fc73e5511977c4310924ec1b261ffdf68a002 Mon Sep 17 00:00:00 2001 From: crosire Date: Sun, 21 Jun 2026 12:48:21 +0200 Subject: [PATCH 12/26] Clean up Vulkan pipeline creation code --- source/vulkan/vulkan_hooks_device.cpp | 11 +- source/vulkan/vulkan_impl_device.cpp | 473 +++++++++++--------------- source/vulkan/vulkan_impl_device.hpp | 1 + 3 files changed, 199 insertions(+), 286 deletions(-) diff --git a/source/vulkan/vulkan_hooks_device.cpp b/source/vulkan/vulkan_hooks_device.cpp index 9edfb19b30..97966089b7 100644 --- a/source/vulkan/vulkan_hooks_device.cpp +++ b/source/vulkan/vulkan_hooks_device.cpp @@ -1395,7 +1395,7 @@ VkResult VKAPI_CALL vkCreateGraphicsPipelines(VkDevice device, VkPipelineCache p RESHADE_VULKAN_GET_DEVICE_DISPATCH_PTR(CreateGraphicsPipelines, device_impl); #if RESHADE_ADDON >= 2 - // Disable custom allocator when pipeline may be overriden, so corresponding 'vkDestroyPipeline' is not called with mismatching callbacks + // Disable custom allocator when pipeline may be overridden, so corresponding 'vkDestroyPipeline' is not called with mismatching callbacks if (reshade::has_addon_event()) pAllocator = nullptr; @@ -1664,7 +1664,7 @@ VkResult VKAPI_CALL vkCreateComputePipelines(VkDevice device, VkPipelineCache pi RESHADE_VULKAN_GET_DEVICE_DISPATCH_PTR(CreateComputePipelines, device_impl); #if RESHADE_ADDON >= 2 - // Disable custom allocator when pipeline may be overriden, so corresponding 'vkDestroyPipeline' is not called with mismatching callbacks + // Disable custom allocator when pipeline may be overridden, so corresponding 'vkDestroyPipeline' is not called with mismatching callbacks if (reshade::has_addon_event()) pAllocator = nullptr; @@ -1749,7 +1749,7 @@ VkResult VKAPI_CALL vkCreateRayTracingPipelinesKHR(VkDevice device, VkDeferredOp RESHADE_VULKAN_GET_DEVICE_DISPATCH_PTR(CreateRayTracingPipelinesKHR, device_impl); #if RESHADE_ADDON >= 2 - // Disable custom allocator when pipeline may be overriden, so corresponding 'vkDestroyPipeline' is not called with mismatching callbacks + // Disable custom allocator when pipeline may be overridden, so corresponding 'vkDestroyPipeline' is not called with mismatching callbacks if (reshade::has_addon_event()) pAllocator = nullptr; @@ -1903,10 +1903,9 @@ VkResult VKAPI_CALL vkCreateRayTracingPipelinesKHR(VkDevice device, VkDeferredOp static_assert(sizeof(*pPipelines) == sizeof(reshade::api::pipeline)); assert(deferredOperation == VK_NULL_HANDLE); - assert(create_info.pNext == nullptr); // 'device_impl::create_pipeline' does not support extension structures result = device_impl->create_pipeline( - reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(subobjects.size()), subobjects.data(), reinterpret_cast(&pPipelines[i])) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; + reshade::api::pipeline_layout { (uint64_t)create_info.layout }, static_cast(subobjects.size()), subobjects.data(), reinterpret_cast(&pPipelines[i]), &create_info) ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY; } else { @@ -1968,7 +1967,7 @@ VkResult VKAPI_CALL vkCreatePipelineLayout(VkDevice device, const VkPipelineLayo VkResult result = VK_SUCCESS; #if RESHADE_ADDON >= 2 - // Disable custom allocator when pipeline layout may be overriden, so corresponding 'vkDestroyPipelineLayout' is not called with mismatching callbacks + // Disable custom allocator when pipeline layout may be overridden, so corresponding 'vkDestroyPipelineLayout' is not called with mismatching callbacks if (reshade::has_addon_event()) pAllocator = nullptr; diff --git a/source/vulkan/vulkan_impl_device.cpp b/source/vulkan/vulkan_impl_device.cpp index 0e97d62989..7f20f0ccbb 100644 --- a/source/vulkan/vulkan_impl_device.cpp +++ b/source/vulkan/vulkan_impl_device.cpp @@ -1355,26 +1355,46 @@ static bool setup_shader_stage_inline(VkShaderStageFlagBits stage, const reshade bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline *out_pipeline) { - api::shader_desc vs_desc = {}; - api::shader_desc hs_desc = {}; - api::shader_desc ds_desc = {}; - api::shader_desc gs_desc = {}; - api::shader_desc ps_desc = {}; - api::shader_desc cs_desc = {}; - api::shader_desc as_desc = {}; - api::shader_desc ms_desc = {}; - api::pipeline_subobject input_layout_desc = {}; - api::stream_output_desc stream_output_desc = {}; - api::blend_desc blend_desc = {}; - api::rasterizer_desc rasterizer_desc = {}; - api::depth_stencil_desc depth_stencil_desc = {}; - api::primitive_topology topology = api::primitive_topology::undefined; - api::format depth_stencil_format = api::format::unknown; - api::pipeline_subobject render_target_formats = {}; - api::pipeline_subobject dynamic_states_subobject = {}; - uint32_t sample_mask = UINT32_MAX; - uint32_t sample_count = 1; - uint32_t viewport_count = 1; + bool ray_tracing_pipeline = false; + bool compute_pipeline = false; + + for (uint32_t i = 0; i < subobject_count; ++i) + { + if (subobjects[i].count == 0) + continue; + + switch (subobjects[i].type) + { + case api::pipeline_subobject_type::compute_shader: + compute_pipeline = true; + break; + case api::pipeline_subobject_type::raygen_shader: + case api::pipeline_subobject_type::any_hit_shader: + case api::pipeline_subobject_type::closest_hit_shader: + case api::pipeline_subobject_type::miss_shader: + case api::pipeline_subobject_type::intersection_shader: + case api::pipeline_subobject_type::callable_shader: + case api::pipeline_subobject_type::shader_groups: + ray_tracing_pipeline = true; + break; + } + } + + if (ray_tracing_pipeline) + { + return create_pipeline(layout, subobject_count, subobjects, out_pipeline, static_cast(nullptr)); + } + else if (compute_pipeline) + { + return create_pipeline(layout, subobject_count, subobjects, out_pipeline, static_cast(nullptr)); + } + else + { + return create_pipeline(layout, subobject_count, subobjects, out_pipeline, static_cast(nullptr)); + } +} +bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline *out_pipeline, const VkRayTracingPipelineCreateInfoKHR *orig_create_info) +{ std::vector raygen_desc; std::vector any_hit_desc; std::vector closest_hit_desc; @@ -1395,87 +1415,6 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, switch (subobjects[i].type) { - case api::pipeline_subobject_type::vertex_shader: - assert(subobjects[i].count == 1); - vs_desc = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::hull_shader: - assert(subobjects[i].count == 1); - hs_desc = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::domain_shader: - assert(subobjects[i].count == 1); - ds_desc = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::geometry_shader: - assert(subobjects[i].count == 1); - gs_desc = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::pixel_shader: - assert(subobjects[i].count == 1); - ps_desc = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::compute_shader: - assert(subobjects[i].count == 1); - cs_desc = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::input_layout: - input_layout_desc = subobjects[i]; - break; - case api::pipeline_subobject_type::stream_output_state: - assert(subobjects[i].count == 1); - stream_output_desc = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::blend_state: - assert(subobjects[i].count == 1); - blend_desc = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::rasterizer_state: - assert(subobjects[i].count == 1); - rasterizer_desc = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::depth_stencil_state: - assert(subobjects[i].count == 1); - depth_stencil_desc = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::primitive_topology: - assert(subobjects[i].count == 1); - topology = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::depth_stencil_format: - assert(subobjects[i].count == 1); - depth_stencil_format = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::render_target_formats: - assert(subobjects[i].count <= 8); - render_target_formats = subobjects[i]; - break; - case api::pipeline_subobject_type::sample_mask: - assert(subobjects[i].count == 1); - sample_mask = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::sample_count: - assert(subobjects[i].count == 1); - sample_count = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::viewport_count: - assert(subobjects[i].count == 1); - viewport_count = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::dynamic_pipeline_states: - dynamic_states_subobject = subobjects[i]; - break; - case api::pipeline_subobject_type::max_vertex_count: - assert(subobjects[i].count == 1); - break; // Ignored - case api::pipeline_subobject_type::amplification_shader: - assert(subobjects[i].count == 1); - as_desc = *static_cast(subobjects[i].data); - break; - case api::pipeline_subobject_type::mesh_shader: - assert(subobjects[i].count == 1); - ms_desc = *static_cast(subobjects[i].data); - break; case api::pipeline_subobject_type::raygen_shader: for (uint32_t k = 0; k < subobjects[i].count; ++k) raygen_desc.push_back(static_cast(subobjects[i].data)[k]); @@ -1530,14 +1469,15 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, } } - if (!raygen_desc.empty() || !shader_groups.empty()) - { #if VK_KHR_ray_tracing_pipeline && VK_KHR_pipeline_library - VkRayTracingPipelineCreateInfoKHR create_info { VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR }; - create_info.flags = convert_pipeline_flags(flags); + { + VkRayTracingPipelineCreateInfoKHR create_info = orig_create_info != nullptr ? *orig_create_info : VkRayTracingPipelineCreateInfoKHR { VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR }; create_info.layout = (VkPipelineLayout)layout.handle; create_info.maxPipelineRayRecursionDepth = max_recursion_depth; + if (find_in_structure_chain(create_info.pNext, VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO) == nullptr) + create_info.flags = convert_pipeline_flags(flags); + const size_t max_shader_stage_count = raygen_desc.size() + any_hit_desc.size() + closest_hit_desc.size() + miss_desc.size() + intersection_desc.size() + callable_desc.size(); std::vector shader_stage_info; std::vector shader_module_info; @@ -1710,16 +1650,8 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, *out_pipeline = { (uint64_t)object }; return true; } -#endif - } - else if (cs_desc.code_size != 0) - { - return create_pipeline(layout, subobject_count, subobjects, out_pipeline, static_cast(nullptr)); - } - else - { - return create_pipeline(layout, subobject_count, subobjects, out_pipeline, static_cast(nullptr)); } +#endif exit_failure: *out_pipeline = { 0 }; @@ -1775,7 +1707,6 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, *out_pipeline = { (uint64_t)object }; return true; } - } exit_failure: @@ -1883,6 +1814,9 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, case api::pipeline_subobject_type::dynamic_pipeline_states: dynamic_states_subobject = subobjects[i]; break; + case api::pipeline_subobject_type::max_vertex_count: + assert(subobjects[i].count == 1); + break; // Ignored case api::pipeline_subobject_type::amplification_shader: assert(subobjects[i].count == 1); as_desc = *static_cast(subobjects[i].data); @@ -1926,105 +1860,95 @@ bool reshade::vulkan::device_impl::create_pipeline(api::pipeline_layout layout, if (!setup_shader_stage_inline(stage, shader_desc, original_stage_info, shader_stage_info[create_info.stageCount], shader_module_info[create_info.stageCount], spec_info[create_info.stageCount], spec_map[create_info.stageCount], pnext_restore_links)) return false; ++create_info.stageCount; - return true; } - - if (original_stage_info != nullptr) + else if (original_stage_info != nullptr) + { shader_stage_info[create_info.stageCount++] = *original_stage_info; - + } return true; }; - const bool stage_setup_success = [&]() { - if (orig_create_info != nullptr) - { - const auto get_shader_desc = [&](VkShaderStageFlagBits stage) -> const api::shader_desc *{ - switch (stage) - { - case VK_SHADER_STAGE_VERTEX_BIT: - return &vs_desc; - case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: - return &hs_desc; - case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: - return &ds_desc; - case VK_SHADER_STAGE_GEOMETRY_BIT: - return &gs_desc; - case VK_SHADER_STAGE_FRAGMENT_BIT: - return &ps_desc; + if (orig_create_info != nullptr) + { + const auto get_shader_desc = [&](VkShaderStageFlagBits stage) -> const api::shader_desc * { + switch (stage) + { + case VK_SHADER_STAGE_VERTEX_BIT: + return &vs_desc; + case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: + return &hs_desc; + case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: + return &ds_desc; + case VK_SHADER_STAGE_GEOMETRY_BIT: + return &gs_desc; + case VK_SHADER_STAGE_FRAGMENT_BIT: + return &ps_desc; #if VK_EXT_mesh_shader - case VK_SHADER_STAGE_TASK_BIT_EXT: - return &as_desc; - case VK_SHADER_STAGE_MESH_BIT_EXT: - return &ms_desc; + case VK_SHADER_STAGE_TASK_BIT_EXT: + return &as_desc; + case VK_SHADER_STAGE_MESH_BIT_EXT: + return &ms_desc; #endif - default: - return nullptr; - } - }; - - VkShaderStageFlags present_stages = 0; + default: + return nullptr; + } + }; - for (uint32_t i = 0; i < orig_create_info->stageCount; ++i) - { - const VkPipelineShaderStageCreateInfo &original_stage_info = orig_create_info->pStages[i]; - const api::shader_desc *shader_desc = get_shader_desc(original_stage_info.stage); + VkShaderStageFlags present_stages = 0; - if (shader_desc == nullptr) - { - shader_stage_info[create_info.stageCount++] = original_stage_info; - continue; - } + for (uint32_t i = 0; i < orig_create_info->stageCount; ++i) + { + const VkPipelineShaderStageCreateInfo &orig_stage_info = orig_create_info->pStages[i]; - present_stages |= original_stage_info.stage; + if (const api::shader_desc *shader_desc = get_shader_desc(orig_stage_info.stage)) + { + present_stages |= orig_stage_info.stage; - if (!append_stage(original_stage_info.stage, *shader_desc, &original_stage_info)) - return false; + if (!append_stage(orig_stage_info.stage, *shader_desc, &orig_stage_info)) + goto exit_failure; + } + else + { + shader_stage_info[create_info.stageCount++] = orig_stage_info; } + } - // Add new added stages from addon - if ((present_stages & VK_SHADER_STAGE_VERTEX_BIT) == 0 && vs_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_VERTEX_BIT, vs_desc, nullptr)) - return false; - if ((present_stages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) == 0 && hs_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, hs_desc, nullptr)) - return false; - if ((present_stages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) == 0 && ds_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, ds_desc, nullptr)) - return false; - if ((present_stages & VK_SHADER_STAGE_GEOMETRY_BIT) == 0 && gs_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_GEOMETRY_BIT, gs_desc, nullptr)) - return false; - if ((present_stages & VK_SHADER_STAGE_FRAGMENT_BIT) == 0 && ps_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_FRAGMENT_BIT, ps_desc, nullptr)) - return false; + // Add new added stages from addon + if ((present_stages & VK_SHADER_STAGE_VERTEX_BIT) == 0 && vs_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_VERTEX_BIT, vs_desc, nullptr)) + goto exit_failure; + if ((present_stages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) == 0 && hs_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, hs_desc, nullptr)) + goto exit_failure; + if ((present_stages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) == 0 && ds_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, ds_desc, nullptr)) + goto exit_failure; + if ((present_stages & VK_SHADER_STAGE_GEOMETRY_BIT) == 0 && gs_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_GEOMETRY_BIT, gs_desc, nullptr)) + goto exit_failure; + if ((present_stages & VK_SHADER_STAGE_FRAGMENT_BIT) == 0 && ps_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_FRAGMENT_BIT, ps_desc, nullptr)) + goto exit_failure; #if VK_EXT_mesh_shader - if ((present_stages & VK_SHADER_STAGE_TASK_BIT_EXT) == 0 && as_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_TASK_BIT_EXT, as_desc, nullptr)) - return false; - if ((present_stages & VK_SHADER_STAGE_MESH_BIT_EXT) == 0 && ms_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_MESH_BIT_EXT, ms_desc, nullptr)) - return false; + if ((present_stages & VK_SHADER_STAGE_TASK_BIT_EXT) == 0 && as_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_TASK_BIT_EXT, as_desc, nullptr)) + goto exit_failure; + if ((present_stages & VK_SHADER_STAGE_MESH_BIT_EXT) == 0 && ms_desc.code_size != 0 && !append_stage(VK_SHADER_STAGE_MESH_BIT_EXT, ms_desc, nullptr)) + goto exit_failure; #endif - } - else - { - if (!append_stage(VK_SHADER_STAGE_VERTEX_BIT, vs_desc, nullptr)) - return false; - if (!append_stage(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, hs_desc, nullptr)) - return false; - if (!append_stage(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, ds_desc, nullptr)) - return false; - if (!append_stage(VK_SHADER_STAGE_GEOMETRY_BIT, gs_desc, nullptr)) - return false; - if (!append_stage(VK_SHADER_STAGE_FRAGMENT_BIT, ps_desc, nullptr)) - return false; + } + else + { + if (!append_stage(VK_SHADER_STAGE_VERTEX_BIT, vs_desc, nullptr)) + goto exit_failure; + if (!append_stage(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, hs_desc, nullptr)) + goto exit_failure; + if (!append_stage(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, ds_desc, nullptr)) + goto exit_failure; + if (!append_stage(VK_SHADER_STAGE_GEOMETRY_BIT, gs_desc, nullptr)) + goto exit_failure; + if (!append_stage(VK_SHADER_STAGE_FRAGMENT_BIT, ps_desc, nullptr)) + goto exit_failure; #if VK_EXT_mesh_shader - if (!append_stage(VK_SHADER_STAGE_TASK_BIT_EXT, as_desc, nullptr)) - return false; - if (!append_stage(VK_SHADER_STAGE_MESH_BIT_EXT, ms_desc, nullptr)) - return false; + if (!append_stage(VK_SHADER_STAGE_TASK_BIT_EXT, as_desc, nullptr)) + goto exit_failure; + if (!append_stage(VK_SHADER_STAGE_MESH_BIT_EXT, ms_desc, nullptr)) + goto exit_failure; #endif - } - - return true; - }(); - - if (!stage_setup_success) - { - goto exit_failure; } std::vector dynamic_states; @@ -2264,9 +2188,7 @@ void reshade::vulkan::device_impl::destroy_pipeline(api::pipeline pipeline) bool reshade::vulkan::device_impl::create_descriptor_set_layout(const api::pipeline_layout_param ¶m, VkDescriptorSetLayout *out_set_layout, std::vector &embedded_samplers) { - assert(out_set_layout != nullptr); assert(param.type != api::pipeline_layout_param_type::push_constants); - *out_set_layout = VK_NULL_HANDLE; bool push_descriptors = (param.type == api::pipeline_layout_param_type::push_descriptors); bool update_after_bind_pool = false; @@ -2277,14 +2199,17 @@ bool reshade::vulkan::device_impl::create_descriptor_set_layout(const api::pipel object_data data; data.num_descriptors = 0; - data.push_descriptors = push_descriptors; - data.ranges.reserve(range_count); if (with_flags) { data.ranges_with_flags.reserve(range_count); data.static_samplers.resize(range_count); } + else + { + data.ranges.reserve(range_count); + } data.binding_to_offset.reserve(range_count); + data.push_descriptors = push_descriptors; std::vector internal_bindings; std::vector internal_binding_flags; @@ -2296,10 +2221,7 @@ bool reshade::vulkan::device_impl::create_descriptor_set_layout(const api::pipel for (uint32_t k = 0, offset = 0; k < range_count; ++k, range = (with_flags ? static_cast(range) + 1 : range + 1)) { if (with_flags) - { - data.ranges_with_flags.push_back(*static_cast(range)); - data.ranges_with_flags.back().static_samplers = nullptr; - } + data.ranges_with_flags.emplace_back(*static_cast(range)).static_samplers = nullptr; else data.ranges.push_back(*range); @@ -2418,15 +2340,15 @@ bool reshade::vulkan::device_impl::create_pipeline_layout(uint32_t param_count, } bool reshade::vulkan::device_impl::create_pipeline_layout(uint32_t param_count, const api::pipeline_layout_param *params, api::pipeline_layout *out_layout, const VkPipelineLayoutCreateInfo *orig_create_info) { + object_data layout_data; + layout_data.set_layouts.reserve(param_count); + layout_data.owned_set_layouts.reserve(param_count); + layout_data.owns_set_layouts = true; + std::vector push_constant_ranges; - std::vector set_layouts; - std::vector owned_set_layouts; - std::vector embedded_samplers; push_constant_ranges.reserve(param_count); - set_layouts.reserve(param_count); - owned_set_layouts.reserve(param_count); - for (uint32_t i = 0, set_index = 0; i < param_count; ++i) + for (uint32_t i = 0; i < param_count; ++i) { if (params[i].type == api::pipeline_layout_param_type::push_constants) { @@ -2437,93 +2359,84 @@ bool reshade::vulkan::device_impl::create_pipeline_layout(uint32_t param_count, continue; } - bool reuse_original_set_layout = false; + bool reuse_orig_set_layout = false; VkDescriptorSetLayout set_layout = VK_NULL_HANDLE; - const VkDescriptorSetLayout original_set_layout = orig_create_info != nullptr && set_index < orig_create_info->setLayoutCount ? orig_create_info->pSetLayouts[set_index] : VK_NULL_HANDLE; - if (original_set_layout != VK_NULL_HANDLE) + if (orig_create_info != nullptr && i < orig_create_info->setLayoutCount) { - const auto original_set_layout_data = get_private_data_for_object(original_set_layout); - - switch (params[i].type) + if (const VkDescriptorSetLayout orig_set_layout = orig_create_info->pSetLayouts[i]) { - case api::pipeline_layout_param_type::descriptor_table: - if (!original_set_layout_data->push_descriptors && - params[i].descriptor_table.count == original_set_layout_data->ranges.size() && - params[i].descriptor_table.ranges == original_set_layout_data->ranges.data()) - { - set_layout = original_set_layout; - reuse_original_set_layout = true; - } - break; - case api::pipeline_layout_param_type::descriptor_table_with_flags: - if (!original_set_layout_data->push_descriptors && - params[i].descriptor_table_with_flags.count == original_set_layout_data->ranges_with_flags.size() && - params[i].descriptor_table_with_flags.ranges == original_set_layout_data->ranges_with_flags.data()) - { - set_layout = original_set_layout; - reuse_original_set_layout = true; - } - break; - case api::pipeline_layout_param_type::push_descriptors_with_ranges: - if (original_set_layout_data->push_descriptors && - params[i].descriptor_table.count == original_set_layout_data->ranges.size() && - params[i].descriptor_table.ranges == original_set_layout_data->ranges.data()) - { - set_layout = original_set_layout; - reuse_original_set_layout = true; - } - break; - case api::pipeline_layout_param_type::push_descriptors_with_ranges_and_flags: - if (original_set_layout_data->push_descriptors && - params[i].descriptor_table_with_flags.count == original_set_layout_data->ranges_with_flags.size() && - params[i].descriptor_table_with_flags.ranges == original_set_layout_data->ranges_with_flags.data()) + const auto orig_set_layout_data = get_private_data_for_object(orig_set_layout); + + switch (params[i].type) { - set_layout = original_set_layout; - reuse_original_set_layout = true; + case api::pipeline_layout_param_type::descriptor_table: + if (!orig_set_layout_data->push_descriptors && + params[i].descriptor_table.count == orig_set_layout_data->ranges.size() && + params[i].descriptor_table.ranges == orig_set_layout_data->ranges.data()) + { + set_layout = orig_set_layout; + reuse_orig_set_layout = true; + } + break; + case api::pipeline_layout_param_type::descriptor_table_with_flags: + if (!orig_set_layout_data->push_descriptors && + params[i].descriptor_table_with_flags.count == orig_set_layout_data->ranges_with_flags.size() && + params[i].descriptor_table_with_flags.ranges == orig_set_layout_data->ranges_with_flags.data()) + { + set_layout = orig_set_layout; + reuse_orig_set_layout = true; + } + break; + case api::pipeline_layout_param_type::push_descriptors_with_ranges: + if (orig_set_layout_data->push_descriptors && + params[i].descriptor_table.count == orig_set_layout_data->ranges.size() && + params[i].descriptor_table.ranges == orig_set_layout_data->ranges.data()) + { + set_layout = orig_set_layout; + reuse_orig_set_layout = true; + } + break; + case api::pipeline_layout_param_type::push_descriptors_with_ranges_and_flags: + if (orig_set_layout_data->push_descriptors && + params[i].descriptor_table_with_flags.count == orig_set_layout_data->ranges_with_flags.size() && + params[i].descriptor_table_with_flags.ranges == orig_set_layout_data->ranges_with_flags.data()) + { + set_layout = orig_set_layout; + reuse_orig_set_layout = true; + } + break; } - break; - default: - break; } - } - else if (orig_create_info != nullptr && - set_index < orig_create_info->setLayoutCount && - params[i].type == api::pipeline_layout_param_type::push_descriptors && - params[i].push_descriptors.count == 0) - { - set_layout = VK_NULL_HANDLE; - reuse_original_set_layout = true; + else if (params[i].type == api::pipeline_layout_param_type::push_descriptors && params[i].push_descriptors.count == 0) + { + set_layout = VK_NULL_HANDLE; + reuse_orig_set_layout = true; + } } - if (!reuse_original_set_layout && !create_descriptor_set_layout(params[i], &set_layout, embedded_samplers)) - goto exit_failure; + if (!reuse_orig_set_layout) + { + if (!create_descriptor_set_layout(params[i], &set_layout, layout_data.embedded_samplers)) + goto exit_failure; - if (!reuse_original_set_layout) - owned_set_layouts.push_back(set_layout); + layout_data.owned_set_layouts.push_back(set_layout); + } - set_layouts.push_back(set_layout); - ++set_index; + layout_data.set_layouts.push_back(set_layout); } { - VkPipelineLayoutCreateInfo create_info { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, orig_create_info != nullptr ? orig_create_info->pNext : nullptr }; - create_info.flags = orig_create_info != nullptr ? orig_create_info->flags : 0; - create_info.setLayoutCount = static_cast(set_layouts.size()); - create_info.pSetLayouts = set_layouts.data(); + VkPipelineLayoutCreateInfo create_info = orig_create_info != nullptr ? *orig_create_info : VkPipelineLayoutCreateInfo { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + create_info.setLayoutCount = static_cast(layout_data.set_layouts.size()); + create_info.pSetLayouts = layout_data.set_layouts.data(); create_info.pushConstantRangeCount = static_cast(push_constant_ranges.size()); create_info.pPushConstantRanges = push_constant_ranges.data(); if (VkPipelineLayout object = VK_NULL_HANDLE; vk.CreatePipelineLayout(_orig, &create_info, nullptr, &object) == VK_SUCCESS) { - object_data data; - data.owned_set_layouts = std::move(owned_set_layouts); - data.embedded_samplers = std::move(embedded_samplers); - data.set_layouts = std::move(set_layouts); - data.owns_set_layouts = true; - - register_object(object, std::move(data)); + register_object(object, std::move(layout_data)); *out_layout = { (uint64_t)object }; return true; @@ -2531,12 +2444,12 @@ bool reshade::vulkan::device_impl::create_pipeline_layout(uint32_t param_count, } exit_failure: - for (const VkSampler sampler : embedded_samplers) + for (const VkSampler sampler : layout_data.embedded_samplers) { vk.DestroySampler(_orig, sampler, nullptr); } - for (const VkDescriptorSetLayout set_layout : owned_set_layouts) + for (const VkDescriptorSetLayout set_layout : layout_data.owned_set_layouts) { unregister_object(set_layout); diff --git a/source/vulkan/vulkan_impl_device.hpp b/source/vulkan/vulkan_impl_device.hpp index 8395bd3aff..809f2691ae 100644 --- a/source/vulkan/vulkan_impl_device.hpp +++ b/source/vulkan/vulkan_impl_device.hpp @@ -73,6 +73,7 @@ namespace reshade::vulkan void update_texture_region(const api::subresource_data &data, api::resource dest, uint32_t dest_subresource, const api::subresource_box *dest_box) final; bool create_pipeline(api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline *out_pipeline) final; + bool create_pipeline(api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline *out_pipeline, const VkRayTracingPipelineCreateInfoKHR *orig_create_info); bool create_pipeline(api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline *out_pipeline, const VkComputePipelineCreateInfo *orig_create_info); bool create_pipeline(api::pipeline_layout layout, uint32_t subobject_count, const api::pipeline_subobject *subobjects, api::pipeline *out_pipeline, const VkGraphicsPipelineCreateInfo *orig_create_info); void destroy_pipeline(api::pipeline pipeline) final; From 0eff32e237578591a44946a0e0f9581620c026b4 Mon Sep 17 00:00:00 2001 From: crosire Date: Sun, 21 Jun 2026 12:49:50 +0200 Subject: [PATCH 13/26] Add descriptor types with dynamic offset --- include/reshade_api_device.hpp | 14 +++++++++++++- include/reshade_api_pipeline.hpp | 2 ++ include/reshade_events.hpp | 4 ++-- source/d3d10/d3d10_impl_command_list.cpp | 2 +- source/d3d10/d3d10_impl_device.hpp | 2 +- source/d3d11/d3d11_impl_command_list.cpp | 2 +- source/d3d11/d3d11_impl_device_context.hpp | 4 ++-- source/d3d12/d3d12_command_list.cpp | 10 +++++++--- source/d3d12/d3d12_impl_command_list.cpp | 2 +- source/d3d12/d3d12_impl_command_list.hpp | 2 +- source/d3d9/d3d9_impl_command_list.cpp | 2 +- source/d3d9/d3d9_impl_device.hpp | 2 +- source/opengl/opengl_impl_command_list.cpp | 2 +- source/opengl/opengl_impl_device_context.hpp | 2 +- source/vulkan/vulkan_hooks_command_list.cpp | 3 ++- source/vulkan/vulkan_impl_command_list.cpp | 8 ++++---- source/vulkan/vulkan_impl_command_list.hpp | 2 +- .../vulkan/vulkan_impl_command_list_immediate.cpp | 7 +++---- source/vulkan/vulkan_impl_device.cpp | 7 +++---- source/vulkan/vulkan_impl_type_convert.cpp | 11 ++++++++--- 20 files changed, 56 insertions(+), 34 deletions(-) diff --git a/include/reshade_api_device.hpp b/include/reshade_api_device.hpp index a7cc2260cb..4f65ddcf19 100644 --- a/include/reshade_api_device.hpp +++ b/include/reshade_api_device.hpp @@ -803,7 +803,7 @@ namespace reshade::api /// Index of the first pipeline parameter that describes the first descriptor table to bind (root parameter index in D3D12, descriptor set index in Vulkan). /// Number of descriptor tables to bind. /// Pointer to the first element of an array of descriptor tables to bind. - virtual void bind_descriptor_tables(shader_stage stages, pipeline_layout layout, uint32_t first, uint32_t count, const descriptor_table *tables) = 0; + virtual void bind_descriptor_tables(shader_stage stages, pipeline_layout layout, uint32_t first, uint32_t count, const descriptor_table *tables) { bind_descriptor_tables2(stages, layout, first, count, tables, 0, nullptr); } /// /// Binds an index buffer to the input-assembler stage. @@ -1177,6 +1177,18 @@ namespace reshade::api /// Optional pointer to a depth-stencil description, or to bind none. /// Additional parameters of the render pass. virtual void begin_render_pass2(uint32_t count, const render_pass_render_target_desc *rts, const render_pass_depth_stencil_desc *ds, render_pass_flags flags) = 0; + + /// + /// Binds an array of descriptor tables. + /// + /// Shader stages that will use the descriptors. + /// Pipeline layout that describes the descriptors. + /// Index of the first pipeline parameter that describes the first descriptor table to bind (root parameter index in D3D12, descriptor set index in Vulkan). + /// Number of descriptor tables to bind. + /// Pointer to the first element of an array of descriptor tables to bind. + /// Number of dynamic offsets. + /// Pointer to the first element of an array of dynamic offsets for any descriptors of type or . + virtual void bind_descriptor_tables2(shader_stage stages, pipeline_layout layout, uint32_t first, uint32_t count, const descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) = 0; }; /// diff --git a/include/reshade_api_pipeline.hpp b/include/reshade_api_pipeline.hpp index 845ee20708..f4c8c7d439 100644 --- a/include/reshade_api_pipeline.hpp +++ b/include/reshade_api_pipeline.hpp @@ -111,10 +111,12 @@ namespace reshade::api /// Descriptors are an array of . /// constant_buffer = 6, + constant_buffer_with_dynamic_offset = 8, /// /// Descriptors are an array of . /// shader_storage_buffer = 7, + shader_storage_buffer_with_dynamic_offset = 9, /// /// Descriptors are an array of . /// diff --git a/include/reshade_events.hpp b/include/reshade_events.hpp index 4d0a3218d2..80d68c1c37 100644 --- a/include/reshade_events.hpp +++ b/include/reshade_events.hpp @@ -1106,7 +1106,7 @@ namespace reshade /// ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable /// vkCmdBindDescriptorSets /// - /// Callback function signature: void (api::command_list *cmd_list, api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) + /// Callback function signature: void (api::command_list *cmd_list, api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) /// bind_descriptor_tables, @@ -1895,7 +1895,7 @@ namespace reshade RESHADE_DEFINE_ADDON_EVENT_TRAITS(addon_event::bind_scissor_rects, void, api::command_list *cmd_list, uint32_t first, uint32_t count, const api::rect *rects); RESHADE_DEFINE_ADDON_EVENT_TRAITS(addon_event::push_constants, void, api::command_list *cmd_list, api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, uint32_t first, uint32_t count, const void *values); RESHADE_DEFINE_ADDON_EVENT_TRAITS(addon_event::push_descriptors, void, api::command_list *cmd_list, api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, const api::descriptor_table_update &update); - RESHADE_DEFINE_ADDON_EVENT_TRAITS(addon_event::bind_descriptor_tables, void, api::command_list *cmd_list, api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables); + RESHADE_DEFINE_ADDON_EVENT_TRAITS(addon_event::bind_descriptor_tables, void, api::command_list *cmd_list, api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets); RESHADE_DEFINE_ADDON_EVENT_TRAITS(addon_event::bind_index_buffer, void, api::command_list *cmd_list, api::resource buffer, uint64_t offset, uint32_t index_size); RESHADE_DEFINE_ADDON_EVENT_TRAITS(addon_event::bind_vertex_buffers, void, api::command_list *cmd_list, uint32_t first, uint32_t count, const api::resource *buffers, const uint64_t *offsets, const uint32_t *strides); RESHADE_DEFINE_ADDON_EVENT_TRAITS(addon_event::bind_stream_output_buffers, void, api::command_list *cmd_list, uint32_t first, uint32_t count, const api::resource *buffers, const uint64_t *offsets, const uint64_t *max_sizes, const api::resource *counter_buffers, const uint64_t *counter_offsets); diff --git a/source/d3d10/d3d10_impl_command_list.cpp b/source/d3d10/d3d10_impl_command_list.cpp index bf5d313c1c..473a9576a5 100644 --- a/source/d3d10/d3d10_impl_command_list.cpp +++ b/source/d3d10/d3d10_impl_command_list.cpp @@ -420,7 +420,7 @@ void reshade::d3d10::device_impl::push_descriptors(api::shader_stage stages, api break; } } -void reshade::d3d10::device_impl::bind_descriptor_tables(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) +void reshade::d3d10::device_impl::bind_descriptor_tables2(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t, const uint32_t *) { for (uint32_t i = 0; i < count; ++i) { diff --git a/source/d3d10/d3d10_impl_device.hpp b/source/d3d10/d3d10_impl_device.hpp index e082457ad3..2cf32a410f 100644 --- a/source/d3d10/d3d10_impl_device.hpp +++ b/source/d3d10/d3d10_impl_device.hpp @@ -119,7 +119,7 @@ namespace reshade::d3d10 void push_constants(api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, uint32_t first, uint32_t count, const void *values) final; void push_descriptors(api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, const api::descriptor_table_update &update) final; - void bind_descriptor_tables(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) final; + void bind_descriptor_tables2(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) final; void bind_index_buffer(api::resource buffer, uint64_t offset, uint32_t index_size) final; void bind_vertex_buffers(uint32_t first, uint32_t count, const api::resource *buffers, const uint64_t *offsets, const uint32_t *strides) final; diff --git a/source/d3d11/d3d11_impl_command_list.cpp b/source/d3d11/d3d11_impl_command_list.cpp index b0bcfffbf5..096a73c507 100644 --- a/source/d3d11/d3d11_impl_command_list.cpp +++ b/source/d3d11/d3d11_impl_command_list.cpp @@ -592,7 +592,7 @@ void reshade::d3d11::device_context_impl::push_descriptors(api::shader_stage sta break; } } -void reshade::d3d11::device_context_impl::bind_descriptor_tables(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) +void reshade::d3d11::device_context_impl::bind_descriptor_tables2(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t, const uint32_t *) { for (uint32_t i = 0; i < count; ++i) { diff --git a/source/d3d11/d3d11_impl_device_context.hpp b/source/d3d11/d3d11_impl_device_context.hpp index 5c7a6cc09f..a68e3ed3d6 100644 --- a/source/d3d11/d3d11_impl_device_context.hpp +++ b/source/d3d11/d3d11_impl_device_context.hpp @@ -29,7 +29,7 @@ namespace reshade::d3d11 void push_constants(api::shader_stage, api::pipeline_layout, uint32_t, uint32_t, uint32_t, const void *) final { assert(false); } void push_descriptors(api::shader_stage, api::pipeline_layout, uint32_t, const api::descriptor_table_update &) final { assert(false); } - void bind_descriptor_tables(api::shader_stage, api::pipeline_layout, uint32_t, uint32_t, const api::descriptor_table *) final { assert(false); } + void bind_descriptor_tables2(api::shader_stage, api::pipeline_layout, uint32_t, uint32_t, const api::descriptor_table *, uint32_t, const uint32_t *) final { assert(false); } void bind_index_buffer(api::resource, uint64_t, uint32_t) final { assert(false); } void bind_vertex_buffers(uint32_t, uint32_t, const api::resource *, const uint64_t *, const uint32_t *) final { assert(false); } @@ -111,7 +111,7 @@ namespace reshade::d3d11 void push_constants(api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, uint32_t first, uint32_t count, const void *values) final; void push_descriptors(api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, const api::descriptor_table_update &update) final; - void bind_descriptor_tables(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) final; + void bind_descriptor_tables2(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) final; void bind_index_buffer(api::resource buffer, uint64_t offset, uint32_t index_size) final; void bind_vertex_buffers(uint32_t first, uint32_t count, const api::resource *buffers, const uint64_t *offsets, const uint32_t *strides) final; diff --git a/source/d3d12/d3d12_command_list.cpp b/source/d3d12/d3d12_command_list.cpp index 8726653564..44f3d73460 100644 --- a/source/d3d12/d3d12_command_list.cpp +++ b/source/d3d12/d3d12_command_list.cpp @@ -211,7 +211,7 @@ void STDMETHODCALLTYPE D3D12GraphicsCommandList::ClearState(ID3D12PipelineState constexpr size_t max_null_objects = D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT * 2; void *const null_objects[max_null_objects] = {}; - reshade::invoke_addon_event(this, reshade::api::shader_stage::all, reshade::api::pipeline_layout {}, 0, 0, nullptr); + reshade::invoke_addon_event(this, reshade::api::shader_stage::all, reshade::api::pipeline_layout {}, 0, 0, nullptr, 0, nullptr); reshade::invoke_addon_event(this, reshade::api::resource {}, 0, 0); reshade::invoke_addon_event(this, 0, D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT, reinterpret_cast(null_objects), reinterpret_cast(null_objects), reinterpret_cast(null_objects)); @@ -529,6 +529,7 @@ void STDMETHODCALLTYPE D3D12GraphicsCommandList::SetComputeRootSignature(ID3D12R reshade::api::shader_stage::all_compute | reshade::api::shader_stage::all_ray_tracing, to_handle(_current_root_signature[1]), 0, + 0, nullptr, 0, nullptr); #endif } @@ -544,6 +545,7 @@ void STDMETHODCALLTYPE D3D12GraphicsCommandList::SetGraphicsRootSignature(ID3D12 reshade::api::shader_stage::all_graphics, to_handle(_current_root_signature[0]), 0, + 0, nullptr, 0, nullptr); #endif } @@ -557,7 +559,8 @@ void STDMETHODCALLTYPE D3D12GraphicsCommandList::SetComputeRootDescriptorTable(U reshade::api::shader_stage::all_compute | reshade::api::shader_stage::all_ray_tracing, to_handle(_current_root_signature[1]), RootParameterIndex, - 1, reinterpret_cast(&BaseDescriptor)); + 1, reinterpret_cast(&BaseDescriptor), + 0, nullptr); #endif } void STDMETHODCALLTYPE D3D12GraphicsCommandList::SetGraphicsRootDescriptorTable(UINT RootParameterIndex, D3D12_GPU_DESCRIPTOR_HANDLE BaseDescriptor) @@ -570,7 +573,8 @@ void STDMETHODCALLTYPE D3D12GraphicsCommandList::SetGraphicsRootDescriptorTable( reshade::api::shader_stage::all_graphics, to_handle(_current_root_signature[0]), RootParameterIndex, - 1, reinterpret_cast(&BaseDescriptor)); + 1, reinterpret_cast(&BaseDescriptor), + 0, nullptr); #endif } void STDMETHODCALLTYPE D3D12GraphicsCommandList::SetComputeRoot32BitConstant(UINT RootParameterIndex, UINT SrcData, UINT DestOffsetIn32BitValues) diff --git a/source/d3d12/d3d12_impl_command_list.cpp b/source/d3d12/d3d12_impl_command_list.cpp index 0dc921c409..b35d645a67 100644 --- a/source/d3d12/d3d12_impl_command_list.cpp +++ b/source/d3d12/d3d12_impl_command_list.cpp @@ -494,7 +494,7 @@ void reshade::d3d12::command_list_impl::push_descriptors(api::shader_stage stage _orig->SetGraphicsRootDescriptorTable(layout_param, base_handle_gpu); } } -void reshade::d3d12::command_list_impl::bind_descriptor_tables(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) +void reshade::d3d12::command_list_impl::bind_descriptor_tables2(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) { assert(tables != nullptr || count == 0); diff --git a/source/d3d12/d3d12_impl_command_list.hpp b/source/d3d12/d3d12_impl_command_list.hpp index 4f0a44b1a5..c9b3fa8396 100644 --- a/source/d3d12/d3d12_impl_command_list.hpp +++ b/source/d3d12/d3d12_impl_command_list.hpp @@ -32,7 +32,7 @@ namespace reshade::d3d12 void push_constants(api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, uint32_t first, uint32_t count, const void *values) final; void push_descriptors(api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, const api::descriptor_table_update &update) final; - void bind_descriptor_tables(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) final; + void bind_descriptor_tables2(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) final; void bind_index_buffer(api::resource buffer, uint64_t offset, uint32_t index_size) final; void bind_vertex_buffers(uint32_t first, uint32_t count, const api::resource *buffers, const uint64_t *offsets, const uint32_t *strides) final; diff --git a/source/d3d9/d3d9_impl_command_list.cpp b/source/d3d9/d3d9_impl_command_list.cpp index e7954ba9eb..52cfeabed4 100644 --- a/source/d3d9/d3d9_impl_command_list.cpp +++ b/source/d3d9/d3d9_impl_command_list.cpp @@ -378,7 +378,7 @@ void reshade::d3d9::device_impl::push_descriptors(api::shader_stage stages, api: } } } -void reshade::d3d9::device_impl::bind_descriptor_tables(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) +void reshade::d3d9::device_impl::bind_descriptor_tables2(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t, const uint32_t *) { for (uint32_t i = 0; i < count; ++i) { diff --git a/source/d3d9/d3d9_impl_device.hpp b/source/d3d9/d3d9_impl_device.hpp index 4a52af8664..5ef8ed4296 100644 --- a/source/d3d9/d3d9_impl_device.hpp +++ b/source/d3d9/d3d9_impl_device.hpp @@ -112,7 +112,7 @@ namespace reshade::d3d9 void push_constants(api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, uint32_t first, uint32_t count, const void *values) final; void push_descriptors(api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, const api::descriptor_table_update &update) final; - void bind_descriptor_tables(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) final; + void bind_descriptor_tables2(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) final; void bind_index_buffer(api::resource buffer, [[maybe_unused]] uint64_t offset, [[maybe_unused]] uint32_t index_size) final; void bind_vertex_buffers(uint32_t first, uint32_t count, const api::resource *buffers, const uint64_t *offsets, const uint32_t *strides) final; diff --git a/source/opengl/opengl_impl_command_list.cpp b/source/opengl/opengl_impl_command_list.cpp index d7e3ebb88d..35fb18e965 100644 --- a/source/opengl/opengl_impl_command_list.cpp +++ b/source/opengl/opengl_impl_command_list.cpp @@ -1200,7 +1200,7 @@ void reshade::opengl::device_context_impl::push_descriptors(api::shader_stage, a break; } } -void reshade::opengl::device_context_impl::bind_descriptor_tables(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) +void reshade::opengl::device_context_impl::bind_descriptor_tables2(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t, const uint32_t *) { for (uint32_t i = 0; i < count; ++i) { diff --git a/source/opengl/opengl_impl_device_context.hpp b/source/opengl/opengl_impl_device_context.hpp index 3e01d33c95..b146d58ca3 100644 --- a/source/opengl/opengl_impl_device_context.hpp +++ b/source/opengl/opengl_impl_device_context.hpp @@ -48,7 +48,7 @@ namespace reshade::opengl void push_constants(api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, uint32_t first, uint32_t count, const void *values) final; void push_descriptors(api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, const api::descriptor_table_update &update) final; - void bind_descriptor_tables(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) final; + void bind_descriptor_tables2(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) final; void bind_index_buffer(api::resource buffer, uint64_t offset, uint32_t index_size) final; void bind_vertex_buffers(uint32_t first, uint32_t count, const api::resource *buffers, const uint64_t *offsets, const uint32_t *strides) final; diff --git a/source/vulkan/vulkan_hooks_command_list.cpp b/source/vulkan/vulkan_hooks_command_list.cpp index 747090ebf2..1457a35c41 100644 --- a/source/vulkan/vulkan_hooks_command_list.cpp +++ b/source/vulkan/vulkan_hooks_command_list.cpp @@ -462,7 +462,8 @@ void VKAPI_CALL vkCmdBindDescriptorSets(VkCommandBuffer commandBuffer, VkPipelin reshade::vulkan::convert_shader_stages(pipelineBindPoint), reshade::api::pipeline_layout { (uint64_t)layout }, firstSet, descriptorSetCount, - reinterpret_cast(pDescriptorSets)); + reinterpret_cast(pDescriptorSets), + dynamicOffsetCount, pDynamicOffsets); #endif } diff --git a/source/vulkan/vulkan_impl_command_list.cpp b/source/vulkan/vulkan_impl_command_list.cpp index 3281ff4322..a89cb296a2 100644 --- a/source/vulkan/vulkan_impl_command_list.cpp +++ b/source/vulkan/vulkan_impl_command_list.cpp @@ -614,7 +614,7 @@ void reshade::vulkan::command_list_impl::push_descriptors(api::shader_stage stag #endif } } -void reshade::vulkan::command_list_impl::bind_descriptor_tables(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) +void reshade::vulkan::command_list_impl::bind_descriptor_tables2(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) { if ((stages & api::shader_stage::all_compute) != 0) { @@ -622,7 +622,7 @@ void reshade::vulkan::command_list_impl::bind_descriptor_tables(api::shader_stag _orig, VK_PIPELINE_BIND_POINT_COMPUTE, (VkPipelineLayout)layout.handle, - first, count, reinterpret_cast(tables), 0, nullptr); + first, count, reinterpret_cast(tables), dynamic_offset_count, dynamic_offsets); } if ((stages & api::shader_stage::all_graphics) != 0) { @@ -630,7 +630,7 @@ void reshade::vulkan::command_list_impl::bind_descriptor_tables(api::shader_stag _orig, VK_PIPELINE_BIND_POINT_GRAPHICS, (VkPipelineLayout)layout.handle, - first, count, reinterpret_cast(tables), 0, nullptr); + first, count, reinterpret_cast(tables), dynamic_offset_count, dynamic_offsets); } if ((stages & api::shader_stage::all_ray_tracing) != 0) { @@ -639,7 +639,7 @@ void reshade::vulkan::command_list_impl::bind_descriptor_tables(api::shader_stag _orig, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, (VkPipelineLayout)layout.handle, - first, count, reinterpret_cast(tables), 0, nullptr); + first, count, reinterpret_cast(tables), dynamic_offset_count, dynamic_offsets); #else assert(false); #endif diff --git a/source/vulkan/vulkan_impl_command_list.hpp b/source/vulkan/vulkan_impl_command_list.hpp index bb5cf4584e..0b923112be 100644 --- a/source/vulkan/vulkan_impl_command_list.hpp +++ b/source/vulkan/vulkan_impl_command_list.hpp @@ -31,7 +31,7 @@ namespace reshade::vulkan void push_constants(api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, uint32_t first, uint32_t count, const void *values) final; void push_descriptors(api::shader_stage stages, api::pipeline_layout layout, uint32_t layout_param, const api::descriptor_table_update &update) override; - void bind_descriptor_tables(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables) final; + void bind_descriptor_tables2(api::shader_stage stages, api::pipeline_layout layout, uint32_t first, uint32_t count, const api::descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) final; void bind_index_buffer(api::resource buffer, uint64_t offset, uint32_t index_size) final; void bind_vertex_buffers(uint32_t first, uint32_t count, const api::resource *buffers, const uint64_t *offsets, const uint32_t *strides) final; diff --git a/source/vulkan/vulkan_impl_command_list_immediate.cpp b/source/vulkan/vulkan_impl_command_list_immediate.cpp index 5034a39a82..62ee909269 100644 --- a/source/vulkan/vulkan_impl_command_list_immediate.cpp +++ b/source/vulkan/vulkan_impl_command_list_immediate.cpp @@ -180,13 +180,12 @@ void reshade::vulkan::command_list_immediate_impl::push_descriptors(api::shader_ break; case api::descriptor_type::constant_buffer: case api::descriptor_type::shader_storage_buffer: + case api::descriptor_type::constant_buffer_with_dynamic_offset: + case api::descriptor_type::shader_storage_buffer_with_dynamic_offset: write.pBufferInfo = reinterpret_cast(update.descriptors); break; default: - if (write.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || write.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) - write.pBufferInfo = reinterpret_cast(update.descriptors); - else - assert(false); + assert(false); break; } diff --git a/source/vulkan/vulkan_impl_device.cpp b/source/vulkan/vulkan_impl_device.cpp index 7f20f0ccbb..2e881a1c1d 100644 --- a/source/vulkan/vulkan_impl_device.cpp +++ b/source/vulkan/vulkan_impl_device.cpp @@ -2658,6 +2658,8 @@ void reshade::vulkan::device_impl::update_descriptor_tables(uint32_t count, cons break; case api::descriptor_type::constant_buffer: case api::descriptor_type::shader_storage_buffer: + case api::descriptor_type::constant_buffer_with_dynamic_offset: + case api::descriptor_type::shader_storage_buffer_with_dynamic_offset: write.pBufferInfo = static_cast(update.descriptors); break; #if VK_KHR_acceleration_structure @@ -2672,10 +2674,7 @@ void reshade::vulkan::device_impl::update_descriptor_tables(uint32_t count, cons break; #endif default: - if (write.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || write.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) - write.pBufferInfo = static_cast(update.descriptors); - else - assert(false); + assert(false); break; } } diff --git a/source/vulkan/vulkan_impl_type_convert.cpp b/source/vulkan/vulkan_impl_type_convert.cpp index 992a0382a7..e5e629ebad 100644 --- a/source/vulkan/vulkan_impl_type_convert.cpp +++ b/source/vulkan/vulkan_impl_type_convert.cpp @@ -2363,6 +2363,10 @@ auto reshade::vulkan::convert_descriptor_type(api::descriptor_type value) -> VkD return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; case api::descriptor_type::shader_storage_buffer: return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + case api::descriptor_type::constant_buffer_with_dynamic_offset: + return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + case api::descriptor_type::shader_storage_buffer_with_dynamic_offset: + return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; #if VK_KHR_acceleration_structure case api::descriptor_type::acceleration_structure: return VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; @@ -2392,15 +2396,16 @@ auto reshade::vulkan::convert_descriptor_type(VkDescriptorType value) -> api::de return api::descriptor_type::constant_buffer; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: return api::descriptor_type::shader_storage_buffer; + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + return api::descriptor_type::constant_buffer_with_dynamic_offset; + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + return api::descriptor_type::shader_storage_buffer_with_dynamic_offset; #if VK_KHR_acceleration_structure case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: return api::descriptor_type::acceleration_structure; #endif default: assert(false); - [[fallthrough]]; - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: return static_cast(value); } } From f191dc03ce8fb435fb1df2ed59fac1e7f944c90e Mon Sep 17 00:00:00 2001 From: HYPER-THEORY <73767767+HYPER-THEORY@users.noreply.github.com> Date: Sun, 28 Jun 2026 19:06:14 +0800 Subject: [PATCH 14/26] Fix build of add-on examples after addition of descriptor types with dynamic offset (#429) --- examples/04-api_trace/api_trace_addon.cpp | 2 +- examples/10-texture_overlay/texture_overlay_addon.cpp | 2 +- examples/13-effects_during_frame/effects_during_frame_addon.cpp | 2 +- examples/utils/state_tracking.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/04-api_trace/api_trace_addon.cpp b/examples/04-api_trace/api_trace_addon.cpp index eb653f1081..434600894a 100644 --- a/examples/04-api_trace/api_trace_addon.cpp +++ b/examples/04-api_trace/api_trace_addon.cpp @@ -585,7 +585,7 @@ static void on_push_descriptors(command_list *, shader_stage stages, pipeline_la reshade::log::message(reshade::log::level::info, s.str().c_str()); } -static void on_bind_descriptor_tables(command_list *, shader_stage stages, pipeline_layout layout, uint32_t first, uint32_t count, const descriptor_table *tables) +static void on_bind_descriptor_tables(command_list *, shader_stage stages, pipeline_layout layout, uint32_t first, uint32_t count, const descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) { if (!s_do_capture) return; diff --git a/examples/10-texture_overlay/texture_overlay_addon.cpp b/examples/10-texture_overlay/texture_overlay_addon.cpp index 5b7d05097c..e6ff8a3f43 100644 --- a/examples/10-texture_overlay/texture_overlay_addon.cpp +++ b/examples/10-texture_overlay/texture_overlay_addon.cpp @@ -200,7 +200,7 @@ static void on_push_descriptors(command_list *cmd_list, shader_stage stages, pip } } } -static void on_bind_descriptor_tables(command_list *cmd_list, shader_stage stages, pipeline_layout layout, uint32_t first, uint32_t count, const descriptor_table *tables) +static void on_bind_descriptor_tables(command_list *cmd_list, shader_stage stages, pipeline_layout layout, uint32_t first, uint32_t count, const descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) { if ((stages & shader_stage::pixel) != shader_stage::pixel) return; diff --git a/examples/13-effects_during_frame/effects_during_frame_addon.cpp b/examples/13-effects_during_frame/effects_during_frame_addon.cpp index 5218b52020..48697a72fb 100644 --- a/examples/13-effects_during_frame/effects_during_frame_addon.cpp +++ b/examples/13-effects_during_frame/effects_during_frame_addon.cpp @@ -82,7 +82,7 @@ static bool on_end_render_pass(command_list *cmd_list) const resource_desc render_target_desc = device->get_resource_desc(device->get_resource_from_view(cmd_data.current_main_rtv)); if (render_target_desc.texture.width != width || render_target_desc.texture.height != height) - return; + return false; } // Render post-processing effects when a specific render pass is found (instead of at the end of the frame) diff --git a/examples/utils/state_tracking.cpp b/examples/utils/state_tracking.cpp index 604f2add1c..a22f51653f 100644 --- a/examples/utils/state_tracking.cpp +++ b/examples/utils/state_tracking.cpp @@ -124,7 +124,7 @@ static void on_bind_scissor_rects(command_list *cmd_list, uint32_t first, uint32 state.scissor_rects[i + first] = rects[i]; } -static void on_bind_descriptor_tables(command_list *cmd_list, shader_stage stages, pipeline_layout layout, uint32_t first, uint32_t count, const descriptor_table *tables) +static void on_bind_descriptor_tables(command_list *cmd_list, shader_stage stages, pipeline_layout layout, uint32_t first, uint32_t count, const descriptor_table *tables, uint32_t dynamic_offset_count, const uint32_t *dynamic_offsets) { auto &state = cmd_list->get_private_data()->descriptor_tables[stages]; From 5c669de226546088b395f1e75375e390316bf6b2 Mon Sep 17 00:00:00 2001 From: Ritsu <19637006+MohannedElfatih@users.noreply.github.com> Date: Sat, 1 Aug 2026 14:38:24 +0400 Subject: [PATCH 15/26] Enable support for push descriptors in Vulkan 1.4 when available (#432) --- source/vulkan/vulkan_hooks_device.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/source/vulkan/vulkan_hooks_device.cpp b/source/vulkan/vulkan_hooks_device.cpp index 97966089b7..28a4d938ff 100644 --- a/source/vulkan/vulkan_hooks_device.cpp +++ b/source/vulkan/vulkan_hooks_device.cpp @@ -450,16 +450,35 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi } VkPhysicalDeviceHostImageCopyFeatures host_image_copy_features; + VkPhysicalDeviceVulkan14Features vulkan_14_features; + if (instance.api_version >= VK_API_VERSION_1_4) + { + VkPhysicalDeviceVulkan14Features supported_vulkan_14_features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES }; + VkPhysicalDeviceFeatures2 supported_features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, &supported_vulkan_14_features }; + instance.dispatch_table.GetPhysicalDeviceFeatures2(physicalDevice, &supported_features); + + push_descriptor_ext = supported_vulkan_14_features.pushDescriptor; + } if (const auto existing_vulkan_14_features = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES)) { assert(instance.api_version >= VK_API_VERSION_1_4); - push_descriptor_ext = existing_vulkan_14_features->pushDescriptor; + if (push_descriptor_ext) { + const_cast(existing_vulkan_14_features)->pushDescriptor = VK_TRUE; + } host_image_copy_ext = existing_vulkan_14_features->hostImageCopy; } else { + if (instance.api_version >= VK_API_VERSION_1_4 && push_descriptor_ext) + { + vulkan_14_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES, const_cast(create_info.pNext) }; + vulkan_14_features.pushDescriptor = VK_TRUE; + + create_info.pNext = &vulkan_14_features; + } + if (const auto existing_host_image_copy_features = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES)) { From 8cb3dd6cce02b52793a188152a742cdde3ed52a6 Mon Sep 17 00:00:00 2001 From: bwlupus-ctrl Date: Sat, 1 Aug 2026 06:44:17 -0400 Subject: [PATCH 16/26] Fix RGB10A2 pixel format detection in OpenGL (#430) Co-Authored-By: Claude Opus 4.8 --- source/opengl/opengl_impl_type_convert.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/opengl/opengl_impl_type_convert.cpp b/source/opengl/opengl_impl_type_convert.cpp index 9152c4d635..57d283cfd9 100644 --- a/source/opengl/opengl_impl_type_convert.cpp +++ b/source/opengl/opengl_impl_type_convert.cpp @@ -726,6 +726,8 @@ auto reshade::opengl::convert_pixel_format(const PIXELFORMATDESCRIPTOR &pfd) -> return api::format::b5g6r5_unorm; case 24: case 32: + if (pfd.cRedBits == 10 && pfd.cGreenBits == 10 && pfd.cBlueBits == 10) + return api::format::r10g10b10a2_unorm; if (pfd.cRedBits == 11 && pfd.cGreenBits == 11 && pfd.cBlueBits == 10) return api::format::r11g11b10_float; if (pfd.cAlphaBits != 0) From 7540384425d66cb95b7a1a899af533b3e49050ae Mon Sep 17 00:00:00 2001 From: crosire Date: Sat, 1 Aug 2026 13:21:28 +0200 Subject: [PATCH 17/26] Add "dynamic_state::input_element_stride" --- include/reshade_api_pipeline.hpp | 2 ++ source/d3d10/d3d10_device.cpp | 3 +++ source/d3d10/d3d10_impl_device.cpp | 1 + source/d3d11/d3d11_device.cpp | 3 +++ source/d3d11/d3d11_impl_device.cpp | 1 + source/d3d12/d3d12_device.cpp | 1 + source/d3d12/d3d12_impl_device.cpp | 1 + source/d3d9/d3d9_device.cpp | 5 ++++- source/opengl/opengl.cpp | 2 +- source/vulkan/vulkan_impl_type_convert.cpp | 16 ++++++++++++++++ 10 files changed, 33 insertions(+), 2 deletions(-) diff --git a/include/reshade_api_pipeline.hpp b/include/reshade_api_pipeline.hpp index f4c8c7d439..353db522be 100644 --- a/include/reshade_api_pipeline.hpp +++ b/include/reshade_api_pipeline.hpp @@ -607,6 +607,7 @@ namespace reshade::api /// Stride of the entire vertex (this has to be consistent for all elements per vertex buffer binding). /// Set to zero in case this is unknown. /// + /// uint32_t stride = 0; /// /// Number of instances to draw using the same per-instance data before advancing by one element. @@ -1303,6 +1304,7 @@ namespace reshade::api alpha_func = 25, srgb_write_enable = 194, primitive_topology = 1000, + input_element_stride = 1009, sample_mask = 162, // Blend state diff --git a/source/d3d10/d3d10_device.cpp b/source/d3d10/d3d10_device.cpp index 979d63fd10..0d0d39c91b 100644 --- a/source/d3d10/d3d10_device.cpp +++ b/source/d3d10/d3d10_device.cpp @@ -1163,12 +1163,15 @@ HRESULT STDMETHODCALLTYPE D3D10Device::CreateInputLayout(const D3D10_INPUT_ELEME for (UINT i = 0; i < NumElements; ++i) desc.push_back(reshade::d3d10::convert_input_element(pInputElementDescs[i])); + reshade::api::dynamic_state dynamic_states[] = { reshade::api::dynamic_state::input_element_stride }; + reshade::api::shader_desc signature_desc = {}; signature_desc.code = pShaderBytecodeWithInputSignature; signature_desc.code_size = BytecodeLength; const reshade::api::pipeline_subobject subobjects[] = { { reshade::api::pipeline_subobject_type::input_layout, static_cast(desc.size()), desc.data() }, + { reshade::api::pipeline_subobject_type::dynamic_pipeline_states, static_cast(std::size(dynamic_states)), dynamic_states }, { reshade::api::pipeline_subobject_type::vertex_shader, 1, &signature_desc } }; diff --git a/source/d3d10/d3d10_impl_device.cpp b/source/d3d10/d3d10_impl_device.cpp index 431d3b8eed..deef0bf826 100644 --- a/source/d3d10/d3d10_impl_device.cpp +++ b/source/d3d10/d3d10_impl_device.cpp @@ -874,6 +874,7 @@ bool reshade::d3d10::device_impl::create_pipeline(api::pipeline_layout, uint32_t for (uint32_t k = 0; k < subobjects[i].count; ++k) if (const auto state = static_cast(subobjects[i].data)[k]; state != api::dynamic_state::primitive_topology && + state != api::dynamic_state::input_element_stride && state != api::dynamic_state::blend_constant && state != api::dynamic_state::sample_mask && state != api::dynamic_state::front_stencil_reference_value && diff --git a/source/d3d11/d3d11_device.cpp b/source/d3d11/d3d11_device.cpp index ecacfb95a2..7969a02d8e 100644 --- a/source/d3d11/d3d11_device.cpp +++ b/source/d3d11/d3d11_device.cpp @@ -627,12 +627,15 @@ HRESULT STDMETHODCALLTYPE D3D11Device::CreateInputLayout(const D3D11_INPUT_ELEME for (UINT i = 0; i < NumElements; ++i) desc.push_back(reshade::d3d11::convert_input_element(pInputElementDescs[i])); + reshade::api::dynamic_state dynamic_states[] = { reshade::api::dynamic_state::input_element_stride }; + reshade::api::shader_desc signature_desc = {}; signature_desc.code = pShaderBytecodeWithInputSignature; signature_desc.code_size = BytecodeLength; const reshade::api::pipeline_subobject subobjects[] = { { reshade::api::pipeline_subobject_type::input_layout, static_cast(desc.size()), desc.data() }, + { reshade::api::pipeline_subobject_type::dynamic_pipeline_states, static_cast(std::size(dynamic_states)), dynamic_states }, { reshade::api::pipeline_subobject_type::vertex_shader, 1, &signature_desc } }; diff --git a/source/d3d11/d3d11_impl_device.cpp b/source/d3d11/d3d11_impl_device.cpp index 2dc551e30e..cb6f505e76 100644 --- a/source/d3d11/d3d11_impl_device.cpp +++ b/source/d3d11/d3d11_impl_device.cpp @@ -1121,6 +1121,7 @@ bool reshade::d3d11::device_impl::create_pipeline(api::pipeline_layout, uint32_t for (uint32_t k = 0; k < subobjects[i].count; ++k) if (const auto state = static_cast(subobjects[i].data)[k]; state != api::dynamic_state::primitive_topology && + state != api::dynamic_state::input_element_stride && state != api::dynamic_state::blend_constant && state != api::dynamic_state::sample_mask && state != api::dynamic_state::front_stencil_reference_value && diff --git a/source/d3d12/d3d12_device.cpp b/source/d3d12/d3d12_device.cpp index 1f04f3ce76..fab80ad327 100644 --- a/source/d3d12/d3d12_device.cpp +++ b/source/d3d12/d3d12_device.cpp @@ -2798,6 +2798,7 @@ bool D3D12Device::invoke_create_and_init_pipeline_event(const D3D12_PIPELINE_STA std::vector dynamic_states = { reshade::api::dynamic_state::primitive_topology, + reshade::api::dynamic_state::input_element_stride, reshade::api::dynamic_state::blend_constant, reshade::api::dynamic_state::front_stencil_reference_value, reshade::api::dynamic_state::back_stencil_reference_value diff --git a/source/d3d12/d3d12_impl_device.cpp b/source/d3d12/d3d12_impl_device.cpp index 22520da700..b1bbfc0288 100644 --- a/source/d3d12/d3d12_impl_device.cpp +++ b/source/d3d12/d3d12_impl_device.cpp @@ -931,6 +931,7 @@ bool reshade::d3d12::device_impl::create_pipeline(api::pipeline_layout layout, u for (uint32_t k = 0; k < subobjects[i].count; ++k) if (const auto state = static_cast(subobjects[i].data)[k]; state != api::dynamic_state::primitive_topology && + state != api::dynamic_state::input_element_stride && state != api::dynamic_state::blend_constant && state != api::dynamic_state::front_stencil_reference_value && state != api::dynamic_state::back_stencil_reference_value && diff --git a/source/d3d9/d3d9_device.cpp b/source/d3d9/d3d9_device.cpp index de38c46b8c..293edcb814 100644 --- a/source/d3d9/d3d9_device.cpp +++ b/source/d3d9/d3d9_device.cpp @@ -1902,8 +1902,11 @@ HRESULT STDMETHODCALLTYPE Direct3DDevice9::CreateVertexDeclaration(const D3DVERT for (const D3DVERTEXELEMENT9 *internal_element = pVertexElements; internal_element->Stream != 0xFF; ++internal_element) desc.push_back(reshade::d3d9::convert_input_element(*internal_element)); + reshade::api::dynamic_state dynamic_states[] = { reshade::api::dynamic_state::input_element_stride }; + const reshade::api::pipeline_subobject subobjects[] = { - { reshade::api::pipeline_subobject_type::input_layout, static_cast(desc.size()), desc.data() } + { reshade::api::pipeline_subobject_type::input_layout, static_cast(desc.size()), desc.data() }, + { reshade::api::pipeline_subobject_type::dynamic_pipeline_states, static_cast(std::size(dynamic_states)), dynamic_states } }; if (reshade::invoke_addon_event(this, _global_pipeline_layout, static_cast(std::size(subobjects)), subobjects)) diff --git a/source/opengl/opengl.cpp b/source/opengl/opengl.cpp index 32686357cc..21b779fd4f 100644 --- a/source/opengl/opengl.cpp +++ b/source/opengl/opengl.cpp @@ -841,7 +841,7 @@ extern "C" void APIENTRY glEnable(GLenum cap) reshade::has_addon_event()) { uint32_t value = GL_TRUE; - reshade::api::dynamic_state state = { reshade::api::dynamic_state::unknown }; + reshade::api::dynamic_state state = reshade::api::dynamic_state::unknown; switch (cap) { case GL_ALPHA_TEST: diff --git a/source/vulkan/vulkan_impl_type_convert.cpp b/source/vulkan/vulkan_impl_type_convert.cpp index e5e629ebad..515e338632 100644 --- a/source/vulkan/vulkan_impl_type_convert.cpp +++ b/source/vulkan/vulkan_impl_type_convert.cpp @@ -1646,6 +1646,9 @@ void reshade::vulkan::convert_dynamic_states(uint32_t count, const api::dynamic_ case api::dynamic_state::primitive_topology: internal_states.push_back(VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY); break; + case api::dynamic_state::input_element_stride: + internal_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE); + break; case api::dynamic_state::depth_enable: internal_states.push_back(VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE); break; @@ -1730,6 +1733,8 @@ std::vector reshade::vulkan::convert_dynamic_states case VK_DYNAMIC_STATE_BLEND_CONSTANTS: states.push_back(api::dynamic_state::blend_constant); break; + case VK_DYNAMIC_STATE_DEPTH_BOUNDS: + break; case VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK: states.push_back(api::dynamic_state::front_stencil_read_mask); states.push_back(api::dynamic_state::back_stencil_read_mask); @@ -1751,6 +1756,9 @@ std::vector reshade::vulkan::convert_dynamic_states case VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY: states.push_back(api::dynamic_state::primitive_topology); break; + case VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE: + states.push_back(api::dynamic_state::input_element_stride); + break; case VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE: states.push_back(api::dynamic_state::depth_enable); break; @@ -1760,6 +1768,8 @@ std::vector reshade::vulkan::convert_dynamic_states case VK_DYNAMIC_STATE_DEPTH_COMPARE_OP: states.push_back(api::dynamic_state::depth_func); break; + case VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE: + break; case VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE: states.push_back(api::dynamic_state::stencil_enable); break; @@ -1767,15 +1777,21 @@ std::vector reshade::vulkan::convert_dynamic_states states.push_back(api::dynamic_state::front_stencil_func); states.push_back(api::dynamic_state::back_stencil_func); break; + case VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE: + break; case VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE: states.push_back(api::dynamic_state::depth_bias); states.push_back(api::dynamic_state::depth_bias_clamp); states.push_back(api::dynamic_state::depth_bias_slope_scaled); break; + case VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE: + break; #if VK_EXT_extended_dynamic_state2 case VK_DYNAMIC_STATE_LOGIC_OP_EXT: states.push_back(api::dynamic_state::logic_op); break; + case VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT: + break; case VK_DYNAMIC_STATE_POLYGON_MODE_EXT: states.push_back(api::dynamic_state::fill_mode); break; From 99e711a30e544e8736b5d68cae654830741cb513 Mon Sep 17 00:00:00 2001 From: crosire Date: Sat, 1 Aug 2026 16:07:26 +0200 Subject: [PATCH 18/26] Fix crash when unloading effect that failed to compile during pipeline creation The call to "free_descriptor_table" in "destroy_effect" could attempt to free a descriptor table that was already freed in the failure cause in "create_effect" --- source/runtime.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/runtime.cpp b/source/runtime.cpp index 6e3b6ac80c..9ad4b62eaa 100644 --- a/source/runtime.cpp +++ b/source/runtime.cpp @@ -2826,8 +2826,7 @@ bool reshade::runtime::create_effect(size_t effect_index, size_t permutation_ind return true; exit_failure: - _device->free_descriptor_tables(static_cast(shader_resource_view_tables.size()), shader_resource_view_tables.data()); - _device->free_descriptor_tables(static_cast(unordered_access_view_tables.size()), unordered_access_view_tables.data()); + destroy_effect(effect_index, false); return false; } From a19814ffa900ac13d4f50beccd9053936d2c6339 Mon Sep 17 00:00:00 2001 From: crosire Date: Sun, 2 Aug 2026 03:33:39 +0200 Subject: [PATCH 19/26] Remove double literal parsing --- source/effect_lexer.cpp | 62 +++++++++++++++--------------------- source/effect_parser_exp.cpp | 7 ---- source/effect_token.hpp | 2 -- source/imgui_code_editor.cpp | 1 - 4 files changed, 26 insertions(+), 46 deletions(-) diff --git a/source/effect_lexer.cpp b/source/effect_lexer.cpp index 9c9db86e70..1371f19f52 100644 --- a/source/effect_lexer.cpp +++ b/source/effect_lexer.cpp @@ -96,7 +96,6 @@ static const std::unordered_map s_token_lookup = { { tokenid::int_literal, "integral literal" }, { tokenid::uint_literal, "integral literal" }, { tokenid::float_literal, "floating point literal" }, - { tokenid::double_literal, "floating point literal" }, { tokenid::string_literal, "string literal" }, { tokenid::namespace_, "namespace" }, { tokenid::struct_, "struct" }, @@ -576,7 +575,7 @@ reshadefx::token reshadefx::lexer::lex() tok.location = _cur_location; tok.offset = input_offset(); tok.length = 1; - tok.literal_as_double = 0; + tok.literal_as_uint = 0; tok.literal_as_string.clear(); assert(_cur <= _end); @@ -1064,7 +1063,7 @@ void reshadefx::lexer::parse_numeric_literal(token &tok) const // This routine handles both integer and floating point numbers auto *const begin = _cur, *end = _cur; int mantissa_size = 0, decimal_location = -1, radix = 10; - long long fraction = 0, exponent = 0; + long long mantissa = 0, exponent = 0; // If a literal starts with '0' it is either an octal or hexadecimal ('0x') value if (begin[0] == '0') @@ -1109,16 +1108,15 @@ void reshadefx::lexer::parse_numeric_literal(token &tok) const // Found a decimal character, as such convert current values if (radix == 8) { + mantissa = octal_to_decimal(mantissa); radix = 10; - fraction = octal_to_decimal(fraction); } decimal_location = mantissa_size; continue; } - fraction *= radix; - fraction += c; + mantissa = mantissa * radix + c; } // Ignore additional digits that cannot affect the value @@ -1129,7 +1127,6 @@ void reshadefx::lexer::parse_numeric_literal(token &tok) const if (decimal_location < 0) { tok.id = tokenid::int_literal; - decimal_location = mantissa_size; } else { @@ -1141,50 +1138,51 @@ void reshadefx::lexer::parse_numeric_literal(token &tok) const if (*end == 'E' || *end == 'e') { auto tmp = end + 1; - const bool negative = *tmp == '-'; - if (negative || *tmp == '+') + const bool exponent_negative = *tmp == '-'; + if (exponent_negative || *tmp == '+') tmp++; if (is_decimal_digit(*tmp)) { - end = tmp; - - tok.id = tokenid::float_literal; - do { - exponent *= 10; - exponent += (*end++) - '0'; - } while (is_decimal_digit(*end)); + exponent = exponent * 10 + (*tmp++) - '0'; + } while (is_decimal_digit(*tmp)); - if (negative) + if (exponent_negative) exponent = -exponent; + + end = tmp; + tok.id = tokenid::float_literal; } } // Various suffixes force specific literal types - if (*end == 'F' || *end == 'f') + if (decimal_location < 0 && (*end == 'U' || *end == 'u')) // The 'u' suffix is only valid on integers and needs to be ignored otherwise { end++; // Consume the suffix - tok.id = tokenid::float_literal; + tok.id = tokenid::uint_literal; } - else if (*end == 'L' || *end == 'l') + else if (*end == 'F' || *end == 'f' || *end == 'L' || *end == 'l') { end++; // Consume the suffix - tok.id = tokenid::double_literal; + tok.id = tokenid::float_literal; } - else if (tok.id == tokenid::int_literal && (*end == 'U' || *end == 'u')) // The 'u' suffix is only valid on integers and needs to be ignored otherwise + + if (mantissa == 0) { - end++; // Consume the suffix - tok.id = tokenid::uint_literal; + tok.literal_as_uint = 0; } - - if (tok.id == tokenid::float_literal || tok.id == tokenid::double_literal) + else if (decimal_location < 0) + { + // Limit the maximum value to what fits into our token structure + tok.literal_as_uint = static_cast(mantissa & 0xFFFFFFFF); + } + else { exponent += decimal_location - mantissa_size; const bool exponent_negative = exponent < 0; - if (exponent_negative) exponent = -exponent; @@ -1210,15 +1208,7 @@ void reshadefx::lexer::parse_numeric_literal(token &tok) const if (exponent & 1) e *= *d; - if (tok.id == tokenid::float_literal) - tok.literal_as_float = exponent_negative ? fraction / static_cast(e) : fraction * static_cast(e); - else - tok.literal_as_double = exponent_negative ? fraction / e : fraction * e; - } - else - { - // Limit the maximum value to what fits into our token structure - tok.literal_as_uint = static_cast(fraction & 0xFFFFFFFF); + tok.literal_as_float = exponent_negative ? mantissa / static_cast(e) : mantissa * static_cast(e); } tok.length = end - begin; diff --git a/source/effect_parser_exp.cpp b/source/effect_parser_exp.cpp index f8ff56ffa8..333c5891f7 100644 --- a/source/effect_parser_exp.cpp +++ b/source/effect_parser_exp.cpp @@ -852,13 +852,6 @@ bool reshadefx::parser::parse_expression_unary(expression &exp) { exp.reset_to_rvalue_constant(location, _token.literal_as_float); } - else if (accept(tokenid::double_literal)) - { - // Convert double literal to float literal for now - warning(location, 5000, "double literal truncated to float literal"); - - exp.reset_to_rvalue_constant(location, static_cast(_token.literal_as_double)); - } else if (accept(tokenid::string_literal)) { std::string value = std::move(_token.literal_as_string); diff --git a/source/effect_token.hpp b/source/effect_token.hpp index e46f82cf98..2cc3cf646b 100644 --- a/source/effect_token.hpp +++ b/source/effect_token.hpp @@ -97,7 +97,6 @@ namespace reshadefx int_literal, uint_literal, float_literal, - double_literal, string_literal, // keywords @@ -270,7 +269,6 @@ namespace reshadefx int literal_as_int; unsigned int literal_as_uint; float literal_as_float; - double literal_as_double; }; std::string literal_as_string; diff --git a/source/imgui_code_editor.cpp b/source/imgui_code_editor.cpp index 879fd9fa55..51d404c1f5 100644 --- a/source/imgui_code_editor.cpp +++ b/source/imgui_code_editor.cpp @@ -1989,7 +1989,6 @@ void reshade::imgui::code_editor::colorize() case reshadefx::tokenid::int_literal: case reshadefx::tokenid::uint_literal: case reshadefx::tokenid::float_literal: - case reshadefx::tokenid::double_literal: col = color_number_literal; break; case reshadefx::tokenid::string_literal: From b949b8a3226fe7d521791f608f116b164d873954 Mon Sep 17 00:00:00 2001 From: crosire Date: Sun, 2 Aug 2026 03:34:36 +0200 Subject: [PATCH 20/26] Replace floating-point parsing with "std::from_chars" to avoid floating-point exceptions --- source/effect_lexer.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/effect_lexer.cpp b/source/effect_lexer.cpp index 1371f19f52..77b3f3f4a1 100644 --- a/source/effect_lexer.cpp +++ b/source/effect_lexer.cpp @@ -5,6 +5,7 @@ #include "effect_lexer.hpp" #include +#include // std::from_chars #include #include // Used for static lookup tables @@ -1180,6 +1181,7 @@ void reshadefx::lexer::parse_numeric_literal(token &tok) const } else { +#if 0 exponent += decimal_location - mantissa_size; const bool exponent_negative = exponent < 0; @@ -1209,6 +1211,9 @@ void reshadefx::lexer::parse_numeric_literal(token &tok) const e *= *d; tok.literal_as_float = exponent_negative ? mantissa / static_cast(e) : mantissa * static_cast(e); +#else + std::from_chars(begin, end, tok.literal_as_float); +#endif } tok.length = end - begin; From b25b8a81d090a8a1fdaff9a60a80ee1158c34678 Mon Sep 17 00:00:00 2001 From: crosire Date: Sun, 2 Aug 2026 04:10:12 +0200 Subject: [PATCH 21/26] Add detection for small default stack size to setup tool --- setup/MainWindow.xaml.cs | 10 +++++ setup/Utilities/PEInfo.cs | 85 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/setup/MainWindow.xaml.cs b/setup/MainWindow.xaml.cs index dc74706efd..9ce8d3ddba 100644 --- a/setup/MainWindow.xaml.cs +++ b/setup/MainWindow.xaml.cs @@ -684,6 +684,16 @@ void InstallStep_AnalyzeExecutable() return; } + if (peInfo.StackSize < 1024 * 1024) + { + UpdateStatus("Waiting for user confirmation ..."); + + Dispatcher.Invoke(() => + { + MessageBox.Show(this, "The target application uses a small default stack size.\nIn order to use ReShade without crashing you'll have to patch the executable to increase the stack size from " + peInfo.StackSize + " bytes to at least 1 MB.", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning); + }); + } + if (compatibilityIni != null && compatibilityIni.HasValue(executableName, "RenderApi")) { if (compatibilityIni.HasValue(executableName, "InstallTarget")) diff --git a/setup/Utilities/PEInfo.cs b/setup/Utilities/PEInfo.cs index 4cf3846be7..7dce4a2534 100644 --- a/setup/Utilities/PEInfo.cs +++ b/setup/Utilities/PEInfo.cs @@ -39,11 +39,17 @@ struct LOADED_IMAGE public UInt32 SizeOfImage; } - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Explicit)] struct IMAGE_NT_HEADERS { + [FieldOffset(0)] public UInt32 Signature; + [FieldOffset(4)] public IMAGE_FILE_HEADER FileHeader; + [FieldOffset(24)] + public IMAGE_OPTIONAL_HEADER32 OptionalHeader32; + [FieldOffset(24)] + public IMAGE_OPTIONAL_HEADER64 OptionalHeader64; } [StructLayout(LayoutKind.Sequential)] @@ -58,6 +64,74 @@ struct IMAGE_FILE_HEADER public UInt16 Characteristics; } + [StructLayout(LayoutKind.Sequential)] + struct IMAGE_OPTIONAL_HEADER32 + { + public UInt16 Magic; + public Byte MajorLinkerVersion; + public Byte MinorLinkerVersion; + public UInt32 SizeOfCode; + public UInt32 SizeOfInitializedData; + public UInt32 SizeOfUninitializedData; + public UInt32 AddressOfEntryPoint; + public UInt32 BaseOfCode; + public UInt32 BaseOfData; + public UInt32 ImageBase; + public UInt32 SectionAlignment; + public UInt32 FileAlignment; + public UInt16 MajorOperatingSystemVersion; + public UInt16 MinorOperatingSystemVersion; + public UInt16 MajorImageVersion; + public UInt16 MinorImageVersion; + public UInt16 MajorSubsystemVersion; + public UInt16 MinorSubsystemVersion; + public UInt32 Win32VersionValue; + public UInt32 SizeOfImage; + public UInt32 SizeOfHeaders; + public UInt32 CheckSum; + public UInt16 Subsystem; + public UInt16 DllCharacteristics; + public UInt32 SizeOfStackReserve; + public UInt32 SizeOfStackCommit; + public UInt32 SizeOfHeapReserve; + public UInt32 SizeOfHeapCommit; + public UInt32 LoaderFlags; + public UInt32 NumberOfRvaAndSizes; + } + [StructLayout(LayoutKind.Sequential)] + struct IMAGE_OPTIONAL_HEADER64 + { + public UInt16 Magic; + public Byte MajorLinkerVersion; + public Byte MinorLinkerVersion; + public UInt32 SizeOfCode; + public UInt32 SizeOfInitializedData; + public UInt32 SizeOfUninitializedData; + public UInt32 AddressOfEntryPoint; + public UInt32 BaseOfCode; + public UInt64 ImageBase; + public UInt32 SectionAlignment; + public UInt32 FileAlignment; + public UInt16 MajorOperatingSystemVersion; + public UInt16 MinorOperatingSystemVersion; + public UInt16 MajorImageVersion; + public UInt16 MinorImageVersion; + public UInt16 MajorSubsystemVersion; + public UInt16 MinorSubsystemVersion; + public UInt32 Win32VersionValue; + public UInt32 SizeOfImage; + public UInt32 SizeOfHeaders; + public UInt32 CheckSum; + public UInt16 Subsystem; + public UInt16 DllCharacteristics; + public UInt64 SizeOfStackReserve; + public UInt64 SizeOfStackCommit; + public UInt64 SizeOfHeapReserve; + public UInt64 SizeOfHeapCommit; + public UInt32 LoaderFlags; + public UInt32 NumberOfRvaAndSizes; + } + [StructLayout(LayoutKind.Explicit)] struct IMAGE_IMPORT_DESCRIPTOR { @@ -131,7 +205,9 @@ public PEInfo(string path) } } - Type = ((IMAGE_NT_HEADERS*)image.FileHeader)->FileHeader.Machine; + var headers = (IMAGE_NT_HEADERS*)image.FileHeader; + Type = headers->FileHeader.Machine; + StackSize = Type == BinaryType.IMAGE_FILE_MACHINE_AMD64 ? headers->OptionalHeader64.SizeOfStackReserve : (ulong)headers->OptionalHeader32.SizeOfStackReserve; UnMapAndLoad(ref image); } @@ -149,6 +225,11 @@ public IEnumerable Modules get; } + public ulong StackSize + { + get; + } + public static string ReadResourceString(string path, ushort id) { string result = null; From 309775d932be3b01c5abff9cc58b7afe7d9d3eb1 Mon Sep 17 00:00:00 2001 From: crosire Date: Sun, 2 Aug 2026 14:50:15 +0200 Subject: [PATCH 22/26] Fix some spelling mistakes --- include/reshade.hpp | 2 +- source/addon_manager.hpp | 2 +- source/d3d9/d3d9_impl_swapchain.cpp | 2 +- source/dxgi/dxgi_swapchain.cpp | 4 ++-- source/input_windows.cpp | 2 +- source/vulkan/vulkan_hooks_command_list.cpp | 10 +++++----- source/vulkan/vulkan_hooks_device.cpp | 10 ++++++---- source/vulkan/vulkan_hooks_swapchain.cpp | 9 +++++---- 8 files changed, 22 insertions(+), 19 deletions(-) diff --git a/include/reshade.hpp b/include/reshade.hpp index fa2d57ccaa..f596db33ef 100644 --- a/include/reshade.hpp +++ b/include/reshade.hpp @@ -370,7 +370,7 @@ namespace reshade } /// - /// Creates a new effect runtime for an existing swapchain, for when it was not already hooked by ReShade (e.g. because the RESHADE_DISABLE_GRAPHICS_HOOK environment variable is set). + /// Creates a new effect runtime for an existing swap chain, for when it was not already hooked by ReShade (e.g. because the RESHADE_DISABLE_GRAPHICS_HOOK environment variable is set). /// /// Underlying graphics API used. /// 'IDirect3DDevice9', 'ID3D10Device', 'ID3D11Device', 'ID3D12Device', 'HGLRC' or 'VkDevice', depending on the graphics API. diff --git a/source/addon_manager.hpp b/source/addon_manager.hpp index 00d359fc4c..2a9ab7e489 100644 --- a/source/addon_manager.hpp +++ b/source/addon_manager.hpp @@ -174,7 +174,7 @@ namespace reshade "Event that is disabled with limited add-on support was used!"); if constexpr ( - ev != addon_event::create_resource_view) // This is needed by the Generic Depth add-on so that view creation succeeds for resources where the format was overriden + ev != addon_event::create_resource_view) // This is needed by the Generic Depth add-on so that view creation succeeds for resources where the format was overridden if (!addon_enabled) return false; #endif diff --git a/source/d3d9/d3d9_impl_swapchain.cpp b/source/d3d9/d3d9_impl_swapchain.cpp index c4b90ea485..43a4da109d 100644 --- a/source/d3d9/d3d9_impl_swapchain.cpp +++ b/source/d3d9/d3d9_impl_swapchain.cpp @@ -20,7 +20,7 @@ reshade::api::device *reshade::d3d9::swapchain_impl::get_device() void *reshade::d3d9::swapchain_impl::get_hwnd() const { - // Destination window may be temporarily overriden by 'hDestWindowOverride' parameter during present call + // Destination window may be temporarily overridden by 'hDestWindowOverride' parameter during present call if (_hwnd != nullptr) return _hwnd; diff --git a/source/dxgi/dxgi_swapchain.cpp b/source/dxgi/dxgi_swapchain.cpp index bfdc28ed7d..ce7d6acb0b 100644 --- a/source/dxgi/dxgi_swapchain.cpp +++ b/source/dxgi/dxgi_swapchain.cpp @@ -404,7 +404,7 @@ HRESULT STDMETHODCALLTYPE DXGISwapChain::ResizeBuffers(UINT BufferCount, UINT Wi _orig->GetDesc(&desc); g_in_dxgi_runtime = was_in_dxgi_runtime; - // Restore default parameters to tho original values, rather than leaving them at the last values potentially overriden by an add-on + // Restore default parameters to tho original values, rather than leaving them at the last values potentially overridden by an add-on if (0 == BufferCount) BufferCount = _orig_desc.BufferCount; if (DXGI_FORMAT_UNKNOWN == NewFormat) @@ -775,7 +775,7 @@ HRESULT STDMETHODCALLTYPE DXGISwapChain::ResizeBuffers1(UINT BufferCount, UINT W _orig->GetFullscreenState(&fullscreen, nullptr); g_in_dxgi_runtime = was_in_dxgi_runtime; - // Restore default parameters to tho original values, rather than leaving them at the last values potentially overriden by an add-on + // Restore default parameters to tho original values, rather than leaving them at the last values potentially overridden by an add-on if (0 == BufferCount) BufferCount = _orig_desc.BufferCount; if (DXGI_FORMAT_UNKNOWN == NewFormat) diff --git a/source/input_windows.cpp b/source/input_windows.cpp index 03e973d763..d022aeb44a 100644 --- a/source/input_windows.cpp +++ b/source/input_windows.cpp @@ -656,7 +656,7 @@ extern "C" BOOL WINAPI HookGetCursorPosition(LPPOINT lpPoint) { assert(lpPoint != nullptr); - // Update position in case it is not overriden via 'SetCursorPos' + // Update position in case it is not overridden via 'SetCursorPos' s_last_cursor_position = *lpPoint; } diff --git a/source/vulkan/vulkan_hooks_command_list.cpp b/source/vulkan/vulkan_hooks_command_list.cpp index 1457a35c41..2f7c1748aa 100644 --- a/source/vulkan/vulkan_hooks_command_list.cpp +++ b/source/vulkan/vulkan_hooks_command_list.cpp @@ -1202,7 +1202,7 @@ void VKAPI_CALL vkCmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContent if (cmd_impl->_is_in_render_pass & 0x80) { - // Render pass was overriden by an add-on, so there are no subpasses + // Render pass was overridden by an add-on, so there are no subpasses return; } @@ -1245,7 +1245,7 @@ void VKAPI_CALL vkCmdEndRenderPass(VkCommandBuffer commandBuffer) } if (cmd_impl->_is_in_render_pass & 0x80) { - // Render pass was overriden by an add-on, so need to end it the same way + // Render pass was overridden by an add-on, so need to end it the same way cmd_impl->end_render_pass(); return; } @@ -1343,7 +1343,7 @@ void VKAPI_CALL vkCmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpass if (cmd_impl->_is_in_render_pass & 0x80) { - // Render pass was overriden by an add-on, so there are no subpasses + // Render pass was overridden by an add-on, so there are no subpasses return; } @@ -1387,7 +1387,7 @@ void VKAPI_CALL vkCmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpa } if (cmd_impl->_is_in_render_pass & 0x80) { - // Render pass was overriden by an add-on, so need to end it the same way + // Render pass was overridden by an add-on, so need to end it the same way cmd_impl->end_render_pass(); return; } @@ -1803,7 +1803,7 @@ void VKAPI_CALL vkCmdEndRendering(VkCommandBuffer commandBuffer) } if (cmd_impl->_is_in_render_pass & 0x80) { - // Render pass was overriden by an add-on, so need to end it the same way + // Render pass was overridden by an add-on, so need to end it the same way cmd_impl->end_render_pass(); return; } diff --git a/source/vulkan/vulkan_hooks_device.cpp b/source/vulkan/vulkan_hooks_device.cpp index 28a4d938ff..bee774e26c 100644 --- a/source/vulkan/vulkan_hooks_device.cpp +++ b/source/vulkan/vulkan_hooks_device.cpp @@ -17,7 +17,7 @@ #include // std::strcmp, std::strncmp #include // std::find_if, std::min -// Set during Vulkan device creation and presentation, to avoid hooking internal D3D devices created e.g. by NVIDIA Ansel, Optimus or layered DXGI swapchain +// Set during Vulkan device creation and presentation, to avoid hooking internal D3D devices created e.g. by NVIDIA Ansel, Optimus or layered DXGI swap chain extern thread_local bool g_in_dxgi_runtime; extern lockfree_linear_map g_vulkan_instances; @@ -1260,13 +1260,15 @@ VkResult VKAPI_CALL vkCreateImage(VkDevice device, const VkImageCreateInfo *pCre reshade::vulkan::convert_resource_desc(desc, create_info); pCreateInfo = &create_info; - // Remove format list info if format was overriden + // Remove format list info if format was overridden if (const auto existing_format_list_info = find_in_structure_chain( create_info.pNext, VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO)) { - if (std::find(existing_format_list_info->pViewFormats, existing_format_list_info->pViewFormats + existing_format_list_info->viewFormatCount, create_info.format) == (existing_format_list_info->pViewFormats + existing_format_list_info->viewFormatCount)) - // This is evil, because writing into application memory, but it is what it is + if (const VkFormat *const formats_begin = existing_format_list_info->pViewFormats, *const formats_end = existing_format_list_info->pViewFormats + existing_format_list_info->viewFormatCount; + std::find(formats_begin, formats_end, create_info.format) == formats_end) + { const_cast(existing_format_list_info)->viewFormatCount = 0; + } } } #endif diff --git a/source/vulkan/vulkan_hooks_swapchain.cpp b/source/vulkan/vulkan_hooks_swapchain.cpp index 93446343e5..96da1efe4f 100644 --- a/source/vulkan/vulkan_hooks_swapchain.cpp +++ b/source/vulkan/vulkan_hooks_swapchain.cpp @@ -67,7 +67,6 @@ VkResult VKAPI_CALL vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreat std::sort(format_list.begin(), format_list.end()); format_list.erase(std::unique(format_list.begin(), format_list.end()), format_list.end()); - // This is evil, because writing into application memory, but eh =) const_cast(existing_format_list_info)->viewFormatCount = static_cast(format_list.size()); const_cast(existing_format_list_info)->pViewFormats = format_list.data(); } @@ -257,13 +256,15 @@ VkResult VKAPI_CALL vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreat if (desc.sync_interval == 0) create_info.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; - // Remove format list info if format was overriden + // Remove format list info if format was overridden if (const auto existing_format_list_info = find_in_structure_chain( create_info.pNext, VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO)) { - if (std::find(existing_format_list_info->pViewFormats, existing_format_list_info->pViewFormats + existing_format_list_info->viewFormatCount, create_info.imageFormat) == (existing_format_list_info->pViewFormats + existing_format_list_info->viewFormatCount)) - // This is evil, because potentially writing into application memory, but it is what it is + if (const VkFormat *const formats_begin = existing_format_list_info->pViewFormats, *const formats_end = existing_format_list_info->pViewFormats + existing_format_list_info->viewFormatCount; + std::find(formats_begin, formats_end, create_info.imageFormat) == formats_end) + { const_cast(existing_format_list_info)->viewFormatCount = 0; + } } } #endif From 604106de23a0d904d4a4be6db29657945c6787c9 Mon Sep 17 00:00:00 2001 From: crosire Date: Sun, 2 Aug 2026 14:52:47 +0200 Subject: [PATCH 23/26] Clean up Vulkan extension features code --- source/vulkan/vulkan_hooks.hpp | 10 +- source/vulkan/vulkan_hooks_device.cpp | 400 ++++++++------------- source/vulkan/vulkan_hooks_instance.cpp | 40 +-- source/vulkan/vulkan_impl_device.cpp | 1 - source/vulkan/vulkan_impl_type_convert.cpp | 2 +- source/vulkan/vulkan_impl_type_convert.hpp | 9 + 6 files changed, 188 insertions(+), 274 deletions(-) diff --git a/source/vulkan/vulkan_hooks.hpp b/source/vulkan/vulkan_hooks.hpp index ae5f42ef54..6e42fdb7e8 100644 --- a/source/vulkan/vulkan_hooks.hpp +++ b/source/vulkan/vulkan_hooks.hpp @@ -21,13 +21,11 @@ inline T *find_layer_info(const void *structure_chain, VkStructureType type, VkL next = reinterpret_cast(const_cast(next->pNext)); return next; } -template -inline const T *find_in_structure_chain(const void *structure_chain, VkStructureType type) + +inline void append_to_structure_chain(void *structure_chain, void *new_structure) { - const T *next = reinterpret_cast(structure_chain); - while (next != nullptr && next->sType != type) - next = reinterpret_cast(next->pNext); - return next; + static_cast(new_structure)->pNext = static_cast(structure_chain)->pNext; + static_cast(structure_chain)->pNext = static_cast(new_structure); } inline void *const dispatch_key_from_handle(const void *dispatch_handle) diff --git a/source/vulkan/vulkan_hooks_device.cpp b/source/vulkan/vulkan_hooks_device.cpp index bee774e26c..7aef85f4d6 100644 --- a/source/vulkan/vulkan_hooks_device.cpp +++ b/source/vulkan/vulkan_hooks_device.cpp @@ -62,6 +62,22 @@ void destroy_default_view(reshade::vulkan::device_impl *device_impl, VkImage ima } #endif +struct VkLayerDeviceLink +{ + VkLayerDeviceLink *pNext; + PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; + PFN_vkGetDeviceProcAddr pfnNextGetDeviceProcAddr; +}; +struct VkLayerDeviceCreateInfo +{ + VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO + const void *pNext; + VkLayerFunction function; + union { + VkLayerDeviceLink *pLayerInfo; + } u; +}; + VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) { reshade::log::message(reshade::log::level::info, "Redirecting vkCreateDevice(physicalDevice = %p, pCreateInfo = %p, pAllocator = %p, pDevice = %p) ...", physicalDevice, pCreateInfo, pAllocator, pDevice); @@ -69,22 +85,6 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi assert(pCreateInfo != nullptr && pDevice != nullptr); // Look for layer link info if installed as a layer (provided by the Vulkan loader) - struct VkLayerDeviceLink - { - VkLayerDeviceLink *pNext; - PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; - PFN_vkGetDeviceProcAddr pfnNextGetDeviceProcAddr; - }; - struct VkLayerDeviceCreateInfo - { - VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - const void *pNext; - VkLayerFunction function; - union { - VkLayerDeviceLink *pLayerInfo; - } u; - }; - const auto link_info = find_layer_info(pCreateInfo->pNext, VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO, VK_LAYER_LINK_INFO); const vulkan_instance &instance = g_vulkan_instances.at(dispatch_key_from_handle(physicalDevice)); @@ -130,28 +130,6 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi if (enum_queue_families == nullptr || enum_device_extensions == nullptr) return VK_ERROR_INITIALIZATION_FAILED; - uint32_t num_queue_families = 0; - enum_queue_families(physicalDevice, &num_queue_families, nullptr); - std::vector queue_families(num_queue_families); - enum_queue_families(physicalDevice, &num_queue_families, queue_families.data()); - - uint32_t graphics_queue_family_index = std::numeric_limits::max(); - for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; ++i) - { - const uint32_t queue_family_index = pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex; - assert(queue_family_index < num_queue_families); - - // Find the first queue family which supports graphics and has at least one queue - if (pCreateInfo->pQueueCreateInfos[i].queueCount > 0 && (queue_families[queue_family_index].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) - { - if (pCreateInfo->pQueueCreateInfos[i].pQueuePriorities[0] < 1.0f) - reshade::log::message(reshade::log::level::warning, "Vulkan queue used for rendering has a low priority (%f).", pCreateInfo->pQueueCreateInfos[i].pQueuePriorities[0]); - - graphics_queue_family_index = queue_family_index; - break; - } - } - VkPhysicalDeviceFeatures enabled_features = {}; const VkPhysicalDeviceFeatures2 *const features2 = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2); @@ -165,23 +143,59 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; ++i) enabled_extensions.push_back(pCreateInfo->ppEnabledExtensionNames[i]); - bool buffer_device_address_ext = false; - bool timeline_semaphore_ext = false; - bool host_query_reset_ext = false; - bool dynamic_rendering_ext = false; - bool extended_dynamic_state_ext = false; - bool push_descriptor_ext = false; - bool host_image_copy_ext = false; - bool custom_border_color_ext = false; - bool conservative_rasterization_ext = false; - bool ray_tracing_ext = false; - bool descriptor_indexing_ext = false; + struct + { + uint32_t host_query_reset : 1; + uint32_t timeline_semaphore : 1; + uint32_t buffer_device_address : 1; + uint32_t dynamic_rendering : 1; + uint32_t extended_dynamic_state : 1; + uint32_t push_descriptor : 1; + uint32_t host_image_copy : 1; + uint32_t custom_border_color : 1; + uint32_t conservative_rasterization : 1; + uint32_t ray_tracing : 1; + uint32_t descriptor_indexing : 1; + + VkPhysicalDeviceHostQueryResetFeatures host_query_reset_features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES }; + VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore_features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES }; + VkPhysicalDeviceBufferDeviceAddressFeatures buffer_device_address_features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES }; + VkPhysicalDevicePrivateDataFeatures private_data_features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES }; + VkPhysicalDeviceDynamicRenderingFeatures dynamic_rendering_features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES }; + VkPhysicalDeviceMaintenance5Features maintenance5_features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES }; + VkPhysicalDeviceHostImageCopyFeatures host_image_copy_features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES }; + } ext = {}; + std::vector queue_families; + uint32_t graphics_queue_family_index = std::numeric_limits::max(); + + { + uint32_t num_queue_families = 0; + enum_queue_families(physicalDevice, &num_queue_families, nullptr); + queue_families.resize(num_queue_families); + enum_queue_families(physicalDevice, &num_queue_families, queue_families.data()); + + for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; ++i) + { + const uint32_t queue_family_index = pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex; + assert(queue_family_index < num_queue_families); + + // Find the first queue family which supports graphics and has at least one queue + if (pCreateInfo->pQueueCreateInfos[i].queueCount > 0 && (queue_families[queue_family_index].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) + { + if (pCreateInfo->pQueueCreateInfos[i].pQueuePriorities[0] < 1.0f) + reshade::log::message(reshade::log::level::warning, "Vulkan queue used for rendering has a low priority (%f).", pCreateInfo->pQueueCreateInfos[i].pQueuePriorities[0]); + + graphics_queue_family_index = queue_family_index; + break; + } + } + } { - uint32_t num_extensions = 0; - enum_device_extensions(physicalDevice, nullptr, &num_extensions, nullptr); - std::vector extensions(num_extensions); - enum_device_extensions(physicalDevice, nullptr, &num_extensions, extensions.data()); + uint32_t num_device_extensions = 0; + enum_device_extensions(physicalDevice, nullptr, &num_device_extensions, nullptr); + std::vector extensions(num_device_extensions); + enum_device_extensions(physicalDevice, nullptr, &num_device_extensions, extensions.data()); // Make sure the driver actually supports the requested extensions const auto add_extension = [&extensions, &enabled_extensions, &graphics_queue_family_index](const char *name, bool required) { @@ -208,7 +222,6 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi return false; }; - #pragma region Enable features and extensions // Enable features that ReShade requires enabled_features.samplerAnisotropy = VK_TRUE; enabled_features.shaderImageGatherExtended = VK_TRUE; @@ -219,17 +232,25 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi if (instance.api_version < VK_API_VERSION_1_2) { #if VK_KHR_timeline_semaphore - timeline_semaphore_ext = add_extension(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, false); + ext.timeline_semaphore = + add_extension(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, true); #endif + #if VK_EXT_host_query_reset - host_query_reset_ext = add_extension(VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, false); + ext.host_query_reset = + add_extension(VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, false); #endif } if (instance.api_version < VK_API_VERSION_1_3) { +#if VK_EXT_private_data + if (!add_extension(VK_EXT_PRIVATE_DATA_EXTENSION_NAME, true)) +#endif + return VK_ERROR_EXTENSION_NOT_PRESENT; + #if VK_KHR_dynamic_rendering - dynamic_rendering_ext = + ext.dynamic_rendering = add_extension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, false) && // Add extensions that are required by VK_KHR_dynamic_rendering when not using the core variant add_extension(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME, false) && @@ -237,24 +258,17 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi #endif #if VK_EXT_extended_dynamic_state - extended_dynamic_state_ext = add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); -#endif - -#if VK_EXT_private_data - if (!add_extension(VK_EXT_PRIVATE_DATA_EXTENSION_NAME, true)) + ext.extended_dynamic_state = + add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); #endif - return VK_ERROR_EXTENSION_NOT_PRESENT; } if (instance.api_version < VK_API_VERSION_1_4) { add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME, true); -#if VK_KHR_push_descriptor - push_descriptor_ext = add_extension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, false); -#endif #if VK_EXT_host_image_copy - host_image_copy_ext = + ext.host_image_copy = add_extension(VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME, false) && // Add extensions that are required by VK_EXT_host_image_copy when not using the core variant add_extension(VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME, false) && @@ -262,28 +276,14 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi #endif } +#if VK_KHR_push_descriptor + ext.push_descriptor = + add_extension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, false); +#endif + #if VK_KHR_external_memory_win32 add_extension(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, false); #endif -#if VK_EXT_custom_border_color - custom_border_color_ext = add_extension(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false); -#endif -#if VK_EXT_conservative_rasterization - conservative_rasterization_ext = add_extension(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME, false); -#endif - -#if 0 - ray_tracing_ext = - add_extension(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME, false) && - add_extension(VK_KHR_SPIRV_1_4_EXTENSION_NAME, false) && - add_extension(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, false) && - add_extension(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME, false) && - add_extension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, false) && - add_extension(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, false) && - add_extension(VK_KHR_RAY_TRACING_MAINTENANCE_1_EXTENSION_NAME, false); - buffer_device_address_ext = ray_tracing_ext && add_extension(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, false); -#endif - #pragma endregion // Check if the device is used for presenting if (std::find_if(enabled_extensions.cbegin(), enabled_extensions.cend(), @@ -315,181 +315,137 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi create_info.enabledExtensionCount = static_cast(enabled_extensions.size()); create_info.ppEnabledExtensionNames = enabled_extensions.data(); + VkDevicePrivateDataCreateInfo private_data_info { VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO }; + private_data_info.privateDataSlotRequestCount = 1; + append_to_structure_chain(&create_info, &private_data_info); + #pragma region Patch the enabled features // Patch the enabled features if (features2 != nullptr) - // This is evil, because overwriting application memory, but whatever (RenderDoc does this too) const_cast(features2)->features = enabled_features; else create_info.pEnabledFeatures = &enabled_features; - VkPhysicalDeviceHostQueryResetFeatures host_query_reset_features; - VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore_features; - VkPhysicalDeviceBufferDeviceAddressFeatures buffer_device_address_features; if (const auto existing_vulkan_12_features = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES)) { assert(instance.api_version >= VK_API_VERSION_1_2); - buffer_device_address_ext = existing_vulkan_12_features->bufferDeviceAddress; - + ext.host_query_reset = existing_vulkan_12_features->hostQueryReset; // Force enable timeline semaphore support (used for effect runtime present/graphics queue synchronization in case of present from compute, e.g. in Indiana Jones and the Great Circle and DOOM Eternal) - timeline_semaphore_ext = true; - const_cast(existing_vulkan_12_features)->timelineSemaphore = VK_TRUE; - - host_query_reset_ext = existing_vulkan_12_features->hostQueryReset; - descriptor_indexing_ext = existing_vulkan_12_features->descriptorIndexing; + ext.timeline_semaphore = const_cast(existing_vulkan_12_features)->timelineSemaphore = VK_TRUE; + ext.descriptor_indexing = existing_vulkan_12_features->descriptorIndexing; + ext.buffer_device_address = existing_vulkan_12_features->bufferDeviceAddress; } else { -#if VK_EXT_descriptor_indexing - if (const auto existing_descriptor_indexing_features = find_in_structure_chain( - pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES)) - { - descriptor_indexing_ext = existing_descriptor_indexing_features->descriptorBindingPartiallyBound || - existing_descriptor_indexing_features->descriptorBindingUniformBufferUpdateAfterBind || - existing_descriptor_indexing_features->descriptorBindingSampledImageUpdateAfterBind || - existing_descriptor_indexing_features->descriptorBindingStorageImageUpdateAfterBind || - existing_descriptor_indexing_features->descriptorBindingStorageBufferUpdateAfterBind || - existing_descriptor_indexing_features->descriptorBindingUniformTexelBufferUpdateAfterBind || - existing_descriptor_indexing_features->descriptorBindingStorageTexelBufferUpdateAfterBind || - existing_descriptor_indexing_features->descriptorBindingUpdateUnusedWhilePending || - existing_descriptor_indexing_features->descriptorBindingVariableDescriptorCount || - existing_descriptor_indexing_features->runtimeDescriptorArray; - } -#endif - - if (const auto existing_buffer_device_address_features = find_in_structure_chain( - pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES)) + if (const auto existing_host_query_reset_features = find_in_structure_chain( + pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES)) { - buffer_device_address_ext = existing_buffer_device_address_features->bufferDeviceAddress; + ext.host_query_reset = existing_host_query_reset_features->hostQueryReset; } - else if (buffer_device_address_ext) + else if (ext.host_query_reset) { - buffer_device_address_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES, const_cast(create_info.pNext) }; - buffer_device_address_features.bufferDeviceAddress = VK_TRUE; - - create_info.pNext = &buffer_device_address_features; + append_to_structure_chain(&create_info, &ext.host_query_reset_features); + ext.host_query_reset_features.hostQueryReset = VK_TRUE; } if (const auto existing_timeline_semaphore_features = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES)) { - timeline_semaphore_ext = true; - const_cast(existing_timeline_semaphore_features)->timelineSemaphore = VK_TRUE; + ext.timeline_semaphore = const_cast(existing_timeline_semaphore_features)->timelineSemaphore = VK_TRUE; } - else if (timeline_semaphore_ext) + else if (ext.timeline_semaphore) { - timeline_semaphore_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, const_cast(create_info.pNext) }; - timeline_semaphore_features.timelineSemaphore = VK_TRUE; - - create_info.pNext = &timeline_semaphore_features; + append_to_structure_chain(&create_info, &ext.timeline_semaphore_features); + ext.timeline_semaphore_features.timelineSemaphore = VK_TRUE; } - if (const auto existing_host_query_reset_features = find_in_structure_chain( - pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES)) + if (const auto existing_buffer_device_address_features = find_in_structure_chain( + pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES)) { - host_query_reset_ext = existing_host_query_reset_features->hostQueryReset; + ext.buffer_device_address = existing_buffer_device_address_features->bufferDeviceAddress; } - else if (host_query_reset_ext) + else if (ext.buffer_device_address) { - host_query_reset_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES, const_cast(create_info.pNext) }; - host_query_reset_features.hostQueryReset = VK_TRUE; + append_to_structure_chain(&create_info, &ext.buffer_device_address_features); + ext.buffer_device_address_features.bufferDeviceAddress = VK_TRUE; + } - create_info.pNext = &host_query_reset_features; + if (const auto existing_descriptor_indexing_features = find_in_structure_chain( + pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES)) + { + ext.descriptor_indexing = existing_descriptor_indexing_features->descriptorBindingPartiallyBound || existing_descriptor_indexing_features->descriptorBindingUniformBufferUpdateAfterBind || existing_descriptor_indexing_features->descriptorBindingSampledImageUpdateAfterBind || existing_descriptor_indexing_features->descriptorBindingStorageImageUpdateAfterBind || existing_descriptor_indexing_features->descriptorBindingStorageBufferUpdateAfterBind || existing_descriptor_indexing_features->descriptorBindingUniformTexelBufferUpdateAfterBind || existing_descriptor_indexing_features->descriptorBindingStorageTexelBufferUpdateAfterBind || existing_descriptor_indexing_features->descriptorBindingUpdateUnusedWhilePending || existing_descriptor_indexing_features->descriptorBindingVariableDescriptorCount || existing_descriptor_indexing_features->runtimeDescriptorArray ? 1 : 0; } } - // Enable private data feature - VkDevicePrivateDataCreateInfo private_data_info { VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO, create_info.pNext }; - private_data_info.privateDataSlotRequestCount = 1; - - VkPhysicalDevicePrivateDataFeatures private_data_features; - VkPhysicalDeviceDynamicRenderingFeatures dynamic_rendering_features; if (const auto existing_vulkan_13_features = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES)) { assert(instance.api_version >= VK_API_VERSION_1_3); - create_info.pNext = &private_data_info; - - dynamic_rendering_ext = existing_vulkan_13_features->dynamicRendering; - - // Forcefully enable private data in Vulkan 1.3, again, evil =) const_cast(existing_vulkan_13_features)->privateData = VK_TRUE; + + ext.dynamic_rendering = existing_vulkan_13_features->dynamicRendering; } else { if (const auto existing_private_data_features = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES)) { - create_info.pNext = &private_data_info; - const_cast(existing_private_data_features)->privateData = VK_TRUE; } else { - private_data_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES, &private_data_info }; - private_data_features.privateData = VK_TRUE; - - create_info.pNext = &private_data_features; + append_to_structure_chain(&create_info, &ext.private_data_features); + ext.private_data_features.privateData = VK_TRUE; } if (const auto existing_dynamic_rendering_features = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES)) { - dynamic_rendering_ext = existing_dynamic_rendering_features->dynamicRendering; + ext.dynamic_rendering = existing_dynamic_rendering_features->dynamicRendering; } - else if (dynamic_rendering_ext) + else if (ext.dynamic_rendering) { - dynamic_rendering_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, const_cast(create_info.pNext) }; - dynamic_rendering_features.dynamicRendering = VK_TRUE; - - create_info.pNext = &dynamic_rendering_features; + append_to_structure_chain(&create_info, &ext.dynamic_rendering_features); + ext.dynamic_rendering_features.dynamicRendering = VK_TRUE; } } - VkPhysicalDeviceHostImageCopyFeatures host_image_copy_features; - VkPhysicalDeviceVulkan14Features vulkan_14_features; - if (instance.api_version >= VK_API_VERSION_1_4) - { - VkPhysicalDeviceVulkan14Features supported_vulkan_14_features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES }; - VkPhysicalDeviceFeatures2 supported_features { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, &supported_vulkan_14_features }; - instance.dispatch_table.GetPhysicalDeviceFeatures2(physicalDevice, &supported_features); - - push_descriptor_ext = supported_vulkan_14_features.pushDescriptor; - } if (const auto existing_vulkan_14_features = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES)) { assert(instance.api_version >= VK_API_VERSION_1_4); - if (push_descriptor_ext) { + const_cast(existing_vulkan_14_features)->maintenance5 = VK_TRUE; + if (ext.push_descriptor) const_cast(existing_vulkan_14_features)->pushDescriptor = VK_TRUE; - } - host_image_copy_ext = existing_vulkan_14_features->hostImageCopy; + + ext.host_image_copy = existing_vulkan_14_features->hostImageCopy; } else { - if (instance.api_version >= VK_API_VERSION_1_4 && push_descriptor_ext) + if (const auto existing_maintenance5_features = find_in_structure_chain( + pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES)) { - vulkan_14_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES, const_cast(create_info.pNext) }; - vulkan_14_features.pushDescriptor = VK_TRUE; - - create_info.pNext = &vulkan_14_features; + const_cast(existing_maintenance5_features)->maintenance5 = VK_TRUE; + } + else + { + append_to_structure_chain(&create_info, &ext.maintenance5_features); + ext.maintenance5_features.maintenance5 = VK_TRUE; } if (const auto existing_host_image_copy_features = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES)) { - host_image_copy_ext = existing_host_image_copy_features->hostImageCopy; + ext.host_image_copy = existing_host_image_copy_features->hostImageCopy; } - else if (host_image_copy_ext) + else if (ext.host_image_copy) { - host_image_copy_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES, const_cast(create_info.pNext) }; - host_image_copy_features.hostImageCopy = VK_TRUE; - - create_info.pNext = &host_image_copy_features; + append_to_structure_chain(&create_info, &ext.host_image_copy_features); + ext.host_image_copy_features.hostImageCopy = VK_TRUE; } } @@ -502,76 +458,28 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi } #if VK_EXT_custom_border_color - // Optionally enable custom border color feature - VkPhysicalDeviceCustomBorderColorFeaturesEXT custom_border_features; if (const auto existing_custom_border_features = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT)) { - custom_border_color_ext = existing_custom_border_features->customBorderColors; - } - else if (custom_border_color_ext) - { - custom_border_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT, const_cast(create_info.pNext) }; - custom_border_features.customBorderColors = VK_TRUE; - custom_border_features.customBorderColorWithoutFormat = VK_TRUE; - - create_info.pNext = &custom_border_features; + ext.custom_border_color = existing_custom_border_features->customBorderColors; } #endif #if VK_EXT_extended_dynamic_state - // Optionally enable extended dynamic state feature - VkPhysicalDeviceExtendedDynamicStateFeaturesEXT extended_dynamic_state_features; if (const auto existing_extended_dynamic_state_features = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT)) { - extended_dynamic_state_ext = existing_extended_dynamic_state_features->extendedDynamicState; - } - else if (extended_dynamic_state_ext) - { - extended_dynamic_state_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT, const_cast(create_info.pNext) }; - extended_dynamic_state_features.extendedDynamicState = VK_TRUE; - - create_info.pNext = &extended_dynamic_state_features; + ext.extended_dynamic_state = existing_extended_dynamic_state_features->extendedDynamicState; } #endif #if VK_KHR_acceleration_structure && VK_KHR_ray_tracing_pipeline - // Optionally enable ray tracing feature - VkPhysicalDeviceRayTracingPipelineFeaturesKHR ray_tracing_features; - VkPhysicalDeviceAccelerationStructureFeaturesKHR acceleration_structure_features; if (const auto existing_ray_tracing_features = find_in_structure_chain( pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR)) { - ray_tracing_ext = existing_ray_tracing_features->rayTracingPipeline; - } - else if (ray_tracing_ext) - { - ray_tracing_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR, const_cast(create_info.pNext) }; - ray_tracing_features.rayTracingPipeline = VK_TRUE; - - create_info.pNext = &ray_tracing_features; - - acceleration_structure_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR, const_cast(create_info.pNext) }; - acceleration_structure_features.accelerationStructure = VK_TRUE; - - create_info.pNext = &acceleration_structure_features; + ext.ray_tracing = existing_ray_tracing_features->rayTracingPipeline; } #endif - - VkPhysicalDeviceMaintenance5Features maintenance5_features; - if (const auto existing_maintenance5_features = find_in_structure_chain( - pCreateInfo->pNext, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES)) - { - const_cast(existing_maintenance5_features)->maintenance5 = VK_TRUE; - } - else - { - maintenance5_features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES, const_cast(create_info.pNext) }; - maintenance5_features.maintenance5 = true; - - create_info.pNext = &maintenance5_features; - } #pragma endregion // Continue calling down the chain @@ -635,40 +543,40 @@ VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDevi #pragma region Initialize features and extensions #if VK_KHR_buffer_device_address - device.dispatch_table.KHR_buffer_device_address &= buffer_device_address_ext ? 1 : 0; + device.dispatch_table.KHR_buffer_device_address &= ext.buffer_device_address; #endif #if VK_KHR_timeline_semaphore - device.dispatch_table.KHR_timeline_semaphore &= timeline_semaphore_ext ? 1 : 0; + device.dispatch_table.KHR_timeline_semaphore &= ext.timeline_semaphore; #endif #if VK_KHR_dynamic_rendering - device.dispatch_table.KHR_dynamic_rendering &= dynamic_rendering_ext ? 1 : 0; + device.dispatch_table.KHR_dynamic_rendering &= ext.dynamic_rendering; #endif #if VK_KHR_push_descriptor - device.dispatch_table.KHR_push_descriptor &= push_descriptor_ext ? 1 : 0; + device.dispatch_table.KHR_push_descriptor &= ext.push_descriptor; #endif #if VK_KHR_ray_tracing_pipeline - device.dispatch_table.KHR_ray_tracing_pipeline &= ray_tracing_ext ? 1 : 0; + device.dispatch_table.KHR_ray_tracing_pipeline &= ext.ray_tracing; #endif #if VK_KHR_acceleration_structure - device.dispatch_table.KHR_acceleration_structure &= ray_tracing_ext ? 1 : 0; + device.dispatch_table.KHR_acceleration_structure &= ext.ray_tracing; #endif #if VK_EXT_host_query_reset - device.dispatch_table.EXT_host_query_reset &= host_query_reset_ext ? 1 : 0; + device.dispatch_table.EXT_host_query_reset &= ext.host_query_reset; #endif #if VK_EXT_extended_dynamic_state - device.dispatch_table.EXT_extended_dynamic_state &= extended_dynamic_state_ext ? 1 : 0; + device.dispatch_table.EXT_extended_dynamic_state &= ext.extended_dynamic_state; #endif #if VK_EXT_host_image_copy - device.dispatch_table.EXT_host_image_copy &= host_image_copy_ext ? 1 : 0; + device.dispatch_table.EXT_host_image_copy &= ext.host_image_copy; #endif #if VK_EXT_custom_border_color - device.dispatch_table.EXT_custom_border_color &= custom_border_color_ext ? 1 : 0; + device.dispatch_table.EXT_custom_border_color &= ext.custom_border_color; #endif #if VK_EXT_conservative_rasterization - device.dispatch_table.EXT_conservative_rasterization &= conservative_rasterization_ext ? 1 : 0; + device.dispatch_table.EXT_conservative_rasterization &= ext.conservative_rasterization; #endif #if VK_EXT_descriptor_indexing - device.dispatch_table.EXT_descriptor_indexing &= descriptor_indexing_ext ? 1 : 0; + device.dispatch_table.EXT_descriptor_indexing &= ext.descriptor_indexing; #endif if (instance.api_version < VK_API_VERSION_1_2) diff --git a/source/vulkan/vulkan_hooks_instance.cpp b/source/vulkan/vulkan_hooks_instance.cpp index 5d939f89df..313ba93e69 100644 --- a/source/vulkan/vulkan_hooks_instance.cpp +++ b/source/vulkan/vulkan_hooks_instance.cpp @@ -16,6 +16,22 @@ lockfree_linear_map g_vulkan_surfaces; lockfree_linear_map g_vulkan_instances; +struct VkLayerInstanceLink +{ + VkLayerInstanceLink *pNext; + PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; + PFN_vkGetInstanceProcAddr pfnNextGetPhysicalDeviceProcAddr; +}; +struct VkLayerInstanceCreateInfo +{ + VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO + const void *pNext; + VkLayerFunction function; + union { + VkLayerInstanceLink *pLayerInfo; + } u; +}; + VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) { reshade::log::message(reshade::log::level::info, "Redirecting vkCreateInstance(pCreateInfo = %p, pAllocator = %p, pInstance = %p) ...", pCreateInfo, pAllocator, pInstance); @@ -23,22 +39,6 @@ VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo, co assert(pCreateInfo != nullptr && pInstance != nullptr); // Look for layer link info if installed as a layer (provided by the Vulkan loader) - struct VkLayerInstanceLink - { - VkLayerInstanceLink *pNext; - PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; - PFN_vkGetInstanceProcAddr pfnNextGetPhysicalDeviceProcAddr; - }; - struct VkLayerInstanceCreateInfo - { - VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO - const void *pNext; - VkLayerFunction function; - union { - VkLayerInstanceLink *pLayerInfo; - } u; - }; - const auto link_info = find_layer_info(pCreateInfo->pNext, VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO, VK_LAYER_LINK_INFO); // Get trampoline function pointers @@ -123,10 +123,10 @@ VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo, co enabled_extensions.push_back(pCreateInfo->ppEnabledExtensionNames[i]); { - uint32_t num_extensions = 0; - enum_instance_extensions(nullptr, &num_extensions, nullptr); - std::vector extensions(num_extensions); - enum_instance_extensions(nullptr, &num_extensions, extensions.data()); + uint32_t num_instance_extensions = 0; + enum_instance_extensions(nullptr, &num_instance_extensions, nullptr); + std::vector extensions(num_instance_extensions); + enum_instance_extensions(nullptr, &num_instance_extensions, extensions.data()); // Make sure the driver actually supports the requested extensions const auto add_extension = [&extensions, &enabled_extensions](const char *name, bool required) { diff --git a/source/vulkan/vulkan_impl_device.cpp b/source/vulkan/vulkan_impl_device.cpp index 2e881a1c1d..2995061fc8 100644 --- a/source/vulkan/vulkan_impl_device.cpp +++ b/source/vulkan/vulkan_impl_device.cpp @@ -4,7 +4,6 @@ */ #include "vulkan_impl_device.hpp" -#include "vulkan_hooks.hpp" #include "vulkan_impl_command_queue.hpp" #include "vulkan_impl_type_convert.hpp" #include "dll_log.hpp" diff --git a/source/vulkan/vulkan_impl_type_convert.cpp b/source/vulkan/vulkan_impl_type_convert.cpp index 515e338632..3004e2167e 100644 --- a/source/vulkan/vulkan_impl_type_convert.cpp +++ b/source/vulkan/vulkan_impl_type_convert.cpp @@ -3,8 +3,8 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include "vulkan_hooks.hpp" #include "vulkan_impl_type_convert.hpp" +#include #include // std::copy_n, std::fill_n, std::find_if, std::max auto reshade::vulkan::convert_format(api::format format, VkComponentMapping *components) -> VkFormat diff --git a/source/vulkan/vulkan_impl_type_convert.hpp b/source/vulkan/vulkan_impl_type_convert.hpp index 37ab24487e..056fd3f24b 100644 --- a/source/vulkan/vulkan_impl_type_convert.hpp +++ b/source/vulkan/vulkan_impl_type_convert.hpp @@ -16,6 +16,15 @@ using VmaAllocation = void *; using VmaPool = void *; #endif +template +inline const T *find_in_structure_chain(const void *structure_chain, VkStructureType type) +{ + const T *next = reinterpret_cast(structure_chain); + while (next != nullptr && next->sType != type) + next = reinterpret_cast(next->pNext); + return next; +} + namespace reshade::vulkan { static_assert(sizeof(VkBuffer) == sizeof(api::resource)); From bedcedbd775c92631e3b68b52ea10a1382b262a0 Mon Sep 17 00:00:00 2001 From: crosire Date: Sun, 2 Aug 2026 15:03:23 +0200 Subject: [PATCH 24/26] Show "Choose last clear operation with high number of draw calls" option when there are multiple items See #431 --- examples/09-depth/generic_depth_addon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/09-depth/generic_depth_addon.cpp b/examples/09-depth/generic_depth_addon.cpp index 952164296a..5e3d4e3d7a 100644 --- a/examples/09-depth/generic_depth_addon.cpp +++ b/examples/09-depth/generic_depth_addon.cpp @@ -1363,7 +1363,7 @@ static void draw_settings_overlay(effect_runtime *runtime) clear_stats.clear_op == clear_op::fullscreen_draw ? " Fullscreen draw call" : ""); } - if (sorted_item_list.size() == 1 && !is_d3d12_or_vulkan) + if (!is_d3d12_or_vulkan) { if (bool value = (depth_stencil_backup->force_clear_index == std::numeric_limits::max()); ImGui::Checkbox(" Choose last clear operation with high number of draw calls", &value)) From 18deaa52de0c425a78b329e9cb3c497281cd00ec Mon Sep 17 00:00:00 2001 From: crosire Date: Sun, 2 Aug 2026 18:05:34 +0200 Subject: [PATCH 25/26] Fix parsing of floating-point literals with an exponent This was a regression introduced in commit a19814ffa900ac13d4f50beccd9053936d2c6339 --- source/effect_lexer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/effect_lexer.cpp b/source/effect_lexer.cpp index 77b3f3f4a1..7d6069ebf7 100644 --- a/source/effect_lexer.cpp +++ b/source/effect_lexer.cpp @@ -1159,7 +1159,7 @@ void reshadefx::lexer::parse_numeric_literal(token &tok) const } // Various suffixes force specific literal types - if (decimal_location < 0 && (*end == 'U' || *end == 'u')) // The 'u' suffix is only valid on integers and needs to be ignored otherwise + if ((*end == 'U' || *end == 'u') && tok.id == tokenid::int_literal) // The 'u' suffix is only valid on integers and needs to be ignored otherwise { end++; // Consume the suffix tok.id = tokenid::uint_literal; @@ -1174,7 +1174,7 @@ void reshadefx::lexer::parse_numeric_literal(token &tok) const { tok.literal_as_uint = 0; } - else if (decimal_location < 0) + else if (tok.id == tokenid::int_literal || tok.id == tokenid::uint_literal) { // Limit the maximum value to what fits into our token structure tok.literal_as_uint = static_cast(mantissa & 0xFFFFFFFF); From 80eacfc989443b768dc015b608927e119f5030e9 Mon Sep 17 00:00:00 2001 From: crosire Date: Wed, 5 Aug 2026 00:35:57 +0200 Subject: [PATCH 26/26] Fix font scale always being reset to default when font size is at default --- source/runtime_gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/runtime_gui.cpp b/source/runtime_gui.cpp index 3217ae06ea..3f630d4d1e 100644 --- a/source/runtime_gui.cpp +++ b/source/runtime_gui.cpp @@ -4688,7 +4688,7 @@ void reshade::runtime::draw_code_editor(editor_instance &instance) bool reshade::runtime::init_imgui_resources() { // Adjust default font size based on the vertical resolution - if (_font_size == 13.0f) + if (_font_size == 13.0f && _imgui_context->Style.FontScaleMain == 1.0f) _imgui_context->Style.FontScaleMain = _height >= 2160 ? 2.0f : _height >= 1440 ? 1.5f : 1.0f; const bool has_combined_sampler_and_view = _device->check_capability(api::device_caps::sampler_with_resource_view);