From d422545c78a999041f956e02a5d2e74203e79493 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 31 Jan 2018 17:13:06 +0100 Subject: [PATCH] Changed shadow task to do a single octree query as well as pipeline/depth sort for all cascades. Still issue with disapearing objects from shadow map with viewpoint --- .../render-utils/src/RenderShadowTask.cpp | 105 +++++---- libraries/render-utils/src/RenderShadowTask.h | 17 +- libraries/render-utils/src/RenderViewTask.cpp | 9 +- libraries/render/src/render/CullTask.cpp | 203 ++++++++++++++---- libraries/render/src/render/CullTask.h | 40 +++- .../src/render/RenderFetchCullSortTask.cpp | 6 +- 6 files changed, 273 insertions(+), 107 deletions(-) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index b83911582c..c9df67dad1 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -213,28 +213,34 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende initZPassPipelines(*shapePlumber, state); } - const auto coarseFrustum = task.addJob("ShadowSetup"); + const auto setupOutput = task.addJob("ShadowSetup"); + // Fetch and cull the items from the scene + static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); + const auto fetchInput = render::Varying(shadowCasterFilter); + const auto shadowSelection = task.addJob("FetchShadowTree", fetchInput); + const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying(); + const auto shadowItems = task.addJob("FetchShadowSelection", selectionInputs); + + // Sort + const auto sortedPipelines = task.addJob("PipelineSortShadow", shadowItems); + const auto sortedShapes = task.addJob("DepthSortShadow", sortedPipelines, true); for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { char jobName[64]; sprintf(jobName, "ShadowCascadeSetup%d", i); - const auto setupOutput = task.addJob(jobName, i); - const auto shadowFilter = setupOutput.getN(1); + const auto shadowFilter = task.addJob(jobName, i); - // CPU jobs: - // Fetch and cull the items from the scene - const auto shadowSelection = task.addJob("FetchShadowSelection", shadowFilter); - const auto cullInputs = CullSpatialSelection::Inputs(shadowSelection, shadowFilter).asVarying(); - const auto culledShadowSelection = task.addJob("CullShadowSelection", cullInputs, cullFunctor, RenderDetails::SHADOW); - - // Sort - const auto sortedPipelines = task.addJob("PipelineSortShadowSort", culledShadowSelection); - const auto sortedShapesAndBounds = task.addJob("DepthSortShadowMap", sortedPipelines, true); + // CPU jobs: finer grained culling + const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowFilter).asVarying(); + const auto culledShadowItemsAndBounds = task.addJob("CullShadowCascade", cullInputs, cullFunctor, RenderDetails::SHADOW); // GPU jobs: Render to shadow map - task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i); - task.addJob("ShadowCascadeTeardown", setupOutput); + sprintf(jobName, "RenderShadowMap%d", i); + task.addJob(jobName, culledShadowItemsAndBounds, shapePlumber, i); + task.addJob("ShadowCascadeTeardown", shadowFilter); } + + task.addJob("ShadowTeardown", setupOutput); } void RenderShadowTask::configure(const Config& configuration) { @@ -267,16 +273,19 @@ void RenderShadowSetup::setSlopeBias(int cascadeIndex, float value) { _bias[cascadeIndex]._slope = value * value * value * 0.01f; } -void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Output& output) { +void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { auto lightStage = renderContext->_scene->getStage(); assert(lightStage); // Cache old render args RenderArgs* args = renderContext->args; + output.edit0() = args->_renderMode; + output.edit1() = args->_sizeScale; + const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow) { globalShadow->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); - auto firstCascadeFrustum = globalShadow->getCascade(0).getFrustum(); + auto& firstCascadeFrustum = globalShadow->getCascade(0).getFrustum(); unsigned int cascadeIndex; _coarseShadowFrustum->setPosition(firstCascadeFrustum->getPosition()); _coarseShadowFrustum->setOrientation(firstCascadeFrustum->getOrientation()); @@ -296,12 +305,13 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O auto near = firstCascadeFrustum->getNearClip(); auto far = firstCascadeFrustum->getFarClip(); for (cascadeIndex = 1; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) { - auto cascadeLeft = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getRight()); - auto cascadeRight = glm::dot(firstCascadeFrustum->getFarTopRight(), firstCascadeFrustum->getRight()); - auto cascadeTop = glm::dot(firstCascadeFrustum->getFarTopLeft(), firstCascadeFrustum->getUp()); - auto cascadeBottom = glm::dot(firstCascadeFrustum->getFarBottomRight(), firstCascadeFrustum->getUp()); - auto cascadeNear = firstCascadeFrustum->getNearClip(); - auto cascadeFar = firstCascadeFrustum->getFarClip(); + auto& cascadeFrustum = globalShadow->getCascade(cascadeIndex).getFrustum(); + auto cascadeLeft = glm::dot(cascadeFrustum->getFarTopLeft(), cascadeFrustum->getRight()); + auto cascadeRight = glm::dot(cascadeFrustum->getFarTopRight(), cascadeFrustum->getRight()); + auto cascadeTop = glm::dot(cascadeFrustum->getFarTopLeft(), cascadeFrustum->getUp()); + auto cascadeBottom = glm::dot(cascadeFrustum->getFarBottomRight(), cascadeFrustum->getUp()); + auto cascadeNear = cascadeFrustum->getNearClip(); + auto cascadeFar = cascadeFrustum->getFarClip(); left = glm::min(left, cascadeLeft); right = glm::max(right, cascadeRight); bottom = glm::min(bottom, cascadeBottom); @@ -312,9 +322,14 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O _coarseShadowFrustum->setProjection(glm::ortho(left, right, bottom, top, near, far)); _coarseShadowFrustum->calculate(); - output = _coarseShadowFrustum; - } else { - output = nullptr; + // Push frustum for further culling and selection + args->pushViewFrustum(*_coarseShadowFrustum); + + args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; + if (lightStage->getCurrentKeyLight()->getType() == graphics::Light::SUN) { + // Set to ridiculously high amount to prevent solid angle culling in octree selection + args->_sizeScale = 1e16f; + } } } @@ -324,37 +339,41 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon // Cache old render args RenderArgs* args = renderContext->args; - output.edit0() = args->_renderMode; - output.edit2() = args->_sizeScale; - const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow && _cascadeIndexgetCascadeCount()) { - output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); + output = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); // Set the keylight render args - args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); - args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; - if (lightStage->getCurrentKeyLight()->getType() == graphics::Light::SUN) { - const float shadowSizeScale = 1e16f; - // Set the size scale to a ridiculously high value to prevent small object culling which assumes - // the view frustum is a perspective projection. But this isn't the case for the sun which - // is an orthographic projection. - args->_sizeScale = shadowSizeScale; - } - + auto& cascade = globalShadow->getCascade(_cascadeIndex); + auto& cascadeFrustum = cascade.getFrustum(); + args->pushViewFrustum(*cascadeFrustum); + // Set the cull threshold to 2 shadow texels. + auto texelSize = glm::max(cascadeFrustum->getHeight(), cascadeFrustum->getWidth()) / cascade.framebuffer->getSize().x; + texelSize *= 2.0f; + // SizeScale is used in the shadow cull function defined ine RenderViewTask + args->_sizeScale = texelSize * texelSize; } else { - output.edit1() = ItemFilter::Builder::nothing(); + output = ItemFilter::Builder::nothing(); } } void RenderShadowCascadeTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) { RenderArgs* args = renderContext->args; - if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE && !input.get1().selectsNothing()) { + if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE && !input.selectsNothing()) { + args->popViewFrustum(); + } + assert(args->hasViewFrustum()); +} + +void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) { + RenderArgs* args = renderContext->args; + + if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE) { args->popViewFrustum(); } assert(args->hasViewFrustum()); // Reset the render args args->_renderMode = input.get0(); - args->_sizeScale = input.get2(); -}; + args->_sizeScale = input.get1(); +} diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index f488c46b8e..1736d07fd5 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -82,13 +82,13 @@ signals: class RenderShadowSetup { public: - using Output = ViewFrustumPointer; + using Outputs = render::VaryingSet2; using Config = RenderShadowSetupConfig; - using JobModel = render::Job::ModelO; + using JobModel = render::Job::ModelO; RenderShadowSetup(); void configure(const Config& configuration); - void run(const render::RenderContextPointer& renderContext, Output& output); + void run(const render::RenderContextPointer& renderContext, Outputs& output); private: @@ -104,7 +104,7 @@ private: class RenderShadowCascadeSetup { public: - using Outputs = render::VaryingSet3; + using Outputs = render::ItemFilter; using JobModel = render::Job::ModelO; RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} @@ -117,9 +117,16 @@ private: class RenderShadowCascadeTeardown { public: - using Input = RenderShadowCascadeSetup::Outputs; + using Input = render::ItemFilter; using JobModel = render::Job::ModelI; void run(const render::RenderContextPointer& renderContext, const Input& input); }; +class RenderShadowTeardown { +public: + using Input = RenderShadowSetup::Outputs; + using JobModel = render::Job::ModelI; + void run(const render::RenderContextPointer& renderContext, const Input& input); +}; + #endif // hifi_RenderShadowTask_h diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index dc6c66e058..c2e43582cd 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -21,13 +21,8 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render: // but the cullFunctor passed is probably tailored for perspective projection and culls too much. task.addJob("RenderShadowTask", [](const RenderArgs* args, const AABox& bounds) { // Cull only objects that are too small relatively to shadow frustum - auto& frustum = args->getViewFrustum(); - auto frustumSize = std::max(frustum.getHeight(), frustum.getWidth()); - const auto boundsRadius = bounds.getDimensions().length(); - const auto relativeBoundRadius = boundsRadius / frustumSize; - const auto threshold = 1e-3f; - return relativeBoundRadius > threshold; - return true; + const auto boundsSquareRadius = glm::dot(bounds.getDimensions(), bounds.getDimensions()); + return boundsSquareRadius > args->_sizeScale; }); const auto items = task.addJob("FetchCullSort", cullFunctor); diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 70331cdb47..c6ff224560 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -19,6 +19,50 @@ using namespace render; +// Culling Frustum / solidAngle test helper class +struct Test { + CullFunctor _functor; + RenderArgs* _args; + RenderDetails::Item& _renderDetails; + glm::vec3 _eyePos; + float _squareTanAlpha; + + Test(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails) : + _functor(functor), + _args(pargs), + _renderDetails(renderDetails) { + // FIXME: Keep this code here even though we don't use it yet + /*_eyePos = _args->getViewFrustum().getPosition(); + float a = glm::degrees(Octree::getAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust)); + auto angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees + angle = std::max(glm::radians(1.0f / 60.0f), a); // no better than 1 minute of degree + auto tanAlpha = tan(angle); + _squareTanAlpha = (float)(tanAlpha * tanAlpha); + */ + } + + bool frustumTest(const AABox& bound) { + if (!_args->getViewFrustum().boxIntersectsFrustum(bound)) { + _renderDetails._outOfView++; + return false; + } + return true; + } + + bool solidAngleTest(const AABox& bound) { + // FIXME: Keep this code here even though we don't use it yet + //auto eyeToPoint = bound.calcCenter() - _eyePos; + //auto boundSize = bound.getDimensions(); + //float test = (glm::dot(boundSize, boundSize) / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha; + //if (test < 0.0f) { + if (!_functor(_args, bound)) { + _renderDetails._tooSmall++; + return false; + } + return true; + } +}; + void render::cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details, const ItemBounds& inItems, ItemBounds& outItems) { assert(renderContext->args); @@ -82,18 +126,20 @@ void FetchSpatialTree::configure(const Config& config) { _lodAngle = config.lodAngle; } -void FetchSpatialTree::run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection) { +void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemSpatialTree::ItemSelection& outSelection) { // start fresh outSelection.clear(); + auto& filter = inputs; + if (!filter.selectsNothing()) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; auto& scene = renderContext->_scene; - // Eventually use a frozen frustum auto queryFrustum = args->getViewFrustum(); + // Eventually use a frozen frustum if (_freezeFrustum) { if (_justFrozeFrustum) { _justFrozeFrustum = false; @@ -134,50 +180,6 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, args->pushViewFrustum(_frozenFrustum); // replace the true view frustum by the frozen one } - // Culling Frustum / solidAngle test helper class - struct Test { - CullFunctor _functor; - RenderArgs* _args; - RenderDetails::Item& _renderDetails; - glm::vec3 _eyePos; - float _squareTanAlpha; - - Test(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails) : - _functor(functor), - _args(pargs), - _renderDetails(renderDetails) - { - // FIXME: Keep this code here even though we don't use it yet - /*_eyePos = _args->getViewFrustum().getPosition(); - float a = glm::degrees(Octree::getAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust)); - auto angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees - angle = std::max(glm::radians(1.0f / 60.0f), a); // no better than 1 minute of degree - auto tanAlpha = tan(angle); - _squareTanAlpha = (float)(tanAlpha * tanAlpha); - */ - } - - bool frustumTest(const AABox& bound) { - if (!_args->getViewFrustum().boxIntersectsFrustum(bound)) { - _renderDetails._outOfView++; - return false; - } - return true; - } - - bool solidAngleTest(const AABox& bound) { - // FIXME: Keep this code here even though we don't use it yet - //auto eyeToPoint = bound.calcCenter() - _eyePos; - //auto boundSize = bound.getDimensions(); - //float test = (glm::dot(boundSize, boundSize) / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha; - //if (test < 0.0f) { - if (!_functor(_args, bound)) { - _renderDetails._tooSmall++; - return false; - } - return true; - } - }; Test test(_cullFunctor, args, details); // Now we have a selection of items to render @@ -309,3 +311,112 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, std::static_pointer_cast(renderContext->jobConfig)->numItems = (int)outItems.size(); } + +void CullShapeBounds::run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + RenderArgs* args = renderContext->args; + + const auto& inShapes = inputs.get0(); + const auto& filter = inputs.get1(); + auto& outShapes = outputs.edit0(); + auto& outBounds = outputs.edit1(); + + outShapes.clear(); + outBounds = AABox(); + + if (!filter.selectsNothing()) { + auto& details = args->_details.edit(_detailType); + Test test(_cullFunctor, args, details); + + for (auto& inItems : inShapes) { + auto key = inItems.first; + auto outItems = outShapes.find(key); + if (outItems == outShapes.end()) { + outItems = outShapes.insert(std::make_pair(key, ItemBounds{})).first; + outItems->second.reserve(inItems.second.size()); + } + + details._considered += (int)inItems.second.size(); + + for (auto& item : inItems.second) { + if (test.frustumTest(item.bound) && test.solidAngleTest(item.bound)) { + outItems->second.emplace_back(item); + outBounds += item.bound; + } + } + + details._rendered += (int)outItems->second.size(); + } + + for (auto& items : outShapes) { + items.second.shrink_to_fit(); + } + } +} + +void FetchSpatialSelection::run(const RenderContextPointer& renderContext, + const Inputs& inputs, ItemBounds& outItems) { + assert(renderContext->args); + RenderArgs* args = renderContext->args; + auto& scene = renderContext->_scene; + auto& inSelection = inputs.get0(); + + // Now we have a selection of items to render + outItems.clear(); + outItems.reserve(inSelection.numItems()); + + const auto filter = inputs.get1(); + if (!filter.selectsNothing()) { + // Now get the bound, and + // filter individually against the _filter + + // inside & fit items: filter only, culling is disabled + { + PerformanceTimer perfTimer("insideFitItems"); + for (auto id : inSelection.insideItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + // inside & subcell items: filter only, culling is disabled + { + PerformanceTimer perfTimer("insideSmallItems"); + for (auto id : inSelection.insideSubcellItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + // partial & fit items: filter only, culling is disabled + { + PerformanceTimer perfTimer("partialFitItems"); + for (auto id : inSelection.partialItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + // partial & subcell items: filter only, culling is disabled + { + PerformanceTimer perfTimer("partialSmallItems"); + for (auto id : inSelection.partialSubcellItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + } +} diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 486c4f4cdf..a140a86aee 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -55,12 +55,13 @@ namespace render { float _lodAngle; public: using Config = FetchSpatialTreeConfig; - using JobModel = Job::ModelIO; + using Inputs = ItemFilter; + using JobModel = Job::ModelIO; FetchSpatialTree() {} void configure(const Config& config); - void run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection); + void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemSpatialTree::ItemSelection& outSelection); }; class CullSpatialSelectionConfig : public Job::Config { @@ -96,7 +97,8 @@ namespace render { _detailType(type) {} CullSpatialSelection(CullFunctor cullFunctor) : - _cullFunctor{ cullFunctor } {} + _cullFunctor{ cullFunctor } { + } CullFunctor _cullFunctor; RenderDetails::Type _detailType{ RenderDetails::OTHER }; @@ -105,6 +107,38 @@ namespace render { void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems); }; + class CullShapeBounds { + public: + using Inputs = render::VaryingSet2; + using Outputs = render::VaryingSet2; + using JobModel = Job::ModelIO; + + CullShapeBounds(CullFunctor cullFunctor, RenderDetails::Type type) : + _cullFunctor{ cullFunctor }, + _detailType(type) {} + + CullShapeBounds(CullFunctor cullFunctor) : + _cullFunctor{ cullFunctor } { + } + + void run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); + + private: + + CullFunctor _cullFunctor; + RenderDetails::Type _detailType{ RenderDetails::OTHER }; + + }; + + class FetchSpatialSelection { + public: + using Inputs = render::VaryingSet2; + using JobModel = Job::ModelIO; + + FetchSpatialSelection() {} + void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems); + }; + } #endif // hifi_render_CullTask_h; \ No newline at end of file diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index d7294fa2bd..a1b4f079e7 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -23,9 +23,9 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin // CPU jobs: // Fetch and cull the items from the scene const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); - const auto spatialFilter = render::Varying(filter); - const auto spatialSelection = task.addJob("FetchSceneSelection", spatialFilter); - const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, spatialFilter).asVarying(); + const auto fetchInput = render::Varying(filter); + const auto spatialSelection = task.addJob("FetchSceneSelection", fetchInput); + const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, render::Varying(filter)).asVarying(); const auto culledSpatialSelection = task.addJob("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM); // Overlays are not culled