6298ef095f
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 <https://github.com/Genymobile/scrcpy/issues/3947>
285 lines
8.5 KiB
C
285 lines
8.5 KiB
C
#include "display.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include "util/log.h"
|
|
|
|
bool
|
|
sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) {
|
|
display->renderer =
|
|
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
|
if (!display->renderer) {
|
|
LOGE("Could not create renderer: %s", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
SDL_RendererInfo renderer_info;
|
|
int r = SDL_GetRendererInfo(display->renderer, &renderer_info);
|
|
const char *renderer_name = r ? NULL : renderer_info.name;
|
|
LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)");
|
|
|
|
display->mipmaps = false;
|
|
|
|
// starts with "opengl"
|
|
bool use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
|
|
if (use_opengl) {
|
|
|
|
#ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE
|
|
// Persuade macOS to give us something better than OpenGL 2.1.
|
|
// If we create a Core Profile context, we get the best OpenGL version.
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
|
|
SDL_GL_CONTEXT_PROFILE_CORE);
|
|
|
|
LOGD("Creating OpenGL Core Profile context");
|
|
display->gl_context = SDL_GL_CreateContext(window);
|
|
if (!display->gl_context) {
|
|
LOGE("Could not create OpenGL context: %s", SDL_GetError());
|
|
SDL_DestroyRenderer(display->renderer);
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
struct sc_opengl *gl = &display->gl;
|
|
sc_opengl_init(gl);
|
|
|
|
LOGI("OpenGL version: %s", gl->version);
|
|
|
|
if (mipmaps) {
|
|
bool supports_mipmaps =
|
|
sc_opengl_version_at_least(gl, 3, 0, /* OpenGL 3.0+ */
|
|
2, 0 /* OpenGL ES 2.0+ */);
|
|
if (supports_mipmaps) {
|
|
LOGI("Trilinear filtering enabled");
|
|
display->mipmaps = true;
|
|
} else {
|
|
LOGW("Trilinear filtering disabled "
|
|
"(OpenGL 3.0+ or ES 2.0+ required");
|
|
}
|
|
} else {
|
|
LOGI("Trilinear filtering disabled");
|
|
}
|
|
} else if (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
|
|
if (display->texture) {
|
|
SDL_DestroyTexture(display->texture);
|
|
}
|
|
SDL_DestroyRenderer(display->renderer);
|
|
}
|
|
|
|
static SDL_Texture *
|
|
sc_display_create_texture(struct sc_display *display,
|
|
struct sc_size size) {
|
|
SDL_Renderer *renderer = display->renderer;
|
|
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
|
|
SDL_TEXTUREACCESS_STREAMING,
|
|
size.width, size.height);
|
|
if (!texture) {
|
|
LOGD("Could not create texture: %s", SDL_GetError());
|
|
return NULL;
|
|
}
|
|
|
|
if (display->mipmaps) {
|
|
struct sc_opengl *gl = &display->gl;
|
|
|
|
SDL_GL_BindTexture(texture, NULL, NULL);
|
|
|
|
// Enable trilinear filtering for downscaling
|
|
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR_MIPMAP_LINEAR);
|
|
gl->TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -1.f);
|
|
|
|
SDL_GL_UnbindTexture(texture);
|
|
}
|
|
|
|
return texture;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
display->texture = sc_display_create_texture(display, size);
|
|
if (!display->texture) {
|
|
return false;
|
|
}
|
|
|
|
LOGI("Texture: %" PRIu16 "x%" PRIu16, size.width, size.height);
|
|
return true;
|
|
}
|
|
|
|
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) {
|
|
LOGD("Could not update texture: %s", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
if (display->mipmaps) {
|
|
SDL_GL_BindTexture(display->texture, NULL, NULL);
|
|
display->gl.GenerateMipmap(GL_TEXTURE_2D);
|
|
SDL_GL_UnbindTexture(display->texture);
|
|
}
|
|
|
|
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
|
|
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 SC_DISPLAY_RESULT_ERROR;
|
|
}
|
|
} else {
|
|
// rotation in RenderCopyEx() is clockwise, while screen->rotation is
|
|
// counterclockwise (to be consistent with --lock-video-orientation)
|
|
int cw_rotation = (4 - rotation) % 4;
|
|
double angle = 90 * cw_rotation;
|
|
|
|
const SDL_Rect *dstrect = NULL;
|
|
SDL_Rect rect;
|
|
if (rotation & 1) {
|
|
rect.x = geometry->x + (geometry->w - geometry->h) / 2;
|
|
rect.y = geometry->y + (geometry->h - geometry->w) / 2;
|
|
rect.w = geometry->h;
|
|
rect.h = geometry->w;
|
|
dstrect = ▭
|
|
} else {
|
|
assert(rotation == 2);
|
|
dstrect = geometry;
|
|
}
|
|
|
|
int ret = SDL_RenderCopyEx(renderer, texture, NULL, dstrect, angle,
|
|
NULL, 0);
|
|
if (ret) {
|
|
LOGE("Could not render texture: %s", SDL_GetError());
|
|
return SC_DISPLAY_RESULT_ERROR;
|
|
}
|
|
}
|
|
|
|
SDL_RenderPresent(display->renderer);
|
|
return SC_DISPLAY_RESULT_OK;
|
|
}
|