/*** 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 . ***/ #include "videorendererthreadbase.h" #include #include #include "common/define.h" #include "videorenderbackend.h" VideoRendererThreadBase::VideoRendererThreadBase(VideoRenderBackend* parent, QOpenGLContext *share_ctx, const VideoRenderingParams ¶ms) : parent_(parent), share_ctx_(share_ctx), render_instance_(params) { connect(share_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Cancel())); } RenderInstance *VideoRendererThreadBase::render_instance() { return &render_instance_; } void VideoRendererThreadBase::Start() { render_instance_.SetShareContext(share_ctx_); // Allocate and create resources render_instance_.Start(); // Set up download functions f = render_instance()->context()->functions(); xf = render_instance()->context()->extraFunctions(); f->glGenFramebuffers(1, &read_buffer_); int buffer_size = PixelService::GetBufferSize(render_instance()->params().format(), render_instance()->params().width(), render_instance()->params().height()); data_buffer_.resize(buffer_size); format_info_ = PixelService::GetPixelFormatInfo(render_instance()->params().format()); // Set up OIIO::ImageSpec for compressing cached images on disk spec_ = OIIO::ImageSpec(render_instance()->params().width(), render_instance()->params().height(), kRGBAChannels, format_info_.oiio_desc); spec_.attribute("compression", "dwaa:200"); } void VideoRendererThreadBase::Stop() { f->glDeleteFramebuffers(1, &read_buffer_); // Free all resources render_instance_.Stop(); thread()->quit(); } void VideoRendererThreadBase::Process(const NodeDependency &path, bool sibling) { // Process the Node NodeOutput* output_to_process = path.node(); Node* node_to_process = output_to_process->parent(); texture_ = nullptr; QList all_deps; bool has_hash = false; bool can_cache = true; QByteArray hash; if (!sibling) { node_to_process->Lock(); all_deps = node_to_process->GetDependencies(); foreach (Node* dep, all_deps) { dep->Lock(); } // Check hash QCryptographicHash hasher(QCryptographicHash::Sha1); node_to_process->Hash(&hasher, output_to_process, path.in()); hash = hasher.result(); has_hash = parent_->HasHash(hash); can_cache = false; } if (!has_hash){ if ((can_cache = parent_->TryCache(hash))) { QList deps = node_to_process->RunDependencies(output_to_process, path.in()); // Ask for other threads to run these deps while we're here if (!deps.isEmpty()) { for (int i=1;iget_value(path.in(), path.in()).value(); render_instance()->context()->functions()->glFinish(); } } if (!sibling) { foreach (Node* dep, all_deps) { dep->Unlock(); } node_to_process->Unlock(); } if (can_cache) { // We cached this frame, signal that it will need to be downloaded to disk emit CachedFrame(texture_, path.in(), hash); } else { // This hash already exists, no need to cache, just map it emit FrameSkipped(path.in(), hash); } } void VideoRendererThreadBase::Download(RenderTexturePtr texture, const QString &fn, const QByteArray &hash) { // Download the texture f->glBindFramebuffer(GL_READ_FRAMEBUFFER, read_buffer_); xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture(), 0); f->glReadPixels(0, 0, texture->width(), texture->height(), format_info_.pixel_format, format_info_.gl_pixel_type, data_buffer_.data()); xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); f->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); std::string working_fn_std = fn.toStdString(); std::unique_ptr out = OIIO::ImageOutput::create(working_fn_std); if (out) { out->open(working_fn_std, spec_); out->write_image(format_info_.oiio_desc, data_buffer_.data()); out->close(); emit Downloaded(hash); } else { qWarning() << QStringLiteral("Failed to open output file \"%1\"").arg(fn); } }