Audacity 3.2.0
Classes | Public Types | Public Member Functions | Static Public Member Functions | Private Member Functions | Private Attributes | List of all members
EffectStage Class Referencefinal

Decorates a source with a non-timewarping effect, which may have latency. More...

#include <EffectStage.h>

Inheritance diagram for EffectStage:
[legend]
Collaboration diagram for EffectStage:
[legend]

Classes

struct  CreateToken
 

Public Types

using Factory = std::function< std::shared_ptr< EffectInstance >()>
 
- Public Types inherited from AudioGraph::Source
using Buffers = AudioGraph::Buffers
 

Public Member Functions

 EffectStage (CreateToken, int channel, int nInputChannels, Source &upstream, Buffers &inBuffers, const Factory &factory, EffectSettings &settings, double sampleRate, std::optional< sampleCount > genLength)
 Don't call directly but use Create() More...
 
 EffectStage (const EffectStage &)=delete
 
EffectStageoperator= (const EffectStage &)=delete
 
 ~EffectStage () override
 Finalizes the instance. More...
 
bool AcceptsBuffers (const Buffers &buffers) const override
 
bool AcceptsBlockSize (size_t size) const override
 See postcondition of constructor. More...
 
std::optional< size_t > Acquire (Buffers &data, size_t bound) override
 Occupy vacant space in Buffers with some data. More...
 
sampleCount Remaining () const override
 Result includes any amount Acquired and not yet Released. More...
 
bool Release () override
 Caller is done examining last Acquire()d positions. More...
 
- Public Member Functions inherited from AudioGraph::Source
virtual ~Source ()
 
virtual bool AcceptsBuffers (const Buffers &buffers) const =0
 
virtual bool AcceptsBlockSize (size_t blockSize) const =0
 
virtual std::optional< size_t > Acquire (Buffers &data, size_t bound)=0
 Occupy vacant space in Buffers with some data. More...
 
virtual sampleCount Remaining () const =0
 Result includes any amount Acquired and not yet Released. More...
 
virtual bool Release ()=0
 Caller is done examining last Acquire()d positions. More...
 
virtual bool Terminates () const
 Needed only to make some postconditions assertable; defaults true. More...
 

Static Public Member Functions

static std::unique_ptr< EffectStageCreate (int channel, int nInputChannels, Source &upstream, Buffers &inBuffers, const Factory &factory, EffectSettings &settings, double sampleRate, std::optional< sampleCount > genLength)
 Satisfies postcondition of constructor or returns null. More...
 

Private Member Functions

sampleCount DelayRemaining () const
 
bool Process (EffectInstance &instance, size_t channel, const Buffers &data, size_t curBlockSize, size_t outBufferOffset) const
 Produce exactly curBlockSize samples in data More...
 
std::optional< size_t > FetchProcessAndAdvance (Buffers &data, size_t bound, bool doZeros, size_t outBufferOffset=0)
 

Private Attributes

Source & mUpstream
 
BuffersmInBuffers
 
const std::vector< std::shared_ptr< EffectInstance > > mInstances
 
EffectSettingsmSettings
 
const double mSampleRate
 
const bool mIsProcessor
 
sampleCount mDelayRemaining
 
size_t mLastProduced {}
 
size_t mLastZeroes {}
 
bool mLatencyDone { false }
 
bool mCleared { false }
 

Detailed Description

Decorates a source with a non-timewarping effect, which may have latency.

Definition at line 26 of file EffectStage.h.

Member Typedef Documentation

◆ Factory

using EffectStage::Factory = std::function<std::shared_ptr<EffectInstance>()>

Definition at line 30 of file EffectStage.h.

Constructor & Destructor Documentation

◆ EffectStage() [1/2]

EffectStage::EffectStage ( CreateToken  ,
int  channel,
int  nInputChannels,
Source &  upstream,
Buffers inBuffers,
const Factory factory,
EffectSettings settings,
double  sampleRate,
std::optional< sampleCount genLength 
)

Don't call directly but use Create()

Parameters
channelselects one channel if non-negative; else, all channels
factoryused only in construction, will be invoked one or more times
Precondition
upstream.AcceptsBlockSize(inBuffers.BlockSize())
Postcondition
AcceptsBlockSize(inBuffers.BlockSize())
ProcessInitialize() succeeded on each instance that was made by factory
Parameters
mapnot required after construction
Precondition
channel < sequence.NChannels()

Definition at line 59 of file EffectStage.cpp.

63 : mUpstream { upstream }
64 , mInBuffers { inBuffers }
66 factory, settings, sampleRate, genLength, channel, nInputChannels) }
69 , mIsProcessor { !genLength.has_value() }
70 , mDelayRemaining { genLength ? *genLength : sampleCount::max() }
71{
72 assert(upstream.AcceptsBlockSize(inBuffers.BlockSize()));
73 assert(this->AcceptsBlockSize(inBuffers.BlockSize()));
74
75 // Establish invariant
77}
static RegisteredToolbarFactory factory
static Settings & settings()
Definition: TrackInfo.cpp:51
void Rewind()
Reset positions to starts of buffers.
Source & mUpstream
Definition: EffectStage.h:88
bool AcceptsBlockSize(size_t size) const override
See postcondition of constructor.
const std::vector< std::shared_ptr< EffectInstance > > mInstances
Definition: EffectStage.h:91
const double mSampleRate
Definition: EffectStage.h:93
sampleCount mDelayRemaining
Definition: EffectStage.h:96
Buffers & mInBuffers
Definition: EffectStage.h:90
const bool mIsProcessor
Definition: EffectStage.h:94
EffectSettings & mSettings
Definition: EffectStage.h:92
static sampleCount max()
Definition: SampleCount.h:69
std::vector< std::shared_ptr< EffectInstance > > MakeInstances(const EffectStage::Factory &factory, EffectSettings &settings, double sampleRate, std::optional< sampleCount > genLength, int channel, int nInputChannels)
Definition: EffectStage.cpp:20

References AcceptsBlockSize(), AudioGraph::Buffers::BlockSize(), mInBuffers, and AudioGraph::Buffers::Rewind().

Here is the call graph for this function:

◆ EffectStage() [2/2]

EffectStage::EffectStage ( const EffectStage )
delete

◆ ~EffectStage()

EffectStage::~EffectStage ( )
override

Finalizes the instance.

Definition at line 94 of file EffectStage.cpp.

95{
96 // Allow the instances to cleanup
97 for (auto &pInstance : mInstances)
98 if (pInstance)
99 pInstance->ProcessFinalize();
100}

References mInstances.

Member Function Documentation

◆ AcceptsBlockSize()

bool EffectStage::AcceptsBlockSize ( size_t  size) const
overridevirtual

See postcondition of constructor.

Implements AudioGraph::Source.

Definition at line 107 of file EffectStage.cpp.

108{
109 // Test the equality of input and output block sizes
110 return mInBuffers.BlockSize() == size;
111}
size_t BlockSize() const

References AudioGraph::Buffers::BlockSize(), mInBuffers, and size.

Referenced by Acquire(), and EffectStage().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AcceptsBuffers()

bool EffectStage::AcceptsBuffers ( const Buffers buffers) const
overridevirtual
Returns
true

Implements AudioGraph::Source.

Definition at line 102 of file EffectStage.cpp.

103{
104 return true;
105}

Referenced by Acquire().

Here is the caller graph for this function:

◆ Acquire()

std::optional< size_t > EffectStage::Acquire ( Buffers data,
size_t  bound 
)
overridevirtual

Occupy vacant space in Buffers with some data.

May exceeed a single block of production Can assume same buffer is passed each time, while the caller advances it over the previous production, or discards it, or rotates the buffer. May rewind or rotate the buffer.

Returns
number of positions available to read from data or nullopt to fail
Precondition
AcceptsBuffers(data)
AcceptsBlockSize(data.BlockSize())
bound <= data.BlockSize()
data.BlockSize() <= data.Remaining()
Postcondition
result: !result || *result <= bound
result: !result || *result <= data.Remaining()
result: !result || *result <= Remaining()
data.Remaining() > 0
result: !result || bound == 0 || Remaining() == 0 || *result > 0 (progress guarantee)
!Terminates() or Remaining() was not previously defined, or is unchanged

Implements AudioGraph::Source.

Definition at line 113 of file EffectStage.cpp.

114{
115 assert(AcceptsBuffers(data));
116 assert(AcceptsBlockSize(data.BlockSize()));
117 // pre, needed for Process() and Discard()
118 assert(bound <= std::min(data.BlockSize(), data.Remaining()));
119
120 // For each input block of samples, we pass it to the effect along with a
121 // variable output location. This output location is simply a pointer into a
122 // much larger buffer. This reduces the number of calls required to add the
123 // samples to the output track.
124 //
125 // Upon return from the effect, the output samples are "moved to the left" by
126 // the number of samples in the current latency setting, effectively removing any
127 // delay introduced by the effect.
128 //
129 // At the same time the total number of delayed samples are gathered and when
130 // there is no further input data to process, the loop continues to call the
131 // effect with an empty input buffer until the effect has had a chance to
132 // return all of the remaining delayed samples.
133
134 // Invariant satisfies pre for mUpstream.Acquire() and for Process()
136
137 size_t curBlockSize = 0;
138
139 if (auto oCurBlockSize = FetchProcessAndAdvance(data, bound, false)
140 ; !oCurBlockSize
141 )
142 return {};
143 else {
144 curBlockSize = *oCurBlockSize;
145 if (mIsProcessor && !mLatencyDone) {
146 // Come here only in the first call to Acquire()
147 // Some effects (like ladspa/lv2 swh plug-ins) don't report latency
148 // until at least one block of samples is processed. Find latency
149 // once only for the track and assume it doesn't vary
150 auto delay = mDelayRemaining =
151 mInstances[0]->GetLatency(mSettings, mSampleRate);
152 for (size_t ii = 1, nn = mInstances.size(); ii < nn; ++ii)
153 if (mInstances[ii] &&
154 mInstances[ii]->GetLatency(mSettings, mSampleRate) != delay)
155 // This mismatch is unexpected. Fail
156 return {};
157 // Discard all the latency
158 while (delay > 0 && curBlockSize > 0) {
159 auto discard = limitSampleBufferSize(curBlockSize, delay);
160 data.Discard(discard, curBlockSize - discard);
161 delay -= discard;
162 curBlockSize -= discard;
163 if (curBlockSize == 0) {
164 if (!(oCurBlockSize = FetchProcessAndAdvance(data, bound, false)
165 ))
166 return {};
167 else
168 curBlockSize = *oCurBlockSize;
169 }
170 mLastProduced -= discard;
171 }
172 if (curBlockSize > 0) {
173 assert(delay == 0);
174 if (curBlockSize < bound) {
175 // Discarded all the latency, while upstream may still be
176 // producing. Try to fill the buffer up to the bound.
177 if (!(oCurBlockSize = FetchProcessAndAdvance(
178 data, bound - curBlockSize, false, curBlockSize)
179 ))
180 return {};
181 else
182 curBlockSize += *oCurBlockSize;
183 }
184 }
185 else while (delay > 0) {
186 assert(curBlockSize == 0);
187 // Finish one-time delay in case it exceeds entire upstream length
188 // Upstream must have been exhausted
189 assert(mUpstream.Remaining() == 0);
190 // Feed zeroes to the effect
191 auto zeroes = limitSampleBufferSize(data.BlockSize(), delay);
192 if (!(FetchProcessAndAdvance(data, zeroes, true)))
193 return {};
194 delay -= zeroes;
195 // Debit mDelayRemaining later in Release()
196 }
197 mLatencyDone = true;
198 }
199 }
200
201 if (mIsProcessor && curBlockSize < bound) {
202 // If there is still a short buffer by this point, upstream must have
203 // been exhausted
204 assert(mUpstream.Remaining() == 0);
205
206 // Continue feeding zeroes; this code block will produce as many zeroes
207 // at the end as were discarded at the beginning (over one or more visits)
208 auto zeroes =
209 limitSampleBufferSize(bound - curBlockSize, mDelayRemaining);
210 if (!FetchProcessAndAdvance(data, zeroes, true, curBlockSize))
211 return {};
212 // Debit mDelayRemaining later in Release()
213 }
214
215 auto result = mLastProduced + mLastZeroes;
216 // assert the post
217 assert(data.Remaining() > 0);
218 assert(result <= bound);
219 assert(result <= data.Remaining());
220 assert(result <= Remaining());
221 assert(bound == 0 || Remaining() == 0 || result > 0);
222 return { result };
223}
int min(int a, int b)
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
size_t Remaining() const
std::optional< size_t > FetchProcessAndAdvance(Buffers &data, size_t bound, bool doZeros, size_t outBufferOffset=0)
size_t mLastZeroes
Definition: EffectStage.h:98
sampleCount Remaining() const override
Result includes any amount Acquired and not yet Released.
bool AcceptsBuffers(const Buffers &buffers) const override
size_t mLastProduced
Definition: EffectStage.h:97
bool mLatencyDone
Definition: EffectStage.h:99

References AcceptsBlockSize(), AcceptsBuffers(), AudioGraph::Buffers::BlockSize(), AudioGraph::Buffers::Discard(), FetchProcessAndAdvance(), limitSampleBufferSize(), mDelayRemaining, min(), mInBuffers, mInstances, mIsProcessor, mLastProduced, mLastZeroes, mLatencyDone, mSampleRate, mSettings, mUpstream, AudioGraph::Buffers::Remaining(), and Remaining().

Here is the call graph for this function:

◆ Create()

auto EffectStage::Create ( int  channel,
int  nInputChannels,
Source &  upstream,
Buffers inBuffers,
const Factory factory,
EffectSettings settings,
double  sampleRate,
std::optional< sampleCount genLength 
)
static

Satisfies postcondition of constructor or returns null.

Definition at line 79 of file EffectStage.cpp.

83{
84 try {
85 return std::make_unique<EffectStage>(
86 CreateToken {}, channel, nInputChannels, upstream, inBuffers, factory,
87 settings, sampleRate, genLength);
88 }
89 catch (const std::exception &) {
90 return nullptr;
91 }
92}

References factory, anonymous_namespace{ClipSegmentTest.cpp}::sampleRate, and settings().

Referenced by PerTrackEffect::ProcessTrack(), and Mixer::RegisterEffectStage().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ DelayRemaining()

sampleCount EffectStage::DelayRemaining ( ) const
inlineprivate

Definition at line 72 of file EffectStage.h.

73 { return std::max<sampleCount>(0, mDelayRemaining); }

Referenced by FetchProcessAndAdvance(), and Remaining().

Here is the caller graph for this function:

◆ FetchProcessAndAdvance()

std::optional< size_t > EffectStage::FetchProcessAndAdvance ( Buffers data,
size_t  bound,
bool  doZeros,
size_t  outBufferOffset = 0 
)
private

Definition at line 225 of file EffectStage.cpp.

227{
228 std::optional<size_t> oCurBlockSize;
229 // Generator always supplies zeroes in
230 doZeroes = doZeroes || !mIsProcessor;
231 if (!doZeroes)
232 oCurBlockSize = mUpstream.Acquire(mInBuffers, bound);
233 else {
234 if (!mCleared) {
235 // Need to do this the first time, only, that we begin to give zeroes
236 // to the processor
238 const auto blockSize = mInBuffers.BlockSize();
239 for (size_t ii = 0; ii < mInBuffers.Channels(); ++ii) {
240 auto p = &mInBuffers.GetWritePosition(ii);
241 std::fill(p, p + blockSize, 0);
242 }
243 mCleared = true;
244 }
245 oCurBlockSize = {
247 if (!mIsProcessor)
248 // Do this (ignoring result) so we can correctly Release() upstream
249 mUpstream.Acquire(mInBuffers, bound);
250 }
251 if (!oCurBlockSize)
252 return {};
253
254 const auto curBlockSize = *oCurBlockSize;
255 if (curBlockSize == 0)
256 assert(doZeroes || mUpstream.Remaining() == 0); // post of Acquire()
257 else {
258 // Called only in Acquire()
259 // invariant or post of mUpstream.Acquire() satisfies pre of Process()
260 // because curBlockSize <= bound <= mInBuffers.blockSize()
261 // == data.BlockSize()
262 // and mInBuffers.BlockSize() <= mInBuffers.Remaining() by invariant
263 // and data.BlockSize() <= data.Remaining() by pre of Acquire()
264 for (size_t ii = 0, nn = mInstances.size(); ii < nn; ++ii) {
265 auto &pInstance = mInstances[ii];
266 if (!pInstance)
267 continue;
268 if (!Process(*pInstance, ii, data, curBlockSize, outBufferOffset))
269 return {};
270 }
271
272 if (doZeroes) {
273 // Either a generator or doing the tail; will count down delay
275 if (!mIsProcessor) {
276 // This allows polling the progress meter for a generator
277 if (!mUpstream.Release())
278 return {};
279 }
280 }
281 else {
282 // Will count down the upstream
283 mLastProduced += curBlockSize;
284 if (!mUpstream.Release())
285 return {};
286 mInBuffers.Advance(curBlockSize);
288 // Maintain invariant minimum availability
290 }
291 }
292 return oCurBlockSize;
293}
float & GetWritePosition(unsigned iChannel)
Get writable position for one channel.
void Advance(size_t count)
Move the positions.
size_t Rotate()
Shift all data at and after the old position to position 0.
unsigned Channels() const
bool Process(EffectInstance &instance, size_t channel, const Buffers &data, size_t curBlockSize, size_t outBufferOffset) const
Produce exactly curBlockSize samples in data
sampleCount DelayRemaining() const
Definition: EffectStage.h:72

References AudioGraph::Buffers::Advance(), AudioGraph::Buffers::BlockSize(), AudioGraph::Buffers::Channels(), DelayRemaining(), AudioGraph::Buffers::GetWritePosition(), limitSampleBufferSize(), mCleared, mDelayRemaining, mInBuffers, mInstances, mIsProcessor, mLastProduced, mLastZeroes, mUpstream, Process(), AudioGraph::Buffers::Remaining(), AudioGraph::Buffers::Rewind(), and AudioGraph::Buffers::Rotate().

Referenced by Acquire().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ operator=()

EffectStage & EffectStage::operator= ( const EffectStage )
delete

◆ Process()

bool EffectStage::Process ( EffectInstance instance,
size_t  channel,
const Buffers data,
size_t  curBlockSize,
size_t  outBufferOffset 
) const
private

Produce exactly curBlockSize samples in data

Precondition
curBlockSize <= data.BlockSize() - outBufferOffset
curBlockSize <= data.Remaining() - outBufferOffset
curBlockSize <= mInBuffers.Remaining()
Returns
success

Definition at line 295 of file EffectStage.cpp.

298{
299 size_t processed{};
300 try {
301 const auto positions = mInBuffers.Positions();
302 const auto nPositions = mInBuffers.Channels();
303 // channel may be nonzero in the case of a plug-in that only reads
304 // one channel at a time, so multiple instances are made to mix stereo
305 assert(channel <= nPositions);
306 std::vector<float *> inPositions(
307 positions + channel, positions + nPositions - channel);
308 // When the plug-in expects many input channels, replicate the last
309 // buffer (assumed to be zero-filled) as dummy input
310 inPositions.resize(
311 instance.GetAudioInCount() - channel, inPositions.back());
312
313 std::vector<float *> advancedOutPositions;
314 const auto size = instance.GetAudioOutCount() - channel;
315 advancedOutPositions.reserve(size);
316
317 auto outPositions = data.Positions();
318 // It is assumed that data has at least one dummy buffer last
319 auto channels = data.Channels();
320 // channel may be nonzero in the case of a plug-in that only writes
321 // one channel at a time, so multiple instances are made to mix stereo
322 for (size_t ii = channel; ii < channels; ++ii)
323 advancedOutPositions.push_back(outPositions[ii] + outBufferOffset);
324 // When the plug-in expects many output channels, replicate the last
325 // as dummy output
326 advancedOutPositions.resize(size, advancedOutPositions.back());
327
328 processed = instance.ProcessBlock(mSettings,
329 inPositions.data(), advancedOutPositions.data(), curBlockSize);
330 }
331 catch (const AudacityException &) {
332 // PRL: Bug 437:
333 // Pass this along to our application-level handler
334 throw;
335 }
336 catch (...) {
337 // PRL:
338 // Exceptions for other reasons, maybe in third-party code...
339 // Continue treating them as we used to, but I wonder if these
340 // should now be treated the same way.
341 return false;
342 }
343
344 return (processed == curBlockSize);
345}
Base class for exceptions specially processed by the application.
float *const * Positions() const
Get array of positions in the buffers.
virtual unsigned GetAudioInCount() const =0
How many input buffers to allocate at once.
virtual size_t ProcessBlock(EffectSettings &settings, const float *const *inBlock, float *const *outBlock, size_t blockLen)=0
Called for destructive effect computation.
virtual unsigned GetAudioOutCount() const =0
How many output buffers to allocate at once.

References AudioGraph::Buffers::Channels(), EffectInstance::GetAudioInCount(), EffectInstance::GetAudioOutCount(), mInBuffers, mSettings, AudioGraph::Buffers::Positions(), EffectInstance::ProcessBlock(), and size.

Referenced by FetchProcessAndAdvance().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Release()

bool EffectStage::Release ( )
overridevirtual

Caller is done examining last Acquire()d positions.

May be called only after at least one successful call to Acquire()

Returns
success
Postcondition
!Terminates() or Remaining() reduced by what was last returned by Acquire()

Implements AudioGraph::Source.

Definition at line 359 of file EffectStage.cpp.

360{
361 // Progress toward termination (Remaining() == 0),
362 // if mLastProduced + mLastZeroes > 0,
363 // which is what Acquire() last returned
365 assert(mDelayRemaining >= 0);
367 return true;
368}

References mDelayRemaining, mLastProduced, and mLastZeroes.

◆ Remaining()

sampleCount EffectStage::Remaining ( ) const
overridevirtual

Result includes any amount Acquired and not yet Released.

May be undefined before the first successful call to Acquire()

Postcondition
result: result >= 0

Implements AudioGraph::Source.

Definition at line 347 of file EffectStage.cpp.

348{
349 // Not correct until at least one call to Acquire() so that mDelayRemaining
350 // is assigned.
351 // mLastProduced will have the up-front latency discarding deducted.
352 // mDelayRemaining later decreases to 0 as zeroes are supplied to the
353 // processor at the end, compensating for the discarding.
354 return mLastProduced
355 + (mIsProcessor ? mUpstream.Remaining() : 0)
356 + DelayRemaining();
357}

References DelayRemaining(), mIsProcessor, mLastProduced, and mUpstream.

Referenced by Acquire().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ mCleared

bool EffectStage::mCleared { false }
private

Definition at line 100 of file EffectStage.h.

Referenced by FetchProcessAndAdvance().

◆ mDelayRemaining

sampleCount EffectStage::mDelayRemaining
private

Definition at line 96 of file EffectStage.h.

Referenced by Acquire(), FetchProcessAndAdvance(), and Release().

◆ mInBuffers

Buffers& EffectStage::mInBuffers
private
Invariant
mInBuffers.BlockSize() <= mInBuffers.Remaining()

Definition at line 90 of file EffectStage.h.

Referenced by AcceptsBlockSize(), Acquire(), EffectStage(), FetchProcessAndAdvance(), and Process().

◆ mInstances

const std::vector<std::shared_ptr<EffectInstance> > EffectStage::mInstances
private

Definition at line 91 of file EffectStage.h.

Referenced by Acquire(), FetchProcessAndAdvance(), and ~EffectStage().

◆ mIsProcessor

const bool EffectStage::mIsProcessor
private

Definition at line 94 of file EffectStage.h.

Referenced by Acquire(), FetchProcessAndAdvance(), and Remaining().

◆ mLastProduced

size_t EffectStage::mLastProduced {}
private

Definition at line 97 of file EffectStage.h.

Referenced by Acquire(), FetchProcessAndAdvance(), Release(), and Remaining().

◆ mLastZeroes

size_t EffectStage::mLastZeroes {}
private

Definition at line 98 of file EffectStage.h.

Referenced by Acquire(), FetchProcessAndAdvance(), and Release().

◆ mLatencyDone

bool EffectStage::mLatencyDone { false }
private

Definition at line 99 of file EffectStage.h.

Referenced by Acquire().

◆ mSampleRate

const double EffectStage::mSampleRate
private

Definition at line 93 of file EffectStage.h.

Referenced by Acquire().

◆ mSettings

EffectSettings& EffectStage::mSettings
private

Definition at line 92 of file EffectStage.h.

Referenced by Acquire(), and Process().

◆ mUpstream

Source& EffectStage::mUpstream
private

Definition at line 88 of file EffectStage.h.

Referenced by Acquire(), FetchProcessAndAdvance(), and Remaining().


The documentation for this class was generated from the following files: