Merge pull request #4147 from Overv/opengl-optimization

Optimise the OpenGL drawing engine by batching the drawing of sprites.
This commit is contained in:
Ted John 2016-07-27 22:22:28 +01:00 committed by GitHub
commit 0cd0eb5ec5
26 changed files with 738 additions and 431 deletions

View File

@ -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 = "<group>"; };
D43407C11D0E14BE00C2B3D4 /* CopyFramebufferShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopyFramebufferShader.h; sourceTree = "<group>"; };
D43407C21D0E14BE00C2B3D4 /* DrawImageMaskedShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DrawImageMaskedShader.cpp; sourceTree = "<group>"; };
D43407C31D0E14BE00C2B3D4 /* DrawImageMaskedShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrawImageMaskedShader.h; sourceTree = "<group>"; };
D43407C41D0E14BE00C2B3D4 /* DrawImageShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DrawImageShader.cpp; sourceTree = "<group>"; };
D43407C51D0E14BE00C2B3D4 /* DrawImageShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrawImageShader.h; sourceTree = "<group>"; };
D43407C61D0E14BE00C2B3D4 /* DrawLineShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DrawLineShader.cpp; sourceTree = "<group>"; };
@ -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 */,

View File

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

View File

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

View File

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

View File

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

View File

@ -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.

View File

@ -56,7 +56,6 @@
<ClCompile Include="src\drawing\drawing.c" />
<ClCompile Include="src\drawing\drawing_fast.cpp" />
<ClCompile Include="src\drawing\engines\opengl\CopyFramebufferShader.cpp" />
<ClCompile Include="src\drawing\engines\opengl\DrawImageMaskedShader.cpp" />
<ClCompile Include="src\drawing\engines\opengl\DrawImageShader.cpp" />
<ClCompile Include="src\drawing\engines\opengl\DrawLineShader.cpp" />
<ClCompile Include="src\drawing\engines\opengl\FillRectShader.cpp" />
@ -383,7 +382,7 @@
<ClInclude Include="src\drawing\drawing.h" />
<ClInclude Include="src\drawing\engines\OpenGLAPI.h" />
<ClInclude Include="src\drawing\engines\opengl\CopyFramebufferShader.h" />
<ClInclude Include="src\drawing\engines\opengl\DrawImageMaskedShader.h" />
<ClInclude Include="src\drawing\engines\opengl\DrawCommands.h" />
<ClInclude Include="src\drawing\engines\opengl\DrawImageShader.h" />
<ClInclude Include="src\drawing\engines\opengl\DrawLineShader.h" />
<ClInclude Include="src\drawing\engines\opengl\FillRectShader.h" />

View File

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

View File

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

View File

@ -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
}

View File

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

View File

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

View File

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

View File

@ -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 */

View File

@ -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 <SDL_pixels.h>
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();
};

View File

@ -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<DrawImageInstance>& 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 */

View File

@ -19,22 +19,41 @@
#include "GLSLTypes.h"
#include "OpenGLShaderProgram.h"
#include <SDL_pixels.h>
#include <vector>
// 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<DrawImageInstance>& instances);
private:
void GetLocations();

View File

@ -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 */

View File

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

View File

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

View File

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

View File

@ -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<DrawRectCommand> rectangles;
std::vector<DrawLineCommand> lines;
std::vector<DrawImageCommand> 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<DrawImageInstance> 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)

View File

@ -17,6 +17,7 @@
#ifndef DISABLE_OPENGL
#include <vector>
#include <stdexcept>
#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<char>(_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<GLuint>(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 */

View File

@ -16,10 +16,13 @@
#pragma once
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <SDL_pixels.h>
#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<GLuint> _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<uint32, GLuint> _imageTextureMap;
std::unordered_map<GlyphId, GLuint, GlyphId::Hash, GlyphId::Equal> _glyphTextureMap;
bool _atlasesTextureInitialised = false;
GLuint _atlasesTexture;
GLint _atlasesTextureDimensions;
GLuint _atlasesTextureIndices;
GLint _atlasesTextureIndicesLimit;
std::vector<Atlas> _atlases;
std::unordered_map<uint32, CachedTextureInfo> _imageTextureMap;
std::unordered_map<GlyphId, CachedTextureInfo, GlyphId::Hash, GlyphId::Equal> _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);

View File

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

View File

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