I'm making a system similar to Qt Quick with its scenegraph. Since it's embedded within Qt Quick with its threading system, the items' Renderer
s should sync some data from their "parent" items at opportune moments, so this data can be used in the rendering thread, while the GUI thread mutates the "parent" item's data.
The relevant Qt Quick things that I'm (partially) imitating are:
- base class
QQuickFramebufferObject
- base class
QQuickFramebufferObject::Renderer
- the function
QQuickFramebufferObject::Renderer::synchronize()
(not included in the sample code here, for brevity)
Now, in Qt's relevant examples, a class subclassing Renderer will have (roughly) the same member variables as its "parent" item, so it can sync data between the two places. But this means that the "synced" data in the Renderer (e.g. source
- an image's source url) are going to live next to non-synced data, such as some an OpenGL texture id needed for the rendering. To keep things tidy, I have created a SyncedData class hierarchy instead. My design attempt looks like this (simplified):
// ###### BASE CLASSES
struct MyItem : public QObject {
int x, y, w, h;
};
struct MyItemSyncedData {
int x, y, w, h;
};
struct MyItemRenderer {
virtual void render() { }
virtual MyItemSyncedData* syncedData() {
return lazyInstantiateSyncedData<MyItemSyncedData>();
}
protected:
// implementation helper for this class and subclasses
template<class SyncedDataSubType>
SyncedDataSubType* lazyInstantiateSyncedData() {
if(m_syncedData == nullptr) {
m_syncedData = new SyncedDataSubType();
}
return m_syncedData;
}
private:
MyItemSyncedData* m_syncedData = nullptr;
};
// ###### DERIVED CLASSES
struct MyImage : public MyItem {
string source;
};
struct MyImageSyncedData : public MyItemSyncedData {
string source;
};
struct MyImageRenderer : public MyItemRenderer {
GLuint textureId; // non-synced stuff
virtual void render() {
// use this->syncedData()->source here
}
// covariant override
virtual MyImageSyncedData* syncedData() {
return lazyInstantiateSyncedData<MyImageSyncedData>();
}
};
I see three disadvantages in this design:
- seems maybe too complex for what it does
- every subclass of
MyItemRenderer
has to include a trivial reimplementation ofsyncedData()
- of course, the duplication of any values I want to sync, but I don't think that's gonna be avoidable.
If you're wondering about my motivation for reinventing the wheel by making a system similar to Qt Quick: see here.
Things I don't want critique on, because they're just to make the code shorter:
- my use of
struct
instead ofclass
- my use of public member variables (imagine they're Qt properties instead)
- my use of plain pointers and skipping any deallocation
- my skipping of the
std::
prefix