Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
scenefx/render/fx_renderer/fx_framebuffer.c (5.5K)
1 #include <stdlib.h>
2 #include <wlr/interfaces/wlr_buffer.h>
3 #include <wlr/render/allocator.h>
4 #include <wlr/render/interface.h>
5 #include <wlr/util/log.h>
6
7 #include "render/egl.h"
8 #include "render/fx_renderer/fx_renderer.h"
9
10 static void handle_buffer_destroy(struct wlr_addon *addon) {
11 struct fx_framebuffer *buffer =
12 wl_container_of(addon, buffer, addon);
13 fx_framebuffer_destroy(buffer);
14 }
15
16 static const struct wlr_addon_interface buffer_addon_impl = {
17 .name = "fx_framebuffer",
18 .destroy = handle_buffer_destroy,
19 };
20
21 GLuint fx_framebuffer_get_fbo(struct fx_framebuffer *buffer) {
22 if (buffer->external_only) {
23 wlr_log(WLR_ERROR, "DMA-BUF format is external-only");
24 return 0;
25 }
26
27 if (buffer->fbo) {
28 return buffer->fbo;
29 }
30
31 push_fx_debug(buffer->renderer);
32
33 if (!buffer->rbo) {
34 glGenRenderbuffers(1, &buffer->rbo);
35 glBindRenderbuffer(GL_RENDERBUFFER, buffer->rbo);
36 buffer->renderer->procs.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER,
37 buffer->image);
38 glBindRenderbuffer(GL_RENDERBUFFER, 0);
39 }
40
41 glGenFramebuffers(1, &buffer->fbo);
42 glBindFramebuffer(GL_FRAMEBUFFER, buffer->fbo);
43 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
44 GL_RENDERBUFFER, buffer->rbo);
45 GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
46
47 // Init stencil buffer
48 glGenRenderbuffers(1, &buffer->sb);
49 glBindRenderbuffer(GL_RENDERBUFFER, buffer->sb);
50 glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
51 buffer->buffer->width, buffer->buffer->height);
52 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
53 GL_RENDERBUFFER, buffer->sb);
54 fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
55
56 glBindFramebuffer(GL_FRAMEBUFFER, 0);
57
58 if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
59 wlr_log(WLR_ERROR, "Failed to create FBO");
60 glDeleteFramebuffers(1, &buffer->fbo);
61 buffer->fbo = 0;
62 }
63
64 pop_fx_debug(buffer->renderer);
65
66 return buffer->fbo;
67 }
68
69 void fx_framebuffer_get_or_create_custom(struct fx_renderer *renderer,
70 struct wlr_allocator *allocator, int width, int height, bool has_alpha,
71 struct fx_framebuffer **fx_framebuffer, bool *failed) {
72 if (*failed) {
73 return;
74 }
75
76 if (*fx_framebuffer != NULL) {
77 struct wlr_buffer *wlr_buffer = (*fx_framebuffer)->buffer;
78 if (wlr_buffer != NULL) {
79 if (wlr_buffer->width == width && wlr_buffer->height == height) {
80 return;
81 }
82 // Create a new wlr_buffer if it's null or if the output size has
83 // changed
84 wlr_buffer_drop(wlr_buffer);
85 } else {
86 fx_framebuffer_destroy(*fx_framebuffer);
87 }
88 *fx_framebuffer = NULL;
89 }
90
91 // Get the best supported DRM format (DMABUF if supported)
92 const struct wlr_drm_format_set *texture_formats = wlr_renderer_get_texture_formats(
93 &renderer->wlr_renderer, renderer->wlr_renderer.render_buffer_caps);
94 const struct fx_pixel_format *pix_format =
95 get_fx_format_from_gl(GL_RGBA, GL_UNSIGNED_BYTE, has_alpha);
96 const struct wlr_drm_format *format = wlr_drm_format_set_get(texture_formats, pix_format->drm_format);
97 if (format == NULL) {
98 wlr_log(WLR_ERROR, "Failed to get a supported texture format while allocating buffer");
99 *failed = true;
100 return;
101 }
102
103 struct wlr_buffer *wlr_buffer = wlr_allocator_create_buffer(allocator, width, height, format);
104 if (wlr_buffer == NULL) {
105 wlr_log(WLR_ERROR, "Failed to allocate wlr_buffer");
106 *failed = true;
107 return;
108 }
109
110 *fx_framebuffer = fx_framebuffer_get_or_create(renderer, wlr_buffer);
111 if (*fx_framebuffer == NULL) {
112 wlr_log(WLR_ERROR, "Failed to allocate fx_buffer");
113 wlr_buffer_drop(wlr_buffer);
114 *failed = true;
115 return;
116 }
117 fx_framebuffer_get_fbo(*fx_framebuffer);
118 }
119
120 struct fx_framebuffer *fx_framebuffer_get_or_create(struct fx_renderer *renderer,
121 struct wlr_buffer *wlr_buffer) {
122 struct wlr_addon *addon =
123 wlr_addon_find(&wlr_buffer->addons, renderer, &buffer_addon_impl);
124 if (addon) {
125 struct fx_framebuffer *buffer = wl_container_of(addon, buffer, addon);
126 return buffer;
127 }
128
129 struct fx_framebuffer *buffer = calloc(1, sizeof(*buffer));
130 if (buffer == NULL) {
131 wlr_log_errno(WLR_ERROR, "Allocation failed");
132 return NULL;
133 }
134 buffer->buffer = wlr_buffer;
135 buffer->renderer = renderer;
136
137 struct wlr_dmabuf_attributes dmabuf = {0};
138 if (!wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) {
139 goto error_buffer;
140 }
141
142 buffer->image = wlr_egl_create_image_from_dmabuf(renderer->egl,
143 &dmabuf, &buffer->external_only);
144 if (buffer->image == EGL_NO_IMAGE_KHR) {
145 goto error_buffer;
146 }
147
148 wlr_addon_init(&buffer->addon, &wlr_buffer->addons, renderer,
149 &buffer_addon_impl);
150
151 wl_list_insert(&renderer->buffers, &buffer->link);
152
153 wlr_log(WLR_DEBUG, "Created GL FBO for buffer %dx%d",
154 wlr_buffer->width, wlr_buffer->height);
155
156 return buffer;
157
158 error_buffer:
159 free(buffer);
160 return NULL;
161 }
162
163 void fx_framebuffer_bind(struct fx_framebuffer *fx_buffer) {
164 glBindFramebuffer(GL_FRAMEBUFFER, fx_framebuffer_get_fbo(fx_buffer));
165 }
166
167 void fx_framebuffer_destroy(struct fx_framebuffer *fx_buffer) {
168 if (!fx_buffer) {
169 wlr_log(WLR_ERROR, "Trying to destroy an already destroyed fx_framebuffer");
170 return;
171 }
172
173 // Release the framebuffer
174 wl_list_remove(&fx_buffer->link);
175 wlr_addon_finish(&fx_buffer->addon);
176
177 struct wlr_egl_context prev_ctx;
178 wlr_egl_make_current(fx_buffer->renderer->egl, &prev_ctx);
179
180 glDeleteFramebuffers(1, &fx_buffer->fbo);
181 fx_buffer->fbo = -1;
182 glDeleteRenderbuffers(1, &fx_buffer->rbo);
183 fx_buffer->rbo = -1;
184 glDeleteTextures(1, &fx_buffer->tex);
185 fx_buffer->tex = -1;
186 glDeleteRenderbuffers(1, &fx_buffer->sb);
187 fx_buffer->sb = -1;
188
189 wlr_egl_destroy_image(fx_buffer->renderer->egl, fx_buffer->image);
190
191 wlr_egl_restore_context(&prev_ctx);
192
193 free(fx_buffer);
194 }
195