started porting renderer to new portable form

This commit is contained in:
itsmattkc
2019-10-31 19:14:58 +11:00
parent d96395ea27
commit 77bcb70dac
77 changed files with 789 additions and 2237 deletions
+1
View File
@@ -96,6 +96,7 @@ target_link_libraries(${OLIVE_TARGET}
FFMPEG::swresample
${OPENCOLORIO_LIBRARIES}
${OIIO_LIBRARIES}
OpenCL
)
set(OLIVE_EFFECTS
+1 -1
View File
@@ -25,7 +25,7 @@
#include <QVector>
#include "common/rational.h"
#include "render/audio/audioparams.h"
#include "render/audioparams.h"
#include "render/pixelformat.h"
class Frame;
+1 -1
View File
@@ -5,7 +5,7 @@
#include <QFile>
#include "audio/sampleformat.h"
#include "render/audio/audioparams.h"
#include "render/audioparams.h"
class WaveOutput
{
-2
View File
@@ -24,8 +24,6 @@ add_subdirectory(output)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
node/code.h
node/code.cpp
node/dependency.h
node/dependency.cpp
node/edge.h
+11 -67
View File
@@ -20,11 +20,6 @@
#include "alphaover.h"
#include "render/gl/functions.h"
#include "render/gl/shadergenerators.h"
#include "render/rendertexture.h"
#include "render/video/videorenderer.h"
AlphaOverBlend::AlphaOverBlend()
{
@@ -45,71 +40,20 @@ QString AlphaOverBlend::Description()
return tr("A blending node that composites one texture over another using its alpha channel.");
}
NodeCode AlphaOverBlend::Code(NodeOutput *output)
QString AlphaOverBlend::Code(NodeOutput *output)
{
if (output == texture_output()) {
return NodeCode("AlphaOver",
"void AlphaOver(const pixel *base_in, const pixel *blend_in, pixel *tex_out) {"
" int i = get_global_id(0);"
" tex_out[i].r = base_in.r - blend_in.a + blend_in.r;"
" tex_out[i].g = base_in.g - blend_in.a + blend_in.g;"
" tex_out[i].b = base_in.b - blend_in.a + blend_in.b;"
" tex_out[i].a = base_in.a - blend_in.a + blend_in.a;"
"}");
return "#version 110"
"\n"
"varying vec2 olive_tex_coord;\n"
"\n"
"uniform sampler2D base_in;\n"
"uniform sampler2D blend_in;\n"
"\n"
"void main(void) {\n"
" gl_FragColor = base_in - blend_in.a + blend_in;\n"
"}\n";
}
return Node::Code(output);
}
void AlphaOverBlend::Release()
{
}
QVariant AlphaOverBlend::Value(NodeOutput *param, const rational &in, const rational &out)
{
// Find the current Renderer instance
RenderInstance* renderer = VideoRendererProcessor::CurrentInstance();
// If nothing is available, don't return a texture
if (renderer == nullptr) {
return 0;
}
// The only parameter should be texture output, but for future proofing we put this here
if (param == texture_output()) {
RenderTexturePtr base = base_input()->get_value(in, out).value<RenderTexturePtr>();
RenderTexturePtr blend = blend_input()->get_value(in, out).value<RenderTexturePtr>();
if (base == nullptr && blend == nullptr) {
return 0;
} else if (base == nullptr) {
return QVariant::fromValue(blend);
} else if (blend == nullptr) {
return QVariant::fromValue(base);
}
// Attach framebuffer to the backbuffer of base
renderer->buffer()->Attach(base);
renderer->buffer()->Bind();
// Bind blend
blend->Bind();
// Set compositing strategy to alpha over
renderer->context()->functions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
// Draw blend on base
olive::gl::Blit(renderer->default_pipeline());
// Release all
blend->Release();
renderer->buffer()->Release();
renderer->buffer()->Detach();
// Return base texture which now has blend composited on top
// NOTE: Blend texture will be implicitly deleted here (if it's not used anywhere else)
return QVariant::fromValue(base);
}
return 0;
}
+1 -5
View File
@@ -22,7 +22,6 @@
#define ALPHAOVER_H
#include "node/blend/blend.h"
#include "render/gl/shaderptr.h"
class AlphaOverBlend : public BlendNode
{
@@ -33,12 +32,9 @@ public:
virtual QString id() override;
virtual QString Description() override;
virtual NodeCode Code(NodeOutput* output) override;
virtual void Release() override;
virtual QString Code(NodeOutput* output) override;
protected:
virtual QVariant Value(NodeOutput* param, const rational &in, const rational &out) override;
private:
};
+7 -2
View File
@@ -25,12 +25,12 @@
Block::Block() :
next_(nullptr)
{
previous_input_ = new NodeInput("prev_block");
previous_input_ = new NodeInput("prev_in");
previous_input_->set_data_type(NodeParam::kBlock);
previous_input_->set_dependent(false);
AddParameter(previous_input_);
block_output_ = new NodeOutput("block_out");
block_output_ = new NodeOutput("this_out");
AddParameter(block_output_);
buffer_output_ = new NodeOutput("buffer_out");
@@ -277,3 +277,8 @@ bool Block::HasLinks()
return !linked_clips_.isEmpty();
}
bool Block::IsBlock()
{
return true;
}
+2
View File
@@ -78,6 +78,8 @@ public:
const QVector<Block*>& linked_clips();
bool HasLinks();
virtual bool IsBlock() override;
public slots:
/**
* @brief Refreshes internal cache of in/out points up to date
-27
View File
@@ -1,27 +0,0 @@
#include "nodecode.h"
NodeCode::NodeCode()
{
}
NodeCode::NodeCode(const QString &function_name, const QString &code) :
function_name_(function_name),
code_(code)
{
}
bool NodeCode::IsValid()
{
return (!function_name_.isEmpty() && !code_.isEmpty());
}
const QString &NodeCode::function_name()
{
return function_name_;
}
const QString &NodeCode::code()
{
return code_;
}
-23
View File
@@ -1,23 +0,0 @@
#ifndef NODECODE_H
#define NODECODE_H
#include <QString>
class NodeCode
{
public:
NodeCode();
NodeCode(const QString& function_name, const QString& code);
bool IsValid();
const QString& function_name();
const QString& code();
private:
QString function_name_;
QString code_;
};
#endif // NODECODE_H
+18 -58
View File
@@ -20,10 +20,6 @@
#include "opacity.h"
#include "render/gl/functions.h"
#include "render/rendertexture.h"
#include "render/video/videorenderer.h"
OpacityNode::OpacityNode()
{
opacity_input_ = new NodeInput("opacity_in");
@@ -61,65 +57,29 @@ QString OpacityNode::id()
return "org.olivevideoeditor.Olive.opacity";
}
QVariant OpacityNode::Value(NodeOutput *output, const rational &in, const rational &out)
{
Q_UNUSED(out)
// Find the current Renderer instance
RenderInstance* renderer = VideoRendererProcessor::CurrentInstance();
// If nothing is available, don't return a texture
if (renderer == nullptr) {
return 0;
}
if (output == texture_output_) {
RenderTexturePtr input_tex = texture_input_->get_value(in).value<RenderTexturePtr>();
if (input_tex == nullptr) {
return 0;
}
// Attach texture's back buffer as frame buffer
renderer->buffer()->AttachBackBuffer(input_tex);
renderer->buffer()->Bind();
// Bind texture's front buffer to draw with
input_tex->Bind();
// Set opacity to value
ShaderPtr pipeline = renderer->default_pipeline();
pipeline->bind();
pipeline->setUniformValue("opacity", opacity_input_->get_value(in).toFloat()*0.01f);
pipeline->release();
renderer->context()->functions()->glBlendFunc(GL_ONE, GL_ZERO);
// Blit
olive::gl::Blit(pipeline);
// Reset to full opacity
pipeline->bind();
pipeline->setUniformValue("opacity", 1.0f);
pipeline->release();
input_tex->Release();
renderer->buffer()->Release();
renderer->buffer()->Detach();
input_tex->SwapFrontAndBack();
return QVariant::fromValue(input_tex);
}
return 0;
}
void OpacityNode::Retranslate()
{
opacity_input_->set_name(tr("Opacity"));
}
QString OpacityNode::Code(NodeOutput *output)
{
if (output == texture_output()) {
return "#version 110"
"\n"
"varying vec2 olive_tex_coord;\n"
"\n"
"uniform sampler2D tex_in;\n"
"uniform float opacity_in;\n"
"\n"
"void main(void) {\n"
" gl_FragColor = tex_in * (opacity_in * 0.01);\n"
"}\n";
}
return Node::Code(output);
}
NodeInput *OpacityNode::texture_input()
{
return texture_input_;
+2 -3
View File
@@ -22,7 +22,6 @@
#define OPACITYNODE_H
#include "node/node.h"
#include "render/gl/shaderptr.h"
class OpacityNode : public Node
{
@@ -36,10 +35,10 @@ public:
virtual QString id() override;
virtual QVariant Value(NodeOutput *output, const rational &in, const rational &out) override;
virtual void Retranslate() override;
virtual QString Code(NodeOutput* output) override;
NodeInput* texture_input();
NodeOutput* texture_output();
+20 -3
View File
@@ -6,10 +6,7 @@
#include "core.h"
#include "decoder/ffmpeg/ffmpegdecoder.h"
#include "project/item/footage/footage.h"
#include "render/gl/shadergenerators.h"
#include "render/gl/functions.h"
#include "render/pixelservice.h"
#include "render/video/videorenderer.h"
VideoInput::VideoInput() :
color_processor_(nullptr),
@@ -68,6 +65,25 @@ NodeOutput *VideoInput::texture_output()
return texture_output_;
}
QString VideoInput::Code(NodeOutput *output)
{
if (output == texture_output()) {
return "#version 110\n"
"\n"
"varying vec2 olive_tex_coord;\n"
"\n"
"uniform sampler2D footage_in;\n"
"uniform mat4 matrix_in;\n"
"\n"
"void main(void) {\n"
" gl_FragColor = texture2D(olive_tex, vec2(vec4(olive_tex_coord, 0.0, 1.0) * matrix_in));\n"
"}\n";
}
return Node::Code(output);
}
/*
void VideoInput::Hash(QCryptographicHash *hash, NodeOutput *from, const rational &time)
{
Node::Hash(hash, from, time);
@@ -253,3 +269,4 @@ QVariant VideoInput::Value(NodeOutput *output, const rational &in, const rationa
return 0;
}
*/
+4 -15
View File
@@ -5,8 +5,6 @@
#include "../media.h"
#include "render/colormanager.h"
#include "render/rendertexture.h"
#include "render/gl/shadergenerators.h"
class VideoInput : public MediaInput
{
@@ -18,33 +16,24 @@ public:
virtual QString Category() override;
virtual QString Description() override;
virtual QString Code() override;
virtual void Release() override;
NodeInput* matrix_input();
NodeOutput* texture_output();
virtual void Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time) override;
virtual QString Code(NodeOutput* output) override;
//virtual void Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time) override;
protected:
virtual QVariant Value(NodeOutput* output, const rational& in, const rational& out) override;
//virtual QVariant Value(NodeOutput* output, const rational& in, const rational& out) override;
private:
NodeInput* matrix_input_;
NodeOutput* texture_output_;
RenderTexture internal_tex_;
ColorProcessorPtr color_processor_;
ShaderPtr pipeline_;
QOpenGLContext* ocio_ctx_;
GLuint ocio_texture_;
};
#endif // VIDEOINPUT_H
+16 -2
View File
@@ -72,6 +72,15 @@ void Node::RemoveParameter(NodeParam *param)
delete param;
}
QVariant Node::Value(NodeOutput *output, const rational &in, const rational &out)
{
Q_UNUSED(output)
Q_UNUSED(in)
Q_UNUSED(out)
return QVariant();
}
void Node::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from)
{
Q_UNUSED(from)
@@ -141,6 +150,11 @@ void Node::SetCanBeDeleted(bool s)
can_be_deleted_ = s;
}
bool Node::IsBlock()
{
return false;
}
rational Node::LastProcessedTime()
{
rational t;
@@ -278,11 +292,11 @@ QList<Node *> Node::GetImmediateDependencies()
return node_list;
}
NodeCode Node::Code(NodeOutput *output)
QString Node::Code(NodeOutput *output)
{
Q_UNUSED(output)
return NodeCode();
return QString();
}
QList<NodeDependency> Node::RunDependencies(NodeOutput *output, const rational &time)
+11 -4
View File
@@ -26,7 +26,6 @@
#include <QObject>
#include "common/rational.h"
#include "node/code.h"
#include "node/dependency.h"
#include "node/input.h"
#include "node/output.h"
@@ -128,7 +127,7 @@ public:
/**
* @brief Generate OpenCL hardware accelerated code for this Node
*/
virtual NodeCode Code(NodeOutput* output);
virtual QString Code(NodeOutput* output);
/**
* @brief Wrapper for Process()
@@ -224,10 +223,18 @@ public:
bool CanBeDeleted();
/**
* @brief Set whether
* @brief Set whether this Node can be deleted in the UI or not
*/
void SetCanBeDeleted(bool s);
/**
* @brief Returns whether this Node is a "Block" type or not
*
* You shouldn't ever need to override this since all derivatives of Block will automatically have this set to true.
* It's just a more convenient way of checking than dynamic_casting.
*/
virtual bool IsBlock();
protected:
/**
* @brief Add a parameter to this node
@@ -258,7 +265,7 @@ protected:
* corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior
* of the NodeParam objects will handle everything related to it automatically.
*/
virtual QVariant Value(NodeOutput* output, const rational &in, const rational &out) = 0;
virtual QVariant Value(NodeOutput* output, const rational &in, const rational &out);
/**
* @brief Retrieve the last timecode Process() was called with
+10
View File
@@ -196,6 +196,16 @@ QVariant TrackOutput::Value(NodeOutput *output, const rational &in, const ration
if (output == track_output_) {
// Set track output correctly
return PtrToValue(this);
} else if (output == buffer_output()) {
ValidateCurrentBlock(in);
if (current_block_ != this) {
// At this point, we must have found the correct block so we use its texture output to produce the image
return current_block_->buffer_output()->get_value(in, out);
}
// No texture is valid
return 0;
}
// Run default node processing
-10
View File
@@ -70,16 +70,6 @@ NodeInput *ViewerOutput::length_input()
return length_input_;
}
RenderTexturePtr ViewerOutput::GetTexture(const rational &time)
{
return texture_input_->get_value(time).value<RenderTexturePtr>();
}
QByteArray ViewerOutput::GetSamples(const rational &in, const rational &out)
{
return samples_input_->get_value(in, out).toByteArray();
}
void ViewerOutput::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from)
{
Node::InvalidateCache(start_range, end_range, from);
+1 -5
View File
@@ -23,8 +23,7 @@
#include "node/node.h"
#include "render/videoparams.h"
#include "render/audio/audioparams.h"
#include "render/rendertexture.h"
#include "render/audioparams.h"
/**
* @brief A bridge between a node system and a ViewerPanel
@@ -46,9 +45,6 @@ public:
NodeInput* samples_input();
NodeInput* length_input();
RenderTexturePtr GetTexture(const rational& time);
QByteArray GetSamples(const rational& in, const rational& out);
virtual void InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from = nullptr) override;
const VideoParams& video_params();
-3
View File
@@ -87,9 +87,6 @@ public:
/// Resolves to `Block*`
kBlock,
/// Resolves to `QList<Block*>`
kBlockList,
/// Resolves to `Footage*`
kFootage,
+1 -1
View File
@@ -40,7 +40,7 @@ Sequence::Sequence() :
void Sequence::Open(SequencePtr sequence)
{
// FIXME: This is fairly "hardcoded" behavior
// FIXME: This is fairly "hardcoded" behavior and doesn't support infinite panels
ViewerPanel* viewer_panel = olive::panel_manager->MostRecentlyFocused<ViewerPanel>();
TimelinePanel* timeline_panel = olive::panel_manager->MostRecentlyFocused<TimelinePanel>();
-1
View File
@@ -26,7 +26,6 @@
#include "node/output/timeline/timeline.h"
#include "node/output/viewer/viewer.h"
#include "render/videoparams.h"
#include "render/video/videorenderer.h"
#include "project/item/item.h"
class Sequence;
+2 -9
View File
@@ -14,13 +14,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(audio)
add_subdirectory(backend)
add_subdirectory(gl)
add_subdirectory(video)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
render/audioparams.h
render/audioparams.cpp
render/colormanager.h
render/colormanager.cpp
render/colorprocessor.h
@@ -29,13 +28,7 @@ set(OLIVE_SOURCES
render/pixelformat.cpp
render/pixelservice.h
render/pixelservice.cpp
render/renderinstance.h
render/renderinstance.cpp
render/rendermodes.h
render/renderframebuffer.h
render/renderframebuffer.cpp
render/rendertexture.h
render/rendertexture.cpp
render/videoparams.h
render/videoparams.cpp
PARENT_SCOPE
-30
View File
@@ -1,30 +0,0 @@
# 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 <http://www.gnu.org/licenses/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
render/audio/audioparams.h
render/audio/audioparams.cpp
render/audio/audiorenderer.h
render/audio/audiorenderer.cpp
render/audio/audiorendererdownloadthread.h
render/audio/audiorendererdownloadthread.cpp
render/audio/audiorendererprocessthread.h
render/audio/audiorendererprocessthread.cpp
render/audio/audiorendererthreadbase.h
render/audio/audiorendererthreadbase.cpp
PARENT_SCOPE
)
-308
View File
@@ -1,308 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#include "audiorenderer.h"
#include <OpenImageIO/imageio.h>
#include <QApplication>
#include <QCryptographicHash>
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QtMath>
#include "common/filefunctions.h"
#include "render/gl/functions.h"
#include "render/gl/shadergenerators.h"
#include "render/pixelservice.h"
AudioRendererProcessor::AudioRendererProcessor(QObject *parent) :
QObject(parent),
started_(false),
caching_(false),
starting_(false),
viewer_node_(nullptr)
{
// FIXME: Cache name should actually be the name of the sequence
SetCacheName("Test");
}
AudioRendererProcessor::~AudioRendererProcessor()
{
Stop();
}
void AudioRendererProcessor::SetCacheName(const QString &s)
{
cache_name_ = s;
cache_time_ = QDateTime::currentMSecsSinceEpoch();
GenerateCacheIDInternal();
}
void AudioRendererProcessor::InvalidateCache(const rational &start_range, const rational &end_range)
{
// Adjust range to min/max values
rational start_range_adj = qMax(rational(0), start_range);
rational end_range_adj = qMin(viewer_node_->Length(), end_range);
qDebug() << "Cache invalidated between"
<< start_range_adj.toDouble()
<< "and"
<< end_range_adj.toDouble();
bool append = true;
for (int i=0;i<cache_queue_.size();i++) {
const TimeRange& const_range = cache_queue_.at(i);
if (start_range_adj >= const_range.in()
&& start_range_adj <= const_range.out()) {
append = false;
if (const_range.out() < end_range_adj) {
// Same in point but longer, extend
cache_queue_[i].set_out(end_range_adj);
}
break;
} else if (end_range_adj <= const_range.out()
&& end_range_adj >= const_range.in()) {
append = false;
if (const_range.in() > start_range_adj) {
// Same out point but longer, extend
cache_queue_[i].set_in(start_range_adj);
}
break;
}
}
if (append) {
cache_queue_.append(TimeRange(start_range_adj, end_range_adj));
}
CacheNext();
}
void AudioRendererProcessor::SetParameters(const AudioRenderingParams& params)
{
// 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
params_ = params;
// Regenerate the cache ID
GenerateCacheIDInternal();
}
void AudioRendererProcessor::Start()
{
if (started_) {
return;
}
QOpenGLContext* ctx = QOpenGLContext::currentContext();
int background_thread_count = QThread::idealThreadCount();
// Some OpenGL implementations (notably wgl) require the context not to be current before sharing
QSurface* old_surface = ctx->surface();
ctx->doneCurrent();
threads_.resize(background_thread_count);
for (int i=0;i<threads_.size();i++) {
threads_[i] = std::make_shared<AudioRendererProcessThread>(this, params_);
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
connect(threads_.at(i).get(),
SIGNAL(RequestSibling(NodeDependency)),
this,
SLOT(ThreadRequestSibling(NodeDependency)),
Qt::QueuedConnection);
}
// Connect first thread (master thread) to the callback
connect(threads_.first().get(),
SIGNAL(CachedFrame(const QByteArray&, const rational&, const rational&)),
this,
SLOT(ThreadCallback(const QByteArray&, const rational&, const rational&)),
Qt::QueuedConnection);
// Restore context now that thread creation is complete
ctx->makeCurrent(old_surface);
started_ = true;
}
void AudioRendererProcessor::Stop()
{
if (!started_) {
return;
}
started_ = false;
foreach (AudioRendererProcessThreadPtr process_thread, threads_) {
process_thread->Cancel();
}
threads_.clear();
}
void AudioRendererProcessor::GenerateCacheIDInternal()
{
if (cache_name_.isEmpty() || !params_.is_valid()) {
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(params_.sample_rate()).toUtf8());
hash.addData(QString::number(params_.channel_layout()).toUtf8());
hash.addData(QString::number(params_.format()).toUtf8());
QByteArray bytes = hash.result();
cache_id_ = bytes.toHex();
}
void AudioRendererProcessor::CacheNext()
{
if (cache_queue_.isEmpty() || viewer_node_ == nullptr || caching_) {
return;
}
// Make sure cache has started
Start();
TimeRange cache_frame = cache_queue_.takeFirst();
qDebug() << "Caching" << cache_frame.in().toDouble() << "-" << cache_frame.out().toDouble();
threads_.first()->Queue(NodeDependency(viewer_node_->texture_input()->get_connected_output(), cache_frame), true, false);
caching_ = true;
}
QString AudioRendererProcessor::CachePathName(const QByteArray &hash)
{
QDir this_cache_dir = QDir(GetMediaCacheLocation()).filePath(cache_id_);
this_cache_dir.mkpath(".");
QString filename = QString("%1.pcm").arg(QString(hash.toHex()));
return this_cache_dir.filePath(filename);
}
void AudioRendererProcessor::ThreadCallback(const QByteArray& samples, const rational& in, const rational& out)
{
// Threads are all done now, time to proceed
caching_ = false;
int start_offset = params_.time_to_bytes(in);
int end_offset = params_.time_to_bytes(out);
// Ensure sample cache is at least large enough for this
if (sample_cache_.size() < end_offset) {
sample_cache_.resize(end_offset);
}
sample_cache_.replace(start_offset, samples.size(), samples);
CacheNext();
}
void AudioRendererProcessor::ThreadRequestSibling(NodeDependency dep)
{
// Try to queue another thread to run this dep in advance
for (int i=1;i<threads_.size();i++) {
if (threads_.at(i)->Queue(dep, false, true)) {
return;
}
}
}
AudioRendererThreadBase* AudioRendererProcessor::CurrentThread()
{
return dynamic_cast<AudioRendererThreadBase*>(QThread::currentThread());
}
AudioParams *AudioRendererProcessor::CurrentInstance()
{
AudioRendererThreadBase* thread = CurrentThread();
if (thread != nullptr) {
return thread->params();
}
return nullptr;
}
QByteArray AudioRendererProcessor::GetCachedSamples(const rational &in, const rational &out)
{
if (viewer_node_ == nullptr || in == out) {
// Nothing is connected - nothing to show or render
return nullptr;
}
if (!params_.is_valid()) {
qWarning() << "Invalid parameters";
return nullptr;
}
if (cache_id_.isEmpty()) {
qWarning() << "No cache ID";
return nullptr;
}
if (out < in || in < 0 || out < 0) {
qWarning() << "Invalid time requested";
return nullptr;
}
int start_offset = qMin(params_.time_to_bytes(in), sample_cache_.size());
int end_offset = qMin(params_.time_to_bytes(out), sample_cache_.size());
int length = end_offset - start_offset;
if (length == 0) {
return nullptr;
}
return sample_cache_.mid(start_offset, length);
}
void AudioRendererProcessor::SetViewerNode(ViewerOutput *viewer)
{
if (viewer_node_ != nullptr) {
disconnect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
}
viewer_node_ = viewer;
if (viewer_node_ != nullptr) {
connect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&)));
// FIXME: Hardcoded format and mode
AudioRenderingParams(viewer_node_->audio_params(), olive::SAMPLE_FMT_FLT);
}
}
-149
View File
@@ -1,149 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#ifndef AUDIORENDERER_H
#define AUDIORENDERER_H
#include <QLinkedList>
#include <QOpenGLTexture>
#include "common/timerange.h"
#include "node/output/viewer/viewer.h"
#include "render/pixelformat.h"
#include "render/rendermodes.h"
#include "audiorendererdownloadthread.h"
#include "audiorendererprocessthread.h"
/**
* @brief A multithreaded OpenGL based renderer for node systems
*/
class AudioRendererProcessor : public QObject
{
Q_OBJECT
public:
/**
* @brief Renderer Constructor
*
* Constructing a Renderer object will not start any threads/backend on its own. Use Start() to do this and Stop()
* when the Renderer is about to be destroyed.
*/
AudioRendererProcessor(QObject* parent);
virtual ~AudioRendererProcessor() override;
void SetCacheName(const QString& s);
/**
* @brief Set parameters of the Renderer
*
* The Renderer owns the buffers that are used in the rendering process and this function sets the kind of buffers
* to use. The Renderer must be stopped when calling this function.
*
* @param width
*
* Buffer width
*
* @param height
*
* Buffer height
*
* @param format
*
* Buffer pixel format
*/
void SetParameters(const AudioRenderingParams &params);
/**
* @brief Return current instance of a RenderThread (or nullptr if there is none)
*
* This function attempts a dynamic_cast on QThread::currentThread() to RendererThread, which will return nullptr if
* the cast fails (e.g. if this function is called from the main thread rather than a RendererThread).
*/
static AudioRendererThreadBase* CurrentThread();
static AudioParams* CurrentInstance();
QByteArray GetCachedSamples(const rational& in, const rational& out);
void SetViewerNode(ViewerOutput* viewer);
private:
/**
* @brief Allocate and start the multithreaded backend
*/
void Start();
/**
* @brief Terminate and deallocate the multithreaded backend
*/
void Stop();
/**
* @brief Internal function for generating the cache ID
*/
void GenerateCacheIDInternal();
/**
* @brief Function called when there are frames in the queue to cache
*
* This function is NOT thread-safe and should only be called in the main thread.
*/
void CacheNext();
/**
* @brief Return the path of the cached image at this time
*/
QString CachePathName(const QByteArray &hash);
/**
* @brief Internal list of RenderProcessThreads
*/
QVector<AudioRendererProcessThreadPtr> threads_;
/**
* @brief Internal variable that contains whether the Renderer has started or not
*/
bool started_;
AudioRenderingParams params_;
QList<TimeRange> cache_queue_;
QString cache_name_;
qint64 cache_time_;
QString cache_id_;
bool caching_;
bool starting_;
ViewerOutput* viewer_node_;
QByteArray sample_cache_;
private slots:
void InvalidateCache(const rational &start_range, const rational &end_range);
void ThreadCallback(const QByteArray& samples, const rational& in, const rational &out);
void ThreadRequestSibling(NodeDependency dep);
};
#endif // AUDIORENDERER_H
@@ -1,128 +0,0 @@
#include "audiorendererdownloadthread.h"
#include <QFile>
#include <QFloat16>
#include <OpenImageIO/imageio.h>
#include "common/define.h"
#include "render/pixelservice.h"
/*AudioRendererDownloadThread::AudioRendererDownloadThread(QOpenGLContext *share_ctx,
const int &width,
const int &height,
const int &divider,
const olive::PixelFormat &format,
const olive::RenderMode &mode) :
AudioRendererThreadBase(share_ctx, width, height, divider, format, mode),
cancelled_(false)
{
}
void AudioRendererDownloadThread::Queue(RenderTexturePtr texture, const QString& fn, const QByteArray &hash)
{
texture_queue_lock_.lock();
texture_queue_.append({texture, fn, hash});
wait_cond_.wakeAll();
texture_queue_lock_.unlock();
}
void AudioRendererDownloadThread::Cancel()
{
cancelled_ = true;
texture_queue_lock_.lock();
wait_cond_.wakeAll();
texture_queue_lock_.unlock();
wait();
}
void AudioRendererDownloadThread::ProcessLoop()
{
QOpenGLFunctions* f = render_instance()->context()->functions();
QOpenGLExtraFunctions* xf = render_instance()->context()->extraFunctions();
f->glGenFramebuffers(1, &read_buffer_);
DownloadQueueEntry entry;
int buffer_size = PixelService::GetBufferSize(render_instance()->format(),
render_instance()->width(),
render_instance()->height());
QVector<uchar> data_buffer;
data_buffer.resize(buffer_size);
PixelFormatInfo format_info = PixelService::GetPixelFormatInfo(render_instance()->format());
// Set up OIIO::ImageSpec for compressing cached images on disk
OIIO::ImageSpec spec(render_instance()->width(), render_instance()->height(), kRGBAChannels, format_info.oiio_desc);
spec.attribute("compression", "dwaa:200");
while (!cancelled_) {
// Check queue for textures to download (use mutex to prevent collisions)
texture_queue_lock_.lock();
while (texture_queue_.isEmpty()) {
// Main waiting condition
wait_cond_.wait(&texture_queue_lock_);
if (cancelled_) {
break;
}
}
if (cancelled_) {
texture_queue_lock_.unlock();
break;
}
entry = texture_queue_.takeFirst();
texture_queue_lock_.unlock();
// Download the texture
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, read_buffer_);
xf->glFramebufferTexture2D(GL_READ_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
entry.texture->texture(),
0);
f->glReadPixels(0,
0,
entry.texture->width(),
entry.texture->height(),
format_info.pixel_format,
format_info.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 = entry.filename.toStdString();
std::unique_ptr<OIIO::ImageOutput> 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(entry.hash);
} else {
qWarning() << tr("Failed to open output file \"%1\"").arg(entry.filename);
}
}
f->glDeleteFramebuffers(1, &read_buffer_);
}*/
@@ -1,49 +0,0 @@
#ifndef AUDIORENDERERDOWNLOADTHREAD_H
#define AUDIORENDERERDOWNLOADTHREAD_H
#include "audiorendererthreadbase.h"
/*class AudioRendererDownloadThread : public AudioRendererThreadBase
{
Q_OBJECT
public:
AudioRendererDownloadThread(QOpenGLContext* share_ctx,
const int& width,
const int& height,
const int &divider,
const olive::PixelFormat& format,
const olive::RenderMode& mode);
void Queue(RenderTexturePtr texture, const QString &fn, const QByteArray &hash);
public slots:
virtual void Cancel() override;
signals:
void Downloaded(const QByteArray& hash);
protected:
virtual void ProcessLoop() override;
private:
struct DownloadQueueEntry {
RenderTexturePtr texture;
QString filename;
QByteArray hash;
};
GLuint read_buffer_;
QVector<DownloadQueueEntry> texture_queue_;
QMutex texture_queue_lock_;
QAtomicInt cancelled_;
QByteArray hash_;
};
using AudioRendererDownloadThreadPtr = std::shared_ptr<AudioRendererDownloadThread>;*/
#endif // AUDIORENDERERDOWNLOADTHREAD_H
@@ -1,116 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#include "audiorendererprocessthread.h"
#include "audiorenderer.h"
AudioRendererProcessThread::AudioRendererProcessThread(AudioRendererProcessor* parent,
const AudioRenderingParams &params) :
AudioRendererThreadBase(params),
parent_(parent),
cancelled_(false)
{
}
bool AudioRendererProcessThread::Queue(const NodeDependency& dep, bool wait, bool sibling)
{
if (wait) {
// Wait for thread to be available
mutex_.lock();
} else if (!mutex_.tryLock()) {
return false;
}
// We can now change params without the other thread using them
path_ = dep;
sibling_ = sibling;
// Prepare to wait for thread to respond
caller_mutex_.lock();
// Wake up our main thread
wait_cond_.wakeAll();
mutex_.unlock();
// Wait for thread to start before returning
wait_cond_.wait(&caller_mutex_);
caller_mutex_.unlock();
return true;
}
void AudioRendererProcessThread::Cancel()
{
cancelled_ = true;
mutex_.lock();
wait_cond_.wakeAll();
mutex_.unlock();
wait();
}
void AudioRendererProcessThread::ProcessLoop()
{
while (!cancelled_) {
// Main waiting condition
wait_cond_.wait(&mutex_);
if (cancelled_) {
break;
}
// Wake up main thread
caller_mutex_.lock();
wait_cond_.wakeAll();
caller_mutex_.unlock();
// Process the Node
NodeOutput* output_to_process = path_.node();
Node* node_to_process = output_to_process->parent();
QList<Node*> all_deps;
QList<NodeDependency> 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;i<deps.size();i++) {
emit RequestSibling(deps.at(i));
}
}
// Get the requested value
QByteArray samples = output_to_process->get_value(path_.in(), path_.out()).toByteArray();
if (!sibling_) {
foreach (Node* dep, all_deps) {
dep->Unlock();
}
node_to_process->Unlock();
}
// Signal that we cached some samples
emit CachedSamples(samples, path_.in(), path_.out());
}
}
@@ -1,61 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#ifndef AUDIORENDERERPROCESSTHREAD_H
#define AUDIORENDERERPROCESSTHREAD_H
#include "audiorendererthreadbase.h"
class AudioRendererProcessor;
class AudioRendererProcessThread : public AudioRendererThreadBase
{
Q_OBJECT
public:
AudioRendererProcessThread(AudioRendererProcessor* parent,
const AudioRenderingParams &params);
bool Queue(const NodeDependency &dep, bool wait, bool sibling);
public slots:
virtual void Cancel() override;
protected:
virtual void ProcessLoop() override;
signals:
void RequestSibling(NodeDependency dep);
void CachedSamples(const QByteArray& samples, const rational& in, const rational& out);
private:
AudioRendererProcessor* parent_;
NodeDependency path_;
QAtomicInt cancelled_;
bool sibling_;
};
using AudioRendererProcessThreadPtr = std::shared_ptr<AudioRendererProcessThread>;
#endif // AUDIORENDERERPROCESSTHREAD_H
@@ -1,69 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#include "audiorendererthreadbase.h"
#include <QDebug>
AudioRendererThreadBase::AudioRendererThreadBase(const AudioRenderingParams &params) :
params_(params)
{
}
AudioParams *AudioRendererThreadBase::params()
{
return &params_;
}
void AudioRendererThreadBase::run()
{
// Lock mutex for main loop
mutex_.lock();
// Signal that main thread can continue now
WakeCaller();
// Main loop (use Cancel() to exit it)
ProcessLoop();
// Unlock mutex before exiting
mutex_.unlock();
}
void AudioRendererThreadBase::WakeCaller()
{
// Signal that main thread can continue now
caller_mutex_.lock();
wait_cond_.wakeAll();
caller_mutex_.unlock();
}
void AudioRendererThreadBase::StartThread(QThread::Priority priority)
{
caller_mutex_.lock();
// Start the thread
QThread::start(priority);
// Wait for thread to finish completion
wait_cond_.wait(&caller_mutex_);
caller_mutex_.unlock();
}
@@ -1,65 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#ifndef AUDIORENDERTHREAD_H
#define AUDIORENDERTHREAD_H
#include <memory>
#include <QMutex>
#include <QThread>
#include <QWaitCondition>
#include "audioparams.h"
#include "node/node.h"
class AudioRendererThreadBase : public QThread
{
Q_OBJECT
public:
AudioRendererThreadBase(const AudioRenderingParams &params);
AudioParams* params();
void StartThread(Priority priority = InheritPriority);
virtual void run() override;
public slots:
virtual void Cancel() = 0;
protected:
virtual void ProcessLoop() = 0;
QWaitCondition wait_cond_;
QMutex mutex_;
QMutex caller_mutex_;
private:
void WakeCaller();
AudioRenderingParams params_;
};
using AudioRendererThreadPtr = std::shared_ptr<AudioRendererThreadBase>;
#endif // AUDIORENDERTHREAD_H
+13 -6
View File
@@ -14,15 +14,22 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(opengl)
add_subdirectory(vulkan)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
render/backend/cudabackend.h
render/backend/cudabackend.cpp
render/backend/metalbackend.h
render/backend/metalbackend.cpp
render/backend/openclbackend.h
render/backend/openclbackend.cpp
render/backend/renderbackend.h
render/backend/renderbackend.cpp
render/backend/audiorenderbackend.h
render/backend/audiorenderbackend.cpp
render/backend/videorenderbackend.h
render/backend/videorenderbackend.cpp
# FIXME: Remove these
render/backend/videorendererdownloadthread.h
render/backend/videorendererdownloadthread.cpp
render/backend/videorendererprocessthread.h
render/backend/videorendererprocessthread.cpp
PARENT_SCOPE
)
@@ -0,0 +1,6 @@
#include "audiorenderbackend.h"
AudioRenderBackend::AudioRenderBackend()
{
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef AUDIORENDERBACKEND_H
#define AUDIORENDERBACKEND_H
#include "renderbackend.h"
class AudioRenderBackend : public RenderBackend
{
Q_OBJECT
public:
AudioRenderBackend();
public slots:
virtual void InvalidateCache(const rational &start_range, const rational &end_range);
};
#endif // AUDIORENDERBACKEND_H
-6
View File
@@ -1,6 +0,0 @@
#include "cudabackend.h"
CUDABackend::CUDABackend()
{
}
-11
View File
@@ -1,11 +0,0 @@
#ifndef CUDABACKEND_H
#define CUDABACKEND_H
class CUDABackend
{
public:
CUDABackend();
};
#endif // CUDABACKEND_H
-6
View File
@@ -1,6 +0,0 @@
#include "metalbackend.h"
MetalBackend::MetalBackend()
{
}
-11
View File
@@ -1,11 +0,0 @@
#ifndef METALBACKEND_H
#define METALBACKEND_H
class MetalBackend
{
public:
MetalBackend();
};
#endif // METALBACKEND_H
-128
View File
@@ -1,128 +0,0 @@
#include "openclbackend.h"
OpenCLBackend::OpenCLBackend()
{
}
bool OpenCLBackend::Init()
{
// Get platform and device information
cl_platform_id platform_id = nullptr;
cl_uint ret_num_devices;
cl_uint ret_num_platforms;
cl_int ret = clGetPlatformIDs(1, &platform_id, &ret_num_platforms);
ret = clGetDeviceIDs( platform_id, CL_DEVICE_TYPE_GPU, 1,
&device_id_, &ret_num_devices);
if (ret != CL_SUCCESS) {
qWarning() << "Failed to find compatible OpenCL device";
return false;
}
// Create an OpenCL context
context_ = clCreateContext(nullptr, 1, &device_id_, nullptr, nullptr, &ret);
// Create a command queue
//command_queue_ = clCreateCommandQueue(context_, device_id_, 0, &ret);
return true;
}
void OpenCLBackend::GenerateFrame(const rational &time)
{
Q_UNUSED(time)
/*
// Copy the lists A and B to their respective memory buffers
cl_int ret = clEnqueueWriteBuffer(command_queue_, a_mem_obj, CL_TRUE, 0,
BITMAP_SZ, source_bmp, 0, NULL, NULL);
// Set the arguments of the kernel
ret = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&a_mem_obj);
ret = clSetKernelArg(kernel, 1, sizeof(cl_mem), (void *)&c_mem_obj);
// Execute the OpenCL kernel on the list
size_t global_item_size = BITMAP_SZ; // Process the entire lists
size_t local_item_size = 30; // Divide work items into groups of 64
ret = clEnqueueNDRangeKernel(command_queue_, kernel, 1, NULL,
&global_item_size, &local_item_size, 0, NULL, NULL);
if (ret != CL_SUCCESS) {
fprintf(stderr, "Failed to run\n");
exit(1);
}
// Read the memory buffer C on the device to the local variable C
ret = clEnqueueReadBuffer(command_queue_, c_mem_obj, CL_TRUE, 0,
BITMAP_SZ, dest_bmp, 0, NULL, NULL);
// Clean up
ret = clFlush(command_queue_);
ret = clFinish(command_queue_);
*/
}
void OpenCLBackend::Close()
{
Decompile();
cl_int ret;
/*ret = clReleaseKernel(kernel);
ret = clReleaseMemObject(a_mem_obj);
ret = clReleaseMemObject(c_mem_obj);*/
ret = clReleaseCommandQueue(command_queue_);
ret = clReleaseContext(context_);
}
void OpenCLBackend::Decompile()
{
clReleaseProgram(program_);
}
void OpenCLBackend::Compile()
{
/*
cl_int ret;
// Create memory buffers on the device for each vector
cl_mem a_mem_obj = clCreateBuffer(context_, CL_MEM_READ_ONLY, BITMAP_SZ, nullptr, &ret);
cl_mem c_mem_obj = clCreateBuffer(context_, CL_MEM_WRITE_ONLY, BITMAP_SZ, nullptr, &ret);
// Create a program from the kernel source
program_ = clCreateProgramWithSource(context_,
1,
(const char **)&source_str,
(const size_t *)&source_size,
&ret);
// Build the program
ret = clBuildProgram(program_, 1, &device_id_, nullptr, nullptr, nullptr);
if (ret != CL_SUCCESS) {
// Decompile failed, the user will probably want to know why
size_t error_len = 0;
clGetProgramBuildInfo(program_, device_id_, CL_PROGRAM_BUILD_LOG, 0, nullptr, &error_len);
char* err = new char[error_len];
clGetProgramBuildInfo(program_, device_id_, CL_PROGRAM_BUILD_LOG, error_len, err, nullptr);
SetError(err);
//fprintf(stderr, "%s\n", err);
delete [] err;
}
// Create the OpenCL kernel
cl_kernel kernel = clCreateKernel(program_, "vector_add", &ret);
*/
}
void OpenCLBackend::GenerateCode()
{
if (viewer_node() == nullptr) {
return;
}
}
-40
View File
@@ -1,40 +0,0 @@
#ifndef OPENCLBACKEND_H
#define OPENCLBACKEND_H
#ifdef __APPLE__
#include <OpenCL/opencl.h>
#else
#include <CL/cl.h>
#endif
#include "renderbackend.h"
class OpenCLBackend : public RenderBackend
{
public:
OpenCLBackend();
virtual bool Init() override;
virtual void GenerateFrame(const rational& time) override;
virtual void Close() override;
void Compile();
protected:
virtual void Decompile() override;
private:
void GenerateCode();
cl_context context_;
cl_program program_;
cl_command_queue command_queue_;
cl_device_id device_id_;
};
#endif // OPENCLBACKEND_H
@@ -16,13 +16,15 @@
set(OLIVE_SOURCES
${OLIVE_SOURCES}
render/video/videorenderer.h
render/video/videorenderer.cpp
render/video/videorendererthreadbase.h
render/video/videorendererthreadbase.cpp
render/video/videorendererdownloadthread.h
render/video/videorendererdownloadthread.cpp
render/video/videorendererprocessthread.h
render/video/videorendererprocessthread.cpp
render/backend/opengl/functions.h
render/backend/opengl/functions.cpp
render/backend/opengl/openglbackend.h
render/backend/opengl/openglbackend.cpp
render/backend/opengl/openglframebuffer.h
render/backend/opengl/openglframebuffer.cpp
render/backend/opengl/openglshader.h
render/backend/opengl/openglshader.cpp
render/backend/opengl/opengltexture.h
render/backend/opengl/opengltexture.cpp
PARENT_SCOPE
)
@@ -72,7 +72,7 @@ void PrepareToDraw(QOpenGLFunctions* f) {
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
}
void olive::gl::Blit(ShaderPtr pipeline, bool flipped, QMatrix4x4 matrix) {
void olive::gl::Blit(OpenGLShaderPtr pipeline, bool flipped, QMatrix4x4 matrix) {
// FIXME: is currentContext() reliable here?
QOpenGLFunctions* func = QOpenGLContext::currentContext()->functions();
@@ -121,7 +121,7 @@ void olive::gl::Blit(ShaderPtr pipeline, bool flipped, QMatrix4x4 matrix) {
m_vao.destroy();
}
void olive::gl::OCIOBlit(ShaderPtr pipeline,
void olive::gl::OCIOBlit(OpenGLShaderPtr pipeline,
GLuint lut,
bool flipped,
QMatrix4x4 matrix)
@@ -23,7 +23,7 @@
#include <QMatrix4x4>
#include "shaderptr.h"
#include "openglshader.h"
namespace olive {
namespace gl {
@@ -43,9 +43,9 @@ namespace gl {
*
* Transformation matrix to use when drawing (defaults to no transform)
*/
void Blit(ShaderPtr pipeline, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4());
void Blit(OpenGLShaderPtr pipeline, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4());
void OCIOBlit(ShaderPtr pipeline, GLuint lut, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4());
void OCIOBlit(OpenGLShaderPtr pipeline, GLuint lut, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4());
}
}
+232
View File
@@ -0,0 +1,232 @@
#include "openglbackend.h"
#include <QThread>
OpenGLBackend::OpenGLBackend(QOpenGLContext *share_ctx) :
share_ctx_(share_ctx)
{
}
OpenGLBackend::~OpenGLBackend()
{
Close();
}
bool OpenGLBackend::Init()
{
threads_.resize(QThread::idealThreadCount());
// Some OpenGL implementations (notably wgl) require the context not to be current before sharing. We block the main
// thread here to prevent QOpenGLWidget trying to reclaim the context before we're done
QSurface* old_surface = share_ctx_->surface();
share_ctx_->doneCurrent();
// Initiate one thread per CPU core
for (int i=0;i<threads_.size();i++) {
// Instantiate thread
QThread* thread = new QThread(this);
threads_.replace(i, thread);
// Create one processor object for each thread
OpenGLProcessor* processor = new OpenGLProcessor(share_ctx_, thread);
// FIXME: Hardcoded values
processor->SetParameters(VideoRenderingParams(viewer_node()->video_params(), olive::PIX_FMT_RGBA16F, olive::kOffline));
// Finally, we can move it to its own thread
processor->moveToThread(thread);
}
// We've finished creating shared contexts, we can now restore the context to its previous current state
share_ctx_->makeCurrent(old_surface);
return true;
}
void OpenGLBackend::GenerateFrame(const rational &time)
{
Q_UNUSED(time)
}
void OpenGLBackend::Close()
{
Decompile();
// Clear all cores
for (int i=0;i<threads_.size();i++) {
delete threads_.at(i);
}
threads_.clear();
}
bool OpenGLBackend::Compile()
{
Decompile();
if (viewer_node() == nullptr || !viewer_node()->texture_input()->IsConnected()) {
// Nothing to be done, nothing to compile
return true;
}
// Traverse node graph compiling where necessary
bool ret = TraverseCompiling(viewer_node());
if (ret) {
qDebug() << "Compiled successfully!";
} else {
qDebug() << "Compile failed:" << GetError();
}
return ret;
}
void OpenGLBackend::Decompile()
{
foreach (const CompiledNode& info, compiled_nodes_) {
delete info.program;
}
compiled_nodes_.clear();
}
bool OpenGLBackend::TraverseCompiling(Node *n)
{
foreach (NodeParam* param, n->parameters()) {
if (param->type() == NodeParam::kInput && param->IsConnected()) {
NodeOutput* connected_output = static_cast<NodeInput*>(param)->get_connected_output();
// Generate the ID we'd use for this shader
QString output_id = GenerateShaderID(connected_output);
// Check if we have a shader or not
if (GetShaderFromID(output_id) == nullptr) {
// Since we don't have a shader, compile one now
QString node_code = connected_output->parent()->Code(connected_output);
// If the node has no code, it mustn't be GPU accelerated
if (!node_code.isEmpty()) {
// Since we have shader code, compile it now
CompiledNode compiled_info;
compiled_info.id = output_id;
if (!(compiled_info.program = new QOpenGLShaderProgram())) {
SetError("Failed to create OpenGL shader object");
return false;
}
if (!compiled_info.program->create()) {
SetError("Failed to create OpenGL shader on device");
return false;
}
if (!compiled_info.program->addShaderFromSourceCode(QOpenGLShader::Fragment, node_code)) {
SetError("Failed to add OpenGL shader code");
return false;
}
if (compiled_info.program->link()) {
SetError("Failed to compile OpenGL shader");
return false;
}
compiled_nodes_.append(compiled_info);
qDebug() << "Compiled" << compiled_info.id;
}
}
if (!TraverseCompiling(connected_output->parent())) {
return false;
}
}
}
return true;
}
QOpenGLShaderProgram* OpenGLBackend::GetShaderFromID(const QString &id)
{
foreach (const CompiledNode& info, compiled_nodes_) {
if (info.id == id) {
return info.program;
}
}
return nullptr;
}
QString OpenGLBackend::GenerateShaderID(NodeOutput *output)
{
// Creates a unique identifier for this specific node and this specific output
return QString("%1:%2").arg(output->parent()->id(), output->id());
}
OpenGLProcessor::OpenGLProcessor(QOpenGLContext *share_ctx, QObject *parent) :
QObject(parent),
share_ctx_(share_ctx),
ctx_(nullptr),
functions_(nullptr)
{
surface_.create();
}
OpenGLProcessor::~OpenGLProcessor()
{
surface_.destroy();
}
bool OpenGLProcessor::IsStarted()
{
return ctx_ != nullptr;
}
void OpenGLProcessor::SetParameters(const VideoRenderingParams &video_params)
{
video_params_ = video_params;
}
void OpenGLProcessor::Init()
{
// Create context object
ctx_ = new QOpenGLContext();
// Set share context
ctx_->setShareContext(share_ctx_);
// Create OpenGL context (automatically destroys any existing if there is one)
if (!ctx_->create()) {
qWarning() << "Failed to create OpenGL context in thread" << thread();
Close();
return;
}
// Make context current on that surface
if (!ctx_->makeCurrent(&surface_)) {
qWarning() << "Failed to makeCurrent() on offscreen surface in thread" << thread();
Close();
return;
}
// Store OpenGL functions instance
functions_ = ctx_->functions();
// Set up OpenGL parameters as necessary
functions_->glEnable(GL_BLEND);
UpdateViewportFromParams();
buffer_.Create(ctx_);
}
void OpenGLProcessor::Close()
{
buffer_.Destroy();
functions_ = nullptr;
delete ctx_;
}
void OpenGLProcessor::UpdateViewportFromParams()
{
if (functions_ != nullptr && video_params_.is_valid()) {
functions_->glViewport(0, 0, video_params_.effective_width(), video_params_.effective_height());
}
}
+81
View File
@@ -0,0 +1,81 @@
#ifndef OPENGLBACKEND_H
#define OPENGLBACKEND_H
#include <memory>
#include <QOffscreenSurface>
#include <QOpenGLShaderProgram>
#include "../renderbackend.h"
#include "openglframebuffer.h"
class OpenGLProcessor : public QObject {
public:
OpenGLProcessor(QOpenGLContext* share_ctx, QObject* parent = nullptr);
virtual ~OpenGLProcessor() override;
Q_DISABLE_COPY_MOVE(OpenGLProcessor)
bool IsStarted();
void SetParameters(const VideoRenderingParams& video_params);
public slots:
void Init();
void Close();
private:
void UpdateViewportFromParams();
QOpenGLContext* share_ctx_;
QOpenGLContext* ctx_;
QOffscreenSurface surface_;
QOpenGLFunctions* functions_;
OpenGLFramebuffer buffer_;
VideoRenderingParams video_params_;
};
class OpenGLBackend : public RenderBackend
{
public:
OpenGLBackend(QOpenGLContext* share_ctx);
virtual ~OpenGLBackend() override;
virtual bool Init() override;
virtual void GenerateFrame(const rational& time) override;
virtual void Close() override;
public slots:
virtual bool Compile() override;
virtual void Decompile() override;
private:
QOpenGLContext* share_ctx_;
struct CompiledNode {
QString id;
QOpenGLShaderProgram* program;
};
bool TraverseCompiling(Node* n);
QOpenGLShaderProgram *GetShaderFromID(const QString& id);
QString GenerateShaderID(NodeOutput* output);
QList<CompiledNode> compiled_nodes_;
QVector<QThread*> threads_;
QVector<OpenGLProcessor*> processors_;
};
#endif // OPENGLBACKEND_H
@@ -18,12 +18,12 @@
***/
#include "renderframebuffer.h"
#include "openglframebuffer.h"
#include <QDebug>
#include <QOpenGLExtraFunctions>
RenderFramebuffer::RenderFramebuffer() :
OpenGLFramebuffer::OpenGLFramebuffer() :
context_(nullptr),
buffer_(0),
texture_(nullptr)
@@ -31,12 +31,12 @@ RenderFramebuffer::RenderFramebuffer() :
}
RenderFramebuffer::~RenderFramebuffer()
OpenGLFramebuffer::~OpenGLFramebuffer()
{
Destroy();
}
void RenderFramebuffer::Create(QOpenGLContext *ctx)
void OpenGLFramebuffer::Create(QOpenGLContext *ctx)
{
if (ctx == nullptr) {
qWarning() << tr("RenderTexture::Create was passed an invalid context");
@@ -54,7 +54,7 @@ void RenderFramebuffer::Create(QOpenGLContext *ctx)
context_->functions()->glGenFramebuffers(1, &buffer_);
}
void RenderFramebuffer::Destroy()
void OpenGLFramebuffer::Destroy()
{
if (context_ != nullptr) {
disconnect(context_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Destroy()));
@@ -67,12 +67,12 @@ void RenderFramebuffer::Destroy()
}
}
bool RenderFramebuffer::IsCreated() const
bool OpenGLFramebuffer::IsCreated() const
{
return (buffer_ > 0);
}
void RenderFramebuffer::Bind()
void OpenGLFramebuffer::Bind()
{
if (context_ == nullptr) {
return;
@@ -80,7 +80,7 @@ void RenderFramebuffer::Bind()
context_->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, buffer_);
}
void RenderFramebuffer::Release()
void OpenGLFramebuffer::Release()
{
if (context_ == nullptr) {
return;
@@ -88,7 +88,7 @@ void RenderFramebuffer::Release()
context_->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
void RenderFramebuffer::Attach(RenderTexturePtr texture)
void OpenGLFramebuffer::Attach(RenderTexturePtr texture)
{
if (context_ == nullptr) {
return;
@@ -98,7 +98,7 @@ void RenderFramebuffer::Attach(RenderTexturePtr texture)
AttachInternal(texture_->texture(), false);
}
void RenderFramebuffer::AttachBackBuffer(RenderTexturePtr texture)
void OpenGLFramebuffer::AttachBackBuffer(RenderTexturePtr texture)
{
if (context_ == nullptr) {
return;
@@ -108,7 +108,7 @@ void RenderFramebuffer::AttachBackBuffer(RenderTexturePtr texture)
AttachInternal(texture_->back_texture(), true);
}
void RenderFramebuffer::Detach()
void OpenGLFramebuffer::Detach()
{
if (context_ == nullptr) {
return;
@@ -129,12 +129,12 @@ void RenderFramebuffer::Detach()
texture_ = nullptr;
}
const GLuint &RenderFramebuffer::buffer() const
const GLuint &OpenGLFramebuffer::buffer() const
{
return buffer_;
}
void RenderFramebuffer::AttachInternal(GLuint tex, bool clear)
void OpenGLFramebuffer::AttachInternal(GLuint tex, bool clear)
{
Detach();
@@ -18,23 +18,21 @@
***/
#ifndef RENDERFRAMEBUFFER_H
#define RENDERFRAMEBUFFER_H
#ifndef OPENGLFRAMEBUFFER_H
#define OPENGLFRAMEBUFFER_H
#include <QOpenGLContext>
#include "rendertexture.h"
#include "opengltexture.h"
class RenderFramebuffer : public QObject
class OpenGLFramebuffer : public QObject
{
Q_OBJECT
public:
RenderFramebuffer();
~RenderFramebuffer();
RenderFramebuffer(const RenderFramebuffer& other) = delete;
RenderFramebuffer(RenderFramebuffer&& other) = delete;
RenderFramebuffer& operator=(const RenderFramebuffer& other) = delete;
RenderFramebuffer& operator=(RenderFramebuffer&& other) = delete;
OpenGLFramebuffer();
virtual ~OpenGLFramebuffer() override;
Q_DISABLE_COPY_MOVE(OpenGLFramebuffer)
void Create(QOpenGLContext *ctx);
@@ -65,4 +63,4 @@ private:
RenderTexturePtr texture_;
};
#endif // RENDERFRAMEBUFFER_H
#endif // OPENGLFRAMEBUFFER_H
+120
View File
@@ -0,0 +1,120 @@
#include "openglshader.h"
OpenGLShader::OpenGLShader()
{
}
OpenGLShaderPtr OpenGLShader::CreateDefault(const QString &function_name, const QString &shader_code)
{
OpenGLShaderPtr program = std::make_shared<OpenGLShader>();
// Add shaders to program
program->addShaderFromSourceCode(QOpenGLShader::Vertex, CodeDefaultVertex());
program->addShaderFromSourceCode(QOpenGLShader::Fragment, CodeDefaultFragment(function_name, shader_code));
program->link();
return program;
}
QString OpenGLShader::CodeDefaultFragment(const QString &function_name, const QString &shader_code)
{
QString frag_code = QStringLiteral("#version 110\n"
"\n"
"#ifdef GL_ES\n"
"precision highp int;\n"
"precision highp float;\n"
"#endif\n"
"\n"
"uniform sampler2D texture;\n"
"uniform bool color_only;\n"
"uniform vec4 color_only_color;\n"
"varying vec2 v_texcoord;\n"
"\n");
// Finish the function with the main function
// Check if additional code was passed to this function, add it here
if (shader_code.isEmpty()) {
// If not, just add a pure main() function
frag_code.append(QStringLiteral("\n"
"void main() {\n"
" if (color_only) {\n"
" gl_FragColor = color_only_color;"
" } else {\n"
" vec4 color = texture2D(texture, v_texcoord);\n"
" gl_FragColor = color;\n"
" }\n"
"}\n"));
} else {
// If additional code was passed, add it and reference it in main().
//
// The function in the additional code is expected to be `vec4 function_name(vec4 color)`. The texture coordinate
// can be acquired through `v_texcoord`.
frag_code.append(shader_code);
frag_code.append(QString(QStringLiteral("\n"
"void main() {\n"
" vec4 color = %1(texture2D(texture, v_texcoord));\n"
" gl_FragColor = color;\n"
"}\n")).arg(function_name));
}
return frag_code;
}
QString OpenGLShader::CodeDefaultVertex()
{
// Generate vertex shader
return QStringLiteral("#version 110\n"
"\n"
"#ifdef GL_ES\n"
"precision highp int;\n"
"precision highp float;\n"
"#endif\n"
"\n"
"uniform mat4 mvp_matrix;\n"
"\n"
"attribute vec4 a_position;\n"
"attribute vec2 a_texcoord;\n"
"\n"
"varying vec2 v_texcoord;\n"
"\n"
"void main() {\n"
" gl_Position = mvp_matrix * a_position;\n"
" v_texcoord = a_texcoord;\n"
"}\n");
}
QString OpenGLShader::CodeAlphaDisassociate(const QString &function_name)
{
return QString(QStringLiteral("vec4 %1(vec4 col) {\n"
" if (col.a > 0.0) {\n"
" return vec4(col.rgb / col.a, col.a);"
" }\n"
" return col;\n"
"}\n")).arg(function_name);
}
QString OpenGLShader::CodeAlphaReassociate(const QString &function_name)
{
return QString(QStringLiteral("vec4 %1(vec4 col) {\n"
" if (col.a > 0.0) {\n"
" return vec4(col.rgb * col.a, col.a);"
" }\n"
" return col;\n"
"}\n")).arg(function_name);
}
QString OpenGLShader::CodeAlphaAssociate(const QString &function_name)
{
return QString(QStringLiteral("vec4 %1(vec4 col) {\n"
" return vec4(col.rgb * col.a, col.a);\n"
"}\n")).arg(function_name);
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef OPENGLSHADER_H
#define OPENGLSHADER_H
#include <memory>
#include <QOpenGLShaderProgram>
class OpenGLShader;
using OpenGLShaderPtr = std::shared_ptr<OpenGLShader>;
class OpenGLShader : public QOpenGLShaderProgram {
public:
OpenGLShader();
static OpenGLShaderPtr CreateDefault(const QString &function_name = QString(),
const QString &shader_code = QString());
static QString CodeDefaultFragment(const QString &function_name = QString(),
const QString &shader_code = QString());
static QString CodeDefaultVertex();
static QString CodeAlphaDisassociate(const QString& function_name);
static QString CodeAlphaReassociate(const QString& function_name);
static QString CodeAlphaAssociate(const QString& function_name);
private:
};
#endif // OPENGLSHADER_H
@@ -18,14 +18,14 @@
***/
#include "rendertexture.h"
#include "opengltexture.h"
#include <QDateTime>
#include <QDebug>
#include "render/pixelservice.h"
RenderTexture::RenderTexture() :
OpenGLTexture::OpenGLTexture() :
context_(nullptr),
texture_(0),
back_texture_(0),
@@ -35,22 +35,22 @@ RenderTexture::RenderTexture() :
{
}
RenderTexture::~RenderTexture()
OpenGLTexture::~OpenGLTexture()
{
Destroy();
}
bool RenderTexture::IsCreated() const
bool OpenGLTexture::IsCreated() const
{
return (texture_ != 0);
}
void RenderTexture::Create(QOpenGLContext *ctx, int width, int height, const olive::PixelFormat &format, void* data)
void OpenGLTexture::Create(QOpenGLContext *ctx, int width, int height, const olive::PixelFormat &format, void* data)
{
Create(ctx, width, height, format, kSingleBuffer, data);
}
void RenderTexture::Create(QOpenGLContext *ctx, int width, int height, const olive::PixelFormat &format, const RenderTexture::Type &type, void *data)
void OpenGLTexture::Create(QOpenGLContext *ctx, int width, int height, const olive::PixelFormat &format, const OpenGLTexture::Type &type, void *data)
{
if (ctx == nullptr) {
qWarning() << tr("RenderTexture::Create was passed an invalid context");
@@ -75,7 +75,7 @@ void RenderTexture::Create(QOpenGLContext *ctx, int width, int height, const oli
}
}
void RenderTexture::Destroy()
void OpenGLTexture::Destroy()
{
if (context_ != nullptr) {
disconnect(context_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Destroy()));
@@ -90,7 +90,7 @@ void RenderTexture::Destroy()
}
}
void RenderTexture::Bind()
void OpenGLTexture::Bind()
{
if (context_ == nullptr) {
qWarning() << "RenderTexture::Bind() called with an invalid context";
@@ -100,7 +100,7 @@ void RenderTexture::Bind()
context_->functions()->glBindTexture(GL_TEXTURE_2D, texture_);
}
void RenderTexture::Release()
void OpenGLTexture::Release()
{
if (context_ == nullptr) {
qWarning() << "RenderTexture::Release() called with an invalid context";
@@ -110,44 +110,44 @@ void RenderTexture::Release()
context_->functions()->glBindTexture(GL_TEXTURE_2D, 0);
}
const int &RenderTexture::width() const
const int &OpenGLTexture::width() const
{
return width_;
}
const int &RenderTexture::height() const
const int &OpenGLTexture::height() const
{
return height_;
}
const olive::PixelFormat &RenderTexture::format() const
const olive::PixelFormat &OpenGLTexture::format() const
{
return format_;
}
QOpenGLContext *RenderTexture::context() const
QOpenGLContext *OpenGLTexture::context() const
{
return context_;
}
const GLuint &RenderTexture::texture() const
const GLuint &OpenGLTexture::texture() const
{
return texture_;
}
const GLuint &RenderTexture::back_texture() const
const GLuint &OpenGLTexture::back_texture() const
{
return back_texture_;
}
void RenderTexture::SwapFrontAndBack()
void OpenGLTexture::SwapFrontAndBack()
{
GLuint temp = texture_;
texture_ = back_texture_;
back_texture_ = temp;
}
void RenderTexture::Upload(const void *data)
void OpenGLTexture::Upload(const void *data)
{
if (!IsCreated()) {
qWarning() << tr("RenderTexture::Upload() called while it wasn't created");
@@ -171,7 +171,7 @@ void RenderTexture::Upload(const void *data)
Release();
}
uchar *RenderTexture::Download() const
uchar *OpenGLTexture::Download() const
{
if (!IsCreated()) {
qWarning() << tr("RenderTexture::Download() called while it wasn't created");
@@ -203,7 +203,7 @@ uchar *RenderTexture::Download() const
return data;
}
void RenderTexture::CreateInternal(GLuint* tex, void *data)
void OpenGLTexture::CreateInternal(GLuint* tex, void *data)
{
QOpenGLFunctions* f = context_->functions();
@@ -18,15 +18,15 @@
***/
#ifndef RENDERTEXTURE_H
#define RENDERTEXTURE_H
#ifndef OPENGLTEXTURE_H
#define OPENGLTEXTURE_H
#include <memory>
#include <QOpenGLFunctions>
#include "pixelformat.h"
#include "render/pixelformat.h"
class RenderTexture : public QObject
class OpenGLTexture : public QObject
{
Q_OBJECT
public:
@@ -35,12 +35,10 @@ public:
kDoubleBuffer
};
RenderTexture();
~RenderTexture();
RenderTexture(const RenderTexture& other) = delete;
RenderTexture(RenderTexture&& other) = delete;
RenderTexture& operator=(const RenderTexture& other) = delete;
RenderTexture& operator=(RenderTexture&& other) = delete;
OpenGLTexture();
virtual ~OpenGLTexture() override;
Q_DISABLE_COPY_MOVE(OpenGLTexture)
void Create(QOpenGLContext* ctx, int width, int height, const olive::PixelFormat &format, void *data = nullptr);
void Create(QOpenGLContext* ctx, int width, int height, const olive::PixelFormat &format, const Type& type, void *data = nullptr);
@@ -86,7 +84,7 @@ private:
olive::PixelFormat format_;
};
using RenderTexturePtr = std::shared_ptr<RenderTexture>;
using RenderTexturePtr = std::shared_ptr<OpenGLTexture>;
Q_DECLARE_METATYPE(RenderTexturePtr)
#endif // RENDERTEXTURE_H
#endif // OPENGLTEXTURE_H
+77 -2
View File
@@ -8,7 +8,6 @@ RenderBackend::RenderBackend() :
RenderBackend::~RenderBackend()
{
}
const QString &RenderBackend::GetError()
@@ -16,13 +15,89 @@ const QString &RenderBackend::GetError()
return error_;
}
void RenderBackend::set_viewer_node(ViewerOutput *viewer_node)
void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
{
if (viewer_node_ != nullptr) {
disconnect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(Compile()));
}
viewer_node_ = viewer_node;
if (viewer_node_ != nullptr) {
connect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(Compile()));
}
Decompile();
}
void RenderBackend::InvalidateCache(const rational &start_range, const rational &end_range)
{
if (!params_.is_valid()) {
return;
}
// Adjust range to min/max values
rational start_range_adj = qMax(rational(0), start_range);
rational end_range_adj = qMin(viewer_node_->Length(), end_range);
qDebug() << "Cache invalidated between"
<< start_range_adj.toDouble()
<< "and"
<< end_range_adj.toDouble();
// Snap start_range to timebase
double start_range_dbl = start_range_adj.toDouble();
double start_range_numf = start_range_dbl * static_cast<double>(params_.time_base().denominator());
int64_t start_range_numround = qFloor(start_range_numf/static_cast<double>(params_.time_base().numerator())) * params_.time_base().numerator();
rational true_start_range(start_range_numround, params_.time_base().denominator());
for (rational r=true_start_range;r<=end_range_adj;r+=params_.time_base()) {
// Try to order the queue from closest to the playhead to furthest
rational last_time = last_time_requested_;
rational diff = r - last_time;
if (diff < 0) {
// FIXME: Hardcoded number
// If the number is before the playhead, we still prioritize its closeness but not nearly as much (5:1 in this
// example)
diff = qAbs(diff) * 5;
}
bool contains = false;
bool added = false;
QLinkedList<rational>::iterator insert_iterator;
for (QLinkedList<rational>::iterator i = cache_queue_.begin();i != cache_queue_.end();i++) {
rational compare = *i;
if (!added) {
rational compare_diff = compare - last_time;
if (compare_diff > diff) {
insert_iterator = i;
added = true;
}
}
if (compare == r) {
contains = true;
break;
}
}
if (!contains) {
if (added) {
cache_queue_.insert(insert_iterator, r);
} else {
cache_queue_.append(r);
}
}
}
CacheNext();
}
void RenderBackend::SetError(const QString &error)
{
error_ = error;
+11 -6
View File
@@ -3,28 +3,33 @@
#include "node/output/viewer/viewer.h"
class RenderBackend : QObject
class RenderBackend : public QObject
{
Q_OBJECT
public:
RenderBackend();
virtual ~RenderBackend();
virtual ~RenderBackend() override;
Q_DISABLE_COPY_MOVE(RenderBackend)
virtual bool Init() = 0;
virtual void GenerateFrame(const rational& time) = 0;
virtual void GenerateSamples(const rational& time, const rational& length) = 0;
virtual void Close() = 0;
const QString& GetError();
void set_viewer_node(ViewerOutput* viewer_node);
void SetViewerNode(ViewerOutput* viewer_node);
public slots:
virtual void InvalidateCache(const rational &start_range, const rational &end_range) = 0;
virtual bool Compile() = 0;
protected:
virtual void Decompile() = 0;
protected:
void SetError(const QString& error);
ViewerOutput* viewer_node() const;
@@ -18,8 +18,8 @@
***/
#ifndef RENDERER_H
#define RENDERER_H
#ifndef VIDEORENDERERBACKEND_H
#define VIDEORENDERERBACKEND_H
#include <QLinkedList>
#include <QOpenGLTexture>
@@ -166,8 +166,8 @@ private:
RenderTexturePtr master_texture_;
rational push_time_;
RenderFramebuffer copy_buffer_;
ShaderPtr copy_pipeline_;
OpenGLFramebuffer copy_buffer_;
OpenGLShaderPtr copy_pipeline_;
QMap<rational, QByteArray> time_hash_map_;
@@ -193,4 +193,4 @@ private slots:
};
#endif // RENDERER_H
#endif // VIDEORENDERERBACKEND_H
@@ -16,10 +16,7 @@
set(OLIVE_SOURCES
${OLIVE_SOURCES}
render/gl/functions.h
render/gl/functions.cpp
render/gl/shadergenerators.h
render/gl/shadergenerators.cpp
render/gl/shaderptr.h
render/backend/vulkan/vulkanbackend.h
render/backend/vulkan/vulkanbackend.cpp
PARENT_SCOPE
)
@@ -0,0 +1,6 @@
#include "vulkanbackend.h"
VulkanBackend::VulkanBackend()
{
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef VULKANBACKEND_H
#define VULKANBACKEND_H
class VulkanBackend
{
public:
VulkanBackend();
};
#endif // VULKANBACKEND_H
-1
View File
@@ -5,7 +5,6 @@
#include "colorprocessor.h"
#include "decoder/frame.h"
#include "render/gl/shadergenerators.h"
class ColorManager : public QObject
{
-254
View File
@@ -1,254 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#include "shadergenerators.h"
#include <QOpenGLExtraFunctions>
namespace olive {
ShaderPtr ShaderGenerator::DefaultPipeline(const QString& function_name, const QString& shader_code)
{
ShaderPtr program = std::make_shared<QOpenGLShaderProgram>();
// Generate vertex shader
QString vert_shader = "#version 110\n"
"\n"
"#ifdef GL_ES\n"
"precision mediump int;\n"
"precision mediump float;\n"
"#endif\n"
"\n"
"uniform mat4 mvp_matrix;\n"
"\n"
"attribute vec4 a_position;\n"
"attribute vec2 a_texcoord;\n"
"\n"
"varying vec2 v_texcoord;\n"
"\n"
"void main() {\n"
" gl_Position = mvp_matrix * a_position;\n"
" v_texcoord = a_texcoord;\n"
"}\n";
// Generate fragment shader
QString frag_shader = "#version 110\n"
"\n"
"#ifdef GL_ES\n"
"precision mediump int;\n"
"precision mediump float;\n"
"#endif\n"
"\n"
"uniform sampler2D texture;\n"
"uniform float opacity;\n"
"uniform bool color_only;\n"
"uniform vec4 color_only_color;\n"
"varying vec2 v_texcoord;\n"
"\n";
// Finish the function with the main function
// Check if additional code was passed to this function, add it here
if (shader_code.isEmpty()) {
// If not, just add a pure main() function
frag_shader.append("\n"
"void main() {\n"
" if (color_only) {\n"
" gl_FragColor = color_only_color;"
" } else {\n"
" vec4 color = texture2D(texture, v_texcoord)*opacity;\n"
" gl_FragColor = color;\n"
" }\n"
"}\n");
} else {
// If additional code was passed, add it and reference it in main().
//
// The function in the additional code is expected to be `vec4 function_name(vec4 color)`. The texture coordinate
// can be acquired through `v_texcoord`.
frag_shader.append(shader_code);
frag_shader.append(QString("\n"
"void main() {\n"
" vec4 color = %1(texture2D(texture, v_texcoord))*opacity;\n"
" gl_FragColor = color;\n"
"}\n").arg(function_name));
}
// Add shaders to program
program->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_shader);
program->addShaderFromSourceCode(QOpenGLShader::Fragment, frag_shader);
program->link();
// Set opacity default to 100%
program->bind();
program->setUniformValue("opacity", 1.0f);
program->release();
return program;
}
QString ShaderGenerator::AlphaDisassociateFunction(const QString &function_name)
{
return QString("vec4 %1(vec4 col) {\n"
" if (col.a > 0.0) {\n"
" return vec4(col.rgb / col.a, col.a);"
" }\n"
" return col;\n"
"}\n").arg(function_name);
}
QString ShaderGenerator::AlphaReassociateFunction(const QString &function_name)
{
return QString("vec4 %1(vec4 col) {\n"
" if (col.a > 0.0) {\n"
" return vec4(col.rgb * col.a, col.a);"
" }\n"
" return col;\n"
"}\n").arg(function_name);
}
QString ShaderGenerator::AlphaAssociateFunction(const QString &function_name)
{
return QString("vec4 %1(vec4 col) {\n"
" return vec4(col.rgb * col.a, col.a);\n"
"}\n").arg(function_name);
}
// copied from source code to OCIODisplay
const int OCIO_LUT3D_EDGE_SIZE = 32;
// copied from source code to OCIODisplay, expanded from 3*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE
const int OCIO_NUM_3D_ENTRIES = 98304;
ShaderPtr ShaderGenerator::OCIOPipeline(QOpenGLContext* ctx,
GLuint& lut_texture,
OCIO::ConstProcessorRcPtr processor,
bool alpha_is_associated)
{
QOpenGLExtraFunctions* xf = ctx->extraFunctions();
// Create LUT texture
xf->glGenTextures(1, &lut_texture);
// Bind LUT
xf->glBindTexture(GL_TEXTURE_3D, lut_texture);
// Set texture parameters
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
// Allocate storage for texture
xf->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB,
OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE,
0, GL_RGB,GL_FLOAT, nullptr);
//
// SET UP GLSL SHADER
//
OCIO::GpuShaderDesc shaderDesc;
const char* ocio_func_name = "OCIODisplay";
shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0);
shaderDesc.setFunctionName(ocio_func_name);
shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE);
//
// COMPUTE 3D LUT
//
GLfloat* ocio_lut_data = new GLfloat[OCIO_NUM_3D_ENTRIES];
processor->getGpuLut3D(ocio_lut_data, shaderDesc);
// Upload LUT data to texture
xf->glTexSubImage3D(GL_TEXTURE_3D, 0,
0, 0, 0,
OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE,
GL_RGB, GL_FLOAT, ocio_lut_data);
delete [] ocio_lut_data;
// Create OCIO shader code
QString shader_text(processor->getGpuShaderText(shaderDesc));
QString shader_call;
// Enforce alpha association
if (alpha_is_associated) {
// If alpha is already associated, we'll need to disassociate and reassociate
shader_text.append("\n");
QString disassociate_func_name = "disassoc";
shader_text.append(AlphaDisassociateFunction(disassociate_func_name));
QString reassociate_func_name = "reassoc";
shader_text.append(AlphaReassociateFunction(reassociate_func_name));
// Make OCIO call pass through disassociate and reassociate function
shader_call = QString("%3(%1(%2(col), tex2));").arg(ocio_func_name,
disassociate_func_name,
reassociate_func_name);
} else {
// If alpha is not already associated, we can just associate after OCIO
// Add associate function
QString associate_func_name = "assoc";
shader_text.append(AlphaAssociateFunction(associate_func_name));
// Make OCIO call pass through associate function
shader_call = QString("%2(%1(col, tex2));").arg(ocio_func_name, associate_func_name);
}
// Add process() function, which GetPipeline() will call if specified
QString process_function_name = "process";
shader_text.append(QString("\n"
"uniform sampler3D tex2;\n"
"\n"
"vec4 %2(vec4 col) {\n"
" return %1\n"
"}\n").arg(shader_call, process_function_name));
// Get pipeline-based shader to inject OCIO shader into
ShaderPtr shader = ShaderGenerator::DefaultPipeline(process_function_name, shader_text);
// Release LUT
xf->glBindTexture(GL_TEXTURE_3D, 0);
return shader;
}
}
-55
View File
@@ -1,55 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#ifndef SHADERGENERATORS_H
#define SHADERGENERATORS_H
#include <OpenColorIO/OpenColorIO.h>
namespace OCIO = OCIO_NAMESPACE::v1;
#include "shaderptr.h"
/**
*
* Olive standardizes on OpenGL 3.2 Core which has no fixed pipeline. Instead, the pipeline is provided by the
* programmer in the form of a shader. This is a collection of OpenGL shader pipeline generators for use throughout
* Olive.
*
*/
namespace olive {
class ShaderGenerator {
public:
static ShaderPtr DefaultPipeline(const QString &function_name = QString(), const QString &shader_code = QString());
static ShaderPtr OCIOPipeline(QOpenGLContext *ctx,
GLuint &lut_texture,
OCIO::ConstProcessorRcPtr processor,
bool alpha_is_associated);
static QString AlphaDisassociateFunction(const QString& function_name);
static QString AlphaReassociateFunction(const QString& function_name);
static QString AlphaAssociateFunction(const QString& function_name);
};
}
#endif // SHADERGENERATORS_H
-32
View File
@@ -1,32 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#ifndef QOPENGLSHADERPROGRAMPTR_H
#define QOPENGLSHADERPROGRAMPTR_H
#include <QOpenGLShaderProgram>
#include <memory>
/**
* @brief A simple shared_ptr around QOpenGLShaderProgram to simplify shader creation/destruction
*/
using ShaderPtr = std::shared_ptr<QOpenGLShaderProgram>;
#endif // QOPENGLSHADERPROGRAMPTR_H
-125
View File
@@ -1,125 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#include "renderinstance.h"
#include <QDebug>
#include "render/gl/shadergenerators.h"
RenderInstance::RenderInstance(const VideoRenderingParams& params) :
share_ctx_(nullptr),
params_(params)
{
// Create offscreen surface
surface_.create();
}
RenderInstance::~RenderInstance()
{
// Destroy offscreen surface
surface_.destroy();
}
void RenderInstance::SetShareContext(QOpenGLContext *share)
{
Q_ASSERT(!IsStarted());
share_ctx_ = share;
}
bool RenderInstance::Start()
{
if (IsStarted()) {
return true;
}
// Create context object
ctx_ = new QOpenGLContext();
// If we're sharing resources, set this up now
if (share_ctx_ != nullptr) {
ctx_->setShareContext(share_ctx_);
}
// Create OpenGL context (automatically destroys any existing if there is one)
if (!ctx_->create()) {
qWarning() << tr("Failed to create OpenGL context in thread %1").arg(reinterpret_cast<quintptr>(this));
return false;
}
// Make context current on that surface
if (!ctx_->makeCurrent(&surface_)) {
qWarning() << tr("Failed to makeCurrent() on offscreen surface in thread %1").arg(reinterpret_cast<quintptr>(this));
return false;
}
buffer_.Create(ctx_);
// Set viewport to the compositing dimensions
ctx_->functions()->glViewport(0, 0, params_.effective_width(), params_.effective_height());
ctx_->functions()->glEnable(GL_BLEND);
// Set up default pipeline
default_pipeline_ = olive::ShaderGenerator::DefaultPipeline();
return true;
}
void RenderInstance::Stop()
{
if (IsStarted()) {
return;
}
// Destroy pipeline
default_pipeline_ = nullptr;
// Destroy buffer
buffer_.Destroy();
// Destroy context
delete ctx_;
}
bool RenderInstance::IsStarted()
{
return buffer_.IsCreated();
}
RenderFramebuffer *RenderInstance::buffer()
{
return &buffer_;
}
QOpenGLContext *RenderInstance::context()
{
return ctx_;
}
const VideoRenderingParams &RenderInstance::params() const
{
return params_;
}
ShaderPtr RenderInstance::default_pipeline() const
{
return default_pipeline_;
}
-98
View File
@@ -1,98 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#ifndef GLINSTANCE_H
#define GLINSTANCE_H
#include <QMatrix4x4>
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include "render/gl/shaderptr.h"
#include "render/renderframebuffer.h"
#include "render/rendermodes.h"
#include "render/videoparams.h"
/**
* @brief An object containing all resources necessary for each thread to support hardware accelerated rendering
*
* RenderInstance contains everything that Nodes will need to draw with on a per-thread basis.
*
* Due to its usage of QOffscreenSurface, a RenderInstance instance must be constructed in the main (GUI) thread. From
* there it is safe to call Start() on in another thread.
*/
class RenderInstance : public QObject
{
public:
RenderInstance(const VideoRenderingParams &params);
virtual ~RenderInstance() override;
/**
* @brief Deleted copy constructor
*/
RenderInstance(const RenderInstance& other) = delete;
/**
* @brief Deleted move constructor
*/
RenderInstance(RenderInstance&& other) = delete;
/**
* @brief Deleted copy assignment
*/
RenderInstance& operator=(const RenderInstance& other) = delete;
/**
* @brief Deleted move assignment
*/
RenderInstance& operator=(RenderInstance&& other) = delete;
void SetShareContext(QOpenGLContext* share);
bool Start();
void Stop();
bool IsStarted();
RenderFramebuffer* buffer();
QOpenGLContext* context();
const VideoRenderingParams& params() const;
ShaderPtr default_pipeline() const;
private:
QOpenGLContext* ctx_;
QOpenGLContext* share_ctx_;
QOffscreenSurface surface_;
RenderFramebuffer buffer_;
VideoRenderingParams params_;
ShaderPtr default_pipeline_;
};
#endif // GLINSTANCE_H
@@ -1,83 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#include "videorendererthreadbase.h"
#include <QDebug>
VideoRendererThreadBase::VideoRendererThreadBase(QOpenGLContext *share_ctx, const VideoRenderingParams &params) :
share_ctx_(share_ctx),
render_instance_(params)
{
connect(share_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Cancel()));
}
RenderInstance *VideoRendererThreadBase::render_instance()
{
return &render_instance_;
}
void VideoRendererThreadBase::run()
{
// Lock mutex for main loop
mutex_.lock();
render_instance_.SetShareContext(share_ctx_);
// Allocate and create resources
bool started = render_instance_.Start();
// Signal that main thread can continue now
WakeCaller();
if (started) {
// Main loop (use Cancel() to exit it)
ProcessLoop();
}
// Free all resources
render_instance_.Stop();
// Unlock mutex before exiting
mutex_.unlock();
}
void VideoRendererThreadBase::WakeCaller()
{
// Signal that main thread can continue now
caller_mutex_.lock();
wait_cond_.wakeAll();
caller_mutex_.unlock();
}
void VideoRendererThreadBase::StartThread(QThread::Priority priority)
{
caller_mutex_.lock();
// Start the thread
QThread::start(priority);
// Wait for thread to finish completion
wait_cond_.wait(&caller_mutex_);
caller_mutex_.unlock();
}
@@ -1,67 +0,0 @@
/***
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 <http://www.gnu.org/licenses/>.
***/
#ifndef RENDERTHREAD_H
#define RENDERTHREAD_H
#include <memory>
#include <QMutex>
#include <QThread>
#include <QWaitCondition>
#include "node/node.h"
#include "render/renderinstance.h"
class VideoRendererThreadBase : public QThread
{
Q_OBJECT
public:
VideoRendererThreadBase(QOpenGLContext* share_ctx, const VideoRenderingParams& params);
RenderInstance* render_instance();
void StartThread(Priority priority = InheritPriority);
virtual void run() override;
public slots:
virtual void Cancel() = 0;
protected:
virtual void ProcessLoop() = 0;
QWaitCondition wait_cond_;
QMutex mutex_;
QMutex caller_mutex_;
private:
void WakeCaller();
QOpenGLContext* share_ctx_;
RenderInstance render_instance_;
};
using RendererThreadPtr = std::shared_ptr<VideoRendererThreadBase>;
#endif // RENDERTHREAD_H
+1 -1
View File
@@ -78,7 +78,7 @@ void AudioMonitor::paintEvent(QPaintEvent *)
qreal log_val = QAudio::convertVolume(i, QAudio::DecibelVolumeScale, QAudio::LogarithmicVolumeScale);
QRect db_marking_rect = db_labels_rect;
db_marking_rect.adjust(0, db_labels_rect.y() + db_labels_rect.height() - qRound(log_val * db_labels_rect.height()), 0, 0);
db_marking_rect.adjust(0, db_labels_rect.height() - qRound(log_val * db_labels_rect.height()), 0, 0);
db_marking_rect.setHeight(fm.height());
// Prevent any dB markings overlapping
+1
View File
@@ -161,6 +161,7 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node)
}
video_renderer_->SetViewerNode(viewer_node_);
opengl_backend_.SetViewerNode(viewer_node_);
}
void ViewerWidget::DisconnectViewerNode()
+2 -8
View File
@@ -30,9 +30,7 @@
#include "common/rational.h"
#include "node/output/viewer/viewer.h"
#include "render/audio/audiorenderer.h"
#include "render/video/videorenderer.h"
#include "render/backend/openclbackend.h"
#include "render/backend/opengl/openglbackend.h"
#include "viewerglwidget.h"
#include "viewersizer.h"
#include "widget/playbackcontrols/playbackcontrols.h"
@@ -112,12 +110,8 @@ private:
void PushScrubbedAudio();
VideoRendererProcessor* video_renderer_;
AudioRendererProcessor* audio_renderer_;
// FIXME: Test code only
OpenCLBackend opencl_backend_;
OpenGLBackend opengl_backend_;
// End test code
ViewerSizer* sizer_;
+2 -2
View File
@@ -24,7 +24,7 @@
#include <QOpenGLWidget>
#include "render/colormanager.h"
#include "render/gl/shaderptr.h"
#include "render/backend/opengl/openglshader.h"
/**
* @brief The inner display/rendering widget of a Viewer class.
@@ -143,7 +143,7 @@ private:
*
* Retrieved every initializeGL() in order to stay up to date when new contexts are generated.
*/
ShaderPtr pipeline_;
OpenGLShaderPtr pipeline_;
/**
* @brief OCIO LUT texture used for conversions