I have therefore written a new library spawnlib.bbc, which is compatible with both BB4W and BBCSDL, and with all the supported desktop platforms (Windows, x86 MacOS, Raspberry Pi and Linux). This library significantly simplifies the task of coordinating multiple processes by sharing a nominated block of memory - in the form of a structure - between them.
This example is of dividing the task of drawing the Mandelbrot set between eight sub-processes, each of which renders one strip of the final image:
Code: Select all
10 REM Mandelbrot Parallel Renderer
20 REM Demonstrates multi-process rendering using shared memory structures
30 REM Originally suggested by DeepSeek AI, got working by Richard Russell
40
50 INSTALL @lib$ + "spawnlib"
60
70 REM Graphics setup
80 MODE 20 : REM 800x600
90 OFF
100
110 REM Set window dimensions
120 Width% = 800 : Height% = 560
130
140 REM Number of child processes (strips)
150 numProcs% = 8
160
170 REM Divide screen into horizontal strips
180 stripHeight% = Height% DIV numProcs%
190
200 REM Maximum number of iterations
210 maxIter% = 20
220
230 REM Create shared structure array for each child process
240 DIM Shared{(numProcs%-1) bfType&(1), bfSize%, bfReserved%, bfOffBits%, \
250 \ biSize%, biWidth%, biHeight%, biPlanes&(1), biBitCount&(1), \
260 \ biCompression%, biSizeImage%, biXPelsPerMeter%, biYPelsPerMeter%, \
270 \ biClrUsed%, biClrImportant%, palette%(255), p&(Height%-1,Width%-1), \
280 \ startY%, fullY%, complete%, xmin, xmax, ymin, ymax, maxIter%}
290
300 REM Populate the structures
310 FOR p% = 0 TO numProcs% - 1
320 REM Initialise bitmap
330 Shared{(p%)}.bfType&(0) = ASC"B"
340 Shared{(p%)}.bfType&(1) = ASC"M"
350 Shared{(p%)}.bfSize% = ^Shared{(p%)}.startY% - Shared{(p%)}
360 Shared{(p%)}.bfOffBits% = ^Shared{(p%)}.p&(0,0) - Shared{(p%)}
370 Shared{(p%)}.biSize% = 40
380 Shared{(p%)}.biWidth% = Width%
390 Shared{(p%)}.biHeight% = stripHeight%
400 Shared{(p%)}.biPlanes&(0) = 1
410 Shared{(p%)}.biBitCount&(0) = 8
420 Shared{(p%)}.fullY% = Height%
430
440 REM Set palette
450 FOR index% = 1 TO maxIter%-1
460 r% = &FF
470 g% = 100 + 155 * index% / maxIter%
480 b% = 0
490 Shared{(p%)}.palette%(index%) = &FF000000 OR (r% << 16) OR (g% << 8) OR b%
500 NEXT
510
520 REM Fractal parameters - adjust for interesting views
530 Shared{(p%)}.xmin = -2.1
540 Shared{(p%)}.xmax = 0.8
550 Shared{(p%)}.ymin = -1.2
560 Shared{(p%)}.ymax = 1.2
570 Shared{(p%)}.maxIter% = maxIter%
580
590 REM Calculate strip boundaries
600 Shared{(p%)}.startY% = p% * stripHeight%
610 NEXT
620
630 REM Update display in a timer interrupt
640 ON TIME PROCdisplay : RETURN
650
660 REM Spawn processes
670 PRINT " Spawning "; numProcs%; " worker processes... ";
680 PRINT "Each process renders a different horizontal strip of the screen."
690 TIME = 0
700 FOR p% = 0 TO numProcs%-1
710 PROC_spawn(PROCmandelworker(), Shared{(p%)}, "-hidden")
720 PRINT ," Spawn ";p%;
730 NEXT p%
740
750 REM Wait for all children to complete
760 REPEAT
770 allDone% = TRUE
780 FOR p% = 0 TO numProcs%-1
790 IF NOT Shared{(p%)}.complete% THEN allDone% = FALSE
800 NEXT p%
810
820 WAIT 5 : REM Don't hammer the CPU
830 UNTIL allDone%
840
850 PRINT STRING$(POS, CHR$127); " Rendering complete in "; TIME/100; " seconds"
860
870 REPEAT UNTIL INKEY(0) <> -1
880 END
890
900 DEF PROCdisplay
910 LOCAL p%
920 *HEX 64
930 FOR p% = 0 TO numProcs%-1
940 OSCLI "MDISPLAY " + STR$~Shared{(p%)} + " 0," + STR$(p% * stripHeight% * 2)
950 NEXT
960 *REFRESH
970 ENDPROC
980
990 REM Mandelbrot Strip Renderer for Parallel Processing
1000 REM Parameters are passed in via shared structures
1010
1020 DEF PROCmandelworker(shared{})
1030
1040 xmin = shared.xmin : xmax = shared.xmax
1050 ymin = shared.ymin : ymax = shared.ymax
1060 startY% = shared.startY%
1070 fullY% = shared.fullY%
1080 maxIter% = shared.maxIter%
1090 width% = shared.biWidth%
1100 height% = shared.biHeight%
1110
1120 REM Renders a horizontal strip starting from startY%
1130
1140 LOCAL x%, y%, iter%, cr, ci, zr, zi, zr2, zi2, smooth, col%
1150
1160 FOR y% = 0 TO height% - 1
1170 REM Map y pixel to imaginary coordinate
1180 ci = ymax - ((y% + startY%) / fullY%) * (ymax - ymin)
1190
1200 FOR x% = 0 TO width%-1
1210 REM Map x pixel to real coordinate
1220 cr = xmin + (x% / width%) * (xmax - xmin)
1230
1240 zr = 0.0
1250 zi = 0.0
1260 zr2 = 0.0
1270 zi2 = 0.0
1280 iter% = 0
1290
1300 REM Iterate until escape or max iterations reached
1310 WHILE (zr2 + zi2) < 4.0 AND iter% < maxIter%
1320 zi = 2.0 * zr * zi + ci
1330 zr = zr2 - zi2 + cr
1340 zr2 = zr * zr
1350 zi2 = zi * zi
1360 iter% += 1
1370 ENDWHILE
1380
1390 REM Colour mapping - smooth gradient for escaped points
1400 IF iter% = maxIter% THEN
1410 col% = 0 : REM Black for points in the set
1420 ELSE
1430 REM Smooth colouring for nicer gradients
1440 smooth = iter% + 1 - LN(LN(zr2 + zi2) / 2) / LN(2)
1450 col% = smooth
1460 ENDIF
1470
1480 REM Plot the pixel
1490 shared.p&(y%,x%) = col%
1500 NEXT x%
1510
1520 REM Allow other processes to run - good practice for co-operative multitasking
1530 WAIT 0
1540 NEXT y%
1550
1560 shared.complete% = TRUE
1570 ENDPROC
https://youtu.be/03mEBCjx2VU