From 6403bb15cbf4dcba8341df058c20e24738f26740 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net>
Date: Sat, 18 Jan 2025 18:10:08 -0500
Subject: [PATCH] Fix major performance regression in RIP rendering

A high number of RIP commands use setpixel(), and it was updated
to scan the whole vmem for changes on each call.  Now we just extract
the part that is relevant and only update the one cell if needed.

This also adds parameter validation to bitmap_setpixel() which can
prevent some memory corruption and crashes.
---
 src/conio/bitmap_con.c | 89 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 79 insertions(+), 10 deletions(-)

diff --git a/src/conio/bitmap_con.c b/src/conio/bitmap_con.c
index a030f1e7f5..444cdf88d7 100644
--- a/src/conio/bitmap_con.c
+++ b/src/conio/bitmap_con.c
@@ -1745,12 +1745,24 @@ int bitmap_attr2palette(uint8_t attr, uint32_t *fgp, uint32_t *bgp)
 
 int bitmap_setpixel(uint32_t x, uint32_t y, uint32_t colour)
 {
-	update_from_vmem(FALSE);
+	int xchar = x / vstat.charwidth;
+	int ychar = y / vstat.charheight;
+
 	do_rwlock_wrlock(&vstatlock);
-	int off = vmem_cell_offset(vstat.vmem, x / vstat.charwidth, y / vstat.charheight);
+	pthread_mutex_lock(&screenlock);
+	if (screena.rect == NULL || screenb.rect == NULL || x >= screena.screenwidth || y >= screena.screenheight) {
+		pthread_mutex_unlock(&screenlock);
+		do_rwlock_unlock(&vstatlock);
+		return 0;
+	}
+	int off = vmem_cell_offset(vstat.vmem, xchar, ychar);
+	if (!same_cell(&bitmap_drawn[off], &vstat.vmem->vmem[off])) {
+		pthread_mutex_unlock(&screenlock);
+		bitmap_draw_from_vmem(xchar + 1, ychar + 1, xchar + 1, ychar + 1);
+		pthread_mutex_lock(&screenlock);
+	}
 	vstat.vmem->vmem[off].bg |= 0x04000000;
 	bitmap_drawn[off].bg |= 0x04000000;
-	pthread_mutex_lock(&screenlock);
 	if (x < screena.screenwidth && y < screena.screenheight) {
 		if (screena.rect->data[pixel_offset(&screena, x, y)] != colour) {
 			screena.update_pixels = 1;
@@ -1801,7 +1813,6 @@ int bitmap_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_
 			return 0;
 	}
 
-	update_from_vmem(FALSE);
 	do_rwlock_wrlock(&vstatlock);
 	pthread_mutex_lock(&screenlock);
 	if (ex > screena.screenwidth || ey > screena.screenheight) {
@@ -1810,13 +1821,44 @@ int bitmap_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_
 		return 0;
 	}
 
+	int charsx = sx / vstat.charwidth;
+	int charx = charsx;
+	int chary = sy / vstat.charheight;
+	int cpx = sx % vstat.charwidth;
+	int cpy = sy % vstat.charheight;
+	bool xupdated = false;
+	bool yupdated = false;
+	int off;
 	for (y = sy; y <= ey; y++) {
 		pos = pixels->width*(y-sy+y_off)+x_off;
+		if (!yupdated) {
+			off = vmem_cell_offset(vstat.vmem, charx, chary);
+		}
+		charx = charsx;
 		if (mask == NULL) {
 			for (x = sx; x <= ex; x++) {
-				int off = vmem_cell_offset(vstat.vmem, x / vstat.charwidth, y / vstat.charheight);
-				vstat.vmem->vmem[off].bg |= 0x04000000;
-				bitmap_drawn[off].bg |= 0x04000000;
+				if (!yupdated) {
+					if (!xupdated) {
+						if (!same_cell(&bitmap_drawn[off], &vstat.vmem->vmem[off])) {
+							pthread_mutex_unlock(&screenlock);
+							bitmap_draw_from_vmem(charx + 1, chary + 1, charx + 1, chary + 1);
+							pthread_mutex_lock(&screenlock);
+						}
+						if (vstat.vmem && vstat.vmem->vmem) {
+							vstat.vmem->vmem[off].bg |= 0x04000000;
+						}
+						if (bitmap_drawn) {
+							bitmap_drawn[off].bg |= 0x04000000;
+						}
+						xupdated = true;
+					}
+				}
+				if (++cpx >= vstat.charwidth) {
+					cpx = 0;
+					charx++;
+					xupdated = false;
+					off = vmem_next_offset(vstat.vmem, off);
+				}
 				if (screena.rect->data[pixel_offset(&screena, x, y)] != pixels->pixels[pos]) {
 					screena.rect->data[pixel_offset(&screena, x, y)] = pixels->pixels[pos];
 					screena.update_pixels = 1;
@@ -1839,9 +1881,28 @@ int bitmap_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_
 		else {
 			mpos = mask->width * (y - sy + my_off) + mx_off;
 			for (x = sx; x <= ex; x++) {
-				int off = vmem_cell_offset(vstat.vmem, x / vstat.charwidth, y / vstat.charheight);
-				vstat.vmem->vmem[off].bg |= 0x04000000;
-				bitmap_drawn[off].bg |= 0x04000000;
+				if (!yupdated) {
+					if (!xupdated) {
+						if (!same_cell(&bitmap_drawn[off], &vstat.vmem->vmem[off])) {
+							pthread_mutex_unlock(&screenlock);
+							bitmap_draw_from_vmem(charx + 1, chary + 1, charx + 1, chary + 1);
+							pthread_mutex_lock(&screenlock);
+						}
+						if (vstat.vmem && vstat.vmem->vmem) {
+							vstat.vmem->vmem[off].bg |= 0x04000000;
+						}
+						if (bitmap_drawn) {
+							bitmap_drawn[off].bg |= 0x04000000;
+						}
+						xupdated = true;
+					}
+				}
+				if (++cpx >= vstat.charwidth) {
+					cpx = 0;
+					charx++;
+					xupdated = false;
+					off = vmem_next_offset(vstat.vmem, off);
+				}
 				mask_byte = mpos / 8;
 				mask_bit = mpos % 8;
 				mask_bit = 0x80 >> mask_bit;
@@ -1867,6 +1928,14 @@ int bitmap_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_
 				mpos++;
 			}
 		}
+		cpy++;
+		if (cpy >= vstat.charheight) {
+			cpy = 0;
+			yupdated = false;
+			xupdated = false;
+		}
+		else
+			yupdated = true;
 	}
 	pthread_mutex_unlock(&screenlock);
 	do_rwlock_unlock(&vstatlock);
-- 
GitLab