So I have now tested it out. The result is not really surprising.
But it is nice to have a comparison.
The aim was to create a background stream.
Three images with different sizes (256x256,1024x1024 and 2048x2048) are filled with random colours in the background at the same time.
First version is a pure AppGameKit solution.
Second version is the same approach as a plugin.
Third version is a multithreaded solution.
I don't know how far your solution matches mine nor if the speed is comparable.
The values on my PC are as follows.
AGK solution: 50-60 FPS, All three images (256,1024 and 2048) are updated approx. 1.6 times per second.
Plugin solution: approx. 2500 FPS, All three images (256,1024 and 2048) are updated approx. 18.5 times per second.
Muiltithread solution: Fluctuates between 3000 and 4000 FPS.
256x256 image is updated approx. 510 times per second.
1024x1024 image is updated approx. 59 times per second.
2048x2048 image is updated approx. 16.6 times per second.
In the attachment you will find the executable project.
Here is the code.
If you can use some of it, feel free to help yourself.
If you have any questions about the code, feel free to ask.
#import_plugin Test as tt
// Project: ThreadTest
// Created: 2022-11-16
// show all errors
SetErrorMode(2)
// set window properties
SetWindowTitle( "ThreadTest" )
SetWindowSize( 1024, 768, 0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window
// set display properties
SetVirtualResolution( 1024, 768 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate( 0, 0 ) // 30fps instead of 60 to save battery
SetScissor( 0,0,0,0 ) // use the maximum available screen space, no black borders
UseNewDefaultFonts( 1 ) // since version 2.0.22 we can use nicer default fonts
#constant STATE_AGK_COMMON 0
#constant STATE_PLUGIN_COMMON 1
#constant STATE_PLUGIN_THREAD 2
state as integer = STATE_PLUGIN_THREAD
type tStreamMemblock
mem_id as integer
locked as integer
size as integer
chunk as integer
ptr as integer
endtype
block256 as tStreamMemblock
block1024 as tStreamMemblock
block2048 as tStreamMemblock
count256 as integer
count1024 as integer
count2048 as integer
CreateRenderImage(1,256,256,0,0)
CreateMemblockFromImage(1,1)
SetMemblockStream(1, block256)
b256 = tt.SetMemblockStream(1)
CreateRenderImage(2,1024,1024,0,0)
CreateMemblockFromImage(2,2)
SetMemblockStream(2, block1024)
b1024 = tt.SetMemblockStream(2)
CreateRenderImage(3,2048,2048,0,0)
CreateMemblockFromImage(3,3)
SetMemblockStream(3, block2048)
b2048 = tt.SetMemblockStream(3)
CreateSprite(1,1)
SetSpritePosition(1,0,256)
CreateSprite(2,2)
SetSpriteSize(2,256,256)
SetSpritePosition(2,256,256)
CreateSprite(3,3)
SetSpriteSize(3,256,256)
SetSpritePosition(3,512,256)
start as float
start = Timer()
do
select state
case STATE_AGK_COMMON
if FillMemblockStream(block256) = 1
CreateImageFromMemblock(1,1)
inc count256
endif
if FillMemblockStream(block1024) = 1
CreateImageFromMemblock(2,2)
inc count1024
endif
if FillMemblockStream(block2048) = 1
CreateImageFromMemblock(3,3)
inc count2048
endif
endcase
case STATE_PLUGIN_COMMON
if tt.FillMemblockStream(b256) = 1
CreateImageFromMemblock(1,1)
inc count256
endif
if tt.FillMemblockStream(b1024) = 1
CreateImageFromMemblock(2,2)
inc count1024
endif
if tt.FillMemblockStream(b2048) = 1
CreateImageFromMemblock(3,3)
inc count2048
endif
endcase
case STATE_PLUGIN_THREAD
if tt.GetMemblockLocked(b256) = 0
CreateImageFromMemblock(1,1)
inc count256
tt.StartFillMemblockThread(b256)
endif
if tt.GetMemblockLocked(b1024) = 0
CreateImageFromMemblock(2,2)
inc count1024
tt.StartFillMemblockThread(b1024)
endif
if tt.GetMemblockLocked(b2048) = 0
CreateImageFromMemblock(3,3)
inc count2048
tt.StartFillMemblockThread(b2048)
endif
endcase
endselect
Print( ScreenFPS() )
print("image 256 : "+str(count256)+"(IPS: "+str(count256/(Timer()-start))+")")
print("image 1024: "+str(count1024)+"(IPS: "+str(count1024/(Timer()-start))+")")
print("image 2048: "+str(count2048)+"(IPS: "+str(count2048/(Timer()-start))+")")
Sync()
loop
function SetMemblockStream(id as integer, block ref as tStreamMemblock)
if id <= 0 then exitfunction -1
if GetMemblockExists(id) = 0 then exitfunction -1
block.size = GetMemblockSize(id)
block.ptr = 12
block.locked = 0
block.mem_id = id
block.chunk = block.size / 128
endfunction 1
function FillMemblockStream(block ref as tStreamMemblock)
if block.ptr < block.size
block.locked = 1
for i=0 to block.chunk
SetMemblockInt(block.mem_id,block.ptr,MakeColor(Random(0,255),Random(0,255),Random(0,255),255))
inc block.ptr,4
if block.ptr >= block.size then exit
next
exitfunction 0
endif
block.locked = 0
block.ptr = 12
endfunction 1
Plugin-Code
#include "AGKLibraryCommands.h"
#include <vector>
#include <thread>
struct SStreamBlock
{
int mem_id;
bool locked;
std::thread* thread;
int size;
int chunk;
unsigned char* mem_ptr;
unsigned char* mem_end;
SStreamBlock(int id) :
mem_id(id),
locked(false), size(0), chunk(8192*2),
thread(NULL), mem_ptr(NULL), mem_end(NULL) {}
};
std::vector<SStreamBlock*> stream_array;
void stream_fnc(SStreamBlock* block)
{
*((unsigned int*)block->mem_ptr) = agk::MakeColor(
agk::Random(0, 255),
agk::Random(0, 255),
agk::Random(0, 255),
255);
block->mem_ptr += 4;
}
DLL_EXPORT int SetMemblockStream(int id)
{
if (id <= 0) return -1;
if (!agk::GetMemblockExists(id)) return -1;
SStreamBlock* block = new SStreamBlock(id);
int result = (int)stream_array.size();
block->mem_ptr = agk::GetMemblockPtr(id)+12;
block->size = agk::GetMemblockSize(id);
block->mem_end = block->mem_ptr + block->size;
block->chunk = block->size / 128;
stream_array.push_back(block);
return result;
}
DLL_EXPORT int FillMemblockStream(int id)
{
SStreamBlock* block = stream_array[id];
if (block->mem_ptr < block->mem_end)
{
block->locked = 1;
unsigned char* end = std::min(block->mem_ptr + block->chunk, block->mem_end);
for (; block->mem_ptr < end; block->mem_ptr += 4)
{
*((unsigned int*)block->mem_ptr) = agk::MakeColor(
agk::Random(0, 255),
agk::Random(0, 255),
agk::Random(0, 255),
255);
}
return 0;
}
block->locked = 0;
block->mem_ptr = agk::GetMemblockPtr(block->mem_id)+12;
return 1;
}
void fill_memblock(SStreamBlock* block)
{
block->locked = 1;
unsigned char* end = std::min(block->mem_ptr + block->chunk, block->mem_end);
for (unsigned char* ptr= block->mem_ptr; ptr < block->mem_end; ptr += 4)
{
*((unsigned int*)ptr) = agk::MakeColor(
agk::Random(0, 255),
agk::Random(0, 255),
agk::Random(0, 255),
255);
}
block->locked = 0;
}
DLL_EXPORT int GetMemblockLocked(int id)
{
SStreamBlock* block = stream_array[id];
if (!block) return -1;
return block->locked;
}
DLL_EXPORT int StartFillMemblockThread(int id)
{
SStreamBlock* block = stream_array[id];
if (block)
{
if (!block->locked)
{
if (block->thread)
{
block->thread->join();
delete block->thread;
}
block->thread = new std::thread(fill_memblock, block);
return 1;
}
}
return 0;
}