// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #include #include #include #include #include #include #include #include #include #include #include #include #include namespace anki { MainRenderer::MainRenderer() { } MainRenderer::~MainRenderer() { ANKI_R_LOGI("Destroying main renderer"); } Error MainRenderer::init(const MainRendererInitInfo& inf) { ANKI_R_LOGI("Initializing main renderer"); m_alloc = HeapAllocator(inf.m_allocCallback, inf.m_allocCallbackUserData, "MainRenderer"); m_frameAlloc = StackAllocator(inf.m_allocCallback, inf.m_allocCallbackUserData, 10_MB, 1.0f); // Init renderer and manipulate the width/height m_swapchainResolution = inf.m_swapchainSize; m_rDrawToDefaultFb = inf.m_config->getRRenderScaling() == 1.0f; m_r.reset(m_alloc.newInstance()); ANKI_CHECK(m_r->init(inf.m_threadHive, inf.m_resourceManager, inf.m_gr, inf.m_stagingMemory, inf.m_ui, m_alloc, inf.m_config, inf.m_globTimestamp, m_swapchainResolution)); // Init other if(!m_rDrawToDefaultFb) { ANKI_CHECK(inf.m_resourceManager->loadResource("ShaderBinaries/BlitRaster.ankiprogbin", m_blitProg)); const ShaderProgramResourceVariant* variant; m_blitProg->getOrCreateVariant(variant); m_blitGrProg = variant->getProgram(); // The RT desc UVec2 resolution = UVec2(Vec2(m_swapchainResolution) * inf.m_config->getRRenderScaling()); alignRoundDown(2, resolution.x()); alignRoundDown(2, resolution.y()); m_tmpRtDesc = m_r->create2DRenderTargetDescription( resolution.x(), resolution.y(), (m_r->getGrManager().getDeviceCapabilities().m_unalignedBbpTextureFormats) ? Format::R8G8B8_UNORM : Format::R8G8B8A8_UNORM, "Final Composite"); m_tmpRtDesc.bake(); // FB descr m_fbDescr.m_colorAttachmentCount = 1; m_fbDescr.bake(); ANKI_R_LOGI("There will be a blit pass to the swapchain because render scaling is not 1.0"); } m_rgraph = inf.m_gr->newRenderGraph(); ANKI_R_LOGI("Main renderer initialized. Swapchain resolution %ux%u", m_swapchainResolution.x(), m_swapchainResolution.y()); return Error::NONE; } Error MainRenderer::render(RenderQueue& rqueue, TexturePtr presentTex) { ANKI_TRACE_SCOPED_EVENT(RENDER); m_stats.m_renderingCpuTime = (m_statsEnabled) ? HighRezTimer::getCurrentTime() : -1.0; // First thing, reset the temp mem pool m_frameAlloc.getMemoryPool().reset(); // Run renderer RenderingContext ctx(m_frameAlloc); m_runCtx.m_ctx = &ctx; m_runCtx.m_secondaryTaskId.setNonAtomically(0); ctx.m_renderGraphDescr.setStatisticsEnabled(m_statsEnabled); RenderTargetHandle presentRt = ctx.m_renderGraphDescr.importRenderTarget(presentTex, TextureUsageBit::NONE); if(m_rDrawToDefaultFb) { // m_r will draw to a presentable texture ctx.m_outRenderTarget = presentRt; } else { // m_r will draw to a temp tex ctx.m_outRenderTarget = ctx.m_renderGraphDescr.newRenderTarget(m_tmpRtDesc); } ctx.m_renderQueue = &rqueue; ANKI_CHECK(m_r->populateRenderGraph(ctx)); // Blit renderer's result to default FB if needed if(!m_rDrawToDefaultFb) { GraphicsRenderPassDescription& pass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Final Blit"); pass.setFramebufferInfo(m_fbDescr, {presentRt}); pass.setWork([this](RenderPassWorkContext& rgraphCtx) { CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer; cmdb->setViewport(0, 0, m_swapchainResolution.x(), m_swapchainResolution.y()); cmdb->bindShaderProgram(m_blitGrProg); cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearClamp); rgraphCtx.bindColorTexture(0, 1, m_runCtx.m_ctx->m_outRenderTarget); cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3); }); pass.newDependency(RenderPassDependency(presentRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE)); pass.newDependency(RenderPassDependency(ctx.m_outRenderTarget, TextureUsageBit::SAMPLED_FRAGMENT)); } // Create a dummy pass to transition the presentable image to present { ComputeRenderPassDescription& pass = ctx.m_renderGraphDescr.newComputeRenderPass("Present"); pass.setWork([](RenderPassWorkContext& rgraphCtx) { // Do nothing. This pass is dummy }); pass.newDependency({presentRt, TextureUsageBit::PRESENT}); } // Bake the render graph m_rgraph->compileNewGraph(ctx.m_renderGraphDescr, m_frameAlloc); // Populate the 2nd level command buffers Array tasks; for(U i = 0; i < m_r->getThreadHive().getThreadCount(); ++i) { tasks[i].m_argument = this; tasks[i].m_callback = [](void* userData, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* signalSemaphore) { MainRenderer& self = *static_cast(userData); const U32 taskId = self.m_runCtx.m_secondaryTaskId.fetchAdd(1); self.m_rgraph->runSecondLevel(taskId); }; } m_r->getThreadHive().submitTasks(&tasks[0], m_r->getThreadHive().getThreadCount()); m_r->getThreadHive().waitAllTasks(); // Populate 1st level command buffers m_rgraph->run(); // Flush m_rgraph->flush(); // Reset for the next frame m_rgraph->reset(); m_r->finalize(ctx); // Stats if(m_statsEnabled) { m_stats.m_renderingCpuTime = HighRezTimer::getCurrentTime() - m_stats.m_renderingCpuTime; RenderGraphStatistics rgraphStats; m_rgraph->getStatistics(rgraphStats); m_stats.m_renderingGpuTime = rgraphStats.m_gpuTime; m_stats.m_renderingGpuSubmitTimestamp = rgraphStats.m_cpuStartTime; } return Error::NONE; } Dbg& MainRenderer::getDbg() { return m_r->getDbg(); } F32 MainRenderer::getAspectRatio() const { return m_r->getAspectRatio(); } } // end namespace anki