PDA

View Full Version : [QML] Pause recursive ShaderEffect



KeineAhnung
17th January 2015, 14:44
Hi,

I found some interesting code online about recursive shaders (http://quitcoding.com/?page=work#gforce). It was not to difficult to build a mobile app with it but my app is not getting approved. The issue is that it is not going to sleep when the phone is.
To send it to sleep I tried to build in an active property and binding the shader to it but that does not help.


ShaderEffect {
id: inputItem
property variant source: ShaderEffectSource { sourceItem: sourceArea; smooth: false }
property variant recursiveSource: recursiveSource
property real ledWidthPercent: ledScreen.ledWidth/ledScreen.width
anchors.fill: sourceArea
visible: false
enabled: root.active

fragmentShader: "
varying mediump vec2 qt_TexCoord0;
uniform highp float qt_Opacity;
uniform highp float ledWidthPercent;
uniform lowp sampler2D source;
uniform lowp sampler2D recursiveSource;
void main() {
highp vec4 pix;
if (qt_TexCoord0.x > (1.0 - ledWidthPercent)) {
pix = texture2D(source, qt_TexCoord0);
} else {
highp vec2 pos = vec2(qt_TexCoord0.x+ledWidthPercent, qt_TexCoord0.y);
pix = texture2D(recursiveSource, pos) * (1.0-ledWidthPercent*2.0);
}
gl_FragColor = pix * qt_Opacity;
}
"
}

ShaderEffectSource {
id: recursiveSource
sourceItem: inputItem
hideSource: true
visible: false
live: root.updateFrequency == 1//false
recursive: true
smooth: false
enabled: root.active
}

Can someone help me to understand how this code works and how I can pause it?

Thanks

anda_skoa
17th January 2015, 18:57
enabled is a property that determines whether an element will receive user input.

try to bind the active property to the visible property instead

Cheers,
_

KeineAhnung
18th January 2015, 11:13
Thank you for the hint but I already tried that. As you can see above the visibility is already set to false. There is another shader in the code and I added an active property here as well that should control the visibility:


ShaderEffect {
id: effectItem
width: screenWidth * root.ledWidth
height: screenHeight * root.ledHeight
anchors.centerIn: parent
smooth: false
visible: root.active

property real screenWidth: Math.floor(root.width / root.ledWidth)
property real screenHeight: Math.floor(root.height / root.ledHeight)
property var source: effectSource
property var sledOn: Image { source: "qrc:/images/led_on2.png"; sourceSize.width: root.ledWidth; sourceSize.height: root.ledHeight; visible: false }
property var sledOff: Image { source: "qrc:/images/led_off2.png"; sourceSize.width: root.ledWidth; sourceSize.height: root.ledHeight; visible: false }
property point screenSize: Qt.point(screenWidth, screenHeight)
property alias ledColor: root.ledColor
property real useSourceColors: root.useSourceColors ? 1.0 : 0.0
property alias threshold: root.threshold

fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform lowp float qt_Opacity;
uniform sampler2D source;
uniform sampler2D sledOn;
uniform sampler2D sledOff;
uniform highp vec2 screenSize;
uniform highp vec4 ledColor;
uniform lowp float useSourceColors;
uniform lowp float threshold;

void main() {
highp vec2 cpos = (floor(qt_TexCoord0 * screenSize) + 0.5) / screenSize;
highp vec4 tex = texture2D(source, cpos);
highp vec2 lpos = fract(qt_TexCoord0 * screenSize);
lowp float isOn = step(threshold, tex.r);
highp vec4 pix = mix(texture2D(sledOff, lpos), texture2D(sledOn, lpos), isOn);
highp vec4 color = mix(ledColor, tex, isOn * useSourceColors);
gl_FragColor = pix * color * qt_Opacity;
}"
}


But the app is still active and causes several wake-ups when it should be idle. =(

anda_skoa
18th January 2015, 16:06
But the app is still active and causes several wake-ups when it should be idle. =(

Are you sure the cause is the ShaderEffect?
Does it also happen if you don't have it?

Cheers,
_

KeineAhnung
19th January 2015, 21:28
If I take out the graph class(?) I do not have any issues. The Sensor is turned off correctly. Unfortunately I do not understand the code and cannot see what is active. I assumed it would be enough to stop the sensor because of the onValueChanged but something else sees to be permanently active within the shader.

wysota
19th January 2015, 22:48
Try breaking the loop between the two textures (should be enough to set recursive and/or live to false). Of course that will break your effect too so when it comes up again it will have to start from scratch. However making the shader effect invisible should have stopped its recalculation as well.

KeineAhnung
20th January 2015, 17:01
Thanks for the suggestions but they are not helping. The app is active as before. I will send it in and ask them to check if it is a bug on their side.

wysota
20th January 2015, 17:06
The app is active as before.
Is the shader active as well? Maybe you are looking for the problem in the wrong place?

KeineAhnung
20th January 2015, 17:12
I manually turned off the shader. Made sure that the screen is clear. Locked the phone and did the test. Same result as before. =(
If I comment out the graph and do the same, the app passes the test.

wysota
20th January 2015, 17:40
Can you specify the difference between the "graph" and the "shader"?

KeineAhnung
20th January 2015, 18:17
The main file consists of basically three parts. The graph, the sensor and the reflection. The graph is build by two shader effects if I understand the code correctly.



QUItMeterComponent {
id: meter

width: parent.width
height: parent.height/3*2
ledWidth: ledWidthSlider.value
ledHeight: ledHeightSlider.value
updateFrequency: meterSpeedSlider.value
active: root.active

}

Sensors.Accelerometer {
id: accel
dataRate: 100
active: root.sensor === 1
onReadingChanged: {
var v = Math.sqrt(accel.reading.x * accel.reading.x + accel.reading.y * accel.reading.y + accel.reading.z * accel.reading.z);
// Max 3G force
v = v / (3*9.81);
meter.value = v;
}
}

// Add mirror effect
ShaderEffect {
property variant source: ShaderEffectSource { sourceItem: meter; hideSource: false }

visible: root.active

anchors.top: meter.bottom
width: meter.width
height: parent.height/3

fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform highp float qt_Opacity;
uniform sampler2D source;
void main(void) {
highp vec2 pos = vec2(qt_TexCoord0.x, (1.0 - qt_TexCoord0.y*0.8));
pos.x += (qt_TexCoord0.y*0.2) * (pos.x * -1.0 + 1.0);
highp vec4 pix = texture2D(source, pos);
pix *= (0.4 - qt_TexCoord0.y*0.5) * min(qt_TexCoord0.y*5.0, 1.0);
gl_FragColor = pix * qt_Opacity;
}"
}


If I comment out the QUItMeterComponent meter I have no issues but no graph as well. I added the active property and bound visible, live and enabled to it but that does not help to send the app into an idle state.

wysota
20th January 2015, 19:57
So regarding post #9 what exactly did you turn off and what you didn't? Did you for example remove the accelerator object? What is the type of "root.sensor"? Does it take other values than 1? In what conditions?

KeineAhnung
20th January 2015, 20:28
Wow! Thanks for taking so much time to look into this!

Regarding root.sensor. I wanted to use different sensors with the graph. root.sensor tells the sensor to be active or not.
What do you mean by removing the accelerator object? Commenting out the sensor accel? No I did not do that. But so far if only the sensor is active the app is okay. Meaning that the sensor gets turned off incase the app is idle.

This is my modifed LedScreen.qml:


import QtQuick 2.0

Item {
id: root
// QML content used as a source
property alias sourceItem: effectSource.sourceItem
// Size of one led, in pixels
property real ledWidth: 64
property real ledHeight: 32
// Color of a led, default yellow
property color ledColor: Qt.rgba(1.0, 1.0, 0.0, 1.0);
// Set this true to use colors from sourceItem instead of ledColor
property bool useSourceColors: false
// Amount of opacity required to switch a led on
property real threshold: 0.5
// Stop ShaderEffect
property bool active: true

ShaderEffectSource {
id: effectSource
hideSource: true
smooth: false
live: root.active
recursive: root.active
}

ShaderEffect {
id: effectItem
width: screenWidth * root.ledWidth
height: screenHeight * root.ledHeight
anchors.centerIn: parent
smooth: false
visible: root.active

property real screenWidth: Math.floor(root.width / root.ledWidth)
property real screenHeight: Math.floor(root.height / root.ledHeight)
property var source: effectSource
property var sledOn: Image { source: "qrc:/images/led_on2.png"; sourceSize.width: root.ledWidth; sourceSize.height: root.ledHeight; visible: false }
property var sledOff: Image { source: "qrc:/images/led_off2.png"; sourceSize.width: root.ledWidth; sourceSize.height: root.ledHeight; visible: false }
property point screenSize: Qt.point(screenWidth, screenHeight)
property alias ledColor: root.ledColor
property real useSourceColors: root.useSourceColors ? 1.0 : 0.0
property alias threshold: root.threshold

fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform lowp float qt_Opacity;
uniform sampler2D source;
uniform sampler2D sledOn;
uniform sampler2D sledOff;
uniform highp vec2 screenSize;
uniform highp vec4 ledColor;
uniform lowp float useSourceColors;
uniform lowp float threshold;

void main() {
highp vec2 cpos = (floor(qt_TexCoord0 * screenSize) + 0.5) / screenSize;
highp vec4 tex = texture2D(source, cpos);
highp vec2 lpos = fract(qt_TexCoord0 * screenSize);
lowp float isOn = step(threshold, tex.r);
highp vec4 pix = mix(texture2D(sledOff, lpos), texture2D(sledOn, lpos), isOn);
highp vec4 color = mix(ledColor, tex, isOn * useSourceColors);
gl_FragColor = pix * color * qt_Opacity;
}"
}
}


I added the active property ad all root.active things. Within the QUItMeterComponet.qml I did the same if the default values were not flase:



import QtQuick 2.0
import "ledscreencomponent"

Item {
id: root

// Current value 0...1
property real value: 0.5
// Width and height of individual leds
property alias ledWidth: ledScreen.ledWidth
property alias ledHeight: ledScreen.ledHeight
// How frequently ledscreen progresses, 60fps/updateFrequency
property int updateFrequency: 5;
// is aktive
property bool active: true
// *** private ***
property int _updateFrequencyCounter: 0;
property real _peakValue: root.value

onValueChanged: {
root.value = Math.min(1.0, root.value)
root._peakValue = Math.max(root._peakValue, root.value);
}

width: 400
height: 300

LedScreen {
id: ledScreen
anchors.fill: parent
sourceItem: inputItem
useSourceColors: true
ledColor: Qt.rgba(0.4, 0.4, 0.4, 0.2)
threshold: 0.01
ledWidth: 32
ledHeight: 24
active: root.active
visible: root.active
}

// This area is the source, one pixel is one led
Rectangle {
id: sourceArea
width: Math.floor(root.width / ledScreen.ledWidth)
height: Math.floor(root.height / ledScreen.ledHeight)
color:"#000000"
visible: false

Image {
anchors.right: parent.right
anchors.bottom: parent.bottom
width: 1
height: parent.height*root.value
source: ("qrc:/images/eqline.png")
smooth: false
}

Rectangle {
anchors.right: parent.right
color: "#ffffff"
width: 1
height: 1
y: Math.floor(parent.height * (1 - root._peakValue))
}
}

ShaderEffect {
id: inputItem
property variant source: ShaderEffectSource { sourceItem: sourceArea; smooth: false }
property variant recursiveSource: recursiveSource
property real ledWidthPercent: ledScreen.ledWidth/ledScreen.width
anchors.fill: sourceArea
visible: false

fragmentShader: "
varying mediump vec2 qt_TexCoord0;
uniform highp float qt_Opacity;
uniform highp float ledWidthPercent;
uniform lowp sampler2D source;
uniform lowp sampler2D recursiveSource;
void main() {
highp vec4 pix;
if (qt_TexCoord0.x > (1.0 - ledWidthPercent)) {
pix = texture2D(source, qt_TexCoord0);
} else {
highp vec2 pos = vec2(qt_TexCoord0.x+ledWidthPercent, qt_TexCoord0.y);
pix = texture2D(recursiveSource, pos) * (1.0-ledWidthPercent*2.0);
}
gl_FragColor = pix * qt_Opacity;
}
"
}

ShaderEffectSource {
id: recursiveSource
sourceItem: inputItem
hideSource: true
visible: false
live: root.updateFrequency == 1//false
recursive: root.active
smooth: false
}

// Dummy element for syncing updates
Item {
id: dummyItem
NumberAnimation on rotation {
from:0
to: 360
duration: 1000
loops: Animation.Infinite
}
onRotationChanged: {
root._updateFrequencyCounter++;
if (root._updateFrequencyCounter == 1) {
// Reduce peak in first iteratio after repaint
var reduced_peakValue = root._peakValue - (root._peakValue - root.value)/2;
root._peakValue = Math.max(root.value, reduced_peakValue);
}
if (root._updateFrequencyCounter >= root.updateFrequency) {
recursiveSource.scheduleUpdate();
root._updateFrequencyCounter = 0;
}
}
}
}

wysota
20th January 2015, 22:53
First of all get rid of root.active and disable all what you want disabled by explicitly setting property values to false. See if that makes your app pass the test. Make sure the problem is not root.active never becoming false in the first place (I don't see any condition when it is set to false). In my opinion explicitly setting the shader's live property to false should disable updating the texture.