Comments (20)
I would personally recommend using the V-total reg for this. V-total is PDC regs + 0x24. I had heard that a few people had issues with H-total. Here is another example. In this case i'm adjusting the value every frame to perfectly synchronize with GBA output.
https://github.com/profi200/libn3ds/blob/master/source/arm11/drivers/lgyfb.c#L42
from red-viper.
The holy grail! That would be amazing. I thought it was impossible on the 3DS hardware. VB doomed to be locked to 50fps on a 60Hz refresh.
Framepacing in games like Jack Bros is stuttery compared to when I play it on a 50Hz panel. Galactic Pinball is even more stuttery compared to how smooth it should look.
Wario plays fine enough, Clash even more so with the lack of scrolling backgrounds, it totally depends on scrolling background and animation speeds in the game if it'll look okay on a refresh mismatch.
But proper 50Hz would be so nice, it's a lot more apparent if you're already used to the buttery smooth motion of native refresh. It's one of the, once you notice you never can unnotice, type of things.
from red-viper.
Also see the VTotal reg below for a formula to calculate the refresh rate with the current settings:
https://www.3dbrew.org/wiki/GPU/External_Registers#LCD_Source_Framebuffer_Setup
from red-viper.
@asiekierka told me about this a couple days after the initial release. I tried implementing it, but couldn't get it to work: it still ran at 60Hz, and exiting the emulator crashed my 3DS.
I'd love to see this working though! If any of you wanna see if you can get it working you're more than welcome to, otherwise I'll get to it whenever I get to it.
from red-viper.
Does the emu synchronize with VBlank or with audio? If audio then of course it will still run at whatever the audio rate is.
And a little tip. With the whole apt hook cookie stuff shown in the commit vaguerant linked i would just backup the whole register instead of assuming what the register was set to on app launch.
edit:
Here comes a simple example that shows how it works. If you set VTotal to about 2476 you get 10 fps. It's ridiculous to see how slow HOME menu runs. The fps printed also updates very slow and you can just about see how the LCD draws the pixels. However something often resets VTotal so it will go back to normal.
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <3ds.h>
int main(int argc, char* argv[])
{
gfxInitDefault();
consoleInit(GFX_TOP, NULL);
u32 vtotal;
Result res = GSPGPU_ReadHWRegs(0x400424, &vtotal, 4);
printf("VTotal test: 0x%" PRIX32 "\nUp: + 1, Left: + 10, Down: - 1, Right: - 10\n", res);
u64 startTicks = 0;
u32 frameCount = 0;
bool update = true;
while (aptMainLoop())
{
if(frameCount == 0) startTicks = svcGetSystemTick();
gspWaitForVBlank();
frameCount++;
if(frameCount == 60)
{
const u64 ticks = svcGetSystemTick() - startTicks;
printf("\x1b[5;1H%f fps ", (double)268111856 * 60u / ticks);
frameCount = 0;
}
gfxFlushBuffers();
gfxSwapBuffers();
hidScanInput();
// Your code goes here
u32 kDown = hidKeysDown();
if (kDown & KEY_START)
break; // break in order to return to hbmenu
else if(kDown & KEY_DUP)
{
vtotal++;
update = true;
}
else if(kDown & KEY_DLEFT)
{
vtotal += 10;
update = true;
}
else if(kDown & KEY_DDOWN)
{
vtotal--;
update = true;
}
else if(kDown & KEY_DRIGHT)
{
vtotal -= 10;
update = true;
}
if(update)
{
update = false;
vtotal &= 0xFFFu; // VTotal is 12 bits.
GSPGPU_WriteHWRegs(0x400424, &vtotal, 4);
printf("\x1b[4;1HVTotal: %" PRIu32 " ", vtotal);
}
}
gfxExit();
return 0;
}
from red-viper.
It's currently on a hardware 20ms timer, but when I tested this I largely copied the atari800 implementation, including syncing on vblank and storing the initial register value.
While I haven't gotten around to testing your snippet yet, I did try the atari800 emulator at the time, and it seemed to work.
from red-viper.
As mentioned above you probably also have to backup/restore the old value each time the app is going into background. Keywords sleep mode, HOME menu, app close, power button press. I assume this can all be done via apt hooks. Most of the software expects 59.8 fps but i don't think it would cause crashes anywhere.
I don't know the exact frame rate of the Virtual Boy. It's unlikely that you will get a perfect match with VTotal alone meaning you could always try the approach open_agb_firm uses picking the 2 VTotal values that give the closest fps to VB and then switching between them on a frame by frame basis. This is absolutely invisible to your eyes and never failed me so far.
from red-viper.
My backlight PR #48 does some APT hook stuff if you need an example that already works in Red Viper.
from red-viper.
Found out why things started glitching out in my previous attempt. All the existing calculations work when 3D is disabled, in which case the resting VTotal is 413. When 3D is enabled (i.e. gfxSet3D(true)
, not the slider), the resting VTotal doubles to 827 (the value actually used is VTotal+1 which explains the off-by-one). This only seems to happen after rendering a frame in citro3d and waiting for vblank.
I need to verify this on other 3DS models (especially 2DS models) before implementing it, but this is an important find.
from red-viper.
On 2DS 3D and wide modes don't work at all. They will display incorrectly.
Also yeah, that value doubles. I should have mentioned it: https://github.com/profi200/libn3ds/blob/master/include/arm11/drivers/pdc_presets.h#L109
from red-viper.
I wonder how this interacts with capture cards. I see the most recent commit in libn3ds relates to those, but that at least is still roughly 60Hz.
from red-viper.
Got someone to test this on a 2DS and made interesting observations:
- Both VTotals read as 413 by default, even after gfxSet3D(true)
- Leaving the VTotal sets unchanged from the regular 3DS version has the game run at full speed but with awful screen tearing, and the home menu runs very slowly
- The order in which the VTotals are set is not significant
- Setting both VTotals to the 2D speed results in the effect working as intended, with no tearing or home menu slowdown
from red-viper.
From what i gather the capture cards use a lazy approach by synchronizing to one LCD only and then capturing both frames at the same time. The way i did it previously made the LCDs not run at the same speed so they were not in sync. Certain capture cards didn't like that and showed garbage frames.
The HOME menu slowdown you mentioned comes from HOME menu not handling out of sync LCDs correctly i have been told. It will get VBlank events at entirely different times.
Leaving the v-total sets unchanged from the regular 3DS version has the game run at full speed but with awful screen tearing, and the home menu runs very slowly
That's because setting v-total higher makes it slower. And gsp likely refuses to turn on the pixel doubling mode on 2DS. So you effectively only slow down the LCD.
from red-viper.
I'll try to clear up any confusion.
2DS is the only known 3DS with 1:1 pixels (as opposed to the 2:1 pixels, where two pixels on top of eachother form a 1:1 pseudo-pixel in 2D mode), so that's why the image appears stretched when 3D is enabled. In fact, it's non-2DS that stretches the image, so it doesn't appear squished.
It does actually matter when and how you change VTotal. Not only do SDK programs expect both VSync to happen at the same time, but 2DS screen actually glitches out if the two screens are way too desynchronized. Not only that, but when the VTotal write happens also matters, as I heard that IPS screens are notoriously sensitive for timing "glitches".
Preferably you would wait until the sync pulse is done, and then set VTotal. This would ensure that you have the most amount of IPC latency available, and won't accidentally race something nasty. The easiest way would be to check if previous VCount is bigger than current VCount (so it overflowed to 0 or so, but due to IPC latency you might not even see the value 0, just that previous > current).
Note that I said VCount, not VTotal. VCount is like LY in Game Boy, or VCOUNT on DS, except here it doesn't seem to be writable.
As for 3D switching, wait for gsp to switch 3D mode, then switch the timings using the method above.
DO NOT use clock doubling on 2DS, even if you manage to somehow bypass gsp in this regard. If you really need fake 3D mode, you can use AB interlacing, use a stride size that is double of the real stide size, adjust the 2nd buffer's framebuffer pointer by one real stride's worth, and you've got 2D mode interlacing. So basically what gsp does in 3D mode, except we have to do the stride and pointer nonsense, so it works on 2DS in particular. Although not sure you want to do this, but this is an option.
If there are any questions, please ping me with the questions, I'm more than happy to answer 😃
from red-viper.
I implemented the thing where you wait for VCount to decrease, but it seems to break when you press the power button and return to home menu. For some reason, when closing the software in this way, VCount never changes. I made it abort if it takes longer than 20ms as a workaround, but is there a better way to deal with that?
from red-viper.
Not sure why your aptHookCookie doesn't work as expected (source/3ds/gui_hard.c:aptBacklight
), there is probably a race condition somewhere?
As for LCDs getting corrupted, there should be a way to force gsp to re-synchronize the LCDs.
You should also wait for both top and bottom to have VCount overflow (sadly the logic for that is more difficult), and then write both regs. As it currently stands, it could be possible that the bottom screen's VCount did not overflow yet (pretty sure gsp turns on the top screen first), and that could also cause corruption.
It could be possible that you measure which LCD got turned on 2nd (pretty sure it's the bottom one), and synchronize to that instead. Optionally, you could just synchronize to the bottom LCD instead, and hope that the bottom one is a bit lagging behind.
Alternatively, you could synchronize twice after toggling VTotal, just for good luck, to avoid any race conditions possible in gsp and such.
from red-viper.
Found out that if I write to VTotal lazily (i.e. don't write when the state doesn't change), the previously mentioned bug is fixed. I will do the bottom screen thing though, it makes sense.
from red-viper.
Changing the vcount wait to the other screen doesn't seem to have made any difference. Screens still get out of sync sometimes. Is gspWaitForVBlank() not sufficient for what you're trying to accomplish? Simply removing the vcount wait makes it work 100% of the time for me.
With that fixed, lazy vtotal write isn't really necessary either (though harmless).
I'd suggest getting rid of both things. In other words, waitForVblank was all you needed to do.
from red-viper.
Thanks for getting in touch! I'll probably keep the lazy write, but if removing the vcount wait helps I'll do that.
from red-viper.
Is this about capture cards? Because i had to make changes recently as well. None of the capture cards on the market handle the situation well when the LCDs don't stay synchronized down to the exact same output cycle. The solution for me was to set the LCD timings of both LCDs at the same time.
from red-viper.
Related Issues (20)
- Crash when launching VB Wario Land HOT 2
- The reset button is too easy to accidentally press HOT 1
- Fully customizable controls? HOT 1
- Locomotive games (SD Gundam, V-Tetris, Virtual Fishing) have incorrect colors HOT 4
- LED HOT 4
- Support executing code from RAM HOT 1
- Language Support
- Anaglyph mode HOT 1
- [Suggestion] - Color Accuracy fix HOT 3
- [Suggestion] Put the savestates in the 3ds folder HOT 2
- Home screen banner suggestion
- n3DS ZL/ZR alternate toggle use suggestion HOT 1
- Move rv_config.ini into config folder
- Circle Pad Pro support HOT 6
- [Suggestion] Frame/flicker blend? HOT 1
- [Feature request] Android port HOT 2
- Jack Bros. freezing after credits
- All roms crash the 3ds; Can't play anything
- [Suggestion] Toggle face buttons/right dpad with another button instead of the touch screen
- Red viper crashing in retroarch citra core
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from red-viper.