问题
I've got a big number of items that are quite complex (memory-heavy), and in order to fit in limited RAM, I want to cache some of them into textures and not keep the actual complex item in memory. In particular, items for which I want to do that are all those (among my complex items) that do not currently need to animate (and this means most of them). For the purposes of this question I call this kind of items "inactive".
My plan was:
- When an item "foo" becomes inactive:
- Create dynamically a
ShaderEffectSource
(with no associatedShaderEffect
) withlive: false; source: foo
, in the same rectangle that was occupied byfoo
. - call
scheduleUpdate()
on theShaderEffectSource
- when the
ShaderEffectSource
becomes updated, calldestroy()
onfoo
- Create dynamically a
The problem with this plan is with the "when the ShaderEffectSource
becomes updated" thing: there is no signal to notify me when that happens. I could use the fact (from the docs) that scheduleUpdate()
schedules the update for the next frame. So I would maybe start a Timer
that looks something like this:
Timer {
interval: 1 // 1ms interval meaning "fire every frame"
property int timesTriggered: 0
repeat: true
running: false
onTriggered: {
timesTriggered++;
if(timesTriggered == 2) {
complexItem.destroy();
running = false;
}
}
}
The == 2
check is to ensure that we've not only reached the next frame (which doesn't guaranteed that the scheduled update has happened already), but we've also reached the frame after that.
But that approach is a hack. Any approach that is not a hack?
回答1:
Sounds like you need to use QQuickRenderControl
. I haven't had my time with it yet, but from the doc it sounds like it is exactly what you need:
The QQuickRenderControl class provides a mechanism for rendering the Qt Quick scenegraph onto an offscreen render target in a fully application-controlled manner.
QQuickWindow and QQuickView and their associated internal render loops render the Qt Quick scene onto a native window. In some cases, for example when integrating with 3rd party OpenGL renderers, it might be beneficial to get the scene into a texture that can then be used in arbitrary ways by the external rendering engine. QQuickRenderControl makes this possible in a hardware accelerated manner, unlike the performance-wise limited alternative of using QQuickWindow::grabWindow()
When using a QQuickRenderControl, the QQuickWindow does not have to be shown or even created at all..
Management of the context and framebuffer object is up to the application...
There were some ways to achieve something similar prior to the introduction of QQuickRenderControl
, but that adds a lot of convenience to the process. The texture don't have to traverse VRAM to RAM to VRAM, and you have full control of its lifetime and such, so you can implement a QQuickItem
which basically just draws the texture using the efficient SG API.
回答2:
Maybe you can do it with grabToImage
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: appWindow
width: 1024
height: 800
visible: true
Button {
y: 410
text: 'run ' + counter
onClicked: foo()
}
property int counter: 0
function foo() {
counter++
gc()
var c = test.createObject(appWindow)
c.grabToImage(function(result) {
if (image.result) { // <-- Delete what is possible
image.result.image.destroy()
image.result.destroy()
}
image.result = result
}, Qt.size(400, 400))
c.destroy()
}
Image {
id: image
width: 400
height: 400
x: 100
property var result
source: result ? result.url : ''
onSourceChanged: foo()
}
Component {
id: test
Grid {
width: 400
height: 400
columns: 40
rows: 40
Repeater {
model: 40 * 40
delegate: Rectangle {
width: 10
height: 10
color: 'green' // <--- don't always create a new color
}
}
Component.onCompleted: console.log('Created')
Component.onDestruction: console.log('Destroyed')
}
}
}
来源:https://stackoverflow.com/questions/42698176/how-to-cache-complex-item-into-texture-and-release-the-memory-for-the-actual-com