Merge pull request #7553 from zzmp/fix/tex-invoke-method

Fix ImageReader threading issues
This commit is contained in:
Brad Hefta-Gaub 2016-04-04 18:29:24 -07:00
commit ff4f693267

View file

@ -13,20 +13,22 @@
#include <mutex> #include <mutex>
#include <glm/glm.hpp>
#include <glm/gtc/random.hpp>
#include <QNetworkReply> #include <QNetworkReply>
#include <QPainter> #include <QPainter>
#include <QRunnable> #include <QRunnable>
#include <QThreadPool> #include <QThreadPool>
#include <qimagereader.h> #include <QImageReader>
#include <shared/NsightHelpers.h> #include <glm/glm.hpp>
#include <PathUtils.h> #include <glm/gtc/random.hpp>
#include <gpu/Batch.h> #include <gpu/Batch.h>
#include <shared/NsightHelpers.h>
#include <Finally.h>
#include <PathUtils.h>
#include "ModelNetworkingLogging.h" #include "ModelNetworkingLogging.h"
TextureCache::TextureCache() { TextureCache::TextureCache() {
@ -198,7 +200,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture
{ {
_textureLoader = textureLoader; _textureLoader = textureLoader;
} }
NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
switch (_type) { switch (_type) {
case CUBE_TEXTURE: { case CUBE_TEXTURE: {
@ -242,14 +244,14 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
class ImageReader : public QRunnable { class ImageReader : public QRunnable {
public: public:
ImageReader(const QWeakPointer<Resource>& texture, const QByteArray& data, const QUrl& url = QUrl()); ImageReader(const QWeakPointer<Resource>& resource, const QByteArray& data, const QUrl& url = QUrl());
virtual void run(); virtual void run();
private: private:
static void listSupportedImageFormats(); static void listSupportedImageFormats();
QWeakPointer<Resource> _texture; QWeakPointer<Resource> _resource;
QUrl _url; QUrl _url;
QByteArray _content; QByteArray _content;
}; };
@ -263,9 +265,9 @@ void NetworkTexture::loadContent(const QByteArray& content) {
QThreadPool::globalInstance()->start(new ImageReader(_self, content, _url)); QThreadPool::globalInstance()->start(new ImageReader(_self, content, _url));
} }
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, const QByteArray& data, ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QByteArray& data,
const QUrl& url) : const QUrl& url) :
_texture(texture), _resource(resource),
_url(url), _url(url),
_content(data) _content(data)
{ {
@ -286,26 +288,28 @@ void ImageReader::run() {
originalPriority = QThread::NormalPriority; originalPriority = QThread::NormalPriority;
} }
QThread::currentThread()->setPriority(QThread::LowPriority); QThread::currentThread()->setPriority(QThread::LowPriority);
Finally restorePriority([originalPriority]{
QThread::currentThread()->setPriority(originalPriority);
});
auto texture = _texture.toStrongRef(); if (!_resource.data()) {
if (!texture) { qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
qCWarning(modelnetworking) << "Could not get strong ref";
return; return;
} }
listSupportedImageFormats(); listSupportedImageFormats();
// try to help the QImage loader by extracting the image file format from the url filename ext // Help the QImage loader by extracting the image file format from the url filename ext.
// Some tga are not created properly for example without it // Some tga are not created properly without it.
auto filename = _url.fileName().toStdString(); auto filename = _url.fileName().toStdString();
auto filenameExtension = filename.substr(filename.find_last_of('.') + 1); auto filenameExtension = filename.substr(filename.find_last_of('.') + 1);
QImage image = QImage::fromData(_content, filenameExtension.c_str()); QImage image = QImage::fromData(_content, filenameExtension.c_str());
// Note that QImage.format is the pixel format which is different from the "format" of the image file... // Note that QImage.format is the pixel format which is different from the "format" of the image file...
auto imageFormat = image.format(); auto imageFormat = image.format();
int originalWidth = image.width(); int originalWidth = image.width();
int originalHeight = image.height(); int originalHeight = image.height();
if (originalWidth == 0 || originalHeight == 0 || imageFormat == QImage::Format_Invalid) { if (originalWidth == 0 || originalHeight == 0 || imageFormat == QImage::Format_Invalid) {
if (filenameExtension.empty()) { if (filenameExtension.empty()) {
qCDebug(modelnetworking) << "QImage failed to create from content, no file extension:" << _url; qCDebug(modelnetworking) << "QImage failed to create from content, no file extension:" << _url;
@ -315,26 +319,40 @@ void ImageReader::run() {
return; return;
} }
gpu::Texture* theTexture = nullptr; gpu::Texture* texture = nullptr;
auto ntex = texture.dynamicCast<NetworkTexture>(); {
if (ntex) { // Double-check the resource still exists between long operations.
auto resource = _resource.toStrongRef();
if (!resource) {
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
return;
}
auto url = _url.toString().toStdString();
PROFILE_RANGE_EX(__FUNCTION__"::textureLoader", 0xffffff00, nullptr); PROFILE_RANGE_EX(__FUNCTION__"::textureLoader", 0xffffff00, nullptr);
theTexture = ntex->getTextureLoader()(image, _url.toString().toStdString()); texture = resource.dynamicCast<NetworkTexture>()->getTextureLoader()(image, url);
} }
QMetaObject::invokeMethod(texture.data(), "setImage", // Ensure the resource has not been deleted, and won't be while invokeMethod is in flight.
Q_ARG(void*, theTexture), auto resource = _resource.toStrongRef();
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight)); if (!resource) {
QThread::currentThread()->setPriority(originalPriority); qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
delete texture;
} else {
QMetaObject::invokeMethod(resource.data(), "setImage", Qt::BlockingQueuedConnection,
Q_ARG(void*, texture),
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
}
} }
void NetworkTexture::setImage(void* voidTexture, int originalWidth, void NetworkTexture::setImage(void* voidTexture, int originalWidth,
int originalHeight) { int originalHeight) {
_originalWidth = originalWidth; _originalWidth = originalWidth;
_originalHeight = originalHeight; _originalHeight = originalHeight;
gpu::Texture* texture = static_cast<gpu::Texture*>(voidTexture); gpu::Texture* texture = static_cast<gpu::Texture*>(voidTexture);
// Passing ownership // Passing ownership
_textureSource->resetTexture(texture); _textureSource->resetTexture(texture);
auto gpuTexture = _textureSource->getGPUTexture(); auto gpuTexture = _textureSource->getGPUTexture();