graphics fixes

* zooming without real FBOs kinda works, but with lower quality
* hardware detection for glGenerateMipmaps
* possibility to disable bilinear filtering, mipmaps, framebuffers, and realtime mipmap generation in g_graphics
* otclient works well using 3D acceleration in VirtualBox again
* many fixes regarding FBOs fallback implementation
master
Eduardo Bart 12 years ago
parent 01d5fad315
commit c7469e4454

@ -23,6 +23,7 @@
#include "framebuffer.h"
#include "graphics.h"
#include "texture.h"
#include <framework/platform/platformwindow.h>
uint FrameBuffer::boundFbo = 0;
std::vector<bool> auxBuffers;
@ -42,14 +43,14 @@ FrameBuffer::FrameBuffer(const Size& size)
void FrameBuffer::internalCreate()
{
if(g_graphics.hasFBO()) {
if(g_graphics.canUseFBO()) {
glGenFramebuffers(1, &m_fbo);
if(!m_fbo)
logFatal("Unable to create framebuffer object");
} else { // use auxiliar buffers when FBOs are not supported
m_fbo = 0;
if(auxBuffers.size() == 0) {
int maxAuxs;
int maxAuxs = 0;
glGetIntegerv(GL_AUX_BUFFERS, &maxAuxs);
auxBuffers.resize(maxAuxs+1, false);
}
@ -57,16 +58,17 @@ void FrameBuffer::internalCreate()
if(auxBuffers[i] == false) {
m_fbo = i;
auxBuffers[i] = true;
break;
}
}
if(!m_fbo)
logFatal("There is no available auxiliar buffer for a new framebuffer");
logFatal("There is no available auxiliar buffer for a new framebuffer, total AUXs: ", auxBuffers.size()-1);
}
}
FrameBuffer::~FrameBuffer()
{
if(g_graphics.hasFBO()) {
if(g_graphics.canUseFBO()) {
glDeleteFramebuffers(1, &m_fbo);
} else {
auxBuffers[m_fbo] = false;
@ -84,7 +86,7 @@ void FrameBuffer::resize(const Size& size)
m_texture = TexturePtr(new Texture(size.width(), size.height(), 4));
m_texture->setSmooth(true);
if(g_graphics.hasFBO()) {
if(g_graphics.canUseFBO()) {
internalBind();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture->getId(), 0);
@ -121,11 +123,6 @@ void FrameBuffer::release()
g_graphics.setViewportSize(m_oldViewportSize);
}
void FrameBuffer::generateMipmaps()
{
m_texture->generateMipmaps();
}
void FrameBuffer::draw(const Rect& dest, const Rect& src)
{
g_painter.drawTexturedRect(dest, m_texture, src);
@ -133,7 +130,10 @@ void FrameBuffer::draw(const Rect& dest, const Rect& src)
void FrameBuffer::draw(const Rect& dest)
{
g_painter.drawTexturedRect(dest, m_texture);
if(g_graphics.canUseFBO())
g_painter.drawTexturedRect(dest, m_texture);
else
g_painter.drawTexturedRect(dest, m_texture, Rect(0, 0, g_window.getSize()));
}
void FrameBuffer::internalBind()
@ -142,7 +142,7 @@ void FrameBuffer::internalBind()
return;
assert(boundFbo != m_fbo);
if(g_graphics.hasFBO()) {
if(g_graphics.canUseFBO()) {
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
} else {
int buffer = GL_AUX0 + m_fbo - 1;
@ -157,11 +157,13 @@ void FrameBuffer::internalBind()
void FrameBuffer::internalRelease()
{
assert(boundFbo == m_fbo);
if(g_graphics.hasFBO()) {
if(g_graphics.canUseFBO()) {
glBindFramebuffer(GL_FRAMEBUFFER, m_prevBoundFbo);
} else {
m_texture->bind();
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_texture->getWidth(), m_texture->getHeight());
Size size = getSize();
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.width(), size.height());
int buffer = GL_BACK;
if(m_prevBoundFbo != 0)
@ -172,3 +174,14 @@ void FrameBuffer::internalRelease()
}
boundFbo = m_prevBoundFbo;
}
Size FrameBuffer::getSize()
{
if(g_graphics.canUseFBO()) {
return m_texture->getSize();
} else {
// the buffer size is limited by the window size
return Size(std::min(m_texture->getWidth(), g_window.getWidth()),
std::min(m_texture->getHeight(), g_window.getHeight()));
}
}

@ -36,14 +36,13 @@ public:
void resize(const Size& size);
void bind(bool clear = true);
void release();
void generateMipmaps();
void draw(const Rect& dest);
void draw(const Rect& dest, const Rect& src);
void setClearColor(const Color& color) { m_clearColor = color; }
TexturePtr getTexture() { return m_texture; }
const Size& getSize() { return m_texture->getSize(); }
Size getSize();
private:
void internalCreate();

@ -30,22 +30,35 @@ Graphics g_graphics;
void Graphics::init()
{
logInfo("GPU ", glGetString(GL_RENDERER));
logInfo("OpenGL ", glGetString(GL_VERSION));
#ifndef OPENGL_ES2
// init GL extensions
GLenum err = glewInit();
if(err != GLEW_OK)
logFatal("Unable to init GLEW: ", glewGetErrorString(err));
if(!GLEW_ARB_vertex_program || !GLEW_ARB_vertex_shader ||
!GLEW_ARB_fragment_program || !GLEW_ARB_fragment_shader ||
!GLEW_ARB_texture_non_power_of_two || !GLEW_ARB_multitexture)
logFatal("Some OpenGL 2.0 extensions is not supported by your system graphics, please try updating your video drivers or buy a new hardware.");
m_useFBO = GLEW_ARB_framebuffer_object;
m_useBilinearFiltering = true;
m_generateMipmaps = true;
m_generateHardwareMipmaps = m_useFBO; // glGenerateMipmap is supported when FBO is
m_generateRealtimeMipmaps = m_generateHardwareMipmaps;
#else
m_useFBO = true; // FBOs is always supported by mobile devices
m_useBilinearFiltering = true;
m_generateMipmaps = true;
m_generateHardwareMipmaps = true;
m_realtimeMipmapGeneration = false; // realtime mipmaps can be slow on mobile devices
#endif
glEnable(GL_BLEND);
logInfo("GPU ", glGetString(GL_RENDERER));
logInfo("OpenGL ", glGetString(GL_VERSION));
m_emptyTexture = TexturePtr(new Texture);
g_painter.init();
@ -58,15 +71,6 @@ void Graphics::terminate()
m_emptyTexture.reset();
}
bool Graphics::hasFBO()
{
#ifndef OPENGL_ES2
return GLEW_ARB_framebuffer_object;
#else
return true;
#endif
}
void Graphics::resize(const Size& size)
{
setViewportSize(size);

@ -32,7 +32,11 @@ public:
void init();
void terminate();
bool hasFBO();
bool canUseFBO() { return m_useFBO; }
bool canUseBilinearFiltering() { return m_useBilinearFiltering; }
bool canGenerateMipmaps() { return m_generateMipmaps; }
bool canGenerateHardwareMipmaps() { return m_generateHardwareMipmaps; }
bool canGenerateRealtimeMipmaps() { return m_generateRealtimeMipmaps; }
void resize(const Size& size);
void beginRender();
@ -47,6 +51,12 @@ public:
private:
Size m_viewportSize;
TexturePtr m_emptyTexture;
Boolean<false> m_useFBO;
Boolean<false> m_useBilinearFiltering;
Boolean<false> m_generateMipmaps;
Boolean<false> m_generateHardwareMipmaps;
Boolean<false> m_generateRealtimeMipmaps;
};
extern Graphics g_graphics;

@ -99,6 +99,19 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int
void Texture::generateMipmaps()
{
if(g_graphics.canGenerateHardwareMipmaps())
generateHardwareMipmaps();
else {
// fallback to software mipmaps generation, this can be slow
generateSoftwareMipmaps(getPixels());
}
}
void Texture::generateHardwareMipmaps()
{
if(!g_graphics.canGenerateHardwareMipmaps())
return;
bind();
if(!m_hasMipmaps) {
@ -111,6 +124,9 @@ void Texture::generateMipmaps()
void Texture::setSmooth(bool smooth)
{
if(smooth && !g_graphics.canUseBilinearFiltering())
return;
if(smooth == m_smooth)
return;
@ -140,7 +156,7 @@ std::vector<uint8> Texture::getPixels()
return pixels;
}
void Texture::generateBilinearMipmaps(std::vector<uint8> inPixels)
void Texture::generateSoftwareMipmaps(std::vector<uint8> inPixels)
{
bind();
@ -155,6 +171,7 @@ void Texture::generateBilinearMipmaps(std::vector<uint8> inPixels)
int mipmap = 1;
while(true) {
// this is a simple bilinear filtering algorithm, it combines every 4 pixels in one pixel
for(int x=0;x<outSize.width();++x) {
for(int y=0;y<outSize.height();++y) {
uint8 *inPixel[4];
@ -180,13 +197,13 @@ void Texture::generateBilinearMipmaps(std::vector<uint8> inPixels)
usedPixels++;
}
// try to guess the alpha pixel more accurately
for(int i=0;i<4;++i) {
if(usedPixels > 0)
outPixel[i] = pixelsSum[i] / usedPixels;
else
outPixel[i] = 0;
}
outPixel[3] = pixelsSum[3]/4;
}
}

@ -34,11 +34,14 @@ public:
void bind() { glBindTexture(GL_TEXTURE_2D, m_textureId); }
/// Tries to generate mipmaps via hardware, otherwise fallback to software implementation
void generateMipmaps();
/// Generate mipmaps via hardware
void generateHardwareMipmaps();
/// Generate mipmaps via software, which has a special algorithm for combining alpha pixels
void generateSoftwareMipmaps(std::vector<uint8> inPixels);
// generate bilinear mipmaps optimized for alpha textures
void generateBilinearMipmaps(std::vector<uint8> inPixels);
/// Activate texture altialising
void setSmooth(bool smooth);
GLuint getId() { return m_textureId; }

@ -39,7 +39,7 @@ X11Window::X11Window()
m_xic = 0;
m_screen = 0;
m_wmDelete = 0;
m_size = Size(16,16);
m_size = Size(600,480);
#ifndef OPENGL_ES2
m_glxContext = 0;
@ -203,7 +203,6 @@ X11Window::X11Window()
void X11Window::init()
{
m_size = Size(200, 200);
internalOpenDisplay();
internalCheckGL();
internalChooseGLVisual();

@ -24,6 +24,7 @@
#include <framework/graphics/painter.h>
#include <framework/graphics/texture.h>
#include <framework/graphics/texturemanager.h>
#include <framework/graphics/graphics.h>
void UIWidget::initImage()
{
@ -167,7 +168,7 @@ void UIWidget::drawImage(const Rect& screenCoords)
m_imageTexture->setSmooth(m_imageSmooth);
// this will increase fps when rendering larger images, like the background, and improve image quality
if(m_imageSmooth && !m_imageTexture->hasMipmaps())
if(m_imageSmooth && g_graphics.canGenerateMipmaps() && !m_imageTexture->hasMipmaps())
m_imageTexture->generateMipmaps();
g_painter.setColor(m_imageColor);

@ -57,7 +57,8 @@ void UIWidget::drawText(const Rect& screenCoords)
m_textCachedScreenCoords = screenCoords;
m_textCoordsBuffer.clear();
m_font->calculateDrawTextCoords(m_textCoordsBuffer, m_text, screenCoords, m_textAlign);
m_font->calculateDrawTextCoords(m_textCoordsBuffer, m_text, screenCoords.translated(m_textOffset), m_textAlign);
}
g_painter.setColor(m_color);

@ -99,9 +99,13 @@ void MapView::draw(const Rect& rect)
}
}
}
m_framebuffer->generateMipmaps();
m_framebuffer->release();
// generating mipmaps each frame can be slow in older cards
if(g_graphics.canGenerateRealtimeMipmaps())
m_framebuffer->getTexture()->generateHardwareMipmaps();
m_mustDrawVisibleTilesCache = false;
}
@ -257,7 +261,7 @@ void MapView::updateVisibleTilesCache(int start)
} else {
static std::vector<Point> points;
points.clear();
assert(m_drawDimension.width() % 2 == 0 && m_drawDimension.height() % 2 == 0);
//assert(m_drawDimension.width() % 2 == 0 && m_drawDimension.height() % 2 == 0);
Point quadTopLeft(m_drawDimension.width()/2 - 1, m_drawDimension.height()/2 - 1);
for(int step = 1; !(quadTopLeft.x < 0 && quadTopLeft.y < 0) && !stop; ++step) {
int quadWidth = std::min(2*step, m_drawDimension.width());
@ -404,12 +408,14 @@ void MapView::setVisibleDimension(const Size& visibleDimension)
int possiblesTileSizes[] = {32,16,8,4,2,1};
int tileSize = 0;
Size drawDimension = visibleDimension + Size(3,3);
Size framebufferSize = m_framebuffer->getSize();
for(int candidateTileSize : possiblesTileSizes) {
Size candidateDrawSize = drawDimension * candidateTileSize;
// found a valid size
if(candidateDrawSize <= m_framebuffer->getSize()) {
if(candidateDrawSize.width() <= framebufferSize.width() && candidateDrawSize.height() <= framebufferSize.height()) {
tileSize = candidateTileSize;
dump << candidateDrawSize << m_framebuffer->getSize() << tileSize;
break;
}
}

@ -134,7 +134,10 @@ TexturePtr SpriteManager::loadSpriteTexture(int id)
TexturePtr spriteTex(new Texture(32, 32, 4, &pixels[0]));
spriteTex->setSmooth(true);
spriteTex->generateBilinearMipmaps(pixels);
if(g_graphics.canGenerateMipmaps())
spriteTex->generateSoftwareMipmaps(pixels);
return spriteTex;
}

Loading…
Cancel
Save