Advertisement
Ameisen

Untitled

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