mirror of https://github.com/red-prig/fpPS4.git
316 lines
6.1 KiB
Plaintext
316 lines
6.1 KiB
Plaintext
unit pm4_ring;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
errno,
|
|
md_map,
|
|
systm,
|
|
pm4defs,
|
|
bittype;
|
|
|
|
const
|
|
GC_RING_SIZE=$80000;
|
|
GC_RING_PADD=64*1024;
|
|
|
|
type
|
|
p_pm4_ring=^t_pm4_ring;
|
|
t_pm4_ring=packed record
|
|
buff:Pointer;
|
|
size:DWORD;
|
|
rptr:DWORD;
|
|
wptr:DWORD;
|
|
aptr:DWORD;
|
|
end;
|
|
|
|
function gc_ring_create(ring:p_pm4_ring;size:ptruint):Integer;
|
|
function gc_ring_free (ring:p_pm4_ring):Integer;
|
|
|
|
function gc_ring_pm4_alloc (ring:p_pm4_ring;size:DWORD;buff:PPointer):Boolean;
|
|
procedure gc_ring_pm4_submit (ring:p_pm4_ring);
|
|
procedure gc_ring_pm4_release(ring:p_pm4_ring);
|
|
|
|
function gc_ring_pm4_peek (ring:p_pm4_ring;size:PDWORD;buff:PPointer):Boolean;
|
|
function gc_ring_pm4_drain(ring:p_pm4_ring;size:DWORD):Boolean;
|
|
|
|
function gc_submit_internal (ring:p_pm4_ring;count:DWORD;cmds:Pointer):Integer;
|
|
function gc_switch_buffer_internal(ring:p_pm4_ring):Integer;
|
|
function gc_pm4_event_write_eop (ring:p_pm4_ring;addr:Pointer;data:QWORD;intSel,wait:Integer):Integer;
|
|
|
|
implementation
|
|
|
|
function gc_ring_create(ring:p_pm4_ring;size:ptruint):Integer;
|
|
var
|
|
hMem:THandle;
|
|
begin
|
|
Result:=0;
|
|
if (ring=nil) then Exit(-1);
|
|
|
|
size:=1 shl BsfQWORD(size);
|
|
if (size<GC_RING_PADD) then size:=GC_RING_PADD;
|
|
|
|
ring^.buff:=nil;
|
|
ring^.size:=size;
|
|
ring^.rptr:=0;
|
|
ring^.wptr:=0;
|
|
ring^.aptr:=0;
|
|
|
|
Result:=md_reserve_ex(ring^.buff,size shl 1);
|
|
if (Result<>0) then Exit;
|
|
|
|
Result:=md_split(ring^.buff,size);
|
|
if (Result<>0) then Exit;
|
|
|
|
hMem:=0;
|
|
Result:=md_memfd_create(hMem,size,VM_RW);
|
|
if (Result<>0) then Exit;
|
|
|
|
Result:=md_file_mmap_ex(hMem,ring^.buff,0,size,VM_RW);
|
|
if (Result<>0) then
|
|
begin
|
|
md_memfd_close(hMem);
|
|
Exit;
|
|
end;
|
|
|
|
Result:=md_file_mmap_ex(hMem,ring^.buff+size,0,size,VM_RW);
|
|
|
|
md_memfd_close(hMem);
|
|
end;
|
|
|
|
function gc_ring_free(ring:p_pm4_ring):Integer;
|
|
begin
|
|
Result:=0;
|
|
if (ring=nil) then Exit;
|
|
if (ring^.buff=nil) then Exit;
|
|
if (ring^.size=0) then Exit;
|
|
|
|
Result:=md_unmap_ex(ring^.buff,ring^.size shl 1);
|
|
end;
|
|
|
|
function block_id(val,size:DWORD):DWORD; inline;
|
|
begin
|
|
Result:=val and (not (size-1));
|
|
end;
|
|
|
|
function block_ofs(val,size:DWORD):DWORD; inline;
|
|
begin
|
|
Result:=val and (size-1);
|
|
end;
|
|
|
|
//need lock
|
|
function gc_ring_pm4_alloc(ring:p_pm4_ring;size:DWORD;buff:PPointer):Boolean;
|
|
var
|
|
rsiz:DWORD;
|
|
rptr:DWORD;
|
|
next:DWORD;
|
|
begin
|
|
Result:=False;
|
|
|
|
rsiz:=ring^.size;
|
|
if (size>rsiz) then Exit;
|
|
|
|
rptr:=ring^.rptr;
|
|
next:=ring^.aptr+size;
|
|
|
|
if (block_id(next,rsiz)=block_id(rptr,rsiz)) then
|
|
begin
|
|
if (block_ofs(next,rsiz)<block_ofs(rptr,rsiz)) then Exit;
|
|
end else
|
|
begin
|
|
if (block_ofs(next,rsiz)>block_ofs(rptr,rsiz)) then Exit;
|
|
end;
|
|
|
|
buff^:=ring^.buff+block_ofs(ring^.aptr,rsiz);
|
|
ring^.aptr:=next;
|
|
Result:=True;
|
|
end;
|
|
|
|
procedure gc_ring_pm4_submit(ring:p_pm4_ring);
|
|
begin
|
|
System.InterlockedExchange(ring^.wptr,ring^.aptr);
|
|
end;
|
|
|
|
procedure gc_ring_pm4_release(ring:p_pm4_ring);
|
|
begin
|
|
ring^.aptr:=ring^.wptr;
|
|
end;
|
|
|
|
//single consumer
|
|
function gc_ring_pm4_peek(ring:p_pm4_ring;size:PDWORD;buff:PPointer):Boolean;
|
|
var
|
|
rsiz:DWORD;
|
|
rptr:DWORD;
|
|
wptr:DWORD;
|
|
s :DWORD;
|
|
begin
|
|
Result:=False;
|
|
|
|
rsiz:=ring^.size;
|
|
rptr:=ring^.rptr;
|
|
wptr:=ring^.wptr;
|
|
|
|
if (block_id(rptr,rsiz)=block_id(wptr,rsiz)) then
|
|
begin
|
|
s:=block_ofs(wptr,rsiz)-block_ofs(rptr,rsiz);
|
|
end else
|
|
begin
|
|
s:=(rsiz-block_ofs(rptr,rsiz))+block_ofs(wptr,rsiz);
|
|
end;
|
|
|
|
if (s<>0) then
|
|
begin
|
|
size^:=s;
|
|
buff^:=ring^.buff+block_ofs(rptr,rsiz);
|
|
Result:=True;
|
|
end;
|
|
end;
|
|
|
|
//single consumer
|
|
function gc_ring_pm4_drain(ring:p_pm4_ring;size:DWORD):Boolean;
|
|
var
|
|
rsiz:DWORD;
|
|
rptr:DWORD;
|
|
wptr:DWORD;
|
|
s :DWORD;
|
|
begin
|
|
Result:=False;
|
|
|
|
rsiz:=ring^.size;
|
|
rptr:=ring^.rptr;
|
|
wptr:=ring^.wptr;
|
|
|
|
if (block_id(rptr,rsiz)=block_id(wptr,rsiz)) then
|
|
begin
|
|
s:=block_ofs(wptr,rsiz)-block_ofs(rptr,rsiz);
|
|
end else
|
|
begin
|
|
s:=(rsiz-block_ofs(rptr,rsiz))+block_ofs(wptr,rsiz);
|
|
end;
|
|
|
|
if (size>s) then Exit;
|
|
|
|
rptr:=rptr+size;
|
|
|
|
System.InterlockedExchange(ring^.rptr,rptr);
|
|
|
|
Result:=True;
|
|
end;
|
|
|
|
function gc_submit_internal(ring:p_pm4_ring;count:DWORD;cmds:Pointer):Integer;
|
|
var
|
|
size:QWORD;
|
|
buf:PPM4CMDINDIRECTBUFFER;
|
|
op:DWORD;
|
|
begin
|
|
Result:=0;
|
|
if (count=0) then Exit;
|
|
|
|
if (count>=$1000) then Exit(-2142502897);
|
|
|
|
size:=(count*16);
|
|
|
|
buf:=nil;
|
|
if not gc_ring_pm4_alloc(ring,size,@buf) then
|
|
begin
|
|
Writeln(stderr,'### gc_submit_common : Cannot allocate a space in ring buffer.');
|
|
Exit(EBUSY);
|
|
end;
|
|
|
|
Result:=copyin(cmds,buf,size);
|
|
|
|
if (Result<>0) then
|
|
begin
|
|
gc_ring_pm4_release(ring);
|
|
Exit(-2142502898);
|
|
end;
|
|
|
|
while (count<>0) do
|
|
begin
|
|
op:=buf^.header;
|
|
|
|
if ((op<>$c0023300) and (op<>$c0023f00)) then
|
|
begin
|
|
Writeln(stderr,'## gc_insert_indirect_buffer: invalid opcode = 0x',HexStr(op,8));
|
|
gc_ring_pm4_release(ring);
|
|
Exit(-2142502896);
|
|
end;
|
|
|
|
if (buf^.ibSize=0) then
|
|
begin
|
|
Writeln(stderr,'## gc_insert_indirect_buffer: invalid ib_size = 0x',HexStr(buf^.ibSize,5));
|
|
gc_ring_pm4_release(ring);
|
|
Exit(-2142502895);
|
|
end;
|
|
|
|
Inc(buf);
|
|
Dec(count);
|
|
end;
|
|
|
|
gc_ring_pm4_submit(ring);
|
|
end;
|
|
|
|
function gc_switch_buffer_internal(ring:p_pm4_ring):Integer;
|
|
var
|
|
buf:PPM4CMDSWITCHBUFFER;
|
|
begin
|
|
Result:=0;
|
|
|
|
buf:=nil;
|
|
if not gc_ring_pm4_alloc(ring,sizeof(PM4CMDSWITCHBUFFER),@buf) then
|
|
begin
|
|
Writeln(stderr,'### gc_switch_buffer_internal : Cannot allocate a space in ring buffer.');
|
|
Exit(EBUSY);
|
|
end;
|
|
|
|
//IT_SWITCH_BUFFER = $0000008b;
|
|
|
|
buf^.header:=$c0008b00;
|
|
buf^.data :=0;
|
|
|
|
gc_ring_pm4_submit(ring);
|
|
end;
|
|
|
|
function gc_pm4_event_write_eop(ring:p_pm4_ring;addr:Pointer;data:QWORD;intSel,wait:Integer):Integer;
|
|
var
|
|
buf:PPM4CMDEVENTWRITEEOP;
|
|
begin
|
|
Result:=0;
|
|
|
|
buf:=nil;
|
|
if not gc_ring_pm4_alloc(ring,sizeof(PM4CMDEVENTWRITEEOP),@buf) then
|
|
begin
|
|
Writeln(stderr,'### gc_pm4_event_write_eop : Cannot allocate a space in ring buffer.');
|
|
Exit(EBUSY);
|
|
end;
|
|
|
|
buf^:=Default(PM4CMDEVENTWRITEEOP);
|
|
|
|
// IT_EVENT_WRITE_EOP
|
|
DWORD(buf^.header):=$C0044700;
|
|
|
|
if (wait=0) then
|
|
begin
|
|
buf^.eventType:=kEopCbDbReadsDone;
|
|
end else
|
|
begin
|
|
buf^.eventType:=kEopFlushCbDbCaches;
|
|
end;
|
|
|
|
buf^.eventIndex :=5;
|
|
buf^.invalidateL2:=1;
|
|
buf^.address :=QWORD(addr);
|
|
buf^.intSel :=ord(intSel<>0)*EVENTWRITEEOP_INT_SEL_SEND_INT_ON_CONFIRM;
|
|
buf^.dataSel :=EVENTWRITEEOP_DATA_SEL_SEND_DATA64;
|
|
buf^.DATA :=data;
|
|
|
|
gc_ring_pm4_submit(ring);
|
|
end;
|
|
|
|
|
|
end.
|
|
|
|
|