diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 745389213e..5bb09302f0 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -6,7 +6,9 @@ #include #include +#include #include +#include #include #include @@ -805,98 +807,377 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .pAttachments = cb_attachments.data(), .blendConstants = {} }; - static_vector dynamic_states{ - VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, - VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, - VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, - VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, - VK_DYNAMIC_STATE_LINE_WIDTH, + enum class DynamicStateSubset : u8 { + VertexInput, + PreRaster, + FragmentOutput, }; + + static_vector dynamic_states; + static_vector vertex_input_dynamic_states; + static_vector pre_raster_dynamic_states; + static_vector fragment_output_dynamic_states; + + const auto add_dynamic_state = [&](VkDynamicState state, DynamicStateSubset subset) { + dynamic_states.push_back(state); + switch (subset) { + case DynamicStateSubset::VertexInput: + vertex_input_dynamic_states.push_back(state); + break; + case DynamicStateSubset::PreRaster: + pre_raster_dynamic_states.push_back(state); + break; + case DynamicStateSubset::FragmentOutput: + fragment_output_dynamic_states.push_back(state); + break; + } + }; + + add_dynamic_state(VK_DYNAMIC_STATE_VIEWPORT, DynamicStateSubset::PreRaster); + add_dynamic_state(VK_DYNAMIC_STATE_SCISSOR, DynamicStateSubset::PreRaster); + add_dynamic_state(VK_DYNAMIC_STATE_DEPTH_BIAS, DynamicStateSubset::PreRaster); + add_dynamic_state(VK_DYNAMIC_STATE_BLEND_CONSTANTS, DynamicStateSubset::FragmentOutput); + add_dynamic_state(VK_DYNAMIC_STATE_DEPTH_BOUNDS, DynamicStateSubset::FragmentOutput); + add_dynamic_state(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, DynamicStateSubset::FragmentOutput); + add_dynamic_state(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, DynamicStateSubset::FragmentOutput); + add_dynamic_state(VK_DYNAMIC_STATE_STENCIL_REFERENCE, DynamicStateSubset::FragmentOutput); + add_dynamic_state(VK_DYNAMIC_STATE_LINE_WIDTH, DynamicStateSubset::PreRaster); + if (key.state.extended_dynamic_state) { static constexpr std::array extended{ - VK_DYNAMIC_STATE_CULL_MODE_EXT, - VK_DYNAMIC_STATE_FRONT_FACE_EXT, - //VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT, //Disabled for VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME - VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, - VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, - VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, - VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT, - VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT, - VK_DYNAMIC_STATE_STENCIL_OP_EXT, + std::pair{VK_DYNAMIC_STATE_CULL_MODE_EXT, DynamicStateSubset::PreRaster}, + std::pair{VK_DYNAMIC_STATE_FRONT_FACE_EXT, DynamicStateSubset::PreRaster}, + std::pair{VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, DynamicStateSubset::FragmentOutput}, + std::pair{VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, DynamicStateSubset::FragmentOutput}, + std::pair{VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, DynamicStateSubset::FragmentOutput}, + std::pair{VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT, + DynamicStateSubset::FragmentOutput}, + std::pair{VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT, DynamicStateSubset::FragmentOutput}, + std::pair{VK_DYNAMIC_STATE_STENCIL_OP_EXT, DynamicStateSubset::FragmentOutput}, }; if (key.state.dynamic_vertex_input) { - dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT); + add_dynamic_state(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT, DynamicStateSubset::VertexInput); + } + for (const auto& [state, subset] : extended) { + add_dynamic_state(state, subset); } - dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); if (key.state.extended_dynamic_state_2) { static constexpr std::array extended2{ - VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT, - VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT, - VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT, + std::pair{VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT, DynamicStateSubset::PreRaster}, + std::pair{VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT, + DynamicStateSubset::VertexInput}, + std::pair{VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT, + DynamicStateSubset::PreRaster}, }; - dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end()); + for (const auto& [state, subset] : extended2) { + add_dynamic_state(state, subset); + } } if (key.state.extended_dynamic_state_2_extra) { - dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT); + add_dynamic_state(VK_DYNAMIC_STATE_LOGIC_OP_EXT, DynamicStateSubset::FragmentOutput); } if (key.state.extended_dynamic_state_3_blend) { static constexpr std::array extended3{ - VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT, - VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT, - VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT, - - // VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT, + std::pair{VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT, + DynamicStateSubset::FragmentOutput}, + std::pair{VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT, + DynamicStateSubset::FragmentOutput}, + std::pair{VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT, + DynamicStateSubset::FragmentOutput}, }; - dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); + for (const auto& [state, subset] : extended3) { + add_dynamic_state(state, subset); + } } if (key.state.extended_dynamic_state_3_enables) { static constexpr std::array extended3{ - VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT, - VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT, - - // additional state3 extensions - VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT, - - VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT, - - VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT, - VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT, - VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT, - VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT, - VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT, + std::pair{VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT, DynamicStateSubset::PreRaster}, + std::pair{VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT, + DynamicStateSubset::FragmentOutput}, + std::pair{VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT, + DynamicStateSubset::PreRaster}, + std::pair{VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT, + DynamicStateSubset::PreRaster}, + std::pair{VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT, + DynamicStateSubset::PreRaster}, + std::pair{VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT, + DynamicStateSubset::FragmentOutput}, + std::pair{VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT, + DynamicStateSubset::FragmentOutput}, + std::pair{VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT, DynamicStateSubset::PreRaster}, + std::pair{VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT, + DynamicStateSubset::PreRaster}, }; - dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); + for (const auto& [state, subset] : extended3) { + add_dynamic_state(state, subset); + } } } - const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .dynamicStateCount = static_cast(dynamic_states.size()), - .pDynamicStates = dynamic_states.data(), + const auto make_dynamic_ci = [](const auto& states) -> VkPipelineDynamicStateCreateInfo { + return VkPipelineDynamicStateCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .dynamicStateCount = static_cast(states.size()), + .pDynamicStates = states.empty() ? nullptr : states.data(), + }; }; + + const VkPipelineDynamicStateCreateInfo dynamic_state_ci = make_dynamic_ci(dynamic_states); + [[maybe_unused]] const VkPipelineDynamicStateCreateInfo vertex_input_dynamic_state_ci = + make_dynamic_ci(vertex_input_dynamic_states); + [[maybe_unused]] const VkPipelineDynamicStateCreateInfo pre_raster_dynamic_state_ci = + make_dynamic_ci(pre_raster_dynamic_states); + [[maybe_unused]] const VkPipelineDynamicStateCreateInfo fragment_output_dynamic_state_ci = + make_dynamic_ci(fragment_output_dynamic_states); + + [[maybe_unused]] const VkPipelineDynamicStateCreateInfo* vertex_input_dynamic_state_ci_ptr = + vertex_input_dynamic_states.empty() ? nullptr : &vertex_input_dynamic_state_ci; + [[maybe_unused]] const VkPipelineDynamicStateCreateInfo* pre_raster_dynamic_state_ci_ptr = + pre_raster_dynamic_states.empty() ? nullptr : &pre_raster_dynamic_state_ci; + [[maybe_unused]] const VkPipelineDynamicStateCreateInfo* fragment_output_dynamic_state_ci_ptr = + fragment_output_dynamic_states.empty() ? nullptr : &fragment_output_dynamic_state_ci; [[maybe_unused]] const VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroup_size_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT, .pNext = nullptr, .requiredSubgroupSize = GuestWarpSize, }; static_vector shader_stages; + [[maybe_unused]] static_vector preraster_shader_stages; + [[maybe_unused]] std::optional fragment_shader_stage; for (size_t stage = 0; stage < Maxwell::MaxShaderStage; ++stage) { if (!spv_modules[stage]) { continue; } - [[maybe_unused]] auto& stage_ci = - shader_stages.emplace_back(VkPipelineShaderStageCreateInfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = MaxwellToVK::ShaderStage(Shader::StageFromIndex(stage)), - .module = *spv_modules[stage], - .pName = "main", - .pSpecializationInfo = nullptr, - }); + VkPipelineShaderStageCreateInfo stage_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = MaxwellToVK::ShaderStage(Shader::StageFromIndex(stage)), + .module = *spv_modules[stage], + .pName = "main", + .pSpecializationInfo = nullptr, + }; + shader_stages.push_back(stage_ci); + if (stage_ci.stage == VK_SHADER_STAGE_FRAGMENT_BIT) { + fragment_shader_stage = stage_ci; + } else { + preraster_shader_stages.push_back(stage_ci); + } } + +#if defined(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME) && defined(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME) + if (device.UseGraphicsPipelineLibrary()) { + for (auto& library : pipeline_libraries) { + library.reset(); + } + const bool capture_stats = + device.IsKhrPipelineExecutablePropertiesEnabled() && + Settings::values.renderer_debug.GetValue(); + const auto build_with_gpl = [&]() -> bool { + try { + VkPipelineCreateFlags library_flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR; + if (capture_stats) { + library_flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; + } +#if defined(VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT) + if (device.ShouldRetainLinkTimeOptimizationInfo()) { + library_flags |= + VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT; + } +#endif + const VkGraphicsPipelineLibraryCreateInfoEXT vertex_input_library_info{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, + .pNext = nullptr, + .flags = VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT, + }; + VkGraphicsPipelineCreateInfo vertex_input_library_ci{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = &vertex_input_library_info, + .flags = library_flags, + .stageCount = 0, + .pStages = nullptr, + .pVertexInputState = &vertex_input_ci, + .pInputAssemblyState = &input_assembly_ci, + .pTessellationState = nullptr, + .pViewportState = nullptr, + .pRasterizationState = nullptr, + .pMultisampleState = nullptr, + .pDepthStencilState = nullptr, + .pColorBlendState = nullptr, + .pDynamicState = vertex_input_dynamic_state_ci_ptr, + .layout = VK_NULL_HANDLE, + .renderPass = VK_NULL_HANDLE, + .subpass = 0, + .basePipelineHandle = nullptr, + .basePipelineIndex = 0, + }; + pipeline_libraries[0] = device.GetLogical().CreateGraphicsPipeline( + vertex_input_library_ci, *pipeline_cache); + + const VkGraphicsPipelineLibraryCreateInfoEXT pre_raster_library_info{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, + .pNext = nullptr, + .flags = VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT, + }; + VkGraphicsPipelineCreateInfo pre_raster_library_ci{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = &pre_raster_library_info, + .flags = library_flags, + .stageCount = static_cast(preraster_shader_stages.size()), + .pStages = preraster_shader_stages.empty() ? nullptr + : preraster_shader_stages.data(), + .pVertexInputState = nullptr, + .pInputAssemblyState = nullptr, + .pTessellationState = &tessellation_ci, + .pViewportState = &viewport_ci, + .pRasterizationState = &rasterization_ci, + .pMultisampleState = nullptr, + .pDepthStencilState = nullptr, + .pColorBlendState = nullptr, + .pDynamicState = pre_raster_dynamic_state_ci_ptr, + .layout = *pipeline_layout, + .renderPass = render_pass, + .subpass = 0, + .basePipelineHandle = nullptr, + .basePipelineIndex = 0, + }; + pipeline_libraries[1] = device.GetLogical().CreateGraphicsPipeline( + pre_raster_library_ci, *pipeline_cache); + + pipeline_libraries[2].reset(); + if (fragment_shader_stage) { + const VkGraphicsPipelineLibraryCreateInfoEXT fragment_shader_library_info{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, + .pNext = nullptr, + .flags = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT, + }; + VkPipelineShaderStageCreateInfo fragment_stage_ci = *fragment_shader_stage; + VkGraphicsPipelineCreateInfo fragment_shader_library_ci{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = &fragment_shader_library_info, + .flags = library_flags, + .stageCount = 1, + .pStages = &fragment_stage_ci, + .pVertexInputState = nullptr, + .pInputAssemblyState = nullptr, + .pTessellationState = nullptr, + .pViewportState = nullptr, + .pRasterizationState = nullptr, + .pMultisampleState = nullptr, + .pDepthStencilState = nullptr, + .pColorBlendState = nullptr, + .pDynamicState = nullptr, + .layout = *pipeline_layout, + .renderPass = render_pass, + .subpass = 0, + .basePipelineHandle = nullptr, + .basePipelineIndex = 0, + }; + pipeline_libraries[2] = device.GetLogical().CreateGraphicsPipeline( + fragment_shader_library_ci, *pipeline_cache); + } + + const VkGraphicsPipelineLibraryCreateInfoEXT fragment_output_library_info{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, + .pNext = nullptr, + .flags = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT, + }; + VkGraphicsPipelineCreateInfo fragment_output_library_ci{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = &fragment_output_library_info, + .flags = library_flags, + .stageCount = 0, + .pStages = nullptr, + .pVertexInputState = nullptr, + .pInputAssemblyState = nullptr, + .pTessellationState = nullptr, + .pViewportState = nullptr, + .pRasterizationState = nullptr, + .pMultisampleState = &multisample_ci, + .pDepthStencilState = &depth_stencil_ci, + .pColorBlendState = &color_blend_ci, + .pDynamicState = fragment_output_dynamic_state_ci_ptr, + .layout = VK_NULL_HANDLE, + .renderPass = render_pass, + .subpass = 0, + .basePipelineHandle = nullptr, + .basePipelineIndex = 0, + }; + pipeline_libraries[3] = device.GetLogical().CreateGraphicsPipeline( + fragment_output_library_ci, *pipeline_cache); + + std::array linked_libraries{}; + u32 linked_count = 0; + for (const auto& library : pipeline_libraries) { + if (library) { + linked_libraries[linked_count++] = *library; + } + } + if (linked_count == 0) { + return false; + } + + VkPipelineLibraryCreateInfoKHR link_info{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR, + .pNext = nullptr, + .libraryCount = linked_count, + .pLibraries = linked_libraries.data(), + }; + VkGraphicsPipelineLibraryCreateInfoEXT executable_library_info{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, + .pNext = &link_info, + .flags = 0, + }; + VkPipelineCreateFlags executable_flags = capture_stats + ? VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR + : 0; +#if defined(VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT) + if (device.ShouldEnableLinkTimeOptimization()) { + executable_flags |= VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT; + } +#endif + VkGraphicsPipelineCreateInfo executable_ci{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = &executable_library_info, + .flags = executable_flags, + .stageCount = 0, + .pStages = nullptr, + .pVertexInputState = nullptr, + .pInputAssemblyState = nullptr, + .pTessellationState = nullptr, + .pViewportState = nullptr, + .pRasterizationState = nullptr, + .pMultisampleState = nullptr, + .pDepthStencilState = nullptr, + .pColorBlendState = nullptr, + .pDynamicState = nullptr, + .layout = *pipeline_layout, + .renderPass = render_pass, + .subpass = 0, + .basePipelineHandle = nullptr, + .basePipelineIndex = 0, + }; + pipeline = device.GetLogical().CreateGraphicsPipeline(executable_ci, *pipeline_cache); + return true; + } catch (const vk::Exception& exception) { + LOG_WARNING(Render_Vulkan, + "VK_EXT_graphics_pipeline_library pipeline creation failed: {}", + exception.what()); + for (auto& library : pipeline_libraries) { + library.reset(); + } + device.DisableGraphicsPipelineLibrary(exception.what()); + return false; + } + }; + if (build_with_gpl()) { + return; + } + } +#endif + VkPipelineCreateFlags flags{}; if (device.IsKhrPipelineExecutablePropertiesEnabled() && Settings::values.renderer_debug.GetValue()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 650c8e07ed..e95af1b365 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -154,6 +154,7 @@ private: DescriptorAllocator descriptor_allocator; vk::PipelineLayout pipeline_layout; vk::DescriptorUpdateTemplate descriptor_update_template; + std::array pipeline_libraries{}; vk::Pipeline pipeline; std::condition_variable build_condvar; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 0e0bec2ce3..b3cebcb58d 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -974,6 +974,8 @@ bool Device::HasTimelineSemaphore() const { bool Device::GetSuitability(bool requires_swapchain) { // Assume we will be suitable. bool suitable = true; + use_graphics_pipeline_library = false; + graphics_pipeline_library_disable_reason.clear(); // Configure properties. VkPhysicalDeviceVulkan12Features features_1_2{}; @@ -1024,6 +1026,19 @@ bool Device::GetSuitability(bool requires_swapchain) { #undef FEATURE_EXTENSION #undef EXTENSION +#if defined(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME) + if (supported_extensions.contains(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME)) { + loaded_extensions.insert(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME); + extensions.pipeline_library = true; + } +#endif +#if defined(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME) + if (supported_extensions.contains(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME)) { + loaded_extensions.insert(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); + extensions.graphics_pipeline_library = true; + } +#endif + // Some extensions are mandatory. Check those. #define CHECK_EXTENSION(extension_name) \ if (!loaded_extensions.contains(extension_name)) { \ @@ -1089,6 +1104,14 @@ bool Device::GetSuitability(bool requires_swapchain) { FOR_EACH_VK_FEATURE_1_3(EXT_FEATURE); } +#if (defined(VK_EXT_graphics_pipeline_library) || defined(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME)) && defined(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME) + if (extensions.graphics_pipeline_library) { + features.graphics_pipeline_library.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT; + SetNext(next, features.graphics_pipeline_library); + } +#endif + #undef EXT_FEATURE #undef FEATURE @@ -1158,6 +1181,13 @@ bool Device::GetSuitability(bool requires_swapchain) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT; SetNext(next, properties.transform_feedback); } +#if (defined(VK_EXT_graphics_pipeline_library) || defined(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME)) && defined(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME) + if (extensions.graphics_pipeline_library) { + properties.graphics_pipeline_library.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT; + SetNext(next, properties.graphics_pipeline_library); + } +#endif // Perform the property fetch. physical.GetProperties2(properties2); @@ -1168,6 +1198,31 @@ bool Device::GetSuitability(bool requires_swapchain) { // Unload extensions if feature support is insufficient. RemoveUnsuitableExtensions(); +#if (defined(VK_EXT_graphics_pipeline_library) || defined(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME)) && defined(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME) + const bool gpl_extensions_supported = + extensions.pipeline_library && extensions.graphics_pipeline_library; + const bool gpl_feature_supported = + extensions.graphics_pipeline_library && + features.graphics_pipeline_library.graphicsPipelineLibrary == VK_TRUE; + const bool gpl_supported = gpl_extensions_supported && gpl_feature_supported; + + const bool gpl_enabled = gpl_supported; + if (!gpl_supported) { + graphics_pipeline_library_disable_reason = gpl_extensions_supported + ? "VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT not reported" + : "Required pipeline library extensions missing"; + } else { + graphics_pipeline_library_disable_reason.clear(); + LOG_INFO(Render_Vulkan, "VK_EXT_graphics_pipeline_library enabled by default"); + } + + use_graphics_pipeline_library = gpl_enabled; + if (!use_graphics_pipeline_library && !graphics_pipeline_library_disable_reason.empty()) { + LOG_DEBUG(Render_Vulkan, "VK_EXT_graphics_pipeline_library disabled: {}", + graphics_pipeline_library_disable_reason); + } +#endif + // Check limits. struct Limit { u32 minimum; @@ -1194,6 +1249,46 @@ bool Device::GetSuitability(bool requires_swapchain) { return suitable; } +#if (defined(VK_EXT_graphics_pipeline_library) || defined(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME)) && defined(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME) +bool Device::UseGraphicsPipelineLibrary() const noexcept { + return use_graphics_pipeline_library; +} + +bool Device::ShouldRetainLinkTimeOptimizationInfo() const noexcept { + return use_graphics_pipeline_library && + properties.graphics_pipeline_library.graphicsPipelineLibraryFastLinking == VK_TRUE; +} + +bool Device::ShouldEnableLinkTimeOptimization() const noexcept { + return use_graphics_pipeline_library && + properties.graphics_pipeline_library.graphicsPipelineLibraryFastLinking == VK_TRUE; +} + +void Device::DisableGraphicsPipelineLibrary(std::string_view reason) const noexcept { + if (!use_graphics_pipeline_library) { + return; + } + use_graphics_pipeline_library = false; + graphics_pipeline_library_disable_reason.assign(reason.begin(), reason.end()); + LOG_WARNING(Render_Vulkan, + "Disabling VK_EXT_graphics_pipeline_library for this session: {}", reason); +} +#else +bool Device::UseGraphicsPipelineLibrary() const noexcept { + return false; +} + +bool Device::ShouldRetainLinkTimeOptimizationInfo() const noexcept { + return false; +} + +bool Device::ShouldEnableLinkTimeOptimization() const noexcept { + return false; +} + +void Device::DisableGraphicsPipelineLibrary(std::string_view) const noexcept {} +#endif + void Device::RemoveUnsuitableExtensions() { // VK_EXT_custom_border_color extensions.custom_border_color = features.custom_border_color.customBorderColors && @@ -1318,6 +1413,14 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.workgroup_memory_explicit_layout, features.workgroup_memory_explicit_layout, VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); +#if (defined(VK_EXT_graphics_pipeline_library) || defined(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME)) && defined(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME) + if (!(extensions.pipeline_library && extensions.graphics_pipeline_library && + features.graphics_pipeline_library.graphicsPipelineLibrary)) { + RemoveExtensionFeature(extensions.graphics_pipeline_library, + features.graphics_pipeline_library, + VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); + } +#endif } void Device::SetupFamilies(VkSurfaceKHR surface) { diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index cb13f28523..7705d32ec7 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -435,6 +436,11 @@ public: return extensions.pipeline_executable_properties; } + bool UseGraphicsPipelineLibrary() const noexcept; + bool ShouldRetainLinkTimeOptimizationInfo() const noexcept; + bool ShouldEnableLinkTimeOptimization() const noexcept; + void DisableGraphicsPipelineLibrary(std::string_view reason) const noexcept; + /// Returns true if VK_KHR_swapchain_mutable_format is enabled. bool IsKhrSwapchainMutableFormatEnabled() const { return extensions.swapchain_mutable_format; @@ -789,6 +795,13 @@ private: FOR_EACH_VK_FEATURE_EXT(FEATURE); FOR_EACH_VK_EXTENSION(EXTENSION); +#if defined(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME) + bool pipeline_library{}; +#endif +#if defined(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME) + bool graphics_pipeline_library{}; +#endif + #undef EXTENSION #undef FEATURE }; @@ -804,6 +817,10 @@ private: FOR_EACH_VK_FEATURE_1_3(FEATURE_CORE); FOR_EACH_VK_FEATURE_EXT(FEATURE_EXT); +#if defined(VK_EXT_graphics_pipeline_library) || defined(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME) + VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT graphics_pipeline_library{}; +#endif + #undef FEATURE_CORE #undef FEATURE_EXT @@ -817,6 +834,9 @@ private: VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor{}; VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{}; +#if defined(VK_EXT_graphics_pipeline_library) || defined(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME) + VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT graphics_pipeline_library{}; +#endif VkPhysicalDeviceProperties properties{}; }; @@ -849,6 +869,8 @@ private: bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3. bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3. bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow. + mutable bool use_graphics_pipeline_library{}; ///< Runtime flag for VK_EXT_graphics_pipeline_library usage + mutable std::string graphics_pipeline_library_disable_reason; u64 device_access_memory{}; ///< Total size of device local memory in bytes. u32 sets_per_pool{}; ///< Sets per Description Pool NvidiaArchitecture nvidia_arch{NvidiaArchitecture::Arch_AmpereOrNewer};