Skip to content

Commit

Permalink
Improve eval times by moving "EvalProgram" to its own project
Browse files Browse the repository at this point in the history
Before, "EvalProgram" was compiled and injected into the running game by running MSBuild on the "Eval" target. This took a couple of seconds.

Now, "EvalProgram" is in its own project. Compilation and injection into the running game is done by building the "Eval" project in Visual Studio. It takes maybe a second.

The performance improvement seems to come from compilation via Visual Studio's UI being faster than compilation via invoking MSBuild.exe.
  • Loading branch information
rigdern committed Sep 11, 2023
1 parent 7f8296b commit cf8248d
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 34 deletions.
8 changes: 8 additions & 0 deletions Eval/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This file contains project properties used by the build. -->
<Project>
<PropertyGroup>
<ImportBSMTTargets>True</ImportBSMTTargets>
<BSMTProjectType>BSIPA</BSMTProjectType>
</PropertyGroup>
</Project>
155 changes: 155 additions & 0 deletions Eval/Eval.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{AD941956-A7E0-4DF6-896A-2171FA294784}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Eval</RootNamespace>
<AssemblyName>Eval</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<LocalRefsDir Condition="Exists('..\Refs')">..\Refs</LocalRefsDir>
<BeatSaberDir>$(LocalRefsDir)</BeatSaberDir>
<AppOutputBase>$(MSBuildProjectDirectory)\</AppOutputBase>
<!--<PathMap>$(AppOutputBase)=X:\$(AssemblyName)\</PathMap>-->
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="$(DefineConstants.Contains('CIBuild')) OR '$(NCrunch)' == '1'">
<DisableCopyToPlugins>True</DisableCopyToPlugins>
</PropertyGroup>
<PropertyGroup Condition="'$(NCrunch)' == '1'">
<DisableCopyToPlugins>True</DisableCopyToPlugins>
<DisableZipRelease>True</DisableZipRelease>
</PropertyGroup>
<PropertyGroup>
<!--
Disable various Beat Saber Modding Tools features that aren't needed for
the "Eval" project. Perhaps the modding tools aren't needed at all. The
main feature I'm interested in is the "Beat Saber Reference Manager" so
the project can reference Beat Saber's dependencies and other Beat Saber
plugins.
-->
<DisableCopyToGame>True</DisableCopyToGame>
<DisableCopyToPlugins>True</DisableCopyToPlugins>
<DisableZipRelease>True</DisableZipRelease>
</PropertyGroup>
<ItemGroup>
<Reference Include="BeatmapCore">
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\BeatmapCore.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="InfiniteBeatSaber">
<HintPath>$(BeatSaberDir)\Plugins\InfiniteBeatSaber.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Main">
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\Main.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="HMLib">
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\HMLib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="HMUI">
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\HMUI.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="IPA.Loader">
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\IPA.Loader.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.TextMeshPro">
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\Unity.TextMeshPro.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine">
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UIElementsModule">
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UIElementsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UIModule">
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.VRModule">
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.VRModule.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="EvalProgram.cs" />
<Compile Include="Plugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="manifest.json" />
</ItemGroup>
<ItemGroup>
<None Include="Directory.Build.props" Condition="Exists('Directory.Build.props')" />
<None Include="Eval.csproj.user" Condition="Exists('Eval.csproj.user')" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BeatSaberModdingTools.Tasks">
<Version>2.0.0-beta1</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!--
In this project, the intention of the "build" command is actually "eval the
code in the game".
A problematic case is that, if the source files are unchanged, then the
"build" command will be a no-op. No code will be evaled in the game which
violates the user's expectation. This is because both the "Build" and "Eval"
targets will be skipped.
"TouchSource", a pre-build target, is the workaround. It touches the source
file, making it look dirty, and consequently forcing the "Build" and "Eval"
targets to run.
-->
<Target Name="TouchSource" BeforeTargets="Build">
<Exec Command="powershell (Get-Item $(ProjectDir)EvalProgram.cs).LastWriteTime = Get-Date" />
</Target>
<Target Name="Eval" AfterTargets="Build">
<Exec Command="powershell.exe -ExecutionPolicy Bypass -File &quot;$(SolutionDir)debug-tools\repl\eval.ps1&quot; -dllPath &quot;$(TargetPath)&quot;" />
</Target>
</Project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Newtonsoft.Json;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand All @@ -14,10 +13,10 @@ namespace InfiniteBeatSaber.DebugTools
{
public static class EvalProgram
{
// `EvalMain` is intended to be used like a REPL. While Beat Saber is running, write
// code in `EvalMain` and then run `debug-tools\repl\msbuildEval.vbs` to have it
// injected into and executed in the running Beat Saber game. Only works in debug
// builds.
// `EvalMain` is intended to be used like a REPL. While Beat Saber is
// running, write code in `EvalMain` and then build the "Eval" project
// to have it injected into and executed in the running Beat Saber game.
// Only works in debug builds.
public static void EvalMain(IDictionary<string, object> state)
{
// Example: Log the name of a Beat Saber pack.
Expand Down
35 changes: 35 additions & 0 deletions Eval/Plugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using IPA;
using IPALogger = IPA.Logging.Logger;

namespace Eval
{
[Plugin(RuntimeOptions.SingleStartInit)]
public class Plugin
{
internal static Plugin Instance { get; private set; }
/// <summary>
/// Use to send log messages through BSIPA.
/// </summary>
internal static IPALogger Log { get; private set; }

[Init]
public Plugin(IPALogger logger)
{
Instance = this;
Log = logger;
}

[OnStart]
public void OnApplicationStart()
{
Plugin.Log.Info("OnApplicationStart");
}

[OnExit]
public void OnApplicationQuit()
{

}

}
}
36 changes: 36 additions & 0 deletions Eval/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Eval")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HP")]
[assembly: AssemblyProduct("Eval")]
[assembly: AssemblyCopyright("Copyright © HP 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("406a47c5-25bf-4c86-8f2c-4cba825b2052")]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.0.1")]
[assembly: AssemblyFileVersion("0.0.1")]
12 changes: 12 additions & 0 deletions Eval/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "https://raw.githubusercontent.com/bsmg/BSIPA-MetadataFileSchema/master/Schema.json",
"id": "Eval",
"name": "Eval",
"author": "",
"version": "0.0.1",
"description": "",
"gameVersion": "1.31.1",
"dependsOn": {
"BSIPA": "^4.2.0"
}
}
4 changes: 4 additions & 0 deletions InfiniteBeatSaber.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ VisualStudioVersion = 17.6.33829.357
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfiniteBeatSaber", "InfiniteBeatSaber\InfiniteBeatSaber.csproj", "{3FEFBC8B-6254-4F16-A613-1DEDE2E32B29}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Eval", "Eval\Eval.csproj", "{AD941956-A7E0-4DF6-896A-2171FA294784}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -15,6 +17,8 @@ Global
{3FEFBC8B-6254-4F16-A613-1DEDE2E32B29}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3FEFBC8B-6254-4F16-A613-1DEDE2E32B29}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3FEFBC8B-6254-4F16-A613-1DEDE2E32B29}.Release|Any CPU.Build.0 = Release|Any CPU
{AD941956-A7E0-4DF6-896A-2171FA294784}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD941956-A7E0-4DF6-896A-2171FA294784}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
8 changes: 0 additions & 8 deletions InfiniteBeatSaber/InfiniteBeatSaber.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@
<Compile Include="$(SolutionDir)Generated\BuildConstants\Release.cs" Link="BuildConstants\Release.cs" Condition="'$(Configuration)' == 'Release'" />
<Compile Include="FloatComparison.cs" />
<Compile Include="DebugTools\Eval.cs" Condition="'$(Configuration)' == 'Debug'" />
<Compile Include="DebugTools\EvalProgram.cs" Condition="'$(Configuration)' == 'Debug'" />
<Compile Include="DebugTools\RemixVisualizer.cs" Condition="'$(Configuration)' == 'Debug'" />
<Compile Include="DebugTools\WebSocketServer.cs" Condition="'$(Configuration)' == 'Debug'" />
<Compile Include="Extensions\AudioClipExtensions.cs" />
Expand Down Expand Up @@ -200,11 +199,4 @@
<Target Name="GenerateReleaseBuildConstants" BeforeTargets="PreBuildEvent" Condition="'$(Configuration)'=='Release'">
<Exec Command="powershell.exe -ExecutionPolicy Bypass -File &quot;$(SolutionDir)build-tools\genBuildConstants.ps1&quot;" />
</Target>
<Target Name="Eval" DependsOnTargets="ResolveReferences">
<PropertyGroup>
<EvalProgramDllPath>$(ProjectDir)$(OutputPath)EvalProgram.dll</EvalProgramDllPath>
</PropertyGroup>
<Csc TargetType="library" Sources="$(ProjectDir)DebugTools\EvalProgram.cs" OutputAssembly="$(EvalProgramDllPath)" References="@(ReferencePath);$(BeatSaberDir)\Plugins\InfiniteBeatSaber.dll" />
<Exec Command="powershell.exe -ExecutionPolicy Bypass -File &quot;$(SolutionDir)debug-tools\repl\eval.ps1&quot; -dllPath &quot;$(EvalProgramDllPath)&quot;" />
</Target>
</Project>
20 changes: 0 additions & 20 deletions debug-tools/repl/msbuildEval.vbs

This file was deleted.

0 comments on commit cf8248d

Please sign in to comment.