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 System.Diagnostics;
6using osuTK.Graphics.ES30;
7using osu.Framework.Graphics.OpenGL;
8using osu.Framework.Graphics.OpenGL.Textures;
9using osu.Framework.Graphics.Textures;
10using osu.Framework.Platform;
11
12namespace osu.Framework.Graphics.Video
13{
14 internal unsafe class VideoTexture : TextureGLSingle
15 {
16 private int[] textureIds;
17
18 /// <summary>
19 /// Whether the latest frame data has been uploaded.
20 /// </summary>
21 public bool UploadComplete { get; private set; }
22
23 public VideoTexture(int width, int height, WrapMode wrapModeS = WrapMode.None, WrapMode wrapModeT = WrapMode.None)
24 : base(width, height, true, All.Linear, wrapModeS, wrapModeT)
25 {
26 }
27
28 private NativeMemoryTracker.NativeMemoryLease memoryLease;
29
30 internal override void SetData(ITextureUpload upload, WrapMode wrapModeS, WrapMode wrapModeT, Opacity? uploadOpacity)
31 {
32 if (uploadOpacity != null && uploadOpacity != Opacity.Opaque)
33 throw new InvalidOperationException("Video texture uploads must always be opaque");
34
35 UploadComplete = false;
36
37 // We do not support videos with transparency at this point,
38 // so the upload's opacity as well as the texture's opacity
39 // is always opaque.
40 base.SetData(upload, wrapModeS, wrapModeT, Opacity = Opacity.Opaque);
41 }
42
43 public override int TextureId => textureIds?[0] ?? 0;
44
45 private int textureSize;
46
47 public override int GetByteSize() => textureSize;
48
49 internal override bool Bind(TextureUnit unit, WrapMode wrapModeS, WrapMode wrapModeT)
50 {
51 if (!Available)
52 throw new ObjectDisposedException(ToString(), "Can not bind a disposed texture.");
53
54 Upload();
55
56 if (textureIds == null)
57 return false;
58
59 bool anyBound = false;
60
61 for (int i = 0; i < textureIds.Length; i++)
62 anyBound |= GLWrapper.BindTexture(textureIds[i], unit + i, wrapModeS, wrapModeT);
63
64 if (anyBound)
65 BindCount++;
66
67 return true;
68 }
69
70 protected override void DoUpload(ITextureUpload upload, IntPtr dataPointer)
71 {
72 if (!(upload is VideoTextureUpload videoUpload))
73 return;
74
75 // Do we need to generate a new texture?
76 if (textureIds == null)
77 {
78 Debug.Assert(memoryLease == null);
79 memoryLease = NativeMemoryTracker.AddMemory(this, Width * Height * 3 / 2);
80
81 textureIds = new int[3];
82 GL.GenTextures(textureIds.Length, textureIds);
83
84 for (int i = 0; i < textureIds.Length; i++)
85 {
86 GLWrapper.BindTexture(textureIds[i]);
87
88 if (i == 0)
89 {
90 int width = videoUpload.Frame->width;
91 int height = videoUpload.Frame->height;
92
93 GL.TexImage2D(TextureTarget2d.Texture2D, 0, TextureComponentCount.R8, width, height, 0, PixelFormat.Red, PixelType.UnsignedByte, IntPtr.Zero);
94
95 textureSize += width * height;
96 }
97 else
98 {
99 int width = (videoUpload.Frame->width + 1) / 2;
100 int height = (videoUpload.Frame->height + 1) / 2;
101
102 GL.TexImage2D(TextureTarget2d.Texture2D, 0, TextureComponentCount.R8, width, height, 0, PixelFormat.Red, PixelType.UnsignedByte, IntPtr.Zero);
103
104 textureSize += width * height;
105 }
106
107 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
108 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
109
110 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
111 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
112 }
113 }
114
115 for (int i = 0; i < textureIds.Length; i++)
116 {
117 GLWrapper.BindTexture(textureIds[i]);
118
119 GL.PixelStore(PixelStoreParameter.UnpackRowLength, videoUpload.Frame->linesize[(uint)i]);
120 GL.TexSubImage2D(TextureTarget2d.Texture2D, 0, 0, 0, videoUpload.Frame->width / (i > 0 ? 2 : 1), videoUpload.Frame->height / (i > 0 ? 2 : 1),
121 PixelFormat.Red, PixelType.UnsignedByte, (IntPtr)videoUpload.Frame->data[(uint)i]);
122 }
123
124 GL.PixelStore(PixelStoreParameter.UnpackRowLength, 0);
125
126 UploadComplete = true;
127 }
128
129 #region Disposal
130
131 protected override void Dispose(bool isDisposing)
132 {
133 base.Dispose(isDisposing);
134
135 memoryLease?.Dispose();
136
137 GLWrapper.ScheduleDisposal(unload);
138 }
139
140 private void unload()
141 {
142 if (textureIds == null)
143 return;
144
145 for (int i = 0; i < textureIds.Length; i++)
146 {
147 if (textureIds[i] >= 0)
148 GL.DeleteTextures(1, new[] { textureIds[i] });
149 }
150 }
151
152 #endregion
153 }
154}