diff options
Diffstat (limited to 'src/plugins/platforms/winrt/qwinrtbackingstore.cpp')
-rw-r--r-- | src/plugins/platforms/winrt/qwinrtbackingstore.cpp | 359 |
1 files changed, 76 insertions, 283 deletions
diff --git a/src/plugins/platforms/winrt/qwinrtbackingstore.cpp b/src/plugins/platforms/winrt/qwinrtbackingstore.cpp index b8418eef6a..7475965d11 100644 --- a/src/plugins/platforms/winrt/qwinrtbackingstore.cpp +++ b/src/plugins/platforms/winrt/qwinrtbackingstore.cpp @@ -45,343 +45,136 @@ #include "qwinrtwindow.h" #include "qwinrteglcontext.h" #include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLFramebufferObject> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> +#include <GLES3/gl3.h> +#include <GLES3/gl3ext.h> -// Generated shader headers -#include "blitps.h" -#include "blitvs.h" - -namespace { // Utility namespace for writing out an ANGLE-compatible binary blob - -// Must match packaged ANGLE -enum : quint32 { - AngleMajorVersion = 1, - AngleMinorVersion = 3 -}; - -struct ShaderString -{ - ShaderString(const char *data = 0) : data(data) { } - const char *data; -}; - -// ANGLE stream compatibility - when size_t is 32-bit, QDataStream::writeBytes() also works -QDataStream &operator<<(QDataStream &stream, const ShaderString &shaderString) -{ - if (!shaderString.data) - return stream << size_t(0); - - size_t len = strlen(shaderString.data); - stream << len; - stream.writeRawData(shaderString.data, int(len)); - return stream; -} - -struct Attribute -{ - Attribute(GLenum type = 0, const char *name = 0, quint32 index = 0) - : type(type), name(name), index(index) { } - GLenum type; - ShaderString name; - quint32 index; -}; - -struct Sampler -{ - enum TextureType { Texture2D, TextureCube }; - Sampler(bool active = false, GLint unit = 0, TextureType type = Texture2D) - : active(active), unit(unit), type(type) { } - bool active; - GLint unit; - TextureType type; -}; - -struct Uniform -{ - Uniform() { } - Uniform(GLenum type, quint32 precision, const char *name, quint32 arraySize, - quint32 psRegisterIndex, quint32 vsRegisterIndex, quint32 registerCount) - : type(type), precision(precision), name(name), arraySize(arraySize) - , psRegisterIndex(psRegisterIndex), vsRegisterIndex(vsRegisterIndex), registerCount(registerCount) { } - GLenum type; - quint32 precision; - ShaderString name; - quint32 arraySize; - quint32 psRegisterIndex; - quint32 vsRegisterIndex; - quint32 registerCount; -}; +QT_BEGIN_NAMESPACE -struct UniformIndex +class QWinRTBackingStorePrivate { - UniformIndex(const char *name = 0, quint32 element = 0, quint32 index = 0) - : name(name), element(element), index(index) { } - ShaderString name; - quint32 element; - quint32 index; +public: + bool initialized; + QSize size; + QScopedPointer<QOpenGLContext> context; + QScopedPointer<QOpenGLFramebufferObject> fbo; + QWinRTScreen *screen; + QImage paintDevice; }; -static const QByteArray createAngleBinary( - const QVector<Attribute> &attributes, - const QVector<Sampler> &textureSamplers, - const QVector<Sampler> &vertexSamplers, - const QVector<Uniform> &uniforms, - const QVector<UniformIndex> &uniformIndex, - const QByteArray &pixelShader, - const QByteArray &vertexShader, - const QByteArray &geometryShader = QByteArray(), - bool usesPointSize = false) +QWinRTBackingStore::QWinRTBackingStore(QWindow *window) + : QPlatformBackingStore(window), d_ptr(new QWinRTBackingStorePrivate) { - QByteArray binary; - - QDataStream stream(&binary, QIODevice::WriteOnly); - stream.setByteOrder(QDataStream::LittleEndian); - - stream << quint32(GL_PROGRAM_BINARY_ANGLE) - << qint32(AngleMajorVersion) - << qint32(AngleMinorVersion); - - // Vertex attributes - for (int i = 0; i < 16; ++i) { - if (i < attributes.size()) - stream << quint32(attributes[i].type) << attributes[i].name << attributes[i].index; - else - stream << quint32(GL_NONE) << ShaderString() << qint32(-1); - } - - // Texture units - for (int i = 0; i < 16; ++i) { - if (i < textureSamplers.size()) - stream << textureSamplers[i].active << textureSamplers[i].unit << qint32(textureSamplers[i].type); - else - stream << false << qint32(0) << qint32(Sampler::Texture2D); - } - - // Vertex texture units - for (int i = 0; i < 16; ++i) { - if (i < vertexSamplers.size()) - stream << vertexSamplers[i].active << vertexSamplers[i].unit << qint32(vertexSamplers[i].type); - else - stream << false << qint32(0) << qint32(Sampler::Texture2D); - } - - stream << vertexSamplers.size() - << textureSamplers.size() - << usesPointSize; - - stream << size_t(uniforms.size()); - foreach (const Uniform &uniform, uniforms) { - stream << uniform.type << uniform.precision << uniform.name << uniform.arraySize - << uniform.psRegisterIndex << uniform.vsRegisterIndex << uniform.registerCount; - } - - stream << size_t(uniformIndex.size()); - foreach (const UniformIndex &index, uniformIndex) - stream << index.name << index.element << index.index; - - stream << quint32(pixelShader.size()) - << quint32(vertexShader.size()) - << quint32(geometryShader.size()); - - stream.writeRawData(pixelShader.constData(), pixelShader.size()); - stream.writeRawData(vertexShader.constData(), vertexShader.size()); - if (!geometryShader.isEmpty()) - stream.writeRawData(geometryShader.constData(), geometryShader.size()); - - return binary; -} - -} // namespace - -QT_BEGIN_NAMESPACE + Q_D(QWinRTBackingStore); -static const GLfloat normCoords[] = { -1, 1, 1, 1, 1, -1, -1, -1 }; -static const GLfloat quadCoords[] = { 0, 0, 1, 0, 1, 1, 0, 1 }; + d->initialized = false; + d->screen = static_cast<QWinRTScreen*>(window->screen()->handle()); -QWinRTBackingStore::QWinRTBackingStore(QWindow *window) - : QPlatformBackingStore(window) - , m_context(new QOpenGLContext) - , m_shaderProgram(0) - , m_fbo(0) - , m_texture(0) - , m_screen(static_cast<QWinRTScreen*>(window->screen()->handle())) - , m_initialized(false) -{ window->setSurfaceType(QSurface::OpenGLSurface); // Required for flipping, but could be done in the swap } bool QWinRTBackingStore::initialize() { - if (m_initialized) + Q_D(QWinRTBackingStore); + + if (d->initialized) return true; - m_context->setFormat(window()->requestedFormat()); - m_context->setScreen(window()->screen()); - if (!m_context->create()) + d->context.reset(new QOpenGLContext); + QSurfaceFormat format = window()->requestedFormat(); + format.setVersion(3, 0); // Required for ES3 framebuffer blit + d->context->setFormat(format); + d->context->setScreen(window()->screen()); + if (!d->context->create()) return false; - if (!m_context->makeCurrent(window())) + if (!d->context->makeCurrent(window())) return false; - glGenFramebuffers(1, &m_fbo); - glGenRenderbuffers(1, &m_rbo); - glGenTextures(1, &m_texture); - m_shaderProgram = glCreateProgram(); - -#if 0 // Standard GLES passthrough shader program - static const char *vertexShaderSource = - "attribute vec4 pos0;\n" - "attribute vec2 tex0;\n" - "varying vec2 coord;\n" - "void main() {\n" - " coord = tex0;\n" - " gl_Position = pos0;\n" - "}\n"; - static const char *fragmentShaderSource = - "uniform sampler2D texture;\n" - "varying highp vec2 coord;\n" - "void main() {\n" - " gl_FragColor = texture2D(texture, coord);\n" - "}\n"; - GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); - glCompileShader(vertexShader); - GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); - glCompileShader(fragmentShader); - glAttachShader(m_shaderProgram, vertexShader); - glAttachShader(m_shaderProgram, fragmentShader); - glLinkProgram(m_shaderProgram); -#else // Precompiled passthrough shader - QVector<Attribute> attributes = QVector<Attribute>() << Attribute(GL_FLOAT_VEC4, "pos0", 0) - << Attribute(GL_FLOAT_VEC2, "tex0", 1); - QVector<Sampler> textureSamplers = QVector<Sampler>() << Sampler(true, 0, Sampler::Texture2D); - QVector<Sampler> vertexSamplers; - QVector<Uniform> uniforms = QVector<Uniform>() << Uniform(GL_SAMPLER_2D, 0, "texture", 0, 0, -1, 1); - QVector<UniformIndex> uniformsIndex = QVector<UniformIndex>() << UniformIndex("texture", 0, 0); - QByteArray pixelShader(reinterpret_cast<const char *>(q_blitps), sizeof(q_blitps)); - QByteArray vertexShader(reinterpret_cast<const char *>(q_blitvs), sizeof(q_blitvs)); - QByteArray binary = createAngleBinary(attributes, textureSamplers, vertexSamplers, - uniforms, uniformsIndex, pixelShader, vertexShader); - glProgramBinaryOES(m_shaderProgram, GL_PROGRAM_BINARY_ANGLE, binary.constData(), binary.size()); -#endif - m_context->doneCurrent(); - m_initialized = true; + d->context->doneCurrent(); + d->initialized = true; return true; } QWinRTBackingStore::~QWinRTBackingStore() { - if (!m_initialized) - return; - glDeleteBuffers(1, &m_fbo); - glDeleteRenderbuffers(1, &m_rbo); - glDeleteTextures(1, &m_texture); - glDeleteProgram(m_shaderProgram); } QPaintDevice *QWinRTBackingStore::paintDevice() { - return &m_paintDevice; + Q_D(QWinRTBackingStore); + return &d->paintDevice; } void QWinRTBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) { + Q_D(QWinRTBackingStore); Q_UNUSED(offset) - if (m_size.isEmpty()) + + if (d->size.isEmpty()) return; - m_context->makeCurrent(window); - - // Blitting the entire image width trades zero image copy/relayout for a larger texture upload. - // Since we're blitting the whole width anyway, the boundingRect() is used in the assumption that - // we don't repeat upload. This is of course dependent on the distance between update regions. - // Ideally, we would use the GL_EXT_unpack_subimage extension, which should be possible to implement - // since D3D11_MAPPED_SUBRESOURCE supports RowPitch (see below). - // Note that single-line blits in a loop are *very* slow, so reducing calls to glTexSubImage2D - // is probably a good idea anyway. - glBindTexture(GL_TEXTURE_2D, m_texture); - QRect bounds = region.boundingRect(); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, bounds.y(), m_size.width(), bounds.height(), - GL_BGRA_EXT, GL_UNSIGNED_BYTE, m_paintDevice.constScanLine(bounds.y())); - // TODO: Implement GL_EXT_unpack_subimage in ANGLE for more minimal uploads - //glPixelStorei(GL_UNPACK_ROW_LENGTH, image->bytesPerLine()); - //glTexSubImage2D(GL_TEXTURE_2D, 0, bounds.x(), bounds.y(), bounds.width(), bounds.height(), - // GL_BGRA_EXT, GL_UNSIGNED_BYTE, image->scanLine(bounds.y()) + bounds.x() * 4); - - // Bind render buffer - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo); - - // Bind position - glUseProgram(m_shaderProgram); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, normCoords); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, quadCoords); - - // Render - const QSize blitSize = m_size * window->devicePixelRatio(); - glViewport(0, 0, blitSize.width(), blitSize.height()); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - // Unbind - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - glUseProgram(0); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); + const bool ok = d->context->makeCurrent(window); + if (!ok) + qWarning("unable to flush"); - // fast blit - TODO: perform the blit inside swap buffers instead - glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, m_fbo); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, 0); - glBlitFramebufferANGLE(0, 0, blitSize.width(), blitSize.height(), // TODO: blit only the changed rectangle - 0, 0, blitSize.width(), blitSize.height(), - GL_COLOR_BUFFER_BIT, GL_NEAREST); + const QRect bounds = region.boundingRect(); + glBindTexture(GL_TEXTURE_2D, d->fbo->texture()); + // TODO: when ANGLE GLES3 support is finished, use the glPixelStorei functions to minimize upload + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, bounds.y(), d->size.width(), bounds.height(), + GL_RGBA, GL_UNSIGNED_BYTE, d->paintDevice.constScanLine(bounds.y())); + glBindTexture(GL_TEXTURE_2D, 0); - m_context->swapBuffers(window); - m_context->doneCurrent(); + glBindFramebuffer(GL_READ_FRAMEBUFFER, d->fbo->handle()); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + const int y1 = bounds.height() + bounds.y(); + const int y2 = d->size.height() - y1; + const int x1 = bounds.x(); + const int x2 = x1 + bounds.width(); + glBlitFramebuffer(x1, y2, x2, y1, + x1, y1, x2, y2, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + + d->context->swapBuffers(window); + d->context->doneCurrent(); } void QWinRTBackingStore::resize(const QSize &size, const QRegion &staticContents) { + Q_D(QWinRTBackingStore); Q_UNUSED(staticContents) + if (!initialize()) return; - if (m_size == size) + if (d->size == size) return; - m_size = size; - if (m_size.isEmpty()) + d->size = size; + if (d->size.isEmpty()) return; - m_paintDevice = QImage(m_size, QImage::Format_ARGB32_Premultiplied); - - m_context->makeCurrent(window()); - // Input texture - glBindTexture(GL_TEXTURE_2D, m_texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, m_size.width(), m_size.height(), - 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBindTexture(GL_TEXTURE_2D, 0); - // Render buffer - glBindRenderbuffer(GL_RENDERBUFFER, m_rbo); - const QSize blitSize = m_size * window()->devicePixelRatio(); - glRenderbufferStorage(GL_RENDERBUFFER, GL_BGRA8_EXT, blitSize.width(), blitSize.height()); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - m_context->doneCurrent(); + d->paintDevice = QImage(d->size, QImage::Format_RGBA8888_Premultiplied); + + const bool ok = d->context->makeCurrent(window()); + if (!ok) + qWarning("unable to resize"); + + d->fbo.reset(new QOpenGLFramebufferObject(d->size)); + + d->context->doneCurrent(); +} + +QImage QWinRTBackingStore::toImage() const +{ + Q_D(const QWinRTBackingStore); + return d->paintDevice; } void QWinRTBackingStore::beginPaint(const QRegion ®ion) { - Q_UNUSED(region) - resize(window()->size(), QRegion()); + resize(window()->size(), region); } void QWinRTBackingStore::endPaint() |