Files
OpenRA/OpenRA.Platforms.Default/FrameBuffer.cs
2023-01-11 11:58:54 +02:00

158 lines
4.3 KiB
C#

#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Diagnostics;
using System.IO;
using OpenRA.Primitives;
namespace OpenRA.Platforms.Default
{
sealed class FrameBuffer : ThreadAffine, IFrameBuffer
{
readonly ITexture texture;
readonly Size size;
readonly Color clearColor;
uint framebuffer, depth;
bool disposed;
bool scissored;
public FrameBuffer(Size size, ITextureInternal texture, Color clearColor)
{
this.size = size;
this.clearColor = clearColor;
if (!Exts.IsPowerOf2(size.Width) || !Exts.IsPowerOf2(size.Height))
throw new InvalidDataException($"Frame buffer size ({size.Width}x{size.Height}) must be a power of two");
OpenGL.glGenFramebuffers(1, out framebuffer);
OpenGL.CheckGLError();
OpenGL.glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, framebuffer);
OpenGL.CheckGLError();
// Color
this.texture = texture;
texture.SetEmpty(size.Width, size.Height);
OpenGL.glFramebufferTexture2D(OpenGL.GL_FRAMEBUFFER, OpenGL.GL_COLOR_ATTACHMENT0, OpenGL.GL_TEXTURE_2D, texture.ID, 0);
OpenGL.CheckGLError();
// Depth
OpenGL.glGenRenderbuffers(1, out depth);
OpenGL.CheckGLError();
OpenGL.glBindRenderbuffer(OpenGL.GL_RENDERBUFFER, depth);
OpenGL.CheckGLError();
var glDepth = OpenGL.Profile == GLProfile.Embedded ? OpenGL.GL_DEPTH_COMPONENT16 : OpenGL.GL_DEPTH_COMPONENT;
OpenGL.glRenderbufferStorage(OpenGL.GL_RENDERBUFFER, glDepth, size.Width, size.Height);
OpenGL.CheckGLError();
OpenGL.glFramebufferRenderbuffer(OpenGL.GL_FRAMEBUFFER, OpenGL.GL_DEPTH_ATTACHMENT, OpenGL.GL_RENDERBUFFER, depth);
OpenGL.CheckGLError();
// Test for completeness
var status = OpenGL.glCheckFramebufferStatus(OpenGL.GL_FRAMEBUFFER);
if (status != OpenGL.GL_FRAMEBUFFER_COMPLETE)
{
var error = $"Error creating framebuffer: {status}\n{new StackTrace()}";
OpenGL.WriteGraphicsLog(error);
throw new InvalidOperationException("OpenGL Error: See graphics.log for details.");
}
// Restore default buffer
OpenGL.glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, 0);
OpenGL.CheckGLError();
}
static int[] ViewportRectangle()
{
var v = new int[4];
OpenGL.glGetIntegerv(OpenGL.GL_VIEWPORT, out v[0]);
OpenGL.CheckGLError();
return v;
}
int[] cv = new int[4];
public void Bind()
{
VerifyThreadAffinity();
// Cache viewport rect to restore when unbinding
cv = ViewportRectangle();
OpenGL.glFlush();
OpenGL.CheckGLError();
OpenGL.glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, framebuffer);
OpenGL.CheckGLError();
OpenGL.glViewport(0, 0, size.Width, size.Height);
OpenGL.CheckGLError();
OpenGL.glClearColor(clearColor.R, clearColor.G, clearColor.B, clearColor.A);
OpenGL.CheckGLError();
OpenGL.glClear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
OpenGL.CheckGLError();
}
public void Unbind()
{
if (scissored)
throw new InvalidOperationException("Attempting to unbind FrameBuffer with an active scissor region.");
VerifyThreadAffinity();
OpenGL.glFlush();
OpenGL.CheckGLError();
OpenGL.glBindFramebuffer(OpenGL.GL_FRAMEBUFFER, 0);
OpenGL.CheckGLError();
OpenGL.glViewport(cv[0], cv[1], cv[2], cv[3]);
OpenGL.CheckGLError();
}
public void EnableScissor(Rectangle rect)
{
VerifyThreadAffinity();
OpenGL.glScissor(rect.X, rect.Y, Math.Max(rect.Width, 0), Math.Max(rect.Height, 0));
OpenGL.CheckGLError();
OpenGL.glEnable(OpenGL.GL_SCISSOR_TEST);
OpenGL.CheckGLError();
scissored = true;
}
public void DisableScissor()
{
VerifyThreadAffinity();
OpenGL.glDisable(OpenGL.GL_SCISSOR_TEST);
OpenGL.CheckGLError();
scissored = false;
}
public ITexture Texture
{
get
{
VerifyThreadAffinity();
return texture;
}
}
public void Dispose()
{
if (disposed)
return;
disposed = true;
texture.Dispose();
OpenGL.glDeleteFramebuffers(1, ref framebuffer);
OpenGL.CheckGLError();
OpenGL.glDeleteRenderbuffers(1, ref depth);
OpenGL.CheckGLError();
}
}
}