private RenderTarget CreateRenderTarget(Vector2i size, RenderTargetFormatParameters format,
                                                TextureSampleParameters?sampleParameters = null, string name = null)
        {
            // Cache currently bound framebuffers
            // so if somebody creates a framebuffer while drawing it won't ruin everything.
            var boundDrawBuffer = GL.GetInteger(GetPName.DrawFramebufferBinding);
            var boundReadBuffer = GL.GetInteger(GetPName.ReadFramebufferBinding);

            // Generate FBO.
            var fbo = new GLHandle(GL.GenFramebuffer());

            // Bind color attachment to FBO.
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, fbo.Handle);

            ObjectLabelMaybe(ObjectLabelIdentifier.Framebuffer, fbo, name);

            var(width, height) = size;

            ClydeTexture textureObject;
            GLHandle     depthStencilBuffer = default;

            // Color attachment.
            {
                var texture = new GLHandle(GL.GenTexture());

                GL.BindTexture(TextureTarget.Texture2D, texture.Handle);

                ApplySampleParameters(sampleParameters);

                var internalFormat = format.ColorFormat switch
                {
                    RenderTargetColorFormat.Rgba8 => PixelInternalFormat.Rgba8,
                    RenderTargetColorFormat.Rgba16F => PixelInternalFormat.Rgba16f,
                    RenderTargetColorFormat.Rgba8Srgb => PixelInternalFormat.Srgb8Alpha8,
                    RenderTargetColorFormat.R11FG11FB10F => PixelInternalFormat.R11fG11fB10f,
                    RenderTargetColorFormat.R32F => PixelInternalFormat.R32f,
                    _ => throw new ArgumentOutOfRangeException(nameof(format.ColorFormat), format.ColorFormat, null)
                };

                GL.TexImage2D(TextureTarget.Texture2D, 0, internalFormat, width, height, 0, PixelFormat.Red,
                              PixelType.Byte, IntPtr.Zero);

                GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0,
                                      texture.Handle,
                                      0);

                textureObject = GenTexture(texture, size, name == null ? null : $"{name}-color");
            }

            // Depth/stencil buffers.
            if (format.HasDepthStencil)
            {
                depthStencilBuffer = new GLHandle(GL.GenRenderbuffer());
                GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle);

                ObjectLabelMaybe(ObjectLabelIdentifier.Renderbuffer, depthStencilBuffer,
                                 name == null ? null : $"{name}-depth-stencil");

                GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, width,
                                       height);

                GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment,
                                           RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle);
            }

            // This should always pass but OpenGL makes it easy to check for once so let's.
            var status = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer);

            DebugTools.Assert(status == FramebufferErrorCode.FramebufferComplete,
                              $"new framebuffer has bad status {status}");

            // Re-bind previous framebuffers.
            GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, boundDrawBuffer);
            GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, boundReadBuffer);

            var handle       = AllocRid();
            var renderTarget = new RenderTarget(size, textureObject, fbo, this, handle, depthStencilBuffer);

            _renderTargets.Add(handle, renderTarget);
            return(renderTarget);
        }
 IRenderTarget IClyde.CreateRenderTarget(Vector2i size, RenderTargetFormatParameters format,
                                         TextureSampleParameters?sampleParameters, string name)
 {
     return(CreateRenderTarget(size, format, sampleParameters, name));
 }
        private RenderTexture CreateRenderTarget(Vector2i size, RenderTargetFormatParameters format,
                                                 TextureSampleParameters?sampleParameters = null, string?name = null)
        {
            DebugTools.Assert(size.X != 0);
            DebugTools.Assert(size.Y != 0);

            // Cache currently bound framebuffers
            // so if somebody creates a framebuffer while drawing it won't ruin everything.
            // Note that this means _currentBoundRenderTarget goes temporarily out of sync here
            var boundDrawBuffer = GL.GetInteger(GetPName.DrawFramebufferBinding);
            var boundReadBuffer = 0;

            if (_hasGLReadFramebuffer)
            {
                boundReadBuffer = GL.GetInteger(GetPName.ReadFramebufferBinding);
            }

            // Generate FBO.
            var fbo = new GLHandle(GL.GenFramebuffer());

            // Bind color attachment to FBO.
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, fbo.Handle);
            CheckGlError();

            ObjectLabelMaybe(ObjectLabelIdentifier.Framebuffer, fbo, name);

            var(width, height) = size;

            ClydeTexture textureObject;
            GLHandle     depthStencilBuffer = default;

            var estPixSize = 0L;

            // Color attachment.
            {
                var texture = new GLHandle(GL.GenTexture());
                CheckGlError();

                GL.BindTexture(TextureTarget.Texture2D, texture.Handle);
                CheckGlError();

                ApplySampleParameters(sampleParameters);

                var colorFormat = format.ColorFormat;
                if ((!_hasGLSrgb) && (colorFormat == RTCF.Rgba8Srgb))
                {
                    // If SRGB is not supported, switch formats.
                    // The shaders will have to compensate.
                    // Note that a check is performed on the *original* format.
                    colorFormat = RTCF.Rgba8;
                }
                // This isn't good
                if (!_hasGLFloatFramebuffers)
                {
                    switch (colorFormat)
                    {
                    case RTCF.R32F:
                    case RTCF.RG32F:
                    case RTCF.R11FG11FB10F:
                    case RTCF.Rgba16F:
                        Logger.WarningS("clyde.ogl", "The framebuffer {0} [{1}] is trying to be floating-point when that's not supported. Forcing Rgba8.", name == null ? "[unnamed]" : name, size);
                        colorFormat = RTCF.Rgba8;
                        break;
                    }
                }

                // Make sure to specify the correct pixel type and formats even if we're not uploading any data.
                // Not doing this (just sending red/byte) is fine on desktop GL but illegal on ES.
                var(internalFormat, pixFormat, pixType) = colorFormat switch
                {
                    // using block comments to force formatters to not f**k this up.
                    RTCF.Rgba8 => /*       */ (PIF.Rgba8, /*       */ PF.Rgba, /**/ PT.UnsignedByte),
                    RTCF.Rgba16F => /*     */ (PIF.Rgba16f, /*     */ PF.Rgba, /**/ PT.Float),
                    RTCF.Rgba8Srgb => /*   */ (PIF.Srgb8Alpha8, /* */ PF.Rgba, /**/ PT.UnsignedByte),
                    RTCF.R11FG11FB10F => /**/ (PIF.R11fG11fB10f, /**/ PF.Rgb, /* */ PT.Float),
                    RTCF.R32F => /*        */ (PIF.R32f, /*        */ PF.Red, /* */ PT.Float),
                    RTCF.RG32F => /*       */ (PIF.Rg32f, /*       */ PF.Rg, /*  */ PT.Float),
                    RTCF.R8 => /*          */ (PIF.R8, /*          */ PF.Red, /* */ PT.UnsignedByte),
                    _ => throw new ArgumentOutOfRangeException(nameof(format.ColorFormat), format.ColorFormat, null)
                };

                estPixSize += EstPixelSize(internalFormat);

                GL.TexImage2D(TextureTarget.Texture2D, 0, internalFormat, width, height, 0, pixFormat,
                              pixType, IntPtr.Zero);
                CheckGlError();

                if (!_isGLES)
                {
                    GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0,
                                          texture.Handle,
                                          0);
                }
                else
                {
                    // OpenGL ES uses a different name, and has an odd added target argument
                    GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0,
                                            TextureTarget.Texture2D, texture.Handle, 0);
                }
                CheckGlError();

                // Check on original format is NOT a bug, this is so srgb emulation works
                textureObject = GenTexture(texture, size, format.ColorFormat == RTCF.Rgba8Srgb, name == null ? null : $"{name}-color");
            }

            // Depth/stencil buffers.
            if (format.HasDepthStencil)
            {
                depthStencilBuffer = new GLHandle(GL.GenRenderbuffer());
                GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle);
                CheckGlError();

                ObjectLabelMaybe(ObjectLabelIdentifier.Renderbuffer, depthStencilBuffer,
                                 name == null ? null : $"{name}-depth-stencil");

                GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, width,
                                       height);
                CheckGlError();

                estPixSize += 4;

                GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment,
                                           RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle);
                GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.StencilAttachment,
                                           RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle);
                CheckGlError();
            }

            // This should always pass but OpenGL makes it easy to check for once so let's.
            var status = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer);

            CheckGlError();
            DebugTools.Assert(status == FramebufferErrorCode.FramebufferComplete,
                              $"new framebuffer has bad status {status}");

            // Re-bind previous framebuffers (thus _currentBoundRenderTarget is back in sync)
            GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, boundDrawBuffer);
            CheckGlError();
            if (_hasGLReadFramebuffer)
            {
                GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, boundReadBuffer);
                CheckGlError();
            }

            var pressure = estPixSize * size.X * size.Y;

            var handle = AllocRid();
            var data   = new LoadedRenderTarget
            {
                IsWindow           = false,
                IsSrgb             = textureObject.IsSrgb,
                DepthStencilHandle = depthStencilBuffer,
                FramebufferHandle  = fbo,
                Size           = size,
                TextureHandle  = textureObject.TextureId,
                MemoryPressure = pressure
            };

            //GC.AddMemoryPressure(pressure);
            var renderTarget = new RenderTexture(size, textureObject, this, handle);

            _renderTargets.Add(handle, data);
            return(renderTarget);
        }
Exemple #4
0
 public IRenderTexture CreateRenderTarget(Vector2i size, RenderTargetFormatParameters format,
                                          TextureSampleParameters?sampleParameters = null, string?name = null)
 {
     return(new DummyRenderTexture(size, new DummyTexture(size)));
 }
Exemple #5
0
        private RenderTexture CreateRenderTarget(Vector2i size, RenderTargetFormatParameters format,
                                                 TextureSampleParameters?sampleParameters = null, string?name = null)
        {
            DebugTools.Assert(size.X != 0);
            DebugTools.Assert(size.Y != 0);

            // Cache currently bound framebuffers
            // so if somebody creates a framebuffer while drawing it won't ruin everything.
            var boundDrawBuffer = GL.GetInteger(GetPName.DrawFramebufferBinding);
            var boundReadBuffer = GL.GetInteger(GetPName.ReadFramebufferBinding);

            // Generate FBO.
            var fbo = new GLHandle(GL.GenFramebuffer());

            // Bind color attachment to FBO.
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, fbo.Handle);

            ObjectLabelMaybe(ObjectLabelIdentifier.Framebuffer, fbo, name);

            var(width, height) = size;

            ClydeTexture textureObject;
            GLHandle     depthStencilBuffer = default;

            var estPixSize = 0L;

            // Color attachment.
            {
                var texture = new GLHandle(GL.GenTexture());

                GL.BindTexture(TextureTarget.Texture2D, texture.Handle);

                ApplySampleParameters(sampleParameters);

                // Make sure to specify the correct pixel type and formats even if we're not uploading any data.
                // Not doing this (just sending red/byte) is fine on desktop GL but illegal on ES.
                var(internalFormat, pixFormat, pixType) = format.ColorFormat switch
                {
                    // using block comments to force formatters to not f**k this up.
                    RTCF.Rgba8 => /*       */ (PIF.Rgba8, /*       */ PF.Rgba, /**/ PT.UnsignedByte),
                    RTCF.Rgba16F => /*     */ (PIF.Rgba16f, /*     */ PF.Rgba, /**/ PT.Float),
                    RTCF.Rgba8Srgb => /*   */ (PIF.Srgb8Alpha8, /* */ PF.Rgba, /**/ PT.UnsignedByte),
                    RTCF.R11FG11FB10F => /**/ (PIF.R11fG11fB10f, /**/ PF.Rgb, /* */ PT.Float),
                    RTCF.R32F => /*        */ (PIF.R32f, /*        */ PF.Red, /* */ PT.Float),
                    RTCF.RG32F => /*       */ (PIF.Rg32f, /*       */ PF.Rg, /*  */ PT.Float),
                    RTCF.R8 => /*          */ (PIF.R8, /*          */ PF.Red, /* */ PT.UnsignedByte),
                    _ => throw new ArgumentOutOfRangeException(nameof(format.ColorFormat), format.ColorFormat, null)
                };

                estPixSize += EstPixelSize(internalFormat);

                GL.TexImage2D(TextureTarget.Texture2D, 0, internalFormat, width, height, 0, pixFormat,
                              pixType, IntPtr.Zero);

                GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0,
                                      texture.Handle,
                                      0);

                textureObject = GenTexture(texture, size, name == null ? null : $"{name}-color");
            }

            // Depth/stencil buffers.
            if (format.HasDepthStencil)
            {
                depthStencilBuffer = new GLHandle(GL.GenRenderbuffer());
                GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle);

                ObjectLabelMaybe(ObjectLabelIdentifier.Renderbuffer, depthStencilBuffer,
                                 name == null ? null : $"{name}-depth-stencil");

                GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, width,
                                       height);

                estPixSize += 4;

                GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment,
                                           RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle);
            }

            // This should always pass but OpenGL makes it easy to check for once so let's.
            var status = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer);

            DebugTools.Assert(status == FramebufferErrorCode.FramebufferComplete,
                              $"new framebuffer has bad status {status}");

            // Re-bind previous framebuffers.
            GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, boundDrawBuffer);
            GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, boundReadBuffer);

            var pressure = estPixSize * size.X * size.Y;

            var handle = AllocRid();
            var data   = new LoadedRenderTarget
            {
                IsWindow           = false,
                DepthStencilHandle = depthStencilBuffer,
                FramebufferHandle  = fbo,
                Size           = size,
                TextureHandle  = textureObject.TextureId,
                MemoryPressure = pressure
            };

            //GC.AddMemoryPressure(pressure);
            var renderTarget = new RenderTexture(size, textureObject, this, handle);

            _renderTargets.Add(handle, data);
            return(renderTarget);
        }