From cab95c5eedf170a527d727ee0ac046a1feac2981 Mon Sep 17 00:00:00 2001 From: Quantum Date: Fri, 6 Aug 2021 23:51:07 -0400 Subject: [PATCH] [common] rects: refactor rect buffer copy code to common module This also fixes a bug of accidentally multiplying the stride by 4 when the stride is already in bytes and not pixels. --- common/CMakeLists.txt | 1 + common/include/common/rects.h | 45 +++++ common/src/rects.c | 174 ++++++++++++++++++ host/platform/Windows/capture/DXGI/src/dxgi.c | 142 +------------- .../Windows/capture/NVFBC/src/nvfbc.c | 12 +- 5 files changed, 224 insertions(+), 150 deletions(-) create mode 100644 common/include/common/rects.h create mode 100644 common/src/rects.c diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 08ca65fb..c33c3a9a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -29,6 +29,7 @@ set(COMMON_SOURCES src/framebuffer.c src/KVMFR.c src/countedbuffer.c + src/rects.c src/runningavg.c src/ringbuffer.c ) diff --git a/common/include/common/rects.h b/common/include/common/rects.h new file mode 100644 index 00000000..ca79d503 --- /dev/null +++ b/common/include/common/rects.h @@ -0,0 +1,45 @@ +/** + * Looking Glass + * Copyright © 2017-2021 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LG_COMMON_RECTS_H_ +#define _LG_COMMON_RECTS_H_ + +#include +#include + +#include "common/framebuffer.h" +#include "common/types.h" + +inline static void rectCopyUnaligned(uint8_t * dest, const uint8_t * src, + int ystart, int yend, int dx, int dstStride, int srcStride, int width) +{ + for (int i = ystart; i < yend; ++i) + { + unsigned int srcOffset = i * srcStride + dx; + unsigned int dstOffset = i * dstStride + dx; + memcpy(dest + dstOffset, src + srcOffset, width); + } +} + +void rectsBufferToFramebuffer(FrameDamageRect * rects, int count, + FrameBuffer * frame, int dstStride, int height, + const uint8_t * src, int srcStride); + +#endif diff --git a/common/src/rects.c b/common/src/rects.c new file mode 100644 index 00000000..e92b03a7 --- /dev/null +++ b/common/src/rects.c @@ -0,0 +1,174 @@ +/** + * Looking Glass + * Copyright © 2017-2021 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common/rects.h" + +#include + +struct Corner +{ + int x; + int y; + int delta; +}; + +struct Edge +{ + int x; + int delta; +}; + +static int cornerCompare(const void * a_, const void * b_) +{ + const struct Corner * a = a_; + const struct Corner * b = b_; + + if (a->y < b->y) return -1; + if (a->y > b->y) return +1; + if (a->x < b->x) return -1; + if (a->x > b->x) return +1; + return 0; +} + +inline static void rectsBufferCopy(FrameDamageRect * rects, int count, + uint8_t * dst, int dstStride, int height, + const uint8_t * src, int srcStride, void * opaque, + void (*rowCopyFinish)(int y, void * opaque)) +{ + const int cornerCount = 4 * count; + struct Corner corners[cornerCount]; + + for (int i = 0; i < count; ++i) + { + FrameDamageRect * rect = rects + i; + corners[4 * i + 0] = (struct Corner) { + .x = rect->x, .y = rect->y, .delta = 1 + }; + corners[4 * i + 1] = (struct Corner) { + .x = rect->x + rect->width, .y = rect->y, .delta = -1 + }; + corners[4 * i + 2] = (struct Corner) { + .x = rect->x, .y = rect->y + rect->height, .delta = -1 + }; + corners[4 * i + 3] = (struct Corner) { + .x = rect->x + rect->width, .y = rect->y + rect->height, .delta = 1 + }; + } + qsort(corners, cornerCount, sizeof(struct Corner), cornerCompare); + + struct Edge active_[2][cornerCount]; + struct Edge change[cornerCount]; + int prev_y = 0; + int activeRow = 0; + int actives = 0; + + for (int rs = 0;;) + { + int y = corners[rs].y; + int re = rs; + while (re < cornerCount && corners[re].y == y) + ++re; + + if (y > height) + y = height; + + int changes = 0; + for (int i = rs; i < re; ) + { + int x = corners[i].x; + int delta = 0; + while (i < re && corners[i].x == x) + delta += corners[i++].delta; + change[changes++] = (struct Edge) { .x = x, .delta = delta }; + } + + struct Edge * active = active_[activeRow]; + int x1 = 0; + int in_rect = 0; + for (int i = 0; i < actives; ++i) + { + if (!in_rect) + x1 = active[i].x; + in_rect += active[i].delta; + if (!in_rect) + rectCopyUnaligned(dst, src, prev_y, y, x1 * 4, dstStride, srcStride, + (active[i].x - x1) * 4); + } + + if (re >= cornerCount || y == height) + break; + + if (rowCopyFinish) + rowCopyFinish(y, opaque); + + struct Edge * new = active_[activeRow ^ 1]; + int ai = 0; + int ci = 0; + int ni = 0; + + while (ai < actives && ci < changes) + { + if (active[ai].x < change[ci].x) + new[ni++] = active[ai++]; + else if (active[ai].x > change[ci].x) + new[ni++] = change[ci++]; + else + { + active[ai].delta += change[ci++].delta; + if (active[ai].delta != 0) + new[ni++] = active[ai]; + ++ai; + } + } + + // only one of (actives - ai) and (changes - ci) will be non-zero. + memcpy(new + ni, active + ai, (actives - ai) * sizeof(struct Edge)); + memcpy(new + ni, change + ci, (changes - ci) * sizeof(struct Edge)); + ni += actives - ai; + ni += changes - ci; + + actives = ni; + prev_y = y; + rs = re; + activeRow ^= 1; + } +} + +struct ToFramebufferData +{ + FrameBuffer * frame; + int stride; +}; + +static void fbRowFinish(int y, void * opaque) +{ + struct ToFramebufferData * data = opaque; + framebuffer_set_write_ptr(data->frame, y * data->stride); +} + +void rectsBufferToFramebuffer(FrameDamageRect * rects, int count, + FrameBuffer * frame, int dstStride, int height, + const uint8_t * src, int srcStride) +{ + struct ToFramebufferData data = { .frame = frame, .stride = dstStride }; + rectsBufferCopy(rects, count, framebuffer_get_data(frame), dstStride, height, + src, srcStride, &data, fbRowFinish); + framebuffer_set_write_ptr(frame, height * dstStride); +} diff --git a/host/platform/Windows/capture/DXGI/src/dxgi.c b/host/platform/Windows/capture/DXGI/src/dxgi.c index 485521c6..72a24163 100644 --- a/host/platform/Windows/capture/DXGI/src/dxgi.c +++ b/host/platform/Windows/capture/DXGI/src/dxgi.c @@ -26,6 +26,7 @@ #include "common/option.h" #include "common/locking.h" #include "common/event.h" +#include "common/rects.h" #include "common/runningavg.h" #include "common/KVMFR.h" @@ -1027,144 +1028,6 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame, const size_t maxFrameS return CAPTURE_RESULT_OK; } -struct Corner -{ - int x; - int y; - int delta; -}; - -struct Edge -{ - int x; - int delta; -}; - -static int cornerCompare(const void * a_, const void * b_) -{ - const struct Corner * a = a_; - const struct Corner * b = b_; - - if (a->y < b->y) return -1; - if (a->y > b->y) return +1; - if (a->x < b->x) return -1; - if (a->x > b->x) return +1; - return 0; -} - -inline static void rectCopyUnaligned(uint8_t * dest, const uint8_t * src, int ystart, - int yend, int dx, int stride, int width) -{ - for (int i = ystart; i < yend; ++i) - { - unsigned int offset = i * stride + dx; - memcpy(dest + offset, src + offset, width); - } -} - -static void copyRects(struct FrameDamage * damage, FrameBuffer * frame, int height, - const uint8_t * src, int stride) -{ - uint8_t * frameData = framebuffer_get_data(frame); - struct Corner corners[4 * KVMFR_MAX_DAMAGE_RECTS]; - const int cornerCount = 4 * damage->count; - - for (int i = 0; i < damage->count; ++i) - { - FrameDamageRect * rect = damage->rects + i; - corners[4 * i + 0] = (struct Corner) { - .x = rect->x, .y = rect->y, .delta = 1 - }; - corners[4 * i + 1] = (struct Corner) { - .x = rect->x + rect->width, .y = rect->y, .delta = -1 - }; - corners[4 * i + 2] = (struct Corner) { - .x = rect->x, .y = rect->y + rect->height, .delta = -1 - }; - corners[4 * i + 3] = (struct Corner) { - .x = rect->x + rect->width, .y = rect->y + rect->height, .delta = 1 - }; - } - qsort(corners, cornerCount, sizeof(struct Corner), cornerCompare); - - struct Edge active_[2][4 * KVMFR_MAX_DAMAGE_RECTS]; - struct Edge change[4 * KVMFR_MAX_DAMAGE_RECTS]; - int prev_y = 0; - int activeRow = 0; - int actives = 0; - - for (int rs = 0;;) - { - int y = corners[rs].y; - int re = rs; - while (re < cornerCount && corners[re].y == y) - ++re; - - if (y > height) - y = height; - - int changes = 0; - for (int i = rs; i < re; ) - { - int x = corners[i].x; - int delta = 0; - while (i < re && corners[i].x == x) - delta += corners[i++].delta; - change[changes++] = (struct Edge) { .x = x, .delta = delta }; - } - - struct Edge * active = active_[activeRow]; - int x1 = 0; - int in_rect = 0; - for (int i = 0; i < actives; ++i) - { - if (!in_rect) - x1 = active[i].x; - in_rect += active[i].delta; - if (!in_rect) - rectCopyUnaligned(frameData, src, prev_y, y, x1 * 4, stride, (active[i].x - x1) * 4); - } - - if (re >= cornerCount || y == height) - break; - - framebuffer_set_write_ptr(frame, y * stride * 4); - - struct Edge * new = active_[activeRow ^ 1]; - int ai = 0; - int ci = 0; - int ni = 0; - - while (ai < actives && ci < changes) - { - if (active[ai].x < change[ci].x) - new[ni++] = active[ai++]; - else if (active[ai].x > change[ci].x) - new[ni++] = change[ci++]; - else - { - active[ai].delta += change[ci++].delta; - if (active[ai].delta != 0) - new[ni++] = active[ai]; - ++ai; - } - } - - // only one of (actives - ai) and (changes - ci) will be non-zero. - memcpy(new + ni, active + ai, (actives - ai) * sizeof(struct Edge)); - memcpy(new + ni, change + ci, (changes - ci) * sizeof(struct Edge)); - ni += actives - ai; - ni += changes - ci; - - actives = ni; - prev_y = y; - rs = re; - activeRow ^= 1; - } - - framebuffer_set_write_ptr(frame, height * stride * 4); -} - static CaptureResult dxgi_getFrame(FrameBuffer * frame, const unsigned int height, int frameIndex) { @@ -1184,7 +1047,8 @@ static CaptureResult dxgi_getFrame(FrameBuffer * frame, memcpy(damage->rects + damage->count, tex->damageRects, tex->damageRectsCount * sizeof(FrameDamageRect)); damage->count += tex->damageRectsCount; - copyRects(damage, frame, height, tex->map.pData, this->pitch); + rectsBufferToFramebuffer(damage->rects, damage->count, frame, this->pitch, + height, tex->map.pData, this->pitch); } for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i) diff --git a/host/platform/Windows/capture/NVFBC/src/nvfbc.c b/host/platform/Windows/capture/NVFBC/src/nvfbc.c index 63e0b58c..09e637f9 100644 --- a/host/platform/Windows/capture/NVFBC/src/nvfbc.c +++ b/host/platform/Windows/capture/NVFBC/src/nvfbc.c @@ -26,6 +26,7 @@ #include "common/option.h" #include "common/framebuffer.h" #include "common/event.h" +#include "common/rects.h" #include "common/thread.h" #include "common/KVMFR.h" #include @@ -482,17 +483,6 @@ static CaptureResult nvfbc_waitFrame(CaptureFrame * frame, return CAPTURE_RESULT_OK; } -inline static void rectCopyUnaligned(uint8_t * dest, uint8_t * src, int ystart, - int yend, int dx, int dstStride, int srcStride, int width) -{ - for (int i = ystart; i < yend; ++i) - { - unsigned int srcOffset = i * srcStride + dx; - unsigned int dstOffset = i * dstStride + dx; - memcpy(dest + dstOffset, src + srcOffset, width); - } -} - static CaptureResult nvfbc_getFrame(FrameBuffer * frame, const unsigned int height, int frameIndex) {