started porting renderer to new portable form
This commit is contained in:
@@ -96,6 +96,7 @@ target_link_libraries(${OLIVE_TARGET}
|
||||
FFMPEG::swresample
|
||||
${OPENCOLORIO_LIBRARIES}
|
||||
${OIIO_LIBRARIES}
|
||||
OpenCL
|
||||
)
|
||||
|
||||
set(OLIVE_EFFECTS
|
||||
|
||||
+1
-1
@@ -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
@@ -5,7 +5,7 @@
|
||||
#include <QFile>
|
||||
|
||||
#include "audio/sampleformat.h"
|
||||
#include "render/audio/audioparams.h"
|
||||
#include "render/audioparams.h"
|
||||
|
||||
class WaveOutput
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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_;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -87,9 +87,6 @@ public:
|
||||
/// Resolves to `Block*`
|
||||
kBlock,
|
||||
|
||||
/// Resolves to `QList<Block*>`
|
||||
kBlockList,
|
||||
|
||||
/// Resolves to `Footage*`
|
||||
kFootage,
|
||||
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 ¶ms);
|
||||
|
||||
/**
|
||||
* @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 ÷r,
|
||||
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 ÷r,
|
||||
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 ¶ms) :
|
||||
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 ¶ms);
|
||||
|
||||
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 ¶ms) :
|
||||
params_(params)
|
||||
{
|
||||
}
|
||||
|
||||
AudioParams *AudioRendererThreadBase::params()
|
||||
{
|
||||
return ¶ms_;
|
||||
}
|
||||
|
||||
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 ¶ms);
|
||||
|
||||
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
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,6 +0,0 @@
|
||||
#include "cudabackend.h"
|
||||
|
||||
CUDABackend::CUDABackend()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef CUDABACKEND_H
|
||||
#define CUDABACKEND_H
|
||||
|
||||
|
||||
class CUDABackend
|
||||
{
|
||||
public:
|
||||
CUDABackend();
|
||||
};
|
||||
|
||||
#endif // CUDABACKEND_H
|
||||
@@ -1,6 +0,0 @@
|
||||
#include "metalbackend.h"
|
||||
|
||||
MetalBackend::MetalBackend()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef METALBACKEND_H
|
||||
#define METALBACKEND_H
|
||||
|
||||
|
||||
class MetalBackend
|
||||
{
|
||||
public:
|
||||
MetalBackend();
|
||||
};
|
||||
|
||||
#endif // METALBACKEND_H
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
+13
-13
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef VULKANBACKEND_H
|
||||
#define VULKANBACKEND_H
|
||||
|
||||
|
||||
class VulkanBackend
|
||||
{
|
||||
public:
|
||||
VulkanBackend();
|
||||
};
|
||||
|
||||
#endif // VULKANBACKEND_H
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include "colorprocessor.h"
|
||||
#include "decoder/frame.h"
|
||||
#include "render/gl/shadergenerators.h"
|
||||
|
||||
class ColorManager : public QObject
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_;
|
||||
}
|
||||
@@ -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 ¶ms);
|
||||
|
||||
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 ¶ms) :
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -161,6 +161,7 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node)
|
||||
}
|
||||
|
||||
video_renderer_->SetViewerNode(viewer_node_);
|
||||
opengl_backend_.SetViewerNode(viewer_node_);
|
||||
}
|
||||
|
||||
void ViewerWidget::DisconnectViewerNode()
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user