summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Reider <[email protected]>2024-01-06 02:31:14 +0100
committerGitHub <[email protected]>2024-01-06 02:31:14 +0100
commit6759e8da7ab53a46b0eb04e5045b8c67262c3a11 (patch)
treec65ed83ca04b61bdbae7e1b8a7f2c16f29b89730
parentb929a2bbadf467864796ad4ec90882ce86cfebff (diff)
parentace97585b2b4d8cbb5ead6cd0f72fa8e8889c9d7 (diff)
Merge pull request #24 from wlrfx/wlroots-0.17-rebase
Rebase to wlroots 0.17
-rw-r--r--include/meson.build1
-rw-r--r--include/render/egl.h117
-rw-r--r--include/render/fx_renderer/fx_renderer.h230
-rw-r--r--include/render/fx_renderer/fx_stencilbuffer.h20
-rw-r--r--include/render/fx_renderer/matrix.h8
-rw-r--r--include/render/fx_renderer/shaders.h65
-rw-r--r--include/render/fx_renderer/util.h11
-rw-r--r--include/render/pass.h69
-rw-r--r--include/render/pixel_format.h60
-rw-r--r--include/types/fx/shadow_data.h3
-rw-r--r--include/types/wlr_buffer.h69
-rw-r--r--include/types/wlr_output.h10
-rw-r--r--include/types/wlr_scene.h2
-rw-r--r--include/util/env.h14
-rw-r--r--include/util/time.h8
-rw-r--r--include/wlr/types/wlr_scene.h152
-rw-r--r--meson.build53
-rw-r--r--render/egl.c1021
-rw-r--r--render/fx_renderer/fx_framebuffer.c137
-rw-r--r--render/fx_renderer/fx_pass.c509
-rw-r--r--render/fx_renderer/fx_renderer.c1016
-rw-r--r--render/fx_renderer/fx_stencilbuffer.c50
-rw-r--r--render/fx_renderer/fx_texture.c368
-rw-r--r--render/fx_renderer/gles2/shaders/box_shadow.frag5
-rw-r--r--render/fx_renderer/gles2/shaders/common.vert9
-rw-r--r--render/fx_renderer/gles2/shaders/quad.frag8
-rw-r--r--render/fx_renderer/gles2/shaders/stencil_mask.frag5
-rw-r--r--render/fx_renderer/gles2/shaders/tex.frag5
-rw-r--r--render/fx_renderer/meson.build8
-rw-r--r--render/fx_renderer/pixel_format.c175
-rw-r--r--render/fx_renderer/shaders.c203
-rw-r--r--render/fx_renderer/util.c113
-rw-r--r--render/meson.build1
-rw-r--r--render/pixel_format.c398
-rw-r--r--tinywl/tinywl.c411
-rw-r--r--types/buffer/buffer.c43
-rw-r--r--types/fx/shadow_data.c5
-rw-r--r--types/meson.build6
-rw-r--r--types/output/wlr_output.c24
-rw-r--r--types/scene/layer_shell_v1.c186
-rw-r--r--types/scene/output_layout.c157
-rw-r--r--types/scene/subsurface_tree.c259
-rw-r--r--types/scene/surface.c211
-rw-r--r--types/scene/wlr_scene.c934
-rw-r--r--types/scene/xdg_shell.c124
-rw-r--r--util/env.c2
-rw-r--r--util/time.c6
47 files changed, 4876 insertions, 2415 deletions
diff --git a/include/meson.build b/include/meson.build
deleted file mode 100644
index 4c388f0..0000000
--- a/include/meson.build
+++ /dev/null
@@ -1 +0,0 @@
-exclude_files = ['meson.build', 'config.h.in', 'version.h.in']
diff --git a/include/render/egl.h b/include/render/egl.h
new file mode 100644
index 0000000..e8b8596
--- /dev/null
+++ b/include/render/egl.h
@@ -0,0 +1,117 @@
+#ifndef RENDER_EGL_H
+#define RENDER_EGL_H
+
+#include <wlr/render/egl.h>
+
+struct wlr_egl {
+ EGLDisplay display;
+ EGLContext context;
+ EGLDeviceEXT device; // may be EGL_NO_DEVICE_EXT
+ struct gbm_device *gbm_device;
+
+ struct {
+ // Display extensions
+ bool KHR_image_base;
+ bool EXT_image_dma_buf_import;
+ bool EXT_image_dma_buf_import_modifiers;
+ bool IMG_context_priority;
+ bool EXT_create_context_robustness;
+
+ // Device extensions
+ bool EXT_device_drm;
+ bool EXT_device_drm_render_node;
+
+ // Client extensions
+ bool EXT_device_query;
+ bool KHR_platform_gbm;
+ bool EXT_platform_device;
+ bool KHR_display_reference;
+ } exts;
+
+ struct {
+ PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
+ PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
+ PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
+ PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT;
+ PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT;
+ PFNEGLDEBUGMESSAGECONTROLKHRPROC eglDebugMessageControlKHR;
+ PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT;
+ PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT;
+ PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT;
+ } procs;
+
+ bool has_modifiers;
+ struct wlr_drm_format_set dmabuf_texture_formats;
+ struct wlr_drm_format_set dmabuf_render_formats;
+};
+
+struct wlr_egl_context {
+ EGLDisplay display;
+ EGLContext context;
+ EGLSurface draw_surface;
+ EGLSurface read_surface;
+};
+
+/**
+ * Initializes an EGL context for the given DRM FD.
+ *
+ * Will attempt to load all possibly required API functions.
+ */
+struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd);
+
+/**
+ * Frees all related EGL resources, makes the context not-current and
+ * unbinds a bound wayland display.
+ */
+void wlr_egl_destroy(struct wlr_egl *egl);
+
+/**
+ * Creates an EGL image from the given dmabuf attributes. Check usability
+ * of the dmabuf with wlr_egl_check_import_dmabuf once first.
+ */
+EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl,
+ struct wlr_dmabuf_attributes *attributes, bool *external_only);
+
+/**
+ * Get DMA-BUF formats suitable for sampling usage.
+ */
+const struct wlr_drm_format_set *wlr_egl_get_dmabuf_texture_formats(
+ struct wlr_egl *egl);
+/**
+ * Get DMA-BUF formats suitable for rendering usage.
+ */
+const struct wlr_drm_format_set *wlr_egl_get_dmabuf_render_formats(
+ struct wlr_egl *egl);
+
+/**
+ * Destroys an EGL image created with the given wlr_egl.
+ */
+bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImageKHR image);
+
+int wlr_egl_dup_drm_fd(struct wlr_egl *egl);
+
+/**
+ * Save the current EGL context to the structure provided in the argument.
+ *
+ * This includes display, context, draw surface and read surface.
+ */
+void wlr_egl_save_context(struct wlr_egl_context *context);
+
+/**
+ * Restore EGL context that was previously saved using wlr_egl_save_current().
+ */
+bool wlr_egl_restore_context(struct wlr_egl_context *context);
+
+/**
+ * Make the EGL context current.
+ *
+ * Callers are expected to clear the current context when they are done by
+ * calling wlr_egl_unset_current().
+ */
+bool wlr_egl_make_current(struct wlr_egl *egl);
+
+bool wlr_egl_unset_current(struct wlr_egl *egl);
+
+bool wlr_egl_is_current(struct wlr_egl *egl);
+
+#endif
diff --git a/include/render/fx_renderer/fx_renderer.h b/include/render/fx_renderer/fx_renderer.h
index f569aa9..4d99866 100644
--- a/include/render/fx_renderer/fx_renderer.h
+++ b/include/render/fx_renderer/fx_renderer.h
@@ -4,72 +4,141 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <stdbool.h>
+#include <time.h>
#include <wlr/render/egl.h>
+#include <wlr/render/interface.h>
#include <wlr/render/wlr_texture.h>
#include <wlr/util/addon.h>
#include <wlr/util/box.h>
-#include "render/fx_renderer/fx_stencilbuffer.h"
+
+#include "render/fx_renderer/shaders.h"
+#include "render/pass.h"
#include "types/fx/shadow_data.h"
-enum fx_tex_shader_source {
- SHADER_SOURCE_TEXTURE_RGBA = 1,
- SHADER_SOURCE_TEXTURE_RGBX = 2,
- SHADER_SOURCE_TEXTURE_EXTERNAL = 3,
+struct fx_pixel_format {
+ uint32_t drm_format;
+ // optional field, if empty then internalformat = format
+ GLint gl_internalformat;
+ GLint gl_format, gl_type;
+ bool has_alpha;
};
-struct quad_shader {
- GLuint program;
- GLint proj;
- GLint color;
- GLint pos_attrib;
-};
+bool is_fx_pixel_format_supported(const struct fx_renderer *renderer,
+ const struct fx_pixel_format *format);
+const struct fx_pixel_format *get_fx_format_from_drm(uint32_t fmt);
+const struct fx_pixel_format *get_fx_format_from_gl(
+ GLint gl_format, GLint gl_type, bool alpha);
+const uint32_t *get_fx_shm_formats(const struct fx_renderer *renderer,
+ size_t *len);
+
+///
+/// fx_framebuffer
+///
+
+struct fx_framebuffer {
+ struct wlr_buffer *buffer;
+ struct fx_renderer *renderer;
+ struct wl_list link; // fx_renderer.buffers
+
+ EGLImageKHR image;
+ GLuint rbo;
+ GLuint fbo;
+ GLuint sb; // Stencil
-struct tex_shader {
- GLuint program;
- GLint proj;
- GLint tex;
- GLint alpha;
- GLint pos_attrib;
- GLint tex_attrib;
- GLint size;
- GLint position;
- GLint radius;
+ struct wlr_addon addon;
};
-struct stencil_mask_shader {
- GLuint program;
- GLint proj;
- GLint color;
- GLint pos_attrib;
- GLint half_size;
- GLint position;
- GLint radius;
+struct fx_framebuffer *fx_framebuffer_get_or_create(struct fx_renderer *renderer,
+ struct wlr_buffer *wlr_buffer);
+
+void fx_framebuffer_bind(struct fx_framebuffer *buffer);
+
+void fx_framebuffer_bind_wlr_fbo(struct fx_renderer *renderer);
+
+void fx_framebuffer_destroy(struct fx_framebuffer *buffer);
+
+///
+/// fx_texture
+///
+
+struct fx_texture {
+ struct wlr_texture wlr_texture;
+ struct fx_renderer *fx_renderer;
+ struct wl_list link; // fx_renderer.textures
+
+ // Basically:
+ // GL_TEXTURE_2D == mutable
+ // GL_TEXTURE_EXTERNAL_OES == immutable
+ GLuint target;
+ GLuint tex;
+
+ EGLImageKHR image;
+
+ bool has_alpha;
+
+ // Only affects target == GL_TEXTURE_2D
+ uint32_t drm_format; // used to interpret upload data
+ // If imported from a wlr_buffer
+ struct wlr_buffer *buffer;
+ struct wlr_addon buffer_addon;
};
-struct box_shadow_shader {
- GLuint program;
- GLint proj;
- GLint color;
- GLint pos_attrib;
- GLint position;
- GLint size;
- GLint blur_sigma;
- GLint corner_radius;
+struct fx_texture_attribs {
+ GLenum target; /* either GL_TEXTURE_2D or GL_TEXTURE_EXTERNAL_OES */
+ GLuint tex;
+
+ bool has_alpha;
};
-struct fx_renderer {
- float projection[9];
+struct fx_texture *fx_get_texture(struct wlr_texture *wlr_texture);
- struct fx_stencilbuffer stencil_buffer;
+struct wlr_texture *fx_texture_from_buffer(struct wlr_renderer *wlr_renderer,
+ struct wlr_buffer *buffer);
- struct wlr_addon addon;
+void fx_texture_destroy(struct fx_texture *texture);
+
+bool wlr_texture_is_fx(struct wlr_texture *wlr_texture);
+void fx_texture_get_attribs(struct wlr_texture *texture,
+ struct fx_texture_attribs *attribs);
+
+///
+/// fx_renderer
+///
+
+struct fx_renderer {
+ struct wlr_renderer wlr_renderer;
+
+ float projection[9];
+ struct wlr_egl *egl;
+ int drm_fd;
+
+ const char *exts_str;
struct {
+ bool EXT_read_format_bgra;
+ bool KHR_debug;
bool OES_egl_image_external;
+ bool OES_egl_image;
+ bool EXT_texture_type_2_10_10_10_REV;
+ bool OES_texture_half_float_linear;
+ bool EXT_texture_norm16;
+ bool EXT_disjoint_timer_query;
} exts;
struct {
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
+ PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR;
+ PFNGLDEBUGMESSAGECONTROLKHRPROC glDebugMessageControlKHR;
+ PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR;
+ PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR;
+ PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES;
+ PFNGLGETGRAPHICSRESETSTATUSKHRPROC glGetGraphicsResetStatusKHR;
+ PFNGLGENQUERIESEXTPROC glGenQueriesEXT;
+ PFNGLDELETEQUERIESEXTPROC glDeleteQueriesEXT;
+ PFNGLQUERYCOUNTEREXTPROC glQueryCounterEXT;
+ PFNGLGETQUERYOBJECTIVEXTPROC glGetQueryObjectivEXT;
+ PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT;
+ PFNGLGETINTEGER64VEXTPROC glGetInteger64vEXT;
} procs;
struct {
@@ -80,48 +149,47 @@ struct fx_renderer {
struct box_shadow_shader box_shadow;
struct stencil_mask_shader stencil_mask;
} shaders;
-};
-
-void fx_renderer_init_addon(struct wlr_egl *egl, struct wlr_addon_set *addons,
- const void * owner);
-
-struct fx_renderer *fx_renderer_addon_find(struct wlr_addon_set *addons,
- const void * owner);
-
-struct fx_renderer *fx_renderer_create(struct wlr_egl *egl);
-void fx_renderer_fini(struct fx_renderer *renderer);
+ struct wl_list buffers; // fx_framebuffer.link
+ struct wl_list textures; // fx_texture.link
-void fx_renderer_begin(struct fx_renderer *renderer, int width, int height);
-
-void fx_renderer_clear(const float color[static 4]);
-
-void fx_renderer_scissor(struct wlr_box *box);
-
-// Initialize the stenciling work
-void fx_renderer_stencil_mask_init(void);
-
-// Close the mask
-void fx_renderer_stencil_mask_close(bool draw_inside_mask);
-
-// Finish stenciling and clear the buffer
-void fx_renderer_stencil_mask_fini(void);
-
-void fx_renderer_stencil_enable(void);
-
-void fx_renderer_stencil_disable(void);
-
-bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer,
- struct wlr_texture *wlr_texture, const struct wlr_fbox *src_box,
- const struct wlr_box *dst_box, const float matrix[static 9],
- float opacity, int corner_radius);
+ struct fx_framebuffer *current_buffer;
+ uint32_t viewport_width, viewport_height;
+};
-void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box,
- const float color[static 4], const float projection[static 9]);
+bool wlr_renderer_is_fx(struct wlr_renderer *wlr_renderer);
+
+struct fx_renderer *fx_get_renderer(
+ struct wlr_renderer *wlr_renderer);
+struct fx_render_timer *fx_get_render_timer(
+ struct wlr_render_timer *timer);
+struct fx_texture *fx_get_texture(
+ struct wlr_texture *wlr_texture);
+
+struct wlr_renderer *fx_renderer_create_with_drm_fd(int drm_fd);
+struct wlr_renderer *fx_renderer_create(struct wlr_backend *backend);
+struct wlr_renderer *fx_renderer_create_egl(struct wlr_egl *egl);
+
+struct wlr_egl *wlr_fx_renderer_get_egl(struct wlr_renderer *renderer);
+
+void push_fx_debug_(struct fx_renderer *renderer,
+ const char *file, const char *func);
+#define push_fx_debug(renderer) push_fx_debug_(renderer, _WLR_FILENAME, __func__)
+void pop_fx_debug(struct fx_renderer *renderer);
+
+///
+/// Render Timer
+///
+
+struct fx_render_timer {
+ struct wlr_render_timer base;
+ struct fx_renderer *renderer;
+ struct timespec cpu_start;
+ struct timespec cpu_end;
+ GLuint id;
+ GLint64 gl_cpu_end;
+};
-void fx_render_box_shadow(struct fx_renderer *renderer,
- const struct wlr_box *box, const struct wlr_box *stencil_box,
- const float matrix[static 9], int corner_radius,
- struct shadow_data *shadow_data);
+bool wlr_render_timer_is_fx(struct wlr_render_timer *timer);
#endif
diff --git a/include/render/fx_renderer/fx_stencilbuffer.h b/include/render/fx_renderer/fx_stencilbuffer.h
deleted file mode 100644
index 6909f96..0000000
--- a/include/render/fx_renderer/fx_stencilbuffer.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef FX_STENCILBUFFER_H
-#define FX_STENCILBUFFER_H
-
-#include <GLES2/gl2.h>
-#include <stdbool.h>
-#include <wlr/render/wlr_texture.h>
-
-struct fx_stencilbuffer {
- GLuint rb;
- int width;
- int height;
-};
-
-struct fx_stencilbuffer fx_stencilbuffer_create(void);
-
-void fx_stencilbuffer_init(struct fx_stencilbuffer *stencil_buffer, int width, int height);
-
-void fx_stencilbuffer_release(struct fx_stencilbuffer *stencil_buffer);
-
-#endif
diff --git a/include/render/fx_renderer/matrix.h b/include/render/fx_renderer/matrix.h
index 6931e8d..c3bae42 100644
--- a/include/render/fx_renderer/matrix.h
+++ b/include/render/fx_renderer/matrix.h
@@ -3,7 +3,13 @@
#include <wlr/types/wlr_output.h>
+/**
+ * Writes a 2D orthographic projection matrix to mat of (width, height) with a
+ * specified wl_output_transform.
+ *
+ * Equivalent to glOrtho(0, width, 0, height, 1, -1) with the transform applied.
+ */
void matrix_projection(float mat[static 9], int width, int height,
- enum wl_output_transform transform);
+ enum wl_output_transform transform);
#endif
diff --git a/include/render/fx_renderer/shaders.h b/include/render/fx_renderer/shaders.h
new file mode 100644
index 0000000..92a14d5
--- /dev/null
+++ b/include/render/fx_renderer/shaders.h
@@ -0,0 +1,65 @@
+#ifndef _FX_SHADERS_H
+#define _FX_SHADERS_H
+
+#include <GLES2/gl2.h>
+#include <stdbool.h>
+
+struct fx_renderer;
+
+GLuint compile_shader(GLuint type, const GLchar *src);
+
+GLuint link_program(const GLchar *frag_src);
+
+bool check_gl_ext(const char *exts, const char *ext);
+
+void load_gl_proc(void *proc_ptr, const char *name);
+
+enum fx_tex_shader_source {
+ SHADER_SOURCE_TEXTURE_RGBA = 1,
+ SHADER_SOURCE_TEXTURE_RGBX = 2,
+ SHADER_SOURCE_TEXTURE_EXTERNAL = 3,
+};
+
+struct quad_shader {
+ GLuint program;
+ GLint proj;
+ GLint color;
+ GLint pos_attrib;
+};
+
+struct tex_shader {
+ GLuint program;
+ GLint proj;
+ GLint tex_proj;
+ GLint tex;
+ GLint alpha;
+ GLint pos_attrib;
+ GLint size;
+ GLint position;
+ GLint radius;
+};
+
+struct stencil_mask_shader {
+ GLuint program;
+ GLint proj;
+ GLint color;
+ GLint pos_attrib;
+ GLint half_size;
+ GLint position;
+ GLint radius;
+};
+
+struct box_shadow_shader {
+ GLuint program;
+ GLint proj;
+ GLint color;
+ GLint pos_attrib;
+ GLint position;
+ GLint size;
+ GLint blur_sigma;
+ GLint corner_radius;
+};
+
+bool link_shaders(struct fx_renderer *renderer);
+
+#endif
diff --git a/include/render/fx_renderer/util.h b/include/render/fx_renderer/util.h
new file mode 100644
index 0000000..c0afc69
--- /dev/null
+++ b/include/render/fx_renderer/util.h
@@ -0,0 +1,11 @@
+#ifndef _FX_UTIL_H
+#define _FX_UTIL_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wlr/backend/interface.h>
+
+bool open_preferred_drm_fd(struct wlr_backend *backend, int *drm_fd_ptr,
+ bool *own_drm_fd);
+
+#endif
diff --git a/include/render/pass.h b/include/render/pass.h
new file mode 100644
index 0000000..05bf1e9
--- /dev/null
+++ b/include/render/pass.h
@@ -0,0 +1,69 @@
+#ifndef FX_RENDER_PASS_H
+#define FX_RENDER_PASS_H
+
+#include <stdbool.h>
+#include <wlr/render/pass.h>
+#include <wlr/util/box.h>
+#include <wlr/render/interface.h>
+#include "types/fx/shadow_data.h"
+
+struct fx_gles_render_pass {
+ struct wlr_render_pass base;
+ struct fx_framebuffer *buffer;
+ float projection_matrix[9];
+ struct fx_render_timer *timer;
+};
+
+/**
+ * Begin a new render pass with the supplied destination buffer.
+ *
+ * Callers must call wlr_render_pass_submit() once they are done with the
+ * render pass.
+ */
+struct fx_gles_render_pass *fx_renderer_begin_buffer_pass(struct wlr_renderer *renderer,
+ struct wlr_buffer *buffer, const struct wlr_buffer_pass_options *options);
+
+struct fx_render_texture_options {
+ struct wlr_render_texture_options base;
+ float scale;
+ struct wlr_box *clip_box; // Used to clip csd. Ignored if NULL
+ int corner_radius;
+};
+
+struct fx_render_texture_options fx_render_texture_options_default(
+ const struct wlr_render_texture_options *base);
+
+struct fx_render_rect_options {
+ struct wlr_render_rect_options base;
+ float scale;
+};
+
+struct fx_render_rect_options fx_render_rect_options_default(
+ const struct wlr_render_rect_options *base);
+
+/**
+ * Render a fx texture.
+ */
+void fx_render_pass_add_texture(struct fx_gles_render_pass *render_pass,
+ const struct fx_render_texture_options *options);
+
+/**
+ * Render a rectangle.
+ */
+void fx_render_pass_add_rect(struct fx_gles_render_pass *render_pass,
+ const struct fx_render_rect_options *options);
+
+/**
+ * Render a stencil mask.
+ */
+void fx_render_pass_add_stencil_mask(struct fx_gles_render_pass *pass,
+ const struct fx_render_rect_options *fx_options, int corner_radius);
+
+/**
+ * Render a box shadow.
+ */
+void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass,
+ const struct fx_render_rect_options *fx_options,
+ int corner_radius, struct shadow_data *shadow_data);
+
+#endif
diff --git a/include/render/pixel_format.h b/include/render/pixel_format.h
index d045b6a..6ca11f3 100644
--- a/include/render/pixel_format.h
+++ b/include/render/pixel_format.h
@@ -1,26 +1,64 @@
-#ifndef RENDER_PIXEL_FORMAT_H
-#define RENDER_PIXEL_FORMAT_H
+#ifndef FX_RENDER_PIXEL_FORMAT_H
+#define FX_RENDER_PIXEL_FORMAT_H
#include <wayland-server-protocol.h>
+/**
+ * Information about a pixel format.
+ *
+ * A pixel format is identified via its DRM four character code (see <drm_fourcc.h>).
+ *
+ * Simple formats have a block size of 1×1 pixels and bytes_per_block contains
+ * the number of bytes per pixel (including padding).
+ *
+ * Tiled formats (e.g. sub-sampled YCbCr) are described with a block size
+ * greater than 1×1 pixels. A block is a rectangle of pixels which are stored
+ * next to each other in a byte-aligned memory region.
+ */
struct wlr_pixel_format_info {
- uint32_t drm_format;
+ uint32_t drm_format;
- /* Equivalent of the format if it has an alpha channel,
- * DRM_FORMAT_INVALID (0) if NA
- */
- uint32_t opaque_substitute;
+ /* Equivalent of the format if it has an alpha channel,
+ * DRM_FORMAT_INVALID (0) if NA
+ */
+ uint32_t opaque_substitute;
- /* Bits per pixels */
- uint32_t bpp;
+ /* Bytes per block (including padding) */
+ uint32_t bytes_per_block;
+ /* Size of a block in pixels (zero for 1×1) */
+ uint32_t block_width, block_height;
- /* True if the format has an alpha channel */
- bool has_alpha;
+ /* True if the format has an alpha channel */
+ bool has_alpha;
};
+/**
+ * Get pixel format information from a DRM FourCC.
+ *
+ * NULL is returned if the pixel format is unknown.
+ */
const struct wlr_pixel_format_info *drm_get_pixel_format_info(uint32_t fmt);
+/**
+ * Get the number of pixels per block for a pixel format.
+ */
+uint32_t pixel_format_info_pixels_per_block(const struct wlr_pixel_format_info *info);
+/**
+ * Get the minimum stride for a given pixel format and width.
+ */
+int32_t pixel_format_info_min_stride(const struct wlr_pixel_format_info *info, int32_t width);
+/**
+ * Check whether a stride is large enough for a given pixel format and width.
+ */
+bool pixel_format_info_check_stride(const struct wlr_pixel_format_info *info,
+ int32_t stride, int32_t width);
+/**
+ * Convert an enum wl_shm_format to a DRM FourCC.
+ */
uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt);
+/**
+ * Convert a DRM FourCC to an enum wl_shm_format.
+ */
enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt);
#endif
diff --git a/include/types/fx/shadow_data.h b/include/types/fx/shadow_data.h
index 804acfe..d96a084 100644
--- a/include/types/fx/shadow_data.h
+++ b/include/types/fx/shadow_data.h
@@ -3,10 +3,11 @@
#include <stdbool.h>
#include <wlr/util/addon.h>
+#include <wlr/render/pass.h>
struct shadow_data {
bool enabled;
- float *color;
+ struct wlr_render_color color;
float blur_sigma;
};
diff --git a/include/types/wlr_buffer.h b/include/types/wlr_buffer.h
index 59d78e9..016cae8 100644
--- a/include/types/wlr_buffer.h
+++ b/include/types/wlr_buffer.h
@@ -3,75 +3,6 @@
#include <wlr/types/wlr_buffer.h>
-struct wlr_shm_client_buffer {
- struct wlr_buffer base;
-
- uint32_t format;
- size_t stride;
-
- // The following fields are NULL if the client has destroyed the wl_buffer
- struct wl_resource *resource;
- struct wl_shm_buffer *shm_buffer;
-
- // This is used to keep the backing storage alive after the client has
- // destroyed the wl_buffer
- struct wl_shm_pool *saved_shm_pool;
- void *saved_data;
-
- struct wl_listener resource_destroy;
- struct wl_listener release;
-};
-
-struct wlr_shm_client_buffer *shm_client_buffer_get_or_create(
- struct wl_resource *resource);
-
-/**
- * A read-only buffer that holds a data pointer.
- *
- * This is suitable for passing raw pixel data to a function that accepts a
- * wlr_buffer.
- */
-struct wlr_readonly_data_buffer {
- struct wlr_buffer base;
-
- const void *data;
- uint32_t format;
- size_t stride;
-
- void *saved_data;
-};
-
-/**
- * Wraps a read-only data pointer into a wlr_buffer. The data pointer may be
- * accessed until readonly_data_buffer_drop() is called.
- */
-struct wlr_readonly_data_buffer *readonly_data_buffer_create(uint32_t format,
- size_t stride, uint32_t width, uint32_t height, const void *data);
-/**
- * Drops ownership of the buffer (see wlr_buffer_drop() for more details) and
- * perform a copy of the data pointer if a consumer still has the buffer locked.
- */
-bool readonly_data_buffer_drop(struct wlr_readonly_data_buffer *buffer);
-
-struct wlr_dmabuf_buffer {
- struct wlr_buffer base;
- struct wlr_dmabuf_attributes dmabuf;
- bool saved;
-};
-
-/**
- * Wraps a DMA-BUF into a wlr_buffer. The DMA-BUF may be accessed until
- * dmabuf_buffer_drop() is called.
- */
-struct wlr_dmabuf_buffer *dmabuf_buffer_create(
- struct wlr_dmabuf_attributes *dmabuf);
-/**
- * Drops ownership of the buffer (see wlr_buffer_drop() for more details) and
- * takes a reference to the DMA-BUF (by dup'ing its file descriptors) if a
- * consumer still has the buffer locked.
- */
-bool dmabuf_buffer_drop(struct wlr_dmabuf_buffer *buffer);
-
/**
* Check whether a buffer is fully opaque.
*
diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h
new file mode 100644
index 0000000..b239b6a
--- /dev/null
+++ b/include/types/wlr_output.h
@@ -0,0 +1,10 @@
+#ifndef TYPES_WLR_OUTPUT_H
+#define TYPES_WLR_OUTPUT_H
+
+#include <wlr/render/drm_format_set.h>
+#include <wlr/types/wlr_output.h>
+
+void output_pending_resolution(struct wlr_output *output,
+ const struct wlr_output_state *state, int *width, int *height);
+
+#endif
diff --git a/include/types/wlr_scene.h b/include/types/wlr_scene.h
index 64c11bc..80dcfd1 100644
--- a/include/types/wlr_scene.h
+++ b/include/types/wlr_scene.h
@@ -5,4 +5,6 @@
struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node);
+void scene_surface_set_clip(struct wlr_scene_surface *surface, struct wlr_box *clip);
+
#endif
diff --git a/include/util/env.h b/include/util/env.h
index 6720fa8..e271f4b 100644
--- a/include/util/env.h
+++ b/include/util/env.h
@@ -4,8 +4,20 @@
#include <stdbool.h>
#include <unistd.h>
+/**
+ * Parse a bool from an environment variable.
+ *
+ * On success, the parsed value is returned. On error, false is returned.
+ */
bool env_parse_bool(const char *option);
-ssize_t env_parse_switch(const char *option, const char **switches);
+/**
+ * Pick a choice from an environment variable.
+ *
+ * On success, the choice index is returned. On error, zero is returned.
+ *
+ * switches is a NULL-terminated array.
+ */
+size_t env_parse_switch(const char *option, const char **switches);
#endif
diff --git a/include/util/time.h b/include/util/time.h
index 287698d..3f76aa4 100644
--- a/include/util/time.h
+++ b/include/util/time.h
@@ -1,12 +1,13 @@
#ifndef UTIL_TIME_H
#define UTIL_TIME_H
+#include <stdint.h>
#include <time.h>
/**
* Get the current time, in milliseconds.
*/
-uint32_t get_current_time_msec(void);
+int64_t get_current_time_msec(void);
/**
* Convert a timespec to milliseconds.
@@ -14,6 +15,11 @@ uint32_t get_current_time_msec(void);
int64_t timespec_to_msec(const struct timespec *a);
/**
+ * Convert a timespec to nanoseconds.
+ */
+int64_t timespec_to_nsec(const struct timespec *a);
+
+/**
* Convert nanoseconds to a timespec.
*/
void timespec_from_nsec(struct timespec *r, int64_t nsec);
diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h
index 7b4c002..f52e167 100644
--- a/include/wlr/types/wlr_scene.h
+++ b/include/wlr/types/wlr_scene.h
@@ -20,21 +20,33 @@
*/
#include <pixman.h>
+#include <time.h>
#include <wayland-server-core.h>
-#include <wlr/types/wlr_compositor.h>
+#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_damage_ring.h>
#include "types/fx/shadow_data.h"
+#include <wlr/types/wlr_linux_dmabuf_v1.h>
+#include <wlr/util/addon.h>
+#include <wlr/util/box.h>
struct wlr_output;
struct wlr_output_layout;
+struct wlr_output_layout_output;
struct wlr_xdg_surface;
struct wlr_layer_surface_v1;
+struct wlr_drag_icon;
+struct wlr_surface;
struct wlr_scene_node;
struct wlr_scene_buffer;
+struct wlr_scene_output_layout;
+
+struct wlr_presentation;
+struct wlr_linux_dmabuf_v1;
+struct wlr_output_state;
typedef bool (*wlr_scene_buffer_point_accepts_input_func_t)(
- struct wlr_scene_buffer *buffer, int sx, int sy);
+ struct wlr_scene_buffer *buffer, double *sx, double *sy);
typedef void (*wlr_scene_buffer_iterator_func_t)(
struct wlr_scene_buffer *buffer, int sx, int sy, void *user_data);
@@ -89,10 +101,12 @@ struct wlr_scene {
// May be NULL
struct wlr_presentation *presentation;
+ struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1;
// private state
struct wl_listener presentation_destroy;
+ struct wl_listener linux_dmabuf_v1_destroy;
enum wlr_scene_debug_damage_option debug_damage_option;
bool direct_scanout;
@@ -106,11 +120,14 @@ struct wlr_scene_surface {
// private state
+ struct wlr_box clip;
+
struct wlr_addon addon;
+ struct wl_listener outputs_update;
struct wl_listener output_enter;
struct wl_listener output_leave;
- struct wl_listener output_present;
+ struct wl_listener output_sample;
struct wl_listener frame_done;
struct wl_listener surface_destroy;
struct wl_listener surface_commit;
@@ -123,6 +140,16 @@ struct wlr_scene_rect {
float color[4];
};
+struct wlr_scene_outputs_update_event {
+ struct wlr_scene_output **active;
+ size_t size;
+};
+
+struct wlr_scene_output_sample_event {
+ struct wlr_scene_output *output;
+ bool direct_scanout;
+};
+
/** A scene-graph node displaying a buffer */
struct wlr_scene_buffer {
struct wlr_scene_node node;
@@ -131,9 +158,10 @@ struct wlr_scene_buffer {
struct wlr_buffer *buffer;
struct {
+ struct wl_signal outputs_update; // struct wlr_scene_outputs_update_event
struct wl_signal output_enter; // struct wlr_scene_output
struct wl_signal output_leave; // struct wlr_scene_output
- struct wl_signal output_present; // struct wlr_scene_output
+ struct wl_signal output_sample; // struct wlr_scene_output_sample_event
struct wl_signal frame_done; // struct timespec
} events;
@@ -148,18 +176,20 @@ struct wlr_scene_buffer {
*/
struct wlr_scene_output *primary_output;
- // private state
-
float opacity;
int corner_radius;
struct shadow_data shadow_data;
-
- uint64_t active_outputs;
- struct wlr_texture *texture;
+ enum wlr_scale_filter_mode filter_mode;
struct wlr_fbox src_box;
int dst_width, dst_height;
enum wl_output_transform transform;
pixman_region32_t opaque_region;
+
+ // private state
+
+ uint64_t active_outputs;
+ struct wlr_texture *texture;
+ struct wlr_linux_dmabuf_feedback_v1_init_options prev_feedback_options;
};
/** A viewport for an output in the scene-graph */
@@ -183,7 +213,6 @@ struct wlr_scene_output {
bool prev_scanout;
struct wl_listener output_commit;
- struct wl_listener output_mode;
struct wl_listener output_damage;
struct wl_listener output_needs_frame;
@@ -192,6 +221,11 @@ struct wlr_scene_output {
struct wl_array render_list;
};
+struct wlr_scene_timer {
+ int64_t pre_render_duration;
+ struct wlr_render_timer *render_timer;
+};
+
/** A layer shell scene helper */
struct wlr_scene_layer_surface_v1 {
struct wlr_scene_tree *tree;
@@ -269,6 +303,7 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node,
* Create a new scene-graph.
*/
struct wlr_scene *wlr_scene_create(void);
+
/**
* Handle presentation feedback for all surfaces in the scene, assuming that
* scene outputs and the scene rendering functions are used.
@@ -279,6 +314,15 @@ void wlr_scene_set_presentation(struct wlr_scene *scene,
struct wlr_presentation *presentation);
/**
+ * Handles linux_dmabuf_v1 feedback for all surfaces in the scene.
+ *
+ * Asserts that a struct wlr_linux_dmabuf_v1 hasn't already been set for the scene.
+ */
+void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene,
+ struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1);
+
+
+/**
* Add a node displaying nothing but its children.
*/
struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent);
@@ -295,13 +339,29 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent);
struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent,
struct wlr_surface *surface);
+/**
+ * If this node represents a wlr_scene_buffer, that buffer will be returned. It
+ * is not legal to feed a node that does not represent a wlr_scene_buffer.
+ */
struct wlr_scene_buffer *wlr_scene_buffer_from_node(struct wlr_scene_node *node);
/**
+ * If this node represents a wlr_scene_tree, that tree will be returned. It
+ * is not legal to feed a node that does not represent a wlr_scene_tree.
+ */
+struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node);
+
+/**
+ * If this node represents a wlr_scene_rect, that rect will be returned. It
+ * is not legal to feed a node that does not represent a wlr_scene_rect.
+ */
+struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node);
+
+/**
* If this buffer is backed by a surface, then the struct wlr_scene_surface is
* returned. If not, NULL will be returned.
*/
-struct wlr_scene_surface *wlr_scene_surface_from_buffer(
+struct wlr_scene_surface *wlr_scene_surface_try_from_buffer(
struct wlr_scene_buffer *scene_buffer);
/**
@@ -343,14 +403,14 @@ void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer,
* the whole buffer node will be damaged.
*/
void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer,
- struct wlr_buffer *buffer, pixman_region32_t *region);
+ struct wlr_buffer *buffer, const pixman_region32_t *region);
/**
* Sets the buffer's opaque region. This is an optimization hint used to
* determine if buffers which reside under this one need to be rendered or not.
*/
void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer,
- pixman_region32_t *region);
+ const pixman_region32_t *region);
/**
* Set the source rectangle describing the region of the buffer which will be
@@ -384,6 +444,12 @@ void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer,
float opacity);
/**
+* Sets the filter mode to use when scaling the buffer
+*/
+void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer,
+ enum wlr_scale_filter_mode filter_mode);
+
+/**
* Sets the corner radius of this buffer
*/
void wlr_scene_buffer_set_corner_radius(struct wlr_scene_buffer *scene_buffer,
@@ -417,10 +483,32 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output);
*/
void wlr_scene_output_set_position(struct wlr_scene_output *scene_output,
int lx, int ly);
+
+struct wlr_scene_output_state_options {
+ struct wlr_scene_timer *timer;
+};
+
/**
* Render and commit an output.
*/
-bool wlr_scene_output_commit(struct wlr_scene_output *scene_output);
+bool wlr_scene_output_commit(struct wlr_scene_output *scene_output,
+ const struct wlr_scene_output_state_options *options);
+
+/**
+ * Render and populate given output state.
+ */
+bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
+ struct wlr_output_state *state, const struct wlr_scene_output_state_options *options);
+
+/**
+ * Retrieve the duration in nanoseconds between the last wlr_scene_output_commit() call and the end
+ * of its operations, including those on the GPU that may have finished after the call returned.
+ *
+ * Returns -1 if the duration is unavailable.
+ */
+int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer);
+void wlr_scene_timer_finish(struct wlr_scene_timer *timer);
+
/**
* Call wlr_surface_send_frame_done() on all surfaces in the scene rendered by
* wlr_scene_output_commit() for which wlr_scene_surface.primary_output
@@ -446,15 +534,24 @@ struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene,
/**
* Attach an output layout to a scene.
*
- * Adding, removing, or repositioning an output in the output layout
- * will respectively add, remove or reposition a corresponding
- * scene-graph output. When the output layout is destroyed, scene-graph
- * outputs which were created by this helper will be destroyed.
+ * The resulting scene output layout allows to synchronize the positions of scene
+ * outputs with the positions of corresponding layout outputs.
+ *
+ * It is automatically destroyed when the scene or the output layout is destroyed.
*/
-bool wlr_scene_attach_output_layout(struct wlr_scene *scene,
+struct wlr_scene_output_layout *wlr_scene_attach_output_layout(struct wlr_scene *scene,
struct wlr_output_layout *output_layout);
/**
+ * Add an output to the scene output layout.
+ *
+ * When the layout output is repositioned, the scene output will be repositioned
+ * accordingly.
+ */
+void wlr_scene_output_layout_add_output(struct wlr_scene_output_layout *sol,
+ struct wlr_output_layout_output *lo, struct wlr_scene_output *so);
+
+/**
* Add a node displaying a surface and all of its sub-surfaces to the
* scene-graph.
*/
@@ -462,6 +559,16 @@ struct wlr_scene_tree *wlr_scene_subsurface_tree_create(
struct wlr_scene_tree *parent, struct wlr_surface *surface);
/**
+ * Sets a cropping region for any subsurface trees that are children of this
+ * scene node. The clip coordinate space will be that of the root surface of
+ * the subsurface tree.
+ *
+ * A NULL or empty clip will disable clipping
+ */
+void wlr_scene_subsurface_tree_set_clip(struct wlr_scene_node *node,
+ struct wlr_box *clip);
+
+/**
* Add a node displaying an xdg_surface and all of its sub-surfaces to the
* scene-graph.
*
@@ -495,4 +602,11 @@ void wlr_scene_layer_surface_v1_configure(
struct wlr_scene_layer_surface_v1 *scene_layer_surface,
const struct wlr_box *full_area, struct wlr_box *usable_area);
+/**
+ * Add a node displaying a drag icon and all its sub-surfaces to the
+ * scene-graph.
+ */
+struct wlr_scene_tree *wlr_scene_drag_icon_create(
+ struct wlr_scene_tree *parent, struct wlr_drag_icon *drag_icon);
+
#endif
diff --git a/meson.build b/meson.build
index e96f2e6..070a07a 100644
--- a/meson.build
+++ b/meson.build
@@ -80,34 +80,20 @@ if cc.has_argument('-fmacro-prefix-map=/prefix/to/hide=')
)
else
add_project_arguments(
- '-DWLR_REL_SRC_DIR="@0@"'.format(relative_dir),
+ '-D_WLR_REL_SRC_DIR="@0@"'.format(relative_dir),
language: 'c',
)
endif
-features = {
- 'drm-backend': false,
- 'x11-backend': false,
- 'libinput-backend': false,
- 'xwayland': false,
- 'gles2-renderer': false,
- 'vulkan-renderer': false,
- 'gbm-allocator': false,
-}
-internal_features = {
- 'xcb-errors': false,
- 'egl': false,
-}
-
wayland_project_options = ['tests=false', 'documentation=false']
wayland_server = dependency('wayland-server',
- version: '>=1.21',
+ version: '>=1.22',
fallback: 'wayland',
default_options: wayland_project_options,
)
wlroots_options = [ 'examples=false' ]
-wlroots_version = ['>=0.16.0', '<0.17.0']
+wlroots_version = ['>=0.17.0', '<0.18.0']
subproject(
'wlroots',
default_options: wlroots_options,
@@ -120,7 +106,7 @@ wlroots = dependency('wlroots',
)
drm = dependency('libdrm',
- version: '>=2.4.113',
+ version: '>=2.4.114',
fallback: 'libdrm',
default_options: [
'intel=disabled',
@@ -140,9 +126,11 @@ drm = dependency('libdrm',
'tests=false',
],
)
-xkbcommon = dependency('xkbcommon')
-udev = dependency('libudev')
-pixman = dependency('pixman-1')
+pixman = dependency('pixman-1',
+ version: '>=0.42.0',
+ fallback: 'pixman',
+ default_options: ['werror=false'],
+)
math = cc.find_library('m')
rt = cc.find_library('rt')
@@ -151,8 +139,6 @@ wlr_deps = [
wlroots,
wayland_server,
drm,
- xkbcommon,
- udev,
pixman,
math,
rt,
@@ -164,43 +150,25 @@ subdir('render')
subdir('types')
subdir('util')
-subdir('include')
-
-foreach name, have : internal_features
- add_project_arguments(
- '-DHAS_@0@=@1@'.format(name.underscorify().to_upper(), have.to_int()),
- language: 'c',
- )
-endforeach
-
scenefx_inc = include_directories('include')
-proto_inc = include_directories('protocol')
lib_scenefx = library(
meson.project_name(), wlr_files,
soversion: soversion.to_string(),
dependencies: wlr_deps,
- include_directories: [ scenefx_inc, proto_inc ],
+ include_directories: [ scenefx_inc ],
install: true,
)
-wlr_vars = {}
-foreach name, have : features
- wlr_vars += { 'have_' + name.underscorify(): have.to_string() }
-endforeach
-
scenefx = declare_dependency(
link_with: lib_scenefx,
dependencies: wlr_deps,
include_directories: scenefx_inc,
- variables: wlr_vars,
)
meson.override_dependency('scenefx', scenefx)
-summary(features + internal_features, bool_yn: true)
-
if get_option('examples')
# TODO: subdir('examples')
subdir('tinywl')
@@ -212,5 +180,4 @@ pkgconfig.generate(lib_scenefx,
filebase: meson.project_name(),
name: meson.project_name(),
description: 'Wlroots effects library',
- variables: wlr_vars,
)
diff --git a/render/egl.c b/render/egl.c
new file mode 100644
index 0000000..162634b
--- /dev/null
+++ b/render/egl.c
@@ -0,0 +1,1021 @@
+#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
+#include <drm_fourcc.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <gbm.h>
+#include <wlr/render/egl.h>
+#include <wlr/util/log.h>
+#include <wlr/util/region.h>
+#include <xf86drm.h>
+#include "render/egl.h"
+#include "util/env.h"
+
+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 const char *egl_error_str(EGLint error) {
+ switch (error) {
+ case EGL_SUCCESS:
+ return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_DEVICE_EXT:
+ return "EGL_BAD_DEVICE_EXT";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ }
+ return "unknown error";
+}
+
+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] command: %s, error: %s (0x%x), message: \"%s\"",
+ command, egl_error_str(error), error, 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 load_egl_proc(void *proc_ptr, const char *name) {
+ void *proc = (void *)eglGetProcAddress(name);
+ if (proc == NULL) {
+ wlr_log(WLR_ERROR, "eglGetProcAddress(%s) failed", name);
+ abort();
+ }
+ *(void **)proc_ptr = proc;
+}
+
+static int get_egl_dmabuf_formats(struct wlr_egl *egl, EGLint **formats);
+static int get_egl_dmabuf_modifiers(struct wlr_egl *egl, EGLint format,
+ uint64_t **modifiers, EGLBoolean **external_only);
+
+static void log_modifier(uint64_t modifier, bool external_only) {
+ char *mod_name = drmGetFormatModifierName(modifier);
+ wlr_log(WLR_DEBUG, " %s (0x%016"PRIX64"): ✓ texture %s render",
+ mod_name ? mod_name : "<unknown>", modifier, external_only ? "✗" : "✓");
+ free(mod_name);
+}
+
+static void init_dmabuf_formats(struct wlr_egl *egl) {
+ bool no_modifiers = env_parse_bool("WLR_EGL_NO_MODIFIERS");
+ if (no_modifiers) {
+ wlr_log(WLR_INFO, "WLR_EGL_NO_MODIFIERS set, disabling modifiers for EGL");
+ }
+
+ EGLint *formats;
+ int formats_len = get_egl_dmabuf_formats(egl, &formats);
+ if (formats_len < 0) {
+ return;
+ }
+
+ wlr_log(WLR_DEBUG, "Supported DMA-BUF formats:");
+
+ bool has_modifiers = false;
+ for (int i = 0; i < formats_len; i++) {
+ EGLint fmt = formats[i];
+
+ uint64_t *modifiers = NULL;
+ EGLBoolean *external_only = NULL;
+ int modifiers_len = 0;
+ if (!no_modifiers) {
+ modifiers_len = get_egl_dmabuf_modifiers(egl, fmt, &modifiers, &external_only);
+ }
+ if (modifiers_len < 0) {
+ continue;
+ }
+
+ has_modifiers = has_modifiers || modifiers_len > 0;
+
+ bool all_external_only = true;
+ for (int j = 0; j < modifiers_len; j++) {
+ wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt,
+ modifiers[j]);
+ if (!external_only[j]) {
+ wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt,
+ modifiers[j]);
+ all_external_only = false;
+ }
+ }
+
+ // EGL always supports implicit modifiers. If at least one modifier supports rendering,
+ // assume the implicit modifier supports rendering too.
+ wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt,
+ DRM_FORMAT_MOD_INVALID);
+ if (modifiers_len == 0 || !all_external_only) {
+ wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt,
+ DRM_FORMAT_MOD_INVALID);
+ }
+
+ if (modifiers_len == 0) {
+ // Asume the linear layout is supported if the driver doesn't
+ // explicitly say otherwise
+ wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt,
+ DRM_FORMAT_MOD_LINEAR);
+ wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt,
+ DRM_FORMAT_MOD_LINEAR);
+ }
+
+ if (wlr_log_get_verbosity() >= WLR_DEBUG) {
+ char *fmt_name = drmGetFormatName(fmt);
+ wlr_log(WLR_DEBUG, " %s (0x%08"PRIX32")",
+ fmt_name ? fmt_name : "<unknown>", fmt);
+ free(fmt_name);
+
+ log_modifier(DRM_FORMAT_MOD_INVALID, false);
+ if (modifiers_len == 0) {
+ log_modifier(DRM_FORMAT_MOD_LINEAR, false);
+ }
+ for (int j = 0; j < modifiers_len; j++) {
+ log_modifier(modifiers[j], external_only[j]);
+ }
+ }
+
+ free(modifiers);
+ free(external_only);
+ }
+ free(formats);
+
+ egl->has_modifiers = has_modifiers;
+ if (!no_modifiers) {
+ wlr_log(WLR_DEBUG, "EGL DMA-BUF format modifiers %s",
+ has_modifiers ? "supported" : "unsupported");
+ }
+}
+
+static struct wlr_egl *egl_create(void) {
+ const char *client_exts_str = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (client_exts_str == NULL) {
+ if (eglGetError() == EGL_BAD_DISPLAY) {
+ wlr_log(WLR_ERROR, "EGL_EXT_client_extensions not supported");
+ } else {
+ wlr_log(WLR_ERROR, "Failed to query EGL client extensions");
+ }
+ return NULL;
+ }
+
+ wlr_log(WLR_INFO, "Supported EGL client extensions: %s", client_exts_str);
+
+ if (!check_egl_ext(client_exts_str, "EGL_EXT_platform_base")) {
+ wlr_log(WLR_ERROR, "EGL_EXT_platform_base not supported");
+ return NULL;
+ }
+
+ struct wlr_egl *egl = calloc(1, sizeof(*egl));
+ if (egl == NULL) {
+ wlr_log_errno(WLR_ERROR, "Allocation failed");
+ return NULL;
+ }
+
+ load_egl_proc(&egl->procs.eglGetPlatformDisplayEXT,
+ "eglGetPlatformDisplayEXT");
+
+ egl->exts.KHR_platform_gbm = check_egl_ext(client_exts_str,
+ "EGL_KHR_platform_gbm");
+ egl->exts.EXT_platform_device = check_egl_ext(client_exts_str,
+ "EGL_EXT_platform_device");
+ egl->exts.KHR_display_reference = check_egl_ext(client_exts_str,
+ "EGL_KHR_display_reference");
+
+ if (check_egl_ext(client_exts_str, "EGL_EXT_device_base") || check_egl_ext(client_exts_str, "EGL_EXT_device_enumeration")) {
+ load_egl_proc(&egl->procs.eglQueryDevicesEXT, "eglQueryDevicesEXT");
+ }
+
+ if (check_egl_ext(client_exts_str, "EGL_EXT_device_base") || check_egl_ext(client_exts_str, "EGL_EXT_device_query")) {
+ egl->exts.EXT_device_query = true;
+ load_egl_proc(&egl->procs.eglQueryDeviceStringEXT,
+ "eglQueryDeviceStringEXT");
+ load_egl_proc(&egl->procs.eglQueryDisplayAttribEXT,
+ "eglQueryDisplayAttribEXT");
+ }
+
+ if (check_egl_ext(client_exts_str, "EGL_KHR_debug")) {
+ load_egl_proc(&egl->procs.eglDebugMessageControlKHR,
+ "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,
+ };
+ egl->procs.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");
+ free(egl);
+ return NULL;
+ }
+
+ return egl;
+}
+
+static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) {
+ egl->display = display;
+
+ EGLint major, minor;
+ if (eglInitialize(egl->display, &major, &minor) == EGL_FALSE) {
+ wlr_log(WLR_ERROR, "Failed to initialize EGL");
+ return false;
+ }
+
+ const char *display_exts_str = eglQueryString(egl->display, EGL_EXTENSIONS);
+ if (display_exts_str == NULL) {
+ wlr_log(WLR_ERROR, "Failed to query EGL display extensions");
+ return false;
+ }
+
+ if (check_egl_ext(display_exts_str, "EGL_KHR_image_base")) {
+ egl->exts.KHR_image_base = true;
+ load_egl_proc(&egl->procs.eglCreateImageKHR, "eglCreateImageKHR");
+ load_egl_proc(&egl->procs.eglDestroyImageKHR, "eglDestroyImageKHR");
+ }
+
+ egl->exts.EXT_image_dma_buf_import =
+ check_egl_ext(display_exts_str, "EGL_EXT_image_dma_buf_import");
+ if (check_egl_ext(display_exts_str,
+ "EGL_EXT_image_dma_buf_import_modifiers")) {
+ egl->exts.EXT_image_dma_buf_import_modifiers = true;
+ load_egl_proc(&egl->procs.eglQueryDmaBufFormatsEXT,
+ "eglQueryDmaBufFormatsEXT");
+ load_egl_proc(&egl->procs.eglQueryDmaBufModifiersEXT,
+ "eglQueryDmaBufModifiersEXT");
+ }
+
+ egl->exts.EXT_create_context_robustness =
+ check_egl_ext(display_exts_str, "EGL_EXT_create_context_robustness");
+
+ const char *device_exts_str = NULL, *driver_name = NULL;
+ if (egl->exts.EXT_device_query) {
+ EGLAttrib device_attrib;
+ if (!egl->procs.eglQueryDisplayAttribEXT(egl->display,
+ EGL_DEVICE_EXT, &device_attrib)) {
+ wlr_log(WLR_ERROR, "eglQueryDisplayAttribEXT(EGL_DEVICE_EXT) failed");
+ return false;
+ }
+ egl->device = (EGLDeviceEXT)device_attrib;
+
+ device_exts_str =
+ egl->procs.eglQueryDeviceStringEXT(egl->device, EGL_EXTENSIONS);
+ if (device_exts_str == NULL) {
+ wlr_log(WLR_ERROR, "eglQueryDeviceStringEXT(EGL_EXTENSIONS) failed");
+ return false;
+ }
+
+ if (check_egl_ext(device_exts_str, "EGL_MESA_device_software")) {
+ if (env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) {
+ wlr_log(WLR_INFO, "Using software rendering");
+ } else {
+ wlr_log(WLR_ERROR, "Software rendering detected, please use "
+ "the WLR_RENDERER_ALLOW_SOFTWARE environment variable "
+ "to proceed");
+ return false;
+ }
+ }
+
+#ifdef EGL_DRIVER_NAME_EXT
+ if (check_egl_ext(device_exts_str, "EGL_EXT_device_persistent_id")) {
+ driver_name = egl->procs.eglQueryDeviceStringEXT(egl->device,
+ EGL_DRIVER_NAME_EXT);
+ }
+#endif
+
+ egl->exts.EXT_device_drm =
+ check_egl_ext(device_exts_str, "EGL_EXT_device_drm");
+ egl->exts.EXT_device_drm_render_node =
+ check_egl_ext(device_exts_str, "EGL_EXT_device_drm_render_node");
+ }
+
+ if (!check_egl_ext(display_exts_str, "EGL_KHR_no_config_context") &&
+ !check_egl_ext(display_exts_str, "EGL_MESA_configless_context")) {
+ wlr_log(WLR_ERROR, "EGL_KHR_no_config_context or "
+ "EGL_MESA_configless_context not supported");
+ return false;
+ }
+
+ if (!check_egl_ext(display_exts_str, "EGL_KHR_surfaceless_context")) {
+ wlr_log(WLR_ERROR, "EGL_KHR_surfaceless_context not supported");
+ return false;
+ }
+
+ egl->exts.IMG_context_priority =
+ check_egl_ext(display_exts_str, "EGL_IMG_context_priority");
+
+ 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) {
+ wlr_log(WLR_INFO, "Supported EGL device extensions: %s", device_exts_str);
+ }
+ wlr_log(WLR_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR));
+ if (driver_name != NULL) {
+ wlr_log(WLR_INFO, "EGL driver name: %s", driver_name);
+ }
+
+ init_dmabuf_formats(egl);
+
+ return true;
+}
+
+static bool egl_init(struct wlr_egl *egl, EGLenum platform,
+ void *remote_display) {
+ EGLint display_attribs[3] = {0};
+ size_t display_attribs_len = 0;
+
+ if (egl->exts.KHR_display_reference) {
+ display_attribs[display_attribs_len++] = EGL_TRACK_REFERENCES_KHR;
+ display_attribs[display_attribs_len++] = EGL_TRUE;
+ }
+
+ display_attribs[display_attribs_len++] = EGL_NONE;
+ assert(display_attribs_len < sizeof(display_attribs) / sizeof(display_attribs[0]));
+
+ EGLDisplay display = egl->procs.eglGetPlatformDisplayEXT(platform,
+ remote_display, display_attribs);
+ if (display == EGL_NO_DISPLAY) {
+ wlr_log(WLR_ERROR, "Failed to create EGL display");
+ return false;
+ }
+
+ if (!egl_init_display(egl, display)) {
+ if (egl->exts.KHR_display_reference) {
+ eglTerminate(display);
+ }
+ return false;
+ }
+
+ size_t atti = 0;
+ EGLint attribs[7];
+ attribs[atti++] = EGL_CONTEXT_CLIENT_VERSION;
+ attribs[atti++] = 2;
+
+ // Request a high priority context if possible
+ // TODO: only do this if we're running as the DRM master
+ bool request_high_priority = egl->exts.IMG_context_priority;
+
+ // 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;
+ }
+
+ if (egl->exts.EXT_create_context_robustness) {
+ attribs[atti++] = EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT;
+ attribs[atti++] = EGL_LOSE_CONTEXT_ON_RESET_EXT;
+ }
+
+ attribs[atti++] = EGL_NONE;
+ assert(atti <= sizeof(attribs)/sizeof(attribs[0]));
+
+ egl->context = eglCreateContext(egl->display, EGL_NO_CONFIG_KHR,
+ EGL_NO_CONTEXT, attribs);
+ if (egl->context == EGL_NO_CONTEXT) {
+ wlr_log(WLR_ERROR, "Failed to create EGL context");
+ return false;
+ }
+
+ 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");
+ }
+ }
+
+ return true;
+}
+
+static bool device_has_name(const drmDevice *device, const char *name);
+
+static EGLDeviceEXT get_egl_device_from_drm_fd(struct wlr_egl *egl,
+ int drm_fd) {
+ if (egl->procs.eglQueryDevicesEXT == NULL) {
+ wlr_log(WLR_DEBUG, "EGL_EXT_device_enumeration not supported");
+ return EGL_NO_DEVICE_EXT;
+ } else if (!egl->exts.EXT_device_query) {
+ wlr_log(WLR_DEBUG, "EGL_EXT_device_query not supported");
+ return EGL_NO_DEVICE_EXT;
+ }
+
+ EGLint nb_devices = 0;
+ if (!egl->procs.eglQueryDevicesEXT(0, NULL, &nb_devices)) {
+ wlr_log(WLR_ERROR, "Failed to query EGL devices");
+ return EGL_NO_DEVICE_EXT;
+ }
+
+ EGLDeviceEXT *devices = calloc(nb_devices, sizeof(*devices));
+ if (devices == NULL) {
+ wlr_log_errno(WLR_ERROR, "Failed to allocate EGL device list");
+ return EGL_NO_DEVICE_EXT;
+ }
+
+ if (!egl->procs.eglQueryDevicesEXT(nb_devices, devices, &nb_devices)) {
+ wlr_log(WLR_ERROR, "Failed to query EGL devices");
+ return EGL_NO_DEVICE_EXT;
+ }
+
+ drmDevice *device = NULL;
+ int ret = drmGetDevice(drm_fd, &device);
+ if (ret < 0) {
+ wlr_log(WLR_ERROR, "Failed to get DRM device: %s", strerror(-ret));
+ return EGL_NO_DEVICE_EXT;
+ }
+
+ EGLDeviceEXT egl_device = NULL;
+ for (int i = 0; i < nb_devices; i++) {
+ const char *egl_device_name = egl->procs.eglQueryDeviceStringEXT(
+ devices[i], EGL_DRM_DEVICE_FILE_EXT);
+ if (egl_device_name == NULL) {
+ continue;
+ }
+
+ if (device_has_name(device, egl_device_name)) {
+ wlr_log(WLR_DEBUG, "Using EGL device %s", egl_device_name);
+ egl_device = devices[i];
+ break;
+ }
+ }
+
+ drmFreeDevice(&device);
+ free(devices);
+
+ return egl_device;
+}
+
+static int open_render_node(int drm_fd) {
+ char *render_name = drmGetRenderDeviceNameFromFd(drm_fd);
+ if (render_name == NULL) {
+ // This can happen on split render/display platforms, fallback to
+ // primary node
+ render_name = drmGetPrimaryDeviceNameFromFd(drm_fd);
+ if (render_name == NULL) {
+ wlr_log_errno(WLR_ERROR, "drmGetPrimaryDeviceNameFromFd failed");
+ return -1;
+ }
+ wlr_log(WLR_DEBUG, "DRM device '%s' has no render node, "
+ "falling back to primary node", render_name);
+ }
+
+ int render_fd = open(render_name, O_RDWR | O_CLOEXEC);
+ if (render_fd < 0) {
+ wlr_log_errno(WLR_ERROR, "Failed to open DRM node '%s'", render_name);
+ }
+ free(render_name);
+ return render_fd;
+}
+
+struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) {
+ struct wlr_egl *egl = egl_create();
+ if (egl == NULL) {
+ wlr_log(WLR_ERROR, "Failed to create EGL context");
+ return NULL;
+ }
+
+ if (egl->exts.EXT_platform_device) {
+ /*
+ * Search for the EGL device matching the DRM fd using the
+ * EXT_device_enumeration extension.
+ */
+ EGLDeviceEXT egl_device = get_egl_device_from_drm_fd(egl, drm_fd);
+ if (egl_device != EGL_NO_DEVICE_EXT) {
+ if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device)) {
+ wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_DEVICE_EXT");
+ return egl;
+ }
+ goto error;
+ }
+ /* Falls back on GBM in case the device was not found */
+ } else {
+ wlr_log(WLR_DEBUG, "EXT_platform_device not supported");
+ }
+
+ if (egl->exts.KHR_platform_gbm) {
+ int gbm_fd = open_render_node(drm_fd);
+ if (gbm_fd < 0) {
+ wlr_log(WLR_ERROR, "Failed to open DRM render node");
+ goto error;
+ }
+
+ egl->gbm_device = gbm_create_device(gbm_fd);
+ if (!egl->gbm_device) {
+ close(gbm_fd);
+ wlr_log(WLR_ERROR, "Failed to create GBM device");
+ goto error;
+ }
+
+ if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device)) {
+ wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_GBM_KHR");
+ return egl;
+ }
+
+ gbm_device_destroy(egl->gbm_device);
+ close(gbm_fd);
+ } else {
+ wlr_log(WLR_DEBUG, "KHR_platform_gbm not supported");
+ }
+
+error:
+ wlr_log(WLR_ERROR, "Failed to initialize EGL context");
+ free(egl);
+ eglReleaseThread();
+ return NULL;
+}
+
+struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display,
+ EGLContext context) {
+ EGLint client_type;
+ if (!eglQueryContext(display, context, EGL_CONTEXT_CLIENT_TYPE, &client_type) ||
+ client_type != EGL_OPENGL_ES_API) {
+ wlr_log(WLR_ERROR, "Unsupported EGL context client type (need OpenGL ES)");
+ return NULL;
+ }
+
+ EGLint client_version;
+ if (!eglQueryContext(display, context, EGL_CONTEXT_CLIENT_VERSION, &client_version) ||
+ client_version < 2) {
+ wlr_log(WLR_ERROR, "Unsupported EGL context client version (need OpenGL ES >= 2)");
+ return NULL;
+ }
+
+ struct wlr_egl *egl = egl_create();
+ if (egl == NULL) {
+ return NULL;
+ }
+
+ if (!egl_init_display(egl, display)) {
+ free(egl);
+ return NULL;
+ }
+
+ egl->context = context;
+
+ return egl;
+}
+
+void wlr_egl_destroy(struct wlr_egl *egl) {
+ if (egl == NULL) {
+ return;
+ }
+
+ wlr_drm_format_set_finish(&egl->dmabuf_render_formats);
+ wlr_drm_format_set_finish(&egl->dmabuf_texture_formats);
+
+ eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(egl->display, egl->context);
+
+ if (egl->exts.KHR_display_reference) {
+ eglTerminate(egl->display);
+ }
+
+ eglReleaseThread();
+
+ if (egl->gbm_device) {
+ int gbm_fd = gbm_device_get_fd(egl->gbm_device);
+ gbm_device_destroy(egl->gbm_device);
+ close(gbm_fd);
+ }
+
+ free(egl);
+}
+
+EGLDisplay wlr_egl_get_display(struct wlr_egl *egl) {
+ return egl->display;
+}
+
+EGLContext wlr_egl_get_context(struct wlr_egl *egl) {
+ return egl->context;
+}
+
+bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImage image) {
+ if (!egl->exts.KHR_image_base) {
+ return false;
+ }
+ if (!image) {
+ return true;
+ }
+ return egl->procs.eglDestroyImageKHR(egl->display, image);
+}
+
+bool wlr_egl_make_current(struct wlr_egl *egl) {
+ if (!eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ egl->context)) {
+ wlr_log(WLR_ERROR, "eglMakeCurrent failed");
+ return false;
+ }
+ return true;
+}
+
+bool wlr_egl_unset_current(struct wlr_egl *egl) {
+ if (!eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT)) {
+ wlr_log(WLR_ERROR, "eglMakeCurrent failed");
+ return false;
+ }
+ return true;
+}
+
+bool wlr_egl_is_current(struct wlr_egl *egl) {
+ return eglGetCurrentContext() == egl->context;
+}
+
+void wlr_egl_save_context(struct wlr_egl_context *context) {
+ context->display = eglGetCurrentDisplay();
+ context->context = eglGetCurrentContext();
+ context->draw_surface = eglGetCurrentSurface(EGL_DRAW);
+ context->read_surface = eglGetCurrentSurface(EGL_READ);
+}
+
+bool wlr_egl_restore_context(struct wlr_egl_context *context) {
+ // If the saved context is a null-context, we must use the current
+ // display instead of the saved display because eglMakeCurrent() can't
+ // handle EGL_NO_DISPLAY.
+ EGLDisplay display = context->display == EGL_NO_DISPLAY ?
+ eglGetCurrentDisplay() : context->display;
+
+ // If the current display is also EGL_NO_DISPLAY, we assume that there
+ // is currently no context set and no action needs to be taken to unset
+ // the context.
+ if (display == EGL_NO_DISPLAY) {
+ return true;
+ }
+
+ return eglMakeCurrent(display, context->draw_surface,
+ context->read_surface, context->context);
+}
+
+EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl,
+ struct wlr_dmabuf_attributes *attributes, bool *external_only) {
+ if (!egl->exts.KHR_image_base || !egl->exts.EXT_image_dma_buf_import) {
+ wlr_log(WLR_ERROR, "dmabuf import extension not present");
+ return NULL;
+ }
+
+ if (attributes->modifier != DRM_FORMAT_MOD_INVALID &&
+ attributes->modifier != DRM_FORMAT_MOD_LINEAR &&
+ !egl->has_modifiers) {
+ wlr_log(WLR_ERROR, "EGL implementation doesn't support modifiers");
+ return NULL;
+ }
+
+ 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 (egl->has_modifiers &&
+ attributes->modifier != DRM_FORMAT_MOD_INVALID) {
+ attribs[atti++] = attr_names[i].mod_lo;
+ attribs[atti++] = attributes->modifier & 0xFFFFFFFF;
+ attribs[atti++] = attr_names[i].mod_hi;
+ attribs[atti++] = attributes->modifier >> 32;
+ }
+ }
+
+ // Our clients don't expect our usage to trash the buffer contents
+ attribs[atti++] = EGL_IMAGE_PRESERVED_KHR;
+ attribs[atti++] = EGL_TRUE;
+
+ attribs[atti++] = EGL_NONE;
+ assert(atti < sizeof(attribs)/sizeof(attribs[0]));
+
+ EGLImageKHR image = egl->procs.eglCreateImageKHR(egl->display, EGL_NO_CONTEXT,
+ EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
+ if (image == EGL_NO_IMAGE_KHR) {
+ wlr_log(WLR_ERROR, "eglCreateImageKHR failed");
+ return EGL_NO_IMAGE_KHR;
+ }
+
+ *external_only = !wlr_drm_format_set_has(&egl->dmabuf_render_formats,
+ attributes->format, attributes->modifier);
+ return image;
+}
+
+static int get_egl_dmabuf_formats(struct wlr_egl *egl, EGLint **formats) {
+ if (!egl->exts.EXT_image_dma_buf_import) {
+ wlr_log(WLR_DEBUG, "DMA-BUF 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.EXT_image_dma_buf_import_modifiers) {
+ static const EGLint fallback_formats[] = {
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XRGB8888,
+ };
+ int num = sizeof(fallback_formats) / sizeof(fallback_formats[0]);
+
+ *formats = calloc(num, sizeof(**formats));
+ if (!*formats) {
+ wlr_log_errno(WLR_ERROR, "Allocation failed");
+ return -1;
+ }
+
+ memcpy(*formats, fallback_formats, num * sizeof(**formats));
+ return num;
+ }
+
+ EGLint num;
+ if (!egl->procs.eglQueryDmaBufFormatsEXT(egl->display, 0, NULL, &num)) {
+ wlr_log(WLR_ERROR, "Failed to query number of dmabuf formats");
+ return -1;
+ }
+
+ *formats = calloc(num, sizeof(**formats));
+ if (*formats == NULL) {
+ wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno));
+ return -1;
+ }
+
+ if (!egl->procs.eglQueryDmaBufFormatsEXT(egl->display, num, *formats, &num)) {
+ wlr_log(WLR_ERROR, "Failed to query dmabuf format");
+ free(*formats);
+ return -1;
+ }
+ return num;
+}
+
+static int get_egl_dmabuf_modifiers(struct wlr_egl *egl, EGLint format,
+ uint64_t **modifiers, EGLBoolean **external_only) {
+ *modifiers = NULL;
+ *external_only = NULL;
+
+ if (!egl->exts.EXT_image_dma_buf_import) {
+ wlr_log(WLR_DEBUG, "DMA-BUF extension not present");
+ return -1;
+ }
+ if (!egl->exts.EXT_image_dma_buf_import_modifiers) {
+ return 0;
+ }
+
+ EGLint num;
+ if (!egl->procs.eglQueryDmaBufModifiersEXT(egl->display, format, 0,
+ NULL, NULL, &num)) {
+ wlr_log(WLR_ERROR, "Failed to query dmabuf number of modifiers");
+ return -1;
+ }
+ if (num == 0) {
+ return 0;
+ }
+
+ *modifiers = calloc(num, sizeof(**modifiers));
+ if (*modifiers == NULL) {
+ wlr_log_errno(WLR_ERROR, "Allocation failed");
+ return -1;
+ }
+ *external_only = calloc(num, sizeof(**external_only));
+ if (*external_only == NULL) {
+ wlr_log_errno(WLR_ERROR, "Allocation failed");
+ free(*modifiers);
+ *modifiers = NULL;
+ return -1;
+ }
+
+ if (!egl->procs.eglQueryDmaBufModifiersEXT(egl->display, format, num,
+ *modifiers, *external_only, &num)) {
+ wlr_log(WLR_ERROR, "Failed to query dmabuf modifiers");
+ free(*modifiers);
+ free(*external_only);
+ return -1;
+ }
+ return num;
+}
+
+const struct wlr_drm_format_set *wlr_egl_get_dmabuf_texture_formats(
+ struct wlr_egl *egl) {
+ return &egl->dmabuf_texture_formats;
+}
+
+const struct wlr_drm_format_set *wlr_egl_get_dmabuf_render_formats(
+ struct wlr_egl *egl) {
+ return &egl->dmabuf_render_formats;
+}
+
+static bool device_has_name(const drmDevice *device, const char *name) {
+ for (size_t i = 0; i < DRM_NODE_MAX; i++) {
+ if (!(device->available_nodes & (1 << i))) {
+ continue;
+ }
+ if (strcmp(device->nodes[i], name) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static char *get_render_name(const char *name) {
+ uint32_t flags = 0;
+ int devices_len = drmGetDevices2(flags, NULL, 0);
+ if (devices_len < 0) {
+ wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
+ return NULL;
+ }
+ drmDevice **devices = calloc(devices_len, sizeof(*devices));
+ if (devices == NULL) {
+ wlr_log_errno(WLR_ERROR, "Allocation failed");
+ return NULL;
+ }
+ devices_len = drmGetDevices2(flags, devices, devices_len);
+ if (devices_len < 0) {
+ free(devices);
+ wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
+ return NULL;
+ }
+
+ const drmDevice *match = NULL;
+ for (int i = 0; i < devices_len; i++) {
+ if (device_has_name(devices[i], name)) {
+ match = devices[i];
+ break;
+ }
+ }
+
+ char *render_name = NULL;
+ if (match == NULL) {
+ wlr_log(WLR_ERROR, "Cannot find DRM device %s", name);
+ } else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) {
+ // Likely a split display/render setup. Pick the primary node and hope
+ // Mesa will open the right render node under-the-hood.
+ wlr_log(WLR_DEBUG, "DRM device %s has no render node, "
+ "falling back to primary node", name);
+ assert(match->available_nodes & (1 << DRM_NODE_PRIMARY));
+ render_name = strdup(match->nodes[DRM_NODE_PRIMARY]);
+ } else {
+ render_name = strdup(match->nodes[DRM_NODE_RENDER]);
+ }
+
+ for (int i = 0; i < devices_len; i++) {
+ drmFreeDevice(&devices[i]);
+ }
+ free(devices);
+
+ return render_name;
+}
+
+static int dup_egl_device_drm_fd(struct wlr_egl *egl) {
+ if (egl->device == EGL_NO_DEVICE_EXT || (!egl->exts.EXT_device_drm &&
+ !egl->exts.EXT_device_drm_render_node)) {
+ return -1;
+ }
+
+ char *render_name = NULL;
+#ifdef EGL_EXT_device_drm_render_node
+ if (egl->exts.EXT_device_drm_render_node) {
+ const char *name = egl->procs.eglQueryDeviceStringEXT(egl->device,
+ EGL_DRM_RENDER_NODE_FILE_EXT);
+ if (name == NULL) {
+ wlr_log(WLR_DEBUG, "EGL device has no render node");
+ return -1;
+ }
+ render_name = strdup(name);
+ }
+#endif
+
+ if (render_name == NULL) {
+ const char *primary_name = egl->procs.eglQueryDeviceStringEXT(egl->device,
+ EGL_DRM_DEVICE_FILE_EXT);
+ if (primary_name == NULL) {
+ wlr_log(WLR_ERROR,
+ "eglQueryDeviceStringEXT(EGL_DRM_DEVICE_FILE_EXT) failed");
+ return -1;
+ }
+
+ render_name = get_render_name(primary_name);
+ if (render_name == NULL) {
+ wlr_log(WLR_ERROR, "Can't find render node name for device %s",
+ primary_name);
+ return -1;
+ }
+ }
+
+ int render_fd = open(render_name, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+ if (render_fd < 0) {
+ wlr_log_errno(WLR_ERROR, "Failed to open DRM render node %s",
+ render_name);
+ free(render_name);
+ return -1;
+ }
+ free(render_name);
+
+ return render_fd;
+}
+
+int wlr_egl_dup_drm_fd(struct wlr_egl *egl) {
+ int fd = dup_egl_device_drm_fd(egl);
+ if (fd >= 0) {
+ return fd;
+ }
+
+ // Fallback to GBM's FD if we can't use EGLDevice
+ if (egl->gbm_device == NULL) {
+ return -1;
+ }
+
+ fd = fcntl(gbm_device_get_fd(egl->gbm_device), F_DUPFD_CLOEXEC, 0);
+ if (fd < 0) {
+ wlr_log_errno(WLR_ERROR, "Failed to dup GBM FD");
+ }
+ return fd;
+}
diff --git a/render/fx_renderer/fx_framebuffer.c b/render/fx_renderer/fx_framebuffer.c
new file mode 100644
index 0000000..fea101b
--- /dev/null
+++ b/render/fx_renderer/fx_framebuffer.c
@@ -0,0 +1,137 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <wlr/interfaces/wlr_buffer.h>
+#include <wlr/render/allocator.h>
+#include <wlr/render/interface.h>
+#include <wlr/render/swapchain.h>
+#include <wlr/util/log.h>
+
+#include "render/egl.h"
+#include "render/fx_renderer/fx_renderer.h"
+
+static void handle_buffer_destroy(struct wlr_addon *addon) {
+ struct fx_framebuffer *buffer =
+ wl_container_of(addon, buffer, addon);
+ fx_framebuffer_destroy(buffer);
+}
+
+static const struct wlr_addon_interface buffer_addon_impl = {
+ .name = "fx_framebuffer",
+ .destroy = handle_buffer_destroy,
+};
+
+struct fx_framebuffer *fx_framebuffer_get_or_create(struct fx_renderer *renderer,
+ struct wlr_buffer *wlr_buffer) {
+ struct wlr_addon *addon =
+ wlr_addon_find(&wlr_buffer->addons, renderer, &buffer_addon_impl);
+ if (addon) {
+ struct fx_framebuffer *buffer = wl_container_of(addon, buffer, addon);
+ return buffer;
+ }
+
+ struct fx_framebuffer *buffer = calloc(1, sizeof(*buffer));
+ if (buffer == NULL) {
+ wlr_log_errno(WLR_ERROR, "Allocation failed");
+ return NULL;
+ }
+ buffer->buffer = wlr_buffer;
+ buffer->renderer = renderer;
+
+ struct wlr_dmabuf_attributes dmabuf = {0};
+ if (!wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) {
+ goto error_buffer;
+ }
+
+ bool external_only;
+ buffer->image = wlr_egl_create_image_from_dmabuf(renderer->egl,
+ &dmabuf, &external_only);
+ if (buffer->image == EGL_NO_IMAGE_KHR) {
+ goto error_buffer;
+ }
+
+ push_fx_debug(renderer);
+
+ glGenRenderbuffers(1, &buffer->rbo);
+ glBindRenderbuffer(GL_RENDERBUFFER, buffer->rbo);
+ renderer->procs.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER,
+ buffer->image);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+
+ glGenFramebuffers(1, &buffer->fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, buffer->fbo);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, buffer->rbo);
+ GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
+ wlr_log(WLR_ERROR, "Failed to create FBO");
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ goto error_image;
+ }
+
+ // Init stencil buffer
+ glGenRenderbuffers(1, &buffer->sb);
+ glBindRenderbuffer(GL_RENDERBUFFER, buffer->sb);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
+ wlr_buffer->width, wlr_buffer->height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, buffer->sb);
+ fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
+ wlr_log(WLR_ERROR,
+ "Stencil buffer incomplete, couldn't create! (FB status: %i)",
+ fb_status);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ goto error_stencil;
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ pop_fx_debug(renderer);
+
+ wlr_addon_init(&buffer->addon, &wlr_buffer->addons, renderer,
+ &buffer_addon_impl);
+
+ wl_list_insert(&renderer->buffers, &buffer->link);
+
+ wlr_log(WLR_DEBUG, "Created GL FBO for buffer %dx%d",
+ wlr_buffer->width, wlr_buffer->height);
+
+ return buffer;
+
+error_stencil:
+ glDeleteRenderbuffers(1, &buffer->sb);
+error_image:
+ wlr_egl_destroy_image(renderer->egl, buffer->image);
+error_buffer:
+ free(buffer);
+ return NULL;
+}
+
+void fx_framebuffer_bind(struct fx_framebuffer *fx_buffer) {
+ glBindFramebuffer(GL_FRAMEBUFFER, fx_buffer->fbo);
+}
+
+void fx_framebuffer_bind_wlr_fbo(struct fx_renderer *renderer) {
+ glBindFramebuffer(GL_FRAMEBUFFER, renderer->current_buffer->fbo);
+}
+
+void fx_framebuffer_destroy(struct fx_framebuffer *fx_buffer) {
+ // Release the framebuffer
+ wl_list_remove(&fx_buffer->link);
+ wlr_addon_finish(&fx_buffer->addon);
+
+ struct wlr_egl_context prev_ctx;
+ wlr_egl_save_context(&prev_ctx);
+ wlr_egl_make_current(fx_buffer->renderer->egl);
+
+ glDeleteFramebuffers(1, &fx_buffer->fbo);
+ fx_buffer->fbo = -1;
+ glDeleteRenderbuffers(1, &fx_buffer->rbo);
+ fx_buffer->rbo = -1;
+
+ wlr_egl_destroy_image(fx_buffer->renderer->egl, fx_buffer->image);
+
+ wlr_egl_restore_context(&prev_ctx);
+
+ free(fx_buffer);
+}
diff --git a/render/fx_renderer/fx_pass.c b/render/fx_renderer/fx_pass.c
new file mode 100644
index 0000000..9a8c90c
--- /dev/null
+++ b/render/fx_renderer/fx_pass.c
@@ -0,0 +1,509 @@
+#define _POSIX_C_SOURCE 199309L
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <pixman.h>
+#include <time.h>
+#include <wlr/types/wlr_matrix.h>
+#include <wlr/util/log.h>
+
+#include "render/egl.h"
+#include "render/fx_renderer/fx_renderer.h"
+#include "render/fx_renderer/matrix.h"
+#include "render/pass.h"
+#include "types/fx/shadow_data.h"
+
+#define MAX_QUADS 86 // 4kb
+
+struct fx_render_texture_options fx_render_texture_options_default(
+ const struct wlr_render_texture_options *base) {
+ struct fx_render_texture_options options = {
+ .corner_radius = 0,
+ .scale = 1.0f,
+ .clip_box = NULL,
+ };
+ memcpy(&options.base, base, sizeof(*base));
+ return options;
+}
+
+struct fx_render_rect_options fx_render_rect_options_default(
+ const struct wlr_render_rect_options *base) {
+ struct fx_render_rect_options options = {
+ .scale = 1.0f,
+ };
+ memcpy(&options.base, base, sizeof(*base));
+ return options;
+}
+
+///
+/// Base Wlroots pass functions
+///
+
+static const struct wlr_render_pass_impl render_pass_impl;
+
+static struct fx_gles_render_pass *get_render_pass(struct wlr_render_pass *wlr_pass) {
+ assert(wlr_pass->impl == &render_pass_impl);
+ struct fx_gles_render_pass *pass = wl_container_of(wlr_pass, pass, base);
+ return pass;
+}
+
+static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
+ struct fx_gles_render_pass *pass = get_render_pass(wlr_pass);
+ struct fx_renderer *renderer = pass->buffer->renderer;
+ struct fx_render_timer *timer = pass->timer;
+
+ push_fx_debug(renderer);
+
+ if (timer) {
+ // clear disjoint flag
+ GLint64 disjoint;
+ renderer->procs.glGetInteger64vEXT(GL_GPU_DISJOINT_EXT, &disjoint);
+ // set up the query
+ renderer->procs.glQueryCounterEXT(timer->id, GL_TIMESTAMP_EXT);
+ // get end-of-CPU-work time in GL time domain
+ renderer->procs.glGetInteger64vEXT(GL_TIMESTAMP_EXT, &timer->gl_cpu_end);
+ // get end-of-CPU-work time in CPU time domain
+ clock_gettime(CLOCK_MONOTONIC, &timer->cpu_end);
+ }
+
+ glFlush();
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ pop_fx_debug(renderer);
+
+ wlr_buffer_unlock(pass->buffer->buffer);
+ free(pass);
+
+ return true;
+}
+
+static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
+ const struct wlr_render_texture_options *options) {
+ struct fx_gles_render_pass *pass = get_render_pass(wlr_pass);
+ const struct fx_render_texture_options fx_options =
+ fx_render_texture_options_default(options);
+ // Re-use fx function but with default options
+ fx_render_pass_add_texture(pass, &fx_options);
+}
+
+static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
+ const struct wlr_render_rect_options *options) {
+ struct fx_gles_render_pass *pass = get_render_pass(wlr_pass);
+ const struct fx_render_rect_options fx_options =
+ fx_render_rect_options_default(options);
+ // Re-use fx function but with default options
+ fx_render_pass_add_rect(pass, &fx_options);
+}
+
+static const struct wlr_render_pass_impl render_pass_impl = {
+ .submit = render_pass_submit,
+ .add_texture = render_pass_add_texture,
+ .add_rect = render_pass_add_rect,
+};
+
+///
+/// FX pass functions
+///
+
+static void render(const struct wlr_box *box, const pixman_region32_t *clip, GLint attrib) {
+ pixman_region32_t region;
+ pixman_region32_init_rect(&region, box->x, box->y, box->width, box->height);
+
+ if (clip) {
+ pixman_region32_intersect(&region, &region, clip);
+ }
+
+ int rects_len;
+ const pixman_box32_t *rects = pixman_region32_rectangles(&region, &rects_len);
+ if (rects_len == 0) {
+ pixman_region32_fini(&region);
+ return;
+ }
+
+ glEnableVertexAttribArray(attrib);
+
+ for (int i = 0; i < rects_len;) {
+ int batch = rects_len - i < MAX_QUADS ? rects_len - i : MAX_QUADS;
+ int batch_end = batch + i;
+
+ size_t vert_index = 0;
+ GLfloat verts[MAX_QUADS * 6 * 2];
+ for (; i < batch_end; i++) {
+ const pixman_box32_t *rect = &rects[i];
+
+ verts[vert_index++] = (GLfloat)(rect->x1 - box->x) / box->width;
+ verts[vert_index++] = (GLfloat)(rect->y1 - box->y) / box->height;
+ verts[vert_index++] = (GLfloat)(rect->x2 - box->x) / box->width;
+ verts[vert_index++] = (GLfloat)(rect->y1 - box->y) / box->height;
+ verts[vert_index++] = (GLfloat)(rect->x1 - box->x) / box->width;
+ verts[vert_index++] = (GLfloat)(rect->y2 - box->y) / box->height;
+ verts[vert_index++] = (GLfloat)(rect->x2 - box->x) / box->width;
+ verts[vert_index++] = (GLfloat)(rect->y1 - box->y) / box->height;
+ verts[vert_index++] = (GLfloat)(rect->x2 - box->x) / box->width;
+ verts[vert_index++] = (GLfloat)(rect->y2 - box->y) / box->height;
+ verts[vert_index++] = (GLfloat)(rect->x1 - box->x) / box->width;
+ verts[vert_index++] = (GLfloat)(rect->y2 - box->y) / box->height;
+ }
+
+ glVertexAttribPointer(attrib, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glDrawArrays(GL_TRIANGLES, 0, batch * 6);
+ }
+
+ glDisableVertexAttribArray(attrib);
+
+ pixman_region32_fini(&region);
+}
+
+static void set_proj_matrix(GLint loc, float proj[9], const struct wlr_box *box) {
+ float gl_matrix[9];
+ wlr_matrix_identity(gl_matrix);
+ wlr_matrix_translate(gl_matrix, box->x, box->y);
+ wlr_matrix_scale(gl_matrix, box->width, box->height);
+ wlr_matrix_multiply(gl_matrix, proj, gl_matrix);
+ glUniformMatrix3fv(loc, 1, GL_FALSE, gl_matrix);
+}
+
+static void set_tex_matrix(GLint loc, enum wl_output_transform trans,
+ const struct wlr_fbox *box) {
+ float tex_matrix[9];
+ wlr_matrix_identity(tex_matrix);
+ wlr_matrix_translate(tex_matrix, box->x, box->y);
+ wlr_matrix_scale(tex_matrix, box->width, box->height);
+ wlr_matrix_translate(tex_matrix, .5, .5);
+
+ // since textures have a different origin point we have to transform
+ // differently if we are rotating
+ if (trans & WL_OUTPUT_TRANSFORM_90) {
+ wlr_matrix_transform(tex_matrix, wlr_output_transform_invert(trans));
+ } else {
+ wlr_matrix_transform(tex_matrix, trans);
+ }
+ wlr_matrix_translate(tex_matrix, -.5, -.5);
+
+ glUniformMatrix3fv(loc, 1, GL_FALSE, tex_matrix);
+}
+
+static void setup_blending(enum wlr_render_blend_mode mode) {
+ switch (mode) {
+ case WLR_RENDER_BLEND_MODE_PREMULTIPLIED:
+ glEnable(GL_BLEND);
+ break;
+ case WLR_RENDER_BLEND_MODE_NONE:
+ glDisable(GL_BLEND);
+ break;
+ }
+}
+
+// Initialize the stenciling work
+static void stencil_mask_init(void) {
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ glEnable(GL_STENCIL_TEST);
+
+ glStencilFunc(GL_ALWAYS, 1, 0xFF);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+ // Disable writing to color buffer
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+}
+
+// Close the mask
+static void stencil_mask_close(bool draw_inside_mask) {
+ // Reenable writing to color buffer
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ if (draw_inside_mask) {
+ glStencilFunc(GL_EQUAL, 1, 0xFF);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+ return;
+ }
+ glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+}
+
+// Finish stenciling and clear the buffer
+static void stencil_mask_fini(void) {
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ glDisable(GL_STENCIL_TEST);
+}
+
+// make sure the texture source box does not try and sample outside of the
+// texture
+static void check_tex_src_box(const struct wlr_render_texture_options *options) {
+ if (!wlr_fbox_empty(&options->src_box)) {
+ const struct wlr_fbox *box = &options->src_box;
+ assert(box->x >= 0 && box->y >= 0 &&
+ box->x + box->width <= options->texture->width &&
+ box->y + box->height <= options->texture->height);
+ }
+}
+
+void fx_render_pass_add_texture(struct fx_gles_render_pass *pass,
+ const struct fx_render_texture_options *fx_options) {
+ const struct wlr_render_texture_options *options = &fx_options->base;
+
+ check_tex_src_box(options);
+
+ struct fx_renderer *renderer = pass->buffer->renderer;
+ struct fx_texture *texture = fx_get_texture(options->texture);
+
+ struct tex_shader *shader = NULL;
+
+ switch (texture->target) {
+ case GL_TEXTURE_2D:
+ if (texture->has_alpha) {
+ shader = &renderer->shaders.tex_rgba;
+ } else {
+ shader = &renderer->shaders.tex_rgbx;
+ }
+ break;
+ case GL_TEXTURE_EXTERNAL_OES:
+ // EGL_EXT_image_dma_buf_import_modifiers requires
+ // GL_OES_EGL_image_external
+ assert(renderer->exts.OES_egl_image_external);
+ shader = &renderer->shaders.tex_ext;
+ break;
+ default:
+ abort();
+ }
+
+ struct wlr_box dst_box;
+ struct wlr_fbox src_fbox;
+ wlr_render_texture_options_get_src_box(options, &src_fbox);
+ wlr_render_texture_options_get_dst_box(options, &dst_box);
+ float alpha = wlr_render_texture_options_get_alpha(options);
+
+ struct wlr_box *clip_box = &dst_box;
+ if (!wlr_box_empty(fx_options->clip_box)) {
+ clip_box = fx_options->clip_box;
+ }
+
+ src_fbox.x /= options->texture->width;
+ src_fbox.y /= options->texture->height;
+ src_fbox.width /= options->texture->width;
+ src_fbox.height /= options->texture->height;
+
+ push_fx_debug(renderer);
+ setup_blending(!texture->has_alpha && alpha == 1.0 ?
+ WLR_RENDER_BLEND_MODE_NONE : options->blend_mode);
+
+ glUseProgram(shader->program);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(texture->target, texture->tex);
+
+ switch (options->filter_mode) {
+ case WLR_SCALE_FILTER_BILINEAR:
+ glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ break;
+ case WLR_SCALE_FILTER_NEAREST:
+ glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ break;
+ }
+
+ glUniform1i(shader->tex, 0);
+ glUniform1f(shader->alpha, alpha);
+ glUniform2f(shader->size, clip_box->width, clip_box->height);
+ glUniform2f(shader->position, clip_box->x, clip_box->y);
+ glUniform1f(shader->radius, fx_options->corner_radius);
+
+ set_proj_matrix(shader->proj, pass->projection_matrix, &dst_box);
+ set_tex_matrix(shader->tex_proj, options->transform, &src_fbox);
+
+ render(&dst_box, options->clip, shader->pos_attrib);
+
+ glBindTexture(texture->target, 0);
+ pop_fx_debug(renderer);
+}
+
+void fx_render_pass_add_rect(struct fx_gles_render_pass *pass,
+ const struct fx_render_rect_options *fx_options) {
+ const struct wlr_render_rect_options *options = &fx_options->base;
+
+ struct fx_renderer *renderer = pass->buffer->renderer;
+
+ const struct wlr_render_color *color = &options->color;
+ struct wlr_box box;
+ wlr_render_rect_options_get_box(options, pass->buffer->buffer, &box);
+
+ push_fx_debug(renderer);
+ setup_blending(color->a == 1.0 ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode);
+
+ glUseProgram(renderer->shaders.quad.program);
+
+ set_proj_matrix(renderer->shaders.quad.proj, pass->projection_matrix, &box);
+ glUniform4f(renderer->shaders.quad.color, color->r, color->g, color->b, color->a);
+
+ render(&box, options->clip, renderer->shaders.quad.pos_attrib);
+
+ pop_fx_debug(renderer);
+}
+
+void fx_render_pass_add_stencil_mask(struct fx_gles_render_pass *pass,
+ const struct fx_render_rect_options *fx_options, int corner_radius) {
+ const struct wlr_render_rect_options *options = &fx_options->base;
+
+ struct fx_renderer *renderer = pass->buffer->renderer;
+
+ const struct wlr_render_color *color = &options->color;
+ struct wlr_box box;
+ wlr_render_rect_options_get_box(options, pass->buffer->buffer, &box);
+ assert(box.width > 0 && box.height > 0);
+
+ push_fx_debug(renderer);
+ setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
+
+ glUseProgram(renderer->shaders.stencil_mask.program);
+
+ set_proj_matrix(renderer->shaders.stencil_mask.proj, pass->projection_matrix, &box);
+ glUniform4f(renderer->shaders.stencil_mask.color, color->r, color->g, color->b, color->a);
+ glUniform2f(renderer->shaders.stencil_mask.half_size, box.width * 0.5, box.height * 0.5);
+ glUniform2f(renderer->shaders.stencil_mask.position, box.x, box.y);
+ glUniform1f(renderer->shaders.stencil_mask.radius, corner_radius);
+
+ render(&box, options->clip, renderer->shaders.stencil_mask.pos_attrib);
+
+ pop_fx_debug(renderer);
+}
+
+void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass,
+ const struct fx_render_rect_options *fx_options,
+ int corner_radius, struct shadow_data *shadow_data) {
+ const struct wlr_render_rect_options *options = &fx_options->base;
+
+ struct fx_renderer *renderer = pass->buffer->renderer;
+
+ const struct wlr_render_color *color = &shadow_data->color;
+ struct wlr_box box;
+ wlr_render_rect_options_get_box(options, pass->buffer->buffer, &box);
+ assert(box.width > 0 && box.height > 0);
+ struct wlr_box surface_box = box;
+ float blur_sigma = shadow_data->blur_sigma * fx_options->scale;
+
+ // Extend the size of the box
+ box.x -= blur_sigma;
+ box.y -= blur_sigma;
+ box.width += blur_sigma * 2;
+ box.height += blur_sigma * 2;
+
+ pixman_region32_t render_region;
+ pixman_region32_init(&render_region);
+
+ pixman_region32_t inner_region;
+ pixman_region32_init(&inner_region);
+ pixman_region32_union_rect(&inner_region, &inner_region,
+ surface_box.x + corner_radius * 0.5,
+ surface_box.y + corner_radius * 0.5,
+ surface_box.width - corner_radius,
+ surface_box.height - corner_radius);
+ pixman_region32_subtract(&render_region, options->clip, &inner_region);
+ pixman_region32_fini(&inner_region);
+
+ push_fx_debug(renderer);
+
+ // Init stencil work
+ stencil_mask_init();
+ // Draw the rounded rect as a mask
+ fx_render_pass_add_stencil_mask(pass, fx_options, corner_radius);
+ stencil_mask_close(false);
+
+ // blending will practically always be needed (unless we have a madman
+ // who uses opaque shadows with zero sigma), so just enable it
+ setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glUseProgram(renderer->shaders.box_shadow.program);
+
+ set_proj_matrix(renderer->shaders.box_shadow.proj, pass->projection_matrix, &box);
+ glUniform4f(renderer->shaders.box_shadow.color, color->r, color->g, color->b, color->a);
+ glUniform1f(renderer->shaders.box_shadow.blur_sigma, blur_sigma);
+ glUniform1f(renderer->shaders.box_shadow.corner_radius, corner_radius);
+ glUniform2f(renderer->shaders.box_shadow.size, box.width, box.height);
+ glUniform2f(renderer->shaders.box_shadow.position, box.x, box.y);
+
+ render(&box, &render_region, renderer->shaders.box_shadow.pos_attrib);
+ pixman_region32_fini(&render_region);
+
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ stencil_mask_fini();
+
+ pop_fx_debug(renderer);
+}
+
+static const char *reset_status_str(GLenum status) {
+ switch (status) {
+ case GL_GUILTY_CONTEXT_RESET_KHR:
+ return "guilty";
+ case GL_INNOCENT_CONTEXT_RESET_KHR:
+ return "innocent";
+ case GL_UNKNOWN_CONTEXT_RESET_KHR:
+ return "unknown";
+ default:
+ return "<invalid>";
+ }
+}
+
+static struct fx_gles_render_pass *begin_buffer_pass(struct fx_framebuffer *buffer,
+ struct fx_render_timer *timer) {
+ struct fx_renderer *renderer = buffer->renderer;
+ struct wlr_buffer *wlr_buffer = buffer->buffer;
+
+ if (renderer->procs.glGetGraphicsResetStatusKHR) {
+ GLenum status = renderer->procs.glGetGraphicsResetStatusKHR();
+ if (status != GL_NO_ERROR) {
+ wlr_log(WLR_ERROR, "GPU reset (%s)", reset_status_str(status));
+ wl_signal_emit_mutable(&renderer->wlr_renderer.events.lost, NULL);
+ return NULL;
+ }
+ }
+
+ struct fx_gles_render_pass *pass = calloc(1, sizeof(*pass));
+ if (pass == NULL) {
+ return NULL;
+ }
+
+ wlr_render_pass_init(&pass->base, &render_pass_impl);
+ wlr_buffer_lock(wlr_buffer);
+ pass->buffer = buffer;
+ pass->timer = timer;
+
+ matrix_projection(pass->projection_matrix, wlr_buffer->width, wlr_buffer->height,
+ WL_OUTPUT_TRANSFORM_FLIPPED_180);
+
+ push_fx_debug(renderer);
+ glBindFramebuffer(GL_FRAMEBUFFER, buffer->fbo);
+
+ glViewport(0, 0, wlr_buffer->width, wlr_buffer->height);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_SCISSOR_TEST);
+ pop_fx_debug(renderer);
+
+ return pass;
+}
+
+struct fx_gles_render_pass *fx_renderer_begin_buffer_pass(struct wlr_renderer *wlr_renderer,
+ struct wlr_buffer *wlr_buffer, const struct wlr_buffer_pass_options *options) {
+ struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
+ if (!wlr_egl_make_current(renderer->egl)) {
+ return NULL;
+ }
+
+ struct fx_render_timer *timer = NULL;
+ if (options->timer) {
+ timer = fx_get_render_timer(options->timer);
+ clock_gettime(CLOCK_MONOTONIC, &timer->cpu_start);
+ }
+
+ struct fx_framebuffer *buffer = fx_framebuffer_get_or_create(renderer, wlr_buffer);
+ if (!buffer) {
+ return NULL;
+ }
+
+ struct fx_gles_render_pass *pass = begin_buffer_pass(buffer, timer);
+ if (!pass) {
+ return NULL;
+ }
+ return pass;
+}
diff --git a/render/fx_renderer/fx_renderer.c b/render/fx_renderer/fx_renderer.c
index 730e314..da9437c 100644
--- a/render/fx_renderer/fx_renderer.c
+++ b/render/fx_renderer/fx_renderer.c
@@ -3,27 +3,28 @@
https://gitlab.freedesktop.org/wlroots/wlroots/-/tree/master/render/gles2
*/
+#define _POSIX_C_SOURCE 199309L
#include <assert.h>
+#include <drm_fourcc.h>
#include <GLES2/gl2.h>
#include <stdio.h>
#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
#include <wlr/backend.h>
+#include <wlr/render/allocator.h>
#include <wlr/render/egl.h>
-#include <wlr/render/gles2.h>
+#include <wlr/render/interface.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
+#include "render/egl.h"
#include "render/fx_renderer/fx_renderer.h"
-#include "render/fx_renderer/fx_stencilbuffer.h"
#include "render/fx_renderer/matrix.h"
-
-// shaders
-#include "common_vert_src.h"
-#include "quad_frag_src.h"
-#include "tex_frag_src.h"
-#include "stencil_mask_frag_src.h"
-#include "box_shadow_frag_src.h"
+#include "render/fx_renderer/util.h"
+#include "render/pixel_format.h"
+#include "util/time.h"
static const GLfloat verts[] = {
1, 0, // top right
@@ -32,372 +33,179 @@ static const GLfloat verts[] = {
0, 1, // bottom left
};
-static GLuint compile_shader(GLuint type, const GLchar *src) {
- GLuint shader = glCreateShader(type);
- glShaderSource(shader, 1, &src, NULL);
- glCompileShader(shader);
-
- GLint ok;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
- if (ok == GL_FALSE) {
- wlr_log(WLR_ERROR, "Failed to compile shader");
- glDeleteShader(shader);
- shader = 0;
- }
+static const struct wlr_renderer_impl renderer_impl;
+static const struct wlr_render_timer_impl render_timer_impl;
- return shader;
+bool wlr_renderer_is_fx(struct wlr_renderer *wlr_renderer) {
+ return wlr_renderer->impl == &renderer_impl;
}
-static GLuint link_program(const GLchar *frag_src) {
- const GLchar *vert_src = common_vert_src;
- 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) {
- wlr_log(WLR_ERROR, "Failed to link shader");
- glDeleteProgram(prog);
- goto error;
- }
-
- return prog;
-
-error:
- return 0;
+struct fx_renderer *fx_get_renderer(
+ struct wlr_renderer *wlr_renderer) {
+ assert(wlr_renderer_is_fx(wlr_renderer));
+ struct fx_renderer *renderer = wl_container_of(wlr_renderer, renderer, wlr_renderer);
+ return renderer;
}
-static bool link_quad_program(struct quad_shader *shader) {
- GLuint prog;
- shader->program = prog = link_program(quad_frag_src);
- if (!shader->program) {
- return false;
- }
-
- shader->proj = glGetUniformLocation(prog, "proj");
- shader->color = glGetUniformLocation(prog, "color");
- shader->pos_attrib = glGetAttribLocation(prog, "pos");
+static struct fx_renderer *fx_get_renderer_in_context(
+ struct wlr_renderer *wlr_renderer) {
+ struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
+ assert(wlr_egl_is_current(renderer->egl));
+ assert(renderer->current_buffer != NULL);
+ return renderer;
+}
- return true;
+bool wlr_render_timer_is_fx(struct wlr_render_timer *timer) {
+ return timer->impl == &render_timer_impl;
}
-static bool link_tex_program(struct tex_shader *shader,
- enum fx_tex_shader_source source) {
- GLchar frag_src[2048];
- snprintf(frag_src, sizeof(frag_src),
- "#define SOURCE %d\n%s", source, tex_frag_src);
+struct fx_render_timer *fx_get_render_timer(struct wlr_render_timer *wlr_timer) {
+ assert(wlr_render_timer_is_fx(wlr_timer));
+ struct fx_render_timer *timer = wl_container_of(wlr_timer, timer, base);
+ return timer;
+}
- GLuint prog;
- shader->program = prog = link_program(frag_src);
- if (!shader->program) {
- return false;
- }
+static bool fx_bind_main_buffer(struct wlr_renderer *wlr_renderer,
+ struct wlr_buffer *wlr_buffer) {
+ struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
- shader->proj = glGetUniformLocation(prog, "proj");
- shader->tex = glGetUniformLocation(prog, "tex");
- shader->alpha = glGetUniformLocation(prog, "alpha");
- shader->pos_attrib = glGetAttribLocation(prog, "pos");
- shader->tex_attrib = glGetAttribLocation(prog, "texcoord");
- shader->size = glGetUniformLocation(prog, "size");
- shader->position = glGetUniformLocation(prog, "position");
- shader->radius = glGetUniformLocation(prog, "radius");
+ if (renderer->current_buffer != NULL) {
+ assert(wlr_egl_is_current(renderer->egl));
- return true;
-}
+ push_fx_debug(renderer);
+ glFlush();
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ pop_fx_debug(renderer);
-static bool link_stencil_mask_program(struct stencil_mask_shader *shader) {
- GLuint prog;
- shader->program = prog = link_program(stencil_mask_frag_src);
- if (!shader->program) {
- return false;
+ wlr_buffer_unlock(renderer->current_buffer->buffer);
+ renderer->current_buffer = NULL;
}
- shader->proj = glGetUniformLocation(prog, "proj");
- shader->color = glGetUniformLocation(prog, "color");
- shader->pos_attrib = glGetAttribLocation(prog, "pos");
- shader->position = glGetUniformLocation(prog, "position");
- shader->half_size = glGetUniformLocation(prog, "half_size");
- shader->radius = glGetUniformLocation(prog, "radius");
+ if (wlr_buffer == NULL) {
+ wlr_egl_unset_current(renderer->egl);
+ return true;
+ }
- return true;
-}
+ wlr_egl_make_current(renderer->egl);
-static bool link_box_shadow_program(struct box_shadow_shader *shader) {
- GLuint prog;
- shader->program = prog = link_program(box_shadow_frag_src);
- if (!shader->program) {
+ struct fx_framebuffer *buffer = fx_framebuffer_get_or_create(renderer, wlr_buffer);
+ if (buffer == NULL) {
return false;
}
- shader->proj = glGetUniformLocation(prog, "proj");
- shader->color = glGetUniformLocation(prog, "color");
- shader->pos_attrib = glGetAttribLocation(prog, "pos");
- shader->position = glGetUniformLocation(prog, "position");
- shader->size = glGetUniformLocation(prog, "size");
- shader->blur_sigma = glGetUniformLocation(prog, "blur_sigma");
- shader->corner_radius = glGetUniformLocation(prog, "corner_radius");
- return true;
-}
+ wlr_buffer_lock(wlr_buffer);
+ renderer->current_buffer = buffer;
-static bool check_gl_ext(const char *exts, const char *ext) {
- size_t extlen = strlen(ext);
- const char *end = exts + strlen(exts);
+ push_fx_debug(renderer);
+ glBindFramebuffer(GL_FRAMEBUFFER, renderer->current_buffer->fbo);
+ pop_fx_debug(renderer);
- 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;
+ return true;
}
-static void load_gl_proc(void *proc_ptr, const char *name) {
- void *proc = (void *)eglGetProcAddress(name);
- if (proc == NULL) {
- wlr_log(WLR_ERROR, "GLES2 RENDERER: eglGetProcAddress(%s) failed", name);
- abort();
+static const char *reset_status_str(GLenum status) {
+ switch (status) {
+ case GL_GUILTY_CONTEXT_RESET_KHR:
+ return "guilty";
+ case GL_INNOCENT_CONTEXT_RESET_KHR:
+ return "innocent";
+ case GL_UNKNOWN_CONTEXT_RESET_KHR:
+ return "unknown";
+ default:
+ return "<invalid>";
}
- *(void **)proc_ptr = proc;
}
-static void fx_renderer_handle_destroy(struct wlr_addon *addon) {
+static bool fx_renderer_begin(struct wlr_renderer *wlr_renderer, uint32_t width,
+ uint32_t height) {
struct fx_renderer *renderer =
- wl_container_of(addon, renderer, addon);
- fx_renderer_fini(renderer);
- free(renderer);
-}
-static const struct wlr_addon_interface fx_renderer_addon_impl = {
- .name = "fx_renderer",
- .destroy = fx_renderer_handle_destroy,
-};
-
-void fx_renderer_init_addon(struct wlr_egl *egl, struct wlr_addon_set *addons,
- const void * owner) {
- struct fx_renderer *renderer = fx_renderer_create(egl);
- if (!renderer) {
- wlr_log(WLR_ERROR, "Failed to create fx_renderer");
- abort();
- }
- wlr_addon_init(&renderer->addon, addons, owner, &fx_renderer_addon_impl);
-}
-
-struct fx_renderer *fx_renderer_addon_find(struct wlr_addon_set *addons,
- const void * owner) {
- struct wlr_addon *addon =
- wlr_addon_find(addons, owner, &fx_renderer_addon_impl);
- if (addon == NULL) {
- return NULL;
- }
- struct fx_renderer *renderer = wl_container_of(addon, renderer, addon);
- return renderer;
-}
-
-struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) {
- struct fx_renderer *renderer = calloc(1, sizeof(struct fx_renderer));
- if (renderer == NULL) {
- return NULL;
- }
-
- if (!eglMakeCurrent(wlr_egl_get_display(egl), EGL_NO_SURFACE, EGL_NO_SURFACE,
- wlr_egl_get_context(egl))) {
- wlr_log(WLR_ERROR, "GLES2 RENDERER: Could not make EGL current");
- return NULL;
- }
-
- renderer->stencil_buffer = fx_stencilbuffer_create();
-
- // get extensions
- const char *exts_str = (const char *)glGetString(GL_EXTENSIONS);
- if (exts_str == NULL) {
- wlr_log(WLR_ERROR, "GLES2 RENDERER: Failed to get GL_EXTENSIONS");
- return NULL;
- }
-
- wlr_log(WLR_INFO, "Creating scenefx GLES2 renderer");
- wlr_log(WLR_INFO, "Using %s", glGetString(GL_VERSION));
- wlr_log(WLR_INFO, "GL vendor: %s", glGetString(GL_VENDOR));
- wlr_log(WLR_INFO, "GL renderer: %s", glGetString(GL_RENDERER));
- wlr_log(WLR_INFO, "Supported GLES2 extensions: %s", exts_str);
+ fx_get_renderer_in_context(wlr_renderer);
- // TODO: the rest of the gl checks
- if (check_gl_ext(exts_str, "GL_OES_EGL_image_external")) {
- renderer->exts.OES_egl_image_external = true;
- load_gl_proc(&renderer->procs.glEGLImageTargetTexture2DOES,
- "glEGLImageTargetTexture2DOES");
- }
+ push_fx_debug(renderer);
- // quad fragment shader
- if (!link_quad_program(&renderer->shaders.quad)) {
- goto error;
- }
- // fragment shaders
- if (!link_tex_program(&renderer->shaders.tex_rgba, SHADER_SOURCE_TEXTURE_RGBA)) {
- goto error;
- }
- if (!link_tex_program(&renderer->shaders.tex_rgbx, SHADER_SOURCE_TEXTURE_RGBX)) {
- goto error;
- }
- if (!link_tex_program(&renderer->shaders.tex_ext, SHADER_SOURCE_TEXTURE_EXTERNAL)) {
- goto error;
- }
-
- // stencil mask shader
- if (!link_stencil_mask_program(&renderer->shaders.stencil_mask)) {
- goto error;
- }
- // box shadow shader
- if (!link_box_shadow_program(&renderer->shaders.box_shadow)) {
- goto error;
+ if (renderer->procs.glGetGraphicsResetStatusKHR) {
+ GLenum status = renderer->procs.glGetGraphicsResetStatusKHR();
+ if (status != GL_NO_ERROR) {
+ wlr_log(WLR_ERROR, "GPU reset (%s)", reset_status_str(status));
+ wl_signal_emit_mutable(&wlr_renderer->events.lost, NULL);
+ pop_fx_debug(renderer);
+ return false;
+ }
}
- if (!eglMakeCurrent(wlr_egl_get_display(egl),
- EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
- wlr_log(WLR_ERROR, "GLES2 RENDERER: Could not unset current EGL");
- goto error;
- }
+ glViewport(0, 0, width, height);
+ renderer->viewport_width = width;
+ renderer->viewport_height = height;
- wlr_log(WLR_INFO, "GLES2 RENDERER: Shaders Initialized Successfully");
- return renderer;
+ // refresh projection matrix
+ matrix_projection(renderer->projection, width, height,
+ WL_OUTPUT_TRANSFORM_FLIPPED_180);
-error:
- glDeleteProgram(renderer->shaders.quad.program);
- glDeleteProgram(renderer->shaders.tex_rgba.program);
- glDeleteProgram(renderer->shaders.tex_rgbx.program);
- glDeleteProgram(renderer->shaders.tex_ext.program);
- glDeleteProgram(renderer->shaders.stencil_mask.program);
- glDeleteProgram(renderer->shaders.box_shadow.program);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- if (!eglMakeCurrent(wlr_egl_get_display(egl),
- EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
- wlr_log(WLR_ERROR, "GLES2 RENDERER: Could not unset current EGL");
- }
+ // XXX: maybe we should save output projection and remove some of the need
+ // for users to sling matricies themselves
- // TODO: more freeing?
- free(renderer);
+ pop_fx_debug(renderer);
- wlr_log(WLR_ERROR, "GLES2 RENDERER: Error Initializing Shaders");
- return NULL;
+ return true;
}
-void fx_renderer_fini(struct fx_renderer *renderer) {
- fx_stencilbuffer_release(&renderer->stencil_buffer);
+static void fx_renderer_end(struct wlr_renderer *wlr_renderer) {
+ fx_get_renderer_in_context(wlr_renderer);
+ // no-op
}
-void fx_renderer_begin(struct fx_renderer *renderer, int width, int height) {
- fx_stencilbuffer_init(&renderer->stencil_buffer, width, height);
-
- glViewport(0, 0, width, height);
-
- // refresh projection matrix
- matrix_projection(renderer->projection, width, height,
- WL_OUTPUT_TRANSFORM_FLIPPED_180);
-
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-}
+static void fx_renderer_clear(struct wlr_renderer *wlr_renderer,
+ const float color[static 4]) {
+ struct fx_renderer *renderer =
+ fx_get_renderer_in_context(wlr_renderer);
-void fx_renderer_clear(const float color[static 4]) {
+ push_fx_debug(renderer);
glClearColor(color[0], color[1], color[2], color[3]);
glClearStencil(0);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ pop_fx_debug(renderer);
}
-void fx_renderer_scissor(struct wlr_box *box) {
- if (box) {
+static void fx_renderer_scissor(struct wlr_renderer *wlr_renderer,
+ struct wlr_box *box) {
+ struct fx_renderer *renderer =
+ fx_get_renderer_in_context(wlr_renderer);
+
+ push_fx_debug(renderer);
+ if (box != NULL) {
glScissor(box->x, box->y, box->width, box->height);
glEnable(GL_SCISSOR_TEST);
} else {
glDisable(GL_SCISSOR_TEST);
}
+ pop_fx_debug(renderer);
}
-void fx_renderer_stencil_mask_init(void) {
- glClearStencil(0);
- glClear(GL_STENCIL_BUFFER_BIT);
- glEnable(GL_STENCIL_TEST);
-
- glStencilFunc(GL_ALWAYS, 1, 0xFF);
- glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
- // Disable writing to color buffer
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
-}
-
-void fx_renderer_stencil_mask_close(bool draw_inside_mask) {
- // Reenable writing to color buffer
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- if (draw_inside_mask) {
- glStencilFunc(GL_EQUAL, 1, 0xFF);
- glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
- return;
- }
- glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
- glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
-}
-
-void fx_renderer_stencil_mask_fini(void) {
- glClearStencil(0);
- glClear(GL_STENCIL_BUFFER_BIT);
- glDisable(GL_STENCIL_TEST);
-}
-
-void fx_renderer_stencil_enable(void) {
- glEnable(GL_STENCIL_TEST);
-}
-
-void fx_renderer_stencil_disable(void) {
- glDisable(GL_STENCIL_TEST);
-}
-
-bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer,
- struct wlr_texture *wlr_texture, const struct wlr_fbox *src_box,
- const struct wlr_box *dst_box, const float matrix[static 9],
- float opacity, int corner_radius) {
-
- assert(wlr_texture_is_gles2(wlr_texture));
- struct wlr_gles2_texture_attribs texture_attrs;
- wlr_gles2_texture_get_attribs(wlr_texture, &texture_attrs);
+static bool fx_render_subtexture_with_matrix(
+ struct wlr_renderer *wlr_renderer, struct wlr_texture *wlr_texture,
+ const struct wlr_fbox *box, const float matrix[static 9],
+ float alpha) {
+ struct fx_renderer *renderer = fx_get_renderer_in_context(wlr_renderer);
+ struct fx_texture *texture = fx_get_texture(wlr_texture);
+ assert(texture->fx_renderer == renderer);
struct tex_shader *shader = NULL;
- switch (texture_attrs.target) {
+ switch (texture->target) {
case GL_TEXTURE_2D:
- if (texture_attrs.has_alpha) {
+ if (texture->has_alpha) {
shader = &renderer->shaders.tex_rgba;
} else {
shader = &renderer->shaders.tex_rgbx;
}
break;
case GL_TEXTURE_EXTERNAL_OES:
+ // EGL_EXT_image_dma_buf_import_modifiers requires
+ // GL_OES_EGL_image_external
+ assert(renderer->exts.OES_egl_image_external);
shader = &renderer->shaders.tex_ext;
-
- if (!renderer->exts.OES_egl_image_external) {
- wlr_log(WLR_ERROR, "Failed to render texture: "
- "GL_TEXTURE_EXTERNAL_OES not supported");
- return false;
- }
break;
default:
wlr_log(WLR_ERROR, "Aborting render");
@@ -407,12 +215,9 @@ bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer,
float gl_matrix[9];
wlr_matrix_multiply(gl_matrix, renderer->projection, matrix);
- // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set
- // to GL_FALSE
- wlr_matrix_transpose(gl_matrix, gl_matrix);
+ push_fx_debug(renderer);
- // if there's no opacity or rounded corners we don't need to blend
- if (!texture_attrs.has_alpha && opacity == 1.0 && !corner_radius) {
+ if (!texture->has_alpha && alpha == 1.0) {
glDisable(GL_BLEND);
} else {
glEnable(GL_BLEND);
@@ -421,62 +226,49 @@ bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer,
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glActiveTexture(GL_TEXTURE0);
- glBindTexture(texture_attrs.target, texture_attrs.tex);
+ glBindTexture(texture->target, texture->tex);
- glTexParameteri(texture_attrs.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glUseProgram(shader->program);
glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix);
glUniform1i(shader->tex, 0);
- glUniform2f(shader->size, dst_box->width, dst_box->height);
- glUniform2f(shader->position, dst_box->x, dst_box->y);
- glUniform1f(shader->alpha, opacity);
- glUniform1f(shader->radius, corner_radius);
-
- const GLfloat x1 = src_box->x / wlr_texture->width;
- const GLfloat y1 = src_box->y / wlr_texture->height;
- const GLfloat x2 = (src_box->x + src_box->width) / wlr_texture->width;
- const GLfloat y2 = (src_box->y + src_box->height) / wlr_texture->height;
- const GLfloat texcoord[] = {
- x2, y1, // top right
- x1, y1, // top left
- x2, y2, // bottom right
- x1, y2, // bottom left
- };
+ glUniform1f(shader->alpha, alpha);
+ glUniform2f(shader->size, box->width, box->height);
+ glUniform2f(shader->position, box->x, box->y);
+ glUniform1f(shader->radius, 0);
+
+ float tex_matrix[9];
+ wlr_matrix_identity(tex_matrix);
+ wlr_matrix_translate(tex_matrix, box->x / texture->wlr_texture.width,
+ box->y / texture->wlr_texture.height);
+ wlr_matrix_scale(tex_matrix, box->width / texture->wlr_texture.width,
+ box->height / texture->wlr_texture.height);
+ glUniformMatrix3fv(shader->tex_proj, 1, GL_FALSE, tex_matrix);
glVertexAttribPointer(shader->pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts);
- glVertexAttribPointer(shader->tex_attrib, 2, GL_FLOAT, GL_FALSE, 0, texcoord);
glEnableVertexAttribArray(shader->pos_attrib);
- glEnableVertexAttribArray(shader->tex_attrib);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(shader->pos_attrib);
- glDisableVertexAttribArray(shader->tex_attrib);
- glBindTexture(texture_attrs.target, 0);
+ glBindTexture(texture->target, 0);
+ pop_fx_debug(renderer);
return true;
}
-void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box,
- const float color[static 4], const float projection[static 9]) {
- if (box->width == 0 || box->height == 0) {
- return;
- }
- assert(box->width > 0 && box->height > 0);
- float matrix[9];
- wlr_matrix_project_box(matrix, box, WL_OUTPUT_TRANSFORM_NORMAL, 0, projection);
+static void fx_render_quad_with_matrix(struct wlr_renderer *wlr_renderer,
+ const float color[static 4], const float matrix[static 9]) {
+ struct fx_renderer *renderer = fx_get_renderer_in_context(wlr_renderer);
float gl_matrix[9];
wlr_matrix_multiply(gl_matrix, renderer->projection, matrix);
- // TODO: investigate why matrix is flipped prior to this cmd
- // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix);
-
- wlr_matrix_transpose(gl_matrix, gl_matrix);
+ push_fx_debug(renderer);
if (color[3] == 1.0) {
glDisable(GL_BLEND);
@@ -484,115 +276,505 @@ void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box,
glEnable(GL_BLEND);
}
- struct quad_shader shader = renderer->shaders.quad;
- glUseProgram(shader.program);
+ glUseProgram(renderer->shaders.quad.program);
- glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix);
- glUniform4f(shader.color, color[0], color[1], color[2], color[3]);
+ glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, gl_matrix);
+ glUniform4f(renderer->shaders.quad.color, color[0], color[1], color[2], color[3]);
- glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE,
+ glVertexAttribPointer(renderer->shaders.quad.pos_attrib, 2, GL_FLOAT, GL_FALSE,
0, verts);
- glEnableVertexAttribArray(shader.pos_attrib);
+ glEnableVertexAttribArray(renderer->shaders.quad.pos_attrib);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- glDisableVertexAttribArray(shader.pos_attrib);
+ glDisableVertexAttribArray(renderer->shaders.quad.pos_attrib);
+
+ pop_fx_debug(renderer);
}
-static void fx_render_stencil_mask(struct fx_renderer *renderer,
- const struct wlr_box *box, const float matrix[static 9],
- int corner_radius) {
- if (box->width == 0 || box->height == 0) {
- return;
+static const uint32_t *fx_get_shm_texture_formats(
+ struct wlr_renderer *wlr_renderer, size_t *len) {
+ struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
+ return get_fx_shm_formats(renderer, len);
+}
+
+static const struct wlr_drm_format_set *fx_get_dmabuf_texture_formats(
+ struct wlr_renderer *wlr_renderer) {
+ struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
+ return wlr_egl_get_dmabuf_texture_formats(renderer->egl);
+}
+
+static const struct wlr_drm_format_set *fx_get_render_formats(
+ struct wlr_renderer *wlr_renderer) {
+ struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
+ return wlr_egl_get_dmabuf_render_formats(renderer->egl);
+}
+
+static uint32_t fx_preferred_read_format(
+ struct wlr_renderer *wlr_renderer) {
+ struct fx_renderer *renderer =
+ fx_get_renderer_in_context(wlr_renderer);
+
+ push_fx_debug(renderer);
+
+ GLint gl_format = -1, gl_type = -1, alpha_size = -1;
+ glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &gl_format);
+ glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &gl_type);
+ glGetIntegerv(GL_ALPHA_BITS, &alpha_size);
+
+ pop_fx_debug(renderer);
+
+ const struct fx_pixel_format *fmt =
+ get_fx_format_from_gl(gl_format, gl_type, alpha_size > 0);
+ if (fmt != NULL) {
+ return fmt->drm_format;
}
- assert(box->width > 0 && box->height > 0);
- // TODO: just pass gl_matrix?
- float gl_matrix[9];
- wlr_matrix_multiply(gl_matrix, renderer->projection, matrix);
+ if (renderer->exts.EXT_read_format_bgra) {
+ return DRM_FORMAT_XRGB8888;
+ }
+ return DRM_FORMAT_XBGR8888;
+}
- // TODO: investigate why matrix is flipped prior to this cmd
- // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix);
+static bool fx_read_pixels(struct wlr_renderer *wlr_renderer,
+ uint32_t drm_format, 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 fx_renderer *renderer =
+ fx_get_renderer_in_context(wlr_renderer);
- wlr_matrix_transpose(gl_matrix, gl_matrix);
+ const struct fx_pixel_format *fmt =
+ get_fx_format_from_drm(drm_format);
+ if (fmt == NULL || !is_fx_pixel_format_supported(renderer, fmt)) {
+ wlr_log(WLR_ERROR, "Cannot read pixels: unsupported pixel format 0x%"PRIX32, drm_format);
+ return false;
+ }
- glEnable(GL_BLEND);
+ if (fmt->gl_format == GL_BGRA_EXT && !renderer->exts.EXT_read_format_bgra) {
+ wlr_log(WLR_ERROR,
+ "Cannot read pixels: missing GL_EXT_read_format_bgra extension");
+ return false;
+ }
- struct stencil_mask_shader shader = renderer->shaders.stencil_mask;
+ const struct wlr_pixel_format_info *drm_fmt =
+ drm_get_pixel_format_info(fmt->drm_format);
+ assert(drm_fmt);
+ if (pixel_format_info_pixels_per_block(drm_fmt) != 1) {
+ wlr_log(WLR_ERROR, "Cannot read pixels: block formats are not supported");
+ return false;
+ }
- glUseProgram(shader.program);
+ push_fx_debug(renderer);
- glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix);
+ // Make sure any pending drawing is finished before we try to read it
+ glFinish();
- glUniform2f(shader.half_size, box->width * 0.5, box->height * 0.5);
- glUniform2f(shader.position, box->x, box->y);
- glUniform1f(shader.radius, corner_radius);
+ glGetError(); // Clear the error flag
- glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE,
- 0, verts);
+ unsigned char *p = (unsigned char *)data + dst_y * stride;
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ uint32_t pack_stride = pixel_format_info_min_stride(drm_fmt, width);
+ if (pack_stride == stride && dst_x == 0) {
+ // Under these particular conditions, we can read the pixels with only
+ // one glReadPixels call
- glEnableVertexAttribArray(shader.pos_attrib);
+ glReadPixels(src_x, src_y, width, height, fmt->gl_format, fmt->gl_type, p);
+ } else {
+ // Unfortunately GLES2 doesn't support GL_PACK_ROW_LENGTH, 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->bytes_per_block);
+ }
+ }
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ pop_fx_debug(renderer);
+
+ return glGetError() == GL_NO_ERROR;
+}
+
+static int fx_get_drm_fd(struct wlr_renderer *wlr_renderer) {
+ struct fx_renderer *renderer =
+ fx_get_renderer(wlr_renderer);
+
+ if (renderer->drm_fd < 0) {
+ renderer->drm_fd = wlr_egl_dup_drm_fd(renderer->egl);
+ }
- glDisableVertexAttribArray(shader.pos_attrib);
+ return renderer->drm_fd;
}
-void fx_render_box_shadow(struct fx_renderer *renderer,
- const struct wlr_box *box, const struct wlr_box *stencil_box,
- const float matrix[static 9], int corner_radius,
- struct shadow_data *shadow_data) {
- if (box->width == 0 || box->height == 0) {
+static uint32_t fx_get_render_buffer_caps(struct wlr_renderer *wlr_renderer) {
+ return WLR_BUFFER_CAP_DMABUF;
+}
+
+static void fx_renderer_destroy(struct wlr_renderer *wlr_renderer) {
+ struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
+
+ wlr_egl_make_current(renderer->egl);
+
+ struct fx_framebuffer *fx_buffer, *fx_buffer_tmp;
+ wl_list_for_each_safe(fx_buffer, fx_buffer_tmp, &renderer->buffers, link) {
+ fx_framebuffer_destroy(fx_buffer);
+ }
+
+ struct fx_texture *tex, *tex_tmp;
+ wl_list_for_each_safe(tex, tex_tmp, &renderer->textures, link) {
+ fx_texture_destroy(tex);
+ }
+
+ push_fx_debug(renderer);
+ glDeleteProgram(renderer->shaders.quad.program);
+ glDeleteProgram(renderer->shaders.tex_rgba.program);
+ glDeleteProgram(renderer->shaders.tex_rgbx.program);
+ glDeleteProgram(renderer->shaders.tex_ext.program);
+ pop_fx_debug(renderer);
+
+ if (renderer->exts.KHR_debug) {
+ glDisable(GL_DEBUG_OUTPUT_KHR);
+ renderer->procs.glDebugMessageCallbackKHR(NULL, NULL);
+ }
+
+ wlr_egl_unset_current(renderer->egl);
+ wlr_egl_destroy(renderer->egl);
+
+ if (renderer->drm_fd >= 0) {
+ close(renderer->drm_fd);
+ }
+
+ free(renderer);
+}
+
+static struct wlr_render_pass *begin_buffer_pass(struct wlr_renderer *wlr_renderer,
+ struct wlr_buffer *wlr_buffer, const struct wlr_buffer_pass_options *options) {
+ struct fx_gles_render_pass *pass =
+ fx_renderer_begin_buffer_pass(wlr_renderer, wlr_buffer, options);
+ if (!pass) {
+ return NULL;
+ }
+ return &pass->base;
+}
+
+static struct wlr_render_timer *fx_render_timer_create(struct wlr_renderer *wlr_renderer) {
+ struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
+ if (!renderer->exts.EXT_disjoint_timer_query) {
+ wlr_log(WLR_ERROR, "can't create timer, EXT_disjoint_timer_query not available");
+ return NULL;
+ }
+
+ struct fx_render_timer *timer = calloc(1, sizeof(*timer));
+ if (!timer) {
+ return NULL;
+ }
+ timer->base.impl = &render_timer_impl;
+ timer->renderer = renderer;
+
+ struct wlr_egl_context prev_ctx;
+ wlr_egl_save_context(&prev_ctx);
+ wlr_egl_make_current(renderer->egl);
+ renderer->procs.glGenQueriesEXT(1, &timer->id);
+ wlr_egl_restore_context(&prev_ctx);
+
+ return &timer->base;
+}
+
+static int fx_get_render_time(struct wlr_render_timer *wlr_timer) {
+ struct fx_render_timer *timer = fx_get_render_timer(wlr_timer);
+ struct fx_renderer *renderer = timer->renderer;
+
+ struct wlr_egl_context prev_ctx;
+ wlr_egl_save_context(&prev_ctx);
+ wlr_egl_make_current(renderer->egl);
+
+ GLint64 disjoint;
+ renderer->procs.glGetInteger64vEXT(GL_GPU_DISJOINT_EXT, &disjoint);
+ if (disjoint) {
+ wlr_log(WLR_ERROR, "a disjoint operation occurred and the render timer is invalid");
+ wlr_egl_restore_context(&prev_ctx);
+ return -1;
+ }
+
+ GLint available;
+ renderer->procs.glGetQueryObjectivEXT(timer->id,
+ GL_QUERY_RESULT_AVAILABLE_EXT, &available);
+ if (!available) {
+ wlr_log(WLR_ERROR, "timer was read too early, gpu isn't done!");
+ wlr_egl_restore_context(&prev_ctx);
+ return -1;
+ }
+
+ GLuint64 gl_render_end;
+ renderer->procs.glGetQueryObjectui64vEXT(timer->id, GL_QUERY_RESULT_EXT,
+ &gl_render_end);
+
+ int64_t cpu_nsec_total = timespec_to_nsec(&timer->cpu_end) - timespec_to_nsec(&timer->cpu_start);
+
+ wlr_egl_restore_context(&prev_ctx);
+ return gl_render_end - timer->gl_cpu_end + cpu_nsec_total;
+}
+
+static void fx_render_timer_destroy(struct wlr_render_timer *wlr_timer) {
+ struct fx_render_timer *timer = wl_container_of(wlr_timer, timer, base);
+ struct fx_renderer *renderer = timer->renderer;
+
+ struct wlr_egl_context prev_ctx;
+ wlr_egl_save_context(&prev_ctx);
+ wlr_egl_make_current(renderer->egl);
+ renderer->procs.glDeleteQueriesEXT(1, &timer->id);
+ wlr_egl_restore_context(&prev_ctx);
+ free(timer);
+}
+
+static const struct wlr_renderer_impl renderer_impl = {
+ .destroy = fx_renderer_destroy,
+ .bind_buffer = fx_bind_main_buffer,
+ .begin = fx_renderer_begin,
+ .end = fx_renderer_end,
+ .clear = fx_renderer_clear,
+ .scissor = fx_renderer_scissor,
+ .render_subtexture_with_matrix = fx_render_subtexture_with_matrix,
+ .render_quad_with_matrix = fx_render_quad_with_matrix,
+ .get_shm_texture_formats = fx_get_shm_texture_formats,
+ .get_dmabuf_texture_formats = fx_get_dmabuf_texture_formats,
+ .get_render_formats = fx_get_render_formats,
+ .preferred_read_format = fx_preferred_read_format,
+ .read_pixels = fx_read_pixels,
+ .get_drm_fd = fx_get_drm_fd,
+ .get_render_buffer_caps = fx_get_render_buffer_caps,
+ .texture_from_buffer = fx_texture_from_buffer,
+ .begin_buffer_pass = begin_buffer_pass,
+ .render_timer_create = fx_render_timer_create,
+};
+
+static const struct wlr_render_timer_impl render_timer_impl = {
+ .get_duration_ns = fx_get_render_time,
+ .destroy = fx_render_timer_destroy,
+};
+
+void push_fx_debug_(struct fx_renderer *renderer,
+ const char *file, const char *func) {
+ if (!renderer->procs.glPushDebugGroupKHR) {
return;
}
- assert(box->width > 0 && box->height > 0);
- float *color = shadow_data->color;
- float blur_sigma = shadow_data->blur_sigma;
+ int len = snprintf(NULL, 0, "%s:%s", file, func) + 1;
+ char str[len];
+ snprintf(str, len, "%s:%s", file, func);
+ renderer->procs.glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, 1, -1, str);
+}
- float gl_matrix[9];
- wlr_matrix_multiply(gl_matrix, renderer->projection, matrix);
+void pop_fx_debug(struct fx_renderer *renderer) {
+ if (renderer->procs.glPopDebugGroupKHR) {
+ renderer->procs.glPopDebugGroupKHR();
+ }
+}
+
+static enum wlr_log_importance fx_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;
+ }
+}
- // TODO: investigate why matrix is flipped prior to this cmd
- // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix);
+static void fx_log(GLenum src, GLenum type, GLuint id, GLenum severity,
+ GLsizei len, const GLchar *msg, const void *user) {
+ _wlr_log(fx_log_importance_to_wlr(type), "[GLES2] %s", msg);
+}
- wlr_matrix_transpose(gl_matrix, gl_matrix);
+static struct wlr_renderer *renderer_autocreate(struct wlr_backend *backend, int drm_fd) {
+ bool own_drm_fd = false;
+ if (!open_preferred_drm_fd(backend, &drm_fd, &own_drm_fd)) {
+ wlr_log(WLR_ERROR, "Cannot create GLES2 renderer: no DRM FD available");
+ return NULL;
+ }
+
+ struct wlr_egl *egl = wlr_egl_create_with_drm_fd(drm_fd);
+ if (egl == NULL) {
+ wlr_log(WLR_ERROR, "Could not initialize EGL");
+ return NULL;
+ }
- // Init stencil work
- fx_renderer_stencil_mask_init();
- // Draw the rounded rect as a mask
- fx_render_stencil_mask(renderer, stencil_box, matrix, corner_radius);
- fx_renderer_stencil_mask_close(false);
+ struct wlr_renderer *renderer = fx_renderer_create_egl(egl);
+ if (!renderer) {
+ wlr_log(WLR_ERROR, "Failed to create the FX renderer");
+ wlr_egl_destroy(egl);
+ return NULL;
+ }
- // blending will practically always be needed (unless we have a madman
- // who uses opaque shadows with zero sigma), so just enable it
- glEnable(GL_BLEND);
+ if (own_drm_fd && drm_fd >= 0) {
+ close(drm_fd);
+ }
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ return renderer;
+}
- struct box_shadow_shader shader = renderer->shaders.box_shadow;
+struct wlr_renderer *fx_renderer_create_with_drm_fd(int drm_fd) {
+ assert(drm_fd >= 0);
- glUseProgram(shader.program);
+ return renderer_autocreate(NULL, drm_fd);
+}
- glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix);
- glUniform4f(shader.color, color[0], color[1], color[2], color[3]);
- glUniform1f(shader.blur_sigma, blur_sigma);
- glUniform1f(shader.corner_radius, corner_radius);
+struct wlr_renderer *fx_renderer_create(struct wlr_backend *backend) {
+ return renderer_autocreate(backend, -1);
+}
- glUniform2f(shader.size, box->width, box->height);
- glUniform2f(shader.position, box->x, box->y);
+struct wlr_renderer *fx_renderer_create_egl(struct wlr_egl *egl) {
+ if (!wlr_egl_make_current(egl)) {
+ return NULL;
+ }
- glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE,
- 0, verts);
+ const char *exts_str = (const char *)glGetString(GL_EXTENSIONS);
+ if (exts_str == NULL) {
+ wlr_log(WLR_ERROR, "Failed to get GL_EXTENSIONS");
+ return NULL;
+ }
- glEnableVertexAttribArray(shader.pos_attrib);
+ struct fx_renderer *renderer = calloc(1, sizeof(struct fx_renderer));
+ if (renderer == NULL) {
+ return NULL;
+ }
+ wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ wl_list_init(&renderer->buffers);
+ wl_list_init(&renderer->textures);
- glDisableVertexAttribArray(shader.pos_attrib);
+ renderer->egl = egl;
+ renderer->exts_str = exts_str;
+ renderer->drm_fd = -1;
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ wlr_log(WLR_INFO, "Creating scenefx FX renderer");
+ wlr_log(WLR_INFO, "Using %s", glGetString(GL_VERSION));
+ wlr_log(WLR_INFO, "GL vendor: %s", glGetString(GL_VENDOR));
+ wlr_log(WLR_INFO, "GL renderer: %s", glGetString(GL_RENDERER));
+ wlr_log(WLR_INFO, "Supported FX extensions: %s", exts_str);
+
+ if (!renderer->egl->exts.EXT_image_dma_buf_import) {
+ wlr_log(WLR_ERROR, "EGL_EXT_image_dma_buf_import not supported");
+ free(renderer);
+ return NULL;
+ }
+ if (!check_gl_ext(exts_str, "GL_EXT_texture_format_BGRA8888")) {
+ wlr_log(WLR_ERROR, "BGRA8888 format not supported by GLES2");
+ free(renderer);
+ return NULL;
+ }
+ if (!check_gl_ext(exts_str, "GL_EXT_unpack_subimage")) {
+ wlr_log(WLR_ERROR, "GL_EXT_unpack_subimage not supported");
+ free(renderer);
+ return NULL;
+ }
+
+ renderer->exts.EXT_read_format_bgra =
+ check_gl_ext(exts_str, "GL_EXT_read_format_bgra");
+
+ renderer->exts.EXT_texture_type_2_10_10_10_REV =
+ check_gl_ext(exts_str, "GL_EXT_texture_type_2_10_10_10_REV");
+
+ renderer->exts.OES_texture_half_float_linear =
+ check_gl_ext(exts_str, "GL_OES_texture_half_float_linear");
- fx_renderer_stencil_mask_fini();
+ renderer->exts.EXT_texture_norm16 =
+ check_gl_ext(exts_str, "GL_EXT_texture_norm16");
+
+ if (check_gl_ext(exts_str, "GL_KHR_debug")) {
+ renderer->exts.KHR_debug = true;
+ load_gl_proc(&renderer->procs.glDebugMessageCallbackKHR,
+ "glDebugMessageCallbackKHR");
+ load_gl_proc(&renderer->procs.glDebugMessageControlKHR,
+ "glDebugMessageControlKHR");
+ }
+
+ // TODO: the rest of the gl checks
+ if (check_gl_ext(exts_str, "GL_OES_EGL_image_external")) {
+ renderer->exts.OES_egl_image_external = true;
+ load_gl_proc(&renderer->procs.glEGLImageTargetTexture2DOES,
+ "glEGLImageTargetTexture2DOES");
+ }
+
+ if (check_gl_ext(exts_str, "GL_OES_EGL_image")) {
+ renderer->exts.OES_egl_image = true;
+ load_gl_proc(&renderer->procs.glEGLImageTargetRenderbufferStorageOES,
+ "glEGLImageTargetRenderbufferStorageOES");
+ }
+
+ if (check_gl_ext(exts_str, "GL_KHR_robustness")) {
+ GLint notif_strategy = 0;
+ glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_KHR, &notif_strategy);
+ switch (notif_strategy) {
+ case GL_LOSE_CONTEXT_ON_RESET_KHR:
+ wlr_log(WLR_DEBUG, "GPU reset notifications are enabled");
+ load_gl_proc(&renderer->procs.glGetGraphicsResetStatusKHR,
+ "glGetGraphicsResetStatusKHR");
+ break;
+ case GL_NO_RESET_NOTIFICATION_KHR:
+ wlr_log(WLR_DEBUG, "GPU reset notifications are disabled");
+ break;
+ }
+ }
+
+ if (check_gl_ext(exts_str, "GL_EXT_disjoint_timer_query")) {
+ renderer->exts.EXT_disjoint_timer_query = true;
+ load_gl_proc(&renderer->procs.glGenQueriesEXT, "glGenQueriesEXT");
+ load_gl_proc(&renderer->procs.glDeleteQueriesEXT, "glDeleteQueriesEXT");
+ load_gl_proc(&renderer->procs.glQueryCounterEXT, "glQueryCounterEXT");
+ load_gl_proc(&renderer->procs.glGetQueryObjectivEXT, "glGetQueryObjectivEXT");
+ load_gl_proc(&renderer->procs.glGetQueryObjectui64vEXT, "glGetQueryObjectui64vEXT");
+ load_gl_proc(&renderer->procs.glGetInteger64vEXT, "glGetInteger64vEXT");
+ }
+
+ if (renderer->exts.KHR_debug) {
+ glEnable(GL_DEBUG_OUTPUT_KHR);
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
+ renderer->procs.glDebugMessageCallbackKHR(fx_log, NULL);
+
+ // Silence unwanted message types
+ renderer->procs.glDebugMessageControlKHR(GL_DONT_CARE,
+ GL_DEBUG_TYPE_POP_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE);
+ renderer->procs.glDebugMessageControlKHR(GL_DONT_CARE,
+ GL_DEBUG_TYPE_PUSH_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE);
+ }
+
+ push_fx_debug(renderer);
+
+ // Link all shaders
+ if (!link_shaders(renderer)) {
+ goto error;
+ }
+ pop_fx_debug(renderer);
+
+ wlr_log(WLR_INFO, "FX RENDERER: Shaders Initialized Successfully");
+
+ wlr_egl_unset_current(renderer->egl);
+
+ return &renderer->wlr_renderer;
+
+error:
+ glDeleteProgram(renderer->shaders.quad.program);
+ glDeleteProgram(renderer->shaders.tex_rgba.program);
+ glDeleteProgram(renderer->shaders.tex_rgbx.program);
+ glDeleteProgram(renderer->shaders.tex_ext.program);
+ glDeleteProgram(renderer->shaders.stencil_mask.program);
+ glDeleteProgram(renderer->shaders.box_shadow.program);
+
+ pop_fx_debug(renderer);
+
+ if (renderer->exts.KHR_debug) {
+ glDisable(GL_DEBUG_OUTPUT_KHR);
+ renderer->procs.glDebugMessageCallbackKHR(NULL, NULL);
+ }
+
+ wlr_egl_unset_current(renderer->egl);
+
+ free(renderer);
+ return NULL;
}
diff --git a/render/fx_renderer/fx_stencilbuffer.c b/render/fx_renderer/fx_stencilbuffer.c
deleted file mode 100644
index 4f57216..0000000
--- a/render/fx_renderer/fx_stencilbuffer.c
+++ /dev/null
@@ -1,50 +0,0 @@
-#include <assert.h>
-#include <wlr/render/gles2.h>
-#include <wlr/util/log.h>
-
-#include "include/render/fx_renderer/fx_stencilbuffer.h"
-
-struct fx_stencilbuffer fx_stencilbuffer_create(void) {
- return (struct fx_stencilbuffer) {
- .rb = -1,
- .width = -1,
- .height = -1,
- };
-}
-
-void fx_stencilbuffer_init(struct fx_stencilbuffer *stencil_buffer, int width, int height) {
- bool first_alloc = false;
-
- if (stencil_buffer->rb == (uint32_t) -1) {
- glGenRenderbuffers(1, &stencil_buffer->rb);
- first_alloc = true;
- }
-
- if (first_alloc || stencil_buffer->width != width || stencil_buffer->height != height) {
- glBindRenderbuffer(GL_RENDERBUFFER, stencil_buffer->rb);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
- stencil_buffer->width = width;
- stencil_buffer->height = height;
-
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- wlr_log(WLR_ERROR,
- "Stencil buffer incomplete, couldn't create! (FB status: %i)",
- status);
- return;
- }
- }
-
- // Reattach the RenderBuffer to the FrameBuffer
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, stencil_buffer->rb);
-}
-
-void fx_stencilbuffer_release(struct fx_stencilbuffer *stencil_buffer) {
- if (stencil_buffer->rb != (uint32_t) -1 && stencil_buffer->rb) {
- glDeleteRenderbuffers(1, &stencil_buffer->rb);
- }
- stencil_buffer->rb = -1;
- stencil_buffer->width = -1;
- stencil_buffer->height = -1;
-}
diff --git a/render/fx_renderer/fx_texture.c b/render/fx_renderer/fx_texture.c
new file mode 100644
index 0000000..1952311
--- /dev/null
+++ b/render/fx_renderer/fx_texture.c
@@ -0,0 +1,368 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <wlr/render/interface.h>
+#include <wlr/types/wlr_buffer.h>
+#include <wlr/util/log.h>
+#include <drm_fourcc.h>
+
+#include "render/fx_renderer/fx_renderer.h"
+#include "render/pixel_format.h"
+#include "render/egl.h"
+
+static const struct wlr_texture_impl texture_impl;
+
+bool wlr_texture_is_fx(struct wlr_texture *wlr_texture) {
+ return wlr_texture->impl == &texture_impl;
+}
+
+struct fx_texture *fx_get_texture(struct wlr_texture *wlr_texture) {
+ assert(wlr_texture_is_fx(wlr_texture));
+ struct fx_texture *texture = wl_container_of(wlr_texture, texture, wlr_texture);
+ return texture;
+}
+
+static bool fx_texture_update_from_buffer(struct wlr_texture *wlr_texture,
+ struct wlr_buffer *buffer, const pixman_region32_t *damage) {
+ struct fx_texture *texture = fx_get_texture(wlr_texture);
+
+ if (texture->target != GL_TEXTURE_2D || texture->image != EGL_NO_IMAGE_KHR) {
+ return false;
+ }
+
+ void *data;
+ uint32_t format;
+ size_t stride;
+ if (!wlr_buffer_begin_data_ptr_access(buffer,
+ WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) {
+ return false;
+ }
+
+ if (format != texture->drm_format) {
+ wlr_buffer_end_data_ptr_access(buffer);
+ return false;
+ }
+
+ const struct fx_pixel_format *fmt =
+ get_fx_format_from_drm(texture->drm_format);
+ assert(fmt);
+
+ const struct wlr_pixel_format_info *drm_fmt =
+ drm_get_pixel_format_info(texture->drm_format);
+ assert(drm_fmt);
+ if (pixel_format_info_pixels_per_block(drm_fmt) != 1) {
+ wlr_buffer_end_data_ptr_access(buffer);
+ wlr_log(WLR_ERROR, "Cannot update texture: block formats are not supported");
+ return false;
+ }
+
+ if (!pixel_format_info_check_stride(drm_fmt, stride, buffer->width)) {
+ wlr_buffer_end_data_ptr_access(buffer);
+ return false;
+ }
+
+ struct wlr_egl_context prev_ctx;
+ wlr_egl_save_context(&prev_ctx);
+ wlr_egl_make_current(texture->fx_renderer->egl);
+
+ push_fx_debug(texture->fx_renderer);
+
+ glBindTexture(GL_TEXTURE_2D, texture->tex);
+
+ int rects_len = 0;
+ pixman_box32_t *rects = pixman_region32_rectangles(damage, &rects_len);
+
+ for (int i = 0; i < rects_len; i++) {
+ pixman_box32_t rect = rects[i];
+
+ glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / drm_fmt->bytes_per_block);
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, rect.x1);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, rect.y1);
+
+ int width = rect.x2 - rect.x1;
+ int height = rect.y2 - rect.y1;
+ glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x1, rect.y1, 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);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ pop_fx_debug(texture->fx_renderer);
+
+ wlr_egl_restore_context(&prev_ctx);
+
+ wlr_buffer_end_data_ptr_access(buffer);
+
+ return true;
+}
+
+static bool fx_texture_invalidate(struct fx_texture *texture) {
+ if (texture->image == EGL_NO_IMAGE_KHR) {
+ return false;
+ }
+ if (texture->target == GL_TEXTURE_EXTERNAL_OES) {
+ // External changes are immediately made visible by the GL implementation
+ return true;
+ }
+
+ struct wlr_egl_context prev_ctx;
+ wlr_egl_save_context(&prev_ctx);
+ wlr_egl_make_current(texture->fx_renderer->egl);
+
+ push_fx_debug(texture->fx_renderer);
+
+ glBindTexture(texture->target, texture->tex);
+ texture->fx_renderer->procs.glEGLImageTargetTexture2DOES(texture->target,
+ texture->image);
+ glBindTexture(texture->target, 0);
+
+ pop_fx_debug(texture->fx_renderer);
+
+ wlr_egl_restore_context(&prev_ctx);
+
+ return true;
+}
+
+void fx_texture_destroy(struct fx_texture *texture) {
+ wl_list_remove(&texture->link);
+ if (texture->buffer != NULL) {
+ wlr_addon_finish(&texture->buffer_addon);
+ }
+
+ struct wlr_egl_context prev_ctx;
+ wlr_egl_save_context(&prev_ctx);
+ wlr_egl_make_current(texture->fx_renderer->egl);
+
+ glDeleteTextures(1, &texture->tex);
+ wlr_egl_destroy_image(texture->fx_renderer->egl, texture->image);
+
+ wlr_egl_restore_context(&prev_ctx);
+
+ free(texture);
+}
+
+static void fx_texture_unref(struct wlr_texture *wlr_texture) {
+ struct fx_texture *texture = fx_get_texture(wlr_texture);
+ if (texture->buffer != NULL) {
+ // Keep the texture around, in case the buffer is re-used later. We're
+ // still listening to the buffer's destroy event.
+ wlr_buffer_unlock(texture->buffer);
+ } else {
+ fx_texture_destroy(texture);
+ }
+}
+
+static const struct wlr_texture_impl texture_impl = {
+ .update_from_buffer = fx_texture_update_from_buffer,
+ .destroy = fx_texture_unref,
+};
+
+static struct fx_texture *fx_texture_create(
+ struct fx_renderer *renderer, uint32_t width, uint32_t height) {
+ struct fx_texture *texture = calloc(1, sizeof(struct fx_texture));
+ if (texture == NULL) {
+ wlr_log_errno(WLR_ERROR, "Allocation failed");
+ return NULL;
+ }
+ wlr_texture_init(&texture->wlr_texture, &renderer->wlr_renderer,
+ &texture_impl, width, height);
+ texture->fx_renderer = renderer;
+ wl_list_insert(&renderer->textures, &texture->link);
+ return texture;
+}
+
+static struct wlr_texture *fx_texture_from_pixels(
+ struct wlr_renderer *wlr_renderer,
+ uint32_t drm_format, uint32_t stride, uint32_t width,
+ uint32_t height, const void *data) {
+ struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
+
+ const struct fx_pixel_format *fmt = get_fx_format_from_drm(drm_format);
+ if (fmt == NULL) {
+ wlr_log(WLR_ERROR, "Unsupported pixel format 0x%"PRIX32, drm_format);
+ return NULL;
+ }
+
+ const struct wlr_pixel_format_info *drm_fmt =
+ drm_get_pixel_format_info(drm_format);
+ assert(drm_fmt);
+ if (pixel_format_info_pixels_per_block(drm_fmt) != 1) {
+ wlr_log(WLR_ERROR, "Cannot upload texture: block formats are not supported");
+ return NULL;
+ }
+
+ if (!pixel_format_info_check_stride(drm_fmt, stride, width)) {
+ return NULL;
+ }
+
+ struct fx_texture *texture =
+ fx_texture_create(renderer, width, height);
+ if (texture == NULL) {
+ return NULL;
+ }
+ texture->target = GL_TEXTURE_2D;
+ texture->has_alpha = fmt->has_alpha;
+ texture->drm_format = fmt->drm_format;
+
+ GLint internal_format = fmt->gl_internalformat;
+ if (!internal_format) {
+ internal_format = fmt->gl_format;
+ }
+
+ struct wlr_egl_context prev_ctx;
+ wlr_egl_save_context(&prev_ctx);
+ wlr_egl_make_current(renderer->egl);
+
+ push_fx_debug(renderer);
+
+ glGenTextures(1, &texture->tex);
+ glBindTexture(GL_TEXTURE_2D, texture->tex);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / drm_fmt->bytes_per_block);
+ glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0,
+ fmt->gl_format, fmt->gl_type, data);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ pop_fx_debug(renderer);
+
+ wlr_egl_restore_context(&prev_ctx);
+
+ return &texture->wlr_texture;
+}
+
+static struct wlr_texture *fx_texture_from_dmabuf(
+ struct wlr_renderer *wlr_renderer,
+ struct wlr_dmabuf_attributes *attribs) {
+ struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
+
+ if (!renderer->procs.glEGLImageTargetTexture2DOES) {
+ return NULL;
+ }
+
+ struct fx_texture *texture =
+ fx_texture_create(renderer, attribs->width, attribs->height);
+ if (texture == NULL) {
+ return NULL;
+ }
+ texture->drm_format = DRM_FORMAT_INVALID; // texture can't be written anyways
+
+ const struct wlr_pixel_format_info *drm_fmt =
+ drm_get_pixel_format_info(attribs->format);
+ if (drm_fmt != NULL) {
+ texture->has_alpha = drm_fmt->has_alpha;
+ } else {
+ // We don't know, assume the texture has an alpha channel
+ texture->has_alpha = true;
+ }
+
+ struct wlr_egl_context prev_ctx;
+ wlr_egl_save_context(&prev_ctx);
+ wlr_egl_make_current(renderer->egl);
+
+ bool external_only;
+ texture->image =
+ wlr_egl_create_image_from_dmabuf(renderer->egl, attribs, &external_only);
+ if (texture->image == EGL_NO_IMAGE_KHR) {
+ wlr_log(WLR_ERROR, "Failed to create EGL image from DMA-BUF");
+ wlr_egl_restore_context(&prev_ctx);
+ wl_list_remove(&texture->link);
+ free(texture);
+ return NULL;
+ }
+
+ texture->target = external_only ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
+
+ push_fx_debug(renderer);
+
+ glGenTextures(1, &texture->tex);
+ glBindTexture(texture->target, texture->tex);
+ glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ renderer->procs.glEGLImageTargetTexture2DOES(texture->target, texture->image);
+ glBindTexture(texture->target, 0);
+
+ pop_fx_debug(renderer);
+
+ wlr_egl_restore_context(&prev_ctx);
+
+ return &texture->wlr_texture;
+}
+
+static void texture_handle_buffer_destroy(struct wlr_addon *addon) {
+ struct fx_texture *texture =
+ wl_container_of(addon, texture, buffer_addon);
+ fx_texture_destroy(texture);
+}
+
+static const struct wlr_addon_interface texture_addon_impl = {
+ .name = "fx_texture",
+ .destroy = texture_handle_buffer_destroy,
+};
+
+static struct wlr_texture *fx_texture_from_dmabuf_buffer(
+ struct fx_renderer *renderer, struct wlr_buffer *buffer,
+ struct wlr_dmabuf_attributes *dmabuf) {
+ struct wlr_addon *addon =
+ wlr_addon_find(&buffer->addons, renderer, &texture_addon_impl);
+ if (addon != NULL) {
+ struct fx_texture *texture =
+ wl_container_of(addon, texture, buffer_addon);
+ if (!fx_texture_invalidate(texture)) {
+ wlr_log(WLR_ERROR, "Failed to invalidate texture");
+ return false;
+ }
+ wlr_buffer_lock(texture->buffer);
+ return &texture->wlr_texture;
+ }
+
+ struct wlr_texture *wlr_texture =
+ fx_texture_from_dmabuf(&renderer->wlr_renderer, dmabuf);
+ if (wlr_texture == NULL) {
+ return false;
+ }
+
+ struct fx_texture *texture = fx_get_texture(wlr_texture);
+ texture->buffer = wlr_buffer_lock(buffer);
+ wlr_addon_init(&texture->buffer_addon, &buffer->addons,
+ renderer, &texture_addon_impl);
+
+ return &texture->wlr_texture;
+}
+
+struct wlr_texture *fx_texture_from_buffer(struct wlr_renderer *wlr_renderer,
+ struct wlr_buffer *buffer) {
+ struct fx_renderer *renderer = fx_get_renderer(wlr_renderer);
+
+ void *data;
+ uint32_t format;
+ size_t stride;
+ struct wlr_dmabuf_attributes dmabuf;
+ if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) {
+ return fx_texture_from_dmabuf_buffer(renderer, buffer, &dmabuf);
+ } else if (wlr_buffer_begin_data_ptr_access(buffer,
+ WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) {
+ struct wlr_texture *tex = fx_texture_from_pixels(wlr_renderer,
+ format, stride, buffer->width, buffer->height, data);
+ wlr_buffer_end_data_ptr_access(buffer);
+ return tex;
+ } else {
+ return NULL;
+ }
+}
+
+void fx_texture_get_attribs(struct wlr_texture *wlr_texture,
+ struct fx_texture_attribs *attribs) {
+ struct fx_texture *texture = fx_get_texture(wlr_texture);
+ *attribs = (struct fx_texture_attribs){
+ .target = texture->target,
+ .tex = texture->tex,
+ .has_alpha = texture->has_alpha,
+ };
+}
diff --git a/render/fx_renderer/gles2/shaders/box_shadow.frag b/render/fx_renderer/gles2/shaders/box_shadow.frag
index c9b2b91..92d40fc 100644
--- a/render/fx_renderer/gles2/shaders/box_shadow.frag
+++ b/render/fx_renderer/gles2/shaders/box_shadow.frag
@@ -1,6 +1,11 @@
// Writeup: https://madebyevan.com/shaders/fast-rounded-rectangle-shadows/
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
precision mediump float;
+#endif
+
varying vec4 v_color;
varying vec2 v_texcoord;
diff --git a/render/fx_renderer/gles2/shaders/common.vert b/render/fx_renderer/gles2/shaders/common.vert
index 811e0f2..9e7b073 100644
--- a/render/fx_renderer/gles2/shaders/common.vert
+++ b/render/fx_renderer/gles2/shaders/common.vert
@@ -1,12 +1,13 @@
uniform mat3 proj;
uniform vec4 color;
+uniform mat3 tex_proj;
attribute vec2 pos;
-attribute vec2 texcoord;
varying vec4 v_color;
varying vec2 v_texcoord;
void main() {
- gl_Position = vec4(proj * vec3(pos, 1.0), 1.0);
- v_color = color;
- v_texcoord = texcoord;
+ vec3 pos3 = vec3(pos, 1.0);
+ gl_Position = vec4(pos3 * proj, 1.0);
+ v_color = color;
+ v_texcoord = (pos3 * tex_proj).xy;
}
diff --git a/render/fx_renderer/gles2/shaders/quad.frag b/render/fx_renderer/gles2/shaders/quad.frag
index 7c76327..97d3a31 100644
--- a/render/fx_renderer/gles2/shaders/quad.frag
+++ b/render/fx_renderer/gles2/shaders/quad.frag
@@ -1,7 +1,13 @@
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
precision mediump float;
+#endif
+
varying vec4 v_color;
varying vec2 v_texcoord;
+uniform vec4 color;
void main() {
- gl_FragColor = v_color;
+ gl_FragColor = color;
}
diff --git a/render/fx_renderer/gles2/shaders/stencil_mask.frag b/render/fx_renderer/gles2/shaders/stencil_mask.frag
index ee03307..523adc8 100644
--- a/render/fx_renderer/gles2/shaders/stencil_mask.frag
+++ b/render/fx_renderer/gles2/shaders/stencil_mask.frag
@@ -1,4 +1,9 @@
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
precision mediump float;
+#endif
+
varying vec2 v_texcoord;
uniform vec2 half_size;
diff --git a/render/fx_renderer/gles2/shaders/tex.frag b/render/fx_renderer/gles2/shaders/tex.frag
index bd3c596..8c14373 100644
--- a/render/fx_renderer/gles2/shaders/tex.frag
+++ b/render/fx_renderer/gles2/shaders/tex.frag
@@ -10,7 +10,11 @@
#extension GL_OES_EGL_image_external : require
#endif
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
precision mediump float;
+#endif
varying vec2 v_texcoord;
@@ -21,6 +25,7 @@ uniform sampler2D tex;
#endif
uniform float alpha;
+
uniform vec2 size;
uniform vec2 position;
uniform float radius;
diff --git a/render/fx_renderer/meson.build b/render/fx_renderer/meson.build
index 394caa3..d7160a8 100644
--- a/render/fx_renderer/meson.build
+++ b/render/fx_renderer/meson.build
@@ -7,7 +7,12 @@ endif
wlr_files += files(
'matrix.c',
- 'fx_stencilbuffer.c',
+ 'util.c',
+ 'shaders.c',
+ 'pixel_format.c',
+ 'fx_pass.c',
+ 'fx_framebuffer.c',
+ 'fx_texture.c',
'fx_renderer.c',
)
@@ -18,7 +23,6 @@ if 'gles2' in renderers or 'auto' in renderers
if egl.found() and gbm.found() and glesv2.found()
wlr_deps += [egl, gbm, glesv2]
- internal_features += { 'egl': true , 'gles2-renderer': true }
endif
subdir('gles2')
endif
diff --git a/render/fx_renderer/pixel_format.c b/render/fx_renderer/pixel_format.c
new file mode 100644
index 0000000..2693018
--- /dev/null
+++ b/render/fx_renderer/pixel_format.c
@@ -0,0 +1,175 @@
+#include <drm_fourcc.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include "render/fx_renderer/fx_renderer.h"
+
+/*
+ * The DRM formats are little endian while the GL formats are big endian,
+ * so DRM_FORMAT_ARGB8888 is actually compatible with GL_BGRA_EXT.
+ */
+static const struct fx_pixel_format formats[] = {
+ {
+ .drm_format = DRM_FORMAT_ARGB8888,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_BYTE,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_XRGB8888,
+ .gl_format = GL_BGRA_EXT,
+ .gl_type = GL_UNSIGNED_BYTE,
+ .has_alpha = false,
+ },
+ {
+ .drm_format = DRM_FORMAT_XBGR8888,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_BYTE,
+ .has_alpha = false,
+ },
+ {
+ .drm_format = DRM_FORMAT_ABGR8888,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_BYTE,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_BGR888,
+ .gl_format = GL_RGB,
+ .gl_type = GL_UNSIGNED_BYTE,
+ .has_alpha = false,
+ },
+#if WLR_LITTLE_ENDIAN
+ {
+ .drm_format = DRM_FORMAT_RGBX4444,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_SHORT_4_4_4_4,
+ .has_alpha = false,
+ },
+ {
+ .drm_format = DRM_FORMAT_RGBA4444,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_SHORT_4_4_4_4,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_RGBX5551,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_SHORT_5_5_5_1,
+ .has_alpha = false,
+ },
+ {
+ .drm_format = DRM_FORMAT_RGBA5551,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_SHORT_5_5_5_1,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_RGB565,
+ .gl_format = GL_RGB,
+ .gl_type = GL_UNSIGNED_SHORT_5_6_5,
+ .has_alpha = false,
+ },
+ {
+ .drm_format = DRM_FORMAT_XBGR2101010,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT,
+ .has_alpha = false,
+ },
+ {
+ .drm_format = DRM_FORMAT_ABGR2101010,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_XBGR16161616F,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_HALF_FLOAT_OES,
+ .has_alpha = false,
+ },
+ {
+ .drm_format = DRM_FORMAT_ABGR16161616F,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_HALF_FLOAT_OES,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_XBGR16161616,
+ .gl_internalformat = GL_RGBA16_EXT,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_SHORT,
+ .has_alpha = false,
+ },
+ {
+ .drm_format = DRM_FORMAT_ABGR16161616,
+ .gl_internalformat = GL_RGBA16_EXT,
+ .gl_format = GL_RGBA,
+ .gl_type = GL_UNSIGNED_SHORT,
+ .has_alpha = true,
+ },
+#endif
+};
+
+// TODO: more pixel formats
+
+/*
+ * Return true if supported for texturing, even if other operations like
+ * reading aren't supported.
+ */
+bool is_fx_pixel_format_supported(const struct fx_renderer *renderer,
+ const struct fx_pixel_format *format) {
+ if (format->gl_type == GL_UNSIGNED_INT_2_10_10_10_REV_EXT
+ && !renderer->exts.EXT_texture_type_2_10_10_10_REV) {
+ return false;
+ }
+ if (format->gl_type == GL_HALF_FLOAT_OES
+ && !renderer->exts.OES_texture_half_float_linear) {
+ return false;
+ }
+ if (format->gl_type == GL_UNSIGNED_SHORT
+ && !renderer->exts.EXT_texture_norm16) {
+ return false;
+ }
+ /*
+ * Note that we don't need to check for GL_EXT_texture_format_BGRA8888
+ * here, since we've already checked if we have it at renderer creation
+ * time and bailed out if not. We do the check there because Wayland
+ * requires all compositors to support SHM buffers in that format.
+ */
+ return true;
+}
+
+const struct fx_pixel_format *get_fx_format_from_drm(uint32_t fmt) {
+ for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); ++i) {
+ if (formats[i].drm_format == fmt) {
+ return &formats[i];
+ }
+ }
+ return NULL;
+}
+
+const struct fx_pixel_format *get_fx_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 uint32_t *get_fx_shm_formats(const struct fx_renderer *renderer,
+ size_t *len) {
+ static uint32_t shm_formats[sizeof(formats) / sizeof(formats[0])];
+ size_t j = 0;
+ for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
+ if (!is_fx_pixel_format_supported(renderer, &formats[i])) {
+ continue;
+ }
+ shm_formats[j++] = formats[i].drm_format;
+ }
+ *len = j;
+ return shm_formats;
+}
diff --git a/render/fx_renderer/shaders.c b/render/fx_renderer/shaders.c
new file mode 100644
index 0000000..9257ca3
--- /dev/null
+++ b/render/fx_renderer/shaders.c
@@ -0,0 +1,203 @@
+#include <EGL/egl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wlr/util/log.h>
+
+#include "render/fx_renderer/fx_renderer.h"
+#include "render/fx_renderer/shaders.h"
+
+// shaders
+#include "common_vert_src.h"
+#include "quad_frag_src.h"
+#include "tex_frag_src.h"
+#include "stencil_mask_frag_src.h"
+#include "box_shadow_frag_src.h"
+
+GLuint compile_shader(GLuint type, const GLchar *src) {
+ GLuint shader = glCreateShader(type);
+ glShaderSource(shader, 1, &src, NULL);
+ glCompileShader(shader);
+
+ GLint ok;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
+ if (ok == GL_FALSE) {
+ wlr_log(WLR_ERROR, "Failed to compile shader");
+ glDeleteShader(shader);
+ shader = 0;
+ }
+
+ return shader;
+}
+
+GLuint link_program(const GLchar *frag_src) {
+ const GLchar *vert_src = common_vert_src;
+ 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) {
+ wlr_log(WLR_ERROR, "Failed to link shader");
+ glDeleteProgram(prog);
+ goto error;
+ }
+
+ return prog;
+
+error:
+ return 0;
+}
+
+
+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;
+}
+
+void load_gl_proc(void *proc_ptr, const char *name) {
+ void *proc = (void *)eglGetProcAddress(name);
+ if (proc == NULL) {
+ wlr_log(WLR_ERROR, "FX RENDERER: eglGetProcAddress(%s) failed", name);
+ abort();
+ }
+ *(void **)proc_ptr = proc;
+}
+
+// Shaders
+
+static bool link_quad_program(struct quad_shader *shader) {
+ GLuint prog;
+ shader->program = prog = link_program(quad_frag_src);
+ if (!shader->program) {
+ return false;
+ }
+
+ shader->proj = glGetUniformLocation(prog, "proj");
+ shader->color = glGetUniformLocation(prog, "color");
+ shader->pos_attrib = glGetAttribLocation(prog, "pos");
+
+ return true;
+}
+
+static bool link_tex_program(struct tex_shader *shader,
+ enum fx_tex_shader_source source) {
+ GLchar frag_src[2048];
+ snprintf(frag_src, sizeof(frag_src),
+ "#define SOURCE %d\n%s", source, tex_frag_src);
+
+ GLuint prog;
+ shader->program = prog = link_program(frag_src);
+ if (!shader->program) {
+ return false;
+ }
+
+ shader->proj = glGetUniformLocation(prog, "proj");
+ shader->tex = glGetUniformLocation(prog, "tex");
+ shader->alpha = glGetUniformLocation(prog, "alpha");
+ shader->pos_attrib = glGetAttribLocation(prog, "pos");
+ shader->tex_proj = glGetUniformLocation(prog, "tex_proj");
+ shader->size = glGetUniformLocation(prog, "size");
+ shader->position = glGetUniformLocation(prog, "position");
+ shader->radius = glGetUniformLocation(prog, "radius");
+
+ return true;
+}
+
+static bool link_stencil_mask_program(struct stencil_mask_shader *shader) {
+ GLuint prog;
+ shader->program = prog = link_program(stencil_mask_frag_src);
+ if (!shader->program) {
+ return false;
+ }
+
+ shader->proj = glGetUniformLocation(prog, "proj");
+ shader->color = glGetUniformLocation(prog, "color");
+ shader->pos_attrib = glGetAttribLocation(prog, "pos");
+ shader->position = glGetUniformLocation(prog, "position");
+ shader->half_size = glGetUniformLocation(prog, "half_size");
+ shader->radius = glGetUniformLocation(prog, "radius");
+
+ return true;
+}
+
+static bool link_box_shadow_program(struct box_shadow_shader *shader) {
+ GLuint prog;
+ shader->program = prog = link_program(box_shadow_frag_src);
+ if (!shader->program) {
+ return false;
+ }
+ shader->proj = glGetUniformLocation(prog, "proj");
+ shader->color = glGetUniformLocation(prog, "color");
+ shader->pos_attrib = glGetAttribLocation(prog, "pos");
+ shader->position = glGetUniformLocation(prog, "position");
+ shader->size = glGetUniformLocation(prog, "size");
+ shader->blur_sigma = glGetUniformLocation(prog, "blur_sigma");
+ shader->corner_radius = glGetUniformLocation(prog, "corner_radius");
+
+ return true;
+}
+
+bool link_shaders(struct fx_renderer *renderer) {
+ // quad fragment shader
+ if (!link_quad_program(&renderer->shaders.quad)) {
+ wlr_log(WLR_ERROR, "Could not link quad shader");
+ return false;
+ }
+ // fragment shaders
+ if (!link_tex_program(&renderer->shaders.tex_rgba, SHADER_SOURCE_TEXTURE_RGBA)) {
+ wlr_log(WLR_ERROR, "Could not link tex_RGBA shader");
+ return false;
+ }
+ if (!link_tex_program(&renderer->shaders.tex_rgbx, SHADER_SOURCE_TEXTURE_RGBX)) {
+ wlr_log(WLR_ERROR, "Could not link tex_RGBX shader");
+ return false;
+ }
+ if (!link_tex_program(&renderer->shaders.tex_ext, SHADER_SOURCE_TEXTURE_EXTERNAL)) {
+ wlr_log(WLR_ERROR, "Could not link tex_EXTERNAL shader");
+ return false;
+ }
+
+ // stencil mask shader
+ if (!link_stencil_mask_program(&renderer->shaders.stencil_mask)) {
+ wlr_log(WLR_ERROR, "Could not link stencil mask shader");
+ return false;
+ }
+ // box shadow shader
+ if (!link_box_shadow_program(&renderer->shaders.box_shadow)) {
+ wlr_log(WLR_ERROR, "Could not link box shadow shader");
+ return false;
+ }
+
+ return true;
+}
diff --git a/render/fx_renderer/util.c b/render/fx_renderer/util.c
new file mode 100644
index 0000000..c262aab
--- /dev/null
+++ b/render/fx_renderer/util.c
@@ -0,0 +1,113 @@
+#define _POSIX_C_SOURCE 200809L
+#include <fcntl.h>
+#include <unistd.h>
+#include <wlr/util/log.h>
+#include <wlr/types/wlr_buffer.h>
+#include <xf86drm.h>
+
+#include "render/fx_renderer/util.h"
+
+static uint32_t backend_get_buffer_caps(struct wlr_backend *backend) {
+ if (!backend->impl->get_buffer_caps) {
+ return 0;
+ }
+
+ return backend->impl->get_buffer_caps(backend);
+}
+
+static int open_drm_render_node(void) {
+ uint32_t flags = 0;
+ int devices_len = drmGetDevices2(flags, NULL, 0);
+ if (devices_len < 0) {
+ wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
+ return -1;
+ }
+ drmDevice **devices = calloc(devices_len, sizeof(*devices));
+ if (devices == NULL) {
+ wlr_log_errno(WLR_ERROR, "Allocation failed");
+ return -1;
+ }
+ devices_len = drmGetDevices2(flags, devices, devices_len);
+ if (devices_len < 0) {
+ free(devices);
+ wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
+ return -1;
+ }
+
+ int fd = -1;
+ for (int i = 0; i < devices_len; i++) {
+ drmDevice *dev = devices[i];
+ if (dev->available_nodes & (1 << DRM_NODE_RENDER)) {
+ const char *name = dev->nodes[DRM_NODE_RENDER];
+ wlr_log(WLR_DEBUG, "Opening DRM render node '%s'", name);
+ fd = open(name, O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ wlr_log_errno(WLR_ERROR, "Failed to open '%s'", name);
+ goto out;
+ }
+ break;
+ }
+ }
+ if (fd < 0) {
+ wlr_log(WLR_ERROR, "Failed to find any DRM render node");
+ }
+
+out:
+ for (int i = 0; i < devices_len; i++) {
+ drmFreeDevice(&devices[i]);
+ }
+ free(devices);
+
+ return fd;
+}
+
+bool open_preferred_drm_fd(struct wlr_backend *backend, int *drm_fd_ptr,
+ bool *own_drm_fd) {
+ if (*drm_fd_ptr >= 0) {
+ return true;
+ }
+
+ // Allow the user to override the render node
+ const char *render_name = getenv("WLR_RENDER_DRM_DEVICE");
+ if (render_name != NULL) {
+ wlr_log(WLR_INFO,
+ "Opening DRM render node '%s' from WLR_RENDER_DRM_DEVICE",
+ render_name);
+ int drm_fd = open(render_name, O_RDWR | O_CLOEXEC);
+ if (drm_fd < 0) {
+ wlr_log_errno(WLR_ERROR, "Failed to open '%s'", render_name);
+ return false;
+ }
+ if (drmGetNodeTypeFromFd(drm_fd) != DRM_NODE_RENDER) {
+ wlr_log(WLR_ERROR, "'%s' is not a DRM render node", render_name);
+ close(drm_fd);
+ return false;
+ }
+ *drm_fd_ptr = drm_fd;
+ *own_drm_fd = true;
+ return true;
+ }
+
+ // Prefer the backend's DRM node, if any
+ int backend_drm_fd = wlr_backend_get_drm_fd(backend);
+ if (backend_drm_fd >= 0) {
+ *drm_fd_ptr = backend_drm_fd;
+ *own_drm_fd = false;
+ return true;
+ }
+
+ // If the backend hasn't picked a DRM FD, but accepts DMA-BUFs, pick an
+ // arbitrary render node
+ uint32_t backend_caps = backend_get_buffer_caps(backend);
+ if (backend_caps & WLR_BUFFER_CAP_DMABUF) {
+ int drm_fd = open_drm_render_node();
+ if (drm_fd < 0) {
+ return false;
+ }
+ *drm_fd_ptr = drm_fd;
+ *own_drm_fd = true;
+ return true;
+ }
+
+ return false;
+}
diff --git a/render/meson.build b/render/meson.build
index 4dd5c6a..56579ee 100644
--- a/render/meson.build
+++ b/render/meson.build
@@ -1,5 +1,6 @@
wlr_files += files(
'pixel_format.c',
+ 'egl.c',
)
subdir('fx_renderer')
diff --git a/render/pixel_format.c b/render/pixel_format.c
index 46bcecf..b81f561 100644
--- a/render/pixel_format.c
+++ b/render/pixel_format.c
@@ -1,178 +1,252 @@
-#include "render/pixel_format.h"
+#include <assert.h>
#include <drm_fourcc.h>
+#include <wlr/util/log.h>
+#include "render/pixel_format.h"
static const struct wlr_pixel_format_info pixel_format_info[] = {
- {
- .drm_format = DRM_FORMAT_XRGB8888,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 32,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_ARGB8888,
- .opaque_substitute = DRM_FORMAT_XRGB8888,
- .bpp = 32,
- .has_alpha = true,
- },
- {
- .drm_format = DRM_FORMAT_XBGR8888,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 32,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_ABGR8888,
- .opaque_substitute = DRM_FORMAT_XBGR8888,
- .bpp = 32,
- .has_alpha = true,
- },
- {
- .drm_format = DRM_FORMAT_RGBX8888,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 32,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_RGBA8888,
- .opaque_substitute = DRM_FORMAT_RGBX8888,
- .bpp = 32,
- .has_alpha = true,
- },
- {
- .drm_format = DRM_FORMAT_BGRX8888,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 32,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_BGRA8888,
- .opaque_substitute = DRM_FORMAT_BGRX8888,
- .bpp = 32,
- .has_alpha = true,
- },
- {
- .drm_format = DRM_FORMAT_BGR888,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 24,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_RGBX4444,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 16,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_RGBA4444,
- .opaque_substitute = DRM_FORMAT_RGBX4444,
- .bpp = 16,
- .has_alpha = true,
- },
- {
- .drm_format = DRM_FORMAT_RGBX5551,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 16,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_RGBA5551,
- .opaque_substitute = DRM_FORMAT_RGBX5551,
- .bpp = 16,
- .has_alpha = true,
- },
- {
- .drm_format = DRM_FORMAT_RGB565,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 16,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_BGR565,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 16,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_XRGB2101010,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 32,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_ARGB2101010,
- .opaque_substitute = DRM_FORMAT_XRGB2101010,
- .bpp = 32,
- .has_alpha = true,
- },
- {
- .drm_format = DRM_FORMAT_XBGR2101010,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 32,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_ABGR2101010,
- .opaque_substitute = DRM_FORMAT_XBGR2101010,
- .bpp = 32,
- .has_alpha = true,
- },
- {
- .drm_format = DRM_FORMAT_XBGR16161616F,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 64,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_ABGR16161616F,
- .opaque_substitute = DRM_FORMAT_XBGR16161616F,
- .bpp = 64,
- .has_alpha = true,
- },
- {
- .drm_format = DRM_FORMAT_XBGR16161616,
- .opaque_substitute = DRM_FORMAT_INVALID,
- .bpp = 64,
- .has_alpha = false,
- },
- {
- .drm_format = DRM_FORMAT_ABGR16161616,
- .opaque_substitute = DRM_FORMAT_XBGR16161616,
- .bpp = 64,
- .has_alpha = true,
- },
+ {
+ .drm_format = DRM_FORMAT_XRGB8888,
+ .bytes_per_block = 4,
+ },
+ {
+ .drm_format = DRM_FORMAT_ARGB8888,
+ .opaque_substitute = DRM_FORMAT_XRGB8888,
+ .bytes_per_block = 4,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_XBGR8888,
+ .bytes_per_block = 4,
+ },
+ {
+ .drm_format = DRM_FORMAT_ABGR8888,
+ .opaque_substitute = DRM_FORMAT_XBGR8888,
+ .bytes_per_block = 4,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_RGBX8888,
+ .bytes_per_block = 4,
+ },
+ {
+ .drm_format = DRM_FORMAT_RGBA8888,
+ .opaque_substitute = DRM_FORMAT_RGBX8888,
+ .bytes_per_block = 4,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_BGRX8888,
+ .bytes_per_block = 4,
+ },
+ {
+ .drm_format = DRM_FORMAT_BGRA8888,
+ .opaque_substitute = DRM_FORMAT_BGRX8888,
+ .bytes_per_block = 4,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_R8,
+ .bytes_per_block = 1,
+ },
+ {
+ .drm_format = DRM_FORMAT_GR88,
+ .bytes_per_block = 2,
+ },
+ {
+ .drm_format = DRM_FORMAT_RGB888,
+ .bytes_per_block = 3,
+ },
+ {
+ .drm_format = DRM_FORMAT_BGR888,
+ .bytes_per_block = 3,
+ },
+ {
+ .drm_format = DRM_FORMAT_RGBX4444,
+ .bytes_per_block = 2,
+ },
+ {
+ .drm_format = DRM_FORMAT_RGBA4444,
+ .opaque_substitute = DRM_FORMAT_RGBX4444,
+ .bytes_per_block = 2,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_BGRX4444,
+ .bytes_per_block = 2,
+ },
+ {
+ .drm_format = DRM_FORMAT_BGRA4444,
+ .opaque_substitute = DRM_FORMAT_BGRX4444,
+ .bytes_per_block = 2,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_RGBX5551,
+ .bytes_per_block = 2,
+ },
+ {
+ .drm_format = DRM_FORMAT_RGBA5551,
+ .opaque_substitute = DRM_FORMAT_RGBX5551,
+ .bytes_per_block = 2,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_BGRX5551,
+ .bytes_per_block = 2,
+ },
+ {
+ .drm_format = DRM_FORMAT_BGRA5551,
+ .opaque_substitute = DRM_FORMAT_BGRX5551,
+ .bytes_per_block = 2,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_XRGB1555,
+ .bytes_per_block = 2,
+ },
+ {
+ .drm_format = DRM_FORMAT_ARGB1555,
+ .opaque_substitute = DRM_FORMAT_XRGB1555,
+ .bytes_per_block = 2,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_RGB565,
+ .bytes_per_block = 2,
+ },
+ {
+ .drm_format = DRM_FORMAT_BGR565,
+ .bytes_per_block = 2,
+ },
+ {
+ .drm_format = DRM_FORMAT_XRGB2101010,
+ .bytes_per_block = 4,
+ },
+ {
+ .drm_format = DRM_FORMAT_ARGB2101010,
+ .opaque_substitute = DRM_FORMAT_XRGB2101010,
+ .bytes_per_block = 4,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_XBGR2101010,
+ .bytes_per_block = 4,
+ },
+ {
+ .drm_format = DRM_FORMAT_ABGR2101010,
+ .opaque_substitute = DRM_FORMAT_XBGR2101010,
+ .bytes_per_block = 4,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_XBGR16161616F,
+ .bytes_per_block = 8,
+ },
+ {
+ .drm_format = DRM_FORMAT_ABGR16161616F,
+ .opaque_substitute = DRM_FORMAT_XBGR16161616F,
+ .bytes_per_block = 8,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_XBGR16161616,
+ .bytes_per_block = 8,
+ },
+ {
+ .drm_format = DRM_FORMAT_ABGR16161616,
+ .opaque_substitute = DRM_FORMAT_XBGR16161616,
+ .bytes_per_block = 8,
+ .has_alpha = true,
+ },
+ {
+ .drm_format = DRM_FORMAT_YVYU,
+ .bytes_per_block = 4,
+ .block_width = 2,
+ .block_height = 1,
+ },
+ {
+ .drm_format = DRM_FORMAT_VYUY,
+ .bytes_per_block = 4,
+ .block_width = 2,
+ .block_height = 1,
+ },
};
static const size_t pixel_format_info_size =
- sizeof(pixel_format_info) / sizeof(pixel_format_info[0]);
+ sizeof(pixel_format_info) / sizeof(pixel_format_info[0]);
const struct wlr_pixel_format_info *drm_get_pixel_format_info(uint32_t fmt) {
- for (size_t i = 0; i < pixel_format_info_size; ++i) {
- if (pixel_format_info[i].drm_format == fmt) {
- return &pixel_format_info[i];
- }
- }
+ for (size_t i = 0; i < pixel_format_info_size; ++i) {
+ if (pixel_format_info[i].drm_format == fmt) {
+ return &pixel_format_info[i];
+ }
+ }
- return NULL;
+ return NULL;
}
uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt) {
- switch (fmt) {
- case WL_SHM_FORMAT_XRGB8888:
- return DRM_FORMAT_XRGB8888;
- case WL_SHM_FORMAT_ARGB8888:
- return DRM_FORMAT_ARGB8888;
- default:
- return (uint32_t)fmt;
- }
+ switch (fmt) {
+ case WL_SHM_FORMAT_XRGB8888:
+ return DRM_FORMAT_XRGB8888;
+ case WL_SHM_FORMAT_ARGB8888:
+ return DRM_FORMAT_ARGB8888;
+ default:
+ return (uint32_t)fmt;
+ }
}
enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt) {
- switch (fmt) {
- case DRM_FORMAT_XRGB8888:
- return WL_SHM_FORMAT_XRGB8888;
- case DRM_FORMAT_ARGB8888:
- return WL_SHM_FORMAT_ARGB8888;
- default:
- return (enum wl_shm_format)fmt;
- }
+ switch (fmt) {
+ case DRM_FORMAT_XRGB8888:
+ return WL_SHM_FORMAT_XRGB8888;
+ case DRM_FORMAT_ARGB8888:
+ return WL_SHM_FORMAT_ARGB8888;
+ default:
+ return (enum wl_shm_format)fmt;
+ }
+}
+
+uint32_t pixel_format_info_pixels_per_block(const struct wlr_pixel_format_info *info) {
+ uint32_t pixels = info->block_width * info->block_height;
+ return pixels > 0 ? pixels : 1;
+}
+
+static int32_t div_round_up(int32_t dividend, int32_t divisor) {
+ int32_t quotient = dividend / divisor;
+ if (dividend % divisor != 0) {
+ quotient++;
+ }
+ return quotient;
+}
+
+int32_t pixel_format_info_min_stride(const struct wlr_pixel_format_info *fmt, int32_t width) {
+ int32_t pixels_per_block = (int32_t)pixel_format_info_pixels_per_block(fmt);
+ int32_t bytes_per_block = (int32_t)fmt->bytes_per_block;
+ if (width > INT32_MAX / bytes_per_block) {
+ wlr_log(WLR_DEBUG, "Invalid width %d (overflow)", width);
+ return 0;
+ }
+ return div_round_up(width * bytes_per_block, pixels_per_block);
+}
+
+bool pixel_format_info_check_stride(const struct wlr_pixel_format_info *fmt,
+ int32_t stride, int32_t width) {
+ int32_t bytes_per_block = (int32_t)fmt->bytes_per_block;
+ if (stride % bytes_per_block != 0) {
+ wlr_log(WLR_DEBUG, "Invalid stride %d (incompatible with %d "
+ "bytes-per-block)", stride, bytes_per_block);
+ return false;
+ }
+
+ int32_t min_stride = pixel_format_info_min_stride(fmt, width);
+ if (min_stride <= 0) {
+ return false;
+ } else if (stride < min_stride) {
+ wlr_log(WLR_DEBUG, "Invalid stride %d (too small for %d "
+ "bytes-per-block and width %d)", stride, bytes_per_block, width);
+ return false;
+ }
+
+ return true;
}
diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c
index 56091e3..03fafee 100644
--- a/tinywl/tinywl.c
+++ b/tinywl/tinywl.c
@@ -29,6 +29,8 @@
#include "types/wlr_scene.h"
+#include "render/fx_renderer/fx_renderer.h"
+
/* For brevity's sake, struct members are annotated where they are used. */
enum tinywl_cursor_mode {
TINYWL_CURSOR_PASSTHROUGH,
@@ -42,10 +44,11 @@ struct tinywl_server {
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_scene *scene;
+ struct wlr_scene_output_layout *scene_layout;
struct wlr_xdg_shell *xdg_shell;
struct wl_listener new_xdg_surface;
- struct wl_list views;
+ struct wl_list toplevels;
struct wlr_cursor *cursor;
struct wlr_xcursor_manager *cursor_mgr;
@@ -61,7 +64,7 @@ struct tinywl_server {
struct wl_listener request_set_selection;
struct wl_list keyboards;
enum tinywl_cursor_mode cursor_mode;
- struct tinywl_view *grabbed_view;
+ struct tinywl_toplevel *grabbed_toplevel;
double grab_x, grab_y;
struct wlr_box grab_geobox;
uint32_t resize_edges;
@@ -76,10 +79,11 @@ struct tinywl_output {
struct tinywl_server *server;
struct wlr_output *wlr_output;
struct wl_listener frame;
+ struct wl_listener request_state;
struct wl_listener destroy;
};
-struct tinywl_view {
+struct tinywl_toplevel {
struct wl_list link;
struct tinywl_server *server;
struct wlr_xdg_toplevel *xdg_toplevel;
@@ -91,9 +95,6 @@ struct tinywl_view {
struct wl_listener request_resize;
struct wl_listener request_maximize;
struct wl_listener request_fullscreen;
- int x, y;
-
- struct wlr_addon addon;
float opacity;
int corner_radius;
@@ -110,50 +111,12 @@ struct tinywl_keyboard {
struct wl_listener destroy;
};
-static void tinywl_view_destroy(struct tinywl_view *view) {
- wl_list_remove(&view->map.link);
- wl_list_remove(&view->unmap.link);
- wl_list_remove(&view->destroy.link);
- wl_list_remove(&view->request_move.link);
- wl_list_remove(&view->request_resize.link);
- wl_list_remove(&view->request_maximize.link);
- wl_list_remove(&view->request_fullscreen.link);
-
- free(view);
-}
-
-static void tinywl_view_addon_handle_destroy(struct wlr_addon *addon) {
- struct tinywl_view *view = wl_container_of(addon, view, addon);
-
- tinywl_view_destroy(view);
-}
-static const struct wlr_addon_interface tinywl_view_addon_impl = {
- .name = "tinywl_view",
- .destroy = tinywl_view_addon_handle_destroy,
-};
-
-static void tinywl_view_addon_assign(struct tinywl_view *view, struct wlr_addon_set *addons,
- const void * owner) {
- wlr_addon_init(&view->addon, addons, owner, &tinywl_view_addon_impl);
-}
-
-static struct tinywl_view *tinywl_view_addon_get(struct wlr_addon_set *addons,
- const void * owner) {
- struct wlr_addon *addon =
- wlr_addon_find(addons, owner, &tinywl_view_addon_impl);
- if (addon == NULL) {
- return NULL;
- }
- struct tinywl_view *view = wl_container_of(addon, view, addon);
- return view;
-}
-
-static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) {
+static void focus_toplevel(struct tinywl_toplevel *toplevel, struct wlr_surface *surface) {
/* Note: this function only deals with keyboard focus. */
- if (view == NULL) {
+ if (toplevel == NULL) {
return;
}
- struct tinywl_server *server = view->server;
+ struct tinywl_server *server = toplevel->server;
struct wlr_seat *seat = server->seat;
struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface;
if (prev_surface == surface) {
@@ -166,25 +129,26 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) {
* it no longer has focus and the client will repaint accordingly, e.g.
* stop displaying a caret.
*/
- struct wlr_xdg_surface *previous = wlr_xdg_surface_from_wlr_surface(
- seat->keyboard_state.focused_surface);
- assert(previous->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
- wlr_xdg_toplevel_set_activated(previous->toplevel, false);
+ struct wlr_xdg_toplevel *prev_toplevel =
+ wlr_xdg_toplevel_try_from_wlr_surface(prev_surface);
+ if (prev_toplevel != NULL) {
+ wlr_xdg_toplevel_set_activated(prev_toplevel, false);
+ }
}
struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat);
- /* Move the view to the front */
- wlr_scene_node_raise_to_top(&view->scene_tree->node);
- wl_list_remove(&view->link);
- wl_list_insert(&server->views, &view->link);
+ /* Move the toplevel to the front */
+ wlr_scene_node_raise_to_top(&toplevel->scene_tree->node);
+ wl_list_remove(&toplevel->link);
+ wl_list_insert(&server->toplevels, &toplevel->link);
/* Activate the new surface */
- wlr_xdg_toplevel_set_activated(view->xdg_toplevel, true);
+ wlr_xdg_toplevel_set_activated(toplevel->xdg_toplevel, true);
/*
* Tell the seat to have the keyboard enter this surface. wlroots will keep
* track of this and automatically send key events to the appropriate
* clients without additional work on your part.
*/
if (keyboard != NULL) {
- wlr_seat_keyboard_notify_enter(seat, view->xdg_toplevel->base->surface,
+ wlr_seat_keyboard_notify_enter(seat, toplevel->xdg_toplevel->base->surface,
keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
}
}
@@ -220,13 +184,13 @@ static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) {
wl_display_terminate(server->wl_display);
break;
case XKB_KEY_F1:
- /* Cycle to the next view */
- if (wl_list_length(&server->views) < 2) {
+ /* Cycle to the next toplevel */
+ if (wl_list_length(&server->toplevels) < 2) {
break;
}
- struct tinywl_view *next_view = wl_container_of(
- server->views.prev, next_view, link);
- focus_view(next_view, next_view->xdg_toplevel->base->surface);
+ struct tinywl_toplevel *next_toplevel =
+ wl_container_of(server->toplevels.prev, next_toplevel, link);
+ focus_toplevel(next_toplevel, next_toplevel->xdg_toplevel->base->surface);
break;
default:
return false;
@@ -287,8 +251,7 @@ static void server_new_keyboard(struct tinywl_server *server,
struct wlr_input_device *device) {
struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device);
- struct tinywl_keyboard *keyboard =
- calloc(1, sizeof(struct tinywl_keyboard));
+ struct tinywl_keyboard *keyboard = calloc(1, sizeof(*keyboard));
keyboard->server = server;
keyboard->wlr_keyboard = wlr_keyboard;
@@ -382,12 +345,12 @@ static void seat_request_set_selection(struct wl_listener *listener, void *data)
wlr_seat_set_selection(server->seat, event->source, event->serial);
}
-static struct tinywl_view *desktop_view_at(
+static struct tinywl_toplevel *desktop_toplevel_at(
struct tinywl_server *server, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy) {
/* This returns the topmost node in the scene at the given layout coords.
- * we only care about surface nodes as we are specifically looking for a
- * surface in the surface tree of a tinywl_view. */
+ * We only care about surface nodes as we are specifically looking for a
+ * surface in the surface tree of a tinywl_toplevel. */
struct wlr_scene_node *node = wlr_scene_node_at(
&server->scene->tree.node, lx, ly, sx, sy);
if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) {
@@ -395,13 +358,13 @@ static struct tinywl_view *desktop_view_at(
}
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
struct wlr_scene_surface *scene_surface =
- wlr_scene_surface_from_buffer(scene_buffer);
+ wlr_scene_surface_try_from_buffer(scene_buffer);
if (!scene_surface) {
return NULL;
}
*surface = scene_surface->surface;
- /* Find the node corresponding to the tinywl_view at the root of this
+ /* Find the node corresponding to the tinywl_toplevel at the root of this
* surface tree, it is the only one for which we set the data field. */
struct wlr_scene_tree *tree = node->parent;
while (tree != NULL && tree->node.data == NULL) {
@@ -413,29 +376,29 @@ static struct tinywl_view *desktop_view_at(
static void reset_cursor_mode(struct tinywl_server *server) {
/* Reset the cursor mode to passthrough. */
server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH;
- server->grabbed_view = NULL;
+ server->grabbed_toplevel = NULL;
}
static void process_cursor_move(struct tinywl_server *server, uint32_t time) {
- /* Move the grabbed view to the new position. */
- struct tinywl_view *view = server->grabbed_view;
- view->x = server->cursor->x - server->grab_x;
- view->y = server->cursor->y - server->grab_y;
- wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y);
+ /* Move the grabbed toplevel to the new position. */
+ struct tinywl_toplevel *toplevel = server->grabbed_toplevel;
+ wlr_scene_node_set_position(&toplevel->scene_tree->node,
+ server->cursor->x - server->grab_x,
+ server->cursor->y - server->grab_y);
}
static void process_cursor_resize(struct tinywl_server *server, uint32_t time) {
/*
- * Resizing the grabbed view can be a little bit complicated, because we
- * could be resizing from any corner or edge. This not only resizes the view
- * on one or two axes, but can also move the view if you resize from the top
- * or left edges (or top-left corner).
+ * Resizing the grabbed toplevel can be a little bit complicated, because we
+ * could be resizing from any corner or edge. This not only resizes the
+ * toplevel on one or two axes, but can also move the toplevel if you resize
+ * from the top or left edges (or top-left corner).
*
- * Note that I took some shortcuts here. In a more fleshed-out compositor,
- * you'd wait for the client to prepare a buffer at the new size, then
- * commit any movement that was prepared.
+ * Note that some shortcuts are taken here. In a more fleshed-out
+ * compositor, you'd wait for the client to prepare a buffer at the new
+ * size, then commit any movement that was prepared.
*/
- struct tinywl_view *view = server->grabbed_view;
+ struct tinywl_toplevel *toplevel = server->grabbed_toplevel;
double border_x = server->cursor->x - server->grab_x;
double border_y = server->cursor->y - server->grab_y;
int new_left = server->grab_geobox.x;
@@ -467,14 +430,13 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) {
}
struct wlr_box geo_box;
- wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box);
- view->x = new_left - geo_box.x;
- view->y = new_top - geo_box.y;
- wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y);
+ wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box);
+ wlr_scene_node_set_position(&toplevel->scene_tree->node,
+ new_left - geo_box.x, new_top - geo_box.y);
int new_width = new_right - new_left;
int new_height = new_bottom - new_top;
- wlr_xdg_toplevel_set_size(view->xdg_toplevel, new_width, new_height);
+ wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, new_width, new_height);
}
static void process_cursor_motion(struct tinywl_server *server, uint32_t time) {
@@ -487,18 +449,17 @@ static void process_cursor_motion(struct tinywl_server *server, uint32_t time) {
return;
}
- /* Otherwise, find the view under the pointer and send the event along. */
+ /* Otherwise, find the toplevel under the pointer and send the event along. */
double sx, sy;
struct wlr_seat *seat = server->seat;
struct wlr_surface *surface = NULL;
- struct tinywl_view *view = desktop_view_at(server,
+ struct tinywl_toplevel *toplevel = desktop_toplevel_at(server,
server->cursor->x, server->cursor->y, &surface, &sx, &sy);
- if (!view) {
- /* If there's no view under the cursor, set the cursor image to a
+ if (!toplevel) {
+ /* If there's no toplevel under the cursor, set the cursor image to a
* default. This is what makes the cursor image appear when you move it
- * around the screen, not over any views. */
- wlr_xcursor_manager_set_cursor_image(
- server->cursor_mgr, "left_ptr", server->cursor);
+ * around the screen, not over any toplevels. */
+ wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default");
}
if (surface) {
/*
@@ -564,14 +525,14 @@ static void server_cursor_button(struct wl_listener *listener, void *data) {
event->time_msec, event->button, event->state);
double sx, sy;
struct wlr_surface *surface = NULL;
- struct tinywl_view *view = desktop_view_at(server,
+ struct tinywl_toplevel *toplevel = desktop_toplevel_at(server,
server->cursor->x, server->cursor->y, &surface, &sx, &sy);
if (event->state == WLR_BUTTON_RELEASED) {
/* If you released any buttons, we exit interactive move/resize mode. */
reset_cursor_mode(server);
} else {
/* Focus that client if the button was _pressed_ */
- focus_view(view, surface);
+ focus_toplevel(toplevel, surface);
}
}
@@ -599,47 +560,45 @@ static void server_cursor_frame(struct wl_listener *listener, void *data) {
}
static void output_configure_scene(struct wlr_scene_node *node,
- struct tinywl_view *view) {
+ struct tinywl_toplevel *toplevel) {
if (!node->enabled) {
return;
}
- struct tinywl_view *_view = tinywl_view_addon_get(&node->addons, node);
- if (_view) {
- view = _view;
+ struct tinywl_toplevel *_toplevel = node->data;
+ if (_toplevel) {
+ toplevel = _toplevel;
}
if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
- struct wlr_scene_surface * scene_surface
- = wlr_scene_surface_from_buffer(buffer);
+ struct wlr_scene_surface * scene_surface =
+ wlr_scene_surface_try_from_buffer(buffer);
if (!scene_surface) {
return;
}
- struct wlr_xdg_surface *xdg_surface = NULL;
- if (wlr_surface_is_xdg_surface(scene_surface->surface)) {
- xdg_surface = wlr_xdg_surface_from_wlr_surface(scene_surface->surface);
- }
+ struct wlr_xdg_surface *xdg_surface =
+ wlr_xdg_surface_try_from_wlr_surface(scene_surface->surface);
- if (view &&
+ if (toplevel &&
xdg_surface &&
xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
// TODO: Be able to set whole decoration_data instead of calling
// each individually?
- wlr_scene_buffer_set_opacity(buffer, view->opacity);
+ wlr_scene_buffer_set_opacity(buffer, toplevel->opacity);
- if (!wlr_surface_is_subsurface(xdg_surface->surface)) {
- wlr_scene_buffer_set_corner_radius(buffer, view->corner_radius);
- wlr_scene_buffer_set_shadow_data(buffer, view->shadow_data);
+ if (!wlr_subsurface_try_from_wlr_surface(xdg_surface->surface)) {
+ wlr_scene_buffer_set_corner_radius(buffer, toplevel->corner_radius);
+ wlr_scene_buffer_set_shadow_data(buffer, toplevel->shadow_data);
}
}
} else if (node->type == WLR_SCENE_NODE_TREE) {
struct wlr_scene_tree *tree = wl_container_of(node, tree, node);
struct wlr_scene_node *node;
wl_list_for_each(node, &tree->children, link) {
- output_configure_scene(node, view);
+ output_configure_scene(node, toplevel);
}
}
}
@@ -656,17 +615,27 @@ static void output_frame(struct wl_listener *listener, void *data) {
output_configure_scene(&scene_output->scene->tree.node, NULL);
/* Render the scene if needed and commit the output */
- wlr_scene_output_commit(scene_output);
+ wlr_scene_output_commit(scene_output, NULL);
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
wlr_scene_output_send_frame_done(scene_output, &now);
}
+static void output_request_state(struct wl_listener *listener, void *data) {
+ /* This function is called when the backend requests a new state for
+ * the output. For example, Wayland and X11 backends request a new mode
+ * when the output window is resized. */
+ struct tinywl_output *output = wl_container_of(listener, output, request_state);
+ const struct wlr_output_event_request_state *event = data;
+ wlr_output_commit_state(output->wlr_output, event->state);
+}
+
static void output_destroy(struct wl_listener *listener, void *data) {
struct tinywl_output *output = wl_container_of(listener, output, destroy);
wl_list_remove(&output->frame.link);
+ wl_list_remove(&output->request_state.link);
wl_list_remove(&output->destroy.link);
wl_list_remove(&output->link);
free(output);
@@ -683,30 +652,39 @@ static void server_new_output(struct wl_listener *listener, void *data) {
* and our renderer. Must be done once, before commiting the output */
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
+ /* The output may be disabled, switch it on. */
+ struct wlr_output_state state;
+ wlr_output_state_init(&state);
+ wlr_output_state_set_enabled(&state, true);
+
/* Some backends don't have modes. DRM+KMS does, and we need to set a mode
* before we can use the output. The mode is a tuple of (width, height,
* refresh rate), and each monitor supports only a specific set of modes. We
* just pick the monitor's preferred mode, a more sophisticated compositor
* would let the user configure it. */
- if (!wl_list_empty(&wlr_output->modes)) {
- struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
- wlr_output_set_mode(wlr_output, mode);
- wlr_output_enable(wlr_output, true);
- if (!wlr_output_commit(wlr_output)) {
- return;
- }
+ struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
+ if (mode != NULL) {
+ wlr_output_state_set_mode(&state, mode);
}
+ /* Atomically applies the new output state. */
+ wlr_output_commit_state(wlr_output, &state);
+ wlr_output_state_finish(&state);
+
/* Allocates and configures our state for this output */
- struct tinywl_output *output =
- calloc(1, sizeof(struct tinywl_output));
+ struct tinywl_output *output = calloc(1, sizeof(*output));
output->wlr_output = wlr_output;
output->server = server;
- /* Sets up a listener for the frame notify event. */
+
+ /* Sets up a listener for the frame event. */
output->frame.notify = output_frame;
wl_signal_add(&wlr_output->events.frame, &output->frame);
- /* Sets up a listener for the destroy notify event. */
+ /* Sets up a listener for the state request event. */
+ output->request_state.notify = output_request_state;
+ wl_signal_add(&wlr_output->events.request_state, &output->request_state);
+
+ /* Sets up a listener for the destroy event. */
output->destroy.notify = output_destroy;
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
@@ -721,70 +699,81 @@ static void server_new_output(struct wl_listener *listener, void *data) {
* display, which Wayland clients can see to find out information about the
* output (such as DPI, scale factor, manufacturer, etc).
*/
- wlr_output_layout_add_auto(server->output_layout, wlr_output);
+ struct wlr_output_layout_output *l_output = wlr_output_layout_add_auto(server->output_layout,
+ wlr_output);
+ struct wlr_scene_output *scene_output = wlr_scene_output_create(server->scene, wlr_output);
+ wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output);
}
static void xdg_toplevel_map(struct wl_listener *listener, void *data) {
/* Called when the surface is mapped, or ready to display on-screen. */
- struct tinywl_view *view = wl_container_of(listener, view, map);
+ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, map);
- wl_list_insert(&view->server->views, &view->link);
+ wl_list_insert(&toplevel->server->toplevels, &toplevel->link);
- focus_view(view, view->xdg_toplevel->base->surface);
+ focus_toplevel(toplevel, toplevel->xdg_toplevel->base->surface);
}
static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) {
/* Called when the surface is unmapped, and should no longer be shown. */
- struct tinywl_view *view = wl_container_of(listener, view, unmap);
+ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, unmap);
- /* Reset the cursor mode if the grabbed view was unmapped. */
- if (view == view->server->grabbed_view) {
- reset_cursor_mode(view->server);
+ /* Reset the cursor mode if the grabbed toplevel was unmapped. */
+ if (toplevel == toplevel->server->grabbed_toplevel) {
+ reset_cursor_mode(toplevel->server);
}
- wl_list_remove(&view->link);
+ wl_list_remove(&toplevel->link);
}
static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) {
- /* Called when the surface is destroyed and should never be shown again. */
- struct tinywl_view *view = wl_container_of(listener, view, destroy);
-
- tinywl_view_destroy(view);
+ /* Called when the xdg_toplevel is destroyed. */
+ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, destroy);
+
+ wl_list_remove(&toplevel->map.link);
+ wl_list_remove(&toplevel->unmap.link);
+ wl_list_remove(&toplevel->destroy.link);
+ wl_list_remove(&toplevel->request_move.link);
+ wl_list_remove(&toplevel->request_resize.link);
+ wl_list_remove(&toplevel->request_maximize.link);
+ wl_list_remove(&toplevel->request_fullscreen.link);
+
+ free(toplevel);
}
-static void begin_interactive(struct tinywl_view *view,
+static void begin_interactive(struct tinywl_toplevel *toplevel,
enum tinywl_cursor_mode mode, uint32_t edges) {
/* This function sets up an interactive move or resize operation, where the
* compositor stops propegating pointer events to clients and instead
* consumes them itself, to move or resize windows. */
- struct tinywl_server *server = view->server;
+ struct tinywl_server *server = toplevel->server;
struct wlr_surface *focused_surface =
server->seat->pointer_state.focused_surface;
- if (view->xdg_toplevel->base->surface !=
+ if (toplevel->xdg_toplevel->base->surface !=
wlr_surface_get_root_surface(focused_surface)) {
/* Deny move/resize requests from unfocused clients. */
return;
}
- server->grabbed_view = view;
+ server->grabbed_toplevel = toplevel;
server->cursor_mode = mode;
if (mode == TINYWL_CURSOR_MOVE) {
- server->grab_x = server->cursor->x - view->x;
- server->grab_y = server->cursor->y - view->y;
+ server->grab_x = server->cursor->x - toplevel->scene_tree->node.x;
+ server->grab_y = server->cursor->y - toplevel->scene_tree->node.y;
} else {
struct wlr_box geo_box;
- wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box);
+ wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box);
- double border_x = (view->x + geo_box.x) +
+ double border_x = (toplevel->scene_tree->node.x + geo_box.x) +
((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0);
- double border_y = (view->y + geo_box.y) +
+ double border_y = (toplevel->scene_tree->node.y + geo_box.y) +
((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0);
server->grab_x = server->cursor->x - border_x;
server->grab_y = server->cursor->y - border_y;
server->grab_geobox = geo_box;
- server->grab_geobox.x += view->x;
- server->grab_geobox.y += view->y;
+ server->grab_geobox.x += toplevel->scene_tree->node.x;
+ server->grab_geobox.y += toplevel->scene_tree->node.y;
server->resize_edges = edges;
}
@@ -797,8 +786,8 @@ static void xdg_toplevel_request_move(
* decorations. Note that a more sophisticated compositor should check the
* provided serial against a list of button press serials sent to this
* client, to prevent the client from requesting this whenever they want. */
- struct tinywl_view *view = wl_container_of(listener, view, request_move);
- begin_interactive(view, TINYWL_CURSOR_MOVE, 0);
+ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_move);
+ begin_interactive(toplevel, TINYWL_CURSOR_MOVE, 0);
}
static void xdg_toplevel_request_resize(
@@ -809,8 +798,8 @@ static void xdg_toplevel_request_resize(
* provided serial against a list of button press serials sent to this
* client, to prevent the client from requesting this whenever they want. */
struct wlr_xdg_toplevel_resize_event *event = data;
- struct tinywl_view *view = wl_container_of(listener, view, request_resize);
- begin_interactive(view, TINYWL_CURSOR_RESIZE, event->edges);
+ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_resize);
+ begin_interactive(toplevel, TINYWL_CURSOR_RESIZE, event->edges);
}
static void xdg_toplevel_request_maximize(
@@ -820,17 +809,17 @@ static void xdg_toplevel_request_maximize(
* client-side decorations. tinywl doesn't support maximization, but
* to conform to xdg-shell protocol we still must send a configure.
* wlr_xdg_surface_schedule_configure() is used to send an empty reply. */
- struct tinywl_view *view =
- wl_container_of(listener, view, request_maximize);
- wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base);
+ struct tinywl_toplevel *toplevel =
+ wl_container_of(listener, toplevel, request_maximize);
+ wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base);
}
static void xdg_toplevel_request_fullscreen(
struct wl_listener *listener, void *data) {
/* Just as with request_maximize, we must send a configure here. */
- struct tinywl_view *view =
- wl_container_of(listener, view, request_fullscreen);
- wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base);
+ struct tinywl_toplevel *toplevel =
+ wl_container_of(listener, toplevel, request_fullscreen);
+ wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base);
}
static void server_new_xdg_surface(struct wl_listener *listener, void *data) {
@@ -846,8 +835,9 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) {
* we always set the user data field of xdg_surfaces to the corresponding
* scene node. */
if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
- struct wlr_xdg_surface *parent = wlr_xdg_surface_from_wlr_surface(
- xdg_surface->popup->parent);
+ struct wlr_xdg_surface *parent =
+ wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent);
+ assert(parent != NULL);
struct wlr_scene_tree *parent_tree = parent->data;
xdg_surface->data = wlr_scene_xdg_surface_create(
parent_tree, xdg_surface);
@@ -855,46 +845,42 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) {
}
assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
- /* Allocate a tinywl_view for this surface */
- struct tinywl_view *view =
- calloc(1, sizeof(struct tinywl_view));
- view->server = server;
- view->xdg_toplevel = xdg_surface->toplevel;
- view->scene_tree = wlr_scene_xdg_surface_create(
- &view->server->scene->tree, view->xdg_toplevel->base);
- view->scene_tree->node.data = view;
- xdg_surface->data = view->scene_tree;
-
- /* Add the view to the trees addon */
- tinywl_view_addon_assign(view, &view->scene_tree->node.addons, &view->scene_tree->node);
+ /* Allocate a tinywl_toplevel for this surface */
+ struct tinywl_toplevel *toplevel = calloc(1, sizeof(*toplevel));
+ toplevel->server = server;
+ toplevel->xdg_toplevel = xdg_surface->toplevel;
+ toplevel->scene_tree = wlr_scene_xdg_surface_create(
+ &toplevel->server->scene->tree, toplevel->xdg_toplevel->base);
+ toplevel->scene_tree->node.data = toplevel;
+ xdg_surface->data = toplevel->scene_tree;
/* Set the scene_nodes decoration data */
- view->opacity = 1;
- view->corner_radius = 20;
- view->shadow_data = shadow_data_get_default();
- view->shadow_data.enabled = true;
- memcpy(view->shadow_data.color, (float[]) {1.0f, 0.0f, 0.0f, 1.0f}, sizeof(float[4]));
+ toplevel->opacity = 1;
+ toplevel->corner_radius = 20;
+ toplevel->shadow_data = shadow_data_get_default();
+ toplevel->shadow_data.enabled = true;
+ toplevel->shadow_data.color = (struct wlr_render_color) {1.0f, 0.0f, 0.0f, 1.0f};
/* Listen to the various events it can emit */
- view->map.notify = xdg_toplevel_map;
- wl_signal_add(&xdg_surface->events.map, &view->map);
- view->unmap.notify = xdg_toplevel_unmap;
- wl_signal_add(&xdg_surface->events.unmap, &view->unmap);
- view->destroy.notify = xdg_toplevel_destroy;
- wl_signal_add(&xdg_surface->events.destroy, &view->destroy);
+ toplevel->map.notify = xdg_toplevel_map;
+ wl_signal_add(&xdg_surface->surface->events.map, &toplevel->map);
+ toplevel->unmap.notify = xdg_toplevel_unmap;
+ wl_signal_add(&xdg_surface->surface->events.unmap, &toplevel->unmap);
+ toplevel->destroy.notify = xdg_toplevel_destroy;
+ wl_signal_add(&xdg_surface->events.destroy, &toplevel->destroy);
/* cotd */
- struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel;
- view->request_move.notify = xdg_toplevel_request_move;
- wl_signal_add(&toplevel->events.request_move, &view->request_move);
- view->request_resize.notify = xdg_toplevel_request_resize;
- wl_signal_add(&toplevel->events.request_resize, &view->request_resize);
- view->request_maximize.notify = xdg_toplevel_request_maximize;
- wl_signal_add(&toplevel->events.request_maximize,
- &view->request_maximize);
- view->request_fullscreen.notify = xdg_toplevel_request_fullscreen;
- wl_signal_add(&toplevel->events.request_fullscreen,
- &view->request_fullscreen);
+ struct wlr_xdg_toplevel *xdg_toplevel = xdg_surface->toplevel;
+ toplevel->request_move.notify = xdg_toplevel_request_move;
+ wl_signal_add(&xdg_toplevel->events.request_move, &toplevel->request_move);
+ toplevel->request_resize.notify = xdg_toplevel_request_resize;
+ wl_signal_add(&xdg_toplevel->events.request_resize, &toplevel->request_resize);
+ toplevel->request_maximize.notify = xdg_toplevel_request_maximize;
+ wl_signal_add(&xdg_toplevel->events.request_maximize,
+ &toplevel->request_maximize);
+ toplevel->request_fullscreen.notify = xdg_toplevel_request_fullscreen;
+ wl_signal_add(&xdg_toplevel->events.request_fullscreen,
+ &toplevel->request_fullscreen);
}
int main(int argc, char *argv[]) {
@@ -917,7 +903,7 @@ int main(int argc, char *argv[]) {
return 0;
}
- struct tinywl_server server;
+ struct tinywl_server server = {0};
/* The Wayland display is managed by libwayland. It handles accepting
* clients from the Unix socket, manging Wayland globals, and so on. */
server.wl_display = wl_display_create();
@@ -925,7 +911,7 @@ int main(int argc, char *argv[]) {
* output hardware. The autocreate option will choose the most suitable
* backend based on the current environment, such as opening an X11 window
* if an X11 server is running. */
- server.backend = wlr_backend_autocreate(server.wl_display);
+ server.backend = wlr_backend_autocreate(server.wl_display, NULL);
if (server.backend == NULL) {
wlr_log(WLR_ERROR, "failed to create wlr_backend");
return 1;
@@ -935,7 +921,7 @@ int main(int argc, char *argv[]) {
* can also specify a renderer using the WLR_RENDERER env var.
* The renderer is responsible for defining the various pixel formats it
* supports for shared memory, this configures that for clients. */
- server.renderer = wlr_renderer_autocreate(server.backend);
+ server.renderer = fx_renderer_create(server.backend);
if (server.renderer == NULL) {
wlr_log(WLR_ERROR, "failed to create wlr_renderer");
return 1;
@@ -947,7 +933,7 @@ int main(int argc, char *argv[]) {
* The allocator is the bridge between the renderer and the backend. It
* handles the buffer creation, allowing wlroots to render onto the
* screen */
- server.allocator = wlr_allocator_autocreate(server.backend,
+ server.allocator = wlr_allocator_autocreate(server.backend,
server.renderer);
if (server.allocator == NULL) {
wlr_log(WLR_ERROR, "failed to create wlr_allocator");
@@ -961,7 +947,7 @@ int main(int argc, char *argv[]) {
* to dig your fingers in and play with their behavior if you want. Note that
* the clients cannot set the selection directly without compositor approval,
* see the handling of the request_set_selection event below.*/
- wlr_compositor_create(server.wl_display, server.renderer);
+ wlr_compositor_create(server.wl_display, 5, server.renderer);
wlr_subcompositor_create(server.wl_display);
wlr_data_device_manager_create(server.wl_display);
@@ -982,15 +968,13 @@ int main(int argc, char *argv[]) {
* necessary.
*/
server.scene = wlr_scene_create();
- wlr_scene_attach_output_layout(server.scene, server.output_layout);
+ server.scene_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout);
/* Set up xdg-shell version 3. The xdg-shell is a Wayland protocol which is
- * used for application windows. For more detail on shells, refer to my
- * article:
- *
- * https://drewdevault.com/2018/07/29/Wayland-shells.html
+ * used for application windows. For more detail on shells, refer to
+ * https://drewdevault.com/2018/07/29/Wayland-shells.html.
*/
- wl_list_init(&server.views);
+ wl_list_init(&server.toplevels);
server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 3);
server.new_xdg_surface.notify = server_new_xdg_surface;
wl_signal_add(&server.xdg_shell->events.new_surface,
@@ -1006,19 +990,16 @@ int main(int argc, char *argv[]) {
/* Creates an xcursor manager, another wlroots utility which loads up
* Xcursor themes to source cursor images from and makes sure that cursor
* images are available at all scale factors on the screen (necessary for
- * HiDPI support). We add a cursor theme at scale factor 1 to begin with. */
+ * HiDPI support). */
server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24);
- wlr_xcursor_manager_load(server.cursor_mgr, 1);
/*
* wlr_cursor *only* displays an image on screen. It does not move around
* when the pointer moves. However, we can attach input devices to it, and
* it will generate aggregate events for all of them. In these events, we
* can choose how we want to process them, forwarding them to clients and
- * moving the cursor around. More detail on this process is described in my
- * input handling blog post:
- *
- * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
+ * moving the cursor around. More detail on this process is described in
+ * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html.
*
* And more comments are sprinkled throughout the notify functions above.
*/
@@ -1083,8 +1064,12 @@ int main(int argc, char *argv[]) {
socket);
wl_display_run(server.wl_display);
- /* Once wl_display_run returns, we shut down the server. */
+ /* Once wl_display_run returns, we destroy all clients then shut down the
+ * server. */
wl_display_destroy_clients(server.wl_display);
+ wlr_scene_node_destroy(&server.scene->tree.node);
+ wlr_xcursor_manager_destroy(server.cursor_mgr);
+ wlr_output_layout_destroy(server.output_layout);
wl_display_destroy(server.wl_display);
return 0;
}
diff --git a/types/buffer/buffer.c b/types/buffer/buffer.c
index d4c47bc..5303927 100644
--- a/types/buffer/buffer.c
+++ b/types/buffer/buffer.c
@@ -2,28 +2,27 @@
#include "types/wlr_buffer.h"
bool buffer_is_opaque(struct wlr_buffer *buffer) {
- void *data;
- uint32_t format;
- size_t stride;
- struct wlr_dmabuf_attributes dmabuf;
- struct wlr_shm_attributes shm;
- if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) {
- format = dmabuf.format;
- } else if (wlr_buffer_get_shm(buffer, &shm)) {
- format = shm.format;
- } else if (wlr_buffer_begin_data_ptr_access(buffer,
- WLR_BUFFER_DATA_PTR_ACCESS_READ,
- &data, &format, &stride)) {
- wlr_buffer_end_data_ptr_access(buffer);
- } else {
- return false;
- }
+ void *data;
+ uint32_t format;
+ size_t stride;
+ struct wlr_dmabuf_attributes dmabuf;
+ struct wlr_shm_attributes shm;
+ if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) {
+ format = dmabuf.format;
+ } else if (wlr_buffer_get_shm(buffer, &shm)) {
+ format = shm.format;
+ } else if (wlr_buffer_begin_data_ptr_access(buffer,
+ WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) {
+ wlr_buffer_end_data_ptr_access(buffer);
+ } else {
+ return false;
+ }
- const struct wlr_pixel_format_info *format_info =
- drm_get_pixel_format_info(format);
- if (format_info == NULL) {
- return false;
- }
+ const struct wlr_pixel_format_info *format_info =
+ drm_get_pixel_format_info(format);
+ if (format_info == NULL) {
+ return false;
+ }
- return !format_info->has_alpha;
+ return !format_info->has_alpha;
}
diff --git a/types/fx/shadow_data.c b/types/fx/shadow_data.c
index 0c2d1d2..fae76cf 100644
--- a/types/fx/shadow_data.c
+++ b/types/fx/shadow_data.c
@@ -5,14 +5,13 @@
#include "wlr/util/log.h"
struct shadow_data shadow_data_get_default(void) {
- static float default_shadow_color[] = {0.0f, 0.0f, 0.0f, 0.5f};
return (struct shadow_data) {
.blur_sigma = 20,
- .color = default_shadow_color,
+ .color = {0.0f, 0.0f, 0.0f, 0.5f},
.enabled = false,
};
}
bool scene_buffer_has_shadow(struct shadow_data *data) {
- return data->enabled && data->blur_sigma > 0 && data->color[3] > 0.0;
+ return data->enabled && data->blur_sigma > 0 && data->color.a > 0.0;
}
diff --git a/types/meson.build b/types/meson.build
index b5857d0..0527b7c 100644
--- a/types/meson.build
+++ b/types/meson.build
@@ -1,10 +1,6 @@
wlr_files += files(
- 'scene/subsurface_tree.c',
- 'scene/surface.c',
+ 'output/wlr_output.c',
'scene/wlr_scene.c',
- 'scene/output_layout.c',
- 'scene/xdg_shell.c',
- 'scene/layer_shell_v1.c',
'buffer/buffer.c',
)
diff --git a/types/output/wlr_output.c b/types/output/wlr_output.c
new file mode 100644
index 0000000..43325cf
--- /dev/null
+++ b/types/output/wlr_output.c
@@ -0,0 +1,24 @@
+#define _POSIX_C_SOURCE 200809L
+#include <stdlib.h>
+
+#include "types/wlr_output.h"
+
+void output_pending_resolution(struct wlr_output *output,
+ const struct wlr_output_state *state, int *width, int *height) {
+ if (state->committed & WLR_OUTPUT_STATE_MODE) {
+ switch (state->mode_type) {
+ case WLR_OUTPUT_STATE_MODE_FIXED:
+ *width = state->mode->width;
+ *height = state->mode->height;
+ return;
+ case WLR_OUTPUT_STATE_MODE_CUSTOM:
+ *width = state->custom_mode.width;
+ *height = state->custom_mode.height;
+ return;
+ }
+ abort();
+ } else {
+ *width = output->width;
+ *height = output->height;
+ }
+}
diff --git a/types/scene/layer_shell_v1.c b/types/scene/layer_shell_v1.c
deleted file mode 100644
index 3ed616a..0000000
--- a/types/scene/layer_shell_v1.c
+++ /dev/null
@@ -1,186 +0,0 @@
-#include <stdlib.h>
-#include <wlr/types/wlr_scene.h>
-#include <wlr/types/wlr_layer_shell_v1.h>
-
-static void scene_layer_surface_handle_tree_destroy(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_layer_surface_v1 *scene_layer_surface =
- wl_container_of(listener, scene_layer_surface, tree_destroy);
- // tree and surface_node will be cleaned up by scene_node_finish
- wl_list_remove(&scene_layer_surface->tree_destroy.link);
- wl_list_remove(&scene_layer_surface->layer_surface_destroy.link);
- wl_list_remove(&scene_layer_surface->layer_surface_map.link);
- wl_list_remove(&scene_layer_surface->layer_surface_unmap.link);
- free(scene_layer_surface);
-}
-
-static void scene_layer_surface_handle_layer_surface_destroy(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_layer_surface_v1 *scene_layer_surface =
- wl_container_of(listener, scene_layer_surface, layer_surface_destroy);
- wlr_scene_node_destroy(&scene_layer_surface->tree->node);
-}
-
-static void scene_layer_surface_handle_layer_surface_map(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_layer_surface_v1 *scene_layer_surface =
- wl_container_of(listener, scene_layer_surface, layer_surface_map);
- wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, true);
-}
-
-static void scene_layer_surface_handle_layer_surface_unmap(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_layer_surface_v1 *scene_layer_surface =
- wl_container_of(listener, scene_layer_surface, layer_surface_unmap);
- wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, false);
-}
-
-static void layer_surface_exclusive_zone(
- struct wlr_layer_surface_v1_state *state,
- struct wlr_box *usable_area) {
- switch (state->anchor) {
- case ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP:
- case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT):
- // Anchor top
- usable_area->y += state->exclusive_zone + state->margin.top;
- usable_area->height -= state->exclusive_zone + state->margin.top;
- break;
- case ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM:
- case (ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT):
- // Anchor bottom
- usable_area->height -= state->exclusive_zone + state->margin.bottom;
- break;
- case ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT:
- case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT):
- // Anchor left
- usable_area->x += state->exclusive_zone + state->margin.left;
- usable_area->width -= state->exclusive_zone + state->margin.left;
- break;
- case ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT:
- case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
- ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT):
- // Anchor right
- usable_area->width -= state->exclusive_zone + state->margin.right;
- break;
- }
-}
-
-void wlr_scene_layer_surface_v1_configure(
- struct wlr_scene_layer_surface_v1 *scene_layer_surface,
- const struct wlr_box *full_area, struct wlr_box *usable_area) {
- struct wlr_layer_surface_v1 *layer_surface =
- scene_layer_surface->layer_surface;
- struct wlr_layer_surface_v1_state *state = &layer_surface->current;
-
- // If the exclusive zone is set to -1, the layer surface will use the
- // full area of the output, otherwise it is constrained to the
- // remaining usable area.
- struct wlr_box bounds;
- if (state->exclusive_zone == -1) {
- bounds = *full_area;
- } else {
- bounds = *usable_area;
- }
-
- struct wlr_box box = {
- .width = state->desired_width,
- .height = state->desired_height,
- };
-
- // Horizontal positioning
- if (box.width == 0) {
- box.x = bounds.x + state->margin.left;
- box.width = bounds.width -
- (state->margin.left + state->margin.right);
- } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT &&
- state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) {
- box.x = bounds.x + bounds.width/2 -box.width/2;
- } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) {
- box.x = bounds.x + state->margin.left;
- } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) {
- box.x = bounds.x + bounds.width - box.width - state->margin.right;
- } else {
- box.x = bounds.x + bounds.width/2 - box.width/2;
- }
-
- // Vertical positioning
- if (box.height == 0) {
- box.y = bounds.y + state->margin.top;
- box.height = bounds.height -
- (state->margin.top + state->margin.bottom);
- } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP &&
- state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) {
- box.y = bounds.y + bounds.height/2 - box.height/2;
- } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) {
- box.y = bounds.y + state->margin.top;
- } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) {
- box.y = bounds.y + bounds.height - box.height - state->margin.bottom;
- } else {
- box.y = bounds.y + bounds.height/2 - box.height/2;
- }
-
- wlr_scene_node_set_position(&scene_layer_surface->tree->node, box.x, box.y);
- wlr_layer_surface_v1_configure(layer_surface, box.width, box.height);
-
- if (layer_surface->mapped && state->exclusive_zone > 0) {
- layer_surface_exclusive_zone(state, usable_area);
- }
-}
-
-struct wlr_scene_layer_surface_v1 *wlr_scene_layer_surface_v1_create(
- struct wlr_scene_tree *parent,
- struct wlr_layer_surface_v1 *layer_surface) {
- struct wlr_scene_layer_surface_v1 *scene_layer_surface =
- calloc(1, sizeof(*scene_layer_surface));
- if (scene_layer_surface == NULL) {
- return NULL;
- }
-
- scene_layer_surface->layer_surface = layer_surface;
-
- scene_layer_surface->tree = wlr_scene_tree_create(parent);
- if (scene_layer_surface->tree == NULL) {
- free(scene_layer_surface);
- return NULL;
- }
-
- struct wlr_scene_tree *surface_tree = wlr_scene_subsurface_tree_create(
- scene_layer_surface->tree, layer_surface->surface);
- if (surface_tree == NULL) {
- wlr_scene_node_destroy(&scene_layer_surface->tree->node);
- free(scene_layer_surface);
- return NULL;
- }
-
- scene_layer_surface->tree_destroy.notify =
- scene_layer_surface_handle_tree_destroy;
- wl_signal_add(&scene_layer_surface->tree->node.events.destroy,
- &scene_layer_surface->tree_destroy);
-
- scene_layer_surface->layer_surface_destroy.notify =
- scene_layer_surface_handle_layer_surface_destroy;
- wl_signal_add(&layer_surface->events.destroy,
- &scene_layer_surface->layer_surface_destroy);
-
- scene_layer_surface->layer_surface_map.notify =
- scene_layer_surface_handle_layer_surface_map;
- wl_signal_add(&layer_surface->events.map,
- &scene_layer_surface->layer_surface_map);
-
- scene_layer_surface->layer_surface_unmap.notify =
- scene_layer_surface_handle_layer_surface_unmap;
- wl_signal_add(&layer_surface->events.unmap,
- &scene_layer_surface->layer_surface_unmap);
-
- wlr_scene_node_set_enabled(&scene_layer_surface->tree->node,
- layer_surface->mapped);
-
- return scene_layer_surface;
-}
diff --git a/types/scene/output_layout.c b/types/scene/output_layout.c
deleted file mode 100644
index 1d1484a..0000000
--- a/types/scene/output_layout.c
+++ /dev/null
@@ -1,157 +0,0 @@
-#include <stdlib.h>
-#include <wlr/types/wlr_output_layout.h>
-#include <wlr/types/wlr_scene.h>
-
-struct wlr_scene_output_layout {
- struct wlr_output_layout *layout;
- struct wlr_scene *scene;
-
- struct wl_list outputs; // wlr_scene_output_layout_output.link
-
- struct wl_listener layout_add;
- struct wl_listener layout_change;
- struct wl_listener layout_destroy;
- struct wl_listener scene_destroy;
-};
-
-struct wlr_scene_output_layout_output {
- struct wlr_output_layout_output *layout_output;
- struct wlr_scene_output *scene_output;
-
- struct wl_list link; // wlr_scene_output_layout.outputs
-
- struct wl_listener layout_output_destroy;
- struct wl_listener scene_output_destroy;
-};
-
-static void scene_output_layout_output_destroy(
- struct wlr_scene_output_layout_output *solo) {
- wl_list_remove(&solo->layout_output_destroy.link);
- wl_list_remove(&solo->scene_output_destroy.link);
- wl_list_remove(&solo->link);
- wlr_scene_output_destroy(solo->scene_output);
- free(solo);
-}
-
-static void scene_output_layout_output_handle_layout_output_destroy(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_output_layout_output *solo =
- wl_container_of(listener, solo, layout_output_destroy);
- scene_output_layout_output_destroy(solo);
-}
-
-static void scene_output_layout_output_handle_scene_output_destroy(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_output_layout_output *solo =
- wl_container_of(listener, solo, scene_output_destroy);
- solo->scene_output = NULL;
- scene_output_layout_output_destroy(solo);
-}
-
-static void scene_output_layout_destroy(struct wlr_scene_output_layout *sol) {
- struct wlr_scene_output_layout_output *solo, *tmp;
- wl_list_for_each_safe(solo, tmp, &sol->outputs, link) {
- scene_output_layout_output_destroy(solo);
- }
- wl_list_remove(&sol->layout_add.link);
- wl_list_remove(&sol->layout_change.link);
- wl_list_remove(&sol->layout_destroy.link);
- wl_list_remove(&sol->scene_destroy.link);
- free(sol);
-}
-
-static void scene_output_layout_handle_layout_change(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_output_layout *sol =
- wl_container_of(listener, sol, layout_change);
-
- struct wlr_scene_output_layout_output *solo;
- wl_list_for_each(solo, &sol->outputs, link) {
- wlr_scene_output_set_position(solo->scene_output,
- solo->layout_output->x, solo->layout_output->y);
- }
-}
-
-static void scene_output_layout_add(struct wlr_scene_output_layout *sol,
- struct wlr_output_layout_output *lo) {
- struct wlr_scene_output_layout_output *solo = calloc(1, sizeof(*solo));
- if (solo == NULL) {
- return;
- }
-
- solo->scene_output = wlr_scene_output_create(sol->scene, lo->output);
- if (solo->scene_output == NULL) {
- free(solo);
- return;
- }
-
- solo->layout_output = lo;
-
- solo->layout_output_destroy.notify =
- scene_output_layout_output_handle_layout_output_destroy;
- wl_signal_add(&lo->events.destroy, &solo->layout_output_destroy);
-
- solo->scene_output_destroy.notify =
- scene_output_layout_output_handle_scene_output_destroy;
- wl_signal_add(&solo->scene_output->events.destroy,
- &solo->scene_output_destroy);
-
- wl_list_insert(&sol->outputs, &solo->link);
-
- wlr_scene_output_set_position(solo->scene_output, lo->x, lo->y);
-}
-
-static void scene_output_layout_handle_layout_add(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_output_layout *sol =
- wl_container_of(listener, sol, layout_add);
- struct wlr_output_layout_output *lo = data;
-
- scene_output_layout_add(sol, lo);
-}
-
-static void scene_output_layout_handle_layout_destroy(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_output_layout *sol =
- wl_container_of(listener, sol, layout_destroy);
- scene_output_layout_destroy(sol);
-}
-
-static void scene_output_layout_handle_scene_destroy(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_output_layout *sol =
- wl_container_of(listener, sol, scene_destroy);
- scene_output_layout_destroy(sol);
-}
-
-bool wlr_scene_attach_output_layout(struct wlr_scene *scene,
- struct wlr_output_layout *output_layout) {
- struct wlr_scene_output_layout *sol = calloc(1, sizeof(*sol));
- if (sol == NULL) {
- return false;
- }
-
- sol->scene = scene;
- sol->layout = output_layout;
-
- wl_list_init(&sol->outputs);
-
- sol->layout_destroy.notify = scene_output_layout_handle_layout_destroy;
- wl_signal_add(&output_layout->events.destroy, &sol->layout_destroy);
-
- sol->layout_change.notify = scene_output_layout_handle_layout_change;
- wl_signal_add(&output_layout->events.change, &sol->layout_change);
-
- sol->layout_add.notify = scene_output_layout_handle_layout_add;
- wl_signal_add(&output_layout->events.add, &sol->layout_add);
-
- sol->scene_destroy.notify = scene_output_layout_handle_scene_destroy;
- wl_signal_add(&scene->tree.node.events.destroy, &sol->scene_destroy);
-
- struct wlr_output_layout_output *lo;
- wl_list_for_each(lo, &output_layout->outputs, link) {
- scene_output_layout_add(sol, lo);
- }
-
- return true;
-}
diff --git a/types/scene/subsurface_tree.c b/types/scene/subsurface_tree.c
deleted file mode 100644
index 35420ab..0000000
--- a/types/scene/subsurface_tree.c
+++ /dev/null
@@ -1,259 +0,0 @@
-#include <assert.h>
-#include <stdlib.h>
-#include <wlr/types/wlr_scene.h>
-#include <wlr/types/wlr_subcompositor.h>
-#include <wlr/util/addon.h>
-
-/**
- * A tree for a surface and all of its child sub-surfaces.
- *
- * `tree` contains `scene_surface` and one node per sub-surface.
- */
-struct wlr_scene_subsurface_tree {
- struct wlr_scene_tree *tree;
- struct wlr_surface *surface;
- struct wlr_scene_surface *scene_surface;
-
- struct wl_listener tree_destroy;
- struct wl_listener surface_destroy;
- struct wl_listener surface_commit;
- struct wl_listener surface_new_subsurface;
-
- struct wlr_scene_subsurface_tree *parent; // NULL for the top-level surface
-
- // Only valid if the surface is a sub-surface
-
- struct wlr_addon surface_addon;
-
- struct wl_listener subsurface_destroy;
- struct wl_listener subsurface_map;
- struct wl_listener subsurface_unmap;
-};
-
-static void subsurface_tree_handle_tree_destroy(struct wl_listener *listener,
- void *data) {
- struct wlr_scene_subsurface_tree *subsurface_tree =
- wl_container_of(listener, subsurface_tree, tree_destroy);
- // tree and scene_surface will be cleaned up by scene_node_finish
- if (subsurface_tree->parent) {
- wlr_addon_finish(&subsurface_tree->surface_addon);
- wl_list_remove(&subsurface_tree->subsurface_destroy.link);
- wl_list_remove(&subsurface_tree->subsurface_map.link);
- wl_list_remove(&subsurface_tree->subsurface_unmap.link);
- }
- wl_list_remove(&subsurface_tree->tree_destroy.link);
- wl_list_remove(&subsurface_tree->surface_destroy.link);
- wl_list_remove(&subsurface_tree->surface_commit.link);
- wl_list_remove(&subsurface_tree->surface_new_subsurface.link);
- free(subsurface_tree);
-}
-
-static const struct wlr_addon_interface subsurface_tree_addon_impl;
-
-static struct wlr_scene_subsurface_tree *subsurface_tree_from_subsurface(
- struct wlr_scene_subsurface_tree *parent,
- struct wlr_subsurface *subsurface) {
- struct wlr_addon *addon = wlr_addon_find(&subsurface->surface->addons,
- parent, &subsurface_tree_addon_impl);
- assert(addon != NULL);
- struct wlr_scene_subsurface_tree *subsurface_tree =
- wl_container_of(addon, subsurface_tree, surface_addon);
- return subsurface_tree;
-}
-
-static void subsurface_tree_reconfigure(
- struct wlr_scene_subsurface_tree *subsurface_tree) {
- struct wlr_surface *surface = subsurface_tree->surface;
-
- struct wlr_scene_node *prev = NULL;
- struct wlr_subsurface *subsurface;
- wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
- current.link) {
- struct wlr_scene_subsurface_tree *child =
- subsurface_tree_from_subsurface(subsurface_tree, subsurface);
- if (prev != NULL) {
- wlr_scene_node_place_above(&child->tree->node, prev);
- }
- prev = &child->tree->node;
-
- wlr_scene_node_set_position(&child->tree->node,
- subsurface->current.x, subsurface->current.y);
- }
-
- if (prev != NULL) {
- wlr_scene_node_place_above(&subsurface_tree->scene_surface->buffer->node, prev);
- }
- prev = &subsurface_tree->scene_surface->buffer->node;
-
- wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
- current.link) {
- struct wlr_scene_subsurface_tree *child =
- subsurface_tree_from_subsurface(subsurface_tree, subsurface);
- wlr_scene_node_place_above(&child->tree->node, prev);
- prev = &child->tree->node;
-
- wlr_scene_node_set_position(&child->tree->node,
- subsurface->current.x, subsurface->current.y);
- }
-}
-
-static void subsurface_tree_handle_surface_destroy(struct wl_listener *listener,
- void *data) {
- struct wlr_scene_subsurface_tree *subsurface_tree =
- wl_container_of(listener, subsurface_tree, surface_destroy);
- wlr_scene_node_destroy(&subsurface_tree->tree->node);
-}
-
-static void subsurface_tree_handle_surface_commit(struct wl_listener *listener,
- void *data) {
- struct wlr_scene_subsurface_tree *subsurface_tree =
- wl_container_of(listener, subsurface_tree, surface_commit);
-
- // TODO: only do this on subsurface order or position change
- subsurface_tree_reconfigure(subsurface_tree);
-}
-
-static void subsurface_tree_handle_subsurface_destroy(struct wl_listener *listener,
- void *data) {
- struct wlr_scene_subsurface_tree *subsurface_tree =
- wl_container_of(listener, subsurface_tree, subsurface_destroy);
- wlr_scene_node_destroy(&subsurface_tree->tree->node);
-}
-
-static void subsurface_tree_handle_subsurface_map(struct wl_listener *listener,
- void *data) {
- struct wlr_scene_subsurface_tree *subsurface_tree =
- wl_container_of(listener, subsurface_tree, subsurface_map);
-
- wlr_scene_node_set_enabled(&subsurface_tree->tree->node, true);
-}
-
-static void subsurface_tree_handle_subsurface_unmap(struct wl_listener *listener,
- void *data) {
- struct wlr_scene_subsurface_tree *subsurface_tree =
- wl_container_of(listener, subsurface_tree, subsurface_unmap);
-
- wlr_scene_node_set_enabled(&subsurface_tree->tree->node, false);
-}
-
-static void subsurface_tree_addon_destroy(struct wlr_addon *addon) {
- struct wlr_scene_subsurface_tree *subsurface_tree =
- wl_container_of(addon, subsurface_tree, surface_addon);
- wlr_scene_node_destroy(&subsurface_tree->tree->node);
-}
-
-static const struct wlr_addon_interface subsurface_tree_addon_impl = {
- .name = "wlr_scene_subsurface_tree",
- .destroy = subsurface_tree_addon_destroy,
-};
-
-static struct wlr_scene_subsurface_tree *scene_surface_tree_create(
- struct wlr_scene_tree *parent, struct wlr_surface *surface);
-
-static bool subsurface_tree_create_subsurface(
- struct wlr_scene_subsurface_tree *parent,
- struct wlr_subsurface *subsurface) {
- struct wlr_scene_subsurface_tree *child = scene_surface_tree_create(
- parent->tree, subsurface->surface);
- if (child == NULL) {
- return false;
- }
-
- child->parent = parent;
- wlr_scene_node_set_enabled(&child->tree->node, subsurface->mapped);
-
- wlr_addon_init(&child->surface_addon, &subsurface->surface->addons,
- parent, &subsurface_tree_addon_impl);
-
- child->subsurface_destroy.notify = subsurface_tree_handle_subsurface_destroy;
- wl_signal_add(&subsurface->events.destroy, &child->subsurface_destroy);
-
- child->subsurface_map.notify = subsurface_tree_handle_subsurface_map;
- wl_signal_add(&subsurface->events.map, &child->subsurface_map);
-
- child->subsurface_unmap.notify = subsurface_tree_handle_subsurface_unmap;
- wl_signal_add(&subsurface->events.unmap, &child->subsurface_unmap);
-
- return true;
-}
-
-static void subsurface_tree_handle_surface_new_subsurface(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_subsurface_tree *subsurface_tree =
- wl_container_of(listener, subsurface_tree, surface_new_subsurface);
- struct wlr_subsurface *subsurface = data;
- if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) {
- wl_resource_post_no_memory(subsurface->resource);
- }
-}
-
-static struct wlr_scene_subsurface_tree *scene_surface_tree_create(
- struct wlr_scene_tree *parent, struct wlr_surface *surface) {
- struct wlr_scene_subsurface_tree *subsurface_tree =
- calloc(1, sizeof(*subsurface_tree));
- if (subsurface_tree == NULL) {
- return NULL;
- }
-
- subsurface_tree->tree = wlr_scene_tree_create(parent);
- if (subsurface_tree->tree == NULL) {
- goto error_surface_tree;
- }
-
- subsurface_tree->scene_surface =
- wlr_scene_surface_create(subsurface_tree->tree, surface);
- if (subsurface_tree->scene_surface == NULL) {
- goto error_scene_surface;
- }
-
- subsurface_tree->surface = surface;
-
- struct wlr_subsurface *subsurface;
- wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
- current.link) {
- if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) {
- goto error_scene_surface;
- }
- }
- wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
- current.link) {
- if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) {
- goto error_scene_surface;
- }
- }
-
- subsurface_tree_reconfigure(subsurface_tree);
-
- subsurface_tree->tree_destroy.notify = subsurface_tree_handle_tree_destroy;
- wl_signal_add(&subsurface_tree->tree->node.events.destroy,
- &subsurface_tree->tree_destroy);
-
- subsurface_tree->surface_destroy.notify = subsurface_tree_handle_surface_destroy;
- wl_signal_add(&surface->events.destroy, &subsurface_tree->surface_destroy);
-
- subsurface_tree->surface_commit.notify = subsurface_tree_handle_surface_commit;
- wl_signal_add(&surface->events.commit, &subsurface_tree->surface_commit);
-
- subsurface_tree->surface_new_subsurface.notify =
- subsurface_tree_handle_surface_new_subsurface;
- wl_signal_add(&surface->events.new_subsurface,
- &subsurface_tree->surface_new_subsurface);
-
- return subsurface_tree;
-
-error_scene_surface:
- wlr_scene_node_destroy(&subsurface_tree->tree->node);
-error_surface_tree:
- free(subsurface_tree);
- return NULL;
-}
-
-struct wlr_scene_tree *wlr_scene_subsurface_tree_create(
- struct wlr_scene_tree *parent, struct wlr_surface *surface) {
- struct wlr_scene_subsurface_tree *subsurface_tree =
- scene_surface_tree_create(parent, surface);
- if (subsurface_tree == NULL) {
- return NULL;
- }
- return subsurface_tree->tree;
-}
diff --git a/types/scene/surface.c b/types/scene/surface.c
deleted file mode 100644
index 553cc42..0000000
--- a/types/scene/surface.c
+++ /dev/null
@@ -1,211 +0,0 @@
-#include <assert.h>
-#include <stdlib.h>
-#include <wlr/types/wlr_scene.h>
-#include <wlr/types/wlr_presentation_time.h>
-#include "types/wlr_scene.h"
-
-static void handle_scene_buffer_output_enter(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_surface *surface =
- wl_container_of(listener, surface, output_enter);
- struct wlr_scene_output *output = data;
-
- wlr_surface_send_enter(surface->surface, output->output);
-}
-
-static void handle_scene_buffer_output_leave(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_surface *surface =
- wl_container_of(listener, surface, output_leave);
- struct wlr_scene_output *output = data;
-
- wlr_surface_send_leave(surface->surface, output->output);
-}
-
-static void handle_scene_buffer_output_present(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_surface *surface =
- wl_container_of(listener, surface, output_present);
- struct wlr_scene_output *scene_output = data;
-
- if (surface->buffer->primary_output == scene_output) {
- struct wlr_scene *root = scene_node_get_root(&surface->buffer->node);
- struct wlr_presentation *presentation = root->presentation;
-
- if (presentation) {
- wlr_presentation_surface_sampled_on_output(
- presentation, surface->surface, scene_output->output);
- }
- }
-}
-
-static void handle_scene_buffer_frame_done(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_surface *surface =
- wl_container_of(listener, surface, frame_done);
- struct timespec *now = data;
-
- wlr_surface_send_frame_done(surface->surface, now);
-}
-
-static void scene_surface_handle_surface_destroy(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_surface *surface =
- wl_container_of(listener, surface, surface_destroy);
-
- wlr_scene_node_destroy(&surface->buffer->node);
-}
-
-// This is used for wlr_scene where it unconditionally locks buffers preventing
-// reuse of the existing texture for shm clients. With the usage pattern of
-// wlr_scene surface handling, we can mark its locked buffer as safe
-// for mutation.
-static void client_buffer_mark_next_can_damage(struct wlr_client_buffer *buffer) {
- buffer->n_ignore_locks++;
-}
-
-static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buffer) {
- if (!scene_buffer->buffer) {
- return;
- }
-
- struct wlr_client_buffer *buffer = wlr_client_buffer_get(scene_buffer->buffer);
- if (!buffer) {
- return;
- }
-
- assert(buffer->n_ignore_locks > 0);
- buffer->n_ignore_locks--;
-}
-
-static void set_buffer_with_surface_state(struct wlr_scene_buffer *scene_buffer,
- struct wlr_surface *surface) {
- struct wlr_surface_state *state = &surface->current;
-
- wlr_scene_buffer_set_opaque_region(scene_buffer, &surface->opaque_region);
-
- struct wlr_fbox src_box;
- wlr_surface_get_buffer_source_box(surface, &src_box);
- wlr_scene_buffer_set_source_box(scene_buffer, &src_box);
-
- wlr_scene_buffer_set_dest_size(scene_buffer, state->width, state->height);
- wlr_scene_buffer_set_transform(scene_buffer, state->transform);
-
- scene_buffer_unmark_client_buffer(scene_buffer);
-
- if (surface->buffer) {
- client_buffer_mark_next_can_damage(surface->buffer);
-
- wlr_scene_buffer_set_buffer_with_damage(scene_buffer,
- &surface->buffer->base, &surface->buffer_damage);
- } else {
- wlr_scene_buffer_set_buffer(scene_buffer, NULL);
- }
-}
-
-static void handle_scene_surface_surface_commit(
- struct wl_listener *listener, void *data) {
- struct wlr_scene_surface *surface =
- wl_container_of(listener, surface, surface_commit);
- struct wlr_scene_buffer *scene_buffer = surface->buffer;
-
- set_buffer_with_surface_state(scene_buffer, surface->surface);
-
- // If the surface has requested a frame done event, honour that. The
- // frame_callback_list will be populated in this case. We should only
- // schedule the frame however if the node is enabled and there is an
- // output intersecting, otherwise the frame done events would never reach
- // the surface anyway.
- int lx, ly;
- bool enabled = wlr_scene_node_coords(&scene_buffer->node, &lx, &ly);
-
- if (!wl_list_empty(&surface->surface->current.frame_callback_list) &&
- surface->buffer->primary_output != NULL && enabled) {
- wlr_output_schedule_frame(surface->buffer->primary_output->output);
- }
-}
-
-static bool scene_buffer_point_accepts_input(struct wlr_scene_buffer *scene_buffer,
- int sx, int sy) {
- struct wlr_scene_surface *scene_surface =
- wlr_scene_surface_from_buffer(scene_buffer);
-
- return wlr_surface_point_accepts_input(scene_surface->surface, sx, sy);
-}
-
-static void surface_addon_destroy(struct wlr_addon *addon) {
- struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon);
-
- scene_buffer_unmark_client_buffer(surface->buffer);
-
- wlr_addon_finish(&surface->addon);
-
- wl_list_remove(&surface->output_enter.link);
- wl_list_remove(&surface->output_leave.link);
- wl_list_remove(&surface->output_present.link);
- wl_list_remove(&surface->frame_done.link);
- wl_list_remove(&surface->surface_destroy.link);
- wl_list_remove(&surface->surface_commit.link);
-
- free(surface);
-}
-
-static const struct wlr_addon_interface surface_addon_impl = {
- .name = "wlr_scene_surface",
- .destroy = surface_addon_destroy,
-};
-
-struct wlr_scene_surface *wlr_scene_surface_from_buffer(
- struct wlr_scene_buffer *scene_buffer) {
- struct wlr_addon *addon = wlr_addon_find(&scene_buffer->node.addons,
- scene_buffer, &surface_addon_impl);
- if (!addon) {
- return NULL;
- }
-
- struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon);
- return surface;
-}
-
-struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent,
- struct wlr_surface *wlr_surface) {
- struct wlr_scene_surface *surface = calloc(1, sizeof(*surface));
- if (surface == NULL) {
- return NULL;
- }
-
- struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create(parent, NULL);
- if (!scene_buffer) {
- free(surface);
- return NULL;
- }
-
- surface->buffer = scene_buffer;
- surface->surface = wlr_surface;
- scene_buffer->point_accepts_input = scene_buffer_point_accepts_input;
-
- surface->output_enter.notify = handle_scene_buffer_output_enter;
- wl_signal_add(&scene_buffer->events.output_enter, &surface->output_enter);
-
- surface->output_leave.notify = handle_scene_buffer_output_leave;
- wl_signal_add(&scene_buffer->events.output_leave, &surface->output_leave);
-
- surface->output_present.notify = handle_scene_buffer_output_present;
- wl_signal_add(&scene_buffer->events.output_present, &surface->output_present);
-
- surface->frame_done.notify = handle_scene_buffer_frame_done;
- wl_signal_add(&scene_buffer->events.frame_done, &surface->frame_done);
-
- surface->surface_destroy.notify = scene_surface_handle_surface_destroy;
- wl_signal_add(&wlr_surface->events.destroy, &surface->surface_destroy);
-
- surface->surface_commit.notify = handle_scene_surface_surface_commit;
- wl_signal_add(&wlr_surface->events.commit, &surface->surface_commit);
-
- wlr_addon_init(&surface->addon, &scene_buffer->node.addons,
- scene_buffer, &surface_addon_impl);
-
- set_buffer_with_surface_state(scene_buffer, wlr_surface);
-
- return surface;
-}
diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c
index 4a32a6b..8ded6a8 100644
--- a/types/scene/wlr_scene.c
+++ b/types/scene/wlr_scene.c
@@ -4,18 +4,21 @@
#include <string.h>
#include <wlr/backend.h>
#include <wlr/render/gles2.h>
-#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_damage_ring.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_subcompositor.h>
+#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
-#include "render/fx_renderer/fx_renderer.h"
+#include <wlr/render/swapchain.h>
+
+#include "render/pass.h"
#include "types/fx/shadow_data.h"
#include "types/wlr_buffer.h"
+#include "types/wlr_output.h"
#include "types/wlr_scene.h"
#include "util/array.h"
#include "util/env.h"
@@ -23,14 +26,13 @@
#define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250
-static struct wlr_scene_tree *scene_tree_from_node(struct wlr_scene_node *node) {
+struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) {
assert(node->type == WLR_SCENE_NODE_TREE);
struct wlr_scene_tree *tree = wl_container_of(node, tree, node);
return tree;
}
-static struct wlr_scene_rect *scene_rect_from_node(
- struct wlr_scene_node *node) {
+struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node) {
assert(node->type == WLR_SCENE_NODE_RECT);
struct wlr_scene_rect *rect = wl_container_of(node, rect, node);
return rect;
@@ -46,7 +48,7 @@ struct wlr_scene_buffer *wlr_scene_buffer_from_node(
struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) {
struct wlr_scene_tree *tree;
if (node->type == WLR_SCENE_NODE_TREE) {
- tree = scene_tree_from_node(node);
+ tree = wlr_scene_tree_from_node(node);
} else {
tree = node->parent;
}
@@ -54,15 +56,17 @@ struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) {
while (tree->node.parent != NULL) {
tree = tree->node.parent;
}
- return (struct wlr_scene *)tree;
+ struct wlr_scene *scene = wl_container_of(tree, scene, tree);
+ return scene;
}
static void scene_node_init(struct wlr_scene_node *node,
enum wlr_scene_node_type type, struct wlr_scene_tree *parent) {
- memset(node, 0, sizeof(*node));
- node->type = type;
- node->parent = parent;
- node->enabled = true;
+ *node = (struct wlr_scene_node){
+ .type = type,
+ .parent = parent,
+ .enabled = true,
+ };
wl_list_init(&node->link);
@@ -114,7 +118,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) {
wlr_buffer_unlock(scene_buffer->buffer);
pixman_region32_fini(&scene_buffer->opaque_region);
} else if (node->type == WLR_SCENE_NODE_TREE) {
- struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
if (scene_tree == &scene->tree) {
assert(!node->parent);
@@ -124,6 +128,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) {
}
wl_list_remove(&scene->presentation_destroy.link);
+ wl_list_remove(&scene->linux_dmabuf_v1_destroy.link);
} else {
assert(node->parent);
}
@@ -142,13 +147,13 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) {
static void scene_tree_init(struct wlr_scene_tree *tree,
struct wlr_scene_tree *parent) {
- memset(tree, 0, sizeof(*tree));
+ *tree = (struct wlr_scene_tree){0};
scene_node_init(&tree->node, WLR_SCENE_NODE_TREE, parent);
wl_list_init(&tree->children);
}
struct wlr_scene *wlr_scene_create(void) {
- struct wlr_scene *scene = calloc(1, sizeof(struct wlr_scene));
+ struct wlr_scene *scene = calloc(1, sizeof(*scene));
if (scene == NULL) {
return NULL;
}
@@ -157,6 +162,7 @@ struct wlr_scene *wlr_scene_create(void) {
wl_list_init(&scene->outputs);
wl_list_init(&scene->presentation_destroy.link);
+ wl_list_init(&scene->linux_dmabuf_v1_destroy.link);
const char *debug_damage_options[] = {
"none",
@@ -175,7 +181,7 @@ struct wlr_scene *wlr_scene_create(void) {
struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) {
assert(parent);
- struct wlr_scene_tree *tree = calloc(1, sizeof(struct wlr_scene_tree));
+ struct wlr_scene_tree *tree = calloc(1, sizeof(*tree));
if (tree == NULL) {
return NULL;
}
@@ -197,7 +203,7 @@ static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box
switch (node->type) {
case WLR_SCENE_NODE_TREE:;
- struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each_reverse(child, &scene_tree->children, link) {
if (_scene_nodes_in_box(child, box, iterator, user_data, lx + child->x, ly + child->y)) {
@@ -230,8 +236,11 @@ static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box,
static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
pixman_region32_t *opaque) {
+ int width, height;
+ scene_node_get_size(node, &width, &height);
+
if (node->type == WLR_SCENE_NODE_RECT) {
- struct wlr_scene_rect *scene_rect = scene_rect_from_node(node);
+ struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
if (scene_rect->color[3] != 1) {
return;
}
@@ -242,20 +251,22 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
return;
}
- // Buffer is translucent
- if (scene_buffer->opacity != 1 || scene_buffer->corner_radius > 0) {
+ if (scene_buffer->opacity != 1) {
+ return;
+ }
+
+ if (scene_buffer->corner_radius > 0) {
return;
}
if (!buffer_is_opaque(scene_buffer->buffer)) {
pixman_region32_copy(opaque, &scene_buffer->opaque_region);
+ pixman_region32_intersect_rect(opaque, opaque, 0, 0, width, height);
pixman_region32_translate(opaque, x, y);
return;
}
}
- int width, height;
- scene_node_get_size(node, &width, &height);
pixman_region32_fini(opaque);
pixman_region32_init_rect(opaque, x, y, width, height);
}
@@ -287,6 +298,28 @@ static void scale_output_damage(pixman_region32_t *damage, float scale) {
}
}
+struct render_data {
+ enum wl_output_transform transform;
+ float scale;
+ struct wlr_box logical;
+ int trans_width, trans_height;
+
+ struct wlr_scene_output *output;
+
+ struct fx_gles_render_pass *render_pass;
+ pixman_region32_t damage;
+};
+
+static void transform_output_damage(pixman_region32_t *damage, const struct render_data *data) {
+ enum wl_output_transform transform = wlr_output_transform_invert(data->transform);
+ wlr_region_transform(damage, damage, transform, data->trans_width, data->trans_height);
+}
+
+static void transform_output_box(struct wlr_box *box, const struct render_data *data) {
+ enum wl_output_transform transform = wlr_output_transform_invert(data->transform);
+ wlr_box_transform(box, box, transform, data->trans_width, data->trans_height);
+}
+
static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *damage) {
if (!pixman_region32_not_empty(damage)) {
return;
@@ -308,7 +341,8 @@ static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *dam
}
static void update_node_update_outputs(struct wlr_scene_node *node,
- struct wl_list *outputs, struct wlr_scene_output *ignore) {
+ struct wl_list *outputs, struct wlr_scene_output *ignore,
+ struct wlr_scene_output *force) {
if (node->type != WLR_SCENE_NODE_BUFFER) {
return;
}
@@ -316,8 +350,10 @@ static void update_node_update_outputs(struct wlr_scene_node *node,
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
uint32_t largest_overlap = 0;
+ struct wlr_scene_output *old_primary_output = scene_buffer->primary_output;
scene_buffer->primary_output = NULL;
+ size_t count = 0;
uint64_t active_outputs = 0;
// let's update the outputs in two steps:
@@ -356,11 +392,17 @@ static void update_node_update_outputs(struct wlr_scene_node *node,
}
active_outputs |= 1ull << scene_output->index;
+ count++;
}
pixman_region32_fini(&intersection);
}
+ if (old_primary_output != scene_buffer->primary_output) {
+ scene_buffer->prev_feedback_options =
+ (struct wlr_linux_dmabuf_feedback_v1_init_options){0};
+ }
+
uint64_t old_active = scene_buffer->active_outputs;
scene_buffer->active_outputs = active_outputs;
@@ -379,6 +421,31 @@ static void update_node_update_outputs(struct wlr_scene_node *node,
// if there are active outputs on this node, we should always have a primary
// output
assert(!scene_buffer->active_outputs || scene_buffer->primary_output);
+
+ // Skip output update event if nothing was updated
+ if (old_active == active_outputs &&
+ (!force || ((1ull << force->index) & ~active_outputs)) &&
+ old_primary_output == scene_buffer->primary_output) {
+ return;
+ }
+
+ struct wlr_scene_output *outputs_array[64];
+ struct wlr_scene_outputs_update_event event = {
+ .active = outputs_array,
+ .size = count,
+ };
+
+ size_t i = 0;
+ wl_list_for_each(scene_output, outputs, link) {
+ if (~active_outputs & (1ull << scene_output->index)) {
+ continue;
+ }
+
+ assert(i < count);
+ outputs_array[i++] = scene_output;
+ }
+
+ wl_signal_emit_mutable(&scene_buffer->events.outputs_update, &event);
}
static bool scene_node_update_iterator(struct wlr_scene_node *node,
@@ -404,13 +471,14 @@ static bool scene_node_update_iterator(struct wlr_scene_node *node,
// Expand the nodes visible region by the shadow size
if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
- struct shadow_data *data = &buffer->shadow_data;
- if (scene_buffer_has_shadow(data)) {
- wlr_region_expand(&node->visible, &node->visible, data->blur_sigma);
+ struct shadow_data *shadow_data = &buffer->shadow_data;
+ if (scene_buffer_has_shadow(shadow_data)) {
+ wlr_region_expand(&node->visible, &node->visible, shadow_data->blur_sigma);
+ wlr_region_expand(data->visible, data->visible, shadow_data->blur_sigma);
}
}
- update_node_update_outputs(node, data->outputs, NULL);
+ update_node_update_outputs(node, data->outputs, NULL, NULL);
return false;
}
@@ -422,7 +490,7 @@ static void scene_node_visibility(struct wlr_scene_node *node,
}
if (node->type == WLR_SCENE_NODE_TREE) {
- struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
scene_node_visibility(child, visible);
@@ -440,7 +508,7 @@ static void scene_node_bounds(struct wlr_scene_node *node,
}
if (node->type == WLR_SCENE_NODE_TREE) {
- struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
scene_node_bounds(child, x + child->x, y + child->y, visible);
@@ -517,8 +585,7 @@ static void scene_node_update(struct wlr_scene_node *node,
struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent,
int width, int height, const float color[static 4]) {
- struct wlr_scene_rect *scene_rect =
- calloc(1, sizeof(struct wlr_scene_rect));
+ struct wlr_scene_rect *scene_rect = calloc(1, sizeof(*scene_rect));
if (scene_rect == NULL) {
return NULL;
}
@@ -566,12 +633,12 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent,
scene_buffer->buffer = wlr_buffer_lock(buffer);
}
+ wl_signal_init(&scene_buffer->events.outputs_update);
wl_signal_init(&scene_buffer->events.output_enter);
wl_signal_init(&scene_buffer->events.output_leave);
- wl_signal_init(&scene_buffer->events.output_present);
+ wl_signal_init(&scene_buffer->events.output_sample);
wl_signal_init(&scene_buffer->events.frame_done);
pixman_region32_init(&scene_buffer->opaque_region);
-
scene_buffer->opacity = 1;
scene_buffer->corner_radius = 0;
scene_buffer->shadow_data = shadow_data_get_default();
@@ -582,14 +649,13 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent,
}
void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer,
- struct wlr_buffer *buffer, pixman_region32_t *damage) {
+ struct wlr_buffer *buffer, const pixman_region32_t *damage) {
// specifying a region for a NULL buffer doesn't make sense. We need to know
// about the buffer to scale the buffer local coordinates down to scene
// coordinates.
assert(buffer || !damage);
bool update = false;
- wlr_buffer_unlock(scene_buffer->buffer);
wlr_texture_destroy(scene_buffer->texture);
scene_buffer->texture = NULL;
@@ -603,8 +669,10 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff
(scene_buffer->buffer->width != buffer->width ||
scene_buffer->buffer->height != buffer->height));
+ wlr_buffer_unlock(scene_buffer->buffer);
scene_buffer->buffer = wlr_buffer_lock(buffer);
} else {
+ wlr_buffer_unlock(scene_buffer->buffer);
update = true;
scene_buffer->buffer = NULL;
}
@@ -694,8 +762,8 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff
pixman_region32_fini(&cull_region);
pixman_region32_translate(&output_damage,
- (lx - scene_output->x) * output_scale,
- (ly - scene_output->y) * output_scale);
+ (int)round((lx - scene_output->x) * output_scale),
+ (int)round((ly - scene_output->y) * output_scale));
if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) {
wlr_output_schedule_frame(scene_output->output);
}
@@ -712,27 +780,35 @@ void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer,
}
void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer,
- pixman_region32_t *region) {
+ const pixman_region32_t *region) {
if (pixman_region32_equal(&scene_buffer->opaque_region, region)) {
return;
}
pixman_region32_copy(&scene_buffer->opaque_region, region);
- scene_node_update(&scene_buffer->node, NULL);
+
+ int x, y;
+ if (!wlr_scene_node_coords(&scene_buffer->node, &x, &y)) {
+ return;
+ }
+
+ pixman_region32_t update_region;
+ pixman_region32_init(&update_region);
+ scene_node_bounds(&scene_buffer->node, x, y, &update_region);
+ scene_update_region(scene_node_get_root(&scene_buffer->node), &update_region);
+ pixman_region32_fini(&update_region);
}
void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer,
const struct wlr_fbox *box) {
- struct wlr_fbox *cur = &scene_buffer->src_box;
- if ((wlr_fbox_empty(box) && wlr_fbox_empty(cur)) ||
- (box != NULL && wlr_fbox_equal(cur, box))) {
+ if (wlr_fbox_equal(&scene_buffer->src_box, box)) {
return;
}
if (box != NULL) {
- memcpy(cur, box, sizeof(*box));
+ scene_buffer->src_box = *box;
} else {
- memset(cur, 0, sizeof(*cur));
+ scene_buffer->src_box = (struct wlr_fbox){0};
}
scene_node_update(&scene_buffer->node, NULL);
@@ -776,6 +852,16 @@ void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer,
scene_node_update(&scene_buffer->node, NULL);
}
+void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer,
+ enum wlr_scale_filter_mode filter_mode) {
+ if (scene_buffer->filter_mode == filter_mode) {
+ return;
+ }
+
+ scene_buffer->filter_mode = filter_mode;
+ scene_node_update(&scene_buffer->node, NULL);
+}
+
void wlr_scene_buffer_set_corner_radius(struct wlr_scene_buffer *scene_buffer,
int radii) {
if (scene_buffer->corner_radius == radii) {
@@ -791,7 +877,10 @@ void wlr_scene_buffer_set_shadow_data(struct wlr_scene_buffer *scene_buffer,
struct shadow_data *buff_data = &scene_buffer->shadow_data;
if (buff_data->enabled == shadow_data.enabled &&
buff_data->blur_sigma == shadow_data.blur_sigma &&
- buff_data->color && shadow_data.color) {
+ buff_data->color.r && shadow_data.color.r &&
+ buff_data->color.g && shadow_data.color.g &&
+ buff_data->color.b && shadow_data.color.b &&
+ buff_data->color.a && shadow_data.color.a) {
return;
}
@@ -826,7 +915,7 @@ static void scene_node_get_size(struct wlr_scene_node *node,
case WLR_SCENE_NODE_TREE:
return;
case WLR_SCENE_NODE_RECT:;
- struct wlr_scene_rect *scene_rect = scene_rect_from_node(node);
+ struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
*width = scene_rect->width;
*height = scene_rect->height;
break;
@@ -995,7 +1084,7 @@ static void scene_node_for_each_scene_buffer(struct wlr_scene_node *node,
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
user_iterator(scene_buffer, lx, ly, user_data);
} else if (node->type == WLR_SCENE_NODE_TREE) {
- struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
scene_node_for_each_scene_buffer(child, lx, ly, user_iterator, user_data);
@@ -1025,7 +1114,7 @@ static bool scene_node_at_iterator(struct wlr_scene_node *node,
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
if (scene_buffer->point_accepts_input &&
- !scene_buffer->point_accepts_input(scene_buffer, rx, ry)) {
+ !scene_buffer->point_accepts_input(scene_buffer, &rx, &ry)) {
return false;
}
}
@@ -1063,208 +1152,175 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node,
return NULL;
}
-static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) {
- struct wlr_box box = {
- .x = rect->x1,
- .y = rect->y1,
- .width = rect->x2 - rect->x1,
- .height = rect->y2 - rect->y1,
- };
-
- int ow, oh;
- wlr_output_transformed_resolution(output, &ow, &oh);
-
- enum wl_output_transform transform =
- wlr_output_transform_invert(output->transform);
- wlr_box_transform(&box, &box, transform, ow, oh);
-
- fx_renderer_scissor(&box);
-}
-
-static void render_rect(struct fx_renderer *fx_renderer, struct wlr_output *output,
- pixman_region32_t *damage, const float color[static 4],
- const struct wlr_box *box, const float matrix[static 9]) {
- assert(fx_renderer);
-
- int nrects;
- pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
- for (int i = 0; i < nrects; ++i) {
- scissor_output(output, &rects[i]);
- fx_render_rect(fx_renderer, box, color, matrix);
- }
-}
-
-static void render_texture(struct fx_renderer *fx_renderer, struct wlr_output *output,
- pixman_region32_t *damage, struct wlr_texture *texture,
- const struct wlr_fbox *src_box, const struct wlr_box *dst_box,
- const float matrix[static 9], float opacity, int corner_radius) {
- assert(fx_renderer);
-
- struct wlr_fbox default_src_box = {0};
- if (wlr_fbox_empty(src_box)) {
- default_src_box.width = texture->width;
- default_src_box.height = texture->height;
- src_box = &default_src_box;
- }
-
- // ensure the box is updated as per the output orientation
- struct wlr_box transformed_box;
- int width, height;
- wlr_output_transformed_resolution(output, &width, &height);
- wlr_box_transform(&transformed_box, dst_box,
- wlr_output_transform_invert(output->transform), width, height);
-
- int nrects;
- pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
- for (int i = 0; i < nrects; ++i) {
- scissor_output(output, &rects[i]);
-
- fx_render_subtexture_with_matrix(fx_renderer, texture, src_box,
- &transformed_box, matrix, opacity, corner_radius);
+// Some surfaces (mostly GTK 4) decorate their windows with shadows
+// which extends the node size past the actual window size. This gets
+// the actual surface geometry, mostly ignoring CSD decorations
+// but only if we need to.
+static void clip_xdg(struct wlr_scene_node *node,
+ pixman_region32_t *clip, struct wlr_box *dst_box,
+ int x, int y, float scale) {
+ struct wlr_scene_buffer *scene_buffer = NULL;
+ switch (node->type) {
+ default:
+ return;
+ case WLR_SCENE_NODE_BUFFER:
+ scene_buffer = wlr_scene_buffer_from_node(node);
+ break;
}
-}
-
-static void render_box_shadow(struct fx_renderer *fx_renderer,
- struct wlr_output *output, pixman_region32_t *surface_damage,
- const struct wlr_box *surface_box, int corner_radius,
- struct shadow_data *shadow_data) {
- // don't damage area behind window since we dont render it anyway
- pixman_region32_t inner_region;
- pixman_region32_init(&inner_region);
- pixman_region32_union_rect(&inner_region, &inner_region,
- surface_box->x + corner_radius * 0.5,
- surface_box->y + corner_radius * 0.5,
- surface_box->width - corner_radius,
- surface_box->height - corner_radius);
- pixman_region32_intersect(&inner_region, &inner_region, surface_damage);
- pixman_region32_t damage;
- pixman_region32_init(&damage);
- pixman_region32_subtract(&damage, surface_damage, &inner_region);
- if (!pixman_region32_not_empty(&damage)) {
- goto damage_finish;
+ if (!scene_buffer || (scene_buffer->corner_radius == 0 &&
+ !scene_buffer_has_shadow(&scene_buffer->shadow_data))) {
+ return;
}
- struct wlr_box shadow_box = {
- .x = surface_box->x - shadow_data->blur_sigma,
- .y = surface_box->y - shadow_data->blur_sigma,
- .width = surface_box->width + 2 * shadow_data->blur_sigma,
- .height = surface_box->height + 2 * shadow_data->blur_sigma,
- };
- float matrix[9];
- wlr_matrix_project_box(matrix, &shadow_box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
- output->transform_matrix);
+ struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer);
+ struct wlr_xdg_surface *xdg_surface = NULL;
+ if (scene_surface &&
+ (xdg_surface = wlr_xdg_surface_try_from_wlr_surface(scene_surface->surface))) {
+ struct wlr_box geometry;
+ wlr_xdg_surface_get_geometry(xdg_surface, &geometry);
+ scale_box(&geometry, scale);
- // ensure the box is updated as per the output orientation
- struct wlr_box transformed_box;
- int width, height;
- wlr_output_transformed_resolution(output, &width, &height);
- wlr_box_transform(&transformed_box, &shadow_box,
- wlr_output_transform_invert(output->transform), width, height);
+ if (dst_box->width > geometry.width) {
+ dst_box->width = geometry.width;
+ dst_box->x = geometry.x + x;
+ }
+ if (dst_box->height > geometry.height) {
+ dst_box->height = geometry.height;
+ dst_box->y = geometry.y + y;
+ }
- int nrects;
- pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
- for (int i = 0; i < nrects; ++i) {
- scissor_output(output, &rects[i]);
- fx_render_box_shadow(fx_renderer, &transformed_box, surface_box, matrix,
- corner_radius, shadow_data);
+ pixman_region32_intersect_rect(clip, clip,
+ dst_box->x, dst_box->y,
+ dst_box->width, dst_box->height);
}
-
-damage_finish:
- pixman_region32_fini(&damage);
- pixman_region32_fini(&inner_region);
}
-static void scene_node_render(struct fx_renderer *fx_renderer, struct wlr_scene_node *node,
- struct wlr_scene_output *scene_output, pixman_region32_t *damage) {
+struct render_list_entry {
+ struct wlr_scene_node *node;
+ bool sent_dmabuf_feedback;
int x, y;
- wlr_scene_node_coords(node, &x, &y);
- x -= scene_output->x;
- y -= scene_output->y;
+};
- struct wlr_output *output = scene_output->output;
+static void scene_entry_render(struct render_list_entry *entry, const struct render_data *data) {
+ struct wlr_scene_node *node = entry->node;
pixman_region32_t render_region;
pixman_region32_init(&render_region);
pixman_region32_copy(&render_region, &node->visible);
- pixman_region32_translate(&render_region, -scene_output->x, -scene_output->y);
- scale_output_damage(&render_region, output->scale);
- pixman_region32_intersect(&render_region, &render_region, damage);
+ pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y);
+ scale_output_damage(&render_region, data->scale);
+ pixman_region32_intersect(&render_region, &render_region, &data->damage);
if (!pixman_region32_not_empty(&render_region)) {
pixman_region32_fini(&render_region);
return;
}
struct wlr_box dst_box = {
- .x = x,
- .y = y,
+ .x = entry->x - data->logical.x,
+ .y = entry->y - data->logical.y,
};
scene_node_get_size(node, &dst_box.width, &dst_box.height);
- scale_box(&dst_box, output->scale);
+ scale_box(&dst_box, data->scale);
+
+ pixman_region32_t opaque;
+ pixman_region32_init(&opaque);
+ scene_node_opaque_region(node, dst_box.x, dst_box.y, &opaque);
+ scale_output_damage(&opaque, data->scale);
+ pixman_region32_subtract(&opaque, &render_region, &opaque);
+
+ struct wlr_box xdg_box = dst_box;
+ // Tries to clip
+ clip_xdg(node, &render_region, &xdg_box, dst_box.x, dst_box.y, data->scale);
+
+ transform_output_box(&dst_box, data);
+ transform_output_box(&xdg_box, data);
+ transform_output_damage(&render_region, data);
- struct wlr_texture *texture;
- float matrix[9];
- enum wl_output_transform transform;
switch (node->type) {
case WLR_SCENE_NODE_TREE:
assert(false);
break;
case WLR_SCENE_NODE_RECT:;
- struct wlr_scene_rect *scene_rect = scene_rect_from_node(node);
-
- render_rect(fx_renderer, output, &render_region, scene_rect->color, &dst_box,
- output->transform_matrix);
+ struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
+
+ struct fx_render_rect_options rect_options = {
+ .base = {
+ .box = dst_box,
+ .color = {
+ .r = scene_rect->color[0],
+ .g = scene_rect->color[1],
+ .b = scene_rect->color[2],
+ .a = scene_rect->color[3],
+ },
+ .clip = &render_region,
+ },
+ .scale = data->scale,
+ };
+ fx_render_pass_add_rect(data->render_pass, &rect_options);
break;
case WLR_SCENE_NODE_BUFFER:;
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
assert(scene_buffer->buffer);
- struct wlr_renderer *renderer = output->renderer;
- texture = scene_buffer_get_texture(scene_buffer, renderer);
-
- transform = wlr_output_transform_invert(scene_buffer->transform);
- wlr_matrix_project_box(matrix, &dst_box, transform, 0.0,
- output->transform_matrix);
-
- // Some surfaces (mostly GTK 4) decorate their windows with shadows
- // which extends the node size past the actual window size. This gets
- // the actual surface geometry, mostly ignoring CSD decorations
- // but only if we need to.
- if (scene_buffer->corner_radius != 0 ||
- scene_buffer_has_shadow(&scene_buffer->shadow_data)) {
- struct wlr_scene_surface *scene_surface = NULL;
- if ((scene_surface = wlr_scene_surface_from_buffer(scene_buffer)) &&
- wlr_surface_is_xdg_surface(scene_surface->surface)) {
- struct wlr_xdg_surface *xdg_surface =
- wlr_xdg_surface_from_wlr_surface(scene_surface->surface);
-
- struct wlr_box geometry;
- wlr_xdg_surface_get_geometry(xdg_surface, &geometry);
- dst_box.width = fmin(dst_box.width, geometry.width);
- dst_box.height = fmin(dst_box.height, geometry.height);
- dst_box.x = fmax(dst_box.x, geometry.x + x);
- dst_box.y = fmax(dst_box.y, geometry.y + y);
- }
+ struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer,
+ data->output->output->renderer);
+ if (texture == NULL) {
+ break;
}
+ enum wl_output_transform transform =
+ wlr_output_transform_invert(scene_buffer->transform);
+ transform = wlr_output_transform_compose(transform, data->transform);
+
// Shadow
if (scene_buffer_has_shadow(&scene_buffer->shadow_data)) {
// TODO: Compensate for SSD borders here
- render_box_shadow(fx_renderer, output, &render_region, &dst_box,
- scene_buffer->corner_radius, &scene_buffer->shadow_data);
+ pixman_region32_t shadow_clip;
+ pixman_region32_init(&shadow_clip);
+ // Extend the size of the clip box
+ wlr_region_expand(&shadow_clip, &render_region,
+ scene_buffer->shadow_data.blur_sigma * data->scale);
+ struct fx_render_rect_options shadow_options = {
+ .base = {
+ .box = xdg_box,
+ .clip = &shadow_clip,
+ },
+ .scale = data->scale,
+ };
+ fx_render_pass_add_box_shadow(data->render_pass, &shadow_options,
+ scene_buffer->corner_radius * data->scale,
+ &scene_buffer->shadow_data);
+ pixman_region32_fini(&shadow_clip);
}
- // Clip the damage to the dst_box before rendering the texture
- pixman_region32_intersect_rect(&render_region, &render_region,
- dst_box.x, dst_box.y, dst_box.width, dst_box.height);
-
- render_texture(fx_renderer, output, &render_region, texture, &scene_buffer->src_box,
- &dst_box, matrix, scene_buffer->opacity, scene_buffer->corner_radius);
+ struct fx_render_texture_options tex_options = {
+ .base = (struct wlr_render_texture_options){
+ .texture = texture,
+ .src_box = scene_buffer->src_box,
+ .dst_box = dst_box,
+ .transform = transform,
+ .clip = &render_region,
+ .alpha = &scene_buffer->opacity,
+ .filter_mode = scene_buffer->filter_mode,
+ .blend_mode = pixman_region32_not_empty(&opaque) ?
+ WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE,
+ },
+ .scale = data->scale,
+ .clip_box = &xdg_box,
+ .corner_radius = scene_buffer->corner_radius * data->scale,
+ };
+ fx_render_pass_add_texture(data->render_pass, &tex_options);
- wl_signal_emit_mutable(&scene_buffer->events.output_present, scene_output);
+ struct wlr_scene_output_sample_event sample_event = {
+ .output = data->output,
+ .direct_scanout = false,
+ };
+ wl_signal_emit_mutable(&scene_buffer->events.output_sample, &sample_event);
break;
}
+ pixman_region32_fini(&opaque);
pixman_region32_fini(&render_region);
}
@@ -1285,6 +1341,23 @@ void wlr_scene_set_presentation(struct wlr_scene *scene,
wl_signal_add(&presentation->events.destroy, &scene->presentation_destroy);
}
+static void scene_handle_linux_dmabuf_v1_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_scene *scene =
+ wl_container_of(listener, scene, linux_dmabuf_v1_destroy);
+ wl_list_remove(&scene->linux_dmabuf_v1_destroy.link);
+ wl_list_init(&scene->linux_dmabuf_v1_destroy.link);
+ scene->linux_dmabuf_v1 = NULL;
+}
+
+void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene,
+ struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1) {
+ assert(scene->linux_dmabuf_v1 == NULL);
+ scene->linux_dmabuf_v1 = linux_dmabuf_v1;
+ scene->linux_dmabuf_v1_destroy.notify = scene_handle_linux_dmabuf_v1_destroy;
+ wl_signal_add(&linux_dmabuf_v1->events.destroy, &scene->linux_dmabuf_v1_destroy);
+}
+
static void scene_output_handle_destroy(struct wlr_addon *addon) {
struct wlr_scene_output *scene_output =
wl_container_of(addon, scene_output, addon);
@@ -1296,50 +1369,47 @@ static const struct wlr_addon_interface output_addon_impl = {
.destroy = scene_output_handle_destroy,
};
-
static void scene_node_output_update(struct wlr_scene_node *node,
- struct wl_list *outputs, struct wlr_scene_output *ignore) {
+ struct wl_list *outputs, struct wlr_scene_output *ignore,
+ struct wlr_scene_output *force) {
if (node->type == WLR_SCENE_NODE_TREE) {
- struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
- scene_node_output_update(child, outputs, ignore);
+ scene_node_output_update(child, outputs, ignore, force);
}
return;
}
- update_node_update_outputs(node, outputs, ignore);
+ update_node_update_outputs(node, outputs, ignore, force);
}
-static void scene_output_update_geometry(struct wlr_scene_output *scene_output) {
- int width, height;
- wlr_output_transformed_resolution(scene_output->output, &width, &height);
- wlr_damage_ring_set_bounds(&scene_output->damage_ring, width, height);
+static void scene_output_update_geometry(struct wlr_scene_output *scene_output,
+ bool force_update) {
+ wlr_damage_ring_add_whole(&scene_output->damage_ring);
wlr_output_schedule_frame(scene_output->output);
scene_node_output_update(&scene_output->scene->tree.node,
- &scene_output->scene->outputs, NULL);
+ &scene_output->scene->outputs, NULL, force_update ? scene_output : NULL);
}
static void scene_output_handle_commit(struct wl_listener *listener, void *data) {
struct wlr_scene_output *scene_output = wl_container_of(listener,
scene_output, output_commit);
struct wlr_output_event_commit *event = data;
+ const struct wlr_output_state *state = event->state;
+
+ bool force_update = state->committed & (
+ WLR_OUTPUT_STATE_TRANSFORM |
+ WLR_OUTPUT_STATE_SCALE |
+ WLR_OUTPUT_STATE_SUBPIXEL);
- if (event->committed & (WLR_OUTPUT_STATE_MODE |
- WLR_OUTPUT_STATE_TRANSFORM |
- WLR_OUTPUT_STATE_SCALE |
+ if (force_update || state->committed & (WLR_OUTPUT_STATE_MODE |
WLR_OUTPUT_STATE_ENABLED)) {
- scene_output_update_geometry(scene_output);
+ scene_output_update_geometry(scene_output, force_update);
}
}
-static void scene_output_handle_mode(struct wl_listener *listener, void *data) {
- struct wlr_scene_output *scene_output = wl_container_of(listener,
- scene_output, output_mode);
- scene_output_update_geometry(scene_output);
-}
-
static void scene_output_handle_damage(struct wl_listener *listener, void *data) {
struct wlr_scene_output *scene_output = wl_container_of(listener,
scene_output, output_damage);
@@ -1366,10 +1436,6 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene,
scene_output->scene = scene;
wlr_addon_init(&scene_output->addon, &output->addons, scene, &output_addon_impl);
- // Init FX Renderer
- struct wlr_egl *egl = wlr_gles2_renderer_get_egl(output->renderer);
- fx_renderer_init_addon(egl, &output->addons, scene);
-
wlr_damage_ring_init(&scene_output->damage_ring);
wl_list_init(&scene_output->damage_highlight_regions);
@@ -1395,16 +1461,13 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene,
scene_output->output_commit.notify = scene_output_handle_commit;
wl_signal_add(&output->events.commit, &scene_output->output_commit);
- scene_output->output_mode.notify = scene_output_handle_mode;
- wl_signal_add(&output->events.mode, &scene_output->output_mode);
-
scene_output->output_damage.notify = scene_output_handle_damage;
wl_signal_add(&output->events.damage, &scene_output->output_damage);
scene_output->output_needs_frame.notify = scene_output_handle_needs_frame;
wl_signal_add(&output->events.needs_frame, &scene_output->output_needs_frame);
- scene_output_update_geometry(scene_output);
+ scene_output_update_geometry(scene_output, false);
return scene_output;
}
@@ -1423,7 +1486,7 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
wl_signal_emit_mutable(&scene_output->events.destroy, NULL);
scene_node_output_update(&scene_output->scene->tree.node,
- &scene_output->scene->outputs, scene_output);
+ &scene_output->scene->outputs, scene_output, NULL);
struct highlight_region *damage, *tmp_damage;
wl_list_for_each_safe(damage, tmp_damage, &scene_output->damage_highlight_regions, link) {
@@ -1434,7 +1497,6 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
wlr_damage_ring_finish(&scene_output->damage_ring);
wl_list_remove(&scene_output->link);
wl_list_remove(&scene_output->output_commit.link);
- wl_list_remove(&scene_output->output_mode.link);
wl_list_remove(&scene_output->output_damage.link);
wl_list_remove(&scene_output->output_needs_frame.link);
@@ -1463,14 +1525,14 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output,
scene_output->x = lx;
scene_output->y = ly;
- scene_output_update_geometry(scene_output);
+ scene_output_update_geometry(scene_output, false);
}
static bool scene_node_invisible(struct wlr_scene_node *node) {
if (node->type == WLR_SCENE_NODE_TREE) {
return true;
} else if (node->type == WLR_SCENE_NODE_RECT) {
- struct wlr_scene_rect *rect = scene_rect_from_node(node);
+ struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
return rect->color[3] == 0.f;
} else if (node->type == WLR_SCENE_NODE_BUFFER) {
@@ -1500,7 +1562,7 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node,
// If we see a black rect, we can ignore rendering everything under the rect
// and even the rect itself.
if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility) {
- struct wlr_scene_rect *rect = scene_rect_from_node(node);
+ struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f };
if (memcmp(rect->color, black, sizeof(float) * 4) == 0) {
@@ -1520,16 +1582,66 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node,
pixman_region32_fini(&intersection);
- struct wlr_scene_node **entry = wl_array_add(data->render_list,
- sizeof(struct wlr_scene_node *));
- if (entry) {
- *entry = node;
+ struct render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry));
+ if (!entry) {
+ return false;
}
+
+ *entry = (struct render_list_entry){
+ .node = node,
+ .x = lx,
+ .y = ly,
+ };
+
return false;
}
-static bool scene_node_try_direct_scanout(struct wlr_scene_node *node,
- struct wlr_scene_output *scene_output, struct wlr_box *box) {
+static void output_state_apply_damage(const struct render_data *data,
+ struct wlr_output_state *state) {
+ pixman_region32_t frame_damage;
+ pixman_region32_init(&frame_damage);
+ pixman_region32_copy(&frame_damage, &data->output->damage_ring.current);
+ transform_output_damage(&frame_damage, data);
+ wlr_output_state_set_damage(state, &frame_damage);
+ pixman_region32_fini(&frame_damage);
+}
+
+static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene,
+ struct wlr_scene_buffer *scene_buffer,
+ const struct wlr_linux_dmabuf_feedback_v1_init_options *options) {
+ if (!scene->linux_dmabuf_v1) {
+ return;
+ }
+
+ struct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(scene_buffer);
+ if (!surface) {
+ return;
+ }
+
+ // compare to the previous options so that we don't send
+ // duplicate feedback events.
+ if (memcmp(options, &scene_buffer->prev_feedback_options, sizeof(*options)) == 0) {
+ return;
+ }
+
+ scene_buffer->prev_feedback_options = *options;
+
+ struct wlr_linux_dmabuf_feedback_v1 feedback = {0};
+ if (!wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, options)) {
+ return;
+ }
+
+ wlr_linux_dmabuf_v1_set_surface_feedback(scene->linux_dmabuf_v1,
+ surface->surface, &feedback);
+
+ wlr_linux_dmabuf_feedback_v1_finish(&feedback);
+}
+
+static bool scene_entry_try_direct_scanout(struct render_list_entry *entry,
+ struct wlr_output_state *state, const struct render_data *data) {
+ struct wlr_scene_output *scene_output = data->output;
+ struct wlr_scene_node *node = entry->node;
+
if (!scene_output->scene->direct_scanout) {
return false;
}
@@ -1545,8 +1657,18 @@ static bool scene_node_try_direct_scanout(struct wlr_scene_node *node,
return false;
}
+ if (state->committed & (WLR_OUTPUT_STATE_MODE |
+ WLR_OUTPUT_STATE_ENABLED |
+ WLR_OUTPUT_STATE_RENDER_FORMAT)) {
+ // Legacy DRM will explode if we try to modeset with a direct scanout buffer
+ return false;
+ }
+
+ if (!wlr_output_is_direct_scanout_allowed(scene_output->output)) {
+ return false;
+ }
+
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
- struct wlr_output *output = scene_output->output;
struct wlr_fbox default_box = {0};
if (buffer->transform & WL_OUTPUT_TRANSFORM_90) {
@@ -1562,75 +1684,170 @@ static bool scene_node_try_direct_scanout(struct wlr_scene_node *node,
return false;
}
- if (buffer->transform != output->transform) {
+ if (buffer->transform != data->transform) {
return false;
}
- struct wlr_box node_box;
- wlr_scene_node_coords(node, &node_box.x, &node_box.y);
+ struct wlr_box node_box = { .x = entry->x, .y = entry->y };
scene_node_get_size(node, &node_box.width, &node_box.height);
- if (!wlr_box_equal(box, &node_box)) {
+ if (!wlr_box_equal(&data->logical, &node_box)) {
return false;
}
- wlr_output_attach_buffer(output, buffer->buffer);
- if (!wlr_output_test(output)) {
- wlr_output_rollback(output);
+ if (buffer->primary_output == scene_output) {
+ struct wlr_linux_dmabuf_feedback_v1_init_options options = {
+ .main_renderer = scene_output->output->renderer,
+ .scanout_primary_output = scene_output->output,
+ };
+
+ scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options);
+ entry->sent_dmabuf_feedback = true;
+ }
+
+ struct wlr_output_state pending;
+ wlr_output_state_init(&pending);
+ if (!wlr_output_state_copy(&pending, state)) {
+ return false;
+ }
+
+ wlr_output_state_set_buffer(&pending, buffer->buffer);
+ output_state_apply_damage(data, &pending);
+
+ if (!wlr_output_test_state(scene_output->output, &pending)) {
+ wlr_output_state_finish(&pending);
return false;
}
- return wlr_output_commit(output);
+ wlr_output_state_copy(state, &pending);
+ wlr_output_state_finish(&pending);
+
+ struct wlr_scene_output_sample_event sample_event = {
+ .output = scene_output,
+ .direct_scanout = true,
+ };
+ wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event);
+ return true;
}
-bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
+bool wlr_scene_output_commit(struct wlr_scene_output *scene_output,
+ const struct wlr_scene_output_state_options *options) {
+ if (!scene_output->output->needs_frame && !pixman_region32_not_empty(
+ &scene_output->damage_ring.current)) {
+ return true;
+ }
+
+ bool ok = false;
+ struct wlr_output_state state;
+ wlr_output_state_init(&state);
+ if (!wlr_scene_output_build_state(scene_output, &state, options)) {
+ goto out;
+ }
+
+ ok = wlr_output_commit_state(scene_output->output, &state);
+ if (!ok) {
+ goto out;
+ }
+
+ wlr_damage_ring_rotate(&scene_output->damage_ring);
+
+out:
+ wlr_output_state_finish(&state);
+ return ok;
+}
+
+bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
+ struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) {
+ struct wlr_scene_output_state_options default_options = {0};
+ if (!options) {
+ options = &default_options;
+ }
+ struct wlr_scene_timer *timer = options->timer;
+ struct timespec start_time;
+ if (timer) {
+ clock_gettime(CLOCK_MONOTONIC, &start_time);
+ wlr_scene_timer_finish(timer);
+ *timer = (struct wlr_scene_timer){0};
+ }
+
+ if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && !state->enabled) {
+ // if the state is being disabled, do nothing.
+ return true;
+ }
+
struct wlr_output *output = scene_output->output;
enum wlr_scene_debug_damage_option debug_damage =
scene_output->scene->debug_damage_option;
- // Find the fx_renderer addon
- struct fx_renderer *renderer =
- fx_renderer_addon_find(&output->addons, scene_output->scene);
- assert(renderer != NULL);
+ struct render_data render_data = {
+ .transform = output->transform,
+ .scale = output->scale,
+ .logical = { .x = scene_output->x, .y = scene_output->y },
+ .output = scene_output,
+ };
+
+ output_pending_resolution(output, state,
+ &render_data.trans_width, &render_data.trans_height);
+
+ if (state->committed & WLR_OUTPUT_STATE_TRANSFORM) {
+ if (render_data.transform != state->transform) {
+ wlr_damage_ring_add_whole(&scene_output->damage_ring);
+ }
+
+ render_data.transform = state->transform;
+ }
+
+ if (state->committed & WLR_OUTPUT_STATE_SCALE) {
+ if (render_data.scale != state->scale) {
+ wlr_damage_ring_add_whole(&scene_output->damage_ring);
+ }
+
+ render_data.scale = state->scale;
+ }
+
+ if (render_data.transform & WL_OUTPUT_TRANSFORM_90) {
+ int tmp = render_data.trans_width;
+ render_data.trans_width = render_data.trans_height;
+ render_data.trans_height = tmp;
+ }
+
+ render_data.logical.width = render_data.trans_width / render_data.scale;
+ render_data.logical.height = render_data.trans_height / render_data.scale;
struct render_list_constructor_data list_con = {
- .box = { .x = scene_output->x, .y = scene_output->y },
+ .box = render_data.logical,
.render_list = &scene_output->render_list,
.calculate_visibility = scene_output->scene->calculate_visibility,
};
- wlr_output_effective_resolution(output,
- &list_con.box.width, &list_con.box.height);
list_con.render_list->size = 0;
scene_nodes_in_box(&scene_output->scene->tree.node, &list_con.box,
construct_render_list_iterator, &list_con);
array_realloc(list_con.render_list, list_con.render_list->size);
- int list_len = list_con.render_list->size / sizeof(struct wlr_scene_node *);
- struct wlr_scene_node **list_data = list_con.render_list->data;
+ struct render_list_entry *list_data = list_con.render_list->data;
+ int list_len = list_con.render_list->size / sizeof(*list_data);
- // if there is only one thing to render let's see if that thing can be
- // directly scanned out
- bool scanout = false;
- if (list_len == 1) {
- struct wlr_scene_node *node = list_data[0];
- scanout = scene_node_try_direct_scanout(node, scene_output, &list_con.box);
- }
+ bool scanout = list_len == 1 &&
+ scene_entry_try_direct_scanout(&list_data[0], state, &render_data);
if (scene_output->prev_scanout != scanout) {
scene_output->prev_scanout = scanout;
wlr_log(WLR_DEBUG, "Direct scan-out %s",
scanout ? "enabled" : "disabled");
- // When exiting direct scan-out, damage everything
- wlr_damage_ring_add_whole(&scene_output->damage_ring);
+ if (!scanout) {
+ // When exiting direct scan-out, damage everything
+ wlr_damage_ring_add_whole(&scene_output->damage_ring);
+ }
}
if (scanout) {
- struct wlr_scene_node *node = list_data[0];
-
- assert(node->type == WLR_SCENE_NODE_BUFFER);
- struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
- wl_signal_emit_mutable(&buffer->events.output_present, scene_output);
+ if (timer) {
+ struct timespec end_time, duration;
+ clock_gettime(CLOCK_MONOTONIC, &end_time);
+ timespec_sub(&duration, &end_time, &start_time);
+ timer->pre_render_duration = timespec_to_nsec(&duration);
+ }
return true;
}
@@ -1645,8 +1862,7 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
// add the current frame's damage if there is damage
if (pixman_region32_not_empty(&scene_output->damage_ring.current)) {
- struct highlight_region *current_damage =
- calloc(1, sizeof(*current_damage));
+ struct highlight_region *current_damage = calloc(1, sizeof(*current_damage));
if (current_damage) {
pixman_region32_init(&current_damage->region);
pixman_region32_copy(&current_damage->region,
@@ -1677,38 +1893,54 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
pixman_region32_fini(&acc_damage);
}
+ wlr_damage_ring_set_bounds(&scene_output->damage_ring,
+ render_data.trans_width, render_data.trans_height);
+
+ if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) {
+ return false;
+ }
+
int buffer_age;
- if (!wlr_output_attach_render(output, &buffer_age)) {
+ struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, &buffer_age);
+ if (buffer == NULL) {
return false;
}
- pixman_region32_t damage;
- pixman_region32_init(&damage);
- wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring,
- buffer_age, &damage);
- if (!output->needs_frame && !pixman_region32_not_empty(
- &scene_output->damage_ring.current)) {
- pixman_region32_fini(&damage);
- wlr_output_rollback(output);
- return true;
+ if (timer) {
+ timer->render_timer = wlr_render_timer_create(output->renderer);
+
+ struct timespec end_time, duration;
+ clock_gettime(CLOCK_MONOTONIC, &end_time);
+ timespec_sub(&duration, &end_time, &start_time);
+ timer->pre_render_duration = timespec_to_nsec(&duration);
}
- fx_renderer_begin(renderer, output->width, output->height);
+ struct fx_gles_render_pass *render_pass =
+ fx_renderer_begin_buffer_pass(output->renderer, buffer,
+ &(struct wlr_buffer_pass_options){
+ .timer = timer ? timer->render_timer : NULL,
+ }
+ );
+ if (render_pass == NULL) {
+ wlr_buffer_unlock(buffer);
+ return false;
+ }
+
+ render_data.render_pass = render_pass;
+ pixman_region32_init(&render_data.damage);
+ wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring,
+ buffer_age, &render_data.damage);
pixman_region32_t background;
pixman_region32_init(&background);
- pixman_region32_copy(&background, &damage);
+ pixman_region32_copy(&background, &render_data.damage);
// Cull areas of the background that are occluded by opaque regions of
// scene nodes above. Those scene nodes will just render atop having us
// never see the background.
if (scene_output->scene->calculate_visibility) {
- float output_scale = scene_output->output->scale;
-
for (int i = list_len - 1; i >= 0; i--) {
- struct wlr_scene_node *node = list_data[i];
- int x, y;
- wlr_scene_node_coords(node, &x, &y);
+ struct render_list_entry *entry = &list_data[i];
// We must only cull opaque regions that are visible by the node.
// The node's visibility will have the knowledge of a black rect
@@ -1717,38 +1949,49 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
// rendering in that black rect region, consider the node's visibility.
pixman_region32_t opaque;
pixman_region32_init(&opaque);
- scene_node_opaque_region(node, x, y, &opaque);
- pixman_region32_intersect(&opaque, &opaque, &node->visible);
+ scene_node_opaque_region(entry->node, entry->x, entry->y, &opaque);
+ pixman_region32_intersect(&opaque, &opaque, &entry->node->visible);
pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y);
- wlr_region_scale(&opaque, &opaque, output_scale);
+ wlr_region_scale(&opaque, &opaque, render_data.scale);
pixman_region32_subtract(&background, &background, &opaque);
pixman_region32_fini(&opaque);
}
- if (floor(output_scale) != output_scale) {
+ if (floor(render_data.scale) != render_data.scale) {
wlr_region_expand(&background, &background, 1);
// reintersect with the damage because we never want to render
// outside of the damage region
- pixman_region32_intersect(&background, &background, &damage);
+ pixman_region32_intersect(&background, &background, &render_data.damage);
}
}
- int nrects;
- pixman_box32_t *rects = pixman_region32_rectangles(&background, &nrects);
- for (int i = 0; i < nrects; ++i) {
- scissor_output(output, &rects[i]);
- fx_renderer_clear((float[4]){ 0.0, 0.0, 0.0, 1.0 });
- }
+ transform_output_damage(&background, &render_data);
+ wlr_render_pass_add_rect(&render_pass->base, &(struct wlr_render_rect_options){
+ .box = { .width = buffer->width, .height = buffer->height },
+ .color = { .r = 0, .g = 0, .b = 0, .a = 1 },
+ .clip = &background,
+ });
pixman_region32_fini(&background);
for (int i = list_len - 1; i >= 0; i--) {
- struct wlr_scene_node *node = list_data[i];
- scene_node_render(renderer, node, scene_output, &damage);
- }
+ struct render_list_entry *entry = &list_data[i];
+ scene_entry_render(entry, &render_data);
+
+ if (entry->node->type == WLR_SCENE_NODE_BUFFER) {
+ struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node);
- fx_renderer_scissor(NULL);
+ if (buffer->primary_output == scene_output && !entry->sent_dmabuf_feedback) {
+ struct wlr_linux_dmabuf_feedback_v1_init_options options = {
+ .main_renderer = output->renderer,
+ .scanout_primary_output = NULL,
+ };
+
+ scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options);
+ }
+ }
+ }
if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
struct highlight_region *damage;
@@ -1758,55 +2001,48 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
int64_t time_diff_ms = timespec_to_msec(&time_diff);
float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME;
- int nrects;
- pixman_box32_t *rects = pixman_region32_rectangles(&damage->region, &nrects);
- for (int i = 0; i < nrects; ++i) {
- struct wlr_box box = {
- .x = rects[i].x1,
- .y = rects[i].y1,
- .width = rects[i].x2 - rects[i].x1,
- .height = rects[i].y2 - rects[i].y1,
- };
-
- float color[4] = { alpha * .5, 0.0, 0.0, alpha * .5 };
- fx_render_rect(renderer, &box, color, output->transform_matrix);
- }
+ wlr_render_pass_add_rect(&render_pass->base, &(struct wlr_render_rect_options){
+ .box = { .width = buffer->width, .height = buffer->height },
+ .color = { .r = alpha * 0.5, .g = 0, .b = 0, .a = alpha * 0.5 },
+ .clip = &damage->region,
+ });
}
}
- // Draw the software cursors
- wlr_renderer_begin(output->renderer, output->width, output->height);
- wlr_output_render_software_cursors(output, &damage);
- wlr_renderer_end(output->renderer);
-
- pixman_region32_fini(&damage);
+ wlr_output_add_software_cursors_to_render_pass(output, &render_pass->base, &render_data.damage);
- int tr_width, tr_height;
- wlr_output_transformed_resolution(output, &tr_width, &tr_height);
+ pixman_region32_fini(&render_data.damage);
- enum wl_output_transform transform =
- wlr_output_transform_invert(output->transform);
-
- pixman_region32_t frame_damage;
- pixman_region32_init(&frame_damage);
- wlr_region_transform(&frame_damage,
- &scene_output->damage_ring.current,
- transform, tr_width, tr_height);
- wlr_output_set_damage(output, &frame_damage);
- pixman_region32_fini(&frame_damage);
-
- bool success = wlr_output_commit(output);
-
- if (success) {
- wlr_damage_ring_rotate(&scene_output->damage_ring);
+ if (!wlr_render_pass_submit(&render_pass->base)) {
+ wlr_buffer_unlock(buffer);
+ return false;
}
+ wlr_output_state_set_buffer(state, buffer);
+ wlr_buffer_unlock(buffer);
+ output_state_apply_damage(&render_data, state);
+
if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT &&
!wl_list_empty(&scene_output->damage_highlight_regions)) {
wlr_output_schedule_frame(scene_output->output);
}
- return success;
+ return true;
+}
+
+int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer) {
+ int64_t pre_render = timer->pre_render_duration;
+ if (!timer->render_timer) {
+ return pre_render;
+ }
+ int64_t render = wlr_render_timer_get_duration_ns(timer->render_timer);
+ return render != -1 ? pre_render + render : -1;
+}
+
+void wlr_scene_timer_finish(struct wlr_scene_timer *timer) {
+ if (timer->render_timer) {
+ wlr_render_timer_destroy(timer->render_timer);
+ }
}
static void scene_node_send_frame_done(struct wlr_scene_node *node,
@@ -1823,7 +2059,7 @@ static void scene_node_send_frame_done(struct wlr_scene_node *node,
wlr_scene_buffer_send_frame_done(scene_buffer, now);
}
} else if (node->type == WLR_SCENE_NODE_TREE) {
- struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
scene_node_send_frame_done(child, scene_output, now);
@@ -1858,7 +2094,7 @@ static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box,
user_iterator(scene_buffer, lx, ly, user_data);
}
} else if (node->type == WLR_SCENE_NODE_TREE) {
- struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
scene_output_for_each_scene_buffer(output_box, child, lx, ly,
diff --git a/types/scene/xdg_shell.c b/types/scene/xdg_shell.c
deleted file mode 100644
index 1688348..0000000
--- a/types/scene/xdg_shell.c
+++ /dev/null
@@ -1,124 +0,0 @@
-#include <stdlib.h>
-#include <wlr/types/wlr_scene.h>
-#include <wlr/types/wlr_xdg_shell.h>
-
-struct wlr_scene_xdg_surface {
- struct wlr_scene_tree *tree;
- struct wlr_xdg_surface *xdg_surface;
- struct wlr_scene_tree *surface_tree;
-
- struct wl_listener tree_destroy;
- struct wl_listener xdg_surface_destroy;
- struct wl_listener xdg_surface_map;
- struct wl_listener xdg_surface_unmap;
- struct wl_listener xdg_surface_commit;
-};
-
-static void scene_xdg_surface_handle_tree_destroy(struct wl_listener *listener,
- void *data) {
- struct wlr_scene_xdg_surface *scene_xdg_surface =
- wl_container_of(listener, scene_xdg_surface, tree_destroy);
- // tree and surface_node will be cleaned up by scene_node_finish
- wl_list_remove(&scene_xdg_surface->tree_destroy.link);
- wl_list_remove(&scene_xdg_surface->xdg_surface_destroy.link);
- wl_list_remove(&scene_xdg_surface->xdg_surface_map.link);
- wl_list_remove(&scene_xdg_surface->xdg_surface_unmap.link);
- wl_list_remove(&scene_xdg_surface->xdg_surface_commit.link);
- free(scene_xdg_surface);
-}
-
-static void scene_xdg_surface_handle_xdg_surface_destroy(struct wl_listener *listener,
- void *data) {
- struct wlr_scene_xdg_surface *scene_xdg_surface =
- wl_container_of(listener, scene_xdg_surface, xdg_surface_destroy);
- wlr_scene_node_destroy(&scene_xdg_surface->tree->node);
-}
-
-static void scene_xdg_surface_handle_xdg_surface_map(struct wl_listener *listener,
- void *data) {
- struct wlr_scene_xdg_surface *scene_xdg_surface =
- wl_container_of(listener, scene_xdg_surface, xdg_surface_map);
- wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, true);
-}
-
-static void scene_xdg_surface_handle_xdg_surface_unmap(struct wl_listener *listener,
- void *data) {
- struct wlr_scene_xdg_surface *scene_xdg_surface =
- wl_container_of(listener, scene_xdg_surface, xdg_surface_unmap);
- wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, false);
-}
-
-static void scene_xdg_surface_update_position(
- struct wlr_scene_xdg_surface *scene_xdg_surface) {
- struct wlr_xdg_surface *xdg_surface = scene_xdg_surface->xdg_surface;
-
- struct wlr_box geo = {0};
- wlr_xdg_surface_get_geometry(xdg_surface, &geo);
- wlr_scene_node_set_position(&scene_xdg_surface->surface_tree->node,
- -geo.x, -geo.y);
-
- if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
- struct wlr_xdg_popup *popup = xdg_surface->popup;
- wlr_scene_node_set_position(&scene_xdg_surface->tree->node,
- popup->current.geometry.x, popup->current.geometry.y);
- }
-}
-
-static void scene_xdg_surface_handle_xdg_surface_commit(struct wl_listener *listener,
- void *data) {
- struct wlr_scene_xdg_surface *scene_xdg_surface =
- wl_container_of(listener, scene_xdg_surface, xdg_surface_commit);
- scene_xdg_surface_update_position(scene_xdg_surface);
-}
-
-struct wlr_scene_tree *wlr_scene_xdg_surface_create(
- struct wlr_scene_tree *parent, struct wlr_xdg_surface *xdg_surface) {
- struct wlr_scene_xdg_surface *scene_xdg_surface =
- calloc(1, sizeof(*scene_xdg_surface));
- if (scene_xdg_surface == NULL) {
- return NULL;
- }
-
- scene_xdg_surface->xdg_surface = xdg_surface;
-
- scene_xdg_surface->tree = wlr_scene_tree_create(parent);
- if (scene_xdg_surface->tree == NULL) {
- free(scene_xdg_surface);
- return NULL;
- }
-
- scene_xdg_surface->surface_tree = wlr_scene_subsurface_tree_create(
- scene_xdg_surface->tree, xdg_surface->surface);
- if (scene_xdg_surface->surface_tree == NULL) {
- wlr_scene_node_destroy(&scene_xdg_surface->tree->node);
- free(scene_xdg_surface);
- return NULL;
- }
-
- scene_xdg_surface->tree_destroy.notify =
- scene_xdg_surface_handle_tree_destroy;
- wl_signal_add(&scene_xdg_surface->tree->node.events.destroy,
- &scene_xdg_surface->tree_destroy);
-
- scene_xdg_surface->xdg_surface_destroy.notify =
- scene_xdg_surface_handle_xdg_surface_destroy;
- wl_signal_add(&xdg_surface->events.destroy, &scene_xdg_surface->xdg_surface_destroy);
-
- scene_xdg_surface->xdg_surface_map.notify =
- scene_xdg_surface_handle_xdg_surface_map;
- wl_signal_add(&xdg_surface->events.map, &scene_xdg_surface->xdg_surface_map);
-
- scene_xdg_surface->xdg_surface_unmap.notify =
- scene_xdg_surface_handle_xdg_surface_unmap;
- wl_signal_add(&xdg_surface->events.unmap, &scene_xdg_surface->xdg_surface_unmap);
-
- scene_xdg_surface->xdg_surface_commit.notify =
- scene_xdg_surface_handle_xdg_surface_commit;
- wl_signal_add(&xdg_surface->surface->events.commit,
- &scene_xdg_surface->xdg_surface_commit);
-
- wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, xdg_surface->mapped);
- scene_xdg_surface_update_position(scene_xdg_surface);
-
- return scene_xdg_surface->tree;
-}
diff --git a/util/env.c b/util/env.c
index b0a9efd..77911a2 100644
--- a/util/env.c
+++ b/util/env.c
@@ -19,7 +19,7 @@ bool env_parse_bool(const char *option) {
return false;
}
-ssize_t env_parse_switch(const char *option, const char **switches) {
+size_t env_parse_switch(const char *option, const char **switches) {
const char *env = getenv(option);
if (env) {
wlr_log(WLR_INFO, "Loading %s option: %s", option, env);
diff --git a/util/time.c b/util/time.c
index 06e42b4..78faac5 100644
--- a/util/time.c
+++ b/util/time.c
@@ -10,12 +10,16 @@ int64_t timespec_to_msec(const struct timespec *a) {
return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
}
+int64_t timespec_to_nsec(const struct timespec *a) {
+ return (int64_t)a->tv_sec * NSEC_PER_SEC + a->tv_nsec;
+}
+
void timespec_from_nsec(struct timespec *r, int64_t nsec) {
r->tv_sec = nsec / NSEC_PER_SEC;
r->tv_nsec = nsec % NSEC_PER_SEC;
}
-uint32_t get_current_time_msec(void) {
+int64_t get_current_time_msec(void) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return timespec_to_msec(&now);