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, Source &upstream, Buffers &inBuffers, const Factory &factory, EffectSettings &settings, double sampleRate, std::optional< sampleCount > genLength, const WideSampleSequence &sequence)
 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, Source &upstream, Buffers &inBuffers, const Factory &factory, EffectSettings &settings, double sampleRate, std::optional< sampleCount > genLength, const WideSampleSequence &sequence)
 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,
Source &  upstream,
Buffers inBuffers,
const Factory factory,
EffectSettings settings,
double  sampleRate,
std::optional< sampleCount genLength,
const WideSampleSequence sequence 
)

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 60 of file EffectStage.cpp.

65 : mUpstream{ upstream }, mInBuffers{ inBuffers }
67 genLength, channel) }
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:47
void Rewind()
Reset positions to starts of buffers.
Source & mUpstream
Definition: EffectStage.h:89
bool AcceptsBlockSize(size_t size) const override
See postcondition of constructor.
const std::vector< std::shared_ptr< EffectInstance > > mInstances
Definition: EffectStage.h:92
const double mSampleRate
Definition: EffectStage.h:94
sampleCount mDelayRemaining
Definition: EffectStage.h:97
Buffers & mInBuffers
Definition: EffectStage.h:91
const bool mIsProcessor
Definition: EffectStage.h:95
EffectSettings & mSettings
Definition: EffectStage.h:93
static sampleCount max()
Definition: SampleCount.h:69
std::vector< std::shared_ptr< EffectInstance > > MakeInstances(const EffectStage::Factory &factory, EffectSettings &settings, double sampleRate, const WideSampleSequence &sequence, std::optional< sampleCount > genLength, int channel)
Definition: EffectStage.cpp:21

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 96 of file EffectStage.cpp.

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

References mInstances.

Member Function Documentation

◆ AcceptsBlockSize()

bool EffectStage::AcceptsBlockSize ( size_t  size) const
overridevirtual

See postcondition of constructor.

Implements AudioGraph::Source.

Definition at line 109 of file EffectStage.cpp.

110{
111 // Test the equality of input and output block sizes
112 return mInBuffers.BlockSize() == size;
113}
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 104 of file EffectStage.cpp.

105{
106 return true;
107}

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 115 of file EffectStage.cpp.

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

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,
Source &  upstream,
Buffers inBuffers,
const Factory factory,
EffectSettings settings,
double  sampleRate,
std::optional< sampleCount genLength,
const WideSampleSequence sequence 
)
static

Satisfies postcondition of constructor or returns null.

Definition at line 79 of file EffectStage.cpp.

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

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

Referenced by PerTrackEffect::ProcessTrack().

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 73 of file EffectStage.h.

74 { 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 227 of file EffectStage.cpp.

229{
230 std::optional<size_t> oCurBlockSize;
231 // Generator always supplies zeroes in
232 doZeroes = doZeroes || !mIsProcessor;
233 if (!doZeroes)
234 oCurBlockSize = mUpstream.Acquire(mInBuffers, bound);
235 else {
236 if (!mCleared) {
237 // Need to do this the first time, only, that we begin to give zeroes
238 // to the processor
240 const auto blockSize = mInBuffers.BlockSize();
241 for (size_t ii = 0; ii < mInBuffers.Channels(); ++ii) {
242 auto p = &mInBuffers.GetWritePosition(ii);
243 std::fill(p, p + blockSize, 0);
244 }
245 mCleared = true;
246 }
247 oCurBlockSize = {
249 if (!mIsProcessor)
250 // Do this (ignoring result) so we can correctly Release() upstream
251 mUpstream.Acquire(mInBuffers, bound);
252 }
253 if (!oCurBlockSize)
254 return {};
255
256 const auto curBlockSize = *oCurBlockSize;
257 if (curBlockSize == 0)
258 assert(doZeroes || mUpstream.Remaining() == 0); // post of Acquire()
259 else {
260 // Called only in Acquire()
261 // invariant or post of mUpstream.Acquire() satisfies pre of Process()
262 // because curBlockSize <= bound <= mInBuffers.blockSize()
263 // == data.BlockSize()
264 // and mInBuffers.BlockSize() <= mInBuffers.Remaining() by invariant
265 // and data.BlockSize() <= data.Remaining() by pre of Acquire()
266 for (size_t ii = 0, nn = mInstances.size(); ii < nn; ++ii) {
267 auto &pInstance = mInstances[ii];
268 if (!pInstance)
269 continue;
270 if (!Process(*pInstance, ii, data, curBlockSize, outBufferOffset))
271 return {};
272 }
273
274 if (doZeroes) {
275 // Either a generator or doing the tail; will count down delay
277 if (!mIsProcessor) {
278 // This allows polling the progress meter for a generator
279 if (!mUpstream.Release())
280 return {};
281 }
282 }
283 else {
284 // Will count down the upstream
285 mLastProduced += curBlockSize;
286 if (!mUpstream.Release())
287 return {};
288 mInBuffers.Advance(curBlockSize);
290 // Maintain invariant minimum availability
292 }
293 }
294 return oCurBlockSize;
295}
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:73

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 297 of file EffectStage.cpp.

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

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

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 349 of file EffectStage.cpp.

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

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 101 of file EffectStage.h.

Referenced by FetchProcessAndAdvance().

◆ mDelayRemaining

sampleCount EffectStage::mDelayRemaining
private

Definition at line 97 of file EffectStage.h.

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

◆ mInBuffers

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

Definition at line 91 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 92 of file EffectStage.h.

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

◆ mIsProcessor

const bool EffectStage::mIsProcessor
private

Definition at line 95 of file EffectStage.h.

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

◆ mLastProduced

size_t EffectStage::mLastProduced {}
private

Definition at line 98 of file EffectStage.h.

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

◆ mLastZeroes

size_t EffectStage::mLastZeroes {}
private

Definition at line 99 of file EffectStage.h.

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

◆ mLatencyDone

bool EffectStage::mLatencyDone { false }
private

Definition at line 100 of file EffectStage.h.

Referenced by Acquire().

◆ mSampleRate

const double EffectStage::mSampleRate
private

Definition at line 94 of file EffectStage.h.

Referenced by Acquire().

◆ mSettings

EffectSettings& EffectStage::mSettings
private

Definition at line 93 of file EffectStage.h.

Referenced by Acquire(), and Process().

◆ mUpstream

Source& EffectStage::mUpstream
private

Definition at line 89 of file EffectStage.h.

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


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