尝试修复粉紫屏未果

This commit is contained in:
2026-01-16 21:11:24 +08:00
parent 3f774dfa53
commit e851653fa3
30 changed files with 1377 additions and 238 deletions
+145 -10
View File
@@ -21,6 +21,9 @@
#include "render/rendermanager.h"
#include "render/job/pluginjob.h"
#include "pluginSupport/OlivePluginInstance.h"
#include <algorithm>
#include <cstring>
static QString ClipLabelForName(const std::string &name,
const OFX::Host::ImageEffect::ClipDescriptor *desc)
{
@@ -150,6 +153,71 @@ olive::plugin::PluginNode::PluginNode(
SetInputProperty(input_id, QStringLiteral("ui_page"),
page_for_param.value(input_id));
}
if (type == NodeValue::kCombo || type == NodeValue::kStrCombo) {
QStringList option_labels;
QStringList option_values;
const int label_count =
props.getDimension(kOfxParamPropChoiceOption);
const int value_count =
props.getDimension(kOfxParamPropChoiceEnum);
for (int i = 0; i < label_count; ++i) {
const std::string &label =
props.getStringProperty(kOfxParamPropChoiceOption, i);
option_labels.append(QString::fromStdString(label));
}
for (int i = 0; i < value_count; ++i) {
const std::string &value =
props.getStringProperty(kOfxParamPropChoiceEnum, i);
option_values.append(QString::fromStdString(value));
}
if (option_labels.isEmpty() && !option_values.isEmpty()) {
option_labels = option_values;
}
if (option_values.isEmpty() && !option_labels.isEmpty()) {
option_values = option_labels;
}
const int order_count =
props.getDimension(kOfxParamPropChoiceOrder);
if (order_count == option_labels.size() &&
option_labels.size() == option_values.size()) {
QVector<int> indices(option_labels.size());
for (int i = 0; i < indices.size(); ++i) {
indices[i] = i;
}
std::stable_sort(indices.begin(), indices.end(),
[&](int a, int b) {
return props.getIntProperty(
kOfxParamPropChoiceOrder,
a) <
props.getIntProperty(
kOfxParamPropChoiceOrder,
b);
});
QStringList ordered_labels;
QStringList ordered_values;
for (int index : indices) {
ordered_labels.append(option_labels.at(index));
ordered_values.append(option_values.at(index));
}
option_labels = ordered_labels;
option_values = ordered_values;
}
if (!option_labels.isEmpty()) {
SetComboBoxStrings(input_id, option_labels);
if (type == NodeValue::kStrCombo) {
SetInputProperty(input_id,
QStringLiteral("combo_value_str"),
option_values);
}
}
}
}
const auto &clips = plugin_instance_->getDescriptor().getClips();
@@ -162,9 +230,21 @@ olive::plugin::PluginNode::PluginNode(
SetInputName(input_id, ClipLabelForName(entry.first, entry.second));
has_texture_input = true;
}
if (!has_texture_input) {
AddInput(kTextureInput, NodeValue::kTexture);
SetInputName(kTextureInput, tr("Texture"));
const QString source_id =
QString::fromUtf8(kOfxImageEffectSimpleSourceClipName);
if (HasInputWithID(source_id)) {
SetEffectInput(source_id);
} else if (HasInputWithID(kTextureInput)) {
SetEffectInput(kTextureInput);
}
else {
if (has_texture_input) {
AddInput(kTextureInput, NodeValue::kTexture);
SetInputName(kTextureInput, tr("Texture"));
SetEffectInput(kTextureInput);
}
}
}
@@ -198,23 +278,77 @@ void olive::plugin::PluginNode::ProcessSamples(const NodeValueRow &values,
SampleBuffer &output,
int index) const
{
(void)values;
(void)input;
(void)output;
(void)index;
Q_UNUSED(values)
Q_UNUSED(index)
if (!input.is_allocated() || input.channel_count() == 0 ||
input.sample_count() == 0) {
if (output.is_allocated()) {
output.silence();
}
return;
}
if (!output.is_allocated() ||
output.channel_count() != input.channel_count() ||
output.sample_count() != input.sample_count()) {
output.set_audio_params(input.audio_params());
output.set_sample_count(input.sample_count());
output.allocate();
}
if (!output.is_allocated()) {
return;
}
for (int channel = 0; channel < input.channel_count(); ++channel) {
output.fast_set(input, channel);
}
}
void olive::plugin::PluginNode::GenerateFrame(FramePtr frame,
const GenerateJob &job) const
{
(void)frame;
(void)job;
Q_UNUSED(job)
if (!frame) {
return;
}
if (!frame->is_allocated()) {
frame->allocate();
}
if (!frame->is_allocated()) {
return;
}
std::memset(frame->data(), 0, static_cast<size_t>(frame->allocated_size()));
}
void olive::plugin::PluginNode::Value(const NodeValueRow &value,
const NodeGlobals &globals,
NodeValueTable *table) const
{
TexturePtr tex = value.value(kTextureInput).toTexture();
for (auto it = value.cbegin(); it != value.cend(); ++it) {
const NodeValue &input_value = it.value();
if (input_value.type() == NodeValue::kTexture ||
input_value.type() == NodeValue::kNone) {
continue;
}
NodeValue tagged = input_value;
tagged.set_tag(it.key());
table->Push(tagged);
}
TexturePtr tex = nullptr;
const QString source_key =
QString::fromUtf8(kOfxImageEffectSimpleSourceClipName);
if (value.contains(source_key)) {
tex = value.value(source_key).toTexture();
}
if (!tex) {
tex = value.value(kTextureInput).toTexture();
}
if (!tex) {
for (auto it = value.cbegin(); it != value.cend(); ++it) {
if (it.value().type() == NodeValue::kTexture) {
@@ -227,6 +361,7 @@ void olive::plugin::PluginNode::Value(const NodeValueRow &value,
}
if (tex && plugin_instance_) {
PluginJob job(plugin_instance_, this, value, globals.time().in());
table->Push(NodeValue::kTexture, tex->toJob(job), this);
}
}
+13 -20
View File
@@ -406,23 +406,14 @@ olive::plugin::OliveClipInstance::getImage(OfxTime time,
static_cast<int>(std::floor(rod_d.y1)),
static_cast<int>(std::ceil(rod_d.x2)),
static_cast<int>(std::ceil(rod_d.y2)) };
(void)optionalBounds;
// Always return full-frame images to keep input data consistent.
OfxRectI bounds = rod;
if (optionalBounds) {
bounds.x1 = static_cast<int>(std::floor(optionalBounds->x1));
bounds.y1 = static_cast<int>(std::floor(optionalBounds->y1));
bounds.x2 = static_cast<int>(std::ceil(optionalBounds->x2));
bounds.y2 = static_cast<int>(std::ceil(optionalBounds->y2));
}
// Clamp bounds to ROD to keep host/plugin coords consistent.
bounds.x1 = std::max(bounds.x1, rod.x1);
bounds.y1 = std::max(bounds.y1, rod.y1);
bounds.x2 = std::min(bounds.x2, rod.x2);
bounds.y2 = std::min(bounds.y2, rod.y2);
if (name_ == "Output") {
if (!images_.contains(time)) {
// make a new ref counted image
images_.insert(time, std::make_shared<Image>(*const_cast<OliveClipInstance *>(this),
images_.insert(time, new Image(*const_cast<OliveClipInstance *>(this),
params_, bounds, rod, true));
}
@@ -435,13 +426,13 @@ olive::plugin::OliveClipInstance::getImage(OfxTime time,
images_[time]->EnsureAllocatedFromParams(params_, bounds, rod, true);
// return it
return images_[time].get();
return images_[time];
} else {
if (images_.contains(time)) {
std::shared_ptr<Image> image = images_.value(time);
Image* image = images_.value(time);
image->EnsureAllocatedFromParams(params_, bounds, rod, false);
image->addReference();
return image.get();
return image;
}
// Fetch on demand for the input clip.
@@ -455,7 +446,7 @@ olive::plugin::OliveClipInstance::getImage(OfxTime time,
}
}
std::shared_ptr<OFX::Host::ImageEffect::Image>
OFX::Host::ImageEffect::Image*
olive::plugin::OliveClipInstance::getOutputImage(OfxTime time)
{
if (images_.contains(time)) {
@@ -469,7 +460,7 @@ olive::plugin::OliveClipInstance::getOutputImage(OfxTime time)
static_cast<int>(std::ceil(rod_d.y2)) };
OfxRectI bounds = rod;
auto image = std::make_shared<Image>(*this, params_, bounds, rod, true);
auto image = new Image(*this, params_, bounds, rod, true);
images_.insert(time, image);
return image;
}
@@ -530,18 +521,18 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
static_cast<int>(std::ceil(rod_d.x2)),
static_cast<int>(std::ceil(rod_d.y2)) };
std::shared_ptr<Image> image;
Image* image;
if (images_.contains(time)) {
image = images_.value(time);
image->EnsureAllocatedFromParams(params_, bounds, regionOfDefinition,
false);
} else {
image = std::make_shared<Image>(*this, params_, bounds,
image = new Image(*this, params_, bounds,
regionOfDefinition, false);
images_.insert(time, image);
}
uint8_t *dst = image->data();
uint8_t *dst = (uint8_t*)image->data();
if (!dst) {
return;
}
@@ -603,6 +594,8 @@ copy_pixels:
std::memcpy(dst + y * dst_row_bytes, src + y * src_row_bytes,
copy_bytes);
}
}
void olive::plugin::OliveClipInstance::setOutputTexture(TexturePtr texture,
+2 -2
View File
@@ -44,7 +44,7 @@ public:
{
params_ = params;
}
std::shared_ptr<OFX::Host::ImageEffect::Image> getOutputImage(OfxTime time);
OFX::Host::ImageEffect::Image* getOutputImage(OfxTime time);
const std::string &getUnmappedBitDepth() const override;
const std::string &getUnmappedComponents() const override;
@@ -83,7 +83,7 @@ private:
OfxRectD defaultRegionOfDefinitions_;
std::string name_;
QMap<OfxTime,std::shared_ptr<Image>> images_;
QMap<OfxTime, Image*> images_;
#ifdef OFX_SUPPORTS_OPENGLRENDER
QMap<OfxTime, TexturePtr> input_textures_;
QMap<OfxTime, TexturePtr> output_textures_;
+13
View File
@@ -160,6 +160,19 @@ const std::string &OlivePluginInstance::getDefaultOutputFielding() const
return FieldOrderForParams(params_);
}
void OlivePluginInstance::setNode(std::shared_ptr<PluginNode> node)
{
node_ = node;
for (const auto &entry : getParams()) {
if (!entry.second) {
continue;
}
if (auto *bound = dynamic_cast<NodeBoundParam *>(entry.second)) {
bound->SetNode(node_);
}
}
}
OfxStatus OlivePluginInstance::vmessage(const char *type, const char *id,
const char *format, va_list args)
{
+3 -2
View File
@@ -74,9 +74,10 @@ public:
{
this->params_=params;
}
void setNode(std::shared_ptr<PluginNode> node)
void setNode(std::shared_ptr<PluginNode> node);
std::shared_ptr<PluginNode> node() const
{
node_ = node;
return node_;
}
void setOpenGLEnabled(bool enabled)
{
+4 -1
View File
@@ -21,10 +21,13 @@
#define OLIVE_EDITOR_PLUGIN_IMAGE_H
#include "ofxCore.h"
#include "ofxImageEffect.h"
#include "ofxhClip.h"
#include "olive/core/render/pixelformat.h"
#include "render/loopmode.h"
#include "render/videoparams.h"
#include <cstdint>
#include <qtypes.h>
#include <string>
#include <vector>
namespace olive
@@ -41,7 +44,7 @@ public:
bool clear = true);
~Image();
uint8_t *data() {
return image_.empty() ? nullptr : image_.data();
return (uint8_t *)getPointerProperty(kOfxImagePropData);
}
int width();
int height();
+84 -13
View File
@@ -42,7 +42,14 @@ inline QString ParamChangeLabel(const OFX::Host::Param::Descriptor &descriptor)
void SubmitUndoCommand(const std::shared_ptr<PluginNode> &node,
UndoCommand *command, const QString &label);
class PushbuttonInstance : public OFX::Host::Param::PushbuttonInstance {
class NodeBoundParam {
public:
virtual ~NodeBoundParam() = default;
virtual void SetNode(const std::shared_ptr<PluginNode> &node) = 0;
};
class PushbuttonInstance : public OFX::Host::Param::PushbuttonInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor *_descriptor;
@@ -54,9 +61,14 @@ public:
{
_descriptor = &descriptor;
};
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
node = new_node;
}
};
class IntegerInstance : public OFX::Host::Param::IntegerInstance {
class IntegerInstance : public OFX::Host::Param::IntegerInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> _node;
OFX::Host::Param::Descriptor& _descriptor;
@@ -69,6 +81,10 @@ public:
, _node(node)
, _descriptor(descriptor)
{}
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
_node = new_node;
}
OfxStatus get(int &a)
{
if (!_node) {
@@ -137,7 +153,8 @@ public:
}
};
class DoubleInstance : public OFX::Host::Param::DoubleInstance {
class DoubleInstance : public OFX::Host::Param::DoubleInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
@@ -151,6 +168,10 @@ public:
{
(void)name;
}
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
node = new_node;
}
OfxStatus get(double& data)
{
if (!node) {
@@ -219,7 +240,8 @@ public:
}
};
class BooleanInstance : public OFX::Host::Param::BooleanInstance {
class BooleanInstance : public OFX::Host::Param::BooleanInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
@@ -233,6 +255,10 @@ public:
{
(void)name;
}
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
node = new_node;
}
OfxStatus get(bool& data)
{
if (!node) {
@@ -293,7 +319,8 @@ public:
}
};
class ChoiceInstance : public OFX::Host::Param::ChoiceInstance {
class ChoiceInstance : public OFX::Host::Param::ChoiceInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
@@ -307,6 +334,10 @@ public:
{
(void)name;
}
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
node = new_node;
}
OfxStatus get(int& data)
{
if (!node) {
@@ -367,7 +398,8 @@ public:
}
};
class RGBAInstance : public OFX::Host::Param::RGBAInstance {
class RGBAInstance : public OFX::Host::Param::RGBAInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
@@ -381,6 +413,10 @@ public:
{
(void)name;
}
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
node = new_node;
}
OfxStatus get(double& r,double& g,double& b,double& a)
{
if (!node) {
@@ -472,7 +508,8 @@ public:
};
class RGBInstance : public OFX::Host::Param::RGBInstance {
class RGBInstance : public OFX::Host::Param::RGBInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
@@ -486,6 +523,10 @@ public:
{
(void)name;
}
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
node = new_node;
}
OfxStatus get(double& r,double& g,double& b)
{
if (!node) {
@@ -568,7 +609,8 @@ public:
}
};
class Double2DInstance : public OFX::Host::Param::Double2DInstance {
class Double2DInstance : public OFX::Host::Param::Double2DInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
@@ -582,6 +624,10 @@ public:
{
(void)name;
}
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
node = new_node;
}
OfxStatus get(double& x,double& y)
{
if (!node) {
@@ -653,7 +699,8 @@ public:
}
};
class Integer2DInstance : public OFX::Host::Param::Integer2DInstance {
class Integer2DInstance : public OFX::Host::Param::Integer2DInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
@@ -667,6 +714,10 @@ public:
{
(void)name;
}
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
node = new_node;
}
OfxStatus get(int& x,int& y)
{
if (!node) {
@@ -738,7 +789,8 @@ public:
}
};
class Double3DInstance : public OFX::Host::Param::Double3DInstance {
class Double3DInstance : public OFX::Host::Param::Double3DInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
@@ -753,6 +805,10 @@ public:
{
(void)name;
}
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
node = new_node;
}
OfxStatus get(double& x,double& y,double& z)
{
if (!node) {
@@ -832,7 +888,8 @@ public:
}
};
class Integer3DInstance : public OFX::Host::Param::Integer3DInstance {
class Integer3DInstance : public OFX::Host::Param::Integer3DInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
@@ -847,6 +904,10 @@ public:
{
(void)name;
}
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
node = new_node;
}
OfxStatus get(int& x,int& y,int& z)
{
if (!node) {
@@ -926,7 +987,8 @@ public:
}
};
class StringInstance : public OFX::Host::Param::StringInstance {
class StringInstance : public OFX::Host::Param::StringInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
@@ -941,6 +1003,10 @@ public:
{
(void)name;
}
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
node = new_node;
}
OfxStatus get(std::string &data)
{
if (!node) {
@@ -1002,7 +1068,8 @@ public:
}
};
class CustomInstance : public OFX::Host::Param::CustomInstance {
class CustomInstance : public OFX::Host::Param::CustomInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
@@ -1017,6 +1084,10 @@ public:
{
(void)name;
}
void SetNode(const std::shared_ptr<PluginNode> &new_node) override
{
node = new_node;
}
OfxStatus get(std::string &data)
{
if (!node) {
+14 -1
View File
@@ -187,6 +187,9 @@ QVariant OpenGLRenderer::CreateNativeTexture(int width, int height, int depth,
const void *data, int linesize)
{
GL_PREAMBLE;
if (!EnsureContextCurrent(__FUNCTION__)) {
return QVariant();
}
bool is_3d = depth > 1;
@@ -237,7 +240,10 @@ void OpenGLRenderer::AttachTextureAsDestination(const QVariant &texture)
void OpenGLRenderer::DetachTextureAsDestination()
{
functions_->glBindFramebuffer(GL_FRAMEBUFFER, 0);
// QOpenGLWidget renders to a non-zero default FBO.
const GLuint default_fbo =
context_ ? context_->defaultFramebufferObject() : 0;
functions_->glBindFramebuffer(GL_FRAMEBUFFER, default_fbo);
}
void OpenGLRenderer::DestroyNativeTexture(QVariant texture)
@@ -423,6 +429,12 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
{
GL_PREAMBLE;
try {
if (!destination) {
// Ensure we're drawing to the default framebuffer for this context.
GLuint fbo = context_ ? context_->defaultFramebufferObject() : 0;
functions_->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
}
ShaderJob &s_job=dynamic_cast<ShaderJob &>(a_job);
ShaderJob job(s_job);
// If this node is iterative, we'll pick up which input here
@@ -694,6 +706,7 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
PRINT_GL_ERRORS;
functions_->glDrawArrays(GL_TRIANGLES, 0, blit_vertices.size() / 3);
}
}
if (destination) {
+2 -2
View File
@@ -73,6 +73,8 @@ public:
return context_;
}
bool EnsureContextCurrent(const char *caller);
protected:
virtual void Blit(QVariant shader, olive::AcceleratedJob& job,
olive::Texture *destination,
@@ -99,8 +101,6 @@ private:
static GLenum GetPixelFormat(int channel_count);
bool EnsureContextCurrent(const char *caller);
void PrepareInputTexture(GLenum target, Texture::Interpolation interp);
void ClearDestinationInternal(double r = 0.0, double g = 0.0,
File diff suppressed because it is too large Load Diff
+10
View File
@@ -38,15 +38,25 @@ namespace olive
{
namespace plugin{
namespace detail {
// 作用:将字节行跨度转换为像素跨度,便于纹理读写。
// Purpose: Convert byte stride to pixel stride for texture I/O.
int BytesToPixels(int byte_linesize, const olive::VideoParams &params);
}
// 作用:OFX 插件渲染器,负责 CPU/GL 路径下的插件调用和纹理桥接。
// Purpose: OFX plugin renderer that drives CPU/GL render paths and texture bridging.
class PluginRenderer : public olive::OpenGLRenderer{
Q_OBJECT
public:
PluginRenderer(QObject *parent=nullptr):OpenGLRenderer(parent){};
virtual ~PluginRenderer() override{};
// 作用:将目标纹理绑定为插件输出。
// Purpose: Attach destination texture as OFX output.
void AttachOutputTexture(olive::TexturePtr texture);
// 作用:解除目标纹理绑定。
// Purpose: Detach destination texture binding.
void DetachOutputTexture();
// 作用:执行插件渲染流程(参数配置、输入/输出、调用渲染动作)。
// Purpose: Execute plugin render flow (params, inputs/outputs, render actions).
void RenderPlugin(TexturePtr src, olive::plugin::PluginJob& job,
olive::TexturePtr destination,
olive::VideoParams destination_params,
+16 -4
View File
@@ -530,8 +530,11 @@ void PreviewAutoCacher::TryRender()
t->property("dry").toBool());
video_immediate_passthroughs_[watcher].append(t);
} else {
qWarning() << "Failed to find copied node for SFR ticket";
t->Finish();
qWarning() << "Failed to find copied node for SFR ticket, requeueing";
single_frame_render_ = t;
if (!delayed_requeue_timer_.isActive()) {
delayed_requeue_timer_.start();
}
}
}
@@ -560,7 +563,11 @@ void PreviewAutoCacher::TryRender()
}
}
} else {
qCritical() << "Failed to find node copy for video job";
qWarning() << "Failed to find node copy for video job, retrying";
if (!delayed_requeue_timer_.isActive()) {
delayed_requeue_timer_.start();
}
break;
}
if (d.iterator.HasNext()) {
@@ -598,7 +605,12 @@ void PreviewAutoCacher::TryRender()
RenderAudio(copy, d.context, use_range, d.cache);
} else {
qCritical() << "Failed to find node copy for audio job";
qWarning() << "Failed to find node copy for audio job, retrying";
pop = false;
if (!delayed_requeue_timer_.isActive()) {
delayed_requeue_timer_.start();
}
break;
}
if (pop) {
+31 -1
View File
@@ -28,9 +28,18 @@ namespace olive
Renderer::Renderer(QObject *parent)
: QObject(parent)
, lifetime_(std::make_shared<RendererLifetime>())
{
}
Renderer::~Renderer()
{
destroyed_ = true;
if (lifetime_) {
lifetime_->alive = false;
}
}
TexturePtr Renderer::CreateTexture(const VideoParams &params, const void *data,
int linesize)
{
@@ -68,6 +77,9 @@ TexturePtr Renderer::CreateTexture(const VideoParams &params, const void *data,
void Renderer::DestroyTexture(Texture *texture)
{
if (destroyed_) {
return;
}
if (USE_TEXTURE_CACHE) {
// HACK: Dirty, dirty hack. OpenGL uses "contexts" to store all of its data, and each context
// can only be used by the thread that created it. However there are also "shared contexts"
@@ -142,6 +154,10 @@ QVariant Renderer::GetDefaultShader()
void Renderer::Destroy()
{
destroyed_ = true;
if (lifetime_) {
lifetime_->alive = false;
}
if (!default_shader_.isNull()) {
DestroyNativeShader(default_shader_);
default_shader_.clear();
@@ -169,7 +185,7 @@ TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v,
return nullptr;
}
return std::make_shared<Texture>(this, v, params);
return std::make_shared<Texture>(this, v, params, lifetime_);
}
bool Renderer::GetColorContext(const ColorTransformJob &color_job,
@@ -323,6 +339,20 @@ void Renderer::BlitColorManaged(const ColorTransformJob &color_job,
{
ColorContext color_ctx;
if (!GetColorContext(color_job, &color_ctx)) {
ShaderJob fallback_job;
fallback_job.Insert(QStringLiteral("ove_maintex"),
color_job.GetInputTexture());
fallback_job.Insert(
QStringLiteral("ove_mvpmat"),
NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix()));
if (destination) {
BlitToTexture(GetDefaultShader(), fallback_job, destination,
color_job.IsClearDestinationEnabled());
} else {
Blit(GetDefaultShader(), fallback_job, params,
color_job.IsClearDestinationEnabled());
}
return;
}
+9
View File
@@ -22,6 +22,8 @@
#include <QMutex>
#include <QObject>
#include <QVariant>
#include <atomic>
#include <memory>
#include "common/define.h"
#include "node/node.h"
@@ -40,6 +42,7 @@ class Renderer : public QObject {
Q_OBJECT
public:
Renderer(QObject *parent = nullptr);
virtual ~Renderer() override;
virtual bool Init() = 0;
@@ -106,6 +109,10 @@ public:
virtual Color GetPixelFromTexture(olive::Texture *texture,
const QPointF &pt) = 0;
std::shared_ptr<RendererLifetime> GetLifetime() const
{
return lifetime_;
}
protected:
virtual void Blit(QVariant shader, olive::AcceleratedJob& job,
@@ -122,6 +129,8 @@ protected:
virtual void DestroyInternal() = 0;
private:
std::atomic<bool> destroyed_{false};
std::shared_ptr<RendererLifetime> lifetime_;
struct ColorContext {
struct LUT {
TexturePtr texture;
+1 -1
View File
@@ -90,7 +90,7 @@ RenderThread *RenderManager::CreateThread(Renderer *renderer)
{
auto t = new RenderThread(renderer, decoder_cache_, shader_cache_, this);
render_threads_.push_back(t);
t->start(QThread::IdlePriority);
t->start(QThread::NormalPriority);
return t;
}
+49 -1
View File
@@ -635,8 +635,56 @@ TexturePtr RenderProcessor::ProcessPluginJob(TexturePtr texture,
plugin_renderer_->PostInit();
}
NodeValueRow &values = plugin_job->GetValues();
auto is_usable_texture = [](const TexturePtr &tex) {
if (!tex) {
return false;
}
if (!tex->IsDummy() && tex->renderer()) {
return true;
}
AVFramePtr frame = tex->frame();
return frame && frame->data[0];
};
TexturePtr src = nullptr;
QString effect_input_id;
if (plugin_job->node()) {
effect_input_id = plugin_job->node()->GetEffectInputID();
}
if (!effect_input_id.isEmpty()) {
if (TexturePtr effect_tex = values.value(effect_input_id).toTexture();
is_usable_texture(effect_tex)) {
src = effect_tex;
}
}
if (!src) {
const QString source_key =
QString::fromUtf8(kOfxImageEffectSimpleSourceClipName);
if (TexturePtr source_tex = values.value(source_key).toTexture();
is_usable_texture(source_tex)) {
src = source_tex;
} else if (TexturePtr effect_tex =
values.value(plugin::kTextureInput).toTexture();
is_usable_texture(effect_tex)) {
src = effect_tex;
}
}
if (!src) {
for (auto it = values.cbegin(); it != values.cend(); ++it) {
if (it.value().type() == NodeValue::kTexture) {
if (TexturePtr any_tex = it.value().toTexture();
is_usable_texture(any_tex)) {
src = any_tex;
break;
}
}
}
}
plugin_renderer_->RenderPlugin(
texture,
src,
*plugin_job,
destination,
destination->params(),
+3 -3
View File
@@ -28,7 +28,7 @@ const Texture::Interpolation Texture::kDefaultInterpolation =
Texture::~Texture()
{
if (renderer_) {
if (IsRendererAlive()) {
renderer_->DestroyTexture(this);
}
@@ -39,14 +39,14 @@ Texture::~Texture()
void Texture::Upload(void *data, int linesize)
{
if (renderer_) {
if (IsRendererAlive()) {
renderer_->UploadToTexture(this->id(), this->params(), data, linesize);
}
}
void Texture::Download(void *data, int linesize)
{
if (renderer_) {
if (IsRendererAlive()) {
renderer_->DownloadFromTexture(this->id(), this->params(), data,
linesize);
}
+15 -1
View File
@@ -21,6 +21,7 @@
#include "common/ffmpegutils.h"
#include <atomic>
#include <memory>
#include <QVariant>
@@ -31,6 +32,9 @@ namespace olive
class AcceleratedJob;
class Renderer;
struct RendererLifetime {
std::atomic<bool> alive{true};
};
class Texture;
using TexturePtr = std::shared_ptr<Texture>;
@@ -46,6 +50,7 @@ public:
*/
Texture(const VideoParams &param)
: renderer_(nullptr)
, renderer_lifetime_(nullptr)
, params_(param)
, job_(nullptr)
{
@@ -62,8 +67,10 @@ public:
* @brief Construct a real texture linked to a renderer backend
*/
Texture(Renderer *renderer, const QVariant &native,
const VideoParams &param)
const VideoParams &param,
std::shared_ptr<RendererLifetime> lifetime = nullptr)
: renderer_(renderer)
, renderer_lifetime_(lifetime)
, params_(param)
, id_(native)
, job_(nullptr)
@@ -158,7 +165,14 @@ public:
return frame_;
}
private:
bool IsRendererAlive() const
{
return renderer_ &&
(!renderer_lifetime_ || renderer_lifetime_->alive.load());
}
Renderer *renderer_;
std::shared_ptr<RendererLifetime> renderer_lifetime_;
VideoParams params_;
+14 -1
View File
@@ -19,10 +19,12 @@
#ifndef VIDEOPARAMS_H
#define VIDEOPARAMS_H
#include "ofxImageEffect.h"
#include <olive/core/core.h>
#include <QVector2D>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <string>
namespace olive
{
@@ -173,7 +175,18 @@ public:
{
channel_count_ = c;
}
void set_channel_count(std::string ofxComponent)
{
if (ofxComponent == kOfxImageComponentAlpha){
channel_count_ = 1;
}
else if (ofxComponent == kOfxImageComponentRGB){
channel_count_ = kRGBChannelCount;
}
else if(ofxComponent == kOfxImageComponentRGBA){
channel_count_ = kRGBAChannelCount;
}
}
const rational &pixel_aspect_ratio() const
{
return pixel_aspect_ratio_;
@@ -74,7 +74,7 @@ void NodeParamViewContext::RemoveNode(Node *node, Node *ctx)
if (item->GetNode() == node && item->GetContext() == ctx) {
emit AboutToDeleteItem(item);
delete item;
dock_area_->RemoveItem(item);
it = items_.erase(it);
} else {
it++;
@@ -89,7 +89,7 @@ void NodeParamViewContext::RemoveNodesWithContext(Node *ctx)
if (item->GetContext() == ctx) {
emit AboutToDeleteItem(item);
delete item;
dock_area_->RemoveItem(item);
it = items_.erase(it);
} else {
it++;
@@ -47,4 +47,14 @@ void NodeParamViewDockArea::AddItem(QDockWidget *item)
addDockWidget(Qt::LeftDockWidgetArea, item);
}
void NodeParamViewDockArea::RemoveItem(QDockWidget *item)
{
if (!item) {
return;
}
removeDockWidget(item);
item->setParent(nullptr);
item->deleteLater();
}
}
@@ -34,6 +34,7 @@ public:
virtual QMenu *createPopupMenu() override;
void AddItem(QDockWidget *item);
void RemoveItem(QDockWidget *item);
};
}
@@ -110,12 +110,22 @@ void NodeParamViewWidgetBridge::CreateWidgets()
CreateSliders<FloatSlider>(GetSliderCount(t), parent);
break;
}
case NodeValue::kCombo: {
case NodeValue::kCombo:
case NodeValue::kStrCombo: {
QComboBox *combobox = new QComboBox(parent);
QStringList items = GetInnerInput().GetComboBoxStrings();
foreach (const QString &s, items) {
combobox->addItem(s);
QStringList values =
GetInnerInput().GetProperty("combo_value_str").toStringList();
const bool use_value_data =
(t == NodeValue::kStrCombo) && !values.isEmpty();
for (int i = 0; i < items.size(); ++i) {
const QString &label = items.at(i);
if (use_value_data && i < values.size()) {
combobox->addItem(label, values.at(i));
} else {
combobox->addItem(label);
}
}
widgets_.append(combobox);
@@ -378,6 +388,16 @@ void NodeParamViewWidgetBridge::WidgetCallback()
SetInputValue(index, 0);
break;
}
case NodeValue::kStrCombo: {
QComboBox *cb = static_cast<QComboBox *>(widgets_.first());
const QVariant data = cb->currentData();
if (data.isValid()) {
SetInputValue(data.toString(), 0);
} else {
SetInputValue(cb->currentText(), 0);
}
break;
}
case NodeValue::kBezier: {
// Widget is a FloatSlider (child of BezierWidget)
BezierWidget *bw = static_cast<BezierWidget *>(widgets_.first());
@@ -561,6 +581,22 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
cb->blockSignals(false);
break;
}
case NodeValue::kStrCombo: {
QComboBox *cb = static_cast<QComboBox *>(widgets_.first());
cb->blockSignals(true);
const QString current =
GetInnerInput().GetValueAtTime(node_time).toString();
for (int i = 0; i < cb->count(); ++i) {
const QVariant data = cb->itemData(i);
if ((data.isValid() && data.toString() == current) ||
(!data.isValid() && cb->itemText(i) == current)) {
cb->setCurrentIndex(i);
break;
}
}
cb->blockSignals(false);
break;
}
case NodeValue::kBezier: {
BezierWidget *bw = static_cast<BezierWidget *>(widgets_.first());
bw->SetValue(GetInnerInput().GetValueAtTime(node_time).value<Bezier>());
@@ -772,7 +808,7 @@ void NodeParamViewWidgetBridge::SetProperty(const QString &key,
}
// ComboBox strings changing
if (data_type == NodeValue::kCombo) {
if (data_type == NodeValue::kCombo || data_type == NodeValue::kStrCombo) {
if (key == QStringLiteral("combo_str")) {
QComboBox *cb = static_cast<QComboBox *>(widgets_.first());
@@ -784,14 +820,23 @@ void NodeParamViewWidgetBridge::SetProperty(const QString &key,
cb->clear();
QStringList items = value.toStringList();
QStringList values =
GetInnerInput().GetProperty("combo_value_str").toStringList();
const bool use_value_data =
(data_type == NodeValue::kStrCombo) && !values.isEmpty();
int index = 0;
foreach (const QString &s, items) {
for (int i = 0; i < items.size(); ++i) {
const QString &s = items.at(i);
if (s.isEmpty()) {
cb->insertSeparator(cb->count());
cb->setItemData(cb->count() - 1, -1);
} else {
cb->addItem(s, index);
index++;
if (use_value_data && i < values.size()) {
cb->addItem(s, values.at(i));
} else {
cb->addItem(s, index);
index++;
}
}
}
+38 -9
View File
@@ -1270,6 +1270,7 @@ RenderTicketWatcher *ViewerWidget::RequestNextFrameForQueue(bool increment)
}
watcher = new RenderTicketWatcher();
watcher->setProperty("start", QDateTime::currentMSecsSinceEpoch());
watcher->setProperty("time", QVariant::fromValue(next_time));
DetectMulticamNode(next_time);
connect(watcher, &RenderTicketWatcher::Finished, this,
@@ -1283,6 +1284,10 @@ RenderTicketWatcher *ViewerWidget::RequestNextFrameForQueue(bool increment)
RenderTicketPtr ViewerWidget::GetFrame(const rational &t)
{
if (IsPlaying() || prequeuing_video_) {
return GetSingleFrame(t);
}
QString cache_fn =
GetConnectedNode()->video_frame_cache()->GetValidCacheFilename(t);
@@ -1447,21 +1452,45 @@ void ViewerWidget::RendererGeneratedFrameForQueue()
if (watcher->HasResult()) {
QVariant frame = watcher->Get();
bool drop_frame = false;
// Ignore this signal if we've paused now
if (IsPlaying() || prequeuing_video_) {
const qint64 start_ms =
watcher->property("start").toLongLong();
const qint64 now_ms =
QDateTime::currentMSecsSinceEpoch();
const int playback_step = qMax(1, qAbs(playback_speed_));
const double frame_interval_ms = qMax(
1.0,
timebase().toDouble() * 1000.0 /
static_cast<double>(playback_step));
if (start_ms > 0 &&
(now_ms - start_ms) > frame_interval_ms) {
drop_frame = true;
}
rational ts = watcher->property("time").value<rational>();
foreach (ViewerDisplayWidget *dw, playback_devices_) {
QVariant push;
if (dynamic_cast<MulticamDisplay *>(dw)) {
push =
watcher->GetTicket()->property("multicam_output");
} else {
push = frame;
}
if (!drop_frame) {
foreach (ViewerDisplayWidget *dw, playback_devices_) {
const bool is_multicam =
dynamic_cast<MulticamDisplay *>(dw);
QVariant push;
if (is_multicam) {
push = watcher->GetTicket()->property(
"multicam_output");
if (!push.isValid() || push.isNull()) {
// Fall back to the primary frame when multicam isn't available.
push = frame;
}
} else {
push = frame;
}
dw->queue()->AppendTimewise({ ts, push }, playback_speed_);
dw->queue()->AppendTimewise({ ts, push },
playback_speed_);
}
}
if (prequeuing_video_) {
+30 -2
View File
@@ -38,6 +38,7 @@
#include "config/config.h"
#include "core.h"
#include "node/block/subtitle/subtitle.h"
#include "codec/frame.h"
#include "node/gizmo/path.h"
#include "node/gizmo/point.h"
#include "node/gizmo/polygon.h"
@@ -401,8 +402,35 @@ void ViewerDisplayWidget::OnPaint()
texture_->Upload(frame->data(), frame->linesize_pixels());
}
} else if (TexturePtr texture = load_frame_.value<TexturePtr>()) {
// This is a GPU texture, switch to it directly
texture_ = texture;
// This is a GPU texture, switch to it directly when possible.
if (texture && texture->renderer() &&
texture->renderer() != renderer()) {
bool copied = false;
QOpenGLContext *ctx = QOpenGLContext::currentContext();
if (ctx) {
QOpenGLFunctions *funcs = ctx->functions();
GLuint tex_id = texture->id().value<GLuint>();
if (funcs && tex_id && funcs->glIsTexture(tex_id)) {
FramePtr frame = Frame::Create();
frame->set_video_params(texture->params());
if (frame->allocate()) {
renderer()->DownloadFromTexture(
texture->id(), texture->params(),
frame->data(), frame->linesize_pixels());
texture_ = renderer()->CreateTexture(
frame->video_params(), frame->data(),
frame->linesize_pixels());
copied = true;
}
}
}
if (!copied) {
texture_ = texture;
}
} else {
texture_ = texture;
}
} else {
texture_ = LoadCustomTextureFromFrame(load_frame_);
}