mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-05-15 07:09:39 +02:00
390 lines
14 KiB
C++
Executable file
390 lines
14 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 "DebugDeferredBuffer.h"
|
|
#include "DeferredLightingEffect.h"
|
|
#include "FramebufferCache.h"
|
|
#include "HitEffect.h"
|
|
#include "TextureCache.h"
|
|
|
|
#include "AmbientOcclusionEffect.h"
|
|
#include "AntialiasingEffect.h"
|
|
#include "ToneMappingEffect.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];
|
|
|
|
// GPU jobs: Start preparing the deferred and lighting buffer
|
|
addJob<PrepareDeferred>("PrepareDeferred");
|
|
|
|
// Render opaque objects in DeferredBuffer
|
|
addJob<DrawDeferred>("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);
|
|
|
|
// 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);
|
|
|
|
// 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");
|
|
|
|
// 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->_viewFrustum)) {
|
|
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->_viewFrustum);
|
|
|
|
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);
|
|
|
|
config->setNumDrawn((int)inItems.size());
|
|
emit config->numDrawnChanged();
|
|
|
|
glm::mat4 projMat;
|
|
Transform viewMat;
|
|
args->_viewFrustum->evalProjectionMatrix(projMat);
|
|
args->_viewFrustum->evalViewTransform(viewMat);
|
|
|
|
batch.setProjectionTransform(projMat);
|
|
batch.setViewTransform(viewMat);
|
|
|
|
renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn);
|
|
args->_batch = nullptr;
|
|
});
|
|
}
|
|
|
|
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->_viewFrustum);
|
|
|
|
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->_viewFrustum->evalProjectionMatrix(projMat);
|
|
args->_viewFrustum->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->_viewFrustum);
|
|
|
|
// 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->_viewFrustum);
|
|
|
|
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->_viewFrustum->evalProjectionMatrix(projMat);
|
|
args->_viewFrustum->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);
|
|
}
|
|
});
|
|
}
|