From 2a569dc062fe24ceea52f5a275e59aaefcea3d1d Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 11 Jun 2016 15:18:31 +0100 Subject: [PATCH] implement a ping-pong framebuffer --- data/shaders/copyframebuffer.frag | 13 +++ data/shaders/copyframebuffer.vert | 42 +++++++++ openrct2.vcxproj | 6 ++ .../engines/opengl/CopyFramebufferShader.cpp | 86 +++++++++++++++++++ .../engines/opengl/CopyFramebufferShader.h | 48 +++++++++++ .../engines/opengl/DrawImageMaskedShader.cpp | 2 +- .../engines/opengl/DrawImageShader.cpp | 2 +- src/drawing/engines/opengl/FillRectShader.cpp | 2 +- .../engines/opengl/OpenGLDrawingEngine.cpp | 46 +++++----- .../engines/opengl/OpenGLFramebuffer.cpp | 2 +- .../engines/opengl/OpenGLFramebuffer.h | 2 +- .../engines/opengl/SwapFramebuffer.cpp | 68 +++++++++++++++ src/drawing/engines/opengl/SwapFramebuffer.h | 70 +++++++++++++++ 13 files changed, 363 insertions(+), 26 deletions(-) create mode 100644 data/shaders/copyframebuffer.frag create mode 100644 data/shaders/copyframebuffer.vert create mode 100644 src/drawing/engines/opengl/CopyFramebufferShader.cpp create mode 100644 src/drawing/engines/opengl/CopyFramebufferShader.h create mode 100644 src/drawing/engines/opengl/SwapFramebuffer.cpp create mode 100644 src/drawing/engines/opengl/SwapFramebuffer.h diff --git a/data/shaders/copyframebuffer.frag b/data/shaders/copyframebuffer.frag new file mode 100644 index 0000000000..65071fff10 --- /dev/null +++ b/data/shaders/copyframebuffer.frag @@ -0,0 +1,13 @@ +#version 330 + +uniform sampler2D uTexture; + +in vec2 fPosition; +in vec2 fTextureCoordinate; + +layout (location = 0) out vec4 oColour; + +void main() +{ + oColour = texture(uTexture, fTextureCoordinate); +} diff --git a/data/shaders/copyframebuffer.vert b/data/shaders/copyframebuffer.vert new file mode 100644 index 0000000000..577ec0ef2b --- /dev/null +++ b/data/shaders/copyframebuffer.vert @@ -0,0 +1,42 @@ +#version 330 + +uniform ivec2 uScreenSize; +uniform ivec4 uBounds; +uniform ivec4 uTextureCoordinates; + +in int vIndex; + +out vec2 fPosition; +out vec2 fTextureCoordinate; + +void main() +{ + vec2 pos; + switch (vIndex) { + case 0: + pos = uBounds.xy; + fTextureCoordinate = uTextureCoordinates.xy; + break; + case 1: + pos = uBounds.zy; + fTextureCoordinate = uTextureCoordinates.zy; + break; + case 2: + pos = uBounds.zw; + fTextureCoordinate = uTextureCoordinates.zw; + break; + case 3: + pos = uBounds.xw; + fTextureCoordinate = uTextureCoordinates.xw; + break; + } + + fPosition = pos; + + // Transform screen coordinates to viewport + pos.x = (pos.x * (2.0 / uScreenSize.x)) - 1.0; + pos.y = (pos.y * (2.0 / uScreenSize.y)) - 1.0; + pos.y *= -1; + + gl_Position = vec4(pos, 0.0, 1.0); +} diff --git a/openrct2.vcxproj b/openrct2.vcxproj index c048c39194..d9475594b5 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -16,6 +16,8 @@ + + @@ -48,6 +50,7 @@ + @@ -55,6 +58,7 @@ + @@ -349,6 +353,7 @@ + @@ -356,6 +361,7 @@ + diff --git a/src/drawing/engines/opengl/CopyFramebufferShader.cpp b/src/drawing/engines/opengl/CopyFramebufferShader.cpp new file mode 100644 index 0000000000..943ab6bdcc --- /dev/null +++ b/src/drawing/engines/opengl/CopyFramebufferShader.cpp @@ -0,0 +1,86 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#ifndef DISABLE_OPENGL + +#include "CopyFramebufferShader.h" + +CopyFramebufferShader::CopyFramebufferShader() : OpenGLShaderProgram("copyframebuffer") +{ + GetLocations(); + + glGenBuffers(1, &_vbo); + glGenVertexArrays(1, &_vao); + + vec2i vertices[] = { 0, 1, 2, 3 }; + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindVertexArray(_vao); + glEnableVertexAttribArray(vIndex); + glVertexAttribIPointer(vIndex, 1, GL_INT, 0, nullptr); + + Use(); + SetTextureCoordinates(0, 0, 1, 1); +} + +CopyFramebufferShader::~CopyFramebufferShader() +{ + glDeleteBuffers(1, &_vbo); + glDeleteVertexArrays(1, &_vao); + + glBindVertexArray(_vao); +} + +void CopyFramebufferShader::GetLocations() +{ + uScreenSize = GetUniformLocation("uScreenSize"); + uBounds = GetUniformLocation("uBounds"); + uTextureCoordinates = GetUniformLocation("uTextureCoordinates"); + uTexture = GetUniformLocation("uTexture"); + + vIndex = GetAttributeLocation("vIndex"); +} + +void CopyFramebufferShader::SetScreenSize(sint32 width, sint32 height) +{ + glUniform2i(uScreenSize, width, height); +} + +void CopyFramebufferShader::SetBounds(sint32 left, sint32 top, sint32 right, sint32 bottom) +{ + glUniform4i(uBounds, left, top, right, bottom); +} + +void CopyFramebufferShader::SetTextureCoordinates(sint32 left, sint32 top, sint32 right, sint32 bottom) +{ + glUniform4i(uTextureCoordinates, left, top, right, bottom); +} + +void CopyFramebufferShader::SetTexture(GLuint texture) +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + glUniform1i(uTexture, 0); +} + +void CopyFramebufferShader::Draw() +{ + glBindVertexArray(_vao); + glDrawArrays(GL_QUADS, 0, 4); +} + +#endif /* DISABLE_OPENGL */ diff --git a/src/drawing/engines/opengl/CopyFramebufferShader.h b/src/drawing/engines/opengl/CopyFramebufferShader.h new file mode 100644 index 0000000000..2ba4cb3887 --- /dev/null +++ b/src/drawing/engines/opengl/CopyFramebufferShader.h @@ -0,0 +1,48 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#pragma once + +#include "GLSLTypes.h" +#include "OpenGLShaderProgram.h" + +class CopyFramebufferShader : public OpenGLShaderProgram +{ +private: + GLuint uScreenSize; + GLuint uBounds; + GLuint uTextureCoordinates; + GLuint uTexture; + + GLuint vIndex; + + GLuint _vbo; + GLuint _vao; + +public: + CopyFramebufferShader(); + ~CopyFramebufferShader() override; + + void SetScreenSize(sint32 width, sint32 height); + void SetBounds(sint32 left, sint32 top, sint32 right, sint32 bottom); + void SetTextureCoordinates(sint32 left, sint32 top, sint32 right, sint32 bottom); + void SetTexture(GLuint texture); + + void Draw(); + +private: + void GetLocations(); +}; diff --git a/src/drawing/engines/opengl/DrawImageMaskedShader.cpp b/src/drawing/engines/opengl/DrawImageMaskedShader.cpp index c548110fed..f7a1f1187a 100644 --- a/src/drawing/engines/opengl/DrawImageMaskedShader.cpp +++ b/src/drawing/engines/opengl/DrawImageMaskedShader.cpp @@ -27,7 +27,7 @@ DrawImageMaskedShader::DrawImageMaskedShader() : OpenGLShaderProgram("drawimagem vec2i vertices[] = { 0, 1, 2, 3 }; glBindBuffer(GL_ARRAY_BUFFER, _vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindVertexArray(_vao); glEnableVertexAttribArray(vIndex); diff --git a/src/drawing/engines/opengl/DrawImageShader.cpp b/src/drawing/engines/opengl/DrawImageShader.cpp index 6a42fa6e09..b45a52d0e8 100644 --- a/src/drawing/engines/opengl/DrawImageShader.cpp +++ b/src/drawing/engines/opengl/DrawImageShader.cpp @@ -27,7 +27,7 @@ DrawImageShader::DrawImageShader() : OpenGLShaderProgram("drawimage") vec2i vertices[] = { 0, 1, 2, 3 }; glBindBuffer(GL_ARRAY_BUFFER, _vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindVertexArray(_vao); glEnableVertexAttribArray(vIndex); diff --git a/src/drawing/engines/opengl/FillRectShader.cpp b/src/drawing/engines/opengl/FillRectShader.cpp index a24b98b2ca..417a35cb2e 100644 --- a/src/drawing/engines/opengl/FillRectShader.cpp +++ b/src/drawing/engines/opengl/FillRectShader.cpp @@ -27,7 +27,7 @@ FillRectShader::FillRectShader() : OpenGLShaderProgram("fillrect") vec2i vertices[] = { 0, 1, 2, 3 }; glBindBuffer(GL_ARRAY_BUFFER, _vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindVertexArray(_vao); glEnableVertexAttribArray(vIndex); diff --git a/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp b/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp index 8b0fd9dfe7..6f03fa3f07 100644 --- a/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp +++ b/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp @@ -32,9 +32,11 @@ IDrawingEngine * DrawingEngineFactory::CreateOpenGL() #include "GLSLTypes.h" #include "OpenGLAPI.h" #include "OpenGLFramebuffer.h" +#include "CopyFramebufferShader.h" #include "DrawImageShader.h" #include "DrawImageMaskedShader.h" #include "FillRectShader.h" +#include "SwapFramebuffer.h" #include "../../../core/Console.hpp" #include "../../../core/Exception.hpp" @@ -221,9 +223,9 @@ private: OpenGLDrawingContext * _drawingContext; - DrawImageShader * _drawImageShader = nullptr; - OpenGLFramebuffer * _screenFramebuffer = nullptr; - OpenGLFramebuffer * _canvasFramebuffer = nullptr; + CopyFramebufferShader * _copyFramebufferShader = nullptr; + OpenGLFramebuffer * _screenFramebuffer = nullptr; + SwapFramebuffer * _swapFramebuffer = nullptr; public: SDL_Color Palette[256]; @@ -236,7 +238,7 @@ public: ~OpenGLDrawingEngine() override { - delete _drawImageShader; + delete _copyFramebufferShader; delete _drawingContext; delete [] _bits; @@ -265,7 +267,7 @@ public: glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); - _drawImageShader = new DrawImageShader(); + _copyFramebufferShader = new CopyFramebufferShader(); } void Resize(uint32 width, uint32 height) override @@ -296,9 +298,9 @@ public: void Draw() override { assert(_screenFramebuffer != nullptr); - assert(_canvasFramebuffer != nullptr); + assert(_swapFramebuffer != nullptr); - _canvasFramebuffer->Bind(); + _swapFramebuffer->Bind(); if (gIntroState != INTRO_STATE_NONE) { intro_draw(&_bitsDPI); @@ -308,6 +310,8 @@ public: window_update_all(); gfx_draw_pickedup_peep(&_bitsDPI); + + _swapFramebuffer->SwapCopy(); rct2_draw(&_bitsDPI); } @@ -317,20 +321,20 @@ public: sint32 width = _screenFramebuffer->GetWidth(); sint32 height = _screenFramebuffer->GetHeight(); - _drawImageShader->Use(); - _drawImageShader->SetScreenSize(width, height); - _drawImageShader->SetClip(0, 0, width, height); - _drawImageShader->SetTexture(_canvasFramebuffer->GetTexture()); - _drawImageShader->SetTextureCoordinates(0, 1, 1, 0); - _drawImageShader->Draw(0, 0, width, height); + _copyFramebufferShader->Use(); + _copyFramebufferShader->SetTexture(_swapFramebuffer->GetTargetFramebuffer() + ->GetTexture()); + _copyFramebufferShader->Draw(); Display(); } sint32 Screenshot() override { - _canvasFramebuffer->Bind(); - void * pixels = _canvasFramebuffer->GetPixels(); + const OpenGLFramebuffer * framebuffer = _swapFramebuffer->GetTargetFramebuffer(); + framebuffer->Bind(); + void * pixels = framebuffer->GetPixels(); + int result = screenshot_dump_png_32bpp(_width, _height, pixels); Memory::Free(pixels); return result; @@ -425,13 +429,13 @@ private: _screenFramebuffer = new OpenGLFramebuffer(_window); // Re-create canvas framebuffer - delete _canvasFramebuffer; - _canvasFramebuffer = new OpenGLFramebuffer(_width, _height); + delete _swapFramebuffer; + _swapFramebuffer = new SwapFramebuffer(_width, _height); - _drawImageShader->Use(); - _drawImageShader->SetScreenSize(_width, _height); - _drawImageShader->SetClip(0, 0, _width, _height); - _drawImageShader->SetTexture(_canvasFramebuffer->GetTexture()); + _copyFramebufferShader->Use(); + _copyFramebufferShader->SetScreenSize(_width, _height); + _copyFramebufferShader->SetBounds(0, 0, _width, _height); + _copyFramebufferShader->SetTextureCoordinates(0, 1, 1, 0); } void Display() diff --git a/src/drawing/engines/opengl/OpenGLFramebuffer.cpp b/src/drawing/engines/opengl/OpenGLFramebuffer.cpp index 5cab9df4ae..c1e21e433c 100644 --- a/src/drawing/engines/opengl/OpenGLFramebuffer.cpp +++ b/src/drawing/engines/opengl/OpenGLFramebuffer.cpp @@ -52,7 +52,7 @@ OpenGLFramebuffer::~OpenGLFramebuffer() } } -void OpenGLFramebuffer::Bind() +void OpenGLFramebuffer::Bind() const { glBindFramebuffer(GL_FRAMEBUFFER, _id); glViewport(0, 0, (GLsizei)_width, (GLsizei)_height); diff --git a/src/drawing/engines/opengl/OpenGLFramebuffer.h b/src/drawing/engines/opengl/OpenGLFramebuffer.h index bab36dd67d..9e5c5054d3 100644 --- a/src/drawing/engines/opengl/OpenGLFramebuffer.h +++ b/src/drawing/engines/opengl/OpenGLFramebuffer.h @@ -38,6 +38,6 @@ public: GLuint GetHeight() const { return _height; } GLuint GetTexture() const { return _texture; } - void Bind(); + void Bind() const; void * GetPixels() const; }; diff --git a/src/drawing/engines/opengl/SwapFramebuffer.cpp b/src/drawing/engines/opengl/SwapFramebuffer.cpp new file mode 100644 index 0000000000..259733f241 --- /dev/null +++ b/src/drawing/engines/opengl/SwapFramebuffer.cpp @@ -0,0 +1,68 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#ifndef DISABLE_OPENGL + +#include "CopyFramebufferShader.h" +#include "OpenGLFramebuffer.h" +#include "SwapFramebuffer.h" + +SwapFramebuffer::SwapFramebuffer(sint32 width, sint32 height) +{ + _width = width; + _height = height; + _targetFramebufferIndex = 0; + _framebuffer[0] = new OpenGLFramebuffer(width, height); + _framebuffer[1] = new OpenGLFramebuffer(width, height); + _targetFramebuffer = _framebuffer[0]; + + _copyFramebufferShader = new CopyFramebufferShader(); + _copyFramebufferShader->Use(); + _copyFramebufferShader->SetScreenSize(_width, _height); + _copyFramebufferShader->SetBounds(0, 0, _width, _height); + _copyFramebufferShader->SetTextureCoordinates(0, 1, 1, 0); +} + +SwapFramebuffer::~SwapFramebuffer() +{ + delete _framebuffer[0]; + delete _framebuffer[1]; + delete _copyFramebufferShader; +} + +GLuint SwapFramebuffer::GetSourceTexture() const +{ + return _sourceFramebuffer->GetTexture(); +} + +void SwapFramebuffer::SwapCopy() +{ + _sourceFramebuffer = _targetFramebuffer; + _targetFramebufferIndex = (_targetFramebufferIndex + 1) & 1; + _targetFramebuffer = _framebuffer[_targetFramebufferIndex]; + _targetFramebuffer->Bind(); + + _copyFramebufferShader->Use(); + _copyFramebufferShader->SetTexture(GetSourceTexture()); + _copyFramebufferShader->Draw(); +} + +void SwapFramebuffer::Bind() +{ + _targetFramebuffer->Bind(); +} + +#endif /* DISABLE_OPENGL */ diff --git a/src/drawing/engines/opengl/SwapFramebuffer.h b/src/drawing/engines/opengl/SwapFramebuffer.h new file mode 100644 index 0000000000..68b638cd53 --- /dev/null +++ b/src/drawing/engines/opengl/SwapFramebuffer.h @@ -0,0 +1,70 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#pragma once + +#include "../../../common.h" +#include "OpenGLAPI.h" + +class CopyFramebufferShader; +class OpenGLFramebuffer; + +/** + * Class to maintain two different framebuffers where the active framebuffer + * will swap between the two, copying the other's pixels in the process for + * performing pre-processing filters. + * + * When you need to bind the current frame to a shader, call SwapCopy and + * then bind the value of GetSourceTexture to your shader. + */ +class SwapFramebuffer +{ +private: + sint32 _width; + sint32 _height; + uint8 _targetFramebufferIndex; + OpenGLFramebuffer * _targetFramebuffer; + OpenGLFramebuffer * _sourceFramebuffer; + OpenGLFramebuffer * _framebuffer[2]; + + CopyFramebufferShader * _copyFramebufferShader = nullptr; + +public: + SwapFramebuffer(sint32 width, sint32 height); + ~SwapFramebuffer(); + + /** + * Gets the current target framebuffer. + */ + const OpenGLFramebuffer * GetTargetFramebuffer() const { return _targetFramebuffer; } + + /** + * Gets the texture ID for the source framebuffer. + */ + GLuint GetSourceTexture() const; + + /** + * Swaps the target framebuffer, binds it and then draws the previous + * framebuffer resulting in the two buffers matching and ready for + * pre-processing. + */ + void SwapCopy(); + + /** + * Binds the current target framebuffer. + */ + void Bind(); +};