From fe136fcd295271341f2f180e04f768002471f846 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 21 Jun 2020 16:26:10 +0000 Subject: [PATCH] Create hash based damage checker --- include/damage-refinery.h | 21 ++++++++ include/murmurhash.h | 48 +++++++++++++++++ include/util.h | 4 ++ src/damage-refinery.c | 111 ++++++++++++++++++++++++++++++++++++++ src/murmurhash.c | 91 +++++++++++++++++++++++++++++++ 5 files changed, 275 insertions(+) create mode 100644 include/damage-refinery.h create mode 100644 include/murmurhash.h create mode 100644 include/util.h create mode 100644 src/damage-refinery.c create mode 100644 src/murmurhash.c diff --git a/include/damage-refinery.h b/include/damage-refinery.h new file mode 100644 index 0000000..8c26e56 --- /dev/null +++ b/include/damage-refinery.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +struct pixman_region16; +struct wv_buffer; + +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); diff --git a/include/murmurhash.h b/include/murmurhash.h new file mode 100644 index 0000000..b12ac2d --- /dev/null +++ b/include/murmurhash.h @@ -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 + +#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 diff --git a/include/util.h b/include/util.h new file mode 100644 index 0000000..dadf5b7 --- /dev/null +++ b/include/util.h @@ -0,0 +1,4 @@ +#pragma once + +#define UDIV_UP(a, b) (((a) + (b) - 1) / (b)) +#define ALIGN_UP(n, a) (UDIV_UP(n, a) * a) diff --git a/src/damage-refinery.c b/src/damage-refinery.c new file mode 100644 index 0000000..9dd89c8 --- /dev/null +++ b/src/damage-refinery.c @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include + +#include "damage-refinery.h" +#include "buffer.h" +#include "util.h" +#include "murmurhash.h" + +#define HASH_SEED 0xdeadbeef + +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 size_t damage_copy_tile(struct damage_refinery* self, void* tile_buffer, + uint32_t tx, uint32_t ty, const struct wv_buffer* buffer) +{ + // TODO: Support different pixel sizes + uint32_t* tile = tile_buffer; + uint32_t* pixels = buffer->pixels; + int pixel_stride = buffer->stride / 4; + + size_t i = 0; + int x_start = tx * 32; + int x_stop = MIN(((int)tx + 1) * 32, buffer->width); + int y_start = ty * 32; + int y_stop = MIN(((int)ty + 1) * 32, buffer->height); + + for (int y = y_start; y < y_stop; ++y) + for (int x = x_start; x < x_stop; ++x) + tile[i++] = pixels[x + y * pixel_stride]; + + return i; +} + +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_tile(struct damage_refinery* self, + struct pixman_region16* refined, uint32_t tx, uint32_t ty, + int invert_y) +{ + uint32_t theight = UDIV_UP(self->height, 32); + if (invert_y) + pixman_region_union_rect(refined, refined, tx * 32, + (theight - ty - 1) * 32, 32, 32); + else + pixman_region_union_rect(refined, refined, + tx * 32, ty * 32, 32, 32); +} + +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 tile[32 * 32]; + size_t len = damage_copy_tile(self, tile, tx, ty, buffer); + uint32_t hash = murmurhash((void*)tile, len, HASH_SEED); + 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) + damage_tile(self, refined, tx, ty, buffer->y_inverted); +} + +void damage_refine(struct damage_refinery* self, + struct pixman_region16* refined, + struct pixman_region16* hint, + const struct wv_buffer* buffer) +{ + // TODO: Use hint + + assert(self->width == (uint32_t)buffer->width && + self->height == (uint32_t)buffer->height); + + uint32_t twidth = UDIV_UP(self->width, 32); + uint32_t theight = UDIV_UP(self->height, 32); + + for (uint32_t ty = 0; ty < theight; ++ty) + for (uint32_t tx = 0; tx < twidth; ++tx) + damage_refine_tile(self, refined, tx, ty, buffer); + + pixman_region_intersect_rect(refined, refined, 0, 0, self->width, + self->height); +} diff --git a/src/murmurhash.c b/src/murmurhash.c new file mode 100644 index 0000000..2edbf0e --- /dev/null +++ b/src/murmurhash.c @@ -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 +#include +#include +#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; +}