From 0b4b4609dcaf448baecdbbaf1b8bf40c8b989c25 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 7 Feb 2021 19:26:58 +0100 Subject: [PATCH] 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). --- app/meson.build | 1 + app/src/frame_texture.c | 120 ++++++++++++++++++++++++++++++++++++ app/src/frame_texture.h | 33 ++++++++++ app/src/screen.c | 132 +++++++--------------------------------- app/src/screen.h | 10 ++- 5 files changed, 180 insertions(+), 116 deletions(-) create mode 100644 app/src/frame_texture.c create mode 100644 app/src/frame_texture.h diff --git a/app/meson.build b/app/meson.build index 1f50209e..6a36c2c1 100644 --- a/app/meson.build +++ b/app/meson.build @@ -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', diff --git a/app/src/frame_texture.c b/app/src/frame_texture.c new file mode 100644 index 00000000..95ead7c7 --- /dev/null +++ b/app/src/frame_texture.c @@ -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; +} diff --git a/app/src/frame_texture.h b/app/src/frame_texture.h new file mode 100644 index 00000000..626a600b --- /dev/null +++ b/app/src/frame_texture.h @@ -0,0 +1,33 @@ +#ifndef SC_FRAME_TEXTURE_H +#define SC_FRAME_TEXTURE_H + +#include "common.h" + +#include +#include +#include + +#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 diff --git a/app/src/screen.c b/app/src/screen.c index fb2eb6cd..0d3f8038 100644 --- a/app/src/screen.c +++ b/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 diff --git a/app/src/screen.h b/app/src/screen.h index 35d5df50..9a3a0d25 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -8,6 +8,7 @@ #include #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