For testing purposes I need a tool that will occupy some amount of VRAM, leaving a reduced available VRAM to the rest of the applications. I implemented a version that somewhat works using D3D12 API, but it has a problem of allocated resources being demoted to Shared RAM, thus leaving more available VRAM to tested application than anticipated.
Is there a way to make sure the allocation will not be demoted like this and will stay in hardware VRAM for the whole duration irrespective of the rest of applications' GPU usage?
Here is the snippet on how the allocation is implemented in my current version (uses Odin language bindings to D3D12). I use L1 memory pool preference, which is supposed to be GPU-only pool, but seems like it still can work with shared memory:
resources: [dynamic]^dx.IResource
allocated_bytes: u64 = 0
chunk_size: u64 = 64 * 1024 * 1024 // 64 MB chunks
// Allocate chunks of set size + a left-over chunk of remaining size
for allocated_bytes < target_bytes {
actual_chunk := min(chunk_size, target_bytes - allocated_bytes)
heap_props := dx.HEAP_PROPERTIES {
Type = .CUSTOM,
CPUPageProperty = .NOT_AVAILABLE,
MemoryPoolPreference = .L1,
CreationNodeMask = 1,
VisibleNodeMask = 1,
}
resource_desc := dx.RESOURCE_DESC {
Dimension = .BUFFER,
Alignment = 0,
Width = actual_chunk,
Height = 1,
DepthOrArraySize = 1,
MipLevels = 1,
Format = .UNKNOWN,
SampleDesc = { Count = 1, Quality = 0 },
Layout = .ROW_MAJOR,
Flags = {},
}
resource: ^dx.IResource
hr := ctx.device->CreateCommittedResource(
&heap_props,
{}, // heap flags
&resource_desc,
{}, // resource states
nil, // clear value
dx.IResource_UUID,
cast(^rawptr)&resource,
)
if hr < 0 {
// If we can't allocate the full chunk, try a smaller chunk
MIN_CHUNK_SIZE :: 1024 * 1024
if chunk_size > MIN_CHUNK_SIZE {
chunk_size = chunk_size / 2
continue
}
fmt.printfln("\nFailed to allocate more VRAM (error %d). VRAM might be full or fragmented.", hr)
break
}
append(&resources, resource)
allocated_bytes += actual_chunk
fmt.printf("Allocated : %.2f GB / %.2f GB\r", bytes_to_gb(allocated_bytes), bytes_to_gb(target_bytes))
}