// // RenderDeferredTask.cpp // render-utils/src/ // // Created by Sam Gateau on 5/29/15. // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "RenderDeferredTask.h" #include #include #include #include #include #include #include #include #include #include #include #include "DebugDeferredBuffer.h" #include "DeferredLightingEffect.h" #include "SurfaceGeometryPass.h" #include "FramebufferCache.h" #include "HitEffect.h" #include "TextureCache.h" #include "AmbientOcclusionEffect.h" #include "AntialiasingEffect.h" #include "ToneMappingEffect.h" #include "SubsurfaceScattering.h" using namespace render; extern void initStencilPipeline(gpu::PipelinePointer& pipeline); extern void initOverlay3DPipelines(render::ShapePlumber& plumber); extern void initDeferredPipelines(render::ShapePlumber& plumber); void PrepareDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { DependencyManager::get()->prepare(renderContext->args); } void RenderDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { DependencyManager::get()->render(renderContext); } RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; // Prepare the ShapePipelines ShapePlumberPointer shapePlumber = std::make_shared(); initDeferredPipelines(*shapePlumber); // CPU jobs: // Fetch and cull the items from the scene auto spatialFilter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); const auto spatialSelection = addJob("FetchSceneSelection", spatialFilter); const auto culledSpatialSelection = addJob("CullSceneSelection", spatialSelection, cullFunctor, RenderDetails::ITEM, spatialFilter); // Overlays are not culled const auto nonspatialSelection = addJob("FetchOverlaySelection"); // Multi filter visible items into different buckets const int NUM_FILTERS = 3; const int OPAQUE_SHAPE_BUCKET = 0; const int TRANSPARENT_SHAPE_BUCKET = 1; const int LIGHT_BUCKET = 2; const int BACKGROUND_BUCKET = 2; MultiFilterItem::ItemFilterArray spatialFilters = { { ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::transparentShape(), ItemFilter::Builder::light() } }; MultiFilterItem::ItemFilterArray nonspatialFilters = { { ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::transparentShape(), ItemFilter::Builder::background() } }; const auto filteredSpatialBuckets = addJob>("FilterSceneSelection", culledSpatialSelection, spatialFilters).get::ItemBoundsArray>(); const auto filteredNonspatialBuckets = addJob>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters).get::ItemBoundsArray>(); // Extract / Sort opaques / Transparents / Lights / Overlays const auto opaques = addJob("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]); const auto transparents = addJob("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto lights = filteredSpatialBuckets[LIGHT_BUCKET]; const auto overlayOpaques = addJob("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]); const auto overlayTransparents = addJob("DepthSortOverlayTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET]; // Prepare deferred, generate the shared Deferred Frame Transform const auto deferredFrameTransform = addJob("EvalDeferredFrameTransform"); // GPU jobs: Start preparing the deferred and lighting buffer addJob("PrepareDeferred"); // Render opaque objects in DeferredBuffer addJob("DrawOpaqueDeferred", opaques, shapePlumber); // Once opaque is all rendered create stencil background addJob("DrawOpaqueStencil"); // Use Stencil and start drawing background in Lighting buffer addJob("DrawBackgroundDeferred", background); // Opaque all rendered, generate surface geometry buffers const auto curvatureFramebufferAndDepth = addJob("SurfaceGeometry", deferredFrameTransform); addJob("DiffuseCurvature", curvatureFramebufferAndDepth); const auto diffusedCurvatureFramebuffer = addJob("DiffuseCurvature2", curvatureFramebufferAndDepth, true); // AO job addJob("AmbientOcclusion"); // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now. addJob("DrawLight", lights); const auto scatteringInputs = render::Varying(SubsurfaceScattering::Inputs(deferredFrameTransform, curvatureFramebufferAndDepth[0])); const auto scatteringFramebuffer = addJob("Scattering", scatteringInputs); // DeferredBuffer is complete, now let's shade it into the LightingBuffer addJob("RenderDeferred"); // AA job to be revisited addJob("Antialiasing"); // Render transparent objects forward in LightingBuffer addJob("DrawTransparentDeferred", transparents, shapePlumber); // Lighting Buffer ready for tone mapping addJob("ToneMapping"); // Overlays addJob("DrawOverlay3DOpaque", overlayOpaques, true); addJob("DrawOverlay3DTransparent", overlayTransparents, false); // Debugging stages { // Debugging Deferred buffer job addJob("DebugDeferredBuffer", scatteringFramebuffer); // Scene Octree Debuging job { addJob("DrawSceneOctree", spatialSelection); addJob("DrawItemSelection", spatialSelection); } // Status icon rendering job { // Grab a texture map representing the different status icons and assign that to the drawStatsuJob auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); addJob("DrawStatus", opaques, DrawStatus(statusIconMap)); } } // FIXME: Hit effect is never used, let's hide it for now, probably a more generic way to add custom post process effects // addJob("HitEffect"); // Blit! addJob("Blit"); } void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { // sanity checks assert(sceneContext); if (!sceneContext->_scene) { return; } // Is it possible that we render without a viewFrustum ? if (!(renderContext->args && renderContext->args->hasViewFrustum())) { return; } for (auto job : _jobs) { job.run(sceneContext, renderContext); } } void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); auto config = std::static_pointer_cast(renderContext->jobConfig); RenderArgs* args = renderContext->args; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); glm::mat4 projMat; Transform viewMat; args->getViewFrustum().evalProjectionMatrix(projMat); args->getViewFrustum().evalViewTransform(viewMat); batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); args->_batch = nullptr; }); config->setNumDrawn((int)inItems.size()); } void DrawStateSortDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); auto config = std::static_pointer_cast(renderContext->jobConfig); RenderArgs* args = renderContext->args; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); glm::mat4 projMat; Transform viewMat; args->getViewFrustum().evalProjectionMatrix(projMat); args->getViewFrustum().evalViewTransform(viewMat); batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); if (_stateSort) { renderStateSortShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); } else { renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); } args->_batch = nullptr; }); config->setNumDrawn((int)inItems.size()); } DrawOverlay3D::DrawOverlay3D(bool opaque) : _shapePlumber(std::make_shared()), _opaquePass(opaque) { initOverlay3DPipelines(*_shapePlumber); } void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const render::ItemBounds& inItems) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); auto config = std::static_pointer_cast(renderContext->jobConfig); config->setNumDrawn((int)inItems.size()); emit config->numDrawnChanged(); if (!inItems.empty()) { RenderArgs* args = renderContext->args; // Clear the framebuffer without stereo // Needs to be distinct from the other batch because using the clear call // while stereo is enabled triggers a warning if (_opaquePass) { gpu::Batch batch; batch.enableStereo(false); batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true); args->_context->render(batch); } // Render the items gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); glm::mat4 projMat; Transform viewMat; args->getViewFrustum().evalProjectionMatrix(projMat); args->getViewFrustum().evalViewTransform(viewMat); batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); args->_batch = nullptr; }); } } gpu::PipelinePointer DrawStencilDeferred::_opaquePipeline; const gpu::PipelinePointer& DrawStencilDeferred::getOpaquePipeline() { if (!_opaquePipeline) { initStencilPipeline(_opaquePipeline); } return _opaquePipeline; } void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); // from the touched pixel generate the stencil buffer RenderArgs* args = renderContext->args; doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; auto deferredFboColorDepthStencil = DependencyManager::get()->getDeferredFramebufferDepthColor(); batch.enableStereo(false); batch.setFramebuffer(deferredFboColorDepthStencil); batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); batch.setPipeline(getOpaquePipeline()); batch.draw(gpu::TRIANGLE_STRIP, 4); batch.setResourceTexture(0, nullptr); }); args->_batch = nullptr; } void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; _gpuTimer.begin(batch); auto lightingFBO = DependencyManager::get()->getLightingFramebuffer(); batch.enableSkybox(true); batch.setFramebuffer(lightingFBO); batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); glm::mat4 projMat; Transform viewMat; args->getViewFrustum().evalProjectionMatrix(projMat); args->getViewFrustum().evalViewTransform(viewMat); batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); renderItems(sceneContext, renderContext, inItems); _gpuTimer.end(batch); }); args->_batch = nullptr; std::static_pointer_cast(renderContext->jobConfig)->gpuTime = _gpuTimer.getAverage(); } void Blit::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { assert(renderContext->args); assert(renderContext->args->_context); RenderArgs* renderArgs = renderContext->args; auto blitFbo = renderArgs->_blitFramebuffer; if (!blitFbo) { return; } // Determine size from viewport int width = renderArgs->_viewport.z; int height = renderArgs->_viewport.w; // Blit primary to blit FBO auto framebufferCache = DependencyManager::get(); auto primaryFbo = framebufferCache->getPrimaryFramebuffer(); gpu::doInBatch(renderArgs->_context, [&](gpu::Batch& batch) { batch.setFramebuffer(blitFbo); if (renderArgs->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { if (renderArgs->_context->isStereo()) { gpu::Vec4i srcRectLeft; srcRectLeft.z = width / 2; srcRectLeft.w = height; gpu::Vec4i srcRectRight; srcRectRight.x = width / 2; srcRectRight.z = width; srcRectRight.w = height; gpu::Vec4i destRectLeft; destRectLeft.x = srcRectLeft.z; destRectLeft.z = srcRectLeft.x; destRectLeft.y = srcRectLeft.y; destRectLeft.w = srcRectLeft.w; gpu::Vec4i destRectRight; destRectRight.x = srcRectRight.z; destRectRight.z = srcRectRight.x; destRectRight.y = srcRectRight.y; destRectRight.w = srcRectRight.w; // Blit left to right and right to left in stereo batch.blit(primaryFbo, srcRectRight, blitFbo, destRectLeft); batch.blit(primaryFbo, srcRectLeft, blitFbo, destRectRight); } else { gpu::Vec4i srcRect; srcRect.z = width; srcRect.w = height; gpu::Vec4i destRect; destRect.x = width; destRect.y = 0; destRect.z = 0; destRect.w = height; batch.blit(primaryFbo, srcRect, blitFbo, destRect); } } else { gpu::Vec4i rect; rect.z = width; rect.w = height; batch.blit(primaryFbo, rect, blitFbo, rect); } }); }