unit srUniform;

{$mode ObjFPC}{$H+}

interface

uses
 sysutils,
 spirv,
 ginodes,
 srNode,
 srRefId,
 srType,
 srTypes,
 srLayout,
 srVariable,
 srCapability,
 srDecorate,
 srConfig;

type
 String4=String[4];

 TsrRegUniform=class(TsrNode)
  private
   ID:TsrRefId;            //post id
   //
   FWriter:TsrNode;
   FVar:TsrVariable;
   Procedure SetWriter(t:TsrNode);
  public
   //
   function  _Down        :TsrNode;         override;
   Procedure _SetWriter   (w,line:TsrNode); override;
   Procedure _ResetWriter (w:TsrNode);      override;
   function  _GetPrintName:RawByteString;   override;
   function  _GetRef      :Pointer;         override;
   //
   property  pLine:TsrNode read FWriter;
   Procedure Init(pVar:TsrVariable); inline;
   function  GetPrintName:RawByteString;
 end;

 ntRegUniform=TsrRegUniform;

 TsrArrayChain=class(TsrRegUniform)
  pNext:TsrArrayChain;
  //
  idx0:TsrNode;
 end;

 TsrArrayChainList=specialize TNodeStackClass<TsrArrayChain>;

 PsrUniformKey=^TsrUniformKey;
 TsrUniformKey=record
  pLayout:TsrDataLayout;
  FType  :TsrType;
  Fdegam :Boolean; //[S#] force_degamma
 end;

 TsrUniform=class(TsrDescriptor)
  public
   pLeft,pRight:TsrUniform;
   class function c(n1,n2:PsrUniformKey):Integer; static;
  private
   key:TsrUniformKey;
   //
   FReg:TsrRegUniform;
   FArrayChainList:TsrArrayChainList;
  public
   FMipArray:Boolean;
   //
   function  _GetStorageName:RawByteString; override;
   //
   Procedure Init(); inline;
   property  pReg:TsrRegUniform read FReg;
   function  FetchArrayChain(pLine:TsrNode;idx0:TsrNode):TsrArrayChain;
   function  chain_read:DWORD;
   function  chain_write:DWORD;
   function  GetStorageName:RawByteString;
   function  ForceDegamma:Boolean;
   function  GetFlagsStr:String4;
   function  GetTypeStr:RawByteString;
   //
   procedure AllocSourceExtension2(var Writer:TseWriter); override;
 end;

 ntUniform=TsrUniform;

 PsrUniformList=^TsrUniformList;
 TsrUniformList=object
  type
   TNodeTree=specialize TNodeTreeClass<TsrUniform>;
  var
   FEmit:TCustomEmit;
   FTree:TNodeTree;
  procedure Init(Emit:TCustomEmit);
  function  Fetch(s:TsrDataLayout;t:TsrType;degam:Boolean):TsrUniform;
  Function  First:TsrUniform;
  Function  Next(node:TsrUniform):TsrUniform;
  procedure AllocBinding(Var FBinding:Integer);
 end;

implementation

uses
 emit_op;

function TsrRegUniform._Down:TsrNode;
begin
 Result:=FVar;
end;

Procedure TsrRegUniform._SetWriter(w,line:TsrNode);
begin
 SetWriter(w);
end;

Procedure TsrRegUniform._ResetWriter(w:TsrNode);
begin
 if (FWriter=w) then
 begin
  SetWriter(nil);
 end;
end;

function TsrRegUniform._GetPrintName:RawByteString;
begin
 Result:=GetPrintName;
end;

function TsrRegUniform._GetRef:Pointer;
begin
 Result:=@ID;
end;

//

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

//

Procedure TsrRegUniform.Init(pVar:TsrVariable); inline;
begin
 FVar:=pVar;
end;

Procedure TsrRegUniform.SetWriter(t:TsrNode);
begin
 if (FWriter=t) then Exit;
 if isUsed then
 begin
        t.mark_read  (Self);
  FWriter.mark_unread(Self);
 end;
 FWriter:=t;
end;

function TsrRegUniform.GetPrintName:RawByteString;
begin
 Result:=FVar.GetStorageName;
 if (Result<>'') then
 begin
  Result:='r_'+Result;
 end else
 begin
  Assert(ID.Alloc);
  Result:='r'+IntToStr(ID.ID);
 end;
end;

//

class function TsrUniform.c(n1,n2:PsrUniformKey):Integer;
begin
 //first pLayout
 Result:=ord(n1^.pLayout.Order>n2^.pLayout.Order)-ord(n1^.pLayout.Order<n2^.pLayout.Order);
 if (Result<>0) then Exit;
 //second pType (order sort)
 Result:=ord(n1^.FType.Order>n2^.FType.Order)-ord(n1^.FType.Order<n2^.FType.Order);
 if (Result<>0) then Exit;
 //third Fdegam
 Result:=ord(n1^.Fdegam)-ord(n2^.Fdegam);
end;

Procedure TsrUniform.Init(); inline;
begin
 FStorage:=StorageClass.UniformConstant;
 FBinding:=-1;
 //
 FReg    :=Emit.specialize New<TsrRegUniform>;
end;

function TsrUniform.FetchArrayChain(pLine:TsrNode;idx0:TsrNode):TsrArrayChain;
var
 iType:TsrType;
 pChain:TsrNode;
begin
 Result:=nil;

 iType:=pType.GetItem(0).specialize AsType<ntType>; //Image
 if (iType=nil) then Exit;

 //Check dublicate?
 pChain:=TEmitOp(Emit).OpAccessChainTo(iType,pVar,idx0,@pLine);

 Result:=Emit.specialize New<TsrArrayChain>;
 Result.idx0:=idx0;

 TEmitOp(Emit).OpLoad(pLine,iType,Result,pChain);

 //
 FArrayChainList.Push_head(Result);
end;

function TsrUniform.GetStorageName:RawByteString;
label
 _image_info;
var
 image_info:TsrTypeImageInfo;
 pChild:TsrType;
begin
 Result:='';
 if (FType<>nil) then
   Case FType.dtype of
    dtTypeImage:
     begin
      image_info:=FType.image_info;

      _image_info:

      if (image_info.Dim=Dim.Buffer) then
      begin
       Case image_info.Sampled of
           1:Result:='uTex'+IntToStr(FBinding);
           2:Result:='sTex'+IntToStr(FBinding);
        else Result:='rTex'+IntToStr(FBinding);
       end;
      end else
      begin
       Case image_info.Sampled of
           1:Result:='uImg'+IntToStr(FBinding);
           2:Result:='sImg'+IntToStr(FBinding);
        else Result:='rImg'+IntToStr(FBinding);
       end;
      end;
     end;
    //
    dtTypeSampler:Result:='uSmp'+IntToStr(FBinding);
    //
    dtTypeArray,
    dtTypeRuntimeArray:
     begin
      pChild:=pType.GetItem(0).specialize AsType<ntType>; //Image

      if (pChild<>nil) then
      if (pChild.dtype=dtTypeImage) then
      begin
       image_info:=pChild.image_info;

       goto _image_info;
      end;

     end;
    else;
   end;
end;

function TsrUniform.chain_read:DWORD;
var
 node:TsrArrayChain;
begin
 Result:=0;
 if FReg.IsUsed then
 begin
  Result:=FReg.read_count;
 end;
 //
 node:=FArrayChainList.pHead;
 While (node<>nil) do
 begin
  if node.IsUsed then
  begin
   Result:=Result+node.read_count;
  end;
  node:=node.pNext;
 end;
end;

function TsrUniform.chain_write:DWORD;
var
 node:TsrArrayChain;
begin
 Result:=0;
 if FReg.IsUsed then
 begin
  Result:=FReg.write_count;
 end;
 //
 node:=FArrayChainList.pHead;
 While (node<>nil) do
 begin
  if node.IsUsed then
  begin
   Result:=Result+node.write_count;
  end;
  node:=node.pNext;
 end;
end;

function TsrUniform.ForceDegamma:Boolean;
begin
 Result:=key.Fdegam;
end;

function TsrUniform.GetFlagsStr:String4;
const
 _R:array[0..1] of AnsiChar='_R';
 _W:array[0..1] of AnsiChar='_W';
 _M:array[0..1] of AnsiChar='_M';
 _D:array[0..1] of AnsiChar='_D';
begin
 Result:=_R[ord(chain_read <>0)]+
         _W[ord(chain_write<>0)]+
         _M[ord(FMipArray     )]+
         _D[ord(ForceDegamma  )];
end;

//VTX_ATR -> VERTEX ATTRIBUTE
//SAMPLER -> VK_DESCRIPTOR_TYPE_SAMPLER
//SAM_IMG -> VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE
//STR_IMG -> VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
//RNT_IMG
//UTX_BUF -> VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
//STX_BUF -> VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER
//RTX_BUF
//UNF_BUF -> VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
//STR_BUF -> VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
//PSH_CST -> PUSH CONSTANT

function _GetTypeStr(FType:TsrType):PChar;
const
 TEXEL_BUFFER_STR:array[0..2] of PChar=(
  'RTX_BUF',
  'UTX_BUF',
  'STX_BUF'
 );
 IMAGE_STR:array[0..2] of PChar=(
  'RNT_IMG',
  'SAM_IMG',
  'STR_IMG'
 );
var
 image_info:TsrTypeImageInfo;
begin
 if (FType<>nil) then
   Case FType.dtype of
    dtTypeImage:
     begin
      image_info:=FType.image_info;
      //
      if (image_info.Dim=Dim.Buffer) then
      begin
       Result:=TEXEL_BUFFER_STR[image_info.Sampled];
      end else
      begin
       Result:=IMAGE_STR[image_info.Sampled];
      end;
     end;
    //
    dtTypeSampler:Result:='SAMPLER';
    //
    else;
   end;
end;

function TsrUniform.GetTypeStr:RawByteString;
var
 pChild:TsrType;
begin
 Result:='';
 if (FType<>nil) then
   Case FType.dtype of
    dtTypeImage,
    dtTypeSampler:
    begin
     Result:='+'+_GetTypeStr(FType);
    end;
    //
    dtTypeArray,
    dtTypeRuntimeArray:
     begin
      pChild:=pType.GetItem(0).specialize AsType<ntType>; //Image

      Assert(pChild<>nil,'GetTypeChar#1');
      Assert(pChild.dtype=dtTypeImage,'GetTypeChar#2');

      Case FType.dtype of
       dtTypeArray       :Result:='%'+_GetTypeStr(pChild);
       dtTypeRuntimeArray:Result:='*'+_GetTypeStr(pChild);
       else;
      end;

     end;
    else
     Assert(false,'GetTypeChar#3');
   end;
end;

procedure TsrUniform.AllocSourceExtension2(var Writer:TseWriter);
begin
 if (pVar<>nil) and IsUsed then
 begin
  //start block
  Writer.Header(GetTypeStr);
  //
  Writer.IntOpt('BND',FBinding);
  Writer.StrOpt('FLG',GetFlagsStr);
  //
 end;
end;

procedure TsrUniformList.Init(Emit:TCustomEmit);
begin
 FEmit:=Emit;
end;

function TsrUniformList.Fetch(s:TsrDataLayout;t:TsrType;degam:Boolean):TsrUniform;
var
 key:TsrUniformKey;
begin
 key:=Default(TsrUniformKey);
 key.pLayout:=s;
 key.FType  :=t;
 key.Fdegam :=degam;
 //
 Result:=FTree.Find(@key);
 if (Result=nil) then
 begin
  Result:=FEmit.specialize New<TsrUniform>;
  Result.Init();
  Result.key  :=key;
  Result.pType:=t;
  //
  Result.InitVar();
  Result.FReg.Init(Result.FVar);
  //
  FTree.Insert(Result);
  //
  s.FDescList.Push_tail(Result);
 end;
end;

Function TsrUniformList.First:TsrUniform;
begin
 Result:=FTree.Min;
end;

Function TsrUniformList.Next(node:TsrUniform):TsrUniform;
begin
 Result:=FTree.Next(node);
end;

procedure TsrUniformList.AllocBinding(Var FBinding:Integer);
var
 pConfig:PsrConfig;
 pDecorateList:TsrDecorateList;
 pCapabilityList:PsrCapabilityList;
 //
 node:TsrUniform;
 pVar:TsrVariable;
 //
 FType:TsrType;
 image_info:TsrTypeImageInfo;
begin
 pConfig        :=FEmit.GetConfig;
 pDecorateList  :=FEmit.GetDecorateList;
 pCapabilityList:=FEmit.GetCapabilityList;

 node:=First;
 While (node<>nil) do
 begin
  pVar:=node.pVar;
  if (pVar<>nil) and node.IsUsed and (node.FBinding=-1) then
  begin
   pDecorateList.OpDecorate(pVar,Decoration.Binding      ,FBinding);
   pDecorateList.OpDecorate(pVar,Decoration.DescriptorSet,pConfig^.DescriptorSet);
   //
   if (node.Flags.Coherent) then
   begin
    pDecorateList.OpDecorate(pVar,Decoration.Coherent,0);
   end;
   //
   if (node.Flags.Volatile) then
   begin
    pDecorateList.OpDecorate(pVar,Decoration.Volatile,0);
   end;
   //
   if (node.Flags.Aliased) and (not node.Flags.Bitcast) then
   begin
    pDecorateList.OpDecorate(pVar,Decoration.Aliased,0);
   end;
   //
   node.FBinding:=FBinding;
   Inc(FBinding);

   FType:=node.pType;
   if (FType<>nil) then
   begin
    Case FType.dtype of
     dtTypeImage:
      begin
       image_info:=FType.image_info;

       if (image_info.Sampled=2) then //storage image
       begin
        if (node.chain_read=0) then
        begin
         pDecorateList.OpDecorate(pVar,Decoration.NonReadable,0);
        end;
        if (node.chain_write=0) then
        begin
         pDecorateList.OpDecorate(pVar,Decoration.NonWritable,0);
        end;
       end;

       Case image_info.Dim of
        Dim.Dim1D:
         Case image_info.Sampled of
          1:pCapabilityList^.Add(Capability.Sampled1D); //sampling
          2:pCapabilityList^.Add(Capability.Image1D);   //read/write
          else;
         end;
        Dim.Buffer:
         Case image_info.Sampled of
          1:pCapabilityList^.Add(Capability.SampledBuffer); //sampling
          2:pCapabilityList^.Add(Capability.ImageBuffer);   //read/write
          else;
         end;
        else;
       end;

       if (image_info.Sampled=2) and
          (image_info.Arrayed=1) then
       begin
        pCapabilityList^.Add(Capability.ImageMSArray);
       end;

       Case image_info.Format of

        ImageFormat.Unknown:
         if (image_info.Sampled=2) then //storage image
         begin
          if (node.chain_read<>0) then
          begin
           pCapabilityList^.Add(Capability.StorageImageWriteWithoutFormat);
          end;
          if (node.chain_write<>0) then
          begin
           pCapabilityList^.Add(Capability.StorageImageWriteWithoutFormat);
          end;
         end;

        ImageFormat.Rg32f       ,
        ImageFormat.Rg16f       ,
        ImageFormat.R11fG11fB10f,
        ImageFormat.R16f        ,
        ImageFormat.Rgba16      ,
        ImageFormat.Rgb10A2     ,
        ImageFormat.Rg16        ,
        ImageFormat.Rg8         ,
        ImageFormat.R16         ,
        ImageFormat.R8          ,
        ImageFormat.Rgba16Snorm ,
        ImageFormat.Rg16Snorm   ,
        ImageFormat.Rg8Snorm    ,
        ImageFormat.R16Snorm    ,
        ImageFormat.R8Snorm     ,
        ImageFormat.Rg32i       ,
        ImageFormat.Rg16i       ,
        ImageFormat.Rg8i        ,
        ImageFormat.R16i        ,
        ImageFormat.R8i         ,
        ImageFormat.Rgb10a2ui   ,
        ImageFormat.Rg32ui      ,
        ImageFormat.Rg16ui      ,
        ImageFormat.Rg8ui       ,
        ImageFormat.R16ui       ,
        ImageFormat.R8ui        :pCapabilityList^.Add(Capability.StorageImageExtendedFormats);

        else;
       end;

      end;
     else;
    end;
   end;

  end;
  node:=Next(node);
 end;
end;

end.

