From c7abf81786f4a0c6940fd606ea32621be7f32a56 Mon Sep 17 00:00:00 2001 From: Andrew Knight Date: Tue, 5 Aug 2014 23:21:54 +0300 Subject: winrt: Refactor backing store Thanks to new features in ANGLE, the backing store implementation complexity can be greatly reduced. By using ES3 framebuffer blit, no shader code is required, and the shader loading code and blit shader can be removed. Change-Id: Iab3d915e279ad6468a75ef6257794f12acd8cb65 Reviewed-by: Oliver Wolff --- src/plugins/platforms/winrt/qwinrtbackingstore.cpp | 359 +++++---------------- 1 file changed, 76 insertions(+), 283 deletions(-) (limited to 'src/plugins/platforms/winrt/qwinrtbackingstore.cpp') 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 +#include -#include -#include +#include +#include -// 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 context; + QScopedPointer fbo; + QWinRTScreen *screen; + QImage paintDevice; }; -static const QByteArray createAngleBinary( - const QVector &attributes, - const QVector &textureSamplers, - const QVector &vertexSamplers, - const QVector &uniforms, - const QVector &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(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(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 attributes = QVector() << Attribute(GL_FLOAT_VEC4, "pos0", 0) - << Attribute(GL_FLOAT_VEC2, "tex0", 1); - QVector textureSamplers = QVector() << Sampler(true, 0, Sampler::Texture2D); - QVector vertexSamplers; - QVector uniforms = QVector() << Uniform(GL_SAMPLER_2D, 0, "texture", 0, 0, -1, 1); - QVector uniformsIndex = QVector() << UniformIndex("texture", 0, 0); - QByteArray pixelShader(reinterpret_cast(q_blitps), sizeof(q_blitps)); - QByteArray vertexShader(reinterpret_cast(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() -- cgit v1.2.3