Skip to content

Commit 90a46b4

Browse files
committed
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.
1 parent 063cfd1 commit 90a46b4

File tree

3 files changed

+35
-26
lines changed

3 files changed

+35
-26
lines changed

app/src/scrcpy.c

+10-5
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ static void event_loop(void) {
5757
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "User requested to quit");
5858
return;
5959
case EVENT_NEW_FRAME:
60+
if (!screen.has_frame) {
61+
screen.has_frame = SDL_TRUE;
62+
// this is the very first frame, show the window
63+
screen_show_window(&screen);
64+
}
6065
if (!screen_update_frame(&screen, &frames)) {
6166
return;
6267
}
@@ -101,6 +106,11 @@ SDL_bool scrcpy(const char *serial, Uint16 local_port, Uint16 max_size, Uint32 b
101106

102107
SDL_bool ret = SDL_TRUE;
103108

109+
if (!sdl_init_and_configure()) {
110+
ret = SDL_FALSE;
111+
goto finally_destroy_server;
112+
}
113+
104114
// to reduce startup time, we could be tempted to init other stuff before blocking here
105115
// but we should not block after SDL_Init since it handles the signals (Ctrl+C) in its
106116
// 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
149159
goto finally_destroy_controller;
150160
}
151161

152-
if (!sdl_init_and_configure()) {
153-
ret = SDL_FALSE;
154-
goto finally_stop_and_join_controller;
155-
}
156-
157162
if (!screen_init_rendering(&screen, device_name, frame_size)) {
158163
ret = SDL_FALSE;
159164
goto finally_stop_and_join_controller;

app/src/screen.c

+6-5
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ SDL_bool screen_init_rendering(struct screen *screen, const char *device_name, s
137137

138138
struct size window_size = get_initial_optimal_size(frame_size);
139139
screen->window = SDL_CreateWindow(device_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
140-
window_size.width, window_size.height, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
140+
window_size.width, window_size.height, SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE);
141141
if (!screen->window) {
142142
SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "Could not create window: %s", SDL_GetError());
143143
return SDL_FALSE;
@@ -178,6 +178,10 @@ SDL_bool screen_init_rendering(struct screen *screen, const char *device_name, s
178178
return SDL_TRUE;
179179
}
180180

181+
void screen_show_window(struct screen *screen) {
182+
SDL_ShowWindow(screen->window);
183+
}
184+
181185
void screen_destroy(struct screen *screen) {
182186
if (screen->texture) {
183187
SDL_DestroyTexture(screen->texture);
@@ -231,7 +235,6 @@ static SDL_bool prepare_for_frame(struct screen *screen, struct size new_frame_s
231235

232236
// write the frame into the texture
233237
static void update_texture(struct screen *screen, const AVFrame *frame) {
234-
screen->texture_initialized = SDL_TRUE;
235238
SDL_UpdateYUVTexture(screen->texture, NULL,
236239
frame->data[0], frame->linesize[0],
237240
frame->data[1], frame->linesize[1],
@@ -255,9 +258,7 @@ SDL_bool screen_update_frame(struct screen *screen, struct frames *frames) {
255258

256259
void screen_render(struct screen *screen) {
257260
SDL_RenderClear(screen->renderer);
258-
if (screen->texture_initialized) {
259-
SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL);
260-
}
261+
SDL_RenderCopy(screen->renderer, screen->texture, NULL, NULL);
261262
SDL_RenderPresent(screen->renderer);
262263
}
263264

app/src/screen.h

+19-16
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,24 @@ struct screen {
1414
struct size frame_size;
1515
//used only in fullscreen mode to know the windowed window size
1616
struct size windowed_window_size;
17-
SDL_bool texture_initialized;
17+
SDL_bool has_frame;
1818
SDL_bool fullscreen;
1919
};
2020

21-
#define SCREEN_INITIALIZER { \
22-
.window = NULL, \
23-
.renderer = NULL, \
24-
.texture = NULL, \
25-
.frame_size = { \
26-
.width = 0, \
27-
.height = 0, \
28-
}, \
29-
.windowed_window_size = { \
30-
.width = 0, \
31-
.height = 0, \
32-
}, \
33-
.texture_initialized = SDL_FALSE, \
34-
.fullscreen = SDL_FALSE, \
21+
#define SCREEN_INITIALIZER { \
22+
.window = NULL, \
23+
.renderer = NULL, \
24+
.texture = NULL, \
25+
.frame_size = { \
26+
.width = 0, \
27+
.height = 0, \
28+
}, \
29+
.windowed_window_size = { \
30+
.width = 0, \
31+
.height = 0, \
32+
}, \
33+
.has_frame = SDL_FALSE, \
34+
.fullscreen = SDL_FALSE, \
3535
}
3636

3737
// init SDL and set appropriate hints
@@ -40,11 +40,14 @@ SDL_bool sdl_init_and_configure(void);
4040
// initialize default values
4141
void screen_init(struct screen *screen);
4242

43-
// initialize screen, create window, renderer and texture
43+
// initialize screen, create window, renderer and texture (window is hidden)
4444
SDL_bool screen_init_rendering(struct screen *screen,
4545
const char *device_name,
4646
struct size frame_size);
4747

48+
// show the window
49+
void screen_show_window(struct screen *screen);
50+
4851
// destroy window, renderer and texture (if any)
4952
void screen_destroy(struct screen *screen);
5053

0 commit comments

Comments
 (0)