Skip to content

Commit

Permalink
UI: Support for NativeAOT
Browse files Browse the repository at this point in the history
Added Windows & Linux builds using AOT compilation
  • Loading branch information
SourMesen committed Jun 5, 2024
1 parent 99e4106 commit d208fdc
Show file tree
Hide file tree
Showing 195 changed files with 34,239 additions and 31,138 deletions.
52 changes: 29 additions & 23 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,70 +11,76 @@ jobs:
strategy:
matrix:
platform: [
{netversion: 6.x, targetframework: net6.0},
{netversion: 8.x, targetframework: net8.0}
{netversion: 6.x, targetframework: net6.0, aot: false, singleFile: true, aotString: ""},
{netversion: 8.x, targetframework: net8.0, aot: false, singleFile: true, aotString: ""},
{netversion: 8.x, targetframework: net8.0, aot: true, singleFile: false, aotString: " - AoT"}
]
fail-fast: false
runs-on: windows-latest

steps:
- name: Checkout repo
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install .NET Core
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.platform.netversion }}

- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.1
uses: microsoft/setup-msbuild@v2
with:
msbuild-architecture: x64

- name: Restore packages
run: dotnet restore /p:TargetFramework="${{ matrix.platform.targetframework }}"
run: dotnet restore -p:TargetFramework="${{ matrix.platform.targetframework }}" -r win-x64 -p:PublishAot="${{ matrix.platform.aot }}"

- name: Build Mesen
run: msbuild -nologo -v:d -clp:ForceConsoleColor -m -p:Configuration=Release -p:Platform=x64 -t:Clean,UI /p:TargetFramework="${{ matrix.platform.targetframework }}"
run: msbuild -nologo -v:d -clp:ForceConsoleColor -m -p:Configuration=Release -p:Platform=x64 -t:Clean,UI -p:TargetFramework="${{ matrix.platform.targetframework }}"

- name: Publish Mesen
run: dotnet publish -nologo --no-restore -c Release /p:Platform="Any CPU" /p:OptimizeUi="true" /p:TargetFramework="${{ matrix.platform.targetframework }}" Mesen.sln /p:PublishProfile=UI\Properties\PublishProfiles\Release.pubxml
run: dotnet publish --no-restore -c Release -p:PublishAot="${{ matrix.platform.aot }}" -p:SelfContained="${{ matrix.platform.aot }}" -p:PublishSingleFile="${{ matrix.platform.singleFile }}" -p:OptimizeUi="true" -p:Platform="Any CPU" -p:TargetFramework="${{ matrix.platform.targetframework }}" -r win-x64 Mesen.sln /p:PublishProfile=UI\Properties\PublishProfiles\Release.pubxml

- name: Upload Mesen
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Mesen (Windows - ${{ matrix.platform.targetframework }})
name: Mesen (Windows - ${{ matrix.platform.targetframework }}${{ matrix.platform.aotString }})
path: |
build/TmpReleaseBuild/Mesen.exe
linux:
strategy:
matrix:
compiler: [gcc, clang]
compiler: [gcc, clang, clang_aot]
os: [ubuntu-22.04, ubuntu-20.04]
exclude:
# This currently fails to build.
- os: ubuntu-20.04
compiler: gcc
# Don't need 2 separate AoT builds
- os: ubuntu-22.04
compiler: clang_aot
include:
- compiler: gcc
use_gcc: "USE_GCC=true"
make_flags: "USE_GCC=true"
- compiler: clang
use_gcc: ""
make_flags: ""
- compiler: clang_aot
make_flags: "USE_AOT=true"
fail-fast: false
runs-on: ${{ matrix.os }}

steps:
- name: Checkout repo
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install .NET Core
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.x

Expand All @@ -87,10 +93,10 @@ jobs:
# forcing colours makes the log a little nicer to read.
- name: Build Mesen
run: |
make -j$(nproc) -O ${{ matrix.use_gcc }} LTO=true STATICLINK=true SYSTEM_LIBEVDEV=false
make -j$(nproc) -O ${{ matrix.make_flags }} LTO=true STATICLINK=true SYSTEM_LIBEVDEV=false
- name: Upload Mesen
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Mesen (Linux - ${{ matrix.os }} - ${{ matrix.compiler }})
path: bin/linux-x64/Release/linux-x64/publish/Mesen
Expand All @@ -100,12 +106,12 @@ jobs:

steps:
- name: Checkout repo
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install .NET Core
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.x

Expand All @@ -118,7 +124,7 @@ jobs:
run: |
Linux/appimage/appimage.sh
- name: Upload Mesen (AppImage)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Mesen (Linux x64 - AppImage)
path: Mesen.AppImage
Expand All @@ -137,12 +143,12 @@ jobs:

steps:
- name: Checkout repo
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install .NET Core
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.x

Expand Down Expand Up @@ -186,7 +192,7 @@ jobs:
ditto -c -k --sequesterRsrc --keepParent bin/osx-${{ matrix.platform.arch }}/Release/osx-${{ matrix.platform.arch }}/publish/Mesen.app bin/osx-${{ matrix.platform.arch }}/Release/Mesen.app.zip
- name: Upload Mesen
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Mesen (macOS - ${{ matrix.platform.os }})
path: bin/osx-${{ matrix.platform.arch }}/Release/Mesen.app.zip
8 changes: 4 additions & 4 deletions COMPILING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@

To build under Linux you need a version of Clang or GCC that supports C++17.

Additionally, SDL2 and the [.NET 6 SDK](https://learn.microsoft.com/en-us/dotnet/core/install/linux) must also be installed.
Additionally, SDL2 and the [.NET 8 SDK](https://learn.microsoft.com/en-us/dotnet/core/install/linux) must also be installed.

Once SDL2 and the .NET 6 SDK are installed, run `make` to compile with Clang.
Once SDL2 and the .NET 8 SDK are installed, run `make` to compile with Clang.
To compile with GCC instead, use `USE_GCC=true make`.
**Note:** Mesen usually runs faster when built with Clang instead of GCC.


## macOS

To build macOS, install SDL2 (i.e via Homebrew) and the [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0).
To build macOS, install SDL2 (i.e via Homebrew) and the [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0).

Once SDL2 and the .NET 6 SDK are installed, run `make`.
Once SDL2 and the .NET 8 SDK are installed, run `make`.
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,31 @@ Mesen is a multi-system emulator (NES, SNES, Game Boy, Game Boy Advance, PC Engi

[![Mesen](https://github.com/SourMesen/Mesen2/actions/workflows/build.yml/badge.svg)](https://github.com/SourMesen/Mesen2/actions/workflows/build.yml)

Latest development builds:
#### <ins>Native builds</ins> ####

* [Windows 10 / 11 (.NET 8)](https://nightly.link/SourMesen/Mesen2/workflows/build/master/Mesen%20%28Windows%20-%20net8.0%29.zip)
These builds don't require .NET to be installed and offer improved start up times.

* [Windows 10 / 11](https://nightly.link/SourMesen/Mesen2/workflows/build/master/Mesen%20%28Windows%20-%20net8.0%20-%20AoT%29.zip)
* [Linux](https://nightly.link/SourMesen/Mesen2/workflows/build/master/Mesen%20%28Linux%20-%20ubuntu-20.04%20-%20clang_aot%29.zip) (requires **SDL2**)

#### <ins>.NET builds</ins> ####

These builds require **.NET 8** to be installed (except the Windows 7 build which requires .NET 6).
For Linux and macOS, **SDL2** must also be installed.

* [Windows 10 / 11](https://nightly.link/SourMesen/Mesen2/workflows/build/master/Mesen%20%28Windows%20-%20net8.0%29.zip)
* [Windows 7 / 8 (.NET 6)](https://nightly.link/SourMesen/Mesen2/workflows/build/master/Mesen%20%28Windows%20-%20net6.0%29.zip)
* [macOS - Intel](https://nightly.link/SourMesen/Mesen2/workflows/build/master/Mesen%20%28macOS%20-%20macos-12%29.zip)
* [macOS - Apple Silicon](https://nightly.link/SourMesen/Mesen2/workflows/build/master/Mesen%20%28macOS%20-%20macos-14%29.zip)
* [Linux](https://nightly.link/SourMesen/Mesen2/workflows/build/master/Mesen%20%28Linux%20-%20ubuntu-20.04%20-%20clang%29.zip)
* [Linux - AppImage](https://nightly.link/SourMesen/Mesen2/workflows/build/master/Mesen%20(Linux%20x64%20-%20AppImage).zip)

#### Notes / limitations
#### <ins>Notes / limitations</ins> ####

**macOS**: The macOS build still has a number of limitations (e.g no gamepad support).

**SteamOS**: See [SteamOS.md](SteamOS.md)

## Requirements

To run Mesen, the following prerequisites must be installed:

**Windows 10 / 11**: [.NET 8 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
**Windows 7 / 8**: [.NET 6 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) (.NET 8 is not supported on Windows 7 / 8)
**Linux**: [.NET 8 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/8.0), SDL2
**macOS**: [.NET 8 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/8.0), SDL2

## Compiling

See [COMPILING.md](COMPILING.md)
Expand Down
4 changes: 2 additions & 2 deletions UI/App.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@
</Application.Resources>

<Application.DataTemplates>
<dbg:DebuggerViewLocator />

<DataTemplate DataType="x:Enum">
<TextBlock Text="{Binding, Converter={StaticResource Enum}}" />
</DataTemplate>
</Application.DataTemplates>

<Application.Styles>
<FluentTheme />
<StyleInclude Source="/Styles/MesenStyles.xaml" />
</Application.Styles>

<NativeMenu.Menu>
Expand Down
13 changes: 5 additions & 8 deletions UI/App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,23 @@ public class App : Application

public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
StyleHelper.LoadStartupStyles();
if(Design.IsDesignMode) {
StyleHelper.ApplyTheme(MesenTheme.Light);
if(Design.IsDesignMode || ShowConfigWindow) {
RequestedThemeVariant = ThemeVariant.Light;
StyleHelper.LoadDebuggerStyles();
} else {
RequestedThemeVariant = ConfigManager.Config.Preferences.Theme == MesenTheme.Dark ? ThemeVariant.Dark : ThemeVariant.Light;
}

AvaloniaXamlLoader.Load(this);
ResourceHelper.LoadResources();
}

public override void OnFrameworkInitializationCompleted()
{
if(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
if(ShowConfigWindow) {
StyleHelper.ApplyTheme(MesenTheme.Light);
new PreferencesConfig().InitializeFontDefaults();
RequestedThemeVariant = ThemeVariant.Light;
desktop.MainWindow = new SetupWizardWindow();
} else {
RequestedThemeVariant = ConfigManager.Config.Preferences.Theme == MesenTheme.Dark ? ThemeVariant.Dark : ThemeVariant.Light;
desktop.MainWindow = new MainWindow();
}
}
Expand Down
4 changes: 2 additions & 2 deletions UI/Config/BaseConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ public T Clone()

public bool IsIdentical(T other)
{
string a = JsonSerializer.Serialize(this, this.GetType(), JsonHelper.Options);
string b = JsonSerializer.Serialize(other, this.GetType(), JsonHelper.Options);
string a = JsonSerializer.Serialize(this, this.GetType(), MesenSerializerContext.Default);
string b = JsonSerializer.Serialize(other, this.GetType(), MesenSerializerContext.Default);
return a == b;
}
}
Expand Down
40 changes: 36 additions & 4 deletions UI/Config/BaseWindowConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ namespace Mesen.Config
{
public class BaseWindowConfig<T> : BaseConfig<T> where T : class
{
public PixelSize WindowSize { get; set; } = new PixelSize(0, 0);
public PixelPoint WindowLocation { get; set; } = new PixelPoint(0, 0);
public MesenSize WindowSize { get; set; } = new PixelSize(0, 0);
public MesenPoint WindowLocation { get; set; } = new PixelPoint(0, 0);
public bool WindowIsMaximized { get; set; } = false;

private PixelRect _restoreBounds;
Expand Down Expand Up @@ -48,10 +48,10 @@ public void LoadWindowSettings(Window wnd)
} else {
//Window top left corner is offscreen, adjust position
if(WindowLocation.Y < screen.WorkingArea.Position.Y) {
WindowLocation = WindowLocation.WithY(screen.WorkingArea.Position.Y);
WindowLocation = ((PixelPoint)WindowLocation).WithY(screen.WorkingArea.Position.Y);
}
if(WindowLocation.X < screen.WorkingArea.Position.X) {
WindowLocation = WindowLocation.WithX(screen.WorkingArea.Position.X);
WindowLocation = ((PixelPoint)WindowLocation).WithX(screen.WorkingArea.Position.X);
}

wndRect = new PixelRect(WindowLocation, WindowSize);
Expand Down Expand Up @@ -109,4 +109,36 @@ private void UpdateRestoreBounds(Window wnd)
}
}
}

public struct MesenSize
{
public int Width { get; set; }
public int Height { get; set; }

public static implicit operator MesenSize(PixelSize size)
{
return new MesenSize { Width = size.Width, Height = size.Height };
}

public static implicit operator PixelSize(MesenSize size)
{
return new PixelSize(size.Width, size.Height);
}
}

public struct MesenPoint
{
public int X { get; set; }
public int Y { get; set; }

public static implicit operator MesenPoint(PixelPoint point)
{
return new MesenPoint { X = point.X, Y = point.Y };
}

public static implicit operator PixelPoint(MesenPoint point)
{
return new PixelPoint(point.X, point.Y);
}
}
}
6 changes: 3 additions & 3 deletions UI/Config/CheatCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class CheatCodes
{
private static string FilePath { get { return Path.Combine(ConfigManager.CheatFolder, EmuApi.GetRomInfo().GetRomName() + ".json"); } }

public List<CheatCode> Cheats = new List<CheatCode>();
public List<CheatCode> Cheats { get; set; } = new List<CheatCode>();

public static CheatCodes LoadCheatCodes()
{
Expand All @@ -27,7 +27,7 @@ private static CheatCodes Deserialize(string path)

if(File.Exists(path)) {
try {
cheats = JsonSerializer.Deserialize<CheatCodes>(File.ReadAllText(path), JsonHelper.Options) ?? new CheatCodes();
cheats = (CheatCodes?)JsonSerializer.Deserialize(File.ReadAllText(path), typeof(CheatCodes), MesenSerializerContext.Default) ?? new CheatCodes();
} catch { }
}

Expand All @@ -38,7 +38,7 @@ public void Save()
{
try {
if(Cheats.Count > 0) {
FileHelper.WriteAllText(CheatCodes.FilePath, JsonSerializer.Serialize(this, JsonHelper.Options));
FileHelper.WriteAllText(CheatCodes.FilePath, JsonSerializer.Serialize(this, typeof(CheatCodes), MesenSerializerContext.Default));
} else {
if(File.Exists(CheatCodes.FilePath)) {
File.Delete(CheatCodes.FilePath);
Expand Down
4 changes: 2 additions & 2 deletions UI/Config/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public static Configuration Deserialize(string configFile)

try {
string fileData = File.ReadAllText(configFile);
config = JsonSerializer.Deserialize<Configuration>(fileData, JsonHelper.Options) ?? Configuration.CreateConfig();
config = (Configuration?)JsonSerializer.Deserialize(fileData, typeof(Configuration), MesenSerializerContext.Default) ?? Configuration.CreateConfig();
config._fileData = fileData;
} catch {
try {
Expand All @@ -260,7 +260,7 @@ public static Configuration Deserialize(string configFile)
public void Serialize(string configFile)
{
try {
string cfgData = JsonSerializer.Serialize(this, typeof(Configuration), JsonHelper.Options);
string cfgData = JsonSerializer.Serialize(this, typeof(Configuration), MesenSerializerContext.Default);
if(_fileData != cfgData && !Design.IsDesignMode) {
FileHelper.WriteAllText(configFile, cfgData);
_fileData = cfgData;
Expand Down
1 change: 0 additions & 1 deletion UI/Config/Debugger/AssemblerConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ namespace Mesen.Config
{
public class AssemblerConfig : BaseWindowConfig<AssemblerConfig>
{
public int Zoom = 100;
}
}
Loading

0 comments on commit d208fdc

Please sign in to comment.