Fixes a "pure virtual method" crash. The inputs will try to send invalidate
cache signals through the node while it's being destroyed if these are left
connected. There's no purpose to sending invalidate cache signals since any
nodes affected by this one will automatically invalidate the cache from its
outputs being disconnected and the node being destroyed so this shouldn't
cause issues.
Fixes recurring design issue that the Blocks were a frequent exception to the
DAG concept. The Blocks connecting to each others inputs/outputs while not
necessarily being "dependent" on each other to produce an image continually
causes issues while trying to create a rendering code path. This redesign
should provide a more "directed" approach to the directed acyclic graph.
NodeParams used to be destroyed automatically through the QObject system, but
now that NodeParams perform actions that access the parent Node object in
their destructor, the NodeParam destructor needs to be called before the Node
has been fully destroyed. By destroying explicitly in the Node destructor we
accomplish this.
If a NodeParam is destroyed (presumably as the result of its parent Node being
destroyed), any edge objects connecting to another param should be destroyed
or else the edge will be floating referencing a now destroyed object.
It seems unwise to let NodeInputs take more than one value, but at times
it makes sense to create a list or array of inputs. This class will create
sub-parameters as an easy way to provide variable inputs while also enforcing
one connection per input parameter.
This is also the primary motivation for the previous commit (disambiguating
when a NodeParam's parent is a Node vs some other type).
Old code assumes that a NodeParam's parent will always be a Node. The function
has been separated off and tweaked in the event that this is not the case.
It was becoming inevitable to get rid of this, it was continuously an exception
that had to be made to the DAG concept seeing as blocks are never really
"dependents" on each other, i.e. the images they produce have nothing to do
with other blocks. The only exception is a transition which will also be
easier to accomplish with this slightly different design.
Audio no longer needs to be the same format/sample rate to play correctly. This
should be the last step to making audio work correctly (minus several
optimizations still yet to be done).
For accuracy, whenever we need to perform an audio resample, we need to do it
in advance. This is in a process called "conforming" and this commit introduces
the framework by which the decoder can automatically conform an audio stream
to arbitrary parameters for accurate rendering.
I was under the impression Q_DISABLE_COPY_MOVE was a much older function than
it is. Since we historically target 5.6 and these functions are small
convenience functions, I just implemented them in the actual codebase.
Previous iteration had params attached to the backend and the params couldn't
change without being destroyed and re-instantiated. This is not necessary in
this iteration so doing so only wastes resources.
Since we're now working with a separate proxy copy of the original node graph,
if the user changes a parameter in one of those nodes (triggering an
InvalidateCache signal), the values in our copied graph need to be updated
with these new values too.
A signal emitted and received in the same thread will call other functions
before returning to the one that emitted the signal. If these other functions
try to lock a mutex while a mutex is already locked by the emitting function,
we get stuck in a deadlock. These changes ensure that a signal is never emitted
by any function until all the nodes locked by it are unlocked.
Previous iterations would use mutexes to prevent changing of the graph
mid-render, however several user actions would need to capture these mutexes
causing the main thread to hang until the current render job (frame/range of
samples) was complete. We now copy the nodes necessary as part of the "compile"
process so that the main thread shouldn't need nearly as much blocking while
caching occurs.
Useful for detecting when a graph needs recompiling, in tandem with the
"InvalidateCache()" signal when necessary, this signal ripples through the
nodes when any of the connections change which will likely need handling by
the renderer.