Redesign framebuffer update loop

Rendering may now only happen inside the rendering callback. The user is
also allowed to change out the entire buffer in the callback.

The callback is triggered by nvnc_damage_region(), nvnc_damage_whole()
and/or framebuffer update requests.

This fixes #26
pull/30/head
Andri Yngvason 2020-04-07 22:21:11 +00:00
parent 048b796ff5
commit 77b866096d
5 changed files with 118 additions and 70 deletions

View File

@ -25,8 +25,12 @@
#include <pixman.h>
#include <libdrm/drm_fourcc.h>
#define MAX_COORD 128
struct draw {
struct nvnc_fb* fb;
struct { uint16_t x, y; } coord[MAX_COORD];
int index;
};
void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y,
@ -41,19 +45,41 @@ void on_pointer_event(struct nvnc_client* client, uint16_t x, uint16_t y,
struct draw* draw = nvnc_get_userdata(server);
assert(draw);
uint32_t* image = nvnc_fb_get_addr(draw->fb);
int width = nvnc_fb_get_width(draw->fb);
int height = nvnc_fb_get_height(draw->fb);
image[x + y * width] = 0;
if (draw->index >= MAX_COORD)
return;
draw->coord[draw->index].x = x;
draw->coord[draw->index].y = y;
draw->index++;
struct pixman_region16 region;
pixman_region_init_rect(&region, 0, 0, width, height);
pixman_region_intersect_rect(&region, &region, x, y, 1, 1);
nvnc_feed_frame(server, draw->fb, &region);
nvnc_damage_region(server, &region);
pixman_region_fini(&region);
}
void on_render(struct nvnc* server, struct nvnc_fb* fb)
{
struct draw* draw = nvnc_get_userdata(server);
assert(draw);
uint32_t* image = nvnc_fb_get_addr(draw->fb);
int width = nvnc_fb_get_width(draw->fb);
for (int i = 0; i < draw->index; ++i) {
uint16_t x = draw->coord[i].x;
uint16_t y = draw->coord[i].y;
image[x + y * width] = 0;
}
draw->index = 0;
}
void on_sigint()
{
aml_exit(aml_get_default());
@ -61,7 +87,7 @@ void on_sigint()
int main(int argc, char* argv[])
{
struct draw draw;
struct draw draw = { 0 };
int width = 500, height = 500;
uint32_t format = DRM_FORMAT_RGBX8888;
@ -81,6 +107,8 @@ int main(int argc, char* argv[])
nvnc_set_name(server, "Draw");
nvnc_set_pointer_fn(server, on_pointer_event);
nvnc_set_userdata(server, &draw);
nvnc_set_buffer(server, draw.fb);
nvnc_set_render_fn(server, on_render);
struct aml_signal* sig = aml_signal_new(SIGINT, on_sigint, NULL, NULL);
aml_start(aml_get_default(), sig);

View File

@ -54,13 +54,10 @@ int main(int argc, char* argv[])
uint32_t fourcc_format = nvnc_fb_get_fourcc_format(fb);
nvnc_set_dimensions(server, width, height, fourcc_format);
nvnc_set_buffer(server, fb);
nvnc_set_name(server, file);
struct pixman_region16 region;
pixman_region_init_rect(&region, 0, 0, nvnc_fb_get_width(fb),
nvnc_fb_get_height(fb));
nvnc_feed_frame(server, fb, &region);
pixman_region_fini(&region);
nvnc_damage_whole(server);
struct aml_signal* sig = aml_signal_new(SIGINT, on_sigint, NULL, NULL);
aml_start(aml_get_default(), sig);

View File

@ -51,6 +51,7 @@ enum nvnc_client_state {
struct nvnc;
struct stream;
struct aml_handler;
struct aml_idle;
struct nvnc_common {
void* userdata;
@ -70,7 +71,6 @@ struct nvnc_client {
struct pixman_region16 damage;
int n_pending_requests;
bool is_updating;
bool needs_whole_frame;
nvnc_client_fn cleanup_fn;
z_stream z_stream;
struct tight_encoder tight_encoder;
@ -92,6 +92,7 @@ struct nvnc {
struct nvnc_common common;
int fd;
struct aml_handler* poll_handle;
struct aml_idle* dispatch_handler;
struct nvnc_client_list clients;
struct vnc_display display;
void* userdata;
@ -99,7 +100,8 @@ struct nvnc {
nvnc_pointer_fn pointer_fn;
nvnc_fb_req_fn fb_req_fn;
nvnc_client_fn new_client_fn;
struct nvnc_fb* frame;
nvnc_render_fn render_fn;
struct nvnc_fb* buffer;
#ifdef ENABLE_TLS
gnutls_certificate_credentials_t tls_creds;

View File

@ -47,6 +47,7 @@ typedef void (*nvnc_client_fn)(struct nvnc_client*);
typedef void (*nvnc_damage_fn)(struct pixman_region16* damage, void* userdata);
typedef bool (*nvnc_auth_fn)(const char* username, const char* password,
void* userdata);
typedef void (*nvnc_render_fn)(struct nvnc*, struct nvnc_fb*);
struct nvnc* nvnc_open(const char* addr, uint16_t port);
void nvnc_close(struct nvnc* self);
@ -59,12 +60,15 @@ struct nvnc* nvnc_get_server(const struct nvnc_client* client);
void nvnc_set_dimensions(struct nvnc* self, uint16_t width, uint16_t height,
uint32_t fourcc_format);
void nvnc_set_buffer(struct nvnc*, struct nvnc_fb*);
void nvnc_set_name(struct nvnc* self, const char* name);
void nvnc_set_key_fn(struct nvnc* self, nvnc_key_fn);
void nvnc_set_pointer_fn(struct nvnc* self, nvnc_pointer_fn);
void nvnc_set_fb_req_fn(struct nvnc* self, nvnc_fb_req_fn);
void nvnc_set_new_client_fn(struct nvnc* self, nvnc_client_fn);
void nvnc_set_render_fn(struct nvnc* self, nvnc_render_fn fn);
void nvnc_set_client_cleanup_fn(struct nvnc_client* self, nvnc_client_fn fn);
bool nvnc_has_auth(void);
@ -77,9 +81,6 @@ struct nvnc_fb* nvnc_fb_new(uint16_t width, uint16_t height,
void nvnc_fb_ref(struct nvnc_fb* fb);
void nvnc_fb_unref(struct nvnc_fb* fb);
bool nvnc_fb_lock(struct nvnc_fb*);
void nvnc_fb_unlock(struct nvnc_fb*);
enum nvnc_fb_flags nvnc_fb_get_flags(const struct nvnc_fb*);
void nvnc_fb_set_flags(struct nvnc_fb*, enum nvnc_fb_flags);
@ -88,12 +89,8 @@ uint16_t nvnc_fb_get_width(const struct nvnc_fb* fb);
uint16_t nvnc_fb_get_height(const struct nvnc_fb* fb);
uint32_t nvnc_fb_get_fourcc_format(const struct nvnc_fb* fb);
/*
* Feed a new frame to the server. The damaged region is sent to clients
* immediately.
*/
int nvnc_feed_frame(struct nvnc* self, struct nvnc_fb* fb,
const struct pixman_region16* damage);
void nvnc_damage_region(struct nvnc*, const struct pixman_region16* damage);
void nvnc_damage_whole(struct nvnc*);
/*
* Find the regions that differ between fb0 and fb1. Regions outside the hinted

View File

@ -465,7 +465,7 @@ static int on_client_set_encodings(struct nvnc_client* client)
static void process_fb_update_requests(struct nvnc_client* client)
{
if (!client->server->frame)
if (!client->server->buffer)
return;
if (client->net_stream->state == STREAM_STATE_CLOSED)
@ -477,22 +477,8 @@ static void process_fb_update_requests(struct nvnc_client* client)
if (client->is_updating || client->n_pending_requests == 0)
return;
struct nvnc_fb* fb = client->server->frame;
if (!nvnc_fb_lock(fb))
return;
if (client->needs_whole_frame && (fb->flags & NVNC_FB_PARTIAL))
goto abort;
client->is_updating = true;
if (schedule_client_update_fb(client) < 0)
goto abort;
return;
abort:
nvnc_fb_unlock(fb);
client->is_updating = false;
}
@ -513,9 +499,6 @@ static int on_client_fb_update_request(struct nvnc_client* client)
int width = ntohs(msg->width);
int height = ntohs(msg->height);
if (!incremental)
client->needs_whole_frame = true;
client->n_pending_requests++;
/* Note: The region sent from the client is ignored for incremental
@ -531,8 +514,6 @@ static int on_client_fb_update_request(struct nvnc_client* client)
if (fn)
fn(client, incremental, x, y, width, height);
process_fb_update_requests(client);
return sizeof(*msg);
}
@ -812,6 +793,31 @@ failure:
return fd;
}
bool nvnc__is_damaged(struct nvnc* self)
{
struct nvnc_client* client;
LIST_FOREACH(client, &self->clients, link)
if (pixman_region_not_empty(&client->damage))
return true;
return false;
}
void on_main_dispatch(void* aml_obj)
{
struct nvnc* self = aml_get_userdata(aml_obj);
if (!nvnc__is_damaged(self))
return;
if (self->render_fn)
self->render_fn(self, self->buffer);
struct nvnc_client* client;
LIST_FOREACH(client, &self->clients, link)
process_fb_update_requests(client);
}
EXPORT
struct nvnc* nvnc_open(const char* address, uint16_t port)
{
@ -837,11 +843,22 @@ struct nvnc* nvnc_open(const char* address, uint16_t port)
goto failure;
if (aml_start(aml_get_default(), self->poll_handle) < 0)
goto start_failure;
goto poll_start_failure;
self->dispatch_handler = aml_idle_new(on_main_dispatch, self, NULL);
if (!self->dispatch_handler)
goto new_idle_failure;
if (aml_start(aml_get_default(), self->dispatch_handler) < 0)
goto idle_start_failure;
return self;
start_failure:
idle_start_failure:
aml_unref(self->dispatch_handler);
new_idle_failure:
aml_stop(aml_get_default(), self->poll_handle);
poll_start_failure:
aml_unref(self->poll_handle);
failure:
close(self->fd);
@ -853,13 +870,14 @@ void nvnc_close(struct nvnc* self)
{
struct nvnc_client* client;
if (self->frame)
nvnc_fb_unref(self->frame);
if (self->buffer)
nvnc_fb_unref(self->buffer);
struct nvnc_client* tmp;
LIST_FOREACH_SAFE (client, &self->clients, link, tmp)
client_unref(client);
aml_stop(aml_get_default(), self->dispatch_handler);
aml_stop(aml_get_default(), self->poll_handle);
close(self->fd);
@ -943,7 +961,6 @@ void on_client_update_fb_done(void* work)
struct nvnc_client* client = update->client;
struct vec* frame = &update->frame;
nvnc_fb_unlock(update->fb);
nvnc_fb_unref(update->fb);
client_ref(client);
@ -954,7 +971,6 @@ void on_client_update_fb_done(void* work)
stream_send(client->net_stream, payload, on_write_frame_done,
client);
DTRACE_PROBE1(neatvnc, send_fb_done, client);
client->needs_whole_frame = false;
} else {
client->is_updating = false;
vec_destroy(frame);
@ -962,7 +978,6 @@ void on_client_update_fb_done(void* work)
}
client->n_pending_requests--;
process_fb_update_requests(client);
DTRACE_PROBE1(neatvnc, update_fb_done, client);
@ -973,7 +988,7 @@ void on_client_update_fb_done(void* work)
int schedule_client_update_fb(struct nvnc_client* client)
{
struct nvnc_fb* fb = client->server->frame;
struct nvnc_fb* fb = client->server->buffer;
assert(fb);
DTRACE_PROBE1(neatvnc, update_fb_start, client);
@ -1028,31 +1043,24 @@ pixfmt_failure:
}
EXPORT
int nvnc_feed_frame(struct nvnc* self, struct nvnc_fb* fb,
const struct pixman_region16* damage)
void nvnc_damage_region(struct nvnc* self, const struct pixman_region16* damage)
{
struct nvnc_client* client;
if (self->frame)
nvnc_fb_unref(self->frame);
self->frame = fb;
nvnc_fb_ref(self->frame);
struct nvnc_client* tmp;
LIST_FOREACH_SAFE (client, &self->clients, link, tmp) {
if (client->net_stream->state == STREAM_STATE_CLOSED)
continue;
LIST_FOREACH(client, &self->clients, link)
if (client->net_stream->state != STREAM_STATE_CLOSED)
pixman_region_union(&client->damage, &client->damage,
(struct pixman_region16*)damage);
pixman_region_intersect_rect(&client->damage, &client->damage,
0, 0, fb->width, fb->height);
}
process_fb_update_requests(client);
}
return 0;
EXPORT
void nvnc_damage_whole(struct nvnc* self)
{
struct pixman_region16 damage;
pixman_region_init_rect(&damage, 0, 0, self->display.width,
self->display.height);
nvnc_damage_region(self, &damage);
pixman_region_fini(&damage);
}
EXPORT
@ -1093,6 +1101,12 @@ void nvnc_set_new_client_fn(struct nvnc* self, nvnc_client_fn fn)
self->new_client_fn = fn;
}
EXPORT
void nvnc_set_render_fn(struct nvnc* self, nvnc_render_fn fn)
{
self->render_fn = fn;
}
EXPORT
void nvnc_set_client_cleanup_fn(struct nvnc_client* self, nvnc_client_fn fn)
{
@ -1108,6 +1122,16 @@ void nvnc_set_dimensions(struct nvnc* self, uint16_t width, uint16_t height,
self->display.pixfmt = fourcc_format;
}
EXPORT
void nvnc_set_buffer(struct nvnc* self, struct nvnc_fb* fb)
{
if (self->buffer)
nvnc_fb_unref(self->buffer);
self->buffer = fb;
nvnc_fb_ref(fb);
}
EXPORT
struct nvnc* nvnc_get_server(const struct nvnc_client* client)
{