/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2025 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #include #include #include #include #include #include #include #include namespace openspace { AtlasManager::AtlasManager(TSP* tsp) : _tsp(tsp) {} bool AtlasManager::initialize() { TSP::Header header = _tsp->header(); _nBricksPerDim = header.xNumBricks; _nOtLeaves = _nBricksPerDim * _nBricksPerDim * _nBricksPerDim; _nOtNodes = _tsp->numOTNodes(); _nOtLevels = static_cast(log(_nOtLeaves) / log(8) + 1); _paddedBrickDim = _tsp->paddedBrickDim(); _nBricksInMap = _nBricksPerDim * _nBricksPerDim * _nBricksPerDim; _atlasDim = _nBricksPerDim * _paddedBrickDim; _nBrickVals = _paddedBrickDim*_paddedBrickDim*_paddedBrickDim; _brickSize = _nBrickVals * sizeof(float); _volumeSize = _brickSize * _nOtLeaves; _atlasMap = std::vector(_nOtLeaves, NotUsedIndex); _nBricksInAtlas = _nBricksInMap; _freeAtlasCoords = std::vector(_nBricksInAtlas, 0); for (unsigned int i = 0; i < _nBricksInAtlas; i++) { _freeAtlasCoords[i] = i; } _textureAtlas = new ghoul::opengl::Texture( glm::size3_t(_atlasDim, _atlasDim, _atlasDim), GL_TEXTURE_3D, ghoul::opengl::Texture::Format::RGBA, GL_RGBA, GL_FLOAT ); _textureAtlas->uploadTexture(); glGenBuffers(2, _pboHandle); glGenBuffers(1, &_atlasMapBuffer); glBindBuffer(GL_SHADER_STORAGE_BUFFER, _atlasMapBuffer); glBufferData( GL_SHADER_STORAGE_BUFFER, sizeof(GLint) * _nBricksInMap, nullptr, GL_DYNAMIC_READ ); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); return true; } const std::vector& AtlasManager::atlasMap() const { return _atlasMap; } unsigned int AtlasManager::atlasMapBuffer() const { return _atlasMapBuffer; } void AtlasManager::updateAtlas(BufferIndex bufferIndex, std::vector& brickIndices) { size_t nBrickIndices = brickIndices.size(); _requiredBricks.clear(); for (size_t i = 0; i < nBrickIndices; i++) { _requiredBricks.insert(brickIndices[i]); } for (unsigned int it : _prevRequiredBricks) { if (!_requiredBricks.count(it)) { removeFromAtlas(it); } } // Stats _nUsedBricks = static_cast(_requiredBricks.size()); _nStreamedBricks = 0; _nDiskReads = 0; glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _pboHandle[bufferIndex]); glBufferData(GL_PIXEL_UNPACK_BUFFER, _volumeSize, nullptr, GL_STREAM_DRAW); float* mappedBuffer = reinterpret_cast( glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY) ); if (!mappedBuffer) { LERRORC("AtlasManager", "Failed to map PBO"); return; } for (auto itStart = _requiredBricks.begin(); itStart != _requiredBricks.end();) { int firstBrick = *itStart; int lastBrick = firstBrick; auto itEnd = itStart; for (itEnd++; itEnd != _requiredBricks.end() && *itEnd == static_cast(lastBrick) + 1; itEnd++) { lastBrick = *itEnd; } addToAtlas(firstBrick, lastBrick, mappedBuffer); itStart = itEnd; } glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); for (size_t i = 0; i < nBrickIndices; i++) { _atlasMap[i] = _brickMap[brickIndices[i]]; } std::swap(_prevRequiredBricks, _requiredBricks); pboToAtlas(bufferIndex); glBindBuffer(GL_SHADER_STORAGE_BUFFER, _atlasMapBuffer); GLint* to = reinterpret_cast( glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_WRITE_ONLY) ); memcpy(to, _atlasMap.data(), sizeof(GLint)*_atlasMap.size()); glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); } void AtlasManager::addToAtlas(int firstBrickIndex, int lastBrickIndex, float* mappedBuffer) { while (_brickMap.count(firstBrickIndex) && firstBrickIndex <= lastBrickIndex) { firstBrickIndex++; } while (_brickMap.count(lastBrickIndex) && lastBrickIndex >= firstBrickIndex) { lastBrickIndex--; } if (lastBrickIndex < firstBrickIndex) { return; } int sequenceLength = lastBrickIndex - firstBrickIndex + 1; float* sequenceBuffer = new float[sequenceLength*_nBrickVals]; size_t bufferSize = sequenceLength * _brickSize; long long offset = TSP::dataPosition() + static_cast(firstBrickIndex) * static_cast(_brickSize); _tsp->file().seekg(offset); _tsp->file().read(reinterpret_cast(sequenceBuffer), bufferSize); _nDiskReads++; for (int brickIndex = firstBrickIndex; brickIndex <= lastBrickIndex; brickIndex++) { if (!_brickMap.count(brickIndex)) { unsigned int atlasCoords = _freeAtlasCoords.back(); _freeAtlasCoords.pop_back(); int level = _nOtLevels - static_cast( floor(log1p((7.0 * (float(brickIndex % _nOtNodes))))/log(8)) - 1 ); ghoul_assert(atlasCoords <= 0x0FFFFFFF, "@MISSING"); unsigned int atlasData = (level << 28) + atlasCoords; _brickMap.emplace(brickIndex, atlasData); _nStreamedBricks++; fillVolume( &sequenceBuffer[_nBrickVals*(brickIndex - firstBrickIndex)], mappedBuffer, atlasCoords ); } } delete[] sequenceBuffer; } void AtlasManager::removeFromAtlas(int brickIndex) { unsigned int atlasData = _brickMap[brickIndex]; unsigned int atlasCoords = atlasData & 0x0FFFFFFF; _brickMap.erase(brickIndex); _freeAtlasCoords.push_back(atlasCoords); } void AtlasManager::fillVolume(float* in, float* out, unsigned int linearAtlasCoords) { int x = linearAtlasCoords % _nBricksPerDim; int y = (linearAtlasCoords / _nBricksPerDim) % _nBricksPerDim; int z = linearAtlasCoords / _nBricksPerDim / _nBricksPerDim; unsigned int xMin = x*_paddedBrickDim; unsigned int yMin = y*_paddedBrickDim; unsigned int zMin = z*_paddedBrickDim; unsigned int xMax = xMin + _paddedBrickDim; unsigned int yMax = yMin + _paddedBrickDim; unsigned int zMax = zMin + _paddedBrickDim; unsigned int from = 0; for (unsigned int zValCoord = zMin; zValCoorddimensions(); glBindTexture(GL_TEXTURE_3D, *_textureAtlas); glTexSubImage3D( GL_TEXTURE_3D, 0, 0, 0, 0, static_cast(dim[0]), static_cast(dim[1]), static_cast(dim[2]), GL_RED, GL_FLOAT, nullptr ); glBindTexture(GL_TEXTURE_3D, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } ghoul::opengl::Texture& AtlasManager::textureAtlas() { ghoul_assert(_textureAtlas != nullptr, "Texture atlas is nullptr"); return *_textureAtlas; } unsigned int AtlasManager::numDiskReads() const { return _nDiskReads; } unsigned int AtlasManager::numUsedBricks() const { return _nUsedBricks; } unsigned int AtlasManager::numStreamedBricks() const { return _nStreamedBricks; } glm::size3_t AtlasManager::textureSize() const { return _textureAtlas->dimensions(); } } // namespace openspace