libs/vkd3d: Allocate GPU virtual addresses for resources.

Direct3D 12 resources, buffers in particular, are bound to the pipeline
by their GPU virtual address. In Vulkan, these addresses are not visible
to the application. We previously handled this by returning the VkBuffer
handle as virtual address, but this can't work when the application
binds anything other than the resource's base GPU VA. Instead, we
allocate ranges of GPU address space and associate resources with it.

This uses the (naive) approach of simply allocating subsequent ranges,
and never reclaiming them. Eventually we'll have to revisit that.
This commit is contained in:
Henri Verbeet 2017-07-25 01:56:16 +02:00
parent 1acd3d44b1
commit 605a02274e
4 changed files with 124 additions and 13 deletions

View File

@ -2379,14 +2379,16 @@ static void STDMETHODCALLTYPE d3d12_command_list_SetGraphicsRootConstantBufferVi
const struct vkd3d_vk_device_procs *vk_procs;
struct VkWriteDescriptorSet descriptor_write;
struct VkDescriptorBufferInfo buffer_info;
struct d3d12_resource *resource;
TRACE("iface %p, root_parameter_index %u, address %#"PRIx64".\n",
iface, root_parameter_index, address);
vk_procs = &list->device->vk_procs;
buffer_info.buffer = (VkBuffer)address;
buffer_info.offset = 0;
resource = vkd3d_gpu_va_allocator_dereference(&list->device->gpu_va_allocator, address);
buffer_info.buffer = resource->u.vk_buffer;
buffer_info.offset = address - resource->gpu_address;
buffer_info.range = VK_WHOLE_SIZE;
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
@ -2435,6 +2437,7 @@ static void STDMETHODCALLTYPE d3d12_command_list_IASetIndexBuffer(ID3D12Graphics
{
struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList(iface);
const struct vkd3d_vk_device_procs *vk_procs;
struct d3d12_resource *resource;
enum VkIndexType index_type;
TRACE("iface %p, view %p.\n", iface, view);
@ -2454,7 +2457,9 @@ static void STDMETHODCALLTYPE d3d12_command_list_IASetIndexBuffer(ID3D12Graphics
return;
}
VK_CALL(vkCmdBindIndexBuffer(list->vk_command_buffer, (VkBuffer)view->BufferLocation, 0, index_type));
resource = vkd3d_gpu_va_allocator_dereference(&list->device->gpu_va_allocator, view->BufferLocation);
VK_CALL(vkCmdBindIndexBuffer(list->vk_command_buffer, resource->u.vk_buffer,
view->BufferLocation - resource->gpu_address, index_type));
}
static void STDMETHODCALLTYPE d3d12_command_list_IASetVertexBuffers(ID3D12GraphicsCommandList *iface,
@ -2464,6 +2469,7 @@ static void STDMETHODCALLTYPE d3d12_command_list_IASetVertexBuffers(ID3D12Graphi
VkDeviceSize offsets[ARRAY_SIZE(list->strides)];
const struct vkd3d_vk_device_procs *vk_procs;
VkBuffer buffers[ARRAY_SIZE(list->strides)];
struct d3d12_resource *resource;
unsigned int i;
TRACE("iface %p, start_slot %u, view_count %u, views %p.\n", iface, start_slot, view_count, views);
@ -2478,8 +2484,9 @@ static void STDMETHODCALLTYPE d3d12_command_list_IASetVertexBuffers(ID3D12Graphi
for (i = 0; i < view_count; ++i)
{
offsets[i] = 0;
buffers[i] = (VkBuffer)views[i].BufferLocation;
resource = vkd3d_gpu_va_allocator_dereference(&list->device->gpu_va_allocator, views[i].BufferLocation);
offsets[i] = views[i].BufferLocation - resource->gpu_address;
buffers[i] = resource->u.vk_buffer;
list->strides[start_slot + i] = views[i].StrideInBytes;
}

View File

@ -480,6 +480,75 @@ static HRESULT vkd3d_create_vk_device(struct d3d12_device *device)
return S_OK;
}
D3D12_GPU_VIRTUAL_ADDRESS vkd3d_gpu_va_allocator_allocate(struct vkd3d_gpu_va_allocator *allocator,
size_t size, void *ptr)
{
D3D12_GPU_VIRTUAL_ADDRESS ceiling = ~(D3D12_GPU_VIRTUAL_ADDRESS)0;
struct vkd3d_gpu_va_allocation *allocation;
if (!vkd3d_array_reserve((void **)&allocator->allocations, &allocator->allocations_size,
allocator->allocation_count + 1, sizeof(*allocator->allocations)))
return 0;
if (size > ceiling || ceiling - size < allocator->floor)
return 0;
allocation = &allocator->allocations[allocator->allocation_count++];
allocation->base = allocator->floor;
allocation->size = size;
allocation->ptr = ptr;
allocator->floor += size;
return allocation->base;
}
static int vkd3d_gpu_va_allocation_compare(const void *k, const void *e)
{
const struct vkd3d_gpu_va_allocation *allocation = e;
const D3D12_GPU_VIRTUAL_ADDRESS *address = k;
if (*address < allocation->base)
return -1;
if (*address - allocation->base >= allocation->size)
return 1;
return 0;
}
void *vkd3d_gpu_va_allocator_dereference(struct vkd3d_gpu_va_allocator *allocator, D3D12_GPU_VIRTUAL_ADDRESS address)
{
struct vkd3d_gpu_va_allocation *allocation;
if (!(allocation = bsearch(&address, allocator->allocations, allocator->allocation_count,
sizeof(*allocation), vkd3d_gpu_va_allocation_compare)))
return NULL;
return allocation->ptr;
}
void vkd3d_gpu_va_allocator_free(struct vkd3d_gpu_va_allocator *allocator, D3D12_GPU_VIRTUAL_ADDRESS address)
{
struct vkd3d_gpu_va_allocation *allocation;
if (!(allocation = bsearch(&address, allocator->allocations, allocator->allocation_count,
sizeof(*allocation), vkd3d_gpu_va_allocation_compare)))
return;
if (allocation->base == address)
allocation->ptr = NULL;
}
void vkd3d_gpu_va_allocator_init(struct vkd3d_gpu_va_allocator *allocator)
{
memset(allocator, 0, sizeof(*allocator));
allocator->floor = 0x1000;
}
void vkd3d_gpu_va_allocator_cleanup(struct vkd3d_gpu_va_allocator *allocator)
{
vkd3d_free(allocator->allocations);
}
/* ID3D12Device */
static inline struct d3d12_device *impl_from_ID3D12Device(ID3D12Device *iface)
{
@ -527,6 +596,7 @@ static ULONG STDMETHODCALLTYPE d3d12_device_Release(ID3D12Device *iface)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
vkd3d_gpu_va_allocator_cleanup(&device->gpu_va_allocator);
vkd3d_fence_worker_stop(&device->fence_worker);
VK_CALL(vkDestroyDevice(device->vk_device, NULL));
vkd3d_instance_destroy(&device->vkd3d_instance);
@ -1270,6 +1340,8 @@ static HRESULT d3d12_device_init(struct d3d12_device *device,
return hr;
}
vkd3d_gpu_va_allocator_init(&device->gpu_va_allocator);
return S_OK;
}

View File

@ -276,10 +276,20 @@ static HRESULT vkd3d_allocate_buffer_memory(struct d3d12_resource *resource, str
if (FAILED(hr = vkd3d_allocate_device_memory(device, heap_properties, heap_flags,
&memory_requirements, &resource->vk_memory)))
return hr;
if (!(resource->gpu_address = vkd3d_gpu_va_allocator_allocate(&device->gpu_va_allocator,
memory_requirements.size, resource)))
{
ERR("Failed to allocate GPU VA.\n");
VK_CALL(vkFreeMemory(device->vk_device, resource->vk_memory, NULL));
resource->vk_memory = VK_NULL_HANDLE;
return E_OUTOFMEMORY;
}
if ((vr = VK_CALL(vkBindBufferMemory(device->vk_device, resource->u.vk_buffer, resource->vk_memory, 0))) < 0)
{
WARN("Failed to bind memory, vr %d.\n", vr);
vkd3d_gpu_va_allocator_free(&device->gpu_va_allocator, resource->gpu_address);
resource->gpu_address = 0;
VK_CALL(vkFreeMemory(device->vk_device, resource->vk_memory, NULL));
resource->vk_memory = VK_NULL_HANDLE;
return hresult_from_vk_result(vr);
@ -322,6 +332,9 @@ static void d3d12_resource_destroy(struct d3d12_resource *resource, struct d3d12
if (resource->flags & VKD3D_RESOURCE_EXTERNAL)
return;
if (resource->gpu_address)
vkd3d_gpu_va_allocator_free(&device->gpu_va_allocator, resource->gpu_address);
if (d3d12_resource_is_buffer(resource))
VK_CALL(vkDestroyBuffer(device->vk_device, resource->u.vk_buffer, NULL));
else
@ -531,13 +544,7 @@ static D3D12_GPU_VIRTUAL_ADDRESS STDMETHODCALLTYPE d3d12_resource_GetGPUVirtualA
TRACE("iface %p.\n", iface);
if (!d3d12_resource_is_buffer(resource))
{
WARN("GPU virtual address for textures is always 0.\n");
return 0;
}
return resource->u.gpu_address;
return resource->gpu_address;
}
static HRESULT STDMETHODCALLTYPE d3d12_resource_WriteToSubresource(ID3D12Resource *iface,
@ -649,6 +656,7 @@ static HRESULT d3d12_committed_resource_init(struct d3d12_resource *resource, st
if (optimized_clear_value)
FIXME("Ignoring optimized clear value.\n");
resource->gpu_address = 0;
resource->flags = 0;
switch (desc->Dimension)

View File

@ -92,6 +92,29 @@ HRESULT vkd3d_fence_worker_start(struct vkd3d_fence_worker *worker,
struct d3d12_device *device) DECLSPEC_HIDDEN;
HRESULT vkd3d_fence_worker_stop(struct vkd3d_fence_worker *worker) DECLSPEC_HIDDEN;
struct vkd3d_gpu_va_allocator
{
D3D12_GPU_VIRTUAL_ADDRESS floor;
struct vkd3d_gpu_va_allocation
{
D3D12_GPU_VIRTUAL_ADDRESS base;
SIZE_T size;
void *ptr;
} *allocations;
size_t allocations_size;
size_t allocation_count;
};
D3D12_GPU_VIRTUAL_ADDRESS vkd3d_gpu_va_allocator_allocate(struct vkd3d_gpu_va_allocator *allocator,
size_t size, void *ptr) DECLSPEC_HIDDEN;
void vkd3d_gpu_va_allocator_cleanup(struct vkd3d_gpu_va_allocator *allocator) DECLSPEC_HIDDEN;
void *vkd3d_gpu_va_allocator_dereference(struct vkd3d_gpu_va_allocator *allocator,
D3D12_GPU_VIRTUAL_ADDRESS address) DECLSPEC_HIDDEN;
void vkd3d_gpu_va_allocator_free(struct vkd3d_gpu_va_allocator *allocator,
D3D12_GPU_VIRTUAL_ADDRESS address) DECLSPEC_HIDDEN;
void vkd3d_gpu_va_allocator_init(struct vkd3d_gpu_va_allocator *allocator) DECLSPEC_HIDDEN;
/* ID3D12Fence */
struct d3d12_fence
{
@ -127,9 +150,9 @@ struct d3d12_resource
D3D12_RESOURCE_DESC desc;
D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
union
{
D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
VkBuffer vk_buffer;
VkImage vk_image;
} u;
@ -427,6 +450,7 @@ struct d3d12_device
struct vkd3d_vk_device_procs vk_procs;
vkd3d_signal_event_pfn signal_event;
struct vkd3d_gpu_va_allocator gpu_va_allocator;
struct vkd3d_fence_worker fence_worker;
unsigned int direct_queue_family_index;