Right, it is functionally equivalent. If you do not use WaitVsync(1) anywhere in your code, it might be ok to add the buffering code just that. Most code that I ever see will have some part where there are some few frame delays using WaitVsync(x) that doesn't make it back to the main loop each frame. In any case it does not matter how it happens, just that the SD music buffering code gets a chance to run every frame;just seems easier to use WaitVsync() IMO, but I do not know what your code is like.nicksen782 wrote:Code: Select all
while (!GetVsyncFlag()) {} ClearVsyncFlag();
Yes they do the same thing, and this tight loop is indeed happening during user code...which will eventually get interrupted, the frame drawn, then the vsync flag set by the kernel. This code will resume, and of course when it does the flag was just set before the frame was drawn(but after the user code was interrupted to do so).nicksen782 wrote:Both of these would wait until the next vsync, right?
Yes, I think it is the only reasonable way to juggle SD music and also arbitrary other uses you might require. It could be done in assembly, but not cycle counted form since it is unknown what delay it will be, so it would need to happen after frame rendering anyway. Most of it is variable length waiting, so I think there is no need for assembly.nicksen782 wrote:You are suggesting reading from the SD card while in the user-code portion, right?
It is always deterministic but it is not possible to assign one value to answer this, because each frame is going to take a different amount of time. From collision detection, logic branches, ram tile blitting on/off tile boundaries, music, etc. there are a whole host of things that happen in a frame that make each one have much different timing. Of course it doesn't matter so long as you know the worst case never takes too long, which is what basically all Uzebox games do. The amount of time spent waiting is how many cycles you didn't use out of the full user code time which always changes. Vsync always happens at exactly the same time, but it is based on how many lines you are drawing, etc. I don't remember off the top of my head the exact cycle count, but that interrupt should go off roughly 28,686868/60 = ~478,114 cycles of which each scanline takes IIRC 1820 cycles. So if you run 200 scanlines tall, it takes 364,000 cycles away from user code. Also even during user code when the frame is not being drawn, user code will repeatedly get interrupted(but you wont even notice) by the hsync that continues to drive audio even for "invisible scanlines". To clarify, user code is always the same amount of time each frame, but waiting for vsync is always different depending on how many cycles got used.nicksen782 wrote:My question is how long of a wait is it?
In the pseudo code above I showed a modified song player that should basically work. It is ram based so you save a cycle here and there on LD vs. LPM, but this is unfortunately very insignificant to the large amount of cycles that you will be waiting for the SD card inter sector delay. This delay depends on the card, but in all cases SD music will absolutely be slower than the flash music player. Basically the custom WaitVsync() is trying to claw back those extra cycles that go to waste, and hoping that happens often enough, that there is no real performance penalty to the main program and the music never stalls.nicksen782 wrote:So, you've discussed when to fill the buffer. What about how to get the sound engine to read from that buffer instead of progmem? This buffer would be ram-based and therefore (correct me if I am wrong) would take less cycles/time because of reducing the progmem reads. Does that mean that we could gain some time with the modifications to the sound engine? How much time?...
Having the separation allows more flexibility in how it can be used. My early experiment did not do this, and it was unusable. The player code needs to run every frame like it does normally to keep the music going, and there is no sense in manually controlling that through the program because it is needless complexity added to anything that wait for a frame to end. Having the buffer part separate is just to keep it light weight, as it will be in a tight loop waiting on vsync(we want it to stop when it happens, so we do not eat too far into the next frame) and potentially the state machine could run several times per frame or no times a frame, where the song player will always run once.nicksen782 wrote:Any reason not to put the SD card read portion in there so it is automatic?
Let me get back to you on that, as to be honest I have forgotten which one I even use for what. For controllers I use PostVsync so I think timers would be good there also. For graphics modifications I think I had to use Pre?..but it is very specific to what you are doing. If you are doing sprite recoloring from sprites blit by the kernel like Alter Ego did(BTW do not do this, it is very inefficient and the wrong way) it does indeed matter which one...basically if it works for your purpose then it works, but certain things that need to happen after different kernel functions you need to use the appropriate one. Even using the wrong one to read joypads could potentially introduce a 1 frame delay in player response for no reason.nicksen782 wrote:Another question, there is the pre and the post vsync call back. Why might you choose to use one vs the other? I'm using the post vsync callback to increment some counters that I use as simple timers (such as when the title screen triforce should change color or how long a character should flash after being hit.) Is that appropriate?
Hope that made any sort of sense, it is sort of difficult to explain because it involves so much of the Uzebox kernel.