From 163ad76456374d51b8516315ee67a35e2f9a4188 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 29 Feb 2020 01:00:17 +1100 Subject: [PATCH] renderer: rewrote frame compression algorithm This function was noticeably lagging the main thread while caching. The cause was OpenEXR's internal thread pool competing with our main thread. Since we have our own system of worker threads, its thread pool was unnecessary for caching, however for normal playback it was a useful optimization. Unfortunately OIIO (which we were using to save EXRs) didn't provide quite enough control over OpenEXR's threading behavior (only providing control for over the global thread pool and not on a per-image basis), so for caching we've switched to using OpenEXR directly. This has noticeably sped up the main thread while causing no noticeable slowdown to the caching process. --- app/main.cpp | 2 +- app/render/backend/videorenderframecache.cpp | 8 +- app/render/backend/videorenderworker.cpp | 101 ++++++++++++++----- 3 files changed, 84 insertions(+), 27 deletions(-) diff --git a/app/main.cpp b/app/main.cpp index 7c8df6577..e8252bbbf 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -31,9 +31,9 @@ extern "C" { #include } +#include #include #include -#include #include "core.h" #include "common/crashhandler.h" diff --git a/app/render/backend/videorenderframecache.cpp b/app/render/backend/videorenderframecache.cpp index 3df6aa615..deebefff4 100644 --- a/app/render/backend/videorenderframecache.cpp +++ b/app/render/backend/videorenderframecache.cpp @@ -126,9 +126,11 @@ QString VideoRenderFrameCache::CachePathName(const QByteArray& hash, const Pixel { QString ext; - if (pix_fmt == PixelFormat::PIX_FMT_RGBA8 || pix_fmt == PixelFormat::PIX_FMT_RGBA16U) { - // For some reason, integer EXRs are extremely slow to load, so we use TIFF instead. - ext = QStringLiteral("tiff"); + if (pix_fmt == PixelFormat::PIX_FMT_RGB8 + || pix_fmt == PixelFormat::PIX_FMT_RGBA8 + || pix_fmt == PixelFormat::PIX_FMT_RGB16U + || pix_fmt == PixelFormat::PIX_FMT_RGBA16U) { + ext = QStringLiteral("jpg"); } else { ext = QStringLiteral("exr"); } diff --git a/app/render/backend/videorenderworker.cpp b/app/render/backend/videorenderworker.cpp index 1859f9cca..4c4221426 100644 --- a/app/render/backend/videorenderworker.cpp +++ b/app/render/backend/videorenderworker.cpp @@ -1,5 +1,10 @@ #include "videorenderworker.h" +#include +#include +#include +#include + #include "common/define.h" #include "common/functiontimer.h" #include "node/block/transition/transition.h" @@ -222,39 +227,89 @@ void VideoRenderWorker::CloseInternal() void VideoRenderWorker::Download(const rational& time, QVariant texture, QString filename) { - // Set up OIIO::ImageSpec for compressing cached images on disk - OIIO::ImageSpec spec(video_params().effective_width(), - video_params().effective_height(), - PixelFormat::ChannelCount(video_params().format()), - PixelFormat::GetOIIOTypeDesc(video_params().format())); - - if (video_params_.format() != PixelFormat::PIX_FMT_RGBA8 - && video_params_.format() != PixelFormat::PIX_FMT_RGBA16U) { - // Integer types don't use EXR (they use TIFF instead) because EXR is very slow with integer formats - spec.attribute("compression", "dwaa:200"); - } - if (operating_mode_ & kDownloadOnly) { TextureToBuffer(texture, download_buffer_.data()); - std::string working_fn_std = filename.toStdString(); + switch (video_params().format()) { + case PixelFormat::PIX_FMT_RGB8: + case PixelFormat::PIX_FMT_RGBA8: + case PixelFormat::PIX_FMT_RGB16U: + case PixelFormat::PIX_FMT_RGBA16U: + { + // Integer types are stored in JPEG which we run through OIIO - auto out = OIIO::ImageOutput::create(working_fn_std); + std::string fn_std = filename.toStdString(); - // Keep export to this thread only - out->threads(1); + auto out = OIIO::ImageOutput::create(fn_std); - if (out) { - out->open(working_fn_std, spec); - out->write_image(PixelFormat::GetOIIOTypeDesc(video_params().format()), download_buffer_.data()); - out->close(); + if (out) { + // Attempt to keep this write to one thread + out->threads(1); + + out->open(fn_std, OIIO::ImageSpec(video_params().effective_width(), + video_params().effective_height(), + PixelFormat::ChannelCount(video_params().format()), + PixelFormat::GetOIIOTypeDesc(video_params().format()))); + + out->write_image(PixelFormat::GetOIIOTypeDesc(video_params().format()), download_buffer_.data()); + + out->close(); #if OIIO_VERSION < 10903 - OIIO::ImageOutput::destroy(out); + OIIO::ImageOutput::destroy(out); #endif - } else { - qWarning() << "Failed to open output file:" << filename; + } else { + qCritical() << "Failed to write JPEG file:" << OIIO::geterror().c_str(); + } + break; + } + case PixelFormat::PIX_FMT_RGB16F: + case PixelFormat::PIX_FMT_RGBA16F: + case PixelFormat::PIX_FMT_RGB32F: + case PixelFormat::PIX_FMT_RGBA32F: + { + // Floating point types are stored in EXR + Imf::PixelType pix_type; + + if (video_params().format() == PixelFormat::PIX_FMT_RGB16F + || video_params().format() == PixelFormat::PIX_FMT_RGBA16F) { + pix_type = Imf::HALF; + } else { + pix_type = Imf::FLOAT; + } + + Imf::Header header(video_params().effective_width(), + video_params().effective_height()); + header.channels().insert("R", Imf::Channel(pix_type)); + header.channels().insert("G", Imf::Channel(pix_type)); + header.channels().insert("B", Imf::Channel(pix_type)); + header.channels().insert("A", Imf::Channel(pix_type)); + + header.compression() = Imf::DWAA_COMPRESSION; + header.insert("dwaCompressionLevel", Imf::FloatAttribute(200.0f)); + + Imf::OutputFile out(filename.toUtf8(), header, 0); + + int bpc = PixelFormat::BytesPerChannel(video_params().format()); + + size_t xs = kRGBAChannels * bpc; + size_t ys = video_params().effective_width() * kRGBAChannels * bpc; + + Imf::FrameBuffer framebuffer; + framebuffer.insert("R", Imf::Slice(pix_type, download_buffer_.data(), xs, ys)); + framebuffer.insert("G", Imf::Slice(pix_type, download_buffer_.data() + bpc, xs, ys)); + framebuffer.insert("B", Imf::Slice(pix_type, download_buffer_.data() + 2*bpc, xs, ys)); + framebuffer.insert("A", Imf::Slice(pix_type, download_buffer_.data() + 3*bpc, xs, ys)); + out.setFrameBuffer(framebuffer); + + out.writePixels(video_params().effective_height()); + break; + } + case PixelFormat::PIX_FMT_INVALID: + case PixelFormat::PIX_FMT_COUNT: + qCritical() << "Unable to cache invalid pixel format" << video_params().format(); + break; } } else {