1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-15 03:52:40 +01:00

Implement sprite batch drawing using instancing

This commit is contained in:
Alexander Overvoorde
2016-07-23 00:56:41 +02:00
parent 84f2a8c17c
commit fbb7029de4
8 changed files with 111 additions and 85 deletions

View File

@@ -1,12 +1,13 @@
#version 150 #version 150
uniform ivec4 uClip;
uniform int uFlags;
uniform vec4 uColour;
uniform usampler2DArray uTexture;
uniform vec4 uPalette[256]; uniform vec4 uPalette[256];
uniform vec2 uTexCoordScale; uniform usampler2DArray uTexture;
uniform int uTexSlot;
flat in ivec4 fClip;
flat in int fFlags;
in vec4 fColour;
in vec2 fTexCoordScale;
flat in int fTexSlot;
in vec2 fPosition; in vec2 fPosition;
in vec2 fTextureCoordinate; in vec2 fTextureCoordinate;
@@ -15,16 +16,16 @@ out vec4 oColour;
void main() void main()
{ {
if (fPosition.x < uClip.x || fPosition.x > uClip.z || if (fPosition.x < fClip.x || fPosition.x > fClip.z ||
fPosition.y < uClip.y || fPosition.y > uClip.w) fPosition.y < fClip.y || fPosition.y > fClip.w)
{ {
discard; discard;
} }
vec4 texel = uPalette[texture(uTexture, vec3(fTextureCoordinate * uTexCoordScale, float(uTexSlot))).r]; vec4 texel = uPalette[texture(uTexture, vec3(fTextureCoordinate * fTexCoordScale, float(fTexSlot))).r];
if ((uFlags & 1) != 0) if ((fFlags & 1) != 0)
{ {
oColour = vec4(uColour.rgb, uColour.a * texel.a); oColour = vec4(fColour.rgb, fColour.a * texel.a);
} }
else else
{ {

View File

@@ -1,32 +1,43 @@
#version 150 #version 150
uniform ivec2 uScreenSize; uniform ivec2 uScreenSize;
uniform ivec4 uBounds;
uniform ivec4 uTextureCoordinates; uniform ivec4 uTextureCoordinates;
in ivec4 ivClip;
in vec2 ivTexCoordScale;
in int ivTexSlot;
in int ivFlags;
in vec4 ivColour;
in ivec4 ivBounds;
in uint vIndex; 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;
out vec2 fTexCoordScale;
flat out int fTexSlot;
void main() void main()
{ {
vec2 pos; vec2 pos;
switch (vIndex) { switch (vIndex) {
case 0u: case 0u:
pos = uBounds.xy; pos = ivBounds.xy;
fTextureCoordinate = uTextureCoordinates.xy; fTextureCoordinate = uTextureCoordinates.xy;
break; break;
case 1u: case 1u:
pos = uBounds.zy; pos = ivBounds.zy;
fTextureCoordinate = uTextureCoordinates.zy; fTextureCoordinate = uTextureCoordinates.zy;
break; break;
case 2u: case 2u:
pos = uBounds.xw; pos = ivBounds.xw;
fTextureCoordinate = uTextureCoordinates.xw; fTextureCoordinate = uTextureCoordinates.xw;
break; break;
case 3u: case 3u:
pos = uBounds.zw; pos = ivBounds.zw;
fTextureCoordinate = uTextureCoordinates.zw; fTextureCoordinate = uTextureCoordinates.zw;
break; break;
} }
@@ -38,5 +49,11 @@ void main()
pos.y = (pos.y * (2.0 / uScreenSize.y)) - 1.0; pos.y = (pos.y * (2.0 / uScreenSize.y)) - 1.0;
pos.y *= -1; pos.y *= -1;
fClip = ivClip;
fFlags = ivFlags;
fColour = ivColour;
fTexCoordScale = ivTexCoordScale;
fTexSlot = ivTexSlot;
gl_Position = vec4(pos, 0.0, 1.0); gl_Position = vec4(pos, 0.0, 1.0);
} }

View File

@@ -23,6 +23,7 @@ DrawImageShader::DrawImageShader() : OpenGLShaderProgram("drawimage")
GetLocations(); GetLocations();
glGenBuffers(1, &_vbo); glGenBuffers(1, &_vbo);
glGenBuffers(1, &_vboInstances);
glGenVertexArrays(1, &_vao); glGenVertexArrays(1, &_vao);
GLuint vertices[] = { 0, 1, 2, 2, 1, 3 }; GLuint vertices[] = { 0, 1, 2, 2, 1, 3 };
@@ -34,7 +35,6 @@ DrawImageShader::DrawImageShader() : OpenGLShaderProgram("drawimage")
glVertexAttribIPointer(vIndex, 1, GL_INT, 0, nullptr); glVertexAttribIPointer(vIndex, 1, GL_INT, 0, nullptr);
Use(); Use();
SetFlags(0);
SetTextureCoordinates(0, 0, 1, 1); SetTextureCoordinates(0, 0, 1, 1);
glUniform1i(uTexture, 0); glUniform1i(uTexture, 0);
} }
@@ -42,6 +42,7 @@ DrawImageShader::DrawImageShader() : OpenGLShaderProgram("drawimage")
DrawImageShader::~DrawImageShader() DrawImageShader::~DrawImageShader()
{ {
glDeleteBuffers(1, &_vbo); glDeleteBuffers(1, &_vbo);
glDeleteBuffers(1, &_vboInstances);
glDeleteVertexArrays(1, &_vao); glDeleteVertexArrays(1, &_vao);
glBindVertexArray(_vao); glBindVertexArray(_vao);
@@ -50,15 +51,9 @@ DrawImageShader::~DrawImageShader()
void DrawImageShader::GetLocations() void DrawImageShader::GetLocations()
{ {
uScreenSize = GetUniformLocation("uScreenSize"); uScreenSize = GetUniformLocation("uScreenSize");
uClip = GetUniformLocation("uClip");
uBounds = GetUniformLocation("uBounds");
uTextureCoordinates = GetUniformLocation("uTextureCoordinates");
uTexture = GetUniformLocation("uTexture"); uTexture = GetUniformLocation("uTexture");
uColour = GetUniformLocation("uColour");
uFlags = GetUniformLocation("uFlags");
uPalette = GetUniformLocation("uPalette"); uPalette = GetUniformLocation("uPalette");
uTexCoordScale = GetUniformLocation("uTexCoordScale"); uTextureCoordinates = GetUniformLocation("uTextureCoordinates");
uTexSlot = GetUniformLocation("uTexSlot");
vIndex = GetAttributeLocation("vIndex"); vIndex = GetAttributeLocation("vIndex");
} }
@@ -68,14 +63,9 @@ void DrawImageShader::SetScreenSize(sint32 width, sint32 height)
glUniform2i(uScreenSize, width, height); glUniform2i(uScreenSize, width, height);
} }
void DrawImageShader::SetClip(sint32 left, sint32 top, sint32 right, sint32 bottom) void DrawImageShader::SetPalette(const vec4f *glPalette)
{ {
glUniform4i(uClip, left, top, right, bottom); glUniform4fv(uPalette, 256, (const GLfloat *) glPalette);
}
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) void DrawImageShader::SetTextureCoordinates(sint32 left, sint32 top, sint32 right, sint32 bottom)
@@ -83,36 +73,38 @@ void DrawImageShader::SetTextureCoordinates(sint32 left, sint32 top, sint32 righ
glUniform4i(uTextureCoordinates, left, top, right, bottom); glUniform4i(uTextureCoordinates, left, top, right, bottom);
} }
void DrawImageShader::SetTextureCoordScale(float width, float height) { void DrawImageShader::DrawInstances(const std::vector<DrawImageInstance>& instances)
glUniform2f(uTexCoordScale, width, height);
}
void DrawImageShader::SetTextureSlot(GLuint slot)
{ {
glUniform1i(uTexSlot, slot); // Copy instance data to vbo for single-use
} glBindBuffer(GL_ARRAY_BUFFER, _vboInstances);
glBufferData(GL_ARRAY_BUFFER, sizeof(instances[0]) * instances.size(), instances.data(), GL_STREAM_DRAW);
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)
{
SetBounds(left, top, right, bottom);
// Bind vertex attributes
glBindVertexArray(_vao); glBindVertexArray(_vao);
glDrawArrays(GL_TRIANGLES, 0, 6);
glVertexAttribIPointer(GetAttributeLocation("ivClip"), 4, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, clip));
glVertexAttribPointer(GetAttributeLocation("ivTexCoordScale"), 2, GL_FLOAT, GL_FALSE, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, texCoordScale));
glVertexAttribIPointer(GetAttributeLocation("ivTexSlot"), 1, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, texSlot));
glVertexAttribIPointer(GetAttributeLocation("ivFlags"), 1, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, flags));
glVertexAttribPointer(GetAttributeLocation("ivColour"), 4, GL_FLOAT, GL_FALSE, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, colour));
glVertexAttribIPointer(GetAttributeLocation("ivBounds"), 4, GL_INT, sizeof(DrawImageInstance), (void*) offsetof(DrawImageInstance, bounds));
glEnableVertexAttribArray(GetAttributeLocation("ivClip"));
glEnableVertexAttribArray(GetAttributeLocation("ivTexCoordScale"));
glEnableVertexAttribArray(GetAttributeLocation("ivTexSlot"));
glEnableVertexAttribArray(GetAttributeLocation("ivFlags"));
glEnableVertexAttribArray(GetAttributeLocation("ivColour"));
glEnableVertexAttribArray(GetAttributeLocation("ivBounds"));
glVertexAttribDivisor(GetAttributeLocation("ivClip"), 1);
glVertexAttribDivisor(GetAttributeLocation("ivTexCoordScale"), 1);
glVertexAttribDivisor(GetAttributeLocation("ivTexSlot"), 1);
glVertexAttribDivisor(GetAttributeLocation("ivFlags"), 1);
glVertexAttribDivisor(GetAttributeLocation("ivColour"), 1);
glVertexAttribDivisor(GetAttributeLocation("ivBounds"), 1);
// Draw instances
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, instances.size());
} }
#endif /* DISABLE_OPENGL */ #endif /* DISABLE_OPENGL */

View File

@@ -19,24 +19,30 @@
#include "GLSLTypes.h" #include "GLSLTypes.h"
#include "OpenGLShaderProgram.h" #include "OpenGLShaderProgram.h"
#include <SDL_pixels.h> #include <SDL_pixels.h>
#include <vector>
// Per-instance data for images
struct DrawImageInstance {
vec4i clip;
vec2f texCoordScale;
int texSlot;
int flags;
vec4f colour;
vec4i bounds;
};
class DrawImageShader : public OpenGLShaderProgram class DrawImageShader : public OpenGLShaderProgram
{ {
private: private:
GLuint uScreenSize; GLuint uScreenSize;
GLuint uClip;
GLuint uBounds;
GLuint uTextureCoordinates;
GLuint uTexture; GLuint uTexture;
GLuint uColour;
GLuint uFlags;
GLuint uPalette; GLuint uPalette;
GLuint uTexCoordScale; GLuint uTextureCoordinates;
GLuint uTexSlot;
GLuint vIndex; GLuint vIndex;
GLuint _vbo; GLuint _vbo;
GLuint _vboInstances;
GLuint _vao; GLuint _vao;
SDL_Color _palette[256]; SDL_Color _palette[256];
@@ -46,15 +52,9 @@ public:
~DrawImageShader() override; ~DrawImageShader() override;
void SetScreenSize(sint32 width, sint32 height); 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 SetTextureCoordScale(float width, float height);
void SetTextureSlot(GLuint slot);
void SetColour(vec4f colour);
void SetFlags(uint32 flags);
void SetPalette(const vec4f *glPalette); void SetPalette(const vec4f *glPalette);
void Draw(sint32 left, sint32 top, sint32 right, sint32 bottom); void SetTextureCoordinates(sint32 left, sint32 top, sint32 right, sint32 bottom);
void DrawInstances(const std::vector<DrawImageInstance>& instances);
private: private:
void GetLocations(); void GetLocations();

View File

@@ -110,6 +110,8 @@ static const char * TryLoadAllProcAddresses()
SetupOpenGLFunction(glVertexAttribPointer); SetupOpenGLFunction(glVertexAttribPointer);
SetupOpenGLFunction(glTexStorage3D); SetupOpenGLFunction(glTexStorage3D);
SetupOpenGLFunction(glDebugMessageCallback); SetupOpenGLFunction(glDebugMessageCallback);
SetupOpenGLFunction(glDrawArraysInstanced);
SetupOpenGLFunction(glVertexAttribDivisor);
return nullptr; return nullptr;
} }

View File

@@ -162,6 +162,8 @@ GLAPI_DECL PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer GLAP
GLAPI_DECL PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer GLAPI_SET; GLAPI_DECL PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer GLAPI_SET;
GLAPI_DECL PFNGLTEXSTORAGE3DPROC glTexStorage3D GLAPI_SET; GLAPI_DECL PFNGLTEXSTORAGE3DPROC glTexStorage3D GLAPI_SET;
GLAPI_DECL PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback GLAPI_SET; GLAPI_DECL PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback GLAPI_SET;
GLAPI_DECL PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced GLAPI_SET;
GLAPI_DECL PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor GLAPI_SET;
#endif /* OPENGL_NO_LINK */ #endif /* OPENGL_NO_LINK */

View File

@@ -803,7 +803,8 @@ void OpenGLDrawingContext::DrawSpriteRawMasked(sint32 x, sint32 y, uint32 maskIm
_commandBuffers.maskedImages.push_back(command); _commandBuffers.maskedImages.push_back(command);
// Currently not properly ordered with regular images yet // Currently not properly ordered with regular images yet
FlushCommandBuffers(); // TODO: uncomment once masked images are mixed with normal images using depth sorting
//FlushCommandBuffers();
} }
void OpenGLDrawingContext::DrawSpriteSolid(uint32 image, sint32 x, sint32 y, uint8 colour) void OpenGLDrawingContext::DrawSpriteSolid(uint32 image, sint32 x, sint32 y, uint8 colour)
@@ -943,18 +944,29 @@ void OpenGLDrawingContext::FlushLines() {
} }
void OpenGLDrawingContext::FlushImages() { void OpenGLDrawingContext::FlushImages() {
if (_commandBuffers.images.size() == 0) return;
OpenGLAPI::SetTexture(0, GL_TEXTURE_2D_ARRAY, _textureCache->GetArrayTexture()); OpenGLAPI::SetTexture(0, GL_TEXTURE_2D_ARRAY, _textureCache->GetArrayTexture());
std::vector<DrawImageInstance> instances;
instances.reserve(_commandBuffers.images.size());
for (const auto& command : _commandBuffers.images) { for (const auto& command : _commandBuffers.images) {
_drawImageShader->Use(); DrawImageInstance instance;
_drawImageShader->SetClip(command.clip[0], command.clip[1], command.clip[2], command.clip[3]);
_drawImageShader->SetTextureCoordScale(command.texColour.dimensions.x, command.texColour.dimensions.y); instance.clip = {command.clip[0], command.clip[1], command.clip[2], command.clip[3]};
_drawImageShader->SetTextureSlot(command.texColour.slot); instance.texCoordScale = command.texColour.dimensions;
_drawImageShader->SetFlags(command.flags); instance.texSlot = command.texColour.slot;
_drawImageShader->SetColour(command.colour); instance.flags = command.flags;
_drawImageShader->Draw(command.bounds[0], command.bounds[1], command.bounds[2], command.bounds[3]); instance.colour = command.colour;
instance.bounds = {command.bounds[0], command.bounds[1], command.bounds[2], command.bounds[3]};
instances.push_back(instance);
} }
_drawImageShader->Use();
_drawImageShader->DrawInstances(instances);
_commandBuffers.images.clear(); _commandBuffers.images.clear();
} }

View File

@@ -64,7 +64,7 @@ CachedTextureInfo TextureCache::GetOrLoadImageTexture(uint32 image)
auto cacheInfo = LoadImageTexture(image); auto cacheInfo = LoadImageTexture(image);
_imageTextureMap[image & 0x7FFFF] = cacheInfo; _imageTextureMap[image & 0x7FFFF] = cacheInfo;
printf("%d slots left\n", (int) _freeSlots.size()); //printf("%d slots left\n", (int) _freeSlots.size());
return cacheInfo; return cacheInfo;
} }
@@ -86,7 +86,7 @@ CachedTextureInfo TextureCache::GetOrLoadGlyphTexture(uint32 image, uint8 * pale
auto cacheInfo = LoadGlyphTexture(image, palette); auto cacheInfo = LoadGlyphTexture(image, palette);
_glyphTextureMap[glyphId] = cacheInfo; _glyphTextureMap[glyphId] = cacheInfo;
printf("%d slots left\n", (int) _freeSlots.size()); //printf("%d slots left\n", (int) _freeSlots.size());
return cacheInfo; return cacheInfo;
} }