unit srOutput;

{$mode ObjFPC}{$H+}

interface

uses
 typinfo,
 spirv,
 srNode,
 srType,
 srOp,
 srReg,
 srLayout,
 srBitcast,
 srVariable,
 srDecorate;

type
 TpsslExportType=(
  etMrt0,etMrt1,etMrt2,etMrt3,
  etMrt4,etMrt5,etMrt6,etMrt7,
  etMrtz,
  etNull,
  etUnknow0,etUnknow1,
  etPos0,etPos1,etPos2,etPos3,
  etUnknow2,etUnknow3,
  etUnknow4,etUnknow5,
  etUnknow6,etUnknow7,
  etUnknow8,etUnknow9,
  etUnknow10,etUnknow11,
  etUnknow12,etUnknow13,
  etUnknow14,etUnknow15,
  etUnknow16,etUnknow17,
  etParam0 ,etParam1 ,etParam2 ,etParam3,
  etParam4 ,etParam5 ,etParam6 ,etParam7,
  etParam8 ,etParam9 ,etParam10,etParam11,
  etParam12,etParam13,etParam14,etParam15,
  etParam16,etParam17,etParam18,etParam19,
  etParam20,etParam21,etParam22,etParam23,
  etParam24,etParam25,etParam26,etParam27,
  etParam28,etParam29,etParam30,etParam31
 );

 TDepthMode=(
  foDepthNone,
  foDepthReplacing,
  foDepthGreater,
  foDepthLess,
  foDepthUnchanged
 );

 TsrOutput=class(TsrDescriptor)
  var
   etype:TpsslExportType;
   FLineList:TDependenceNodeList;
  //
  function  _GetStorageName:RawByteString; override;
  //
  function  GetStorageName:RawByteString;
  procedure AddLine(pLine:TSpirvOp);
  Procedure FetchStore(pLine:TSpirvOp;src:TsrRegNode);
  Procedure UpdateRegType;
 end;

 ntOutput=TsrOutput;

 PsrOutputList=^TsrOutputList;
 TsrOutputList=object
  FEmit:TCustomEmit;
  FDepthMode:TDepthMode;
  data:array[TpsslExportType] of TsrOutput;
  Procedure Init(Emit:TCustomEmit); inline;
  function  Fetch(etype:TpsslExportType;rtype:TsrDataType):TsrOutput;
  procedure Post;
  procedure AllocBinding;
  procedure AllocEntryPoint(EntryPoint:TSpirvOp);
 end;

implementation

function TsrOutput._GetStorageName:RawByteString;
begin
 Result:=GetStorageName;
end;

//

function TsrOutput.GetStorageName:RawByteString;
begin
 Result:=GetEnumName(TypeInfo(TpsslExportType),ord(etype));
end;

procedure TsrOutput.AddLine(pLine:TSpirvOp);
var
 node:TDependenceNode;
begin
 node:=NewDependence;
 node.pNode:=pLine;
 FLineList.Push_tail(node);
end;

Procedure TsrOutput.FetchStore(pLine:TSpirvOp;src:TsrRegNode);
begin
 if (src=nil) then Exit;

 pLine:=Emit.OpStore(pLine,FVar,src);

 AddLine(pLine);
end;

Procedure TsrOutput.UpdateRegType;
var
 pBitcastList:PsrBitcastList;
 node:TDependenceNode;
 pLine:TSpirvOp;
 Value:TsrNode;
 dst:TsrRegNode;
 old,rtype:TsrDataType;
begin
 rtype:=FType.dtype;

 pBitcastList:=Emit.GetBitcastList;

 node:=FLineList.pHead;
 While (node<>nil) do
 begin
  pLine:=node.pNode;

  Case pLine.OpId of

   Op.OpStore:
    begin
     Value:=pLine.ParamNode(1).Value;
     Value.PrepType(ord(rtype));

     dst:=Value.specialize AsType<ntReg>;
     if (dst<>nil) then
     begin
      old:=dst.dtype;
      if (old<>dtUnknow) and (rtype<>old) then
      begin
       //OpStore <- new <- dst
       dst:=pBitcastList^.FetchRead(rtype,dst);
       pLine.ParamNode(1).Value:=dst;
      end;
     end;
    end;

   else;
  end;

  node:=node.pNext;
 end;
end;

//

Procedure TsrOutputList.Init(Emit:TCustomEmit); inline;
begin
 FEmit:=Emit;
end;

function TsrOutputList.Fetch(etype:TpsslExportType;rtype:TsrDataType):TsrOutput;
begin
 Result:=data[etype];
 //
 if (Result=nil) then
 begin
  Result:=FEmit.specialize New<TsrOutput>;
  Result.etype   :=etype;
  Result.FStorage:=StorageClass.Output;
  Result.FBinding:=-1;
  //
  data[etype]:=Result;
 end;
 //
 Result.InitType(rtype);
 Result.InitVar();
end;

//

procedure TsrOutputList.Post;
var
 i:TpsslExportType;
begin
 For i:=Low(TpsslExportType) to High(TpsslExportType) do
 if (data[i]<>nil) then
 begin
  if (data[i].pVar<>nil) and data[i].IsUsed then
  begin
   data[i].UpdateRegType;
  end;
 end;
end;

procedure TsrOutputList.AllocBinding;
var
 pDecorateList:TsrDecorateList;
 i:TpsslExportType;
 pVar:TsrVariable;
 FLocation:Integer;
begin
 pDecorateList:=FEmit.GetDecorateList;
 For i:=Low(TpsslExportType) to High(TpsslExportType) do
 if (data[i]<>nil) then
 begin
  pVar:=data[i].pVar;
  if (pVar<>nil) and data[i].IsUsed then
  begin
   Case i of
    etMrt0..etMrt7:
      begin
       if (data[i].FBinding=-1) then //alloc
       begin
        FLocation:=ord(i)-ord(etMrt0);
        pDecorateList.OpDecorate(pVar,Decoration.Location,FLocation);
        data[i].FBinding:=FLocation;
       end;
       //Decoration.Index; ???
      end;
    etMrtz:
      begin
       //force Depth Replacing
       FDepthMode:=foDepthReplacing;
       //
       pDecorateList.OpDecorate(pVar,Decoration.BuiltIn,BuiltIn.FragDepth);
      end;
    etPos0:
      begin
       pDecorateList.OpDecorate(pVar,Decoration.BuiltIn,BuiltIn.Position);
      end;
    //etPos1..etPos3,
    etParam0..etParam31: //interpolate param
     begin
      FLocation:=ord(i)-ord(etParam0);
      pDecorateList.OpDecorate(pVar,Decoration.Location,FLocation);
      data[i].FBinding:=FLocation;
     end;
    else
     Assert(false,'AllocBinding:'+GetEnumName(TypeInfo(TpsslExportType),ord(i)));
   end;
  end;
 end;

end;

procedure TsrOutputList.AllocEntryPoint(EntryPoint:TSpirvOp);
var
 i:TpsslExportType;
 pVar:TsrVariable;
begin
 if (EntryPoint=nil) then Exit;
 For i:=Low(TpsslExportType) to High(TpsslExportType) do
 if (data[i]<>nil) then
 begin
  pVar:=data[i].pVar;
  if (pVar<>nil) and data[i].IsUsed then
  begin
   EntryPoint.AddParam(pVar);
  end;
 end;
end;

end.

