unit vMemory; {$mode objfpc}{$H+} interface uses sysutils, mqueue, vmparam, vm_mmap, Vulkan, vDevice, vDependence, atomic, kern_hazard_pointer; type TvMemInfo=bitpacked record heap_index :0..VK_MAX_MEMORY_HEAPS-1; mem_type :0..VK_MAX_MEMORY_TYPES-1; device_local :Boolean; device_coherent:Boolean; host_visible :Boolean; host_coherent :Boolean; end; pp_gpu_map_entry=^p_gpu_map_entry; p_gpu_map_entry=^gpu_map_entry; gpu_map_entry=packed record prev :p_gpu_map_entry; // previous entry next :p_gpu_map_entry; // next entry left :p_gpu_map_entry; // left child in binary search tree right :p_gpu_map_entry; // right child in binary search tree start :TVkDeviceSize; // start address __end :TVkDeviceSize; // end address adj_free:TVkDeviceSize; // amount of adjacent free space max_free:TVkDeviceSize; // max free space in subtree desc :Pointer; end; p_gpu_map=^gpu_map; gpu_map=object header :gpu_map_entry; // List of entries root :p_gpu_map_entry; // Root of a binary search tree size :TVkDeviceSize; // virtual size nentries:Integer; // Number of entries property min_offset:TVkDeviceSize read header.start write header.start; property max_offset:TVkDeviceSize read header.__end write header.__end; end; TvDeviceMemory=class(TvDependenciesObject) entry :TAILQ_ENTRY; // FHandle :TVkDeviceMemory; FSize :TVkDeviceSize; FMemInfo:TvMemInfo; FMap :gpu_map; // Constructor Create(Handle:TVkDeviceMemory;Size:TVkDeviceSize;mem_type:Byte;mem_info:PVkMemoryType); Destructor Destroy; override; Procedure remove_all; Procedure Unmap; Procedure Flush; function Acquire(Sender:TObject):Boolean; override; function Release(Sender:TObject):Boolean; override; end; TvHostMemory=class(TvDeviceMemory) FStart:QWORD; F__End:QWORD; end; TvPointer=packed object FMemory:TvDeviceMemory; FOffset:TVkDeviceSize; function Acquire:TvPointer; function Release:Boolean; function Hold:Boolean; function Drop:Boolean; end; Const GRANULAR_DEV_BLOCK_SIZE=128*1024*1024; GRANULAR_MAP_BLOCK_SIZE= 16*1024*1024; type PvHeap=^TvHeap; TvHeap=bitpacked record heap_size :TVkDeviceSize; heap_index :0..VK_MAX_MEMORY_HEAPS-1; def_mem_type :0..VK_MAX_MEMORY_TYPES-1; device_local :Boolean; device_coherent:Boolean; host_visible :Boolean; host_coherent :Boolean; end; const V_PROP_DEVICE_LOCAL =ord(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ); V_PROP_HOST_VISIBLE =ord(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ); V_PROP_HOST_COHERENT =ord(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ); V_PROP_HOST_CACHED =ord(VK_MEMORY_PROPERTY_HOST_CACHED_BIT ); V_PROP_DEVICE_COHERENT=ord(VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD); V_PROP_DEVICE_UNCACHED=ord(VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD); // V_PROP_BEST_FIT =$80000000; type TvMemManager=class public FProperties:TVkPhysicalDeviceMemoryProperties; FSparceMemoryTypes:TVkUInt32; FHeaps:array of TvHeap; private FDevs:TAILQ_HEAD; //TvDeviceMemory FBacked:TvDeviceMemory; FHosts:TAILQ_HEAD; //TvHostMemory FHosts_count:Integer; public Constructor Create; function findMemoryType(Filter:TVkUInt32;prop:TVkMemoryPropertyFlags;start:Integer):Integer; procedure LoadMemoryHeaps; procedure PrintMemoryHeaps; procedure PrintMemoryType(typeFilter:TVkUInt32); Function FetchMemory(const mr:TVkMemoryRequirements; prop:TVkMemoryPropertyFlags; desc:Pointer):TvPointer; Function FetchMemory(Size :TVkDeviceSize; Align :TVkDeviceSize; memoryTypeBits:TVkUInt32; desc :Pointer; best_fit :Boolean):TvPointer; Function FreeMemory(P:TvPointer):Boolean; private Function _AllocDevBlock(Size:TVkDeviceSize;mtindex:Byte):TvDeviceMemory; procedure _FreeDevBlock(node:TvDeviceMemory); Function _shrink_dev_block(max:TVkDeviceSize;heap_index:Byte):TVkDeviceSize; Function _shrink_host_map (max:TVkDeviceSize;heap_index:Byte):TVkDeviceSize; Function _shrink(max:TVkDeviceSize;mtindex:Byte;mode:Byte):TVkDeviceSize; public procedure unmap_host(start,__end:QWORD); Function FetchHostMap(Addr,Size:TVkDeviceSize;mtindex:Byte):TvPointer; Function FetchHostMap(Addr,Size:TVkDeviceSize;device_local:Boolean):TvPointer; Procedure Flush; Procedure _print_host; Procedure _print_devs; end; var MemManager:TvMemManager; const buf_ext:TVkExternalMemoryBufferCreateInfo=( sType:VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO; pNext:nil; handleTypes:ord(VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT); ); function vkAllocMemory (device:TVkDevice;Size:TVkDeviceSize;mtindex:TVkUInt32):TVkDeviceMemory; function vkAllocHostMemory (device:TVkDevice;Size:TVkDeviceSize;mtindex:TVkUInt32;addr:Pointer ):TVkDeviceMemory; function vkAllocDedicatedImage (device:TVkDevice;Size:TVkDeviceSize;mtindex:TVkUInt32;FHandle:TVkImage ):TVkDeviceMemory; function vkAllocDedicatedBuffer(device:TVkDevice;Size:TVkDeviceSize;mtindex:TVkUInt32;FHandle:TVkBuffer):TVkDeviceMemory; function GetHostMappedRequirements:TVkMemoryRequirements; function GetSparceMemoryTypes:TVkUInt32; function GetMemoryBudget(var budget:TVkPhysicalDeviceMemoryBudgetPropertiesEXT):Boolean; function MemoryBudgetCanBeAlloc(heap_id:Byte;size:TVkDeviceSize):Boolean; procedure PrintMemoryBudget; implementation uses subr_backtrace, kern_rwlock, kern_dmem; var global_mem_lock:Pointer=nil; function AlignUp(addr:PtrUInt;alignment:PtrUInt):PtrUInt; inline; var tmp:PtrUInt; begin tmp:=addr+PtrUInt(alignment-1); Result:=tmp-(tmp mod alignment); end; function AlignDw(addr:PtrUInt;alignment:PtrUInt):PtrUInt; inline; begin Result:=addr-(addr mod alignment); end; function Max(a,b:Ptruint):Ptruint; inline; begin if (a>b) then Result:=a else Result:=b; end; function Min(a,b:Ptruint):Ptruint; inline; begin if (anil),'gpu_map_entry_create: kernel resources exhausted'); Result:=new_entry; end; procedure gpu_map_entry_dispose(map:p_gpu_map;entry:p_gpu_map_entry); inline; begin FreeMem(entry); end; procedure gpu_map_entry_set_max_free(entry:p_gpu_map_entry); begin entry^.max_free:=entry^.adj_free; if (entry^.left<>nil) then if (entry^.left^.max_free>entry^.max_free) then begin entry^.max_free:=entry^.left^.max_free; end; if (entry^.right<>nil) then if (entry^.right^.max_free>entry^.max_free) then begin entry^.max_free:=entry^.right^.max_free; end; end; function gpu_map_entry_splay(addr:TVkDeviceSize;root:p_gpu_map_entry):p_gpu_map_entry; var llist,rlist:p_gpu_map_entry; ltree,rtree:p_gpu_map_entry; y :p_gpu_map_entry; begin { Special case of empty tree. } if (root=nil) then Exit(root); { * Pass One: Splay down the tree until we find addr or a nil * pointer where addr would go. llist and rlist are the two * sides in reverse order (bottom-up), with llist linked by * the right pointer and rlist linked by the left pointer in * the vm_map_entry. Wait until Pass Two to set max_free on * the two spines. } llist:=nil; rlist:=nil; repeat { root is never nil in here. } if (addrnil) then begin { Rotate right and put y on rlist. } root^.left:=y^.right; y^.right:=root; gpu_map_entry_set_max_free(root); root:=y^.left; y^.left:=rlist; rlist:=y; end else begin { Put root on rlist. } root^.left:=rlist; rlist:=root; root:=y; end; end else if (addr>=root^.__end) then begin y:=root^.right; if (y=nil) then break; if (addr>=y^.__end) and (y^.right<>nil) then begin { Rotate left and put y on llist. } root^.right:=y^.left; y^.left:=root; gpu_map_entry_set_max_free(root); root:=y^.right; y^.right:=llist; llist:=y; end else begin { Put root on llist. } root^.right:=llist; llist:=root; root:=y; end; end else begin break; end; until false; { * Pass Two: Walk back up the two spines, flip the pointers * and set max_free. The subtrees of the root go at the * bottom of llist and rlist. } ltree:=root^.left; while (llist<>nil) do begin y:=llist^.right; llist^.right:=ltree; gpu_map_entry_set_max_free(llist); ltree:=llist; llist:=y; end; rtree:=root^.right; while (rlist<>nil) do begin y:=rlist^.left; rlist^.left:=rtree; gpu_map_entry_set_max_free(rlist); rtree:=rlist; rlist:=y; end; { * Final assembly: add ltree and rtree as subtrees of root. } root^.left:=ltree; root^.right:=rtree; gpu_map_entry_set_max_free(root); Result:=(root); end; procedure gpu_map_entry_link( map :p_gpu_map; after_where:p_gpu_map_entry; entry :p_gpu_map_entry); var i:TVkDeviceSize; begin Inc(map^.nentries); entry^.prev:=after_where; entry^.next:=after_where^.next; entry^.next^.prev:=entry; after_where^.next:=entry; if (after_where<>@map^.header) then begin if (after_where<>map^.root) then begin gpu_map_entry_splay(after_where^.start, map^.root); end; entry^.right:=after_where^.right; entry^.left:=after_where; after_where^.right:=nil; after_where^.adj_free:=entry^.start - after_where^.__end; gpu_map_entry_set_max_free(after_where); end else begin entry^.right:=map^.root; entry^.left:=nil; end; if (entry^.next=@map^.header) then begin i:=map^.max_offset; end else begin i:=entry^.next^.start; end; entry^.adj_free:=i-entry^.__end; gpu_map_entry_set_max_free(entry); map^.root:=entry; end; procedure gpu_map_entry_unlink( map :p_gpu_map; entry:p_gpu_map_entry); var next,prev,root:p_gpu_map_entry; i:TVkDeviceSize; begin if (entry<>map^.root) then begin gpu_map_entry_splay(entry^.start, map^.root); end; if (entry^.left=nil) then begin root:=entry^.right; end else begin root:=gpu_map_entry_splay(entry^.start, entry^.left); root^.right:=entry^.right; if (entry^.next=@map^.header) then begin i:=map^.max_offset; end else begin i:=entry^.next^.start; end; root^.adj_free:=i-root^.__end; gpu_map_entry_set_max_free(root); end; map^.root:=root; prev:=entry^.prev; next:=entry^.next; next^.prev:=prev; prev^.next:=next; Dec(map^.nentries); end; procedure gpu_map_entry_resize_free(map:p_gpu_map;entry:p_gpu_map_entry); begin if (entry<>map^.root) then begin map^.root:=gpu_map_entry_splay(entry^.start, map^.root); end; if (entry^.next=@map^.header) then begin entry^.adj_free:=map^.max_offset-entry^.__end; end else begin entry^.adj_free:=entry^.next^.start-entry^.__end; end; gpu_map_entry_set_max_free(entry); end; function gpu_map_lookup_entry( map :p_gpu_map; address:TVkDeviceSize; entry :pp_gpu_map_entry):Boolean; var cur:p_gpu_map_entry; begin cur:=map^.root; if (cur=nil) then begin entry^:=@map^.header; end else if (address>=cur^.start) and (cur^.__end>address) then begin entry^:=cur; Exit(TRUE); end else begin cur:=gpu_map_entry_splay(address,cur); map^.root:=cur; if (address>=cur^.start) then begin entry^:=cur; if (cur^.__end>address) then begin Exit(TRUE); end; end else begin entry^:=cur^.prev; end; end; Result:=(FALSE); end; function gpu_map_adj_free(map:p_gpu_map; start:TVkDeviceSize; entry:p_gpu_map_entry):TVkDeviceSize; begin if (entry=@map^.header) then begin if (entry^.next=@map^.header) then begin Result:=entry^.adj_free - start; end else begin Result:=entry^.next^.start - start; end; end else begin Result:=entry^.adj_free - (start - entry^.__end); end; end; function gpu_map_test( map :p_gpu_map; size :PVkDeviceSize; start:TVkDeviceSize; __end:TVkDeviceSize ):Integer; var entry:p_gpu_map_entry; _size:TVkDeviceSize; begin size^:=0; if (startmap^.max_offset) or (start>=__end) then begin Exit(GPU_INVALID_ADDRESS); end; if gpu_map_lookup_entry(map,start,@entry) then begin Exit(GPU_NO_SPACE); end; if (entry^.next<>@map^.header) and (entry^.next^.start<__end) then begin Exit(GPU_NO_SPACE); end; _size:=gpu_map_adj_free(map,start,entry); if (entry^.next<>@map^.header) then if (entry^.next^.start<(start + _size)) then begin //something is wrong Exit(GPU_NO_SPACE); end; if (_size<(__end-start)) then begin Assert(false,'gpu_map_test'); end; if ((start+_size)>map^.max_offset) then begin Assert(false,'gpu_map_test'); end; size^:=_size; Result:=GPU_SUCCESS; end; function gpu_map_insert( map :p_gpu_map; start:TVkDeviceSize; __end:TVkDeviceSize; desc :Pointer ):Integer; var new_entry :p_gpu_map_entry; prev_entry:p_gpu_map_entry; temp_entry:p_gpu_map_entry; begin if (startmap^.max_offset) or (start>=__end) then begin Exit(GPU_INVALID_ADDRESS); end; if gpu_map_lookup_entry(map,start,@temp_entry) then begin Exit(GPU_NO_SPACE); end; prev_entry:=temp_entry; if (prev_entry^.next<>@map^.header) and (prev_entry^.next^.start<__end) then begin Exit(GPU_NO_SPACE); end; new_entry:=gpu_map_entry_create(map); new_entry^.start:=start; new_entry^.__end:=__end; new_entry^.desc :=desc; gpu_map_entry_link(map, prev_entry, new_entry); map^.size:=map^.size+(new_entry^.__end - new_entry^.start); Result:=GPU_SUCCESS; end; function gpu_map_findspace(map :p_gpu_map; start :TVkDeviceSize; length:TVkDeviceSize; addr :PVkDeviceSize):Integer; label _nxt; var entry:p_gpu_map_entry; st:TVkDeviceSize; begin if (startmap^.max_offset) or (start + lengthmap^.root^.__end) then begin st:=start; end else begin st:=map^.root^.__end end; if (length<=map^.root^.__end + map^.root^.adj_free - st) then begin addr^:=st; Exit(0); end; entry:=map^.root^.right; if (entry=nil) then begin Exit(1); end; if (length>entry^.max_free) then begin Exit(1); end; while (entry<>nil) do begin if (entry^.left<>nil) then begin if not (entry^.left^.max_free>=length) then goto _nxt; entry:=entry^.left; end else begin _nxt: if (entry^.adj_free>=length) then begin addr^:=entry^.__end; Exit(0); end else begin entry:=entry^.right; end; end; end; { Can't get here, so panic if we do. } Assert(false,'gpu_map_findspace: max_free corrupt'); end; type t_gpu_find_mode=Set of (fmInsert,fmBestfit); function gpu_map_find(map :p_gpu_map; addr :PVkDeviceSize; size :PVkDeviceSize; length:TVkDeviceSize; align :TVkDeviceSize; desc :Pointer; mode :t_gpu_find_mode ):Integer; var start :TVkDeviceSize; tmp :TVkDeviceSize; save_addr:TVkDeviceSize; save_size:TVkDeviceSize; begin if (addr=nil) then begin start:=0; end else begin start:=addr^; end; save_addr:=0; save_size:=0; repeat tmp:=0; if (gpu_map_findspace(map, start, length, @tmp)<>0) then begin Break; end; if ((tmp mod align)<>0) then begin tmp:=tmp - (tmp mod align); tmp:=tmp + align; end; start:=tmp; tmp:=0; Result:=gpu_map_test(map, @tmp, start, start + length); if (Result=GPU_SUCCESS) then begin if (fmBestfit in mode) then begin //save best fit if (save_size=0) or (save_size>tmp) then begin save_addr:=start; save_size:=tmp; end; end else begin //find first save_addr:=start; save_size:=tmp; Break; end; end; until (Result<>GPU_NO_SPACE); if (save_size=0) then begin Result:=GPU_NO_SPACE; end else if (fmInsert in mode) then begin Result:=gpu_map_insert(map, start, start + length, desc); end else begin Result:=GPU_SUCCESS; end; if (addr<>nil) then begin addr^:=save_addr; end; if (size<>nil) then begin size^:=save_size; end; end; procedure gpu_map_entry_delete(map:p_gpu_map;entry:p_gpu_map_entry); var size:TVkDeviceSize; begin gpu_map_entry_unlink(map, entry); size:=entry^.__end - entry^.start; map^.size:=map^.size-size; gpu_map_entry_dispose(map,entry); end; function gpu_map_delete(map:p_gpu_map;start:TVkDeviceSize):Integer; var entry:p_gpu_map_entry; begin if (not gpu_map_lookup_entry(map, start, @entry)) then begin entry:=entry^.next; end; if (entry=@map^.header) then begin Exit(GPU_INVALID_ADDRESS); end; if (entry^.start<>start) then begin Exit(GPU_INVALID_ADDRESS); end; gpu_map_entry_delete(map, entry); Result:=GPU_SUCCESS; end; function gpu_map_add(map:p_gpu_map;start,length:TVkDeviceSize;desc:Pointer):Integer; begin Result:=gpu_map_insert(map, start, start + length, desc); end; function gpu_map_remove(map:p_gpu_map;start:TVkDeviceSize):Integer; begin Result:=gpu_map_delete(map, start); end; procedure gpu_map_remove_all(map:p_gpu_map); var entry,next:p_gpu_map_entry; begin entry:=map^.header.next; while (entry<>@map^.header) do begin next:=entry^.next; gpu_map_entry_delete(map, entry); entry:=next; end; end; // function convert_meminfo(mem_type:Byte;mem_info:PVkMemoryType):TvMemInfo; inline; begin Result.heap_index :=mem_info^.heapIndex; Result.mem_type :=mem_type; Result.device_local :=(mem_info^.propertyFlags and ord(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ))<>0; Result.device_coherent:=(mem_info^.propertyFlags and ord(VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD))<>0; Result.host_visible :=(mem_info^.propertyFlags and ord(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ))<>0; Result.host_coherent :=(mem_info^.propertyFlags and ord(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ))<>0; end; Constructor TvDeviceMemory.Create(Handle:TVkDeviceMemory;Size:TVkDeviceSize;mem_type:Byte;mem_info:PVkMemoryType); begin FHandle:=Handle; FSize :=Size; // FMemInfo:=convert_meminfo(mem_type,mem_info); // gpu_map_init(@FMap,0,Size); end; Destructor TvDeviceMemory.Destroy; var F:TVkDeviceMemory; begin ReleaseAllDependencies(Self); // gpu_map_remove_all(@FMap); // TGuard.WaitFor(Self); // F:=System.InterlockedExchange64(FHandle,VK_NULL_HANDLE); if (F<>VK_NULL_HANDLE) then begin vkFreeMemory(Device.FHandle,F,nil); //PrintMemoryBudget; end; // inherited; end; Procedure TvDeviceMemory.remove_all; begin gpu_map_remove_all(@FMap); end; Procedure TvDeviceMemory.Unmap; var F:TVkDeviceMemory; begin F:=System.InterlockedExchangeAdd64(FHandle,0); if (F<>VK_NULL_HANDLE) then begin vkUnmapMemory(Device.FHandle,F); end; end; Procedure TvDeviceMemory.Flush; var range:TVkMappedMemoryRange; begin if (not FMemInfo.host_coherent) then begin range:=Default(TVkMappedMemoryRange); range.sType :=VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range.memory:=FHandle; range.offset:=0; range.size :=FSize; // vkFlushMappedMemoryRanges(Device.FHandle,1,@range); end; end; // function TvDeviceMemory.Acquire(Sender:TObject):Boolean; const mark_delete:QWORD=QWORD(1) shl (SizeOf(FRefs)*8-1); var i:ptruint; begin Result:=False; repeat i:=load_acq_rel(FRefs); if (i and mark_delete)<>0 then Exit; until CAS(FRefs,i,i+1); Result:=True; end; function TvDeviceMemory.Release(Sender:TObject):Boolean; const mark_delete:QWORD=QWORD(1) shl (SizeOf(FRefs)*8-1); var i:ptruint; begin Result:=False; repeat i:=load_acq_rel(FRefs); if (i and mark_delete)<>0 then Exit; if (i=1) then begin if CAS(FRefs,i,mark_delete) then Break; end else begin if CAS(FRefs,i,i-1) then Break; end; until false; if (i=1) then begin Free; end; Result:=True; end; // function TvPointer.Acquire:TvPointer; var F:TvDeviceMemory; Guard:TGuard; begin Result:=Default(TvPointer); Guard:=TGuard.New; F:=TvDeviceMemory(Guard.Protect(Pointer(FMemory))); if (F=nil) then begin Guard.Free; Exit; end; if F.Acquire(nil) then begin Result.FMemory:=F; Result.FOffset:=FOffset; end; Guard.Free; end; function TvPointer.Release:Boolean; var F:TvDeviceMemory; Guard:TGuard; begin Result:=False; Guard:=TGuard.New; F:=TvDeviceMemory(Guard.Protect(Pointer(FMemory))); if (F=nil) then begin Guard.Free; Exit; end; Result:=F.Release(nil); Guard.Free; end; function TvPointer.Hold:Boolean; var F:TvDeviceMemory; Guard:TGuard; begin Result:=False; Guard:=TGuard.New; F:=TvDeviceMemory(Guard.Protect(Pointer(FMemory))); if (F=nil) then begin Guard.Free; Exit; end; Result:=F.Hold(nil); Guard.Free; end; function TvPointer.Drop:Boolean; var F:TvDeviceMemory; Guard:TGuard; begin Result:=False; Guard:=TGuard.New; F:=TvDeviceMemory(Guard.Protect(Pointer(FMemory))); if (F=nil) then begin Guard.Free; Exit; end; Result:=F.Drop(nil); Guard.Free; end; // function GetCompatibleHostMapped(flags:TVkBufferCreateFlags):Boolean; var info:TVkPhysicalDeviceExternalBufferInfo; prop:TVkExternalBufferProperties; begin if (vkGetPhysicalDeviceExternalBufferProperties=nil) then Exit(False); info:=Default(TVkPhysicalDeviceExternalBufferInfo); info.sType :=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO; info.flags :=flags; info.usage :=ord(VK_BUFFER_USAGE_TRANSFER_SRC_BIT) or ord(VK_BUFFER_USAGE_TRANSFER_DST_BIT); info.handleType:=VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT; prop:=Default(TVkExternalBufferProperties); prop.sType:=VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES; vkGetPhysicalDeviceExternalBufferProperties(VulkanApp.FPhysicalDevice,@info,@prop); Result:=(prop.externalMemoryProperties.compatibleHandleTypes and ord(VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT))<>0; end; function GetHostMappedRequirements:TVkMemoryRequirements; var cinfo:TVkBufferCreateInfo; r:TVkResult; FHandle:TVkBuffer; begin Result:=Default(TVkMemoryRequirements); cinfo:=Default(TVkBufferCreateInfo); cinfo.sType :=VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; cinfo.size :=64*1024; cinfo.usage :=ord(VK_BUFFER_USAGE_TRANSFER_SRC_BIT) or ord(VK_BUFFER_USAGE_TRANSFER_DST_BIT); cinfo.sharingMode:=VK_SHARING_MODE_EXCLUSIVE; if limits.VK_EXT_external_memory_host then begin cinfo.pNext:=@buf_ext; end; r:=vkCreateBuffer(Device.FHandle,@cinfo,nil,@FHandle); if (r=VK_SUCCESS) then begin vkGetBufferMemoryRequirements(Device.FHandle,FHandle,@Result); vkDestroyBuffer(Device.FHandle,FHandle,nil); end; end; function GetSparceMemoryTypes:TVkUInt32; var cinfo:TVkBufferCreateInfo; mr:TVkMemoryRequirements; r:TVkResult; FHandle:TVkBuffer; begin Result:=0; if not sparseBinding then Exit; mr:=Default(TVkMemoryRequirements); cinfo:=Default(TVkBufferCreateInfo); cinfo.sType :=VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; cinfo.flags :=ord(VK_BUFFER_CREATE_SPARSE_BINDING_BIT); cinfo.size :=64*1024; cinfo.usage :=ord(VK_BUFFER_USAGE_TRANSFER_SRC_BIT) or ord(VK_BUFFER_USAGE_TRANSFER_DST_BIT); cinfo.sharingMode:=VK_SHARING_MODE_EXCLUSIVE; cinfo.pNext :=@buf_ext; r:=vkCreateBuffer(Device.FHandle,@cinfo,nil,@FHandle); if (r=VK_SUCCESS) then begin vkGetBufferMemoryRequirements(Device.FHandle,FHandle,@mr); vkDestroyBuffer(Device.FHandle,FHandle,nil); Result:=mr.memoryTypeBits; end; end; Constructor TvMemManager.Create; var c:Boolean; mr:TVkMemoryRequirements; s:RawByteString; i:Byte; begin Writeln('[HostMappedRequirements]'); c:=GetCompatibleHostMapped(0); Writeln(' BufferHostMapped=',c); if c then begin mr:=GetHostMappedRequirements; Writeln(' Alignment=',mr.alignment); s:=''; For i:=0 to VK_MAX_MEMORY_TYPES-1 do if ((1 shl i) and (mr.memoryTypeBits))<>0 then begin if (s='') then begin s:=IntToStr(i); end else begin s:=s+','+IntToStr(i); end; end; Writeln(' MemoryType=',S); end; FSparceMemoryTypes:=0; c:=GetCompatibleHostMapped(ord(VK_BUFFER_CREATE_SPARSE_BINDING_BIT)); Writeln(' SparceHostMapped=',c); if c then begin FSparceMemoryTypes:=GetSparceMemoryTypes; s:=''; For i:=0 to VK_MAX_MEMORY_TYPES-1 do if ((1 shl i) and (FSparceMemoryTypes))<>0 then begin if (s='') then begin s:=IntToStr(i); end else begin s:=s+','+IntToStr(i); end; end; Writeln(' SparceType=',s); end; FProperties:=Default(TVkPhysicalDeviceMemoryProperties); vkGetPhysicalDeviceMemoryProperties(VulkanApp.FPhysicalDevice,@FProperties); LoadMemoryHeaps; PrintMemoryHeaps; PrintMemoryBudget; TAILQ_INIT(@FDevs ); TAILQ_INIT(@FHosts); end; function TvMemManager.findMemoryType(Filter:TVkUInt32;prop:TVkMemoryPropertyFlags;start:Integer):Integer; var i:Integer; begin Result:=-1; if (start<0) or (start>=FProperties.memoryTypeCount) then Exit; For i:=start to FProperties.memoryTypeCount-1 do begin if ((Filter and (1 shl i))<>0) and ((FProperties.memoryTypes[i].propertyFlags and prop)=prop) then begin Exit(i); end; end; end; procedure TvMemManager.LoadMemoryHeaps; var i:TVkUInt32; mtype:Integer; function get_host_visible(heapIndex:TVkUInt32):Boolean; inline; var i:TVkUInt32; begin Result:=False; For i:=0 to FProperties.memoryTypeCount-1 do if (FProperties.memoryTypes[i].heapIndex=heapIndex) then if ((FProperties.memoryTypes[i].propertyFlags and ord(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))<>0) then begin Exit(True); end; end; function get_mem_type(heapIndex:TVkUInt32; device_local, host_visible, device_coherent, host_coherent:Boolean):Integer; inline; var i,mask:TVkUInt32; begin Result:=-1; mask:=(ord(device_local )*ord(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) or (ord(host_visible )*ord(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) or (ord(device_coherent)*ord(VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD)) or (ord(host_coherent )*ord(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); For i:=0 to FProperties.memoryTypeCount-1 do if (FProperties.memoryTypes[i].heapIndex=heapIndex) then if ((FProperties.memoryTypes[i].propertyFlags and mask)=mask) then begin Exit(i); end; end; begin SetLength(FHeaps,FProperties.memoryHeapCount); if (FProperties.memoryHeapCount<>0) then For i:=0 to FProperties.memoryHeapCount-1 do begin FHeaps[i].heap_index :=i; FHeaps[i].heap_size :=FProperties.memoryHeaps[i].size; FHeaps[i].device_local:=(FProperties.memoryHeaps[i].flags and ord(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT))<>0; FHeaps[i].host_visible:=get_host_visible(i); FHeaps[i].device_coherent:=FHeaps[i].device_local; FHeaps[i].host_coherent :=FHeaps[i].host_visible; repeat mtype:=get_mem_type(i, FHeaps[i].device_local, FHeaps[i].host_visible, FHeaps[i].device_coherent, FHeaps[i].host_coherent); if (mtype=-1) then begin if FHeaps[i].device_coherent then begin FHeaps[i].device_coherent:=False; end else if FHeaps[i].host_coherent then begin FHeaps[i].host_coherent:=False; end else begin Assert(false,'load memory type'); end; end else begin Break; end; until false; FHeaps[i].def_mem_type:=mtype; end; end; procedure TvMemManager.PrintMemoryHeaps; var i:TVkUInt32; function get_flags_str(flags:TVkUInt32):RawByteString; inline; begin Result:=''; if (flags and ord(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT))<>0 then begin Result:='DEVICE_LOCAL'; end else begin Result:='HOST_LOCAL'; end; if (flags and ord(VK_MEMORY_HEAP_MULTI_INSTANCE_BIT))<>0 then begin if (Result<>'') then Result:=Result+','; Result:=Result+'MULTI_INSTANCE'; end; end; function get_types_str(heapIndex:TVkUInt32):RawByteString; inline; var i:TVkUInt32; begin Result:=''; For i:=0 to FProperties.memoryTypeCount-1 do if (FProperties.memoryTypes[i].heapIndex=heapIndex) then begin if (Result<>'') then Result:=Result+','; Result:=Result+IntToStr(i); end; end; begin For i:=0 to FProperties.memoryHeapCount-1 do begin Writeln('[Heap]:',i); Writeln(' size =0x',HexStr(FProperties.memoryHeaps[i].size,16)); Writeln(' flags=',get_flags_str(FProperties.memoryHeaps[i].flags)); Writeln(' types=',get_types_str(i)); end; // if Length(FHeaps)<>0 then For i:=0 to High(FHeaps) do begin Writeln('[Heap]:',i); Writeln(' heap_size =0x',HexStr(FHeaps[i].heap_size,16)); Writeln(' heap_id =',FHeaps[i].heap_index); Writeln(' def_mem_type =',FHeaps[i].def_mem_type); Writeln(' device_local =',FHeaps[i].device_local); Writeln(' device_coherent=',FHeaps[i].device_coherent); Writeln(' host_visible =',FHeaps[i].host_visible); Writeln(' host_coherent =',FHeaps[i].host_coherent); end; end; procedure TvMemManager.PrintMemoryType(typeFilter:TVkUInt32); var s:RawByteString; i:TVkUInt32; procedure append(TestFlag:TVkFlags;const name:RawByteString); inline; begin if ((FProperties.memoryTypes[i].propertyFlags and TestFlag)<>0) then begin if (s='') then begin s:=s+name; end else begin s:=s+'|'+name; end; end; end; begin For i:=0 to FProperties.memoryTypeCount-1 do begin if ((typeFilter and (1 shl i))<>0) then begin s:=''; append(ord(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ),'DEVICE_LOCAL'); append(ord(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ),'HOST_VISIBLE'); append(ord(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ),'HOST_COHERENT'); append(ord(VK_MEMORY_PROPERTY_HOST_CACHED_BIT ),'HOST_CACHED'); append(ord(VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT ),'LAZILY_ALLOCATED'); append(ord(VK_MEMORY_PROPERTY_PROTECTED_BIT ),'PROTECTED'); append(ord(VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD),'DEVICE_COHERENT_AMD'); append(ord(VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD),'DEVICE_UNCACHED_AMD'); append(ord(VK_MEMORY_PROPERTY_RDMA_CAPABLE_BIT_NV ),'RDMA_CAPABLE_NV'); Write(i,':',HexStr(FProperties.memoryTypes[i].propertyFlags,8),':',s); end; end; end; Function TvMemManager._AllocDevBlock(Size:TVkDeviceSize;mtindex:Byte):TvDeviceMemory; label _retry; var FHandle:TVkDeviceMemory; node:TvDeviceMemory; tmp:TVkDeviceSize; heap_id:Byte; begin Result:=nil; node:=nil; if (FBacked<>nil) then begin if (FBacked.FSize>=Size) and (FBacked.FMemInfo.mem_type=mtindex) then begin node:=FBacked; FBacked:=nil; end; end; if (node=nil) then begin _retry: heap_id:=FProperties.memoryTypes[mtindex].heapIndex; if MemoryBudgetCanBeAlloc(heap_id,Size) then begin FHandle:=vkAllocMemory(Device.FHandle,Size,mtindex); //PrintMemoryBudget; end else begin FHandle:=VK_NULL_HANDLE; end; if (FHandle=VK_NULL_HANDLE) then begin //try shrink tmp:=_shrink(Size,mtindex,0); if (tmp>0) then begin //fail Exit; end else begin goto _retry; end; end; // node:=TvDeviceMemory.Create(FHandle,Size,mtindex,@FProperties.memoryTypes[mtindex]); node.Acquire(nil); //list end; TAILQ_INSERT_HEAD(@FDevs,node,@node.entry); // Result:=node; end; procedure TvMemManager._FreeDevBlock(node:TvDeviceMemory); begin if (node=nil) then Exit; TAILQ_REMOVE(@FDevs,node,@node.entry); if (FBacked<>nil) then begin if (node.FSize>FBacked.FSize) then begin ReleaseAndNil(FBacked); //free old FBacked:=node; //set new end else begin ReleaseAndNil(node); //free new end; end else begin FBacked:=node; //set new end; end; //GRANULAR_DEV_BLOCK_SIZE Function TvMemManager.FetchMemory(const mr:TVkMemoryRequirements;prop:TVkMemoryPropertyFlags;desc:Pointer):TvPointer; var i:Byte; memoryTypeBits:TVkUInt32; best_fit :Boolean; begin Result:=Default(TvPointer); memoryTypeBits:=mr.memoryTypeBits; best_fit:=(prop and V_PROP_BEST_FIT)<>0; prop :=prop and (not V_PROP_BEST_FIT); //filter by prop For i:=0 to VK_MAX_MEMORY_TYPES-1 do if (((1 shl i) and memoryTypeBits)<>0) then begin //if not contain flags if ((FProperties.memoryTypes[i].propertyFlags and prop)<>prop) then begin //exclude memoryTypeBits:=memoryTypeBits and (not (1 shl i)); end; end; Result:=FetchMemory(mr.size,mr.alignment,memoryTypeBits,desc,best_fit); end; Function TvMemManager.FetchMemory(Size :TVkDeviceSize; Align :TVkDeviceSize; memoryTypeBits:TVkUInt32; desc :Pointer; best_fit :Boolean):TvPointer; label _repeat; var node:TvDeviceMemory; _addr:TVkDeviceSize; _size:TVkDeviceSize; save_addr:TVkDeviceSize; save_size:TVkDeviceSize; save_node:TvDeviceMemory; mem_type:Integer; i:Byte; begin Result:=Default(TvPointer); if (Size=0) or (memoryTypeBits=0) then Exit; // if (Align=0) then Align:=1; // save_addr:=0; save_size:=0; save_node:=nil; // rw_wlock(global_mem_lock); // _repeat: node:=TvDeviceMemory(TAILQ_FIRST(@FDevs)); while (node<>nil) do begin _addr:=0; _size:=0; if (((1 shl node.FMemInfo.mem_type) and memoryTypeBits)<>0) then begin if best_fit then begin if gpu_map_find(@node.FMap, @_addr, @_size, Size, Align, desc, [fmBestfit] )=GPU_SUCCESS then begin //save best fit if (save_size=0) or (save_size>_size) then begin save_addr:=_addr; save_size:=_size; save_node:=node; end; end; end else begin if gpu_map_find(@node.FMap, @_addr, @_size, Size, Align, desc, [fmInsert] )=GPU_SUCCESS then begin save_addr:=_addr; save_size:=_size; save_node:=node; Break; end; end; end; node:=TvDeviceMemory(TAILQ_NEXT(node,@node.entry)); end; if (save_size=0) then begin mem_type:=-1; //find by default memtypes For i:=0 to High(FHeaps) do if (((1 shl FHeaps[i].def_mem_type) and memoryTypeBits)<>0) then begin mem_type:=FHeaps[i].def_mem_type; Break; end; if (mem_type=-1) then begin //find first bit mem_type:=BsfByte(memoryTypeBits); end; save_node:=_AllocDevBlock(AlignUp(Size,GRANULAR_DEV_BLOCK_SIZE),mem_type); if (save_node<>nil) then begin if gpu_map_add(@save_node.FMap,0,Size,desc)=GPU_SUCCESS then begin Result.FMemory:=save_node; Result.FOffset:=0; end else begin //wtf? goto _repeat; end; end; end else if best_fit then begin if gpu_map_add(@save_node.FMap,save_addr,size,desc)=GPU_SUCCESS then begin Result.FMemory:=save_node; Result.FOffset:=save_addr; end else begin //Something has changed goto _repeat; end; end else begin //Already inserted Result.FMemory:=save_node; Result.FOffset:=save_addr; end; // if (Result.FMemory<>nil) then begin if (Result.FMemory<>TvDeviceMemory(TAILQ_FIRST(@FDevs))) then begin TAILQ_REMOVE (@FDevs,Result.FMemory,@Result.FMemory.entry); TAILQ_INSERT_HEAD(@FDevs,Result.FMemory,@Result.FMemory.entry); end; // Result.FMemory.Acquire(nil); //fetch ref end; // rw_wunlock(global_mem_lock); end; Function TvMemManager.FreeMemory(P:TvPointer):Boolean; begin Result:=False; if (P.FMemory=nil) then Exit; if (P.FMemory.ClassType<>TvDeviceMemory) then Exit; // rw_wlock(global_mem_lock); // if (gpu_map_remove(@P.FMemory.FMap,P.FOffset)=GPU_SUCCESS) then begin Result:=True; // if (P.FMemory.FMap.size=0) then //is free begin _FreeDevBlock(P.FMemory); end; end; // rw_wunlock(global_mem_lock); end; function get_str_mem_info(const m:TvMemInfo):RawByteString; forward; Function TvMemManager._shrink_dev_block(max:TVkDeviceSize;heap_index:Byte):TVkDeviceSize; var node,prev:TvDeviceMemory; begin Result:=0; if (FBacked<>nil) then begin node:=FBacked; FBacked:=nil; Writeln('Unload:0x',HexStr(node),':0x',HexStr(node.FHandle,16),':[',get_str_mem_info(node.FMemInfo),']'); Result:=Result+node.FSize; ReleaseAndNil(node); if (Result>=max) then Exit; end; node:=TvDeviceMemory(TAILQ_LAST(@FDevs)); while (node<>nil) do begin prev:=TvDeviceMemory(TAILQ_PREV(node,@node.entry)); if (node.FMemInfo.heap_index=heap_index) then if (node.FHold=0) then //lock Hold? begin Writeln('Unload:0x',HexStr(node),':0x',HexStr(node.FHandle,16),':[',get_str_mem_info(node.FMemInfo),']'); // Result:=Result+node.FSize; // TAILQ_REMOVE(@FDevs,node,@node.entry); node.ReleaseAllDependencies(node); node.remove_all; ReleaseAndNil(node); //list // if (Result>=max) then Break; end; node:=prev; end; end; Function TvMemManager._shrink_host_map(max:TVkDeviceSize;heap_index:Byte):TVkDeviceSize; var node,prev:TvHostMemory; i:Integer; begin Result:=0; node:=TvHostMemory(TAILQ_LAST(@FHosts)); while (node<>nil) do begin prev:=TvHostMemory(TAILQ_PREV(node,@node.entry)); if (node.FMemInfo.heap_index=heap_index) then if (node.FHold=0) then //lock Hold? begin Writeln('Unload:0x',HexStr(node),':0x',HexStr(node.FHandle,16),':[',get_str_mem_info(node.FMemInfo),']'); // Result:=Result+node.FSize; // Dec(FHosts_count); TAILQ_REMOVE(@FHosts,node,@node.entry); node.ReleaseAllDependencies(node); ReleaseAndNil(node); //list // if (Result>=max) then Break; end; node:=prev; end; end; Function TvMemManager._shrink(max:TVkDeviceSize;mtindex:Byte;mode:Byte):TVkDeviceSize; var tmp:QWORD; heap_index:Byte; begin heap_index:=FProperties.memoryTypes[mtindex].heapIndex; if (mode=0) then begin //dev->host tmp:=_shrink_dev_block(max,heap_index); end else begin //host->dev tmp:=_shrink_host_map(max,heap_index); end; if (tmp0) then begin //try shrink 2 if (mode=0) then begin //dev->host tmp:=_shrink_host_map(max,heap_index); end else begin //host->dev tmp:=_shrink_dev_block(max,heap_index); end; if (tmpnil) do begin next:=TvHostMemory(TAILQ_NEXT(node,@node.entry)); if (__end>node.FStart) and (startnil) do begin if (FStart>=node.FStart) and (F__End<=node.F__End) then begin Break; end; node:=TvHostMemory(TAILQ_NEXT(node,@node.entry)); end; if (node=nil) then begin FStart_align:=FStart; F__End_align:=F__End; gpu_get_bound(FStart_align,F__End_align); if (FStart_align=0) then begin Writeln('Addres:0x',HexStr(FStart,11),' not gpu mapped!'); Assert(false,'FetchHostMap:gpu_get_bound'); end; FStart_align:=Max(FStart_align,AlignDw(FStart,GRANULAR_MAP_BLOCK_SIZE)); F__End_align:=Min(F__End_align,AlignUp(F__End,GRANULAR_MAP_BLOCK_SIZE)); _retry: tmp:=F__End_align-FStart_align; heap_id:=FProperties.memoryTypes[mtindex].heapIndex; if MemoryBudgetCanBeAlloc(heap_id,tmp) then begin FHandle:=vkAllocHostMemory(Device.FHandle,tmp,mtindex,Pointer(FStart_align)); //PrintMemoryBudget; end else begin FHandle:=VK_NULL_HANDLE; end; if (FHandle=VK_NULL_HANDLE) then begin //try shrink tmp:=_shrink(tmp,mtindex,1); if (tmp>0) then begin node:=nil; goto _fail; end else begin goto _retry; end; end; node:=TvHostMemory.Create(FHandle,tmp,mtindex,@FProperties.memoryTypes[mtindex]); node.FStart:=FStart_align; node.F__End:=F__End_align; node.Acquire(nil); //map ref TAILQ_INSERT_HEAD(@FHosts,node,@node.entry); Inc(FHosts_count); end; node.Acquire(nil); _fail: if (node=nil) then begin _print_host; _print_devs; _print_dmem_fd; PrintMemoryBudget; end; // rw_wunlock(global_mem_lock); // if (node<>nil) then begin Result.FMemory:=TvDeviceMemory(node); Result.FOffset:=Addr-node.FStart; if ((Result.FOffset+Size)>node.FSize) then begin Assert(false); end; end; end; Function TvMemManager.FetchHostMap(Addr,Size:TVkDeviceSize;device_local:Boolean):TvPointer; var i:Byte; begin Result:=Default(TvPointer); Assert(Length(FHeaps)<>0); For i:=0 to High(FHeaps) do if (FHeaps[i].host_visible) then if (FHeaps[i].device_local=device_local) then begin Exit(FetchHostMap(Addr,Size,FHeaps[i].def_mem_type)); end; end; Procedure TvMemManager.Flush; var node:TvHostMemory; begin if (Self=nil) then Exit; rw_wlock(global_mem_lock); // node:=TvHostMemory(TAILQ_FIRST(@FHosts)); while (node<>nil) do begin node.Flush; node:=TvHostMemory(TAILQ_NEXT(node,@node.entry)); end; // rw_wunlock(global_mem_lock); end; function get_str_mem_info(const m:TvMemInfo):RawByteString; begin Result:='HI:0x'+HexStr(m.heap_index,2)+'|'+ 'MI:0x'+HexStr(m.mem_type ,2)+'|'; if m.device_local then begin Result:=Result+'DL|'; end; if m.device_coherent then begin Result:=Result+'DC|'; end; if m.host_visible then begin Result:=Result+'HV|'; end; if m.host_coherent then begin Result:=Result+'HC|'; end; end; Procedure TvMemManager._print_host; var node:TvHostMemory; begin if (Self=nil) then Exit; Writeln('[Host]:'); node:=TvHostMemory(TAILQ_FIRST(@FHosts)); while (node<>nil) do begin Writeln(' 0x',HexStr(node.FHandle,16),':[',get_str_mem_info(node.FMemInfo),']'); Writeln(' 0x',HexStr(node.FStart,16),'..',HexStr(node.F__End,16)); node:=TvHostMemory(TAILQ_NEXT(node,@node.entry)); end; end; function get_desc_str(desc:Pointer):RawByteString; begin if (desc=nil) then begin Result:='(nil)'; end else begin Result:='(0x'+HexStr(desc)+')'+TObject(desc).ClassName; end; end; procedure gpu_map_print_all(map:p_gpu_map); var entry:p_gpu_map_entry; begin entry:=map^.header.next; while (entry<>@map^.header) do begin Writeln(' ',(entry^.start),'..',(entry^.__end),':',(entry^.__end-entry^.start),':',get_desc_str(entry^.desc)); entry:=entry^.next; end; end; Procedure TvMemManager._print_devs; var node:TvDeviceMemory; begin if (Self=nil) then Exit; Writeln('[Devs]:'); node:=TvDeviceMemory(TAILQ_FIRST(@FDevs)); while (node<>nil) do begin Writeln(' 0x',HexStr(node.FHandle,16),':[',get_str_mem_info(node.FMemInfo),']'); Writeln(' ',(node.FMap.size),'/',(node.FSize),':',(node.FMap.size/node.FSize)*100:0:2,'%'); gpu_map_print_all(@node.FMap); node:=TvDeviceMemory(TAILQ_NEXT(node,@node.entry)); end; end; threadvar last_alloc_error:TVkResult; // function vkAllocMemory(device:TVkDevice;Size:TVkDeviceSize;mtindex:TVkUInt32):TVkDeviceMemory; var ainfo:TVkMemoryAllocateInfo; r:TVkResult; begin ainfo:=Default(TVkMemoryAllocateInfo); ainfo.sType :=VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; ainfo.allocationSize :=Size; ainfo.memoryTypeIndex:=mtindex; // Result:=VK_NULL_HANDLE; r:=vkAllocateMemory(device,@ainfo,nil,@Result); if (r<>VK_SUCCESS) then begin last_alloc_error:=r; Writeln(StdErr,'vkAllocateMemory:',r,' Size=0x',HexStr(Size,16),' mtindex=',mtindex); //print_backtrace(StdErr,Get_pc_addr,get_frame,0); end; end; function vkAllocHostMemory(device:TVkDevice;Size:TVkDeviceSize;mtindex:TVkUInt32;addr:Pointer):TVkDeviceMemory; var ainfo:TVkMemoryAllocateInfo; import:TVkImportMemoryHostPointerInfoEXT; r:TVkResult; begin ainfo:=Default(TVkMemoryAllocateInfo); ainfo.sType :=VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; ainfo.allocationSize :=Size; ainfo.memoryTypeIndex:=mtindex; ainfo.pNext:=@import; // import:=Default(TVkImportMemoryHostPointerInfoEXT); import.sType :=VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT; import.handleType :=VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT; import.pHostPointer:=addr; // Result:=VK_NULL_HANDLE; r:=vkAllocateMemory(device,@ainfo,nil,@Result); if (r<>VK_SUCCESS) then begin last_alloc_error:=r; Writeln(StdErr,'vkAllocHostMemory:',r,' Size=0x',HexStr(Size,16),' mtindex=',mtindex,' addr=0x',HexStr(addr)); end; end; function vkAllocDedicatedImage(device:TVkDevice;Size:TVkDeviceSize;mtindex:TVkUInt32;FHandle:TVkImage):TVkDeviceMemory; var ainfo:TVkMemoryAllocateInfo; dinfo:TVkMemoryDedicatedAllocateInfo; r:TVkResult; begin ainfo:=Default(TVkMemoryAllocateInfo); ainfo.sType :=VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; ainfo.allocationSize :=Size; ainfo.memoryTypeIndex:=mtindex; ainfo.pNext:=@dinfo; dinfo:=Default(TVkMemoryDedicatedAllocateInfo); dinfo.sType:=VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; dinfo.image:=FHandle; Result:=VK_NULL_HANDLE; r:=vkAllocateMemory(device,@ainfo,nil,@Result); if (r<>VK_SUCCESS) then begin last_alloc_error:=r; Writeln(StdErr,'vkAllocateMemory:',r); end; end; function vkAllocDedicatedBuffer(device:TVkDevice;Size:TVkDeviceSize;mtindex:TVkUInt32;FHandle:TVkBuffer):TVkDeviceMemory; var ainfo:TVkMemoryAllocateInfo; dinfo:TVkMemoryDedicatedAllocateInfo; r:TVkResult; begin ainfo:=Default(TVkMemoryAllocateInfo); ainfo.sType :=VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; ainfo.allocationSize :=Size; ainfo.memoryTypeIndex:=mtindex; ainfo.pNext:=@dinfo; dinfo:=Default(TVkMemoryDedicatedAllocateInfo); dinfo.sType:=VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; dinfo.buffer:=FHandle; Result:=VK_NULL_HANDLE; r:=vkAllocateMemory(device,@ainfo,nil,@Result); if (r<>VK_SUCCESS) then begin last_alloc_error:=r; Writeln(StdErr,'vkAllocateMemory:',r); end; end; function GetMemoryBudget(var budget:TVkPhysicalDeviceMemoryBudgetPropertiesEXT):Boolean; var prop:TVkPhysicalDeviceMemoryProperties2; begin if (vkGetPhysicalDeviceMemoryProperties2=nil) then Exit(False); // prop.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2; prop.pNext:=@budget; // budget.sType:=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; budget.pNext:=nil; // vkGetPhysicalDeviceMemoryProperties2(VulkanApp.FPhysicalDevice,@prop); // Result:=True; end; function MemoryBudgetCanBeAlloc(heap_id:Byte;size:TVkDeviceSize):Boolean; var budget:TVkPhysicalDeviceMemoryBudgetPropertiesEXT; i:TVkDeviceSize; begin Result:=True; budget:=Default(TVkPhysicalDeviceMemoryBudgetPropertiesEXT); if GetMemoryBudget(budget) then begin i:=budget.heapUsage[heap_id] + size + 128*1024*1024; Result:=(i<=budget.heapBudget[heap_id]); end; end; procedure PrintMemoryBudget; var budget:TVkPhysicalDeviceMemoryBudgetPropertiesEXT; i:Integer; FMemInfo:TvMemInfo; mem_type:Byte; begin budget:=Default(TVkPhysicalDeviceMemoryBudgetPropertiesEXT); if GetMemoryBudget(budget) then begin Writeln('[MemoryBudget]'); For i:=0 to VK_MAX_MEMORY_HEAPS-1 do if (budget.heapBudget[i]<>0) then begin if MemManager<>nil then begin mem_type:=MemManager.FHeaps[i].def_mem_type; FMemInfo:=convert_meminfo(mem_type,@MemManager.FProperties.memoryTypes[mem_type]); end; Writeln(' [',i,']:',get_str_mem_info(FMemInfo),':0x',HexStr(budget.heapUsage[i],16),'/',HexStr(budget.heapBudget[i],16),':', (budget.heapUsage[i]/budget.heapBudget[i]*100):0:2,'%' ); end; end; end; end.