Skip to content

Add MonoGame 3.8.5 Content Builder export mode (closes DX12/Vulkan shader gate)#120

Open
vchelaru wants to merge 5 commits into
mainfrom
feat/monogame-content-builder-export
Open

Add MonoGame 3.8.5 Content Builder export mode (closes DX12/Vulkan shader gate)#120
vchelaru wants to merge 5 commits into
mainfrom
feat/monogame-content-builder-export

Conversation

@vchelaru

Copy link
Copy Markdown
Owner

Summary

  • Adds an opt-in ContentBuildMode.ContentBuilder export choice, available for every MonoGame target. It routes both assets and shipped .fx shaders through MonoGame 3.8.5 GA's new code-first Content Builder (a generated {ProjectName}.Content console project + Builder.cs + BuildContent.targets) instead of raw/ShadowDusk/MGCB, superseding ShaderCompileMode for that export.
  • Unlike the legacy mgcb.exe, the new Content Builder isn't limited to a fixed platform list — so this is the mechanism that finally closes the DX12/Vulkan shader gate from issue Export shaders to gated targets: iOS, MonoGame DX12/Vulkan #52 (CompilesShippedShaders now returns true there under Content Builder mode).
  • MonoGame.Content.Builder.Task stays installed on classic MonoGame targets even in Content Builder mode: Apos.Shapes/Gum ship their own .fx + Content.mgcb via buildTransitive, invisible to the new pipeline (which only walks the fiddle's own Assets/ folder). Both pipelines deliberately coexist there.
  • New export-dialog "Content pipeline" radio (Raw / Content Builder), gated to ExportRuntime.MonoGame; picking Content Builder hides the now-superseded shader-compilation radio and updates the status banner.

Stacks on #119 (chore/monogame-3.8.5-ga, not yet merged) — this branch is based on that PR's HEAD, so the diff includes its commit until #119 merges.

Implementation notes / deviations from the plan

  • Asset exclusion list widened from 2 to 13 extensions. The plan named only .achx/.fnt as needing to stay raw (no pipeline importer). A grep of Index.razor.cs's SupportedAssetExtensions (broadened by Broaden asset allowlist to include tilemap/level and text-data formats #115, the commit right before this stack) turned up the full set that XnaFiddle ships raw via TitleContainer.OpenStream/dedicated loaders rather than ContentManager.Load<T>: .achx .fnt .ttf .ember .xnb .tmx .tsx .world .ldtk .ogmo .json .txt .xml. Only .png/.wav are actually ContentManager-loadable, so those are the only ones routed into {ProjectName}.Content/Assets/; everything else keeps shipping into the head's own Content/ folder as before.
  • Fixed a real bug in the plan's own predicate spec. The plan's verbatim UsesShadowDuskShaders/UsesMgcbShaders guarded on contentMode != ContentBuildMode.ContentBuilder (global), which would have gated all shaders — including KNI/FNA — to "unsupported" the moment ContentBuildMode.ContentBuilder was passed to Export(), even for a non-MonoGame target. Changed the guard to !UsesContentBuilder(target, contentMode) (target-aware) so KNI/FNA stay a true no-op, matching the plan's own required "KNI/FNA no-op" test.
  • GenerateCsproj's raw-copy suppression is conditional, not unconditional. The plan said to unconditionally suppress the <None Include>/<AndroidAsset Include> raw-copy block under Content Builder mode. That would silently drop the .achx/.fnt-style excluded files from the build output entirely (they'd still be zipped into the head's Content/ folder, but never copied to the output dir). Instead the block now stays gated on whether there's any Content-Builder-excluded content left (hasRawContentBuilderAssets), so those files still copy correctly.

Verification

  • dotnet test — 337/337 passing (7 new facts + 5 new Theory rows for Content Builder mode).
  • dotnet buildXnaFiddle.Core, XnaFiddle.Tests, XnaFiddle.BlazorGL all clean.
  • Real end-to-end build attempted (network/NuGet was reachable in this sandbox, including the brand-new MonoGame.Tool.Dxc etc. packages): generated a real Content-Builder-mode zip for a classic target (MonoGameDesktopGL) and a next-gen target (MonoGameWindowsDX12), dotnet restore + dotnet build both.
    • Classic target: asset (.png) and shader (.fx) both correctly picked up by WildcardRule("*") and compiled to .xnb. Confirms the asset-routing/exclusion logic is correct and there's no MonoGame.Content.Builder.Task vs. Content Builder target-name collision.
    • DX12 target: asset compiled fine; the shader failed with a legitimate, expected error (Invalid DirectX 12 pixel profile 'ps_3_0'! Requires ps_6_0) — my throwaway test .fx used a legacy SM3 profile, which DX12's real DXC-based compiler correctly rejects. This confirms the DX12 plumbing (packages, Import, WildcardRule routing) works end-to-end; it's not an exporter bug, just a reminder that a real shipped .fx needs an SM6-compatible technique to build on DX12.
    • Found a real, reproducible issue (not a target-name collision, but the same category of risk): running dotnet build on the whole .slnx (default multi-core parallelism) races — the Content project is both a normal top-level .slnx member and invoked via a nested <MSBuild Projects="...Content.csproj"> inside BuildContent.targets, and the two concurrent builds of the same project collide on the output DLL (CS2012: ... being used by another process). Verified this is not something this PR introduced — MonoGame's own official 3.8.5 GA template (fetched from MonoGame/MonoGame.Templates) has the exact same structure (byte-for-byte matching BuildContent.targets/{Name}.Content.csproj, confirmed by direct comparison) and would hit the identical race. Two verified workarounds: dotnet build *.slnx -maxcpucount:1, or dotnet restore *.slnx once followed by dotnet build on just the single head .csproj. A ProjectReference ReferenceOutputAssembly="false" fix doesn't work as-is (cross-TFM error: head is net8.0, Content project is net9.0 per the reference template). Flagging this for follow-up rather than fixing here, since it's an upstream MonoGame template characteristic, not specific to this PR's logic.

Test plan

  • dotnet test (337/337)
  • dotnet build across XnaFiddle.Core/XnaFiddle.Tests/XnaFiddle.BlazorGL
  • Real dotnet restore/dotnet build of a generated Content-Builder-mode export (classic + DX12 targets)
  • Manual verification in the running app (export dialog UI)

🤖 Generated with Claude Code

vchelaru and others added 5 commits July 18, 2026 19:40
3.8.5 shipped stable on nuget.org, replacing the preview line. Removes
the now-dead preview/stable duality (version props, UI selector,
.Native version-gating, the monoGameVersion override) — WindowsDX12
and DesktopVK are now regular, always-available MonoGame targets.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ader gate)

Opt-in ContentBuildMode.ContentBuilder routes assets and shaders through
MonoGame's new code-first Content Builder (Builder.cs + BuildContent.targets)
instead of raw/ShadowDusk/MGCB, superseding ShaderCompileMode when chosen.
Unlike mgcb.exe it isn't limited to a fixed platform list, so it also closes
the DX12/Vulkan shader gate from issue #52. MonoGame.Content.Builder.Task
stays installed on classic targets for buildTransitive-only libraries
(Apos.Shapes/Gum) that the new pipeline can't see.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
MonoGame's DX12/Vulkan Content Builder compiles via DXC, which requires
Shader Model 6 (vs_6_0/ps_6_0), rejects the legacy sampler2D/tex2D combo,
and requires SV_Target0 instead of COLOR/COLOR0 for the pixel output.
None of the example shaders had an SM6 branch, so ContentBuilder exports
to MonoGameWindowsDX12/MonoGameDesktopVK failed to compile every shader.

Adds an `#elif defined(SM6) || defined(VULKAN)` branch to each file's
shader-model defines, a matching Texture2D<float4>/SamplerState
declaration for the texture(s), and a fully separate SM6 pixel-shader
entry point (ShadowDusk's OpenGL translator requires the classic
`: COLOR` / `tex2D(...)` entry point to stay literal and unsplit, so the
two shader models get independent function bodies rather than sharing
one via macros or a split signature). The `#if OPENGL` / classic branches
are untouched.

Verified: ShadowDuskCLI /Profile:OpenGL produces byte-identical .mgfx
output before/after for all 16 files; a real `dotnet build` of a
ContentBuildMode.ContentBuilder export targeting MonoGameWindowsDX12
compiles all 16 shaders with zero errors; full XnaFiddle.Tests suite
(337 tests) passes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… Vulkan backend

ShadowDusk 0.12.0 adds a real Vulkan backend that compiles the same plain
.fx source as every other target (no SM6 source rewrite needed, verified
against the pre-SM6-branch form of Grayscale.fx via ShadowDuskCLI and a
real dotnet build). GetShaderExportInfo now wires MonoGameDesktopVK through
ShadowDusk.Compiler/PlatformTarget.Vulkan, closing the DesktopVK half of
issue #52; MonoGameWindowsDX12 is unchanged (still gated, still needs
ContentBuildMode.ContentBuilder for shaders).

Verified the 0.11.0 -> 0.12.0 GL codegen fidelity change (pow/division
strength-reduction) produces byte-identical .mgfx for all 16 bundled
example shaders, so no rendering regression risk there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
"Shader compilation:" and "Content pipeline:" silently overrode each
other and both used the words "Content Pipeline"/"MGCB" for different
things. Replaced with a single "Content strategy:" group (ShadowDusk /
Classic MGCB / MonoGame Content Builder) backed by one UI-only
ContentStrategy enum that maps to ProjectExporter's existing
(ShaderCompileMode, ContentBuildMode) pair. No changes to
ProjectExporter's public API.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant