diff --git a/app/src/display.c b/app/src/display.c index 4852952b..07fef7b6 100644 --- a/app/src/display.c +++ b/app/src/display.c @@ -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 @@ -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; } @@ -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); } @@ -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; } @@ -139,11 +216,32 @@ 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); + bool ok = sc_display_apply_pending(display); + if (!ok) { + return SC_DISPLAY_RESULT_PENDING; + } + SDL_Renderer *renderer = display->renderer; SDL_Texture *texture = display->texture; @@ -151,7 +249,7 @@ sc_display_render(struct sc_display *display, const SDL_Rect *geometry, 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 @@ -176,10 +274,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; } diff --git a/app/src/display.h b/app/src/display.h index e30b4822..6b83a5c9 100644 --- a/app/src/display.h +++ b/app/src/display.h @@ -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 @@ -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); diff --git a/app/src/screen.c b/app/src/screen.c index 70665ed6..14fd2a00 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -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__) @@ -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 @@ -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;