Merge pull request #7218 from zzmp/fix/resort-in-octree

Fix item key lookup on updated items in scene
This commit is contained in:
samcake 2016-03-08 17:48:51 -08:00
commit bfed0304c3
15 changed files with 309 additions and 385 deletions

View file

@ -231,23 +231,19 @@ bool Overlays::editOverlay(unsigned int id, const QVariant& properties) {
Overlay::Pointer thisOverlay = getOverlay(id);
if (thisOverlay) {
thisOverlay->setProperties(properties.toMap());
if (thisOverlay->is3D()) {
render::ItemKey oldItemKey = render::payloadGetKey(thisOverlay);
thisOverlay->setProperties(properties.toMap());
render::ItemKey itemKey = render::payloadGetKey(thisOverlay);
if (itemKey != oldItemKey) {
auto itemID = thisOverlay->getRenderItemID();
if (render::Item::isValidID(itemID)) {
render::ScenePointer scene = qApp->getMain3DScene();
auto itemID = thisOverlay->getRenderItemID();
if (render::Item::isValidID(itemID)) {
render::ScenePointer scene = qApp->getMain3DScene();
const render::Item& item = scene->getItem(itemID);
if (item.getKey() != render::payloadGetKey(thisOverlay)) {
render::PendingChanges pendingChanges;
pendingChanges.resortItem(itemID, oldItemKey, itemKey);
pendingChanges.updateItem(itemID);
scene->enqueuePendingChanges(pendingChanges);
}
}
} else {
thisOverlay->setProperties(properties.toMap());
}
return true;
@ -601,4 +597,4 @@ float Overlays::width() const {
float Overlays::height() const {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
return offscreenUi->getWindow()->size().height();
}
}

View file

@ -10,27 +10,30 @@
// 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 "render/DrawTask.h"
#include "render/DrawStatus.h"
#include "render/DrawSceneOctree.h"
#include "AmbientOcclusionEffect.h"
#include "AntialiasingEffect.h"
#include "ToneMappingEffect.h"
#include "RenderDeferredTask.h"
using namespace render;
extern void initStencilPipeline(gpu::PipelinePointer& pipeline);
@ -54,34 +57,42 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
// CPU jobs:
// Fetch and cull the items from the scene
auto sceneFilter = ItemFilter::Builder::visibleWorldItems().withoutLayered();
const auto sceneSelection = addJob<FetchSpatialTree>("FetchSceneSelection", sceneFilter);
const auto culledSceneSelection = addJob<CullSpatialSelection>("CullSceneSelection", sceneSelection, cullFunctor, RenderDetails::ITEM, sceneFilter);
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;
MultiFilterItem<NUM_FILTERS>::ItemFilterArray triageFilters = { {
const int BACKGROUND_BUCKET = 2;
MultiFilterItem<NUM_FILTERS>::ItemFilterArray spatialFilters = { {
ItemFilter::Builder::opaqueShape(),
ItemFilter::Builder::transparentShape(),
ItemFilter::Builder::light()
} };
const auto filteredItemsBuckets = addJob<MultiFilterItem<NUM_FILTERS>>("FilterSceneSelection", culledSceneSelection, triageFilters).get<MultiFilterItem<NUM_FILTERS>::ItemBoundsArray>();
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", filteredItemsBuckets[OPAQUE_SHAPE_BUCKET]);
const auto transparents = addJob<DepthSortItems>("DepthSortTransparent", filteredItemsBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false));
const auto lights = filteredItemsBuckets[LIGHT_BUCKET];
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];
// Overlays are not culled because we want to make sure they are seen
// Could be considered a bug in the current cullfunctor
const auto overlayOpaques = addJob<FetchItems>("FetchOverlayOpaque", ItemFilter::Builder::opaqueShapeLayered());
const auto fetchedOverlayOpaques = addJob<FetchItems>("FetchOverlayTransparents", ItemFilter::Builder::transparentShapeLayered());
const auto overlayTransparents = addJob<DepthSortItems>("DepthSortTransparentOverlay", fetchedOverlayOpaques, DepthSortItems(false));
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
// GPU jobs: Start preparing the deferred and lighting buffer
addJob<PrepareDeferred>("PrepareDeferred");
// Render opaque objects in DeferredBuffer
@ -91,7 +102,7 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
addJob<DrawStencilDeferred>("DrawOpaqueStencil");
// Use Stencil and start drawing background in Lighting buffer
addJob<DrawBackgroundDeferred>("DrawBackgroundDeferred");
addJob<DrawBackgroundDeferred>("DrawBackgroundDeferred", background);
// AO job
addJob<AmbientOcclusionEffect>("AmbientOcclusion");
@ -123,8 +134,8 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
// Scene Octree Debuging job
{
addJob<DrawSceneOctree>("DrawSceneOctree", sceneSelection);
addJob<DrawItemSelection>("DrawItemSelection", sceneSelection);
addJob<DrawSceneOctree>("DrawSceneOctree", spatialSelection);
addJob<DrawItemSelection>("DrawItemSelection", spatialSelection);
}
// Status icon rendering job
@ -170,9 +181,9 @@ void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderCont
RenderArgs* args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
args->_batch = &batch;
config->setNumDrawn((int)inItems.size());
emit config->numDrawnChanged();
@ -222,7 +233,8 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
// Render the items
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
args->_whiteTexture = DependencyManager::get<TextureCache>()->getWhiteTexture();
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
glm::mat4 projMat;
Transform viewMat;
@ -231,14 +243,10 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
batch.setResourceTexture(0, args->_whiteTexture);
renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn);
args->_batch = nullptr;
});
args->_batch = nullptr;
args->_whiteTexture.reset();
}
}
@ -276,20 +284,10 @@ void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const Ren
args->_batch = nullptr;
}
void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render backgrounds
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::background());
ItemBounds inItems;
inItems.reserve(items.size());
for (auto id : items) {
inItems.emplace_back(id);
}
RenderArgs* args = renderContext->args;
doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;

View file

@ -12,9 +12,8 @@
#ifndef hifi_RenderDeferredTask_h
#define hifi_RenderDeferredTask_h
#include "gpu/Pipeline.h"
#include "render/DrawTask.h"
#include <gpu/Pipeline.h>
#include <render/CullTask.h>
class SetupDeferred {
public:
@ -85,9 +84,9 @@ protected:
class DrawBackgroundDeferred {
public:
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemBounds& inItems);
using JobModel = render::Job::Model<DrawBackgroundDeferred>;
using JobModel = render::Job::ModelI<DrawBackgroundDeferred, render::ItemBounds>;
};
class DrawOverlay3DConfig : public render::Job::Config {

View file

@ -9,16 +9,20 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "RenderShadowTask.h"
#include <gpu/Context.h>
#include <ViewFrustum.h>
#include "render/Context.h"
#include <render/Context.h>
#include <render/CullTask.h>
#include <render/SortTask.h>
#include <render/DrawTask.h>
#include "DeferredLightingEffect.h"
#include "FramebufferCache.h"
#include "RenderShadowTask.h"
#include "model_shadow_vert.h"
#include "skin_model_shadow_vert.h"
@ -28,7 +32,7 @@
using namespace render;
void RenderShadowMap::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext,
const render::ShapesIDsBounds& inShapes) {
const render::ShapeBounds& inShapes) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
@ -104,20 +108,18 @@ RenderShadowTask::RenderShadowTask(CullFunctor cullFunctor) : Task(std::make_sha
skinProgram, state);
}
// CPU: Fetch shadow-casting opaques
const auto fetchedItems = addJob<FetchItems>("FetchShadowMap");
// CPU jobs:
// Fetch and cull the items from the scene
auto shadowFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered();
const auto shadowSelection = addJob<FetchSpatialTree>("FetchShadowSelection", shadowFilter);
const auto culledShadowSelection = addJob<CullSpatialSelection>("CullShadowSelection", shadowSelection, cullFunctor, RenderDetails::SHADOW, shadowFilter);
// CPU: Cull against KeyLight frustum (nearby viewing camera)
const auto culledItems = addJob<CullItems<RenderDetails::SHADOW>>("CullShadowMap", fetchedItems, cullFunctor);
// Sort
const auto sortedPipelines = addJob<PipelineSortShapes>("PipelineSortShadowSort", culledShadowSelection);
const auto sortedShapes = addJob<DepthSortShapes>("DepthSortShadowMap", sortedPipelines);
// CPU: Sort by pipeline
const auto sortedShapes = addJob<PipelineSortShapes>("PipelineSortShadowSort", culledItems);
// CPU: Sort front to back
const auto shadowShapes = addJob<DepthSortShapes>("DepthSortShadowMap", sortedShapes);
// GPU: Render to shadow map
addJob<RenderShadowMap>("RenderShadowMap", shadowShapes, shapePlumber);
// GPU jobs: Render to shadow map
addJob<RenderShadowMap>("RenderShadowMap", sortedShapes, shapePlumber);
}
void RenderShadowTask::configure(const Config& configuration) {

View file

@ -15,17 +15,17 @@
#include <gpu/Framebuffer.h>
#include <gpu/Pipeline.h>
#include <render/DrawTask.h>
#include <render/CullTask.h>
class ViewFrustum;
class RenderShadowMap {
public:
using JobModel = render::Job::ModelI<RenderShadowMap, render::ShapesIDsBounds>;
using JobModel = render::Job::ModelI<RenderShadowMap, render::ShapeBounds>;
RenderShadowMap(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {}
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext,
const render::ShapesIDsBounds& inShapes);
const render::ShapeBounds& inShapes);
protected:
render::ShapePlumberPointer _shapePlumber;

View file

@ -62,99 +62,21 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc
details._rendered += (int)outItems.size();
}
struct ItemBoundSort {
float _centerDepth = 0.0f;
float _nearDepth = 0.0f;
float _farDepth = 0.0f;
ItemID _id = 0;
AABox _bounds;
ItemBoundSort() {}
ItemBoundSort(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {}
};
struct FrontToBackSort {
bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) {
return (left._centerDepth < right._centerDepth);
}
};
struct BackToFrontSort {
bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) {
return (left._centerDepth > right._centerDepth);
}
};
void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->args;
// Allocate and simply copy
outItems.clear();
outItems.reserve(inItems.size());
// Make a local dataset of the center distance and closest point distance
std::vector<ItemBoundSort> itemBoundSorts;
itemBoundSorts.reserve(outItems.size());
for (auto itemDetails : inItems) {
auto item = scene->getItem(itemDetails.id);
auto bound = itemDetails.bound; // item.getBound();
float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter());
itemBoundSorts.emplace_back(ItemBoundSort(distance, distance, distance, itemDetails.id, bound));
}
// sort against Z
if (frontToBack) {
FrontToBackSort frontToBackSort;
std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), frontToBackSort);
} else {
BackToFrontSort backToFrontSort;
std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), backToFrontSort);
}
// FInally once sorted result to a list of itemID
for (auto& item : itemBoundSorts) {
outItems.emplace_back(ItemBound(item._id, item._bounds));
}
}
void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) {
depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems);
}
void FetchItems::configure(const Config& config) {
}
void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems) {
void FetchNonspatialItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
auto& scene = sceneContext->_scene;
outItems.clear();
const auto& bucket = scene->getMasterBucket();
const auto& items = bucket.find(_filter);
if (items != bucket.end()) {
outItems.reserve(items->second.size());
for (auto& id : items->second) {
auto& item = scene->getItem(id);
outItems.emplace_back(ItemBound(id, item.getBound()));
}
const auto& items = scene->getNonspatialSet();
outItems.reserve(items.size());
for (auto& id : items) {
auto& item = scene->getItem(id);
outItems.emplace_back(ItemBound(id, item.getBound()));
}
std::static_pointer_cast<Config>(renderContext->jobConfig)->numItems = (int)outItems.size();
}
void FetchSpatialTree::configure(const Config& config) {
_justFrozeFrustum = _justFrozeFrustum || (config.freezeFrustum && !_freezeFrustum);
_freezeFrustum = config.freezeFrustum;

View file

@ -21,52 +21,13 @@ namespace render {
void cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details,
const ItemBounds& inItems, ItemBounds& outItems);
void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems);
class FetchItemsConfig : public Job::Config {
Q_OBJECT
Q_PROPERTY(int numItems READ getNumItems)
class FetchNonspatialItems {
public:
int getNumItems() { return numItems; }
int numItems{ 0 };
};
class FetchItems {
public:
using Config = FetchItemsConfig;
using JobModel = Job::ModelO<FetchItems, ItemBounds, Config>;
FetchItems() {}
FetchItems(const ItemFilter& filter) : _filter(filter) {}
ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() };
void configure(const Config& config);
using JobModel = Job::ModelO<FetchNonspatialItems, ItemBounds>;
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems);
};
template<RenderDetails::Type T>
class CullItems {
public:
using JobModel = Job::ModelIO<CullItems<T>, ItemBounds, ItemBounds>;
CullItems(CullFunctor cullFunctor) : _cullFunctor{ cullFunctor } {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) {
const auto& args = renderContext->args;
auto& details = args->_details.edit(T);
outItems.clear();
outItems.reserve(inItems.size());
render::cullItems(renderContext, _cullFunctor, details, inItems, outItems);
}
protected:
CullFunctor _cullFunctor;
};
class FetchSpatialTreeConfig : public Job::Config {
Q_OBJECT
Q_PROPERTY(int numItems READ getNumItems)
@ -209,28 +170,19 @@ namespace render {
outItems[i].template edit<ItemBounds>().clear();
}
// For each item, filter it into the buckets
// For each item, filter it into one bucket
for (auto itemBound : inItems) {
auto& item = scene->getItem(itemBound.id);
auto itemKey = item.getKey();
for (size_t i = 0; i < NUM_FILTERS; i++) {
if (_filters[i].test(itemKey)) {
outItems[i].template edit<ItemBounds>().emplace_back(itemBound);
break;
}
}
}
}
};
class DepthSortItems {
public:
using JobModel = Job::ModelIO<DepthSortItems, ItemBounds, ItemBounds>;
bool _frontToBack;
DepthSortItems(bool frontToBack = true) : _frontToBack(frontToBack) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems);
};
}
#endif // hifi_render_CullTask_h;

View file

@ -73,38 +73,3 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext
args->_batch = nullptr;
});
}
void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ShapesIDsBounds& outShapes) {
auto& scene = sceneContext->_scene;
outShapes.clear();
for (const auto& item : inItems) {
auto key = scene->getItem(item.id).getShapeKey();
auto outItems = outShapes.find(key);
if (outItems == outShapes.end()) {
outItems = outShapes.insert(std::make_pair(key, ItemBounds{})).first;
outItems->second.reserve(inItems.size());
}
outItems->second.push_back(item);
}
for (auto& items : outShapes) {
items.second.shrink_to_fit();
}
}
void DepthSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapesIDsBounds& inShapes, ShapesIDsBounds& outShapes) {
outShapes.clear();
outShapes.reserve(inShapes.size());
for (auto& pipeline : inShapes) {
auto& inItems = pipeline.second;
auto outItems = outShapes.find(pipeline.first);
if (outItems == outShapes.end()) {
outItems = outShapes.insert(std::make_pair(pipeline.first, ItemBounds{})).first;
}
depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems->second);
}
}

View file

@ -13,10 +13,6 @@
#define hifi_render_DrawTask_h
#include "Engine.h"
#include "CullTask.h"
#include "ShapePipeline.h"
#include "gpu/Batch.h"
namespace render {
@ -31,22 +27,6 @@ public:
protected:
};
class PipelineSortShapes {
public:
using JobModel = Job::ModelIO<PipelineSortShapes, ItemBounds, ShapesIDsBounds>;
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ShapesIDsBounds& outShapes);
};
class DepthSortShapes {
public:
using JobModel = Job::ModelIO<DepthSortShapes, ShapesIDsBounds, ShapesIDsBounds>;
bool _frontToBack;
DepthSortShapes(bool frontToBack = true) : _frontToBack(frontToBack) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapesIDsBounds& inShapes, ShapesIDsBounds& outShapes);
};
}
#endif // hifi_render_DrawTask_h

View file

@ -63,6 +63,13 @@ void Item::PayloadInterface::addStatusGetters(const Status::Getters& getters) {
}
}
void Item::update(const UpdateFunctorPointer& updateFunctor) {
if (updateFunctor) {
_payload->update(updateFunctor);
}
_key = _payload->getKey();
}
void Item::resetPayload(const PayloadPointer& payload) {
if (!payload) {
kill();

View file

@ -112,6 +112,7 @@ public:
bool isPickable() const { return _flags[PICKABLE]; }
bool isLayered() const { return _flags[LAYERED]; }
bool isSpatial() const { return !isLayered(); }
// Probably not public, flags used by the scene
bool isSmall() const { return _flags[SMALLER]; }
@ -312,11 +313,11 @@ public:
Item() {}
~Item() {}
// Main scene / item managment interface Reset/Update/Kill
// Main scene / item managment interface reset/update/kill
void resetPayload(const PayloadPointer& payload);
void resetCell(ItemCell cell, bool _small) { _cell = cell; _key.setSmaller(_small); }
void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Communicate update to the payload
void kill() { _payload.reset(); _key._flags.reset(); _cell = INVALID_CELL; } // Kill means forget the payload and key and cell
void resetCell(ItemCell cell = INVALID_CELL, bool _small = false) { _cell = cell; _key.setSmaller(_small); }
void update(const UpdateFunctorPointer& updateFunctor); // communicate update to payload
void kill() { _payload.reset(); resetCell(); _key._flags.reset(); } // forget the payload, key, cell
// Check heuristic key
const ItemKey& getKey() const { return _key; }
@ -464,7 +465,7 @@ public:
using ItemBounds = std::vector<ItemBound>;
// A map of items by ShapeKey to optimize rendering pipeline assignments
using ShapesIDsBounds = std::unordered_map<ShapeKey, ItemBounds, ShapeKey::Hash, ShapeKey::KeyEqual>;
using ShapeBounds = std::unordered_map<ShapeKey, ItemBounds, ShapeKey::Hash, ShapeKey::KeyEqual>;
}

View file

@ -15,59 +15,11 @@
using namespace render;
void ItemBucketMap::insert(const ItemID& id, const ItemKey& key) {
// Insert the itemID in every bucket where it filters true
for (auto& bucket : (*this)) {
if (bucket.first.test(key)) {
bucket.second.insert(id);
}
}
}
void ItemBucketMap::erase(const ItemID& id, const ItemKey& key) {
// Remove the itemID in every bucket where it filters true
for (auto& bucket : (*this)) {
if (bucket.first.test(key)) {
bucket.second.erase(id);
}
}
}
void ItemBucketMap::reset(const ItemID& id, const ItemKey& oldKey, const ItemKey& newKey) {
// Reset the itemID in every bucket,
// Remove from the buckets where oldKey filters true AND newKey filters false
// Insert into the buckets where newKey filters true
for (auto& bucket : (*this)) {
if (bucket.first.test(oldKey)) {
if (!bucket.first.test(newKey)) {
bucket.second.erase(id);
}
} else if (bucket.first.test(newKey)) {
bucket.second.insert(id);
}
}
}
void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() {
(*this)[ItemFilter::Builder::opaqueShape().withoutLayered()];
(*this)[ItemFilter::Builder::transparentShape().withoutLayered()];
(*this)[ItemFilter::Builder::light()];
(*this)[ItemFilter::Builder::background()];
(*this)[ItemFilter::Builder::opaqueShape().withLayered()];
(*this)[ItemFilter::Builder::transparentShape().withLayered()];
}
void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) {
_resetItems.push_back(id);
_resetPayloads.push_back(payload);
}
void PendingChanges::resortItem(ItemID id, ItemKey oldKey, ItemKey newKey) {
_resortItems.push_back(id);
_resortOldKeys.push_back(oldKey);
_resortNewKeys.push_back(newKey);
}
void PendingChanges::removeItem(ItemID id) {
_removedItems.push_back(id);
}
@ -80,9 +32,6 @@ void PendingChanges::updateItem(ItemID id, const UpdateFunctorPointer& functor)
void PendingChanges::merge(PendingChanges& changes) {
_resetItems.insert(_resetItems.end(), changes._resetItems.begin(), changes._resetItems.end());
_resetPayloads.insert(_resetPayloads.end(), changes._resetPayloads.begin(), changes._resetPayloads.end());
_resortItems.insert(_resortItems.end(), changes._resortItems.begin(), changes._resortItems.end());
_resortOldKeys.insert(_resortOldKeys.end(), changes._resortOldKeys.begin(), changes._resortOldKeys.end());
_resortNewKeys.insert(_resortNewKeys.end(), changes._resortNewKeys.begin(), changes._resortNewKeys.end());
_removedItems.insert(_removedItems.end(), changes._removedItems.begin(), changes._removedItems.end());
_updatedItems.insert(_updatedItems.end(), changes._updatedItems.begin(), changes._updatedItems.end());
_updateFunctors.insert(_updateFunctors.end(), changes._updateFunctors.begin(), changes._updateFunctors.end());
@ -92,7 +41,6 @@ Scene::Scene(glm::vec3 origin, float size) :
_masterSpatialTree(origin, size)
{
_items.push_back(Item()); // add the itemID #0 to nothing
_masterBucketMap.allocateStandardOpaqueTranparentBuckets();
}
ItemID Scene::allocateID() {
@ -133,7 +81,6 @@ void Scene::processPendingChangesQueue() {
// capture anything coming from the pendingChanges
resetItems(consolidatedPendingChanges._resetItems, consolidatedPendingChanges._resetPayloads);
updateItems(consolidatedPendingChanges._updatedItems, consolidatedPendingChanges._updateFunctors);
resortItems(consolidatedPendingChanges._resortItems, consolidatedPendingChanges._resortOldKeys, consolidatedPendingChanges._resortNewKeys);
removeItems(consolidatedPendingChanges._removedItems);
// ready to go back to rendering activities
@ -141,7 +88,6 @@ void Scene::processPendingChangesQueue() {
}
void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) {
auto resetPayload = payloads.begin();
for (auto resetID : ids) {
// Access the true item
@ -153,28 +99,20 @@ void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) {
item.resetPayload(*resetPayload);
auto newKey = item.getKey();
// Reset the item in the Bucket map
_masterBucketMap.reset(resetID, oldKey, newKey);
// Reset the item in the spatial tree
auto newCell = _masterSpatialTree.resetItem(oldCell, oldKey, item.getBound(), resetID, newKey);
item.resetCell(newCell, newKey.isSmall());
// Update the item's container
assert((oldKey.isSpatial() == newKey.isSpatial()) || oldKey._flags.none());
if (newKey.isSpatial()) {
auto newCell = _masterSpatialTree.resetItem(oldCell, oldKey, item.getBound(), resetID, newKey);
item.resetCell(newCell, newKey.isSmall());
} else {
_masterNonspatialSet.insert(resetID);
}
// next loop
resetPayload++;
}
}
void Scene::resortItems(const ItemIDs& ids, ItemKeys& oldKeys, ItemKeys& newKeys) {
auto resortID = ids.begin();
auto oldKey = oldKeys.begin();
auto newKey = newKeys.begin();
for (; resortID != ids.end(); resortID++, oldKey++, newKey++) {
_masterBucketMap.reset(*resortID, *oldKey, *newKey);
}
}
void Scene::removeItems(const ItemIDs& ids) {
for (auto removedID :ids) {
// Access the true item
@ -182,11 +120,12 @@ void Scene::removeItems(const ItemIDs& ids) {
auto oldCell = item.getCell();
auto oldKey = item.getKey();
// Remove from Bucket map
_masterBucketMap.erase(removedID, item.getKey());
// Remove from spatial tree
_masterSpatialTree.removeItem(oldCell, oldKey, removedID);
// Remove the item
if (oldKey.isSpatial()) {
_masterSpatialTree.removeItem(oldCell, oldKey, removedID);
} else {
_masterNonspatialSet.erase(removedID);
}
// Kill it
item.kill();
@ -202,14 +141,30 @@ void Scene::updateItems(const ItemIDs& ids, UpdateFunctors& functors) {
auto oldCell = item.getCell();
auto oldKey = item.getKey();
// Update it
_items[updateID].update((*updateFunctor));
// Update the item
item.update((*updateFunctor));
auto newKey = item.getKey();
// Update the citem in the spatial tree if needed
auto newCell = _masterSpatialTree.resetItem(oldCell, oldKey, item.getBound(), updateID, newKey);
item.resetCell(newCell, newKey.isSmall());
// Update the item's container
if (oldKey.isSpatial() == newKey.isSpatial()) {
if (newKey.isSpatial()) {
auto newCell = _masterSpatialTree.resetItem(oldCell, oldKey, item.getBound(), updateID, newKey);
item.resetCell(newCell, newKey.isSmall());
}
} else {
if (newKey.isSpatial()) {
_masterNonspatialSet.erase(updateID);
auto newCell = _masterSpatialTree.resetItem(oldCell, oldKey, item.getBound(), updateID, newKey);
item.resetCell(newCell, newKey.isSmall());
} else {
_masterSpatialTree.removeItem(oldCell, oldKey, updateID);
item.resetCell();
_masterNonspatialSet.insert(updateID);
}
}
// next loop
updateFunctor++;

View file

@ -17,21 +17,6 @@
namespace render {
// A map of ItemIDSets allowing to create bucket lists of items which are filtered according to their key
class ItemBucketMap : public std::map<ItemFilter, ItemIDSet, ItemFilter::Less> {
public:
ItemBucketMap() {}
void insert(const ItemID& id, const ItemKey& key);
void erase(const ItemID& id, const ItemKey& key);
void reset(const ItemID& id, const ItemKey& oldKey, const ItemKey& newKey);
// standard builders allocating the main buckets
void allocateStandardOpaqueTranparentBuckets();
};
class Engine;
class PendingChanges {
@ -40,7 +25,6 @@ public:
~PendingChanges() {}
void resetItem(ItemID id, const PayloadPointer& payload);
void resortItem(ItemID id, ItemKey oldKey, ItemKey newKey);
void removeItem(ItemID id);
template <class T> void updateItem(ItemID id, std::function<void(T&)> func) {
@ -48,14 +32,12 @@ public:
}
void updateItem(ItemID id, const UpdateFunctorPointer& functor);
void updateItem(ItemID id) { updateItem(id, nullptr); }
void merge(PendingChanges& changes);
ItemIDs _resetItems;
Payloads _resetPayloads;
ItemIDs _resortItems;
ItemKeys _resortOldKeys;
ItemKeys _resortNewKeys;
ItemIDs _removedItems;
ItemIDs _updatedItems;
UpdateFunctors _updateFunctors;
@ -75,27 +57,27 @@ public:
Scene(glm::vec3 origin, float size);
~Scene() {}
/// This call is thread safe, can be called from anywhere to allocate a new ID
// This call is thread safe, can be called from anywhere to allocate a new ID
ItemID allocateID();
/// Enqueue change batch to the scene
// Enqueue change batch to the scene
void enqueuePendingChanges(const PendingChanges& pendingChanges);
// Process the penging changes equeued
void processPendingChangesQueue();
/// Access a particular item form its ID
/// WARNING, There is No check on the validity of the ID, so this could return a bad Item
// Access a particular item form its ID
// WARNING, There is No check on the validity of the ID, so this could return a bad Item
const Item& getItem(const ItemID& id) const { return _items[id]; }
size_t getNumItems() const { return _items.size(); }
/// Access the main bucketmap of items
const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; }
/// Access the item spatial tree
// Access the spatialized items
const ItemSpatialTree& getSpatialTree() const { return _masterSpatialTree; }
// Access non-spatialized items (overlays, backgrounds)
const ItemIDSet& getNonspatialSet() const { return _masterNonspatialSet; }
protected:
// Thread safe elements that can be accessed from anywhere
std::atomic<unsigned int> _IDAllocator{ 1 }; // first valid itemID will be One
@ -106,12 +88,10 @@ protected:
// database of items is protected for editing by a mutex
std::mutex _itemsMutex;
Item::Vector _items;
ItemBucketMap _masterBucketMap;
ItemSpatialTree _masterSpatialTree;
ItemIDSet _masterNonspatialSet;
void resetItems(const ItemIDs& ids, Payloads& payloads);
void resortItems(const ItemIDs& ids, ItemKeys& oldKeys, ItemKeys& newKeys);
void removeItems(const ItemIDs& ids);
void updateItems(const ItemIDs& ids, UpdateFunctors& functors);

View file

@ -0,0 +1,120 @@
//
// CullTask.cpp
// render/src/render
//
// Created by Sam Gateau on 2/2/16.
// 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 "SortTask.h"
#include "ShapePipeline.h"
#include <assert.h>
#include <ViewFrustum.h>
using namespace render;
struct ItemBoundSort {
float _centerDepth = 0.0f;
float _nearDepth = 0.0f;
float _farDepth = 0.0f;
ItemID _id = 0;
AABox _bounds;
ItemBoundSort() {}
ItemBoundSort(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {}
};
struct FrontToBackSort {
bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) {
return (left._centerDepth < right._centerDepth);
}
};
struct BackToFrontSort {
bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) {
return (left._centerDepth > right._centerDepth);
}
};
void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->args;
// Allocate and simply copy
outItems.clear();
outItems.reserve(inItems.size());
// Make a local dataset of the center distance and closest point distance
std::vector<ItemBoundSort> itemBoundSorts;
itemBoundSorts.reserve(outItems.size());
for (auto itemDetails : inItems) {
auto item = scene->getItem(itemDetails.id);
auto bound = itemDetails.bound; // item.getBound();
float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter());
itemBoundSorts.emplace_back(ItemBoundSort(distance, distance, distance, itemDetails.id, bound));
}
// sort against Z
if (frontToBack) {
FrontToBackSort frontToBackSort;
std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), frontToBackSort);
} else {
BackToFrontSort backToFrontSort;
std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), backToFrontSort);
}
// Finally once sorted result to a list of itemID
for (auto& item : itemBoundSorts) {
outItems.emplace_back(ItemBound(item._id, item._bounds));
}
}
void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ShapeBounds& outShapes) {
auto& scene = sceneContext->_scene;
outShapes.clear();
for (const auto& item : inItems) {
auto key = scene->getItem(item.id).getShapeKey();
auto outItems = outShapes.find(key);
if (outItems == outShapes.end()) {
outItems = outShapes.insert(std::make_pair(key, ItemBounds{})).first;
outItems->second.reserve(inItems.size());
}
outItems->second.push_back(item);
}
for (auto& items : outShapes) {
items.second.shrink_to_fit();
}
}
void DepthSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapeBounds& inShapes, ShapeBounds& outShapes) {
outShapes.clear();
outShapes.reserve(inShapes.size());
for (auto& pipeline : inShapes) {
auto& inItems = pipeline.second;
auto outItems = outShapes.find(pipeline.first);
if (outItems == outShapes.end()) {
outItems = outShapes.insert(std::make_pair(pipeline.first, ItemBounds{})).first;
}
depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems->second);
}
}
void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) {
depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems);
}

View file

@ -0,0 +1,47 @@
//
// SortTask.h
// render/src/render
//
// Created by Zach Pomerantz on 2/26/2016.
// 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
//
#ifndef hifi_render_SortTask_h
#define hifi_render_SortTask_h
#include "Engine.h"
namespace render {
void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems);
class PipelineSortShapes {
public:
using JobModel = Job::ModelIO<PipelineSortShapes, ItemBounds, ShapeBounds>;
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ShapeBounds& outShapes);
};
class DepthSortShapes {
public:
using JobModel = Job::ModelIO<DepthSortShapes, ShapeBounds, ShapeBounds>;
bool _frontToBack;
DepthSortShapes(bool frontToBack = true) : _frontToBack(frontToBack) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapeBounds& inShapes, ShapeBounds& outShapes);
};
class DepthSortItems {
public:
using JobModel = Job::ModelIO<DepthSortItems, ItemBounds, ItemBounds>;
bool _frontToBack;
DepthSortItems(bool frontToBack = true) : _frontToBack(frontToBack) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems);
};
}
#endif // hifi_render_SortTask_h;