Skip to content

Commit 244e14a

Browse files
author
Grok Compression
committed
support multiple decompressions from a single codec instance
1 parent 5821846 commit 244e14a

11 files changed

Lines changed: 378 additions & 16 deletions

src/lib/core/codestream/decompress/CodeStreamDecompress.cpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,11 @@ bool CodeStreamDecompress::decompress(grk_plugin_tile* tile)
163163
for(auto tileIndex : slated)
164164
{
165165
auto cacheEntry = tileCache_->get(tileIndex);
166-
if(cacheEntry && cacheEntry->processor->getImage() && !cacheEntry->dirty_)
166+
if(!cacheEntry)
167+
continue;
168+
auto proc = cacheEntry->processor;
169+
if(proc->isBestEffortDecompressed() ||
170+
(proc->getImage() && (!cacheEntry->dirty_ || proc->allSOTMarkersParsed())))
167171
tileCompletion_->complete(tileIndex);
168172
}
169173
}
@@ -183,7 +187,10 @@ bool CodeStreamDecompress::decompressImpl(std::set<uint16_t> slated)
183187
// Filter out fully cached tiles from slated
184188
std::erase_if(slated, [this](uint16_t index) {
185189
auto cacheEntry = tileCache_->get(index);
186-
return cacheEntry && cacheEntry->processor->getImage() && !cacheEntry->dirty_;
190+
if(!cacheEntry)
191+
return false;
192+
auto proc = cacheEntry->processor;
193+
return proc->isBestEffortDecompressed() || (proc->getImage() && !cacheEntry->dirty_);
187194
});
188195
if(slated.empty())
189196
return true;
@@ -385,6 +392,7 @@ void CodeStreamDecompress::decompressSequentialPrepare(void)
385392
{
386393
stream_->seek(markerCache_->getTileStreamStart() + MARKER_BYTES);
387394
markerParser_.setSOT();
395+
tileCache_->resetSOTParsing();
388396
if(cp_.plmMarkers_)
389397
cp_.plmMarkers_->rewind();
390398
stream_->memAdvise(stream_->tell(), 0, GrkAccessPattern::ACCESS_RANDOM);
@@ -400,6 +408,11 @@ void CodeStreamDecompress::decompressSequential(void)
400408
{
401409
if(!sequentialParseAndSchedule(true))
402410
{
411+
// If we've exhausted all slated tiles' markers or the codestream
412+
// ran out of data (truncated image), stop gracefully.
413+
if(tileCache_->allSlatedSOTMarkersParsed(tilesToDecompress_.getSlatedTiles()) ||
414+
markerParser_.endOfCodeStream() || stream_->numBytesLeft() == 0)
415+
break;
403416
success_ = false;
404417
break;
405418
}
@@ -443,6 +456,19 @@ void CodeStreamDecompress::decompressSequential(void)
443456
if(tileCache_->allSlatedSOTMarkersParsed(tilesToDecompress_.getSlatedTiles()))
444457
break;
445458
}
459+
460+
// Mark tiles that were never parsed (missing from truncated codestream)
461+
// as complete so wait() doesn't hang. Tiles that WERE parsed are still
462+
// being decompressed asynchronously and must not be completed here.
463+
if(tileCompletion_)
464+
{
465+
for(auto tileIndex : tilesToDecompress_.getSlatedTiles())
466+
{
467+
auto cached = tileCache_->get(tileIndex);
468+
if(!cached || !cached->processor || !cached->processor->allSOTMarkersParsed())
469+
tileCompletion_->complete(tileIndex);
470+
}
471+
}
446472
}
447473

448474
bool CodeStreamDecompress::sequentialParseAndSchedule(bool multiTile)
@@ -468,7 +494,7 @@ bool CodeStreamDecompress::sequentialParseAndSchedule(bool multiTile)
468494
if(!processed)
469495
return false;
470496
}
471-
catch(const CorruptSOTMarkerException& csme)
497+
catch([[maybe_unused]] const CorruptSOTMarkerException& csme)
472498
{
473499
return false;
474500
}
@@ -832,6 +858,7 @@ std::function<void()> CodeStreamDecompress::postMultiTile(ITileProcessor* tilePr
832858
return;
833859
}
834860
tileProcessor->post_decompressT2T1(scratchImage_.get());
861+
tileProcessor->setBestEffortDecompressed();
835862
numTilesDecompressed_++;
836863
auto tileImage = tileProcessor->getImage();
837864
if(!cp_.codingParams_.dec_.skipAllocateComposite_ && scratchImage_->has_multiple_tiles &&

src/lib/core/codestream/decompress/CodeStreamDecompress_ReadMarkers.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ void CodeStreamDecompress::postReadHeader(void)
188188
// set up tile completion based on tile and image bounds
189189
if(headerRead_)
190190
{
191+
setDecompressRegion(RectD(cp_.dw_x0, cp_.dw_y0, cp_.dw_x1, cp_.dw_y1));
192+
191193
if(cp_.asynchronous_ && cp_.simulate_synchronous_)
192194
{
193195
auto bounds = headerImage_->getBounds();
@@ -198,8 +200,6 @@ void CodeStreamDecompress::postReadHeader(void)
198200
},
199201
[this]() { scheduleTileBatch(); }, tilesToDecompress_.getSlatedTileRect());
200202
}
201-
202-
setDecompressRegion(RectD(cp_.dw_x0, cp_.dw_y0, cp_.dw_x1, cp_.dw_y1));
203203
}
204204
}
205205

@@ -370,6 +370,15 @@ bool CodeStreamDecompress::readSOT(uint8_t* headerData, uint16_t headerSize)
370370
: true;
371371
}
372372

373+
auto cached = tileCache_->get(tileIndex);
374+
if(cached && cached->processor && cached->processor->isBestEffortDecompressed())
375+
{
376+
currTileIndex_ = -1;
377+
return currTilePartInfo_.tilePartLength_
378+
? stream_->skip((int64_t)(currTilePartInfo_.tilePartLength_ - sotMarkerSegmentLen))
379+
: true;
380+
}
381+
373382
auto processor = getTileProcessor(tileIndex);
374383
if(!processor->readSOT(this->stream_, headerData, headerSize, currTilePartInfo_, false))
375384
return false;

src/lib/core/codestream/markers/MarkerParser.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ uint16_t MarkerParser::currId(void)
8989
void MarkerParser::setSOT(void)
9090
{
9191
currMarkerId_ = SOT;
92+
foundEOC_ = false;
9293
}
9394

9495
bool MarkerParser::readSOTorEOC(void)

src/lib/core/tile_processor/ITileProcessor.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,11 @@ struct ITileProcessor
310310
*/
311311
virtual bool allSOTMarkersParsed() = 0;
312312

313+
/**
314+
* @brief Reset SOT parsing state so the tile can be re-parsed from the codestream.
315+
*/
316+
virtual void resetSOTParsing() = 0;
317+
313318
/**
314319
* @brief Sets the processor to truncated state if not all tile parts are parsed
315320
*/
@@ -326,6 +331,13 @@ struct ITileProcessor
326331
* @return true if initialized, false otherwise
327332
*/
328333
virtual bool isInitialized(void) = 0;
334+
335+
/**
336+
* @brief Check if tile was decompressed on a best-effort basis (may have been truncated or
337+
* errored). Such tiles should not be re-decompressed on codec reuse.
338+
*/
339+
virtual bool isBestEffortDecompressed(void) = 0;
340+
virtual void setBestEffortDecompressed(void) = 0;
329341
};
330342

331343
} // namespace grk

src/lib/core/tile_processor/TileCache.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,12 @@ class TileCache
128128
if(!cache_[tile_index])
129129
{
130130
cache_[tile_index] = new TileCacheEntry(processor);
131-
grklog.debug("Added ITileProcessor at tile index %u, address %p", tile_index, processor);
132131
}
133132
else
134133
{
135134
if(cache_[tile_index]->processor)
136-
{
137-
grklog.debug("Removing ITileProcessor at tile index %u, address %p", tile_index,
138-
cache_[tile_index]->processor);
139-
delete cache_[tile_index]->processor; // Delete old processor
140-
}
135+
delete cache_[tile_index]->processor;
141136
cache_[tile_index]->processor = processor;
142-
grklog.debug("Added ITileProcessor at tile index %u, address %p", tile_index, processor);
143137
}
144138
return cache_[tile_index];
145139
}
@@ -153,12 +147,18 @@ class TileCache
153147

154148
void release(uint16_t tileIndex)
155149
{
156-
// Release the tile processor if it exists
157150
if(cache_[tileIndex] && cache_[tileIndex]->processor)
158-
{
159-
grklog.debug("Releasing ITileProcessor at tile index %u, address %p", tileIndex,
160-
cache_[tileIndex]->processor);
161151
cache_[tileIndex]->processor->release(GRK_TILE_CACHE_NONE);
152+
}
153+
154+
void resetSOTParsing()
155+
{
156+
for(auto* entry : cache_)
157+
{
158+
if(!entry || !entry->processor)
159+
continue;
160+
if(!entry->processor->isBestEffortDecompressed())
161+
entry->processor->resetSOTParsing();
162162
}
163163
}
164164

src/lib/core/tile_processor/TileProcessor.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,22 @@ bool TileProcessor::isInitialized(void)
245245
return initialized_;
246246
}
247247

248+
bool TileProcessor::isBestEffortDecompressed(void)
249+
{
250+
return bestEffortDecompressed_;
251+
}
252+
253+
void TileProcessor::setBestEffortDecompressed(void)
254+
{
255+
bestEffortDecompressed_ = true;
256+
}
257+
258+
void TileProcessor::resetSOTParsing()
259+
{
260+
numSOTsParsed_ = 0;
261+
tcp_->tilePartCounter_ = 0;
262+
}
263+
248264
bool TileProcessor::init(void)
249265
{
250266
if(!tile_)

src/lib/core/tile_processor/TileProcessor.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,10 @@ struct TileProcessor : virtual public ITileProcessor
336336

337337
bool isInitialized(void) override;
338338

339+
bool isBestEffortDecompressed(void) override;
340+
void setBestEffortDecompressed(void) override;
341+
void resetSOTParsing() override;
342+
339343
protected:
340344
/**
341345
* @brief header @ref GrkImage
@@ -467,6 +471,12 @@ struct TileProcessor : virtual public ITileProcessor
467471
*/
468472
bool truncated_ = false;
469473

474+
/**
475+
* @brief true if tile was decompressed on a best-effort basis
476+
* (may have been truncated or errored). Not re-decompressed on codec reuse.
477+
*/
478+
bool bestEffortDecompressed_ = false;
479+
470480
/**
471481
* @brief @ref GrkImage for this tile
472482
*

tests/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ target_link_libraries(compare_dump_files ${GROK_CORE_NAME} spdlog::spdlog)
3232
add_executable(j2k_random_tile_access j2k_random_tile_access.cpp GrkRandomTileAccess.cpp)
3333
target_link_libraries(j2k_random_tile_access ${GROK_CORE_NAME} spdlog::spdlog)
3434

35+
add_executable(j2k_multi_region_decompress j2k_multi_region_decompress.cpp GrkMultiRegionDecompress.cpp)
36+
target_link_libraries(j2k_multi_region_decompress ${GROK_CORE_NAME} spdlog::spdlog)
37+
3538
add_executable(compare_raw_files compare_raw_files.cpp GrkCompareRawFiles.cpp)
3639
target_link_libraries(compare_raw_files ${GROK_CORE_NAME} spdlog::spdlog)
3740

0 commit comments

Comments
 (0)