opengl: use shared contexts and downgrade to ES 2.0 (BIG CHANGE)
If you run into new OpenGL issues, this is likely the commit that caused it. I've tested this on as many systems as I have access to (including AMD, Apple, Intel, and NVIDIA GPUs with a mixture of Windows, Linux, and macOS) and it appears to work fine, however we all know how unpredictable GPUs can be. This should help increase GPU performance and compatibility. Squashed commit of the following: commit fa3c0ac7c469aebe2941a803736bdc06cdd5a2d8 Author: itsmattkc <itsmattkc@gmail.com> Date: Thu May 27 13:17:44 2021 +1000 viewer: updated pixel sampling for shared contexts commit 9334261da235f09cb67b193c5bb6169abd7a95f7 Author: itsmattkc <itsmattkc@gmail.com> Date: Thu May 27 13:12:39 2021 +1000 pixelsampler: show color preview box commit dacd9be6971c5c0312ae45c8db349d47910a80dd Author: itsmattkc <itsmattkc@gmail.com> Date: Thu May 27 12:43:32 2021 +1000 renderer: interlace on GPU commit 074cea06cb87e9fdc4e8ea6b09587be6df296057 Author: itsmattkc <itsmattkc@gmail.com> Date: Thu May 27 12:43:14 2021 +1000 renderer: added texture clear option commit 5d4f86b4cf1b1dcd3fb8e8c854ffa7f6fb8ca346 Author: itsmattkc <itsmattkc@gmail.com> Date: Thu May 27 12:42:12 2021 +1000 renderer: updated scopes for shared textures commit 6fad96358d189874cf45f7887b7e13cc2b1603c4 Author: itsmattkc <itsmattkc@gmail.com> Date: Thu May 27 11:42:05 2021 +1000 opengl: fixed edge case commit 1a19ffb53703ac6f30ed210f6d8eb5fc4b7b0f2d Author: itsmattkc <itsmattkc@gmail.com> Date: Thu May 27 10:56:13 2021 +1000 opengl: fix deinterlace shader commit 085961aa39cca90ebf51b919a78ef07a4882f0e1 Author: itsmattkc <itsmattkc@gmail.com> Date: Thu May 27 10:08:40 2021 +1000 opengl: downgrade to ES 2.0 With half-float/float/3D extensions, this should be doable commit ea1ab45cbcf5e017dfd0d8f28b231ff3255dfe92 Author: itsmattkc <itsmattkc@gmail.com> Date: Thu May 27 10:06:59 2021 +1000 renderer: fixed mutex deadlock commit ecc2f08b16f03421870c5b61625ecd26f2f96582 Author: itsmattkc <itsmattkc@gmail.com> Date: Wed May 26 21:28:13 2021 +1000 opengl: fixed shared contexts commit 261c689ea8089463155cf11b9ab15aa3f879f439 Merge: bfd4338a91c88b1cAuthor: itsmattkc <itsmattkc@gmail.com> Date: Wed May 26 19:52:00 2021 +1000 Merge branch 'master' into shared-contexts commit bfd4338aaa3b09b6fbb7717c637face8d5490721 Author: itsmattkc <itsmattkc@gmail.com> Date: Wed May 26 18:52:38 2021 +1000 viewer: remove debug line commit 45868d8a396181bf81c24b92a1ac5ce9ffed3426 Merge: 642f8e9dd0bd5e21Author: itsmattkc <itsmattkc@gmail.com> Date: Wed May 26 17:21:48 2021 +1000 Merge branch 'master' into shared-contexts commit 642f8e9df7d7d1a9ed4202d428f4d968ff668552 Merge: 861e5b894de0a727Author: itsmattkc <itsmattkc@gmail.com> Date: Wed May 26 17:01:38 2021 +1000 Merge branch 'master' of https://github.com/olive-editor/olive into shared-contexts commit 861e5b89a38c281897dc44b20e3587d39c4f808c Merge: 472598a7ef31c3fbAuthor: itsmattkc <itsmattkc@gmail.com> Date: Wed May 26 14:28:21 2021 +1000 Merge branch 'master' into shared-contexts commit 472598a740a095aa43a4aad5680dd5abcf453a4c Author: itsmattkc <itsmattkc@gmail.com> Date: Fri May 14 16:29:35 2021 +1000 attempt at sharing contexts
This commit is contained in:
@@ -76,6 +76,11 @@ private:
|
||||
|
||||
#define PRINT_GL_ERRORS ErrorPrinter __e(__FUNCTION__, functions_)
|
||||
|
||||
#define GL_PREAMBLE \
|
||||
QMutexLocker __l(&global_opengl_mutex);
|
||||
|
||||
QMutex global_opengl_mutex;
|
||||
|
||||
OpenGLRenderer::OpenGLRenderer(QObject* parent) :
|
||||
Renderer(parent),
|
||||
cache_timer_(this),
|
||||
@@ -104,6 +109,8 @@ void OpenGLRenderer::Init(QOpenGLContext *existing_ctx)
|
||||
|
||||
bool OpenGLRenderer::Init()
|
||||
{
|
||||
QMutexLocker locker(&global_opengl_mutex);
|
||||
|
||||
if (context_) {
|
||||
qCritical() << "Can't initialize already initialized OpenGLRenderer";
|
||||
return false;
|
||||
@@ -112,6 +119,7 @@ bool OpenGLRenderer::Init()
|
||||
surface_.create();
|
||||
|
||||
context_ = new QOpenGLContext(this);
|
||||
context_->setShareContext(QOpenGLContext::globalShareContext());
|
||||
if (!context_->create()) {
|
||||
qCritical() << "Failed to create OpenGL context";
|
||||
return false;
|
||||
@@ -132,6 +140,8 @@ void OpenGLRenderer::PostDestroy()
|
||||
|
||||
void OpenGLRenderer::PostInit()
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
|
||||
// Make context current on that surface
|
||||
if (context_->parent() == this && !context_->makeCurrent(&surface_)) {
|
||||
qCritical() << "Failed to makeCurrent() on offscreen surface in thread" << thread();
|
||||
@@ -152,6 +162,8 @@ void OpenGLRenderer::PostInit()
|
||||
void OpenGLRenderer::DestroyInternal()
|
||||
{
|
||||
if (context_) {
|
||||
GL_PREAMBLE;
|
||||
|
||||
// Delete framebuffer
|
||||
functions_->glDeleteFramebuffers(1, &framebuffer_);
|
||||
framebuffer_ = 0;
|
||||
@@ -171,55 +183,32 @@ void OpenGLRenderer::DestroyInternal()
|
||||
cache_timer_.stop();
|
||||
}
|
||||
|
||||
void OpenGLRenderer::ClearDestination(double r, double g, double b, double a)
|
||||
void OpenGLRenderer::ClearDestination(Texture *texture, double r, double g, double b, double a)
|
||||
{
|
||||
functions_->glClearColor(r, g, b, a);
|
||||
functions_->glClear(GL_COLOR_BUFFER_BIT);
|
||||
GL_PREAMBLE;
|
||||
|
||||
if (texture) {
|
||||
AttachTextureAsDestination(texture);
|
||||
}
|
||||
|
||||
ClearDestinationInternal(r, g, b, a);
|
||||
|
||||
if (texture) {
|
||||
DetachTextureAsDestination();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant OpenGLRenderer::CreateNativeTexture2D(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize)
|
||||
{
|
||||
GLuint texture = GetCachedTexture(width, height, 1, format, channel_count);
|
||||
GL_PREAMBLE;
|
||||
|
||||
// If no texture in cache, generate new texture
|
||||
bool new_tex = (texture == 0);
|
||||
if (new_tex) {
|
||||
functions_->glGenTextures(1, &texture);
|
||||
texture_params_.insert(texture, {width, height, 1, format, channel_count});
|
||||
}
|
||||
|
||||
if (new_tex || data) {
|
||||
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;
|
||||
if (new_tex) {
|
||||
functions_->glTexImage2D(GL_TEXTURE_2D, 0, GetInternalFormat(format, channel_count),
|
||||
width, height, 0, GetPixelFormat(channel_count),
|
||||
GetPixelType(format), data);
|
||||
} else {
|
||||
functions_->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
|
||||
width, height,
|
||||
GetPixelFormat(channel_count), GetPixelType(format),
|
||||
data);
|
||||
}
|
||||
}
|
||||
|
||||
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
functions_->glBindTexture(GL_TEXTURE_2D, current_tex);
|
||||
}
|
||||
|
||||
return texture;
|
||||
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;
|
||||
|
||||
GLuint texture = GetCachedTexture(width, height, depth, format, channel_count);
|
||||
|
||||
// If no texture in cache, generate new texture
|
||||
@@ -287,6 +276,8 @@ void OpenGLRenderer::DestroyNativeTexture(QVariant texture)
|
||||
|
||||
QVariant OpenGLRenderer::CreateNativeShader(ShaderCode code)
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
|
||||
PRINT_GL_ERRORS;
|
||||
|
||||
QOpenGLShaderProgram* program = new QOpenGLShaderProgram(context_);
|
||||
@@ -294,17 +285,8 @@ QVariant OpenGLRenderer::CreateNativeShader(ShaderCode code)
|
||||
QString vert_code = code.vert_code();
|
||||
QString frag_code = code.frag_code();
|
||||
|
||||
QString shader_preamble;
|
||||
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
|
||||
shader_preamble = QStringLiteral("#version 300 es\n"
|
||||
"\n"
|
||||
"precision highp int;\n"
|
||||
"precision highp float;\n"
|
||||
"\n");
|
||||
} else {
|
||||
shader_preamble = QStringLiteral("#version 150\n"
|
||||
"\n");
|
||||
}
|
||||
QString shader_preamble = QStringLiteral("#version 110\n"
|
||||
"\n");
|
||||
|
||||
vert_code.prepend(shader_preamble);
|
||||
frag_code.prepend(shader_preamble);
|
||||
@@ -333,12 +315,14 @@ error:
|
||||
|
||||
void OpenGLRenderer::DestroyNativeShader(QVariant shader)
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
|
||||
delete Node::ValueToPtr<QOpenGLShaderProgram>(shader);
|
||||
}
|
||||
|
||||
void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int linesize)
|
||||
{
|
||||
PRINT_GL_ERRORS;
|
||||
GL_PREAMBLE;
|
||||
|
||||
GLuint t = texture->id().value<GLuint>();
|
||||
const VideoParams& p = texture->params();
|
||||
@@ -354,16 +338,20 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin
|
||||
|
||||
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize);
|
||||
|
||||
if (texture->type() == Texture::k2D) {
|
||||
functions_->glTexSubImage2D(tex_type, 0, 0, 0,
|
||||
p.effective_width(), p.effective_height(),
|
||||
GetPixelFormat(p.channel_count()), GetPixelType(p.format()),
|
||||
data);
|
||||
} else {
|
||||
context_->extraFunctions()->glTexSubImage3D(tex_type, 0, 0, 0, 0,
|
||||
p.effective_width(), p.effective_height(), p.effective_depth(),
|
||||
GetPixelFormat(p.channel_count()), GetPixelType(p.format()),
|
||||
data);
|
||||
{
|
||||
PRINT_GL_ERRORS;
|
||||
|
||||
if (texture->type() == Texture::k2D) {
|
||||
functions_->glTexSubImage2D(tex_type, 0, 0, 0,
|
||||
p.effective_width(), p.effective_height(),
|
||||
GetPixelFormat(p.channel_count()), GetPixelType(p.format()),
|
||||
data);
|
||||
} else {
|
||||
context_->extraFunctions()->glTexSubImage3D(tex_type, 0, 0, 0, 0,
|
||||
p.effective_width(), p.effective_height(), p.effective_depth(),
|
||||
GetPixelFormat(p.channel_count()), GetPixelType(p.format()),
|
||||
data);
|
||||
}
|
||||
}
|
||||
|
||||
functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
@@ -373,6 +361,8 @@ void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int lin
|
||||
|
||||
void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int linesize)
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
|
||||
const VideoParams& p = texture->params();
|
||||
|
||||
GLint current_tex;
|
||||
@@ -388,7 +378,7 @@ void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int lines
|
||||
0,
|
||||
p.effective_width(),
|
||||
p.effective_height(),
|
||||
(QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) ? GL_RGBA : GetPixelFormat(p.channel_count()),
|
||||
GetPixelFormat(p.channel_count()),
|
||||
GetPixelType(p.format()),
|
||||
data);
|
||||
}
|
||||
@@ -400,6 +390,33 @@ void OpenGLRenderer::DownloadFromTexture(Texture* texture, void *data, int lines
|
||||
functions_->glBindTexture(GL_TEXTURE_2D, current_tex);
|
||||
}
|
||||
|
||||
void OpenGLRenderer::Flush()
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
|
||||
functions_->glFinish();
|
||||
}
|
||||
|
||||
Color OpenGLRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt)
|
||||
{
|
||||
AttachTextureAsDestination(texture);
|
||||
|
||||
QByteArray data(VideoParams::GetBytesPerPixel(texture->format(), texture->channel_count()), Qt::Uninitialized);
|
||||
|
||||
functions_->glReadPixels(pt.x(), pt.y(), 1, 1, GetPixelFormat(texture->channel_count()), GetPixelType(texture->format()), data.data());
|
||||
|
||||
Color c = Color::fromData(data.data(), texture->format(), texture->channel_count());
|
||||
|
||||
if (texture->channel_count() == VideoParams::kRGBChannelCount) {
|
||||
// No alpha channel, set to 1.0
|
||||
c.set_alpha(1.0);
|
||||
}
|
||||
|
||||
DetachTextureAsDestination();
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
struct TextureToBind {
|
||||
TexturePtr texture;
|
||||
Texture::Interpolation interpolation;
|
||||
@@ -407,6 +424,8 @@ struct TextureToBind {
|
||||
|
||||
void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, VideoParams destination_params, bool clear_destination)
|
||||
{
|
||||
GL_PREAMBLE;
|
||||
|
||||
// If this node is iterative, we'll pick up which input here
|
||||
QString iterative_name;
|
||||
GLuint iterative_input = 0;
|
||||
@@ -581,11 +600,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 = CreateTexture(destination_params);
|
||||
output_tex = CreateTextureFromNativeHandle(CreateNativeTexture2DInternal(destination_params), destination_params);
|
||||
|
||||
if (real_iteration_count > 2) {
|
||||
// Create a second texture bounce off
|
||||
input_tex = CreateTexture(destination_params);
|
||||
input_tex = CreateTextureFromNativeHandle(CreateNativeTexture2DInternal(destination_params), destination_params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -606,7 +625,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
|
||||
|
||||
// Clear the destination if the caller requested it
|
||||
if (clear_destination) {
|
||||
ClearDestination();
|
||||
ClearDestinationInternal();
|
||||
}
|
||||
} else {
|
||||
// Always draw to output_tex, which gets swapped with input_tex every iteration
|
||||
@@ -771,6 +790,58 @@ void OpenGLRenderer::PrepareInputTexture(GLenum target, Texture::Interpolation i
|
||||
functions_->glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
void OpenGLRenderer::ClearDestinationInternal(double r, double g, double b, double a)
|
||||
{
|
||||
functions_->glClearColor(r, g, b, a);
|
||||
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 = GetCachedTexture(width, height, 1, format, channel_count);
|
||||
|
||||
// If no texture in cache, generate new texture
|
||||
bool new_tex = (texture == 0);
|
||||
if (new_tex) {
|
||||
functions_->glGenTextures(1, &texture);
|
||||
texture_params_.insert(texture, {width, height, 1, format, channel_count});
|
||||
}
|
||||
|
||||
if (new_tex || data) {
|
||||
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;
|
||||
if (new_tex) {
|
||||
functions_->glTexImage2D(GL_TEXTURE_2D, 0, GetInternalFormat(format, channel_count),
|
||||
width, height, 0, GetPixelFormat(channel_count),
|
||||
GetPixelType(format), data);
|
||||
} else {
|
||||
functions_->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
|
||||
width, height,
|
||||
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::GetCachedTexture(int width, int height, int depth, VideoParams::Format format, int channel_count)
|
||||
{
|
||||
TextureCacheKey input_key = {width, height, depth, format, channel_count};
|
||||
@@ -795,6 +866,7 @@ void OpenGLRenderer::GarbageCollectTextureCache()
|
||||
qint64 max_age = QDateTime::currentMSecsSinceEpoch() - kTextureCacheMaxSize;
|
||||
for (auto it=texture_cache_.begin(); it!=texture_cache_.end(); ) {
|
||||
if (it->age < max_age) {
|
||||
GL_PREAMBLE;
|
||||
GLuint t = it->texture;
|
||||
texture_params_.remove(t);
|
||||
functions_->glDeleteTextures(1, &t);
|
||||
|
||||
@@ -52,7 +52,7 @@ public slots:
|
||||
|
||||
virtual void DestroyInternal() override;
|
||||
|
||||
virtual void ClearDestination(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) 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;
|
||||
@@ -67,6 +67,10 @@ public slots:
|
||||
|
||||
virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) override;
|
||||
|
||||
virtual void Flush() override;
|
||||
|
||||
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) override;
|
||||
|
||||
protected slots:
|
||||
virtual void Blit(QVariant shader,
|
||||
olive::ShaderJob job,
|
||||
@@ -87,6 +91,11 @@ private:
|
||||
|
||||
void PrepareInputTexture(GLenum target, Texture::Interpolation interp);
|
||||
|
||||
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 GetCachedTexture(int width, int height, int depth, VideoParams::Format format, int channel_count);
|
||||
|
||||
QTimer cache_timer_;
|
||||
|
||||
@@ -530,7 +530,8 @@ void PreviewAutoCacher::TryRender()
|
||||
} else {
|
||||
watcher = RenderFrame(hash,
|
||||
single_frame_render_->property("time").value<rational>(),
|
||||
single_frame_render_->property("prioritize").toBool());
|
||||
single_frame_render_->property("prioritize").toBool(),
|
||||
paused_);
|
||||
|
||||
video_immediate_passthroughs_[watcher].append(single_frame_render_);
|
||||
}
|
||||
@@ -539,7 +540,7 @@ void PreviewAutoCacher::TryRender()
|
||||
}
|
||||
}
|
||||
|
||||
RenderTicketWatcher* PreviewAutoCacher::RenderFrame(const QByteArray &hash, const rational& time, bool prioritize)
|
||||
RenderTicketWatcher* PreviewAutoCacher::RenderFrame(const QByteArray &hash, const rational& time, bool prioritize, bool texture_only)
|
||||
{
|
||||
RenderTicketWatcher* watcher = new RenderTicketWatcher();
|
||||
watcher->setProperty("hash", hash);
|
||||
@@ -550,7 +551,8 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(const QByteArray &hash, cons
|
||||
time,
|
||||
RenderMode::kOffline,
|
||||
viewer_node_->video_frame_cache(),
|
||||
prioritize));
|
||||
prioritize,
|
||||
texture_only));
|
||||
return watcher;
|
||||
}
|
||||
|
||||
@@ -585,7 +587,7 @@ void PreviewAutoCacher::RequeueFrames()
|
||||
// We want this hash, if we're not already rendering, start render now
|
||||
if (!render_task && !video_download_tasks_.key(hash)) {
|
||||
// Don't render any hash more than once
|
||||
RenderFrame(hash, t, false);
|
||||
RenderFrame(hash, t, false, false);
|
||||
}
|
||||
} else if (render_task) {
|
||||
// Cancel this frame unless it's already started
|
||||
|
||||
@@ -84,7 +84,7 @@ private:
|
||||
|
||||
void TryRender();
|
||||
|
||||
RenderTicketWatcher *RenderFrame(const QByteArray& hash, const rational &time, bool prioritize);
|
||||
RenderTicketWatcher *RenderFrame(const QByteArray& hash, const rational &time, bool prioritize, bool texture_only);
|
||||
|
||||
/**
|
||||
* @brief Process all changes to internal NodeGraph copy
|
||||
|
||||
+44
-18
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "renderer.h"
|
||||
|
||||
#include <QVector2D>
|
||||
|
||||
#include "common/ocioutils.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -42,11 +44,7 @@ TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, Texture::Type type
|
||||
params.channel_count(), data, linesize);
|
||||
}
|
||||
|
||||
if (v.isNull()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_shared<Texture>(this, v, params, type);
|
||||
return CreateTextureFromNativeHandle(v, params, type);
|
||||
}
|
||||
|
||||
TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, const void *data, int linesize)
|
||||
@@ -64,13 +62,47 @@ void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr so
|
||||
BlitColorManagedInternal(color_processor, source, source_is_premultiplied, nullptr, params, clear_destination, matrix, crop_matrix);
|
||||
}
|
||||
|
||||
TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms)
|
||||
{
|
||||
color_cache_mutex_.lock();
|
||||
if (interlace_texture_.isNull()) {
|
||||
interlace_texture_ = CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/interlace.frag"))));
|
||||
}
|
||||
color_cache_mutex_.unlock();
|
||||
|
||||
ShaderJob job;
|
||||
job.InsertValue(QStringLiteral("top_tex_in"), NodeValue(NodeValue::kTexture, QVariant::fromValue(top)));
|
||||
job.InsertValue(QStringLiteral("bottom_tex_in"), NodeValue(NodeValue::kTexture, QVariant::fromValue(bottom)));
|
||||
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(params.effective_width(), params.effective_height())));
|
||||
|
||||
TexturePtr output = CreateTexture(params);
|
||||
|
||||
BlitToTexture(interlace_texture_, job, output.get());
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void Renderer::Destroy()
|
||||
{
|
||||
color_cache_.clear();
|
||||
|
||||
if (!interlace_texture_.isNull()) {
|
||||
DestroyNativeShader(interlace_texture_);
|
||||
interlace_texture_.clear();
|
||||
}
|
||||
|
||||
DestroyInternal();
|
||||
}
|
||||
|
||||
TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms, Texture::Type type)
|
||||
{
|
||||
if (v.isNull()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_shared<Texture>(this, v, params, type);
|
||||
}
|
||||
|
||||
bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::ColorContext *ctx)
|
||||
{
|
||||
QMutexLocker locker(&color_cache_mutex_);
|
||||
@@ -103,15 +135,9 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo
|
||||
"#define ALPHA_UNASSOC 1\n"
|
||||
"#define ALPHA_ASSOC 2\n"
|
||||
"\n"
|
||||
"// Macros so OCIO's shaders work on this GLSL version\n"
|
||||
"#define texture2D texture\n"
|
||||
"#define texture3D texture\n"
|
||||
"\n"
|
||||
"// Main texture coordinate\n"
|
||||
"in vec2 ove_texcoord;\n"
|
||||
"\n"
|
||||
"// Texture output\n"
|
||||
"out vec4 fragColor;\n"));
|
||||
"varying vec2 ove_texcoord;\n"
|
||||
"\n"));
|
||||
shader_frag.append(shader_desc->getShaderText());
|
||||
shader_frag.append(QStringLiteral("\n"
|
||||
"// Alpha association functions\n"
|
||||
@@ -128,13 +154,13 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo
|
||||
"}\n"
|
||||
"\n"
|
||||
"void main() {\n"
|
||||
" vec2 cropped_coord = (vec4(ove_texcoord-vec2(0.5, 0.5), 0.0, 1.0)*inverse(ove_cropmatrix)).xy + vec2(0.5, 0.5);\n"
|
||||
" vec2 cropped_coord = (vec4(ove_texcoord-vec2(0.5, 0.5), 0.0, 1.0)*ove_cropmatrix).xy + vec2(0.5, 0.5);\n"
|
||||
" if (cropped_coord.x < 0.0 || cropped_coord.x >= 1.0 || cropped_coord.y < 0.0 || cropped_coord.y >= 1.0) {\n"
|
||||
" fragColor = vec4(0.0);\n"
|
||||
" gl_FragColor = vec4(0.0);\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
" \n"
|
||||
" vec4 col = texture(ove_maintex, cropped_coord);\n"
|
||||
" vec4 col = texture2D(ove_maintex, cropped_coord);\n"
|
||||
"\n"
|
||||
" // If alpha is associated, de-associate now\n"
|
||||
" if (ove_maintex_alpha == ALPHA_ASSOC) {\n"
|
||||
@@ -151,7 +177,7 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo
|
||||
" col = assoc(col);\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" fragColor = col;\n"
|
||||
" gl_FragColor = col;\n"
|
||||
"}\n").arg(ocio_func_name));
|
||||
|
||||
// Try to compile shader
|
||||
@@ -244,7 +270,7 @@ void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, Textu
|
||||
|
||||
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(source)));
|
||||
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix));
|
||||
job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix));
|
||||
job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix.inverted()));
|
||||
|
||||
AlphaAssociated associated;
|
||||
if (source->channel_count() == VideoParams::kRGBAChannelCount) {
|
||||
|
||||
+12
-1
@@ -66,6 +66,8 @@ public:
|
||||
void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, bool source_is_premultiplied, Texture* destination, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4());
|
||||
void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, bool source_is_premultiplied, VideoParams params, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4());
|
||||
|
||||
TexturePtr InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms);
|
||||
|
||||
void Destroy();
|
||||
|
||||
virtual void PostDestroy() = 0;
|
||||
@@ -75,7 +77,7 @@ public slots:
|
||||
|
||||
virtual void DestroyInternal() = 0;
|
||||
|
||||
virtual void ClearDestination(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) = 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;
|
||||
@@ -90,6 +92,10 @@ public slots:
|
||||
|
||||
virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) = 0;
|
||||
|
||||
virtual void Flush() = 0;
|
||||
|
||||
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) = 0;
|
||||
|
||||
protected slots:
|
||||
virtual void Blit(QVariant shader,
|
||||
olive::ShaderJob job,
|
||||
@@ -97,6 +103,9 @@ protected slots:
|
||||
olive::VideoParams destination_params,
|
||||
bool clear_destination) = 0;
|
||||
|
||||
protected:
|
||||
TexturePtr CreateTextureFromNativeHandle(const QVariant &v, const VideoParams ¶ms, Texture::Type type = Texture::k2D);
|
||||
|
||||
private:
|
||||
struct ColorContext {
|
||||
struct LUT {
|
||||
@@ -128,6 +137,8 @@ private:
|
||||
|
||||
QMutex color_cache_mutex_;
|
||||
|
||||
QVariant interlace_texture_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -69,9 +69,10 @@ void RendererThreadWrapper::DestroyInternal()
|
||||
}
|
||||
}
|
||||
|
||||
void RendererThreadWrapper::ClearDestination(double r, double g, double b, double a)
|
||||
void RendererThreadWrapper::ClearDestination(Texture *texture, double r, double g, double b, double a)
|
||||
{
|
||||
QMetaObject::invokeMethod(inner_, "ClearDestination", Qt::BlockingQueuedConnection,
|
||||
OLIVE_NS_ARG(Texture*, texture),
|
||||
Q_ARG(double, r),
|
||||
Q_ARG(double, g),
|
||||
Q_ARG(double, b),
|
||||
@@ -150,6 +151,23 @@ void RendererThreadWrapper::DownloadFromTexture(Texture *texture, void *data, in
|
||||
Q_ARG(int, linesize));
|
||||
}
|
||||
|
||||
void RendererThreadWrapper::Flush()
|
||||
{
|
||||
QMetaObject::invokeMethod(inner_, "Flush", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
|
||||
Color RendererThreadWrapper::GetPixelFromTexture(Texture *texture, const QPointF &pt)
|
||||
{
|
||||
Color c;
|
||||
|
||||
QMetaObject::invokeMethod(inner_, "GetPixelFromTexture", Qt::BlockingQueuedConnection,
|
||||
OLIVE_NS_RETURN_ARG(Color, c),
|
||||
OLIVE_NS_ARG(Texture*, texture),
|
||||
Q_ARG(QPointF, pt));
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void RendererThreadWrapper::Blit(QVariant shader, ShaderJob job, Texture *destination, VideoParams destination_params, bool clear_destination)
|
||||
{
|
||||
QMetaObject::invokeMethod(inner_, "Blit", Qt::BlockingQueuedConnection,
|
||||
|
||||
@@ -48,7 +48,7 @@ public slots:
|
||||
|
||||
virtual void DestroyInternal() override;
|
||||
|
||||
virtual void ClearDestination(double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) 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;
|
||||
@@ -63,6 +63,10 @@ public slots:
|
||||
|
||||
virtual void DownloadFromTexture(olive::Texture* texture, void* data, int linesize) override;
|
||||
|
||||
virtual void Flush() override;
|
||||
|
||||
virtual Color GetPixelFromTexture(olive::Texture *texture, const QPointF &pt) override;
|
||||
|
||||
protected slots:
|
||||
virtual void Blit(QVariant shader,
|
||||
olive::ShaderJob job,
|
||||
|
||||
@@ -126,7 +126,7 @@ QByteArray RenderManager::Hash(const Node *n, const QString& output, const Video
|
||||
|
||||
RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* color_manager,
|
||||
const rational& time, RenderMode::Mode mode,
|
||||
FrameHashCache* cache, bool prioritize)
|
||||
FrameHashCache* cache, bool prioritize, bool texture_only)
|
||||
{
|
||||
return RenderFrame(viewer,
|
||||
color_manager,
|
||||
@@ -139,7 +139,8 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* c
|
||||
VideoParams::kFormatInvalid,
|
||||
nullptr,
|
||||
cache,
|
||||
prioritize);
|
||||
prioritize,
|
||||
texture_only);
|
||||
}
|
||||
|
||||
RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* color_manager,
|
||||
@@ -148,7 +149,7 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* c
|
||||
const QSize& force_size,
|
||||
const QMatrix4x4& force_matrix, VideoParams::Format force_format,
|
||||
ColorProcessorPtr force_color_output,
|
||||
FrameHashCache* cache, bool prioritize)
|
||||
FrameHashCache* cache, bool prioritize, bool texture_only)
|
||||
{
|
||||
// Create ticket
|
||||
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
|
||||
@@ -164,6 +165,7 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* c
|
||||
ticket->setProperty("coloroutput", QVariant::fromValue(force_color_output));
|
||||
ticket->setProperty("vparam", QVariant::fromValue(video_params));
|
||||
ticket->setProperty("aparam", QVariant::fromValue(audio_params));
|
||||
ticket->setProperty("textureonly", texture_only);
|
||||
|
||||
if (cache) {
|
||||
ticket->setProperty("cache", cache->GetCacheDirectory());
|
||||
|
||||
@@ -86,14 +86,14 @@ public:
|
||||
*/
|
||||
RenderTicketPtr RenderFrame(ViewerOutput *viewer, ColorManager* color_manager,
|
||||
const rational& time, RenderMode::Mode mode,
|
||||
FrameHashCache* cache = nullptr, bool prioritize = false);
|
||||
FrameHashCache* cache = nullptr, bool prioritize = false, bool texture_only = false);
|
||||
RenderTicketPtr RenderFrame(ViewerOutput* viewer, ColorManager* color_manager,
|
||||
const rational& time, RenderMode::Mode mode,
|
||||
const VideoParams& video_params, const AudioParams& audio_params,
|
||||
const QSize& force_size,
|
||||
const QMatrix4x4& force_matrix, VideoParams::Format force_format,
|
||||
ColorProcessorPtr force_color_output,
|
||||
FrameHashCache* cache = nullptr, bool prioritize = false);
|
||||
FrameHashCache* cache = nullptr, bool prioritize = false, bool texture_only = false);
|
||||
|
||||
/**
|
||||
* @brief Asynchronously generate a chunk of audio
|
||||
|
||||
@@ -40,7 +40,7 @@ RenderProcessor::RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx, S
|
||||
{
|
||||
}
|
||||
|
||||
FramePtr RenderProcessor::GenerateFrame(const rational& time, const rational& frame_length)
|
||||
TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational &frame_length)
|
||||
{
|
||||
ViewerOutput* viewer = Node::ValueToPtr<ViewerOutput>(ticket_->property("viewer"));
|
||||
|
||||
@@ -51,8 +51,11 @@ FramePtr RenderProcessor::GenerateFrame(const rational& time, const rational& fr
|
||||
TimeRange(time, time + frame_length));
|
||||
}
|
||||
|
||||
TexturePtr texture = table.Get(NodeValue::kTexture).value<TexturePtr>();
|
||||
return table.Get(NodeValue::kTexture).value<TexturePtr>();
|
||||
}
|
||||
|
||||
FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time)
|
||||
{
|
||||
// Set up output frame parameters
|
||||
VideoParams frame_params = GetCacheVideoParams();
|
||||
|
||||
@@ -128,25 +131,35 @@ void RenderProcessor::Run()
|
||||
frame_length /= 2;
|
||||
}
|
||||
|
||||
FramePtr frame = GenerateFrame(time, frame_length);
|
||||
TexturePtr texture = GenerateTexture(time, frame_length);
|
||||
|
||||
if (GetCacheVideoParams().interlacing() != VideoParams::kInterlaceNone) {
|
||||
// Get next between frame and interlace it
|
||||
FramePtr next_frame = GenerateFrame(time + frame_length, frame_length);
|
||||
TexturePtr top = texture;
|
||||
TexturePtr bottom = GenerateTexture(time + frame_length, frame_length);
|
||||
|
||||
FramePtr top, bottom;
|
||||
if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedTopFirst) {
|
||||
top = frame;
|
||||
bottom = next_frame;
|
||||
} else {
|
||||
top = next_frame;
|
||||
bottom = frame;
|
||||
if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedBottomFirst) {
|
||||
std::swap(top, bottom);
|
||||
}
|
||||
|
||||
frame = Frame::Interlace(top, bottom);
|
||||
texture = render_ctx_->InterlaceTexture(top, bottom, GetCacheVideoParams());
|
||||
}
|
||||
|
||||
ticket_->Finish(QVariant::fromValue(frame));
|
||||
if (ticket_->property("textureonly").toBool()) {
|
||||
// Return GPU texture
|
||||
if (!texture) {
|
||||
texture = render_ctx_->CreateTexture(GetCacheVideoParams());
|
||||
}
|
||||
|
||||
render_ctx_->Flush();
|
||||
|
||||
ticket_->Finish(QVariant::fromValue(texture));
|
||||
} else {
|
||||
// Convert to CPU frame
|
||||
FramePtr frame = GenerateFrame(texture, time);
|
||||
|
||||
ticket_->Finish(QVariant::fromValue(frame));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RenderManager::kTypeAudio:
|
||||
|
||||
@@ -62,7 +62,9 @@ protected:
|
||||
private:
|
||||
RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx, StillImageCache* still_image_cache, DecoderCache* decoder_cache, ShaderCache* shader_cache, QVariant default_shader);
|
||||
|
||||
FramePtr GenerateFrame(const rational &time, const rational &frame_length);
|
||||
TexturePtr GenerateTexture(const rational& time, const rational& frame_length);
|
||||
|
||||
FramePtr GenerateFrame(TexturePtr texture, const rational &time);
|
||||
|
||||
void Run();
|
||||
|
||||
|
||||
@@ -120,6 +120,11 @@ public:
|
||||
return type_;
|
||||
}
|
||||
|
||||
Renderer* renderer() const
|
||||
{
|
||||
return renderer_;
|
||||
}
|
||||
|
||||
private:
|
||||
Renderer* renderer_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user