Add MonoGame 3.8.5 Content Builder export mode (closes DX12/Vulkan shader gate)#120
Merged
Merged
Conversation
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.
Splits the 3-way content strategy choice into two independent enums: ContentBuildMode (Raw/ClassicMgcb/ContentBuilder) for non-shader assets, and ShaderCompileMode (ShadowDusk/Native, renamed from ContentPipeline) for .fx compilation. ShadowDusk is now the default shader compiler under every content strategy, not just Raw - previously Content Builder mode silently forced shaders through its own native pipeline. ClassicMgcb becomes a real asset pipeline for the first time: .png/.wav compile via TextureImporter/WavImporter, with ColorKeyEnabled=False forced to stop MonoGame's default magenta color-keying from punching holes in shipped textures. DX12 was NOT wired into GetShaderExportInfo - checked ShadowDusk's NuGet feed and PlatformTarget.cs; still 0.12.0 with no DirectX12 backend (README lists it "Not yet"). CompilesShippedShaders(DX12, ShadowDusk, ContentBuilder) stays gated until ShadowDusk ships one. Fixed a bug surfaced by the axis decoupling: a ShadowDusk-mode .fx can now stay in the head's own Content/ under ContentBuildMode.ContentBuilder, but the csproj's copy-to-output gate assumed nothing ever did, so the shader shipped in the zip but never reached the build output. UI: two independent radio groups replace the single 3-way choice; Raw shows a fixed "ShadowDusk (fixed)" label since it has no native pipeline. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Multi-platform exports listed the shared Common library and Content Builder console project before any actual game project — Visual Studio picks the first .slnx entry as the default startup project, so users had to manually retarget it every time. Platform-head projects now come first. Also refreshes the two now-stale skill docs (project-export, shaders) and CLAUDE.md's skill-maintenance policy for the orthogonal content/shader axes rework, and removes "(default)" from radio labels that snuck back in. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ContentBuildMode.ContentBuilderexport choice, available for every MonoGame target. It routes both assets and shipped.fxshaders through MonoGame 3.8.5 GA's new code-first Content Builder (a generated{ProjectName}.Contentconsole project +Builder.cs+BuildContent.targets) instead of raw/ShadowDusk/MGCB, supersedingShaderCompileModefor that export.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 (CompilesShippedShadersnow returnstruethere under Content Builder mode).MonoGame.Content.Builder.Taskstays installed on classic MonoGame targets even in Content Builder mode: Apos.Shapes/Gum ship their own.fx+Content.mgcbviabuildTransitive, invisible to the new pipeline (which only walks the fiddle's ownAssets/folder). Both pipelines deliberately coexist there.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
.achx/.fntas needing to stay raw (no pipeline importer). A grep ofIndex.razor.cs'sSupportedAssetExtensions(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 viaTitleContainer.OpenStream/dedicated loaders rather thanContentManager.Load<T>:.achx .fnt .ttf .ember .xnb .tmx .tsx .world .ldtk .ogmo .json .txt .xml. Only.png/.wavare actuallyContentManager-loadable, so those are the only ones routed into{ProjectName}.Content/Assets/; everything else keeps shipping into the head's ownContent/folder as before.UsesShadowDuskShaders/UsesMgcbShadersguarded oncontentMode != ContentBuildMode.ContentBuilder(global), which would have gated all shaders — including KNI/FNA — to "unsupported" the momentContentBuildMode.ContentBuilderwas passed toExport(), 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'sContent/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 newTheoryrows for Content Builder mode).dotnet build—XnaFiddle.Core,XnaFiddle.Tests,XnaFiddle.BlazorGLall clean.MonoGame.Tool.Dxcetc. packages): generated a real Content-Builder-mode zip for a classic target (MonoGameDesktopGL) and a next-gen target (MonoGameWindowsDX12),dotnet restore+dotnet buildboth..png) and shader (.fx) both correctly picked up byWildcardRule("*")and compiled to.xnb. Confirms the asset-routing/exclusion logic is correct and there's noMonoGame.Content.Builder.Taskvs. Content Builder target-name collision.Invalid DirectX 12 pixel profile 'ps_3_0'! Requires ps_6_0) — my throwaway test.fxused a legacy SM3 profile, which DX12's real DXC-based compiler correctly rejects. This confirms the DX12 plumbing (packages,Import,WildcardRulerouting) works end-to-end; it's not an exporter bug, just a reminder that a real shipped.fxneeds an SM6-compatible technique to build on DX12.dotnet buildon the whole.slnx(default multi-core parallelism) races — the Content project is both a normal top-level.slnxmember and invoked via a nested<MSBuild Projects="...Content.csproj">insideBuildContent.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 fromMonoGame/MonoGame.Templates) has the exact same structure (byte-for-byte matchingBuildContent.targets/{Name}.Content.csproj, confirmed by direct comparison) and would hit the identical race. Two verified workarounds:dotnet build *.slnx -maxcpucount:1, ordotnet restore *.slnxonce followed bydotnet buildon just the single head.csproj. AProjectReference 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 buildacrossXnaFiddle.Core/XnaFiddle.Tests/XnaFiddle.BlazorGLdotnet restore/dotnet buildof a generated Content-Builder-mode export (classic + DX12 targets)Update: reworked into two independent axes
The design above conflated "how assets build" and "how shaders compile" into one 3-way choice — picking Content Builder unconditionally forced shaders through its native DXC pipeline and hid ShadowDusk entirely. Reworked into two orthogonal enums:
ContentBuildMode:Raw/ClassicMgcb(new) /ContentBuilder— how non-shader assets build.ShaderCompileMode:ShadowDusk/Native(renamed fromContentPipeline) — how.fxcompiles, independent of asset strategy.ShadowDusk is now the default shader compiler under every content strategy, not just Raw.
ClassicMgcbis a real asset pipeline for the first time (previously it only ever touched.fx) —.png/.wavnow compile viaTextureImporter/WavImporter, withColorKeyEnabled=Falseforced (MonoGame'sTextureProcessordefaults to color-keying pure magenta to transparent, which would otherwise silently punch holes in shipped textures — confirmed with a real end-to-end build, see Verification below).DX12 + ShadowDusk: per the plan, checked ShadowDusk's NuGet feed and
PlatformTarget.csfor a DX12 backend before wiringMonoGameWindowsDX12intoGetShaderExportInfo. Still at 0.12.0, noDirectX12enum member, README lists DX12 as "Not yet". Left the existing_ => defaultfallthrough as-is;CompilesShippedShaders(DX12, ShadowDusk, ContentBuilder)staysfalse. TestDX12_ContentBuilder_ShadowDusk_StaysGated_UntilShadowDuskAddsDx12pins this and documents the flip: once ShadowDusk ships DX12, add theMonoGameWindowsDX12case and this assertion flips totruewith no other code change needed.Bug found during implementation (not called out in the plan): decoupling the axes means a ShadowDusk-mode
.fxcan now stay in the head's ownContent/folder even underContentBuildMode.ContentBuilder(previously Content Builder always redirected shaders too, so this case never existed). The csproj's wholesale-copy-to-output gate assumed nothing was ever left inContent/under Content Builder mode, so that.fxwould ship in the zip but never getCopyToOutputDirectory— build succeeds, but the shader fails to load at runtime. Fixed by broadeninghasNonPipelineContentBuilderAssets's computation to also coverhasShaders && !useContentBuilderShaders; regression-tested bySinglePlatform_ContentBuilder_ShadowDuskShaders_HeadContentShaderStillWiredForCopyToOutput.Verification (this update):
dotnet test XnaFiddle.Tests— 350/350 passing (76 tests inProjectExporterTests).dotnet build XnaFiddle.sln— clean.ClassicMgcb+ShadowDusk, one magenta.pngasset) via a throwaway console driver callingProjectExporter.Exportdirectly, extracted the zip, ran a realdotnet restore/dotnet build, then parsed the compiledmagenta.xnb's raw pixel bytes directly — all 4 texels stayed opaque magenta (alpha=255), confirmingColorKeyEnabled=Falseworks end-to-end, not just structurally in the generatedContent.mgcbtext.Scope: this update covers the core enum/predicate rework,
GenerateContentMgcb/asset-routing/GenerateCsproj, the UI (two independent radio groups), and theProjectExporterTests.csrewrite. The automated build-verification test suite (XnaFiddle.ExportBuild.Tests) remains a separate, deferred follow-up.🤖 Generated with Claude Code