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.Collections.Generic;
5using JetBrains.Annotations;
6using osu.Framework.Allocation;
7using osu.Framework.Bindables;
8using osu.Framework.Graphics;
9using osu.Framework.Graphics.Containers;
10
11namespace osu.Framework.Testing
12{
13 internal class DrawFrameRecordingContainer : Container
14 {
15 private readonly Bindable<RecordState> recordState = new Bindable<RecordState>();
16 private readonly BindableInt currentFrame = new BindableInt();
17 private readonly List<DrawNode> recordedFrames = new List<DrawNode>();
18
19 protected override Container<Drawable> Content => content;
20
21 private readonly Container content;
22
23 public DrawFrameRecordingContainer()
24 {
25 InternalChildren = new Drawable[]
26 {
27 new InputCapturingDrawable { RelativeSizeAxes = Axes.Both },
28 content = new Container { RelativeSizeAxes = Axes.Both }
29 };
30 }
31
32 [BackgroundDependencyLoader(true)]
33 private void load([CanBeNull] TestBrowser browser)
34 {
35 if (browser != null)
36 {
37 recordState.BindTo(browser.RecordState);
38 currentFrame.BindTo(browser.CurrentFrame);
39 }
40 }
41
42 protected override bool CanBeFlattened => false;
43
44 internal override DrawNode GenerateDrawNodeSubtree(ulong frame, int treeIndex, bool forceNewDrawNode)
45 {
46 switch (recordState.Value)
47 {
48 default:
49 case RecordState.Normal:
50 foreach (var drawNode in recordedFrames)
51 disposeRecursively(drawNode);
52
53 recordedFrames.Clear();
54
55 currentFrame.Value = currentFrame.MaxValue = 0;
56
57 return base.GenerateDrawNodeSubtree(frame, treeIndex, forceNewDrawNode);
58
59 case RecordState.Recording:
60 var node = base.GenerateDrawNodeSubtree(frame, treeIndex, true);
61
62 referenceRecursively(node);
63
64 recordedFrames.Add(node);
65 currentFrame.Value = currentFrame.MaxValue = recordedFrames.Count - 1;
66
67 return node;
68
69 case RecordState.Stopped:
70 return recordedFrames[currentFrame.Value];
71 }
72 }
73
74 private void referenceRecursively(DrawNode drawNode)
75 {
76 drawNode.Reference();
77
78 if (!(drawNode is ICompositeDrawNode composite))
79 return;
80
81 foreach (var child in composite.Children)
82 referenceRecursively(child);
83 }
84
85 private void disposeRecursively(DrawNode drawNode)
86 {
87 drawNode.Dispose();
88
89 if (!(drawNode is ICompositeDrawNode composite))
90 return;
91
92 foreach (var child in composite.Children)
93 disposeRecursively(child);
94 }
95
96 // An empty drawable which captures DrawVisualiser input in this container
97 private class InputCapturingDrawable : Drawable
98 {
99 // Required for the DrawVisualiser to not treat this Drawable as an overlay input receptor
100 // ReSharper disable once RedundantOverriddenMember
101 protected override DrawNode CreateDrawNode() => base.CreateDrawNode();
102 }
103 }
104}