Remove dead code

pixman-rendering
Andri Yngvason 2020-06-25 21:28:30 +00:00
parent a7283e68fd
commit 0ea4bd6646
11 changed files with 8 additions and 1671 deletions

View File

@ -1,40 +0,0 @@
/*
* Copyright (c) 2020 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <stdint.h>
#include <stdio.h>
struct pixman_region16;
struct pixman_box16;
void damage_check_row(uint8_t* dst, const uint8_t* src, uint32_t width);
void damage_check_tile_row(struct pixman_region16* damage,
uint8_t* row_buffer, const uint8_t* buffer,
uint32_t y_start, uint32_t width, uint32_t height);
void damage_check(struct pixman_region16* damage, const uint8_t* buffer,
uint32_t width, uint32_t height, struct pixman_box16* hint);
int damage_check_async(uint8_t* buffer, uint32_t width, uint32_t height,
struct pixman_box16* hint,
void (*on_done)(struct pixman_region16*, void*),
void* userdata);
void damage_dump(FILE* stream, struct pixman_region16* damage,
uint32_t width, uint32_t height, uint32_t tile_size);

View File

@ -1,59 +0,0 @@
/*
* Copyright (c) 2019 - 2020 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <aml.h>
#include "frame-capture.h"
struct zwlr_export_dmabuf_manager_v1;
struct zwlr_export_dmabuf_frame_v1;
struct wl_output;
struct aml_timer;
struct dmabuf_plane {
int fd;
uint32_t offset;
uint32_t size;
uint32_t pitch;
uint64_t modifier;
};
struct dmabuf_frame {
uint32_t width;
uint32_t height;
uint32_t format;
uint32_t n_planes;
struct dmabuf_plane plane[4];
};
struct dmabuf_capture {
struct frame_capture fc;
struct zwlr_export_dmabuf_manager_v1* manager;
struct zwlr_export_dmabuf_frame_v1* zwlr_frame;
struct dmabuf_frame frame;
uint64_t render_finish_time;
uint64_t start_time;
struct aml_timer* timer;
};
void dmabuf_capture_init(struct dmabuf_capture* self);
void dmabuf_capture_destroy(struct dmabuf_capture* self);

View File

@ -1,82 +0,0 @@
/*
* Copyright (c) 2019 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <GLES2/gl2.h>
#include <EGL/egl.h>
#include <unistd.h>
#include <wayland-client.h>
struct dmabuf_frame;
struct output;
enum renderer_input_type {
RENDERER_INPUT_FB,
RENDERER_INPUT_DMABUF,
};
struct renderer_fbo {
GLuint rbo;
GLuint fbo;
GLuint tex;
};
struct renderer {
EGLDisplay display;
EGLContext context;
const struct output* output;
GLint read_format;
GLint read_type;
struct renderer_fbo frame_fbo[2];
int frame_index;
struct renderer_fbo damage_fbo;
struct {
GLuint program;
GLint u_tex0;
GLint u_proj;
} frame_shader;
struct {
GLuint program;
GLint u_tex0;
GLint u_tex1;
} damage_shader;
};
int renderer_init(struct renderer* self, const struct output* output,
enum renderer_input_type input_type);
void renderer_destroy(struct renderer* self);
int render_dmabuf(struct renderer* self, struct dmabuf_frame* frame);
int render_framebuffer(struct renderer* self, const void* addr, uint32_t format,
uint32_t width, uint32_t height, uint32_t stride);
/* Copy a horizontal stripe from the GL frame into a pixel buffer */
void renderer_read_frame(struct renderer* self, void* dst, uint32_t y,
uint32_t height);
void renderer_read_damage(struct renderer* self, void* dst, uint32_t y,
uint32_t height);
void renderer_swap_textures(struct renderer* self);
void render_damage(struct renderer* self);

View File

@ -28,8 +28,6 @@ libm = cc.find_library('m', required: false)
librt = cc.find_library('rt', required: false) librt = cc.find_library('rt', required: false)
pixman = dependency('pixman-1') pixman = dependency('pixman-1')
egl = dependency('egl')
glesv2 = dependency('glesv2')
gbm = dependency('gbm') gbm = dependency('gbm')
drm = dependency('libdrm') drm = dependency('libdrm')
xkbcommon = dependency('xkbcommon') xkbcommon = dependency('xkbcommon')
@ -59,8 +57,6 @@ subdir('protocols')
sources = [ sources = [
'src/main.c', 'src/main.c',
'src/render.c',
'src/dmabuf.c',
'src/strlcpy.c', 'src/strlcpy.c',
'src/shm.c', 'src/shm.c',
'src/screencopy.c', 'src/screencopy.c',
@ -71,7 +67,6 @@ sources = [
'src/smooth.c', 'src/smooth.c',
'src/cfg.c', 'src/cfg.c',
'src/intset.c', 'src/intset.c',
'src/damage.c',
'src/buffer.c', 'src/buffer.c',
'src/pixels.c', 'src/pixels.c',
'src/pixman-renderer.c', 'src/pixman-renderer.c',
@ -85,8 +80,6 @@ dependencies = [
librt, librt,
pixman, pixman,
aml, aml,
egl,
glesv2,
gbm, gbm,
drm, drm,
wayland_client, wayland_client,
@ -124,5 +117,3 @@ executable(
include_directories: inc, include_directories: inc,
install: true, install: true,
) )
subdir('test')

View File

@ -1,180 +0,0 @@
/*
* Copyright (c) 2020 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <sys/param.h>
#include <pixman.h>
#include <aml.h>
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
struct damage_work {
void (*on_done)(struct pixman_region16*, void*);
void* userdata;
uint8_t* buffer;
uint32_t width, height;
struct pixman_region16 damage;
};
struct damage_work* damage_work_new(void)
{
struct damage_work* work = calloc(1, sizeof(*work));
if (!work)
return NULL;
pixman_region_init(&work->damage);
return work;
}
void damage_work_free(void* ud)
{
struct damage_work* work = ud;
free(work->buffer);
pixman_region_fini(&work->damage);
free(work);
}
bool damage_check_32_byte_block(const void* block)
{
const uint64_t* a = block;
return a[0] || a[1] || a[2] || a[3];
}
void damage_check_row(uint8_t* dst, const uint8_t* src, uint32_t width)
{
uint32_t aligned_width = (width / 32) * 32;
for (uint32_t x = 0; x < aligned_width; x += 32)
dst[x / 32] |= damage_check_32_byte_block(&src[x]);
for (uint32_t x = aligned_width; x < width; ++x)
dst[x / 32] |= src[x];
}
void damage_check_tile_row(struct pixman_region16* damage,
uint8_t* row_buffer, const uint8_t* buffer,
uint32_t y_start, uint32_t width, uint32_t height)
{
uint32_t tiled_width = UDIV_UP(width, 32);
memset(row_buffer, 0, tiled_width);
for (uint32_t y = y_start; y < y_start + height; ++y)
damage_check_row(row_buffer, buffer + y * width, width);
for (uint32_t x = 0; x < tiled_width; ++x)
if (row_buffer[x])
pixman_region_union_rect(damage, damage,
x * 32, y_start, 32, 32);
}
void damage_check(struct pixman_region16* damage, const uint8_t* buffer,
uint32_t width, uint32_t height, struct pixman_box16* hint)
{
uint32_t tiled_width = UDIV_UP(width, 32);
uint8_t* row_buffer = malloc(tiled_width);
assert(row_buffer);
for (uint32_t y = 0; y < height; y += 32) {
uint32_t current_height = MIN(32, height - y);
damage_check_tile_row(damage, row_buffer, buffer, y, width,
current_height);
}
pixman_region_intersect_rect(damage, damage, 0, 0, width, height);
free(row_buffer);
}
static void do_damage_check(void* aml_obj)
{
struct damage_work* priv = aml_get_userdata(aml_obj);
damage_check(&priv->damage, priv->buffer, priv->width, priv->height, NULL);
}
static void on_damage_check_done(void* aml_obj)
{
struct damage_work* priv = aml_get_userdata(aml_obj);
priv->on_done(&priv->damage, priv->userdata);
}
// TODO: Use the hint
// TODO: Split rows between jobs
// XXX: This takes ownership of the damage buffer
int damage_check_async(uint8_t* buffer, uint32_t width, uint32_t height,
struct pixman_box16* hint,
void (*on_done)(struct pixman_region16*, void*),
void* userdata)
{
struct damage_work* priv = damage_work_new();
if (!priv)
return -1;
priv->buffer = buffer;
priv->width = width;
priv->height = height;
priv->on_done = on_done;
priv->userdata = userdata;
struct aml_work* work;
work = aml_work_new(do_damage_check, on_damage_check_done, priv,
damage_work_free);
if (!work)
goto oom;
int rc = aml_start(aml_get_default(), work);
aml_unref(work);
return rc;
oom:
damage_work_free(priv);
return -1;
}
void damage_dump(FILE* stream, struct pixman_region16* damage,
uint32_t width, uint32_t height, uint32_t tile_size)
{
uint32_t tiled_width = UDIV_UP(width, tile_size);
uint32_t tiled_height = UDIV_UP(height, tile_size);
fprintf(stream, "\033[2J");
for (uint32_t y = 0; y < tiled_height; ++y) {
for (uint32_t x = 0; x < tiled_width; ++x) {
struct pixman_box16 box = {
.x1 = x * tile_size,
.x2 = ((x + 1) * tile_size) - 1,
.y1 = y * tile_size,
.y2 = ((y + 1) * tile_size) - 1,
};
pixman_region_overlap_t overlap =
pixman_region_contains_rectangle(damage, &box);
putc(overlap == PIXMAN_REGION_IN ? 'x' : ' ', stream);
}
putc('\n', stream);
}
}

View File

@ -1,243 +0,0 @@
/*
* Copyright (c) 2019 - 2020 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <inttypes.h>
#include <wayland-client.h>
#include <wayland-util.h>
#include <time-util.h>
#include "frame-capture.h"
#include "logging.h"
#include "dmabuf.h"
#include "wlr-export-dmabuf-unstable-v1.h"
#include "render.h"
#include "usdt.h"
#define RATE_LIMIT 30.0
#define MIN_PERIOD ((uint64_t)(1000.0 / RATE_LIMIT))
#define PROCESSING_DURATION_LIMIT 16 /* ms */
static int dmabuf_capture_start_now(struct frame_capture* fc,
enum frame_capture_options options);
static void dmabuf_close_fds(struct dmabuf_capture* self)
{
for (size_t i = 0; i < self->frame.n_planes; ++i)
close(self->frame.plane[i].fd);
self->frame.n_planes = 0;
}
static void dmabuf_capture_stop(struct frame_capture* fc)
{
struct dmabuf_capture* self = (void*)fc;
aml_stop(aml_get_default(), self->timer);
fc->status = CAPTURE_STOPPED;
if (self->zwlr_frame) {
zwlr_export_dmabuf_frame_v1_destroy(self->zwlr_frame);
self->zwlr_frame = NULL;
}
}
static void dmabuf_frame_start(void* data,
struct zwlr_export_dmabuf_frame_v1* 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 dmabuf_capture* self = data;
struct frame_capture* fc = data;
dmabuf_close_fds(self);
uint64_t mod = ((uint64_t)mod_high << 32) | (uint64_t)mod_low;
self->frame.width = width;
self->frame.height = height;
self->frame.n_planes = num_objects;
self->frame.format = format;
self->frame.plane[0].modifier = mod;
self->frame.plane[1].modifier = mod;
self->frame.plane[2].modifier = mod;
self->frame.plane[3].modifier = mod;
fc->damage_hint.x = 0;
fc->damage_hint.y = 0;
fc->damage_hint.width = width;
fc->damage_hint.height = height;
}
static void dmabuf_frame_object(void* data,
struct zwlr_export_dmabuf_frame_v1* frame,
uint32_t index, int32_t fd, uint32_t size,
uint32_t offset, uint32_t stride,
uint32_t plane_index)
{
struct dmabuf_capture* self = data;
self->frame.plane[plane_index].fd = fd;
self->frame.plane[plane_index].size = size;
self->frame.plane[plane_index].offset = offset;
self->frame.plane[plane_index].pitch = stride;
}
static void dmabuf_frame_ready(void* data,
struct zwlr_export_dmabuf_frame_v1* frame,
uint32_t tv_sec_hi, uint32_t tv_sec_lo,
uint32_t tv_nsec)
{
struct dmabuf_capture* self = data;
struct frame_capture* fc = data;
struct timespec ts = {
.tv_sec = ((uint64_t)tv_sec_hi << 32) | tv_sec_lo,
.tv_nsec = tv_nsec
};
uint64_t precommit_time = timespec_to_ms(&ts);
DTRACE_PROBE2(wayvnc, dmabuf_frame_ready, self, precommit_time);
dmabuf_capture_stop(fc);
fc->status = CAPTURE_DONE;
fc->on_done(fc);
dmabuf_close_fds(self);
uint64_t processing_duration =
self->render_finish_time - precommit_time;
DTRACE_PROBE3(wayvnc, dmabuf_frame_release, self,
self->render_finish_time, processing_duration);
if (processing_duration > PROCESSING_DURATION_LIMIT)
log_debug("Processing dmabuf took %"PRIu64" ms.\n",
processing_duration);
}
static void dmabuf_frame_cancel(void* data,
struct zwlr_export_dmabuf_frame_v1* frame,
uint32_t reason)
{
struct dmabuf_capture* self = data;
struct frame_capture* fc = data;
DTRACE_PROBE1(wayvnc, dmabuf_frame_cancel, self);
dmabuf_capture_stop(fc);
fc->status = reason == ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERMANENT
? CAPTURE_FATAL : CAPTURE_FAILED;
fc->on_done(fc);
dmabuf_close_fds(self);
}
static void dmabuf__poll(void* obj)
{
struct dmabuf_capture* self = aml_get_userdata(obj);
struct frame_capture* fc = (struct frame_capture*)self;
dmabuf_capture_start_now(fc, 0);
}
static int dmabuf_capture_start(struct frame_capture* fc,
enum frame_capture_options options)
{
struct dmabuf_capture* self = (void*)fc;
if (fc->status == CAPTURE_IN_PROGRESS)
return -1;
uint64_t now = gettime_ms();
uint64_t dt = now - self->start_time;
fc->status = CAPTURE_IN_PROGRESS;
if (dt < MIN_PERIOD) {
uint64_t time_left = MIN_PERIOD - dt;
aml_set_duration(self->timer, time_left);
return aml_start(aml_get_default(), self->timer);
}
return dmabuf_capture_start_now(fc, options);
}
static int dmabuf_capture_start_now(struct frame_capture* fc,
enum frame_capture_options options)
{
struct dmabuf_capture* self = (void*)fc;
static const struct zwlr_export_dmabuf_frame_v1_listener
dmabuf_frame_listener = {
.frame = dmabuf_frame_start,
.object = dmabuf_frame_object,
.ready = dmabuf_frame_ready,
.cancel = dmabuf_frame_cancel,
};
DTRACE_PROBE1(wayvnc, dmabuf_capture_start, self);
self->zwlr_frame =
zwlr_export_dmabuf_manager_v1_capture_output(self->manager,
fc->overlay_cursor,
fc->wl_output);
if (!self->zwlr_frame)
return -1;
self->start_time = gettime_ms();
zwlr_export_dmabuf_frame_v1_add_listener(self->zwlr_frame,
&dmabuf_frame_listener, self);
return 0;
}
static void dmabuf_capture_render(struct frame_capture* fc,
struct renderer* render, struct nvnc_fb* fb)
{
struct dmabuf_capture* self = (void*)fc;
render_dmabuf(render, &self->frame);
self->render_finish_time = gettime_ms();
}
void dmabuf_capture_init(struct dmabuf_capture* self)
{
self->timer = aml_timer_new(0, dmabuf__poll, self, NULL);
assert(self->timer);
self->fc.backend.start = dmabuf_capture_start;
self->fc.backend.stop = dmabuf_capture_stop;
self->fc.backend.render = dmabuf_capture_render;
}
void dmabuf_capture_destroy(struct dmabuf_capture* self)
{
aml_stop(aml_get_default(), self->timer);
aml_unref(self->timer);
}

View File

@ -28,10 +28,7 @@
#include <aml.h> #include <aml.h>
#include <signal.h> #include <signal.h>
#include <libdrm/drm_fourcc.h> #include <libdrm/drm_fourcc.h>
#include <wayland-client-protocol.h>
#include <wayland-client.h> #include <wayland-client.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <pixman.h> #include <pixman.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/types.h> #include <sys/types.h>
@ -41,14 +38,11 @@
#include <xf86drm.h> #include <xf86drm.h>
#include "frame-capture.h" #include "frame-capture.h"
#include "wlr-export-dmabuf-unstable-v1.h"
#include "wlr-screencopy-unstable-v1.h" #include "wlr-screencopy-unstable-v1.h"
#include "wlr-virtual-pointer-unstable-v1.h" #include "wlr-virtual-pointer-unstable-v1.h"
#include "virtual-keyboard-unstable-v1.h" #include "virtual-keyboard-unstable-v1.h"
#include "xdg-output-unstable-v1.h" #include "xdg-output-unstable-v1.h"
#include "linux-dmabuf-unstable-v1.h" #include "linux-dmabuf-unstable-v1.h"
#include "render.h"
#include "dmabuf.h"
#include "screencopy.h" #include "screencopy.h"
#include "strlcpy.h" #include "strlcpy.h"
#include "logging.h" #include "logging.h"
@ -57,7 +51,6 @@
#include "keyboard.h" #include "keyboard.h"
#include "seat.h" #include "seat.h"
#include "cfg.h" #include "cfg.h"
#include "damage.h"
#include "pixman-renderer.h" #include "pixman-renderer.h"
#include "transform-util.h" #include "transform-util.h"
#include "damage-refinery.h" #include "damage-refinery.h"
@ -68,12 +61,6 @@
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b)) #define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
#define ALIGN_UP(n, a) (UDIV_UP(n, a) * a) #define ALIGN_UP(n, a) (UDIV_UP(n, a) * a)
enum frame_capture_backend_type {
FRAME_CAPTURE_BACKEND_NONE = 0,
FRAME_CAPTURE_BACKEND_SCREENCOPY,
FRAME_CAPTURE_BACKEND_DMABUF,
};
struct wayvnc { struct wayvnc {
bool do_exit; bool do_exit;
@ -89,11 +76,9 @@ struct wayvnc {
int pointer_manager_version; int pointer_manager_version;
struct renderer renderer;
const struct output* selected_output; const struct output* selected_output;
const struct seat* selected_seat; const struct seat* selected_seat;
struct dmabuf_capture dmabuf_backend;
struct screencopy screencopy_backend; struct screencopy screencopy_backend;
struct frame_capture* capture_backend; struct frame_capture* capture_backend;
struct pointer pointer_backend; struct pointer pointer_backend;
@ -120,18 +105,6 @@ struct wl_shm* wl_shm = NULL;
struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf = NULL; struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf = NULL;
struct gbm_device* gbm_device = NULL; struct gbm_device* gbm_device = NULL;
static enum frame_capture_backend_type
frame_capture_backend_from_string(const char* str)
{
if (strcmp(str, "screencopy") == 0)
return FRAME_CAPTURE_BACKEND_SCREENCOPY;
if (strcmp(str, "dmabuf") == 0)
return FRAME_CAPTURE_BACKEND_DMABUF;
return FRAME_CAPTURE_BACKEND_NONE;
}
static void registry_add(void* data, struct wl_registry* registry, static void registry_add(void* data, struct wl_registry* registry,
uint32_t id, const char* interface, uint32_t id, const char* interface,
uint32_t version) uint32_t version)
@ -165,14 +138,6 @@ static void registry_add(void* data, struct wl_registry* registry,
return; return;
} }
if (strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name) == 0) {
self->dmabuf_backend.manager =
wl_registry_bind(registry, id,
&zwlr_export_dmabuf_manager_v1_interface,
1);
return;
}
if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) { if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
self->screencopy_backend.manager = self->screencopy_backend.manager =
wl_registry_bind(registry, id, wl_registry_bind(registry, id,
@ -292,9 +257,6 @@ void wayvnc_destroy(struct wayvnc* self)
if (self->screencopy_backend.manager) if (self->screencopy_backend.manager)
zwlr_screencopy_manager_v1_destroy(self->screencopy_backend.manager); zwlr_screencopy_manager_v1_destroy(self->screencopy_backend.manager);
if (self->dmabuf_backend.manager)
zwlr_export_dmabuf_manager_v1_destroy(self->dmabuf_backend.manager);
wl_display_disconnect(self->display); wl_display_disconnect(self->display);
} }
@ -348,14 +310,11 @@ static int init_wayland(struct wayvnc* self)
wl_display_dispatch(self->display); wl_display_dispatch(self->display);
wl_display_roundtrip(self->display); wl_display_roundtrip(self->display);
if (!self->dmabuf_backend.manager && !self->screencopy_backend.manager) { if (!self->screencopy_backend.manager) {
log_error("Compositor supports neither screencopy nor export-dmabuf! Exiting.\n"); log_error("Compositor doesn't support screencopy! Exiting.\n");
goto failure; goto failure;
} }
self->dmabuf_backend.fc.on_done = on_capture_done;
self->dmabuf_backend.fc.userdata = self;
self->screencopy_backend.frame_capture.on_done = on_capture_done; self->screencopy_backend.frame_capture.on_done = on_capture_done;
self->screencopy_backend.frame_capture.userdata = self; self->screencopy_backend.frame_capture.userdata = self;
@ -425,16 +384,6 @@ int init_main_loop(struct wayvnc* self)
return 0; return 0;
} }
uint32_t fourcc_from_gl_format(uint32_t format)
{
switch (format) {
case GL_BGRA_EXT: return DRM_FORMAT_XRGB8888;
case GL_RGBA: return DRM_FORMAT_XBGR8888;
}
return DRM_FORMAT_INVALID;
}
static void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y, static void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y,
enum nvnc_button_mask button_mask) enum nvnc_button_mask button_mask)
{ {
@ -534,35 +483,10 @@ static void wayvnc_damage_region(struct wayvnc* self,
nvnc_display_damage_region(self->nvnc_display, damage); nvnc_display_damage_region(self->nvnc_display, damage);
} }
static void wayvnc_damage_whole(struct wayvnc* self)
{
uint16_t width = nvnc_fb_get_width(self->buffer);
uint16_t height = nvnc_fb_get_height(self->buffer);
struct pixman_region16 damage;
pixman_region_init_rect(&damage, 0, 0, width, height);
wayvnc_damage_region(self, &damage);
pixman_region_fini(&damage);
}
static void on_damage_check_done(struct pixman_region16* damage, void* userdata)
{
struct wayvnc* self = userdata;
wayvnc_damage_region(self, damage);
if (wayvnc_start_capture(self, 0) < 0) {
log_error("Failed to start capture. Exiting...\n");
wayvnc_exit(self);
}
}
void wayvnc_process_frame(struct wayvnc* self) void wayvnc_process_frame(struct wayvnc* self)
{ {
// uint32_t format = fourcc_from_gl_format(self->renderer.read_format);
uint32_t width = output_get_transformed_width(self->selected_output); uint32_t width = output_get_transformed_width(self->selected_output);
uint32_t height = output_get_transformed_height(self->selected_output); uint32_t height = output_get_transformed_height(self->selected_output);
bool is_first_frame = false;
if (!self->buffer) { if (!self->buffer) {
self->buffer = nvnc_fb_new(width, height, DRM_FORMAT_XBGR8888); self->buffer = nvnc_fb_new(width, height, DRM_FORMAT_XBGR8888);
@ -571,8 +495,6 @@ void wayvnc_process_frame(struct wayvnc* self)
damage_refinery_init(&self->damage_refinery, damage_refinery_init(&self->damage_refinery,
self->screencopy_backend.back->width, self->screencopy_backend.back->width,
self->screencopy_backend.back->height); self->screencopy_backend.back->height);
is_first_frame = true;
} else { } else {
// TODO: Reallocate // TODO: Reallocate
assert(width == nvnc_fb_get_width(self->buffer)); assert(width == nvnc_fb_get_width(self->buffer));
@ -637,7 +559,6 @@ int wayvnc_usage(FILE* stream, int rc)
"Usage: wayvnc [options] [address [port]]\n" "Usage: wayvnc [options] [address [port]]\n"
"\n" "\n"
" -C,--config=<path> Select a config file.\n" " -C,--config=<path> Select a config file.\n"
" -c,--frame-capturing=screencopy|dmabuf Select frame capturing backend.\n"
" -o,--output=<name> Select output to capture.\n" " -o,--output=<name> Select output to capture.\n"
" -k,--keyboard=<layout> Select keyboard layout.\n" " -k,--keyboard=<layout> Select keyboard layout.\n"
" -s,--seat=<name> Select seat by name.\n" " -s,--seat=<name> Select seat by name.\n"
@ -696,7 +617,6 @@ int main(int argc, char* argv[])
int port = 0; int port = 0;
const char* output_name = NULL; const char* output_name = NULL;
enum frame_capture_backend_type fcbackend = FRAME_CAPTURE_BACKEND_NONE;
const char* seat_name = NULL; const char* seat_name = NULL;
bool overlay_cursor = false; bool overlay_cursor = false;
@ -706,7 +626,6 @@ int main(int argc, char* argv[])
static const struct option longopts[] = { static const struct option longopts[] = {
{ "config", required_argument, NULL, 'C' }, { "config", required_argument, NULL, 'C' },
{ "frame-capturing", required_argument, NULL, 'c' },
{ "output", required_argument, NULL, 'o' }, { "output", required_argument, NULL, 'o' },
{ "keyboard", required_argument, NULL, 'k' }, { "keyboard", required_argument, NULL, 'k' },
{ "seat", required_argument, NULL, 's' }, { "seat", required_argument, NULL, 's' },
@ -724,15 +643,6 @@ int main(int argc, char* argv[])
case 'C': case 'C':
cfg_file = optarg; cfg_file = optarg;
break; break;
case 'c':
fcbackend = frame_capture_backend_from_string(optarg);
if (fcbackend == FRAME_CAPTURE_BACKEND_NONE) {
fprintf(stderr, "Invalid backend: %s\n\n",
optarg);
return wayvnc_usage(stderr, 1);
}
break;
case 'o': case 'o':
output_name = optarg; output_name = optarg;
break; break;
@ -821,7 +731,6 @@ int main(int argc, char* argv[])
self.selected_output = out; self.selected_output = out;
self.selected_seat = seat; self.selected_seat = seat;
self.dmabuf_backend.fc.wl_output = out->wl_output;
self.screencopy_backend.frame_capture.wl_output = out->wl_output; self.screencopy_backend.frame_capture.wl_output = out->wl_output;
self.keyboard_backend.virtual_keyboard = self.keyboard_backend.virtual_keyboard =
@ -854,15 +763,6 @@ int main(int argc, char* argv[])
if (!gbm_device) if (!gbm_device)
goto failure; goto failure;
enum renderer_input_type renderer_input_type =
fcbackend == FRAME_CAPTURE_BACKEND_DMABUF ?
RENDERER_INPUT_DMABUF : RENDERER_INPUT_FB;
if (renderer_init(&self.renderer, self.selected_output,
renderer_input_type) < 0) {
log_error("Failed to initialise renderer\n");
goto failure;
}
struct aml* aml = aml_new(NULL, 0); struct aml* aml = aml_new(NULL, 0);
if (!aml) if (!aml)
goto main_loop_failure; goto main_loop_failure;
@ -878,36 +778,13 @@ int main(int argc, char* argv[])
if (self.screencopy_backend.manager) if (self.screencopy_backend.manager)
screencopy_init(&self.screencopy_backend); screencopy_init(&self.screencopy_backend);
if (self.dmabuf_backend.manager) if (!self.screencopy_backend.manager) {
dmabuf_capture_init(&self.dmabuf_backend); log_error("screencopy is not supported by compositor\n");
goto capture_failure;
switch (fcbackend) {
case FRAME_CAPTURE_BACKEND_SCREENCOPY:
if (!self.screencopy_backend.manager) {
log_error("screencopy is not supported by compositor\n");
goto capture_failure;
}
self.capture_backend = &self.screencopy_backend.frame_capture;
break;
case FRAME_CAPTURE_BACKEND_DMABUF:
if (!self.screencopy_backend.manager) {
log_error("export-dmabuf is not supported by compositor\n");
goto capture_failure;
}
self.capture_backend = &self.dmabuf_backend.fc;
break;
case FRAME_CAPTURE_BACKEND_NONE:
if (self.screencopy_backend.manager)
self.capture_backend = &self.screencopy_backend.frame_capture;
else if (self.dmabuf_backend.manager)
self.capture_backend = &self.dmabuf_backend.fc;
else
goto capture_failure;
break;
} }
self.capture_backend = &self.screencopy_backend.frame_capture;
pixman_region_init(&self.current_damage); pixman_region_init(&self.current_damage);
self.capture_backend->overlay_cursor = overlay_cursor; self.capture_backend->overlay_cursor = overlay_cursor;
@ -931,13 +808,10 @@ int main(int argc, char* argv[])
nvnc_display_unref(self.nvnc_display); nvnc_display_unref(self.nvnc_display);
nvnc_close(self.nvnc); nvnc_close(self.nvnc);
renderer_destroy(&self.renderer);
if (zwp_linux_dmabuf) if (zwp_linux_dmabuf)
zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf); zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf);
if (self.screencopy_backend.manager) if (self.screencopy_backend.manager)
screencopy_destroy(&self.screencopy_backend); screencopy_destroy(&self.screencopy_backend);
if (self.dmabuf_backend.manager)
dmabuf_capture_destroy(&self.dmabuf_backend);
wayvnc_destroy(&self); wayvnc_destroy(&self);
aml_unref(aml); aml_unref(aml);
@ -948,7 +822,6 @@ capture_failure:
nvnc_close(self.nvnc); nvnc_close(self.nvnc);
nvnc_failure: nvnc_failure:
main_loop_failure: main_loop_failure:
renderer_destroy(&self.renderer);
failure: failure:
gbm_device_destroy(gbm_device); gbm_device_destroy(gbm_device);
if (drm_fd >= 0) if (drm_fd >= 0)

View File

@ -1,795 +0,0 @@
/*
* Copyright (c) 2019 - 2020 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <wayland-client.h>
#include <libdrm/drm_fourcc.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "logging.h"
#include "render.h"
#include "dmabuf.h"
#include "output.h"
#include "config.h"
#include "usdt.h"
#define MAYBE_UNUSED __attribute__((unused))
#define SHADER_PATH PREFIX "/share/wayvnc/shaders"
enum {
ATTR_INDEX_POS = 0,
ATTR_INDEX_TEXTURE,
};
#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
#define XSTR(s) STR(s)
#define STR(s) #s
#define X_GL_EARLY_EXTENSIONS \
X(PFNEGLGETPLATFORMDISPLAYEXTPROC, eglGetPlatformDisplayEXT) \
X(PFNEGLDEBUGMESSAGECONTROLKHRPROC, eglDebugMessageControlKHR) \
X(PFNGLDEBUGMESSAGECALLBACKKHRPROC, glDebugMessageCallbackKHR) \
#define X_GL_LATE_EXTENSIONS \
X(PFNEGLCREATEIMAGEKHRPROC, eglCreateImageKHR) \
X(PFNEGLDESTROYIMAGEKHRPROC, eglDestroyImageKHR) \
X(PFNGLEGLIMAGETARGETTEXTURE2DOESPROC, glEGLImageTargetTexture2DOES) \
#define X_GL_EXTENSIONS \
X_GL_EARLY_EXTENSIONS \
X_GL_LATE_EXTENSIONS \
#define X(type, name) type name;
X_GL_EXTENSIONS
#undef X
static const float transforms[][4] = {
[WL_OUTPUT_TRANSFORM_NORMAL] = {
1.0f, 0.0f,
0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_90] = {
0.0f, 1.0f,
-1.0f, 0.0f,
},
[WL_OUTPUT_TRANSFORM_180] = {
-1.0f, 0.0f,
0.0f, -1.0f,
},
[WL_OUTPUT_TRANSFORM_270] = {
0.0f, -1.0f,
1.0f, 0.0f,
},
[WL_OUTPUT_TRANSFORM_FLIPPED] = {
-1.0f, 0.0f,
0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_FLIPPED_90] = {
0.0f, 1.0f,
1.0f, 0.0f,
},
[WL_OUTPUT_TRANSFORM_FLIPPED_180] = {
1.0f, 0.0f,
0.0f, -1.0f,
},
[WL_OUTPUT_TRANSFORM_FLIPPED_270] = {
0.0f, -1.0f,
-1.0f, 0.0f,
},
};
int gl_format_from_fourcc(GLenum* result, uint32_t format)
{
switch (format) {
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_ARGB8888:
*result = GL_BGRA_EXT;
return 0;
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_ABGR8888:
*result = GL_RGBA;
return 0;
}
return -1;
}
static inline void* gl_load_single_extension(const char* name)
{
void* ext = eglGetProcAddress(name);
if (!ext)
log_debug("GL: Failed to load procedure: %s\n", name);
return ext;
}
static int gl_load_early_extensions(void)
{
#define X(type, name) \
name = gl_load_single_extension(XSTR(name)); \
if (!name) \
return -1;
X_GL_EARLY_EXTENSIONS
#undef X
return 0;
}
static int gl_load_late_extensions(void)
{
#define X(type, name) \
name = gl_load_single_extension(XSTR(name)); \
if (!name) \
return -1;
X_GL_LATE_EXTENSIONS
#undef X
return 0;
}
MAYBE_UNUSED
static void egl_log(EGLenum error, const char* command, EGLint msg_type,
EGLLabelKHR thread, EGLLabelKHR obj, const char *msg)
{
(void)error;
(void)msg_type;
(void)thread;
(void)obj;
log_debug("EGL: %s: %s\n", command, msg);
}
MAYBE_UNUSED
static void gles2_log(GLenum src, GLenum type, GLuint id, GLenum severity,
GLsizei len, const GLchar *msg, const void *user)
{
(void)src;
(void)type;
(void)id;
(void)severity;
(void)len;
(void)user;
log_debug("GLES2: %s\n", msg);
}
static void gl_debug_init()
{
#ifndef NDEBUG
static const EGLAttrib debug_attribs[] = {
EGL_DEBUG_MSG_CRITICAL_KHR, EGL_TRUE,
EGL_DEBUG_MSG_ERROR_KHR, EGL_TRUE,
EGL_DEBUG_MSG_WARN_KHR, EGL_TRUE,
EGL_DEBUG_MSG_INFO_KHR, EGL_TRUE,
EGL_NONE,
};
eglDebugMessageControlKHR(egl_log, debug_attribs);
glEnable(GL_DEBUG_OUTPUT_KHR);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
glDebugMessageCallbackKHR(gles2_log, NULL);
#endif
}
static void gl_shader_log(GLuint shader)
{
GLint len = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
char* log = malloc(len);
assert(log);
glGetShaderInfoLog(shader, len, &len, log);
fwrite(log, 1, len, stderr);
free(log);
}
static int gl_load_shader(GLuint* dst, const char* path, const char* source,
GLenum type)
{
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, NULL);
glCompileShader(shader);
GLint is_compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &is_compiled);
if (!is_compiled) {
log_error("Failed to compile shader: %s\n", path);
gl_shader_log(shader);
glDeleteShader(shader);
return -1;
}
*dst = shader;
return 0;
}
static char* read_file(const char* path)
{
FILE* stream = fopen(path, "r");
if (!stream)
return NULL;
size_t size = 4096;
size_t rsize = 0;
char* contents = malloc(size);
if (!contents)
goto alloc_failure;
while (1) {
rsize += fread(contents + rsize, 1, size - rsize, stream);
if (rsize < size)
break;
size *= 2;
contents = realloc(contents, size);
if (!contents)
goto read_failure;
}
if (ferror(stream))
goto read_failure;
if (rsize == size) {
contents = realloc(contents, size + 1);
if (!contents)
goto read_failure;
}
contents[rsize] = '\0';
fclose(stream);
return contents;
read_failure:
free(contents);
alloc_failure:
fclose(stream);
return NULL;
}
static char* read_file_at_path(const char* prefix, const char* file)
{
char path[256];
snprintf(path, sizeof(path), "%s/%s", prefix, file);
path[sizeof(path) - 1] = '\0';
return read_file(path);
}
static int gl_load_shader_from_file(GLuint* dst, const char* file, GLenum type)
{
char* source = read_file_at_path("shaders", file);
if (!source)
source = read_file_at_path(SHADER_PATH, file);
if (!source)
return -1;
int rc = gl_load_shader(dst, file, source, type);
free(source);
return rc;
}
static int gl_compile_shader_program(GLuint* dst, const char* vertex_path,
const char* fragment_path)
{
int rc = -1;
GLuint vertex, fragment;
if (gl_load_shader_from_file(&vertex, vertex_path, GL_VERTEX_SHADER) < 0)
return -1;
if (gl_load_shader_from_file(&fragment, fragment_path,
GL_FRAGMENT_SHADER) < 0)
goto fragment_failure;
GLuint program = glCreateProgram();
glAttachShader(program, vertex);
glAttachShader(program, fragment);
glBindAttribLocation(program, ATTR_INDEX_POS, "pos");
glBindAttribLocation(program, ATTR_INDEX_TEXTURE, "texture");
glLinkProgram(program);
glDeleteShader(vertex);
glDeleteShader(fragment);
GLint is_linked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &is_linked);
if (!is_linked) {
log_error("Failed to link shaders %s and %s\n", vertex_path,
fragment_path);
gl_shader_log(program);
glDeleteProgram(program);
goto program_failure;
}
*dst = program;
rc = 0;
program_failure:
glDeleteShader(fragment);
fragment_failure:
glDeleteShader(vertex);
return rc;
}
void gl_clear(void)
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
void gl_draw(void)
{
static const GLfloat s_vertices[4][2] = {
{ -1.0, 1.0 },
{ 1.0, 1.0 },
{ -1.0, -1.0 },
{ 1.0, -1.0 },
};
static const GLfloat s_positions[4][2] = {
{ 0, 0 },
{ 1, 0 },
{ 0, 1 },
{ 1, 1 },
};
gl_clear();
glVertexAttribPointer(ATTR_INDEX_POS, 2, GL_FLOAT, GL_FALSE, 0,
s_vertices);
glVertexAttribPointer(ATTR_INDEX_TEXTURE, 2, GL_FLOAT, GL_FALSE, 0,
s_positions);
glEnableVertexAttribArray(ATTR_INDEX_POS);
glEnableVertexAttribArray(ATTR_INDEX_TEXTURE);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(ATTR_INDEX_TEXTURE);
glDisableVertexAttribArray(ATTR_INDEX_POS);
}
void renderer_swap(struct renderer* self)
{
self->frame_index ^= 1;
}
void render_frame(struct renderer* self)
{
renderer_swap(self);
glBindFramebuffer(GL_FRAMEBUFFER, self->frame_fbo[self->frame_index].fbo);
glUseProgram(self->frame_shader.program);
glUniform1i(self->frame_shader.u_tex0, 0);
const float* proj = transforms[self->output->transform];
glUniformMatrix2fv(self->frame_shader.u_proj, 1, GL_FALSE, proj);
gl_draw();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void render_damage(struct renderer* self)
{
glBindFramebuffer(GL_FRAMEBUFFER, self->damage_fbo.fbo);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, self->frame_fbo[self->frame_index].tex);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, self->frame_fbo[!self->frame_index].tex);
glUseProgram(self->damage_shader.program);
glUniform1i(self->damage_shader.u_tex0, 0);
glUniform1i(self->damage_shader.u_tex1, 1);
gl_draw();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void destroy_fbo(struct renderer_fbo* fbo)
{
glDeleteFramebuffers(1, &fbo->fbo);
glDeleteRenderbuffers(1, &fbo->rbo);
if (fbo->tex)
glDeleteTextures(1, &fbo->tex);
}
int create_fbo(struct renderer_fbo* dst, GLint format, uint32_t width,
uint32_t height)
{
GLuint rbo = 0;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, rbo);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (status != GL_FRAMEBUFFER_COMPLETE) {
log_error("Framebuffer incomplete\n");
return -1;
}
dst->fbo = fbo;
dst->rbo = rbo;
dst->tex = 0;
return 0;
}
int create_textured_fbo(struct renderer_fbo* dst, GLint format, uint32_t width,
uint32_t height)
{
GLuint tex = 0;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format,
GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, tex, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (status != GL_FRAMEBUFFER_COMPLETE) {
log_error("Framebuffer incomplete\n");
return -1;
}
dst->fbo = fbo;
dst->rbo = 0;
dst->tex = tex;
return 0;
}
void renderer_destroy(struct renderer* self)
{
glDeleteProgram(self->frame_shader.program);
eglMakeCurrent(self->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
destroy_fbo(&self->frame_fbo[1]);
destroy_fbo(&self->frame_fbo[0]);
eglDestroyContext(self->display, self->context);
eglTerminate(self->display);
}
int renderer_init(struct renderer* self, const struct output* output,
enum renderer_input_type input_type)
{
if (!eglBindAPI(EGL_OPENGL_ES_API)) {
log_error("Failed to bind EGL API\n");
return -1;
}
if (gl_load_early_extensions() < 0) {
log_error("Failed to load early GL extensions\n");
return -1;
}
gl_debug_init();
self->display =
eglGetPlatformDisplayEXT(EGL_PLATFORM_SURFACELESS_MESA,
EGL_DEFAULT_DISPLAY, NULL);
if (!self->display) {
log_error("Failed to get EGL display\n");
return -1;
}
if (!eglInitialize(self->display, NULL, NULL)) {
log_error("Failed to get initialize EGL\n");
return -1;
}
static const EGLint cfg_attr[] = {
EGL_SURFACE_TYPE, 0,
EGL_ALPHA_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_NONE
};
EGLConfig cfg;
EGLint cfg_count;
if (!eglChooseConfig(self->display, cfg_attr, &cfg, 1, &cfg_count)) {
log_error("Could not choose EGL config\n");
return -1;
}
static const EGLint ctx_attr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
self->context = eglCreateContext(self->display, cfg, EGL_NO_CONTEXT,
ctx_attr);
if (!self->context) {
log_error("Failed to create EGL context\n");
return -1;
}
if (!eglMakeCurrent(self->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
self->context)) {
log_error("Failed to make EGL context current\n");
goto make_current_failure;
}
log_debug("%s\n", glGetString(GL_VERSION));
if (gl_load_late_extensions() < 0) {
log_error("Failed to load late GL extensions\n");
goto late_extension_failure;
}
uint32_t tf_width = output_get_transformed_width(output);
uint32_t tf_height = output_get_transformed_height(output);
if (create_textured_fbo(&self->frame_fbo[0], GL_RGBA, tf_width,
tf_height) < 0) {
log_error("Failed to create frame FBO 0\n");
goto frame_fbo_failure_0;
}
if (create_textured_fbo(&self->frame_fbo[1], GL_RGBA, tf_width,
tf_height) < 0) {
log_error("Failed to create frame FBO 1\n");
goto frame_fbo_failure_1;
}
if (create_fbo(&self->damage_fbo, GL_R8_EXT, tf_width, tf_height) < 0) {
log_error("Failed to create damage FBO\n");
goto damage_fbo_failure;
}
glBindFramebuffer(GL_FRAMEBUFFER, self->frame_fbo[0].fbo);
switch (input_type) {
case RENDERER_INPUT_DMABUF:
if (gl_compile_shader_program(&self->frame_shader.program,
"dmabuf-vertex.glsl",
"dmabuf-fragment.glsl") < 0)
goto frame_shader_failure;
break;
case RENDERER_INPUT_FB:
if (gl_compile_shader_program(&self->frame_shader.program,
"texture-vertex.glsl",
"texture-fragment.glsl") < 0)
goto frame_shader_failure;
break;
}
if (gl_compile_shader_program(&self->damage_shader.program,
"damage-vertex.glsl",
"damage-fragment.glsl") < 0)
goto damage_shader_failure;
self->frame_shader.u_tex0 =
glGetUniformLocation(self->frame_shader.program, "u_tex0");
self->frame_shader.u_proj =
glGetUniformLocation(self->frame_shader.program, "u_proj");
self->damage_shader.u_tex0 =
glGetUniformLocation(self->damage_shader.program, "u_tex0");
self->damage_shader.u_tex1 =
glGetUniformLocation(self->damage_shader.program, "u_tex1");
self->output = output;
glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &self->read_format);
glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &self->read_type);
glViewport(0, 0,
output_get_transformed_width(output),
output_get_transformed_height(output));
gl_clear();
return 0;
damage_shader_failure:
glDeleteShader(self->frame_shader.program);
frame_shader_failure:
destroy_fbo(&self->damage_fbo);
damage_fbo_failure:
destroy_fbo(&self->frame_fbo[1]);
frame_fbo_failure_1:
destroy_fbo(&self->frame_fbo[0]);
frame_fbo_failure_0:
late_extension_failure:
make_current_failure:
eglDestroyContext(self->display, self->context);
return -1;
}
static inline void append_attr(EGLint* dst, int* i, EGLint name, EGLint value)
{
dst[*i] = name;
i[0] += 1;
dst[*i] = value;
i[0] += 1;
}
static void dmabuf_attr_append_planes(EGLint* dst, int* i,
struct dmabuf_frame* frame)
{
#define APPEND_PLANE_ATTR(n) \
if (frame->n_planes <= n) \
return; \
\
append_attr(dst, i, EGL_DMA_BUF_PLANE##n##_FD_EXT, frame->plane[n].fd); \
append_attr(dst, i, EGL_DMA_BUF_PLANE##n##_OFFSET_EXT, frame->plane[n].offset); \
append_attr(dst, i, EGL_DMA_BUF_PLANE##n##_PITCH_EXT, frame->plane[n].pitch); \
append_attr(dst, i, EGL_DMA_BUF_PLANE##n##_MODIFIER_LO_EXT, frame->plane[n].modifier); \
append_attr(dst, i, EGL_DMA_BUF_PLANE##n##_MODIFIER_HI_EXT, frame->plane[n].modifier >> 32); \
APPEND_PLANE_ATTR(0);
APPEND_PLANE_ATTR(1);
APPEND_PLANE_ATTR(2);
APPEND_PLANE_ATTR(3);
#undef APPEND_PLANE_ATTR
}
int render_dmabuf(struct renderer* self, struct dmabuf_frame* frame)
{
DTRACE_PROBE1(wayvnc, render_dmabuf_start, self);
int index = 0;
EGLint attr[6 + 10 * 4 + 1];
if (frame->n_planes == 0)
return -1;
append_attr(attr, &index, EGL_WIDTH, frame->width);
append_attr(attr, &index, EGL_HEIGHT, frame->height);
append_attr(attr, &index, EGL_LINUX_DRM_FOURCC_EXT, frame->format);
dmabuf_attr_append_planes(attr, &index, frame);
attr[index++] = EGL_NONE;
EGLImageKHR image =
eglCreateImageKHR(self->display, EGL_NO_CONTEXT,
EGL_LINUX_DMA_BUF_EXT, NULL, attr);
if (!image)
return -1;
GLuint tex = 0;
glGenTextures(1, &tex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
eglDestroyImageKHR(self->display, image);
render_frame(self);
glFinish();
glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
glDeleteTextures(1, &tex);
DTRACE_PROBE(wayvnc, render_dmabuf_end);
return 0;
}
int render_framebuffer(struct renderer* self, const void* addr, uint32_t format,
uint32_t width, uint32_t height, uint32_t stride)
{
DTRACE_PROBE1(wayvnc, render_framebuffer_start, self);
GLuint tex = 0;
glGenTextures(1, &tex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
GLenum gl_format;
if (gl_format_from_fourcc(&gl_format, format) < 0)
return -1;
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / 4);
glTexImage2D(GL_TEXTURE_2D, 0, self->read_format, width, height, 0,
gl_format, GL_UNSIGNED_BYTE, addr);
glGenerateMipmap(GL_TEXTURE_2D);
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
render_frame(self);
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteTextures(1, &tex);
DTRACE_PROBE(wayvnc, render_framebuffer_end);
return 0;
}
void renderer_read_pixels(struct renderer* self, void* dst, uint32_t y,
uint32_t height)
{
assert(y + height <= output_get_transformed_height(self->output));
uint32_t width = output_get_transformed_width(self->output);
GLint read_format, read_type;
glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &read_format);
glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &read_type);
glFinish();
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, y, width, height, read_format, read_type, dst);
}
void renderer_read_frame(struct renderer* self, void* dst, uint32_t y,
uint32_t height)
{
DTRACE_PROBE3(wayvnc, render_read_frame_start, self, y, height);
glBindFramebuffer(GL_FRAMEBUFFER, self->frame_fbo[self->frame_index].fbo);
renderer_read_pixels(self, dst, y, height);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
DTRACE_PROBE(wayvnc, render_read_frame_end);
}
void renderer_read_damage(struct renderer* self, void* dst, uint32_t y,
uint32_t height)
{
DTRACE_PROBE1(wayvnc, render_read_damage_start, self);
glBindFramebuffer(GL_FRAMEBUFFER, self->damage_fbo.fbo);
renderer_read_pixels(self, dst, y, height);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
DTRACE_PROBE(wayvnc, render_read_damage_end);
}

View File

@ -31,7 +31,6 @@
#include "screencopy.h" #include "screencopy.h"
#include "smooth.h" #include "smooth.h"
#include "time-util.h" #include "time-util.h"
#include "render.h"
#include "usdt.h" #include "usdt.h"
#define RATE_LIMIT 20.0 // Hz #define RATE_LIMIT 20.0 // Hz
@ -133,9 +132,9 @@ static void screencopy_flags(void* data,
struct zwlr_screencopy_frame_v1* frame, struct zwlr_screencopy_frame_v1* frame,
uint32_t flags) uint32_t flags)
{ {
struct screencopy* self = data;
(void)frame; (void)frame;
// TODO
// self->buffer->y_inverted = !!(flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT); // self->buffer->y_inverted = !!(flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT);
} }
@ -271,14 +270,6 @@ static int screencopy_start(struct frame_capture* fc,
static void screencopy_render(struct frame_capture* fc, static void screencopy_render(struct frame_capture* fc,
struct renderer* renderer, struct nvnc_fb* fb) struct renderer* renderer, struct nvnc_fb* fb)
{ {
/*
uint32_t width = fc->frame_info.width;
uint32_t height = fc->frame_info.height;
uint32_t stride = fc->frame_info.stride;
uint32_t format = fc->frame_info.fourcc_format;
render_framebuffer(renderer, self->pixels, format, width, height, stride);
*/
} }
void screencopy_init(struct screencopy* self) void screencopy_init(struct screencopy* self)

View File

@ -1,15 +0,0 @@
test_inc = include_directories('../include')
test('damage',
executable('test-damage',
[
'test-damage.c',
'../src/damage.c',
],
include_directories: test_inc,
dependencies: [
pixman,
aml,
]
)
)

View File

@ -1,104 +0,0 @@
#include "damage.h"
#include "tst.h"
#include <pixman.h>
static int test_damage_check_row_aligned(void)
{
uint32_t width = 32;
uint8_t row[width];
uint8_t r = 0;
memset(row, 0, width);
damage_check_row(&r, row, width);
ASSERT_FALSE(r);
row[0] = 1;
r = 0;
damage_check_row(&r, row, width);
ASSERT_TRUE(r);
return 0;
}
static int test_damage_check_row_unaligned(void)
{
uint32_t width = 33;
uint8_t row[width];
uint8_t r[2] = { 0 };
memset(row, 0, width);
damage_check_row(r, row, width);
ASSERT_FALSE(r[0]);
ASSERT_FALSE(r[1]);
row[32] = 1;
r[0] = 0;
r[1] = 0;
damage_check_row(r, row, width);
ASSERT_FALSE(r[0]);
ASSERT_TRUE(r[1]);
return 0;
}
static int test_damage_check_tile_row_aligned(void)
{
uint32_t width = 32;
uint32_t height = 32;
uint8_t tile[width * height];
struct pixman_region16 damage;
uint8_t row = 0;
pixman_region_init(&damage);
memset(tile, 0, sizeof(tile));
damage_check_tile_row(&damage, &row, tile, 0, width, height);
ASSERT_FALSE(pixman_region_not_empty(&damage));
row = 0;
pixman_region_clear(&damage);
tile[0] = 1;
damage_check_tile_row(&damage, &row, tile, 0, width, height);
ASSERT_TRUE(pixman_region_not_empty(&damage));
pixman_region_fini(&damage);
return 0;
}
static int test_damage_check_tile_row_unaligned(void)
{
uint32_t width = 33;
uint32_t height = 32;
uint8_t tile[width * height];
struct pixman_region16 damage;
uint8_t row[2] = { 0 };
pixman_region_init(&damage);
memset(tile, 0, sizeof(tile));
damage_check_tile_row(&damage, row, tile, 0, width, height);
ASSERT_FALSE(pixman_region_not_empty(&damage));
row[0] = 0;
row[1] = 0;
pixman_region_clear(&damage);
tile[32] = 1;
damage_check_tile_row(&damage, row, tile, 0, width, height);
ASSERT_TRUE(pixman_region_not_empty(&damage));
pixman_region_fini(&damage);
return 0;
}
int main()
{
int r = 0;
RUN_TEST(test_damage_check_row_aligned);
RUN_TEST(test_damage_check_row_unaligned);
RUN_TEST(test_damage_check_tile_row_aligned);
RUN_TEST(test_damage_check_tile_row_unaligned);
return r;
}