diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 3813ee347..dab602ae7 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -76,6 +76,7 @@ target_link_libraries(${OLIVE_TARGET} FFMPEG::swscale FFMPEG::swresample OpenColorIO + OpenImageIO ) set(OLIVE_EFFECTS diff --git a/app/common/define.h b/app/common/define.h new file mode 100644 index 000000000..37ff4cc5d --- /dev/null +++ b/app/common/define.h @@ -0,0 +1,26 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef OLIVECOMMONDEFINE_H +#define OLIVECOMMONDEFINE_H + +const int kRGBAChannels = 4; + +#endif // OLIVECOMMONDEFINE_H diff --git a/app/core.cpp b/app/core.cpp index 9a12dd5c5..37ee31929 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -280,7 +280,8 @@ void Core::CreateNewSequence() rp->SetParameters(new_sequence->video_width(), new_sequence->video_height(), olive::PIX_FMT_RGBA16F, // FIXME: Make this configurable - olive::RenderMode::kOffline); + olive::RenderMode::kOffline, + 2); // Set the "cache name" only here to aid the cache ID's uniqueness rp->SetCacheName(new_sequence->name()); diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 9363e7d5c..f2d0981c0 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -223,9 +223,6 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &time) renderer->buffer()->Attach(output_texture); renderer->buffer()->Bind(); - glClearColor(1.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - // Draw with the internal texture internal_tex_.Bind(); diff --git a/app/node/processor/renderer/renderer.cpp b/app/node/processor/renderer/renderer.cpp index 33eb536f6..e5b8f3cb4 100644 --- a/app/node/processor/renderer/renderer.cpp +++ b/app/node/processor/renderer/renderer.cpp @@ -20,6 +20,7 @@ #include "renderer.h" +#include #include #include #include @@ -29,11 +30,13 @@ #include #include "common/filefunctions.h" +#include "render/pixelservice.h" RendererProcessor::RendererProcessor() : started_(false), width_(0), height_(0), + divider_(1), caching_(false) { texture_input_ = new NodeInput("tex_in"); @@ -92,23 +95,21 @@ QVariant RendererProcessor::Value(NodeOutput* output, const rational& time) return 0; } - // FIXME: Test code only QString fn = CachePathName(time); if (QFileInfo::exists(fn)) { - QFile cache_img(fn); + auto in = OIIO::ImageInput::open(fn.toStdString()); - if (cache_img.open(QFile::ReadOnly)) { + if (in) { + in->read_image(PixelService::GetPixelFormatInfo(format_).oiio_desc, cache_frame_load_buffer_.data()); - QByteArray bytes = cache_img.readAll(); + in->close(); - master_texture_->Upload(bytes.constData()); - - cache_img.close(); + master_texture_->Upload(cache_frame_load_buffer_.data()); return QVariant::fromValue(master_texture_); } + } - // End test code } return 0; @@ -152,7 +153,8 @@ void RendererProcessor::SetTimebase(const rational &timebase) void RendererProcessor::SetParameters(const int &width, const int &height, const olive::PixelFormat &format, - const olive::RenderMode &mode) + const olive::RenderMode &mode, + const int& divider) { // 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. @@ -164,6 +166,27 @@ void RendererProcessor::SetParameters(const int &width, format_ = format; mode_ = mode; + // divider's default value is 0, so we can assume if it's 0 a divider wasn't specified + if (divider > 0) { + divider_ = divider; + } + + CalculateEffectiveDimensions(); + + // Regenerate the cache ID + GenerateCacheIDInternal(); +} + +void RendererProcessor::SetDivider(const int ÷r) +{ + Q_ASSERT(divider_ > 0); + + Stop(); + + divider_ = divider; + + CalculateEffectiveDimensions(); + // Regenerate the cache ID GenerateCacheIDInternal(); } @@ -176,11 +199,13 @@ void RendererProcessor::Start() QOpenGLContext* ctx = QOpenGLContext::currentContext(); - threads_.resize(QThread::idealThreadCount()); + int background_thread_count = qMax(1, QThread::idealThreadCount() - 1); + + threads_.resize(background_thread_count); for (int i=0;i(ctx, width_, height_, format_, mode_); - threads_[i]->StartThread(QThread::HighPriority); + threads_[i] = std::make_shared(ctx, effective_width_, effective_height_, format_, mode_); + threads_[i]->StartThread(QThread::LowPriority); // Ensure this connection is "Queued" so that it always runs in this object's threaded rather than any of the // other threads @@ -188,13 +213,21 @@ void RendererProcessor::Start() connect(threads_[i].get(), SIGNAL(RequestSibling(NodeDependency)), this, SLOT(ThreadRequestSibling(NodeDependency)), Qt::QueuedConnection); } - // Create download thread - download_thread_ = std::make_shared(ctx, width_, height_, format_, mode_); - download_thread_->StartThread(QThread::HighPriority); + download_threads_.resize(background_thread_count); + + for (int i=0;i(ctx, effective_width_, effective_height_, format_, mode_); + download_threads_[i]->StartThread(QThread::LowPriority); + } + + last_download_thread_ = 0; // Create master texture (the one sent to the viewer) master_texture_ = std::make_shared(); - master_texture_->Create(ctx, width_, height_, format_); + master_texture_->Create(ctx, effective_width_, effective_height_, format_); + + cache_frame_load_buffer_.resize(PixelService::GetBufferSize(format_, effective_width_, effective_height_)); started_ = true; } @@ -212,15 +245,19 @@ void RendererProcessor::Stop() } threads_.clear(); - download_thread_->Cancel(); - download_thread_ = nullptr; + foreach (RendererDownloadThreadPtr download_thread_, download_threads_) { + download_thread_->Cancel(); + } + download_threads_.clear(); master_texture_ = nullptr; + + cache_frame_load_buffer_.clear(); } void RendererProcessor::GenerateCacheIDInternal() { - if (cache_name_.isEmpty() || width_ == 0 || height_ == 0) { + if (cache_name_.isEmpty() || effective_width_ == 0 || effective_height_ == 0) { return; } @@ -228,8 +265,8 @@ void RendererProcessor::GenerateCacheIDInternal() 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(effective_width_).toUtf8()); + hash.addData(QString::number(effective_height_).toUtf8()); hash.addData(QString::number(format_).toUtf8()); QByteArray bytes = hash.result(); @@ -261,11 +298,17 @@ QString RendererProcessor::CachePathName(const rational &time) QDir this_cache_dir = QDir(GetMediaCacheLocation()).filePath(cache_id_); this_cache_dir.mkpath("."); - QString filename = QString("%1.%2.jpg").arg(QString::number(time.numerator()), QString::number(time.denominator())); + QString filename = QString("%1.%2.exr").arg(QString::number(time.numerator()), QString::number(time.denominator())); return this_cache_dir.filePath(filename); } +void RendererProcessor::CalculateEffectiveDimensions() +{ + effective_width_ = width_ / divider_; + effective_height_ = height_ / divider_; +} + void RendererProcessor::ThreadCallback() { if (sender() == master_thread_) { @@ -280,7 +323,9 @@ void RendererProcessor::ThreadCallback() if (texture == nullptr && QFileInfo::exists(fn)) { QFile(fn).remove(); } else { - download_thread_->Queue(texture, fn); + // Choose download thread with the smallest queue + download_threads_[last_download_thread_%download_threads_.size()]->Queue(texture, fn); + last_download_thread_++; } CacheNext(); diff --git a/app/node/processor/renderer/renderer.h b/app/node/processor/renderer/renderer.h index 1c0742d2c..8283b2043 100644 --- a/app/node/processor/renderer/renderer.h +++ b/app/node/processor/renderer/renderer.h @@ -78,7 +78,10 @@ public: void SetParameters(const int& width, const int& height, const olive::PixelFormat& format, - const olive::RenderMode& mode); + const olive::RenderMode& mode, + const int ÷r = 0); + + void SetDivider(const int& divider); /** * @brief Return current instance of a RenderThread (or nullptr if there is none) @@ -140,9 +143,14 @@ private: NodeOutput* texture_output_; int width_; - int height_; + void CalculateEffectiveDimensions(); + + int divider_; + int effective_width_; + int effective_height_; + olive::PixelFormat format_; olive::RenderMode mode_; @@ -158,8 +166,10 @@ private: bool caching_; RendererProcessThread* master_thread_; rational cache_frame_; + QVector cache_frame_load_buffer_; - RendererDownloadThreadPtr download_thread_; + QVector download_threads_; + int last_download_thread_; RenderTexturePtr master_texture_; diff --git a/app/node/processor/renderer/rendererdownloadthread.cpp b/app/node/processor/renderer/rendererdownloadthread.cpp index 4c499b883..c0250f184 100644 --- a/app/node/processor/renderer/rendererdownloadthread.cpp +++ b/app/node/processor/renderer/rendererdownloadthread.cpp @@ -1,7 +1,9 @@ #include "rendererdownloadthread.h" #include +#include +#include "common/define.h" #include "render/pixelservice.h" RendererDownloadThread::RendererDownloadThread(QOpenGLContext *share_ctx, @@ -81,21 +83,31 @@ void RendererDownloadThread::ProcessLoop() format_info.pixel_type, data_buffer); - render_instance()->context()->extraFunctions()->glFramebufferTexture2D( - GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0 - ); + xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + 0, + 0); f->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - QFile data_dump(working_filename); + std::string working_fn_std = working_filename.toStdString(); - if (data_dump.open(QFile::WriteOnly)) { - data_dump.write(reinterpret_cast(data_buffer), buffer_size); + std::unique_ptr out = OIIO::ImageOutput::create(working_fn_std); - data_dump.close(); + if (out) { + OIIO::ImageSpec spec(working_texture->width(), working_texture->height(), kRGBAChannels, format_info.oiio_desc); + + spec.attribute("compression", "dwaa:200"); + + out->open(working_fn_std, spec); + + out->write_image(format_info.oiio_desc, data_buffer); + + out->close(); } - qDebug() << "Saved" << working_filename; + qDebug() << this << "saved" << working_filename; } diff --git a/app/render/colorservice.cpp b/app/render/colorservice.cpp index 533d60bd8..7c57fafd7 100644 --- a/app/render/colorservice.cpp +++ b/app/render/colorservice.cpp @@ -1,6 +1,6 @@ #include "colorservice.h" -const int kRGBAChannels = 4; +#include "common/define.h" ColorService::ColorService(const char* source_space, const char* dest_space) { diff --git a/app/render/pixelservice.cpp b/app/render/pixelservice.cpp index fc9815f83..babaf293f 100644 --- a/app/render/pixelservice.cpp +++ b/app/render/pixelservice.cpp @@ -24,7 +24,7 @@ #include #include -const int kRGBAChannels = 4; +#include "common/define.h" PixelService::PixelService() { @@ -39,21 +39,25 @@ PixelFormatInfo PixelService::GetPixelFormatInfo(const olive::PixelFormat &forma info.name = tr("8-bit"); info.internal_format = GL_RGBA8; info.pixel_type = GL_UNSIGNED_BYTE; + info.oiio_desc = OIIO::TypeDesc::UINT8; break; case olive::PIX_FMT_RGBA16: info.name = tr("16-bit Integer"); info.internal_format = GL_RGBA16; info.pixel_type = GL_UNSIGNED_SHORT; + info.oiio_desc = OIIO::TypeDesc::UINT16; break; case olive::PIX_FMT_RGBA16F: info.name = tr("Half-Float (16-bit)"); info.internal_format = GL_RGBA16F; info.pixel_type = GL_HALF_FLOAT; + info.oiio_desc = OIIO::TypeDesc::HALF; break; case olive::PIX_FMT_RGBA32F: info.name = tr("Full-Float (32-bit)"); info.internal_format = GL_RGBA32F; info.pixel_type = GL_FLOAT; + info.oiio_desc = OIIO::TypeDesc::FLOAT; break; default: qFatal("Invalid pixel format requested"); diff --git a/app/render/pixelservice.h b/app/render/pixelservice.h index cc41f1f1f..6cb0f5102 100644 --- a/app/render/pixelservice.h +++ b/app/render/pixelservice.h @@ -23,6 +23,7 @@ #include #include +#include #include "decoder/frame.h" #include "pixelformat.h" @@ -43,6 +44,7 @@ struct PixelFormatInfo { GLenum pixel_format; GLenum pixel_type; int bytes_per_pixel; + OIIO::TypeDesc oiio_desc; }; class PixelService : public QObject { diff --git a/app/render/renderinstance.h b/app/render/renderinstance.h index e61372752..0d6c47a6d 100644 --- a/app/render/renderinstance.h +++ b/app/render/renderinstance.h @@ -79,6 +79,8 @@ private: olive::PixelFormat format_; olive::RenderMode mode_; + + int divider_; }; #endif // GLINSTANCE_H