/* * Copyright 2016 Józef Kucia for CodeWeavers * Copyright 2016 Henri Verbeet for CodeWeavers * Copyright 2021 Conor McCarthy 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 */ #include "vkd3d_private.h" #include "vkd3d_shaders.h" #include "vkd3d_shader_utils.h" /* ID3D12RootSignature */ static inline struct d3d12_root_signature *impl_from_ID3D12RootSignature(ID3D12RootSignature *iface) { return CONTAINING_RECORD(iface, struct d3d12_root_signature, ID3D12RootSignature_iface); } static HRESULT STDMETHODCALLTYPE d3d12_root_signature_QueryInterface(ID3D12RootSignature *iface, REFIID riid, void **object) { TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object); if (IsEqualGUID(riid, &IID_ID3D12RootSignature) || IsEqualGUID(riid, &IID_ID3D12DeviceChild) || IsEqualGUID(riid, &IID_ID3D12Object) || IsEqualGUID(riid, &IID_IUnknown)) { ID3D12RootSignature_AddRef(iface); *object = iface; return S_OK; } WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid)); *object = NULL; return E_NOINTERFACE; } static ULONG STDMETHODCALLTYPE d3d12_root_signature_AddRef(ID3D12RootSignature *iface) { struct d3d12_root_signature *root_signature = impl_from_ID3D12RootSignature(iface); ULONG refcount = InterlockedIncrement(&root_signature->refcount); TRACE("%p increasing refcount to %u.\n", root_signature, refcount); return refcount; } static void d3d12_descriptor_set_layout_cleanup( struct d3d12_descriptor_set_layout *layout, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, layout->vk_layout, NULL)); } static void d3d12_root_signature_cleanup(struct d3d12_root_signature *root_signature, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; unsigned int i; if (root_signature->vk_pipeline_layout) VK_CALL(vkDestroyPipelineLayout(device->vk_device, root_signature->vk_pipeline_layout, NULL)); for (i = 0; i < root_signature->vk_set_count; ++i) { d3d12_descriptor_set_layout_cleanup(&root_signature->descriptor_set_layouts[i], device); } if (root_signature->parameters) { for (i = 0; i < root_signature->parameter_count; ++i) { if (root_signature->parameters[i].parameter_type == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) vkd3d_free(root_signature->parameters[i].u.descriptor_table.ranges); } vkd3d_free(root_signature->parameters); } if (root_signature->descriptor_mapping) vkd3d_free(root_signature->descriptor_mapping); vkd3d_free(root_signature->descriptor_offsets); vkd3d_free(root_signature->uav_counter_mapping); vkd3d_free(root_signature->uav_counter_offsets); if (root_signature->root_constants) vkd3d_free(root_signature->root_constants); for (i = 0; i < root_signature->static_sampler_count; ++i) { if (root_signature->static_samplers[i]) VK_CALL(vkDestroySampler(device->vk_device, root_signature->static_samplers[i], NULL)); } if (root_signature->static_samplers) vkd3d_free(root_signature->static_samplers); } static ULONG STDMETHODCALLTYPE d3d12_root_signature_Release(ID3D12RootSignature *iface) { struct d3d12_root_signature *root_signature = impl_from_ID3D12RootSignature(iface); ULONG refcount = InterlockedDecrement(&root_signature->refcount); TRACE("%p decreasing refcount to %u.\n", root_signature, refcount); if (!refcount) { struct d3d12_device *device = root_signature->device; vkd3d_private_store_destroy(&root_signature->private_store); d3d12_root_signature_cleanup(root_signature, device); vkd3d_free(root_signature); d3d12_device_release(device); } return refcount; } static HRESULT STDMETHODCALLTYPE d3d12_root_signature_GetPrivateData(ID3D12RootSignature *iface, REFGUID guid, UINT *data_size, void *data) { struct d3d12_root_signature *root_signature = impl_from_ID3D12RootSignature(iface); TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); return vkd3d_get_private_data(&root_signature->private_store, guid, data_size, data); } static HRESULT STDMETHODCALLTYPE d3d12_root_signature_SetPrivateData(ID3D12RootSignature *iface, REFGUID guid, UINT data_size, const void *data) { struct d3d12_root_signature *root_signature = impl_from_ID3D12RootSignature(iface); TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); return vkd3d_set_private_data(&root_signature->private_store, guid, data_size, data); } static HRESULT STDMETHODCALLTYPE d3d12_root_signature_SetPrivateDataInterface(ID3D12RootSignature *iface, REFGUID guid, const IUnknown *data) { struct d3d12_root_signature *root_signature = impl_from_ID3D12RootSignature(iface); TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data); return vkd3d_set_private_data_interface(&root_signature->private_store, guid, data); } static HRESULT STDMETHODCALLTYPE d3d12_root_signature_SetName(ID3D12RootSignature *iface, const WCHAR *name) { struct d3d12_root_signature *root_signature = impl_from_ID3D12RootSignature(iface); TRACE("iface %p, name %s.\n", iface, debugstr_w(name, root_signature->device->wchar_size)); return name ? S_OK : E_INVALIDARG; } static HRESULT STDMETHODCALLTYPE d3d12_root_signature_GetDevice(ID3D12RootSignature *iface, REFIID iid, void **device) { struct d3d12_root_signature *root_signature = impl_from_ID3D12RootSignature(iface); TRACE("iface %p, iid %s, device %p.\n", iface, debugstr_guid(iid), device); return d3d12_device_query_interface(root_signature->device, iid, device); } static const struct ID3D12RootSignatureVtbl d3d12_root_signature_vtbl = { /* IUnknown methods */ d3d12_root_signature_QueryInterface, d3d12_root_signature_AddRef, d3d12_root_signature_Release, /* ID3D12Object methods */ d3d12_root_signature_GetPrivateData, d3d12_root_signature_SetPrivateData, d3d12_root_signature_SetPrivateDataInterface, d3d12_root_signature_SetName, /* ID3D12DeviceChild methods */ d3d12_root_signature_GetDevice, }; struct d3d12_root_signature *unsafe_impl_from_ID3D12RootSignature(ID3D12RootSignature *iface) { if (!iface) return NULL; assert(iface->lpVtbl == &d3d12_root_signature_vtbl); return impl_from_ID3D12RootSignature(iface); } static VkShaderStageFlags stage_flags_from_visibility(D3D12_SHADER_VISIBILITY visibility) { switch (visibility) { case D3D12_SHADER_VISIBILITY_ALL: return VK_SHADER_STAGE_ALL; case D3D12_SHADER_VISIBILITY_VERTEX: return VK_SHADER_STAGE_VERTEX_BIT; case D3D12_SHADER_VISIBILITY_HULL: return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; case D3D12_SHADER_VISIBILITY_DOMAIN: return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; case D3D12_SHADER_VISIBILITY_GEOMETRY: return VK_SHADER_STAGE_GEOMETRY_BIT; case D3D12_SHADER_VISIBILITY_PIXEL: return VK_SHADER_STAGE_FRAGMENT_BIT; default: return 0; } } static enum vkd3d_shader_visibility vkd3d_shader_visibility_from_d3d12(D3D12_SHADER_VISIBILITY visibility) { switch (visibility) { case D3D12_SHADER_VISIBILITY_ALL: return VKD3D_SHADER_VISIBILITY_ALL; case D3D12_SHADER_VISIBILITY_VERTEX: return VKD3D_SHADER_VISIBILITY_VERTEX; case D3D12_SHADER_VISIBILITY_HULL: return VKD3D_SHADER_VISIBILITY_HULL; case D3D12_SHADER_VISIBILITY_DOMAIN: return VKD3D_SHADER_VISIBILITY_DOMAIN; case D3D12_SHADER_VISIBILITY_GEOMETRY: return VKD3D_SHADER_VISIBILITY_GEOMETRY; case D3D12_SHADER_VISIBILITY_PIXEL: return VKD3D_SHADER_VISIBILITY_PIXEL; default: FIXME("Unhandled visibility %#x.\n", visibility); return VKD3D_SHADER_VISIBILITY_ALL; } } static VkDescriptorType vk_descriptor_type_from_vkd3d_descriptor_type(enum vkd3d_shader_descriptor_type type, bool is_buffer) { switch (type) { case VKD3D_SHADER_DESCRIPTOR_TYPE_SRV: return is_buffer ? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER : VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; case VKD3D_SHADER_DESCRIPTOR_TYPE_UAV: return is_buffer ? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER : VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; case VKD3D_SHADER_DESCRIPTOR_TYPE_CBV: return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; case VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER: return VK_DESCRIPTOR_TYPE_SAMPLER; default: FIXME("Unhandled descriptor range type type %#x.\n", type); return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; } } static VkDescriptorType vk_descriptor_type_from_d3d12_root_parameter(D3D12_ROOT_PARAMETER_TYPE type) { switch (type) { /* SRV and UAV root parameters are buffer views. */ case D3D12_ROOT_PARAMETER_TYPE_SRV: return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; case D3D12_ROOT_PARAMETER_TYPE_UAV: return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; case D3D12_ROOT_PARAMETER_TYPE_CBV: return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; default: FIXME("Unhandled descriptor root parameter type %#x.\n", type); return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; } } static enum vkd3d_shader_descriptor_type vkd3d_descriptor_type_from_d3d12_range_type( D3D12_DESCRIPTOR_RANGE_TYPE type) { switch (type) { case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: return VKD3D_SHADER_DESCRIPTOR_TYPE_SRV; case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: return VKD3D_SHADER_DESCRIPTOR_TYPE_UAV; case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: return VKD3D_SHADER_DESCRIPTOR_TYPE_CBV; case D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER: return VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER; default: FIXME("Unhandled descriptor range type type %#x.\n", type); return VKD3D_SHADER_DESCRIPTOR_TYPE_SRV; } } static enum vkd3d_shader_descriptor_type vkd3d_descriptor_type_from_d3d12_root_parameter_type( D3D12_ROOT_PARAMETER_TYPE type) { switch (type) { case D3D12_ROOT_PARAMETER_TYPE_SRV: return VKD3D_SHADER_DESCRIPTOR_TYPE_SRV; case D3D12_ROOT_PARAMETER_TYPE_UAV: return VKD3D_SHADER_DESCRIPTOR_TYPE_UAV; case D3D12_ROOT_PARAMETER_TYPE_CBV: return VKD3D_SHADER_DESCRIPTOR_TYPE_CBV; default: FIXME("Unhandled descriptor root parameter type %#x.\n", type); return VKD3D_SHADER_DESCRIPTOR_TYPE_SRV; } } static bool vk_binding_from_d3d12_descriptor_range(struct VkDescriptorSetLayoutBinding *binding_desc, enum vkd3d_shader_descriptor_type descriptor_type, D3D12_SHADER_VISIBILITY shader_visibility, bool is_buffer, uint32_t vk_binding, unsigned int descriptor_count) { binding_desc->binding = vk_binding; binding_desc->descriptorType = vk_descriptor_type_from_vkd3d_descriptor_type(descriptor_type, is_buffer); binding_desc->descriptorCount = descriptor_count; binding_desc->stageFlags = stage_flags_from_visibility(shader_visibility); binding_desc->pImmutableSamplers = NULL; return true; } struct d3d12_root_signature_info { size_t binding_count; size_t uav_range_count; size_t root_constant_count; size_t root_descriptor_count; unsigned int cbv_count; unsigned int srv_count; unsigned int uav_count; unsigned int sampler_count; unsigned int cbv_unbounded_range_count; unsigned int srv_unbounded_range_count; unsigned int uav_unbounded_range_count; unsigned int sampler_unbounded_range_count; size_t cost; }; static HRESULT d3d12_root_signature_info_count_descriptors(struct d3d12_root_signature_info *info, const D3D12_ROOT_DESCRIPTOR_TABLE *table, bool use_array) { bool cbv_unbounded_range = false, srv_unbounded_range = false, uav_unbounded_range = false; bool sampler_unbounded_range = false; bool unbounded = false; unsigned int i, count; for (i = 0; i < table->NumDescriptorRanges; ++i) { const D3D12_DESCRIPTOR_RANGE *range = &table->pDescriptorRanges[i]; unsigned int binding_count; if (!range->NumDescriptors) { WARN("A descriptor range is empty.\n"); return E_INVALIDARG; } if (range->NumDescriptors != UINT_MAX && !vkd3d_bound_range(range->BaseShaderRegister, range->NumDescriptors, UINT_MAX)) { WARN("A descriptor range overflows.\n"); return E_INVALIDARG; } if (unbounded && range->OffsetInDescriptorsFromTableStart == D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) { WARN("An unbounded range with offset D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND occurs after " "another unbounded range.\n"); return E_INVALIDARG; } count = range->NumDescriptors; if (range->NumDescriptors == UINT_MAX) { unbounded = true; count = 0; } binding_count = use_array ? 1 : range->NumDescriptors; switch (range->RangeType) { case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: /* XXX: Vulkan buffer and image descriptors have different types. In order * to preserve compatibility between Vulkan resource bindings for the same * root signature, we create descriptor set layouts with two bindings for * each SRV and UAV. */ info->binding_count += binding_count; info->srv_count += count * 2u; srv_unbounded_range |= unbounded; break; case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: /* As above. */ info->binding_count += binding_count; info->uav_count += count * 2u; uav_unbounded_range |= unbounded; ++info->uav_range_count; break; case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: info->cbv_count += count; cbv_unbounded_range |= unbounded; break; case D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER: info->sampler_count += count; sampler_unbounded_range |= unbounded; break; default: FIXME("Unhandled descriptor type %#x.\n", range->RangeType); return E_NOTIMPL; } info->binding_count += binding_count; } if (unbounded && !use_array) { FIXME("The device does not support unbounded descriptor ranges.\n"); return E_FAIL; } info->srv_unbounded_range_count += srv_unbounded_range * 2u; info->uav_unbounded_range_count += uav_unbounded_range * 2u; info->cbv_unbounded_range_count += cbv_unbounded_range; info->sampler_unbounded_range_count += sampler_unbounded_range; return S_OK; } static HRESULT d3d12_root_signature_info_from_desc(struct d3d12_root_signature_info *info, const D3D12_ROOT_SIGNATURE_DESC *desc, bool use_array) { unsigned int i; HRESULT hr; memset(info, 0, sizeof(*info)); for (i = 0; i < desc->NumParameters; ++i) { const D3D12_ROOT_PARAMETER *p = &desc->pParameters[i]; switch (p->ParameterType) { case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE: if (FAILED(hr = d3d12_root_signature_info_count_descriptors(info, &p->u.DescriptorTable, use_array))) return hr; ++info->cost; break; case D3D12_ROOT_PARAMETER_TYPE_CBV: ++info->root_descriptor_count; ++info->cbv_count; ++info->binding_count; info->cost += 2; break; case D3D12_ROOT_PARAMETER_TYPE_SRV: ++info->root_descriptor_count; ++info->srv_count; ++info->binding_count; info->cost += 2; break; case D3D12_ROOT_PARAMETER_TYPE_UAV: ++info->root_descriptor_count; ++info->uav_count; ++info->binding_count; info->cost += 2; break; case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS: ++info->root_constant_count; info->cost += p->u.Constants.Num32BitValues; break; default: FIXME("Unhandled type %#x for parameter %u.\n", p->ParameterType, i); return E_NOTIMPL; } } info->binding_count += desc->NumStaticSamplers; info->sampler_count += desc->NumStaticSamplers; return S_OK; } static HRESULT d3d12_root_signature_init_push_constants(struct d3d12_root_signature *root_signature, const D3D12_ROOT_SIGNATURE_DESC *desc, struct VkPushConstantRange push_constants[D3D12_SHADER_VISIBILITY_PIXEL + 1], uint32_t *push_constant_range_count) { uint32_t push_constants_offset[D3D12_SHADER_VISIBILITY_PIXEL + 1]; bool use_vk_heaps = root_signature->device->use_vk_heaps; unsigned int i, j, push_constant_count; uint32_t offset; memset(push_constants, 0, (D3D12_SHADER_VISIBILITY_PIXEL + 1) * sizeof(*push_constants)); memset(push_constants_offset, 0, sizeof(push_constants_offset)); for (i = 0; i < desc->NumParameters; ++i) { const D3D12_ROOT_PARAMETER *p = &desc->pParameters[i]; if (p->ParameterType != D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS) continue; assert(p->ShaderVisibility <= D3D12_SHADER_VISIBILITY_PIXEL); push_constants[p->ShaderVisibility].stageFlags = use_vk_heaps ? VK_SHADER_STAGE_ALL : stage_flags_from_visibility(p->ShaderVisibility); push_constants[p->ShaderVisibility].size += p->u.Constants.Num32BitValues * sizeof(uint32_t); } if (push_constants[D3D12_SHADER_VISIBILITY_ALL].size) { /* When D3D12_SHADER_VISIBILITY_ALL is used we use a single push * constants range because the Vulkan spec states: * * "Any two elements of pPushConstantRanges must not include the same * stage in stageFlags". */ push_constant_count = 1; for (i = 0; i <= D3D12_SHADER_VISIBILITY_PIXEL; ++i) { if (i == D3D12_SHADER_VISIBILITY_ALL) continue; push_constants[D3D12_SHADER_VISIBILITY_ALL].size += push_constants[i].size; push_constants[i].size = 0; } } else { /* Move non-empty push constants ranges to front and compute offsets. */ offset = 0; for (i = 0, j = 0; i <= D3D12_SHADER_VISIBILITY_PIXEL; ++i) { if (push_constants[i].size) { push_constants[j] = push_constants[i]; push_constants[j].offset = offset; push_constants_offset[i] = offset; offset += push_constants[j].size; ++j; } } push_constant_count = j; } for (i = 0, j = 0; i < desc->NumParameters; ++i) { struct d3d12_root_constant *root_constant = &root_signature->parameters[i].u.constant; const D3D12_ROOT_PARAMETER *p = &desc->pParameters[i]; unsigned int idx; if (p->ParameterType != D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS) continue; idx = push_constant_count == 1 ? 0 : p->ShaderVisibility; offset = push_constants_offset[idx]; push_constants_offset[idx] += p->u.Constants.Num32BitValues * sizeof(uint32_t); root_signature->parameters[i].parameter_type = p->ParameterType; root_constant->stage_flags = push_constant_count == 1 ? push_constants[0].stageFlags : stage_flags_from_visibility(p->ShaderVisibility); root_constant->offset = offset; root_signature->root_constants[j].register_space = p->u.Constants.RegisterSpace; root_signature->root_constants[j].register_index = p->u.Constants.ShaderRegister; root_signature->root_constants[j].shader_visibility = vkd3d_shader_visibility_from_d3d12(p->ShaderVisibility); root_signature->root_constants[j].offset = offset; root_signature->root_constants[j].size = p->u.Constants.Num32BitValues * sizeof(uint32_t); ++j; } *push_constant_range_count = push_constant_count; return S_OK; } struct vkd3d_descriptor_set_context { VkDescriptorSetLayoutBinding *current_binding; VkDescriptorSetLayoutBinding *first_binding; unsigned int table_index; unsigned int unbounded_offset; unsigned int descriptor_index; unsigned int uav_counter_index; unsigned int push_constant_index; uint32_t descriptor_binding; }; static bool vkd3d_validate_descriptor_set_count(struct d3d12_device *device, unsigned int set_count) { uint32_t max_count = min(VKD3D_MAX_DESCRIPTOR_SETS, device->vk_info.device_limits.maxBoundDescriptorSets); if (set_count > max_count) { /* NOTE: If maxBoundDescriptorSets is < 9, try VKD3D_CONFIG=virtual_heaps */ ERR("Required descriptor set count exceeds maximum allowed count of %u.\n", max_count); return false; } return true; } static HRESULT vkd3d_create_descriptor_set_layout(struct d3d12_device *device, VkDescriptorSetLayoutCreateFlags flags, unsigned int binding_count, bool unbounded, const VkDescriptorSetLayoutBinding *bindings, VkDescriptorSetLayout *set_layout); static HRESULT d3d12_root_signature_append_descriptor_set_layout(struct d3d12_root_signature *root_signature, struct vkd3d_descriptor_set_context *context, VkDescriptorSetLayoutCreateFlags flags) { struct d3d12_descriptor_set_layout *layout; unsigned int index; HRESULT hr; if (!context->descriptor_binding) return S_OK; index = root_signature->vk_set_count; layout = &root_signature->descriptor_set_layouts[index]; if (!vkd3d_validate_descriptor_set_count(root_signature->device, index + 1)) return E_INVALIDARG; if (FAILED(hr = vkd3d_create_descriptor_set_layout(root_signature->device, flags, context->descriptor_binding, context->unbounded_offset != UINT_MAX, context->first_binding, &layout->vk_layout))) return hr; layout->table_index = context->table_index; layout->unbounded_offset = context->unbounded_offset; ++root_signature->vk_set_count; context->current_binding = context->first_binding; context->descriptor_binding = 0; return S_OK; } static void d3d12_root_signature_append_vk_binding(struct d3d12_root_signature *root_signature, enum vkd3d_shader_descriptor_type descriptor_type, unsigned int register_space, unsigned int register_idx, bool buffer_descriptor, enum vkd3d_shader_visibility shader_visibility, unsigned int descriptor_count, struct vkd3d_descriptor_set_context *context) { struct vkd3d_shader_descriptor_offset *offset = root_signature->descriptor_offsets ? &root_signature->descriptor_offsets[context->descriptor_index] : NULL; struct vkd3d_shader_resource_binding *mapping = &root_signature->descriptor_mapping[context->descriptor_index++]; mapping->type = descriptor_type; mapping->register_space = register_space; mapping->register_index = register_idx; mapping->shader_visibility = shader_visibility; mapping->flags = buffer_descriptor ? VKD3D_SHADER_BINDING_FLAG_BUFFER : VKD3D_SHADER_BINDING_FLAG_IMAGE; mapping->binding.set = root_signature->vk_set_count; mapping->binding.binding = context->descriptor_binding++; mapping->binding.count = descriptor_count; if (offset) { offset->static_offset = 0; offset->dynamic_offset_index = ~0u; } if (context->unbounded_offset != UINT_MAX) d3d12_root_signature_append_descriptor_set_layout(root_signature, context, 0); } static uint32_t d3d12_root_signature_assign_vk_bindings(struct d3d12_root_signature *root_signature, enum vkd3d_shader_descriptor_type descriptor_type, unsigned int register_space, unsigned int base_register_idx, unsigned int binding_count, bool is_buffer_descriptor, bool duplicate_descriptors, enum vkd3d_shader_visibility shader_visibility, struct vkd3d_descriptor_set_context *context) { uint32_t first_binding; unsigned int i; is_buffer_descriptor |= descriptor_type == VKD3D_SHADER_DESCRIPTOR_TYPE_CBV; duplicate_descriptors = (descriptor_type == VKD3D_SHADER_DESCRIPTOR_TYPE_SRV || descriptor_type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV) && duplicate_descriptors; first_binding = context->descriptor_binding; for (i = 0; i < binding_count; ++i) { if (duplicate_descriptors) d3d12_root_signature_append_vk_binding(root_signature, descriptor_type, register_space, base_register_idx + i, true, shader_visibility, 1, context); d3d12_root_signature_append_vk_binding(root_signature, descriptor_type, register_space, base_register_idx + i, is_buffer_descriptor, shader_visibility, 1, context); } return first_binding; } static uint32_t vkd3d_descriptor_magic_from_d3d12(D3D12_DESCRIPTOR_RANGE_TYPE type) { switch (type) { case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: return VKD3D_DESCRIPTOR_MAGIC_SRV; case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: return VKD3D_DESCRIPTOR_MAGIC_UAV; case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: return VKD3D_DESCRIPTOR_MAGIC_CBV; case D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER: return VKD3D_DESCRIPTOR_MAGIC_SAMPLER; default: ERR("Invalid range type %#x.\n", type); return VKD3D_DESCRIPTOR_MAGIC_FREE; } } static unsigned int vk_binding_count_from_descriptor_range(const struct d3d12_root_descriptor_table_range *range, const struct d3d12_root_signature_info *info, const struct vkd3d_device_descriptor_limits *limits) { unsigned int count, limit; if (range->descriptor_count != UINT_MAX) return range->descriptor_count; switch (range->type) { case VKD3D_SHADER_DESCRIPTOR_TYPE_CBV: limit = limits->uniform_buffer_max_descriptors; count = (limit - min(info->cbv_count, limit)) / info->cbv_unbounded_range_count; break; case VKD3D_SHADER_DESCRIPTOR_TYPE_SRV: limit = limits->sampled_image_max_descriptors; count = (limit - min(info->srv_count, limit)) / info->srv_unbounded_range_count; break; case VKD3D_SHADER_DESCRIPTOR_TYPE_UAV: limit = limits->storage_image_max_descriptors; count = (limit - min(info->uav_count, limit)) / info->uav_unbounded_range_count; break; case VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER: limit = limits->sampler_max_descriptors; count = (limit - min(info->sampler_count, limit)) / info->sampler_unbounded_range_count; break; default: ERR("Unhandled type %#x.\n", range->type); return 1; } if (!count) { WARN("Descriptor table exceeds type %#x limit of %u.\n", range->type, limit); count = 1; } return min(count, VKD3D_MAX_VIRTUAL_HEAP_DESCRIPTORS_PER_TYPE); } static HRESULT d3d12_root_signature_init_descriptor_array_binding(struct d3d12_root_signature *root_signature, const struct d3d12_root_descriptor_table_range *range, D3D12_SHADER_VISIBILITY visibility, struct vkd3d_descriptor_set_context *context) { enum vkd3d_shader_visibility shader_visibility = vkd3d_shader_visibility_from_d3d12(visibility); bool is_buffer = range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_CBV; enum vkd3d_shader_descriptor_type descriptor_type = range->type; if (range->descriptor_count == UINT_MAX) context->unbounded_offset = range->offset; if (descriptor_type == VKD3D_SHADER_DESCRIPTOR_TYPE_SRV || descriptor_type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV) { if (!vk_binding_from_d3d12_descriptor_range(context->current_binding, descriptor_type, visibility, true, context->descriptor_binding, range->vk_binding_count)) return E_NOTIMPL; ++context->current_binding; d3d12_root_signature_append_vk_binding(root_signature, descriptor_type, range->register_space, range->base_register_idx, true, shader_visibility, range->vk_binding_count, context); } if (!vk_binding_from_d3d12_descriptor_range(context->current_binding, descriptor_type, visibility, is_buffer, context->descriptor_binding, range->vk_binding_count)) return E_NOTIMPL; ++context->current_binding; d3d12_root_signature_append_vk_binding(root_signature, descriptor_type, range->register_space, range->base_register_idx, is_buffer, shader_visibility, range->vk_binding_count, context); context->unbounded_offset = UINT_MAX; return S_OK; } static void d3d12_root_signature_map_vk_unbounded_binding(struct d3d12_root_signature *root_signature, const struct d3d12_root_descriptor_table_range *range, unsigned int descriptor_offset, bool buffer_descriptor, enum vkd3d_shader_visibility shader_visibility, struct vkd3d_descriptor_set_context *context) { struct vkd3d_shader_resource_binding *mapping = &root_signature->descriptor_mapping[context->descriptor_index]; struct vkd3d_shader_descriptor_offset *offset = &root_signature->descriptor_offsets[context->descriptor_index++]; mapping->type = range->type; mapping->register_space = range->register_space; mapping->register_index = range->base_register_idx; mapping->shader_visibility = shader_visibility; mapping->flags = buffer_descriptor ? VKD3D_SHADER_BINDING_FLAG_BUFFER : VKD3D_SHADER_BINDING_FLAG_IMAGE; mapping->binding.set = root_signature->main_set + range->set + ((range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_SRV || range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV) && !buffer_descriptor); mapping->binding.binding = range->binding; mapping->binding.count = range->vk_binding_count; offset->static_offset = descriptor_offset; offset->dynamic_offset_index = ~0u; } static unsigned int vk_heap_binding_count_from_descriptor_range(const struct d3d12_root_descriptor_table_range *range, unsigned int descriptor_set_size) { unsigned int max_count; if (descriptor_set_size <= range->offset) { ERR("Descriptor range offset %u exceeds maximum available offset %u.\n", range->offset, descriptor_set_size - 1); max_count = 0; } else { max_count = descriptor_set_size - range->offset; } if (range->descriptor_count != UINT_MAX) { if (range->descriptor_count > max_count) ERR("Range size %u exceeds available descriptor count %u.\n", range->descriptor_count, max_count); return range->descriptor_count; } else { /* Prefer an unsupported binding count vs a zero count, because shader compilation will fail * to match a declaration to a zero binding, resulting in failure of pipline state creation. */ return max_count + !max_count; } } static void vkd3d_descriptor_heap_binding_from_descriptor_range(const struct d3d12_root_descriptor_table_range *range, bool is_buffer, const struct d3d12_root_signature *root_signature, struct vkd3d_shader_descriptor_binding *binding) { const struct vkd3d_device_descriptor_limits *descriptor_limits = &root_signature->device->vk_info.descriptor_limits; unsigned int descriptor_set_size; switch (range->type) { case VKD3D_SHADER_DESCRIPTOR_TYPE_SRV: binding->set = is_buffer ? VKD3D_SET_INDEX_UNIFORM_TEXEL_BUFFER : VKD3D_SET_INDEX_SAMPLED_IMAGE; descriptor_set_size = descriptor_limits->sampled_image_max_descriptors; break; case VKD3D_SHADER_DESCRIPTOR_TYPE_UAV: binding->set = is_buffer ? VKD3D_SET_INDEX_STORAGE_TEXEL_BUFFER : VKD3D_SET_INDEX_STORAGE_IMAGE; descriptor_set_size = descriptor_limits->storage_image_max_descriptors; break; case VKD3D_SHADER_DESCRIPTOR_TYPE_CBV: binding->set = VKD3D_SET_INDEX_UNIFORM_BUFFER; descriptor_set_size = descriptor_limits->uniform_buffer_max_descriptors; break; case VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER: binding->set = VKD3D_SET_INDEX_SAMPLER; descriptor_set_size = descriptor_limits->sampler_max_descriptors; break; default: FIXME("Unhandled descriptor range type type %#x.\n", range->type); binding->set = VKD3D_SET_INDEX_SAMPLED_IMAGE; descriptor_set_size = descriptor_limits->sampled_image_max_descriptors; break; } binding->set += root_signature->vk_set_count; binding->binding = 0; binding->count = vk_heap_binding_count_from_descriptor_range(range, descriptor_set_size); } static void d3d12_root_signature_map_vk_heap_binding(struct d3d12_root_signature *root_signature, const struct d3d12_root_descriptor_table_range *range, bool buffer_descriptor, enum vkd3d_shader_visibility shader_visibility, struct vkd3d_descriptor_set_context *context) { struct vkd3d_shader_resource_binding *mapping = &root_signature->descriptor_mapping[context->descriptor_index]; struct vkd3d_shader_descriptor_offset *offset = &root_signature->descriptor_offsets[context->descriptor_index++]; mapping->type = range->type; mapping->register_space = range->register_space; mapping->register_index = range->base_register_idx; mapping->shader_visibility = shader_visibility; mapping->flags = buffer_descriptor ? VKD3D_SHADER_BINDING_FLAG_BUFFER : VKD3D_SHADER_BINDING_FLAG_IMAGE; vkd3d_descriptor_heap_binding_from_descriptor_range(range, buffer_descriptor, root_signature, &mapping->binding); offset->static_offset = range->offset; offset->dynamic_offset_index = context->push_constant_index; } static void d3d12_root_signature_map_vk_heap_uav_counter(struct d3d12_root_signature *root_signature, const struct d3d12_root_descriptor_table_range *range, enum vkd3d_shader_visibility shader_visibility, struct vkd3d_descriptor_set_context *context) { struct vkd3d_shader_uav_counter_binding *mapping = &root_signature->uav_counter_mapping[context->uav_counter_index]; struct vkd3d_shader_descriptor_offset *offset = &root_signature->uav_counter_offsets[context->uav_counter_index++]; mapping->register_space = range->register_space; mapping->register_index = range->base_register_idx; mapping->shader_visibility = shader_visibility; mapping->binding.set = root_signature->vk_set_count + VKD3D_SET_INDEX_UAV_COUNTER; mapping->binding.binding = 0; mapping->binding.count = vk_heap_binding_count_from_descriptor_range(range, root_signature->device->vk_info.descriptor_limits.storage_image_max_descriptors); offset->static_offset = range->offset; offset->dynamic_offset_index = context->push_constant_index; } static void d3d12_root_signature_map_descriptor_heap_binding(struct d3d12_root_signature *root_signature, const struct d3d12_root_descriptor_table_range *range, enum vkd3d_shader_visibility shader_visibility, struct vkd3d_descriptor_set_context *context) { bool is_buffer = range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_CBV; if (range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_SRV || range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV) { d3d12_root_signature_map_vk_heap_binding(root_signature, range, true, shader_visibility, context); if (range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV) d3d12_root_signature_map_vk_heap_uav_counter(root_signature, range, shader_visibility, context); } d3d12_root_signature_map_vk_heap_binding(root_signature, range, is_buffer, shader_visibility, context); } static void d3d12_root_signature_map_descriptor_unbounded_binding(struct d3d12_root_signature *root_signature, const struct d3d12_root_descriptor_table_range *range, unsigned int descriptor_offset, enum vkd3d_shader_visibility shader_visibility, struct vkd3d_descriptor_set_context *context) { bool is_buffer = range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_CBV; if (range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_SRV || range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV) d3d12_root_signature_map_vk_unbounded_binding(root_signature, range, descriptor_offset, true, shader_visibility, context); d3d12_root_signature_map_vk_unbounded_binding(root_signature, range, descriptor_offset, is_buffer, shader_visibility, context); } static int compare_register_range(const void *a, const void *b) { const struct d3d12_root_descriptor_table_range *range_a = a, *range_b = b; int ret; if ((ret = vkd3d_u32_compare(range_a->type, range_b->type))) return ret; if ((ret = vkd3d_u32_compare(range_a->register_space, range_b->register_space))) return ret; return vkd3d_u32_compare(range_a->base_register_idx, range_b->base_register_idx); } static int compare_descriptor_range(const void *a, const void *b) { const struct d3d12_root_descriptor_table_range *range_a = a, *range_b = b; int ret; if ((ret = vkd3d_u32_compare(range_a->type, range_b->type))) return ret; if ((ret = vkd3d_u32_compare(range_a->offset, range_b->offset))) return ret; return (range_a->descriptor_count == UINT_MAX) - (range_b->descriptor_count == UINT_MAX); } static HRESULT validate_descriptor_register_ranges(const struct d3d12_root_descriptor_table_range *ranges, unsigned int count) { const struct d3d12_root_descriptor_table_range *range, *prev; unsigned int i; for (i = 1; i < count; ++i) { range = &ranges[i]; prev = &ranges[i - 1]; if (range->type == prev->type && range->register_space == prev->register_space && range->base_register_idx - prev->base_register_idx < prev->descriptor_count) return E_INVALIDARG; } return S_OK; } static HRESULT d3d12_root_signature_init_root_descriptor_tables(struct d3d12_root_signature *root_signature, const D3D12_ROOT_SIGNATURE_DESC *desc, const struct d3d12_root_signature_info *info, struct vkd3d_descriptor_set_context *context) { const struct d3d12_device *device = root_signature->device; bool use_vk_heaps = root_signature->device->use_vk_heaps; struct d3d12_root_descriptor_table *table; unsigned int i, j, k, range_count; uint32_t vk_binding; HRESULT hr; root_signature->descriptor_table_mask = 0; for (i = 0; i < desc->NumParameters; ++i) { const struct d3d12_root_descriptor_table_range *base_range = NULL; const D3D12_ROOT_PARAMETER *p = &desc->pParameters[i]; enum vkd3d_shader_visibility shader_visibility; unsigned int offset = 0; if (p->ParameterType != D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) continue; root_signature->descriptor_table_mask |= 1ull << i; table = &root_signature->parameters[i].u.descriptor_table; range_count = p->u.DescriptorTable.NumDescriptorRanges; shader_visibility = vkd3d_shader_visibility_from_d3d12(p->ShaderVisibility); root_signature->parameters[i].parameter_type = p->ParameterType; table->range_count = range_count; if (!(table->ranges = vkd3d_calloc(table->range_count, sizeof(*table->ranges)))) return E_OUTOFMEMORY; context->table_index = i; for (j = 0; j < range_count; ++j) { const D3D12_DESCRIPTOR_RANGE *range = &p->u.DescriptorTable.pDescriptorRanges[j]; if (range->OffsetInDescriptorsFromTableStart != D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) offset = range->OffsetInDescriptorsFromTableStart; if (range->NumDescriptors != UINT_MAX && !vkd3d_bound_range(offset, range->NumDescriptors, UINT_MAX)) return E_INVALIDARG; table->ranges[j].offset = offset; table->ranges[j].descriptor_count = range->NumDescriptors; table->ranges[j].type = vkd3d_descriptor_type_from_d3d12_range_type(range->RangeType); table->ranges[j].descriptor_magic = vkd3d_descriptor_magic_from_d3d12(range->RangeType); table->ranges[j].register_space = range->RegisterSpace; table->ranges[j].base_register_idx = range->BaseShaderRegister; TRACE("Descriptor table %u, range %u, offset %u, type %#x, count %u.\n", i, j, offset, range->RangeType, range->NumDescriptors); /* If NumDescriptors == UINT_MAX, validation during counting ensures this offset is not used. */ offset += range->NumDescriptors; } qsort(table->ranges, range_count, sizeof(*table->ranges), compare_register_range); if (FAILED(hr = validate_descriptor_register_ranges(table->ranges, range_count))) return hr; qsort(table->ranges, range_count, sizeof(*table->ranges), compare_descriptor_range); for (j = 0; j < range_count; ++j) { struct d3d12_root_descriptor_table_range *range; VkDescriptorSetLayoutBinding *cur_binding; range = &table->ranges[j]; if (use_vk_heaps) { /* set, binding and vk_binding_count are not used. */ range->set = 0; range->binding = 0; range->vk_binding_count = 0; d3d12_root_signature_map_descriptor_heap_binding(root_signature, range, shader_visibility, context); continue; } range->set = root_signature->vk_set_count - root_signature->main_set; if (root_signature->use_descriptor_arrays) { if (j && range->type != table->ranges[j - 1].type) base_range = NULL; /* Bounded and unbounded ranges can follow unbounded ones, * so map them all into the first unbounded range. */ if (base_range) { unsigned int rel_offset = range->offset - base_range->offset; if (rel_offset >= base_range->vk_binding_count) { ERR("Available binding size of %u is insufficient for an offset of %u.\n", base_range->vk_binding_count, rel_offset); continue; } range->set = base_range->set; range->binding = base_range->binding; range->vk_binding_count = base_range->vk_binding_count - rel_offset; d3d12_root_signature_map_descriptor_unbounded_binding(root_signature, range, rel_offset, shader_visibility, context); continue; } else if (range->descriptor_count == UINT_MAX) { base_range = range; } range->binding = context->descriptor_binding; range->vk_binding_count = vk_binding_count_from_descriptor_range(range, info, &device->vk_info.descriptor_limits); if (FAILED(hr = d3d12_root_signature_init_descriptor_array_binding(root_signature, range, p->ShaderVisibility, context))) return hr; continue; } cur_binding = context->current_binding; vk_binding = d3d12_root_signature_assign_vk_bindings(root_signature, range->type, range->register_space, range->base_register_idx, range->descriptor_count, false, true, shader_visibility, context); /* Unroll descriptor range. */ for (k = 0; k < range->descriptor_count; ++k) { uint32_t vk_current_binding = vk_binding + k; if (range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_SRV || range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV) { vk_current_binding = vk_binding + 2 * k; /* Assign binding for image view. */ if (!vk_binding_from_d3d12_descriptor_range(cur_binding, range->type, p->ShaderVisibility, false, vk_current_binding + 1, 1)) return E_NOTIMPL; ++cur_binding; } if (!vk_binding_from_d3d12_descriptor_range(cur_binding, range->type, p->ShaderVisibility, true, vk_current_binding, 1)) return E_NOTIMPL; ++cur_binding; } table->ranges[j].vk_binding_count = table->ranges[j].descriptor_count; table->ranges[j].binding = vk_binding; context->current_binding = cur_binding; } ++context->push_constant_index; } return S_OK; } static HRESULT d3d12_root_signature_init_root_descriptors(struct d3d12_root_signature *root_signature, const D3D12_ROOT_SIGNATURE_DESC *desc, struct vkd3d_descriptor_set_context *context) { VkDescriptorSetLayoutBinding *cur_binding = context->current_binding; unsigned int i; root_signature->push_descriptor_mask = 0; for (i = 0; i < desc->NumParameters; ++i) { const D3D12_ROOT_PARAMETER *p = &desc->pParameters[i]; if (p->ParameterType != D3D12_ROOT_PARAMETER_TYPE_CBV && p->ParameterType != D3D12_ROOT_PARAMETER_TYPE_SRV && p->ParameterType != D3D12_ROOT_PARAMETER_TYPE_UAV) continue; root_signature->push_descriptor_mask |= 1u << i; cur_binding->binding = d3d12_root_signature_assign_vk_bindings(root_signature, vkd3d_descriptor_type_from_d3d12_root_parameter_type(p->ParameterType), p->u.Descriptor.RegisterSpace, p->u.Descriptor.ShaderRegister, 1, true, false, vkd3d_shader_visibility_from_d3d12(p->ShaderVisibility), context); cur_binding->descriptorType = vk_descriptor_type_from_d3d12_root_parameter(p->ParameterType); cur_binding->descriptorCount = 1; cur_binding->stageFlags = stage_flags_from_visibility(p->ShaderVisibility); cur_binding->pImmutableSamplers = NULL; root_signature->parameters[i].parameter_type = p->ParameterType; root_signature->parameters[i].u.descriptor.binding = cur_binding->binding; ++cur_binding; } context->current_binding = cur_binding; return S_OK; } static HRESULT d3d12_root_signature_init_static_samplers(struct d3d12_root_signature *root_signature, struct d3d12_device *device, const D3D12_ROOT_SIGNATURE_DESC *desc, struct vkd3d_descriptor_set_context *context) { VkDescriptorSetLayoutBinding *cur_binding = context->current_binding; unsigned int i; HRESULT hr; assert(root_signature->static_sampler_count == desc->NumStaticSamplers); for (i = 0; i < desc->NumStaticSamplers; ++i) { const D3D12_STATIC_SAMPLER_DESC *s = &desc->pStaticSamplers[i]; if (FAILED(hr = vkd3d_create_static_sampler(device, s, &root_signature->static_samplers[i]))) return hr; cur_binding->binding = d3d12_root_signature_assign_vk_bindings(root_signature, VKD3D_SHADER_DESCRIPTOR_TYPE_SAMPLER, s->RegisterSpace, s->ShaderRegister, 1, false, false, vkd3d_shader_visibility_from_d3d12(s->ShaderVisibility), context); cur_binding->descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; cur_binding->descriptorCount = 1; cur_binding->stageFlags = stage_flags_from_visibility(s->ShaderVisibility); cur_binding->pImmutableSamplers = &root_signature->static_samplers[i]; ++cur_binding; } context->current_binding = cur_binding; if (device->use_vk_heaps) return d3d12_root_signature_append_descriptor_set_layout(root_signature, context, 0); return S_OK; } static void d3d12_root_signature_init_descriptor_table_push_constants(struct d3d12_root_signature *root_signature, const struct vkd3d_descriptor_set_context *context) { root_signature->descriptor_table_offset = 0; if ((root_signature->descriptor_table_count = context->push_constant_index)) { VkPushConstantRange *range = &root_signature->push_constant_ranges[D3D12_SHADER_VISIBILITY_ALL]; root_signature->descriptor_table_offset = align(range->size, 16); range->size = root_signature->descriptor_table_offset + root_signature->descriptor_table_count * sizeof(uint32_t); if (range->size > root_signature->device->vk_info.device_limits.maxPushConstantsSize) FIXME("Push constants size %u exceeds maximum allowed size %u. Try VKD3D_CONFIG=virtual_heaps.\n", range->size, root_signature->device->vk_info.device_limits.maxPushConstantsSize); if (!root_signature->push_constant_range_count) { root_signature->push_constant_range_count = 1; range->stageFlags = VK_SHADER_STAGE_ALL; } } } static bool vk_binding_uses_partial_binding(const VkDescriptorSetLayoutBinding *binding) { if (binding->descriptorCount == 1) return false; switch (binding->descriptorType) { /* Types mapped in vk_descriptor_type_from_vkd3d_descriptor_type() from D3D12 SRV and UAV types, * i.e. those which can be a buffer or an image. */ case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: return true; default: return false; } } static HRESULT vkd3d_create_descriptor_set_layout(struct d3d12_device *device, VkDescriptorSetLayoutCreateFlags flags, unsigned int binding_count, bool unbounded, const VkDescriptorSetLayoutBinding *bindings, VkDescriptorSetLayout *set_layout) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VkDescriptorSetLayoutBindingFlagsCreateInfoEXT flags_info; VkDescriptorBindingFlagsEXT *set_flags = NULL; VkDescriptorSetLayoutCreateInfo set_desc; VkResult vr; set_desc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; set_desc.pNext = NULL; set_desc.flags = flags; set_desc.bindingCount = binding_count; set_desc.pBindings = bindings; if (device->vk_info.EXT_descriptor_indexing) { unsigned int i; for (i = 0; i < binding_count; ++i) if (unbounded || vk_binding_uses_partial_binding(&bindings[i])) break; if (i < binding_count) { if (!(set_flags = vkd3d_malloc(binding_count * sizeof(*set_flags)))) return E_OUTOFMEMORY; for (i = 0; i < binding_count; ++i) set_flags[i] = vk_binding_uses_partial_binding(&bindings[i]) ? VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT : 0; if (unbounded) set_flags[binding_count - 1] = VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT; flags_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT; flags_info.pNext = NULL; flags_info.bindingCount = binding_count; flags_info.pBindingFlags = set_flags; set_desc.pNext = &flags_info; } } vr = VK_CALL(vkCreateDescriptorSetLayout(device->vk_device, &set_desc, NULL, set_layout)); vkd3d_free(set_flags); if (vr < 0) { WARN("Failed to create Vulkan descriptor set layout, vr %d.\n", vr); return hresult_from_vk_result(vr); } return S_OK; } static HRESULT vkd3d_create_pipeline_layout(struct d3d12_device *device, unsigned int set_layout_count, const VkDescriptorSetLayout *set_layouts, unsigned int push_constant_count, const VkPushConstantRange *push_constants, VkPipelineLayout *pipeline_layout) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; struct VkPipelineLayoutCreateInfo pipeline_layout_info; VkResult vr; if (!vkd3d_validate_descriptor_set_count(device, set_layout_count)) return E_INVALIDARG; pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_info.pNext = NULL; pipeline_layout_info.flags = 0; pipeline_layout_info.setLayoutCount = set_layout_count; pipeline_layout_info.pSetLayouts = set_layouts; pipeline_layout_info.pushConstantRangeCount = push_constant_count; pipeline_layout_info.pPushConstantRanges = push_constants; if ((vr = VK_CALL(vkCreatePipelineLayout(device->vk_device, &pipeline_layout_info, NULL, pipeline_layout))) < 0) { WARN("Failed to create Vulkan pipeline layout, vr %d.\n", vr); return hresult_from_vk_result(vr); } return S_OK; } static unsigned int d3d12_root_signature_copy_descriptor_set_layouts(const struct d3d12_root_signature *root_signature, VkDescriptorSetLayout *vk_set_layouts) { const struct d3d12_device *device = root_signature->device; enum vkd3d_vk_descriptor_set_index set; unsigned int i; for (i = 0; i < root_signature->vk_set_count; ++i) vk_set_layouts[i] = root_signature->descriptor_set_layouts[i].vk_layout; if (device->use_vk_heaps) { for (set = 0; set < ARRAY_SIZE(device->vk_descriptor_heap_layouts); ++set) vk_set_layouts[i++] = device->vk_descriptor_heap_layouts[set].vk_set_layout; } return i; } static HRESULT d3d12_root_signature_init(struct d3d12_root_signature *root_signature, struct d3d12_device *device, const D3D12_ROOT_SIGNATURE_DESC *desc) { VkDescriptorSetLayout vk_layouts[VKD3D_MAX_DESCRIPTOR_SETS]; const struct vkd3d_vulkan_info *vk_info = &device->vk_info; struct vkd3d_descriptor_set_context context; VkDescriptorSetLayoutBinding *binding_desc; struct d3d12_root_signature_info info; bool use_vk_heaps; unsigned int i; HRESULT hr; memset(&context, 0, sizeof(context)); context.unbounded_offset = UINT_MAX; binding_desc = NULL; root_signature->ID3D12RootSignature_iface.lpVtbl = &d3d12_root_signature_vtbl; root_signature->refcount = 1; root_signature->vk_pipeline_layout = VK_NULL_HANDLE; root_signature->vk_set_count = 0; root_signature->parameters = NULL; root_signature->flags = desc->Flags; root_signature->descriptor_mapping = NULL; root_signature->descriptor_offsets = NULL; root_signature->uav_counter_mapping = NULL; root_signature->uav_counter_offsets = NULL; root_signature->static_sampler_count = 0; root_signature->static_samplers = NULL; root_signature->device = device; if (desc->Flags & ~(D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT)) FIXME("Ignoring root signature flags %#x.\n", desc->Flags); if (FAILED(hr = d3d12_root_signature_info_from_desc(&info, desc, device->vk_info.EXT_descriptor_indexing))) return hr; if (info.cost > D3D12_MAX_ROOT_COST) { WARN("Root signature cost %zu exceeds maximum allowed cost.\n", info.cost); return E_INVALIDARG; } root_signature->binding_count = info.binding_count; root_signature->uav_mapping_count = info.uav_range_count; root_signature->static_sampler_count = desc->NumStaticSamplers; root_signature->root_descriptor_count = info.root_descriptor_count; root_signature->use_descriptor_arrays = device->vk_info.EXT_descriptor_indexing; root_signature->descriptor_table_count = 0; use_vk_heaps = device->use_vk_heaps; hr = E_OUTOFMEMORY; root_signature->parameter_count = desc->NumParameters; if (!(root_signature->parameters = vkd3d_calloc(root_signature->parameter_count, sizeof(*root_signature->parameters)))) goto fail; if (!(root_signature->descriptor_mapping = vkd3d_calloc(root_signature->binding_count, sizeof(*root_signature->descriptor_mapping)))) goto fail; if (use_vk_heaps && (!(root_signature->uav_counter_mapping = vkd3d_calloc(root_signature->uav_mapping_count, sizeof(*root_signature->uav_counter_mapping))) || !(root_signature->uav_counter_offsets = vkd3d_calloc(root_signature->uav_mapping_count, sizeof(*root_signature->uav_counter_offsets))))) goto fail; if (root_signature->use_descriptor_arrays && !(root_signature->descriptor_offsets = vkd3d_calloc( root_signature->binding_count, sizeof(*root_signature->descriptor_offsets)))) goto fail; root_signature->root_constant_count = info.root_constant_count; if (!(root_signature->root_constants = vkd3d_calloc(root_signature->root_constant_count, sizeof(*root_signature->root_constants)))) goto fail; if (!(root_signature->static_samplers = vkd3d_calloc(root_signature->static_sampler_count, sizeof(*root_signature->static_samplers)))) goto fail; if (!(binding_desc = vkd3d_calloc(info.binding_count, sizeof(*binding_desc)))) goto fail; context.first_binding = binding_desc; context.current_binding = binding_desc; if (FAILED(hr = d3d12_root_signature_init_root_descriptors(root_signature, desc, &context))) goto fail; /* We use KHR_push_descriptor for root descriptor parameters. */ if (vk_info->KHR_push_descriptor) { if (FAILED(hr = d3d12_root_signature_append_descriptor_set_layout(root_signature, &context, VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR))) goto fail; } root_signature->main_set = root_signature->vk_set_count; if (FAILED(hr = d3d12_root_signature_init_push_constants(root_signature, desc, root_signature->push_constant_ranges, &root_signature->push_constant_range_count))) goto fail; if (FAILED(hr = d3d12_root_signature_init_static_samplers(root_signature, device, desc, &context))) goto fail; context.push_constant_index = 0; if (FAILED(hr = d3d12_root_signature_init_root_descriptor_tables(root_signature, desc, &info, &context))) goto fail; if (use_vk_heaps) d3d12_root_signature_init_descriptor_table_push_constants(root_signature, &context); if (FAILED(hr = d3d12_root_signature_append_descriptor_set_layout(root_signature, &context, 0))) goto fail; vkd3d_free(binding_desc); binding_desc = NULL; i = d3d12_root_signature_copy_descriptor_set_layouts(root_signature, vk_layouts); if (FAILED(hr = vkd3d_create_pipeline_layout(device, i, vk_layouts, root_signature->push_constant_range_count, root_signature->push_constant_ranges, &root_signature->vk_pipeline_layout))) goto fail; if (FAILED(hr = vkd3d_private_store_init(&root_signature->private_store))) goto fail; d3d12_device_add_ref(device); return S_OK; fail: vkd3d_free(binding_desc); d3d12_root_signature_cleanup(root_signature, device); return hr; } HRESULT d3d12_root_signature_create(struct d3d12_device *device, const void *bytecode, size_t bytecode_length, struct d3d12_root_signature **root_signature) { const struct vkd3d_shader_code dxbc = {bytecode, bytecode_length}; union { D3D12_VERSIONED_ROOT_SIGNATURE_DESC d3d12; struct vkd3d_shader_versioned_root_signature_desc vkd3d; } root_signature_desc; struct d3d12_root_signature *object; HRESULT hr; int ret; if ((ret = vkd3d_parse_root_signature_v_1_0(&dxbc, &root_signature_desc.vkd3d)) < 0) { WARN("Failed to parse root signature, vkd3d result %d.\n", ret); return hresult_from_vkd3d_result(ret); } if (!(object = vkd3d_malloc(sizeof(*object)))) { vkd3d_shader_free_root_signature(&root_signature_desc.vkd3d); return E_OUTOFMEMORY; } hr = d3d12_root_signature_init(object, device, &root_signature_desc.d3d12.u.Desc_1_0); vkd3d_shader_free_root_signature(&root_signature_desc.vkd3d); if (FAILED(hr)) { vkd3d_free(object); return hr; } TRACE("Created root signature %p.\n", object); *root_signature = object; return S_OK; } /* vkd3d_render_pass_cache */ struct vkd3d_render_pass_entry { struct vkd3d_render_pass_key key; VkRenderPass vk_render_pass; }; STATIC_ASSERT(sizeof(struct vkd3d_render_pass_key) == 48); static HRESULT vkd3d_render_pass_cache_create_pass_locked(struct vkd3d_render_pass_cache *cache, struct d3d12_device *device, const struct vkd3d_render_pass_key *key, VkRenderPass *vk_render_pass) { VkAttachmentReference attachment_references[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT + 1]; VkAttachmentDescription attachments[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT + 1]; const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; struct vkd3d_render_pass_entry *entry; unsigned int index, attachment_index; VkSubpassDescription sub_pass_desc; VkRenderPassCreateInfo pass_info; bool have_depth_stencil; unsigned int rt_count; VkResult vr; if (!vkd3d_array_reserve((void **)&cache->render_passes, &cache->render_passes_size, cache->render_pass_count + 1, sizeof(*cache->render_passes))) { *vk_render_pass = VK_NULL_HANDLE; return E_OUTOFMEMORY; } entry = &cache->render_passes[cache->render_pass_count]; entry->key = *key; have_depth_stencil = key->depth_enable || key->stencil_enable; rt_count = have_depth_stencil ? key->attachment_count - 1 : key->attachment_count; assert(rt_count <= D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT); for (index = 0, attachment_index = 0; index < rt_count; ++index) { if (!key->vk_formats[index]) { attachment_references[index].attachment = VK_ATTACHMENT_UNUSED; attachment_references[index].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; continue; } attachments[attachment_index].flags = 0; attachments[attachment_index].format = key->vk_formats[index]; attachments[attachment_index].samples = key->sample_count; attachments[attachment_index].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachments[attachment_index].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[attachment_index].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[attachment_index].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[attachment_index].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachments[attachment_index].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachment_references[index].attachment = attachment_index; attachment_references[index].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; ++attachment_index; } if (have_depth_stencil) { VkImageLayout depth_layout = key->depth_stencil_write ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; attachments[attachment_index].flags = 0; attachments[attachment_index].format = key->vk_formats[index]; attachments[attachment_index].samples = key->sample_count; if (key->depth_enable) { attachments[attachment_index].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachments[attachment_index].storeOp = VK_ATTACHMENT_STORE_OP_STORE; } else { attachments[attachment_index].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[attachment_index].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; } if (key->stencil_enable) { attachments[attachment_index].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachments[attachment_index].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; } else { attachments[attachment_index].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[attachment_index].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; } attachments[attachment_index].initialLayout = depth_layout; attachments[attachment_index].finalLayout = depth_layout; attachment_references[index].attachment = attachment_index; attachment_references[index].layout = depth_layout; attachment_index++; } sub_pass_desc.flags = 0; sub_pass_desc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; sub_pass_desc.inputAttachmentCount = 0; sub_pass_desc.pInputAttachments = NULL; sub_pass_desc.colorAttachmentCount = rt_count; sub_pass_desc.pColorAttachments = attachment_references; sub_pass_desc.pResolveAttachments = NULL; if (have_depth_stencil) sub_pass_desc.pDepthStencilAttachment = &attachment_references[rt_count]; else sub_pass_desc.pDepthStencilAttachment = NULL; sub_pass_desc.preserveAttachmentCount = 0; sub_pass_desc.pPreserveAttachments = NULL; pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; pass_info.pNext = NULL; pass_info.flags = 0; pass_info.attachmentCount = attachment_index; pass_info.pAttachments = attachments; pass_info.subpassCount = 1; pass_info.pSubpasses = &sub_pass_desc; pass_info.dependencyCount = 0; pass_info.pDependencies = NULL; if ((vr = VK_CALL(vkCreateRenderPass(device->vk_device, &pass_info, NULL, vk_render_pass))) >= 0) { entry->vk_render_pass = *vk_render_pass; ++cache->render_pass_count; } else { WARN("Failed to create Vulkan render pass, vr %d.\n", vr); *vk_render_pass = VK_NULL_HANDLE; } return hresult_from_vk_result(vr); } HRESULT vkd3d_render_pass_cache_find(struct vkd3d_render_pass_cache *cache, struct d3d12_device *device, const struct vkd3d_render_pass_key *key, VkRenderPass *vk_render_pass) { bool found = false; HRESULT hr = S_OK; unsigned int i; vkd3d_mutex_lock(&device->mutex); for (i = 0; i < cache->render_pass_count; ++i) { struct vkd3d_render_pass_entry *current = &cache->render_passes[i]; if (!memcmp(¤t->key, key, sizeof(*key))) { *vk_render_pass = current->vk_render_pass; found = true; break; } } if (!found) hr = vkd3d_render_pass_cache_create_pass_locked(cache, device, key, vk_render_pass); vkd3d_mutex_unlock(&device->mutex); return hr; } void vkd3d_render_pass_cache_init(struct vkd3d_render_pass_cache *cache) { cache->render_passes = NULL; cache->render_pass_count = 0; cache->render_passes_size = 0; } void vkd3d_render_pass_cache_cleanup(struct vkd3d_render_pass_cache *cache, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; unsigned int i; for (i = 0; i < cache->render_pass_count; ++i) { struct vkd3d_render_pass_entry *current = &cache->render_passes[i]; VK_CALL(vkDestroyRenderPass(device->vk_device, current->vk_render_pass, NULL)); } vkd3d_free(cache->render_passes); cache->render_passes = NULL; } struct vkd3d_pipeline_key { D3D12_PRIMITIVE_TOPOLOGY topology; uint32_t strides[D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT]; VkFormat dsv_format; }; struct vkd3d_compiled_pipeline { struct list entry; struct vkd3d_pipeline_key key; VkPipeline vk_pipeline; VkRenderPass vk_render_pass; }; /* ID3D12PipelineState */ static inline struct d3d12_pipeline_state *impl_from_ID3D12PipelineState(ID3D12PipelineState *iface) { return CONTAINING_RECORD(iface, struct d3d12_pipeline_state, ID3D12PipelineState_iface); } static HRESULT STDMETHODCALLTYPE d3d12_pipeline_state_QueryInterface(ID3D12PipelineState *iface, REFIID riid, void **object) { TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object); if (IsEqualGUID(riid, &IID_ID3D12PipelineState) || IsEqualGUID(riid, &IID_ID3D12Pageable) || IsEqualGUID(riid, &IID_ID3D12DeviceChild) || IsEqualGUID(riid, &IID_ID3D12Object) || IsEqualGUID(riid, &IID_IUnknown)) { ID3D12PipelineState_AddRef(iface); *object = iface; return S_OK; } WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid)); *object = NULL; return E_NOINTERFACE; } static ULONG STDMETHODCALLTYPE d3d12_pipeline_state_AddRef(ID3D12PipelineState *iface) { struct d3d12_pipeline_state *state = impl_from_ID3D12PipelineState(iface); ULONG refcount = InterlockedIncrement(&state->refcount); TRACE("%p increasing refcount to %u.\n", state, refcount); return refcount; } static void d3d12_pipeline_state_destroy_graphics(struct d3d12_pipeline_state *state, struct d3d12_device *device) { struct d3d12_graphics_pipeline_state *graphics = &state->u.graphics; const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; struct vkd3d_compiled_pipeline *current, *e; unsigned int i; for (i = 0; i < graphics->stage_count; ++i) { VK_CALL(vkDestroyShaderModule(device->vk_device, graphics->stages[i].module, NULL)); } LIST_FOR_EACH_ENTRY_SAFE(current, e, &graphics->compiled_pipelines, struct vkd3d_compiled_pipeline, entry) { VK_CALL(vkDestroyPipeline(device->vk_device, current->vk_pipeline, NULL)); vkd3d_free(current); } } static void d3d12_pipeline_uav_counter_state_cleanup(struct d3d12_pipeline_uav_counter_state *uav_counters, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; if (uav_counters->vk_set_layout) VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, uav_counters->vk_set_layout, NULL)); if (uav_counters->vk_pipeline_layout) VK_CALL(vkDestroyPipelineLayout(device->vk_device, uav_counters->vk_pipeline_layout, NULL)); vkd3d_free(uav_counters->bindings); } static ULONG STDMETHODCALLTYPE d3d12_pipeline_state_Release(ID3D12PipelineState *iface) { struct d3d12_pipeline_state *state = impl_from_ID3D12PipelineState(iface); ULONG refcount = InterlockedDecrement(&state->refcount); TRACE("%p decreasing refcount to %u.\n", state, refcount); if (!refcount) { struct d3d12_device *device = state->device; const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; vkd3d_private_store_destroy(&state->private_store); if (d3d12_pipeline_state_is_graphics(state)) d3d12_pipeline_state_destroy_graphics(state, device); else if (d3d12_pipeline_state_is_compute(state)) VK_CALL(vkDestroyPipeline(device->vk_device, state->u.compute.vk_pipeline, NULL)); d3d12_pipeline_uav_counter_state_cleanup(&state->uav_counters, device); vkd3d_free(state); d3d12_device_release(device); } return refcount; } static HRESULT STDMETHODCALLTYPE d3d12_pipeline_state_GetPrivateData(ID3D12PipelineState *iface, REFGUID guid, UINT *data_size, void *data) { struct d3d12_pipeline_state *state = impl_from_ID3D12PipelineState(iface); TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data); return vkd3d_get_private_data(&state->private_store, guid, data_size, data); } static HRESULT STDMETHODCALLTYPE d3d12_pipeline_state_SetPrivateData(ID3D12PipelineState *iface, REFGUID guid, UINT data_size, const void *data) { struct d3d12_pipeline_state *state = impl_from_ID3D12PipelineState(iface); TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data); return vkd3d_set_private_data(&state->private_store, guid, data_size, data); } static HRESULT STDMETHODCALLTYPE d3d12_pipeline_state_SetPrivateDataInterface(ID3D12PipelineState *iface, REFGUID guid, const IUnknown *data) { struct d3d12_pipeline_state *state = impl_from_ID3D12PipelineState(iface); TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data); return vkd3d_set_private_data_interface(&state->private_store, guid, data); } static HRESULT STDMETHODCALLTYPE d3d12_pipeline_state_SetName(ID3D12PipelineState *iface, const WCHAR *name) { struct d3d12_pipeline_state *state = impl_from_ID3D12PipelineState(iface); TRACE("iface %p, name %s.\n", iface, debugstr_w(name, state->device->wchar_size)); if (d3d12_pipeline_state_is_compute(state)) { return vkd3d_set_vk_object_name(state->device, (uint64_t)state->u.compute.vk_pipeline, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, name); } return name ? S_OK : E_INVALIDARG; } static HRESULT STDMETHODCALLTYPE d3d12_pipeline_state_GetDevice(ID3D12PipelineState *iface, REFIID iid, void **device) { struct d3d12_pipeline_state *state = impl_from_ID3D12PipelineState(iface); TRACE("iface %p, iid %s, device %p.\n", iface, debugstr_guid(iid), device); return d3d12_device_query_interface(state->device, iid, device); } static HRESULT STDMETHODCALLTYPE d3d12_pipeline_state_GetCachedBlob(ID3D12PipelineState *iface, ID3DBlob **blob) { FIXME("iface %p, blob %p stub!\n", iface, blob); return E_NOTIMPL; } static const struct ID3D12PipelineStateVtbl d3d12_pipeline_state_vtbl = { /* IUnknown methods */ d3d12_pipeline_state_QueryInterface, d3d12_pipeline_state_AddRef, d3d12_pipeline_state_Release, /* ID3D12Object methods */ d3d12_pipeline_state_GetPrivateData, d3d12_pipeline_state_SetPrivateData, d3d12_pipeline_state_SetPrivateDataInterface, d3d12_pipeline_state_SetName, /* ID3D12DeviceChild methods */ d3d12_pipeline_state_GetDevice, /* ID3D12PipelineState methods */ d3d12_pipeline_state_GetCachedBlob, }; struct d3d12_pipeline_state *unsafe_impl_from_ID3D12PipelineState(ID3D12PipelineState *iface) { if (!iface) return NULL; assert(iface->lpVtbl == &d3d12_pipeline_state_vtbl); return impl_from_ID3D12PipelineState(iface); } static inline unsigned int typed_uav_compile_option(const struct d3d12_device *device) { return device->vk_info.uav_read_without_format ? VKD3D_SHADER_COMPILE_OPTION_TYPED_UAV_READ_FORMAT_UNKNOWN : VKD3D_SHADER_COMPILE_OPTION_TYPED_UAV_READ_FORMAT_R32; } static HRESULT create_shader_stage(struct d3d12_device *device, struct VkPipelineShaderStageCreateInfo *stage_desc, enum VkShaderStageFlagBits stage, const D3D12_SHADER_BYTECODE *code, const struct vkd3d_shader_interface_info *shader_interface) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; struct vkd3d_shader_compile_info compile_info; struct VkShaderModuleCreateInfo shader_desc; struct vkd3d_shader_code spirv = {0}; VkResult vr; int ret; const struct vkd3d_shader_compile_option options[] = { {VKD3D_SHADER_COMPILE_OPTION_API_VERSION, VKD3D_SHADER_API_VERSION_1_8}, {VKD3D_SHADER_COMPILE_OPTION_TYPED_UAV, typed_uav_compile_option(device)}, {VKD3D_SHADER_COMPILE_OPTION_WRITE_TESS_GEOM_POINT_SIZE, 0}, }; stage_desc->sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage_desc->pNext = NULL; stage_desc->flags = 0; stage_desc->stage = stage; stage_desc->pName = "main"; stage_desc->pSpecializationInfo = NULL; shader_desc.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; shader_desc.pNext = NULL; shader_desc.flags = 0; compile_info.type = VKD3D_SHADER_STRUCTURE_TYPE_COMPILE_INFO; compile_info.next = shader_interface; compile_info.source.code = code->pShaderBytecode; compile_info.source.size = code->BytecodeLength; compile_info.target_type = VKD3D_SHADER_TARGET_SPIRV_BINARY; compile_info.options = options; compile_info.option_count = ARRAY_SIZE(options); compile_info.log_level = VKD3D_SHADER_LOG_NONE; compile_info.source_name = NULL; if ((ret = vkd3d_shader_parse_dxbc_source_type(&compile_info.source, &compile_info.source_type, NULL)) < 0 || (ret = vkd3d_shader_compile(&compile_info, &spirv, NULL)) < 0) { WARN("Failed to compile shader, vkd3d result %d.\n", ret); return hresult_from_vkd3d_result(ret); } shader_desc.codeSize = spirv.size; shader_desc.pCode = spirv.code; vr = VK_CALL(vkCreateShaderModule(device->vk_device, &shader_desc, NULL, &stage_desc->module)); vkd3d_shader_free_shader_code(&spirv); if (vr < 0) { WARN("Failed to create Vulkan shader module, vr %d.\n", vr); return hresult_from_vk_result(vr); } return S_OK; } static int vkd3d_scan_dxbc(const struct d3d12_device *device, const D3D12_SHADER_BYTECODE *code, struct vkd3d_shader_scan_descriptor_info *descriptor_info) { struct vkd3d_shader_compile_info compile_info; enum vkd3d_result ret; const struct vkd3d_shader_compile_option options[] = { {VKD3D_SHADER_COMPILE_OPTION_API_VERSION, VKD3D_SHADER_API_VERSION_1_8}, {VKD3D_SHADER_COMPILE_OPTION_TYPED_UAV, typed_uav_compile_option(device)}, }; compile_info.type = VKD3D_SHADER_STRUCTURE_TYPE_COMPILE_INFO; compile_info.next = descriptor_info; compile_info.source.code = code->pShaderBytecode; compile_info.source.size = code->BytecodeLength; compile_info.target_type = VKD3D_SHADER_TARGET_SPIRV_BINARY; compile_info.options = options; compile_info.option_count = ARRAY_SIZE(options); compile_info.log_level = VKD3D_SHADER_LOG_NONE; compile_info.source_name = NULL; if ((ret = vkd3d_shader_parse_dxbc_source_type(&compile_info.source, &compile_info.source_type, NULL)) < 0) return ret; return vkd3d_shader_scan(&compile_info, NULL); } static HRESULT vkd3d_create_compute_pipeline(struct d3d12_device *device, const D3D12_SHADER_BYTECODE *code, const struct vkd3d_shader_interface_info *shader_interface, VkPipelineLayout vk_pipeline_layout, VkPipeline *vk_pipeline) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VkComputePipelineCreateInfo pipeline_info; VkResult vr; HRESULT hr; pipeline_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; pipeline_info.pNext = NULL; pipeline_info.flags = 0; if (FAILED(hr = create_shader_stage(device, &pipeline_info.stage, VK_SHADER_STAGE_COMPUTE_BIT, code, shader_interface))) return hr; pipeline_info.layout = vk_pipeline_layout; pipeline_info.basePipelineHandle = VK_NULL_HANDLE; pipeline_info.basePipelineIndex = -1; vr = VK_CALL(vkCreateComputePipelines(device->vk_device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, vk_pipeline)); VK_CALL(vkDestroyShaderModule(device->vk_device, pipeline_info.stage.module, NULL)); if (vr < 0) { WARN("Failed to create Vulkan compute pipeline, hr %#x.\n", hr); return hresult_from_vk_result(vr); } return S_OK; } static HRESULT d3d12_pipeline_state_init_uav_counters(struct d3d12_pipeline_state *state, struct d3d12_device *device, const struct d3d12_root_signature *root_signature, const struct vkd3d_shader_scan_descriptor_info *shader_info, VkShaderStageFlags stage_flags) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VkDescriptorSetLayout set_layouts[VKD3D_MAX_DESCRIPTOR_SETS + 1]; VkDescriptorSetLayoutBinding *binding_desc; uint32_t set_index, descriptor_binding; unsigned int uav_counter_count = 0; unsigned int i, j; HRESULT hr; assert(vkd3d_popcount(stage_flags) == 1); for (i = 0; i < shader_info->descriptor_count; ++i) { const struct vkd3d_shader_descriptor_info *d = &shader_info->descriptors[i]; if (d->type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV && (d->flags & VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_UAV_COUNTER)) ++uav_counter_count; } if (!uav_counter_count) return S_OK; /* It should be possible to support other stages in Vulkan, but in a graphics pipeline * D3D12 currently only supports counters in pixel shaders, and handling multiple stages * would be more complex. */ if (!(stage_flags & (VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_COMPUTE_BIT))) { FIXME("Found a UAV counter for Vulkan shader stage %#x. UAV counters in a " "graphics pipeline are only supported in pixel shaders.\n", stage_flags); return E_INVALIDARG; } if (!(binding_desc = vkd3d_calloc(uav_counter_count, sizeof(*binding_desc)))) return E_OUTOFMEMORY; if (!(state->uav_counters.bindings = vkd3d_calloc(uav_counter_count, sizeof(*state->uav_counters.bindings)))) { vkd3d_free(binding_desc); return E_OUTOFMEMORY; } state->uav_counters.binding_count = uav_counter_count; descriptor_binding = 0; set_index = d3d12_root_signature_copy_descriptor_set_layouts(root_signature, set_layouts); for (i = 0, j = 0; i < shader_info->descriptor_count; ++i) { const struct vkd3d_shader_descriptor_info *d = &shader_info->descriptors[i]; if (d->type != VKD3D_SHADER_DESCRIPTOR_TYPE_UAV || !(d->flags & VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_UAV_COUNTER)) continue; state->uav_counters.bindings[j].register_space = d->register_space; state->uav_counters.bindings[j].register_index = d->register_index; state->uav_counters.bindings[j].shader_visibility = (stage_flags == VK_SHADER_STAGE_COMPUTE_BIT) ? VKD3D_SHADER_VISIBILITY_COMPUTE : VKD3D_SHADER_VISIBILITY_PIXEL; state->uav_counters.bindings[j].binding.set = set_index; state->uav_counters.bindings[j].binding.binding = descriptor_binding; state->uav_counters.bindings[j].binding.count = 1; binding_desc[j].binding = descriptor_binding; binding_desc[j].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; binding_desc[j].descriptorCount = 1; binding_desc[j].stageFlags = stage_flags; binding_desc[j].pImmutableSamplers = NULL; ++descriptor_binding; ++j; } /* Create a descriptor set layout for UAV counters. */ hr = vkd3d_create_descriptor_set_layout(device, 0, descriptor_binding, false, binding_desc, &state->uav_counters.vk_set_layout); vkd3d_free(binding_desc); if (FAILED(hr)) { vkd3d_free(state->uav_counters.bindings); return hr; } /* Create a pipeline layout which is compatible for all other descriptor * sets with the root signature's pipeline layout. */ state->uav_counters.set_index = set_index; set_layouts[set_index++] = state->uav_counters.vk_set_layout; if (FAILED(hr = vkd3d_create_pipeline_layout(device, set_index, set_layouts, root_signature->push_constant_range_count, root_signature->push_constant_ranges, &state->uav_counters.vk_pipeline_layout))) { VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, state->uav_counters.vk_set_layout, NULL)); vkd3d_free(state->uav_counters.bindings); return hr; } return S_OK; } static HRESULT d3d12_pipeline_state_find_and_init_uav_counters(struct d3d12_pipeline_state *state, struct d3d12_device *device, const struct d3d12_root_signature *root_signature, const D3D12_SHADER_BYTECODE *code, VkShaderStageFlags stage_flags) { struct vkd3d_shader_scan_descriptor_info shader_info; HRESULT hr; int ret; if (device->use_vk_heaps) return S_OK; shader_info.type = VKD3D_SHADER_STRUCTURE_TYPE_SCAN_DESCRIPTOR_INFO; shader_info.next = NULL; if ((ret = vkd3d_scan_dxbc(device, code, &shader_info)) < 0) { WARN("Failed to scan shader bytecode, stage %#x, vkd3d result %d.\n", stage_flags, ret); return hresult_from_vkd3d_result(ret); } if (FAILED(hr = d3d12_pipeline_state_init_uav_counters(state, device, root_signature, &shader_info, stage_flags))) WARN("Failed to create descriptor set layout for UAV counters, hr %#x.\n", hr); vkd3d_shader_free_scan_descriptor_info(&shader_info); return hr; } static HRESULT d3d12_pipeline_state_init_compute(struct d3d12_pipeline_state *state, struct d3d12_device *device, const D3D12_COMPUTE_PIPELINE_STATE_DESC *desc) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; struct vkd3d_shader_interface_info shader_interface; struct vkd3d_shader_descriptor_offset_info offset_info; const struct d3d12_root_signature *root_signature; struct vkd3d_shader_spirv_target_info target_info; VkPipelineLayout vk_pipeline_layout; HRESULT hr; state->ID3D12PipelineState_iface.lpVtbl = &d3d12_pipeline_state_vtbl; state->refcount = 1; memset(&state->uav_counters, 0, sizeof(state->uav_counters)); if (!(root_signature = unsafe_impl_from_ID3D12RootSignature(desc->pRootSignature))) { WARN("Root signature is NULL.\n"); return E_INVALIDARG; } if (FAILED(hr = d3d12_pipeline_state_find_and_init_uav_counters(state, device, root_signature, &desc->CS, VK_SHADER_STAGE_COMPUTE_BIT))) return hr; memset(&target_info, 0, sizeof(target_info)); target_info.type = VKD3D_SHADER_STRUCTURE_TYPE_SPIRV_TARGET_INFO; target_info.environment = VKD3D_SHADER_SPIRV_ENVIRONMENT_VULKAN_1_0; target_info.extensions = device->vk_info.shader_extensions; target_info.extension_count = device->vk_info.shader_extension_count; if (root_signature->descriptor_offsets) { offset_info.type = VKD3D_SHADER_STRUCTURE_TYPE_DESCRIPTOR_OFFSET_INFO; offset_info.next = NULL; offset_info.descriptor_table_offset = root_signature->descriptor_table_offset; offset_info.descriptor_table_count = root_signature->descriptor_table_count; offset_info.binding_offsets = root_signature->descriptor_offsets; offset_info.uav_counter_offsets = root_signature->uav_counter_offsets; vkd3d_prepend_struct(&target_info, &offset_info); } shader_interface.type = VKD3D_SHADER_STRUCTURE_TYPE_INTERFACE_INFO; shader_interface.next = &target_info; shader_interface.bindings = root_signature->descriptor_mapping; shader_interface.binding_count = root_signature->binding_count; shader_interface.push_constant_buffers = root_signature->root_constants; shader_interface.push_constant_buffer_count = root_signature->root_constant_count; shader_interface.combined_samplers = NULL; shader_interface.combined_sampler_count = 0; if (root_signature->uav_counter_mapping) { shader_interface.uav_counters = root_signature->uav_counter_mapping; shader_interface.uav_counter_count = root_signature->uav_mapping_count; } else { shader_interface.uav_counters = state->uav_counters.bindings; shader_interface.uav_counter_count = state->uav_counters.binding_count; } vk_pipeline_layout = state->uav_counters.vk_pipeline_layout ? state->uav_counters.vk_pipeline_layout : root_signature->vk_pipeline_layout; if (FAILED(hr = vkd3d_create_compute_pipeline(device, &desc->CS, &shader_interface, vk_pipeline_layout, &state->u.compute.vk_pipeline))) { WARN("Failed to create Vulkan compute pipeline, hr %#x.\n", hr); d3d12_pipeline_uav_counter_state_cleanup(&state->uav_counters, device); return hr; } if (FAILED(hr = vkd3d_private_store_init(&state->private_store))) { VK_CALL(vkDestroyPipeline(device->vk_device, state->u.compute.vk_pipeline, NULL)); d3d12_pipeline_uav_counter_state_cleanup(&state->uav_counters, device); return hr; } state->vk_bind_point = VK_PIPELINE_BIND_POINT_COMPUTE; d3d12_device_add_ref(state->device = device); return S_OK; } HRESULT d3d12_pipeline_state_create_compute(struct d3d12_device *device, const D3D12_COMPUTE_PIPELINE_STATE_DESC *desc, struct d3d12_pipeline_state **state) { struct d3d12_pipeline_state *object; HRESULT hr; if (!(object = vkd3d_malloc(sizeof(*object)))) return E_OUTOFMEMORY; if (FAILED(hr = d3d12_pipeline_state_init_compute(object, device, desc))) { vkd3d_free(object); return hr; } TRACE("Created compute pipeline state %p.\n", object); *state = object; return S_OK; } static enum VkPolygonMode vk_polygon_mode_from_d3d12(D3D12_FILL_MODE mode) { switch (mode) { case D3D12_FILL_MODE_WIREFRAME: return VK_POLYGON_MODE_LINE; case D3D12_FILL_MODE_SOLID: return VK_POLYGON_MODE_FILL; default: FIXME("Unhandled fill mode %#x.\n", mode); return VK_POLYGON_MODE_FILL; } } static enum VkCullModeFlagBits vk_cull_mode_from_d3d12(D3D12_CULL_MODE mode) { switch (mode) { case D3D12_CULL_MODE_NONE: return VK_CULL_MODE_NONE; case D3D12_CULL_MODE_FRONT: return VK_CULL_MODE_FRONT_BIT; case D3D12_CULL_MODE_BACK: return VK_CULL_MODE_BACK_BIT; default: FIXME("Unhandled cull mode %#x.\n", mode); return VK_CULL_MODE_NONE; } } static void rs_desc_from_d3d12(VkPipelineRasterizationStateCreateInfo *vk_desc, const D3D12_RASTERIZER_DESC *d3d12_desc) { vk_desc->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; vk_desc->pNext = NULL; vk_desc->flags = 0; vk_desc->depthClampEnable = !d3d12_desc->DepthClipEnable; vk_desc->rasterizerDiscardEnable = VK_FALSE; vk_desc->polygonMode = vk_polygon_mode_from_d3d12(d3d12_desc->FillMode); vk_desc->cullMode = vk_cull_mode_from_d3d12(d3d12_desc->CullMode); vk_desc->frontFace = d3d12_desc->FrontCounterClockwise ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE; vk_desc->depthBiasEnable = d3d12_desc->DepthBias || d3d12_desc->SlopeScaledDepthBias; vk_desc->depthBiasConstantFactor = d3d12_desc->DepthBias; vk_desc->depthBiasClamp = d3d12_desc->DepthBiasClamp; vk_desc->depthBiasSlopeFactor = d3d12_desc->SlopeScaledDepthBias; vk_desc->lineWidth = 1.0f; if (d3d12_desc->MultisampleEnable) FIXME_ONCE("Ignoring MultisampleEnable %#x.\n", d3d12_desc->MultisampleEnable); if (d3d12_desc->AntialiasedLineEnable) FIXME_ONCE("Ignoring AntialiasedLineEnable %#x.\n", d3d12_desc->AntialiasedLineEnable); if (d3d12_desc->ForcedSampleCount) FIXME("Ignoring ForcedSampleCount %#x.\n", d3d12_desc->ForcedSampleCount); if (d3d12_desc->ConservativeRaster) FIXME("Ignoring ConservativeRaster %#x.\n", d3d12_desc->ConservativeRaster); } static void rs_depth_clip_info_from_d3d12(VkPipelineRasterizationDepthClipStateCreateInfoEXT *depth_clip_info, VkPipelineRasterizationStateCreateInfo *vk_rs_desc, const D3D12_RASTERIZER_DESC *d3d12_desc) { vk_rs_desc->depthClampEnable = VK_TRUE; depth_clip_info->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT; depth_clip_info->pNext = NULL; depth_clip_info->flags = 0; depth_clip_info->depthClipEnable = d3d12_desc->DepthClipEnable; vk_prepend_struct(vk_rs_desc, depth_clip_info); } static void rs_stream_info_from_d3d12(VkPipelineRasterizationStateStreamCreateInfoEXT *stream_info, VkPipelineRasterizationStateCreateInfo *vk_rs_desc, const D3D12_STREAM_OUTPUT_DESC *so_desc, const struct vkd3d_vulkan_info *vk_info) { if (!so_desc->NumEntries || !so_desc->RasterizedStream || so_desc->RasterizedStream == D3D12_SO_NO_RASTERIZED_STREAM) return; if (!vk_info->rasterization_stream) { FIXME("Rasterization stream select is not supported by Vulkan implementation.\n"); return; } stream_info->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT; stream_info->pNext = NULL; stream_info->flags = 0; stream_info->rasterizationStream = so_desc->RasterizedStream; vk_prepend_struct(vk_rs_desc, stream_info); } static enum VkStencilOp vk_stencil_op_from_d3d12(D3D12_STENCIL_OP op) { switch (op) { case D3D12_STENCIL_OP_KEEP: return VK_STENCIL_OP_KEEP; case D3D12_STENCIL_OP_ZERO: return VK_STENCIL_OP_ZERO; case D3D12_STENCIL_OP_REPLACE: return VK_STENCIL_OP_REPLACE; case D3D12_STENCIL_OP_INCR_SAT: return VK_STENCIL_OP_INCREMENT_AND_CLAMP; case D3D12_STENCIL_OP_DECR_SAT: return VK_STENCIL_OP_DECREMENT_AND_CLAMP; case D3D12_STENCIL_OP_INVERT: return VK_STENCIL_OP_INVERT; case D3D12_STENCIL_OP_INCR: return VK_STENCIL_OP_INCREMENT_AND_WRAP; case D3D12_STENCIL_OP_DECR: return VK_STENCIL_OP_DECREMENT_AND_WRAP; default: FIXME("Unhandled stencil op %#x.\n", op); return VK_STENCIL_OP_KEEP; } } 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: FIXME("Unhandled compare op %#x.\n", op); return VK_COMPARE_OP_NEVER; } } static void vk_stencil_op_state_from_d3d12(struct VkStencilOpState *vk_desc, const D3D12_DEPTH_STENCILOP_DESC *d3d12_desc, uint32_t compare_mask, uint32_t write_mask) { vk_desc->failOp = vk_stencil_op_from_d3d12(d3d12_desc->StencilFailOp); vk_desc->passOp = vk_stencil_op_from_d3d12(d3d12_desc->StencilPassOp); vk_desc->depthFailOp = vk_stencil_op_from_d3d12(d3d12_desc->StencilDepthFailOp); vk_desc->compareOp = vk_compare_op_from_d3d12(d3d12_desc->StencilFunc); vk_desc->compareMask = compare_mask; vk_desc->writeMask = write_mask; /* The stencil reference value is a dynamic state. Set by OMSetStencilRef(). */ vk_desc->reference = 0; } static void ds_desc_from_d3d12(struct VkPipelineDepthStencilStateCreateInfo *vk_desc, const D3D12_DEPTH_STENCIL_DESC *d3d12_desc) { memset(vk_desc, 0, sizeof(*vk_desc)); vk_desc->sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; vk_desc->pNext = NULL; vk_desc->flags = 0; if ((vk_desc->depthTestEnable = d3d12_desc->DepthEnable)) { vk_desc->depthWriteEnable = d3d12_desc->DepthWriteMask & D3D12_DEPTH_WRITE_MASK_ALL; vk_desc->depthCompareOp = vk_compare_op_from_d3d12(d3d12_desc->DepthFunc); } else { vk_desc->depthWriteEnable = VK_FALSE; vk_desc->depthCompareOp = VK_COMPARE_OP_NEVER; } vk_desc->depthBoundsTestEnable = VK_FALSE; if ((vk_desc->stencilTestEnable = d3d12_desc->StencilEnable)) { vk_stencil_op_state_from_d3d12(&vk_desc->front, &d3d12_desc->FrontFace, d3d12_desc->StencilReadMask, d3d12_desc->StencilWriteMask); vk_stencil_op_state_from_d3d12(&vk_desc->back, &d3d12_desc->BackFace, d3d12_desc->StencilReadMask, d3d12_desc->StencilWriteMask); } else { memset(&vk_desc->front, 0, sizeof(vk_desc->front)); memset(&vk_desc->back, 0, sizeof(vk_desc->back)); } vk_desc->minDepthBounds = 0.0f; vk_desc->maxDepthBounds = 1.0f; } static enum VkBlendFactor vk_blend_factor_from_d3d12(D3D12_BLEND blend, bool alpha) { switch (blend) { case D3D12_BLEND_ZERO: return VK_BLEND_FACTOR_ZERO; case D3D12_BLEND_ONE: return VK_BLEND_FACTOR_ONE; case D3D12_BLEND_SRC_COLOR: return VK_BLEND_FACTOR_SRC_COLOR; case D3D12_BLEND_INV_SRC_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; case D3D12_BLEND_SRC_ALPHA: return VK_BLEND_FACTOR_SRC_ALPHA; case D3D12_BLEND_INV_SRC_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; case D3D12_BLEND_DEST_ALPHA: return VK_BLEND_FACTOR_DST_ALPHA; case D3D12_BLEND_INV_DEST_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; case D3D12_BLEND_DEST_COLOR: return VK_BLEND_FACTOR_DST_COLOR; case D3D12_BLEND_INV_DEST_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; case D3D12_BLEND_SRC_ALPHA_SAT: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; case D3D12_BLEND_BLEND_FACTOR: if (alpha) return VK_BLEND_FACTOR_CONSTANT_ALPHA; return VK_BLEND_FACTOR_CONSTANT_COLOR; case D3D12_BLEND_INV_BLEND_FACTOR: if (alpha) return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA; return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; case D3D12_BLEND_SRC1_COLOR: return VK_BLEND_FACTOR_SRC1_COLOR; case D3D12_BLEND_INV_SRC1_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR; case D3D12_BLEND_SRC1_ALPHA: return VK_BLEND_FACTOR_SRC1_ALPHA; case D3D12_BLEND_INV_SRC1_ALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA; default: FIXME("Unhandled blend %#x.\n", blend); return VK_BLEND_FACTOR_ZERO; } } static enum VkBlendOp vk_blend_op_from_d3d12(D3D12_BLEND_OP op) { switch (op) { case D3D12_BLEND_OP_ADD: return VK_BLEND_OP_ADD; case D3D12_BLEND_OP_SUBTRACT: return VK_BLEND_OP_SUBTRACT; case D3D12_BLEND_OP_REV_SUBTRACT: return VK_BLEND_OP_REVERSE_SUBTRACT; case D3D12_BLEND_OP_MIN: return VK_BLEND_OP_MIN; case D3D12_BLEND_OP_MAX: return VK_BLEND_OP_MAX; default: FIXME("Unhandled blend op %#x.\n", op); return VK_BLEND_OP_ADD; } } static void blend_attachment_from_d3d12(struct VkPipelineColorBlendAttachmentState *vk_desc, const D3D12_RENDER_TARGET_BLEND_DESC *d3d12_desc) { if (d3d12_desc->BlendEnable) { vk_desc->blendEnable = VK_TRUE; vk_desc->srcColorBlendFactor = vk_blend_factor_from_d3d12(d3d12_desc->SrcBlend, false); vk_desc->dstColorBlendFactor = vk_blend_factor_from_d3d12(d3d12_desc->DestBlend, false); vk_desc->colorBlendOp = vk_blend_op_from_d3d12(d3d12_desc->BlendOp); vk_desc->srcAlphaBlendFactor = vk_blend_factor_from_d3d12(d3d12_desc->SrcBlendAlpha, true); vk_desc->dstAlphaBlendFactor = vk_blend_factor_from_d3d12(d3d12_desc->DestBlendAlpha, true); vk_desc->alphaBlendOp = vk_blend_op_from_d3d12(d3d12_desc->BlendOpAlpha); } else { memset(vk_desc, 0, sizeof(*vk_desc)); } vk_desc->colorWriteMask = 0; if (d3d12_desc->RenderTargetWriteMask & D3D12_COLOR_WRITE_ENABLE_RED) vk_desc->colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; if (d3d12_desc->RenderTargetWriteMask & D3D12_COLOR_WRITE_ENABLE_GREEN) vk_desc->colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; if (d3d12_desc->RenderTargetWriteMask & D3D12_COLOR_WRITE_ENABLE_BLUE) vk_desc->colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; if (d3d12_desc->RenderTargetWriteMask & D3D12_COLOR_WRITE_ENABLE_ALPHA) vk_desc->colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; } static bool is_dual_source_blending_blend(D3D12_BLEND b) { return b == D3D12_BLEND_SRC1_COLOR || b == D3D12_BLEND_INV_SRC1_COLOR || b == D3D12_BLEND_SRC1_ALPHA || b == D3D12_BLEND_INV_SRC1_ALPHA; } static bool is_dual_source_blending(const D3D12_RENDER_TARGET_BLEND_DESC *desc) { return desc->BlendEnable && (is_dual_source_blending_blend(desc->SrcBlend) || is_dual_source_blending_blend(desc->DestBlend) || is_dual_source_blending_blend(desc->SrcBlendAlpha) || is_dual_source_blending_blend(desc->DestBlendAlpha)); } static HRESULT compute_input_layout_offsets(const struct d3d12_device *device, const D3D12_INPUT_LAYOUT_DESC *input_layout_desc, uint32_t *offsets) { uint32_t input_slot_offsets[D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT] = {0}; const D3D12_INPUT_ELEMENT_DESC *e; const struct vkd3d_format *format; unsigned int i; if (input_layout_desc->NumElements > D3D12_VS_INPUT_REGISTER_COUNT) { FIXME("InputLayout.NumElements %u > %u, ignoring extra elements.\n", input_layout_desc->NumElements, D3D12_VS_INPUT_REGISTER_COUNT); } for (i = 0; i < min(input_layout_desc->NumElements, D3D12_VS_INPUT_REGISTER_COUNT); ++i) { e = &input_layout_desc->pInputElementDescs[i]; if (e->InputSlot >= ARRAY_SIZE(input_slot_offsets)) { WARN("Invalid input slot %#x.\n", e->InputSlot); return E_INVALIDARG; } /* TODO: DXGI_FORMAT_UNKNOWN will return a format with byte_count == 1, * which may not match driver behaviour (return E_INVALIDARG?). */ if (!(format = vkd3d_get_format(device, e->Format, false))) { WARN("Invalid input element format %#x.\n", e->Format); return E_INVALIDARG; } if (e->AlignedByteOffset != D3D12_APPEND_ALIGNED_ELEMENT) offsets[i] = e->AlignedByteOffset; else offsets[i] = align(input_slot_offsets[e->InputSlot], min(4, format->byte_count)); input_slot_offsets[e->InputSlot] = offsets[i] + format->byte_count; } return S_OK; } static unsigned int vkd3d_get_rt_format_swizzle(const struct vkd3d_format *format) { if (format->dxgi_format == DXGI_FORMAT_A8_UNORM) return VKD3D_SHADER_SWIZZLE(W, X, Y, Z); return VKD3D_SHADER_NO_SWIZZLE; } STATIC_ASSERT(sizeof(struct vkd3d_shader_transform_feedback_element) == sizeof(D3D12_SO_DECLARATION_ENTRY)); static HRESULT d3d12_graphics_pipeline_state_create_render_pass( struct d3d12_graphics_pipeline_state *graphics, struct d3d12_device *device, VkFormat dynamic_dsv_format, VkRenderPass *vk_render_pass) { struct vkd3d_render_pass_key key; VkFormat dsv_format; unsigned int i; memcpy(key.vk_formats, graphics->rtv_formats, sizeof(graphics->rtv_formats)); key.attachment_count = graphics->rt_count; if (!(dsv_format = graphics->dsv_format) && (graphics->null_attachment_mask & dsv_attachment_mask(graphics))) dsv_format = dynamic_dsv_format; if (dsv_format) { assert(graphics->ds_desc.front.writeMask == graphics->ds_desc.back.writeMask); key.depth_enable = graphics->ds_desc.depthTestEnable; key.stencil_enable = graphics->ds_desc.stencilTestEnable; key.depth_stencil_write = graphics->ds_desc.depthWriteEnable || graphics->ds_desc.front.writeMask; key.vk_formats[key.attachment_count++] = dsv_format; } else { key.depth_enable = false; key.stencil_enable = false; key.depth_stencil_write = false; } if (key.attachment_count != ARRAY_SIZE(key.vk_formats)) key.vk_formats[ARRAY_SIZE(key.vk_formats) - 1] = VK_FORMAT_UNDEFINED; for (i = key.attachment_count; i < ARRAY_SIZE(key.vk_formats); ++i) assert(key.vk_formats[i] == VK_FORMAT_UNDEFINED); key.padding = 0; key.sample_count = graphics->ms_desc.rasterizationSamples; return vkd3d_render_pass_cache_find(&device->render_pass_cache, device, &key, vk_render_pass); } static VkLogicOp vk_logic_op_from_d3d12(D3D12_LOGIC_OP op) { switch (op) { case D3D12_LOGIC_OP_CLEAR: return VK_LOGIC_OP_CLEAR; case D3D12_LOGIC_OP_SET: return VK_LOGIC_OP_SET; case D3D12_LOGIC_OP_COPY: return VK_LOGIC_OP_COPY; case D3D12_LOGIC_OP_COPY_INVERTED: return VK_LOGIC_OP_COPY_INVERTED; case D3D12_LOGIC_OP_NOOP: return VK_LOGIC_OP_NO_OP; case D3D12_LOGIC_OP_INVERT: return VK_LOGIC_OP_INVERT; case D3D12_LOGIC_OP_AND: return VK_LOGIC_OP_AND; case D3D12_LOGIC_OP_NAND: return VK_LOGIC_OP_NAND; case D3D12_LOGIC_OP_OR: return VK_LOGIC_OP_OR; case D3D12_LOGIC_OP_NOR: return VK_LOGIC_OP_NOR; case D3D12_LOGIC_OP_XOR: return VK_LOGIC_OP_XOR; case D3D12_LOGIC_OP_EQUIV: return VK_LOGIC_OP_EQUIVALENT; case D3D12_LOGIC_OP_AND_REVERSE: return VK_LOGIC_OP_AND_REVERSE; case D3D12_LOGIC_OP_AND_INVERTED: return VK_LOGIC_OP_AND_INVERTED; case D3D12_LOGIC_OP_OR_REVERSE: return VK_LOGIC_OP_OR_REVERSE; case D3D12_LOGIC_OP_OR_INVERTED: return VK_LOGIC_OP_OR_INVERTED; default: FIXME("Unhandled logic op %#x.\n", op); return VK_LOGIC_OP_NO_OP; } } static HRESULT d3d12_pipeline_state_init_graphics(struct d3d12_pipeline_state *state, struct d3d12_device *device, const D3D12_GRAPHICS_PIPELINE_STATE_DESC *desc) { unsigned int ps_output_swizzle[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT]; struct d3d12_graphics_pipeline_state *graphics = &state->u.graphics; const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; const D3D12_STREAM_OUTPUT_DESC *so_desc = &desc->StreamOutput; VkVertexInputBindingDivisorDescriptionEXT *binding_divisor; const struct vkd3d_vulkan_info *vk_info = &device->vk_info; uint32_t instance_divisors[D3D12_VS_INPUT_REGISTER_COUNT]; struct vkd3d_shader_spirv_target_info *stage_target_info; uint32_t aligned_offsets[D3D12_VS_INPUT_REGISTER_COUNT]; struct vkd3d_shader_descriptor_offset_info offset_info; struct vkd3d_shader_parameter ps_shader_parameters[1]; struct vkd3d_shader_transform_feedback_info xfb_info; struct vkd3d_shader_spirv_target_info ps_target_info; struct vkd3d_shader_interface_info shader_interface; struct vkd3d_shader_spirv_target_info target_info; const struct d3d12_root_signature *root_signature; struct vkd3d_shader_signature input_signature; bool have_attachment, is_dsv_format_unknown; VkShaderStageFlagBits xfb_stage = 0; VkSampleCountFlagBits sample_count; const struct vkd3d_format *format; unsigned int instance_divisor; VkVertexInputRate input_rate; unsigned int i, j; size_t rt_count; uint32_t mask; HRESULT hr; int ret; static const DWORD default_ps_code[] = { #if 0 ps_4_0 ret #endif 0x43425844, 0x19cbf606, 0x18f562b9, 0xdaeed4db, 0xc324aa46, 0x00000001, 0x00000060, 0x00000003, 0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f, 0x00000008, 0x00000000, 0x00000008, 0x52444853, 0x0000000c, 0x00000040, 0x00000003, 0x0100003e, }; static const D3D12_SHADER_BYTECODE default_ps = {default_ps_code, sizeof(default_ps_code)}; static const struct { enum VkShaderStageFlagBits stage; ptrdiff_t offset; } shader_stages[] = { {VK_SHADER_STAGE_VERTEX_BIT, offsetof(D3D12_GRAPHICS_PIPELINE_STATE_DESC, VS)}, {VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, offsetof(D3D12_GRAPHICS_PIPELINE_STATE_DESC, HS)}, {VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, offsetof(D3D12_GRAPHICS_PIPELINE_STATE_DESC, DS)}, {VK_SHADER_STAGE_GEOMETRY_BIT, offsetof(D3D12_GRAPHICS_PIPELINE_STATE_DESC, GS)}, {VK_SHADER_STAGE_FRAGMENT_BIT, offsetof(D3D12_GRAPHICS_PIPELINE_STATE_DESC, PS)}, }; state->ID3D12PipelineState_iface.lpVtbl = &d3d12_pipeline_state_vtbl; state->refcount = 1; memset(&state->uav_counters, 0, sizeof(state->uav_counters)); graphics->stage_count = 0; memset(&input_signature, 0, sizeof(input_signature)); for (i = desc->NumRenderTargets; i < ARRAY_SIZE(desc->RTVFormats); ++i) { if (desc->RTVFormats[i] != DXGI_FORMAT_UNKNOWN) { WARN("Format must be set to DXGI_FORMAT_UNKNOWN for inactive render targets.\n"); return E_INVALIDARG; } } if (!(root_signature = unsafe_impl_from_ID3D12RootSignature(desc->pRootSignature))) { WARN("Root signature is NULL.\n"); return E_INVALIDARG; } sample_count = vk_samples_from_dxgi_sample_desc(&desc->SampleDesc); if (desc->SampleDesc.Count != 1 && desc->SampleDesc.Quality) WARN("Ignoring sample quality %u.\n", desc->SampleDesc.Quality); rt_count = desc->NumRenderTargets; if (rt_count > ARRAY_SIZE(graphics->blend_attachments)) { FIXME("NumRenderTargets %zu > %zu, ignoring extra formats.\n", rt_count, ARRAY_SIZE(graphics->blend_attachments)); rt_count = ARRAY_SIZE(graphics->blend_attachments); } graphics->om_logic_op_enable = desc->BlendState.RenderTarget[0].LogicOpEnable && device->feature_options.OutputMergerLogicOp; graphics->om_logic_op = graphics->om_logic_op_enable ? vk_logic_op_from_d3d12(desc->BlendState.RenderTarget[0].LogicOp) : VK_LOGIC_OP_COPY; if (desc->BlendState.RenderTarget[0].LogicOpEnable && !graphics->om_logic_op_enable) WARN("The device does not support output merger logic ops. Ignoring logic op %#x.\n", desc->BlendState.RenderTarget[0].LogicOp); graphics->null_attachment_mask = 0; for (i = 0; i < rt_count; ++i) { const D3D12_RENDER_TARGET_BLEND_DESC *rt_desc; if (desc->RTVFormats[i] == DXGI_FORMAT_UNKNOWN) { graphics->null_attachment_mask |= 1u << i; ps_output_swizzle[i] = VKD3D_SHADER_NO_SWIZZLE; graphics->rtv_formats[i] = VK_FORMAT_UNDEFINED; } else if ((format = vkd3d_get_format(device, desc->RTVFormats[i], false))) { ps_output_swizzle[i] = vkd3d_get_rt_format_swizzle(format); graphics->rtv_formats[i] = format->vk_format; } else { WARN("Invalid RTV format %#x.\n", desc->RTVFormats[i]); hr = E_INVALIDARG; goto fail; } rt_desc = &desc->BlendState.RenderTarget[desc->BlendState.IndependentBlendEnable ? i : 0]; if (desc->BlendState.IndependentBlendEnable && rt_desc->LogicOpEnable) { WARN("IndependentBlendEnable must be FALSE when logic operations are enabled.\n"); hr = E_INVALIDARG; goto fail; } if (rt_desc->BlendEnable && rt_desc->LogicOpEnable) { WARN("Only one of BlendEnable or LogicOpEnable can be set to TRUE.\n"); hr = E_INVALIDARG; goto fail; } blend_attachment_from_d3d12(&graphics->blend_attachments[i], rt_desc); } for (i = rt_count; i < ARRAY_SIZE(graphics->rtv_formats); ++i) graphics->rtv_formats[i] = VK_FORMAT_UNDEFINED; graphics->rt_count = rt_count; ds_desc_from_d3d12(&graphics->ds_desc, &desc->DepthStencilState); if (desc->DSVFormat == DXGI_FORMAT_UNKNOWN && graphics->ds_desc.depthTestEnable && !graphics->ds_desc.depthWriteEnable && graphics->ds_desc.depthCompareOp == VK_COMPARE_OP_ALWAYS && !graphics->ds_desc.stencilTestEnable) { TRACE("Disabling depth test.\n"); graphics->ds_desc.depthTestEnable = VK_FALSE; } graphics->dsv_format = VK_FORMAT_UNDEFINED; if (graphics->ds_desc.depthTestEnable || graphics->ds_desc.stencilTestEnable) { if (desc->DSVFormat == DXGI_FORMAT_UNKNOWN) { WARN("DSV format is DXGI_FORMAT_UNKNOWN.\n"); graphics->dsv_format = VK_FORMAT_UNDEFINED; graphics->null_attachment_mask |= dsv_attachment_mask(graphics); } else if ((format = vkd3d_get_format(device, desc->DSVFormat, true))) { if (!(format->vk_aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))) FIXME("Format %#x is not depth/stencil format.\n", format->dxgi_format); graphics->dsv_format = format->vk_format; } else { WARN("Invalid DSV format %#x.\n", desc->DSVFormat); hr = E_INVALIDARG; goto fail; } if (!desc->PS.pShaderBytecode) { if (FAILED(hr = create_shader_stage(device, &graphics->stages[graphics->stage_count], VK_SHADER_STAGE_FRAGMENT_BIT, &default_ps, NULL))) goto fail; ++graphics->stage_count; } } ps_shader_parameters[0].name = VKD3D_SHADER_PARAMETER_NAME_RASTERIZER_SAMPLE_COUNT; ps_shader_parameters[0].type = VKD3D_SHADER_PARAMETER_TYPE_IMMEDIATE_CONSTANT; ps_shader_parameters[0].data_type = VKD3D_SHADER_PARAMETER_DATA_TYPE_UINT32; ps_shader_parameters[0].u.immediate_constant.u.u32 = sample_count; ps_target_info.type = VKD3D_SHADER_STRUCTURE_TYPE_SPIRV_TARGET_INFO; ps_target_info.next = NULL; ps_target_info.entry_point = "main"; ps_target_info.environment = VKD3D_SHADER_SPIRV_ENVIRONMENT_VULKAN_1_0; ps_target_info.extensions = vk_info->shader_extensions; ps_target_info.extension_count = vk_info->shader_extension_count; ps_target_info.parameters = ps_shader_parameters; ps_target_info.parameter_count = ARRAY_SIZE(ps_shader_parameters); ps_target_info.dual_source_blending = is_dual_source_blending(&desc->BlendState.RenderTarget[0]); ps_target_info.output_swizzles = ps_output_swizzle; ps_target_info.output_swizzle_count = rt_count; if (ps_target_info.dual_source_blending && rt_count > 1) { WARN("Only one render target is allowed when dual source blending is used.\n"); hr = E_INVALIDARG; goto fail; } if (ps_target_info.dual_source_blending && desc->BlendState.IndependentBlendEnable) { for (i = 1; i < ARRAY_SIZE(desc->BlendState.RenderTarget); ++i) { if (desc->BlendState.RenderTarget[i].BlendEnable) { WARN("Blend enable cannot be set for render target %u when dual source blending is used.\n", i); hr = E_INVALIDARG; goto fail; } } } memset(&target_info, 0, sizeof(target_info)); target_info.type = VKD3D_SHADER_STRUCTURE_TYPE_SPIRV_TARGET_INFO; target_info.environment = VKD3D_SHADER_SPIRV_ENVIRONMENT_VULKAN_1_0; target_info.extensions = vk_info->shader_extensions; target_info.extension_count = vk_info->shader_extension_count; graphics->xfb_enabled = false; if (so_desc->NumEntries) { if (!(root_signature->flags & D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT)) { WARN("Stream output is used without D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT.\n"); hr = E_INVALIDARG; goto fail; } if (!vk_info->EXT_transform_feedback) { FIXME("Transform feedback is not supported by Vulkan implementation.\n"); hr = E_NOTIMPL; goto fail; } graphics->xfb_enabled = true; xfb_info.type = VKD3D_SHADER_STRUCTURE_TYPE_TRANSFORM_FEEDBACK_INFO; xfb_info.next = NULL; xfb_info.elements = (const struct vkd3d_shader_transform_feedback_element *)so_desc->pSODeclaration; xfb_info.element_count = so_desc->NumEntries; xfb_info.buffer_strides = so_desc->pBufferStrides; xfb_info.buffer_stride_count = so_desc->NumStrides; if (desc->GS.pShaderBytecode) xfb_stage = VK_SHADER_STAGE_GEOMETRY_BIT; else if (desc->DS.pShaderBytecode) xfb_stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; else xfb_stage = VK_SHADER_STAGE_VERTEX_BIT; } shader_interface.type = VKD3D_SHADER_STRUCTURE_TYPE_INTERFACE_INFO; shader_interface.next = NULL; shader_interface.bindings = root_signature->descriptor_mapping; shader_interface.binding_count = root_signature->binding_count; shader_interface.push_constant_buffers = root_signature->root_constants; shader_interface.push_constant_buffer_count = root_signature->root_constant_count; shader_interface.combined_samplers = NULL; shader_interface.combined_sampler_count = 0; if (root_signature->descriptor_offsets) { offset_info.type = VKD3D_SHADER_STRUCTURE_TYPE_DESCRIPTOR_OFFSET_INFO; offset_info.next = NULL; offset_info.descriptor_table_offset = root_signature->descriptor_table_offset; offset_info.descriptor_table_count = root_signature->descriptor_table_count; offset_info.binding_offsets = root_signature->descriptor_offsets; offset_info.uav_counter_offsets = root_signature->uav_counter_offsets; } for (i = 0; i < ARRAY_SIZE(shader_stages); ++i) { const D3D12_SHADER_BYTECODE *b = (const void *)((uintptr_t)desc + shader_stages[i].offset); const struct vkd3d_shader_code dxbc = {b->pShaderBytecode, b->BytecodeLength}; if (!b->pShaderBytecode) continue; if (FAILED(hr = d3d12_pipeline_state_find_and_init_uav_counters(state, device, root_signature, b, shader_stages[i].stage))) goto fail; shader_interface.uav_counters = NULL; shader_interface.uav_counter_count = 0; stage_target_info = &target_info; switch (shader_stages[i].stage) { case VK_SHADER_STAGE_VERTEX_BIT: if ((ret = vkd3d_shader_parse_input_signature(&dxbc, &input_signature, NULL)) < 0) { hr = hresult_from_vkd3d_result(ret); goto fail; } break; case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: if (desc->PrimitiveTopologyType != D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH) { WARN("D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH must be used with tessellation shaders.\n"); hr = E_INVALIDARG; goto fail; } break; case VK_SHADER_STAGE_GEOMETRY_BIT: break; case VK_SHADER_STAGE_FRAGMENT_BIT: shader_interface.uav_counters = root_signature->uav_counter_mapping ? root_signature->uav_counter_mapping : state->uav_counters.bindings; shader_interface.uav_counter_count = root_signature->uav_counter_mapping ? root_signature->uav_mapping_count : state->uav_counters.binding_count; stage_target_info = &ps_target_info; break; default: hr = E_INVALIDARG; goto fail; } shader_interface.next = NULL; xfb_info.next = NULL; ps_target_info.next = NULL; target_info.next = NULL; offset_info.next = NULL; if (shader_stages[i].stage == xfb_stage) vkd3d_prepend_struct(&shader_interface, &xfb_info); vkd3d_prepend_struct(&shader_interface, stage_target_info); if (root_signature->descriptor_offsets) vkd3d_prepend_struct(&shader_interface, &offset_info); if (FAILED(hr = create_shader_stage(device, &graphics->stages[graphics->stage_count], shader_stages[i].stage, b, &shader_interface))) goto fail; ++graphics->stage_count; } graphics->attribute_count = desc->InputLayout.NumElements; if (graphics->attribute_count > ARRAY_SIZE(graphics->attributes)) { FIXME("InputLayout.NumElements %zu > %zu, ignoring extra elements.\n", graphics->attribute_count, ARRAY_SIZE(graphics->attributes)); graphics->attribute_count = ARRAY_SIZE(graphics->attributes); } if (graphics->attribute_count && !(root_signature->flags & D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)) { WARN("Input layout is used without D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT.\n"); hr = E_INVALIDARG; goto fail; } if (FAILED(hr = compute_input_layout_offsets(device, &desc->InputLayout, aligned_offsets))) goto fail; graphics->instance_divisor_count = 0; for (i = 0, j = 0, mask = 0; i < graphics->attribute_count; ++i) { const D3D12_INPUT_ELEMENT_DESC *e = &desc->InputLayout.pInputElementDescs[i]; const struct vkd3d_shader_signature_element *signature_element; /* TODO: DXGI_FORMAT_UNKNOWN will succeed here, which may not match * driver behaviour (return E_INVALIDARG?). */ if (!(format = vkd3d_get_format(device, e->Format, false))) { WARN("Invalid input element format %#x.\n", e->Format); hr = E_INVALIDARG; goto fail; } if (e->InputSlot >= ARRAY_SIZE(graphics->input_rates) || e->InputSlot >= ARRAY_SIZE(instance_divisors)) { WARN("Invalid input slot %#x.\n", e->InputSlot); hr = E_INVALIDARG; goto fail; } if (!(signature_element = vkd3d_shader_find_signature_element(&input_signature, e->SemanticName, e->SemanticIndex, 0))) { WARN("Unused input element %u.\n", i); continue; } graphics->attributes[j].location = signature_element->register_index; graphics->attributes[j].binding = e->InputSlot; graphics->attributes[j].format = format->vk_format; if (e->AlignedByteOffset != D3D12_APPEND_ALIGNED_ELEMENT) graphics->attributes[j].offset = e->AlignedByteOffset; else graphics->attributes[j].offset = aligned_offsets[i]; ++j; switch (e->InputSlotClass) { case D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA: input_rate = VK_VERTEX_INPUT_RATE_VERTEX; instance_divisor = 1; break; case D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA: input_rate = VK_VERTEX_INPUT_RATE_INSTANCE; instance_divisor = e->InstanceDataStepRate; if (instance_divisor > vk_info->max_vertex_attrib_divisor || (!instance_divisor && !vk_info->vertex_attrib_zero_divisor)) { FIXME("Instance divisor %u not supported by Vulkan implementation.\n", instance_divisor); instance_divisor = 1; } break; default: FIXME("Unhandled input slot class %#x on input element %u.\n", e->InputSlotClass, i); hr = E_INVALIDARG; goto fail; } if (mask & (1u << e->InputSlot) && (graphics->input_rates[e->InputSlot] != input_rate || instance_divisors[e->InputSlot] != instance_divisor)) { FIXME("Input slot rate %#x, instance divisor %u on input element %u conflicts " "with earlier input slot rate %#x, instance divisor %u.\n", input_rate, instance_divisor, e->InputSlot, graphics->input_rates[e->InputSlot], instance_divisors[e->InputSlot]); hr = E_INVALIDARG; goto fail; } graphics->input_rates[e->InputSlot] = input_rate; instance_divisors[e->InputSlot] = instance_divisor; if (instance_divisor != 1 && !(mask & (1u << e->InputSlot))) { binding_divisor = &graphics->instance_divisors[graphics->instance_divisor_count++]; binding_divisor->binding = e->InputSlot; binding_divisor->divisor = instance_divisor; } mask |= 1u << e->InputSlot; } graphics->attribute_count = j; vkd3d_shader_free_shader_signature(&input_signature); switch (desc->IBStripCutValue) { case D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED: case D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF: case D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF: graphics->index_buffer_strip_cut_value = desc->IBStripCutValue; break; default: WARN("Invalid index buffer strip cut value %#x.\n", desc->IBStripCutValue); hr = E_INVALIDARG; goto fail; } is_dsv_format_unknown = graphics->null_attachment_mask & dsv_attachment_mask(graphics); rs_desc_from_d3d12(&graphics->rs_desc, &desc->RasterizerState); have_attachment = graphics->rt_count || graphics->dsv_format || is_dsv_format_unknown; if ((!have_attachment && !(desc->PS.pShaderBytecode && desc->PS.BytecodeLength)) || (graphics->xfb_enabled && so_desc->RasterizedStream == D3D12_SO_NO_RASTERIZED_STREAM)) graphics->rs_desc.rasterizerDiscardEnable = VK_TRUE; rs_stream_info_from_d3d12(&graphics->rs_stream_info, &graphics->rs_desc, so_desc, vk_info); if (vk_info->EXT_depth_clip_enable) rs_depth_clip_info_from_d3d12(&graphics->rs_depth_clip_info, &graphics->rs_desc, &desc->RasterizerState); graphics->ms_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; graphics->ms_desc.pNext = NULL; graphics->ms_desc.flags = 0; graphics->ms_desc.rasterizationSamples = sample_count; graphics->ms_desc.sampleShadingEnable = VK_FALSE; graphics->ms_desc.minSampleShading = 0.0f; graphics->ms_desc.pSampleMask = NULL; if (desc->SampleMask != ~0u) { assert(DIV_ROUND_UP(sample_count, 32) <= ARRAY_SIZE(graphics->sample_mask)); graphics->sample_mask[0] = desc->SampleMask; graphics->sample_mask[1] = 0xffffffffu; graphics->ms_desc.pSampleMask = graphics->sample_mask; } graphics->ms_desc.alphaToCoverageEnable = desc->BlendState.AlphaToCoverageEnable; graphics->ms_desc.alphaToOneEnable = VK_FALSE; /* We defer creating the render pass for pipelines wth DSVFormat equal to * DXGI_FORMAT_UNKNOWN. We take the actual DSV format from the bound DSV. */ if (is_dsv_format_unknown) graphics->render_pass = VK_NULL_HANDLE; else if (FAILED(hr = d3d12_graphics_pipeline_state_create_render_pass(graphics, device, 0, &graphics->render_pass))) goto fail; graphics->root_signature = root_signature; list_init(&graphics->compiled_pipelines); if (FAILED(hr = vkd3d_private_store_init(&state->private_store))) goto fail; state->vk_bind_point = VK_PIPELINE_BIND_POINT_GRAPHICS; d3d12_device_add_ref(state->device = device); return S_OK; fail: for (i = 0; i < graphics->stage_count; ++i) { VK_CALL(vkDestroyShaderModule(device->vk_device, state->u.graphics.stages[i].module, NULL)); } vkd3d_shader_free_shader_signature(&input_signature); d3d12_pipeline_uav_counter_state_cleanup(&state->uav_counters, device); return hr; } HRESULT d3d12_pipeline_state_create_graphics(struct d3d12_device *device, const D3D12_GRAPHICS_PIPELINE_STATE_DESC *desc, struct d3d12_pipeline_state **state) { struct d3d12_pipeline_state *object; HRESULT hr; if (!(object = vkd3d_malloc(sizeof(*object)))) return E_OUTOFMEMORY; if (FAILED(hr = d3d12_pipeline_state_init_graphics(object, device, desc))) { vkd3d_free(object); return hr; } TRACE("Created graphics pipeline state %p.\n", object); *state = object; return S_OK; } static enum VkPrimitiveTopology vk_topology_from_d3d12_topology(D3D12_PRIMITIVE_TOPOLOGY topology) { switch (topology) { case D3D_PRIMITIVE_TOPOLOGY_POINTLIST: return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; case D3D_PRIMITIVE_TOPOLOGY_LINELIST: return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; case D3D_PRIMITIVE_TOPOLOGY_LINESTRIP: return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; case D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; case D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; case D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_2_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_5_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_6_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_7_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_8_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_9_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_10_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_11_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_12_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_13_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_14_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_15_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_16_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_17_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_18_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_19_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_20_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_21_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_22_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_23_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_24_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_25_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_26_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_27_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_28_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_29_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_30_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_31_CONTROL_POINT_PATCHLIST: case D3D_PRIMITIVE_TOPOLOGY_32_CONTROL_POINT_PATCHLIST: return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; case D3D_PRIMITIVE_TOPOLOGY_UNDEFINED: return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM; default: FIXME("Unhandled primitive topology %#x.\n", topology); return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; } } static bool vk_topology_can_restart(VkPrimitiveTopology topology) { switch (topology) { case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: return false; default: return true; } } static VkPipeline d3d12_pipeline_state_find_compiled_pipeline(const struct d3d12_pipeline_state *state, const struct vkd3d_pipeline_key *key, VkRenderPass *vk_render_pass) { const struct d3d12_graphics_pipeline_state *graphics = &state->u.graphics; struct d3d12_device *device = state->device; VkPipeline vk_pipeline = VK_NULL_HANDLE; struct vkd3d_compiled_pipeline *current; *vk_render_pass = VK_NULL_HANDLE; vkd3d_mutex_lock(&device->mutex); LIST_FOR_EACH_ENTRY(current, &graphics->compiled_pipelines, struct vkd3d_compiled_pipeline, entry) { if (!memcmp(¤t->key, key, sizeof(*key))) { vk_pipeline = current->vk_pipeline; *vk_render_pass = current->vk_render_pass; break; } } vkd3d_mutex_unlock(&device->mutex); return vk_pipeline; } static bool d3d12_pipeline_state_put_pipeline_to_cache(struct d3d12_pipeline_state *state, const struct vkd3d_pipeline_key *key, VkPipeline vk_pipeline, VkRenderPass vk_render_pass) { struct d3d12_graphics_pipeline_state *graphics = &state->u.graphics; struct vkd3d_compiled_pipeline *compiled_pipeline, *current; struct d3d12_device *device = state->device; if (!(compiled_pipeline = vkd3d_malloc(sizeof(*compiled_pipeline)))) return false; compiled_pipeline->key = *key; compiled_pipeline->vk_pipeline = vk_pipeline; compiled_pipeline->vk_render_pass = vk_render_pass; vkd3d_mutex_lock(&device->mutex); LIST_FOR_EACH_ENTRY(current, &graphics->compiled_pipelines, struct vkd3d_compiled_pipeline, entry) { if (!memcmp(¤t->key, key, sizeof(*key))) { vkd3d_free(compiled_pipeline); compiled_pipeline = NULL; break; } } if (compiled_pipeline) list_add_tail(&graphics->compiled_pipelines, &compiled_pipeline->entry); vkd3d_mutex_unlock(&device->mutex); return compiled_pipeline; } VkPipeline d3d12_pipeline_state_get_or_create_pipeline(struct d3d12_pipeline_state *state, D3D12_PRIMITIVE_TOPOLOGY topology, const uint32_t *strides, VkFormat dsv_format, VkRenderPass *vk_render_pass) { VkVertexInputBindingDescription bindings[D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT]; const struct vkd3d_vk_device_procs *vk_procs = &state->device->vk_procs; struct d3d12_graphics_pipeline_state *graphics = &state->u.graphics; VkPipelineVertexInputDivisorStateCreateInfoEXT input_divisor_info; VkPipelineTessellationStateCreateInfo tessellation_info; VkPipelineVertexInputStateCreateInfo input_desc; VkPipelineInputAssemblyStateCreateInfo ia_desc; VkPipelineColorBlendStateCreateInfo blend_desc; struct d3d12_device *device = state->device; VkGraphicsPipelineCreateInfo pipeline_desc; struct vkd3d_pipeline_key pipeline_key; size_t binding_count = 0; VkPipeline vk_pipeline; unsigned int i; uint32_t mask; VkResult vr; HRESULT hr; static const VkPipelineViewportStateCreateInfo vp_desc = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pNext = NULL, .flags = 0, .viewportCount = 1, .pViewports = NULL, .scissorCount = 1, .pScissors = NULL, }; static const VkDynamicState dynamic_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_BLEND_CONSTANTS, VK_DYNAMIC_STATE_STENCIL_REFERENCE, }; static const VkPipelineDynamicStateCreateInfo dynamic_desc = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pNext = NULL, .flags = 0, .dynamicStateCount = ARRAY_SIZE(dynamic_states), .pDynamicStates = dynamic_states, }; assert(d3d12_pipeline_state_is_graphics(state)); memset(&pipeline_key, 0, sizeof(pipeline_key)); pipeline_key.topology = topology; for (i = 0, mask = 0; i < graphics->attribute_count; ++i) { struct VkVertexInputBindingDescription *b; uint32_t binding; binding = graphics->attributes[i].binding; if (mask & (1u << binding)) continue; if (binding_count == ARRAY_SIZE(bindings)) { FIXME("Maximum binding count exceeded.\n"); break; } mask |= 1u << binding; b = &bindings[binding_count]; b->binding = binding; b->stride = strides[binding]; b->inputRate = graphics->input_rates[binding]; pipeline_key.strides[binding_count] = strides[binding]; ++binding_count; } pipeline_key.dsv_format = dsv_format; if ((vk_pipeline = d3d12_pipeline_state_find_compiled_pipeline(state, &pipeline_key, vk_render_pass))) return vk_pipeline; input_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; input_desc.pNext = NULL; input_desc.flags = 0; input_desc.vertexBindingDescriptionCount = binding_count; input_desc.pVertexBindingDescriptions = bindings; input_desc.vertexAttributeDescriptionCount = graphics->attribute_count; input_desc.pVertexAttributeDescriptions = graphics->attributes; if (graphics->instance_divisor_count) { input_desc.pNext = &input_divisor_info; input_divisor_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT; input_divisor_info.pNext = NULL; input_divisor_info.vertexBindingDivisorCount = graphics->instance_divisor_count; input_divisor_info.pVertexBindingDivisors = graphics->instance_divisors; } ia_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; ia_desc.pNext = NULL; ia_desc.flags = 0; ia_desc.topology = vk_topology_from_d3d12_topology(topology); ia_desc.primitiveRestartEnable = graphics->index_buffer_strip_cut_value && vk_topology_can_restart(ia_desc.topology); if (ia_desc.topology == VK_PRIMITIVE_TOPOLOGY_MAX_ENUM) { WARN("Primitive topology is undefined.\n"); return VK_NULL_HANDLE; } tessellation_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; tessellation_info.pNext = NULL; tessellation_info.flags = 0; tessellation_info.patchControlPoints = max(topology - D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + 1, 1); blend_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; blend_desc.pNext = NULL; blend_desc.flags = 0; blend_desc.logicOpEnable = graphics->om_logic_op_enable; blend_desc.logicOp = graphics->om_logic_op; blend_desc.attachmentCount = graphics->rt_count; blend_desc.pAttachments = graphics->blend_attachments; blend_desc.blendConstants[0] = D3D12_DEFAULT_BLEND_FACTOR_RED; blend_desc.blendConstants[1] = D3D12_DEFAULT_BLEND_FACTOR_GREEN; blend_desc.blendConstants[2] = D3D12_DEFAULT_BLEND_FACTOR_BLUE; blend_desc.blendConstants[3] = D3D12_DEFAULT_BLEND_FACTOR_ALPHA; pipeline_desc.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipeline_desc.pNext = NULL; pipeline_desc.flags = 0; pipeline_desc.stageCount = graphics->stage_count; pipeline_desc.pStages = graphics->stages; pipeline_desc.pVertexInputState = &input_desc; pipeline_desc.pInputAssemblyState = &ia_desc; pipeline_desc.pTessellationState = &tessellation_info; pipeline_desc.pViewportState = &vp_desc; pipeline_desc.pRasterizationState = &graphics->rs_desc; pipeline_desc.pMultisampleState = &graphics->ms_desc; pipeline_desc.pDepthStencilState = &graphics->ds_desc; pipeline_desc.pColorBlendState = &blend_desc; pipeline_desc.pDynamicState = &dynamic_desc; pipeline_desc.layout = state->uav_counters.vk_pipeline_layout ? state->uav_counters.vk_pipeline_layout : graphics->root_signature->vk_pipeline_layout; pipeline_desc.subpass = 0; pipeline_desc.basePipelineHandle = VK_NULL_HANDLE; pipeline_desc.basePipelineIndex = -1; /* Create a render pass for pipelines with DXGI_FORMAT_UNKNOWN. */ if (!(pipeline_desc.renderPass = graphics->render_pass)) { if (graphics->null_attachment_mask & dsv_attachment_mask(graphics)) TRACE("Compiling %p with DSV format %#x.\n", state, dsv_format); if (FAILED(hr = d3d12_graphics_pipeline_state_create_render_pass(graphics, device, dsv_format, &pipeline_desc.renderPass))) return VK_NULL_HANDLE; } *vk_render_pass = pipeline_desc.renderPass; if ((vr = VK_CALL(vkCreateGraphicsPipelines(device->vk_device, device->vk_pipeline_cache, 1, &pipeline_desc, NULL, &vk_pipeline))) < 0) { WARN("Failed to create Vulkan graphics pipeline, vr %d.\n", vr); return VK_NULL_HANDLE; } if (d3d12_pipeline_state_put_pipeline_to_cache(state, &pipeline_key, vk_pipeline, pipeline_desc.renderPass)) return vk_pipeline; /* Other thread compiled the pipeline before us. */ VK_CALL(vkDestroyPipeline(device->vk_device, vk_pipeline, NULL)); vk_pipeline = d3d12_pipeline_state_find_compiled_pipeline(state, &pipeline_key, vk_render_pass); if (!vk_pipeline) ERR("Could not get the pipeline compiled by other thread from the cache.\n"); return vk_pipeline; } static void vkd3d_uav_clear_pipelines_cleanup(struct vkd3d_uav_clear_pipelines *pipelines, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VK_CALL(vkDestroyPipeline(device->vk_device, pipelines->image_3d, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, pipelines->image_2d_array, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, pipelines->image_2d, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, pipelines->image_1d_array, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, pipelines->image_1d, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, pipelines->buffer, NULL)); } void vkd3d_uav_clear_state_cleanup(struct vkd3d_uav_clear_state *state, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; vkd3d_uav_clear_pipelines_cleanup(&state->pipelines_uint, device); vkd3d_uav_clear_pipelines_cleanup(&state->pipelines_float, device); VK_CALL(vkDestroyPipelineLayout(device->vk_device, state->vk_pipeline_layout_image, NULL)); VK_CALL(vkDestroyPipelineLayout(device->vk_device, state->vk_pipeline_layout_buffer, NULL)); VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, state->vk_set_layout_image, NULL)); VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, state->vk_set_layout_buffer, NULL)); } HRESULT vkd3d_uav_clear_state_init(struct vkd3d_uav_clear_state *state, struct d3d12_device *device) { struct vkd3d_shader_push_constant_buffer push_constant; struct vkd3d_shader_interface_info shader_interface; struct vkd3d_shader_resource_binding binding; VkDescriptorSetLayoutBinding set_binding; VkPushConstantRange push_constant_range; unsigned int i; HRESULT hr; const struct { VkDescriptorSetLayout *set_layout; VkPipelineLayout *pipeline_layout; VkDescriptorType descriptor_type; } set_layouts[] = { {&state->vk_set_layout_buffer, &state->vk_pipeline_layout_buffer, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER}, {&state->vk_set_layout_image, &state->vk_pipeline_layout_image, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}, }; const struct { VkPipeline *pipeline; VkPipelineLayout *pipeline_layout; D3D12_SHADER_BYTECODE code; } pipelines[] = { #define SHADER_CODE(name) {name, sizeof(name)} {&state->pipelines_float.buffer, &state->vk_pipeline_layout_buffer, SHADER_CODE(cs_uav_clear_buffer_float_code)}, {&state->pipelines_float.image_1d, &state->vk_pipeline_layout_image, SHADER_CODE(cs_uav_clear_1d_float_code)}, {&state->pipelines_float.image_1d_array, &state->vk_pipeline_layout_image, SHADER_CODE(cs_uav_clear_1d_array_float_code)}, {&state->pipelines_float.image_2d, &state->vk_pipeline_layout_image, SHADER_CODE(cs_uav_clear_2d_float_code)}, {&state->pipelines_float.image_2d_array, &state->vk_pipeline_layout_image, SHADER_CODE(cs_uav_clear_2d_array_float_code)}, {&state->pipelines_float.image_3d, &state->vk_pipeline_layout_image, SHADER_CODE(cs_uav_clear_3d_float_code)}, {&state->pipelines_uint.buffer, &state->vk_pipeline_layout_buffer, SHADER_CODE(cs_uav_clear_buffer_uint_code)}, {&state->pipelines_uint.image_1d, &state->vk_pipeline_layout_image, SHADER_CODE(cs_uav_clear_1d_uint_code)}, {&state->pipelines_uint.image_1d_array, &state->vk_pipeline_layout_image, SHADER_CODE(cs_uav_clear_1d_array_uint_code)}, {&state->pipelines_uint.image_2d, &state->vk_pipeline_layout_image, SHADER_CODE(cs_uav_clear_2d_uint_code)}, {&state->pipelines_uint.image_2d_array, &state->vk_pipeline_layout_image, SHADER_CODE(cs_uav_clear_2d_array_uint_code)}, {&state->pipelines_uint.image_3d, &state->vk_pipeline_layout_image, SHADER_CODE(cs_uav_clear_3d_uint_code)}, #undef SHADER_CODE }; memset(state, 0, sizeof(*state)); set_binding.binding = 0; set_binding.descriptorCount = 1; set_binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; set_binding.pImmutableSamplers = NULL; binding.type = VKD3D_SHADER_DESCRIPTOR_TYPE_UAV; binding.register_space = 0; binding.register_index = 0; binding.shader_visibility = VKD3D_SHADER_VISIBILITY_COMPUTE; binding.binding.set = 0; binding.binding.binding = 0; binding.binding.count = 1; push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; push_constant_range.offset = 0; push_constant_range.size = sizeof(struct vkd3d_uav_clear_args); push_constant.register_space = 0; push_constant.register_index = 0; push_constant.shader_visibility = VKD3D_SHADER_VISIBILITY_COMPUTE; push_constant.offset = 0; push_constant.size = sizeof(struct vkd3d_uav_clear_args); for (i = 0; i < ARRAY_SIZE(set_layouts); ++i) { set_binding.descriptorType = set_layouts[i].descriptor_type; if (FAILED(hr = vkd3d_create_descriptor_set_layout(device, 0, 1, false, &set_binding, set_layouts[i].set_layout))) { ERR("Failed to create descriptor set layout %u, hr %#x.\n", i, hr); goto fail; } if (FAILED(hr = vkd3d_create_pipeline_layout(device, 1, set_layouts[i].set_layout, 1, &push_constant_range, set_layouts[i].pipeline_layout))) { ERR("Failed to create pipeline layout %u, hr %#x.\n", i, hr); goto fail; } } shader_interface.type = VKD3D_SHADER_STRUCTURE_TYPE_INTERFACE_INFO; shader_interface.next = NULL; shader_interface.bindings = &binding; shader_interface.binding_count = 1; shader_interface.push_constant_buffers = &push_constant; shader_interface.push_constant_buffer_count = 1; shader_interface.combined_samplers = NULL; shader_interface.combined_sampler_count = 0; shader_interface.uav_counters = NULL; shader_interface.uav_counter_count = 0; for (i = 0; i < ARRAY_SIZE(pipelines); ++i) { if (pipelines[i].pipeline_layout == &state->vk_pipeline_layout_buffer) binding.flags = VKD3D_SHADER_BINDING_FLAG_BUFFER; else binding.flags = VKD3D_SHADER_BINDING_FLAG_IMAGE; if (FAILED(hr = vkd3d_create_compute_pipeline(device, &pipelines[i].code, &shader_interface, *pipelines[i].pipeline_layout, pipelines[i].pipeline))) { ERR("Failed to create compute pipeline %u, hr %#x.\n", i, hr); goto fail; } } return S_OK; fail: vkd3d_uav_clear_state_cleanup(state, device); return hr; }