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

Full GBA Controllers Integration with mGBA #9600

Merged
merged 18 commits into from Jul 21, 2021
Merged

Conversation

Bonta0
Copy link
Contributor

@Bonta0 Bonta0 commented Mar 19, 2021

This replaces the current GBA devices implementation that relied on VBA-M connections and fully emulates them with libmgba instead. This one has multiple advantages: it works, it's deterministic, more convenient and makes it trivial to support SaveStates, NetPlay and Movies for those devices
image
image
New GBA profiles:
image
NetPlay: Device type can be configured where you would normally assign controllers
image
The host has the option to make players see only GBAs that correspond to a local pad since most games will expose secrets to the players on the GBA screen
image
Additional settings:
image
mGBA was added as a git submodule unlike other libraries in this project. If copying the entire tree is preferred for consistency I can do it but I believe this is more appropriate. A CMake option USE_MGBA was added to exclude the library if desired.
Rendering is only implemented in Qt.

Known Issues:

  • DSP HLE: Booting with multiple GBAs will fail. This is a DSP issue, the same behavior would happen with VBA-M if you could somehow connect multiple instances simultaneously. In the meantime either use DSPLLE or connect GBAs manually one by one
  • Final Fantasy Crystal Chronicles: Game will randomly drop inputs when the minimap is active on the GBA. While this still uses the same inaccurate timings as the previous implementation, using more accurate ones does not fix it, I suspect that there are more inaccuracies in Dolphin's SI implementation that also need to be fixed
  • Sonic Adventure 2: Does not connect with Sonic Advance

Source/Core/Core/Movie.h Outdated Show resolved Hide resolved
@JMC47
Copy link
Contributor

JMC47 commented Mar 19, 2021

So Sonic Adventure not connecting is because the most common guide appears to be incorrect. I need to get around to doing what the booklet says to do lol.

Edit: Nope, still doesn't work. Guides were correct enough anyway and the booklets are even more vague than the guide. This is a case where it should just work, but doesn't. As endrift pointed out below, there's probably more bugs in the protocol.

@JMC47
Copy link
Contributor

JMC47 commented Mar 19, 2021

@endrift this might interest you as well

@RinMaru
Copy link

RinMaru commented Mar 19, 2021

could this be used to get Gameboy Player to work in the future?

Copy link
Contributor

@endrift endrift left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems exceedingly fragile and inclined to break at any given moment when I update, well, pretty much anything upstream, especially since some of the APIs used are going to be changing in the future. While having it as an option seems neat, I'm extremely unfond of wholesale replacing the old SI_DeviceGBA since it means that your version of Dolphin and your version of mGBA are tightly integrated. This has a huge number of issues, including causing a large burden on bug reporting for games that have been fixed upstream but not in your downstream, making packaging difficult for distros, and a lot more. Furthermore, as evidenced I've done a bunch of work lately on fixing the old version to work better (with still more room to go) so this kind of feels like a slap in the face.

Source/Core/Core/HW/GBACore.cpp Outdated Show resolved Hide resolved
static VFile* LoadROM_Zip(const char* path)
{
VFile* vf{};
unzFile zip = unzOpen(path);
if (zip)
{
do
{
unz_file_info info{};
if (unzGetCurrentFileInfo(zip, &info, nullptr, 0, nullptr, 0, nullptr, 0) != UNZ_OK ||
!info.uncompressed_size)
continue;

std::vector<u8> buffer(info.uncompressed_size);
if (!Common::ReadFileFromZip(zip, &buffer))
continue;

vf = VFileMemChunk(buffer.data(), info.uncompressed_size);
if (mCoreIsCompatible(vf) == mPLATFORM_GBA)
{
vf->seek(vf, 0, SEEK_SET);
break;
}

vf->close(vf);
vf = nullptr;
} while (unzGoToNextFile(zip) == UNZ_OK);
unzClose(zip);
}
return vf;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mGBA has minizip integration built in already. Why did you add a second version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Building with LIBMGBA_ONLY will disable every dependency, except for lzma for some reason which is convenient in our case since Dolphin doesn't have 7z support
If anything I'd rather go the opposite direction and disable lzma in mGBA and have Dolphin handle it instead

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Dolphin is already linking against minizip it would be preferable to use mGBA's existing minizip code and just point it to the right headers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dolphin's minizip doesn't have the zipping functions required for mGBA's vfs-zip. We could update it but that would only introduce dead code

static_cast<s32>((gc_ticks - m_last_gc_ticks) * GBA_ARM7TDMI_FREQUENCY / gc_frequency));
m_waiting_for_event = true;

s32 start_time = mTimingCurrentTime(m_core->timing);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to use mTimingGlobalTime which is a u64.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out that is only available when USE_DEBUGGERS is defined, this is not an issue since we frequently sync devices and already use an s32 for the event anyway

Source/Core/Core/HW/GBACore.h Outdated Show resolved Hide resolved
Externals/mGBA/make_version.c.js Outdated Show resolved Hide resolved
@endrift
Copy link
Contributor

endrift commented Mar 19, 2021

could this be used to get Gameboy Player to work in the future?

Short answer: not really, but it's complicated
Long answer: the biggest thing holding back GBP was a good way to attach libmgba, which this solves. I already have a lot of work on a branch that integrates GBP but its libmgba integration was rather poorly done, so having that done here would help, especially since I could pretty much file a PR immediately after rebasing (though sound will be garbage if I do it that way).

@JosJuice
Copy link
Member

I agree that we should keep the old way of connecting to a GBA emulator as an option.

@endrift
Copy link
Contributor

endrift commented Mar 19, 2021

Especially since connecting via libmgba is enabled or disabled at compile time, removing the old approach seems like a bad idea.

Copy link
Member

@BhaaLseN BhaaLseN left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pleasently surprised how little code this needed. Also, welcome to the project Bonta (first you conquered ALTTPR, now you conquer emulation? Nice.)

I do wonder if we should keep around the other GBA implementation for...something. Mainly because it had the loose coupling of a GBA implementation (vs. locking us into a specific version of mGBA.)
If we do, I feel like we should make this the user-facing default tho, since it has the It-Just-Works™ factor many end-users seek.

Source/Core/AudioCommon/AudioCommon.cpp Show resolved Hide resolved
Source/Core/Core/HW/SI/SI.cpp Outdated Show resolved Hide resolved
Source/Core/Core/HW/SI/SI.cpp Outdated Show resolved Hide resolved
Source/Core/Core/Movie.cpp Outdated Show resolved Hide resolved
Source/Core/Core/Movie.h Outdated Show resolved Hide resolved
Source/Core/DolphinQt/GBAWidget.cpp Outdated Show resolved Hide resolved
Source/Core/DolphinQt/Settings/GameCubePane.cpp Outdated Show resolved Hide resolved
Source/Core/Core/HW/GBACore.cpp Show resolved Hide resolved
@JMC47
Copy link
Contributor

JMC47 commented Mar 20, 2021

I do think we keep around the old path if possible, but at the same time there is a level of usability, especially on Windows, that is lacking with the old protocol. Linux definitely performs better than Windows with the old setup with separate emulators (verified in Crystal Chronicles) but with this everyone is brought to an even playing field and it even works on netplay between different OSes in my experience!

There are definitely some protocol bugs, as endrift has already mentioned. We have a lot of unknowns with the protocol as well. Also there's the fact that by removing the old support, we'd be essentially removing support for connecting to VBA-M from Dolphin immediately. They'd still be able to connect to old builds and releases, so it's not a huge problem. And you could talk me into that from a user standpoint considering how big of a usability upgrade this actually is; this makes it super easy to setup and adds a ton of features that users really want.

I don't see this as an end-point to GBA <-> GCN, however. I see it as a transitional point with the real tests ahead. This makes things more consistent, more deterministic, and more useable which should result in us being able to track down the remaining bugs more easily without having to worry about latency being unrealistically high like with the old connectivity on Windows.

@Bonta0
Copy link
Contributor Author

Bonta0 commented Mar 21, 2021

I've restored the previous GBA implementation, a force-push was necessary to keep a coherent commit history, sorry about that
This PR now has a dependency on #9603 to address a comment

@JosJuice
Copy link
Member

Don't worry about the force push, we do that all the time in pull requests to keep the history tidy.

@Rumi-Larry
Copy link

I would split the "Movies" and "Netplay" to a different pr(s), to reduce the size of the diff. This would also narrow down what needs to be tested, reducing the possibility of unnoticed bugs from being merged.

@JMC47
Copy link
Contributor

JMC47 commented Mar 22, 2021

Considering it's adding a new feature and things are fairly well segmented between the different modules, I'm not certain that's necessary? The way it is now makes it so everything can be tested in one place and it is pretty explicit about what it adds.

@JMC47
Copy link
Contributor

JMC47 commented Mar 24, 2021

On the latest version of this Pull Request downloaded from buildbot, I can't seem to run any games regardless of controller settings. Hard crash. I will run from Visual Studio to see if I get the same crash and get a stack trace.

@JMC47
Copy link
Contributor

JMC47 commented Mar 24, 2021

Here's the stack trace along with Visual Studio saying what happened.

image

image

@JMC47
Copy link
Contributor

JMC47 commented Mar 24, 2021

@dolphin-emu-bot rebuild

@JMC47
Copy link
Contributor

JMC47 commented Mar 24, 2021

After retesting this and testing both the internal GBA and GBA over TCP, I'm pleased with the performance of this build. I'm able to do things like use savestates, record movies, and even play netplay. And if the old way to do things is always there if I want to connect over TCP, for say playing across the network on a separate device.

I know there's a lot of code review to happen and things to discuss on that front, but I'm extremely happy with how well it works. Usage wise, huge thumbs up.

Source/Core/Core/HW/GBACore.cpp Show resolved Hide resolved
Source/Core/Core/HW/SI/SI_DeviceGBAEmu.h Outdated Show resolved Hide resolved
Source/Core/DolphinQt/GBAWidget.cpp Show resolved Hide resolved
@Bonta0
Copy link
Contributor Author

Bonta0 commented Mar 26, 2021

I have included the commit from #9608 for now so this can be tested DSP HLE as well

@JMC47
Copy link
Contributor

JMC47 commented Mar 26, 2021

@dolphin-emu-bot rebuild

@JMC47
Copy link
Contributor

JMC47 commented Apr 6, 2021

Reported issues with Pokemon Box were not related to GBA <-> GCN and were fixed here -> #9625

This should mean that once this is merged along with the timing fixes, all GBA <-> GCN games will be fully supported except one. The true final boss of the GameCube and the last game not to reach gameplay: The GameBoy Player.

Copy link
Contributor

@endrift endrift left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I apparently wrote this some time ago and forgot to hit submit review.

m_core->init(m_core);

mCoreInitConfig(m_core, "dolphin");
mCoreConfigSetValue(&m_core->config, "idleOptimization", "detect");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this config setting this is necessary, and I'm not sure if it contributes anything. It probably doesn't hurt anything either though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be more efficient than the default "remove" in some games. I left it there as a way to potentially avoid stalling Dolphin or lower CPU usage

Copy link
Contributor

@endrift endrift left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, though it has brought some potential upstream issues to my attention.

set(USE_MGBA 0)
endif()
if(USE_MGBA)
message(STATUS "Using static libmgba from Externals")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth noting that some distros do ship libmgba, though they don't tend to ship libmgba-dev.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could be a issue for NetPlay or Movies, we may have to enforce a specific version for those cases

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. Distros can get angry about it though.

endif()

if(ANDROID)
target_compile_definitions(mgba PRIVATE -Dfutimes=futimens)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should fix this upstream.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fixed upstream now.

constexpr auto SAMPLES = 512;
constexpr auto SAMPLE_RATE = 48000;

// libmGBA does not return the correct frequency for some GB models
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to know this too

#ifdef HAS_LIBMGBA
{SerialInterface::SIDEVICE_GC_GBA_EMULATED, "GBA (Emulated)"},
#endif
{SerialInterface::SIDEVICE_GC_GBA, "GBA (TCP)"},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this wording is confusing. Both are emulated after all, just one internal to Dolphin and one external. Perhaps "Integrated" and "Remote".

Copy link
Member

@leoetlino leoetlino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 15 of 15 files at r1, 4 of 4 files at r2, 2 of 2 files at r3, 7 of 7 files at r4, 2 of 2 files at r5, 3 of 3 files at r6, 2 of 2 files at r7, 15 of 15 files at r8, 10 of 10 files at r9, 3 of 3 files at r10, 6 of 6 files at r11, 4 of 4 files at r12, 9 of 9 files at r13, 7 of 7 files at r14, 8 of 8 files at r15, 9 of 9 files at r16, 16 of 16 files at r17, 5 of 5 files at r18.
Reviewable status: all files reviewed, 7 unresolved discussions (waiting on @Bonta0)


Source/Core/DolphinQt/GBAHost.cpp, line 28 at r15 (raw file):

  u32 width, height;
  core_ptr->GetVideoDimensions(&width, &height);
  QMetaObject::invokeMethod(

This wasn't immediately obvious to me, but for anyone wondering the reason invokeMethod is used (rather than a direct call or emit) is for thread safety reasons.


Source/Core/DolphinQt/GBAHost.cpp, line 42 at r15 (raw file):

{
  auto core_ptr = m_core.lock();
  if (!core_ptr->IsStarted())

Should we check or assert that m_core is still valid (or equivalently, that core_ptr is not null)?


Source/Core/DolphinQt/GBAWidget.cpp, line 233 at r15 (raw file):

  else
  {
    move(x() - (frameGeometry().width() / (m_local_pad & 1 ? -2 : 2)),

% 2 != 0? not sure I understand the check here


Source/Core/DolphinQt/GBAWidget.cpp, line 234 at r15 (raw file):

  {
    move(x() - (frameGeometry().width() / (m_local_pad & 1 ? -2 : 2)),
         y() - (frameGeometry().height() / (m_local_pad & 2 ? -2 : 2)));

and here


Source/Core/DolphinQt/GBAWidget.cpp, line 262 at r15 (raw file):

void GBAWidget::contextMenuEvent(QContextMenuEvent* event)
{
  QMenu* menu = new QMenu(this);

these can be auto too


Source/Core/DolphinQt/GBAWidget.cpp, line 409 at r15 (raw file):

}

GBAWidgetController::GBAWidgetController()

GBAWidgetController::GBAWidgetController() = default;


Source/Core/DolphinQt/Config/GamecubeControllersWidget.cpp, line 29 at r14 (raw file):

static const std::vector<std::pair<SerialInterface::SIDevices, const char*>> s_gc_types = {
    {SerialInterface::SIDEVICE_NONE, "None"},

These strings should be marked as translatable manually with _trans, like this: _trans("None")

(String literals that are passed to tr() are automatically marked as translatable, but here you need to do this manually by using the macro.)


Source/Core/DolphinQt/Config/GamecubeControllersWidget.cpp, line 117 at r14 (raw file):

    {
      const SerialInterface::SIDevices si_device = FromGCMenuIndex(box->currentIndex());
      m_gc_buttons[i]->setEnabled(si_device != SerialInterface::SIDEVICE_NONE &&

nit: might be worth making a function for this check? It's repeated 3 times ^^

@leoetlino
Copy link
Member

(Used Reviewable because it was getting difficult to track changes across pushes with GitHub's web interface. Even 4 years later the built-in review tools are still mostly unusable for large PRs.)

Copy link
Contributor Author

@Bonta0 Bonta0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: all files reviewed, 6 unresolved discussions (waiting on @leoetlino)


Source/Core/DolphinQt/GBAHost.cpp, line 28 at r15 (raw file):

Previously, leoetlino (Léo Lam) wrote…

This wasn't immediately obvious to me, but for anyone wondering the reason invokeMethod is used (rather than a direct call or emit) is for thread safety reasons.

I wasn't aware of QueueOnObject at the time, I've replaced those invokeMethod to use that instead to be more consistent with the rest of the code base


Source/Core/DolphinQt/GBAHost.cpp, line 42 at r15 (raw file):

Previously, leoetlino (Léo Lam) wrote…

Should we check or assert that m_core is still valid (or equivalently, that core_ptr is not null)?

This is called directly by the core itself so it can't happen. I've added the check anyway


Source/Core/DolphinQt/GBAWidget.cpp, line 233 at r15 (raw file):

Previously, leoetlino (Léo Lam) wrote…

% 2 != 0? not sure I understand the check here

The point was to arrange the windows in a nice square pattern on the first run but Qt does a good enough job on its own so I'll just let Qt handle it and remove this entirely


Source/Core/DolphinQt/GBAWidget.cpp, line 234 at r15 (raw file):

Previously, leoetlino (Léo Lam) wrote…

and here

Done.


Source/Core/DolphinQt/GBAWidget.cpp, line 262 at r15 (raw file):

Previously, leoetlino (Léo Lam) wrote…

these can be auto too

Done.


Source/Core/DolphinQt/GBAWidget.cpp, line 409 at r15 (raw file):

Previously, leoetlino (Léo Lam) wrote…

GBAWidgetController::GBAWidgetController() = default;

Done.


Source/Core/DolphinQt/Config/GamecubeControllersWidget.cpp, line 29 at r14 (raw file):

Previously, leoetlino (Léo Lam) wrote…

These strings should be marked as translatable manually with _trans, like this: _trans("None")

(String literals that are passed to tr() are automatically marked as translatable, but here you need to do this manually by using the macro.)

Done.


Source/Core/DolphinQt/Config/GamecubeControllersWidget.cpp, line 117 at r14 (raw file):

Previously, leoetlino (Léo Lam) wrote…

nit: might be worth making a function for this check? It's repeated 3 times ^^

Done.

Copy link
Member

@leoetlino leoetlino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 of 85 files at r19, 85 of 85 files at r20.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on @Bonta0)

@JMC47
Copy link
Contributor

JMC47 commented Jul 21, 2021

A feature I never thought would be... is.

@JMC47 JMC47 merged commit a1e806e into dolphin-emu:master Jul 21, 2021
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet