[Vulkan] wave64 optimization for AMD RDNA gpus (5000/6000/7000 series)

This commit is contained in:
Herman S.
2026-01-12 01:12:23 +09:00
parent 762a5679d5
commit 29f1c0645e
5 changed files with 110 additions and 10 deletions
@@ -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<const uint32_t*, size_t> 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 "
+57
View File
@@ -168,6 +168,7 @@ std::unique_ptr<VulkanDevice> 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> 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> VulkanDevice::CreateIfSupported(
VulkanFeatures<VkPhysicalDeviceDynamicRenderingFeatures,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES>
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> 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> 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
+13
View File
@@ -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_; }
+20 -4
View File
@@ -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;
+6 -2
View File
@@ -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