Make KTX async tasks thread safe

This commit is contained in:
Atlante45 2017-06-14 17:20:38 -07:00
parent fb8aa96a57
commit 4d3fc39ab6
2 changed files with 53 additions and 61 deletions

View file

@ -406,19 +406,18 @@ void NetworkTexture::makeRequest() {
} }
void NetworkTexture::startRequestForNextMipLevel() { void NetworkTexture::startRequestForNextMipLevel() {
PROFILE_RANGE(app, __FUNCTION__);
if (_lowestKnownPopulatedMip == 0) {
qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _lowestKnownPopulatedMip
<< " " << _textureSource->getGPUTexture()->minAvailableMipLevel() << " " << _url;
return;
}
if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) {
auto self = _self.lock(); auto self = _self.lock();
if (!self) { if (!self) {
return; return;
} }
auto texture = _textureSource->getGPUTexture();
if (!texture || _ktxResourceState != WAITING_FOR_MIP_REQUEST) {
return;
}
_lowestKnownPopulatedMip = texture->minAvailableMipLevel();
if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) {
_ktxResourceState = PENDING_MIP_REQUEST; _ktxResourceState = PENDING_MIP_REQUEST;
init(false); init(false);
@ -434,7 +433,6 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) {
if (_ktxMipRequest) { if (_ktxMipRequest) {
return; return;
} }
PROFILE_RANGE(app, __FUNCTION__);
bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL; bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL;
@ -477,7 +475,6 @@ void NetworkTexture::ktxInitialDataRequestFinished() {
// Wait for both request to be finished // Wait for both request to be finished
return; return;
} }
PROFILE_RANGE(app, __FUNCTION__);
Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA);
Q_ASSERT_X(_ktxHeaderRequest && _ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxInitialDataRequestFinished"); Q_ASSERT_X(_ktxHeaderRequest && _ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxInitialDataRequestFinished");
@ -487,6 +484,8 @@ void NetworkTexture::ktxInitialDataRequestFinished() {
{ "size_mb", _bytesTotal / 1000000.0 } { "size_mb", _bytesTotal / 1000000.0 }
}); });
PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } });
setSize(_bytesTotal); setSize(_bytesTotal);
TextureCache::requestCompleted(_self); TextureCache::requestCompleted(_self);
@ -520,8 +519,6 @@ void NetworkTexture::ktxInitialDataRequestFinished() {
} }
void NetworkTexture::ktxMipRequestFinished() { void NetworkTexture::ktxMipRequestFinished() {
PROFILE_RANGE(app, __FUNCTION__);
Q_ASSERT_X(_ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxMipRequestFinished"); Q_ASSERT_X(_ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxMipRequestFinished");
Q_ASSERT(_ktxResourceState == REQUESTING_MIP); Q_ASSERT(_ktxResourceState == REQUESTING_MIP);
@ -530,6 +527,9 @@ void NetworkTexture::ktxMipRequestFinished() {
{ "size_mb", _bytesTotal / 1000000.0 } { "size_mb", _bytesTotal / 1000000.0 }
}); });
PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } });
setSize(_bytesTotal); setSize(_bytesTotal);
if (!_ktxMipRequest || _ktxMipRequest != sender()) { if (!_ktxMipRequest || _ktxMipRequest != sender()) {
@ -550,12 +550,16 @@ void NetworkTexture::ktxMipRequestFinished() {
Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL); Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL);
Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0); Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0);
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
auto self = _self; auto self = _self;
auto url = _url;
auto data = _ktxMipRequest->getData(); auto data = _ktxMipRequest->getData();
auto mipLevel = _ktxMipLevelRangeInFlight.first; auto mipLevel = _ktxMipLevelRangeInFlight.first;
auto texture = _textureSource->getGPUTexture();
DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing"); DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
QtConcurrent::run(QThreadPool::globalInstance(), [this, self, data, mipLevel] { QtConcurrent::run(QThreadPool::globalInstance(), [self, data, mipLevel, url, texture] {
PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Mip Data", 0xffff0000, 0, { { "url", _url.toString() } }); PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Mip Data", 0xffff0000, 0, { { "url", url.toString() } });
DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing"); DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing");
CounterStat counter("Processing"); CounterStat counter("Processing");
@ -566,33 +570,22 @@ void NetworkTexture::ktxMipRequestFinished() {
QThread::currentThread()->setPriority(QThread::LowPriority); QThread::currentThread()->setPriority(QThread::LowPriority);
Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); }); Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); });
auto that = self.lock(); auto resource = self.lock();
if (!that) { if (!resource) {
// Resource no longer exists, bail // Resource no longer exists, bail
return; return;
} }
auto texture = _textureSource->getGPUTexture(); Q_ASSERT_X(texture, "Async - NetworkTexture::ktxMipRequestFinished", "NetworkTexture should have been assigned a GPU texture by now.");
if (texture) {
texture->assignStoredMip(mipLevel, data.size(), reinterpret_cast<const uint8_t*>(data.data())); texture->assignStoredMip(mipLevel, data.size(), reinterpret_cast<const uint8_t*>(data.data()));
_lowestKnownPopulatedMip = texture->minAvailableMipLevel(); QMetaObject::invokeMethod(resource.data(), "setImage",
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
QMetaObject::invokeMethod(this, "setImage",
Q_ARG(gpu::TexturePointer, texture), Q_ARG(gpu::TexturePointer, texture),
Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getWidth()),
Q_ARG(int, texture->getHeight())); Q_ARG(int, texture->getHeight()));
if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) { QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel");
QMetaObject::invokeMethod(this, "startRequestForNextMipLevel");
}
} else {
QMetaObject::invokeMethod(this, "setImage",
Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0),
Q_ARG(int, 0));
}
}); });
} else { } else {
qWarning(networking) << "Mip request finished in an unexpected state: " << _ktxResourceState; qWarning(networking) << "Mip request finished in an unexpected state: " << _ktxResourceState;
@ -616,18 +609,19 @@ void NetworkTexture::handleFinishedInitialLoad() {
Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA);
Q_ASSERT(!_ktxHeaderData.isEmpty() && !_ktxHighMipData.isEmpty()); Q_ASSERT(!_ktxHeaderData.isEmpty() && !_ktxHighMipData.isEmpty());
PROFILE_RANGE(app, __FUNCTION__);
// create ktx... // create ktx...
auto ktxHeaderData = _ktxHeaderData; auto ktxHeaderData = _ktxHeaderData;
auto ktxHighMipData = _ktxHighMipData; auto ktxHighMipData = _ktxHighMipData;
_ktxHeaderData.clear(); _ktxHeaderData.clear();
_ktxHighMipData.clear(); _ktxHighMipData.clear();
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
auto self = _self; auto self = _self;
auto url = _url;
DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing"); DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
QtConcurrent::run(QThreadPool::globalInstance(), [this, self, ktxHeaderData, ktxHighMipData] { QtConcurrent::run(QThreadPool::globalInstance(), [self, ktxHeaderData, ktxHighMipData, url] {
PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Initial Data", 0xffff0000, 0, { { "url", _url.toString() } }); PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Initial Data", 0xffff0000, 0, { { "url", url.toString() } });
DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing"); DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing");
CounterStat counter("Processing"); CounterStat counter("Processing");
@ -638,8 +632,8 @@ void NetworkTexture::handleFinishedInitialLoad() {
QThread::currentThread()->setPriority(QThread::LowPriority); QThread::currentThread()->setPriority(QThread::LowPriority);
Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); }); Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); });
auto that = self.lock(); auto resource = self.lock();
if (!that) { if (!resource) {
// Resource no longer exists, bail // Resource no longer exists, bail
return; return;
} }
@ -647,8 +641,8 @@ void NetworkTexture::handleFinishedInitialLoad() {
auto header = reinterpret_cast<const ktx::Header*>(ktxHeaderData.data()); auto header = reinterpret_cast<const ktx::Header*>(ktxHeaderData.data());
if (!ktx::checkIdentifier(header->identifier)) { if (!ktx::checkIdentifier(header->identifier)) {
qWarning() << "Cannot load " << _url << ", invalid header identifier"; qWarning() << "Cannot load " << url << ", invalid header identifier";
QMetaObject::invokeMethod(this, "setImage", QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0), Q_ARG(int, 0),
Q_ARG(int, 0)); Q_ARG(int, 0));
@ -657,8 +651,8 @@ void NetworkTexture::handleFinishedInitialLoad() {
auto kvSize = header->bytesOfKeyValueData; auto kvSize = header->bytesOfKeyValueData;
if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) { if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) {
qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request"; qWarning() << "Cannot load " << url << ", did not receive all kv data with initial request";
QMetaObject::invokeMethod(this, "setImage", QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0), Q_ARG(int, 0),
Q_ARG(int, 0)); Q_ARG(int, 0));
@ -669,14 +663,16 @@ void NetworkTexture::handleFinishedInitialLoad() {
auto imageDescriptors = header->generateImageDescriptors(); auto imageDescriptors = header->generateImageDescriptors();
if (imageDescriptors.size() == 0) { if (imageDescriptors.size() == 0) {
qWarning(networking) << "Failed to process ktx file " << _url; qWarning(networking) << "Failed to process ktx file " << url;
QMetaObject::invokeMethod(this, "setImage", QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0), Q_ARG(int, 0),
Q_ARG(int, 0)); Q_ARG(int, 0));
return; return;
} }
_originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors)); auto originalKtxDescriptor = new ktx::KTXDescriptor(*header, keyValues, imageDescriptors);
QMetaObject::invokeMethod(resource.data(), "setOriginalDescriptor",
Q_ARG(ktx::KTXDescriptor*, originalKtxDescriptor));
// Create bare ktx in memory // Create bare ktx in memory
auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool { auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool {
@ -686,7 +682,7 @@ void NetworkTexture::handleFinishedInitialLoad() {
std::string hash; std::string hash;
if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) { if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) {
qWarning("Invalid source hash key found, bailing"); qWarning("Invalid source hash key found, bailing");
QMetaObject::invokeMethod(this, "setImage", QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0), Q_ARG(int, 0),
Q_ARG(int, 0)); Q_ARG(int, 0));
@ -717,7 +713,7 @@ void NetworkTexture::handleFinishedInitialLoad() {
auto memKtx = ktx::KTX::createBare(*header, keyValues); auto memKtx = ktx::KTX::createBare(*header, keyValues);
if (!memKtx) { if (!memKtx) {
qWarning() << " Ktx could not be created, bailing"; qWarning() << " Ktx could not be created, bailing";
QMetaObject::invokeMethod(this, "setImage", QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0), Q_ARG(int, 0),
Q_ARG(int, 0)); Q_ARG(int, 0));
@ -730,8 +726,8 @@ void NetworkTexture::handleFinishedInitialLoad() {
KTXFilePointer file; KTXFilePointer file;
auto& ktxCache = textureCache->_ktxCache; auto& ktxCache = textureCache->_ktxCache;
if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) { if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) {
qCWarning(modelnetworking) << _url << " failed to write cache file"; qCWarning(modelnetworking) << url << " failed to write cache file";
QMetaObject::invokeMethod(this, "setImage", QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr), Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0), Q_ARG(int, 0),
Q_ARG(int, 0)); Q_ARG(int, 0));
@ -746,7 +742,7 @@ void NetworkTexture::handleFinishedInitialLoad() {
texture->setKtxBacking(file->getFilepath()); texture->setKtxBacking(file->getFilepath());
texture->setSource(filename); texture->setSource(filename);
auto& images = _originalKtxDescriptor->images; auto& images = originalKtxDescriptor->images;
size_t imageSizeRemaining = ktxHighMipData.size(); size_t imageSizeRemaining = ktxHighMipData.size();
const uint8_t* ktxData = reinterpret_cast<const uint8_t*>(ktxHighMipData.data()); const uint8_t* ktxData = reinterpret_cast<const uint8_t*>(ktxHighMipData.data());
ktxData += ktxHighMipData.size(); ktxData += ktxHighMipData.size();
@ -768,18 +764,12 @@ void NetworkTexture::handleFinishedInitialLoad() {
texture = textureCache->cacheTextureByHash(filename, texture); texture = textureCache->cacheTextureByHash(filename, texture);
} }
_lowestKnownPopulatedMip = texture->minAvailableMipLevel(); QMetaObject::invokeMethod(resource.data(), "setImage",
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
QMetaObject::invokeMethod(this, "setImage",
Q_ARG(gpu::TexturePointer, texture), Q_ARG(gpu::TexturePointer, texture),
Q_ARG(int, texture->getWidth()), Q_ARG(int, texture->getWidth()),
Q_ARG(int, texture->getHeight())); Q_ARG(int, texture->getHeight()));
if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) { QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel");
QMetaObject::invokeMethod(this, "startRequestForNextMipLevel");
}
}); });
} }

View file

@ -58,6 +58,8 @@ public:
void refresh() override; void refresh() override;
Q_INVOKABLE void setOriginalDescriptor(ktx::KTXDescriptor* descriptor) { _originalKtxDescriptor.reset(descriptor); }
signals: signals:
void networkTextureCreated(const QWeakPointer<NetworkTexture>& self); void networkTextureCreated(const QWeakPointer<NetworkTexture>& self);