mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-04-21 03:55:05 +00:00
Extract texture management to a separate file
Move texture management out of screen.c, which already handles the window and the rendering. This paves the way to implement optional software filtering (swscale) properly, as an alternative to mipmaps (which are not available everywhere).
This commit is contained in:
parent
edf710af3c
commit
0b4b4609dc
5 changed files with 180 additions and 116 deletions
|
@ -11,6 +11,7 @@ src = [
|
|||
'src/event_converter.c',
|
||||
'src/file_handler.c',
|
||||
'src/fps_counter.c',
|
||||
'src/frame_texture.c',
|
||||
'src/input_manager.c',
|
||||
'src/opengl.c',
|
||||
'src/receiver.c',
|
||||
|
|
120
app/src/frame_texture.c
Normal file
120
app/src/frame_texture.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
#include "frame_texture.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static SDL_Texture *
|
||||
create_texture(struct sc_frame_texture *ftex, struct size size) {
|
||||
SDL_Renderer *renderer = ftex->renderer;
|
||||
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
size.width, size.height);
|
||||
if (!texture) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ftex->mipmaps) {
|
||||
struct sc_opengl *gl = &ftex->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, -1.f);
|
||||
|
||||
SDL_GL_UnbindTexture(texture);
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
bool
|
||||
sc_frame_texture_init(struct sc_frame_texture *ftex, SDL_Renderer *renderer,
|
||||
bool mipmaps, struct size initial_size) {
|
||||
SDL_RendererInfo renderer_info;
|
||||
int r = SDL_GetRendererInfo(renderer, &renderer_info);
|
||||
const char *renderer_name = r ? NULL : renderer_info.name;
|
||||
LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)");
|
||||
|
||||
ftex->mipmaps = false;
|
||||
|
||||
// starts with "opengl"
|
||||
bool use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
|
||||
if (use_opengl) {
|
||||
struct sc_opengl *gl = &ftex->gl;
|
||||
sc_opengl_init(gl);
|
||||
|
||||
LOGI("OpenGL version: %s", gl->version);
|
||||
|
||||
if (mipmaps) {
|
||||
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");
|
||||
ftex->mipmaps = true;
|
||||
} else {
|
||||
LOGW("Trilinear filtering disabled "
|
||||
"(OpenGL 3.0+ or ES 2.0+ required)");
|
||||
}
|
||||
} else {
|
||||
LOGI("Trilinear filtering disabled");
|
||||
}
|
||||
} else {
|
||||
LOGD("Trilinear filtering disabled (not an OpenGL renderer)");
|
||||
}
|
||||
|
||||
LOGI("Initial texture: %" PRIu16 "x%" PRIu16, initial_size.width,
|
||||
initial_size.height);
|
||||
ftex->renderer = renderer;
|
||||
ftex->texture = create_texture(ftex, initial_size);
|
||||
if (!ftex->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
SDL_DestroyRenderer(ftex->renderer);
|
||||
return false;
|
||||
}
|
||||
|
||||
ftex->texture_size = initial_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
sc_frame_texture_destroy(struct sc_frame_texture *ftex) {
|
||||
if (ftex->texture) {
|
||||
SDL_DestroyTexture(ftex->texture);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
sc_frame_texture_update(struct sc_frame_texture *ftex, const AVFrame *frame) {
|
||||
struct size frame_size = {frame->width, frame->height};
|
||||
if (ftex->texture_size.width != frame_size.width
|
||||
|| ftex->texture_size.height != frame_size.height) {
|
||||
// Frame dimensions changed, destroy texture
|
||||
SDL_DestroyTexture(ftex->texture);
|
||||
|
||||
LOGI("New texture: %" PRIu16 "x%" PRIu16, frame_size.width,
|
||||
frame_size.height);
|
||||
ftex->texture = create_texture(ftex, frame_size);
|
||||
if (!ftex->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
ftex->texture_size = frame_size;
|
||||
}
|
||||
|
||||
SDL_UpdateYUVTexture(ftex->texture, NULL,
|
||||
frame->data[0], frame->linesize[0],
|
||||
frame->data[1], frame->linesize[1],
|
||||
frame->data[2], frame->linesize[2]);
|
||||
|
||||
if (ftex->mipmaps) {
|
||||
SDL_GL_BindTexture(ftex->texture, NULL, NULL);
|
||||
ftex->gl.GenerateMipmap(GL_TEXTURE_2D);
|
||||
SDL_GL_UnbindTexture(ftex->texture);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
33
app/src/frame_texture.h
Normal file
33
app/src/frame_texture.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#ifndef SC_FRAME_TEXTURE_H
|
||||
#define SC_FRAME_TEXTURE_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include "coords.h"
|
||||
#include "opengl.h"
|
||||
|
||||
struct sc_frame_texture {
|
||||
SDL_Renderer *renderer; // owned by struct screen
|
||||
|
||||
bool mipmaps;
|
||||
struct sc_opengl gl;
|
||||
|
||||
SDL_Texture *texture;
|
||||
struct size texture_size;
|
||||
};
|
||||
|
||||
bool
|
||||
sc_frame_texture_init(struct sc_frame_texture *ftex, SDL_Renderer *renderer,
|
||||
bool mipmaps, struct size initial_size);
|
||||
|
||||
void
|
||||
sc_frame_texture_destroy(struct sc_frame_texture *ftex);
|
||||
|
||||
bool
|
||||
sc_frame_texture_update(struct sc_frame_texture *ftex, const AVFrame *frame);
|
||||
|
||||
#endif
|
132
app/src/screen.c
132
app/src/screen.c
|
@ -195,33 +195,6 @@ screen_init(struct screen *screen) {
|
|||
*screen = (struct screen) SCREEN_INITIALIZER;
|
||||
}
|
||||
|
||||
static inline SDL_Texture *
|
||||
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, -1.f);
|
||||
|
||||
SDL_GL_UnbindTexture(texture);
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
bool
|
||||
screen_init_rendering(struct screen *screen, const char *window_title,
|
||||
struct size frame_size, bool always_on_top,
|
||||
|
@ -270,39 +243,17 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
|||
SDL_RENDERER_ACCELERATED);
|
||||
if (!screen->renderer) {
|
||||
LOGC("Could not create renderer: %s", SDL_GetError());
|
||||
screen_destroy(screen);
|
||||
SDL_DestroyWindow(screen->window);
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_RendererInfo renderer_info;
|
||||
int r = SDL_GetRendererInfo(screen->renderer, &renderer_info);
|
||||
const char *renderer_name = r ? NULL : renderer_info.name;
|
||||
LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)");
|
||||
|
||||
// starts with "opengl"
|
||||
bool use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
|
||||
if (use_opengl) {
|
||||
struct sc_opengl *gl = &screen->gl;
|
||||
sc_opengl_init(gl);
|
||||
|
||||
LOGI("OpenGL version: %s", gl->version);
|
||||
|
||||
if (mipmaps) {
|
||||
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 {
|
||||
LOGI("Trilinear filtering disabled");
|
||||
}
|
||||
} else {
|
||||
LOGD("Trilinear filtering disabled (not an OpenGL renderer)");
|
||||
bool ok = sc_frame_texture_init(&screen->ftex, screen->renderer, mipmaps,
|
||||
frame_size);
|
||||
if (!ok) {
|
||||
LOGC("Could not init frame texture");
|
||||
SDL_DestroyRenderer(screen->renderer);
|
||||
SDL_DestroyWindow(screen->window);
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_Surface *icon = read_xpm(icon_xpm);
|
||||
|
@ -313,15 +264,6 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
|||
LOGW("Could not load icon");
|
||||
}
|
||||
|
||||
LOGI("Initial texture: %" PRIu16 "x%" PRIu16, frame_size.width,
|
||||
frame_size.height);
|
||||
screen->texture = create_texture(screen);
|
||||
if (!screen->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
screen_destroy(screen);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset the window size to trigger a SIZE_CHANGED event, to workaround
|
||||
// HiDPI issues with some SDL renderers when several displays having
|
||||
// different HiDPI scaling are connected
|
||||
|
@ -339,15 +281,9 @@ screen_show_window(struct screen *screen) {
|
|||
|
||||
void
|
||||
screen_destroy(struct screen *screen) {
|
||||
if (screen->texture) {
|
||||
SDL_DestroyTexture(screen->texture);
|
||||
}
|
||||
if (screen->renderer) {
|
||||
SDL_DestroyRenderer(screen->renderer);
|
||||
}
|
||||
if (screen->window) {
|
||||
SDL_DestroyWindow(screen->window);
|
||||
}
|
||||
sc_frame_texture_destroy(&screen->ftex);
|
||||
SDL_DestroyRenderer(screen->renderer);
|
||||
SDL_DestroyWindow(screen->window);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -408,13 +344,10 @@ screen_set_rotation(struct screen *screen, unsigned rotation) {
|
|||
}
|
||||
|
||||
// recreate the texture and resize the window if the frame size has changed
|
||||
static bool
|
||||
static void
|
||||
prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
||||
if (screen->frame_size.width != new_frame_size.width
|
||||
|| screen->frame_size.height != new_frame_size.height) {
|
||||
// frame dimension changed, destroy texture
|
||||
SDL_DestroyTexture(screen->texture);
|
||||
|
||||
screen->frame_size = new_frame_size;
|
||||
|
||||
struct size new_content_size =
|
||||
|
@ -422,31 +355,6 @@ prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
|||
set_content_size(screen, new_content_size);
|
||||
|
||||
screen_update_content_rect(screen);
|
||||
|
||||
LOGI("New texture: %" PRIu16 "x%" PRIu16,
|
||||
screen->frame_size.width, screen->frame_size.height);
|
||||
screen->texture = create_texture(screen);
|
||||
if (!screen->texture) {
|
||||
LOGC("Could not create texture: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// write the frame into the texture
|
||||
static void
|
||||
update_texture(struct screen *screen, const AVFrame *frame) {
|
||||
SDL_UpdateYUVTexture(screen->texture, NULL,
|
||||
frame->data[0], frame->linesize[0],
|
||||
frame->data[1], frame->linesize[1],
|
||||
frame->data[2], frame->linesize[2]);
|
||||
|
||||
if (screen->mipmaps) {
|
||||
SDL_GL_BindTexture(screen->texture, NULL, NULL);
|
||||
screen->gl.GenerateMipmap(GL_TEXTURE_2D);
|
||||
SDL_GL_UnbindTexture(screen->texture);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -454,10 +362,12 @@ bool
|
|||
screen_update_frame(struct screen *screen, struct video_buffer *vb) {
|
||||
const AVFrame *frame = video_buffer_take_rendering_frame(vb);
|
||||
struct size new_frame_size = {frame->width, frame->height};
|
||||
if (!prepare_for_frame(screen, new_frame_size)) {
|
||||
prepare_for_frame(screen, new_frame_size);
|
||||
|
||||
bool ok = sc_frame_texture_update(&screen->ftex, frame);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
update_texture(screen, frame);
|
||||
|
||||
screen_render(screen, false);
|
||||
return true;
|
||||
|
@ -469,9 +379,11 @@ screen_render(struct screen *screen, bool update_content_rect) {
|
|||
screen_update_content_rect(screen);
|
||||
}
|
||||
|
||||
SDL_RenderClear(screen->renderer);
|
||||
struct sc_frame_texture *ftex = &screen->ftex;
|
||||
SDL_RenderClear(ftex->renderer);
|
||||
if (screen->rotation == 0) {
|
||||
SDL_RenderCopy(screen->renderer, screen->texture, NULL, &screen->rect);
|
||||
SDL_RenderCopy(screen->renderer, ftex->texture, NULL,
|
||||
&screen->rect);
|
||||
} else {
|
||||
// rotation in RenderCopyEx() is clockwise, while screen->rotation is
|
||||
// counterclockwise (to be consistent with --lock-video-orientation)
|
||||
|
@ -491,10 +403,10 @@ screen_render(struct screen *screen, bool update_content_rect) {
|
|||
dstrect = &screen->rect;
|
||||
}
|
||||
|
||||
SDL_RenderCopyEx(screen->renderer, screen->texture, NULL, dstrect,
|
||||
SDL_RenderCopyEx(screen->renderer, ftex->texture, NULL, dstrect,
|
||||
angle, NULL, 0);
|
||||
}
|
||||
SDL_RenderPresent(screen->renderer);
|
||||
SDL_RenderPresent(ftex->renderer);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <libavformat/avformat.h>
|
||||
|
||||
#include "coords.h"
|
||||
#include "frame_texture.h"
|
||||
#include "opengl.h"
|
||||
|
||||
struct video_buffer;
|
||||
|
@ -15,8 +16,8 @@ struct video_buffer;
|
|||
struct screen {
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Texture *texture;
|
||||
struct sc_opengl gl;
|
||||
struct sc_frame_texture ftex;
|
||||
|
||||
struct size frame_size;
|
||||
struct size content_size; // rotated frame_size
|
||||
|
||||
|
@ -33,14 +34,12 @@ struct screen {
|
|||
bool fullscreen;
|
||||
bool maximized;
|
||||
bool no_window;
|
||||
bool mipmaps;
|
||||
};
|
||||
|
||||
#define SCREEN_INITIALIZER { \
|
||||
.window = NULL, \
|
||||
.renderer = NULL, \
|
||||
.texture = NULL, \
|
||||
.gl = {0}, \
|
||||
.ftex = {0}, \
|
||||
.frame_size = { \
|
||||
.width = 0, \
|
||||
.height = 0, \
|
||||
|
@ -65,7 +64,6 @@ struct screen {
|
|||
.fullscreen = false, \
|
||||
.maximized = false, \
|
||||
.no_window = false, \
|
||||
.mipmaps = false, \
|
||||
}
|
||||
|
||||
// initialize default values
|
||||
|
|
Loading…
Add table
Reference in a new issue