diff --git a/app/meson.build b/app/meson.build index 05c463e6..061fdcab 100644 --- a/app/meson.build +++ b/app/meson.build @@ -14,6 +14,7 @@ src = [ 'src/delay_buffer.c', 'src/demuxer.c', 'src/device_msg.c', + 'src/display.c', 'src/icon.c', 'src/file_pusher.c', 'src/fps_counter.c', diff --git a/app/src/display.c b/app/src/display.c new file mode 100644 index 00000000..96ff9e06 --- /dev/null +++ b/app/src/display.c @@ -0,0 +1,166 @@ +#include "display.h" + +#include + +#include "util/log.h" + +bool +sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) { + display->renderer = + SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (!display->renderer) { + LOGE("Could not create renderer: %s", SDL_GetError()); + return false; + } + + SDL_RendererInfo renderer_info; + int r = SDL_GetRendererInfo(display->renderer, &renderer_info); + const char *renderer_name = r ? NULL : renderer_info.name; + LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)"); + + display->mipmaps = false; + + // starts with "opengl" + bool use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6); + if (use_opengl) { + struct sc_opengl *gl = &display->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"); + display->mipmaps = true; + } else { + LOGW("Trilinear filtering disabled " + "(OpenGL 3.0+ or ES 2.0+ required"); + } + } else { + LOGI("Trilinear filtering disabled"); + } + } else if (mipmaps) { + LOGD("Trilinear filtering disabled (not an OpenGL renderer"); + } + + return true; +} + +void +sc_display_destroy(struct sc_display *display) { + if (display->texture) { + SDL_DestroyTexture(display->texture); + } + SDL_DestroyRenderer(display->renderer); +} + +static SDL_Texture * +sc_display_create_texture(struct sc_display *display, + struct sc_size size) { + SDL_Renderer *renderer = display->renderer; + SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, + SDL_TEXTUREACCESS_STREAMING, + size.width, size.height); + if (!texture) { + LOGE("Could not create texture: %s", SDL_GetError()); + return NULL; + } + + if (display->mipmaps) { + struct sc_opengl *gl = &display->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_display_set_texture_size(struct sc_display *display, struct sc_size size) { + if (display->texture) { + SDL_DestroyTexture(display->texture); + } + + display->texture = sc_display_create_texture(display, size); + if (!display->texture) { + return false; + } + + LOGI("Texture: %" PRIu16 "x%" PRIu16, size.width, size.height); + return true; +} + +bool +sc_display_update_texture(struct sc_display *display, const AVFrame *frame) { + int ret = SDL_UpdateYUVTexture(display->texture, NULL, + frame->data[0], frame->linesize[0], + frame->data[1], frame->linesize[1], + frame->data[2], frame->linesize[2]); + if (ret) { + LOGE("Could not update texture: %s", SDL_GetError()); + return false; + } + + if (display->mipmaps) { + SDL_GL_BindTexture(display->texture, NULL, NULL); + display->gl.GenerateMipmap(GL_TEXTURE_2D); + SDL_GL_UnbindTexture(display->texture); + } + + return true; +} + +bool +sc_display_render(struct sc_display *display, const SDL_Rect *geometry, + unsigned rotation) { + SDL_RenderClear(display->renderer); + + SDL_Renderer *renderer = display->renderer; + SDL_Texture *texture = display->texture; + + if (rotation == 0) { + int ret = SDL_RenderCopy(renderer, texture, NULL, geometry); + if (ret) { + LOGE("Could not render texture: %s", SDL_GetError()); + return false; + } + } else { + // rotation in RenderCopyEx() is clockwise, while screen->rotation is + // counterclockwise (to be consistent with --lock-video-orientation) + int cw_rotation = (4 - rotation) % 4; + double angle = 90 * cw_rotation; + + const SDL_Rect *dstrect = NULL; + SDL_Rect rect; + if (rotation & 1) { + rect.x = geometry->x + (geometry->w - geometry->h) / 2; + rect.y = geometry->y + (geometry->h - geometry->w) / 2; + rect.w = geometry->h; + rect.h = geometry->w; + dstrect = ▭ + } else { + assert(rotation == 2); + dstrect = geometry; + } + + int ret = SDL_RenderCopyEx(renderer, texture, NULL, dstrect, angle, + NULL, 0); + if (ret) { + LOGE("Could not render texture: %s", SDL_GetError()); + return false; + } + } + + SDL_RenderPresent(display->renderer); + return true; +} diff --git a/app/src/display.h b/app/src/display.h new file mode 100644 index 00000000..8856afcd --- /dev/null +++ b/app/src/display.h @@ -0,0 +1,37 @@ +#ifndef SC_DISPLAY_H +#define SC_DISPLAY_H + +#include "common.h" + +#include +#include +#include + +#include "coords.h" +#include "opengl.h" + +struct sc_display { + SDL_Renderer *renderer; + SDL_Texture *texture; + + struct sc_opengl gl; + bool mipmaps; +}; + +bool +sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps); + +void +sc_display_destroy(struct sc_display *display); + +bool +sc_display_set_texture_size(struct sc_display *display, struct sc_size size); + +bool +sc_display_update_texture(struct sc_display *display, const AVFrame *frame); + +bool +sc_display_render(struct sc_display *display, const SDL_Rect *geometry, + unsigned rotation); + +#endif diff --git a/app/src/screen.c b/app/src/screen.c index 65a4047d..70665ed6 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -239,35 +239,6 @@ sc_screen_update_content_rect(struct sc_screen *screen) { } } -static bool -create_texture(struct sc_screen *screen) { - SDL_Renderer *renderer = screen->renderer; - struct sc_size size = screen->frame_size; - SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, - SDL_TEXTUREACCESS_STREAMING, - size.width, size.height); - if (!texture) { - LOGE("Could not create texture: %s", SDL_GetError()); - return false; - } - - 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); - } - - screen->texture = texture; - return true; -} - // render the texture to the renderer // // Set the update_content_rect flag if the window or content size may have @@ -278,35 +249,11 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) { sc_screen_update_content_rect(screen); } - SDL_RenderClear(screen->renderer); - if (screen->rotation == 0) { - SDL_RenderCopy(screen->renderer, screen->texture, NULL, &screen->rect); - } else { - // rotation in RenderCopyEx() is clockwise, while screen->rotation is - // counterclockwise (to be consistent with --lock-video-orientation) - int cw_rotation = (4 - screen->rotation) % 4; - double angle = 90 * cw_rotation; - - SDL_Rect *dstrect = NULL; - SDL_Rect rect; - if (screen->rotation & 1) { - rect.x = screen->rect.x + (screen->rect.w - screen->rect.h) / 2; - rect.y = screen->rect.y + (screen->rect.h - screen->rect.w) / 2; - rect.w = screen->rect.h; - rect.h = screen->rect.w; - dstrect = ▭ - } else { - assert(screen->rotation == 2); - dstrect = &screen->rect; - } - - SDL_RenderCopyEx(screen->renderer, screen->texture, NULL, dstrect, - angle, NULL, 0); - } - SDL_RenderPresent(screen->renderer); + bool ok = sc_display_render(&screen->display, &screen->rect, + screen->rotation); + (void) ok; // error already logged } - #if defined(__APPLE__) || defined(__WINDOWS__) # define CONTINUOUS_RESIZING_WORKAROUND #endif @@ -453,46 +400,11 @@ sc_screen_init(struct sc_screen *screen, goto error_destroy_fps_counter; } - screen->renderer = SDL_CreateRenderer(screen->window, -1, - SDL_RENDERER_ACCELERATED); - if (!screen->renderer) { - LOGE("Could not create renderer: %s", SDL_GetError()); + ok = sc_display_init(&screen->display, screen->window, params->mipmaps); + if (!ok) { goto error_destroy_window; } - 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)"); - - screen->mipmaps = false; - - // 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 (params->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 if (params->mipmaps) { - LOGD("Trilinear filtering disabled (not an OpenGL renderer)"); - } - SDL_Surface *icon = scrcpy_icon_load(); if (icon) { SDL_SetWindowIcon(screen->window, icon); @@ -504,7 +416,7 @@ sc_screen_init(struct sc_screen *screen, screen->frame = av_frame_alloc(); if (!screen->frame) { LOG_OOM(); - goto error_destroy_renderer; + goto error_destroy_display; } struct sc_input_manager_params im_params = { @@ -539,8 +451,8 @@ sc_screen_init(struct sc_screen *screen, return true; -error_destroy_renderer: - SDL_DestroyRenderer(screen->renderer); +error_destroy_display: + sc_display_destroy(&screen->display); error_destroy_window: SDL_DestroyWindow(screen->window); error_destroy_fps_counter: @@ -596,11 +508,8 @@ sc_screen_destroy(struct sc_screen *screen) { #ifndef NDEBUG assert(!screen->open); #endif + sc_display_destroy(&screen->display); av_frame_free(&screen->frame); - if (screen->texture) { - SDL_DestroyTexture(screen->texture); - } - SDL_DestroyRenderer(screen->renderer); SDL_DestroyWindow(screen->window); sc_fps_counter_destroy(&screen->fps_counter); sc_frame_buffer_destroy(&screen->fb); @@ -667,7 +576,6 @@ static bool sc_screen_init_size(struct sc_screen *screen) { // Before first frame assert(!screen->has_frame); - assert(!screen->texture); // The requested size is passed via screen->frame_size @@ -675,48 +583,27 @@ sc_screen_init_size(struct sc_screen *screen) { get_rotated_size(screen->frame_size, screen->rotation); screen->content_size = content_size; - LOGI("Initial texture: %" PRIu16 "x%" PRIu16, - screen->frame_size.width, screen->frame_size.height); - return create_texture(screen); + return sc_display_set_texture_size(&screen->display, screen->frame_size); } // recreate the texture and resize the window if the frame size has changed static bool prepare_for_frame(struct sc_screen *screen, struct sc_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 sc_size new_content_size = - get_rotated_size(new_frame_size, screen->rotation); - set_content_size(screen, new_content_size); - - sc_screen_update_content_rect(screen); - - LOGI("New texture: %" PRIu16 "x%" PRIu16, - screen->frame_size.width, screen->frame_size.height); - return create_texture(screen); + if (screen->frame_size.width == new_frame_size.width + && screen->frame_size.height == new_frame_size.height) { + return true; } - return true; -} + // frame dimension changed + screen->frame_size = new_frame_size; -// write the frame into the texture -static void -update_texture(struct sc_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]); + struct sc_size new_content_size = + get_rotated_size(new_frame_size, screen->rotation); + set_content_size(screen, new_content_size); - if (screen->mipmaps) { - SDL_GL_BindTexture(screen->texture, NULL, NULL); - screen->gl.GenerateMipmap(GL_TEXTURE_2D); - SDL_GL_UnbindTexture(screen->texture); - } + sc_screen_update_content_rect(screen); + + return sc_display_set_texture_size(&screen->display, screen->frame_size); } static bool @@ -731,7 +618,10 @@ sc_screen_update_frame(struct sc_screen *screen) { if (!prepare_for_frame(screen, new_frame_size)) { return false; } - update_texture(screen, frame); + + if (!sc_display_update_texture(&screen->display, frame)) { + return false; + } if (!screen->has_frame) { screen->has_frame = true; diff --git a/app/src/screen.h b/app/src/screen.h index ffb896a7..2c032119 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -9,6 +9,7 @@ #include "controller.h" #include "coords.h" +#include "display.h" #include "fps_counter.h" #include "frame_buffer.h" #include "input_manager.h" @@ -24,6 +25,7 @@ struct sc_screen { bool open; // track the open/close state to assert correct behavior #endif + struct sc_display display; struct sc_input_manager im; struct sc_frame_buffer fb; struct sc_fps_counter fps_counter; @@ -39,9 +41,6 @@ struct sc_screen { } req; SDL_Window *window; - SDL_Renderer *renderer; - SDL_Texture *texture; - struct sc_opengl gl; struct sc_size frame_size; struct sc_size content_size; // rotated frame_size @@ -57,7 +56,6 @@ struct sc_screen { bool has_frame; bool fullscreen; bool maximized; - bool mipmaps; // To enable/disable mouse capture, a mouse capture key (LALT, LGUI or // RGUI) must be pressed. This variable tracks the pressed capture key.