Skip to content

Commit 99263c0

Browse files
author
Grok Compression
committed
memory back presssure: fix for grk_decompress decompress to disk
1 parent 61ec9f9 commit 99263c0

6 files changed

Lines changed: 90 additions & 8 deletions

File tree

.vscode/launch.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,15 @@
139139
"name": "SPOT6",
140140
"type": "cppdbg",
141141
"request": "launch",
142-
//"program": "/usr/bin/time",
143-
"program": "${workspaceFolder}/build/bin/core_decompress",
142+
"program": "/usr/bin/time",
143+
//"program": "${workspaceFolder}/build/bin/grk_decompress",
144144
"args": [
145-
// "-v",
146-
// "${workspaceFolder}/build/bin/core_decompress",
145+
"-v",
146+
"${workspaceFolder}/build/bin/grk_decompress",
147147
"-i",
148148
"$HOME/temp/IMG_SPOT6_PMS_201305251604372_ORT_816009101_R1C1.JP2",
149-
// "-o",
150-
// "$HOME/temp/spot6.tif"
149+
"-o",
150+
"$HOME/temp/spot6.tif"
151151
],
152152
"cwd": "${workspaceFolder}",
153153
"environment": [

src/lib/codec/apps/GrkDecompress.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,9 +1121,17 @@ int GrkDecompress::preProcess(grk_plugin_decompress_callback_info* info)
11211121
// io_buffer_callback (strip write) doesn't write raw/unprocessed strips
11221122
// during decompress.
11231123
incrementalWriteActive_ = false;
1124+
{
1125+
bool ppNoOp = grk_image_is_post_process_no_op(info->image);
1126+
spdlog::info("Strip path check: storeToDisk={}, single_tile={}, init_decompressors={}, "
1127+
"postProcessNoOp={}, dw_y1={}",
1128+
storeToDisk, parameters->single_tile_decompress,
1129+
(info->init_decompressors_func != nullptr), ppNoOp, parameters->dw_y1);
1130+
}
11241131
if(storeToDisk && !parameters->single_tile_decompress && !info->init_decompressors_func &&
11251132
grk_image_is_post_process_no_op(info->image) && parameters->dw_y1 == 0)
11261133
{
1134+
spdlog::info("Strip-based incremental write path activated");
11271135
if(!writeInit(info))
11281136
goto cleanup;
11291137
// write header early — metadata is already final since postProcess is a no-op

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,18 @@ bool CodeStreamDecompress::decompress(grk_plugin_tile* tile)
288288

289289
if(!ioBandCallback_(band.yBegin, band.yEnd, scratchImage_.get(), ioBandUserData_))
290290
success_ = false;
291+
292+
// Release tile processors for this completed row — compositing is done,
293+
// so tile image data is no longer needed.
294+
for(uint16_t col = 0; col < band.numCols; col++)
295+
{
296+
uint16_t tileIndex = nextBandTileY_ * numTileCols + (band.tileX0 + col);
297+
grklog.info("Releasing tile %u after strip write (row %u)", tileIndex,
298+
(uint32_t)nextBandTileY_);
299+
tileCache_->releaseForSwath(tileIndex);
300+
}
301+
MemoryManager::releaseFreedPages();
302+
291303
pendingBands_.erase(nextBandTileY_);
292304

293305
// Advance strip buffer for the next tile row
@@ -310,6 +322,8 @@ bool CodeStreamDecompress::decompress(grk_plugin_tile* tile)
310322

311323
nextBandTileY_ = nextTileY;
312324
}
325+
// Wake the parser thread so it can schedule more tiles
326+
bandDrainCV_.notify_one();
313327
},
314328
nullptr, tilesToDecompress_.getSlatedTileRect());
315329
}
@@ -825,6 +839,17 @@ bool CodeStreamDecompress::sequentialParseAndSchedule(bool multiTile)
825839
return true;
826840
}
827841

842+
// Backpressure for strip-based band callback: block the parser thread if
843+
// this tile's row is too far ahead of the row currently being drained.
844+
// Without this, all tiles decompress into memory before any row is written.
845+
if(ioBandCallback_ && tileCompletion_)
846+
{
847+
uint16_t numTileCols = tileCompletion_->getNumTileCols();
848+
uint16_t tileY = tileProcessor->getIndex() / numTileCols;
849+
std::unique_lock<std::mutex> lock(bandOrderMutex_);
850+
bandDrainCV_.wait(lock, [this, tileY] { return tileY < nextBandTileY_ + 2 || !success_; });
851+
}
852+
828853
// schedule
829854
return schedule(tileProcessor, multiTile);
830855
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ class CodeStreamDecompress final : public CodeStream, public IDecompressor
412412

413413
// band ordering for incremental writes
414414
std::mutex bandOrderMutex_;
415+
std::condition_variable bandDrainCV_;
415416
uint16_t nextBandTileY_ = 0;
416417
struct PendingBand_
417418
{

src/lib/core/util/GrkImage.cpp

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,11 @@ bool GrkImage::isOpacity(uint16_t compno) const
444444
}
445445
bool GrkImage::isPostProcessNoOp(void) const
446446
{
447-
// applyColour: palette or channel_definition requires processing
448-
if(meta && (meta->color.palette || meta->color.channel_definition))
447+
// applyColour: palette requires processing; channel_definition only blocks
448+
// if it needs component data swaps (type-only metadata is applied early)
449+
if(meta && meta->color.palette)
450+
return false;
451+
if(needsChannelDefinitionSwap())
449452
return false;
450453
// applyColourManagement: ICC profile that must be applied
451454
if(meta && meta->color.icc_profile_buf)
@@ -504,6 +507,11 @@ bool GrkImage::isPostProcessNoOp(void) const
504507
}
505508
void GrkImage::postReadHeader(CodingParams* cp)
506509
{
510+
// Apply channel definition types (e.g. alpha) early so format writers
511+
// can emit correct metadata (TIFF EXTRASAMPLES, etc.) before strip-based
512+
// decompress starts. Component data swaps are deferred to postProcess.
513+
applyChannelDefinitionTypes();
514+
507515
uint8_t prec = comps[0].prec;
508516
if(precision)
509517
prec = precision->prec;
@@ -596,6 +604,44 @@ void GrkImage::allocPalette(uint8_t num_channels, uint16_t num_entries)
596604
((GrkImageMeta*)meta)->allocPalette(num_channels, num_entries);
597605
}
598606

607+
bool GrkImage::needsChannelDefinitionSwap() const
608+
{
609+
if(!meta || !meta->color.channel_definition)
610+
return false;
611+
auto info = meta->color.channel_definition->descriptions;
612+
uint16_t n = meta->color.channel_definition->num_channel_descriptions;
613+
for(uint16_t i = 0; i < n; ++i)
614+
{
615+
if(info[i].typ != GRK_CHANNEL_TYPE_COLOUR)
616+
continue;
617+
if(info[i].asoc == GRK_CHANNEL_ASSOC_WHOLE_IMAGE)
618+
continue;
619+
uint16_t channel = info[i].channel;
620+
uint16_t asoc = info[i].asoc;
621+
if(channel >= numcomps || asoc > numcomps)
622+
continue;
623+
uint16_t asoc_index = (uint16_t)(asoc - 1);
624+
if(channel != asoc_index)
625+
return true;
626+
}
627+
return false;
628+
}
629+
630+
void GrkImage::applyChannelDefinitionTypes()
631+
{
632+
if(!meta || !meta->color.channel_definition)
633+
return;
634+
auto info = meta->color.channel_definition->descriptions;
635+
uint16_t n = meta->color.channel_definition->num_channel_descriptions;
636+
for(uint16_t i = 0; i < n; ++i)
637+
{
638+
uint16_t channel = info[i].channel;
639+
if(channel >= numcomps)
640+
continue;
641+
comps[channel].type = (GRK_CHANNEL_TYPE)info[i].typ;
642+
}
643+
}
644+
599645
void GrkImage::apply_channel_definition()
600646
{
601647
if(channel_definition_applied)

src/lib/core/util/GrkImage.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ class GrkImage : public grk_image
123123

124124
bool check_color(uint16_t signalledNumComps);
125125
void apply_channel_definition(void);
126+
void applyChannelDefinitionTypes(void);
127+
bool needsChannelDefinitionSwap(void) const;
126128
void allocPalette(uint8_t num_channels, uint16_t num_entries);
127129
uint32_t width(void) const;
128130
uint32_t height(void) const;

0 commit comments

Comments
 (0)