cegli / dolphin

Dolphin is a GameCube/Wii emulator, allowing you to play games for these two platforms on PC, with improvements.
GNU General Public License v2.0
5 stars 2 forks source link

everything up to the difficult merge #8

Closed CarlKenner closed 9 years ago

CarlKenner commented 9 years ago

Everything up to the difficult merge, plus the RachelBryk commit after.

CarlKenner commented 9 years ago

Have you got any idea how to detect when the Rift is set to 60Hz? Is Direct Mode always 75Hz (for DK2)? In extended mode and fullscreen I guess it will be whatever refresh rate we decide (perhaps we should add an option), but in extended mode and windowed, I'm not sure how it works.

When you say a 75 FPS game, you mean a 60 FPS game when you set the Frame Limit to 75 FPS, is that right? And for the other games, what was Frame Limit set to?

The basic idea of my synchronous timewarp is this... The game runs as normal, unaware that frames are being displayed at the refresh rate of 75 Hz using timewarp. But when the game tries to draw a frame, sometimes the draw call returns slightly faster than the game expects (because it only takes a 75th of a second) and sometimes the draw call returns slightly slower than the game expects (because it waits to draw an extra frame or two using timewarp). After every ovrHmd_EndFrame call it calls ShouldAddTimewarpFrame() in a loop, rendering one frame of timewarp every time it returns true. Currently, I'm using a similar method to Dolphin's frameskipping, where I check if the amount of time that has elapsed so far this second is more than the number of 75Hz frames we have drawn, and if it is more then I add an extra frame. I don't think that's a very good method though. It might be better to use some sort of fixed pattern, like detecting the game's frame rate, and if it is 60 FPS then after every 4 frames we add one extra frame, or if it is 30 FPS, then after every frame we add one extra frame, and every 2 frames we add another extra frame. But we also need to handle 50 FPS PAL games and maybe 25 FPS PAL games (do these exist?). And for the consumer version (and Crescent Bay) we need to support 90 Hz refresh rates.

cegli commented 9 years ago

I've wanted to try doing this for a while, but I haven't gotten around to it yet: http://stackoverflow.com/questions/4110731/how-to-get-the-screen-refresh-rate Looks like it would only work for Windows.

I agree on the fixed pattern. I think it's probably the best way to do it with the least overhead. The easiest thing to support in the beginning would be a 1:2 ratio, keeping the Frame Limiter at 75hz. That would at least get 30fps games to 60fps, allowing them to run correctly with the Rift at 60hz, or at 1.25x speed at 75hz.

There are 25fps games. You could use a 2 timewarped to 1 rendered option at that point, but 25fps is really pushing it in VR, even with timewarp.

If those examples above worked properly, we could try something like this for 60fps games (time scale in ms): 0.00 warp 16.66 33.33 49.99 66.66 warp 83.33 99.99 _(60fps) 0.00 13.33 26.66 39.99 53.33 66.66 79.99 93.33 106.66 (75fps)

Might be a bit trickier to get it to line-up, but I haven't really thought too much about how it all works yet.

When I was testing the current timewarp code, I tried both 60fps and 75fps on the frame limiter. There didn't seem to be a big difference between the two.

CarlKenner commented 9 years ago

You can do that fixed 1:2 ratio now... just set both min and max to 1. That means every frame you will get exactly one extra frame after it.

cegli commented 9 years ago

Right, that makes sense! I will try that out and see how it goes.

cegli commented 9 years ago

Great news on the min/max = 1! Running Wind Waker at a frame-limiter of 75fps with a 75hz rift is quite smooth. It's not perfect (almost like the timing with the Vsync is off?), but it is consistent and it seems to have minimized the judder by 90%. I would consider 30fps games very playable now, where before I would definitely not play them at all. There are no dropped frames and the music is smooth too at 4x Internal Resolution. Awesome work!

I think we need to figure out why this isn't 100% smooth, fix that, then try and get it going with the 60fps -> 75fps warp. We should look at the v-sync timing I think.

Also, grab my latest commit to fix a bug in the KBM/XInput code!

CarlKenner commented 9 years ago

Done! Also I merged master. It compiles and runs, but OpenGL currently just shows black because I haven't implemented anything to blit the 3D texture into two 2D textures yet. I'm working on it now.

CarlKenner commented 9 years ago

The timing with the Vsync might be off. I copied the magic numbers from jherico's asynchronous timewarp example, with no understanding of why those numbers are used.

cegli commented 9 years ago

I just made a commit where I butchered your code to get 20fps -> 60fps and 30fps ->60fps working. It seems to work almost perfectly in everything I've tried so far except Wind Waker, where sometimes it starts drawing the next frame on top of the previous frame causing slow downs and corruption. This only happen once every minute or so, but is annoying.

I've added a Vsync Tweak option. Usually it should be left at zero, but I found a slightly higher number can get around the Wind Waker issue while adding a slight amount of buzzing judder. It's a trade-off, but good for debugging right now.

The whole thing is really rough, but it's the first time I've seen the low frame rate games working without judder. I am of course running with a frame-limiter of 75fps, which makes the game run 25% too fast.

I tried for a long time to get 60fps -> 75fps working, but could not get it to work without judder...

CarlKenner commented 9 years ago

The only differences I can see between what you are doing and what I was doing are that you are forcing min and max to be the same, and you are changing the delay. Also you aren't counting the VR FPS, and there is a couple of frames delay when the user clicks the Stop button. Have you tried removing the delay altogether, and just doing beginFrame endFrame?.

Are there 20 FPS games? I thought we needed to support 25, 30, 50, and 60 FPS.

Do you know where dolphin stops and waits for a frame to render? If the graphics card takes a long time to render (on the video thread), the game runs slower (on the other thread), but I can't see where that happens. I think part of the problem trying to render 75 frames in a 60FPS game might be that we need to sometimes take longer to draw a frame than the game's frame rate, causing a few ms gap in the audio. Or it might be that the game sometimes doesn't tell us to draw the next 60Hz frame until after we have missed the 75Hz vsync. Maybe to get it working properly, we will have to change the timings in the core so that frames aren't all the same length. So it would do three VI frames of 13.33ms, then one frame of 26.66ms, instead of four frames of 16.66ms.

cegli commented 9 years ago

For sure waiting for .NextFrameSeconds is the only way to get it work without judder. I tried removing the delay and tried tweaking it live with different values through the GUI, and .NextFrameSeconds with no subtraction or addition is the best (on my computer at least). On most games it actually seems to be about as good as it could get (assuming you want 60fps).

I did find that there are 20fps games. These are usually ports of N64 games, such as Zelda: OoT. I tested the 20fps->60fps pullup with that game, and it works great (smooth headtracking), but the 20fps movement looks pretty choppy as expected.

I tried doing Min 1 and Max 1 on your code, but I was still having problems with judder. It only went away when I made it into a simple "for loop". I think the Min 1 and Max 1 code doesn't always choose the correct path, because I also noticed I had to set it to 0 and 0 to when I was running a game at 75fps to not get judder. If it put 0 and 2, it would sometimes try to inject a frame, when it should never enter the while loop. I didn't spend any time debugging it for now, focusing instead on getting the most basic mode working.

I agree we might have to change some timing in the core for 60fps->75fps. I tried using the time to the next vsync to tell when to inject the frame, but realized that the timing was always off because the frames were always being rendered at 16.66ms. Throwing an extra frame in there wasn't helping that.

I was hoping you could shed some light on how the synchronization works! I was trying to figure that out for a bit last night, because I think tweaking it would fix Wind Waker, but it wasn't immediately obvious. I'll let you know if I find it.