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
This commit is contained in:
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…
Reference in New Issue