@@ -59,8 +59,10 @@ struct ITileProcessor;
5959namespace grk
6060{
6161CompressScheduler::CompressScheduler (Tile* tile, bool needsRateControl, TileCodingParams* tcp,
62- const double * mct_norms, uint16_t mct_numcomps)
62+ const double * mct_norms, uint16_t mct_numcomps,
63+ bool progressiveRateControl)
6364 : SchedulerStandard(tile->numcomps_), tile_(tile), needsRateControl_(needsRateControl),
65+ progressiveRateControl_ (progressiveRateControl),
6466 blockCount_(-1 ), tcp_(tcp), mct_norms_(mct_norms), mct_numcomps_(mct_numcomps)
6567{
6668 rateControlStats_.init (tile->numcomps_ );
@@ -135,6 +137,11 @@ bool CompressScheduler::scheduleT1(ITileProcessor* proc)
135137 encodeBlocks_ = blocks;
136138 const size_t maxBlocks = blocks.size ();
137139
140+ // Initialize progressive slope estimator when rate control is active.
141+ // The estimator needs: total samples (sum of all block areas) and target rate (bytes/sample).
142+ // This enables early termination of coding passes predicted to be discarded by PCRD.
143+ initSlopeEstimator (blocks);
144+
138145 tf::Taskflow taskflow;
139146 size_t num_threads = TFSingleton::num_threads ();
140147 auto node = new tf::Task[num_threads];
@@ -223,6 +230,9 @@ bool CompressScheduler::populateT1Flow(FlowComponent* flow)
223230 encodeBlocks_ = blocks;
224231 const size_t maxBlocks = blocks.size ();
225232
233+ // Initialize progressive slope estimator (same as scheduleT1 path)
234+ initSlopeEstimator (blocks);
235+
226236 size_t num_threads = TFSingleton::num_threads ();
227237 for (auto i = 0U ; i < num_threads; i++)
228238 {
@@ -244,6 +254,12 @@ bool CompressScheduler::compress(size_t workerId, uint64_t maxBlocks)
244254 if (index >= maxBlocks)
245255 return false ;
246256 auto block = encodeBlocks_[index];
257+
258+ // Set the early-stop threshold from the progressive estimator.
259+ // This is a lock-free atomic read — the estimator updates it as blocks complete.
260+ if (slopeEstimator_)
261+ block->earlyStopSlope = slopeEstimator_->getEarlyStopSlope ();
262+
247263 compress (coder, block);
248264 delete block;
249265
@@ -270,6 +286,24 @@ void CompressScheduler::compress(t1::ICoder* coder, t1::CompressBlockExec* block
270286 if (cblk->getNumPasses () > 0 )
271287 RateControl::convexHull (cblk->getPass (0 ), cblk->getNumPasses ());
272288
289+ // Feed completed block data to the progressive slope estimator.
290+ // This builds the slope-rate histogram used to predict the PCRD threshold.
291+ if (slopeEstimator_ && cblk->getNumPasses () > 0 )
292+ {
293+ // Extract slopes and rates into stack arrays for the estimator.
294+ // Maximum coding passes per block: 3 * maxBitPlanes ≈ 3*16 = 48.
295+ uint8_t numPasses = cblk->getNumPasses ();
296+ uint16_t slopes[48 ];
297+ uint16_t rates[48 ];
298+ for (uint8_t p = 0 ; p < numPasses; p++)
299+ {
300+ auto pass = cblk->getPass (p);
301+ slopes[p] = pass->slope_ ;
302+ rates[p] = pass->rate_ ;
303+ }
304+ slopeEstimator_->updateStats (slopes, rates, numPasses, num_pix);
305+ }
306+
273307 // Collect slope stats for both bisect algorithms
274308 for (uint8_t passno = 0 ; passno < cblk->getNumPasses (); passno++)
275309 {
@@ -305,4 +339,45 @@ void CompressScheduler::compress(t1::ICoder* coder, t1::CompressBlockExec* block
305339 }
306340}
307341
342+ void CompressScheduler::initSlopeEstimator (
343+ const std::vector<t1::CompressBlockExec*>& blocks)
344+ {
345+ // The progressive slope estimator requires:
346+ // 1. A target rate is specified (rate-distortion mode)
347+ // 2. Rate control is needed (not lossless, not zero-rate)
348+ //
349+ // For multi-layer encoding, we must use the HIGHEST quality layer's target
350+ // (the largest byte budget). This is the most permissive threshold — it
351+ // determines the minimum slope below which passes are DEFINITELY discarded
352+ // by ALL layers. Using a smaller layer's target would over-estimate the
353+ // threshold and incorrectly terminate passes needed by higher-quality layers.
354+ //
355+ // tcp_->rates_[k] contains target byte counts after conversion from
356+ // compression ratios. Higher indices = higher quality = more bytes.
357+ if (!needsRateControl_)
358+ return ;
359+ if (!progressiveRateControl_)
360+ return ;
361+
362+ // Find the maximum target rate across all layers
363+ double maxTargetBytes = 0.0 ;
364+ for (uint16_t k = 0 ; k < tcp_->numLayers_ ; ++k)
365+ {
366+ if (tcp_->rates_ [k] > maxTargetBytes)
367+ maxTargetBytes = tcp_->rates_ [k];
368+ }
369+ if (maxTargetBytes <= 0.0 )
370+ return ;
371+
372+ uint64_t totalSamples = 0 ;
373+ for (const auto * blk : blocks)
374+ totalSamples += static_cast <uint64_t >(blk->cblk ->width ()) * blk->cblk ->height ();
375+
376+ if (totalSamples == 0 )
377+ return ;
378+
379+ double targetRate = maxTargetBytes / static_cast <double >(totalSamples);
380+ slopeEstimator_ = std::make_unique<ProgressiveSlopeEstimator>(totalSamples, targetRate);
381+ }
382+
308383} // namespace grk
0 commit comments