unit vDescriptorSet;

{$mode ObjFPC}{$H+}

interface

uses
 sysutils,
 g_node_splay,
 Vulkan,
 vDevice,
 vPipeline,
 vDependence;

type
 PvDescriptorCache=^TvDescriptorCache;
 TvDescriptorCache=packed object
  //
  layout:TvPipelineLayout; //Must be the first element in memory
  //
  p_write:PVkWriteDescriptorSet;
  //
  p_count_all:Byte;
  //            set      bind
  p_count:array[0..6] of Byte;
  p_binds:array[0..6] of PVkWriteDescriptorSet;
  P_bmaxd:array[0..6] of PByte;
  //
  p_change_any:Boolean;
  p_change:array[0..6] of Boolean;
  //
  pLeft :PvDescriptorCache;
  pRight:PvDescriptorCache;
  //
  function  c(a,b:PvDescriptorCache):Integer; static;
  procedure ClearAllChange;
  procedure SetAllChange;
  procedure BindBuffer (aSet,aBind:TVkUInt32;dtype:TVkDescriptorType;buffer:TVkBuffer;offset,range:TVkDeviceSize);
  procedure BindImage  (aSet,aBind:TVkUInt32;dtype:TVkDescriptorType;img:TVkImageView;aLayout:TVkImageLayout);
  procedure BindImages (aSet,aBind,aElem,aCount:TVkUInt32;dtype:TVkDescriptorType;img:PVkImageView;aLayout:TVkImageLayout);
  procedure BindSampler(aSet,aBind:TVkUInt32;smp:TVkSampler);
 end;

 TvDescriptorCacheSet=specialize TNodeSplay<TvDescriptorCache>;

 TvDescriptorInterface=object
  FHandle:PvDescriptorCache;
  Procedure  BindBuffer  (aSet,aBind:TVkUInt32;buffer:TVkBuffer;offset,range:TVkDeviceSize);
  Procedure  BindUniform (aSet,aBind:TVkUInt32;buffer:TVkBuffer;offset,range:TVkDeviceSize);
  Procedure  BindStorage (aSet,aBind:TVkUInt32;img:TVkImageView;Layout:TVkImageLayout);
  Procedure  BindStorages(aSet,aBind,aElem,aCount:TVkUInt32;img:PVkImageView;Layout:TVkImageLayout);
  Procedure  BindImage   (aSet,aBind:TVkUInt32;img:TVkImageView;Layout:TVkImageLayout);
  Procedure  BindSampler (aSet,aBind:TVkUInt32;smp:TVkSampler);
 end;

 TvDescriptorSet2=object
  FHandle:TVkDescriptorSet;
  Function   IsValid:Boolean;
  Procedure  FillHandle (dwrite:PVkWriteDescriptorSet;count:Integer);
  Procedure  BindBuffer (aBind,aElem:TVkUInt32;buffer:TVkBuffer;offset,range:TVkDeviceSize);
  Procedure  BindUniform(aBind,aElem:TVkUInt32;buffer:TVkBuffer;offset,range:TVkDeviceSize);
  Procedure  BindStorage(aBind,aElem:TVkUInt32;img:TVkImageView;Layout:TVkImageLayout);
  Procedure  BindImage  (aBind,aElem:TVkUInt32;img:TVkImageView;Layout:TVkImageLayout);
  Procedure  BindSampler(aBind,aElem:TVkUInt32;smp:TVkSampler);
 end;

 AvDescriptorSet2=Array of TvDescriptorSet2;

 TvDescriptorGroup=class(TvRefsObject)
  FLayout:TvPipelineLayout;
  FSets  :AvDescriptorSet2;
  Procedure Bind(Cache:PvDescriptorCache);
 end;

function AllocDescriptorCache(obj:TvDependenciesObject;layout:TvPipelineLayout):PvDescriptorCache;

implementation

Procedure TvDescriptorGroup.Bind(Cache:PvDescriptorCache);
var
 i:Integer;
begin
 if (Cache=nil) then Exit;

 Assert(FLayout=Cache^.layout,'bind on wrong layout');

 if (Length(FSets)<>0) then
 For i:=0 to High(FSets) do
 begin
  FSets[i].FillHandle(Cache^.p_binds[i],Cache^.p_count[i]);
 end;

 if (Cache^.p_count_all<>0) then
 begin
  vkUpdateDescriptorSets(Device.FHandle,Cache^.p_count_all,Cache^.p_write,0,nil);
 end;
end;

//

Function TvDescriptorSet2.IsValid:Boolean;
begin
 Result:=FHandle<>VK_NULL_HANDLE;
end;

Procedure TvDescriptorSet2.FillHandle(dwrite:PVkWriteDescriptorSet;count:Integer);
var
 i:Integer;
begin
 if (dwrite=nil) or (count=0) then Exit;

 For i:=0 to count-1 do
 begin
  dwrite[i].dstSet:=FHandle;
 end;
end;

Procedure TvDescriptorSet2.BindBuffer(aBind,aElem:TVkUInt32;buffer:TVkBuffer;offset,range:TVkDeviceSize);
var
 dwrite:TVkWriteDescriptorSet;
 buf:TVkDescriptorBufferInfo;
begin
 buf:=Default(TVkDescriptorBufferInfo);
 buf.buffer:=buffer;
 buf.offset:=offset;
 buf.range :=range ;

 dwrite:=Default(TVkWriteDescriptorSet);
 dwrite.sType          :=VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 dwrite.dstSet         :=FHandle;
 dwrite.dstBinding     :=aBind;
 dwrite.dstArrayElement:=aElem;
 dwrite.descriptorType :=VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
 dwrite.descriptorCount:=1;
 dwrite.pBufferInfo    :=@buf;

 vkUpdateDescriptorSets(Device.FHandle,1,@dwrite,0,nil);
end;

Procedure TvDescriptorSet2.BindUniform(aBind,aElem:TVkUInt32;buffer:TVkBuffer;offset,range:TVkDeviceSize);
var
 dwrite:TVkWriteDescriptorSet;
 buf:TVkDescriptorBufferInfo;
begin
 buf:=Default(TVkDescriptorBufferInfo);
 buf.buffer:=buffer;
 buf.offset:=offset;
 buf.range :=range ;

 dwrite:=Default(TVkWriteDescriptorSet);
 dwrite.sType          :=VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 dwrite.dstSet         :=FHandle;
 dwrite.dstBinding     :=aBind;
 dwrite.dstArrayElement:=aElem;
 dwrite.descriptorType :=VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
 dwrite.descriptorCount:=1;
 dwrite.pBufferInfo    :=@buf;

 vkUpdateDescriptorSets(Device.FHandle,1,@dwrite,0,nil);
end;

Procedure TvDescriptorSet2.BindStorage(aBind,aElem:TVkUInt32;img:TVkImageView;Layout:TVkImageLayout);
var
 dwrite:TVkWriteDescriptorSet;
 dimg:TVkDescriptorImageInfo;
begin
 dimg:=Default(TVkDescriptorImageInfo);
 dimg.imageView  :=img;
 dimg.imageLayout:=Layout;

 dwrite:=Default(TVkWriteDescriptorSet);
 dwrite.sType          :=VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 dwrite.dstSet         :=FHandle;
 dwrite.dstBinding     :=aBind;
 dwrite.dstArrayElement:=aElem;
 dwrite.descriptorType :=VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
 dwrite.descriptorCount:=1;
 dwrite.pImageInfo     :=@dimg;
 vkUpdateDescriptorSets(Device.FHandle,1,@dwrite,0,nil);
end;

Procedure TvDescriptorSet2.BindImage(aBind,aElem:TVkUInt32;img:TVkImageView;Layout:TVkImageLayout);
var
 dwrite:TVkWriteDescriptorSet;
 dimg:TVkDescriptorImageInfo;
begin
 dimg:=Default(TVkDescriptorImageInfo);
 dimg.imageView  :=img;
 dimg.imageLayout:=Layout;

 dwrite:=Default(TVkWriteDescriptorSet);
 dwrite.sType          :=VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 dwrite.dstSet         :=FHandle;
 dwrite.dstBinding     :=aBind;
 dwrite.dstArrayElement:=aElem;
 dwrite.descriptorType :=VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
 dwrite.descriptorCount:=1;
 dwrite.pImageInfo     :=@dimg;
 vkUpdateDescriptorSets(Device.FHandle,1,@dwrite,0,nil);
end;

Procedure TvDescriptorSet2.BindSampler(aBind,aElem:TVkUInt32;smp:TVkSampler);
var
 dwrite:TVkWriteDescriptorSet;
 dimg:TVkDescriptorImageInfo;
begin
 dimg:=Default(TVkDescriptorImageInfo);
 dimg.sampler:=smp;

 dwrite:=Default(TVkWriteDescriptorSet);
 dwrite.sType          :=VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 dwrite.dstSet         :=FHandle;
 dwrite.dstBinding     :=aBind;
 dwrite.dstArrayElement:=aElem;
 dwrite.descriptorType :=VK_DESCRIPTOR_TYPE_SAMPLER;
 dwrite.descriptorCount:=1;
 dwrite.pImageInfo     :=@dimg;
 vkUpdateDescriptorSets(Device.FHandle,1,@dwrite,0,nil);
end;

//

Procedure TvDescriptorInterface.BindBuffer(aSet,aBind:TVkUInt32;buffer:TVkBuffer;offset,range:TVkDeviceSize);
begin
 if (FHandle=nil) then Exit;

 FHandle^.BindBuffer(aSet,aBind,VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,buffer,offset,range);
end;

Procedure TvDescriptorInterface.BindUniform(aSet,aBind:TVkUInt32;buffer:TVkBuffer;offset,range:TVkDeviceSize);
begin
 if (FHandle=nil) then Exit;

 FHandle^.BindBuffer(aSet,aBind,VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,buffer,offset,range);
end;


Procedure TvDescriptorInterface.BindStorage(aSet,aBind:TVkUInt32;img:TVkImageView;Layout:TVkImageLayout);
begin
 if (FHandle=nil) then Exit;

 FHandle^.BindImage(aSet,aBind,VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,img,Layout);
end;

Procedure TvDescriptorInterface.BindStorages(aSet,aBind,aElem,aCount:TVkUInt32;img:PVkImageView;Layout:TVkImageLayout);
begin
 if (FHandle=nil) then Exit;

 FHandle^.BindImages(aSet,aBind,aElem,aCount,VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,img,Layout);
end;

Procedure TvDescriptorInterface.BindImage(aSet,aBind:TVkUInt32;img:TVkImageView;Layout:TVkImageLayout);
begin
 if (FHandle=nil) then Exit;

 FHandle^.BindImage(aSet,aBind,VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,img,Layout);
end;

Procedure TvDescriptorInterface.BindSampler(aSet,aBind:TVkUInt32;smp:TVkSampler);
begin
 if (FHandle=nil) then Exit;

 FHandle^.BindSampler(aSet,aBind,smp);
end;


//

function AllocDescriptorCache(obj:TvDependenciesObject;layout:TvPipelineLayout):PvDescriptorCache;
var
 i,b         :Integer;
 dwrite_count:Integer;
 dimg_count  :Integer;
 dbuf_count  :Integer;
 dmax_count  :Integer;
 size        :Integer;
 base        :Pointer;
 ends        :Pointer;

 p_write_base:PVkWriteDescriptorSet;
 p_write_ends:PVkWriteDescriptorSet;

 function AllocBase(size:Integer):Pointer; inline;
 begin
  Assert((base+size)<=ends,'AllocBase');
  Result:=base;
  base:=base+size;
 end;

 function AllocWrite(count:Integer):PVkWriteDescriptorSet; inline;
 begin
  Assert((p_write_base+count)<=p_write_ends,'AllocWrite');
  Result:=p_write_base;
  p_write_base:=p_write_base+count;
 end;

begin
 Assert(layout<>nil,'layout not binded');

 Assert(layout.FCounts[ord(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER  )]=0,'TODO');
 Assert(layout.FCounts[ord(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER  )]=0,'TODO');
 Assert(layout.FCounts[ord(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)]=0,'TODO');
 Assert(layout.FCounts[ord(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)]=0,'TODO');
 Assert(layout.FCounts[ord(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT      )]=0,'TODO');

 dwrite_count:=layout.FBinds;
 Assert(dwrite_count<=255,'dwrite_count limit');

 dimg_count:=
  layout.FCounts[ord(VK_DESCRIPTOR_TYPE_SAMPLER)]+
  layout.FCounts[ord(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)]+
  layout.FCounts[ord(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE)]+
  layout.FCounts[ord(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)];

 dbuf_count:=
  layout.FCounts[ord(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)]+
  layout.FCounts[ord(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)];

 dmax_count:=dwrite_count;

 size:=SizeOf(TvDescriptorCache)+
       dwrite_count*SizeOf(TVkWriteDescriptorSet)+
       dmax_count  *SizeOf(Byte)+
       dimg_count  *SizeOf(TVkDescriptorImageInfo)+
       dbuf_count  *SizeOf(TVkDescriptorBufferInfo);

 base:=obj.OnAlloc(size);
 ends:=base+size;

 Result:=AllocBase(SizeOf(TvDescriptorCache));

 Result^.layout:=layout;

 p_write_base:=AllocBase(dwrite_count*SizeOf(TVkWriteDescriptorSet));
 p_write_ends:=base;

 Result^.p_write:=p_write_base;
 Result^.p_count_all:=dwrite_count;

 if Length(layout.key.FLayouts)<>0 then
 begin
  For i:=0 to High(layout.key.FLayouts) do
   With layout.key.FLayouts[i] do
    if (Length(key.FBinds)<>0) then
    begin

     Result^.p_count[i]:=Length(key.FBinds);
     Result^.p_binds[i]:=AllocWrite(Length(key.FBinds));
     Result^.P_bmaxd[i]:=AllocBase (Length(key.FBinds)*SizeOf(Byte));

     For b:=0 to High(key.FBinds) do
      with key.FBinds[b] do
      begin

       Result^.p_binds[i][binding].sType          :=VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
       Result^.p_binds[i][binding].dstBinding     :=binding;
       Result^.p_binds[i][binding].descriptorCount:=1; //initial
       Result^.p_binds[i][binding].descriptorType :=descriptorType;

       Result^.P_bmaxd[i][binding]:=descriptorCount; //max

       case descriptorType of
        VK_DESCRIPTOR_TYPE_SAMPLER,
        VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
        VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
        VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
          begin
           Result^.p_binds[i][binding].pImageInfo:=AllocBase(SizeOf(TVkDescriptorImageInfo)*descriptorCount);
          end;

        VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
        VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
          begin
           Result^.p_binds[i][binding].pBufferInfo:=AllocBase(SizeOf(TVkDescriptorBufferInfo)*descriptorCount);
          end;

        else;
       end;

      end;
    end;
 end;

end;

///

function TvDescriptorCache.c(a,b:PvDescriptorCache):Integer;
begin
 Result:=Integer(Pointer(a^.layout)>Pointer(b^.layout))-Integer(Pointer(a^.layout)<Pointer(b^.layout));
end;

procedure TvDescriptorCache.ClearAllChange;
begin
 //hack
 PQWORD(@p_change_any)^:=0;
end;

procedure TvDescriptorCache.SetAllChange;
begin
 //hack
 PQWORD(@p_change_any)^:=$0101010101010101;
end;

procedure TvDescriptorCache.BindBuffer(aSet,aBind:TVkUInt32;dtype:TVkDescriptorType;buffer:TVkBuffer;offset,range:TVkDeviceSize);
var
 dwrite:PVkWriteDescriptorSet;
 dbuf  :PVkDescriptorBufferInfo;
 change:Boolean;
begin
 Assert(aSet<7,IntToStr(aSet)+'<7');
 Assert(aBind<p_count[aSet],IntToStr(aBind)+'<'+IntToStr(p_count[aSet]));
 Assert(p_binds[aSet]<>nil,'0x'+HexStr(p_binds[aSet])+'<>nil');

 dwrite:=@p_binds[aSet][aBind];

 Assert(dwrite^.descriptorType=dtype);

 dbuf:=dwrite^.pBufferInfo;

 Assert(dbuf<>nil);

 change:=False;

 change:=change or (dbuf^.buffer<>buffer);
 change:=change or (dbuf^.offset<>offset);
 change:=change or (dbuf^.range <>range );

 dbuf^.buffer:=buffer;
 dbuf^.offset:=offset;
 dbuf^.range :=range ;

 if change then
 begin
  p_change_any:=True;
  p_change[aSet]:=True;
 end;
end;

procedure TvDescriptorCache.BindImage(aSet,aBind:TVkUInt32;dtype:TVkDescriptorType;img:TVkImageView;aLayout:TVkImageLayout);
var
 dwrite:PVkWriteDescriptorSet;
 dimg  :PVkDescriptorImageInfo;
 change:Boolean;
begin
 Assert(aSet<7,IntToStr(aSet)+'<7');
 Assert(aBind<p_count[aSet],IntToStr(aBind)+'<'+IntToStr(p_count[aSet]));
 Assert(p_binds[aSet]<>nil,'0x'+HexStr(p_binds[aSet])+'<>nil');

 dwrite:=@p_binds[aSet][aBind];

 Assert(dwrite^.descriptorType=dtype);

 dimg:=dwrite^.pImageInfo;

 Assert(dimg<>nil);

 change:=False;

 change:=change or (dimg^.imageView  <>img    );
 change:=change or (dimg^.imageLayout<>aLayout);

 dimg^.imageView  :=img;
 dimg^.imageLayout:=aLayout;

 if change then
 begin
  p_change_any:=True;
  p_change[aSet]:=True;
 end;
end;

procedure TvDescriptorCache.BindImages(aSet,aBind,aElem,aCount:TVkUInt32;dtype:TVkDescriptorType;img:PVkImageView;aLayout:TVkImageLayout);
var
 dwrite:PVkWriteDescriptorSet;
 dimg  :PVkDescriptorImageInfo;
 i     :TVkUInt32;
 change:Boolean;
begin
 Assert(aSet<7,IntToStr(aSet)+'<7');
 Assert(aBind<p_count[aSet],IntToStr(aBind)+'<'+IntToStr(p_count[aSet]));
 Assert(aCount>0,IntToStr(aCount)+'>0');
 Assert(p_binds[aSet]<>nil,'0x'+HexStr(p_binds[aSet])+'<>nil');

 dwrite:=@p_binds[aSet][aBind];

 Assert(dwrite^.descriptorType=dtype);

 Assert((aElem+aCount)<=P_bmaxd[aSet][aBind]);

 dimg:=dwrite^.pImageInfo;

 Assert(dimg<>nil);

 change:=False;

 change:=change or (dwrite^.dstArrayElement<>aElem );
 change:=change or (dwrite^.descriptorCount<>aCount);

 dwrite^.dstArrayElement:=aElem;
 dwrite^.descriptorCount:=aCount;

 For i:=0 to aCount-1 do
 begin
  change:=change or (dimg[i].imageView  <>img[i] );
  change:=change or (dimg[i].imageLayout<>aLayout);

  dimg[i].imageView  :=img[i];
  dimg[i].imageLayout:=aLayout;
 end;

 if change then
 begin
  p_change_any:=True;
  p_change[aSet]:=True;
 end;
end;

procedure TvDescriptorCache.BindSampler(aSet,aBind:TVkUInt32;smp:TVkSampler);
var
 dwrite:PVkWriteDescriptorSet;
 dimg  :PVkDescriptorImageInfo;
 change:Boolean;
begin
 Assert(aSet<7,IntToStr(aSet)+'<7');
 Assert(aBind<p_count[aSet],IntToStr(aBind)+'<'+IntToStr(p_count[aSet]));
 Assert(p_binds[aSet]<>nil,'0x'+HexStr(p_binds[aSet])+'<>nil');

 dwrite:=@p_binds[aSet][aBind];

 Assert(dwrite^.descriptorType=VK_DESCRIPTOR_TYPE_SAMPLER);

 dimg:=dwrite^.pImageInfo;

 Assert(dimg<>nil);

 change:=False;

 change:=change or (dimg^.sampler<>smp);

 dimg^.sampler:=smp;

 if change then
 begin
  p_change_any:=True;
  p_change[aSet]:=True;
 end;
end;


end.

