Skip to content

Commit 2748c96

Browse files
committed
tests: shared render stage with multiple output formats
1 parent e8ab64b commit 2748c96

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
2+
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
3+
4+
using System.Threading.Tasks;
5+
using Stride.Core;
6+
using Stride.Core.Mathematics;
7+
using Stride.Engine;
8+
using Stride.Rendering;
9+
using Stride.Rendering.Compositing;
10+
using Stride.Rendering.Materials;
11+
using Stride.Rendering.Materials.ComputeColors;
12+
using Stride.Rendering.ProceduralModels;
13+
using Xunit;
14+
15+
namespace Stride.Graphics.Tests
16+
{
17+
/// <summary>
18+
/// Renders a single shared render stage into two render targets with different pixel formats
19+
/// (RGBA and BGRA), as happens when a stage is shared between a render-texture and the backbuffer
20+
/// (e.g. the AnimatedModel sample on Vulkan, where the surface only offers BGRA). The pipeline
21+
/// state is cached per output format on <see cref="RenderEffect"/>; without that, the second
22+
/// target reuses the first's pipeline state and trips a Vulkan framebuffer/renderpass format
23+
/// mismatch. <see cref="GameTestBase"/> fails the test on any unexpected GPU validation error.
24+
/// </summary>
25+
public class TestSharedStageMultipleOutputs : Graphics.Regression.GameTestBase
26+
{
27+
private Texture renderTargetRgba;
28+
private Texture renderTargetBgra;
29+
30+
public TestSharedStageMultipleOutputs()
31+
{
32+
GraphicsDeviceManager.PreferredBackBufferWidth = 256;
33+
GraphicsDeviceManager.PreferredBackBufferHeight = 256;
34+
GraphicsDeviceManager.PreferredDepthStencilFormat = PixelFormat.D24_UNorm_S8_UInt;
35+
GraphicsDeviceManager.DeviceCreationFlags = DeviceCreationFlags.Debug;
36+
GraphicsDeviceManager.PreferredGraphicsProfile = [GraphicsProfile.Level_11_0];
37+
}
38+
39+
protected override async Task LoadContent()
40+
{
41+
await base.LoadContent();
42+
43+
renderTargetRgba = Texture.New2D(GraphicsDevice, 256, 256, PixelFormat.R8G8B8A8_UNorm_SRgb, TextureFlags.ShaderResource | TextureFlags.RenderTarget).DisposeBy(this);
44+
renderTargetBgra = Texture.New2D(GraphicsDevice, 256, 256, PixelFormat.B8G8R8A8_UNorm_SRgb, TextureFlags.ShaderResource | TextureFlags.RenderTarget).DisposeBy(this);
45+
46+
var camera = new CameraComponent();
47+
var compositor = GraphicsCompositorHelper.CreateDefault(enablePostEffects: false, camera: camera, graphicsProfile: GraphicsProfile.Level_9_1);
48+
var forwardRgba = (ForwardRenderer)compositor.SingleView;
49+
50+
// A second renderer sharing the same render stages, so the same object is drawn into both
51+
// targets through one stage (and thus one cached pipeline state per (object, stage)).
52+
var forwardBgra = new ForwardRenderer
53+
{
54+
Clear = { Color = Color.Black },
55+
OpaqueRenderStage = forwardRgba.OpaqueRenderStage,
56+
TransparentRenderStage = forwardRgba.TransparentRenderStage,
57+
};
58+
59+
var cameraSlot = compositor.Cameras[0];
60+
compositor.Game = new SceneRendererCollection
61+
{
62+
new SceneCameraRenderer { Camera = cameraSlot, Child = new RenderTextureSceneRenderer { RenderTexture = renderTargetRgba, Child = forwardRgba } },
63+
new SceneCameraRenderer { Camera = cameraSlot, Child = new RenderTextureSceneRenderer { RenderTexture = renderTargetBgra, Child = forwardBgra } },
64+
};
65+
66+
var scene = new Scene();
67+
68+
var cameraEntity = new Entity { camera };
69+
cameraEntity.Transform.Position = new Vector3(0, 0, 3);
70+
scene.Entities.Add(cameraEntity);
71+
72+
// Unlit emissive material: the cube only needs to draw (so the pipeline states and
73+
// framebuffers are created for both outputs); lighting is irrelevant to the repro.
74+
var model = new CubeProceduralModel().Generate(Services);
75+
model.Materials.Add(Material.New(GraphicsDevice, new MaterialDescriptor
76+
{
77+
Attributes =
78+
{
79+
Emissive = new MaterialEmissiveMapFeature(new ComputeColor(Color.White)),
80+
},
81+
}));
82+
scene.Entities.Add(new Entity { new ModelComponent { Model = model } });
83+
84+
SceneSystem.SceneInstance = new SceneInstance(Services, scene);
85+
SceneSystem.GraphicsCompositor = compositor;
86+
}
87+
88+
[SkippableFact]
89+
public void RunSharedStageMultipleOutputs()
90+
{
91+
var game = new TestSharedStageMultipleOutputs();
92+
game.Script.AddTask(async () =>
93+
{
94+
game.ScreenShotAutomationEnabled = false;
95+
96+
// Render several frames so the shared effect compiles and is drawn into both targets.
97+
for (int i = 0; i < 10; i++)
98+
await game.Script.NextFrame();
99+
100+
game.Exit();
101+
});
102+
RunGameTest(game);
103+
}
104+
}
105+
}

0 commit comments

Comments
 (0)