vkd3d-proton/libs/vkd3d-shader/dxil.c

1222 lines
48 KiB
C

/*
* Copyright 2020 Hans-Kristian Arntzen for Valve Corporation
*
* 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
*/
#define VKD3D_DBG_CHANNEL VKD3D_DBG_CHANNEL_SHADER
#include "vkd3d_shader_private.h"
#include "vkd3d_utf8.h"
#include "vkd3d_string.h"
#include <inttypes.h>
#include <stdio.h>
#include <dxil_spirv_c.h>
static bool dxil_match_shader_visibility(enum vkd3d_shader_visibility visibility,
dxil_spv_shader_stage stage)
{
if (visibility == VKD3D_SHADER_VISIBILITY_ALL)
return true;
switch (stage)
{
case DXIL_SPV_STAGE_VERTEX:
return visibility == VKD3D_SHADER_VISIBILITY_VERTEX;
case DXIL_SPV_STAGE_HULL:
return visibility == VKD3D_SHADER_VISIBILITY_HULL;
case DXIL_SPV_STAGE_DOMAIN:
return visibility == VKD3D_SHADER_VISIBILITY_DOMAIN;
case DXIL_SPV_STAGE_GEOMETRY:
return visibility == VKD3D_SHADER_VISIBILITY_GEOMETRY;
case DXIL_SPV_STAGE_PIXEL:
return visibility == VKD3D_SHADER_VISIBILITY_PIXEL;
case DXIL_SPV_STAGE_COMPUTE:
return visibility == VKD3D_SHADER_VISIBILITY_COMPUTE;
default:
return false;
}
}
static unsigned dxil_resource_flags_from_kind(dxil_spv_resource_kind kind, bool ssbo)
{
switch (kind)
{
case DXIL_SPV_RESOURCE_KIND_RAW_BUFFER:
case DXIL_SPV_RESOURCE_KIND_STRUCTURED_BUFFER:
if (ssbo)
return VKD3D_SHADER_BINDING_FLAG_BUFFER | VKD3D_SHADER_BINDING_FLAG_RAW_SSBO;
else
return VKD3D_SHADER_BINDING_FLAG_BUFFER;
case DXIL_SPV_RESOURCE_KIND_TYPED_BUFFER:
return VKD3D_SHADER_BINDING_FLAG_BUFFER;
case DXIL_SPV_RESOURCE_KIND_RT_ACCELERATION_STRUCTURE:
/* Acceleration structures use aux buffer to store raw AS pointers. */
return VKD3D_SHADER_BINDING_FLAG_AUX_BUFFER;
default:
return VKD3D_SHADER_BINDING_FLAG_IMAGE;
}
}
static bool dxil_resource_is_in_range(const struct vkd3d_shader_resource_binding *binding,
const dxil_spv_d3d_binding *d3d_binding)
{
if (binding->register_space != d3d_binding->register_space)
return false;
if (d3d_binding->register_index < binding->register_index)
return false;
return binding->register_count == UINT_MAX ||
((d3d_binding->register_index - binding->register_index) < binding->register_count);
}
static bool vkd3d_shader_binding_is_root_descriptor(const struct vkd3d_shader_resource_binding *binding)
{
const uint32_t relevant_flags = VKD3D_SHADER_BINDING_FLAG_RAW_VA |
VKD3D_SHADER_BINDING_FLAG_AUX_BUFFER;
const uint32_t expected_flags = VKD3D_SHADER_BINDING_FLAG_RAW_VA;
return (binding->flags & relevant_flags) == expected_flags;
}
struct vkd3d_dxil_remap_userdata
{
const struct vkd3d_shader_interface_info *shader_interface_info;
const struct vkd3d_shader_interface_local_info *shader_interface_local_info;
unsigned int num_root_descriptors;
};
struct vkd3d_dxil_remap_info
{
const struct vkd3d_shader_resource_binding *bindings;
unsigned int binding_count;
unsigned int num_root_descriptors;
unsigned int descriptor_table_offset_words;
};
static dxil_spv_bool dxil_remap_inner(
const struct vkd3d_dxil_remap_info *remap,
enum vkd3d_shader_descriptor_type descriptor_type,
const dxil_spv_d3d_binding *d3d_binding,
dxil_spv_vulkan_binding *vk_binding,
uint32_t resource_flags)
{
unsigned int root_descriptor_index = 0;
unsigned int i;
for (i = 0; i < remap->binding_count; i++)
{
const struct vkd3d_shader_resource_binding *binding = &remap->bindings[i];
const uint32_t mask = ~(VKD3D_SHADER_BINDING_FLAG_BINDLESS | VKD3D_SHADER_BINDING_FLAG_RAW_VA);
uint32_t match_flags = binding->flags & mask;
if (binding->type == descriptor_type &&
dxil_resource_is_in_range(binding, d3d_binding) &&
(match_flags & resource_flags) == resource_flags &&
dxil_match_shader_visibility(binding->shader_visibility, d3d_binding->stage))
{
memset(vk_binding, 0, sizeof(*vk_binding));
if (vkd3d_shader_binding_is_root_descriptor(binding))
{
vk_binding->descriptor_type = DXIL_SPV_VULKAN_DESCRIPTOR_TYPE_BUFFER_DEVICE_ADDRESS;
vk_binding->root_constant_index = root_descriptor_index;
}
else if (binding->flags & VKD3D_SHADER_BINDING_FLAG_BINDLESS)
{
vk_binding->bindless.use_heap = DXIL_SPV_TRUE;
vk_binding->bindless.heap_root_offset = binding->descriptor_offset +
d3d_binding->register_index - binding->register_index;
vk_binding->root_constant_index = binding->descriptor_table + remap->descriptor_table_offset_words;
vk_binding->set = binding->binding.set;
vk_binding->binding = binding->binding.binding;
if (vk_binding->root_constant_index < 2 * remap->num_root_descriptors)
{
ERR("Bindless push constant table offset is impossible. %u < 2 * %u\n",
vk_binding->root_constant_index, remap->num_root_descriptors);
return DXIL_SPV_FALSE;
}
vk_binding->root_constant_index -= 2 * remap->num_root_descriptors;
/* Acceleration structures are mapped to SSBO uvec2[] array instead of normal heap. */
if (d3d_binding->kind == DXIL_SPV_RESOURCE_KIND_RT_ACCELERATION_STRUCTURE)
vk_binding->descriptor_type = DXIL_SPV_VULKAN_DESCRIPTOR_TYPE_SSBO;
}
else
{
vk_binding->set = binding->binding.set;
vk_binding->binding = binding->binding.binding + d3d_binding->register_index - binding->register_index;
}
return DXIL_SPV_TRUE;
}
if (vkd3d_shader_binding_is_root_descriptor(binding))
root_descriptor_index++;
}
return DXIL_SPV_FALSE;
}
static dxil_spv_bool dxil_remap(const struct vkd3d_dxil_remap_userdata *remap,
enum vkd3d_shader_descriptor_type descriptor_type, const struct dxil_spv_d3d_binding *d3d_binding,
struct dxil_spv_vulkan_binding *vk_binding, unsigned int resource_flags)
{
const struct vkd3d_shader_interface_local_info *shader_interface_local_info;
const struct vkd3d_shader_interface_info *shader_interface_info;
struct vkd3d_dxil_remap_info remap_info;
shader_interface_info = remap->shader_interface_info;
shader_interface_local_info = remap->shader_interface_local_info;
remap_info.bindings = shader_interface_info->bindings;
remap_info.binding_count = shader_interface_info->binding_count;
remap_info.descriptor_table_offset_words = shader_interface_info->descriptor_tables.offset / sizeof(uint32_t);
remap_info.num_root_descriptors = remap->num_root_descriptors;
if (!dxil_remap_inner(&remap_info, descriptor_type, d3d_binding, vk_binding, resource_flags))
{
if (shader_interface_local_info)
{
/* This fallback is relevant only so that we can report
* set/binding for bindless record buffer TABLE descriptor.
* Root descriptor and constants are resolved internally in dxil-spirv. */
remap_info.bindings = shader_interface_local_info->bindings;
remap_info.binding_count = shader_interface_local_info->binding_count;
/* Not relevant. */
remap_info.descriptor_table_offset_words = 0;
remap_info.num_root_descriptors = 0;
return dxil_remap_inner(&remap_info, descriptor_type, d3d_binding, vk_binding, resource_flags);
}
else
return DXIL_SPV_FALSE;
}
else
return DXIL_SPV_TRUE;
}
static dxil_spv_bool dxil_srv_remap(void *userdata, const dxil_spv_d3d_binding *d3d_binding,
dxil_spv_srv_vulkan_binding *vk_binding)
{
const struct vkd3d_shader_interface_info *shader_interface_info;
const struct vkd3d_dxil_remap_userdata *remap = userdata;
unsigned int resource_flags, resource_flags_ssbo;
bool use_ssbo;
shader_interface_info = remap->shader_interface_info;
resource_flags_ssbo = dxil_resource_flags_from_kind(d3d_binding->kind, true);
resource_flags = dxil_resource_flags_from_kind(d3d_binding->kind, false);
use_ssbo = resource_flags_ssbo != resource_flags;
if (use_ssbo && dxil_remap(remap, VKD3D_SHADER_DESCRIPTOR_TYPE_SRV,
d3d_binding, &vk_binding->buffer_binding, resource_flags_ssbo))
{
vk_binding->buffer_binding.descriptor_type = DXIL_SPV_VULKAN_DESCRIPTOR_TYPE_SSBO;
if (shader_interface_info->flags & VKD3D_SHADER_INTERFACE_SSBO_OFFSET_BUFFER)
{
vk_binding->offset_binding.set = shader_interface_info->offset_buffer_binding->set;
vk_binding->offset_binding.binding = shader_interface_info->offset_buffer_binding->binding;
}
return DXIL_SPV_TRUE;
}
else
{
vk_binding->buffer_binding.descriptor_type = DXIL_SPV_VULKAN_DESCRIPTOR_TYPE_TEXEL_BUFFER;
if (shader_interface_info->flags & VKD3D_SHADER_INTERFACE_TYPED_OFFSET_BUFFER)
{
vk_binding->offset_binding.set = shader_interface_info->offset_buffer_binding->set;
vk_binding->offset_binding.binding = shader_interface_info->offset_buffer_binding->binding;
}
}
return dxil_remap(remap, VKD3D_SHADER_DESCRIPTOR_TYPE_SRV,
d3d_binding, &vk_binding->buffer_binding, resource_flags);
}
static dxil_spv_bool dxil_sampler_remap(void *userdata, const dxil_spv_d3d_binding *d3d_binding,
dxil_spv_vulkan_binding *vk_binding)
{
const struct vkd3d_dxil_remap_userdata *remap = userdata;
return dxil_remap(remap, VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER,
d3d_binding, vk_binding, VKD3D_SHADER_BINDING_FLAG_IMAGE);
}
static dxil_spv_bool dxil_input_remap(void *userdata, const dxil_spv_d3d_vertex_input *d3d_input,
dxil_spv_vulkan_vertex_input *vk_input)
{
(void)userdata;
vk_input->location = d3d_input->start_row;
return DXIL_SPV_TRUE;
}
static dxil_spv_bool dxil_output_remap(void *userdata, const dxil_spv_d3d_stream_output *d3d_output,
dxil_spv_vulkan_stream_output *vk_output)
{
const struct vkd3d_shader_transform_feedback_info *xfb_info = userdata;
const struct vkd3d_shader_transform_feedback_element *xfb_element;
unsigned int i, offset, stride;
offset = 0;
xfb_element = NULL;
for (i = 0; i < xfb_info->element_count; ++i)
{
const struct vkd3d_shader_transform_feedback_element *e = &xfb_info->elements[i];
/* TODO: Stream index matching? */
if (!ascii_strcasecmp(e->semantic_name, d3d_output->semantic) && e->semantic_index == d3d_output->semantic_index)
{
xfb_element = e;
break;
}
offset += 4 * e->component_count;
}
if (!xfb_element)
{
vk_output->enable = DXIL_SPV_FALSE;
return DXIL_SPV_TRUE;
}
if (xfb_element->output_slot < xfb_info->buffer_stride_count)
{
stride = xfb_info->buffer_strides[xfb_element->output_slot];
}
else
{
stride = 0;
for (i = 0; i < xfb_info->element_count; ++i)
{
const struct vkd3d_shader_transform_feedback_element *e = &xfb_info->elements[i];
if (e->stream_index == xfb_element->stream_index && e->output_slot == xfb_element->output_slot)
stride += 4 * e->component_count;
}
}
vk_output->enable = DXIL_SPV_TRUE;
vk_output->offset = offset;
vk_output->stride = stride;
vk_output->buffer_index = xfb_element->output_slot;
return DXIL_SPV_TRUE;
}
static dxil_spv_bool dxil_uav_remap(void *userdata, const dxil_spv_uav_d3d_binding *d3d_binding,
dxil_spv_uav_vulkan_binding *vk_binding)
{
const struct vkd3d_shader_interface_info *shader_interface_info;
const struct vkd3d_dxil_remap_userdata *remap = userdata;
unsigned int resource_flags, resource_flags_ssbo;
bool use_ssbo;
shader_interface_info = remap->shader_interface_info;
resource_flags_ssbo = dxil_resource_flags_from_kind(d3d_binding->d3d_binding.kind, true);
resource_flags = dxil_resource_flags_from_kind(d3d_binding->d3d_binding.kind, false);
use_ssbo = resource_flags != resource_flags_ssbo;
if (use_ssbo)
{
if (dxil_remap(remap, VKD3D_SHADER_DESCRIPTOR_TYPE_UAV, &d3d_binding->d3d_binding,
&vk_binding->buffer_binding, resource_flags_ssbo))
{
vk_binding->buffer_binding.descriptor_type = DXIL_SPV_VULKAN_DESCRIPTOR_TYPE_SSBO;
if (shader_interface_info->flags & VKD3D_SHADER_INTERFACE_SSBO_OFFSET_BUFFER)
{
vk_binding->offset_binding.set = shader_interface_info->offset_buffer_binding->set;
vk_binding->offset_binding.binding = shader_interface_info->offset_buffer_binding->binding;
}
}
else if (!dxil_remap(remap, VKD3D_SHADER_DESCRIPTOR_TYPE_UAV, &d3d_binding->d3d_binding,
&vk_binding->buffer_binding, resource_flags))
{
return DXIL_SPV_FALSE;
}
else if (vk_binding->buffer_binding.descriptor_type != DXIL_SPV_VULKAN_DESCRIPTOR_TYPE_BUFFER_DEVICE_ADDRESS)
{
/* By default, we use TEXEL_BUFFER unless dxil_remap remaps it to BDA.
* We won't trigger SSBO path when using BDA. */
vk_binding->buffer_binding.descriptor_type = DXIL_SPV_VULKAN_DESCRIPTOR_TYPE_TEXEL_BUFFER;
if (shader_interface_info->flags & VKD3D_SHADER_INTERFACE_TYPED_OFFSET_BUFFER)
{
vk_binding->offset_binding.set = shader_interface_info->offset_buffer_binding->set;
vk_binding->offset_binding.binding = shader_interface_info->offset_buffer_binding->binding;
}
}
}
else
{
vk_binding->buffer_binding.descriptor_type = DXIL_SPV_VULKAN_DESCRIPTOR_TYPE_TEXEL_BUFFER;
if (!dxil_remap(remap, VKD3D_SHADER_DESCRIPTOR_TYPE_UAV, &d3d_binding->d3d_binding,
&vk_binding->buffer_binding, resource_flags))
{
return DXIL_SPV_FALSE;
}
if (shader_interface_info->flags & VKD3D_SHADER_INTERFACE_TYPED_OFFSET_BUFFER)
{
vk_binding->offset_binding.set = shader_interface_info->offset_buffer_binding->set;
vk_binding->offset_binding.binding = shader_interface_info->offset_buffer_binding->binding;
}
}
if (d3d_binding->has_counter)
{
if (!dxil_remap(remap, VKD3D_SHADER_DESCRIPTOR_TYPE_UAV, &d3d_binding->d3d_binding,
&vk_binding->counter_binding, VKD3D_SHADER_BINDING_FLAG_AUX_BUFFER))
{
return DXIL_SPV_FALSE;
}
}
return DXIL_SPV_TRUE;
}
static dxil_spv_bool dxil_cbv_remap(void *userdata, const dxil_spv_d3d_binding *d3d_binding,
dxil_spv_cbv_vulkan_binding *vk_binding)
{
const struct vkd3d_shader_interface_info *shader_interface_info;
const struct vkd3d_dxil_remap_userdata *remap = userdata;
unsigned int i;
shader_interface_info = remap->shader_interface_info;
/* Try to map to root constant -> push constant.
* Do not consider shader visibility here, as DXBC path does not appear to do it either. */
for (i = 0; i < shader_interface_info->push_constant_buffer_count; i++)
{
const struct vkd3d_shader_push_constant_buffer *push = &shader_interface_info->push_constant_buffers[i];
if (push->register_space == d3d_binding->register_space &&
push->register_index == d3d_binding->register_index)
{
memset(vk_binding, 0, sizeof(*vk_binding));
vk_binding->push_constant = DXIL_SPV_TRUE;
vk_binding->vulkan.push_constant.offset_in_words = push->offset / sizeof(uint32_t);
if (vk_binding->vulkan.push_constant.offset_in_words < remap->num_root_descriptors * 2)
{
ERR("Root descriptor offset of %u is impossible with %u root descriptors.\n",
vk_binding->vulkan.push_constant.offset_in_words, remap->num_root_descriptors);
return DXIL_SPV_FALSE;
}
vk_binding->vulkan.push_constant.offset_in_words -= remap->num_root_descriptors * 2;
return DXIL_SPV_TRUE;
}
}
vk_binding->push_constant = DXIL_SPV_FALSE;
return dxil_remap(remap, VKD3D_SHADER_DESCRIPTOR_TYPE_CBV,
d3d_binding, &vk_binding->vulkan.uniform_binding,
VKD3D_SHADER_BINDING_FLAG_BUFFER);
}
static void vkd3d_dxil_log_callback(void *userdata, dxil_spv_log_level level, const char *msg)
{
/* msg already has a newline. */
(void)userdata;
switch (level)
{
case DXIL_SPV_LOG_LEVEL_ERROR:
ERR("dxil-spirv: %s", msg);
break;
case DXIL_SPV_LOG_LEVEL_WARN:
WARN("dxil-spirv: %s", msg);
break;
default:
case DXIL_SPV_LOG_LEVEL_DEBUG:
TRACE("dxil-spirv: %s", msg);
break;
}
}
int vkd3d_shader_compile_dxil(const struct vkd3d_shader_code *dxbc,
struct vkd3d_shader_code *spirv,
const struct vkd3d_shader_interface_info *shader_interface_info,
const struct vkd3d_shader_compile_arguments *compiler_args)
{
const struct vkd3d_shader_transform_feedback_info *xfb_info;
struct vkd3d_dxil_remap_userdata remap_userdata;
unsigned int non_raw_va_binding_count = 0;
unsigned int raw_va_binding_count = 0;
unsigned int num_root_descriptors = 0;
unsigned int root_constant_words = 0;
dxil_spv_converter converter = NULL;
dxil_spv_parsed_blob blob = NULL;
dxil_spv_compiled_spirv compiled;
unsigned int i, max_size;
vkd3d_shader_hash_t hash;
int ret = VKD3D_OK;
void *code;
dxil_spv_set_thread_log_callback(vkd3d_dxil_log_callback, NULL);
hash = vkd3d_shader_hash(dxbc);
spirv->meta.replaced = false;
spirv->meta.hash = hash;
if (vkd3d_shader_replace(hash, &spirv->code, &spirv->size))
{
spirv->meta.replaced = true;
return ret;
}
dxil_spv_begin_thread_allocator_context();
vkd3d_shader_dump_shader(hash, dxbc, "dxil");
if (dxil_spv_parse_dxil_blob(dxbc->code, dxbc->size, &blob) != DXIL_SPV_SUCCESS)
{
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
if (dxil_spv_create_converter(blob, &converter) != DXIL_SPV_SUCCESS)
{
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
/* Figure out how many words we need for push constants. */
for (i = 0; i < shader_interface_info->push_constant_buffer_count; i++)
{
max_size = shader_interface_info->push_constant_buffers[i].offset +
shader_interface_info->push_constant_buffers[i].size;
max_size = (max_size + 3) / 4;
if (max_size > root_constant_words)
root_constant_words = max_size;
}
max_size = shader_interface_info->descriptor_tables.offset / sizeof(uint32_t) +
shader_interface_info->descriptor_tables.count;
if (max_size > root_constant_words)
root_constant_words = max_size;
for (i = 0; i < shader_interface_info->binding_count; i++)
{
/* Bindless UAV counters are implemented as physical storage buffer pointers.
* For simplicity, dxil-spirv only accepts either fully RAW VA, or all non-raw VA. */
if ((shader_interface_info->bindings[i].flags &
(VKD3D_SHADER_BINDING_FLAG_AUX_BUFFER | VKD3D_SHADER_BINDING_FLAG_BINDLESS)) ==
(VKD3D_SHADER_BINDING_FLAG_AUX_BUFFER | VKD3D_SHADER_BINDING_FLAG_BINDLESS))
{
if (shader_interface_info->bindings[i].flags & VKD3D_SHADER_BINDING_FLAG_RAW_VA)
raw_va_binding_count++;
else
non_raw_va_binding_count++;
}
if (vkd3d_shader_binding_is_root_descriptor(&shader_interface_info->bindings[i]))
num_root_descriptors++;
}
if (raw_va_binding_count && non_raw_va_binding_count)
{
ERR("dxil-spirv currently cannot mix and match bindless UAV counters with RAW VA and texel buffer.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
/* Root constants come after root descriptors. Offset the counts. */
if (root_constant_words < num_root_descriptors * 2)
root_constant_words = num_root_descriptors * 2;
root_constant_words -= num_root_descriptors * 2;
{
const struct dxil_spv_option_ssbo_alignment helper =
{ { DXIL_SPV_OPTION_SSBO_ALIGNMENT }, shader_interface_info->min_ssbo_alignment };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support SSBO_ALIGNMENT.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
if (shader_interface_info->flags & VKD3D_SHADER_INTERFACE_PUSH_CONSTANTS_AS_UNIFORM_BUFFER)
{
const struct dxil_spv_option_root_constant_inline_uniform_block helper =
{ { DXIL_SPV_OPTION_ROOT_CONSTANT_INLINE_UNIFORM_BLOCK },
shader_interface_info->push_constant_ubo_binding->set,
shader_interface_info->push_constant_ubo_binding->binding,
DXIL_SPV_TRUE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support PUSH_CONSTANTS_AS_UNIFORM_BUFFER.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
if (shader_interface_info->flags & VKD3D_SHADER_INTERFACE_BINDLESS_CBV_AS_STORAGE_BUFFER)
{
static const struct dxil_spv_option_bindless_cbv_ssbo_emulation helper =
{ { DXIL_SPV_OPTION_BINDLESS_CBV_SSBO_EMULATION },
DXIL_SPV_TRUE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support BINDLESS_CBV_AS_STORAGE_BUFFER.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
{
const struct dxil_spv_option_physical_storage_buffer helper =
{ { DXIL_SPV_OPTION_PHYSICAL_STORAGE_BUFFER },
raw_va_binding_count || num_root_descriptors ? DXIL_SPV_TRUE : DXIL_SPV_FALSE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support PHYSICAL_STORAGE_BUFFER.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
if (shader_interface_info->flags & VKD3D_SHADER_INTERFACE_TYPED_OFFSET_BUFFER)
{
const struct dxil_spv_option_bindless_typed_buffer_offsets helper =
{ { DXIL_SPV_OPTION_BINDLESS_TYPED_BUFFER_OFFSETS },
DXIL_SPV_TRUE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support BINDLESS_TYPED_BUFFER_OFFSETS.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
{
const struct dxil_spv_option_bindless_offset_buffer_layout helper =
{ { DXIL_SPV_OPTION_BINDLESS_OFFSET_BUFFER_LAYOUT },
0, 1, 2 };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support BINDLESS_OFFSET_BUFFER_LAYOUT.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
{
char buffer[16 + 5 + 1];
const struct dxil_spv_option_shader_source_file helper =
{ { DXIL_SPV_OPTION_SHADER_SOURCE_FILE }, buffer };
sprintf(buffer, "%016"PRIx64".dxil", spirv->meta.hash);
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
WARN("dxil-spirv does not support SHADER_SOURCE_FILE.\n");
}
if (compiler_args)
{
for (i = 0; i < compiler_args->target_extension_count; i++)
{
if (compiler_args->target_extensions[i] == VKD3D_SHADER_TARGET_EXTENSION_SPV_EXT_DEMOTE_TO_HELPER_INVOCATION)
{
static const dxil_spv_option_shader_demote_to_helper helper =
{ { DXIL_SPV_OPTION_SHADER_DEMOTE_TO_HELPER }, DXIL_SPV_TRUE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
WARN("dxil-spirv does not support DEMOTE_TO_HELPER. Slower path will be used.\n");
}
}
else if (compiler_args->target_extensions[i] == VKD3D_SHADER_TARGET_EXTENSION_READ_STORAGE_IMAGE_WITHOUT_FORMAT)
{
static const dxil_spv_option_typed_uav_read_without_format helper =
{ { DXIL_SPV_OPTION_TYPED_UAV_READ_WITHOUT_FORMAT }, DXIL_SPV_TRUE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support TYPED_UAV_READ_WITHOUT_FORMAT.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
}
if (compiler_args->dual_source_blending)
{
static const dxil_spv_option_dual_source_blending helper =
{ { DXIL_SPV_OPTION_DUAL_SOURCE_BLENDING }, DXIL_SPV_TRUE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support DUAL_SOURCE_BLENDING.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
if (compiler_args->output_swizzle_count != 0)
{
const dxil_spv_option_output_swizzle helper =
{ { DXIL_SPV_OPTION_OUTPUT_SWIZZLE }, compiler_args->output_swizzles, compiler_args->output_swizzle_count };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support OUTPUT_SWIZZLE.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
for (i = 0; i < compiler_args->parameter_count; i++)
{
const struct vkd3d_shader_parameter *argument = &compiler_args->parameters[i];
if (argument->name == VKD3D_SHADER_PARAMETER_NAME_RASTERIZER_SAMPLE_COUNT)
{
bool spec_constant = argument->type == VKD3D_SHADER_PARAMETER_TYPE_SPECIALIZATION_CONSTANT;
const dxil_spv_option_rasterizer_sample_count helper =
{ { DXIL_SPV_OPTION_RASTERIZER_SAMPLE_COUNT },
spec_constant ? argument->specialization_constant.id : argument->immediate_constant.u32,
spec_constant ? DXIL_SPV_TRUE : DXIL_SPV_FALSE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support RASTERIZER_SAMPLE_COUNT.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
}
}
remap_userdata.shader_interface_info = shader_interface_info;
remap_userdata.shader_interface_local_info = NULL;
remap_userdata.num_root_descriptors = num_root_descriptors;
dxil_spv_converter_set_root_constant_word_count(converter, root_constant_words);
dxil_spv_converter_set_root_descriptor_count(converter, num_root_descriptors);
dxil_spv_converter_set_srv_remapper(converter, dxil_srv_remap, &remap_userdata);
dxil_spv_converter_set_sampler_remapper(converter, dxil_sampler_remap, &remap_userdata);
dxil_spv_converter_set_uav_remapper(converter, dxil_uav_remap, &remap_userdata);
dxil_spv_converter_set_cbv_remapper(converter, dxil_cbv_remap, &remap_userdata);
dxil_spv_converter_set_vertex_input_remapper(converter, dxil_input_remap, (void *)shader_interface_info);
xfb_info = vkd3d_find_struct(shader_interface_info->next, TRANSFORM_FEEDBACK_INFO);
if (xfb_info)
dxil_spv_converter_set_stream_output_remapper(converter, dxil_output_remap, (void *)xfb_info);
if (dxil_spv_converter_run(converter) != DXIL_SPV_SUCCESS)
{
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
if (dxil_spv_converter_get_compiled_spirv(converter, &compiled) != DXIL_SPV_SUCCESS)
{
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
if (!(code = vkd3d_malloc(compiled.size)))
{
ret = VKD3D_ERROR_OUT_OF_MEMORY;
goto end;
}
memcpy(code, compiled.data, compiled.size);
spirv->code = code;
spirv->size = compiled.size;
vkd3d_shader_dump_spirv_shader(hash, spirv);
end:
dxil_spv_converter_free(converter);
dxil_spv_parsed_blob_free(blob);
dxil_spv_end_thread_allocator_context();
return ret;
}
int vkd3d_shader_compile_dxil_export(const struct vkd3d_shader_code *dxil,
const char *export,
struct vkd3d_shader_code *spirv,
const struct vkd3d_shader_interface_info *shader_interface_info,
const struct vkd3d_shader_interface_local_info *shader_interface_local_info,
const struct vkd3d_shader_compile_arguments *compiler_args)
{
const struct vkd3d_shader_push_constant_buffer *record_constant_buffer;
const struct vkd3d_shader_resource_binding *resource_binding;
const struct vkd3d_shader_root_parameter *root_parameter;
struct vkd3d_dxil_remap_userdata remap_userdata;
unsigned int non_raw_va_binding_count = 0;
unsigned int raw_va_binding_count = 0;
unsigned int num_root_descriptors = 0;
unsigned int root_constant_words = 0;
dxil_spv_converter converter = NULL;
dxil_spv_parsed_blob blob = NULL;
dxil_spv_compiled_spirv compiled;
unsigned int i, j, max_size;
vkd3d_shader_hash_t hash;
int ret = VKD3D_OK;
void *code;
dxil_spv_set_thread_log_callback(vkd3d_dxil_log_callback, NULL);
hash = vkd3d_shader_hash(dxil);
spirv->meta.replaced = false;
spirv->meta.hash = hash;
/* TODO: Probably need to add per-export shader replacement. */
dxil_spv_begin_thread_allocator_context();
vkd3d_shader_dump_shader(hash, dxil, "lib.dxil");
if (dxil_spv_parse_dxil_blob(dxil->code, dxil->size, &blob) != DXIL_SPV_SUCCESS)
{
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
if (dxil_spv_create_converter(blob, &converter) != DXIL_SPV_SUCCESS)
{
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
/* Figure out how many words we need for push constants. */
for (i = 0; i < shader_interface_info->push_constant_buffer_count; i++)
{
max_size = shader_interface_info->push_constant_buffers[i].offset +
shader_interface_info->push_constant_buffers[i].size;
max_size = (max_size + 3) / 4;
if (max_size > root_constant_words)
root_constant_words = max_size;
}
max_size = shader_interface_info->descriptor_tables.offset / sizeof(uint32_t) +
shader_interface_info->descriptor_tables.count;
if (max_size > root_constant_words)
root_constant_words = max_size;
for (i = 0; i < shader_interface_info->binding_count; i++)
{
/* Bindless UAV counters are implemented as physical storage buffer pointers.
* For simplicity, dxil-spirv only accepts either fully RAW VA, or all non-raw VA. */
if ((shader_interface_info->bindings[i].flags &
(VKD3D_SHADER_BINDING_FLAG_AUX_BUFFER | VKD3D_SHADER_BINDING_FLAG_BINDLESS)) ==
(VKD3D_SHADER_BINDING_FLAG_AUX_BUFFER | VKD3D_SHADER_BINDING_FLAG_BINDLESS))
{
if (shader_interface_info->bindings[i].flags & VKD3D_SHADER_BINDING_FLAG_RAW_VA)
raw_va_binding_count++;
else
non_raw_va_binding_count++;
}
if (vkd3d_shader_binding_is_root_descriptor(&shader_interface_info->bindings[i]))
num_root_descriptors++;
}
/* Push local root parameters. We cannot rely on callbacks here
* since the local root signature has a physical layout in ShaderRecordKHR
* which needs to be precisely specified up front. */
for (i = 0; i < shader_interface_local_info->local_root_parameter_count; i++)
{
root_parameter = &shader_interface_local_info->local_root_parameters[i];
switch (root_parameter->parameter_type)
{
case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS:
record_constant_buffer =
&shader_interface_local_info->shader_record_constant_buffers[root_parameter->constant.constant_index];
dxil_spv_converter_add_local_root_constants(converter,
record_constant_buffer->register_space, record_constant_buffer->register_index,
root_parameter->constant.constant_count);
break;
case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE:
if (dxil_spv_converter_begin_local_root_descriptor_table(converter) != DXIL_SPV_SUCCESS)
{
ret = VKD3D_ERROR_INVALID_ARGUMENT;
goto end;
}
for (j = 0; j < root_parameter->descriptor_table.binding_count; j++)
{
dxil_spv_resource_class resource_class;
resource_binding = &root_parameter->descriptor_table.first_binding[j];
switch (resource_binding->type)
{
case VKD3D_SHADER_DESCRIPTOR_TYPE_CBV:
resource_class = DXIL_SPV_RESOURCE_CLASS_CBV;
break;
case VKD3D_SHADER_DESCRIPTOR_TYPE_SRV:
resource_class = DXIL_SPV_RESOURCE_CLASS_SRV;
break;
case VKD3D_SHADER_DESCRIPTOR_TYPE_UAV:
resource_class = DXIL_SPV_RESOURCE_CLASS_UAV;
break;
case VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER:
resource_class = DXIL_SPV_RESOURCE_CLASS_SAMPLER;
break;
default:
ret = VKD3D_ERROR_INVALID_ARGUMENT;
goto end;
}
dxil_spv_converter_add_local_root_descriptor_table(converter,
resource_class,
resource_binding->register_space, resource_binding->register_index,
resource_binding->register_count, resource_binding->descriptor_offset);
}
if (dxil_spv_converter_end_local_root_descriptor_table(converter) != DXIL_SPV_SUCCESS)
{
ret = VKD3D_ERROR_INVALID_ARGUMENT;
goto end;
}
break;
case D3D12_ROOT_PARAMETER_TYPE_CBV:
dxil_spv_converter_add_local_root_descriptor(converter,
DXIL_SPV_RESOURCE_CLASS_CBV, root_parameter->descriptor.binding->register_space,
root_parameter->descriptor.binding->register_index);
break;
case D3D12_ROOT_PARAMETER_TYPE_SRV:
dxil_spv_converter_add_local_root_descriptor(converter,
DXIL_SPV_RESOURCE_CLASS_SRV, root_parameter->descriptor.binding->register_space,
root_parameter->descriptor.binding->register_index);
break;
case D3D12_ROOT_PARAMETER_TYPE_UAV:
dxil_spv_converter_add_local_root_descriptor(converter,
DXIL_SPV_RESOURCE_CLASS_UAV, root_parameter->descriptor.binding->register_space,
root_parameter->descriptor.binding->register_index);
break;
default:
ret = VKD3D_ERROR_INVALID_ARGUMENT;
goto end;
}
}
if (raw_va_binding_count && non_raw_va_binding_count)
{
ERR("dxil-spirv currently cannot mix and match bindless UAV counters with RAW VA and texel buffer.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
/* Root constants come after root descriptors. Offset the counts. */
if (root_constant_words < num_root_descriptors * 2)
root_constant_words = num_root_descriptors * 2;
root_constant_words -= num_root_descriptors * 2;
{
const struct dxil_spv_option_ssbo_alignment helper =
{ { DXIL_SPV_OPTION_SSBO_ALIGNMENT }, shader_interface_info->min_ssbo_alignment };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support SSBO_ALIGNMENT.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
if (shader_interface_info->flags & VKD3D_SHADER_INTERFACE_PUSH_CONSTANTS_AS_UNIFORM_BUFFER)
{
const struct dxil_spv_option_root_constant_inline_uniform_block helper =
{ { DXIL_SPV_OPTION_ROOT_CONSTANT_INLINE_UNIFORM_BLOCK },
shader_interface_info->push_constant_ubo_binding->set,
shader_interface_info->push_constant_ubo_binding->binding,
DXIL_SPV_TRUE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support PUSH_CONSTANTS_AS_UNIFORM_BUFFER.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
if (shader_interface_info->flags & VKD3D_SHADER_INTERFACE_BINDLESS_CBV_AS_STORAGE_BUFFER)
{
static const struct dxil_spv_option_bindless_cbv_ssbo_emulation helper =
{ { DXIL_SPV_OPTION_BINDLESS_CBV_SSBO_EMULATION },
DXIL_SPV_TRUE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support BINDLESS_CBV_AS_STORAGE_BUFFER.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
/* BDA is mandatory for RT. */
{
const struct dxil_spv_option_physical_storage_buffer helper =
{ { DXIL_SPV_OPTION_PHYSICAL_STORAGE_BUFFER }, DXIL_SPV_TRUE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support PHYSICAL_STORAGE_BUFFER.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
if (shader_interface_info->flags & VKD3D_SHADER_INTERFACE_TYPED_OFFSET_BUFFER)
{
const struct dxil_spv_option_bindless_typed_buffer_offsets helper =
{ { DXIL_SPV_OPTION_BINDLESS_TYPED_BUFFER_OFFSETS },
DXIL_SPV_TRUE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support BINDLESS_TYPED_BUFFER_OFFSETS.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
{
const struct dxil_spv_option_bindless_offset_buffer_layout helper =
{ { DXIL_SPV_OPTION_BINDLESS_OFFSET_BUFFER_LAYOUT },
0, 1, 2 };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support BINDLESS_OFFSET_BUFFER_LAYOUT.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
{
char buffer[1024];
const struct dxil_spv_option_shader_source_file helper =
{ { DXIL_SPV_OPTION_SHADER_SOURCE_FILE }, buffer };
snprintf(buffer, sizeof(buffer), "%016"PRIx64".%s.dxil", spirv->meta.hash, export);
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
WARN("dxil-spirv does not support SHADER_SOURCE_FILE.\n");
}
if (compiler_args)
{
for (i = 0; i < compiler_args->target_extension_count; i++)
{
if (compiler_args->target_extensions[i] == VKD3D_SHADER_TARGET_EXTENSION_READ_STORAGE_IMAGE_WITHOUT_FORMAT)
{
static const dxil_spv_option_typed_uav_read_without_format helper =
{ { DXIL_SPV_OPTION_TYPED_UAV_READ_WITHOUT_FORMAT }, DXIL_SPV_TRUE };
if (dxil_spv_converter_add_option(converter, &helper.base) != DXIL_SPV_SUCCESS)
{
ERR("dxil-spirv does not support TYPED_UAV_READ_WITHOUT_FORMAT.\n");
ret = VKD3D_ERROR_NOT_IMPLEMENTED;
goto end;
}
}
}
}
dxil_spv_converter_set_entry_point(converter, export);
remap_userdata.shader_interface_info = shader_interface_info;
remap_userdata.shader_interface_local_info = shader_interface_local_info;
remap_userdata.num_root_descriptors = num_root_descriptors;
dxil_spv_converter_set_root_constant_word_count(converter, root_constant_words);
dxil_spv_converter_set_root_descriptor_count(converter, num_root_descriptors);
dxil_spv_converter_set_srv_remapper(converter, dxil_srv_remap, &remap_userdata);
dxil_spv_converter_set_sampler_remapper(converter, dxil_sampler_remap, &remap_userdata);
dxil_spv_converter_set_uav_remapper(converter, dxil_uav_remap, &remap_userdata);
dxil_spv_converter_set_cbv_remapper(converter, dxil_cbv_remap, &remap_userdata);
if (dxil_spv_converter_run(converter) != DXIL_SPV_SUCCESS)
{
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
if (dxil_spv_converter_get_compiled_spirv(converter, &compiled) != DXIL_SPV_SUCCESS)
{
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
if (!(code = vkd3d_malloc(compiled.size)))
{
ret = VKD3D_ERROR_OUT_OF_MEMORY;
goto end;
}
memcpy(code, compiled.data, compiled.size);
spirv->code = code;
spirv->size = compiled.size;
vkd3d_shader_dump_spirv_shader(hash, spirv);
end:
dxil_spv_converter_free(converter);
dxil_spv_parsed_blob_free(blob);
dxil_spv_end_thread_allocator_context();
return ret;
}
void vkd3d_shader_dxil_free_library_entry_points(struct vkd3d_shader_library_entry_point *entry_points, size_t count)
{
size_t i;
for (i = 0; i < count; i++)
{
vkd3d_free(entry_points[i].mangled_entry_point);
vkd3d_free(entry_points[i].plain_entry_point);
vkd3d_free(entry_points[i].real_entry_point);
}
vkd3d_free(entry_points);
}
static VkShaderStageFlagBits convert_stage(dxil_spv_shader_stage stage)
{
/* Only interested in RT stages. There is no way yet to use lib_6_3+ for non-RT. */
switch (stage)
{
case DXIL_SPV_STAGE_RAY_GENERATION:
return VK_SHADER_STAGE_RAYGEN_BIT_KHR;
case DXIL_SPV_STAGE_CLOSEST_HIT:
return VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
case DXIL_SPV_STAGE_MISS:
return VK_SHADER_STAGE_MISS_BIT_KHR;
case DXIL_SPV_STAGE_CALLABLE:
return VK_SHADER_STAGE_CALLABLE_BIT_KHR;
case DXIL_SPV_STAGE_INTERSECTION:
return VK_SHADER_STAGE_INTERSECTION_BIT_KHR;
default:
return VK_SHADER_STAGE_ALL;
}
}
static bool vkd3d_dxil_build_entry(struct vkd3d_shader_library_entry_point *entry,
unsigned int identifier,
const char *mangled_name, dxil_spv_shader_stage stage)
{
entry->identifier = identifier;
entry->mangled_entry_point = vkd3d_dup_entry_point(mangled_name);
if (!entry->mangled_entry_point)
return false;
entry->plain_entry_point = vkd3d_dup_demangled_entry_point(mangled_name);
if (!entry->plain_entry_point)
{
vkd3d_free(entry->mangled_entry_point);
return false;
}
entry->real_entry_point = vkd3d_strdup(mangled_name);
entry->stage = convert_stage(stage);
return true;
}
int vkd3d_shader_dxil_append_library_entry_points(
const D3D12_DXIL_LIBRARY_DESC *library_desc,
unsigned int identifier,
struct vkd3d_shader_library_entry_point **entry_points,
size_t *entry_point_size, size_t *entry_point_count)
{
struct vkd3d_shader_library_entry_point new_entry;
dxil_spv_parsed_blob blob = NULL;
dxil_spv_shader_stage stage;
const char *mangled_entry;
char *ascii_entry = NULL;
unsigned int count, i;
int ret = VKD3D_OK;
memset(&new_entry, 0, sizeof(new_entry));
dxil_spv_begin_thread_allocator_context();
if (dxil_spv_parse_dxil_blob(
library_desc->DXILLibrary.pShaderBytecode,
library_desc->DXILLibrary.BytecodeLength,
&blob) != DXIL_SPV_SUCCESS)
{
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
if (library_desc->NumExports)
{
for (i = 0; i < library_desc->NumExports; i++)
{
if (library_desc->pExports[i].ExportToRename)
ascii_entry = vkd3d_strdup_w_utf8(library_desc->pExports[i].ExportToRename, 0);
else
ascii_entry = vkd3d_strdup_w_utf8(library_desc->pExports[i].Name, 0);
stage = dxil_spv_parsed_blob_get_shader_stage_for_entry(blob, ascii_entry);
if (stage == DXIL_SPV_STAGE_UNKNOWN)
{
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
new_entry.real_entry_point = ascii_entry;
new_entry.plain_entry_point = vkd3d_wstrdup(library_desc->pExports[i].Name);
new_entry.mangled_entry_point = NULL;
new_entry.identifier = identifier;
new_entry.stage = convert_stage(stage);
ascii_entry = NULL;
vkd3d_array_reserve((void**)entry_points, entry_point_size,
*entry_point_count + 1, sizeof(new_entry));
(*entry_points)[(*entry_point_count)++] = new_entry;
memset(&new_entry, 0, sizeof(new_entry));
}
}
else
{
if (dxil_spv_parsed_blob_get_num_entry_points(blob, &count) != DXIL_SPV_SUCCESS)
{
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
for (i = 0; i < count; i++)
{
dxil_spv_parsed_blob_get_entry_point_name(blob, i, &mangled_entry);
stage = dxil_spv_parsed_blob_get_shader_stage_for_entry(blob, mangled_entry);
if (stage == DXIL_SPV_STAGE_UNKNOWN)
{
ERR("Invalid shader stage for %s.\n", mangled_entry);
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
if (!vkd3d_dxil_build_entry(&new_entry, identifier, mangled_entry, stage))
{
ret = VKD3D_ERROR_INVALID_SHADER;
goto end;
}
vkd3d_array_reserve((void**)entry_points, entry_point_size,
*entry_point_count + 1, sizeof(new_entry));
(*entry_points)[(*entry_point_count)++] = new_entry;
memset(&new_entry, 0, sizeof(new_entry));
}
}
end:
vkd3d_free(ascii_entry);
vkd3d_free(new_entry.mangled_entry_point);
vkd3d_free(new_entry.plain_entry_point);
vkd3d_free(new_entry.real_entry_point);
if (blob)
dxil_spv_parsed_blob_free(blob);
dxil_spv_end_thread_allocator_context();
return ret;
}