Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3DS] DevilutionX and Hellfire builds #1035

Closed
2 of 3 tasks
MrHuu opened this issue Feb 21, 2021 · 43 comments
Closed
2 of 3 tasks

[3DS] DevilutionX and Hellfire builds #1035

MrHuu opened this issue Feb 21, 2021 · 43 comments

Comments

@MrHuu
Copy link
Contributor

MrHuu commented Feb 21, 2021

It's great to see the 3DS port merged, huge thanks to anyone making it possible.
Current builds are running great on New3DS hardware and already has served me, and hopefully a few others, many hours of fun with this project.

As for future improvements, i was thinking about eventually moving away from SDL for rendering.
This allows easier access to features as wide screen (800x240), possibly 3d and moving menu's, automap and status-bar to the bottom screen.

Feel free to ask questions, or just laugh (or cry) at some of the changes made. And do let me know should any issue arise.

That said, there are a few details which may need attention:

  • Previous 3ds builds are based on having separate executables for diablo and hellfire, including assets.
    Recent commits unifying hellfire results in both executables behaving in a similar fashion.

    Both .cia builds will load hellfire if found.
    The .3dsx builds allow us to pass --diablo as argument, to force running diablo.

Should the hellfire build be removed, including it's assets?

  • The current manual points to outdated releases, not reflecting these changes:
    https://github.com/diasurgical/devilutionX/blob/master/docs/manual/platforms/n3ds.md

    Note: Savefiles created with the old hellfire builds linked to, are not compatible with current builds.

  • I also just noticed, after loading a new hellfire savefile, oils aren't saved / loaded properly.
    The icon remains in inventory, without description. Interacting makes them disappear.

@AJenbo
Copy link
Member

AJenbo commented Feb 21, 2021

A lot happened over the course of the landing of this port, one of the big changes was widescreen support.
Since the 3DS uses SDL1.2 the following build parameters should be all that is needed for setting it to widescreen:

-DSDL1_VIDEO_MODE_WIDTH=400 -DSDL1_VIDEO_MODE_HEIGHT=240

We are actually planing to move more of the rendering to SDL exactly to achieve greater flexibility in regards to where the menus are places etc.

@AJenbo
Copy link
Member

AJenbo commented Feb 21, 2021

The .3dsx builds allow us to pass --diablo as argument, to force running diablo.

If this is reasonable for the user to set then that sounds like a good solution. Or if you can make is so that the builds set it that might be an option too.

@AJenbo
Copy link
Member

AJenbo commented Feb 21, 2021

Note: Savefiles created with the old hellfire builds linked to, are not compatible with current builds.

Is this specific to the 3DS version, or simply the in-development Hellfire support? The end result should be compatible with save games from the original Hellfire, anything in between are considered broken beta versions.

I also just noticed, after loading a new hellfire savefile, oils aren't saved / loaded properly.
The icon remains in inventory, without description. Interacting makes them disappear.

yes this is known and somewhat expected while we iron out the last bits of Hellfire support prior to the next release.
#981

@MrHuu
Copy link
Contributor Author

MrHuu commented Feb 22, 2021

A lot happened over the course of the landing of this port, one of the big changes was widescreen support.
Since the 3DS uses SDL1.2 the following build parameters should be all that is needed for setting it to widescreen:

-DSDL1_VIDEO_MODE_WIDTH=400 -DSDL1_VIDEO_MODE_HEIGHT=240

We are actually planing to move more of the rendering to SDL exactly to achieve greater flexibility in regards to where the menus are places etc.

The current 3ds devilutionx build renders at 800x480. Scaled down to 400x240. Previous attempts to render at lower than diablo's default 640x480, resulted in a loss of quality and none scaling main menu. That's why i ended up having it rendered in widescreen, higher than the default resolution.

It's the 3DS implementation of SDL which lacks a few features. Only video-modes available are rendering to top screen, bottom screen or both using a single SDL screen. Maximum resolution would be 400x240 for top, 400x480 for top and bottom, or 320x480 to match the bottom screens width.

The 3DS has a 800x240 top screen, when rendering 3d its 400x240 for the left and 400x240 for the right eye.

SDL is only able to render to the top left framebuffer.

Without SDL, using libctru or citro3d, we can utilize the top right framebuffer (and bottom). And enable or disable the parallax barrier.

Using the top right framebuffer with sprites drawn with offset, a 3d effect could be created.
Having the parallax layer disabled, allows us to use the full 800x240 resolution.

I was thinking of working towards having a second render buffer for the bottom screen, for in-game menu's and such.
And enable a third for rendering with offset when using 3d.

@AJenbo
Copy link
Member

AJenbo commented Feb 22, 2021

It's the 3DS implementation of SDL which lacks a few features. Only video-modes available are rendering to the top screen, bottom screen, or both using a single SDL screen. The maximum resolution would be 400x240 for top, 400x480 for top and bottom, or 320x480 to match the bottom screens width.

Ok, that makes sense, the port is using SDL1.2 which is limited to one window. Maybe this would be possible with SDL2.

I forgot if SDL2 is available for 3DS, but some work is currently being put into having a hardware-accelerated SDL2 implementation of the UI, and maybe even full render. If that is supported that might offset the additional overhead of SDL2.

Using the top right framebuffer with sprites drawn with offset, a 3d effect could be created.

There isn't much 3D data available in the game, we can tell that when a tile leaves the base area that it must be protruding vertical (walls, etc), but often they actually start rising earlier. So that might give a dissatisfactory effect. Here for illustration, on the right is how the wall should look in 3D, but to the left is the only data that the engine has:

image
Of cause, someone could create the additional data as an additional tile layer, but that could be a lot of work. But we have been talking about doing a similar thing in order to have real-time directional light and shadows.

@spitfire
Copy link
Contributor

It's great to see the 3DS port merged, huge thanks to anyone making it possible.
Current builds are running great on New3DS hardware and already has served me, and hopefully a few others, many hours of fun with this project.

Great work, thanks!

As for future improvements, i was thinking about eventually moving away from SDL for rendering.
This allows easier access to features as wide screen (800x240), possibly 3d and moving menu's, automap and status-bar to the bottom screen.

Feel free to ask questions, or just laugh (or cry) at some of the changes made. And do let me know should any issue arise.

That said, there are a few details which may need attention:

  • Previous 3ds builds are based on having separate executables for diablo and hellfire, including assets.
    Recent commits unifying hellfire results in both executables behaving in a similar fashion.
    Both .cia builds will load hellfire if found.
    The .3dsx builds allow us to pass --diablo as argument, to force running diablo.

Should the hellfire build be removed, including it's assets?

  • The current manual points to outdated releases, not reflecting these changes:
    https://github.com/diasurgical/devilutionX/blob/master/docs/manual/platforms/n3ds.md
    Note: Savefiles created with the old hellfire builds linked to, are not compatible with current builds.
  • I also just noticed, after loading a new hellfire savefile, oils aren't saved / loaded properly.
    The icon remains in inventory, without description. Interacting makes them disappear.

It would also be useful to get the right joystick (Circle Pad?) on the N3DS working like on other platforms (for scrolling map and maybe cursor interaction)

Multiplayer/networking doesn't seem to work just like on Switch (which is probably related) - see #880

@AJenbo
Copy link
Member

AJenbo commented Feb 24, 2021

3DS isn't being built with network support so this is a know missing feature: https://github.com/diasurgical/devilutionX/blob/master/CMake/ctr/n3ds_defs.cmake#L2

@MrHuu
Copy link
Contributor Author

MrHuu commented Feb 24, 2021

I forgot if SDL2 is available for 3DS, but some work is currently being put into having a hardware-accelerated SDL2 implementation of the UI, and maybe even full render. If that is supported that might offset the additional overhead of SDL2.

Unfortunately, SDL2 only has unfinished and outdated implementations for 3DS available for public use.

There isn't much 3D data available in the game, we can tell that when a tile leaves the base area that it must be protruding vertical (walls, etc), but often they actually start rising earlier. So that might give a dissatisfactory effect. Here for illustration, on the right is how the wall should look in 3D, but to the left is the only data that the engine has:

[image]
Of cause, someone could create the additional data as an additional tile layer, but that could be a lot of work. But we have been talking about doing a similar thing in order to have real-time directional light and shadows.

You're right, such occasions would be quite hard to display properly without additional information. Maybe i should just stick with some floating cows.
Another use case for 3d could be to create distance between the automap and gamescreen.

But, the main goal would be 800px support for most 3ds/2ds models and bottom screen support.

It would also be useful to get the right joystick (Circle Pad?) on the N3DS working like on other platforms (for scrolling map and maybe cursor interaction)

Yes, the c-stick should also be utilized, I'll look into that soon. Same as relative positioning for the touchpad.

Multiplayer/networking doesn't seem to work just like on Switch (which is probably related) - see #880

As for network functionality, i did look into it briefly. While i was able to compile libsodium, i wasn't able to include asio due to various errors and missing dependencies. Those are unrelated to the switch build.

@AJenbo
Copy link
Member

AJenbo commented Feb 25, 2021

Another use case for 3d could be to create distance between the automap and gamescreen.

thats a really good idea.

But, the main goal would be 800px support for most 3ds/2ds models and bottom screen support.

I guess you could still lave all the rendering the same, no matter if SDL or memcpy is used, and then just change the step that handles screen presentation. Unless there is something special about how frame buffers are handled, or you want to put in the effort in order to do hardware rendering.
In anycase I think the future revamping of the render pipeline will make this work easier, though it could also result in duplicate effor or merge conflicts in the short run.

i wasn't able to include asio due to various errors and missing dependencies. Those are unrelated to the switch build.

Do you know if libzt or SDL_net would be viable options?

@spitfire
Copy link
Contributor

spitfire commented Feb 25, 2021

Another use case for 3d could be to create distance between the automap and gamescreen.

Seems like a good idea, but have you thought about (optionally) putting Toolbar and automap (or maybe even character, inventory and quest windows )on the bottom screen?

@MrHuu
Copy link
Contributor Author

MrHuu commented Mar 2, 2021

Do you know if libzt or SDL_net would be viable options?

Both libraries are not offered on any channels i'm aware of.
SDL_net was initially being build, but it hasn't been carried over to the current devkitpro repository.
I don't think it was in a usable state at that point.

The 3DS (libctru) supports BSD sockets, but both options would require some work porting the libraries.

Seems like a good idea, but have you thought about (optionally) putting Toolbar and automap (or maybe even character, inventory and quest windows )on the bottom screen?

Yes, i have. As much as i love to make use of the bottom screen, i do think it should be optional. Original layout should also stay available to toggle, for nostalgic reasons.

@glebm
Copy link
Collaborator

glebm commented Mar 24, 2021

Now that we explicitly pass the buffer to render to into all the functions, this should be more feasible.

Currently we use GlobalOutputBuffer() throughout but we could split it into InterfaceOutputBuffer() and GameOutputBuffer().

@AJenbo AJenbo added this to the 1.2.0 milestone Mar 27, 2021
@oaken-source
Copy link

I just came across this, thank you for bringing Diablo to the 3ds, it has indeed brought me many hours of fun and nostalgia already :)

Maybe another cosmetic usecase for the 3D effect would be to bring the menu, and overlayed interface items forward, such as spell icons and the inventory / character screen. It would also be amazing if we could have the minimap on the bottom screen.

@oaken-source
Copy link

Another use case for 3d could be to create distance between the automap and gamescreen.

Seems like a good idea, but have you thought about (optionally) putting Toolbar and automap (or maybe even character, inventory and quest windows )on the bottom screen?

Inventory window on the bottom screen would allow inventory interaction through the touchscreen / with the stylus. that sounds quite good to have.

@Tumayto
Copy link

Tumayto commented Mar 27, 2021

@MrHuu If we were to get UI/inventory/map on bottom screen, what do you think the best resolution for top screen would be?
I've been thinking about that myself. I'm not a programmer but here are a few mockups I made.

Here's the 3DS's current simulated resolution as a reference.

Bottom screen

All interfaces scaled to half size. Map as underlying visual shown at all times (perhaps on a black bg).
Functionality to drag items from inventory onto map portion to drop them.

With these elements on their own screen, we're now free to have a resolution better suited to the 3DS's top screen (assuming scaling can be set differently per screen).


NOTE: The non-UI screen space of Diablo's default 640x480 resolution is actually 640x352 (or 640x340 if you count the top parts of the health/mana globe).

400x240 (100%, no scaling)

1:1 at the cost of very limited view. Ranged classes might be near unplayable.

480x288 (83.3% scaled)

Seems like a nice middle ground. Ranged classes might still be somewhat functional.

600x360 (66.7% scaled)

Stays close to default height at the cost of 40px of width.

640x384 (62.5% scaled)

Keeps Diablo's original width and we gain 32px height.

@AJenbo
Copy link
Member

AJenbo commented Mar 27, 2021

@Tumayto you should probably scale all of them to 400x240 to see how it affects the pixels. I don't remember if the 3DS is able to do this in the hardware or if it's done using NN in software?

@AJenbo
Copy link
Member

AJenbo commented Mar 27, 2021

480->400 NN
image

600->400 NN
image

640->400 NN
image

@Tumayto
Copy link

Tumayto commented Mar 27, 2021

@AJenbo I'm not sure how the 3DS handles the scaling, but I was able to run a 640x384 resolution on it, gave me a good idea of what it might look like. A lot more visible than the 50% scaling (at least if the UI was out of the way). As of now, seems like if you try to render anything below 640 width it crashes.

@AJenbo
Copy link
Member

AJenbo commented Mar 27, 2021

It likely crashes because it can't fit the panels on the screen. If they are disabled it should work.

@Tumayto
Copy link

Tumayto commented Mar 27, 2021

It likely crashes because it can't fit the panels on the screen. If they are disabled it should work.

Ahh, that's what it seemed like. Is there any way to disable those elements through .ini?

@qndel
Copy link
Member

qndel commented Mar 27, 2021

no

@AJenbo
Copy link
Member

AJenbo commented Mar 29, 2021

* Previous 3ds builds are based on having separate executables for diablo and hellfire, including assets.
  Recent commits unifying hellfire results in both executables behaving in a similar fashion.
  Both .cia builds will load hellfire if found.
  The .3dsx builds allow us to pass --diablo as argument, to force running diablo.

Should the hellfire build be removed, including it's assets?

I have decided to go the route of a single bin. Not being able to force Diablo only mode when using .cia is an acceptable limitation. f64e297

@AJenbo AJenbo removed this from the 1.2.0 milestone Mar 29, 2021
@spitfire
Copy link
Contributor

spitfire commented Mar 30, 2021

I have decided to go the route of a single bin. Not being able to force Diablo only mode when using .cia is an acceptable limitation. f64e297

While possible how usable/discoverable is using command line arguments for 3dsx? I don't know how to execute apps with command line parameters on 3DS

@AJenbo
Copy link
Member

AJenbo commented Mar 30, 2021

While possible how usable/discoverable is using command line arguments for 3dsx? I don't know how to execute apps with command line parameters on 3DS

https://github.com/fincs/new-hbmenu#usage

you also have the option of not-installing the expansion.

If there is a way for us to ship preconfigured xml shortcuts? I would be happy with that solution.

@MrHuu
Copy link
Contributor Author

MrHuu commented Apr 1, 2021

While possible how usable/discoverable is using command line arguments for 3dsx? I don't know how to execute apps with command line parameters on 3DS

https://github.com/fincs/new-hbmenu#usage

you also have the option of not-installing the expansion.

If there is a way for us to ship preconfigured xml shortcuts? I would be happy with that solution.

We could provide preconfigured shortcuts. But i don't think this makes much sense.
As the default executable would be hellfire, with a shortcut to original diablo. When providing shortcuts, I would prefer the main executable as regular diablo, with the ability to launch expansions through command-line.

DevilutionX isn't developed this way, as it includes hellfire by default. If an in-game menu is available to be able to select the 'default' diablo, additional shortcuts wouldn't be needed.

@AJenbo
Copy link
Member

AJenbo commented Apr 1, 2021

it will be eventually

@MrHuu
Copy link
Contributor Author

MrHuu commented Apr 1, 2021

Exactly. And like you already mentioned, if the hellfire assets aren't provided, it will still launch as regular diablo. Which makes me think to just have one main DevilutionX executable.

This; f64e297, should be sufficient.

@AJenbo
Copy link
Member

AJenbo commented Apr 29, 2021

@MrHuu with the latest master we are now low enough in memory usage that the game will run on old 3DS models. Performance is pretty low, something like 7 FPS. But if you where planning to make an OpenGL ES render for the port then that would probably allow the game to run decent on all 3DS models :)

@glebm
Copy link
Collaborator

glebm commented Apr 29, 2021

Does the 3ds have an 8-bit output mode? If it does, setting these can significantly improve performance:

set(SDL1_VIDEO_MODE_BPP 8)
set(SDL1_VIDEO_MODE_FLAGS SDL_FULLSCREEN|SDL_HWSURFACE)

Additionally, if it also supports arbitrary resolutions, you could switch the video mode before playing videos using this setting:

set(SDL1_FORCE_SVID_VIDEO_MODE ON)

@AJenbo
Copy link
Member

AJenbo commented Apr 29, 2021

SDL1_VIDEO_MODE_BPP 8 didn't seem to help but SDL1_VIDEO_MODE_BPP 16 gave a significant boost, it's now able to do 19FPS in town.

@glebm
Copy link
Collaborator

glebm commented Apr 29, 2021

@AJenbo I'm guessing it's because it doesn't support 8 bpp, so it falls back to 32 if you try to set 8?

@AJenbo
Copy link
Member

AJenbo commented Apr 29, 2021

I'm just relaying from chat :)

P.s. could you update to the latest SDL_audiolib so that we don't have all this audio cracheling :)
(there was a bug in mono -> resampeling)

@StephenCWills
Copy link
Member

@AJenbo I'm guessing it's because it doesn't support 8 bpp, so it falls back to 32 if you try to set 8?

It looks to me as though 8 bpp is supported so it shouldn't be falling back to 32.
https://github.com/nop90/SDL-3DS#video

FYI, I had told @AJenbo it had no effect, but I think that was one of the times where I forgot to install the game after transferring the cia. It seems the real problem with 8 bpp is that the game freezes, crashes, and displays weird colors. I've also seen it repeatedly loop back to the beginning of the intro movie not long after reaching the Diablo splash screen.

@StephenCWills
Copy link
Member

So now that the stability issues have been resolved, I've been able to give all these options a fair assessment.

  • The 8 bpp option works fine, but I don't see any noticeable improvement in performance over 16 bpp. Both 8 and 16 bpp perform way better than not having them.
  • SDL_HWSURFACE (with 8 bpp) appears to cause rendering to fail or something, because the game still works but the display doesn't update (nearly all the time).
  • The svid option (with 8 bpp) also works fine, but no noticeable improvement over not having it.

@glebm
Copy link
Collaborator

glebm commented May 2, 2021

We should use 8bpp wherever possible for maximum performance -- even if there is no FPS difference, the memory usage may be lower
SDL_HWSURFACE won't be useful for a while but once we've finished removing the buffer padding it will help a lot -- unfortunately it's a lot of work

@StephenCWills
Copy link
Member

I just found one important difference between 8 bpp vs 16 bpp. When I change the resolution from 800x480 to 640x480, I get a sizable boost to performance on 16 bpp, but 8 bpp acts like I just turned on SDL_HWSURFACE and fails to update the display. Both bpp settings are still playable on 641x480, but the performance is just as bad as 800x480, which I guess means the scaling is taking a good chunk of my CPU cycles. Anyway, perhaps there is another root cause for this discrepancy so 640x480 and/or SDL_HWSURFACE could be playable on 8 bpp?

@glebm
Copy link
Collaborator

glebm commented May 3, 2021

This sounds like there is some hack in 3DS's SDL specifically for 640x480x8

@MrHuu
Copy link
Contributor Author

MrHuu commented May 3, 2021

I'd also like to note this:

devilutionX/Source/dx.cpp

Lines 315 to 317 in e4df940

#ifdef __3DS__
gspWaitForVBlank();
#endif

It has been added to address rendering getting out of sync and stopping rendering completely. Mostly noticed with scaling enabled, either in town or during heavy encounters.

This is far from a ideal solution.
Since no separate thread is being used to process the frame, the entire main thread waits until VBlank.

This should be taken care of by SDL, but right now calling SDL_Flip() does not guarantee the image is actually displayed.

Eventually this needs to be replaced with a better solution, as it does limit performance.

If no scaling (SDL) is used, rendering at 400x240, performance should be good enough to stop running out of sync and gspWaitForVBlank() could probably be removed.

@StephenCWills
Copy link
Member

StephenCWills commented May 3, 2021

Thanks, @MrHuu. I spent a lot of time looking at this last night, and I came to the same conclusion. I find it weird that SDL_Flip() doesn't provide that guarantee. It seems to me that it actually does call gspWaitForVBlank() itself, though I haven't really verified whether I was looking at the latest codebase for the devkitpro package.

https://github.com/nop90/SDL-3DS/blob/ad5e685a3db012af4e19c4cfb258e419e2194cb0/SDL-1.2.15/src/video/n3ds/SDL_n3dsvideo.c#L550

@StephenCWills
Copy link
Member

StephenCWills commented May 3, 2021

I found the relevant code in devkitpro/pacman-packages. It's changed a bit, but fundamentally does the same thing as the nop90 repo.
https://github.com/devkitPro/pacman-packages/blob/a909e45bb0a3fd9a294b0d9b0b62f091ef3f3749/3ds/SDL/SDL-1.2.15.patch#L2220

EDIT: Better yet, they have a branch in the SDL repo.
https://github.com/devkitPro/SDL/blob/96cd50226ab33f2e6a1e310907e686d8ffd5aab1/src/video/n3ds/SDL_n3dsvideo.c#L550

@StephenCWills
Copy link
Member

StephenCWills commented May 4, 2021

Okay, so after dropping gspWaitForVBlank() from the devilutionX codebase, I was able to get the display to update at 640x480x8. However, I got a massive amount of stuttering in the display, and once I got to the dungeon there was more stutter than not. By enabling console output for the bottom screen on my build, I was able to verify that the stuttering had nothing to do with lag, and the game was rendering all frames at very even intervals. It was just like @MrHuu said, that SDL_Flip() wasn't guaranteed to display the image.

After reviewing the SDL code for possible reasons why, I noticed the following comment that looks pretty suspicious to me.
https://github.com/devkitPro/SDL/blob/96cd50226ab33f2e6a1e310907e686d8ffd5aab1/src/video/n3ds/SDL_n3dsvideo.c#L506

And here are the descriptions for C3D_FRAME_SYNCDRAW and C3D_FRAME_NONBLOCK.
https://github.com/devkitPro/citro3d/blob/c7425b653ea68a253f8d88e9de7c89ad1fb487f9/include/c3d/renderqueue.h#L21-L25

It seems apparent based on those descriptions that SDL is just frequently failing to display our frames because the GPU is often busy. I decided to experiment with a build that uses C3D_FRAME_SYNCDRAW instead of C3D_FRAME_NONBLOCK. This completely resolves the aforementioned issues with SDL's failure to display frames, and the game feels significantly smoother even at low framerates.

So now we know our issue can be fixed with a change to the SDL library for 3DS, which means we'd need a custom build or support from the maintainers. I guess we should submit an issue to devkitPro and see what they say.

Note: The d-pad stopped working on my custom build. I haven't done any sort of investigation just yet, except that I did try another custom build with C3D_FRAME_NONBLOCK and the d-pad still didn't work. So something's off in the 3ds-sdl-1.2 branch.

EDIT: This probably explains the d-pad.
devkitPro/SDL#66

@StephenCWills
Copy link
Member

devkitPro/SDL#75

@AJenbo
Copy link
Member

AJenbo commented Nov 2, 2021

Since there now is an ingame way to switch i consider this issue closed. I know there are other things mentioned in the issue, but instead new issues whould be opened for them if someone intent to work on it in some capacity.

@AJenbo AJenbo closed this as completed Nov 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants