FPPS4/sys/dev/dev_dce.pas

1414 lines
31 KiB
Plaintext

unit dev_dce;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
sysutils,
bittype,
vm,
vmparam,
sys_conf,
sys_event,
time,
kern_mtx,
sys_bootparam,
display_interface;
procedure dce_initialize();
function TriggerFlipEop(submit_id:QWORD):Integer;
type
p_dce_page=^t_dce_page;
t_dce_page=packed record //0x170
zero :array[0..19] of QWORD;
labels :array[0..15] of QWORD; //sceVideoOutGetBufferLabelAddress
label_ :QWORD;
unk2 :QWORD;
ident_flip :QWORD;
ident_vblank :QWORD;
filter_id :Integer;
_align :Integer;
vopen_counter:QWORD;
ident_setmode:QWORD;
unk3 :array[0..23] of Byte;
end;
{$IF sizeof(t_dce_page)<>$170}{$STOP sizeof(t_dce_page)<>$170}{$ENDIF}
var
dce_interface:TAbstractDisplay=TDisplayHandle;
dce_handle:TDisplayHandle;
dce_mtx:mtx;
dce_page:p_dce_page;
knlist_lock_flip:mtx;
g_video_out_event_flip:t_knlist;
implementation
uses
errno,
systm,
md_time,
vm_pmap,
subr_backtrace,
sys_vm_object,
vm_pager,
kern_proc,
kern_timeout;
const
EVENTID_FLIP =$0006;
EVENTID_VBLANK =$0007;
EVENTID_SETMODE =$0051;
EVENTID_POSITION =$0058;
EVENTID_PREVBLANK=$0059;
//$0061 sys_update_scaler
//$0062 sys_prevblank wZI4fmUJMlw / vXMpe7Murfc
//$0063 sys_vblank
procedure knote_eventid(event_id:WORD;flipArg:QWORD;lockflags:Integer);
begin
knote(@g_video_out_event_flip, event_id or (flipArg shl 16), lockflags);
end;
var
vblank:record
lock :mtx;
callout:t_callout;
refs :Int64;
count :QWORD;
ptime :QWORD;
tsc :QWORD;
fps_cnt:QWORD;
fps_tsc:QWORD;
end;
procedure vblank_expire(arg:Pointer);
var
i:QWORD;
trigger:Boolean;
begin
if (vblank.refs<>0) then
begin
knote_eventid(EVENTID_PREVBLANK, vblank.count, 0); //SCE_VIDEO_OUT_EVENT_PRE_VBLANK_START
//mtx_lock(dce_mtx);
trigger:=False;
if (dce_handle<>nil) then
begin
trigger:=dce_handle.Vblank();
end;
trigger:=True;
if trigger then
begin
vblank.ptime:=GetProcessTime;
vblank.tsc :=rdtsc();
i:=System.InterlockedIncrement64(vblank.count)-1;
end;
//mtx_unlock(dce_mtx);
//
callout_reset(@vblank.callout, vblank.callout.c_time, @vblank_expire, nil);
//
if trigger then
begin
knote_eventid(EVENTID_VBLANK , i, 0); //SCE_VIDEO_OUT_EVENT_VBLANK
end;
if (vblank.fps_tsc=0) then
begin
vblank.fps_tsc:=rdtsc();
vblank.fps_cnt:=0;
end else
begin
//Inc(fps_cnt);
if ((rdtsc()-vblank.fps_tsc) div tsc_freq)>=1 then
begin
p_host_ipc.SetCaptionFPS(vblank.fps_cnt);
vblank.fps_cnt:=0;
vblank.fps_tsc:=rdtsc();
end;
end;
end;
end;
procedure open_vblank;
var
time:Int64;
begin
if (System.InterlockedIncrement64(vblank.refs)=1) then
begin
time:=round(hz/59.94);
callout_reset(@vblank.callout, time, @vblank_expire, nil);
end;
end;
procedure close_vblank;
begin
System.InterlockedDecrement64(vblank.refs);
end;
type
p_flip_control_args=^t_flip_control_args;
t_flip_control_args=packed record
id :DWORD;
align:DWORD;
arg2 :QWORD;
arg3 :QWORD;
arg4 :QWORD;
arg5 :QWORD;
arg6 :QWORD;
end;
p_resolution_status=^t_resolution_status;
t_resolution_status=packed record
width :DWORD;
heigth :DWORD;
paneWidth :DWORD;
paneHeight :DWORD;
refreshHz :DWORD; //Single
screenSizeInInch:DWORD; //Single
padding:array[0..19] of Byte;
end;
t_scaler_info=array[0..63] of Byte;
//SCE_VIDEO_OUT_REFRESH_RATE_UNKNOWN = 0,
//SCE_VIDEO_OUT_REFRESH_RATE_23_98HZ = 1,
//SCE_VIDEO_OUT_REFRESH_RATE_50HZ = 2,
//SCE_VIDEO_OUT_REFRESH_RATE_59_94HZ = 3,
//SCE_VIDEO_OUT_REFRESH_RATE_29_97HZ = 6,
//SCE_VIDEO_OUT_REFRESH_RATE_89_91HZ = 35, 0x23
//SCE_VIDEO_OUT_REFRESH_RATE_119_88HZ = 13, 0xD
//refreshRate = 0 SCE_VIDEO_OUT_REFRESH_RATE_UNKNOWN
//refreshRate = 3; result.refreshHz = 0x426fc28f( 59.94) SCE_VIDEO_OUT_REFRESH_RATE_59_94HZ
//refreshRate = 2, result.refreshHz = 0x42480000( 50.00) SCE_VIDEO_OUT_REFRESH_RATE_50HZ
//refreshRate = 1, result.refreshHz = 0x41bfd70a( 23.98) SCE_VIDEO_OUT_REFRESH_RATE_23_98HZ
//refreshRate = 4, result.refreshHz = 0x41c00000( 24.00)
//refreshRate = 5, result.refreshHz = 0x41f00000( 30.00)
//refreshRate = 6, result.refreshHz = 0x41efc28f( 29.97) SCE_VIDEO_OUT_REFRESH_RATE_29_97HZ
//refreshRate = 7, result.refreshHz = 0x41c80000( 25.00)
//refreshRate = 9, result.refreshHz = 0x42700000( 60.00)
//refreshRate = 10, result.refreshHz = 0x42400000( 48.00)
//refreshRate = 0xb, result.refreshHz = 0x423fcccd( 47.95)
//refreshRate = 0xc, result.refreshHz = 0x42c80000(100.00)
//refreshRate = 0xd, result.refreshHz = 0x42efc28f(119.88) SCE_VIDEO_OUT_REFRESH_RATE_119_88HZ
//refreshRate = 0xe, result.refreshHz = 0x42f00000(120.00)
//refreshRate = 0xf, result.refreshHz = 0x43480000(200.00)
//refreshRate = 0x10, result.refreshHz = 0x436fc28f(239.76)
//refreshRate = 0x11, result.refreshHz = 0x43700000(240.00)
//refreshRate = 0x14, result.refreshHz = 0x413fd70a( 11.99)
//refreshRate = 0x15, result.refreshHz = 0x41400000( 12.00)
//refreshRate = 0x16, result.refreshHz = 0x416fd70a( 14.99)
//refreshRate = 0x17, result.refreshHz = 0x41700000( 15.00)
//refreshRate = 0x23, result.refreshHz = 0x42b3d1ec( 89.91) SCE_VIDEO_OUT_REFRESH_RATE_89_91HZ
type
p_flip_status=^t_flip_status;
t_flip_status=packed record
flipArg :QWORD;
flipArg2 :QWORD;
count :QWORD;
processTime :QWORD;
tsc :QWORD;
currentBuffer :DWORD;
flipPendingNum0:DWORD;
gcQueueNum :DWORD;
flipPendingNum1:DWORD;
submitTsc :QWORD;
f_0x40 :QWORD;
end;
p_vblank_args=^t_vblank_args;
t_vblank_args=packed record
count :QWORD;
processTime:QWORD;
tsc :QWORD;
flags :Byte;
_align :array[0..6] of Byte;
reserved :QWORD;
end;
pSceVideoOutColorSettings=^SceVideoOutColorSettings;
SceVideoOutColorSettings=packed record
gamma :array[0..2] of Single;
option:DWORD;
end;
type
p_cursor_enable=^t_cursor_enable;
t_cursor_enable=packed record
rtype :DWORD; //0
index :DWORD; //Cursor index (0-1)
args :DWORD; //3 = enable; 1 = disable
enable :DWORD; //1 = enable; 0 = disable
addr_hi:DWORD;
addr_lo:DWORD;
padding:array[0..13] of DWORD;
end;
p_cursor_img_addr=^t_cursor_img_addr;
t_cursor_img_addr=packed record
rtype :DWORD; //1
index :DWORD; //Cursor index (0-1)
args :DWORD; //2
addr_hi:DWORD;
addr_lo:DWORD;
padding:array[0..14] of DWORD;
end;
p_cursor_pos=^t_cursor_pos;
t_cursor_pos=packed record
rtype :DWORD; //2
index :DWORD; //Cursor index (0-1)
args :DWORD; //2
posX :DWORD;
posY :DWORD;
padding:array[0..14] of DWORD;
end;
p_cursor_pos_stereo=^t_cursor_pos_stereo;
t_cursor_pos_stereo=packed record
rtype :DWORD; //3
index :DWORD; //Cursor index (0-1)
args :DWORD; //3
posX :DWORD;
posY :DWORD;
offset :DWORD;
padding:array[0..13] of DWORD;
end;
p_cursor_hot_spot=^t_cursor_hot_spot;
t_cursor_hot_spot=packed record
rtype :DWORD; //4
index :DWORD; //Cursor index (0-1)
args :DWORD; //2
hotX :DWORD;
hotY :DWORD;
padding:array[0..14] of DWORD;
end;
p_cursor_magnify=^t_cursor_magnify;
t_cursor_magnify=packed record
rtype :DWORD; //5
index :DWORD; //Cursor index (0-1)
args :DWORD; //1
enable :DWORD;
padding:array[0..15] of DWORD;
end;
p_cursor_update_pending=^t_cursor_update_pending;
t_cursor_update_pending=packed record
rtype :DWORD; //6
index :DWORD; //Cursor index (0-1)
args :DWORD; //1
ptype :DWORD;
padding:array[0..14] of DWORD;
result :DWORD; //0,1
end;
Function dce_set_cursor_info(dev:p_cdev;canary:QWORD;arg5:DWORD;data:Pointer):Integer;
var
info:t_cursor_enable;
addr:qword;
begin
info:=Default(t_cursor_enable);
Result:=copyin(data,@info,80);
if (Result<>0) then Exit;
case info.rtype of
0: //enable/disable
begin
case info.enable of
1:
begin
addr:=info.addr_lo or (QWORD(info.addr_hi) shl 32);
Writeln('dce_set_cursor_enable:',canary,' ',
info.index,' ',
HexStr(addr,16));
end;
0:
begin
Writeln('dce_set_cursor_disable:',canary,' ',
info.index);
end;
end;
end;
1: //SetImageAddress
begin
addr:=p_cursor_img_addr(@info)^.addr_lo or (QWORD(p_cursor_img_addr(@info)^.addr_hi) shl 32);
Writeln('dce_set_image_addr:',canary,' ',
p_cursor_img_addr(@info)^.index,' ',
HexStr(addr,16));
end;
2: //SetPosition
begin
Writeln('dce_set_cursor_pos:',canary,' ',
p_cursor_pos(@info)^.index,' ',
p_cursor_pos(@info)^.posX,' ',
p_cursor_pos(@info)^.posY);
end;
3:; //SetPositionStereo
4:; //SetHotSpot
5:; //Set2xMagnify
6: //IsUpdatePending
begin
Writeln('dce_is_update_pending:',canary,' ',
p_cursor_update_pending(@info)^.index,' ',
p_cursor_update_pending(@info)^.ptype);
addr:=1;
Result:=copyout(@addr,@p_cursor_update_pending(data)^.result,8);
end;
else
Exit(EINVAL);
end;
end;
var
f_vopen_counter:QWORD=0;
f_eop_count:Integer=1;
Function dce_flip_control(dev:p_cdev;data:p_flip_control_args):Integer;
var
ptr :Pointer;
poff:PQWORD;
plen:PQWORD;
len:QWORD;
u:record
case byte of
0:(r_status:t_resolution_status);
1:(f_status:t_flip_status);
2:(i_scaler:t_scaler_info);
3:(v_vblank:t_vblank_args);
4:(color :SceVideoOutColorSettings)
end;
begin
Result:=0;
//Writeln('dce_flip_control(',data^.id,')');
//id -> 0..0x24
case data^.id of
0: //video open
begin
if (data^.arg6=0) and (data^.arg2=0) then
begin
//arg3 [0..1]
case Integer(data^.arg3) of
0:;
1:begin
//sceSblACMgrIsSystemUcred
Exit(EPERM);
end;
else
begin
Exit(EINVAL);
end;
end;
mtx_lock(dce_mtx);
if (dce_handle=nil) then
begin
dce_handle:=dce_interface.Create;
if (dce_handle=nil) then
begin
Result:=EBUSY;
end else
begin
dce_handle.event_flip:=@g_video_out_event_flip;
dce_handle.labels :=@dce_page^.labels;
dce_handle.dce_mtx :=@dce_mtx;
dce_handle.p_fps_cnt :=@vblank.fps_cnt;
Result:=dce_handle.Open();
end;
end else
begin
Result:=EBUSY;
end;
if (Result=0) then
begin
f_vopen_counter:=f_vopen_counter+1;
FillChar(dce_page^,SizeOf(t_dce_page),0);
dce_page^.filter_id:=-13;
dce_page^.ident_flip :=QWORD($06000000000000);
dce_page^.ident_vblank :=QWORD($07000000000000);
dce_page^.ident_setmode:=QWORD($51000000000000);
dce_page^.vopen_counter:=f_vopen_counter;
f_eop_count:=1;
end;
mtx_unlock(dce_mtx);
if (Result<>0) then Exit;
Writeln('dce_video_open');
ptr:=Pointer(data^.arg5);
len:=$a5a5; //canary
copyout(@len,ptr,SizeOf(QWORD));
open_vblank;
Exit(0);
end;
Exit(EINVAL);
end;
1: //video close
begin
if (data^.arg3=0) and (data^.arg4=0) and
(data^.arg5=0) and (data^.arg6=0) then
begin
//arg2 -> canary
if (data^.arg2<>$a5a5) then Exit(EINVAL);
mtx_lock(dce_mtx);
if (dce_handle=nil) then
begin
Result:=EINVAL;
end else
begin
FreeAndNil(dce_handle);
end;
if (Result=0) then
begin
FillChar(dce_page^,SizeOf(t_dce_page),0);
end;
mtx_unlock(dce_mtx);
if (Result<>0) then Exit;
Writeln('dce_video_close:',data^.arg2);
close_vblank;
kqueue_deregister(EVFILT_DISPLAY,p_proc.p_pid,QWORD($06000000000000));
kqueue_deregister(EVFILT_DISPLAY,p_proc.p_pid,QWORD($07000000000000));
kqueue_deregister(EVFILT_DISPLAY,p_proc.p_pid,QWORD($51000000000000));
kqueue_deregister(EVFILT_DISPLAY,p_proc.p_pid,QWORD($58000000000000));
kqueue_deregister(EVFILT_DISPLAY,p_proc.p_pid,QWORD($59000000000000));
Exit(0);
end;
Exit(EINVAL);
end;
4: //UnregisterBuffer
begin
if (data^.arg4=0) and (data^.arg5=0) and (data^.arg6=0) then
begin
//arg2 -> canary
//arg3 -> buffer id
if (data^.arg2<>$a5a5) then Exit(EINVAL);
if (DWORD(data^.arg3)>15) then Exit(EINVAL);
mtx_lock(dce_mtx);
if (dce_handle=nil) then
begin
Result:=EINVAL;
end else
begin
Result:=dce_handle.UnregisterBuffer(Integer(data^.arg3));
end;
mtx_unlock(dce_mtx);
if (Result<>0) then Exit;
Writeln('UnregisterBuffer:',data^.arg2,' ',data^.arg3);
Exit(0);
end;
Exit(EINVAL);
end;
5: //UnregisterBufferAttribute
begin
if (data^.arg4=0) and (data^.arg5=0) and (data^.arg6=0) then
begin
//arg2 -> canary
//arg3 -> attr id
if (data^.arg2<>$a5a5) then Exit(EINVAL);
if (DWORD(data^.arg3)>3) then Exit(EINVAL);
mtx_lock(dce_mtx);
if (dce_handle=nil) then
begin
Result:=EINVAL;
end else
begin
Result:=dce_handle.UnregisterBufferAttribute(Integer(data^.arg3));
end;
mtx_unlock(dce_mtx);
if (Result<>0) then Exit;
Writeln('UnregisterBufferAttribute:',data^.arg2,' ',data^.arg3);
Exit(0);
end;
Exit(EINVAL);
end;
6: //SetFlipRate
begin
if (data^.arg4=0) and (data^.arg5=0) and (data^.arg6=0) then
begin
//arg2 -> canary
//arg3 -> rate [0..7]
if (data^.arg2<>$a5a5) then Exit(EINVAL);
if (DWORD(data^.arg3)>7) then Exit(EINVAL);
mtx_lock(dce_mtx);
if (dce_handle=nil) then
begin
Result:=EINVAL;
end else
begin
Result:=dce_handle.SetFlipRate(Integer(data^.arg3));
end;
mtx_unlock(dce_mtx);
if (Result<>0) then Exit;
Writeln('SetFlipRate:',data^.arg3);
Exit(0);
end;
Exit(EINVAL);
end;
7: //sceVideoOutSetWindowModeMargins
begin
if (data^.arg5=0) and (data^.arg6=0) then
begin
//arg2 -> canary
//arg3 -> top
//arg4 -> bottom
if (DWORD(data^.arg3) > $fff) or (DWORD(data^.arg4) > $fff) then
begin
Exit(EINVAL);
end;
Writeln('sceVideoOutSetWindowModeMargins:',DWORD(data^.arg3),' ',DWORD(data^.arg4));
Exit(0);
end;
Exit(EINVAL);
end;
9:
begin //get page info
if (data^.arg5=0) and (data^.arg6=0) then
begin
//arg2 -> canary
if (data^.arg2<>$a5a5) then Exit(EINVAL);
poff:=Pointer(data^.arg3); //output offset //0..3FFC000
plen:=Pointer(data^.arg4); //output len //4000
len:=$4000;
Result:=copyout(@len,plen,SizeOf(QWORD));
if (Result=0) then
begin
len:=0;
Result:=copyout(@len,poff,SizeOf(QWORD));
end;
Exit;
end;
Exit(EINVAL);
end;
10: //sceVideoOutGetFlipStatus
begin
if (data^.arg6=0) and (Integer(data^.arg4)>0) then
begin
//arg2 -> canary
//arg3 = &result;
//arg4 = 72
if (data^.arg2<>$a5a5) then Exit(EINVAL);
ptr:=Pointer(data^.arg3);
len:=DWORD(data^.arg4);
if (len>SizeOf(t_flip_status)) then
begin
len:=SizeOf(t_flip_status);
end;
u.f_status:=Default(t_flip_status);
mtx_lock(dce_mtx);
if (dce_handle=nil) then
begin
Result:=EINVAL;
end else
begin
Result:=dce_handle.GetFlipStatus(@u.f_status);
end;
mtx_unlock(dce_mtx);
if (Result<>0) then Exit;
Result:=copyout(@u.f_status,ptr,len);
Exit;
end;
Exit(EINVAL);
end;
11: //sceVideoOutGetVblankStatus
begin
if (data^.arg5=0) and (data^.arg6=0) and (Integer(data^.arg4)>0) then
begin
//arg2 -> canary
//arg3 = &result;
//arg4 = 40
//Writeln('sceVideoOutGetVblankStatus');
if (data^.arg2<>$a5a5) then Exit(EINVAL);
ptr:=Pointer(data^.arg3);
len:=DWORD(data^.arg4);
if (len>SizeOf(t_vblank_args)) then
begin
len:=SizeOf(t_vblank_args);
end;
u.v_vblank:=Default(t_vblank_args);
mtx_lock(vblank.lock);
u.v_vblank.count :=vblank.count;
u.v_vblank.processTime:=vblank.ptime;
u.v_vblank.tsc :=vblank.tsc;
mtx_unlock(vblank.lock);
Result:=copyout(@u.v_vblank,ptr,len);
Exit;
end;
Exit(EINVAL);
end;
12: //sceVideoOutSysUpdateScalerParameters
begin
if (data^.arg5=0) and (data^.arg6=0) then
begin
if (data^.arg4=$30) or (data^.arg4=$40) then
begin
//arg2 -> canary
//arg3 -> ptr
//arg4 -> 64
if (data^.arg2<>$a5a5) then Exit(EINVAL);
ptr:=Pointer(data^.arg3);
len:=QWORD (data^.arg4);
u.i_scaler:=Default(t_scaler_info);
//Writeln('dce_flip_control(',data^.id,'):get_data?');
//print_backtrace_td(stderr);
Result:=copyout(@u.i_scaler,ptr,len);
Exit;
end;
end;
Exit(EINVAL);
end;
19: //sceVideoOutGetResolutionStatus
begin
if (data^.arg4=44) then
begin
//arg2 -> canary
//arg3 = &result;
//arg4 = 44;
if (data^.arg2<>$a5a5) then Exit(EINVAL);
ptr:=Pointer(data^.arg3);
len:=QWORD (data^.arg4);
u.r_status:=Default(t_resolution_status);
mtx_lock(dce_mtx);
if (dce_handle=nil) then
begin
Result:=EINVAL;
end else
begin
Result:=dce_handle.GetResolutionStatus(@u.r_status);
end;
mtx_unlock(dce_mtx);
if (Result<>0) then Exit;
Result:=copyout(@u.r_status,ptr,len);
Exit;
end;
Exit(EINVAL);
end;
24: //sceVideoOutCursor*
begin
if (data^.arg6=0) and (data^.arg4=80) then
begin
//arg2 -> canary
//arg4 = 80;
if (data^.arg2<>$a5a5) then Exit(EINVAL);
Result:=dce_set_cursor_info(dev,data^.arg2,data^.arg5,Pointer(data^.arg3));
Exit;
end;
Exit(EINVAL);
end;
31: //sys
begin
//arg2 -> canary
//arg3 <- subtype 0..13
if (data^.arg2<>$a5a5) then Exit(EINVAL);
if (data^.arg3>13) then Exit(EINVAL);
case data^.arg3 of
12:Writeln('SetLabelsAddr:',' 0x',HexStr(data^.arg4,10));
else
Writeln('dce_flip_control(',data^.id,'):',data^.arg3,' 0x',HexStr(data^.arg4,10));
end;
end;
33: //sceVideoOutAdjustColor_
begin
//arg2 -> canary
//arg3 -> pSetting;
//arg4 -> size;
if (data^.arg2<>$a5a5) then Exit(EINVAL);
ptr:=Pointer(data^.arg3);
len:=data^.arg4;
if (len<>16) and (len<>12) then Exit(EINVAL);
u.color:=Default(SceVideoOutColorSettings);
if (len<>12) then
begin
u.color.option:=$ffffffff;
end;
Result:=copyin(ptr,@u.color,len);
if (Result<>0) then Exit;
Writeln('sceVideoOutAdjustColor(',u.color.gamma[0]:0:2,',',
u.color.gamma[1]:0:2,',',
u.color.gamma[2]:0:2,',0x',HexStr(u.color.option,8)+')');
Exit(0);
end;
else
begin
print_error_td('dce_flip_control('+IntToStr(data^.id)+')');
Assert(False);
Result:=EINVAL;
end;
end;
end;
type
p_register_buffer_attr_args=^t_register_buffer_attr_args;
t_register_buffer_attr_args=packed record
canary :QWORD; //arg5 data in dce_flip_control:0:pointer(arg5)^
attrid :Byte; //attribute id [0..3]
submit :Byte; //0 = RegisterBuffers ; 1 = SubmitChangeBufferAttribute
f_0xa :WORD;
pixelFormat:DWORD;
tilingMode :DWORD;
pitchPixel :DWORD;
width :DWORD;
height :DWORD;
f_0x20 :Byte;
f_0x21 :Byte;
options :WORD;
reserved1 :QWORD;
reserved2 :DWORD;
end;
t_pixelFormat=bitpacked record
unknow_1 :bit8; //[0..7] (always zero)
GRPH_BLUE_CROSSBAR :bit2; //[8..9] [BARG]
GRPH_GREEN_CROSSBAR:bit2; //[10..11] [GBAR]
GRPH_RED_CROSSBAR :bit2; //[12..13] [RGBA]
GRPH_ALPHA_CROSSBAR:bit2; //[14..15] [ARGB]
COLORSPACE :bit3; //[16..18] [SRGB=0,2,HDR=4,5,NORM=6]
unknow_3 :bit1; //[19..19] (always zero)
unknow_4 :bit4; //[20..23] [0,2,3,4,5,7,8]
IS_FLOAT :bit1; //[24..24] [0,1]
unknow_5 :bit2; //[25..26] (always zero)
GRPH_FORMAT :bit3; //[27..29] [0,1,4,6,7]
GRPH_DEPTH :bit2; //[30..31] [16BPP=1,32BPP=2,3]
end;
Function dce_register_buffer_attr(dev:p_cdev;data:p_register_buffer_attr_args):Integer;
begin
Result:=0;
if (data^.canary<>$a5a5) then Exit(EINVAL);
if (data^.attrid>3) then Exit(EINVAL);
if (data^.reserved2<>0) or
(data^.f_0x21<>0) or
(data^.pitchPixel<data^.width) or
(data^.f_0x20>1) then Exit(EINVAL);
if (data^.pitchPixel>$2000) or
(data^.width>$2000) or
(data^.height>$2000) then Exit(EINVAL);
case data^.pixelFormat of
$80000000:; //SCE_VIDEO_OUT_PIXEL_FORMAT_A8R8G8B8_SRGB
$80002200:; //SCE_VIDEO_OUT_PIXEL_FORMAT_A8B8G8R8_SRGB
$80060000:;
$80062200:;
$80220000:;
$80320000:;
$80420000:;
$80520000:;
$88000000:; //SCE_VIDEO_OUT_PIXEL_FORMAT_A2R10G10B10_SRGB
$88060000:; //SCE_VIDEO_OUT_PIXEL_FORMAT_A2R10G10B10
$88720000:;
$88740000:; //SCE_VIDEO_OUT_PIXEL_FORMAT_A2R10G10B10_BT2020_PQ
$88750000:;
$88820000:;
$88840000:;
$88850000:;
$b0000000:;
$b0060000:;
$b0720000:;
$b0740000:;
$b0750000:;
$b0820000:;
$b0840000:;
$b0850000:;
$c1060000:; //SCE_VIDEO_OUT_PIXEL_FORMAT_A16R16G16B16_FLOAT
$c1760000:;
else
Exit(EINVAL);
end;
mtx_lock(dce_mtx);
if (dce_handle=nil) then
begin
Result:=EINVAL;
end else
begin
if (data^.submit=0) then
begin
Result:=dce_handle.RegisterBufferAttribute(data^.attrid,@data^.pixelFormat);
end else
begin
Result:=dce_handle.SubmitBufferAttribute (data^.attrid,@data^.pixelFormat);
end;
end;
mtx_unlock(dce_mtx);
Writeln('register_buffer_attr:',data^.attrid,' ',
data^.submit,' ',
'0x',HexStr(data^.pixelFormat,8),' ',
data^.tilingMode,' ',
data^.pitchPixel,' ',
data^.width,' ',
data^.height,' ',
'0x',HexStr(data^.options,4));
end;
type
p_register_buffer_ptrs=^t_register_buffer_ptrs;
t_register_buffer_ptrs=packed record
canary:QWORD; //arg5 data in dce_flip_control:0:pointer(arg5)^
index :DWORD; //buffer index [0..15]
attrid:DWORD; //attribute id [0..3]
left :Pointer; //buffer ptr
right :Pointer; //Stereo ptr
stereo:Integer;
end;
Function dce_register_buffer_ptrs(dev:p_cdev;data:p_register_buffer_ptrs):Integer;
begin
Result:=0;
if (data^.canary<>$a5a5) then Exit(EINVAL);
if (data^.index>15) then Exit(EINVAL);
if (data^.attrid>3) then Exit(EINVAL);
mtx_lock(dce_mtx);
if (dce_handle=nil) then
begin
Result:=EINVAL;
end else
begin
Result:=dce_handle.RegisterBuffer(@data^.index);
end;
mtx_unlock(dce_mtx);
Writeln('register_buffer_ptrs:',data^.attrid,' ',
data^.index,' ',
'0x',HexStr(data^.left),' ',
'0x',HexStr(data^.right));
end;
type
p_submit_flip_args=^t_submit_flip_args;
t_submit_flip_args=packed record
canary :QWORD; //arg5 data in dce_flip_control:0:pointer(arg5)^
bufferIndex:QWORD;
flipMode :DWORD;
f_0x14 :DWORD;
flipArg :QWORD;
flipArg2 :QWORD;
eop_nz :DWORD;
f_0x2c :DWORD;
eop_val :PQWORD;
f_0x38 :QWORD;
rout :PQWORD; //extraout of result
end;
function TriggerFlipEop(submit_id:QWORD):Integer;
begin
if (dce_handle=nil) then
begin
Result:=EINVAL;
end else
begin
Result:=dce_handle.TriggerFlipEop(submit_id);
end;
end;
Function dce_submit_flip(dev:p_cdev;data:p_submit_flip_args):Integer;
var
submit:t_submit_flip;
submit_eop:QWORD;
ures:QWORD;
begin
Result:=0;
if (data^.canary<>$a5a5) then Exit(EINVAL);
if (Integer(data^.bufferIndex)>15) or
(Integer(data^.bufferIndex)<-1) then Exit(EINVAL);
if (data^.flipMode>6) then Exit(EINVAL);
submit.bufferIndex:=data^.bufferIndex;
submit.flipMode :=data^.flipMode;
submit.flipArg :=data^.flipArg;
submit.flipArg2 :=data^.flipArg2;
mtx_lock(dce_mtx);
if (dce_handle=nil) then
begin
Result:=EINVAL;
end else
begin
if (data^.eop_nz=1) then
begin
// count canary
//eop=0x[00000001] [ff] [00] [a5a5]
submit_eop:=(QWORD(f_eop_count) shl 32) or QWORD($ff00a5a5);
Writeln('submit_eop=0x',HexStr(submit_eop,16));
f_eop_count:=f_eop_count+1;
Result:=dce_handle.SubmitFlipEop(@submit,submit_eop);
if (Result<>0) then
begin
Writeln('SubmitFlipEopError=',Result);
end;
end else
begin
Result:=dce_handle.SubmitFlip(@submit);
if (Result<>0) then
begin
Writeln('SubmitFlipError=',Result);
end;
end;
end;
mtx_unlock(dce_mtx);
//print_backtrace_td(stderr);
Writeln('submit_flip: ','bufferIndex=',data^.bufferIndex,' ',
'flipMode=',data^.flipMode,' ',
'flipArg=','0x',HexStr(data^.flipArg,16),' ',
'eop_val=','0x',HexStr(data^.eop_val));
if (Result=0) then
if (data^.eop_nz=1) then
begin
Result:=copyout(@submit_eop,data^.eop_val,SizeOf(QWORD));
end;
if (data^.rout<>nil) then
begin
ures:=Result;
copyout(@ures,data^.rout,SizeOf(QWORD));
end;
end;
Function dce_deregister_ident(dev:p_cdev;data:PQWORD):Integer;
begin
writeln('dce_deregister_ident:',HexStr(data^,16));
kqueue_deregister(EVFILT_DISPLAY,p_proc.p_pid,data^);
Result:=0;
end;
Function dce_ioctl(dev:p_cdev;cmd:QWORD;data:Pointer;fflag:Integer):Integer;
begin
Result:=0;
//Writeln('dce_ioctl(0x',HexStr(cmd,8),')');
case cmd of
$C0308203:Result:=dce_flip_control (dev,data); //SCE_SYS_DCE_IOCTL_FLIP_CONTROL
$C0308207:Result:=dce_register_buffer_attr(dev,data); //SCE_SYS_DCE_IOCTL_REGISTER_BUFFER_ATTRIBUTE
$C0308206:Result:=dce_register_buffer_ptrs(dev,data);
$C0488204:Result:=dce_submit_flip (dev,data);
$80088209:Result:=dce_deregister_ident (dev,data);
else
begin
print_error_td('dce_ioctl(0x'+HexStr(cmd,8)+')');
Assert(False);
Result:=EINVAL;
end;
end;
end;
Function dce_mmap(dev:p_cdev;offset:vm_ooffset_t;paddr:p_vm_paddr_t;nprot:Integer;memattr:p_vm_memattr_t):Integer;
var
foff:vm_ooffset_t;
begin
Result:=0;
if (offset > $3ffffff) then
begin
Exit(EINVAL);
end;
if ((nprot and $ffffffcc)<>0) then
begin
Exit(EINVAL);
end;
foff:=offset; //?
offset:=((DWORD(offset) mod PAGE_SIZE) + foff);
offset:=(foff and $1fffff) { or (uVar1 and $fffffffe00000)};
paddr^:=DWORD(offset) + QWORD(dce_page);
end;
Function dce_mmap_single(cdev:p_cdev;offset:p_vm_ooffset_t;size:vm_size_t;pobj:p_vm_object_t;nprot:Integer):Integer;
var
off:vm_ooffset_t;
obj:vm_object_t;
begin
Result:=0;
if (size<>PAGE_SIZE) then
begin
Exit(EINVAL);
end;
off:=offset^;
if ((off and QWORD($fffffffffc003fff))<>0) then //0..3FFC000
begin
Exit(EINVAL);
end;
if (off<>0) then
begin
Assert(false);
end;
if (nprot<>$33) then
begin
Exit(EACCES);
end;
obj:=vm_pager_allocate(OBJT_DEVICE,cdev,PAGE_SIZE,$33,off);
obj^.un_pager.map_base:=dce_page;
if (obj=nil) then
begin
Exit(EINVAL);
end;
pobj^:=obj;
end;
Function dce_kqfilter(dev:p_cdev;kn:p_knote):Integer;
begin
if (kn^.kn_kevent.filter=EVFILT_DISPLAY) then
begin
knlist_add(@g_video_out_event_flip,kn,1);
end;
Result:=EINVAL;
end;
const
dce_cdevsw:t_cdevsw=(
d_version :D_VERSION;
d_flags :0;
d_name :'dce';
d_ioctl :@dce_ioctl;
d_mmap :@dce_mmap;
d_kqfilter :@dce_kqfilter;
d_mmap_single :@dce_mmap_single;
);
function filt_display_attach(kn:p_knote):Integer;
var
event_id:WORD;
begin
kn^.kn_flags:=kn^.kn_flags or EV_CLEAR; { automatically set }
if ((kn^.kn_kevent.ident and QWORD($f00000000000))=0) then
begin
//_display_attach(&kn->kn_kevent);
end;
kn^.kn_hook:=Pointer(p_proc.p_pid);
event_id:=kn^.kn_kevent.ident shr 48;
case event_id of
EVENTID_FLIP, //SCE_VIDEO_OUT_EVENT_FLIP
EVENTID_VBLANK, //SCE_VIDEO_OUT_EVENT_VBLANK
EVENTID_SETMODE, //8
EVENTID_POSITION, //12
EVENTID_PREVBLANK, //SCE_VIDEO_OUT_EVENT_PRE_VBLANK_START
$0060,
$0061,
$0062,
$0063:
begin
knlist_add(@g_video_out_event_flip,kn,0);
end;
else
begin
//Writeln(stderr,'filt_display_attach:',event_id);
//Assert(false,'filt_display_attach');
Result:=EINVAL;
end;
end;
Exit(0);
end;
procedure filt_display_detach(kn:p_knote);
var
event_id:WORD;
begin
event_id:=kn^.kn_kevent.ident shr 48;
case event_id of
EVENTID_FLIP, //SCE_VIDEO_OUT_EVENT_FLIP
EVENTID_VBLANK, //SCE_VIDEO_OUT_EVENT_VBLANK
EVENTID_SETMODE, //8
EVENTID_POSITION, //12
EVENTID_PREVBLANK, //SCE_VIDEO_OUT_EVENT_PRE_VBLANK_START
$0060,
$0061,
$0062,
$0063:
begin
knlist_remove(@g_video_out_event_flip,kn,0)
end;
else;
end;
if ((kn^.kn_kevent.ident and QWORD($f00000000000))=0) then
begin
//_display_detach(&kn->kn_kevent);
end;
end;
type
t_dce_hint=bitpacked record
event_id:bit8;
video_id:bit8;
flip_arg:bit48;
end;
t_dce_data=bitpacked record
time :bit12;
counter :bit4;
flip_arg:bit48;
end;
function filt_display_event(kn:p_knote;hint:QWORD):Integer;
var
event_id:WORD;
video_id:BYTE;
counter :BYTE;
time :QWORD;
begin
if (hint=0) then
begin
Result:=t_dce_data(kn^.kn_kevent.data).counter;
end else
begin
event_id:=kn^.kn_kevent.ident shr 48;
video_id:=kn^.kn_kevent.ident shr 40;
if (t_dce_hint(hint).event_id=event_id) and
(event_id<>$fe) and
((t_dce_hint(hint).video_id xor video_id)=0) then
begin
time:=rdtsc();
counter:=t_dce_data(kn^.kn_kevent.data).counter;
if (counter<>$f) then
begin
counter:=counter+1;
end;
kn^.kn_kevent.data:=(time and $fff) or //time
(counter shl 12) or //counter
(hint and QWORD($ffffffffffff0000)); //flip_arg
Result:=1;
end else
begin
Result:=0;
end;
end;
end;
const
filterops_display:t_filterops=(
f_isfd :0;
f_attach:@filt_display_attach;
f_detach:@filt_display_detach;
f_event :@filt_display_event;
);
procedure dce_initialize();
begin
mtx_init(knlist_lock_flip,'knlist_lock_flip');
mtx_init(dce_mtx,'dce');
knlist_init_mtx(@g_video_out_event_flip,@knlist_lock_flip);
mtx_init(vblank.lock,'vblank.lock');
callout_init_mtx(@vblank.callout,vblank.lock,0);
kqueue_add_filteropts(EVFILT_DISPLAY,@filterops_display);
dce_page:=dev_mem_alloc(1);
make_dev(@dce_cdevsw,0,0,0,0666,'dce',[]);
end;
end.