From 40e49b2376a90cf7184588cb7e09fb98d071e103 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 8 Feb 2016 18:23:29 -0800 Subject: [PATCH] Starting to remove empty bricks and empty cells --- .../utilities/tools/debugRenderCulling.js | 18 ++++ .../render/src/render/DrawSceneOctree.cpp | 19 +++- libraries/render/src/render/DrawSceneOctree.h | 10 ++ libraries/render/src/render/Octree.cpp | 95 +++++++++++++++++-- libraries/render/src/render/Octree.h | 50 +++++++--- .../render/src/render/drawItemBounds.slf | 8 +- .../render/src/render/drawItemBounds.slv | 4 +- 7 files changed, 179 insertions(+), 25 deletions(-) diff --git a/examples/utilities/tools/debugRenderCulling.js b/examples/utilities/tools/debugRenderCulling.js index 4579f434d0..3d260fc95c 100644 --- a/examples/utilities/tools/debugRenderCulling.js +++ b/examples/utilities/tools/debugRenderCulling.js @@ -55,6 +55,24 @@ panel.newCheckbox("Show Partial Subcell Items", function(value) { return (value); } ); +/* +panel.newSlider('Cells Free / Allocated', -1, 1, + function(value) { value; }, // setter + function() { return Render.RenderDeferredTask.DrawSceneOctree.numFreeCells; }, // getter + function(value) { return value; }); + +this.update = function () { + var numFree = Render.RenderDeferredTask.DrawSceneOctree.numFreeCells; + var numAlloc = Render.RenderDeferredTask.DrawSceneOctree.numAllocatedCells; + var title = [ + ' ' + name, + numFree + ' / ' + numAlloc + ].join('\t'); + + widget.editTitle({ text: title }); + slider.setMaxValue(numAlloc); +}; +*/ function mouseMoveEvent(event) { panel.mouseMoveEvent(event); } diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index f60b20a69f..c62a77288e 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -93,6 +93,10 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext, RenderArgs* args = renderContext->args; auto& scene = sceneContext->_scene; + std::static_pointer_cast(renderContext->jobConfig)->numAllocatedCells = (int)scene->getSpatialTree().getNumAllocatedCells(); + std::static_pointer_cast(renderContext->jobConfig)->numFreeCells = (int)scene->getSpatialTree().getNumFreeCells(); + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { glm::mat4 projMat; Transform viewMat; @@ -171,6 +175,8 @@ const gpu::PipelinePointer DrawItemSelection::getDrawItemBoundPipeline() { _drawItemBoundPosLoc = program->getUniforms().findLocation("inBoundPos"); _drawItemBoundDimLoc = program->getUniforms().findLocation("inBoundDim"); + _drawCellLocationLoc = program->getUniforms().findLocation("inCellLocation"); + auto state = std::make_shared(); state->setDepthTest(true, false, gpu::LESS_EQUAL); @@ -217,7 +223,10 @@ void DrawItemSelection::run(const SceneContextPointer& sceneContext, for (const auto& itemID : inSelection.insideItems) { auto& item = scene->getItem(itemID); auto itemBound = item.getBound(); + auto itemCell = scene->getSpatialTree().getCellLocation(item.getCell()); + glm::ivec4 cellLocation(0, 0, 0, itemCell.depth); + batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner())); batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale())); @@ -229,7 +238,10 @@ void DrawItemSelection::run(const SceneContextPointer& sceneContext, for (const auto& itemID : inSelection.insideSubcellItems) { auto& item = scene->getItem(itemID); auto itemBound = item.getBound(); + auto itemCell = scene->getSpatialTree().getCellLocation(item.getCell()); + glm::ivec4 cellLocation(0, 0, 1, itemCell.depth); + batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner())); batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale())); @@ -241,7 +253,10 @@ void DrawItemSelection::run(const SceneContextPointer& sceneContext, for (const auto& itemID : inSelection.partialItems) { auto& item = scene->getItem(itemID); auto itemBound = item.getBound(); + auto itemCell = scene->getSpatialTree().getCellLocation(item.getCell()); + glm::ivec4 cellLocation(0, 0, 0, itemCell.depth); + batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner())); batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale())); @@ -253,7 +268,9 @@ void DrawItemSelection::run(const SceneContextPointer& sceneContext, for (const auto& itemID : inSelection.partialSubcellItems) { auto& item = scene->getItem(itemID); auto itemBound = item.getBound(); - + auto itemCell = scene->getSpatialTree().getCellLocation(item.getCell()); + glm::ivec4 cellLocation(0, 0, 1, itemCell.depth); + batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation))); batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*)(&itemBound.getCorner())); batch._glUniform3fv(_drawItemBoundDimLoc, 1, (const float*)(&itemBound.getScale())); diff --git a/libraries/render/src/render/DrawSceneOctree.h b/libraries/render/src/render/DrawSceneOctree.h index 7301a776f3..63e7203b38 100644 --- a/libraries/render/src/render/DrawSceneOctree.h +++ b/libraries/render/src/render/DrawSceneOctree.h @@ -23,6 +23,9 @@ namespace render { Q_PROPERTY(bool showVisibleCells MEMBER showVisibleCells WRITE setShowVisibleCells) Q_PROPERTY(bool showEmptyCells MEMBER showEmptyCells WRITE setShowEmptyCells) Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum) + Q_PROPERTY(int numAllocatedCells READ getNumAllocatedCells) + Q_PROPERTY(int numFreeCells READ getNumFreeCells) + public: DrawSceneOctreeConfig() : Job::Config(false) {} @@ -31,6 +34,12 @@ namespace render { bool showEmptyCells{ false }; bool freezeFrustum{ false }; + int numAllocatedCells{ 0 }; + int numFreeCells{ 0 }; + + int getNumAllocatedCells() const { return numAllocatedCells; } + int getNumFreeCells() const { return numFreeCells; } + public slots: void setShowVisibleCells(bool show) { showVisibleCells = show; emit dirty(); } void setShowEmptyCells(bool show) { showEmptyCells = show; emit dirty(); } @@ -103,6 +112,7 @@ namespace render { int _drawItemBoundPosLoc = -1; int _drawItemBoundDimLoc = -1; + int _drawCellLocationLoc = -1; gpu::PipelinePointer _drawItemBoundPipeline; bool _showInsideItems; // initialized by Config diff --git a/libraries/render/src/render/Octree.cpp b/libraries/render/src/render/Octree.cpp index f96f691f38..ef2f10e983 100644 --- a/libraries/render/src/render/Octree.cpp +++ b/libraries/render/src/render/Octree.cpp @@ -109,21 +109,70 @@ Octree::Indices Octree::indexConcreteCellPath(const Locations& path) const { Octree::Index Octree::allocateCell(Index parent, const Location& location) { - if (_cells[parent].asChild(location.octant())) { + if (_cells[parent].hasChild(location.octant())) { assert(_cells[parent].child(location.octant()) == INVALID); return _cells[parent].child(location.octant()); } assert(_cells[parent].getlocation().child(location.octant()) == location); - auto newIndex = (Index) _cells.size(); - _cells.push_back(Cell(parent, location)); - _cells[parent].setChild(location.octant(), newIndex); + Index newIndex; + if (_freeCells.empty()) { + newIndex = (Index)_cells.size(); + _cells.push_back(Cell(parent, location)); + } else { + newIndex = _freeCells.back(); + _freeCells.pop_back(); + _cells[newIndex] = Cell(parent, location); + } + _cells[parent].setChild(location.octant(), newIndex); return newIndex; } +void Octree::freeCell(Index index) { + if (checkCellIndex(index)) { + auto & cell = _cells[index]; + cell.free(); + _freeCells.push_back(index); + } +} + +void Octree::clearCell(Index index) { + auto& cell = editCell(index); + + // Free the brick + if (cell.hasBrick()) { + freeBrick(cell.brick()); + cell.setBrick(INVALID); + } + + // Free the cell ? + Index parentIdx = cell.parent(); + if (!cell.hasParent()) { + if (index == ROOT_CELL) { + // Stop here, this is the root cell! + return; + } else { + // THis is not expected + assert(false); + return; + } + } + + bool traverseParent = false; + if (!cell.hasChildren()) { + editCell(parentIdx).setChild(cell.getlocation().octant(), INVALID); + freeCell(index); + traverseParent = true; + } + + if (traverseParent) { + clearCell(parentIdx); + } +} + Octree::Indices Octree::indexCellPath(const Locations& path) { // First through the allocated cells Indices cellPath = indexConcreteCellPath(path); @@ -147,13 +196,27 @@ Octree::Indices Octree::indexCellPath(const Locations& path) { Octree::Index Octree::allocateBrick() { - Index brickIdx = (int) _bricks.size(); - _bricks.push_back(Brick()); - return brickIdx; + if (_freeBricks.empty()) { + Index brickIdx = (int)_bricks.size(); + _bricks.push_back(Brick()); + return brickIdx; + } else { + Index brickIdx = _freeBricks.back(); + _freeBricks.pop_back(); + return brickIdx; + } +} + +void Octree::freeBrick(Index index) { + if (checkCellIndex(index)) { + auto & brick = _bricks[index]; + // brick.free(); + _freeBricks.push_back(index); + } } Octree::Index Octree::accessCellBrick(Index cellID, const CellBrickAccessor& accessor, bool createBrick) { - assert(cellID != INVALID); + assert(checkCellIndex(cellID)); auto& cell = editCell(cellID); if (!cell.hasBrick()) { if (!createBrick) { @@ -220,6 +283,7 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID auto success = false; // Remove the item from the brick + bool emptyCell = false; accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index brickID) { auto& itemList = (key.isSmall() ? brick.subcellItems : brick.items); @@ -227,10 +291,16 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID if (brick.items.empty() && brick.subcellItems.empty()) { cell.signalBrickEmpty(); + emptyCell = true; } success = true; }, false); // do not create brick! + + if (emptyCell) { + clearCell(cellIdx); + } + return success; } @@ -245,7 +315,11 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& auto rangeSizef = maxCoordf - minCoordf; float cellFitSize = getCellHalfDiagonalSquare(location.depth); bool subcellItem = glm::dot(rangeSizef, rangeSizef) < cellFitSize; - newKey.setSmaller(subcellItem); + if (subcellItem) { + newKey.setSmaller(subcellItem); + } else { + newKey.setSmaller(false); + } auto newCell = indexCell(location); @@ -461,3 +535,6 @@ int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& return (int) selection.numItems(); } + + + diff --git a/libraries/render/src/render/Octree.h b/libraries/render/src/render/Octree.h index c22313afe3..7602706ff3 100644 --- a/libraries/render/src/render/Octree.h +++ b/libraries/render/src/render/Octree.h @@ -172,13 +172,23 @@ namespace render { // the cell description class Cell { public: + void free() { _location = Location(); for (auto& link : _links) { link = INVALID; } } + const Location& getlocation() const { return _location; } Index parent() const { return _links[Parent]; } - bool asParent() const { return parent() != INVALID; } + bool hasParent() const { return parent() != INVALID; } Index child(Link octant) const { return _links[octant]; } - bool asChild(Link octant) const { return child(octant) != INVALID; } + bool hasChild(Link octant) const { return child(octant) != INVALID; } + bool hasChildren() const { + for (LinkStorage octant = Octant_L_B_N; octant < NUM_OCTANTS; octant++) { + if (hasChild((Link)octant)) { + return true; + } + } + return false; + } void setChild(Link octant, Index child) { _links[octant] = child; } Index brick() const { return _links[BrickLink]; } @@ -205,13 +215,17 @@ namespace render { using Bricks = std::vector< Brick >; - - // Octree members - Cells _cells = Cells(1, Cell()); // start with only the Cell root - Bricks _bricks; + bool checkCellIndex(Index index) const { return (index >= 0) && (index < _cells.size()); } + bool checkBrickIndex(Index index) const { return (index >= 0) && (index < _bricks.size()); } Octree() {}; + // Clear a cell: + // Check that the cell brick is empty, if so free it + // CHeck that the cell has no children, if so free itself + // Apply the same logic to the parent cell + void clearCell(Index index); + // Indexing/Allocating the cells as the tree gets populated // Return the cell Index/Indices at the specified location/path, allocate all the cells on the path from the root if needed Indices indexCellPath(const Locations& path); @@ -223,7 +237,7 @@ namespace render { // Get the cell location from the CellID Location getCellLocation(Index cellID) const { - if ((cellID >= 0) && (cellID < _cells.size())) { + if (checkCellIndex(cellID)) { return getConcreteCell(cellID).getlocation(); } return Location(); @@ -231,12 +245,10 @@ namespace render { // Reach a concrete cell const Cell& getConcreteCell(Index index) const { - assert(index < _cells.size()); + assert(checkCellIndex(index)); return _cells[index]; } - - // Let s talk about the Cell Bricks now using CellBrickAccessor = std::function; @@ -244,9 +256,8 @@ namespace render { // This returns the Brick index Index accessCellBrick(Index cellID, const CellBrickAccessor& accessor, bool createBrick = true); - const Brick& getConcreteBrick(Index index) const { - assert(index < _bricks.size()); + assert(checkBrickIndex(index)); return _bricks[index]; } @@ -297,15 +308,28 @@ namespace render { int selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const; int selectCellBrick(Index cellID, CellSelection& selection, bool inside) const; + + int getNumAllocatedCells() const { return (int)_cells.size(); } + int getNumFreeCells() const { return (int)_freeCells.size(); } + protected: Index allocateCell(Index parent, const Location& location); + void freeCell(Index index); + Index allocateBrick(); + void freeBrick(Index index); Cell& editCell(Index index) { - assert(index < _cells.size()); + assert(checkCellIndex(index)); return _cells[index]; } + + // Octree members + Cells _cells = Cells(1, Cell()); // start with only the Cell root + Bricks _bricks; + Indices _freeCells; // stack of free cells to be reused for allocation + Indices _freeBricks; // stack of free bricks to be reused for allocation }; } diff --git a/libraries/render/src/render/drawItemBounds.slf b/libraries/render/src/render/drawItemBounds.slf index 6f5db0a6c2..4fb23df8f6 100644 --- a/libraries/render/src/render/drawItemBounds.slf +++ b/libraries/render/src/render/drawItemBounds.slf @@ -19,5 +19,11 @@ out vec4 outFragColor; void main(void) { float var = step(fract(varTexcoord.x * varTexcoord.y * 1.0), 0.5); - outFragColor = vec4(mix(vec3(1.0), varColor.xyz, var), varColor.a); + if (varColor.a == 0) { + outFragColor = vec4(mix(vec3(0.0), varColor.xyz, var), mix(0.0, 1.0, var)); + + } else { + outFragColor = vec4(mix(vec3(1.0), varColor.xyz, var), varColor.a); + } + } diff --git a/libraries/render/src/render/drawItemBounds.slv b/libraries/render/src/render/drawItemBounds.slv index 08c5fc7ed4..cf1eab7163 100644 --- a/libraries/render/src/render/drawItemBounds.slv +++ b/libraries/render/src/render/drawItemBounds.slv @@ -59,7 +59,9 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, pos, gl_Position)$> - varColor = vec4(colorWheel(fract(float(inCellLocation.w) / 5.0)), 1.0); + bool subcell = bool((inCellLocation.z)); + float cellDepth = float(inCellLocation.w); + varColor = vec4(colorWheel(fract(cellDepth / 5.0)), 1.0 - float(subcell)); varTexcoord = vec2(cubeVec.w, length(inBoundDim)); } \ No newline at end of file