foundations for background caching
This commit is contained in:
@@ -20,10 +20,19 @@
|
||||
|
||||
#include "renderer.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QtMath>
|
||||
|
||||
#include "render/rendertypes.h"
|
||||
#include "rendererprobe.h"
|
||||
|
||||
RendererProcessor::RendererProcessor() :
|
||||
started_(false)
|
||||
started_(false),
|
||||
width_(0),
|
||||
height_(0)
|
||||
{
|
||||
texture_input_ = new NodeInput("tex_in");
|
||||
texture_input_->add_data_input(NodeInput::kTexture);
|
||||
@@ -54,27 +63,73 @@ QString RendererProcessor::id()
|
||||
return "org.olivevideoeditor.Olive.renderervenus";
|
||||
}
|
||||
|
||||
void RendererProcessor::SetCacheName(const QString &s)
|
||||
{
|
||||
cache_name_ = s;
|
||||
cache_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
GenerateCacheIDInternal();
|
||||
}
|
||||
|
||||
void RendererProcessor::Process(const rational &time)
|
||||
{
|
||||
Q_UNUSED(time)
|
||||
|
||||
texture_output_->set_value(0);
|
||||
|
||||
if (!texture_input_->IsConnected()) {
|
||||
// Nothing is connected - nothing to show or render
|
||||
return;
|
||||
}
|
||||
|
||||
if (cache_id_.isEmpty()) {
|
||||
qWarning() << "RendererProcessor has no cache ID";
|
||||
return;
|
||||
}
|
||||
|
||||
if (timebase_.isNull()) {
|
||||
qWarning() << "RendererProcessor has no timebase";
|
||||
return;
|
||||
}
|
||||
|
||||
// This Renderer node relies on a disk cache so this Process() function should be quite fast. Either it returns the
|
||||
// cached frame or it returns nothing.
|
||||
|
||||
// Perhaps it should lookahead to load textures into VRAM in advance?
|
||||
|
||||
// Should it cache the final result or the result in an 8-bit image or a 16-bit intermediate image?
|
||||
|
||||
/*qDebug() << QString("Requesting %1/%2").arg(QString::number(time.numerator()), QString::number(time.denominator()));
|
||||
|
||||
if (cache_map_.contains(time)) {
|
||||
qDebug() << " We have this frame!";
|
||||
} else {
|
||||
qDebug() << " No frame at this address";
|
||||
cache_map_.insert(time, true);
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// FIXME: Test code only
|
||||
GLuint tex = texture_input_->get_value(time).value<GLuint>();
|
||||
//GLuint tex = texture_input_->get_value(time).value<GLuint>();
|
||||
|
||||
//glReadPixels()
|
||||
|
||||
texture_output_->set_value(tex);
|
||||
//texture_output_->set_value(tex);
|
||||
// End test code
|
||||
|
||||
/*
|
||||
// Ensure we have started
|
||||
Start();
|
||||
|
||||
if (!started_) {
|
||||
qWarning() << tr("An error occurred starting the Renderer node");
|
||||
return;
|
||||
}
|
||||
Start();
|
||||
|
||||
if (!started_) {
|
||||
qWarning() << tr("An error occurred starting the Renderer node");
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -85,8 +140,45 @@ void RendererProcessor::Release()
|
||||
|
||||
void RendererProcessor::InvalidateCache(const rational &start_range, const rational &end_range)
|
||||
{
|
||||
Q_UNUSED(start_range)
|
||||
Q_UNUSED(end_range)
|
||||
qDebug() << "[RendererProcessor] Cache invalidated between"
|
||||
<< start_range.toDouble()
|
||||
<< "and"
|
||||
<< end_range.toDouble();
|
||||
|
||||
for (rational r=start_range;r<=end_range;r+=timebase_) {
|
||||
if (!cache_queue_.contains(r)) {
|
||||
cache_queue_.append(r);
|
||||
}
|
||||
}
|
||||
|
||||
CacheCallback();
|
||||
|
||||
Node::InvalidateCache(start_range, end_range);
|
||||
}
|
||||
|
||||
void RendererProcessor::SetTimebase(const rational &timebase)
|
||||
{
|
||||
timebase_ = timebase;
|
||||
timebase_dbl_ = timebase_.toDouble();
|
||||
}
|
||||
|
||||
void RendererProcessor::SetParameters(const int &width,
|
||||
const int &height,
|
||||
const olive::PixelFormat &format,
|
||||
const olive::RenderMode &mode)
|
||||
{
|
||||
// Since we're changing parameters, all the existing threads are invalid and must be removed. They will start again
|
||||
// next time this Node has to process anything.
|
||||
Stop();
|
||||
|
||||
// Set new parameters
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
format_ = format;
|
||||
mode_ = mode;
|
||||
|
||||
// Regenerate the cache ID
|
||||
GenerateCacheIDInternal();
|
||||
}
|
||||
|
||||
void RendererProcessor::Start()
|
||||
@@ -98,8 +190,8 @@ void RendererProcessor::Start()
|
||||
threads_.resize(QThread::idealThreadCount());
|
||||
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
threads_[i] = std::make_shared<RendererThread>();
|
||||
threads_[i]->run();
|
||||
threads_[i] = std::make_shared<RendererThread>(width_, height_, format_, mode_);
|
||||
threads_[i]->StartThread(QThread::HighPriority);
|
||||
}
|
||||
|
||||
started_ = true;
|
||||
@@ -120,11 +212,77 @@ void RendererProcessor::Stop()
|
||||
threads_.clear();
|
||||
}
|
||||
|
||||
void RendererProcessor::GenerateCacheIDInternal()
|
||||
{
|
||||
if (cache_name_.isEmpty() || width_ == 0 || height_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate an ID that is more or less guaranteed to be unique to this Sequence
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
hash.addData(cache_name_.toUtf8());
|
||||
hash.addData(QString::number(cache_time_).toUtf8());
|
||||
hash.addData(QString::number(width_).toUtf8());
|
||||
hash.addData(QString::number(height_).toUtf8());
|
||||
hash.addData(QString::number(format_).toUtf8());
|
||||
|
||||
QByteArray bytes = hash.result();
|
||||
cache_id_ = bytes.toHex();
|
||||
}
|
||||
|
||||
void RendererProcessor::CacheCallback()
|
||||
{
|
||||
if (cache_queue_.isEmpty() || !texture_input_->IsConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rational time_to_cache = cache_queue_.first();
|
||||
|
||||
Node* node_to_cache = texture_input_->edges().first()->output()->parent();
|
||||
|
||||
RendererProbe::ProbeNode(node_to_cache, QThread::idealThreadCount(), time_to_cache);
|
||||
|
||||
/*
|
||||
// Make sure cache has started
|
||||
Start();
|
||||
|
||||
bool caching = false;
|
||||
|
||||
// Look for a thread that's available
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
RendererThreadPtr thread = threads_.at(i);
|
||||
|
||||
if (thread->Queue(node_to_cache, time_to_cache)) {
|
||||
// This thread is free and we've just taken control of it
|
||||
caching = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (caching) {
|
||||
cache_queue_.removeFirst();
|
||||
|
||||
qDebug() << "[RendererProcessor] Ready to cache" << time_to_cache.numerator() << "/" << time_to_cache.denominator();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
RendererThread* RendererProcessor::CurrentThread()
|
||||
{
|
||||
return dynamic_cast<RendererThread*>(QThread::currentThread());
|
||||
}
|
||||
|
||||
RenderInstance *RendererProcessor::CurrentInstance()
|
||||
{
|
||||
RendererThread* thread = CurrentThread();
|
||||
|
||||
if (thread != nullptr) {
|
||||
return thread->render_instance();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NodeInput *RendererProcessor::texture_input()
|
||||
{
|
||||
return texture_input_;
|
||||
|
||||
Reference in New Issue
Block a user