diff options
author | Miikka Heikkinen <miikka.heikkinen@theqtcompany.com> | 2015-06-04 14:19:10 +0300 |
---|---|---|
committer | Pasi Keränen <pasi.keranen@digia.com> | 2015-06-17 10:36:47 +0000 |
commit | 4c374429f96d3ba24d0ea81a1aeeec06af20c09a (patch) | |
tree | 070a3cd8393e840e54979fb88388ecf030640b30 | |
parent | 611e2e940f54210346330657077038324deabd02 (diff) |
Using Qt Quick item as Canvas3D texture
Implemented QTCANVAS3D_texture_provider extension for creating
a texture object for QQuickItems that implement texture providers.
[ChangeLog][Renderer] Enabled using Qt Quick items as textures.
Change-Id: I947304155734af50c8a21f34292b244bb099702f
Reviewed-by: Tomi Korpipää <tomi.korpipaa@theqtcompany.com>
Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
Reviewed-by: Topi Reiniö <topi.reinio@digia.com>
Reviewed-by: Pasi Keränen <pasi.keranen@digia.com>
24 files changed, 1698 insertions, 15 deletions
diff --git a/examples/canvas3d/canvas3d/canvas3d.pro b/examples/canvas3d/canvas3d/canvas3d.pro index 978d455..0f603bc 100644 --- a/examples/canvas3d/canvas3d/canvas3d.pro +++ b/examples/canvas3d/canvas3d/canvas3d.pro @@ -3,7 +3,7 @@ SUBDIRS += textureandlight \ framebuffer \ interaction \ jsonmodels \ + quickitemtexture \ threejs OTHER_FILES += 3rdparty/* - diff --git a/examples/canvas3d/canvas3d/quickitemtexture/doc/src/quickitemtexture.qdoc b/examples/canvas3d/canvas3d/quickitemtexture/doc/src/quickitemtexture.qdoc new file mode 100644 index 0000000..9db0227 --- /dev/null +++ b/examples/canvas3d/canvas3d/quickitemtexture/doc/src/quickitemtexture.qdoc @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCanvas3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example canvas3d/quickitemtexture + \since QtCanvas3D 1.0 + \title Qt Quick Item as Texture Example + \ingroup qtcanvas3d-examples + \brief A simple cube with a Qt Quick item as a texture + + The Qt Quick Item as Texture example shows how to use other Qt Quick items as + a texture source for Qt Canvas3D textures. + + \image quickitemtexture-example.png + + \section1 Using Qt Quick Item as a Texture + + First we create a \l Rectangle with a label that displays the current frame rate and rotation + values of the cube: + + \snippet canvas3d/quickitemtexture/qml/quickitemtexture/main.qml 0 + + We want to use the above \l Rectangle as the texture on our 3D cube. As a \l Rectangle item + doesn't implement QQuickItem::textureProvider() by itself, we make it layered by setting the + \c{layer.enabled} property to \c{true}. + + To create a Canvas3DTexture out of our layered \l{Rectangle}, we create a + \l{Canvas3DTextureProvider}{QTCANVAS3D_texture_provider} extension and the texture + in the \c initializeGL() function in our JavaScript implementation: + + \snippet canvas3d/quickitemtexture/qml/quickitemtexture/quickitemtexture.js 0 + + Once the \c cubeTexture item is created, it can be used like any other texture item in + the JavaScript. + + \note The method of creating the texture from a Qt Quick item differs from how one would create + texture from an HTML item in WebGL API. Textures created with + \l{Canvas3DTextureProvider}{QTCANVAS3D_texture_provider} extension + support automatic live updates, without having to call textureImage2D each frame + to re-upload fresh texture data from the item. +*/ diff --git a/examples/canvas3d/canvas3d/quickitemtexture/main.cpp b/examples/canvas3d/canvas3d/quickitemtexture/main.cpp new file mode 100644 index 0000000..a5264fb --- /dev/null +++ b/examples/canvas3d/canvas3d/quickitemtexture/main.cpp @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCanvas3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/QGuiApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/examples/canvas3d/canvas3d/quickitemtexture/qml/quickitemtexture/main.qml b/examples/canvas3d/canvas3d/quickitemtexture/qml/quickitemtexture/main.qml new file mode 100644 index 0000000..27def02 --- /dev/null +++ b/examples/canvas3d/canvas3d/quickitemtexture/qml/quickitemtexture/main.qml @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCanvas3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtCanvas3D 1.1 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.2 + +import "quickitemtexture.js" as GLCode + +Window { + id: mainview + width: 800 + height: 600 + visible: true + title: "Qt Quick Item as Texture" + + Canvas3D { + id: canvas3d + anchors.fill:parent + focus: true + property double xRotAnim: 0 + property double yRotAnim: 0 + property double zRotAnim: 0 + property bool isRunning: true + + ColumnLayout { + Layout.fillWidth: true + x: 4 + y: 4 + //! [0] + Rectangle { + id: textureSource + color: "lightgreen" + width: 256 + height: 256 + border.color: "blue" + border.width: 4 + layer.enabled: true + layer.smooth: true + opacity: 0.8 + Label { + anchors.fill: parent + anchors.margins: 16 + text: "X Rot:" + (canvas3d.xRotAnim | 0) + "\n" + + "Y Rot:" + (canvas3d.yRotAnim | 0) + "\n" + + "Z Rot:" + (canvas3d.zRotAnim | 0) + "\n" + + "FPS:" + canvas3d.fps + color: "red" + font.pointSize: 30 + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + } + //! [0] + Button { + Layout.fillWidth: true + Layout.minimumWidth: 256 + text: textureSource.visible ? "Hide texture source" : "Show texture source" + onClicked: textureSource.visible = !textureSource.visible + } + Button { + Layout.fillWidth: true + Layout.minimumWidth: 256 + text: "Quit" + onClicked: Qt.quit() + } + } + + // Emitted when one time initializations should happen + onInitializeGL: { + GLCode.initializeGL(canvas3d, textureSource); + } + + // Emitted each time Canvas3D is ready for a new frame + onPaintGL: { + GLCode.paintGL(canvas3d); + } + + onResizeGL: { + GLCode.resizeGL(canvas3d); + } + + Keys.onSpacePressed: { + canvas3d.isRunning = !canvas3d.isRunning + if (canvas3d.isRunning) { + objAnimationX.pause(); + objAnimationY.pause(); + objAnimationZ.pause(); + } else { + objAnimationX.resume(); + objAnimationY.resume(); + objAnimationZ.resume(); + } + } + + SequentialAnimation { + id: objAnimationX + loops: Animation.Infinite + running: true + NumberAnimation { + target: canvas3d + property: "xRotAnim" + from: 0.0 + to: 120.0 + duration: 7000 + easing.type: Easing.InOutQuad + } + NumberAnimation { + target: canvas3d + property: "xRotAnim" + from: 120.0 + to: 0.0 + duration: 7000 + easing.type: Easing.InOutQuad + } + } + + SequentialAnimation { + id: objAnimationY + loops: Animation.Infinite + running: true + NumberAnimation { + target: canvas3d + property: "yRotAnim" + from: 0.0 + to: 240.0 + duration: 5000 + easing.type: Easing.InOutCubic + } + NumberAnimation { + target: canvas3d + property: "yRotAnim" + from: 240.0 + to: 0.0 + duration: 5000 + easing.type: Easing.InOutCubic + } + } + + SequentialAnimation { + id: objAnimationZ + loops: Animation.Infinite + running: true + NumberAnimation { + target: canvas3d + property: "zRotAnim" + from: -100.0 + to: 100.0 + duration: 3000 + easing.type: Easing.InOutSine + } + NumberAnimation { + target: canvas3d + property: "zRotAnim" + from: 100.0 + to: -100.0 + duration: 3000 + easing.type: Easing.InOutSine + } + } + } +} diff --git a/examples/canvas3d/canvas3d/quickitemtexture/qml/quickitemtexture/quickitemtexture.js b/examples/canvas3d/canvas3d/quickitemtexture/qml/quickitemtexture/quickitemtexture.js new file mode 100644 index 0000000..3ac898d --- /dev/null +++ b/examples/canvas3d/canvas3d/quickitemtexture/qml/quickitemtexture/quickitemtexture.js @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCanvas3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +Qt.include("/gl-matrix.js") + +// +// Draws a cube that has a Qt Quick item as decal texture on each face. +// A simple per vertex lighting equation is used to emulate light landing on the rotating cube. +// + +var gl; +var cubeTexture = 0; +var vertexPositionAttribute; +var textureCoordAttribute; +var vertexNormalAttribute; +var mvMatrix = mat4.create(); +var pMatrix = mat4.create(); +var nMatrix = mat4.create(); +var pMatrixUniform; +var mvMatrixUniform; +var nUniform; +var width = 0; +var height = 0; +var canvas3d; +var pixelSize; +var canvasTextureProvider = null; +var textureSourceItem = null; + +function initializeGL(canvas, textureSource) { + canvas3d = canvas; + textureSourceItem = textureSource; + + // Get the OpenGL context object that represents the API we call + gl = canvas.getContext("canvas3d", {depth:true, antialias:true}); + + // Setup the OpenGL state + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LESS); + gl.enable(gl.CULL_FACE); + gl.cullFace(gl.BACK); + gl.clearColor(0.98, 0.98, 0.98, 1.0); + gl.clearDepth(1.0); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + + // Set viewport + gl.viewport(0, 0, canvas.width, canvas.height); + + // Initialize the shader program + initShaders(); + + // Initialize vertex and color buffers + initBuffers(); + + // Create a texture from the Qt Quick item + //! [0] + canvasTextureProvider = gl.getExtension("QTCANVAS3D_texture_provider"); + cubeTexture = canvasTextureProvider.createTextureFromSource(textureSourceItem); + //! [0] + + gl.bindTexture(gl.TEXTURE_2D, cubeTexture); +} + +function resizeGL(canvas) +{ + var pixelRatio = canvas.devicePixelRatio; + canvas.pixelSize = Qt.size(canvas.width * pixelRatio, + canvas.height * pixelRatio); +} + +function degToRad(degrees) { + return degrees * Math.PI / 180; +} + +function paintGL(canvas) { + var pixelRatio = canvas.devicePixelRatio; + var currentWidth = canvas.width * pixelRatio; + var currentHeight = canvas.height * pixelRatio; + if (currentWidth !== width || currentHeight !== height ) { + width = currentWidth; + height = currentHeight; + gl.viewport(0, 0, width, height); + mat4.perspective(pMatrix, degToRad(45), width / height, 0.1, 500.0); + gl.uniformMatrix4fv(pMatrixUniform, false, pMatrix); + } + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + mat4.identity(mvMatrix); + mat4.translate(mvMatrix, mvMatrix, [(canvas.yRotAnim - 120.0) / 120.0, + (canvas.xRotAnim - 60.0) / 50.0, + -7.0]); + mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.xRotAnim), [0, 1, 0]); + mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.yRotAnim), [1, 0, 0]); + mat4.rotate(mvMatrix, mvMatrix, degToRad(canvas.zRotAnim), [0, 0, 1]); + gl.uniformMatrix4fv(mvMatrixUniform, false, mvMatrix); + + mat4.invert(nMatrix, mvMatrix); + mat4.transpose(nMatrix, nMatrix); + gl.uniformMatrix4fv(nUniform, false, nMatrix); + + gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0); +} + +function initBuffers() +{ + var cubeVertexPositionBuffer = gl.createBuffer(); + cubeVertexPositionBuffer.name = "cubeVertexPositionBuffer"; + gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array([// Front face + -1.0, -1.0, 1.0, + 1.0, -1.0, 1.0, + 1.0, 1.0, 1.0, + -1.0, 1.0, 1.0, + + // Back face + -1.0, -1.0, -1.0, + -1.0, 1.0, -1.0, + 1.0, 1.0, -1.0, + 1.0, -1.0, -1.0, + + // Top face + -1.0, 1.0, -1.0, + -1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, -1.0, + + // Bottom face + -1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + 1.0, -1.0, 1.0, + -1.0, -1.0, 1.0, + + // Right face + 1.0, -1.0, -1.0, + 1.0, 1.0, -1.0, + 1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, + + // Left face + -1.0, -1.0, -1.0, + -1.0, -1.0, 1.0, + -1.0, 1.0, 1.0, + -1.0, 1.0, -1.0 + ]), + gl.STATIC_DRAW); + gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); + + var cubeVertexIndexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, + new Uint16Array([ + 0, 1, 2, 0, 2, 3, // front + 4, 5, 6, 4, 6, 7, // back + 8, 9, 10, 8, 10, 11, // top + 12, 13, 14, 12, 14, 15, // bottom + 16, 17, 18, 16, 18, 19, // right + 20, 21, 22, 20, 22, 23 // left + ]), + gl.STATIC_DRAW); + + var cubeVerticesTextureCoordBuffer = gl.createBuffer(); + cubeVerticesTextureCoordBuffer.name = "cubeVerticesTextureCoordBuffer"; + gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer); + var textureCoordinates = [ + // Front + 1.0, 0.0, + 0.0, 0.0, + 0.0, 1.0, + 1.0, 1.0, + // Back + 1.0, 0.0, + 0.0, 0.0, + 0.0, 1.0, + 1.0, 1.0, + // Top + 1.0, 0.0, + 0.0, 0.0, + 0.0, 1.0, + 1.0, 1.0, + // Bottom + 1.0, 0.0, + 0.0, 0.0, + 0.0, 1.0, + 1.0, 1.0, + // Right + 1.0, 0.0, + 0.0, 0.0, + 0.0, 1.0, + 1.0, 1.0, + // Left + 1.0, 0.0, + 0.0, 0.0, + 0.0, 1.0, + 1.0, 1.0 + ]; + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates), + gl.STATIC_DRAW); + gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0); + + var cubeVerticesNormalBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + // Front + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, + + // Back + 0.0, 0.0, -1.0, + 0.0, 0.0, -1.0, + 0.0, 0.0, -1.0, + 0.0, 0.0, -1.0, + + // Top + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, + + // Bottom + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, -1.0, 0.0, + + // Right + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, + + // Left + -1.0, 0.0, 0.0, + -1.0, 0.0, 0.0, + -1.0, 0.0, 0.0, + -1.0, 0.0, 0.0 + ]), gl.STATIC_DRAW); + gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0); +} + +function initShaders() +{ + var vertexShader = getShader(gl, + "attribute highp vec3 aVertexNormal; \ + attribute highp vec3 aVertexPosition; \ + attribute highp vec2 aTextureCoord; \ + \ + uniform highp mat4 uNormalMatrix; \ + uniform mat4 uMVMatrix; \ + uniform mat4 uPMatrix; \ + \ + varying mediump vec4 vColor; \ + varying highp vec2 vTextureCoord; \ + varying highp vec3 vLighting; \ + \ + void main(void) { \ + gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); \ + vTextureCoord = aTextureCoord; \ + highp vec3 ambientLight = vec3(0.5, 0.5, 0.5); \ + highp vec3 directionalLightColor = vec3(0.75, 0.75, 0.75); \ + highp vec3 directionalVector = vec3(0.85, 0.8, 0.75); \ + highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0); \ + highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0); \ + vLighting = ambientLight + (directionalLightColor * directional); \ + }", gl.VERTEX_SHADER); + + var fragmentShader = getShader(gl, + "varying highp vec2 vTextureCoord; \ + varying highp vec3 vLighting; \ + \ + uniform sampler2D uSampler; \ + \ + void main(void) { \ + mediump vec3 texelColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)).rgb; \ + gl_FragColor = vec4(texelColor * vLighting, 1.0); \ + }", gl.FRAGMENT_SHADER); + + // Create the Program3D for shader + var shaderProgram = gl.createProgram(); + + // Attach the shader sources to the shader program + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + + // Link the program + gl.linkProgram(shaderProgram); + + // Check the linking status + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + console.log("Could not initialize shaders"); + console.log(gl.getProgramInfoLog(shaderProgram)); + } + + // Take the shader program into use + gl.useProgram(shaderProgram); + + // Look up where the vertex data needs to go + vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); + gl.enableVertexAttribArray(vertexPositionAttribute); + textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); + gl.enableVertexAttribArray(textureCoordAttribute); + vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal"); + gl.enableVertexAttribArray(vertexNormalAttribute); + + // Get the uniform locations + pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); + mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); + nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix"); + + // Setup texture sampler uniform + var textureSamplerUniform = gl.getUniformLocation(shaderProgram, "uSampler") + gl.activeTexture(gl.TEXTURE0); + gl.uniform1i(textureSamplerUniform, 0); + gl.bindTexture(gl.TEXTURE_2D, 0); +} + +function getShader(gl, str, type) { + var shader = gl.createShader(type); + gl.shaderSource(shader, str); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + console.log("JS:Shader compile failed"); + console.log(gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +} diff --git a/examples/canvas3d/canvas3d/quickitemtexture/quickitemtexture.pro b/examples/canvas3d/canvas3d/quickitemtexture/quickitemtexture.pro new file mode 100644 index 0000000..bbfa099 --- /dev/null +++ b/examples/canvas3d/canvas3d/quickitemtexture/quickitemtexture.pro @@ -0,0 +1,12 @@ +!include( ../../../examples.pri ) { + error( "Couldn't find the examples.pri file!" ) +} + +SOURCES += main.cpp + +RESOURCES += quickitemtexture.qrc + +OTHER_FILES += qml/quickitemtexture/* \ + doc/src/* \ + doc/images/* + diff --git a/examples/canvas3d/canvas3d/quickitemtexture/quickitemtexture.qrc b/examples/canvas3d/canvas3d/quickitemtexture/quickitemtexture.qrc new file mode 100644 index 0000000..b0e7ecb --- /dev/null +++ b/examples/canvas3d/canvas3d/quickitemtexture/quickitemtexture.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/"> + <file alias="gl-matrix.js">../3rdparty/gl-matrix.js</file> + <file alias="main.qml">qml/quickitemtexture/main.qml</file> + <file alias="quickitemtexture.js">qml/quickitemtexture/quickitemtexture.js</file> + </qresource> +</RCC> diff --git a/src/imports/qtcanvas3d/canvas3d.cpp b/src/imports/qtcanvas3d/canvas3d.cpp index e110a13..b367650 100644 --- a/src/imports/qtcanvas3d/canvas3d.cpp +++ b/src/imports/qtcanvas3d/canvas3d.cpp @@ -292,6 +292,10 @@ QJSValue Canvas::getContext(const QString &type, const QVariantMap &options) contextVersion, extensions, m_renderer->commandQueue()); + connect(m_renderer, &CanvasRenderer::textureIdResolved, + m_context3D, &CanvasContext::handleTextureIdResolved, + Qt::QueuedConnection); + // Verify that width and height are not initially too large, in case width and height // were set before getting GL_MAX_VIEWPORT_DIMS if (width() > m_maxSize.width()) { diff --git a/src/imports/qtcanvas3d/canvas3d_p.h b/src/imports/qtcanvas3d/canvas3d_p.h index bf1af58..dacc127 100644 --- a/src/imports/qtcanvas3d/canvas3d_p.h +++ b/src/imports/qtcanvas3d/canvas3d_p.h @@ -94,14 +94,11 @@ public: float devicePixelRatio(); QSize pixelSize(); void setPixelSize(QSize pixelSize); - void createFBOs(); void setWidth(int width); int width(); void setHeight(int height); int height(); - void bindCurrentRenderTarget(); - uint fps(); Q_INVOKABLE QJSValue getContext(const QString &name); diff --git a/src/imports/qtcanvas3d/canvasrenderer.cpp b/src/imports/qtcanvas3d/canvasrenderer.cpp index cd3d716..678b0f9 100644 --- a/src/imports/qtcanvas3d/canvasrenderer.cpp +++ b/src/imports/qtcanvas3d/canvasrenderer.cpp @@ -178,6 +178,10 @@ void CanvasRenderer::shutDown() // Nothing to do, uniforms do not actually consume resources break; } + case CanvasGlCommandQueue::internalClearQuickItemAsTexture: { + // Nothing to do, scenegraph will handle texture clearing + break; + } default: qWarning() << __FUNCTION__ << "Invalid command, cannot cleanup:" << commandId << "Resource:" << glId; @@ -408,6 +412,40 @@ void CanvasRenderer::render() { // Skip render if there is no context or nothing to render if (m_glContext && m_executeQueueCount) { + // Update tracked quick item textures + int providerCount = m_commandQueue.providerCache().size(); + if (providerCount) { + QMap<GLint, CanvasGlCommandQueue::ProviderCacheItem *>::iterator i = + m_commandQueue.providerCache().begin(); + while (i != m_commandQueue.providerCache().end()) { + CanvasGlCommandQueue::ProviderCacheItem *cacheItem = i.value(); + QSGTextureProvider *texProvider = cacheItem->providerPtr.data(); + GLint id = i.key(); + QMap<GLint, CanvasGlCommandQueue::ProviderCacheItem *>::iterator prev = i; + i++; + + if (texProvider) { + QSGDynamicTexture *texture = + qobject_cast<QSGDynamicTexture *>(texProvider->texture()); + if (texture) { + texture->updateTexture(); + int textureId = texture->textureId(); + int currentTextureId = m_commandQueue.getGlId(id); + if (textureId && textureId != currentTextureId) { + m_commandQueue.setGlIdToMap( + id, textureId, + CanvasGlCommandQueue::internalClearQuickItemAsTexture); + emit textureIdResolved(cacheItem->quickItem); + } + } + } else { + // Clean obsolete providers off the cache + m_commandQueue.providerCache().erase(prev); + delete cacheItem; + } + } + } + // Render to offscreen fbo QOpenGLContext *oldContext = QOpenGLContext::currentContext(); QSurface *oldSurface = oldContext->surface(); @@ -1275,6 +1313,12 @@ void CanvasRenderer::executeCommandQueue() bindCurrentRenderTarget(); break; } + case CanvasGlCommandQueue::internalClearQuickItemAsTexture: { + // Used to clear mapped quick item texture ids when no longer needed + m_commandQueue.removeResourceIdFromMap(command.i1); + delete m_commandQueue.providerCache().take(command.i1); + break; + } default: { qWarning() << __FUNCTION__ << "Unsupported GL command handled:" << command.id; diff --git a/src/imports/qtcanvas3d/canvasrenderer_p.h b/src/imports/qtcanvas3d/canvasrenderer_p.h index 5b2f248..4525f48 100644 --- a/src/imports/qtcanvas3d/canvasrenderer_p.h +++ b/src/imports/qtcanvas3d/canvasrenderer_p.h @@ -55,6 +55,7 @@ #include <QtGui/QOpenGLContext> #include <QtGui/QOpenGLFunctions> #include <QtGui/QOpenGLFramebufferObject> +#include <QtQuick/QQuickItem> QT_BEGIN_NAMESPACE @@ -106,8 +107,8 @@ public slots: signals: void fpsChanged(uint fps); - void textureReady(int id, const QSize &size); + void textureIdResolved(QQuickItem *item); private: QSize m_fboSize; diff --git a/src/imports/qtcanvas3d/canvastextureprovider.cpp b/src/imports/qtcanvas3d/canvastextureprovider.cpp new file mode 100644 index 0000000..e2aaf89 --- /dev/null +++ b/src/imports/qtcanvas3d/canvastextureprovider.cpp @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCanvas3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "canvastextureprovider_p.h" +#include "texture3d_p.h" + +#include <QtQuick/QQuickItem> + +QT_BEGIN_NAMESPACE +QT_CANVAS3D_BEGIN_NAMESPACE + +/*! + \qmltype Canvas3DTextureProvider + \since QtCanvas3D 1.1 + \inqmlmodule QtCanvas3D + \brief Provides means to get QQuickItem as Canvas3DTexture. + + An uncreatable QML type that provides an extension API that can be used to get QQuickItem as + Canvas3DTexture. Only QQuickItems that implement QQuickItem::textureProvider() method can be + used as a texture source, which in most cases means the \c{layer.enabled} property of the + item must be set to \c {true}. + + Typical usage would be something like this: + \code + // In QML code, declare a layered item you wish to show as texture + Rectangle { + id: textureSource + layer.enabled: true + // ... + } + . + . + // In JavaScript code, declare the variables for the extension and the texture + var textureProvider; + var myTexture; + . + . + // Get the extension after the context has been created + textureProvider = gl.getExtension("QTCANVAS3D_texture_provider"); + . + . + // Get the Canvas3DTexture object representing our source item + if (textureProvider) + myTexture = textureProvider.createTextureFromSource(textureSource); + . + . + // Normally the above is enough. However, in cases where you utilize synchronous OpenGL + // commands or dynamically enable the source item layer, it is not guaranteed that the texture + // is valid immediately after calling createTextureFromSource(). + // To ensure you don't use the texture before it is ready, connect the textureReady() signal + // to a handler function that will use the texture. + textureProvider.textureReady.connect(function(sourceItem) { + if (sourceItem === textureSource) { + gl.bindTexture(gl.TEXTURE_2D, myTexture); + // ... + } + }); + \endcode + + \sa Context3D + */ +CanvasTextureProvider::CanvasTextureProvider(CanvasContext *canvasContext, + QObject *parent) : + QObject(parent), + m_canvasContext(canvasContext) +{ +} + +CanvasTextureProvider::~CanvasTextureProvider() +{ +} + +/*! + * \qmlmethod QJSValue Canvas3DTextureProvider::createTextureFromSource(Item *source) + * + * Creates and returns a Canvas3DTexture object for the supplied \a source item. + * + * The \a source item must be of a type that implements a texture provider, which in most + * cases means the \c{layer.enabled} property of the item must be set to \c {true}. + * ShaderEffectSource items can also be used as texture sources. + * The texture provider of the \a source item owns the OpenGL texture. + * If the \a source item is deleted or the \c{layer.enabled} property is set to \c{false} + * while the texture is still in use in Canvas3D, the rendered texture contents become undefined. + * + * Trying to bind the returned Canvas3DTexture object is not guaranteed to work until + * a \l{Canvas3DTextureProvider::textureReady}{textureReady()} signal corresponding to the \a source item has been emitted. + * However, if you don't have any synchronous OpenGL calls between the first use of the texture + * and the end of your paingGL() handler, and if you can guarantee that the source item has been + * fully rendered at least once after its layer was enabled, you can immediately use the returned + * texture without waiting for the \l{Canvas3DTextureProvider::textureReady}{textureReady()} signal. + * + * Disabling the \a source item's layer will destroy the underlying texture provider, so it + * is necessary to call this method again for the \a source item if you re-enable its layer. + * + * If this function is called twice for same \a source, it doesn't create a new Canvas3DTexture + * instance, but instead returns a reference to a previously created one, as long as the previous + * instance is still alive. + */ +QJSValue CanvasTextureProvider::createTextureFromSource(QQuickItem *source) +{ + return m_canvasContext->createTextureFromSource(source); +} + +/*! + * \qmlsignal void Canvas3DTextureProvider::textureReady(Item *source) + * + * Indicates that the texture created with createTextureFromSource() method for the \a source item + * is ready to be used. + */ +QT_CANVAS3D_END_NAMESPACE +QT_END_NAMESPACE diff --git a/src/imports/qtcanvas3d/canvastextureprovider_p.h b/src/imports/qtcanvas3d/canvastextureprovider_p.h new file mode 100644 index 0000000..285739b --- /dev/null +++ b/src/imports/qtcanvas3d/canvastextureprovider_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCanvas3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtCanvas3D API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef CANVASTEXTUREPROVIDER_P_H +#define CANVASTEXTUREPROVIDER_P_H + +#include "canvas3dcommon_p.h" + +#include <QtCore/QObject> +#include <QtGui/qopengl.h> +#include <QtQuick/QQuickItem> + +QT_BEGIN_NAMESPACE +QT_CANVAS3D_BEGIN_NAMESPACE + +class CanvasContext; + +class CanvasTextureProvider : public QObject +{ + Q_OBJECT +public: + CanvasTextureProvider(CanvasContext *canvasContext, QObject *parent = 0); + ~CanvasTextureProvider(); + + Q_INVOKABLE QJSValue createTextureFromSource(QQuickItem *source); + +signals: + void textureReady(QQuickItem *source); + +private: + CanvasContext *m_canvasContext; + QJSValue m_textureObject; +}; + +QT_CANVAS3D_END_NAMESPACE +QT_END_NAMESPACE + +#endif // CANVASTEXTUREPROVIDER_P_H diff --git a/src/imports/qtcanvas3d/context3d.cpp b/src/imports/qtcanvas3d/context3d.cpp index b7e0e4b..16a09df 100644 --- a/src/imports/qtcanvas3d/context3d.cpp +++ b/src/imports/qtcanvas3d/context3d.cpp @@ -104,6 +104,7 @@ CanvasContext::CanvasContext(QQmlEngine *engine, bool isES2, int maxVertexAttrib m_isOpenGLES2(isES2), m_commandQueue(commandQueue), m_stateDumpExt(0), + m_textureProviderExt(0), m_standardDerivatives(0), m_compressedTextureS3TC(0), m_compressedTexturePVRTC(0) @@ -120,6 +121,11 @@ CanvasContext::~CanvasContext() { qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__; EnumToStringMap::deleteInstance(); + + // Cleanup quick item textures to avoid crash when parent gets deleted before children + QList<CanvasTexture *> quickItemTextures = m_quickItemToTextureMap.values(); + foreach (CanvasTexture *texture, quickItemTextures) + texture->del(); } /*! @@ -6103,6 +6109,42 @@ QJSValue CanvasContext::getVertexAttrib(uint index, glEnums pname) } /*! + * \internal + * + * Implements CanvasTextureProvider::createTextureFromSource() extension functionality + */ +QJSValue CanvasContext::createTextureFromSource(QQuickItem *item) +{ + // First check if we have a CanvasTexture already for this item + CanvasTexture *texture = m_quickItemToTextureMap.value(item, 0); + if (!texture) + texture = new CanvasTexture(m_commandQueue, this, item); + + m_quickItemToTextureMap.insert(item, texture); + + QJSValue value = m_engine->newQObject(texture); + + qCDebug(canvas3drendering).nospace() << "Context3D::" << __FUNCTION__ + << "(quickItem:" << item + << "):" << value.toString(); + + // We attempt to add item as texture again even if it is already created to make sure the + // provider is cached. This allows user to fix the texture after e.g. disabling and enabling + // a layer, which destroys and recreates the texture provider. + m_commandQueue->addQuickItemAsTexture(item, texture->textureId()); + + return value; +} + +/*! + * \internal + */ +QMap<QQuickItem *, CanvasTexture *> &CanvasContext::quickItemToTextureMap() +{ + return m_quickItemToTextureMap; +} + +/*! * \qmlmethod variant Context3D::getUniform(Canvas3DProgram program, Canvas3DUniformLocation location3D) * Returns the uniform value at the given \a location3D in the \a program. * The type returned is dependent on the uniform type, as shown in the table: @@ -6413,7 +6455,6 @@ void CanvasContext::scheduleSyncCommand(GlSyncCommand *command) * \internal * Schedules a blocking job to clear the queue. */ - void CanvasContext::handleFullCommandQueue() { // Use no command to simply force the execution of the pending queue @@ -6421,6 +6462,16 @@ void CanvasContext::handleFullCommandQueue() } /*! + * \internal + */ +void CanvasContext::handleTextureIdResolved(QQuickItem *item) +{ + CanvasTexture *texture = m_quickItemToTextureMap.value(item, 0); + if (texture && texture->isAlive() && m_textureProviderExt) + emit m_textureProviderExt->textureReady(item); +} + +/*! * \qmlmethod variant Context3D::getExtension(string name) * \return object if given \a name matches a supported extension. * Otherwise returns \c{null}. The returned object may contain constants and/or functions provided @@ -6442,6 +6493,10 @@ QVariant CanvasContext::getExtension(const QString &name) if (!m_stateDumpExt) m_stateDumpExt = new CanvasGLStateDump(this, m_isOpenGLES2, this); return QVariant::fromValue(m_stateDumpExt); + } else if (upperCaseName == QStringLiteral("QTCANVAS3D_TEXTURE_PROVIDER")) { + if (!m_textureProviderExt) + m_textureProviderExt = new CanvasTextureProvider(this, this); + return QVariant::fromValue(m_textureProviderExt); } else if (upperCaseName == QStringLiteral("OES_STANDARD_DERIVATIVES") && m_extensions.contains("OES_standard_derivatives")) { if (!m_standardDerivatives) diff --git a/src/imports/qtcanvas3d/context3d_p.h b/src/imports/qtcanvas3d/context3d_p.h index 4e209ec..b3d632b 100644 --- a/src/imports/qtcanvas3d/context3d_p.h +++ b/src/imports/qtcanvas3d/context3d_p.h @@ -51,6 +51,7 @@ #include "contextattributes_p.h" #include "abstractobject3d_p.h" #include "canvasglstatedump_p.h" +#include "canvastextureprovider_p.h" #include <QtCore/QMutex> #include <QtCore/QWaitCondition> @@ -1181,10 +1182,14 @@ public: Q_INVOKABLE uint getVertexAttribOffset(uint index, glEnums pname); Q_INVOKABLE QJSValue getVertexAttrib(uint index, glEnums pname); + QJSValue createTextureFromSource(QQuickItem *item); + QMap<QQuickItem *, CanvasTexture *> &quickItemToTextureMap(); + void scheduleSyncCommand(GlSyncCommand *command); public slots: void handleFullCommandQueue(); + void handleTextureIdResolved(QQuickItem *item); signals: void canvasChanged(Canvas *canvas); @@ -1279,6 +1284,7 @@ private: CanvasGlCommandQueue *m_commandQueue; // Not owned QMutex m_renderJobMutex; QWaitCondition m_renderJobCondition; + QMap<QQuickItem *, CanvasTexture *> m_quickItemToTextureMap; bool invalidEnumFlag; bool invalidValueFlag; @@ -1291,6 +1297,7 @@ private: // EXTENSIONS CanvasGLStateDump *m_stateDumpExt; + CanvasTextureProvider *m_textureProviderExt; QObject *m_standardDerivatives; CompressedTextureS3TC *m_compressedTextureS3TC; CompressedTexturePVRTC *m_compressedTexturePVRTC; diff --git a/src/imports/qtcanvas3d/glcommandqueue.cpp b/src/imports/qtcanvas3d/glcommandqueue.cpp index 7e2d1bd..9882634 100644 --- a/src/imports/qtcanvas3d/glcommandqueue.cpp +++ b/src/imports/qtcanvas3d/glcommandqueue.cpp @@ -35,6 +35,8 @@ ****************************************************************************/ #include "glcommandqueue_p.h" +#include "canvas3d_p.h" // for logging categories + #include <QtCore/QMap> #include <QtCore/QMutexLocker> @@ -89,6 +91,7 @@ GlCommand &CanvasGlCommandQueue::queueCommand(CanvasGlCommandQueue::GlCommandId if (m_queuedCount) { deleteUntransferedCommandData(); m_queuedCount = 0; + clearQuickItemAsTextureList(); } } @@ -169,6 +172,33 @@ int CanvasGlCommandQueue::transferCommands(QVector<GlCommand> &executeQueue) const int count = m_queuedCount; m_queuedCount = 0; + // Grab texture providers from quick items and cache them + const int quickItemCount = m_quickItemsAsTextureList.size(); + if (quickItemCount) { + for (int i = 0; i < quickItemCount; i++) { + const ItemAndId *itemAndId = m_quickItemsAsTextureList.at(i); + if (!itemAndId->itemPtr.isNull()) { + QQuickItem *quickItem = itemAndId->itemPtr.data(); + QSGTextureProvider *texProvider = quickItem->textureProvider(); + if (texProvider) { + // Make sure the old provider, if any, gets cleared up before inserting a new one + delete m_providerCache.take(itemAndId->id); + m_providerCache.insert(itemAndId->id, + new ProviderCacheItem(texProvider, quickItem)); + // Reset the mapped glId so it gets resolved at render time + setGlIdToMap(itemAndId->id, 0, + CanvasGlCommandQueue::internalClearQuickItemAsTexture); + } else { + qCWarning(canvas3drendering).nospace() << "CanvasGlCommandQueue::" + << __FUNCTION__ + << ": The Quick item doesn't implement a texture provider: " + << quickItem; + } + } + } + clearQuickItemAsTextureList(); + } + return count; } @@ -180,6 +210,7 @@ int CanvasGlCommandQueue::transferCommands(QVector<GlCommand> &executeQueue) void CanvasGlCommandQueue::resetQueue(int size) { deleteUntransferedCommandData(); + clearQuickItemAsTextureList(); m_queuedCount = 0; m_maxSize = size; @@ -344,5 +375,21 @@ void CanvasGlCommandQueue::handleGenerateCommand(const GlCommand &command, GLuin setGlIdToMap(command.i1, glId, command.id); } +/*! + * \internal + * Adds a quick item to list of items that need to be converted to texture IDs on the + * next command transfer. + */ +void CanvasGlCommandQueue::addQuickItemAsTexture(QQuickItem *quickItem, GLint textureId) +{ + m_quickItemsAsTextureList.append(new ItemAndId(quickItem, textureId)); +} + +void CanvasGlCommandQueue::clearQuickItemAsTextureList() +{ + qDeleteAll(m_quickItemsAsTextureList); + m_quickItemsAsTextureList.clear(); +} + QT_CANVAS3D_END_NAMESPACE QT_END_NAMESPACE diff --git a/src/imports/qtcanvas3d/glcommandqueue_p.h b/src/imports/qtcanvas3d/glcommandqueue_p.h index b633982..50d610c 100644 --- a/src/imports/qtcanvas3d/glcommandqueue_p.h +++ b/src/imports/qtcanvas3d/glcommandqueue_p.h @@ -48,12 +48,16 @@ #define GLCOMMANDQUEUE_P_H #include "canvas3dcommon_p.h" +#include "canvastextureprovider_p.h" #include <QtCore/QVariantList> #include <QtCore/QMutex> +#include <QtCore/QPointer> #include <QtGui/qopengl.h> #include <QtGui/QOpenGLShader> #include <QtGui/QOpenGLShaderProgram> +#include <QtQuick/QQuickItem> +#include <QtQuick/QSGTextureProvider> QT_BEGIN_NAMESPACE QT_CANVAS3D_BEGIN_NAMESPACE @@ -207,6 +211,7 @@ public: internalGetUniformType, internalClearLocation, // Used to clear a mapped uniform location from map when no longer needed internalTextureComplete, // Indicates texture is complete and needs to be updated to screen at this point + internalClearQuickItemAsTexture, // Used to clear mapped quick item texture ids when no longer needed extStateDump }; @@ -266,6 +271,19 @@ public: QMutex *resourceMutex() { return &m_resourceMutex; } + void addQuickItemAsTexture(QQuickItem *quickItem, GLint textureId); + void clearQuickItemAsTextureList(); + + struct ProviderCacheItem { + ProviderCacheItem(QSGTextureProvider *provider, QQuickItem *item) : + providerPtr(provider), + quickItem(item) {} + + QPointer<QSGTextureProvider> providerPtr; + QQuickItem *quickItem; // Not owned, nor accessed - only used as identifier + }; + QMap<GLint, ProviderCacheItem *> &providerCache() { return m_providerCache; } + signals: void queueFull(); @@ -280,6 +298,18 @@ private: GLint m_nextResourceId; bool m_resourceIdOverflow; QMutex m_resourceMutex; + + struct ItemAndId { + ItemAndId(QQuickItem *item, GLint itemId) : + itemPtr(item), + id(itemId) {} + + QPointer<QQuickItem> itemPtr; + GLint id; + }; + QList<ItemAndId *> m_quickItemsAsTextureList; + + QMap<GLint, ProviderCacheItem *> m_providerCache; }; class GlCommand diff --git a/src/imports/qtcanvas3d/qcanvas3d_plugin.cpp b/src/imports/qtcanvas3d/qcanvas3d_plugin.cpp index 3c421a5..ec905d2 100644 --- a/src/imports/qtcanvas3d/qcanvas3d_plugin.cpp +++ b/src/imports/qtcanvas3d/qcanvas3d_plugin.cpp @@ -46,6 +46,8 @@ void QtCanvas3DPlugin::registerTypes(const char *uri) { // @uri com.digia.qtcanvas3d + // QtCanvas3D 1.0 + // QTCANVAS3D CORE API qmlRegisterSingletonType<CanvasTextureImageFactory>(uri, 1, 0, @@ -108,6 +110,14 @@ void QtCanvas3DPlugin::registerTypes(const char *uri) 1, 0, "GLStateDumpExt", QLatin1String("Trying to create uncreatable: GLStateDumpExt, use Context3D.getExtension(\"QTCANVAS3D_gl_state_dump\") instead.")); + + // QtCanvas3D 1.1 + + // EXTENSIONS + qmlRegisterUncreatableType<CanvasTextureProvider>(uri, + 1, 1, + "Canvas3DTextureProvider", + QLatin1String("Trying to create uncreatable: Canvas3DTextureProvider, use Context3D.getExtension(\"QTCANVAS3D_texture_provider\") instead.")); } QT_CANVAS3D_END_NAMESPACE diff --git a/src/imports/qtcanvas3d/qcanvas3d_plugin.h b/src/imports/qtcanvas3d/qcanvas3d_plugin.h index 34fe771..3ee83d8 100644 --- a/src/imports/qtcanvas3d/qcanvas3d_plugin.h +++ b/src/imports/qtcanvas3d/qcanvas3d_plugin.h @@ -51,6 +51,7 @@ #include "shaderprecisionformat_p.h" #include "activeinfo3d_p.h" #include "canvasglstatedump_p.h" +#include "canvastextureprovider_p.h" #include <QQmlExtensionPlugin> @@ -73,6 +74,7 @@ QML_DECLARE_TYPE(CanvasRenderBuffer) QML_DECLARE_TYPE(CanvasShaderPrecisionFormat) QML_DECLARE_TYPE(CanvasActiveInfo) QML_DECLARE_TYPE(CanvasGLStateDump) +QML_DECLARE_TYPE(CanvasTextureProvider) QT_BEGIN_NAMESPACE QT_CANVAS3D_BEGIN_NAMESPACE diff --git a/src/imports/qtcanvas3d/qtcanvas3d.pro b/src/imports/qtcanvas3d/qtcanvas3d.pro index 6ddad5a..af06b46 100644 --- a/src/imports/qtcanvas3d/qtcanvas3d.pro +++ b/src/imports/qtcanvas3d/qtcanvas3d.pro @@ -30,7 +30,8 @@ SOURCES += arrayutils.cpp \ compressedtexturepvrtc.cpp \ glcommandqueue.cpp \ renderjob.cpp \ - canvasrenderer.cpp + canvasrenderer.cpp \ + canvastextureprovider.cpp HEADERS += arrayutils_p.h \ qcanvas3d_plugin.h \ @@ -56,7 +57,8 @@ HEADERS += arrayutils_p.h \ compressedtexturepvrtc_p.h \ glcommandqueue_p.h \ renderjob_p.h \ - canvasrenderer_p.h + canvasrenderer_p.h \ + canvastextureprovider_p.h OTHER_FILES = qmldir \ doc/* \ diff --git a/src/imports/qtcanvas3d/texture3d.cpp b/src/imports/qtcanvas3d/texture3d.cpp index 4ef2226..3b34152 100644 --- a/src/imports/qtcanvas3d/texture3d.cpp +++ b/src/imports/qtcanvas3d/texture3d.cpp @@ -53,14 +53,20 @@ QT_CANVAS3D_BEGIN_NAMESPACE /*! * \internal */ -CanvasTexture::CanvasTexture(CanvasGlCommandQueue *queue, QObject *parent) : - CanvasAbstractObject(queue, parent), +CanvasTexture::CanvasTexture(CanvasGlCommandQueue *queue, CanvasContext *context, + QQuickItem *quickItem) : + CanvasAbstractObject(queue, context), m_textureId(queue->createResourceId()), - m_isAlive(true) + m_isAlive(true), + m_context(context), + m_quickItem(quickItem) { Q_ASSERT(m_commandQueue); - m_commandQueue->queueCommand(CanvasGlCommandQueue::glGenTextures, m_textureId); + if (m_quickItem) + connect(m_quickItem, &QObject::destroyed, this, &CanvasTexture::handleItemDestroyed); + else + m_commandQueue->queueCommand(CanvasGlCommandQueue::glGenTextures, m_textureId); } /*! @@ -96,6 +102,14 @@ GLint CanvasTexture::textureId() const /*! * \internal */ +void CanvasTexture::handleItemDestroyed() +{ + del(); +} + +/*! + * \internal + */ bool CanvasTexture::isAlive() const { return bool(m_textureId); @@ -106,8 +120,16 @@ bool CanvasTexture::isAlive() const */ void CanvasTexture::del() { - if (m_textureId) - m_commandQueue->queueCommand(CanvasGlCommandQueue::glDeleteTextures, m_textureId); + if (m_textureId) { + if (m_quickItem) { + m_context->quickItemToTextureMap().remove(m_quickItem); + m_quickItem = 0; + m_commandQueue->queueCommand(CanvasGlCommandQueue::internalClearQuickItemAsTexture, + m_textureId); + } else { + m_commandQueue->queueCommand(CanvasGlCommandQueue::glDeleteTextures, m_textureId); + } + } m_textureId = 0; } diff --git a/src/imports/qtcanvas3d/texture3d_p.h b/src/imports/qtcanvas3d/texture3d_p.h index cd55ce4..88874e7 100644 --- a/src/imports/qtcanvas3d/texture3d_p.h +++ b/src/imports/qtcanvas3d/texture3d_p.h @@ -49,6 +49,9 @@ #include "context3d_p.h" #include "abstractobject3d_p.h" +#include <QtCore/QPointer> + +class QQuickItem; QT_BEGIN_NAMESPACE QT_CANVAS3D_BEGIN_NAMESPACE @@ -58,7 +61,8 @@ class CanvasTexture : public CanvasAbstractObject Q_OBJECT public: - explicit CanvasTexture(CanvasGlCommandQueue *queue, QObject *parent = 0); + explicit CanvasTexture(CanvasGlCommandQueue *queue, CanvasContext *context, + QQuickItem *quickItem = 0); ~CanvasTexture(); void bind(CanvasContext::glEnums target); @@ -69,8 +73,14 @@ public: friend QDebug operator<< (QDebug d, const CanvasTexture *texture); +public slots: + void handleItemDestroyed(); + +private: GLint m_textureId; bool m_isAlive; + CanvasContext *m_context; + QQuickItem *m_quickItem; }; QT_CANVAS3D_END_NAMESPACE diff --git a/tests/auto/qmltest/canvas3d/tst_quick_item_as_texture.js b/tests/auto/qmltest/canvas3d/tst_quick_item_as_texture.js new file mode 100644 index 0000000..79d290f --- /dev/null +++ b/tests/auto/qmltest/canvas3d/tst_quick_item_as_texture.js @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCanvas3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +Qt.include("../../../../examples/canvas3d/canvas3d/3rdparty/gl-matrix.js") + +var gl; + +var mvMatrix = mat4.create(); +var pMatrix = mat4.create(); + +var texture = null; + +var pMatrixUniform; +var mvMatrixUniform; + +var vertexPositionAttribute; +var textureCoordAttribute; + +var vertexBuffer; +var uvBuffer; +var indexBuffer; + +var readyTextures; +var quickTextureProvider; + +function initializeGL(canvas) { + gl = canvas.getContext(""); + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + var pixelRatio = canvas.devicePixelRatio; + gl.viewport(0, 0, pixelRatio * canvas.width, pixelRatio * canvas.height); + + initShaders(); + initBuffers(); + + mat4.perspective(pMatrix, degToRad(45), canvas.width / canvas.height, 0.1, 500.0); + gl.uniformMatrix4fv(pMatrixUniform, false, pMatrix); + mat4.identity(mvMatrix); + mat4.translate(mvMatrix, mvMatrix, [0, 0, -5]); + gl.uniformMatrix4fv(mvMatrixUniform, false, mvMatrix); +} + +function paintGL(x, y) { + gl.clearColor(64.0 / 255.0, 80.0 / 255.0, 96.0 / 255.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + + var pixels = checkPixel(x, y); + + return pixels; +} + +function checkPixel(x, y) { + var pixels = new Uint8Array(4); + gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + return pixels; +} + +function initBuffers() +{ + vertexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, + new Float32Array([-1.0, -1.0, 1.0, + 1.0, -1.0, 1.0, + 1.0, 1.0, 1.0, + -1.0, 1.0, 1.0]), + gl.STATIC_DRAW); + gl.enableVertexAttribArray(vertexPositionAttribute); + gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); + + uvBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer); + gl.bufferData(gl.ARRAY_BUFFER, + new Float32Array([1.0, 0.0, + 0.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]), + gl.STATIC_DRAW); + gl.enableVertexAttribArray(textureCoordAttribute); + gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0); + + indexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, + new Uint16Array([0, 1, 2, + 0, 2, 3]), + gl.STATIC_DRAW); +} + +function initShaders() +{ + var vertexShader = getShader( + gl, + "attribute highp vec3 aVertexPosition; \ + attribute mediump vec4 aVertexColor; \ + attribute highp vec2 aTextureCoord; \ + uniform mat4 uMVMatrix; \ + uniform mat4 uPMatrix; \ + varying highp vec2 vTextureCoord; \ + void main(void) { \ + gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); \ + vTextureCoord = aTextureCoord; \ + }", + gl.VERTEX_SHADER); + var fragmentShader = getShader( + gl, + "varying highp vec2 vTextureCoord; \ + uniform sampler2D uSampler; \ + void main(void) { \ + gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)); \ + }", + gl.FRAGMENT_SHADER); + + var shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.linkProgram(shaderProgram); + gl.deleteShader(vertexShader); + gl.deleteShader(fragmentShader); + + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + console.log("Could not initialize shaders"); + console.log(gl.getProgramInfoLog(shaderProgram)); + } + + gl.useProgram(shaderProgram); + + vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); + gl.enableVertexAttribArray(vertexPositionAttribute); + textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); + gl.enableVertexAttribArray(textureCoordAttribute); + + pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); + mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); + + var textureSamplerUniform = gl.getUniformLocation(shaderProgram, "uSampler") + + gl.activeTexture(gl.TEXTURE0); + gl.uniform1i(textureSamplerUniform, 0); +} + +function resetReadyTextures() { + readyTextures = []; +} + +function readyTexturesCount() { + return readyTextures.length; +} + +function onTextureReady(sourceItem) { + readyTextures[readyTextures.length] = sourceItem; + gl.bindTexture(gl.TEXTURE_2D, texture); +} + +function updateTexture(newTextureSource) { + if (!quickTextureProvider) { + quickTextureProvider = gl.getExtension("QTCANVAS3D_texture_provider"); + quickTextureProvider.textureReady.connect(onTextureReady); + } + + texture = quickTextureProvider.createTextureFromSource(newTextureSource); +} + +function getShader(gl, str, type) { + var shader = gl.createShader(type); + gl.shaderSource(shader, str); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + console.log("JS:Shader compile failed"); + console.log(gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +} + +function degToRad(degrees) { + return degrees * Math.PI / 180; +} diff --git a/tests/auto/qmltest/canvas3d/tst_quick_item_as_texture.qml b/tests/auto/qmltest/canvas3d/tst_quick_item_as_texture.qml new file mode 100644 index 0000000..7aa5953 --- /dev/null +++ b/tests/auto/qmltest/canvas3d/tst_quick_item_as_texture.qml @@ -0,0 +1,308 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCanvas3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.0 + +import "tst_quick_item_as_texture.js" as Content + +Item { + id: top + height: 300 + width: 300 + + property var canvas3d: null + property var shaderEffectSource: null + property var shaderEffectSource2: null + property var activeContent: Content + property bool initOk: false + property bool renderOk: false + property var canvasWindow: null + property bool windowHidden: false + property int xpos: 150 + property int ypos: 150 + property var pixels + property int red: -1 + property int green: -1 + property int blue: -1 + property int alpha: -1 + + Rectangle { + id: testRect + anchors.bottom: parent.bottom + anchors.left: parent.left + width: 64 + height: 64 + color: "#102030" + z:1 + } + + Rectangle { + id: testRect2 + anchors.bottom: parent.bottom + anchors.right: parent.right + width: 64 + height: 64 + color: "#122232" + z:1 + } + + Rectangle { + id: testRect3 + anchors.top: parent.top + anchors.right: parent.right + width: 64 + height: 64 + color: "#132333" + z:1 + } + + function createCanvas() { + canvas3d = Qt.createQmlObject(" + import QtQuick 2.2 + import QtCanvas3D 1.1 + Canvas3D { + onInitializeGL: { + activeContent.initializeGL(this) + initOk = true; + } + onPaintGL: { + pixels = activeContent.paintGL(xpos, ypos) + red = pixels[0] + green = pixels[1] + blue = pixels[2] + alpha = pixels[3] + delete pixels + renderOk = true + } + }", top) + canvas3d.anchors.fill = top + } + + function createShaderEffectSource(source) { + var effect = Qt.createQmlObject(" + import QtQuick 2.2 + ShaderEffectSource { + width: 64 + height: 64 + live: false + hideSource: false + mipmap: true + z: 1 + }", top) + effect.sourceItem = source + return effect + } + + function resetRenderCheck() { + renderOk = false + red = -1 + green = -1 + blue = -1 + alpha = -1 + activeContent.resetReadyTextures() + } + + TestCase { + name: "Canvas3D_quick_item_as_texture" + when: windowShown + + function test_quick_item_as_texture_1() { + verify(canvas3d === null) + verify(initOk === false) + verify(renderOk === false) + createCanvas() + verify(canvas3d !== null) + waitForRendering(canvas3d) + + tryCompare(top, "initOk", true) + + shaderEffectSource = createShaderEffectSource(testRect) + shaderEffectSource2 = createShaderEffectSource(testRect2) + testRect3.layer.enabled = true + verify(shaderEffectSource !== null) + verify(shaderEffectSource2 !== null) + + resetRenderCheck() + activeContent.updateTexture(shaderEffectSource) + waitForRendering(canvas3d) + + tryCompare(activeContent.readyTexturesCount() === 1) + tryCompare(top, "renderOk", true) + tryCompare(top, "red", 0x10) + tryCompare(top, "green", 0x20) + tryCompare(top, "blue", 0x30) + tryCompare(top, "alpha", 0xff) + + resetRenderCheck() + activeContent.updateTexture(shaderEffectSource2) + waitForRendering(canvas3d) + + tryCompare(activeContent.readyTexturesCount() === 1) + tryCompare(top, "renderOk", true) + tryCompare(top, "red", 0x12) + tryCompare(top, "green", 0x22) + tryCompare(top, "blue", 0x32) + tryCompare(top, "alpha", 0xff) + + // testRect3 is layered + resetRenderCheck() + activeContent.updateTexture(testRect3) + tryCompare(activeContent.readyTexturesCount() === 1) + waitForRendering(canvas3d) + + tryCompare(top, "renderOk", true) + tryCompare(top, "red", 0x13) + tryCompare(top, "green", 0x23) + tryCompare(top, "blue", 0x33) + tryCompare(top, "alpha", 0xff) + + // Texture is not live, color should not change + testRect.color = "#405060" + resetRenderCheck() + activeContent.updateTexture(shaderEffectSource) + tryCompare(activeContent.readyTexturesCount() === 1) + + waitForRendering(canvas3d) + + tryCompare(top, "renderOk", true) + tryCompare(top, "red", 0x10) + tryCompare(top, "green", 0x20) + tryCompare(top, "blue", 0x30) + tryCompare(top, "alpha", 0xff) + + // Turn live on, color will change to what we previously set it + resetRenderCheck() + shaderEffectSource.live = true + + waitForRendering(canvas3d) + + tryCompare(top, "renderOk", true) + tryCompare(top, "red", 0x40) + tryCompare(top, "green", 0x50) + tryCompare(top, "blue", 0x60) + tryCompare(top, "alpha", 0xff) + + // Live texture, new color + testRect.color = "#AABBCC" + resetRenderCheck() + + waitForRendering(canvas3d) + + tryCompare(top, "renderOk", true) + tryCompare(top, "red", 0xAA) + tryCompare(top, "green", 0xBB) + tryCompare(top, "blue", 0xCC) + tryCompare(top, "alpha", 0xff) + + // Destroy shaderEffectSources + shaderEffectSource.destroy() + shaderEffectSource2.destroy() + testRect3.layer.enabled = false + + waitForRendering(canvas3d) + + testRect.color = "#718191" + testRect2.color = "#728292" + testRect3.color = "#738393" + + waitForRendering(canvas3d) + + // Recreate shaderEffectSources + shaderEffectSource = createShaderEffectSource(testRect) + shaderEffectSource2 = createShaderEffectSource(testRect2) + testRect3.layer.enabled = true + verify(shaderEffectSource !== null) + verify(shaderEffectSource2 !== null) + + waitForRendering(canvas3d) + + // Test that each textureProvider is valid second time around + activeContent.updateTexture(shaderEffectSource) + + resetRenderCheck() + waitForRendering(canvas3d) + + tryCompare(top, "renderOk", true) + tryCompare(top, "red", 0x71) + tryCompare(top, "green", 0x81) + tryCompare(top, "blue", 0x91) + tryCompare(top, "alpha", 0xff) + + activeContent.updateTexture(shaderEffectSource2) + + resetRenderCheck() + waitForRendering(canvas3d) + + tryCompare(top, "renderOk", true) + tryCompare(top, "red", 0x72) + tryCompare(top, "green", 0x82) + tryCompare(top, "blue", 0x92) + tryCompare(top, "alpha", 0xff) + + activeContent.updateTexture(testRect3) + + resetRenderCheck() + waitForRendering(canvas3d) + + tryCompare(top, "renderOk", true) + tryCompare(top, "red", 0x73) + tryCompare(top, "green", 0x83) + tryCompare(top, "blue", 0x93) + tryCompare(top, "alpha", 0xff) + + shaderEffectSource.destroy() + shaderEffectSource2.destroy() + testRect3.layer.enabled = false + + waitForRendering(canvas3d) + + testRect.layer.enabled = true + testRect2.layer.enabled = true + testRect3.layer.enabled = true + + resetRenderCheck() + waitForRendering(canvas3d) + + // Test that multiple items each trigger textureReady signal + activeContent.updateTexture(testRect) + activeContent.updateTexture(testRect2) + activeContent.updateTexture(testRect3) + + tryCompare(activeContent.readyTexturesCount() === 3) + } + } +} |