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:
Romain Vimont 2021-02-07 19:26:58 +01:00
commit 0b4b4609dc
5 changed files with 180 additions and 116 deletions

View file

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

120
app/src/frame_texture.c Normal file
View 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
View 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

View file

@ -195,33 +195,6 @@ screen_init(struct screen *screen) {
*screen = (struct screen) SCREEN_INITIALIZER; *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 bool
screen_init_rendering(struct screen *screen, const char *window_title, screen_init_rendering(struct screen *screen, const char *window_title,
struct size frame_size, bool always_on_top, 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); SDL_RENDERER_ACCELERATED);
if (!screen->renderer) { if (!screen->renderer) {
LOGC("Could not create renderer: %s", SDL_GetError()); LOGC("Could not create renderer: %s", SDL_GetError());
screen_destroy(screen); SDL_DestroyWindow(screen->window);
return false; return false;
} }
SDL_RendererInfo renderer_info; bool ok = sc_frame_texture_init(&screen->ftex, screen->renderer, mipmaps,
int r = SDL_GetRendererInfo(screen->renderer, &renderer_info); frame_size);
const char *renderer_name = r ? NULL : renderer_info.name; if (!ok) {
LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)"); LOGC("Could not init frame texture");
SDL_DestroyRenderer(screen->renderer);
// starts with "opengl" SDL_DestroyWindow(screen->window);
bool use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6); return false;
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)");
} }
SDL_Surface *icon = read_xpm(icon_xpm); 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"); 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 // Reset the window size to trigger a SIZE_CHANGED event, to workaround
// HiDPI issues with some SDL renderers when several displays having // HiDPI issues with some SDL renderers when several displays having
// different HiDPI scaling are connected // different HiDPI scaling are connected
@ -339,15 +281,9 @@ screen_show_window(struct screen *screen) {
void void
screen_destroy(struct screen *screen) { screen_destroy(struct screen *screen) {
if (screen->texture) { sc_frame_texture_destroy(&screen->ftex);
SDL_DestroyTexture(screen->texture); SDL_DestroyRenderer(screen->renderer);
} SDL_DestroyWindow(screen->window);
if (screen->renderer) {
SDL_DestroyRenderer(screen->renderer);
}
if (screen->window) {
SDL_DestroyWindow(screen->window);
}
} }
static void 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 // 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) { prepare_for_frame(struct screen *screen, struct size new_frame_size) {
if (screen->frame_size.width != new_frame_size.width if (screen->frame_size.width != new_frame_size.width
|| screen->frame_size.height != new_frame_size.height) { || screen->frame_size.height != new_frame_size.height) {
// frame dimension changed, destroy texture
SDL_DestroyTexture(screen->texture);
screen->frame_size = new_frame_size; screen->frame_size = new_frame_size;
struct size new_content_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); set_content_size(screen, new_content_size);
screen_update_content_rect(screen); 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) { screen_update_frame(struct screen *screen, struct video_buffer *vb) {
const AVFrame *frame = video_buffer_take_rendering_frame(vb); const AVFrame *frame = video_buffer_take_rendering_frame(vb);
struct size new_frame_size = {frame->width, frame->height}; 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; return false;
} }
update_texture(screen, frame);
screen_render(screen, false); screen_render(screen, false);
return true; return true;
@ -469,9 +379,11 @@ screen_render(struct screen *screen, bool update_content_rect) {
screen_update_content_rect(screen); screen_update_content_rect(screen);
} }
SDL_RenderClear(screen->renderer); struct sc_frame_texture *ftex = &screen->ftex;
SDL_RenderClear(ftex->renderer);
if (screen->rotation == 0) { if (screen->rotation == 0) {
SDL_RenderCopy(screen->renderer, screen->texture, NULL, &screen->rect); SDL_RenderCopy(screen->renderer, ftex->texture, NULL,
&screen->rect);
} else { } else {
// rotation in RenderCopyEx() is clockwise, while screen->rotation is // rotation in RenderCopyEx() is clockwise, while screen->rotation is
// counterclockwise (to be consistent with --lock-video-orientation) // counterclockwise (to be consistent with --lock-video-orientation)
@ -491,10 +403,10 @@ screen_render(struct screen *screen, bool update_content_rect) {
dstrect = &screen->rect; dstrect = &screen->rect;
} }
SDL_RenderCopyEx(screen->renderer, screen->texture, NULL, dstrect, SDL_RenderCopyEx(screen->renderer, ftex->texture, NULL, dstrect,
angle, NULL, 0); angle, NULL, 0);
} }
SDL_RenderPresent(screen->renderer); SDL_RenderPresent(ftex->renderer);
} }
void void

View file

@ -8,6 +8,7 @@
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include "coords.h" #include "coords.h"
#include "frame_texture.h"
#include "opengl.h" #include "opengl.h"
struct video_buffer; struct video_buffer;
@ -15,8 +16,8 @@ struct video_buffer;
struct screen { struct screen {
SDL_Window *window; SDL_Window *window;
SDL_Renderer *renderer; SDL_Renderer *renderer;
SDL_Texture *texture; struct sc_frame_texture ftex;
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
@ -33,14 +34,12 @@ 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, \ .ftex = {0}, \
.gl = {0}, \
.frame_size = { \ .frame_size = { \
.width = 0, \ .width = 0, \
.height = 0, \ .height = 0, \
@ -65,7 +64,6 @@ struct screen {
.fullscreen = false, \ .fullscreen = false, \
.maximized = false, \ .maximized = false, \
.no_window = false, \ .no_window = false, \
.mipmaps = false, \
} }
// initialize default values // initialize default values