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
|
||||
.ycm_extra_conf.py
|
||||
perf.*
|
||||
*.pem
|
||||
|
|
|
@ -36,10 +36,10 @@ dnf install wayvnc
|
|||
## Building
|
||||
### Runtime Dependencies
|
||||
* aml
|
||||
* EGL
|
||||
* drm
|
||||
* gbm
|
||||
* libxkbcommon
|
||||
* neatvnc
|
||||
* OpenGL ES V2.0
|
||||
* pixman
|
||||
|
||||
### 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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -14,15 +14,24 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
precision mediump float;
|
||||
#pragma once
|
||||
|
||||
uniform sampler2D u_tex0;
|
||||
uniform sampler2D u_tex1;
|
||||
#include <stdint.h>
|
||||
|
||||
varying vec2 v_texture;
|
||||
struct pixman_region16;
|
||||
struct wv_buffer;
|
||||
|
||||
void main()
|
||||
{
|
||||
float r = float(texture2D(u_tex0, v_texture).rgb != texture2D(u_tex1, v_texture).rgb);
|
||||
gl_FragColor = vec4(r);
|
||||
}
|
||||
struct damage_refinery {
|
||||
uint32_t* hashes;
|
||||
uint32_t width;
|
||||
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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -14,15 +14,12 @@
|
|||
* 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;
|
||||
|
||||
varying vec2 v_texture;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = texture2D(u_tex0, v_texture);
|
||||
}
|
||||
enum wl_shm_format fourcc_to_wl_shm(uint32_t in);
|
||||
bool fourcc_to_pixman_fmt(pixman_format_code_t* dst, uint32_t src);
|
|
@ -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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -14,15 +14,14 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
uniform mat2 u_proj;
|
||||
#pragma once
|
||||
|
||||
attribute vec2 pos;
|
||||
attribute vec2 texture;
|
||||
#include <wayland-client.h>
|
||||
|
||||
varying vec2 v_texture;
|
||||
struct nvnc_fb;
|
||||
struct wv_buffer;
|
||||
struct pixman_region16;
|
||||
|
||||
void main()
|
||||
{
|
||||
v_texture = vec2(texture.s, 1.0 - texture.t);
|
||||
gl_Position = vec4(u_proj * pos, 0, 1);
|
||||
}
|
||||
void wv_pixman_render(struct nvnc_fb* dst, const struct wv_buffer* src,
|
||||
enum wl_output_transform transform,
|
||||
struct pixman_region16* damage);
|
|
@ -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 "wlr-screencopy-unstable-v1.h"
|
||||
#include "frame-capture.h"
|
||||
#include "smooth.h"
|
||||
#include "buffer.h"
|
||||
|
||||
struct zwlr_screencopy_manager_v1;
|
||||
struct zwlr_screencopy_frame_v1;
|
||||
|
@ -31,23 +31,26 @@ struct aml_timer;
|
|||
struct renderer;
|
||||
|
||||
enum screencopy_status {
|
||||
SCREENCOPY_STATUS_CAPTURING = 0,
|
||||
SCREENCOPY_STATUS_FATAL,
|
||||
SCREENCOPY_STATUS_FAILED,
|
||||
SCREENCOPY_STATUS_DONE,
|
||||
SCREENCOPY_STOPPED = 0,
|
||||
SCREENCOPY_IN_PROGRESS,
|
||||
SCREENCOPY_FAILED,
|
||||
SCREENCOPY_FATAL,
|
||||
SCREENCOPY_DONE,
|
||||
};
|
||||
|
||||
struct screencopy {
|
||||
struct frame_capture frame_capture;
|
||||
enum screencopy_status status;
|
||||
|
||||
struct wl_shm* wl_shm;
|
||||
struct wl_buffer* buffer;
|
||||
|
||||
void* pixels;
|
||||
size_t bufsize;
|
||||
struct wv_buffer_pool* pool;
|
||||
struct wv_buffer* front;
|
||||
struct wv_buffer* back;
|
||||
|
||||
struct zwlr_screencopy_manager_v1* manager;
|
||||
struct zwlr_screencopy_frame_v1* frame;
|
||||
int version;
|
||||
|
||||
void* userdata;
|
||||
void (*on_done)(struct screencopy*);
|
||||
|
||||
uint64_t last_time;
|
||||
uint64_t start_time;
|
||||
|
@ -56,7 +59,23 @@ struct screencopy {
|
|||
struct smooth delay_smoother;
|
||||
double delay;
|
||||
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_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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -14,13 +14,7 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
precision mediump float;
|
||||
#pragma once
|
||||
|
||||
uniform sampler2D u_tex0;
|
||||
|
||||
varying vec2 v_texture;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = texture2D(u_tex0, v_texture);
|
||||
}
|
||||
#define UDIV_UP(a, b) (((a) + (b) - 1) / (b))
|
||||
#define ALIGN_UP(n, a) (UDIV_UP(n, a) * a)
|
21
meson.build
21
meson.build
|
@ -28,8 +28,8 @@ libm = cc.find_library('m', required: false)
|
|||
librt = cc.find_library('rt', required: false)
|
||||
|
||||
pixman = dependency('pixman-1')
|
||||
egl = dependency('egl')
|
||||
glesv2 = dependency('glesv2')
|
||||
gbm = dependency('gbm')
|
||||
drm = dependency('libdrm')
|
||||
xkbcommon = dependency('xkbcommon')
|
||||
wayland_client = dependency('wayland-client')
|
||||
|
||||
|
@ -57,8 +57,6 @@ subdir('protocols')
|
|||
|
||||
sources = [
|
||||
'src/main.c',
|
||||
'src/render.c',
|
||||
'src/dmabuf.c',
|
||||
'src/strlcpy.c',
|
||||
'src/shm.c',
|
||||
'src/screencopy.c',
|
||||
|
@ -69,7 +67,12 @@ sources = [
|
|||
'src/smooth.c',
|
||||
'src/cfg.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 = [
|
||||
|
@ -77,8 +80,8 @@ dependencies = [
|
|||
librt,
|
||||
pixman,
|
||||
aml,
|
||||
egl,
|
||||
glesv2,
|
||||
gbm,
|
||||
drm,
|
||||
wayland_client,
|
||||
neatvnc,
|
||||
xkbcommon,
|
||||
|
@ -105,8 +108,6 @@ configure_file(
|
|||
configuration: config,
|
||||
)
|
||||
|
||||
install_subdir('shaders', install_dir: 'share/wayvnc')
|
||||
|
||||
executable(
|
||||
'wayvnc',
|
||||
sources,
|
||||
|
@ -114,5 +115,3 @@ executable(
|
|||
include_directories: inc,
|
||||
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',
|
||||
'virtual-keyboard-unstable-v1.xml',
|
||||
'xdg-output-unstable-v1.xml',
|
||||
'linux-dmabuf-unstable-v1.xml',
|
||||
]
|
||||
|
||||
client_protos_src = []
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
interface version number is reset.
|
||||
</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">
|
||||
This object is a manager which offers requests to start capturing from a
|
||||
source.
|
||||
|
@ -80,13 +80,18 @@
|
|||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_screencopy_frame_v1" version="2">
|
||||
<interface name="zwlr_screencopy_frame_v1" version="3">
|
||||
<description summary="a frame ready for copy">
|
||||
This object represents a single frame.
|
||||
|
||||
When created, a "buffer" event will be sent. 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.
|
||||
When created, a series of buffer events will be sent, each representing a
|
||||
supported buffer type. The "buffer_done" event is sent afterwards to
|
||||
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
|
||||
before the "ready" event.
|
||||
|
@ -96,14 +101,12 @@
|
|||
</description>
|
||||
|
||||
<event name="buffer">
|
||||
<description summary="buffer information">
|
||||
Provides information about the frame's buffer. This event is sent once
|
||||
as soon as the frame is created.
|
||||
|
||||
The client should then create a buffer with the provided attributes, and
|
||||
send a "copy" request.
|
||||
<description summary="wl_shm buffer information">
|
||||
Provides information about wl_shm buffer parameters that need to be
|
||||
used for this frame. This event is sent once after the frame is created
|
||||
if wl_shm buffers are supported.
|
||||
</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="height" type="uint" summary="buffer height"/>
|
||||
<arg name="stride" type="uint" summary="buffer stride"/>
|
||||
|
@ -112,8 +115,9 @@
|
|||
<request name="copy">
|
||||
<description summary="copy the frame">
|
||||
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
|
||||
have a supported format.
|
||||
correct size, see zwlr_screencopy_frame_v1.buffer and
|
||||
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
|
||||
sent. Otherwise, a "failed" event is sent.
|
||||
|
@ -203,5 +207,26 @@
|
|||
<arg name="width" type="uint" summary="current width"/>
|
||||
<arg name="height" type="uint" summary="current height"/>
|
||||
</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>
|
||||
</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 <signal.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <wayland-client-protocol.h>
|
||||
#include <wayland-client.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <pixman.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#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-virtual-pointer-unstable-v1.h"
|
||||
#include "virtual-keyboard-unstable-v1.h"
|
||||
#include "xdg-output-unstable-v1.h"
|
||||
#include "render.h"
|
||||
#include "dmabuf.h"
|
||||
#include "linux-dmabuf-unstable-v1.h"
|
||||
#include "screencopy.h"
|
||||
#include "strlcpy.h"
|
||||
#include "logging.h"
|
||||
|
@ -51,20 +50,14 @@
|
|||
#include "keyboard.h"
|
||||
#include "seat.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_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 {
|
||||
bool do_exit;
|
||||
|
||||
|
@ -80,13 +73,10 @@ struct wayvnc {
|
|||
|
||||
int pointer_manager_version;
|
||||
|
||||
struct renderer renderer;
|
||||
const struct output* selected_output;
|
||||
const struct seat* selected_seat;
|
||||
|
||||
struct dmabuf_capture dmabuf_backend;
|
||||
struct screencopy screencopy_backend;
|
||||
struct frame_capture* capture_backend;
|
||||
struct screencopy screencopy;
|
||||
struct pointer pointer_backend;
|
||||
struct keyboard keyboard_backend;
|
||||
|
||||
|
@ -97,26 +87,19 @@ struct wayvnc {
|
|||
struct nvnc_display* nvnc_display;
|
||||
struct nvnc_fb* buffer;
|
||||
|
||||
struct damage_refinery damage_refinery;
|
||||
struct pixman_region16 current_damage;
|
||||
|
||||
const char* kb_layout;
|
||||
};
|
||||
|
||||
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 enum frame_capture_backend_type
|
||||
frame_capture_backend_from_string(const char* str)
|
||||
{
|
||||
if (strcmp(str, "screencopy") == 0)
|
||||
return FRAME_CAPTURE_BACKEND_SCREENCOPY;
|
||||
|
||||
if (strcmp(str, "dmabuf") == 0)
|
||||
return FRAME_CAPTURE_BACKEND_DMABUF;
|
||||
|
||||
return FRAME_CAPTURE_BACKEND_NONE;
|
||||
}
|
||||
struct wl_shm* wl_shm = NULL;
|
||||
struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf = NULL;
|
||||
struct gbm_device* gbm_device = NULL;
|
||||
|
||||
static void registry_add(void* data, struct wl_registry* registry,
|
||||
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) {
|
||||
self->screencopy_backend.wl_shm
|
||||
= 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);
|
||||
wl_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
|
||||
self->screencopy_backend.manager =
|
||||
self->screencopy.manager =
|
||||
wl_registry_bind(registry, id,
|
||||
&zwlr_screencopy_manager_v1_interface,
|
||||
2);
|
||||
version);
|
||||
self->screencopy.version = version;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -200,6 +175,12 @@ static void registry_add(void* data, struct wl_registry* registry,
|
|||
1);
|
||||
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,
|
||||
|
@ -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)
|
||||
{
|
||||
output_list_destroy(&self->outputs);
|
||||
|
@ -241,7 +241,7 @@ void wayvnc_destroy(struct wayvnc* self)
|
|||
|
||||
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_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);
|
||||
pointer_destroy(&self->pointer_backend);
|
||||
|
||||
if (self->screencopy_backend.manager)
|
||||
zwlr_screencopy_manager_v1_destroy(self->screencopy_backend.manager);
|
||||
|
||||
if (self->dmabuf_backend.manager)
|
||||
zwlr_export_dmabuf_manager_v1_destroy(self->dmabuf_backend.manager);
|
||||
if (self->screencopy.manager)
|
||||
zwlr_screencopy_manager_v1_destroy(self->screencopy.manager);
|
||||
|
||||
wl_registry_destroy(self->registry);
|
||||
wl_display_disconnect(self->display);
|
||||
}
|
||||
|
||||
|
@ -309,16 +307,13 @@ static int init_wayland(struct wayvnc* self)
|
|||
wl_display_dispatch(self->display);
|
||||
wl_display_roundtrip(self->display);
|
||||
|
||||
if (!self->dmabuf_backend.manager && !self->screencopy_backend.manager) {
|
||||
log_error("Compositor supports neither screencopy nor export-dmabuf! Exiting.\n");
|
||||
if (!self->screencopy.manager) {
|
||||
log_error("Compositor doesn't support screencopy! Exiting.\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
self->dmabuf_backend.fc.on_done = on_capture_done;
|
||||
self->dmabuf_backend.fc.userdata = self;
|
||||
|
||||
self->screencopy_backend.frame_capture.on_done = on_capture_done;
|
||||
self->screencopy_backend.frame_capture.userdata = self;
|
||||
self->screencopy.on_done = on_capture_done;
|
||||
self->screencopy.userdata = self;
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -386,16 +381,6 @@ int init_main_loop(struct wayvnc* self)
|
|||
return 0;
|
||||
}
|
||||
|
||||
uint32_t fourcc_from_gl_format(uint32_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case GL_BGRA_EXT: return DRM_FORMAT_XRGB8888;
|
||||
case GL_RGBA: return DRM_FORMAT_XBGR8888;
|
||||
}
|
||||
|
||||
return DRM_FORMAT_INVALID;
|
||||
}
|
||||
|
||||
static void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y,
|
||||
enum nvnc_button_mask button_mask)
|
||||
{
|
||||
|
@ -469,9 +454,14 @@ failure:
|
|||
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)
|
||||
|
@ -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 wayvnc* self = nvnc_get_userdata(nvnc);
|
||||
|
||||
struct pixman_box16* ext = pixman_region_extents(&self->current_damage);
|
||||
uint32_t y = ext->y1;
|
||||
uint32_t damage_height = ext->y2 - ext->y1;
|
||||
if (!self->screencopy.back)
|
||||
return;
|
||||
|
||||
uint32_t* addr = nvnc_fb_get_addr(fb);
|
||||
uint32_t width = nvnc_fb_get_width(fb);
|
||||
|
||||
renderer_read_frame(&self->renderer, addr + y * width, y, damage_height);
|
||||
DTRACE_PROBE(wayvnc, render_start);
|
||||
|
||||
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);
|
||||
|
||||
DTRACE_PROBE(wayvnc, render_end);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void wayvnc_damage_whole(struct wayvnc* self)
|
||||
{
|
||||
uint16_t width = nvnc_fb_get_width(self->buffer);
|
||||
uint16_t height = nvnc_fb_get_height(self->buffer);
|
||||
|
||||
struct pixman_region16 damage;
|
||||
pixman_region_init_rect(&damage, 0, 0, width, height);
|
||||
wayvnc_damage_region(self, &damage);
|
||||
pixman_region_fini(&damage);
|
||||
}
|
||||
|
||||
static void on_damage_check_done(struct pixman_region16* damage, void* userdata)
|
||||
{
|
||||
struct wayvnc* self = userdata;
|
||||
|
||||
wayvnc_damage_region(self, damage);
|
||||
|
||||
if (wayvnc_start_capture(self, 0) < 0) {
|
||||
log_error("Failed to start capture. Exiting...\n");
|
||||
wayvnc_exit(self);
|
||||
}
|
||||
}
|
||||
|
||||
void wayvnc_process_frame(struct wayvnc* self)
|
||||
{
|
||||
uint32_t format = fourcc_from_gl_format(self->renderer.read_format);
|
||||
uint32_t width = output_get_transformed_width(self->selected_output);
|
||||
uint32_t height = output_get_transformed_height(self->selected_output);
|
||||
bool is_first_frame = false;
|
||||
uint32_t format = self->screencopy.back->format;
|
||||
|
||||
if (!self->buffer) {
|
||||
self->buffer = nvnc_fb_new(width, height, format);
|
||||
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 {
|
||||
// TODO: Reallocate
|
||||
assert(width == nvnc_fb_get_width(self->buffer));
|
||||
assert(height == nvnc_fb_get_height(self->buffer));
|
||||
}
|
||||
|
||||
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);
|
||||
if (width != nvnc_fb_get_width(self->buffer) ||
|
||||
height != nvnc_fb_get_height(self->buffer)) {
|
||||
wayvnc_exit(self);
|
||||
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");
|
||||
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) {
|
||||
case CAPTURE_STOPPED:
|
||||
switch (sc->status) {
|
||||
case SCREENCOPY_STOPPED:
|
||||
break;
|
||||
case CAPTURE_IN_PROGRESS:
|
||||
case SCREENCOPY_IN_PROGRESS:
|
||||
break;
|
||||
case CAPTURE_FATAL:
|
||||
case SCREENCOPY_FATAL:
|
||||
log_error("Fatal error while capturing. Exiting...\n");
|
||||
wayvnc_exit(self);
|
||||
break;
|
||||
case CAPTURE_FAILED:
|
||||
if (wayvnc_start_capture(self, CAPTURE_NOW) < 0) {
|
||||
case SCREENCOPY_FAILED:
|
||||
if (wayvnc_start_capture_immediate(self) < 0) {
|
||||
log_error("Failed to start capture. Exiting...\n");
|
||||
wayvnc_exit(self);
|
||||
}
|
||||
break;
|
||||
case CAPTURE_DONE:
|
||||
case SCREENCOPY_DONE:
|
||||
wayvnc_process_frame(self);
|
||||
break;
|
||||
}
|
||||
|
@ -612,11 +567,11 @@ int wayvnc_usage(FILE* stream, int rc)
|
|||
"Usage: wayvnc [options] [address [port]]\n"
|
||||
"\n"
|
||||
" -C,--config=<path> Select a config file.\n"
|
||||
" -c,--frame-capturing=screencopy|dmabuf Select frame capturing backend.\n"
|
||||
" -o,--output=<name> Select output to capture.\n"
|
||||
" -k,--keyboard=<layout> Select keyboard layout.\n"
|
||||
" -s,--seat=<name> Select seat by name.\n"
|
||||
" -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"
|
||||
"\n";
|
||||
|
||||
|
@ -671,20 +626,21 @@ int main(int argc, char* argv[])
|
|||
int port = 0;
|
||||
|
||||
const char* output_name = NULL;
|
||||
enum frame_capture_backend_type fcbackend = FRAME_CAPTURE_BACKEND_NONE;
|
||||
const char* seat_name = NULL;
|
||||
|
||||
bool overlay_cursor = false;
|
||||
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[] = {
|
||||
{ "config", required_argument, NULL, 'C' },
|
||||
{ "frame-capturing", required_argument, NULL, 'c' },
|
||||
{ "output", required_argument, NULL, 'o' },
|
||||
{ "keyboard", required_argument, NULL, 'k' },
|
||||
{ "seat", required_argument, NULL, 's' },
|
||||
{ "render-cursor", no_argument, NULL, 'r' },
|
||||
{ "max-fps", required_argument, NULL, 'f' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
@ -698,15 +654,6 @@ int main(int argc, char* argv[])
|
|||
case 'C':
|
||||
cfg_file = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
fcbackend = frame_capture_backend_from_string(optarg);
|
||||
if (fcbackend == FRAME_CAPTURE_BACKEND_NONE) {
|
||||
fprintf(stderr, "Invalid backend: %s\n\n",
|
||||
optarg);
|
||||
|
||||
return wayvnc_usage(stderr, 1);
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
output_name = optarg;
|
||||
break;
|
||||
|
@ -719,6 +666,9 @@ int main(int argc, char* argv[])
|
|||
case 'r':
|
||||
overlay_cursor = true;
|
||||
break;
|
||||
case 'f':
|
||||
max_rate = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
return wayvnc_usage(stdout, 0);
|
||||
default:
|
||||
|
@ -795,8 +745,8 @@ int main(int argc, char* argv[])
|
|||
|
||||
self.selected_output = out;
|
||||
self.selected_seat = seat;
|
||||
self.dmabuf_backend.fc.wl_output = out->wl_output;
|
||||
self.screencopy_backend.frame_capture.wl_output = out->wl_output;
|
||||
self.screencopy.wl_output = out->wl_output;
|
||||
self.screencopy.rate_limit = max_rate;
|
||||
|
||||
self.keyboard_backend.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);
|
||||
|
||||
enum renderer_input_type renderer_input_type =
|
||||
fcbackend == FRAME_CAPTURE_BACKEND_DMABUF ?
|
||||
RENDERER_INPUT_DMABUF : RENDERER_INPUT_FB;
|
||||
if (renderer_init(&self.renderer, self.selected_output,
|
||||
renderer_input_type) < 0) {
|
||||
log_error("Failed to initialise renderer\n");
|
||||
char render_node[256];
|
||||
if (find_render_node(render_node, sizeof(render_node)) < 0)
|
||||
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)
|
||||
goto main_loop_failure;
|
||||
|
||||
|
@ -837,44 +790,19 @@ int main(int argc, char* argv[])
|
|||
if (init_nvnc(&self, address, port) < 0)
|
||||
goto nvnc_failure;
|
||||
|
||||
if (self.screencopy_backend.manager)
|
||||
screencopy_init(&self.screencopy_backend);
|
||||
if (self.screencopy.manager)
|
||||
screencopy_init(&self.screencopy);
|
||||
|
||||
if (self.dmabuf_backend.manager)
|
||||
dmabuf_capture_init(&self.dmabuf_backend);
|
||||
|
||||
switch (fcbackend) {
|
||||
case FRAME_CAPTURE_BACKEND_SCREENCOPY:
|
||||
if (!self.screencopy_backend.manager) {
|
||||
if (!self.screencopy.manager) {
|
||||
log_error("screencopy is not supported by compositor\n");
|
||||
goto capture_failure;
|
||||
}
|
||||
|
||||
self.capture_backend = &self.screencopy_backend.frame_capture;
|
||||
break;
|
||||
case FRAME_CAPTURE_BACKEND_DMABUF:
|
||||
if (!self.screencopy_backend.manager) {
|
||||
log_error("export-dmabuf is not supported by compositor\n");
|
||||
goto capture_failure;
|
||||
}
|
||||
|
||||
self.capture_backend = &self.dmabuf_backend.fc;
|
||||
break;
|
||||
case FRAME_CAPTURE_BACKEND_NONE:
|
||||
if (self.screencopy_backend.manager)
|
||||
self.capture_backend = &self.screencopy_backend.frame_capture;
|
||||
else if (self.dmabuf_backend.manager)
|
||||
self.capture_backend = &self.dmabuf_backend.fc;
|
||||
else
|
||||
goto capture_failure;
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
wl_display_dispatch(self.display);
|
||||
|
@ -885,19 +813,22 @@ int main(int argc, char* argv[])
|
|||
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);
|
||||
|
||||
nvnc_display_unref(self.nvnc_display);
|
||||
nvnc_close(self.nvnc);
|
||||
renderer_destroy(&self.renderer);
|
||||
if (self.screencopy_backend.manager)
|
||||
screencopy_destroy(&self.screencopy_backend);
|
||||
if (self.dmabuf_backend.manager)
|
||||
dmabuf_capture_destroy(&self.dmabuf_backend);
|
||||
if (zwp_linux_dmabuf)
|
||||
zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf);
|
||||
if (self.screencopy.manager)
|
||||
screencopy_destroy(&self.screencopy);
|
||||
gbm_device_destroy(gbm_device);
|
||||
wayvnc_destroy(&self);
|
||||
aml_unref(aml);
|
||||
|
||||
|
@ -908,8 +839,10 @@ capture_failure:
|
|||
nvnc_close(self.nvnc);
|
||||
nvnc_failure:
|
||||
main_loop_failure:
|
||||
renderer_destroy(&self.renderer);
|
||||
failure:
|
||||
gbm_device_destroy(gbm_device);
|
||||
if (drm_fd >= 0)
|
||||
close(drm_fd);
|
||||
wayvnc_destroy(&self);
|
||||
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 <aml.h>
|
||||
|
||||
#include "frame-capture.h"
|
||||
#include "wlr-screencopy-unstable-v1.h"
|
||||
#include "buffer.h"
|
||||
#include "shm.h"
|
||||
#include "screencopy.h"
|
||||
#include "smooth.h"
|
||||
#include "time-util.h"
|
||||
#include "render.h"
|
||||
#include "usdt.h"
|
||||
|
||||
#define RATE_LIMIT 20.0 // Hz
|
||||
#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);
|
||||
|
||||
self->frame_capture.status = CAPTURE_STOPPED;
|
||||
self->status = SCREENCOPY_STOPPED;
|
||||
|
||||
if (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,
|
||||
struct zwlr_screencopy_frame_v1* frame,
|
||||
enum wl_shm_format format, uint32_t width,
|
||||
|
@ -107,34 +105,28 @@ static void screencopy_buffer(void* data,
|
|||
{
|
||||
struct screencopy* self = data;
|
||||
|
||||
if (screencopy_buffer_init(self, format, width, height, stride) < 0) {
|
||||
self->frame_capture.status = CAPTURE_FATAL;
|
||||
screencopy_stop(&self->frame_capture);
|
||||
self->frame_capture.on_done(&self->frame_capture);
|
||||
self->wl_shm_format = format;
|
||||
self->wl_shm_width = width;
|
||||
self->wl_shm_height = height;
|
||||
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,
|
||||
struct zwlr_screencopy_frame_v1* frame,
|
||||
uint32_t flags)
|
||||
{
|
||||
(void)data;
|
||||
(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,
|
||||
|
@ -149,24 +141,25 @@ static void screencopy_ready(void* data,
|
|||
|
||||
DTRACE_PROBE1(wayvnc, screencopy_ready, self);
|
||||
|
||||
screencopy_stop(&self->frame_capture);
|
||||
screencopy_stop(self);
|
||||
|
||||
self->last_time = gettime_us();
|
||||
|
||||
double delay = (self->last_time - self->start_time) * 1.0e-6;
|
||||
self->delay = smooth(&self->delay_smoother, delay);
|
||||
|
||||
if (self->is_immediate_copy) {
|
||||
self->frame_capture.damage_hint.x = 0;
|
||||
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;
|
||||
}
|
||||
if (self->is_immediate_copy)
|
||||
wv_buffer_damage_whole(self->front);
|
||||
|
||||
self->frame_capture.status = CAPTURE_DONE;
|
||||
self->frame_capture.on_done(&self->frame_capture);
|
||||
if (self->back)
|
||||
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,
|
||||
|
@ -176,9 +169,9 @@ static void screencopy_failed(void* data,
|
|||
|
||||
DTRACE_PROBE1(wayvnc, screencopy_failed, self);
|
||||
|
||||
screencopy_stop(&self->frame_capture);
|
||||
self->frame_capture.status = CAPTURE_FAILED;
|
||||
self->frame_capture.on_done(&self->frame_capture);
|
||||
screencopy_stop(self);
|
||||
self->status = SCREENCOPY_FAILED;
|
||||
self->on_done(self);
|
||||
}
|
||||
|
||||
static void screencopy_damage(void* data,
|
||||
|
@ -190,20 +183,17 @@ static void screencopy_damage(void* data,
|
|||
|
||||
DTRACE_PROBE1(wayvnc, screencopy_damage, self);
|
||||
|
||||
self->frame_capture.damage_hint.x = x;
|
||||
self->frame_capture.damage_hint.y = y;
|
||||
self->frame_capture.damage_hint.width = width;
|
||||
self->frame_capture.damage_hint.height = height;
|
||||
wv_buffer_damage_rect(self->front, x, y, width, 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);
|
||||
|
||||
static const struct zwlr_screencopy_frame_v1_listener frame_listener = {
|
||||
.buffer = screencopy_buffer,
|
||||
.linux_dmabuf = screencopy_linux_dmabuf,
|
||||
.buffer_done = screencopy_buffer_done,
|
||||
.flags = screencopy_flags,
|
||||
.ready = screencopy_ready,
|
||||
.failed = screencopy_failed,
|
||||
|
@ -212,10 +202,8 @@ static int screencopy__start_capture(struct frame_capture* fc)
|
|||
|
||||
self->start_time = gettime_us();
|
||||
|
||||
self->frame =
|
||||
zwlr_screencopy_manager_v1_capture_output(self->manager,
|
||||
fc->overlay_cursor,
|
||||
fc->wl_output);
|
||||
self->frame = zwlr_screencopy_manager_v1_capture_output(self->manager,
|
||||
self->overlay_cursor, self->wl_output);
|
||||
if (!self->frame)
|
||||
return -1;
|
||||
|
||||
|
@ -228,58 +216,51 @@ static int screencopy__start_capture(struct frame_capture* fc)
|
|||
static void screencopy__poll(void* 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,
|
||||
enum frame_capture_options options)
|
||||
static int screencopy__start(struct screencopy* self, bool is_immediate_copy)
|
||||
{
|
||||
struct screencopy* self = (void*)fc;
|
||||
|
||||
if (fc->status == CAPTURE_IN_PROGRESS)
|
||||
if (self->status == SCREENCOPY_IN_PROGRESS)
|
||||
return -1;
|
||||
|
||||
self->is_immediate_copy = !!(options & CAPTURE_NOW);
|
||||
self->is_immediate_copy = is_immediate_copy;
|
||||
|
||||
uint64_t now = gettime_us();
|
||||
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) {
|
||||
aml_set_duration(self->timer, time_left);
|
||||
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,
|
||||
struct renderer* renderer, struct nvnc_fb* fb)
|
||||
int screencopy_start(struct screencopy* self)
|
||||
{
|
||||
struct screencopy* self = (void*)fc;
|
||||
return screencopy__start(self, false);
|
||||
}
|
||||
|
||||
uint32_t width = fc->frame_info.width;
|
||||
uint32_t height = fc->frame_info.height;
|
||||
uint32_t stride = fc->frame_info.stride;
|
||||
uint32_t format = fc->frame_info.fourcc_format;
|
||||
|
||||
render_framebuffer(renderer, self->pixels, format, width, height, stride);
|
||||
int screencopy_start_immediate(struct screencopy* self)
|
||||
{
|
||||
return screencopy__start(self, true);
|
||||
}
|
||||
|
||||
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);
|
||||
assert(self->timer);
|
||||
|
||||
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)
|
||||
|
@ -287,6 +268,10 @@ void screencopy_destroy(struct screencopy* self)
|
|||
aml_stop(aml_get_default(), self->timer);
|
||||
aml_unref(self->timer);
|
||||
|
||||
if (self->buffer)
|
||||
wl_buffer_destroy(self->buffer);
|
||||
if (self->back)
|
||||
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 (only sending)', 'sdt_neatvnc', 'send_fb_start', 'send_fb_done'),
|
||||
StateTracker('Screencopy', 'sdt_wayvnc', 'screencopy_start', 'screencopy_ready'),
|
||||
StateTracker('Dmabuf', 'sdt_wayvnc', 'dmabuf_capture_start', 'dmabuf_frame_ready'),
|
||||
StateTracker('Render', 'sdt_wayvnc', 'render_framebuffer_start', 'render_framebuffer_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'),
|
||||
StateTracker('Refine damage', 'sdt_wayvnc', 'refine_damage_start', 'refine_damage_end'),
|
||||
StateTracker('Render', 'sdt_wayvnc', 'render_start', 'render_end'),
|
||||
]
|
||||
|
||||
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