Memory exhausted, transition to SublimeKSP

Reid115
Reid115 Member Posts: 47 Member
edited March 2022 in Scripting Workshop

I've been working on a big project and I just ran into the "memory exhausted" issue, so (instead of stooping to 1=1) I'm condensing the ICB by making one big control param array, which I should have done from the beginning. In order for me to understand what refers to what, I need to put a comment at the end of every row so I know what the parameters are for, like so:

declare %CONTROL[$NUM_CONTROLS * $NUM_PARAMS + 1] := (...
    -1, 515, 5, 74, 18, -1, ... { $button1 }
    -1, 46, 5, -1, -1, -1, ... { $button2 }
    -1, 2, 0, -1, -1, -1, ... { etc... }
0)

The problem is, I've been writing in raw KSP this whole time (intentionally), and KSP doesn't let you write comments in the middle of arrays. So I installed Sublime and everything, but I don't want to write in some extended version of KSP. I literally just want to keep writing raw KSP but have the compiler remove the comments. It would also be nice if the compiled code was actually written to a file that I could then link to the script editor, that way I could just save/compile the file, press the "!" button, and stay in performance view like I've been doing instead of having to constantly open it up after every change and paste code. Is this possible? Also, my raw KSP (that works in Kontakt) doesn't compile in Sublime, which seems really counterintuitive. This line gives me a syntax error:

$in := real_to_int(pow(int_to_real($in_knob) / 1000.0, 3.0) * .025)

A couple other things about the control parameter arrays:

Sometimes I don't want to set a control parameter. For example, maybe I don't want to set the height of an element because I just want it to use the default height (or if it's not vertically resizeable). I figured I would just set the array value to -1 for things I don't want to set, and then do this in the loop that applies the params:

$c := 0
while ($c < $NUM_CONTROLS)
    $d := $c * $NUM_PARAMS
    { ... }
    if (%CONTROL[$d + 4] # -1)
        set_control_par(%ID[$c], $CONTROL_PAR_HEIGHT, %CONTROL[$d + 4])
    end if
    { ... }
    inc($c)
end while

Is this a reasonable way of handling that or should I do something different? And lastly, is there any point to making a control parameter array for string variables? The whole point of this is to condense the code for Kontakt, but string array values can't be set together in the declaration -- they have to be set with separate statements. So you need 1 line to set a value in the string array, and then another line in the array loop to set that control par, totalling 2 lines. If you just set the given control par string right after you declare the ui element, you only need 1 line.

Thanks a bunch.

«1

Comments

  • EvilDragon
    EvilDragon Moderator Posts: 1,022 mod
    edited March 2022

    You can write comments in the middle of arrays, but the comment needs to come before ... to continue in the next row. See some of factory scripts that do exactly that (for example, "Constrain to Scale" script).

    I used to do exactly this sort of thing - one big array with control pars for all widgets (and several string arrays for picture, help text, automation name). It gets tricky if you need to add new widgets somewhere in the middle and then you need to update multiple arrays and possibly later references to certain UI IDs...

    Yes it makes sense to use string arrays. You just skip entries you want to leave undefined.


    I would definitely suggest to learn SublimeKSP. It makes life so much easier in so many ways. And yes you can compile it to a file then link to that file from a resource container. See documentation for SublimeKSP on pragma directives 😀(That said, the doc is old - paths can be relative nowadays.)



    EDIT: As for the syntax error, it seems SublimeKSP requires you to have the integer value for a floating point number (so you'd need to use 0.025 not .025). That would be a bug in the compiler, I will log it.

  • Reid115
    Reid115 Member Posts: 47 Member

    Wow, didn't know I could put comments at the beginning...

    It gets tricky if you need to add new widgets somewhere in the middle and then you need to update multiple arrays and possibly later references to certain UI IDs...

    That was why I originally avoided doing this but I don't really have a choice now with how huge my ICB has become.

    I think I'm a little standoffish towards SublimeKSP because the project I'm working on is really complicated (and at times CPU intensive), and I worry that the code's efficiency might be compromised by however SublimeKSP compiles the code. But I've never used it; I'll definitely look into it.

  • EvilDragon
    EvilDragon Moderator Posts: 1,022 mod

    Especially if your project is really complicated, SublimeKSP saves the day. Many large projects would be unimaginable without it. And you in the end decide how many (or any) SublimeKSP features you want to use. But it's all just really really useful stuff that makes you write LESS code. Plus, it's pretty easy to test what it compiles into, so then you can also learn what your code ends up being (but in general you shouldn't worry about this too much, or at all).

  • Reid115
    Reid115 Member Posts: 47 Member

    Okay. I fixed the floating point stuff and it compiles now. I'll go through the SublimeKSP wiki and try to start using it from now on. Thanks!

  • EvilDragon
    EvilDragon Moderator Posts: 1,022 mod

    And in the meantime I managed to find the bug in the compiler, so whenever next update comes around, you will be able to write .025 style floats (not requiring the part to the left of the decimal point).

  • Reid115
    Reid115 Member Posts: 47 Member

    @EvilDragon

    Followup... So I learned sKSP and made the switch, and I also refactored all the UI control parameters into a giant int array and string array. Everything is working now, except this issue has resurfaced: https://community.native-instruments.com/discussion/407/gui-is-sluggish-too-many-elements

    The GUI is really jittery (e.g., when I move a slider, it appears to move in sluggish steps). Fortunately, I can track down the issue this time because I have an older version that is working smoothly still. I know the following:

    • It's not because of the number of lines of code -- the new ICB has less than half the lines the old code does.
    • It's not because of the size of the control array or the fact that it's inlined. I know this because both versions have 3 huge arrays with 10000 elements each inlined in the ICB for storing envelope curves, and it's not a problem in the old code. To be sure, I put them into .nkas in the new version and did load_array(), and it didn't fix the problem.

    I started deleting a ton of rows from the tail end of the control array to see if it would speed things up, and it did. After some widdling down, I determined that the problem lies in the control param loop:

    for c := 0 to NUM_CONTROLS - 1
        d := c * NUM_INT_PARAMS
        if CONTROL_INT[d] # na
            ID[c] -> parent_panel := CONTROL_INT[d] // *** PROBLEM LINE ***
        end if
        ID[c] -> pos_x := CONTROL_INT[d + 1]
        ID[c] -> pos_y := CONTROL_INT[d + 2]
        if CONTROL_INT[d + 3] # na
            ID[c] -> width := CONTROL_INT[d + 3]
        end if
        if CONTROL_INT[d + 4] # na
            ID[c] -> height := CONTROL_INT[d + 4]
        end if
        if CONTROL_INT[d + 5] # na
            ID[c] -> z_layer := CONTROL_INT[d + 5]
        end if
        if CONTROL_INT[d + 6] # na
            ID[c] -> default_value := CONTROL_INT[d + 6]
            ID[c] -> value := CONTROL_INT[d + 6]
        end if
        if CONTROL_INT[d + 7] # na
            ID[c] -> unit := CONTROL_INT[d + 7]
        end if
        if CONTROL_INT[d + 8] # na
            ID[c] -> mouse_behaviour := CONTROL_INT[d + 8]
        end if
        if CONTROL_INT[d + 9] # na
            ID[c] -> font_type := CONTROL_INT[d + 9]
        end if
        if CONTROL_INT[d + 10] # na
            ID[c] -> textpos_y := CONTROL_INT[d + 10]
        end if
        if CONTROL_INT[d + 11] # na
            ID[c] -> text_alignment := CONTROL_INT[d + 11]
        end if
        if CONTROL_INT[d + 12] # na
            ID[c] -> hide := CONTROL_INT[d + 12]
        end if
        if CONTROL_INT[d + 13] # na
            ID[c] -> show_arrows := CONTROL_INT[d + 13]
        end if
    end for
    

    For some reason, the UI becomes really sluggish when I set the parent panel of the elements. I don't have a clue why this would be (or why it's just now becoming a problem when the old code uses parent panels just as much). I know this is the problem because I started doing this:

    if CONTROL_INT[d] # na
        if c < 256
            ID[c] -> parent_panel := CONTROL_INT[d]
        else
            //ID[c] -> parent_panel := CONTROL_INT[d]  // base case, very jittery
            //ID[c] -> parent_panel := get_ui_id(panel)  // set to general panel instead of panel specified in array, better but still fairly jittery
            // do nothing, fixes jitter
        end if
    end if
    

    I wrapped different parts of the control loop in an arbitrary cutoff of 256, which was about the number of rows I had to delete from the array to get the problem to noticeably go away (the control array currently has about 460 rows total). The above part that sets the parent panel was the only part that resulted in the fix. If I change 256 to a really high number such that all the parent panels get set, the problem comes back. I know it has nothing to do with the pictures of the elements, as I've left the string array unbothered, and I know it has nothing to do with the number of elements on the screen. In fact, most panels are hidden, so removing the problem line actually results in even more elements on the screen. I thought it might have something to do with the new looping structure vs. setting the parents directly line by line, so I wrote a quick python script to get this block of explicit lines: https://pastebin.com/raw/jGZkbdCT

    If I comment out the above part that sets the parent in the loop, and instead paste that big block of explicit lines right after the big UI declare block, the problem still happens. If I comment out about half of the parent lines, the problem goes away. So it seems to be something specifically about how KSP handles parent panels, and nothing to do with this new control array structure. Here's what the control array looks like if you're curious: https://i.imgur.com/Kj3c92t.png

    I guess I could try getting rid of all the parent panels and just use the absolute x/y positions, but I know I'll need to keep at least some for managing the hiding and z-layers. And that doesn't really feel like a solution. Any ideas? ☹️

  • EvilDragon
    EvilDragon Moderator Posts: 1,022 mod
    edited April 2022

    Hmmm why all those checks if a control par is "na"? If you just have a 0 in the array where you store control pars, it is fine to assign control par like that. It's not saving you any work - doing the check vs setting a control par. Not that it would affect anything regarding panels, but it's just a bit less readable code IMO.

    That said, I wouldn't know why you get this behavior... You only have 36 panels here. Shouldn't be a problem. I'm using twice that amount (and they're even nested) in Butch Vig Drums (it's all done with Creator Tools' GUI Designer, though), and never an issue with stuttery controls...

  • Reid115
    Reid115 Member Posts: 47 Member
    edited April 2022

    Hmmm why all those checks if a control par is "na"? If you just have a 0 in the array where you store control pars, it is fine to assign control par like that. It's not saving you any work - doing the check vs setting a control par. Not that it would affect anything regarding panels, but it's just a bit less readable code IMO.

    na = N/A (defined as -2 because I know I will never use -2 in a control param). I have elements that I want to just use the default width/height. If I set them to 0 without a check, it will make them disappear (0w x 0h).

    That said, I wouldn't know why you get this behavior... You only have 36 panels here. Shouldn't be a problem. I'm using twice that amount (and they're even nested) in Butch Vig Drums (it's all done with Creator Tools' GUI Designer, though), and never an issue with stuttery controls...

    I think what must be happening is that I have some code that's causing problems for the UI thread, and so inevitably, when I remove UI elements/parameters, the problem is less impactful. Why it's specifically reacting to parent panels I don't know, but that has to be a red herring. Last time this happened, I think the source had to do with continuously hiding an element in the LCB if voices weren't playing, which I've since fixed. I think the issue is more complex this time and unrelated to LCB, but it's probably something like that somewhere. I found a version from right before I restructured the ICB (and right after I switched to sKSP) where the issue exists, but not in the version previous to that. So I'll go through both really closely and try to track down the specific problem change. I noticed that when I move any control in the issue version, Reaper's CPU meter is almost double what it is when I move the same control in the non-issue version, despite the UI CBs being the same...

  • EvilDragon
    EvilDragon Moderator Posts: 1,022 mod

    na = N/A (defined as -2 because I know I will never use -2 in a control param). I have elements that I want to just use the default width/height. If I set them to 0 without a check, it will make them disappear (0w x 0h).

    I'd just spell out the exact pixel values anyways and not rely on default values at all. All those ifs are really unnecessary IMO, but fine if you want it like that.

    This is a really weird but also a really curious issue. If you manage to figure it out, let me know!

  • Reid115
    Reid115 Member Posts: 47 Member
    edited April 2022

    It gets weirder... So I slowly incorporated more and more changes to an old version that was working until I found the culprit that introduces the problem. Basically, several months ago (Dec/Jan) I had an idea for a dialog that pops up to do something. I created a big button as a placeholder for the background of the dialog (button instead of label so the clicks don't pass through). I think it was on a panel that was hidden or something -- it had no purpose whatsoever and I later scrapped the idea. In the process of refactoring my ICB, I saw this button was still in my code and deleted it. Deleting this button is what introduces the problem despite the fact that this button is used NOWHERE in the code. I reduced the code down to these two lines:

    declare ui_button $test
    set_control_par_str(get_ui_id($test), $CONTROL_PAR_PICTURE, "test")
    

    If I take these two simple lines and paste them ANYWHERE in my ICB in the most current version, the problem goes away -- no more UI jitter, no more 2x Reaper CPU on UI moving. The button must not be hidden, but it can be on any z-layer. The name of the variable/picture is irrelevant, but the non-resizeable picture must be about 550x300 (the original picture was 547x270). I don't think there's an exact pixel cutoff, but as you reduce the size of the picture, the problem will introduce itself. So the KSP engine is basically saying it needs there to be a big button on the screen at all times... ??? 😵 No clue where to go from here. This is probably the strangest bug I've ever encounted (KSP or otherwise).

    EDIT: The funny thing is, I can make the picture completely transparent, and then do this as a workaround to fix the problem:

    declare ui_panel test_pl
    test_pl -> parent_panel := get_ui_id(panel)
    test_pl -> z_layer := -1
    declare ui_button test
    test -> parent_panel := get_ui_id(test_pl)
    test -> z_layer := -1
    test -> picture := "test"
    test -> text := ""
    

    Obviously that's not a real solution though.

  • EvilDragon
    EvilDragon Moderator Posts: 1,022 mod
    edited April 2022

    OK so this makes sense. If the button is not hidden, it will trigger invalidation for that whole area and everything below it gets redrawn, even if the picture is transparent.


    EDIT: No wait, it DOESN'T make sense that the button needs to be there to reduce CPU. Weird.

  • EvilDragon
    EvilDragon Moderator Posts: 1,022 mod

    Does this happen with vanilla unskinned controls too? Could you make a minimum case to reproduce, then I could send this to Kontakt team for inspection.

  • Reid115
    Reid115 Member Posts: 47 Member

    everything below it gets redrawn

    I was thinking something along the same lines but the button can be on the lowest z-layer with no problem. It can even be on a hidden panel, so long as the button itself is not hidden.

    Does this happen with vanilla unskinned controls too?

    Yes. Adding more and more UI elements/pictures to existing elements increases the impact of the problem, but it definitely happens without skinned controls.

    I just sent you a zip of the code in a PM.

  • Reid115
    Reid115 Member Posts: 47 Member
    edited April 2022

    One final thing I just discovered... this behavior is not binary. The GUI smoothness increases and CPU usage decreases the more buttons you make in the same fashion. This is what my code looks like now:

    declare ui_panel test_pl
    test_pl -> parent_panel := get_ui_id(panel)
    test_pl -> z_layer := -1
    test_pl -> hide := HIDE_WHOLE_CONTROL
    macro reduce_CPU(#n#)
        declare ui_button test#n#
        test#n# -> parent_panel := get_ui_id(test_pl)
        test#n# -> z_layer := -1
        test#n# -> picture := "test"
        test#n# -> text := ""
    end macro
    iterate_macro(reduce_CPU) := 1 to 10
    

    There's a pretty proportional Reaper CPU falloff as I increase the number of buttons. Once I get to about 10, the UI uses basically 0% CPU. Roughly: 0 buttons = 17%, 1 button = 10%, 3 buttons = 7%, 10 buttons = 0%.

  • Reid115
    Reid115 Member Posts: 47 Member

    Another detail -- the issue only happens when the movement of a ui control results in some control parameter being set. For example, the first two knobs will have the cpu problem, the third will not:

    on ui_control (knob1)
        blah1 -> text := ""
    end on
    
    on ui_control (knob2)
        blah2 -> pos_x := 0
    end on
    
    on ui_control (knob3)
        blah3 := 0
    end on
    
Back To Top