diff --git a/examples/draw.c b/examples/draw.c index dfbf56e..5237f0f 100644 --- a/examples/draw.c +++ b/examples/draw.c @@ -80,7 +80,8 @@ static void damage_all_buffers(struct draw* draw, pixman_region_union(&item->damage, &item->damage, region); } -static void update_vnc_buffer(struct draw* draw) +static void update_vnc_buffer(struct draw* draw, + struct pixman_region16* frame_damage) { struct nvnc_fb *fb = nvnc_fb_pool_acquire(draw->fb_pool); assert(fb); @@ -116,7 +117,7 @@ static void update_vnc_buffer(struct draw* draw) /* The buffer is now up to date, so the damage region can be cleared. */ pixman_region_clear(&fb_side_data->damage); - nvnc_display_set_buffer(draw->display, fb); + nvnc_display_feed_buffer(draw->display, fb, frame_damage); nvnc_fb_unref(fb); } @@ -158,13 +159,7 @@ static void draw_dot(struct draw *draw, struct coord coord, int radius, */ damage_all_buffers(draw, ®ion); - update_vnc_buffer(draw); - - /* This sends the frame damage to nvnc so it knows what needs to be sent - * to the clients. Sending the buffer damage of the current buffer would - * be excessive. - */ - nvnc_display_damage_region(draw->display, ®ion); + update_vnc_buffer(draw, ®ion); pixman_region_fini(®ion); } @@ -233,8 +228,10 @@ int main(int argc, char* argv[]) aml_start(aml_get_default(), sig); aml_unref(sig); - update_vnc_buffer(&draw); - nvnc_display_damage_whole(draw.display); + struct pixman_region16 damage; + pixman_region_init_rect(&damage, 0, 0, draw.width, draw.height); + update_vnc_buffer(&draw, &damage); + pixman_region_fini(&damage); aml_run(aml); diff --git a/examples/png-server.c b/examples/png-server.c index 4dbe9bd..b8cc015 100644 --- a/examples/png-server.c +++ b/examples/png-server.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Andri Yngvason + * Copyright (c) 2019 - 2021 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 @@ -53,12 +53,14 @@ int main(int argc, char* argv[]) struct nvnc_display* display = nvnc_display_new(0, 0); assert(display); - nvnc_display_set_buffer(display, fb); - nvnc_add_display(server, display); nvnc_set_name(server, file); - nvnc_display_damage_whole(display); + struct pixman_region16 damage; + pixman_region_init_rect(&damage, 0, 0, nvnc_fb_get_width(fb), + nvnc_fb_get_height(fb)); + nvnc_display_feed_buffer(display, fb, &damage); + pixman_region_fini(&damage); struct aml_signal* sig = aml_signal_new(SIGINT, on_sigint, NULL, NULL); aml_start(aml_get_default(), sig); diff --git a/include/display.h b/include/display.h index 74527f7..a3d289d 100644 --- a/include/display.h +++ b/include/display.h @@ -17,6 +17,7 @@ #pragma once #include "neatvnc.h" +#include "resampler.h" #include #include @@ -29,4 +30,5 @@ struct nvnc_display { struct nvnc* server; uint16_t x_pos, y_pos; struct nvnc_fb* buffer; + struct resampler resampler; }; diff --git a/include/neatvnc.h b/include/neatvnc.h index 89d9d05..8df91c1 100644 --- a/include/neatvnc.h +++ b/include/neatvnc.h @@ -136,10 +136,7 @@ void nvnc_display_unref(struct nvnc_display*); struct nvnc* nvnc_display_get_server(const struct nvnc_display*); -void nvnc_display_set_buffer(struct nvnc_display*, struct nvnc_fb*); - -void nvnc_display_damage_region(struct nvnc_display*, - const struct pixman_region16*); -void nvnc_display_damage_whole(struct nvnc_display*); +void nvnc_display_feed_buffer(struct nvnc_display*, struct nvnc_fb*, + struct pixman_region16* damage); void nvnc_send_cut_text(struct nvnc*, const char* text, uint32_t len); diff --git a/include/resampler.h b/include/resampler.h new file mode 100644 index 0000000..637edd1 --- /dev/null +++ b/include/resampler.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +struct nvnc_fb; +struct nvnc_fb_pool; +struct pixman_region16; + +struct resampler { + struct nvnc_fb_pool *pool; + void (*on_done)(struct resampler*, struct nvnc_fb*, + struct pixman_region16* damage); +}; + +int resampler_init(struct resampler*); +void resampler_destroy(struct resampler*); + +int resampler_feed(struct resampler*, struct nvnc_fb* fb, + struct pixman_region16* damage); diff --git a/meson.build b/meson.build index 7dd8d9a..c450abb 100644 --- a/meson.build +++ b/meson.build @@ -73,6 +73,8 @@ sources = [ 'src/tight.c', 'src/enc-util.c', 'src/qnum-to-evdev.c', + 'src/resampler.c', + 'src/transform-util.c', ] dependencies = [ diff --git a/src/display.c b/src/display.c index f862c53..abb27fb 100644 --- a/src/display.c +++ b/src/display.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Andri Yngvason + * Copyright (c) 2020 - 2021 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 @@ -18,12 +18,33 @@ #include "neatvnc.h" #include "common.h" #include "fb.h" +#include "type-macros.h" #include #include #define EXPORT __attribute__((visibility("default"))) +static void nvnc_display__on_resampler_done(struct resampler* resampler, + struct nvnc_fb* fb, struct pixman_region16* damage) +{ + struct nvnc_display* self = container_of(resampler, struct nvnc_display, + resampler); + + if (self->buffer) { + nvnc_fb_release(self->buffer); + nvnc_fb_unref(self->buffer); + } + + self->buffer = fb; + nvnc_fb_ref(fb); + nvnc_fb_hold(fb); + + // TODO: Shift according to display position + assert(self->server); + nvnc__damage_region(self->server, damage); +} + EXPORT struct nvnc_display* nvnc_display_new(uint16_t x_pos, uint16_t y_pos) { @@ -31,9 +52,15 @@ struct nvnc_display* nvnc_display_new(uint16_t x_pos, uint16_t y_pos) if (!self) return NULL; + if (resampler_init(&self->resampler) < 0) { + free(self); + return NULL; + } + self->ref = 1; self->x_pos = x_pos; self->y_pos = y_pos; + self->resampler.on_done = nvnc_display__on_resampler_done; return self; } @@ -44,6 +71,7 @@ static void nvnc__display_free(struct nvnc_display* self) nvnc_fb_release(self->buffer); nvnc_fb_unref(self->buffer); } + resampler_destroy(&self->resampler); free(self); } @@ -67,37 +95,8 @@ struct nvnc* nvnc_display_get_server(const struct nvnc_display* self) } EXPORT -void nvnc_display_set_buffer(struct nvnc_display* self, struct nvnc_fb* fb) +void nvnc_display_feed_buffer(struct nvnc_display* self, struct nvnc_fb* fb, + struct pixman_region16* damage) { - if (self->buffer) { - nvnc_fb_release(self->buffer); - nvnc_fb_unref(self->buffer); - } - - self->buffer = fb; - nvnc_fb_ref(fb); - nvnc_fb_hold(fb); -} - -EXPORT -void nvnc_display_damage_region(struct nvnc_display* self, - const struct pixman_region16* region) -{ - // TODO: Shift according to display position - assert(self->server); - nvnc__damage_region(self->server, region); -} - -EXPORT -void nvnc_display_damage_whole(struct nvnc_display* self) -{ - assert(self->server); - - 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); - nvnc_display_damage_region(self, &damage); - pixman_region_fini(&damage); + resampler_feed(&self->resampler, fb, damage); } diff --git a/src/resampler.c b/src/resampler.c new file mode 100644 index 0000000..f664aee --- /dev/null +++ b/src/resampler.c @@ -0,0 +1,140 @@ +#include "resampler.h" +#include "neatvnc.h" +#include "fb.h" +#include "transform-util.h" +#include "pixels.h" + +#include +#include +#include +#include +#include + +struct resampler_work { + struct pixman_region16 damage; + struct nvnc_fb* src; + struct nvnc_fb* dst; + struct resampler* resampler; +}; + +static void resampler_work_free(void* userdata) +{ + struct resampler_work* work = userdata; + + nvnc_fb_release(work->src); + nvnc_fb_unref(work->src); + + nvnc_fb_unref(work->dst); + + pixman_region_fini(&work->damage); + + free(work); +} + +int resampler_init(struct resampler* self) +{ + self->pool = nvnc_fb_pool_new(0, 0, DRM_FORMAT_INVALID, 0); + return self->pool ? 0 : -1; +} + +void resampler_destroy(struct resampler* self) +{ + nvnc_fb_pool_unref(self->pool); +} + +static void do_work(void* handle) +{ + struct aml_work* work = handle; + struct resampler_work* ctx = aml_get_userdata(work); + + struct nvnc_fb* src = ctx->src; + struct nvnc_fb* dst = ctx->dst; + + assert(dst->transform == NVNC_TRANSFORM_NORMAL); + + bool ok __attribute__((unused)); + + pixman_format_code_t dst_fmt = 0; + ok = fourcc_to_pixman_fmt(&dst_fmt, dst->fourcc_format); + assert(ok); + + pixman_image_t* dstimg = pixman_image_create_bits_no_clear( + dst_fmt, dst->width, dst->height, dst->addr, + nvnc_fb_get_pixel_size(dst) * dst->stride); + + pixman_format_code_t src_fmt = 0; + ok = fourcc_to_pixman_fmt(&src_fmt, src->fourcc_format); + assert(ok); + + pixman_image_t* srcimg = pixman_image_create_bits_no_clear( + src_fmt, src->width, src->height, src->addr, + nvnc_fb_get_pixel_size(src) * src->stride); + + pixman_transform_t pxform; + nvnc_transform_to_pixman_transform(&pxform, src->transform, + src->width, src->height); + + pixman_image_set_transform(srcimg, &pxform); + + pixman_image_set_clip_region(dstimg, &ctx->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); +} + +static void on_work_done(void* handle) +{ + struct aml_work* work = handle; + struct resampler_work* ctx = aml_get_userdata(work); + + ctx->resampler->on_done(ctx->resampler, ctx->dst, &ctx->damage); +} + +int resampler_feed(struct resampler* self, struct nvnc_fb* fb, + struct pixman_region16* damage) +{ + if (nvnc_fb_get_transform(fb) == NVNC_TRANSFORM_NORMAL) { + self->on_done(self, fb, damage); + return 0; + } + + nvnc_fb_pool_resize(self->pool, fb->width, fb->height, + fb->fourcc_format, fb->stride); + + struct aml* aml = aml_get_default(); + assert(aml); + + struct resampler_work* ctx = calloc(1, sizeof(*ctx)); + if (!ctx) + return -1; + + pixman_region_init(&ctx->damage); + pixman_region_copy(&ctx->damage, damage); + + ctx->dst = nvnc_fb_pool_acquire(self->pool); + + ctx->src = fb; + nvnc_fb_ref(fb); + nvnc_fb_hold(fb); + + ctx->resampler = self; + + struct aml_work* work = aml_work_new(do_work, on_work_done, ctx, + resampler_work_free); + if (!work) { + resampler_work_free(ctx); + return -1; + } + + nvnc_fb_map(fb); + + int rc = aml_start(aml, work); + aml_unref(work); + return rc; +}