mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Add loading of lower mips to NetworkTexture
This commit is contained in:
parent
39c3fee838
commit
ab7099b3eb
7 changed files with 121 additions and 20 deletions
|
@ -190,6 +190,7 @@ void GL45ResourceTexture::populateTransferQueue() {
|
|||
qDebug() << "populateTransferQueue " << QString::fromStdString(_gpuObject.source()) << sourceMip << " " << targetMip;
|
||||
for (uint8_t face = 0; face < maxFace; ++face) {
|
||||
if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) {
|
||||
const_cast<gpu::Texture&>(_gpuObject).requestInterestInMip(sourceMip);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -476,6 +476,32 @@ void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePoin
|
|||
}
|
||||
}
|
||||
|
||||
void Texture::requestInterestInMip(uint16 level) {
|
||||
if (!_storage->isMipAvailable(level, 0)) {
|
||||
std::lock_guard<std::mutex> lock(_mipInterestListenersMutex);
|
||||
for (auto& callback : _mipInterestListeners) {
|
||||
callback->handleMipInterestCallback(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Texture::isStoredMipFaceAvailable(uint16 level, uint8 face) const {
|
||||
return _storage->isMipAvailable(level, face);
|
||||
}
|
||||
|
||||
void Texture::registerMipInterestListener(MipInterestListener* listener) {
|
||||
std::lock_guard<std::mutex> lock(_mipInterestListenersMutex);
|
||||
_mipInterestListeners.push_back(listener);
|
||||
}
|
||||
|
||||
void Texture::unregisterMipInterestListener(MipInterestListener* listener) {
|
||||
std::lock_guard<std::mutex> lock(_mipInterestListenersMutex);
|
||||
auto it = find(_mipInterestListeners.begin(), _mipInterestListeners.end(), listener);
|
||||
if (it != _mipInterestListeners.end()) {
|
||||
_mipInterestListeners.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::setAutoGenerateMips(bool enable) {
|
||||
bool changed = false;
|
||||
if (!_autoGenerateMips) {
|
||||
|
|
|
@ -310,19 +310,22 @@ public:
|
|||
KtxStorage(const std::string& filename);
|
||||
PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override;
|
||||
Size getMipFaceSize(uint16 level, uint8 face = 0) const override;
|
||||
// By convention, all mip levels and faces MUST be populated when using KTX backing
|
||||
bool isMipAvailable(uint16 level, uint8 face = 0) const override;
|
||||
|
||||
void assignMipData(uint16 level, const storage::StoragePointer& storage) override;
|
||||
|
||||
void assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) override;
|
||||
|
||||
void reset() override { }
|
||||
|
||||
protected:
|
||||
std::shared_ptr<storage::FileStorage> maybeOpenFile();
|
||||
|
||||
std::mutex _cacheFileCreateMutex;
|
||||
std::mutex _cacheFileWriteMutex;
|
||||
std::weak_ptr<storage::FileStorage> _cacheFile;
|
||||
|
||||
std::string _filename;
|
||||
uint8_t _minMipLevelAvailable;
|
||||
//storage::FileStorage _cacheFile;
|
||||
std::atomic<uint8_t> _minMipLevelAvailable;
|
||||
|
||||
ktx::KTXDescriptorPointer _ktxDescriptor;
|
||||
friend class Texture;
|
||||
};
|
||||
|
@ -470,7 +473,7 @@ public:
|
|||
|
||||
// Access the stored mips and faces
|
||||
const PixelsPointer accessStoredMipFace(uint16 level, uint8 face = 0) const { return _storage->getMipFace(level, face); }
|
||||
bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); }
|
||||
bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const;// { return _storage->isMipAvailable(level, face); }
|
||||
Size getStoredMipFaceSize(uint16 level, uint8 face = 0) const { return _storage->getMipFaceSize(level, face); }
|
||||
Size getStoredMipSize(uint16 level) const;
|
||||
Size getStoredSize() const;
|
||||
|
@ -478,6 +481,17 @@ public:
|
|||
void setStorage(std::unique_ptr<Storage>& newStorage);
|
||||
void setKtxBacking(const std::string& filename);
|
||||
|
||||
class MipInterestListener {
|
||||
public:
|
||||
virtual void handleMipInterestCallback(uint16 level) = 0;
|
||||
};
|
||||
void registerMipInterestListener(MipInterestListener* listener);
|
||||
void unregisterMipInterestListener(MipInterestListener* listener);
|
||||
std::vector<MipInterestListener*> _mipInterestListeners;
|
||||
std::mutex _mipInterestListenersMutex;
|
||||
|
||||
void requestInterestInMip(uint16 level);
|
||||
|
||||
// Usage is a a set of flags providing Semantic about the usage of the Texture.
|
||||
void setUsage(const Usage& usage) { _usage = usage; }
|
||||
Usage getUsage() const { return _usage; }
|
||||
|
|
|
@ -69,6 +69,19 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) {
|
|||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<storage::FileStorage> KtxStorage::maybeOpenFile() {
|
||||
std::shared_ptr<storage::FileStorage> file = _cacheFile.lock();
|
||||
if (file) {
|
||||
return file;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock { _cacheFileCreateMutex };
|
||||
file = std::make_shared<storage::FileStorage>(_filename.c_str());
|
||||
_cacheFile = file;
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const {
|
||||
qDebug() << "getMipFace: " << QString::fromStdString(_filename) << ": " << level << " " << face;
|
||||
storage::StoragePointer result;
|
||||
|
@ -86,9 +99,8 @@ Size KtxStorage::getMipFaceSize(uint16 level, uint8 face) const {
|
|||
|
||||
|
||||
bool KtxStorage::isMipAvailable(uint16 level, uint8 face) const {
|
||||
auto minLevel = _minMipLevelAvailable;
|
||||
auto avail = level >= minLevel;
|
||||
qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << minLevel << " " << _ktxDescriptor->header.numberOfMipmapLevels;
|
||||
auto avail = level >= _minMipLevelAvailable;
|
||||
qDebug() << "isMipAvailable: " << QString::fromStdString(_filename) << ": " << level << " " << face << avail << _minMipLevelAvailable << " " << _ktxDescriptor->header.numberOfMipmapLevels;
|
||||
//return true;
|
||||
return avail;
|
||||
}
|
||||
|
@ -108,8 +120,9 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor
|
|||
}
|
||||
|
||||
|
||||
auto fileStorage = new storage::FileStorage(_filename.c_str());
|
||||
ktx::StoragePointer file { fileStorage };
|
||||
//auto fileStorage = new storage::FileStorage(_filename.c_str());
|
||||
//ktx::StoragePointer file { fileStorage };
|
||||
auto file = maybeOpenFile();
|
||||
auto data = file->mutableData();
|
||||
data += file->size();
|
||||
|
||||
|
@ -119,8 +132,17 @@ void KtxStorage::assignMipData(uint16 level, const storage::StoragePointer& stor
|
|||
data -= 4;
|
||||
}
|
||||
data += 4;
|
||||
memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize);
|
||||
_minMipLevelAvailable = level;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock { _cacheFileWriteMutex };
|
||||
|
||||
if (level != _minMipLevelAvailable - 1) {
|
||||
qWarning() << "Invalid level to be stored";
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(data, storage->data(), _ktxDescriptor->images[level]._imageSize);
|
||||
_minMipLevelAvailable = level;
|
||||
}
|
||||
}
|
||||
|
||||
void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::StoragePointer& storage) {
|
||||
|
|
|
@ -70,7 +70,7 @@ end
|
|||
|
||||
|
||||
namespace ktx {
|
||||
const std::string HIFI_MIN_POPULATED_MIP_KEY = "hifiMinMip";
|
||||
const std::string HIFI_MIN_POPULATED_MIP_KEY = "hifi.minMip";
|
||||
|
||||
|
||||
const uint32_t PACKING_SIZE { sizeof(uint32_t) };
|
||||
|
@ -414,10 +414,10 @@ 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;
|
||||
|
|
|
@ -244,6 +244,10 @@ gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) {
|
|||
return result;
|
||||
}
|
||||
|
||||
NetworkTexture::~NetworkTexture() {
|
||||
_textureSource->getGPUTexture()->unregisterMipInterestListener(this);
|
||||
}
|
||||
|
||||
/// Returns a texture version of an image file
|
||||
gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) {
|
||||
QImage image = QImage(path);
|
||||
|
@ -371,6 +375,21 @@ void NetworkTexture::makeRequest() {
|
|||
startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL);
|
||||
}
|
||||
|
||||
void NetworkTexture::handleMipInterestCallback(uint16_t level) {
|
||||
QMetaObject::invokeMethod(this, "handleMipInterestLevel", Qt::QueuedConnection, Q_ARG(uint16_t, level));
|
||||
}
|
||||
|
||||
void NetworkTexture::handleMipInterestLevel(uint16_t level) {
|
||||
_lowestRequestedMipLevel = std::min(level, _lowestRequestedMipLevel);
|
||||
if (!_ktxMipRequest) {
|
||||
startRequestForNextMipLevel();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTexture::startRequestForNextMipLevel() {
|
||||
startMipRangeRequest(std::max(0, _lowestKnownPopulatedMip - 1), std::max(0, _lowestKnownPopulatedMip - 1));
|
||||
}
|
||||
|
||||
// Load mips in the range [low, high] (inclusive)
|
||||
void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) {
|
||||
if (_ktxMipRequest) {
|
||||
|
@ -430,6 +449,8 @@ void NetworkTexture::ktxMipRequestFinished() {
|
|||
if (_ktxMipRequest->getResult() == ResourceRequest::Success) {
|
||||
if (_initialKtxLoaded) {
|
||||
assert(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0);
|
||||
|
||||
_lowestKnownPopulatedMip = _ktxMipLevelRangeInFlight.first;
|
||||
|
||||
_textureSource->getGPUTexture()->assignStoredMip(_ktxMipLevelRangeInFlight.first,
|
||||
_ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data()));
|
||||
|
@ -493,6 +514,7 @@ void NetworkTexture::maybeCreateKTX() {
|
|||
texture.reset(gpu::Texture::unserialize(_file->getFilepath(), *_ktxDescriptor));
|
||||
texture->setKtxBacking(file->getFilepath());
|
||||
texture->setSource(filename);
|
||||
texture->registerMipInterestListener(this);
|
||||
|
||||
auto& images = _ktxDescriptor->images;
|
||||
size_t imageSizeRemaining = _ktxHighMipData.size();
|
||||
|
@ -521,6 +543,15 @@ void NetworkTexture::maybeCreateKTX() {
|
|||
texture = textureCache->cacheTextureByHash(filename, texture);
|
||||
}
|
||||
|
||||
|
||||
_lowestKnownPopulatedMip = _ktxDescriptor->header.numberOfMipmapLevels;
|
||||
for (uint16_t l = 0; l < 200; l++) {
|
||||
if (texture->isStoredMipFaceAvailable(l)) {
|
||||
_lowestKnownPopulatedMip = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setImage(texture, header->getPixelWidth(), header->getPixelHeight());
|
||||
|
||||
|
||||
|
@ -528,7 +559,8 @@ void NetworkTexture::maybeCreateKTX() {
|
|||
{
|
||||
QTimer* timer = new QTimer();
|
||||
connect(timer, &QTimer::timeout, this, [=]() {
|
||||
startMipRangeRequest(level, level);
|
||||
//startMipRangeRequest(level, level);
|
||||
startRequestForNextMipLevel();
|
||||
});
|
||||
timer->setSingleShot(true);
|
||||
timer->setInterval(4000);
|
||||
|
@ -538,7 +570,8 @@ void NetworkTexture::maybeCreateKTX() {
|
|||
{
|
||||
QTimer* timer = new QTimer();
|
||||
connect(timer, &QTimer::timeout, this, [=]() {
|
||||
startMipRangeRequest(level - 1, level - 1);
|
||||
//startMipRangeRequest(level - 1, level - 1);
|
||||
startRequestForNextMipLevel();
|
||||
});
|
||||
timer->setSingleShot(true);
|
||||
timer->setInterval(6000);
|
||||
|
|
|
@ -41,7 +41,7 @@ public:
|
|||
};
|
||||
|
||||
/// A texture loaded from the network.
|
||||
class NetworkTexture : public Resource, public Texture {
|
||||
class NetworkTexture : public Resource, public Texture, public gpu::Texture::MipInterestListener {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -57,6 +57,9 @@ public:
|
|||
|
||||
gpu::TexturePointer getFallbackTexture() const;
|
||||
|
||||
void handleMipInterestCallback(uint16_t level) override;
|
||||
Q_INVOKABLE void handleMipInterestLevel(uint16_t level);
|
||||
|
||||
signals:
|
||||
void networkTextureCreated(const QWeakPointer<NetworkTexture>& self);
|
||||
|
||||
|
@ -77,6 +80,8 @@ protected:
|
|||
Q_INVOKABLE void loadContent(const QByteArray& content);
|
||||
Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight);
|
||||
|
||||
void startRequestForNextMipLevel();
|
||||
|
||||
void startMipRangeRequest(uint16_t low, uint16_t high);
|
||||
void maybeCreateKTX();
|
||||
|
||||
|
@ -92,9 +97,7 @@ private:
|
|||
DONE_LOADING
|
||||
};
|
||||
|
||||
|
||||
bool _initialKtxLoaded { false };
|
||||
//KTXLoadState _ktxLoadState;
|
||||
KTXFilePointer _file;
|
||||
static const uint16_t NULL_MIP_LEVEL;
|
||||
bool _sourceIsKTX { false };
|
||||
|
@ -102,6 +105,8 @@ private:
|
|||
std::pair<uint16_t, uint16_t> _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL };
|
||||
ResourceRequest* _ktxHeaderRequest { nullptr };
|
||||
ResourceRequest* _ktxMipRequest { nullptr };
|
||||
uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL };
|
||||
uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL };
|
||||
QByteArray _ktxHeaderData;
|
||||
QByteArray _ktxHighMipData;
|
||||
|
||||
|
|
Loading…
Reference in a new issue