2
\$\begingroup\$

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' Renderers 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 of syncedData()
  • 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 of class
  • 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
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.