// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #include #include #include namespace anki { DepthDownscale::~DepthDownscale() { if(m_copyToBuff.m_buffAddr) { m_copyToBuff.m_buff->unmap(); } } Error DepthDownscale::initInternal(const ConfigSet&) { const U32 width = m_r->getWidth() >> 1; const U32 height = m_r->getHeight() >> 1; m_mipCount = computeMaxMipmapCount2d(width, height, HIERARCHICAL_Z_MIN_HEIGHT); const U32 lastMipWidth = width >> (m_mipCount - 1); const U32 lastMipHeight = height >> (m_mipCount - 1); ANKI_R_LOGI("Initializing HiZ. Mip count %u, last mip size %ux%u", m_mipCount, lastMipWidth, lastMipHeight); // Create RT descr TextureInitInfo texInit = m_r->create2DRenderTargetInitInfo( width, height, Format::R32_SFLOAT, TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::SAMPLED_COMPUTE | TextureUsageBit::IMAGE_COMPUTE_WRITE, "HiZ"); texInit.m_mipmapCount = U8(m_mipCount); texInit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT; m_hizTex = m_r->createAndClearRenderTarget(texInit); // Progs ANKI_CHECK(getResourceManager().loadResource("shaders/DepthDownscale.ankiprog", m_prog)); ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog); variantInitInfo.addMutation("SAMPLE_RESOLVE_TYPE", 2); const ShaderProgramResourceVariant* variant; m_prog->getOrCreateVariant(variantInitInfo, variant); m_grProg = variant->getProgram(); // Copy to buffer { m_copyToBuff.m_lastMipWidth = lastMipWidth; m_copyToBuff.m_lastMipHeight = lastMipHeight; // Create buffer BufferInitInfo buffInit("HiZ Client"); buffInit.m_mapAccess = BufferMapAccessBit::READ; buffInit.m_size = lastMipHeight * lastMipWidth * sizeof(F32); buffInit.m_usage = BufferUsageBit::STORAGE_COMPUTE_WRITE; m_copyToBuff.m_buff = getGrManager().newBuffer(buffInit); m_copyToBuff.m_buffAddr = m_copyToBuff.m_buff->map(0, buffInit.m_size, BufferMapAccessBit::READ); // Fill the buffer with 1.0f for(U32 i = 0; i < lastMipHeight * lastMipWidth; ++i) { static_cast(m_copyToBuff.m_buffAddr)[i] = 1.0f; } } return Error::NONE; } Error DepthDownscale::init(const ConfigSet& cfg) { ANKI_R_LOGI("Initializing depth downscale passes"); Error err = initInternal(cfg); if(err) { ANKI_R_LOGE("Failed to initialize depth downscale passes"); } return err; } void DepthDownscale::importRenderTargets(RenderingContext& ctx) { RenderGraphDescription& rgraph = ctx.m_renderGraphDescr; // Import RT if(m_hizTexImportedOnce) { m_runCtx.m_hizRt = rgraph.importRenderTarget(m_hizTex); } else { m_runCtx.m_hizRt = rgraph.importRenderTarget(m_hizTex, TextureUsageBit::SAMPLED_FRAGMENT); m_hizTexImportedOnce = true; } } void DepthDownscale::populateRenderGraph(RenderingContext& ctx) { RenderGraphDescription& rgraph = ctx.m_renderGraphDescr; m_runCtx.m_mip = 0; static const Array passNames = {"HiZ #0", "HiZ #1", "HiZ #2", "HiZ #3", "HiZ #4"}; // Every pass can do MIPS_WRITTEN_PER_PASS mips for(U32 i = 0; i < m_mipCount; i += MIPS_WRITTEN_PER_PASS) { const U mipsToFill = (i + 1 < m_mipCount) ? MIPS_WRITTEN_PER_PASS : 1; ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass(passNames[i / MIPS_WRITTEN_PER_PASS]); if(i == 0) { pass.newDependency({m_r->getGBuffer().getDepthRt(), TextureUsageBit::SAMPLED_COMPUTE, TextureSubresourceInfo(DepthStencilAspectBit::DEPTH)}); } else { TextureSubresourceInfo subresource; subresource.m_firstMipmap = i - 1; pass.newDependency({m_runCtx.m_hizRt, TextureUsageBit::SAMPLED_COMPUTE, subresource}); } TextureSubresourceInfo subresource; subresource.m_firstMipmap = i; pass.newDependency({m_runCtx.m_hizRt, TextureUsageBit::IMAGE_COMPUTE_WRITE, subresource}); if(mipsToFill == MIPS_WRITTEN_PER_PASS) { subresource.m_firstMipmap = i + 1; pass.newDependency({m_runCtx.m_hizRt, TextureUsageBit::IMAGE_COMPUTE_WRITE, subresource}); } auto callback = [](RenderPassWorkContext& rgraphCtx) { DepthDownscale* const self = static_cast(rgraphCtx.m_userData); self->run(rgraphCtx); }; pass.setWork(callback, this, 0); } } void DepthDownscale::run(RenderPassWorkContext& rgraphCtx) { CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer; const U32 level = m_runCtx.m_mip; m_runCtx.m_mip += MIPS_WRITTEN_PER_PASS; const U32 mipsToFill = (level + 1 < m_mipCount) ? MIPS_WRITTEN_PER_PASS : 1; const U32 copyToClientLevel = (level + mipsToFill == m_mipCount) ? mipsToFill - 1 : MAX_U32; const U32 level0Width = m_r->getWidth() >> (level + 1); const U32 level0Height = m_r->getHeight() >> (level + 1); const U32 level1Width = level0Width >> 1; const U32 level1Height = level0Height >> 1; cmdb->bindShaderProgram(m_grProg); // Uniforms struct PushConsts { UVec2 m_level0WriteImgSize; UVec2 m_level1WriteImgSize; U32 m_copyToClientLevel; U32 m_writeLevel1; U32 m_padding0; U32 m_padding1; } regs; regs.m_level0WriteImgSize = UVec2(level0Width, level0Height); regs.m_level1WriteImgSize = UVec2(level1Width, level1Height); regs.m_copyToClientLevel = copyToClientLevel; regs.m_writeLevel1 = mipsToFill == MIPS_WRITTEN_PER_PASS; cmdb->setPushConstants(®s, sizeof(regs)); cmdb->bindSampler(0, 0, m_r->getSamplers().m_nearestNearestClamp); // Bind input texure if(level == 0) { rgraphCtx.bindTexture(0, 1, m_r->getGBuffer().getDepthRt(), TextureSubresourceInfo(DepthStencilAspectBit::DEPTH)); } else { TextureSubresourceInfo subresource; subresource.m_firstMipmap = level - 1; rgraphCtx.bindTexture(0, 1, m_runCtx.m_hizRt, subresource); } // 1st level TextureSubresourceInfo subresource; subresource.m_firstMipmap = level; rgraphCtx.bindImage(0, 2, m_runCtx.m_hizRt, subresource); // 2nd level subresource.m_firstMipmap = (mipsToFill == MIPS_WRITTEN_PER_PASS) ? level + 1 : level; // Bind the next or the same rgraphCtx.bindImage(0, 3, m_runCtx.m_hizRt, subresource); // Client buffer cmdb->bindStorageBuffer(0, 4, m_copyToBuff.m_buff, 0, m_copyToBuff.m_buff->getSize()); // Done dispatchPPCompute(cmdb, 8, 8, level0Width, level0Height); } } // end namespace anki