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.Graphics.OpenGL;
6using osu.Framework.Graphics.OpenGL.Vertices;
7using osu.Framework.Graphics.Primitives;
8
9namespace osu.Framework.Graphics
10{
11 public abstract partial class Drawable
12 {
13 private class ProxyDrawable : Drawable
14 {
15 private readonly ulong[] drawNodeValidationIds = new ulong[GLWrapper.MAX_DRAW_NODES];
16 private readonly DrawNode[] originalDrawNodes;
17
18 internal ProxyDrawable(Drawable original)
19 {
20 Original = original;
21 originalDrawNodes = (original as ProxyDrawable)?.originalDrawNodes ?? original.drawNodes;
22
23 original.LifetimeChanged += _ => LifetimeChanged?.Invoke(this);
24 }
25
26 internal override void ValidateProxyDrawNode(int treeIndex, ulong frame)
27 {
28 if (HasProxy)
29 {
30 // Proxy of a proxy - forward the next proxy
31 base.ValidateProxyDrawNode(treeIndex, frame);
32 return;
33 }
34
35 drawNodeValidationIds[treeIndex] = frame;
36 }
37
38 protected override DrawNode CreateDrawNode() => new ProxyDrawNode(this);
39
40 internal override DrawNode GenerateDrawNodeSubtree(ulong frame, int treeIndex, bool forceNewDrawNode)
41 {
42 var node = (ProxyDrawNode)base.GenerateDrawNodeSubtree(frame, treeIndex, forceNewDrawNode);
43
44 node.DrawNodeIndex = treeIndex;
45 node.FrameCount = frame;
46
47 return node;
48 }
49
50 internal sealed override Drawable Original { get; }
51
52 public override bool RemoveWhenNotAlive => Original.RemoveWhenNotAlive;
53
54 protected internal override bool ShouldBeAlive => Original.ShouldBeAlive;
55
56 public override double LifetimeStart => Original.LifetimeStart;
57
58 public override double LifetimeEnd => Original.LifetimeEnd;
59
60 // We do not want to receive updates. That is the business of the original drawable.
61 public override bool IsPresent => false;
62
63 public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds)
64 {
65 if (Original.IsDisposed)
66 return false;
67
68 return Original.UpdateSubTreeMasking(this, maskingBounds);
69 }
70
71 private class ProxyDrawNode : DrawNode
72 {
73 /// <summary>
74 /// The index of the original draw node to draw.
75 /// </summary>
76 public int DrawNodeIndex;
77
78 /// <summary>
79 /// The current draw frame index.
80 /// If this differs from the frame index of the original draw node, the original drawable will have not been drawn this frame.
81 /// </summary>
82 public ulong FrameCount;
83
84 protected new ProxyDrawable Source => (ProxyDrawable)base.Source;
85
86 public ProxyDrawNode(ProxyDrawable proxyDrawable)
87 : base(proxyDrawable)
88 {
89 }
90
91 internal override void DrawOpaqueInteriorSubTree(DepthValue depthValue, Action<TexturedVertex2D> vertexAction)
92 => getCurrentFrameSource()?.DrawOpaqueInteriorSubTree(depthValue, vertexAction);
93
94 public override void Draw(Action<TexturedVertex2D> vertexAction)
95 => getCurrentFrameSource()?.Draw(vertexAction);
96
97 protected internal override bool CanDrawOpaqueInterior => getCurrentFrameSource()?.CanDrawOpaqueInterior ?? false;
98
99 private DrawNode getCurrentFrameSource()
100 {
101 var target = Source.originalDrawNodes[DrawNodeIndex];
102
103 if (target == null)
104 return null;
105
106 if (Source.drawNodeValidationIds[DrawNodeIndex] != FrameCount)
107 return null;
108
109 if (target.IsDisposed)
110 return null;
111
112 return target;
113 }
114 }
115 }
116 }
117}