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 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.UserPaint, true);
SetStyle(ControlStyles.Opaque, true); SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.UserMouse, true); SetStyle(ControlStyles.UserMouse, true);
DoubleBuffered = true; DoubleBuffered = true;
BackColor = Color.Black;
} }
private (SDGraphics Graphics, Rectangle Rectangle) GetControlRenderContext() private (SDGraphics Graphics, Rectangle Rectangle) GetControlRenderContext()
@ -28,11 +32,6 @@ namespace BizHawk.Bizware.Graphics.Controls
return (graphics, ClientRectangle); return (graphics, ClientRectangle);
} }
/// <summary>
/// The render target for rendering to this control
/// </summary>
private GDIPlusRenderTarget RenderTarget { get; }
public override void AllowTearing(bool state) public override void AllowTearing(bool state)
{ {
// not controllable // not controllable
@ -54,22 +53,22 @@ namespace BizHawk.Bizware.Graphics.Controls
protected override void OnHandleCreated(EventArgs e) protected override void OnHandleCreated(EventArgs e)
{ {
base.OnHandleCreated(e); base.OnHandleCreated(e);
RenderTarget.CreateGraphics(); _renderTarget.CreateGraphics();
} }
protected override void OnHandleDestroyed(EventArgs e) protected override void OnHandleDestroyed(EventArgs e)
{ {
base.OnHandleDestroyed(e); base.OnHandleDestroyed(e);
RenderTarget.Dispose(); _renderTarget.Dispose();
} }
protected override void OnResize(EventArgs e) protected override void OnResize(EventArgs e)
{ {
base.OnResize(e); base.OnResize(e);
RenderTarget.CreateGraphics(); _renderTarget.CreateGraphics();
} }
public override void SwapBuffers() 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 FeatureLevel DeviceFeatureLevel;
public readonly HashSet<RenderTarget> RenderTargets = new(); public D3D11RenderTarget CurRenderTarget;
public readonly HashSet<D3D11Texture2D> ShaderTextures = new();
public readonly HashSet<D3D11Texture2D> Textures = new();
public readonly HashSet<Shader> VertexShaders = new(); public readonly HashSet<Shader> VertexShaders = new();
public readonly HashSet<Shader> PixelShaders = new(); public readonly HashSet<Shader> PixelShaders = new();
public readonly HashSet<Pipeline> Pipelines = new(); public readonly HashSet<Pipeline> Pipelines = new();
@ -94,6 +95,11 @@ namespace BizHawk.Bizware.Graphics
RasterizerState = Device.CreateRasterizerState(rd); RasterizerState = Device.CreateRasterizerState(rd);
Context.IASetPrimitiveTopology(PrimitiveTopology.TriangleStrip); Context.IASetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
foreach (var tex2d in Textures)
{
tex2d.CreateTexture();
}
} }
catch 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?.Dispose();
LinearSamplerState = null; LinearSamplerState = null;
PointSamplerState?.Dispose(); PointSamplerState?.Dispose();
@ -128,5 +141,11 @@ namespace BizHawk.Bizware.Graphics
Factory1?.Dispose(); Factory1?.Dispose();
Factory1 = null; Factory1 = null;
} }
public void Dispose()
{
DestroyResources();
Textures.Clear();
}
} }
} }

View File

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

View File

@ -38,10 +38,10 @@ namespace BizHawk.Bizware.Graphics
private ID3D11RasterizerState RasterizerState => _resources.RasterizerState; private ID3D11RasterizerState RasterizerState => _resources.RasterizerState;
private FeatureLevel DeviceFeatureLevel => _resources.DeviceFeatureLevel; private FeatureLevel DeviceFeatureLevel => _resources.DeviceFeatureLevel;
private D3D11RenderTarget CurRenderTarget => _resources.CurRenderTarget;
// rendering state // rendering state
private Pipeline _curPipeline; private Pipeline _curPipeline;
private RenderTarget _curRenderTarget;
private D3D11SwapChain.SwapChainResources _controlSwapChain; private D3D11SwapChain.SwapChainResources _controlSwapChain;
public IGL_D3D11() public IGL_D3D11()
@ -151,32 +151,9 @@ namespace BizHawk.Bizware.Graphics
sw.PS = null; sw.PS = null;
} }
foreach (var rw in _resources.RenderTargets.Select(rt => (RenderTargetWrapper)rt.Opaque)) _resources.DestroyResources();
{
rw.RTV.Dispose();
rw.RTV = null;
}
foreach (var tex2d in _resources.ShaderTextures)
{
tex2d.DestroyTexture();
}
_resources.Dispose();
_resources.CreateResources(); _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)) foreach (var sw in _resources.VertexShaders.Select(vertexShader => (ShaderWrapper)vertexShader.Opaque))
{ {
sw.VS = Device.CreateVertexShader(sw.Bytecode.Span); sw.VS = Device.CreateVertexShader(sw.Bytecode.Span);
@ -264,17 +241,7 @@ namespace BizHawk.Bizware.Graphics
} }
public void ClearColor(Color color) public void ClearColor(Color color)
{ => Context.ClearRenderTargetView(CurRenderTarget?.RTV ?? _controlSwapChain.RTV, new(color.R, color.B, color.G, color.A));
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));
}
}
private class ShaderWrapper // Disposable fields cleaned up by Internal_FreeShader private class ShaderWrapper // Disposable fields cleaned up by Internal_FreeShader
{ {
@ -607,11 +574,6 @@ namespace BizHawk.Bizware.Graphics
public ShaderWrapper VertexShader, FragmentShader; public ShaderWrapper VertexShader, FragmentShader;
} }
private class RenderTargetWrapper
{
public ID3D11RenderTargetView RTV;
}
private class VertexLayoutWrapper private class VertexLayoutWrapper
{ {
public ID3D11InputLayout VertexInputLayout; public ID3D11InputLayout VertexInputLayout;
@ -875,36 +837,13 @@ namespace BizHawk.Bizware.Graphics
Context.RSSetScissorRect(x, y, width, height); 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; _resources.CurRenderTarget = null;
rw.RTV.Dispose(); Context.OMSetRenderTargets(_controlSwapChain.RTV);
_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);
} }
public void Draw(IntPtr data, int count) 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; using SDGraphics = System.Drawing.Graphics;
namespace BizHawk.Bizware.Graphics namespace BizHawk.Bizware.Graphics
{ {
public class GDIPlusRenderTarget : IDisposable internal sealed class GDIPlusRenderTarget : GDIPlusTexture2D, IRenderTarget
{ {
internal GDIPlusRenderTarget(Func<BufferedGraphicsContext> getBufferedGraphicsContext, private readonly IGL_GDIPlus _gdiPlus;
Func<(SDGraphics Graphics, Rectangle Rectangle)> getControlRenderContext = null) public SDGraphics TextureGraphics;
internal GDIPlusRenderTarget(IGL_GDIPlus gdiPlus, int width, int height)
: base(width, height)
{ {
_getBufferedGraphicsContext = getBufferedGraphicsContext; _gdiPlus = gdiPlus;
_getControlRenderContext = getControlRenderContext; TextureGraphics = SDGraphics.FromImage(SDBitmap);
} }
public void Dispose() public override void Dispose()
{ {
if (_getControlRenderContext != null) TextureGraphics?.Dispose();
{ TextureGraphics = null;
CurGraphics?.Dispose(); base.Dispose();
CurGraphics = null;
}
BufferedGraphics?.Dispose();
BufferedGraphics = null;
} }
private readonly Func<BufferedGraphicsContext> _getBufferedGraphicsContext; public void Bind()
=> _gdiPlus.CurRenderTarget = this;
/// <summary> public override string ToString()
/// get Graphics and Rectangle from a control, if any => $"GDI+ RenderTarget: {Width}x{Height}";
/// </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);
}
} }
} }

View File

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

View File

@ -5,15 +5,22 @@ using System.Numerics;
using SDGraphics = System.Drawing.Graphics; using SDGraphics = System.Drawing.Graphics;
//TODO - maybe a layer to cache Graphics parameters (notably, filtering) ?
namespace BizHawk.Bizware.Graphics namespace BizHawk.Bizware.Graphics
{ {
public class IGL_GDIPlus : IGL public class IGL_GDIPlus : IGL
{ {
private GDIPlusControlRenderTarget _controlRenderTarget;
internal GDIPlusRenderTarget CurRenderTarget;
public EDispMethod DispMethodEnum => EDispMethod.GdiPlus; public EDispMethod DispMethodEnum => EDispMethod.GdiPlus;
public void Dispose() public void Dispose()
=> BufferedGraphicsContext.Dispose(); {
}
internal SDGraphics GetCurrentGraphics()
=> CurRenderTarget?.TextureGraphics ?? _controlRenderTarget.BufferedGraphics.Graphics;
public void ClearColor(Color color) public void ClearColor(Color color)
=> GetCurrentGraphics().Clear(color); => GetCurrentGraphics().Clear(color);
@ -118,62 +125,21 @@ namespace BizHawk.Bizware.Graphics
{ {
} }
public void FreeRenderTarget(RenderTarget rt) public IRenderTarget CreateRenderTarget(int width, int height)
{ => new GDIPlusRenderTarget(this, width, height);
var grt = (GDIPlusRenderTarget)rt.Opaque;
grt.Dispose();
}
public RenderTarget CreateRenderTarget(int width, int height) public void BindDefaultRenderTarget()
{ => CurRenderTarget = null;
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 BindRenderTarget(RenderTarget rt) public GDIPlusControlRenderTarget CreateControlRenderTarget(Func<(SDGraphics Graphics, Rectangle Rectangle)> getControlRenderContext)
{
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)
{ {
if (_controlRenderTarget != null) if (_controlRenderTarget != null)
{ {
throw new InvalidOperationException($"{nameof(IGL_GDIPlus)} can only have one control render target"); throw new InvalidOperationException($"{nameof(IGL_GDIPlus)} can only have one control render target");
} }
_controlRenderTarget = new(() => BufferedGraphicsContext, getControlRenderContext); _controlRenderTarget = new(getControlRenderContext);
return _controlRenderTarget; 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; } EDispMethod DispMethodEnum { get; }
/// <summary> /// <summary>
/// Clears the color buffer with the specified color /// Clears the currently bound render target with the specified color
/// </summary> /// </summary>
void ClearColor(Color color); void ClearColor(Color color);
/// <summary> /// <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> /// </summary>
Shader CreateFragmentShader(string source, string entry, bool required); Shader CreateFragmentShader(string source, string entry, bool required);
/// <summary> /// <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> /// </summary>
Shader CreateVertexShader(string source, string entry, bool required); 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); void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4x4 mat, bool transpose);
/// <summary> /// <summary>
/// sets a uniform value /// Sets a uniform value
/// </summary> /// </summary>
void SetPipelineUniform(PipelineUniform uniform, Vector4 value); void SetPipelineUniform(PipelineUniform uniform, Vector4 value);
/// <summary> /// <summary>
/// sets a uniform value /// Sets a uniform value
/// </summary> /// </summary>
void SetPipelineUniform(PipelineUniform uniform, Vector2 value); void SetPipelineUniform(PipelineUniform uniform, Vector2 value);
/// <summary> /// <summary>
/// sets a uniform value /// Sets a uniform value
/// </summary> /// </summary>
void SetPipelineUniform(PipelineUniform uniform, float value); void SetPipelineUniform(PipelineUniform uniform, float value);
/// <summary> /// <summary>
/// sets uniform values /// Sets uniform values
/// </summary> /// </summary>
void SetPipelineUniform(PipelineUniform uniform, Vector4[] values); void SetPipelineUniform(PipelineUniform uniform, Vector4[] values);
/// <summary> /// <summary>
/// sets a uniform value /// Sets a uniform value
/// </summary> /// </summary>
void SetPipelineUniform(PipelineUniform uniform, bool value); void SetPipelineUniform(PipelineUniform uniform, bool value);
/// <summary> /// <summary>
/// Draws based on the currently set pipeline /// Draws based on the currently set pipeline
/// data contains vertexes based on the pipeline's VertexLayout /// Data contains vertexes based on the pipeline's VertexLayout
/// count is the vertex count /// Count is the vertex count
/// Vertexes must form triangle strips /// Vertexes must form triangle strips
/// </summary> /// </summary>
void Draw(IntPtr data, int count); void Draw(IntPtr data, int count);
/// <summary> /// <summary>
/// creates a vertex layout resource /// Creates a vertex layout resource
/// </summary> /// </summary>
VertexLayout CreateVertexLayout(); VertexLayout CreateVertexLayout();
@ -119,35 +119,32 @@ namespace BizHawk.Bizware.Graphics
ITexture2D WrapGLTexture2D(int glTexId, int width, int height); ITexture2D WrapGLTexture2D(int glTexId, int width, int height);
/// <summary> /// <summary>
/// sets the viewport (and scissor) according to the provided specifications /// Sets the viewport (and scissor) according to the provided specifications
/// </summary> /// </summary>
void SetViewport(int x, int y, int width, int height); void SetViewport(int x, int y, int width, int height);
/// <summary> /// <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> /// </summary>
Matrix4x4 CreateGuiProjectionMatrix(int width, int height); Matrix4x4 CreateGuiProjectionMatrix(int width, int height);
/// <summary> /// <summary>
/// generates a proper view transform for a standard 2d ortho projection, including half-pixel jitter if necessary and /// Generates a proper view transform for a standard 2D othographic projection, including half-pixel jitter if necessary
/// re-establishing of a normal 2d graphics top-left origin. suitable for use in a GUI /// and re-establishing of a normal 2D graphics top-left origin. Suitable for use in a GUI
/// </summary> /// </summary>
Matrix4x4 CreateGuiViewMatrix(int width, int height, bool autoflip = true); Matrix4x4 CreateGuiViewMatrix(int width, int height, bool autoflip = true);
/// <summary> /// <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> /// </summary>
RenderTarget CreateRenderTarget(int width, int height); IRenderTarget CreateRenderTarget(int width, int height);
/// <summary> /// <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> /// </summary>
void BindRenderTarget(RenderTarget rt); void BindDefaultRenderTarget();
/// <summary>
/// Frees the provided render target. Same as disposing the resource.
/// </summary>
void FreeRenderTarget(RenderTarget rt);
/// <summary> /// <summary>
/// Frees the provided pipeline. Same as disposing the resource. /// 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 // rendering state
private Pipeline _currPipeline; private Pipeline _currPipeline;
private RenderTarget _currRenderTarget; internal bool DefaultRenderTargetBound;
// this IGL either requires at least OpenGL 3.0 // this IGL either requires at least OpenGL 3.0
public static bool Available => OpenGLVersion.SupportsVersion(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) public ITexture2D WrapGLTexture2D(int glTexId, int width, int height)
=> new OpenGLTexture2D(GL, (uint)glTexId, width, 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> /// <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); GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
DefaultRenderTargetBound = true;
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);
}
} }
public Matrix4x4 CreateGuiProjectionMatrix(int width, int height) public Matrix4x4 CreateGuiProjectionMatrix(int width, int height)
@ -511,7 +475,7 @@ namespace BizHawk.Bizware.Graphics
ret.M22 = -1.0f; ret.M22 = -1.0f;
ret.M41 = width * -0.5f; ret.M41 = width * -0.5f;
ret.M42 = height * 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.M22 = 1.0f;
ret.M42 *= -1; 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; IsUpsideDown = true;
} }
public void Dispose() public virtual void Dispose()
=> GL.DeleteTexture(TexID); => GL.DeleteTexture(TexID);
public unsafe BitmapBuffer Resolve() 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 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); return _callback(size);
} }
public DisplayManagerRenderTargetProvider(Func<Size, RenderTarget> callback) public DisplayManagerRenderTargetProvider(Func<Size, IRenderTarget> callback)
{ {
_callback = callback; _callback = callback;
} }
@ -864,7 +864,7 @@ namespace BizHawk.Client.Common
public void Blank() public void Blank()
{ {
ActivateGraphicsControlContext(); ActivateGraphicsControlContext();
_gl.BindRenderTarget(null); _gl.BindDefaultRenderTarget();
_gl.ClearColor(Color.Black); _gl.ClearColor(Color.Black);
SwapBuffersOfGraphicsControl(); SwapBuffersOfGraphicsControl();
} }
@ -880,11 +880,11 @@ namespace BizHawk.Client.Common
RunFilterChainSteps(ref rtCounter, out var rtCurr, out _); RunFilterChainSteps(ref rtCounter, out var rtCurr, out _);
job.OffscreenBb = rtCurr.Texture2D.Resolve(); job.OffscreenBb = rtCurr.Resolve();
job.OffscreenBb.DiscardAlpha(); 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; ITexture2D texCurr = null;
rtCurr = null; rtCurr = null;
@ -907,7 +907,7 @@ namespace BizHawk.Client.Common
break; break;
case FilterProgram.ProgramStepType.FinalTarget: case FilterProgram.ProgramStepType.FinalTarget:
_currentFilterProgram.CurrRenderTarget = rtCurr = null; _currentFilterProgram.CurrRenderTarget = rtCurr = null;
_gl.BindRenderTarget(rtCurr); _gl.BindDefaultRenderTarget();
inFinalTarget = true; inFinalTarget = true;
break; break;
default: default:

View File

@ -38,7 +38,7 @@ namespace BizHawk.Client.Common.FilterManager
public interface IRenderTargetProvider public interface IRenderTargetProvider
{ {
RenderTarget Get(Size size); IRenderTarget Get(Size size);
} }
public class FilterProgram public class FilterProgram
@ -69,9 +69,9 @@ namespace BizHawk.Client.Common.FilterManager
public IGL GL; public IGL GL;
public IRenderTargetProvider RenderTargetProvider; 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)); 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? // 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); return FilterProgram.GetTempTarget(width, height);
} }

View File

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

View File

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

View File

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

View File

@ -40,7 +40,7 @@ namespace BizHawk.Client.EmuHawk
private readonly IGL _gl; private readonly IGL _gl;
private readonly GraphicsControl _graphicsControl; private readonly GraphicsControl _graphicsControl;
private RenderTarget _rt; private IRenderTarget _rt;
private readonly GuiRenderer _guiRenderer; private readonly GuiRenderer _guiRenderer;
protected override void OnPaint(PaintEventArgs e) protected override void OnPaint(PaintEventArgs e)
@ -75,7 +75,7 @@ namespace BizHawk.Client.EmuHawk
{ {
if (_retain) if (_retain)
{ {
_rt.Unbind(); _gl.BindDefaultRenderTarget();
} }
_graphicsControl.End(); _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. // 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(); Draw();
} }
@ -104,9 +104,9 @@ namespace BizHawk.Client.EmuHawk
_guiRenderer.Begin(Width, Height); _guiRenderer.Begin(Width, Height);
_guiRenderer.DisableBlending(); _guiRenderer.DisableBlending();
_guiRenderer.Draw(_rt.Texture2D); _guiRenderer.Draw(_rt);
_guiRenderer.End(); _guiRenderer.End();
_graphicsControl.SwapBuffers(); _graphicsControl.SwapBuffers();
} }
} }
} }