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); }
public IRenderTexture CreateRenderTarget(Vector2i size, RenderTargetFormatParameters format, TextureSampleParameters?sampleParameters = null, string?name = null) { return(new DummyRenderTexture(size, new DummyTexture(size))); }
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); }