This is, unfortunately, going to be pretty vague / open-ended, so I'm apologizing in advance.
For one of my previous projects, in Java, I utilized a multithreaded approach to certain drawing and AI functions. My game's currently running, stable and largely stays in the 300-500 FPS range right now, with most of the major features in place, but there are occasional heavy events that require serious horsepower, and I'd like to explore what can be multithreaded or pushed to pure C#. So, here are some newbie Q's. Again, sorry if these seem woefully ignorant.
1. My limited understanding is that GMS can be extended via DLLs or their OS-specific equivalent. These would (typically) be built in <language of choice> in something like Visual Studio, then used as Extensions in GMS. There isn't much documentation about this, other than the basics, and so I have some serious questions about the utility of doing these things.
For example, one of the things I'd really like to do is accelerate some code that deals with large collections of points. I don't see anything in the documentation suggesting that I can send complex data structures (ds_lists, for example) via a call to an Extension. How is this typically worked around?
2. My previous experience with multithreading was via the Lombok library for Java. If using C# instead, does anybody recommend a library that provides (relatively) simple access to thread-safe methods, locking, etc.?
3. If passing multithreaded code back to GMS, what happens if it can't return during the current frame? Will GMS's main frame be halted awaiting results? Will it crash? Be garbage-collected?
4. If I just want to squeeze a little more optimization out of my game for certain math-heavy operations, are there other ways I should explore this, like writing Extension code to handle very specific issues?
5. What about memory management? GMS is already a bit alarming in how it handles certain data types; with Extensions, this seems like it might be one of the big challenges. Is it typical, for example, in developing Extensions for GMS, to set up memory allocations via statics at runtime, so that there aren't leaks, which means variables have to be scoped carefully and overflows of, say, arguments consisting of many floating-point numbers will cause a crash? Do I have to be wary about creating temporary variables, beyond the usual care one requires with any language?
Anyhow, sorry for the vagueness. I'm staring at GMS's documentation right now, and the limit to "4 arguments, unless they're all the same type", "don't send data structures", "numbers are always doubles", etc. is all a little confusing / intimidating, and there's pretty much zero information in the documentation about dealing with thread safety in state machines, etc.
Here's a typical piece of code I'd like to explore accelerating, because it's so inherently slow in GMS:
None of the steps are particularly slow, but the processing required is quite heavy when we're talking about many Instances being evaluated; there's a lot of trig involved (and a fair amount before we get this code, doing distance checks and so forth).
However, I'd have to send all the data being acted upon as doubles, then send it back (and it's not entirely clear how one returns data; can I return an array of doubles, or am I limited to exactly one double, or a string I'd have to laboriously convert back into an array?). If I'm limited to a return value of one double, there's really no point in any of this being in an extension, of course. Honestly, the more I look at this, the less pleased I am; what use is something that only returns one value, and, if not GML, cannot call GM internal functions directly? Or is there some way to do that, and documentation of the functions somewhere?
For one of my previous projects, in Java, I utilized a multithreaded approach to certain drawing and AI functions. My game's currently running, stable and largely stays in the 300-500 FPS range right now, with most of the major features in place, but there are occasional heavy events that require serious horsepower, and I'd like to explore what can be multithreaded or pushed to pure C#. So, here are some newbie Q's. Again, sorry if these seem woefully ignorant.
1. My limited understanding is that GMS can be extended via DLLs or their OS-specific equivalent. These would (typically) be built in <language of choice> in something like Visual Studio, then used as Extensions in GMS. There isn't much documentation about this, other than the basics, and so I have some serious questions about the utility of doing these things.
For example, one of the things I'd really like to do is accelerate some code that deals with large collections of points. I don't see anything in the documentation suggesting that I can send complex data structures (ds_lists, for example) via a call to an Extension. How is this typically worked around?
2. My previous experience with multithreading was via the Lombok library for Java. If using C# instead, does anybody recommend a library that provides (relatively) simple access to thread-safe methods, locking, etc.?
3. If passing multithreaded code back to GMS, what happens if it can't return during the current frame? Will GMS's main frame be halted awaiting results? Will it crash? Be garbage-collected?
4. If I just want to squeeze a little more optimization out of my game for certain math-heavy operations, are there other ways I should explore this, like writing Extension code to handle very specific issues?
5. What about memory management? GMS is already a bit alarming in how it handles certain data types; with Extensions, this seems like it might be one of the big challenges. Is it typical, for example, in developing Extensions for GMS, to set up memory allocations via statics at runtime, so that there aren't leaks, which means variables have to be scoped carefully and overflows of, say, arguments consisting of many floating-point numbers will cause a crash? Do I have to be wary about creating temporary variables, beyond the usual care one requires with any language?
Anyhow, sorry for the vagueness. I'm staring at GMS's documentation right now, and the limit to "4 arguments, unless they're all the same type", "don't send data structures", "numbers are always doubles", etc. is all a little confusing / intimidating, and there's pretty much zero information in the documentation about dealing with thread safety in state machines, etc.
Here's a typical piece of code I'd like to explore accelerating, because it's so inherently slow in GMS:
GML:
if(isCircle = false){
var leftNum, rightNum;
for(var i = 0; i < aura_shadow_points; i++;){
a_tx[i] = ((x + aura_shadow_x[i]) - a_ox) + a_rad;
a_ty[i] = ((y + aura_shadow_y[i]) - a_oy) + a_rad;
a_dir = point_direction(a_tx[i], a_ty[i], a_rad, a_rad) + 180;
if(i = 0){
rightX = a_tx[i];
rightY = a_ty[i];
leftX = a_tx[i];
leftY = a_ty[i];
rightAngle = a_dir;
leftAngle = a_dir;
leftNum = 0;
rightNum = 0;
} else {
if(angle_difference(a_dir,rightAngle) > 0){
rightX = a_tx[i];
rightY = a_ty[i];
rightAngle = a_dir;
rightNum = i;
}
if(angle_difference(a_dir,leftAngle) < 0){
leftX = a_tx[i];
leftY = a_ty[i];
leftAngle = a_dir;
leftNum = i;
}
}
}
} else {
var offX = (x - a_ox) + a_rad -1.0;
var offY = (y - a_oy) + a_rad -1.0;
var centerAngle = point_direction(offX,offY,a_rad,a_rad)+180;
var rightPAng = centerAngle + 90;
var leftPAng = centerAngle + 270;
var size = (abs(sprite_width)*0.5)-1;
rightX = (offX) + lengthdir_x(size,rightPAng);
leftX = (offX) + lengthdir_x(size,leftPAng);
rightY = (offY) + lengthdir_y(size,rightPAng);
leftY = (offY) + lengthdir_y(size,leftPAng);
rightAngle = point_direction(rightX,rightY,a_rad,a_rad)+180;
leftAngle = point_direction(leftX,leftY,a_rad,a_rad)+180;
}
//Now that we have the furthest-right and furthest-left, let's draw.
//First, we need to get the far-away points on the triangles.
rightFarPointX = rightX + lengthdir_x(a_rad_big,rightAngle);
rightFarPointY = rightY + lengthdir_y(a_rad_big,rightAngle);
leftFarPointX = leftX + lengthdir_x(a_rad_big,leftAngle);
leftFarPointY = leftY + lengthdir_y(a_rad_big,leftAngle);
if(!canMoveEver){
ds_map_add(myRightX,obstacleID,rightX);
ds_map_add(myRightY,obstacleID,rightY);
ds_map_add(myRightFarPointX,obstacleID,rightFarPointX);
ds_map_add(myRightFarPointY,obstacleID,rightFarPointY);
ds_map_add(myLeftX,obstacleID,leftX);
ds_map_add(myLeftY,obstacleID,leftY);
ds_map_add(myLeftFarPointX,obstacleID,leftFarPointX);
ds_map_add(myLeftFarPointY,obstacleID,leftFarPointY);
}
However, I'd have to send all the data being acted upon as doubles, then send it back (and it's not entirely clear how one returns data; can I return an array of doubles, or am I limited to exactly one double, or a string I'd have to laboriously convert back into an array?). If I'm limited to a return value of one double, there's really no point in any of this being in an extension, of course. Honestly, the more I look at this, the less pleased I am; what use is something that only returns one value, and, if not GML, cannot call GM internal functions directly? Or is there some way to do that, and documentation of the functions somewhere?