Render via an intermediate frame buffer.

This commit is contained in:
Paul Chote
2019-08-28 00:31:08 +01:00
committed by teinarss
parent d2298b6f04
commit c0ee346c1c
5 changed files with 63 additions and 21 deletions

View File

@@ -41,6 +41,8 @@ namespace OpenRA
Size WindowSize { get; }
float WindowScale { get; }
Size SurfaceSize { get; }
event Action<float, float> 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();

View File

@@ -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);

View File

@@ -38,13 +38,16 @@ namespace OpenRA
readonly IVertexBuffer<Vertex> tempBuffer;
readonly Stack<Rectangle> scissorState = new Stack<Rectangle>();
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();
}

View File

@@ -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();

View File

@@ -59,7 +59,7 @@ namespace OpenRA.Platforms.Default
}
}
internal Size SurfaceSize
public Size SurfaceSize
{
get
{