alekmaul / pvsneslib

PVSnesLib : A small, open and free development kit for the Nintendo SNES
MIT License
836 stars 73 forks source link

scrolling background #11

Closed odelot closed 3 years ago

odelot commented 6 years ago

Hi! Nice work you have done! congrats.

I am porting SoR2 from Genesis to study how to make a beat n up. I already saw that I can't copy more than 10 lines of background without wait for another vblank when scrolling. That's why the mario game just have a 16 line background right?

Is this limitation caused by the use of C instead assembly?

I noticed that DMA is very fast, the problem is that we cannot use DMA to load the second screen in just one call, we have to make 32 call for each line becuase of how the map is organized.

Well, I am thinking about load 2 screens in the memory (2 x 256 of wide) and stream the rest when parts of the backgound are not visible. Is this the way to go?

Thank you again for your work (sorry for post this question here, I tried the forum but it is blocked for new registers)

alekmaul commented 6 years ago

Yeah, C is slow and bad with current compiler. This is also the reason why a python script is ran after C->ASM. Concerning DMA, the best choice is to use a buffer to put what you need and then, doing a DMA transfert (this is what I do in VBlank).

odelot commented 6 years ago

Do you mean a buffer for the tilemap the size of the screen ? How do I copy to buffer, is it using memcpy?

It is funny because if it was a game with vertical scrolling the tilemap would be already align with the vram. It would be very fast to copy a line then a column of tilemaps.

When I was using DMA copy of a entire tile column (28 calls of DMA copys of 2 bytes) without handling waitForVBlank it justs copys about 10 lines. What happen when we get a interrupt from another vblack to ignore the other DMA calls?

Is there a way to know if a DMA call worked?

Sorry for all the questions. Any help or direction you provide will help me ^.^ thanks

alekmaul commented 6 years ago

sorry, forgot to reply :( ... Yes, can use memcpy. Regarding DMA, you need to be close to a VBL, or the DMA will not work (the best is to copy inside the VBL, as I did for default VBL function). I don't know if there is a way to know if a DMA worked or not, sorry.

odelot commented 6 years ago

thanks @alekmaul! I think I figured it out. I changed the gfx2snes to create tiles maps like it was a 32x64 screen mode.

Now I could scroll 3 screens to the right. \o/ Test rom is here -> https://drive.google.com/open?id=1VPMaYUCjRdswkUE0WFZEhYCFofL9jHOm

Thank you by the reply and by this awesome set of tools

odelot commented 6 years ago

hahaha I celebrated too soon. I noticed I was refreshing the character sprites with oamInitGfxSet that waits for vblank. If in the future I put some opponents and use the same function, I would have a lot of waits for vblank to draw a single frame.

Now I am using dmaCopyVram to update this as well. Strange is that if I put this code inside the function called by the vblank interruption (together with oamUpdate) the sprite becomes to blink.

well, next step is load two opponents and see if it is still possible to keep a decent frame rate. Probably will implement a queue to update a frame of animation of each character on screen per frame. Each 64x64 character are 4k and I can upload about 6k of data by DMA per vblank. Maybe it will work.

I think I will try nesdev forum to share my experience.

alekmaul commented 6 years ago

oamInitGfxSet is not to use for such refresh. Take a look at https://github.com/alekmaul/pvsneslib/tree/master/snes-examples/graphics/Sprites/DynamicSprite to see how to refresh sprites easily.

odelot commented 6 years ago

@alekmaul perfect! this is what I planned to do, create a queue, and with a mutex to avoid handle the queue during vblank. I was thinking about put the background tile map refresh together in the queue (with top priority because of the scroll) and maybe update the OAM array just if needed and copying just the necessary portion of it.

Thank you again for your feedback.

odelot commented 6 years ago

@alekmaul I follow your hint and it is working great! two backgrounds and the sprites being update in the vblank interruption using a queue.

I have another simple question, but regarding to sound. I converted a IT file and it generates two banks. The audio engine swap between audio banks automatically?

Thank you (if you want to see how the work is going, here is the ROM https://drive.google.com/drive/u/1/folders/1VPMaYUCjRdswkUE0WFZEhYCFofL9jHOm)

alekmaul commented 6 years ago

Yes , you must declare the banks consecutives as in this example if your raw soundbank is 3 times 32768 bytes : ` ; // 82 524 bytes -> 32 768 + 32 768 + 16988

.bank 5 .section "SOUNDBANK" ; need dedicated bank(s) SOUNDBANK: .incbin "SoundData.bnk" read $8000 .ends

.bank 6 .section "SOUNDBANK1" ; need dedicated bank(s) SOUNDBANK1: .incbin "SoundData.bnk" skip $8000 read $8000 .ends

.bank 7 .section "SOUNDBANK2" ; need dedicated bank(s) SOUNDBANK2: .incbin "SoundData.bnk" skip $10000 .ends `

I have a 404 not found for the link ;)

odelot commented 6 years ago

hi @alekmaul, here it is the working link, sorry https://drive.google.com/drive/u/1/folders/1VPMaYUCjRdswkUE0WFZEhYCFofL9jHOm

(I made a test with SoR4 sprites shared today by lizardcube to see how it would be displayed in a 16bit game rs you will find this in the link above)

a snes song can have a max of 64kb right? mine has 48000, that's why it used two banks. I was just wondering if I had to explicitly change from __SOUNDBANK_0 to __SOUNDBANK_1. but if it is automatic, it is better.

sometimes part of the song becomes strange, maybe because data.asm banks mess the sounds bank. does it make any sense?

I struggled a little to understand how to use more than one sprite, but now I got it. I am very excited. Thank you for the helpful info. Hope it doesn't anoy you.

alekmaul commented 6 years ago

Nope, it doesn't anoy me ;) and can be helpful for other people. Regarding sound, you need to explicitly use __SOUNDBANK_XXX and when you want to init sound, you need to do that (I really need to change music example with such explanation ...)

in your init sound file (for two banks only, can be same for more banks) : extern char SOUNDBANK0, SOUNDBANK1;

then in the init function (yes, reverse order) :

spcSetBank(&__SOUNDBANK__1);
spcSetBank(&__SOUNDBANK__0);

You can use HUGE size, even more 64K, as you cut each part in a bank size (32k), this is the reason why I put here an example for last bank, less than 32K. For example, in Sydney Hunter, i'm using more than 240K of music, cut from bank 5 to bank 12.

Regarding strange sound, it is because your samples in your IT files must respect some constraints, take a look of pvsneslib_snesmod.txt in pvsneslib and also some advices from my friend Kungfu Furby who did some wonderful musics for some of my games : Here's what went wrong for the songs you attempted to convert this time:

I use Schism Tracker to perform the job of loop point analysis and loop point adjustments since I can just simply type in the numbers.

I use a calculator to take care of loop point problems simply related to the sample being at the wrong sample rate to have loop point lengths divisible by 16 (the length of the looping portion of the sample should at the very least be divisible by 16). I usually perform this on samples with shorter loop point lengths. I don't think it works so well on ones with longer loop point lengths, mainly because by then you're probably not dealing with simple waveforms as loops.

Using the Bass & Lead sample as an example...

Loop point is currently defined as... Start: 3213 End: 3382

That's a loop length of 169.

I like using powers of 2 for my loop points so that if I have to decrease the quality of the sample, then I can do so as painlessly as possible to the sample (unless I find the degraded quality to be a bad idea), so that means 128 is the value I use here.

Divide 169 by 128 gets you an unusual decimal number... copy that number.

Now get the full length of the sample (that's 3383 for this sample) and divide by that decimal number you acquired earlier (169/128). You'll most likely get another unusual decimal number. Round that off and there's your new length that you will resize the sample to.

I use Alt-E in Schism Tracker to perform the resize sample command. The program will ask for the new size of the sample.

Now you should have a loop length that is divisible by 16. You can perfect the loop point by adjusting them so that the loop point themselves are divisible by 16.

You'd have to duplicate the instruments and then enter the sample ID for all of those notes... and then you have to redefine the instrument IDs depending on the pitch from the old note table. Yeah...

For the one song that has this problem, I usually resize the sample and make it half of its length... however, I may have to make additional adjustments depending on how the loop points are holding up (length may or may not be involved, although usually I'm checking the loop points themselves to make sure that they are divisible by 32 or some higher power of 16... this indicates how many times you can cut the sample in half).

One of these songs is the most visibly affected by this problem, and that's because SNESMod doesn't virtually allocate channels. You have to modify the patterns so that the note off commands go where the note would originally play, and the new note is put on another channel.

KungFuFurby commented 6 years ago

WLA-DX has support for a kind of section that WLALink will not drop later on if it has no references to it, citing line 2180 of https://github.com/alekmaul/pvsneslib/blob/master/pvsneslib/pvsneslib_wla-dx-readme.txt . I think it may be useful in this case if you don't want to call spcSetBank for each ROM bank of your soundbank.

odelot commented 5 years ago

Hi @alekmaul! thank you for the reply. I sent all your tips to my friend that tracked the "go straight" to the SNES for me.

I think I will create some examples to with things I have difficulties to understand and send to you by pull requests to help people facing the same problems I did. I am just concerned about the sprites I will use in the examples, maybe I will use simplified (b&w) sprites from SoR.

I think I will make an example with mode1 and three backgrounds, using the BG3_MODE1_PRORITY_HIGH mode and scrolling BG1 and BG2.

The way I organized the scrolling made me create another option in gfx2snes (create maps like it was in the 64x32 option, but for wider images). I think I will also implement a way to put a offset when creating tileMaps, then I can use the same memory spaces for 2 small sets of background tiles.

I am now creating the collision code and I am using it to swap sprites in the OAM table for priority. I created a code to compute the optimal boundery box analysing the sprite information, but it took 15 seconds for snes to precompute the BB of seventeen 64x64 sprites haha probably I will use a default size or precompute it during the compilation time.

I am wondering if it is better to keep a ordenated Y list instead of test collisions between every characters to control the sprite priority. It is hard to keep track of how much of CPU I am using. I was thinking to use this script https://www.youtube.com/watch?v=tD6fCPPmBMA but I don't know how to get the code ROM address from the end of vblank and the end of the main loop.

I am still very excited coding for SNES. thank you for this great library that makes me code without the use of assembly (but always with the fear of the leck of cpu power rs)

hoit commented 3 years ago

Hi, i think the base question was solved and you provided sample to pvsneslib. You shared your code here : https://github.com/alekmaul/pvsneslib/issues/44

Do you confirm that we can close this issue ? Thanks in advance

alekmaul commented 3 years ago

yep, we can close