Merge pull request #4996 from samcake/punk

TEAM TEACHING: Culling, LOD and Depth sort added to DrawSceneTask
This commit is contained in:
Brad Hefta-Gaub 2015-05-29 15:06:17 -07:00
commit c71519bfb1
6 changed files with 330 additions and 29 deletions

View file

@ -3421,18 +3421,23 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
pendingChanges.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload);
}
{
PerformanceTimer perfTimer("SceneProcessPendingChanges");
_main3DScene->enqueuePendingChanges(pendingChanges);
_main3DScene->enqueuePendingChanges(pendingChanges);
_main3DScene->processPendingChangesQueue();
_main3DScene->processPendingChangesQueue();
}
// FOr now every frame pass the renderCOntext
render::RenderContext renderContext;
renderContext.args = renderArgs;
_renderEngine->setRenderContext(renderContext);
{
PerformanceTimer perfTimer("EngineRun");
render::RenderContext renderContext;
renderContext.args = renderArgs;
_renderEngine->setRenderContext(renderContext);
// Before the deferred pass, let's try to use the render engine
_renderEngine->run();
// Before the deferred pass, let's try to use the render engine
_renderEngine->run();
}
{
DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight());

View file

@ -14,6 +14,11 @@
namespace render {
template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload) {
if (payload && payload->entity) {
if (payload->entity->getType() == EntityTypes::Light) {
return ItemKey::Builder::light();
}
}
return ItemKey::Builder::opaqueShape();
}

View file

@ -16,9 +16,20 @@
#include "gpu/Batch.h"
#include "gpu/Context.h"
#include "ViewFrustum.h"
#include "RenderArgs.h"
#include <algorithm>
#include <assert.h>
using namespace render;
DrawSceneTask::DrawSceneTask() : Task() {
_jobs.push_back(Job(DrawOpaque()));
_jobs.push_back(Job(DrawLight()));
_jobs.push_back(Job(DrawTransparent()));
}
DrawSceneTask::~DrawSceneTask() {
}
@ -29,25 +40,239 @@ void DrawSceneTask::run(const SceneContextPointer& sceneContext, const RenderCon
if (!sceneContext->_scene) {
return;
}
auto& scene = sceneContext->_scene;
auto& itemBucketMap = scene->getMasterBucket();
// Is it possible that we render without a viewFrustum ?
if (!(renderContext->args && renderContext->args->_viewFrustum)) {
return;
}
for (auto job : _jobs) {
job.run(sceneContext, renderContext);
}
};
Job::~Job() {
}
template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render opaques
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape());
ItemIDs inItems;
inItems.reserve(items.size());
for (auto id : items) {
inItems.push_back(id);
}
ItemIDs culledItems;
cullItems(sceneContext, renderContext, inItems, culledItems);
ItemIDs sortedItems;
depthSortItems(sceneContext, renderContext, true, culledItems, sortedItems); // Sort Front to back opaque items!
renderItems(sceneContext, renderContext, sortedItems);
}
template <> void render::jobRun(const DrawTransparent& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render transparents
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape());
ItemIDs inItems;
inItems.reserve(items.size());
for (auto id : items) {
inItems.push_back(id);
}
ItemIDs culledItems;
cullItems(sceneContext, renderContext, inItems, culledItems);
ItemIDs sortedItems;
depthSortItems(sceneContext, renderContext, false, culledItems, sortedItems); // Sort Back to front transparent items!
renderItems(sceneContext, renderContext, sortedItems);
}
template <> void render::jobRun(const DrawLight& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render lights
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::light());
ItemIDs inItems;
inItems.reserve(items.size());
for (auto id : items) {
inItems.push_back(id);
}
ItemIDs culledItems;
cullItems(sceneContext, renderContext, inItems, culledItems);
renderItems(sceneContext, renderContext, culledItems);
}
/*
bool LODManager::shouldRenderMesh(float largestDimension, float distanceToCamera) {
const float octreeToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it.
float octreeSizeScale = getOctreeSizeScale();
int boundaryLevelAdjust = getBoundaryLevelAdjust();
float maxScale = (float)TREE_SCALE;
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / octreeToMeshRatio;
if (_shouldRenderTableNeedsRebuilding) {
_shouldRenderTable.clear();
float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small
float scale = maxScale;
float visibleDistanceAtScale = visibleDistanceAtMaxScale;
while (scale > SMALLEST_SCALE_IN_TABLE) {
scale /= 2.0f;
visibleDistanceAtScale /= 2.0f;
_shouldRenderTable[scale] = visibleDistanceAtScale;
}
_shouldRenderTableNeedsRebuilding = false;
}
float closestScale = maxScale;
float visibleDistanceAtClosestScale = visibleDistanceAtMaxScale;
QMap<float, float>::const_iterator lowerBound = _shouldRenderTable.lowerBound(largestDimension);
if (lowerBound != _shouldRenderTable.constEnd()) {
closestScale = lowerBound.key();
visibleDistanceAtClosestScale = lowerBound.value();
}
if (closestScale < largestDimension) {
visibleDistanceAtClosestScale *= 2.0f;
}
return (distanceToCamera <= visibleDistanceAtClosestScale);
}*/
void render::cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDs& inItems, ItemIDs& outItems) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->args;
// Culling / LOD
for (auto id : inItems) {
auto item = scene->getItem(id);
auto bound = item.getBound();
if (bound.isNull()) {
outItems.push_back(id); // One more Item to render
args->_itemsRendered++;
continue;
}
// TODO: some entity types (like lights) might want to be rendered even
// when they are outside of the view frustum...
float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter());
bool outOfView = args->_viewFrustum->boxInFrustum(bound) == ViewFrustum::OUTSIDE;
if (!outOfView) {
bool bigEnoughToRender = true; //_viewState->shouldRenderMesh(bound.getLargestDimension(), distance);
if (bigEnoughToRender) {
outItems.push_back(id); // One more Item to render
args->_itemsRendered++;
} else {
args->_itemsTooSmall++;
}
} else {
args->_itemsOutOfView++;
}
}
}
struct ItemBound {
float _centerDepth = 0.0f;
float _nearDepth = 0.0f;
float _farDepth = 0.0f;
ItemID _id = 0;
ItemBound() {}
ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id) {}
};
struct FrontToBackSort {
bool operator() (const ItemBound& left, const ItemBound& right) {
return (left._centerDepth < right._centerDepth);
}
};
struct BackToFrontSort {
bool operator() (const ItemBound& left, const ItemBound& right) {
return (left._centerDepth > right._centerDepth);
}
};
void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDs& inItems, ItemIDs& outItems) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->args;
// Allocate and simply copy
outItems.reserve(inItems.size());
// Make a local dataset of the center distance and closest point distance
std::vector<ItemBound> itemBounds;
itemBounds.reserve(outItems.size());
for (auto id : inItems) {
auto item = scene->getItem(id);
auto bound = item.getBound();
float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter());
itemBounds.emplace_back(ItemBound(distance, distance, distance, id));
}
// sort against Z
if (frontToBack) {
FrontToBackSort frontToBackSort;
std::sort (itemBounds.begin(), itemBounds.end(), frontToBackSort);
} else {
BackToFrontSort backToFrontSort;
std::sort (itemBounds.begin(), itemBounds.end(), backToFrontSort);
}
// FInally once sorted result to a list of itemID
for (auto& itemBound : itemBounds) {
outItems.emplace_back(itemBound._id);
}
}
void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDs& inItems) {
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->args;
gpu::Batch theBatch;
args->_batch = &theBatch;
// render opaques
auto filter = ItemFilter::Builder::opaqueShape();
auto& opaqueShapeItems = itemBucketMap.at(filter);
for (auto id : opaqueShapeItems) {
// render
for (auto id : inItems) {
auto item = scene->getItem(id);
item.render(args);
}
args->_context->enqueueBatch((*args->_batch));
args->_batch = nullptr;
};
}

View file

@ -16,19 +16,81 @@
namespace render {
template <class T> void jobRun(const T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { }
class DrawSceneTask : public Task {
class Job {
public:
DrawSceneTask() : Task() {}
~DrawSceneTask();
template <class T>
Job(T data) : _concept(new Model<T>(data)) {}
Job(const Job& other) : _concept(other._concept) {}
~Job();
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
if (_concept) {
_concept->run(sceneContext, renderContext);
}
}
protected:
class Concept {
public:
virtual ~Concept() = default;
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0;
};
template <class T> class Model : public Concept {
public:
typedef T Data;
Data _data;
Model(Data data): _data(data) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { jobRun(_data, sceneContext, renderContext); }
};
std::shared_ptr<Concept> _concept;
};
typedef std::vector<Job> Jobs;
void cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDs& inItems, ItemIDs& outITems);
void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDs& inItems, ItemIDs& outITems);
void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDs& inItems);
class DrawOpaque {
public:
};
template <> void jobRun(const DrawOpaque& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
class DrawTransparent {
public:
};
template <> void jobRun(const DrawTransparent& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
class DrawLight {
public:
};
template <> void jobRun(const DrawLight& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
class DrawSceneTask : public Task {
public:
DrawSceneTask();
~DrawSceneTask();
Jobs _jobs;
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
};
}
#endif // hifi_render_Task_h

View file

@ -47,6 +47,8 @@ void ItemBucketMap::reset(const ItemID& id, const ItemKey& oldKey, const ItemKey
void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() {
(*this)[ItemFilter::Builder::opaqueShape()];
(*this)[ItemFilter::Builder::transparentShape()];
auto lightFilter = ItemFilter::Builder().withTypeLight().build();
(*this)[lightFilter];
}

View file

@ -53,7 +53,7 @@ public:
ItemKey(const Flags& flags) : _flags(flags) {}
class Builder {
Flags _flags;
Flags _flags{ 0 };
public:
Builder() {}
@ -72,6 +72,7 @@ public:
// Convenient standard keys that we will keep on using all over the place
static ItemKey opaqueShape() { return Builder().withTypeShape().build(); }
static ItemKey transparentShape() { return Builder().withTypeShape().withTransparent().build(); }
static ItemKey light() { return Builder().withTypeLight().build(); }
};
bool isOpaque() const { return !_flags[TRANSLUCENT]; }
@ -111,8 +112,8 @@ public:
ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {}
class Builder {
ItemKey::Flags _value;
ItemKey::Flags _mask;
ItemKey::Flags _value{ 0 };
ItemKey::Flags _mask{ 0 };
public:
Builder() {}
@ -142,8 +143,9 @@ public:
Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); }
// Convenient standard keys that we will keep on using all over the place
static ItemFilter opaqueShape() { return Builder().withTypeShape().withOpaque().build(); }
static ItemFilter transparentShape() { return Builder().withTypeShape().withTransparent().build(); }
static ItemFilter opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace().build(); }
static ItemFilter transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace().build(); }
static ItemFilter light() { return Builder().withTypeLight().build(); }
};
// Item Filter operator testing if a key pass the filter
@ -152,10 +154,10 @@ public:
class Less {
public:
bool operator() (const ItemFilter& left, const ItemFilter& right) const {
if (left._value.to_ulong() >= right._value.to_ulong()) {
if (left._value.to_ulong() == right._value.to_ulong()) {
return left._mask.to_ulong() < right._mask.to_ulong();
} else {
return true;
return left._value.to_ulong() < right._value.to_ulong();
}
}
};