From a6818a6cf4cebef1687391beb83268c4d22e8830 Mon Sep 17 00:00:00 2001 From: kotofyt Date: Wed, 26 Aug 2026 21:07:12 +0300 Subject: [PATCH] render pass first work --- funnyassets/shaders/agx.shader | 4 +- game/client/worldrender.cpp | 20 +- materialsystem/vulkan/commandbuffer.cpp | 3 +- materialsystem/vulkan/commands.h | 5 +- materialsystem/vulkan/commands/draw.cpp | 253 ++++++++++++++++---- materialsystem/vulkan/commands/transfer.cpp | 10 +- materialsystem/vulkan/device_extensions.h | 10 + materialsystem/vulkan/rendercommandlist.cpp | 2 +- materialsystem/vulkan/rendercontext.cpp | 24 +- materialsystem/vulkan/shader.cpp | 73 +++++- materialsystem/vulkan/vulkan_state.h | 10 +- public/materialsystem/imaterialsystem.h | 10 +- shadercompiler/CallableMain | Bin 720 -> 716 bytes shadercompiler/csMain | Bin 1596 -> 856 bytes shadercompiler/psMain | Bin 2688 -> 2564 bytes shadercompiler/rayMain | Bin 1140 -> 1140 bytes shadercompiler/slang/vulkan_spirv.cpp | 2 +- shadercompiler/vsMain | Bin 2772 -> 2672 bytes 18 files changed, 342 insertions(+), 84 deletions(-) diff --git a/funnyassets/shaders/agx.shader b/funnyassets/shaders/agx.shader index 20f2ae4..5cd47b5 100644 --- a/funnyassets/shaders/agx.shader +++ b/funnyassets/shaders/agx.shader @@ -22,11 +22,11 @@ CS Texture2D g_in; RWTexture2D g_out; - [numthreads(1,1,1)] + [numthreads(32,32,1)] void csMain( int3 threadId : SV_DispatchThreadID ) { float4 vColor = g_in.Load(threadId); - vColor.xyz = agxDefaultContrastApprox(vColor.xyz); + //vColor.xyz = agxDefaultContrastApprox(vColor.xyz); g_out.Store(threadId.xy, vColor); } diff --git a/game/client/worldrender.cpp b/game/client/worldrender.cpp index a9b0c89..b892cdb 100644 --- a/game/client/worldrender.cpp +++ b/game/client/worldrender.cpp @@ -48,13 +48,13 @@ void CFunnyViewport::UpdateResolution( uint32_t uX, uint32_t uY ) g_pRenderContext->DestroyImage(m_pColorResolved); g_pRenderContext->DestroyImage(m_pRenderImage); } - m_pColor = g_pRenderContext->CreateRenderTarget(uX, uY, IMAGE_FORMAT_RGBA8_UNORM, MULTISAMPLE_TYPE_4_SAMPLES); + m_pColor = g_pRenderContext->CreateRenderTarget(uX, uY, IMAGE_FORMAT_RGBA8_UNORM, MULTISAMPLE_TYPE_1_SAMPLES); m_pColor->SetDebugName("m_pColor"); - m_pNormal = g_pRenderContext->CreateRenderTarget(uX, uY, IMAGE_FORMAT_RGBA16_SNORM, MULTISAMPLE_TYPE_4_SAMPLES); + m_pNormal = g_pRenderContext->CreateRenderTarget(uX, uY, IMAGE_FORMAT_RGBA16_SNORM, MULTISAMPLE_TYPE_1_SAMPLES); m_pNormal->SetDebugName("m_pNormal"); - m_pWorldCoords = g_pRenderContext->CreateRenderTarget(uX, uY, IMAGE_FORMAT_RGBA32_SFLOAT, MULTISAMPLE_TYPE_4_SAMPLES); + m_pWorldCoords = g_pRenderContext->CreateRenderTarget(uX, uY, IMAGE_FORMAT_RGBA32_SFLOAT, MULTISAMPLE_TYPE_1_SAMPLES); m_pWorldCoords->SetDebugName("m_pWorldCoords"); - m_pDepth = g_pRenderContext->CreateRenderTarget(uX, uY, IMAGE_FORMAT_D32_SFLOAT, MULTISAMPLE_TYPE_4_SAMPLES); + m_pDepth = g_pRenderContext->CreateRenderTarget(uX, uY, IMAGE_FORMAT_D32_SFLOAT, MULTISAMPLE_TYPE_1_SAMPLES); m_pDepth->SetDebugName("m_pDepth"); m_pColorResolved = g_pRenderContext->CreateRenderTarget(uX, uY, IMAGE_FORMAT_RGBA8_UNORM, MULTISAMPLE_TYPE_1_SAMPLES); m_pColorResolved->SetDebugName("m_pColorResolved"); @@ -268,7 +268,7 @@ void CFunnyWorldRenderer::ConfigureShader( IShader *pShader ) pShader->AddOutputImage(1, IMAGE_FORMAT_RGBA16_SNORM); pShader->AddOutputImage(2, IMAGE_FORMAT_RGBA32_SFLOAT); - pShader->SetMultisampling(MULTISAMPLE_TYPE_4_SAMPLES); + pShader->SetMultisampling(MULTISAMPLE_TYPE_1_SAMPLES); pShader->SetDepthImage(IMAGE_FORMAT_D32_SFLOAT); } @@ -402,9 +402,10 @@ void CFunnyWorldRenderer::Frame( float fDelta ) m_pRasterCommandList->Barrier(STAGE_HAZARD_RASTER_COLOR_OUT, STAGE_HAZARD_NONE, 0); m_pRasterCommandList->BarrierTransition( v->m_pColor, - IMAGE_LAYOUT_COLOR, IMAGE_LAYOUT_TRANSFER_SRC, - STAGE_HAZARD_RASTER_COLOR_OUT, STAGE_HAZARD_TRANSFER, + IMAGE_LAYOUT_COLOR, IMAGE_LAYOUT_GENERAL, + STAGE_HAZARD_RASTER_COLOR_OUT, STAGE_HAZARD_COMPUTE, 0); + /* m_pRasterCommandList->BarrierTransition( v->m_pColorResolved, IMAGE_LAYOUT_UNDEFINED, IMAGE_LAYOUT_TRANSFER_DST, @@ -417,16 +418,17 @@ void CFunnyWorldRenderer::Frame( float fDelta ) IMAGE_LAYOUT_TRANSFER_DST, IMAGE_LAYOUT_GENERAL, STAGE_HAZARD_TRANSFER, STAGE_HAZARD_COMPUTE, 0); + */ m_pRasterCommandList->BarrierTransition( v->m_pRenderImage, IMAGE_LAYOUT_UNDEFINED, IMAGE_LAYOUT_GENERAL, STAGE_HAZARD_NONE, STAGE_HAZARD_COMPUTE, 0); - m_pPostProcessingMaterial->CSSetTexture(0, 0, v->m_pColorResolved); + m_pPostProcessingMaterial->CSSetTexture(0, 0, v->m_pColor); m_pPostProcessingMaterial->CSSetTexture(1, 0, v->m_pRenderImage); m_pRasterCommandList->SetMaterial(m_pPostProcessingMaterial); - m_pRasterCommandList->DispatchCompute(uWidth, uHeight, 1); + m_pRasterCommandList->DispatchCompute((uWidth+31)/32, (uHeight+31)/32, 1); m_pRasterCommandList->Barrier(STAGE_HAZARD_COMPUTE, STAGE_HAZARD_NONE, 0); m_pRasterCommandList->EndRecording(); diff --git a/materialsystem/vulkan/commandbuffer.cpp b/materialsystem/vulkan/commandbuffer.cpp index 5e21df9..5b62960 100644 --- a/materialsystem/vulkan/commandbuffer.cpp +++ b/materialsystem/vulkan/commandbuffer.cpp @@ -17,6 +17,7 @@ private: CUtlVector m_commands; VkCommandBuffer m_hBuffer = NULL; + VkDevice m_hDevice; IVkCommandBufferManager *m_pMgr; VkCommandPool m_hPool = NULL; @@ -82,7 +83,7 @@ void CVkCommandBuffer::Render() for (auto &pCommand: m_commands) { - pCommand->Execute(m_hBuffer, 0); + pCommand->Execute(m_hDevice, m_hBuffer, 0); }; vkEndCommandBuffer(m_hBuffer); diff --git a/materialsystem/vulkan/commands.h b/materialsystem/vulkan/commands.h index 2608d0e..f9e5adf 100644 --- a/materialsystem/vulkan/commands.h +++ b/materialsystem/vulkan/commands.h @@ -36,11 +36,12 @@ BEGIN_VULKAN_COMMAND(Begin) bool bDepthEnabled = {}; uint32_t nResolutionX = 0; uint32_t nResolutionY = 0; - - + VkRenderPass hRenderPass; + VkFramebuffer hFrameBuffer; END_VULKAN_COMMAND() BEGIN_VULKAN_COMMAND(End) + CVkBeginCommand *pBeginCommand; END_VULKAN_COMMAND() BEGIN_VULKAN_COMMAND(SetVertexBuffer) diff --git a/materialsystem/vulkan/commands/draw.cpp b/materialsystem/vulkan/commands/draw.cpp index ec3c077..60f2fa0 100644 --- a/materialsystem/vulkan/commands/draw.cpp +++ b/materialsystem/vulkan/commands/draw.cpp @@ -21,57 +21,170 @@ DECLARE_VULKAN_COMMAND(ClearColor) DECLARE_VULKAN_COMMAND(Begin) { - VkRenderingInfo stRenderingInfo = {}; - CUtlVector attachments = {}; - VkRenderingAttachmentInfo depthAttachment = {}; - for (auto i: images) + if (vkCmdBeginRendering || vkCmdBeginRenderingKHR) { - VkRenderingAttachmentInfo a = {}; - a.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; - a.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - switch( i.m_eLoadMode ) + VkRenderingInfo stRenderingInfo = {}; + CUtlVector attachments = {}; + VkRenderingAttachmentInfo depthAttachment = {}; + for (auto i: images) { - case LOAD_MODE_DONT_CARE: - a.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - break; - case LOAD_MODE_LOAD: - a.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - break; - case LOAD_MODE_CLEAR: - a.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - a.clearValue.color.float32[0] = i.m_fClearColor[0]; - a.clearValue.color.float32[1] = i.m_fClearColor[1]; - a.clearValue.color.float32[2] = i.m_fClearColor[2]; - a.clearValue.color.float32[3] = i.m_fClearColor[3]; - break; + VkRenderingAttachmentInfo a = {}; + a.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + a.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + switch( i.m_eLoadMode ) + { + case LOAD_MODE_DONT_CARE: + a.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + break; + case LOAD_MODE_LOAD: + a.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + break; + case LOAD_MODE_CLEAR: + a.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + a.clearValue.color.float32[0] = i.m_fClearColor[0]; + a.clearValue.color.float32[1] = i.m_fClearColor[1]; + a.clearValue.color.float32[2] = i.m_fClearColor[2]; + a.clearValue.color.float32[3] = i.m_fClearColor[3]; + break; + } + a.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + a.imageView = ((CVkImage*)VulkanGetObject(i.m_stImage, iCurrentFrame))->m_imageView; + attachments.AppendTail(a); + } + if (bDepthEnabled) + { + depthAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + depthAttachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; + depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depthAttachment.clearValue.depthStencil.depth = stDepthImage.m_fClearDepth; + depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + depthAttachment.imageView = ((CVkImage*)VulkanGetObject(stDepthImage.m_stImage, iCurrentFrame))->m_imageView; + stRenderingInfo.pDepthAttachment = &depthAttachment; } - a.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - a.imageView = ((CVkImage*)VulkanGetObject(i.m_stImage, iCurrentFrame))->m_imageView; - attachments.AppendTail(a); - } - if (bDepthEnabled) - { - depthAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; - depthAttachment.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; - depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - depthAttachment.clearValue.depthStencil.depth = stDepthImage.m_fClearDepth; - depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - depthAttachment.imageView = ((CVkImage*)VulkanGetObject(stDepthImage.m_stImage, iCurrentFrame))->m_imageView; - stRenderingInfo.pDepthAttachment = &depthAttachment; - } - stRenderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; - stRenderingInfo.layerCount = 1; - stRenderingInfo.colorAttachmentCount = attachments.GetSize(); - stRenderingInfo.pColorAttachments = attachments.GetData(); - stRenderingInfo.renderArea = (VkRect2D){0,0,nResolutionX,nResolutionY}; - - vkCmdBeginRendering(hCommandBuffer, &stRenderingInfo); + stRenderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; + stRenderingInfo.layerCount = 1; + stRenderingInfo.colorAttachmentCount = attachments.GetSize(); + stRenderingInfo.pColorAttachments = attachments.GetData(); + stRenderingInfo.renderArea = (VkRect2D){0,0,nResolutionX,nResolutionY}; + + if (vkCmdBeginRendering) + vkCmdBeginRendering(hCommandBuffer, &stRenderingInfo); + else + vkCmdBeginRenderingKHR(hCommandBuffer, &stRenderingInfo); + } + else + { + CUtlVector attachments = {}; + CUtlVector input_refs = {}; + CUtlVector image_views = {}; + CUtlVector colors = {}; + attachments.Resize(images.GetSize()); + input_refs.Resize(images.GetSize()); + image_views.Resize(images.GetSize()); + colors.Resize(images.GetSize()); + uint32_t i = 0; + + for (auto f: images) + { + VkAttachmentDescription a = {}; + a.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + a.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + a.format = CVkImage::GetImageFormat( + ((CVkImage*)VulkanGetObject(f.m_stImage, iCurrentFrame))->m_eFormat); + a.samples = CVkImage::GetMultisampling( + ((CVkImage*)VulkanGetObject(f.m_stImage, iCurrentFrame))->m_eMultisampleType); + switch( f.m_eLoadMode ) + { + case LOAD_MODE_DONT_CARE: + a.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + break; + case LOAD_MODE_LOAD: + a.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + break; + case LOAD_MODE_CLEAR: + a.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colors[i].color.float32[0] = f.m_fClearColor[0]; + colors[i].color.float32[1] = f.m_fClearColor[1]; + colors[i].color.float32[2] = f.m_fClearColor[2]; + colors[i].color.float32[3] = f.m_fClearColor[3]; + break; + } + a.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[i] = a; + + VkAttachmentReference ref = {}; + ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + ref.attachment = i; + input_refs[i] = ref; + + image_views[i] = ((CVkImage*)VulkanGetObject(f.m_stImage, iCurrentFrame))->m_imageView; + i++; + + } + if (bDepthEnabled) + { + CVkImage *depth = (CVkImage*)VulkanGetObject(stDepthImage.m_stImage, iCurrentFrame); + VkAttachmentDescription desc = {}; + desc.format = CVkImage::GetImageFormat(depth->m_eFormat); + desc.samples = CVkImage::GetMultisampling(depth->m_eMultisampleType); + desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; + desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; + desc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments.AppendTail(desc); + VkClearValue val = {}; + val.depthStencil.depth = stDepthImage.m_fClearDepth; + colors.AppendTail(val); + image_views.AppendTail(depth->m_imageView); + } + VkAttachmentReference depthRef; + depthRef.attachment = attachments.GetSize() - 1; + depthRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = input_refs.GetSize(); + subpass.pColorAttachments = input_refs.GetData(); + if (bDepthEnabled) + { + subpass.pDepthStencilAttachment = &depthRef; + } + VkRenderPassCreateInfo rpci = {}; + rpci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rpci.attachmentCount = attachments.GetSize(); + rpci.pAttachments = attachments.GetData(); + rpci.subpassCount = 1; + rpci.pSubpasses = &subpass; + vkCreateRenderPass(hDevice, &rpci, NULL, &hRenderPass); + + VkFramebufferCreateInfo fbci = {}; + fbci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbci.renderPass = hRenderPass; + fbci.attachmentCount = image_views.GetSize(); + fbci.pAttachments = image_views.GetData(); + fbci.width = nResolutionX; + fbci.height = nResolutionY; + fbci.layers = 1; + vkCreateFramebuffer(hDevice, &fbci, NULL, &hFrameBuffer); + VkRenderPassBeginInfo rpbi = {}; + rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + rpbi.renderPass = hRenderPass; + rpbi.framebuffer = hFrameBuffer; + rpbi.renderArea = (VkRect2D){0,0,nResolutionX,nResolutionY}; + rpbi.clearValueCount = colors.GetSize(); + rpbi.pClearValues = colors.GetData(); + vkCmdBeginRenderPass(hCommandBuffer, &rpbi, VK_SUBPASS_CONTENTS_INLINE); + } } DECLARE_VULKAN_COMMAND(End) { - vkCmdEndRendering(hCommandBuffer); + if (vkCmdEndRendering) + vkCmdEndRendering(hCommandBuffer); + else if (vkCmdEndRenderingKHR) + vkCmdEndRenderingKHR(hCommandBuffer); + else + vkCmdEndRenderPass(hCommandBuffer); } DECLARE_VULKAN_COMMAND(SetShader) @@ -118,7 +231,10 @@ DECLARE_VULKAN_COMMAND(SetScissors) uHeight, }, }; - vkCmdSetScissorWithCount(hCommandBuffer, 1, &r); + if (vkCmdSetScissorWithCount) + vkCmdSetScissorWithCount(hCommandBuffer, 1, &r); + else + vkCmdSetScissor(hCommandBuffer, 0, 1, &r); } DECLARE_VULKAN_COMMAND(SetViewport) @@ -131,7 +247,10 @@ DECLARE_VULKAN_COMMAND(SetViewport) fDepthMin, fDepthMax }; - vkCmdSetViewportWithCount(hCommandBuffer, 1, &v); + if (vkCmdSetScissorWithCount) + vkCmdSetViewportWithCount(hCommandBuffer, 1, &v); + else + vkCmdSetViewport(hCommandBuffer, 0, 1, &v); } DECLARE_VULKAN_COMMAND(ResolveImage) { @@ -176,7 +295,7 @@ static VkPipelineStageFlags2 GetStage( EStageHazard stage ) case STAGE_HAZARD_NONE: return VK_PIPELINE_STAGE_2_NONE; case STAGE_HAZARD_RASTER_EARLY_DEPTH_OUT: - return VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT; + return VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT; case STAGE_HAZARD_RASTER_LATE_DEPTH_OUT: return VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT; } @@ -197,7 +316,7 @@ static VkAccessFlags2 GetAccessSrc( uint32_t hazard, EStageHazard stage ) if (stage == STAGE_HAZARD_RASTER_COLOR_OUT) { if (hazard & BARRIER_HAZARD_RASTER_COLOR) - f |= VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + f |= VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT; } if (hazard == 0) @@ -244,7 +363,6 @@ static VkImageLayout GetLayout( EImageLayoutHazard hazard ) DECLARE_VULKAN_COMMAND(Barrier) { - VkMemoryBarrier2 barrier = {}; barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2; barrier.srcStageMask = GetStage(eStageIn); @@ -255,7 +373,23 @@ DECLARE_VULKAN_COMMAND(Barrier) dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; dependency.memoryBarrierCount = 1; dependency.pMemoryBarriers = &barrier; - vkCmdPipelineBarrier2(hCommandBuffer, &dependency); + if (vkCmdPipelineBarrier2) + vkCmdPipelineBarrier2(hCommandBuffer, &dependency); + else if (vkCmdPipelineBarrier2KHR) + vkCmdPipelineBarrier2KHR(hCommandBuffer, &dependency); + else + { + VkMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + barrier.srcAccessMask = GetAccessSrc(eHazard, eStageIn); + barrier.dstAccessMask = GetAccessDst(eHazard, eStageOut); + vkCmdPipelineBarrier(hCommandBuffer, + GetStage(eStageIn), + GetStage(eStageOut), + 0, 1,&barrier, 0, NULL, 0, NULL); + + } + } DECLARE_VULKAN_COMMAND(LayoutBarrier) @@ -274,5 +408,24 @@ DECLARE_VULKAN_COMMAND(LayoutBarrier) dependency.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; dependency.imageMemoryBarrierCount = 1; dependency.pImageMemoryBarriers = &barrier; - vkCmdPipelineBarrier2(hCommandBuffer, &dependency); + if (vkCmdPipelineBarrier2) + vkCmdPipelineBarrier2(hCommandBuffer, &dependency); + else if (vkCmdPipelineBarrier2KHR) + vkCmdPipelineBarrier2KHR(hCommandBuffer, &dependency); + else + { + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.srcAccessMask = GetAccessSrc(eHazard, eStageIn); + barrier.dstAccessMask = GetAccessDst(eHazard, eStageOut); + barrier.image = ((CVkImage*)pImage)->m_image; + barrier.subresourceRange = ((CVkImage*)pImage)->m_range; + barrier.oldLayout = GetLayout(eLayoutIn); + barrier.newLayout = GetLayout(eLayoutOut); + vkCmdPipelineBarrier(hCommandBuffer, + GetStage(eStageIn), + GetStage(eStageOut), + 0, 0, NULL, 0, NULL, 1, &barrier); + + } } diff --git a/materialsystem/vulkan/commands/transfer.cpp b/materialsystem/vulkan/commands/transfer.cpp index 6b27416..25891f1 100644 --- a/materialsystem/vulkan/commands/transfer.cpp +++ b/materialsystem/vulkan/commands/transfer.cpp @@ -45,6 +45,11 @@ DECLARE_VULKAN_COMMAND(CopyBufferToImage) .imageOffset = {iOffsetX, iOffsetY, iOffsetZ}, .imageExtent = {iImageX, iImageY, iImageZ}, }; + VkBufferImageCopy r2 = { + .imageSubresource = r.imageSubresource, + .imageOffset = r.imageOffset, + .imageExtent = r.imageExtent + }; VkCopyBufferToImageInfo2 c = { .sType = VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2, .srcBuffer = ((CVkBuffer*)pBuffer)->m_buffer, @@ -53,5 +58,8 @@ DECLARE_VULKAN_COMMAND(CopyBufferToImage) .regionCount = 1, .pRegions = &r, }; - vkCmdCopyBufferToImage2(hCommandBuffer, &c); + if (vkCmdCopyBufferToImage2) + vkCmdCopyBufferToImage2(hCommandBuffer, &c); + else + vkCmdCopyBufferToImage(hCommandBuffer, c.srcBuffer, c.dstImage, c.dstImageLayout, 1, &r2); } diff --git a/materialsystem/vulkan/device_extensions.h b/materialsystem/vulkan/device_extensions.h index 71679cc..d3320f2 100644 --- a/materialsystem/vulkan/device_extensions.h +++ b/materialsystem/vulkan/device_extensions.h @@ -2,6 +2,16 @@ REQUIRED_EXTENSION(VK_KHR_SWAPCHAIN) REQUIRED_EXTENSION(VK_KHR_PIPELINE_LIBRARY) REQUIRED_EXTENSION(VK_EXT_GRAPHICS_PIPELINE_LIBRARY) REQUIRED_EXTENSION(VK_KHR_EXTERNAL_FENCE_CAPABILITIES) +REQUIRED_EXTENSION(VK_EXT_DESCRIPTOR_INDEXING) +REQUIRED_EXTENSION(VK_KHR_TIMELINE_SEMAPHORE ) +REQUIRED_EXTENSION(VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS) OPTIONAL_EXTENSION(VK_KHR_RAY_TRACING_PIPELINE) OPTIONAL_EXTENSION(VK_KHR_ACCELERATION_STRUCTURE) OPTIONAL_EXTENSION(VK_KHR_DEFERRED_HOST_OPERATIONS) +OPTIONAL_EXTENSION(VK_KHR_BUFFER_DEVICE_ADDRESS) +OPTIONAL_EXTENSION(VK_EXT_BUFFER_DEVICE_ADDRESS) +OPTIONAL_EXTENSION(VK_KHR_MAINTENANCE_5) +//OPTIONAL_EXTENSION(VK_KHR_DYNAMIC_RENDERING) +//OPTIONAL_EXTENSION(VK_KHR_SYNCHRONIZATION2) +//OPTIONAL_EXTENSION(VK_KHR_COPY_COMMANDS2) +//OPTIONAL_EXTENSION(VK_KHR_DYNAMIC_RENDERING) diff --git a/materialsystem/vulkan/rendercommandlist.cpp b/materialsystem/vulkan/rendercommandlist.cpp index d2d9282..e2a1f6f 100644 --- a/materialsystem/vulkan/rendercommandlist.cpp +++ b/materialsystem/vulkan/rendercommandlist.cpp @@ -120,8 +120,8 @@ void CVkRenderCommandList::SetMaterial( IMaterial *pMaterial ) { pBeginCommand->bDepthEnabled = m_bDepthEnabled; pBeginCommand->stDepthImage = m_depth; - } + m_materials[m_materials.GetSize()-1].m_pBeginCommand = pBeginCommand; m_pCurrentMaterialBuffer->AddCommand(pBeginCommand); CVkSetShaderCommand *pSetShader = CREATE_COMMAND(m_pCommandBufferManager, SetShader); diff --git a/materialsystem/vulkan/rendercontext.cpp b/materialsystem/vulkan/rendercontext.cpp index 6d90b3d..7c24d85 100644 --- a/materialsystem/vulkan/rendercontext.cpp +++ b/materialsystem/vulkan/rendercontext.cpp @@ -276,7 +276,9 @@ CVkBuffer::CVkBuffer( uint32_t nSize, VkBufferUsageFlags2 eUsage, uint32_t nAlig stBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; stBufferInfo.size = nSize; - stBufferInfo.pNext = &stUsage; + stBufferInfo.usage = stUsage.usage; + if (g_vkAvailableExtensions.bIsSupported_VK_KHR_MAINTENANCE_5) + stBufferInfo.pNext = &stUsage; stAllocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; stAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; @@ -286,7 +288,12 @@ CVkBuffer::CVkBuffer( uint32_t nSize, VkBufferUsageFlags2 eUsage, uint32_t nAlig stAddress.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; stAddress.buffer = m_buffer; - m_address = vkGetBufferDeviceAddress(s_vkDevice, &stAddress); + if (vkGetBufferDeviceAddress) + m_address = vkGetBufferDeviceAddress(s_vkDevice, &stAddress); + else if (vkGetBufferDeviceAddressEXT) + m_address = vkGetBufferDeviceAddressEXT(s_vkDevice, &stAddress); + else if (vkGetBufferDeviceAddressKHR) + m_address = vkGetBufferDeviceAddressKHR(s_vkDevice, &stAddress); m_nSize = nSize; } @@ -411,7 +418,7 @@ uint32_t CVkTextureArray::CreateTexture( uint32_t i, const char *szPath ) pBuffer->Unmap(); pBuffer->Unlock(); vkDeviceWaitIdle(s_vkDevice); - IImage *pImage = m_pRenderContext->CreateTexture(uWidth, uHeight, IMAGE_FORMAT_RGBA8_UNORM, MULTISAMPLE_TYPE_NONE); + IImage *pImage = m_pRenderContext->CreateTexture(uWidth, uHeight, IMAGE_FORMAT_RGBA8_UNORM, MULTISAMPLE_TYPE_1_SAMPLES); m_pImages[i] = (CVkImage*)pImage; stbi_image_free(pImg); @@ -872,6 +879,10 @@ void CVkRenderContext::Init() vk11Features.shaderDrawParameters = VK_TRUE; vk11Features.multiview = VK_TRUE; + VkPhysicalDeviceFeatures features = {}; + features.shaderStorageImageReadWithoutFormat = VK_TRUE; + features.shaderStorageImageWriteWithoutFormat = VK_TRUE; + stDeviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; stDeviceCreateInfo.queueCreateInfoCount = 1; @@ -879,6 +890,7 @@ void CVkRenderContext::Init() stDeviceCreateInfo.enabledExtensionCount = enabledDeviceExtensions.GetSize(); stDeviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.GetData(); stDeviceCreateInfo.pNext = &vk11Features; + stDeviceCreateInfo.pEnabledFeatures = &features; r = vkCreateDevice(s_vkPhysicalDevice, &stDeviceCreateInfo, NULL, &s_vkDevice); VULKAN_RESULT_PRINT(r, vkEnumeratePhysicalDevices); for (auto &extension: enabledDeviceExtensions) @@ -994,6 +1006,7 @@ void CVkRenderContext::Frame( float fDeltaTime ) { + CVkBlitCommand *pBlitCommand = NULL; if (s.m_pWindow->GetOutputImage()) { @@ -1070,7 +1083,10 @@ void CVkRenderContext::Frame( float fDeltaTime ) wi.semaphoreCount = 1; wi.pSemaphores = &semaphore; wi.pValues = &v; - vkWaitSemaphores(s_vkDevice, &wi, UINT64_MAX); + if (vkWaitSemaphores) + vkWaitSemaphores(s_vkDevice, &wi, UINT64_MAX); + else + vkWaitSemaphoresKHR(s_vkDevice, &wi, UINT64_MAX); VkPresentInfoKHR stPresentInfo = {}; stPresentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; diff --git a/materialsystem/vulkan/shader.cpp b/materialsystem/vulkan/shader.cpp index d83726f..86c80c3 100644 --- a/materialsystem/vulkan/shader.cpp +++ b/materialsystem/vulkan/shader.cpp @@ -81,6 +81,8 @@ void CVkShader::Build() VkPipelineRenderingCreateInfo render = {}; CUtlVector attachments = {}; VkDynamicState dynamicStates[] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT, VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT, }; @@ -178,6 +180,8 @@ void CVkShader::Build() rasterState.frontFace = VK_FRONT_FACE_CLOCKWISE; viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.scissorCount = 1; msaa.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; msaa.rasterizationSamples = CVkImage::GetMultisampling(m_eMultiSampling); @@ -186,10 +190,70 @@ void CVkShader::Build() for ( uint32_t i = 0; i < m_formats.GetSize(); i++ ) formats[i] = m_formats[i].m_eFormat; - render.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; - render.colorAttachmentCount = formats.GetSize(); - render.pColorAttachmentFormats = formats.GetData(); + if (! (vkCmdBeginRendering || vkCmdBeginRenderingKHR) ) + { + CUtlVector attachments = {}; + CUtlVector input_refs = {}; + attachments.Resize(formats.GetSize()); + input_refs.Resize(formats.GetSize()); + uint32_t i = 0; + for (auto f: formats) + { + /* fuck vulkan 1.0*/ + /* anyway we create compatible render passes somewhere */ + VkAttachmentDescription desc = {}; + desc.format = f; + desc.samples = CVkImage::GetMultisampling(m_eMultiSampling); + desc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + desc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[i] = desc; + VkAttachmentReference ref = {}; + ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + ref.attachment = i; + input_refs[i] = ref; + i++; + } + if (m_eDepthFormat == VK_FORMAT_D32_SFLOAT) + { + VkAttachmentDescription desc = {}; + desc.format = m_eDepthFormat; + desc.samples = CVkImage::GetMultisampling(m_eMultiSampling); + desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments.AppendTail(desc); + } + VkAttachmentReference depthRef; + depthRef.attachment = attachments.GetSize() - 1; + depthRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = input_refs.GetSize(); + subpass.pColorAttachments = input_refs.GetData(); + if (m_eDepthFormat == VK_FORMAT_D32_SFLOAT) + { + subpass.pDepthStencilAttachment = &depthRef; + } + VkRenderPassCreateInfo rpci = {}; + rpci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rpci.attachmentCount = attachments.GetSize(); + rpci.pAttachments = attachments.GetData(); + rpci.subpassCount = 1; + rpci.pSubpasses = &subpass; + vkCreateRenderPass(m_hDevice, &rpci, NULL, &m_hRenderPass); + createInfo.renderPass = m_hRenderPass; + + } + else + { + render.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; + render.colorAttachmentCount = formats.GetSize(); + render.pColorAttachmentFormats = formats.GetData(); + if (m_eDepthFormat == VK_FORMAT_D32_SFLOAT) + render.depthAttachmentFormat = m_eDepthFormat; + + + } depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; if (m_eDepthFormat == VK_FORMAT_D32_SFLOAT) { @@ -235,7 +299,8 @@ void CVkShader::Build() createInfo.pDepthStencilState = &depthStencil; createInfo.pColorBlendState = &blend; createInfo.pMultisampleState = &msaa; - createInfo.pNext = &render; + if (vkCmdBeginRendering || vkCmdBeginRenderingKHR) + createInfo.pNext = &render; vkCreateGraphicsPipelines(m_hDevice, NULL, 1, &createInfo, NULL, &m_hPipeline); } diff --git a/materialsystem/vulkan/vulkan_state.h b/materialsystem/vulkan/vulkan_state.h index 2a0d10e..9a9ed0b 100644 --- a/materialsystem/vulkan/vulkan_state.h +++ b/materialsystem/vulkan/vulkan_state.h @@ -54,7 +54,7 @@ struct VulkanCommandParameter_t class CVkCommand { public: - virtual void Execute( VkCommandBuffer hCommandBuffer, int iCurrentFrame ) = 0; + virtual void Execute( VkDevice hDevice, VkCommandBuffer hCommandBuffer, int iCurrentFrame ) = 0; virtual ~CVkCommand() = default; //virtual const char *GetName(); @@ -99,7 +99,7 @@ public: class CVk##name##Command : public CVkCommand \ { \ public: \ - virtual void Execute( VkCommandBuffer hCommandBuffer, int iCurrentFrame ) override; \ + virtual void Execute( VkDevice hDevice, VkCommandBuffer hCommandBuffer, int iCurrentFrame ) override; \ #define END_VULKAN_COMMAND() \ }; \ @@ -107,7 +107,7 @@ public: \ #define DECLARE_VULKAN_COMMAND(name) \ CVkCommand *_VulkanCommandCreate_##name() { return new CVk##name##Command; } \ CVkCommandRegistry _VulkanCommandRegistry_##name( #name, _VulkanCommandCreate_##name ); \ -void CVk##name##Command::Execute( VkCommandBuffer hCommandBuffer, int iCurrentFrame ) \ +void CVk##name##Command::Execute( VkDevice hDevice, VkCommandBuffer hCommandBuffer, int iCurrentFrame ) \ #undef __cplusplus #include "vulkan/vk_enum_string_helper.h" @@ -253,11 +253,12 @@ public: CCompiledShader m_shader; CUtlVector m_setLayouts; CUtlVector m_bindings; + VkRenderPass m_hRenderPass; private: CUtlVector m_layouts; CUtlVector m_attributes; CUtlVector m_formats; - EMultisampleType m_eMultiSampling; + EMultisampleType m_eMultiSampling = MULTISAMPLE_TYPE_1_SAMPLES; VkFormat m_eDepthFormat; bool m_bIsFragmentEnabled; }; @@ -380,6 +381,7 @@ struct VulkanMaterialCommandBuffer_t { IMaterial *m_pMaterial; IVkCommandBuffer *m_pCommandBuffer; + void *m_pBeginCommand; }; diff --git a/public/materialsystem/imaterialsystem.h b/public/materialsystem/imaterialsystem.h index a636b7b..43c1725 100644 --- a/public/materialsystem/imaterialsystem.h +++ b/public/materialsystem/imaterialsystem.h @@ -81,11 +81,11 @@ enum EImageType enum EMultisampleType { - MULTISAMPLE_TYPE_NONE, - MULTISAMPLE_TYPE_1_SAMPLES = MULTISAMPLE_TYPE_NONE, - MULTISAMPLE_TYPE_2_SAMPLES, - MULTISAMPLE_TYPE_4_SAMPLES, - MULTISAMPLE_TYPE_8_SAMPLES, + MULTISAMPLE_TYPE_NONE = 0, + MULTISAMPLE_TYPE_1_SAMPLES = 1, + MULTISAMPLE_TYPE_2_SAMPLES = 2, + MULTISAMPLE_TYPE_4_SAMPLES = 4, + MULTISAMPLE_TYPE_8_SAMPLES = 8, }; enum EResolveMode diff --git a/shadercompiler/CallableMain b/shadercompiler/CallableMain index 5bbecc0cc05b5967d210b88aee9ea16080a7b8a9..035aecba1ae1f726bbccd4bbc848da1acba6200d 100644 GIT binary patch delta 34 ocmcb>dWMyanMs+Qfq5dE3O_3YD+2=q6AoR{`zploM$1wLn z{me%HL=HxOAjcv;FQCUxXY$;KJUHgtng&1cw=QZrs^PuVVF$t$ML4$NglmfQ!BfBG z`GTXK`@vBU-VK<2b;ULnoxG>lus<+*)O2YN4MubclxrIsvNya?tNA{V?KdZuDh#aS`j}cOQEX<}$vYUC#U-@IiL#$g6M3_y(uG z6)dM-?DrFU?_*Rt!fron$i))7?9tV5Gp#j_gUzw#1+e~E?C!74AA!$t zo7>*Om%&>nw{~zp_}kClH$(5dJpB6QM&Lp4<3E?K2Ob80yf!rCdB*VlCUK_;{Quo4 z?&qDR(74nNf&GrYwf7c#XW!Z-R_|N7`gkS3!3yR%!miMD_jmOB{N+@>@sS=bqOYHmfG{0XK;3+`#o8w>6ke(;-l?sGVK=OA=1@a3%+x(dEJ=i@`3JB_c_Sm>(w_Hb^Z z-b;LW>wU^|XYi*nW05}>IJ!<%BjfHLw-+T3A(PJK8es_-ru=&O!cM;!Q=QMh} z!dGi7bg%KPHHgKTm+;jZi`->=d1uP^c78`NdDkSb>-)aJ;-^;vK7MwkEQf||&OyXFv_iDQFGz^kz z(nwN#-PT9F*lXuL?WuiSx~N?X)A=L_|5dx!#wk?!v7arZezVo^vna9WqBnK&`tsjP zGUuc>K6?-3+VSb#V@}In&(ij@teu9z)%Nq}p{C8}#mDlzIEXLbtIHu2z#I4Q!1b^Pn7G_v4jD-nUD$K9a9xO~G93FY`?5 z(PcJC_nih_mSe%hx?DKQ9XbpziDyw8 zT#ko~#Ix|jF)(KW^JOgi;Y_(czdJ5|yRzszrkOb}`(4^`7o(Ehs>j`d`5oJ-&pzo< z2@5c-Iem9T9WAZUSm|lk@tYgX|@1O)*S*U$fHu^Hd9^naT z;+b_spKuV&%p(Plh(YjCgWnW-!?ArjJ6vEtqCIa9Chn{>aoE7;3+(Dl7MOncrwUB( z9m=dqb3X8O$xp2zv)RmP&zUz#sP7h-#Z0r(#8LMKMlEdMTNZ8jYr^C*2k&=Jn)=ip z*5`d`@|L7o;JxbcP}=o~wTBPXxTIL_d0awWFn0mRsR?GLn-Uh78Q`^xrx!eCIX^sh ze&XS=!}N#8%s@OC9+xpZ2A|ivlIQLY+gBktKrZ}yFL7r9uNC;RDzJxNQ;LO0Mn#E?kh{8!K$^;i3dPvDD9J$nErIu1gYXz?aJs;`x3M zPycBN_D_nbN?$8%aOjI_3`(=UD;{6JWI)0lu%|X0nV0ZJ)N`}k(Vlm@qsib&-R{Bp z;$De`7Yh<lJ1JLK9*poF3a6F_HUYx*_o4h-Aqrk=U!Ya pxExE4iw(4gM-IQx9v(UTPWv?pwcrz&JzOG|Jv^e$53y)c@(Z>M7Ek~H literal 2688 zcma)--ES0C7{(9X?iMM58j;V~w37H$EXbD-BeoW46$(qYlpB-bv@?_;b9Oefv!*0m zaPN&?81>G3@BP>ONQ^$evvao7N(^uF?0eqN^PY2_-8RO~PMFpKW6XQzXMG~Cn=$j- zv7sh%$V{3}dn3BNvKIBrys+Czv^jYEI4MTvR%ZKsYLA!(%20Pyf8)|)g0pE?%3Kj{ z2_507;DnxFg*!qb%n4KKL*0bX(BGsqJ)7pR<}qd|cP{VFui7#xQk&hhrHx9B6JNe` zAA92w)i`ixp=Sz8(7sOgVMqd{VhB2+1@}8IezdaQ8y~#sf)|=SN zdVeT?)w`V(t9hL4ou7U!bI_27xX9UZy|CSWX3I49_sF@35Bn>=mX{&Oxrq6Dr`BH) z=bA7VW??uj$D}2`*_3cs7!X~qnD>Q z@0WIagK}QCv7YQK<#`eN9uxk(NBJG=dRd|upsr;eNcX)f{?dtDL;twh*!3&#py$Y_ z|HHh<;&$Fohc`kW3P-RsGaU4S?7t3lVZNtUlltU zdPD(V=U%z4+D!RPaUMRu+J1Wv!5#8u|DDa4Wqp(QUQ7#o!-knT z`3P}1h|T=iaIYSRQ}zCECFH-MD)yb#85+{8k82g?KcRlyEjGWaZ|alZ#Tfyamd$S+ zIrxd6lAaOhHCNfEwMJ%SpOGdXg!ru1%#Y1p?+VB{Ws&!efUgdEeIOtEu;)qH^U~xq zE3?5-Y-XF*+rmY105N*Njl%-5w{&*6!FpP2xPVRGWoh#8Ve`(|^f{*KZ%8vgdo2k6 zNKTp8%uTI16LWILAlL|dE=iL|-!E`V@L?}|Yd3ydHnp6K_gR&uKfO=uvn@^Cnlyra zMtiJFhdolQd2f1rqFnBAPM|L~XNA+851W~82?#bb+z~=Pd%;sA#I4pLPCh&hHv7X< zW*{Hi?X%%2@vC|_>cZXO`$HCQOf7u+K?rBT{sL*#9emce*DFp4_c`_!0Ic z7hc>G=z$Gq?hCx>hWf*u2Ld_xAL_2?&;LvO^hLsbfindProfile("spirv_1_6"); + stTargetDesc.profile = s_pGlobalSession->findProfile("spirv_1_3"); for ( i = 0; i@BZXw^dKoN)%`4u|iY@80{p=6;61LjheIHH0pO5aZ&Z9f+g`gbpDDU4!_z zG@w!{SIMt{I?w=`fC(glGLV0L`jx|Zs%q_~K3yr?Zd_hhN*BT_3{h6kT2$86yS5-3 zs;kY(CirBhpnIjNdqPrIkDe9vP2wEyaXyabJ+{ER+^1#>Zp{|lnk_iG1#WgG{6Xnu!C95=3$AUF0c~s8LTrK4 zw+^I`Bmuh(@MmT1Gds2~1)D~zOR&4@#~;b#9kNb=|r#C7`(R z!pmOBc>!;HfRbnM4f+bb?+XdW$W|(+z0A~j4vVir83$QLPTypvs#J9-mQVpj=oTzX zXN>D@SeC>%^)W1;-Hvsrm0d%YH+Cs%iF+5~b9~@L^Bh~^S*}yFrL<;CY0Z`r9m4Mq zZtX|PWy$XWXIln~xUK@b1{lAJrX{i-$5vsvXC8I~aQ5|h&t@E3gk3`8%dj8y&j-aX zpXh-yNHTne&v=qy3qE7KCCTs=K4+U`*oMzoI)lp$;SQoMcp9zV^{@+{@g&0@e8#o` zGjI(1fKOXTy&S{0I5r1MUgFXC)a;)=pP7}?=M!^3{1?%mfeFrVF@I|3pm4V+{aF9Q H1n0qj5XNe8