mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-04-21 06:46:46 -04:00
Merge branch 'github/fork/emersion/pbo' into 'master'
Draft: render: asynchronous read_pixels See merge request wlroots/wlroots!2099
This commit is contained in:
commit
54769e21db
4 changed files with 189 additions and 5 deletions
|
|
@ -38,6 +38,11 @@ struct wlr_egl {
|
||||||
PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT;
|
PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT;
|
||||||
PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT;
|
PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT;
|
||||||
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT;
|
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT;
|
||||||
|
PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR;
|
||||||
|
PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR;
|
||||||
|
PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID;
|
||||||
|
PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR;
|
||||||
|
PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR;
|
||||||
} procs;
|
} procs;
|
||||||
|
|
||||||
bool has_modifiers;
|
bool has_modifiers;
|
||||||
|
|
@ -114,4 +119,12 @@ bool wlr_egl_unset_current(struct wlr_egl *egl);
|
||||||
|
|
||||||
bool wlr_egl_is_current(struct wlr_egl *egl);
|
bool wlr_egl_is_current(struct wlr_egl *egl);
|
||||||
|
|
||||||
|
EGLSyncKHR wlr_egl_create_sync(struct wlr_egl *egl, int fence_fd);
|
||||||
|
|
||||||
|
void wlr_egl_destroy_sync(struct wlr_egl *egl, EGLSyncKHR sync);
|
||||||
|
|
||||||
|
int wlr_egl_dup_fence_fd(struct wlr_egl *egl, EGLSyncKHR sync);
|
||||||
|
|
||||||
|
bool wlr_egl_wait_sync(struct wlr_egl *egl, EGLSyncKHR sync);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,9 @@ struct wlr_gles2_renderer {
|
||||||
bool EXT_texture_type_2_10_10_10_REV;
|
bool EXT_texture_type_2_10_10_10_REV;
|
||||||
bool OES_texture_half_float_linear;
|
bool OES_texture_half_float_linear;
|
||||||
bool EXT_texture_norm16;
|
bool EXT_texture_norm16;
|
||||||
|
bool NV_pixel_buffer_object;
|
||||||
|
bool OES_mapbuffer;
|
||||||
|
bool EXT_map_buffer_range;
|
||||||
} exts;
|
} exts;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
@ -57,6 +60,8 @@ struct wlr_gles2_renderer {
|
||||||
PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR;
|
PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR;
|
||||||
PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR;
|
PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR;
|
||||||
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES;
|
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES;
|
||||||
|
PFNGLMAPBUFFERRANGEEXTPROC glMapBufferRangeEXT;
|
||||||
|
PFNGLUNMAPBUFFEROESPROC glUnmapBufferOES;
|
||||||
} procs;
|
} procs;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
|
||||||
75
render/egl.c
75
render/egl.c
|
|
@ -344,6 +344,19 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) {
|
||||||
egl->exts.IMG_context_priority =
|
egl->exts.IMG_context_priority =
|
||||||
check_egl_ext(display_exts_str, "EGL_IMG_context_priority");
|
check_egl_ext(display_exts_str, "EGL_IMG_context_priority");
|
||||||
|
|
||||||
|
if (check_egl_ext(display_exts_str, "EGL_KHR_fence_sync") &&
|
||||||
|
check_egl_ext(display_exts_str, "EGL_ANDROID_native_fence_sync")) {
|
||||||
|
load_egl_proc(&egl->procs.eglCreateSyncKHR, "eglCreateSyncKHR");
|
||||||
|
load_egl_proc(&egl->procs.eglDestroySyncKHR, "eglDestroySyncKHR");
|
||||||
|
load_egl_proc(&egl->procs.eglDupNativeFenceFDANDROID,
|
||||||
|
"eglDupNativeFenceFDANDROID");
|
||||||
|
load_egl_proc(&egl->procs.eglClientWaitSyncKHR, "eglClientWaitSyncKHR");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_egl_ext(display_exts_str, "EGL_KHR_wait_sync")) {
|
||||||
|
load_egl_proc(&egl->procs.eglWaitSyncKHR, "eglWaitSyncKHR");
|
||||||
|
}
|
||||||
|
|
||||||
wlr_log(WLR_INFO, "Using EGL %d.%d", (int)major, (int)minor);
|
wlr_log(WLR_INFO, "Using EGL %d.%d", (int)major, (int)minor);
|
||||||
wlr_log(WLR_INFO, "Supported EGL display extensions: %s", display_exts_str);
|
wlr_log(WLR_INFO, "Supported EGL display extensions: %s", display_exts_str);
|
||||||
if (device_exts_str != NULL) {
|
if (device_exts_str != NULL) {
|
||||||
|
|
@ -986,3 +999,65 @@ int wlr_egl_dup_drm_fd(struct wlr_egl *egl) {
|
||||||
|
|
||||||
return render_fd;
|
return render_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EGLSyncKHR wlr_egl_create_sync(struct wlr_egl *egl, int fence_fd) {
|
||||||
|
if (!egl->procs.eglCreateSyncKHR) {
|
||||||
|
return EGL_NO_SYNC_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLint attribs[3] = { EGL_NONE };
|
||||||
|
int dup_fd = -1;
|
||||||
|
if (fence_fd >= 0) {
|
||||||
|
dup_fd = fcntl(fence_fd, F_DUPFD_CLOEXEC, 0);
|
||||||
|
if (dup_fd < 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "dup failed");
|
||||||
|
return EGL_NO_SYNC_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
attribs[0] = EGL_SYNC_NATIVE_FENCE_FD_ANDROID;
|
||||||
|
attribs[1] = dup_fd;
|
||||||
|
attribs[2] = EGL_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLSyncKHR sync = egl->procs.eglCreateSyncKHR(egl->display,
|
||||||
|
EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
|
||||||
|
if (sync == EGL_NO_SYNC_KHR) {
|
||||||
|
wlr_log(WLR_ERROR, "eglCreateSyncKHR failed");
|
||||||
|
if (dup_fd >= 0) {
|
||||||
|
close(dup_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wlr_egl_destroy_sync(struct wlr_egl *egl, EGLSyncKHR sync) {
|
||||||
|
if (sync == EGL_NO_SYNC_KHR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assert(egl->procs.eglDestroySyncKHR);
|
||||||
|
if (egl->procs.eglDestroySyncKHR(egl->display, sync) != EGL_TRUE) {
|
||||||
|
wlr_log(WLR_ERROR, "eglDestroySyncKHR failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int wlr_egl_dup_fence_fd(struct wlr_egl *egl, EGLSyncKHR sync) {
|
||||||
|
if (!egl->procs.eglDupNativeFenceFDANDROID) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = egl->procs.eglDupNativeFenceFDANDROID(egl->display, sync);
|
||||||
|
if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
|
||||||
|
wlr_log(WLR_ERROR, "eglDupNativeFenceFDANDROID failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wlr_egl_wait_sync(struct wlr_egl *egl, EGLSyncKHR sync) {
|
||||||
|
if (egl->procs.eglWaitSyncKHR(egl->display, sync, 0) != EGL_TRUE) {
|
||||||
|
wlr_log(WLR_ERROR, "eglWaitSyncKHR failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include <drm_fourcc.h>
|
#include <drm_fourcc.h>
|
||||||
#include <GLES2/gl2.h>
|
#include <GLES2/gl2.h>
|
||||||
#include <GLES2/gl2ext.h>
|
#include <GLES2/gl2ext.h>
|
||||||
|
#include <poll.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
@ -443,28 +444,105 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer,
|
||||||
|
|
||||||
push_gles2_debug(renderer);
|
push_gles2_debug(renderer);
|
||||||
|
|
||||||
// Make sure any pending drawing is finished before we try to read it
|
|
||||||
glFinish();
|
|
||||||
|
|
||||||
glGetError(); // Clear the error flag
|
glGetError(); // Clear the error flag
|
||||||
|
|
||||||
unsigned char *p = (unsigned char *)data + dst_y * stride;
|
unsigned char *p = (unsigned char *)data + dst_y * stride;
|
||||||
uint32_t pack_stride = width * drm_fmt->bpp / 8;
|
uint32_t pack_stride = width * drm_fmt->bpp / 8;
|
||||||
|
unsigned char *out_p = p;
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
GLuint pbo = 0;
|
||||||
|
#else
|
||||||
|
static GLuint pbo = 0;
|
||||||
|
#endif
|
||||||
|
bool use_pbo = true;
|
||||||
|
if (renderer->exts.NV_pixel_buffer_object && renderer->exts.OES_mapbuffer &&
|
||||||
|
renderer->exts.EXT_map_buffer_range && use_pbo) {
|
||||||
|
// Ideally we should use the GL_STREAM_READ usage instead of
|
||||||
|
// GL_STREAM_DRAW, however it's not available in GLES2. It's just a
|
||||||
|
// hint and it's in general ignored by GL implementations, see:
|
||||||
|
// https://github.com/KhronosGroup/OpenGL-API/issues/66
|
||||||
|
|
||||||
|
if (pbo == 0) {
|
||||||
|
wlr_log(WLR_DEBUG, "create PBO");
|
||||||
|
glGenBuffers(1, &pbo);
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER_NV, pbo);
|
||||||
|
glBufferData(GL_PIXEL_PACK_BUFFER_NV, stride * height, NULL,
|
||||||
|
GL_STREAM_DRAW);
|
||||||
|
} else {
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER_NV, pbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
out_p = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (pack_stride == stride && dst_x == 0) {
|
if (pack_stride == stride && dst_x == 0) {
|
||||||
// Under these particular conditions, we can read the pixels with only
|
// Under these particular conditions, we can read the pixels with only
|
||||||
// one glReadPixels call
|
// one glReadPixels call
|
||||||
|
|
||||||
glReadPixels(src_x, src_y, width, height, fmt->gl_format, fmt->gl_type, p);
|
wlr_log(WLR_DEBUG, "glReadPixels start");
|
||||||
|
glReadPixels(src_x, src_y, width, height, fmt->gl_format, fmt->gl_type, out_p);
|
||||||
|
wlr_log(WLR_DEBUG, "glReadPixels end");
|
||||||
} else {
|
} else {
|
||||||
// Unfortunately GLES2 doesn't support GL_PACK_*, so we have to read
|
// Unfortunately GLES2 doesn't support GL_PACK_*, so we have to read
|
||||||
// the lines out row by row
|
// the lines out row by row
|
||||||
for (size_t i = 0; i < height; ++i) {
|
for (size_t i = 0; i < height; ++i) {
|
||||||
uint32_t y = src_y + i;
|
uint32_t y = src_y + i;
|
||||||
glReadPixels(src_x, y, width, 1, fmt->gl_format,
|
glReadPixels(src_x, y, width, 1, fmt->gl_format,
|
||||||
fmt->gl_type, p + i * stride + dst_x * drm_fmt->bpp / 8);
|
fmt->gl_type, NULL /* TODO */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pbo != 0) {
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER_NV, 0);
|
||||||
|
glFlush();
|
||||||
|
|
||||||
|
EGLSyncKHR sync = wlr_egl_create_sync(renderer->egl, -1);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int fence_fd = wlr_egl_dup_fence_fd(renderer->egl, sync);
|
||||||
|
|
||||||
|
wlr_log(WLR_DEBUG, "poll start %d", fence_fd);
|
||||||
|
struct pollfd pollfd = { .fd = fence_fd, .events = POLLIN };
|
||||||
|
if (poll(&pollfd, 1, -1) <= 0) {
|
||||||
|
wlr_log(WLR_ERROR, "poll failed");
|
||||||
|
}
|
||||||
|
wlr_log(WLR_DEBUG, "poll end");
|
||||||
|
|
||||||
|
close(fence_fd);
|
||||||
|
#else
|
||||||
|
wlr_log(WLR_DEBUG, "eglClientWaitSyncKHR start");
|
||||||
|
EGLint ret = renderer->egl->procs.eglClientWaitSyncKHR(
|
||||||
|
renderer->egl->display, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
|
||||||
|
EGL_FOREVER_KHR);
|
||||||
|
if (ret != EGL_CONDITION_SATISFIED_KHR) {
|
||||||
|
wlr_log(WLR_ERROR, "eglClientWaitSyncKHR failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
wlr_log(WLR_DEBUG, "eglClientWaitSyncKHR end");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
wlr_egl_destroy_sync(renderer->egl, sync);
|
||||||
|
|
||||||
|
wlr_log(WLR_DEBUG, "glMapBufferRangeEXT");
|
||||||
|
|
||||||
|
// glMapBufferOES doesn't allow for read access, so we need to use
|
||||||
|
// glMapBufferRangeEXT instead
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER_NV, pbo);
|
||||||
|
unsigned char *in = renderer->procs.glMapBufferRangeEXT(
|
||||||
|
GL_PIXEL_PACK_BUFFER_NV, 0, stride * height, GL_MAP_READ_BIT_EXT);
|
||||||
|
// TODO: error handling
|
||||||
|
|
||||||
|
wlr_log(WLR_DEBUG, "memcpy");
|
||||||
|
memcpy(p, in, stride * height);
|
||||||
|
|
||||||
|
wlr_log(WLR_DEBUG, "glUnmapBufferOES");
|
||||||
|
renderer->procs.glUnmapBufferOES(GL_PIXEL_PACK_BUFFER_NV);
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER_NV, 0);
|
||||||
|
|
||||||
|
wlr_log(WLR_DEBUG, "done!");
|
||||||
|
}
|
||||||
|
|
||||||
pop_gles2_debug(renderer);
|
pop_gles2_debug(renderer);
|
||||||
|
|
||||||
return glGetError() == GL_NO_ERROR;
|
return glGetError() == GL_NO_ERROR;
|
||||||
|
|
@ -769,6 +847,19 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
|
||||||
"glEGLImageTargetRenderbufferStorageOES");
|
"glEGLImageTargetRenderbufferStorageOES");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderer->exts.NV_pixel_buffer_object =
|
||||||
|
check_gl_ext(exts_str, "GL_NV_pixel_buffer_object");
|
||||||
|
|
||||||
|
if (check_gl_ext(exts_str, "GL_OES_mapbuffer")) {
|
||||||
|
renderer->exts.OES_mapbuffer = true;
|
||||||
|
load_gl_proc(&renderer->procs.glUnmapBufferOES, "glUnmapBufferOES");
|
||||||
|
}
|
||||||
|
if (check_gl_ext(exts_str, "GL_EXT_map_buffer_range")) {
|
||||||
|
renderer->exts.EXT_map_buffer_range = true;
|
||||||
|
load_gl_proc(&renderer->procs.glMapBufferRangeEXT,
|
||||||
|
"glMapBufferRangeEXT");
|
||||||
|
}
|
||||||
|
|
||||||
if (renderer->exts.KHR_debug) {
|
if (renderer->exts.KHR_debug) {
|
||||||
glEnable(GL_DEBUG_OUTPUT_KHR);
|
glEnable(GL_DEBUG_OUTPUT_KHR);
|
||||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue