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

Core for hydra multi emulator frontend #319

Merged
merged 3 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@
[submodule "third_party/mio"]
path = third_party/mio
url = https://github.com/vimpunk/mio
[submodule "third_party/hydra_core"]
path = third_party/hydra_core
url = https://github.com/hydra-emu/core
13 changes: 12 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ option(ENABLE_HTTP_SERVER "Enable HTTP server. Used for Discord bot support" OFF
option(ENABLE_DISCORD_RPC "Compile with Discord RPC support (disabled by default)" ON)
option(ENABLE_LUAJIT "Enable scripting with the Lua programming language" ON)
option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF)
option(BUILD_HYDRA_CORE "Build a Hydra core" OFF)

include_directories(${PROJECT_SOURCE_DIR}/include/)
include_directories(${PROJECT_SOURCE_DIR}/include/kernel)
Expand All @@ -59,6 +60,10 @@ add_compile_definitions(NOMINMAX) # Make windows.h not define min/ma
add_compile_definitions(WIN32_LEAN_AND_MEAN) # Make windows.h not include literally everything
add_compile_definitions(SDL_MAIN_HANDLED)

if(BUILD_HYDRA_CORE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()

if(ENABLE_DISCORD_RPC AND NOT ANDROID)
add_subdirectory(third_party/discord-rpc)
include_directories(third_party/discord-rpc/include)
Expand Down Expand Up @@ -373,7 +378,13 @@ if(ENABLE_VULKAN)
set(ALL_SOURCES ${ALL_SOURCES} ${RENDERER_VK_SOURCE_FILES})
endif()

add_executable(Alber ${ALL_SOURCES})
if(BUILD_HYDRA_CORE)
include_directories(third_party/hydra_core/include)
add_library(Alber SHARED ${ALL_SOURCES} src/hydra_core.cpp)
target_compile_definitions(Alber PRIVATE PANDA3DS_HYDRA_CORE=1)
else()
add_executable(Alber ${ALL_SOURCES})
endif()

if(ENABLE_LTO OR ENABLE_USER_BUILD)
set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
Expand Down
1 change: 1 addition & 0 deletions include/emulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class Emulator {
void setOutputSize(u32 width, u32 height) { gpu.setOutputSize(width, height); }

EmulatorConfig& getConfig() { return config; }
ServiceManager& getServiceManager() { return kernel.getServiceManager(); }
RendererType getRendererType() const { return config.rendererType; }
Renderer* getRenderer() { return gpu.getRenderer(); }
};
7 changes: 7 additions & 0 deletions include/helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ namespace Helpers {
return false;
}

static constexpr bool isHydraCore() {
#ifdef PANDA3DS_HYDRA_CORE
return true;
#endif
return false;
}

static void debug_printf(const char* fmt, ...) {
if constexpr (buildingInDebugMode()) {
std::va_list args;
Expand Down
1 change: 1 addition & 0 deletions include/services/hid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class HIDService {

void pressKey(u32 mask) { newButtons |= mask; }
void releaseKey(u32 mask) { newButtons &= ~mask; }
void setKey(u32 mask, bool pressed) { pressed ? pressKey(mask) : releaseKey(mask); }

u32 getOldButtons() const { return oldButtons; }
s16 getCirclepadX() const { return circlePadX; }
Expand Down
10 changes: 6 additions & 4 deletions src/core/renderer_gl/renderer_gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -522,9 +522,11 @@ void RendererGL::display() {
OpenGL::draw(OpenGL::TriangleStrip, 4);
}

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
screenFramebuffer.bind(OpenGL::ReadFramebuffer);
glBlitFramebuffer(0, 0, 400, 480, 0, 0, outputWindowWidth, outputWindowHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
if constexpr (!Helpers::isHydraCore()) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
screenFramebuffer.bind(OpenGL::ReadFramebuffer);
glBlitFramebuffer(0, 0, 400, 480, 0, 0, outputWindowWidth, outputWindowHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
}

void RendererGL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
Expand Down Expand Up @@ -799,4 +801,4 @@ void RendererGL::screenshot(const std::string& name) {
}

stbi_write_png(name.c_str(), width, height, 4, flippedPixels.data(), 0);
}
}
4 changes: 2 additions & 2 deletions src/http_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ void HttpServer::startHttpServer() {
}

std::string HttpServer::status() {
HIDService& hid = emulator->kernel.getServiceManager().getHID();
HIDService& hid = emulator->getServiceManager().getHID();
std::stringstream stringStream;

stringStream << "Panda3DS\n";
Expand Down Expand Up @@ -271,7 +271,7 @@ void HttpServer::processActions() {
return;
}

HIDService& hid = emulator->kernel.getServiceManager().getHID();
HIDService& hid = emulator->getServiceManager().getHID();

while (!actionQueue.empty()) {
std::unique_ptr<HttpAction> action = std::move(actionQueue.front());
Expand Down
122 changes: 122 additions & 0 deletions src/hydra_core.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include <emulator.hpp>
#include <hydra/core.hxx>
#include <renderer_gl/renderer_gl.hpp>

class HC_GLOBAL HydraCore final : public hydra::IBase, public hydra::IOpenGlRendered, public hydra::IFrontendDriven, public hydra::IInput {
HYDRA_CLASS
public:
HydraCore();

private:
// IBase
bool loadFile(const char* type, const char* path) override;
void reset() override;
hydra::Size getNativeSize() override;
void setOutputSize(hydra::Size size) override;

// IOpenGlRendered
void setFbo(unsigned handle) override;
void setContext(void* context) override;
void setGetProcAddress(void* function) override;

// IFrontendDriven
void runFrame() override;

// IInput
void setPollInputCallback(void (*callback)()) override;
void setCheckButtonCallback(int32_t (*callback)(uint32_t player, hydra::ButtonType button)) override;

std::unique_ptr<Emulator> emulator;
RendererGL* renderer;
void (*pollInputCallback)() = nullptr;
int32_t (*checkButtonCallback)(uint32_t player, hydra::ButtonType button) = nullptr;
};

HydraCore::HydraCore() : emulator(new Emulator) {
if (emulator->getRendererType() != RendererType::OpenGL) {
throw std::runtime_error("HydraCore: Renderer is not OpenGL");
}
renderer = static_cast<RendererGL*>(emulator->getRenderer());
}

bool HydraCore::loadFile(const char* type, const char* path) {
if (std::string(type) == "rom") {
return emulator->loadROM(path);
} else {
return false;
}
}

void HydraCore::runFrame() {
renderer->resetStateManager();

pollInputCallback();
HIDService& hid = emulator->getServiceManager().getHID();
hid.setKey(HID::Keys::A, checkButtonCallback(0, hydra::ButtonType::A));
hid.setKey(HID::Keys::B, checkButtonCallback(0, hydra::ButtonType::B));
hid.setKey(HID::Keys::X, checkButtonCallback(0, hydra::ButtonType::X));
hid.setKey(HID::Keys::Y, checkButtonCallback(0, hydra::ButtonType::Y));
hid.setKey(HID::Keys::L, checkButtonCallback(0, hydra::ButtonType::L1));
hid.setKey(HID::Keys::R, checkButtonCallback(0, hydra::ButtonType::R1));
hid.setKey(HID::Keys::Start, checkButtonCallback(0, hydra::ButtonType::Start));
hid.setKey(HID::Keys::Select, checkButtonCallback(0, hydra::ButtonType::Select));
hid.setKey(HID::Keys::Up, checkButtonCallback(0, hydra::ButtonType::Keypad1Up));
hid.setKey(HID::Keys::Down, checkButtonCallback(0, hydra::ButtonType::Keypad1Down));
hid.setKey(HID::Keys::Left, checkButtonCallback(0, hydra::ButtonType::Keypad1Left));
hid.setKey(HID::Keys::Right, checkButtonCallback(0, hydra::ButtonType::Keypad1Right));

int x = !!checkButtonCallback(0, hydra::ButtonType::Analog1Right) - !!checkButtonCallback(0, hydra::ButtonType::Analog1Left);
int y = !!checkButtonCallback(0, hydra::ButtonType::Analog1Up) - !!checkButtonCallback(0, hydra::ButtonType::Analog1Down);
hid.setCirclepadX(x * 0x9C);
hid.setCirclepadY(y * 0x9C);

emulator->runFrame();
}

void HydraCore::reset() { emulator->reset(Emulator::ReloadOption::Reload); }

hydra::Size HydraCore::getNativeSize() { return {400, 480}; }

// Size doesn't matter as the glBlitFramebuffer call is commented out for the core
void HydraCore::setOutputSize(hydra::Size size) {}

void HydraCore::setGetProcAddress(void* function) {
#ifdef __ANDROID__
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(function))) {
Helpers::panic("OpenGL ES init failed");
}
#else
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(function))) {
Helpers::panic("OpenGL init failed");
}
#endif
// SDL_Window is not used, so we pass nullptr
emulator->initGraphicsContext(nullptr);
}

void HydraCore::setContext(void*) {}

void HydraCore::setFbo(unsigned handle) { renderer->setFBO(handle); }

void HydraCore::setPollInputCallback(void (*callback)()) { pollInputCallback = callback; }

void HydraCore::setCheckButtonCallback(int32_t (*callback)(uint32_t player, hydra::ButtonType button)) { checkButtonCallback = callback; }

HC_API hydra::IBase* createEmulator() { return new HydraCore; }

HC_API void destroyEmulator(hydra::IBase* emulator) { delete emulator; }

HC_API const char* getInfo(hydra::InfoType type) {
switch (type) {
case hydra::InfoType::CoreName: return "Panda3DS";
case hydra::InfoType::SystemName: return "Nintendo 3DS";
case hydra::InfoType::Description: return "HLE 3DS emulator. There's a little Alber in your computer and he runs Nintendo 3DS games.";
case hydra::InfoType::Author: return "wheremyfoodat (Peach)";
case hydra::InfoType::Version: return "0.7";
case hydra::InfoType::License: return "GPLv3";
case hydra::InfoType::Website: return "https://panda3ds.com/";
case hydra::InfoType::Extensions: return "3ds,cci,cxi,app,3dsx,elf,axf";
case hydra::InfoType::Firmware: return "";
default: return nullptr;
}
}
1 change: 1 addition & 0 deletions third_party/hydra_core
Submodule hydra_core added at 090c8a
Loading