unit vImageManager; {$mode objfpc}{$H+} interface uses SysUtils, RWLock, g23tree, //sys_types, Vulkan, vDevice, vMemory, vImage, vCmdBuffer, vImageTiling; type TvImageView2Compare=object function c(a,b:PvImageViewKey):Integer; static; end; TvImage2=class; TvImageView2=class(TvImageView) Parent:TvImage2; key:TvImageViewKey; // //Barrier:TvImageBarrier; // //Constructor Create; procedure PushBarrier(cmd:TvCustomCmdBuffer; dstAccessMask:TVkAccessFlags; newImageLayout:TVkImageLayout; dstStageMask:TVkPipelineStageFlags); procedure Release(Sender:TObject); Function GetSubresRange:TVkImageSubresourceRange; Function GetSubresLayer:TVkImageSubresourceLayers; end; TvImageView2Set=specialize T23treeSet; { TvHostImage2=class(TvCustomImage) Parent:TvImage2; FUsage:TVkFlags; // Barrier:TvImageBarrier; // Constructor Create; function GetImageInfo:TVkImageCreateInfo; override; procedure PushBarrier(cmd:TvCustomCmdBuffer; dstAccessMask:TVkAccessFlags; newImageLayout:TVkImageLayout; dstStageMask:TVkPipelineStageFlags); end; } TvImage2=class(TvCustomImage) key:TvImageKey; FUsage:TVkFlags; // lock:TRWLock; FViews:TvImageView2Set; // Barrier:TvImageBarrier; // //FHostImage:TvHostImage2; // Fdevc:TvPointer; // FRefs:ptruint; FDeps:TObjectSetLock; // submit_id:ptruint; hash:dword; // data_usage:Byte; Constructor Create; Destructor Destroy; override; function GetImageInfo:TVkImageCreateInfo; override; Function GetSubresRange:TVkImageSubresourceRange; Function GetSubresLayer:TVkImageSubresourceLayers; function FetchView(cmd:TvCustomCmdBuffer;var F:TvImageViewKey):TvImageView2; function FetchView(cmd:TvCustomCmdBuffer):TvImageView2; //function FetchHostImage(cmd:TvCustomCmdBuffer;usage:TVkFlags):TvHostImage2; procedure PushBarrier(cmd:TvCustomCmdBuffer; dstAccessMask:TVkAccessFlags; newImageLayout:TVkImageLayout; dstStageMask:TVkPipelineStageFlags); Procedure Acquire(Sender:TObject); procedure Release(Sender:TObject); end; function FetchImage(cmd:TvCustomCmdBuffer;var F:TvImageKey;usage:TVkFlags;data_usage:Byte):TvImage2; function FindImage(cmd:TvCustomCmdBuffer;Addr:Pointer;cformat:TVkFormat):TvImage2; const img_ext:TVkExternalMemoryImageCreateInfo=( sType:VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO; pNext:nil; handleTypes:ord(VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT); ); var IMAGE_TEST_HACK:Boolean=False; IMAGE_LOAD_HACK:Boolean=False; implementation type TvImageKeyCompare=object function c(a,b:PvImageKey):Integer; static; end; _TvImage2Set=specialize T23treeSet; TvImage2Set=object(_TvImage2Set) lock:TRWLock; Procedure Init; Procedure Lock_wr; Procedure Unlock; end; var FImage2Set:TvImage2Set; Procedure TvImage2Set.Init; begin rwlock_init(lock); end; Procedure TvImage2Set.Lock_wr; begin rwlock_wrlock(lock); end; Procedure TvImage2Set.Unlock; begin rwlock_unlock(lock); end; function TvImageKeyCompare.c(a,b:PvImageKey):Integer; begin //1 Addr Result:=Integer(a^.Addr>b^.Addr)-Integer(a^.Addr0) then Exit; //2 cformat Result:=Integer(a^.cformat>b^.cformat)-Integer(a^.cformat0) then Exit; //3 params Result:=CompareByte(a^.params,b^.params,SizeOf(TvImageKey.params)); end; function TvImageView2Compare.c(a,b:PvImageViewKey):Integer; begin Result:=CompareByte(a^,b^,SizeOf(TvImageViewKey)); end; { Constructor TvImageView2.Create; begin inherited; Barrier.Init; end; } procedure TvImageView2.PushBarrier(cmd:TvCustomCmdBuffer; dstAccessMask:TVkAccessFlags; newImageLayout:TVkImageLayout; dstStageMask:TVkPipelineStageFlags); begin if (Parent=nil) then Exit; Parent.PushBarrier(cmd,dstAccessMask,newImageLayout,dstStageMask); { if (cmd=nil) then Exit; if (not cmd.BeginCmdBuffer) then Exit; if Barrier.Push(cmd.cmdbuf, Parent.FHandle, GetSubresRange, dstAccessMask, newImageLayout, dstStageMask) then begin Inc(cmd.cmd_count); end; } end; procedure TvImageView2.Release(Sender:TObject); begin inherited Release; end; Function TvImageView2.GetSubresRange:TVkImageSubresourceRange; begin Result:=Default(TVkImageSubresourceRange); Result.aspectMask :=GetAspectMaskByFormat(key.cformat); Result.baseMipLevel :=key.base_level; Result.levelCount :=key.last_level-key.base_level+1; Result.baseArrayLayer:=key.base_array; Result.layerCount :=key.last_array-key.base_array+1; end; Function TvImageView2.GetSubresLayer:TVkImageSubresourceLayers; begin Result:=Default(TVkImageSubresourceLayers); Result.aspectMask :=GetAspectMaskByFormat(key.cformat); Result.mipLevel :=key.base_level; Result.baseArrayLayer:=key.base_array; Result.layerCount :=key.last_array-key.base_array+1; end; Constructor TvImage2.Create; begin inherited; rwlock_init(lock); Barrier.Init; end; Destructor TvImage2.Destroy; var i:TvImageView2Set.Iterator; t:TvImageView2; begin i:=FViews.cbegin; While (i.Item<>nil) do begin t:=TvImageView2(ptruint(i.Item^)-ptruint(@TvImageView2(nil).key)); t.Release(nil); i.Next; end; FViews.Free; MemManager.Free(Fdevc); inherited; end; function TvImage2.GetImageInfo:TVkImageCreateInfo; begin Result:=Default(TVkImageCreateInfo); Result.sType :=VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; Result.imageType :=TVkImageType(key.params.itype); Result.format :=key.cformat; Result.extent.Create(key.params.extend.width,key.params.extend.height,key.params.extend.depth); Result.mipLevels :=key.params.mipLevels; Result.arrayLayers :=key.params.arrayLayers; Result.samples :=TVkSampleCountFlagBits(key.params.samples); Result.tiling :=VK_IMAGE_TILING_OPTIMAL; Result.usage :=FUsage; Result.initialLayout:=VK_IMAGE_LAYOUT_UNDEFINED; end; { Constructor TvHostImage2.Create; begin inherited; Barrier.Init; end; function TvHostImage2.GetImageInfo:TVkImageCreateInfo; var bpp,size:qword; begin Result:=Parent.GetImageInfo; Result.tiling:=VK_IMAGE_TILING_LINEAR; Result.usage :=FUsage; Result.flags :=ord(VK_IMAGE_CREATE_ALIAS_BIT); if (Parent.key.params.tiling_idx=8) then begin size:=Result.extent.width; bpp:=getFormatSize(Result.format); if IsTexelFormat(Result.format) then begin size:=(size+3) div 4; end; size:=size*bpp; size:=AlignUp(size,128); size:=size div bpp; if IsTexelFormat(Result.format) then begin size:=size*4; end; Result.extent.width:=size; end; end; } Function TvImage2.GetSubresRange:TVkImageSubresourceRange; begin Result:=Default(TVkImageSubresourceRange); Result.aspectMask:=GetAspectMaskByFormat(key.cformat); Result.levelCount:=key.params.mipLevels; Result.layerCount:=key.params.arrayLayers; end; Function TvImage2.GetSubresLayer:TVkImageSubresourceLayers; begin Result:=Default(TVkImageSubresourceLayers); Result.aspectMask :=GetAspectMaskByFormat(key.cformat); Result.mipLevel :=0; Result.baseArrayLayer:=0; Result.layerCount :=key.params.arrayLayers; end; function TvImage2.FetchView(cmd:TvCustomCmdBuffer;var F:TvImageViewKey):TvImageView2; var i:TvImageView2Set.Iterator; t:TvImageView2; cinfo:TVkImageViewCreateInfo; FView:TVkImageView; r:TVkResult; begin Result:=nil; if (Self=nil) then Exit; if (FHandle=VK_NULL_HANDLE) then Exit; rwlock_wrlock(lock); t:=nil; i:=FViews.find(@F); if (i.Item<>nil) then begin t:=TvImageView2(ptruint(i.Item^)-ptruint(@TvImageView2(nil).key)); end else begin cinfo:=Default(TVkImageViewCreateInfo); cinfo.sType :=VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; cinfo.image :=FHandle; cinfo.viewType :=TVkImageViewType(F.vtype); cinfo.format :=key.cformat; cinfo.components.r:=TVkComponentSwizzle(F.dstSel.r); cinfo.components.g:=TVkComponentSwizzle(F.dstSel.g); cinfo.components.b:=TVkComponentSwizzle(F.dstSel.b); cinfo.components.a:=TVkComponentSwizzle(F.dstSel.a); cinfo.subresourceRange.aspectMask :=GetAspectMaskByFormat(F.cformat); cinfo.subresourceRange.baseMipLevel :=F.base_level; cinfo.subresourceRange.levelCount :=F.last_level-F.base_level+1; cinfo.subresourceRange.baseArrayLayer:=F.base_array; cinfo.subresourceRange.layerCount :=F.last_array-F.base_array+1; cinfo.format:=vkFixFormatSupport(cinfo.format,VK_IMAGE_TILING_OPTIMAL,FUsage); FView:=VK_NULL_HANDLE; r:=vkCreateImageView(Device.FHandle,@cinfo,nil,@FView); if (r<>VK_SUCCESS) then begin rwlock_unlock(lock); Writeln(StdErr,'vkCreateImageView:',r); Exit; end; t:=TvImageView2.Create; t.FHandle:=FView; t.Parent :=Self; t.key :=F; t.Acquire; FViews.Insert(@t.key); end; if (cmd<>nil) and (t<>nil) then begin if cmd.AddDependence(@t.Release) then begin t.Acquire; end; end; rwlock_unlock(lock); Result:=t; end; function TvImage2.FetchView(cmd:TvCustomCmdBuffer):TvImageView2; var F:TvImageViewKey; begin if (Self=nil) then Exit; F:=Default(TvImageViewKey); F.cformat:=key.cformat; Case TVkImageType(key.params.itype) of VK_IMAGE_TYPE_1D: begin if (key.params.arrayLayers>1) then F.vtype:=ord(VK_IMAGE_VIEW_TYPE_1D_ARRAY) else F.vtype:=ord(VK_IMAGE_VIEW_TYPE_1D); end; VK_IMAGE_TYPE_2D: begin if (key.params.arrayLayers>1) then F.vtype:=ord(VK_IMAGE_VIEW_TYPE_2D_ARRAY) else F.vtype:=ord(VK_IMAGE_VIEW_TYPE_2D); //VK_IMAGE_VIEW_TYPE_CUBE //VK_IMAGE_VIEW_TYPE_CUBE_ARRAY end; VK_IMAGE_TYPE_3D:F.vtype:=ord(VK_IMAGE_VIEW_TYPE_3D); end; F.last_level:=key.params.mipLevels-1; F.last_array:=key.params.arrayLayers-1; Result:=FetchView(cmd,F); end; { function TvImage2.FetchHostImage(cmd:TvCustomCmdBuffer;usage:TVkFlags):TvHostImage2; var t:TvHostImage2; Fhost:TvPointer; begin Result:=nil; t:=FHostImage; if (t<>nil) then begin if ((t.FUsage and usage)<>usage) then begin Assert(false,'TODO'); end; Exit(t); end; t:=TvHostImage2.Create; t.Parent:=Self; t.FUsage:=usage; if not t.Compile(@img_ext) then begin t.Free; Exit; end; if TryGetHostPointerByAddr(key.Addr,Fhost) then begin if (t.BindMem(Fhost)<>VK_SUCCESS) then begin t.Free; Exit; end; end else begin t.Free; Exit; end; FHostImage:=t; Result:=t; if (cmd<>nil) and (Self<>nil) then begin if cmd.AddDependence(@Self.Release) then begin Self.Acquire(cmd); end; end; end; } procedure TvImage2.PushBarrier(cmd:TvCustomCmdBuffer; dstAccessMask:TVkAccessFlags; newImageLayout:TVkImageLayout; dstStageMask:TVkPipelineStageFlags); begin if (cmd=nil) then Exit; if (not cmd.BeginCmdBuffer) then Exit; rwlock_wrlock(lock); if Barrier.Push(cmd.cmdbuf, FHandle, GetSubresRange, dstAccessMask, newImageLayout, dstStageMask) then begin Inc(cmd.cmd_count); end; rwlock_unlock(lock); end; { procedure TvHostImage2.PushBarrier(cmd:TvCustomCmdBuffer; dstAccessMask:TVkAccessFlags; newImageLayout:TVkImageLayout; dstStageMask:TVkPipelineStageFlags); begin if (cmd=nil) then Exit; if (not cmd.BeginCmdBuffer) then Exit; if Barrier.Push(cmd.cmdbuf, FHandle, Parent.GetSubresRange, dstAccessMask, newImageLayout, dstStageMask) then begin Inc(cmd.cmd_count); end; end; } Procedure TvImage2.Acquire(Sender:TObject); begin System.InterlockedIncrement(Pointer(FRefs)); if (Sender<>nil) then begin FDeps.Insert(Sender); end; end; procedure TvImage2.Release(Sender:TObject); begin if (Sender<>nil) then begin FDeps.delete(Sender); end; if System.InterlockedDecrement(Pointer(FRefs))=nil then begin Free; end; end; function _Find(var F:TvImageKey):TvImage2; var i:TvImage2Set.Iterator; begin Result:=nil; i:=FImage2Set.find(@F); if (i.Item<>nil) then begin Result:=TvImage2(ptruint(i.Item^)-ptruint(@TvImage2(nil).key)); end; end; procedure print_img_usage(usage:TVkFlags); begin if (usage and ord(VK_IMAGE_USAGE_TRANSFER_SRC_BIT ))<>0 then Write(' TRANSFER_SRC'); if (usage and ord(VK_IMAGE_USAGE_TRANSFER_DST_BIT ))<>0 then Write(' TRANSFER_DST'); if (usage and ord(VK_IMAGE_USAGE_SAMPLED_BIT ))<>0 then Write(' SAMPLED'); if (usage and ord(VK_IMAGE_USAGE_STORAGE_BIT ))<>0 then Write(' STORAGE'); if (usage and ord(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT ))<>0 then Write(' COLOR_ATTACHMENT'); if (usage and ord(VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT))<>0 then Write(' DEPTH_STENCIL_ATTACHMENT'); if (usage and ord(VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT ))<>0 then Write(' TRANSIENT_ATTACHMENT'); if (usage and ord(VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT ))<>0 then Write(' INPUT_ATTACHMENT'); end; function _FetchImage(var F:TvImageKey;usage:TVkFlags):TvImage2; var t:TvImage2; begin Result:=nil; t:=_Find(F); if (t<>nil) then begin if ((t.FUsage and usage)<>usage) then begin Write('Current usage:'); print_img_usage(t.FUsage); Writeln; Write('Need usage:'); print_img_usage(usage); Writeln; Assert(false,'TODO'); end; end else begin t:=TvImage2.Create; t.key :=F; t.FUsage:=Usage; if not t.Compile(nil) then begin FreeAndNil(t); end else begin t.Fdevc:=MemManager.Alloc( t.GetRequirements, ord(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) ); t.BindMem(t.Fdevc); t.Acquire(nil); FImage2Set.Insert(@t.key); end; end; Result:=t; end; function _FindImage(Addr:Pointer;cformat:TVkFormat):TvImage2; var i:TvImage2Set.Iterator; t:TvImage2; F:TvImageKey; begin F:=Default(TvImageKey); F.Addr:=Addr; F.cformat:=cformat; t:=nil; i:=FImage2Set.find_be(@F); if (i.Item<>nil) then begin t:=TvImage2(ptruint(i.Item^)-ptruint(@TvImage2(nil).key)); if (t.key.Addr<>Addr) then t:=nil; end; Result:=t; end; function FetchImage(cmd:TvCustomCmdBuffer;var F:TvImageKey;usage:TVkFlags;data_usage:Byte):TvImage2; begin FImage2Set.Lock_wr; Result:=_FetchImage(F,usage); if (cmd<>nil) and (Result<>nil) then begin if cmd.AddDependence(@Result.Release) then begin Result.Acquire(cmd); end; if not cmd.IsRenderPass then begin if ((Result.data_usage and TM_READ)<>0) and (Result.submit_id<>cmd.submit_id) then begin //hash test if not IMAGE_LOAD_HACK then begin if IMAGE_TEST_HACK then begin Result.data_usage:=Result.data_usage and (not TM_READ); end else if CheckFromBuffer(Result) then begin Result.data_usage:=Result.data_usage and (not TM_READ); end; end; end; if ((Result.data_usage and TM_READ)=0) and ((data_usage and TM_READ)<>0) then begin Result.submit_id:=cmd.submit_id; Result.data_usage:=Result.data_usage or TM_READ; LoadFromBuffer(cmd,Result); end; end; Result.data_usage:=Result.data_usage or (data_usage and TM_WRITE); end; FImage2Set.Unlock; end; function FindImage(cmd:TvCustomCmdBuffer;Addr:Pointer;cformat:TVkFormat):TvImage2; begin FImage2Set.Lock_wr; Result:=_FindImage(Addr,cformat); if (cmd<>nil) and (Result<>nil) then begin if cmd.AddDependence(@Result.Release) then begin Result.Acquire(cmd); end; end; FImage2Set.Unlock; end; initialization FImage2Set.Init; end.