Skip to content

Commit

Permalink
MER: eglfs: Allow scaling down the window surface
Browse files Browse the repository at this point in the history
This patch introduces the QT_QPA_EGLFS_SCALE environment variable.
When set to an integer number > 1 it will scale the draws window
content down by that scale factor.

This is only needed for the Sailfish SDK emulator, for now and i'm
not sure this belongs upstream.
  • Loading branch information
giucam committed Jul 23, 2015
1 parent 3f0086c commit 0061edc
Show file tree
Hide file tree
Showing 2 changed files with 242 additions and 1 deletion.
230 changes: 229 additions & 1 deletion src/plugins/platforms/eglfs/qeglfscontext.cpp
Expand Up @@ -48,15 +48,213 @@
#include <QtPlatformSupport/private/qeglpbuffer_p.h>
#include <QtGui/QSurface>
#include <QtDebug>
#include <QOpenGLFramebufferObject>
#include <QOpenGLShaderProgram>

QT_BEGIN_NAMESPACE


class Blitter : public QOpenGLFunctions
{
public:
Blitter(QEglFSContext *context)
: m_context(context)
{
initializeOpenGLFunctions();
m_blitProgram = new QOpenGLShaderProgram();
m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute vec4 position;\n\
attribute vec2 texCoords;\n\
varying vec2 outTexCoords;\n\
void main()\n\
{\n\
gl_Position = position;\n\
outTexCoords = texCoords;\n\
}");
m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, "varying highp vec2 outTexCoords;\n\
uniform sampler2D texture;\n\
void main()\n\
{\n\
gl_FragColor = texture2D(texture, outTexCoords);\n\
}");

m_blitProgram->bindAttributeLocation("position", 0);
m_blitProgram->bindAttributeLocation("texCoords", 1);

if (!m_blitProgram->link()) {
qDebug() << "Shader Program link failed.";
qDebug() << m_blitProgram->log();
}
}
~Blitter()
{
delete m_blitProgram;
}
void blit(QOpenGLFramebufferObject *fbo, const QSize &size)
{
glViewport(0, 0, size.width(), size.height());

glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
glDisable(GL_SCISSOR_TEST);
glDepthMask(GL_FALSE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

m_context->m_useNativeDefaultFbo = true;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
m_context->m_useNativeDefaultFbo = false;

static const GLfloat squareVertices[] = {
-1.f, -1.f,
1.0f, -1.f,
-1.f, 1.0f,
1.0f, 1.0f
};

static const GLfloat textureVertices[] = {
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};

glBindBuffer(GL_ARRAY_BUFFER, 0);
m_blitProgram->bind();

m_blitProgram->enableAttributeArray(0);
m_blitProgram->enableAttributeArray(1);
m_blitProgram->setAttributeArray(1, textureVertices, 2);

glActiveTexture(GL_TEXTURE0);

//Draw Content
m_blitProgram->setAttributeArray(0, squareVertices, 2);
glBindTexture(GL_TEXTURE_2D, fbo->texture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

//Cleanup
m_blitProgram->disableAttributeArray(0);
m_blitProgram->disableAttributeArray(1);
}

QOpenGLShaderProgram *m_blitProgram;
QEglFSContext *m_context;
};

#define STATE_GUARD_VERTEX_ATTRIB_COUNT 2

class StateGuard
{
public:
StateGuard() {
QOpenGLFunctions glFuncs(QOpenGLContext::currentContext());

glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *) &m_program);
glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &m_activeTextureUnit);
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &m_texture);
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &m_fbo);
glGetIntegerv(GL_VIEWPORT, m_viewport);
glGetIntegerv(GL_DEPTH_WRITEMASK, &m_depthWriteMask);
glGetIntegerv(GL_COLOR_WRITEMASK, m_colorWriteMask);
m_blend = glIsEnabled(GL_BLEND);
m_depth = glIsEnabled(GL_DEPTH_TEST);
m_cull = glIsEnabled(GL_CULL_FACE);
m_scissor = glIsEnabled(GL_SCISSOR_TEST);
for (int i = 0; i < STATE_GUARD_VERTEX_ATTRIB_COUNT; ++i) {
glFuncs.glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, (GLint *) &m_vertexAttribs[i].enabled);
glFuncs.glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, (GLint *) &m_vertexAttribs[i].arrayBuffer);
glFuncs.glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &m_vertexAttribs[i].size);
glFuncs.glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &m_vertexAttribs[i].stride);
glFuncs.glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, (GLint *) &m_vertexAttribs[i].type);
glFuncs.glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, (GLint *) &m_vertexAttribs[i].normalized);
glFuncs.glGetVertexAttribPointerv(i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &m_vertexAttribs[i].pointer);
}
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLint *) &m_minFilter);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLint *) &m_magFilter);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (GLint *) &m_wrapS);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (GLint *) &m_wrapT);
}

~StateGuard() {
QOpenGLFunctions glFuncs(QOpenGLContext::currentContext());

glFuncs.glUseProgram(m_program);
glActiveTexture(m_activeTextureUnit);
glBindTexture(GL_TEXTURE_2D, m_texture);
glFuncs.glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glViewport(m_viewport[0], m_viewport[1], m_viewport[2], m_viewport[3]);
glDepthMask(m_depthWriteMask);
glColorMask(m_colorWriteMask[0], m_colorWriteMask[1], m_colorWriteMask[2], m_colorWriteMask[3]);
if (m_blend)
glEnable(GL_BLEND);
if (m_depth)
glEnable(GL_DEPTH_TEST);
if (m_cull)
glEnable(GL_CULL_FACE);
if (m_scissor)
glEnable(GL_SCISSOR_TEST);
for (int i = 0; i < STATE_GUARD_VERTEX_ATTRIB_COUNT; ++i) {
if (m_vertexAttribs[i].enabled)
glFuncs.glEnableVertexAttribArray(i);
GLuint prevBuf;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint *) &prevBuf);
glFuncs.glBindBuffer(GL_ARRAY_BUFFER, m_vertexAttribs[i].arrayBuffer);
glFuncs.glVertexAttribPointer(i, m_vertexAttribs[i].size, m_vertexAttribs[i].type,
m_vertexAttribs[i].normalized, m_vertexAttribs[i].stride,
m_vertexAttribs[i].pointer);
glFuncs.glBindBuffer(GL_ARRAY_BUFFER, prevBuf);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_magFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrapS);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrapT);
}

private:
GLuint m_program;
GLenum m_activeTextureUnit;
GLuint m_texture;
GLuint m_fbo;
GLint m_depthWriteMask;
GLint m_colorWriteMask[4];
GLboolean m_blend;
GLboolean m_depth;
GLboolean m_cull;
GLboolean m_scissor;
GLint m_viewport[4];
struct VertexAttrib {
bool enabled;
GLuint arrayBuffer;
GLint size;
GLint stride;
GLenum type;
bool normalized;
void *pointer;
} m_vertexAttribs[STATE_GUARD_VERTEX_ATTRIB_COUNT];
GLenum m_minFilter;
GLenum m_magFilter;
GLenum m_wrapS;
GLenum m_wrapT;
};

QEglFSContext::QEglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share,
EGLDisplay display, EGLenum eglApi)
: QEGLPlatformContext(QEglFSHooks::hooks()->surfaceFormatFor(format), share, display,
QEglFSIntegration::chooseConfig(display, QEglFSHooks::hooks()->surfaceFormatFor(format)), eglApi),
m_swapIntervalSet(false)
m_swapIntervalSet(false),
m_useNativeDefaultFbo(false),
m_fbo(0),
m_blitter(0)
{
m_scale = qgetenv("QT_QPA_EGLFS_SCALE").toInt();
}

QEglFSContext::~QEglFSContext()
{
delete m_blitter;
delete m_fbo;
}

bool QEglFSContext::makeCurrent(QPlatformSurface *surface)
Expand All @@ -76,6 +274,9 @@ bool QEglFSContext::makeCurrent(QPlatformSurface *surface)
eglSwapInterval(eglDisplay(), swapInterval);
}

if (m_scale > 1)
fbo(surface)->bind();

return success;
}

Expand All @@ -89,6 +290,16 @@ EGLSurface QEglFSContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface

void QEglFSContext::swapBuffers(QPlatformSurface *surface)
{
if (m_scale > 1) {
// Must save & restore all state. Applications are usually not prepared
// for random context state changes in a swapBuffers() call.
StateGuard stateGuard;

if (!m_blitter)
m_blitter = new Blitter(this);
m_blitter->blit(m_fbo, surface->surface()->size() / m_scale);
}

if (surface->surface()->surfaceClass() == QSurface::Window) {
QEglFSWindow *window = static_cast<QEglFSWindow *>(surface);
// draw the cursor
Expand All @@ -100,5 +311,22 @@ void QEglFSContext::swapBuffers(QPlatformSurface *surface)
QEGLPlatformContext::swapBuffers(surface);
}

QOpenGLFramebufferObject *QEglFSContext::fbo(QPlatformSurface *surface) const
{
QSize size = surface->surface()->size();
if (!m_fbo || m_fbo->size() != size) {
delete m_fbo;
m_fbo = new QOpenGLFramebufferObject(size);
}
return m_fbo;
}

GLuint QEglFSContext::defaultFramebufferObject(QPlatformSurface *surface) const
{
if (m_useNativeDefaultFbo || m_scale <= 1)
return 0;
return fbo(surface)->handle();
}

QT_END_NAMESPACE

13 changes: 13 additions & 0 deletions src/plugins/platforms/eglfs/qeglfscontext.h
Expand Up @@ -47,17 +47,30 @@

QT_BEGIN_NAMESPACE

class QOpenGLFramebufferObject;
class Blitter;

class QEglFSContext : public QEGLPlatformContext
{
public:
QEglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display,
EGLenum eglApi = EGL_OPENGL_ES_API);
~QEglFSContext();
bool makeCurrent(QPlatformSurface *surface);
EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface);
void swapBuffers(QPlatformSurface *surface);
GLuint defaultFramebufferObject(QPlatformSurface *surface) const;

private:
QOpenGLFramebufferObject *fbo(QPlatformSurface *surface) const;

bool m_swapIntervalSet;
bool m_useNativeDefaultFbo;
mutable QOpenGLFramebufferObject *m_fbo;
Blitter *m_blitter;
int m_scale;

friend class Blitter;
};

QT_END_NAMESPACE
Expand Down

0 comments on commit 0061edc

Please sign in to comment.