Compare commits
53 Commits
master
...
pixman-ren
Author | SHA1 | Date |
---|---|---|
Andri Yngvason | 39976ab776 | |
Andri Yngvason | 0ed6ed9e3c | |
Andri Yngvason | a4c093bbdf | |
Andri Yngvason | 5d2dd100b7 | |
Andri Yngvason | b47b3cf7c1 | |
Andri Yngvason | c40cb823d6 | |
Andri Yngvason | 78c1616a46 | |
Andri Yngvason | f49791880f | |
Andri Yngvason | cc0c6246d2 | |
Andri Yngvason | 8a10ffb33f | |
Andri Yngvason | 1786e0d549 | |
Andri Yngvason | cb2a5f543e | |
Andri Yngvason | d23c443e14 | |
Andri Yngvason | 2ebbd86aec | |
Andri Yngvason | 82f7bd93ed | |
Andri Yngvason | cdd021b21c | |
Andri Yngvason | aa1bfb58e9 | |
Andri Yngvason | c2d6a7daa6 | |
Andri Yngvason | 63d15d9fe8 | |
Andri Yngvason | dc568d14e8 | |
Andri Yngvason | 815b6ad52f | |
Andri Yngvason | 023333a4d1 | |
Andri Yngvason | f14eb5a813 | |
Andri Yngvason | 30c0909656 | |
Andri Yngvason | ea10193747 | |
Andri Yngvason | a67c9a6837 | |
Andri Yngvason | 0ea4bd6646 | |
Andri Yngvason | a7283e68fd | |
Andri Yngvason | 0b15b465df | |
Andri Yngvason | 8c9211eed6 | |
Andri Yngvason | c4ca264772 | |
Andri Yngvason | 6a015d9dc0 | |
Andri Yngvason | 3facb3a58e | |
Andri Yngvason | bea97623c9 | |
Andri Yngvason | ef91f040f9 | |
Andri Yngvason | a43fdd4779 | |
Andri Yngvason | f546173bf7 | |
Andri Yngvason | 0f09581686 | |
Andri Yngvason | 473ce5eb23 | |
Andri Yngvason | 17ee85dfd0 | |
Andri Yngvason | ba23559283 | |
Andri Yngvason | 7fda124c69 | |
Andri Yngvason | a4e1a957c9 | |
Andri Yngvason | fe136fcd29 | |
Andri Yngvason | a48b7a66b0 | |
Andri Yngvason | d9ff6292dc | |
Andri Yngvason | 197c165fa4 | |
Andri Yngvason | 1f229c3129 | |
Andri Yngvason | 49a2d578d9 | |
Andri Yngvason | 47a8dc8040 | |
Andri Yngvason | 4740967bfd | |
Andri Yngvason | 5e5806fcf6 | |
Andri Yngvason | 03114c80e2 |
|
@ -4,3 +4,4 @@ subprojects
|
||||||
.clang_complete
|
.clang_complete
|
||||||
.ycm_extra_conf.py
|
.ycm_extra_conf.py
|
||||||
perf.*
|
perf.*
|
||||||
|
*.pem
|
||||||
|
|
|
@ -36,10 +36,10 @@ dnf install wayvnc
|
||||||
## Building
|
## Building
|
||||||
### Runtime Dependencies
|
### Runtime Dependencies
|
||||||
* aml
|
* aml
|
||||||
* EGL
|
* drm
|
||||||
|
* gbm
|
||||||
* libxkbcommon
|
* libxkbcommon
|
||||||
* neatvnc
|
* neatvnc
|
||||||
* OpenGL ES V2.0
|
|
||||||
* pixman
|
* pixman
|
||||||
|
|
||||||
### Build Dependencies
|
### Build Dependencies
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* 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 "sys/queue.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
|
||||||
|
struct wl_buffer;
|
||||||
|
struct gbm_bo;
|
||||||
|
|
||||||
|
enum wv_buffer_type {
|
||||||
|
WV_BUFFER_UNSPEC = 0,
|
||||||
|
WV_BUFFER_SHM,
|
||||||
|
WV_BUFFER_DMABUF,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wv_buffer {
|
||||||
|
enum wv_buffer_type type;
|
||||||
|
TAILQ_ENTRY(wv_buffer) link;
|
||||||
|
|
||||||
|
struct wl_buffer* wl_buffer;
|
||||||
|
|
||||||
|
void* pixels;
|
||||||
|
size_t size;
|
||||||
|
int width, height, stride;
|
||||||
|
uint32_t format;
|
||||||
|
bool y_inverted;
|
||||||
|
|
||||||
|
struct pixman_region16 damage;
|
||||||
|
|
||||||
|
/* The following is only applicable to DMABUF */
|
||||||
|
struct gbm_bo* bo;
|
||||||
|
void* bo_map_handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
TAILQ_HEAD(wv_buffer_queue, wv_buffer);
|
||||||
|
|
||||||
|
struct wv_buffer_pool {
|
||||||
|
struct wv_buffer_queue queue;
|
||||||
|
enum wv_buffer_type type;
|
||||||
|
int width, height, stride;
|
||||||
|
uint32_t format;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wv_buffer* wv_buffer_create(enum wv_buffer_type, int width, int height,
|
||||||
|
int stride, uint32_t fourcc);
|
||||||
|
void wv_buffer_destroy(struct wv_buffer* self);
|
||||||
|
|
||||||
|
int wv_buffer_map(struct wv_buffer* self);
|
||||||
|
void wv_buffer_unmap(struct wv_buffer* self);
|
||||||
|
|
||||||
|
void wv_buffer_damage_rect(struct wv_buffer* self, int x, int y, int width,
|
||||||
|
int height);
|
||||||
|
void wv_buffer_damage_whole(struct wv_buffer* self);
|
||||||
|
void wv_buffer_damage_clear(struct wv_buffer* self);
|
||||||
|
|
||||||
|
struct wv_buffer_pool* wv_buffer_pool_create(enum wv_buffer_type, int width,
|
||||||
|
int height, int stride, uint32_t format);
|
||||||
|
void wv_buffer_pool_destroy(struct wv_buffer_pool* pool);
|
||||||
|
void wv_buffer_pool_resize(struct wv_buffer_pool* pool, enum wv_buffer_type,
|
||||||
|
int width, int height, int stride, uint32_t format);
|
||||||
|
struct wv_buffer* wv_buffer_pool_acquire(struct wv_buffer_pool* pool);
|
||||||
|
void wv_buffer_pool_release(struct wv_buffer_pool* pool,
|
||||||
|
struct wv_buffer* buffer);
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 - 2020 Andri Yngvason
|
* Copyright (c) 2020 Andri Yngvason
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -14,15 +14,24 @@
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
precision mediump float;
|
#pragma once
|
||||||
|
|
||||||
uniform sampler2D u_tex0;
|
#include <stdint.h>
|
||||||
uniform sampler2D u_tex1;
|
|
||||||
|
|
||||||
varying vec2 v_texture;
|
struct pixman_region16;
|
||||||
|
struct wv_buffer;
|
||||||
|
|
||||||
void main()
|
struct damage_refinery {
|
||||||
{
|
uint32_t* hashes;
|
||||||
float r = float(texture2D(u_tex0, v_texture).rgb != texture2D(u_tex1, v_texture).rgb);
|
uint32_t width;
|
||||||
gl_FragColor = vec4(r);
|
uint32_t height;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
int damage_refinery_init(struct damage_refinery* self, uint32_t width,
|
||||||
|
uint32_t height);
|
||||||
|
void damage_refinery_destroy(struct damage_refinery* self);
|
||||||
|
|
||||||
|
void damage_refine(struct damage_refinery* self,
|
||||||
|
struct pixman_region16* refined,
|
||||||
|
struct pixman_region16* hint,
|
||||||
|
const struct wv_buffer* buffer);
|
|
@ -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,84 +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>
|
|
||||||
|
|
||||||
struct wl_output;
|
|
||||||
struct nvnc_fb;
|
|
||||||
struct renderer;
|
|
||||||
|
|
||||||
enum frame_capture_status {
|
|
||||||
CAPTURE_STOPPED = 0,
|
|
||||||
CAPTURE_IN_PROGRESS,
|
|
||||||
CAPTURE_FAILED,
|
|
||||||
CAPTURE_FATAL,
|
|
||||||
CAPTURE_DONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum frame_capture_options {
|
|
||||||
CAPTURE_NOW = 1 << 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct frame_capture {
|
|
||||||
enum frame_capture_status status;
|
|
||||||
|
|
||||||
bool overlay_cursor;
|
|
||||||
struct wl_output* wl_output;
|
|
||||||
|
|
||||||
void* userdata;
|
|
||||||
void (*on_done)(struct frame_capture*);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
uint32_t fourcc_format;
|
|
||||||
uint32_t width;
|
|
||||||
uint32_t height;
|
|
||||||
uint32_t stride;
|
|
||||||
} frame_info;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
uint32_t x;
|
|
||||||
uint32_t y;
|
|
||||||
uint32_t width;
|
|
||||||
uint32_t height;
|
|
||||||
} damage_hint;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
void (*render)(struct frame_capture*, struct renderer*,
|
|
||||||
struct nvnc_fb* fb);
|
|
||||||
int (*start)(struct frame_capture*, enum frame_capture_options);
|
|
||||||
void (*stop)(struct frame_capture*);
|
|
||||||
} backend;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline int frame_capture_start(struct frame_capture* self, enum frame_capture_options options)
|
|
||||||
{
|
|
||||||
return self->backend.start(self, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void frame_capture_stop(struct frame_capture* self)
|
|
||||||
{
|
|
||||||
self->backend.stop(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void frame_capture_render(struct frame_capture* self,
|
|
||||||
struct renderer* renderer,
|
|
||||||
struct nvnc_fb* fb)
|
|
||||||
{
|
|
||||||
return self->backend.render(self, renderer, fb);
|
|
||||||
}
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Joseph Werle
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MURMURHASH_H
|
||||||
|
#define MURMURHASH_H 1
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define MURMURHASH_VERSION "0.0.3"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a murmur hash of `key' based on `seed'
|
||||||
|
* using the MurmurHash3 algorithm
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
murmurhash (const char *, uint32_t, uint32_t);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 - 2020 Andri Yngvason
|
* Copyright (c) 2020 Andri Yngvason
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -14,15 +14,12 @@
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#extension GL_OES_EGL_image_external: require
|
#pragma once
|
||||||
|
|
||||||
precision mediump float;
|
#include <pixman.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <libdrm/drm_fourcc.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
uniform samplerExternalOES u_tex0;
|
enum wl_shm_format fourcc_to_wl_shm(uint32_t in);
|
||||||
|
bool fourcc_to_pixman_fmt(pixman_format_code_t* dst, uint32_t src);
|
||||||
varying vec2 v_texture;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
gl_FragColor = texture2D(u_tex0, v_texture);
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 - 2020 Andri Yngvason
|
* Copyright (c) 2020 Andri Yngvason
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -14,15 +14,14 @@
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
uniform mat2 u_proj;
|
#pragma once
|
||||||
|
|
||||||
attribute vec2 pos;
|
#include <wayland-client.h>
|
||||||
attribute vec2 texture;
|
|
||||||
|
|
||||||
varying vec2 v_texture;
|
struct nvnc_fb;
|
||||||
|
struct wv_buffer;
|
||||||
|
struct pixman_region16;
|
||||||
|
|
||||||
void main()
|
void wv_pixman_render(struct nvnc_fb* dst, const struct wv_buffer* src,
|
||||||
{
|
enum wl_output_transform transform,
|
||||||
v_texture = vec2(texture.s, 1.0 - texture.t);
|
struct pixman_region16* damage);
|
||||||
gl_Position = vec4(u_proj * pos, 0, 1);
|
|
||||||
}
|
|
|
@ -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);
|
|
|
@ -19,8 +19,8 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "wlr-screencopy-unstable-v1.h"
|
#include "wlr-screencopy-unstable-v1.h"
|
||||||
#include "frame-capture.h"
|
|
||||||
#include "smooth.h"
|
#include "smooth.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
struct zwlr_screencopy_manager_v1;
|
struct zwlr_screencopy_manager_v1;
|
||||||
struct zwlr_screencopy_frame_v1;
|
struct zwlr_screencopy_frame_v1;
|
||||||
|
@ -31,23 +31,26 @@ struct aml_timer;
|
||||||
struct renderer;
|
struct renderer;
|
||||||
|
|
||||||
enum screencopy_status {
|
enum screencopy_status {
|
||||||
SCREENCOPY_STATUS_CAPTURING = 0,
|
SCREENCOPY_STOPPED = 0,
|
||||||
SCREENCOPY_STATUS_FATAL,
|
SCREENCOPY_IN_PROGRESS,
|
||||||
SCREENCOPY_STATUS_FAILED,
|
SCREENCOPY_FAILED,
|
||||||
SCREENCOPY_STATUS_DONE,
|
SCREENCOPY_FATAL,
|
||||||
|
SCREENCOPY_DONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct screencopy {
|
struct screencopy {
|
||||||
struct frame_capture frame_capture;
|
enum screencopy_status status;
|
||||||
|
|
||||||
struct wl_shm* wl_shm;
|
struct wv_buffer_pool* pool;
|
||||||
struct wl_buffer* buffer;
|
struct wv_buffer* front;
|
||||||
|
struct wv_buffer* back;
|
||||||
void* pixels;
|
|
||||||
size_t bufsize;
|
|
||||||
|
|
||||||
struct zwlr_screencopy_manager_v1* manager;
|
struct zwlr_screencopy_manager_v1* manager;
|
||||||
struct zwlr_screencopy_frame_v1* frame;
|
struct zwlr_screencopy_frame_v1* frame;
|
||||||
|
int version;
|
||||||
|
|
||||||
|
void* userdata;
|
||||||
|
void (*on_done)(struct screencopy*);
|
||||||
|
|
||||||
uint64_t last_time;
|
uint64_t last_time;
|
||||||
uint64_t start_time;
|
uint64_t start_time;
|
||||||
|
@ -56,7 +59,23 @@ struct screencopy {
|
||||||
struct smooth delay_smoother;
|
struct smooth delay_smoother;
|
||||||
double delay;
|
double delay;
|
||||||
bool is_immediate_copy;
|
bool is_immediate_copy;
|
||||||
|
bool overlay_cursor;
|
||||||
|
struct wl_output* wl_output;
|
||||||
|
|
||||||
|
uint32_t wl_shm_width, wl_shm_height, wl_shm_stride;
|
||||||
|
enum wl_shm_format wl_shm_format;
|
||||||
|
|
||||||
|
bool have_linux_dmabuf;
|
||||||
|
uint32_t dmabuf_width, dmabuf_height;
|
||||||
|
uint32_t fourcc;
|
||||||
|
|
||||||
|
double rate_limit;
|
||||||
};
|
};
|
||||||
|
|
||||||
void screencopy_init(struct screencopy* self);
|
void screencopy_init(struct screencopy* self);
|
||||||
void screencopy_destroy(struct screencopy* self);
|
void screencopy_destroy(struct screencopy* self);
|
||||||
|
|
||||||
|
int screencopy_start(struct screencopy* self);
|
||||||
|
int screencopy_start_immediate(struct screencopy* self);
|
||||||
|
|
||||||
|
void screencopy_stop(struct screencopy* self);
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* 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 <wayland-client.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
|
||||||
|
void wv_region_transform(struct pixman_region16 *dst,
|
||||||
|
struct pixman_region16 *src, enum wl_output_transform transform,
|
||||||
|
int width, int height);
|
||||||
|
|
||||||
|
void wv_pixman_transform_from_wl_output_transform(pixman_transform_t* dst,
|
||||||
|
enum wl_output_transform src, int width, int height);
|
||||||
|
|
||||||
|
enum wl_output_transform wv_output_transform_invert(enum wl_output_transform tr);
|
||||||
|
enum wl_output_transform wv_output_transform_compose(
|
||||||
|
enum wl_output_transform tr_a, enum wl_output_transform tr_b);
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 - 2020 Andri Yngvason
|
* Copyright (c) 2020 Andri Yngvason
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -14,13 +14,7 @@
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
precision mediump float;
|
#pragma once
|
||||||
|
|
||||||
uniform sampler2D u_tex0;
|
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
|
||||||
|
#define ALIGN_UP(n, a) (UDIV_UP(n, a) * a)
|
||||||
varying vec2 v_texture;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
gl_FragColor = texture2D(u_tex0, v_texture);
|
|
||||||
}
|
|
21
meson.build
21
meson.build
|
@ -28,8 +28,8 @@ 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')
|
gbm = dependency('gbm')
|
||||||
glesv2 = dependency('glesv2')
|
drm = dependency('libdrm')
|
||||||
xkbcommon = dependency('xkbcommon')
|
xkbcommon = dependency('xkbcommon')
|
||||||
wayland_client = dependency('wayland-client')
|
wayland_client = dependency('wayland-client')
|
||||||
|
|
||||||
|
@ -57,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',
|
||||||
|
@ -69,7 +67,12 @@ 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/pixels.c',
|
||||||
|
'src/pixman-renderer.c',
|
||||||
|
'src/transform-util.c',
|
||||||
|
'src/damage-refinery.c',
|
||||||
|
'src/murmurhash.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
@ -77,8 +80,8 @@ dependencies = [
|
||||||
librt,
|
librt,
|
||||||
pixman,
|
pixman,
|
||||||
aml,
|
aml,
|
||||||
egl,
|
gbm,
|
||||||
glesv2,
|
drm,
|
||||||
wayland_client,
|
wayland_client,
|
||||||
neatvnc,
|
neatvnc,
|
||||||
xkbcommon,
|
xkbcommon,
|
||||||
|
@ -105,8 +108,6 @@ configure_file(
|
||||||
configuration: config,
|
configuration: config,
|
||||||
)
|
)
|
||||||
|
|
||||||
install_subdir('shaders', install_dir: 'share/wayvnc')
|
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
'wayvnc',
|
'wayvnc',
|
||||||
sources,
|
sources,
|
||||||
|
@ -114,5 +115,3 @@ executable(
|
||||||
include_directories: inc,
|
include_directories: inc,
|
||||||
install: true,
|
install: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
subdir('test')
|
|
||||||
|
|
|
@ -0,0 +1,362 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="linux_dmabuf_unstable_v1">
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2014, 2015 Collabora, Ltd.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice (including the next
|
||||||
|
paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<interface name="zwp_linux_dmabuf_v1" version="3">
|
||||||
|
<description summary="factory for creating dmabuf-based wl_buffers">
|
||||||
|
Following the interfaces from:
|
||||||
|
https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_image_dma_buf_import.txt
|
||||||
|
https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
|
||||||
|
and the Linux DRM sub-system's AddFb2 ioctl.
|
||||||
|
|
||||||
|
This interface offers ways to create generic dmabuf-based
|
||||||
|
wl_buffers. Immediately after a client binds to this interface,
|
||||||
|
the set of supported formats and format modifiers is sent with
|
||||||
|
'format' and 'modifier' events.
|
||||||
|
|
||||||
|
The following are required from clients:
|
||||||
|
|
||||||
|
- Clients must ensure that either all data in the dma-buf is
|
||||||
|
coherent for all subsequent read access or that coherency is
|
||||||
|
correctly handled by the underlying kernel-side dma-buf
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
- Don't make any more attachments after sending the buffer to the
|
||||||
|
compositor. Making more attachments later increases the risk of
|
||||||
|
the compositor not being able to use (re-import) an existing
|
||||||
|
dmabuf-based wl_buffer.
|
||||||
|
|
||||||
|
The underlying graphics stack must ensure the following:
|
||||||
|
|
||||||
|
- The dmabuf file descriptors relayed to the server will stay valid
|
||||||
|
for the whole lifetime of the wl_buffer. This means the server may
|
||||||
|
at any time use those fds to import the dmabuf into any kernel
|
||||||
|
sub-system that might accept it.
|
||||||
|
|
||||||
|
To create a wl_buffer from one or more dmabufs, a client creates a
|
||||||
|
zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params
|
||||||
|
request. All planes required by the intended format are added with
|
||||||
|
the 'add' request. Finally, a 'create' or 'create_immed' request is
|
||||||
|
issued, which has the following outcome depending on the import success.
|
||||||
|
|
||||||
|
The 'create' request,
|
||||||
|
- on success, triggers a 'created' event which provides the final
|
||||||
|
wl_buffer to the client.
|
||||||
|
- on failure, triggers a 'failed' event to convey that the server
|
||||||
|
cannot use the dmabufs received from the client.
|
||||||
|
|
||||||
|
For the 'create_immed' request,
|
||||||
|
- on success, the server immediately imports the added dmabufs to
|
||||||
|
create a wl_buffer. No event is sent from the server in this case.
|
||||||
|
- on failure, the server can choose to either:
|
||||||
|
- terminate the client by raising a fatal error.
|
||||||
|
- mark the wl_buffer as failed, and send a 'failed' event to the
|
||||||
|
client. If the client uses a failed wl_buffer as an argument to any
|
||||||
|
request, the behaviour is compositor implementation-defined.
|
||||||
|
|
||||||
|
Warning! The protocol described in this file is experimental and
|
||||||
|
backward incompatible changes may be made. Backward compatible changes
|
||||||
|
may be added together with the corresponding interface version bump.
|
||||||
|
Backward incompatible changes are done by bumping the version number in
|
||||||
|
the protocol and interface names and resetting the interface version.
|
||||||
|
Once the protocol is to be declared stable, the 'z' prefix and the
|
||||||
|
version number in the protocol and interface names are removed and the
|
||||||
|
interface version number is reset.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="unbind the factory">
|
||||||
|
Objects created through this interface, especially wl_buffers, will
|
||||||
|
remain valid.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="create_params">
|
||||||
|
<description summary="create a temporary object for buffer parameters">
|
||||||
|
This temporary object is used to collect multiple dmabuf handles into
|
||||||
|
a single batch to create a wl_buffer. It can only be used once and
|
||||||
|
should be destroyed after a 'created' or 'failed' event has been
|
||||||
|
received.
|
||||||
|
</description>
|
||||||
|
<arg name="params_id" type="new_id" interface="zwp_linux_buffer_params_v1"
|
||||||
|
summary="the new temporary"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="format">
|
||||||
|
<description summary="supported buffer format">
|
||||||
|
This event advertises one buffer format that the server supports.
|
||||||
|
All the supported formats are advertised once when the client
|
||||||
|
binds to this interface. A roundtrip after binding guarantees
|
||||||
|
that the client has received all supported formats.
|
||||||
|
|
||||||
|
For the definition of the format codes, see the
|
||||||
|
zwp_linux_buffer_params_v1::create request.
|
||||||
|
|
||||||
|
Warning: the 'format' event is likely to be deprecated and replaced
|
||||||
|
with the 'modifier' event introduced in zwp_linux_dmabuf_v1
|
||||||
|
version 3, described below. Please refrain from using the information
|
||||||
|
received from this event.
|
||||||
|
</description>
|
||||||
|
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="modifier" since="3">
|
||||||
|
<description summary="supported buffer format modifier">
|
||||||
|
This event advertises the formats that the server supports, along with
|
||||||
|
the modifiers supported for each format. All the supported modifiers
|
||||||
|
for all the supported formats are advertised once when the client
|
||||||
|
binds to this interface. A roundtrip after binding guarantees that
|
||||||
|
the client has received all supported format-modifier pairs.
|
||||||
|
|
||||||
|
For legacy support, DRM_FORMAT_MOD_INVALID (that is, modifier_hi ==
|
||||||
|
0x00ffffff and modifier_lo == 0xffffffff) is allowed in this event.
|
||||||
|
It indicates that the server can support the format with an implicit
|
||||||
|
modifier. When a plane has DRM_FORMAT_MOD_INVALID as its modifier, it
|
||||||
|
is as if no explicit modifier is specified. The effective modifier
|
||||||
|
will be derived from the dmabuf.
|
||||||
|
|
||||||
|
For the definition of the format and modifier codes, see the
|
||||||
|
zwp_linux_buffer_params_v1::create and zwp_linux_buffer_params_v1::add
|
||||||
|
requests.
|
||||||
|
</description>
|
||||||
|
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
|
||||||
|
<arg name="modifier_hi" type="uint"
|
||||||
|
summary="high 32 bits of layout modifier"/>
|
||||||
|
<arg name="modifier_lo" type="uint"
|
||||||
|
summary="low 32 bits of layout modifier"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwp_linux_buffer_params_v1" version="3">
|
||||||
|
<description summary="parameters for creating a dmabuf-based wl_buffer">
|
||||||
|
This temporary object is a collection of dmabufs and other
|
||||||
|
parameters that together form a single logical buffer. The temporary
|
||||||
|
object may eventually create one wl_buffer unless cancelled by
|
||||||
|
destroying it before requesting 'create'.
|
||||||
|
|
||||||
|
Single-planar formats only require one dmabuf, however
|
||||||
|
multi-planar formats may require more than one dmabuf. For all
|
||||||
|
formats, an 'add' request must be called once per plane (even if the
|
||||||
|
underlying dmabuf fd is identical).
|
||||||
|
|
||||||
|
You must use consecutive plane indices ('plane_idx' argument for 'add')
|
||||||
|
from zero to the number of planes used by the drm_fourcc format code.
|
||||||
|
All planes required by the format must be given exactly once, but can
|
||||||
|
be given in any order. Each plane index can be set only once.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="already_used" value="0"
|
||||||
|
summary="the dmabuf_batch object has already been used to create a wl_buffer"/>
|
||||||
|
<entry name="plane_idx" value="1"
|
||||||
|
summary="plane index out of bounds"/>
|
||||||
|
<entry name="plane_set" value="2"
|
||||||
|
summary="the plane index was already set"/>
|
||||||
|
<entry name="incomplete" value="3"
|
||||||
|
summary="missing or too many planes to create a buffer"/>
|
||||||
|
<entry name="invalid_format" value="4"
|
||||||
|
summary="format not supported"/>
|
||||||
|
<entry name="invalid_dimensions" value="5"
|
||||||
|
summary="invalid width or height"/>
|
||||||
|
<entry name="out_of_bounds" value="6"
|
||||||
|
summary="offset + stride * height goes out of dmabuf bounds"/>
|
||||||
|
<entry name="invalid_wl_buffer" value="7"
|
||||||
|
summary="invalid wl_buffer resulted from importing dmabufs via
|
||||||
|
the create_immed request on given buffer_params"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="delete this object, used or not">
|
||||||
|
Cleans up the temporary data sent to the server for dmabuf-based
|
||||||
|
wl_buffer creation.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="add">
|
||||||
|
<description summary="add a dmabuf to the temporary set">
|
||||||
|
This request adds one dmabuf to the set in this
|
||||||
|
zwp_linux_buffer_params_v1.
|
||||||
|
|
||||||
|
The 64-bit unsigned value combined from modifier_hi and modifier_lo
|
||||||
|
is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the
|
||||||
|
fb modifier, which is defined in drm_mode.h of Linux UAPI.
|
||||||
|
This is an opaque token. Drivers use this token to express tiling,
|
||||||
|
compression, etc. driver-specific modifications to the base format
|
||||||
|
defined by the DRM fourcc code.
|
||||||
|
|
||||||
|
Warning: It should be an error if the format/modifier pair was not
|
||||||
|
advertised with the modifier event. This is not enforced yet because
|
||||||
|
some implementations always accept DRM_FORMAT_MOD_INVALID. Also
|
||||||
|
version 2 of this protocol does not have the modifier event.
|
||||||
|
|
||||||
|
This request raises the PLANE_IDX error if plane_idx is too large.
|
||||||
|
The error PLANE_SET is raised if attempting to set a plane that
|
||||||
|
was already set.
|
||||||
|
</description>
|
||||||
|
<arg name="fd" type="fd" summary="dmabuf fd"/>
|
||||||
|
<arg name="plane_idx" type="uint" summary="plane index"/>
|
||||||
|
<arg name="offset" type="uint" summary="offset in bytes"/>
|
||||||
|
<arg name="stride" type="uint" summary="stride in bytes"/>
|
||||||
|
<arg name="modifier_hi" type="uint"
|
||||||
|
summary="high 32 bits of layout modifier"/>
|
||||||
|
<arg name="modifier_lo" type="uint"
|
||||||
|
summary="low 32 bits of layout modifier"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="flags">
|
||||||
|
<entry name="y_invert" value="1" summary="contents are y-inverted"/>
|
||||||
|
<entry name="interlaced" value="2" summary="content is interlaced"/>
|
||||||
|
<entry name="bottom_first" value="4" summary="bottom field first"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<request name="create">
|
||||||
|
<description summary="create a wl_buffer from the given dmabufs">
|
||||||
|
This asks for creation of a wl_buffer from the added dmabuf
|
||||||
|
buffers. The wl_buffer is not created immediately but returned via
|
||||||
|
the 'created' event if the dmabuf sharing succeeds. The sharing
|
||||||
|
may fail at runtime for reasons a client cannot predict, in
|
||||||
|
which case the 'failed' event is triggered.
|
||||||
|
|
||||||
|
The 'format' argument is a DRM_FORMAT code, as defined by the
|
||||||
|
libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the
|
||||||
|
authoritative source on how the format codes should work.
|
||||||
|
|
||||||
|
The 'flags' is a bitfield of the flags defined in enum "flags".
|
||||||
|
'y_invert' means the that the image needs to be y-flipped.
|
||||||
|
|
||||||
|
Flag 'interlaced' means that the frame in the buffer is not
|
||||||
|
progressive as usual, but interlaced. An interlaced buffer as
|
||||||
|
supported here must always contain both top and bottom fields.
|
||||||
|
The top field always begins on the first pixel row. The temporal
|
||||||
|
ordering between the two fields is top field first, unless
|
||||||
|
'bottom_first' is specified. It is undefined whether 'bottom_first'
|
||||||
|
is ignored if 'interlaced' is not set.
|
||||||
|
|
||||||
|
This protocol does not convey any information about field rate,
|
||||||
|
duration, or timing, other than the relative ordering between the
|
||||||
|
two fields in one buffer. A compositor may have to estimate the
|
||||||
|
intended field rate from the incoming buffer rate. It is undefined
|
||||||
|
whether the time of receiving wl_surface.commit with a new buffer
|
||||||
|
attached, applying the wl_surface state, wl_surface.frame callback
|
||||||
|
trigger, presentation, or any other point in the compositor cycle
|
||||||
|
is used to measure the frame or field times. There is no support
|
||||||
|
for detecting missed or late frames/fields/buffers either, and
|
||||||
|
there is no support whatsoever for cooperating with interlaced
|
||||||
|
compositor output.
|
||||||
|
|
||||||
|
The composited image quality resulting from the use of interlaced
|
||||||
|
buffers is explicitly undefined. A compositor may use elaborate
|
||||||
|
hardware features or software to deinterlace and create progressive
|
||||||
|
output frames from a sequence of interlaced input buffers, or it
|
||||||
|
may produce substandard image quality. However, compositors that
|
||||||
|
cannot guarantee reasonable image quality in all cases are recommended
|
||||||
|
to just reject all interlaced buffers.
|
||||||
|
|
||||||
|
Any argument errors, including non-positive width or height,
|
||||||
|
mismatch between the number of planes and the format, bad
|
||||||
|
format, bad offset or stride, may be indicated by fatal protocol
|
||||||
|
errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS,
|
||||||
|
OUT_OF_BOUNDS.
|
||||||
|
|
||||||
|
Dmabuf import errors in the server that are not obvious client
|
||||||
|
bugs are returned via the 'failed' event as non-fatal. This
|
||||||
|
allows attempting dmabuf sharing and falling back in the client
|
||||||
|
if it fails.
|
||||||
|
|
||||||
|
This request can be sent only once in the object's lifetime, after
|
||||||
|
which the only legal request is destroy. This object should be
|
||||||
|
destroyed after issuing a 'create' request. Attempting to use this
|
||||||
|
object after issuing 'create' raises ALREADY_USED protocol error.
|
||||||
|
|
||||||
|
It is not mandatory to issue 'create'. If a client wants to
|
||||||
|
cancel the buffer creation, it can just destroy this object.
|
||||||
|
</description>
|
||||||
|
<arg name="width" type="int" summary="base plane width in pixels"/>
|
||||||
|
<arg name="height" type="int" summary="base plane height in pixels"/>
|
||||||
|
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
|
||||||
|
<arg name="flags" type="uint" summary="see enum flags"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="created">
|
||||||
|
<description summary="buffer creation succeeded">
|
||||||
|
This event indicates that the attempted buffer creation was
|
||||||
|
successful. It provides the new wl_buffer referencing the dmabuf(s).
|
||||||
|
|
||||||
|
Upon receiving this event, the client should destroy the
|
||||||
|
zlinux_dmabuf_params object.
|
||||||
|
</description>
|
||||||
|
<arg name="buffer" type="new_id" interface="wl_buffer"
|
||||||
|
summary="the newly created wl_buffer"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="failed">
|
||||||
|
<description summary="buffer creation failed">
|
||||||
|
This event indicates that the attempted buffer creation has
|
||||||
|
failed. It usually means that one of the dmabuf constraints
|
||||||
|
has not been fulfilled.
|
||||||
|
|
||||||
|
Upon receiving this event, the client should destroy the
|
||||||
|
zlinux_buffer_params object.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="create_immed" since="2">
|
||||||
|
<description summary="immediately create a wl_buffer from the given
|
||||||
|
dmabufs">
|
||||||
|
This asks for immediate creation of a wl_buffer by importing the
|
||||||
|
added dmabufs.
|
||||||
|
|
||||||
|
In case of import success, no event is sent from the server, and the
|
||||||
|
wl_buffer is ready to be used by the client.
|
||||||
|
|
||||||
|
Upon import failure, either of the following may happen, as seen fit
|
||||||
|
by the implementation:
|
||||||
|
- the client is terminated with one of the following fatal protocol
|
||||||
|
errors:
|
||||||
|
- INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS,
|
||||||
|
in case of argument errors such as mismatch between the number
|
||||||
|
of planes and the format, bad format, non-positive width or
|
||||||
|
height, or bad offset or stride.
|
||||||
|
- INVALID_WL_BUFFER, in case the cause for failure is unknown or
|
||||||
|
plaform specific.
|
||||||
|
- the server creates an invalid wl_buffer, marks it as failed and
|
||||||
|
sends a 'failed' event to the client. The result of using this
|
||||||
|
invalid wl_buffer as an argument in any request by the client is
|
||||||
|
defined by the compositor implementation.
|
||||||
|
|
||||||
|
This takes the same arguments as a 'create' request, and obeys the
|
||||||
|
same restrictions.
|
||||||
|
</description>
|
||||||
|
<arg name="buffer_id" type="new_id" interface="wl_buffer"
|
||||||
|
summary="id for the newly created wl_buffer"/>
|
||||||
|
<arg name="width" type="int" summary="base plane width in pixels"/>
|
||||||
|
<arg name="height" type="int" summary="base plane height in pixels"/>
|
||||||
|
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
|
||||||
|
<arg name="flags" type="uint" summary="see enum flags"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
</protocol>
|
|
@ -19,6 +19,7 @@ client_protocols = [
|
||||||
'wlr-virtual-pointer-unstable-v1.xml',
|
'wlr-virtual-pointer-unstable-v1.xml',
|
||||||
'virtual-keyboard-unstable-v1.xml',
|
'virtual-keyboard-unstable-v1.xml',
|
||||||
'xdg-output-unstable-v1.xml',
|
'xdg-output-unstable-v1.xml',
|
||||||
|
'linux-dmabuf-unstable-v1.xml',
|
||||||
]
|
]
|
||||||
|
|
||||||
client_protos_src = []
|
client_protos_src = []
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
interface version number is reset.
|
interface version number is reset.
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<interface name="zwlr_screencopy_manager_v1" version="2">
|
<interface name="zwlr_screencopy_manager_v1" version="3">
|
||||||
<description summary="manager to inform clients and begin capturing">
|
<description summary="manager to inform clients and begin capturing">
|
||||||
This object is a manager which offers requests to start capturing from a
|
This object is a manager which offers requests to start capturing from a
|
||||||
source.
|
source.
|
||||||
|
@ -80,13 +80,18 @@
|
||||||
</request>
|
</request>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
||||||
<interface name="zwlr_screencopy_frame_v1" version="2">
|
<interface name="zwlr_screencopy_frame_v1" version="3">
|
||||||
<description summary="a frame ready for copy">
|
<description summary="a frame ready for copy">
|
||||||
This object represents a single frame.
|
This object represents a single frame.
|
||||||
|
|
||||||
When created, a "buffer" event will be sent. The client will then be able
|
When created, a series of buffer events will be sent, each representing a
|
||||||
to send a "copy" request. If the capture is successful, the compositor
|
supported buffer type. The "buffer_done" event is sent afterwards to
|
||||||
will send a "flags" followed by a "ready" event.
|
indicate that all supported buffer types have been enumerated. The client
|
||||||
|
will then be able to send a "copy" request. If the capture is successful,
|
||||||
|
the compositor will send a "flags" followed by a "ready" event.
|
||||||
|
|
||||||
|
For objects version 2 or lower, wl_shm buffers are always supported, ie.
|
||||||
|
the "buffer" event is guaranteed to be sent.
|
||||||
|
|
||||||
If the capture failed, the "failed" event is sent. This can happen anytime
|
If the capture failed, the "failed" event is sent. This can happen anytime
|
||||||
before the "ready" event.
|
before the "ready" event.
|
||||||
|
@ -96,14 +101,12 @@
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<event name="buffer">
|
<event name="buffer">
|
||||||
<description summary="buffer information">
|
<description summary="wl_shm buffer information">
|
||||||
Provides information about the frame's buffer. This event is sent once
|
Provides information about wl_shm buffer parameters that need to be
|
||||||
as soon as the frame is created.
|
used for this frame. This event is sent once after the frame is created
|
||||||
|
if wl_shm buffers are supported.
|
||||||
The client should then create a buffer with the provided attributes, and
|
|
||||||
send a "copy" request.
|
|
||||||
</description>
|
</description>
|
||||||
<arg name="format" type="uint" summary="buffer format"/>
|
<arg name="format" type="uint" enum="wl_shm.format" summary="buffer format"/>
|
||||||
<arg name="width" type="uint" summary="buffer width"/>
|
<arg name="width" type="uint" summary="buffer width"/>
|
||||||
<arg name="height" type="uint" summary="buffer height"/>
|
<arg name="height" type="uint" summary="buffer height"/>
|
||||||
<arg name="stride" type="uint" summary="buffer stride"/>
|
<arg name="stride" type="uint" summary="buffer stride"/>
|
||||||
|
@ -112,8 +115,9 @@
|
||||||
<request name="copy">
|
<request name="copy">
|
||||||
<description summary="copy the frame">
|
<description summary="copy the frame">
|
||||||
Copy the frame to the supplied buffer. The buffer must have a the
|
Copy the frame to the supplied buffer. The buffer must have a the
|
||||||
correct size, see zwlr_screencopy_frame_v1.buffer. The buffer needs to
|
correct size, see zwlr_screencopy_frame_v1.buffer and
|
||||||
have a supported format.
|
zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a
|
||||||
|
supported format.
|
||||||
|
|
||||||
If the frame is successfully copied, a "flags" and a "ready" events are
|
If the frame is successfully copied, a "flags" and a "ready" events are
|
||||||
sent. Otherwise, a "failed" event is sent.
|
sent. Otherwise, a "failed" event is sent.
|
||||||
|
@ -203,5 +207,26 @@
|
||||||
<arg name="width" type="uint" summary="current width"/>
|
<arg name="width" type="uint" summary="current width"/>
|
||||||
<arg name="height" type="uint" summary="current height"/>
|
<arg name="height" type="uint" summary="current height"/>
|
||||||
</event>
|
</event>
|
||||||
|
|
||||||
|
<!-- Version 3 additions -->
|
||||||
|
<event name="linux_dmabuf" since="3">
|
||||||
|
<description summary="linux-dmabuf buffer information">
|
||||||
|
Provides information about linux-dmabuf buffer parameters that need to
|
||||||
|
be used for this frame. This event is sent once after the frame is
|
||||||
|
created if linux-dmabuf buffers are supported.
|
||||||
|
</description>
|
||||||
|
<arg name="format" type="uint" summary="fourcc pixel format"/>
|
||||||
|
<arg name="width" type="uint" summary="buffer width"/>
|
||||||
|
<arg name="height" type="uint" summary="buffer height"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="buffer_done" since="3">
|
||||||
|
<description summary="all buffer types reported">
|
||||||
|
This event is sent once after all buffer events have been sent.
|
||||||
|
|
||||||
|
The client should proceed to create a buffer of one of the supported
|
||||||
|
types, and send a "copy" request.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
</interface>
|
</interface>
|
||||||
</protocol>
|
</protocol>
|
||||||
|
|
|
@ -1,26 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
attribute vec2 pos;
|
|
||||||
attribute vec2 texture;
|
|
||||||
|
|
||||||
varying vec2 v_texture;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
v_texture = vec2(texture.s, 1.0 - texture.t);
|
|
||||||
gl_Position = vec4(pos, 0, 1);
|
|
||||||
}
|
|
|
@ -1,28 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
uniform mat2 u_proj;
|
|
||||||
|
|
||||||
attribute vec2 pos;
|
|
||||||
attribute vec2 texture;
|
|
||||||
|
|
||||||
varying vec2 v_texture;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
v_texture = texture;
|
|
||||||
gl_Position = vec4(u_proj * pos, 0, 1);
|
|
||||||
}
|
|
|
@ -0,0 +1,354 @@
|
||||||
|
/*
|
||||||
|
* 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 <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <libdrm/drm_fourcc.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <gbm.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
|
||||||
|
#include "linux-dmabuf-unstable-v1.h"
|
||||||
|
#include "shm.h"
|
||||||
|
#include "sys/queue.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "pixels.h"
|
||||||
|
|
||||||
|
extern struct wl_shm* wl_shm;
|
||||||
|
extern struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf;
|
||||||
|
extern struct gbm_device* gbm_device;
|
||||||
|
|
||||||
|
struct wv_buffer* wv_buffer_create_shm(int width,
|
||||||
|
int height, int stride, uint32_t fourcc)
|
||||||
|
{
|
||||||
|
assert(wl_shm);
|
||||||
|
enum wl_shm_format wl_fmt = fourcc_to_wl_shm(fourcc);
|
||||||
|
|
||||||
|
struct wv_buffer* self = calloc(1, sizeof(*self));
|
||||||
|
if (!self)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
self->type = WV_BUFFER_SHM;
|
||||||
|
self->width = width;
|
||||||
|
self->height = height;
|
||||||
|
self->stride = stride;
|
||||||
|
self->format = fourcc;
|
||||||
|
|
||||||
|
self->size = height * stride;
|
||||||
|
int fd = shm_alloc_fd(self->size);
|
||||||
|
if (fd < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
self->pixels = mmap(NULL, self->size, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED, fd, 0);
|
||||||
|
if (!self->pixels)
|
||||||
|
goto mmap_failure;
|
||||||
|
|
||||||
|
struct wl_shm_pool* pool = wl_shm_create_pool(wl_shm, fd, self->size);
|
||||||
|
if (!pool)
|
||||||
|
goto pool_failure;
|
||||||
|
|
||||||
|
self->wl_buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
|
||||||
|
stride, wl_fmt);
|
||||||
|
wl_shm_pool_destroy(pool);
|
||||||
|
if (!self->wl_buffer)
|
||||||
|
goto shm_failure;
|
||||||
|
|
||||||
|
pixman_region_init(&self->damage);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return self;
|
||||||
|
|
||||||
|
shm_failure:
|
||||||
|
pool_failure:
|
||||||
|
munmap(self->pixels, self->size);
|
||||||
|
mmap_failure:
|
||||||
|
close(fd);
|
||||||
|
failure:
|
||||||
|
free(self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wv_buffer* wv_buffer_create_dmabuf(int width, int height,
|
||||||
|
uint32_t fourcc)
|
||||||
|
{
|
||||||
|
assert(zwp_linux_dmabuf);
|
||||||
|
|
||||||
|
struct wv_buffer* self = calloc(1, sizeof(*self));
|
||||||
|
if (!self)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
self->type = WV_BUFFER_DMABUF;
|
||||||
|
self->width = width;
|
||||||
|
self->height = height;
|
||||||
|
self->format = fourcc;
|
||||||
|
|
||||||
|
self->bo = gbm_bo_create(gbm_device, width, height, fourcc,
|
||||||
|
GBM_BO_USE_RENDERING);
|
||||||
|
if (!self->bo)
|
||||||
|
goto bo_failure;
|
||||||
|
|
||||||
|
struct zwp_linux_buffer_params_v1* params;
|
||||||
|
params = zwp_linux_dmabuf_v1_create_params(zwp_linux_dmabuf);
|
||||||
|
if (!params)
|
||||||
|
goto params_failure;
|
||||||
|
|
||||||
|
uint32_t offset = gbm_bo_get_offset(self->bo, 0);
|
||||||
|
uint32_t stride = gbm_bo_get_stride(self->bo);
|
||||||
|
uint64_t mod = gbm_bo_get_modifier(self->bo);
|
||||||
|
int fd = gbm_bo_get_fd(self->bo);
|
||||||
|
if (fd < 0)
|
||||||
|
goto fd_failure;
|
||||||
|
|
||||||
|
zwp_linux_buffer_params_v1_add(params, fd, 0, offset, stride,
|
||||||
|
mod >> 32, mod & 0xffffffff);
|
||||||
|
self->wl_buffer = zwp_linux_buffer_params_v1_create_immed(params, width,
|
||||||
|
height, fourcc, /* flags */ 0);
|
||||||
|
zwp_linux_buffer_params_v1_destroy(params);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
if (!self->wl_buffer)
|
||||||
|
goto buffer_failure;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
|
||||||
|
buffer_failure:
|
||||||
|
fd_failure:
|
||||||
|
zwp_linux_buffer_params_v1_destroy(params);
|
||||||
|
params_failure:
|
||||||
|
gbm_bo_destroy(self->bo);
|
||||||
|
bo_failure:
|
||||||
|
free(self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wv_buffer* wv_buffer_create(enum wv_buffer_type type, int width,
|
||||||
|
int height, int stride, uint32_t fourcc)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case WV_BUFFER_SHM:
|
||||||
|
return wv_buffer_create_shm(width, height, stride, fourcc);
|
||||||
|
case WV_BUFFER_DMABUF:
|
||||||
|
return wv_buffer_create_dmabuf(width, height, fourcc);
|
||||||
|
case WV_BUFFER_UNSPEC:;
|
||||||
|
}
|
||||||
|
|
||||||
|
abort();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wv_buffer_destroy_shm(struct wv_buffer* self)
|
||||||
|
{
|
||||||
|
wl_buffer_destroy(self->wl_buffer);
|
||||||
|
munmap(self->pixels, self->size);
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wv_buffer_destroy_dmabuf(struct wv_buffer* self)
|
||||||
|
{
|
||||||
|
wl_buffer_destroy(self->wl_buffer);
|
||||||
|
gbm_bo_destroy(self->bo);
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wv_buffer_destroy(struct wv_buffer* self)
|
||||||
|
{
|
||||||
|
pixman_region_fini(&self->damage);
|
||||||
|
wv_buffer_unmap(self);
|
||||||
|
|
||||||
|
switch (self->type) {
|
||||||
|
case WV_BUFFER_SHM:
|
||||||
|
wv_buffer_destroy_shm(self);
|
||||||
|
return;
|
||||||
|
case WV_BUFFER_DMABUF:
|
||||||
|
wv_buffer_destroy_dmabuf(self);
|
||||||
|
return;
|
||||||
|
case WV_BUFFER_UNSPEC:;
|
||||||
|
}
|
||||||
|
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wv_buffer_map_dmabuf(struct wv_buffer* self)
|
||||||
|
{
|
||||||
|
if (self->bo_map_handle)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
uint32_t stride = 0;
|
||||||
|
self->pixels = gbm_bo_map(self->bo, 0, 0, self->width, self->height,
|
||||||
|
GBM_BO_TRANSFER_READ, &stride, &self->bo_map_handle);
|
||||||
|
self->stride = stride;
|
||||||
|
if (self->pixels)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
self->bo_map_handle = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wv_buffer_map(struct wv_buffer* self)
|
||||||
|
{
|
||||||
|
switch (self->type) {
|
||||||
|
case WV_BUFFER_SHM:
|
||||||
|
return 0;
|
||||||
|
case WV_BUFFER_DMABUF:
|
||||||
|
return wv_buffer_map_dmabuf(self);
|
||||||
|
case WV_BUFFER_UNSPEC:;
|
||||||
|
}
|
||||||
|
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wv_buffer_unmap_dmabuf(struct wv_buffer* self)
|
||||||
|
{
|
||||||
|
if (self->bo_map_handle)
|
||||||
|
gbm_bo_unmap(self->bo, self->bo_map_handle);
|
||||||
|
self->bo_map_handle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wv_buffer_unmap(struct wv_buffer* self)
|
||||||
|
{
|
||||||
|
switch (self->type) {
|
||||||
|
case WV_BUFFER_SHM:
|
||||||
|
return;
|
||||||
|
case WV_BUFFER_DMABUF:
|
||||||
|
return wv_buffer_unmap_dmabuf(self);
|
||||||
|
case WV_BUFFER_UNSPEC:;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wv_buffer_damage_rect(struct wv_buffer* self, int x, int y, int width,
|
||||||
|
int height)
|
||||||
|
{
|
||||||
|
pixman_region_union_rect(&self->damage, &self->damage, x, y, width,
|
||||||
|
height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wv_buffer_damage_whole(struct wv_buffer* self)
|
||||||
|
{
|
||||||
|
wv_buffer_damage_rect(self, 0, 0, self->width, self->height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wv_buffer_damage_clear(struct wv_buffer* self)
|
||||||
|
{
|
||||||
|
pixman_region_clear(&self->damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wv_buffer_pool* wv_buffer_pool_create(enum wv_buffer_type type,
|
||||||
|
int width, int height, int stride, uint32_t format)
|
||||||
|
{
|
||||||
|
struct wv_buffer_pool* self = calloc(1, sizeof(*self));
|
||||||
|
if (!self)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
TAILQ_INIT(&self->queue);
|
||||||
|
self->type = type;
|
||||||
|
self->width = width;
|
||||||
|
self->height = height;
|
||||||
|
self->stride = stride;
|
||||||
|
self->format = format;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wv_buffer_pool_clear(struct wv_buffer_pool* pool)
|
||||||
|
{
|
||||||
|
while (!TAILQ_EMPTY(&pool->queue)) {
|
||||||
|
struct wv_buffer* buffer = TAILQ_FIRST(&pool->queue);
|
||||||
|
TAILQ_REMOVE(&pool->queue, buffer, link);
|
||||||
|
wv_buffer_destroy(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wv_buffer_pool_destroy(struct wv_buffer_pool* pool)
|
||||||
|
{
|
||||||
|
wv_buffer_pool_clear(pool);
|
||||||
|
free(pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wv_buffer_pool_resize(struct wv_buffer_pool* pool,
|
||||||
|
enum wv_buffer_type type, int width, int height, int stride,
|
||||||
|
uint32_t format)
|
||||||
|
{
|
||||||
|
if (pool->type != type || pool->width != width || pool->height != height
|
||||||
|
|| pool->stride != stride || pool->format != format) {
|
||||||
|
wv_buffer_pool_clear(pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
pool->type = type;
|
||||||
|
pool->width = width;
|
||||||
|
pool->height = height;
|
||||||
|
pool->stride = stride;
|
||||||
|
pool->format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool wv_buffer_pool_match_buffer(struct wv_buffer_pool* pool,
|
||||||
|
struct wv_buffer* buffer)
|
||||||
|
{
|
||||||
|
if (pool->type != buffer->type)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (pool->type) {
|
||||||
|
case WV_BUFFER_SHM:
|
||||||
|
if (pool->stride != buffer->stride)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* fall-through */
|
||||||
|
case WV_BUFFER_DMABUF:
|
||||||
|
if (pool->width != buffer->width
|
||||||
|
|| pool->height != buffer->height
|
||||||
|
|| pool->format != buffer->format)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
case WV_BUFFER_UNSPEC:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wv_buffer* wv_buffer_pool_acquire(struct wv_buffer_pool* pool)
|
||||||
|
{
|
||||||
|
struct wv_buffer* buffer = TAILQ_FIRST(&pool->queue);
|
||||||
|
if (buffer) {
|
||||||
|
assert(wv_buffer_pool_match_buffer(pool, buffer));
|
||||||
|
TAILQ_REMOVE(&pool->queue, buffer, link);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wv_buffer_create(pool->type, pool->width, pool->height,
|
||||||
|
pool->stride, pool->format);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wv_buffer_pool_release(struct wv_buffer_pool* pool,
|
||||||
|
struct wv_buffer* buffer)
|
||||||
|
{
|
||||||
|
wv_buffer_damage_clear(buffer);
|
||||||
|
wv_buffer_unmap(buffer);
|
||||||
|
|
||||||
|
if (wv_buffer_pool_match_buffer(pool, buffer)) {
|
||||||
|
TAILQ_INSERT_TAIL(&pool->queue, buffer, link);
|
||||||
|
} else {
|
||||||
|
wv_buffer_destroy(buffer);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* 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 <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
|
||||||
|
#include "damage-refinery.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "murmurhash.h"
|
||||||
|
|
||||||
|
#define HASH_SEED 0
|
||||||
|
|
||||||
|
int damage_refinery_init(struct damage_refinery* self, uint32_t width,
|
||||||
|
uint32_t height)
|
||||||
|
{
|
||||||
|
self->width = width;
|
||||||
|
self->height = height;
|
||||||
|
|
||||||
|
uint32_t twidth = UDIV_UP(width, 32);
|
||||||
|
uint32_t theight = UDIV_UP(height, 32);
|
||||||
|
|
||||||
|
self->hashes = calloc(twidth * theight, sizeof(*self->hashes));
|
||||||
|
if (!self->hashes)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void damage_refinery_destroy(struct damage_refinery* self)
|
||||||
|
{
|
||||||
|
free(self->hashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t damage_hash_tile(struct damage_refinery* self, uint32_t tx,
|
||||||
|
uint32_t ty, const struct wv_buffer* buffer)
|
||||||
|
{
|
||||||
|
// TODO: Support different pixel sizes
|
||||||
|
uint32_t* pixels = buffer->pixels;
|
||||||
|
int pixel_stride = buffer->stride / 4;
|
||||||
|
|
||||||
|
if (buffer->y_inverted) {
|
||||||
|
pixels += (buffer->height - 1) * pixel_stride;
|
||||||
|
pixel_stride *= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x_start = tx * 32;
|
||||||
|
int x_stop = MIN((tx + 1) * 32, self->width);
|
||||||
|
int y_start = ty * 32;
|
||||||
|
int y_stop = MIN((ty + 1) * 32, self->height);
|
||||||
|
|
||||||
|
uint32_t hash = 0;
|
||||||
|
|
||||||
|
for (int y = y_start; y < y_stop; ++y)
|
||||||
|
hash = murmurhash((void*)&(pixels[x_start + y * pixel_stride]),
|
||||||
|
4 * (x_stop - x_start), hash);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t* damage_tile_hash_ptr(struct damage_refinery* self,
|
||||||
|
uint32_t tx, uint32_t ty)
|
||||||
|
{
|
||||||
|
uint32_t twidth = UDIV_UP(self->width, 32);
|
||||||
|
return &self->hashes[tx + ty * twidth];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void damage_refine_tile(struct damage_refinery* self,
|
||||||
|
struct pixman_region16* refined, uint32_t tx, uint32_t ty,
|
||||||
|
const struct wv_buffer* buffer)
|
||||||
|
{
|
||||||
|
uint32_t hash = damage_hash_tile(self, tx, ty, buffer);
|
||||||
|
uint32_t* old_hash_ptr = damage_tile_hash_ptr(self, tx, ty);
|
||||||
|
int is_damaged = hash != *old_hash_ptr;
|
||||||
|
*old_hash_ptr = hash;
|
||||||
|
|
||||||
|
if (is_damaged)
|
||||||
|
pixman_region_union_rect(refined, refined, tx * 32, ty * 32, 32,
|
||||||
|
32);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tile_region_from_region(struct pixman_region16* dst,
|
||||||
|
struct pixman_region16* src)
|
||||||
|
{
|
||||||
|
int n_rects = 0;
|
||||||
|
struct pixman_box16* rects = pixman_region_rectangles(src, &n_rects);
|
||||||
|
|
||||||
|
for (int i = 0; i < n_rects; ++i) {
|
||||||
|
int x1 = rects[i].x1 / 32;
|
||||||
|
int y1 = rects[i].y1 / 32;
|
||||||
|
int x2 = UDIV_UP(rects[i].x2, 32);
|
||||||
|
int y2 = UDIV_UP(rects[i].y2, 32);
|
||||||
|
|
||||||
|
pixman_region_union_rect(dst, dst, x1, y1, x2 - x1, y2 - y1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void damage_refine(struct damage_refinery* self,
|
||||||
|
struct pixman_region16* refined,
|
||||||
|
struct pixman_region16* hint,
|
||||||
|
const struct wv_buffer* buffer)
|
||||||
|
{
|
||||||
|
assert(self->width == (uint32_t)buffer->width &&
|
||||||
|
self->height == (uint32_t)buffer->height);
|
||||||
|
|
||||||
|
struct pixman_region16 tile_region;
|
||||||
|
pixman_region_init(&tile_region);
|
||||||
|
tile_region_from_region(&tile_region, hint);
|
||||||
|
|
||||||
|
int n_rects = 0;
|
||||||
|
struct pixman_box16* rects = pixman_region_rectangles(&tile_region,
|
||||||
|
&n_rects);
|
||||||
|
|
||||||
|
for (int i = 0; i < n_rects; ++i)
|
||||||
|
for (int ty = rects[i].y1; ty < rects[i].y2; ++ty)
|
||||||
|
for (int tx = rects[i].x1; tx < rects[i].x2; ++tx)
|
||||||
|
damage_refine_tile(self, refined, tx, ty, buffer);
|
||||||
|
|
||||||
|
pixman_region_fini(&tile_region);
|
||||||
|
pixman_region_intersect_rect(refined, refined, 0, 0, self->width,
|
||||||
|
self->height);
|
||||||
|
}
|
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);
|
|
||||||
}
|
|
355
src/main.c
355
src/main.c
|
@ -28,21 +28,20 @@
|
||||||
#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/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <gbm.h>
|
||||||
|
#include <xf86drm.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 "render.h"
|
#include "linux-dmabuf-unstable-v1.h"
|
||||||
#include "dmabuf.h"
|
|
||||||
#include "screencopy.h"
|
#include "screencopy.h"
|
||||||
#include "strlcpy.h"
|
#include "strlcpy.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
@ -51,20 +50,14 @@
|
||||||
#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 "transform-util.h"
|
||||||
|
#include "damage-refinery.h"
|
||||||
|
#include "usdt.h"
|
||||||
|
|
||||||
#define DEFAULT_ADDRESS "127.0.0.1"
|
#define DEFAULT_ADDRESS "127.0.0.1"
|
||||||
#define DEFAULT_PORT 5900
|
#define DEFAULT_PORT 5900
|
||||||
|
|
||||||
#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 {
|
struct wayvnc {
|
||||||
bool do_exit;
|
bool do_exit;
|
||||||
|
|
||||||
|
@ -80,13 +73,10 @@ 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;
|
||||||
struct screencopy screencopy_backend;
|
|
||||||
struct frame_capture* capture_backend;
|
|
||||||
struct pointer pointer_backend;
|
struct pointer pointer_backend;
|
||||||
struct keyboard keyboard_backend;
|
struct keyboard keyboard_backend;
|
||||||
|
|
||||||
|
@ -97,26 +87,19 @@ struct wayvnc {
|
||||||
struct nvnc_display* nvnc_display;
|
struct nvnc_display* nvnc_display;
|
||||||
struct nvnc_fb* buffer;
|
struct nvnc_fb* buffer;
|
||||||
|
|
||||||
|
struct damage_refinery damage_refinery;
|
||||||
struct pixman_region16 current_damage;
|
struct pixman_region16 current_damage;
|
||||||
|
|
||||||
const char* kb_layout;
|
const char* kb_layout;
|
||||||
};
|
};
|
||||||
|
|
||||||
void wayvnc_exit(struct wayvnc* self);
|
void wayvnc_exit(struct wayvnc* self);
|
||||||
void on_capture_done(struct frame_capture* capture);
|
void on_capture_done(struct screencopy* sc);
|
||||||
static void on_render(struct nvnc_display* display, struct nvnc_fb* fb);
|
static void on_render(struct nvnc_display* display, struct nvnc_fb* fb);
|
||||||
|
|
||||||
static enum frame_capture_backend_type
|
struct wl_shm* wl_shm = NULL;
|
||||||
frame_capture_backend_from_string(const char* str)
|
struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf = NULL;
|
||||||
{
|
struct gbm_device* gbm_device = NULL;
|
||||||
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,
|
||||||
|
@ -147,24 +130,16 @@ static void registry_add(void* data, struct wl_registry* registry,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(interface, wl_shm_interface.name) == 0) {
|
if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||||
self->screencopy_backend.wl_shm
|
wl_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
|
||||||
= wl_registry_bind(registry, id, &wl_shm_interface, 1);
|
|
||||||
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;
|
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.manager =
|
||||||
wl_registry_bind(registry, id,
|
wl_registry_bind(registry, id,
|
||||||
&zwlr_screencopy_manager_v1_interface,
|
&zwlr_screencopy_manager_v1_interface,
|
||||||
2);
|
version);
|
||||||
|
self->screencopy.version = version;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +175,12 @@ static void registry_add(void* data, struct wl_registry* registry,
|
||||||
1);
|
1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) {
|
||||||
|
zwp_linux_dmabuf = wl_registry_bind(registry, id,
|
||||||
|
&zwp_linux_dmabuf_v1_interface, 3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void registry_remove(void* data, struct wl_registry* registry,
|
static void registry_remove(void* data, struct wl_registry* registry,
|
||||||
|
@ -234,6 +215,25 @@ static void registry_remove(void* data, struct wl_registry* registry,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int find_render_node(char *node, size_t maxlen) {
|
||||||
|
bool r = -1;
|
||||||
|
drmDevice *devices[64];
|
||||||
|
|
||||||
|
int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0]));
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
drmDevice *dev = devices[i];
|
||||||
|
if (!(dev->available_nodes & (1 << DRM_NODE_RENDER)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
strlcpy(node, dev->nodes[DRM_NODE_RENDER], maxlen);
|
||||||
|
r = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmFreeDevices(devices, n);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
void wayvnc_destroy(struct wayvnc* self)
|
void wayvnc_destroy(struct wayvnc* self)
|
||||||
{
|
{
|
||||||
output_list_destroy(&self->outputs);
|
output_list_destroy(&self->outputs);
|
||||||
|
@ -241,7 +241,7 @@ void wayvnc_destroy(struct wayvnc* self)
|
||||||
|
|
||||||
zxdg_output_manager_v1_destroy(self->xdg_output_manager);
|
zxdg_output_manager_v1_destroy(self->xdg_output_manager);
|
||||||
|
|
||||||
wl_shm_destroy(self->screencopy_backend.wl_shm);
|
wl_shm_destroy(wl_shm);
|
||||||
|
|
||||||
zwp_virtual_keyboard_v1_destroy(self->keyboard_backend.virtual_keyboard);
|
zwp_virtual_keyboard_v1_destroy(self->keyboard_backend.virtual_keyboard);
|
||||||
zwp_virtual_keyboard_manager_v1_destroy(self->keyboard_manager);
|
zwp_virtual_keyboard_manager_v1_destroy(self->keyboard_manager);
|
||||||
|
@ -250,12 +250,10 @@ void wayvnc_destroy(struct wayvnc* self)
|
||||||
zwlr_virtual_pointer_manager_v1_destroy(self->pointer_manager);
|
zwlr_virtual_pointer_manager_v1_destroy(self->pointer_manager);
|
||||||
pointer_destroy(&self->pointer_backend);
|
pointer_destroy(&self->pointer_backend);
|
||||||
|
|
||||||
if (self->screencopy_backend.manager)
|
if (self->screencopy.manager)
|
||||||
zwlr_screencopy_manager_v1_destroy(self->screencopy_backend.manager);
|
zwlr_screencopy_manager_v1_destroy(self->screencopy.manager);
|
||||||
|
|
||||||
if (self->dmabuf_backend.manager)
|
|
||||||
zwlr_export_dmabuf_manager_v1_destroy(self->dmabuf_backend.manager);
|
|
||||||
|
|
||||||
|
wl_registry_destroy(self->registry);
|
||||||
wl_display_disconnect(self->display);
|
wl_display_disconnect(self->display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,16 +307,13 @@ 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.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->screencopy.on_done = on_capture_done;
|
||||||
self->dmabuf_backend.fc.userdata = self;
|
self->screencopy.userdata = self;
|
||||||
|
|
||||||
self->screencopy_backend.frame_capture.on_done = on_capture_done;
|
|
||||||
self->screencopy_backend.frame_capture.userdata = self;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -386,16 +381,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)
|
||||||
{
|
{
|
||||||
|
@ -469,9 +454,14 @@ failure:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int wayvnc_start_capture(struct wayvnc* self, enum frame_capture_options opt)
|
int wayvnc_start_capture(struct wayvnc* self)
|
||||||
{
|
{
|
||||||
return frame_capture_start(self->capture_backend, opt);
|
return screencopy_start(&self->screencopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
int wayvnc_start_capture_immediate(struct wayvnc* self)
|
||||||
|
{
|
||||||
|
return screencopy_start_immediate(&self->screencopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_render(struct nvnc_display* display, struct nvnc_fb* fb)
|
static void on_render(struct nvnc_display* display, struct nvnc_fb* fb)
|
||||||
|
@ -479,16 +469,17 @@ static void on_render(struct nvnc_display* display, struct nvnc_fb* fb)
|
||||||
struct nvnc* nvnc = nvnc_display_get_server(display);
|
struct nvnc* nvnc = nvnc_display_get_server(display);
|
||||||
struct wayvnc* self = nvnc_get_userdata(nvnc);
|
struct wayvnc* self = nvnc_get_userdata(nvnc);
|
||||||
|
|
||||||
struct pixman_box16* ext = pixman_region_extents(&self->current_damage);
|
if (!self->screencopy.back)
|
||||||
uint32_t y = ext->y1;
|
return;
|
||||||
uint32_t damage_height = ext->y2 - ext->y1;
|
|
||||||
|
|
||||||
uint32_t* addr = nvnc_fb_get_addr(fb);
|
DTRACE_PROBE(wayvnc, render_start);
|
||||||
uint32_t width = nvnc_fb_get_width(fb);
|
|
||||||
|
|
||||||
renderer_read_frame(&self->renderer, addr + y * width, y, damage_height);
|
|
||||||
|
|
||||||
|
enum wl_output_transform transform = self->selected_output->transform;
|
||||||
|
wv_pixman_render(fb, self->screencopy.back, transform,
|
||||||
|
&self->current_damage);
|
||||||
pixman_region_clear(&self->current_damage);
|
pixman_region_clear(&self->current_damage);
|
||||||
|
|
||||||
|
DTRACE_PROBE(wayvnc, render_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wayvnc_damage_region(struct wayvnc* self,
|
static void wayvnc_damage_region(struct wayvnc* self,
|
||||||
|
@ -498,109 +489,73 @@ 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;
|
uint32_t format = self->screencopy.back->format;
|
||||||
|
|
||||||
if (!self->buffer) {
|
if (!self->buffer) {
|
||||||
self->buffer = nvnc_fb_new(width, height, format);
|
self->buffer = nvnc_fb_new(width, height, format);
|
||||||
nvnc_display_set_buffer(self->nvnc_display, self->buffer);
|
nvnc_display_set_buffer(self->nvnc_display, self->buffer);
|
||||||
is_first_frame = true;
|
|
||||||
|
damage_refinery_init(&self->damage_refinery,
|
||||||
|
self->screencopy.back->width,
|
||||||
|
self->screencopy.back->height);
|
||||||
} else {
|
} else {
|
||||||
// TODO: Reallocate
|
// TODO: Reallocate
|
||||||
assert(width == nvnc_fb_get_width(self->buffer));
|
if (width != nvnc_fb_get_width(self->buffer) ||
|
||||||
assert(height == nvnc_fb_get_height(self->buffer));
|
height != nvnc_fb_get_height(self->buffer)) {
|
||||||
}
|
wayvnc_exit(self);
|
||||||
|
|
||||||
struct nvnc_fb* fb = self->buffer;
|
|
||||||
|
|
||||||
// TODO: Fix constness on fb in this function
|
|
||||||
frame_capture_render(self->capture_backend, &self->renderer, fb);
|
|
||||||
|
|
||||||
if (!is_first_frame) {
|
|
||||||
uint32_t hint_x = self->capture_backend->damage_hint.x;
|
|
||||||
uint32_t hint_y = self->capture_backend->damage_hint.y;
|
|
||||||
uint32_t hint_width = self->capture_backend->damage_hint.width;
|
|
||||||
uint32_t hint_height = self->capture_backend->damage_hint.height;
|
|
||||||
|
|
||||||
uint32_t tfx0, tfy0, tfx1, tfy1;
|
|
||||||
output_transform_box_coord(self->selected_output,
|
|
||||||
hint_x, hint_y,
|
|
||||||
hint_x + hint_width,
|
|
||||||
hint_y + hint_height,
|
|
||||||
&tfx0, &tfy0, &tfx1, &tfy1);
|
|
||||||
|
|
||||||
struct pixman_box16 hint = {
|
|
||||||
.x1 = tfx0, .y1 = tfy0,
|
|
||||||
.x2 = tfx1, .y2 = tfy1,
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t alignment = MAX(4, sizeof(void*));
|
|
||||||
size_t damage_buffer_size = ALIGN_UP(width * height, alignment);
|
|
||||||
uint8_t* damage_buffer =
|
|
||||||
aligned_alloc(alignment, damage_buffer_size);
|
|
||||||
render_damage(&self->renderer);
|
|
||||||
renderer_read_damage(&self->renderer, damage_buffer, 0, height);
|
|
||||||
|
|
||||||
damage_check_async(damage_buffer, width, height, &hint,
|
|
||||||
on_damage_check_done, self);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wayvnc_damage_whole(self);
|
DTRACE_PROBE(wayvnc, refine_damage_start);
|
||||||
|
|
||||||
if (wayvnc_start_capture(self, 0) < 0) {
|
struct pixman_region16 txdamage, refined;
|
||||||
|
pixman_region_init(&txdamage);
|
||||||
|
pixman_region_init(&refined);
|
||||||
|
damage_refine(&self->damage_refinery, &refined,
|
||||||
|
&self->screencopy.back->damage,
|
||||||
|
self->screencopy.back);
|
||||||
|
wv_region_transform(&txdamage, &refined,
|
||||||
|
self->selected_output->transform,
|
||||||
|
self->selected_output->width,
|
||||||
|
self->selected_output->height);
|
||||||
|
// damage_dump(stdout, &txdamage, width, height, 32);
|
||||||
|
wayvnc_damage_region(self, &txdamage);
|
||||||
|
pixman_region_fini(&refined);
|
||||||
|
pixman_region_fini(&txdamage);
|
||||||
|
|
||||||
|
DTRACE_PROBE(wayvnc, refine_damage_end);
|
||||||
|
|
||||||
|
if (wayvnc_start_capture(self) < 0) {
|
||||||
log_error("Failed to start capture. Exiting...\n");
|
log_error("Failed to start capture. Exiting...\n");
|
||||||
wayvnc_exit(self);
|
wayvnc_exit(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_capture_done(struct frame_capture* capture)
|
void on_capture_done(struct screencopy* sc)
|
||||||
{
|
{
|
||||||
struct wayvnc* self = capture->userdata;
|
struct wayvnc* self = sc->userdata;
|
||||||
|
|
||||||
switch (capture->status) {
|
switch (sc->status) {
|
||||||
case CAPTURE_STOPPED:
|
case SCREENCOPY_STOPPED:
|
||||||
break;
|
break;
|
||||||
case CAPTURE_IN_PROGRESS:
|
case SCREENCOPY_IN_PROGRESS:
|
||||||
break;
|
break;
|
||||||
case CAPTURE_FATAL:
|
case SCREENCOPY_FATAL:
|
||||||
log_error("Fatal error while capturing. Exiting...\n");
|
log_error("Fatal error while capturing. Exiting...\n");
|
||||||
wayvnc_exit(self);
|
wayvnc_exit(self);
|
||||||
break;
|
break;
|
||||||
case CAPTURE_FAILED:
|
case SCREENCOPY_FAILED:
|
||||||
if (wayvnc_start_capture(self, CAPTURE_NOW) < 0) {
|
if (wayvnc_start_capture_immediate(self) < 0) {
|
||||||
log_error("Failed to start capture. Exiting...\n");
|
log_error("Failed to start capture. Exiting...\n");
|
||||||
wayvnc_exit(self);
|
wayvnc_exit(self);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CAPTURE_DONE:
|
case SCREENCOPY_DONE:
|
||||||
wayvnc_process_frame(self);
|
wayvnc_process_frame(self);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -612,11 +567,11 @@ 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"
|
||||||
" -r,--render-cursor Enable overlay cursor rendering.\n"
|
" -r,--render-cursor Enable overlay cursor rendering.\n"
|
||||||
|
" -f,--max-fps=<fps> Set the rate limit (default 30).\n"
|
||||||
" -h,--help Get help (this text).\n"
|
" -h,--help Get help (this text).\n"
|
||||||
"\n";
|
"\n";
|
||||||
|
|
||||||
|
@ -671,20 +626,21 @@ 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;
|
||||||
|
int max_rate = 30;
|
||||||
|
|
||||||
static const char* shortopts = "C:c:o:k:s:rh";
|
static const char* shortopts = "C:o:k:s:rf:h";
|
||||||
|
int drm_fd = -1;
|
||||||
|
|
||||||
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' },
|
||||||
{ "render-cursor", no_argument, NULL, 'r' },
|
{ "render-cursor", no_argument, NULL, 'r' },
|
||||||
|
{ "max-fps", required_argument, NULL, 'f' },
|
||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
{ NULL, 0, NULL, 0 }
|
{ NULL, 0, NULL, 0 }
|
||||||
};
|
};
|
||||||
|
@ -698,15 +654,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;
|
||||||
|
@ -719,6 +666,9 @@ int main(int argc, char* argv[])
|
||||||
case 'r':
|
case 'r':
|
||||||
overlay_cursor = true;
|
overlay_cursor = true;
|
||||||
break;
|
break;
|
||||||
|
case 'f':
|
||||||
|
max_rate = atoi(optarg);
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
return wayvnc_usage(stdout, 0);
|
return wayvnc_usage(stdout, 0);
|
||||||
default:
|
default:
|
||||||
|
@ -795,8 +745,8 @@ 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.wl_output = out->wl_output;
|
||||||
self.screencopy_backend.frame_capture.wl_output = out->wl_output;
|
self.screencopy.rate_limit = max_rate;
|
||||||
|
|
||||||
self.keyboard_backend.virtual_keyboard =
|
self.keyboard_backend.virtual_keyboard =
|
||||||
zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
|
zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
|
||||||
|
@ -816,16 +766,19 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
pointer_init(&self.pointer_backend);
|
pointer_init(&self.pointer_backend);
|
||||||
|
|
||||||
enum renderer_input_type renderer_input_type =
|
char render_node[256];
|
||||||
fcbackend == FRAME_CAPTURE_BACKEND_DMABUF ?
|
if (find_render_node(render_node, sizeof(render_node)) < 0)
|
||||||
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;
|
goto failure;
|
||||||
}
|
|
||||||
|
|
||||||
struct aml* aml = aml_new(NULL, 0);
|
drm_fd = open(render_node, O_RDWR);
|
||||||
|
if (drm_fd < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
gbm_device = gbm_create_device(drm_fd);
|
||||||
|
if (!gbm_device)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
struct aml* aml = aml_new();
|
||||||
if (!aml)
|
if (!aml)
|
||||||
goto main_loop_failure;
|
goto main_loop_failure;
|
||||||
|
|
||||||
|
@ -837,44 +790,19 @@ int main(int argc, char* argv[])
|
||||||
if (init_nvnc(&self, address, port) < 0)
|
if (init_nvnc(&self, address, port) < 0)
|
||||||
goto nvnc_failure;
|
goto nvnc_failure;
|
||||||
|
|
||||||
if (self.screencopy_backend.manager)
|
if (self.screencopy.manager)
|
||||||
screencopy_init(&self.screencopy_backend);
|
screencopy_init(&self.screencopy);
|
||||||
|
|
||||||
if (self.dmabuf_backend.manager)
|
if (!self.screencopy.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");
|
log_error("screencopy is not supported by compositor\n");
|
||||||
goto capture_failure;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pixman_region_init(&self.current_damage);
|
pixman_region_init(&self.current_damage);
|
||||||
|
|
||||||
self.capture_backend->overlay_cursor = overlay_cursor;
|
self.screencopy.overlay_cursor = overlay_cursor;
|
||||||
|
|
||||||
if (wayvnc_start_capture(&self, 0) < 0)
|
if (wayvnc_start_capture(&self) < 0)
|
||||||
goto capture_failure;
|
goto capture_failure;
|
||||||
|
|
||||||
wl_display_dispatch(self.display);
|
wl_display_dispatch(self.display);
|
||||||
|
@ -885,19 +813,22 @@ int main(int argc, char* argv[])
|
||||||
aml_dispatch(aml);
|
aml_dispatch(aml);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame_capture_stop(self.capture_backend);
|
screencopy_stop(&self.screencopy);
|
||||||
|
|
||||||
if (self.buffer) nvnc_fb_unref(self.buffer);
|
if (self.buffer) {
|
||||||
|
damage_refinery_destroy(&self.damage_refinery);
|
||||||
|
nvnc_fb_unref(self.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
pixman_region_fini(&self.current_damage);
|
pixman_region_fini(&self.current_damage);
|
||||||
|
|
||||||
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 (self.screencopy_backend.manager)
|
zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf);
|
||||||
screencopy_destroy(&self.screencopy_backend);
|
if (self.screencopy.manager)
|
||||||
if (self.dmabuf_backend.manager)
|
screencopy_destroy(&self.screencopy);
|
||||||
dmabuf_capture_destroy(&self.dmabuf_backend);
|
gbm_device_destroy(gbm_device);
|
||||||
wayvnc_destroy(&self);
|
wayvnc_destroy(&self);
|
||||||
aml_unref(aml);
|
aml_unref(aml);
|
||||||
|
|
||||||
|
@ -908,8 +839,10 @@ 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);
|
||||||
|
if (drm_fd >= 0)
|
||||||
|
close(drm_fd);
|
||||||
wayvnc_destroy(&self);
|
wayvnc_destroy(&self);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Joseph Werle
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "murmurhash.h"
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
murmurhash (const char *key, uint32_t len, uint32_t seed) {
|
||||||
|
uint32_t c1 = 0xcc9e2d51;
|
||||||
|
uint32_t c2 = 0x1b873593;
|
||||||
|
uint32_t r1 = 15;
|
||||||
|
uint32_t r2 = 13;
|
||||||
|
uint32_t m = 5;
|
||||||
|
uint32_t n = 0xe6546b64;
|
||||||
|
uint32_t h = 0;
|
||||||
|
uint32_t k = 0;
|
||||||
|
uint8_t *d = (uint8_t *) key; // 32 bit extract from `key'
|
||||||
|
const uint32_t *chunks = NULL;
|
||||||
|
const uint8_t *tail = NULL; // tail - last 8 bytes
|
||||||
|
int i = 0;
|
||||||
|
int l = len / 4; // chunk length
|
||||||
|
|
||||||
|
h = seed;
|
||||||
|
|
||||||
|
chunks = (const uint32_t *) (d + l * 4); // body
|
||||||
|
tail = (const uint8_t *) (d + l * 4); // last 8 byte chunk of `key'
|
||||||
|
|
||||||
|
// for each 4 byte chunk of `key'
|
||||||
|
for (i = -l; i != 0; ++i) {
|
||||||
|
// next 4 byte chunk of `key'
|
||||||
|
k = chunks[i];
|
||||||
|
|
||||||
|
// encode next 4 byte chunk of `key'
|
||||||
|
k *= c1;
|
||||||
|
k = (k << r1) | (k >> (32 - r1));
|
||||||
|
k *= c2;
|
||||||
|
|
||||||
|
// append to hash
|
||||||
|
h ^= k;
|
||||||
|
h = (h << r2) | (h >> (32 - r2));
|
||||||
|
h = h * m + n;
|
||||||
|
}
|
||||||
|
|
||||||
|
k = 0;
|
||||||
|
|
||||||
|
// remainder
|
||||||
|
switch (len & 3) { // `len % 4'
|
||||||
|
case 3: k ^= (tail[2] << 16);
|
||||||
|
case 2: k ^= (tail[1] << 8);
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
k ^= tail[0];
|
||||||
|
k *= c1;
|
||||||
|
k = (k << r1) | (k >> (32 - r1));
|
||||||
|
k *= c2;
|
||||||
|
h ^= k;
|
||||||
|
}
|
||||||
|
|
||||||
|
h ^= len;
|
||||||
|
|
||||||
|
h ^= (h >> 16);
|
||||||
|
h *= 0x85ebca6b;
|
||||||
|
h ^= (h >> 13);
|
||||||
|
h *= 0xc2b2ae35;
|
||||||
|
h ^= (h >> 16);
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* 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 "pixels.h"
|
||||||
|
|
||||||
|
#include <pixman.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <libdrm/drm_fourcc.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <endian.h>
|
||||||
|
|
||||||
|
enum wl_shm_format fourcc_to_wl_shm(uint32_t in)
|
||||||
|
{
|
||||||
|
assert(!(in & DRM_FORMAT_BIG_ENDIAN));
|
||||||
|
|
||||||
|
switch (in) {
|
||||||
|
case DRM_FORMAT_ARGB8888: return WL_SHM_FORMAT_ARGB8888;
|
||||||
|
case DRM_FORMAT_XRGB8888: return WL_SHM_FORMAT_XRGB8888;
|
||||||
|
}
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fourcc_to_pixman_fmt(pixman_format_code_t* dst, uint32_t src)
|
||||||
|
{
|
||||||
|
assert(!(src & DRM_FORMAT_BIG_ENDIAN));
|
||||||
|
|
||||||
|
#define LOWER_R r
|
||||||
|
#define LOWER_G g
|
||||||
|
#define LOWER_B b
|
||||||
|
#define LOWER_A a
|
||||||
|
#define LOWER_X x
|
||||||
|
#define LOWER_
|
||||||
|
#define LOWER(x) LOWER_##x
|
||||||
|
|
||||||
|
#define CONCAT_(a, b) a ## b
|
||||||
|
#define CONCAT(a, b) CONCAT_(a, b)
|
||||||
|
|
||||||
|
#define FMT_DRM(x, y, z, v, a, b, c, d) DRM_FORMAT_##x##y##z##v##a##b##c##d
|
||||||
|
|
||||||
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
|
#define FMT_PIXMAN(x, y, z, v, a, b, c, d) \
|
||||||
|
CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(\
|
||||||
|
PIXMAN_, LOWER(x)), a), LOWER(y)), b), LOWER(z)), c), LOWER(v)), d)
|
||||||
|
#elif
|
||||||
|
#define FMT_PIXMAN(x, y, z, v, a, b, c, d) \
|
||||||
|
CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(\
|
||||||
|
PIXMAN_, LOWER(v)), d), LOWER(z)), c), LOWER(y)), b), LOWER(x)), a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (src) {
|
||||||
|
#define X(...) \
|
||||||
|
case FMT_DRM(__VA_ARGS__): *dst = FMT_PIXMAN(__VA_ARGS__); break
|
||||||
|
|
||||||
|
/* 32 bits */
|
||||||
|
X(A,R,G,B,8,8,8,8);
|
||||||
|
X(A,B,G,R,8,8,8,8);
|
||||||
|
X(X,R,G,B,8,8,8,8);
|
||||||
|
X(X,B,G,R,8,8,8,8);
|
||||||
|
X(R,G,B,A,8,8,8,8);
|
||||||
|
X(B,G,R,A,8,8,8,8);
|
||||||
|
X(R,G,B,X,8,8,8,8);
|
||||||
|
X(B,G,R,X,8,8,8,8);
|
||||||
|
|
||||||
|
/* 24 bits */
|
||||||
|
X(R,G,B,,8,8,8,);
|
||||||
|
X(B,G,R,,8,8,8,);
|
||||||
|
|
||||||
|
/* 16 bits */
|
||||||
|
X(R,G,B,,5,6,5,);
|
||||||
|
X(B,G,R,,5,6,5,);
|
||||||
|
|
||||||
|
/* These are incompatible on big endian */
|
||||||
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
|
X(A,R,G,B,1,5,5,5);
|
||||||
|
X(A,B,G,R,1,5,5,5);
|
||||||
|
X(X,R,G,B,1,5,5,5);
|
||||||
|
X(X,B,G,R,1,5,5,5);
|
||||||
|
X(A,R,G,B,4,4,4,4);
|
||||||
|
X(A,B,G,R,4,4,4,4);
|
||||||
|
X(X,R,G,B,4,4,4,4);
|
||||||
|
X(X,B,G,R,4,4,4,4);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* 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 <stdlib.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <neatvnc.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "pixels.h"
|
||||||
|
#include "transform-util.h"
|
||||||
|
|
||||||
|
void wv_pixman_render(struct nvnc_fb* dst, const struct wv_buffer* src,
|
||||||
|
enum wl_output_transform transform,
|
||||||
|
struct pixman_region16* damage)
|
||||||
|
{
|
||||||
|
uint32_t* dst_pixels = nvnc_fb_get_addr(dst);
|
||||||
|
uint32_t dst_width = nvnc_fb_get_width(dst);
|
||||||
|
uint32_t dst_height = nvnc_fb_get_height(dst);
|
||||||
|
bool ok __attribute__((unused));
|
||||||
|
|
||||||
|
// TODO: Check that both buffers have the same dimensions after applying
|
||||||
|
// transform
|
||||||
|
|
||||||
|
pixman_format_code_t dst_fmt = 0;
|
||||||
|
ok = fourcc_to_pixman_fmt(&dst_fmt, nvnc_fb_get_fourcc_format(dst));
|
||||||
|
assert(ok);
|
||||||
|
|
||||||
|
pixman_image_t* dstimg = pixman_image_create_bits_no_clear(
|
||||||
|
dst_fmt, dst_width, dst_height, dst_pixels,
|
||||||
|
4 * dst_width);
|
||||||
|
|
||||||
|
intptr_t src_offset = src->y_inverted ?
|
||||||
|
src->stride * (src->height - 1) : 0;
|
||||||
|
void* src_pixels = (void*)((intptr_t)src->pixels + src_offset);
|
||||||
|
int src_stride = src->y_inverted ? -src->stride : src->stride;
|
||||||
|
|
||||||
|
pixman_format_code_t src_fmt = 0;
|
||||||
|
ok = fourcc_to_pixman_fmt(&src_fmt, src->format);
|
||||||
|
assert(ok);
|
||||||
|
|
||||||
|
pixman_image_t* srcimg = pixman_image_create_bits_no_clear(
|
||||||
|
src_fmt, src->width, src->height, src_pixels,
|
||||||
|
src_stride);
|
||||||
|
|
||||||
|
pixman_transform_t pxform;
|
||||||
|
wv_pixman_transform_from_wl_output_transform(&pxform, transform,
|
||||||
|
src->width, src->height);
|
||||||
|
|
||||||
|
pixman_image_set_transform(srcimg, &pxform);
|
||||||
|
pixman_image_set_clip_region(dstimg, damage);
|
||||||
|
|
||||||
|
pixman_image_composite(PIXMAN_OP_OVER, srcimg, NULL, dstimg,
|
||||||
|
0, 0,
|
||||||
|
0, 0,
|
||||||
|
0, 0,
|
||||||
|
dst_width, dst_height);
|
||||||
|
|
||||||
|
pixman_image_unref(srcimg);
|
||||||
|
pixman_image_unref(dstimg);
|
||||||
|
}
|
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);
|
|
||||||
}
|
|
239
src/screencopy.c
239
src/screencopy.c
|
@ -24,75 +24,21 @@
|
||||||
#include <libdrm/drm_fourcc.h>
|
#include <libdrm/drm_fourcc.h>
|
||||||
#include <aml.h>
|
#include <aml.h>
|
||||||
|
|
||||||
#include "frame-capture.h"
|
#include "wlr-screencopy-unstable-v1.h"
|
||||||
|
#include "buffer.h"
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
#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 DELAY_SMOOTHER_TIME_CONSTANT 0.5 // s
|
#define DELAY_SMOOTHER_TIME_CONSTANT 0.5 // s
|
||||||
|
|
||||||
static uint32_t fourcc_from_wl_shm(enum wl_shm_format in)
|
void screencopy_stop(struct screencopy* self)
|
||||||
{
|
{
|
||||||
switch (in) {
|
|
||||||
case WL_SHM_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888;
|
|
||||||
case WL_SHM_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888;
|
|
||||||
default: return in;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int screencopy_buffer_init(struct screencopy* self,
|
|
||||||
enum wl_shm_format format, uint32_t width,
|
|
||||||
uint32_t height, uint32_t stride)
|
|
||||||
{
|
|
||||||
if (self->buffer)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
size_t size = stride * height;
|
|
||||||
|
|
||||||
int fd = shm_alloc_fd(size);
|
|
||||||
if (fd < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
||||||
if (!addr)
|
|
||||||
goto mmap_failure;
|
|
||||||
|
|
||||||
struct wl_shm_pool* pool = wl_shm_create_pool(self->wl_shm, fd, size);
|
|
||||||
if (!pool)
|
|
||||||
goto shm_failure;
|
|
||||||
|
|
||||||
struct wl_buffer* buffer =
|
|
||||||
wl_shm_pool_create_buffer(pool, 0, width, height, stride,
|
|
||||||
format);
|
|
||||||
wl_shm_pool_destroy(pool);
|
|
||||||
if (!buffer)
|
|
||||||
goto shm_failure;
|
|
||||||
|
|
||||||
self->buffer = buffer;
|
|
||||||
self->pixels = addr;
|
|
||||||
self->bufsize = size;
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
shm_failure:
|
|
||||||
munmap(addr, size);
|
|
||||||
mmap_failure:
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void screencopy_stop(struct frame_capture* fc)
|
|
||||||
{
|
|
||||||
struct screencopy* self = (void*)fc;
|
|
||||||
|
|
||||||
aml_stop(aml_get_default(), self->timer);
|
aml_stop(aml_get_default(), self->timer);
|
||||||
|
|
||||||
self->frame_capture.status = CAPTURE_STOPPED;
|
self->status = SCREENCOPY_STOPPED;
|
||||||
|
|
||||||
if (self->frame) {
|
if (self->frame) {
|
||||||
zwlr_screencopy_frame_v1_destroy(self->frame);
|
zwlr_screencopy_frame_v1_destroy(self->frame);
|
||||||
|
@ -100,6 +46,58 @@ static void screencopy_stop(struct frame_capture* fc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void screencopy_linux_dmabuf(void* data,
|
||||||
|
struct zwlr_screencopy_frame_v1* frame,
|
||||||
|
uint32_t format, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
struct screencopy* self = data;
|
||||||
|
|
||||||
|
self->have_linux_dmabuf = true;
|
||||||
|
self->dmabuf_width = width;
|
||||||
|
self->dmabuf_height = height;
|
||||||
|
self->fourcc = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void screencopy_buffer_done(void* data,
|
||||||
|
struct zwlr_screencopy_frame_v1* frame)
|
||||||
|
{
|
||||||
|
struct screencopy* self = data;
|
||||||
|
uint32_t width, height, stride, fourcc;
|
||||||
|
|
||||||
|
if (self->have_linux_dmabuf) {
|
||||||
|
width = self->dmabuf_width;
|
||||||
|
height = self->dmabuf_height;
|
||||||
|
stride = 0;
|
||||||
|
fourcc = self->fourcc;
|
||||||
|
wv_buffer_pool_resize(self->pool, WV_BUFFER_DMABUF, width,
|
||||||
|
height, stride, fourcc);
|
||||||
|
} else {
|
||||||
|
width = self->wl_shm_width;
|
||||||
|
height = self->wl_shm_height;
|
||||||
|
stride = self->wl_shm_stride;
|
||||||
|
fourcc = self->fourcc;
|
||||||
|
wv_buffer_pool_resize(self->pool, WV_BUFFER_SHM, width,
|
||||||
|
height, stride, self->wl_shm_format);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wv_buffer* buffer = wv_buffer_pool_acquire(self->pool);
|
||||||
|
if (!buffer) {
|
||||||
|
screencopy_stop(self);
|
||||||
|
self->status = SCREENCOPY_FATAL;
|
||||||
|
self->on_done(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!self->front);
|
||||||
|
self->front = buffer;
|
||||||
|
|
||||||
|
if (self->is_immediate_copy)
|
||||||
|
zwlr_screencopy_frame_v1_copy(self->frame, buffer->wl_buffer);
|
||||||
|
else
|
||||||
|
zwlr_screencopy_frame_v1_copy_with_damage(self->frame,
|
||||||
|
buffer->wl_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
static void screencopy_buffer(void* data,
|
static void screencopy_buffer(void* data,
|
||||||
struct zwlr_screencopy_frame_v1* frame,
|
struct zwlr_screencopy_frame_v1* frame,
|
||||||
enum wl_shm_format format, uint32_t width,
|
enum wl_shm_format format, uint32_t width,
|
||||||
|
@ -107,34 +105,28 @@ static void screencopy_buffer(void* data,
|
||||||
{
|
{
|
||||||
struct screencopy* self = data;
|
struct screencopy* self = data;
|
||||||
|
|
||||||
if (screencopy_buffer_init(self, format, width, height, stride) < 0) {
|
self->wl_shm_format = format;
|
||||||
self->frame_capture.status = CAPTURE_FATAL;
|
self->wl_shm_width = width;
|
||||||
screencopy_stop(&self->frame_capture);
|
self->wl_shm_height = height;
|
||||||
self->frame_capture.on_done(&self->frame_capture);
|
self->wl_shm_stride = stride;
|
||||||
|
|
||||||
|
if (self->version < 3) {
|
||||||
|
self->have_linux_dmabuf = false;
|
||||||
|
screencopy_buffer_done(data, frame);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self->frame_capture.frame_info.fourcc_format =
|
|
||||||
fourcc_from_wl_shm(format);
|
|
||||||
self->frame_capture.frame_info.width = width;
|
|
||||||
self->frame_capture.frame_info.height = height;
|
|
||||||
self->frame_capture.frame_info.stride = stride;
|
|
||||||
|
|
||||||
if (self->is_immediate_copy)
|
|
||||||
zwlr_screencopy_frame_v1_copy(self->frame, self->buffer);
|
|
||||||
else
|
|
||||||
zwlr_screencopy_frame_v1_copy_with_damage(self->frame,
|
|
||||||
self->buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void screencopy_flags(void* data,
|
static void screencopy_flags(void* data,
|
||||||
struct zwlr_screencopy_frame_v1* frame,
|
struct zwlr_screencopy_frame_v1* frame,
|
||||||
uint32_t flags)
|
uint32_t flags)
|
||||||
{
|
{
|
||||||
(void)data;
|
|
||||||
(void)frame;
|
(void)frame;
|
||||||
(void)flags;
|
|
||||||
|
|
||||||
/* TODO. Assume y-invert for now */
|
struct screencopy* self = data;
|
||||||
|
|
||||||
|
self->front->y_inverted =
|
||||||
|
!!(flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void screencopy_ready(void* data,
|
static void screencopy_ready(void* data,
|
||||||
|
@ -149,24 +141,25 @@ static void screencopy_ready(void* data,
|
||||||
|
|
||||||
DTRACE_PROBE1(wayvnc, screencopy_ready, self);
|
DTRACE_PROBE1(wayvnc, screencopy_ready, self);
|
||||||
|
|
||||||
screencopy_stop(&self->frame_capture);
|
screencopy_stop(self);
|
||||||
|
|
||||||
self->last_time = gettime_us();
|
self->last_time = gettime_us();
|
||||||
|
|
||||||
double delay = (self->last_time - self->start_time) * 1.0e-6;
|
double delay = (self->last_time - self->start_time) * 1.0e-6;
|
||||||
self->delay = smooth(&self->delay_smoother, delay);
|
self->delay = smooth(&self->delay_smoother, delay);
|
||||||
|
|
||||||
if (self->is_immediate_copy) {
|
if (self->is_immediate_copy)
|
||||||
self->frame_capture.damage_hint.x = 0;
|
wv_buffer_damage_whole(self->front);
|
||||||
self->frame_capture.damage_hint.y = 0;
|
|
||||||
self->frame_capture.damage_hint.width =
|
|
||||||
self->frame_capture.frame_info.width;
|
|
||||||
self->frame_capture.damage_hint.height =
|
|
||||||
self->frame_capture.frame_info.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->frame_capture.status = CAPTURE_DONE;
|
if (self->back)
|
||||||
self->frame_capture.on_done(&self->frame_capture);
|
wv_buffer_pool_release(self->pool, self->back);
|
||||||
|
self->back = self->front;
|
||||||
|
self->front = NULL;
|
||||||
|
|
||||||
|
wv_buffer_map(self->back);
|
||||||
|
|
||||||
|
self->status = SCREENCOPY_DONE;
|
||||||
|
self->on_done(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void screencopy_failed(void* data,
|
static void screencopy_failed(void* data,
|
||||||
|
@ -176,9 +169,9 @@ static void screencopy_failed(void* data,
|
||||||
|
|
||||||
DTRACE_PROBE1(wayvnc, screencopy_failed, self);
|
DTRACE_PROBE1(wayvnc, screencopy_failed, self);
|
||||||
|
|
||||||
screencopy_stop(&self->frame_capture);
|
screencopy_stop(self);
|
||||||
self->frame_capture.status = CAPTURE_FAILED;
|
self->status = SCREENCOPY_FAILED;
|
||||||
self->frame_capture.on_done(&self->frame_capture);
|
self->on_done(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void screencopy_damage(void* data,
|
static void screencopy_damage(void* data,
|
||||||
|
@ -190,20 +183,17 @@ static void screencopy_damage(void* data,
|
||||||
|
|
||||||
DTRACE_PROBE1(wayvnc, screencopy_damage, self);
|
DTRACE_PROBE1(wayvnc, screencopy_damage, self);
|
||||||
|
|
||||||
self->frame_capture.damage_hint.x = x;
|
wv_buffer_damage_rect(self->front, x, y, width, height);
|
||||||
self->frame_capture.damage_hint.y = y;
|
|
||||||
self->frame_capture.damage_hint.width = width;
|
|
||||||
self->frame_capture.damage_hint.height = height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int screencopy__start_capture(struct frame_capture* fc)
|
static int screencopy__start_capture(struct screencopy* self)
|
||||||
{
|
{
|
||||||
struct screencopy* self = (void*)fc;
|
|
||||||
|
|
||||||
DTRACE_PROBE1(wayvnc, screencopy_start, self);
|
DTRACE_PROBE1(wayvnc, screencopy_start, self);
|
||||||
|
|
||||||
static const struct zwlr_screencopy_frame_v1_listener frame_listener = {
|
static const struct zwlr_screencopy_frame_v1_listener frame_listener = {
|
||||||
.buffer = screencopy_buffer,
|
.buffer = screencopy_buffer,
|
||||||
|
.linux_dmabuf = screencopy_linux_dmabuf,
|
||||||
|
.buffer_done = screencopy_buffer_done,
|
||||||
.flags = screencopy_flags,
|
.flags = screencopy_flags,
|
||||||
.ready = screencopy_ready,
|
.ready = screencopy_ready,
|
||||||
.failed = screencopy_failed,
|
.failed = screencopy_failed,
|
||||||
|
@ -212,10 +202,8 @@ static int screencopy__start_capture(struct frame_capture* fc)
|
||||||
|
|
||||||
self->start_time = gettime_us();
|
self->start_time = gettime_us();
|
||||||
|
|
||||||
self->frame =
|
self->frame = zwlr_screencopy_manager_v1_capture_output(self->manager,
|
||||||
zwlr_screencopy_manager_v1_capture_output(self->manager,
|
self->overlay_cursor, self->wl_output);
|
||||||
fc->overlay_cursor,
|
|
||||||
fc->wl_output);
|
|
||||||
if (!self->frame)
|
if (!self->frame)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -228,58 +216,51 @@ static int screencopy__start_capture(struct frame_capture* fc)
|
||||||
static void screencopy__poll(void* obj)
|
static void screencopy__poll(void* obj)
|
||||||
{
|
{
|
||||||
struct screencopy* self = aml_get_userdata(obj);
|
struct screencopy* self = aml_get_userdata(obj);
|
||||||
struct frame_capture* fc = (struct frame_capture*)self;
|
|
||||||
|
|
||||||
screencopy__start_capture(fc);
|
screencopy__start_capture(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int screencopy_start(struct frame_capture* fc,
|
static int screencopy__start(struct screencopy* self, bool is_immediate_copy)
|
||||||
enum frame_capture_options options)
|
|
||||||
{
|
{
|
||||||
struct screencopy* self = (void*)fc;
|
|
||||||
|
|
||||||
if (fc->status == CAPTURE_IN_PROGRESS)
|
if (self->status == SCREENCOPY_IN_PROGRESS)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
self->is_immediate_copy = !!(options & CAPTURE_NOW);
|
self->is_immediate_copy = is_immediate_copy;
|
||||||
|
|
||||||
uint64_t now = gettime_us();
|
uint64_t now = gettime_us();
|
||||||
double dt = (now - self->last_time) * 1.0e-6;
|
double dt = (now - self->last_time) * 1.0e-6;
|
||||||
double time_left = (1.0 / RATE_LIMIT - dt - self->delay) * 1.0e3;
|
double time_left = (1.0 / self->rate_limit - dt - self->delay) * 1.0e3;
|
||||||
|
|
||||||
fc->status = CAPTURE_IN_PROGRESS;
|
self->status = SCREENCOPY_IN_PROGRESS;
|
||||||
|
|
||||||
if (time_left > 0) {
|
if (time_left > 0) {
|
||||||
aml_set_duration(self->timer, time_left);
|
aml_set_duration(self->timer, time_left);
|
||||||
return aml_start(aml_get_default(), self->timer);
|
return aml_start(aml_get_default(), self->timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return screencopy__start_capture(fc);
|
return screencopy__start_capture(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void screencopy_render(struct frame_capture* fc,
|
int screencopy_start(struct screencopy* self)
|
||||||
struct renderer* renderer, struct nvnc_fb* fb)
|
|
||||||
{
|
{
|
||||||
struct screencopy* self = (void*)fc;
|
return screencopy__start(self, false);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t width = fc->frame_info.width;
|
int screencopy_start_immediate(struct screencopy* self)
|
||||||
uint32_t height = fc->frame_info.height;
|
{
|
||||||
uint32_t stride = fc->frame_info.stride;
|
return screencopy__start(self, true);
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
self->pool = wv_buffer_pool_create(0, 0, 0, 0, 0);
|
||||||
|
assert(self->pool);
|
||||||
|
|
||||||
self->timer = aml_timer_new(0, screencopy__poll, self, NULL);
|
self->timer = aml_timer_new(0, screencopy__poll, self, NULL);
|
||||||
assert(self->timer);
|
assert(self->timer);
|
||||||
|
|
||||||
self->delay_smoother.time_constant = DELAY_SMOOTHER_TIME_CONSTANT;
|
self->delay_smoother.time_constant = DELAY_SMOOTHER_TIME_CONSTANT;
|
||||||
|
|
||||||
self->frame_capture.backend.start = screencopy_start;
|
|
||||||
self->frame_capture.backend.stop = screencopy_stop;
|
|
||||||
self->frame_capture.backend.render = screencopy_render;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void screencopy_destroy(struct screencopy* self)
|
void screencopy_destroy(struct screencopy* self)
|
||||||
|
@ -287,6 +268,10 @@ void screencopy_destroy(struct screencopy* self)
|
||||||
aml_stop(aml_get_default(), self->timer);
|
aml_stop(aml_get_default(), self->timer);
|
||||||
aml_unref(self->timer);
|
aml_unref(self->timer);
|
||||||
|
|
||||||
if (self->buffer)
|
if (self->back)
|
||||||
wl_buffer_destroy(self->buffer);
|
wv_buffer_pool_release(self->pool, self->back);
|
||||||
|
if (self->front)
|
||||||
|
wv_buffer_pool_release(self->pool, self->front);
|
||||||
|
|
||||||
|
wv_buffer_pool_destroy(self->pool);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,236 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* For code borrowed from wlroots:
|
||||||
|
* Copyright (c) 2017, 2018 Drew DeVault
|
||||||
|
* Copyright (c) 2014 Jari Vetoniemi
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
|
||||||
|
/* Note: This function yields the inverse pixman transform of the
|
||||||
|
* wl_output_transform.
|
||||||
|
*/
|
||||||
|
void wv_pixman_transform_from_wl_output_transform(pixman_transform_t* dst,
|
||||||
|
enum wl_output_transform src, int width, int height)
|
||||||
|
{
|
||||||
|
#define F1 pixman_fixed_1
|
||||||
|
switch (src) {
|
||||||
|
case WL_OUTPUT_TRANSFORM_NORMAL:
|
||||||
|
{
|
||||||
|
pixman_transform_t t = {{
|
||||||
|
{ F1, 0, 0 },
|
||||||
|
{ 0, F1, 0 },
|
||||||
|
{ 0, 0, F1 },
|
||||||
|
}};
|
||||||
|
*dst = t;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case WL_OUTPUT_TRANSFORM_90:
|
||||||
|
{
|
||||||
|
pixman_transform_t t = {{
|
||||||
|
{ 0, F1, 0 },
|
||||||
|
{ -F1, 0, height * F1 },
|
||||||
|
{ 0, 0, F1 },
|
||||||
|
}};
|
||||||
|
*dst = t;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case WL_OUTPUT_TRANSFORM_180:
|
||||||
|
{
|
||||||
|
pixman_transform_t t = {{
|
||||||
|
{ -F1, 0, width * F1 },
|
||||||
|
{ 0, -F1, height * F1 },
|
||||||
|
{ 0, 0, F1 },
|
||||||
|
}};
|
||||||
|
*dst = t;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case WL_OUTPUT_TRANSFORM_270:
|
||||||
|
{
|
||||||
|
pixman_transform_t t = {{
|
||||||
|
{ 0, -F1, width * F1 },
|
||||||
|
{ F1, 0, 0 },
|
||||||
|
{ 0, 0, F1 },
|
||||||
|
}};
|
||||||
|
*dst = t;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case WL_OUTPUT_TRANSFORM_FLIPPED:
|
||||||
|
{
|
||||||
|
pixman_transform_t t = {{
|
||||||
|
{ -F1, 0, width * F1 },
|
||||||
|
{ 0, F1, 0 },
|
||||||
|
{ 0, 0, F1 },
|
||||||
|
}};
|
||||||
|
*dst = t;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
|
||||||
|
{
|
||||||
|
pixman_transform_t t = {{
|
||||||
|
{ 0, F1, 0 },
|
||||||
|
{ F1, 0, 0 },
|
||||||
|
{ 0, 0, F1 },
|
||||||
|
}};
|
||||||
|
*dst = t;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
|
||||||
|
{
|
||||||
|
pixman_transform_t t = {{
|
||||||
|
{ F1, 0, 0 },
|
||||||
|
{ 0, -F1, height * F1 },
|
||||||
|
{ 0, 0, F1 },
|
||||||
|
}};
|
||||||
|
*dst = t;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
|
||||||
|
{
|
||||||
|
pixman_transform_t t = {{
|
||||||
|
{ 0, -F1, width * F1 },
|
||||||
|
{ -F1, 0, height * F1 },
|
||||||
|
{ 0, 0, F1 },
|
||||||
|
}};
|
||||||
|
*dst = t;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#undef F1
|
||||||
|
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Borrowed these from wlroots */
|
||||||
|
void wv_region_transform(struct pixman_region16* dst,
|
||||||
|
struct pixman_region16* src, enum wl_output_transform transform,
|
||||||
|
int width, int height)
|
||||||
|
{
|
||||||
|
if (transform == WL_OUTPUT_TRANSFORM_NORMAL) {
|
||||||
|
pixman_region_copy(dst, src);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nrects = 0;
|
||||||
|
pixman_box16_t* src_rects = pixman_region_rectangles(src, &nrects);
|
||||||
|
|
||||||
|
pixman_box16_t* dst_rects = malloc(nrects * sizeof(*dst_rects));
|
||||||
|
if (dst_rects == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < nrects; ++i) {
|
||||||
|
switch (transform) {
|
||||||
|
case WL_OUTPUT_TRANSFORM_NORMAL:
|
||||||
|
dst_rects[i].x1 = src_rects[i].x1;
|
||||||
|
dst_rects[i].y1 = src_rects[i].y1;
|
||||||
|
dst_rects[i].x2 = src_rects[i].x2;
|
||||||
|
dst_rects[i].y2 = src_rects[i].y2;
|
||||||
|
break;
|
||||||
|
case WL_OUTPUT_TRANSFORM_90:
|
||||||
|
dst_rects[i].x1 = height - src_rects[i].y2;
|
||||||
|
dst_rects[i].y1 = src_rects[i].x1;
|
||||||
|
dst_rects[i].x2 = height - src_rects[i].y1;
|
||||||
|
dst_rects[i].y2 = src_rects[i].x2;
|
||||||
|
break;
|
||||||
|
case WL_OUTPUT_TRANSFORM_180:
|
||||||
|
dst_rects[i].x1 = width - src_rects[i].x2;
|
||||||
|
dst_rects[i].y1 = height - src_rects[i].y2;
|
||||||
|
dst_rects[i].x2 = width - src_rects[i].x1;
|
||||||
|
dst_rects[i].y2 = height - src_rects[i].y1;
|
||||||
|
break;
|
||||||
|
case WL_OUTPUT_TRANSFORM_270:
|
||||||
|
dst_rects[i].x1 = src_rects[i].y1;
|
||||||
|
dst_rects[i].y1 = width - src_rects[i].x2;
|
||||||
|
dst_rects[i].x2 = src_rects[i].y2;
|
||||||
|
dst_rects[i].y2 = width - src_rects[i].x1;
|
||||||
|
break;
|
||||||
|
case WL_OUTPUT_TRANSFORM_FLIPPED:
|
||||||
|
dst_rects[i].x1 = width - src_rects[i].x2;
|
||||||
|
dst_rects[i].y1 = src_rects[i].y1;
|
||||||
|
dst_rects[i].x2 = width - src_rects[i].x1;
|
||||||
|
dst_rects[i].y2 = src_rects[i].y2;
|
||||||
|
break;
|
||||||
|
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
|
||||||
|
dst_rects[i].x1 = src_rects[i].y1;
|
||||||
|
dst_rects[i].y1 = src_rects[i].x1;
|
||||||
|
dst_rects[i].x2 = src_rects[i].y2;
|
||||||
|
dst_rects[i].y2 = src_rects[i].x2;
|
||||||
|
break;
|
||||||
|
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
|
||||||
|
dst_rects[i].x1 = src_rects[i].x1;
|
||||||
|
dst_rects[i].y1 = height - src_rects[i].y2;
|
||||||
|
dst_rects[i].x2 = src_rects[i].x2;
|
||||||
|
dst_rects[i].y2 = height - src_rects[i].y1;
|
||||||
|
break;
|
||||||
|
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
|
||||||
|
dst_rects[i].x1 = height - src_rects[i].y2;
|
||||||
|
dst_rects[i].y1 = width - src_rects[i].x2;
|
||||||
|
dst_rects[i].x2 = height - src_rects[i].y1;
|
||||||
|
dst_rects[i].y2 = width - src_rects[i].x1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pixman_region_fini(dst);
|
||||||
|
pixman_region_init_rects(dst, dst_rects, nrects);
|
||||||
|
free(dst_rects);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum wl_output_transform wv_output_transform_invert(enum wl_output_transform tr)
|
||||||
|
{
|
||||||
|
if ((tr & WL_OUTPUT_TRANSFORM_90) && !(tr & WL_OUTPUT_TRANSFORM_FLIPPED)) {
|
||||||
|
tr ^= WL_OUTPUT_TRANSFORM_180;
|
||||||
|
}
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum wl_output_transform wv_output_transform_compose(
|
||||||
|
enum wl_output_transform tr_a, enum wl_output_transform tr_b)
|
||||||
|
{
|
||||||
|
uint32_t flipped = (tr_a ^ tr_b) & WL_OUTPUT_TRANSFORM_FLIPPED;
|
||||||
|
uint32_t rotation_mask = WL_OUTPUT_TRANSFORM_90 | WL_OUTPUT_TRANSFORM_180;
|
||||||
|
uint32_t rotated;
|
||||||
|
if (tr_b & WL_OUTPUT_TRANSFORM_FLIPPED) {
|
||||||
|
// When a rotation of k degrees is followed by a flip, the
|
||||||
|
// equivalent transform is a flip followed by a rotation of
|
||||||
|
// -k degrees.
|
||||||
|
rotated = (tr_b - tr_a) & rotation_mask;
|
||||||
|
} else {
|
||||||
|
rotated = (tr_a + tr_b) & rotation_mask;
|
||||||
|
}
|
||||||
|
return flipped | rotated;
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -58,10 +58,8 @@ trackers = [
|
||||||
StateTracker('Framebuffer update', 'sdt_neatvnc', 'update_fb_start', 'update_fb_done'),
|
StateTracker('Framebuffer update', 'sdt_neatvnc', 'update_fb_start', 'update_fb_done'),
|
||||||
StateTracker('Framebuffer update (only sending)', 'sdt_neatvnc', 'send_fb_start', 'send_fb_done'),
|
StateTracker('Framebuffer update (only sending)', 'sdt_neatvnc', 'send_fb_start', 'send_fb_done'),
|
||||||
StateTracker('Screencopy', 'sdt_wayvnc', 'screencopy_start', 'screencopy_ready'),
|
StateTracker('Screencopy', 'sdt_wayvnc', 'screencopy_start', 'screencopy_ready'),
|
||||||
StateTracker('Dmabuf', 'sdt_wayvnc', 'dmabuf_capture_start', 'dmabuf_frame_ready'),
|
StateTracker('Refine damage', 'sdt_wayvnc', 'refine_damage_start', 'refine_damage_end'),
|
||||||
StateTracker('Render', 'sdt_wayvnc', 'render_framebuffer_start', 'render_framebuffer_end'),
|
StateTracker('Render', 'sdt_wayvnc', 'render_start', 'render_end'),
|
||||||
StateTracker('Copy damage map', 'sdt_wayvnc', 'render_read_damage_start', 'render_read_damage_end'),
|
|
||||||
StateTracker('Copy frame pixels', 'sdt_wayvnc', 'render_read_frame_start', 'render_read_frame_end'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for line in stream:
|
for line in stream:
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
valgrind --leak-check=full \
|
||||||
|
--show-leak-kinds=all \
|
||||||
|
--suppressions=util/valgrind.supp \
|
||||||
|
$@
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
Ignore dlopen bug.
|
||||||
|
Memcheck:Leak
|
||||||
|
...
|
||||||
|
fun:_dl_*
|
||||||
|
...
|
||||||
|
}
|
Loading…
Reference in New Issue