unit vRender; {$mode objfpc}{$H+} interface uses Classes, SysUtils, RWLock, sys_types, g23tree, ps4_libSceVideoOut, si_ci_vi_merged_enum, Vulkan, vDevice, vMemory, vShader, vShaderExt, vPipeline, //vSetsPools, vImage; type TvComputePipeline2=class(TvComputePipeline) FRefs:ptruint; Procedure Acquire(Sender:TObject); Procedure Release(Sender:TOBject); end; TvRenderPass=class(vPipeline.TvRenderPass) AtCount:TVkUInt32; ColorAt:array[0..8] of TVkAttachmentDescription; ColorRef:array[0..7] of TVkAttachmentReference; //subpass.colorAttachmentCount pDepthStencilRef:TVkAttachmentReference; subpass:TVkSubpassDescription; dependency:TVkSubpassDependency; Procedure Clear; Procedure SetZorderStage(s:TVkPipelineStageFlags); Procedure AddColorRef(id:TVkUInt32;IMAGE_USAGE:Byte); Procedure SetDepthStencilRef(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); Function Compile:Boolean; end; TvGraphicsPipeline=class(TvPipeline) FRenderPass:TvRenderPass; Viewports:array[0..15] of TVkViewport; //viewportState.viewportCount Scissors :array[0..15] of TVkRect2D; //viewportState.scissorCount ColorBlends:array[0..7] of TVkPipelineColorBlendAttachmentState; //colorBlending.attachmentCount FShaderGroup:TvShaderGroup; dynamicStates:array[0..1] of TVkDynamicState; //dynamicState.dynamicStateCount vertexInputInfo:TVkPipelineVertexInputStateCreateInfo; inputAssembly:TVkPipelineInputAssemblyStateCreateInfo; viewportState:TVkPipelineViewportStateCreateInfo; rasterizer:TVkPipelineRasterizationStateCreateInfo; multisampling:TVkPipelineMultisampleStateCreateInfo; colorBlending:TVkPipelineColorBlendStateCreateInfo; DepthStencil:TVkPipelineDepthStencilStateCreateInfo; dynamicState:TVkPipelineDynamicStateCreateInfo; emulate_primtype:Integer; Procedure SetPrimType(t:TVkPrimitiveTopology); Procedure SetPrimReset(enable:TVkBool32); Procedure AddVPort(const V:TVkViewport;const S:TVkRect2D); Procedure AddBlend(const b:TVkPipelineColorBlendAttachmentState); Procedure Clear; procedure SetBlendColors(P:PSingle); procedure SetRenderPass(RenderPass:TvRenderPass); function Compile:Boolean; end; TvRenderTargets=class FRenderPass:TvRenderPass; FPipeline:TvGraphicsPipeline; FFramebuffer:TvFramebuffer; FRenderArea:TVkRect2D; FClearValuesCount:TVkUInt32; FClearValues:array[0..8] of TVkClearValue; // FRefs:ptruint; // Procedure AddClearColor(clr:TVkClearValue); Function GetInfo:TVkRenderPassBeginInfo; class function c(const a,b:TvRenderTargets):Integer; Destructor Destroy; override; Procedure Acquire(Sender:TObject); Procedure Release(Sender:TOBject); end; TvRenderTargetsSet=specialize T23treeSet; Function GetDepthStencilLayout(DEPTH_USAGE,STENCIL_USAGE:Byte):TVkImageLayout; Function GetDepthStencilAccessMask(DEPTH_USAGE,STENCIL_USAGE:Byte):TVkAccessFlags; Function GetColorAccessMask(IMAGE_USAGE:Byte):TVkAccessFlags; implementation { const buf_ext:TVkExternalMemoryBufferCreateInfo=( sType:VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO; pNext:nil; handleTypes:ord(VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT); ); 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); ); } Procedure TvRenderPass.Clear; begin if (FHandle<>VK_NULL_HANDLE) then begin vkDestroyRenderPass(Device.FHandle,FHandle,nil); FHandle:=VK_NULL_HANDLE; end; AtCount:=0; FillChar(ColorAt,SizeOf(ColorAt),0); FillChar(ColorRef,SizeOf(ColorRef),0); subpass:=Default(TVkSubpassDescription); subpass.pipelineBindPoint:=VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.inputAttachmentCount :=0; subpass.pInputAttachments :=nil; subpass.colorAttachmentCount:=0; subpass.pColorAttachments :=@ColorRef; subpass.pResolveAttachments :=nil; //colorAttachmentCount VK_ATTACHMENT_UNUSED subpass.pDepthStencilAttachment:=nil; //1 subpass.preserveAttachmentCount:=0; subpass.pPreserveAttachments :=nil; dependency:=Default(TVkSubpassDependency); dependency.srcSubpass :=VK_SUBPASS_EXTERNAL; dependency.dstSubpass :=0; dependency.srcStageMask :=0{ord(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)}; dependency.dstStageMask :=0{ord(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)}; dependency.srcAccessMask:=0; dependency.dstAccessMask:=0{ord(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT)}; end; Procedure TvRenderPass.SetZorderStage(s:TVkPipelineStageFlags); begin dependency.srcStageMask:=dependency.srcStageMask or s; dependency.dstStageMask:=dependency.dstStageMask or s; end; Procedure TvRenderPass.AddColorRef(id:TVkUInt32;IMAGE_USAGE:Byte); var am:TVkAccessFlags; begin if (subpass.colorAttachmentCount>7) then Exit; ColorRef[subpass.colorAttachmentCount].attachment:=id; ColorRef[subpass.colorAttachmentCount].layout :={VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}VK_IMAGE_LAYOUT_GENERAL; Inc(subpass.colorAttachmentCount); 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 TvRenderPass.SetDepthStencilRef(id:TVkUInt32;DEPTH_USAGE,STENCIL_USAGE:Byte); var am:TVkAccessFlags; begin subpass.pDepthStencilAttachment:=@pDepthStencilRef; pDepthStencilRef.attachment :=id; pDepthStencilRef.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 TvRenderPass.AddColorAt(format:TVkFormat;IMAGE_USAGE:Byte;samples:TVkSampleCountFlagBits); begin if (AtCount>8) then Exit; format:=vkFixFormatSupport(format,VK_IMAGE_TILING_OPTIMAL,ord(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)); ColorAt[AtCount]:=Default(TVkAttachmentDescription); ColorAt[AtCount].format :=format; ColorAt[AtCount].samples :=samples{VK_SAMPLE_COUNT_1_BIT}; With ColorAt[AtCount] 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 ColorAt[AtCount] 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; ColorAt[AtCount].stencilLoadOp :=VK_ATTACHMENT_LOAD_OP_DONT_CARE; ColorAt[AtCount].stencilStoreOp:=VK_ATTACHMENT_STORE_OP_DONT_CARE; With ColorAt[AtCount] do if (IMAGE_USAGE and TM_READ<>0) then begin initialLayout :={VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}VK_IMAGE_LAYOUT_GENERAL; end else begin initialLayout :=VK_IMAGE_LAYOUT_UNDEFINED; end; With ColorAt[AtCount] do finalLayout:={VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}VK_IMAGE_LAYOUT_GENERAL; Inc(AtCount); end; 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 TvRenderPass.AddDepthAt(format:TVkFormat;DEPTH_USAGE,STENCIL_USAGE:Byte); begin if (AtCount>8) then Exit; format:=vkFixFormatSupport(format,VK_IMAGE_TILING_OPTIMAL,ord(VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)); ColorAt[AtCount]:=Default(TVkAttachmentDescription); ColorAt[AtCount].format :=format; ColorAt[AtCount].samples :=VK_SAMPLE_COUNT_1_BIT; With ColorAt[AtCount] 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 ColorAt[AtCount] 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 ColorAt[AtCount] 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 ColorAt[AtCount] 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 ColorAt[AtCount] 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 ColorAt[AtCount] do finalLayout:=GetDepthStencilLayout(DEPTH_USAGE,STENCIL_USAGE); Inc(AtCount); end; Function TvRenderPass.Compile:Boolean; var r:TVkResult; info:TVkRenderPassCreateInfo; begin Result:=False; if (AtCount=0) then Exit; if (FHandle<>VK_NULL_HANDLE) then Exit(True); info:=Default(TVkRenderPassCreateInfo); info.sType :=VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; info.attachmentCount:=AtCount; info.pAttachments :=@ColorAt; info.subpassCount :=1; info.pSubpasses :=@subpass; info.dependencyCount:=1; info.pDependencies :=@dependency; r:=vkCreateRenderPass(Device.FHandle,@info,nil,@FHandle); if (r<>VK_SUCCESS) then begin Writeln(StdErr,'vkCreateRenderPass:',r); Exit; end; Result:=True; end; // Procedure TvGraphicsPipeline.SetPrimType(t:TVkPrimitiveTopology); begin Case ord(t) of ord(VK_PRIMITIVE_TOPOLOGY_POINT_LIST)..ord(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY): begin inputAssembly.topology:=t; emulate_primtype:=0; end; DI_PT_RECTLIST: begin inputAssembly.topology:=VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; emulate_primtype:=Integer(t); end; DI_PT_LINELOOP , DI_PT_QUADLIST , DI_PT_QUADSTRIP, DI_PT_POLYGON : begin inputAssembly.topology:=VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; emulate_primtype:=Integer(t); end; end; end; Procedure TvGraphicsPipeline.SetPrimReset(enable:TVkBool32); begin inputAssembly.primitiveRestartEnable:=enable; end; Procedure TvGraphicsPipeline.AddVPort(const V:TVkViewport;const S:TVkRect2D); begin if (s.extent.width=0) or (s.extent.height=0) then Assert(false); if (viewportState.viewportCount>15) then Exit; Viewports[viewportState.viewportCount]:=V; Scissors [viewportState.viewportCount]:=S; Inc(viewportState.viewportCount); viewportState.scissorCount:=viewportState.viewportCount; end; Procedure TvGraphicsPipeline.AddBlend(const b:TVkPipelineColorBlendAttachmentState); begin if (colorBlending.attachmentCount>7) then Exit; ColorBlends[colorBlending.attachmentCount]:=b; Inc(colorBlending.attachmentCount); end; Procedure TvGraphicsPipeline.Clear; begin if (FHandle<>VK_NULL_HANDLE) then begin vkDestroyPipeline(Device.FHandle,FHandle,nil); FHandle:=VK_NULL_HANDLE; end; vertexInputInfo:=Default(TVkPipelineVertexInputStateCreateInfo); vertexInputInfo.sType :=VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; inputAssembly:=Default(TVkPipelineInputAssemblyStateCreateInfo); inputAssembly.sType :=VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; inputAssembly.topology :=VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; inputAssembly.primitiveRestartEnable:=VK_FALSE; //VGT_MULTI_PRIM_IB_RESET_EN FillChar(Viewports ,SizeOf(Viewports),0); FillChar(Scissors ,SizeOf(Scissors) ,0); FillChar(ColorBlends,SizeOf(ColorBlends),0); FShaderGroup:=nil; viewportState:=Default(TVkPipelineViewportStateCreateInfo); viewportState.sType :=VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportState.viewportCount:=0; viewportState.pViewports :=@Viewports; viewportState.scissorCount :=0; viewportState.pScissors :=@Scissors; rasterizer:=Default(TVkPipelineRasterizationStateCreateInfo); rasterizer.sType :=VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer.depthClampEnable:=VK_FALSE; rasterizer.rasterizerDiscardEnable:=VK_FALSE; //DB_SHADER_CONTROL.KILL_ENABLE DB_RENDER_CONTROL.FORCE_COLOR_KILL rasterizer.polygonMode :=VK_POLYGON_MODE_FILL; //PA_SU_SC_MODE_CNTL.POLY_MODE POLYMODE_FRONT_PTYPE POLYMODE_BACK_PTYPE rasterizer.lineWidth :=1; //PA_SU_LINE_CNTL.WIDTH rasterizer.cullMode :=ord(VK_CULL_MODE_NONE); //CULL_FRONT CULL_BACK rasterizer.frontFace :=VK_FRONT_FACE_COUNTER_CLOCKWISE; //FACE rasterizer.depthBiasEnable :=VK_FALSE; rasterizer.depthBiasConstantFactor:=0; rasterizer.depthBiasClamp :=0; rasterizer.depthBiasSlopeFactor :=0; multisampling:=Default(TVkPipelineMultisampleStateCreateInfo); multisampling.sType :=VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.sampleShadingEnable :=VK_FALSE; multisampling.rasterizationSamples :=VK_SAMPLE_COUNT_1_BIT; multisampling.minSampleShading :=1; multisampling.pSampleMask :=nil; multisampling.alphaToCoverageEnable:=VK_FALSE; multisampling.alphaToOneEnable :=VK_FALSE; colorBlending:=Default(TVkPipelineColorBlendStateCreateInfo); colorBlending.sType :=VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; colorBlending.logicOpEnable :=VK_FALSE; colorBlending.logicOp :=VK_LOGIC_OP_COPY; colorBlending.attachmentCount :=0; colorBlending.pAttachments :=@ColorBlends; DepthStencil:=Default(TVkPipelineDepthStencilStateCreateInfo); DepthStencil.sType :=VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; DepthStencil.depthTestEnable :=VK_FALSE; DepthStencil.depthWriteEnable :=VK_FALSE; DepthStencil.depthCompareOp :=VK_COMPARE_OP_LESS; DepthStencil.depthBoundsTestEnable:=VK_FALSE; DepthStencil.stencilTestEnable :=VK_FALSE; //DepthStencil.front :TVkStencilOpState; //DepthStencil.back :TVkStencilOpState; DepthStencil.minDepthBounds :=0; DepthStencil.maxDepthBounds :=0; dynamicState:=Default(TVkPipelineDynamicStateCreateInfo); dynamicState.sType :=VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamicState.dynamicStateCount:=0{2}; dynamicState.pDynamicStates :=@dynamicStates; dynamicStates[0]:=VK_DYNAMIC_STATE_VIEWPORT; dynamicStates[1]:=VK_DYNAMIC_STATE_LINE_WIDTH; end; type PVec4f=^TVec4f; TVec4f=array[0..3] of Single; procedure TvGraphicsPipeline.SetBlendColors(P:PSingle); begin if (P=nil) then Exit; colorBlending.blendConstants:=PVec4f(P)^; end; procedure TvGraphicsPipeline.SetRenderPass(RenderPass:TvRenderPass); begin if (FRenderPass<>RenderPass) then begin FRenderPass:=RenderPass; end; end; function TvGraphicsPipeline.Compile:Boolean; type AVkPipelineShaderStageCreateInfo=array[0..6] of TVkPipelineShaderStageCreateInfo; var r:TVkResult; Stages:AVkPipelineShaderStageCreateInfo; // info.stageCount info:TVkGraphicsPipelineCreateInfo; begin Result:=False; if (FShaderGroup=nil) then Exit; if (FRenderPass=nil) then Exit; if (viewportState.viewportCount=0) then Exit; if (viewportState.scissorCount=0) then Exit; info:=Default(TVkGraphicsPipelineCreateInfo); Stages:=Default(AVkPipelineShaderStageCreateInfo); FShaderGroup.FKey.ExportStages(@Stages,@info.stageCount); if (info.stageCount=0) then Exit; if (not FShaderGroup.Compile) then Exit; if (not FRenderPass.Compile) then Exit; info.sType :=VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; info.pStages :=@Stages; info.pVertexInputState :=@vertexInputInfo; info.pInputAssemblyState:=@inputAssembly; info.pViewportState :=@viewportState; info.pRasterizationState:=@rasterizer; info.pMultisampleState :=@multisampling; info.pDepthStencilState :=@DepthStencil; info.pColorBlendState :=@colorBlending; info.pDynamicState :=@dynamicState; info.layout :=FShaderGroup.FLayout.FHandle; info.renderPass :=FRenderPass.FHandle; info.subpass :=0; info.basePipelineHandle :=VK_NULL_HANDLE; info.basePipelineIndex :=-1; r:=vkCreateGraphicsPipelines(Device.FHandle,VK_NULL_HANDLE,1,@info,nil,@FHandle); if (r<>VK_SUCCESS) then begin Writeln(StdErr,'vkCreateGraphicsPipelines:',r); exit; end; Result:=True; end; ///////// Procedure TvRenderTargets.AddClearColor(clr:TVkClearValue); begin if (FClearValuesCount>8) then Exit; FClearValues[FClearValuesCount]:=clr; Inc(FClearValuesCount); end; Function TvRenderTargets.GetInfo:TVkRenderPassBeginInfo; begin Result:=Default(TVkRenderPassBeginInfo); Result.sType :=VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; Result.renderPass :=FRenderPass.FHandle; Result.renderArea :=FRenderArea; Result.clearValueCount:=FClearValuesCount; Result.pClearValues :=FClearValues; Result.framebuffer :=FFramebuffer.FHandle; end; class function TvRenderTargets.c(const a,b:TvRenderTargets):Integer; begin Result:=CompareByte(a,b,SizeOf(Pointer)); end; Destructor TvRenderTargets.Destroy; begin FreeAndNil(FRenderPass); FreeAndNil(FPipeline); FreeAndNil(FFramebuffer); inherited; end; Procedure TvRenderTargets.Acquire(Sender:TObject); begin System.InterlockedIncrement(Pointer(FRefs)); end; Procedure TvRenderTargets.Release(Sender:TOBject); begin if System.InterlockedDecrement(Pointer(FRefs))=nil then begin Free; end; end; Procedure TvComputePipeline2.Acquire(Sender:TObject); begin System.InterlockedIncrement(Pointer(FRefs)); end; Procedure TvComputePipeline2.Release(Sender:TOBject); begin if System.InterlockedDecrement(Pointer(FRefs))=nil then begin Free; end; end; ////////////// end.