vkd3d-shader/spirv: Implement support for rasteriser-ordered views.

Using SPV_EXT_fragment_shader_interlock.
This commit is contained in:
Evan Tang 2023-10-11 16:09:23 -05:00 committed by Alexandre Julliard
parent 21f76257b0
commit 4553b2a0dc
Notes: Alexandre Julliard 2024-03-08 23:48:58 +01:00
Approved-by: Giovanni Mascellani (@giomasce)
Approved-by: Henri Verbeet (@hverbeet)
Approved-by: Alexandre Julliard (@julliard)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/695
5 changed files with 189 additions and 22 deletions

View File

@ -892,6 +892,8 @@ enum vkd3d_shader_spirv_extension
VKD3D_SHADER_SPIRV_EXTENSION_EXT_STENCIL_EXPORT,
/** \since 1.11 */
VKD3D_SHADER_SPIRV_EXTENSION_EXT_VIEWPORT_INDEX_LAYER,
/** \since 1.12 */
VKD3D_SHADER_SPIRV_EXTENSION_EXT_FRAGMENT_SHADER_INTERLOCK,
VKD3D_FORCE_32_BIT_ENUM(VKD3D_SHADER_SPIRV_EXTENSION),
};

View File

@ -1953,6 +1953,9 @@ static bool vkd3d_spirv_compile_module(struct vkd3d_spirv_builder *builder,
|| vkd3d_spirv_capability_is_enabled(builder, SpvCapabilityStorageImageArrayDynamicIndexing)
|| vkd3d_spirv_capability_is_enabled(builder, SpvCapabilityShaderNonUniformEXT))
vkd3d_spirv_build_op_extension(&stream, "SPV_EXT_descriptor_indexing");
if (vkd3d_spirv_capability_is_enabled(builder, SpvCapabilityFragmentShaderPixelInterlockEXT)
|| vkd3d_spirv_capability_is_enabled(builder, SpvCapabilityFragmentShaderSampleInterlockEXT))
vkd3d_spirv_build_op_extension(&stream, "SPV_EXT_fragment_shader_interlock");
if (vkd3d_spirv_capability_is_enabled(builder, SpvCapabilityStencilExportEXT))
vkd3d_spirv_build_op_extension(&stream, "SPV_EXT_shader_stencil_export");
if (vkd3d_spirv_capability_is_enabled(builder, SpvCapabilityShaderViewportIndexLayerEXT))
@ -2359,6 +2362,7 @@ struct spirv_compiler
unsigned int output_control_point_count;
bool use_vocp;
bool use_invocation_interlock;
bool emit_point_size;
enum vkd3d_shader_opcode phase;
@ -6285,9 +6289,24 @@ static void spirv_compiler_emit_resource_declaration(struct spirv_compiler *comp
if (!(d->flags & VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_UAV_READ))
vkd3d_spirv_build_op_decorate(builder, var_id, SpvDecorationNonReadable, NULL, 0);
if (d->uav_flags & VKD3DSUF_GLOBALLY_COHERENT)
/* ROVs are implicitly globally coherent. */
if (d->uav_flags & (VKD3DSUF_GLOBALLY_COHERENT | VKD3DSUF_RASTERISER_ORDERED_VIEW))
vkd3d_spirv_build_op_decorate(builder, var_id, SpvDecorationCoherent, NULL, 0);
if (d->uav_flags & VKD3DSUF_RASTERISER_ORDERED_VIEW)
{
if (compiler->shader_type != VKD3D_SHADER_TYPE_PIXEL)
spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_UNSUPPORTED_FEATURE,
"Rasteriser-ordered views are only supported in fragment shaders.");
else if (!spirv_compiler_is_target_extension_supported(compiler,
VKD3D_SHADER_SPIRV_EXTENSION_EXT_FRAGMENT_SHADER_INTERLOCK))
spirv_compiler_error(compiler, VKD3D_SHADER_ERROR_SPV_UNSUPPORTED_FEATURE,
"Cannot enable fragment shader interlock. "
"The target environment does not support fragment shader interlock.");
else
compiler->use_invocation_interlock = true;
}
if (d->flags & VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_UAV_COUNTER)
{
assert(structure_stride); /* counters are valid only for structured buffers */
@ -7737,11 +7756,31 @@ static uint32_t spirv_compiler_emit_conditional_branch(struct spirv_compiler *co
return merge_block_id;
}
static void spirv_compiler_end_invocation_interlock(struct spirv_compiler *compiler)
{
struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
if (vkd3d_spirv_capability_is_enabled(builder, SpvCapabilitySampleRateShading))
{
spirv_compiler_emit_execution_mode(compiler, SpvExecutionModeSampleInterlockOrderedEXT, NULL, 0);
vkd3d_spirv_enable_capability(builder, SpvCapabilityFragmentShaderSampleInterlockEXT);
}
else
{
spirv_compiler_emit_execution_mode(compiler, SpvExecutionModePixelInterlockOrderedEXT, NULL, 0);
vkd3d_spirv_enable_capability(builder, SpvCapabilityFragmentShaderPixelInterlockEXT);
}
vkd3d_spirv_build_op(&builder->function_stream, SpvOpEndInvocationInterlockEXT);
}
static void spirv_compiler_emit_return(struct spirv_compiler *compiler,
const struct vkd3d_shader_instruction *instruction)
{
struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
if (compiler->use_invocation_interlock)
spirv_compiler_end_invocation_interlock(compiler);
if (compiler->shader_type != VKD3D_SHADER_TYPE_GEOMETRY && (is_in_default_phase(compiler)
|| is_in_control_point_phase(compiler)))
spirv_compiler_emit_shader_epilogue_invocation(compiler);
@ -9510,6 +9549,11 @@ static void spirv_compiler_emit_main_prolog(struct spirv_compiler *compiler)
if (compiler->emit_point_size)
spirv_compiler_emit_point_size(compiler);
/* Maybe in the future we can try to shrink the size of the interlocked
* section. */
if (compiler->use_invocation_interlock)
vkd3d_spirv_build_op(&compiler->spirv_builder.function_stream, SpvOpBeginInvocationInterlockEXT);
}
static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler,

View File

@ -64,6 +64,6 @@ shader model >= 5.0
rov
[test]
todo draw triangle list 93
draw triangle list 93
probe all rui(0x6aaaaaaa)
probe uav 1 all rui(0x2aaaaaaa)

View File

@ -73,11 +73,24 @@ struct vulkan_shader_runner
} samplers[MAX_SAMPLERS];
DECLARE_VK_PFN(vkCreateInstance);
DECLARE_VK_PFN(vkEnumerateInstanceExtensionProperties);
#define VK_INSTANCE_PFN DECLARE_VK_PFN
#define VK_DEVICE_PFN DECLARE_VK_PFN
#include "vulkan_procs.h"
};
struct extension_list
{
const char **names;
size_t count;
};
struct physical_device_info
{
VkPhysicalDeviceFeatures2 features2;
VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT interlock_features;
};
static struct vulkan_shader_runner *vulkan_shader_runner(struct shader_runner *r)
{
return CONTAINING_RECORD(r, struct vulkan_shader_runner, r);
@ -504,6 +517,16 @@ static bool compile_shader(struct vulkan_shader_runner *runner, const char *sour
spirv_info.next = &interface_info;
spirv_info.environment = VKD3D_SHADER_SPIRV_ENVIRONMENT_VULKAN_1_0;
if (runner->caps.rov)
{
static const enum vkd3d_shader_spirv_extension extensions[] =
{
VKD3D_SHADER_SPIRV_EXTENSION_EXT_FRAGMENT_SHADER_INTERLOCK,
};
spirv_info.extensions = extensions;
spirv_info.extension_count = ARRAY_SIZE(extensions);
}
push_constants.register_space = 0;
push_constants.register_index = 0;
@ -1253,12 +1276,6 @@ static bool get_graphics_queue_index(const struct vulkan_shader_runner *runner,
return false;
}
static const char *const required_device_extensions[] =
{
VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME,
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
};
static bool has_extension(const VkExtensionProperties *extensions, uint32_t count, const char *extension_name)
{
uint32_t i;
@ -1271,21 +1288,75 @@ static bool has_extension(const VkExtensionProperties *extensions, uint32_t coun
return false;
}
static bool check_device_extensions(struct vulkan_shader_runner *runner)
static void check_instance_extensions(struct vulkan_shader_runner *runner, struct extension_list *enabled_extensions)
{
VkExtensionProperties *extensions;
uint32_t count, i;
static const char *instance_extensions[] =
{
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
};
enabled_extensions->names = calloc(ARRAY_SIZE(instance_extensions), sizeof(*enabled_extensions->names));
enabled_extensions->count = 0;
VK_CALL(vkEnumerateInstanceExtensionProperties(NULL, &count, NULL));
extensions = calloc(count, sizeof(*extensions));
VK_CALL(vkEnumerateInstanceExtensionProperties(NULL, &count, extensions));
for (i = 0; i < ARRAY_SIZE(instance_extensions); ++i)
{
const char *name = instance_extensions[i];
if (has_extension(extensions, count, name))
enabled_extensions->names[enabled_extensions->count++] = name;
}
free(extensions);
}
static bool check_device_extensions(struct vulkan_shader_runner *runner, struct extension_list *enabled_extensions)
{
VkPhysicalDevice phys_device = runner->phys_device;
VkExtensionProperties *extensions;
uint32_t i, count;
static const struct
{
const char *name;
bool required;
}
device_extensions[] =
{
{VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME},
{VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME, true},
{VK_KHR_MAINTENANCE1_EXTENSION_NAME, true},
};
enabled_extensions->names = calloc(ARRAY_SIZE(device_extensions), sizeof(*enabled_extensions->names));
enabled_extensions->count = 0;
VK_CALL(vkEnumerateDeviceExtensionProperties(phys_device, NULL, &count, NULL));
extensions = calloc(count, sizeof(*extensions));
VK_CALL(vkEnumerateDeviceExtensionProperties(phys_device, NULL, &count, extensions));
for (i = 0; i < ARRAY_SIZE(required_device_extensions); ++i)
for (i = 0; i < ARRAY_SIZE(device_extensions); ++i)
{
if (!has_extension(extensions, count, required_device_extensions[i]))
const char *name = device_extensions[i].name;
if (has_extension(extensions, count, name))
{
skip("The selected Vulkan device does not support %s.\n", required_device_extensions[i]);
enabled_extensions->names[enabled_extensions->count++] = name;
if (!strcmp(name, VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME))
runner->caps.rov = true;
continue;
}
if (device_extensions[i].required)
{
skip("The selected Vulkan device does not support %s.\n", name);
free(enabled_extensions->names);
free(extensions);
return false;
}
@ -1295,6 +1366,23 @@ static bool check_device_extensions(struct vulkan_shader_runner *runner)
return true;
}
static void get_physical_device_info(struct vulkan_shader_runner *runner, struct physical_device_info *info)
{
memset(info, 0, sizeof(*info));
info->features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
if (runner->caps.rov)
{
info->features2.pNext = &info->interlock_features;
info->interlock_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT;
}
if (runner->vkGetPhysicalDeviceFeatures2KHR)
VK_CALL(vkGetPhysicalDeviceFeatures2KHR(runner->phys_device, &info->features2));
else
VK_CALL(vkGetPhysicalDeviceFeatures(runner->phys_device, &info->features2.features));
}
static bool init_vulkan_runner(struct vulkan_shader_runner *runner)
{
VkDescriptorPoolCreateInfo descriptor_pool_desc = {.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
@ -1303,9 +1391,13 @@ static bool init_vulkan_runner(struct vulkan_shader_runner *runner)
VkDeviceQueueCreateInfo queue_desc = {.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO};
VkInstanceCreateInfo instance_desc = {.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
VkDeviceCreateInfo device_desc = {.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
VkPhysicalDeviceFeatures ret_features, features;
VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT interlock_features;
VkDescriptorPoolSize descriptor_pool_sizes[5];
const VkPhysicalDeviceFeatures *ret_features;
struct extension_list enabled_extensions;
static const float queue_priority = 1.0f;
struct physical_device_info device_info;
VkPhysicalDeviceFeatures features;
VkFormatProperties format_props;
uint32_t count, graphics_index;
VkDevice device;
@ -1320,8 +1412,15 @@ static bool init_vulkan_runner(struct vulkan_shader_runner *runner)
vkGetInstanceProcAddr = vkd3d_dlsym(libvulkan, "vkGetInstanceProcAddr");
runner->vkCreateInstance = (void *)vkGetInstanceProcAddr(NULL, "vkCreateInstance");
runner->vkEnumerateInstanceExtensionProperties = (void *)vkGetInstanceProcAddr(NULL,
"vkEnumerateInstanceExtensionProperties");
if ((vr = VK_CALL(vkCreateInstance(&instance_desc, NULL, &runner->instance))) < 0)
check_instance_extensions(runner, &enabled_extensions);
instance_desc.ppEnabledExtensionNames = enabled_extensions.names;
instance_desc.enabledExtensionCount = enabled_extensions.count;
vr = VK_CALL(vkCreateInstance(&instance_desc, NULL, &runner->instance));
free(enabled_extensions.names);
if (vr < 0)
{
skip("Failed to create a Vulkan instance, vr %d.\n", vr);
return false;
@ -1351,15 +1450,15 @@ static bool init_vulkan_runner(struct vulkan_shader_runner *runner)
device_desc.pQueueCreateInfos = &queue_desc;
device_desc.queueCreateInfoCount = 1;
device_desc.enabledExtensionCount = ARRAY_SIZE(required_device_extensions);
device_desc.ppEnabledExtensionNames = required_device_extensions;
queue_desc.queueFamilyIndex = graphics_index;
queue_desc.queueCount = 1;
queue_desc.pQueuePriorities = &queue_priority;
if (!check_device_extensions(runner))
if (!check_device_extensions(runner, &enabled_extensions))
goto out_destroy_instance;
device_desc.ppEnabledExtensionNames = enabled_extensions.names;
device_desc.enabledExtensionCount = enabled_extensions.count;
VK_CALL(vkGetPhysicalDeviceFormatProperties(runner->phys_device, VK_FORMAT_R32G32B32A32_SFLOAT, &format_props));
if (!(format_props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
@ -1369,7 +1468,8 @@ static bool init_vulkan_runner(struct vulkan_shader_runner *runner)
}
runner->caps.runner = "Vulkan";
VK_CALL(vkGetPhysicalDeviceFeatures(runner->phys_device, &ret_features));
get_physical_device_info(runner, &device_info);
ret_features = &device_info.features2.features;
device_desc.pEnabledFeatures = &features;
memset(&features, 0, sizeof(features));
@ -1377,7 +1477,7 @@ static bool init_vulkan_runner(struct vulkan_shader_runner *runner)
/* FIXME: Probably make these optional. */
#define ENABLE_FEATURE(x) \
if (!ret_features.x) \
if (!ret_features->x) \
{ \
skip("The selected Vulkan device does not support " #x ".\n"); \
goto out_destroy_instance; \
@ -1385,21 +1485,41 @@ static bool init_vulkan_runner(struct vulkan_shader_runner *runner)
features.x = VK_TRUE
ENABLE_FEATURE(fragmentStoresAndAtomics);
/* For SV_PrimitiveID/SpvBuiltInPrimitiveId in fragment shaders. */
ENABLE_FEATURE(geometryShader);
ENABLE_FEATURE(shaderImageGatherExtended);
ENABLE_FEATURE(shaderStorageImageWriteWithoutFormat);
if (ret_features.shaderFloat64)
if (ret_features->shaderFloat64)
{
features.shaderFloat64 = VK_TRUE;
runner->caps.float64 = true;
}
if (ret_features.shaderInt64)
if (ret_features->shaderInt64)
{
features.shaderInt64 = VK_TRUE;
runner->caps.int64 = true;
}
if ((vr = VK_CALL(vkCreateDevice(runner->phys_device, &device_desc, NULL, &device))))
if (device_info.interlock_features.fragmentShaderSampleInterlock
&& device_info.interlock_features.fragmentShaderPixelInterlock)
{
memset(&interlock_features, 0, sizeof(interlock_features));
interlock_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT;
interlock_features.pNext = (void *)device_desc.pNext;
interlock_features.fragmentShaderSampleInterlock = VK_TRUE;
interlock_features.fragmentShaderPixelInterlock = VK_TRUE;
device_desc.pNext = &interlock_features;
}
else
{
runner->caps.rov = false;
}
vr = VK_CALL(vkCreateDevice(runner->phys_device, &device_desc, NULL, &device));
free(enabled_extensions.names);
if (vr)
{
skip("Failed to create device, vr %d.\n", vr);
goto out_destroy_instance;

View File

@ -39,6 +39,7 @@ VK_INSTANCE_PFN(vkEnumerateDeviceExtensionProperties)
VK_INSTANCE_PFN(vkEnumeratePhysicalDevices)
VK_INSTANCE_PFN(vkGetDeviceProcAddr)
VK_INSTANCE_PFN(vkGetPhysicalDeviceFeatures)
VK_INSTANCE_PFN(vkGetPhysicalDeviceFeatures2KHR)
VK_INSTANCE_PFN(vkGetPhysicalDeviceFormatProperties)
VK_INSTANCE_PFN(vkGetPhysicalDeviceMemoryProperties)
VK_INSTANCE_PFN(vkGetPhysicalDeviceQueueFamilyProperties)