/* * Copyright 2019 Philip Rebohle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define VKD3D_DBG_CHANNEL VKD3D_DBG_CHANNEL_API #include "vkd3d_private.h" #include "vkd3d_shaders.h" #define SPIRV_CODE(name) name, sizeof(name) static VkResult vkd3d_meta_create_shader_module(struct d3d12_device *device, const uint32_t *code, size_t code_size, VkShaderModule *module) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VkShaderModuleCreateInfo shader_module_info; shader_module_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; shader_module_info.pNext = NULL; shader_module_info.flags = 0; shader_module_info.codeSize = code_size; shader_module_info.pCode = code; return VK_CALL(vkCreateShaderModule(device->vk_device, &shader_module_info, NULL, module)); } static VkResult vkd3d_meta_create_descriptor_set_layout(struct d3d12_device *device, uint32_t binding_count, const VkDescriptorSetLayoutBinding *bindings, VkDescriptorSetLayout *set_layout) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VkDescriptorSetLayoutCreateInfo set_layout_info; set_layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; set_layout_info.pNext = NULL; set_layout_info.flags = 0; set_layout_info.bindingCount = binding_count; set_layout_info.pBindings = bindings; return VK_CALL(vkCreateDescriptorSetLayout(device->vk_device, &set_layout_info, NULL, set_layout)); } static VkResult vkd3d_meta_create_sampler(struct d3d12_device *device, VkFilter filter, VkSampler *vk_sampler) { struct vkd3d_view_key view_key; struct vkd3d_view *view; D3D12_SAMPLER_DESC desc; memset(&desc, 0, sizeof(desc)); desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; desc.Filter = filter == VK_FILTER_LINEAR ? D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT : D3D12_FILTER_MIN_MAG_MIP_POINT; view_key.view_type = VKD3D_VIEW_TYPE_SAMPLER; view_key.u.sampler = desc; view = vkd3d_view_map_create_view(&device->sampler_map, device, &view_key); if (!view) return VK_ERROR_OUT_OF_HOST_MEMORY; *vk_sampler = view->vk_sampler; return VK_SUCCESS; } static VkResult vkd3d_meta_create_pipeline_layout(struct d3d12_device *device, uint32_t set_layout_count, const VkDescriptorSetLayout *set_layouts, uint32_t push_constant_range_count, const VkPushConstantRange *push_constant_ranges, VkPipelineLayout *pipeline_layout) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VkPipelineLayoutCreateInfo pipeline_layout_info; 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_range_count; pipeline_layout_info.pPushConstantRanges = push_constant_ranges; return VK_CALL(vkCreatePipelineLayout(device->vk_device, &pipeline_layout_info, NULL, pipeline_layout)); } static void vkd3d_meta_make_shader_stage(VkPipelineShaderStageCreateInfo *info, VkShaderStageFlagBits stage, VkShaderModule module, const char* entry_point, const VkSpecializationInfo *spec_info) { info->sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; info->pNext = NULL; info->flags = 0; info->stage = stage; info->module = module; info->pName = entry_point; info->pSpecializationInfo = spec_info; } static VkResult vkd3d_meta_create_compute_pipeline(struct d3d12_device *device, size_t code_size, const uint32_t *code, VkPipelineLayout layout, const VkSpecializationInfo *specialization_info, VkPipeline *pipeline) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VkComputePipelineCreateInfo pipeline_info; VkShaderModule module; VkResult vr; if ((vr = vkd3d_meta_create_shader_module(device, code, code_size, &module)) < 0) { ERR("Failed to create shader module, vr %d.", vr); return vr; } pipeline_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; pipeline_info.pNext = NULL; pipeline_info.flags = 0; pipeline_info.layout = layout; pipeline_info.basePipelineHandle = VK_NULL_HANDLE; pipeline_info.basePipelineIndex = -1; vkd3d_meta_make_shader_stage(&pipeline_info.stage, VK_SHADER_STAGE_COMPUTE_BIT, module, "main", specialization_info); vr = VK_CALL(vkCreateComputePipelines(device->vk_device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, pipeline)); VK_CALL(vkDestroyShaderModule(device->vk_device, module, NULL)); return vr; } static VkResult vkd3d_meta_create_graphics_pipeline(struct vkd3d_meta_ops *meta_ops, VkPipelineLayout layout, VkFormat color_format, VkFormat ds_format, VkImageAspectFlags vk_aspect_mask, VkShaderModule vs_module, VkShaderModule fs_module, VkSampleCountFlagBits samples, const VkPipelineDepthStencilStateCreateInfo *ds_state, const VkPipelineColorBlendStateCreateInfo *cb_state, const VkSpecializationInfo *spec_info, VkPipeline *vk_pipeline) { const struct vkd3d_vk_device_procs *vk_procs = &meta_ops->device->vk_procs; VkPipelineShaderStageCreateInfo shader_stages[3]; VkPipelineInputAssemblyStateCreateInfo ia_state; VkPipelineRasterizationStateCreateInfo rs_state; VkPipelineRenderingCreateInfoKHR rendering_info; VkPipelineVertexInputStateCreateInfo vi_state; VkPipelineMultisampleStateCreateInfo ms_state; VkPipelineViewportStateCreateInfo vp_state; VkPipelineDynamicStateCreateInfo dyn_state; VkGraphicsPipelineCreateInfo pipeline_info; const uint32_t sample_mask = 0xFFFFFFFF; VkResult vr; static const VkDynamicState dynamic_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, }; vi_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vi_state.pNext = NULL; vi_state.flags = 0; vi_state.vertexBindingDescriptionCount = 0; vi_state.pVertexBindingDescriptions = NULL; vi_state.vertexAttributeDescriptionCount = 0; vi_state.pVertexAttributeDescriptions = NULL; ia_state.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; ia_state.pNext = NULL; ia_state.flags = 0; ia_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; ia_state.primitiveRestartEnable = false; vp_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; vp_state.pNext = NULL; vp_state.flags = 0; vp_state.viewportCount = 1; vp_state.pViewports = NULL; vp_state.scissorCount = 1; vp_state.pScissors = NULL; rs_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rs_state.pNext = NULL; rs_state.flags = 0; rs_state.depthClampEnable = VK_TRUE; rs_state.rasterizerDiscardEnable = VK_FALSE; rs_state.polygonMode = VK_POLYGON_MODE_FILL; rs_state.cullMode = VK_CULL_MODE_NONE; rs_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rs_state.depthBiasEnable = VK_FALSE; rs_state.depthBiasConstantFactor = 0.0f; rs_state.depthBiasClamp = 0.0f; rs_state.depthBiasSlopeFactor = 0.0f; rs_state.lineWidth = 1.0f; ms_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; ms_state.pNext = NULL; ms_state.flags = 0; ms_state.rasterizationSamples = samples; ms_state.sampleShadingEnable = samples != VK_SAMPLE_COUNT_1_BIT; ms_state.minSampleShading = 1.0f; ms_state.pSampleMask = &sample_mask; ms_state.alphaToCoverageEnable = VK_FALSE; ms_state.alphaToOneEnable = VK_FALSE; dyn_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dyn_state.pNext = NULL; dyn_state.flags = 0; dyn_state.dynamicStateCount = ARRAY_SIZE(dynamic_states); dyn_state.pDynamicStates = dynamic_states; rendering_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR; rendering_info.pNext = NULL; rendering_info.viewMask = 0; rendering_info.colorAttachmentCount = color_format && (vk_aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) ? 1 : 0; rendering_info.pColorAttachmentFormats = color_format ? &color_format : NULL; rendering_info.depthAttachmentFormat = (vk_aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) ? ds_format : VK_FORMAT_UNDEFINED; rendering_info.stencilAttachmentFormat = (vk_aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) ? ds_format : VK_FORMAT_UNDEFINED; pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipeline_info.pNext = &rendering_info; pipeline_info.flags = 0; pipeline_info.stageCount = 0; pipeline_info.pStages = shader_stages; pipeline_info.pVertexInputState = &vi_state; pipeline_info.pInputAssemblyState = &ia_state; pipeline_info.pTessellationState = NULL; pipeline_info.pViewportState = &vp_state; pipeline_info.pRasterizationState = &rs_state; pipeline_info.pMultisampleState = &ms_state; pipeline_info.pDepthStencilState = ds_state; pipeline_info.pColorBlendState = cb_state; pipeline_info.pDynamicState = &dyn_state; pipeline_info.layout = layout; pipeline_info.renderPass = VK_NULL_HANDLE; pipeline_info.subpass = 0; pipeline_info.basePipelineHandle = VK_NULL_HANDLE; pipeline_info.basePipelineIndex = -1; vkd3d_meta_make_shader_stage(&shader_stages[pipeline_info.stageCount++], VK_SHADER_STAGE_VERTEX_BIT, vs_module ? vs_module : meta_ops->common.vk_module_fullscreen_vs, "main", NULL); if (meta_ops->common.vk_module_fullscreen_gs) { vkd3d_meta_make_shader_stage(&shader_stages[pipeline_info.stageCount++], VK_SHADER_STAGE_GEOMETRY_BIT, meta_ops->common.vk_module_fullscreen_gs, "main", NULL); } if (fs_module) { vkd3d_meta_make_shader_stage(&shader_stages[pipeline_info.stageCount++], VK_SHADER_STAGE_FRAGMENT_BIT, fs_module, "main", spec_info); } if ((vr = VK_CALL(vkCreateGraphicsPipelines(meta_ops->device->vk_device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, vk_pipeline)))) ERR("Failed to create graphics pipeline, vr %d.\n", vr); return vr; } HRESULT vkd3d_clear_uav_ops_init(struct vkd3d_clear_uav_ops *meta_clear_uav_ops, struct d3d12_device *device) { VkDescriptorSetLayoutBinding set_binding; VkPushConstantRange push_constant_range; unsigned int i; VkResult vr; struct { VkDescriptorSetLayout *set_layout; VkPipelineLayout *pipeline_layout; VkDescriptorType descriptor_type; } set_layouts[] = { { &meta_clear_uav_ops->vk_set_layout_buffer_raw, &meta_clear_uav_ops->vk_pipeline_layout_buffer_raw, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER }, { &meta_clear_uav_ops->vk_set_layout_buffer, &meta_clear_uav_ops->vk_pipeline_layout_buffer, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER }, { &meta_clear_uav_ops->vk_set_layout_image, &meta_clear_uav_ops->vk_pipeline_layout_image, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE }, }; struct { VkPipeline *pipeline; VkPipelineLayout *pipeline_layout; const uint32_t *code; size_t code_size; } pipelines[] = { { &meta_clear_uav_ops->clear_float.buffer, &meta_clear_uav_ops->vk_pipeline_layout_buffer, SPIRV_CODE(cs_clear_uav_buffer_float) }, { &meta_clear_uav_ops->clear_float.image_1d, &meta_clear_uav_ops->vk_pipeline_layout_image, SPIRV_CODE(cs_clear_uav_image_1d_float) }, { &meta_clear_uav_ops->clear_float.image_1d_array, &meta_clear_uav_ops->vk_pipeline_layout_image, SPIRV_CODE(cs_clear_uav_image_1d_array_float) }, { &meta_clear_uav_ops->clear_float.image_2d, &meta_clear_uav_ops->vk_pipeline_layout_image, SPIRV_CODE(cs_clear_uav_image_2d_float) }, { &meta_clear_uav_ops->clear_float.image_2d_array, &meta_clear_uav_ops->vk_pipeline_layout_image, SPIRV_CODE(cs_clear_uav_image_2d_array_float) }, { &meta_clear_uav_ops->clear_float.image_3d, &meta_clear_uav_ops->vk_pipeline_layout_image, SPIRV_CODE(cs_clear_uav_image_3d_float) }, { &meta_clear_uav_ops->clear_uint.buffer, &meta_clear_uav_ops->vk_pipeline_layout_buffer, SPIRV_CODE(cs_clear_uav_buffer_uint) }, { &meta_clear_uav_ops->clear_uint.buffer_raw, &meta_clear_uav_ops->vk_pipeline_layout_buffer_raw, SPIRV_CODE(cs_clear_uav_buffer_raw) }, { &meta_clear_uav_ops->clear_uint.image_1d, &meta_clear_uav_ops->vk_pipeline_layout_image, SPIRV_CODE(cs_clear_uav_image_1d_uint) }, { &meta_clear_uav_ops->clear_uint.image_1d_array, &meta_clear_uav_ops->vk_pipeline_layout_image, SPIRV_CODE(cs_clear_uav_image_1d_array_uint) }, { &meta_clear_uav_ops->clear_uint.image_2d, &meta_clear_uav_ops->vk_pipeline_layout_image, SPIRV_CODE(cs_clear_uav_image_2d_uint) }, { &meta_clear_uav_ops->clear_uint.image_2d_array, &meta_clear_uav_ops->vk_pipeline_layout_image, SPIRV_CODE(cs_clear_uav_image_2d_array_uint) }, { &meta_clear_uav_ops->clear_uint.image_3d, &meta_clear_uav_ops->vk_pipeline_layout_image, SPIRV_CODE(cs_clear_uav_image_3d_uint) }, }; memset(meta_clear_uav_ops, 0, sizeof(*meta_clear_uav_ops)); set_binding.binding = 0; set_binding.descriptorCount = 1; set_binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; set_binding.pImmutableSamplers = NULL; push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; push_constant_range.offset = 0; push_constant_range.size = sizeof(struct vkd3d_clear_uav_args); for (i = 0; i < ARRAY_SIZE(set_layouts); i++) { set_binding.descriptorType = set_layouts[i].descriptor_type; vr = vkd3d_meta_create_descriptor_set_layout(device, 1, &set_binding, set_layouts[i].set_layout); if (vr < 0) { ERR("Failed to create descriptor set layout %u, vr %d.", i, vr); goto fail; } vr = vkd3d_meta_create_pipeline_layout(device, 1, set_layouts[i].set_layout, 1, &push_constant_range, set_layouts[i].pipeline_layout); if (vr < 0) { ERR("Failed to create pipeline layout %u, vr %d.", i, vr); goto fail; } } for (i = 0; i < ARRAY_SIZE(pipelines); i++) { if ((vr = vkd3d_meta_create_compute_pipeline(device, pipelines[i].code_size, pipelines[i].code, *pipelines[i].pipeline_layout, NULL, pipelines[i].pipeline)) < 0) { ERR("Failed to create compute pipeline %u, vr %d.", i, vr); goto fail; } } return S_OK; fail: vkd3d_clear_uav_ops_cleanup(meta_clear_uav_ops, device); return hresult_from_vk_result(vr); } void vkd3d_clear_uav_ops_cleanup(struct vkd3d_clear_uav_ops *meta_clear_uav_ops, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; unsigned int i; struct vkd3d_clear_uav_pipelines* pipeline_sets[] = { &meta_clear_uav_ops->clear_float, &meta_clear_uav_ops->clear_uint, }; VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, meta_clear_uav_ops->vk_set_layout_buffer_raw, NULL)); VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, meta_clear_uav_ops->vk_set_layout_buffer, NULL)); VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, meta_clear_uav_ops->vk_set_layout_image, NULL)); VK_CALL(vkDestroyPipelineLayout(device->vk_device, meta_clear_uav_ops->vk_pipeline_layout_buffer_raw, NULL)); VK_CALL(vkDestroyPipelineLayout(device->vk_device, meta_clear_uav_ops->vk_pipeline_layout_buffer, NULL)); VK_CALL(vkDestroyPipelineLayout(device->vk_device, meta_clear_uav_ops->vk_pipeline_layout_image, NULL)); for (i = 0; i < ARRAY_SIZE(pipeline_sets); i++) { VK_CALL(vkDestroyPipeline(device->vk_device, pipeline_sets[i]->buffer, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, pipeline_sets[i]->buffer_raw, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, pipeline_sets[i]->image_1d, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, pipeline_sets[i]->image_2d, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, pipeline_sets[i]->image_3d, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, pipeline_sets[i]->image_1d_array, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, pipeline_sets[i]->image_2d_array, NULL)); } } struct vkd3d_clear_uav_pipeline vkd3d_meta_get_clear_buffer_uav_pipeline(struct vkd3d_meta_ops *meta_ops, bool as_uint, bool raw) { struct vkd3d_clear_uav_ops *meta_clear_uav_ops = &meta_ops->clear_uav; struct vkd3d_clear_uav_pipeline info; const struct vkd3d_clear_uav_pipelines *pipelines = (as_uint || raw) ? &meta_clear_uav_ops->clear_uint : &meta_clear_uav_ops->clear_float; info.vk_set_layout = raw ? meta_clear_uav_ops->vk_set_layout_buffer_raw : meta_clear_uav_ops->vk_set_layout_buffer; info.vk_pipeline_layout = raw ? meta_clear_uav_ops->vk_pipeline_layout_buffer_raw : meta_clear_uav_ops->vk_pipeline_layout_buffer; info.vk_pipeline = raw ? pipelines->buffer_raw : pipelines->buffer; return info; } struct vkd3d_clear_uav_pipeline vkd3d_meta_get_clear_image_uav_pipeline(struct vkd3d_meta_ops *meta_ops, VkImageViewType image_view_type, bool as_uint) { struct vkd3d_clear_uav_ops *meta_clear_uav_ops = &meta_ops->clear_uav; struct vkd3d_clear_uav_pipeline info; const struct vkd3d_clear_uav_pipelines *pipelines = as_uint ? &meta_clear_uav_ops->clear_uint : &meta_clear_uav_ops->clear_float; info.vk_set_layout = meta_clear_uav_ops->vk_set_layout_image; info.vk_pipeline_layout = meta_clear_uav_ops->vk_pipeline_layout_image; switch (image_view_type) { case VK_IMAGE_VIEW_TYPE_1D: info.vk_pipeline = pipelines->image_1d; break; case VK_IMAGE_VIEW_TYPE_2D: info.vk_pipeline = pipelines->image_2d; break; case VK_IMAGE_VIEW_TYPE_3D: info.vk_pipeline = pipelines->image_3d; break; case VK_IMAGE_VIEW_TYPE_1D_ARRAY: info.vk_pipeline = pipelines->image_1d_array; break; case VK_IMAGE_VIEW_TYPE_2D_ARRAY: info.vk_pipeline = pipelines->image_2d_array; break; default: ERR("Unhandled view type %d.\n", image_view_type); info.vk_pipeline = VK_NULL_HANDLE; } return info; } VkExtent3D vkd3d_meta_get_clear_image_uav_workgroup_size(VkImageViewType view_type) { switch (view_type) { case VK_IMAGE_VIEW_TYPE_1D: case VK_IMAGE_VIEW_TYPE_1D_ARRAY: { VkExtent3D result = { 64, 1, 1 }; return result; } case VK_IMAGE_VIEW_TYPE_2D: case VK_IMAGE_VIEW_TYPE_2D_ARRAY: case VK_IMAGE_VIEW_TYPE_3D: { VkExtent3D result = { 8, 8, 1 }; return result; } default: { VkExtent3D result = { 0, 0, 0 }; ERR("Unhandled view type %d.\n", view_type); return result; } } } HRESULT vkd3d_copy_image_ops_init(struct vkd3d_copy_image_ops *meta_copy_image_ops, struct d3d12_device *device) { VkDescriptorSetLayoutBinding set_binding; VkPushConstantRange push_constant_range; VkResult vr; int rc; memset(meta_copy_image_ops, 0, sizeof(*meta_copy_image_ops)); if ((rc = pthread_mutex_init(&meta_copy_image_ops->mutex, NULL))) { ERR("Failed to initialize mutex, error %d.\n", rc); return hresult_from_errno(rc); } set_binding.binding = 0; set_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; set_binding.descriptorCount = 1; set_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; set_binding.pImmutableSamplers = NULL; if ((vr = vkd3d_meta_create_descriptor_set_layout(device, 1, &set_binding, &meta_copy_image_ops->vk_set_layout)) < 0) { ERR("Failed to create descriptor set layout, vr %d.\n", vr); goto fail; } push_constant_range.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; push_constant_range.offset = 0; push_constant_range.size = sizeof(struct vkd3d_copy_image_args); if ((vr = vkd3d_meta_create_pipeline_layout(device, 1, &meta_copy_image_ops->vk_set_layout, 1, &push_constant_range, &meta_copy_image_ops->vk_pipeline_layout))) { ERR("Failed to create pipeline layout, vr %d.\n", vr); goto fail; } if ((vr = vkd3d_meta_create_shader_module(device, SPIRV_CODE(fs_copy_image_float), &meta_copy_image_ops->vk_fs_float_module)) < 0) { ERR("Failed to create shader modules, vr %d.\n", vr); goto fail; } if ((vr = vkd3d_meta_create_shader_module(device, SPIRV_CODE(fs_copy_image_uint), &meta_copy_image_ops->vk_fs_uint_module)) < 0) { ERR("Failed to create shader modules, vr %d.\n", vr); goto fail; } if (device->vk_info.EXT_shader_stencil_export) { if ((vr = vkd3d_meta_create_shader_module(device, SPIRV_CODE(fs_copy_image_stencil), &meta_copy_image_ops->vk_fs_stencil_module)) < 0) { ERR("Failed to create shader modules, vr %d.\n", vr); goto fail; } } return S_OK; fail: vkd3d_copy_image_ops_cleanup(meta_copy_image_ops, device); return hresult_from_vk_result(vr); } void vkd3d_copy_image_ops_cleanup(struct vkd3d_copy_image_ops *meta_copy_image_ops, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; size_t i; for (i = 0; i < meta_copy_image_ops->pipeline_count; i++) { struct vkd3d_copy_image_pipeline *pipeline = &meta_copy_image_ops->pipelines[i]; VK_CALL(vkDestroyPipeline(device->vk_device, pipeline->vk_pipeline, NULL)); } VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, meta_copy_image_ops->vk_set_layout, NULL)); VK_CALL(vkDestroyPipelineLayout(device->vk_device, meta_copy_image_ops->vk_pipeline_layout, NULL)); VK_CALL(vkDestroyShaderModule(device->vk_device, meta_copy_image_ops->vk_fs_float_module, NULL)); VK_CALL(vkDestroyShaderModule(device->vk_device, meta_copy_image_ops->vk_fs_uint_module, NULL)); VK_CALL(vkDestroyShaderModule(device->vk_device, meta_copy_image_ops->vk_fs_stencil_module, NULL)); pthread_mutex_destroy(&meta_copy_image_ops->mutex); vkd3d_free(meta_copy_image_ops->pipelines); } static HRESULT vkd3d_meta_create_swapchain_pipeline(struct vkd3d_meta_ops *meta_ops, const struct vkd3d_swapchain_pipeline_key *key, struct vkd3d_swapchain_pipeline *pipeline) { struct vkd3d_swapchain_ops *meta_swapchain_ops = &meta_ops->swapchain; VkPipelineColorBlendAttachmentState blend_att; VkPipelineColorBlendStateCreateInfo cb_state; VkResult vr; memset(&cb_state, 0, sizeof(cb_state)); memset(&blend_att, 0, sizeof(blend_att)); cb_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; cb_state.attachmentCount = 1; cb_state.pAttachments = &blend_att; blend_att.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; if ((vr = vkd3d_meta_create_graphics_pipeline(meta_ops, meta_swapchain_ops->vk_pipeline_layouts[key->filter], key->format, VK_FORMAT_UNDEFINED, VK_IMAGE_ASPECT_COLOR_BIT, meta_swapchain_ops->vk_vs_module, meta_swapchain_ops->vk_fs_module, 1, NULL, &cb_state, NULL, &pipeline->vk_pipeline)) < 0) return hresult_from_vk_result(vr); pipeline->key = *key; return S_OK; } static HRESULT vkd3d_meta_create_copy_image_pipeline(struct vkd3d_meta_ops *meta_ops, const struct vkd3d_copy_image_pipeline_key *key, struct vkd3d_copy_image_pipeline *pipeline) { struct vkd3d_copy_image_ops *meta_copy_image_ops = &meta_ops->copy_image; VkPipelineColorBlendAttachmentState blend_attachment; VkPipelineDepthStencilStateCreateInfo ds_state; VkPipelineColorBlendStateCreateInfo cb_state; VkSpecializationInfo spec_info; VkShaderModule vk_module; bool has_depth_target; VkResult vr; struct spec_data { enum vkd3d_meta_copy_mode mode; } spec_data; static const VkSpecializationMapEntry map_entries[] = { { 0, offsetof(struct spec_data, mode), sizeof(spec_data.mode) }, }; if (key->view_type == VK_IMAGE_VIEW_TYPE_1D_ARRAY) { spec_data.mode = VKD3D_META_COPY_MODE_1D; } else if (key->view_type == VK_IMAGE_VIEW_TYPE_2D_ARRAY) { spec_data.mode = key->sample_count == VK_SAMPLE_COUNT_1_BIT ? VKD3D_META_COPY_MODE_2D : VKD3D_META_COPY_MODE_MS; } else { ERR("Unhandled view type %u.\n", key->view_type); return E_INVALIDARG; } spec_info.mapEntryCount = ARRAY_SIZE(map_entries); spec_info.pMapEntries = map_entries; spec_info.dataSize = sizeof(spec_data); spec_info.pData = &spec_data; has_depth_target = (key->format->vk_aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0; ds_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; ds_state.pNext = NULL; ds_state.flags = 0; ds_state.depthTestEnable = (key->dst_aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) ? VK_TRUE : VK_FALSE; ds_state.depthWriteEnable = ds_state.depthTestEnable; ds_state.depthCompareOp = VK_COMPARE_OP_ALWAYS; ds_state.depthBoundsTestEnable = VK_FALSE; if (key->dst_aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) { ds_state.stencilTestEnable = VK_TRUE; ds_state.front.reference = 0; ds_state.front.writeMask = 0xff; ds_state.front.compareMask = 0xff; ds_state.front.passOp = VK_STENCIL_OP_REPLACE; ds_state.front.failOp = VK_STENCIL_OP_KEEP; ds_state.front.depthFailOp = VK_STENCIL_OP_KEEP; ds_state.front.compareOp = VK_COMPARE_OP_ALWAYS; ds_state.back = ds_state.front; } else { ds_state.stencilTestEnable = VK_FALSE; memset(&ds_state.front, 0, sizeof(ds_state.front)); memset(&ds_state.back, 0, sizeof(ds_state.back)); } ds_state.minDepthBounds = 0.0f; ds_state.maxDepthBounds = 1.0f; memset(&blend_attachment, 0, sizeof(blend_attachment)); blend_attachment.blendEnable = VK_FALSE; blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; cb_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; cb_state.pNext = NULL; cb_state.flags = 0; cb_state.logicOpEnable = VK_FALSE; cb_state.logicOp = VK_LOGIC_OP_NO_OP; cb_state.attachmentCount = 1; cb_state.pAttachments = &blend_attachment; memset(&cb_state.blendConstants, 0, sizeof(cb_state.blendConstants)); /* Special path when copying stencil -> color. */ if (key->format->vk_format == VK_FORMAT_R8_UINT) { /* Special path when copying stencil -> color. */ vk_module = meta_copy_image_ops->vk_fs_uint_module; } else if (key->dst_aspect_mask == VK_IMAGE_ASPECT_STENCIL_BIT) { /* FragStencilRef path. */ vk_module = meta_copy_image_ops->vk_fs_stencil_module; } else { /* Depth or float color path. */ vk_module = meta_copy_image_ops->vk_fs_float_module; } if ((vr = vkd3d_meta_create_graphics_pipeline(meta_ops, meta_copy_image_ops->vk_pipeline_layout, has_depth_target ? VK_FORMAT_UNDEFINED : key->format->vk_format, has_depth_target ? key->format->vk_format : VK_FORMAT_UNDEFINED, key->format->vk_aspect_mask, VK_NULL_HANDLE, vk_module, key->sample_count, has_depth_target ? &ds_state : NULL, has_depth_target ? NULL : &cb_state, &spec_info, &pipeline->vk_pipeline)) < 0) return hresult_from_vk_result(vr); pipeline->key = *key; return S_OK; } HRESULT vkd3d_meta_get_copy_image_pipeline(struct vkd3d_meta_ops *meta_ops, const struct vkd3d_copy_image_pipeline_key *key, struct vkd3d_copy_image_info *info) { struct vkd3d_copy_image_ops *meta_copy_image_ops = &meta_ops->copy_image; struct vkd3d_copy_image_pipeline *pipeline; HRESULT hr; size_t i; int rc; if ((rc = pthread_mutex_lock(&meta_copy_image_ops->mutex))) { ERR("Failed to lock mutex, error %d.\n", rc); return hresult_from_errno(rc); } info->vk_set_layout = meta_copy_image_ops->vk_set_layout; info->vk_pipeline_layout = meta_copy_image_ops->vk_pipeline_layout; for (i = 0; i < meta_copy_image_ops->pipeline_count; i++) { pipeline = &meta_copy_image_ops->pipelines[i]; if (!memcmp(key, &pipeline->key, sizeof(*key))) { info->vk_pipeline = pipeline->vk_pipeline; pthread_mutex_unlock(&meta_copy_image_ops->mutex); return S_OK; } } if (!vkd3d_array_reserve((void **)&meta_copy_image_ops->pipelines, &meta_copy_image_ops->pipelines_size, meta_copy_image_ops->pipeline_count + 1, sizeof(*meta_copy_image_ops->pipelines))) { ERR("Failed to reserve space for pipeline.\n"); return E_OUTOFMEMORY; } pipeline = &meta_copy_image_ops->pipelines[meta_copy_image_ops->pipeline_count++]; if (FAILED(hr = vkd3d_meta_create_copy_image_pipeline(meta_ops, key, pipeline))) { pthread_mutex_unlock(&meta_copy_image_ops->mutex); return hr; } info->vk_pipeline = pipeline->vk_pipeline; pthread_mutex_unlock(&meta_copy_image_ops->mutex); return S_OK; } VkImageViewType vkd3d_meta_get_copy_image_view_type(D3D12_RESOURCE_DIMENSION dim) { switch (dim) { case D3D12_RESOURCE_DIMENSION_TEXTURE1D: return VK_IMAGE_VIEW_TYPE_1D_ARRAY; case D3D12_RESOURCE_DIMENSION_TEXTURE2D: return VK_IMAGE_VIEW_TYPE_2D_ARRAY; default: ERR("Unhandled resource dimension %u.\n", dim); return VK_IMAGE_VIEW_TYPE_2D_ARRAY; } } const struct vkd3d_format *vkd3d_meta_get_copy_image_attachment_format(struct vkd3d_meta_ops *meta_ops, const struct vkd3d_format *dst_format, const struct vkd3d_format *src_format, VkImageAspectFlags dst_aspect, VkImageAspectFlags src_aspect) { DXGI_FORMAT dxgi_format = DXGI_FORMAT_UNKNOWN; if (dst_aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) return dst_format; assert(src_aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)); switch (src_format->vk_format) { case VK_FORMAT_D16_UNORM: dxgi_format = DXGI_FORMAT_R16_UNORM; break; case VK_FORMAT_D16_UNORM_S8_UINT: dxgi_format = (src_aspect & VK_IMAGE_ASPECT_DEPTH_BIT) ? DXGI_FORMAT_R16_UNORM : DXGI_FORMAT_R8_UINT; break; case VK_FORMAT_D32_SFLOAT: dxgi_format = DXGI_FORMAT_R32_FLOAT; break; case VK_FORMAT_D32_SFLOAT_S8_UINT: dxgi_format = (src_aspect & VK_IMAGE_ASPECT_DEPTH_BIT) ? DXGI_FORMAT_R32_FLOAT : DXGI_FORMAT_R8_UINT; break; default: ERR("Unhandled format %u.\n", src_format->vk_format); return NULL; } return vkd3d_get_format(meta_ops->device, dxgi_format, false); } static HRESULT vkd3d_meta_ops_common_init(struct vkd3d_meta_ops_common *meta_ops_common, struct d3d12_device *device) { VkResult vr; if (device->vk_info.EXT_shader_viewport_index_layer) { if ((vr = vkd3d_meta_create_shader_module(device, SPIRV_CODE(vs_fullscreen_layer), &meta_ops_common->vk_module_fullscreen_vs)) < 0) { ERR("Failed to create shader modules, vr %d.\n", vr); return hresult_from_vk_result(vr); } } else { if ((vr = vkd3d_meta_create_shader_module(device, SPIRV_CODE(vs_fullscreen), &meta_ops_common->vk_module_fullscreen_vs)) < 0 || (vr = vkd3d_meta_create_shader_module(device, SPIRV_CODE(gs_fullscreen), &meta_ops_common->vk_module_fullscreen_gs)) < 0) { ERR("Failed to create shader modules, vr %d.\n", vr); return hresult_from_vk_result(vr); } } return S_OK; } static void vkd3d_meta_ops_common_cleanup(struct vkd3d_meta_ops_common *meta_ops_common, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VK_CALL(vkDestroyShaderModule(device->vk_device, meta_ops_common->vk_module_fullscreen_vs, NULL)); VK_CALL(vkDestroyShaderModule(device->vk_device, meta_ops_common->vk_module_fullscreen_gs, NULL)); } HRESULT vkd3d_swapchain_ops_init(struct vkd3d_swapchain_ops *meta_swapchain_ops, struct d3d12_device *device) { VkDescriptorSetLayoutBinding set_binding; unsigned int i; VkResult vr; int rc; memset(meta_swapchain_ops, 0, sizeof(*meta_swapchain_ops)); if ((rc = pthread_mutex_init(&meta_swapchain_ops->mutex, NULL))) { ERR("Failed to initialize mutex, error %d.\n", rc); return hresult_from_errno(rc); } set_binding.binding = 0; set_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; set_binding.descriptorCount = 1; set_binding.stageFlags = VK_SHADER_STAGE_ALL; /* Could be compute or graphics, so just use ALL. */ for (i = 0; i < 2; i++) { if ((vr = vkd3d_meta_create_sampler(device, (VkFilter)i, &meta_swapchain_ops->vk_samplers[i]))) { ERR("Failed to create sampler, vr, %d.\n", vr); goto fail; } set_binding.pImmutableSamplers = &meta_swapchain_ops->vk_samplers[i]; if ((vr = vkd3d_meta_create_descriptor_set_layout(device, 1, &set_binding, &meta_swapchain_ops->vk_set_layouts[i])) < 0) { ERR("Failed to create descriptor set layout, vr %d.\n", vr); goto fail; } if ((vr = vkd3d_meta_create_pipeline_layout(device, 1, &meta_swapchain_ops->vk_set_layouts[i], 0, NULL, &meta_swapchain_ops->vk_pipeline_layouts[i]))) { ERR("Failed to create pipeline layout, vr %d.\n", vr); goto fail; } } if ((vr = vkd3d_meta_create_shader_module(device, SPIRV_CODE(vs_swapchain_fullscreen), &meta_swapchain_ops->vk_vs_module)) < 0) { ERR("Failed to create shader modules, vr %d.\n", vr); goto fail; } if ((vr = vkd3d_meta_create_shader_module(device, SPIRV_CODE(fs_swapchain_fullscreen), &meta_swapchain_ops->vk_fs_module)) < 0) { ERR("Failed to create shader modules, vr %d.\n", vr); goto fail; } return S_OK; fail: vkd3d_swapchain_ops_cleanup(meta_swapchain_ops, device); return hresult_from_vk_result(vr); } void vkd3d_swapchain_ops_cleanup(struct vkd3d_swapchain_ops *meta_swapchain_ops, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; size_t i; for (i = 0; i < meta_swapchain_ops->pipeline_count; i++) { struct vkd3d_swapchain_pipeline *pipeline = &meta_swapchain_ops->pipelines[i]; VK_CALL(vkDestroyPipeline(device->vk_device, pipeline->vk_pipeline, NULL)); } for (i = 0; i < 2; i++) { VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, meta_swapchain_ops->vk_set_layouts[i], NULL)); VK_CALL(vkDestroyPipelineLayout(device->vk_device, meta_swapchain_ops->vk_pipeline_layouts[i], NULL)); } VK_CALL(vkDestroyShaderModule(device->vk_device, meta_swapchain_ops->vk_vs_module, NULL)); VK_CALL(vkDestroyShaderModule(device->vk_device, meta_swapchain_ops->vk_fs_module, NULL)); pthread_mutex_destroy(&meta_swapchain_ops->mutex); vkd3d_free(meta_swapchain_ops->pipelines); } HRESULT vkd3d_meta_get_swapchain_pipeline(struct vkd3d_meta_ops *meta_ops, const struct vkd3d_swapchain_pipeline_key *key, struct vkd3d_swapchain_info *info) { struct vkd3d_swapchain_ops *meta_swapchain_ops = &meta_ops->swapchain; struct vkd3d_swapchain_pipeline *pipeline; HRESULT hr; size_t i; int rc; if ((rc = pthread_mutex_lock(&meta_swapchain_ops->mutex))) { ERR("Failed to lock mutex, error %d.\n", rc); return hresult_from_errno(rc); } info->vk_set_layout = meta_swapchain_ops->vk_set_layouts[key->filter]; info->vk_pipeline_layout = meta_swapchain_ops->vk_pipeline_layouts[key->filter]; for (i = 0; i < meta_swapchain_ops->pipeline_count; i++) { pipeline = &meta_swapchain_ops->pipelines[i]; if (!memcmp(key, &pipeline->key, sizeof(*key))) { info->vk_pipeline = pipeline->vk_pipeline; pthread_mutex_unlock(&meta_swapchain_ops->mutex); return S_OK; } } if (!vkd3d_array_reserve((void **)&meta_swapchain_ops->pipelines, &meta_swapchain_ops->pipelines_size, meta_swapchain_ops->pipeline_count + 1, sizeof(*meta_swapchain_ops->pipelines))) { ERR("Failed to reserve space for pipeline.\n"); return E_OUTOFMEMORY; } pipeline = &meta_swapchain_ops->pipelines[meta_swapchain_ops->pipeline_count++]; if (FAILED(hr = vkd3d_meta_create_swapchain_pipeline(meta_ops, key, pipeline))) { pthread_mutex_unlock(&meta_swapchain_ops->mutex); return hr; } info->vk_pipeline = pipeline->vk_pipeline; pthread_mutex_unlock(&meta_swapchain_ops->mutex); return S_OK; } HRESULT vkd3d_query_ops_init(struct vkd3d_query_ops *meta_query_ops, struct d3d12_device *device) { VkPushConstantRange push_constant_range; VkSpecializationInfo spec_info; uint32_t field_count; VkResult vr; static const VkDescriptorSetLayoutBinding gather_bindings[] = { { 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT }, { 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT }, { 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT }, }; static const VkDescriptorSetLayoutBinding resolve_bindings[] = { { 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT }, { 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT }, }; static const VkSpecializationMapEntry spec_map = { 0, 0, sizeof(uint32_t) }; if ((vr = vkd3d_meta_create_descriptor_set_layout(device, ARRAY_SIZE(gather_bindings), gather_bindings, &meta_query_ops->vk_gather_set_layout)) < 0) goto fail; push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; push_constant_range.offset = 0; push_constant_range.size = sizeof(struct vkd3d_query_gather_args); if ((vr = vkd3d_meta_create_pipeline_layout(device, 1, &meta_query_ops->vk_gather_set_layout, 1, &push_constant_range, &meta_query_ops->vk_gather_pipeline_layout)) < 0) goto fail; spec_info.mapEntryCount = 1; spec_info.pMapEntries = &spec_map; spec_info.dataSize = sizeof(field_count); spec_info.pData = &field_count; field_count = 1; if ((vr = vkd3d_meta_create_compute_pipeline(device, sizeof(cs_resolve_query), cs_resolve_query, meta_query_ops->vk_gather_pipeline_layout, &spec_info, &meta_query_ops->vk_gather_occlusion_pipeline)) < 0) goto fail; field_count = 2; if ((vr = vkd3d_meta_create_compute_pipeline(device, sizeof(cs_resolve_query), cs_resolve_query, meta_query_ops->vk_gather_pipeline_layout, &spec_info, &meta_query_ops->vk_gather_so_statistics_pipeline)) < 0) goto fail; push_constant_range.size = sizeof(struct vkd3d_query_resolve_args); if ((vr = vkd3d_meta_create_descriptor_set_layout(device, ARRAY_SIZE(resolve_bindings), resolve_bindings, &meta_query_ops->vk_resolve_set_layout)) < 0) goto fail; if ((vr = vkd3d_meta_create_pipeline_layout(device, 1, &meta_query_ops->vk_resolve_set_layout, 1, &push_constant_range, &meta_query_ops->vk_resolve_pipeline_layout)) < 0) goto fail; if ((vr = vkd3d_meta_create_compute_pipeline(device, sizeof(cs_resolve_binary_queries), cs_resolve_binary_queries, meta_query_ops->vk_resolve_pipeline_layout, NULL, &meta_query_ops->vk_resolve_binary_pipeline)) < 0) goto fail; return S_OK; fail: vkd3d_query_ops_cleanup(meta_query_ops, device); return hresult_from_vk_result(vr); } void vkd3d_query_ops_cleanup(struct vkd3d_query_ops *meta_query_ops, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VK_CALL(vkDestroyPipeline(device->vk_device, meta_query_ops->vk_gather_occlusion_pipeline, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, meta_query_ops->vk_gather_so_statistics_pipeline, NULL)); VK_CALL(vkDestroyPipelineLayout(device->vk_device, meta_query_ops->vk_gather_pipeline_layout, NULL)); VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, meta_query_ops->vk_gather_set_layout, NULL)); VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, meta_query_ops->vk_resolve_set_layout, NULL)); VK_CALL(vkDestroyPipelineLayout(device->vk_device, meta_query_ops->vk_resolve_pipeline_layout, NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, meta_query_ops->vk_resolve_binary_pipeline, NULL)); } bool vkd3d_meta_get_query_gather_pipeline(struct vkd3d_meta_ops *meta_ops, D3D12_QUERY_HEAP_TYPE heap_type, struct vkd3d_query_gather_info *info) { const struct vkd3d_query_ops *query_ops = &meta_ops->query; info->vk_set_layout = query_ops->vk_gather_set_layout; info->vk_pipeline_layout = query_ops->vk_gather_pipeline_layout; switch (heap_type) { case D3D12_QUERY_HEAP_TYPE_OCCLUSION: info->vk_pipeline = query_ops->vk_gather_occlusion_pipeline; return true; case D3D12_QUERY_HEAP_TYPE_SO_STATISTICS: info->vk_pipeline = query_ops->vk_gather_so_statistics_pipeline; return true; default: ERR("No pipeline for query heap type %u.\n", heap_type); return false; } } HRESULT vkd3d_predicate_ops_init(struct vkd3d_predicate_ops *meta_predicate_ops, struct d3d12_device *device) { VkPushConstantRange push_constant_range; VkSpecializationInfo spec_info; VkResult vr; size_t i; static const struct spec_data { uint32_t arg_count; VkBool32 arg_indirect; } spec_data[] = { { 4, VK_FALSE }, /* VKD3D_PREDICATE_OP_DRAW */ { 5, VK_FALSE }, /* VKD3D_PREDICATE_OP_DRAW_INDEXED */ { 1, VK_FALSE }, /* VKD3D_PREDICATE_OP_DRAW_INDIRECT */ { 1, VK_TRUE }, /* VKD3D_PREDICATE_OP_DRAW_INDIRECT_COUNT */ { 3, VK_FALSE }, /* VKD3D_PREDICATE_OP_DISPATCH */ { 3, VK_TRUE }, /* VKD3D_PREDICATE_OP_DISPATCH_INDIRECT */ }; static const VkSpecializationMapEntry spec_map[] = { { 0, offsetof(struct spec_data, arg_count), sizeof(uint32_t) }, { 1, offsetof(struct spec_data, arg_indirect), sizeof(VkBool32) }, }; memset(meta_predicate_ops, 0, sizeof(*meta_predicate_ops)); push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; push_constant_range.offset = 0; push_constant_range.size = sizeof(struct vkd3d_predicate_command_args); if ((vr = vkd3d_meta_create_pipeline_layout(device, 0, NULL, 1, &push_constant_range, &meta_predicate_ops->vk_command_pipeline_layout)) < 0) return hresult_from_vk_result(vr); push_constant_range.size = sizeof(struct vkd3d_predicate_resolve_args); if ((vr = vkd3d_meta_create_pipeline_layout(device, 0, NULL, 1, &push_constant_range, &meta_predicate_ops->vk_resolve_pipeline_layout)) < 0) return hresult_from_vk_result(vr); spec_info.mapEntryCount = ARRAY_SIZE(spec_map); spec_info.pMapEntries = spec_map; spec_info.dataSize = sizeof(struct spec_data); for (i = 0; i < ARRAY_SIZE(spec_data); i++) { spec_info.pData = &spec_data[i]; if ((vr = vkd3d_meta_create_compute_pipeline(device, sizeof(cs_predicate_command), cs_predicate_command, meta_predicate_ops->vk_command_pipeline_layout, &spec_info, &meta_predicate_ops->vk_command_pipelines[i])) < 0) goto fail; meta_predicate_ops->data_sizes[i] = spec_data[i].arg_count * sizeof(uint32_t); } if ((vr = vkd3d_meta_create_compute_pipeline(device, sizeof(cs_resolve_predicate), cs_resolve_predicate, meta_predicate_ops->vk_resolve_pipeline_layout, &spec_info, &meta_predicate_ops->vk_resolve_pipeline)) < 0) goto fail; return S_OK; fail: vkd3d_predicate_ops_cleanup(meta_predicate_ops, device); return hresult_from_vk_result(vr); } void vkd3d_predicate_ops_cleanup(struct vkd3d_predicate_ops *meta_predicate_ops, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; size_t i; for (i = 0; i < VKD3D_PREDICATE_COMMAND_COUNT; i++) VK_CALL(vkDestroyPipeline(device->vk_device, meta_predicate_ops->vk_command_pipelines[i], NULL)); VK_CALL(vkDestroyPipeline(device->vk_device, meta_predicate_ops->vk_resolve_pipeline, NULL)); VK_CALL(vkDestroyPipelineLayout(device->vk_device, meta_predicate_ops->vk_command_pipeline_layout, NULL)); VK_CALL(vkDestroyPipelineLayout(device->vk_device, meta_predicate_ops->vk_resolve_pipeline_layout, NULL)); } void vkd3d_meta_get_predicate_pipeline(struct vkd3d_meta_ops *meta_ops, enum vkd3d_predicate_command_type command_type, struct vkd3d_predicate_command_info *info) { const struct vkd3d_predicate_ops *predicate_ops = &meta_ops->predicate; info->vk_pipeline_layout = predicate_ops->vk_command_pipeline_layout; info->vk_pipeline = predicate_ops->vk_command_pipelines[command_type]; info->data_size = predicate_ops->data_sizes[command_type]; } HRESULT vkd3d_execute_indirect_ops_init(struct vkd3d_execute_indirect_ops *meta_indirect_ops, struct d3d12_device *device) { VkPushConstantRange push_constant_range; VkResult vr; int rc; if ((rc = pthread_mutex_init(&meta_indirect_ops->mutex, NULL))) return hresult_from_errno(rc); push_constant_range.offset = 0; push_constant_range.size = sizeof(struct vkd3d_execute_indirect_args); push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; if ((vr = vkd3d_meta_create_pipeline_layout(device, 0, NULL, 1, &push_constant_range, &meta_indirect_ops->vk_pipeline_layout)) < 0) { pthread_mutex_destroy(&meta_indirect_ops->mutex); return hresult_from_vk_result(vr); } meta_indirect_ops->pipelines_count = 0; meta_indirect_ops->pipelines_size = 0; meta_indirect_ops->pipelines = NULL; return S_OK; } struct vkd3d_meta_execute_indirect_spec_constant_data { struct vkd3d_shader_debug_ring_spec_constants constants; uint32_t workgroup_size_x; }; HRESULT vkd3d_meta_get_execute_indirect_pipeline(struct vkd3d_meta_ops *meta_ops, uint32_t patch_command_count, struct vkd3d_execute_indirect_info *info) { struct vkd3d_meta_execute_indirect_spec_constant_data execute_indirect_spec_constants; VkSpecializationMapEntry map_entry[VKD3D_SHADER_DEBUG_RING_SPEC_INFO_MAP_ENTRIES + 1]; struct vkd3d_execute_indirect_ops *meta_indirect_ops = &meta_ops->execute_indirect; struct vkd3d_shader_debug_ring_spec_info debug_ring_info; VkSpecializationInfo spec; HRESULT hr = S_OK; VkResult vr; bool debug; size_t i; int rc; if ((rc = pthread_mutex_lock(&meta_indirect_ops->mutex))) { ERR("Failed to lock mutex, error %d.\n", rc); return hresult_from_errno(rc); } for (i = 0; i < meta_indirect_ops->pipelines_count; i++) { if (meta_indirect_ops->pipelines[i].workgroup_size_x == patch_command_count) { info->vk_pipeline_layout = meta_indirect_ops->vk_pipeline_layout; info->vk_pipeline = meta_indirect_ops->pipelines[i].vk_pipeline; goto out; } } debug = meta_ops->device->debug_ring.active; /* If we have debug ring, we can dump indirect command buffer data to the ring as well. * Vital for debugging broken execute indirect data with templates. */ if (debug) { vkd3d_shader_debug_ring_init_spec_constant(meta_ops->device, &debug_ring_info, 0 /* Reserve this hash for internal debug streams. */); memset(&execute_indirect_spec_constants, 0, sizeof(execute_indirect_spec_constants)); execute_indirect_spec_constants.constants = debug_ring_info.constants; execute_indirect_spec_constants.workgroup_size_x = patch_command_count; memcpy(map_entry, debug_ring_info.map_entries, sizeof(debug_ring_info.map_entries)); map_entry[VKD3D_SHADER_DEBUG_RING_SPEC_INFO_MAP_ENTRIES].constantID = 4; map_entry[VKD3D_SHADER_DEBUG_RING_SPEC_INFO_MAP_ENTRIES].offset = offsetof(struct vkd3d_meta_execute_indirect_spec_constant_data, workgroup_size_x); map_entry[VKD3D_SHADER_DEBUG_RING_SPEC_INFO_MAP_ENTRIES].size = sizeof(patch_command_count); spec.pMapEntries = map_entry; spec.pData = &execute_indirect_spec_constants; spec.mapEntryCount = ARRAY_SIZE(map_entry); spec.dataSize = sizeof(execute_indirect_spec_constants); } else { map_entry[0].constantID = 0; map_entry[0].offset = 0; map_entry[0].size = sizeof(patch_command_count); spec.pMapEntries = map_entry; spec.pData = &patch_command_count; spec.mapEntryCount = 1; spec.dataSize = sizeof(patch_command_count); } vkd3d_array_reserve((void**)&meta_indirect_ops->pipelines, &meta_indirect_ops->pipelines_size, meta_indirect_ops->pipelines_count + 1, sizeof(*meta_indirect_ops->pipelines)); meta_indirect_ops->pipelines[meta_indirect_ops->pipelines_count].workgroup_size_x = patch_command_count; vr = vkd3d_meta_create_compute_pipeline(meta_ops->device, debug ? sizeof(cs_execute_indirect_patch_debug_ring) : sizeof(cs_execute_indirect_patch), debug ? cs_execute_indirect_patch_debug_ring : cs_execute_indirect_patch, meta_indirect_ops->vk_pipeline_layout, &spec, &meta_indirect_ops->pipelines[meta_indirect_ops->pipelines_count].vk_pipeline); if (vr) { hr = hresult_from_vk_result(vr); goto out; } info->vk_pipeline_layout = meta_indirect_ops->vk_pipeline_layout; info->vk_pipeline = meta_indirect_ops->pipelines[meta_indirect_ops->pipelines_count].vk_pipeline; meta_indirect_ops->pipelines_count++; out: pthread_mutex_unlock(&meta_indirect_ops->mutex); return hr; } void vkd3d_execute_indirect_ops_cleanup(struct vkd3d_execute_indirect_ops *meta_indirect_ops, struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; size_t i; for (i = 0; i < meta_indirect_ops->pipelines_count; i++) VK_CALL(vkDestroyPipeline(device->vk_device, meta_indirect_ops->pipelines[i].vk_pipeline, NULL)); VK_CALL(vkDestroyPipelineLayout(device->vk_device, meta_indirect_ops->vk_pipeline_layout, NULL)); pthread_mutex_destroy(&meta_indirect_ops->mutex); } HRESULT vkd3d_meta_ops_init(struct vkd3d_meta_ops *meta_ops, struct d3d12_device *device) { HRESULT hr; memset(meta_ops, 0, sizeof(*meta_ops)); meta_ops->device = device; if (FAILED(hr = vkd3d_meta_ops_common_init(&meta_ops->common, device))) goto fail_common; if (FAILED(hr = vkd3d_clear_uav_ops_init(&meta_ops->clear_uav, device))) goto fail_clear_uav_ops; if (FAILED(hr = vkd3d_copy_image_ops_init(&meta_ops->copy_image, device))) goto fail_copy_image_ops; if (FAILED(hr = vkd3d_swapchain_ops_init(&meta_ops->swapchain, device))) goto fail_swapchain_ops; if (FAILED(hr = vkd3d_query_ops_init(&meta_ops->query, device))) goto fail_query_ops; if (FAILED(hr = vkd3d_predicate_ops_init(&meta_ops->predicate, device))) goto fail_predicate_ops; if (FAILED(hr = vkd3d_execute_indirect_ops_init(&meta_ops->execute_indirect, device))) goto fail_execute_indirect_ops; return S_OK; fail_execute_indirect_ops: vkd3d_predicate_ops_cleanup(&meta_ops->predicate, device); fail_predicate_ops: vkd3d_query_ops_cleanup(&meta_ops->query, device); fail_query_ops: vkd3d_swapchain_ops_cleanup(&meta_ops->swapchain, device); fail_swapchain_ops: vkd3d_copy_image_ops_cleanup(&meta_ops->copy_image, device); fail_copy_image_ops: vkd3d_clear_uav_ops_cleanup(&meta_ops->clear_uav, device); fail_clear_uav_ops: vkd3d_meta_ops_common_cleanup(&meta_ops->common, device); fail_common: return hr; } HRESULT vkd3d_meta_ops_cleanup(struct vkd3d_meta_ops *meta_ops, struct d3d12_device *device) { vkd3d_execute_indirect_ops_cleanup(&meta_ops->execute_indirect, device); vkd3d_predicate_ops_cleanup(&meta_ops->predicate, device); vkd3d_query_ops_cleanup(&meta_ops->query, device); vkd3d_swapchain_ops_cleanup(&meta_ops->swapchain, device); vkd3d_copy_image_ops_cleanup(&meta_ops->copy_image, device); vkd3d_clear_uav_ops_cleanup(&meta_ops->clear_uav, device); vkd3d_meta_ops_common_cleanup(&meta_ops->common, device); return S_OK; }