/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2017 * * * * 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 namespace { const std::string _loggerCat = "BrickManager"; } namespace openspace { BrickManager::BrickManager(TSP* tsp) : _tsp(tsp) , numBricks_(0) , brickDim_(0) , paddedBrickDim_(0) , atlasDim_(0) , numBrickVals_(0) , numBricksFrame_(0) , numBricksTree_(0) , brickSize_(0) , volumeSize_(0) , numValsTot_(0) , xCoord_(0) , yCoord_(0) , zCoord_(0) , textureAtlas_(nullptr) , hasReadHeader_(false) , atlasInitialized_(false) {} BrickManager::~BrickManager() { } bool BrickManager::readHeader() { if (!_tsp->file().is_open()) return false; _header = _tsp->header(); LDEBUG("Grid type: " << _header.gridType_); LDEBUG("Original num timesteps: " << _header.numOrigTimesteps_); LDEBUG("Num timesteps: " << _header.numTimesteps_); LDEBUG("Brick dims: " << _header.xBrickDim_ << " " << _header.yBrickDim_ << " " << _header.zBrickDim_); LDEBUG("Num bricks: " << _header.xNumBricks_ << " " << _header.yNumBricks_ << " " << _header.zNumBricks_); LDEBUG(""); brickDim_ = _header.xBrickDim_; numBricks_ = _header.xNumBricks_; paddedBrickDim_ = brickDim_ + paddingWidth_ * 2; atlasDim_ = paddedBrickDim_*numBricks_; LDEBUG("Padded brick dim: " << paddedBrickDim_); LDEBUG("Atlas dim: " << atlasDim_); numBrickVals_ = paddedBrickDim_*paddedBrickDim_*paddedBrickDim_; // Number of bricks per frame numBricksFrame_ = numBricks_*numBricks_*numBricks_; // Calculate number of bricks in tree unsigned int numOTLevels = static_cast(log((int)numBricks_) / log(2) + 1); unsigned int numOTNodes = static_cast((pow(8, numOTLevels) - 1) / 7); unsigned int numBSTNodes = static_cast(_header.numTimesteps_ * 2 - 1); numBricksTree_ = numOTNodes * numBSTNodes; LDEBUG("Num OT levels: " << numOTLevels); LDEBUG("Num OT nodes: " << numOTNodes); LDEBUG("Num BST nodes: " << numBSTNodes); LDEBUG("Num bricks in tree: " << numBricksTree_); LDEBUG("Num values per brick: " << numBrickVals_); brickSize_ = sizeof(float)*numBrickVals_; volumeSize_ = brickSize_*numBricksFrame_; numValsTot_ = numBrickVals_*numBricksFrame_; _tsp->file().seekg(0, _tsp->file().end); long long fileSize = _tsp->file().tellg(); long long calcFileSize = static_cast(numBricksTree_)* static_cast(brickSize_) + TSP::dataPosition(); if (fileSize != calcFileSize) { LERROR("Sizes don't match"); LERROR("calculated file size: " << calcFileSize); LERROR("file size: " << fileSize); return false; } hasReadHeader_ = true; // Hold two brick lists brickLists_.resize(2); // Make sure the brick list can hold the maximum number of bricks // Each entry holds tree coordinates brickLists_[EVEN].resize(numBricksTree_ * 3, -1); brickLists_[ODD].resize(numBricksTree_ * 3, -1); // Allocate space for keeping tracks of bricks in PBO bricksInPBO_.resize(2); bricksInPBO_[EVEN].resize(numBricksTree_, -1); bricksInPBO_[ODD].resize(numBricksTree_, -1); // Allocate space for keeping track of the used coordinates in atlas usedCoords_.resize(2); usedCoords_[EVEN].resize(numBricksFrame_, false); usedCoords_[ODD].resize(numBricksFrame_, false); return true; } bool BrickManager::initialize() { if (atlasInitialized_) { LWARNING("InitAtlas() - already initialized"); } if (!hasReadHeader_) { LWARNING("InitAtlas() - Has not read header, trying to read"); return readHeader(); } // Prepare the 3D texture std::vector dims; dims.push_back(atlasDim_); dims.push_back(atlasDim_); dims.push_back(atlasDim_); textureAtlas_ = new ghoul::opengl::Texture( glm::size3_t(atlasDim_, atlasDim_, atlasDim_), ghoul::opengl::Texture::Format::RGBA, GL_RGBA, GL_FLOAT); textureAtlas_->uploadTexture(); //textureAtlas_ = Texture3D::New(dims); //if (!textureAtlas_->Init()) return false; atlasInitialized_ = true; glGenBuffers(2, pboHandle_); return true; } bool BrickManager::BuildBrickList(BUFFER_INDEX _bufIdx, std::vector &_brickRequest) { // Keep track of number bricks used and number of bricks cached // (for benchmarking) int numBricks = 0; int numCached = 0; // For every non-zero entry in the request list, assign a texture atlas // coordinate. For zero entries, signal "no brick" using -1. for (unsigned int i = 0; i<_brickRequest.size(); ++i) { if (_brickRequest[i] > 0) { numBricks++; //INFO("Checking brick " << i); // If the brick is already in the atlas, keep the coordinate if (bricksInPBO_[_bufIdx][i] != -1) { numCached++; // Get the corresponding coordinates from index int x, y, z; CoordsFromLin(bricksInPBO_[_bufIdx][i], x, y, z); brickLists_[_bufIdx][3 * i + 0] = x; brickLists_[_bufIdx][3 * i + 1] = y; brickLists_[_bufIdx][3 * i + 2] = z; // Mark coordinate as used usedCoords_[_bufIdx][bricksInPBO_[_bufIdx][i]] = true; } else { // If coord is already usedi by another brick, // skip it and try the next one while (usedCoords_[_bufIdx][LinearCoord(xCoord_, yCoord_, zCoord_)]) { IncCoord(); } brickLists_[_bufIdx][3 * i + 0] = xCoord_; brickLists_[_bufIdx][3 * i + 1] = yCoord_; brickLists_[_bufIdx][3 * i + 2] = zCoord_; usedCoords_[_bufIdx][LinearCoord(xCoord_, yCoord_, zCoord_)] = true; IncCoord(); } } else { // -1 is for "not used" brickLists_[_bufIdx][3 * i + 0] = -1; brickLists_[_bufIdx][3 * i + 1] = -1; brickLists_[_bufIdx][3 * i + 2] = -1; } // Reset brick list during iteration _brickRequest[i] = 0; } // Brick list is build, reset coordinate list for (auto it = usedCoords_[_bufIdx].begin(); it != usedCoords_[_bufIdx].end(); ++it) { *it = false; } //INFO("bricks NOT used: " << (float)(numBricksFrame_-numBricks) / (float)(numBricksFrame_)); //INFO("bricks cached: " << (float)numCached / (float)(numBricksFrame_)); return true; } bool BrickManager::FillVolume(float *_in, float *_out, unsigned int _x, unsigned int _y, unsigned int _z) { //timer_.start(); 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_; // Loop over the brick using three loops unsigned int from = 0; for (unsigned int zValCoord = zMin; zValCoord( glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)); if (!mappedBuffer) { LERROR("Failed to map PBO"); return false; } // Loop over brick request list unsigned int brickIndex = 0; while (brickIndex < brickLists_[_pboIndex].size() / 3) { // Find first active brick index in list while (brickIndex(brickIndex) * static_cast(brickSize_); */ long offset = TSP::dataPosition() + static_cast(brickIndex)* static_cast(brickSize_); // Skip reading if all bricks in sequence is already in PBO if (inPBO != sequence) { //timer_.start(); /* std::streamoff off = static_cast(offset); in_.seekg(off); if (in_.tellg() == -1) { ERROR("Failed to get input stream position"); INFO("offset: " << offset); INFO("streamoff max: " << std::numeric_limits::max()); INFO("size_t max: " << std::numeric_limits::max()); return false; } INFO("in.tellg(): " << in_.tellg()); in_.read(reinterpret_cast(seqBuffer), brickSize_*sequence); */ _tsp->file().seekg(offset); _tsp->file().read(reinterpret_cast(seqBuffer), bufSize); //timer_.stop(); //double time = timer_.elapsed().wall / 1.0e9; //double mb = (brickSize_*sequence) / 1048576.0; //INFO("Disk read "<( brickLists_[_pboIndex][3 * (brickIndex + i) + 0]); unsigned int y = static_cast( brickLists_[_pboIndex][3 * (brickIndex + i) + 1]); unsigned int z = static_cast( brickLists_[_pboIndex][3 * (brickIndex + i) + 2]); // Put each brick in the correct buffer place. // This needs to be done because the values are in brick order, and // the volume needs to be filled with one big float array. FillVolume(&seqBuffer[numBrickVals_*i], mappedBuffer, x, y, z); // Update the atlas list since the brick will be uploaded //INFO(brickIndex+i); bricksInPBO_[_pboIndex][brickIndex + i] = LinearCoord(x, y, z); } } } // if in pbo // Update the brick index brickIndex += sequence; delete[] seqBuffer; } glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); return true; } bool BrickManager::PBOToAtlas(BUFFER_INDEX _pboIndex) { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboHandle_[_pboIndex]); glm::size3_t dim = textureAtlas_->dimensions(); glBindTexture(GL_TEXTURE_3D, *textureAtlas_); glTexSubImage3D(GL_TEXTURE_3D, // target 0, // level 0, // xoffset 0, // yoffset 0, // zoffset dim[0], // width dim[1], // height dim[2], // depth GL_RED, // format GL_FLOAT, // type NULL); // *pixels glBindTexture(GL_TEXTURE_3D, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); return true; } ghoul::opengl::Texture* BrickManager::textureAtlas() { return textureAtlas_; } unsigned int BrickManager::pbo(BUFFER_INDEX _pboIndex) { return pboHandle_[_pboIndex]; } const std::vector& BrickManager::brickList(BUFFER_INDEX index) const { return brickLists_.at(index); } }