Advertisement
Ameisen

Untitled

May 10th, 2022
1,285
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 5.33 KB | None | 0 0
  1.     private static class GLExt {
  2.         private static class Generic<T> where T : class? {
  3.             internal delegate T? LoadFunctionDelegate(string function, bool throwIfNotFound = false);
  4.  
  5.             internal static readonly LoadFunctionDelegate LoadFunction =
  6.                 typeof(MonoGame.OpenGL.GL).GetStaticMethod("LoadFunction")?.MakeGenericMethod(typeof(T)).CreateDelegate<LoadFunctionDelegate>() ??
  7.                 ((_, _) => null);
  8.         }
  9.  
  10.         [System.Security.SuppressUnmanagedCodeSecurity]
  11.         [UnmanagedFunctionPointer(CallingConvention.Winapi)]
  12.         [MonoNativeFunctionWrapper]
  13.         internal delegate void TexStorage2DDelegate(
  14.             TextureTarget target,
  15.             int levels,
  16.             PixelInternalFormat internalFormat,
  17.             int width,
  18.             int height
  19.         );
  20.  
  21.         internal static readonly TexStorage2DDelegate? TexStorage2D = Generic<TexStorage2DDelegate>.LoadFunction("glTexStorage2D");
  22.     }
  23.  
  24.     private int ConstructLevel(
  25.         ReadOnlyPinnedSpan<byte> data,
  26.         Vector2I size,
  27.         int level,
  28.         int levelSize,
  29.         bool useSubImage
  30.     ) {
  31.         if (glFormat == PixelFormat.CompressedTextureFormats) {
  32.             if (useSubImage) {
  33.                 MonoGame.OpenGL.GL.CompressedTexSubImage2D(
  34.                     TextureTarget.Texture2D,
  35.                     level,
  36.                     0,
  37.                     0,
  38.                     size.X,
  39.                     size.Y,
  40.                     glInternalFormat,
  41.                     levelSize,
  42.                     data[..levelSize].GetIntPointer()
  43.                 );
  44.             }
  45.             else {
  46.                 MonoGame.OpenGL.GL.CompressedTexImage2D(
  47.                     TextureTarget.Texture2D,
  48.                     level,
  49.                     glInternalFormat,
  50.                     size.X,
  51.                     size.Y,
  52.                     0,
  53.                     levelSize,
  54.                     data[..levelSize].GetIntPointer()
  55.                 );
  56.             }
  57.         }
  58.         else {
  59.             if (useSubImage) {
  60.                 MonoGame.OpenGL.GL.TexSubImage2D(
  61.                     TextureTarget.Texture2D,
  62.                     level,
  63.                     0,
  64.                     0,
  65.                     size.X,
  66.                     size.Y,
  67.                     glFormat,
  68.                     glType,
  69.                     data[..levelSize].GetIntPointer()
  70.                 );
  71.             }
  72.             else {
  73.                 MonoGame.OpenGL.GL.TexImage2D(
  74.                     TextureTarget.Texture2D,
  75.                     level,
  76.                     glInternalFormat,
  77.                     size.X,
  78.                     size.Y,
  79.                     0,
  80.                     glFormat,
  81.                     glType,
  82.                     data[..levelSize].GetIntPointer()
  83.                 );
  84.             }
  85.         }
  86.  
  87.         GraphicsExtensions.CheckGLError();
  88.  
  89.         return levelSize;
  90.     }
  91.  
  92.     private void Construct(
  93.         ReadOnlyPinnedSpan<byte>.FixedSpan dataIn,
  94.         Vector2I size,
  95.         bool mipmap,
  96.         SurfaceFormat format,
  97.         SurfaceType type,
  98.         bool shared
  99.     ) {
  100.         glTarget = TextureTarget.Texture2D;
  101.         format.GetGLFormat(GraphicsDevice, out glInternalFormat, out glFormat, out glType);
  102.  
  103.         // Use glTexStorage2D if it's available.
  104.         // Presently, since we are not yet overriding 'SetData' to use glMeowTexSubImage2D,
  105.         // only use it if we are populating the texture now
  106.         bool useSubImage = !dataIn.IsEmpty && GLExt.TexStorage2D is not null;
  107.  
  108.         // Calculate the number of texture levels
  109.         int levels = 1;
  110.         if (useSubImage) {
  111.             if (mipmap) {
  112.                 var tempDimensions = size;
  113.                 while (tempDimensions != (1, 1)) {
  114.                     tempDimensions >>= 1;
  115.                     tempDimensions = tempDimensions.Min(1);
  116.                     ++levels;
  117.                 }
  118.             }
  119.         }
  120.  
  121.         // Mostly taken from MonoGame, but completely refactored.
  122.         // Returns the size given dimensions, adjusted/aligned for block formats.
  123.         Func<Vector2I, int> getLevelSize = format switch {
  124.             // PVRTC has explicit calculations for imageSize
  125.             // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt
  126.             SurfaceFormat.RgbPvrtc2Bpp or
  127.             SurfaceFormat.RgbaPvrtc2Bpp =>
  128.                 static size => {
  129.                     var maxDimensions = size.Max((16, 8));
  130.                     return ((maxDimensions.X * maxDimensions.Y) << 1 + 7) >> 3;
  131.                 },
  132.  
  133.             SurfaceFormat.RgbPvrtc4Bpp or
  134.             SurfaceFormat.RgbaPvrtc4Bpp =>
  135.                 static size => {
  136.                     var maxDimensions = size.Max((8, 8));
  137.                     return ((maxDimensions.X * maxDimensions.Y) << 2 + 7) >> 3;
  138.                 },
  139.  
  140.             _ when glFormat == PixelFormat.CompressedTextureFormats =>
  141.                 size => {
  142.                     int blockSize = format.GetSize();
  143.                     var blockDimensions = format.BlockEdge();
  144.  
  145.                     var blocks = (size + (blockDimensions - 1)) / blockDimensions;
  146.                     return blocks.X * blocks.Y * blockSize;
  147.                 },
  148.  
  149.             _ =>
  150.                 size => (int)format.SizeBytes(size.Area)
  151.         };
  152.  
  153.         Threading.BlockOnUIThread(() => {
  154.             ReadOnlyPinnedSpan<byte> data = default;
  155.  
  156.             if (!dataIn.IsEmpty) {
  157.                 data = dataIn.AsSpan;
  158.             }
  159.  
  160.             GenerateGLTextureIfRequired();
  161.  
  162.             if (!data.IsEmpty) {
  163.                 MonoGame.OpenGL.GL.PixelStore(PixelStoreParameter.UnpackAlignment, Math.Min(Format.GetSize(), 8));
  164.             }
  165.  
  166.             if (useSubImage) {
  167.                 // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexStorage2D.xhtml
  168.                 // Using glTexStorage and glMeowTexSubImage2D to populate textures is more efficient,
  169.                 // as the way that MonoGame normally does it requires the texture to be kept largely in flux,
  170.                 // and also requires it to be discarded a significant number of times.
  171.                 GLExt.TexStorage2D!(
  172.                     TextureTarget.Texture2D,
  173.                     levels,
  174.                     glInternalFormat,
  175.                     size.Width,
  176.                     size.Height
  177.                 );
  178.             }
  179.  
  180.             var levelDimensions = size;
  181.             int level = 0;
  182.             int currentOffset = 0;
  183.  
  184.             // Loop over every level and populate it, starting from the largest.
  185.             while (true) {
  186.                 currentOffset += ConstructLevel(
  187.                     data: data[currentOffset..],
  188.                     size: levelDimensions,
  189.                     level: level++,
  190.                     levelSize: getLevelSize(levelDimensions),
  191.                     useSubImage: useSubImage
  192.                 );
  193.  
  194.                 if (levelDimensions == (1, 1) || !mipmap)
  195.                     break;
  196.  
  197.                 levelDimensions >>= 1;
  198.                 levelDimensions = levelDimensions.Min(1);
  199.                 ++level;
  200.             }
  201.         });
  202.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement