Skip to content

Commit

Permalink
Accept texture failures
Browse files Browse the repository at this point in the history
When the scrcpy window is minimized on Windows with D3D9, texture
creation and update fail.

In that case, do not terminate scrcpy. Instead, store the pending size
or frame to update, to attempt again during the next update or
rendering.

Fixes #3947 <#3947>
  • Loading branch information
rom1v committed May 22, 2023
1 parent 7d33798 commit 6298ef0
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 21 deletions.
120 changes: 110 additions & 10 deletions app/src/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,17 @@ sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) {
LOGD("Trilinear filtering disabled (not an OpenGL renderer");
}

display->pending.flags = 0;
display->pending.frame = NULL;

return true;
}

void
sc_display_destroy(struct sc_display *display) {
if (display->pending.frame) {
av_frame_free(&display->pending.frame);
}
#ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE
SDL_GL_DeleteContext(display->gl_context);
#endif
Expand All @@ -84,7 +90,7 @@ sc_display_create_texture(struct sc_display *display,
SDL_TEXTUREACCESS_STREAMING,
size.width, size.height);
if (!texture) {
LOGE("Could not create texture: %s", SDL_GetError());
LOGD("Could not create texture: %s", SDL_GetError());
return NULL;
}

Expand All @@ -104,8 +110,66 @@ sc_display_create_texture(struct sc_display *display,
return texture;
}

bool
sc_display_set_texture_size(struct sc_display *display, struct sc_size size) {
static inline void
sc_display_set_pending_size(struct sc_display *display, struct sc_size size) {
assert(!display->texture);
display->pending.size = size;
display->pending.flags |= SC_DISPLAY_PENDING_FLAG_SIZE;
}

static bool
sc_display_set_pending_frame(struct sc_display *display, const AVFrame *frame) {
if (!display->pending.frame) {
display->pending.frame = av_frame_alloc();
if (!display->pending.frame) {
LOG_OOM();
return false;
}
}

int r = av_frame_ref(display->pending.frame, frame);
if (r) {
LOGE("Could not ref frame: %d", r);
return false;
}

display->pending.flags |= SC_DISPLAY_PENDING_FLAG_FRAME;

return true;
}

static bool
sc_display_apply_pending(struct sc_display *display) {
if (display->pending.flags & SC_DISPLAY_PENDING_FLAG_SIZE) {
assert(!display->texture);
display->texture =
sc_display_create_texture(display, display->pending.size);
if (!display->texture) {
return false;
}

display->pending.flags &= ~SC_DISPLAY_PENDING_FLAG_SIZE;
}

if (display->pending.flags & SC_DISPLAY_PENDING_FLAG_FRAME) {
assert(display->pending.frame);
bool ok = sc_display_update_texture(display, display->pending.frame);
if (!ok) {
return false;
}

av_frame_unref(display->pending.frame);
display->pending.flags &= ~SC_DISPLAY_PENDING_FLAG_FRAME;
}

return true;
}

static bool
sc_display_set_texture_size_internal(struct sc_display *display,
struct sc_size size) {
assert(size.width && size.height);

if (display->texture) {
SDL_DestroyTexture(display->texture);
}
Expand All @@ -119,14 +183,27 @@ sc_display_set_texture_size(struct sc_display *display, struct sc_size size) {
return true;
}

bool
sc_display_update_texture(struct sc_display *display, const AVFrame *frame) {
enum sc_display_result
sc_display_set_texture_size(struct sc_display *display, struct sc_size size) {
bool ok = sc_display_set_texture_size_internal(display, size);
if (!ok) {
sc_display_set_pending_size(display, size);
return SC_DISPLAY_RESULT_PENDING;

}

return SC_DISPLAY_RESULT_OK;
}

static bool
sc_display_update_texture_internal(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());
LOGD("Could not update texture: %s", SDL_GetError());
return false;
}

Expand All @@ -139,19 +216,42 @@ sc_display_update_texture(struct sc_display *display, const AVFrame *frame) {
return true;
}

bool
enum sc_display_result
sc_display_update_texture(struct sc_display *display, const AVFrame *frame) {
bool ok = sc_display_update_texture_internal(display, frame);
if (!ok) {
ok = sc_display_set_pending_frame(display, frame);
if (!ok) {
LOGE("Could not set pending frame");
return SC_DISPLAY_RESULT_ERROR;
}

return SC_DISPLAY_RESULT_PENDING;
}

return SC_DISPLAY_RESULT_OK;
}

enum sc_display_result
sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
unsigned rotation) {
SDL_RenderClear(display->renderer);

if (display->pending.flags) {
bool ok = sc_display_apply_pending(display);
if (!ok) {
return SC_DISPLAY_RESULT_PENDING;
}
}

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;
return SC_DISPLAY_RESULT_ERROR;
}
} else {
// rotation in RenderCopyEx() is clockwise, while screen->rotation is
Expand All @@ -176,10 +276,10 @@ sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
NULL, 0);
if (ret) {
LOGE("Could not render texture: %s", SDL_GetError());
return false;
return SC_DISPLAY_RESULT_ERROR;
}
}

SDL_RenderPresent(display->renderer);
return true;
return SC_DISPLAY_RESULT_OK;
}
20 changes: 17 additions & 3 deletions app/src/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ struct sc_display {
#endif

bool mipmaps;

struct {
#define SC_DISPLAY_PENDING_FLAG_SIZE 1
#define SC_DISPLAY_PENDING_FLAG_FRAME 2
int8_t flags;
struct sc_size size;
AVFrame *frame;
} pending;
};

enum sc_display_result {
SC_DISPLAY_RESULT_OK,
SC_DISPLAY_RESULT_PENDING,
SC_DISPLAY_RESULT_ERROR,
};

bool
Expand All @@ -32,13 +46,13 @@ sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps);
void
sc_display_destroy(struct sc_display *display);

bool
enum sc_display_result
sc_display_set_texture_size(struct sc_display *display, struct sc_size size);

bool
enum sc_display_result
sc_display_update_texture(struct sc_display *display, const AVFrame *frame);

bool
enum sc_display_result
sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
unsigned rotation);

Expand Down
28 changes: 20 additions & 8 deletions app/src/screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,9 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
sc_screen_update_content_rect(screen);
}

bool ok = sc_display_render(&screen->display, &screen->rect,
screen->rotation);
(void) ok; // error already logged
enum sc_display_result res =
sc_display_render(&screen->display, &screen->rect, screen->rotation);
(void) res; // any error already logged
}

#if defined(__APPLE__) || defined(__WINDOWS__)
Expand Down Expand Up @@ -583,15 +583,17 @@ sc_screen_init_size(struct sc_screen *screen) {
get_rotated_size(screen->frame_size, screen->rotation);
screen->content_size = content_size;

return sc_display_set_texture_size(&screen->display, screen->frame_size);
enum sc_display_result res =
sc_display_set_texture_size(&screen->display, screen->frame_size);
return res != SC_DISPLAY_RESULT_ERROR;
}

// recreate the texture and resize the window if the frame size has changed
static bool
static enum sc_display_result
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) {
return true;
return SC_DISPLAY_RESULT_OK;
}

// frame dimension changed
Expand All @@ -615,13 +617,23 @@ sc_screen_update_frame(struct sc_screen *screen) {
sc_fps_counter_add_rendered_frame(&screen->fps_counter);

struct sc_size new_frame_size = {frame->width, frame->height};
if (!prepare_for_frame(screen, new_frame_size)) {
enum sc_display_result res = prepare_for_frame(screen, new_frame_size);
if (res == SC_DISPLAY_RESULT_ERROR) {
return false;
}
if (res == SC_DISPLAY_RESULT_PENDING) {
// Not an error, but do not continue
return true;
}

if (!sc_display_update_texture(&screen->display, frame)) {
res = sc_display_update_texture(&screen->display, frame);
if (res == SC_DISPLAY_RESULT_ERROR) {
return false;
}
if (res == SC_DISPLAY_RESULT_PENDING) {
// Not an error, but do not continue
return true;
}

if (!screen->has_frame) {
screen->has_frame = true;
Expand Down

0 comments on commit 6298ef0

Please sign in to comment.