diff --git a/src/xenia/gpu/vulkan/vulkan_render_target_cache.cc b/src/xenia/gpu/vulkan/vulkan_render_target_cache.cc index aae3f345d..cd590eede 100644 --- a/src/xenia/gpu/vulkan/vulkan_render_target_cache.cc +++ b/src/xenia/gpu/vulkan/vulkan_render_target_cache.cc @@ -480,12 +480,15 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) { resolve_copy_shader_code.unscaled_size_bytes && resolve_copy_shader_code.scaled && resolve_copy_shader_code.scaled_size_bytes); + // Resolve copy shaders use 8x8 = 64 threads per group. Request wave64 mode + // on RDNA GPUs to ensure one full wave per group. VkPipeline resolve_copy_pipeline = ui::vulkan::util::CreateComputePipeline( vulkan_device, resolve_copy_pipeline_layout_, draw_resolution_scaled ? resolve_copy_shader_code.scaled : resolve_copy_shader_code.unscaled, draw_resolution_scaled ? resolve_copy_shader_code.scaled_size_bytes - : resolve_copy_shader_code.unscaled_size_bytes); + : resolve_copy_shader_code.unscaled_size_bytes, + nullptr, "main", 64); if (resolve_copy_pipeline == VK_NULL_HANDLE) { XELOGE( "VulkanRenderTargetCache: Failed to create the resolve copy " @@ -551,10 +554,13 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) { for (size_t i = 0; i < xe::countof(host_depth_store_shaders); ++i) { const std::pair host_depth_store_shader = host_depth_store_shaders[i]; + // Host depth store shaders use 8x8 = 64 threads per group. Request wave64 + // mode on RDNA GPUs to ensure one full wave per group. VkPipeline host_depth_store_pipeline = ui::vulkan::util::CreateComputePipeline( vulkan_device, host_depth_store_pipeline_layout_, - host_depth_store_shader.first, host_depth_store_shader.second); + host_depth_store_shader.first, host_depth_store_shader.second, + nullptr, "main", 64); if (host_depth_store_pipeline == VK_NULL_HANDLE) { XELOGE( "VulkanRenderTargetCache: Failed to create the {}-sample host " @@ -740,12 +746,15 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) { Shutdown(); return false; } + // Resolve clear shaders use 8x8 = 64 threads per group. Request wave64 + // mode on RDNA GPUs to ensure one full wave per group. resolve_fsi_clear_32bpp_pipeline_ = ui::vulkan::util::CreateComputePipeline( vulkan_device, resolve_fsi_clear_pipeline_layout_, draw_resolution_scaled ? shaders::resolve_clear_32bpp_scaled_cs : shaders::resolve_clear_32bpp_cs, draw_resolution_scaled ? sizeof(shaders::resolve_clear_32bpp_scaled_cs) - : sizeof(shaders::resolve_clear_32bpp_cs)); + : sizeof(shaders::resolve_clear_32bpp_cs), + nullptr, "main", 64); if (resolve_fsi_clear_32bpp_pipeline_ == VK_NULL_HANDLE) { XELOGE( "VulkanRenderTargetCache: Failed to create the 32bpp resolve EDRAM " @@ -758,7 +767,8 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) { draw_resolution_scaled ? shaders::resolve_clear_64bpp_scaled_cs : shaders::resolve_clear_64bpp_cs, draw_resolution_scaled ? sizeof(shaders::resolve_clear_64bpp_scaled_cs) - : sizeof(shaders::resolve_clear_64bpp_cs)); + : sizeof(shaders::resolve_clear_64bpp_cs), + nullptr, "main", 64); if (resolve_fsi_clear_64bpp_pipeline_ == VK_NULL_HANDLE) { XELOGE( "VulkanRenderTargetCache: Failed to create the 64bpp resolve EDRAM " diff --git a/src/xenia/ui/vulkan/vulkan_device.cc b/src/xenia/ui/vulkan/vulkan_device.cc index b9018f54d..4d2b0f0ea 100644 --- a/src/xenia/ui/vulkan/vulkan_device.cc +++ b/src/xenia/ui/vulkan/vulkan_device.cc @@ -168,6 +168,7 @@ std::unique_ptr VulkanDevice::CreateIfSupported( bool ext_1_3_EXT_shader_demote_to_helper_invocation = false; bool ext_1_3_KHR_dynamic_rendering = false; bool ext_EXT_non_seamless_cube_map = false; + bool ext_1_3_EXT_subgroup_size_control = false; if (with_gpu_emulation) { // #15. XE_UI_VULKAN_LOCAL_PROMOTED_EXTENSION(KHR_sampler_mirror_clamp_to_edge, 1, @@ -192,6 +193,8 @@ std::unique_ptr VulkanDevice::CreateIfSupported( EXT_shader_demote_to_helper_invocation, 1, 3) // #423. XE_UI_VULKAN_LOCAL_EXTENSION(EXT_non_seamless_cube_map) + // #226. + XE_UI_VULKAN_LOCAL_PROMOTED_EXTENSION(EXT_subgroup_size_control, 1, 3) } if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 1, 0)) { // #237. @@ -299,6 +302,17 @@ std::unique_ptr VulkanDevice::CreateIfSupported( VulkanFeatures features_1_3_KHR_dynamic_rendering; + // Vulkan 1.1 core subgroup properties. + VkPhysicalDeviceSubgroupProperties properties_subgroup = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES}; + // VK_EXT_subgroup_size_control (#226, promoted to 1.3). + VkPhysicalDeviceSubgroupSizeControlProperties + properties_1_3_EXT_subgroup_size_control = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES}; + VulkanFeatures< + VkPhysicalDeviceSubgroupSizeControlFeatures, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES> + features_1_3_EXT_subgroup_size_control; if (get_physical_device_properties2_supported) { if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 2, 0)) { @@ -336,6 +350,18 @@ std::unique_ptr VulkanDevice::CreateIfSupported( features_EXT_non_seamless_cube_map.Link(supported_features_2, device_create_info); } + // Subgroup properties are Vulkan 1.1 core. + if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 1, 0)) { + properties_subgroup.pNext = properties_2.pNext; + properties_2.pNext = &properties_subgroup; + } + // VK_EXT_subgroup_size_control properties and features. + if (ext_1_3_EXT_subgroup_size_control) { + properties_1_3_EXT_subgroup_size_control.pNext = properties_2.pNext; + properties_2.pNext = &properties_1_3_EXT_subgroup_size_control; + features_1_3_EXT_subgroup_size_control.Link(supported_features_2, + device_create_info); + } ifn.vkGetPhysicalDeviceProperties2(physical_device, &properties_2); ifn.vkGetPhysicalDeviceFeatures2(physical_device, &supported_features_2); } @@ -713,6 +739,37 @@ std::unique_ptr VulkanDevice::CreateIfSupported( } } + // Vulkan 1.1 core subgroup properties. + if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 1, 0)) { + XE_UI_VULKAN_PROPERTY_2(properties_subgroup, subgroupSize); + device->properties_.subgroupSupportedStages = + properties_subgroup.supportedStages; + XELOGI("* subgroupSupportedStages: {}", + vk::to_string( + vk::ShaderStageFlags(properties_subgroup.supportedStages))); + device->properties_.subgroupSupportedOperations = + properties_subgroup.supportedOperations; + XELOGI("* subgroupSupportedOperations: {}", + vk::to_string(vk::SubgroupFeatureFlags( + properties_subgroup.supportedOperations))); + } + + // VK_EXT_subgroup_size_control (#226, promoted to 1.3). + if (ext_1_3_EXT_subgroup_size_control) { + XE_UI_VULKAN_PROPERTY_2(properties_1_3_EXT_subgroup_size_control, + minSubgroupSize); + XE_UI_VULKAN_PROPERTY_2(properties_1_3_EXT_subgroup_size_control, + maxSubgroupSize); + if (with_gpu_emulation) { + XE_UI_VULKAN_FEATURE_2(features_1_3_EXT_subgroup_size_control, + subgroupSizeControl); + XE_UI_VULKAN_FEATURE_2(features_1_3_EXT_subgroup_size_control, + computeFullSubgroups); + } + } + device->extensions_.ext_1_3_EXT_subgroup_size_control = + ext_1_3_EXT_subgroup_size_control; + #undef XE_UI_VULKAN_LIMIT #undef XE_UI_VULKAN_ENUM_LIMIT #undef XE_UI_VULKAN_FEATURE diff --git a/src/xenia/ui/vulkan/vulkan_device.h b/src/xenia/ui/vulkan/vulkan_device.h index 1bfff1b10..ad04809af 100644 --- a/src/xenia/ui/vulkan/vulkan_device.h +++ b/src/xenia/ui/vulkan/vulkan_device.h @@ -161,6 +161,17 @@ class VulkanDevice { // VK_EXT_non_seamless_cube_map (#423) bool nonSeamlessCubeMap = false; + + // Vulkan 1.1 Subgroup Properties + uint32_t subgroupSize = 32; + VkShaderStageFlags subgroupSupportedStages = 0; + VkSubgroupFeatureFlags subgroupSupportedOperations = 0; + + // VK_EXT_subgroup_size_control (#226, promoted to 1.3) + uint32_t minSubgroupSize = 0; + uint32_t maxSubgroupSize = 0; + bool subgroupSizeControl = false; + bool computeFullSubgroups = false; }; // Properties of the core API and enabled extensions, and enabled features. @@ -188,6 +199,8 @@ class VulkanDevice { bool ext_1_3_KHR_maintenance4 = false; // #414 // VK_KHR_dynamic_rendering (#55, promoted to 1.3) bool ext_1_3_KHR_dynamic_rendering = false; + // VK_EXT_subgroup_size_control (#226, promoted to 1.3) + bool ext_1_3_EXT_subgroup_size_control = false; }; const Extensions& extensions() const { return extensions_; } diff --git a/src/xenia/ui/vulkan/vulkan_util.cc b/src/xenia/ui/vulkan/vulkan_util.cc index 209c9cfb5..9954c42cf 100644 --- a/src/xenia/ui/vulkan/vulkan_util.cc +++ b/src/xenia/ui/vulkan/vulkan_util.cc @@ -200,7 +200,11 @@ VkPipeline CreateComputePipeline( const VulkanDevice* const vulkan_device, const VkPipelineLayout layout, const VkShaderModule shader, const VkSpecializationInfo* const specialization_info, - const char* const entry_point) { + const char* const entry_point, const uint32_t required_subgroup_size) { + VkPipelineShaderStageRequiredSubgroupSizeCreateInfo subgroup_size_info = {}; + subgroup_size_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO; + VkComputePipelineCreateInfo pipeline_create_info; pipeline_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; pipeline_create_info.pNext = nullptr; @@ -216,6 +220,16 @@ VkPipeline CreateComputePipeline( pipeline_create_info.layout = layout; pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; pipeline_create_info.basePipelineIndex = -1; + + // Request specific subgroup size if supported and in range. + if (required_subgroup_size != 0 && + vulkan_device->properties().subgroupSizeControl && + required_subgroup_size >= vulkan_device->properties().minSubgroupSize && + required_subgroup_size <= vulkan_device->properties().maxSubgroupSize) { + subgroup_size_info.requiredSubgroupSize = required_subgroup_size; + pipeline_create_info.stage.pNext = &subgroup_size_info; + } + VkPipeline pipeline; if (vulkan_device->functions().vkCreateComputePipelines( vulkan_device->device(), VK_NULL_HANDLE, 1, &pipeline_create_info, @@ -228,14 +242,16 @@ VkPipeline CreateComputePipeline( VkPipeline CreateComputePipeline( const VulkanDevice* const vulkan_device, VkPipelineLayout layout, const uint32_t* shader_code, size_t shader_code_size_bytes, - const VkSpecializationInfo* specialization_info, const char* entry_point) { + const VkSpecializationInfo* specialization_info, const char* entry_point, + const uint32_t required_subgroup_size) { const VkShaderModule shader = CreateShaderModule(vulkan_device, shader_code, shader_code_size_bytes); if (shader == VK_NULL_HANDLE) { return VK_NULL_HANDLE; } - const VkPipeline pipeline = CreateComputePipeline( - vulkan_device, layout, shader, specialization_info, entry_point); + const VkPipeline pipeline = + CreateComputePipeline(vulkan_device, layout, shader, specialization_info, + entry_point, required_subgroup_size); vulkan_device->functions().vkDestroyShaderModule(vulkan_device->device(), shader, nullptr); return pipeline; diff --git a/src/xenia/ui/vulkan/vulkan_util.h b/src/xenia/ui/vulkan/vulkan_util.h index ba9578c6f..c03c90204 100644 --- a/src/xenia/ui/vulkan/vulkan_util.h +++ b/src/xenia/ui/vulkan/vulkan_util.h @@ -180,16 +180,20 @@ inline VkShaderModule CreateShaderModule( : VK_NULL_HANDLE; } +// If required_subgroup_size is non-zero and VK_EXT_subgroup_size_control is +// supported with the requested size in range, the pipeline will be created +// with that subgroup size requirement. This can be used to request wave64 +// mode on RDNA GPUs for 64-thread compute shaders. VkPipeline CreateComputePipeline( const VulkanDevice* vulkan_device, VkPipelineLayout layout, VkShaderModule shader, const VkSpecializationInfo* specialization_info = nullptr, - const char* entry_point = "main"); + const char* entry_point = "main", uint32_t required_subgroup_size = 0); VkPipeline CreateComputePipeline( const VulkanDevice* vulkan_device, VkPipelineLayout layout, const uint32_t* shader_code, size_t shader_code_size_bytes, const VkSpecializationInfo* specialization_info = nullptr, - const char* entry_point = "main"); + const char* entry_point = "main", uint32_t required_subgroup_size = 0); } // namespace util } // namespace vulkan