From 90a46b4c45637d083e877020d85ade52a9a5fa8e Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 9 Feb 2018 13:50:54 +0100 Subject: [PATCH] Improve startup time On startup, the client has to: 1. listen on a port 2. push and start the server to the device 3. wait for the server to connect (accept) 4. read device name and size 5. initialize SDL 6. initialize the window and renderer 7. show the window From the execution of the app_process command to start the server on the device, to the execution of the java main method, it takes ~800ms. As a consequence, step 3 also takes ~800ms on the client. Once complete, the client initializes SDL, which takes ~500ms. These two expensive actions are executed sequentially: HOST DEVICE listen on port | | push/start the server |----------------->|| app_process loads the jar accept the connection . ^ || . | || . | WASTE || . | OF || . | TIME || . | || . | || . v X execution of our java main connection accepted |<-----------------| connect to the host init SDL || | || ,----------------| send frames || |,---------------| || ||,--------------| || |||,-------------| || ||||,------------| init window/renderer | |||||,-----------| display frames |<++++++-----------| (many frames skipped) The rationale for step 3 occuring before step 5 is that initializing SDL replaces the SIGTERM handler to receive the event in the event loop, so pressing Ctrl+C during step 5 would not work (since it blocks the event loop). But this is not so important; let's parallelize the SDL initialization with the app_process execution (we'll just add a timeout to the connection): HOST DEVICE listen on port | | push/start the server |----------------->||app_process loads the jar init SDL || || || || || || || || || || || || accept the connection . || . X execution of our java main connection accepted |<-----------------| connect to the host init window/renderer | | display frames |<-----------------| send frames |<-----------------| In addition, show the window only once the first frame is available to avoid flickering (opening a black window for 100~200ms). Note: the window and renderer are initialized after the connection is accepted because they use the device information received from the device. --- app/src/scrcpy.c | 15 ++++++++++----- app/src/screen.c | 11 ++++++----- app/src/screen.h | 35 +++++++++++++++++++---------------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 6a354290..29ce87b5 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -57,6 +57,11 @@ static void event_loop(void) { SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "User requested to quit"); return; case EVENT_NEW_FRAME: + if (!screen.has_frame) { + screen.has_frame = SDL_TRUE; + // this is the very first frame, show the window + screen_show_window(&screen); + } if (!screen_update_frame(&screen, &frames)) { return; } @@ -101,6 +106,11 @@ SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 max_size, Uint32 b SDL_bool ret = SDL_TRUE; + if (!sdl_init_and_configure()) { + ret = SDL_FALSE; + goto finally_destroy_server; + } + // to reduce startup time, we could be tempted to init other stuff before blocking here // but we should not block after SDL_Init since it handles the signals (Ctrl+C) in its // event loop: blocking could lead to deadlock @@ -149,11 +159,6 @@ SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 max_size, Uint32 b goto finally_destroy_controller; } - if (!sdl_init_and_configure()) { - ret = SDL_FALSE; - goto finally_stop_and_join_controller; - } - if (!screen_init_rendering(&screen, device_name, frame_size)) { ret = SDL_FALSE; goto finally_stop_and_join_controller; diff --git a/app/src/screen.c b/app/src/screen.c index b56f8861..613bdabe 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -137,7 +137,7 @@ SDL_bool screen_init_rendering(struct screen *screen, const char *device_name, s struct size window_size = get_initial_optimal_size(frame_size); screen->window = SDL_CreateWindow(device_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - window_size.width, window_size.height, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); + window_size.width, window_size.height, SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE); if (!screen->window) { SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "Could not create window: %s", SDL_GetError()); return SDL_FALSE; @@ -178,6 +178,10 @@ SDL_bool screen_init_rendering(struct screen *screen, const char *device_name, s return SDL_TRUE; } +void screen_show_window(struct screen *screen) { + SDL_ShowWindow(screen->window); +} + void screen_destroy(struct screen *screen) { if (screen->texture) { SDL_DestroyTexture(screen->texture); @@ -231,7 +235,6 @@ static SDL_bool prepare_for_frame(struct screen *screen, struct size new_frame_s // write the frame into the texture static void update_texture(struct screen *screen, const AVFrame *frame) { - screen->texture_initialized = SDL_TRUE; SDL_UpdateYUVTexture(screen->texture, NULL, frame->data[0], frame->linesize[0], frame->data[1], frame->linesize[1], @@ -255,9 +258,7 @@ SDL_bool screen_update_frame(struct screen *screen, struct frames *frames) { void screen_render(struct screen *screen) { SDL_RenderClear(screen->renderer); - if (screen->texture_initialized) { - SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL); - } + SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL); SDL_RenderPresent(screen->renderer); } diff --git a/app/src/screen.h b/app/src/screen.h index 1dc31904..13103eaa 100644 --- a/app/src/screen.h +++ b/app/src/screen.h @@ -14,24 +14,24 @@ struct screen { struct size frame_size; //used only in fullscreen mode to know the windowed window size struct size windowed_window_size; - SDL_bool texture_initialized; + SDL_bool has_frame; SDL_bool fullscreen; }; -#define SCREEN_INITIALIZER { \ - .window = NULL, \ - .renderer = NULL, \ - .texture = NULL, \ - .frame_size = { \ - .width = 0, \ - .height = 0, \ - }, \ - .windowed_window_size = { \ - .width = 0, \ - .height = 0, \ - }, \ - .texture_initialized = SDL_FALSE, \ - .fullscreen = SDL_FALSE, \ +#define SCREEN_INITIALIZER { \ + .window = NULL, \ + .renderer = NULL, \ + .texture = NULL, \ + .frame_size = { \ + .width = 0, \ + .height = 0, \ + }, \ + .windowed_window_size = { \ + .width = 0, \ + .height = 0, \ + }, \ + .has_frame = SDL_FALSE, \ + .fullscreen = SDL_FALSE, \ } // init SDL and set appropriate hints @@ -40,11 +40,14 @@ SDL_bool sdl_init_and_configure(void); // initialize default values void screen_init(struct screen *screen); -// initialize screen, create window, renderer and texture +// initialize screen, create window, renderer and texture (window is hidden) SDL_bool screen_init_rendering(struct screen *screen, const char *device_name, struct size frame_size); +// show the window +void screen_show_window(struct screen *screen); + // destroy window, renderer and texture (if any) void screen_destroy(struct screen *screen);