diff --git a/src/BizHawk.Bizware.Graphics.Controls/Controls/OpenGLControl.cs b/src/BizHawk.Bizware.Graphics.Controls/Controls/OpenGLControl.cs index c9c5a6c3544..ae3d38b3a95 100644 --- a/src/BizHawk.Bizware.Graphics.Controls/Controls/OpenGLControl.cs +++ b/src/BizHawk.Bizware.Graphics.Controls/Controls/OpenGLControl.cs @@ -7,10 +7,13 @@ namespace BizHawk.Bizware.Graphics.Controls { internal sealed class OpenGLControl : GraphicsControl { - public SDL2OpenGLContext Context { get; private set; } + private readonly Action _initGLState; + private SDL2OpenGLContext _context; - public OpenGLControl() + public OpenGLControl(Action initGLState) { + _initGLState = initGLState; + // according to OpenTK, these are the styles we want to set SetStyle(ControlStyles.Opaque, true); SetStyle(ControlStyles.UserPaint, true); @@ -41,14 +44,15 @@ protected override CreateParams CreateParams protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); - Context = new(Handle, 3, 2, true); + _context = new(Handle, 3, 2, true); + _initGLState(); } protected override void OnHandleDestroyed(EventArgs e) { base.OnHandleDestroyed(e); - Context.Dispose(); - Context = null; + _context.Dispose(); + _context = null; } private void MakeContextCurrent() @@ -58,13 +62,13 @@ private void MakeContextCurrent() throw new ObjectDisposedException(nameof(OpenGLControl)); } - if (Context is null) + if (_context is null) { CreateControl(); } else { - Context.MakeContextCurrent(); + _context.MakeContextCurrent(); } } @@ -76,23 +80,19 @@ public override void AllowTearing(bool state) public override void SetVsync(bool state) { MakeContextCurrent(); - Context.SetVsync(state); + _context.SetVsync(state); } public override void Begin() - { - MakeContextCurrent(); - } + => MakeContextCurrent(); public override void End() - { - SDL2OpenGLContext.MakeNoneCurrent(); - } + => SDL2OpenGLContext.MakeNoneCurrent(); public override void SwapBuffers() { MakeContextCurrent(); - Context.SwapBuffers(); + _context.SwapBuffers(); } } -} \ No newline at end of file +} diff --git a/src/BizHawk.Bizware.Graphics.Controls/GraphicsControlFactory.cs b/src/BizHawk.Bizware.Graphics.Controls/GraphicsControlFactory.cs index e40f8675862..49085d304d1 100644 --- a/src/BizHawk.Bizware.Graphics.Controls/GraphicsControlFactory.cs +++ b/src/BizHawk.Bizware.Graphics.Controls/GraphicsControlFactory.cs @@ -11,7 +11,7 @@ public static GraphicsControl CreateGraphicsControl(IGL gl) { GraphicsControl ret = gl switch { - IGL_OpenGL => new OpenGLControl(), + IGL_OpenGL openGL => new OpenGLControl(openGL.InitGLState), IGL_D3D11 d3d11 => new D3D11Control(d3d11.CreateSwapChain), IGL_GDIPlus gdiPlus => new GDIPlusControl(gdiPlus.CreateControlRenderTarget), _ => throw new InvalidOperationException() diff --git a/src/BizHawk.Bizware.Graphics/D3D11/D3D11Resources.cs b/src/BizHawk.Bizware.Graphics/D3D11/D3D11Resources.cs index 590eeba7e29..f9fb4e43f11 100644 --- a/src/BizHawk.Bizware.Graphics/D3D11/D3D11Resources.cs +++ b/src/BizHawk.Bizware.Graphics/D3D11/D3D11Resources.cs @@ -26,13 +26,14 @@ internal sealed class D3D11Resources : IDisposable public ID3D11RasterizerState RasterizerState; public FeatureLevel DeviceFeatureLevel; + public int MaxTextureDimension; public bool PresentAllowTearing; public D3D11RenderTarget CurRenderTarget; public D3D11Pipeline CurPipeline; - public readonly HashSet Textures = new(); - public readonly HashSet Pipelines = new(); + public readonly HashSet Textures = [ ]; + public readonly HashSet Pipelines = [ ]; public void CreateResources() { @@ -99,6 +100,14 @@ public void CreateResources() DeviceFeatureLevel = Device.FeatureLevel; + MaxTextureDimension = DeviceFeatureLevel switch + { + FeatureLevel.Level_9_1 or FeatureLevel.Level_9_2 => 2048, + FeatureLevel.Level_9_3 => 4096, + FeatureLevel.Level_10_0 or FeatureLevel.Level_10_1 => 8192, + _ => ID3D11Resource.MaximumTexture2DSize, + }; + var rd = new RasterizerDescription { CullMode = CullMode.None, diff --git a/src/BizHawk.Bizware.Graphics/D3D11/IGL_D3D11.cs b/src/BizHawk.Bizware.Graphics/D3D11/IGL_D3D11.cs index 43eb8c99e82..2eb2559eac7 100644 --- a/src/BizHawk.Bizware.Graphics/D3D11/IGL_D3D11.cs +++ b/src/BizHawk.Bizware.Graphics/D3D11/IGL_D3D11.cs @@ -182,6 +182,8 @@ public void Dispose() _resources.Dispose(); } + public int MaxTextureDimension => _resources.MaxTextureDimension; + public void ClearColor(Color color) => Context.ClearRenderTargetView(CurRenderTarget?.RTV ?? _controlSwapChain.RTV, new(color.R, color.B, color.G, color.A)); diff --git a/src/BizHawk.Bizware.Graphics/GDIPlus/IGL_GDIPlus.cs b/src/BizHawk.Bizware.Graphics/GDIPlus/IGL_GDIPlus.cs index 967866482fa..be0e386172d 100644 --- a/src/BizHawk.Bizware.Graphics/GDIPlus/IGL_GDIPlus.cs +++ b/src/BizHawk.Bizware.Graphics/GDIPlus/IGL_GDIPlus.cs @@ -19,6 +19,9 @@ public void Dispose() { } + // maximum bitmap size doesn't seem to be well defined... we'll just use D3D11's maximum size + public int MaxTextureDimension => 16384; + internal SDGraphics GetCurrentGraphics() => CurRenderTarget?.TextureGraphics ?? _controlRenderTarget.BufferedGraphics.Graphics; diff --git a/src/BizHawk.Bizware.Graphics/Interfaces/IGL.cs b/src/BizHawk.Bizware.Graphics/Interfaces/IGL.cs index d62624bca9c..346871fb9a6 100644 --- a/src/BizHawk.Bizware.Graphics/Interfaces/IGL.cs +++ b/src/BizHawk.Bizware.Graphics/Interfaces/IGL.cs @@ -15,6 +15,12 @@ public interface IGL : IDisposable /// EDispMethod DispMethodEnum { get; } + /// + /// Returns the maximum size any dimension of a texture may have + /// This should be set on init, and therefore shouldn't need a graphics context active... + /// + int MaxTextureDimension { get; } + /// /// Creates a texture with the specified dimensions /// The texture will use a clamping address mode and nearest neighbor filtering by default diff --git a/src/BizHawk.Bizware.Graphics/OpenGL/IGL_OpenGL.cs b/src/BizHawk.Bizware.Graphics/OpenGL/IGL_OpenGL.cs index d3b2653097e..fcbee07e387 100644 --- a/src/BizHawk.Bizware.Graphics/OpenGL/IGL_OpenGL.cs +++ b/src/BizHawk.Bizware.Graphics/OpenGL/IGL_OpenGL.cs @@ -35,6 +35,22 @@ public IGL_OpenGL() public void Dispose() => GL.Dispose(); + /// + /// Should be called once the GL context is created + /// + public void InitGLState() + { + GL.GetInteger(GetPName.MaxTextureSize, out var maxTextureDimension); + if (maxTextureDimension == 0) + { + throw new($"Failed to get max texture size, GL error: {GL.GetError()}"); + } + + MaxTextureDimension = maxTextureDimension; + } + + public int MaxTextureDimension { get; private set; } + public void ClearColor(Color color) { GL.ClearColor(color); diff --git a/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs b/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs index 4abed963236..caa38a4ba45 100644 --- a/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs +++ b/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs @@ -304,6 +304,10 @@ private FilterProgram BuildDefaultChain(Size chainInSize, Size chainOutSize, boo if (size.Width < 1) size.Width = 1; if (size.Height < 1) size.Height = 1; + // if either of the dimensions exceed the maximum size of a texture, we need to constrain them + size.Width = Math.Min(size.Width, _gl.MaxTextureDimension); + size.Height = Math.Min(size.Height, _gl.MaxTextureDimension); + var fPadding = new FinalPresentation(size); chain.AddFilter(fPadding, "padding"); fPadding.Config_PadOnly = true; @@ -771,10 +775,14 @@ private FilterProgram UpdateSourceInternal(JobInfo job) vw += padding.Horizontal; vh += padding.Vertical; - //in case the user requested so much padding that the dimensions are now negative, just turn it to something small. + // in case the user requested so much padding that the dimensions are now negative, just turn it to something small. if (vw < 1) vw = 1; if (vh < 1) vh = 1; + // if either of the dimensions exceed the maximum size of a texture, we need to constrain them + vw = Math.Min(vw, _gl.MaxTextureDimension); + vh = Math.Min(vh, _gl.MaxTextureDimension); + BitmapBuffer bb = null; ITexture2D videoTexture = null; if (!simulate) diff --git a/src/BizHawk.Client.Common/DisplayManager/Filters/Gui.cs b/src/BizHawk.Client.Common/DisplayManager/Filters/Gui.cs index 21357697ce7..0941f33ac32 100644 --- a/src/BizHawk.Client.Common/DisplayManager/Filters/Gui.cs +++ b/src/BizHawk.Client.Common/DisplayManager/Filters/Gui.cs @@ -452,9 +452,22 @@ public override void Initialize() public override void SetInputFormat(string channel, SurfaceState state) { - var outputSize = state.SurfaceFormat.Size; - outputSize.Width *= Scale; - outputSize.Height *= Scale; + var inputSize = state.SurfaceFormat.Size; + var outputSize = new Size(inputSize.Width * Scale, inputSize.Height * Scale); + var maxTexDimension = FilterProgram.GL.MaxTextureDimension; + while (outputSize.Width > maxTexDimension || outputSize.Height > maxTexDimension) + { + outputSize.Width -= inputSize.Width; + outputSize.Height -= inputSize.Height; + Scale--; + } + + // this hopefully never happens + if (outputSize.Width == 0 || outputSize.Height == 0) + { + throw new InvalidOperationException("Prescale input was too large for a texture"); + } + var ss = new SurfaceState(new(outputSize), SurfaceDisposition.RenderTarget); DeclareOutput(ss, channel); } diff --git a/src/BizHawk.Client.EmuHawk/Program.cs b/src/BizHawk.Client.EmuHawk/Program.cs index 35bd84e55b1..cee0661214e 100644 --- a/src/BizHawk.Client.EmuHawk/Program.cs +++ b/src/BizHawk.Client.EmuHawk/Program.cs @@ -254,8 +254,9 @@ IGL CheckRenderer(IGL gl) // need to have a context active for checking renderer, will be disposed afterwards using (new SDL2OpenGLContext(3, 2, true)) { - using var testIgl = new IGL_OpenGL(); - _ = CheckRenderer(testIgl); + using var testOpenGL = new IGL_OpenGL(); + testOpenGL.InitGLState(); + _ = CheckRenderer(testOpenGL); } // don't return the same IGL, we don't want the test context to be part of this IGL