unit vShaderExt; {$mode objfpc}{$H+} interface uses Classes, SysUtils, ps4_shader, vm_mmap, vRegs2Vulkan, Vulkan, vDevice, vPipeline, vShader, vImage, vSetLayoutManager, vPipelineLayoutManager, si_ci_vi_merged_enum, si_ci_vi_merged_registers, si_ci_vi_merged_groups; type TvResourceType=( vtRoot, vtImmData, vtBufPtr2, vtFunPtr2, vtVSharp2, vtVSharp4, vtSSharp4, vtTSharp4, vtTSharp8, vtLDS, vtGDS ); TvDescriptorType=( dtVTX_ATR, //VERTEX ATTRIBUTE dtSAMPLER, //VK_DESCRIPTOR_TYPE_SAMPLER dtSAM_IMG, //VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE dtSTR_IMG, //VK_DESCRIPTOR_TYPE_STORAGE_IMAGE dtRNT_IMG, // dtUTX_BUF, //VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER dtSTX_BUF, //VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER dtRTX_BUF, // dtUNF_BUF, //VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER dtSTR_BUF, //VK_DESCRIPTOR_TYPE_STORAGE_BUFFER dtPSH_CST //PUSH CONSTANT ); TrDstSel=bitpacked record x,y,z,w:0..15; //(0..7) end; TvResInfo=bitpacked record enable :Boolean; //1 -> dfmt,nfmt,dstsel invalid:Boolean; //1 dfmt :0..63; //6 nfmt :0..15; //4 rtype :0..15; //4 dstsel :TrDstSel; //16 degam :Boolean; //1 _align :0..32767; //15 stride :Word; //16 end; {$IF sizeof(TvResInfo)<>8}{$STOP sizeof(TvResInfo)<>8}{$ENDIF} TvDataLayout=packed record rtype :TvResourceType; offset:DWORD; rinfo :TvResInfo; end; ADataLayout=array of TvDataLayout; TvFuncCb=procedure(addr:ADataLayout) of object; TvLayoutFlags=Set of (vMemoryRead,vMemoryWrite,vMipArray,vForceDegamma); PvCustomLayout=^TvCustomLayout; TvCustomLayout=packed object dtype :TvDescriptorType; bind :DWORD; size :DWORD; offset:DWORD; flags :TvLayoutFlags; addr :ADataLayout; function GetVulkanDescType:TVkDescriptorType; end; ACustomLayout=array of TvCustomLayout; TvCustomLayoutCb=procedure(const L:TvCustomLayout;Fset:TVkUInt32;pUserData,pImmData:PDWORD) of object; TvShaderParserExt=class(TvShaderParser) type t_context_state=(cNone,cData,cDesc,cCache); PvDataLayout2=^TvDataLayout2; TvDataLayout2=packed record state :t_context_state; // rtype :TvResourceType; dtype :TvDescriptorType; // bind :DWORD; //Desc Layout size :DWORD; //Desc/Data Layout offset:DWORD; //Desc/Data Layout flags :TvLayoutFlags; //Desc Layout rinfo :TvResInfo; //Data Layout imm :array of DWORD; //Data Layout immofs:Boolean; //Data Layout end; ADataLayout2=array of TvDataLayout2; var FDataStack:ADataLayout2; FINPUT_CNTL_ID :Integer; FEXPORT_INFO_ID:Integer; procedure Parse(data:Pointer;size:Ptruint); override; procedure OnDescriptorSet(var Target,id:DWORD); override; procedure OnSourceExtension(P:PChar); override; procedure CloseData(c_deep:Integer;c_header:Boolean); procedure PushData (const N:RawByteString); procedure PushDesc (const N:RawByteString); procedure PushCache(const N:RawByteString); procedure OnValue (const N,V:RawByteString); function GetLayoutAddr:ADataLayout; procedure PopData (L:PvDataLayout2); procedure PopDesc (L:PvDataLayout2); end; A_INPUT_CNTL=array[0..31] of TSPI_PS_INPUT_CNTL_0; PRENDER_TARGET=^TRENDER_TARGET; TEXPORT_COLOR=packed record FORMAT :Byte; NUMBER_TYPE:Byte; COMP_SWAP :Byte; end; AEXPORT_COLOR=array[0..7] of TEXPORT_COLOR; TImmData=array of DWORD; TvShaderExt=class(TvShader) FDescSetId:Integer; FHash_gcn:QWORD; FHash_spv:QWORD; FSetLayout:TvSetLayout; FVertLayouts:ACustomLayout; FUnifLayouts:ACustomLayout; FFuncLayouts:ACustomLayout; FPushConst:TvCustomLayout; FImmData:TImmData; FParams:record VGPR_COMP_CNT:Byte; // NUM_INTERP :Byte; EXPORT_COUNT :Byte; STEP_RATE_0:DWORD; STEP_RATE_1:DWORD; // SHADER_CONTROL:TDB_SHADER_CONTROL; INPUT_CNTL :A_INPUT_CNTL; EXPORT_COLOR :AEXPORT_COLOR; // NUM_THREAD_X:TCOMPUTE_NUM_THREAD_X; NUM_THREAD_Y:TCOMPUTE_NUM_THREAD_Y; NUM_THREAD_Z:TCOMPUTE_NUM_THREAD_Z; end; FGeomRectList:TvShaderExt; procedure ClearInfo; override; Destructor Destroy; override; function parser:CvShaderParser; override; procedure InitSetLayout; procedure AddToPipeline(p:TvPipelineLayout); Procedure EnumFuncLayout(cb:TvFuncCb); Procedure AddVertLayout2(addr:ADataLayout;bind:DWORD); Procedure EnumVertLayout(cb:TvCustomLayoutCb;Fset:TVkUInt32;pUserData,pImmData:PDWORD); Procedure AddBuffLayout2(dtype:TvDescriptorType; addr:ADataLayout; bind,size,offset:DWORD; flags:TvLayoutFlags); Procedure SetPushConst2(addr:ADataLayout;size:DWORD); Function GetPushConstData(pUserData:Pointer):Pointer; Procedure AddUnifLayout2(dtype:TvDescriptorType; addr:ADataLayout; bind:DWORD; flags:TvLayoutFlags); Procedure EnumUnifLayout(cb:TvCustomLayoutCb;Fset:TVkUInt32;pUserData,pImmData:PDWORD); Procedure AddFuncLayout2(addr:ADataLayout;size:DWORD;imm:TImmData); function InsertImm(imm:TImmData):DWORD; Procedure EnumFuncLayout(cb:TvCustomLayoutCb;Fset:TVkUInt32;pUserData,pImmData:PDWORD); Procedure AddImmData(D:DWORD); function GetImmData:PDWORD; function IsPSSimpleShader:Boolean; function IsVSSimpleShader:Boolean; function IsCSClearShader:Boolean; function IsVSRectListShader:Boolean; end; TBufBindExt=packed record fset :TVkUInt32; bind :TVkUInt32; offset:TVkUInt32; memuse:TVkUInt32; addr :Pointer; size :TVkUInt32; end; TvBindImageType=(vbSampled,vbStorage,vbMipStorage); TImageBindExt=packed record btype :TvBindImageType; fset :TVkUInt32; bind :TVkUInt32; memuse:TVkUInt32; FImage:TvImageKey; FView :TvImageViewKey; end; TSamplerBindExt=packed record fset:TVkUInt32; bind:TVkUInt32; PS:PSSharpResource4; end; TvUniformBuilder=object FBuffers :array of TBufBindExt; FImages :array of TImageBindExt; FSamplers:array of TSamplerBindExt; Procedure AddVSharp2(PV:PVSharpResource2;fset,bind,size,offset:DWord;flags:TvLayoutFlags); Procedure AddVSharp4(PV:PVSharpResource4;fset,bind,size,offset:DWord;flags:TvLayoutFlags); Procedure AddBufPtr (P:Pointer ;fset,bind,size,offset:DWord;flags:TvLayoutFlags); Procedure AddTSharp4(PT:PTSharpResource4;btype:TvBindImageType;fset,bind:DWord;flags:TvLayoutFlags); Procedure AddTSharp8(PT:PTSharpResource8;btype:TvBindImageType;fset,bind:DWord;flags:TvLayoutFlags); Procedure AddSSharp4(PS:PSSharpResource4;fset,bind:DWord); procedure AddAttr (const b:TvCustomLayout;Fset:TVkUInt32;pUserData,pImmData:PDWORD); end; AvShaderStage=array[TvShaderStage] of TvShaderExt; PvShadersKey=^TvShadersKey; TvShadersKey=object FShaders:AvShaderStage; FPrimtype:Integer; Procedure SetLSShader(Shader:TvShaderExt); Procedure SetHSShader(Shader:TvShaderExt); Procedure SetESShader(Shader:TvShaderExt); Procedure SetGSShader(Shader:TvShaderExt); Procedure SetVSShader(Shader:TvShaderExt); Procedure SetPSShader(Shader:TvShaderExt); Procedure SetCSShader(Shader:TvShaderExt); procedure ExportLayout(var A:AvSetLayout;var B:AvPushConstantRange); Procedure ExportStages(Stages:PVkPipelineShaderStageCreateInfo;stageCount:PVkUInt32); end; TvBindVertexBuffer=packed object min_addr:Pointer; binding :Word; stride :Word; count :TVkUInt32; Function GetSize:TVkUInt32; end; AvVertexInputBindingDescription =array[0..31] of TVkVertexInputBindingDescription; AvBindVertexBuffer =array[0..31] of TvBindVertexBuffer; AvVertexInputAttributeDescription =array[0..31] of TVkVertexInputAttributeDescription; AvVertexInputBindingDescription2 =array[0..31] of TVkVertexInputBindingDescription2EXT; AvVertexInputAttributeDescription2=array[0..31] of TVkVertexInputAttributeDescription2EXT; TvVertexInputEXT=record vertexBindingDescriptionCount :TVkUInt32; vertexAttributeDescriptionCount:TVkUInt32; VertexBindingDescriptions :AvVertexInputBindingDescription2; VertexAttributeDescriptions :AvVertexInputAttributeDescription2; end; TvAttrBuilder=object const maxVertexInputBindingStride=16383; maxVertexInputBindings =32; maxVertexInputAttributes =32; var FBindDescsCount:Byte; FAttrDescsCount:Byte; FBindDescs:AvVertexInputBindingDescription; FBindVBufs:AvBindVertexBuffer; FAttrDescs:AvVertexInputAttributeDescription; // function NewBindDesc(binding,stride,count:TVkUInt32;base:Pointer):TVkUInt32; procedure NewAttrDesc(location,binding,offset:TVkUInt32;format:TVkFormat); procedure PatchAttr(binding,offset:TVkUInt32); Procedure AddVSharp(PV:PVSharpResource4;location:DWord); procedure AddAttr(const v:TvCustomLayout;Fset:TVkUInt32;pUserData,pImmData:PDWORD); Procedure Export2(var input:TvVertexInputEXT); end; TvUnifChecker=object FResult:Boolean; procedure AddAttr(const b:TvCustomLayout;Fset:TVkUInt32;pUserData,pImmData:PDWORD); end; TvShaderGroup=class FKey :TvShadersKey; FLayout:TvPipelineLayout; Procedure Clear; Function Compile:Boolean; Procedure ExportAttrBuilder(var AttrBuilder :TvAttrBuilder ;GPU_USERDATA:PGPU_USERDATA); Procedure ExportUnifBuilder(var UniformBuilder:TvUniformBuilder;GPU_USERDATA:PGPU_USERDATA); end; function GetSharpByPatch(pUserData,pImmData:Pointer;const addr:ADataLayout):Pointer; function IsClearDepthShaders(const FShaders:AvShaderStage):Boolean; inline; implementation uses kern_dmem; function TvCustomLayout.GetVulkanDescType:TVkDescriptorType; begin case dtype of dtSAMPLER:Result:=VK_DESCRIPTOR_TYPE_SAMPLER; dtSAM_IMG:Result:=VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; dtSTR_IMG:Result:=VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; dtUTX_BUF:Result:=VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; dtSTX_BUF:Result:=VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; dtUNF_BUF:Result:=VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; dtSTR_BUF:Result:=VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; else Assert(false); end; end; { function TShaderFuncKey.c(var a,b:TShaderFuncKey):Integer; begin //1 FLen Result:=Integer((a.FLen>b.FLen) and (b.FLen<>0))-Integer((a.FLen0)); if (Result<>0) then Exit; //2 pData Result:=CompareDWord(a.pData^,b.pData^,Max(a.FLen,b.FLen) div 4); end; } Function TvBindVertexBuffer.GetSize:TVkUInt32; begin if (stride=0) then begin Result:=count; end else begin Result:=stride*count; end; end; procedure TvShaderExt.ClearInfo; begin inherited; //dont clear FDescSetId FSetLayout:=nil; FVertLayouts:=Default(ACustomLayout); FUnifLayouts:=Default(ACustomLayout); FFuncLayouts:=Default(ACustomLayout); FPushConst:=Default(TvCustomLayout); SetLength(FImmData,0); end; Destructor TvShaderExt.Destroy; begin inherited; end; function TvShaderExt.parser:CvShaderParser; begin Result:=TvShaderParserExt; end; procedure TvShaderExt.InitSetLayout; var i,p:Integer; descriptorCount:TVkUInt32; A:AVkDescriptorSetLayoutBinding; begin if (FSetLayout<>nil) then Exit; A:=Default(AVkDescriptorSetLayoutBinding); //++ other todo SetLength(A, Length(FUnifLayouts) ); p:=0; if (Length(FUnifLayouts)<>0) then begin For i:=0 to High(FUnifLayouts) do begin if (vMipArray in FUnifLayouts[i].flags) then begin descriptorCount:=16; end else begin descriptorCount:=1; end; // A[p]:=Default(TVkDescriptorSetLayoutBinding); A[p].binding :=FUnifLayouts[i].bind; A[p].descriptorType :=FUnifLayouts[i].GetVulkanDescType; A[p].descriptorCount:=descriptorCount; A[p].stageFlags :=ord(FStage); // Inc(p); end; end; FSetLayout:=FetchSetLayout(ord(FStage),0,A); end; procedure TvShaderExt.AddToPipeline(p:TvPipelineLayout); begin InitSetLayout; p.AddLayout(FSetLayout); if (FPushConst.size<>0) then begin p.AddPushConst(0,FPushConst.size,ord(FStage)); end; end; procedure TvShaderParserExt.Parse(data:Pointer;size:Ptruint); begin FEXPORT_INFO_ID:=-1; inherited; CloseData(0,True); end; procedure TvShaderParserExt.OnDescriptorSet(var Target,id:DWORD); begin with TvShaderExt(FOwner) do begin if (FDescSetId>=0) then id:=FDescSetId; end; end; function _get_hex_dword(P:PChar):DWord; var Error:word; s:string[9]; begin s[0]:=#9; s[1]:='$'; PQWORD(@s[2])^:=PQWORD(P)^; Result:=0; Val(s,Result,Error); end; function _get_hex_char(P:PChar):DWord; begin case P^ of '0'..'9':Result:=ord(P^)-ord('0'); 'A'..'F':Result:=ord(P^)-ord('A')+$A; end; end; Procedure AddToCustomLayout(var A:ACustomLayout;const v:TvCustomLayout); begin Insert(v,A,Length(A)); end; Procedure AddToDataLayout(var A:ADataLayout;const v:TvDataLayout); begin Insert(v,A,Length(A)); end; procedure TvShaderParserExt.OnSourceExtension(P:PChar); label _state2; var curr :PChar; c_state:Integer; c_deep :Integer; c_hdtp :AnsiChar; c_name :RawByteString; begin c_state:=0; c_deep :=0; c_hdtp :=#0; c_name :=''; //Writeln(P); curr:=P; while (curr^<>#0) do begin case c_state of 0: begin case curr^ of #9,' ': begin Inc(c_deep); end; '#','+','-','*','%': begin //header Inc(c_deep); c_hdtp :=curr^; c_state:=1; end; else begin c_state:=2; goto _state2; end; end; end; 1: //[ ][ ][#][HEADER] begin CloseData(c_deep,True); case c_hdtp of '#':PushData(Trim(curr)); '+':PushDesc(Trim(curr)); '-':PushCache(Trim(curr)); '*':PushDesc(Trim(curr)); '%':PushDesc(Trim(curr)); else; end; Exit; end; 2: //[ ][ ][NAME] begin _state2: case curr^ of ':': begin c_state:=3; end; else begin c_name:=c_name+curr^; end; end; end; 3: //[ ][ ][NAME][:][VALUE] begin CloseData(c_deep,False); OnValue(Trim(c_name),Trim(curr)); Exit; end; else Assert(false); end; Inc(curr); end; end; { +R +VTX +VTX +F +S V G +B } procedure TvShaderParserExt.CloseData(c_deep:Integer;c_header:Boolean); Procedure Pop; var L:PvDataLayout2; begin L:=@FDataStack[High(FDataStack)]; // case L^.state of cData:PopData(L); cDesc:PopDesc(L); else; end; // L^:=Default(TvDataLayout2); // SetLength(FDataStack,High(FDataStack)); end; begin if c_header then begin while (Length(FDataStack)<>0) and (c_deep<=Length(FDataStack)) do begin Pop; end; end else begin while (Length(FDataStack)<>0) and (c_deep0); 'INVL':L^.rinfo.invalid:=(StrToDWord2(V)<>0); 'DFMT':L^.rinfo.dfmt :=StrToDWord2(V); 'NFMT':L^.rinfo.nfmt :=StrToDWord2(V); 'STRD':L^.rinfo.stride :=StrToDWord2(V); 'TYPE':L^.rinfo.rtype :=StrToDWord2(V); 'DSEL':L^.rinfo.dstsel :=StrToDstSel(V); 'FDGM':L^.rinfo.degam :=(StrToDWord2(V)<>0); 'IMM':Insert(StrToDWord2(V),L^.imm,Length(L^.imm)); 'VGPR_COMP_CNT': with TvShaderExt(FOwner) do begin FParams.VGPR_COMP_CNT:=StrToDWord2(V); end; 'VGT_STEP_RATE_0': with TvShaderExt(FOwner) do begin FParams.STEP_RATE_0:=StrToDWord2(V); end; 'VGT_STEP_RATE_1': with TvShaderExt(FOwner) do begin FParams.STEP_RATE_1:=StrToDWord2(V); end; 'DB_SHADER_CONTROL': with TvShaderExt(FOwner) do begin DWORD(FParams.SHADER_CONTROL):=StrToDWord2(V); end; 'PS_NUM_INTERP': with TvShaderExt(FOwner) do begin FParams.NUM_INTERP:=StrToDWord2(V); end; 'PS_INPUT_CNTL': with TvShaderExt(FOwner) do begin if (FINPUT_CNTL_ID=0) then if (FEXPORT_INFO_ID=0) then if (FEXPORT_INFO_ID=0) then if (FEXPORT_INFO_ID0 then For i:=High(FDataStack) downto 0 do begin L:=@FDataStack[i]; if (L^.state=cData) then begin //alloc offset if (L^.rtype=vtImmData) then if (not L^.immofs) then begin with TvShaderExt(FOwner) do begin L^.offset:=InsertImm(L^.imm); end; L^.immofs:=True; end; D.rtype :=L^.rtype; D.offset:=L^.offset; D.rinfo :=L^.rinfo; Insert(D,Result,Length(Result)); end; end; end; procedure TvShaderParserExt.PopData(L:PvDataLayout2); begin case L^.rtype of vtFunPtr2: begin with TvShaderExt(FOwner) do begin AddFuncLayout2(Self.GetLayoutAddr,L^.size,L^.imm); end; end; else; end; end; procedure TvShaderParserExt.PopDesc(L:PvDataLayout2); begin with TvShaderExt(FOwner) do case L^.dtype of dtVTX_ATR:AddVertLayout2(Self.GetLayoutAddr,L^.bind); dtSAMPLER:AddUnifLayout2(L^.dtype,Self.GetLayoutAddr,L^.bind,L^.flags); dtSAM_IMG:AddUnifLayout2(L^.dtype,Self.GetLayoutAddr,L^.bind,L^.flags); dtSTR_IMG:AddUnifLayout2(L^.dtype,Self.GetLayoutAddr,L^.bind,L^.flags); dtRNT_IMG:AddUnifLayout2(L^.dtype,Self.GetLayoutAddr,L^.bind,L^.flags); dtUTX_BUF:Assert(False,'TODO:UTX_BUF'); dtSTX_BUF:Assert(False,'TODO:STX_BUF'); dtRTX_BUF:Assert(False,'TODO:RTX_BUF'); dtUNF_BUF:AddBuffLayout2(L^.dtype,Self.GetLayoutAddr,L^.bind,L^.size,L^.offset,L^.flags); dtSTR_BUF:AddBuffLayout2(L^.dtype,Self.GetLayoutAddr,L^.bind,L^.size,L^.offset,L^.flags); dtPSH_CST:SetPushConst2(Self.GetLayoutAddr,L^.size); else; end; end; Procedure TvShaderExt.EnumFuncLayout(cb:TvFuncCb); var i:Integer; begin if (cb=nil) then Exit; if (Length(FFuncLayouts)=0) then Exit; For i:=0 to High(FFuncLayouts) do begin cb(FFuncLayouts[i].addr); end; end; Procedure TvShaderExt.AddVertLayout2(addr:ADataLayout;bind:DWORD); var v:TvCustomLayout; begin v:=Default(TvCustomLayout); v.bind :=bind; v.addr :=addr; v.flags:=[vMemoryRead]; AddToCustomLayout(FVertLayouts,v); end; Procedure TvShaderExt.EnumVertLayout(cb:TvCustomLayoutCb;Fset:TVkUInt32;pUserData,pImmData:PDWORD); var i:Integer; begin if (cb=nil) then Exit; if (Length(FVertLayouts)=0) then Exit; For i:=0 to High(FVertLayouts) do begin cb(FVertLayouts[i],Fset,pUserData,pImmData); end; end; Procedure TvShaderExt.AddBuffLayout2(dtype:TvDescriptorType; addr:ADataLayout; bind,size,offset:DWORD; flags:TvLayoutFlags); var v:TvCustomLayout; begin v:=Default(TvCustomLayout); v.dtype :=dtype; v.bind :=bind; v.size :=size; v.offset:=offset; v.flags :=flags; v.addr :=addr; AddToCustomLayout(FUnifLayouts,v); end; Procedure TvShaderExt.SetPushConst2(addr:ADataLayout;size:DWORD); begin FPushConst:=Default(TvCustomLayout); FPushConst.dtype:=dtPSH_CST; FPushConst.size :=size; FPushConst.addr :=addr; end; Function TvShaderExt.GetPushConstData(pUserData:Pointer):Pointer; begin Result:=nil; if (pUserData=nil) then Exit; if (FPushConst.size=0) then Exit; Result:=GetSharpByPatch(pUserData,GetImmData,FPushConst.addr); if (Result=nil) then Exit; Case FPushConst.addr[0].rtype of vtVSharp2, vtVSharp4:Result:=Pointer(PVSharpResource4(Result)^.base and (not 3)); vtTSharp4, vtTSharp8:Result:=Pointer(QWORD(PTSharpResource4(Result)^.base) shl 8); else; end; end; Procedure TvShaderExt.AddUnifLayout2(dtype:TvDescriptorType; addr:ADataLayout; bind:DWORD; flags:TvLayoutFlags); var v:TvCustomLayout; begin v:=Default(TvCustomLayout); v.dtype:=dtype; v.bind :=bind; v.flags:=flags; v.addr :=addr; AddToCustomLayout(FUnifLayouts,v); end; Procedure TvShaderExt.EnumUnifLayout(cb:TvCustomLayoutCb;Fset:TVkUInt32;pUserData,pImmData:PDWORD); var i:Integer; begin if (cb=nil) then Exit; if (Length(FUnifLayouts)=0) then Exit; For i:=0 to High(FUnifLayouts) do begin cb(FUnifLayouts[i],Fset,pUserData,pImmData); end; end; Procedure TvShaderExt.AddFuncLayout2(addr:ADataLayout;size:DWORD;imm:TImmData); var v:TvCustomLayout; begin v:=Default(TvCustomLayout); v.size :=size; v.addr :=addr; v.offset:=InsertImm(imm); AddToCustomLayout(FFuncLayouts,v); end; function TvShaderExt.InsertImm(imm:TImmData):DWORD; begin Result:=Length(FImmData)*SizeOf(DWORD); // Insert(imm,FImmData,Length(FImmData)); end; Procedure TvShaderExt.EnumFuncLayout(cb:TvCustomLayoutCb;Fset:TVkUInt32;pUserData,pImmData:PDWORD); var i:Integer; begin if (cb=nil) then Exit; if (Length(FFuncLayouts)=0) then Exit; For i:=0 to High(FFuncLayouts) do begin cb(FFuncLayouts[i],Fset,pUserData,pImmData); end; end; Procedure TvShaderExt.AddImmData(D:DWORD); begin Insert(D,FImmData,Length(FImmData)); end; function TvShaderExt.GetImmData:PDWORD; begin Result:=@FImmData[0]; end; // function TvShaderExt.IsPSSimpleShader:Boolean; begin if (self=nil) then Exit(False); Result:=(FHash_gcn=QWORD($E9FF5D4699E5B9AD)); end; function TvShaderExt.IsVSSimpleShader:Boolean; begin if (self=nil) then Exit(False); Result:=(FHash_gcn=QWORD($00DF6E6331449451)); end; function TvShaderExt.IsCSClearShader:Boolean; begin if (self=nil) then Exit(False); Result:=(FHash_gcn=QWORD($7DCE68F83F66B337)); end; function TvShaderExt.IsVSRectListShader:Boolean; begin if (self=nil) then Exit(False); Result:=(FHash_gcn=QWORD($00DF6E6331449451)); end; function IsClearDepthShaders(const FShaders:AvShaderStage):Boolean; inline; begin Result:=False; if (FShaders[vShaderStageLs]=nil) and (FShaders[vShaderStageHs]=nil) and (FShaders[vShaderStageEs]=nil) and (FShaders[vShaderStageGs]=nil) and (FShaders[vShaderStageCs]=nil) then if (FShaders[vShaderStageVs].IsVSSimpleShader) and ( FShaders[vShaderStagePs].IsPSSimpleShader or (FShaders[vShaderStagePs]=nil) ) then begin Result:=True; end; end; /// function GetSharpByPatch(pUserData,pImmData:Pointer;const addr:ADataLayout):Pointer; var i:Integer; pData :Pointer; pSharp:Pointer; pDmem :Pointer; begin Result:=nil; if (Length(addr)=0) then Exit; pData :=pUserData; pSharp:=pUserData; pDmem :=pUserData; For i:=High(addr) downto 0 do begin pData:=pData+addr[i].offset; pDmem:=pDmem+addr[i].offset; Case addr[i].rtype of vtRoot: begin pSharp:=pData; pDmem :=pData; end; vtImmData: begin pData :=pImmData+addr[i].offset; pSharp:=pData; pDmem :=pData; end; vtBufPtr2: begin pData:=Pointer(PPtrUint(pDmem)^ and (not 3)); pDmem:=get_dmem_ptr(pData); pSharp:=pData; end; vtFunPtr2: begin pData:=PPointer(pDmem)^; pDmem:=get_dmem_ptr(pData); pSharp:=pData; end; vtVSharp2, vtVSharp4: begin pSharp:=pData; if (i<>0) then begin pData:=Pointer(PVSharpResource4(pDmem)^.base and (not 3)); pDmem:=get_dmem_ptr(pData); end; end; vtSSharp4: begin pSharp:=pData; Break; end; vtTSharp4, vtTSharp8: begin pSharp:=pData; if (i<>0) then begin pData:=Pointer(QWORD(PTSharpResource4(pDmem)^.base) shl 8); pDmem:=get_dmem_ptr(pData); end; end; else Assert(false,'GetSharpByPatch'); end; end; Result:=pSharp; end; // function TvAttrBuilder.NewBindDesc(binding,stride,count:TVkUInt32;base:Pointer):TVkUInt32; var i:Byte; begin if (FBindDescsCount>=maxVertexInputBindings) then begin Assert(false,'maxVertexInputBindings'); end; i:=FBindDescsCount; FBindDescsCount:=FBindDescsCount+1; FBindVBufs[i].min_addr:=base; FBindVBufs[i].binding :=binding; FBindVBufs[i].stride :=stride; FBindVBufs[i].count :=count; FBindDescs[i].binding :=binding; FBindDescs[i].stride :=stride; FBindDescs[i].inputRate:=VK_VERTEX_INPUT_RATE_VERTEX; Result:=i; end; procedure TvAttrBuilder.NewAttrDesc(location,binding,offset:TVkUInt32;format:TVkFormat); var i:Integer; begin if (FAttrDescsCount>=maxVertexInputAttributes) then begin Assert(false,'maxVertexInputAttributes'); end; i:=FAttrDescsCount; FAttrDescsCount:=FAttrDescsCount+1; FAttrDescs[i].location:=location; FAttrDescs[i].binding :=binding ; FAttrDescs[i].format :=format ; FAttrDescs[i].offset :=offset ; end; procedure TvAttrBuilder.PatchAttr(binding,offset:TVkUInt32); var i:Integer; begin if (FAttrDescsCount<>0) then For i:=0 to FAttrDescsCount-1 do if (FAttrDescs[i].binding=binding) then begin FAttrDescs[i].offset:=FAttrDescs[i].offset+offset; end; end; function _ptr_diff(p1,p2:Pointer):TVkUInt32; inline; begin if (p1>p2) then Result:=p1-p2 else Result:=p2-p1; end; Procedure TvAttrBuilder.AddVSharp(PV:PVSharpResource4;location:DWord); var pv_base :Pointer; pv_stride:TVkUInt32; pv_count :TVkUInt32; offset :TVkUInt32; i:Integer; begin if (PV=nil) then Exit; Assert(PV^.dfmt<>0,'AddVSharp:invalid dfmt!'); pv_base :=Pointer(PV^.base and (not 3)); pv_stride:=PV^.stride; pv_count :=PV^.num_records; if (FBindDescsCount<>0) then For i:=0 to FBindDescsCount-1 do begin With FBindVBufs[i] do begin //If the element's stride is the same, // add the attribute to the binding if (stride=pv_stride) then begin //Let's calculate the difference in addresses // between the binding and the new buffer offset:=_ptr_diff(min_addr,pv_base); //If the difference is greater than the stride, // then we skip if (offset<=stride-1) then begin //If the offset is negative relative // to the base, then if (min_addr>pv_base) then begin //We patch the previous attributes, // adjust the offset PatchAttr(binding,offset); //This is now the new base address, // so reset the offset. min_addr:=pv_base; offset :=0; end; //update count if (count0) then For i:=0 to FBindDescsCount-1 do begin input.VertexBindingDescriptions[i].sType :=VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT; input.VertexBindingDescriptions[i].binding :=FBindDescs[i].binding; input.VertexBindingDescriptions[i].stride :=FBindDescs[i].stride; input.VertexBindingDescriptions[i].inputRate:=FBindDescs[i].inputRate; input.VertexBindingDescriptions[i].divisor :=1; end; if (FAttrDescsCount<>0) then For i:=0 to FAttrDescsCount-1 do begin input.VertexAttributeDescriptions[i].sType :=VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT; input.VertexAttributeDescriptions[i].location:=FAttrDescs[i].location; input.VertexAttributeDescriptions[i].binding :=FAttrDescs[i].binding; input.VertexAttributeDescriptions[i].format :=FAttrDescs[i].format; input.VertexAttributeDescriptions[i].offset :=FAttrDescs[i].offset; end; end; // function _get_buf_mem_usage(flags:TvLayoutFlags):Byte; inline; begin Result:=(ord(vMemoryRead in flags)*TM_READ) or (ord(vMemoryWrite in flags)*TM_WRITE); end; Procedure TvUniformBuilder.AddVSharp2(PV:PVSharpResource2;fset,bind,size,offset:DWord;flags:TvLayoutFlags); var b:TBufBindExt; stride:Integer; base,start,__end,_size:QWORD; begin Assert(PV<>nil); if (PV=nil) then Exit; //print_vsharp(PVSharpResource4(PV)); b:=Default(TBufBindExt); b.fset :=fset; b.bind :=bind; b.offset:=offset; b.memuse:=_get_buf_mem_usage(flags); b.addr:=Pointer(PV^.base and (not 3)); stride:=PV^.stride; if (stride=0) then stride:=1; //size is unknow, try 4KB base :=QWORD(get_dmem_ptr(b.addr)); start:=base; __end:=base+4*1024; gpu_get_bound(start,__end); if (start=0) then begin Assert(false,'vtVSharp2'); end; _size:=(__end-base); _size:=_size+offset; //take into account the offset inside the shader if (_size>4*1024) then begin _size:=4*1024; end; // b.size:=_size; Insert(b,FBuffers,Length(FBuffers)); end; function IsInvalidVSharp(dfmt,num_records:DWORD):Boolean; inline; begin Result:=(dfmt=0) or (num_records=0); end; Procedure TvUniformBuilder.AddVSharp4(PV:PVSharpResource4;fset,bind,size,offset:DWord;flags:TvLayoutFlags); var b:TBufBindExt; stride,num_records:Integer; invalid:Integer; begin Assert(PV<>nil); if (PV=nil) then Exit; //print_vsharp(PV); invalid:=ord(IsInvalidVSharp(PV^.dfmt,PV^.num_records))*TM_INVAL; b:=Default(TBufBindExt); b.fset :=fset; b.bind :=bind; b.offset:=offset; b.memuse:=_get_buf_mem_usage(flags) or invalid; b.addr:=Pointer(PV^.base and (not 3)); stride :=PV^.stride; num_records:=PV^.num_records; // if (stride=0) then stride:=1; if (num_records=0) then num_records:=1; // b.size:=(stride*num_records)+offset; //take into account the offset inside the shader // if (b.size>size) then b.size:=size; //input size already taking into account offset Insert(b,FBuffers,Length(FBuffers)); end; Procedure TvUniformBuilder.AddBufPtr(P:Pointer;fset,bind,size,offset:DWord;flags:TvLayoutFlags); var b:TBufBindExt; begin Assert(P<>nil); if (P=nil) or (size=0) then Exit; b:=Default(TBufBindExt); b.fset :=fset; b.bind :=bind; b.offset:=offset; b.memuse:=_get_buf_mem_usage(flags); b.addr:=P; b.size:=size; //input size already taking into account offset Insert(b,FBuffers,Length(FBuffers)); end; Procedure TvUniformBuilder.AddTSharp4(PT:PTSharpResource4;btype:TvBindImageType;fset,bind:DWord;flags:TvLayoutFlags); var b:TImageBindExt; hint:s_image_usage; //start,__end:QWORD; begin Assert(PT<>nil); if (PT=nil) then Exit; //print_tsharp4(PT); b:=Default(TImageBindExt); b.btype :=btype; b.fset :=fset; b.bind :=bind; b.memuse:=_get_buf_mem_usage(flags); hint:=[]; if (vForceDegamma in flags) then begin hint:=hint+[iu_degamma]; end; if (btype in [vbStorage,vbMipStorage]) then begin hint:=hint+[iu_storage]; end; b.FImage:=_get_tsharp4_image_info(PT,hint); b.FView :=_get_tsharp4_image_view(PT,hint); { //Marking textures that contain incorrect // virtual memory as invalid, // I don't know how correct this approach is start:=QWORD(get_dmem_ptr(b.FImage.Addr)); __end:=start+1; gpu_get_bound(start,__end); if (start=0) then begin Writeln(HexStr(QWORD(b.FImage.Addr),10),'->INVALID'); b.FImage.params.invalid:=1; end; } b.memuse:=b.memuse or (b.FImage.params.invalid)*TM_INVAL; Insert(b,FImages,Length(FImages)); end; Procedure TvUniformBuilder.AddTSharp8(PT:PTSharpResource8;btype:TvBindImageType;fset,bind:DWord;flags:TvLayoutFlags); var b:TImageBindExt; hint:s_image_usage; //start,__end:QWORD; begin Assert(PT<>nil); if (PT=nil) then Exit; //print_tsharp8(PT); b:=Default(TImageBindExt); b.btype :=btype; b.fset :=fset; b.bind :=bind; b.memuse:=_get_buf_mem_usage(flags); hint:=[]; if (vForceDegamma in flags) then begin hint:=hint+[iu_degamma]; end; if (btype in [vbStorage,vbMipStorage]) then begin hint:=hint+[iu_storage]; end; b.FImage:=_get_tsharp8_image_info(PT,hint); b.FView :=_get_tsharp8_image_view(PT,hint); { //Marking textures that contain incorrect // virtual memory as invalid, // I don't know how correct this approach is start:=QWORD(get_dmem_ptr(b.FImage.Addr)); __end:=start+1; gpu_get_bound(start,__end); if (start=0) then begin Writeln(HexStr(QWORD(b.FImage.Addr),10),'->INVALID'); b.FImage.params.invalid:=1; end; } b.memuse:=b.memuse or (b.FImage.params.invalid)*TM_INVAL; Insert(b,FImages,Length(FImages)); end; procedure TvUniformBuilder.AddAttr(const b:TvCustomLayout;Fset:TVkUInt32;pUserData,pImmData:PDWORD); var P:Pointer; begin P:=GetSharpByPatch(pUserData,pImmData,b.addr); Assert(P<>nil); if (P=nil) then Exit; Case b.dtype of dtSAMPLER: Case b.addr[0].rtype of vtSSharp4:AddSSharp4(P,fset,b.bind); else Assert(false,'AddAttr'); end; // dtSAM_IMG: Case b.addr[0].rtype of vtTSharp4:AddTSharp4(P,vbSampled,fset,b.bind,b.flags); vtTSharp8:AddTSharp8(P,vbSampled,fset,b.bind,b.flags); else Assert(false,'AddAttr'); end; // dtSTR_IMG: if (vMipArray in b.flags) then begin Case b.addr[0].rtype of vtTSharp4:AddTSharp4(P,vbMipStorage,fset,b.bind,b.flags); vtTSharp8:AddTSharp8(P,vbMipStorage,fset,b.bind,b.flags); else Assert(false,'AddAttr'); end; end else begin Case b.addr[0].rtype of vtTSharp4:AddTSharp4(P,vbStorage,fset,b.bind,b.flags); vtTSharp8:AddTSharp8(P,vbStorage,fset,b.bind,b.flags); else Assert(false,'AddAttr'); end; end; // dtUNF_BUF, dtSTR_BUF: Case b.addr[0].rtype of vtRoot, vtBufPtr2:AddBufPtr (P,Fset,b.bind,b.size,b.offset,b.flags); vtVSharp2:AddVSharp2(P,Fset,b.bind,b.size,b.offset,b.flags); vtVSharp4:AddVSharp4(P,Fset,b.bind,b.size,b.offset,b.flags); else Assert(false,'AddAttr'); end; else Assert(false,'AddAttr'); end; //Writeln('----'); end; function AlignShift(addr:Pointer;alignment:PtrUInt):PtrUInt; inline; begin if (alignment>1) then begin Result:=(PtrUInt(addr) mod alignment); end else begin Result:=0; end; end; Procedure TvUniformBuilder.AddSSharp4(PS:PSSharpResource4;fset,bind:DWord); var b:TSamplerBindExt; begin Assert(PS<>nil); if (PS=nil) then Exit; //print_ssharp4(PS); b:=Default(TSamplerBindExt); b.fset:=fset; b.bind:=bind; b.PS:=PS; Insert(b,FSamplers,Length(FSamplers)); end; // Procedure TvShadersKey.SetLSShader(Shader:TvShaderExt); begin if (Shader=nil) then Exit; if (Shader.FStage=VK_SHADER_STAGE_VERTEX_BIT) then FShaders[vShaderStageLs]:=Shader; end; Procedure TvShadersKey.SetHSShader(Shader:TvShaderExt); begin if (Shader=nil) then Exit; if (Shader.FStage=VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) then FShaders[vShaderStageHs]:=Shader; end; Procedure TvShadersKey.SetESShader(Shader:TvShaderExt); begin if (Shader=nil) then Exit; if (Shader.FStage=VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) then FShaders[vShaderStageEs]:=Shader; end; Procedure TvShadersKey.SetGSShader(Shader:TvShaderExt); begin if (Shader=nil) then Exit; if (Shader.FStage=VK_SHADER_STAGE_GEOMETRY_BIT) then FShaders[vShaderStageGs]:=Shader; end; Procedure TvShadersKey.SetVSShader(Shader:TvShaderExt); begin if (Shader=nil) then Exit; if (Shader.FStage=VK_SHADER_STAGE_VERTEX_BIT) then FShaders[vShaderStageVs]:=Shader; end; Procedure TvShadersKey.SetPSShader(Shader:TvShaderExt); begin if (Shader=nil) then Exit; if (Shader.FStage=VK_SHADER_STAGE_FRAGMENT_BIT) then FShaders[vShaderStagePs]:=Shader; end; Procedure TvShadersKey.SetCSShader(Shader:TvShaderExt); begin if (Shader=nil) then Exit; if (Shader.FStage=VK_SHADER_STAGE_COMPUTE_BIT) then begin FShaders[vShaderStageCs]:=Shader; end; end; procedure TvShadersKey.ExportLayout(var A:AvSetLayout; var B:AvPushConstantRange); var i:TvShaderStage; Shader:TvShaderExt; ia,p:Integer; CacheLayout:TvSetLayout; begin p:=0; //need sorted by FDescSetId For i:=Low(TvShaderStage) to High(TvShaderStage) do begin Shader:=FShaders[i]; if (Shader<>nil) then begin Shader.InitSetLayout; p:=Shader.FDescSetId; ia:=Length(A); if ((p+1)>ia) then begin SetLength(A,p+1); For ia:=ia to High(A) do begin A[ia]:=nil; end; end; A[p]:=Shader.FSetLayout; if (Shader.FPushConst.size<>0) then begin p:=Length(B); SetLength(B,p+1); B[p]:=Default(TVkPushConstantRange); B[p].stageFlags:=ord(Shader.FStage); B[p].offset :=Shader.FPushConst.offset; B[p].size :=Shader.FPushConst.size; end; end; end; //fill zeros if (Length(A)<>0) then begin CacheLayout:=nil; For ia:=0 to High(A) do if (A[ia]=nil) then begin if (CacheLayout=nil) then begin CacheLayout:=FetchSetLayout(0,0,[]); end; A[ia]:=CacheLayout; end; end; end; Procedure TvShadersKey.ExportStages(Stages:PVkPipelineShaderStageCreateInfo;stageCount:PVkUInt32); var i:TvShaderStage; c:Integer; begin c:=0; For i:=Low(TvShaderStage) to High(TvShaderStage) do if (FShaders[i]<>nil) then begin Assert(FShaders[i].FHandle<>VK_NULL_HANDLE); Stages[c].sType :=VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; Stages[c].stage :=FShaders[i].FStage; Stages[c].module:=FShaders[i].FHandle; Stages[c].pName :=PChar(FShaders[i].FEntry); Inc(c); end; stageCount^:=c; end; Procedure TvShaderGroup.Clear; begin FKey:=Default(TvShadersKey); FLayout:=nil; end; Function TvShaderGroup.Compile:Boolean; var A:AvSetLayout; B:AvPushConstantRange; begin Result:=True; if (FLayout<>nil) then Exit; A:=Default(AvSetLayout); B:=Default(AvPushConstantRange); FKey.ExportLayout(A,B); FLayout:=FetchPipelineLayout(A,B); Result:=(FLayout<>nil); end; Procedure TvShaderGroup.ExportAttrBuilder(var AttrBuilder:TvAttrBuilder;GPU_USERDATA:PGPU_USERDATA); var Shader:TvShaderExt; pUserData,pImmData:PDWORD; begin Shader:=FKey.FShaders[vShaderStageVs]; if (Shader<>nil) then begin pUserData:=GPU_USERDATA^.get_user_data(vShaderStageVs); pImmData :=Shader.GetImmData; Shader.EnumVertLayout(@AttrBuilder.AddAttr,Shader.FDescSetId,pUserData,pImmData); end; end; Procedure TvShaderGroup.ExportUnifBuilder(var UniformBuilder:TvUniformBuilder;GPU_USERDATA:PGPU_USERDATA); var Shader:TvShaderExt; i:TvShaderStage; pUserData,pImmData:PDWORD; begin For i:=Low(TvShaderStage) to High(TvShaderStage) do begin Shader:=FKey.FShaders[i]; if (Shader<>nil) then begin pUserData:=GPU_USERDATA^.get_user_data(i); pImmData :=Shader.GetImmData; Shader.EnumUnifLayout(@UniformBuilder.AddAttr,Shader.FDescSetId,pUserData,pImmData); end; end; end; function GetNumType(nfmt:Byte):Byte; inline; begin Case nfmt of IMG_NUM_FORMAT_UNORM :Result:=IMG_NUM_FORMAT_UNORM; IMG_NUM_FORMAT_SRGB :Result:=IMG_NUM_FORMAT_UNORM; IMG_NUM_FORMAT_SNORM :Result:=IMG_NUM_FORMAT_SNORM; IMG_NUM_FORMAT_UINT :Result:=IMG_NUM_FORMAT_UINT; IMG_NUM_FORMAT_SINT :Result:=IMG_NUM_FORMAT_SINT; IMG_NUM_FORMAT_USCALED:Result:=IMG_NUM_FORMAT_FLOAT; IMG_NUM_FORMAT_SSCALED:Result:=IMG_NUM_FORMAT_FLOAT; IMG_NUM_FORMAT_FLOAT :Result:=IMG_NUM_FORMAT_FLOAT; else Result:=IMG_NUM_FORMAT_RESERVED_15; end; end; procedure TvUnifChecker.AddAttr(const b:TvCustomLayout;Fset:TVkUInt32;pUserData,pImmData:PDWORD); var P:Pointer; PV:PVSharpResource4 absolute P; PT:PTSharpResource4 absolute P; PS:PSSharpResource4 absolute P; a:QWORD; rinfo:TvResInfo; begin if not FResult then Exit; P:=GetSharpByPatch(pUserData,pImmData,b.addr); if (P=nil) then Exit; Case b.dtype of dtUNF_BUF, dtSTR_BUF: Case b.addr[0].rtype of vtRoot, vtBufPtr2: begin a:=AlignShift(P,limits.minStorageBufferOffsetAlignment); if (a<>b.offset) then begin FResult:=False; Exit; end; end; vtVSharp2, vtVSharp4: begin a:=AlignShift(Pointer(PV^.base and (not 3)),limits.minStorageBufferOffsetAlignment); if (a<>b.offset) then begin FResult:=False; Exit; end; end; else Assert(false,'AddAttr'); end; else; end; rinfo:=b.addr[0].rinfo; // Case b.addr[0].rtype of vtVSharp2: with PVSharpResource2(P)^ do begin if rinfo.enable then if (stride<>rinfo.stride) then begin FResult:=False; Exit; end; end; vtVSharp4: with PV^ do begin if rinfo.enable then begin if (IsInvalidVSharp(dfmt,num_records)<>rinfo.invalid) or ( dfmt<>rinfo.dfmt ) or ( nfmt<>rinfo.nfmt ) or ( stride<>rinfo.stride ) or (dst_sel_x<>rinfo.dstsel.x) or (dst_sel_y<>rinfo.dstsel.y) or (dst_sel_z<>rinfo.dstsel.z) or (dst_sel_w<>rinfo.dstsel.w) then begin FResult:=False; Exit; end; end else begin if (IsInvalidVSharp(dfmt,num_records)<>rinfo.invalid) then begin FResult:=False; Exit; end; end; end; vtSSharp4: begin with PS^ do begin if (force_degamma<>ord(rinfo.degam)) then begin FResult:=False; Exit; end; end; end; vtTSharp4, vtTSharp8: with PT^ do begin if rinfo.rtype<>_type then begin FResult:=False; Exit; end; if rinfo.enable then begin if ( dfmt<>rinfo.dfmt ) or ( nfmt<>rinfo.nfmt ) or (dst_sel_x<>rinfo.dstsel.x) or (dst_sel_y<>rinfo.dstsel.y) or (dst_sel_z<>rinfo.dstsel.z) or (dst_sel_w<>rinfo.dstsel.w) then begin FResult:=False; Exit; end; end else begin if ( dfmt<>rinfo.dfmt ) or (GetNumType(nfmt)<>GetNumType(rinfo.nfmt)) then begin FResult:=False; Exit; end; end; end; else; end; end; end.