unit vCmdBuffer; {$mode objfpc}{$H+} interface uses Classes, SysUtils, g_node_splay, si_ci_vi_merged_enum, kern_proc, vm_map, vm_tracking_map, Vulkan, vDependence, vDevice, vMemory, //vShader, vShaderExt, vImage, vPipeline, vPipelineManager, vSetsPoolManager, vDescriptorSet, vRender; type TvCmdBuffer=class; PvSemaphoreWait=^TvSemaphoreWait; TvSemaphoreWait=object // FSemaphore:TvSemaphore; //Must be the first element in memory FWaitStage:TVkPipelineStageFlags; // pLeft :PvSemaphoreWait; pRight:PvSemaphoreWait; // function c(a,b:PvSemaphoreWait):Integer; static; end; TvSemaphoreWaitSet=specialize TNodeSplay; p_cmd_track_deferred=^t_cmd_track_deferred; t_cmd_track_deferred=object // start :QWORD; __end :QWORD; exclude:Pointer; // pLeft :p_cmd_track_deferred; pRight:p_cmd_track_deferred; // function c(a,b:p_cmd_track_deferred):Integer; static; end; t_cmd_track_deferred_set=specialize TNodeSplay; const BP_GRAPHICS=VK_PIPELINE_BIND_POINT_GRAPHICS; BP_COMPUTE =VK_PIPELINE_BIND_POINT_COMPUTE; type TvCustomCmdBuffer=class(TvDependenciesObject) FParent:TvCustomCmdPool; FQueue :TvQueue; FCmdbuf:TVkCommandBuffer; cmd_count:qword; ret:Integer; submit_id:ptruint; FCurrPipeline:array[BP_GRAPHICS..BP_COMPUTE] of TVkPipeline; FCurrLayout :array[BP_GRAPHICS..BP_COMPUTE] of TvPipelineLayout; FCurrBinds :array[BP_GRAPHICS..BP_COMPUTE] of PvDescriptorCache; FCurrGroup :array[BP_GRAPHICS..BP_COMPUTE] of TvDescriptorGroup; FDescriptorCacheSet:TvDescriptorCacheSet; FRenderPass:TVkRenderPass; FWaitSemaphores:TvSemaphoreWaitSet; FWaitSemaphoresCount:Integer; FSignalSemaphore:TvSemaphore; FFence:TvFence; FCBState:(cbFree,cbInit,cbBegin,cbEnd,cbSubmit); FPlannedTriggers:t_cmd_track_deferred_set; Constructor Create(pool:TvCustomCmdPool;Queue:TvQueue); Destructor Destroy; override; function AllocCmdBuffer:Boolean; Function IsAllocated:Boolean; function BeginCmdBuffer:Boolean; Procedure EndCmdBuffer; Procedure BindPipeline(BindPoint:TVkPipelineBindPoint;F:TVkPipeline); Function IsRenderPass:Boolean; Procedure EndRenderPass; function BeforePushBarrier:TVkCommandBuffer; function QueueSubmit:TVkResult; function Wait(timeout:TVkUInt64):TVkResult; function Status:TVkResult; Procedure ReleaseResource; Procedure FreeAllSemaphores; Procedure AddWaitSemaphore(S:TvSemaphore;W:TVkPipelineStageFlags); function GetSignaledSemaphore:TvSemaphore; Procedure ReleaseAllPlannedTriggers; Procedure AddPlannedTrigger(start,__end:QWORD;exclude:Pointer); Procedure FreeAllDescriptorCache; Procedure BindLayout(BindPoint:TVkPipelineBindPoint;F:TvPipelineLayout); Procedure BindSet(BindPoint:TVkPipelineBindPoint;fset:TVkUInt32;FHandle:TVkDescriptorSet); Procedure PushConstant(BindPoint:TVkPipelineBindPoint;stageFlags:TVkShaderStageFlags;offset,size:TVkUInt32;const pValues:PVkVoid); Procedure DispatchDirect(X,Y,Z:TVkUInt32); Procedure BindVertexBuffers(const FAttrBuilder:TvAttrBuilder); Procedure SetVertexInput(const FAttrBuilder:TvAttrBuilder); Procedure ClearDepthStencilImage(image:TVkImage; imageLayout:TVkImageLayout; const pDepthStencil:PVkClearDepthStencilValue; rangeCount:TVkUInt32; const pRanges:PVkImageSubresourceRange); Procedure ClearDepthStencilImage(image:TVkImage; imageLayout:TVkImageLayout; const pDepthStencil:PVkClearDepthStencilValue; const range:TVkImageSubresourceRange); Procedure ClearColorImage(image:TVkImage; imageLayout:TVkImageLayout; const pColor:PVkClearColorValue; rangeCount:TVkUInt32; const pRanges:PVkImageSubresourceRange); Procedure ResolveImage(srcImage:TVkImage; srcImageLayout:TVkImageLayout; dstImage:TVkImage; dstImageLayout:TVkImageLayout; regionCount:TVkUInt32; const pRegions:PVkImageResolve); procedure CopyBufferToImage(srcBuffer:TVkBuffer; dstImage:TVkImage; dstImageLayout: TVkImageLayout; regionCount:TVkUInt32; const pRegions:PVkBufferImageCopy); procedure CopyImageToBuffer(srcImage:TVkImage; srcImageLayout:TVkImageLayout; dstBuffer:TVkBuffer; regionCount:TVkUInt32; const pRegions:PVkBufferImageCopy); procedure BufferMemoryBarrier(buffer:TVkBuffer; srcAccessMask:TVkAccessFlags; dstAccessMask:TVkAccessFlags; offset,size:TVkDeviceSize; srcStageMask:TVkPipelineStageFlags; dstStageMask:TVkPipelineStageFlags); Procedure InsertLabel(pLabelName:PVkChar); Procedure BeginLabel(pLabelName:PVkChar); Procedure EndLabel(); function FetchDescriptorCache(layout:TvPipelineLayout):PvDescriptorCache; function FetchDescriptorCache(BindPoint:TVkPipelineBindPoint):PvDescriptorCache; function FetchDescriptorInterface(BindPoint:TVkPipelineBindPoint):TvDescriptorInterface; Procedure ApplyDescriptorCache(BindPoint:TVkPipelineBindPoint); Procedure BindSets(BindPoint:TVkPipelineBindPoint;F:TvDescriptorGroup); end; TvCmdBuffer=class(TvCustomCmdBuffer) FRender:TvRenderPassBeginInfo; Femulate_primtype:SmallInt; Fshader_primtype :SmallInt; FinstanceCount:DWORD; FINDEX_TYPE:TVkIndexType; function BeginRenderPass(RT:PvRenderPassBeginInfo;GP:TvGraphicsPipeline2):Boolean; function BindCompute(CP:TvComputePipeline2):Boolean; Procedure dmaData1(src,dst:Pointer;byteCount:DWORD;isBlocking:Boolean); Procedure dmaData2(src:DWORD;dst:Pointer;byteCount:DWORD;isBlocking:Boolean); Procedure WriteEos(eventType:Byte;dst:Pointer;value:DWORD;isBlocking:Boolean); Procedure WriteEvent(eventType:Byte); Procedure DrawIndexOffset2(IndexBase:Pointer;indexOffset,vertexOffset,indexCount:DWORD); Procedure DrawIndexAuto (vertexOffset,indexCount:DWORD); end; implementation uses vBuffer, vHostBufferManager; function TvSemaphoreWait.c(a,b:PvSemaphoreWait):Integer; begin Result:=Integer(Pointer(a^.FSemaphore)>Pointer(b^.FSemaphore))-Integer(Pointer(a^.FSemaphore)b^.start)-Integer(a^.start0) then Exit; //__end Result:=Integer(a^.__end>b^.__end)-Integer(a^.__end0) then Exit; //exclude Result:=Integer(a^.exclude>b^.exclude)-Integer(a^.exclude0) then Exit; end; // Constructor TvCustomCmdBuffer.Create(pool:TvCustomCmdPool;Queue:TvQueue); begin FParent:=pool; FQueue:=Queue; FFence:=TvFence.Create(False); FSignalSemaphore:=TvSemaphore.Create; FCBState:=cbFree; end; Destructor TvCustomCmdBuffer.Destroy; begin ReleaseResource; FreeAndNil(FFence); FreeAndNil(FSignalSemaphore); if (FParent<>nil) and (FCmdbuf<>VK_NULL_HANDLE) then begin FParent.Free(FCmdbuf); end; inherited; end; function TvCustomCmdBuffer.AllocCmdBuffer:Boolean; begin if (FCmdbuf<>VK_NULL_HANDLE) then Exit(True); FCmdbuf:=FParent.Alloc; if (FCmdbuf=VK_NULL_HANDLE) then Exit(False); FCBState:=cbInit; Result:=True; end; Function TvCustomCmdBuffer.IsAllocated:Boolean; begin Result:=False; if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; Result:=(FCmdbuf<>VK_NULL_HANDLE); end; function TvCustomCmdBuffer.BeginCmdBuffer:Boolean; var r:TVkResult; Info:TVkCommandBufferBeginInfo; begin Result:=False; if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (not AllocCmdBuffer) then Exit; case FCBState of cbInit:; //need start cbBegin: begin //is started Exit(True); end; else //idk why it called in this state Exit(False); end; Info:=Default(TVkCommandBufferBeginInfo); Info.sType:=VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; Info.flags:=ord(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); Info.pInheritanceInfo:=nil; r:=vkBeginCommandBuffer(FCmdbuf,@Info); if (r<>VK_SUCCESS) then begin Writeln(StdErr,'vkBeginCommandBuffer:',r); Exit; end; FCBState:=cbBegin; Result:=True; end; Procedure TvCustomCmdBuffer.EndCmdBuffer; var r:TVkResult; begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (FCBState<>cbBegin) then Exit; EndRenderPass; FCurrLayout[BP_GRAPHICS]:=nil; FCurrLayout[BP_COMPUTE ]:=nil; FCurrBinds[BP_GRAPHICS]:=nil; FCurrBinds[BP_COMPUTE ]:=nil; FCurrGroup[BP_GRAPHICS]:=nil; FCurrGroup[BP_COMPUTE ]:=nil; FCurrPipeline[BP_GRAPHICS]:=VK_NULL_HANDLE; FCurrPipeline[BP_COMPUTE ]:=VK_NULL_HANDLE; r:=vkEndCommandBuffer(FCmdbuf); if (r<>VK_SUCCESS) then begin Writeln(StdErr,'vkEndCommandBuffer:',r); end; FCBState:=cbEnd; end; Procedure TvCustomCmdBuffer.BindPipeline(BindPoint:TVkPipelineBindPoint;F:TVkPipeline); begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (FCurrPipeline[BindPoint]=F) then Exit; if (not BeginCmdBuffer) then Exit; Inc(cmd_count); vkCmdBindPipeline(FCmdbuf,BindPoint,F); FCurrPipeline[BindPoint]:=F; end; function TvCmdBuffer.BeginRenderPass(RT:PvRenderPassBeginInfo;GP:TvGraphicsPipeline2):Boolean; var rinfo:TVkRenderPassBeginInfo; ainfo:TVkRenderPassAttachmentBeginInfo; begin Result:=False; if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (RT=nil) then Exit; if (RT^.FRenderPass =nil) then Exit; if (RT^.FFramebuffer=nil) then Exit; if (GP=nil) then Exit; if (not BeginCmdBuffer) then Exit; if (FRenderPass=VK_NULL_HANDLE) or (CompareByte(FRender,RT^,SizeOf(TvRenderPassBeginInfo))<>0) then begin //reset render pass EndRenderPass; FRender:=RT^; rinfo:=FRender.GetRInfo; if FRender.FFramebuffer.IsImageless then begin ainfo:=FRender.GetAInfo; rinfo.pNext:=@ainfo; end; Inc(cmd_count); //start render pass vkCmdBeginRenderPass(FCmdbuf,@rinfo,VK_SUBPASS_CONTENTS_INLINE); FRenderPass:=rinfo.renderPass; end; Femulate_primtype:=GP.Key.emulate_primtype; Fshader_primtype :=GP.Key.shader_primtype; BindPipeline(BP_GRAPHICS,GP.FHandle); BindLayout (BP_GRAPHICS,GP.Key.FShaderGroup.FLayout); RefTo(FRender.FRenderPass ); RefTo(FRender.FFramebuffer); RefTo(GP); Result:=True; end; Function TvCustomCmdBuffer.IsRenderPass:Boolean; begin Result:=False; if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; Result:=(FRenderPass<>VK_NULL_HANDLE); end; Procedure TvCustomCmdBuffer.EndRenderPass; begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (FCmdbuf=VK_NULL_HANDLE) then Exit; if (FRenderPass<>VK_NULL_HANDLE) then begin Inc(cmd_count); vkCmdEndRenderPass(FCmdbuf); FRenderPass:=VK_NULL_HANDLE; // FCurrLayout[BP_GRAPHICS]:=nil; FCurrBinds [BP_GRAPHICS]:=nil; FCurrGroup [BP_GRAPHICS]:=nil; // FCurrPipeline[BP_GRAPHICS]:=VK_NULL_HANDLE; end; end; function TvCustomCmdBuffer.BeforePushBarrier:TVkCommandBuffer; begin Result:=0; if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (not BeginCmdBuffer) then Exit; EndRenderPass; Result:=FCmdbuf; end; function TvCmdBuffer.BindCompute(CP:TvComputePipeline2):Boolean; begin Result:=False; if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (CP=nil) then Exit; if (not BeginCmdBuffer) then Exit; BindPipeline(BP_COMPUTE,CP.FHandle); BindLayout (BP_COMPUTE,CP.Key.FShaderGroup.FLayout); RefTo(CP); Result:=True; end; function TvCustomCmdBuffer.QueueSubmit:TVkResult; var info:TVkSubmitInfo; FFenceHandle:TVkFence; FHandles:array of TVkSemaphore; FStages :array of TVkPipelineStageFlags; i:Integer; t:PvSemaphoreWait; begin Result:=VK_ERROR_UNKNOWN; if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; case FCBState of cbFree, cbInit: begin //zero cmd buffer Exit(VK_SUCCESS); end; cbSubmit: begin //cmd buffer submitted Exit(VK_SUCCESS); end; else; end; if (FCmdbuf=VK_NULL_HANDLE) then Exit; EndCmdBuffer; info:=Default(TVkSubmitInfo); info.sType :=VK_STRUCTURE_TYPE_SUBMIT_INFO; info.commandBufferCount :=1; info.pCommandBuffers :=@FCmdbuf; if (FWaitSemaphoresCount<>0) then begin FHandles:=nil; FStages :=nil; SetLength(FHandles,FWaitSemaphoresCount); SetLength(FStages ,FWaitSemaphoresCount); i:=0; t:=FWaitSemaphores.Min; While (t<>nil) do begin FHandles[i]:=t^.FSemaphore.FHandle; FStages [i]:=t^.FWaitStage; Inc(i); // t:=FWaitSemaphores.Next(t); end; info.waitSemaphoreCount:=i; info.pWaitSemaphores :=@FHandles[0]; info.pWaitDstStageMask :=@FStages[0]; end; if (FSignalSemaphore<>nil) then begin info.signalSemaphoreCount:=1; info.pSignalSemaphores :=@FSignalSemaphore.FHandle; end; FFenceHandle:=VK_NULL_HANDLE; if (FFence<>nil) then begin FFenceHandle:=FFence.FHandle; end; //Writeln('vkQueueSubmit>'); Result:=FQueue.Submit(1,@info,FFenceHandle); //Writeln('vkQueueSubmit<'); ret:=Integer(Result); if (Result<>VK_SUCCESS) then begin Writeln(StdErr,'vkQueueSubmit:',Result); end; FCBState:=cbSubmit; end; function TvCustomCmdBuffer.Wait(timeout:TVkUInt64):TVkResult; begin Result:=VK_ERROR_UNKNOWN; if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (FFence=nil) then Exit; case FCBState of cbFree, cbInit: begin //zero cmd buffer Exit(VK_SUCCESS); end; cbSubmit: begin //cmd buffer submitted Result:=FFence.Wait(timeout); end; else //idk why it called in this state Exit(VK_ERROR_UNKNOWN); end; end; function TvCustomCmdBuffer.Status:TVkResult; begin Result:=VK_ERROR_UNKNOWN; if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (FFence=nil) then Exit; case FCBState of cbFree, cbInit: begin //zero cmd buffer Exit(VK_SUCCESS); end; cbSubmit: begin //cmd buffer submitted Result:=FFence.Status; end; else //idk why it called in this state Exit(VK_ERROR_UNKNOWN); end; end; Procedure TvCustomCmdBuffer.ReleaseResource; begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; ReleaseAllDependencies(Self); ReleaseAllPlannedTriggers; FreeAllDescriptorCache; FreeAllSemaphores; cmd_count:=0; ret:=0; end; Procedure TvCustomCmdBuffer.FreeAllSemaphores; var node:PvSemaphoreWait; begin if IsLinearAlloc then begin FWaitSemaphores:=Default(TvSemaphoreWaitSet); end else begin node:=FWaitSemaphores.Min; while (node<>nil) do begin FWaitSemaphores.delete(node); OnFree(node); node:=FWaitSemaphores.Min; end; end; FWaitSemaphoresCount:=0; end; Procedure TvCustomCmdBuffer.AddWaitSemaphore(S:TvSemaphore;W:TVkPipelineStageFlags); Var node:PvSemaphoreWait; begin if (S=nil) then Exit; node:=FWaitSemaphores.Find(@S); if (node=nil) then begin node:=OnAlloc(SizeOf(TvSemaphoreWait)); node^.FSemaphore:=S; node^.FWaitStage:=W; // FWaitSemaphores.Insert(node); // Inc(FWaitSemaphoresCount); end; end; function TvCustomCmdBuffer.GetSignaledSemaphore:TvSemaphore; begin Result:=FSignalSemaphore; end; Procedure TvCustomCmdBuffer.ReleaseAllPlannedTriggers; var node:p_cmd_track_deferred; begin node:=FPlannedTriggers.Min; while (node<>nil) do begin //deffered trigger vm_map_track_trigger(p_proc.p_vmspace,node^.start,node^.__end,node^.exclude,M_GPU_APPLY); if IsLinearAlloc then begin node:=FPlannedTriggers.Next(node); end else begin FPlannedTriggers.delete(node); OnFree(node); // node:=FPlannedTriggers.Min; end; end; if IsLinearAlloc then begin FPlannedTriggers:=Default(t_cmd_track_deferred_set); end; end; Procedure TvCustomCmdBuffer.AddPlannedTrigger(start,__end:QWORD;exclude:Pointer); Var tmp:t_cmd_track_deferred; node:p_cmd_track_deferred; begin //planned trigger vm_map_track_trigger(p_proc.p_vmspace,start,__end,exclude,M_GPU_PLANNED); tmp:=Default(t_cmd_track_deferred); tmp.start :=start; tmp. __end :=__end; tmp.exclude:=exclude; node:=FPlannedTriggers.Find(@tmp); if (node=nil) then begin node:=OnAlloc(SizeOf(t_cmd_track_deferred)); node^:=tmp; // FPlannedTriggers.Insert(node); end; end; Procedure TvCustomCmdBuffer.FreeAllDescriptorCache; var node:PvDescriptorCache; begin if IsLinearAlloc then begin FDescriptorCacheSet:=Default(TvDescriptorCacheSet); end else begin node:=FDescriptorCacheSet.Min; while (node<>nil) do begin FDescriptorCacheSet.delete(node); OnFree(node); node:=FDescriptorCacheSet.Min; end; end; end; Procedure TvCustomCmdBuffer.BindLayout(BindPoint:TVkPipelineBindPoint;F:TvPipelineLayout); begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (F=nil) then Exit; if (FCurrLayout[BindPoint]<>F) then begin FCurrLayout[BindPoint]:=F; FCurrBinds [BindPoint]:=nil; FCurrGroup [BindPoint]:=nil; end; end; Procedure TvCustomCmdBuffer.BindSet(BindPoint:TVkPipelineBindPoint;fset:TVkUInt32;FHandle:TVkDescriptorSet); begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (FHandle=VK_NULL_HANDLE) then Exit; if (FCurrLayout[BindPoint]=nil) then Exit; if (not BeginCmdBuffer) then Exit; Inc(cmd_count); vkCmdBindDescriptorSets(FCmdbuf, BindPoint, FCurrLayout[BindPoint].FHandle, fset,1, @FHandle, 0,nil); end; Procedure TvCustomCmdBuffer.PushConstant(BindPoint:TVkPipelineBindPoint;stageFlags:TVkShaderStageFlags;offset,size:TVkUInt32;const pValues:PVkVoid); begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (pValues=nil) or (size=0) then Exit; if (FCurrLayout[BindPoint]=nil) then Exit; if (not BeginCmdBuffer) then Exit; Inc(cmd_count); vkCmdPushConstants(FCmdbuf, FCurrLayout[BindPoint].FHandle, stageFlags, offset,size, pValues); end; Procedure TvCustomCmdBuffer.DispatchDirect(X,Y,Z:TVkUInt32); begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (FCurrPipeline[BP_COMPUTE]=VK_NULL_HANDLE) then Exit; if (not BeginCmdBuffer) then Exit; ApplyDescriptorCache(BP_COMPUTE); Inc(cmd_count); vkCmdDispatch(FCmdbuf,X,Y,Z); end; Procedure TvCustomCmdBuffer.BindVertexBuffers(const FAttrBuilder:TvAttrBuilder); var i,c:Integer; rb:TvHostBuffer; Buffers:array[0..31] of TVkBuffer; Offsets:array[0..31] of TVkDeviceSize; last_binding:TVkUInt32; last_size :TVkUInt32; diff:TVkDeviceSize; begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; c:=FAttrBuilder.FBindDescsCount; if (c=0) then Exit; if (not BeginCmdBuffer) then Exit; rb:=nil; last_binding:=0; last_size :=0; For i:=0 to c-1 do With FAttrBuilder.FBindVBufs[i] do begin rb:=FetchHostBuffer(Self,QWORD(min_addr),GetSize); if (last_binding<>binding) then begin //flush if (last_size<>0) then begin vkCmdBindVertexBuffers(FCmdbuf,last_binding,last_size,@Buffers,@Offsets); last_size:=0; end; // last_binding:=binding; end; diff:=QWORD(min_addr)-rb.FAddr; Buffers[last_size]:=rb.FHandle; Offsets[last_size]:=diff; Inc(last_size); end; //flush if (last_size<>0) then begin vkCmdBindVertexBuffers(FCmdbuf,last_binding,last_size,@Buffers,@Offsets); end; end; Procedure TvCustomCmdBuffer.SetVertexInput(const FAttrBuilder:TvAttrBuilder); var input:TvVertexInputEXT; begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if not limits.VK_EXT_vertex_input_dynamic_state then Exit; if (not BeginCmdBuffer) then Exit; FAttrBuilder.Export2(input); if (vkCmdSetVertexInputEXT=nil) then begin TPFN_vkVoidFunction(vkCmdSetVertexInputEXT):=vkGetInstanceProcAddr(VulkanApp.FInstance,'vkCmdSetVertexInputEXT'); end; vkCmdSetVertexInputEXT(FCmdbuf, input.vertexBindingDescriptionCount, @input.VertexBindingDescriptions[0], input.vertexAttributeDescriptionCount, @input.VertexAttributeDescriptions[0]); end; Procedure TvCustomCmdBuffer.ClearDepthStencilImage(image:TVkImage; imageLayout:TVkImageLayout; const pDepthStencil:PVkClearDepthStencilValue; rangeCount:TVkUInt32; const pRanges:PVkImageSubresourceRange); begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; EndRenderPass; if (not BeginCmdBuffer) then Exit; Inc(cmd_count); vkCmdClearDepthStencilImage( FCmdbuf, image, imageLayout, pDepthStencil, rangeCount, pRanges); end; Procedure TvCustomCmdBuffer.ClearDepthStencilImage(image:TVkImage; imageLayout:TVkImageLayout; const pDepthStencil:PVkClearDepthStencilValue; const range:TVkImageSubresourceRange); begin ClearDepthStencilImage(image, imageLayout, pDepthStencil, 1, @range); end; Procedure TvCustomCmdBuffer.ClearColorImage(image:TVkImage; imageLayout:TVkImageLayout; const pColor:PVkClearColorValue; rangeCount:TVkUInt32; const pRanges:PVkImageSubresourceRange); begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; EndRenderPass; if (not BeginCmdBuffer) then Exit; Inc(cmd_count); vkCmdClearColorImage( FCmdbuf, image, imageLayout, pColor, rangeCount, pRanges); end; Procedure TvCustomCmdBuffer.ResolveImage(srcImage:TVkImage; srcImageLayout:TVkImageLayout; dstImage:TVkImage; dstImageLayout:TVkImageLayout; regionCount:TVkUInt32; const pRegions:PVkImageResolve); begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; EndRenderPass; if (not BeginCmdBuffer) then Exit; Inc(cmd_count); vkCmdResolveImage( FCmdbuf, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions); end; procedure TvCustomCmdBuffer.CopyBufferToImage(srcBuffer:TVkBuffer; dstImage:TVkImage; dstImageLayout: TVkImageLayout; regionCount:TVkUInt32; const pRegions:PVkBufferImageCopy); begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; EndRenderPass; if (not BeginCmdBuffer) then Exit; Inc(cmd_count); vkCmdCopyBufferToImage( FCmdbuf, srcBuffer, dstImage, dstImageLayout, regionCount, pRegions); end; procedure TvCustomCmdBuffer.CopyImageToBuffer(srcImage:TVkImage; srcImageLayout:TVkImageLayout; dstBuffer:TVkBuffer; regionCount:TVkUInt32; const pRegions:PVkBufferImageCopy); begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; EndRenderPass; if (not BeginCmdBuffer) then Exit; Inc(cmd_count); vkCmdCopyImageToBuffer( FCmdbuf, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions); end; procedure TvCustomCmdBuffer.BufferMemoryBarrier(buffer:TVkBuffer; srcAccessMask:TVkAccessFlags; dstAccessMask:TVkAccessFlags; offset,size:TVkDeviceSize; srcStageMask:TVkPipelineStageFlags; dstStageMask:TVkPipelineStageFlags); begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (not BeginCmdBuffer) then Exit; Inc(cmd_count); vkBufferMemoryBarrier(FCmdbuf, buffer, srcAccessMask, dstAccessMask, offset,size, srcStageMask, dstStageMask); end; Const VK_ACCESS_ANY= ord(VK_ACCESS_INDIRECT_COMMAND_READ_BIT ) or ord(VK_ACCESS_INDEX_READ_BIT ) or ord(VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT ) or ord(VK_ACCESS_UNIFORM_READ_BIT ) or ord(VK_ACCESS_INPUT_ATTACHMENT_READ_BIT ) or ord(VK_ACCESS_SHADER_READ_BIT ) or ord(VK_ACCESS_SHADER_WRITE_BIT ) or ord(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT ) or ord(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT ) or ord(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT ) or ord(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) or ord(VK_ACCESS_TRANSFER_READ_BIT ) or ord(VK_ACCESS_TRANSFER_WRITE_BIT ) or ord(VK_ACCESS_HOST_READ_BIT ) or ord(VK_ACCESS_HOST_WRITE_BIT ) or ord(VK_ACCESS_MEMORY_READ_BIT ) or ord(VK_ACCESS_MEMORY_WRITE_BIT ); Procedure TvCmdBuffer.dmaData1(src,dst:Pointer;byteCount:DWORD;isBlocking:Boolean); var srcb,dstb:TvHostBuffer; info:TVkBufferCopy; begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; EndRenderPass; if (not BeginCmdBuffer) then Exit; DebugReport.CmdBeginLabel(FCmdbuf,'dmaData'); srcb:=FetchHostBuffer(Self,QWORD(src),byteCount); Assert(srcb<>nil); dstb:=FetchHostBuffer(Self,QWORD(dst),byteCount); Assert(dstb<>nil); info:=Default(TVkBufferCopy); info.srcOffset:=QWORD(src)-srcb.FAddr; info.dstOffset:=QWORD(dst)-dstb.FAddr; info.size :=byteCount; Inc(cmd_count); vkBufferMemoryBarrier(FCmdbuf, srcb.FHandle, VK_ACCESS_ANY, ord(VK_ACCESS_TRANSFER_READ_BIT), info.srcOffset,byteCount, ord(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT), ord(VK_PIPELINE_STAGE_TRANSFER_BIT)); Inc(cmd_count); vkBufferMemoryBarrier(FCmdbuf, dstb.FHandle, VK_ACCESS_ANY, ord(VK_ACCESS_TRANSFER_WRITE_BIT), info.dstOffset,byteCount, ord(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT), ord(VK_PIPELINE_STAGE_TRANSFER_BIT)); Inc(cmd_count); vkCmdCopyBuffer(FCmdbuf, srcb.FHandle, dstb.FHandle, 1,@info); if isBlocking then begin Inc(cmd_count); vkMemoryBarrier(FCmdbuf, ord(VK_ACCESS_TRANSFER_WRITE_BIT), //srcAccessMask VK_ACCESS_ANY, //dstAccessMask ord(VK_PIPELINE_STAGE_TRANSFER_BIT), //srcStageMask ord(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT)); //dstStageMask end; DebugReport.CmdEndLabel(FCmdbuf); AddPlannedTrigger(QWORD(dst),QWORD(dst)+byteCount,nil); end; Procedure TvCmdBuffer.dmaData2(src:DWORD;dst:Pointer;byteCount:DWORD;isBlocking:Boolean); var dstb:TvHostBuffer; dstOffset:TVkDeviceSize; begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; byteCount:=byteCount and (not 3); //4 byte align EndRenderPass; if (not BeginCmdBuffer) then Exit; DebugReport.CmdBeginLabel(FCmdbuf,'dmaData'); dstb:=FetchHostBuffer(Self,QWORD(dst),byteCount); Assert(dstb<>nil); dstOffset:=QWORD(dst)-dstb.FAddr; Inc(cmd_count); vkBufferMemoryBarrier(FCmdbuf, dstb.FHandle, VK_ACCESS_ANY, ord(VK_ACCESS_TRANSFER_WRITE_BIT), dstOffset,byteCount, ord(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT), ord(VK_PIPELINE_STAGE_TRANSFER_BIT)); Inc(cmd_count); vkCmdFillBuffer(FCmdbuf, dstb.FHandle, dstOffset, byteCount, src); if isBlocking then begin Inc(cmd_count); vkMemoryBarrier(FCmdbuf, ord(VK_ACCESS_TRANSFER_WRITE_BIT), //srcAccessMask VK_ACCESS_ANY, //dstAccessMask ord(VK_PIPELINE_STAGE_TRANSFER_BIT), //srcStageMask ord(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT)); //dstStageMask end; DebugReport.CmdEndLabel(FCmdbuf); AddPlannedTrigger(QWORD(dst),QWORD(dst)+byteCount,nil); end; const VK_ACCESS_CS= ord(VK_ACCESS_UNIFORM_READ_BIT) or ord(VK_ACCESS_SHADER_READ_BIT ) or ord(VK_ACCESS_SHADER_WRITE_BIT); VK_ACCESS_PS= ord(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT ) or ord(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); Procedure TvCmdBuffer.WriteEos(eventType:Byte;dst:Pointer;value:DWORD;isBlocking:Boolean); var rb:TvHostBuffer; BufOffset:TVkDeviceSize; begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; EndRenderPass; if (not BeginCmdBuffer) then Exit; DebugReport.CmdBeginLabel(FCmdbuf,'WriteEos'); Case eventType of CS_DONE: begin Inc(cmd_count); DebugReport.CmdInsertLabel(FCmdbuf,'CS_DONE'); vkMemoryBarrier(FCmdbuf, VK_ACCESS_CS, //srcAccessMask ord(VK_ACCESS_TRANSFER_WRITE_BIT), //dstAccessMask ord(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT), //srcStageMask ord(VK_PIPELINE_STAGE_TRANSFER_BIT)); //dstStageMask end; PS_DONE: begin Inc(cmd_count); DebugReport.CmdInsertLabel(FCmdbuf,'PS_DONE'); vkMemoryBarrier(FCmdbuf, VK_ACCESS_PS, //srcAccessMask ord(VK_ACCESS_TRANSFER_WRITE_BIT), //dstAccessMask ord(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT), //srcStageMask ord(VK_PIPELINE_STAGE_TRANSFER_BIT)); //dstStageMask end; else Assert(false,'WriteEos.eventType'); end; rb:=FetchHostBuffer(Self,QWORD(dst),4); Assert(rb<>nil); BufOffset:=QWORD(dst)-rb.FAddr; Inc(cmd_count); vkCmdFillBuffer(FCmdbuf, rb.FHandle, BufOffset, 4,value); if isBlocking then begin Inc(cmd_count); vkMemoryBarrier(FCmdbuf, ord(VK_ACCESS_TRANSFER_WRITE_BIT), //srcAccessMask VK_ACCESS_ANY, //dstAccessMask ord(VK_PIPELINE_STAGE_TRANSFER_BIT), //srcStageMask ord(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT)); //dstStageMask end; DebugReport.CmdEndLabel(FCmdbuf); AddPlannedTrigger(QWORD(dst),QWORD(dst)+4,nil); end; const VK_ACCESS_DB= ord(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT ) or ord(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); VK_STAGE_DB= ord(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) or ord(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT) or ord(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT); Procedure TvCmdBuffer.WriteEvent(eventType:Byte); begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; EndRenderPass; if (not BeginCmdBuffer) then Exit; Case eventType of CS_PARTIAL_FLUSH: begin Inc(cmd_count); DebugReport.CmdInsertLabel(FCmdbuf,'CS_PARTIAL_FLUSH'); vkMemoryBarrier(FCmdbuf, VK_ACCESS_CS, //srcAccessMask VK_ACCESS_ANY, //dstAccessMask ord(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT), //srcStageMask ord(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT)); //dstStageMask end; DB_CACHE_FLUSH_AND_INV: //DB begin Inc(cmd_count); DebugReport.CmdInsertLabel(FCmdbuf,'DB_CACHE_FLUSH_AND_INV'); vkMemoryBarrier(FCmdbuf, VK_ACCESS_DB, //srcAccessMask VK_ACCESS_ANY, //dstAccessMask VK_STAGE_DB, //srcStageMask ord(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT)); //dstStageMask end; FLUSH_AND_INV_DB_META: //HTILE begin Inc(cmd_count); DebugReport.CmdInsertLabel(FCmdbuf,'FLUSH_AND_INV_DB_META'); vkMemoryBarrier(FCmdbuf, VK_ACCESS_DB, //srcAccessMask VK_ACCESS_ANY, //dstAccessMask VK_STAGE_DB, //srcStageMask ord(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT)); //dstStageMask end; FLUSH_AND_INV_CB_META: //CMASK begin Inc(cmd_count); DebugReport.CmdInsertLabel(FCmdbuf,'FLUSH_AND_INV_CB_META'); vkMemoryBarrier(FCmdbuf, VK_ACCESS_PS, //srcAccessMask ord(VK_ACCESS_TRANSFER_WRITE_BIT), //dstAccessMask ord(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT), //srcStageMask ord(VK_PIPELINE_STAGE_TRANSFER_BIT)); //dstStageMask end; FLUSH_AND_INV_CB_PIXEL_DATA: //CB begin Inc(cmd_count); DebugReport.CmdInsertLabel(FCmdbuf,'FLUSH_AND_INV_CB_PIXEL_DATA'); vkMemoryBarrier(FCmdbuf, VK_ACCESS_PS, //srcAccessMask ord(VK_ACCESS_TRANSFER_WRITE_BIT), //dstAccessMask ord(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT), //srcStageMask ord(VK_PIPELINE_STAGE_TRANSFER_BIT)); //dstStageMask end; CACHE_FLUSH_AND_INV_EVENT: //CB,DB begin Inc(cmd_count); vkMemoryBarrier(FCmdbuf, VK_ACCESS_PS or VK_ACCESS_DB, //srcAccessMask VK_ACCESS_ANY, //dstAccessMask VK_STAGE_DB or ord(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT), //srcStageMask ord(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT)); //dstStageMask end; else Assert(false,'WriteEvent.eventType'); end; end; Procedure TvCustomCmdBuffer.InsertLabel(pLabelName:PVkChar); begin if (Self=nil) then Exit; if (DebugReport.FCmdInsertDebugUtilsLabel=nil) then Exit; if (not BeginCmdBuffer) then Exit; DebugReport.CmdInsertLabel(FCmdbuf,pLabelName); end; Procedure TvCustomCmdBuffer.BeginLabel(pLabelName:PVkChar); begin if (Self=nil) then Exit; if (DebugReport.FCmdBeginDebugUtilsLabel=nil) then Exit; if (not BeginCmdBuffer) then Exit; DebugReport.CmdBeginLabel(FCmdbuf,pLabelName); end; Procedure TvCustomCmdBuffer.EndLabel(); begin if (Self=nil) then Exit; if (not IsAllocated) then Exit; DebugReport.CmdEndLabel(FCmdbuf); end; function GET_INDEX_TYPE_SIZE(INDEX_TYPE:TVkIndexType):Byte; inline; const _s:array[0..1] of Byte=(2,4); begin Result:=_s[ord(INDEX_TYPE) and 1]; end; Procedure TvCmdBuffer.DrawIndexOffset2(IndexBase:Pointer;indexOffset,vertexOffset,indexCount:DWORD); var rb:TvHostBuffer; Size:TVkDeviceSize; BufOffset:TVkDeviceSize; i,h:DWORD; begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (FRenderPass=VK_NULL_HANDLE) then Exit; if (FCurrPipeline[BP_GRAPHICS]=VK_NULL_HANDLE) then Exit; if (not BeginCmdBuffer) then Exit; ApplyDescriptorCache(BP_GRAPHICS); if (FinstanceCount=0) then FinstanceCount:=1; Size:=(indexOffset+indexCount)*GET_INDEX_TYPE_SIZE(FINDEX_TYPE); rb:=FetchHostBuffer(Self,QWORD(IndexBase),Size); Assert(rb<>nil); Inc(cmd_count); BufOffset:=QWORD(IndexBase)-rb.FAddr; vkCmdBindIndexBuffer( Fcmdbuf, rb.FHandle, BufOffset, FINDEX_TYPE); Inc(cmd_count); Case Femulate_primtype of 0: begin vkCmdDrawIndexed( Fcmdbuf, indexCount, //indexCount FinstanceCount, //instanceCount indexOffset, //firstIndex vertexOffset, //vertexOffset 0); //firstInstance end; DI_PT_QUADLIST: begin InsertLabel('DI_PT_QUADLIST'); Assert(FinstanceCount<=1,'instance DI_PT_QUADLIST'); h:=indexCount div 4; if (h>0) then h:=h-1; For i:=0 to h do begin vkCmdDrawIndexed( Fcmdbuf, 4, //indexCount 1, //instanceCount indexOffset+i*4, //firstIndex vertexOffset, //vertexOffset 0); //firstInstance end; end; else Assert(false,'TODO'); end; end; Procedure TvCmdBuffer.DrawIndexAuto(vertexOffset,indexCount:DWORD); var i,h:DWORD; begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (FRenderPass=VK_NULL_HANDLE) then Exit; if (FCurrPipeline[BP_GRAPHICS]=VK_NULL_HANDLE) then Exit; if (not BeginCmdBuffer) then Exit; ApplyDescriptorCache(BP_GRAPHICS); if (FinstanceCount=0) then FinstanceCount:=1; Case Femulate_primtype of 0: begin Inc(cmd_count); vkCmdDraw( FCmdbuf, indexCount, //vertexCount FinstanceCount, //instanceCount vertexOffset, //firstVertex 0); //firstInstance end; DI_PT_RECTLIST: begin InsertLabel('DI_PT_RECTLIST'); Assert(FinstanceCount<=1,'instance DI_PT_RECTLIST'); { VK_EXT_primitive_topology_list_restart ??? 0 3 1 2 } //0 1 2 //0 2 3 if (Fshader_primtype=-1) then begin h:=indexCount div 3; if (h>0) then h:=h-1; For i:=0 to h do begin Inc(cmd_count); vkCmdDraw( FCmdbuf, 4, //vertexCount 1, //instanceCount vertexOffset+i*3, //firstVertex 0); //firstInstance end; end else begin //geom shader emulate Inc(cmd_count); vkCmdDraw( FCmdbuf, indexCount, //vertexCount FinstanceCount, //instanceCount vertexOffset, //firstVertex 0); //firstInstance end; end; //DI_PT_LINELOOP :; DI_PT_QUADLIST: begin InsertLabel('DI_PT_QUADLIST'); Assert(FinstanceCount<=1,'instance DI_PT_QUADLIST'); h:=indexCount div 4; if (h>0) then h:=h-1; For i:=0 to h do begin Inc(cmd_count); vkCmdDraw( FCmdbuf, 4, //vertexCount 1, //instanceCount vertexOffset+i*4, //firstVertex 0); //firstInstance end; end; //DI_PT_QUADSTRIP:; //DI_PT_POLYGON :; else begin Assert(false,'TODO'); end; end; end; // function TvCustomCmdBuffer.FetchDescriptorCache(layout:TvPipelineLayout):PvDescriptorCache; begin Result:=FDescriptorCacheSet.Find(@layout); if (Result=nil) then begin Result:=AllocDescriptorCache(Self,layout); FDescriptorCacheSet.Insert(Result); end; end; function TvCustomCmdBuffer.FetchDescriptorCache(BindPoint:TVkPipelineBindPoint):PvDescriptorCache; begin Result:=nil; if (FCurrLayout[BindPoint]=nil) then Exit; Result:=FCurrBinds[BindPoint]; if (Result=nil) then begin Result:=FetchDescriptorCache(FCurrLayout[BindPoint]); // FCurrBinds[BindPoint]:=Result; // Result^.SetAllChange; end; end; function TvCustomCmdBuffer.FetchDescriptorInterface(BindPoint:TVkPipelineBindPoint):TvDescriptorInterface; begin Result:=Default(TvDescriptorInterface); if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; Result:=TvDescriptorInterface(FetchDescriptorCache(BindPoint)); end; Procedure TvCustomCmdBuffer.ApplyDescriptorCache(BindPoint:TVkPipelineBindPoint); var Cache:PvDescriptorCache; Group:TvDescriptorGroup; begin if (FCurrLayout[BindPoint]=nil) then Exit; Cache:=FCurrBinds[BindPoint]; if (Cache=nil) then Exit; if (Cache^.p_count_all=0) then Exit; //no sets Group:=FCurrGroup[BindPoint]; if (Cache^.p_change_any) or (Group=nil) then begin Group:=FetchDescriptorGroup(Self,FCurrLayout[BindPoint]); // FCurrGroup[BindPoint]:=Group; // Group.Bind(Cache); // BindSets(BindPoint,Group); // Cache^.ClearAllChange; end; //VK_KHR_push_descriptor vkCmdPushDescriptorSetKHR TODO end; Procedure TvCustomCmdBuffer.BindSets(BindPoint:TVkPipelineBindPoint;F:TvDescriptorGroup); var A:array[0..6] of TVkDescriptorSet; i,start,pos:Integer; procedure Flush; inline; begin Inc(cmd_count); vkCmdBindDescriptorSets(FCmdbuf, BindPoint, FCurrLayout[BindPoint].FHandle, start,pos, @A[0], 0,nil); pos:=0; end; begin if (Self=nil) then begin Writeln(stderr,'Self=nil,',{$I %LINE%}); Exit; end; if (F=nil) then Exit; if (FCurrLayout[BindPoint]=nil) then Exit; if (Length(F.FSets)=0) then Exit; if (not BeginCmdBuffer) then Exit; pos:=0; For i:=0 to High(F.FSets) do begin if F.FSets[i].IsValid then begin if (pos=0) then begin start:=i; end; A[pos]:=F.FSets[i].FHandle; Inc(pos); if (pos=7) then begin Flush; end; end else if (pos<>0) then begin Flush; end; end; if (pos<>0) then begin Flush; end; end; // end.