void PtexReader::readFaceData(FilePos pos, FaceDataHeader fdh, Res res, int levelid, FaceData*& face) { // keep new face local until fully initialized FaceData* volatile newface = 0; seek(pos); switch (fdh.encoding()) { case enc_constant: { ConstantFace* pf = new ConstantFace((void**)&face, _cache, _pixelsize); readBlock(pf->data(), _pixelsize); if (levelid==0 && _premultiply && _header.hasAlpha()) PtexUtils::multalpha(pf->data(), 1, _header.datatype, _header.nchannels, _header.alphachan); newface = pf; } break; case enc_tiled: { Res tileres; readBlock(&tileres, sizeof(tileres)); uint32_t tileheadersize; readBlock(&tileheadersize, sizeof(tileheadersize)); TiledFace* tf = new TiledFace((void**)&face, _cache, res, tileres, levelid, this); readZipBlock(&tf->_fdh[0], tileheadersize, FaceDataHeaderSize * tf->_ntiles); computeOffsets(tell(), tf->_ntiles, &tf->_fdh[0], &tf->_offsets[0]); newface = tf; } break; case enc_zipped: case enc_diffzipped: { int uw = res.u(), vw = res.v(); int npixels = uw * vw; int unpackedSize = _pixelsize * npixels; PackedFace* pf = new PackedFace((void**)&face, _cache, res, _pixelsize, unpackedSize); bool useMalloc = unpackedSize > AllocaMax; void* tmp = useMalloc ? malloc(unpackedSize) : alloca(unpackedSize); readZipBlock(tmp, fdh.blocksize(), unpackedSize); if (fdh.encoding() == enc_diffzipped) PtexUtils::decodeDifference(tmp, unpackedSize, _header.datatype); PtexUtils::interleave(tmp, uw * DataSize(_header.datatype), uw, vw, pf->data(), uw * _pixelsize, _header.datatype, _header.nchannels); if (levelid==0 && _premultiply && _header.hasAlpha()) PtexUtils::multalpha(pf->data(), npixels, _header.datatype, _header.nchannels, _header.alphachan); newface = pf; if (useMalloc) free(tmp); } break; } face = newface; }
void PtexReader::readFace(int levelid, Level* level, int faceid) { // temporarily release cache lock so other threads can proceed _cache->cachelock.unlock(); // get read lock and make sure we still need to read FaceData*& face = level->faces[faceid]; AutoMutex locker(readlock); if (face) { // another thread must have read it while we were waiting _cache->cachelock.lock(); // make sure it's still there now that we have the lock if (face) { face->ref(); return; } _cache->cachelock.unlock(); } // Go ahead and read the face, and read nearby faces if // possible. The goal is to coalesce small faces into single // runs of consecutive reads to minimize seeking and take // advantage of read-ahead buffering. // Try to read as many faces as will fit in BlockSize. Use the // in-memory size rather than the on-disk size to prevent flooding // the memory cache. And don't coalesce w/ tiled faces as these // are meant to be read individually. // scan both backwards and forwards looking for unread faces int first = faceid, last = faceid; int totalsize = 0; FaceDataHeader fdh = level->fdh[faceid]; if (fdh.encoding() != enc_tiled) { totalsize += unpackedSize(fdh, levelid, faceid); int nfaces = int(level->fdh.size()); while (1) { int f = first-1; if (f < 0 || level->faces[f]) break; fdh = level->fdh[f]; if (fdh.encoding() == enc_tiled) break; int size = totalsize + unpackedSize(fdh, levelid, f); if (size > BlockSize) break; first = f; totalsize = size; } while (1) { int f = last+1; if (f >= nfaces || level->faces[f]) break; fdh = level->fdh[f]; if (fdh.encoding() == enc_tiled) break; int size = totalsize + unpackedSize(fdh, levelid, f); if (size > BlockSize) break; last = f; totalsize = size; } } // read all faces in range // keep track of extra faces we read so we can add them to the cache later std::vector<FaceData*> extraFaces; extraFaces.reserve(last-first); for (int i = first; i <= last; i++) { fdh = level->fdh[i]; // skip faces with zero size (which is true for level-0 constant faces) if (fdh.blocksize()) { FaceData*& face = level->faces[i]; readFaceData(level->offsets[i], fdh, getRes(levelid, i), levelid, face); if (i != faceid) extraFaces.push_back(face); } } // reacquire cache lock, then unref extra faces to add them to the cache _cache->cachelock.lock(); for (size_t i = 0, size = extraFaces.size(); i < size; i++) extraFaces[i]->unref(); }