Remove dead code
parent
84c57a7333
commit
0edaded063
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -28,8 +28,6 @@ libm = cc.find_library('m', required: false)
|
|||
librt = cc.find_library('rt', required: false)
|
||||
|
||||
pixman = dependency('pixman-1')
|
||||
egl = dependency('egl')
|
||||
glesv2 = dependency('glesv2')
|
||||
gbm = dependency('gbm')
|
||||
drm = dependency('libdrm')
|
||||
xkbcommon = dependency('xkbcommon')
|
||||
|
@ -59,8 +57,6 @@ subdir('protocols')
|
|||
|
||||
sources = [
|
||||
'src/main.c',
|
||||
'src/render.c',
|
||||
'src/dmabuf.c',
|
||||
'src/strlcpy.c',
|
||||
'src/shm.c',
|
||||
'src/screencopy.c',
|
||||
|
@ -71,7 +67,6 @@ sources = [
|
|||
'src/smooth.c',
|
||||
'src/cfg.c',
|
||||
'src/intset.c',
|
||||
'src/damage.c',
|
||||
'src/buffer.c',
|
||||
'src/pixels.c',
|
||||
'src/pixman-renderer.c',
|
||||
|
@ -85,8 +80,6 @@ dependencies = [
|
|||
librt,
|
||||
pixman,
|
||||
aml,
|
||||
egl,
|
||||
glesv2,
|
||||
gbm,
|
||||
drm,
|
||||
wayland_client,
|
||||
|
@ -124,5 +117,3 @@ executable(
|
|||
include_directories: inc,
|
||||
install: true,
|
||||
)
|
||||
|
||||
subdir('test')
|
||||
|
|
180
src/damage.c
180
src/damage.c
|
@ -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);
|
||||
}
|
||||
}
|
243
src/dmabuf.c
243
src/dmabuf.c
|
@ -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);
|
||||
}
|
143
src/main.c
143
src/main.c
|
@ -28,10 +28,7 @@
|
|||
#include <aml.h>
|
||||
#include <signal.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <wayland-client-protocol.h>
|
||||
#include <wayland-client.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <pixman.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -41,14 +38,11 @@
|
|||
#include <xf86drm.h>
|
||||
|
||||
#include "frame-capture.h"
|
||||
#include "wlr-export-dmabuf-unstable-v1.h"
|
||||
#include "wlr-screencopy-unstable-v1.h"
|
||||
#include "wlr-virtual-pointer-unstable-v1.h"
|
||||
#include "virtual-keyboard-unstable-v1.h"
|
||||
#include "xdg-output-unstable-v1.h"
|
||||
#include "linux-dmabuf-unstable-v1.h"
|
||||
#include "render.h"
|
||||
#include "dmabuf.h"
|
||||
#include "screencopy.h"
|
||||
#include "strlcpy.h"
|
||||
#include "logging.h"
|
||||
|
@ -57,7 +51,6 @@
|
|||
#include "keyboard.h"
|
||||
#include "seat.h"
|
||||
#include "cfg.h"
|
||||
#include "damage.h"
|
||||
#include "pixman-renderer.h"
|
||||
#include "transform-util.h"
|
||||
#include "damage-refinery.h"
|
||||
|
@ -68,12 +61,6 @@
|
|||
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
|
||||
#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 {
|
||||
bool do_exit;
|
||||
|
||||
|
@ -89,11 +76,9 @@ struct wayvnc {
|
|||
|
||||
int pointer_manager_version;
|
||||
|
||||
struct renderer renderer;
|
||||
const struct output* selected_output;
|
||||
const struct seat* selected_seat;
|
||||
|
||||
struct dmabuf_capture dmabuf_backend;
|
||||
struct screencopy screencopy_backend;
|
||||
struct frame_capture* capture_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 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,
|
||||
uint32_t id, const char* interface,
|
||||
uint32_t version)
|
||||
|
@ -165,14 +138,6 @@ static void registry_add(void* data, struct wl_registry* registry,
|
|||
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) {
|
||||
self->screencopy_backend.manager =
|
||||
wl_registry_bind(registry, id,
|
||||
|
@ -292,9 +257,6 @@ void wayvnc_destroy(struct wayvnc* self)
|
|||
if (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);
|
||||
}
|
||||
|
||||
|
@ -348,14 +310,11 @@ static int init_wayland(struct wayvnc* self)
|
|||
wl_display_dispatch(self->display);
|
||||
wl_display_roundtrip(self->display);
|
||||
|
||||
if (!self->dmabuf_backend.manager && !self->screencopy_backend.manager) {
|
||||
log_error("Compositor supports neither screencopy nor export-dmabuf! Exiting.\n");
|
||||
if (!self->screencopy_backend.manager) {
|
||||
log_error("Compositor doesn't support screencopy! Exiting.\n");
|
||||
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.userdata = self;
|
||||
|
||||
|
@ -425,16 +384,6 @@ int init_main_loop(struct wayvnc* self)
|
|||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// uint32_t format = fourcc_from_gl_format(self->renderer.read_format);
|
||||
uint32_t width = output_get_transformed_width(self->selected_output);
|
||||
uint32_t height = output_get_transformed_height(self->selected_output);
|
||||
bool is_first_frame = false;
|
||||
|
||||
if (!self->buffer) {
|
||||
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,
|
||||
self->screencopy_backend.back->width,
|
||||
self->screencopy_backend.back->height);
|
||||
|
||||
is_first_frame = true;
|
||||
} else {
|
||||
// TODO: Reallocate
|
||||
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"
|
||||
"\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"
|
||||
" -k,--keyboard=<layout> Select keyboard layout.\n"
|
||||
" -s,--seat=<name> Select seat by name.\n"
|
||||
|
@ -696,7 +617,6 @@ int main(int argc, char* argv[])
|
|||
int port = 0;
|
||||
|
||||
const char* output_name = NULL;
|
||||
enum frame_capture_backend_type fcbackend = FRAME_CAPTURE_BACKEND_NONE;
|
||||
const char* seat_name = NULL;
|
||||
|
||||
bool overlay_cursor = false;
|
||||
|
@ -706,7 +626,6 @@ int main(int argc, char* argv[])
|
|||
|
||||
static const struct option longopts[] = {
|
||||
{ "config", required_argument, NULL, 'C' },
|
||||
{ "frame-capturing", required_argument, NULL, 'c' },
|
||||
{ "output", required_argument, NULL, 'o' },
|
||||
{ "keyboard", required_argument, NULL, 'k' },
|
||||
{ "seat", required_argument, NULL, 's' },
|
||||
|
@ -724,15 +643,6 @@ int main(int argc, char* argv[])
|
|||
case 'C':
|
||||
cfg_file = optarg;
|
||||
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':
|
||||
output_name = optarg;
|
||||
break;
|
||||
|
@ -821,7 +731,6 @@ int main(int argc, char* argv[])
|
|||
|
||||
self.selected_output = out;
|
||||
self.selected_seat = seat;
|
||||
self.dmabuf_backend.fc.wl_output = out->wl_output;
|
||||
self.screencopy_backend.frame_capture.wl_output = out->wl_output;
|
||||
|
||||
self.keyboard_backend.virtual_keyboard =
|
||||
|
@ -854,16 +763,7 @@ int main(int argc, char* argv[])
|
|||
if (!gbm_device)
|
||||
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();
|
||||
struct aml* aml = aml_new(NULL, 0);
|
||||
if (!aml)
|
||||
goto main_loop_failure;
|
||||
|
||||
|
@ -878,36 +778,13 @@ int main(int argc, char* argv[])
|
|||
if (self.screencopy_backend.manager)
|
||||
screencopy_init(&self.screencopy_backend);
|
||||
|
||||
if (self.dmabuf_backend.manager)
|
||||
dmabuf_capture_init(&self.dmabuf_backend);
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
pixman_region_init(&self.current_damage);
|
||||
|
||||
self.capture_backend->overlay_cursor = overlay_cursor;
|
||||
|
@ -931,13 +808,10 @@ int main(int argc, char* argv[])
|
|||
|
||||
nvnc_display_unref(self.nvnc_display);
|
||||
nvnc_close(self.nvnc);
|
||||
renderer_destroy(&self.renderer);
|
||||
if (zwp_linux_dmabuf)
|
||||
zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf);
|
||||
if (self.screencopy_backend.manager)
|
||||
screencopy_destroy(&self.screencopy_backend);
|
||||
if (self.dmabuf_backend.manager)
|
||||
dmabuf_capture_destroy(&self.dmabuf_backend);
|
||||
wayvnc_destroy(&self);
|
||||
aml_unref(aml);
|
||||
|
||||
|
@ -948,7 +822,6 @@ capture_failure:
|
|||
nvnc_close(self.nvnc);
|
||||
nvnc_failure:
|
||||
main_loop_failure:
|
||||
renderer_destroy(&self.renderer);
|
||||
failure:
|
||||
gbm_device_destroy(gbm_device);
|
||||
if (drm_fd >= 0)
|
||||
|
|
795
src/render.c
795
src/render.c
|
@ -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);
|
||||
}
|
|
@ -31,7 +31,6 @@
|
|||
#include "screencopy.h"
|
||||
#include "smooth.h"
|
||||
#include "time-util.h"
|
||||
#include "render.h"
|
||||
#include "usdt.h"
|
||||
|
||||
#define RATE_LIMIT 20.0 // Hz
|
||||
|
@ -133,9 +132,9 @@ static void screencopy_flags(void* data,
|
|||
struct zwlr_screencopy_frame_v1* frame,
|
||||
uint32_t flags)
|
||||
{
|
||||
struct screencopy* self = data;
|
||||
(void)frame;
|
||||
|
||||
// TODO
|
||||
// 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,
|
||||
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)
|
||||
|
|
|
@ -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,
|
||||
]
|
||||
)
|
||||
)
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue