Add some code for rendering screencopy frames
parent
1132dd6a3c
commit
ddaea0ceab
|
@ -2,3 +2,4 @@ build-*
|
||||||
*.swp
|
*.swp
|
||||||
protocols/build
|
protocols/build
|
||||||
.clang_complete
|
.clang_complete
|
||||||
|
perf.*
|
||||||
|
|
|
@ -37,6 +37,8 @@ int renderer_init(struct renderer* self, uint32_t width, uint32_t height);
|
||||||
void renderer_destroy(struct renderer* self);
|
void renderer_destroy(struct renderer* self);
|
||||||
|
|
||||||
int render_dmabuf_frame(struct renderer* self, struct dmabuf_frame* frame);
|
int render_dmabuf_frame(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 */
|
/* Copy a horizontal stripe from the GL frame into a pixel buffer */
|
||||||
void render_copy_pixels(struct renderer* self, void* dst, uint32_t y,
|
void render_copy_pixels(struct renderer* self, void* dst, uint32_t y,
|
||||||
|
|
|
@ -19,6 +19,10 @@ enum screencopy_status {
|
||||||
struct screencopy {
|
struct screencopy {
|
||||||
struct wl_shm* wl_shm;
|
struct wl_shm* wl_shm;
|
||||||
struct wl_buffer* buffer;
|
struct wl_buffer* buffer;
|
||||||
|
|
||||||
|
void* pixels;
|
||||||
|
size_t bufsize;
|
||||||
|
|
||||||
struct zwlr_screencopy_manager_v1* manager;
|
struct zwlr_screencopy_manager_v1* manager;
|
||||||
struct zwlr_screencopy_frame_v1* frame;
|
struct zwlr_screencopy_frame_v1* frame;
|
||||||
|
|
||||||
|
@ -26,4 +30,14 @@ struct screencopy {
|
||||||
struct wl_output* output;
|
struct wl_output* output;
|
||||||
enum screencopy_status status;
|
enum screencopy_status status;
|
||||||
void (*on_done)(struct screencopy*);
|
void (*on_done)(struct screencopy*);
|
||||||
|
|
||||||
|
enum wl_shm_format format;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t stride;
|
||||||
|
|
||||||
|
void* userdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int screencopy_start(struct screencopy* self);
|
||||||
|
void screencopy_stop(struct screencopy* self);
|
||||||
|
|
123
src/main.c
123
src/main.c
|
@ -33,6 +33,7 @@
|
||||||
#include "wlr-screencopy-unstable-v1.h"
|
#include "wlr-screencopy-unstable-v1.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "dmabuf.h"
|
#include "dmabuf.h"
|
||||||
|
#include "screencopy.h"
|
||||||
#include "strlcpy.h"
|
#include "strlcpy.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
|
||||||
|
@ -55,22 +56,24 @@ struct wayvnc {
|
||||||
struct renderer renderer;
|
struct renderer renderer;
|
||||||
const struct output* selected_output;
|
const struct output* selected_output;
|
||||||
|
|
||||||
struct wl_shm* wl_shm;
|
|
||||||
struct zwlr_screencopy_manager_v1* screencopy_manager;
|
|
||||||
|
|
||||||
struct dmabuf_capture dmabuf_backend;
|
struct dmabuf_capture dmabuf_backend;
|
||||||
|
struct screencopy screencopy_backend;
|
||||||
|
|
||||||
uv_poll_t wayland_poller;
|
uv_poll_t wayland_poller;
|
||||||
uv_prepare_t flusher;
|
uv_prepare_t flusher;
|
||||||
uv_signal_t signal_handler;
|
uv_signal_t signal_handler;
|
||||||
|
uv_timer_t performance_timer;
|
||||||
|
|
||||||
struct nvnc* nvnc;
|
struct nvnc* nvnc;
|
||||||
|
|
||||||
struct nvnc_fb* current_fb;
|
struct nvnc_fb* current_fb;
|
||||||
struct nvnc_fb* last_fb;
|
struct nvnc_fb* last_fb;
|
||||||
|
|
||||||
|
uint32_t n_frames;
|
||||||
};
|
};
|
||||||
|
|
||||||
void on_capture_done(struct dmabuf_capture* capture);
|
void on_capture_done(struct dmabuf_capture* capture);
|
||||||
|
void on_screencopy_done(struct screencopy* capture);
|
||||||
|
|
||||||
static void output_handle_geometry(void* data, struct wl_output* wl_output,
|
static void output_handle_geometry(void* data, struct wl_output* wl_output,
|
||||||
int32_t x, int32_t y, int32_t phys_width,
|
int32_t x, int32_t y, int32_t phys_width,
|
||||||
|
@ -166,8 +169,8 @@ static void registry_add(void* data, struct wl_registry* registry,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(interface, wl_shm_interface.name) == 0) {
|
if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||||
self->wl_shm = wl_registry_bind(registry, id, &wl_shm_interface,
|
self->screencopy_backend.wl_shm
|
||||||
1);
|
= wl_registry_bind(registry, id, &wl_shm_interface, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +183,7 @@ static void registry_add(void* data, struct wl_registry* registry,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
|
if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
|
||||||
self->screencopy_manager =
|
self->screencopy_backend.manager =
|
||||||
wl_registry_bind(registry, id,
|
wl_registry_bind(registry, id,
|
||||||
&zwlr_screencopy_manager_v1_interface,
|
&zwlr_screencopy_manager_v1_interface,
|
||||||
2);
|
2);
|
||||||
|
@ -240,6 +243,9 @@ static int init_wayland(struct wayvnc* self)
|
||||||
self->dmabuf_backend.on_done = on_capture_done;
|
self->dmabuf_backend.on_done = on_capture_done;
|
||||||
self->dmabuf_backend.userdata = self;
|
self->dmabuf_backend.userdata = self;
|
||||||
|
|
||||||
|
self->screencopy_backend.on_done = on_screencopy_done;
|
||||||
|
self->screencopy_backend.userdata = self;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
export_manager_failure:
|
export_manager_failure:
|
||||||
|
@ -256,6 +262,7 @@ int wayvnc_select_first_output(struct wayvnc* self)
|
||||||
wl_list_for_each(out, &self->outputs, link) {
|
wl_list_for_each(out, &self->outputs, link) {
|
||||||
self->selected_output = out;
|
self->selected_output = out;
|
||||||
self->dmabuf_backend.output = out->wl_output;
|
self->dmabuf_backend.output = out->wl_output;
|
||||||
|
self->screencopy_backend.output = out->wl_output;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,6 +297,14 @@ void on_signal(uv_signal_t* handle, int signo)
|
||||||
wayvnc_exit();
|
wayvnc_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_performance_tick(uv_timer_t* handle)
|
||||||
|
{
|
||||||
|
struct wayvnc* self = wl_container_of(handle, self, performance_timer);
|
||||||
|
|
||||||
|
printf("%"PRIu32" FPS\n", self->n_frames);
|
||||||
|
self->n_frames = 0;
|
||||||
|
}
|
||||||
|
|
||||||
int init_main_loop(struct wayvnc* self)
|
int init_main_loop(struct wayvnc* self)
|
||||||
{
|
{
|
||||||
uv_loop_t* loop = uv_default_loop();
|
uv_loop_t* loop = uv_default_loop();
|
||||||
|
@ -304,6 +319,10 @@ int init_main_loop(struct wayvnc* self)
|
||||||
uv_signal_init(loop, &self->signal_handler);
|
uv_signal_init(loop, &self->signal_handler);
|
||||||
uv_signal_start(&self->signal_handler, on_signal, SIGINT);
|
uv_signal_start(&self->signal_handler, on_signal, SIGINT);
|
||||||
|
|
||||||
|
uv_timer_init(loop, &self->performance_timer);
|
||||||
|
uv_timer_start(&self->performance_timer, on_performance_tick, 1000,
|
||||||
|
1000);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,7 +359,8 @@ int init_nvnc(struct wayvnc* self, const char* addr, uint16_t port)
|
||||||
|
|
||||||
int wayvnc_start_capture(struct wayvnc* self)
|
int wayvnc_start_capture(struct wayvnc* self)
|
||||||
{
|
{
|
||||||
return dmabuf_capture_start(&self->dmabuf_backend);
|
return screencopy_start(&self->screencopy_backend);
|
||||||
|
// return dmabuf_capture_start(&self->dmabuf_backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_damage_check_done(struct pixman_region16* damage, void* userdata)
|
void on_damage_check_done(struct pixman_region16* damage, void* userdata)
|
||||||
|
@ -356,6 +376,34 @@ void on_damage_check_done(struct pixman_region16* damage, void* userdata)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wayvnc_update_vnc(struct wayvnc* self, struct nvnc_fb* fb)
|
||||||
|
{
|
||||||
|
uint32_t width = nvnc_fb_get_width(fb);
|
||||||
|
uint32_t height = nvnc_fb_get_height(fb);
|
||||||
|
|
||||||
|
if (self->last_fb)
|
||||||
|
nvnc_fb_unref(self->last_fb);
|
||||||
|
|
||||||
|
self->last_fb = self->current_fb;
|
||||||
|
self->current_fb = fb;
|
||||||
|
|
||||||
|
if (self->last_fb) {
|
||||||
|
nvnc_check_damage(self->current_fb, self->last_fb, 0, 0,
|
||||||
|
width, height, on_damage_check_done, self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pixman_region16 damage;
|
||||||
|
pixman_region_init_rect(&damage, 0, 0, width, height);
|
||||||
|
nvnc_feed_frame(self->nvnc, self->current_fb, &damage);
|
||||||
|
pixman_region_fini(&damage);
|
||||||
|
|
||||||
|
if (wayvnc_start_capture(self) < 0) {
|
||||||
|
log_error("Failed to start capture. Exiting...\n");
|
||||||
|
wayvnc_exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void wayvnc_process_frame(struct wayvnc* self, struct dmabuf_frame* frame)
|
void wayvnc_process_frame(struct wayvnc* self, struct dmabuf_frame* frame)
|
||||||
{
|
{
|
||||||
uint32_t format = fourcc_from_gl_format(self->renderer.read_format);
|
uint32_t format = fourcc_from_gl_format(self->renderer.read_format);
|
||||||
|
@ -366,28 +414,26 @@ void wayvnc_process_frame(struct wayvnc* self, struct dmabuf_frame* frame)
|
||||||
render_dmabuf_frame(&self->renderer, frame);
|
render_dmabuf_frame(&self->renderer, frame);
|
||||||
render_copy_pixels(&self->renderer, addr, 0, frame->height);
|
render_copy_pixels(&self->renderer, addr, 0, frame->height);
|
||||||
|
|
||||||
if (self->last_fb)
|
wayvnc_update_vnc(self, fb);
|
||||||
nvnc_fb_unref(self->last_fb);
|
}
|
||||||
|
|
||||||
self->last_fb = self->current_fb;
|
void wayvnc_process_screen(struct wayvnc* self)
|
||||||
self->current_fb = fb;
|
{
|
||||||
|
uint32_t format = fourcc_from_gl_format(self->renderer.read_format);
|
||||||
|
|
||||||
if (self->last_fb) {
|
void* pixels = self->screencopy_backend.pixels;
|
||||||
nvnc_check_damage(self->current_fb, self->last_fb, 0, 0,
|
uint32_t width = self->screencopy_backend.width;
|
||||||
frame->width, frame->height,
|
uint32_t height = self->screencopy_backend.height;
|
||||||
on_damage_check_done, self);
|
uint32_t stride = self->screencopy_backend.stride;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct pixman_region16 damage;
|
struct nvnc_fb* fb = nvnc_fb_new(width, height, format);
|
||||||
pixman_region_init_rect(&damage, 0, 0, frame->width, frame->height);
|
void* addr = nvnc_fb_get_addr(fb);
|
||||||
nvnc_feed_frame(self->nvnc, self->current_fb, &damage);
|
|
||||||
pixman_region_fini(&damage);
|
|
||||||
|
|
||||||
if (wayvnc_start_capture(self) < 0) {
|
render_framebuffer(&self->renderer, pixels, format, width, height,
|
||||||
log_error("Failed to start capture. Exiting...\n");
|
stride);
|
||||||
wayvnc_exit();
|
render_copy_pixels(&self->renderer, addr, 0, height);
|
||||||
}
|
|
||||||
|
wayvnc_update_vnc(self, fb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_capture_done(struct dmabuf_capture* capture)
|
void on_capture_done(struct dmabuf_capture* capture)
|
||||||
|
@ -408,6 +454,7 @@ void on_capture_done(struct dmabuf_capture* capture)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DMABUF_CAPTURE_DONE:
|
case DMABUF_CAPTURE_DONE:
|
||||||
|
self->n_frames++;
|
||||||
wayvnc_process_frame(self, &capture->frame);
|
wayvnc_process_frame(self, &capture->frame);
|
||||||
break;
|
break;
|
||||||
case DMABUF_CAPTURE_UNSPEC:
|
case DMABUF_CAPTURE_UNSPEC:
|
||||||
|
@ -415,6 +462,30 @@ void on_capture_done(struct dmabuf_capture* capture)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_screencopy_done(struct screencopy* capture)
|
||||||
|
{
|
||||||
|
struct wayvnc* self = capture->userdata;
|
||||||
|
|
||||||
|
switch (capture->status) {
|
||||||
|
case SCREENCOPY_STATUS_CAPTURING:
|
||||||
|
break;
|
||||||
|
case SCREENCOPY_STATUS_FATAL:
|
||||||
|
log_error("Fatal error while capturing. Exiting...\n");
|
||||||
|
wayvnc_exit();
|
||||||
|
break;
|
||||||
|
case SCREENCOPY_STATUS_FAILED:
|
||||||
|
if (wayvnc_start_capture(self) < 0) {
|
||||||
|
log_error("Failed to start capture. Exiting...\n");
|
||||||
|
wayvnc_exit();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCREENCOPY_STATUS_DONE:
|
||||||
|
self->n_frames++;
|
||||||
|
wayvnc_process_screen(self);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
struct wayvnc self = { 0 };
|
struct wayvnc self = { 0 };
|
||||||
|
@ -454,7 +525,7 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
||||||
|
|
||||||
dmabuf_capture_stop(&self.dmabuf_backend);
|
// dmabuf_capture_stop(&self.dmabuf_backend);
|
||||||
|
|
||||||
if (self.current_fb) nvnc_fb_unref(self.current_fb);
|
if (self.current_fb) nvnc_fb_unref(self.current_fb);
|
||||||
if (self.last_fb) nvnc_fb_unref(self.last_fb);
|
if (self.last_fb) nvnc_fb_unref(self.last_fb);
|
||||||
|
|
29
src/render.c
29
src/render.c
|
@ -54,22 +54,13 @@
|
||||||
X_GL_EXTENSIONS
|
X_GL_EXTENSIONS
|
||||||
#undef X
|
#undef X
|
||||||
|
|
||||||
GLenum gl_format_from_wl_shm(GLuint* result, enum wl_shm_format format)
|
int gl_format_from_wl_shm(GLenum* result, enum wl_shm_format format)
|
||||||
{
|
{
|
||||||
switch (format) {
|
|
||||||
case WL_SHM_FORMAT_ARGB8888:
|
|
||||||
case WL_SHM_FORMAT_XRGB8888:
|
|
||||||
*result = GL_BGRA_EXT;
|
*result = GL_BGRA_EXT;
|
||||||
return 0;
|
|
||||||
case WL_SHM_FORMAT_ABGR8888:
|
|
||||||
case WL_SHM_FORMAT_XBGR8888:
|
|
||||||
*result = GL_RGBA;
|
|
||||||
return 0;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
// TODO: Actually detect the format
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void* gl_load_single_extension(const char* name)
|
static inline void* gl_load_single_extension(const char* name)
|
||||||
|
@ -185,6 +176,15 @@ static const char dmabuf_fragment_src[] =
|
||||||
" gl_FragColor = texture2D(u_tex, v_texture);\n"
|
" gl_FragColor = texture2D(u_tex, v_texture);\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
|
|
||||||
|
static const char texture_vertex_src[] =
|
||||||
|
"attribute vec2 pos;\n"
|
||||||
|
"attribute vec2 texture;\n"
|
||||||
|
"varying vec2 v_texture;\n"
|
||||||
|
"void main() {\n"
|
||||||
|
" v_texture = texture;\n"
|
||||||
|
" gl_Position = vec4(pos, 0, 1);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
static const char texture_fragment_src[] =
|
static const char texture_fragment_src[] =
|
||||||
"precision mediump float;\n"
|
"precision mediump float;\n"
|
||||||
"uniform sampler2D u_tex;\n"
|
"uniform sampler2D u_tex;\n"
|
||||||
|
@ -345,7 +345,7 @@ int renderer_init(struct renderer* self, uint32_t width, uint32_t height)
|
||||||
goto shader_failure;
|
goto shader_failure;
|
||||||
|
|
||||||
if (gl_compile_shader_program(&self->texture_shader_program,
|
if (gl_compile_shader_program(&self->texture_shader_program,
|
||||||
dmabuf_vertex_src,
|
texture_vertex_src,
|
||||||
texture_fragment_src) < 0)
|
texture_fragment_src) < 0)
|
||||||
goto shader_failure;
|
goto shader_failure;
|
||||||
|
|
||||||
|
@ -447,6 +447,7 @@ int render_framebuffer(struct renderer* self, const void* addr, uint32_t format,
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / 4);
|
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / 4);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, self->read_format, width, height, 0,
|
glTexImage2D(GL_TEXTURE_2D, 0, self->read_format, width, height, 0,
|
||||||
gl_format, GL_UNSIGNED_BYTE, addr);
|
gl_format, GL_UNSIGNED_BYTE, addr);
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
|
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
|
||||||
|
|
||||||
glUseProgram(self->texture_shader_program);
|
glUseProgram(self->texture_shader_program);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
|
@ -36,18 +37,33 @@ static int screencopy_buffer_init(struct screencopy* self,
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return -1;
|
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);
|
struct wl_shm_pool* pool = wl_shm_create_pool(self->wl_shm, fd, size);
|
||||||
close(fd);
|
|
||||||
if (!pool)
|
if (!pool)
|
||||||
return -1;
|
goto shm_failure;
|
||||||
|
|
||||||
struct wl_buffer* buffer =
|
struct wl_buffer* buffer =
|
||||||
wl_shm_pool_create_buffer(pool, 0, width, height, stride,
|
wl_shm_pool_create_buffer(pool, 0, width, height, stride,
|
||||||
format);
|
format);
|
||||||
wl_shm_pool_destroy(pool);
|
wl_shm_pool_destroy(pool);
|
||||||
|
if (!buffer)
|
||||||
|
goto shm_failure;
|
||||||
|
|
||||||
self->buffer = buffer;
|
self->buffer = buffer;
|
||||||
|
self->pixels = addr;
|
||||||
|
self->bufsize = size;
|
||||||
|
|
||||||
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
shm_failure:
|
||||||
|
munmap(addr, size);
|
||||||
|
mmap_failure:
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void screencopy_stop(struct screencopy* self)
|
void screencopy_stop(struct screencopy* self)
|
||||||
|
@ -71,6 +87,11 @@ static void screencopy_buffer(void* data,
|
||||||
self->on_done(self);
|
self->on_done(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self->format = format;
|
||||||
|
self->width = width;
|
||||||
|
self->height = height;
|
||||||
|
self->stride = stride;
|
||||||
|
|
||||||
zwlr_screencopy_frame_v1_copy(self->frame, self->buffer);
|
zwlr_screencopy_frame_v1_copy(self->frame, self->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue