overte-HifiExperiments/libraries/render-utils/src/RenderDeferredTask.cpp

437 lines
16 KiB
C++
Executable file

//
// 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 <PerfStat.h>
#include <PathUtils.h>
#include <RenderArgs.h>
#include <ViewFrustum.h>
#include <gpu/Context.h>
#include <render/CullTask.h>
#include <render/SortTask.h>
#include <render/DrawTask.h>
#include <render/DrawStatus.h>
#include <render/DrawSceneOctree.h>
#include <render/BlurTask.h>
#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<DeferredLightingEffect>()->prepare(renderContext->args);
}
void RenderDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
DependencyManager::get<DeferredLightingEffect>()->render(renderContext);
}
RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; };
// Prepare the ShapePipelines
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
initDeferredPipelines(*shapePlumber);
// CPU jobs:
// Fetch and cull the items from the scene
auto spatialFilter = ItemFilter::Builder::visibleWorldItems().withoutLayered();
const auto spatialSelection = addJob<FetchSpatialTree>("FetchSceneSelection", spatialFilter);
const auto culledSpatialSelection = addJob<CullSpatialSelection>("CullSceneSelection", spatialSelection, cullFunctor, RenderDetails::ITEM, spatialFilter);
// Overlays are not culled
const auto nonspatialSelection = addJob<FetchNonspatialItems>("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<NUM_FILTERS>::ItemFilterArray spatialFilters = { {
ItemFilter::Builder::opaqueShape(),
ItemFilter::Builder::transparentShape(),
ItemFilter::Builder::light()
} };
MultiFilterItem<NUM_FILTERS>::ItemFilterArray nonspatialFilters = { {
ItemFilter::Builder::opaqueShape(),
ItemFilter::Builder::transparentShape(),
ItemFilter::Builder::background()
} };
const auto filteredSpatialBuckets = addJob<MultiFilterItem<NUM_FILTERS>>("FilterSceneSelection", culledSpatialSelection, spatialFilters).get<MultiFilterItem<NUM_FILTERS>::ItemBoundsArray>();
const auto filteredNonspatialBuckets = addJob<MultiFilterItem<NUM_FILTERS>>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters).get<MultiFilterItem<NUM_FILTERS>::ItemBoundsArray>();
// Extract / Sort opaques / Transparents / Lights / Overlays
const auto opaques = addJob<DepthSortItems>("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]);
const auto transparents = addJob<DepthSortItems>("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false));
const auto lights = filteredSpatialBuckets[LIGHT_BUCKET];
const auto overlayOpaques = addJob<DepthSortItems>("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]);
const auto overlayTransparents = addJob<DepthSortItems>("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<GenerateDeferredFrameTransform>("EvalDeferredFrameTransform");
// GPU jobs: Start preparing the deferred and lighting buffer
addJob<PrepareDeferred>("PrepareDeferred");
// Render opaque objects in DeferredBuffer
addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaques, shapePlumber);
// Once opaque is all rendered create stencil background
addJob<DrawStencilDeferred>("DrawOpaqueStencil");
// Use Stencil and start drawing background in Lighting buffer
addJob<DrawBackgroundDeferred>("DrawBackgroundDeferred", background);
// Opaque all rendered, generate surface geometry buffers
const auto curvatureFramebufferAndDepth = addJob<SurfaceGeometryPass>("SurfaceGeometry", deferredFrameTransform);
addJob<render::BlurGaussianDepthAware>("DiffuseCurvature", curvatureFramebufferAndDepth);
const auto diffusedCurvatureFramebuffer = addJob<render::BlurGaussianDepthAware>("DiffuseCurvature2", curvatureFramebufferAndDepth, true);
// AO job
addJob<AmbientOcclusionEffect>("AmbientOcclusion");
// Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now.
addJob<DrawLight>("DrawLight", lights);
const auto scatteringInputs = render::Varying(SubsurfaceScattering::Inputs(deferredFrameTransform, curvatureFramebufferAndDepth[0]));
const auto scatteringFramebuffer = addJob<SubsurfaceScattering>("Scattering", scatteringInputs);
// DeferredBuffer is complete, now let's shade it into the LightingBuffer
addJob<RenderDeferred>("RenderDeferred");
// AA job to be revisited
addJob<Antialiasing>("Antialiasing");
// Render transparent objects forward in LightingBuffer
addJob<DrawDeferred>("DrawTransparentDeferred", transparents, shapePlumber);
// Lighting Buffer ready for tone mapping
addJob<ToneMappingDeferred>("ToneMapping");
// Overlays
addJob<DrawOverlay3D>("DrawOverlay3DOpaque", overlayOpaques, true);
addJob<DrawOverlay3D>("DrawOverlay3DTransparent", overlayTransparents, false);
// Debugging stages
{
// Debugging Deferred buffer job
addJob<DebugDeferredBuffer>("DebugDeferredBuffer", scatteringFramebuffer);
// Scene Octree Debuging job
{
addJob<DrawSceneOctree>("DrawSceneOctree", spatialSelection);
addJob<DrawItemSelection>("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<TextureCache>()->getImageTexture(iconMapPath);
addJob<DrawStatus>("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>("HitEffect");
// Blit!
addJob<Blit>("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<Config>(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<Config>(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<ShapePlumber>()),
_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<Config>(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<FramebufferCache>()->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<FramebufferCache>()->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<Config>(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<FramebufferCache>();
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);
}
});
}