From c0ee346c1c67f3ffc348f9ecaf35a2bc0c1e4890 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 28 Aug 2019 00:31:08 +0100 Subject: [PATCH] Render via an intermediate frame buffer. --- OpenRA.Game/Graphics/PlatformInterfaces.cs | 4 +- OpenRA.Game/Graphics/SpriteRenderer.cs | 4 +- OpenRA.Game/Renderer.cs | 65 +++++++++++++++---- .../Sdl2GraphicsContext.cs | 9 ++- .../Sdl2PlatformWindow.cs | 2 +- 5 files changed, 63 insertions(+), 21 deletions(-) diff --git a/OpenRA.Game/Graphics/PlatformInterfaces.cs b/OpenRA.Game/Graphics/PlatformInterfaces.cs index 25c66c249b..3bf522d431 100644 --- a/OpenRA.Game/Graphics/PlatformInterfaces.cs +++ b/OpenRA.Game/Graphics/PlatformInterfaces.cs @@ -41,6 +41,8 @@ namespace OpenRA Size WindowSize { get; } float WindowScale { get; } + Size SurfaceSize { get; } + event Action OnWindowScaleChanged; void PumpInput(IInputHandler inputHandler); @@ -62,7 +64,7 @@ namespace OpenRA IFrameBuffer CreateFrameBuffer(Size s); IFrameBuffer CreateFrameBuffer(Size s, Color clearColor); IShader CreateShader(string name); - void EnableScissor(int left, int top, int width, int height); + void EnableScissor(int x, int y, int width, int height); void DisableScissor(); void SaveScreenshot(string path); void Present(); diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs index 88d6d2d8e7..1193f449c8 100644 --- a/OpenRA.Game/Graphics/SpriteRenderer.cs +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -160,9 +160,9 @@ namespace OpenRA.Graphics shader.SetVec("Scroll", scroll.X, scroll.Y, scroll.Y); shader.SetVec("r1", zoom * 2f / screen.Width, - -zoom * 2f / screen.Height, + zoom * 2f / screen.Height, -depthScale * zoom / screen.Height); - shader.SetVec("r2", -1, 1, 1 - depthOffset); + shader.SetVec("r2", -1, -1, 1 - depthOffset); // Texture index is sampled as a float, so convert to pixels then scale shader.SetVec("DepthTextureScale", 128 * depthScale * zoom / screen.Height); diff --git a/OpenRA.Game/Renderer.cs b/OpenRA.Game/Renderer.cs index 049f65e667..e0b7b616bd 100644 --- a/OpenRA.Game/Renderer.cs +++ b/OpenRA.Game/Renderer.cs @@ -38,13 +38,16 @@ namespace OpenRA readonly IVertexBuffer tempBuffer; readonly Stack scissorState = new Stack(); + IFrameBuffer screenBuffer; + Sprite screenSprite; + SheetBuilder fontSheetBuilder; readonly IPlatform platform; float depthScale; float depthOffset; - Size lastResolution = new Size(-1, -1); + Size lastBufferSize = new Size(-1, -1); int2 lastScroll = new int2(-1, -1); float lastZoom = -1f; ITexture currentPaletteTexture; @@ -123,26 +126,55 @@ namespace OpenRA public void BeginFrame(int2 scroll, float zoom) { Context.Clear(); + + var surfaceSize = Window.SurfaceSize; + var surfaceBufferSize = surfaceSize.NextPowerOf2(); + + if (screenSprite == null || screenSprite.Sheet.Size != surfaceBufferSize) + { + if (screenBuffer != null) + screenBuffer.Dispose(); + + // Render the screen into a frame buffer to simplify reading back screenshots + screenBuffer = Context.CreateFrameBuffer(surfaceBufferSize, Color.FromArgb(0xFF, 0, 0, 0)); + } + + if (screenSprite == null || surfaceSize.Width != screenSprite.Bounds.Width || -surfaceSize.Height != screenSprite.Bounds.Height) + { + var screenSheet = new Sheet(SheetType.BGRA, screenBuffer.Texture); + + // Flip sprite in Y to match OpenGL's bottom-left origin + var screenBounds = Rectangle.FromLTRB(0, surfaceSize.Height, surfaceSize.Width, 0); + screenSprite = new Sprite(screenSheet, screenBounds, TextureChannel.RGBA); + } + + screenBuffer.Bind(); SetViewportParams(scroll, zoom); } public void SetViewportParams(int2 scroll, float zoom) { - // PERF: Calling SetViewportParams on each renderer is slow. Only call it when things change. - var resolutionChanged = lastResolution != Resolution; - if (resolutionChanged) - { - lastResolution = Resolution; - SpriteRenderer.SetViewportParams(lastResolution, 0f, 0f, 1f, int2.Zero); - } + // In HiDPI windows we follow Apple's convention of defining window coordinates as for standard resolution windows + // but to have a higher resolution backing surface with more than 1 texture pixel per viewport pixel. + // We must convert the surface buffer size to a viewport size - in general this is NOT just the window size + // rounded to the next power of two, as the NextPowerOf2 calculation is done in the surface pixel coordinates + var scale = Window.WindowScale; + var surfaceBufferSize = Window.SurfaceSize.NextPowerOf2(); + var bufferSize = new Size((int)(surfaceBufferSize.Width / scale), (int)(surfaceBufferSize.Height / scale)); - // If zoom evaluates as different due to floating point weirdness that's OK, setting the parameters again is harmless. - if (resolutionChanged || lastScroll != scroll || lastZoom != zoom) + // PERF: Calling SetViewportParams on each renderer is slow. Only call it when things change. + // If zoom evaluates as different due to floating point weirdness that's OK, it will be going away soon + if (lastBufferSize != bufferSize || lastScroll != scroll || lastZoom != zoom) { + if (lastBufferSize != bufferSize) + SpriteRenderer.SetViewportParams(bufferSize, 0f, 0f, 1f, int2.Zero); + + WorldSpriteRenderer.SetViewportParams(bufferSize, depthScale, depthOffset, zoom, scroll); + WorldModelRenderer.SetViewportParams(bufferSize, zoom, scroll); + + lastBufferSize = bufferSize; lastScroll = scroll; lastZoom = zoom; - WorldSpriteRenderer.SetViewportParams(lastResolution, depthScale, depthOffset, zoom, scroll); - WorldModelRenderer.SetViewportParams(lastResolution, zoom, scroll); } } @@ -162,6 +194,15 @@ namespace OpenRA public void EndFrame(IInputHandler inputHandler) { Flush(); + + screenBuffer.Unbind(); + + // Render the compositor buffer to the screen + // HACK / PERF: Fudge the coordinates to cover the actual window while keeping the buffer viewport parameters + // This saves us two redundant (and expensive) SetViewportParams each frame + RgbaSpriteRenderer.DrawSprite(screenSprite, new float3(0, lastBufferSize.Height, 0), new float3(lastBufferSize.Width, -lastBufferSize.Height, 0)); + Flush(); + Window.PumpInput(inputHandler); Context.Present(); } diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs index dcadf3d3b8..570592c9d7 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs @@ -83,7 +83,7 @@ namespace OpenRA.Platforms.Default return new Shader(name); } - public void EnableScissor(int left, int top, int width, int height) + public void EnableScissor(int x, int y, int width, int height) { VerifyThreadAffinity(); @@ -97,16 +97,15 @@ namespace OpenRA.Platforms.Default var windowScale = window.WindowScale; var surfaceSize = window.SurfaceSize; - var bottom = windowSize.Height - (top + height); if (windowSize != surfaceSize) { - left = (int)Math.Round(windowScale * left); - bottom = (int)Math.Round(windowScale * bottom); + x = (int)Math.Round(windowScale * x); + y = (int)Math.Round(windowScale * y); width = (int)Math.Round(windowScale * width); height = (int)Math.Round(windowScale * height); } - OpenGL.glScissor(left, bottom, width, height); + OpenGL.glScissor(x, y, width, height); OpenGL.CheckGLError(); OpenGL.glEnable(OpenGL.GL_SCISSOR_TEST); OpenGL.CheckGLError(); diff --git a/OpenRA.Platforms.Default/Sdl2PlatformWindow.cs b/OpenRA.Platforms.Default/Sdl2PlatformWindow.cs index 6aac86eee1..820e1f6a64 100644 --- a/OpenRA.Platforms.Default/Sdl2PlatformWindow.cs +++ b/OpenRA.Platforms.Default/Sdl2PlatformWindow.cs @@ -59,7 +59,7 @@ namespace OpenRA.Platforms.Default } } - internal Size SurfaceSize + public Size SurfaceSize { get {