FPPS4/vulkan/vHostBufferManager.pas

290 lines
4.8 KiB
Plaintext

unit vHostBufferManager;
{$mode objfpc}{$H+}
interface
uses
SysUtils,
g23tree,
Vulkan,
vMemory,
vBuffer,
vDependence;
type
TvHostBuffer=class(TvBuffer)
FAddr:QWORD;
end;
function FetchHostBuffer(cmd:TvDependenciesObject;
Addr:QWORD;
Size:TVkDeviceSize;
device_local:Boolean=False):TvHostBuffer;
implementation
uses
kern_rwlock,
kern_dmem;
type
TvHostBufferKey=packed record
FAddr :QWORD;
FBuffer:TvHostBuffer;
end;
TvAddrCompare=object
function c(const a,b:TvHostBufferKey):Integer; static;
end;
_TvHostBufferSet=specialize T23treeSet<TvHostBufferKey,TvAddrCompare>;
TvHostBufferSet=object(_TvHostBufferSet)
lock:Pointer;
Procedure Lock_wr;
Procedure Unlock_wr;
end;
var
FHostBufferSet:TvHostBufferSet;
Procedure TvHostBufferSet.Lock_wr;
begin
rw_wlock(lock);
end;
Procedure TvHostBufferSet.Unlock_wr;
begin
rw_wunlock(lock);
end;
function TvAddrCompare.c(const a,b:TvHostBufferKey):Integer;
begin
//1 FAddr
Result:=Integer(a.FAddr>b.FAddr)-Integer(a.FAddr<b.FAddr);
if (Result<>0) then Exit;
end;
var
host_alignment:TVkDeviceSize=0;
function _fix_buf_size(var Addr:QWORD;var Size:TVkDeviceSize;usage:TVkFlags):TVkDeviceSize;
var
mr:TVkMemoryRequirements;
begin
if (host_alignment=0) then
begin
mr:=GetRequirements(false,Size,usage,@buf_ext);
host_alignment:=mr.alignment;
end;
Result:=(Addr mod host_alignment);
Addr:=Addr-Result;
Size:=Size+Result;
end;
function _FindHostBuffer(Addr:QWORD;Size:TVkDeviceSize):TvHostBuffer;
label
_repeat,
_delete;
var
It:TvHostBufferSet.Iterator;
tmp:TvHostBufferKey;
buf:TvHostBuffer;
__end:QWORD;
begin
Result:=nil;
__end:=Addr+Size;
_repeat:
tmp:=Default(TvHostBufferKey);
tmp.FAddr :=Addr;
It:=FHostBufferSet.find(tmp);
if (It.Item<>nil) then
begin
buf:=It.Item^.FBuffer;
if buf.Hold(nil) then
begin
if ((buf.FAddr+buf.FSize)>=__end) then
begin
Exit(buf);
end else
begin
buf.Drop(nil);
//The search key matches but the size does not.
goto _delete;
end;
end else
begin
_delete:
//mem is deleted, free buf
FHostBufferSet.erase(It);
buf.Release(nil); //map ref
buf:=nil;
goto _repeat;
end;
end;
tmp:=Default(TvHostBufferKey);
tmp.FAddr :=__end;
//[s|new|e] ->
// [s|old|e]
It:=FHostBufferSet.find_ls(tmp);
while (It.Item<>nil) do
begin
buf:=It.Item^.FBuffer;
if buf.Hold(nil) then
begin
if (buf.FAddr<=Addr) and
((buf.FAddr+buf.FSize)>=__end) then
begin
Exit(buf);
end;
buf.Drop(nil);
end else
begin
goto _delete;
end;
if not It.Prev then Break;
end;
end;
const
ALL_BUFFER_USAGE=
ord(VK_BUFFER_USAGE_TRANSFER_SRC_BIT) or
ord(VK_BUFFER_USAGE_TRANSFER_DST_BIT) or
ord(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) or
ord(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) or
ord(VK_BUFFER_USAGE_INDEX_BUFFER_BIT) or
ord(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) or
ord(VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
function FetchHostBuffer(cmd:TvDependenciesObject;
Addr:QWORD;
Size:TVkDeviceSize;
device_local:Boolean=False):TvHostBuffer;
label
_repeat;
var
dmem_addr:QWORD;
key:TvHostBufferKey;
mem:TvPointer;
begin
Result:=nil;
Assert(Size<>0);
dmem_addr:=QWORD(get_dmem_ptr(Pointer(Addr)));
dmem_addr:=dmem_addr-_fix_buf_size(Addr,Size,ALL_BUFFER_USAGE);
key:=Default(TvHostBufferKey);
key.FAddr:=Addr;
//
FHostBufferSet.Lock_wr;
//
_repeat:
key.FBuffer:=_FindHostBuffer(Addr,Size);
//
FHostBufferSet.Unlock_wr;
//
if (key.FBuffer=nil) then
begin
//create new
mem:=MemManager.FetchHostMap(dmem_addr,Size,device_local);
if (mem.FMemory=nil) then
begin
if device_local then
begin
mem:=MemManager.FetchHostMap(dmem_addr,Size,False);
end;
end;
if (mem.FMemory=nil) then
begin
//try shrink?
//ENOMEM
Exit(nil);
end;
key.FBuffer:=TvHostBuffer.Create(Size,ALL_BUFFER_USAGE,@buf_ext);
key.FBuffer.FAddr:=Addr;
key.FBuffer.SetObjectName('HB_0x'+HexStr(Addr,10)+'..'+HexStr(Addr+Size,10));
if (key.FBuffer.BindMem(mem)<>VK_SUCCESS) then
begin
//unknow error
FreeAndNil(key.FBuffer);
mem.Release; //release [FetchHostMap]
//
Exit(nil);
end;
//
FHostBufferSet.Lock_wr;
//
if FHostBufferSet.Insert(key) then
begin
key.FBuffer.Acquire(nil); //map ref
end else
begin
//collision?
FreeAndNil(key.FBuffer);
mem.Release; //release [FetchHostMap]
//
goto _repeat;
end;
key.FBuffer.Hold(nil); //analog ref in [_FindHostBuffer]
mem.Release; //release [FetchHostMap]
//
FHostBufferSet.Unlock_wr;
//
//create new
end;
Result:=key.FBuffer;
//add dep
cmd.RefTo(Result);
if (Result<>nil) then
begin
Result.Drop(nil); //release [_FindHostBuffer]
end;
end;
end.