尝试解决插件bug

This commit is contained in:
2026-01-10 18:56:58 +08:00
parent e37972b227
commit 4d393f3d23
24 changed files with 959 additions and 161 deletions
+14 -1
View File
@@ -21,6 +21,7 @@
#define PLUGINJOB_H
#include "acceleratedjob.h"
#include "pluginSupport/OlivePluginInstance.h"
#include "olive/core/util/rational.h"
#include <any>
#include <chrono>
@@ -31,13 +32,20 @@ namespace plugin {
class PluginJob :public AcceleratedJob{
public:
explicit PluginJob(const OFX::Host::ImageEffect::Instance* pluginInstance,
const PluginNode* node, NodeValueRow row)
const PluginNode* node, NodeValueRow row,
const olive::core::rational &time)
: AcceleratedJob()
, time_seconds_(time.toDouble())
{
this->pluginInstance_ = pluginInstance;
this->node_=node;
Insert(row);
}
explicit PluginJob(const OFX::Host::ImageEffect::Instance* pluginInstance,
const PluginNode* node, NodeValueRow row)
: PluginJob(pluginInstance, node, row, olive::core::rational(0))
{
}
PluginNode *node() const {
return const_cast<PluginNode *>(node_);
@@ -47,6 +55,10 @@ public:
return const_cast<OFX::Host::ImageEffect::Instance*>(pluginInstance_);
}
double time_seconds() const {
return time_seconds_;
}
private:
const OFX::Host::ImageEffect::Instance *pluginInstance_=nullptr;
@@ -55,6 +67,7 @@ private:
QHash<QString, std::any> params;
const PluginNode *node_=nullptr;
double time_seconds_ = 0.0;
};
} // plugin
+56 -2
View File
@@ -151,8 +151,9 @@ void OpenGLRenderer::DestroyInternal()
if (context_) {
GL_PREAMBLE;
// Delete framebuffer
functions_->glDeleteFramebuffers(1, &framebuffer_);
if (functions_ && framebuffer_) {
functions_->glDeleteFramebuffers(1, &framebuffer_);
}
framebuffer_ = 0;
// Delete context if it belongs to us
@@ -160,6 +161,7 @@ void OpenGLRenderer::DestroyInternal()
delete context_;
}
context_ = nullptr;
functions_ = nullptr;
}
}
@@ -335,11 +337,28 @@ void OpenGLRenderer::DownloadFromTexture(const QVariant &id,
{
GL_PREAMBLE;
if (!EnsureContextCurrent(__FUNCTION__)) {
return;
}
GLuint texture_id = id.value<GLuint>();
if (!texture_id || !functions_->glIsTexture(texture_id)) {
qWarning() << "DownloadFromTexture called with invalid texture";
return;
}
GLint current_tex;
functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_tex);
AttachTextureAsDestination(id);
GLenum status = functions_->glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
qWarning() << "DownloadFromTexture framebuffer incomplete" << status;
DetachTextureAsDestination();
return;
}
functions_->glPixelStorei(GL_PACK_ROW_LENGTH, linesize);
{
@@ -921,4 +940,39 @@ GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code)
return shader;
}
bool OpenGLRenderer::EnsureContextCurrent(const char *caller)
{
if (!context_) {
qWarning() << caller << "called without an OpenGL context";
return false;
}
if (QOpenGLContext::currentContext() != context_) {
if (context_->parent() == this && surface_.isValid()) {
if (!context_->makeCurrent(&surface_)) {
qWarning() << caller << "failed to make context current";
return false;
}
} else {
qWarning() << caller << "OpenGL context not current";
return false;
}
}
if (!functions_) {
functions_ = context_->functions();
}
if (!functions_) {
qWarning() << caller << "OpenGL functions not available";
return false;
}
if (!framebuffer_) {
functions_->glGenFramebuffers(1, &framebuffer_);
}
return true;
}
}
+2
View File
@@ -99,6 +99,8 @@ 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,
+348 -18
View File
@@ -20,6 +20,9 @@
//
// Created by mikesolar on 25-10-19.
//
#include "render/texture.h"
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <qtypes.h>
@@ -31,6 +34,7 @@
#include "common/ffmpegutils.h"
#include "ofxImageEffect.h"
#include "ofxhUtilities.h"
#include "ofxGPURender.h"
extern "C"{
#include <libavutil/pixfmt.h>
#include <libavutil/pixdesc.h>
@@ -280,7 +284,7 @@ static const char *GetRenderFieldForParams(const olive::VideoParams &params)
return kOfxImageFieldNone;
}
static olive::AVFramePtr ReadbackTextureToFrame(olive::Texture *texture,
static olive::AVFramePtr ReadbackTextureToFrame(olive::TexturePtr texture,
const olive::VideoParams &params)
{
if (!texture || texture->IsDummy()) {
@@ -307,8 +311,11 @@ static olive::AVFramePtr ReadbackTextureToFrame(olive::Texture *texture,
}
if (texture->renderer()) {
const int linesize_pixels =
olive::plugin::detail::BytesToPixels(frame->linesize[0],
params);
texture->renderer()->DownloadFromTexture(
texture->id(), params, frame->data[0], frame->linesize[0]);
texture->id(), params, frame->data[0], linesize_pixels);
}
return frame;
}
@@ -327,9 +334,11 @@ static olive::AVFramePtr ReadbackTextureToFrame(olive::Texture *texture,
}
if (texture->renderer()) {
const int linesize_pixels =
olive::plugin::detail::BytesToPixels(rgba_frame->linesize[0],
rgba_params);
texture->renderer()->DownloadFromTexture(
texture->id(), rgba_params, rgba_frame->data[0],
rgba_frame->linesize[0]);
texture->id(), rgba_params, rgba_frame->data[0], linesize_pixels);
}
olive::AVFramePtr dst = olive::CreateAVFramePtr();
@@ -356,6 +365,18 @@ static olive::AVFramePtr ReadbackTextureToFrame(olive::Texture *texture,
return dst;
}
int olive::plugin::detail::BytesToPixels(int byte_linesize,
const olive::VideoParams &params)
{
const int bytes_per_pixel =
olive::VideoParams::GetBytesPerPixel(params.format(),
params.channel_count());
if (byte_linesize <= 0 || bytes_per_pixel <= 0) {
return 0;
}
return byte_linesize / bytes_per_pixel;
}
static olive::AVFramePtr ConvertFrameIfNeeded(olive::AVFramePtr src,
const olive::VideoParams &dst_params)
{
@@ -382,6 +403,240 @@ static olive::AVFramePtr ConvertFrameIfNeeded(olive::AVFramePtr src,
return src;
}
auto float_channels = [](AVPixelFormat fmt) -> int {
switch (fmt) {
case AV_PIX_FMT_GRAYF32LE:
case AV_PIX_FMT_GRAYF32BE:
return 1;
case AV_PIX_FMT_RGBF32LE:
case AV_PIX_FMT_RGBF32BE:
return 3;
case AV_PIX_FMT_RGBAF32LE:
case AV_PIX_FMT_RGBAF32BE:
return 4;
default:
return 0;
}
};
auto dst_packed_info = [](AVPixelFormat fmt, int *channels,
int *bytes_per_component) -> bool {
switch (fmt) {
case AV_PIX_FMT_GRAY8:
*channels = 1;
*bytes_per_component = 1;
return true;
case AV_PIX_FMT_RGB24:
*channels = 3;
*bytes_per_component = 1;
return true;
case AV_PIX_FMT_RGBA:
*channels = 4;
*bytes_per_component = 1;
return true;
case AV_PIX_FMT_GRAY16LE:
*channels = 1;
*bytes_per_component = 2;
return true;
case AV_PIX_FMT_RGB48LE:
*channels = 3;
*bytes_per_component = 2;
return true;
case AV_PIX_FMT_RGBA64LE:
*channels = 4;
*bytes_per_component = 2;
return true;
default:
return false;
}
};
auto float_dst_from_packed = [&](const olive::AVFramePtr &packed_src,
AVPixelFormat float_fmt,
const olive::AVFramePtr &float_dst) -> bool {
if (!packed_src || !float_dst || !packed_src->data[0] ||
!float_dst->data[0]) {
return false;
}
const int dst_channels = float_channels(float_fmt);
if (dst_channels == 0) {
return false;
}
int src_channels = 0;
int bytes_per_component = 0;
if (!dst_packed_info(static_cast<AVPixelFormat>(packed_src->format),
&src_channels, &bytes_per_component)) {
return false;
}
const float inv_scale =
(bytes_per_component == 2) ? (1.0f / 65535.0f)
: (1.0f / 255.0f);
for (int y = 0; y < packed_src->height; ++y) {
const uint8_t *src_row =
packed_src->data[0] + y * packed_src->linesize[0];
float *dst_row = reinterpret_cast<float *>(
float_dst->data[0] + y * float_dst->linesize[0]);
if (bytes_per_component == 2) {
const uint16_t *src_u16 =
reinterpret_cast<const uint16_t *>(src_row);
for (int x = 0; x < packed_src->width; ++x) {
const uint16_t *pix = src_u16 + x * src_channels;
float r = pix[0] * inv_scale;
float g = (src_channels > 1) ? pix[1] * inv_scale : r;
float b = (src_channels > 2) ? pix[2] * inv_scale : r;
float a = (src_channels > 3) ? pix[3] * inv_scale : 1.0f;
dst_row[x * dst_channels + 0] = r;
if (dst_channels > 1) {
dst_row[x * dst_channels + 1] = g;
}
if (dst_channels > 2) {
dst_row[x * dst_channels + 2] = b;
}
if (dst_channels > 3) {
dst_row[x * dst_channels + 3] = a;
}
}
} else {
for (int x = 0; x < packed_src->width; ++x) {
const uint8_t *pix = src_row + x * src_channels;
float r = pix[0] * inv_scale;
float g = (src_channels > 1) ? pix[1] * inv_scale : r;
float b = (src_channels > 2) ? pix[2] * inv_scale : r;
float a = (src_channels > 3) ? pix[3] * inv_scale : 1.0f;
dst_row[x * dst_channels + 0] = r;
if (dst_channels > 1) {
dst_row[x * dst_channels + 1] = g;
}
if (dst_channels > 2) {
dst_row[x * dst_channels + 2] = b;
}
if (dst_channels > 3) {
dst_row[x * dst_channels + 3] = a;
}
}
}
}
return true;
};
const int dst_float_channels = float_channels(dst_fmt);
if (dst_float_channels > 0) {
int src_channels = 0;
int bytes_per_component = 0;
if (dst_packed_info(static_cast<AVPixelFormat>(src->format),
&src_channels, &bytes_per_component)) {
if (float_dst_from_packed(src, dst_fmt, dst)) {
return dst;
}
} else {
olive::AVFramePtr packed = olive::CreateAVFramePtr();
AVPixelFormat packed_fmt = (dst_float_channels == 4)
? AV_PIX_FMT_RGBA
: (dst_float_channels == 3)
? AV_PIX_FMT_RGB24
: AV_PIX_FMT_GRAY8;
packed->format = packed_fmt;
packed->width = dst->width;
packed->height = dst->height;
if (av_frame_get_buffer(packed.get(), 0) >= 0) {
SwsContext *pre_ctx = sws_getContext(
src->width, src->height,
static_cast<AVPixelFormat>(src->format),
packed->width, packed->height, packed_fmt, SWS_POINT,
nullptr, nullptr, nullptr);
if (pre_ctx) {
sws_scale(pre_ctx, src->data, src->linesize, 0, src->height,
packed->data, packed->linesize);
sws_freeContext(pre_ctx);
if (float_dst_from_packed(packed, dst_fmt, dst)) {
return dst;
}
}
}
}
}
auto clamp01 = [](float v) -> float {
return std::clamp(v, 0.0f, 1.0f);
};
const int src_float_channels = float_channels(
static_cast<AVPixelFormat>(src->format));
if (src_float_channels > 0) {
int dst_channels = 0;
int bytes_per_component = 0;
if (dst_packed_info(dst_fmt, &dst_channels, &bytes_per_component) &&
src->data[0] && dst->data[0]) {
for (int y = 0; y < src->height; ++y) {
const float *src_row = reinterpret_cast<const float *>(
src->data[0] + y * src->linesize[0]);
uint8_t *dst_row = dst->data[0] + y * dst->linesize[0];
if (bytes_per_component == 2) {
auto *dst_row_u16 =
reinterpret_cast<uint16_t *>(dst_row);
for (int x = 0; x < src->width; ++x) {
const float *pix =
src_row + x * src_float_channels;
float r = pix[0];
float g = (src_float_channels > 1) ? pix[1] : r;
float b = (src_float_channels > 2) ? pix[2] : r;
float a = (src_float_channels > 3) ? pix[3] : 1.0f;
if (dst_channels == 1) {
float luma = 0.2126f * r + 0.7152f * g + 0.0722f * b;
dst_row_u16[x] = static_cast<uint16_t>(
std::lround(clamp01(luma) * 65535.0f));
continue;
}
dst_row_u16[x * dst_channels + 0] =
static_cast<uint16_t>(
std::lround(clamp01(r) * 65535.0f));
dst_row_u16[x * dst_channels + 1] =
static_cast<uint16_t>(
std::lround(clamp01(g) * 65535.0f));
dst_row_u16[x * dst_channels + 2] =
static_cast<uint16_t>(
std::lround(clamp01(b) * 65535.0f));
if (dst_channels == 4) {
dst_row_u16[x * dst_channels + 3] =
static_cast<uint16_t>(
std::lround(clamp01(a) * 65535.0f));
}
}
} else {
for (int x = 0; x < src->width; ++x) {
const float *pix =
src_row + x * src_float_channels;
float r = pix[0];
float g = (src_float_channels > 1) ? pix[1] : r;
float b = (src_float_channels > 2) ? pix[2] : r;
float a = (src_float_channels > 3) ? pix[3] : 1.0f;
if (dst_channels == 1) {
float luma = 0.2126f * r + 0.7152f * g + 0.0722f * b;
dst_row[x] = static_cast<uint8_t>(
std::lround(clamp01(luma) * 255.0f));
continue;
}
dst_row[x * dst_channels + 0] =
static_cast<uint8_t>(
std::lround(clamp01(r) * 255.0f));
dst_row[x * dst_channels + 1] =
static_cast<uint8_t>(
std::lround(clamp01(g) * 255.0f));
dst_row[x * dst_channels + 2] =
static_cast<uint8_t>(
std::lround(clamp01(b) * 255.0f));
if (dst_channels == 4) {
dst_row[x * dst_channels + 3] =
static_cast<uint8_t>(
std::lround(clamp01(a) * 255.0f));
}
}
}
}
return dst;
}
}
SwsContext *sws_ctx = sws_getContext(
src->width, src->height, static_cast<AVPixelFormat>(src->format),
dst->width, dst->height, dst_fmt, SWS_POINT,
@@ -407,6 +662,42 @@ static int LinesizeToPixels(const olive::VideoParams &params, int linesize_bytes
return linesize_bytes / bytes_per_pixel;
}
static olive::TexturePtr ConvertTextureForParams(olive::TexturePtr src,
const olive::VideoParams &dst_params)
{
if (!src) {
return nullptr;
}
const olive::VideoParams &src_params = src->params();
if (src_params.format() == dst_params.format() &&
src_params.channel_count() == dst_params.channel_count() &&
src_params.width() == dst_params.width() &&
src_params.height() == dst_params.height()) {
return src;
}
olive::AVFramePtr frame = src->frame();
if (!frame || !frame->data[0]) {
frame = ReadbackTextureToFrame(src, src_params);
}
if (!frame || !frame->data[0]) {
return src;
}
olive::AVFramePtr converted = ConvertFrameIfNeeded(frame, dst_params);
if (!converted || !converted->data[0]) {
return src;
}
auto dst = std::make_shared<olive::Texture>(dst_params);
int linesize_pixels = LinesizeToPixels(dst_params, converted->linesize[0]);
if (linesize_pixels <= 0) {
linesize_pixels = dst_params.effective_width();
}
dst->Upload(converted->data[0], linesize_pixels);
return dst;
}
static QString PluginIdForInstance(const OFX::Host::ImageEffect::Instance *instance)
{
if (!instance) {
@@ -432,15 +723,15 @@ static void LogOfxFailure(const char *action, OfxStatus stat,
<< "(" << stat << ")";
}
static void MarkRenderFailure(olive::Texture *destination)
static void MarkRenderFailure(olive::TexturePtr destination)
{
if (destination && destination->renderer()) {
destination->renderer()->ClearDestination(destination, 1.0, 0.0, 1.0, 1.0);
destination->renderer()->ClearDestination(destination.get(), 1.0, 0.0, 1.0, 1.0);
}
}
void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::PluginJob& job,
olive::Texture *destination,
olive::TexturePtr destination,
olive::VideoParams destination_params,
bool clear_destination, bool interactive)
{
@@ -448,10 +739,18 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
if (!instance) {
return;
}
bool supports_opengl = false;
#ifdef OFX_SUPPORTS_OPENGLRENDER
const std::string &gl_supported =
instance->getDescriptor().getProps().getStringProperty(
kOfxImageEffectPropOpenGLRenderSupported);
supports_opengl = (gl_supported == "true" || gl_supported == "1");
#endif
auto *olive_instance =
dynamic_cast<olive::plugin::OlivePluginInstance *>(instance);
const bool use_opengl =
destination && destination->renderer() && destination->id().isValid();
supports_opengl && destination && destination->renderer() &&
destination->id().isValid();
if (olive_instance) {
olive_instance->setOpenGLEnabled(use_opengl);
olive_instance->setVideoParam(destination_params);
@@ -501,7 +800,7 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
}
// call get region of interest on each of the inputs
OfxTime frame = 0;
OfxTime frame = job.time_seconds();
const NodeValueRow &values = job.GetValues();
const auto &clips = instance->getDescriptor().getClips();
@@ -523,7 +822,6 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
}
if (input_tex) {
input_textures[entry.first] = input_tex;
input_clip->setInputTexture(input_tex, frame);
}
}
@@ -538,6 +836,8 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
return;
}
olive::VideoParams output_params = destination_params;
if (ApplyClipPreferencesToParams(*clip, &output_params)) {
clip->setParams(output_params);
@@ -551,15 +851,27 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
}
olive::VideoParams input_params = entry.second->params();
if (ApplyClipPreferencesToParams(*input_clip, &input_params)) {
/*
input_params.set_width(entry.second->params().width());
input_params.set_height(entry.second->params().height());
input_params.set_depth(entry.second->params().depth());
input_params.set_pixel_aspect_ratio(
entry.second->params().pixel_aspect_ratio());
input_params.set_interlacing(entry.second->params().interlacing());
input_params.set_premultiplied_alpha(
entry.second->params().premultiplied_alpha());
input_params.set_divider(entry.second->params().divider());*/
input_clip->setParams(input_params);
}
input_clip->setInputTexture(entry.second, frame);
olive::TexturePtr converted =
ConvertTextureForParams(entry.second, input_params);
input_clip->setInputTexture(converted, frame);
}
clip->setRegionOfDefinition(regionOfDefinition, frame);
clip->setOutputTexture(destination, frame);
stat = instance->beginRenderAction(0, numFramesToRender,
stat = instance->beginRenderAction(frame, numFramesToRender,
1.0, false, renderScale, true,
interactive);
if (stat != kOfxStatOK && stat != kOfxStatReplyDefault) {
@@ -589,14 +901,24 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
return;
}
if (!output_params.is_valid()) {
qWarning().noquote()
<< "OFX render skipped due to invalid output params for plugin="
<< PluginIdForInstance(instance);
MarkRenderFailure(destination);
instance->endRenderAction(frame, numFramesToRender, 1.0, interactive,
renderScale, true, interactive);
return;
}
// render a frame
const char *render_field = GetRenderFieldForParams(destination_params);
stat = instance->renderAction(0, render_field, renderWindow, renderScale,
stat = instance->renderAction(frame, render_field, renderWindow, renderScale,
true, interactive, interactive);
if (stat != kOfxStatOK && stat != kOfxStatReplyDefault) {
LogOfxFailure("render", stat, instance);
MarkRenderFailure(destination);
instance->endRenderAction(0, numFramesToRender, 1.0, interactive,
instance->endRenderAction(frame, numFramesToRender, 1.0, interactive,
renderScale, true, interactive);
return;
}
@@ -636,19 +958,27 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
qWarning().noquote()
<< "OFX output image conversion failed for plugin="
<< PluginIdForInstance(instance);
instance->endRenderAction(0, numFramesToRender, 1.0, interactive,
renderScale, true, interactive);
instance->endRenderAction(frame, numFramesToRender, 1.0, interactive,
renderScale, true, interactive);
return;
}
AVFramePtr converted = ConvertFrameIfNeeded(frame_ptr, destination_params);
const AVPixelFormat expected_fmt =
GetDestinationAVPixelFormat(destination_params);
destination->handleFrame(converted);
if (destination->renderer() && converted && converted->data[0]) {
if (destination->renderer() && converted && converted->data[0] &&
(expected_fmt == AV_PIX_FMT_NONE ||
converted->format == expected_fmt)) {
int linesize_pixels =
LinesizeToPixels(destination_params, converted->linesize[0]);
if (linesize_pixels <= 0) {
linesize_pixels = destination_params.effective_width();
}
destination->Upload(converted->data[0], linesize_pixels);
} else if (destination->renderer() && converted && converted->data[0]) {
qWarning().noquote()
<< "OFX output pixel format mismatch for plugin="
<< PluginIdForInstance(instance);
}
} else {
AVFramePtr frame_ptr =
@@ -669,7 +999,7 @@ void olive::plugin::PluginRenderer::RenderPlugin(TexturePtr src, olive::plugin::
}
void olive::plugin::PluginRenderer::AttachOutputTexture(olive::Texture *texture)
void olive::plugin::PluginRenderer::AttachOutputTexture(olive::TexturePtr texture)
{
if (!texture) {
return;
+5 -2
View File
@@ -37,15 +37,18 @@
namespace olive
{
namespace plugin{
namespace detail {
int BytesToPixels(int byte_linesize, const olive::VideoParams &params);
}
class PluginRenderer : public olive::OpenGLRenderer{
Q_OBJECT
public:
PluginRenderer(QObject *parent=nullptr):OpenGLRenderer(parent){};
virtual ~PluginRenderer() override{};
void AttachOutputTexture(olive::Texture *texture);
void AttachOutputTexture(olive::TexturePtr texture);
void DetachOutputTexture();
void RenderPlugin(TexturePtr src, olive::plugin::PluginJob& job,
olive::Texture *destination,
olive::TexturePtr destination,
olive::VideoParams destination_params,
bool clear_destination, bool interactive);
+1 -1
View File
@@ -638,7 +638,7 @@ TexturePtr RenderProcessor::ProcessPluginJob(TexturePtr texture,
plugin_renderer_->RenderPlugin(
texture,
*plugin_job,
destination.get(),
destination,
destination->params(),
true,
false);