// 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 #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(ThreadHive* hive, ResourceManager* resources, GrManager* gr, StagingGpuMemoryManager* stagingMem, UiManager* ui, AllocAlignedCallback allocCb, void* allocCbUserData, const ConfigSet& config, Timestamp* globTimestamp) { ANKI_R_LOGI("Initializing main renderer"); m_alloc = HeapAllocator(allocCb, allocCbUserData); m_frameAlloc = StackAllocator(allocCb, allocCbUserData, 1024 * 1024 * 10, 1.0f); // Init renderer and manipulate the width/height m_width = config.getNumberU32("width"); m_height = config.getNumberU32("height"); ConfigSet config2 = config; m_renderingQuality = config.getNumberF32("r_renderingQuality"); UVec2 size(U32(m_renderingQuality * F32(m_width)), U32(m_renderingQuality * F32(m_height))); config2.set("width", size.x()); config2.set("height", size.y()); m_rDrawToDefaultFb = m_renderingQuality == 1.0; m_r.reset(m_alloc.newInstance()); ANKI_CHECK(m_r->init(hive, resources, gr, stagingMem, ui, m_alloc, config2, globTimestamp)); // Init other if(!m_rDrawToDefaultFb) { ANKI_CHECK(resources->loadResource("shaders/Blit.ankiprog", m_blitProg)); const ShaderProgramResourceVariant* variant; m_blitProg->getOrCreateVariant(variant); m_blitGrProg = variant->getProgram(); // The RT desc m_tmpRtDesc = m_r->create2DRenderTargetDescription(m_width, m_height, Format::R8G8B8_UNORM, "Final Composite"); m_tmpRtDesc.bake(); ANKI_R_LOGI("The main renderer will have to blit the offscreen renderer's result"); } m_rgraph = gr->newRenderGraph(); ANKI_R_LOGI("Main renderer initialized. Rendering size %ux%u", m_width, m_height); return Error::NONE; } Error MainRenderer::render(RenderQueue& rqueue, TexturePtr presentTex) { ANKI_TRACE_SCOPED_EVENT(RENDER); m_r->setStatsEnabled(m_statsEnabled); 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; ctx.m_outRenderTargetWidth = presentTex->getWidth(); ctx.m_outRenderTargetHeight = presentTex->getHeight(); } else { // m_r will draw to a temp tex ctx.m_outRenderTarget = ctx.m_renderGraphDescr.newRenderTarget(m_tmpRtDesc); ctx.m_outRenderTargetWidth = m_width; ctx.m_outRenderTargetHeight = m_height; } 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"); FramebufferDescription fbDescr; fbDescr.m_colorAttachmentCount = 1; fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::DONT_CARE; fbDescr.bake(); pass.setFramebufferInfo(fbDescr, {{presentRt}}, {}); pass.setWork( [](RenderPassWorkContext& rgraphCtx) { MainRenderer* const self = static_cast(rgraphCtx.m_userData); self->runBlit(rgraphCtx); }, this, 0); pass.newDependency({presentRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE}); pass.newDependency({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 }, nullptr, 0); 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) { static_cast(m_stats) = m_r->getStats(); 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; } void MainRenderer::runBlit(RenderPassWorkContext& rgraphCtx) { CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer; cmdb->setViewport(0, 0, m_width, m_height); 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, 1); } Dbg& MainRenderer::getDbg() { return m_r->getDbg(); } F32 MainRenderer::getAspectRatio() const { return m_r->getAspectRatio(); } } // end namespace anki