A game framework written with osu! in mind.
at master 154 lines 5.6 kB view raw
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}