unit vRenderPassManager; {$mode ObjFPC}{$H+} interface uses SysUtils, RWLock, g23tree, Vulkan, vDevice, vImage, vPipeline, 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(format:TVkFormat;IMAGE_USAGE:Byte;samples:TVkSampleCountFlagBits); Procedure AddDepthAt(format:TVkFormat;DEPTH_USAGE,STENCIL_USAGE:Byte); end; TvRenderPass2=class(TvRenderPass) Key:TvRenderPassKey; // FRefs:ptruint; Function Compile:Boolean; Procedure Acquire; procedure Release(Sender:TObject); end; function FetchRenderPass(cmd:TvCustomCmdBuffer;P:PvRenderPassKey):TvRenderPass2; implementation type TvRenderPassKey2Compare=object function c(a,b:PvRenderPassKey):Integer; static; end; _TvRenderPass2Set=specialize T23treeSet; TvRenderPass2Set=object(_TvRenderPass2Set) lock:TRWLock; Procedure Init; Procedure Lock_wr; Procedure Unlock; end; var FRenderPass2Set:TvRenderPass2Set; Function GetDepthStencilLayout(DEPTH_USAGE,STENCIL_USAGE:Byte):TVkImageLayout; begin if ((DEPTH_USAGE or STENCIL_USAGE) and (TM_WRITE or TM_CLEAR)<>0) then begin Result:=VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; end else begin Result:=VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; end; end; Function GetDepthStencilAccessMask(DEPTH_USAGE,STENCIL_USAGE:Byte):TVkAccessFlags; begin Result:=(ord(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT) *ord((DEPTH_USAGE or STENCIL_USAGE) and TM_READ <>0) ) or (ord(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT)*ord((DEPTH_USAGE or STENCIL_USAGE) and (TM_WRITE or TM_CLEAR)<>0) ); end; Function GetColorAccessMask(IMAGE_USAGE:Byte):TVkAccessFlags; begin Result:=(ord(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT) *ord(IMAGE_USAGE and TM_READ<>0) ) or (ord(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT)*ord(IMAGE_USAGE and (TM_WRITE or TM_CLEAR)<>0) ); 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; ColorRef[RefCount].attachment:=id; ColorRef[RefCount].layout :=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL{VK_IMAGE_LAYOUT_GENERAL}; Inc(RefCount); 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); am:=GetColorAccessMask(IMAGE_USAGE); Dependency.srcAccessMask:=Dependency.srcAccessMask or am; Dependency.dstAccessMask:=Dependency.dstAccessMask or am; end; Procedure TvRenderPassKey.SetDepthRef(id:TVkUInt32;DEPTH_USAGE,STENCIL_USAGE:Byte); var am:TVkAccessFlags; begin DepCount:=1; DepthRef.attachment :=id; DepthRef.layout :=GetDepthStencilLayout(DEPTH_USAGE,STENCIL_USAGE); am:=GetDepthStencilAccessMask(DEPTH_USAGE,STENCIL_USAGE); Dependency.srcAccessMask:=Dependency.srcAccessMask or am; Dependency.dstAccessMask:=Dependency.dstAccessMask or am; end; Procedure TvRenderPassKey.AddColorAt(format:TVkFormat;IMAGE_USAGE:Byte;samples:TVkSampleCountFlagBits); begin if (AtdCount>8) then Exit; ColorAtd[AtdCount]:=Default(TVkAttachmentDescription); ColorAtd[AtdCount].format :=format; ColorAtd[AtdCount].samples :=samples; 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; ColorAtd[AtdCount].stencilLoadOp :=VK_ATTACHMENT_LOAD_OP_DONT_CARE; ColorAtd[AtdCount].stencilStoreOp:=VK_ATTACHMENT_STORE_OP_DONT_CARE; With ColorAtd[AtdCount] do if (IMAGE_USAGE and TM_READ<>0) then begin initialLayout :=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; end else begin initialLayout :=VK_IMAGE_LAYOUT_UNDEFINED; end; With ColorAtd[AtdCount] do finalLayout:=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; Inc(AtdCount); end; Procedure TvRenderPassKey.AddDepthAt(format:TVkFormat;DEPTH_USAGE,STENCIL_USAGE:Byte); begin if (AtdCount>8) then Exit; ColorAtd[AtdCount]:=Default(TVkAttachmentDescription); ColorAtd[AtdCount].format :=format; ColorAtd[AtdCount].samples :=VK_SAMPLE_COUNT_1_BIT; 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 if ((DEPTH_USAGE or STENCIL_USAGE) and TM_READ<>0) then begin initialLayout :=GetDepthStencilLayout(DEPTH_USAGE,STENCIL_USAGE); end else begin initialLayout :=VK_IMAGE_LAYOUT_UNDEFINED; end; With ColorAtd[AtdCount] do finalLayout:=GetDepthStencilLayout(DEPTH_USAGE,STENCIL_USAGE); Inc(AtdCount); end; /// Function TvRenderPass2.Compile:Boolean; var r:TVkResult; sub:TVkSubpassDescription; info:TVkRenderPassCreateInfo; begin Result:=False; if (Key.AtdCount=0) then Exit; if (FHandle<>VK_NULL_HANDLE) then Exit(True); sub:=Default(TVkSubpassDescription); sub.pipelineBindPoint:=VK_PIPELINE_BIND_POINT_GRAPHICS; sub.inputAttachmentCount :=0; sub.pInputAttachments :=nil; sub.colorAttachmentCount :=Key.RefCount; sub.pColorAttachments :=@Key.ColorRef; sub.pResolveAttachments :=nil; //colorAttachmentCount VK_ATTACHMENT_UNUSED if (Key.DepCount<>0) then begin sub.pDepthStencilAttachment:=@Key.DepthRef; //1 end; sub.preserveAttachmentCount:=0; sub.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 :=@sub; 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; Procedure TvRenderPass2Set.Init; begin rwlock_init(lock); end; Procedure TvRenderPass2Set.Lock_wr; begin rwlock_wrlock(lock); end; Procedure TvRenderPass2Set.Unlock; begin rwlock_unlock(lock); end; Procedure TvRenderPass2.Acquire; begin System.InterlockedIncrement(Pointer(FRefs)); end; procedure TvRenderPass2.Release(Sender:TObject); begin if System.InterlockedDecrement(Pointer(FRefs))=nil then begin Free; end; end; function TvRenderPassKey2Compare.c(a,b:PvRenderPassKey):Integer; begin Result:=CompareByte(a^,b^,SizeOf(TvRenderPassKey)); 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; FRenderPass2Set.Insert(@t.key); end; end; Result:=t; end; function FetchRenderPass(cmd:TvCustomCmdBuffer;P:PvRenderPassKey):TvRenderPass2; begin Result:=nil; if (P=nil) then Exit; FRenderPass2Set.Lock_wr; Result:=_FetchRenderPass(P); if (cmd<>nil) and (Result<>nil) then begin if cmd.AddDependence(@TvRenderPass2(Result).Release) then begin TvRenderPass2(Result).Acquire; end; end; FRenderPass2Set.Unlock; end; initialization FRenderPass2Set.Init; end.