Compare commits
1 Commits
master
...
export-dma
Author | SHA1 | Date | |
---|---|---|---|
|
3ac3f6cab2 |
@ -31,6 +31,7 @@ Build requirements are:
|
|||||||
|
|
||||||
- meson
|
- meson
|
||||||
- GTK+3
|
- GTK+3
|
||||||
|
- libdrm
|
||||||
- epoxy
|
- epoxy
|
||||||
- wayland-client
|
- wayland-client
|
||||||
|
|
||||||
|
@ -21,9 +21,11 @@ wayland_scanner_client = generator(
|
|||||||
|
|
||||||
client_protocols = [
|
client_protocols = [
|
||||||
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
[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'],
|
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
|
||||||
['wlr-output-management-unstable-v1.xml'],
|
['wlr-output-management-unstable-v1.xml'],
|
||||||
['wlr-screencopy-unstable-v1.xml'],
|
['wlr-screencopy-unstable-v1.xml'],
|
||||||
|
['wlr-export-dmabuf-unstable-v1.xml'],
|
||||||
['wlr-layer-shell-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 <gtk/gtk.h>
|
||||||
#include <gdk/gdkwayland.h>
|
#include <gdk/gdkwayland.h>
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
|
||||||
#include "wdisplays.h"
|
#include "wdisplays.h"
|
||||||
#include "glviewport.h"
|
#include "glviewport.h"
|
||||||
#include "headform.h"
|
#include "headform.h"
|
||||||
|
|
||||||
|
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
__attribute__((noreturn)) void wd_fatal_error(int status, const char *message) {
|
__attribute__((noreturn)) void wd_fatal_error(int status, const char *message) {
|
||||||
GtkWindow *parent = gtk_application_get_active_window(GTK_APPLICATION(g_application_get_default()));
|
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);
|
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;
|
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) {
|
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);
|
uint64_t tick = gdk_frame_clock_get_frame_time(clock);
|
||||||
|
|
||||||
wd_capture_frame(state);
|
wd_capture_frame(state);
|
||||||
|
state->render.external_images = state->export_manager != NULL;
|
||||||
|
|
||||||
struct wd_head *head;
|
struct wd_head *head;
|
||||||
wl_list_for_each(head, &state->heads, link) {
|
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);
|
frame = wl_container_of(output->frames.prev, frame, link);
|
||||||
}
|
}
|
||||||
if (render != NULL) {
|
if (render != NULL) {
|
||||||
if (state->capture && frame != NULL && frame->pixels != NULL) {
|
if (state->capture && frame != NULL && frame->ready) {
|
||||||
if (frame->tick > render->updated_at) {
|
if (frame->tick > render->updated_at) {
|
||||||
render->tex_stride = frame->stride;
|
|
||||||
render->tex_width = frame->width;
|
render->tex_width = frame->width;
|
||||||
render->tex_height = frame->height;
|
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->preview = TRUE;
|
||||||
render->updated_at = tick;
|
render->updated_at = tick;
|
||||||
render->y_invert = frame->y_invert;
|
|
||||||
render->swap_rgb = frame->swap_rgb;
|
|
||||||
}
|
}
|
||||||
if (render->preview) {
|
if (render->preview) {
|
||||||
render->active.rotation = render->queued.rotation;
|
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,
|
head->surface = draw_head(pango, &state->render, head->name,
|
||||||
render->tex_width, render->tex_height);
|
render->tex_width, render->tex_height);
|
||||||
render->pixels = cairo_image_surface_get_data(head->surface);
|
render->pixels = cairo_image_surface_get_data(head->surface);
|
||||||
|
render->image = NULL;
|
||||||
render->tex_stride = cairo_image_surface_get_stride(head->surface);
|
render->tex_stride = cairo_image_surface_get_stride(head->surface);
|
||||||
render->updated_at = tick;
|
render->updated_at = tick;
|
||||||
render->active.rotation = 0;
|
render->active.rotation = 0;
|
||||||
render->active.x_invert = FALSE;
|
render->active.x_invert = FALSE;
|
||||||
|
render->has_alpha = FALSE;
|
||||||
render->y_invert = FALSE;
|
render->y_invert = FALSE;
|
||||||
render->swap_rgb = FALSE;
|
render->swap_rgb = FALSE;
|
||||||
}
|
}
|
||||||
@ -1054,7 +1076,7 @@ static void activate(GtkApplication* app, gpointer user_data) {
|
|||||||
if (state->xdg_output_manager == NULL) {
|
if (state->xdg_output_manager == NULL) {
|
||||||
wd_fatal_error(1, "Compositor doesn't support xdg-output-unstable-v1");
|
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;
|
state->capture = FALSE;
|
||||||
g_simple_action_set_state(capture_action, g_variant_new_boolean(state->capture));
|
g_simple_action_set_state(capture_action, g_variant_new_boolean(state->capture));
|
||||||
g_simple_action_set_enabled(capture_action, FALSE);
|
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')
|
gtk = dependency('gtk+-3.0', version: '>= 3.24')
|
||||||
assert(gdk.get_pkgconfig_variable('targets').split().contains('wayland'), 'Wayland GDK backend not present')
|
assert(gdk.get_pkgconfig_variable('targets').split().contains('wayland'), 'Wayland GDK backend not present')
|
||||||
epoxy = dependency('epoxy')
|
epoxy = dependency('epoxy')
|
||||||
|
libdrm = dependency('libdrm')
|
||||||
|
|
||||||
configure_file(input: 'config.h.in', output: 'config.h', configuration: conf)
|
configure_file(input: 'config.h.in', output: 'config.h', configuration: conf)
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ executable(
|
|||||||
wayland_client,
|
wayland_client,
|
||||||
client_protos,
|
client_protos,
|
||||||
epoxy,
|
epoxy,
|
||||||
|
libdrm,
|
||||||
gtk
|
gtk
|
||||||
],
|
],
|
||||||
install: true
|
install: true
|
||||||
|
192
src/outputs.c
192
src/outputs.c
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
#include "wlr-output-management-unstable-v1-client-protocol.h"
|
#include "wlr-output-management-unstable-v1-client-protocol.h"
|
||||||
#include "xdg-output-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-screencopy-unstable-v1-client-protocol.h"
|
||||||
#include "wlr-layer-shell-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) {
|
static void wd_frame_destroy(struct wd_frame *frame) {
|
||||||
if (frame->pixels != NULL)
|
if (frame->pixels != NULL)
|
||||||
munmap(frame->pixels, frame->height * frame->stride);
|
munmap(frame->pixels, frame->height * frame->strides[0]);
|
||||||
if (frame->buffer != NULL)
|
if (frame->buffer != NULL)
|
||||||
wl_buffer_destroy(frame->buffer);
|
wl_buffer_destroy(frame->buffer);
|
||||||
if (frame->pool != NULL)
|
if (frame->pool != NULL)
|
||||||
wl_shm_pool_destroy(frame->pool);
|
wl_shm_pool_destroy(frame->pool);
|
||||||
if (frame->capture_fd != -1)
|
for (int i = 0; i < frame->n_planes; i++)
|
||||||
close(frame->capture_fd);
|
if (frame->fds[i])
|
||||||
if (frame->wlr_frame != NULL)
|
close(frame->fds[i]);
|
||||||
zwlr_screencopy_frame_v1_destroy(frame->wlr_frame);
|
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);
|
wl_list_remove(&frame->link);
|
||||||
free(frame);
|
free(frame);
|
||||||
@ -189,6 +194,112 @@ static int create_shm_file(size_t size, const char *fmt, ...) {
|
|||||||
return fd;
|
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,
|
static void capture_buffer(void *data,
|
||||||
struct zwlr_screencopy_frame_v1 *copy_frame,
|
struct zwlr_screencopy_frame_v1 *copy_frame,
|
||||||
uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
|
uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
|
||||||
@ -199,22 +310,23 @@ static void capture_buffer(void *data,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frame->n_planes = 1;
|
||||||
size_t size = stride * height;
|
size_t size = stride * height;
|
||||||
frame->capture_fd = create_shm_file(size, "/wd-%s", frame->output->name);
|
frame->fds[0] = create_shm_file(size, "/wd-%s", frame->output->name);
|
||||||
if (frame->capture_fd == -1) {
|
if (frame->fds[0] == -1) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame->pool = wl_shm_create_pool(frame->output->state->shm,
|
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,
|
frame->buffer = wl_shm_pool_create_buffer(frame->pool, 0,
|
||||||
width, height, stride, format);
|
width, height, stride, format);
|
||||||
zwlr_screencopy_frame_v1_copy(copy_frame, frame->buffer);
|
zwlr_screencopy_frame_v1_copy(copy_frame, frame->buffer);
|
||||||
frame->stride = stride;
|
frame->offsets[0] = 0;
|
||||||
|
frame->strides[0] = stride;
|
||||||
frame->width = width;
|
frame->width = width;
|
||||||
frame->height = height;
|
frame->height = height;
|
||||||
frame->swap_rgb = format == WL_SHM_FORMAT_ABGR8888
|
frame->format = format;
|
||||||
|| format == WL_SHM_FORMAT_XBGR8888;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
err:
|
err:
|
||||||
@ -225,7 +337,9 @@ static void capture_flags(void *data,
|
|||||||
struct zwlr_screencopy_frame_v1 *wlr_frame,
|
struct zwlr_screencopy_frame_v1 *wlr_frame,
|
||||||
uint32_t flags) {
|
uint32_t flags) {
|
||||||
struct wd_frame *frame = data;
|
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,
|
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) {
|
uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
|
||||||
struct wd_frame *frame = data;
|
struct wd_frame *frame = data;
|
||||||
|
|
||||||
frame->pixels = mmap(NULL, frame->stride * frame->height,
|
frame->pixels = mmap(NULL, frame->strides[0] * frame->height,
|
||||||
PROT_READ, MAP_SHARED, frame->capture_fd, 0);
|
PROT_READ, MAP_SHARED, frame->fds[0], 0);
|
||||||
if (frame->pixels == MAP_FAILED) {
|
if (frame->pixels == MAP_FAILED) {
|
||||||
frame->pixels = NULL;
|
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);
|
wd_frame_destroy(frame);
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
uint64_t tv_sec = (uint64_t) tv_sec_hi << 32 | tv_sec_lo;
|
|
||||||
frame->tick = (tv_sec * 1000000) + (tv_nsec / 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
zwlr_screencopy_frame_v1_destroy(frame->wlr_frame);
|
make_frame_current(frame, tv_sec_hi, tv_sec_lo, tv_nsec);
|
||||||
frame->wlr_frame = NULL;
|
|
||||||
|
|
||||||
struct wd_frame *frame_iter, *frame_tmp;
|
zwlr_screencopy_frame_v1_destroy(frame->copy_frame);
|
||||||
wl_list_for_each_safe(frame_iter, frame_tmp, &frame->output->frames, link) {
|
frame->copy_frame = NULL;
|
||||||
if (frame != frame_iter) {
|
frame->ready = true;
|
||||||
wd_frame_destroy(frame_iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void capture_failed(void *data,
|
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) {
|
wl_list_for_each(output, &state->outputs, link) {
|
||||||
struct wd_frame *frame;
|
struct wd_frame *frame;
|
||||||
wl_list_for_each(frame, &output->frames, link) {
|
wl_list_for_each(frame, &output->frames, link) {
|
||||||
if (frame->pixels == NULL) {
|
if (!frame->ready) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -283,8 +390,8 @@ static bool has_pending_captures(struct wd_state *state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void wd_capture_frame(struct wd_state *state) {
|
void wd_capture_frame(struct wd_state *state) {
|
||||||
if (state->copy_manager == NULL || has_pending_captures(state)
|
if ((state->export_manager == NULL && state->copy_manager == NULL)
|
||||||
|| !state->capture) {
|
|| has_pending_captures(state) || !state->capture) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,12 +399,21 @@ void wd_capture_frame(struct wd_state *state) {
|
|||||||
wl_list_for_each(output, &state->outputs, link) {
|
wl_list_for_each(output, &state->outputs, link) {
|
||||||
struct wd_frame *frame = calloc(1, sizeof(*frame));
|
struct wd_frame *frame = calloc(1, sizeof(*frame));
|
||||||
frame->output = output;
|
frame->output = output;
|
||||||
frame->capture_fd = -1;
|
for (int i = 0; i < WD_MAX_PLANES; i++)
|
||||||
frame->wlr_frame =
|
frame->fds[i] = -1;
|
||||||
zwlr_screencopy_manager_v1_capture_output(state->copy_manager, 1,
|
if (state->export_manager) {
|
||||||
output->wl_output);
|
frame->export_frame =
|
||||||
zwlr_screencopy_frame_v1_add_listener(frame->wlr_frame, &capture_listener,
|
zwlr_export_dmabuf_manager_v1_capture_output(state->export_manager, 1,
|
||||||
frame);
|
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);
|
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) {
|
} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
|
||||||
state->xdg_output_manager = wl_registry_bind(registry, name,
|
state->xdg_output_manager = wl_registry_bind(registry, name,
|
||||||
&zxdg_output_manager_v1_interface, version);
|
&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) {
|
} else if(strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
|
||||||
state->copy_manager = wl_registry_bind(registry, name,
|
state->copy_manager = wl_registry_bind(registry, name,
|
||||||
&zwlr_screencopy_manager_v1_interface, version);
|
&zwlr_screencopy_manager_v1_interface, version);
|
||||||
@ -689,6 +808,9 @@ void wd_state_destroy(struct wd_state *state) {
|
|||||||
if (state->layer_shell != NULL) {
|
if (state->layer_shell != NULL) {
|
||||||
zwlr_layer_shell_v1_destroy(state->layer_shell);
|
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) {
|
if (state->copy_manager != NULL) {
|
||||||
zwlr_screencopy_manager_v1_destroy(state->copy_manager);
|
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-FileCopyrightText: 2020 Jason Francis <jason@cycles.network>
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
* 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"
|
#include "wdisplays.h"
|
||||||
|
|
||||||
|
#define WL_EGL_PLATFORM 1
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <epoxy/gl.h>
|
|
||||||
#include <wayland-util.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_VERT_SIZE (2 + 2)
|
||||||
#define BT_UV_QUAD_SIZE (6 * BT_UV_VERT_SIZE)
|
#define BT_UV_QUAD_SIZE (6 * BT_UV_VERT_SIZE)
|
||||||
@ -29,22 +40,25 @@ enum gl_buffers {
|
|||||||
NUM_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 {
|
struct wd_gl_data {
|
||||||
|
EGLDisplay display;
|
||||||
|
|
||||||
GLuint color_program;
|
GLuint color_program;
|
||||||
GLuint color_vertex_shader;
|
|
||||||
GLuint color_fragment_shader;
|
|
||||||
GLuint color_position_attribute;
|
GLuint color_position_attribute;
|
||||||
GLuint color_color_attribute;
|
GLuint color_color_attribute;
|
||||||
GLuint color_screen_size_uniform;
|
GLuint color_screen_size_uniform;
|
||||||
|
|
||||||
GLuint texture_program;
|
struct wd_gl_texture_program rgb;
|
||||||
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;
|
|
||||||
|
|
||||||
GLuint buffers[NUM_BUFFERS];
|
GLuint buffers[NUM_BUFFERS];
|
||||||
|
|
||||||
@ -90,8 +104,9 @@ precision mediump float;\n\
|
|||||||
varying vec2 uv_out;\n\
|
varying vec2 uv_out;\n\
|
||||||
uniform sampler2D texture;\n\
|
uniform sampler2D texture;\n\
|
||||||
uniform mat4 color_transform;\n\
|
uniform mat4 color_transform;\n\
|
||||||
|
uniform vec4 color_add;\n\
|
||||||
void main(void) {\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) {
|
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));
|
struct wd_gl_data *res = calloc(1, sizeof(struct wd_gl_data));
|
||||||
|
res->display = eglGetDisplay(display);
|
||||||
res->color_program = glCreateProgram();
|
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);
|
color_vertex_shader_src);
|
||||||
glAttachShader(res->color_program, res->color_vertex_shader);
|
glAttachShader(res->color_program, vertex_shader);
|
||||||
res->color_fragment_shader = gl_make_shader(GL_FRAGMENT_SHADER,
|
GLuint fragment_shader = gl_make_shader(GL_FRAGMENT_SHADER,
|
||||||
color_fragment_shader_src);
|
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);
|
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,
|
res->color_position_attribute = glGetAttribLocation(res->color_program,
|
||||||
"position");
|
"position");
|
||||||
res->color_color_attribute = glGetAttribLocation(res->color_program,
|
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,
|
res->color_screen_size_uniform = glGetUniformLocation(res->color_program,
|
||||||
"screen_size");
|
"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);
|
glGenBuffers(NUM_BUFFERS, res->buffers);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, res->buffers[TEXTURE_BUFFER]);
|
glBindBuffer(GL_ARRAY_BUFFER, res->buffers[TEXTURE_BUFFER]);
|
||||||
glBufferData(GL_ARRAY_BUFFER, BT_UV_MAX * sizeof(float),
|
glBufferData(GL_ARRAY_BUFFER, BT_UV_MAX * sizeof(float),
|
||||||
@ -198,6 +236,83 @@ struct wd_gl_data *wd_gl_setup(void) {
|
|||||||
return res;
|
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] = {
|
static const GLfloat TRANSFORM_RGB[16] = {
|
||||||
1, 0, 0, 0,
|
1, 0, 0, 0,
|
||||||
0, 1, 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 };
|
float screen_size[2] = { info->viewport_width, info->viewport_height };
|
||||||
|
|
||||||
if (tri_verts > 0) {
|
if (tri_verts > 0) {
|
||||||
glUseProgram(res->texture_program);
|
glUseProgram(res->rgb.id);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, res->buffers[TEXTURE_BUFFER]);
|
glBindBuffer(GL_ARRAY_BUFFER, res->buffers[TEXTURE_BUFFER]);
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, 0,
|
glBufferSubData(GL_ARRAY_BUFFER, 0,
|
||||||
tri_verts * BT_UV_VERT_SIZE * sizeof(float), res->verts);
|
tri_verts * BT_UV_VERT_SIZE * sizeof(float), res->verts);
|
||||||
glEnableVertexAttribArray(res->texture_position_attribute);
|
glEnableVertexAttribArray(res->rgb.position_attribute);
|
||||||
glEnableVertexAttribArray(res->texture_uv_attribute);
|
glEnableVertexAttribArray(res->rgb.uv_attribute);
|
||||||
glVertexAttribPointer(res->texture_position_attribute,
|
glVertexAttribPointer(res->rgb.position_attribute,
|
||||||
2, GL_FLOAT, GL_FALSE,
|
2, GL_FLOAT, GL_FALSE,
|
||||||
BT_UV_VERT_SIZE * sizeof(float), (void *) (0 * sizeof(float)));
|
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)));
|
BT_UV_VERT_SIZE * sizeof(float), (void *) (2 * sizeof(float)));
|
||||||
glUniform2fv(res->texture_screen_size_uniform, 1, screen_size);
|
glUniform2fv(res->rgb.screen_size_uniform, 1, screen_size);
|
||||||
glUniform1i(res->texture_texture_uniform, 0);
|
glUniform1i(res->rgb.texture_uniform, 0);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
wl_list_for_each_reverse(head, &info->heads, link) {
|
wl_list_for_each_reverse(head, &info->heads, link) {
|
||||||
glBindTexture(GL_TEXTURE_2D, res->textures[i]);
|
glBindTexture(GL_TEXTURE_2D, res->textures[i]);
|
||||||
if (head->updated_at == tick) {
|
if (head->updated_at == tick) {
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, head->tex_stride / 4);
|
if (head->image) {
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
|
assert_gl_ext(GL_OES_EGL_image);
|
||||||
head->tex_width, head->tex_height,
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, head->image);
|
||||||
0, GL_RGBA, GL_UNSIGNED_BYTE, head->pixels);
|
} else if (head->pixels) {
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
|
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);
|
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);
|
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);
|
glDrawArrays(GL_TRIANGLES, i * 6, 6);
|
||||||
i++;
|
i++;
|
||||||
if (i >= HEADS_MAX)
|
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) {
|
void wd_gl_cleanup(struct wd_gl_data *res) {
|
||||||
glDeleteBuffers(NUM_BUFFERS, res->buffers);
|
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->color_program);
|
||||||
|
|
||||||
|
glDeleteProgram(res->rgb.id);
|
||||||
|
|
||||||
free(res);
|
free(res);
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@ struct zxdg_output_manager_v1;
|
|||||||
struct zwlr_output_mode_v1;
|
struct zwlr_output_mode_v1;
|
||||||
struct zwlr_output_head_v1;
|
struct zwlr_output_head_v1;
|
||||||
struct zwlr_output_manager_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_manager_v1;
|
||||||
struct zwlr_screencopy_frame_v1;
|
struct zwlr_screencopy_frame_v1;
|
||||||
struct zwlr_layer_shell_v1;
|
struct zwlr_layer_shell_v1;
|
||||||
@ -41,6 +43,8 @@ typedef struct _GdkCursor GdkCursor;
|
|||||||
struct _cairo_surface;
|
struct _cairo_surface;
|
||||||
typedef struct _cairo_surface cairo_surface_t;
|
typedef struct _cairo_surface cairo_surface_t;
|
||||||
|
|
||||||
|
typedef void *EGLImageKHR;
|
||||||
|
|
||||||
struct wd_output {
|
struct wd_output {
|
||||||
struct wd_state *state;
|
struct wd_state *state;
|
||||||
struct zxdg_output_v1 *xdg_output;
|
struct zxdg_output_v1 *xdg_output;
|
||||||
@ -53,21 +57,30 @@ struct wd_output {
|
|||||||
struct zwlr_layer_surface_v1 *overlay_layer_surface;
|
struct zwlr_layer_surface_v1 *overlay_layer_surface;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define WD_MAX_PLANES 4
|
||||||
|
|
||||||
struct wd_frame {
|
struct wd_frame {
|
||||||
struct wd_output *output;
|
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;
|
struct wl_list link;
|
||||||
int capture_fd;
|
|
||||||
unsigned stride;
|
|
||||||
unsigned width;
|
unsigned width;
|
||||||
unsigned height;
|
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_shm_pool *pool;
|
||||||
struct wl_buffer *buffer;
|
struct wl_buffer *buffer;
|
||||||
uint8_t *pixels;
|
uint8_t *pixels;
|
||||||
uint64_t tick;
|
bool ready;
|
||||||
bool y_invert;
|
|
||||||
bool swap_rgb;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wd_head_config {
|
struct wd_head_config {
|
||||||
@ -141,15 +154,17 @@ struct wd_render_head_data {
|
|||||||
struct wd_render_head_flags active;
|
struct wd_render_head_flags active;
|
||||||
|
|
||||||
uint8_t *pixels;
|
uint8_t *pixels;
|
||||||
|
EGLImageKHR image;
|
||||||
unsigned tex_stride;
|
unsigned tex_stride;
|
||||||
unsigned tex_width;
|
unsigned tex_width;
|
||||||
unsigned tex_height;
|
unsigned tex_height;
|
||||||
|
|
||||||
bool preview;
|
uint32_t preview : 1,
|
||||||
bool y_invert;
|
y_invert : 1,
|
||||||
bool swap_rgb;
|
swap_rgb : 1,
|
||||||
bool hovered;
|
has_alpha : 1,
|
||||||
bool clicked;
|
hovered : 1,
|
||||||
|
clicked : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wd_render_data {
|
struct wd_render_data {
|
||||||
@ -166,6 +181,7 @@ struct wd_render_data {
|
|||||||
int x_origin;
|
int x_origin;
|
||||||
int y_origin;
|
int y_origin;
|
||||||
uint64_t updated_at;
|
uint64_t updated_at;
|
||||||
|
bool external_images;
|
||||||
|
|
||||||
struct wl_list heads;
|
struct wl_list heads;
|
||||||
};
|
};
|
||||||
@ -178,6 +194,7 @@ struct wd_point {
|
|||||||
struct wd_state {
|
struct wd_state {
|
||||||
struct zxdg_output_manager_v1 *xdg_output_manager;
|
struct zxdg_output_manager_v1 *xdg_output_manager;
|
||||||
struct zwlr_output_manager_v1 *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_screencopy_manager_v1 *copy_manager;
|
||||||
struct zwlr_layer_shell_v1 *layer_shell;
|
struct zwlr_layer_shell_v1 *layer_shell;
|
||||||
struct wl_shm *shm;
|
struct wl_shm *shm;
|
||||||
@ -311,8 +328,14 @@ void wd_ui_show_error(struct wd_state *state, const char *message);
|
|||||||
/*
|
/*
|
||||||
* Compiles the GL shaders.
|
* 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.
|
* Renders the GL scene.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user