From b211df683e0d00bd401f775e14c0f5efdbf67c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B3zef=20Kucia?= Date: Fri, 20 Oct 2017 18:27:17 +0200 Subject: [PATCH] libs/vkd3d: Implement copying between depth/stencil and color textures. This could be implemented more efficiently, but ideally we would get a Vulkan extension for copying between depth/stencil and color textures. --- libs/vkd3d/command.c | 201 +++++++++++++++++++++++++++++++++---- libs/vkd3d/resource.c | 33 +++--- libs/vkd3d/vkd3d_private.h | 17 ++++ 3 files changed, 215 insertions(+), 36 deletions(-) diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c index 7611b02a..b6fea741 100644 --- a/libs/vkd3d/command.c +++ b/libs/vkd3d/command.c @@ -676,6 +676,18 @@ static bool d3d12_command_allocator_add_buffer_view(struct d3d12_command_allocat return true; } +static bool d3d12_command_allocator_add_transfer_buffer(struct d3d12_command_allocator *allocator, + const struct vkd3d_buffer *buffer) +{ + if (!vkd3d_array_reserve((void **)&allocator->transfer_buffers, &allocator->transfer_buffers_size, + allocator->transfer_buffer_count + 1, sizeof(*allocator->transfer_buffers))) + return false; + + allocator->transfer_buffers[allocator->transfer_buffer_count++] = *buffer; + + return true; +} + static void d3d12_command_list_allocator_destroyed(struct d3d12_command_list *list) { TRACE("list %p.\n", list); @@ -684,12 +696,26 @@ static void d3d12_command_list_allocator_destroyed(struct d3d12_command_list *li list->vk_command_buffer = VK_NULL_HANDLE; } +static void vkd3d_buffer_destroy(struct vkd3d_buffer *buffer, struct d3d12_device *device) +{ + const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; + + VK_CALL(vkFreeMemory(device->vk_device, buffer->vk_memory, NULL)); + VK_CALL(vkDestroyBuffer(device->vk_device, buffer->vk_buffer, NULL)); +} + static void d3d12_command_allocator_free_resources(struct d3d12_command_allocator *allocator) { struct d3d12_device *device = allocator->device; const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; unsigned int i; + for (i = 0; i < allocator->transfer_buffer_count; ++i) + { + vkd3d_buffer_destroy(&allocator->transfer_buffers[i], device); + } + allocator->transfer_buffer_count = 0; + for (i = 0; i < allocator->buffer_view_count; ++i) { VK_CALL(vkDestroyBufferView(device->vk_device, allocator->buffer_views[i], NULL)); @@ -775,6 +801,7 @@ static ULONG STDMETHODCALLTYPE d3d12_command_allocator_Release(ID3D12CommandAllo d3d12_command_list_allocator_destroyed(allocator->current_command_list); d3d12_command_allocator_free_resources(allocator); + vkd3d_free(allocator->transfer_buffers); vkd3d_free(allocator->buffer_views); vkd3d_free(allocator->descriptor_pools); vkd3d_free(allocator->pipelines); @@ -962,6 +989,10 @@ static HRESULT d3d12_command_allocator_init(struct d3d12_command_allocator *allo allocator->buffer_views_size = 0; allocator->buffer_view_count = 0; + allocator->transfer_buffers = NULL; + allocator->transfer_buffers_size = 0; + allocator->transfer_buffer_count = 0; + allocator->command_buffers = NULL; allocator->command_buffers_size = 0; allocator->command_buffer_count = 0; @@ -2193,6 +2224,23 @@ static void STDMETHODCALLTYPE d3d12_command_list_CopyBufferRegion(ID3D12Graphics src_resource->u.vk_buffer, dst_resource->u.vk_buffer, 1, &buffer_copy)); } +static void vk_image_subresource_layers_from_d3d12(VkImageSubresourceLayers *subresource, + const struct vkd3d_format *format, unsigned int sub_resource_idx, unsigned int miplevel_count) +{ + subresource->aspectMask = format->vk_aspect_mask; + subresource->mipLevel = sub_resource_idx % miplevel_count; + subresource->baseArrayLayer = sub_resource_idx / miplevel_count; + subresource->layerCount = 1; +} + +static void vk_extent_3d_from_d3d12_miplevel(VkExtent3D *extent, + const D3D12_RESOURCE_DESC *resource_desc, unsigned int miplevel_idx) +{ + extent->width = d3d12_resource_desc_get_width(resource_desc, miplevel_idx); + extent->height = d3d12_resource_desc_get_height(resource_desc, miplevel_idx); + extent->depth = d3d12_resource_desc_get_depth(resource_desc, miplevel_idx); +} + static void vk_buffer_image_copy_from_d3d12(VkBufferImageCopy *buffer_image_copy, const D3D12_PLACED_SUBRESOURCE_FOOTPRINT *footprint, unsigned int sub_resource_idx, const D3D12_RESOURCE_DESC *image_desc, const struct vkd3d_format *format, @@ -2202,10 +2250,8 @@ static void vk_buffer_image_copy_from_d3d12(VkBufferImageCopy *buffer_image_copy buffer_image_copy->bufferRowLength = footprint->Footprint.RowPitch / (format->byte_count * format->block_byte_count) * format->block_width; buffer_image_copy->bufferImageHeight = 0; - buffer_image_copy->imageSubresource.aspectMask = format->vk_aspect_mask; - buffer_image_copy->imageSubresource.mipLevel = sub_resource_idx % image_desc->MipLevels; - buffer_image_copy->imageSubresource.baseArrayLayer = sub_resource_idx / image_desc->MipLevels; - buffer_image_copy->imageSubresource.layerCount = 1; + vk_image_subresource_layers_from_d3d12(&buffer_image_copy->imageSubresource, + format, sub_resource_idx, image_desc->MipLevels); buffer_image_copy->imageOffset.x = dst_x; buffer_image_copy->imageOffset.y = dst_y; buffer_image_copy->imageOffset.z = dst_z; @@ -2220,17 +2266,13 @@ static void vk_image_copy_from_d3d12(VkImageCopy *image_copy, const struct vkd3d_format *src_format, const struct vkd3d_format *dst_format, const D3D12_BOX *src_box, unsigned int dst_x, unsigned int dst_y, unsigned int dst_z) { - image_copy->srcSubresource.aspectMask = src_format->vk_aspect_mask; - image_copy->srcSubresource.mipLevel = src_sub_resource_idx % src_desc->MipLevels; - image_copy->srcSubresource.baseArrayLayer = src_sub_resource_idx / src_desc->MipLevels; - image_copy->srcSubresource.layerCount = 1; + vk_image_subresource_layers_from_d3d12(&image_copy->srcSubresource, + src_format, src_sub_resource_idx, src_desc->MipLevels); image_copy->srcOffset.x = src_box ? src_box->left : 0; image_copy->srcOffset.y = src_box ? src_box->top : 0; image_copy->srcOffset.z = src_box ? src_box->front : 0; - image_copy->dstSubresource.aspectMask = dst_format->vk_aspect_mask; - image_copy->dstSubresource.mipLevel = dst_sub_resource_idx % dst_desc->MipLevels; - image_copy->dstSubresource.baseArrayLayer = dst_sub_resource_idx / dst_desc->MipLevels; - image_copy->dstSubresource.layerCount = 1; + vk_image_subresource_layers_from_d3d12(&image_copy->dstSubresource, + dst_format, dst_sub_resource_idx, dst_desc->MipLevels); image_copy->dstOffset.x = dst_x; image_copy->dstOffset.y = dst_y; image_copy->dstOffset.z = dst_z; @@ -2243,12 +2285,135 @@ static void vk_image_copy_from_d3d12(VkImageCopy *image_copy, else { unsigned int miplevel = image_copy->srcSubresource.mipLevel; - image_copy->extent.width = d3d12_resource_desc_get_width(src_desc, miplevel); - image_copy->extent.height = d3d12_resource_desc_get_height(src_desc, miplevel); - image_copy->extent.depth = d3d12_resource_desc_get_depth(src_desc, miplevel); + vk_extent_3d_from_d3d12_miplevel(&image_copy->extent, src_desc, miplevel); } } +static HRESULT d3d12_command_list_allocate_transfer_buffer(struct d3d12_command_list *list, + VkDeviceSize size, struct vkd3d_buffer *buffer) +{ + struct d3d12_device *device = list->device; + const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; + D3D12_HEAP_PROPERTIES heap_properties; + D3D12_RESOURCE_DESC buffer_desc; + HRESULT hr; + + memset(&heap_properties, 0, sizeof(heap_properties)); + heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT; + + buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + buffer_desc.Alignment = 0; + buffer_desc.Width = size; + buffer_desc.Height = 1; + buffer_desc.DepthOrArraySize = 1; + buffer_desc.MipLevels = 1; + buffer_desc.Format = DXGI_FORMAT_UNKNOWN; + buffer_desc.SampleDesc.Count = 1; + buffer_desc.SampleDesc.Quality = 0; + buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + buffer_desc.Flags = D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; + + if (FAILED(hr = vkd3d_create_buffer(device, &heap_properties, D3D12_HEAP_FLAG_NONE, + &buffer_desc, &buffer->vk_buffer))) + return hr; + if (FAILED(hr = vkd3d_allocate_buffer_memory(device, buffer->vk_buffer, + &heap_properties, D3D12_HEAP_FLAG_NONE, &buffer->vk_memory))) + { + VK_CALL(vkDestroyBuffer(device->vk_device, buffer->vk_buffer, NULL)); + return hr; + } + + if (!d3d12_command_allocator_add_transfer_buffer(list->allocator, buffer)) + { + ERR("Failed to add transfer buffer.\n"); + vkd3d_buffer_destroy(buffer, device); + return E_OUTOFMEMORY; + } + + return S_OK; +} + +/* In Vulkan, each depth/stencil format is only compatible with itself. + * This means that we are not allowed to copy texture regions directly between + * depth/stencil and color formats. + */ +static void d3d12_command_list_copy_incompatible_texture_region(struct d3d12_command_list *list, + struct d3d12_resource *dst_resource, unsigned int dst_sub_resource_idx, + const struct vkd3d_format *dst_format, struct d3d12_resource *src_resource, + unsigned int src_sub_resource_idx, const struct vkd3d_format *src_format) +{ + const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs; + const D3D12_RESOURCE_DESC *dst_desc = &dst_resource->desc; + const D3D12_RESOURCE_DESC *src_desc = &src_resource->desc; + unsigned int dst_miplevel_idx, src_miplevel_idx; + struct vkd3d_buffer transfer_buffer; + VkBufferImageCopy buffer_image_copy; + VkBufferMemoryBarrier vk_barrier; + VkDeviceSize buffer_size; + HRESULT hr; + + WARN("Copying incompatible texture formats %#x, %#x -> %#x, %#x.\n", + src_format->dxgi_format, src_format->vk_format, + dst_format->dxgi_format, dst_format->vk_format); + + assert(d3d12_resource_is_texture(dst_resource)); + assert(d3d12_resource_is_texture(src_resource)); + assert(!vkd3d_format_is_compressed(dst_format)); + assert(!vkd3d_format_is_compressed(src_format)); + assert(dst_format->byte_count == src_format->byte_count); + + buffer_image_copy.bufferOffset = 0; + buffer_image_copy.bufferRowLength = 0; + buffer_image_copy.bufferImageHeight = 0; + vk_image_subresource_layers_from_d3d12(&buffer_image_copy.imageSubresource, + src_format, src_sub_resource_idx, src_desc->MipLevels); + src_miplevel_idx = buffer_image_copy.imageSubresource.mipLevel; + buffer_image_copy.imageOffset.x = 0; + buffer_image_copy.imageOffset.y = 0; + buffer_image_copy.imageOffset.z = 0; + vk_extent_3d_from_d3d12_miplevel(&buffer_image_copy.imageExtent, src_desc, src_miplevel_idx); + + buffer_size = src_format->byte_count * buffer_image_copy.imageExtent.width * + buffer_image_copy.imageExtent.height * buffer_image_copy.imageExtent.depth; + if (FAILED(hr = d3d12_command_list_allocate_transfer_buffer(list, buffer_size, &transfer_buffer))) + { + ERR("Failed to allocate transfer buffer, hr %#x.\n", hr); + return; + } + + VK_CALL(vkCmdCopyImageToBuffer(list->vk_command_buffer, + src_resource->u.vk_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + transfer_buffer.vk_buffer, 1, &buffer_image_copy)); + + vk_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + vk_barrier.pNext = NULL; + vk_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + vk_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + vk_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + vk_barrier.buffer = transfer_buffer.vk_buffer; + vk_barrier.offset = 0; + vk_barrier.size = VK_WHOLE_SIZE; + VK_CALL(vkCmdPipelineBarrier(list->vk_command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + 0, NULL, 1, &vk_barrier, 0, NULL)); + + vk_image_subresource_layers_from_d3d12(&buffer_image_copy.imageSubresource, + dst_format, dst_sub_resource_idx, dst_desc->MipLevels); + dst_miplevel_idx = buffer_image_copy.imageSubresource.mipLevel; + + assert(d3d12_resource_desc_get_width(src_desc, src_miplevel_idx) == + d3d12_resource_desc_get_width(dst_desc, dst_miplevel_idx)); + assert(d3d12_resource_desc_get_height(src_desc, src_miplevel_idx) == + d3d12_resource_desc_get_height(dst_desc, dst_miplevel_idx)); + assert(d3d12_resource_desc_get_depth(src_desc, src_miplevel_idx) == + d3d12_resource_desc_get_depth(dst_desc, dst_miplevel_idx)); + + VK_CALL(vkCmdCopyBufferToImage(list->vk_command_buffer, + transfer_buffer.vk_buffer, dst_resource->u.vk_image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_image_copy)); +} + static void STDMETHODCALLTYPE d3d12_command_list_CopyTextureRegion(ID3D12GraphicsCommandList *iface, const D3D12_TEXTURE_COPY_LOCATION *dst, UINT dst_x, UINT dst_y, UINT dst_z, const D3D12_TEXTURE_COPY_LOCATION *src, const D3D12_BOX *src_box) @@ -2343,9 +2508,9 @@ static void STDMETHODCALLTYPE d3d12_command_list_CopyTextureRegion(ID3D12Graphic if (dst_format->vk_aspect_mask != src_format->vk_aspect_mask) { - FIXME("Formats %#x, %#x -> %#x, %#x not supported yet.\n", - src_format->dxgi_format, src_format->vk_format, - dst_format->dxgi_format, dst_format->vk_format); + d3d12_command_list_copy_incompatible_texture_region(list, + dst_resource, dst->u.SubresourceIndex, dst_format, + src_resource, src->u.SubresourceIndex, src_format); return; } diff --git a/libs/vkd3d/resource.c b/libs/vkd3d/resource.c index 57ed4e68..780b358f 100644 --- a/libs/vkd3d/resource.c +++ b/libs/vkd3d/resource.c @@ -58,9 +58,9 @@ static VkSampleCountFlagBits vk_samples_from_dxgi_sample_desc(const DXGI_SAMPLE_ } } -static HRESULT vkd3d_create_buffer(struct d3d12_resource *resource, struct d3d12_device *device, +HRESULT vkd3d_create_buffer(struct d3d12_device *device, const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags, - const D3D12_RESOURCE_DESC *desc) + const D3D12_RESOURCE_DESC *desc, VkBuffer *vk_buffer) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VkBufferCreateInfo buffer_info; @@ -93,14 +93,12 @@ static HRESULT vkd3d_create_buffer(struct d3d12_resource *resource, struct d3d12 buffer_info.queueFamilyIndexCount = 0; buffer_info.pQueueFamilyIndices = 0; - if ((vr = VK_CALL(vkCreateBuffer(device->vk_device, &buffer_info, NULL, &resource->u.vk_buffer))) < 0) + if ((vr = VK_CALL(vkCreateBuffer(device->vk_device, &buffer_info, NULL, vk_buffer))) < 0) { WARN("Failed to create Vulkan buffer, vr %d.\n", vr); return hresult_from_vk_result(vr); } - TRACE("Created Vulkan buffer for resource %p.\n", resource); - return S_OK; } @@ -186,8 +184,6 @@ static HRESULT vkd3d_create_image(struct d3d12_resource *resource, struct d3d12_ return hresult_from_vk_result(vr); } - TRACE("Created Vulkan image for resource %p.\n", resource); - return S_OK; } @@ -263,26 +259,25 @@ static HRESULT vkd3d_allocate_device_memory(struct d3d12_device *device, return S_OK; } -static HRESULT vkd3d_allocate_buffer_memory(struct d3d12_resource *resource, struct d3d12_device *device, - const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags) +HRESULT vkd3d_allocate_buffer_memory(struct d3d12_device *device, VkBuffer vk_buffer, + const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags, + VkDeviceMemory *vk_memory) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; VkMemoryRequirements memory_requirements; VkResult vr; HRESULT hr; - assert(d3d12_resource_is_buffer(resource)); - - VK_CALL(vkGetBufferMemoryRequirements(device->vk_device, resource->u.vk_buffer, &memory_requirements)); + VK_CALL(vkGetBufferMemoryRequirements(device->vk_device, vk_buffer, &memory_requirements)); if (FAILED(hr = vkd3d_allocate_device_memory(device, heap_properties, heap_flags, - &memory_requirements, &resource->vk_memory))) + &memory_requirements, vk_memory))) return hr; - if ((vr = VK_CALL(vkBindBufferMemory(device->vk_device, resource->u.vk_buffer, resource->vk_memory, 0))) < 0) + if ((vr = VK_CALL(vkBindBufferMemory(device->vk_device, vk_buffer, *vk_memory, 0))) < 0) { WARN("Failed to bind memory, vr %d.\n", vr); - VK_CALL(vkFreeMemory(device->vk_device, resource->vk_memory, NULL)); - resource->vk_memory = VK_NULL_HANDLE; + VK_CALL(vkFreeMemory(device->vk_device, *vk_memory, NULL)); + *vk_memory = VK_NULL_HANDLE; return hresult_from_vk_result(vr); } @@ -663,7 +658,8 @@ static HRESULT d3d12_committed_resource_init(struct d3d12_resource *resource, st switch (desc->Dimension) { case D3D12_RESOURCE_DIMENSION_BUFFER: - if (FAILED(hr = vkd3d_create_buffer(resource, device, heap_properties, heap_flags, desc))) + if (FAILED(hr = vkd3d_create_buffer(device, heap_properties, heap_flags, desc, + &resource->u.vk_buffer))) return hr; if (!(resource->gpu_address = vkd3d_gpu_va_allocator_allocate(&device->gpu_va_allocator, desc->Width, resource))) @@ -672,7 +668,8 @@ static HRESULT d3d12_committed_resource_init(struct d3d12_resource *resource, st d3d12_resource_destroy(resource, device); return E_OUTOFMEMORY; } - if (FAILED(hr = vkd3d_allocate_buffer_memory(resource, device, heap_properties, heap_flags))) + if (FAILED(hr = vkd3d_allocate_buffer_memory(device, resource->u.vk_buffer, + heap_properties, heap_flags, &resource->vk_memory))) { d3d12_resource_destroy(resource, device); return hr; diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h index 1988bbd9..f9c74491 100644 --- a/libs/vkd3d/vkd3d_private.h +++ b/libs/vkd3d/vkd3d_private.h @@ -454,6 +454,12 @@ HRESULT d3d12_pipeline_state_create_graphics(struct d3d12_device *device, const D3D12_GRAPHICS_PIPELINE_STATE_DESC *desc, struct d3d12_pipeline_state **state) DECLSPEC_HIDDEN; struct d3d12_pipeline_state *unsafe_impl_from_ID3D12PipelineState(ID3D12PipelineState *iface) DECLSPEC_HIDDEN; +struct vkd3d_buffer +{ + VkBuffer vk_buffer; + VkDeviceMemory vk_memory; +}; + /* ID3D12CommandAllocator */ struct d3d12_command_allocator { @@ -484,6 +490,10 @@ struct d3d12_command_allocator size_t buffer_views_size; size_t buffer_view_count; + struct vkd3d_buffer *transfer_buffers; + size_t transfer_buffers_size; + size_t transfer_buffer_count; + VkCommandBuffer *command_buffers; size_t command_buffers_size; size_t command_buffer_count; @@ -624,6 +634,13 @@ HRESULT d3d12_device_create(const struct vkd3d_device_create_info *create_info, struct d3d12_device **device) DECLSPEC_HIDDEN; struct d3d12_device *unsafe_impl_from_ID3D12Device(ID3D12Device *iface) DECLSPEC_HIDDEN; +HRESULT vkd3d_create_buffer(struct d3d12_device *device, + const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags, + const D3D12_RESOURCE_DESC *desc, VkBuffer *vk_buffer) DECLSPEC_HIDDEN; +HRESULT vkd3d_allocate_buffer_memory(struct d3d12_device *device, VkBuffer vk_buffer, + const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags, + VkDeviceMemory *vk_memory) DECLSPEC_HIDDEN; + /* utils */ struct vkd3d_format {