mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
192 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40b10f0dcc | ||
|
|
5885549c78 | ||
|
|
89e16d5ba9 | ||
|
|
2afef1480e | ||
|
|
76189579c7 | ||
|
|
114c2bee62 | ||
|
|
ce96331ec4 | ||
|
|
14b17aff6d | ||
|
|
65ed19fa4e | ||
|
|
30cd9eb527 | ||
|
|
3f556814a5 | ||
|
|
17662baaf7 | ||
|
|
dc1464b462 | ||
|
|
48654ac424 | ||
|
|
d9ea1079f7 | ||
|
|
cb384b8242 | ||
|
|
21581df93d | ||
|
|
df98bca4bc | ||
|
|
02b64b7386 | ||
|
|
36e5f10511 | ||
|
|
93d14d55c7 | ||
|
|
c20343601d | ||
|
|
52d3376c9e | ||
|
|
a417a8fd99 | ||
|
|
627856e207 | ||
|
|
aa5cca4c7f | ||
|
|
5b06066fcb | ||
|
|
736e46cd82 | ||
|
|
539d0563b8 | ||
|
|
d9740e3a4f | ||
|
|
89b6bcd8e2 | ||
|
|
7b245260e3 | ||
|
|
68f8d00931 | ||
|
|
57ad191d02 | ||
|
|
8cecdcc8de | ||
|
|
de7cdec86e | ||
|
|
8ffa85c266 | ||
|
|
e5be11458e | ||
|
|
9464ccb500 | ||
|
|
af7f4ec8e7 | ||
|
|
de2dafe507 | ||
|
|
dd41a7ce44 | ||
|
|
c25f6c5e98 | ||
|
|
2799de33c5 | ||
|
|
1fea48fbf4 | ||
|
|
636e287fc5 | ||
|
|
d43c3f2caf | ||
|
|
a1dddf6af1 | ||
|
|
5f1327808d | ||
|
|
d78e3ce157 | ||
|
|
6d31d5ba24 | ||
|
|
773357a80d | ||
|
|
63ef667167 | ||
|
|
e8f2972f69 | ||
|
|
ad056e0b45 | ||
|
|
6f2c45aab7 | ||
|
|
9a3aad4630 | ||
|
|
5e55effc73 | ||
|
|
e308b89fe6 | ||
|
|
68609a94d8 | ||
|
|
76727cec5f | ||
|
|
bb81d88653 | ||
|
|
9b02a4e718 | ||
|
|
2e5856b54d | ||
|
|
9802963933 | ||
|
|
43337a3743 | ||
|
|
c7ba63ed8e | ||
|
|
5ec8589f40 | ||
|
|
077ad1929e | ||
|
|
a74f755692 | ||
|
|
e1b70982f0 | ||
|
|
23de5c4044 | ||
|
|
0498036f1c | ||
|
|
dd86bf980d | ||
|
|
d7abbad717 | ||
|
|
7826e9e365 | ||
|
|
5476cdeff9 | ||
|
|
c1737a540f | ||
|
|
ddfa12808c | ||
|
|
e3fb7e620d | ||
|
|
c94e1742cb | ||
|
|
27fd737e6a | ||
|
|
568880d09d | ||
|
|
7f2ec17651 | ||
|
|
39e30531e2 | ||
|
|
e26bda4c8f | ||
|
|
9137ab2c45 | ||
|
|
5af03247a7 | ||
|
|
352ae60f42 | ||
|
|
d31db257ea | ||
|
|
d548bce347 | ||
|
|
3246ad2d92 | ||
|
|
b4615b9c7a | ||
|
|
788e9386fd | ||
|
|
095c5f58d9 | ||
|
|
b2bf5f9781 | ||
|
|
b936a77207 | ||
|
|
7da11b01c9 | ||
|
|
4f21c8c9b2 | ||
|
|
4747e5a05a | ||
|
|
cfefd760e2 | ||
|
|
d161c3b3b8 | ||
|
|
53e1222b6b | ||
|
|
5f073dff35 | ||
|
|
8de8f5dc7f | ||
|
|
e9dc40be24 | ||
|
|
95d5f7eec1 | ||
|
|
497ca865dc | ||
|
|
b4a0dad67a | ||
|
|
f1c30128d6 | ||
|
|
ad31ec64d3 | ||
|
|
fe35a24e88 | ||
|
|
e875d89e14 | ||
|
|
e41165f9dd | ||
|
|
0414cde339 | ||
|
|
8580ab93a6 | ||
|
|
1a0dab4526 | ||
|
|
dff5b5fe95 | ||
|
|
d579c68082 | ||
|
|
2c3cbaf3b9 | ||
|
|
4c961c627c | ||
|
|
b6b992ba49 | ||
|
|
20d97ff855 | ||
|
|
9d45357ca9 | ||
|
|
b0636c351c | ||
|
|
dab7ba13f5 | ||
|
|
fde2a83f03 | ||
|
|
35fb4cc832 | ||
|
|
6bbeaeeba6 | ||
|
|
f2d74df4d7 | ||
|
|
3175a6cb2b | ||
|
|
465a1fb5bd | ||
|
|
c41d63be27 | ||
|
|
28f7d6497e | ||
|
|
83a1098476 | ||
|
|
107b7c5077 | ||
|
|
879ec72847 | ||
|
|
1bcf04ef76 | ||
|
|
899eef397c | ||
|
|
6aa4fb9594 | ||
|
|
a9ba9192ac | ||
|
|
b8ff394f73 | ||
|
|
b267cd6fb4 | ||
|
|
83ad6042a7 | ||
|
|
46b65260c4 | ||
|
|
663b83821a | ||
|
|
64baee0a22 | ||
|
|
dbde8023ed | ||
|
|
83279ff285 | ||
|
|
14439784dd | ||
|
|
80cad0cd8f | ||
|
|
b15d960c69 | ||
|
|
cd59027089 | ||
|
|
015bf3318b | ||
|
|
3f19d25018 | ||
|
|
fe19ff9bd5 | ||
|
|
c95b4320cf | ||
|
|
602d7833a1 | ||
|
|
4755cb5747 | ||
|
|
7bc0ffb711 | ||
|
|
e49515956a | ||
|
|
f3a3f564e1 | ||
|
|
8b7fbfa646 | ||
|
|
bb4c4ed302 | ||
|
|
c9009342b6 | ||
|
|
3a337e4842 | ||
|
|
e788325cdb | ||
|
|
9a0e3b6b02 | ||
|
|
37eabbabc2 | ||
|
|
ab775af7cd | ||
|
|
8ac5fc58d2 | ||
|
|
37c7aa544e | ||
|
|
7542b1ca16 | ||
|
|
8235bd8478 | ||
|
|
1657a49c1c | ||
|
|
669b515ce6 | ||
|
|
8478e62a3e | ||
|
|
034728258c | ||
|
|
b0fec0fd76 | ||
|
|
665294bee8 | ||
|
|
4b04081749 | ||
|
|
d3a9199b8e | ||
|
|
6fb9ff7554 | ||
|
|
feb9e1db69 | ||
|
|
613705613b | ||
|
|
80a053c0a9 | ||
|
|
657455dae0 | ||
|
|
8ae35e12ee | ||
|
|
4e2c0e431b | ||
|
|
9c41f19eaf | ||
|
|
f75ce13f00 | ||
|
|
ac45a0a64b |
@@ -19,7 +19,7 @@ resharper_place_field_attribute_on_same_line = if_owner_is_single_line
|
||||
resharper_wrap_chained_binary_patterns = chop_if_long
|
||||
resharper_wrap_chained_method_calls = chop_if_long
|
||||
|
||||
[*.{csproj,xml,yml,dll.config,targets,props}]
|
||||
[*.{csproj,xml,yml,dll.config,targets,props,slnx}]
|
||||
indent_size = 2
|
||||
|
||||
[nuget.config]
|
||||
|
||||
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -8,5 +8,3 @@
|
||||
*Command.cs @moonheart08
|
||||
*Commands.cs @moonheart08
|
||||
|
||||
# Physics
|
||||
**/Robust.Shared/Physics/** @metalgearsloth
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
2
.github/workflows/build-docfx.yml
vendored
2
.github/workflows/build-docfx.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
8
.github/workflows/build-test.yml
vendored
8
.github/workflows/build-test.yml
vendored
@@ -22,12 +22,10 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore /p:WarningsAsErrors=nullable
|
||||
- name: Robust.UnitTesting
|
||||
run: dotnet test --no-build Robust.UnitTesting/Robust.UnitTesting.csproj -- NUnit.ConsoleOut=0
|
||||
- name: Robust.Analyzers.Tests
|
||||
run: dotnet test --no-build Robust.Analyzers.Tests/Robust.Analyzers.Tests.csproj -- NUnit.ConsoleOut=0
|
||||
- name: Run tests
|
||||
run: dotnet test --no-build -- NUnit.ConsoleOut=0
|
||||
|
||||
2
.github/workflows/publish-client.yml
vendored
2
.github/workflows/publish-client.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Package client
|
||||
run: Tools/package_client_build.py
|
||||
|
||||
4
.github/workflows/test-content.yml
vendored
4
.github/workflows/test-content.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
- name: Disable submodule autoupdate
|
||||
run: touch BuildChecker/DISABLE_SUBMODULE_AUTOUPDATE
|
||||
|
||||
@@ -30,6 +30,8 @@ jobs:
|
||||
git fetch origin ${{ github.sha }}
|
||||
git checkout FETCH_HEAD
|
||||
git submodule update --init --recursive
|
||||
- name: Replace global.json
|
||||
run: cp RobustToolbox/global.json .
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
|
||||
27
Directory.Build.props
Normal file
27
Directory.Build.props
Normal file
@@ -0,0 +1,27 @@
|
||||
<Project>
|
||||
<ItemDefinitionGroup>
|
||||
<PackageReference>
|
||||
<!--
|
||||
Make all packages used by RT default to having everything private except what's needed to run the game (runtime, native)
|
||||
This is to avoid having them leak to content, as they are primarily an implementation detail.
|
||||
Where necessary, this can be overriden by doing PrivateAssets="none" on the PackageReference entry.
|
||||
-->
|
||||
<PrivateAssets>compile;contentFiles;build;buildMultitargeting;buildTransitive;analyzers</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
Disable transitive project references.
|
||||
This is just to avoid venues of leaky implementation details into content.
|
||||
Being strict is always good.
|
||||
-->
|
||||
<DisableTransitiveProjectReferences>True</DisableTransitiveProjectReferences>
|
||||
|
||||
<!--
|
||||
Shut up broken package pruning warnings.
|
||||
We need these unpruneable packages because of transitive dependency pinning.
|
||||
-->
|
||||
<NoWarn>$(NoWarn);NU1510</NoWarn>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -10,73 +10,75 @@
|
||||
<ManagePackageVersionsCentrally />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0" />
|
||||
<PackageVersion Include="JetBrains.Profiler.Api" Version="1.4.8" />
|
||||
<PackageVersion Include="JetBrains.Annotations" Version="2025.2.4" />
|
||||
<PackageVersion Include="JetBrains.Profiler.Api" Version="1.4.10" />
|
||||
<PackageVersion Include="Linguini.Bundle" Version="0.8.1" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzer.Testing" Version="1.1.2" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.2" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.2" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.12.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.12.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.12.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.12.0" />
|
||||
<PackageVersion Include="Microsoft.CodeCoverage" Version="17.12.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Features" Version="5.0.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="5.0.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="5.0.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="5.0.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="5.0.0" />
|
||||
<PackageVersion Include="Microsoft.CodeCoverage" Version="18.0.1" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.DotNet.RemoteExecutor" Version="8.0.0-beta.24059.4" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.ILVerification" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.ILVerification" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||
<PackageVersion Include="Microsoft.NET.ILLink.Tasks" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageVersion Include="Microsoft.NET.ILLink.Tasks" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
||||
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageVersion Include="Moq" Version="4.20.72" />
|
||||
<PackageVersion Include="NUnit" Version="4.3.2" />
|
||||
<PackageVersion Include="NUnit.Analyzers" Version="4.5.0" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
<PackageVersion Include="NUnit" Version="4.4.0" />
|
||||
<PackageVersion Include="NUnit.Analyzers" Version="4.11.2" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="5.2.0" />
|
||||
<PackageVersion Include="Nett" Version="0.15.0" />
|
||||
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2" />
|
||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
|
||||
<PackageVersion Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
|
||||
<PackageVersion Include="Pidgin" Version="3.3.0" />
|
||||
<PackageVersion Include="Pidgin" Version="3.5.1" />
|
||||
<PackageVersion Include="Robust.Natives" Version="0.2.3" />
|
||||
<PackageVersion Include="Robust.Natives.Zstd" Version="0.1.1-zstd1.5.7" />
|
||||
<PackageVersion Include="Robust.Natives.Cef" Version="131.3.5" />
|
||||
<PackageVersion Include="Robust.Shared.AuthLib" Version="0.1.2" />
|
||||
<PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
|
||||
<PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.2" />
|
||||
<PackageVersion Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.10" />
|
||||
<PackageVersion Include="Serilog" Version="4.2.0" />
|
||||
<PackageVersion Include="Serilog" Version="4.3.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Loki" Version="4.0.0-beta3" />
|
||||
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.1" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.12" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.2.0" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.2.2" />
|
||||
<PackageVersion Include="SpaceWizards.Sdl" Version="1.0.0" />
|
||||
<PackageVersion Include="SpaceWizards.Sdl" Version="1.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.1.0" />
|
||||
<PackageVersion Include="SpaceWizards.Sodium" Version="0.2.1" />
|
||||
<PackageVersion Include="SpaceWizards.Sodium" Version="0.3.0" />
|
||||
<PackageVersion Include="SpaceWizards.Fontconfig.Interop" Version="1.0.0" />
|
||||
<PackageVersion Include="libsodium" Version="1.0.20.1" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.8" />
|
||||
<PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.26100.1" />
|
||||
<PackageVersion Include="TerraFX.Interop.Xlib" Version="6.4.0" />
|
||||
<PackageVersion Include="System.Management" Version="10.0.0" />
|
||||
<PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.26100.5" />
|
||||
<PackageVersion Include="TerraFX.Interop.Xlib" Version="6.4.0.2" />
|
||||
<!-- Intentionally kept back, there is a bug that breaks audio for systems without AVX instructions. And the fix is not yet published. -->
|
||||
<PackageVersion Include="VorbisPizza" Version="1.3.0" />
|
||||
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
|
||||
<PackageVersion Include="prometheus-net" Version="8.2.1" />
|
||||
<PackageVersion Include="prometheus-net.DotNetRuntime" Version="4.4.0" />
|
||||
<PackageVersion Include="prometheus-net.DotNetRuntime" Version="4.4.1" />
|
||||
<PackageVersion Include="PolySharp" Version="1.15.0" />
|
||||
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.1" />
|
||||
<!-- Transitive deps that we need to pin versions for to avoid NuGet warnings. -->
|
||||
<PackageVersion Include="System.Formats.Asn1" Version="9.0.0" />
|
||||
<PackageVersion Include="System.Reflection.Metadata" Version="9.0.0" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.12.0" />
|
||||
<PackageVersion Include="System.Formats.Asn1" Version="10.0.0" />
|
||||
<PackageVersion Include="System.Reflection.Metadata" Version="10.0.0" />
|
||||
<PackageVersion Include="System.Text.Json" Version="10.0.0" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
6
Imports/Benchmarks.props
Normal file
6
Imports/Benchmarks.props
Normal file
@@ -0,0 +1,6 @@
|
||||
<Project>
|
||||
<!-- Include this .props file from content to get access to the APIs in these projects. -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Benchmarks\Robust.Benchmarks.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
6
Imports/Client.props
Normal file
6
Imports/Client.props
Normal file
@@ -0,0 +1,6 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Avalonia.Base\Avalonia.Base.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
6
Imports/Lidgren.props
Normal file
6
Imports/Lidgren.props
Normal file
@@ -0,0 +1,6 @@
|
||||
<Project>
|
||||
<!-- Include this .props file from content to get access to the APIs in these projects. -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
6
Imports/Packaging.props
Normal file
6
Imports/Packaging.props
Normal file
@@ -0,0 +1,6 @@
|
||||
<Project>
|
||||
<!-- Include this .props file from content to get access to the APIs in these projects. -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Packaging\Robust.Packaging.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
6
Imports/Server.props
Normal file
6
Imports/Server.props
Normal file
@@ -0,0 +1,6 @@
|
||||
<Project>
|
||||
<!-- Include this .props file from content to get access to the APIs in these projects. -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Server\Robust.Server.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
8
Imports/Shared.props
Normal file
8
Imports/Shared.props
Normal file
@@ -0,0 +1,8 @@
|
||||
<Project>
|
||||
<!-- Include this .props file from content to get access to the APIs in these projects. -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\NetSerializer\NetSerializer\NetSerializer.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
9
Imports/Testing.props
Normal file
9
Imports/Testing.props
Normal file
@@ -0,0 +1,9 @@
|
||||
<Project>
|
||||
<!-- Include this .props file from content to get access to the APIs in these projects. -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Shared.Maths.Testing\Robust.Shared.Maths.Testing.csproj" />
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Shared.Testing\Robust.Shared.Testing.csproj" />
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Server.Testing\Robust.Server.Testing.csproj" />
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.UnitTesting\Robust.UnitTesting.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
14
Imports/WebView.props
Normal file
14
Imports/WebView.props
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!-- If you are using Robust.Client.WebView, import this to depend on it. -->
|
||||
|
||||
<Import Condition="'$(_RTMacOSAppBundle_targets_imported)' != 'True'"
|
||||
Project="$(MSBuildThisFileDirectory)\..\MSBuild\MacOSAppBundle.targets" />
|
||||
|
||||
<PropertyGroup>
|
||||
<_RTMacOSAppBundle_for_webview>--webview</_RTMacOSAppBundle_for_webview>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Robust.Client.WebView\Robust.Client.WebView.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
24
MSBuild/MacOSAppBundle.targets
Normal file
24
MSBuild/MacOSAppBundle.targets
Normal file
@@ -0,0 +1,24 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!--
|
||||
Depend on this in your client project (e.g. Content.Client) to generate a development app bundle for macOS.
|
||||
This is required for WebView.
|
||||
-->
|
||||
|
||||
<PropertyGroup>
|
||||
<RTMakeAppBundle Condition="'$(TargetOS)' == 'MacOS' And '$(RTMakeAppBundle)' == '' And '$(FullRelease)' != 'True'">True</RTMakeAppBundle>
|
||||
<RTAppBundleName Condition="'$(RTAppBundleName)' == ''">RobustToolbox Project</RTAppBundleName>
|
||||
<RTAppBundleIdentifier Condition="'$(RTAppBundleIdentifier)' == ''">org.robusttoolbox.project</RTAppBundleIdentifier>
|
||||
<!-- RTAppBundleIcon controls icon -->
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<_RTMacOSAppBundle_targets_imported>True</_RTMacOSAppBundle_targets_imported>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="RTMakeAppBundleAfterBuild" Condition="'$(RTMakeAppBundle)' == 'True'" AfterTargets="AfterBuild">
|
||||
<PropertyGroup>
|
||||
<_RTMacOSAppBundle_icon Condition="'$(RTAppBundleIcon)' != ''">--icon "$(RTAppBundleIcon)"</_RTMacOSAppBundle_icon>
|
||||
</PropertyGroup>
|
||||
<Exec Command="$(MSBuildThisFileDirectory)/../Tools/macos_make_appbundle.py $(_RTMacOSAppBundle_for_webview) --name "$(RTAppBundleName)" --directory "$(OutputPath)" --apphost "$(AssemblyName)" --identifier "$(RTAppBundleIdentifier)" $(_RTMacOSAppBundle_icon)" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -13,7 +13,7 @@
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup>
|
||||
<DefineConstants>$(DefineConstants);LINUX;UNIX</DefineConstants>
|
||||
<DefineConstants>$(DefineConstants);LINUX;UNIX;FREEDESKTOP</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<Project>
|
||||
<!-- Engine-specific properties. Content should not use this file. -->
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<LangVersion>13</LangVersion>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>14</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<WarningsAsErrors>nullable</WarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -31,5 +31,6 @@
|
||||
<Python>python3</Python>
|
||||
<Python Condition="'$(ActualOS)' == 'Windows'">py -3</Python>
|
||||
<UseSystemSqlite Condition="'$(TargetOS)' == 'FreeBSD'">True</UseSystemSqlite>
|
||||
<IsFreedesktop Condition="'$(TargetOS)' == 'FreeBSD' Or '$(TargetOS)' == 'Linux'">True</IsFreedesktop>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
17
MSBuild/Robust.ProjectReferences.targets
Normal file
17
MSBuild/Robust.ProjectReferences.targets
Normal file
@@ -0,0 +1,17 @@
|
||||
<Project>
|
||||
<!--
|
||||
Disallow content from using direct references to Robust project files.
|
||||
Content should use the .props files in Imports/ instead.
|
||||
-->
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowDirectRobustReferences Condition="'$(AllowDirectRobustReferences)' != ''">false</AllowDirectRobustReferences>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="_RTCheckForDirectReferences" BeforeTargets="BeforeResolveReferences"
|
||||
Condition="'$(AllowDirectRobustReferences)' != 'true'">
|
||||
<Error File="%(ProjectReference.DefiningProjectFullPath)"
|
||||
Text="Direct reference to %(Filename) is not allowed. Use RobustToolbox/Imports/*.props instead (e.g., Shared.props, Client.props, Server.props)"
|
||||
Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(Identity)', 'RobustToolbox')) and !$([System.Text.RegularExpressions.Regex]::IsMatch('%(DefiningProjectFullPath)', '([Mm]icrosoft|RobustToolbox)'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -3,8 +3,9 @@
|
||||
<!-- Import this at the end of any project files in Robust and Content. -->
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<RTCullDotnetAnalyzers Condition="'$(RTCullDotnetAnalyzers)' == ''">true</RTCullDotnetAnalyzers>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="Robust.Custom.targets" Condition="Exists('Robust.Custom.targets')"/>
|
||||
@@ -28,5 +29,19 @@
|
||||
<Import Project="Robust.Analyzers.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
|
||||
|
||||
<!-- serialization generator -->
|
||||
<Import Project="Robust.Serialization.Generator.targets" Condition="'$(SkipRobustAnalyzer)' != 'true' And '$(SkipRobustSerializationGenerator)' != 'true'" />
|
||||
<Import Project="Robust.Serialization.Generator.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
|
||||
|
||||
<!-- Robust API system -->
|
||||
<Import Project="Robust.ProjectReferences.targets" />
|
||||
|
||||
<!--
|
||||
We don't use these features and they add a not-insignificant amount of time to build perf.
|
||||
Am I micro-optimizing? Maybe.
|
||||
-->
|
||||
<Target Name="_RTRemoveSlowAnalyzers" BeforeTargets="AfterResolveReferences" Returns="@(Analyzer)">
|
||||
<ItemGroup Condition="'$(RTCullDotnetAnalyzers)' == 'true'">
|
||||
<Analyzer Remove="@(Analyzer)" Condition="'%(Analyzer.AssemblyName)' == 'Microsoft.Interop.ComInterfaceGenerator'" />
|
||||
<Analyzer Remove="@(Analyzer)" Condition="'%(Analyzer.AssemblyName)' == 'Microsoft.Interop.JavaScript.JSImportGenerator'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
Submodule NetSerializer updated: 84ab8fec64...61b47fbbbd
5916
RELEASE-NOTES.md
5916
RELEASE-NOTES.md
File diff suppressed because it is too large
Load Diff
@@ -1005,6 +1005,22 @@
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
- name: GLFW
|
||||
license: |
|
||||
Copyright © 2002-2006 Marcus Geelnard
|
||||
|
||||
Copyright © 2006-2019 Camilla Löwy
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
|
||||
The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
|
||||
Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
|
||||
This notice may not be removed or altered from any source distribution.
|
||||
|
||||
- name: FluidSynth
|
||||
license: |
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
id: Audio
|
||||
name: Audio
|
||||
description: Audio entity used by engine
|
||||
save: false
|
||||
save: false # TODO PERSISTENCE what about looping or long sounds?
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: Transform
|
||||
gridTraversal: false
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import Robust::SpriteBatch::{VertexInput, VertexOutput, mainTexture, mainSampler, View};
|
||||
import Robust::Math::srgb_to_linear;
|
||||
|
||||
@vertex
|
||||
fn vs_main(input: VertexInput) -> VertexOutput {
|
||||
var transformed = vec3(input.position, 1.0) * View.projViewMatrix;
|
||||
|
||||
transformed += 1.0;
|
||||
transformed /= View.screenPixelSize * 2.0;
|
||||
transformed = floor(transformed + 0.5);
|
||||
transformed *= View.screenPixelSize * 2.0;
|
||||
transformed -= 1.0;
|
||||
|
||||
var out: VertexOutput;
|
||||
out.position = vec4(transformed, 0.0, 1.0);
|
||||
out.texCoord = input.texCoord;
|
||||
out.color = srgb_to_linear(input.color);
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(input: VertexOutput) -> @location(0) vec4f {
|
||||
var color = textureSample(mainTexture, mainSampler, input.texCoord);
|
||||
color = color * input.color;
|
||||
|
||||
return color;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
fn srgb_to_linear(srgb: vec4f) -> vec4f {
|
||||
let higher = pow((srgb.rgb + 0.055) / 1.055, vec3(2.4));
|
||||
let lower = srgb.rgb / 12.92;
|
||||
let s = max(vec3(0.0), sign(srgb.rgb - 0.04045));
|
||||
return vec4(mix(lower, higher, s), srgb.a);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// Group 0: global constants.
|
||||
struct UniformConstants {
|
||||
time: f32
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<uniform> Constants: UniformConstants;
|
||||
|
||||
// Group 1: parameters that change infrequently in a draw pass.
|
||||
struct UniformView {
|
||||
projViewMatrix: mat2x3f,
|
||||
screenPixelSize: vec2f
|
||||
}
|
||||
|
||||
@group(1) @binding(0) var<uniform> View: UniformView;
|
||||
|
||||
// Group 2: per-draw parameters.
|
||||
@group(2) @binding(0)
|
||||
var mainTexture: texture_2d<f32>;
|
||||
@group(2) @binding(1)
|
||||
var mainSampler: sampler;
|
||||
|
||||
|
||||
struct VertexInput {
|
||||
@location(0) position: vec2f,
|
||||
@location(1) texCoord: vec2f,
|
||||
@location(2) color: vec4f
|
||||
}
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) position: vec4f,
|
||||
@location(0) texCoord: vec2f,
|
||||
@location(1) color: vec4f,
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Loc strings for various entity state & client-side PVS related commands
|
||||
|
||||
cmd-reset-ent-help = Usage: resetent <Entity UID>
|
||||
cmd-reset-ent-desc = Reset an entity to the most recently received server state. This will also reset entities that have been detached to null-space.
|
||||
cmd-reset-ent-help = Usage: {$command} <Entity UID>
|
||||
cmd-reset-ent-desc = Reset an entity to the most recently received server state. This will also reset entities that have been detached to null-space.
|
||||
|
||||
cmd-reset-all-ents-help = Usage: resetallents
|
||||
cmd-reset-all-ents-desc = Resets all entities to the most recently received server state. This only impacts entities that have not been detached to null-space.
|
||||
cmd-reset-all-ents-help = Usage: {$command}
|
||||
cmd-reset-all-ents-desc = Resets all entities to the most recently received server state. This only impacts entities that have not been detached to null-space.
|
||||
|
||||
cmd-detach-ent-help = Usage: detachent <Entity UID>
|
||||
cmd-detach-ent-help = Usage: {$command} <Entity UID>
|
||||
cmd-detach-ent-desc = Detach an entity to null-space, as if it had left PVS range.
|
||||
|
||||
cmd-local-delete-help = Usage: localdelete <Entity UID>
|
||||
cmd-local-delete-help = Usage: {$command} <Entity UID>
|
||||
cmd-local-delete-desc = Deletes an entity. Unlike the normal delete command, this is CLIENT-SIDE. Unless the entity is a client-side entity, this will likely cause errors.
|
||||
|
||||
cmd-full-state-reset-help = Usage: fullstatereset
|
||||
cmd-full-state-reset-help = Usage: {$command}
|
||||
cmd-full-state-reset-desc = Discards any entity state information and requests a full-state from the server.
|
||||
|
||||
@@ -23,8 +23,8 @@ cmd-error-dir-not-found = Could not find directory: {$dir}.
|
||||
cmd-failure-no-attached-entity = There is no entity attached to this shell.
|
||||
|
||||
## 'help' command
|
||||
cmd-help-desc = Display general help or help text for a specific command
|
||||
cmd-help-help = Usage: help [command name]
|
||||
cmd-help-desc = Display general help or help text for a specific command.
|
||||
cmd-help-help = Usage: {$command} [command name]
|
||||
When no command name is provided, displays general-purpose help text. If a command name is provided, displays help text for that command.
|
||||
|
||||
cmd-help-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
|
||||
@@ -35,7 +35,7 @@ cmd-help-arg-cmdname = [command name]
|
||||
|
||||
## 'cvar' command
|
||||
cmd-cvar-desc = Gets or sets a CVar.
|
||||
cmd-cvar-help = Usage: cvar <name | ?> [value]
|
||||
cmd-cvar-help = Usage: {$command} <name | ?> [value]
|
||||
If a value is passed, the value is parsed and stored as the new value of the CVar.
|
||||
If not, the current value of the CVar is displayed.
|
||||
Use 'cvar ?' to get a list of all registered CVars.
|
||||
@@ -49,14 +49,14 @@ cmd-cvar-value-hidden = <value hidden>
|
||||
|
||||
## 'cvar_subs' command
|
||||
cmd-cvar_subs-desc = Lists the OnValueChanged subscriptions for a CVar.
|
||||
cmd-cvar_subs-help = Usage: cvar_subs <name>
|
||||
cmd-cvar_subs-help = Usage: {$command} <name>
|
||||
|
||||
cmd-cvar_subs-invalid-args = Must provide exactly one argument.
|
||||
cmd-cvar_subs-arg-name = <name>
|
||||
|
||||
## 'list' command
|
||||
cmd-list-desc = Lists available commands, with optional search filter
|
||||
cmd-list-help = Usage: list [filter]
|
||||
cmd-list-desc = Lists available commands, with optional search filter.
|
||||
cmd-list-help = Usage: {$command} [filter]
|
||||
Lists all available commands. If an argument is provided, it will be used to filter commands by name.
|
||||
|
||||
cmd-list-heading = SIDE NAME DESC{"\u000A"}-------------------------{"\u000A"}
|
||||
@@ -64,13 +64,13 @@ cmd-list-heading = SIDE NAME DESC{"\u000A"}-------------------------{
|
||||
cmd-list-arg-filter = [filter]
|
||||
|
||||
## '>' command, aka remote exec
|
||||
cmd-remoteexec-desc = Executes server-side commands
|
||||
cmd-remoteexec-desc = Executes server-side commands.
|
||||
cmd-remoteexec-help = Usage: > <command> [arg] [arg] [arg...]
|
||||
Executes a command on the server. This is necessary if a command with the same name exists on the client, as simply running the command would run the client command first.
|
||||
|
||||
## 'gc' command
|
||||
cmd-gc-desc = Run the GC (Garbage Collector)
|
||||
cmd-gc-help = Usage: gc [generation]
|
||||
cmd-gc-desc = Run the GC (Garbage Collector).
|
||||
cmd-gc-help = Usage: {$command} [generation]
|
||||
Uses GC.Collect() to execute the Garbage Collector.
|
||||
If an argument is provided, it is parsed as a GC generation number and GC.Collect(int) is used.
|
||||
Use the 'gfc' command to do an LOH-compacting full GC.
|
||||
@@ -79,13 +79,13 @@ cmd-gc-arg-generation = [generation]
|
||||
|
||||
## 'gcf' command
|
||||
cmd-gcf-desc = Run the GC, fully, compacting LOH and everything.
|
||||
cmd-gcf-help = Usage: gcf
|
||||
cmd-gcf-help = Usage: {$command}
|
||||
Does a full GC.Collect(2, GCCollectionMode.Forced, true, true) while also compacting LOH.
|
||||
This will probably lock up for hundreds of milliseconds, be warned.
|
||||
|
||||
## 'gc_mode' command
|
||||
cmd-gc_mode-desc = Change/Read the GC Latency mode
|
||||
cmd-gc_mode-help = Usage: gc_mode [type]
|
||||
cmd-gc_mode-desc = Change/Read the GC Latency mode.
|
||||
cmd-gc_mode-help = Usage: {$command} [type]
|
||||
If no argument is provided, returns the current GC latency mode.
|
||||
If an argument is passed, it is parsed as GCLatencyMode and set as the GC latency mode.
|
||||
|
||||
@@ -98,8 +98,8 @@ cmd-gc_mode-result = resulting gc latency mode: { $mode }
|
||||
cmd-gc_mode-arg-type = [type]
|
||||
|
||||
## 'mem' command
|
||||
cmd-mem-desc = Prints managed memory info
|
||||
cmd-mem-help = Usage: mem
|
||||
cmd-mem-desc = Prints managed memory info.
|
||||
cmd-mem-help = Usage: {$command}
|
||||
|
||||
cmd-mem-report = Heap Size: { TOSTRING($heapSize, "N0") }
|
||||
Total Allocated: { TOSTRING($totalAllocated, "N0") }
|
||||
@@ -108,26 +108,26 @@ cmd-mem-report = Heap Size: { TOSTRING($heapSize, "N0") }
|
||||
cmd-physics-overlay = {$overlay} is not a recognised overlay
|
||||
|
||||
## 'lsasm' command
|
||||
cmd-lsasm-desc = Lists loaded assemblies by load context
|
||||
cmd-lsasm-desc = Lists loaded assemblies by load context.
|
||||
cmd-lsasm-help = Usage: lsasm
|
||||
|
||||
## 'exec' command
|
||||
cmd-exec-desc = Executes a script file from the game's writeable user data
|
||||
cmd-exec-help = Usage: exec <fileName>
|
||||
cmd-exec-desc = Executes a script file from the game's writeable user data.
|
||||
cmd-exec-help = Usage: {$command} <fileName>
|
||||
Each line in the file is executed as a single command, unless it starts with a #
|
||||
|
||||
cmd-exec-arg-filename = <fileName>
|
||||
|
||||
## 'dump_net_comps' command
|
||||
cmd-dump_net_comps-desc = Prints the table of networked components.
|
||||
cmd-dump_net_comps-help = Usage: dump_net-comps
|
||||
cmd-dump_net_comps-help = Usage: {$command}
|
||||
|
||||
cmd-dump_net_comps-error-writeable = Registration still writeable, network ids have not been generated.
|
||||
cmd-dump_net_comps-header = Networked Component Registrations:
|
||||
|
||||
## 'dump_event_tables' command
|
||||
cmd-dump_event_tables-desc = Prints directed event tables for an entity.
|
||||
cmd-dump_event_tables-help = Usage: dump_event_tables <entityUid>
|
||||
cmd-dump_event_tables-help = Usage: {$command} <entityUid>
|
||||
|
||||
cmd-dump_event_tables-missing-arg-entity = Missing entity argument
|
||||
cmd-dump_event_tables-error-entity = Invalid entity
|
||||
@@ -135,7 +135,7 @@ cmd-dump_event_tables-arg-entity = <entityUid>
|
||||
|
||||
## 'monitor' command
|
||||
cmd-monitor-desc = Toggles a debug monitor in the F3 menu.
|
||||
cmd-monitor-help = Usage: monitor <name>
|
||||
cmd-monitor-help = Usage: {$command} <name>
|
||||
Possible monitors are: { $monitors }
|
||||
You can also use the special values "-all" and "+all" to hide or show all monitors, respectively.
|
||||
|
||||
@@ -148,13 +148,13 @@ cmd-monitor-plus-all-hint = Shows all monitors
|
||||
|
||||
## 'setambientlight' command
|
||||
cmd-set-ambient-light-desc = Allows you to set the ambient light for the specified map, in SRGB.
|
||||
cmd-set-ambient-light-help = setambientlight [mapid] [r g b a]
|
||||
cmd-set-ambient-light-help = Usage: {$command} [mapid] [r g b a]
|
||||
cmd-set-ambient-light-parse = Unable to parse args as a byte values for a color.
|
||||
|
||||
## Mapping commands
|
||||
|
||||
cmd-savemap-desc = Serializes a map to disk. Will not save a post-init map unless forced.
|
||||
cmd-savemap-help = savemap <MapID> <Path> [force]
|
||||
cmd-savemap-help = Usage: {$command} <MapID> <Path> [force]
|
||||
cmd-savemap-not-exist = Target map does not exist.
|
||||
cmd-savemap-init-warning = Attempted to save a post-init map without forcing the save.
|
||||
cmd-savemap-attempt = Attempting to save map {$mapId} to {$path}.
|
||||
@@ -165,7 +165,7 @@ cmd-hint-savemap-path = <Path>
|
||||
cmd-hint-savemap-force = [bool]
|
||||
|
||||
cmd-loadmap-desc = Loads a map from disk into the game.
|
||||
cmd-loadmap-help = loadmap <MapID> <Path> [x] [y] [rotation] [consistentUids]
|
||||
cmd-loadmap-help = Usage: {$command} <MapID> <Path> [x] [y] [rotation] [consistentUids]
|
||||
cmd-loadmap-nullspace = You cannot load into map 0.
|
||||
cmd-loadmap-exists = Map {$mapId} already exists.
|
||||
cmd-loadmap-success = Map {$mapId} has been loaded from {$path}.
|
||||
@@ -180,73 +180,74 @@ cmd-hint-savebp-id = <Grid EntityID>
|
||||
## 'flushcookies' command
|
||||
# Note: the flushcookies command is from Robust.Client.WebView, it's not in the main engine code.
|
||||
|
||||
cmd-flushcookies-desc = Flush CEF cookie storage to disk
|
||||
cmd-flushcookies-help = This ensure cookies are properly saved to disk in the event of unclean shutdowns.
|
||||
cmd-flushcookies-desc = Flush CEF cookie storage to disk.
|
||||
cmd-flushcookies-help = Usage: {$command}
|
||||
This ensure cookies are properly saved to disk in the event of unclean shutdowns.
|
||||
Note that the actual operation is asynchronous.
|
||||
|
||||
cmd-ldrsc-desc = Pre-caches a resource.
|
||||
cmd-ldrsc-help = Usage: ldrsc <path> <type>
|
||||
cmd-ldrsc-help = Usage: {$command} <path> <type>
|
||||
|
||||
cmd-rldrsc-desc = Reloads a resource.
|
||||
cmd-rldrsc-help = Usage: rldrsc <path> <type>
|
||||
cmd-rldrsc-help = Usage: {$command} <path> <type>
|
||||
|
||||
cmd-gridtc-desc = Gets the tile count of a grid.
|
||||
cmd-gridtc-help = Usage: gridtc <gridId>
|
||||
cmd-gridtc-help = Usage: {$command} <gridId>
|
||||
|
||||
|
||||
# Client-side commands
|
||||
cmd-guidump-desc = Dump GUI tree to /guidump.txt in user data.
|
||||
cmd-guidump-help = Usage: guidump
|
||||
cmd-guidump-help = Usage: {$command}
|
||||
|
||||
cmd-uitest-desc = Open a dummy UI testing window
|
||||
cmd-uitest-help = Usage: uitest
|
||||
cmd-uitest-desc = Open a dummy UI testing window.
|
||||
cmd-uitest-help = Usage: {$command}
|
||||
|
||||
## 'uitest2' command
|
||||
cmd-uitest2-desc = Opens a UI control testing OS window
|
||||
cmd-uitest2-help = Usage: uitest2 <tab>
|
||||
cmd-uitest2-desc = Opens a UI control testing OS window.
|
||||
cmd-uitest2-help = Usage: {$command} <tab>
|
||||
cmd-uitest2-arg-tab = <tab>
|
||||
cmd-uitest2-error-args = Expected at most one argument
|
||||
cmd-uitest2-error-tab = Invalid tab: '{$value}'
|
||||
cmd-uitest2-title = UITest2
|
||||
|
||||
|
||||
cmd-setclipboard-desc = Sets the system clipboard
|
||||
cmd-setclipboard-help = Usage: setclipboard <text>
|
||||
cmd-setclipboard-desc = Sets the system clipboard.
|
||||
cmd-setclipboard-help = Usage: {$command} <text>
|
||||
|
||||
cmd-getclipboard-desc = Gets the system clipboard
|
||||
cmd-getclipboard-help = Usage: Getclipboard
|
||||
cmd-getclipboard-desc = Gets the system clipboard.
|
||||
cmd-getclipboard-help = Usage: {$command}
|
||||
|
||||
cmd-togglelight-desc = Toggles light rendering.
|
||||
cmd-togglelight-help = Usage: togglelight
|
||||
cmd-togglelight-help = Usage: {$command}
|
||||
|
||||
cmd-togglefov-desc = Toggles fov for client.
|
||||
cmd-togglefov-help = Usage: togglefov
|
||||
cmd-togglefov-help = Usage: {$command}
|
||||
|
||||
cmd-togglehardfov-desc = Toggles hard fov for client. (for debugging space-station-14#2353)
|
||||
cmd-togglehardfov-help = Usage: togglehardfov
|
||||
cmd-togglehardfov-help = Usage: {$command}
|
||||
|
||||
cmd-toggleshadows-desc = Toggles shadow rendering.
|
||||
cmd-toggleshadows-help = Usage: toggleshadows
|
||||
cmd-toggleshadows-help = Usage: {$command}
|
||||
|
||||
cmd-togglelightbuf-desc = Toggles lighting rendering. This includes shadows but not FOV.
|
||||
cmd-togglelightbuf-help = Usage: togglelightbuf
|
||||
cmd-togglelightbuf-help = Usage: {$command}
|
||||
|
||||
cmd-chunkinfo-desc = Gets info about a chunk under your mouse cursor.
|
||||
cmd-chunkinfo-help = Usage: chunkinfo
|
||||
cmd-chunkinfo-help = Usage: {$command}
|
||||
|
||||
cmd-rldshader-desc = Reloads all shaders.
|
||||
cmd-rldshader-help = Usage: rldshader
|
||||
cmd-rldshader-help = Usage: {$command}
|
||||
|
||||
cmd-cldbglyr-desc = Toggle fov and light debug layers.
|
||||
cmd-cldbglyr-help= Usage: cldbglyr <layer>: Toggle <layer>
|
||||
cmd-cldbglyr-help= Usage: {$command} <layer>: Toggle <layer>
|
||||
cldbglyr: Turn all Layers off
|
||||
|
||||
cmd-key-info-desc = Keys key info for a key.
|
||||
cmd-key-info-help = Usage: keyinfo <Key>
|
||||
cmd-key-info-help = Usage: {$command} <Key>
|
||||
|
||||
## 'bind' command
|
||||
cmd-bind-desc = Binds an input key combination to an input command.
|
||||
cmd-bind-help = Usage: bind { cmd-bind-arg-key } { cmd-bind-arg-mode } { cmd-bind-arg-command }
|
||||
cmd-bind-help = Usage: {$command} { cmd-bind-arg-key } { cmd-bind-arg-mode } { cmd-bind-arg-command }
|
||||
Note that this DOES NOT automatically save bindings.
|
||||
Use the 'svbind' command to save binding configuration.
|
||||
|
||||
@@ -255,316 +256,322 @@ cmd-bind-arg-mode = <BindMode>
|
||||
cmd-bind-arg-command = <InputCommand>
|
||||
|
||||
cmd-net-draw-interp-desc = Toggles the debug drawing of the network interpolation.
|
||||
cmd-net-draw-interp-help = Usage: net_draw_interp
|
||||
cmd-net-draw-interp-help = Usage: {$command}
|
||||
|
||||
cmd-net-watch-ent-desc = Dumps all network updates for an EntityId to the console.
|
||||
cmd-net-watch-ent-help = Usage: net_watchent <0|EntityUid>
|
||||
cmd-net-watch-ent-help = Usage: {$command} <0|EntityUid>
|
||||
|
||||
cmd-net-refresh-desc = Requests a full server state.
|
||||
cmd-net-refresh-help = Usage: net_refresh
|
||||
cmd-net-refresh-help = Usage: {$command}
|
||||
|
||||
cmd-net-entity-report-desc = Toggles the net entity report panel.
|
||||
cmd-net-entity-report-help = Usage: net_entityreport
|
||||
cmd-net-entity-report-help = Usage: {$command}
|
||||
|
||||
cmd-fill-desc = Fill up the console for debugging.
|
||||
cmd-fill-help = Fills the console with some nonsense for debugging.
|
||||
cmd-fill-help = Usage: {$command}
|
||||
Fills the console with some nonsense for debugging.
|
||||
|
||||
cmd-cls-desc = Clears the console.
|
||||
cmd-cls-help = Clears the debug console of all messages.
|
||||
cmd-cls-help = Usage: {$command}
|
||||
Clears the debug console of all messages.
|
||||
|
||||
cmd-sendgarbage-desc = Sends garbage to the server.
|
||||
cmd-sendgarbage-help = The server will reply with 'no u'
|
||||
cmd-sendgarbage-help = Usage: {$command}
|
||||
The server will reply with 'no u'
|
||||
|
||||
cmd-loadgrid-desc = Loads a grid from a file into an existing map.
|
||||
cmd-loadgrid-help = loadgrid <MapID> <Path> [x y] [rotation] [storeUids]
|
||||
cmd-loadgrid-help = Usage: {$command} <MapID> <Path> [x y] [rotation] [storeUids]
|
||||
|
||||
cmd-loc-desc = Prints the absolute location of the player's entity to console.
|
||||
cmd-loc-help = loc
|
||||
cmd-loc-help = Usage: {$command}
|
||||
|
||||
cmd-tpgrid-desc = Teleports a grid to a new location.
|
||||
cmd-tpgrid-help = tpgrid <gridId> <X> <Y> [<MapId>]
|
||||
cmd-tpgrid-help = Usage: {$command} <gridId> <X> <Y> [<MapId>]
|
||||
|
||||
cmd-rmgrid-desc = Removes a grid from a map. You cannot remove the default grid.
|
||||
cmd-rmgrid-help = rmgrid <gridId>
|
||||
cmd-rmgrid-help = Usage: {$command} <gridId>
|
||||
|
||||
cmd-mapinit-desc = Runs map init on a map.
|
||||
cmd-mapinit-help = mapinit <mapID>
|
||||
cmd-mapinit-help = Usage: {$command} <mapID>
|
||||
|
||||
cmd-lsmap-desc = Lists maps.
|
||||
cmd-lsmap-help = lsmap
|
||||
cmd-lsmap-help = Usage: {$command}
|
||||
|
||||
cmd-lsgrid-desc = Lists grids.
|
||||
cmd-lsgrid-help = lsgrid
|
||||
cmd-lsgrid-help = Usage: {$command}
|
||||
|
||||
cmd-addmap-desc = Adds a new empty map to the round. If the mapID already exists, this command does nothing.
|
||||
cmd-addmap-help = addmap <mapID> [pre-init]
|
||||
cmd-addmap-help = Usage: {$command} <mapID> [pre-init]
|
||||
|
||||
cmd-rmmap-desc = Removes a map from the world. You cannot remove nullspace.
|
||||
cmd-rmmap-help = rmmap <mapId>
|
||||
cmd-rmmap-help = Usage: {$command} <mapId>
|
||||
|
||||
cmd-savegrid-desc = Serializes a grid to disk.
|
||||
cmd-savegrid-help = savegrid <gridID> <Path>
|
||||
cmd-savegrid-help = Usage: {$command} <gridID> <Path>
|
||||
|
||||
cmd-testbed-desc = Loads a physics testbed on the specified map.
|
||||
cmd-testbed-help = testbed <mapid> <test>
|
||||
cmd-testbed-help = Usage: {$command} <mapid> <test>
|
||||
|
||||
## 'flushcookies' command
|
||||
# Note: the flushcookies command is from Robust.Client.WebView, it's not in the main engine code.
|
||||
|
||||
## 'addcomp' command
|
||||
cmd-addcomp-desc = Adds a component to an entity.
|
||||
cmd-addcomp-help = addcomp <uid> <componentName>
|
||||
cmd-addcomp-help = Usage: {$command} <uid> <componentName>
|
||||
cmd-addcompc-desc = Adds a component to an entity on the client.
|
||||
cmd-addcompc-help = addcompc <uid> <componentName>
|
||||
cmd-addcompc-help = Usage: {$command} <uid> <componentName>
|
||||
|
||||
## 'rmcomp' command
|
||||
cmd-rmcomp-desc = Removes a component from an entity.
|
||||
cmd-rmcomp-help = rmcomp <uid> <componentName>
|
||||
cmd-rmcomp-help = Usage: {$command} <uid> <componentName>
|
||||
cmd-rmcompc-desc = Removes a component from an entity on the client.
|
||||
cmd-rmcompc-help = rmcomp <uid> <componentName>
|
||||
cmd-rmcompc-help = Usage: {$command} <uid> <componentName>
|
||||
|
||||
## 'addview' command
|
||||
cmd-addview-desc = Allows you to subscribe to an entity's view for debugging purposes.
|
||||
cmd-addview-help = addview <entityUid>
|
||||
cmd-addview-help = Usage: {$command} <entityUid>
|
||||
cmd-addviewc-desc = Allows you to subscribe to an entity's view for debugging purposes.
|
||||
cmd-addviewc-help = addview <entityUid>
|
||||
cmd-addviewc-help = Usage: {$command} <entityUid>
|
||||
|
||||
## 'removeview' command
|
||||
cmd-removeview-desc = Allows you to unsubscribe to an entity's view for debugging purposes.
|
||||
cmd-removeview-help = removeview <entityUid>
|
||||
cmd-removeview-help = Usage: {$command} <entityUid>
|
||||
|
||||
## 'loglevel' command
|
||||
cmd-loglevel-desc = Changes the log level for a provided sawmill.
|
||||
cmd-loglevel-help = Usage: loglevel <sawmill> <level>
|
||||
cmd-loglevel-help = Usage: {$command} <sawmill> <level>
|
||||
sawmill: A label prefixing log messages. This is the one you're setting the level for.
|
||||
level: The log level. Must match one of the values of the LogLevel enum.
|
||||
|
||||
cmd-testlog-desc = Writes a test log to a sawmill.
|
||||
cmd-testlog-help = Usage: testlog <sawmill> <level> <message>
|
||||
cmd-testlog-help = Usage: {$command} <sawmill> <level> <message>
|
||||
sawmill: A label prefixing the logged message.
|
||||
level: The log level. Must match one of the values of the LogLevel enum.
|
||||
message: The message to be logged. Wrap this in double quotes if you want to use spaces.
|
||||
|
||||
## 'vv' command
|
||||
cmd-vv-desc = Opens View Variables.
|
||||
cmd-vv-help = Usage: vv <entity ID|IoC interface name|SIoC interface name>
|
||||
cmd-vv-help = Usage: {$command} <entity ID|IoC interface name|SIoC interface name>
|
||||
|
||||
## 'showvelocities' command
|
||||
cmd-showvelocities-desc = Displays your angular and linear velocities.
|
||||
cmd-showvelocities-help = Usage: showvelocities
|
||||
cmd-showvelocities-help = Usage: {$command}
|
||||
|
||||
## 'setinputcontext' command
|
||||
cmd-setinputcontext-desc = Sets the active input context.
|
||||
cmd-setinputcontext-help = Usage: setinputcontext <context>
|
||||
cmd-setinputcontext-help = Usage: {$command} <context>
|
||||
|
||||
## 'forall' command
|
||||
cmd-forall-desc = Runs a command over all entities with a given component.
|
||||
cmd-forall-help = Usage: forall <bql query> do <command...>
|
||||
cmd-forall-help = Usage: {$command} <bql query> do <command...>
|
||||
|
||||
## 'delete' command
|
||||
cmd-delete-desc = Deletes the entity with the specified ID.
|
||||
cmd-delete-help = delete <entity UID>
|
||||
cmd-delete-help = Usage: {$command} <entity UID>
|
||||
|
||||
# System commands
|
||||
cmd-showtime-desc = Shows the server time.
|
||||
cmd-showtime-help = showtime
|
||||
cmd-showtime-help = Usage: {$command}
|
||||
|
||||
cmd-restart-desc = Gracefully restarts the server (not just the round).
|
||||
cmd-restart-help = restart
|
||||
cmd-restart-help = Usage: {$command}
|
||||
|
||||
cmd-shutdown-desc = Gracefully shuts down the server.
|
||||
cmd-shutdown-help = shutdown
|
||||
cmd-shutdown-help = Usage: {$command}
|
||||
|
||||
cmd-saveconfig-desc = Saves the server configuration to the config file.
|
||||
cmd-saveconfig-help = saveconfig
|
||||
cmd-saveconfig-help = Usage: {$command}
|
||||
|
||||
cmd-netaudit-desc = Prints into about NetMsg security.
|
||||
cmd-netaudit-help = netaudit
|
||||
cmd-netaudit-help = Usage: {$command}
|
||||
|
||||
# Player commands
|
||||
cmd-tp-desc = Teleports a player to any location in the round.
|
||||
cmd-tp-help = tp <x> <y> [<mapID>]
|
||||
cmd-tp-help = Usage: {$command} <x> <y> [<mapID>]
|
||||
|
||||
cmd-tpto-desc = Teleports the current player or the specified players/entities to the location of the first player/entity.
|
||||
cmd-tpto-help = tpto <username|uid> [username|NetEntity]...
|
||||
cmd-tpto-help = Usage: {$command} <username|uid> [username|NetEntity]...
|
||||
cmd-tpto-destination-hint = destination (NetEntity or username)
|
||||
cmd-tpto-victim-hint = entity to teleport (NetEntity or username)
|
||||
cmd-tpto-parse-error = Cant resolve entity or player: {$str}
|
||||
|
||||
cmd-listplayers-desc = Lists all players currently connected.
|
||||
cmd-listplayers-help = listplayers
|
||||
cmd-listplayers-help = Usage: {$command}
|
||||
|
||||
cmd-kick-desc = Kicks a connected player out of the server, disconnecting them.
|
||||
cmd-kick-help = kick <PlayerIndex> [<Reason>]
|
||||
cmd-kick-help = Usage: {$command} <PlayerIndex> [<Reason>]
|
||||
|
||||
# Spin command
|
||||
cmd-spin-desc = Causes an entity to spin. Default entity is the attached player's parent.
|
||||
cmd-spin-help = spin velocity [drag] [entityUid]
|
||||
cmd-spin-help = Usage: {$command} velocity [drag] [entityUid]
|
||||
|
||||
# Localization command
|
||||
cmd-rldloc-desc = Reloads localization (client & server).
|
||||
cmd-rldloc-help = Usage: rldloc
|
||||
cmd-rldloc-help = Usage: {$command}
|
||||
|
||||
# Debug entity controls
|
||||
cmd-spawn-desc = Spawns an entity with specific type.
|
||||
cmd-spawn-help = spawn <prototype> OR spawn <prototype> <relative entity ID> OR spawn <prototype> <x> <y>
|
||||
cmd-spawn-help = Usage: {$command} <prototype> | {$command} <prototype> <relative entity ID> | {$command} <prototype> <x> <y>
|
||||
cmd-cspawn-desc = Spawns a client-side entity with specific type at your feet.
|
||||
cmd-cspawn-help = cspawn <entity type>
|
||||
cmd-cspawn-help = Usage: {$command} <entity type>
|
||||
|
||||
cmd-dumpentities-desc = Dump entity list.
|
||||
cmd-dumpentities-help = Dumps entity list of UIDs and prototype.
|
||||
cmd-dumpentities-help = Usage: {$command}
|
||||
Dumps entity list of UIDs and prototype.
|
||||
|
||||
cmd-getcomponentregistration-desc = Gets component registration information.
|
||||
cmd-getcomponentregistration-help = Usage: getcomponentregistration <componentName>
|
||||
cmd-getcomponentregistration-help = Usage: {$command} <componentName>
|
||||
|
||||
cmd-showrays-desc = Toggles debug drawing of physics rays. An integer for <raylifetime> must be provided.
|
||||
cmd-showrays-help = Usage: showrays <raylifetime>
|
||||
cmd-showrays-help = Usage: {$command} <raylifetime>
|
||||
|
||||
cmd-disconnect-desc = Immediately disconnect from the server and go back to the main menu.
|
||||
cmd-disconnect-help = Usage: disconnect
|
||||
cmd-disconnect-help = Usage: {$command}
|
||||
|
||||
cmd-entfo-desc = Displays verbose diagnostics for an entity.
|
||||
cmd-entfo-help = Usage: entfo <entityuid>
|
||||
cmd-entfo-help = Usage: {$command} <entityuid>
|
||||
The entity UID can be prefixed with 'c' to convert it to a client entity UID.
|
||||
|
||||
cmd-fuck-desc = Throws an exception
|
||||
cmd-fuck-help = Usage: fuck
|
||||
cmd-fuck-desc = Throws an exception.
|
||||
cmd-fuck-help = Usage: {$command}
|
||||
|
||||
cmd-showpos-desc = Show the position of all entities on the screen.
|
||||
cmd-showpos-help = Usage: showpos
|
||||
cmd-showpos-help = Usage: {$command}
|
||||
|
||||
cmd-showrot-desc = Show the rotation of all entities on the screen.
|
||||
cmd-showrot-help = Usage: showrot
|
||||
cmd-showrot-help = Usage: {$command}
|
||||
|
||||
cmd-showvel-desc = Show the local velocity of all entites on the screen.
|
||||
cmd-showvel-help = Usage: showvel
|
||||
cmd-showvel-help = Usage: {$command}
|
||||
|
||||
cmd-showangvel-desc = Show the angular velocity of all entities on the screen.
|
||||
cmd-showangvel-help = Usage: showangvel
|
||||
cmd-showangvel-help = Usage: {$command}
|
||||
|
||||
cmd-sggcell-desc = Lists entities on a snap grid cell.
|
||||
cmd-sggcell-help = Usage: sggcell <gridID> <vector2i>\nThat vector2i param is in the form x<int>,y<int>.
|
||||
cmd-sggcell-help = Usage: {$command} <gridID> <vector2i>\nThat vector2i param is in the form x<int>,y<int>.
|
||||
|
||||
cmd-overrideplayername-desc = Changes the name used when attempting to connect to the server.
|
||||
cmd-overrideplayername-help = Usage: overrideplayername <name>
|
||||
cmd-overrideplayername-help = Usage: {$command} <name>
|
||||
|
||||
cmd-showanchored-desc = Shows anchored entities on a particular tile
|
||||
cmd-showanchored-help = Usage: showanchored
|
||||
cmd-showanchored-desc = Shows anchored entities on a particular tile.
|
||||
cmd-showanchored-help = Usage: {$command}
|
||||
|
||||
cmd-dmetamem-desc = Dumps a type's members in a format suitable for the sandbox configuration file.
|
||||
cmd-dmetamem-help = Usage: dmetamem <type>
|
||||
cmd-dmetamem-help = Usage: {$command} <type>
|
||||
|
||||
cmd-launchauth-desc = Load authentication tokens from launcher data to aid in testing of live servers.
|
||||
cmd-launchauth-help = Usage: launchauth <account name>
|
||||
cmd-launchauth-help = Usage: {$command} <account name>
|
||||
|
||||
cmd-lightbb-desc = Toggles whether to show light bounding boxes.
|
||||
cmd-lightbb-help = Usage: lightbb
|
||||
cmd-lightbb-help = Usage: {$command}
|
||||
|
||||
cmd-monitorinfo-desc = Monitors info
|
||||
cmd-monitorinfo-help = Usage: monitorinfo <id>
|
||||
cmd-monitorinfo-desc = Monitors info.
|
||||
cmd-monitorinfo-help = Usage: {$command} <id>
|
||||
|
||||
cmd-setmonitor-desc = Set monitor
|
||||
cmd-setmonitor-help = Usage: setmonitor <id>
|
||||
cmd-setmonitor-desc = Set monitor.
|
||||
cmd-setmonitor-help = Usage: {$command} <id>
|
||||
|
||||
cmd-physics-desc = Shows a debug physics overlay. The arg supplied specifies the overlay.
|
||||
cmd-physics-help = Usage: physics <aabbs / com / contactnormals / contactpoints / distance / joints / shapeinfo / shapes>
|
||||
cmd-physics-help = Usage: {$command} <aabbs / com / contactnormals / contactpoints / distance / joints / shapeinfo / shapes>
|
||||
|
||||
cmd-hardquit-desc = Kills the game client instantly.
|
||||
cmd-hardquit-help = Kills the game client instantly, leaving no traces. No telling the server goodbye.
|
||||
cmd-hardquit-help = Usage: {$command}
|
||||
Kills the game client instantly, leaving no traces. No telling the server goodbye.
|
||||
|
||||
cmd-quit-desc = Shuts down the game client gracefully.
|
||||
cmd-quit-help = Properly shuts down the game client, notifying the connected server and such.
|
||||
cmd-quit-help = Usage: {$command}
|
||||
Properly shuts down the game client, notifying the connected server and such.
|
||||
|
||||
cmd-csi-desc = Opens a C# interactive console.
|
||||
cmd-csi-help = Usage: csi
|
||||
cmd-csi-help = Usage: {$command}
|
||||
|
||||
cmd-scsi-desc = Opens a C# interactive console on the server.
|
||||
cmd-scsi-help = Usage: scsi
|
||||
cmd-scsi-help = Usage: {$command}
|
||||
|
||||
cmd-watch-desc = Opens a variable watch window.
|
||||
cmd-watch-help = Usage: watch
|
||||
cmd-watch-help = Usage: {$command}
|
||||
|
||||
cmd-showspritebb-desc = Toggle whether sprite bounds are shown
|
||||
cmd-showspritebb-help = Usage: showspritebb
|
||||
cmd-showspritebb-desc = Toggle whether sprite bounds are shown.
|
||||
cmd-showspritebb-help = Usage: {$command}
|
||||
|
||||
cmd-togglelookup-desc = Shows / hides entitylookup bounds via an overlay.
|
||||
cmd-togglelookup-help = Usage: togglelookup
|
||||
cmd-togglelookup-help = Usage: {$command}
|
||||
|
||||
cmd-net_entityreport-desc = Toggles the net entity report panel.
|
||||
cmd-net_entityreport-help = Usage: net_entityreport
|
||||
cmd-net_entityreport-help = Usage: {$command}
|
||||
|
||||
cmd-net_refresh-desc = Requests a full server state.
|
||||
cmd-net_refresh-help = Usage: net_refresh
|
||||
cmd-net_refresh-help = Usage: {$command}
|
||||
|
||||
cmd-net_graph-desc = Toggles the net statistics panel.
|
||||
cmd-net_graph-help = Usage: net_graph
|
||||
cmd-net_graph-help = Usage: {$command}
|
||||
|
||||
cmd-net_watchent-desc = Dumps all network updates for an EntityId to the console.
|
||||
cmd-net_watchent-help = Usage: net_watchent <0|EntityUid>
|
||||
cmd-net_watchent-help = Usage: {$command} <0|EntityUid>
|
||||
|
||||
cmd-net_draw_interp-desc = Toggles the debug drawing of the network interpolation.
|
||||
cmd-net_draw_interp-help = Usage: net_draw_interp <0|EntityUid>
|
||||
cmd-net_draw_interp-help = Usage: {$command} <0|EntityUid>
|
||||
|
||||
cmd-vram-desc = Displays video memory usage statics by the game.
|
||||
cmd-vram-help = Usage: vram
|
||||
cmd-vram-help = Usage: {$command}
|
||||
|
||||
cmd-showislands-desc = Shows the current physics bodies involved in each physics island.
|
||||
cmd-showislands-help = Usage: showislands
|
||||
cmd-showislands-help = Usage: {$command}
|
||||
|
||||
cmd-showgridnodes-desc = Shows the nodes for grid split purposes.
|
||||
cmd-showgridnodes-help = Usage: showgridnodes
|
||||
cmd-showgridnodes-help = Usage: {$command}
|
||||
|
||||
cmd-profsnap-desc = Make a profiling snapshot.
|
||||
cmd-profsnap-help = Usage: profsnap
|
||||
cmd-profsnap-help = Usage: {$command}
|
||||
|
||||
cmd-devwindow-desc = Dev Window
|
||||
cmd-devwindow-help = Usage: devwindow
|
||||
cmd-devwindow-desc = Dev Window.
|
||||
cmd-devwindow-help = Usage: {$command}
|
||||
|
||||
cmd-scene-desc = Immediately changes the UI scene/state.
|
||||
cmd-scene-help = Usage: scene <className>
|
||||
cmd-scene-help = Usage: {$command} <className>
|
||||
|
||||
cmd-szr_stats-desc = Report serializer statistics.
|
||||
cmd-szr_stats-help = Usage: szr_stats
|
||||
cmd-szr_stats-help = Usage: {$command}
|
||||
|
||||
cmd-hwid-desc = Returns the current HWID (HardWare ID).
|
||||
cmd-hwid-help = Usage: hwid
|
||||
cmd-hwid-help = Usage: {$command}
|
||||
|
||||
cmd-vvread-desc = Retrieve a path's value using VV (View Variables).
|
||||
cmd-vvread-help = Usage: vvread <path>
|
||||
cmd-vvread-help = Usage: {$command} <path>
|
||||
|
||||
cmd-vvwrite-desc = Modify a path's value using VV (View Variables).
|
||||
cmd-vvwrite-help = Usage: vvwrite <path>
|
||||
cmd-vvwrite-help = Usage: {$command} <path>
|
||||
|
||||
cmd-vvinvoke-desc = Invoke/Call a path with arguments using VV.
|
||||
cmd-vvinvoke-help = Usage: vvinvoke <path> [arguments...]
|
||||
cmd-vvinvoke-help = Usage: {$command} <path> [arguments...]
|
||||
|
||||
cmd-dump_dependency_injectors-desc = Dump IoCManager's dependency injector cache.
|
||||
cmd-dump_dependency_injectors-help = Usage: dump_dependency_injectors
|
||||
cmd-dump_dependency_injectors-help = Usage: {$command}
|
||||
cmd-dump_dependency_injectors-total-count = Total count: { $total }
|
||||
|
||||
cmd-dump_netserializer_type_map-desc = Dump NetSerializer's type map and serializer hash.
|
||||
cmd-dump_netserializer_type_map-help = Usage: dump_netserializer_type_map
|
||||
cmd-dump_netserializer_type_map-help = Usage: {$command}
|
||||
|
||||
cmd-hub_advertise_now-desc = Immediately advertise to the master hub server
|
||||
cmd-hub_advertise_now-help = Usage: hub_advertise_now
|
||||
cmd-hub_advertise_now-desc = Immediately advertise to the master hub server.
|
||||
cmd-hub_advertise_now-help = Usage: {$command}
|
||||
|
||||
cmd-echo-desc = Echo arguments back to the console
|
||||
cmd-echo-help = Usage: echo "<message>"
|
||||
cmd-echo-desc = Echo arguments back to the console.
|
||||
cmd-echo-help = Usage: {$command} "<message>"
|
||||
|
||||
## 'vfs_ls' command
|
||||
cmd-vfs_ls-desc = List directory contents in the VFS.
|
||||
cmd-vfs_ls-help = Usage: vfs_list <path>
|
||||
cmd-vfs_ls-help = Usage: {$command} <path>
|
||||
Example:
|
||||
vfs_list /Assemblies
|
||||
|
||||
cmd-vfs_ls-err-args = Need exactly 1 argument.
|
||||
cmd-vfs_ls-hint-path = <path>
|
||||
|
||||
cmd-reloadtiletextures-desc = Reloads the tile texture atlas to allow hot reloading tile sprites
|
||||
cmd-reloadtiletextures-help = Usage: reloadtiletextures
|
||||
cmd-reloadtiletextures-desc = Reloads the tile texture atlas to allow hot reloading tile sprites.
|
||||
cmd-reloadtiletextures-help = Usage: {$command}
|
||||
|
||||
cmd-audio_length-desc = Shows the length of an audio file
|
||||
cmd-audio_length-help = Usage: audio_length { cmd-audio_length-arg-file-name }
|
||||
cmd-audio_length-help = Usage: {$command} { cmd-audio_length-arg-file-name }
|
||||
cmd-audio_length-arg-file-name = <file name>
|
||||
|
||||
## PVS
|
||||
@@ -573,8 +580,8 @@ cmd-pvs-override-info-empty = Entity {$nuid} has no PVS overrides.
|
||||
cmd-pvs-override-info-global = Entity {$nuid} has a global override.
|
||||
cmd-pvs-override-info-clients = Entity {$nuid} has a session override for {$clients}.
|
||||
|
||||
cmd-localization_set_culture-desc = Set DefaultCulture for the client LocalizationManager
|
||||
cmd-localization_set_culture-help = Usage: localization_set_culture <cultureName>
|
||||
cmd-localization_set_culture-desc = Set DefaultCulture for the client LocalizationManager.
|
||||
cmd-localization_set_culture-help = Usage: {$command} <cultureName>
|
||||
cmd-localization_set_culture-culture-name = <cultureName>
|
||||
cmd-localization_set_culture-changed = Localization changed to { $code } ({ $nativeName } / { $englishName })
|
||||
|
||||
|
||||
@@ -8,3 +8,5 @@ color-selector-sliders-alpha = A
|
||||
|
||||
color-selector-sliders-rgb = RGB
|
||||
color-selector-sliders-hsv = HSV
|
||||
|
||||
option-button-filter = Filter
|
||||
|
||||
@@ -68,6 +68,7 @@ input-key-MouseButton6 = Mouse 6
|
||||
input-key-MouseButton7 = Mouse 7
|
||||
input-key-MouseButton8 = Mouse 8
|
||||
input-key-MouseButton9 = Mouse 9
|
||||
input-key-CapsLock = Caps Lock
|
||||
|
||||
input-key-LSystem-win = Left Win
|
||||
input-key-RSystem-win = Right Win
|
||||
|
||||
@@ -9,6 +9,9 @@ cmd-replay-pause-help = replay_pause
|
||||
cmd-replay-toggle-desc = Resume or pause replay playback.
|
||||
cmd-replay-toggle-help = replay_toggle
|
||||
|
||||
cmd-replay-toggle-screenshot-mode-desc = Toggles screenshot mode for replays, hiding the replay control widget.
|
||||
cmd-replay-toggle-screenshot-mode-help = replay_toggle_screenshot_mode
|
||||
|
||||
cmd-replay-stop-desc = Stop and unload a replay.
|
||||
cmd-replay-stop-help = replay_stop
|
||||
|
||||
|
||||
@@ -428,3 +428,7 @@ command-description-cmd-info =
|
||||
On its own, this means it'll print the command's help message.
|
||||
command-description-comp-rm =
|
||||
Removes the given component from the entity.
|
||||
|
||||
command-description-overlay-toggle = Toggle an overlay on or off
|
||||
command-description-overlay-add = Add an overlay (if it does not already exist)
|
||||
command-description-overlay-remove = Remove an overlay
|
||||
|
||||
@@ -43,6 +43,7 @@ input-key-MouseButton6 = Mouse 6
|
||||
input-key-MouseButton7 = Mouse 7
|
||||
input-key-MouseButton8 = Mouse 8
|
||||
input-key-MouseButton9 = Mouse 9
|
||||
input-key-CapsLock = Caps Lock
|
||||
|
||||
input-key-LSystem-win = Left Win
|
||||
input-key-RSystem-win = Right Win
|
||||
|
||||
177
Robust.Analyzers.Tests/PrototypeAnalyzerTest.cs
Normal file
177
Robust.Analyzers.Tests/PrototypeAnalyzerTest.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using NUnit.Framework;
|
||||
using VerifyCS =
|
||||
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PrototypeAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
|
||||
[TestFixture]
|
||||
[TestOf(typeof(PrototypeAnalyzer))]
|
||||
public sealed class PrototypeAnalyzerTest
|
||||
{
|
||||
private static Task Verifier(string code, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new RTAnalyzerTest<PrototypeAnalyzer>()
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
Sources = { code }
|
||||
},
|
||||
};
|
||||
|
||||
TestHelper.AddEmbeddedSources(
|
||||
test.TestState,
|
||||
"Robust.Shared.Prototypes.Attributes.cs",
|
||||
"Robust.Shared.Prototypes.IPrototype.cs",
|
||||
"Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute.cs"
|
||||
);
|
||||
|
||||
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
|
||||
test.TestState.ExpectedDiagnostics.AddRange(expected);
|
||||
|
||||
return test.RunAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RedundantTypeTest()
|
||||
{
|
||||
const string code = """
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
[Prototype]
|
||||
public sealed partial class GoodAutoPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
|
||||
[Prototype("someOtherName")]
|
||||
public sealed partial class GoodUnmatchedPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
|
||||
[Prototype("badMatched")]
|
||||
public sealed partial class BadMatchedPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
|
||||
[Prototype(ProtoName)]
|
||||
public sealed partial class GoodNonLiteralMatchedPrototype : IPrototype
|
||||
{
|
||||
public const string ProtoName = "goodNonLiteralMatched";
|
||||
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
|
||||
[Prototype(ProtoName)]
|
||||
public sealed partial class GoodNonLiteralUnmatchedPrototype : IPrototype
|
||||
{
|
||||
public const string ProtoName = "someOtherNameEntirely";
|
||||
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
|
||||
[Prototype("goodDoesNotEndWithPrototypeWord")]
|
||||
public sealed partial class GoodDoesNotEndWithPrototypeWord : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(9,2): warning RA0033: Prototype BadMatchedPrototype has explicitly set type "badMatched" that matches autogenerated value
|
||||
VerifyCS.Diagnostic(PrototypeAnalyzer.PrototypeRedundantTypeRule).WithSpan(17, 12, 17, 24).WithArguments("BadMatchedPrototype", "badMatched")
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AliasTest()
|
||||
{
|
||||
const string code = """
|
||||
using Robust.Shared.Prototypes;
|
||||
using PPrototypeAttribute = Robust.Shared.Prototypes.PrototypeAttribute;
|
||||
|
||||
[PPrototype("badMatched")]
|
||||
public sealed partial class BadMatchedPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(4,2): warning RA0042: Prototype BadMatchedPrototype has explicitly set type "badMatched" that matches autogenerated value
|
||||
VerifyCS.Diagnostic(PrototypeAnalyzer.PrototypeRedundantTypeRule).WithSpan(4, 13, 4, 25).WithArguments("BadMatchedPrototype", "badMatched")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task MoreAttributesTest()
|
||||
{
|
||||
const string code = """
|
||||
using System;
|
||||
using Robust.Shared.Prototypes;
|
||||
using PPrototypeAttribute = Robust.Shared.Prototypes.PrototypeAttribute;
|
||||
|
||||
[FooBarAttribute]
|
||||
[PPrototype("badMatched")]
|
||||
public sealed partial class BadMatchedPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class FooBarAttribute : Attribute;
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(4,2): warning RA0042: Prototype BadMatchedPrototype has explicitly set type "badMatched" that matches autogenerated value
|
||||
VerifyCS.Diagnostic(PrototypeAnalyzer.PrototypeRedundantTypeRule).WithSpan(6, 13, 6, 25).WithArguments("BadMatchedPrototype", "badMatched")
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task NameEndsWithPrototypeTest()
|
||||
{
|
||||
const string code = """
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
[Prototype]
|
||||
public sealed partial class GoodAutoPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
|
||||
[Prototype("ThisIsFine")]
|
||||
public sealed partial class GoodManual : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
|
||||
[Prototype]
|
||||
public sealed partial class BadAuto : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
}
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(18,29): error RA0043: Prototype BadAuto does not end with the word Prototype
|
||||
VerifyCS.Diagnostic(PrototypeAnalyzer.PrototypeEndsWithPrototypeRule).WithSpan(18, 29, 18, 36).WithArguments("BadAuto")
|
||||
);
|
||||
}
|
||||
}
|
||||
95
Robust.Analyzers.Tests/PrototypeFixerTest.cs
Normal file
95
Robust.Analyzers.Tests/PrototypeFixerTest.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using NUnit.Framework;
|
||||
using VerifyCS =
|
||||
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PrototypeAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
public sealed class PrototypeFixerTest
|
||||
{
|
||||
private static Task Verifier(string code, string fixedCode, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new CSharpCodeFixTest<PrototypeAnalyzer, PrototypeFixer, DefaultVerifier>()
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
Sources = { code },
|
||||
},
|
||||
FixedState =
|
||||
{
|
||||
Sources = { fixedCode },
|
||||
}
|
||||
};
|
||||
|
||||
test.TestState.Sources.Add(("PrototypeAttribute.cs", PrototypeAttributeDef));
|
||||
test.FixedState.Sources.Add(("PrototypeAttribute.cs", PrototypeAttributeDef));
|
||||
|
||||
test.TestState.ExpectedDiagnostics.AddRange(expected);
|
||||
|
||||
return test.RunAsync();
|
||||
}
|
||||
|
||||
private const string PrototypeAttributeDef = """
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.Prototypes
|
||||
{
|
||||
public class PrototypeAttribute : Attribute
|
||||
{
|
||||
public string? Type { get; internal set; }
|
||||
public readonly int LoadPriority = 1;
|
||||
|
||||
public PrototypeAttribute(string? type = null, int loadPriority = 1)
|
||||
{
|
||||
Type = type;
|
||||
LoadPriority = loadPriority;
|
||||
}
|
||||
|
||||
public PrototypeAttribute(int loadPriority)
|
||||
{
|
||||
Type = null;
|
||||
LoadPriority = loadPriority;
|
||||
}
|
||||
}
|
||||
public interface IPrototype;
|
||||
}
|
||||
""";
|
||||
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
const string code = """
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
[Prototype]
|
||||
public sealed partial class GoodAutoPrototype : IPrototype;
|
||||
|
||||
[Prototype("someOtherName")]
|
||||
public sealed partial class GoodUnmatchedPrototype : IPrototype;
|
||||
|
||||
[Prototype("badMatched")]
|
||||
public sealed partial class BadMatchedPrototype : IPrototype;
|
||||
""";
|
||||
|
||||
const string fixedCode = """
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
[Prototype]
|
||||
public sealed partial class GoodAutoPrototype : IPrototype;
|
||||
|
||||
[Prototype("someOtherName")]
|
||||
public sealed partial class GoodUnmatchedPrototype : IPrototype;
|
||||
|
||||
[Prototype]
|
||||
public sealed partial class BadMatchedPrototype : IPrototype;
|
||||
""";
|
||||
|
||||
await Verifier(code, fixedCode,
|
||||
// /0/Test0.cs(9,2): warning RA0033: Prototype BadMatchedPrototype has explicitly set type "badMatched" that matches autogenerated value
|
||||
VerifyCS.Diagnostic(PrototypeAnalyzer.PrototypeRedundantTypeRule).WithSpan(9, 12, 9, 24).WithArguments("BadMatchedPrototype", "badMatched")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,9 @@
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\PreferNonGenericVariantForAttribute.cs" LogicalName="Robust.Shared.Analyzers.PreferNonGenericVariantForAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\PreferOtherTypeAttribute.cs" LogicalName="Robust.Shared.Analyzers.PreferOtherTypeAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ForbidLiteralAttribute.cs" LogicalName="Robust.Shared.Analyzers.ForbidLiteralAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared.Utility\Analyzers\ObsoleteInheritanceAttribute.cs" LogicalName="Robust.Shared.Analyzers.ObsoleteInheritanceAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared.Utility\IoC\DependencyAttribute.cs" LogicalName="Robust.Shared.IoC.DependencyAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ObsoleteInheritanceAttribute.cs" LogicalName="Robust.Shared.Analyzers.ObsoleteInheritanceAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ValidateMemberAttribute.cs" LogicalName="Robust.Shared.Analyzers.ValidateMemberAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\IoC\DependencyAttribute.cs" LogicalName="Robust.Shared.IoC.DependencyAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\GameObjects\EventBusAttributes.cs" LogicalName="Robust.Shared.GameObjects.EventBusAttributes.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Serialization\NetSerializableAttribute.cs" LogicalName="Robust.Shared.Serialization.NetSerializableAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Prototypes\Attributes.cs" LogicalName="Robust.Shared.Prototypes.Attributes.cs" LinkBase="Implementations" />
|
||||
|
||||
96
Robust.Analyzers.Tests/ValidateMemberAnalyzerTest.cs
Normal file
96
Robust.Analyzers.Tests/ValidateMemberAnalyzerTest.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using NUnit.Framework;
|
||||
using VerifyCS =
|
||||
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.ValidateMemberAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
|
||||
namespace Robust.Analyzers.Tests;
|
||||
|
||||
public sealed class ValidateMemberAnalyzerTest
|
||||
{
|
||||
private static Task Verifier(string code, params DiagnosticResult[] expected)
|
||||
{
|
||||
var test = new CSharpAnalyzerTest<ValidateMemberAnalyzer, DefaultVerifier>()
|
||||
{
|
||||
TestState =
|
||||
{
|
||||
Sources = { code },
|
||||
},
|
||||
};
|
||||
|
||||
TestHelper.AddEmbeddedSources(
|
||||
test.TestState,
|
||||
"Robust.Shared.Analyzers.ValidateMemberAttribute.cs"
|
||||
);
|
||||
|
||||
// ExpectedDiagnostics cannot be set, so we need to AddRange here...
|
||||
test.TestState.ExpectedDiagnostics.AddRange(expected);
|
||||
|
||||
return test.RunAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
const string code = """
|
||||
using System;
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
public sealed class TestComponent
|
||||
{
|
||||
public int IntField;
|
||||
public bool BoolField;
|
||||
}
|
||||
|
||||
public sealed class OtherComponent
|
||||
{
|
||||
public float FloatField;
|
||||
public double DoubleField;
|
||||
}
|
||||
|
||||
public sealed class TestManager
|
||||
{
|
||||
public static void DirtyField<T>(T comp, [ValidateMember]string fieldName) { }
|
||||
public static void DirtyTwoFields<T>(T comp, [ValidateMember]string first, [ValidateMember]string second) { }
|
||||
}
|
||||
|
||||
public sealed class TestCaller
|
||||
{
|
||||
public void Test()
|
||||
{
|
||||
var testComp = new TestComponent();
|
||||
var otherComp = new OtherComponent();
|
||||
|
||||
TestManager.DirtyField(testComp, nameof(TestComponent.IntField));
|
||||
|
||||
TestManager.DirtyField(testComp, nameof(OtherComponent.FloatField));
|
||||
|
||||
TestManager.DirtyField(otherComp, nameof(TestComponent.IntField));
|
||||
|
||||
TestManager.DirtyField(otherComp, nameof(OtherComponent.FloatField));
|
||||
|
||||
TestManager.DirtyTwoFields(testComp, nameof(TestComponent.IntField), nameof(TestComponent.BoolField));
|
||||
|
||||
TestManager.DirtyTwoFields(testComp, nameof(TestComponent.IntField), nameof(OtherComponent.FloatField));
|
||||
|
||||
TestManager.DirtyTwoFields(testComp, nameof(OtherComponent.FloatField), nameof(OtherComponent.DoubleField));
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
await Verifier(code,
|
||||
// /0/Test0.cs(31,42): error RA0033: FloatField is not a member of TestComponent
|
||||
VerifyCS.Diagnostic().WithSpan(31, 42, 31, 75).WithArguments("FloatField", "TestComponent"),
|
||||
// /0/Test0.cs(33,43): error RA0033: IntField is not a member of OtherComponent
|
||||
VerifyCS.Diagnostic().WithSpan(33, 43, 33, 73).WithArguments("IntField", "OtherComponent"),
|
||||
// /0/Test0.cs(39,78): error RA0033: FloatField is not a member of TestComponent
|
||||
VerifyCS.Diagnostic().WithSpan(39, 78, 39, 111).WithArguments("FloatField", "TestComponent"),
|
||||
// /0/Test0.cs(41,46): error RA0033: FloatField is not a member of TestComponent
|
||||
VerifyCS.Diagnostic().WithSpan(41, 46, 41, 79).WithArguments("FloatField", "TestComponent"),
|
||||
// /0/Test0.cs(41,81): error RA0033: DoubleField is not a member of TestComponent
|
||||
VerifyCS.Diagnostic().WithSpan(41, 81, 41, 115).WithArguments("DoubleField", "TestComponent")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string DataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataDefinitionAttribute";
|
||||
private const string ImplicitDataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.ImplicitDataDefinitionForInheritorsAttribute";
|
||||
private const string MeansDataDefinitionNamespace = "Robust.Shared.Serialization.Manager.Attributes.MeansDataDefinitionAttribute";
|
||||
private const string DataFieldBaseNamespace = "Robust.Shared.Serialization.Manager.Attributes.DataFieldBaseAttribute";
|
||||
private const string ViewVariablesNamespace = "Robust.Shared.ViewVariables.ViewVariablesAttribute";
|
||||
private const string NotYamlSerializableName = "Robust.Shared.Serialization.Manager.Attributes.NotYamlSerializableAttribute";
|
||||
@@ -270,6 +271,7 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
return false;
|
||||
|
||||
return HasAttribute(type, DataDefinitionNamespace) ||
|
||||
MeansDataDefinition(type) ||
|
||||
IsImplicitDataDefinition(type);
|
||||
}
|
||||
|
||||
@@ -425,6 +427,19 @@ public sealed class DataDefinitionAnalyzer : DiagnosticAnalyzer
|
||||
return (VVAccess)accessByte == VVAccess.ReadWrite;
|
||||
}
|
||||
|
||||
private static bool MeansDataDefinition(ITypeSymbol type)
|
||||
{
|
||||
foreach (var attribute in type.GetAttributes())
|
||||
{
|
||||
if (attribute.AttributeClass is null)
|
||||
continue;
|
||||
|
||||
if (HasAttribute(attribute.AttributeClass, MeansDataDefinitionNamespace))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsNotYamlSerializable(ISymbol field, ITypeSymbol type)
|
||||
{
|
||||
return HasAttribute(type, NotYamlSerializableName);
|
||||
|
||||
16
Robust.Analyzers/ITypeSymbolExtensions.cs
Normal file
16
Robust.Analyzers/ITypeSymbolExtensions.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
public static class ITypeSymbolExtensions
|
||||
{
|
||||
public static IEnumerable<ITypeSymbol> GetBaseTypesAndThis(this ITypeSymbol type)
|
||||
{
|
||||
var current = type;
|
||||
while (current != null)
|
||||
{
|
||||
yield return current;
|
||||
current = current.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
139
Robust.Analyzers/PrototypeAnalyzer.cs
Normal file
139
Robust.Analyzers/PrototypeAnalyzer.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
#nullable enable
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Robust.Roslyn.Shared;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class PrototypeAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
public static readonly DiagnosticDescriptor PrototypeRedundantTypeRule = new(
|
||||
Diagnostics.IdPrototypeRedundantType,
|
||||
"Redundant Prototype Type specification",
|
||||
"Prototype {0} has explicitly set type \"{1}\" that matches autogenerated value",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Warning,
|
||||
true,
|
||||
"Remove the redundant type specification."
|
||||
);
|
||||
|
||||
public static readonly DiagnosticDescriptor PrototypeEndsWithPrototypeRule = new(
|
||||
Diagnostics.IdPrototypeEndsWithPrototype,
|
||||
"Prototype name must end with the word Prototype",
|
||||
"Prototype {0} does not end with the word Prototype",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Add the word Prototype to the end of the class name or manually specify a name in the Prototype attribute."
|
||||
);
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
|
||||
[PrototypeRedundantTypeRule, PrototypeEndsWithPrototypeRule];
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze |
|
||||
GeneratedCodeAnalysisFlags.ReportDiagnostics);
|
||||
context.EnableConcurrentExecution();
|
||||
|
||||
context.RegisterCompilationStartAction(static ctx =>
|
||||
{
|
||||
var prototypeAttribute =
|
||||
ctx.Compilation.GetTypeByMetadataName("Robust.Shared.Prototypes.PrototypeAttribute");
|
||||
|
||||
// No attribute, no analyzer.
|
||||
if (prototypeAttribute is null)
|
||||
return;
|
||||
|
||||
ctx.RegisterSyntaxNodeAction(
|
||||
symCtx => AnalyzePrototype(symCtx, prototypeAttribute),
|
||||
SyntaxKind.ClassDeclaration);
|
||||
});
|
||||
}
|
||||
|
||||
private static void AnalyzePrototype(SyntaxNodeAnalysisContext context, INamedTypeSymbol prototypeAttributeSymbol)
|
||||
{
|
||||
if (context.Node is not ClassDeclarationSyntax classDeclarationSyntax)
|
||||
return;
|
||||
|
||||
var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax);
|
||||
if (classSymbol is null)
|
||||
return;
|
||||
|
||||
var className = classSymbol.Name;
|
||||
|
||||
if (!AttributeHelper.HasAttribute(classSymbol, prototypeAttributeSymbol, out var attributeData))
|
||||
return;
|
||||
|
||||
var prototypeAttribute = GetAttributeSyntax(attributeData, classDeclarationSyntax);
|
||||
if (prototypeAttribute == null)
|
||||
return;
|
||||
|
||||
// Check for autogenerated type
|
||||
if (prototypeAttribute.ArgumentList?.Arguments[0] is not { } argumentSyntax)
|
||||
{
|
||||
if (!className.EndsWith(PrototypeUtility.PrototypeNameEnding))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(PrototypeEndsWithPrototypeRule,
|
||||
classDeclarationSyntax.Identifier.GetLocation(),
|
||||
className));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We only care about redundancy if the argument is a string literal.
|
||||
// Passing in a value that resolves to a redundant string is fine.
|
||||
if (argumentSyntax.Expression is not LiteralExpressionSyntax literalSyntax)
|
||||
return;
|
||||
|
||||
var literalValue = context.SemanticModel.GetConstantValue(literalSyntax);
|
||||
if (literalValue.Value is not string specifiedName)
|
||||
return;
|
||||
|
||||
var autoName = PrototypeUtility.CalculatePrototypeName(className);
|
||||
|
||||
// Check for name redundancy
|
||||
if (autoName == specifiedName)
|
||||
{
|
||||
// If the class name does not end with "Prototype", allow the redundancy
|
||||
if (!className.EndsWith(PrototypeUtility.PrototypeNameEnding))
|
||||
return;
|
||||
|
||||
var location = argumentSyntax.GetLocation();
|
||||
context.ReportDiagnostic(Diagnostic.Create(PrototypeRedundantTypeRule,
|
||||
location,
|
||||
className,
|
||||
specifiedName));
|
||||
}
|
||||
}
|
||||
|
||||
private static AttributeSyntax? GetAttributeSyntax(
|
||||
AttributeData attributeData,
|
||||
ClassDeclarationSyntax classSyntax)
|
||||
{
|
||||
if (attributeData.ApplicationSyntaxReference is not { } syntaxReference)
|
||||
return null;
|
||||
|
||||
foreach (var attributeList in classSyntax.AttributeLists)
|
||||
{
|
||||
foreach (var attribute in attributeList.Attributes)
|
||||
{
|
||||
if (syntaxReference.SyntaxTree != attribute.SyntaxTree)
|
||||
continue;
|
||||
|
||||
if (!syntaxReference.Span.OverlapsWith(attribute.Span))
|
||||
continue;
|
||||
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
77
Robust.Analyzers/PrototypeFixer.cs
Normal file
77
Robust.Analyzers/PrototypeFixer.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
#nullable enable
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using static Robust.Roslyn.Shared.Diagnostics;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
||||
public sealed class PrototypeFixer : CodeFixProvider
|
||||
{
|
||||
public override ImmutableArray<string> FixableDiagnosticIds => [IdPrototypeRedundantType];
|
||||
|
||||
public override FixAllProvider GetFixAllProvider()
|
||||
{
|
||||
return WellKnownFixAllProviders.BatchFixer;
|
||||
}
|
||||
|
||||
public override Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||
{
|
||||
foreach (var diagnostic in context.Diagnostics)
|
||||
{
|
||||
switch (diagnostic.Id)
|
||||
{
|
||||
case IdPrototypeRedundantType:
|
||||
return RegisterRemoveType(context, diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static async Task RegisterRemoveType(CodeFixContext context, Diagnostic diagnostic)
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
||||
var span = diagnostic.Location.SourceSpan;
|
||||
var token = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<AttributeArgumentSyntax>().First();
|
||||
|
||||
if (token == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(CodeAction.Create(
|
||||
"Remove explicitly set type",
|
||||
c => RemoveType(context.Document, token, c),
|
||||
"Remove explicitly set type"
|
||||
), diagnostic);
|
||||
}
|
||||
|
||||
private static async Task<Document> RemoveType(Document document, AttributeArgumentSyntax syntax, CancellationToken cancellation)
|
||||
{
|
||||
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
||||
|
||||
if (syntax.Parent is not AttributeArgumentListSyntax argListSyntax)
|
||||
return document;
|
||||
|
||||
if (argListSyntax.Arguments.Count == 1)
|
||||
{
|
||||
// If this is the only argument, remove the whole argument list so we don't leave empty parentheses
|
||||
if (argListSyntax.Parent is not AttributeSyntax attributeSyntax)
|
||||
return document;
|
||||
|
||||
var newAttributeSyntax = attributeSyntax.RemoveNode(argListSyntax, SyntaxRemoveOptions.KeepNoTrivia);
|
||||
root = root!.ReplaceNode(attributeSyntax, newAttributeSyntax!);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise just remove the argument
|
||||
var newArgListSyntax = argListSyntax.WithArguments(argListSyntax.Arguments.Remove(syntax));
|
||||
root = root!.ReplaceNode(argListSyntax, newArgListSyntax);
|
||||
}
|
||||
|
||||
|
||||
return document.WithSyntaxRoot(root);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,11 @@
|
||||
<Compile Include="..\Robust.Shared\Serialization\NetSerializableAttribute.cs" LinkBase="Implementations" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Needed for PrototypeAnalyzer. -->
|
||||
<Compile Include="..\Robust.Shared\Prototypes\PrototypeUtility.cs" LinkBase="Implementations" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="../Robust.Roslyn.Shared/Robust.Roslyn.Shared.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
95
Robust.Analyzers/ValidateMemberAnalyzer.cs
Normal file
95
Robust.Analyzers/ValidateMemberAnalyzer.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
#nullable enable
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Robust.Roslyn.Shared;
|
||||
|
||||
namespace Robust.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public sealed class ValidateMemberAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string ValidateMemberType = "Robust.Shared.Analyzers.ValidateMemberAttribute";
|
||||
|
||||
private static readonly DiagnosticDescriptor ValidateMemberDescriptor = new(
|
||||
Diagnostics.IdValidateMember,
|
||||
"Invalid member name",
|
||||
"{0} is not a member of {1}",
|
||||
"Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
true,
|
||||
"Be sure the type and member name are correct.");
|
||||
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [ValidateMemberDescriptor];
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
context.EnableConcurrentExecution();
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
|
||||
context.RegisterSyntaxNodeAction(AnalyzeExpression, SyntaxKind.InvocationExpression);
|
||||
}
|
||||
|
||||
private void AnalyzeExpression(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
if (context.Node is not InvocationExpressionSyntax node)
|
||||
return;
|
||||
|
||||
if (context.SemanticModel.GetSymbolInfo(node.Expression).Symbol is not IMethodSymbol methodSymbol)
|
||||
return;
|
||||
|
||||
// We need at least one type argument for context
|
||||
if (methodSymbol.TypeArguments.Length < 1)
|
||||
return;
|
||||
|
||||
// We'll be checking members of the first type argument
|
||||
if (methodSymbol.TypeArguments[0] is not INamedTypeSymbol targetType)
|
||||
return;
|
||||
|
||||
// We defer building this set until we need it later, so we don't have to build it for every single method invocation!
|
||||
ImmutableHashSet<ISymbol>? members = null;
|
||||
|
||||
// Check each parameter of the method
|
||||
foreach (var parameterContext in node.ArgumentList.Arguments)
|
||||
{
|
||||
|
||||
// Get the symbol for this parameter
|
||||
if (context.SemanticModel.GetOperation(parameterContext) is not IArgumentOperation op || op.Parameter is null)
|
||||
continue;
|
||||
var parameterSymbol = op.Parameter.OriginalDefinition;
|
||||
|
||||
// Make sure the parameter has the ValidateMember attribute
|
||||
if (!AttributeHelper.HasAttribute(parameterSymbol, ValidateMemberType, out _))
|
||||
continue;
|
||||
|
||||
// Find the value passed for this parameter.
|
||||
// We use GetConstantValue to resolve compile-time values - i.e. the result of nameof()
|
||||
if (context.SemanticModel.GetConstantValue(parameterContext.Expression).Value is not string fieldName)
|
||||
continue;
|
||||
|
||||
// Get a set containing all the members of the target type and its ancestors
|
||||
members ??= targetType.GetBaseTypesAndThis().SelectMany(n => n.GetMembers()).ToImmutableHashSet(SymbolEqualityComparer.Default);
|
||||
|
||||
// Check each member of the target type to see if it matches our passed in value
|
||||
var found = false;
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (member.Name == fieldName)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we didn't find it, report the violation
|
||||
if (!found)
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
ValidateMemberDescriptor,
|
||||
parameterContext.GetLocation(),
|
||||
fieldName,
|
||||
targetType.Name
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,5 +59,6 @@ public sealed class DefaultSQLConfig : IConfig
|
||||
public CultureInfo CultureInfo => DefaultConfig.Instance.CultureInfo!;
|
||||
public ConfigOptions Options => DefaultConfig.Instance.Options;
|
||||
public TimeSpan BuildTimeout => DefaultConfig.Instance.BuildTimeout;
|
||||
public WakeLockType WakeLock => DefaultConfig.Instance.WakeLock;
|
||||
public IReadOnlyList<Conclusion> ConfigAnalysisConclusion => DefaultConfig.Instance.ConfigAnalysisConclusion;
|
||||
}
|
||||
|
||||
19
Robust.Benchmarks/NumericsHelpers/Box2Benchmark.cs
Normal file
19
Robust.Benchmarks/NumericsHelpers/Box2Benchmark.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System.Numerics;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Benchmarks.NumericsHelpers;
|
||||
|
||||
[Virtual, DisassemblyDiagnoser]
|
||||
public class Box2Benchmark
|
||||
{
|
||||
public Box2 Box = new();
|
||||
public Matrix3x2 Matrix = new();
|
||||
|
||||
[Benchmark]
|
||||
public Box2 Transform()
|
||||
{
|
||||
return Matrix.TransformBox(Box);
|
||||
}
|
||||
}
|
||||
18
Robust.Benchmarks/NumericsHelpers/Box2RotatedBenchmark.cs
Normal file
18
Robust.Benchmarks/NumericsHelpers/Box2RotatedBenchmark.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Numerics;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Benchmarks.NumericsHelpers;
|
||||
|
||||
[Virtual, DisassemblyDiagnoser]
|
||||
public class Box2RotatedBenchmark
|
||||
{
|
||||
public Box2Rotated Box = new();
|
||||
|
||||
[Benchmark]
|
||||
public Matrix3x2 GetTransform()
|
||||
{
|
||||
return Box.Transform;
|
||||
}
|
||||
}
|
||||
25
Robust.Benchmarks/NumericsHelpers/GetAABBBenchmark.cs
Normal file
25
Robust.Benchmarks/NumericsHelpers/GetAABBBenchmark.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Runtime.Intrinsics;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Benchmarks.NumericsHelpers;
|
||||
|
||||
[Virtual, DisassemblyDiagnoser]
|
||||
public class GetAABBBenchmark
|
||||
{
|
||||
public Vector128<float> X;
|
||||
public Vector128<float> Y;
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public Vector128<float> GetAABB_NoAvx()
|
||||
{
|
||||
return SimdHelpers.GetAABBSlow(X, Y);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public Vector128<float> GetAABB_Avx()
|
||||
{
|
||||
return SimdHelpers.GetAABBAvx(X, Y);
|
||||
}
|
||||
}
|
||||
21
Robust.Benchmarks/NumericsHelpers/SlimPolygonBenchmark.cs
Normal file
21
Robust.Benchmarks/NumericsHelpers/SlimPolygonBenchmark.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Shapes;
|
||||
|
||||
namespace Robust.Benchmarks.NumericsHelpers;
|
||||
|
||||
// SlimPolyon is internal, so this won't compile without changes.
|
||||
/*
|
||||
[Virtual, DisassemblyDiagnoser]
|
||||
public class SlimPolygonBenchmark
|
||||
{
|
||||
public Box2Rotated RotBox = new();
|
||||
|
||||
[Benchmark]
|
||||
public SlimPolygon RotatedBox()
|
||||
{
|
||||
return new SlimPolygon(in RotBox);
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -1,96 +0,0 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.Physics;
|
||||
|
||||
[Virtual]
|
||||
[MediumRunJob]
|
||||
public class PhysicsBoxStackBenchmark
|
||||
{
|
||||
private ISimulation _sim = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_sim = RobustServerSimulation.NewSimulation().InitializeInstance();
|
||||
|
||||
var entManager = _sim.Resolve<IEntityManager>();
|
||||
entManager.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
SetupTumbler(entManager, mapId);
|
||||
|
||||
for (var i = 0; i < 30; i++)
|
||||
{
|
||||
entManager.TickUpdate(0.016f, false);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void BoxStack()
|
||||
{
|
||||
var entManager = _sim.Resolve<IEntityManager>();
|
||||
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
entManager.TickUpdate(0.016f, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupTumbler(IEntityManager entManager, MapId mapId)
|
||||
{
|
||||
var physics = entManager.System<SharedPhysicsSystem>();
|
||||
var fixtures = entManager.System<FixtureSystem>();
|
||||
|
||||
var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
|
||||
var ground = entManager.AddComponent<PhysicsComponent>(groundUid);
|
||||
|
||||
var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0));
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
|
||||
|
||||
var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10));
|
||||
fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground);
|
||||
|
||||
var xs = new[]
|
||||
{
|
||||
0.0f, -10.0f, -5.0f, 5.0f, 10.0f
|
||||
};
|
||||
|
||||
var columnCount = 1;
|
||||
var rowCount = 15;
|
||||
PolygonShape shape;
|
||||
|
||||
for (var j = 0; j < columnCount; j++)
|
||||
{
|
||||
for (var i = 0; i < rowCount; i++)
|
||||
{
|
||||
var x = 0.0f;
|
||||
|
||||
var boxUid = entManager.SpawnEntity(null,
|
||||
new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 1.1f * i), mapId));
|
||||
var box = entManager.AddComponent<PhysicsComponent>(boxUid);
|
||||
|
||||
physics.SetBodyType(boxUid, BodyType.Dynamic, body: box);
|
||||
|
||||
shape = new PolygonShape();
|
||||
shape.SetAsBox(0.5f, 0.5f);
|
||||
physics.SetFixedRotation(boxUid, false, body: box);
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true), body: box);
|
||||
|
||||
physics.WakeBody(boxUid, body: box);
|
||||
physics.SetSleepingAllowed(boxUid, box, false);
|
||||
}
|
||||
}
|
||||
|
||||
physics.WakeBody(groundUid, body: ground);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
using System.Numerics;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.Physics;
|
||||
|
||||
[Virtual]
|
||||
public class PhysicsCircleStackBenchmark
|
||||
{
|
||||
private ISimulation _sim = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_sim = RobustServerSimulation.NewSimulation().InitializeInstance();
|
||||
|
||||
var entManager = _sim.Resolve<IEntityManager>();
|
||||
entManager.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
SetupTumbler(entManager, mapId);
|
||||
|
||||
for (var i = 0; i < 30; i++)
|
||||
{
|
||||
entManager.TickUpdate(0.016f, false);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void CircleStack()
|
||||
{
|
||||
var entManager = _sim.Resolve<IEntityManager>();
|
||||
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
entManager.TickUpdate(0.016f, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupTumbler(IEntityManager entManager, MapId mapId)
|
||||
{
|
||||
var physics = entManager.System<SharedPhysicsSystem>();
|
||||
var fixtures = entManager.System<FixtureSystem>();
|
||||
|
||||
var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
|
||||
var ground = entManager.AddComponent<PhysicsComponent>(groundUid);
|
||||
|
||||
var horizontal = new EdgeShape(new Vector2(-40, 0), new Vector2(40, 0));
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
|
||||
|
||||
var vertical = new EdgeShape(new Vector2(20, 0), new Vector2(20, 20));
|
||||
fixtures.CreateFixture(groundUid, "fix2", new Fixture(vertical, 2, 2, true), body: ground);
|
||||
|
||||
var xs = new[]
|
||||
{
|
||||
0.0f, -10.0f, -5.0f, 5.0f, 10.0f
|
||||
};
|
||||
|
||||
var columnCount = 1;
|
||||
var rowCount = 15;
|
||||
PhysShapeCircle shape;
|
||||
|
||||
for (var j = 0; j < columnCount; j++)
|
||||
{
|
||||
for (var i = 0; i < rowCount; i++)
|
||||
{
|
||||
var x = 0.0f;
|
||||
|
||||
var boxUid = entManager.SpawnEntity(null,
|
||||
new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 2.1f * i), mapId));
|
||||
var box = entManager.AddComponent<PhysicsComponent>(boxUid);
|
||||
|
||||
physics.SetBodyType(boxUid, BodyType.Dynamic, body: box);
|
||||
shape = new PhysShapeCircle(0.5f);
|
||||
physics.SetFixedRotation(boxUid, false, body: box);
|
||||
// TODO: Need to detect shape and work out if we need to use fixedrotation
|
||||
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f));
|
||||
physics.WakeBody(boxUid, body: box);
|
||||
physics.SetSleepingAllowed(boxUid, box, false);
|
||||
}
|
||||
}
|
||||
|
||||
physics.WakeBody(groundUid, body: ground);
|
||||
}
|
||||
}
|
||||
312
Robust.Benchmarks/Physics/PhysicsBenchmark.cs
Normal file
312
Robust.Benchmarks/Physics/PhysicsBenchmark.cs
Normal file
@@ -0,0 +1,312 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.Physics;
|
||||
|
||||
[Virtual, MediumRunJob]
|
||||
public class PhysicsBenchmark
|
||||
{
|
||||
// TODO: Rain
|
||||
// Large pyramid
|
||||
// Joint Grid
|
||||
// Spinner
|
||||
// Washer
|
||||
|
||||
const float frameTime = 0.016f;
|
||||
|
||||
#region Many Pyramids
|
||||
|
||||
private ISimulation _manyPyramidSim = default!;
|
||||
|
||||
[GlobalSetup(Target = nameof(ManyPyramids))]
|
||||
public void PyramidSetup()
|
||||
{
|
||||
_manyPyramidSim = RobustServerSimulation.NewSimulation().InitializeInstance();
|
||||
|
||||
var entManager = _manyPyramidSim.Resolve<IEntityManager>();
|
||||
entManager.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
SetupManyPyramids(entManager, mapId);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ManyPyramids()
|
||||
{
|
||||
var entManager = _manyPyramidSim.Resolve<IEntityManager>();
|
||||
|
||||
for (var i = 0; i < (1f / frameTime) * 10; i++)
|
||||
{
|
||||
entManager.TickUpdate(frameTime, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupManyPyramids(IEntityManager entManager, MapId mapId)
|
||||
{
|
||||
int baseCount = 10;
|
||||
float extent = 0.5f;
|
||||
int rowCount = 20; // 5
|
||||
int columnCount = 5;
|
||||
|
||||
// Setup ground
|
||||
var physics = entManager.System<SharedPhysicsSystem>();
|
||||
var fixtures = entManager.System<FixtureSystem>();
|
||||
physics.SetGravity(new Vector2(0f, -9.8f));
|
||||
|
||||
// Setup boxes
|
||||
float a = 0.5f;
|
||||
PolygonShape shape = new();
|
||||
shape.SetAsBox(a, a);
|
||||
|
||||
float groundDeltaY = 2.0f * extent * ( baseCount + 1.0f );
|
||||
float groundWidth = 2.0f * extent * columnCount * ( baseCount + 1.0f );
|
||||
|
||||
float groundY = 0.0f;
|
||||
|
||||
for ( int i = 0; i < rowCount; ++i )
|
||||
{
|
||||
var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
|
||||
var ground = entManager.AddComponent<PhysicsComponent>(groundUid);
|
||||
|
||||
var horizontal = new EdgeShape(new Vector2(-0.5f * 2.0f * groundWidth, groundY), new Vector2(0.5f * 2.0f * groundWidth, groundY));
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
|
||||
physics.WakeBody(groundUid, body: ground);
|
||||
groundY += groundDeltaY;
|
||||
}
|
||||
|
||||
float baseWidth = 2.0f * extent * baseCount;
|
||||
float baseY = 0.0f;
|
||||
|
||||
for ( int i = 0; i < rowCount; ++i )
|
||||
{
|
||||
for ( int j = 0; j < columnCount; ++j )
|
||||
{
|
||||
float centerX = -0.5f * groundWidth + j * ( baseWidth + 2.0f * extent ) + extent;
|
||||
CreateSmallPyramid(entManager, mapId, baseCount, extent, centerX, baseY);
|
||||
}
|
||||
|
||||
baseY += groundDeltaY;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateSmallPyramid(IEntityManager entManager, MapId mapId, int baseCount, float extent, float centerX, float baseY)
|
||||
{
|
||||
var physics = entManager.System<SharedPhysicsSystem>();
|
||||
var fixtures = entManager.System<FixtureSystem>();
|
||||
var shape = new PolygonShape();
|
||||
shape.SetAsBox(extent, extent);
|
||||
|
||||
for ( int i = 0; i < baseCount; ++i )
|
||||
{
|
||||
float y = ( 2.0f * i + 1.0f ) * extent + baseY;
|
||||
|
||||
for ( int j = i; j < baseCount; ++j )
|
||||
{
|
||||
float x = ( i + 1.0f ) * extent + 2.0f * ( j - i ) * extent + centerX - 0.5f;
|
||||
|
||||
var boxUid = entManager.SpawnEntity(null, new MapCoordinates(new Vector2(x, y), mapId));
|
||||
var box = entManager.AddComponent<PhysicsComponent>(boxUid);
|
||||
physics.SetBodyType(boxUid, BodyType.Dynamic, body: box);
|
||||
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f), body: box);
|
||||
physics.WakeBody(boxUid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Smash
|
||||
|
||||
private ISimulation _smashSim = default!;
|
||||
|
||||
[GlobalSetup(Target = nameof(Smash))]
|
||||
public void SmashSetup()
|
||||
{
|
||||
_smashSim = RobustServerSimulation.NewSimulation().InitializeInstance();
|
||||
|
||||
var entManager = _smashSim.Resolve<IEntityManager>();
|
||||
|
||||
entManager.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
|
||||
var physics = entManager.System<SharedPhysicsSystem>();
|
||||
var fixtures = entManager.System<FixtureSystem>();
|
||||
var joints = entManager.System<SharedJointSystem>();
|
||||
var xformSystem = entManager.System<SharedTransformSystem>();
|
||||
physics.SetGravity(new Vector2(0f, -9.8f));
|
||||
|
||||
{
|
||||
var smashBox = new PolygonShape();
|
||||
smashBox.SetAsBox(4f, 4f);
|
||||
|
||||
var bodyUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId));
|
||||
var body = entManager.AddComponent<PhysicsComponent>(bodyUid);
|
||||
|
||||
physics.SetBodyType(bodyUid, BodyType.Dynamic, body: body);
|
||||
physics.SetSleepingAllowed(bodyUid, body, false);
|
||||
physics.SetFixedRotation(bodyUid, false, body: body);
|
||||
xformSystem.SetLocalPosition(bodyUid, new Vector2(-20f, 0f));
|
||||
physics.SetLinearVelocity(bodyUid, new Vector2(40f, 0f));
|
||||
|
||||
fixtures.TryCreateFixture(bodyUid, smashBox, "fix1", density: 8f, hard: true);
|
||||
}
|
||||
|
||||
float d = 0.4f;
|
||||
var box = new PolygonShape();
|
||||
box.SetAsBox(0.5f * d, 0.5f * d);
|
||||
|
||||
int columns = 120; // 20
|
||||
int rows = 80; // 10
|
||||
|
||||
for ( int i = 0; i < columns; ++i )
|
||||
{
|
||||
for ( int j = 0; j < rows; ++j )
|
||||
{
|
||||
var bodyUid = entManager.SpawnEntity(null, new MapCoordinates(i * d + 30f, ( j - rows / 2.0f ) * d, mapId));
|
||||
var body = entManager.AddComponent<PhysicsComponent>(bodyUid);
|
||||
|
||||
physics.SetBodyType(bodyUid, BodyType.Dynamic, body: body);
|
||||
physics.SetSleepingAllowed(bodyUid, body, false);
|
||||
physics.SetFixedRotation(bodyUid, false, body: body);
|
||||
xformSystem.SetLocalPosition(bodyUid, new Vector2(-20f, 0f));
|
||||
physics.SetLinearVelocity(bodyUid, new Vector2(40f, 0f));
|
||||
|
||||
fixtures.TryCreateFixture(bodyUid, box, "fix1", hard: true);
|
||||
physics.WakeBody(bodyUid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Smash()
|
||||
{
|
||||
var entManager = _smashSim.Resolve<IEntityManager>();
|
||||
|
||||
for (var i = 0; i < (1f / frameTime) * 10; i++)
|
||||
{
|
||||
entManager.TickUpdate(frameTime, false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tumbler
|
||||
|
||||
private ISimulation _tumblerSim = default!;
|
||||
|
||||
[GlobalSetup(Target = nameof(Tumbler))]
|
||||
public void TumblerSetup()
|
||||
{
|
||||
_tumblerSim = RobustServerSimulation.NewSimulation().InitializeInstance();
|
||||
|
||||
var entManager = _tumblerSim.Resolve<IEntityManager>();
|
||||
|
||||
entManager.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
SetupTumbler(entManager, mapId);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Tumbler()
|
||||
{
|
||||
var entManager = _tumblerSim.Resolve<IEntityManager>();
|
||||
|
||||
for (var i = 0; i < 1 / frameTime * 10; i++)
|
||||
{
|
||||
entManager.TickUpdate(frameTime, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupTumbler(IEntityManager entManager, MapId mapId)
|
||||
{
|
||||
var physics = entManager.System<SharedPhysicsSystem>();
|
||||
var fixtures = entManager.System<FixtureSystem>();
|
||||
var joints = entManager.System<SharedJointSystem>();
|
||||
physics.SetGravity(new Vector2(0f, -9.8f));
|
||||
|
||||
{
|
||||
var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 0f, mapId));
|
||||
var ground = entManager.AddComponent<PhysicsComponent>(groundUid);
|
||||
// Due to lookup changes fixtureless bodies are invalid, so
|
||||
var cShape = new PhysShapeCircle(1f);
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(cShape, 0, 0, false));
|
||||
|
||||
var bodyUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId));
|
||||
var body = entManager.AddComponent<PhysicsComponent>(bodyUid);
|
||||
|
||||
physics.SetBodyType(bodyUid, BodyType.Dynamic, body: body);
|
||||
physics.SetSleepingAllowed(bodyUid, body, false);
|
||||
physics.SetFixedRotation(bodyUid, false, body: body);
|
||||
|
||||
|
||||
// TODO: Box2D just deref, bleh shape structs someday
|
||||
var shape1 = new PolygonShape();
|
||||
shape1.SetAsBox(0.5f, 10.0f, new Vector2(10.0f, 0.0f), 0.0f);
|
||||
fixtures.CreateFixture(bodyUid, "fix1", new Fixture(shape1, 2, 0, true, 50f));
|
||||
|
||||
var shape2 = new PolygonShape();
|
||||
shape2.SetAsBox(0.5f, 10.0f, new Vector2(-10.0f, 0.0f), 0f);
|
||||
fixtures.CreateFixture(bodyUid, "fix2", new Fixture(shape2, 2, 0, true, 50f));
|
||||
|
||||
var shape3 = new PolygonShape();
|
||||
shape3.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, 10.0f), 0f);
|
||||
fixtures.CreateFixture(bodyUid, "fix3", new Fixture(shape3, 2, 0, true, 50f));
|
||||
|
||||
var shape4 = new PolygonShape();
|
||||
shape4.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, -10.0f), 0f);
|
||||
fixtures.CreateFixture(bodyUid, "fix4", new Fixture(shape4, 2, 0, true, 50f));
|
||||
|
||||
physics.WakeBody(groundUid, body: ground);
|
||||
physics.WakeBody(bodyUid, body: body);
|
||||
var revolute = joints.CreateRevoluteJoint(groundUid, bodyUid);
|
||||
|
||||
var motorSpeed = 25f;
|
||||
|
||||
revolute.LocalAnchorA = new Vector2(0f, 10f);
|
||||
revolute.LocalAnchorB = new Vector2(0f, 0f);
|
||||
revolute.ReferenceAngle = 0f;
|
||||
revolute.MotorSpeed = MathF.PI / 180f * motorSpeed;
|
||||
revolute.MaxMotorTorque = 100000000f;
|
||||
revolute.EnableMotor = true;
|
||||
}
|
||||
|
||||
// Make boxes
|
||||
{
|
||||
var gridCount = 20; // 45
|
||||
var y = -0.2f * gridCount + 10f;
|
||||
|
||||
var a = 0.125f;
|
||||
PolygonShape shape = new();
|
||||
shape.SetAsBox(a, a);
|
||||
|
||||
for (var i = 0; i < gridCount; i++)
|
||||
{
|
||||
var x = -0.2f * gridCount;
|
||||
|
||||
for (var j = 0; j < gridCount; j++)
|
||||
{
|
||||
var boxUid = entManager.SpawnEntity(null, new MapCoordinates(new Vector2(x, y), mapId));
|
||||
var body = entManager.AddComponent<PhysicsComponent>(boxUid);
|
||||
physics.SetBodyType(boxUid, BodyType.Dynamic, body: body);
|
||||
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f), body: body);
|
||||
x += 0.4f;
|
||||
|
||||
physics.WakeBody(boxUid, body: body);
|
||||
physics.SetSleepingAllowed(boxUid, body, false);
|
||||
}
|
||||
|
||||
y += 0.4f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.Physics;
|
||||
|
||||
[Virtual]
|
||||
public class PhysicsPyramidBenchmark
|
||||
{
|
||||
private ISimulation _sim = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_sim = RobustServerSimulation.NewSimulation().InitializeInstance();
|
||||
|
||||
var entManager = _sim.Resolve<IEntityManager>();
|
||||
entManager.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
SetupTumbler(entManager, mapId);
|
||||
|
||||
for (var i = 0; i < 300; i++)
|
||||
{
|
||||
entManager.TickUpdate(0.016f, false);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Pyramid()
|
||||
{
|
||||
var entManager = _sim.Resolve<IEntityManager>();
|
||||
|
||||
for (var i = 0; i < 5000; i++)
|
||||
{
|
||||
entManager.TickUpdate(0.016f, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupTumbler(IEntityManager entManager, MapId mapId)
|
||||
{
|
||||
const byte count = 20;
|
||||
|
||||
// Setup ground
|
||||
var physics = entManager.System<SharedPhysicsSystem>();
|
||||
var fixtures = entManager.System<FixtureSystem>();
|
||||
var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
|
||||
var ground = entManager.AddComponent<PhysicsComponent>(groundUid);
|
||||
|
||||
var horizontal = new EdgeShape(new Vector2(40, 0), new Vector2(-40, 0));
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(horizontal, 2, 2, true), body: ground);
|
||||
physics.WakeBody(groundUid, body: ground);
|
||||
|
||||
// Setup boxes
|
||||
float a = 0.5f;
|
||||
PolygonShape shape = new();
|
||||
shape.SetAsBox(a, a);
|
||||
|
||||
var x = new Vector2(-7.0f, 0.75f);
|
||||
Vector2 y;
|
||||
Vector2 deltaX = new Vector2(0.5625f, 1.25f);
|
||||
Vector2 deltaY = new Vector2(1.125f, 0.0f);
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
y = x;
|
||||
|
||||
for (var j = i; j < count; ++j)
|
||||
{
|
||||
var boxUid = entManager.SpawnEntity(null, new MapCoordinates(y, mapId));
|
||||
var box = entManager.AddComponent<PhysicsComponent>(boxUid);
|
||||
physics.SetBodyType(boxUid, BodyType.Dynamic, body: box);
|
||||
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 5f), body: box);
|
||||
y += deltaY;
|
||||
|
||||
physics.WakeBody(boxUid, body: box);
|
||||
physics.SetSleepingAllowed(boxUid, box, false);
|
||||
}
|
||||
|
||||
x += deltaX;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.Benchmarks.Physics;
|
||||
|
||||
[Virtual]
|
||||
public class PhysicsTumblerBenchmark
|
||||
{
|
||||
private ISimulation _sim = default!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_sim = RobustServerSimulation.NewSimulation().InitializeInstance();
|
||||
|
||||
var entManager = _sim.Resolve<IEntityManager>();
|
||||
var physics = entManager.System<SharedPhysicsSystem>();
|
||||
var fixtures = entManager.System<FixtureSystem>();
|
||||
var mapUid = entManager.System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
SetupTumbler(entManager, mapId);
|
||||
|
||||
for (var i = 0; i < 300; i++)
|
||||
{
|
||||
entManager.TickUpdate(0.016f, false);
|
||||
var boxUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId));
|
||||
var box = entManager.AddComponent<PhysicsComponent>(boxUid);
|
||||
physics.SetBodyType(boxUid, BodyType.Dynamic, body: box);
|
||||
physics.SetFixedRotation(boxUid, false, body: box);
|
||||
var shape = new PolygonShape();
|
||||
shape.SetAsBox(0.125f, 0.125f);
|
||||
fixtures.CreateFixture(boxUid, "fix1", new Fixture(shape, 2, 2, true, 0.0625f), body: box);
|
||||
physics.WakeBody(boxUid, body: box);
|
||||
physics.SetSleepingAllowed(boxUid, box, false);
|
||||
}
|
||||
|
||||
if (entManager.TryGetComponent(mapUid, out BroadphaseComponent? mapBroadphase))
|
||||
entManager.System<SharedBroadphaseSystem>().RebuildBottomUp(mapBroadphase);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Tumbler()
|
||||
{
|
||||
var entManager = _sim.Resolve<IEntityManager>();
|
||||
|
||||
for (var i = 0; i < 1000; i++)
|
||||
{
|
||||
entManager.TickUpdate(0.016f, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupTumbler(IEntityManager entManager, MapId mapId)
|
||||
{
|
||||
var physics = entManager.System<SharedPhysicsSystem>();
|
||||
var fixtures = entManager.System<FixtureSystem>();
|
||||
var joints = entManager.System<SharedJointSystem>();
|
||||
|
||||
var groundUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 0f, mapId));
|
||||
var ground = entManager.AddComponent<PhysicsComponent>(groundUid);
|
||||
// Due to lookup changes fixtureless bodies are invalid, so
|
||||
var cShape = new PhysShapeCircle(1f);
|
||||
fixtures.CreateFixture(groundUid, "fix1", new Fixture(cShape, 0, 0, false));
|
||||
|
||||
var bodyUid = entManager.SpawnEntity(null, new MapCoordinates(0f, 10f, mapId));
|
||||
var body = entManager.AddComponent<PhysicsComponent>(bodyUid);
|
||||
|
||||
physics.SetBodyType(bodyUid, BodyType.Dynamic, body: body);
|
||||
physics.SetSleepingAllowed(bodyUid, body, false);
|
||||
physics.SetFixedRotation(bodyUid, false, body: body);
|
||||
|
||||
|
||||
// TODO: Box2D just deref, bleh shape structs someday
|
||||
var shape1 = new PolygonShape();
|
||||
shape1.SetAsBox(0.5f, 10.0f, new Vector2(10.0f, 0.0f), 0.0f);
|
||||
fixtures.CreateFixture(bodyUid, "fix1", new Fixture(shape1, 2, 0, true, 20f));
|
||||
|
||||
var shape2 = new PolygonShape();
|
||||
shape2.SetAsBox(0.5f, 10.0f, new Vector2(-10.0f, 0.0f), 0f);
|
||||
fixtures.CreateFixture(bodyUid, "fix2", new Fixture(shape2, 2, 0, true, 20f));
|
||||
|
||||
var shape3 = new PolygonShape();
|
||||
shape3.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, 10.0f), 0f);
|
||||
fixtures.CreateFixture(bodyUid, "fix3", new Fixture(shape3, 2, 0, true, 20f));
|
||||
|
||||
var shape4 = new PolygonShape();
|
||||
shape4.SetAsBox(10.0f, 0.5f, new Vector2(0.0f, -10.0f), 0f);
|
||||
fixtures.CreateFixture(bodyUid, "fix4", new Fixture(shape4, 2, 0, true, 20f));
|
||||
|
||||
physics.WakeBody(groundUid, body: ground);
|
||||
physics.WakeBody(bodyUid, body: body);
|
||||
var revolute = joints.CreateRevoluteJoint(groundUid, bodyUid);
|
||||
revolute.LocalAnchorA = new Vector2(0f, 10f);
|
||||
revolute.LocalAnchorB = new Vector2(0f, 0f);
|
||||
revolute.ReferenceAngle = 0f;
|
||||
revolute.MotorSpeed = 0.05f * MathF.PI;
|
||||
revolute.MaxMotorTorque = 100000000f;
|
||||
revolute.EnableMotor = true;
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,24 @@
|
||||
<OutputPath>../bin/Benchmarks</OutputPath>
|
||||
<OutputType>Exe</OutputType>
|
||||
<NoWarn>RA0003</NoWarn>
|
||||
<IsTestingPlatformApplication>false</IsTestingPlatformApplication>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robust.Server.Testing\Robust.Server.Testing.csproj" />
|
||||
<ProjectReference Include="..\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\Robust.UnitTesting\Robust.UnitTesting.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" />
|
||||
<!-- BenchmarkDotNet autogenerates files that attempt to reference BenchmarkDotNet through Robust.Benchmarks.
|
||||
By default the RT project privates these files, so we have to explicitly state that these files should be made available
|
||||
to the BenchmarkDotNet project so it can build the runner that executes the benchmark. -->
|
||||
<PackageReference Include="BenchmarkDotNet" PrivateAssets="none" />
|
||||
|
||||
<PackageReference Include="JetBrains.Annotations" />
|
||||
<PackageReference Include="YamlDotNet" />
|
||||
<PackageReference Include="prometheus-net" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[module: SkipLocalsInit]
|
||||
@@ -1,22 +0,0 @@
|
||||
global using Robust.Client.Interop.RobustNative.Webgpu;
|
||||
|
||||
global using static Robust.Client.Interop.RobustNative.Webgpu.Wgpu;
|
||||
global using static Robust.Shared.Utility.FfiHelper;
|
||||
|
||||
global using unsafe WGPUTexture = Robust.Client.Interop.RobustNative.Webgpu.WGPUTextureImpl*;
|
||||
global using unsafe WGPUDevice = Robust.Client.Interop.RobustNative.Webgpu.WGPUDeviceImpl*;
|
||||
global using unsafe WGPUQueue = Robust.Client.Interop.RobustNative.Webgpu.WGPUQueueImpl*;
|
||||
global using unsafe WGPUAdapter = Robust.Client.Interop.RobustNative.Webgpu.WGPUAdapterImpl*;
|
||||
global using unsafe WGPUInstance = Robust.Client.Interop.RobustNative.Webgpu.WGPUInstanceImpl*;
|
||||
global using unsafe WGPUTextureView = Robust.Client.Interop.RobustNative.Webgpu.WGPUTextureViewImpl*;
|
||||
global using unsafe WGPUBindGroup = Robust.Client.Interop.RobustNative.Webgpu.WGPUBindGroupImpl*;
|
||||
global using unsafe WGPUBindGroupLayout = Robust.Client.Interop.RobustNative.Webgpu.WGPUBindGroupLayoutImpl*;
|
||||
global using unsafe WGPUBuffer = Robust.Client.Interop.RobustNative.Webgpu.WGPUBufferImpl*;
|
||||
global using unsafe WGPUSampler = Robust.Client.Interop.RobustNative.Webgpu.WGPUSamplerImpl*;
|
||||
global using unsafe WGPUCommandBuffer = Robust.Client.Interop.RobustNative.Webgpu.WGPUCommandBufferImpl*;
|
||||
global using unsafe WGPUCommandEncoder = Robust.Client.Interop.RobustNative.Webgpu.WGPUCommandEncoderImpl*;
|
||||
global using unsafe WGPURenderPassEncoder = Robust.Client.Interop.RobustNative.Webgpu.WGPURenderPassEncoderImpl*;
|
||||
global using unsafe WGPURenderPipeline = Robust.Client.Interop.RobustNative.Webgpu.WGPURenderPipelineImpl*;
|
||||
global using unsafe WGPUPipelineLayout = Robust.Client.Interop.RobustNative.Webgpu.WGPUPipelineLayoutImpl*;
|
||||
global using unsafe WGPUShaderModule = Robust.Client.Interop.RobustNative.Webgpu.WGPUShaderModuleImpl*;
|
||||
global using unsafe WGPUSurface = Robust.Client.Interop.RobustNative.Webgpu.WGPUSurfaceImpl*;
|
||||
@@ -1,131 +0,0 @@
|
||||
using Robust.Client.Graphics.Rhi.WebGpu;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi;
|
||||
|
||||
public abstract partial class RhiBase
|
||||
{
|
||||
//
|
||||
// Clyde <-> RHI API.
|
||||
//
|
||||
|
||||
internal abstract void Init(in RhiInitParams initParams, out RhiWebGpu.WindowData windowData);
|
||||
internal abstract void Shutdown();
|
||||
|
||||
/// <summary>
|
||||
/// A window was created by Clyde. It should be initialized by the RHI to make it ready for rendering.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Does not get called for the main window.
|
||||
/// </remarks>
|
||||
internal abstract RhiWebGpu.WindowData WindowCreated(in RhiWindowSurfaceParams surfaceParams, Vector2i size, bool vsync);
|
||||
|
||||
/// <summary>
|
||||
/// A window is about to be destroyed by Clyde. Clean up resources for it.
|
||||
/// </summary>
|
||||
internal abstract void WindowDestroy(RhiWebGpu.WindowData reg);
|
||||
|
||||
/// <summary>
|
||||
/// Recreate the native swap chain, in case it has become suboptimal (e.g. due to window resizing).
|
||||
/// </summary>
|
||||
internal abstract void WindowRecreateSwapchain(RhiWebGpu.WindowData reg, Vector2i size, bool vsyncEnabled);
|
||||
|
||||
internal abstract RhiTexture GetSurfaceTextureForWindow(RhiWebGpu.WindowData reg);
|
||||
internal abstract void WindowPresent(RhiWebGpu.WindowData reg);
|
||||
|
||||
//
|
||||
// RHI-internal API to de-OOP the public RHI API.
|
||||
//
|
||||
|
||||
internal abstract RhiRenderPassEncoder CommandEncoderBeginRenderPass(
|
||||
RhiCommandEncoder encoder,
|
||||
in RhiRenderPassDescriptor descriptor
|
||||
);
|
||||
|
||||
internal abstract RhiCommandBuffer CommandEncoderFinish(
|
||||
in RhiCommandEncoder encoder,
|
||||
in RhiCommandBufferDescriptor descriptor);
|
||||
|
||||
internal abstract void RenderPassEncoderSetPipeline(
|
||||
in RhiRenderPassEncoder encoder,
|
||||
RhiRenderPipeline pipeline
|
||||
);
|
||||
|
||||
internal abstract void RenderPassEncoderDraw(
|
||||
in RhiRenderPassEncoder encoder,
|
||||
uint vertexCount,
|
||||
uint instanceCount,
|
||||
uint firstVertex,
|
||||
uint firstInstance
|
||||
);
|
||||
|
||||
internal abstract void RenderPassEncoderEnd(RhiRenderPassEncoder encoder);
|
||||
|
||||
internal abstract void QueueSubmit(RhiQueue queue, RhiCommandBuffer[] commandBuffers);
|
||||
|
||||
internal abstract void QueueWriteTexture(
|
||||
RhiQueue queue,
|
||||
in RhiImageCopyTexture destination,
|
||||
ReadOnlySpan<byte> data,
|
||||
in RhiImageDataLayout dataLayout,
|
||||
RhiExtent3D size
|
||||
);
|
||||
|
||||
public abstract void QueueWriteBuffer(RhiBuffer buffer, ulong bufferOffset, ReadOnlySpan<byte> data);
|
||||
|
||||
internal abstract RhiTextureView TextureCreateView(RhiTexture texture, in RhiTextureViewDescriptor descriptor);
|
||||
internal abstract void TextureViewDrop(RhiTextureView textureView);
|
||||
internal abstract void BindGroupDrop(RhiBindGroup rhiBindGroup);
|
||||
|
||||
internal abstract void RenderPassEncoderSetBindGroup(
|
||||
RhiRenderPassEncoder encoder,
|
||||
uint index,
|
||||
RhiBindGroup? bindGroup
|
||||
);
|
||||
|
||||
internal abstract void RenderPassEncoderSetVertexBuffer(RhiRenderPassEncoder encoder,
|
||||
uint slot,
|
||||
RhiBuffer? buffer,
|
||||
ulong offset,
|
||||
ulong? size);
|
||||
|
||||
internal abstract void RenderPassEncoderSetScissorRect(RhiRenderPassEncoder encoder,
|
||||
uint x,
|
||||
uint y,
|
||||
uint w,
|
||||
uint h);
|
||||
|
||||
internal abstract void CommandBufferDrop(RhiCommandBuffer commandBuffer);
|
||||
|
||||
internal abstract RhiBufferMapState BufferGetMapState(RhiBuffer buffer);
|
||||
internal abstract ValueTask BufferMapAsync(RhiBuffer buffer, RhiMapModeFlags mode, nuint offset, nuint size);
|
||||
internal abstract RhiMappedBufferRange BufferGetMappedRange(RhiBuffer buffer, nuint offset, nuint size);
|
||||
internal abstract void BufferUnmap(RhiBuffer buffer);
|
||||
internal abstract void BufferDrop(RhiBuffer buffer);
|
||||
|
||||
internal struct RhiInitParams
|
||||
{
|
||||
public required string Backends;
|
||||
public required RhiPowerPreference PowerPreference;
|
||||
public required RhiWindowSurfaceParams MainWindowSurfaceParams;
|
||||
}
|
||||
|
||||
internal unsafe struct RhiWindowSurfaceParams
|
||||
{
|
||||
#if WINDOWS
|
||||
public void* HInstance;
|
||||
public void* HWnd;
|
||||
#elif MACOS
|
||||
public void* MetalLayer;
|
||||
#elif LINUX
|
||||
public bool Wayland; // False = X11
|
||||
public void* X11Display;
|
||||
public void* X11Window;
|
||||
|
||||
public void* WaylandDisplay;
|
||||
public void* WaylandSurface;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
internal record struct RhiHandle(long Value);
|
||||
@@ -1,40 +0,0 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi;
|
||||
|
||||
/// <summary>
|
||||
/// Equivalent to a WGSL <c>mat2x3f</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This matrix is columnar and 2 columns, 3 rows. This is equivalent to .NET's <see cref="Matrix3x2"/>!
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct ShaderMat2x3F
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public float M11;
|
||||
[FieldOffset(4)]
|
||||
public float M21;
|
||||
[FieldOffset(8)]
|
||||
public float M31;
|
||||
|
||||
[FieldOffset(16)]
|
||||
public float M12;
|
||||
[FieldOffset(20)]
|
||||
public float M22;
|
||||
[FieldOffset(24)]
|
||||
public float M32;
|
||||
|
||||
public static ShaderMat2x3F FromMatrix(in Matrix3x2 matrix)
|
||||
{
|
||||
var ret = default(ShaderMat2x3F);
|
||||
ret.M11 = matrix.M11;
|
||||
ret.M12 = matrix.M12;
|
||||
ret.M21 = matrix.M21;
|
||||
ret.M22 = matrix.M22;
|
||||
ret.M31 = matrix.M31;
|
||||
ret.M32 = matrix.M32;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Engine.props"/>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<SkipRobustSerializationGenerator>true</SkipRobustSerializationGenerator>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robust.Client.Interop.RobustNative\Robust.Client.Interop.RobustNative.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Utility\Robust.Shared.Utility.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets"/>
|
||||
</Project>
|
||||
@@ -1,113 +0,0 @@
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private readonly Dictionary<RhiHandle, BindGroupLayoutReg> _bindGroupLayoutRegistry = new();
|
||||
private readonly Dictionary<RhiHandle, BindGroupReg> _bindGroupRegistry = new();
|
||||
|
||||
|
||||
internal override void BindGroupDrop(RhiBindGroup rhiBindGroup)
|
||||
{
|
||||
wgpuBindGroupRelease(_bindGroupRegistry[rhiBindGroup.Handle].Native);
|
||||
_bindGroupRegistry.Remove(rhiBindGroup.Handle);
|
||||
}
|
||||
|
||||
public override RhiBindGroupLayout CreateBindGroupLayout(in RhiBindGroupLayoutDescriptor descriptor)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[512];
|
||||
|
||||
var pDescriptor = BumpAllocate<WGPUBindGroupLayoutDescriptor>(ref buffer);
|
||||
pDescriptor->label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
var entries = descriptor.Entries;
|
||||
pDescriptor->entryCount = (uint)entries.Length;
|
||||
pDescriptor->entries = BumpAllocate<WGPUBindGroupLayoutEntry>(ref buffer, entries.Length);
|
||||
for (var i = 0; i < entries.Length; i++)
|
||||
{
|
||||
ref var entry = ref entries[i];
|
||||
var pEntry = &pDescriptor->entries[i];
|
||||
|
||||
pEntry->binding = entry.Binding;
|
||||
pEntry->visibility = (ulong)entry.Visibility;
|
||||
|
||||
switch (entry.Layout)
|
||||
{
|
||||
case RhiSamplerBindingLayout sampler:
|
||||
pEntry->sampler.type = (WGPUSamplerBindingType)sampler.Type;
|
||||
break;
|
||||
case RhiTextureBindingLayout texture:
|
||||
pEntry->texture.multisampled = texture.Multisampled ? 1u : 0u;
|
||||
pEntry->texture.sampleType = (WGPUTextureSampleType)texture.SampleType;
|
||||
pEntry->texture.viewDimension =
|
||||
(WGPUTextureViewDimension)ValidateTextureViewDimension(texture.ViewDimension);
|
||||
break;
|
||||
case RhiBufferBindingLayout layoutBuffer:
|
||||
pEntry->buffer.type = (WGPUBufferBindingType) layoutBuffer.Type;
|
||||
pEntry->buffer.hasDynamicOffset = layoutBuffer.HasDynamicOffset ? 1u : 0u;
|
||||
pEntry->buffer.minBindingSize = layoutBuffer.MinBindingSize;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
var native = wgpuDeviceCreateBindGroupLayout(_wgpuDevice, pDescriptor);
|
||||
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_bindGroupLayoutRegistry.Add(handle, new BindGroupLayoutReg { Native = native });
|
||||
return new RhiBindGroupLayout(this, handle);
|
||||
}
|
||||
|
||||
public override RhiBindGroup CreateBindGroup(in RhiBindGroupDescriptor descriptor)
|
||||
{
|
||||
// TODO: SAFETY
|
||||
Span<byte> buffer = stackalloc byte[1024];
|
||||
|
||||
var pDescriptor = BumpAllocate<WGPUBindGroupDescriptor>(ref buffer);
|
||||
pDescriptor->label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
pDescriptor->layout = _bindGroupLayoutRegistry[descriptor.Layout.Handle].Native;
|
||||
|
||||
var entries = descriptor.Entries;
|
||||
pDescriptor->entryCount = (uint) entries.Length;
|
||||
pDescriptor->entries = BumpAllocate<WGPUBindGroupEntry>(ref buffer, entries.Length);
|
||||
for (var i = 0; i < entries.Length; i++)
|
||||
{
|
||||
ref var entry = ref descriptor.Entries[i];
|
||||
var pEntry = &pDescriptor->entries[i];
|
||||
|
||||
pEntry->binding = entry.Binding;
|
||||
switch (entry.Resource)
|
||||
{
|
||||
case RhiSampler rhiSampler:
|
||||
pEntry->sampler = _samplerRegistry[rhiSampler.Handle].Native;
|
||||
break;
|
||||
case RhiTextureView rhiTextureView:
|
||||
pEntry->textureView = _textureViewRegistry[rhiTextureView.Handle].Native;
|
||||
break;
|
||||
case RhiBufferBinding bufferBinding:
|
||||
pEntry->buffer = _bufferRegistry[bufferBinding.Buffer.Handle].Native;
|
||||
pEntry->offset = bufferBinding.Offset;
|
||||
pEntry->size = bufferBinding.Size ?? WGPU_WHOLE_SIZE;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
var bindGroup = wgpuDeviceCreateBindGroup(_wgpuDevice, pDescriptor);
|
||||
|
||||
var handle = AllocRhiHandle();
|
||||
_bindGroupRegistry.Add(handle, new BindGroupReg { Native = bindGroup });
|
||||
return new RhiBindGroup(this, handle);
|
||||
}
|
||||
|
||||
private sealed class BindGroupLayoutReg
|
||||
{
|
||||
public WGPUBindGroupLayout Native;
|
||||
}
|
||||
|
||||
private sealed class BindGroupReg
|
||||
{
|
||||
public WGPUBindGroup Native;
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed partial class RhiWebGpu
|
||||
{
|
||||
private readonly Dictionary<RhiHandle, BufferReg> _bufferRegistry = new();
|
||||
|
||||
public override unsafe RhiBuffer CreateBuffer(in RhiBufferDescriptor descriptor)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[512];
|
||||
var pDescriptor = BumpAllocate<WGPUBufferDescriptor>(ref buffer);
|
||||
pDescriptor->label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
pDescriptor->mappedAtCreation = descriptor.MappedAtCreation ? 1u : 0u;
|
||||
pDescriptor->size = descriptor.Size;
|
||||
pDescriptor->usage = (ulong) descriptor.Usage;
|
||||
|
||||
var native = wgpuDeviceCreateBuffer(_wgpuDevice, pDescriptor);
|
||||
|
||||
var handle = AllocRhiHandle();
|
||||
_bufferRegistry.Add(handle, new BufferReg { Native = native });
|
||||
var rhiBuffer= new RhiBuffer(this, handle);
|
||||
|
||||
if (pDescriptor->mappedAtCreation == 1)
|
||||
{
|
||||
rhiBuffer.Mapping = new RhiBuffer.ActiveMapping(rhiBuffer) { Valid = true };
|
||||
}
|
||||
|
||||
return rhiBuffer;
|
||||
}
|
||||
|
||||
internal override unsafe RhiBufferMapState BufferGetMapState(RhiBuffer buffer)
|
||||
{
|
||||
var nativeBuffer = _bufferRegistry[buffer.Handle].Native;
|
||||
return (RhiBufferMapState) wgpuBufferGetMapState(nativeBuffer);
|
||||
}
|
||||
|
||||
internal override async ValueTask BufferMapAsync(RhiBuffer buffer, RhiMapModeFlags mode, nuint offset, nuint size)
|
||||
{
|
||||
// TODO: Probably need some more locks here idk.
|
||||
// So people can't map the buffer at the same time as or something.
|
||||
|
||||
buffer.MapState = RhiBufferMapState.Pending;
|
||||
|
||||
WgpuMapBufferAsyncResult result;
|
||||
using (var promise = new WgpuPromise<WgpuMapBufferAsyncResult>())
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var nativeBuffer = _bufferRegistry[buffer.Handle].Native;
|
||||
|
||||
wgpuBufferMapAsync(
|
||||
nativeBuffer,
|
||||
(ulong) mode,
|
||||
offset,
|
||||
size,
|
||||
new WGPUBufferMapCallbackInfo
|
||||
{
|
||||
callback = &WgpuMapBufferAsyncCallback,
|
||||
userdata1 = promise.UserData,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: are we handling the error correctly, here?
|
||||
result = await promise.Task;
|
||||
|
||||
buffer.Mapping = new RhiBuffer.ActiveMapping(buffer) { Valid = true };
|
||||
}
|
||||
|
||||
if (result.Status != WGPUMapAsyncStatus.WGPUMapAsyncStatus_Success)
|
||||
throw new RhiException(result.Status.ToString());
|
||||
|
||||
buffer.MapState = RhiBufferMapState.Mapped;
|
||||
}
|
||||
|
||||
internal override unsafe RhiMappedBufferRange BufferGetMappedRange(RhiBuffer buffer, nuint offset, nuint size)
|
||||
{
|
||||
if (size > int.MaxValue)
|
||||
throw new ArgumentException("Mapped area too big!");
|
||||
|
||||
if (buffer.Mapping == null)
|
||||
throw new InvalidOperationException("Buffer is not mapped");
|
||||
|
||||
lock (buffer.Mapping)
|
||||
{
|
||||
if (!buffer.Mapping.Valid)
|
||||
{
|
||||
// Not sure if this is possible, but can't hurt.
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
var nativeBuffer = _bufferRegistry[buffer.Handle].Native;
|
||||
var mapped = wgpuBufferGetMappedRange(nativeBuffer, offset, size);
|
||||
|
||||
return new RhiMappedBufferRange(buffer.Mapping, mapped, (int) size);
|
||||
}
|
||||
}
|
||||
|
||||
internal override unsafe void BufferUnmap(RhiBuffer buffer)
|
||||
{
|
||||
if (buffer.Mapping == null)
|
||||
throw new InvalidOperationException("Buffer is not mapped!");
|
||||
|
||||
lock (buffer.Mapping)
|
||||
{
|
||||
if (!buffer.Mapping.Valid)
|
||||
{
|
||||
// Not sure if this is possible, but can't hurt.
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
if (buffer.Mapping.ActiveSpans > 0)
|
||||
throw new InvalidOperationException("Current thread has buffer accessible as span, cannot unmap!");
|
||||
|
||||
var nativeBuffer = _bufferRegistry[buffer.Handle].Native;
|
||||
wgpuBufferUnmap(nativeBuffer);
|
||||
|
||||
buffer.Mapping.Valid = false;
|
||||
buffer.Mapping = null;
|
||||
buffer.MapState = RhiBufferMapState.Unmapped;
|
||||
}
|
||||
}
|
||||
|
||||
internal override unsafe void BufferDrop(RhiBuffer buffer)
|
||||
{
|
||||
wgpuBufferRelease(_bufferRegistry[buffer.Handle].Native);
|
||||
_bufferRegistry.Remove(buffer.Handle);
|
||||
}
|
||||
|
||||
private sealed unsafe class BufferReg
|
||||
{
|
||||
public WGPUBuffer Native;
|
||||
}
|
||||
|
||||
private record struct WgpuMapBufferAsyncResult(WGPUMapAsyncStatus Status);
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static unsafe void WgpuMapBufferAsyncCallback(
|
||||
WGPUMapAsyncStatus status,
|
||||
WGPUStringView stringView,
|
||||
void* userdata1,
|
||||
void* userdata2)
|
||||
{
|
||||
WgpuPromise<WgpuMapBufferAsyncResult>.SetResult(userdata1, new WgpuMapBufferAsyncResult(status));
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private readonly Dictionary<RhiHandle, CommandBufferReg> _commandBufferRegistry = new();
|
||||
|
||||
/// <summary>
|
||||
/// Command buffer was dropped natively, either via explicit call or implicit side effect (e.g. queue submit).
|
||||
/// </summary>
|
||||
private void CommandBufferDropped(RhiCommandBuffer commandBuffer)
|
||||
{
|
||||
_commandBufferRegistry.Remove(commandBuffer.Handle);
|
||||
GC.SuppressFinalize(commandBuffer);
|
||||
}
|
||||
|
||||
internal override void CommandBufferDrop(RhiCommandBuffer commandBuffer)
|
||||
{
|
||||
wgpuCommandBufferRelease(_commandBufferRegistry[commandBuffer.Handle].Native);
|
||||
CommandBufferDropped(commandBuffer);
|
||||
}
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private readonly Dictionary<RhiHandle, CommandEncoderReg> _commandEncoderRegistry = new();
|
||||
private readonly Dictionary<RhiHandle, RenderPassEncoderReg> _renderPassEncoderRegistry = new();
|
||||
|
||||
public override RhiCommandEncoder CreateCommandEncoder(in RhiCommandEncoderDescriptor descriptor)
|
||||
{
|
||||
WGPUCommandEncoder nativeEncoder;
|
||||
fixed (byte* pLabel = MakeLabel(descriptor.Label))
|
||||
{
|
||||
var nativeDescriptor = new WGPUCommandEncoderDescriptor
|
||||
{
|
||||
label = new WGPUStringView {data = (sbyte*)pLabel, length = WGPU_STRLEN}
|
||||
};
|
||||
|
||||
nativeEncoder = wgpuDeviceCreateCommandEncoder(_wgpuDevice, &nativeDescriptor);
|
||||
}
|
||||
|
||||
// TODO: thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_commandEncoderRegistry.Add(handle, new CommandEncoderReg { Native = nativeEncoder });
|
||||
return new RhiCommandEncoder(this, handle);
|
||||
}
|
||||
|
||||
internal override RhiRenderPassEncoder CommandEncoderBeginRenderPass(
|
||||
RhiCommandEncoder encoder,
|
||||
in RhiRenderPassDescriptor descriptor)
|
||||
{
|
||||
// TODO: Ensure not disposed
|
||||
// TODO: Thread safety
|
||||
|
||||
Span<byte> buffer = stackalloc byte[512];
|
||||
|
||||
var pDescriptor = BumpAllocate<WGPURenderPassDescriptor>(ref buffer);
|
||||
pDescriptor->label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
|
||||
var colorAttachments = descriptor.ColorAttachments;
|
||||
pDescriptor->colorAttachmentCount = (uint)colorAttachments.Length;
|
||||
pDescriptor->colorAttachments = BumpAllocate<WGPURenderPassColorAttachment>(ref buffer, colorAttachments.Length);
|
||||
for (var i = 0; i < colorAttachments.Length; i++)
|
||||
{
|
||||
ref var attachment = ref colorAttachments[i];
|
||||
var pAttachment = &pDescriptor->colorAttachments[i];
|
||||
pAttachment->view = _textureViewRegistry[attachment.View.Handle].Native;
|
||||
if (attachment.ResolveTarget is { } resolveTarget)
|
||||
pAttachment->resolveTarget = _textureViewRegistry[resolveTarget.Handle].Native;
|
||||
pAttachment->clearValue = WgpuColor(attachment.ClearValue);
|
||||
pAttachment->loadOp = (WGPULoadOp)attachment.LoadOp;
|
||||
pAttachment->storeOp = (WGPUStoreOp)attachment.StoreOp;
|
||||
pAttachment->depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
|
||||
}
|
||||
|
||||
if (descriptor.DepthStencilAttachment is { } depthStencilAttachment)
|
||||
{
|
||||
var pDepthStencilAttachment = BumpAllocate<WGPURenderPassDepthStencilAttachment>(ref buffer);
|
||||
pDescriptor->depthStencilAttachment = pDepthStencilAttachment;
|
||||
|
||||
pDepthStencilAttachment->view = _textureViewRegistry[depthStencilAttachment.View.Handle].Native;
|
||||
pDepthStencilAttachment->depthLoadOp = (WGPULoadOp)depthStencilAttachment.DepthLoadOp;
|
||||
pDepthStencilAttachment->depthStoreOp = (WGPUStoreOp)depthStencilAttachment.DepthStoreOp;
|
||||
pDepthStencilAttachment->depthClearValue = depthStencilAttachment.DepthClearValue;
|
||||
pDepthStencilAttachment->depthReadOnly = depthStencilAttachment.DepthReadOnly ? 1u : 0u;
|
||||
pDepthStencilAttachment->stencilLoadOp = (WGPULoadOp)depthStencilAttachment.StencilLoadOp;
|
||||
pDepthStencilAttachment->stencilStoreOp = (WGPUStoreOp)depthStencilAttachment.StencilStoreOp;
|
||||
pDepthStencilAttachment->stencilClearValue = depthStencilAttachment.StencilClearValue;
|
||||
pDepthStencilAttachment->stencilReadOnly = depthStencilAttachment.StencilReadOnly ? 1u : 0u;
|
||||
}
|
||||
|
||||
if (descriptor.OcclusionQuerySet != null)
|
||||
throw new NotImplementedException();
|
||||
|
||||
var pDescriptorMaxDrawCount = BumpAllocate<WGPURenderPassMaxDrawCount>(ref buffer);
|
||||
pDescriptor->nextInChain = (WGPUChainedStruct*)pDescriptorMaxDrawCount;
|
||||
pDescriptorMaxDrawCount->chain.sType = WGPUSType.WGPUSType_RenderPassMaxDrawCount;
|
||||
pDescriptorMaxDrawCount->maxDrawCount = descriptor.MaxDrawCount;
|
||||
|
||||
var nativeEncoder = wgpuCommandEncoderBeginRenderPass(
|
||||
_commandEncoderRegistry[encoder.Handle].Native,
|
||||
pDescriptor
|
||||
);
|
||||
|
||||
// TODO: thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_renderPassEncoderRegistry.Add(handle, new RenderPassEncoderReg { Native = nativeEncoder });
|
||||
return new RhiRenderPassEncoder(this, handle);
|
||||
}
|
||||
|
||||
internal override void RenderPassEncoderSetPipeline(
|
||||
in RhiRenderPassEncoder encoder,
|
||||
RhiRenderPipeline pipeline)
|
||||
{
|
||||
// TODO: safety
|
||||
wgpuRenderPassEncoderSetPipeline(
|
||||
_renderPassEncoderRegistry[encoder.Handle].Native,
|
||||
_renderPipelineRegistry[pipeline.Handle].Native
|
||||
);
|
||||
}
|
||||
|
||||
internal override void RenderPassEncoderDraw(
|
||||
in RhiRenderPassEncoder encoder,
|
||||
uint vertexCount,
|
||||
uint instanceCount,
|
||||
uint firstVertex,
|
||||
uint firstInstance)
|
||||
{
|
||||
// TODO: safety
|
||||
wgpuRenderPassEncoderDraw(
|
||||
_renderPassEncoderRegistry[encoder.Handle].Native,
|
||||
vertexCount,
|
||||
instanceCount,
|
||||
firstVertex,
|
||||
firstInstance
|
||||
);
|
||||
}
|
||||
|
||||
internal override void RenderPassEncoderEnd(RhiRenderPassEncoder encoder)
|
||||
{
|
||||
// TODO: safety
|
||||
var handle = encoder.Handle;
|
||||
|
||||
wgpuRenderPassEncoderEnd(_renderPassEncoderRegistry[handle].Native);
|
||||
RenderPassEncoderDropped(handle);
|
||||
}
|
||||
|
||||
internal override void RenderPassEncoderSetBindGroup(
|
||||
RhiRenderPassEncoder encoder,
|
||||
uint index,
|
||||
RhiBindGroup? bindGroup)
|
||||
{
|
||||
wgpuRenderPassEncoderSetBindGroup(
|
||||
_renderPassEncoderRegistry[encoder.Handle].Native,
|
||||
index,
|
||||
_bindGroupRegistry[bindGroup!.Handle].Native,
|
||||
0, null
|
||||
);
|
||||
}
|
||||
|
||||
internal override void RenderPassEncoderSetVertexBuffer(
|
||||
RhiRenderPassEncoder encoder,
|
||||
uint slot,
|
||||
RhiBuffer? buffer,
|
||||
ulong offset,
|
||||
ulong? size)
|
||||
{
|
||||
WGPUBuffer nativeBuffer = null;
|
||||
if (buffer != null)
|
||||
nativeBuffer = _bufferRegistry[buffer.Handle].Native;
|
||||
|
||||
wgpuRenderPassEncoderSetVertexBuffer(
|
||||
_renderPassEncoderRegistry[encoder.Handle].Native,
|
||||
slot,
|
||||
nativeBuffer,
|
||||
offset,
|
||||
size ?? WGPU_WHOLE_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
internal override void RenderPassEncoderSetScissorRect(
|
||||
RhiRenderPassEncoder encoder,
|
||||
uint x, uint y, uint w, uint h)
|
||||
{
|
||||
// TODO: safety
|
||||
wgpuRenderPassEncoderSetScissorRect(
|
||||
_renderPassEncoderRegistry[encoder.Handle].Native,
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h
|
||||
);
|
||||
}
|
||||
|
||||
internal override RhiCommandBuffer CommandEncoderFinish(
|
||||
in RhiCommandEncoder encoder,
|
||||
in RhiCommandBufferDescriptor descriptor)
|
||||
{
|
||||
// TODO: safety
|
||||
var handle = encoder.Handle;
|
||||
|
||||
Span<byte> buffer = stackalloc byte[512];
|
||||
var pDescriptor = BumpAllocate<WGPUCommandBufferDescriptor>(ref buffer);
|
||||
pDescriptor->label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
|
||||
var nativeBuffer = wgpuCommandEncoderFinish(
|
||||
_commandEncoderRegistry[handle].Native,
|
||||
pDescriptor
|
||||
);
|
||||
|
||||
CommandEncoderDropped(handle);
|
||||
|
||||
var bufferHandle = AllocRhiHandle();
|
||||
_commandBufferRegistry.Add(bufferHandle, new CommandBufferReg { Native = nativeBuffer });
|
||||
return new RhiCommandBuffer(this, bufferHandle);
|
||||
}
|
||||
|
||||
private void CommandEncoderDropped(RhiHandle encoder)
|
||||
{
|
||||
_commandEncoderRegistry.Remove(encoder);
|
||||
}
|
||||
|
||||
private void RenderPassEncoderDropped(RhiHandle encoder)
|
||||
{
|
||||
_renderPassEncoderRegistry.Remove(encoder);
|
||||
}
|
||||
|
||||
private sealed class CommandEncoderReg
|
||||
{
|
||||
public WGPUCommandEncoder Native;
|
||||
}
|
||||
|
||||
private sealed class RenderPassEncoderReg
|
||||
{
|
||||
public WGPURenderPassEncoder Native;
|
||||
}
|
||||
|
||||
private sealed class CommandBufferReg
|
||||
{
|
||||
public WGPUCommandBuffer Native;
|
||||
}
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private static string WgpuVersionToString(uint version)
|
||||
{
|
||||
var a = (version >> 24) & 0xFF;
|
||||
var b = (version >> 16) & 0xFF;
|
||||
var c = (version >> 08) & 0xFF;
|
||||
var d = (version >> 00) & 0xFF;
|
||||
|
||||
return $"{a}.{b}.{c}.{d}";
|
||||
}
|
||||
|
||||
private static WGPUOptionalBool WgpuOptionalBool(bool? value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
null => WGPUOptionalBool.WGPUOptionalBool_Undefined,
|
||||
false => WGPUOptionalBool.WGPUOptionalBool_False,
|
||||
true => WGPUOptionalBool.WGPUOptionalBool_True,
|
||||
};
|
||||
}
|
||||
|
||||
private static WGPUColor WgpuColor(RhiColor color) => new()
|
||||
{
|
||||
r = color.R,
|
||||
g = color.G,
|
||||
b = color.B,
|
||||
a = color.A
|
||||
};
|
||||
|
||||
private static WGPUExtent3D WgpuExtent3D(RhiExtent3D extent)
|
||||
{
|
||||
return new WGPUExtent3D
|
||||
{
|
||||
width = extent.Width,
|
||||
height = extent.Height,
|
||||
depthOrArrayLayers = extent.Depth
|
||||
};
|
||||
}
|
||||
|
||||
private static WGPUOrigin3D WgpuOrigin3D(RhiOrigin3D origin)
|
||||
{
|
||||
return new WGPUOrigin3D
|
||||
{
|
||||
x = origin.X,
|
||||
y = origin.Y,
|
||||
z = origin.Z
|
||||
};
|
||||
}
|
||||
|
||||
private static string? GetString(WGPUStringView stringView)
|
||||
{
|
||||
if (stringView.data == null)
|
||||
{
|
||||
if (stringView.length == WGPU_STRLEN)
|
||||
return null;
|
||||
if (stringView.length == 0)
|
||||
return "";
|
||||
throw new RhiException("Null address to WGPUStringView");
|
||||
}
|
||||
|
||||
if (stringView.length == WGPU_STRLEN)
|
||||
return Marshal.PtrToStringUTF8((IntPtr)stringView.data);
|
||||
|
||||
if (stringView.length > int.MaxValue)
|
||||
throw new RhiException("WGPUStringView too long!");
|
||||
|
||||
var span = new ReadOnlySpan<byte>(stringView.data, (int)stringView.length);
|
||||
return Encoding.UTF8.GetString(span);
|
||||
}
|
||||
|
||||
private static RhiTextureFormat ToRhiFormat(WGPUTextureFormat format)
|
||||
{
|
||||
return (RhiTextureFormat)format;
|
||||
}
|
||||
|
||||
private static WGPUTextureFormat ValidateTextureFormat(RhiTextureFormat format)
|
||||
{
|
||||
if (format is 0 or >= RhiTextureFormat.Final)
|
||||
throw new ArgumentException($"Invalid {nameof(RhiTextureFormat)}");
|
||||
|
||||
return (WGPUTextureFormat)format;
|
||||
}
|
||||
|
||||
private static WGPUTextureDimension ValidateTextureDimension(RhiTextureDimension dimension)
|
||||
{
|
||||
if (dimension > RhiTextureDimension.Dim3D)
|
||||
throw new ArgumentException($"Invalid {nameof(RhiTextureDimension)}");
|
||||
|
||||
return dimension switch
|
||||
{
|
||||
RhiTextureDimension.Dim1D => WGPUTextureDimension.WGPUTextureDimension_1D,
|
||||
RhiTextureDimension.Dim2D => WGPUTextureDimension.WGPUTextureDimension_2D,
|
||||
RhiTextureDimension.Dim3D => WGPUTextureDimension.WGPUTextureDimension_3D,
|
||||
_ => throw new UnreachableException()
|
||||
};
|
||||
}
|
||||
|
||||
private static RhiTextureUsage ValidateTextureUsage(RhiTextureUsage usage)
|
||||
{
|
||||
if (usage >= RhiTextureUsage.Final)
|
||||
throw new ArgumentException($"Invalid {nameof(RhiTextureUsage)}");
|
||||
|
||||
return usage;
|
||||
}
|
||||
|
||||
private static RhiTextureViewDimension ValidateTextureViewDimension(RhiTextureViewDimension dimension)
|
||||
{
|
||||
if (dimension >= RhiTextureViewDimension.Final)
|
||||
throw new ArgumentException($"Invalid {nameof(RhiTextureViewDimension)}");
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
private static RhiTextureAspect ValidateTextureAspect(RhiTextureAspect aspect)
|
||||
{
|
||||
if (aspect >= RhiTextureAspect.Final)
|
||||
throw new ArgumentException($"Invalid {nameof(RhiTextureAspect)}");
|
||||
|
||||
return aspect;
|
||||
}
|
||||
|
||||
private static RhiAddressMode ValidateAddressMode(RhiAddressMode addressMode)
|
||||
{
|
||||
if (addressMode >= RhiAddressMode.Final)
|
||||
throw new ArgumentException($"Invalid {nameof(RhiAddressMode)}");
|
||||
|
||||
return addressMode;
|
||||
}
|
||||
|
||||
private static RhiFilterMode ValidateFilterMode(RhiFilterMode filterMode)
|
||||
{
|
||||
if (filterMode >= RhiFilterMode.Final)
|
||||
throw new ArgumentException($"Invalid {nameof(RhiFilterMode)}");
|
||||
|
||||
return filterMode;
|
||||
}
|
||||
|
||||
private static RhiMipmapFilterMode ValidateMipmapFilterMode(RhiMipmapFilterMode mipmapFilterMode)
|
||||
{
|
||||
if (mipmapFilterMode >= RhiMipmapFilterMode.Final)
|
||||
throw new ArgumentException($"Invalid {nameof(RhiMipmapFilterMode)}");
|
||||
|
||||
return mipmapFilterMode;
|
||||
}
|
||||
|
||||
private static RhiCompareFunction ValidateCompareFunction(RhiCompareFunction compareFunction)
|
||||
{
|
||||
if (compareFunction >= RhiCompareFunction.Final)
|
||||
throw new ArgumentException($"Invalid {nameof(RhiCompareFunction)}");
|
||||
|
||||
return compareFunction;
|
||||
}
|
||||
|
||||
private static WGPUPowerPreference ValidatePowerPreference(RhiPowerPreference powerPreference)
|
||||
{
|
||||
if (powerPreference >= RhiPowerPreference.Final)
|
||||
throw new ArgumentException($"Invalid {nameof(RhiPowerPreference)}");
|
||||
|
||||
return (WGPUPowerPreference) powerPreference;
|
||||
}
|
||||
|
||||
private static string MarshalFromString(byte* str)
|
||||
{
|
||||
return Marshal.PtrToStringUTF8((nint)str)!;
|
||||
}
|
||||
|
||||
[return: NotNullIfNotNull(nameof(label))]
|
||||
private static byte[]? MakeLabel(string? label)
|
||||
{
|
||||
// TODO: Replace with stackalloc
|
||||
|
||||
if (label == null)
|
||||
return null;
|
||||
|
||||
return Encoding.UTF8.GetBytes(label);
|
||||
}
|
||||
|
||||
private static WGPUStringView BumpAllocateStringView(ref Span<byte> buf, string? str)
|
||||
{
|
||||
if (str == null)
|
||||
return WGPUStringView.Null;
|
||||
|
||||
var byteCount = Encoding.UTF8.GetByteCount(str) ;
|
||||
var ptr = BumpAllocate(ref buf, byteCount);
|
||||
var dstSpan = new Span<byte>(ptr, byteCount);
|
||||
Encoding.UTF8.GetBytes(str, dstSpan);
|
||||
|
||||
return new WGPUStringView
|
||||
{
|
||||
data = (sbyte*)ptr,
|
||||
length = (nuint)byteCount
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private sealed class WgpuPromise<TResult> : IDisposable
|
||||
{
|
||||
private readonly TaskCompletionSource<TResult> _tcs;
|
||||
private GCHandle _gcHandle;
|
||||
public Task<TResult> Task => _tcs.Task;
|
||||
public void* UserData => (void*) GCHandle.ToIntPtr(_gcHandle);
|
||||
|
||||
public WgpuPromise()
|
||||
{
|
||||
_tcs = new TaskCompletionSource<TResult>();
|
||||
_gcHandle = GCHandle.Alloc(this);
|
||||
}
|
||||
|
||||
public static void SetResult(void* userdata, TResult result)
|
||||
{
|
||||
var self = (WgpuPromise<TResult>)GCHandle.FromIntPtr((nint) userdata).Target!;
|
||||
self._tcs.SetResult(result);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_gcHandle.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
public override RhiQueue Queue { get; }
|
||||
|
||||
// queue is ignored as parameter, since WebGPU only supports one queue for now.
|
||||
|
||||
internal override void QueueWriteTexture(
|
||||
RhiQueue queue,
|
||||
in RhiImageCopyTexture destination,
|
||||
ReadOnlySpan<byte> data,
|
||||
in RhiImageDataLayout dataLayout,
|
||||
RhiExtent3D size)
|
||||
{
|
||||
// TODO: Thread safety
|
||||
var nativeTexture = _textureRegistry[destination.Texture.Handle].Native;
|
||||
|
||||
var nativeDestination = new WGPUTexelCopyTextureInfo
|
||||
{
|
||||
aspect = (WGPUTextureAspect)ValidateTextureAspect(destination.Aspect),
|
||||
texture = nativeTexture,
|
||||
origin = WgpuOrigin3D(destination.Origin),
|
||||
mipLevel = destination.MipLevel
|
||||
};
|
||||
|
||||
var nativeDataLayout = new WGPUTexelCopyBufferLayout
|
||||
{
|
||||
// TODO: Validation
|
||||
offset = dataLayout.Offset,
|
||||
bytesPerRow = dataLayout.BytesPerRow,
|
||||
rowsPerImage = dataLayout.RowsPerImage
|
||||
};
|
||||
|
||||
var nativeSize = WgpuExtent3D(size);
|
||||
|
||||
fixed (byte* pData = data)
|
||||
{
|
||||
wgpuQueueWriteTexture(
|
||||
_wgpuQueue,
|
||||
&nativeDestination,
|
||||
pData, (nuint) data.Length,
|
||||
&nativeDataLayout,
|
||||
&nativeSize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override void QueueWriteBuffer(RhiBuffer buffer, ulong bufferOffset, ReadOnlySpan<byte> data)
|
||||
{
|
||||
var nativeBuffer = _bufferRegistry[buffer.Handle].Native;
|
||||
|
||||
fixed (byte* pData = data)
|
||||
{
|
||||
wgpuQueueWriteBuffer(
|
||||
_wgpuQueue,
|
||||
nativeBuffer,
|
||||
bufferOffset,
|
||||
pData,
|
||||
(nuint) data.Length);
|
||||
}
|
||||
}
|
||||
|
||||
internal override void QueueSubmit(RhiQueue queue, RhiCommandBuffer[] commandBuffers)
|
||||
{
|
||||
// TODO: Safety
|
||||
|
||||
var pBuffers = stackalloc WGPUCommandBuffer[commandBuffers.Length];
|
||||
for (var i = 0; i < commandBuffers.Length; i++)
|
||||
{
|
||||
pBuffers[i] = _commandBufferRegistry[commandBuffers[i].Handle].Native;
|
||||
}
|
||||
|
||||
wgpuQueueSubmit(
|
||||
_wgpuQueue,
|
||||
(uint) commandBuffers.Length,
|
||||
pBuffers
|
||||
);
|
||||
|
||||
foreach (var commandBuffer in commandBuffers)
|
||||
{
|
||||
CommandBufferDropped(commandBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed partial class RhiWebGpu
|
||||
{
|
||||
private long _rhiHandleCounter;
|
||||
|
||||
private RhiHandle AllocRhiHandle() => new(Interlocked.Increment(ref _rhiHandleCounter));
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private readonly Dictionary<RhiHandle, RenderPipelineReg> _renderPipelineRegistry = new();
|
||||
private readonly Dictionary<RhiHandle, PipelineLayoutReg> _pipelineLayoutRegistry = new();
|
||||
|
||||
public override RhiPipelineLayout CreatePipelineLayout(in RhiPipelineLayoutDescriptor descriptor)
|
||||
{
|
||||
// TODO: SAFETY
|
||||
|
||||
Span<byte> buffer = stackalloc byte[128];
|
||||
var pDescriptor = BumpAllocate<WGPUPipelineLayoutDescriptor>(ref buffer);
|
||||
pDescriptor->label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
|
||||
var layouts = descriptor.BindGroupLayouts;
|
||||
pDescriptor->bindGroupLayoutCount = (uint) layouts.Length;
|
||||
pDescriptor->bindGroupLayouts = BumpAllocatePtr<WGPUBindGroupLayoutImpl>(ref buffer, layouts.Length);
|
||||
for (var i = 0; i < layouts.Length; i++)
|
||||
{
|
||||
pDescriptor->bindGroupLayouts[i] = _bindGroupLayoutRegistry[layouts[i].Handle].Native;
|
||||
}
|
||||
|
||||
var native = wgpuDeviceCreatePipelineLayout(_wgpuDevice, pDescriptor);
|
||||
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_pipelineLayoutRegistry.Add(handle, new PipelineLayoutReg { Native = native });
|
||||
return new RhiPipelineLayout(this, handle);
|
||||
}
|
||||
|
||||
public override RhiRenderPipeline CreateRenderPipeline(in RhiRenderPipelineDescriptor descriptor)
|
||||
{
|
||||
// TODO: THREAD SAFETY
|
||||
// TODO: INPUT VALIDATION
|
||||
|
||||
var vertexShader = _shaderModuleRegistry[descriptor.Vertex.ProgrammableStage.ShaderModule.Handle].Native;
|
||||
|
||||
const int bufferSize = 8192;
|
||||
var bufferPtr = NativeMemory.AlignedAlloc(bufferSize, 8);
|
||||
|
||||
WGPURenderPipeline nativePipeline;
|
||||
try
|
||||
{
|
||||
var buffer = new Span<byte>(bufferPtr, bufferSize);
|
||||
|
||||
WGPURenderPipelineDescriptor pipelineDesc = default;
|
||||
pipelineDesc.label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
|
||||
// Pipeline layout
|
||||
switch (descriptor.Layout)
|
||||
{
|
||||
case RhiPipelineLayout pipelineLayout:
|
||||
pipelineDesc.layout = _pipelineLayoutRegistry[pipelineLayout.Handle].Native;
|
||||
break;
|
||||
|
||||
case RhiAutoLayoutMode:
|
||||
throw new NotSupportedException("wgpu does not support auto layout yet");
|
||||
// Default case: no layout given, do nothing
|
||||
}
|
||||
|
||||
// Vertex state
|
||||
pipelineDesc.vertex.module = vertexShader;
|
||||
pipelineDesc.vertex.entryPoint = BumpAllocateStringView(
|
||||
ref buffer,
|
||||
descriptor.Vertex.ProgrammableStage.EntryPoint);
|
||||
|
||||
WgpuProgrammableConstants(
|
||||
ref buffer,
|
||||
descriptor.Vertex.ProgrammableStage.Constants,
|
||||
out pipelineDesc.vertex.constantCount,
|
||||
out pipelineDesc.vertex.constants);
|
||||
|
||||
var buffers = descriptor.Vertex.Buffers;
|
||||
pipelineDesc.vertex.bufferCount = (uint)buffers.Length;
|
||||
pipelineDesc.vertex.buffers = BumpAllocate<WGPUVertexBufferLayout>(ref buffer, buffers.Length);
|
||||
for (var i = 0; i < buffers.Length; i++)
|
||||
{
|
||||
ref var bufferLayout = ref pipelineDesc.vertex.buffers[i];
|
||||
bufferLayout.arrayStride = buffers[i].ArrayStride;
|
||||
bufferLayout.stepMode = buffers[i].StepMode == RhiVertexStepMode.Instance
|
||||
? WGPUVertexStepMode.WGPUVertexStepMode_Instance
|
||||
: WGPUVertexStepMode.WGPUVertexStepMode_Vertex;
|
||||
|
||||
var attributes = buffers[i].Attributes;
|
||||
bufferLayout.attributeCount = (uint)attributes.Length;
|
||||
bufferLayout.attributes = BumpAllocate<WGPUVertexAttribute>(ref buffer, attributes.Length);
|
||||
for (var j = 0; j < attributes.Length; j++)
|
||||
{
|
||||
ref var attribute = ref bufferLayout.attributes[j];
|
||||
attribute.format = (WGPUVertexFormat)attributes[j].Format;
|
||||
attribute.offset = attributes[j].Offset;
|
||||
attribute.shaderLocation = attributes[j].ShaderLocation;
|
||||
}
|
||||
}
|
||||
|
||||
// Primitive state
|
||||
pipelineDesc.primitive.topology = (WGPUPrimitiveTopology)descriptor.Primitive.Topology;
|
||||
pipelineDesc.primitive.stripIndexFormat = (WGPUIndexFormat)descriptor.Primitive.StripIndexformat;
|
||||
pipelineDesc.primitive.frontFace = (WGPUFrontFace)descriptor.Primitive.FrontFace;
|
||||
pipelineDesc.primitive.cullMode = (WGPUCullMode)descriptor.Primitive.CullMode;
|
||||
pipelineDesc.primitive.unclippedDepth = descriptor.Primitive.UnclippedDepth ? 1u : 0u;
|
||||
|
||||
// Depth stencil state
|
||||
if (descriptor.DepthStencil is { } depthStencil)
|
||||
{
|
||||
var pDepthStencil = BumpAllocate<WGPUDepthStencilState>(ref buffer);
|
||||
pipelineDesc.depthStencil = pDepthStencil;
|
||||
|
||||
pDepthStencil->format = (WGPUTextureFormat)depthStencil.Format;
|
||||
pDepthStencil->depthWriteEnabled = WgpuOptionalBool(depthStencil.DepthWriteEnabled);
|
||||
pDepthStencil->depthCompare = (WGPUCompareFunction)depthStencil.DepthCompare;
|
||||
pDepthStencil->stencilFront = WgpuStencilFaceState(depthStencil.StencilFront ?? new RhiStencilFaceState());
|
||||
pDepthStencil->stencilBack = WgpuStencilFaceState(depthStencil.StencilBack ?? new RhiStencilFaceState());
|
||||
pDepthStencil->stencilReadMask = depthStencil.StencilReadMask;
|
||||
pDepthStencil->stencilWriteMask = depthStencil.StencilWriteMask;
|
||||
pDepthStencil->depthBias = depthStencil.DepthBias;
|
||||
pDepthStencil->depthBiasSlopeScale = depthStencil.DepthBiasSlopeScale;
|
||||
pDepthStencil->depthBiasClamp = depthStencil.DepthBiasClamp;
|
||||
}
|
||||
|
||||
// Multisample state
|
||||
pipelineDesc.multisample.count = descriptor.Multisample.Count;
|
||||
pipelineDesc.multisample.mask = descriptor.Multisample.Mask;
|
||||
pipelineDesc.multisample.alphaToCoverageEnabled = descriptor.Multisample.AlphaToCoverageEnabled ? 1u : 0u;
|
||||
|
||||
// Fragment state
|
||||
if (descriptor.Fragment is { } fragment)
|
||||
{
|
||||
var fragmentShader = _shaderModuleRegistry[fragment.ProgrammableStage.ShaderModule.Handle].Native;
|
||||
|
||||
var pFragment = BumpAllocate<WGPUFragmentState>(ref buffer);
|
||||
pipelineDesc.fragment = pFragment;
|
||||
|
||||
pFragment->module = fragmentShader;
|
||||
pFragment->entryPoint = BumpAllocateStringView(ref buffer, fragment.ProgrammableStage.EntryPoint);
|
||||
|
||||
WgpuProgrammableConstants(
|
||||
ref buffer,
|
||||
fragment.ProgrammableStage.Constants,
|
||||
out pFragment->constantCount,
|
||||
out pFragment->constants);
|
||||
|
||||
var targets = fragment.Targets;
|
||||
pFragment->targetCount = (uint)targets.Length;
|
||||
pFragment->targets = BumpAllocate<WGPUColorTargetState>(ref buffer, targets.Length);
|
||||
for (var i = 0; i < targets.Length; i++)
|
||||
{
|
||||
ref var target = ref pFragment->targets[i];
|
||||
target.format = (WGPUTextureFormat)targets[i].Format;
|
||||
|
||||
if (targets[i].Blend is { } blend)
|
||||
{
|
||||
var pBlend = BumpAllocate<WGPUBlendState>(ref buffer);
|
||||
target.blend = pBlend;
|
||||
|
||||
pBlend->alpha = WgpuBlendComponent(blend.Alpha);
|
||||
pBlend->color = WgpuBlendComponent(blend.Color);
|
||||
}
|
||||
|
||||
target.writeMask = (ulong)targets[i].WriteMask;
|
||||
}
|
||||
}
|
||||
|
||||
nativePipeline = wgpuDeviceCreateRenderPipeline(_wgpuDevice, &pipelineDesc);
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMemory.AlignedFree(bufferPtr);
|
||||
}
|
||||
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_renderPipelineRegistry.Add(handle, new RenderPipelineReg { Native = nativePipeline });
|
||||
return new RhiRenderPipeline(this, handle);
|
||||
}
|
||||
|
||||
private static WGPUStencilFaceState WgpuStencilFaceState(in RhiStencilFaceState state)
|
||||
{
|
||||
return new WGPUStencilFaceState
|
||||
{
|
||||
compare = (WGPUCompareFunction)state.Compare,
|
||||
failOp = (WGPUStencilOperation)state.FailOp,
|
||||
depthFailOp = (WGPUStencilOperation)state.DepthFailOp,
|
||||
passOp = (WGPUStencilOperation)state.PassOp
|
||||
};
|
||||
}
|
||||
|
||||
private static void WgpuProgrammableConstants(
|
||||
ref Span<byte> buffer,
|
||||
RhiConstantEntry[] constants,
|
||||
out nuint constantCount,
|
||||
out WGPUConstantEntry* pConstants)
|
||||
{
|
||||
constantCount = (uint)constants.Length;
|
||||
pConstants = BumpAllocate<WGPUConstantEntry>(ref buffer, constants.Length);
|
||||
for (var i = 0; i < constants.Length; i++)
|
||||
{
|
||||
ref var constant = ref pConstants[i];
|
||||
constant.key = BumpAllocateStringView(ref buffer, constants[i].Key);
|
||||
constant.value = constants[i].Value;
|
||||
}
|
||||
}
|
||||
|
||||
private static WGPUBlendComponent WgpuBlendComponent(in RhiBlendComponent component)
|
||||
{
|
||||
return new WGPUBlendComponent
|
||||
{
|
||||
operation = (WGPUBlendOperation)component.Operation,
|
||||
dstFactor = (WGPUBlendFactor)component.DstFactor,
|
||||
srcFactor = (WGPUBlendFactor)component.SrcFactor,
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class RenderPipelineReg
|
||||
{
|
||||
public WGPURenderPipeline Native;
|
||||
}
|
||||
|
||||
private sealed class PipelineLayoutReg
|
||||
{
|
||||
public WGPUPipelineLayout Native;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private readonly Dictionary<RhiHandle, SamplerReg> _samplerRegistry = new();
|
||||
|
||||
public override RhiSampler CreateSampler(in RhiSamplerDescriptor descriptor)
|
||||
{
|
||||
var addressModeU = ValidateAddressMode(descriptor.AddressModeU);
|
||||
var addressModeV = ValidateAddressMode(descriptor.AddressModeV);
|
||||
var addressModeW = ValidateAddressMode(descriptor.AddressModeW);
|
||||
var magFilter = ValidateFilterMode(descriptor.MagFilter);
|
||||
var minFilter = ValidateFilterMode(descriptor.MinFilter);
|
||||
var mipmapFilter = ValidateMipmapFilterMode(descriptor.MipmapFilter);
|
||||
var compare = ValidateCompareFunction(descriptor.Compare);
|
||||
|
||||
WGPUSampler sampler;
|
||||
fixed (byte* label = MakeLabel(descriptor.Label))
|
||||
{
|
||||
var samplerDesc = new WGPUSamplerDescriptor
|
||||
{
|
||||
addressModeU = (WGPUAddressMode) addressModeU,
|
||||
addressModeV = (WGPUAddressMode) addressModeV,
|
||||
addressModeW = (WGPUAddressMode) addressModeW,
|
||||
magFilter = (WGPUFilterMode) magFilter,
|
||||
minFilter = (WGPUFilterMode) minFilter,
|
||||
mipmapFilter = (WGPUMipmapFilterMode) mipmapFilter,
|
||||
lodMinClamp = descriptor.LodMinClamp,
|
||||
lodMaxClamp = descriptor.LodMaxClamp,
|
||||
compare = (WGPUCompareFunction) compare,
|
||||
maxAnisotropy = descriptor.MaxAnisotropy,
|
||||
label = new WGPUStringView
|
||||
{
|
||||
data = (sbyte*)label,
|
||||
length = WGPU_STRLEN
|
||||
}
|
||||
};
|
||||
|
||||
sampler = wgpuDeviceCreateSampler(_wgpuDevice, &samplerDesc);
|
||||
}
|
||||
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_samplerRegistry.Add(handle, new SamplerReg { Native = sampler });
|
||||
return new RhiSampler(this, handle);
|
||||
}
|
||||
|
||||
private sealed class SamplerReg
|
||||
{
|
||||
public WGPUSampler Native;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private readonly Dictionary<RhiHandle, ShaderModuleReg> _shaderModuleRegistry = new();
|
||||
|
||||
public override RhiShaderModule CreateShaderModule(in RhiShaderModuleDescriptor descriptor)
|
||||
{
|
||||
var codeBytes = Encoding.UTF8.GetBytes(descriptor.Code);
|
||||
|
||||
return CreateShaderModule(new RhiShaderModuleDescriptorUtf8
|
||||
{
|
||||
Code = codeBytes,
|
||||
Label = descriptor.Label
|
||||
});
|
||||
}
|
||||
|
||||
public override RhiShaderModule CreateShaderModule(in RhiShaderModuleDescriptorUtf8 descriptor)
|
||||
{
|
||||
WGPUShaderModule shaderModule;
|
||||
fixed (byte* pCode = descriptor.Code)
|
||||
fixed (byte* pLabel = MakeLabel(descriptor.Label))
|
||||
{
|
||||
var descWgsl = new WGPUShaderSourceWGSL();
|
||||
descWgsl.code = new WGPUStringView
|
||||
{
|
||||
data = (sbyte*)pCode,
|
||||
length = WGPU_STRLEN
|
||||
};
|
||||
descWgsl.chain.sType = WGPUSType.WGPUSType_ShaderSourceWGSL;
|
||||
|
||||
var desc = new WGPUShaderModuleDescriptor();
|
||||
desc.label = new WGPUStringView
|
||||
{
|
||||
data = (sbyte*)pLabel,
|
||||
length = WGPU_STRLEN
|
||||
};
|
||||
desc.nextInChain = (WGPUChainedStruct*) (&descWgsl);
|
||||
|
||||
shaderModule = wgpuDeviceCreateShaderModule(_wgpuDevice, &desc);
|
||||
}
|
||||
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_shaderModuleRegistry.Add(handle, new ShaderModuleReg { Native = shaderModule });
|
||||
return new RhiShaderModule(this, handle);
|
||||
}
|
||||
|
||||
private sealed class ShaderModuleReg
|
||||
{
|
||||
public WGPUShaderModule Native;
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private readonly Dictionary<RhiHandle, TextureReg> _textureRegistry = new();
|
||||
private readonly Dictionary<RhiHandle, TextureViewReg> _textureViewRegistry = new();
|
||||
|
||||
public override RhiTexture CreateTexture(in RhiTextureDescriptor descriptor)
|
||||
{
|
||||
var format = descriptor.Format;
|
||||
var usage = descriptor.Usage;
|
||||
ValidateTextureFormat(format);
|
||||
var dimension = ValidateTextureDimension(descriptor.Dimension);
|
||||
ValidateTextureUsage(usage);
|
||||
|
||||
// TODO: Copy to stackalloc instead.
|
||||
var viewFormats = descriptor.ViewFormats?.ToArray() ?? [];
|
||||
foreach (var vf in viewFormats)
|
||||
{
|
||||
ValidateTextureFormat(vf);
|
||||
}
|
||||
|
||||
Debug.Assert(
|
||||
sizeof(RhiTextureFormat) == sizeof(WGPUTextureFormat),
|
||||
"Pointer to view formats array is cast directly to pass to native, sizes must match");
|
||||
|
||||
WGPUTexture texturePtr;
|
||||
var label = MakeLabel(descriptor.Label);
|
||||
fixed (byte* pLabel = label)
|
||||
fixed (RhiTextureFormat* pViewFormats = viewFormats)
|
||||
{
|
||||
var webGpuDesc = new WGPUTextureDescriptor
|
||||
{
|
||||
sampleCount = descriptor.SampleCount,
|
||||
mipLevelCount = descriptor.MipLevelCount,
|
||||
dimension = dimension,
|
||||
format = (WGPUTextureFormat) format,
|
||||
label = new WGPUStringView
|
||||
{
|
||||
data = (sbyte*)pLabel,
|
||||
length = (UIntPtr)(label?.Length ?? 0),
|
||||
},
|
||||
size = WgpuExtent3D(descriptor.Size),
|
||||
usage = (ulong) usage,
|
||||
viewFormats = (WGPUTextureFormat*) pViewFormats,
|
||||
viewFormatCount = checked((uint) viewFormats.Length),
|
||||
};
|
||||
|
||||
texturePtr = wgpuDeviceCreateTexture(_wgpuDevice, &webGpuDesc);
|
||||
}
|
||||
|
||||
if (texturePtr == null)
|
||||
throw new RhiException("Texture creation failed");
|
||||
|
||||
return AllocRhiTexture(texturePtr);
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_textureRegistry.Add(handle, new TextureReg { Native = texturePtr });
|
||||
return new RhiTexture(this, handle);
|
||||
}
|
||||
|
||||
internal override RhiTextureView TextureCreateView(RhiTexture texture, in RhiTextureViewDescriptor descriptor)
|
||||
{
|
||||
// TODO: Thread safety
|
||||
var nativeTexture = _textureRegistry[texture.Handle].Native;
|
||||
|
||||
var format = ValidateTextureFormat(descriptor.Format);
|
||||
var dimension = ValidateTextureViewDimension(descriptor.Dimension);
|
||||
var aspect = ValidateTextureAspect(descriptor.Aspect);
|
||||
|
||||
var mipLevelCount = descriptor.MipLevelCount;
|
||||
var arrayLayerCount = descriptor.ArrayLayerCount;
|
||||
|
||||
if (mipLevelCount == 0)
|
||||
throw new ArgumentException($"Invalid {nameof(descriptor.MipLevelCount)}");
|
||||
|
||||
if (arrayLayerCount == 0)
|
||||
throw new ArgumentException($"Invalid {nameof(descriptor.ArrayLayerCount)}");
|
||||
|
||||
WGPUTextureView textureView;
|
||||
fixed (byte* label = MakeLabel(descriptor.Label))
|
||||
{
|
||||
var webGpuDesc = new WGPUTextureViewDescriptor
|
||||
{
|
||||
format = (WGPUTextureFormat) format,
|
||||
dimension = (WGPUTextureViewDimension) dimension,
|
||||
aspect = (WGPUTextureAspect) aspect,
|
||||
label = new WGPUStringView
|
||||
{
|
||||
data = (sbyte*)label,
|
||||
length = WGPU_STRLEN
|
||||
},
|
||||
baseMipLevel = descriptor.BaseMipLevel,
|
||||
mipLevelCount = mipLevelCount,
|
||||
baseArrayLayer = descriptor.BaseArrayLayer,
|
||||
arrayLayerCount = descriptor.ArrayLayerCount
|
||||
};
|
||||
|
||||
textureView = wgpuTextureCreateView(nativeTexture, &webGpuDesc);
|
||||
}
|
||||
|
||||
return AllocRhiTextureView(textureView);
|
||||
}
|
||||
|
||||
internal override void TextureViewDrop(RhiTextureView textureView)
|
||||
{
|
||||
wgpuTextureViewRelease(_textureViewRegistry[textureView.Handle].Native);
|
||||
|
||||
_textureViewRegistry.Remove(textureView.Handle);
|
||||
}
|
||||
|
||||
internal override RhiTexture GetSurfaceTextureForWindow(WindowData reg)
|
||||
{
|
||||
// TODO: Thread safety
|
||||
|
||||
var surface = reg.Surface;
|
||||
|
||||
// This creates a new texture view handle.
|
||||
WGPUSurfaceTexture textureRet;
|
||||
wgpuSurfaceGetCurrentTexture(surface, &textureRet);
|
||||
|
||||
return AllocRhiTexture(textureRet.texture);
|
||||
}
|
||||
|
||||
private RhiTexture AllocRhiTexture(WGPUTexture native)
|
||||
{
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_textureRegistry.Add(handle, new TextureReg { Native = native });
|
||||
return new RhiTexture(this, handle);
|
||||
}
|
||||
|
||||
private RhiTextureView AllocRhiTextureView(WGPUTextureView native)
|
||||
{
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_textureViewRegistry.Add(handle, new TextureViewReg { Native = native });
|
||||
return new RhiTextureView(this, handle);
|
||||
}
|
||||
|
||||
private sealed class TextureReg
|
||||
{
|
||||
public WGPUTexture Native;
|
||||
}
|
||||
|
||||
private sealed class TextureViewReg
|
||||
{
|
||||
public WGPUTextureView Native;
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private RhiTextureFormat _mainTextureFormat;
|
||||
private WGPUPresentMode[] _availPresentModes = [];
|
||||
|
||||
public override RhiTextureFormat MainTextureFormat => _mainTextureFormat;
|
||||
|
||||
public sealed class WindowData
|
||||
{
|
||||
public WGPUSurface Surface;
|
||||
}
|
||||
|
||||
private WindowData CreateSurfaceForWindow(in RhiWindowSurfaceParams surfaceParams)
|
||||
{
|
||||
WGPUSurfaceDescriptor surfaceDesc = default;
|
||||
|
||||
#if WINDOWS
|
||||
var surfaceDescHwnd = new WGPUSurfaceSourceWindowsHWND
|
||||
{
|
||||
chain =
|
||||
{
|
||||
sType = WGPUSType.WGPUSType_SurfaceSourceWindowsHWND
|
||||
},
|
||||
hinstance = surfaceParams.HInstance,
|
||||
hwnd = surfaceParams.HWnd,
|
||||
};
|
||||
|
||||
surfaceDesc.nextInChain = (WGPUChainedStruct*)(&surfaceDescHwnd);
|
||||
|
||||
#elif MACOS
|
||||
var surfaceDescMetal = new WGPUSurfaceSourceMetalLayer
|
||||
{
|
||||
chain =
|
||||
{
|
||||
sType = WGPUSType.WGPUSType_SurfaceSourceMetalLayer
|
||||
},
|
||||
layer = surfaceParams.MetalLayer
|
||||
};
|
||||
|
||||
surfaceDesc.nextInChain = (WGPUChainedStruct*)(&surfaceDescMetal);
|
||||
|
||||
#elif LINUX
|
||||
WGPUSurfaceSourceWaylandSurface surfaceDescWayland;
|
||||
WGPUSurfaceSourceXlibWindow surfaceDescX11;
|
||||
|
||||
if (surfaceParams.Wayland)
|
||||
{
|
||||
surfaceDescWayland = new WGPUSurfaceSourceWaylandSurface
|
||||
{
|
||||
chain =
|
||||
{
|
||||
sType = WGPUSType.WGPUSType_SurfaceSourceWaylandSurface
|
||||
},
|
||||
display = surfaceParams.WaylandDisplay,
|
||||
surface = surfaceParams.WaylandSurface,
|
||||
};
|
||||
|
||||
surfaceDesc.nextInChain = (WGPUChainedStruct*)(&surfaceDescWayland);
|
||||
}
|
||||
else
|
||||
{
|
||||
surfaceDescX11 = new WGPUSurfaceSourceXlibWindow()
|
||||
{
|
||||
chain =
|
||||
{
|
||||
sType = WGPUSType.WGPUSType_SurfaceSourceXlibWindow
|
||||
},
|
||||
display = surfaceParams.X11Display,
|
||||
// TODO "Oh my god x11 support might be a nightmare this is outside of your ability to deal with -pjb"
|
||||
// window = surfaceParams.X11Window,
|
||||
};
|
||||
|
||||
surfaceDesc.nextInChain = (WGPUChainedStruct*)(&surfaceDescX11);
|
||||
}
|
||||
#endif
|
||||
|
||||
var surface = wgpuInstanceCreateSurface(_wgpuInstance, &surfaceDesc);
|
||||
return new WindowData
|
||||
{
|
||||
Surface = surface
|
||||
};
|
||||
}
|
||||
|
||||
private void DecideMainTextureFormat(WindowData mainWindow)
|
||||
{
|
||||
WGPUSurfaceCapabilities surfaceCaps;
|
||||
var res = wgpuSurfaceGetCapabilities(mainWindow.Surface, _wgpuAdapter, &surfaceCaps);
|
||||
if (res != WGPUStatus.WGPUStatus_Success)
|
||||
throw new RhiException("wgpuSurfaceGetCapabilities failed");
|
||||
|
||||
var modes = new Span<WGPUPresentMode>(surfaceCaps.presentModes, (int)surfaceCaps.presentModeCount);
|
||||
_availPresentModes = modes.ToArray();
|
||||
_sawmill.Debug($"Available present modes: {string.Join(", ", _availPresentModes)}");
|
||||
|
||||
var formats = new Span<WGPUTextureFormat>(surfaceCaps.formats, (int)surfaceCaps.formatCount);
|
||||
|
||||
var found = false;
|
||||
foreach (var format in formats)
|
||||
{
|
||||
if (format == WGPUTextureFormat.WGPUTextureFormat_BGRA8UnormSrgb ||
|
||||
format == WGPUTextureFormat.WGPUTextureFormat_RGBA8UnormSrgb)
|
||||
{
|
||||
found = true;
|
||||
_mainTextureFormat = ToRhiFormat(format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_sawmill.Debug($"Available surface formats: {string.Join(", ", formats.ToArray())}");
|
||||
|
||||
if (!found)
|
||||
throw new RhiException("Unable to find suitable surface format for main window!");
|
||||
|
||||
_sawmill.Debug($"Preferred surface format is {_mainTextureFormat}");
|
||||
|
||||
wgpuSurfaceCapabilitiesFreeMembers(surfaceCaps);
|
||||
}
|
||||
|
||||
private void ConfigureSurface(WindowData window, Vector2i size, bool vsync)
|
||||
{
|
||||
var swapChainDesc = new WGPUSurfaceConfiguration
|
||||
{
|
||||
format = ValidateTextureFormat(_mainTextureFormat),
|
||||
width = (uint)size.X,
|
||||
height = (uint)size.Y,
|
||||
usage = WGPUTextureUsage_RenderAttachment,
|
||||
presentMode = WGPUPresentMode.WGPUPresentMode_Fifo,
|
||||
device = _wgpuDevice
|
||||
};
|
||||
|
||||
if (!vsync)
|
||||
{
|
||||
if (_availPresentModes.Contains(WGPUPresentMode.WGPUPresentMode_Immediate))
|
||||
swapChainDesc.presentMode = WGPUPresentMode.WGPUPresentMode_Immediate;
|
||||
else if (_availPresentModes.Contains(WGPUPresentMode.WGPUPresentMode_Mailbox))
|
||||
swapChainDesc.presentMode = WGPUPresentMode.WGPUPresentMode_Mailbox;
|
||||
}
|
||||
|
||||
wgpuSurfaceConfigure(window.Surface, &swapChainDesc);
|
||||
|
||||
_sawmill.Verbose("WebGPU Surface reconfigured!");
|
||||
}
|
||||
|
||||
internal override WindowData WindowCreated(in RhiWindowSurfaceParams surfaceParams, Vector2i size, bool vsync)
|
||||
{
|
||||
var windowData = CreateSurfaceForWindow(in surfaceParams);
|
||||
ConfigureSurface(windowData, size, vsync);
|
||||
return windowData;
|
||||
}
|
||||
|
||||
internal override void WindowDestroy(WindowData reg)
|
||||
{
|
||||
wgpuSurfaceUnconfigure(reg.Surface);
|
||||
wgpuSurfaceRelease(reg.Surface);
|
||||
}
|
||||
|
||||
internal override void WindowRecreateSwapchain(WindowData reg, Vector2i size, bool vsyncEnabled)
|
||||
{
|
||||
ConfigureSurface(reg, size, vsyncEnabled);
|
||||
}
|
||||
|
||||
internal override void WindowPresent(WindowData reg)
|
||||
{
|
||||
// TODO: Safety
|
||||
wgpuSurfacePresent(reg.Surface);
|
||||
}
|
||||
}
|
||||
@@ -1,335 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
using RLogLevel = Robust.Shared.Log.LogLevel;
|
||||
|
||||
#pragma warning disable CS8500
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu : RhiBase
|
||||
{
|
||||
private readonly ISawmill _sawmill;
|
||||
private readonly ISawmill _apiLogSawmill;
|
||||
|
||||
private WGPUInstance _wgpuInstance;
|
||||
private WGPUAdapter _wgpuAdapter;
|
||||
private WGPUDevice _wgpuDevice;
|
||||
private WGPUQueue _wgpuQueue;
|
||||
|
||||
private RhiLimits? _deviceLimits;
|
||||
private RhiAdapterInfo? _adapterProperties;
|
||||
private string _description = "not initialized";
|
||||
|
||||
public override RhiLimits DeviceLimits =>
|
||||
_deviceLimits ?? throw new InvalidOperationException("Not initialized yet");
|
||||
|
||||
public override RhiAdapterInfo AdapterInfo =>
|
||||
_adapterProperties ?? throw new InvalidOperationException("Not initialized yet");
|
||||
|
||||
public override string Description => _description;
|
||||
|
||||
public RhiWebGpu(IDependencyCollection dependencies)
|
||||
{
|
||||
var logMgr = dependencies.Resolve<ILogManager>();
|
||||
|
||||
_sawmill = logMgr.GetSawmill("clyde.rhi.webGpu");
|
||||
_apiLogSawmill = logMgr.GetSawmill("clyde.rhi.webGpu.apiLog");
|
||||
|
||||
Queue = new RhiQueue(this, default);
|
||||
}
|
||||
|
||||
internal override void Init(in RhiInitParams initParams, out WindowData windowData)
|
||||
{
|
||||
_sawmill.Info("Initializing WebGPU RHI!");
|
||||
|
||||
InitInstance(in initParams);
|
||||
|
||||
windowData = CreateSurfaceForWindow(in initParams.MainWindowSurfaceParams);
|
||||
|
||||
_sawmill.Debug("WebGPU main surface created!");
|
||||
|
||||
InitAdapterAndDevice(in initParams, windowData.Surface);
|
||||
DecideMainTextureFormat(windowData);
|
||||
}
|
||||
|
||||
private void InitInstance(in RhiInitParams initParams)
|
||||
{
|
||||
var wgpuVersion = WgpuVersionToString(wgpuGetVersion());
|
||||
_sawmill.Debug($"wgpu-native loaded, version: {wgpuVersion}");
|
||||
|
||||
_description = $"WebGPU (wgpu-native {wgpuVersion})";
|
||||
|
||||
InitLogging();
|
||||
|
||||
Span<byte> buffer = stackalloc byte[128];
|
||||
var pInstanceDescriptor = BumpAllocate<WGPUInstanceDescriptor>(ref buffer);
|
||||
|
||||
// Specify instance extras for wgpu-native.
|
||||
var pInstanceExtras = BumpAllocate<WGPUInstanceExtras>(ref buffer);
|
||||
pInstanceDescriptor->nextInChain = (WGPUChainedStruct*)pInstanceExtras;
|
||||
pInstanceExtras->chain.sType = (WGPUSType)WGPUNativeSType.WGPUSType_InstanceExtras;
|
||||
pInstanceExtras->backends = (uint)GetInstanceBackendCfg(initParams.Backends);
|
||||
|
||||
_wgpuInstance = wgpuCreateInstance(pInstanceDescriptor);
|
||||
|
||||
_sawmill.Debug("WebGPU instance created!");
|
||||
}
|
||||
|
||||
private ulong GetInstanceBackendCfg(string backendCvar)
|
||||
{
|
||||
if (backendCvar == "all")
|
||||
return WGPUInstanceBackend_Primary | WGPUInstanceBackend_Secondary;
|
||||
|
||||
var backends = 0ul;
|
||||
foreach (var opt in backendCvar.Split(","))
|
||||
{
|
||||
backends |= opt switch
|
||||
{
|
||||
"vulkan" => WGPUInstanceBackend_Vulkan,
|
||||
"gl" => WGPUInstanceBackend_GL,
|
||||
"metal" => WGPUInstanceBackend_Metal,
|
||||
"dx12" => WGPUInstanceBackend_DX12,
|
||||
"dx11" => WGPUInstanceBackend_DX11,
|
||||
"browser" => WGPUInstanceBackend_BrowserWebGPU,
|
||||
_ => throw new ArgumentException($"Unknown wgpu backend: '{opt}'")
|
||||
};
|
||||
}
|
||||
|
||||
return backends;
|
||||
}
|
||||
|
||||
private void InitAdapterAndDevice(in RhiInitParams initParams, WGPUSurface forSurface)
|
||||
{
|
||||
var powerPreference = ValidatePowerPreference(initParams.PowerPreference);
|
||||
|
||||
var requestAdapterOptions = new WGPURequestAdapterOptions
|
||||
{
|
||||
compatibleSurface = forSurface,
|
||||
powerPreference = powerPreference
|
||||
};
|
||||
|
||||
WgpuRequestAdapterResult result;
|
||||
wgpuInstanceRequestAdapter(
|
||||
_wgpuInstance,
|
||||
&requestAdapterOptions,
|
||||
new WGPURequestAdapterCallbackInfo
|
||||
{
|
||||
callback = &WgpuRequestAdapterCallback,
|
||||
userdata1 = &result,
|
||||
});
|
||||
|
||||
if (result.Status != WGPURequestAdapterStatus.WGPURequestAdapterStatus_Success)
|
||||
throw new RhiException($"Adapter request failed: {result.Message}");
|
||||
|
||||
_sawmill.Debug("WebGPU adapter created!");
|
||||
|
||||
_wgpuAdapter = result.Adapter.P;
|
||||
|
||||
WGPUAdapterInfo adapterProps = default;
|
||||
wgpuAdapterGetInfo(_wgpuAdapter, &adapterProps);
|
||||
|
||||
WGPULimits adapterLimits = default;
|
||||
wgpuAdapterGetLimits(_wgpuAdapter, &adapterLimits);
|
||||
|
||||
_sawmill.Debug($"adapter device: {GetString(adapterProps.device)}");
|
||||
_sawmill.Debug($"adapter vendor: {GetString(adapterProps.vendor)} ({adapterProps.vendorID})");
|
||||
_sawmill.Debug($"adapter description: {GetString(adapterProps.description)}");
|
||||
_sawmill.Debug($"adapter architecture: {GetString(adapterProps.architecture)}");
|
||||
_sawmill.Debug($"adapter backend: {adapterProps.backendType}");
|
||||
_sawmill.Debug($"adapter type: {adapterProps.adapterType}");
|
||||
_sawmill.Debug($"adapter UBO alignment: {adapterLimits.minUniformBufferOffsetAlignment}");
|
||||
|
||||
_adapterProperties = new RhiAdapterInfo(
|
||||
adapterProps.vendorID,
|
||||
adapterProps.deviceID,
|
||||
GetString(adapterProps.vendor) ?? "",
|
||||
GetString(adapterProps.architecture) ?? "",
|
||||
GetString(adapterProps.device) ?? "",
|
||||
GetString(adapterProps.description) ?? "",
|
||||
(RhiAdapterType) adapterProps.adapterType,
|
||||
(RhiBackendType) adapterProps.backendType
|
||||
);
|
||||
|
||||
_description += $", backend: {_adapterProperties.BackendType}";
|
||||
|
||||
// Default limits, from WebGPU spec.
|
||||
var requiredLimits = new WGPULimits();
|
||||
if (false)
|
||||
{
|
||||
// GLES3.0
|
||||
requiredLimits.maxComputeWorkgroupStorageSize = 16384;
|
||||
requiredLimits.maxComputeInvocationsPerWorkgroup = 256;
|
||||
requiredLimits.maxComputeWorkgroupSizeX = 256;
|
||||
requiredLimits.maxComputeWorkgroupSizeY = 256;
|
||||
requiredLimits.maxComputeWorkgroupSizeZ = 256;
|
||||
requiredLimits.maxComputeWorkgroupsPerDimension = 65536;
|
||||
requiredLimits.maxDynamicStorageBuffersPerPipelineLayout = 0;
|
||||
requiredLimits.maxStorageBuffersPerShaderStage = 4;
|
||||
requiredLimits.maxStorageBufferBindingSize = 134217728;
|
||||
}
|
||||
|
||||
// Required minimums
|
||||
requiredLimits.minStorageBufferOffsetAlignment = 256;
|
||||
requiredLimits.minUniformBufferOffsetAlignment = 256;
|
||||
|
||||
requiredLimits.maxTextureArrayLayers = 256;
|
||||
requiredLimits.maxBindGroups = 4;
|
||||
requiredLimits.maxBindingsPerBindGroup = 1000;
|
||||
requiredLimits.maxDynamicUniformBuffersPerPipelineLayout = 8;
|
||||
requiredLimits.maxSampledTexturesPerShaderStage = 16;
|
||||
requiredLimits.maxSamplersPerShaderStage = 16;
|
||||
requiredLimits.maxUniformBuffersPerShaderStage = 12;
|
||||
requiredLimits.maxUniformBufferBindingSize = 65536;
|
||||
requiredLimits.maxVertexBuffers = 8;
|
||||
requiredLimits.maxVertexAttributes = 16;
|
||||
requiredLimits.maxVertexBufferArrayStride = 2048;
|
||||
requiredLimits.maxInterStageShaderVariables = 16;
|
||||
requiredLimits.maxColorAttachments = 8;
|
||||
requiredLimits.maxColorAttachmentBytesPerSample = 32;
|
||||
requiredLimits.maxBufferSize = 268435456;
|
||||
|
||||
// Custom limits
|
||||
// Take as low UBO alignment as we can get.
|
||||
requiredLimits.minUniformBufferOffsetAlignment = adapterLimits.minUniformBufferOffsetAlignment;
|
||||
// Take as large textures as we can get.
|
||||
requiredLimits.maxTextureDimension1D = adapterLimits.maxTextureDimension1D;
|
||||
requiredLimits.maxTextureDimension2D = adapterLimits.maxTextureDimension2D;
|
||||
requiredLimits.maxTextureDimension3D = adapterLimits.maxTextureDimension3D;
|
||||
|
||||
// TODO: clear this.
|
||||
var errorGCHandle = GCHandle.Alloc(this);
|
||||
|
||||
var deviceDesc = new WGPUDeviceDescriptor();
|
||||
deviceDesc.requiredLimits = &requiredLimits;
|
||||
deviceDesc.uncapturedErrorCallbackInfo = new WGPUUncapturedErrorCallbackInfo
|
||||
{
|
||||
callback = &UncapturedErrorCallback,
|
||||
userdata1 = (void*)GCHandle.ToIntPtr(errorGCHandle),
|
||||
};
|
||||
WgpuRequestDeviceResult deviceResult;
|
||||
wgpuAdapterRequestDevice(
|
||||
_wgpuAdapter,
|
||||
&deviceDesc,
|
||||
new WGPURequestDeviceCallbackInfo
|
||||
{
|
||||
callback = &WgpuRequestDeviceCallback,
|
||||
userdata1 = &deviceResult
|
||||
});
|
||||
|
||||
if (deviceResult.Status != WGPURequestDeviceStatus.WGPURequestDeviceStatus_Success)
|
||||
throw new Exception($"Device request failed: {deviceResult.Message}");
|
||||
|
||||
_sawmill.Debug("WebGPU device created!");
|
||||
|
||||
_wgpuDevice = deviceResult.Device;
|
||||
_wgpuQueue = wgpuDeviceGetQueue(_wgpuDevice);
|
||||
|
||||
_deviceLimits = new RhiLimits(
|
||||
requiredLimits.maxTextureDimension1D,
|
||||
requiredLimits.maxTextureDimension2D,
|
||||
requiredLimits.maxTextureDimension3D,
|
||||
requiredLimits.maxTextureArrayLayers,
|
||||
requiredLimits.maxBindGroups,
|
||||
requiredLimits.maxBindingsPerBindGroup,
|
||||
requiredLimits.maxDynamicUniformBuffersPerPipelineLayout,
|
||||
requiredLimits.maxDynamicStorageBuffersPerPipelineLayout,
|
||||
requiredLimits.maxSampledTexturesPerShaderStage,
|
||||
requiredLimits.maxSamplersPerShaderStage,
|
||||
requiredLimits.maxStorageBuffersPerShaderStage,
|
||||
requiredLimits.maxStorageTexturesPerShaderStage,
|
||||
requiredLimits.maxUniformBuffersPerShaderStage,
|
||||
requiredLimits.maxUniformBufferBindingSize,
|
||||
requiredLimits.maxStorageBufferBindingSize,
|
||||
requiredLimits.minUniformBufferOffsetAlignment,
|
||||
requiredLimits.minStorageBufferOffsetAlignment,
|
||||
requiredLimits.maxVertexBuffers,
|
||||
requiredLimits.maxBufferSize,
|
||||
requiredLimits.maxVertexAttributes,
|
||||
requiredLimits.maxVertexBufferArrayStride,
|
||||
requiredLimits.maxInterStageShaderVariables,
|
||||
requiredLimits.maxColorAttachments,
|
||||
requiredLimits.maxColorAttachmentBytesPerSample,
|
||||
requiredLimits.maxComputeWorkgroupStorageSize,
|
||||
requiredLimits.maxComputeInvocationsPerWorkgroup,
|
||||
requiredLimits.maxComputeWorkgroupSizeX,
|
||||
requiredLimits.maxComputeWorkgroupSizeY,
|
||||
requiredLimits.maxComputeWorkgroupSizeZ,
|
||||
requiredLimits.maxComputeWorkgroupsPerDimension
|
||||
);
|
||||
}
|
||||
|
||||
private void InitLogging()
|
||||
{
|
||||
// TODO: clear this.
|
||||
var gcHandle = GCHandle.Alloc(this);
|
||||
|
||||
wgpuSetLogCallback(&LogCallback, (void*)GCHandle.ToIntPtr(gcHandle));
|
||||
wgpuSetLogLevel(WGPULogLevel.WGPULogLevel_Warn);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void LogCallback(WGPULogLevel level, WGPUStringView message, void* userdata)
|
||||
{
|
||||
var self = (RhiWebGpu)GCHandle.FromIntPtr((nint)userdata).Target!;
|
||||
var messageString = GetString(message)!;
|
||||
|
||||
var robustLevel = level switch
|
||||
{
|
||||
WGPULogLevel.WGPULogLevel_Error => RLogLevel.Error,
|
||||
WGPULogLevel.WGPULogLevel_Warn => RLogLevel.Warning,
|
||||
WGPULogLevel.WGPULogLevel_Info => RLogLevel.Info,
|
||||
WGPULogLevel.WGPULogLevel_Debug => RLogLevel.Debug,
|
||||
WGPULogLevel.WGPULogLevel_Trace or _ => RLogLevel.Verbose,
|
||||
};
|
||||
|
||||
self._apiLogSawmill.Log(robustLevel, messageString);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void UncapturedErrorCallback(WGPUDevice* device, WGPUErrorType level, WGPUStringView message, void* userdata1, void* userdata2)
|
||||
{
|
||||
var self = (RhiWebGpu)GCHandle.FromIntPtr((nint)userdata1).Target!;
|
||||
var messageString = GetString(message);
|
||||
|
||||
self._apiLogSawmill.Error(messageString ?? "Unknown error");
|
||||
}
|
||||
|
||||
internal override void Shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
private record struct WgpuRequestAdapterResult(WGPURequestAdapterStatus Status, Ptr<WGPUAdapterImpl> Adapter, string Message);
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void WgpuRequestAdapterCallback(
|
||||
WGPURequestAdapterStatus status,
|
||||
WGPUAdapter adapter,
|
||||
WGPUStringView message,
|
||||
void* userdata1,
|
||||
void* userdata2)
|
||||
{
|
||||
*(WgpuRequestAdapterResult*)userdata1 = new WgpuRequestAdapterResult(
|
||||
status,
|
||||
adapter,
|
||||
GetString(message) ?? "");
|
||||
}
|
||||
|
||||
private record struct WgpuRequestDeviceResult(WGPURequestDeviceStatus Status, Ptr<WGPUDeviceImpl> Device, string Message);
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void WgpuRequestDeviceCallback(
|
||||
WGPURequestDeviceStatus status,
|
||||
WGPUDevice device,
|
||||
WGPUStringView message,
|
||||
void* userdata1,
|
||||
void* userdata2)
|
||||
{
|
||||
*(WgpuRequestDeviceResult*)userdata1 = new WgpuRequestDeviceResult(
|
||||
status,
|
||||
device,
|
||||
GetString(message) ?? "");
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build.Framework" Version="17.8.3" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.5" />
|
||||
<PackageReference Include="Microsoft.Build.Framework" Version="18.0.2" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Engine.props"/>
|
||||
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>../bin/Client.IntegrationTests</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="NUnit"/>
|
||||
<PackageReference Include="NUnit3TestAdapter"/>
|
||||
<PackageReference Include="NUnit.Analyzers"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NetSerializer\NetSerializer\NetSerializer.csproj" />
|
||||
<ProjectReference Include="..\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\Robust.Server.Testing\Robust.Server.Testing.csproj" />
|
||||
<ProjectReference Include="..\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj"/>
|
||||
<ProjectReference Include="..\Robust.Shared.Maths.Testing\Robust.Shared.Maths.Testing.csproj"/>
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj"/>
|
||||
<ProjectReference Include="..\Robust.UnitTesting\Robust.UnitTesting.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets"/>
|
||||
</Project>
|
||||
@@ -2,7 +2,6 @@ using NUnit.Framework;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
{
|
||||
@@ -1,5 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[assembly: InternalsVisibleTo("Robust.Client.Graphics.Rhi")]
|
||||
[module: SkipLocalsInit]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user