Merge branch 'master' into pr/1987
This commit is contained in:
@@ -78,9 +78,9 @@ private:
|
||||
#define PRINT_GL_ERRORS ErrorPrinter __e(__FUNCTION__, functions_)
|
||||
|
||||
#define GL_PREAMBLE \
|
||||
QMutexLocker __l(&global_opengl_mutex);
|
||||
//QMutexLocker __l(&global_opengl_mutex);
|
||||
|
||||
QMutex global_opengl_mutex;
|
||||
//QMutex global_opengl_mutex;
|
||||
|
||||
OpenGLRenderer::OpenGLRenderer(QObject* parent) :
|
||||
Renderer(parent),
|
||||
@@ -107,7 +107,7 @@ void OpenGLRenderer::Init(QOpenGLContext *existing_ctx)
|
||||
|
||||
bool OpenGLRenderer::Init()
|
||||
{
|
||||
QMutexLocker locker(&global_opengl_mutex);
|
||||
GL_PREAMBLE;
|
||||
|
||||
if (context_) {
|
||||
qCritical() << "Can't initialize already initialized OpenGLRenderer";
|
||||
@@ -177,7 +177,7 @@ void OpenGLRenderer::ClearDestination(Texture *texture, double r, double g, doub
|
||||
GL_PREAMBLE;
|
||||
|
||||
if (texture) {
|
||||
AttachTextureAsDestination(texture);
|
||||
AttachTextureAsDestination(texture->id());
|
||||
}
|
||||
|
||||
ClearDestinationInternal(r, g, b, a);
|
||||
@@ -187,16 +187,11 @@ void OpenGLRenderer::ClearDestination(Texture *texture, double r, double g, doub
|
||||
}
|
||||
}
|
||||
|
||||
QVariant OpenGLRenderer::CreateNativeTexture2D(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize)
|
||||
QVariant OpenGLRenderer::CreateNativeTexture(int width, int height, int depth, VideoParams::Format format, int channel_count, const void *data, int linesize)
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
|
||||
return CreateNativeTexture2DInternal(width, height, format, channel_count, data, linesize);
|
||||
}
|
||||
|
||||
QVariant OpenGLRenderer::CreateNativeTexture3D(int width, int height, int depth, VideoParams::Format format, int channel_count, const void *data, int linesize)
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
bool is_3d = depth > 1;
|
||||
|
||||
// Generate new texture
|
||||
GLuint texture;
|
||||
@@ -205,23 +200,32 @@ QVariant OpenGLRenderer::CreateNativeTexture3D(int width, int height, int depth,
|
||||
|
||||
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
|
||||
|
||||
GLenum target_current = is_3d ? GL_TEXTURE_BINDING_3D : GL_TEXTURE_BINDING_2D;
|
||||
GLenum target = is_3d ? GL_TEXTURE_3D : GL_TEXTURE_2D;
|
||||
|
||||
GLint current_tex;
|
||||
functions_->glGetIntegerv(GL_TEXTURE_BINDING_3D, ¤t_tex);
|
||||
functions_->glGetIntegerv(target_current, ¤t_tex);
|
||||
|
||||
functions_->glBindTexture(GL_TEXTURE_3D, texture);
|
||||
functions_->glBindTexture(target, texture);
|
||||
|
||||
context_->extraFunctions()->glTexImage3D(GL_TEXTURE_3D, 0, GetInternalFormat(format, channel_count),
|
||||
width, height, depth, 0, GetPixelFormat(channel_count),
|
||||
GetPixelType(format), data);
|
||||
if (is_3d) {
|
||||
context_->extraFunctions()->glTexImage3D(target, 0, GetInternalFormat(format, channel_count),
|
||||
width, height, depth, 0, GetPixelFormat(channel_count),
|
||||
GetPixelType(format), data);
|
||||
} else {
|
||||
functions_->glTexImage2D(target, 0, GetInternalFormat(format, channel_count),
|
||||
width, height, 0, GetPixelFormat(channel_count),
|
||||
GetPixelType(format), data);
|
||||
}
|
||||
|
||||
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
functions_->glBindTexture(GL_TEXTURE_3D, current_tex);
|
||||
functions_->glBindTexture(target, current_tex);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
void OpenGLRenderer::AttachTextureAsDestination(Texture* texture)
|
||||
void OpenGLRenderer::AttachTextureAsDestination(const QVariant &texture)
|
||||
{
|
||||
PRINT_GL_ERRORS;
|
||||
|
||||
@@ -229,7 +233,7 @@ void OpenGLRenderer::AttachTextureAsDestination(Texture* texture)
|
||||
functions_->glFramebufferTexture2D(GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D,
|
||||
texture->id().value<GLuint>(),
|
||||
texture.value<GLuint>(),
|
||||
0);
|
||||
}
|
||||
|
||||
@@ -287,15 +291,16 @@ void OpenGLRenderer::DestroyNativeShader(QVariant shader)
|
||||
functions_->glDeleteProgram(program);
|
||||
}
|
||||
|
||||
void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int linesize)
|
||||
void OpenGLRenderer::UploadToTexture(const QVariant &handle, const VideoParams &p, const void *data, int linesize)
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
|
||||
GLuint t = texture->id().value<GLuint>();
|
||||
const VideoParams& p = texture->params();
|
||||
GLuint t = handle.value<GLuint>();
|
||||
|
||||
GLenum tex_type = texture->type() == Texture::k2D ? GL_TEXTURE_2D : GL_TEXTURE_3D;
|
||||
GLenum tex_binding = texture->type() == Texture::k2D ? GL_TEXTURE_BINDING_2D : GL_TEXTURE_BINDING_3D;
|
||||
bool is_3d = p.is_3d();
|
||||
|
||||
GLenum tex_type = !is_3d ? GL_TEXTURE_2D : GL_TEXTURE_3D;
|
||||
GLenum tex_binding = !is_3d ? GL_TEXTURE_BINDING_2D : GL_TEXTURE_BINDING_3D;
|
||||
|
||||
// Store currently bound texture so it can be restored later
|
||||
GLint current_tex;
|
||||
@@ -308,7 +313,7 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin
|
||||
{
|
||||
PRINT_GL_ERRORS;
|
||||
|
||||
if (texture->type() == Texture::k2D) {
|
||||
if (!is_3d) {
|
||||
functions_->glTexSubImage2D(tex_type, 0, 0, 0,
|
||||
p.effective_width(), p.effective_height(),
|
||||
GetPixelFormat(p.channel_count()), GetPixelType(p.format()),
|
||||
@@ -326,16 +331,14 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin
|
||||
functions_->glBindTexture(tex_type, current_tex);
|
||||
}
|
||||
|
||||
void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int linesize)
|
||||
void OpenGLRenderer::DownloadFromTexture(const QVariant &id, const VideoParams &p, void *data, int linesize)
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
|
||||
const VideoParams& p = texture->params();
|
||||
|
||||
GLint current_tex;
|
||||
functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_tex);
|
||||
|
||||
AttachTextureAsDestination(texture);
|
||||
AttachTextureAsDestination(id);
|
||||
|
||||
functions_->glPixelStorei(GL_PACK_ROW_LENGTH, linesize);
|
||||
|
||||
@@ -366,7 +369,7 @@ void OpenGLRenderer::Flush()
|
||||
|
||||
Color OpenGLRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt)
|
||||
{
|
||||
AttachTextureAsDestination(texture);
|
||||
AttachTextureAsDestination(texture->id());
|
||||
|
||||
QByteArray data(VideoParams::GetBytesPerPixel(texture->format(), texture->channel_count()), Qt::Uninitialized);
|
||||
|
||||
@@ -502,7 +505,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
|
||||
|
||||
functions_->glActiveTexture(GL_TEXTURE0 + i);
|
||||
|
||||
GLenum target = (texture && texture->type() == Texture::k3D) ? GL_TEXTURE_3D : GL_TEXTURE_2D;
|
||||
GLenum target = (texture && texture->params().is_3d()) ? GL_TEXTURE_3D : GL_TEXTURE_2D;
|
||||
functions_->glBindTexture(target, tex_id);
|
||||
|
||||
if (tex_id) {
|
||||
@@ -584,11 +587,11 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
|
||||
TexturePtr output_tex, input_tex;
|
||||
if (real_iteration_count > 1) {
|
||||
// Create one texture to bounce off
|
||||
output_tex = CreateTextureFromNativeHandle(CreateNativeTexture2DInternal(destination_params), destination_params);
|
||||
output_tex = CreateTexture(destination_params);
|
||||
|
||||
if (real_iteration_count > 2) {
|
||||
// Create a second texture bounce off
|
||||
input_tex = CreateTextureFromNativeHandle(CreateNativeTexture2DInternal(destination_params), destination_params);
|
||||
input_tex = CreateTexture(destination_params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,7 +607,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
|
||||
// This is the last iteration, draw to the destination
|
||||
if (destination) {
|
||||
// If we have a destination texture, draw to it
|
||||
AttachTextureAsDestination(destination);
|
||||
AttachTextureAsDestination(destination->id());
|
||||
} else if (iteration > 0) {
|
||||
// Otherwise, if we were iterating before, detach texture now
|
||||
DetachTextureAsDestination();
|
||||
@@ -616,7 +619,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
|
||||
}
|
||||
} else {
|
||||
// Always draw to output_tex, which gets swapped with input_tex every iteration
|
||||
AttachTextureAsDestination(output_tex.get());
|
||||
AttachTextureAsDestination(output_tex->id());
|
||||
}
|
||||
|
||||
if (iteration > 0) {
|
||||
@@ -648,7 +651,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
|
||||
// Release any textures we bound before
|
||||
for (int i=textures_to_bind.size()-1; i>=0; i--) {
|
||||
TexturePtr texture = textures_to_bind.at(i).texture;
|
||||
GLenum target = (texture && texture->type() == Texture::k3D) ? GL_TEXTURE_3D : GL_TEXTURE_2D;
|
||||
GLenum target = (texture && texture->params().is_3d()) ? GL_TEXTURE_3D : GL_TEXTURE_2D;
|
||||
functions_->glActiveTexture(GL_TEXTURE0 + i);
|
||||
functions_->glBindTexture(target, 0);
|
||||
}
|
||||
@@ -788,38 +791,6 @@ void OpenGLRenderer::ClearDestinationInternal(double r, double g, double b, doub
|
||||
functions_->glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
QVariant OpenGLRenderer::CreateNativeTexture2DInternal(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize)
|
||||
{
|
||||
GLuint texture;
|
||||
functions_->glGenTextures(1, &texture);
|
||||
texture_params_.insert(texture, {width, height, 1, format, channel_count});
|
||||
|
||||
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
|
||||
|
||||
GLint current_tex;
|
||||
functions_->glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_tex);
|
||||
|
||||
functions_->glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
{
|
||||
PRINT_GL_ERRORS;
|
||||
functions_->glTexImage2D(GL_TEXTURE_2D, 0, GetInternalFormat(format, channel_count),
|
||||
width, height, 0, GetPixelFormat(channel_count),
|
||||
GetPixelType(format), data);
|
||||
}
|
||||
|
||||
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
functions_->glBindTexture(GL_TEXTURE_2D, current_tex);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
QVariant OpenGLRenderer::CreateNativeTexture2DInternal(const VideoParams ¶ms, const void *data, int linesize)
|
||||
{
|
||||
return CreateNativeTexture2DInternal(params.effective_width(), params.effective_height(), params.format(), params.channel_count(), data, linesize);
|
||||
}
|
||||
|
||||
GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code)
|
||||
{
|
||||
static const QString shader_preamble =
|
||||
|
||||
@@ -47,37 +47,35 @@ public:
|
||||
|
||||
virtual void PostDestroy() override;
|
||||
|
||||
public slots:
|
||||
virtual void PostInit() override;
|
||||
|
||||
virtual void DestroyInternal() override;
|
||||
|
||||
virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) override;
|
||||
|
||||
virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override;
|
||||
virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override;
|
||||
|
||||
virtual void DestroyNativeTexture(QVariant texture) override;
|
||||
|
||||
virtual QVariant CreateNativeShader(olive::ShaderCode code) override;
|
||||
|
||||
virtual void DestroyNativeShader(QVariant shader) override;
|
||||
|
||||
virtual void UploadToTexture(olive::Texture* texture, const void* data, int linesize) override;
|
||||
virtual void UploadToTexture(const QVariant &handle, const VideoParams ¶ms, const void* data, int linesize) override;
|
||||
|
||||
virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) override;
|
||||
virtual void DownloadFromTexture(const QVariant &handle, const VideoParams ¶ms, void* data, int linesize) override;
|
||||
|
||||
virtual void Flush() override;
|
||||
|
||||
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) override;
|
||||
|
||||
protected slots:
|
||||
protected:
|
||||
virtual void Blit(QVariant shader,
|
||||
olive::ShaderJob job,
|
||||
olive::Texture* destination,
|
||||
olive::VideoParams destination_params,
|
||||
bool clear_destination) override;
|
||||
|
||||
virtual QVariant CreateNativeTexture(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) override;
|
||||
|
||||
virtual void DestroyNativeTexture(QVariant texture) override;
|
||||
|
||||
virtual void DestroyInternal() override;
|
||||
|
||||
private:
|
||||
static GLint GetInternalFormat(VideoParams::Format format, int channel_layout);
|
||||
|
||||
@@ -85,7 +83,7 @@ private:
|
||||
|
||||
static GLenum GetPixelFormat(int channel_count);
|
||||
|
||||
void AttachTextureAsDestination(olive::Texture* texture);
|
||||
void AttachTextureAsDestination(const QVariant &texture);
|
||||
|
||||
void DetachTextureAsDestination();
|
||||
|
||||
@@ -93,9 +91,6 @@ private:
|
||||
|
||||
void ClearDestinationInternal(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0);
|
||||
|
||||
QVariant CreateNativeTexture2DInternal(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0);
|
||||
QVariant CreateNativeTexture2DInternal(const VideoParams ¶ms, const void* data = nullptr, int linesize = 0);
|
||||
|
||||
GLuint CompileShader(GLenum type, const QString &code);
|
||||
|
||||
QOpenGLContext* context_;
|
||||
|
||||
@@ -61,7 +61,7 @@ PreviewAutoCacher::~PreviewAutoCacher()
|
||||
SetViewerNode(nullptr);
|
||||
}
|
||||
|
||||
RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t)
|
||||
RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t, bool dry)
|
||||
{
|
||||
// If we have a single frame render queued (but not yet sent to the RenderManager), cancel it now
|
||||
CancelQueuedSingleFrameRender();
|
||||
@@ -70,6 +70,7 @@ RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t)
|
||||
auto sfr = std::make_shared<RenderTicket>();
|
||||
sfr->Start();
|
||||
sfr->setProperty("time", QVariant::fromValue(t));
|
||||
sfr->setProperty("dry", dry);
|
||||
|
||||
// Queue it and try to render
|
||||
single_frame_render_ = sfr;
|
||||
@@ -566,7 +567,8 @@ void PreviewAutoCacher::TryRender()
|
||||
if (single_frame_render_) {
|
||||
// Check if already caching this
|
||||
RenderTicketWatcher *watcher = RenderFrame(single_frame_render_->property("time").value<rational>(),
|
||||
nullptr);
|
||||
nullptr,
|
||||
single_frame_render_->property("dry").toBool());
|
||||
video_immediate_passthroughs_[watcher].append(single_frame_render_);
|
||||
|
||||
single_frame_render_ = nullptr;
|
||||
@@ -583,7 +585,7 @@ void PreviewAutoCacher::TryRender()
|
||||
// We want this hash, if we're not already rendering, start render now
|
||||
if (!render_task) {
|
||||
// Don't render any hash more than once
|
||||
RenderFrame(t, viewer_node_->video_frame_cache());
|
||||
RenderFrame(t, viewer_node_->video_frame_cache(), false);
|
||||
}
|
||||
|
||||
emit SignalCacheProxyTaskProgress(double(queued_frame_iterator_.frame_index()) / double(queued_frame_iterator_.size()));
|
||||
@@ -609,7 +611,7 @@ void PreviewAutoCacher::TryRender()
|
||||
}
|
||||
}
|
||||
|
||||
RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& time, FrameHashCache *cache)
|
||||
RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational& time, FrameHashCache *cache, bool dry)
|
||||
{
|
||||
RenderTicketWatcher* watcher = new RenderTicketWatcher();
|
||||
watcher->setProperty("job", QVariant::fromValue(last_update_time_));
|
||||
@@ -623,7 +625,8 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational&
|
||||
time,
|
||||
RenderMode::kOffline,
|
||||
cache,
|
||||
RenderManager::kTexture));
|
||||
dry ? RenderManager::kNull : RenderManager::kTexture));
|
||||
|
||||
return watcher;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
|
||||
virtual ~PreviewAutoCacher() override;
|
||||
|
||||
RenderTicketPtr GetSingleFrame(const rational& t);
|
||||
RenderTicketPtr GetSingleFrame(const rational& t, bool dry = false);
|
||||
|
||||
RenderTicketPtr GetRangeOfAudio(TimeRange range);
|
||||
|
||||
@@ -98,10 +98,10 @@ signals:
|
||||
private:
|
||||
void TryRender();
|
||||
|
||||
RenderTicketWatcher *RenderFrame(Node *node, const rational &time, FrameHashCache *cache);
|
||||
RenderTicketWatcher *RenderFrame(const rational &time, FrameHashCache *cache)
|
||||
RenderTicketWatcher *RenderFrame(Node *node, const rational &time, FrameHashCache *cache, bool dry);
|
||||
RenderTicketWatcher *RenderFrame(const rational &time, FrameHashCache *cache, bool dry)
|
||||
{
|
||||
return RenderFrame(copied_viewer_node_->GetConnectedTextureOutput(), time, cache);
|
||||
return RenderFrame(copied_viewer_node_->GetConnectedTextureOutput(), time, cache, dry);
|
||||
}
|
||||
|
||||
RenderTicketPtr RenderAudio(Node *node, const TimeRange &range, bool generate_waveforms);
|
||||
|
||||
+66
-26
@@ -20,36 +20,62 @@
|
||||
|
||||
#include "renderer.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
#include <QVector2D>
|
||||
|
||||
#include "common/ocioutils.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
Renderer::Renderer(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, Texture::Type type, const void *data, int linesize)
|
||||
{
|
||||
QVariant v;
|
||||
|
||||
if (type == Texture::k3D) {
|
||||
v = CreateNativeTexture3D(params.effective_width(), params.effective_height(),
|
||||
params.effective_depth(), params.format(), params.channel_count(), data, linesize);
|
||||
} else {
|
||||
v = CreateNativeTexture2D(params.effective_width(), params.effective_height(), params.format(),
|
||||
params.channel_count(), data, linesize);
|
||||
}
|
||||
|
||||
return CreateTextureFromNativeHandle(v, params, type);
|
||||
}
|
||||
|
||||
TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, const void *data, int linesize)
|
||||
{
|
||||
return CreateTexture(params, Texture::k2D, data, linesize);
|
||||
QVariant v;
|
||||
|
||||
if (USE_TEXTURE_CACHE) {
|
||||
for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); it++) {
|
||||
if (it->width == params.effective_width()
|
||||
&& it->height == params.effective_height()
|
||||
&& it->depth == params.effective_depth()
|
||||
&& it->format == params.format()
|
||||
&& it->channel_count == params.channel_count()) {
|
||||
v = it->handle;
|
||||
texture_cache_.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (v.isNull()) {
|
||||
v = CreateNativeTexture(params.effective_width(), params.effective_height(), params.effective_depth(),
|
||||
params.format(), params.channel_count(), data, linesize);
|
||||
} else if (data) {
|
||||
UploadToTexture(v, params, data, linesize);
|
||||
} else {
|
||||
this->Flush();
|
||||
}
|
||||
|
||||
return CreateTextureFromNativeHandle(v, params);
|
||||
}
|
||||
|
||||
void Renderer::DestroyTexture(Texture *texture)
|
||||
{
|
||||
if (USE_TEXTURE_CACHE) {
|
||||
texture_cache_.push_back({texture->params().effective_width(),
|
||||
texture->params().effective_height(),
|
||||
texture->params().effective_depth(),
|
||||
texture->params().format(),
|
||||
texture->params().channel_count(),
|
||||
texture->id(),
|
||||
QDateTime::currentMSecsSinceEpoch()});
|
||||
|
||||
ClearOldTextures();
|
||||
} else {
|
||||
DestroyNativeTexture(texture->id());
|
||||
}
|
||||
}
|
||||
|
||||
TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms)
|
||||
@@ -95,16 +121,21 @@ void Renderer::Destroy()
|
||||
interlace_texture_.clear();
|
||||
}
|
||||
|
||||
for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); it++) {
|
||||
DestroyNativeTexture(it->handle);
|
||||
}
|
||||
texture_cache_.clear();
|
||||
|
||||
DestroyInternal();
|
||||
}
|
||||
|
||||
TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms, Texture::Type type)
|
||||
TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms)
|
||||
{
|
||||
if (v.isNull()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_shared<Texture>(this, v, params, type);
|
||||
return std::make_shared<Texture>(this, v, params);
|
||||
}
|
||||
|
||||
bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::ColorContext *ctx)
|
||||
@@ -175,8 +206,7 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::Col
|
||||
}
|
||||
|
||||
// Allocate 3D LUT
|
||||
color_ctx.lut3d_textures[i].texture = CreateTexture(VideoParams(edge_len, edge_len, edge_len, VideoParams::kFormatFloat32, VideoParams::kRGBChannelCount),
|
||||
Texture::k3D, values);
|
||||
color_ctx.lut3d_textures[i].texture = CreateTexture(VideoParams(edge_len, edge_len, edge_len, VideoParams::kFormatFloat32, VideoParams::kRGBChannelCount), values);
|
||||
color_ctx.lut3d_textures[i].name = sampler_name;
|
||||
color_ctx.lut3d_textures[i].interpolation = (interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest : Texture::kLinear;
|
||||
}
|
||||
@@ -206,9 +236,7 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::Col
|
||||
}
|
||||
|
||||
// Allocate 1D LUT
|
||||
color_ctx.lut1d_textures[i].texture = CreateTexture(VideoParams(width, height, VideoParams::kFormatFloat32, (channel == OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ? 1 : VideoParams::kRGBChannelCount),
|
||||
Texture::k2D,
|
||||
values);
|
||||
color_ctx.lut1d_textures[i].texture = CreateTexture(VideoParams(width, height, VideoParams::kFormatFloat32, (channel == OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ? 1 : VideoParams::kRGBChannelCount), values);
|
||||
color_ctx.lut1d_textures[i].name = sampler_name;
|
||||
color_ctx.lut1d_textures[i].interpolation = (interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest : Texture::kLinear;
|
||||
}
|
||||
@@ -219,6 +247,18 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::Col
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::ClearOldTextures()
|
||||
{
|
||||
for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); ) {
|
||||
if (it->accessed < QDateTime::currentMSecsSinceEpoch() - MAX_TEXTURE_LIFE) {
|
||||
DestroyNativeTexture(it->handle);
|
||||
it = texture_cache_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::BlitColorManaged(const ColorTransformJob &color_job, Texture *destination, const VideoParams ¶ms)
|
||||
{
|
||||
ColorContext color_ctx;
|
||||
|
||||
+29
-14
@@ -45,9 +45,10 @@ public:
|
||||
|
||||
virtual bool Init() = 0;
|
||||
|
||||
TexturePtr CreateTexture(const VideoParams& params, Texture::Type type, const void* data = nullptr, int linesize = 0);
|
||||
TexturePtr CreateTexture(const VideoParams& params, const void *data = nullptr, int linesize = 0);
|
||||
|
||||
void DestroyTexture(Texture *texture);
|
||||
|
||||
void BlitToTexture(QVariant shader,
|
||||
olive::ShaderJob job,
|
||||
olive::Texture* destination,
|
||||
@@ -82,39 +83,34 @@ public:
|
||||
|
||||
virtual void PostDestroy() = 0;
|
||||
|
||||
public slots:
|
||||
virtual void PostInit() = 0;
|
||||
|
||||
virtual void DestroyInternal() = 0;
|
||||
|
||||
virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) = 0;
|
||||
|
||||
virtual QVariant CreateNativeTexture2D(int width, int height, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0;
|
||||
virtual QVariant CreateNativeTexture3D(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0;
|
||||
|
||||
virtual void DestroyNativeTexture(QVariant texture) = 0;
|
||||
|
||||
virtual QVariant CreateNativeShader(olive::ShaderCode code) = 0;
|
||||
|
||||
virtual void DestroyNativeShader(QVariant shader) = 0;
|
||||
|
||||
virtual void UploadToTexture(olive::Texture* texture, const void* data, int linesize) = 0;
|
||||
virtual void UploadToTexture(const QVariant &handle, const VideoParams ¶ms, const void* data, int linesize) = 0;
|
||||
|
||||
virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) = 0;
|
||||
virtual void DownloadFromTexture(const QVariant &handle, const VideoParams ¶ms, void* data, int linesize) = 0;
|
||||
|
||||
virtual void Flush() = 0;
|
||||
|
||||
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) = 0;
|
||||
|
||||
protected slots:
|
||||
protected:
|
||||
virtual void Blit(QVariant shader,
|
||||
olive::ShaderJob job,
|
||||
olive::Texture* destination,
|
||||
olive::VideoParams destination_params,
|
||||
bool clear_destination) = 0;
|
||||
|
||||
protected:
|
||||
TexturePtr CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms, Texture::Type type = Texture::k2D);
|
||||
virtual QVariant CreateNativeTexture(int width, int height, int depth, olive::VideoParams::Format format, int channel_count, const void* data = nullptr, int linesize = 0) = 0;
|
||||
|
||||
virtual void DestroyNativeTexture(QVariant texture) = 0;
|
||||
|
||||
virtual void DestroyInternal() = 0;
|
||||
|
||||
private:
|
||||
struct ColorContext {
|
||||
@@ -130,10 +126,29 @@ private:
|
||||
|
||||
};
|
||||
|
||||
TexturePtr CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms);
|
||||
|
||||
bool GetColorContext(const ColorTransformJob &color_job, ColorContext* ctx);
|
||||
|
||||
void ClearOldTextures();
|
||||
|
||||
QHash<QString, ColorContext> color_cache_;
|
||||
|
||||
struct CachedTexture
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int depth;
|
||||
VideoParams::Format format;
|
||||
int channel_count;
|
||||
QVariant handle;
|
||||
qint64 accessed;
|
||||
};
|
||||
|
||||
static const int MAX_TEXTURE_LIFE = 5000;
|
||||
static const bool USE_TEXTURE_CACHE = true;
|
||||
std::list<CachedTexture> texture_cache_;
|
||||
|
||||
QMutex color_cache_mutex_;
|
||||
|
||||
QVariant default_shader_;
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
namespace olive {
|
||||
|
||||
RenderManager* RenderManager::instance_ = nullptr;
|
||||
const rational RenderManager::kDryRunInterval = rational(10);
|
||||
|
||||
RenderManager::RenderManager(QObject *parent) :
|
||||
backend_(kOpenGL),
|
||||
@@ -52,9 +53,11 @@ RenderManager::RenderManager(QObject *parent) :
|
||||
|
||||
if (context_) {
|
||||
video_thread_ = new RenderThread(context_, decoder_cache_, shader_cache_, this);
|
||||
dry_run_thread_ = new RenderThread(nullptr, decoder_cache_, shader_cache_, this);
|
||||
audio_thread_ = new RenderThread(nullptr, decoder_cache_, shader_cache_, this);
|
||||
|
||||
video_thread_->start(QThread::IdlePriority);
|
||||
dry_run_thread_->start(QThread::IdlePriority);
|
||||
audio_thread_->start(QThread::IdlePriority);
|
||||
}
|
||||
|
||||
@@ -73,6 +76,9 @@ RenderManager::~RenderManager()
|
||||
video_thread_->quit();
|
||||
video_thread_->wait();
|
||||
|
||||
dry_run_thread_->quit();
|
||||
dry_run_thread_->wait();
|
||||
|
||||
context_->PostDestroy();
|
||||
delete context_;
|
||||
|
||||
@@ -129,7 +135,11 @@ RenderTicketPtr RenderManager::RenderFrame(Node *node, ColorManager* color_manag
|
||||
ticket->setProperty("cacheuuid", QVariant::fromValue(cache->GetUuid()));
|
||||
}
|
||||
|
||||
video_thread_->AddTicket(ticket);
|
||||
if (return_type == ReturnType::kNull) {
|
||||
dry_run_thread_->AddTicket(ticket);
|
||||
} else {
|
||||
video_thread_->AddTicket(ticket);
|
||||
}
|
||||
|
||||
return ticket;
|
||||
}
|
||||
@@ -157,6 +167,8 @@ bool RenderManager::RemoveTicket(RenderTicketPtr ticket)
|
||||
return true;
|
||||
} else if (audio_thread_->RemoveTicket(ticket)) {
|
||||
return true;
|
||||
} else if (dry_run_thread_->RemoveTicket(ticket)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -97,9 +97,12 @@ public:
|
||||
|
||||
enum ReturnType {
|
||||
kTexture,
|
||||
kFrame
|
||||
kFrame,
|
||||
kNull
|
||||
};
|
||||
|
||||
static const rational kDryRunInterval;
|
||||
|
||||
/**
|
||||
* @brief Asynchronously generate a frame at a given time
|
||||
*
|
||||
@@ -168,6 +171,7 @@ private:
|
||||
QTimer *decoder_clear_timer_;
|
||||
|
||||
RenderThread *video_thread_;
|
||||
RenderThread *dry_run_thread_;
|
||||
RenderThread *audio_thread_;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -123,7 +123,7 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time
|
||||
|
||||
render_ctx_->Flush();
|
||||
|
||||
render_ctx_->DownloadFromTexture(texture.get(), frame->data(), frame->linesize_pixels());
|
||||
render_ctx_->DownloadFromTexture(texture->id(), texture->params(), frame->data(), frame->linesize_pixels());
|
||||
}
|
||||
|
||||
return frame;
|
||||
@@ -151,53 +151,57 @@ void RenderProcessor::Run()
|
||||
|
||||
TexturePtr texture = GenerateTexture(time, frame_length);
|
||||
|
||||
if (GetCacheVideoParams().interlacing() != VideoParams::kInterlaceNone) {
|
||||
// Get next between frame and interlace it
|
||||
TexturePtr top = texture;
|
||||
TexturePtr bottom = GenerateTexture(time + frame_length, frame_length);
|
||||
|
||||
if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedBottomFirst) {
|
||||
std::swap(top, bottom);
|
||||
}
|
||||
|
||||
texture = render_ctx_->InterlaceTexture(top, bottom, GetCacheVideoParams());
|
||||
}
|
||||
|
||||
if (HeardCancel()) {
|
||||
// Finish cancelled ticket with nothing since we can't guarantee the frame we generated
|
||||
// is actually "complete
|
||||
if (!render_ctx_) {
|
||||
ticket_->Finish();
|
||||
} else {
|
||||
RenderManager::ReturnType return_type = RenderManager::ReturnType(ticket_->property("return").toInt());
|
||||
if (GetCacheVideoParams().interlacing() != VideoParams::kInterlaceNone) {
|
||||
// Get next between frame and interlace it
|
||||
TexturePtr top = texture;
|
||||
TexturePtr bottom = GenerateTexture(time + frame_length, frame_length);
|
||||
|
||||
FramePtr frame;
|
||||
QString cache = ticket_->property("cache").toString();
|
||||
|
||||
if (return_type == RenderManager::kFrame || !cache.isEmpty()) {
|
||||
// Convert to CPU frame
|
||||
frame = GenerateFrame(texture, time);
|
||||
|
||||
// Save to cache if requested
|
||||
if (!cache.isEmpty()) {
|
||||
rational timebase = ticket_->property("cachetimebase").value<rational>();
|
||||
QUuid uuid = ticket_->property("cacheuuid").value<QUuid>();
|
||||
bool cache_result = FrameHashCache::SaveCacheFrame(cache, uuid, time, timebase, frame);
|
||||
ticket_->setProperty("cached", cache_result);
|
||||
if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedBottomFirst) {
|
||||
std::swap(top, bottom);
|
||||
}
|
||||
|
||||
texture = render_ctx_->InterlaceTexture(top, bottom, GetCacheVideoParams());
|
||||
}
|
||||
|
||||
if (return_type == RenderManager::kTexture) {
|
||||
// Return GPU texture
|
||||
if (!texture) {
|
||||
texture = render_ctx_->CreateTexture(GetCacheVideoParams());
|
||||
render_ctx_->ClearDestination(texture.get());
|
||||
if (HeardCancel()) {
|
||||
// Finish cancelled ticket with nothing since we can't guarantee the frame we generated
|
||||
// is actually "complete
|
||||
ticket_->Finish();
|
||||
} else {
|
||||
RenderManager::ReturnType return_type = RenderManager::ReturnType(ticket_->property("return").toInt());
|
||||
|
||||
FramePtr frame;
|
||||
QString cache = ticket_->property("cache").toString();
|
||||
|
||||
if (return_type == RenderManager::kFrame || !cache.isEmpty()) {
|
||||
// Convert to CPU frame
|
||||
frame = GenerateFrame(texture, time);
|
||||
|
||||
// Save to cache if requested
|
||||
if (!cache.isEmpty()) {
|
||||
rational timebase = ticket_->property("cachetimebase").value<rational>();
|
||||
QUuid uuid = ticket_->property("cacheuuid").value<QUuid>();
|
||||
bool cache_result = FrameHashCache::SaveCacheFrame(cache, uuid, time, timebase, frame);
|
||||
ticket_->setProperty("cached", cache_result);
|
||||
}
|
||||
}
|
||||
|
||||
render_ctx_->Flush();
|
||||
if (return_type == RenderManager::kTexture) {
|
||||
// Return GPU texture
|
||||
if (!texture) {
|
||||
texture = render_ctx_->CreateTexture(GetCacheVideoParams());
|
||||
render_ctx_->ClearDestination(texture.get());
|
||||
}
|
||||
|
||||
ticket_->Finish(QVariant::fromValue(texture));
|
||||
} else {
|
||||
ticket_->Finish(QVariant::fromValue(frame));
|
||||
render_ctx_->Flush();
|
||||
|
||||
ticket_->Finish(QVariant::fromValue(texture));
|
||||
} else {
|
||||
ticket_->Finish(QVariant::fromValue(frame));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -265,6 +269,11 @@ DecoderPtr RenderProcessor::ResolveDecoderFromInput(const QString& decoder_id, c
|
||||
<< "::" << stream.stream();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!render_ctx_) {
|
||||
// Assume dry run and increment access time
|
||||
decoder.decoder->IncrementAccessTime(RenderManager::kDryRunInterval.toDouble() * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
return decoder.decoder;
|
||||
@@ -404,10 +413,6 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
||||
|
||||
void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time)
|
||||
{
|
||||
if (!render_ctx_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ticket_->property("type").value<RenderManager::TicketType>() != RenderManager::kTypeVideo) {
|
||||
// Video cannot contribute to audio, so we do nothing here
|
||||
return;
|
||||
@@ -440,21 +445,23 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
break;
|
||||
case VideoParams::kVideoTypeImageSequence:
|
||||
{
|
||||
// Since image sequences involve multiple files, we don't engage the decoder cache
|
||||
decoder = Decoder::CreateFromID(decoder_id);
|
||||
if (render_ctx_) {
|
||||
// Since image sequences involve multiple files, we don't engage the decoder cache
|
||||
decoder = Decoder::CreateFromID(decoder_id);
|
||||
|
||||
QString frame_filename;
|
||||
QString frame_filename;
|
||||
|
||||
int64_t frame_number = stream_data.get_time_in_timebase_units(input_time);
|
||||
frame_filename = Decoder::TransformImageSequenceFileName(stream.filename(), frame_number);
|
||||
int64_t frame_number = stream_data.get_time_in_timebase_units(input_time);
|
||||
frame_filename = Decoder::TransformImageSequenceFileName(stream.filename(), frame_number);
|
||||
|
||||
// Decoder will close automatically since it's a stream_ptr
|
||||
decoder->Open(Decoder::CodecStream(frame_filename, stream_data.stream_index(), GetCurrentBlock()));
|
||||
// Decoder will close automatically since it's a stream_ptr
|
||||
decoder->Open(Decoder::CodecStream(frame_filename, stream_data.stream_index(), GetCurrentBlock()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (decoder) {
|
||||
if (decoder && render_ctx_) {
|
||||
Decoder::RetrieveVideoParams p;
|
||||
p.divider = stream.video_params().divider();
|
||||
p.maximum_format = destination->format();
|
||||
|
||||
@@ -29,21 +29,21 @@ const Texture::Interpolation Texture::kDefaultInterpolation = Texture::kMipmappe
|
||||
Texture::~Texture()
|
||||
{
|
||||
if (renderer_) {
|
||||
renderer_->DestroyNativeTexture(id_);
|
||||
renderer_->DestroyTexture(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::Upload(void *data, int linesize)
|
||||
{
|
||||
if (renderer_) {
|
||||
renderer_->UploadToTexture(this, data, linesize);
|
||||
renderer_->UploadToTexture(this->id(), this->params(), data, linesize);
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::Download(void *data, int linesize)
|
||||
{
|
||||
if (renderer_) {
|
||||
renderer_->DownloadFromTexture(this, data, linesize);
|
||||
renderer_->DownloadFromTexture(this->id(), this->params(), data, linesize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-17
@@ -32,11 +32,6 @@ class Renderer;
|
||||
class Texture
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
k2D,
|
||||
k3D
|
||||
};
|
||||
|
||||
enum Interpolation {
|
||||
kNearest,
|
||||
kLinear,
|
||||
@@ -50,19 +45,17 @@ public:
|
||||
*/
|
||||
Texture(const VideoParams& param) :
|
||||
renderer_(nullptr),
|
||||
params_(param),
|
||||
type_(k2D)
|
||||
params_(param)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a real texture linked to a renderer backend
|
||||
*/
|
||||
Texture(Renderer* renderer, const QVariant& native, const VideoParams& param, Type type) :
|
||||
Texture(Renderer* renderer, const QVariant& native, const VideoParams& param) :
|
||||
renderer_(renderer),
|
||||
params_(param),
|
||||
id_(native),
|
||||
type_(type)
|
||||
id_(native)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -117,11 +110,6 @@ public:
|
||||
return params_.pixel_aspect_ratio();
|
||||
}
|
||||
|
||||
Type type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
Renderer* renderer() const
|
||||
{
|
||||
return renderer_;
|
||||
@@ -134,8 +122,6 @@ private:
|
||||
|
||||
QVariant id_;
|
||||
|
||||
Type type_;
|
||||
|
||||
};
|
||||
|
||||
using TexturePtr = std::shared_ptr<Texture>;
|
||||
|
||||
@@ -121,6 +121,8 @@ public:
|
||||
calculate_effective_size();
|
||||
}
|
||||
|
||||
bool is_3d() const { return depth_ > 1; }
|
||||
|
||||
const rational& time_base() const
|
||||
{
|
||||
return time_base_;
|
||||
|
||||
Reference in New Issue
Block a user