mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-12 04:02:35 +02:00
Add caching of imageOffset to ktx ImageDescriptor
This commit is contained in:
parent
e9bb895bff
commit
39c3fee838
8 changed files with 79 additions and 104 deletions
|
@ -108,7 +108,8 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor
|
|||
}
|
||||
|
||||
|
||||
ktx::StoragePointer file { new storage::FileStorage(_filename.c_str()) };
|
||||
auto fileStorage = new storage::FileStorage(_filename.c_str());
|
||||
ktx::StoragePointer file { fileStorage };
|
||||
auto data = file->mutableData();
|
||||
data += file->size();
|
||||
|
||||
|
@ -197,19 +198,21 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
|
|||
header.numberOfMipmapLevels = texture.getNumMips();
|
||||
|
||||
ktx::Images images;
|
||||
uint32_t imageOffset = 0;
|
||||
for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) {
|
||||
auto mip = texture.accessStoredMipFace(level);
|
||||
if (mip) {
|
||||
if (numFaces == 1) {
|
||||
images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, mip->readData()));
|
||||
images.emplace_back(ktx::Image(imageOffset, (uint32_t)mip->getSize(), 0, mip->readData()));
|
||||
} else {
|
||||
ktx::Image::FaceBytes cubeFaces(Texture::CUBE_FACE_COUNT);
|
||||
cubeFaces[0] = mip->readData();
|
||||
for (uint32_t face = 1; face < Texture::CUBE_FACE_COUNT; face++) {
|
||||
cubeFaces[face] = texture.accessStoredMipFace(level, face)->readData();
|
||||
}
|
||||
images.emplace_back(ktx::Image((uint32_t)mip->getSize(), 0, cubeFaces));
|
||||
images.emplace_back(ktx::Image(imageOffset, (uint32_t)mip->getSize(), 0, cubeFaces));
|
||||
}
|
||||
imageOffset += mip->getSize() + 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,12 +73,18 @@ size_t Header::evalImageSize(uint32_t level) const {
|
|||
ImageDescriptors Header::generateImageDescriptors() const {
|
||||
ImageDescriptors descriptors;
|
||||
|
||||
uint32_t imageOffset = 0;
|
||||
for (auto level = 0; level < numberOfMipmapLevels; ++level) {
|
||||
auto imageSize = static_cast<uint32_t>(evalImageSize(level));
|
||||
ImageHeader header {
|
||||
numberOfFaces == NUM_CUBEMAPFACES,
|
||||
static_cast<uint32_t>(evalImageSize(level)),
|
||||
imageOffset,
|
||||
imageSize,
|
||||
0
|
||||
};
|
||||
|
||||
imageOffset += imageSize + 4;
|
||||
|
||||
ImageHeader::FaceOffsets offsets;
|
||||
for (auto i = 0; i < numberOfFaces; ++i) {
|
||||
offsets.push_back(0);
|
||||
|
|
|
@ -414,12 +414,17 @@ namespace ktx {
|
|||
struct ImageHeader {
|
||||
using FaceOffsets = std::vector<size_t>;
|
||||
using FaceBytes = std::vector<const Byte*>;
|
||||
// This is the byte offset from the _start_ of the image region. For example, level 0
|
||||
// will have a byte offset of 0.
|
||||
const uint32_t _imageOffset;
|
||||
|
||||
const uint32_t _numFaces;
|
||||
const uint32_t _imageSize;
|
||||
const uint32_t _faceSize;
|
||||
const uint32_t _padding;
|
||||
ImageHeader(bool cube, uint32_t imageSize, uint32_t padding) :
|
||||
ImageHeader(bool cube, uint32_t imageOffset, uint32_t imageSize, uint32_t padding) :
|
||||
_numFaces(cube ? NUM_CUBEMAPFACES : 1),
|
||||
_imageOffset(imageOffset),
|
||||
_imageSize(imageSize * _numFaces),
|
||||
_faceSize(imageSize),
|
||||
_padding(padding) {
|
||||
|
@ -439,11 +444,11 @@ namespace ktx {
|
|||
struct Image : public ImageHeader {
|
||||
FaceBytes _faceBytes;
|
||||
Image(const ImageHeader& header, const FaceBytes& faces) : ImageHeader(header), _faceBytes(faces) {}
|
||||
Image(uint32_t imageSize, uint32_t padding, const Byte* bytes) :
|
||||
ImageHeader(false, imageSize, padding),
|
||||
Image(uint32_t imageOffset, uint32_t imageSize, uint32_t padding, const Byte* bytes) :
|
||||
ImageHeader(false, imageOffset, imageSize, padding),
|
||||
_faceBytes(1, bytes) {}
|
||||
Image(uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) :
|
||||
ImageHeader(true, pageSize, padding)
|
||||
Image(uint32_t imageOffset, uint32_t pageSize, uint32_t padding, const FaceBytes& cubeFaceBytes) :
|
||||
ImageHeader(true, imageOffset, pageSize, padding)
|
||||
{
|
||||
if (cubeFaceBytes.size() == NUM_CUBEMAPFACES) {
|
||||
_faceBytes = cubeFaceBytes;
|
||||
|
|
|
@ -144,6 +144,7 @@ namespace ktx {
|
|||
while ((currentPtr - srcBytes) + sizeof(uint32_t) <= (srcSize)) {
|
||||
|
||||
// Grab the imageSize coming up
|
||||
uint32_t imageOffset = currentPtr - srcBytes;
|
||||
size_t imageSize = *reinterpret_cast<const uint32_t*>(currentPtr);
|
||||
currentPtr += sizeof(uint32_t);
|
||||
|
||||
|
@ -158,10 +159,10 @@ namespace ktx {
|
|||
faces[face] = currentPtr;
|
||||
currentPtr += faceSize;
|
||||
}
|
||||
images.emplace_back(Image((uint32_t) faceSize, padding, faces));
|
||||
images.emplace_back(Image(imageOffset, (uint32_t) faceSize, padding, faces));
|
||||
currentPtr += padding;
|
||||
} else {
|
||||
images.emplace_back(Image((uint32_t) imageSize, padding, currentPtr));
|
||||
images.emplace_back(Image(imageOffset, (uint32_t) imageSize, padding, currentPtr));
|
||||
currentPtr += imageSize + padding;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -211,6 +211,7 @@ namespace ktx {
|
|||
|
||||
for (uint32_t l = 0; l < srcImages.size(); l++) {
|
||||
if (currentDataSize + sizeof(uint32_t) < allocatedImagesDataSize) {
|
||||
uint32_t imageOffset = currentPtr - destBytes;
|
||||
size_t imageSize = srcImages[l]._imageSize;
|
||||
*(reinterpret_cast<uint32_t*> (currentPtr)) = (uint32_t) imageSize;
|
||||
currentPtr += sizeof(uint32_t);
|
||||
|
@ -223,7 +224,7 @@ namespace ktx {
|
|||
// Single face vs cubes
|
||||
if (srcImages[l]._numFaces == 1) {
|
||||
memcpy(currentPtr, srcImages[l]._faceBytes[0], imageSize);
|
||||
destImages.emplace_back(Image((uint32_t) imageSize, padding, currentPtr));
|
||||
destImages.emplace_back(Image(imageOffset, (uint32_t) imageSize, padding, currentPtr));
|
||||
currentPtr += imageSize;
|
||||
} else {
|
||||
Image::FaceBytes faceBytes(NUM_CUBEMAPFACES);
|
||||
|
@ -233,7 +234,7 @@ namespace ktx {
|
|||
faceBytes[face] = currentPtr;
|
||||
currentPtr += faceSize;
|
||||
}
|
||||
destImages.emplace_back(Image(faceSize, padding, faceBytes));
|
||||
destImages.emplace_back(Image(imageOffset, faceSize, padding, faceBytes));
|
||||
}
|
||||
|
||||
currentPtr += padding;
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include <gpu/Batch.h>
|
||||
|
||||
|
||||
#include <image/Image.h>
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
|
@ -395,6 +394,12 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) {
|
|||
_ktxMipRequest->setByteRange(range);
|
||||
} else {
|
||||
// TODO: Discover range for other mips
|
||||
ByteRange range;
|
||||
range.fromInclusive = ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData
|
||||
+ _ktxDescriptor->images[low]._imageOffset + 4;
|
||||
range.toExclusive = ktx::KTX_HEADER_SIZE + _ktxDescriptor->header.bytesOfKeyValueData
|
||||
+ _ktxDescriptor->images[high + 1]._imageOffset;
|
||||
_ktxMipRequest->setByteRange(range);
|
||||
}
|
||||
|
||||
connect(_ktxMipRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxMipRequestProgress);
|
||||
|
@ -423,8 +428,17 @@ void NetworkTexture::ktxMipRequestFinished() {
|
|||
&& _ktxMipLevelRangeInFlight.second == NULL_MIP_LEVEL;
|
||||
|
||||
if (_ktxMipRequest->getResult() == ResourceRequest::Success) {
|
||||
_ktxHighMipData = _ktxMipRequest->getData();
|
||||
maybeCreateKTX();
|
||||
if (_initialKtxLoaded) {
|
||||
assert(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0);
|
||||
|
||||
_textureSource->getGPUTexture()->assignStoredMip(_ktxMipLevelRangeInFlight.first,
|
||||
_ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data()));
|
||||
//texture->assignStoredMip(level, image._imageSize, ktxData);
|
||||
} else {
|
||||
_ktxHighMipData = _ktxMipRequest->getData();
|
||||
maybeCreateKTX();
|
||||
}
|
||||
|
||||
} else {
|
||||
handleFailedRequest(_ktxMipRequest->getResult());
|
||||
}
|
||||
|
@ -457,7 +471,7 @@ void NetworkTexture::maybeCreateKTX() {
|
|||
auto memKtx = ktx::KTX::createBare(*header, keyValues);
|
||||
|
||||
auto d = const_cast<uint8_t*>(memKtx->getStorage()->data());
|
||||
memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size());
|
||||
///memcpy(d + memKtx->_storage->size() - _ktxHighMipData.size(), _ktxHighMipData.data(), _ktxHighMipData.size());
|
||||
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
|
||||
|
@ -485,18 +499,21 @@ void NetworkTexture::maybeCreateKTX() {
|
|||
uint8_t* ktxData = reinterpret_cast<uint8_t*>(_ktxHighMipData.data());
|
||||
ktxData += _ktxHighMipData.size();
|
||||
// TODO Move image offset calculation to ktx ImageDescriptor
|
||||
for (uint16_t i = images.size() - 1; i >= 0; --i) {
|
||||
auto& image = images[i];
|
||||
uint16_t level;
|
||||
for (level = images.size() - 1; level >= 0; --level) {
|
||||
auto& image = images[level];
|
||||
if (image._imageSize > imageSizeRemaining) {
|
||||
break;
|
||||
}
|
||||
qDebug() << "Transferring " << i;
|
||||
qDebug() << "Transferring " << level;
|
||||
ktxData -= image._imageSize;
|
||||
texture->assignStoredMip(i, image._imageSize, ktxData);
|
||||
texture->assignStoredMip(level, image._imageSize, ktxData);
|
||||
ktxData -= 4;
|
||||
imageSizeRemaining - image._imageSize - 4;
|
||||
}
|
||||
|
||||
_initialKtxLoaded = true;
|
||||
|
||||
// We replace the texture with the one stored in the cache. This deals with the possible race condition of two different
|
||||
// images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will
|
||||
// be the winner
|
||||
|
@ -506,6 +523,27 @@ void NetworkTexture::maybeCreateKTX() {
|
|||
|
||||
setImage(texture, header->getPixelWidth(), header->getPixelHeight());
|
||||
|
||||
|
||||
// Force load the next two levels
|
||||
{
|
||||
QTimer* timer = new QTimer();
|
||||
connect(timer, &QTimer::timeout, this, [=]() {
|
||||
startMipRangeRequest(level, level);
|
||||
});
|
||||
timer->setSingleShot(true);
|
||||
timer->setInterval(4000);
|
||||
timer->start();
|
||||
}
|
||||
|
||||
{
|
||||
QTimer* timer = new QTimer();
|
||||
connect(timer, &QTimer::timeout, this, [=]() {
|
||||
startMipRangeRequest(level - 1, level - 1);
|
||||
});
|
||||
timer->setSingleShot(true);
|
||||
timer->setInterval(6000);
|
||||
timer->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -516,87 +554,6 @@ void NetworkTexture::downloadFinished(const QByteArray& data) {
|
|||
void NetworkTexture::loadContent(const QByteArray& content) {
|
||||
if (_sourceIsKTX) {
|
||||
assert(false);
|
||||
if (_ktxLoadState == LOADING_HEADER) {
|
||||
// TODO Handle case where we already have the source hash texture on disk
|
||||
// TODO Handle case where data isn't as large as the ktx header
|
||||
_ktxLoadState = LOADING_LOWEST_SIX;
|
||||
auto header = reinterpret_cast<const ktx::Header*>(content.data());
|
||||
qDebug() << "Identifier:" << QString(QByteArray((char*)header->identifier, 12));
|
||||
qDebug() << "Type:" << header->glType;
|
||||
qDebug() << "TypeSize:" << header->glTypeSize;
|
||||
qDebug() << "numberOfArrayElements:" << header->numberOfArrayElements;
|
||||
qDebug() << "numberOfFaces:" << header->numberOfFaces;
|
||||
qDebug() << "numberOfMipmapLevels:" << header->numberOfMipmapLevels;
|
||||
auto kvSize = header->bytesOfKeyValueData;
|
||||
if (kvSize > content.size() - ktx::KTX_HEADER_SIZE) {
|
||||
qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request";
|
||||
return;
|
||||
}
|
||||
|
||||
auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast<const ktx::Byte*>(content.data()) + ktx::KTX_HEADER_SIZE);
|
||||
|
||||
// Create bare ktx in memory
|
||||
std::string filename = "test";
|
||||
auto memKtx = ktx::KTX::createBare(*header, keyValues);
|
||||
|
||||
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
|
||||
// Move ktx to file
|
||||
const char* data = reinterpret_cast<const char*>(memKtx->_storage->data());
|
||||
size_t length = memKtx->_storage->size();
|
||||
KTXFilePointer file;
|
||||
auto& ktxCache = textureCache->_ktxCache;
|
||||
if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) {
|
||||
qCWarning(modelnetworking) << _url << "file cache failed";
|
||||
} else {
|
||||
_file = file;
|
||||
}
|
||||
|
||||
//auto texture = gpu::Texture::serializeHeader("test.ktx", *header, keyValues);
|
||||
gpu::TexturePointer texture;
|
||||
texture.reset(gpu::Texture::unserialize(_file->getFilepath(), memKtx->toDescriptor()));
|
||||
texture->setKtxBacking(file->getFilepath());
|
||||
|
||||
// We replace the texture with the one stored in the cache. This deals with the possible race condition of two different
|
||||
// images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will
|
||||
// be the winner
|
||||
if (textureCache) {
|
||||
texture = textureCache->cacheTextureByHash(filename, texture);
|
||||
}
|
||||
|
||||
|
||||
|
||||
auto desc = memKtx->toDescriptor();
|
||||
int numMips = desc.images.size();
|
||||
auto numMipsToGet = glm::min(numMips, 6);
|
||||
auto sizeOfTopMips = 0;
|
||||
for (int i = 0; i < numMipsToGet; ++i) {
|
||||
auto mipLevel = numMips - 1 - i;
|
||||
auto& img = desc.images[mipLevel];
|
||||
sizeOfTopMips += img._imageSize;
|
||||
}
|
||||
_requestByteRange.fromInclusive = length - sizeOfTopMips;
|
||||
_requestByteRange.toExclusive = length;
|
||||
QMetaObject::invokeMethod(this, "attemptRequest", Qt::QueuedConnection);
|
||||
|
||||
|
||||
//texture->setMinMip(desc.images.size() - 1);
|
||||
setImage(texture, header->getPixelWidth(), header->getPixelHeight());
|
||||
|
||||
} else {
|
||||
qDebug() << "Got highest 6 mips";
|
||||
|
||||
ktx::StoragePointer storage { new storage::FileStorage(QString::fromStdString(_file->getFilepath())) };
|
||||
auto data = storage->mutableData();
|
||||
auto size = storage->getSize();
|
||||
//*data = 'H';
|
||||
memcpy(data + _requestByteRange.fromInclusive, content.data(), content.size());
|
||||
//getGPUTexture()->setMinMip(getGPUTexture()->getMinMip() - 6);
|
||||
//auto ktxPointer = ktx::KTX::create(storage);
|
||||
|
||||
//ktxPointer->writeMipData(level, data, size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,8 @@ private:
|
|||
};
|
||||
|
||||
|
||||
KTXLoadState _ktxLoadState { LOADING_HEADER };
|
||||
bool _initialKtxLoaded { false };
|
||||
//KTXLoadState _ktxLoadState;
|
||||
KTXFilePointer _file;
|
||||
static const uint16_t NULL_MIP_LEVEL;
|
||||
bool _sourceIsKTX { false };
|
||||
|
|
|
@ -65,7 +65,8 @@ void HTTPResourceRequest::doSend() {
|
|||
if (_byteRange.fromInclusive < 0) {
|
||||
byteRange = QString("bytes=%1").arg(_byteRange.fromInclusive);
|
||||
} else {
|
||||
byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive);
|
||||
// HTTP byte ranges are inclusive on the `to` end: [from, to]
|
||||
byteRange = QString("bytes=%1-%2").arg(_byteRange.fromInclusive).arg(_byteRange.toExclusive - 1);
|
||||
}
|
||||
qDebug() << "Setting http range to " << byteRange;
|
||||
networkRequest.setRawHeader("Range", byteRange.toLatin1());
|
||||
|
|
Loading…
Reference in a new issue