Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed up terrain drawing with glDrawArrays from OpenGL 2.0. #120

Merged
merged 13 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ def configure(env, server_only):
configfile.add("HAVE_OPENGL ", "Defined when OpenGL support is present and compiled")
env.Append(LIBS=gl_libraries)

#Do checks for epoxy, which handles OpenGL 2+ function pointer loading.
if not server_only:
if conf.CheckLib('epoxy') and conf.CheckCXXHeader('epoxy/gl.h'):
env.Append(LIBS=["epoxy"])
else:
print("Could not find epoxy/gl.h")
#Don't add epoxy to `missing` list because that breaks compiling without OpenGL.

#Do checks for fribidi
if not server_only and conf.CheckLib('fribidi') and conf.CheckCXXHeader('fribidi/fribidi.h'):
configfile.add("HAVE_FRIBIDI ", "Defined when FRIBIDI support is present and compiled")
Expand Down
2 changes: 1 addition & 1 deletion debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Source: glob2
Section: games
Priority: optional
Maintainer: Stephane Magnenat <stephane at magnenat dot net>
Build-Depends: debhelper (>> 6.0.0), quilt (>= 0.40), scons, libsdl2-dev (>=2.0.0), libsdl2-image-dev (>=2.0.0), libsdl2-net-dev (>=2.0.0), libsdl2-ttf-dev, libglu1-mesa-dev | libglu-dev, libvorbis-dev, libspeex-dev, libfreetype6-dev, libboost-dev, libboost-thread-dev, libboost-date-time-dev, libfribidi-dev, portaudio19-dev, libboost-math-dev
Build-Depends: debhelper (>> 6.0.0), quilt (>= 0.40), scons, libsdl2-dev (>=2.0.0), libsdl2-image-dev (>=2.0.0), libsdl2-net-dev (>=2.0.0), libsdl2-ttf-dev, libglu1-mesa-dev | libglu-dev, libvorbis-dev, libspeex-dev, libfreetype6-dev, libboost-dev, libboost-thread-dev, libboost-date-time-dev, libfribidi-dev, portaudio19-dev, libboost-math-dev, libepoxy-dev
Standards-Version: 3.8.1
Homepage: http://globulation2.org

Expand Down
43 changes: 41 additions & 2 deletions libgag/include/SDLGraphicContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <string>
#include <iostream>
#include <valarray>
#include <boost/optional.hpp>

#include <set>
#include <boost/tuple/tuple.hpp>
Expand Down Expand Up @@ -151,15 +152,31 @@ namespace GAGCore
virtual void drawString(DrawableSurface *Surface, int x, int y, int w, const std::string text, Uint8 alpha) = 0;
virtual void drawString(DrawableSurface *Surface, float x, float y, float w, const std::string text, Uint8 alpha) = 0;
};

// Texture dimensions
struct TextureInfo
{
//! which animation or texture atlas this surface is part of.
Sprite* sprite = nullptr;
//! sprite sheet coordinates
int texX = 0;
int texY = 0;
//! width and height of this tile if using atlas
int w = 0;
int h = 0;
};

//! A surface on which we can draw
class DrawableSurface
{
protected:
friend struct Color;
friend class GraphicContext;
friend class Sprite;
//! the underlying software SDL surface
SDL_Surface *sdlsurface;
// Texture dimensions
boost::optional<TextureInfo> textureInfo;
//! The clipping rect, we do not draw outside it
SDL_Rect clipRect;
//! this surface has been modified since latest blit
Expand Down Expand Up @@ -209,8 +226,11 @@ namespace GAGCore
virtual void shiftHSV(float hue, float sat, float lum);

// accessors
virtual int getW(void) { return sdlsurface->w; }
virtual int getH(void) { return sdlsurface->h; }
virtual int getW(void) { if (textureInfo) return textureInfo->w; return sdlsurface->w; }
virtual int getH(void) { if (textureInfo) return textureInfo->h; return sdlsurface->h; }

virtual int getTexX(void) { if (textureInfo) { return textureInfo->texX; } return 0; }
virtual int getTexY(void) { if (textureInfo) { return textureInfo->texY; } return 0; }

// capability querying
virtual bool canDrawStretchedSprite(void) { return false; }
Expand Down Expand Up @@ -375,6 +395,8 @@ namespace GAGCore

virtual void drawSurface(int x, int y, int w, int h, DrawableSurface *surface, int sx, int sy, int sw, int sh, Uint8 alpha = Color::ALPHA_OPAQUE);
virtual void drawSurface(float x, float y, float w, float h, DrawableSurface *surface, int sx, int sy, int sw, int sh, Uint8 alpha = Color::ALPHA_OPAQUE);

void finishDrawingSprite(Sprite* sprite, Uint8 alpha);

virtual void drawAlphaMap(const std::valarray<float> &map, int mapW, int mapH, int x, int y, int cellW, int cellH, const Color &color);
virtual void drawAlphaMap(const std::valarray<unsigned char> &map, int mapW, int mapH, int x, int y, int cellW, int cellH, const Color &color);
Expand Down Expand Up @@ -416,10 +438,26 @@ namespace GAGCore
RotatedImage(DrawableSurface *s) { orig = s; }
~RotatedImage();
};

friend class GraphicContext;

std::string fileName;
//#define DEBUG_SPRITE_NOT_DRAWN
#ifdef DEBUG_SPRITE_NOT_DRAWN
static std::vector <Sprite*> sprites;
#endif
std::vector <DrawableSurface *> images;
std::vector <RotatedImage *> rotated;

// Sprite sheet stuff to efficiently draw terrain/water/units.
#ifdef HAVE_OPENGL
std::vector <float> vertices;
std::vector <float> texCoords;
unsigned int vbo = 0; // Vertex buffer object
unsigned int texCoordBuffer = 0;
std::unique_ptr<const DrawableSurface> atlas = nullptr;
#endif
static void checkAllSpritesDrawn();
Color actColor;

friend class DrawableSurface;
Expand All @@ -428,6 +466,7 @@ namespace GAGCore
void loadFrame(SDL_RWops *frameStream, SDL_RWops *rotatedStream);
//! Check if index is within bound and return true, assert false and return false otherwise
bool checkBound(int index);
bool createTextureAtlas();
//! Return a rotated drawable surface for actColor, create it if necessary
virtual DrawableSurface *getRotatedSurface(int index);

Expand Down
111 changes: 93 additions & 18 deletions libgag/src/GraphicContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
#include <string>
#include <sstream>
#include <iostream>
#ifdef HAVE_OPENGL
#include <epoxy/gl.h>
stephanemagnenat marked this conversation as resolved.
Show resolved Hide resolved
#ifdef _MSC_VER
#include <epoxy/wgl.h>
#else
#include <epoxy/glx.h>
#endif // _MSC_VER
#endif // HAVE_OPENGL
#include "SDL_ttf.h"
#include <SDL_image.h>
#include <math.h>
Expand All @@ -38,6 +46,7 @@
#endif

#ifdef HAVE_OPENGL
#define GL_GLEXT_PROTOTYPES
#if defined(__APPLE__) || defined(OPENGL_HEADER_DIRECTORY_OPENGL)
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
Expand Down Expand Up @@ -303,6 +312,8 @@ namespace GAGCore
void DrawableSurface::allocateTexture(void)
{
#ifdef HAVE_OPENGL
if (textureInfo)
return;
if (_gc->optionFlags & GraphicContext::USEGPU)
{
glGenTextures(1, reinterpret_cast<GLuint*>(&texture));
Expand Down Expand Up @@ -345,6 +356,10 @@ namespace GAGCore
void DrawableSurface::uploadToTexture(void)
{
#ifdef HAVE_OPENGL
if (textureInfo)
{
return;
}
if (_gc->optionFlags & GraphicContext::USEGPU)
{
glState.setTexture(texture);
Expand Down Expand Up @@ -1049,22 +1064,22 @@ namespace GAGCore

void DrawableSurface::drawSurface(int x, int y, DrawableSurface *surface, Uint8 alpha)
{
drawSurface(x, y, surface, 0, 0, surface->getW(), surface->getH(), alpha);
drawSurface(x, y, surface, surface->getTexX(), surface->getTexY(), surface->getW(), surface->getH(), alpha);
}

void DrawableSurface::drawSurface(float x, float y, DrawableSurface *surface, Uint8 alpha)
{
drawSurface(x, y, surface, 0, 0, surface->getW(), surface->getH(), alpha);
drawSurface(x, y, surface, surface->getTexX(), surface->getTexY(), surface->getW(), surface->getH(), alpha);
}

void DrawableSurface::drawSurface(int x, int y, int w, int h, DrawableSurface *surface, Uint8 alpha)
{
drawSurface(x, y, w, h, surface, 0, 0, surface->getW(), surface->getH(), alpha);
drawSurface(x, y, w, h, surface, surface->getTexX(), surface->getTexY(), surface->getW(), surface->getH(), alpha);
}

void DrawableSurface::drawSurface(float x, float y, float w, float h, DrawableSurface *surface, Uint8 alpha)
{
drawSurface(x, y, w, h, surface, 0, 0, surface->getW(), surface->getH(), alpha);
drawSurface(x, y, w, h, surface, surface->getTexX(), surface->getTexY(), surface->getW(), surface->getH(), alpha);
}

void DrawableSurface::drawSurface(int x, int y, DrawableSurface *surface, int sx, int sy, int sw, int sh, Uint8 alpha)
Expand Down Expand Up @@ -1617,22 +1632,22 @@ namespace GAGCore

void GraphicContext::drawSurface(int x, int y, DrawableSurface *surface, Uint8 alpha)
{
drawSurface(x, y, surface, 0, 0, surface->getW(), surface->getH(), alpha);
drawSurface(x, y, surface, surface->getTexX(), surface->getTexY(), surface->getW(), surface->getH(), alpha);
}

void GraphicContext::drawSurface(float x, float y, DrawableSurface *surface, Uint8 alpha)
{
drawSurface(x, y, surface, 0, 0, surface->getW(), surface->getH(), alpha);
drawSurface(x, y, surface, surface->getTexX(), surface->getTexY(), surface->getW(), surface->getH(), alpha);
}

void GraphicContext::drawSurface(int x, int y, int w, int h, DrawableSurface *surface, Uint8 alpha)
{
drawSurface(x, y, w, h, surface, 0, 0, surface->getW(), surface->getH(), alpha);
drawSurface(x, y, w, h, surface, surface->getTexX(), surface->getTexY(), surface->getW(), surface->getH(), alpha);
}

void GraphicContext::drawSurface(float x, float y, float w, float h, DrawableSurface *surface, Uint8 alpha)
{
drawSurface(x, y, w, h, surface, 0, 0, surface->getW(), surface->getH(), alpha);
drawSurface(x, y, w, h, surface, surface->getTexX(), surface->getTexY(), surface->getW(), surface->getH(), alpha);
}

void GraphicContext::drawSurface(int x, int y, DrawableSurface *surface, int sx, int sy, int sw, int sh, Uint8 alpha)
Expand Down Expand Up @@ -1682,22 +1697,80 @@ namespace GAGCore

// draw
glState.setTexture(surface->texture);
glBegin(GL_QUADS);
glTexCoord2f(static_cast<float>(sx) * surface->texMultX, static_cast<float>(sy) * surface->texMultY);
glVertex2f(x, y);
glTexCoord2f(static_cast<float>(sx + sw) * surface->texMultX, static_cast<float>(sy) * surface->texMultY);
glVertex2f(x+w, y);
glTexCoord2f(static_cast<float>(sx + sw) * surface->texMultX, static_cast<float>(sy + sh) * surface->texMultY);
glVertex2f(x+w, y+h);
glTexCoord2f(static_cast<float>(sx) * surface->texMultX, static_cast<float>(sy + sh) * surface->texMultY);
glVertex2f(x, y+h);
glEnd();
if (surface->textureInfo && surface->textureInfo->sprite && alpha == Color::ALPHA_OPAQUE)
{
Sprite* sprite = surface->textureInfo->sprite;
sprite->vertices.insert(sprite->vertices.end(), { x, y, x + w, y, x + w, y + h, x, y + h });
sprite->texCoords.insert(sprite->texCoords.end(), {
static_cast<float>(sx) * surface->texMultX, static_cast<float>(sy) * surface->texMultY,
static_cast<float>(sx + sw) * surface->texMultX, static_cast<float>(sy) * surface->texMultY,
static_cast<float>(sx + sw) * surface->texMultX, static_cast<float>(sy + sh) * surface->texMultY,
static_cast<float>(sx) * surface->texMultX, static_cast<float>(sy + sh) * surface->texMultY
});
}
else
{
glBegin(GL_QUADS);
glTexCoord2f(static_cast<float>(sx) * surface->texMultX, static_cast<float>(sy) * surface->texMultY);
glVertex2f(x, y);
glTexCoord2f(static_cast<float>(sx + sw) * surface->texMultX, static_cast<float>(sy) * surface->texMultY);
glVertex2f(x + w, y);
glTexCoord2f(static_cast<float>(sx + sw) * surface->texMultX, static_cast<float>(sy + sh) * surface->texMultY);
glVertex2f(x + w, y + h);
glTexCoord2f(static_cast<float>(sx) * surface->texMultX, static_cast<float>(sy + sh) * surface->texMultY);
glVertex2f(x, y + h);
glEnd();
}
}
else
#endif
DrawableSurface::drawSurface(static_cast<int>(x), static_cast<int>(y), static_cast<int>(w), static_cast<int>(h), surface, sx, sy, sw, sh, alpha);
}

// Lets us efficiently draw terrain and water.
void GraphicContext::finishDrawingSprite(Sprite* sprite, Uint8 alpha)
{
#ifdef HAVE_OPENGL
if (_gc->optionFlags & GraphicContext::USEGPU)
{
if (!sprite->atlas)
{
// No sprite sheet, so we have nothing to draw.
assert(sprite->vertices.empty());
assert(sprite->texCoords.empty());
return;
}
if (sprite->vertices.empty() || sprite->texCoords.empty())
{
// No data.
return;
}
// state change
glState.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glState.doBlend(true);
glState.doTexture(true);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glColor4ub(255, 255, 255, alpha);
glState.setTexture(sprite->atlas->texture);
glBindBuffer(GL_ARRAY_BUFFER, sprite->vbo);
glBufferData(GL_ARRAY_BUFFER, sprite->vertices.size() * sizeof(float), sprite->vertices.data(), GL_STREAM_DRAW);
glVertexPointer(2, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, sprite->texCoordBuffer);
glBufferData(GL_ARRAY_BUFFER, sprite->texCoords.size() * sizeof(float), sprite->texCoords.data(), GL_STREAM_DRAW);
glTexCoordPointer(2, GL_FLOAT, 0, 0);
glDrawArrays(GL_QUADS, 0, sprite->vertices.size() / 2);

sprite->vertices.clear();
sprite->texCoords.clear();

glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
#endif
}

void GraphicContext::drawAlphaMap(const std::valarray<float> &map, int mapW, int mapH, int x, int y, int cellW, int cellH, const Color &color)
{
#ifdef HAVE_OPENGL
Expand Down Expand Up @@ -2168,9 +2241,11 @@ namespace GAGCore
cursorManager.draw(this, mx, my);
}


#ifdef HAVE_OPENGL
if (optionFlags & GraphicContext::USEGPU)
{
Sprite::checkAllSpritesDrawn();
SDL_GL_SwapWindow(window);
//fprintf(stderr, "%d allocated GPU textures\n", glState.alocatedTextureCount);
}
Expand Down
Loading