diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 50680fd63..9e2c816cb 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -43,6 +43,22 @@ jobs: src/frontend/node_modules key: cache-modules-${{ matrix.os }}-${{ matrix.node-version }}-${{ hashFiles('src/frontend/pnpm-lock.yaml') }} + - name: Cache emsdk + id: cache-emsdk + uses: actions/cache@v4 + with: + path: | + tools/emsdk + key: cache-emsdk-${{ hashFiles('tools/download-emscripten.sh') }} + + - name: Cache WASM libraries + id: cache-wasm + uses: actions/cache@v4 + with: + path: | + tools/install + key: cache-wasm-${{ matrix.os }}-${{ matrix.node-version }}-${{ hashFiles('src/engine/**', 'tools/build-wasm.sh', 'tools/download-emscripten.sh') }} + - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: @@ -68,3 +84,15 @@ jobs: - name: pnpm build run: | pnpm build + + - name: Install emsdk + if: steps.cache-emsdk.outputs.cache-hit != 'true' + run: | + ./download-emscripten.sh + working-directory: tools + + - name: Build wasm libraries + if: steps.cache-wasm.outputs.cache-hit != 'true' + run: | + ./build-wasm.sh + working-directory: tools diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 964a43755..000000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/.sunodo diff --git a/README.md b/README.md index 2eddc67ce..112b8cf01 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ The following subfolders compose the repo architecture: * `src/backend/` - The Cartesi Machine logic * `src/frontend/` - The website and frontend logic +* `src/engine/` - The main emulator engine * `src/learning/` - The Python code for AI agents * `tools/` - Tooling for dependencies diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt new file mode 100644 index 000000000..963d8c8d8 --- /dev/null +++ b/src/engine/CMakeLists.txt @@ -0,0 +1,88 @@ +################################################################################ +# +# Copyright (C) 2024 retro.ai +# This file is part of retro3 - https://github.com/retroai/retro3 +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# See the file LICENSE.txt for more information. +# +################################################################################ + +################################################################################ +# +# Build system for C++ libraries +# +# Required CMake variables: +# +# CMAKE_FIND_ROOT_PATH - Point this to dependencies compiled with Emscripten +# CMAKE_INSTALL_PREFIX - Point this to the "public" folder +# +################################################################################ + +################################################################################ +# Project settings +################################################################################ + +project(retro_engine LANGUAGES CXX) +cmake_minimum_required(VERSION 3.10) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +################################################################################ +# Dependencies +################################################################################ + +# TODO + +################################################################################ +# Sources +################################################################################ + +set(SOURCE_FILES + core/retro_engine.cpp + core/retro_engine_embinder.cpp +) + +################################################################################ +# Libraries +################################################################################ + +#include_directories(src) + +# Add the executable based on the source files +add_executable(retro_engine ${SOURCE_FILES}) + +# Add flags for Empscripten builds +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + set_target_properties(retro_engine PROPERTIES + COMPILE_FLAGS " \ + -O3 \ + " + # 26214400 is 25 MiB + LINK_FLAGS " \ + -gsource-map \ + -s ALLOW_MEMORY_GROWTH=1 \ + -s INITIAL_MEMORY=26214400 \ + -s MODULARIZE=1 \ + -s USE_WEBGL2=1 \ + -s WASM=1 \ + --bind \ + --source-map-base https://retro.ai/ \ + " + ) +endif () + +################################################################################ +# Install +################################################################################ + +INSTALL( + FILES + "${CMAKE_BINARY_DIR}/retro_engine.js" + "${CMAKE_BINARY_DIR}/retro_engine.wasm" + "${CMAKE_BINARY_DIR}/retro_engine.wasm.map" + DESTINATION + wasm +) diff --git a/src/engine/core/retro_engine.cpp b/src/engine/core/retro_engine.cpp new file mode 100644 index 000000000..3172bf46b --- /dev/null +++ b/src/engine/core/retro_engine.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 retro.ai + * This file is part of retro3 - https://github.com/retroai/retro3 + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * See the file LICENSE.txt for more information. + */ + +#include "retro_engine.hpp" + +#include + +namespace +{ +constexpr const char* CANVAS_ID = "canvas"; +} + +RetroEngine::RetroEngine() : m_webGLContext(0) +{ +} + +RetroEngine::~RetroEngine() +{ + Deinitialize(); +} + +bool RetroEngine::Initialize() +{ + if (!InitializeWebGL()) + return false; + + return true; +} + +void RetroEngine::Deinitialize() +{ + DeinitializeWebGL(); +} + +bool RetroEngine::InitializeWebGL() +{ + EmscriptenWebGLContextAttributes attrs; + emscripten_webgl_init_context_attributes(&attrs); + + attrs.alpha = EM_FALSE; + attrs.depth = EM_TRUE; + attrs.stencil = EM_TRUE; + attrs.antialias = EM_TRUE; + attrs.preserveDrawingBuffer = EM_FALSE; + attrs.powerPreference = EM_WEBGL_POWER_PREFERENCE_HIGH_PERFORMANCE; + attrs.failIfMajorPerformanceCaveat = EM_FALSE; + attrs.majorVersion = 2; + attrs.minorVersion = 0; + + const EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = + emscripten_webgl_create_context(CANVAS_ID, &attrs); + if (context <= 0) + { + std::cerr << "Failed to create WebGL context" << std::endl; + return false; + } + + EMSCRIPTEN_RESULT result = emscripten_webgl_make_context_current(context); + if (result != EMSCRIPTEN_RESULT_SUCCESS) + { + std::cerr << "Failed to make WebGL context current (result=" << result << ")" << std::endl; + return false; + } + + // Success + m_webGLContext = context; + return true; +} + +void RetroEngine::DeinitializeWebGL() +{ + if (m_webGLContext > 0) + { + EMSCRIPTEN_RESULT result = emscripten_webgl_destroy_context(m_webGLContext); + if (result != EMSCRIPTEN_RESULT_SUCCESS) + std::cerr << "Failed to destroy WebGL context (result=" << result << ")" << std::endl; + + m_webGLContext = 0; + } +} diff --git a/src/engine/core/retro_engine.hpp b/src/engine/core/retro_engine.hpp new file mode 100644 index 000000000..167ca42bc --- /dev/null +++ b/src/engine/core/retro_engine.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 retro.ai + * This file is part of retro3 - https://github.com/retroai/retro3 + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * See the file LICENSE.txt for more information. + */ + +#pragma once + +#include + +class RetroEngine +{ +public: + RetroEngine(); + ~RetroEngine(); + + /*! + * \brief Initialize the engine + * + * \return true if successful, false otherwise + */ + bool Initialize(); + + /*! + * \brief Deinitialize the engine + */ + void Deinitialize(); + +private: + bool InitializeWebGL(); + void DeinitializeWebGL(); + + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE m_webGLContext; +}; diff --git a/src/engine/core/retro_engine_embinder.cpp b/src/engine/core/retro_engine_embinder.cpp new file mode 100644 index 000000000..3ccf83586 --- /dev/null +++ b/src/engine/core/retro_engine_embinder.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 retro.ai + * This file is part of retro3 - https://github.com/retroai/retro3 + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * See the file LICENSE.txt for more information. + */ + +#include "retro_engine.hpp" + +#include + +EMSCRIPTEN_BINDINGS(engine) +{ + emscripten::class_("RetroEngine") + .constructor<>() + .function("initialize", &RetroEngine::Initialize) + .function("deinitialize", &RetroEngine::Deinitialize); +} diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 000000000..31a6e17f2 --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,3 @@ +/build +/emsdk +/install diff --git a/tools/build-wasm.sh b/tools/build-wasm.sh new file mode 100755 index 000000000..52b248755 --- /dev/null +++ b/tools/build-wasm.sh @@ -0,0 +1,68 @@ +#!/bin/bash +################################################################################ +# +# Copyright (C) 2024 retro.ai +# This file is part of retro3 - https://github.com/retroai/retro3 +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# See the file LICENSE.txt for more information. +# +################################################################################ + +################################################################################ +# +# Helper for CI infrastructure. Sets the appropriate paths and calls CMake. +# +################################################################################ + +# Enable strict shell mode +set -o errexit +set -o nounset +set -o pipefail + +################################################################################ +# Environment paths +################################################################################ + +# Get the absolute path to the project root +PROJECT_DIRECTORY="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" + +# Directory for the engine sources +ENGINE_DIRECTORY="${PROJECT_DIRECTORY}/src/engine" + +# Directory for tooling +TOOL_DIRECTORY="${PROJECT_DIRECTORY}/tools" + +# Directory for intermediate build files +BUILD_DIRECTORY="${TOOL_DIRECTORY}/build" + +# Directory of the Emscripten SDK +EMSDK_DIRECTORY="${TOOL_DIRECTORY}/emsdk" + +# Directory to place the generated libraries +INSTALL_DIRECTORY="${TOOL_DIRECTORY}/install" + +# Ensure directories exist +mkdir -p "${BUILD_DIRECTORY}" +mkdir -p "${INSTALL_DIRECTORY}" + +################################################################################ +# Setup environment +################################################################################ + +source "${EMSDK_DIRECTORY}/emsdk_env.sh" + +################################################################################ +# Call CMake +################################################################################ + +cd "${BUILD_DIRECTORY}" + +emcmake cmake \ + "${ENGINE_DIRECTORY}" \ + -DCMAKE_INSTALL_PREFIX="${INSTALL_DIRECTORY}" \ + $(! command -v ccache &> /dev/null || echo "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache") + +cmake \ + --build "${BUILD_DIRECTORY}" \ + --target install diff --git a/tools/download-emscripten.sh b/tools/download-emscripten.sh new file mode 100755 index 000000000..d768e0b6d --- /dev/null +++ b/tools/download-emscripten.sh @@ -0,0 +1,63 @@ +#!/bin/bash +################################################################################ +# +# Copyright (C) 2024 retro.ai +# This file is part of retro3 - https://github.com/retroai/retro3 +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# See the file LICENSE.txt for more information. +# +################################################################################ + +################################################################################ +# +# Helper to download and setup the Emscripten SDK +# +################################################################################ + +# Enable strict shell mode +set -o errexit +set -o nounset +set -o pipefail + +# Emscripten configuration +EMSDK_VERSION="3.1.55" +EMSDK_REPO="https://github.com/emscripten-core/emsdk.git" + +################################################################################ +# Environment paths +################################################################################ + +# Get the absolute path to the tooling scripts +TOOL_DIRECTORY="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# Target directory for the Emscripten SDK installation +EMSDK_DIRECTORY="${TOOL_DIRECTORY}/emsdk" + +# Ensure directories exist +mkdir -p "${EMSDK_DIRECTORY}" + +################################################################################ +# Download and install Emscripten SDK +################################################################################ + +cd "${EMSDK_DIRECTORY}" + +echo "Checking if Emscripten SDK is already installed..." +if [ ! -d "${EMSDK_DIRECTORY}/.git" ]; then + echo "Emscripten SDK not found. Cloning..." + git clone "${EMSDK_REPO}" "${EMSDK_DIRECTORY}" +else + echo "Emscripten SDK found. Updating..." + git fetch origin + git reset --hard origin/main +fi + +echo "Installing Emscripten SDK version ${EMSDK_VERSION}..." +./emsdk install ${EMSDK_VERSION} +./emsdk activate ${EMSDK_VERSION} + +echo "Setting up the environment variables for the current session..." +source "./emsdk_env.sh" + +echo "Emscripten SDK setup completed successfully."