Change RenderTarget to IRenderTarget, implement classes accordingly

Changes GDI+ handling quite a bit, hopefully didn't break anything
This commit is contained in:
CasualPokePlayer 2024-05-18 01:26:37 -07:00
parent 25f62c6620
commit 8aac335d9b
22 changed files with 281 additions and 325 deletions

View File

@ -9,15 +9,19 @@ namespace BizHawk.Bizware.Graphics.Controls
{
internal sealed class GDIPlusControl : GraphicsControl
{
public GDIPlusControl(Func<Func<(SDGraphics Graphics, Rectangle Rectangle)>, GDIPlusRenderTarget> createControlRenderTarget)
/// <summary>
/// The render target for rendering to this control
/// </summary>
private readonly GDIPlusControlRenderTarget _renderTarget;
public GDIPlusControl(Func<Func<(SDGraphics Graphics, Rectangle Rectangle)>, GDIPlusControlRenderTarget> createControlRenderTarget)
{
RenderTarget = createControlRenderTarget(GetControlRenderContext);
_renderTarget = createControlRenderTarget(GetControlRenderContext);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.UserMouse, true);
DoubleBuffered = true;
BackColor = Color.Black;
}
private (SDGraphics Graphics, Rectangle Rectangle) GetControlRenderContext()
@ -28,11 +32,6 @@ namespace BizHawk.Bizware.Graphics.Controls
return (graphics, ClientRectangle);
}
/// <summary>
/// The render target for rendering to this control
/// </summary>
private GDIPlusRenderTarget RenderTarget { get; }
public override void AllowTearing(bool state)
{
// not controllable
@ -54,22 +53,22 @@ namespace BizHawk.Bizware.Graphics.Controls
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
RenderTarget.CreateGraphics();
_renderTarget.CreateGraphics();
}
protected override void OnHandleDestroyed(EventArgs e)
{
base.OnHandleDestroyed(e);
RenderTarget.Dispose();
_renderTarget.Dispose();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
RenderTarget.CreateGraphics();
_renderTarget.CreateGraphics();
}
public override void SwapBuffers()
=> RenderTarget.BufferedGraphics?.Render(RenderTarget.CurGraphics);
=> _renderTarget.BufferedGraphics?.Render(_renderTarget.ControlGraphics);
}
}
}

View File

@ -0,0 +1,38 @@
using Vortice.Direct3D11;
using Vortice.DXGI;
namespace BizHawk.Bizware.Graphics
{
internal sealed class D3D11RenderTarget : D3D11Texture2D, IRenderTarget
{
public ID3D11RenderTargetView RTV;
public D3D11RenderTarget(D3D11Resources resources, int width, int height)
: base(resources, BindFlags.ShaderResource | BindFlags.RenderTarget, ResourceUsage.Default, CpuAccessFlags.None, width, height)
{
}
public override void CreateTexture()
{
base.CreateTexture();
var rtvd = new RenderTargetViewDescription(RenderTargetViewDimension.Texture2D, Format.B8G8R8A8_UNorm);
RTV = Device.CreateRenderTargetView(Texture, rtvd);
}
public override void DestroyTexture()
{
RTV?.Dispose();
RTV = null;
base.DestroyTexture();
}
public void Bind()
{
Context.OMSetRenderTargets(RTV);
_resources.CurRenderTarget = this;
}
public override string ToString()
=> $"D3D11 RenderTarget: {Width}x{Height}";
}
}

View File

@ -26,8 +26,9 @@ namespace BizHawk.Bizware.Graphics
public FeatureLevel DeviceFeatureLevel;
public readonly HashSet<RenderTarget> RenderTargets = new();
public readonly HashSet<D3D11Texture2D> ShaderTextures = new();
public D3D11RenderTarget CurRenderTarget;
public readonly HashSet<D3D11Texture2D> Textures = new();
public readonly HashSet<Shader> VertexShaders = new();
public readonly HashSet<Shader> PixelShaders = new();
public readonly HashSet<Pipeline> Pipelines = new();
@ -94,6 +95,11 @@ namespace BizHawk.Bizware.Graphics
RasterizerState = Device.CreateRasterizerState(rd);
Context.IASetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
foreach (var tex2d in Textures)
{
tex2d.CreateTexture();
}
}
catch
{
@ -102,8 +108,15 @@ namespace BizHawk.Bizware.Graphics
}
}
public void Dispose()
public void DestroyResources()
{
foreach (var tex2d in Textures)
{
tex2d.DestroyTexture();
}
CurRenderTarget = null;
LinearSamplerState?.Dispose();
LinearSamplerState = null;
PointSamplerState?.Dispose();
@ -128,5 +141,11 @@ namespace BizHawk.Bizware.Graphics
Factory1?.Dispose();
Factory1 = null;
}
public void Dispose()
{
DestroyResources();
Textures.Clear();
}
}
}

View File

@ -10,18 +10,18 @@ namespace BizHawk.Bizware.Graphics
{
internal class D3D11Texture2D : ITexture2D
{
private readonly D3D11Resources _resources;
protected readonly D3D11Resources _resources;
private readonly BindFlags _bindFlags;
private readonly ResourceUsage _usage;
private readonly CpuAccessFlags _cpuAccessFlags;
private ID3D11Device Device => _resources.Device;
private ID3D11DeviceContext Context => _resources.Context;
private HashSet<D3D11Texture2D> ShaderTextures => _resources.ShaderTextures;
protected ID3D11Device Device => _resources.Device;
protected ID3D11DeviceContext Context => _resources.Context;
private HashSet<D3D11Texture2D> Textures => _resources.Textures;
private ID3D11Texture2D StagingTexture;
public ID3D11Texture2D Texture;
protected ID3D11Texture2D Texture;
public ID3D11ShaderResourceView SRV;
public bool LinearFiltering;
@ -37,17 +37,18 @@ namespace BizHawk.Bizware.Graphics
_cpuAccessFlags = cpuAccessFlags;
Width = width;
Height = height;
// ReSharper disable once VirtualMemberCallInConstructor
CreateTexture();
ShaderTextures.Add(this);
Textures.Add(this);
}
public void Dispose()
{
DestroyTexture();
ShaderTextures.Remove(this);
Textures.Remove(this);
}
public void CreateTexture()
public virtual void CreateTexture()
{
Texture = Device.CreateTexture2D(
Format.B8G8R8A8_UNorm,
@ -62,7 +63,7 @@ namespace BizHawk.Bizware.Graphics
SRV = Device.CreateShaderResourceView(Texture, srvd);
}
public void DestroyTexture()
public virtual void DestroyTexture()
{
SRV?.Dispose();
SRV = null;

View File

@ -38,10 +38,10 @@ namespace BizHawk.Bizware.Graphics
private ID3D11RasterizerState RasterizerState => _resources.RasterizerState;
private FeatureLevel DeviceFeatureLevel => _resources.DeviceFeatureLevel;
private D3D11RenderTarget CurRenderTarget => _resources.CurRenderTarget;
// rendering state
private Pipeline _curPipeline;
private RenderTarget _curRenderTarget;
private D3D11SwapChain.SwapChainResources _controlSwapChain;
public IGL_D3D11()
@ -151,32 +151,9 @@ namespace BizHawk.Bizware.Graphics
sw.PS = null;
}
foreach (var rw in _resources.RenderTargets.Select(rt => (RenderTargetWrapper)rt.Opaque))
{
rw.RTV.Dispose();
rw.RTV = null;
}
foreach (var tex2d in _resources.ShaderTextures)
{
tex2d.DestroyTexture();
}
_resources.Dispose();
_resources.DestroyResources();
_resources.CreateResources();
foreach (var tex2d in _resources.ShaderTextures)
{
tex2d.CreateTexture();
}
foreach (var rt in _resources.RenderTargets)
{
var rw = (RenderTargetWrapper)rt.Opaque;
var rtvd = new RenderTargetViewDescription(RenderTargetViewDimension.Texture2D, Format.B8G8R8A8_UNorm);
rw.RTV = Device.CreateRenderTargetView(((D3D11Texture2D)rt.Texture2D).Texture, rtvd);
}
foreach (var sw in _resources.VertexShaders.Select(vertexShader => (ShaderWrapper)vertexShader.Opaque))
{
sw.VS = Device.CreateVertexShader(sw.Bytecode.Span);
@ -264,17 +241,7 @@ namespace BizHawk.Bizware.Graphics
}
public void ClearColor(Color color)
{
if (_curRenderTarget == null)
{
Context.ClearRenderTargetView(_controlSwapChain.RTV, new(color.R, color.B, color.G, color.A));
}
else
{
var rw = (RenderTargetWrapper)_curRenderTarget.Opaque;
Context.ClearRenderTargetView(rw.RTV, new(color.R, color.B, color.G, color.A));
}
}
=> Context.ClearRenderTargetView(CurRenderTarget?.RTV ?? _controlSwapChain.RTV, new(color.R, color.B, color.G, color.A));
private class ShaderWrapper // Disposable fields cleaned up by Internal_FreeShader
{
@ -607,11 +574,6 @@ namespace BizHawk.Bizware.Graphics
public ShaderWrapper VertexShader, FragmentShader;
}
private class RenderTargetWrapper
{
public ID3D11RenderTargetView RTV;
}
private class VertexLayoutWrapper
{
public ID3D11InputLayout VertexInputLayout;
@ -875,36 +837,13 @@ namespace BizHawk.Bizware.Graphics
Context.RSSetScissorRect(x, y, width, height);
}
public void FreeRenderTarget(RenderTarget rt)
public IRenderTarget CreateRenderTarget(int width, int height)
=> new D3D11RenderTarget(_resources, width, height);
public void BindDefaultRenderTarget()
{
var rw = (RenderTargetWrapper)rt.Opaque;
rw.RTV.Dispose();
_resources.RenderTargets.Remove(rt);
rt.Texture2D.Dispose();
}
public RenderTarget CreateRenderTarget(int width, int height)
{
var tex = new D3D11Texture2D(_resources, BindFlags.ShaderResource | BindFlags.RenderTarget, ResourceUsage.Default, CpuAccessFlags.None, width, height);
var rtvd = new RenderTargetViewDescription(RenderTargetViewDimension.Texture2D, Format.B8G8R8A8_UNorm);
var rw = new RenderTargetWrapper { RTV = Device.CreateRenderTargetView(tex.Texture, rtvd) };
var rt = new RenderTarget(this, rw, tex);
_resources.RenderTargets.Add(rt);
return rt;
}
public void BindRenderTarget(RenderTarget rt)
{
_curRenderTarget = rt;
if (rt == null)
{
Context.OMSetRenderTargets(_controlSwapChain.RTV);
return;
}
var rw = (RenderTargetWrapper)rt.Opaque;
Context.OMSetRenderTargets(rw.RTV);
_resources.CurRenderTarget = null;
Context.OMSetRenderTargets(_controlSwapChain.RTV);
}
public void Draw(IntPtr data, int count)

View File

@ -0,0 +1,37 @@
using System;
using System.Drawing;
using SDGraphics = System.Drawing.Graphics;
namespace BizHawk.Bizware.Graphics
{
public sealed class GDIPlusControlRenderTarget : IDisposable
{
private readonly Func<(SDGraphics, Rectangle)> _getControlRenderContext;
private BufferedGraphicsContext _bufferedGraphicsContext = new();
public SDGraphics ControlGraphics;
public BufferedGraphics BufferedGraphics;
internal GDIPlusControlRenderTarget(Func<(SDGraphics Graphics, Rectangle Rectangle)> getControlRenderContext)
=> _getControlRenderContext = getControlRenderContext;
public void Dispose()
{
ControlGraphics?.Dispose();
ControlGraphics = null;
BufferedGraphics?.Dispose();
BufferedGraphics = null;
_bufferedGraphicsContext?.Dispose();
_bufferedGraphicsContext = null;
}
public void CreateGraphics()
{
ControlGraphics?.Dispose();
BufferedGraphics?.Dispose();
(ControlGraphics, var r) = _getControlRenderContext();
BufferedGraphics = _bufferedGraphicsContext.Allocate(ControlGraphics, r);
}
}
}

View File

@ -1,63 +1,30 @@
using System;
using System.Drawing;
using SDGraphics = System.Drawing.Graphics;
namespace BizHawk.Bizware.Graphics
{
public class GDIPlusRenderTarget : IDisposable
internal sealed class GDIPlusRenderTarget : GDIPlusTexture2D, IRenderTarget
{
internal GDIPlusRenderTarget(Func<BufferedGraphicsContext> getBufferedGraphicsContext,
Func<(SDGraphics Graphics, Rectangle Rectangle)> getControlRenderContext = null)
private readonly IGL_GDIPlus _gdiPlus;
public SDGraphics TextureGraphics;
internal GDIPlusRenderTarget(IGL_GDIPlus gdiPlus, int width, int height)
: base(width, height)
{
_getBufferedGraphicsContext = getBufferedGraphicsContext;
_getControlRenderContext = getControlRenderContext;
_gdiPlus = gdiPlus;
TextureGraphics = SDGraphics.FromImage(SDBitmap);
}
public void Dispose()
public override void Dispose()
{
if (_getControlRenderContext != null)
{
CurGraphics?.Dispose();
CurGraphics = null;
}
BufferedGraphics?.Dispose();
BufferedGraphics = null;
TextureGraphics?.Dispose();
TextureGraphics = null;
base.Dispose();
}
private readonly Func<BufferedGraphicsContext> _getBufferedGraphicsContext;
public void Bind()
=> _gdiPlus.CurRenderTarget = this;
/// <summary>
/// get Graphics and Rectangle from a control, if any
/// </summary>
private readonly Func<(SDGraphics, Rectangle)> _getControlRenderContext;
/// <summary>
/// the offscreen render target, if that's what this is representing
/// </summary>
public RenderTarget Target;
public SDGraphics CurGraphics;
public BufferedGraphics BufferedGraphics;
public void CreateGraphics()
{
Rectangle r;
if (_getControlRenderContext != null)
{
(CurGraphics, r) = _getControlRenderContext();
}
else
{
var gtex = (GDIPlusTexture2D)Target.Texture2D;
CurGraphics?.Dispose();
CurGraphics = SDGraphics.FromImage(gtex.SDBitmap);
r = gtex.GetRectangle();
}
BufferedGraphics?.Dispose();
BufferedGraphics = _getBufferedGraphicsContext().Allocate(CurGraphics, r);
}
public override string ToString()
=> $"GDI+ RenderTarget: {Width}x{Height}";
}
}

View File

@ -3,7 +3,7 @@ using System.Drawing.Imaging;
namespace BizHawk.Bizware.Graphics
{
public class GDIPlusTexture2D : ITexture2D
internal class GDIPlusTexture2D : ITexture2D
{
public Bitmap SDBitmap;
public bool LinearFiltering;
@ -19,7 +19,7 @@ namespace BizHawk.Bizware.Graphics
SDBitmap = new(width, height, PixelFormat.Format32bppArgb);
}
public void Dispose()
public virtual void Dispose()
{
SDBitmap?.Dispose();
SDBitmap = null;

View File

@ -5,15 +5,22 @@ using System.Numerics;
using SDGraphics = System.Drawing.Graphics;
//TODO - maybe a layer to cache Graphics parameters (notably, filtering) ?
namespace BizHawk.Bizware.Graphics
{
public class IGL_GDIPlus : IGL
{
private GDIPlusControlRenderTarget _controlRenderTarget;
internal GDIPlusRenderTarget CurRenderTarget;
public EDispMethod DispMethodEnum => EDispMethod.GdiPlus;
public void Dispose()
=> BufferedGraphicsContext.Dispose();
{
}
internal SDGraphics GetCurrentGraphics()
=> CurRenderTarget?.TextureGraphics ?? _controlRenderTarget.BufferedGraphics.Graphics;
public void ClearColor(Color color)
=> GetCurrentGraphics().Clear(color);
@ -118,62 +125,21 @@ namespace BizHawk.Bizware.Graphics
{
}
public void FreeRenderTarget(RenderTarget rt)
{
var grt = (GDIPlusRenderTarget)rt.Opaque;
grt.Dispose();
}
public IRenderTarget CreateRenderTarget(int width, int height)
=> new GDIPlusRenderTarget(this, width, height);
public RenderTarget CreateRenderTarget(int width, int height)
{
var tex2d = new GDIPlusTexture2D(width, height);
var grt = new GDIPlusRenderTarget(() => BufferedGraphicsContext);
var rt = new RenderTarget(this, grt, tex2d);
grt.Target = rt;
return rt;
}
public void BindDefaultRenderTarget()
=> CurRenderTarget = null;
public void BindRenderTarget(RenderTarget rt)
{
if (_currOffscreenGraphics != null)
{
_currOffscreenGraphics.Dispose();
_currOffscreenGraphics = null;
}
if (rt == null)
{
// null means to use the default RT for the current control
CurrentRenderTarget = _controlRenderTarget;
}
else
{
var gtex = (GDIPlusTexture2D)rt.Texture2D;
CurrentRenderTarget = (GDIPlusRenderTarget)rt.Opaque;
_currOffscreenGraphics = SDGraphics.FromImage(gtex.SDBitmap);
}
}
private GDIPlusRenderTarget _controlRenderTarget;
public GDIPlusRenderTarget CreateControlRenderTarget(Func<(SDGraphics Graphics, Rectangle Rectangle)> getControlRenderContext)
public GDIPlusControlRenderTarget CreateControlRenderTarget(Func<(SDGraphics Graphics, Rectangle Rectangle)> getControlRenderContext)
{
if (_controlRenderTarget != null)
{
throw new InvalidOperationException($"{nameof(IGL_GDIPlus)} can only have one control render target");
}
_controlRenderTarget = new(() => BufferedGraphicsContext, getControlRenderContext);
_controlRenderTarget = new(getControlRenderContext);
return _controlRenderTarget;
}
private SDGraphics _currOffscreenGraphics;
public SDGraphics GetCurrentGraphics()
=> _currOffscreenGraphics ?? CurrentRenderTarget.BufferedGraphics.Graphics;
public GDIPlusRenderTarget CurrentRenderTarget;
public readonly BufferedGraphicsContext BufferedGraphicsContext = new();
}
}

View File

@ -19,17 +19,17 @@ namespace BizHawk.Bizware.Graphics
EDispMethod DispMethodEnum { get; }
/// <summary>
/// Clears the color buffer with the specified color
/// Clears the currently bound render target with the specified color
/// </summary>
void ClearColor(Color color);
/// <summary>
/// compile a fragment shader. This is the simplified method. A more complex method may be added later which will accept multiple sources and preprocessor definitions independently
/// Compile a fragment shader. This is the simplified method. A more complex method may be added later which will accept multiple sources and preprocessor definitions independently
/// </summary>
Shader CreateFragmentShader(string source, string entry, bool required);
/// <summary>
/// compile a vertex shader. This is the simplified method. A more complex method may be added later which will accept multiple sources and preprocessor definitions independently
/// Compile a vertex shader. This is the simplified method. A more complex method may be added later which will accept multiple sources and preprocessor definitions independently
/// </summary>
Shader CreateVertexShader(string source, string entry, bool required);
@ -59,40 +59,40 @@ namespace BizHawk.Bizware.Graphics
void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4x4 mat, bool transpose);
/// <summary>
/// sets a uniform value
/// Sets a uniform value
/// </summary>
void SetPipelineUniform(PipelineUniform uniform, Vector4 value);
/// <summary>
/// sets a uniform value
/// Sets a uniform value
/// </summary>
void SetPipelineUniform(PipelineUniform uniform, Vector2 value);
/// <summary>
/// sets a uniform value
/// Sets a uniform value
/// </summary>
void SetPipelineUniform(PipelineUniform uniform, float value);
/// <summary>
/// sets uniform values
/// Sets uniform values
/// </summary>
void SetPipelineUniform(PipelineUniform uniform, Vector4[] values);
/// <summary>
/// sets a uniform value
/// Sets a uniform value
/// </summary>
void SetPipelineUniform(PipelineUniform uniform, bool value);
/// <summary>
/// Draws based on the currently set pipeline
/// data contains vertexes based on the pipeline's VertexLayout
/// count is the vertex count
/// Data contains vertexes based on the pipeline's VertexLayout
/// Count is the vertex count
/// Vertexes must form triangle strips
/// </summary>
void Draw(IntPtr data, int count);
/// <summary>
/// creates a vertex layout resource
/// Creates a vertex layout resource
/// </summary>
VertexLayout CreateVertexLayout();
@ -119,35 +119,32 @@ namespace BizHawk.Bizware.Graphics
ITexture2D WrapGLTexture2D(int glTexId, int width, int height);
/// <summary>
/// sets the viewport (and scissor) according to the provided specifications
/// Sets the viewport (and scissor) according to the provided specifications
/// </summary>
void SetViewport(int x, int y, int width, int height);
/// <summary>
/// generates a proper 2d othographic projection for the given destination size, suitable for use in a GUI
/// Generates a proper 2D othographic projection for the given destination size, suitable for use in a GUI
/// </summary>
Matrix4x4 CreateGuiProjectionMatrix(int width, int height);
/// <summary>
/// generates a proper view transform for a standard 2d ortho projection, including half-pixel jitter if necessary and
/// re-establishing of a normal 2d graphics top-left origin. suitable for use in a GUI
/// Generates a proper view transform for a standard 2D othographic projection, including half-pixel jitter if necessary
/// and re-establishing of a normal 2D graphics top-left origin. Suitable for use in a GUI
/// </summary>
Matrix4x4 CreateGuiViewMatrix(int width, int height, bool autoflip = true);
/// <summary>
/// Creates a render target. Only includes a color buffer. Pixel format control TBD
/// Creates a render target. Only includes a color buffer, and will always be in byte order BGRA (i.e. little endian ARGB)
/// This may unbind a previously bound render target
/// </summary>
RenderTarget CreateRenderTarget(int width, int height);
IRenderTarget CreateRenderTarget(int width, int height);
/// <summary>
/// Binds a RenderTarget for current rendering
/// Binds the IGL's default render target (i.e. to the IGL's control)
/// This implicitly unbinds any previously bound IRenderTarget
/// </summary>
void BindRenderTarget(RenderTarget rt);
/// <summary>
/// Frees the provided render target. Same as disposing the resource.
/// </summary>
void FreeRenderTarget(RenderTarget rt);
void BindDefaultRenderTarget();
/// <summary>
/// Frees the provided pipeline. Same as disposing the resource.

View File

@ -0,0 +1,15 @@
namespace BizHawk.Bizware.Graphics
{
/// <summary>
/// A render target, essentially just a 2D texture which can be written as if it was a window
/// As this is effectively a texture, it inherits ITexture2D
/// However, note that semantically the CPU shouldn't be writing to a render target, so LoadFrom might not work!
/// </summary>
public interface IRenderTarget : ITexture2D
{
/// <summary>
/// Binds this render target
/// </summary>
void Bind();
}
}

View File

@ -34,7 +34,7 @@ namespace BizHawk.Bizware.Graphics
// rendering state
private Pipeline _currPipeline;
private RenderTarget _currRenderTarget;
internal bool DefaultRenderTargetBound;
// this IGL either requires at least OpenGL 3.0
public static bool Available => OpenGLVersion.SupportsVersion(3, 0);
@ -451,50 +451,14 @@ namespace BizHawk.Bizware.Graphics
public ITexture2D WrapGLTexture2D(int glTexId, int width, int height)
=> new OpenGLTexture2D(GL, (uint)glTexId, width, height);
public void FreeRenderTarget(RenderTarget rt)
{
rt.Texture2D.Dispose();
GL.DeleteFramebuffer((uint)rt.Opaque);
}
/// <exception cref="InvalidOperationException">framebuffer creation unsuccessful</exception>
public RenderTarget CreateRenderTarget(int width, int height)
public IRenderTarget CreateRenderTarget(int width, int height)
=> new OpenGLRenderTarget(this, GL, width, height);
public void BindDefaultRenderTarget()
{
// create a texture for it
var tex2d = new OpenGLTexture2D(GL, width, height);
// create the FBO
var fbId = GL.GenFramebuffer();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, fbId);
// bind the tex to the FBO
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, tex2d.TexID, 0);
// do something, I guess say which color buffers are used by the framebuffer
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
if ((FramebufferStatus)GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != FramebufferStatus.Complete)
{
throw new InvalidOperationException($"Error creating framebuffer (at {nameof(GL.CheckFramebufferStatus)})");
}
// since we're done configuring unbind this framebuffer, to return to the default
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
return new(this, fbId, tex2d);
}
public void BindRenderTarget(RenderTarget rt)
{
_currRenderTarget = rt;
if (rt == null)
{
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
}
else
{
GL.BindFramebuffer(FramebufferTarget.Framebuffer, (uint)rt.Opaque);
}
DefaultRenderTargetBound = true;
}
public Matrix4x4 CreateGuiProjectionMatrix(int width, int height)
@ -511,7 +475,7 @@ namespace BizHawk.Bizware.Graphics
ret.M22 = -1.0f;
ret.M41 = width * -0.5f;
ret.M42 = height * 0.5f;
if (autoflip && _currRenderTarget is not null) // flip as long as we're not a final render target
if (autoflip && !DefaultRenderTargetBound) // flip as long as we're not a final render target
{
ret.M22 = 1.0f;
ret.M42 *= -1;

View File

@ -0,0 +1,52 @@
using System;
using Silk.NET.OpenGL.Legacy;
namespace BizHawk.Bizware.Graphics
{
internal sealed class OpenGLRenderTarget : OpenGLTexture2D, IRenderTarget
{
private readonly IGL_OpenGL _openGL;
private readonly GL GL;
public readonly uint FBO;
public OpenGLRenderTarget(IGL_OpenGL openGL, GL gl, int width, int height)
: base(gl, width, height)
{
_openGL = openGL;
GL = gl;
// create the FBO
FBO = GL.GenFramebuffer();
Bind();
// bind the tex to the FBO
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, TexID, 0);
// do something, I guess say which color buffers are used by the framebuffer
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
if ((FramebufferStatus)GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != FramebufferStatus.Complete)
{
Dispose();
throw new InvalidOperationException($"Error creating framebuffer (at {nameof(GL.CheckFramebufferStatus)})");
}
}
public override void Dispose()
{
GL.DeleteFramebuffer(FBO);
base.Dispose();
}
public void Bind()
{
GL.BindFramebuffer(FramebufferTarget.Framebuffer, FBO);
_openGL.DefaultRenderTargetBound = false;
}
public override string ToString()
=> $"OpenGL RenderTarget: {Width}x{Height}";
}
}

View File

@ -44,7 +44,7 @@ namespace BizHawk.Bizware.Graphics
IsUpsideDown = true;
}
public void Dispose()
public virtual void Dispose()
=> GL.DeleteTexture(TexID);
public unsafe BitmapBuffer Resolve()

View File

@ -1,38 +0,0 @@
using System;
namespace BizHawk.Bizware.Graphics
{
public class RenderTarget : IDisposable
{
public RenderTarget(IGL owner, object opaque, ITexture2D tex)
{
Owner = owner;
Opaque = opaque;
Texture2D = tex;
}
public override string ToString()
{
return $"GL RT: {Texture2D.Width}x{Texture2D.Height}";
}
public object Opaque { get; }
public IGL Owner { get; }
public ITexture2D Texture2D { get; }
public void Unbind()
{
Owner.BindRenderTarget(null);
}
public void Bind()
{
Owner.BindRenderTarget(this);
}
public void Dispose()
{
Owner.FreeRenderTarget(this);
}
}
}

View File

@ -32,14 +32,14 @@ namespace BizHawk.Client.Common
protected class DisplayManagerRenderTargetProvider : IRenderTargetProvider
{
private readonly Func<Size, RenderTarget> _callback;
private readonly Func<Size, IRenderTarget> _callback;
RenderTarget IRenderTargetProvider.Get(Size size)
public IRenderTarget Get(Size size)
{
return _callback(size);
}
public DisplayManagerRenderTargetProvider(Func<Size, RenderTarget> callback)
public DisplayManagerRenderTargetProvider(Func<Size, IRenderTarget> callback)
{
_callback = callback;
}
@ -864,7 +864,7 @@ namespace BizHawk.Client.Common
public void Blank()
{
ActivateGraphicsControlContext();
_gl.BindRenderTarget(null);
_gl.BindDefaultRenderTarget();
_gl.ClearColor(Color.Black);
SwapBuffersOfGraphicsControl();
}
@ -880,11 +880,11 @@ namespace BizHawk.Client.Common
RunFilterChainSteps(ref rtCounter, out var rtCurr, out _);
job.OffscreenBb = rtCurr.Texture2D.Resolve();
job.OffscreenBb = rtCurr.Resolve();
job.OffscreenBb.DiscardAlpha();
}
protected void RunFilterChainSteps(ref int rtCounter, out RenderTarget rtCurr, out bool inFinalTarget)
protected void RunFilterChainSteps(ref int rtCounter, out IRenderTarget rtCurr, out bool inFinalTarget)
{
ITexture2D texCurr = null;
rtCurr = null;
@ -907,7 +907,7 @@ namespace BizHawk.Client.Common
break;
case FilterProgram.ProgramStepType.FinalTarget:
_currentFilterProgram.CurrRenderTarget = rtCurr = null;
_gl.BindRenderTarget(rtCurr);
_gl.BindDefaultRenderTarget();
inFinalTarget = true;
break;
default:

View File

@ -38,7 +38,7 @@ namespace BizHawk.Client.Common.FilterManager
public interface IRenderTargetProvider
{
RenderTarget Get(Size size);
IRenderTarget Get(Size size);
}
public class FilterProgram
@ -69,9 +69,9 @@ namespace BizHawk.Client.Common.FilterManager
public IGL GL;
public IRenderTargetProvider RenderTargetProvider;
public RenderTarget CurrRenderTarget;
public IRenderTarget CurrRenderTarget;
public RenderTarget GetTempTarget(int width, int height)
public IRenderTarget GetTempTarget(int width, int height)
{
return RenderTargetProvider.Get(new(width, height));
}

View File

@ -111,7 +111,7 @@ namespace BizHawk.Client.Common.Filters
}
// TODO - why a different param order than DeclareOutput?
protected RenderTarget GetTempTarget(int width, int height)
protected IRenderTarget GetTempTarget(int width, int height)
{
return FilterProgram.GetTempTarget(width, height);
}

View File

@ -71,7 +71,7 @@ namespace BizHawk.Client.Common.Filters
public override void Run()
{
YieldOutput(FilterProgram.CurrRenderTarget.Texture2D);
YieldOutput(FilterProgram.CurrRenderTarget);
}
}
}

View File

@ -30,26 +30,26 @@ namespace BizHawk.Client.Common
private void ResetList()
{
_currentRenderTargets = new List<RenderTarget> { null, null };
_currentRenderTargets = new List<IRenderTarget> { null, null };
}
private readonly IGL _gl;
private List<RenderTarget> _currentRenderTargets;
private List<IRenderTarget> _currentRenderTargets;
public RenderTarget Get(Size dimensions)
public IRenderTarget Get(Size dimensions)
{
return Get(dimensions.Width, dimensions.Height);
}
public RenderTarget Get(int width, int height)
public IRenderTarget Get(int width, int height)
{
//get the current entry
var currentRenderTarget = _currentRenderTargets[0];
//check if its rotten and needs recreating
if (currentRenderTarget == null
|| currentRenderTarget.Texture2D.Width != width
|| currentRenderTarget.Texture2D.Height != height)
|| currentRenderTarget.Width != width
|| currentRenderTarget.Height != height)
{
// needs recreating. be sure to kill the old one...
currentRenderTarget?.Dispose();

View File

@ -99,7 +99,7 @@ namespace BizHawk.Client.EmuHawk
if (job.Offscreen)
{
job.OffscreenBb = rtCurr.Texture2D.Resolve();
job.OffscreenBb = rtCurr.Resolve();
job.OffscreenBb.DiscardAlpha();
return;
}

View File

@ -40,7 +40,7 @@ namespace BizHawk.Client.EmuHawk
private readonly IGL _gl;
private readonly GraphicsControl _graphicsControl;
private RenderTarget _rt;
private IRenderTarget _rt;
private readonly GuiRenderer _guiRenderer;
protected override void OnPaint(PaintEventArgs e)
@ -75,7 +75,7 @@ namespace BizHawk.Client.EmuHawk
{
if (_retain)
{
_rt.Unbind();
_gl.BindDefaultRenderTarget();
}
_graphicsControl.End();
@ -91,7 +91,7 @@ namespace BizHawk.Client.EmuHawk
}
// if we're retaining, then we cant draw until we unbind! its semantically a bit odd, but we expect users to call SwapBuffers() before end, so we cant unbind in End() even thoug hit makes a bit more sense.
_rt.Unbind();
_gl.BindDefaultRenderTarget();
Draw();
}
@ -104,9 +104,9 @@ namespace BizHawk.Client.EmuHawk
_guiRenderer.Begin(Width, Height);
_guiRenderer.DisableBlending();
_guiRenderer.Draw(_rt.Texture2D);
_guiRenderer.Draw(_rt);
_guiRenderer.End();
_graphicsControl.SwapBuffers();
}
}
}
}