unit emit_EXP;

{$mode objfpc}{$H+}

interface

uses
  sysutils,
  ps4_pssl,
  si_ci_vi_merged_enum,
  srCFGCursor,
  srCFGParser,
  srConfig,
  srFlow,
  srType,
  srReg,
  srOutput,
  srOp,
  srOpInternal,
  emit_fetch;

type
 Tdst_sel=array[0..3] of Byte;

 TEmit_EXP=class(TEmitFetch)
  procedure emit_EXP;
  function  get_export_type(TGT:Byte):TsrDataType;
  function  is_bindless(TGT:Byte):Boolean;
  function  get_export_sel(TGT:Byte):Tdst_sel;
  procedure fetch_vsrc8_vec2h(VSRC:Word;var dst0,dst1:TsrRegNode);
  procedure shuffle(dst_sel:Tdst_sel;rtype:TsrDataType;src:PPsrRegNode;count:Byte);
 end;

implementation

const
 COLOR_COUNT:array[0..31] of Byte=(
  0, //COLOR_INVALID
  1, //COLOR_8
  1, //COLOR_16
  2, //COLOR_8_8
  1, //COLOR_32
  2, //COLOR_16_16
  3, //COLOR_10_11_11
  3, //COLOR_11_11_10
  4, //COLOR_10_10_10_2
  4, //COLOR_2_10_10_10
  4, //COLOR_8_8_8_8
  2, //COLOR_32_32
  4, //COLOR_16_16_16_16
  3, //COLOR_RESERVED_13    //32_32_32
  4, //COLOR_32_32_32_32
  0, //COLOR_RESERVED_15
  3, //COLOR_5_6_5
  4, //COLOR_1_5_5_5
  4, //COLOR_5_5_5_1
  4, //COLOR_4_4_4_4
  2, //COLOR_8_24
  2, //COLOR_24_8
  3, //COLOR_X24_8_32_FLOAT
  0, //COLOR_RESERVED_23
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  0
 );

const
 z=0;

 dst_sel_ident:Tdst_sel=(4,5,6,7);

 shader_swizzle_map:array[1..4,SWAP_STD..SWAP_ALT_REV] of Tdst_sel=(
  (
    (4,z,z,z),
    (z,4,z,z),
    (z,z,4,z),
    (z,z,z,4)
  ),(
    (4,5,z,z),
    (4,z,z,5),
    (5,4,z,z),
    (5,z,z,4)
  ),(
    (4,5,6,z),
    (4,5,z,6),
    (6,5,4,z),
    (6,5,z,4)
  ),(
    (4,5,6,7),
    (6,5,4,7),
    (7,6,5,4),
    (5,6,7,4)
  )
 );

function TEmit_EXP.get_export_type(TGT:Byte):TsrDataType;
begin
 Result:=dtFloat32;
 case TpsslExportType(TGT) of
  etMrt0..etMrt7:
   begin
    if is_bindless(TGT) then
    begin
     Result:=dtFloat32;
    end else
    case (FExportInfo[TGT].NUMBER_TYPE and 7) of
     NUMBER_UINT:Result:=dtUint32;
     NUMBER_SINT:Result:=dtInt32;
    else;
    end;
   end;
 else;
 end;
end;

function TEmit_EXP.is_bindless(TGT:Byte):Boolean;
begin
 Result:=False;
 case TpsslExportType(TGT) of
  etMrt0..etMrt7:
   begin
    Result:=COLOR_COUNT[FExportInfo[TGT].FORMAT and 31]=0;
   end;
 else;
 end;
end;

function TEmit_EXP.get_export_sel(TGT:Byte):Tdst_sel;
var
 i:Byte;
begin
 Result:=dst_sel_ident;
 case TpsslExportType(TGT) of
  etMrt0..etMrt7:
   begin
    if is_bindless(TGT) then
    begin
     Result:=dst_sel_ident;
    end else
    begin
     i:=COLOR_COUNT[FExportInfo[TGT].FORMAT and 31];
     //
     Result:=shader_swizzle_map[i,FExportInfo[TGT].COMP_SWAP and 3];
    end;
   end;
 else;
 end;
end;

procedure TEmit_EXP.fetch_vsrc8_vec2h(VSRC:Word;var dst0,dst1:TsrRegNode);
var
 pSlot:PsrRegSlot;
 dst:TsrRegNode;
begin
 pSlot:=RegsStory.get_vsrc8(VSRC);

 dst:=MakeRead(pSlot,dtVec2h);
 Assert(dst<>nil,'fetch_vsrc8_vec2h');

 dst0:=NewReg(dtHalf16);
 dst1:=NewReg(dtHalf16);

 OpExtract(line,dst0,dst,0);
 OpExtract(line,dst1,dst,1);
end;

procedure TEmit_EXP.shuffle(dst_sel:Tdst_sel;rtype:TsrDataType;src:PPsrRegNode;count:Byte);
var
 i:Byte;
 dst:array[0..3] of TsrRegNode;
begin
 For i:=0 to count-1 do
 begin

  case dst_sel[i] of
   0:dst[i]:=NewImm_i(rtype.Child,0);
   //1?

   4:dst[i]:=src[0];
   5:dst[i]:=src[1];
   6:dst[i]:=src[2];
   7:dst[i]:=src[3];
   else;
    Assert(False);
  end;

 end;
 //
 For i:=0 to count-1 do
 begin
  src[i]:=dst[i];
 end;
end;

procedure TEmit_EXP.emit_EXP;
Var
 exc:TsrRegNode;
 node:TSpirvOp;
 parent,pOpBlock:TsrOpBlock;

 dout:TsrOutput;
 dst:TsrRegNode;
 src:array[0..3] of TsrRegNode;
 rtype:TsrDataType;
 f,i,p:DWORD;

 dst_sel:Tdst_sel;

 push_count:DWORD;
begin
 //if (VM<>0) and (EXEC<>0) = set pixel else (if DONE=1) discard pixel /(PS only)

 push_count:=0;

 parent:=nil;
 if (FSPI.EXP.VM<>0) and (FSPI.EXP.DONE<>0) then
 begin
  parent:=AllocBlockOp;
  parent.SetInfo(btOther);

  PushBlockOp(line,parent,Default(TsrCursor));
  Inc(push_count);

  exc:=GetThreadBit(get_exec0,get_exec1,dtBool);
  node:=AddSpirvOp(srOpInternal.OpMakeExp);
  node.AddParam(exc); //<-fetch read
 end;

 //before
 if (TpsslExportType(FSPI.EXP.TGT)=etNull) or //only set kill mask
    (FSPI.EXP.EN=0){ or                        //nop
    is_bindless(FSPI.EXP.TGT)} then            //not binded
 begin

  While (push_count<>0) do
  begin
   Main.PopBlock;
   Dec(push_count);
  end;

  Exit;
 end;

 pOpBlock:=AllocBlockOp; //down
 pOpBlock.SetInfo(btOther);

 PushBlockOp(line,pOpBlock,Default(TsrCursor));
 Inc(push_count);

 if (parent<>nil) then
 begin
  parent  .pElse:=pOpBlock; //if->else
  pOpBlock.pIf  :=parent;   //else->if
 end;

 //output

 src[0]:=nil;
 src[1]:=nil;
 src[2]:=nil;
 src[3]:=nil;

 f:=FSPI.EXP.EN;

 if (FSPI.EXP.COMPR=0) then //float32,int32,uint32
 begin

  p:=PopCnt(f);

  if (p=1) then
  begin
   //scalar

   rtype:=get_export_type(FSPI.EXP.TGT);

   Case f of
    $1:src[0]:=fetch_vsrc8(FSPI.EXP.VSRC0,rtype);
    $2:src[0]:=fetch_vsrc8(FSPI.EXP.VSRC1,rtype);
    $4:src[0]:=fetch_vsrc8(FSPI.EXP.VSRC2,rtype);
    $8:src[0]:=fetch_vsrc8(FSPI.EXP.VSRC3,rtype);
    else
     Assert(false,'FSPI.EXP.COMPR='+HexStr(f,1));
   end;

   //shuffle ???

   dout:=FetchOutput(TpsslExportType(FSPI.EXP.TGT),rtype); //output in FSPI.EXP.TGT
   dout.FetchStore(line,src[0]);
  end else
  begin
   //vector

   rtype:=get_export_type(FSPI.EXP.TGT);

   rtype:=rtype.AsVector(p);

   i:=0;
   if (f and $1<>0) then
   begin
    src[i]:=fetch_vsrc8(FSPI.EXP.VSRC0,rtype.Child);
    Inc(i);
   end;
   if (f and $2<>0) then
   begin
    src[i]:=fetch_vsrc8(FSPI.EXP.VSRC1,rtype.Child);
    Inc(i);
   end;
   if (f and $4<>0) then
   begin
    src[i]:=fetch_vsrc8(FSPI.EXP.VSRC2,rtype.Child);
    Inc(i);
   end;
   if (f and $8<>0) then
   begin
    src[i]:=fetch_vsrc8(FSPI.EXP.VSRC3,rtype.Child);
    Inc(i);
   end;

   dst_sel:=get_export_sel(FSPI.EXP.TGT);

   shuffle(dst_sel,rtype,@src,p);

   dst:=OpMakeVec(line,rtype,@src);

   dout:=FetchOutput(TpsslExportType(FSPI.EXP.TGT),rtype); //output in FSPI.EXP.TGT
   dout.FetchStore(line,dst);
  end;

 end else
 begin //half16

  Case f of
    3,
    $F:
      begin
       fetch_vsrc8_vec2h(FSPI.EXP.VSRC0,src[0],src[1]);
       fetch_vsrc8_vec2h(FSPI.EXP.VSRC1,src[2],src[3]);
      end;
   $C:
      begin
       fetch_vsrc8_vec2h(FSPI.EXP.VSRC2,src[0],src[1]);
       fetch_vsrc8_vec2h(FSPI.EXP.VSRC3,src[2],src[3]);
      end;
   else
    Assert(false,'FSPI.EXP.COMPR='+HexStr(f,1));
  end;

  if Config.UseOutput16 then
  begin
   rtype:=dtVec4h;
  end else
  begin
   src[0]:=OpFToF(src[0],dtFloat32);
   src[1]:=OpFToF(src[1],dtFloat32);
   src[2]:=OpFToF(src[2],dtFloat32);
   src[3]:=OpFToF(src[3],dtFloat32);

   rtype:=dtVec4f;
  end;

  dst_sel:=get_export_sel(FSPI.EXP.TGT);

  shuffle(dst_sel,rtype,@src,4);

  dst:=OpMakeVec(line,rtype,@src);

  dout:=FetchOutput(TpsslExportType(FSPI.EXP.TGT),rtype); //output in FSPI.EXP.TGT
  dout.FetchStore(line,dst);
 end;

 While (push_count<>0) do
 begin
  Main.PopBlock;
  Dec(push_count);
 end;
end;

end.

