diff options
Diffstat (limited to 'video/out')
-rw-r--r-- | video/out/gpu/ra.h | 12 | ||||
-rw-r--r-- | video/out/gpu/video.c | 80 | ||||
-rw-r--r-- | video/out/gpu/video.h | 4 | ||||
-rw-r--r-- | video/out/opengl/context.c | 23 | ||||
-rw-r--r-- | video/out/opengl/ra_gl.c | 27 | ||||
-rw-r--r-- | video/out/opengl/utils.c | 22 | ||||
-rw-r--r-- | video/out/opengl/utils.h | 3 | ||||
-rw-r--r-- | video/out/vo.c | 9 | ||||
-rw-r--r-- | video/out/vo.h | 11 | ||||
-rw-r--r-- | video/out/vo_gpu.c | 14 |
10 files changed, 180 insertions, 25 deletions
diff --git a/video/out/gpu/ra.h b/video/out/gpu/ra.h index f1037e45c3..05a7c54a66 100644 --- a/video/out/gpu/ra.h +++ b/video/out/gpu/ra.h @@ -110,6 +110,7 @@ struct ra_tex_params { bool blit_src; // must be usable as a blit source bool blit_dst; // must be usable as a blit destination bool host_mutable; // texture may be updated with tex_upload + bool downloadable; // texture can be read with tex_download // When used as render source texture. bool src_linear; // if false, use nearest sampling (whether this can // be true depends on ra_format.linear_filter) @@ -151,6 +152,13 @@ struct ra_tex_upload_params { ptrdiff_t stride; // The size of a horizontal line in bytes (*not* texels!) }; +struct ra_tex_download_params { + struct ra_tex *tex; // Texture to download from + // Downloading directly (set by caller, data written to by callee): + void *dst; // Address of data (packed with no alignment) + ptrdiff_t stride; // The size of a horizontal line in bytes (*not* texels!) +}; + // Buffer usage type. This restricts what types of operations may be performed // on a buffer. enum ra_buf_type { @@ -379,6 +387,10 @@ struct ra_fns { // Returns whether successful. bool (*tex_upload)(struct ra *ra, const struct ra_tex_upload_params *params); + // Copy data from the texture to memory. ra_tex_params.downloadable must + // have been set to true on texture creation. + bool (*tex_download)(struct ra *ra, struct ra_tex_download_params *params); + // Create a buffer. This can be used as a persistently mapped buffer, // a uniform buffer, a shader storage buffer or possibly others. // Not all usage types must be supported; may return NULL if unavailable. diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c index dcacdb1d01..6edf4b0ccf 100644 --- a/video/out/gpu/video.c +++ b/video/out/gpu/video.c @@ -2852,7 +2852,7 @@ static bool update_surface(struct gl_video *p, struct mp_image *mpi, // Draws an interpolate frame to fbo, based on the frame timing in t // flags: bit set of RENDER_FRAME_* flags static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, - struct ra_fbo fbo, flags) + struct ra_fbo fbo, int flags) { bool is_new = false; @@ -3156,6 +3156,84 @@ done: pass_report_performance(p); } +void gl_video_screenshot(struct gl_video *p, struct vo_frame *frame, + struct voctrl_screenshot *args) +{ + bool ok = false; + struct mp_image *res = NULL; + + if (!p->ra->fns->tex_download) + return; + + struct mp_rect old_src = p->src_rect; + struct mp_rect old_dst = p->dst_rect; + struct mp_osd_res old_osd = p->osd_rect; + + if (!args->scaled) { + int w = p->real_image_params.w; + int h = p->real_image_params.h; + if (w < 1 || h < 1) + return; + + struct mp_rect rc = {0, 0, w, h}; + struct mp_osd_res osd = {.w = w, .h = h, .display_par = 1.0}; + gl_video_resize(p, &rc, &rc, &osd); + } + + gl_video_reset_surfaces(p); + + struct ra_tex_params params = { + .dimensions = 2, + .downloadable = true, + .w = p->osd_rect.w, + .h = p->osd_rect.h, + .render_dst = true, + }; + + params.format = ra_find_unorm_format(p->ra, 1, 4); + int mpfmt = IMGFMT_RGB0; + if (args->high_bit_depth && p->ra_format.component_bits > 8) { + const struct ra_format *fmt = ra_find_unorm_format(p->ra, 2, 4); + if (fmt && fmt->renderable) { + params.format = fmt; + mpfmt = IMGFMT_RGBA64; + } + } + + if (!params.format || !params.format->renderable) + goto done; + struct ra_tex *target = ra_tex_create(p->ra, ¶ms); + if (!target) + goto done; + + int flags = 0; + if (args->subs) + flags |= RENDER_FRAME_SUBS; + if (args->osd) + flags |= RENDER_FRAME_OSD; + gl_video_render_frame(p, frame, (struct ra_fbo){target}, flags); + + res = mp_image_alloc(mpfmt, params.w, params.h); + if (!res) + goto done; + + struct ra_tex_download_params download_params = { + .tex = target, + .dst = res->planes[0], + .stride = res->stride[0], + }; + if (!p->ra->fns->tex_download(p->ra, &download_params)) + goto done; + + ok = true; +done: + ra_tex_free(p->ra, &target); + gl_video_resize(p, &old_src, &old_dst, &old_osd); + if (!ok) + TA_FREEP(&res); + args->res = res; +} + // Use this color instead of the global option. void gl_video_set_clear_color(struct gl_video *p, struct m_color c) { diff --git a/video/out/gpu/video.h b/video/out/gpu/video.h index cd17308810..80f31c2934 100644 --- a/video/out/gpu/video.h +++ b/video/out/gpu/video.h @@ -146,6 +146,7 @@ extern const struct m_sub_options gl_video_conf; struct gl_video; struct vo_frame; +struct voctrl_screenshot; enum { RENDER_FRAME_SUBS = 1 << 0, @@ -172,6 +173,9 @@ void gl_video_set_osd_pts(struct gl_video *p, double pts); bool gl_video_check_osd_change(struct gl_video *p, struct mp_osd_res *osd, double pts); +void gl_video_screenshot(struct gl_video *p, struct vo_frame *frame, + struct voctrl_screenshot *args); + float gl_video_scale_ambient_lux(float lmin, float lmax, float rmin, float rmax, float lux); void gl_video_set_ambient_lux(struct gl_video *p, int lux); diff --git a/video/out/opengl/context.c b/video/out/opengl/context.c index cdaf6320cd..8f44119210 100644 --- a/video/out/opengl/context.c +++ b/video/out/opengl/context.c @@ -251,16 +251,21 @@ struct mp_image *ra_gl_ctx_screenshot(struct ra_swapchain *sw) { struct priv *p = sw->priv; + struct mp_image *screen = mp_image_alloc(IMGFMT_RGB24, p->wrapped_fb->params.w, + p->wrapped_fb->params.h); + if (!screen) + return NULL; + + int dir = p->params.flipped ? 1 : -1; + assert(p->wrapped_fb); - struct mp_image *screen = gl_read_fbo_contents(p->gl, p->main_fb, - p->wrapped_fb->params.w, - p->wrapped_fb->params.h); - - // OpenGL FB is also read in flipped order, so we need to flip when the - // rendering is *not* flipped, which in our case is whenever - // p->params.flipped is true. I hope that made sense - if (screen && p->params.flipped) - mp_image_vflip(screen); + if (!gl_read_fbo_contents(p->gl, p->main_fb, dir, GL_RGB, GL_UNSIGNED_BYTE, + p->wrapped_fb->params.w, p->wrapped_fb->params.h, + screen->planes[0], screen->stride[0])) + { + talloc_free(screen); + return NULL; + } return screen; } diff --git a/video/out/opengl/ra_gl.c b/video/out/opengl/ra_gl.c index 356ed81d03..7112464e87 100644 --- a/video/out/opengl/ra_gl.c +++ b/video/out/opengl/ra_gl.c @@ -277,6 +277,13 @@ static struct ra_tex *gl_tex_create_blank(struct ra *ra, tex_gl->target = GL_TEXTURE_EXTERNAL_OES; } + if (params->downloadable && !(params->dimensions == 2 && + params->format->renderable)) + { + gl_tex_destroy(ra, tex); + return NULL; + } + return tex; } @@ -329,8 +336,11 @@ static struct ra_tex *gl_tex_create(struct ra *ra, gl_check_error(gl, ra->log, "after creating texture"); - // Even blitting needs an FBO in OpenGL for strange reasons - if (tex->params.render_dst || tex->params.blit_src || tex->params.blit_dst) { + // Even blitting needs an FBO in OpenGL for strange reasons. + // Download is handled by reading from an FBO. + if (tex->params.render_dst || tex->params.blit_src || + tex->params.blit_dst || tex->params.downloadable) + { if (!tex->params.format->renderable) { MP_ERR(ra, "Trying to create renderable texture with unsupported " "format.\n"); @@ -512,6 +522,18 @@ static bool gl_tex_upload(struct ra *ra, return true; } +static bool gl_tex_download(struct ra *ra, struct ra_tex_download_params *params) +{ + GL *gl = ra_gl_get(ra); + struct ra_tex *tex = params->tex; + struct ra_tex_gl *tex_gl = tex->priv; + if (!tex_gl->fbo) + return false; + return gl_read_fbo_contents(gl, tex_gl->fbo, 1, tex_gl->format, tex_gl->type, + tex->params.w, tex->params.h, params->dst, + params->stride); +} + static void gl_buf_destroy(struct ra *ra, struct ra_buf *buf) { if (!buf) @@ -1134,6 +1156,7 @@ static struct ra_fns ra_fns_gl = { .tex_create = gl_tex_create, .tex_destroy = gl_tex_destroy, .tex_upload = gl_tex_upload, + .tex_download = gl_tex_download, .buf_create = gl_buf_create, .buf_destroy = gl_buf_destroy, .buf_update = gl_buf_update, diff --git a/video/out/opengl/utils.c b/video/out/opengl/utils.c index 34f4736705..a551ce4299 100644 --- a/video/out/opengl/utils.c +++ b/video/out/opengl/utils.c @@ -105,25 +105,23 @@ void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type, gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4); } -mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h) +bool gl_read_fbo_contents(GL *gl, int fbo, int dir, GLenum format, GLenum type, + int w, int h, uint8_t *dst, int dst_stride) { - if (gl->es) - return NULL; // ES can't read from front buffer - mp_image_t *image = mp_image_alloc(IMGFMT_RGB24, w, h); - if (!image) - return NULL; + assert(dir == 1 || dir == -1); + if (fbo == 0 && gl->es) + return false; // ES can't read from front buffer gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); GLenum obj = fbo ? GL_COLOR_ATTACHMENT0 : GL_FRONT; gl->PixelStorei(GL_PACK_ALIGNMENT, 1); gl->ReadBuffer(obj); - //flip image while reading (and also avoid stride-related trouble) - for (int y = 0; y < h; y++) { - gl->ReadPixels(0, h - y - 1, w, 1, GL_RGB, GL_UNSIGNED_BYTE, - image->planes[0] + y * image->stride[0]); - } + // reading by line allows flipping, and avoids stride-related trouble + int y1 = dir > 0 ? 0 : h; + for (int y = 0; y < h; y++) + gl->ReadPixels(0, y, w, 1, format, type, dst + (y1 + dir * y) * dst_stride); gl->PixelStorei(GL_PACK_ALIGNMENT, 4); gl->BindFramebuffer(GL_FRAMEBUFFER, 0); - return image; + return true; } static void gl_vao_enable_attribs(struct gl_vao *vao) diff --git a/video/out/opengl/utils.h b/video/out/opengl/utils.h index 53127e479e..9bcadae91c 100644 --- a/video/out/opengl/utils.h +++ b/video/out/opengl/utils.h @@ -32,7 +32,8 @@ void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type, const void *dataptr, int stride, int x, int y, int w, int h); -mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h); +bool gl_read_fbo_contents(GL *gl, int fbo, int dir, GLenum format, GLenum type, + int w, int h, uint8_t *dst, int dst_stride); struct gl_vao { GL *gl; diff --git a/video/out/vo.c b/video/out/vo.c index 562c6b048a..c999138eee 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -1324,6 +1324,15 @@ struct mp_image *vo_get_current_frame(struct vo *vo) return r; } +struct vo_frame *vo_get_current_vo_frame(struct vo *vo) +{ + struct vo_internal *in = vo->in; + pthread_mutex_lock(&in->lock); + struct vo_frame *r = vo_frame_ref(vo->in->current_frame); + pthread_mutex_unlock(&in->lock); + return r; +} + static void destroy_frame(void *p) { struct vo_frame *frame = p; diff --git a/video/out/vo.h b/video/out/vo.h index bf5995f722..4ce2cf1371 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -102,8 +102,13 @@ enum mp_voctrl { VOCTRL_GET_DISPLAY_NAMES, // Retrieve window contents. (Normal screenshots use vo_get_current_frame().) + // Deprecated for VOCTRL_SCREENSHOT with corresponding flags. VOCTRL_SCREENSHOT_WIN, // struct mp_image** + // A normal screenshot - VOs can react to this if vo_get_current_frame() is + // not sufficient. + VOCTRL_SCREENSHOT, // struct voctrl_screenshot* + VOCTRL_UPDATE_RENDER_OPTS, VOCTRL_GET_ICC_PROFILE, // bstr* @@ -170,6 +175,11 @@ struct voctrl_performance_data { struct mp_frame_perf fresh, redraw; }; +struct voctrl_screenshot { + bool scaled, subs, osd, high_bit_depth; + struct mp_image *res; +}; + enum { // VO does handle mp_image_params.rotate in 90 degree steps VO_CAP_ROTATE90 = 1 << 0, @@ -447,6 +457,7 @@ double vo_get_estimated_vsync_jitter(struct vo *vo); double vo_get_display_fps(struct vo *vo); double vo_get_delay(struct vo *vo); void vo_discard_timing_info(struct vo *vo); +struct vo_frame *vo_get_current_vo_frame(struct vo *vo); struct mp_image *vo_get_image(struct vo *vo, int imgfmt, int w, int h, int stride_align); diff --git a/video/out/vo_gpu.c b/video/out/vo_gpu.c index d3b27ca5bc..70c874fc54 100644 --- a/video/out/vo_gpu.c +++ b/video/out/vo_gpu.c @@ -188,6 +188,20 @@ static int control(struct vo *vo, uint32_t request, void *data) *(struct mp_image **)data = screen; return true; } + case VOCTRL_SCREENSHOT: { + struct vo_frame *frame = vo_get_current_vo_frame(vo); + if (frame) { + // Disable interpolation and such. + frame->redraw = true; + frame->repeat = false; + frame->still = true; + frame->pts = 0; + frame->duration = -1; + gl_video_screenshot(p->renderer, frame, data); + } + talloc_free(frame); + return true; + } case VOCTRL_LOAD_HWDEC_API: request_hwdec_api(vo); return true; |