mumain/ClientLibrary/MUnique.Client.Library.csproj
janblade 0702144c84 feat(render): GPU bone skinning (15-20x CPU win), retire fixed-function GL
Two changes bundled together: moving skeletal skinning off the CPU onto the
GPU, and retiring the fixed-function pipeline (FFP) in favor of a real
backend-agnostic render hardware interface (RHI) with a GL implementation.

GPU bone skinning
- Per-vertex skeletal skinning (BMD::Transform()'s CPU loop, previously run
  for every vertex of every visible body, every frame) moves to the GPU:
  the CPU now only computes and uploads the (much smaller) per-bone matrix
  palette; the vertex shader does the per-vertex transform in parallel.
- Measured result: `Transform()` self-time at a standard crowded spot
  dropped from ~0.25ms to 0.01-0.02ms -- a 15-20x reduction. This is exactly
  the cost that scales worst in an MMO client (town crowds, mass PK/boss
  events), so the win is disproportionately larger in the scenarios that
  used to hurt most.
- Cloth cape meshes are the one remaining CPU-skinned case (pinned-vertex
  physics read/write-back), deliberately deferred rather than forced into
  this pass.

Eliminating the fixed-function pipeline
- Render/Core/ImmediateRenderer.{h,cpp} ("IR"): a streaming-VBO bridge that
  mirrors the legacy glBegin/glEnd/glVertex/glColor/glTexCoord immediate-mode
  API 1:1 (decomposing GL_QUADS to GL_TRIANGLES), so the ~hundreds of legacy
  immediate-mode call sites across UI/effects code didn't need a per-site
  rewrite -- they draw through IR, which streams into a VBO and issues a real
  shader-backed glDrawArrays instead of relying on FFP's glBegin/glEnd.
- Render/Core/GlobalUBO.{h,cpp} / SceneUBO.{h,cpp} / BoneUBO.{h,cpp}: replace
  the FFP matrix stack (glMatrixMode/glLoadMatrix/glMultMatrix) and per-bone
  transform state with explicit uniform buffers the shaders read instead.
- Render/Core/GLColorIntercept.h: glColor*() under Core Profile is a no-op
  and glGetFloatv(GL_CURRENT_COLOR, ...) is illegal, so ~340 RenderColor()/
  RenderBitmap()-family call sites that used to read back "current color"
  via that query now read a shadow g_CurrentColor mirrored by macro-
  intercepted glColor* wrappers instead.
- Render/Core/BindState.{h,cpp}: single-source-of-truth glUseProgram/
  glBindTexture/glBindVertexArray wrapper with last-bound-id caching, so
  shader/texture/VAO state is tracked explicitly instead of relying on GL's
  implicit FFP-era binding state.
- tools/check_gl_wrapper_monopoly.py, wired into CMake as an always-rerun
  build target (CheckGLWrapperMonopoly): fails the build if a raw gl*() call
  appears outside Render/ that isn't on its allowlist, so no new FFP call
  site can sneak back in. The current allowlist is a deliberately documented
  set of deferrals (screenshot readback, one-time startup queries, event fog
  params), not oversights.
- ~60 supporting files (Models/Effects/UI/GameMaps/Terrain/etc.) updated to
  route through the new wrapper family instead of calling gl* directly.

RHI abstraction
- Render/RHI/{RHI,RHI_GL}.{h,cpp}: buffer/texture/uniform-block/draw
  abstraction so rendering code targets a stable interface rather than raw
  GL calls -- a GL implementation today, sized to add other backends later
  without touching call sites.
- Render/Shaders/{BMDMeshShader,TerrainShader,PassthroughShader,
  PlanarShadowShader,ItemSpecularShader}.{h,cpp} (+ glsl/*.vert/.frag):
  GLSL shader pairs replacing the fixed-function mesh/terrain/UI-sprite/
  planar-shadow/item-specular rendering paths -- this is also what the GPU
  skinning above actually runs inside of.
2026-08-05 16:47:50 +08:00

80 lines
9.3 KiB
XML

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<!-- Platform and PlatformTarget are set by CMake via command line -->
<Nullable>enable</Nullable>
<SupportNativeAot>true</SupportNativeAot>
<IsAotCompatible>true</IsAotCompatible>
<PublishAot>true</PublishAot>
<OptimizationPreference>Speed</OptimizationPreference>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<WarningsAsErrors>nullable;CS4014;VSTHRD110;VSTHRD100</WarningsAsErrors>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.11.20">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="MUnique.OpenMU.Network.Packets" Version="0.9.9" />
<PackageReference Include="Pipelines.Sockets.Unofficial" Version="2.2.8" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" />
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent" Condition="'$(ci)'!='true'"
Inputs="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ClientToServer\ClientToServerPackets.xml;
$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ChatServer\ChatServerPackets.xml;
$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ConnectServer\ConnectServerPackets.xml;
$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\CommonEnums.xml;
GenerateExtensionsDotNet.xslt;GenerateEnums.xslt;GenerateFunctionsHeader.xslt;GenerateFunctions.xslt;GenerateBindingsHeader.xslt"
Outputs="ConnectionManager.ClientToServerFunctions.cs;ConnectionManager.ChatServerFunctions.cs;ConnectionManager.ConnectServerFunctions.cs;
..\src\source\Dotnet\PacketFunctions_CommonEnums.h;..\src\source\Dotnet\PacketFunctions_ClientToServer_Enums.h;..\src\source\Dotnet\PacketFunctions_ChatServer_Enums.h;..\src\source\Dotnet\PacketFunctions_ConnectServer_Enums.h;
..\src\source\Dotnet\PacketFunctions_ClientToServer.h;..\src\source\Dotnet\PacketFunctions_ChatServer.h;..\src\source\Dotnet\PacketFunctions_ConnectServer.h;
..\src\source\Dotnet\PacketFunctions_ClientToServer.cpp;..\src\source\Dotnet\PacketFunctions_ChatServer.cpp;..\src\source\Dotnet\PacketFunctions_ConnectServer.cpp;
..\src\source\Dotnet\PacketBindings_ClientToServer.h;..\src\source\Dotnet\PacketBindings_ChatServer.h;..\src\source\Dotnet\PacketBindings_ConnectServer.h">
<!-- We only run the transformation locally on our dev environment with visual studio.
The CI environment is identified by the parameter 'ci'. It's set in the azure pipelines and Dockerfile.
If you run into errors here under Linux/Mac OS, it might help to add this parameter, too.
Inputs/Outputs added 2026-08-03: without them this target has no incremental-build tracking
and MSBuild reruns it (unconditionally overwriting every output below) on every single
dotnet build/publish, even when the packet XML/XSLT sources haven't changed. See
CLIENT/src/CMakeLists.txt's DOTNET_DLL_PATH comment for the CMake-side half of this fix. -->
<XslTransformation OutputPaths="ConnectionManager.ClientToServerFunctions.cs" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ClientToServer\ClientToServerPackets.xml" XslInputPath="GenerateExtensionsDotNet.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ClientToServer'/&gt;" />
<XslTransformation OutputPaths="ConnectionManager.ChatServerFunctions.cs" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ChatServer\ChatServerPackets.xml" XslInputPath="GenerateExtensionsDotNet.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ChatServer'/&gt;" />
<XslTransformation OutputPaths="ConnectionManager.ConnectServerFunctions.cs" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ConnectServer\ConnectServerPackets.xml" XslInputPath="GenerateExtensionsDotNet.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ConnectServer'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketFunctions_CommonEnums.h" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\CommonEnums.xml" XslInputPath="GenerateEnums.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='CommonEnums'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketFunctions_ClientToServer_Enums.h" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ClientToServer\ClientToServerPackets.xml" XslInputPath="GenerateEnums.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ClientToServer'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketFunctions_ChatServer_Enums.h" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ChatServer\ChatServerPackets.xml" XslInputPath="GenerateEnums.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ChatServer'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketFunctions_ConnectServer_Enums.h" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ConnectServer\ConnectServerPackets.xml" XslInputPath="GenerateEnums.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ConnectServer'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketFunctions_ClientToServer.h" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ClientToServer\ClientToServerPackets.xml" XslInputPath="GenerateFunctionsHeader.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ClientToServer'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketFunctions_ChatServer.h" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ChatServer\ChatServerPackets.xml" XslInputPath="GenerateFunctionsHeader.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ChatServer'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketFunctions_ConnectServer.h" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ConnectServer\ConnectServerPackets.xml" XslInputPath="GenerateFunctionsHeader.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ConnectServer'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketFunctions_ClientToServer.cpp" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ClientToServer\ClientToServerPackets.xml" XslInputPath="GenerateFunctions.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ClientToServer'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketFunctions_ChatServer.cpp" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ChatServer\ChatServerPackets.xml" XslInputPath="GenerateFunctions.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ChatServer'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketFunctions_ConnectServer.cpp" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ConnectServer\ConnectServerPackets.xml" XslInputPath="GenerateFunctions.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ConnectServer'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketBindings_ClientToServer.h" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ClientToServer\ClientToServerPackets.xml" XslInputPath="GenerateBindingsHeader.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ClientToServer'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketBindings_ChatServer.h" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ChatServer\ChatServerPackets.xml" XslInputPath="GenerateBindingsHeader.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ChatServer'/&gt;" />
<XslTransformation OutputPaths="..\src\source\Dotnet\PacketBindings_ConnectServer.h" XmlInputPaths="$(NuGetPackageRoot)\munique.openmu.network.packets\0.9.9\contentFiles\any\net10.0\ConnectServer\ConnectServerPackets.xml" XslInputPath="GenerateBindingsHeader.xslt" Parameters="&lt;Parameter Name='subNamespace' Value='ConnectServer'/&gt;" />
</Target>
</Project>