PtexFaceData* PtexReader::TiledReducedFace::getTile(int tile) { _cache->cachelock.lock(); FaceData*& face = _tiles[tile]; if (face) { face->ref(); _cache->cachelock.unlock(); return face; } // first, get all parent tiles for this tile // and check if they are constant (with the same value) int pntilesu = _parentface->ntilesu(); int pntilesv = _parentface->ntilesv(); int nu = pntilesu / _ntilesu; // num parent tiles for this tile in u dir int nv = pntilesv / _ntilesv; // num parent tiles for this tile in v dir int ntiles = nu*nv; // num parent tiles for this tile PtexFaceData** tiles = (PtexFaceData**) alloca(ntiles * sizeof(PtexFaceData*)); bool allConstant = true; int ptile = (tile/_ntilesu) * nv * pntilesu + (tile%_ntilesu) * nu; for (int i = 0; i < ntiles;) { // temporarily release cache lock while we get parent tile _cache->cachelock.unlock(); PtexFaceData* tile = tiles[i] = _parentface->getTile(ptile); _cache->cachelock.lock(); allConstant = (allConstant && tile->isConstant() && (i==0 || (0 == memcmp(tiles[0]->getData(), tile->getData(), _pixelsize)))); i++; ptile += i%nu? 1 : pntilesu - nu + 1; } // make sure another thread didn't make the tile while we were checking if (face) { face->ref(); _cache->cachelock.unlock(); // release the tiles for (int i = 0; i < ntiles; i++) tiles[i]->release(); return face; } if (allConstant) { // allocate a new constant face face = new ConstantFace((void**)&face, _cache, _pixelsize); memcpy(face->getData(), tiles[0]->getData(), _pixelsize); } else { // allocate a new packed face for the tile face = new PackedFace((void**)&face, _cache, _tileres, _pixelsize, _pixelsize*_tileres.size()); // generate reduction from parent tiles int ptileures = _parentface->tileres().u(); int ptilevres = _parentface->tileres().v(); int sstride = ptileures * _pixelsize; int dstride = _tileres.u() * _pixelsize; int dstepu = dstride/nu; int dstepv = dstride*_tileres.v()/nv - dstepu*(nu-1); char* dst = (char*) face->getData(); for (int i = 0; i < ntiles;) { PtexFaceData* tile = tiles[i]; if (tile->isConstant()) PtexUtils::fill(tile->getData(), dst, dstride, _tileres.u()/nu, _tileres.v()/nv, _pixelsize); else _reducefn(tile->getData(), sstride, ptileures, ptilevres, dst, dstride, _dt, _nchan); i++; dst += i%nu ? dstepu : dstepv; } } _cache->cachelock.unlock(); // release the tiles for (int i = 0; i < ntiles; i++) tiles[i]->release(); return face; }
void PtexReader::TiledFaceBase::reduce(FaceData*& face, PtexReader* r, Res newres, PtexUtils::ReduceFn reducefn) { // get reduce lock and make sure we still need to reduce AutoMutex rlocker(r->reducelock); if (face) { // another thread must have generated it while we were waiting AutoLockCache clocker(_cache->cachelock); // make sure it's still there now that we have the lock if (face) { face->ref(); return; } } /* Tiled reductions should generally only be anisotropic (just u or v, not both) since isotropic reductions are precomputed and stored on disk. (This function should still work for isotropic reductions though.) In the anisotropic case, the number of tiles should be kept the same along the direction not being reduced in order to preserve the laziness of the file access. In contrast, if reductions were not tiled, then any reduction would read all the tiles and defeat the purpose of tiling. */ // keep new face local until fully initialized FaceData* volatile newface = 0; // don't tile if either dimension is 1 (rare, would complicate blendFaces too much) // also, don't tile triangle reductions (too complicated) Res newtileres; bool isTriangle = r->_header.meshtype == mt_triangle; if (newres.ulog2 == 1 || newres.vlog2 == 1 || isTriangle) { newtileres = newres; } else { // propagate the tile res to the reduction newtileres = _tileres; // but make sure tile isn't larger than the new face! if (newtileres.ulog2 > newres.ulog2) newtileres.ulog2 = newres.ulog2; if (newtileres.vlog2 > newres.vlog2) newtileres.vlog2 = newres.vlog2; } // determine how many tiles we will have on the reduction int newntiles = newres.ntiles(newtileres); if (newntiles == 1) { // no need to keep tiling, reduce tiles into a single face // first, get all tiles and check if they are constant (with the same value) PtexFaceData** tiles = (PtexFaceData**) alloca(_ntiles * sizeof(PtexFaceData*)); bool allConstant = true; for (int i = 0; i < _ntiles; i++) { PtexFaceData* tile = tiles[i] = getTile(i); allConstant = (allConstant && tile->isConstant() && (i == 0 || (0 == memcmp(tiles[0]->getData(), tile->getData(), _pixelsize)))); } if (allConstant) { // allocate a new constant face newface = new ConstantFace((void**)&face, _cache, _pixelsize); memcpy(newface->getData(), tiles[0]->getData(), _pixelsize); } else if (isTriangle) { // reassemble all tiles into temporary contiguous image // (triangle reduction doesn't work on tiles) int tileures = _tileres.u(); int tilevres = _tileres.v(); int sstride = _pixelsize * tileures; int dstride = sstride * _ntilesu; int dstepv = dstride * tilevres - sstride*(_ntilesu-1); char* tmp = (char*) malloc(_ntiles * _tileres.size() * _pixelsize); char* tmpptr = tmp; for (int i = 0; i < _ntiles;) { PtexFaceData* tile = tiles[i]; if (tile->isConstant()) PtexUtils::fill(tile->getData(), tmpptr, dstride, tileures, tilevres, _pixelsize); else PtexUtils::copy(tile->getData(), sstride, tmpptr, dstride, tilevres, sstride); i++; tmpptr += i%_ntilesu ? sstride : dstepv; } // allocate a new packed face newface = new PackedFace((void**)&face, _cache, newres, _pixelsize, _pixelsize * newres.size()); // reduce and copy into new face reducefn(tmp, _pixelsize * _res.u(), _res.u(), _res.v(), newface->getData(), _pixelsize * newres.u(), _dt, _nchan); free(tmp); } else { // allocate a new packed face newface = new PackedFace((void**)&face, _cache, newres, _pixelsize, _pixelsize*newres.size()); int tileures = _tileres.u(); int tilevres = _tileres.v(); int sstride = _pixelsize * tileures; int dstride = _pixelsize * newres.u(); int dstepu = dstride/_ntilesu; int dstepv = dstride*newres.v()/_ntilesv - dstepu*(_ntilesu-1); char* dst = (char*) newface->getData(); for (int i = 0; i < _ntiles;) { PtexFaceData* tile = tiles[i]; if (tile->isConstant()) PtexUtils::fill(tile->getData(), dst, dstride, newres.u()/_ntilesu, newres.v()/_ntilesv, _pixelsize); else reducefn(tile->getData(), sstride, tileures, tilevres, dst, dstride, _dt, _nchan); i++; dst += i%_ntilesu ? dstepu : dstepv; } } // release the tiles for (int i = 0; i < _ntiles; i++) tiles[i]->release(); } else { // otherwise, tile the reduced face newface = new TiledReducedFace((void**)&face, _cache, newres, newtileres, _dt, _nchan, this, reducefn); } AutoLockCache clocker(_cache->cachelock); face = newface; // clean up unused data _cache->purgeData(); }