Simplify texture failure handling

When the scrcpy window is minimized on Windows with D3D9, texture
creation and updates fail.

As a workaround, a mechanism was implemented to reattempt applying the
requested changes.

Since SDL3 defaults to the D3D11 backend, remove this workaround,
which adds a lot of complexity for a backend that should almost never
be used.

However, do not close scrcpy when texture creation or updates fail; only
that specific rendering should fail.

Refs SDL/#7651 <https://github.com/libsdl-org/SDL/issues/7651>
Refs #3947 <https://github.com/Genymobile/scrcpy/issues/3947>
Refs 6298ef095f
This commit is contained in:
Romain Vimont
2026-01-23 21:51:13 +01:00
parent 07f056353b
commit c3583a89eb
3 changed files with 43 additions and 177 deletions

View File

@@ -96,8 +96,6 @@ sc_display_init(struct sc_display *display, SDL_Window *window,
}
display->texture = NULL;
display->pending.flags = 0;
display->pending.frame = NULL;
if (icon_novideo) {
// Without video, set a static scrcpy icon as window content
@@ -116,9 +114,6 @@ sc_display_init(struct sc_display *display, SDL_Window *window,
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_DestroyContext(display->gl_context);
#endif
@@ -228,79 +223,10 @@ sc_display_create_texture(struct sc_display *display,
return texture;
}
static inline void
sc_display_set_pending_texture(struct sc_display *display,
struct sc_size size,
enum AVColorRange color_range) {
assert(!display->texture);
display->pending.texture.size = size;
display->pending.texture.color_range = color_range;
display->pending.flags |= SC_DISPLAY_PENDING_FLAG_TEXTURE;
}
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;
}
}
av_frame_unref(display->pending.frame);
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;
}
// Forward declaration
static bool
sc_display_update_texture_internal(struct sc_display *display,
const AVFrame *frame);
static bool
sc_display_apply_pending(struct sc_display *display) {
if (display->pending.flags & SC_DISPLAY_PENDING_FLAG_TEXTURE) {
assert(!display->texture);
display->texture =
sc_display_create_texture(display,
display->pending.texture.size,
display->pending.texture.color_space,
display->pending.texture.color_range);
if (!display->texture) {
return false;
}
display->pending.flags &= ~SC_DISPLAY_PENDING_FLAG_TEXTURE;
}
if (display->pending.flags & SC_DISPLAY_PENDING_FLAG_FRAME) {
assert(display->pending.frame);
bool ok = sc_display_update_texture_internal(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_prepare_texture_internal(struct sc_display *display,
struct sc_size size,
enum AVColorSpace color_space,
enum AVColorRange color_range) {
bool
sc_display_prepare_texture(struct sc_display *display, struct sc_size size,
enum AVColorSpace color_space,
enum AVColorRange color_range) {
assert(size.width && size.height);
if (display->texture) {
@@ -317,24 +243,9 @@ sc_display_prepare_texture_internal(struct sc_display *display,
return true;
}
enum sc_display_result
sc_display_prepare_texture(struct sc_display *display, struct sc_size size,
enum AVColorSpace color_space,
enum AVColorRange color_range) {
bool ok = sc_display_prepare_texture_internal(display, size, color_space,
color_range);
if (!ok) {
sc_display_set_pending_texture(display, size, color_range);
return SC_DISPLAY_RESULT_PENDING;
}
return SC_DISPLAY_RESULT_OK;
}
static bool
sc_display_update_texture_internal(struct sc_display *display,
const AVFrame *frame) {
bool
sc_display_update_texture(struct sc_display *display, const AVFrame *frame) {
assert(display->texture);
bool ok = SDL_UpdateYUVTexture(display->texture, NULL,
frame->data[0], frame->linesize[0],
frame->data[1], frame->linesize[1],
@@ -356,36 +267,18 @@ sc_display_update_texture_internal(struct sc_display *display,
return true;
}
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
bool
sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
enum sc_orientation orientation) {
sc_sdl_render_clear(display->renderer);
if (display->pending.flags) {
bool ok = sc_display_apply_pending(display);
if (!ok) {
return SC_DISPLAY_RESULT_PENDING;
}
}
bool ok = false;
SDL_Renderer *renderer = display->renderer;
SDL_Texture *texture = display->texture;
if (!texture) {
LOGW("No texture to render");
goto end;
}
if (orientation == SC_ORIENTATION_0) {
SDL_FRect frect;
@@ -394,11 +287,7 @@ sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
SDL_RectToFRect(geometry, &frect);
fgeometry = &frect;
}
bool ok = SDL_RenderTexture(renderer, texture, NULL, fgeometry);
if (!ok) {
LOGE("Could not render texture: %s", SDL_GetError());
return SC_DISPLAY_RESULT_ERROR;
}
ok = SDL_RenderTexture(renderer, texture, NULL, fgeometry);
} else {
unsigned cw_rotation = sc_orientation_get_rotation(orientation);
double angle = 90 * cw_rotation;
@@ -416,14 +305,15 @@ sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
SDL_FlipMode flip = sc_orientation_is_mirror(orientation)
? SDL_FLIP_HORIZONTAL : 0;
bool ok = SDL_RenderTextureRotated(renderer, texture, NULL, &frect,
angle, NULL, flip);
if (!ok) {
LOGE("Could not render texture: %s", SDL_GetError());
return SC_DISPLAY_RESULT_ERROR;
}
ok = SDL_RenderTextureRotated(renderer, texture, NULL, &frect, angle,
NULL, flip);
}
if (!ok) {
LOGE("Could not render texture: %s", SDL_GetError());
}
end:
sc_sdl_render_present(display->renderer);
return SC_DISPLAY_RESULT_OK;
return ok;
}

View File

@@ -27,24 +27,6 @@ struct sc_display {
bool mipmaps;
uint32_t texture_id; // only set if mipmaps is enabled
struct {
#define SC_DISPLAY_PENDING_FLAG_TEXTURE 1
#define SC_DISPLAY_PENDING_FLAG_FRAME 2
int8_t flags;
struct {
struct sc_size size;
enum AVColorSpace color_space;
enum AVColorRange color_range;
} texture;
AVFrame *frame;
} pending;
};
enum sc_display_result {
SC_DISPLAY_RESULT_OK,
SC_DISPLAY_RESULT_PENDING,
SC_DISPLAY_RESULT_ERROR,
};
bool
@@ -54,15 +36,15 @@ sc_display_init(struct sc_display *display, SDL_Window *window,
void
sc_display_destroy(struct sc_display *display);
enum sc_display_result
bool
sc_display_prepare_texture(struct sc_display *display, struct sc_size size,
enum AVColorSpace color_space,
enum AVColorRange color_range);
enum sc_display_result
bool
sc_display_update_texture(struct sc_display *display, const AVFrame *frame);
enum sc_display_result
bool
sc_display_render(struct sc_display *display, const SDL_Rect *geometry,
enum sc_orientation orientation);

View File

@@ -192,16 +192,16 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
sc_screen_update_content_rect(screen);
}
enum sc_display_result res =
bool ok =
sc_display_render(&screen->display, &screen->rect, screen->orientation);
(void) res; // any error already logged
(void) ok; // any error already logged
}
static void
sc_screen_render_novideo(struct sc_screen *screen) {
enum sc_display_result res =
bool ok =
sc_display_render(&screen->display, NULL, SC_ORIENTATION_0);
(void) res; // any error already logged
(void) ok; // any error already logged
}
#if defined(__APPLE__) || defined(_WIN32)
@@ -606,6 +606,14 @@ sc_screen_apply_frame(struct sc_screen *screen) {
if (!screen->has_frame
|| screen->frame_size.width != new_frame_size.width
|| screen->frame_size.height != new_frame_size.height) {
bool ok =
sc_display_prepare_texture(&screen->display, new_frame_size,
frame->colorspace, frame->color_range);
if (!ok) {
return false;
}
// frame dimension changed
screen->frame_size = new_frame_size;
@@ -619,28 +627,12 @@ sc_screen_apply_frame(struct sc_screen *screen) {
screen->has_frame = true;
screen->content_size = new_content_size;
}
enum sc_display_result res =
sc_display_prepare_texture(&screen->display, screen->frame_size,
frame->colorspace, frame->color_range);
if (res == SC_DISPLAY_RESULT_ERROR) {
return false;
}
if (res == SC_DISPLAY_RESULT_PENDING) {
// Not an error, but do not continue
return true;
}
}
enum sc_display_result res =
sc_display_update_texture(&screen->display, frame);
if (res == SC_DISPLAY_RESULT_ERROR) {
bool ok = sc_display_update_texture(&screen->display, frame);
if (!ok) {
return false;
}
if (res == SC_DISPLAY_RESULT_PENDING) {
// Not an error, but do not continue
return true;
}
assert(screen->has_frame);
if (!screen->has_video_window) {
@@ -696,7 +688,10 @@ sc_screen_set_paused(struct sc_screen *screen, bool paused) {
av_frame_free(&screen->frame);
screen->frame = screen->resume_frame;
screen->resume_frame = NULL;
sc_screen_apply_frame(screen);
bool ok = sc_screen_apply_frame(screen);
if (!ok) {
LOGE("Resume frame update failed");
}
}
if (!paused) {
@@ -778,7 +773,6 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
bool ok = sc_screen_update_frame(screen);
if (!ok) {
LOGE("Frame update failed\n");
return false;
}
return true;
}