From c7469e4454a569184f278e98534c4968434a652c Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Wed, 21 Mar 2012 09:41:43 -0300 Subject: [PATCH] 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 --- src/framework/graphics/framebuffer.cpp | 41 +++++++++++++++++--------- src/framework/graphics/framebuffer.h | 3 +- src/framework/graphics/graphics.cpp | 28 ++++++++++-------- src/framework/graphics/graphics.h | 12 +++++++- src/framework/graphics/texture.cpp | 21 +++++++++++-- src/framework/graphics/texture.h | 9 ++++-- src/framework/platform/x11window.cpp | 3 +- src/framework/ui/uiwidgetimage.cpp | 3 +- src/framework/ui/uiwidgettext.cpp | 3 +- src/otclient/core/mapview.cpp | 12 ++++++-- src/otclient/core/spritemanager.cpp | 5 +++- 11 files changed, 98 insertions(+), 42 deletions(-) diff --git a/src/framework/graphics/framebuffer.cpp b/src/framework/graphics/framebuffer.cpp index 43e81681..84ef0e6a 100644 --- a/src/framework/graphics/framebuffer.cpp +++ b/src/framework/graphics/framebuffer.cpp @@ -23,6 +23,7 @@ #include "framebuffer.h" #include "graphics.h" #include "texture.h" +#include uint FrameBuffer::boundFbo = 0; std::vector 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())); + } +} diff --git a/src/framework/graphics/framebuffer.h b/src/framework/graphics/framebuffer.h index 969dc514..2a11ca00 100644 --- a/src/framework/graphics/framebuffer.h +++ b/src/framework/graphics/framebuffer.h @@ -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(); diff --git a/src/framework/graphics/graphics.cpp b/src/framework/graphics/graphics.cpp index 5635d771..999b53cc 100644 --- a/src/framework/graphics/graphics.cpp +++ b/src/framework/graphics/graphics.cpp @@ -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); diff --git a/src/framework/graphics/graphics.h b/src/framework/graphics/graphics.h index 228b338d..4eeedf8f 100644 --- a/src/framework/graphics/graphics.h +++ b/src/framework/graphics/graphics.h @@ -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 m_useFBO; + Boolean m_useBilinearFiltering; + Boolean m_generateMipmaps; + Boolean m_generateHardwareMipmaps; + Boolean m_generateRealtimeMipmaps; }; extern Graphics g_graphics; diff --git a/src/framework/graphics/texture.cpp b/src/framework/graphics/texture.cpp index 45ca79ce..feea83e2 100644 --- a/src/framework/graphics/texture.cpp +++ b/src/framework/graphics/texture.cpp @@ -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 Texture::getPixels() return pixels; } -void Texture::generateBilinearMipmaps(std::vector inPixels) +void Texture::generateSoftwareMipmaps(std::vector inPixels) { bind(); @@ -155,6 +171,7 @@ void Texture::generateBilinearMipmaps(std::vector 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 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; } } diff --git a/src/framework/graphics/texture.h b/src/framework/graphics/texture.h index 33ae95a9..abb93f99 100644 --- a/src/framework/graphics/texture.h +++ b/src/framework/graphics/texture.h @@ -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 inPixels); - // generate bilinear mipmaps optimized for alpha textures - void generateBilinearMipmaps(std::vector inPixels); - + /// Activate texture altialising void setSmooth(bool smooth); GLuint getId() { return m_textureId; } diff --git a/src/framework/platform/x11window.cpp b/src/framework/platform/x11window.cpp index 4b257a2a..c21e6776 100644 --- a/src/framework/platform/x11window.cpp +++ b/src/framework/platform/x11window.cpp @@ -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(); diff --git a/src/framework/ui/uiwidgetimage.cpp b/src/framework/ui/uiwidgetimage.cpp index f95b8d16..c56d763f 100644 --- a/src/framework/ui/uiwidgetimage.cpp +++ b/src/framework/ui/uiwidgetimage.cpp @@ -24,6 +24,7 @@ #include #include #include +#include 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); diff --git a/src/framework/ui/uiwidgettext.cpp b/src/framework/ui/uiwidgettext.cpp index d717ddff..9d1c5101 100644 --- a/src/framework/ui/uiwidgettext.cpp +++ b/src/framework/ui/uiwidgettext.cpp @@ -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); diff --git a/src/otclient/core/mapview.cpp b/src/otclient/core/mapview.cpp index b30188ee..ee0c4e9d 100644 --- a/src/otclient/core/mapview.cpp +++ b/src/otclient/core/mapview.cpp @@ -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 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; } } diff --git a/src/otclient/core/spritemanager.cpp b/src/otclient/core/spritemanager.cpp index 4abdff8b..bebb3c61 100644 --- a/src/otclient/core/spritemanager.cpp +++ b/src/otclient/core/spritemanager.cpp @@ -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; }