gonetz / GLideN64

A new generation, open-source graphics plugin for N64 emulators.
Other
777 stars 180 forks source link

Framebuffer Emulation is broken on Raspberry Pi (OpenGL Driver) #1084

Open AlessandroPorcelli91 opened 8 years ago

AlessandroPorcelli91 commented 8 years ago

The game starts beautifully and runs great, though in about half an hour the textures corrupt in a very specific way: wherever the objects are (eg: Link, a tree, a NPC etc.) the terrain becomes transparent, and the transparency stays for as long as the emulator is running effectively making the game unplayable. I am currently running configuration version 12 according to mupen64plus.cfg. What can I do? Thanks.

loganmc10 commented 8 years ago

Do you have Framebuffer Emulation enabled?

AlessandroPorcelli91 commented 8 years ago

Thanks for responding. Yes I do, is that broken in the current build?

loganmc10 commented 8 years ago

If you look inside https://github.com/gonetz/GLideN64/blob/master/src/FrameBuffer.cpp you'll see 3 instances of glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); wrapped inside #ifdef VC (VC means Raspberry Pi)

Basically there is some kind of bug with the Raspberry Pi GL driver, and I've done my best to minimize the issue (everything was transparent all the time without those changes), but it still seems to pop up every now and then.

I haven't been able to figure out a better way to fix the issue. If you disable Framebuffer Emulation you won't run into this issue (but some things won't render properly, like the pause menu in Zelda: OOT)

loganmc10 commented 8 years ago

Alternatively, you can just save your state when it happens, exit the game and reload it until the next time it happens...

AlessandroPorcelli91 commented 8 years ago

I see, I guess the problem must be somewhere else...That is rather unfortunate, I liked the FBEmulation and this plugin runs smoother than the others too, in my experience. Adding more instances would be too cumbersome, wouldn it? :D

I could test whether gles2rice suffers from the same problem with FBEmu enabled...Do you happen to know which option enables it for OOT 1.0 (U)?

loganmc10 commented 8 years ago

I only use this plugin, so I'm not sure about options for gles2rice. Even if you could get it to work in gles2rice it wouldn't bring us any closer to fixing it in this plugin.

AlessandroPorcelli91 commented 8 years ago

I have no experience in coding, I just thought that if other plugins emulate framebuffer in a completely different way and the texture problem still does not vanish for them, then perhaps we would know that it's unfixable, and that is indeed something the driver is handling poorly. Anyway, thanks a lot for replying quickly :)

AlessandroPorcelli91 commented 8 years ago

I'd like to check if further iterations of glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); considerably lower the probability of the glitch happening. What are you using to compile GlideN64?

loganmc10 commented 8 years ago

I follow the instructions here: https://github.com/gonetz/GLideN64/tree/master/projects/cmake

For me it basically boils down to this:

cd src
./getRevision.sh
cd ../projects/cmake
cmake -DMUPENPLUSAPI=On -DNOHQ=On -DVEC4_OPT=On -DNEON_OPT=On ../../src/
make -j4
sudo cp plugin/release/mupen64plus-video-GLideN64.so /usr/local/lib/mupen64plus
AlessandroPorcelli91 commented 8 years ago

Thanks a lot, I shall try to play through the game with a couple more iterations in the code and report if that's significantly better. I am aware this wouldn't be a definitive solution but if the average incidence becomes something like one failure every two hours that could be enough for a long session.

AlessandroPorcelli91 commented 8 years ago

Sorry for double posting again but I feel like a dumb clueless person: in the three instances you clearly perform different operations, so I suppose that just adding another

ifdef VC

        const GLenum discards[]  = {GL_DEPTH_ATTACHMENT};
        glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards);

endif

wouldn't actually accomplish anything, or would it? Sorry if I'm wasting your time.

loganmc10 commented 8 years ago

No I don't think it would make much difference. glDiscardFramebufferEXT is a "hint" to the driver to tell it that it doesn't need to preserve the contents of GL_DEPTH_ATTACHMENT (the depth information) after the Framebuffer Object is unbound. It's unclear why this fixes the depth/transparency issues on the Raspberry Pi, but providing that hint twice in a row in the same place probably won't make much of a difference.

It's a hard thing to test because it only crops up after 30 minutes or so like you say. So to test it is basically: Take a shot in the dark as to where to put that hint, play for 30+ minutes to see if it fixes it, repeat....

loganmc10 commented 8 years ago

By the way it's not a waste of my time, I enjoy working on this kind of thing. I probably spend more time trying to improve N64 emulation on my devices than I do actually playing N64 games lol

AlessandroPorcelli91 commented 8 years ago

LOL, I know right? I have finished LOZ:OOT enough times in my life, I mostly hunt emulation bugs now. Except I can't actually fix them... Would it be useful if I found some locations in which the issue is more likely to happen than others? For example I could stand still and let it run for a while to see if it triggers anyway, or whatever.

loganmc10 commented 8 years ago

If you could fine a situation where it was reproducible, and provide a save state right before it happens, yes that would be extremely useful. It would need to be a situation where I wouldn't have to wait long for it to happen, I can't wait for 30 minutes each time I try something different in the code.

loganmc10 commented 8 years ago

Also for a shameless plug:

You can try https://github.com/loganmc10/GLupeN64

That is a libretro port that I am currently working on that uses the latest GLideN64 code.

Building instructions are here: https://github.com/loganmc10/GLupeN64/blob/master/BUILDING.md

You could see if that gives you any better results than standalone mupen64plus

gizmo98 commented 8 years ago

It would be interesting to see if this problem only occurs on a rpi3. Even without OC the SOC gets really hot after a while. After rpi3 was released i suffered temperature related hangs and crashs. The current firmware seems to be a little bit better (throttling?). If i have some time i will do a test with a rpi2.

AlessandroPorcelli91 commented 8 years ago

I can give it a try later, I have to compile a custom version of libretro mupen64plus core because libretro does not allow to change the AnalogPeak value, and my controller is so sad it needs the default 32768 value to be lowered. I shall give any feedback ASAP.

@gizmo98 Though I have not installed an active cooling solution, according to vcgencmd the temperature was about 65 degrees Celsius after the glitch occured.

AlessandroPorcelli91 commented 8 years ago

Ok, I played with a huge and noisy fan blowing on the RPi 3 keeping it at about 47 °C, and the issue happened anyway.

I am also trying to find some kind of pattern here, I have experienced three failures at very different times (yes, I am using a chronometer XD). One after barely 30 seconds of getting into the game, at Dodongo Cavern entrance, without moving.

Another one happened, again, in Dodongo Cavern after just two minutes: After standing still for the first minute I threw a bomb flower, whose explosion seemingly triggered the glitch.

The third instance was in Kakariko Village after 18 minutes of gameplay, in a spot that also triggered the glitch before.

If I have to take a wild guess, this seems to be correlated with the loading/unloading of some object in a map (the exploding bomb flower one was fishy), but I can't seem to consistently reproduce it. I had a savestate literally 5 seconds before the corruption happened but loading it back after restarting does not do anything, so I am afraid savestates won't help. Since the times ranged from less than 30 seconds to more than 50 minutes this can't even be correlated with lack of memory of some kind. Perhaps I have found something else which may or may not be correlated to this, I shall p The Glitch Gremlin.zip ost the screenshots here as soon as I can...A cutscene has been running on my Pi for the last hour (0.3 FPS) with some graphical madness going on.

UPDATE: I have uploaded a zip file with some pictures. The first four happened while running around, the huge chunk of creepypasta madness in the middle is what happened during the cutscene. All the textures are swapped around with the models (except link's, strangely). The last pictures are what was left after the cutscene, which took over an hour to finish due to the incredible slowdown. Notice that textures from the HUD are duplicated within the transparent frames. The persistence of the frames from the cutscene make me think that is the same issue. I have also included a savestate taken during the cutscene, but chances are nothing strange happens loading it.

loganmc10 commented 8 years ago

@AlessandroPorcelli91 when you get a chance you should try out the latest code from GLupeN64 (https://github.com/loganmc10/GLupeN64)

I just added a commit that does a glDiscardFramebufferEXT before every glBindFramebuffer. Its supposed to be good for performance (https://community.arm.com/groups/arm-mali-graphics/blog/2014/04/28/mali-graphics-performance-2-how-to-correctly-handle-framebuffers) and may solve this problem as well.

AlessandroPorcelli91 commented 8 years ago

Awesome, I shall try it ASAP and let you know :)

AlessandroPorcelli91 commented 8 years ago

I have one question first though, due to GLupeN64 being based on libretro, access to mupen64plus.cfg and InputAutoCfg.ini is disabled, correct? If that's the case I'll have to change a couple values in emulate_game_controller_via_libretro.c in order to make it work with my half assed controller.

loganmc10 commented 8 years ago

I guess, controller configuration is controlled though RetroArch, so yes there is no mupen64plus.cfg or anything

AlessandroPorcelli91 commented 8 years ago

@loganmc10 Alright, I managed to get GLupeN64 up and running and I'm now going to run around for a while to check wether the bug is still there, in the meanwhile though I'd like to report that I'm not able to play at anything higher than 320x240, because the framerate drops dramatically if I change the GLupeN64 resolution option in the retroarch emulators configuration file. Even SM64 becomes pretty much unplayable, why could this happen? Is there some advanced trick enabled by default? By comparison, I get a similar performance at 800x600 in vanilla. Also, the pause screen takes quite a bit to come up, but I guess that's a drawback for the FBEmulation fix.

loganmc10 commented 8 years ago

@AlessandroPorcelli91 thanks for testing it out. I don't think the performance difference is because of this potential fix for Framebuffer Emulation. glDiscardFramebufferEXT should never hurt performance, it can only help it.

RetroArch handles OpenGL frames differently than mupen64plus, so behind the scenes things are working a bit differently. To be honest I haven't really tested it higher than 320x240, although when I test higher resolutions on my phone with GLupeN64 it seems to work fine (this is with Framebuffer Emulation disabled however, it doesn't work on Android right now).

I'll test higher resolutions on my Raspberry Pi over the weekend to see if there are any improvements I could make.

AlessandroPorcelli91 commented 8 years ago

@loganmc10 I should have tried fiddling around with the configuration files a bit more indeed, I shall compare the default settings with the ones I am running in vanilla for the plugin, though some are not accessible in libretro. I have been running the game for about one hour and nothing bad happened, therefore I am rather confident whatever you did most certainly fix the problem. Would it be messy to implement them in the main branch?

loganmc10 commented 8 years ago

It would be a little tricky to implement directly in GLideN64 for technical reasons. glDiscardFramebufferEXT needs to be executed right before glBindFramebuffer. However, glDiscardFramebufferEXT cannot be used when the default framebuffer is bound (framebuffer "0"). The problem is that you don't really know what framebuffer is currently bound if you call glDiscardFramebufferEXT right before glBindFramebuffer.

RetroArch doesn't allow you to bind to framebuffer 0, and GLupeN64 intercepts the GL calls to make sure it doesn't happen, for instance, when GLideN64 does "glBindFramebuffer", this is the code that is actually executed in GLupeN64: https://github.com/loganmc10/GLupeN64/blob/master/custom/glsm/glsm.c#L1580-L1596

Anyway, because in RetroArch/libretro I can be confident that framebuffer "0" is never bound, it is safe to always run glDiscardFramebufferEXT before glBindFramebuffer, that isn't the case with mupen64plus.

AlessandroPorcelli91 commented 8 years ago

@loganmc10 Ok...I might say something stupid now, but keep in mind my knowledge about coding is extremely limited: according to the article on arm.com you posted this morning, Framebuffer "0" gets bounded for every even numbered call of glBindFramebuffer. If this is also the case for the actual application we need, couldn't we keep count of the number of calls and branch the code?

loganmc10 commented 8 years ago

@AlessandroPorcelli91 That was just how their example did it. If you look inside https://github.com/gonetz/GLideN64/blob/master/src/FrameBuffer.cpp https://github.com/gonetz/GLideN64/blob/master/src/OpenGL.cpp

You'll see numerous instances of glBindFramebuffer all over the code, sometimes it binds to "0" and sometimes to an object.

There are even some instances like this: https://github.com/gonetz/GLideN64/blob/master/src/OpenGL.cpp#L605 Where it decides between 0 and another value based on the value of a variable, so you can't be sure what the result will be.

There probably is some magical place you could put the glDiscardFramebufferEXT that would solve the problem, but it's almost impossible to tell given how randomly it occurs.

loganmc10 commented 8 years ago

Actually you know, thinking about this, there is a way to do it, so I take that all back.

I'll see if I can create some code for you to try out in a bit.

AlessandroPorcelli91 commented 8 years ago

@loganmc10 Nice! I only took one exam about C basics at the university and my mark wasn't even that great, lol. I'll wait for your take on this and try it asap.

loganmc10 commented 8 years ago

@AlessandroPorcelli91 Ok so try compiling GLideN64 from this source:

git clone -b gldiscard https://github.com/loganmc10/GLideN64.git
AlessandroPorcelli91 commented 8 years ago

@loganmc10 That was fast! I shall compile it as soon as I get home :)

gizmo98 commented 8 years ago

I played around a little bit. Current glupen64 seems to solve glitches in mario party 2 and mario golf. Other glitches persist but i have not seen any regression.

AlessandroPorcelli91 commented 8 years ago

@gizmo98 Have you tried to change the resolution to something higher than 320x240? What configurations are you currently using?

gizmo98 commented 8 years ago

I have not changed resolution but mupen64plus-libretro was always slower than standalone version. I use 320x240 and nativeResFactor=1. Performance suffers a lot if you use resolution higher than 640x480. You should OC the gpu a little bit if you want 640x480 with enjoyable framerates.

AlessandroPorcelli91 commented 8 years ago

@gizmo98 640x480 is actually my default in standalone mupen64plus, I haven't tried much though Zelda runs fine enough (without even overclocking) and lighter games such as SM64 run fairly smooth at even higher resolutions. I am aware that libretro is slower, doesn't GLupeN64 change the way the core is implemented though?

gizmo98 commented 8 years ago

GLupeN64 runs superior compared to mupen64plus-libretro. I also prefer higher resolutions but to prevent slowdowns in certain areas without OC the best setting is 320x240. Hopefully the next pi has a faster gpu.

AlessandroPorcelli91 commented 8 years ago

@gizmo98 I sure expect that to be the case. Since they'd like to keep their hardware open they're waiting for some other open model to be released.

loganmc10 commented 8 years ago

The question is though, does GLupeN64 run slower than mupen64plus on higher resolutions? If that is indeed the case you can open an issue here: https://github.com/loganmc10/GLupeN64/issues

We should probably keep the discussion here around the FB Emulation issues.

AlessandroPorcelli91 commented 8 years ago

Uhm, I am having some problems trying to compile GLideN64 from the source you posted using the commands you gave some posts ago. What commands do I need to run for a compilation on RPi 3?

loganmc10 commented 8 years ago

It should be the exact same:

cd src
./getRevision.sh
cd ../projects/cmake
cmake -DMUPENPLUSAPI=On -DNOHQ=On -DVEC4_OPT=On -DNEON_OPT=On ../../src/
make -j4
sudo cp plugin/release/mupen64plus-video-GLideN64.so /usr/local/lib/mupen64plus

Something like that

AlessandroPorcelli91 commented 8 years ago

I'm getting lots of Error: selected processor does not support ARM mode Error: selected FPU does not support instruction

and other lines aswell, could I be missing some vital packages in cmake?

loganmc10 commented 8 years ago

Are you trying this on the Raspberry Pi or on your computer?

It sounds like you have some CLFAGS or some kind of compiler flag set wrong, have you been playing with the compiler flags?

AlessandroPorcelli91 commented 8 years ago

I'm on my Raspberry Pi...Did I get it wrong?

AlessandroPorcelli91 commented 8 years ago

It basically gets to 8% of the compilation and then it meets with the creator like this

log.txt

loganmc10 commented 8 years ago

I'm not really sure why it's not working, you can remove -DNEON_OPT=On from the cmake options, but these issues aren't related to my code changes.

gizmo98 commented 8 years ago

Remove -DVEC4_OPT=On or add -vfpu=neon CFLAGS.

AlessandroPorcelli91 commented 8 years ago

Yes, I believe I messed with Neon because it was not allowing me to compile mupen64plus actually...How do I add that CFLAG back? (noob me)

AlessandroPorcelli91 commented 8 years ago

Removing -DVEC4_OPT=On and/or -DNEON_OPT=On brings the compilation much further but still not quite to the end. log2.txt