unit vRenderPassManager;

{$mode ObjFPC}{$H+}

interface

uses
 SysUtils,
 g23tree,
 Vulkan,
 vDevice,
 vDependence,
 vPipeline,
 vImage{,
 vCmdBuffer};

type
 PvRenderPassKey=^TvRenderPassKey;
 TvRenderPassKey=packed object
  AtdCount:Byte;
  RefCount:Byte; //VkSubpassDescription.colorAttachmentCount
  DepCount:Byte; //DepthRef
  _align:Byte;
  ColorAtd:array[0..8] of TVkAttachmentDescription;
  ColorRef:array[0..7] of TVkAttachmentReference;
  DepthRef:TVkAttachmentReference;
  Dependency:TVkSubpassDependency;
  Procedure  Clear;
  Procedure  SetZorderStage(s:TVkPipelineStageFlags);
  Procedure  _AddColorRef(id:TVkUInt32;IMAGE_USAGE:Byte);
  Procedure  _SetDepthRef(id:TVkUInt32;DEPTH_USAGE,STENCIL_USAGE:Byte);
  Procedure  AddColorAt(id:TVkUInt32;aformat:TVkFormat;IMAGE_USAGE,asamples:Byte);
  Procedure  AddDepthAt(id:TVkUInt32;aformat:TVkFormat;DEPTH_USAGE,STENCIL_USAGE,asamples:Byte);
 end;

 TvRenderPass2=class(TvRenderPass)
  Key:TvRenderPassKey;
  //
  Function  Compile:Boolean;
 end;

function FetchRenderPass(cmd:TvDependenciesObject;P:PvRenderPassKey):TvRenderPass2;

implementation

uses
 kern_rwlock;

type
 TvRenderPassKey2Compare=object
  function c(a,b:PvRenderPassKey):Integer; static;
 end;

 _TvRenderPass2Set=specialize T23treeSet<PvRenderPassKey,TvRenderPassKey2Compare>;
 TvRenderPass2Set=object(_TvRenderPass2Set)
  lock:Pointer;
  Procedure Lock_wr;
  Procedure Unlock_wr;
 end;

var
 FRenderPass2Set:TvRenderPass2Set;

function TvRenderPassKey2Compare.c(a,b:PvRenderPassKey):Integer;
begin
 Result:=CompareByte(a^,b^,SizeOf(TvRenderPassKey));
end;

Procedure TvRenderPass2Set.Lock_wr;
begin
 rw_wlock(lock);
end;

Procedure TvRenderPass2Set.Unlock_wr;
begin
 rw_wunlock(lock);
end;

//

Procedure TvRenderPassKey.Clear;
begin
 Self:=Default(TvRenderPassKey);
 Dependency.srcSubpass:=VK_SUBPASS_EXTERNAL;
end;

Procedure TvRenderPassKey.SetZorderStage(s:TVkPipelineStageFlags);
begin
 Dependency.srcStageMask:=Dependency.srcStageMask or s;
 Dependency.dstStageMask:=Dependency.dstStageMask or s;
end;

Procedure TvRenderPassKey._AddColorRef(id:TVkUInt32;IMAGE_USAGE:Byte);
var
 am:TVkAccessFlags;
begin
 if (RefCount>7) then Exit;

 if (id<>VK_ATTACHMENT_UNUSED) then
 begin
  with ColorRef[RefCount] do
  begin
   attachment:=id;
   layout:=GetColorSendLayout(IMAGE_USAGE);
  end;
 end else
 begin
  with ColorRef[RefCount] do
  begin
   attachment:=VK_ATTACHMENT_UNUSED;
   layout:=VK_IMAGE_LAYOUT_GENERAL;
  end;
 end;

 Inc(RefCount);

 //add on unused also
 Dependency.srcStageMask :=Dependency.srcStageMask or ord(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
 Dependency.dstStageMask :=Dependency.dstStageMask or ord(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);

 if (id<>VK_ATTACHMENT_UNUSED) then
 begin
  am:=GetColorAccessAttachMask(IMAGE_USAGE);

  Dependency.srcAccessMask:=Dependency.srcAccessMask or am;
  Dependency.dstAccessMask:=Dependency.dstAccessMask or am;
 end;
end;

Procedure TvRenderPassKey._SetDepthRef(id:TVkUInt32;DEPTH_USAGE,STENCIL_USAGE:Byte);
var
 am:TVkAccessFlags;
begin
 DepCount:=1;

 DepthRef.attachment:=id;
 DepthRef.layout    :=GetDepthStencilSendLayout(DEPTH_USAGE,STENCIL_USAGE);

 am:=GetDepthStencilAccessAttachMask(DEPTH_USAGE,STENCIL_USAGE);

 Dependency.srcAccessMask:=Dependency.srcAccessMask or am;
 Dependency.dstAccessMask:=Dependency.dstAccessMask or am;
end;

Procedure TvRenderPassKey.AddColorAt(id:TVkUInt32;aformat:TVkFormat;IMAGE_USAGE,asamples:Byte);
begin
 if (AtdCount>8) then Exit;

 if (aformat=VK_FORMAT_R10G11B11_UFLOAT_FAKE32) then
 begin
  aformat:=VK_FORMAT_B10G11R11_UFLOAT_PACK32;
 end;

 ColorAtd[AtdCount]:=Default(TVkAttachmentDescription);

 With ColorAtd[AtdCount] do
 begin
  format :=aformat;
  samples:=TVkSampleCountFlagBits(asamples);

  stencilLoadOp :=VK_ATTACHMENT_LOAD_OP_DONT_CARE;
  stencilStoreOp:=VK_ATTACHMENT_STORE_OP_DONT_CARE;
 end;

 if (id<>VK_ATTACHMENT_UNUSED) then
 begin
  With ColorAtd[AtdCount] do
   if ((IMAGE_USAGE and TM_CLEAR)<>0) then
   begin
    loadOp:=VK_ATTACHMENT_LOAD_OP_CLEAR;
   end else
   if ((IMAGE_USAGE and TM_READ)<>0) then
   begin
    loadOp:=VK_ATTACHMENT_LOAD_OP_LOAD;
   end else
   begin
    loadOp:=VK_ATTACHMENT_LOAD_OP_DONT_CARE;
   end;

  With ColorAtd[AtdCount] do
   if ((IMAGE_USAGE and TM_WRITE)<>0) then
   begin
    storeOp:=VK_ATTACHMENT_STORE_OP_STORE;
   end else
   begin
    storeOp:=VK_ATTACHMENT_STORE_OP_DONT_CARE;
   end;

  With ColorAtd[AtdCount] do
   if ((IMAGE_USAGE and TM_READ)<>0) then
   begin
    initialLayout:=GetColorSendLayout(IMAGE_USAGE);
   end else
   begin
    initialLayout:=VK_IMAGE_LAYOUT_UNDEFINED;
   end;

  With ColorAtd[AtdCount] do
   begin
    finalLayout:=GetColorSendLayout(IMAGE_USAGE);
   end;
 end else
 begin
  With ColorAtd[AtdCount] do
  begin
   loadOp :=VK_ATTACHMENT_LOAD_OP_DONT_CARE;
   storeOp:=VK_ATTACHMENT_STORE_OP_DONT_CARE;

   initialLayout:=VK_IMAGE_LAYOUT_UNDEFINED;
   finalLayout  :=VK_IMAGE_LAYOUT_GENERAL;
  end;
 end;

 Inc(AtdCount);

 _AddColorRef(id,IMAGE_USAGE);
end;

Procedure TvRenderPassKey.AddDepthAt(id:TVkUInt32;aformat:TVkFormat;DEPTH_USAGE,STENCIL_USAGE,asamples:Byte);
begin
 if (AtdCount>8) then Exit;

 ColorAtd[AtdCount]:=Default(TVkAttachmentDescription);

 With ColorAtd[AtdCount] do
 begin
  format :=aformat;
  samples:=TVkSampleCountFlagBits(asamples);
 end;

 With ColorAtd[AtdCount] do
  if ((DEPTH_USAGE and TM_CLEAR)<>0) then
  begin
   loadOp:=VK_ATTACHMENT_LOAD_OP_CLEAR;
  end else
  if ((DEPTH_USAGE and TM_READ)<>0) then
  begin
   loadOp:=VK_ATTACHMENT_LOAD_OP_LOAD;
  end else
  begin
   loadOp:=VK_ATTACHMENT_LOAD_OP_DONT_CARE;
  end;

 With ColorAtd[AtdCount] do
  if ((DEPTH_USAGE and TM_WRITE)<>0) then
  begin
   storeOp:=VK_ATTACHMENT_STORE_OP_STORE;
  end else
  begin
   storeOp:=VK_ATTACHMENT_STORE_OP_DONT_CARE;
  end;

 With ColorAtd[AtdCount] do
  if ((STENCIL_USAGE and TM_CLEAR)<>0) then
  begin
   stencilLoadOp:=VK_ATTACHMENT_LOAD_OP_CLEAR;
  end else
  if ((STENCIL_USAGE and TM_READ)<>0) then
  begin
   stencilLoadOp:=VK_ATTACHMENT_LOAD_OP_LOAD;
  end else
  begin
   stencilLoadOp:=VK_ATTACHMENT_LOAD_OP_DONT_CARE;
  end;

 With ColorAtd[AtdCount] do
  if ((STENCIL_USAGE and TM_WRITE)<>0) then
  begin
   stencilStoreOp:=VK_ATTACHMENT_STORE_OP_STORE;
  end else
  begin
   stencilStoreOp:=VK_ATTACHMENT_STORE_OP_DONT_CARE;
  end;

 With ColorAtd[AtdCount] do
  begin
   initialLayout:=GetDepthStencilInitLayout(DEPTH_USAGE,STENCIL_USAGE);
  end;

 With ColorAtd[AtdCount] do
  begin
   finalLayout:=GetDepthStencilSendLayout(DEPTH_USAGE,STENCIL_USAGE);
  end;

 Inc(AtdCount);

 _SetDepthRef(id,DEPTH_USAGE,STENCIL_USAGE);
end;

///

Function TvRenderPass2.Compile:Boolean;
var
 r:TVkResult;
 subpass:TVkSubpassDescription;
 info:TVkRenderPassCreateInfo;
begin
 Result:=False;
 if (Key.AtdCount=0) and (Key.DepCount=0) then Exit;

 if (FHandle<>VK_NULL_HANDLE) then Exit(True);

 subpass:=Default(TVkSubpassDescription);
 subpass.pipelineBindPoint:=VK_PIPELINE_BIND_POINT_GRAPHICS;

 subpass.inputAttachmentCount   :=0;
 subpass.pInputAttachments      :=nil;

 subpass.colorAttachmentCount   :=Key.RefCount;
 subpass.pColorAttachments      :=@Key.ColorRef;

 subpass.pResolveAttachments    :=nil; //colorAttachmentCount VK_ATTACHMENT_UNUSED

 if (Key.DepCount<>0) then
 begin
  subpass.pDepthStencilAttachment:=@Key.DepthRef; //1
 end;

 subpass.preserveAttachmentCount:=0;
 subpass.pPreserveAttachments   :=nil;

 info:=Default(TVkRenderPassCreateInfo);
 info.sType          :=VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
 info.attachmentCount:=Key.AtdCount;
 info.pAttachments   :=@Key.ColorAtd;
 info.subpassCount   :=1;
 info.pSubpasses     :=@subpass;
 info.dependencyCount:=1;
 info.pDependencies  :=@Key.Dependency;

 r:=vkCreateRenderPass(Device.FHandle,@info,nil,@FHandle);
 if (r<>VK_SUCCESS) then
 begin
  Writeln(StdErr,'vkCreateRenderPass:',r);
  Exit;
 end;

 Result:=True;
end;

function _Find(P:PvRenderPassKey):TvRenderPass2;
var
 i:TvRenderPass2Set.Iterator;
begin
 Result:=nil;
 i:=FRenderPass2Set.find(P);
 if (i.Item<>nil) then
 begin
  Result:=TvRenderPass2(ptruint(i.Item^)-ptruint(@TvRenderPass2(nil).key));
 end;
end;

function _FetchRenderPass(P:PvRenderPassKey):TvRenderPass2;
var
 t:TvRenderPass2;
begin
 Result:=nil;

 t:=_Find(P);

 if (t=nil) then
 begin

  t:=TvRenderPass2.Create;
  t.key:=P^;

  if not t.Compile then
  begin
   FreeAndNil(t);
  end else
  begin
   t.Acquire(nil); //map ref
   FRenderPass2Set.Insert(@t.key);
  end;
 end;

 Result:=t;
end;

function FetchRenderPass(cmd:TvDependenciesObject;P:PvRenderPassKey):TvRenderPass2;
begin
 Result:=nil;
 if (P=nil) then Exit;

 FRenderPass2Set.Lock_wr;

 Result:=_FetchRenderPass(P);

 cmd.RefTo(Result);

 FRenderPass2Set.Unlock_wr;
end;



end.

