Enable trilinear filtering for OpenGL

Improve downscaling quality if mipmapping is available.

Suggested-by: Giumo Clanjor (哆啦比猫/兰威举) <cjxgm2@gmail.com>

Fixes #40 <https://github.com/Genymobile/scrcpy/issues/40>
Ref: <https://github.com/Genymobile/scrcpy/issues/40#issuecomment-591917787>
This commit is contained in:
Romain Vimont 2020-04-11 16:08:23 +02:00
parent 8a9b20b27e
commit bea7658807
5 changed files with 155 additions and 6 deletions

View file

@ -11,6 +11,7 @@ src = [
'src/file_handler.c', 'src/file_handler.c',
'src/fps_counter.c', 'src/fps_counter.c',
'src/input_manager.c', 'src/input_manager.c',
'src/opengl.c',
'src/receiver.c', 'src/receiver.c',
'src/recorder.c', 'src/recorder.c',
'src/scrcpy.c', 'src/scrcpy.c',

56
app/src/opengl.c Normal file
View file

@ -0,0 +1,56 @@
#include "opengl.h"
#include <assert.h>
#include <stdio.h>
#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);
}

36
app/src/opengl.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef SC_OPENGL_H
#define SC_OPENGL_H
#include <stdbool.h>
#include <SDL2/SDL_opengl.h>
#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

View file

@ -168,10 +168,30 @@ screen_init(struct screen *screen) {
} }
static inline SDL_Texture * static inline SDL_Texture *
create_texture(SDL_Renderer *renderer, struct size frame_size) { create_texture(struct screen *screen) {
return SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_Renderer *renderer = screen->renderer;
struct size size = screen->frame_size;
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
SDL_TEXTUREACCESS_STREAMING, SDL_TEXTUREACCESS_STREAMING,
frame_size.width, frame_size.height); 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 bool
@ -238,6 +258,28 @@ screen_init_rendering(struct screen *screen, const char *window_title,
return false; 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); SDL_Surface *icon = read_xpm(icon_xpm);
if (icon) { if (icon) {
SDL_SetWindowIcon(screen->window, 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, LOGI("Initial texture: %" PRIu16 "x%" PRIu16, frame_size.width,
frame_size.height); frame_size.height);
screen->texture = create_texture(screen->renderer, frame_size); screen->texture = create_texture(screen);
if (!screen->texture) { if (!screen->texture) {
LOGC("Could not create texture: %s", SDL_GetError()); LOGC("Could not create texture: %s", SDL_GetError());
screen_destroy(screen); screen_destroy(screen);
@ -346,7 +388,7 @@ prepare_for_frame(struct screen *screen, struct size new_frame_size) {
LOGI("New texture: %" PRIu16 "x%" PRIu16, LOGI("New texture: %" PRIu16 "x%" PRIu16,
screen->frame_size.width, screen->frame_size.height); 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) { if (!screen->texture) {
LOGC("Could not create texture: %s", SDL_GetError()); LOGC("Could not create texture: %s", SDL_GetError());
return false; return false;
@ -363,6 +405,13 @@ update_texture(struct screen *screen, const AVFrame *frame) {
frame->data[0], frame->linesize[0], frame->data[0], frame->linesize[0],
frame->data[1], frame->linesize[1], frame->data[1], frame->linesize[1],
frame->data[2], frame->linesize[2]); 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 bool

View file

@ -7,6 +7,7 @@
#include "config.h" #include "config.h"
#include "common.h" #include "common.h"
#include "opengl.h"
#define WINDOW_POSITION_UNDEFINED (-0x8000) #define WINDOW_POSITION_UNDEFINED (-0x8000)
@ -16,6 +17,8 @@ struct screen {
SDL_Window *window; SDL_Window *window;
SDL_Renderer *renderer; SDL_Renderer *renderer;
SDL_Texture *texture; SDL_Texture *texture;
bool use_opengl;
struct sc_opengl gl;
struct size frame_size; struct size frame_size;
struct size content_size; // rotated frame_size struct size content_size; // rotated frame_size
// The window size the last time it was not maximized or fullscreen. // The window size the last time it was not maximized or fullscreen.
@ -29,12 +32,15 @@ struct screen {
bool fullscreen; bool fullscreen;
bool maximized; bool maximized;
bool no_window; bool no_window;
bool mipmaps;
}; };
#define SCREEN_INITIALIZER { \ #define SCREEN_INITIALIZER { \
.window = NULL, \ .window = NULL, \
.renderer = NULL, \ .renderer = NULL, \
.texture = NULL, \ .texture = NULL, \
.use_opengl = false, \
.gl = {0}, \
.frame_size = { \ .frame_size = { \
.width = 0, \ .width = 0, \
.height = 0, \ .height = 0, \
@ -56,6 +62,7 @@ struct screen {
.fullscreen = false, \ .fullscreen = false, \
.maximized = false, \ .maximized = false, \
.no_window = false, \ .no_window = false, \
.mipmaps = false, \
} }
// initialize default values // initialize default values