From 5fb9793f437760234ed839d8c5a6ee18cec1af5e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 13 May 2021 16:38:45 +1000 Subject: [PATCH] opengl: use texture cache Prevents a ton of texture allocation/deallocation. Should theoretically improve things, might make no noticeable difference. --- app/render/opengl/openglrenderer.cpp | 142 +++++++++++++++++++++------ app/render/opengl/openglrenderer.h | 34 +++++++ 2 files changed, 146 insertions(+), 30 deletions(-) diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index c72446c03..d97a6def3 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -20,11 +20,14 @@ #include "openglrenderer.h" +#include #include #include namespace olive { +const int OpenGLRenderer::kTextureCacheMaxSize = 5000; + const QVector blit_vertices = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, @@ -75,8 +78,11 @@ private: OpenGLRenderer::OpenGLRenderer(QObject* parent) : Renderer(parent), + cache_timer_(this), context_(nullptr) { + cache_timer_.setInterval(kTextureCacheMaxSize); + connect(&cache_timer_, &QTimer::timeout, this, &OpenGLRenderer::GarbageCollectTextureCache); } OpenGLRenderer::~OpenGLRenderer() @@ -138,6 +144,8 @@ void OpenGLRenderer::PostInit() // Set up framebuffer used for various things functions_->glGenFramebuffers(1, &framebuffer_); + + cache_timer_.start(); } void OpenGLRenderer::DestroyInternal() @@ -146,12 +154,19 @@ void OpenGLRenderer::DestroyInternal() // Delete framebuffer functions_->glDeleteFramebuffers(1, &framebuffer_); + for (auto it=texture_cache_.cbegin(); it!=texture_cache_.cend(); it++) { + functions_->glDeleteTextures(1, &it->texture); + } + texture_cache_.clear(); + // Delete context if it belongs to us if (context_->parent() == this) { delete context_; } context_ = nullptr; } + + cache_timer_.stop(); } void OpenGLRenderer::ClearDestination(double r, double g, double b, double a) @@ -162,52 +177,79 @@ void OpenGLRenderer::ClearDestination(double r, double g, double b, double a) QVariant OpenGLRenderer::CreateNativeTexture2D(int width, int height, VideoParams::Format format, int channel_count, const void *data, int linesize) { - GLuint texture; - functions_->glGenTextures(1, &texture); + GLuint texture = GetCachedTexture(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); + // 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}); } - functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + if (new_tex || data) { + functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize); - functions_->glBindTexture(GL_TEXTURE_2D, current_tex); + 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::CreateNativeTexture3D(int width, int height, int depth, VideoParams::Format format, int channel_count, const void *data, int linesize) { - PRINT_GL_ERRORS; + GLuint texture = GetCachedTexture(width, height, depth, format, channel_count); - GLuint texture; - functions_->glGenTextures(1, &texture); + // 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, depth, format, channel_count}); + } - functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize); + if (new_tex || data) { + functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, linesize); - GLint current_tex; - functions_->glGetIntegerv(GL_TEXTURE_BINDING_3D, ¤t_tex); + GLint current_tex; + functions_->glGetIntegerv(GL_TEXTURE_BINDING_3D, ¤t_tex); - functions_->glBindTexture(GL_TEXTURE_3D, texture); + functions_->glBindTexture(GL_TEXTURE_3D, texture); - context_->extraFunctions()->glTexImage3D(GL_TEXTURE_3D, 0, GetInternalFormat(format, channel_count), - width, height, depth, 0, GetPixelFormat(channel_count), - GetPixelType(format), data); + if (new_tex) { + context_->extraFunctions()->glTexImage3D(GL_TEXTURE_3D, 0, GetInternalFormat(format, channel_count), + width, height, depth, 0, GetPixelFormat(channel_count), + GetPixelType(format), data); + } else { + context_->extraFunctions()->glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, + width, height, depth, + GetPixelFormat(channel_count), GetPixelType(format), + data); + } - functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + functions_->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - functions_->glBindTexture(GL_TEXTURE_3D, current_tex); + functions_->glBindTexture(GL_TEXTURE_3D, current_tex); + } return texture; } @@ -232,7 +274,13 @@ void OpenGLRenderer::DetachTextureAsDestination() void OpenGLRenderer::DestroyNativeTexture(QVariant texture) { GLuint t = texture.value(); - functions_->glDeleteTextures(1, &t); + + if (t > 0) { + TextureCacheKey key = texture_params_.value(t); + TextureCacheEntry entry = {key, t, QDateTime::currentMSecsSinceEpoch()}; + + texture_cache_.append(entry); + } } QVariant OpenGLRenderer::CreateNativeShader(ShaderCode code) @@ -721,4 +769,38 @@ void OpenGLRenderer::PrepareInputTexture(GLenum target, Texture::Interpolation i functions_->glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } +GLuint OpenGLRenderer::GetCachedTexture(int width, int height, int depth, VideoParams::Format format, int channel_count) +{ + TextureCacheKey input_key = {width, height, depth, format, channel_count}; + + for (int i=0; iage < max_age) { + GLuint t = it->texture; + texture_params_.remove(t); + functions_->glDeleteTextures(1, &t); + it = texture_cache_.erase(it); + } else { + it++; + } + } +} + } diff --git a/app/render/opengl/openglrenderer.h b/app/render/opengl/openglrenderer.h index 1d54ae904..340a9eeee 100644 --- a/app/render/opengl/openglrenderer.h +++ b/app/render/opengl/openglrenderer.h @@ -27,6 +27,7 @@ #include #include #include +#include #include "render/renderer.h" @@ -86,6 +87,10 @@ private: void PrepareInputTexture(GLenum target, Texture::Interpolation interp); + GLuint GetCachedTexture(int width, int height, int depth, VideoParams::Format format, int channel_count); + + QTimer cache_timer_; + QOpenGLContext* context_; QOpenGLFunctions* functions_; @@ -94,6 +99,35 @@ private: GLuint framebuffer_; + struct TextureCacheKey { + int width; + int height; + int depth; + VideoParams::Format format; + int channel_count; + + bool operator==(const TextureCacheKey &rhs) const + { + return width == rhs.width && height == rhs.height && depth == rhs.depth + && format == rhs.format && channel_count == rhs.channel_count; + } + }; + + struct TextureCacheEntry { + TextureCacheKey key; + GLuint texture; + qint64 age; + }; + + QVector texture_cache_; + + QMap texture_params_; + + static const int kTextureCacheMaxSize; + +private slots: + void GarbageCollectTextureCache(); + }; }