A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using osu.Framework.Allocation;
6using osu.Framework.Configuration;
7using osu.Framework.Graphics;
8using osu.Framework.Graphics.Containers;
9using osu.Framework.Graphics.OpenGL;
10using osu.Framework.Graphics.OpenGL.Vertices;
11using osu.Framework.Graphics.Shapes;
12using osu.Framework.Graphics.Sprites;
13using osu.Framework.Utils;
14using osu.Framework.Testing;
15using osuTK;
16using osuTK.Graphics;
17using osuTK.Graphics.ES30;
18using osu.Framework.Graphics.Textures;
19using osu.Framework.Graphics.Primitives;
20using osu.Framework.Graphics.OpenGL.Textures;
21
22namespace osu.Framework.Tests.Visual.Containers
23{
24 public class TestSceneFrontToBack : GridTestScene
25 {
26 private SpriteText labelDrawables;
27 private QueryingCompositeDrawableDrawNode drawNode;
28 private SpriteText labelFrag;
29 private SpriteText labelFrag2;
30 private float currentScale = 1;
31
32 private const int cell_count = 4;
33
34 protected override DrawNode CreateDrawNode() => drawNode = new QueryingCompositeDrawableDrawNode(this);
35
36 public TestSceneFrontToBack()
37 : base(cell_count / 2, cell_count / 2)
38 {
39 }
40
41 [BackgroundDependencyLoader]
42 private void load(FrameworkDebugConfigManager debugConfig, TextureStore store)
43 {
44 var texture = store.Get(@"sample-texture");
45 var repeatedTexture = store.Get(@"sample-texture", WrapMode.Repeat, WrapMode.Repeat);
46 var edgeClampedTexture = store.Get(@"sample-texture", WrapMode.ClampToEdge, WrapMode.ClampToEdge);
47 var borderClampedTexture = store.Get(@"sample-texture", WrapMode.ClampToBorder, WrapMode.ClampToBorder);
48
49 AddStep("add sprites", () => addMoreDrawables(texture, new RectangleF(0, 0, 1, 1)));
50 AddStep("add sprites with shrink", () => addMoreDrawables(texture, new RectangleF(0.25f, 0.25f, 0.5f, 0.5f)));
51 AddStep("add sprites with repeat", () => addMoreDrawables(repeatedTexture, new RectangleF(0.25f, 0.25f, 0.5f, 0.5f)));
52 AddStep("add sprites with edge clamp", () => addMoreDrawables(edgeClampedTexture, new RectangleF(0.25f, 0.25f, 0.5f, 0.5f)));
53 AddStep("add sprites with border clamp", () => addMoreDrawables(borderClampedTexture, new RectangleF(0.25f, 0.25f, 0.5f, 0.5f)));
54 AddStep("add boxes", () => addMoreDrawables(Texture.WhitePixel, new RectangleF(0, 0, 1, 1)));
55 AddToggleStep("disable front to back", val =>
56 {
57 debugConfig.SetValue(DebugSetting.BypassFrontToBackPass, val);
58 Invalidate(Invalidation.DrawNode); // reset counts
59 });
60
61 Add(new Container
62 {
63 AutoSizeAxes = Axes.Both,
64 Depth = float.NegativeInfinity,
65 Anchor = Anchor.Centre,
66 Origin = Anchor.Centre,
67
68 Children = new Drawable[]
69 {
70 new Box
71 {
72 Colour = Color4.Black,
73 RelativeSizeAxes = Axes.Both,
74 Alpha = 0.8f,
75 },
76 new FillFlowContainer
77 {
78 AutoSizeAxes = Axes.Both,
79 Padding = new MarginPadding(10),
80 Direction = FillDirection.Vertical,
81 Children = new Drawable[]
82 {
83 labelDrawables = new SpriteText { Font = FrameworkFont.Condensed },
84 labelFrag = new SpriteText { Font = FrameworkFont.Condensed },
85 labelFrag2 = new SpriteText { Font = FrameworkFont.Condensed },
86 }
87 },
88 }
89 });
90 }
91
92 protected override void Update()
93 {
94 base.Update();
95
96 if (drawNode != null)
97 {
98 labelDrawables.Text = $"boxes: {Cell(1).Children.Count * cell_count:N0}";
99 labelFrag.Text = $"samples ({nameof(DrawNode.Draw)}): {drawNode.DrawSamples:N0}";
100 labelFrag2.Text = $"samples ({nameof(DrawNode.DrawOpaqueInteriorSubTree)}): {drawNode.DrawOpaqueInteriorSubTreeSamples:N0}";
101 }
102 }
103
104 private void addMoreDrawables(Texture texture, RectangleF textureRect)
105 {
106 for (int i = 0; i < 100; i++)
107 {
108 Cell(i % cell_count).Add(new Sprite
109 {
110 Anchor = Anchor.Centre,
111 Origin = Anchor.Centre,
112 Colour = new Color4(RNG.NextSingle(1), RNG.NextSingle(1), RNG.NextSingle(1), 1),
113 RelativeSizeAxes = Axes.Both,
114 Scale = new Vector2(currentScale),
115 Texture = texture,
116 TextureRectangle = textureRect,
117 });
118
119 currentScale -= 0.001f;
120 if (currentScale < 0)
121 currentScale = 1;
122 }
123 }
124
125 private class QueryingCompositeDrawableDrawNode : CompositeDrawableDrawNode
126 {
127 private int queryObject = -1;
128
129 public int DrawSamples { get; private set; }
130 public int DrawOpaqueInteriorSubTreeSamples { get; private set; }
131
132 public QueryingCompositeDrawableDrawNode(CompositeDrawable source)
133 : base(source)
134 {
135 }
136
137 internal override void DrawOpaqueInteriorSubTree(DepthValue depthValue, Action<TexturedVertex2D> vertexAction)
138 {
139 startQuery();
140 base.DrawOpaqueInteriorSubTree(depthValue, vertexAction);
141 DrawOpaqueInteriorSubTreeSamples = endQuery();
142 }
143
144 public override void ApplyState()
145 {
146 DrawSamples = 0;
147 DrawOpaqueInteriorSubTreeSamples = 0;
148 base.ApplyState();
149 }
150
151 public override void Draw(Action<TexturedVertex2D> vertexAction)
152 {
153 startQuery();
154 base.Draw(vertexAction);
155 DrawSamples = endQuery();
156 }
157
158 private int endQuery()
159 {
160 GL.EndQuery(QueryTarget.SamplesPassed);
161 GL.GetQueryObject(queryObject, GetQueryObjectParam.QueryResult, out int result);
162
163 return result;
164 }
165
166 private void startQuery()
167 {
168 if (queryObject == -1)
169 queryObject = GL.GenQuery();
170
171 GL.BeginQuery(QueryTarget.SamplesPassed, queryObject);
172 }
173 }
174 }
175}