2015-09-30 20:52:22 +00:00
|
|
|
/*
|
|
|
|
* Original author: Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
|
|
|
*
|
|
|
|
* This file is part of mpv.
|
|
|
|
*
|
|
|
|
* mpv is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* mpv is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#include <libswscale/swscale.h>
|
|
|
|
|
|
|
|
#include "vo.h"
|
|
|
|
#include "video/csputils.h"
|
|
|
|
#include "video/mp_image.h"
|
|
|
|
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xutil.h>
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
#include "present_sync.h"
|
2015-09-30 20:52:22 +00:00
|
|
|
#include "x11_common.h"
|
|
|
|
|
|
|
|
#include <sys/ipc.h>
|
|
|
|
#include <sys/shm.h>
|
|
|
|
#include <X11/extensions/XShm.h>
|
|
|
|
|
|
|
|
#include "sub/osd.h"
|
|
|
|
#include "sub/draw_bmp.h"
|
|
|
|
|
|
|
|
#include "video/sws_utils.h"
|
|
|
|
#include "video/fmt-conversion.h"
|
|
|
|
|
|
|
|
#include "common/msg.h"
|
|
|
|
#include "input/input.h"
|
|
|
|
#include "options/options.h"
|
|
|
|
#include "osdep/timer.h"
|
|
|
|
|
|
|
|
struct priv {
|
|
|
|
struct vo *vo;
|
|
|
|
|
|
|
|
struct mp_image *original_image;
|
|
|
|
|
|
|
|
XImage *myximage[2];
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
struct mp_image mp_ximages[2];
|
2015-09-30 20:52:22 +00:00
|
|
|
int depth;
|
|
|
|
GC gc;
|
|
|
|
|
|
|
|
uint32_t image_width;
|
|
|
|
uint32_t image_height;
|
|
|
|
|
|
|
|
struct mp_rect src;
|
|
|
|
struct mp_rect dst;
|
|
|
|
struct mp_osd_res osd;
|
|
|
|
|
|
|
|
struct mp_sws_context *sws;
|
|
|
|
|
|
|
|
XVisualInfo vinfo;
|
|
|
|
|
|
|
|
int current_buf;
|
|
|
|
|
|
|
|
int Shmem_Flag;
|
|
|
|
XShmSegmentInfo Shminfo[2];
|
|
|
|
int Shm_Warned_Slow;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool resize(struct vo *vo);
|
|
|
|
|
|
|
|
static bool getMyXImage(struct priv *p, int foo)
|
|
|
|
{
|
|
|
|
struct vo *vo = p->vo;
|
|
|
|
if (vo->x11->display_is_local && XShmQueryExtension(vo->x11->display)) {
|
|
|
|
p->Shmem_Flag = 1;
|
|
|
|
vo->x11->ShmCompletionEvent = XShmGetEventBase(vo->x11->display)
|
|
|
|
+ ShmCompletion;
|
|
|
|
} else {
|
|
|
|
p->Shmem_Flag = 0;
|
|
|
|
MP_WARN(vo, "Shared memory not supported\nReverting to normal Xlib\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p->Shmem_Flag) {
|
|
|
|
p->myximage[foo] =
|
|
|
|
XShmCreateImage(vo->x11->display, p->vinfo.visual, p->depth,
|
|
|
|
ZPixmap, NULL, &p->Shminfo[foo], p->image_width,
|
|
|
|
p->image_height);
|
|
|
|
if (p->myximage[foo] == NULL) {
|
|
|
|
MP_WARN(vo, "Shared memory error,disabling ( Ximage error )\n");
|
|
|
|
goto shmemerror;
|
|
|
|
}
|
|
|
|
p->Shminfo[foo].shmid = shmget(IPC_PRIVATE,
|
|
|
|
p->myximage[foo]->bytes_per_line *
|
|
|
|
p->myximage[foo]->height,
|
|
|
|
IPC_CREAT | 0777);
|
|
|
|
if (p->Shminfo[foo].shmid < 0) {
|
|
|
|
XDestroyImage(p->myximage[foo]);
|
|
|
|
MP_WARN(vo, "Shared memory error,disabling ( seg id error )\n");
|
|
|
|
goto shmemerror;
|
|
|
|
}
|
|
|
|
p->Shminfo[foo].shmaddr = (char *) shmat(p->Shminfo[foo].shmid, 0, 0);
|
|
|
|
|
|
|
|
if (p->Shminfo[foo].shmaddr == ((char *) -1)) {
|
|
|
|
XDestroyImage(p->myximage[foo]);
|
|
|
|
MP_WARN(vo, "Shared memory error,disabling ( address error )\n");
|
|
|
|
goto shmemerror;
|
|
|
|
}
|
|
|
|
p->myximage[foo]->data = p->Shminfo[foo].shmaddr;
|
|
|
|
p->Shminfo[foo].readOnly = False;
|
|
|
|
XShmAttach(vo->x11->display, &p->Shminfo[foo]);
|
|
|
|
|
|
|
|
XSync(vo->x11->display, False);
|
|
|
|
|
|
|
|
shmctl(p->Shminfo[foo].shmid, IPC_RMID, 0);
|
|
|
|
} else {
|
|
|
|
shmemerror:
|
|
|
|
p->Shmem_Flag = 0;
|
2017-12-02 22:19:13 +00:00
|
|
|
|
|
|
|
MP_VERBOSE(vo, "Not using SHM.\n");
|
|
|
|
p->myximage[foo] =
|
|
|
|
XCreateImage(vo->x11->display, p->vinfo.visual, p->depth, ZPixmap,
|
|
|
|
0, NULL, p->image_width, p->image_height, 8, 0);
|
|
|
|
if (!p->myximage[foo]) {
|
|
|
|
MP_WARN(vo, "could not allocate image");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
p->myximage[foo]->data =
|
|
|
|
calloc(1, p->myximage[foo]->bytes_per_line * p->image_height + 32);
|
2015-09-30 20:52:22 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void freeMyXImage(struct priv *p, int foo)
|
|
|
|
{
|
|
|
|
struct vo *vo = p->vo;
|
|
|
|
if (p->Shmem_Flag) {
|
|
|
|
XShmDetach(vo->x11->display, &p->Shminfo[foo]);
|
|
|
|
XDestroyImage(p->myximage[foo]);
|
|
|
|
shmdt(p->Shminfo[foo].shmaddr);
|
2017-12-02 22:19:13 +00:00
|
|
|
} else {
|
2015-09-30 20:52:22 +00:00
|
|
|
if (p->myximage[foo])
|
|
|
|
XDestroyImage(p->myximage[foo]);
|
|
|
|
}
|
|
|
|
p->myximage[foo] = NULL;
|
|
|
|
}
|
|
|
|
|
2020-05-20 16:31:16 +00:00
|
|
|
#define MAKE_MASK(comp) (((1ul << (comp).size) - 1) << (comp).offset)
|
2015-09-30 20:52:22 +00:00
|
|
|
|
2015-10-03 16:20:16 +00:00
|
|
|
static int reconfig(struct vo *vo, struct mp_image_params *fmt)
|
2015-09-30 20:52:22 +00:00
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
|
|
|
mp_image_unrefp(&p->original_image);
|
|
|
|
|
2015-09-30 21:31:34 +00:00
|
|
|
vo_x11_config_vo_window(vo);
|
2015-09-30 20:52:22 +00:00
|
|
|
|
|
|
|
if (!resize(vo))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool resize(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
// Attempt to align. We don't know the size in bytes yet (????), so just
|
|
|
|
// assume worst case (1 byte per pixel).
|
|
|
|
int nw = MPMAX(1, MP_ALIGN_UP(vo->dwidth, MP_IMAGE_BYTE_ALIGN));
|
|
|
|
int nh = MPMAX(1, vo->dheight);
|
2015-09-30 20:52:22 +00:00
|
|
|
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
if (nw > p->image_width || nh > p->image_height) {
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
|
|
freeMyXImage(p, i);
|
2015-09-30 20:52:22 +00:00
|
|
|
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
p->image_width = nw;
|
|
|
|
p->image_height = nh;
|
2015-09-30 20:52:22 +00:00
|
|
|
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
if (!getMyXImage(p, i)) {
|
|
|
|
p->image_width = 0;
|
|
|
|
p->image_height = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2015-09-30 20:52:22 +00:00
|
|
|
}
|
|
|
|
|
2020-05-19 22:14:09 +00:00
|
|
|
int mpfmt = 0;
|
|
|
|
for (int fmt = IMGFMT_START; fmt < IMGFMT_END; fmt++) {
|
|
|
|
struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt);
|
|
|
|
if ((desc.flags & MP_IMGFLAG_HAS_COMPS) && desc.num_planes == 1 &&
|
2020-05-20 16:31:16 +00:00
|
|
|
(desc.flags & MP_IMGFLAG_COLOR_MASK) == MP_IMGFLAG_COLOR_RGB &&
|
|
|
|
(desc.flags & MP_IMGFLAG_TYPE_MASK) == MP_IMGFLAG_TYPE_UINT &&
|
|
|
|
(desc.flags & MP_IMGFLAG_NE) && !(desc.flags & MP_IMGFLAG_ALPHA) &&
|
|
|
|
desc.bpp[0] <= 8 * sizeof(unsigned long) &&
|
2020-05-19 22:14:09 +00:00
|
|
|
p->myximage[0]->bits_per_pixel == desc.bpp[0] &&
|
2020-06-17 17:34:19 +00:00
|
|
|
p->myximage[0]->byte_order == MP_SELECT_LE_BE(LSBFirst, MSBFirst))
|
2020-05-19 22:14:09 +00:00
|
|
|
{
|
2020-06-17 17:34:19 +00:00
|
|
|
// desc.comps[] uses little endian bit offsets, so "swap" the
|
|
|
|
// offsets here.
|
|
|
|
if (MP_SELECT_LE_BE(0, 1)) {
|
|
|
|
// Except for formats that use byte swapping; for these, the
|
|
|
|
// offsets are in native endian. There is no way to distinguish
|
|
|
|
// which one a given format is (could even be both), and using
|
|
|
|
// mp_find_other_endian() is just a guess.
|
|
|
|
if (!mp_find_other_endian(fmt)) {
|
|
|
|
for (int c = 0; c < 3; c++) {
|
|
|
|
desc.comps[c].offset =
|
|
|
|
desc.bpp[0] - desc.comps[c].size -desc.comps[c].offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (p->myximage[0]->red_mask == MAKE_MASK(desc.comps[0]) &&
|
|
|
|
p->myximage[0]->green_mask == MAKE_MASK(desc.comps[1]) &&
|
|
|
|
p->myximage[0]->blue_mask == MAKE_MASK(desc.comps[2]))
|
|
|
|
{
|
|
|
|
mpfmt = fmt;
|
|
|
|
break;
|
|
|
|
}
|
2020-05-19 22:14:09 +00:00
|
|
|
}
|
2015-09-30 20:52:22 +00:00
|
|
|
}
|
2020-05-19 22:14:09 +00:00
|
|
|
|
|
|
|
if (!mpfmt) {
|
2015-09-30 20:52:22 +00:00
|
|
|
MP_ERR(vo, "X server image format not supported, use another VO.\n");
|
2018-11-16 18:55:09 +00:00
|
|
|
return false;
|
2015-09-30 20:52:22 +00:00
|
|
|
}
|
2020-05-19 22:14:09 +00:00
|
|
|
MP_VERBOSE(vo, "Using mp format: %s\n", mp_imgfmt_to_name(mpfmt));
|
2015-09-30 20:52:22 +00:00
|
|
|
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
struct mp_image *img = &p->mp_ximages[i];
|
|
|
|
*img = (struct mp_image){0};
|
|
|
|
mp_image_setfmt(img, mpfmt);
|
|
|
|
mp_image_set_size(img, p->image_width, p->image_height);
|
|
|
|
img->planes[0] = p->myximage[i]->data;
|
|
|
|
img->stride[0] = p->myximage[i]->bytes_per_line;
|
|
|
|
|
|
|
|
mp_image_params_guess_csp(&img->params);
|
|
|
|
}
|
2015-09-30 20:52:22 +00:00
|
|
|
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd);
|
|
|
|
|
|
|
|
if (vo->params) {
|
|
|
|
p->sws->src = *vo->params;
|
|
|
|
p->sws->src.w = mp_rect_w(p->src);
|
|
|
|
p->sws->src.h = mp_rect_h(p->src);
|
|
|
|
|
|
|
|
p->sws->dst = p->mp_ximages[0].params;
|
|
|
|
p->sws->dst.w = mp_rect_w(p->dst);
|
|
|
|
p->sws->dst.h = mp_rect_h(p->dst);
|
|
|
|
|
|
|
|
if (mp_sws_reinit(p->sws) < 0)
|
|
|
|
return false;
|
|
|
|
}
|
2015-09-30 20:52:22 +00:00
|
|
|
|
|
|
|
vo->want_redraw = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Display_Image(struct priv *p, XImage *myximage)
|
|
|
|
{
|
|
|
|
struct vo *vo = p->vo;
|
|
|
|
|
|
|
|
XImage *x_image = p->myximage[p->current_buf];
|
|
|
|
|
|
|
|
if (p->Shmem_Flag) {
|
|
|
|
XShmPutImage(vo->x11->display, vo->x11->window, p->gc, x_image,
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
0, 0, 0, 0, vo->dwidth, vo->dheight, True);
|
2015-09-30 20:52:22 +00:00
|
|
|
vo->x11->ShmCompletionWaitCount++;
|
2017-12-02 22:19:13 +00:00
|
|
|
} else {
|
2015-09-30 20:52:22 +00:00
|
|
|
XPutImage(vo->x11->display, vo->x11->window, p->gc, x_image,
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
0, 0, 0, 0, vo->dwidth, vo->dheight);
|
2015-09-30 20:52:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wait_for_completion(struct vo *vo, int max_outstanding)
|
|
|
|
{
|
|
|
|
struct priv *ctx = vo->priv;
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
if (ctx->Shmem_Flag) {
|
|
|
|
while (x11->ShmCompletionWaitCount > max_outstanding) {
|
|
|
|
if (!ctx->Shm_Warned_Slow) {
|
|
|
|
MP_WARN(vo, "can't keep up! Waiting"
|
|
|
|
" for XShm completion events...\n");
|
|
|
|
ctx->Shm_Warned_Slow = 1;
|
|
|
|
}
|
|
|
|
mp_sleep_us(1000);
|
|
|
|
vo_x11_check_events(vo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void flip_page(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
Display_Image(p, p->myximage[p->current_buf]);
|
|
|
|
p->current_buf = (p->current_buf + 1) % 2;
|
2022-06-22 04:13:44 +00:00
|
|
|
if (vo->x11->use_present) {
|
|
|
|
vo_x11_present(vo);
|
|
|
|
present_sync_swap(vo->x11->present);
|
|
|
|
}
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void get_vsync(struct vo *vo, struct vo_vsync_info *info)
|
|
|
|
{
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
2022-06-22 04:13:44 +00:00
|
|
|
if (x11->use_present)
|
|
|
|
present_sync_get_info(x11->present, info);
|
2015-09-30 20:52:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Note: REDRAW_FRAME can call this with NULL.
|
|
|
|
static void draw_image(struct vo *vo, mp_image_t *mpi)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
|
|
|
wait_for_completion(vo, 1);
|
x11: avoid wasteful rendering when possible
Because wayland is a special snowflake, mpv wound up incorporating a lot
of logic into its render loop where visibilty checks are performed
before rendering anything (in the name of efficiency of course). Only
wayland actually uses this, but there's no reason why other backends
(x11 in this commit) can't be smarter. It's far easier on xorg since we
can just query _NET_WM_STATE_HIDDEN directly and not have to do silly
callback dances.
The function, vo_x11_check_net_wm_state_change, already tracks net wm
changes, including _NET_WM_STATE_HIDDEN. There is an already existing
window_hidden variable but that is actually just for checking if the
window was mapped and has nothing to do with this particular atom. mpv
also currently assumes that a _NET_WM_STATE_HIDDEN is exactly the same
as being minimized but according to the spec, that's not neccesarily
true (in practice, it's likely that these are the same though). Anyways,
just keep track of this state in a new variable (hidden) and use that
for determing if mpv should render or not.
There is one catch though: this cannot work if a display sync mode is
used. This is why the previous commit is needed. The display sync modes
in mpv require a blocking vsync implementation since its render loop is
directly driven by vsync. In xorg, if nothing is actually rendered, then
there's nothing for eglSwapBuffers (or FIFO for vulkan) to block on so
it returns immediately. This, of course, results in completely broken
video. We just need to check to make sure that we aren't in a display
sync mode before trying to be smart about rendering. Display sync is
power inefficient anyways, so no one is really being hurt here. As an
aside, this happens to work in wayland because there's basically a
custom (and ugly) vsync blocking function + timeout but that's off
topic.
2022-04-07 03:36:30 +00:00
|
|
|
bool render = vo_x11_check_visible(vo);
|
|
|
|
if (!render)
|
|
|
|
return;
|
2015-09-30 20:52:22 +00:00
|
|
|
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
struct mp_image *img = &p->mp_ximages[p->current_buf];
|
2015-09-30 20:52:22 +00:00
|
|
|
|
|
|
|
if (mpi) {
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
mp_image_clear_rc_inv(img, p->dst);
|
|
|
|
|
2015-09-30 20:52:22 +00:00
|
|
|
struct mp_image src = *mpi;
|
|
|
|
struct mp_rect src_rc = p->src;
|
|
|
|
src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, src.fmt.align_x);
|
|
|
|
src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, src.fmt.align_y);
|
|
|
|
mp_image_crop_rc(&src, src_rc);
|
|
|
|
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
struct mp_image dst = *img;
|
|
|
|
mp_image_crop_rc(&dst, p->dst);
|
|
|
|
|
|
|
|
mp_sws_scale(p->sws, &dst, &src);
|
2015-09-30 20:52:22 +00:00
|
|
|
} else {
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
mp_image_clear(img, 0, 0, img->w, img->h);
|
2015-09-30 20:52:22 +00:00
|
|
|
}
|
|
|
|
|
vo_x11: allow OSD rendering outside of video region
I'm not sure why it only rendered OSD inside the video. Since OSD
rendering was always done on the X image (after software scaling and
color conversion), there was no technical reason for this. Maybe it was
because the code started out this way, and it was annoying to change it.
Possibly, one reason was that it didn't normally have to clear the black
bars in every frame (if video didn't cover the entire window).
Anyway, simply render OSD to the full window. This gets rid of some
rather weird stuff. It seems to look mostly like vo_wlshm now. The
uncovered regions are cleared every frame, which could probably be
avoided by being clever with the OSD renderer code, but this is where
I'm decidedly losing interest.
There was some mysterious code for aligning the image width to 8 pixels.
Replace that by attempting to align it to SIMD alignment (might matter
for libswscale, or if repack.c gets SIMD). Why are there apparently 4
different ways representing a pixel format (depth, VisualID, Visual,
XVisualInfo), but none of them seem to provide the XImage.bits_per_pixel
value (the actual size of a pixel, including padding)? Even after 33
years, X11 still seems overengineered, confusing, and inconvenient. So
just call X11 a heap of shit, and assume the worst case for alignment.
2020-05-22 12:24:16 +00:00
|
|
|
osd_draw_on_image(vo->osd, p->osd, mpi ? mpi->pts : 0, 0, img);
|
2015-09-30 20:52:22 +00:00
|
|
|
|
|
|
|
if (mpi != p->original_image) {
|
|
|
|
talloc_free(p->original_image);
|
|
|
|
p->original_image = mpi;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int query_format(struct vo *vo, int format)
|
|
|
|
{
|
2019-11-03 21:52:12 +00:00
|
|
|
struct priv *p = vo->priv;
|
|
|
|
if (mp_sws_supports_formats(p->sws, IMGFMT_RGB0, format))
|
2015-09-30 20:52:22 +00:00
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void uninit(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
if (p->myximage[0])
|
|
|
|
freeMyXImage(p, 0);
|
|
|
|
if (p->myximage[1])
|
|
|
|
freeMyXImage(p, 1);
|
|
|
|
if (p->gc)
|
|
|
|
XFreeGC(vo->x11->display, p->gc);
|
|
|
|
|
|
|
|
talloc_free(p->original_image);
|
|
|
|
|
|
|
|
vo_x11_uninit(vo);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int preinit(struct vo *vo)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
p->vo = vo;
|
|
|
|
p->sws = mp_sws_alloc(vo);
|
2019-10-19 23:58:02 +00:00
|
|
|
p->sws->log = vo->log;
|
2019-10-31 14:18:57 +00:00
|
|
|
mp_sws_enable_cmdline_opts(p->sws, vo->global);
|
2015-09-30 20:52:22 +00:00
|
|
|
|
|
|
|
if (!vo_x11_init(vo))
|
|
|
|
goto error;
|
|
|
|
struct vo_x11_state *x11 = vo->x11;
|
|
|
|
|
|
|
|
XWindowAttributes attribs;
|
|
|
|
XGetWindowAttributes(x11->display, x11->rootwin, &attribs);
|
|
|
|
p->depth = attribs.depth;
|
|
|
|
|
|
|
|
if (!XMatchVisualInfo(x11->display, x11->screen, p->depth,
|
|
|
|
TrueColor, &p->vinfo))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
MP_VERBOSE(vo, "selected visual: %d\n", (int)p->vinfo.visualid);
|
|
|
|
|
2015-09-30 21:31:34 +00:00
|
|
|
if (!vo_x11_create_vo_window(vo, &p->vinfo, "x11"))
|
|
|
|
goto error;
|
|
|
|
|
2015-09-30 20:52:22 +00:00
|
|
|
p->gc = XCreateGC(x11->display, x11->window, 0, NULL);
|
2016-07-09 12:19:50 +00:00
|
|
|
MP_WARN(vo, "Warning: this legacy VO has bad performance. Consider fixing "
|
|
|
|
"your graphics drivers, or not forcing the x11 VO.\n");
|
2015-09-30 20:52:22 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
uninit(vo);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int control(struct vo *vo, uint32_t request, void *data)
|
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
|
|
|
switch (request) {
|
|
|
|
case VOCTRL_SET_PANSCAN:
|
|
|
|
if (vo->config_ok)
|
|
|
|
resize(vo);
|
|
|
|
return VO_TRUE;
|
|
|
|
case VOCTRL_REDRAW_FRAME:
|
|
|
|
draw_image(vo, p->original_image);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int events = 0;
|
|
|
|
int r = vo_x11_control(vo, &events, request, data);
|
|
|
|
if (vo->config_ok && (events & (VO_EVENT_EXPOSE | VO_EVENT_RESIZE)))
|
|
|
|
resize(vo);
|
|
|
|
vo_event(vo, events);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct vo_driver video_out_x11 = {
|
2020-02-11 20:43:53 +00:00
|
|
|
.description = "X11 (software scaling)",
|
2015-09-30 20:52:22 +00:00
|
|
|
.name = "x11",
|
|
|
|
.priv_size = sizeof(struct priv),
|
|
|
|
.preinit = preinit,
|
|
|
|
.query_format = query_format,
|
|
|
|
.reconfig = reconfig,
|
|
|
|
.control = control,
|
|
|
|
.draw_image = draw_image,
|
|
|
|
.flip_page = flip_page,
|
x11: support xorg present extension
This builds off of present_sync which was introduced in a previous
commit to support xorg's present extension in all of the X11 backends
(sans vdpau) in mpv. It turns out there is an Xpresent library that
integrates the xorg present extention with Xlib (which barely anyone
seems to use), so this can be added without too much trouble. The
workflow is to first setup the event by telling Xorg we would like to
receive PresentCompleteNotify (there are others in the extension but
this is the only one we really care about). After that, just call
XPresentNotifyMSC after every buffer swap with a target_msc of 0. Xorg
then returns the last presentation through its usual event loop and we
go ahead and use that information to update mpv's values for vsync
timing purposes. One theoretical weakness of this approach is that the
present event is put on the same queue as the rest of the XEvents. It
would be nicer for it be placed somewhere else so we could just wait
on that queue without having to deal with other possible events in
there. In theory, xcb could do that with special events, but it doesn't
really matter in practice.
Unsurprisingly, this doesn't work on NVIDIA. Well NVIDIA does actually
receive presentation events, but for whatever the calculations used make
timings worse which defeats the purpose. This works perfectly fine on
Mesa however. Utilizing the previous commit that detects Xrandr
providers, we can enable this mechanism for users that have both Mesa
and not NVIDIA (to avoid messing up anyone that has a switchable
graphics system or such). Patches welcome if anyone figures out how to
fix this on NVIDIA.
Unlike the EGL/GLX sync extensions, the present extension works with any
graphics API (good for vulkan since its timing extension has been in
development hell). NVIDIA also happens to have zero support for the
EGL/GLX sync extensions, so we can just remove it with no loss. Only
Xorg ever used it and other backends already have their own present
methods. vo_vdpau VO is a special case that has its own fancying timing
code in its flip_page. This presumably works well, and I have no way of
testing it so just leave it as it is.
2022-06-10 16:49:38 +00:00
|
|
|
.get_vsync = get_vsync,
|
2016-07-20 18:52:08 +00:00
|
|
|
.wakeup = vo_x11_wakeup,
|
|
|
|
.wait_events = vo_x11_wait_events,
|
2015-09-30 20:52:22 +00:00
|
|
|
.uninit = uninit,
|
|
|
|
};
|