diff options
Diffstat (limited to 'render')
-rw-r--r-- | render/dmabuf.c | 10 | ||||
-rw-r--r-- | render/egl.c | 609 | ||||
-rw-r--r-- | render/glapi.txt | 19 | ||||
-rw-r--r-- | render/gles2/pixel_format.c | 78 | ||||
-rw-r--r-- | render/gles2/renderer.c | 655 | ||||
-rw-r--r-- | render/gles2/shaders.c | 88 | ||||
-rw-r--r-- | render/gles2/texture.c | 304 | ||||
-rw-r--r-- | render/gles2/util.c | 38 | ||||
-rw-r--r-- | render/meson.build | 37 | ||||
-rw-r--r-- | render/wlr_renderer.c | 226 | ||||
-rw-r--r-- | render/wlr_texture.c | 71 |
11 files changed, 2135 insertions, 0 deletions
diff --git a/render/dmabuf.c b/render/dmabuf.c new file mode 100644 index 00000000..6b500748 --- /dev/null +++ b/render/dmabuf.c @@ -0,0 +1,10 @@ +#include <unistd.h> +#include <wlr/render/dmabuf.h> + +void wlr_dmabuf_attributes_finish( struct wlr_dmabuf_attributes *attribs) { + for (int i = 0; i < attribs->n_planes; ++i) { + close(attribs->fd[i]); + attribs->fd[i] = -1; + } + attribs->n_planes = 0; +} diff --git a/render/egl.c b/render/egl.c new file mode 100644 index 00000000..cc00dece --- /dev/null +++ b/render/egl.c @@ -0,0 +1,609 @@ +#include <assert.h> +#include <drm_fourcc.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <stdio.h> +#include <stdlib.h> +#include <wlr/render/egl.h> +#include <wlr/util/log.h> +#include <wlr/util/region.h> +#include "glapi.h" + +static bool egl_get_config(EGLDisplay disp, EGLint *attribs, EGLConfig *out, + EGLint visual_id) { + EGLint count = 0, matched = 0, ret; + + ret = eglGetConfigs(disp, NULL, 0, &count); + if (ret == EGL_FALSE || count == 0) { + wlr_log(WLR_ERROR, "eglGetConfigs returned no configs"); + return false; + } + + EGLConfig configs[count]; + + ret = eglChooseConfig(disp, attribs, configs, count, &matched); + if (ret == EGL_FALSE) { + wlr_log(WLR_ERROR, "eglChooseConfig failed"); + return false; + } + + for (int i = 0; i < matched; ++i) { + EGLint visual; + if (!eglGetConfigAttrib(disp, configs[i], + EGL_NATIVE_VISUAL_ID, &visual)) { + continue; + } + + if (!visual_id || visual == visual_id) { + *out = configs[i]; + return true; + } + } + + wlr_log(WLR_ERROR, "no valid egl config found"); + return false; +} + +static enum wlr_log_importance egl_log_importance_to_wlr(EGLint type) { + switch (type) { + case EGL_DEBUG_MSG_CRITICAL_KHR: return WLR_ERROR; + case EGL_DEBUG_MSG_ERROR_KHR: return WLR_ERROR; + case EGL_DEBUG_MSG_WARN_KHR: return WLR_ERROR; + case EGL_DEBUG_MSG_INFO_KHR: return WLR_INFO; + default: return WLR_INFO; + } +} + +static void egl_log(EGLenum error, const char *command, EGLint msg_type, + EGLLabelKHR thread, EGLLabelKHR obj, const char *msg) { + _wlr_log(egl_log_importance_to_wlr(msg_type), "[EGL] %s: %s", command, msg); +} + +static bool check_egl_ext(const char *exts, const char *ext) { + size_t extlen = strlen(ext); + const char *end = exts + strlen(exts); + + while (exts < end) { + if (*exts == ' ') { + exts++; + continue; + } + size_t n = strcspn(exts, " "); + if (n == extlen && strncmp(ext, exts, n) == 0) { + return true; + } + exts += n; + } + return false; +} + +static void print_dmabuf_formats(struct wlr_egl *egl) { + /* Avoid log msg if extension is not present */ + if (!egl->exts.image_dmabuf_import_modifiers_ext) { + return; + } + + int *formats; + int num = wlr_egl_get_dmabuf_formats(egl, &formats); + if (num < 0) { + return; + } + + char str_formats[num * 5 + 1]; + for (int i = 0; i < num; i++) { + snprintf(&str_formats[i*5], (num - i) * 5 + 1, "%.4s ", + (char*)&formats[i]); + } + wlr_log(WLR_DEBUG, "Supported dmabuf buffer formats: %s", str_formats); + free(formats); +} + +bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display, + EGLint *config_attribs, EGLint visual_id) { + if (!load_glapi()) { + return false; + } + + if (eglDebugMessageControlKHR) { + static const EGLAttrib debug_attribs[] = { + EGL_DEBUG_MSG_CRITICAL_KHR, EGL_TRUE, + EGL_DEBUG_MSG_ERROR_KHR, EGL_TRUE, + EGL_DEBUG_MSG_WARN_KHR, EGL_TRUE, + EGL_DEBUG_MSG_INFO_KHR, EGL_TRUE, + EGL_NONE, + }; + eglDebugMessageControlKHR(egl_log, debug_attribs); + } + + if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) { + wlr_log(WLR_ERROR, "Failed to bind to the OpenGL ES API"); + goto error; + } + + if (platform == EGL_PLATFORM_SURFACELESS_MESA) { + assert(remote_display == NULL); + egl->display = eglGetPlatformDisplayEXT(platform, EGL_DEFAULT_DISPLAY, NULL); + } else { + egl->display = eglGetPlatformDisplayEXT(platform, remote_display, NULL); + } + if (egl->display == EGL_NO_DISPLAY) { + wlr_log(WLR_ERROR, "Failed to create EGL display"); + goto error; + } + + egl->platform = platform; + + EGLint major, minor; + if (eglInitialize(egl->display, &major, &minor) == EGL_FALSE) { + wlr_log(WLR_ERROR, "Failed to initialize EGL"); + goto error; + } + + if (!egl_get_config(egl->display, config_attribs, &egl->config, visual_id)) { + wlr_log(WLR_ERROR, "Failed to get EGL config"); + goto error; + } + + egl->exts_str = eglQueryString(egl->display, EGL_EXTENSIONS); + + wlr_log(WLR_INFO, "Using EGL %d.%d", (int)major, (int)minor); + wlr_log(WLR_INFO, "Supported EGL extensions: %s", egl->exts_str); + wlr_log(WLR_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR)); + + egl->exts.image_base_khr = + check_egl_ext(egl->exts_str, "EGL_KHR_image_base") + && eglCreateImageKHR && eglDestroyImageKHR; + + egl->exts.buffer_age_ext = + check_egl_ext(egl->exts_str, "EGL_EXT_buffer_age"); + egl->exts.swap_buffers_with_damage_ext = + (check_egl_ext(egl->exts_str, "EGL_EXT_swap_buffers_with_damage") && + eglSwapBuffersWithDamageEXT); + egl->exts.swap_buffers_with_damage_khr = + (check_egl_ext(egl->exts_str, "EGL_KHR_swap_buffers_with_damage") && + eglSwapBuffersWithDamageKHR); + + egl->exts.image_dmabuf_import_ext = + check_egl_ext(egl->exts_str, "EGL_EXT_image_dma_buf_import"); + egl->exts.image_dmabuf_import_modifiers_ext = + check_egl_ext(egl->exts_str, "EGL_EXT_image_dma_buf_import_modifiers") + && eglQueryDmaBufFormatsEXT && eglQueryDmaBufModifiersEXT; + + egl->exts.image_dma_buf_export_mesa = + check_egl_ext(egl->exts_str, "EGL_MESA_image_dma_buf_export") && + eglExportDMABUFImageQueryMESA && eglExportDMABUFImageMESA; + + print_dmabuf_formats(egl); + + egl->exts.bind_wayland_display_wl = + check_egl_ext(egl->exts_str, "EGL_WL_bind_wayland_display") + && eglBindWaylandDisplayWL && eglUnbindWaylandDisplayWL + && eglQueryWaylandBufferWL; + + bool ext_context_priority = + check_egl_ext(egl->exts_str, "EGL_IMG_context_priority"); + + size_t atti = 0; + EGLint attribs[5]; + attribs[atti++] = EGL_CONTEXT_CLIENT_VERSION; + attribs[atti++] = 2; + + // On DRM, request a high priority context if possible + bool request_high_priority = ext_context_priority && + platform == EGL_PLATFORM_GBM_MESA; + + // Try to reschedule all of our rendering to be completed first. If it + // fails, it will fallback to the default priority (MEDIUM). + if (request_high_priority) { + attribs[atti++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG; + attribs[atti++] = EGL_CONTEXT_PRIORITY_HIGH_IMG; + } + + attribs[atti++] = EGL_NONE; + assert(atti <= sizeof(attribs)/sizeof(attribs[0])); + + egl->context = eglCreateContext(egl->display, egl->config, + EGL_NO_CONTEXT, attribs); + if (egl->context == EGL_NO_CONTEXT) { + wlr_log(WLR_ERROR, "Failed to create EGL context"); + goto error; + } + + if (request_high_priority) { + EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG; + eglQueryContext(egl->display, egl->context, + EGL_CONTEXT_PRIORITY_LEVEL_IMG, &priority); + if (priority != EGL_CONTEXT_PRIORITY_HIGH_IMG) { + wlr_log(WLR_INFO, "Failed to obtain a high priority context"); + } else { + wlr_log(WLR_DEBUG, "Obtained high priority context"); + } + } + + if (!eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, + egl->context)) { + wlr_log(WLR_ERROR, "Failed to make EGL context current"); + goto error; + } + + return true; + +error: + eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (egl->display) { + eglTerminate(egl->display); + } + eglReleaseThread(); + return false; +} + +void wlr_egl_finish(struct wlr_egl *egl) { + if (egl == NULL) { + return; + } + + eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (egl->wl_display) { + assert(egl->exts.bind_wayland_display_wl); + eglUnbindWaylandDisplayWL(egl->display, egl->wl_display); + } + + eglDestroyContext(egl->display, egl->context); + eglTerminate(egl->display); + eglReleaseThread(); +} + +bool wlr_egl_bind_display(struct wlr_egl *egl, struct wl_display *local_display) { + if (!egl->exts.bind_wayland_display_wl) { + return false; + } + + if (eglBindWaylandDisplayWL(egl->display, local_display)) { + egl->wl_display = local_display; + return true; + } + + return false; +} + +bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImage image) { + if (!egl->exts.image_base_khr) { + return false; + } + if (!image) { + return true; + } + return eglDestroyImageKHR(egl->display, image); +} + +EGLSurface wlr_egl_create_surface(struct wlr_egl *egl, void *window) { + assert(eglCreatePlatformWindowSurfaceEXT); + EGLSurface surf = eglCreatePlatformWindowSurfaceEXT(egl->display, + egl->config, window, NULL); + if (surf == EGL_NO_SURFACE) { + wlr_log(WLR_ERROR, "Failed to create EGL surface"); + return EGL_NO_SURFACE; + } + return surf; +} + +static int egl_get_buffer_age(struct wlr_egl *egl, EGLSurface surface) { + if (!egl->exts.buffer_age_ext) { + return -1; + } + + EGLint buffer_age; + EGLBoolean ok = eglQuerySurface(egl->display, surface, + EGL_BUFFER_AGE_EXT, &buffer_age); + if (!ok) { + wlr_log(WLR_ERROR, "Failed to get EGL surface buffer age"); + return -1; + } + + return buffer_age; +} + +bool wlr_egl_make_current(struct wlr_egl *egl, EGLSurface surface, + int *buffer_age) { + if (!eglMakeCurrent(egl->display, surface, surface, egl->context)) { + wlr_log(WLR_ERROR, "eglMakeCurrent failed"); + return false; + } + + if (buffer_age != NULL) { + *buffer_age = egl_get_buffer_age(egl, surface); + } + return true; +} + +bool wlr_egl_is_current(struct wlr_egl *egl) { + return eglGetCurrentContext() == egl->context; +} + +bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface, + pixman_region32_t *damage) { + // Never block when swapping buffers on Wayland + if (egl->platform == EGL_PLATFORM_WAYLAND_EXT) { + eglSwapInterval(egl->display, 0); + } + + EGLBoolean ret; + if (damage != NULL && (egl->exts.swap_buffers_with_damage_ext || + egl->exts.swap_buffers_with_damage_khr)) { + EGLint width = 0, height = 0; + eglQuerySurface(egl->display, surface, EGL_WIDTH, &width); + eglQuerySurface(egl->display, surface, EGL_HEIGHT, &height); + + pixman_region32_t flipped_damage; + pixman_region32_init(&flipped_damage); + wlr_region_transform(&flipped_damage, damage, + WL_OUTPUT_TRANSFORM_FLIPPED_180, width, height); + + int nrects; + pixman_box32_t *rects = + pixman_region32_rectangles(&flipped_damage, &nrects); + EGLint egl_damage[4 * nrects]; + for (int i = 0; i < nrects; ++i) { + egl_damage[4*i] = rects[i].x1; + egl_damage[4*i + 1] = rects[i].y1; + egl_damage[4*i + 2] = rects[i].x2 - rects[i].x1; + egl_damage[4*i + 3] = rects[i].y2 - rects[i].y1; + } + + pixman_region32_fini(&flipped_damage); + + if (egl->exts.swap_buffers_with_damage_ext) { + ret = eglSwapBuffersWithDamageEXT(egl->display, surface, egl_damage, + nrects); + } else { + ret = eglSwapBuffersWithDamageKHR(egl->display, surface, egl_damage, + nrects); + } + } else { + ret = eglSwapBuffers(egl->display, surface); + } + + if (!ret) { + wlr_log(WLR_ERROR, "eglSwapBuffers failed"); + return false; + } + return true; +} + +EGLImageKHR wlr_egl_create_image_from_wl_drm(struct wlr_egl *egl, + struct wl_resource *data, EGLint *fmt, int *width, int *height, + bool *inverted_y) { + if (!egl->exts.bind_wayland_display_wl || !egl->exts.image_base_khr) { + return NULL; + } + + if (!eglQueryWaylandBufferWL(egl->display, data, EGL_TEXTURE_FORMAT, fmt)) { + return NULL; + } + + eglQueryWaylandBufferWL(egl->display, data, EGL_WIDTH, width); + eglQueryWaylandBufferWL(egl->display, data, EGL_HEIGHT, height); + + EGLint _inverted_y; + if (eglQueryWaylandBufferWL(egl->display, data, EGL_WAYLAND_Y_INVERTED_WL, + &_inverted_y)) { + *inverted_y = !!_inverted_y; + } else { + *inverted_y = false; + } + + const EGLint attribs[] = { + EGL_WAYLAND_PLANE_WL, 0, + EGL_NONE, + }; + return eglCreateImageKHR(egl->display, egl->context, EGL_WAYLAND_BUFFER_WL, + data, attribs); +} + +EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, + struct wlr_dmabuf_attributes *attributes) { + if (!egl->exts.image_base_khr || !egl->exts.image_dmabuf_import_ext) { + wlr_log(WLR_ERROR, "dmabuf import extension not present"); + return NULL; + } + + bool has_modifier = false; + + // we assume the same way we assumed formats without the import_modifiers + // extension that mod_linear is supported. The special mod mod_invalid + // is sometimes used to signal modifier unawareness which is what we + // have here + if (attributes->modifier != DRM_FORMAT_MOD_INVALID && + attributes->modifier != DRM_FORMAT_MOD_LINEAR) { + if (!egl->exts.image_dmabuf_import_modifiers_ext) { + wlr_log(WLR_ERROR, "dmabuf modifiers extension not present"); + return NULL; + } + has_modifier = true; + } + + unsigned int atti = 0; + EGLint attribs[50]; + attribs[atti++] = EGL_WIDTH; + attribs[atti++] = attributes->width; + attribs[atti++] = EGL_HEIGHT; + attribs[atti++] = attributes->height; + attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; + attribs[atti++] = attributes->format; + + struct { + EGLint fd; + EGLint offset; + EGLint pitch; + EGLint mod_lo; + EGLint mod_hi; + } attr_names[WLR_DMABUF_MAX_PLANES] = { + { + EGL_DMA_BUF_PLANE0_FD_EXT, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, + EGL_DMA_BUF_PLANE0_PITCH_EXT, + EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT + }, { + EGL_DMA_BUF_PLANE1_FD_EXT, + EGL_DMA_BUF_PLANE1_OFFSET_EXT, + EGL_DMA_BUF_PLANE1_PITCH_EXT, + EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT + }, { + EGL_DMA_BUF_PLANE2_FD_EXT, + EGL_DMA_BUF_PLANE2_OFFSET_EXT, + EGL_DMA_BUF_PLANE2_PITCH_EXT, + EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT + }, { + EGL_DMA_BUF_PLANE3_FD_EXT, + EGL_DMA_BUF_PLANE3_OFFSET_EXT, + EGL_DMA_BUF_PLANE3_PITCH_EXT, + EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT + } + }; + + for (int i=0; i < attributes->n_planes; i++) { + attribs[atti++] = attr_names[i].fd; + attribs[atti++] = attributes->fd[i]; + attribs[atti++] = attr_names[i].offset; + attribs[atti++] = attributes->offset[i]; + attribs[atti++] = attr_names[i].pitch; + attribs[atti++] = attributes->stride[i]; + if (has_modifier) { + attribs[atti++] = attr_names[i].mod_lo; + attribs[atti++] = attributes->modifier & 0xFFFFFFFF; + attribs[atti++] = attr_names[i].mod_hi; + attribs[atti++] = attributes->modifier >> 32; + } + } + attribs[atti++] = EGL_NONE; + assert(atti < sizeof(attribs)/sizeof(attribs[0])); + + return eglCreateImageKHR(egl->display, EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, NULL, attribs); +} + +int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl, + int **formats) { + if (!egl->exts.image_dmabuf_import_ext) { + wlr_log(WLR_DEBUG, "dmabuf import extension not present"); + return -1; + } + + // when we only have the image_dmabuf_import extension we can't query + // which formats are supported. These two are on almost always + // supported; it's the intended way to just try to create buffers. + // Just a guess but better than not supporting dmabufs at all, + // given that the modifiers extension isn't supported everywhere. + if (!egl->exts.image_dmabuf_import_modifiers_ext) { + static const int fallback_formats[] = { + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XRGB8888, + }; + static unsigned num = sizeof(fallback_formats) / + sizeof(fallback_formats[0]); + + *formats = calloc(num, sizeof(int)); + if (!*formats) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return -1; + } + + memcpy(*formats, fallback_formats, num * sizeof(**formats)); + return num; + } + + EGLint num; + if (!eglQueryDmaBufFormatsEXT(egl->display, 0, NULL, &num)) { + wlr_log(WLR_ERROR, "failed to query number of dmabuf formats"); + return -1; + } + + *formats = calloc(num, sizeof(int)); + if (*formats == NULL) { + wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno)); + return -1; + } + + if (!eglQueryDmaBufFormatsEXT(egl->display, num, *formats, &num)) { + wlr_log(WLR_ERROR, "failed to query dmabuf format"); + free(*formats); + return -1; + } + return num; +} + +int wlr_egl_get_dmabuf_modifiers(struct wlr_egl *egl, + int format, uint64_t **modifiers) { + if (!egl->exts.image_dmabuf_import_ext) { + wlr_log(WLR_DEBUG, "dmabuf extension not present"); + return -1; + } + + if(!egl->exts.image_dmabuf_import_modifiers_ext) { + *modifiers = NULL; + return 0; + } + + EGLint num; + if (!eglQueryDmaBufModifiersEXT(egl->display, format, 0, + NULL, NULL, &num)) { + wlr_log(WLR_ERROR, "failed to query dmabuf number of modifiers"); + return -1; + } + + *modifiers = calloc(num, sizeof(uint64_t)); + if (*modifiers == NULL) { + wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno)); + return -1; + } + + if (!eglQueryDmaBufModifiersEXT(egl->display, format, num, + *modifiers, NULL, &num)) { + wlr_log(WLR_ERROR, "failed to query dmabuf modifiers"); + free(*modifiers); + return -1; + } + return num; +} + +bool wlr_egl_export_image_to_dmabuf(struct wlr_egl *egl, EGLImageKHR image, + int32_t width, int32_t height, uint32_t flags, + struct wlr_dmabuf_attributes *attribs) { + memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes)); + + if (!egl->exts.image_dma_buf_export_mesa) { + return false; + } + + // Only one set of modifiers is returned for all planes + if (!eglExportDMABUFImageQueryMESA(egl->display, image, + (int *)&attribs->format, &attribs->n_planes, &attribs->modifier)) { + return false; + } + if (attribs->n_planes > WLR_DMABUF_MAX_PLANES) { + wlr_log(WLR_ERROR, "EGL returned %d planes, but only %d are supported", + attribs->n_planes, WLR_DMABUF_MAX_PLANES); + return false; + } + + if (!eglExportDMABUFImageMESA(egl->display, image, attribs->fd, + (EGLint *)attribs->stride, (EGLint *)attribs->offset)) { + return false; + } + + attribs->width = width; + attribs->height = height; + attribs->flags = flags; + return true; +} + +bool wlr_egl_destroy_surface(struct wlr_egl *egl, EGLSurface surface) { + if (!surface) { + return true; + } + return eglDestroySurface(egl->display, surface); +} diff --git a/render/glapi.txt b/render/glapi.txt new file mode 100644 index 00000000..b1166f27 --- /dev/null +++ b/render/glapi.txt @@ -0,0 +1,19 @@ +eglGetPlatformDisplayEXT +eglCreatePlatformWindowSurfaceEXT +-eglCreateImageKHR +-eglDestroyImageKHR +-eglQueryWaylandBufferWL +-eglBindWaylandDisplayWL +-eglUnbindWaylandDisplayWL +-glEGLImageTargetTexture2DOES +-eglSwapBuffersWithDamageEXT +-eglSwapBuffersWithDamageKHR +-eglQueryDmaBufFormatsEXT +-eglQueryDmaBufModifiersEXT +-eglExportDMABUFImageQueryMESA +-eglExportDMABUFImageMESA +-eglDebugMessageControlKHR +-glDebugMessageCallbackKHR +-glDebugMessageControlKHR +-glPopDebugGroupKHR +-glPushDebugGroupKHR diff --git a/render/gles2/pixel_format.c b/render/gles2/pixel_format.c new file mode 100644 index 00000000..a4b4c101 --- /dev/null +++ b/render/gles2/pixel_format.c @@ -0,0 +1,78 @@ +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include "render/gles2.h" + +/* + * The wayland formats are little endian while the GL formats are big endian, + * so WL_SHM_FORMAT_ARGB8888 is actually compatible with GL_BGRA_EXT. + */ +static const struct wlr_gles2_pixel_format formats[] = { + { + .wl_format = WL_SHM_FORMAT_ARGB8888, + .depth = 32, + .bpp = 32, + .gl_format = GL_BGRA_EXT, + .gl_type = GL_UNSIGNED_BYTE, + .has_alpha = true, + }, + { + .wl_format = WL_SHM_FORMAT_XRGB8888, + .depth = 24, + .bpp = 32, + .gl_format = GL_BGRA_EXT, + .gl_type = GL_UNSIGNED_BYTE, + .has_alpha = false, + }, + { + .wl_format = WL_SHM_FORMAT_XBGR8888, + .depth = 24, + .bpp = 32, + .gl_format = GL_RGBA, + .gl_type = GL_UNSIGNED_BYTE, + .has_alpha = false, + }, + { + .wl_format = WL_SHM_FORMAT_ABGR8888, + .depth = 32, + .bpp = 32, + .gl_format = GL_RGBA, + .gl_type = GL_UNSIGNED_BYTE, + .has_alpha = true, + }, +}; + +static const enum wl_shm_format wl_formats[] = { + WL_SHM_FORMAT_ARGB8888, + WL_SHM_FORMAT_XRGB8888, + WL_SHM_FORMAT_ABGR8888, + WL_SHM_FORMAT_XBGR8888, +}; + +// TODO: more pixel formats + +const struct wlr_gles2_pixel_format *get_gles2_format_from_wl( + enum wl_shm_format fmt) { + for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); ++i) { + if (formats[i].wl_format == fmt) { + return &formats[i]; + } + } + return NULL; +} + +const struct wlr_gles2_pixel_format *get_gles2_format_from_gl( + GLint gl_format, GLint gl_type, bool alpha) { + for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); ++i) { + if (formats[i].gl_format == gl_format && + formats[i].gl_type == gl_type && + formats[i].has_alpha == alpha) { + return &formats[i]; + } + } + return NULL; +} + +const enum wl_shm_format *get_gles2_wl_formats(size_t *len) { + *len = sizeof(wl_formats) / sizeof(wl_formats[0]); + return wl_formats; +} diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c new file mode 100644 index 00000000..50689ad4 --- /dev/null +++ b/render/gles2/renderer.c @@ -0,0 +1,655 @@ +#include <assert.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <wayland-server-protocol.h> +#include <wayland-util.h> +#include <wlr/render/egl.h> +#include <wlr/render/interface.h> +#include <wlr/render/wlr_renderer.h> +#include <wlr/types/wlr_matrix.h> +#include <wlr/util/log.h> +#include "glapi.h" +#include "render/gles2.h" + +static const struct wlr_renderer_impl renderer_impl; + +static struct wlr_gles2_renderer *gles2_get_renderer( + struct wlr_renderer *wlr_renderer) { + assert(wlr_renderer->impl == &renderer_impl); + return (struct wlr_gles2_renderer *)wlr_renderer; +} + +static struct wlr_gles2_renderer *gles2_get_renderer_in_context( + struct wlr_renderer *wlr_renderer) { + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + assert(wlr_egl_is_current(renderer->egl)); + return renderer; +} + +static void gles2_begin(struct wlr_renderer *wlr_renderer, uint32_t width, + uint32_t height) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer_in_context(wlr_renderer); + + PUSH_GLES2_DEBUG; + + glViewport(0, 0, width, height); + renderer->viewport_width = width; + renderer->viewport_height = height; + + // enable transparency + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + // XXX: maybe we should save output projection and remove some of the need + // for users to sling matricies themselves + + POP_GLES2_DEBUG; +} + +static void gles2_end(struct wlr_renderer *wlr_renderer) { + gles2_get_renderer_in_context(wlr_renderer); + // no-op +} + +static void gles2_clear(struct wlr_renderer *wlr_renderer, + const float color[static 4]) { + gles2_get_renderer_in_context(wlr_renderer); + + PUSH_GLES2_DEBUG; + glClearColor(color[0], color[1], color[2], color[3]); + glClear(GL_COLOR_BUFFER_BIT); + POP_GLES2_DEBUG; +} + +static void gles2_scissor(struct wlr_renderer *wlr_renderer, + struct wlr_box *box) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer_in_context(wlr_renderer); + + PUSH_GLES2_DEBUG; + if (box != NULL) { + struct wlr_box gl_box; + wlr_box_transform(&gl_box, box, WL_OUTPUT_TRANSFORM_FLIPPED_180, + renderer->viewport_width, renderer->viewport_height); + + glScissor(gl_box.x, gl_box.y, gl_box.width, gl_box.height); + glEnable(GL_SCISSOR_TEST); + } else { + glDisable(GL_SCISSOR_TEST); + } + POP_GLES2_DEBUG; +} + +static void draw_quad(void) { + GLfloat verts[] = { + 1, 0, // top right + 0, 0, // top left + 1, 1, // bottom right + 0, 1, // bottom left + }; + GLfloat texcoord[] = { + 1, 0, // top right + 0, 0, // top left + 1, 1, // bottom right + 0, 1, // bottom left + }; + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texcoord); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); +} + +static bool gles2_render_texture_with_matrix(struct wlr_renderer *wlr_renderer, + struct wlr_texture *wlr_texture, const float matrix[static 9], + float alpha) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer_in_context(wlr_renderer); + struct wlr_gles2_texture *texture = + gles2_get_texture(wlr_texture); + + struct wlr_gles2_tex_shader *shader = NULL; + GLenum target = 0; + + switch (texture->type) { + case WLR_GLES2_TEXTURE_GLTEX: + case WLR_GLES2_TEXTURE_WL_DRM_GL: + if (texture->has_alpha) { + shader = &renderer->shaders.tex_rgba; + } else { + shader = &renderer->shaders.tex_rgbx; + } + target = GL_TEXTURE_2D; + break; + case WLR_GLES2_TEXTURE_WL_DRM_EXT: + case WLR_GLES2_TEXTURE_DMABUF: + shader = &renderer->shaders.tex_ext; + target = GL_TEXTURE_EXTERNAL_OES; + + if (!renderer->exts.egl_image_external_oes) { + wlr_log(WLR_ERROR, "Failed to render texture: " + "GL_TEXTURE_EXTERNAL_OES not supported"); + return false; + } + break; + } + + // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set + // to GL_FALSE + float transposition[9]; + wlr_matrix_transpose(transposition, matrix); + + PUSH_GLES2_DEBUG; + + GLuint tex_id = texture->type == WLR_GLES2_TEXTURE_GLTEX ? + texture->gl_tex : texture->image_tex; + glActiveTexture(GL_TEXTURE0); + glBindTexture(target, tex_id); + + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glUseProgram(shader->program); + + glUniformMatrix3fv(shader->proj, 1, GL_FALSE, transposition); + glUniform1i(shader->invert_y, texture->inverted_y); + glUniform1i(shader->tex, 0); + glUniform1f(shader->alpha, alpha); + + draw_quad(); + + POP_GLES2_DEBUG; + return true; +} + + +static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, + const float color[static 4], const float matrix[static 9]) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer_in_context(wlr_renderer); + + // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set + // to GL_FALSE + float transposition[9]; + wlr_matrix_transpose(transposition, matrix); + + PUSH_GLES2_DEBUG; + glUseProgram(renderer->shaders.quad.program); + + glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, transposition); + glUniform4f(renderer->shaders.quad.color, color[0], color[1], color[2], color[3]); + draw_quad(); + POP_GLES2_DEBUG; +} + +static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer, + const float color[static 4], const float matrix[static 9]) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer_in_context(wlr_renderer); + + // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set + // to GL_FALSE + float transposition[9]; + wlr_matrix_transpose(transposition, matrix); + + PUSH_GLES2_DEBUG; + glUseProgram(renderer->shaders.ellipse.program); + + glUniformMatrix3fv(renderer->shaders.ellipse.proj, 1, GL_FALSE, transposition); + glUniform4f(renderer->shaders.ellipse.color, color[0], color[1], color[2], color[3]); + draw_quad(); + POP_GLES2_DEBUG; +} + +static const enum wl_shm_format *gles2_renderer_formats( + struct wlr_renderer *wlr_renderer, size_t *len) { + return get_gles2_wl_formats(len); +} + +static bool gles2_format_supported(struct wlr_renderer *wlr_renderer, + enum wl_shm_format wl_fmt) { + return get_gles2_format_from_wl(wl_fmt) != NULL; +} + +static bool gles2_resource_is_wl_drm_buffer(struct wlr_renderer *wlr_renderer, + struct wl_resource *resource) { + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + + if (!eglQueryWaylandBufferWL) { + return false; + } + + EGLint fmt; + return eglQueryWaylandBufferWL(renderer->egl->display, resource, + EGL_TEXTURE_FORMAT, &fmt); +} + +static void gles2_wl_drm_buffer_get_size(struct wlr_renderer *wlr_renderer, + struct wl_resource *buffer, int *width, int *height) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer(wlr_renderer); + + if (!eglQueryWaylandBufferWL) { + return; + } + + eglQueryWaylandBufferWL(renderer->egl->display, buffer, EGL_WIDTH, width); + eglQueryWaylandBufferWL(renderer->egl->display, buffer, EGL_HEIGHT, height); +} + +static int gles2_get_dmabuf_formats(struct wlr_renderer *wlr_renderer, + int **formats) { + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + return wlr_egl_get_dmabuf_formats(renderer->egl, formats); +} + +static int gles2_get_dmabuf_modifiers(struct wlr_renderer *wlr_renderer, + int format, uint64_t **modifiers) { + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + return wlr_egl_get_dmabuf_modifiers(renderer->egl, format, modifiers); +} + +static enum wl_shm_format gles2_preferred_read_format( + struct wlr_renderer *wlr_renderer) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer_in_context(wlr_renderer); + + GLint gl_format = -1, gl_type = -1; + PUSH_GLES2_DEBUG; + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &gl_format); + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &gl_type); + POP_GLES2_DEBUG; + + EGLint alpha_size = -1; + eglGetConfigAttrib(renderer->egl->display, renderer->egl->config, + EGL_ALPHA_SIZE, &alpha_size); + + const struct wlr_gles2_pixel_format *fmt = + get_gles2_format_from_gl(gl_format, gl_type, alpha_size > 0); + if (fmt != NULL) { + return fmt->wl_format; + } + + if (renderer->exts.read_format_bgra_ext) { + return WL_SHM_FORMAT_XRGB8888; + } + return WL_SHM_FORMAT_XBGR8888; +} + +static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, + enum wl_shm_format wl_fmt, uint32_t *flags, uint32_t stride, + uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, + uint32_t dst_x, uint32_t dst_y, void *data) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer_in_context(wlr_renderer); + + const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_wl(wl_fmt); + if (fmt == NULL) { + wlr_log(WLR_ERROR, "Cannot read pixels: unsupported pixel format"); + return false; + } + + if (fmt->gl_format == GL_BGRA_EXT && !renderer->exts.read_format_bgra_ext) { + wlr_log(WLR_ERROR, + "Cannot read pixels: missing GL_EXT_read_format_bgra extension"); + return false; + } + + PUSH_GLES2_DEBUG; + + // Make sure any pending drawing is finished before we try to read it + glFinish(); + + glGetError(); // Clear the error flag + + unsigned char *p = data + dst_y * stride; + uint32_t pack_stride = width * fmt->bpp / 8; + if (pack_stride == stride && dst_x == 0 && flags != NULL) { + // Under these particular conditions, we can read the pixels with only + // one glReadPixels call + glReadPixels(src_x, renderer->viewport_height - height - src_y, + width, height, fmt->gl_format, fmt->gl_type, p); + *flags = WLR_RENDERER_READ_PIXELS_Y_INVERT; + } else { + // Unfortunately GLES2 doesn't support GL_PACK_*, so we have to read + // the lines out row by row + for (size_t i = src_y; i < src_y + height; ++i) { + glReadPixels(src_x, src_y + height - i - 1, width, 1, fmt->gl_format, + fmt->gl_type, p + i * stride + dst_x * fmt->bpp / 8); + } + if (flags != NULL) { + *flags = 0; + } + } + + POP_GLES2_DEBUG; + + return glGetError() == GL_NO_ERROR; +} + +static struct wlr_texture *gles2_texture_from_pixels( + struct wlr_renderer *wlr_renderer, enum wl_shm_format wl_fmt, + uint32_t stride, uint32_t width, uint32_t height, const void *data) { + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + return wlr_gles2_texture_from_pixels(renderer->egl, wl_fmt, stride, width, + height, data); +} + +static struct wlr_texture *gles2_texture_from_wl_drm( + struct wlr_renderer *wlr_renderer, struct wl_resource *data) { + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + return wlr_gles2_texture_from_wl_drm(renderer->egl, data); +} + +static struct wlr_texture *gles2_texture_from_dmabuf( + struct wlr_renderer *wlr_renderer, + struct wlr_dmabuf_attributes *attribs) { + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + return wlr_gles2_texture_from_dmabuf(renderer->egl, attribs); +} + +static void gles2_init_wl_display(struct wlr_renderer *wlr_renderer, + struct wl_display *wl_display) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer(wlr_renderer); + if (!wlr_egl_bind_display(renderer->egl, wl_display)) { + wlr_log(WLR_INFO, "failed to bind wl_display to EGL"); + } +} + +static void gles2_destroy(struct wlr_renderer *wlr_renderer) { + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + + wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL); + + PUSH_GLES2_DEBUG; + glDeleteProgram(renderer->shaders.quad.program); + glDeleteProgram(renderer->shaders.ellipse.program); + glDeleteProgram(renderer->shaders.tex_rgba.program); + glDeleteProgram(renderer->shaders.tex_rgbx.program); + glDeleteProgram(renderer->shaders.tex_ext.program); + POP_GLES2_DEBUG; + + if (renderer->exts.debug_khr) { + glDisable(GL_DEBUG_OUTPUT_KHR); + glDebugMessageCallbackKHR(NULL, NULL); + } + + free(renderer); +} + +static const struct wlr_renderer_impl renderer_impl = { + .destroy = gles2_destroy, + .begin = gles2_begin, + .end = gles2_end, + .clear = gles2_clear, + .scissor = gles2_scissor, + .render_texture_with_matrix = gles2_render_texture_with_matrix, + .render_quad_with_matrix = gles2_render_quad_with_matrix, + .render_ellipse_with_matrix = gles2_render_ellipse_with_matrix, + .formats = gles2_renderer_formats, + .format_supported = gles2_format_supported, + .resource_is_wl_drm_buffer = gles2_resource_is_wl_drm_buffer, + .wl_drm_buffer_get_size = gles2_wl_drm_buffer_get_size, + .get_dmabuf_formats = gles2_get_dmabuf_formats, + .get_dmabuf_modifiers = gles2_get_dmabuf_modifiers, + .preferred_read_format = gles2_preferred_read_format, + .read_pixels = gles2_read_pixels, + .texture_from_pixels = gles2_texture_from_pixels, + .texture_from_wl_drm = gles2_texture_from_wl_drm, + .texture_from_dmabuf = gles2_texture_from_dmabuf, + .init_wl_display = gles2_init_wl_display, +}; + +void push_gles2_marker(const char *file, const char *func) { + if (!glPushDebugGroupKHR) { + return; + } + + int len = snprintf(NULL, 0, "%s:%s", file, func) + 1; + char str[len]; + snprintf(str, len, "%s:%s", file, func); + glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, 1, -1, str); +} + +void pop_gles2_marker(void) { + if (glPopDebugGroupKHR) { + glPopDebugGroupKHR(); + } +} + +static enum wlr_log_importance gles2_log_importance_to_wlr(GLenum type) { + switch (type) { + case GL_DEBUG_TYPE_ERROR_KHR: return WLR_ERROR; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR: return WLR_DEBUG; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR: return WLR_ERROR; + case GL_DEBUG_TYPE_PORTABILITY_KHR: return WLR_DEBUG; + case GL_DEBUG_TYPE_PERFORMANCE_KHR: return WLR_DEBUG; + case GL_DEBUG_TYPE_OTHER_KHR: return WLR_DEBUG; + case GL_DEBUG_TYPE_MARKER_KHR: return WLR_DEBUG; + case GL_DEBUG_TYPE_PUSH_GROUP_KHR: return WLR_DEBUG; + case GL_DEBUG_TYPE_POP_GROUP_KHR: return WLR_DEBUG; + default: return WLR_DEBUG; + } +} + +static void gles2_log(GLenum src, GLenum type, GLuint id, GLenum severity, + GLsizei len, const GLchar *msg, const void *user) { + _wlr_log(gles2_log_importance_to_wlr(type), "[GLES2] %s", msg); +} + +static GLuint compile_shader(GLuint type, const GLchar *src) { + PUSH_GLES2_DEBUG; + + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &src, NULL); + glCompileShader(shader); + + GLint ok; + glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); + if (ok == GL_FALSE) { + glDeleteShader(shader); + shader = 0; + } + + POP_GLES2_DEBUG; + return shader; +} + +static GLuint link_program(const GLchar *vert_src, const GLchar *frag_src) { + PUSH_GLES2_DEBUG; + + GLuint vert = compile_shader(GL_VERTEX_SHADER, vert_src); + if (!vert) { + goto error; + } + + GLuint frag = compile_shader(GL_FRAGMENT_SHADER, frag_src); + if (!frag) { + glDeleteShader(vert); + goto error; + } + + GLuint prog = glCreateProgram(); + glAttachShader(prog, vert); + glAttachShader(prog, frag); + glLinkProgram(prog); + + glDetachShader(prog, vert); + glDetachShader(prog, frag); + glDeleteShader(vert); + glDeleteShader(frag); + + GLint ok; + glGetProgramiv(prog, GL_LINK_STATUS, &ok); + if (ok == GL_FALSE) { + glDeleteProgram(prog); + goto error; + } + + POP_GLES2_DEBUG; + return prog; + +error: + POP_GLES2_DEBUG; + return 0; +} + +static bool check_gl_ext(const char *exts, const char *ext) { + size_t extlen = strlen(ext); + const char *end = exts + strlen(exts); + + while (exts < end) { + if (exts[0] == ' ') { + exts++; + continue; + } + size_t n = strcspn(exts, " "); + if (n == extlen && strncmp(ext, exts, n) == 0) { + return true; + } + exts += n; + } + return false; +} + +extern const GLchar quad_vertex_src[]; +extern const GLchar quad_fragment_src[]; +extern const GLchar ellipse_fragment_src[]; +extern const GLchar tex_vertex_src[]; +extern const GLchar tex_fragment_src_rgba[]; +extern const GLchar tex_fragment_src_rgbx[]; +extern const GLchar tex_fragment_src_external[]; + +struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { + if (!load_glapi()) { + return NULL; + } + + struct wlr_gles2_renderer *renderer = + calloc(1, sizeof(struct wlr_gles2_renderer)); + if (renderer == NULL) { + return NULL; + } + wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl); + + renderer->egl = egl; + if (!wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL)) { + free(renderer); + return NULL; + } + + renderer->exts_str = (const char *)glGetString(GL_EXTENSIONS); + wlr_log(WLR_INFO, "Using %s", glGetString(GL_VERSION)); + wlr_log(WLR_INFO, "GL vendor: %s", glGetString(GL_VENDOR)); + wlr_log(WLR_INFO, "Supported GLES2 extensions: %s", renderer->exts_str); + + if (!check_gl_ext(renderer->exts_str, "GL_EXT_texture_format_BGRA8888")) { + wlr_log(WLR_ERROR, "BGRA8888 format not supported by GLES2"); + free(renderer); + return NULL; + } + + renderer->exts.read_format_bgra_ext = + check_gl_ext(renderer->exts_str, "GL_EXT_read_format_bgra"); + renderer->exts.debug_khr = + check_gl_ext(renderer->exts_str, "GL_KHR_debug") && + glDebugMessageCallbackKHR && glDebugMessageControlKHR; + renderer->exts.egl_image_external_oes = + check_gl_ext(renderer->exts_str, "GL_OES_EGL_image_external") && + glEGLImageTargetTexture2DOES; + + if (renderer->exts.debug_khr) { + glEnable(GL_DEBUG_OUTPUT_KHR); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); + glDebugMessageCallbackKHR(gles2_log, NULL); + + // Silence unwanted message types + glDebugMessageControlKHR(GL_DONT_CARE, GL_DEBUG_TYPE_POP_GROUP_KHR, + GL_DONT_CARE, 0, NULL, GL_FALSE); + glDebugMessageControlKHR(GL_DONT_CARE, GL_DEBUG_TYPE_PUSH_GROUP_KHR, + GL_DONT_CARE, 0, NULL, GL_FALSE); + } + + PUSH_GLES2_DEBUG; + + GLuint prog; + renderer->shaders.quad.program = prog = + link_program(quad_vertex_src, quad_fragment_src); + if (!renderer->shaders.quad.program) { + goto error; + } + renderer->shaders.quad.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.quad.color = glGetUniformLocation(prog, "color"); + + renderer->shaders.ellipse.program = prog = + link_program(quad_vertex_src, ellipse_fragment_src); + if (!renderer->shaders.ellipse.program) { + goto error; + } + renderer->shaders.ellipse.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.ellipse.color = glGetUniformLocation(prog, "color"); + + renderer->shaders.tex_rgba.program = prog = + link_program(tex_vertex_src, tex_fragment_src_rgba); + if (!renderer->shaders.tex_rgba.program) { + goto error; + } + renderer->shaders.tex_rgba.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.tex_rgba.invert_y = glGetUniformLocation(prog, "invert_y"); + renderer->shaders.tex_rgba.tex = glGetUniformLocation(prog, "tex"); + renderer->shaders.tex_rgba.alpha = glGetUniformLocation(prog, "alpha"); + + renderer->shaders.tex_rgbx.program = prog = + link_program(tex_vertex_src, tex_fragment_src_rgbx); + if (!renderer->shaders.tex_rgbx.program) { + goto error; + } + renderer->shaders.tex_rgbx.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.tex_rgbx.invert_y = glGetUniformLocation(prog, "invert_y"); + renderer->shaders.tex_rgbx.tex = glGetUniformLocation(prog, "tex"); + renderer->shaders.tex_rgbx.alpha = glGetUniformLocation(prog, "alpha"); + + if (renderer->exts.egl_image_external_oes) { + renderer->shaders.tex_ext.program = prog = + link_program(tex_vertex_src, tex_fragment_src_external); + if (!renderer->shaders.tex_ext.program) { + goto error; + } + renderer->shaders.tex_ext.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.tex_ext.invert_y = glGetUniformLocation(prog, "invert_y"); + renderer->shaders.tex_ext.tex = glGetUniformLocation(prog, "tex"); + renderer->shaders.tex_ext.alpha = glGetUniformLocation(prog, "alpha"); + } + + POP_GLES2_DEBUG; + + return &renderer->wlr_renderer; + +error: + glDeleteProgram(renderer->shaders.quad.program); + glDeleteProgram(renderer->shaders.ellipse.program); + glDeleteProgram(renderer->shaders.tex_rgba.program); + glDeleteProgram(renderer->shaders.tex_rgbx.program); + glDeleteProgram(renderer->shaders.tex_ext.program); + + POP_GLES2_DEBUG; + + if (renderer->exts.debug_khr) { + glDisable(GL_DEBUG_OUTPUT_KHR); + glDebugMessageCallbackKHR(NULL, NULL); + } + + free(renderer); + return NULL; +} diff --git a/render/gles2/shaders.c b/render/gles2/shaders.c new file mode 100644 index 00000000..01410d87 --- /dev/null +++ b/render/gles2/shaders.c @@ -0,0 +1,88 @@ +#include <GLES2/gl2.h> +#include "render/gles2.h" + +// Colored quads +const GLchar quad_vertex_src[] = +"uniform mat3 proj;\n" +"uniform vec4 color;\n" +"attribute vec2 pos;\n" +"attribute vec2 texcoord;\n" +"varying vec4 v_color;\n" +"varying vec2 v_texcoord;\n" +"\n" +"void main() {\n" +" gl_Position = vec4(proj * vec3(pos, 1.0), 1.0);\n" +" v_color = color;\n" +" v_texcoord = texcoord;\n" +"}\n"; + +const GLchar quad_fragment_src[] = +"precision mediump float;\n" +"varying vec4 v_color;\n" +"varying vec2 v_texcoord;\n" +"\n" +"void main() {\n" +" gl_FragColor = v_color;\n" +"}\n"; + +// Colored ellipses +const GLchar ellipse_fragment_src[] = +"precision mediump float;\n" +"varying vec4 v_color;\n" +"varying vec2 v_texcoord;\n" +"\n" +"void main() {\n" +" float l = length(v_texcoord - vec2(0.5, 0.5));\n" +" if (l > 0.5) {\n" +" discard;\n" +" }\n" +" gl_FragColor = v_color;\n" +"}\n"; + +// Textured quads +const GLchar tex_vertex_src[] = +"uniform mat3 proj;\n" +"uniform bool invert_y;\n" +"attribute vec2 pos;\n" +"attribute vec2 texcoord;\n" +"varying vec2 v_texcoord;\n" +"\n" +"void main() {\n" +" gl_Position = vec4(proj * vec3(pos, 1.0), 1.0);\n" +" if (invert_y) {\n" +" v_texcoord = vec2(texcoord.s, 1.0 - texcoord.t);\n" +" } else {\n" +" v_texcoord = texcoord;\n" +" }\n" +"}\n"; + +const GLchar tex_fragment_src_rgba[] = +"precision mediump float;\n" +"varying vec2 v_texcoord;\n" +"uniform sampler2D tex;\n" +"uniform float alpha;\n" +"\n" +"void main() {\n" +" gl_FragColor = texture2D(tex, v_texcoord) * alpha;\n" +"}\n"; + +const GLchar tex_fragment_src_rgbx[] = +"precision mediump float;\n" +"varying vec2 v_texcoord;\n" +"uniform sampler2D tex;\n" +"uniform float alpha;\n" +"\n" +"void main() {\n" +" gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0) * alpha;\n" +"}\n"; + +const GLchar tex_fragment_src_external[] = +"#extension GL_OES_EGL_image_external : require\n\n" +"precision mediump float;\n" +"varying vec2 v_texcoord;\n" +"uniform samplerExternalOES texture0;\n" +"uniform float alpha;\n" +"\n" +"void main() {\n" +" gl_FragColor = texture2D(texture0, v_texcoord) * alpha;\n" +"}\n"; diff --git a/render/gles2/texture.c b/render/gles2/texture.c new file mode 100644 index 00000000..d035841e --- /dev/null +++ b/render/gles2/texture.c @@ -0,0 +1,304 @@ +#include <assert.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <stdint.h> +#include <stdlib.h> +#include <wayland-server-protocol.h> +#include <wayland-util.h> +#include <wlr/render/wlr_texture.h> +#include <wlr/render/egl.h> +#include <wlr/render/interface.h> +#include <wlr/types/wlr_matrix.h> +#include <wlr/util/log.h> +#include "glapi.h" +#include "render/gles2.h" +#include "util/signal.h" + +static const struct wlr_texture_impl texture_impl; + +struct wlr_gles2_texture *gles2_get_texture( + struct wlr_texture *wlr_texture) { + assert(wlr_texture->impl == &texture_impl); + return (struct wlr_gles2_texture *)wlr_texture; +} + +struct wlr_gles2_texture *get_gles2_texture_in_context( + struct wlr_texture *wlr_texture) { + struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); + if (!wlr_egl_is_current(texture->egl)) { + wlr_egl_make_current(texture->egl, EGL_NO_SURFACE, NULL); + } + return texture; +} + +static void gles2_texture_get_size(struct wlr_texture *wlr_texture, int *width, + int *height) { + struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); + *width = texture->width; + *height = texture->height; +} + +static bool gles2_texture_is_opaque(struct wlr_texture *wlr_texture) { + struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); + return !texture->has_alpha; +} + +static bool gles2_texture_write_pixels(struct wlr_texture *wlr_texture, + uint32_t stride, uint32_t width, uint32_t height, + uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, + const void *data) { + struct wlr_gles2_texture *texture = + get_gles2_texture_in_context(wlr_texture); + + if (texture->type != WLR_GLES2_TEXTURE_GLTEX) { + wlr_log(WLR_ERROR, "Cannot write pixels to immutable texture"); + return false; + } + + const struct wlr_gles2_pixel_format *fmt = + get_gles2_format_from_wl(texture->wl_format); + assert(fmt); + + // TODO: what if the unpack subimage extension isn't supported? + PUSH_GLES2_DEBUG; + + glBindTexture(GL_TEXTURE_2D, texture->gl_tex); + + glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / (fmt->bpp / 8)); + glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, src_x); + glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, src_y); + + glTexSubImage2D(GL_TEXTURE_2D, 0, dst_x, dst_y, width, height, + fmt->gl_format, fmt->gl_type, data); + + glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0); + + POP_GLES2_DEBUG; + return true; +} + +static bool gles2_texture_to_dmabuf(struct wlr_texture *wlr_texture, + struct wlr_dmabuf_attributes *attribs) { + struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); + + if (!texture->image) { + assert(texture->type == WLR_GLES2_TEXTURE_GLTEX); + + if (!eglCreateImageKHR) { + return false; + } + + texture->image = eglCreateImageKHR(texture->egl->display, + texture->egl->context, EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer)(uintptr_t)texture->gl_tex, NULL); + if (texture->image == EGL_NO_IMAGE_KHR) { + return false; + } + } + + uint32_t flags = 0; + if (texture->inverted_y) { + flags |= WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT; + } + + return wlr_egl_export_image_to_dmabuf(texture->egl, texture->image, + texture->width, texture->height, flags, attribs); +} + +static void gles2_texture_destroy(struct wlr_texture *wlr_texture) { + if (wlr_texture == NULL) { + return; + } + + struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); + + wlr_egl_make_current(texture->egl, EGL_NO_SURFACE, NULL); + + PUSH_GLES2_DEBUG; + + if (texture->image_tex) { + glDeleteTextures(1, &texture->image_tex); + } + wlr_egl_destroy_image(texture->egl, texture->image); + + if (texture->type == WLR_GLES2_TEXTURE_GLTEX) { + glDeleteTextures(1, &texture->gl_tex); + } + + POP_GLES2_DEBUG; + + free(texture); +} + +static const struct wlr_texture_impl texture_impl = { + .get_size = gles2_texture_get_size, + .is_opaque = gles2_texture_is_opaque, + .write_pixels = gles2_texture_write_pixels, + .to_dmabuf = gles2_texture_to_dmabuf, + .destroy = gles2_texture_destroy, +}; + +struct wlr_texture *wlr_gles2_texture_from_pixels(struct wlr_egl *egl, + enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, + uint32_t height, const void *data) { + if (!wlr_egl_is_current(egl)) { + wlr_egl_make_current(egl, EGL_NO_SURFACE, NULL); + } + + const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_wl(wl_fmt); + if (fmt == NULL) { + wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIu32, wl_fmt); + return NULL; + } + + struct wlr_gles2_texture *texture = + calloc(1, sizeof(struct wlr_gles2_texture)); + if (texture == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + wlr_texture_init(&texture->wlr_texture, &texture_impl); + texture->egl = egl; + texture->width = width; + texture->height = height; + texture->type = WLR_GLES2_TEXTURE_GLTEX; + texture->has_alpha = fmt->has_alpha; + texture->wl_format = fmt->wl_format; + + PUSH_GLES2_DEBUG; + + glGenTextures(1, &texture->gl_tex); + glBindTexture(GL_TEXTURE_2D, texture->gl_tex); + + glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / (fmt->bpp / 8)); + glTexImage2D(GL_TEXTURE_2D, 0, fmt->gl_format, width, height, 0, + fmt->gl_format, fmt->gl_type, data); + glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0); + + POP_GLES2_DEBUG; + return &texture->wlr_texture; +} + +struct wlr_texture *wlr_gles2_texture_from_wl_drm(struct wlr_egl *egl, + struct wl_resource *data) { + if (!wlr_egl_is_current(egl)) { + wlr_egl_make_current(egl, EGL_NO_SURFACE, NULL); + } + + if (!glEGLImageTargetTexture2DOES) { + return NULL; + } + + struct wlr_gles2_texture *texture = + calloc(1, sizeof(struct wlr_gles2_texture)); + if (texture == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + wlr_texture_init(&texture->wlr_texture, &texture_impl); + texture->egl = egl; + texture->wl_drm = data; + + EGLint fmt; + texture->wl_format = 0xFFFFFFFF; // texture can't be written anyways + texture->image = wlr_egl_create_image_from_wl_drm(egl, data, &fmt, + &texture->width, &texture->height, &texture->inverted_y); + if (texture->image == NULL) { + free(texture); + return NULL; + } + + GLenum target; + switch (fmt) { + case EGL_TEXTURE_RGB: + case EGL_TEXTURE_RGBA: + target = GL_TEXTURE_2D; + texture->type = WLR_GLES2_TEXTURE_WL_DRM_GL; + texture->has_alpha = fmt == EGL_TEXTURE_RGBA; + break; + case EGL_TEXTURE_EXTERNAL_WL: + target = GL_TEXTURE_EXTERNAL_OES; + texture->type = WLR_GLES2_TEXTURE_WL_DRM_EXT; + texture->has_alpha = true; + break; + default: + wlr_log(WLR_ERROR, "Invalid or unsupported EGL buffer format"); + free(texture); + return NULL; + } + + PUSH_GLES2_DEBUG; + + glGenTextures(1, &texture->image_tex); + glBindTexture(target, texture->image_tex); + glEGLImageTargetTexture2DOES(target, texture->image); + + POP_GLES2_DEBUG; + return &texture->wlr_texture; +} + +#ifndef DRM_FORMAT_BIG_ENDIAN +#define DRM_FORMAT_BIG_ENDIAN 0x80000000 +#endif + +struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl, + struct wlr_dmabuf_attributes *attribs) { + if (!wlr_egl_is_current(egl)) { + wlr_egl_make_current(egl, EGL_NO_SURFACE, NULL); + } + + if (!glEGLImageTargetTexture2DOES) { + return NULL; + } + + if (!egl->exts.image_dmabuf_import_ext) { + wlr_log(WLR_ERROR, "Cannot create DMA-BUF texture: EGL extension " + "unavailable"); + return NULL; + } + + switch (attribs->format & ~DRM_FORMAT_BIG_ENDIAN) { + case WL_SHM_FORMAT_YUYV: + case WL_SHM_FORMAT_YVYU: + case WL_SHM_FORMAT_UYVY: + case WL_SHM_FORMAT_VYUY: + case WL_SHM_FORMAT_AYUV: + // TODO: YUV based formats not yet supported, require multiple images + return false; + default: + break; + } + + struct wlr_gles2_texture *texture = + calloc(1, sizeof(struct wlr_gles2_texture)); + if (texture == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + wlr_texture_init(&texture->wlr_texture, &texture_impl); + texture->egl = egl; + texture->width = attribs->width; + texture->height = attribs->height; + texture->type = WLR_GLES2_TEXTURE_DMABUF; + texture->has_alpha = true; + texture->wl_format = 0xFFFFFFFF; // texture can't be written anyways + texture->inverted_y = + (attribs->flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT) != 0; + + texture->image = wlr_egl_create_image_from_dmabuf(egl, attribs); + if (texture->image == NULL) { + free(texture); + return NULL; + } + + PUSH_GLES2_DEBUG; + + glGenTextures(1, &texture->image_tex); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture->image_tex); + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, texture->image); + + POP_GLES2_DEBUG; + return &texture->wlr_texture; +} diff --git a/render/gles2/util.c b/render/gles2/util.c new file mode 100644 index 00000000..3ac777ee --- /dev/null +++ b/render/gles2/util.c @@ -0,0 +1,38 @@ +#include <GLES2/gl2.h> +#include <stdbool.h> +#include <stdlib.h> +#include <wlr/util/log.h> +#include "render/gles2.h" + +const char *gles2_strerror(GLenum err) { + switch (err) { + case GL_INVALID_ENUM: + return "Invalid enum"; + case GL_INVALID_VALUE: + return "Invalid value"; + case GL_INVALID_OPERATION: + return "Invalid operation"; + case GL_OUT_OF_MEMORY: + return "Out of memory"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "Invalid framebuffer operation"; + default: + return "Unknown error"; + } +} + +bool _gles2_flush_errors(const char *file, int line) { + GLenum err; + bool failure = false; + while ((err = glGetError()) != GL_NO_ERROR) { + failure = true; + if (err == GL_OUT_OF_MEMORY) { + // The OpenGL context is now undefined + _wlr_log(WLR_ERROR, "[%s:%d] Fatal GL error: out of memory", file, line); + exit(1); + } else { + _wlr_log(WLR_ERROR, "[%s:%d] GL error %d %s", file, line, err, gles2_strerror(err)); + } + } + return failure; +} diff --git a/render/meson.build b/render/meson.build new file mode 100644 index 00000000..e45ea90b --- /dev/null +++ b/render/meson.build @@ -0,0 +1,37 @@ +glgen = find_program('../glgen.sh') + +glapi = custom_target( + 'glapi', + input: 'glapi.txt', + output: ['@BASENAME@.c', '@BASENAME@.h'], + command: [glgen, '@INPUT@', '@OUTDIR@'], +) + +lib_wlr_render = static_library( + 'wlr_render', + files( + 'dmabuf.c', + 'egl.c', + 'gles2/pixel_format.c', + 'gles2/renderer.c', + 'gles2/shaders.c', + 'gles2/texture.c', + 'gles2/util.c', + 'wlr_renderer.c', + 'wlr_texture.c', + ), + glapi, + include_directories: wlr_inc, + dependencies: [ + egl, + drm.partial_dependency(compile_args: true), # <drm_fourcc.h> + glesv2, + pixman, + wayland_server + ], +) + +wlr_render = declare_dependency( + link_with: lib_wlr_render, + sources: glapi[1], +) diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c new file mode 100644 index 00000000..58731d7f --- /dev/null +++ b/render/wlr_renderer.c @@ -0,0 +1,226 @@ +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <wlr/render/gles2.h> +#include <wlr/render/interface.h> +#include <wlr/render/wlr_renderer.h> +#include <wlr/types/wlr_linux_dmabuf_v1.h> +#include <wlr/types/wlr_matrix.h> +#include <wlr/util/log.h> +#include "util/signal.h" + +void wlr_renderer_init(struct wlr_renderer *renderer, + const struct wlr_renderer_impl *impl) { + assert(impl->begin); + assert(impl->clear); + assert(impl->scissor); + assert(impl->render_texture_with_matrix); + assert(impl->render_quad_with_matrix); + assert(impl->render_ellipse_with_matrix); + assert(impl->formats); + assert(impl->format_supported); + assert(impl->texture_from_pixels); + renderer->impl = impl; + + wl_signal_init(&renderer->events.destroy); +} + +void wlr_renderer_destroy(struct wlr_renderer *r) { + if (!r) { + return; + } + wlr_signal_emit_safe(&r->events.destroy, r); + + if (r->impl && r->impl->destroy) { + r->impl->destroy(r); + } else { + free(r); + } +} + +void wlr_renderer_begin(struct wlr_renderer *r, int width, int height) { + r->impl->begin(r, width, height); +} + +void wlr_renderer_end(struct wlr_renderer *r) { + if (r->impl->end) { + r->impl->end(r); + } +} + +void wlr_renderer_clear(struct wlr_renderer *r, const float color[static 4]) { + r->impl->clear(r, color); +} + +void wlr_renderer_scissor(struct wlr_renderer *r, struct wlr_box *box) { + r->impl->scissor(r, box); +} + +bool wlr_render_texture(struct wlr_renderer *r, struct wlr_texture *texture, + const float projection[static 9], int x, int y, float alpha) { + struct wlr_box box = { .x = x, .y = y }; + wlr_texture_get_size(texture, &box.width, &box.height); + + float matrix[9]; + wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, + projection); + + return wlr_render_texture_with_matrix(r, texture, matrix, alpha); +} + +bool wlr_render_texture_with_matrix(struct wlr_renderer *r, + struct wlr_texture *texture, const float matrix[static 9], + float alpha) { + return r->impl->render_texture_with_matrix(r, texture, matrix, alpha); +} + +void wlr_render_rect(struct wlr_renderer *r, const struct wlr_box *box, + const float color[static 4], const float projection[static 9]) { + float matrix[9]; + wlr_matrix_project_box(matrix, box, WL_OUTPUT_TRANSFORM_NORMAL, 0, + projection); + + wlr_render_quad_with_matrix(r, color, matrix); +} + +void wlr_render_quad_with_matrix(struct wlr_renderer *r, + const float color[static 4], const float matrix[static 9]) { + r->impl->render_quad_with_matrix(r, color, matrix); +} + +void wlr_render_ellipse(struct wlr_renderer *r, const struct wlr_box *box, + const float color[static 4], const float projection[static 9]) { + float matrix[9]; + wlr_matrix_project_box(matrix, box, WL_OUTPUT_TRANSFORM_NORMAL, 0, + projection); + + wlr_render_ellipse_with_matrix(r, color, matrix); +} + +void wlr_render_ellipse_with_matrix(struct wlr_renderer *r, + const float color[static 4], const float matrix[static 9]) { + r->impl->render_ellipse_with_matrix(r, color, matrix); +} + +const enum wl_shm_format *wlr_renderer_get_formats( + struct wlr_renderer *r, size_t *len) { + return r->impl->formats(r, len); +} + +bool wlr_renderer_resource_is_wl_drm_buffer(struct wlr_renderer *r, + struct wl_resource *resource) { + if (!r->impl->resource_is_wl_drm_buffer) { + return false; + } + return r->impl->resource_is_wl_drm_buffer(r, resource); +} + +void wlr_renderer_wl_drm_buffer_get_size(struct wlr_renderer *r, + struct wl_resource *buffer, int *width, int *height) { + if (!r->impl->wl_drm_buffer_get_size) { + return; + } + return r->impl->wl_drm_buffer_get_size(r, buffer, width, height); +} + +int wlr_renderer_get_dmabuf_formats(struct wlr_renderer *r, + int **formats) { + if (!r->impl->get_dmabuf_formats) { + return -1; + } + return r->impl->get_dmabuf_formats(r, formats); +} + +int wlr_renderer_get_dmabuf_modifiers(struct wlr_renderer *r, int format, + uint64_t **modifiers) { + if (!r->impl->get_dmabuf_modifiers) { + return -1; + } + return r->impl->get_dmabuf_modifiers(r, format, modifiers); +} + +bool wlr_renderer_read_pixels(struct wlr_renderer *r, enum wl_shm_format fmt, + uint32_t *flags, uint32_t stride, uint32_t width, uint32_t height, + uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, + void *data) { + if (!r->impl->read_pixels) { + return false; + } + return r->impl->read_pixels(r, fmt, flags, stride, width, height, + src_x, src_y, dst_x, dst_y, data); +} + +bool wlr_renderer_format_supported(struct wlr_renderer *r, + enum wl_shm_format fmt) { + return r->impl->format_supported(r, fmt); +} + +void wlr_renderer_init_wl_display(struct wlr_renderer *r, + struct wl_display *wl_display) { + if (wl_display_init_shm(wl_display)) { + wlr_log(WLR_ERROR, "Failed to initialize shm"); + return; + } + + size_t len; + const enum wl_shm_format *formats = wlr_renderer_get_formats(r, &len); + if (formats == NULL) { + wlr_log(WLR_ERROR, "Failed to initialize shm: cannot get formats"); + return; + } + + for (size_t i = 0; i < len; ++i) { + // These formats are already added by default + if (formats[i] != WL_SHM_FORMAT_ARGB8888 && + formats[i] != WL_SHM_FORMAT_XRGB8888) { + wl_display_add_shm_format(wl_display, formats[i]); + } + } + + if (r->impl->texture_from_dmabuf) { + wlr_linux_dmabuf_v1_create(wl_display, r); + } + + if (r->impl->init_wl_display) { + r->impl->init_wl_display(r, wl_display); + } +} + +struct wlr_renderer *wlr_renderer_autocreate(struct wlr_egl *egl, + EGLenum platform, void *remote_display, EGLint *config_attribs, + EGLint visual_id) { + // Append GLES2-specific bits to the provided EGL config attributes + EGLint gles2_config_attribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE, + }; + + size_t config_attribs_len = 0; // not including terminating EGL_NONE + while (config_attribs != NULL && + config_attribs[config_attribs_len] != EGL_NONE) { + ++config_attribs_len; + } + + size_t all_config_attribs_len = config_attribs_len + + sizeof(gles2_config_attribs) / sizeof(gles2_config_attribs[0]); + EGLint all_config_attribs[all_config_attribs_len]; + if (config_attribs_len > 0) { + memcpy(all_config_attribs, config_attribs, + config_attribs_len * sizeof(EGLint)); + } + memcpy(&all_config_attribs[config_attribs_len], gles2_config_attribs, + sizeof(gles2_config_attribs)); + + if (!wlr_egl_init(egl, platform, remote_display, all_config_attribs, + visual_id)) { + wlr_log(WLR_ERROR, "Could not initialize EGL"); + return NULL; + } + + struct wlr_renderer *renderer = wlr_gles2_renderer_create(egl); + if (!renderer) { + wlr_egl_finish(egl); + } + + return renderer; +} diff --git a/render/wlr_texture.c b/render/wlr_texture.c new file mode 100644 index 00000000..833032c9 --- /dev/null +++ b/render/wlr_texture.c @@ -0,0 +1,71 @@ +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <wlr/render/interface.h> +#include <wlr/render/wlr_texture.h> + +void wlr_texture_init(struct wlr_texture *texture, + const struct wlr_texture_impl *impl) { + assert(impl->get_size); + assert(impl->write_pixels); + texture->impl = impl; +} + +void wlr_texture_destroy(struct wlr_texture *texture) { + if (texture && texture->impl && texture->impl->destroy) { + texture->impl->destroy(texture); + } else { + free(texture); + } +} + +struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer, + enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, + uint32_t height, const void *data) { + return renderer->impl->texture_from_pixels(renderer, wl_fmt, stride, width, + height, data); +} + +struct wlr_texture *wlr_texture_from_wl_drm(struct wlr_renderer *renderer, + struct wl_resource *data) { + if (!renderer->impl->texture_from_wl_drm) { + return NULL; + } + return renderer->impl->texture_from_wl_drm(renderer, data); +} + +struct wlr_texture *wlr_texture_from_dmabuf(struct wlr_renderer *renderer, + struct wlr_dmabuf_attributes *attribs) { + if (!renderer->impl->texture_from_dmabuf) { + return NULL; + } + return renderer->impl->texture_from_dmabuf(renderer, attribs); +} + +void wlr_texture_get_size(struct wlr_texture *texture, int *width, + int *height) { + return texture->impl->get_size(texture, width, height); +} + +bool wlr_texture_is_opaque(struct wlr_texture *texture) { + if (!texture->impl->is_opaque) { + return false; + } + return texture->impl->is_opaque(texture); +} + +bool wlr_texture_write_pixels(struct wlr_texture *texture, + uint32_t stride, uint32_t width, uint32_t height, + uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, + const void *data) { + return texture->impl->write_pixels(texture, stride, width, height, + src_x, src_y, dst_x, dst_y, data); +} + +bool wlr_texture_to_dmabuf(struct wlr_texture *texture, + struct wlr_dmabuf_attributes *attribs) { + if (!texture->impl->to_dmabuf) { + return false; + } + return texture->impl->to_dmabuf(texture, attribs); +} |