From 716dd47c3f803a0d12046caa998f2eabe099082d Mon Sep 17 00:00:00 2001 From: askmeaboutloom Date: Tue, 15 Aug 2023 01:33:30 +0200 Subject: [PATCH] WIP --- src/drawdance/libcommon/dpcommon/common.h | 2 + .../libengine/dpengine/paint_engine.c | 10 +- src/drawdance/libengine/dpengine/renderer.c | 370 ++++++++++++++++++ src/drawdance/libengine/dpengine/renderer.h | 23 ++ src/drawdance/libengine/dpengine/save.c | 32 +- src/drawdance/libengine/dpengine/tile.c | 18 + src/drawdance/libengine/dpengine/tile.h | 4 + src/drawdance/libengine/dpengine/view_mode.c | 13 +- src/drawdance/libengine/dpengine/view_mode.h | 12 +- src/libclient/canvas/paintengine.cpp | 2 +- src/libclient/canvas/paintengine.h | 2 +- src/libclient/drawdance/viewmode.cpp | 12 +- src/libclient/drawdance/viewmode.h | 8 +- 13 files changed, 459 insertions(+), 49 deletions(-) create mode 100644 src/drawdance/libengine/dpengine/renderer.c create mode 100644 src/drawdance/libengine/dpengine/renderer.h diff --git a/src/drawdance/libcommon/dpcommon/common.h b/src/drawdance/libcommon/dpcommon/common.h index a7c957b36f..0e8f9e8bf7 100644 --- a/src/drawdance/libcommon/dpcommon/common.h +++ b/src/drawdance/libcommon/dpcommon/common.h @@ -268,9 +268,11 @@ DP_INLINE size_t DP_flex_size(size_t type_size, size_t flex_offset, * member. Takes potential trailing padding being used as part of the flexible * array member into account. */ +// NOLINTBEGIN(bugprone-sizeof-expression) #define DP_FLEX_SIZEOF(TYPE, FIELD, COUNT) \ DP_flex_size(sizeof(TYPE), offsetof(TYPE, FIELD), \ sizeof(((TYPE *)NULL)->FIELD[0]), COUNT) +// NOLINTEND(bugprone-sizeof-expression) void *DP_malloc(size_t size) DP_MALLOC_ATTR; diff --git a/src/drawdance/libengine/dpengine/paint_engine.c b/src/drawdance/libengine/dpengine/paint_engine.c index f017a844c4..e28c21bcd2 100644 --- a/src/drawdance/libengine/dpengine/paint_engine.c +++ b/src/drawdance/libengine/dpengine/paint_engine.c @@ -140,7 +140,7 @@ typedef struct DP_PaintEngineDabsPreview { typedef struct DP_PaintEngineRenderBuffer { DP_ALIGNAS_SIMD DP_Pixel8 pixels[DP_TILE_LENGTH]; - DP_ViewModeBuffer *vmb; + DP_ViewModeBuffer vmb; } DP_PaintEngineRenderBuffer; typedef struct DP_PaintEngineCursorChange { @@ -632,6 +632,7 @@ static void run_paint_engine(void *user) DP_PaintEngine *pe = user; DP_DrawContext *dc = pe->paint_dc; DP_Semaphore *sem = pe->queue_sem; + // NOLINTNEXTLINE(bugprone-sizeof-expression) DP_Message **msgs = DP_malloc(sizeof(*msgs) * MAX_MULTIDAB_MESSAGES); while (true) { DP_SEMAPHORE_MUST_WAIT(sem); @@ -677,7 +678,7 @@ static void render_job(void *user, int thread_index) DP_PaintEngine *pe = render_params->pe; int tile_index = y * render_params->xtiles + x; DP_TransientTile *tt = - flatten_tile(pe, pe->render.buffers[thread_index].vmb, + flatten_tile(pe, &pe->render.buffers[thread_index].vmb, render_params->needs_checkers, tile_index); DP_Pixel8 *pixel_buffer = pe->render.buffers[thread_index].pixels; @@ -796,7 +797,7 @@ DP_PaintEngine *DP_paint_engine_new_inc( pe->render.buffers = DP_malloc_simd(sizeof(DP_PaintEngineRenderBuffer) * DP_int_to_size(render_thread_count)); for (int i = 0; i < render_thread_count; ++i) { - pe->render.buffers[i].vmb = DP_view_mode_buffer_new(); + DP_view_mode_buffer_init(&pe->render.buffers[i].vmb); } return pe; } @@ -809,7 +810,7 @@ void DP_paint_engine_free_join(DP_PaintEngine *pe) DP_semaphore_free(pe->render.tiles_done_sem); int render_thread_count = DP_paint_engine_render_thread_count(pe); for (int i = 0; i < render_thread_count; ++i) { - DP_view_mode_buffer_free(pe->render.buffers[i].vmb); + DP_view_mode_buffer_dispose(&pe->render.buffers[i].vmb); } DP_free_simd(pe->render.buffers); DP_worker_free_join(pe->render.worker); @@ -838,6 +839,7 @@ void DP_paint_engine_free_join(DP_PaintEngine *pe) DP_Message **msgs = DP_msg_internal_dump_command_messages(mi, &count); decref_messages(count, msgs); + break; } default: break; diff --git a/src/drawdance/libengine/dpengine/renderer.c b/src/drawdance/libengine/dpengine/renderer.c new file mode 100644 index 0000000000..b94e107b60 --- /dev/null +++ b/src/drawdance/libengine/dpengine/renderer.c @@ -0,0 +1,370 @@ +#include "renderer.h" +#include "canvas_diff.h" +#include "canvas_state.h" +#include "layer_content.h" +#include "pixels.h" +#include "tile.h" +#include "view_mode.h" +#include +#include +#include +#include +#include + +#define TILE_QUEUE_INITIAL_CAPACITY 1024 + +typedef struct DP_RenderContext { + DP_ALIGNAS_SIMD DP_Pixel8 pixels[DP_TILE_LENGTH]; + DP_TransientTile *tt; + DP_ViewModeBuffer vmb; +} DP_RenderContext; + +typedef enum DP_RenderJobType { + DP_RENDER_JOB_TILE, + DP_RENDER_JOB_RESIZE, + DP_RENDER_JOB_WAIT, + DP_RENDER_JOB_INVALID, + DP_RENDER_JOB_QUIT, +} DP_RenderJobType; + +typedef struct DP_RendererTileCoords { + int tile_x, tile_y; +} DP_RendererTileCoords; + +typedef struct DP_RendererTileJob { + int tile_x, tile_y; + DP_CanvasState *cs; +} DP_RendererTileJob; + +typedef struct DP_RendererResizeJob { + int width, height; + int prev_width, prev_height; + int offset_x, offset_y; +} DP_RendererResizeJob; + +typedef struct DP_RenderJob { + DP_RenderJobType type; + union { + DP_RendererTileJob tile; + DP_RendererResizeJob resize; + }; +} DP_RenderJob; + +struct DP_Renderer { + struct { + DP_RendererTileFn tile; + DP_RendererResizeFn resize; + void *user; + } fn; + DP_RenderContext *contexts; + DP_Queue resize_queue; + DP_Queue tile_queue; + DP_Tile *checker; + DP_CanvasState *cs; + DP_Mutex *queue_mutex; + DP_Semaphore *queue_sem; + DP_Semaphore *wait_ready_sem; + DP_Semaphore *wait_done_sem; + int thread_count; + DP_Thread *threads[]; +}; + + +static void handle_tile_job(DP_Renderer *renderer, DP_RenderContext *rc, + DP_RendererTileJob *job) +{ + DP_CanvasState *cs = job->cs; + int xtiles = DP_tile_count_round(DP_canvas_state_width(cs)); + int tile_x = job->tile_x; + int tile_y = job->tile_y; + int tile_index = tile_y * xtiles + tile_x; + + DP_TransientTile *tt = rc->tt; + DP_Tile *background_tile = DP_canvas_state_background_tile_noinc(cs); + if (background_tile) { + DP_transient_tile_copy(tt, background_tile); + } + else { + DP_transient_tile_clear(tt); + } + + DP_ViewModeFilter vmf = // FIXME + DP_view_mode_filter_make(&rc->vmb, DP_VIEW_MODE_NORMAL, cs, 0, 0, NULL); + + DP_canvas_state_flatten_tile_to(cs, tile_index, tt, true, &vmf); + + bool needs_checkers = false; // FIXME + if (needs_checkers) { + DP_transient_tile_merge(tt, renderer->checker, DP_BIT15, + DP_BLEND_MODE_BEHIND); + } + + DP_Pixel8 *pixel_buffer = rc->pixels; + DP_pixels15_to_8_tile(pixel_buffer, DP_transient_tile_pixels(tt)); + renderer->fn.tile(renderer->fn.user, tile_x, tile_y, pixel_buffer); +} + + +static void handle_resize_job(DP_Renderer *renderer, DP_RendererResizeJob *job) +{ + // Wait for all threads to be in a safe state before proceeding. + int wait_thread_count = renderer->thread_count - 1; + DP_SEMAPHORE_MUST_WAIT_N(renderer->wait_ready_sem, wait_thread_count); + + renderer->fn.resize(renderer->fn.user, job->width, job->height, + job->prev_width, job->prev_height, job->offset_x, + job->offset_y); + + // Unlock the other threads. + DP_SEMAPHORE_MUST_POST_N(renderer->wait_done_sem, wait_thread_count); +} + + +static void handle_wait_job(DP_Renderer *renderer) +{ + DP_SEMAPHORE_MUST_POST(renderer->wait_ready_sem); + DP_SEMAPHORE_MUST_WAIT(renderer->wait_done_sem); +} + + +static DP_RenderJob dequeue_job(DP_Renderer *renderer) +{ + DP_Queue *resize_queue = &renderer->resize_queue; + DP_RenderJob *resize_job = DP_queue_peek(resize_queue, sizeof(*resize_job)); + if (resize_job) { + DP_RenderJob job = *resize_job; + DP_queue_shift(resize_queue); + return job; + } + + DP_Queue *tile_queue = &renderer->tile_queue; + DP_RendererTileCoords *coords = DP_queue_peek(tile_queue, sizeof(*coords)); + if (coords) { + DP_RenderJob job; + if (coords->tile_x >= 0) { + job.type = DP_RENDER_JOB_TILE; + job.tile = (DP_RendererTileJob){ + coords->tile_x, + coords->tile_y, + DP_canvas_state_incref(renderer->cs), + }; + } + else { + job.type = DP_RENDER_JOB_INVALID; + } + DP_queue_shift(tile_queue); + return job; + } + + DP_RenderJob job; + job.type = DP_RENDER_JOB_QUIT; + return job; +} + +static void handle_jobs(DP_Renderer *renderer, int thread_index) +{ + DP_Mutex *queue_mutex = renderer->queue_mutex; + DP_Semaphore *queue_sem = renderer->queue_sem; + DP_RenderContext *rc = &renderer->contexts[thread_index]; + while (true) { + DP_SEMAPHORE_MUST_WAIT(queue_sem); + DP_MUTEX_MUST_LOCK(queue_mutex); + DP_RenderJob job = dequeue_job(renderer); + DP_MUTEX_MUST_UNLOCK(queue_mutex); + switch (job.type) { + case DP_RENDER_JOB_TILE: + handle_tile_job(renderer, rc, &job.tile); + break; + case DP_RENDER_JOB_RESIZE: + handle_resize_job(renderer, &job.resize); + break; + case DP_RENDER_JOB_WAIT: + handle_wait_job(renderer); + break; + case DP_RENDER_JOB_INVALID: + break; + case DP_RENDER_JOB_QUIT: + return; + default: + DP_UNREACHABLE(); + } + } +} + +struct DP_RenderWorkerParams { + DP_Renderer *renderer; + int thread_index; +}; + +static void run_worker_thread(void *user) +{ + struct DP_RenderWorkerParams *params = user; + DP_Renderer *renderer = params->renderer; + int thread_index = params->thread_index; + DP_free(params); + handle_jobs(renderer, thread_index); +} + + +DP_Renderer *DP_renderer_new(int thread_count, DP_RendererTileFn tile_fn, + DP_RendererResizeFn resize_fn, void *user) +{ + DP_ASSERT(thread_count > 0); + size_t size_thread_count = DP_int_to_size(thread_count); + DP_Renderer *renderer = + DP_malloc(DP_FLEX_SIZEOF(DP_Renderer, threads, size_thread_count)); + renderer->fn.tile = tile_fn; + renderer->fn.resize = resize_fn; + renderer->fn.user = user; + renderer->thread_count = thread_count; + renderer->queue_mutex = NULL; + renderer->queue_sem = NULL; + renderer->wait_ready_sem = NULL; + renderer->wait_done_sem = NULL; + DP_queue_init(&renderer->resize_queue, size_thread_count * 2, + sizeof(DP_RenderJob)); + DP_queue_init(&renderer->tile_queue, TILE_QUEUE_INITIAL_CAPACITY, + sizeof(DP_RendererTileCoords)); + renderer->checker = DP_tile_new_checker( + 0, (DP_Pixel15){DP_BIT15 / 2, DP_BIT15 / 2, DP_BIT15 / 2, DP_BIT15}, + (DP_Pixel15){DP_BIT15, DP_BIT15, DP_BIT15, DP_BIT15}); + renderer->cs = DP_canvas_state_new(); + renderer->contexts = DP_malloc_simd(sizeof(*renderer->contexts) + * DP_int_to_size(thread_count)); + for (int i = 0; i < thread_count; ++i) { + DP_view_mode_buffer_init(&renderer->contexts[i].vmb); + renderer->threads[i] = NULL; + } + + bool ok = (renderer->queue_mutex = DP_mutex_new()) + && (renderer->queue_sem = DP_semaphore_new(0)) + && (renderer->wait_ready_sem = DP_semaphore_new(0)) + && (renderer->wait_done_sem = DP_semaphore_new(0)); + if (!ok) { + DP_renderer_free(renderer); + return NULL; + } + + for (int i = 0; i < thread_count; ++i) { + struct DP_RenderWorkerParams *params = DP_malloc(sizeof(*params)); + *params = (struct DP_RenderWorkerParams){renderer, i}; + renderer->threads[i] = DP_thread_new(run_worker_thread, params); + if (!renderer->threads[i]) { + DP_free(params); + DP_renderer_free(renderer); + return NULL; + } + } + + return renderer; +} + +void DP_renderer_free(DP_Renderer *renderer) +{ + if (renderer) { + int thread_count = renderer->thread_count; + DP_MUTEX_MUST_LOCK(renderer->queue_mutex); + renderer->resize_queue.used = 0; + renderer->tile_queue.used = 0; + DP_SEMAPHORE_MUST_POST_N(renderer->queue_sem, thread_count); + DP_MUTEX_MUST_UNLOCK(renderer->queue_mutex); + + for (int i = 0; i < thread_count; ++i) { + DP_thread_free_join(renderer->threads[i]); + } + DP_semaphore_free(renderer->wait_done_sem); + DP_semaphore_free(renderer->wait_ready_sem); + DP_semaphore_free(renderer->queue_sem); + DP_mutex_free(renderer->queue_mutex); + DP_canvas_state_decref(renderer->cs); + DP_tile_decref(renderer->checker); + DP_queue_dispose(&renderer->tile_queue); + DP_queue_dispose(&renderer->resize_queue); + for (int i = 0; i < thread_count; ++i) { + DP_view_mode_buffer_dispose(&renderer->contexts[i].vmb); + } + DP_free_simd(renderer->contexts); + DP_free(renderer); + } +} + + +static void invalidate_tile_coords(void *element, DP_UNUSED void *user) +{ + DP_RendererTileCoords *coords = element; + coords->tile_x = -1; +} + +static int push_resize(DP_Renderer *renderer, int width, int height, + int prev_width, int prev_height, int offset_x, + int offset_y) +{ + DP_Queue *resize_queue = &renderer->resize_queue; + DP_Queue *tile_queue = &renderer->tile_queue; + int thread_count = renderer->thread_count; + int wait_thread_count = renderer->thread_count - 1; + + // Resizing must be synchronized between all render threads, since all tile + // positions are invalidated by the operation. So we enqueue a wait job for + // all except one thread, plus the actual resize job for the last one. + for (int i = 0; i < wait_thread_count; ++i) { + DP_RenderJob *wait_job = DP_queue_push(resize_queue, sizeof(*wait_job)); + wait_job->type = DP_RENDER_JOB_WAIT; + } + + DP_RenderJob *resize_job = DP_queue_push(resize_queue, sizeof(*resize_job)); + resize_job->type = DP_RENDER_JOB_RESIZE; + resize_job->resize = (DP_RendererResizeJob){ + width, height, prev_width, prev_height, offset_x, offset_y}; + + // All current tile jobs are invalidated by the resize, so we turn those + // into tombstones to avoid any pointless processing thereof. + DP_queue_each(tile_queue, sizeof(DP_RendererTileCoords), + invalidate_tile_coords, NULL); + + return thread_count; +} + + +struct DP_RendererPushTileParams { + DP_Queue *tile_queue; + int pushed; +}; + +static void push_tile(void *user, int tile_x, int tile_y) +{ + struct DP_RendererPushTileParams *params = user; + // TODO: deduplicate to avoid re-rendering the same tile. + DP_RendererTileCoords *coords = + DP_queue_push(params->tile_queue, sizeof(*coords)); + *coords = (DP_RendererTileCoords){tile_x, tile_y}; + ++params->pushed; +} + +void DP_renderer_apply(DP_Renderer *renderer, DP_CanvasState *cs, + int prev_width, int prev_height, int offset_x, + int offset_y, DP_CanvasDiff *diff) +{ + DP_ASSERT(renderer); + DP_ASSERT(cs); + DP_ASSERT(diff); + + DP_Mutex *queue_mutex = renderer->queue_mutex; + struct DP_RendererPushTileParams params = {&renderer->tile_queue, 0}; + DP_MUTEX_MUST_LOCK(queue_mutex); + + DP_canvas_state_decref(renderer->cs); + renderer->cs = DP_canvas_state_incref(cs); + + int width = DP_canvas_state_width(cs); + int height = DP_canvas_state_height(cs); + if (width != prev_width || height != prev_height) { + params.pushed += push_resize(renderer, width, height, prev_width, + prev_height, offset_x, offset_y); + } + + DP_canvas_diff_each_pos_reset(diff, push_tile, ¶ms); + DP_SEMAPHORE_MUST_POST_N(renderer->queue_sem, params.pushed); + + DP_MUTEX_MUST_UNLOCK(queue_mutex); +} diff --git a/src/drawdance/libengine/dpengine/renderer.h b/src/drawdance/libengine/dpengine/renderer.h new file mode 100644 index 0000000000..59628724de --- /dev/null +++ b/src/drawdance/libengine/dpengine/renderer.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +#include + +typedef struct DP_CanvasDiff DP_CanvasDiff; +typedef struct DP_CanvasState DP_CanvasState; +typedef union DP_Pixel8 DP_Pixel8; + + +typedef struct DP_Renderer DP_Renderer; +typedef void (*DP_RendererTileFn)(void *user, int x, int y, DP_Pixel8 *pixels); +typedef void (*DP_RendererResizeFn)(void *user, int width, int height, + int prev_width, int prev_height, + int offset_x, int offset_y); + +DP_Renderer *DP_renderer_new(int thread_count, DP_RendererTileFn tile_fn, + DP_RendererResizeFn resize_fn, void *user); + +void DP_renderer_free(DP_Renderer *renderer); + +// Increments refcount on the given canvas state, resets the given diff. +void DP_renderer_apply(DP_Renderer *renderer, DP_CanvasState *cs, + int prev_width, int prev_height, int offset_x, + int offset_y, DP_CanvasDiff *diff); diff --git a/src/drawdance/libengine/dpengine/save.c b/src/drawdance/libengine/dpengine/save.c index 31c4623e72..d8e1534e47 100644 --- a/src/drawdance/libengine/dpengine/save.c +++ b/src/drawdance/libengine/dpengine/save.c @@ -793,7 +793,7 @@ static const char *get_path_separator(const char *path) struct DP_SaveFrameContext { DP_CanvasState *cs; - DP_ViewModeBuffer **vmbs; + DP_ViewModeBuffer *vmbs; int frame_count; const char *path; const char *separator; @@ -810,15 +810,6 @@ struct DP_SaveFrameJobParams { int frames[]; }; -static DP_ViewModeBuffer * -get_frame_view_mode_buffer(struct DP_SaveFrameContext *c, int thread_index) -{ - if (!c->vmbs[thread_index]) { - c->vmbs[thread_index] = DP_view_mode_buffer_new(); - } - return c->vmbs[thread_index]; -} - static void set_error_result(struct DP_SaveFrameContext *c, DP_SaveResult result) { @@ -880,7 +871,7 @@ static void save_frame_job(void *element, int thread_index) *(struct DP_SaveFrameJobParams **)element; struct DP_SaveFrameContext *c = params->c; if (DP_atomic_get(&c->result) == DP_SAVE_RESULT_SUCCESS) { - DP_ViewModeBuffer *vmb = get_frame_view_mode_buffer(c, thread_index); + DP_ViewModeBuffer *vmb = &c->vmbs[thread_index]; // Render and save the first frame given. int first_frame = params->frames[0]; char *path = save_frame(c, vmb, first_frame); @@ -929,7 +920,7 @@ save_animation_frames(DP_CanvasState *cs, const char *path, int thread_count = DP_worker_thread_count(worker); struct DP_SaveFrameContext c = { cs, - DP_malloc_zeroed(sizeof(*c.vmbs) * DP_int_to_size(thread_count)), + DP_malloc(sizeof(*c.vmbs) * DP_int_to_size(thread_count)), frame_count, path, get_path_separator(path), @@ -940,6 +931,10 @@ save_animation_frames(DP_CanvasState *cs, const char *path, 0, }; + for (int i = 0; i < thread_count; ++i) { + DP_view_mode_buffer_init(&c.vmbs[i]); + } + int *frames = DP_malloc(sizeof(*frames) * DP_int_to_size(frame_count)); for (int i = 0; i < frame_count; ++i) { frames[i] = i; @@ -978,7 +973,7 @@ save_animation_frames(DP_CanvasState *cs, const char *path, DP_mutex_free(progress_mutex); for (int i = 0; i < thread_count; ++i) { - DP_view_mode_buffer_free(c.vmbs[i]); + DP_view_mode_buffer_dispose(&c.vmbs[i]); } DP_free(c.vmbs); @@ -1054,7 +1049,8 @@ static DP_SaveResult save_animation_gif(DP_CanvasState *cs, const char *path, return DP_SAVE_RESULT_CANCEL; } - DP_ViewModeBuffer *vmb = DP_view_mode_buffer_new(); + DP_ViewModeBuffer vmb; + DP_view_mode_buffer_init(&vmb); double centiseconds_per_frame = get_gif_centiseconds_per_frame(cs); double delay_frac = 0.0; for (int i = 0; i < frame_count; ++i) { @@ -1066,7 +1062,7 @@ static DP_SaveResult save_animation_gif(DP_CanvasState *cs, const char *path, } DP_ViewModeFilter vmf = - DP_view_mode_filter_make_frame(vmb, cs, i, NULL); + DP_view_mode_filter_make_frame(&vmb, cs, i, NULL); DP_Image *img = DP_canvas_state_to_flat_image( cs, DP_FLAT_IMAGE_RENDER_FLAGS, NULL, &vmf); double delay = centiseconds_per_frame * DP_int_to_double(instances); @@ -1079,18 +1075,18 @@ static DP_SaveResult save_animation_gif(DP_CanvasState *cs, const char *path, if (!frame_ok) { jo_gifx_abort(gif); DP_output_free(output); - DP_view_mode_buffer_free(vmb); + DP_view_mode_buffer_dispose(&vmb); return DP_SAVE_RESULT_WRITE_ERROR; } if (!report_gif_progress(progress_fn, user, i + 1, frame_count)) { jo_gifx_abort(gif); DP_output_free(output); - DP_view_mode_buffer_free(vmb); + DP_view_mode_buffer_dispose(&vmb); return DP_SAVE_RESULT_CANCEL; } } - DP_view_mode_buffer_free(vmb); + DP_view_mode_buffer_dispose(&vmb); if (!jo_gifx_end(write_gif, output, gif) || !DP_output_flush(output)) { DP_output_free(output); diff --git a/src/drawdance/libengine/dpengine/tile.c b/src/drawdance/libengine/dpengine/tile.c index ea587c5735..1efcbb708f 100644 --- a/src/drawdance/libengine/dpengine/tile.c +++ b/src/drawdance/libengine/dpengine/tile.c @@ -651,6 +651,24 @@ void DP_transient_tile_pixel_at_put(DP_TransientTile *tt, int blend_mode, int x, blend_mode); } +void DP_transient_tile_clear(DP_TransientTile *tt) +{ + DP_ASSERT(tt); + DP_ASSERT(DP_atomic_get(&tt->refcount) > 0); + DP_ASSERT(tt->transient); + memset(tt->pixels, 0, DP_TILE_BYTES); +} + +void DP_transient_tile_copy(DP_TransientTile *tt, DP_Tile *t) +{ + DP_ASSERT(tt); + DP_ASSERT(DP_atomic_get(&tt->refcount) > 0); + DP_ASSERT(tt->transient); + DP_ASSERT(t); + DP_ASSERT(DP_atomic_get(&t->refcount) > 0); + memcpy(tt->pixels, t->pixels, DP_TILE_BYTES); +} + bool DP_transient_tile_blank(DP_TransientTile *tt) { DP_ASSERT(tt); diff --git a/src/drawdance/libengine/dpengine/tile.h b/src/drawdance/libengine/dpengine/tile.h index 34f76d194f..aebab8e3f1 100644 --- a/src/drawdance/libengine/dpengine/tile.h +++ b/src/drawdance/libengine/dpengine/tile.h @@ -189,6 +189,10 @@ void DP_transient_tile_pixel_at_set(DP_TransientTile *tt, int x, int y, void DP_transient_tile_pixel_at_put(DP_TransientTile *tt, int blend_mode, int x, int y, DP_Pixel15 pixel); +void DP_transient_tile_clear(DP_TransientTile *tt); + +void DP_transient_tile_copy(DP_TransientTile *tt, DP_Tile *t); + bool DP_transient_tile_blank(DP_TransientTile *tt); void DP_transient_tile_merge(DP_TransientTile *DP_RESTRICT tt, diff --git a/src/drawdance/libengine/dpengine/view_mode.c b/src/drawdance/libengine/dpengine/view_mode.c index 6beaedf076..4d44d6f375 100644 --- a/src/drawdance/libengine/dpengine/view_mode.c +++ b/src/drawdance/libengine/dpengine/view_mode.c @@ -49,12 +49,6 @@ typedef struct DP_ViewModeTrack { const DP_OnionSkin *onion_skin; } DP_ViewModeTrack; -struct DP_ViewModeBuffer { - int capacity; - int count; - DP_ViewModeTrack *tracks; -}; - struct DP_OnionSkins { int count_below; int count_above; @@ -62,14 +56,12 @@ struct DP_OnionSkins { }; -DP_ViewModeBuffer *DP_view_mode_buffer_new(void) +void DP_view_mode_buffer_init(DP_ViewModeBuffer *vmb) { - DP_ViewModeBuffer *vmb = DP_malloc(sizeof(*vmb)); *vmb = (DP_ViewModeBuffer){0, 0, NULL}; - return vmb; } -void DP_view_mode_buffer_free(DP_ViewModeBuffer *vmb) +void DP_view_mode_buffer_dispose(DP_ViewModeBuffer *vmb) { if (vmb) { int track_count = vmb->capacity; // Not count, we want to clean em all. @@ -77,7 +69,6 @@ void DP_view_mode_buffer_free(DP_ViewModeBuffer *vmb) DP_vector_dispose(&vmb->tracks[i].hidden_layer_ids); } DP_free(vmb->tracks); - DP_free(vmb); } } diff --git a/src/drawdance/libengine/dpengine/view_mode.h b/src/drawdance/libengine/dpengine/view_mode.h index 92e85ddf55..59bd5ea651 100644 --- a/src/drawdance/libengine/dpengine/view_mode.h +++ b/src/drawdance/libengine/dpengine/view_mode.h @@ -38,7 +38,13 @@ typedef enum DP_ViewMode { DP_VIEW_MODE_FRAME, } DP_ViewMode; -typedef struct DP_ViewModeBuffer DP_ViewModeBuffer; +typedef struct DP_ViewModeTrack DP_ViewModeTrack; + +typedef struct DP_ViewModeBuffer { + int capacity; + int count; + DP_ViewModeTrack *tracks; +} DP_ViewModeBuffer; typedef struct DP_ViewModeFilter { int internal_type; @@ -84,9 +90,9 @@ typedef struct DP_OnionSkins DP_OnionSkins; typedef void (*DP_AddVisibleLayerFn)(void *user, int layer_id, bool visible); -DP_ViewModeBuffer *DP_view_mode_buffer_new(void); +void DP_view_mode_buffer_init(DP_ViewModeBuffer *vmb); -void DP_view_mode_buffer_free(DP_ViewModeBuffer *vmb); +void DP_view_mode_buffer_dispose(DP_ViewModeBuffer *vmb); DP_ViewModeFilter DP_view_mode_filter_make_default(void); diff --git a/src/libclient/canvas/paintengine.cpp b/src/libclient/canvas/paintengine.cpp index f89622ee35..372efb84a4 100644 --- a/src/libclient/canvas/paintengine.cpp +++ b/src/libclient/canvas/paintengine.cpp @@ -636,7 +636,7 @@ QImage PaintEngine::getLayerImage(int id, const QRect &rect) const } QImage PaintEngine::getFrameImage( - const drawdance::ViewModeBuffer &vmb, int index, const QRect &rect) const + drawdance::ViewModeBuffer &vmb, int index, const QRect &rect) const { drawdance::CanvasState cs = viewCanvasState(); QRect area = rect.isNull() ? QRect{0, 0, cs.width(), cs.height()} : rect; diff --git a/src/libclient/canvas/paintengine.h b/src/libclient/canvas/paintengine.h index 31aca47800..f7e11523ac 100644 --- a/src/libclient/canvas/paintengine.h +++ b/src/libclient/canvas/paintengine.h @@ -72,7 +72,7 @@ class PaintEngine final : public QObject { //! Render a frame QImage getFrameImage( - const drawdance::ViewModeBuffer &vmb, int index, + drawdance::ViewModeBuffer &vmb, int index, const QRect &rect = QRect()) const; //! Receive and handle messages, returns how many messages were actually diff --git a/src/libclient/drawdance/viewmode.cpp b/src/libclient/drawdance/viewmode.cpp index 5f425be5b3..d2a009f190 100644 --- a/src/libclient/drawdance/viewmode.cpp +++ b/src/libclient/drawdance/viewmode.cpp @@ -1,26 +1,22 @@ // SPDX-License-Identifier: GPL-3.0-or-later -extern "C" { -#include "dpengine/view_mode.h" -} - #include "libclient/drawdance/viewmode.h" namespace drawdance { ViewModeBuffer::ViewModeBuffer() - : m_data{DP_view_mode_buffer_new()} { + DP_view_mode_buffer_init(&m_data); } ViewModeBuffer::~ViewModeBuffer() { - DP_view_mode_buffer_free(m_data); + DP_view_mode_buffer_dispose(&m_data); } -DP_ViewModeBuffer *ViewModeBuffer::get() const +DP_ViewModeBuffer *ViewModeBuffer::get() { - return m_data; + return &m_data; } } diff --git a/src/libclient/drawdance/viewmode.h b/src/libclient/drawdance/viewmode.h index 2567f73cb2..fb0d0b0350 100644 --- a/src/libclient/drawdance/viewmode.h +++ b/src/libclient/drawdance/viewmode.h @@ -3,7 +3,9 @@ #ifndef DRAWDANCE_VIEWMODE_H #define DRAWDANCE_VIEWMODE_H -struct DP_ViewModeBuffer; +extern "C" { +#include "dpengine/view_mode.h" +} namespace drawdance { @@ -17,10 +19,10 @@ class ViewModeBuffer final { ViewModeBuffer &operator=(const ViewModeBuffer &) = delete; ViewModeBuffer &operator=(ViewModeBuffer &&) = delete; - DP_ViewModeBuffer *get() const; + DP_ViewModeBuffer *get(); private: - DP_ViewModeBuffer *m_data; + DP_ViewModeBuffer m_data; }; }