Compare commits
	
		
			1 Commits
		
	
	
		
			master
			...
			export-dma
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 3ac3f6cab2 | 
| @@ -31,6 +31,7 @@ Build requirements are: | ||||
|  | ||||
| - meson | ||||
| - GTK+3 | ||||
| - libdrm | ||||
| - epoxy | ||||
| - wayland-client | ||||
|  | ||||
|   | ||||
| @@ -21,9 +21,11 @@ wayland_scanner_client = generator( | ||||
|  | ||||
| client_protocols = [ | ||||
|   [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], | ||||
|   [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], | ||||
|   [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], | ||||
|   ['wlr-output-management-unstable-v1.xml'], | ||||
|   ['wlr-screencopy-unstable-v1.xml'], | ||||
|   ['wlr-export-dmabuf-unstable-v1.xml'], | ||||
|   ['wlr-layer-shell-unstable-v1.xml'] | ||||
| ] | ||||
|  | ||||
|   | ||||
							
								
								
									
										203
									
								
								protocol/wlr-export-dmabuf-unstable-v1.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								protocol/wlr-export-dmabuf-unstable-v1.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,203 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <protocol name="wlr_export_dmabuf_unstable_v1"> | ||||
|   <copyright> | ||||
|     Copyright © 2018 Rostislav Pehlivanov | ||||
|  | ||||
|     Permission is hereby granted, free of charge, to any person obtaining a | ||||
|     copy of this software and associated documentation files (the "Software"), | ||||
|     to deal in the Software without restriction, including without limitation | ||||
|     the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
|     and/or sell copies of the Software, and to permit persons to whom the | ||||
|     Software is furnished to do so, subject to the following conditions: | ||||
|  | ||||
|     The above copyright notice and this permission notice (including the next | ||||
|     paragraph) shall be included in all copies or substantial portions of the | ||||
|     Software. | ||||
|  | ||||
|     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | ||||
|     THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
|     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
|     DEALINGS IN THE SOFTWARE. | ||||
|   </copyright> | ||||
|  | ||||
|   <description summary="a protocol for low overhead screen content capturing"> | ||||
|     An interface to capture surfaces in an efficient way by exporting DMA-BUFs. | ||||
|  | ||||
|     Warning! The protocol described in this file is experimental and | ||||
|     backward incompatible changes may be made. Backward compatible changes | ||||
|     may be added together with the corresponding interface version bump. | ||||
|     Backward incompatible changes are done by bumping the version number in | ||||
|     the protocol and interface names and resetting the interface version. | ||||
|     Once the protocol is to be declared stable, the 'z' prefix and the | ||||
|     version number in the protocol and interface names are removed and the | ||||
|     interface version number is reset. | ||||
|   </description> | ||||
|  | ||||
|   <interface name="zwlr_export_dmabuf_manager_v1" version="1"> | ||||
|     <description summary="manager to inform clients and begin capturing"> | ||||
|       This object is a manager with which to start capturing from sources. | ||||
|     </description> | ||||
|  | ||||
|     <request name="capture_output"> | ||||
|       <description summary="capture a frame from an output"> | ||||
|         Capture the next frame of a an entire output. | ||||
|       </description> | ||||
|       <arg name="frame" type="new_id" interface="zwlr_export_dmabuf_frame_v1"/> | ||||
|       <arg name="overlay_cursor" type="int" | ||||
|            summary="include custom client hardware cursor on top of the frame"/> | ||||
|       <arg name="output" type="object" interface="wl_output"/> | ||||
|     </request> | ||||
|  | ||||
|     <request name="destroy" type="destructor"> | ||||
|       <description summary="destroy the manager"> | ||||
|         All objects created by the manager will still remain valid, until their | ||||
|         appropriate destroy request has been called. | ||||
|       </description> | ||||
|     </request> | ||||
|   </interface> | ||||
|  | ||||
|   <interface name="zwlr_export_dmabuf_frame_v1" version="1"> | ||||
|     <description summary="a DMA-BUF frame"> | ||||
|       This object represents a single DMA-BUF frame. | ||||
|  | ||||
|       If the capture is successful, the compositor will first send a "frame" | ||||
|       event, followed by one or several "object". When the frame is available | ||||
|       for readout, the "ready" event is sent. | ||||
|  | ||||
|       If the capture failed, the "cancel" event is sent. This can happen anytime | ||||
|       before the "ready" event. | ||||
|  | ||||
|       Once either a "ready" or a "cancel" event is received, the client should | ||||
|       destroy the frame. Once an "object" event is received, the client is | ||||
|       responsible for closing the associated file descriptor. | ||||
|  | ||||
|       All frames are read-only and may not be written into or altered. | ||||
|     </description> | ||||
|  | ||||
|     <enum name="flags"> | ||||
|       <description summary="frame flags"> | ||||
|         Special flags that should be respected by the client. | ||||
|       </description> | ||||
|       <entry name="transient" value="0x1" | ||||
|              summary="clients should copy frame before processing"/> | ||||
|     </enum> | ||||
|  | ||||
|     <event name="frame"> | ||||
|       <description summary="a frame description"> | ||||
|         Main event supplying the client with information about the frame. If the | ||||
|         capture didn't fail, this event is always emitted first before any other | ||||
|         events. | ||||
|  | ||||
|         This event is followed by a number of "object" as specified by the | ||||
|         "num_objects" argument. | ||||
|       </description> | ||||
|       <arg name="width" type="uint" | ||||
|            summary="frame width in pixels"/> | ||||
|       <arg name="height" type="uint" | ||||
|            summary="frame height in pixels"/> | ||||
|       <arg name="offset_x" type="uint" | ||||
|            summary="crop offset for the x axis"/> | ||||
|       <arg name="offset_y" type="uint" | ||||
|            summary="crop offset for the y axis"/> | ||||
|       <arg name="buffer_flags" type="uint" | ||||
|            summary="flags which indicate properties (invert, interlacing), | ||||
|                     has the same values as zwp_linux_buffer_params_v1:flags"/> | ||||
|       <arg name="flags" type="uint" enum="flags" | ||||
|            summary="indicates special frame features"/> | ||||
|       <arg name="format" type="uint" | ||||
|            summary="format of the frame (DRM_FORMAT_*)"/> | ||||
|       <arg name="mod_high" type="uint" | ||||
|            summary="drm format modifier, high"/> | ||||
|       <arg name="mod_low" type="uint" | ||||
|            summary="drm format modifier, low"/> | ||||
|       <arg name="num_objects" type="uint" | ||||
|            summary="indicates how many objects (FDs) the frame has (max 4)"/> | ||||
|     </event> | ||||
|  | ||||
|     <event name="object"> | ||||
|       <description summary="an object description"> | ||||
|         Event which serves to supply the client with the file descriptors | ||||
|         containing the data for each object. | ||||
|  | ||||
|         After receiving this event, the client must always close the file | ||||
|         descriptor as soon as they're done with it and even if the frame fails. | ||||
|       </description> | ||||
|       <arg name="index" type="uint" | ||||
|            summary="index of the current object"/> | ||||
|       <arg name="fd" type="fd" | ||||
|            summary="fd of the current object"/> | ||||
|       <arg name="size" type="uint" | ||||
|            summary="size in bytes for the current object"/> | ||||
|       <arg name="offset" type="uint" | ||||
|            summary="starting point for the data in the object's fd"/> | ||||
|       <arg name="stride" type="uint" | ||||
|            summary="line size in bytes"/> | ||||
|       <arg name="plane_index" type="uint" | ||||
|            summary="index of the the plane the data in the object applies to"/> | ||||
|     </event> | ||||
|  | ||||
|     <event name="ready"> | ||||
|       <description summary="indicates frame is available for reading"> | ||||
|         This event is sent as soon as the frame is presented, indicating it is | ||||
|         available for reading. This event includes the time at which | ||||
|         presentation happened at. | ||||
|  | ||||
|         The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, | ||||
|         each component being an unsigned 32-bit value. Whole seconds are in | ||||
|         tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, | ||||
|         and the additional fractional part in tv_nsec as nanoseconds. Hence, | ||||
|         for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part | ||||
|         may have an arbitrary offset at start. | ||||
|  | ||||
|         After receiving this event, the client should destroy this object. | ||||
|       </description> | ||||
|       <arg name="tv_sec_hi" type="uint" | ||||
|            summary="high 32 bits of the seconds part of the timestamp"/> | ||||
|       <arg name="tv_sec_lo" type="uint" | ||||
|            summary="low 32 bits of the seconds part of the timestamp"/> | ||||
|       <arg name="tv_nsec" type="uint" | ||||
|            summary="nanoseconds part of the timestamp"/> | ||||
|     </event> | ||||
|  | ||||
|     <enum name="cancel_reason"> | ||||
|       <description summary="cancel reason"> | ||||
|         Indicates reason for cancelling the frame. | ||||
|       </description> | ||||
|       <entry name="temporary" value="0" | ||||
|              summary="temporary error, source will produce more frames"/> | ||||
|       <entry name="permanent" value="1" | ||||
|              summary="fatal error, source will not produce frames"/> | ||||
|       <entry name="resizing" value="2" | ||||
|              summary="temporary error, source will produce more frames"/> | ||||
|     </enum> | ||||
|  | ||||
|     <event name="cancel"> | ||||
|       <description summary="indicates the frame is no longer valid"> | ||||
|         If the capture failed or if the frame is no longer valid after the | ||||
|         "frame" event has been emitted, this event will be used to inform the | ||||
|         client to scrap the frame. | ||||
|  | ||||
|         If the failure is temporary, the client may capture again the same | ||||
|         source. If the failure is permanent, any further attempts to capture the | ||||
|         same source will fail again. | ||||
|  | ||||
|         After receiving this event, the client should destroy this object. | ||||
|       </description> | ||||
|       <arg name="reason" type="uint" enum="cancel_reason" | ||||
|            summary="indicates a reason for cancelling this frame capture"/> | ||||
|     </event> | ||||
|  | ||||
|     <request name="destroy" type="destructor"> | ||||
|       <description summary="delete this object, used or not"> | ||||
|         Unreferences the frame. This request must be called as soon as its no | ||||
|         longer used. | ||||
|  | ||||
|         It can be called at any time by the client. The client will still have | ||||
|         to close any FDs it has been given. | ||||
|       </description> | ||||
|     </request> | ||||
|   </interface> | ||||
| </protocol> | ||||
							
								
								
									
										36
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								src/main.c
									
									
									
									
									
								
							| @@ -3,11 +3,14 @@ | ||||
|  | ||||
| #include <gtk/gtk.h> | ||||
| #include <gdk/gdkwayland.h> | ||||
| #include <drm_fourcc.h> | ||||
|  | ||||
| #include "wdisplays.h" | ||||
| #include "glviewport.h" | ||||
| #include "headform.h" | ||||
|  | ||||
| #include "linux-dmabuf-unstable-v1-client-protocol.h" | ||||
|  | ||||
| __attribute__((noreturn)) void wd_fatal_error(int status, const char *message) { | ||||
|   GtkWindow *parent = gtk_application_get_active_window(GTK_APPLICATION(g_application_get_default())); | ||||
|   GtkWidget *dialog = gtk_message_dialog_new(parent, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", message); | ||||
| @@ -438,7 +441,11 @@ static void canvas_realize(GtkWidget *widget, gpointer data) { | ||||
|   } | ||||
|  | ||||
|   struct wd_state *state = data; | ||||
|   state->gl_data = wd_gl_setup(); | ||||
|  | ||||
|   GdkWindow *window = gtk_widget_get_window(widget); | ||||
|   GdkDisplay *display = gdk_window_get_display(window); | ||||
|   struct wl_display *wl_display = gdk_wayland_display_get_wl_display(display); | ||||
|   state->gl_data = wd_gl_setup(wl_display); | ||||
| } | ||||
|  | ||||
| static inline bool size_changed(const struct wd_render_head_data *render) { | ||||
| @@ -524,6 +531,7 @@ static void canvas_render(GtkGLArea *area, GdkGLContext *context, gpointer data) | ||||
|   uint64_t tick = gdk_frame_clock_get_frame_time(clock); | ||||
|  | ||||
|   wd_capture_frame(state); | ||||
|   state->render.external_images = state->export_manager != NULL; | ||||
|  | ||||
|   struct wd_head *head; | ||||
|   wl_list_for_each(head, &state->heads, link) { | ||||
| @@ -534,16 +542,28 @@ static void canvas_render(GtkGLArea *area, GdkGLContext *context, gpointer data) | ||||
|       frame = wl_container_of(output->frames.prev, frame, link); | ||||
|     } | ||||
|     if (render != NULL) { | ||||
|       if (state->capture && frame != NULL && frame->pixels != NULL) { | ||||
|       if (state->capture && frame != NULL && frame->ready) { | ||||
|         if (frame->tick > render->updated_at) { | ||||
|           render->tex_stride = frame->stride; | ||||
|           render->tex_width = frame->width; | ||||
|           render->tex_height = frame->height; | ||||
|           render->pixels = frame->pixels; | ||||
|           if (state->render.external_images) { | ||||
|             render->pixels = NULL; | ||||
|             render->image = wd_gl_create_dmabuf_texture(state->gl_data, frame); | ||||
|             render->has_alpha = TRUE; | ||||
|             render->swap_rgb = TRUE; | ||||
|           } else { | ||||
|             render->pixels = frame->pixels; | ||||
|             render->image = NULL; | ||||
|             render->tex_stride = frame->strides[0]; | ||||
|             render->swap_rgb = frame->format == WL_SHM_FORMAT_ABGR8888 | ||||
|               || frame->format == WL_SHM_FORMAT_XBGR8888; | ||||
|             render->has_alpha = frame->format == WL_SHM_FORMAT_ARGB8888 | ||||
|               || frame->format == WL_SHM_FORMAT_ABGR8888; | ||||
|           } | ||||
|           render->y_invert = frame->flags & | ||||
|             ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT; | ||||
|           render->preview = TRUE; | ||||
|           render->updated_at = tick; | ||||
|           render->y_invert = frame->y_invert; | ||||
|           render->swap_rgb = frame->swap_rgb; | ||||
|         } | ||||
|         if (render->preview) { | ||||
|           render->active.rotation = render->queued.rotation; | ||||
| @@ -560,10 +580,12 @@ static void canvas_render(GtkGLArea *area, GdkGLContext *context, gpointer data) | ||||
|         head->surface = draw_head(pango, &state->render, head->name, | ||||
|             render->tex_width, render->tex_height); | ||||
|         render->pixels = cairo_image_surface_get_data(head->surface); | ||||
|         render->image = NULL; | ||||
|         render->tex_stride = cairo_image_surface_get_stride(head->surface); | ||||
|         render->updated_at = tick; | ||||
|         render->active.rotation = 0; | ||||
|         render->active.x_invert = FALSE; | ||||
|         render->has_alpha = FALSE; | ||||
|         render->y_invert = FALSE; | ||||
|         render->swap_rgb = FALSE; | ||||
|       } | ||||
| @@ -1054,7 +1076,7 @@ static void activate(GtkApplication* app, gpointer user_data) { | ||||
|   if (state->xdg_output_manager == NULL) { | ||||
|     wd_fatal_error(1, "Compositor doesn't support xdg-output-unstable-v1"); | ||||
|   } | ||||
|   if (state->copy_manager == NULL) { | ||||
|   if (state->copy_manager == NULL && state->export_manager == NULL) { | ||||
|     state->capture = FALSE; | ||||
|     g_simple_action_set_state(capture_action, g_variant_new_boolean(state->capture)); | ||||
|     g_simple_action_set_enabled(capture_action, FALSE); | ||||
|   | ||||
| @@ -8,6 +8,7 @@ gdk = dependency('gdk-3.0', version: '>= 3.24') | ||||
| gtk = dependency('gtk+-3.0', version: '>= 3.24') | ||||
| assert(gdk.get_pkgconfig_variable('targets').split().contains('wayland'), 'Wayland GDK backend not present') | ||||
| epoxy = dependency('epoxy') | ||||
| libdrm = dependency('libdrm') | ||||
|  | ||||
| configure_file(input: 'config.h.in', output: 'config.h', configuration: conf) | ||||
|  | ||||
| @@ -28,6 +29,7 @@ executable( | ||||
|     wayland_client, | ||||
|     client_protos, | ||||
|     epoxy, | ||||
|     libdrm, | ||||
|     gtk | ||||
|   ], | ||||
|   install: true | ||||
|   | ||||
							
								
								
									
										194
									
								
								src/outputs.c
									
									
									
									
									
								
							
							
						
						
									
										194
									
								
								src/outputs.c
									
									
									
									
									
								
							| @@ -25,6 +25,8 @@ | ||||
|  | ||||
| #include "wlr-output-management-unstable-v1-client-protocol.h" | ||||
| #include "xdg-output-unstable-v1-client-protocol.h" | ||||
| #include "linux-dmabuf-unstable-v1-client-protocol.h" | ||||
| #include "wlr-export-dmabuf-unstable-v1-client-protocol.h" | ||||
| #include "wlr-screencopy-unstable-v1-client-protocol.h" | ||||
| #include "wlr-layer-shell-unstable-v1-client-protocol.h" | ||||
|  | ||||
| @@ -143,15 +145,18 @@ void wd_apply_state(struct wd_state *state, struct wl_list *new_outputs, | ||||
|  | ||||
| static void wd_frame_destroy(struct wd_frame *frame) { | ||||
|   if (frame->pixels != NULL) | ||||
|     munmap(frame->pixels, frame->height * frame->stride); | ||||
|     munmap(frame->pixels, frame->height * frame->strides[0]); | ||||
|   if (frame->buffer != NULL) | ||||
|     wl_buffer_destroy(frame->buffer); | ||||
|   if (frame->pool != NULL) | ||||
|     wl_shm_pool_destroy(frame->pool); | ||||
|   if (frame->capture_fd != -1) | ||||
|     close(frame->capture_fd); | ||||
|   if (frame->wlr_frame != NULL) | ||||
|     zwlr_screencopy_frame_v1_destroy(frame->wlr_frame); | ||||
|   for (int i = 0; i < frame->n_planes; i++) | ||||
|     if (frame->fds[i]) | ||||
|       close(frame->fds[i]); | ||||
|   if (frame->copy_frame != NULL) | ||||
|     zwlr_screencopy_frame_v1_destroy(frame->copy_frame); | ||||
|   if (frame->export_frame != NULL) | ||||
|     zwlr_export_dmabuf_frame_v1_destroy(frame->export_frame); | ||||
|  | ||||
|   wl_list_remove(&frame->link); | ||||
|   free(frame); | ||||
| @@ -189,6 +194,112 @@ static int create_shm_file(size_t size, const char *fmt, ...) { | ||||
|   return fd; | ||||
| } | ||||
|  | ||||
| static void make_frame_current(struct wd_frame *frame, | ||||
|     uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { | ||||
|   uint64_t tv_sec = (uint64_t) tv_sec_hi << 32 | tv_sec_lo; | ||||
|   frame->tick = (tv_sec * 1000000) + (tv_nsec / 1000); | ||||
|  | ||||
|   struct wd_frame *frame_iter, *frame_tmp; | ||||
|   wl_list_for_each_safe(frame_iter, frame_tmp, &frame->output->frames, link) { | ||||
|     if (frame != frame_iter) { | ||||
|       wd_frame_destroy(frame_iter); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| #define WD_FRAME_COPY 0x100 | ||||
|  | ||||
| static void export_frame(void *data, | ||||
|     struct zwlr_export_dmabuf_frame_v1 *wlr_frame, | ||||
|     uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y, | ||||
|     uint32_t buffer_flags, uint32_t flags, uint32_t format, | ||||
|     uint32_t mod_high, uint32_t mod_low, uint32_t num_objects) { | ||||
|   struct wd_frame *frame = data; | ||||
|   frame->width = width; | ||||
|   frame->height = height; | ||||
|   frame->format = format; | ||||
|   frame->flags = buffer_flags; | ||||
| /* | ||||
|   if (flags & ZWLR_EXPORT_DMABUF_FRAME_V1_FLAGS_TRANSIENT) { | ||||
|     frame->flags |= WD_FRAME_COPY; | ||||
|   } | ||||
| */ | ||||
|   frame->modifier = ((uint64_t) mod_high << 32) | mod_low; | ||||
|   frame->n_planes = num_objects; | ||||
| } | ||||
|  | ||||
| static void export_object(void *data, | ||||
|     struct zwlr_export_dmabuf_frame_v1 *wlr_frame, | ||||
|     uint32_t index, int32_t fd, uint32_t size, uint32_t offset, | ||||
|     uint32_t stride, uint32_t plane_index) { | ||||
|   struct wd_frame *frame = data; | ||||
|  | ||||
|   if (frame->flags & WD_FRAME_COPY) { | ||||
|     void *src = NULL; | ||||
|     void *dest = NULL; | ||||
|     int wfd = create_shm_file(size, "/wd-%s", frame->output->name); | ||||
|     if (wfd == -1) { | ||||
|       goto done; | ||||
|     } | ||||
|     src = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, offset); | ||||
|     if (src == MAP_FAILED) { | ||||
|       fprintf(stderr, "mmap: %d: %s\n", fd, strerror(errno)); | ||||
|       close(wfd); | ||||
|       wfd = -1; | ||||
|       goto done; | ||||
|     } | ||||
|     dest = mmap(NULL, size - offset, PROT_READ | PROT_WRITE, | ||||
|         MAP_PRIVATE, wfd, 0); | ||||
|     if (dest == MAP_FAILED) { | ||||
|       fprintf(stderr, "mmap: %d: %s\n", wfd, strerror(errno)); | ||||
|       close(wfd); | ||||
|       wfd = -1; | ||||
|       goto done; | ||||
|     } | ||||
|     memcpy(dest, src, size - offset); | ||||
| done: | ||||
|     if (src) | ||||
|       munmap(src, size); | ||||
|     if (dest) | ||||
|       munmap(dest, size - offset); | ||||
|     close(fd); | ||||
|     frame->fds[plane_index] = wfd; | ||||
|   } else { | ||||
|     frame->fds[plane_index] = fd; | ||||
|     frame->offsets[plane_index] = offset; | ||||
|   } | ||||
|   frame->strides[plane_index] = stride; | ||||
| } | ||||
|  | ||||
| static void export_ready(void *data, | ||||
|     struct zwlr_export_dmabuf_frame_v1 *wlr_frame, | ||||
|     uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { | ||||
|   struct wd_frame *frame = data; | ||||
|  | ||||
|   make_frame_current(frame, tv_sec_hi, tv_sec_lo, tv_nsec); | ||||
|  | ||||
|   if (frame->flags & WD_FRAME_COPY) { | ||||
|     frame->flags ^= WD_FRAME_COPY; | ||||
|     zwlr_export_dmabuf_frame_v1_destroy(frame->export_frame); | ||||
|     frame->export_frame = NULL; | ||||
|   } | ||||
|   frame->ready = true; | ||||
| } | ||||
|  | ||||
| static void export_cancel(void *data, | ||||
|     struct zwlr_export_dmabuf_frame_v1 *wlr_frame, | ||||
|     uint32_t reason) { | ||||
|   struct wd_frame *frame = data; | ||||
|   wd_frame_destroy(frame); | ||||
| } | ||||
|  | ||||
| static struct zwlr_export_dmabuf_frame_v1_listener export_listener = { | ||||
|   .frame = export_frame, | ||||
|   .object = export_object, | ||||
|   .ready = export_ready, | ||||
|   .cancel = export_cancel | ||||
| }; | ||||
|  | ||||
| static void capture_buffer(void *data, | ||||
|     struct zwlr_screencopy_frame_v1 *copy_frame, | ||||
|     uint32_t format, uint32_t width, uint32_t height, uint32_t stride) { | ||||
| @@ -199,22 +310,23 @@ static void capture_buffer(void *data, | ||||
|     goto err; | ||||
|   } | ||||
|  | ||||
|   frame->n_planes = 1; | ||||
|   size_t size = stride * height; | ||||
|   frame->capture_fd = create_shm_file(size, "/wd-%s", frame->output->name); | ||||
|   if (frame->capture_fd == -1) { | ||||
|   frame->fds[0] = create_shm_file(size, "/wd-%s", frame->output->name); | ||||
|   if (frame->fds[0] == -1) { | ||||
|     goto err; | ||||
|   } | ||||
|  | ||||
|   frame->pool = wl_shm_create_pool(frame->output->state->shm, | ||||
|       frame->capture_fd, size); | ||||
|       frame->fds[0], size); | ||||
|   frame->buffer = wl_shm_pool_create_buffer(frame->pool, 0, | ||||
|       width, height, stride, format); | ||||
|   zwlr_screencopy_frame_v1_copy(copy_frame, frame->buffer); | ||||
|   frame->stride = stride; | ||||
|   frame->offsets[0] = 0; | ||||
|   frame->strides[0] = stride; | ||||
|   frame->width = width; | ||||
|   frame->height = height; | ||||
|   frame->swap_rgb = format == WL_SHM_FORMAT_ABGR8888 | ||||
|     || format == WL_SHM_FORMAT_XBGR8888; | ||||
|   frame->format = format; | ||||
|  | ||||
|   return; | ||||
| err: | ||||
| @@ -225,7 +337,9 @@ static void capture_flags(void *data, | ||||
|     struct zwlr_screencopy_frame_v1 *wlr_frame, | ||||
|     uint32_t flags) { | ||||
|   struct wd_frame *frame = data; | ||||
|   frame->y_invert = !!(flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT); | ||||
|   if (flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT) { | ||||
|     frame->flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT; | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void capture_ready(void *data, | ||||
| @@ -233,27 +347,20 @@ static void capture_ready(void *data, | ||||
|     uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { | ||||
|   struct wd_frame *frame = data; | ||||
|  | ||||
|   frame->pixels = mmap(NULL, frame->stride * frame->height, | ||||
|       PROT_READ, MAP_SHARED, frame->capture_fd, 0); | ||||
|   frame->pixels = mmap(NULL, frame->strides[0] * frame->height, | ||||
|       PROT_READ, MAP_SHARED, frame->fds[0], 0); | ||||
|   if (frame->pixels == MAP_FAILED) { | ||||
|     frame->pixels = NULL; | ||||
|     fprintf(stderr, "mmap: %d: %s\n", frame->capture_fd, strerror(errno)); | ||||
|     fprintf(stderr, "mmap: %d: %s\n", frame->fds[0], strerror(errno)); | ||||
|     wd_frame_destroy(frame); | ||||
|     return; | ||||
|   } else { | ||||
|     uint64_t tv_sec = (uint64_t) tv_sec_hi << 32 | tv_sec_lo; | ||||
|     frame->tick = (tv_sec * 1000000) + (tv_nsec / 1000); | ||||
|   } | ||||
|    | ||||
|   make_frame_current(frame, tv_sec_hi, tv_sec_lo, tv_nsec); | ||||
|  | ||||
|   zwlr_screencopy_frame_v1_destroy(frame->wlr_frame); | ||||
|   frame->wlr_frame = NULL; | ||||
|  | ||||
|   struct wd_frame *frame_iter, *frame_tmp; | ||||
|   wl_list_for_each_safe(frame_iter, frame_tmp, &frame->output->frames, link) { | ||||
|     if (frame != frame_iter) { | ||||
|       wd_frame_destroy(frame_iter); | ||||
|     } | ||||
|   } | ||||
|   zwlr_screencopy_frame_v1_destroy(frame->copy_frame); | ||||
|   frame->copy_frame = NULL; | ||||
|   frame->ready = true; | ||||
| } | ||||
|  | ||||
| static void capture_failed(void *data, | ||||
| @@ -274,7 +381,7 @@ static bool has_pending_captures(struct wd_state *state) { | ||||
|   wl_list_for_each(output, &state->outputs, link) { | ||||
|     struct wd_frame *frame; | ||||
|     wl_list_for_each(frame, &output->frames, link) { | ||||
|       if (frame->pixels == NULL) { | ||||
|       if (!frame->ready) { | ||||
|         return true; | ||||
|       } | ||||
|     } | ||||
| @@ -283,8 +390,8 @@ static bool has_pending_captures(struct wd_state *state) { | ||||
| } | ||||
|  | ||||
| void wd_capture_frame(struct wd_state *state) { | ||||
|   if (state->copy_manager == NULL || has_pending_captures(state) | ||||
|       || !state->capture) { | ||||
|   if ((state->export_manager == NULL && state->copy_manager == NULL) | ||||
|        || has_pending_captures(state) || !state->capture) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
| @@ -292,12 +399,21 @@ void wd_capture_frame(struct wd_state *state) { | ||||
|   wl_list_for_each(output, &state->outputs, link) { | ||||
|     struct wd_frame *frame = calloc(1, sizeof(*frame)); | ||||
|     frame->output = output; | ||||
|     frame->capture_fd = -1; | ||||
|     frame->wlr_frame = | ||||
|       zwlr_screencopy_manager_v1_capture_output(state->copy_manager, 1, | ||||
|         output->wl_output); | ||||
|     zwlr_screencopy_frame_v1_add_listener(frame->wlr_frame, &capture_listener, | ||||
|         frame); | ||||
|     for (int i = 0; i < WD_MAX_PLANES; i++) | ||||
|       frame->fds[i] = -1; | ||||
|     if (state->export_manager) { | ||||
|       frame->export_frame = | ||||
|         zwlr_export_dmabuf_manager_v1_capture_output(state->export_manager, 1, | ||||
|           output->wl_output); | ||||
|       zwlr_export_dmabuf_frame_v1_add_listener(frame->export_frame, | ||||
|           &export_listener, frame); | ||||
|     } else if (state->copy_manager) { | ||||
|       frame->copy_frame = | ||||
|         zwlr_screencopy_manager_v1_capture_output(state->copy_manager, 1, | ||||
|           output->wl_output); | ||||
|       zwlr_screencopy_frame_v1_add_listener(frame->copy_frame, | ||||
|           &capture_listener, frame); | ||||
|     } | ||||
|     wl_list_insert(&output->frames, &frame->link); | ||||
|   } | ||||
| } | ||||
| @@ -540,6 +656,9 @@ static void registry_handle_global(void *data, struct wl_registry *registry, | ||||
|   } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { | ||||
|     state->xdg_output_manager = wl_registry_bind(registry, name, | ||||
|         &zxdg_output_manager_v1_interface, version); | ||||
|   } else if(strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name) == 0) { | ||||
|     state->export_manager = wl_registry_bind(registry, name, | ||||
|         &zwlr_export_dmabuf_manager_v1_interface, version); | ||||
|   } else if(strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) { | ||||
|     state->copy_manager = wl_registry_bind(registry, name, | ||||
|         &zwlr_screencopy_manager_v1_interface, version); | ||||
| @@ -689,6 +808,9 @@ void wd_state_destroy(struct wd_state *state) { | ||||
|   if (state->layer_shell != NULL) { | ||||
|     zwlr_layer_shell_v1_destroy(state->layer_shell); | ||||
|   } | ||||
|   if (state->export_manager != NULL) { | ||||
|     zwlr_export_dmabuf_manager_v1_destroy(state->export_manager); | ||||
|   } | ||||
|   if (state->copy_manager != NULL) { | ||||
|     zwlr_screencopy_manager_v1_destroy(state->copy_manager); | ||||
|   } | ||||
|   | ||||
							
								
								
									
										233
									
								
								src/render.c
									
									
									
									
									
								
							
							
						
						
									
										233
									
								
								src/render.c
									
									
									
									
									
								
							| @@ -1,13 +1,24 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Jason Francis <jason@cycles.network> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| /* SPDX-FileCopyrightText: 2018 Simon Ser | ||||
|  * SPDX-FileCopyrightText: 2018 Guido Günther | ||||
|  * SPDX-License-Identifier: MIT */ | ||||
| /* Parts of this file are taken from swaywm/wlroots: | ||||
|  * https://github.com/swaywm/wlroots/blob/e97c2c3639119831ced4f6b9f704b096c2075973/render/egl.c | ||||
|  */ | ||||
|  | ||||
| #include "wdisplays.h" | ||||
|  | ||||
| #define WL_EGL_PLATFORM 1 | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <math.h> | ||||
| #include <epoxy/gl.h> | ||||
| #include <wayland-util.h> | ||||
| #include <epoxy/gl.h> | ||||
| #include <epoxy/egl.h> | ||||
| #include <drm_fourcc.h> | ||||
|  | ||||
| #define BT_UV_VERT_SIZE (2 + 2) | ||||
| #define BT_UV_QUAD_SIZE (6 * BT_UV_VERT_SIZE) | ||||
| @@ -29,22 +40,25 @@ enum gl_buffers { | ||||
|   NUM_BUFFERS | ||||
| }; | ||||
|  | ||||
| struct wd_gl_texture_program { | ||||
|   GLuint id; | ||||
|   GLuint position_attribute; | ||||
|   GLuint uv_attribute; | ||||
|   GLuint screen_size_uniform; | ||||
|   GLuint texture_uniform; | ||||
|   GLuint color_transform_uniform; | ||||
|   GLuint color_add_uniform; | ||||
| }; | ||||
|  | ||||
| struct wd_gl_data { | ||||
|   EGLDisplay display; | ||||
|  | ||||
|   GLuint color_program; | ||||
|   GLuint color_vertex_shader; | ||||
|   GLuint color_fragment_shader; | ||||
|   GLuint color_position_attribute; | ||||
|   GLuint color_color_attribute; | ||||
|   GLuint color_screen_size_uniform; | ||||
|  | ||||
|   GLuint texture_program; | ||||
|   GLuint texture_vertex_shader; | ||||
|   GLuint texture_fragment_shader; | ||||
|   GLuint texture_position_attribute; | ||||
|   GLuint texture_uv_attribute; | ||||
|   GLuint texture_screen_size_uniform; | ||||
|   GLuint texture_texture_uniform; | ||||
|   GLuint texture_color_transform_uniform; | ||||
|   struct wd_gl_texture_program rgb; | ||||
|  | ||||
|   GLuint buffers[NUM_BUFFERS]; | ||||
|  | ||||
| @@ -90,8 +104,9 @@ precision mediump float;\n\ | ||||
| varying vec2 uv_out;\n\ | ||||
| uniform sampler2D texture;\n\ | ||||
| uniform mat4 color_transform;\n\ | ||||
| uniform vec4 color_add;\n\ | ||||
| void main(void) {\n\ | ||||
|   gl_FragColor = texture2D(texture, uv_out) * color_transform;\n\ | ||||
|   gl_FragColor = texture2D(texture, uv_out) * color_transform + color_add;\n\ | ||||
| }"; | ||||
|  | ||||
| static GLuint gl_make_shader(GLenum type, const char *src) { | ||||
| @@ -142,18 +157,62 @@ static void gl_link_and_validate(GLint program) { | ||||
|   } | ||||
| } | ||||
|  | ||||
| struct wd_gl_data *wd_gl_setup(void) { | ||||
| static void setup_texture_program(struct wd_gl_texture_program *prog, | ||||
|   const char *fragment_src) { | ||||
|   prog->id = glCreateProgram(); | ||||
|  | ||||
|   GLuint vertex_shader = gl_make_shader(GL_VERTEX_SHADER, | ||||
|       texture_vertex_shader_src); | ||||
|   glAttachShader(prog->id, vertex_shader); | ||||
|   GLuint fragment_shader = gl_make_shader(GL_FRAGMENT_SHADER, fragment_src); | ||||
|   glAttachShader(prog->id, fragment_shader); | ||||
|   gl_link_and_validate(prog->id); | ||||
|  | ||||
|   glDeleteShader(fragment_shader); | ||||
|   glDeleteShader(vertex_shader); | ||||
|  | ||||
|   prog->position_attribute = glGetAttribLocation(prog->id, "position"); | ||||
|   prog->uv_attribute = glGetAttribLocation(prog->id, "uv"); | ||||
|   prog->screen_size_uniform = glGetUniformLocation(prog->id, "screen_size"); | ||||
|   prog->texture_uniform = glGetUniformLocation(prog->id, "texture"); | ||||
|   prog->color_transform_uniform = glGetUniformLocation(prog->id, | ||||
|       "color_transform"); | ||||
|   prog->color_add_uniform = glGetUniformLocation(prog->id, "color_add"); | ||||
| } | ||||
|  | ||||
| #define assert_ext(_name, _expr) do { \ | ||||
|   static bool _tested_##_name = false; \ | ||||
|   if (!_tested_##_name) { \ | ||||
|     _tested_##_name = true; \ | ||||
|     if (!(_expr)) \ | ||||
|       wd_fatal_error(1, "Extension " #_name " not found\n"); \ | ||||
|   } \ | ||||
| } while (0) | ||||
|  | ||||
| #define assert_gl_ext(_name) \ | ||||
|   assert_ext(_name, epoxy_has_gl_extension(#_name)) | ||||
|  | ||||
| #define assert_egl_ext(_res, _name) \ | ||||
|   assert_ext(_name, epoxy_has_egl_extension((_res)->display, #_name)) | ||||
|     | ||||
| struct wd_gl_data *wd_gl_setup(struct wl_display *display) { | ||||
|   struct wd_gl_data *res = calloc(1, sizeof(struct wd_gl_data)); | ||||
|   res->display = eglGetDisplay(display); | ||||
|   res->color_program = glCreateProgram(); | ||||
|  | ||||
|   res->color_vertex_shader = gl_make_shader(GL_VERTEX_SHADER, | ||||
|   GLuint vertex_shader = gl_make_shader(GL_VERTEX_SHADER, | ||||
|       color_vertex_shader_src); | ||||
|   glAttachShader(res->color_program, res->color_vertex_shader); | ||||
|   res->color_fragment_shader = gl_make_shader(GL_FRAGMENT_SHADER, | ||||
|   glAttachShader(res->color_program, vertex_shader); | ||||
|   GLuint fragment_shader = gl_make_shader(GL_FRAGMENT_SHADER, | ||||
|       color_fragment_shader_src); | ||||
|   glAttachShader(res->color_program, res->color_fragment_shader); | ||||
|   glAttachShader(res->color_program, fragment_shader); | ||||
|   gl_link_and_validate(res->color_program); | ||||
|  | ||||
|   glDeleteShader(fragment_shader); | ||||
|   glDeleteShader(vertex_shader); | ||||
|  | ||||
|   setup_texture_program(&res->rgb, texture_fragment_shader_src); | ||||
|  | ||||
|   res->color_position_attribute = glGetAttribLocation(res->color_program, | ||||
|       "position"); | ||||
|   res->color_color_attribute = glGetAttribLocation(res->color_program, | ||||
| @@ -161,27 +220,6 @@ struct wd_gl_data *wd_gl_setup(void) { | ||||
|   res->color_screen_size_uniform = glGetUniformLocation(res->color_program, | ||||
|       "screen_size"); | ||||
|  | ||||
|   res->texture_program = glCreateProgram(); | ||||
|  | ||||
|   res->texture_vertex_shader = gl_make_shader(GL_VERTEX_SHADER, | ||||
|       texture_vertex_shader_src); | ||||
|   glAttachShader(res->texture_program, res->texture_vertex_shader); | ||||
|   res->texture_fragment_shader = gl_make_shader(GL_FRAGMENT_SHADER, | ||||
|       texture_fragment_shader_src); | ||||
|   glAttachShader(res->texture_program, res->texture_fragment_shader); | ||||
|   gl_link_and_validate(res->texture_program); | ||||
|  | ||||
|   res->texture_position_attribute = glGetAttribLocation(res->texture_program, | ||||
|       "position"); | ||||
|   res->texture_uv_attribute = glGetAttribLocation(res->texture_program, | ||||
|       "uv"); | ||||
|   res->texture_screen_size_uniform = glGetUniformLocation(res->texture_program, | ||||
|       "screen_size"); | ||||
|   res->texture_texture_uniform = glGetUniformLocation(res->texture_program, | ||||
|       "texture"); | ||||
|   res->texture_color_transform_uniform = glGetUniformLocation( | ||||
|       res->texture_program, "color_transform"); | ||||
|  | ||||
|   glGenBuffers(NUM_BUFFERS, res->buffers); | ||||
|   glBindBuffer(GL_ARRAY_BUFFER, res->buffers[TEXTURE_BUFFER]); | ||||
|   glBufferData(GL_ARRAY_BUFFER, BT_UV_MAX * sizeof(float), | ||||
| @@ -198,6 +236,83 @@ struct wd_gl_data *wd_gl_setup(void) { | ||||
|   return res; | ||||
| } | ||||
|  | ||||
| EGLImageKHR wd_gl_create_dmabuf_texture(struct wd_gl_data *res, | ||||
|     struct wd_frame *frame) { | ||||
|   assert(!frame->pixels); | ||||
|   assert_egl_ext(res, EGL_KHR_image_base); | ||||
|   assert_egl_ext(res, EGL_EXT_image_dma_buf_import); | ||||
|  | ||||
|   bool has_modifier = frame->modifier != DRM_FORMAT_MOD_INVALID | ||||
|     && frame->modifier != DRM_FORMAT_MOD_LINEAR; | ||||
|  | ||||
|   if (has_modifier) { | ||||
|     assert_egl_ext(res, EGL_EXT_image_dma_buf_import_modifiers); | ||||
|   } | ||||
|  | ||||
|   unsigned int atti = 0; | ||||
|   EGLint attribs[50]; | ||||
|   attribs[atti++] = EGL_WIDTH; | ||||
|   attribs[atti++] = frame->width; | ||||
|   attribs[atti++] = EGL_HEIGHT; | ||||
|   attribs[atti++] = frame->height; | ||||
|   attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; | ||||
|   attribs[atti++] = frame->format; | ||||
|  | ||||
|   struct { | ||||
|     EGLint fd; | ||||
|     EGLint offset; | ||||
|     EGLint pitch; | ||||
|     EGLint mod_lo; | ||||
|     EGLint mod_hi; | ||||
|   } attr_names[WD_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 < frame->n_planes; i++) { | ||||
|     attribs[atti++] = attr_names[i].fd; | ||||
|     attribs[atti++] = frame->fds[i]; | ||||
|     attribs[atti++] = attr_names[i].offset; | ||||
|     attribs[atti++] = frame->offsets[i]; | ||||
|     attribs[atti++] = attr_names[i].pitch; | ||||
|     attribs[atti++] = frame->strides[i]; | ||||
|     if (has_modifier) { | ||||
|       attribs[atti++] = attr_names[i].mod_lo; | ||||
|       attribs[atti++] = frame->modifier & 0xFFFFFFFF; | ||||
|       attribs[atti++] = attr_names[i].mod_hi; | ||||
|       attribs[atti++] = frame->modifier >> 32; | ||||
|     } | ||||
|   } | ||||
|   attribs[atti++] = EGL_NONE; | ||||
|   assert(atti < sizeof(attribs)/sizeof(attribs[0])); | ||||
|  | ||||
|   return eglCreateImageKHR(res->display, EGL_NO_CONTEXT, | ||||
|       EGL_LINUX_DMA_BUF_EXT, NULL, attribs); | ||||
| } | ||||
|  | ||||
| static const GLfloat TRANSFORM_RGB[16] = { | ||||
|   1, 0, 0, 0, | ||||
|   0, 1, 0, 0, | ||||
| @@ -320,34 +435,42 @@ void wd_gl_render(struct wd_gl_data *res, struct wd_render_data *info, | ||||
|   float screen_size[2] = { info->viewport_width, info->viewport_height }; | ||||
|  | ||||
|   if (tri_verts > 0) { | ||||
|     glUseProgram(res->texture_program); | ||||
|     glUseProgram(res->rgb.id); | ||||
|     glBindBuffer(GL_ARRAY_BUFFER, res->buffers[TEXTURE_BUFFER]); | ||||
|     glBufferSubData(GL_ARRAY_BUFFER, 0, | ||||
|         tri_verts * BT_UV_VERT_SIZE * sizeof(float), res->verts); | ||||
|     glEnableVertexAttribArray(res->texture_position_attribute); | ||||
|     glEnableVertexAttribArray(res->texture_uv_attribute); | ||||
|     glVertexAttribPointer(res->texture_position_attribute, | ||||
|     glEnableVertexAttribArray(res->rgb.position_attribute); | ||||
|     glEnableVertexAttribArray(res->rgb.uv_attribute); | ||||
|     glVertexAttribPointer(res->rgb.position_attribute, | ||||
|         2, GL_FLOAT, GL_FALSE, | ||||
|         BT_UV_VERT_SIZE * sizeof(float), (void *) (0 * sizeof(float))); | ||||
|     glVertexAttribPointer(res->texture_uv_attribute, 2, GL_FLOAT, GL_FALSE, | ||||
|     glVertexAttribPointer(res->rgb.uv_attribute, 2, GL_FLOAT, GL_FALSE, | ||||
|         BT_UV_VERT_SIZE * sizeof(float), (void *) (2 * sizeof(float))); | ||||
|     glUniform2fv(res->texture_screen_size_uniform, 1, screen_size); | ||||
|     glUniform1i(res->texture_texture_uniform, 0); | ||||
|     glUniform2fv(res->rgb.screen_size_uniform, 1, screen_size); | ||||
|     glUniform1i(res->rgb.texture_uniform, 0); | ||||
|     glActiveTexture(GL_TEXTURE0); | ||||
|  | ||||
|     i = 0; | ||||
|     wl_list_for_each_reverse(head, &info->heads, link) { | ||||
|       glBindTexture(GL_TEXTURE_2D, res->textures[i]); | ||||
|       if (head->updated_at == tick) { | ||||
|         glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, head->tex_stride / 4); | ||||
|         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | ||||
|             head->tex_width, head->tex_height, | ||||
|             0, GL_RGBA, GL_UNSIGNED_BYTE, head->pixels); | ||||
|         glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0); | ||||
|         if (head->image) { | ||||
|           assert_gl_ext(GL_OES_EGL_image); | ||||
|           glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, head->image); | ||||
|         } else if (head->pixels) { | ||||
|           assert_gl_ext(GL_EXT_unpack_subimage); | ||||
|           glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, head->tex_stride / 4); | ||||
|           glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, | ||||
|               head->tex_width, head->tex_height, | ||||
|               0, GL_RGBA, GL_UNSIGNED_BYTE, head->pixels); | ||||
|           glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0); | ||||
|         } | ||||
|         glGenerateMipmap(GL_TEXTURE_2D); | ||||
|       } | ||||
|       glUniformMatrix4fv(res->texture_color_transform_uniform, 1, GL_FALSE, | ||||
|       glUniformMatrix4fv(res->rgb.color_transform_uniform, 1, GL_FALSE, | ||||
|         head->swap_rgb ? TRANSFORM_RGB : TRANSFORM_BGR); | ||||
|       float color_add[4] = { 0.f, 0.f, 0.f, head->has_alpha ? 0.f : 1.f }; | ||||
|       glUniform4fv(res->rgb.color_add_uniform, 1, color_add); | ||||
|       glDrawArrays(GL_TRIANGLES, i * 6, 6); | ||||
|       i++; | ||||
|       if (i >= HEADS_MAX) | ||||
| @@ -514,13 +637,9 @@ void wd_gl_render(struct wd_gl_data *res, struct wd_render_data *info, | ||||
|  | ||||
| void wd_gl_cleanup(struct wd_gl_data *res) { | ||||
|   glDeleteBuffers(NUM_BUFFERS, res->buffers); | ||||
|   glDeleteShader(res->texture_fragment_shader); | ||||
|   glDeleteShader(res->texture_vertex_shader); | ||||
|   glDeleteProgram(res->texture_program); | ||||
|  | ||||
|   glDeleteShader(res->color_fragment_shader); | ||||
|   glDeleteShader(res->color_vertex_shader); | ||||
|   glDeleteProgram(res->color_program); | ||||
|  | ||||
|   glDeleteProgram(res->rgb.id); | ||||
|  | ||||
|   free(res); | ||||
| } | ||||
|   | ||||
| @@ -27,6 +27,8 @@ struct zxdg_output_manager_v1; | ||||
| struct zwlr_output_mode_v1; | ||||
| struct zwlr_output_head_v1; | ||||
| struct zwlr_output_manager_v1; | ||||
| struct zwlr_export_dmabuf_manager_v1; | ||||
| struct zwlr_export_dmabuf_frame_v1; | ||||
| struct zwlr_screencopy_manager_v1; | ||||
| struct zwlr_screencopy_frame_v1; | ||||
| struct zwlr_layer_shell_v1; | ||||
| @@ -41,6 +43,8 @@ typedef struct _GdkCursor GdkCursor; | ||||
| struct _cairo_surface; | ||||
| typedef struct _cairo_surface cairo_surface_t; | ||||
|  | ||||
| typedef void *EGLImageKHR; | ||||
|  | ||||
| struct wd_output { | ||||
|   struct wd_state *state; | ||||
|   struct zxdg_output_v1 *xdg_output; | ||||
| @@ -53,21 +57,30 @@ struct wd_output { | ||||
|   struct zwlr_layer_surface_v1 *overlay_layer_surface; | ||||
| }; | ||||
|  | ||||
| #define WD_MAX_PLANES 4 | ||||
|  | ||||
| struct wd_frame { | ||||
|   struct wd_output *output; | ||||
|   struct zwlr_screencopy_frame_v1 *wlr_frame; | ||||
|   struct zwlr_export_dmabuf_frame_v1 *export_frame; | ||||
|   struct zwlr_screencopy_frame_v1 *copy_frame; | ||||
|  | ||||
|   struct wl_list link; | ||||
|   int capture_fd; | ||||
|   unsigned stride; | ||||
|  | ||||
|   unsigned width; | ||||
|   unsigned height; | ||||
|   uint64_t tick; | ||||
|  | ||||
|   uint32_t format; | ||||
|   uint32_t flags; | ||||
|   uint64_t modifier; | ||||
|   uint32_t n_planes; | ||||
|   int fds[WD_MAX_PLANES]; | ||||
|   uint32_t offsets[WD_MAX_PLANES]; | ||||
|   uint32_t strides[WD_MAX_PLANES]; | ||||
|   struct wl_shm_pool *pool; | ||||
|   struct wl_buffer *buffer; | ||||
|   uint8_t *pixels; | ||||
|   uint64_t tick; | ||||
|   bool y_invert; | ||||
|   bool swap_rgb; | ||||
|   bool ready; | ||||
| }; | ||||
|  | ||||
| struct wd_head_config { | ||||
| @@ -141,15 +154,17 @@ struct wd_render_head_data { | ||||
|   struct wd_render_head_flags active; | ||||
|  | ||||
|   uint8_t *pixels; | ||||
|   EGLImageKHR image; | ||||
|   unsigned tex_stride; | ||||
|   unsigned tex_width; | ||||
|   unsigned tex_height; | ||||
|  | ||||
|   bool preview; | ||||
|   bool y_invert; | ||||
|   bool swap_rgb; | ||||
|   bool hovered; | ||||
|   bool clicked; | ||||
|   uint32_t preview : 1, | ||||
|            y_invert : 1, | ||||
|            swap_rgb : 1, | ||||
|            has_alpha : 1, | ||||
|            hovered : 1, | ||||
|            clicked : 1; | ||||
| }; | ||||
|  | ||||
| struct wd_render_data { | ||||
| @@ -166,6 +181,7 @@ struct wd_render_data { | ||||
|   int x_origin; | ||||
|   int y_origin; | ||||
|   uint64_t updated_at; | ||||
|   bool external_images; | ||||
|  | ||||
|   struct wl_list heads; | ||||
| }; | ||||
| @@ -178,6 +194,7 @@ struct wd_point { | ||||
| struct wd_state { | ||||
|   struct zxdg_output_manager_v1 *xdg_output_manager; | ||||
|   struct zwlr_output_manager_v1 *output_manager; | ||||
|   struct zwlr_export_dmabuf_manager_v1 *export_manager; | ||||
|   struct zwlr_screencopy_manager_v1 *copy_manager; | ||||
|   struct zwlr_layer_shell_v1 *layer_shell; | ||||
|   struct wl_shm *shm; | ||||
| @@ -311,8 +328,14 @@ void wd_ui_show_error(struct wd_state *state, const char *message); | ||||
| /* | ||||
|  * Compiles the GL shaders. | ||||
|  */ | ||||
| struct wd_gl_data *wd_gl_setup(void); | ||||
| struct wd_gl_data *wd_gl_setup(struct wl_display *display); | ||||
|  | ||||
| /* | ||||
|  * Creates an EGLImage from a given output frame. The frame must have a dmabuf | ||||
|  * received from a wlr_export_dmabuf_frame. | ||||
|  */ | ||||
| EGLImageKHR wd_gl_create_dmabuf_texture(struct wd_gl_data *res, | ||||
|     struct wd_frame *frame); | ||||
| /* | ||||
|  * Renders the GL scene. | ||||
|  */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user