diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index 6b0c460d07..0d487ce5d3 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -119,7 +119,6 @@ D41B741D1C210A7A0080A7B9 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D41B741C1C210A7A0080A7B9 /* libiconv.tbd */; }; D41B74731C2125E50080A7B9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D41B74721C2125E50080A7B9 /* Assets.xcassets */; }; D43407D61D0E14BE00C2B3D4 /* CopyFramebufferShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D43407C01D0E14BE00C2B3D4 /* CopyFramebufferShader.cpp */; }; - D43407D71D0E14BE00C2B3D4 /* DrawImageMaskedShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D43407C21D0E14BE00C2B3D4 /* DrawImageMaskedShader.cpp */; }; D43407D81D0E14BE00C2B3D4 /* DrawImageShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D43407C41D0E14BE00C2B3D4 /* DrawImageShader.cpp */; }; D43407D91D0E14BE00C2B3D4 /* DrawLineShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D43407C61D0E14BE00C2B3D4 /* DrawLineShader.cpp */; }; D43407DA1D0E14BE00C2B3D4 /* FillRectShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D43407C81D0E14BE00C2B3D4 /* FillRectShader.cpp */; }; @@ -504,8 +503,6 @@ D41B74721C2125E50080A7B9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = distribution/osx/Assets.xcassets; sourceTree = SOURCE_ROOT; }; D43407C01D0E14BE00C2B3D4 /* CopyFramebufferShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CopyFramebufferShader.cpp; sourceTree = ""; }; D43407C11D0E14BE00C2B3D4 /* CopyFramebufferShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopyFramebufferShader.h; sourceTree = ""; }; - D43407C21D0E14BE00C2B3D4 /* DrawImageMaskedShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DrawImageMaskedShader.cpp; sourceTree = ""; }; - D43407C31D0E14BE00C2B3D4 /* DrawImageMaskedShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrawImageMaskedShader.h; sourceTree = ""; }; D43407C41D0E14BE00C2B3D4 /* DrawImageShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DrawImageShader.cpp; sourceTree = ""; }; D43407C51D0E14BE00C2B3D4 /* DrawImageShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrawImageShader.h; sourceTree = ""; }; D43407C61D0E14BE00C2B3D4 /* DrawLineShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DrawLineShader.cpp; sourceTree = ""; }; @@ -1300,8 +1297,6 @@ children = ( D43407C01D0E14BE00C2B3D4 /* CopyFramebufferShader.cpp */, D43407C11D0E14BE00C2B3D4 /* CopyFramebufferShader.h */, - D43407C21D0E14BE00C2B3D4 /* DrawImageMaskedShader.cpp */, - D43407C31D0E14BE00C2B3D4 /* DrawImageMaskedShader.h */, D43407C41D0E14BE00C2B3D4 /* DrawImageShader.cpp */, D43407C51D0E14BE00C2B3D4 /* DrawImageShader.h */, D43407C61D0E14BE00C2B3D4 /* DrawLineShader.cpp */, @@ -2410,7 +2405,6 @@ C686F9311CDBC3B7009F9BFC /* ghost_train.c in Sources */, D44272821CC81B3200D84D28 /* shortcut_key_change.c in Sources */, D442722A1CC81B3200D84D28 /* localisation.c in Sources */, - D43407D71D0E14BE00C2B3D4 /* DrawImageMaskedShader.cpp in Sources */, D44272731CC81B3200D84D28 /* new_ride.c in Sources */, D442721A1CC81B3200D84D28 /* console.c in Sources */, D44271FB1CC81B3200D84D28 /* ScreenshotCommands.cpp in Sources */, diff --git a/data/shaders/drawimage.frag b/data/shaders/drawimage.frag index c07dba6ea6..c4ae8ce042 100644 --- a/data/shaders/drawimage.frag +++ b/data/shaders/drawimage.frag @@ -1,10 +1,16 @@ #version 150 -uniform ivec4 uClip; -uniform int uFlags; -uniform vec4 uColour; -uniform usampler2D uTexture; -uniform vec4 uPalette[256]; +uniform vec4 uPalette[256]; +uniform usampler2DArray uTexture; + +flat in ivec4 fClip; +flat in int fFlags; +in vec4 fColour; +flat in int fTexColourAtlas; +in vec2 fTexColourCoords; +flat in int fTexMaskAtlas; +in vec2 fTexMaskCoords; +flat in int fMask; in vec2 fPosition; in vec2 fTextureCoordinate; @@ -13,19 +19,28 @@ out vec4 oColour; void main() { - if (fPosition.x < uClip.x || fPosition.x > uClip.z || - fPosition.y < uClip.y || fPosition.y > uClip.w) + if (fPosition.x < fClip.x || fPosition.x > fClip.z || + fPosition.y < fClip.y || fPosition.y > fClip.w) { discard; } - vec4 texel = uPalette[texture(uTexture, fTextureCoordinate).r]; - if ((uFlags & 1) != 0) + vec4 texel = uPalette[texture(uTexture, vec3(fTexColourCoords, float(fTexColourAtlas))).r]; + vec4 mask = uPalette[texture(uTexture, vec3(fTexMaskCoords, float(fTexMaskAtlas))).r]; + + if (fMask != 0) { - oColour = vec4(uColour.rgb, uColour.a * texel.a); + oColour = texel * mask; } else { - oColour = texel; + if ((fFlags & 1) != 0) + { + oColour = vec4(fColour.rgb, fColour.a * texel.a); + } + else + { + oColour = texel; + } } } diff --git a/data/shaders/drawimage.vert b/data/shaders/drawimage.vert index 84b893c0dc..064a5e3e56 100644 --- a/data/shaders/drawimage.vert +++ b/data/shaders/drawimage.vert @@ -1,33 +1,53 @@ #version 150 uniform ivec2 uScreenSize; -uniform ivec4 uBounds; -uniform ivec4 uTextureCoordinates; + +in ivec4 ivClip; +in int ivTexColourAtlas; +in vec4 ivTexColourBounds; +in int ivTexMaskAtlas; +in vec4 ivTexMaskBounds; +in int ivFlags; +in vec4 ivColour; +in ivec4 ivBounds; +in int ivMask; in uint vIndex; -out vec2 fPosition; -out vec2 fTextureCoordinate; +out vec2 fTextureCoordinate; +out vec2 fPosition; +flat out ivec4 fClip; +flat out int fFlags; +out vec4 fColour; +flat out int fTexColourAtlas; +out vec2 fTexColourCoords; +flat out int fTexMaskAtlas; +out vec2 fTexMaskCoords; +flat out int fMask; void main() { vec2 pos; switch (vIndex) { case 0u: - pos = uBounds.xy; - fTextureCoordinate = uTextureCoordinates.xy; + pos = ivBounds.xy; + fTexColourCoords = ivTexColourBounds.xy; + fTexMaskCoords = ivTexMaskBounds.xy; break; case 1u: - pos = uBounds.zy; - fTextureCoordinate = uTextureCoordinates.zy; + pos = ivBounds.zy; + fTexColourCoords = ivTexColourBounds.zy; + fTexMaskCoords = ivTexMaskBounds.zy; break; case 2u: - pos = uBounds.xw; - fTextureCoordinate = uTextureCoordinates.xw; + pos = ivBounds.xw; + fTexColourCoords = ivTexColourBounds.xw; + fTexMaskCoords = ivTexMaskBounds.xw; break; case 3u: - pos = uBounds.zw; - fTextureCoordinate = uTextureCoordinates.zw; + pos = ivBounds.zw; + fTexColourCoords = ivTexColourBounds.zw; + fTexMaskCoords = ivTexMaskBounds.zw; break; } @@ -38,5 +58,12 @@ void main() pos.y = (pos.y * (2.0 / uScreenSize.y)) - 1.0; pos.y *= -1; + fClip = ivClip; + fFlags = ivFlags; + fColour = ivColour; + fMask = ivMask; + fTexColourAtlas = ivTexColourAtlas; + fTexMaskAtlas = ivTexMaskAtlas; + gl_Position = vec4(pos, 0.0, 1.0); } diff --git a/data/shaders/drawimagemasked.frag b/data/shaders/drawimagemasked.frag deleted file mode 100644 index 03423395d0..0000000000 --- a/data/shaders/drawimagemasked.frag +++ /dev/null @@ -1,24 +0,0 @@ -#version 150 - -uniform ivec4 uClip; -uniform usampler2D uTextureMask; -uniform usampler2D uTextureColour; -uniform vec4 uPalette[256]; - -in vec2 fPosition; -in vec2 fTextureCoordinate; - -out vec4 oColour; - -void main() -{ - if (fPosition.x < uClip.x || fPosition.x > uClip.z || - fPosition.y < uClip.y || fPosition.y > uClip.w) - { - discard; - } - - vec4 mask = uPalette[texture(uTextureMask, fTextureCoordinate).r]; - vec4 colour = uPalette[texture(uTextureColour, fTextureCoordinate).r]; - oColour = colour * mask; -} diff --git a/data/shaders/drawimagemasked.vert b/data/shaders/drawimagemasked.vert deleted file mode 100644 index e4b505daec..0000000000 --- a/data/shaders/drawimagemasked.vert +++ /dev/null @@ -1,41 +0,0 @@ -#version 150 - -uniform ivec2 uScreenSize; -uniform ivec4 uBounds; - -in uint vIndex; - -out vec2 fPosition; -out vec2 fTextureCoordinate; - -void main() -{ - vec2 pos; - switch (vIndex) { - case 0u: - pos = uBounds.xy; - fTextureCoordinate = vec2(0, 0); - break; - case 1u: - pos = uBounds.zy; - fTextureCoordinate = vec2(1, 0); - break; - case 2u: - pos = uBounds.xw; - fTextureCoordinate = vec2(0, 1); - break; - case 3u: - pos = uBounds.zw; - fTextureCoordinate = vec2(1, 1); - 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/distribution/changelog.txt b/distribution/changelog.txt index 6cda7d3d8e..1a334ae3b3 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -20,6 +20,7 @@ - Feature: Objects directory is scanned recursively. - Improve: Performance and reliability of loading objects. - Improve: Screenshots are now saved with the name of the park and the current date and time. +- Improve: More accurate frame rate calculation - Removed: BMP screenshots. - Removed: Intamin and Phoenix easter eggs. - Fix: [#1038] Guest List is out of order. diff --git a/openrct2.vcxproj b/openrct2.vcxproj index 91b965c2ce..4d00677ce6 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -56,7 +56,6 @@ - @@ -383,7 +382,7 @@ - + diff --git a/src/drawing/IDrawingEngine.h b/src/drawing/IDrawingEngine.h index 537939ae91..90bee4d6a1 100644 --- a/src/drawing/IDrawingEngine.h +++ b/src/drawing/IDrawingEngine.h @@ -41,6 +41,8 @@ interface IDrawingEngine virtual void Resize(uint32 width, uint32 height) abstract; virtual void SetPalette(SDL_Color * colours) abstract; + virtual void SetUncappedFrameRate(bool uncapped) abstract; + virtual void Invalidate(sint32 left, sint32 top, sint32 right, sint32 bottom) abstract; virtual void Draw() abstract; virtual void CopyRect(sint32 x, sint32 y, sint32 width, sint32 height, sint32 dx, sint32 dy) abstract; diff --git a/src/drawing/NewDrawing.cpp b/src/drawing/NewDrawing.cpp index bbfaec4c7c..e8c7c3b7c9 100644 --- a/src/drawing/NewDrawing.cpp +++ b/src/drawing/NewDrawing.cpp @@ -83,6 +83,7 @@ extern "C" try { _drawingEngine->Initialise(gWindow); + _drawingEngine->SetUncappedFrameRate(gConfigGeneral.uncap_fps == 1); } catch (Exception ex) { @@ -164,6 +165,14 @@ extern "C" } } + void drawing_engine_set_fps_uncapped(bool uncapped) + { + if (_drawingEngine != nullptr) + { + _drawingEngine->SetUncappedFrameRate(uncapped); + } + } + void gfx_set_dirty_blocks(sint16 left, sint16 top, sint16 right, sint16 bottom) { if (_drawingEngine != nullptr) diff --git a/src/drawing/NewDrawing.h b/src/drawing/NewDrawing.h index a017306082..f484f61b63 100644 --- a/src/drawing/NewDrawing.h +++ b/src/drawing/NewDrawing.h @@ -33,6 +33,7 @@ void drawing_engine_dispose(); rct_drawpixelinfo * drawing_engine_get_dpi(); bool drawing_engine_has_dirty_optimisations(); void drawing_engine_invalidate_image(uint32 image); +void drawing_engine_set_fps_uncapped(bool uncapped); #ifdef __cplusplus } diff --git a/src/drawing/engines/SoftwareDrawingEngine.cpp b/src/drawing/engines/SoftwareDrawingEngine.cpp index b9bfbe3f60..cad2dfca96 100644 --- a/src/drawing/engines/SoftwareDrawingEngine.cpp +++ b/src/drawing/engines/SoftwareDrawingEngine.cpp @@ -327,6 +327,11 @@ public: } } + void SetUncappedFrameRate(bool uncapped) override + { + // Not applicable for this engine + } + void Invalidate(sint32 left, sint32 top, sint32 right, sint32 bottom) override { left = Math::Max(left, 0); diff --git a/src/drawing/engines/opengl/CopyFramebufferShader.cpp b/src/drawing/engines/opengl/CopyFramebufferShader.cpp index b188f26128..605ef33c68 100644 --- a/src/drawing/engines/opengl/CopyFramebufferShader.cpp +++ b/src/drawing/engines/opengl/CopyFramebufferShader.cpp @@ -71,7 +71,7 @@ void CopyFramebufferShader::SetTextureCoordinates(sint32 left, sint32 top, sint3 void CopyFramebufferShader::SetTexture(GLuint texture) { - OpenGLAPI::SetTexture2D(0, texture); + OpenGLAPI::SetTexture(0, GL_TEXTURE_2D, texture); } void CopyFramebufferShader::Draw() diff --git a/src/drawing/engines/opengl/DrawCommands.h b/src/drawing/engines/opengl/DrawCommands.h new file mode 100644 index 0000000000..d6b8c33fe2 --- /dev/null +++ b/src/drawing/engines/opengl/DrawCommands.h @@ -0,0 +1,45 @@ +#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" +#include "GLSLTypes.h" + +struct DrawRectCommand { + uint32 flags; + GLuint sourceFramebuffer; + vec4f colours[2]; + sint32 clip[4]; + sint32 bounds[4]; +}; + +struct DrawLineCommand { + vec4f colour; + sint32 clip[4]; + sint32 pos[4]; +}; + +struct DrawImageCommand { + uint32 flags; + vec4f colour; + sint32 clip[4]; + CachedTextureInfo texMask; + CachedTextureInfo texColour; + sint32 bounds[4]; + bool mask; +}; \ No newline at end of file diff --git a/src/drawing/engines/opengl/DrawImageMaskedShader.cpp b/src/drawing/engines/opengl/DrawImageMaskedShader.cpp deleted file mode 100644 index f5e2e2d5c1..0000000000 --- a/src/drawing/engines/opengl/DrawImageMaskedShader.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#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 "DrawImageMaskedShader.h" - -DrawImageMaskedShader::DrawImageMaskedShader() : OpenGLShaderProgram("drawimagemasked") -{ - GetLocations(); - - glGenBuffers(1, &_vbo); - glGenVertexArrays(1, &_vao); - - GLuint vertices[] = { 0, 1, 2, 2, 1, 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, 0); - - Use(); - glUniform1i(uTextureMask, 0); - glUniform1i(uTextureColour, 1); -} - -DrawImageMaskedShader::~DrawImageMaskedShader() -{ - glDeleteBuffers(1, &_vbo); - glDeleteVertexArrays(1, &_vao); - - glBindVertexArray(_vao); -} - -void DrawImageMaskedShader::GetLocations() -{ - uScreenSize = GetUniformLocation("uScreenSize"); - uClip = GetUniformLocation("uClip"); - uBounds = GetUniformLocation("uBounds"); - uTextureMask = GetUniformLocation("uTextureMask"); - uTextureColour = GetUniformLocation("uTextureColour"); - uPalette = GetUniformLocation("uPalette"); - - vIndex = GetAttributeLocation("vIndex"); -} - -void DrawImageMaskedShader::SetScreenSize(sint32 width, sint32 height) -{ - glUniform2i(uScreenSize, width, height); -} - -void DrawImageMaskedShader::SetClip(sint32 left, sint32 top, sint32 right, sint32 bottom) -{ - glUniform4i(uClip, left, top, right, bottom); -} - -void DrawImageMaskedShader::SetBounds(sint32 left, sint32 top, sint32 right, sint32 bottom) -{ - glUniform4i(uBounds, left, top, right, bottom); -} - -void DrawImageMaskedShader::SetTextureMask(GLuint texture) -{ - OpenGLAPI::SetTexture2D(0, texture); -} - -void DrawImageMaskedShader::SetTextureColour(GLuint texture) -{ - OpenGLAPI::SetTexture2D(1, texture); -} - -void DrawImageMaskedShader::SetPalette(const vec4f *glPalette) -{ - glUniform4fv(uPalette, 256, (const GLfloat *) glPalette); -} - -void DrawImageMaskedShader::Draw(sint32 left, sint32 top, sint32 right, sint32 bottom) -{ - SetBounds(left, top, right, bottom); - - glBindVertexArray(_vao); - glDrawArrays(GL_TRIANGLES, 0, 6); -} - -#endif /* DISABLE_OPENGL */ diff --git a/src/drawing/engines/opengl/DrawImageMaskedShader.h b/src/drawing/engines/opengl/DrawImageMaskedShader.h deleted file mode 100644 index d42c28b1b8..0000000000 --- a/src/drawing/engines/opengl/DrawImageMaskedShader.h +++ /dev/null @@ -1,52 +0,0 @@ -#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" -#include - -class DrawImageMaskedShader : public OpenGLShaderProgram -{ -private: - GLuint uScreenSize; - GLuint uClip; - GLuint uBounds; - GLuint uTextureMask; - GLuint uTextureColour; - GLuint uPalette; - - GLuint vIndex; - - GLuint _vbo; - GLuint _vao; - -public: - DrawImageMaskedShader(); - ~DrawImageMaskedShader() override; - - void SetScreenSize(sint32 width, sint32 height); - void SetClip(sint32 left, sint32 top, sint32 right, sint32 bottom); - void SetBounds(sint32 left, sint32 top, sint32 right, sint32 bottom); - void SetTextureMask(GLuint texture); - void SetTextureColour(GLuint texture); - void SetPalette(const vec4f *glPalette); - void Draw(sint32 left, sint32 top, sint32 right, sint32 bottom); - -private: - void GetLocations(); -}; diff --git a/src/drawing/engines/opengl/DrawImageShader.cpp b/src/drawing/engines/opengl/DrawImageShader.cpp index 94d7358309..69c6b8d6ba 100644 --- a/src/drawing/engines/opengl/DrawImageShader.cpp +++ b/src/drawing/engines/opengl/DrawImageShader.cpp @@ -23,6 +23,7 @@ DrawImageShader::DrawImageShader() : OpenGLShaderProgram("drawimage") GetLocations(); glGenBuffers(1, &_vbo); + glGenBuffers(1, &_vboInstances); glGenVertexArrays(1, &_vao); GLuint vertices[] = { 0, 1, 2, 2, 1, 3 }; @@ -30,18 +31,49 @@ DrawImageShader::DrawImageShader() : OpenGLShaderProgram("drawimage") glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindVertexArray(_vao); - glEnableVertexAttribArray(vIndex); + glVertexAttribIPointer(vIndex, 1, GL_INT, 0, nullptr); + glBindBuffer(GL_ARRAY_BUFFER, _vboInstances); + glVertexAttribIPointer(vClip, 4, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, clip)); + glVertexAttribIPointer(vTexColourAtlas, 1, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, texColourAtlas)); + glVertexAttribPointer(vTexColourBounds, 4, GL_FLOAT, GL_FALSE, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, texColourBounds)); + glVertexAttribIPointer(vTexMaskAtlas, 1, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, texMaskAtlas)); + glVertexAttribPointer(vTexMaskBounds, 4, GL_FLOAT, GL_FALSE, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, texMaskBounds)); + glVertexAttribIPointer(vFlags, 1, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, flags)); + glVertexAttribPointer(vColour, 4, GL_FLOAT, GL_FALSE, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, colour)); + glVertexAttribIPointer(vBounds, 4, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, bounds)); + glVertexAttribIPointer(vMask, 1, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, mask)); + + glEnableVertexAttribArray(vIndex); + glEnableVertexAttribArray(vClip); + glEnableVertexAttribArray(vTexColourAtlas); + glEnableVertexAttribArray(vTexColourBounds); + glEnableVertexAttribArray(vTexMaskAtlas); + glEnableVertexAttribArray(vTexMaskBounds); + glEnableVertexAttribArray(vFlags); + glEnableVertexAttribArray(vColour); + glEnableVertexAttribArray(vBounds); + glEnableVertexAttribArray(vMask); + + glVertexAttribDivisor(vClip, 1); + glVertexAttribDivisor(vTexColourAtlas, 1); + glVertexAttribDivisor(vTexColourBounds, 1); + glVertexAttribDivisor(vTexMaskAtlas, 1); + glVertexAttribDivisor(vTexMaskBounds, 1); + glVertexAttribDivisor(vFlags, 1); + glVertexAttribDivisor(vColour, 1); + glVertexAttribDivisor(vBounds, 1); + glVertexAttribDivisor(vMask, 1); + Use(); - SetFlags(0); - SetTextureCoordinates(0, 0, 1, 1); glUniform1i(uTexture, 0); } DrawImageShader::~DrawImageShader() { glDeleteBuffers(1, &_vbo); + glDeleteBuffers(1, &_vboInstances); glDeleteVertexArrays(1, &_vao); glBindVertexArray(_vao); @@ -50,15 +82,19 @@ DrawImageShader::~DrawImageShader() void DrawImageShader::GetLocations() { uScreenSize = GetUniformLocation("uScreenSize"); - uClip = GetUniformLocation("uClip"); - uBounds = GetUniformLocation("uBounds"); - uTextureCoordinates = GetUniformLocation("uTextureCoordinates"); uTexture = GetUniformLocation("uTexture"); - uColour = GetUniformLocation("uColour"); - uFlags = GetUniformLocation("uFlags"); uPalette = GetUniformLocation("uPalette"); vIndex = GetAttributeLocation("vIndex"); + vClip = GetAttributeLocation("ivClip"); + vTexColourAtlas = GetAttributeLocation("ivTexColourAtlas"); + vTexColourBounds = GetAttributeLocation("ivTexColourBounds"); + vTexMaskAtlas = GetAttributeLocation("ivTexMaskAtlas"); + vTexMaskBounds = GetAttributeLocation("ivTexMaskBounds"); + vFlags = GetAttributeLocation("ivFlags"); + vColour = GetAttributeLocation("ivColour"); + vBounds = GetAttributeLocation("ivBounds"); + vMask = GetAttributeLocation("ivMask"); } void DrawImageShader::SetScreenSize(sint32 width, sint32 height) @@ -66,47 +102,19 @@ void DrawImageShader::SetScreenSize(sint32 width, sint32 height) glUniform2i(uScreenSize, width, height); } -void DrawImageShader::SetClip(sint32 left, sint32 top, sint32 right, sint32 bottom) -{ - glUniform4i(uClip, left, top, right, bottom); -} - -void DrawImageShader::SetBounds(sint32 left, sint32 top, sint32 right, sint32 bottom) -{ - glUniform4i(uBounds, left, top, right, bottom); -} - -void DrawImageShader::SetTextureCoordinates(sint32 left, sint32 top, sint32 right, sint32 bottom) -{ - glUniform4i(uTextureCoordinates, left, top, right, bottom); -} - -void DrawImageShader::SetTexture(GLuint texture) -{ - OpenGLAPI::SetTexture2D(0, texture); -} - -void DrawImageShader::SetColour(vec4f colour) -{ - glUniform4f(uColour, colour.r, colour.g, colour.b, colour.a); -} - -void DrawImageShader::SetFlags(uint32 flags) -{ - glUniform1i(uFlags, flags); -} - void DrawImageShader::SetPalette(const vec4f *glPalette) { glUniform4fv(uPalette, 256, (const GLfloat *) glPalette); } -void DrawImageShader::Draw(sint32 left, sint32 top, sint32 right, sint32 bottom) +void DrawImageShader::DrawInstances(const std::vector& instances) { - SetBounds(left, top, right, bottom); - glBindVertexArray(_vao); - glDrawArrays(GL_TRIANGLES, 0, 6); + + glBindBuffer(GL_ARRAY_BUFFER, _vboInstances); + glBufferData(GL_ARRAY_BUFFER, sizeof(instances[0]) * instances.size(), instances.data(), GL_STREAM_DRAW); + + glDrawArraysInstanced(GL_TRIANGLES, 0, 6, instances.size()); } #endif /* DISABLE_OPENGL */ diff --git a/src/drawing/engines/opengl/DrawImageShader.h b/src/drawing/engines/opengl/DrawImageShader.h index 09171debd9..f4609bf581 100644 --- a/src/drawing/engines/opengl/DrawImageShader.h +++ b/src/drawing/engines/opengl/DrawImageShader.h @@ -19,22 +19,41 @@ #include "GLSLTypes.h" #include "OpenGLShaderProgram.h" #include +#include + +// Per-instance data for images +struct DrawImageInstance { + vec4i clip; + int texColourAtlas; + vec4f texColourBounds; + int texMaskAtlas; + vec4f texMaskBounds; + int flags; + vec4f colour; + vec4i bounds; + int mask; +}; class DrawImageShader : public OpenGLShaderProgram { private: GLuint uScreenSize; - GLuint uClip; - GLuint uBounds; - GLuint uTextureCoordinates; GLuint uTexture; - GLuint uColour; - GLuint uFlags; GLuint uPalette; GLuint vIndex; + GLuint vClip; + GLuint vTexColourAtlas; + GLuint vTexColourBounds; + GLuint vTexMaskAtlas; + GLuint vTexMaskBounds; + GLuint vFlags; + GLuint vColour; + GLuint vBounds; + GLuint vMask; GLuint _vbo; + GLuint _vboInstances; GLuint _vao; SDL_Color _palette[256]; @@ -44,14 +63,8 @@ public: ~DrawImageShader() override; void SetScreenSize(sint32 width, sint32 height); - void SetClip(sint32 left, sint32 top, sint32 right, sint32 bottom); - 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 SetColour(vec4f colour); - void SetFlags(uint32 flags); void SetPalette(const vec4f *glPalette); - void Draw(sint32 left, sint32 top, sint32 right, sint32 bottom); + void DrawInstances(const std::vector& instances); private: void GetLocations(); diff --git a/src/drawing/engines/opengl/FillRectShader.cpp b/src/drawing/engines/opengl/FillRectShader.cpp index 0baf1065c1..c6fe3542d8 100644 --- a/src/drawing/engines/opengl/FillRectShader.cpp +++ b/src/drawing/engines/opengl/FillRectShader.cpp @@ -87,7 +87,8 @@ void FillRectShader::SetColour(int index, vec4f colour) void FillRectShader::SetSourceFramebuffer(GLuint texture) { - OpenGLAPI::SetTexture2D(0, texture); + _sourceFramebuffer = texture; + OpenGLAPI::SetTexture(0, GL_TEXTURE_2D, texture); } void FillRectShader::Draw(sint32 left, sint32 top, sint32 right, sint32 bottom) @@ -98,4 +99,8 @@ void FillRectShader::Draw(sint32 left, sint32 top, sint32 right, sint32 bottom) glDrawArrays(GL_TRIANGLES, 0, 6); } +GLuint FillRectShader::GetSourceFramebuffer() const { + return _sourceFramebuffer; +} + #endif /* DISABLE_OPENGL */ diff --git a/src/drawing/engines/opengl/FillRectShader.h b/src/drawing/engines/opengl/FillRectShader.h index d96c33c705..80af878fc6 100644 --- a/src/drawing/engines/opengl/FillRectShader.h +++ b/src/drawing/engines/opengl/FillRectShader.h @@ -36,6 +36,8 @@ private: GLuint _vbo; GLuint _vao; + GLuint _sourceFramebuffer = 0; + public: FillRectShader(); ~FillRectShader() override; @@ -49,6 +51,8 @@ public: void Draw(sint32 left, sint32 top, sint32 right, sint32 bottom); + GLuint GetSourceFramebuffer() const; + private: void GetLocations(); }; diff --git a/src/drawing/engines/opengl/OpenGLAPI.cpp b/src/drawing/engines/opengl/OpenGLAPI.cpp index 8683262160..09641de1f4 100644 --- a/src/drawing/engines/opengl/OpenGLAPI.cpp +++ b/src/drawing/engines/opengl/OpenGLAPI.cpp @@ -68,6 +68,10 @@ static const char * TryLoadAllProcAddresses() SetupOpenGLFunction(glTexImage2D); SetupOpenGLFunction(glTexParameteri); SetupOpenGLFunction(glViewport); + SetupOpenGLFunction(glTexSubImage3D); + SetupOpenGLFunction(glTexImage3D); + SetupOpenGLFunction(glGetIntegerv); + SetupOpenGLFunction(glGetTexImage); // 2.0+ functions SetupOpenGLFunction(glAttachShader); @@ -100,12 +104,15 @@ static const char * TryLoadAllProcAddresses() SetupOpenGLFunction(glShaderSource); SetupOpenGLFunction(glUniform1i); SetupOpenGLFunction(glUniform2i); + SetupOpenGLFunction(glUniform2f); SetupOpenGLFunction(glUniform4f); SetupOpenGLFunction(glUniform4i); SetupOpenGLFunction(glUniform4fv); SetupOpenGLFunction(glUseProgram); SetupOpenGLFunction(glVertexAttribIPointer); SetupOpenGLFunction(glVertexAttribPointer); + SetupOpenGLFunction(glDrawArraysInstanced); + SetupOpenGLFunction(glVertexAttribDivisor); return nullptr; } @@ -118,13 +125,13 @@ namespace OpenGLState GLuint CurrentProgram = UINT32_MAX; } -void OpenGLAPI::SetTexture2D(uint16 index, GLuint texture) +void OpenGLAPI::SetTexture(uint16 index, GLenum type, GLuint texture) { if (OpenGLState::ActiveTexture != index) { glActiveTexture(GL_TEXTURE0 + index); } - glBindTexture(GL_TEXTURE_2D, texture); + glBindTexture(type, texture); } bool OpenGLAPI::Initialise() diff --git a/src/drawing/engines/opengl/OpenGLAPI.h b/src/drawing/engines/opengl/OpenGLAPI.h index 5fa377bd9c..3bf9324a60 100644 --- a/src/drawing/engines/opengl/OpenGLAPI.h +++ b/src/drawing/engines/opengl/OpenGLAPI.h @@ -40,6 +40,10 @@ #define glTexImage2D __static__glTexImage2D #define glTexParameteri __static__glTexParameteri #define glViewport __static__glViewport +#define glTexSubImage3D __static__glTexSubImage3D +#define glTexImage3D __static__glTexImage3D +#define glGetIntegerv __static__glGetIntegerv +#define glGetTexImage __static__glGetTexImage #endif @@ -67,6 +71,10 @@ #undef glTexImage2D #undef glTexParameteri #undef glViewport +#undef glTexSubImage3D +#undef glTexImage3D +#undef glGetIntegerv +#undef glGetTexImage // 1.1 function signatures typedef void (APIENTRYP PFNGLBEGINPROC )(GLenum mode); @@ -87,6 +95,10 @@ typedef void (APIENTRYP PFNGLREADPIXELSPROC )(GLint x, GLint y, GLsizei wid typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC )(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC )(GLenum target, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLVIEWPORTPROC )(GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC )(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* data); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC )(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid * data); +typedef void (APIENTRYP PFNGLGETINTERGERVPROC )(GLenum pname, GLint * data); +typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC )(GLenum target, GLint level, GLenum format, GLenum type, GLvoid * img); #ifdef NO_EXTERN_GLAPI // Defines the function pointers @@ -118,6 +130,10 @@ GLAPI_DECL PFNGLREADPIXELSPROC glReadPixels GLAP GLAPI_DECL PFNGLTEXIMAGE2DPROC glTexImage2D GLAPI_SET; GLAPI_DECL PFNGLTEXPARAMETERIPROC glTexParameteri GLAPI_SET; GLAPI_DECL PFNGLVIEWPORTPROC glViewport GLAPI_SET; +GLAPI_DECL PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D GLAPI_SET; +GLAPI_DECL PFNGLTEXIMAGE3DPROC glTexImage3D GLAPI_SET; +GLAPI_DECL PFNGLGETINTERGERVPROC glGetIntegerv GLAPI_SET; +GLAPI_DECL PFNGLGETTEXIMAGEPROC glGetTexImage GLAPI_SET; // 2.0+ function pointers GLAPI_DECL PFNGLATTACHSHADERPROC glAttachShader GLAPI_SET; @@ -150,12 +166,15 @@ GLAPI_DECL PFNGLLINKPROGRAMPROC glLinkProgram GLAP GLAPI_DECL PFNGLSHADERSOURCEPROC glShaderSource GLAPI_SET; GLAPI_DECL PFNGLUNIFORM1IPROC glUniform1i GLAPI_SET; GLAPI_DECL PFNGLUNIFORM2IPROC glUniform2i GLAPI_SET; +GLAPI_DECL PFNGLUNIFORM2FPROC glUniform2f GLAPI_SET; GLAPI_DECL PFNGLUNIFORM4FPROC glUniform4f GLAPI_SET; GLAPI_DECL PFNGLUNIFORM4IPROC glUniform4i GLAPI_SET; GLAPI_DECL PFNGLUNIFORM4FVPROC glUniform4fv GLAPI_SET; GLAPI_DECL PFNGLUSEPROGRAMPROC glUseProgram GLAPI_SET; GLAPI_DECL PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer GLAPI_SET; GLAPI_DECL PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer GLAPI_SET; +GLAPI_DECL PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced GLAPI_SET; +GLAPI_DECL PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor GLAPI_SET; #endif /* OPENGL_NO_LINK */ @@ -172,7 +191,7 @@ inline void CheckGLError() namespace OpenGLAPI { bool Initialise(); - void SetTexture2D(uint16 index, GLuint texture); + void SetTexture(uint16 index, GLenum type, GLuint texture); } namespace OpenGLState diff --git a/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp b/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp index f0b0df073a..119729a808 100644 --- a/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp +++ b/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp @@ -34,11 +34,11 @@ IDrawingEngine * DrawingEngineFactory::CreateOpenGL() #include "OpenGLFramebuffer.h" #include "CopyFramebufferShader.h" #include "DrawImageShader.h" -#include "DrawImageMaskedShader.h" #include "DrawLineShader.h" #include "FillRectShader.h" #include "SwapFramebuffer.h" #include "TextureCache.h" +#include "DrawCommands.h" #include "../../../core/Console.hpp" #include "../../../core/Exception.hpp" @@ -47,6 +47,7 @@ IDrawingEngine * DrawingEngineFactory::CreateOpenGL() #include "../../IDrawingContext.h" #include "../../IDrawingEngine.h" #include "../../Rain.h" +#include "../../../config.h" extern "C" { @@ -63,7 +64,7 @@ struct OpenGLVersion GLint Minor; }; -constexpr OpenGLVersion OPENGL_MINIMUM_REQUIRED_VERSION = { 3, 2 }; +constexpr OpenGLVersion OPENGL_MINIMUM_REQUIRED_VERSION = { 3, 3 }; static const vec3f TransparentColourTable[144 - 44] = { @@ -178,7 +179,6 @@ private: rct_drawpixelinfo * _dpi; DrawImageShader * _drawImageShader = nullptr; - DrawImageMaskedShader * _drawImageMaskedShader = nullptr; DrawLineShader * _drawLineShader = nullptr; FillRectShader * _fillRectShader = nullptr; @@ -191,6 +191,12 @@ private: sint32 _clipRight; sint32 _clipBottom; + struct { + std::vector rectangles; + std::vector lines; + std::vector images; + } _commandBuffers; + public: explicit OpenGLDrawingContext(OpenGLDrawingEngine * engine); ~OpenGLDrawingContext() override; @@ -210,6 +216,12 @@ public: void DrawSpriteSolid(uint32 image, sint32 x, sint32 y, uint8 colour) override; void DrawGlyph(uint32 image, sint32 x, sint32 y, uint8 * palette) override; + void FlushCommandBuffers(); + + void FlushRectangles(); + void FlushLines(); + void FlushImages(); + void SetDPI(rct_drawpixelinfo * dpi); }; @@ -260,6 +272,7 @@ public: SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, requiredVersion.Major); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, requiredVersion.Minor); + _context = SDL_GL_CreateContext(_window); if (_context == nullptr) { @@ -305,6 +318,11 @@ public: _drawingContext->ResetPalette(); } + void SetUncappedFrameRate(bool uncapped) override + { + SDL_GL_SetSwapInterval(uncapped ? 0 : 1); + } + void Invalidate(sint32 left, sint32 top, sint32 right, sint32 bottom) override { } @@ -325,11 +343,14 @@ public: gfx_draw_pickedup_peep(&_bitsDPI); + _drawingContext->FlushCommandBuffers(); _swapFramebuffer->SwapCopy(); rct2_draw(&_bitsDPI); } + _drawingContext->FlushCommandBuffers(); + // Scale up to window _screenFramebuffer->Bind(); _copyFramebufferShader->Use(); @@ -490,7 +511,6 @@ OpenGLDrawingContext::OpenGLDrawingContext(OpenGLDrawingEngine * engine) OpenGLDrawingContext::~OpenGLDrawingContext() { delete _drawImageShader; - delete _drawImageMaskedShader; delete _drawLineShader; delete _fillRectShader; @@ -505,17 +525,16 @@ IDrawingEngine * OpenGLDrawingContext::GetEngine() void OpenGLDrawingContext::Initialise() { _drawImageShader = new DrawImageShader(); - _drawImageMaskedShader = new DrawImageMaskedShader(); _drawLineShader = new DrawLineShader(); _fillRectShader = new FillRectShader(); } void OpenGLDrawingContext::Resize(sint32 width, sint32 height) { + FlushCommandBuffers(); + _drawImageShader->Use(); _drawImageShader->SetScreenSize(width, height); - _drawImageMaskedShader->Use(); - _drawImageMaskedShader->SetScreenSize(width, height); _drawLineShader->Use(); _drawLineShader->SetScreenSize(width, height); _fillRectShader->Use(); @@ -524,11 +543,11 @@ void OpenGLDrawingContext::Resize(sint32 width, sint32 height) void OpenGLDrawingContext::ResetPalette() { + FlushCommandBuffers(); + _textureCache->SetPalette(_engine->Palette); _drawImageShader->Use(); _drawImageShader->SetPalette(_engine->GLPalette); - _drawImageMaskedShader->Use(); - _drawImageMaskedShader->SetPalette(_engine->GLPalette); } void OpenGLDrawingContext::Clear(uint32 colour) @@ -543,6 +562,10 @@ void OpenGLDrawingContext::FillRect(uint32 colour, sint32 left, sint32 top, sint right += _offsetX; bottom += _offsetY; + DrawRectCommand command = {}; + + command.sourceFramebuffer = _fillRectShader->GetSourceFramebuffer(); + vec4f paletteColour[2]; paletteColour[0] = _engine->GLPalette[(colour >> 0) & 0xFF]; paletteColour[1] = paletteColour[0]; @@ -550,8 +573,7 @@ void OpenGLDrawingContext::FillRect(uint32 colour, sint32 left, sint32 top, sint { paletteColour[1].a = 0; - _fillRectShader->Use(); - _fillRectShader->SetFlags(0); + command.flags = 0; } else if (colour & 0x2000000) { @@ -568,20 +590,31 @@ void OpenGLDrawingContext::FillRect(uint32 colour, sint32 left, sint32 top, sint paletteColour[1] = paletteColour[0]; GLuint srcTexture = _engine->SwapCopyReturningSourceTexture(); - _fillRectShader->Use(); - _fillRectShader->SetFlags(1); - _fillRectShader->SetSourceFramebuffer(srcTexture); + command.flags = 1; + command.sourceFramebuffer = srcTexture; } else { - _fillRectShader->Use(); - _fillRectShader->SetFlags(0); + command.flags = 0; } - _fillRectShader->SetColour(0, paletteColour[0]); - _fillRectShader->SetColour(1, paletteColour[1]); - _fillRectShader->SetClip(_clipLeft, _clipTop, _clipRight, _clipBottom); - _fillRectShader->Draw(left, top, right + 1, bottom + 1); + command.colours[0] = paletteColour[0]; + command.colours[1] = paletteColour[1]; + + command.clip[0] = _clipLeft; + command.clip[1] = _clipTop; + command.clip[2] = _clipRight; + command.clip[3] = _clipBottom; + + command.bounds[0] = left; + command.bounds[1] = top; + command.bounds[2] = right + 1; + command.bounds[3] = bottom + 1; + + _commandBuffers.rectangles.push_back(command); + + // Must be rendered in order, depends on already rendered contents + FlushCommandBuffers(); } void OpenGLDrawingContext::DrawLine(uint32 colour, sint32 x1, sint32 y1, sint32 x2, sint32 y2) @@ -592,11 +625,25 @@ void OpenGLDrawingContext::DrawLine(uint32 colour, sint32 x1, sint32 y1, sint32 y2 += _offsetY; vec4f paletteColour = _engine->GLPalette[colour & 0xFF]; + + DrawLineCommand command = {}; - _drawLineShader->Use(); - _drawLineShader->SetClip(_clipLeft, _clipTop, _clipRight, _clipBottom); - _drawLineShader->SetColour(paletteColour); - _drawLineShader->Draw(x1, y1, x2, y2); + command.colour = paletteColour; + + command.clip[0] = _clipLeft; + command.clip[1] = _clipTop; + command.clip[2] = _clipRight; + command.clip[3] = _clipBottom; + + command.pos[0] = x1; + command.pos[1] = y1; + command.pos[2] = x2; + command.pos[3] = y2; + + _commandBuffers.lines.push_back(command); + + // Must be rendered in order right now, because it does not yet use depth + FlushCommandBuffers(); } void OpenGLDrawingContext::DrawSprite(uint32 image, sint32 x, sint32 y, uint32 tertiaryColour) @@ -626,8 +673,6 @@ void OpenGLDrawingContext::DrawSprite(uint32 image, sint32 x, sint32 y, uint32 t } } - GLuint texture = _textureCache->GetOrLoadImageTexture(image); - uint8 zoomLevel = (1 << _dpi->zoom_level); sint32 drawOffsetX = g1Element->x_offset; @@ -664,10 +709,25 @@ void OpenGLDrawingContext::DrawSprite(uint32 image, sint32 x, sint32 y, uint32 t right += _clipLeft; bottom += _clipTop; - _drawImageShader->Use(); - _drawImageShader->SetClip(_clipLeft, _clipTop, _clipRight, _clipBottom); - _drawImageShader->SetTexture(texture); - _drawImageShader->Draw(left, top, right, bottom); + DrawImageCommand command = {}; + + command.flags = 0; + command.mask = false; + + command.clip[0] = _clipLeft; + command.clip[1] = _clipTop; + command.clip[2] = _clipRight; + command.clip[3] = _clipBottom; + + auto texture = _textureCache->GetOrLoadImageTexture(image); + command.texColour = texture; + + command.bounds[0] = left; + command.bounds[1] = top; + command.bounds[2] = right; + command.bounds[3] = bottom; + + _commandBuffers.images.push_back(command); } void OpenGLDrawingContext::DrawSpriteRawMasked(sint32 x, sint32 y, uint32 maskImage, uint32 colourImage) @@ -675,8 +735,8 @@ void OpenGLDrawingContext::DrawSpriteRawMasked(sint32 x, sint32 y, uint32 maskIm rct_g1_element * g1ElementMask = gfx_get_g1_element(maskImage & 0x7FFFF); rct_g1_element * g1ElementColour = gfx_get_g1_element(colourImage & 0x7FFFF); - GLuint textureMask = _textureCache->GetOrLoadImageTexture(maskImage); - GLuint textureColour = _textureCache->GetOrLoadImageTexture(colourImage); + auto textureMask = _textureCache->GetOrLoadImageTexture(maskImage); + auto textureColour = _textureCache->GetOrLoadImageTexture(colourImage); uint8 zoomLevel = (1 << _dpi->zoom_level); @@ -714,11 +774,24 @@ void OpenGLDrawingContext::DrawSpriteRawMasked(sint32 x, sint32 y, uint32 maskIm right += _clipLeft; bottom += _clipTop; - _drawImageMaskedShader->Use(); - _drawImageMaskedShader->SetClip(_clipLeft, _clipTop, _clipRight, _clipBottom); - _drawImageMaskedShader->SetTextureMask(textureMask); - _drawImageMaskedShader->SetTextureColour(textureColour); - _drawImageMaskedShader->Draw(left, top, right, bottom); + DrawImageCommand command = {}; + + command.mask = true; + + command.clip[0] = _clipLeft; + command.clip[1] = _clipTop; + command.clip[2] = _clipRight; + command.clip[3] = _clipBottom; + + command.texMask = textureMask; + command.texColour = textureColour; + + command.bounds[0] = left; + command.bounds[1] = top; + command.bounds[2] = right; + command.bounds[3] = bottom; + + _commandBuffers.images.push_back(command); } void OpenGLDrawingContext::DrawSpriteSolid(uint32 image, sint32 x, sint32 y, uint8 colour) @@ -728,7 +801,7 @@ void OpenGLDrawingContext::DrawSpriteSolid(uint32 image, sint32 x, sint32 y, uin int g1Id = image & 0x7FFFF; rct_g1_element * g1Element = gfx_get_g1_element(g1Id); - GLuint texture = _textureCache->GetOrLoadImageTexture(image); + auto texture = _textureCache->GetOrLoadImageTexture(image); sint32 drawOffsetX = g1Element->x_offset; sint32 drawOffsetY = g1Element->y_offset; @@ -754,13 +827,25 @@ void OpenGLDrawingContext::DrawSpriteSolid(uint32 image, sint32 x, sint32 y, uin right += _offsetX; bottom += _offsetY; - _drawImageShader->Use(); - _drawImageShader->SetClip(_clipLeft, _clipTop, _clipRight, _clipBottom); - _drawImageShader->SetTexture(texture); - _drawImageShader->SetFlags(1); - _drawImageShader->SetColour(paletteColour); - _drawImageShader->Draw(left, top, right, bottom); - _drawImageShader->SetFlags(0); + DrawImageCommand command = {}; + + command.flags = 1; + command.mask = false; + command.colour = paletteColour; + + command.clip[0] = _clipLeft; + command.clip[1] = _clipTop; + command.clip[2] = _clipRight; + command.clip[3] = _clipBottom; + + command.texColour = texture; + + command.bounds[0] = left; + command.bounds[1] = top; + command.bounds[2] = right; + command.bounds[3] = bottom; + + _commandBuffers.images.push_back(command); } void OpenGLDrawingContext::DrawGlyph(uint32 image, sint32 x, sint32 y, uint8 * palette) @@ -768,7 +853,7 @@ void OpenGLDrawingContext::DrawGlyph(uint32 image, sint32 x, sint32 y, uint8 * p int g1Id = image & 0x7FFFF; rct_g1_element * g1Element = gfx_get_g1_element(g1Id); - GLuint texture = _textureCache->GetOrLoadGlyphTexture(image, palette); + auto texture = _textureCache->GetOrLoadGlyphTexture(image, palette); sint32 drawOffsetX = g1Element->x_offset; sint32 drawOffsetY = g1Element->y_offset; @@ -794,11 +879,92 @@ void OpenGLDrawingContext::DrawGlyph(uint32 image, sint32 x, sint32 y, uint8 * p right += _offsetX; bottom += _offsetY; + DrawImageCommand command = {}; + + command.flags = 0; + command.mask = false; + + command.clip[0] = _clipLeft; + command.clip[1] = _clipTop; + command.clip[2] = _clipRight; + command.clip[3] = _clipBottom; + + command.texColour = texture; + + command.bounds[0] = left; + command.bounds[1] = top; + command.bounds[2] = right; + command.bounds[3] = bottom; + + _commandBuffers.images.push_back(command); +} + +void OpenGLDrawingContext::FlushCommandBuffers() +{ + FlushRectangles(); + FlushLines(); + + FlushImages(); +} + +void OpenGLDrawingContext::FlushRectangles() +{ + for (const auto& command : _commandBuffers.rectangles) + { + _fillRectShader->Use(); + _fillRectShader->SetFlags(command.flags); + _fillRectShader->SetSourceFramebuffer(command.sourceFramebuffer); + _fillRectShader->SetColour(0, command.colours[0]); + _fillRectShader->SetColour(1, command.colours[1]); + _fillRectShader->SetClip(command.clip[0], command.clip[1], command.clip[2], command.clip[3]); + _fillRectShader->Draw(command.bounds[0], command.bounds[1], command.bounds[2], command.bounds[3]); + } + + _commandBuffers.rectangles.clear(); +} + +void OpenGLDrawingContext::FlushLines() { + for (const auto& command : _commandBuffers.lines) + { + _drawLineShader->Use(); + _drawLineShader->SetColour(command.colour); + _drawLineShader->SetClip(command.clip[0], command.clip[1], command.clip[2], command.clip[3]); + _drawLineShader->Draw(command.pos[0], command.pos[1], command.pos[2], command.pos[3]); + } + + _commandBuffers.lines.clear(); +} + +void OpenGLDrawingContext::FlushImages() +{ + if (_commandBuffers.images.size() == 0) return; + + OpenGLAPI::SetTexture(0, GL_TEXTURE_2D_ARRAY, _textureCache->GetAtlasesTexture()); + + std::vector instances; + instances.reserve(_commandBuffers.images.size()); + + for (const auto& command : _commandBuffers.images) + { + DrawImageInstance instance; + + instance.clip = {command.clip[0], command.clip[1], command.clip[2], command.clip[3]}; + instance.texColourAtlas = command.texColour.index; + instance.texColourBounds = command.texColour.normalizedBounds; + instance.texMaskAtlas = command.texMask.index; + instance.texMaskBounds = command.texMask.normalizedBounds; + instance.flags = command.flags; + instance.colour = command.colour; + instance.bounds = {command.bounds[0], command.bounds[1], command.bounds[2], command.bounds[3]}; + instance.mask = command.mask; + + instances.push_back(instance); + } + _drawImageShader->Use(); - _drawImageShader->SetClip(_clipLeft, _clipTop, _clipRight, _clipBottom); - _drawImageShader->SetTexture(texture); - _drawImageShader->Draw(left, top, right, bottom); - _drawImageShader->SetFlags(0); + _drawImageShader->DrawInstances(instances); + + _commandBuffers.images.clear(); } void OpenGLDrawingContext::SetDPI(rct_drawpixelinfo * dpi) diff --git a/src/drawing/engines/opengl/TextureCache.cpp b/src/drawing/engines/opengl/TextureCache.cpp index 09c9063612..0608ed0e0f 100644 --- a/src/drawing/engines/opengl/TextureCache.cpp +++ b/src/drawing/engines/opengl/TextureCache.cpp @@ -17,6 +17,7 @@ #ifndef DISABLE_OPENGL #include +#include #include "../../../core/Memory.hpp" #include "TextureCache.h" @@ -44,14 +45,12 @@ void TextureCache::InvalidateImage(uint32 image) auto kvp = _imageTextureMap.find(image); if (kvp != _imageTextureMap.end()) { - GLuint texture = kvp->second; - glDeleteTextures(1, &texture); - + _atlases[kvp->second.index].Free(kvp->second); _imageTextureMap.erase(kvp); } } -GLuint TextureCache::GetOrLoadImageTexture(uint32 image) +CachedTextureInfo TextureCache::GetOrLoadImageTexture(uint32 image) { auto kvp = _imageTextureMap.find(image & 0x7FFFF); if (kvp != _imageTextureMap.end()) @@ -59,13 +58,13 @@ GLuint TextureCache::GetOrLoadImageTexture(uint32 image) return kvp->second; } - GLuint texture = LoadImageTexture(image); - _imageTextureMap[image & 0x7FFFF] = texture; + auto cacheInfo = LoadImageTexture(image); + _imageTextureMap[image & 0x7FFFF] = cacheInfo; - return texture; + return cacheInfo; } -GLuint TextureCache::GetOrLoadGlyphTexture(uint32 image, uint8 * palette) +CachedTextureInfo TextureCache::GetOrLoadGlyphTexture(uint32 image, uint8 * palette) { GlyphId glyphId; glyphId.Image = image; @@ -77,46 +76,83 @@ GLuint TextureCache::GetOrLoadGlyphTexture(uint32 image, uint8 * palette) return kvp->second; } - GLuint texture = LoadGlyphTexture(image, palette); - _glyphTextureMap[glyphId] = texture; + auto cacheInfo = LoadGlyphTexture(image, palette); + _glyphTextureMap[glyphId] = cacheInfo; - return texture; + return cacheInfo; } -GLuint TextureCache::LoadImageTexture(uint32 image) +void TextureCache::CreateAtlasesTexture() { - GLuint texture; - glGenTextures(1, &texture); + if (!_atlasesTextureInitialised) + { + // Determine width and height to use for texture atlases + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_atlasesTextureDimensions); + if (_atlasesTextureDimensions > TEXTURE_CACHE_MAX_ATLAS_SIZE) { + _atlasesTextureDimensions = TEXTURE_CACHE_MAX_ATLAS_SIZE; + } - rct_drawpixelinfo * dpi = GetImageAsDPI(image, 0); + // Determine maximum number of atlases (minimum of size and array limit) + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &_atlasesTextureIndicesLimit); + if (_atlasesTextureDimensions < _atlasesTextureIndicesLimit) _atlasesTextureIndicesLimit = _atlasesTextureDimensions; - glBindTexture(GL_TEXTURE_2D, texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, dpi->width, dpi->height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, dpi->bits); + // Create an array texture to hold all of the atlases + glGenTextures(1, &_atlasesTexture); + glBindTexture(GL_TEXTURE_2D_ARRAY, _atlasesTexture); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + _atlasesTextureInitialised = true; + _atlasesTextureIndices = 0; + } +} + +void TextureCache::EnlargeAtlasesTexture(GLuint newEntries) +{ + CreateAtlasesTexture(); + + GLuint newIndices = _atlasesTextureIndices + newEntries; + + // Retrieve current array data + auto oldPixels = std::vector(_atlasesTextureDimensions * _atlasesTextureDimensions * _atlasesTextureIndices); + glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, oldPixels.data()); + + // Reallocate array + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_R8UI, _atlasesTextureDimensions, _atlasesTextureDimensions, newIndices, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, nullptr); + + // Restore old data + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, _atlasesTextureDimensions, _atlasesTextureDimensions, _atlasesTextureIndices, GL_RED_INTEGER, GL_UNSIGNED_BYTE, oldPixels.data()); + _atlasesTextureIndices = newIndices; +} + +CachedTextureInfo TextureCache::LoadImageTexture(uint32 image) +{ + rct_drawpixelinfo * dpi = GetImageAsDPI(image, 0); + + auto cacheInfo = AllocateImage(dpi->width, dpi->height); + + glBindTexture(GL_TEXTURE_2D_ARRAY, _atlasesTexture); + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, cacheInfo.bounds.x, cacheInfo.bounds.y, cacheInfo.index, dpi->width, dpi->height, 1, GL_RED_INTEGER, GL_UNSIGNED_BYTE, dpi->bits); + DeleteDPI(dpi); - return texture; + return cacheInfo; } -GLuint TextureCache::LoadGlyphTexture(uint32 image, uint8 * palette) +CachedTextureInfo TextureCache::LoadGlyphTexture(uint32 image, uint8 * palette) { - GLuint texture; - glGenTextures(1, &texture); - rct_drawpixelinfo * dpi = GetGlyphAsDPI(image, palette); - glBindTexture(GL_TEXTURE_2D, texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, dpi->width, dpi->height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, dpi->bits); + auto cacheInfo = AllocateImage(dpi->width, dpi->height); + + glBindTexture(GL_TEXTURE_2D_ARRAY, _atlasesTexture); + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, cacheInfo.bounds.x, cacheInfo.bounds.y, cacheInfo.index, dpi->width, dpi->height, 1, GL_RED_INTEGER, GL_UNSIGNED_BYTE, dpi->bits); DeleteDPI(dpi); - return texture; + return cacheInfo; } void * TextureCache::GetImageAsARGB(uint32 image, uint32 tertiaryColour, uint32 * outWidth, uint32 * outHeight) @@ -135,6 +171,42 @@ void * TextureCache::GetImageAsARGB(uint32 image, uint32 tertiaryColour, uint32 return pixels32; } +CachedTextureInfo TextureCache::AllocateImage(int imageWidth, int imageHeight) +{ + CreateAtlasesTexture(); + + // Find an atlas that fits this image + for (Atlas& atlas : _atlases) + { + if (atlas.GetFreeSlots() > 0 && atlas.IsImageSuitable(imageWidth, imageHeight)) + { + return atlas.Allocate(imageWidth, imageHeight); + } + } + + // If there is no such atlas, then create a new one + if ((int) _atlases.size() >= _atlasesTextureIndicesLimit) + { + throw std::runtime_error("more texture atlases required, but device limit reached!"); + } + + int atlasIndex = (int) _atlases.size(); + int atlasSize = (int) powf(2, (float) Atlas::CalculateImageSizeOrder(imageWidth, imageHeight)); + +#ifdef DEBUG + log_verbose("new texture atlas #%d (size %d) allocated\n", atlasIndex, atlasSize); +#endif + + _atlases.push_back(std::move(Atlas(atlasIndex, atlasSize))); + _atlases.back().Initialise(_atlasesTextureDimensions, _atlasesTextureDimensions); + + // Enlarge texture array to support new atlas + EnlargeAtlasesTexture(1); + + // And allocate from the new atlas + return _atlases.back().Allocate(imageWidth, imageHeight); +} + rct_drawpixelinfo * TextureCache::GetImageAsDPI(uint32 image, uint32 tertiaryColour) { rct_g1_element * g1Element = gfx_get_g1_element(image & 0x7FFFF); @@ -204,30 +276,8 @@ void * TextureCache::ConvertDPIto32bpp(const rct_drawpixelinfo * dpi) void TextureCache::FreeTextures() { - // Free images - size_t numTextures = _imageTextureMap.size(); - auto textures = std::vector(numTextures); - for (auto kvp : _imageTextureMap) - { - textures.push_back(kvp.second); - } - if (textures.size() > 0) - { - glDeleteTextures(textures.size(), textures.data()); - } - - // Free glyphs - numTextures = _glyphTextureMap.size(); - textures.clear(); - textures.reserve(numTextures); - for (auto kvp : _glyphTextureMap) - { - textures.push_back(kvp.second); - } - if (textures.size() > 0) - { - glDeleteTextures(textures.size(), textures.data()); - } + // Free array texture + glDeleteTextures(1, &_atlasesTexture); } rct_drawpixelinfo * TextureCache::CreateDPI(sint32 width, sint32 height) @@ -253,4 +303,9 @@ void TextureCache::DeleteDPI(rct_drawpixelinfo* dpi) delete dpi; } +GLuint TextureCache::GetAtlasesTexture() +{ + return _atlasesTexture; +} + #endif /* DISABLE_OPENGL */ diff --git a/src/drawing/engines/opengl/TextureCache.h b/src/drawing/engines/opengl/TextureCache.h index 0d26a47bd3..bee3e055b6 100644 --- a/src/drawing/engines/opengl/TextureCache.h +++ b/src/drawing/engines/opengl/TextureCache.h @@ -16,10 +16,13 @@ #pragma once +#include #include +#include #include #include "../../../common.h" #include "OpenGLAPI.h" +#include "GLSLTypes.h" struct rct_drawpixelinfo; @@ -50,11 +53,150 @@ struct GlyphId }; }; +// This is the maximum width and height of each atlas, basically the +// granularity at which new atlases are allocated (2048 -> 4 MB of VRAM) +constexpr int TEXTURE_CACHE_MAX_ATLAS_SIZE = 2048; + +// Pixel dimensions of smallest supported slots in texture atlases +// Must be a power of 2! +constexpr int TEXTURE_CACHE_SMALLEST_SLOT = 32; + +// Location of an image (texture atlas index, slot and normalized coordinates) +struct CachedTextureInfo +{ + GLuint index; + GLuint slot; + vec4i bounds; + vec4f normalizedBounds; +}; + +// Represents a texture atlas that images of a given maximum size can be allocated from +// Atlases are all stored in the same 2D texture array, occupying the specified index +// Slots in atlases are always squares. +class Atlas +{ +private: + GLuint _index; + int _imageSize; + int _atlasWidth, _atlasHeight; + std::vector _freeSlots; + + int _cols, _rows; + +public: + Atlas(GLuint index, int imageSize) + { + _index = index; + _imageSize = imageSize; + } + + void Initialise(int atlasWidth, int atlasHeight) + { + _atlasWidth = atlasWidth; + _atlasHeight = atlasHeight; + + _cols = _atlasWidth / _imageSize; + _rows = _atlasHeight / _imageSize; + + _freeSlots.resize(_cols * _rows); + for (size_t i = 0; i < _freeSlots.size(); i++) + { + _freeSlots[i] = i; + } + } + + CachedTextureInfo Allocate(int actualWidth, int actualHeight) + { + assert(_freeSlots.size() > 0); + + GLuint slot = _freeSlots.back(); + _freeSlots.pop_back(); + + auto bounds = GetSlotCoordinates(slot, actualWidth, actualHeight); + + return + { + _index, + slot, + bounds, + NormalizeCoordinates(bounds) + }; + } + + void Free(const CachedTextureInfo& info) + { + assert(_index == info.index); + + _freeSlots.push_back(info.slot); + } + + // Checks if specified image would be tightly packed in this atlas + // by checking if it is within the right power of 2 range + bool IsImageSuitable(int actualWidth, int actualHeight) const + { + int imageOrder = CalculateImageSizeOrder(actualWidth, actualHeight); + int atlasOrder = (int) log2(_imageSize); + + return imageOrder == atlasOrder; + } + + int GetFreeSlots() const + { + return (int) _freeSlots.size(); + } + + static int CalculateImageSizeOrder(int actualWidth, int actualHeight) + { + int actualSize = std::max(actualWidth, actualHeight); + + if (actualSize < TEXTURE_CACHE_SMALLEST_SLOT) { + actualSize = TEXTURE_CACHE_SMALLEST_SLOT; + } + + return (int) ceil(log2f((float) actualSize)); + } + +private: + vec4i GetSlotCoordinates(GLuint slot, int actualWidth, int actualHeight) const + { + int row = slot / _cols; + int col = slot % _cols; + + return vec4i + { + _imageSize * col, + _imageSize * row, + _imageSize * col + actualWidth, + _imageSize * row + actualHeight, + }; + } + + vec4f NormalizeCoordinates(const vec4i& coords) const + { + return vec4f + { + coords.x / (float) _atlasWidth, + coords.y / (float) _atlasHeight, + coords.z / (float) _atlasWidth, + coords.w / (float) _atlasHeight + }; + } +}; + class TextureCache { private: - std::unordered_map _imageTextureMap; - std::unordered_map _glyphTextureMap; + bool _atlasesTextureInitialised = false; + + GLuint _atlasesTexture; + GLint _atlasesTextureDimensions; + GLuint _atlasesTextureIndices; + GLint _atlasesTextureIndicesLimit; + std::vector _atlases; + + std::unordered_map _imageTextureMap; + std::unordered_map _glyphTextureMap; + SDL_Color _palette[256]; public: @@ -62,12 +204,17 @@ public: ~TextureCache(); void SetPalette(const SDL_Color * palette); void InvalidateImage(uint32 image); - GLuint GetOrLoadImageTexture(uint32 image); - GLuint GetOrLoadGlyphTexture(uint32 image, uint8 * palette); + CachedTextureInfo GetOrLoadImageTexture(uint32 image); + CachedTextureInfo GetOrLoadGlyphTexture(uint32 image, uint8 * palette); + + GLuint GetAtlasesTexture(); private: - GLuint LoadImageTexture(uint32 image); - GLuint LoadGlyphTexture(uint32 image, uint8 * palette); + void CreateAtlasesTexture(); + void EnlargeAtlasesTexture(GLuint newEntries); + CachedTextureInfo LoadImageTexture(uint32 image); + CachedTextureInfo LoadGlyphTexture(uint32 image, uint8 * palette); + CachedTextureInfo AllocateImage(int imageWidth, int imageHeight); void * GetImageAsARGB(uint32 image, uint32 tertiaryColour, uint32 * outWidth, uint32 * outHeight); rct_drawpixelinfo * GetImageAsDPI(uint32 image, uint32 tertiaryColour); void * GetGlyphAsARGB(uint32 image, uint8 * palette, uint32 * outWidth, uint32 * outHeight); diff --git a/src/rct2.c b/src/rct2.c index 98d4e536c0..50b9827125 100644 --- a/src/rct2.c +++ b/src/rct2.c @@ -301,21 +301,22 @@ void rct2_draw(rct_drawpixelinfo *dpi) gCurrentDrawCount++; } -static uint32 _lastFPSUpdateTicks; -static uint32 _lastFPSTicks; -static float _currentFPS; +static time_t _lastSecond; +static int _currentFPS; +static int _frames; -static float rct2_measure_fps() +static void rct2_measure_fps() { - uint32 currentTicks = SDL_GetTicks(); - if (currentTicks - _lastFPSUpdateTicks > 500) { - _lastFPSUpdateTicks = currentTicks; + _frames++; - uint32 frameDelta = currentTicks - _lastFPSTicks; - _currentFPS = 1000.0f / frameDelta; + time_t currentTime = time(NULL); + + if (currentTime != _lastSecond) { + _currentFPS = _frames; + _frames = 0; } - _lastFPSTicks = currentTicks; - return _currentFPS; + + _lastSecond = currentTime; } static void rct2_draw_fps(rct_drawpixelinfo *dpi) @@ -332,9 +333,8 @@ static void rct2_draw_fps(rct_drawpixelinfo *dpi) ch = utf8_write_codepoint(ch, FORMAT_MEDIUMFONT); ch = utf8_write_codepoint(ch, FORMAT_OUTLINE); ch = utf8_write_codepoint(ch, FORMAT_WHITE); - - const char *formatString = (_currentFPS >= 100.0f ? "%.0f" : "%.1f"); - sprintf(ch, formatString, _currentFPS); + + sprintf(ch, "%d", _currentFPS); // Draw Text int stringWidth = gfx_get_string_width(buffer); diff --git a/src/windows/options.c b/src/windows/options.c index 58cd9933cc..8d7692b71b 100644 --- a/src/windows/options.c +++ b/src/windows/options.c @@ -587,6 +587,7 @@ static void window_options_mouseup(rct_window *w, int widgetIndex) switch (widgetIndex) { case WIDX_UNCAP_FPS_CHECKBOX: gConfigGeneral.uncap_fps ^= 1; + drawing_engine_set_fps_uncapped(gConfigGeneral.uncap_fps); config_save_default(); window_invalidate(w); break;