loading images on their own thread

This commit is contained in:
fourtf 2018-08-09 18:39:46 +02:00
parent bb76a632f4
commit 6344fa6b23
5 changed files with 96 additions and 30 deletions

View file

@ -98,8 +98,6 @@ void Channel::addOrReplaceTimeout(MessagePtr message)
for (int i = snapshotLength - 1; i >= end; --i) { for (int i = snapshotLength - 1; i >= end; --i) {
auto &s = snapshot[i]; auto &s = snapshot[i];
qDebug() << s->parseTime << minimumTime;
if (s->parseTime < minimumTime) { if (s->parseTime < minimumTime) {
break; break;
} }

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <QDebug> #include "debug/Log.hpp"
#include <QElapsedTimer> #include <QElapsedTimer>
#include <boost/current_function.hpp> #include <boost/current_function.hpp>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
@ -29,8 +30,7 @@ public:
~BenchmarkGuard() ~BenchmarkGuard()
{ {
qDebug() << this->name << float(timer.nsecsElapsed()) / 1000000.0f Log("{} {} ms", this->name, float(timer.nsecsElapsed()) / 1000000.0f);
<< "ms";
} }
qreal getElapsedMs() qreal getElapsedMs()

View file

@ -3,6 +3,7 @@
#include "Application.hpp" #include "Application.hpp"
#include "common/NetworkRequest.hpp" #include "common/NetworkRequest.hpp"
#include "debug/AssertInGuiThread.hpp" #include "debug/AssertInGuiThread.hpp"
#include "debug/Benchmark.hpp"
#include "debug/Log.hpp" #include "debug/Log.hpp"
#include "singletons/Emotes.hpp" #include "singletons/Emotes.hpp"
#include "singletons/WindowManager.hpp" #include "singletons/WindowManager.hpp"
@ -15,7 +16,6 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QTimer> #include <QTimer>
#include <functional> #include <functional>
#include <thread> #include <thread>
@ -81,11 +81,11 @@ const QPixmap *Frames::first() const
} }
// functions // functions
std::vector<Frame> readFrames(QImageReader &reader, const Url &url) std::vector<ParseFrame> readFrames(QImageReader &reader, const Url &url)
{ {
std::vector<Frame> frames; std::vector<ParseFrame> frames;
if (reader.imageCount() <= 0) { if (reader.imageCount() == 0) {
Log("Error while reading image {}: '{}'", url.string, Log("Error while reading image {}: '{}'", url.string,
reader.errorString()); reader.errorString());
return frames; return frames;
@ -94,14 +94,14 @@ std::vector<Frame> readFrames(QImageReader &reader, const Url &url)
QImage image; QImage image;
for (int index = 0; index < reader.imageCount(); ++index) { for (int index = 0; index < reader.imageCount(); ++index) {
if (reader.read(&image)) { if (reader.read(&image)) {
auto pixmap = std::make_unique<QPixmap>(QPixmap::fromImage(image)); QPixmap::fromImage(image);
int duration = std::max(20, reader.nextImageDelay()); int duration = std::max(20, reader.nextImageDelay());
frames.push_back(Frame{std::move(pixmap), duration}); frames.push_back(ParseFrame{image, duration});
} }
} }
if (frames.size() != 0) { if (frames.size() == 0) {
Log("Error while reading image {}: '{}'", url.string, Log("Error while reading image {}: '{}'", url.string,
reader.errorString()); reader.errorString());
} }
@ -123,11 +123,65 @@ void queueLoadedEvent()
}); });
} }
} }
// parsed
template <typename Assign>
void asd(std::queue<std::pair<Assign, std::vector<Frame>>> &queued,
std::mutex &mutex, std::atomic_bool &loadedEventQueued)
{
std::lock_guard<std::mutex> lock(mutex);
int i = 0;
while (!queued.empty()) {
queued.front().first(std::move(queued.front().second));
queued.pop();
if (++i > 50) {
QTimer::singleShot(3,
[&] { asd(queued, mutex, loadedEventQueued); });
return;
}
}
getApp()->windows->forceLayoutChannelViews();
loadedEventQueued = false;
}
template <typename Assign>
auto makeConvertCallback(std::vector<ParseFrame> parsed, Assign assign)
{
return [parsed = std::move(parsed), assign] {
// BenchmarkGuard guard("convert image");
// convert to pixmap
auto frames = std::vector<Frame>();
std::transform(parsed.begin(), parsed.end(), std::back_inserter(frames),
[](auto &frame) {
return Frame{std::make_unique<QPixmap>(
QPixmap::fromImage(frame.image)),
frame.duration};
});
// put into stack
static std::queue<std::pair<Assign, std::vector<Frame>>> queued;
static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex);
queued.emplace(assign, std::move(frames));
static std::atomic_bool loadedEventQueued{false};
if (!loadedEventQueued) {
loadedEventQueued = true;
QTimer::singleShot(100,
[=] { asd(queued, mutex, loadedEventQueued); });
}
};
}
} // namespace } // namespace
// IMAGE2 // IMAGE2
std::atomic<bool> Image::loadedEventQueued{false};
ImagePtr Image::fromUrl(const Url &url, qreal scale) ImagePtr Image::fromUrl(const Url &url, qreal scale)
{ {
static std::unordered_map<Url, std::weak_ptr<Image>> cache; static std::unordered_map<Url, std::weak_ptr<Image>> cache;
@ -249,25 +303,36 @@ void Image::load()
NetworkRequest req(this->url().string); NetworkRequest req(this->url().string);
req.setCaller(&this->object_); req.setCaller(&this->object_);
req.setUseQuickLoadCache(true); req.setUseQuickLoadCache(true);
req.onSuccess([this, weak = weakOf(this)](auto result) -> Outcome { req.onSuccess([that = this, weak = weakOf(this)](auto result) -> Outcome {
assertInGuiThread(); assertInGuiThread();
auto shared = weak.lock(); auto shared = weak.lock();
if (!shared) return Failure; if (!shared) return Failure;
// const cast since we are only reading from it static auto parseThread = [] {
QBuffer buffer(const_cast<QByteArray *>(&result.getData())); auto thread = std::make_unique<QThread>();
buffer.open(QIODevice::ReadOnly); thread->start();
QImageReader reader(&buffer); return thread;
}();
this->frames_ = readFrames(reader, this->url()); postToThread(
[data = result.getData(), weak, that] {
// BenchmarkGuard guard("parse image");
// const cast since we are only reading from it
QBuffer buffer(const_cast<QByteArray *>(&data));
buffer.open(QIODevice::ReadOnly);
QImageReader reader(&buffer);
auto parsed = readFrames(reader, that->url());
postToThread(
makeConvertCallback(std::move(parsed), [weak](auto frames) {
if (auto shared = weak.lock())
shared->frames_ = std::move(frames);
}));
},
parseThread.get());
if (!loadedEventQueued) {
QTimer::singleShot(150, [] {
getApp()->windows->forceLayoutChannelViews();
loadedEventQueued = false;
});
}
return Success; return Success;
}); });

View file

@ -4,6 +4,7 @@
#include <QPixmap> #include <QPixmap>
#include <QString> #include <QString>
#include <QThread>
#include <atomic> #include <atomic>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <boost/variant.hpp> #include <boost/variant.hpp>
@ -19,6 +20,10 @@ struct Frame {
Pixmap pixmap; Pixmap pixmap;
int duration; int duration;
}; };
struct ParseFrame {
QImage image;
int duration;
};
class Frames class Frames
{ {
public: public:
@ -77,7 +82,5 @@ private:
bool shouldLoad_{false}; bool shouldLoad_{false};
Frames frames_{}; Frames frames_{};
QObject object_{}; QObject object_{};
static std::atomic<bool> loadedEventQueued;
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -205,7 +205,7 @@ void ChannelView::layoutMessages()
void ChannelView::actuallyLayoutMessages(bool causedByScrollbar) void ChannelView::actuallyLayoutMessages(bool causedByScrollbar)
{ {
// BenchmarkGuard benchmark("layout messages"); // BenchmarkGuard benchmark("layout");
auto app = getApp(); auto app = getApp();
@ -633,7 +633,7 @@ void ChannelView::updatePauseStatus()
void ChannelView::paintEvent(QPaintEvent * /*event*/) void ChannelView::paintEvent(QPaintEvent * /*event*/)
{ {
// BenchmarkGuard benchmark("paint event"); // BenchmarkGuard benchmark("paint");
QPainter painter(this); QPainter painter(this);