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:
parent
8a9b20b27e
commit
bea7658807
5 changed files with 155 additions and 6 deletions
|
@ -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
56
app/src/opengl.c
Normal 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
36
app/src/opengl.h
Normal 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
|
|
@ -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;
|
||||||
SDL_TEXTUREACCESS_STREAMING,
|
struct size size = screen->frame_size;
|
||||||
frame_size.width, frame_size.height);
|
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
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue