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
parent edf710af3c
commit 0b4b4609dc
5 changed files with 180 additions and 116 deletions

View file

@ -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
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;
}
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

View file

@ -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