void PtexWriterBase::writeFaceData(FILE* fp, const void* data, int stride, Res res, FaceDataHeader& fdh) { // determine whether to break into tiles Res tileres = calcTileRes(res); int ntilesu = res.ntilesu(tileres); int ntilesv = res.ntilesv(tileres); int ntiles = ntilesu * ntilesv; if (ntiles == 1) { // write single block writeFaceBlock(fp, data, stride, res, fdh); } else { // write tiles to tilefp temp file rewind(_tilefp); // alloc tile header std::vector<FaceDataHeader> tileHeader(ntiles); int tileures = tileres.u(); int tilevres = tileres.v(); int tileustride = tileures*_pixelSize; int tilevstride = tilevres*stride; // output tiles FaceDataHeader* tdh = &tileHeader[0]; int datasize = 0; const char* rowp = (const char*) data; const char* rowpend = rowp + ntilesv * tilevstride; for (; rowp != rowpend; rowp += tilevstride) { const char* p = rowp; const char* pend = p + ntilesu * tileustride; for (; p != pend; tdh++, p += tileustride) { // determine if tile is constant if (PtexUtils::isConstant(p, stride, tileures, tilevres, _pixelSize)) writeConstFaceBlock(_tilefp, p, *tdh); else writeFaceBlock(_tilefp, p, stride, tileres, *tdh); datasize += tdh->blocksize(); } } // output compressed tile header uint32_t tileheadersize = writeZipBlock(_tilefp, &tileHeader[0], int(sizeof(FaceDataHeader)*tileHeader.size())); // output tile data pre-header int totalsize = 0; totalsize += writeBlock(fp, &tileres, sizeof(Res)); totalsize += writeBlock(fp, &tileheadersize, sizeof(tileheadersize)); // copy compressed tile header from temp file totalsize += copyBlock(fp, _tilefp, datasize, tileheadersize); // copy tile data from temp file totalsize += copyBlock(fp, _tilefp, 0, datasize); fdh.set(totalsize, enc_tiled); } }
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(); }