From bea7658807d276aeab7d18d856a366c83ee05827 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 11 Apr 2020 16:08:23 +0200 Subject: [PATCH] Enable trilinear filtering for OpenGL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve downscaling quality if mipmapping is available. Suggested-by: Giumo Clanjor (哆啦比猫/兰威举) Fixes #40 Ref: --- app/meson.build | 1 + app/src/opengl.c | 56 ++++++++++++++++++++++++++++++++++++++++++++ app/src/opengl.h | 36 ++++++++++++++++++++++++++++ app/src/screen.c | 61 +++++++++++++++++++++++++++++++++++++++++++----- app/src/screen.h | 7 ++++++ 5 files changed, 155 insertions(+), 6 deletions(-) create mode 100644 app/src/opengl.c create mode 100644 app/src/opengl.h diff --git a/app/meson.build b/app/meson.build index 49c4683f..5d2b4caa 100644 --- a/app/meson.build +++ b/app/meson.build @@ -11,6 +11,7 @@ src = [ 'src/file_handler.c', 'src/fps_counter.c', 'src/input_manager.c', + 'src/opengl.c', 'src/receiver.c', 'src/recorder.c', 'src/scrcpy.c', diff --git a/app/src/opengl.c b/app/src/opengl.c new file mode 100644 index 00000000..da05c082 --- /dev/null +++ b/app/src/opengl.c @@ -0,0 +1,56 @@ +#include "opengl.h" + +#include +#include +#include "SDL2/SDL.h" + +void +sc_opengl_init(struct sc_opengl *gl) { + gl->GetString = SDL_GL_GetProcAddress("glGetString"); + assert(gl->GetString); + + gl->TexParameterf = SDL_GL_GetProcAddress("glTexParameterf"); + assert(gl->TexParameterf); + + gl->TexParameteri = SDL_GL_GetProcAddress("glTexParameteri"); + assert(gl->TexParameteri); + + // optional + gl->GenerateMipmap = SDL_GL_GetProcAddress("glGenerateMipmap"); + + const char *version = (const char *) gl->GetString(GL_VERSION); + assert(version); + gl->version = version; + +#define OPENGL_ES_PREFIX "OpenGL ES " + /* starts with "OpenGL ES " */ + gl->is_opengles = !strncmp(gl->version, OPENGL_ES_PREFIX, + sizeof(OPENGL_ES_PREFIX) - 1); + if (gl->is_opengles) { + /* skip the prefix */ + version += sizeof(PREFIX) - 1; + } + + int r = sscanf(version, "%d.%d", &gl->version_major, &gl->version_minor); + if (r != 2) { + // failed to parse the version + gl->version_major = 0; + gl->version_minor = 0; + } +} + +bool +sc_opengl_version_at_least(struct sc_opengl *gl, + int minver_major, int minver_minor, + int minver_es_major, int minver_es_minor) +{ + if (gl->is_opengles) { + return gl->version_major > minver_es_major + || (gl->version_major == minver_es_major + && gl->version_minor >= minver_es_minor); + } + + return gl->version_major > minver_major + || (gl->version_major == minver_major + && gl->version_minor >= minver_minor); +} diff --git a/app/src/opengl.h b/app/src/opengl.h new file mode 100644 index 00000000..f0a89a14 --- /dev/null +++ b/app/src/opengl.h @@ -0,0 +1,36 @@ +#ifndef SC_OPENGL_H +#define SC_OPENGL_H + +#include +#include + +#include "config.h" + +struct sc_opengl { + const char *version; + bool is_opengles; + int version_major; + int version_minor; + + const GLubyte * + (*GetString)(GLenum name); + + void + (*TexParameterf)(GLenum target, GLenum pname, GLfloat param); + + void + (*TexParameteri)(GLenum target, GLenum pname, GLint param); + + void + (*GenerateMipmap)(GLenum target); +}; + +void +sc_opengl_init(struct sc_opengl *gl); + +bool +sc_opengl_version_at_least(struct sc_opengl *gl, + int minver_major, int minver_minor, + int minver_es_major, int minver_es_minor); + +#endif diff --git a/app/src/screen.c b/app/src/screen.c index 83b07362..c982a5c8 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -168,10 +168,30 @@ screen_init(struct screen *screen) { } static inline SDL_Texture * -create_texture(SDL_Renderer *renderer, struct size frame_size) { - return SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, - SDL_TEXTUREACCESS_STREAMING, - frame_size.width, frame_size.height); +create_texture(struct screen *screen) { + SDL_Renderer *renderer = screen->renderer; + struct size size = screen->frame_size; + SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, + SDL_TEXTUREACCESS_STREAMING, + size.width, size.height); + if (!texture) { + return NULL; + } + + if (screen->mipmaps) { + struct sc_opengl *gl = &screen->gl; + + SDL_GL_BindTexture(texture, NULL, NULL); + + // Enable trilinear filtering for downscaling + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR); + gl->TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -.5f); + + SDL_GL_UnbindTexture(texture); + } + + return texture; } bool @@ -238,6 +258,28 @@ screen_init_rendering(struct screen *screen, const char *window_title, return false; } + // stats with "opengl" + screen->use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6); + if (screen->use_opengl) { + struct sc_opengl *gl = &screen->gl; + sc_opengl_init(gl); + + LOGI("OpenGL version: %s", gl->version); + + bool supports_mipmaps = + sc_opengl_version_at_least(gl, 3, 0, /* OpenGL 3.0+ */ + 2, 0 /* OpenGL ES 2.0+ */); + if (supports_mipmaps) { + LOGI("Trilinear filtering enabled"); + screen->mipmaps = true; + } else { + LOGW("Trilinear filtering disabled " + "(OpenGL 3.0+ or ES 2.0+ required)"); + } + } else { + LOGW("Trilinear filtering disabled (not an OpenGL renderer)"); + } + SDL_Surface *icon = read_xpm(icon_xpm); if (icon) { SDL_SetWindowIcon(screen->window, icon); @@ -248,7 +290,7 @@ screen_init_rendering(struct screen *screen, const char *window_title, LOGI("Initial texture: %" PRIu16 "x%" PRIu16, frame_size.width, frame_size.height); - screen->texture = create_texture(screen->renderer, frame_size); + screen->texture = create_texture(screen); if (!screen->texture) { LOGC("Could not create texture: %s", SDL_GetError()); screen_destroy(screen); @@ -346,7 +388,7 @@ prepare_for_frame(struct screen *screen, struct size new_frame_size) { LOGI("New texture: %" PRIu16 "x%" PRIu16, screen->frame_size.width, screen->frame_size.height); - screen->texture = create_texture(screen->renderer, new_frame_size); + screen->texture = create_texture(screen); if (!screen->texture) { LOGC("Could not create texture: %s", SDL_GetError()); return false; @@ -363,6 +405,13 @@ update_texture(struct screen *screen, const AVFrame *frame) { frame->data[0], frame->linesize[0], frame->data[1], frame->linesize[1], frame->data[2], frame->linesize[2]); + + if (screen->mipmaps) { + assert(screen->use_opengl); + SDL_GL_BindTexture(screen->texture, NULL, NULL); + screen->gl.GenerateMipmap(GL_TEXTURE_2D); + SDL_GL_UnbindTexture(screen->texture); + } } bool diff --git a/app/src/screen.h b/app/src/screen.h index 39593e04..90537463 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -7,6 +7,7 @@ #include "config.h" #include "common.h" +#include "opengl.h" #define WINDOW_POSITION_UNDEFINED (-0x8000) @@ -16,6 +17,8 @@ struct screen { SDL_Window *window; SDL_Renderer *renderer; SDL_Texture *texture; + bool use_opengl; + struct sc_opengl gl; struct size frame_size; struct size content_size; // rotated frame_size // The window size the last time it was not maximized or fullscreen. @@ -29,12 +32,15 @@ struct screen { bool fullscreen; bool maximized; bool no_window; + bool mipmaps; }; #define SCREEN_INITIALIZER { \ .window = NULL, \ .renderer = NULL, \ .texture = NULL, \ + .use_opengl = false, \ + .gl = {0}, \ .frame_size = { \ .width = 0, \ .height = 0, \ @@ -56,6 +62,7 @@ struct screen { .fullscreen = false, \ .maximized = false, \ .no_window = false, \ + .mipmaps = false, \ } // initialize default values