Some hardware and driver combinations, e.g. AMD RX 580 / RADV, do not support alignments of 0x1000 and 0x10000. This causes GetResourceAllocationInfo() to return a size of ~0 for valid D3D12 alignments. Some games do not check for this. For example Hitman 2 will allocate heaps of size 0xffffffff and run out of vram. This patch also fixes RX 580 alignment test failures.
Signed-off-by: Conor McCarthy cmccarthy@codeweavers.com --- libs/vkd3d/device.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-)
diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c index 0624318..7421978 100644 --- a/libs/vkd3d/device.c +++ b/libs/vkd3d/device.c @@ -2893,6 +2893,19 @@ static void STDMETHODCALLTYPE d3d12_device_CopyDescriptorsSimple(ID3D12Device *i 1, &src_descriptor_range_offset, &descriptor_count, descriptor_heap_type); }
+static bool d3d12_validate_resource_alignment(const D3D12_RESOURCE_DESC *desc, + uint64_t requested_alignment, uint64_t vk_alignment, uint64_t estimated_size) +{ + /* We must return true if the requested alignment is one of D3D12's default alignments. Some games, + * e.g. Hitman 2, assume that these will work and don't check the returned allocation size for ~0. */ + if (requested_alignment == D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT) + return estimated_size <= D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; + + return !(desc->Alignment % vk_alignment) + || requested_alignment == D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT + || (desc->SampleDesc.Count > 1 && requested_alignment == D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT); +} + static D3D12_RESOURCE_ALLOCATION_INFO * STDMETHODCALLTYPE d3d12_device_GetResourceAllocationInfo( ID3D12Device *iface, D3D12_RESOURCE_ALLOCATION_INFO *info, UINT visible_mask, UINT count, const D3D12_RESOURCE_DESC *resource_descs) @@ -2930,6 +2943,7 @@ static D3D12_RESOURCE_ALLOCATION_INFO * STDMETHODCALLTYPE d3d12_device_GetResour
if (desc->Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) { + estimated_size = desc->Width; info->SizeInBytes = desc->Width; info->Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; } @@ -2943,21 +2957,20 @@ static D3D12_RESOURCE_ALLOCATION_INFO * STDMETHODCALLTYPE d3d12_device_GetResour
info->Alignment = max(info->Alignment, requested_alignment);
- if (info->Alignment < D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT) + if (!(format = vkd3d_format_from_d3d12_resource_desc(device, desc, 0))) { - if (!(format = vkd3d_format_from_d3d12_resource_desc(device, desc, 0))) - { - WARN("Invalid format %#x.\n", desc->Format); - goto invalid; - } - - estimated_size = desc->Width * desc->Height * desc->DepthOrArraySize * format->byte_count; - if (estimated_size > D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT) - info->Alignment = max(info->Alignment, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT); + WARN("Invalid format %#x.\n", desc->Format); + goto invalid; } + + estimated_size = desc->Width * desc->Height * desc->DepthOrArraySize * format->byte_count; + + if (info->Alignment < D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT + && estimated_size > D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT) + info->Alignment = max(info->Alignment, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT); }
- if (desc->Alignment % info->Alignment) + if (!d3d12_validate_resource_alignment(desc, requested_alignment, info->Alignment, estimated_size)) { WARN("Invalid resource alignment %#"PRIx64" (required %#"PRIx64").\n", desc->Alignment, info->Alignment);
Hitman 2 calls GetHeapProperties() for each swapchain buffer and checks if the creation node mask is 1. If not then it fails to store the resource pointers for later rendering.
Signed-off-by: Conor McCarthy cmccarthy@codeweavers.com --- libs/vkd3d/resource.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/libs/vkd3d/resource.c b/libs/vkd3d/resource.c index e93d50b..45a80af 100644 --- a/libs/vkd3d/resource.c +++ b/libs/vkd3d/resource.c @@ -1518,6 +1518,8 @@ static HRESULT STDMETHODCALLTYPE d3d12_resource_GetHeapProperties(ID3D12Resource { memset(heap_properties, 0, sizeof(*heap_properties)); heap_properties->Type = D3D12_HEAP_TYPE_DEFAULT; + heap_properties->CreationNodeMask = 1; + heap_properties->VisibleNodeMask = 1; } if (flags) *flags = D3D12_HEAP_FLAG_NONE;
On Wed, 13 Nov 2019 at 18:34, Conor McCarthy cmccarthy@codeweavers.com wrote:
Hitman 2 calls GetHeapProperties() for each swapchain buffer and checks if the creation node mask is 1. If not then it fails to store the resource pointers for later rendering.
Signed-off-by: Conor McCarthy cmccarthy@codeweavers.com
libs/vkd3d/resource.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/libs/vkd3d/resource.c b/libs/vkd3d/resource.c index e93d50b..45a80af 100644 --- a/libs/vkd3d/resource.c +++ b/libs/vkd3d/resource.c @@ -1518,6 +1518,8 @@ static HRESULT STDMETHODCALLTYPE d3d12_resource_GetHeapProperties(ID3D12Resource { memset(heap_properties, 0, sizeof(*heap_properties)); heap_properties->Type = D3D12_HEAP_TYPE_DEFAULT;
heap_properties->CreationNodeMask = 1;
heap_properties->VisibleNodeMask = 1; } if (flags) *flags = D3D12_HEAP_FLAG_NONE;
It should be trivial to write a corresponding test for this, correct?
November 16, 2019 4:47 AM, "Henri Verbeet" hverbeet@gmail.com wrote:
It should be trivial to write a corresponding test for this, correct?
They should already be in your assigned patches: https://source.winehq.org/patches/data/173675
The tests are best done in Wine, where the swapchain and buffers are created.
On Mon, 18 Nov 2019 at 07:01, Conor McCarthy cmccarthy@codeweavers.com wrote:
November 16, 2019 4:47 AM, "Henri Verbeet" hverbeet@gmail.com wrote:
It should be trivial to write a corresponding test for this, correct?
They should already be in your assigned patches: https://source.winehq.org/patches/data/173675
The tests are best done in Wine, where the swapchain and buffers are created.
That's doesn't work for the purposes of a vkd3d regression test; vkd3d can't depend on the tests for any particular external user of vkd3d being run before each commit is pushed. Having the d3d12 test is fine, but it serves a different purpose than a vkd3d test would.
On Wed, 13 Nov 2019 at 18:34, Conor McCarthy cmccarthy@codeweavers.com wrote:
Some hardware and driver combinations, e.g. AMD RX 580 / RADV, do not support alignments of 0x1000 and 0x10000. This causes GetResourceAllocationInfo() to return a size of ~0 for valid D3D12 alignments. Some games do not check for this. For example Hitman 2 will allocate heaps of size 0xffffffff and run out of vram. This patch also fixes RX 580 alignment test failures.
What are the returned and requested alignments in that case?
November 16, 2019 4:46 AM, "Henri Verbeet" hverbeet@gmail.com wrote:
On Wed, 13 Nov 2019 at 18:34, Conor McCarthy cmccarthy@codeweavers.com wrote:
Some hardware and driver combinations, e.g. AMD RX 580 / RADV, do not support alignments of 0x1000 and 0x10000. This causes GetResourceAllocationInfo() to return a size of ~0 for valid D3D12 alignments. Some games do not check for this. For example Hitman 2 will allocate heaps of size 0xffffffff and run out of vram. This patch also fixes RX 580 alignment test failures.
What are the returned and requested alignments in that case?
Requested 0x10000, returned 0x20000. No invalid heap offsets are sent to vkd3d_bind_heap_memory(), so the game uses the returned value.
On Mon, 18 Nov 2019 at 06:55, Conor McCarthy cmccarthy@codeweavers.com wrote:
November 16, 2019 4:46 AM, "Henri Verbeet" hverbeet@gmail.com wrote:
On Wed, 13 Nov 2019 at 18:34, Conor McCarthy cmccarthy@codeweavers.com wrote:
Some hardware and driver combinations, e.g. AMD RX 580 / RADV, do not support alignments of 0x1000 and 0x10000. This causes GetResourceAllocationInfo() to return a size of ~0 for valid D3D12 alignments. Some games do not check for this. For example Hitman 2 will allocate heaps of size 0xffffffff and run out of vram. This patch also fixes RX 580 alignment test failures.
What are the returned and requested alignments in that case?
Requested 0x10000, returned 0x20000. No invalid heap offsets are sent to vkd3d_bind_heap_memory(), so the game uses the returned value.
I'm probably missing something, but in that case, why would this patch be needed?
November 18, 2019 7:30 PM, "Henri Verbeet" hverbeet@gmail.com wrote:
What are the returned and requested alignments in that case?
Requested 0x10000, returned 0x20000. No invalid heap offsets are sent to vkd3d_bind_heap_memory(), so the game uses the returned value.
I'm probably missing something, but in that case, why would this patch be needed?
Hitman 2 doesn't check for the error value ~0 in SizeInBytes. Somehow it truncates it to 32 bits, then allocates heaps of size 0xffffffff until the system freezes.