Newer
Older
/*
* Shader runner which uses libvkd3d-shader to compile HLSL -> D3D bytecode -> SPIR-V
*
* Copyright 2020-2022 Zebediah Figura for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef _WIN32
#define VK_NO_PROTOTYPES
#define VKD3D_TEST_NO_DEFS
#include "config.h"
#include "vulkan/vulkan.h"
#include "vkd3d.h"
#include "vkd3d_d3dcompiler.h"
#include "shader_runner.h"
#include "vkd3d_test.h"
struct vulkan_resource
{
struct resource r;
VkBuffer buffer;
VkBufferView buffer_view;
VkImageView image_view;
VkDeviceMemory memory;
uint32_t binding;
};
static struct vulkan_resource *vulkan_resource(struct resource *r)
{
return CONTAINING_RECORD(r, struct vulkan_resource, r);
}
#define DECLARE_VK_PFN(name) PFN_##name name;
DECLARE_VK_PFN(vkGetInstanceProcAddr)
struct vulkan_shader_runner
{
struct shader_runner r;
struct shader_runner_caps caps;
Giovanni Mascellani
committed
bool demote_to_helper_invocation;
VkInstance instance;
VkPhysicalDevice phys_device;
VkDevice device;
VkQueue queue;
VkCommandPool command_pool;
VkCommandBuffer cmd_buffer;
VkDescriptorPool descriptor_pool;
Elizabeth Figura
committed
struct vkd3d_shader_scan_signature_info vs_signatures;
struct vulkan_sampler
{
VkSampler vk_sampler;
uint32_t binding;
} 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;
Giovanni Mascellani
committed
VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote_to_helper_invocation_features;
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
static struct vulkan_shader_runner *vulkan_shader_runner(struct shader_runner *r)
{
return CONTAINING_RECORD(r, struct vulkan_shader_runner, r);
}
#define VK_CALL(f) (runner->f)
static void begin_command_buffer(struct vulkan_shader_runner *runner)
{
VkCommandBufferBeginInfo buffer_begin_desc = {.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
VK_CALL(vkBeginCommandBuffer(runner->cmd_buffer, &buffer_begin_desc));
}
static void end_command_buffer(struct vulkan_shader_runner *runner)
{
VkSubmitInfo submit_desc = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO};
VK_CALL(vkEndCommandBuffer(runner->cmd_buffer));
submit_desc.commandBufferCount = 1;
submit_desc.pCommandBuffers = &runner->cmd_buffer;
VK_CALL(vkQueueSubmit(runner->queue, 1, &submit_desc, VK_NULL_HANDLE));
VK_CALL(vkQueueWaitIdle(runner->queue));
}
static void transition_image_layout(struct vulkan_shader_runner *runner,
VkImage image, VkImageAspectFlags aspect_mask, VkImageLayout src_layout, VkImageLayout dst_layout)
{
VkImageMemoryBarrier barrier = {.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
barrier.oldLayout = src_layout;
barrier.newLayout = dst_layout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = aspect_mask;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
VK_CALL(vkCmdPipelineBarrier(runner->cmd_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, NULL, 0, NULL, 1, &barrier));
}
static unsigned int select_vulkan_memory_type(const struct vulkan_shader_runner *runner,
uint32_t memory_type_mask, VkMemoryPropertyFlags required_flags)
{
VkPhysicalDeviceMemoryProperties memory_info;
unsigned int i;
VK_CALL(vkGetPhysicalDeviceMemoryProperties(runner->phys_device, &memory_info));
for (i = 0; i < memory_info.memoryTypeCount; ++i)
{
if (!(memory_type_mask & (1u << i)))
continue;
if ((memory_info.memoryTypes[i].propertyFlags & required_flags) == required_flags)
return i;
}
fatal_error("No valid memory types found matching mask %#x, property flags %#x.\n",
memory_type_mask, required_flags);
}
static VkDeviceMemory allocate_memory(const struct vulkan_shader_runner *runner,
const VkMemoryRequirements *memory_reqs, VkMemoryPropertyFlags flags)
{
VkMemoryAllocateInfo alloc_info = {.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
VkDeviceMemory vk_memory;
VkResult vr;
alloc_info.allocationSize = memory_reqs->size;
alloc_info.memoryTypeIndex = select_vulkan_memory_type(runner,
memory_reqs->memoryTypeBits, flags);
vr = VK_CALL(vkAllocateMemory(runner->device, &alloc_info, NULL, &vk_memory));
ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr);
return vk_memory;
}
static VkBuffer create_buffer(const struct vulkan_shader_runner *runner, VkDeviceSize size,
VkBufferUsageFlags usage, VkMemoryPropertyFlags memory_flags, VkDeviceMemory *memory)
{
VkBufferCreateInfo buffer_info = {.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
VkMemoryRequirements memory_reqs;
VkBuffer buffer;
buffer_info.size = size;
buffer_info.usage = usage;
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_CALL(vkCreateBuffer(runner->device, &buffer_info, NULL, &buffer));
VK_CALL(vkGetBufferMemoryRequirements(runner->device, buffer, &memory_reqs));
*memory = allocate_memory(runner, &memory_reqs, memory_flags);
VK_CALL(vkBindBufferMemory(runner->device, buffer, *memory, 0));
return buffer;
}
static VkBufferView create_buffer_view(const struct vulkan_shader_runner *runner, VkBuffer buffer,
VkFormat format)
{
VkBufferViewCreateInfo view_info = {.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO};
VkBufferView view;
view_info.buffer = buffer;
view_info.format = format;
view_info.range = VK_WHOLE_SIZE;
VK_CALL(vkCreateBufferView(runner->device, &view_info, NULL, &view));
return view;
}
static VkImage create_2d_image(const struct vulkan_shader_runner *runner, uint32_t width, uint32_t height,
uint32_t level_count, VkImageUsageFlags usage, VkFormat format, VkDeviceMemory *memory)
{
VkImageCreateInfo image_info = {.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
VkMemoryRequirements memory_reqs;
VkImage image;
image_info.imageType = VK_IMAGE_TYPE_2D;
image_info.format = format;
image_info.extent.width = width;
image_info.extent.height = height;
image_info.extent.depth = 1;
image_info.mipLevels = level_count;
image_info.arrayLayers = 1;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.usage = usage;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VK_CALL(vkCreateImage(runner->device, &image_info, NULL, &image));
VK_CALL(vkGetImageMemoryRequirements(runner->device, image, &memory_reqs));
*memory = allocate_memory(runner, &memory_reqs, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_CALL(vkBindImageMemory(runner->device, image, *memory, 0));
return image;
}
static VkImageView create_2d_image_view(const struct vulkan_shader_runner *runner, VkImage image, VkFormat format,
VkImageAspectFlags aspect_mask)
{
VkImageViewCreateInfo view_info = {.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
VkImageView view;
view_info.image = image;
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.format = format;
view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
view_info.subresourceRange.aspectMask = aspect_mask;
view_info.subresourceRange.baseMipLevel = 0;
view_info.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
view_info.subresourceRange.baseArrayLayer = 0;
view_info.subresourceRange.layerCount = 1;
VK_CALL(vkCreateImageView(runner->device, &view_info, NULL, &view));
return view;
}
static void resource_init_2d(struct vulkan_shader_runner *runner, struct vulkan_resource *resource,
const struct resource_params *params)
{
VkImageUsageFlagBits usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkFormat format = vkd3d_get_vk_format(params->format);
VkDevice device = runner->device;
unsigned int buffer_offset = 0;
VkDeviceMemory staging_memory;
VkBuffer staging_buffer;
void *data;
if (params->type == RESOURCE_TYPE_UAV)
{
layout = VK_IMAGE_LAYOUT_GENERAL;
usage |= VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
resource->image = create_2d_image(runner, params->width, params->height, params->level_count,
usage, format, &resource->memory);
resource->image_view = create_2d_image_view(runner, resource->image, format, VK_IMAGE_ASPECT_COLOR_BIT);
if (!params->data)
{
begin_command_buffer(runner);
transition_image_layout(runner, resource->image, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, layout);
end_command_buffer(runner);
return;
}
staging_buffer = create_buffer(runner, params->data_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &staging_memory);
VK_CALL(vkMapMemory(device, staging_memory, 0, VK_WHOLE_SIZE, 0, &data));
memcpy(data, params->data, params->data_size);
VK_CALL(vkUnmapMemory(device, staging_memory));
begin_command_buffer(runner);
transition_image_layout(runner, resource->image, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
for (unsigned int level = 0; level < params->level_count; ++level)
{
unsigned int level_width = get_level_dimension(params->width, level);
unsigned int level_height = get_level_dimension(params->height, level);
VkBufferImageCopy region = {0};
region.bufferOffset = buffer_offset;
region.imageSubresource.mipLevel = level;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
region.imageExtent.width = level_width;
region.imageExtent.height = level_height;
region.imageExtent.depth = 1;
VK_CALL(vkCmdCopyBufferToImage(runner->cmd_buffer, staging_buffer, resource->image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion));
buffer_offset += level_width * level_height * params->texel_size;
}
transition_image_layout(runner, resource->image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layout);
end_command_buffer(runner);
VK_CALL(vkFreeMemory(device, staging_memory, NULL));
VK_CALL(vkDestroyBuffer(device, staging_buffer, NULL));
}
static void resource_init_buffer(struct vulkan_shader_runner *runner, struct vulkan_resource *resource,
const struct resource_params *params)
{
VkFormat format = vkd3d_get_vk_format(params->format);
VkDevice device = runner->device;
VkBufferUsageFlagBits usage;
void *data;
if (params->type == RESOURCE_TYPE_UAV)
usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
else
usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
/* d3d12 requires DXGI_FORMAT_UNKNOWN for structured buffers, but Vulkan requires a defined format. */
if (format == VK_FORMAT_UNDEFINED && params->stride)
format = VK_FORMAT_R32_UINT;
resource->buffer = create_buffer(runner, params->data_size, usage,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &resource->memory);
resource->buffer_view = create_buffer_view(runner, resource->buffer, format);
VK_CALL(vkMapMemory(device, resource->memory, 0, VK_WHOLE_SIZE, 0, &data));
memcpy(data, params->data, params->data_size);
VK_CALL(vkUnmapMemory(device, resource->memory));
}
static struct resource *vulkan_runner_create_resource(struct shader_runner *r, const struct resource_params *params)
{
struct vulkan_shader_runner *runner = vulkan_shader_runner(r);
struct vulkan_resource *resource;
VkDevice device = runner->device;
VkFormat format;
void *data;
resource = calloc(1, sizeof(*resource));
init_resource(&resource->r, params);
switch (params->type)
{
case RESOURCE_TYPE_RENDER_TARGET:
format = vkd3d_get_vk_format(params->format);
resource->image = create_2d_image(runner, params->width, params->height, params->level_count,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, format, &resource->memory);
resource->image_view = create_2d_image_view(runner, resource->image, format, VK_IMAGE_ASPECT_COLOR_BIT);
begin_command_buffer(runner);
transition_image_layout(runner, resource->image, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
end_command_buffer(runner);
break;
case RESOURCE_TYPE_DEPTH_STENCIL:
format = vkd3d_get_vk_format(params->format);
resource->image = create_2d_image(runner, params->width, params->height, params->level_count,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, format,
&resource->memory);
resource->image_view = create_2d_image_view(runner, resource->image, format, VK_IMAGE_ASPECT_DEPTH_BIT);
begin_command_buffer(runner);
transition_image_layout(runner, resource->image, VK_IMAGE_ASPECT_DEPTH_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
end_command_buffer(runner);
break;
case RESOURCE_TYPE_UAV:
if (params->dimension == RESOURCE_DIMENSION_BUFFER)
resource_init_buffer(runner, resource, params);
else
resource_init_2d(runner, resource, params);
case RESOURCE_TYPE_VERTEX_BUFFER:
resource->buffer = create_buffer(runner, params->data_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &resource->memory);
VK_CALL(vkMapMemory(device, resource->memory, 0, VK_WHOLE_SIZE, 0, &data));
memcpy(data, params->data, params->data_size);
VK_CALL(vkUnmapMemory(device, resource->memory));
break;
}
return &resource->r;
}
static void vulkan_runner_destroy_resource(struct shader_runner *r, struct resource *res)
{
struct vulkan_shader_runner *runner = vulkan_shader_runner(r);
struct vulkan_resource *resource = vulkan_resource(res);
VkDevice device = runner->device;
if (resource->memory)
VK_CALL(vkFreeMemory(device, resource->memory, NULL));
if (resource->image)
VK_CALL(vkDestroyImage(device, resource->image, NULL));
if (resource->image_view)
VK_CALL(vkDestroyImageView(device, resource->image_view, NULL));
if (resource->buffer)
VK_CALL(vkDestroyBuffer(device, resource->buffer, NULL));
if (resource->buffer_view)
VK_CALL(vkDestroyBufferView(device, resource->buffer_view, NULL));
free(resource);
}
Elizabeth Figura
committed
static bool compile_shader(struct vulkan_shader_runner *runner, const char *source, const char *type,
struct vkd3d_shader_code *dxbc, struct vkd3d_shader_code *spirv)
{
struct vkd3d_shader_spirv_target_info spirv_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_SPIRV_TARGET_INFO};
struct vkd3d_shader_interface_info interface_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_INTERFACE_INFO};
struct vkd3d_shader_hlsl_source_info hlsl_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_HLSL_SOURCE_INFO};
struct vkd3d_shader_compile_info info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_COMPILE_INFO};
struct vkd3d_shader_resource_binding bindings[MAX_RESOURCES + MAX_SAMPLERS];
struct vkd3d_shader_push_constant_buffer push_constants;
Giovanni Mascellani
committed
enum vkd3d_shader_spirv_extension spirv_extensions[2];
struct vkd3d_shader_resource_binding *binding;
struct vkd3d_shader_compile_option options[3];
struct vkd3d_shader_compile_option *option;
unsigned int i, compile_options;
char profile[7];
char *messages;
int ret;
static const char *const shader_models[] =
{
[SHADER_MODEL_2_0] = "2_0",
[SHADER_MODEL_3_0] = "3_0",
[SHADER_MODEL_4_0] = "4_0",
[SHADER_MODEL_4_1] = "4_1",
[SHADER_MODEL_5_0] = "5_0",
[SHADER_MODEL_5_1] = "5_1",
};
info.next = &hlsl_info;
info.source.code = source;
info.source.size = strlen(source);
info.source_type = VKD3D_SHADER_SOURCE_HLSL;
if (runner->r.minimum_shader_model < SHADER_MODEL_4_0)
info.target_type = VKD3D_SHADER_TARGET_D3D_BYTECODE;
else
info.target_type = VKD3D_SHADER_TARGET_DXBC_TPF;
info.log_level = VKD3D_SHADER_LOG_WARNING;
info.options = options;
info.option_count = 0;
option = &options[info.option_count++];
option->name = VKD3D_SHADER_COMPILE_OPTION_API_VERSION;
compile_options = runner->r.compile_options;
if (compile_options)
{
if (compile_options & (D3DCOMPILE_PACK_MATRIX_ROW_MAJOR | D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR))
{
option = &options[info.option_count++];
option->name = VKD3D_SHADER_COMPILE_OPTION_PACK_MATRIX_ORDER;
option->value = 0;
if (compile_options & D3DCOMPILE_PACK_MATRIX_ROW_MAJOR)
option->value |= VKD3D_SHADER_COMPILE_OPTION_PACK_MATRIX_ROW_MAJOR;
if (compile_options & D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR)
option->value |= VKD3D_SHADER_COMPILE_OPTION_PACK_MATRIX_COLUMN_MAJOR;
compile_options &= ~(D3DCOMPILE_PACK_MATRIX_ROW_MAJOR | D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR);
}
Nikolay Sivov
committed
/* FIXME: ignore compatibility flag for now */
if (compile_options & D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY)
compile_options &= ~D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY;
if (compile_options)
fatal_error("Unsupported compiler options %#x.\n", compile_options);
}
hlsl_info.entry_point = "main";
sprintf(profile, "%s_%s", type, shader_models[runner->r.minimum_shader_model]);
hlsl_info.profile = profile;
ret = vkd3d_shader_compile(&info, dxbc, &messages);
if (messages && vkd3d_test_state.debug_level)
trace("%s\n", messages);
vkd3d_shader_free_messages(messages);
if (ret)
return false;
info.next = &spirv_info;
info.source = *dxbc;
if (runner->r.minimum_shader_model < SHADER_MODEL_4_0)
info.source_type = VKD3D_SHADER_SOURCE_D3D_BYTECODE;
else
info.source_type = VKD3D_SHADER_SOURCE_DXBC_TPF;
info.target_type = VKD3D_SHADER_TARGET_SPIRV_BINARY;
option = &options[info.option_count++];
option->name = VKD3D_SHADER_COMPILE_OPTION_FEATURE;
option->value = shader_runner_caps_get_feature_flags(&runner->caps);
spirv_info.next = &interface_info;
spirv_info.environment = VKD3D_SHADER_SPIRV_ENVIRONMENT_VULKAN_1_0;
Giovanni Mascellani
committed
spirv_info.extensions = spirv_extensions;
spirv_info.extension_count = 0;
Giovanni Mascellani
committed
if (runner->caps.rov)
spirv_extensions[spirv_info.extension_count++] = VKD3D_SHADER_SPIRV_EXTENSION_EXT_FRAGMENT_SHADER_INTERLOCK;
if (runner->demote_to_helper_invocation)
spirv_extensions[spirv_info.extension_count++] = VKD3D_SHADER_SPIRV_EXTENSION_EXT_DEMOTE_TO_HELPER_INVOCATION;
push_constants.register_space = 0;
push_constants.register_index = 0;
push_constants.shader_visibility = VKD3D_SHADER_VISIBILITY_ALL;
push_constants.offset = 0;
push_constants.size = runner->r.uniform_count * sizeof(*runner->r.uniforms);
for (i = 0; i < runner->r.resource_count; ++i)
{
const struct vulkan_resource *resource = vulkan_resource(runner->r.resources[i]);
switch (resource->r.type)
{
case RESOURCE_TYPE_RENDER_TARGET:
case RESOURCE_TYPE_VERTEX_BUFFER:
break;
case RESOURCE_TYPE_TEXTURE:
case RESOURCE_TYPE_UAV:
binding = &bindings[interface_info.binding_count++];
if (resource->r.type == RESOURCE_TYPE_UAV)
binding->type = VKD3D_SHADER_DESCRIPTOR_TYPE_UAV;
else
binding->type = VKD3D_SHADER_DESCRIPTOR_TYPE_SRV;
binding->register_space = 0;
binding->register_index = resource->r.slot;
binding->shader_visibility = VKD3D_SHADER_VISIBILITY_ALL;
if (resource->r.dimension == RESOURCE_DIMENSION_BUFFER)
binding->flags = VKD3D_SHADER_BINDING_FLAG_BUFFER;
else
binding->flags = VKD3D_SHADER_BINDING_FLAG_IMAGE;
binding->binding.set = 0;
binding->binding.binding = resource->binding;
binding->binding.count = 1;
break;
}
}
for (i = 0; i < runner->r.sampler_count; ++i)
{
binding = &bindings[interface_info.binding_count++];
binding->type = VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER;
binding->register_space = 0;
binding->register_index = runner->r.samplers[i].slot;
binding->shader_visibility = VKD3D_SHADER_VISIBILITY_ALL;
binding->flags = VKD3D_SHADER_BINDING_FLAG_IMAGE;
binding->binding.set = 0;
binding->binding.binding = runner->samplers[i].binding;
binding->binding.count = 1;
}
interface_info.bindings = bindings;
interface_info.push_constant_buffer_count = 1;
interface_info.push_constant_buffers = &push_constants;
Elizabeth Figura
committed
if (!strcmp(type, "vs"))
{
interface_info.next = &runner->vs_signatures;
runner->vs_signatures.type = VKD3D_SHADER_STRUCTURE_TYPE_SCAN_SIGNATURE_INFO;
runner->vs_signatures.next = NULL;
}
ret = vkd3d_shader_compile(&info, spirv, &messages);
if (messages && vkd3d_test_state.debug_level)
trace("%s\n", messages);
vkd3d_shader_free_messages(messages);
if (ret)
return false;
return true;
}
Elizabeth Figura
committed
static bool create_shader_stage(struct vulkan_shader_runner *runner,
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
VkPipelineShaderStageCreateInfo *stage_info, const char *type, enum VkShaderStageFlagBits stage,
const char *source, struct vkd3d_shader_code *dxbc_ptr)
{
VkShaderModuleCreateInfo module_info = {.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
struct vkd3d_shader_code spirv, dxbc;
if (!dxbc_ptr)
dxbc_ptr = &dxbc;
if (!compile_shader(runner, source, type, dxbc_ptr, &spirv))
return false;
if (dxbc_ptr == &dxbc)
vkd3d_shader_free_shader_code(&dxbc);
memset(stage_info, 0, sizeof(*stage_info));
stage_info->sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage_info->stage = stage;
stage_info->pName = "main";
module_info.codeSize = spirv.size;
module_info.pCode = spirv.code;
VK_CALL(vkCreateShaderModule(runner->device, &module_info, NULL, &stage_info->module));
vkd3d_shader_free_shader_code(&spirv);
return true;
}
static VkPrimitiveTopology vulkan_primitive_topology_from_d3d(D3D_PRIMITIVE_TOPOLOGY topology)
{
switch (topology)
{
default:
fatal_error("Unhandled primitive topology %#x.\n", topology);
/* fall through */
case D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
}
}
static VkPipelineLayout create_pipeline_layout(const struct vulkan_shader_runner *runner,
VkDescriptorSetLayout set_layout)
{
VkPipelineLayoutCreateInfo layout_desc = {.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
VkPushConstantRange push_constant_range;
VkPipelineLayout pipeline_layout;
layout_desc.setLayoutCount = 1;
layout_desc.pSetLayouts = &set_layout;
if (runner->r.uniform_count)
{
layout_desc.pushConstantRangeCount = 1;
layout_desc.pPushConstantRanges = &push_constant_range;
push_constant_range.stageFlags = VK_SHADER_STAGE_ALL;
push_constant_range.offset = 0;
push_constant_range.size = runner->r.uniform_count * sizeof(*runner->r.uniforms);
}
VK_CALL(vkCreatePipelineLayout(runner->device, &layout_desc, NULL, &pipeline_layout));
return pipeline_layout;
}
static enum VkCompareOp vk_compare_op_from_d3d12(D3D12_COMPARISON_FUNC op)
{
switch (op)
{
case D3D12_COMPARISON_FUNC_NEVER:
return VK_COMPARE_OP_NEVER;
case D3D12_COMPARISON_FUNC_LESS:
return VK_COMPARE_OP_LESS;
case D3D12_COMPARISON_FUNC_EQUAL:
return VK_COMPARE_OP_EQUAL;
case D3D12_COMPARISON_FUNC_LESS_EQUAL:
return VK_COMPARE_OP_LESS_OR_EQUAL;
case D3D12_COMPARISON_FUNC_GREATER:
return VK_COMPARE_OP_GREATER;
case D3D12_COMPARISON_FUNC_NOT_EQUAL:
return VK_COMPARE_OP_NOT_EQUAL;
case D3D12_COMPARISON_FUNC_GREATER_EQUAL:
return VK_COMPARE_OP_GREATER_OR_EQUAL;
case D3D12_COMPARISON_FUNC_ALWAYS:
return VK_COMPARE_OP_ALWAYS;
default:
fatal_error("Unhandled compare op %#x.\n", op);
}
}
Elizabeth Figura
committed
static VkPipeline create_graphics_pipeline(struct vulkan_shader_runner *runner, VkRenderPass render_pass,
VkPipelineLayout pipeline_layout, D3D_PRIMITIVE_TOPOLOGY primitive_topology)
{
VkPipelineInputAssemblyStateCreateInfo ia_desc = {.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
VkPipelineRasterizationStateCreateInfo rs_desc = {.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
VkPipelineVertexInputStateCreateInfo input_desc = {.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
VkPipelineColorBlendStateCreateInfo blend_desc = {.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
VkPipelineMultisampleStateCreateInfo ms_desc = {.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
Henri Verbeet
committed
static const VkViewport viewport = {.y = RENDER_TARGET_HEIGHT,
.width = RENDER_TARGET_WIDTH, .height = -RENDER_TARGET_HEIGHT, .maxDepth = 1};
VkPipelineViewportStateCreateInfo vp_desc = {.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
static const VkRect2D rt_rect = {.extent.width = RENDER_TARGET_WIDTH, .extent.height = RENDER_TARGET_HEIGHT};
VkGraphicsPipelineCreateInfo pipeline_desc = {.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
VkPipelineColorBlendAttachmentState attachment_desc[MAX_RESOURCES] = {0};
VkPipelineTessellationStateCreateInfo tessellation_info;
VkVertexInputAttributeDescription input_attributes[32];
VkPipelineDepthStencilStateCreateInfo ds_desc = {0};
VkVertexInputBindingDescription input_bindings[32];
VkPipelineShaderStageCreateInfo stage_desc[4];
struct vkd3d_shader_code vs_dxbc;
VkDevice device = runner->device;
unsigned int stage_count = 0;
VkPipeline pipeline;
unsigned int i, j;
memset(stage_desc, 0, sizeof(stage_desc));
ret = create_shader_stage(runner, &stage_desc[stage_count++], "vs",
VK_SHADER_STAGE_VERTEX_BIT, runner->r.vs_source, &vs_dxbc);
ret &= create_shader_stage(runner, &stage_desc[stage_count++], "ps",
VK_SHADER_STAGE_FRAGMENT_BIT, runner->r.ps_source, NULL);
if (runner->r.hs_source)
{
ret &= create_shader_stage(runner, &stage_desc[stage_count++], "hs",
VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, runner->r.hs_source, NULL);
ret &= create_shader_stage(runner, &stage_desc[stage_count++], "ds",
VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, runner->r.ds_source, NULL);
todo_if (runner->r.is_todo) ok(ret, "Failed to compile shaders.\n");
if (!ret)
for (i = 0; i < ARRAY_SIZE(stage_desc); ++i)
VK_CALL(vkDestroyShaderModule(device, stage_desc[i].module, NULL));
return VK_NULL_HANDLE;
}
if (runner->r.input_element_count > ARRAY_SIZE(input_attributes))
fatal_error("Input element count %zu is too high.\n", runner->r.input_element_count);
for (i = 0; i < runner->r.input_element_count; ++i)
{
VkVertexInputAttributeDescription *attribute = &input_attributes[i];
const struct input_element *element = &runner->r.input_elements[i];
const struct vkd3d_shader_signature_element *signature_element;
Elizabeth Figura
committed
signature_element = vkd3d_shader_find_signature_element(&runner->vs_signatures.input, element->name, element->index, 0);
ok(signature_element, "Cannot find signature element %s%u.\n", element->name, element->index);
attribute->location = signature_element->register_index;
attribute->binding = element->slot;
attribute->format = vkd3d_get_vk_format(element->format);
/* The offset will be filled below. */
}
input_desc.vertexAttributeDescriptionCount = runner->r.input_element_count;
input_desc.pVertexAttributeDescriptions = input_attributes;
input_desc.pVertexBindingDescriptions = input_bindings;
blend_desc.attachmentCount = 0;
blend_desc.pAttachments = attachment_desc;
for (i = 0; i < runner->r.resource_count; ++i)
{
const struct vulkan_resource *resource = vulkan_resource(runner->r.resources[i]);
switch (resource->r.type)
{
case RESOURCE_TYPE_TEXTURE:
case RESOURCE_TYPE_UAV:
case RESOURCE_TYPE_RENDER_TARGET:
attachment_desc[blend_desc.attachmentCount++].colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
| VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
break;
case RESOURCE_TYPE_DEPTH_STENCIL:
ds_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
ds_desc.pNext = NULL;
ds_desc.flags = 0;
ds_desc.depthTestEnable = VK_TRUE;
ds_desc.depthWriteEnable = VK_TRUE;
ds_desc.depthCompareOp = vk_compare_op_from_d3d12(runner->r.depth_func);
ds_desc.depthBoundsTestEnable = VK_FALSE;
ds_desc.stencilTestEnable = VK_FALSE;
ds_desc.minDepthBounds = 0.0f;
ds_desc.maxDepthBounds = 1.0f;
pipeline_desc.pDepthStencilState = &ds_desc;
break;
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
case RESOURCE_TYPE_VERTEX_BUFFER:
{
VkVertexInputBindingDescription *binding = &input_bindings[input_desc.vertexBindingDescriptionCount++];
binding->binding = resource->r.slot;
binding->stride = 0;
binding->inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
for (j = 0; j < runner->r.input_element_count; ++j)
{
if (runner->r.input_elements[j].slot == resource->r.slot)
{
input_attributes[j].offset = binding->stride;
binding->stride += runner->r.input_elements[j].texel_size;
}
}
break;
}
}
}
ia_desc.topology = vulkan_primitive_topology_from_d3d(primitive_topology);
vp_desc.viewportCount = 1;
vp_desc.pViewports = &viewport;
vp_desc.scissorCount = 1;
vp_desc.pScissors = &rt_rect;
rs_desc.cullMode = VK_CULL_MODE_NONE;
Henri Verbeet
committed
rs_desc.frontFace = VK_FRONT_FACE_CLOCKWISE;
rs_desc.lineWidth = 1.0f;
ms_desc.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
pipeline_desc.stageCount = stage_count;
pipeline_desc.pStages = stage_desc;
pipeline_desc.pVertexInputState = &input_desc;
pipeline_desc.pInputAssemblyState = &ia_desc;
pipeline_desc.pViewportState = &vp_desc;
pipeline_desc.pRasterizationState = &rs_desc;
pipeline_desc.pMultisampleState = &ms_desc;
pipeline_desc.pColorBlendState = &blend_desc;
pipeline_desc.layout = pipeline_layout;
Elizabeth Figura
committed
pipeline_desc.renderPass = render_pass;
pipeline_desc.subpass = 0;
if (runner->r.hs_source)
{
tessellation_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
tessellation_info.pNext = NULL;
tessellation_info.flags = 0;
tessellation_info.patchControlPoints
= max(primitive_topology - D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + 1, 1);
pipeline_desc.pTessellationState = &tessellation_info;
}
vr = VK_CALL(vkCreateGraphicsPipelines(runner->device, VK_NULL_HANDLE, 1, &pipeline_desc, NULL, &pipeline));
ok(vr == VK_SUCCESS, "Failed to create graphics pipeline, vr %d.\n", vr);
for (i = 0; i < ARRAY_SIZE(stage_desc); ++i)
VK_CALL(vkDestroyShaderModule(device, stage_desc[i].module, NULL));
Elizabeth Figura
committed
vkd3d_shader_free_scan_signature_info(&runner->vs_signatures);
vkd3d_shader_free_shader_code(&vs_dxbc);
return pipeline;
}
Elizabeth Figura
committed
static VkPipeline create_compute_pipeline(struct vulkan_shader_runner *runner, VkPipelineLayout pipeline_layout)
{
VkComputePipelineCreateInfo pipeline_desc = {.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO};
VkPipeline pipeline;
bool ret;
ret = create_shader_stage(runner, &pipeline_desc.stage, "cs",
VK_SHADER_STAGE_COMPUTE_BIT, runner->r.cs_source, NULL);
todo_if (runner->r.is_todo) ok(ret, "Failed to compile shader.\n");
if (!ret)
return VK_NULL_HANDLE;
pipeline_desc.layout = pipeline_layout;
VK_CALL(vkCreateComputePipelines(runner->device, VK_NULL_HANDLE, 1, &pipeline_desc, NULL, &pipeline));
VK_CALL(vkDestroyShaderModule(runner->device, pipeline_desc.stage.module, NULL));
return pipeline;
}
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
static VkSamplerAddressMode vk_address_mode_from_d3d12(D3D12_TEXTURE_ADDRESS_MODE mode)
{
switch (mode)
{
case D3D12_TEXTURE_ADDRESS_MODE_WRAP:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case D3D12_TEXTURE_ADDRESS_MODE_MIRROR:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
case D3D12_TEXTURE_ADDRESS_MODE_CLAMP:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case D3D12_TEXTURE_ADDRESS_MODE_BORDER:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
default:
fatal_error("Unhandled sampler address mode %#x.\n", mode);
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
}
}
static VkDescriptorSetLayout create_descriptor_set_layout(struct vulkan_shader_runner *runner)
{
VkDescriptorSetLayoutCreateInfo set_desc = {.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
VkDescriptorSetLayoutBinding bindings[MAX_RESOURCES + MAX_SAMPLERS];
VkDescriptorSetLayoutBinding *binding;
VkDescriptorSetLayout set_layout;
uint32_t binding_index = 0;
size_t i;
if (runner->r.resource_count > ARRAY_SIZE(bindings))
fatal_error("Resource count %zu is too high.\n", runner->r.resource_count);
set_desc.pBindings = bindings;
for (i = 0; i < runner->r.resource_count; ++i)
{
struct vulkan_resource *resource = vulkan_resource(runner->r.resources[i]);
switch (resource->r.type)
{
case RESOURCE_TYPE_RENDER_TARGET:
case RESOURCE_TYPE_VERTEX_BUFFER:
break;
case RESOURCE_TYPE_TEXTURE:
case RESOURCE_TYPE_UAV:
binding = &bindings[set_desc.bindingCount++];
resource->binding = binding_index++;
binding->binding = resource->binding;
if (resource->r.type == RESOURCE_TYPE_UAV)
{
if (resource->r.dimension == RESOURCE_DIMENSION_BUFFER)
binding->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
else
binding->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
}
{
if (resource->r.dimension == RESOURCE_DIMENSION_BUFFER)
binding->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
else
binding->descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
}
binding->descriptorCount = 1;
binding->stageFlags = VK_SHADER_STAGE_ALL;
binding->pImmutableSamplers = NULL;
break;
}
}
for (i = 0; i < runner->r.sampler_count; ++i)
{
VkSamplerCreateInfo sampler_desc = {.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
struct vulkan_sampler *vulkan_sampler = &runner->samplers[i];
const struct sampler *sampler = &runner->r.samplers[i];
sampler_desc.magFilter = (sampler->filter & 0x4) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
sampler_desc.minFilter = (sampler->filter & 0x1) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
sampler_desc.mipmapMode = (sampler->filter & 0x10) ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST;
sampler_desc.addressModeU = vk_address_mode_from_d3d12(sampler->u_address);
sampler_desc.addressModeV = vk_address_mode_from_d3d12(sampler->v_address);
sampler_desc.addressModeW = vk_address_mode_from_d3d12(sampler->w_address);
sampler_desc.compareEnable = !!sampler->func;
sampler_desc.compareOp = sampler->func ? vk_compare_op_from_d3d12(sampler->func) : 0;
sampler_desc.maxLod = FLT_MAX;
VK_CALL(vkCreateSampler(runner->device, &sampler_desc, NULL, &vulkan_sampler->vk_sampler));
vulkan_sampler->binding = binding_index++;
binding = &bindings[set_desc.bindingCount++];
binding->binding = vulkan_sampler->binding;
binding->descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
binding->descriptorCount = 1;
binding->stageFlags = VK_SHADER_STAGE_ALL;
binding->pImmutableSamplers = &vulkan_sampler->vk_sampler;