unit SprvEmit;

{$mode objfpc}{$H+}

interface

uses
  sysutils,
  classes,
  spirv,
  si_ci_vi_merged_registers,
  si_ci_vi_merged_groups,
  ps4_pssl,
  srInterface,
  srAllocator,
  srLiteral,
  srType,
  srTypes,
  srReg,
  srOp,
  srInput,
  srOutput,
  srLayout,
  srFlow,
  emit_fetch,
  //
  emit_post,
  emit_alloc,
  emit_print,
  emit_bin;

type
 PSPI_PS_INPUT_CNTL_0=^TSPI_PS_INPUT_CNTL_0;
 PRENDER_TARGET      =^TRENDER_TARGET;

 TSprvEmit=class(TEmitFetch)

  function    NewMain:TSpirvFunc;

  Destructor  Destroy; override;
  Constructor Create;

  procedure   SetUserData(pData:Pointer);
  procedure   FillGPR(VGPRS,USER_SGPR,SGPRS:Word);

  Procedure   InitVs(RSRC1:TSPI_SHADER_PGM_RSRC1_VS;
                     RSRC2:TSPI_SHADER_PGM_RSRC2_VS;
                     STEP_RATE_0,STEP_RATE_1:DWORD);

  Procedure   InitPs(RSRC1:TSPI_SHADER_PGM_RSRC1_PS;
                     RSRC2:TSPI_SHADER_PGM_RSRC2_PS;
                     ENA  :TSPI_PS_INPUT_ENA;
                     ADDR :TSPI_PS_INPUT_ADDR);

  Procedure   SET_SHADER_CONTROL(const SHADER_CONTROL:TDB_SHADER_CONTROL);
  Procedure   SET_INPUT_CNTL    (INPUT_CNTL:PSPI_PS_INPUT_CNTL_0;NUM_INTERP:Byte);
  Procedure   SET_RENDER_TARGETS(R:PRENDER_TARGET;COUNT:Byte);

  Procedure   InitCs(RSRC1:TCOMPUTE_PGM_RSRC1;
                     RSRC2:TCOMPUTE_PGM_RSRC2);

  Procedure   cs_set_initial_exec;

  Procedure   SET_NUM_THREADS(NTX:TCOMPUTE_NUM_THREAD_X;
                              NTY:TCOMPUTE_NUM_THREAD_Y;
                              NTZ:TCOMPUTE_NUM_THREAD_Z);

  Procedure   InitCustomGs();

  procedure   emit_spi; override;

  Procedure   PostStage;
  Procedure   AllocStage;
  Procedure   Print;
  Procedure   SaveToStream(Stream:TStream);
 end;

Function ConvertCountVGPRS(VGPRS:Byte):Byte;
Function ConvertCountSGPRS(SGPRS:Byte):Byte;

implementation

uses
 emit_SOP1,
 emit_SOPC,
 emit_SOPP,
 emit_VOP1,
 emit_VOPC,
 emit_VOP3,
 emit_DS,
 emit_MUBUF,
 emit_MTBUF,
 emit_EXP,
 emit_VINTRP,
 emit_MIMG,
 emit_SMRD,
 emit_SOPK,
 emit_SOP2,
 emit_VOP2;

//%void = OpTypeVoid;
//%f_void = OpTypeFunction %void;
//%f_main = OpFunction %void None %f_void;
//%l_main = OpLabel;

function TSprvEmit.NewMain:TSpirvFunc;
var
 tvoid,tftype:TsrType;
 node:TspirvOp;

begin
 Main:=specialize New<TSpirvFunc>;
 Main.Init('main');
 Main.mark_read(nil); //depended itself

 //OpTypeVoid
 tvoid:=TypeList.Fetch(dtTypeVoid);
 //OpTypeFunction
 tftype:=TypeList.FetchFunction(tvoid);

 //OpFunction
 node:=Main.pTop.dummy;
 node.OpId:=Op.OpFunction;
 node.pType:=tvoid;
 node.pDst :=Main; //self
 node.AddLiteral(FunctionControl.None,'None');
 node.AddParam(tftype);

 //OpLabel
 node:=NewLabelOp(True);
 Main.AddSpirvOp(node);

 Result:=Main;
end;

Destructor TSprvEmit.Destroy;
begin
 Allocator.Free;
 inherited;
end;

Constructor TSprvEmit.Create;
begin
 InitLists;

 FuncList.Insert(NewMain);

 SetConst_b(get_vcc0 ,false); //0
 SetConst_b(get_vcc1 ,false); //0
 SetConst_b(get_exec0,true ); //1
 SetConst_b(get_exec1,false); //0

 InitFlow;
end;

procedure TSprvEmit.SetUserData(pData:Pointer);
begin
 DataLayoutList.SetUserData(pData);
end;

procedure TSprvEmit.FillGPR(VGPRS,USER_SGPR,SGPRS:Word);
var
 p:Byte;
begin
 if (VGPRS>256)       then VGPRS:=256;
 if (USER_SGPR>16)    then USER_SGPR:=16;

 if (SGPRS>=2)        then SGPRS:=SGPRS-2; //VCC
 if (SGPRS>104)       then SGPRS:=104;
 if (USER_SGPR>SGPRS) then SGPRS:=USER_SGPR;

 FVGPRS:=VGPRS;
 FSGPRS:=SGPRS;

 if (VGPRS<>0) then
 For p:=0 to VGPRS-1 do
 begin
  if (RegsStory.VGRP[p].current=nil) then
  begin
   SetConst_i(@RegsStory.VGRP[p],dtUnknow,0);
  end;
 end;

 if (SGPRS>USER_SGPR) then
 For p:=USER_SGPR to SGPRS-1 do
 begin
  if (RegsStory.SGRP[p].current=nil) then
  begin
   SetConst_i(@RegsStory.SGRP[p],dtUnknow,0);
  end;
 end;
end;

Function ConvertCountVGPRS(VGPRS:Byte):Byte;
begin
 Result:=(VGPRS+1)*4
end;

Function ConvertCountSGPRS(SGPRS:Byte):Byte;
begin
 Result:=(SGPRS+1)*8;
end;

Procedure TSprvEmit.InitVs(RSRC1:TSPI_SHADER_PGM_RSRC1_VS;
                           RSRC2:TSPI_SHADER_PGM_RSRC2_VS;
                           STEP_RATE_0,STEP_RATE_1:DWORD);
var
 p:Byte;
begin
 FExecutionModel:=ExecutionModel.Vertex;

 //sgrp
 p:=0;
 if (RSRC2.USER_SGPR<>0) then
 For p:=p to RSRC2.USER_SGPR-1 do
 begin
  AddUserdata(@RegsStory.SGRP[p],p);
 end;
 p:=RSRC2.USER_SGPR;

 if (RSRC2.SO_EN<>0) or
    (RSRC2.OC_LDS_EN<>0) then
 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itVsState);
  p:=p+1;
  //s_vs_state
  // stream_id[1:0], is_offchip[2],
  // streamout_vtx_count[6:0],
  // streamout_enable[15:0]}
 end;

 if (RSRC2.SO_EN<>0) then
 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itWriteIndex);
  p:=p+1;
  //s_so_write_index
  // streamout_write_index[31:0]
 end;

 if (RSRC2.SO_BASE0_EN<>0) then
 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itOffset,0);
  p:=p+1;
  //s_so_base_offset0
  // streamout_offset0[31:0]
 end;

 if (RSRC2.SO_BASE1_EN<>0) then
 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itOffset,1);
  p:=p+1;
  //s_so_base_offset1
  // streamout_offset1[31:0]
 end;

 if (RSRC2.SO_BASE2_EN<>0) then
 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itOffset,2);
  p:=p+1;
  //s_so_base_offset2
  // streamout_offset2[31:0]
 end;

 if (RSRC2.SO_BASE3_EN<>0) then
 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itOffset,3);
  p:=p+1;
  //s_so_base_offset3
  // streamout_offset3[31:0]
 end;

 Assert(RSRC2.OC_LDS_EN=0);

 if (RSRC2.DISPATCH_DRAW_EN<>0) then
 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itWaveId);
  p:=p+1;
  //s_wave_id
  // wave_id [11:0] (dispatch draw term)
 end;

 if (RSRC2.SCRATCH_EN<>0) then
 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itScratch);
  p:=p+1;
  //s_scratch
  // scratch offset (in bytes)
 end;

 //vgrp
 p:=1;
 AddInput(@RegsStory.VGRP[0],dtUint32,itVIndex);

 //0 plain
 //1 step rate 0
 //2 step rate 1

 VGPR_COMP_CNT  :=RSRC1.VGPR_COMP_CNT;
 VGT_STEP_RATE_0:=STEP_RATE_0;
 VGT_STEP_RATE_1:=STEP_RATE_1;

 if (VGPR_COMP_CNT>=1) then
 begin
  AddInstance(@RegsStory.VGRP[p],1,STEP_RATE_0);
  p:=p+1;
 end;

 if (VGPR_COMP_CNT>=2) then
 begin
  AddInstance(@RegsStory.VGRP[p],2,STEP_RATE_1);
  p:=p+1;
 end;

 if (VGPR_COMP_CNT>=3) then
 begin
  AddInstance(@RegsStory.VGRP[p],0,1);
 end;

 FLDS_SIZE:=0;

 FillGPR(ConvertCountVGPRS(RSRC1.VGPRS),
         RSRC2.USER_SGPR,
         ConvertCountSGPRS(RSRC1.SGPRS));

 AddCapability(Capability.Shader);
end;

Procedure TSprvEmit.InitPs(RSRC1:TSPI_SHADER_PGM_RSRC1_PS;
                           RSRC2:TSPI_SHADER_PGM_RSRC2_PS;
                           ENA  :TSPI_PS_INPUT_ENA;
                           ADDR :TSPI_PS_INPUT_ADDR);
var
 p:Byte;
begin
 FExecutionModel:=ExecutionModel.Fragment;

 //sgrp
 p:=0;
 if (RSRC2.USER_SGPR<>0) then
 For p:=p to RSRC2.USER_SGPR-1 do
 begin
  AddUserdata(@RegsStory.SGRP[p],p);
 end;
 p:=RSRC2.USER_SGPR;

 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itPsState);
  p:=p+1;
  //s_ps_state
  // {bc_optimize, prim_mask[14:0],
  // lds_offset[15:0]}
 end;

 if (RSRC2.WAVE_CNT_EN<>0) then
 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itWaveCnt);
  p:=p+1;
  //(s_wave_cnt)
  // {ps_wave_id[9:0], ps_strap_id,
  // ps_pkr_id}
 end;

 if (RSRC2.SCRATCH_EN<>0) then
 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itScratch);
  p:=p+1;
  //s_scratch
  // scratch offset (in bytes)
 end;

 //vgrp
 p:=0;
 if (ADDR.PERSP_SAMPLE_ENA<>0) then
 begin
  if (ENA.PERSP_SAMPLE_ENA<>0) then
  begin
   AddInput(@RegsStory.VGRP[p+0],dtFloat32,itPerspSample,0);
   AddInput(@RegsStory.VGRP[p+1],dtFloat32,itPerspSample,1);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p+0],dtUnknow,0);
   SetConst_i(@RegsStory.VGRP[p+1],dtUnknow,0);
  end;
  p:=p+2;
 end;

 if (ADDR.PERSP_CENTER_ENA<>0) then
 begin
  if (ENA.PERSP_CENTER_ENA<>0) then
  begin
   AddInput(@RegsStory.VGRP[p+0],dtFloat32,itPerspCenter,0);
   AddInput(@RegsStory.VGRP[p+1],dtFloat32,itPerspCenter,1);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p+0],dtUnknow,0);
   SetConst_i(@RegsStory.VGRP[p+1],dtUnknow,0);
  end;
  p:=p+2;
 end;

 if (ADDR.PERSP_CENTROID_ENA<>0) then
 begin
  if (ENA.PERSP_CENTROID_ENA<>0) then
  begin
   AddInput(@RegsStory.VGRP[p+0],dtFloat32,itPerspCentroid,0);
   AddInput(@RegsStory.VGRP[p+1],dtFloat32,itPerspCentroid,1);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p+0],dtUnknow,0);
   SetConst_i(@RegsStory.VGRP[p+1],dtUnknow,0);
  end;
  p:=p+2;
 end;

 if (ADDR.PERSP_PULL_MODEL_ENA<>0) then
 begin
  if (ENA.PERSP_PULL_MODEL_ENA<>0) then
  begin
   AddInput(@RegsStory.VGRP[p+0],dtFloat32,itPerspW,0);
   AddInput(@RegsStory.VGRP[p+1],dtFloat32,itPerspW,1);
   AddInput(@RegsStory.VGRP[p+2],dtFloat32,itPerspW,2);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p+0],dtUnknow,0);
   SetConst_i(@RegsStory.VGRP[p+1],dtUnknow,0);
   SetConst_i(@RegsStory.VGRP[p+2],dtUnknow,0);
  end;
  p:=p+3;
 end;

 if (ADDR.LINEAR_SAMPLE_ENA<>0) then
 begin
  if (ENA.LINEAR_SAMPLE_ENA<>0) then
  begin
   AddInput(@RegsStory.VGRP[p+0],dtFloat32,itLinearSample,0);
   AddInput(@RegsStory.VGRP[p+1],dtFloat32,itLinearSample,1);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p+0],dtUnknow,0);
   SetConst_i(@RegsStory.VGRP[p+1],dtUnknow,0);
  end;
  p:=p+2;
 end;

 if (ADDR.LINEAR_CENTER_ENA<>0) then
 begin
  if (ENA.LINEAR_CENTER_ENA<>0) then
  begin
   AddInput(@RegsStory.VGRP[p+0],dtFloat32,itLinearCenter,0);
   AddInput(@RegsStory.VGRP[p+1],dtFloat32,itLinearCenter,1);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p+0],dtUnknow,0);
   SetConst_i(@RegsStory.VGRP[p+1],dtUnknow,0);
  end;
  p:=p+2;
 end;

 if (ADDR.LINEAR_CENTROID_ENA<>0) then
 begin
  if (ENA.LINEAR_CENTROID_ENA<>0) then
  begin
   AddInput(@RegsStory.VGRP[p+0],dtFloat32,itLinearCentroid,0);
   AddInput(@RegsStory.VGRP[p+1],dtFloat32,itLinearCentroid,1);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p+0],dtUnknow,0);
   SetConst_i(@RegsStory.VGRP[p+1],dtUnknow,0);
  end;
  p:=p+2;
 end;

 if (ADDR.POS_X_FLOAT_ENA<>0) then
 begin
  if (ENA.POS_X_FLOAT_ENA<>0) then
  begin
   AddVecInput(@RegsStory.VGRP[p],dtVec4f,dtFloat32,itFloatPos,0);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p],dtUnknow,0);
  end;
  p:=p+1;
 end;

 if (ADDR.POS_Y_FLOAT_ENA<>0) then
 begin
  if (ENA.POS_Y_FLOAT_ENA<>0) then
  begin
   AddVecInput(@RegsStory.VGRP[p],dtVec4f,dtFloat32,itFloatPos,1);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p],dtUnknow,0);
  end;
  p:=p+1;
 end;

 if (ADDR.POS_Z_FLOAT_ENA<>0) then
 begin
  if (ENA.POS_Z_FLOAT_ENA<>0) then
  begin
   AddVecInput(@RegsStory.VGRP[p],dtVec4f,dtFloat32,itFloatPos,2);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p],dtUnknow,0);
  end;
  p:=p+1;
 end;

 if (ADDR.POS_W_FLOAT_ENA<>0) then
 begin
  if (ENA.POS_W_FLOAT_ENA<>0) then
  begin
   AddVecInput(@RegsStory.VGRP[p],dtVec4f,dtFloat32,itFloatPos,3);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p],dtUnknow,0);
  end;
  p:=p+1;
 end;

 if (ADDR.FRONT_FACE_ENA<>0) then
 begin
  if (ENA.FRONT_FACE_ENA<>0) then
  begin
   AddInput(@RegsStory.VGRP[p],dtBool,itFrontFace);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p],dtUnknow,0);
  end;
  p:=p+1;
 end;

 if (ADDR.ANCILLARY_ENA<>0) then
 begin
  if (ENA.ANCILLARY_ENA<>0) then
  begin
   AddAncillary(@RegsStory.VGRP[p]);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p],dtUnknow,0);
  end;
  p:=p+1;
 end;

 if (ADDR.SAMPLE_COVERAGE_ENA<>0) then
 begin
  if (ENA.SAMPLE_COVERAGE_ENA<>0) then
  begin
   AddInput(@RegsStory.VGRP[p],dtUint32,itSampleCoverage);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p],dtUnknow,0);
  end;
  p:=p+1;
 end;

 if (ADDR.POS_FIXED_PT_ENA<>0) then
 begin
  if (ENA.POS_FIXED_PT_ENA<>0) then
  begin
   AddInput(@RegsStory.VGRP[p],dtUint32,itPosFixed);
  end else
  begin
   SetConst_i(@RegsStory.VGRP[p],dtUnknow,0);
  end;
  p:=p+1;
  //Per-pixel fixed point position Y[31:16], X[15:0]
 end;

 FLDS_SIZE:=RSRC2.EXTRA_LDS_SIZE*128*4;

 FillGPR(ConvertCountVGPRS(RSRC1.VGPRS),
         RSRC2.USER_SGPR,
         ConvertCountSGPRS(RSRC1.SGPRS));

 AddCapability(Capability.Shader);
end;

//ps_z_export_en           -> mrtz.R
//ps_stencil_val_export_en -> mrtz.G bits [0:7]
//ps_stencil_op_export_en  -> mrtz.G bits [8:15]
//ps_mask_export_en        -> mrtz.B
//ps_coverage_export_en    -> mrtz.A

Procedure TSprvEmit.SET_SHADER_CONTROL(const SHADER_CONTROL:TDB_SHADER_CONTROL);
begin
 DB_SHADER_CONTROL:=SHADER_CONTROL;
 //
 case SHADER_CONTROL.CONSERVATIVE_Z_EXPORT of
  1:OutputList.FDepthMode:=foDepthLess;    //EXPORT_LESS_THAN_Z
  2:OutputList.FDepthMode:=foDepthGreater; //EXPORT_GREATER_THAN_Z
  else;
 end;
 //
 FEarlyFragmentTests:=(SHADER_CONTROL.DEPTH_BEFORE_SHADER<>0);
end;

Procedure TSprvEmit.SET_INPUT_CNTL(INPUT_CNTL:PSPI_PS_INPUT_CNTL_0;NUM_INTERP:Byte);
var
 i:Byte;
begin
 PS_NUM_INTERP:=NUM_INTERP;
 //
 if (NUM_INTERP<>0) then
 for i:=0 to NUM_INTERP-1 do
 begin
  FPSInputCntl[i].DATA:=INPUT_CNTL[i];
 end;
end;

Procedure TSprvEmit.SET_RENDER_TARGETS(R:PRENDER_TARGET;COUNT:Byte);
var
 i:Byte;
begin
 EXPORT_COUNT:=COUNT;
 //
 if (COUNT<>0) then
 for i:=0 to COUNT-1 do
 begin
  FExportInfo[i].FORMAT     :=R[i].INFO.FORMAT;
  FExportInfo[i].NUMBER_TYPE:=R[i].INFO.NUMBER_TYPE;
  FExportInfo[i].COMP_SWAP  :=R[i].INFO.COMP_SWAP;
 end;
end;

Procedure TSprvEmit.InitCs(RSRC1:TCOMPUTE_PGM_RSRC1;
                           RSRC2:TCOMPUTE_PGM_RSRC2);
var
 p:Byte;
begin
 FExecutionModel:=ExecutionModel.GLCompute;

 p:=0;
 if (RSRC2.USER_SGPR<>0) then
 For p:=p to RSRC2.USER_SGPR-1 do
 begin
  AddUserdata(@RegsStory.SGRP[p],p);
 end;
 p:=RSRC2.USER_SGPR;

 if (RSRC2.TGID_X_EN<>0) then
 begin
  AddVecInput(@RegsStory.SGRP[p],dtVec3u,dtUint32,itTgid,0);
  p:=p+1;
  //(s_tgid_x) threadgroup_id0[31:0] computePgmRsrc2.tgid_x_en;tgid_x_en(1)  //gl_WorkGroupID
 end;

 if (RSRC2.TGID_Y_EN<>0) then
 begin
  AddVecInput(@RegsStory.SGRP[p],dtVec3u,dtUint32,itTgid,1);
  p:=p+1;
  //(s_tgid_y) threadgroup_id1[31:0] computePgmRsrc2.tgid_y_en;tgid_y_en(1)  //gl_WorkGroupID
 end;

 if (RSRC2.TGID_Z_EN<>0) then
 begin
  AddVecInput(@RegsStory.SGRP[p],dtVec3u,dtUint32,itTgid,2);
  p:=p+1;
  //(s_tgid_z) threadgroup_id2[31:0] computePgmRsrc2.tgid_z_en;tgid_z_en(1)  //gl_WorkGroupID
 end;

 if (RSRC2.TG_SIZE_EN<>0) then
 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itTgSize);
  p:=p+1;
  //(s_tg_size)                    //gl_NumWorkGroups ???
  {first_wave, 14’b0,
  ordered_append_term[10:0],
  threadgroup_size_in_waves[5:0]} //computePgmRsrc2.tg_size_en;tg_size_en(1)
 end;

 if (RSRC2.SCRATCH_EN<>0) then
 begin
  AddInput(@RegsStory.SGRP[p],dtUint32,itScratch);
  p:=p+1;
  //s_scratch
  // scratch offset (in bytes)
 end;

 //vgrp
 p:=0;
 begin
  AddVecInput(@RegsStory.VGRP[p],dtVec3u,dtUint32,itThreadId,0);
  p:=p+1;
  //(v_thread_id_x) Thread ID in group 0  //gl_LocalInvocationID
 end;

 if (RSRC2.TIDIG_COMP_CNT>=1) then
 begin
  AddVecInput(@RegsStory.VGRP[p],dtVec3u,dtUint32,itThreadId,1);
  p:=p+1;
  //(v_thread_id_y) Thread ID in group 1  //gl_LocalInvocationID
 end;

 if (RSRC2.TIDIG_COMP_CNT>=2) then
 begin
  AddVecInput(@RegsStory.VGRP[p],dtVec3u,dtUint32,itThreadId,2);
  p:=p+1;
  //(v_thread_id_z) Thread ID in group 2 //gl_LocalInvocationID
 end;

 FLDS_SIZE:=RSRC2.LDS_SIZE*128*4;

 FillGPR(ConvertCountVGPRS(RSRC1.VGPRS),
         RSRC2.USER_SGPR,
         ConvertCountSGPRS(RSRC1.SGPRS));

 AddCapability(Capability.Shader);
end;

Procedure TSprvEmit.cs_set_initial_exec;
var
 total:PtrUint;
 val:PtrUint;

 vec:TsrRegNode;
 src:array[0..2] of TsrRegNode;
begin
 if not Config.UseExtendedEXECMask then Exit;

 total:=FLocalSize.x*FLocalSize.y*FLocalSize.z;
 val:=not PtrUint(0);

 if (total<64) then
 begin
  val:=(PtrUint(1) shl total)-1;
 end else
 if ((total mod 64)=0) then
 begin
  val:=not PtrUint(0);
 end else
 if (total>64) then
 begin
  Assert(false,'TODO: big unaligned total LocalSize');
 end;

 SetConst_q(get_exec0,dtUnknow,val and (not DWORD(0)));
 SetConst_q(get_exec1,dtUnknow,val shr 32);

 vec:=AddInput(@RegsStory.FUnattach,dtVec3u,itThreadId,0);

 src[0]:=RegsStory.FUnattach.New(dtUint32);
 src[1]:=RegsStory.FUnattach.New(dtUint32);
 src[2]:=RegsStory.FUnattach.New(dtUint32);

 OpExtract(init_line,src[0],vec,0);
 OpExtract(init_line,src[1],vec,1);
 OpExtract(init_line,src[2],vec,2);

 FThread_id:=OpIMulTo(OpIMulTo(src[0],src[1]),src[2]);

 if (total>64) then
 begin
  FThread_id:=OpAndTo(FThread_id,63);
 end;
end;

Procedure TSprvEmit.SET_NUM_THREADS(NTX:TCOMPUTE_NUM_THREAD_X;
                                    NTY:TCOMPUTE_NUM_THREAD_Y;
                                    NTZ:TCOMPUTE_NUM_THREAD_Z);
begin
 CS_NUM_THREAD_X:=DWORD(NTX);
 CS_NUM_THREAD_Y:=DWORD(NTY);
 CS_NUM_THREAD_Z:=DWORD(NTZ);
 //
 FLocalSize.x:=NTX.NUM_THREAD_FULL+NTX.NUM_THREAD_PARTIAL;
 FLocalSize.y:=NTY.NUM_THREAD_FULL+NTY.NUM_THREAD_PARTIAL;
 FLocalSize.z:=NTZ.NUM_THREAD_FULL+NTZ.NUM_THREAD_PARTIAL;
 //
 if (FLocalSize.x=0) then FLocalSize.x:=1;
 if (FLocalSize.y=0) then FLocalSize.y:=1;
 if (FLocalSize.z=0) then FLocalSize.z:=1;
 //
 cs_set_initial_exec;
end;

Procedure TSprvEmit.InitCustomGs();
begin
 FExecutionModel:=ExecutionModel.Geometry;

 FGeometryInfo.outputVertCount:=4;
 FGeometryInfo.invocationCount:=1;
 FGeometryInfo.InputMode      :=ExecutionMode.Triangles;
 FGeometryInfo.OutputMode     :=ExecutionMode.OutputTriangleStrip;

 AddCapability(Capability.Shader);
 AddCapability(Capability.Geometry);
end;

//

procedure TSprvEmit.emit_spi;
var
 obj:TObject absolute Self;
begin
 //OpLine(line,Cursor.prev_adr.Offdw*4,0);

 //
 Case FSPI.CMD.EN of
  W_SOP1  :TEmit_SOP1(obj).emit_SOP1;
  W_SOPC  :TEmit_SOPC(obj).emit_SOPC;
  W_SOPP  :TEmit_SOPP(obj).emit_SOPP;
  W_VOP1  :TEmit_VOP1(obj).emit_VOP1;
  W_VOPC  :TEmit_VOPC(obj).emit_VOPC;
  W_VOP3  :Case FSPI.VOP3a.OP of
              0..255:TEmit_VOP3(obj).emit_VOP3c;
            293..298,
            365..366:TEmit_VOP3(obj).emit_VOP3b;
            else
                     TEmit_VOP3(obj).emit_VOP3a;
           end;
  W_DS    :TEmit_DS    (obj).emit_DS;
  W_MUBUF :TEmit_MUBUF (obj).emit_MUBUF;
  W_MTBUF :TEmit_MTBUF (obj).emit_MTBUF;
  W_EXP   :TEmit_EXP   (obj).emit_EXP;
  W_VINTRP:TEmit_VINTRP(obj).emit_VINTRP;
  W_MIMG  :TEmit_MIMG  (obj).emit_MIMG;
  W_SMRD  :TEmit_SMRD  (obj).emit_SMRD;
  W_SOPK  :TEmit_SOPK  (obj).emit_SOPK;
  W_SOP2  :TEmit_SOP2  (obj).emit_SOP2;
  W_VOP2  :TEmit_VOP2  (obj).emit_VOP2;
  else
   Assert(false,'Unknow encode:0x'+HexStr(FSPI.CMD.EN,4));
 end;
end;

Procedure TSprvEmit.PostStage;
begin
 TSprvEmit_post(TObject(Self)).PostStage;
end;

Procedure TSprvEmit.AllocStage;
begin
 TSprvEmit_alloc(TObject(Self)).AllocStage;
end;

Procedure TSprvEmit.Print;
begin
 TSprvEmit_print(TObject(Self)).Print;
end;

Procedure TSprvEmit.SaveToStream(Stream:TStream);
begin
 TSprvEmit_bin(TObject(Self)).SaveToStream(Stream);
end;

end.

