Exemplo n.º 1
0
        public static unsafe Texture2D CreateFromStreamUnsynchronized(RenderContext renderContext, Stream stream)
        {
            using var image = Image.Load <Rgba32>(stream);
            image.Mutate(x => x.Flip(FlipMode.Vertical));

            var texture = new Texture2D(renderContext)
            {
                Width  = image.Width,
                Height = image.Height,
            };

            var span = image.GetPixelSpan();
            int size = span.Length * sizeof(Rgba32);

            fixed(void *data = &MemoryMarshal.GetReference(span))
            {
                var start = new IntPtr(data);

                using var pixelBuffer = PixelBuffer.Create <Rgba32>(renderContext, size, true, true);

                GLHandle prevPixelBuffer = (GLHandle)GL.GetInteger(GetPName.PixelUnpackBufferBinding);

                GL.BindBuffer(BufferTarget.PixelUnpackBuffer, pixelBuffer.Handle);

                // When we load assets asynchronously, using SubData to set the data will cause the main thread
                // to wait for the GL call to complete, so we use a PBO and fill it with a mapped memory range to
                // prevent OpenGL synchronization from causing frame drops on the main thread.

                IntPtr pixelPointer = GL.MapBufferRange(BufferTarget.PixelUnpackBuffer,
                                                        IntPtr.Zero,
                                                        size,
                                                        BufferAccessMask.MapWriteBit
                                                        | BufferAccessMask.MapUnsynchronizedBit
                                                        | BufferAccessMask.MapInvalidateRangeBit);

                if (pixelPointer == IntPtr.Zero)
                {
                    throw new GraphicsContextException("Could not map PixelUnbackBuffer range.");
                }

                // We send the data to mapped memory in multiple batches instead of one large one for the same
                // reason.

                int       remaining = size;
                const int step      = 2048;

                while (remaining > 0)
                {
                    int   currentStep = Math.Min(remaining, step);
                    int   point       = size - remaining;
                    void *dest        = (void *)IntPtr.Add(pixelPointer, point);
                    void *src         = (void *)IntPtr.Add(start, point);
                    Unsafe.CopyBlock(dest, src, (uint)currentStep);

                    remaining -= step;
                }

                GL.UnmapBuffer(BufferTarget.PixelUnpackBuffer);

                if (GLInfo.HasDirectStateAccess)
                {
                    GL.TextureStorage2D(texture.Handle,
                                        1,
                                        SizedInternalFormat.Rgba8,
                                        texture.Width,
                                        texture.Height);

                    GL.TextureSubImage2D(texture.Handle,
                                         0,
                                         0,
                                         0,
                                         texture.Width,
                                         texture.Height,
                                         PixelFormat.Rgba,
                                         PixelType.UnsignedByte,
                                         IntPtr.Zero);

                    GL.GenerateTextureMipmap(texture.Handle);
                }
                else
                {
                    var previousTexture = renderContext.TextureUnits[0].Texture2D;
                    renderContext.BindTexture(0, texture);

                    GL.TexImage2D(TextureTarget.Texture2D,
                                  0,
                                  PixelInternalFormat.Rgba,
                                  texture.Width,
                                  texture.Height,
                                  0,
                                  PixelFormat.Rgba,
                                  PixelType.UnsignedByte,
                                  IntPtr.Zero);

                    GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);

                    renderContext.BindTexture(0, previousTexture);
                }

                GL.BindBuffer(BufferTarget.PixelUnpackBuffer, prevPixelBuffer);
            }

            texture.SetDefaultTextureParameters(renderContext);

            // TODO: Look into if this is necessary (?)
            GL.Finish();

            return(texture);
        }