SetRenderingParameters shaved 1.7M cycles off level loading

Topics related to the API, programming discussions & questions, coding tips, bugs, etc. should go here.
Post Reply
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

SetRenderingParameters shaved 1.7M cycles off level loading

Post by Artcfox »

Thanks to a conversation with D3thAdd3r, I discovered the joys of calling SetRenderingParameters before and after something cycle intensive to make it happen much quicker.

I was already bracketing my call to LoadLevel with calls to FadeOut and FadeIn, so my screen was already black for the duration of the call to LoadLevel.

I used the "wdr" instruction to get a baseline, and to test the performance of various parameters:

Code: Select all

__asm__ __volatile__ ("wdr");
    levelOffset = LoadLevel(currentLevel, &theme, &treasuresLeft, &timeBonus);
__asm__ __volatile__ ("wdr");
and the results for loading each level really surprised me:

Code: Select all

WDR measured 1942481 cycles
WDR measured 1937134 cycles
WDR measured 1950455 cycles
WDR measured 1946936 cycles
WDR measured 1949280 cycles
WDR measured 1951888 cycles
WDR measured 1950447 cycles
WDR measured 1955282 cycles
WDR measured 2374249 cycles
WDR measured 1951018 cycles
WDR measured 1958714 cycles
WDR measured 2385729 cycles
WDR measured 1940968 cycles
WDR measured 1949972 cycles
WDR measured 1948121 cycles
WDR measured 2380278 cycles
I figured that unpacking levels was slow, but I didn't expect it to be 2M cycles slow! :o

When I bracketed my call to LoadLevel with calls to the SetRenderingParameters function:

Code: Select all

__asm__ __volatile__ ("wdr");
    SetRenderingParameters(262 - 80, 80);
    levelOffset = LoadLevel(currentLevel, &theme, &treasuresLeft, &timeBonus);
    SetRenderingParameters(FIRST_RENDER_LINE, FRAME_LINES);
__asm__ __volatile__ ("wdr");
the end result is it shaved 1.7M cycles off each call to LoadLevel!

Code: Select all

WDR measured 286914 cycles
WDR measured 281486 cycles
WDR measured 294796 cycles
WDR measured 291281 cycles
WDR measured 293272 cycles
WDR measured 296224 cycles
WDR measured 294806 cycles
WDR measured 299639 cycles
WDR measured 308874 cycles
WDR measured 295345 cycles
WDR measured 303093 cycles
WDR measured 319944 cycles
WDR measured 285322 cycles
WDR measured 293959 cycles
WDR measured 292476 cycles
WDR measured 314475 cycles
I am truly astounded! :D

During my search to figure out what I needed to restore the parameters to, I noticed that the demos MegaSokoban and chess4uzebox also call SetRenderingParameters, but they call SetRenderingParameters(1, 1). When I tried those values, it actually didn't save me any cycles, and I'm curious why (I assume) it would save them cycles, but not me. The only difference I can think is that modes 10, and 5 work differently than mode 3, but I'm not familiar enough with those other modes to know why.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: SetRenderingParameters shaved 1.7M cycles off level loading

Post by Artcfox »

Unfortunately I discovered that if TriggerFx() is playing a sound effect at the time SetRenderingParameters is called, the notes get weird (they sustain for a tiny bit, FML). :cry:

This happens very often actually, because when the player dies, a "player death" sound effect is triggered, and your first instinct is to hit the button to reload the level and try again.

So, it looks like I'm left with three options:
  • Try to find a way to stop all sound effects that are currently in progress, but a sudden stop in the middle of an effect might be jarring, and I think there is something to be said for forcing the player hear the full "player death" sound (to rub it in).
  • Figure out when the sound effect has stopped playing, and only then call SetRenderingParameters.
  • Remove all calls to SetRenderingParameters, and let the level load 60 milliseconds slower (and reclaim a tiny bit of flash space).
Or, the following "hit it with a sledgehammer" option is looking more and more tempting:
  • Never call TriggerFx() from user code, instead create a message queue, and when I want to play a sound effect, post a message to the queue, and use SetUserPostVsyncCallback() and a custom VsyncCallBack() function whose only task is to monitor the message queue, and call TriggerFx() from a known safe place/time in the code. (Though that might not actually solve this problem, because it would only ensure that the initial call to TriggerFx() happens from a known safe place/time, not that the sound effect has completed.)
Unless anyone has a better suggestion, since flash space is at a premium, the third option seems the best.
User avatar
uze6666
Site Admin
Posts: 4801
Joined: Tue Aug 12, 2008 9:13 pm
Location: Montreal, Canada
Contact:

Re: SetRenderingParameters shaved 1.7M cycles off level loading

Post by uze6666 »

I really can't understand why setrenderingparameters causes sound issues! But for only 60ms its really not worth anything but option 3. I really like your idea about the queue though. I'll keep this one in mind.
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: SetRenderingParameters shaved 1.7M cycles off level loading

Post by D3thAdd3r »

I'm a big fan of drastic measures, and no clue why it should happen, but I have to agree with Alec. Maybe if you finished up other details and came back to it you wouldn't care about a couple extra frames of black. Then again there is something appreciable for perfectionism and attention to detail too.
Artcfox wrote: I think there is something to be said for forcing the player hear the full "player death" sound (to rub it in).
I agree death sequences are there to add some gravity to input/mechanics choices and make you care. I don't think it would be that hard or take much code to check that all sound effects are done though. This isn't tested, but isn't it as easy as this?(I am not 100% sure)

Code: Select all

//wave table based effects are always channel 1 or 2
//if using music you could also check for TRACK_FLAGS_PRIORITY

inline uint8_t SoundEffectPlaying(){
	return((tracks[1].flags | tracks[2].flags) & TRACK_FLAGS_PLAYING);
}
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: SetRenderingParameters shaved 1.7M cycles off level loading

Post by Artcfox »

It's tracks[0] and tracks[1], and if I don't call WaitVsync(1) as part of the loop, everything just turns black and freezes. So does that prove that the kernel won't in fact interrupt user-code to render a frame?

This seems to work:

Code: Select all

    // Wait until all sound effects have stopped playing to avoid sound glitches
    while ((tracks[0].flags | tracks[1].flags) & TRACK_FLAGS_PLAYING)
      WaitVsync(1);

    SetRenderingParameters(262 - 80, 80);

    levelOffset = LoadLevel(currentLevel, &theme, &treasuresLeft, &timeBonus);

    SetRenderingParameters(FIRST_RENDER_LINE, FRAME_LINES);
I think that I might still steer clear of doing it this way, because after that last fiasco I had with the TriggerFx function, we're not really friends anymore, and I'd rather not have to look at its flags every time I load a level.
Post Reply