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.Batches;
6using osu.Framework.Graphics.Primitives;
7using osuTK.Graphics.ES30;
8using osuTK;
9using osu.Framework.Graphics.Colour;
10using osu.Framework.Graphics.OpenGL.Vertices;
11using osu.Framework.Graphics.Textures;
12
13namespace osu.Framework.Graphics.OpenGL.Textures
14{
15 public abstract class TextureGL : IDisposable
16 {
17 /// <summary>
18 /// The texture wrap mode in horizontal direction.
19 /// </summary>
20 public readonly WrapMode WrapModeS;
21
22 /// <summary>
23 /// The texture wrap mode in vertical direction.
24 /// </summary>
25 public readonly WrapMode WrapModeT;
26
27 protected TextureGL(WrapMode wrapModeS = WrapMode.None, WrapMode wrapModeT = WrapMode.None)
28 {
29 WrapModeS = wrapModeS;
30 WrapModeT = wrapModeT;
31 }
32
33 #region Disposal
34
35 internal virtual bool IsQueuedForUpload { get; set; }
36
37 /// <summary>
38 /// By default, texture uploads are queued for upload at the beginning of each frame, allowing loading them ahead of time.
39 /// When this is true, this will be bypassed and textures will only be uploaded on use. Should be set for every-frame texture uploads
40 /// to avoid overloading the global queue.
41 /// </summary>
42 public bool BypassTextureUploadQueueing;
43
44 /// <summary>
45 /// Whether this <see cref="TextureGL"/> can used for drawing.
46 /// </summary>
47 public bool Available { get; private set; } = true;
48
49 private bool isDisposed;
50
51 protected virtual void Dispose(bool isDisposing) => GLWrapper.ScheduleDisposal(() => Available = false);
52
53 public void Dispose()
54 {
55 if (isDisposed)
56 return;
57
58 isDisposed = true;
59
60 Dispose(true);
61 GC.SuppressFinalize(this);
62 }
63
64 #endregion
65
66 public abstract TextureGL Native { get; }
67
68 public abstract bool Loaded { get; }
69
70 public Opacity Opacity { get; protected set; } = Opacity.Mixed;
71
72 public abstract int TextureId { get; }
73
74 public abstract int Height { get; set; }
75
76 public abstract int Width { get; set; }
77
78 public abstract RectangleI Bounds { get; }
79
80 public Vector2 Size => new Vector2(Width, Height);
81
82 public abstract RectangleF GetTextureRect(RectangleF? textureRect);
83
84 /// <summary>
85 /// Draws a triangle to the screen.
86 /// </summary>
87 /// <param name="vertexTriangle">The triangle to draw.</param>
88 /// <param name="drawColour">The vertex colour.</param>
89 /// <param name="textureRect">The texture rectangle.</param>
90 /// <param name="vertexAction">An action that adds vertices to a <see cref="VertexBatch{T}"/>.</param>
91 /// <param name="inflationPercentage">The percentage amount that <paramref name="textureRect"/> should be inflated.</param>
92 /// <param name="textureCoords">The texture coordinates of the triangle's vertices (translated from the corresponding quad's rectangle).</param>
93 internal abstract void DrawTriangle(Triangle vertexTriangle, ColourInfo drawColour, RectangleF? textureRect = null, Action<TexturedVertex2D> vertexAction = null,
94 Vector2? inflationPercentage = null, RectangleF? textureCoords = null);
95
96 /// <summary>
97 /// Draws a quad to the screen.
98 /// </summary>
99 /// <param name="vertexQuad">The quad to draw.</param>
100 /// <param name="drawColour">The vertex colour.</param>
101 /// <param name="textureRect">The texture rectangle.</param>
102 /// <param name="vertexAction">An action that adds vertices to a <see cref="VertexBatch{T}"/>.</param>
103 /// <param name="inflationPercentage">The percentage amount that <paramref name="textureRect"/> should be inflated.</param>
104 /// <param name="blendRangeOverride">The range over which the edges of the <paramref name="textureRect"/> should be blended.</param>
105 /// <param name="textureCoords">The texture coordinates of the quad's vertices.</param>
106 internal abstract void DrawQuad(Quad vertexQuad, ColourInfo drawColour, RectangleF? textureRect = null, Action<TexturedVertex2D> vertexAction = null, Vector2? inflationPercentage = null,
107 Vector2? blendRangeOverride = null, RectangleF? textureCoords = null);
108
109 /// <summary>
110 /// Bind as active texture.
111 /// </summary>
112 /// <param name="unit">The texture unit to bind to. Defaults to Texture0.</param>
113 /// <returns>True if bind was successful.</returns>
114 public bool Bind(TextureUnit unit = TextureUnit.Texture0) => Bind(unit, WrapModeS, WrapModeT);
115
116 /// <summary>
117 /// Bind as active texture.
118 /// </summary>
119 /// <param name="unit">The texture unit to bind to.</param>
120 /// <param name="wrapModeS">The texture wrap mode in horizontal direction.</param>
121 /// <param name="wrapModeT">The texture wrap mode in vertical direction.</param>
122 /// <returns>True if bind was successful.</returns>
123 internal abstract bool Bind(TextureUnit unit, WrapMode wrapModeS, WrapMode wrapModeT);
124
125 /// <summary>
126 /// Uploads pending texture data to the GPU if it exists.
127 /// </summary>
128 /// <returns>Whether pending data existed and an upload has been performed.</returns>
129 internal abstract bool Upload();
130
131 /// <summary>
132 /// Flush any unprocessed uploads without actually uploading.
133 /// </summary>
134 internal abstract void FlushUploads();
135
136 /// <summary>
137 /// Sets the pixel data of this <see cref="TextureGL"/>.
138 /// </summary>
139 /// <param name="upload">The <see cref="ITextureUpload"/> containing the data.</param>
140 public void SetData(ITextureUpload upload) => SetData(upload, WrapModeS, WrapModeT, null);
141
142 /// <summary>
143 /// Sets the pixel data of this <see cref="TextureGLAtlas"/>.
144 /// </summary>
145 /// <param name="upload">The <see cref="ITextureUpload"/> containing the data.</param>
146 /// <param name="wrapModeS">The texture wrap mode in horizontal direction.</param>
147 /// <param name="wrapModeT">The texture wrap mode in vertical direction.</param>
148 /// <param name="uploadOpacity">Whether the upload is opaque, transparent, or a mix of both..</param>
149 internal abstract void SetData(ITextureUpload upload, WrapMode wrapModeS, WrapMode wrapModeT, Opacity? uploadOpacity);
150
151 protected static Opacity ComputeOpacity(ITextureUpload upload)
152 {
153 // TODO: Investigate performance issues and revert functionality once we are sure there is no overhead.
154 // see https://github.com/ppy/osu/issues/9307
155 return Opacity.Mixed;
156
157 // if (upload.Data.Length == 0)
158 // return Opacity.Transparent;
159 //
160 // bool isTransparent = true;
161 // bool isOpaque = true;
162 //
163 // for (int i = 0; i < upload.Data.Length; ++i)
164 // {
165 // isTransparent &= upload.Data[i].A == 0;
166 // isOpaque &= upload.Data[i].A == 255;
167 //
168 // if (!isTransparent && !isOpaque)
169 // return Opacity.Mixed;
170 // }
171 //
172 // if (isTransparent)
173 // return Opacity.Transparent;
174 //
175 // return Opacity.Opaque;
176 }
177
178 protected void UpdateOpacity(ITextureUpload upload, ref Opacity? uploadOpacity)
179 {
180 // Compute opacity if it doesn't have a value yet
181 uploadOpacity ??= ComputeOpacity(upload);
182
183 // Update the texture's opacity depending on the upload's opacity.
184 // If the upload covers the entire bounds of the texture, it fully
185 // determines the texture's opacity. Otherwise, it can only turn
186 // the texture's opacity into a mixed state (if it disagrees with
187 // the texture's existing opacity).
188 if (upload.Bounds == Bounds && upload.Level == 0)
189 Opacity = uploadOpacity.Value;
190 else if (uploadOpacity.Value != Opacity)
191 Opacity = Opacity.Mixed;
192 }
193 }
194
195 public enum WrapMode
196 {
197 /// <summary>
198 /// No wrapping. If the texture is part of an atlas, this may read outside the texture's bounds.
199 /// </summary>
200 None = 0,
201
202 /// <summary>
203 /// Clamps to the edge of the texture, repeating the edge to fill the remainder of the draw area.
204 /// </summary>
205 ClampToEdge = 1,
206
207 /// <summary>
208 /// Clamps to a transparent-black border around the texture, repeating the border to fill the remainder of the draw area.
209 /// </summary>
210 ClampToBorder = 2,
211
212 /// <summary>
213 /// Repeats the texture.
214 /// </summary>
215 Repeat = 3,
216 }
217
218 public enum Opacity
219 {
220 Opaque,
221 Mixed,
222 Transparent,
223 }
224}