I observed a deadlock when scanning AU plugins in-process in the
AudioPluginHost, and then clicking the "cancel" button in the scan
progress alert window.
The cause of the deadlock seems to be that JUCE uses async messages to
create and destroy AU plugins on the main thread. When running a plugin
scanner on a background thread, it was possible to end up in a situation
where the background thread was waiting on the message thread to process
a create/destroy message; and, at the same time, the main thread was
blocked waiting for all scan jobs to complete. This seemed to happen
because scanFinished() was called directly from the Scanner's
timerCallback as soon as the progress window was cancelled, even if
there was still a scan in progress at that point.
To avoid the deadlock, we now wait until the current scan has completely
finished before allowing the timerCallback to call scanFinished(). If no
scan is in progress, then the main thread can safely destroy the scanner
ThreadPool without needing to wait at that point.
In the old implementation, because pushSample() could be called on the
audio thread, and updated the levels array and the nextSample value
non-atomically, other threads (e.g. the UI thread) were not guaranteed
to see these updates in a consistent order.
We now query the incoming buffers to see how many samples are available.
If the callback's buffers will fit into our preallocated buffer (i.e. the
length in samples is smaller or equal to the preallocated buffer), then
we perform an audio callback with the provided data, even if the number
of samples is smaller than expected.
If the callback's buffers are larger than expected, we split the
incoming buffer into chunks that are no larger than the prepared
buffer-size.
The goal of this change is to ensure that the safeAreaInsets and
keyboardInsets members of Display correctly take the current system UI
and screen cutouts into account.
This change also enables rendering behind the status bar and navigation
bar for JUCE applications. This is in line with the new defaults in
Android 15, where building against the Android SDK 35 will automatically
enable "edge-to-edge" drawing. Enabling this behaviour on older
platforms too provides a more consistent experience.
The OpenGL renderer listens for imageDataChanged() to invalidated cached
textures.
Before this change, the SimpleFFTDemo would display a static (outdated)
image on Windows when using the OpenGLRenderer.
When syncing from CPU->GPU storage, we currently copy the entire image
contents. The contents of the CPU backup completely replace the old GPU
image. Therefore, if any pixels need to retain their existing values, we
need to read those pixels before overwriting them. This in turn implies
that, when a BitmapData refers to a subsection of the image, we should
always flush GPU->CPU storage first, so that the subsequent CPU->GPU
sync doesn't clobber pixels outside of the BitmapData region with
outdated values.
It's clear that copying the entire image back and forth could be
suboptimal when writing to image subsections, but to optimise this
process we'd have to keep track of dirty image regions or similar, which
may in turn pessimise more common cases.