Overlapping views

Hello,

I have a cviewcontainer that contain two custom views (inherited by CView) that are overlapping each other inside the viewcontainer. When I change some parameters that is connected to the lastly added view to the viewcontainer the other view is “flickering” when redrawing are happens on the lastly added view.

Is this expected behavior if you have overlapping views? Is it not recommended to have overlapping views? Or is it something that I have missed to enable this kind of stuff?

“flickering” could be anything. Can you provide a video?
In general overlapping views should work.


The read line is its own CView that uses the vstguitimer to trigger the invalid() function of that CView to be redrawn.

The filter response view and the mouse event handling is in the other CView. That CView does only redraw when a parameter changes.

Both of those views are added to a CViewcontainer and the filter response view is added last so that it takes my mouse events.

The red line Cview shall not handle any mouse events or listen to any parameters. it gets its data pushed to a ringbuffer from the controller. The controller gets the data from the processor with the dataexchange utlitity (it sends the audio samples from the processor).

As you can see in the video there is no flickering of the red line if I dont change any parameter of the filter response view. I also have tried mapped the parameter i changed (HP filter frequency) in the video to a CKnob that is not included in the cviewcontainer and placed it under the graph view. If I then remove the addView(filterResponse) in my cviewcontainer, and I mess with the HP filter frequency parameter from the CKnob, the red line does not flicker.

I suspect that the view with the red line is drawn, but the data it should draw is not there.
Have you checked that you can draw a static red line instead of the FFT?

Just tried that, I commented out the entire draw function for the read line and replaced it with this line:

pContext->drawLine({0.0, getHeight() / 2}, {size.right, getHeight() / 2});

still flickering when I do the same steps as previously

Can you provide a code example? I do this kind of stuff all the time without flickering.
And by the way, which OS and host are you seeing this?

When you said you do this kind of stuff all the time without flickering, I knew I had messed up somewhere :sweat_smile:

It made me at least understand that it should be possible to do this type of drawings. So I started to analyze my code a little bit deeper and found a little bug.

Thank you for your support!

You’re welcome.

Hmm still having some problems with stuttering of the red line.

@Arne_Scheffler You said you are doing this kind of drawings all the time, are you able to draw a waveform kinda view like i do that is updated with a timer and then having a CXYPad embedded in to the same CViewContainer and see the waveform drawing smoothly when you are moving around the handle in the CXYPad?

@singularity1991 , I’m not using a XY pad, just a half transparent slider and it does not flicker.

Hmm okey, how fast is your firetime of the timer? Im using the CVSTGUITimer to fire off every 50ms and in the “timer function” I call invalid() to repaint the Cview of my waveform.

It even stutters for me even if i dont have the two views in a CViewContainer. If I have a standard CXYPad on top of the waveform view it still stutters when I move around with the CXYPad. If I don’t move around the CXYPad, I don’t experience any stutter.

I’m drawing with 60 Hz so every 16,6667 ms.
This more sounds like an issue on how your internal program logic is happening when it also stutters when the views don’t overlap. If you disconnect the xy pad from doing anything when you interact with it, does the stuttering still occurs?


Here’s a video example of the stuffering of the waveform when I interact with a slider I just dragged in from the GuiEditor. They do not belong to the same CViewContainer, its just placed on top of the custom cview. I send 2048 audio samples with every dataexchange call from the processor.

I have been following the dataexchange example plugin to get audio samples from processor to controller. But instead of just drawing the highest peaks of a block, I push all audio samples to my cview to be drawn. And I also dont use the NanoVG graphics that is used in the dataexchange example, I want to be able to use the VSTGUI framework.

MyController.cpp

	void PLUGIN_API EQController::onDataExchangeBlocksReceived(
		Vst::DataExchangeUserContextID userContextID, uint32 numBlocks, Vst::DataExchangeBlock *blocks,
		TBool onBackgroundThread)
	{
		for (auto index = 0u; index < numBlocks; ++index)
		{
			auto dataBlock = toDataBlock(blocks[index]);
			for (int i = 0; i < dataBlock->numSamples; ++i)
			{
				auto data = dataBlock->samples[i];
				dynamic_cast<Waveform *>(mEQviewContainer->getView(0))->pushAudioBufferData(data);
			}

		}
		dynamic_cast<Waveform *>(mEQviewContainer->getView(0))->renderIfNeeded();
	}

Waveform.h

    std::recursive_mutex dataMutex;
    using AudioBufferDataQueue = Steinberg::OneReaderOneWriter::RingBuffer<float>;
    AudioBufferDataQueue audioBufferDataQueue{2048u * 50};
    std::deque<float> mAudioValues;

Waveform.cpp

Waveform::Waveform(const VSTGUI::CRect &size) : VSTGUI::CView(size)
{
    mAudioValues.resize(2048, 0.0f);

    timer = new VSTGUI::CVSTGUITimer(dynamic_cast<CBaseObject *>(this), 50);
    timer->start();
}

VSTGUI::CMessageResult Waveform::notify(VSTGUI::CBaseObject *sender, VSTGUI::IdStringPtr message)
{
    if (message == VSTGUI::CVSTGUITimer::kMsgTimer)
    {
        invalid();
        return VSTGUI::kMessageNotified;
    }

    return VSTGUI::kMessageUnknown;
}

Waveform::~Waveform() {}

bool Waveform::pushAudioBufferData(float data)
{
    return audioBufferDataQueue.push(data);
}

void Waveform::renderIfNeeded()
{
    if (!dataMutex.try_lock())
    {
        return;
    }

    float sample;

    while (audioBufferDataQueue.pop(sample))
    {
        mAudioValues.pop_front();
        mAudioValues.push_back(sample);
    }

    dataMutex.unlock();
}

void Waveform::draw(VSTGUI::CDrawContext *pContext)
{
    std::lock_guard<std::recursive_mutex> g(dataMutex);

    const auto size = getViewSize();
    const int numPoints = size.getWidth();

    pContext->setLineWidth(4);
    pContext->setFrameColor(VSTGUI::kRedCColor);

    double ypt = size.bottom - size.getHeight() / 2 * mAudioValues[0];
    if (ypt > size.bottom - 1)
        ypt = size.getHeight() / 2 - 1;

    VSTGUI::CPoint lastPoint(size.left, ypt);
    auto test = getViewSize().getSize();
    float xAdd = test.x / mAudioValues.size();
    float x{0.0};

    for (auto value : mAudioValues)
    {
        VSTGUI::CPoint currentPoint(x, size.getHeight() / 2 + size.getHeight() * value);
        pContext->drawLine(lastPoint, currentPoint);
        lastPoint.x = currentPoint.x;
        lastPoint.y = currentPoint.y;
        x += xAdd;
    }
    setDirty(false);
}

I dragged in another slider as well and placed it outside the waveform view. When I interact with that slider the waveform does not stutter, but as you can see in the video it stutters when interacting with the slider that is placed in top of the waveform. Both of the sliders are not connected to any parameter

If you copied the example code, you most likely copied the code which set up the data exchange queue to deliver the data on a background thread. If you change that, so that the data is delivered on the main thread, I bet your problems are gone.
The example was explicitly aimed to show how to render on a background thread and does things you don’t need to do when you render on the main thread.

Aha so i should just have:
dispatchOnBackgroundThread = false

And I dont need to use the ringbuffer and have synchronisation between the data received in my controller and what is drawn in my waveform? I can just push it directly to the mAudioValues queue?

Correct, all is happening on one thread, so no thread synchronization needed.

Awesome thank you! Is there anywhere documented or shown how the drawing principles are working? I really like the documentation portal of the VSDSDK but I miss the same docs for the vstgui framework.

My simple understanding is that when you “invalidate” a view, it only “mark it as dirty” and the gui thread (main thread?) goes through all the views and check them if they are dirty and if they are, they are redrawn in the next cycle? Is that correct?

But lets say if I have a CControl that react to mouse event, for example onMouseMoved. If I call invalid() in the onMouseMoved function for example on a CXYPad, does that spam invalid() calls, or does it just ripple down to the CControl to be redrawn every cycle?

VSTGUI is single threaded and driven by the system event loop. The system decides when it is time to redraw, so that you always draw efficiently without knowing the refresh rate of the display your view is currently on. It would not make sense to redraw your view 120 times a second when the display only has a refresh rate of 60 frames per second. So it’s impossible to directly draw on the screen with VSTGUI with a simple call. You always go thru the operating system.