2

I follow a 2D GPU library and saw an issue someone was having with memory allocation. An Array<Map> of enums is called every frame via setBlendMode, ultimately calling Haxe's Type.enumParameters each time.

class GBlendModeFunc
{
    private static var blendFactors:Array<Map<GBlendMode,Array<Context3DBlendFactor>>> = [
        [
            GBlendMode.NONE => [Context3DBlendFactor.ONE, Context3DBlendFactor.ZERO],
            GBlendMode.NORMAL => [Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA],
            GBlendMode.ADD => [Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.DESTINATION_ALPHA],
            GBlendMode.MULTIPLY => [Context3DBlendFactor.DESTINATION_COLOR, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA],
            GBlendMode.SCREEN => [Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE],
            GBlendMode.ERASE => [Context3DBlendFactor.ZERO, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA],
        ],
        [
            GBlendMode.NONE => [Context3DBlendFactor.ONE, Context3DBlendFactor.ZERO],
            GBlendMode.NORMAL => [Context3DBlendFactor.ONE, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA],
            GBlendMode.ADD => [Context3DBlendFactor.ONE, Context3DBlendFactor.ONE],
            GBlendMode.MULTIPLY => [Context3DBlendFactor.DESTINATION_COLOR, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA],
            GBlendMode.SCREEN => [Context3DBlendFactor.ONE, Context3DBlendFactor.ONE_MINUS_SOURCE_COLOR],
            GBlendMode.ERASE => [Context3DBlendFactor.ZERO, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA],
        ]
    ];
    
    static public function setBlendMode(p_context:Context3D, p_mode:GBlendMode, p_premultiplied:Bool):Void {
        var p:Int = (p_premultiplied) ? 1 : 0;
        p_context.setBlendFactors(blendFactors[p][p_mode][0], blendFactors[p][p_mode][1]);
    }
}

The issue here is that Type.enumParameters creates a new Array each time it checks the values of enum array from setBlendMode, quickly adding tens of thousands of variables that trigger GC frequently.

I'm not familiar with best Haxe practices and not sure how to reorganize. Is there a more performant way to store the enums that won't create an allocation issue?

Edit: The GBlendMode enums cannot be converted to abstract enums because it breaks other functions.

Edit 2: A solution was proposed: https://github.com/pshtif/Genome2D-ContextFlash/issues/17

dinorider
  • 191
  • 10

1 Answers1

1

I see this looks like Genome2D code.

But I don't see any code for the GBlendMode enum - and this whole thing hinges on that code. If enum GBlendMode is a normal enum, then at runtime they're arrays. The map getter function has to call Type.enumParameters to compare the keys -- I screen-shot a nodejs callstack to prove this to myself:

enter image description here

However, if GBlendMode is an abstract enum over String, like this:

enum abstract GBlendMode(String) {
  var NONE;
  var NORMAL;
  var ADD;
  var MULTIPLY;
  var SCREEN;
  var ERASE;
}

Then the keys are simple strings, and there is no enum comparison, and there are no intermediate arrays created.

Jeff Ward
  • 16,563
  • 6
  • 48
  • 57
  • It is Genome2D. Here is the original enum class: https://github.com/pshtif/Genome2D-ContextCommon/blob/26ebd5902c3a16506e92026f145e14a91ae4e13b/src/com/genome2d/context/GBlendMode.hx There was pull request that someone issued, but the author declined the pull because it wasn't prototypable: https://github.com/pshtif/Genome2D-ContextCommon/pull/1 Would this cause the same issue or it it different because it's an abstract String? – dinorider Jul 14 '20 at 19:04
  • Ah, this would probably cause the same issue... I'm not sure what `meta-programmed prototyping` is, so I'm not sure I can comment on how to avoid the problem. – Jeff Ward Jul 14 '20 at 19:59