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;
|
||||
PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT;
|
||||
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT;
|
||||
PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR;
|
||||
PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR;
|
||||
PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID;
|
||||
PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR;
|
||||
PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR;
|
||||
} procs;
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ struct wlr_gles2_renderer {
|
|||
bool EXT_texture_type_2_10_10_10_REV;
|
||||
bool OES_texture_half_float_linear;
|
||||
bool EXT_texture_norm16;
|
||||
bool NV_pixel_buffer_object;
|
||||
bool OES_mapbuffer;
|
||||
bool EXT_map_buffer_range;
|
||||
} exts;
|
||||
|
||||
struct {
|
||||
|
|
@ -57,6 +60,8 @@ struct wlr_gles2_renderer {
|
|||
PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR;
|
||||
PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR;
|
||||
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES;
|
||||
PFNGLMAPBUFFERRANGEEXTPROC glMapBufferRangeEXT;
|
||||
PFNGLUNMAPBUFFEROESPROC glUnmapBufferOES;
|
||||
} procs;
|
||||
|
||||
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 =
|
||||
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, "Supported EGL display extensions: %s", display_exts_str);
|
||||
if (device_exts_str != NULL) {
|
||||
|
|
@ -986,3 +999,65 @@ int wlr_egl_dup_drm_fd(struct wlr_egl *egl) {
|
|||
|
||||
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 <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <poll.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -443,28 +444,105 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer,
|
|||
|
||||
push_gles2_debug(renderer);
|
||||
|
||||
// Make sure any pending drawing is finished before we try to read it
|
||||
glFinish();
|
||||
|
||||
glGetError(); // Clear the error flag
|
||||
|
||||
unsigned char *p = (unsigned char *)data + dst_y * stride;
|
||||
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) {
|
||||
// Under these particular conditions, we can read the pixels with only
|
||||
// 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 {
|
||||
// Unfortunately GLES2 doesn't support GL_PACK_*, so we have to read
|
||||
// the lines out row by row
|
||||
for (size_t i = 0; i < height; ++i) {
|
||||
uint32_t y = src_y + i;
|
||||
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);
|
||||
|
||||
return glGetError() == GL_NO_ERROR;
|
||||
|
|
@ -769,6 +847,19 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
|
|||
"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) {
|
||||
glEnable(GL_DEBUG_OUTPUT_KHR);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue