unit dev_gc; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses sys_conf; procedure gc_initialize(); function gc_add_internal_ptr (kq,ptr,udata:Pointer):Integer; register; function gc_del_internal_ptr (kq,ptr:Pointer):Integer; register; procedure gc_wakeup_internal_ptr(ptr:Pointer); register; var sync_me_submit:Boolean=False; //forced wait for all tasks to complete on the GPU side after submit implementation uses errno, kern_mtx, sys_event, kern_event, sched_ule, kern_authinfo, vm, vmparam, vm_pmap, sys_vm_object, vm_pager, vm_map, vm_mmap, kern_rwlock, kern_proc, kern_thr, kern_condvar, time, pm4defs, pm4_ring, pm4_stream, pm4_pfp, pm4_me, dev_dce, vDevice, vMemory, sys_bootparam, subr_backtrace; var gc_page:PDWORD; //SceGnmDingDongArea gc_submits_allowed_vaddr:PInteger=nil; //0=true,1=false (0xfe0100000) gc_submits_allowed_vmirr:PInteger=nil; gc_knlock:mtx; gc_knlist:t_knlist; procedure unmap_dmem_gc(start,__end:QWORD); public; begin if (MemManager<>nil) then begin MemManager.unmap_host(start,__end); end; end; function mmap_addr(paddr,psize:QWORD; prot:Integer; pout_addr:PQWORD):Integer; var map:vm_map_t; begin if ((psize and PAGE_MASK)=0) and ((prot and $33)=prot) then begin map:=@p_vmspace(p_proc.p_vmspace)^.vm_map; if (paddr=0) and ((g_appinfo.mmap_flags and 2)<>0) then begin paddr:=SCE_REPLAY_EXEC_START; end; Result:=vm_mmap2(map, @paddr,psize, prot,prot, MAP_ANON or MAP_SYSTEM or MAP_SHARED,OBJT_DEFAULT, nil,0,nil); if (Result=0) then begin Result:=vm_map_wire(map,paddr,paddr + psize,0); end; if (Result=0) then begin vm_map_set_name(map,paddr,paddr+psize,'SceAppCommArea'); pout_addr^:=paddr; end; end else begin Result:=EINVAL; end; end; type p_SetGsRingSizes=^t_SetGsRingSizes; t_SetGsRingSizes=packed record esgsRingSize:DWORD; gsvsRingSize:DWORD; zero :DWORD; end; p_SetMipStatsReport=^t_SetMipStatsReport; t_SetMipStatsReport=packed record p_type:DWORD; param1:DWORD; param2:DWORD; param3:DWORD; end; p_submit_args=^t_submit_args; t_submit_args=packed record pid :DWORD; count:DWORD; cmds :PQWORD; eop_v:QWORD; wait :Integer; end; p_map_compute_queue_args=^t_map_compute_queue_args; t_map_compute_queue_args=packed record pipeHi :DWORD; pipeLo :DWORD; queueId :DWORD; g_queueId :DWORD; ringBaseAddress:Pointer; readPtrAddress :Pointer; dingDongPtr :Pointer; lenLog2 :DWORD; pipePriority :DWORD; end; p_unmap_compute_queue_args=^t_unmap_compute_queue_args; t_unmap_compute_queue_args=packed record pipeHi :DWORD; pipeLo :DWORD; queueId:DWORD; end; p_ding_dong_args=^t_ding_dong_args; t_ding_dong_args=packed record pipeHi :DWORD; pipeLo :DWORD; queueId :DWORD; nextStartOffsetInDw:DWORD; end; p_set_wave_limit_multipliers=^t_set_wave_limit_multipliers; t_set_wave_limit_multipliers=packed record bitset:Integer; values:array[0..7] of Integer; unk1 :Integer; unk2 :Integer; unk3 :Integer; end; var ring_gfx :t_pm4_ring; ring_gfx_lock :Pointer=nil; type t_gc_priv=object ring_watchdog:t_cv; GC_SRI_event :t_cv; //suspend-resume-idle event GC_IDLE_event:t_cv; //pfp_event :t_cv; gc_suspend_lock:mtx; watchdog_label:Integer; GC_SRI_label :Integer; pfp_label :Integer; sync_to_imdone:Integer; end; var gc_priv:t_gc_priv=( ring_watchdog:(cv_description:nil;cv_waiters:0); GC_SRI_event :(cv_description:nil;cv_waiters:0); GC_IDLE_event:(cv_description:nil;cv_waiters:0); //pfp_event :(cv_description:nil;cv_waiters:0); watchdog_label:0; GC_SRI_label :0; pfp_label :0; sync_to_imdone:0; ); parse_gfx_started:Pointer=nil; parse_gfx_td:p_kthread; pfp_ctx:t_pfp_ctx; pm4_me_gfx:t_pm4_me; var map_queue_valid:QWORD=0; map_queue_hqd :array[0..63] of t_gc_hqd; //asc_queues //gfx ring only procedure onEventWriteEop(pctx:p_pfp_ctx;Body:PPM4CMDEVENTWRITEEOP); var submit_id:QWORD; begin submit_id:=Body^.DATA; if p_print_gpu_ops then begin Writeln('[R]IT_EVENT_WRITE_EOP=0x',HexStr(submit_id,16),' ',Body^.intSel); end; pctx^.stream[stGfxDcb].SubmitFlipEop(Body^.DATA,Body^.intSel); end; procedure onSwitchBuffer(pctx:p_pfp_ctx;Body:Pointer); begin // end; function pm4_parse_gfx_ring(pctx:p_pfp_ctx;token:DWORD;buff:Pointer):Integer; var ibuf:t_pm4_ibuffer; i:Integer; begin Result:=0; case token of $c0023300: begin if p_print_gpu_ops then begin Writeln('[R]INDIRECT_BUFFER (ccb) 0x',HexStr(PPM4CMDINDIRECTBUFFER(buff)^.ibBase,10)); end; if pm4_ibuf_init(@ibuf,buff,@pm4_parse_ccb,stGfxCcb) then begin i:=pm4_ibuf_parse(pctx,@ibuf); if (i<>0) then begin pctx^.add_stall(@ibuf); end; //pm4_me_gfx.Push(pfp_ctx.stream[stGfxCcb]); end; end; $c0023f00: begin if p_print_gpu_ops then begin Writeln('[R]INDIRECT_BUFFER (dcb) 0x',HexStr(PPM4CMDINDIRECTBUFFER(buff)^.ibBase,10)); end; if pm4_ibuf_init(@ibuf,buff,@pm4_parse_dcb,stGfxDcb) then begin i:=pm4_ibuf_parse(pctx,@ibuf); if (i<>0) then begin pctx^.add_stall(@ibuf); end; //pm4_me_gfx.Push(pfp_ctx.stream[stGfxDcb]); end; end; $c0008b00: begin if p_print_gpu_ops then begin Writeln('[R]SWITCH_BUFFER'); end; onSwitchBuffer(pctx,buff); end; $C0044700: //IT_EVENT_WRITE_EOP begin onEventWriteEop(pctx,buff); end; else begin Assert(False); end; end; end; function get_compute_stream_type(c_id:DWORD):t_pm4_stream_type; inline; begin Result:=t_pm4_stream_type(ord(stCompute0) + (c_id div 8)); //pipe id end; procedure pfp_idle; forward; procedure parse_gfx_ring(parameter:pointer); SysV_ABI_CDecl; var buff:Pointer; i,size:DWORD; ibuf:t_pm4_ibuffer; buft:t_pm4_stream_type; base_guest_addr:Pointer; bits:QWORD; c_id:DWORD; p_id:DWORD; send:DWORD; begin sched_prio(curkthread,64); if LoadVulkan then begin InitVulkan; end; repeat if gc_ring_pm4_peek(@ring_gfx,@size,@buff) then begin //Writeln('packet:0x',HexStr(buff),':',size); if pm4_ibuf_init(@ibuf,buff,size,@pm4_parse_gfx_ring,stGfxRing) then begin i:=pm4_ibuf_parse(@pfp_ctx,@ibuf); if (i<>0) then begin //pm4_me_gfx.Push(pfp_ctx.stream_dcb); //pm4_me_gfx.Push(pfp_ctx.stream_ccb); pfp_ctx.add_stall(@ibuf); end; end; gc_ring_pm4_drain(@ring_gfx,size); // for buft:=stGfxDcb to stGfxCcb do begin pfp_ctx.Flush_stream(buft); end; // Continue; end; bits:=map_queue_valid; if (bits<>0) then begin //init sended bits send:=0; while (bits<>0) do begin c_id:=BsfQWord(bits); //start rw_wlock(ring_gfx_lock); p_id:=2; //double check while (p_id<>0) and gc_map_hdq_peek(@map_queue_hqd[c_id],@size,@buff) do begin //adjust guest addr base_guest_addr:=map_queue_hqd[c_id].base_guest_addr + (buff - map_queue_hqd[c_id].base_dmem_addr); // rw_wunlock(ring_gfx_lock); // if pm4_ibuf_init(@ibuf,buff,size,@pm4_parse_compute_ring,get_compute_stream_type(c_id),c_id) then begin //adjust guest addr ibuf.base:=base_guest_addr; i:=pm4_ibuf_parse(@pfp_ctx,@ibuf); if (i<>0) then begin pfp_ctx.add_stall(@ibuf); end; end; // rw_wlock(ring_gfx_lock); // gc_map_hdq_drain(@map_queue_hqd[c_id],size); //set sended bits send:=send or (QWORD(1) shl (c_id div 8)); //by pipe id //It is necessary to force QWORD! Dec(p_id); end; //while rw_wunlock(ring_gfx_lock); //end //clear bits:=bits and (not (QWORD(1) shl c_id)); //It is necessary to force QWORD! end; //while if (send<>0) then begin // while (send<>0) do begin c_id:=BsfDWord(send); pfp_ctx.Flush_stream( t_pm4_stream_type(ord(stCompute0) + c_id) ); //clear send:=send and (not (QWORD(1) shl c_id)); //It is necessary to force QWORD! end; // Continue; end; //(send<>0) end; //(bits<>0) //PFP idle pfp_idle; if (gc_priv.watchdog_label=0) then begin mtx_lock(gc_priv.gc_suspend_lock); _cv_wait_sig(@gc_priv.ring_watchdog,@gc_priv.gc_suspend_lock); mtx_unlock(gc_priv.gc_suspend_lock); end else begin mtx_lock(gc_priv.gc_suspend_lock); _cv_timedwait_sig(@gc_priv.ring_watchdog,@gc_priv.gc_suspend_lock, hz div 10000); mtx_unlock(gc_priv.gc_suspend_lock); end; until false; end; procedure start_gfx_ring; begin if (System.InterlockedExchange(parse_gfx_started,Pointer(1))=nil) then begin pfp_ctx.init; pfp_ctx.on_flush_stream:=@pm4_me_gfx.Push; cv_init (@gc_priv.ring_watchdog ,'ring_watchdog'); cv_init (@gc_priv.GC_SRI_event ,'GC_SRI_event'); cv_init (@gc_priv.GC_IDLE_event ,'GC_IDLE_event'); mtx_init(gc_priv.gc_suspend_lock,'gc_suspend_lock'); kthread_add(@parse_gfx_ring,nil,@parse_gfx_td,(8*1024*1024) div (16*1024),'[GFX_PFP]'); end; end; procedure gc_retrigger_watchdog; begin if (gc_priv.ring_watchdog.cv_description<>nil) then begin gc_priv.watchdog_label:=1; //thread can`t wait cv_signal(@gc_priv.ring_watchdog); end; end; procedure gc_retrigger_watchdog_imdone; begin //if (gc_priv.sync_to_imdone=0) then begin gc_retrigger_watchdog; gc_priv.sync_to_imdone:=1; end; end; procedure gc_wait_GC_SRI; begin if (pm4_me_gfx.started=nil) then Exit; mtx_lock(gc_priv.gc_suspend_lock); if (gc_priv.GC_SRI_label<>0) then begin pm4_me_gfx.trigger; //update if wait _cv_wait_sig(@gc_priv.GC_SRI_event,@gc_priv.gc_suspend_lock); end; mtx_unlock(gc_priv.gc_suspend_lock); //pm4_me_gfx.trigger; //update if wait end; procedure pfp_idle; begin mtx_lock(gc_priv.gc_suspend_lock); //if (gc_priv.pfp_label<>0) then //begin // cv_broadcastpri(@gc_priv.pfp_event,0); //end; gc_priv.pfp_label:=0; mtx_unlock(gc_priv.gc_suspend_lock); pm4_me_gfx.trigger; end; procedure gc_idle; register; begin mtx_lock(gc_priv.gc_suspend_lock); if (gc_priv.pfp_label=0) then begin cv_signal(@gc_priv.GC_IDLE_event); end; mtx_unlock(gc_priv.gc_suspend_lock); end; procedure gc_imdone_tasklet; var ret:Integer; begin mtx_lock(gc_priv.gc_suspend_lock); gc_priv.pfp_label:=1; repeat gc_retrigger_watchdog; pm4_me_gfx.trigger; //update if wait ret:=_cv_timedwait_sig(@gc_priv.GC_IDLE_event,@gc_priv.gc_suspend_lock, hz div 2); if (ret<>0) then begin Writeln(stderr,'GPU failed to become idle within 500 ms after submitDone.'); end; until (ret=0); if (gc_priv.GC_SRI_label<>0) then begin cv_broadcastpri(@gc_priv.GC_SRI_event,0); end; gc_priv.GC_SRI_label:=0; mtx_unlock(gc_priv.gc_suspend_lock); if (gc_submits_allowed_vmirr<>nil) then begin gc_submits_allowed_vmirr^:=0; //true end; end; procedure gc_imdone; begin //if (gc_priv.GC_SRI_event=nil) then Exit; if (pm4_me_gfx.started=nil) then Exit; //gc_wait_GC_SRI; mtx_lock(gc_priv.gc_suspend_lock); gc_priv.sync_to_imdone:=0; { if (gc_priv.pfp_label<>0) then begin gc_retrigger_watchdog; _cv_wait_sig(@gc_priv.pfp_event,@gc_priv.GC_SRI_lock); end; } if (gc_priv.GC_SRI_label<>0) then begin pm4_me_gfx.trigger; //update if wait _cv_wait_sig(@gc_priv.GC_SRI_event,@gc_priv.gc_suspend_lock); end; //gc_priv.pfp_label :=1; gc_priv.GC_SRI_label:=1; mtx_unlock(gc_priv.gc_suspend_lock); //gc_priv.GC_SRI_label:=1; if (gc_submits_allowed_vmirr<>nil) then begin gc_submits_allowed_vmirr^:=1; //false end; gc_priv.watchdog_label:=0; //thread can wait //-> gc_imdone_tasklet gc_imdone_tasklet; pm4_me_gfx.imdone; //gc_wait_GC_SRI; end; Function gc_map_compute_queue(data:p_map_compute_queue_args):Integer; var g_queueId :DWORD; pipeHi :DWORD; pipeLo :DWORD; queueId :DWORD; pipePriority:DWORD; id :DWORD; begin Result:=0; g_queueId :=data^.g_queueId; pipeHi :=data^.pipeHi; pipeLo :=data^.pipeLo; queueId :=data^.queueId; pipePriority:=data^.pipePriority; //if (not IsDevKit) or if (pipeHi <> $0769c766) or (pipeLo <> $72e8e3c1) or (queueId <> $db72af28) or (g_queueId <> $d245ed58) then begin //if (not IsDevKit) or (not IsDiag) if (pipeHi <> $e13ec1f1) or (pipeLo <> $76c0801c) or (queueId <> $75c36152) or (g_queueId <> $4be587dd) then begin if (pipeHi = 2) and (pipeLo = 3) then begin Exit(Integer($804c000a)); end; if (1 < (pipeHi - 1)) then begin Exit(Integer($804c000b)); end; if (3 < pipeLo) then begin Exit(Integer($804c000b)); end; if (7 < queueId) then begin Exit(Integer($804c000b)); end; end else begin pipeHi :=2; g_queueId:=19; pipeLo :=3; queueId :=3; end; end else begin pipeHi :=2; g_queueId:=20; queueId :=4; pipeLo :=3; end; if (pipePriority >= 2) then begin Exit(EINVAL); end; id:=(pipeHi - 1) * 32 + pipeLo * 8 + queueId; if ((map_queue_valid and (QWORD(1) shl id))<>0) then //It is necessary to force QWORD! begin Exit(Integer($804c0012)); end; Result:=gc_map_hqd(data^.ringBaseAddress, data^.readPtrAddress, @gc_page[id], data^.lenLog2, g_queueId, pipePriority, @map_queue_hqd[id]); if (Result=0) then begin map_queue_valid:=map_queue_valid or (QWORD(1) shl id); //It is necessary to force QWORD! end; end; Function gc_unmap_compute_queue(data:p_unmap_compute_queue_args):Integer; var pipeHi :DWORD; pipeLo :DWORD; queueId :DWORD; id :DWORD; begin Result:=0; pipeHi :=data^.pipeHi; pipeLo :=data^.pipeLo; queueId:=data^.queueId; //if (not IsDevKit) or (not IsDiag) if (pipeHi <> $0769c766) or (pipeLo <> $72e8e3c1) or (queueId <> $db72af28) then begin //if (not IsDevKit) or (not IsDiag) if (pipeHi <> $e13ec1f1) or (pipeLo <> $76c0801c) or (queueId <> $75c36152) then begin if (pipeHi = 2) and (pipeLo = 3) then begin Exit(Integer($804c000a)); end; if (1 < (pipeHi - 1)) then begin Exit(Integer($804c000b)); end; if (3 < pipeLo) then begin Exit(Integer($804c000b)); end; if (7 < queueId) then begin Exit(Integer($804c000b)); end; end else begin pipeLo :=3; pipeHi :=2; queueId:=3; end; end else begin queueId:=4; pipeLo :=3; pipeHi :=2; end; id:=(pipeHi - 1) * 32 + pipeLo * 8 + queueId; if ((map_queue_valid and (QWORD(1) shl id))<>0) then //It is necessary to force QWORD! begin gc_unmap_hqd(@map_queue_hqd[id]); map_queue_valid:=map_queue_valid and (not (QWORD(1) shl id)); //It is necessary to force QWORD! end; end; Function gc_ding_dong(data:p_ding_dong_args):Integer; var pipeHi :DWORD; pipeLo :DWORD; queueId:DWORD; id :DWORD; begin Result:=0; pipeHi :=data^.pipeHi; pipeLo :=data^.pipeLo; queueId:=data^.queueId; //if (not IsDevKit) or (not IsDiag) if (pipeHi <> $0769c766) or (pipeLo <> $72e8e3c1) or (queueId <> $db72af28) then begin if (pipeHi = 2) and (pipeLo = 3) then begin Exit(Integer($804c000a)); end; if (1 < (pipeHi - 1)) then begin Exit(Integer($804c000b)); end; if (3 < pipeLo) then begin Exit(Integer($804c000b)); end; if (7 < queueId) then begin Exit(Integer($804c000b)); end; end else begin queueId:=4; pipeLo :=3; pipeHi :=2; end; id:=(pipeHi - 1) * 32 + pipeLo * 8 + queueId; if ((map_queue_valid and (QWORD(1) shl id))=0) then //It is necessary to force QWORD! begin Exit(Integer($804c0001)); end; Result:=gc_map_hdq_ding_dong(@map_queue_hqd[id], data^.nextStartOffsetInDw); end; Function gc_ioctl(dev:p_cdev;cmd:QWORD;data:Pointer;fflag:Integer):Integer; var vaddr:Pointer; begin Result:=0; //Writeln('gc_ioctl(0x',HexStr(cmd,8),')'); case cmd of $C0088111:; //sceGnmDebugHardwareStatus $C0108120: //call in neo mode (Tca) begin Exit(19); end; $C004811F: //sceGnmGetNumTcaUnits -> 0x01 //ext_gpu_timer -> 0x08 begin PInteger(data)^:=0; end; $C00C8110: //sceGnmSetGsRingSizes begin with p_SetGsRingSizes(data)^ do Writeln('SetGsRingSizes(0x',HexStr(esgsRingSize,8),',0x' ,HexStr(gsvsRingSize,8),')'); pfp_ctx.set_esgs_gsvs_ring_size(p_SetGsRingSizes(data)^.esgsRingSize, p_SetGsRingSizes(data)^.gsvsRingSize); end; $C0848119: //*MipStatsReport begin case PInteger(data)^ of $10001: begin with p_SetMipStatsReport(data)^ do Writeln('MipStatsReport(0x',HexStr(param1,8),',0x' ,HexStr(param2,8),',0x' ,HexStr(param3,8),')'); end; $18001:; //diag? else Exit(EINVAL); end; end; $C008811B: //sceGnmAreSubmitsAllowed begin if (gc_submits_allowed_vaddr=nil) then begin vaddr:=nil; Result:=mmap_addr($fe0100000,$4000,1,@vaddr); if (Result<>0) then begin Result:=ENOMEM; Exit; end; gc_submits_allowed_vaddr:=Pointer(vaddr); gc_submits_allowed_vmirr:=mirror_map(vaddr,$4000); end; PPointer(data)^:=gc_submits_allowed_vaddr; gc_submits_allowed_vmirr^:=0; //init true end; $C010810B: //get cu mask begin PInteger(data)[0]:=$10; //& 0x3ff GC SE0 Redundant CU: 0x10 PInteger(data)[1]:=$10; //& 0x3ff GC SE1 Redundant CU: 0x10 PInteger(data)[2]:=$00; //& 0x3ff GC SE2 Redundant CU: 0x00 PInteger(data)[3]:=$00; //& 0x3ff GC SE3 Redundant CU: 0x00 end; $C0048116: //sceGnmSubmitDone begin start_gfx_ring; Writeln('sceGnmSubmitDone'); //rw_wlock(ring_gfx_lock); gc_imdone; //rw_wunlock(ring_gfx_lock); end; $C0048117: //wait idle begin Writeln('gc_wait_idle'); //rw_wlock(ring_gfx_lock); gc_wait_GC_SRI; //rw_wunlock(ring_gfx_lock); end; $C004811D: //???? begin Writeln('gc_wait_free:',PInteger(data)^); //rw_wlock(ring_gfx_lock); gc_wait_GC_SRI; //rw_wunlock(ring_gfx_lock); end; $C0048114: //sceGnmFlushGarlic begin Writeln('sceGnmFlushGarlic'); MemManager.Flush; end; $C0108102: //submit begin start_gfx_ring; rw_wlock(ring_gfx_lock); //gc_retrigger_watchdog; Result:=gc_submit_internal(@ring_gfx, p_submit_args(data)^.count, p_submit_args(data)^.cmds); if (Result=0) then begin gc_retrigger_watchdog; if sync_me_submit then begin //force wait GPU idle gc_wait_GC_SRI; end; //msleep_td(hz); end; rw_wunlock(ring_gfx_lock); end; $C020810C: //submit eop begin start_gfx_ring; rw_wlock(ring_gfx_lock); Result:=gc_submit_internal(@ring_gfx, p_submit_args(data)^.count, p_submit_args(data)^.cmds); if (Result=0) then begin { The original data is an incremental "submit_id | (vmid << 32)", now this is directly sended "eop_v" } Writeln('submit_eop=0x',HexStr(p_submit_args(data)^.eop_v,16),' ',p_submit_args(data)^.wait); Result:=gc_pm4_event_write_eop(@ring_gfx, nil, p_submit_args(data)^.eop_v, 1, p_submit_args(data)^.wait ); end; if (Result=0) then begin gc_retrigger_watchdog; end; rw_wunlock(ring_gfx_lock); end; $C0088101: //switch_buffer begin start_gfx_ring; gc_retrigger_watchdog_imdone; gc_wait_GC_SRI; rw_wlock(ring_gfx_lock); Result:=gc_switch_buffer_internal(@ring_gfx); rw_wunlock(ring_gfx_lock); end; $C030810D: //sceGnmMapComputeQueue begin rw_wlock(ring_gfx_lock); //reset prio p_map_compute_queue_args(data)^.pipePriority:=0; Result:=gc_map_compute_queue(data); if (Result=0) then begin gc_retrigger_watchdog; end; rw_wunlock(ring_gfx_lock); end; $C030811A: //sceGnmMapComputeQueueWithPriority begin rw_wlock(ring_gfx_lock); Result:=gc_map_compute_queue(data); if (Result=0) then begin gc_retrigger_watchdog; end; rw_wunlock(ring_gfx_lock); end; $C00C810E: //sceGnmUnmapComputeQueue begin gc_wait_GC_SRI; rw_wlock(ring_gfx_lock); Result:=gc_unmap_compute_queue(data); rw_wunlock(ring_gfx_lock); end; $C010811C: //sceGnmDingDong begin start_gfx_ring; Writeln('sceGnmDingDong'); gc_retrigger_watchdog_imdone; gc_wait_GC_SRI; rw_wlock(ring_gfx_lock); Result:=gc_ding_dong(data); if (Result=0) then begin gc_retrigger_watchdog; end; rw_wunlock(ring_gfx_lock); end; $C030811E: //sceGnmSetWaveLimitMultipliers begin with p_set_wave_limit_multipliers(data)^ do Writeln('SetWaveLimitMultipliers(b',BinStr(bitset,8),',', values[0],',', values[1],',', values[2],',', values[3],',', values[4],',', values[5],',', values[6],',', values[7],')' ); end; else begin print_error_td('gc_ioctl(0x'+HexStr(cmd,8)+')'); Assert(False); Result:=EINVAL; end; end; end; Function gc_mmap_single(cdev:p_cdev;offset:p_vm_ooffset_t;size:vm_size_t;objp:p_vm_object_t;nprot:Integer):Integer; var obj:vm_object_t; begin if sceSblACMgrHasUseHp3dPipeCapability(@g_authinfo) then begin Exit(EINVAL); end; if (offset^>=PAGE_SIZE) then begin Exit(EPERM); end; if (size<>PAGE_SIZE) then begin Exit(EINVAL); end; obj:=vm_pager_allocate(OBJT_DEVICE,cdev,PAGE_SIZE,nprot,offset^); obj^.un_pager.map_base:=gc_page; if (obj=nil) then begin Exit(EINVAL); end; objp^:=obj; Result:=0; end; Function gc_mmap(dev:p_cdev;offset:vm_ooffset_t;paddr:p_vm_paddr_t;nprot:Integer;memattr:p_vm_memattr_t):Integer; begin if sceSblACMgrHasUseHp3dPipeCapability(@g_authinfo) then begin Exit(EINVAL); end; if (offset>=PAGE_SIZE) then begin Exit(EPERM); end; paddr^:=offset + QWORD(gc_page); memattr^:=0; Result:=0; end; const gc_cdevsw:t_cdevsw=( d_version :D_VERSION; d_flags :0; d_name :'gc'; d_ioctl :@gc_ioctl; d_mmap :@gc_mmap; d_mmap_single :@gc_mmap_single; ); { event_id (SourceID == 0xb5) 0x00 -> Compute0 0x01 -> Compute1 0x02 -> Compute2 0x03 -> Compute3 0x04 -> Compute4 0x05 -> Compute5 0x06 -> Compute6 0x40 -> GfxEop 0x41 -> GfxEop (meid ????) 0x84 -> set timer hz (SourceID == 0xef) 0x83, 0x85, 0x86 -> (dbggc) } function filterops_graphics_core_attach(kn:p_knote):Integer; var kn_sdata:QWORD; event_id:Byte; cap:Boolean; me_id:Byte; begin Result:=0; event_id:=Byte(kn^.kn_sdata); //kev.ident = kev.data cap:=sceSblACMgrHasUseHp3dPipeCapability(@g_authinfo); me_id:=0; case event_id of $00..$41, $80..$86: begin if (not cap) then begin if (event_id=$84) or (event_id=$41) then Exit(EPERM); end else begin if (event_id=$40) then Exit(EPERM); end; kn_sdata:=kn^.kn_sdata; case event_id of $82..$86:kn_sdata:=(kn_sdata and QWORD($ffffffffffff0000)) or QWORD(event_id); else kn_sdata:=(kn_sdata and QWORD($ffffffffffff0000)) or QWORD(event_id) or QWORD((me_id and $ff) shl 8); end; kn^.kn_sdata:=kn_sdata; kn^.kn_flags:=kn^.kn_flags or EV_CLEAR; { automatically set } knlist_add(@gc_knlist,kn,0); end; else Result:=EINVAL; end; end; procedure filterops_graphics_core_detach(kn:p_knote); begin knlist_remove(@gc_knlist,kn,0); end; function filterops_graphics_core_event(kn:p_knote;hint:QWORD):Integer; var me_id:Byte; begin //hint:[event_id:8|meid:8|timestamp:48] if (hint=0) then begin Result:=ord(kn^.kn_kevent.data<>0); end else begin Result:=0; if (Byte(kn^.kn_sdata)=Byte(hint)) then //kn^.kn_sdata.event_id = hint.event_id begin me_id:=(hint shr 8); if (me_id=$80) or (me_id=Byte(kn^.kn_sdata shr 8)) then begin Result:=1; me_id:=Byte(kn^.kn_kevent.data shr 8); kn^.kn_kevent.data:=(hint and QWORD($ffffffffffff00ff)) or (QWORD(me_id) shl 8); end; end; end; end; procedure filterops_graphics_core_touch(kn:p_knote;kev:p_kevent;_type:QWORD); begin if (_type=EVENT_PROCESS) then begin kev^:=kn^.kn_kevent; end; end; const filterops_graphics_core:t_filterops=( f_isfd :0; f_attach:@filterops_graphics_core_attach; f_detach:@filterops_graphics_core_detach; f_event :@filterops_graphics_core_event; f_touch :@filterops_graphics_core_touch ); //// var gc_internal_knlock:mtx; gc_internal_knlist:t_knlist; function filterops_internal_attach(kn:p_knote):Integer; begin Result:=0; kn^.kn_flags:=kn^.kn_flags or EV_CLEAR; { automatically set } knlist_add(@gc_internal_knlist,kn,0); end; procedure filterops_internal_detach(kn:p_knote); begin knlist_remove(@gc_internal_knlist,kn,0); end; function filterops_internal_event(kn:p_knote;hint:QWORD):Integer; begin Result:=0; if (hint=0) then begin if (kn^.kn_kevent.ident=hint) then begin Result:=Integer(kn^.kn_kevent.data); end; end else begin if (kn^.kn_kevent.ident=hint) then begin kn^.kn_kevent.data:=1; Result:=1; end; end; end; const filterops_internal:t_filterops=( f_isfd :0; f_attach:@filterops_internal_attach; f_detach:@filterops_internal_detach; f_event :@filterops_internal_event; ); procedure gc_init_internal(); begin mtx_init(gc_internal_knlock,'gc internal kn lock'); knlist_init_mtx(@gc_internal_knlist,@gc_internal_knlock); end; function gc_add_internal_ptr(kq,ptr,udata:Pointer):Integer; register; [public, alias:'gc_add_internal_ptr']; var kev:t_kevent; fops:p_filterops; begin kev:=Default(t_kevent); kev.ident:=PtrUint(ptr); kev.flags:=EV_ADD; kev.udata:=udata; // fops:=@filterops_internal; Result:=kqueue_register2(kq,@kev,fops); end; function gc_del_internal_ptr(kq,ptr:Pointer):Integer; register; [public, alias:'gc_del_internal_ptr']; var kev:t_kevent; fops:p_filterops; begin kev:=Default(t_kevent); kev.ident:=PtrUint(ptr); kev.flags:=EV_DELETE; // fops:=@filterops_internal; Result:=kqueue_register2(kq,@kev,fops); end; procedure gc_wakeup_internal_ptr(ptr:Pointer); register; [public, alias:'gc_wakeup_internal_ptr']; begin knote(@gc_internal_knlist, PtrUint(ptr), 0); end; //// procedure gc_initialize(); begin gc_page:=dev_mem_alloc(1); make_dev(@gc_cdevsw,0,0,0,&666,'gc',[]); mtx_init(gc_knlock,'gc kn lock'); knlist_init_mtx(@gc_knlist,@gc_knlock); gc_init_internal(); kqueue_add_filteropts(EVFILT_GRAPHICS_CORE,@filterops_graphics_core); gc_ring_create(@ring_gfx,GC_RING_SIZE); pm4_me_gfx.Init(@gc_knlist); pm4_me_gfx.on_idle:=@gc_idle; pm4_me_gfx.on_submit_flip_eop:=@dev_dce.TriggerFlipEop; end; end.