尝试修复粉紫屏未果
This commit is contained in:
+145
-10
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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 ¶ms);
|
||||
}
|
||||
// 作用: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,
|
||||
|
||||
@@ -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
@@ -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 ¶ms, const void *data,
|
||||
int linesize)
|
||||
{
|
||||
@@ -68,6 +77,9 @@ TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
@@ -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 ¶m)
|
||||
: 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 ¶m)
|
||||
const VideoParams ¶m,
|
||||
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_;
|
||||
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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_) {
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user