Skip to content
This repository was archived by the owner on Oct 31, 2022. It is now read-only.

Commit 6cd7b29

Browse files
committed
Update to current hap.h/.c
Internal support for encoding chunked frames, not currently exposed (all frames are single-chunk).
1 parent 94d449d commit 6cd7b29

File tree

3 files changed

+177
-56
lines changed

3 files changed

+177
-56
lines changed

external/hap/hap.c

+158-51
Original file line numberDiff line numberDiff line change
@@ -227,26 +227,71 @@ static unsigned int hap_texture_format_identifier_for_format_constant(unsigned i
227227
}
228228
}
229229

230-
unsigned long HapMaxEncodedLength(unsigned long inputBytes)
230+
// Returns the length of a decode instructions container of chunk_count chunks
231+
// not including the section header
232+
static size_t hap_decode_instructions_length(unsigned int chunk_count)
231233
{
232234
/*
233-
Actually our max encoded length is inputBytes + 8U but snappy may produce longer output
234-
and the only way we can find out is by trying with a suitably-sized buffer
235+
Calculate the size of our Decode Instructions Section
236+
= Second-Stage Compressor Table + Chunk Size Table + headers for both sections
237+
= chunk_count + (4 * chunk_count) + 4 + 4
235238
*/
236-
unsigned long compressedLength = snappy_max_compressed_length(inputBytes);
237-
if (compressedLength < inputBytes) compressedLength = inputBytes;
238-
return compressedLength + 8U;
239+
size_t length = (5 * chunk_count) + 8;
240+
241+
return length;
242+
}
243+
244+
static unsigned int hap_limited_chunk_count_for_frame(size_t input_bytes, unsigned int texture_format, unsigned int chunk_count)
245+
{
246+
// This is a hard limit due to the 4-byte headers we use for the decode instruction container
247+
// (0xFFFFFF == count + (4 x count) + 20)
248+
if (chunk_count > 3355431)
249+
{
250+
chunk_count = 3355431;
251+
}
252+
// Divide frame equally on DXT block boundries (8 or 16 bytes)
253+
unsigned long dxt_block_count = input_bytes / (texture_format == HapTextureFormat_RGB_DXT1 ? 8 : 16);
254+
while (dxt_block_count % chunk_count != 0) {
255+
chunk_count--;
256+
}
257+
258+
return chunk_count;
259+
}
260+
261+
static size_t hap_max_encoded_length(size_t input_bytes, unsigned int texture_format, unsigned int compressor, unsigned int chunk_count)
262+
{
263+
size_t decode_instructions_length, max_compressed_length;
264+
265+
chunk_count = hap_limited_chunk_count_for_frame(input_bytes, texture_format, chunk_count);
266+
267+
decode_instructions_length = hap_decode_instructions_length(chunk_count);
268+
269+
if (compressor == HapCompressorSnappy)
270+
{
271+
size_t chunk_size = input_bytes / chunk_count;
272+
max_compressed_length = snappy_max_compressed_length(chunk_size) * chunk_count;
273+
}
274+
else
275+
{
276+
max_compressed_length = input_bytes;
277+
}
278+
279+
// top section header + decode instructions section header + decode instructions + compressed data
280+
return max_compressed_length + 8U + decode_instructions_length + 4U;
281+
}
282+
283+
unsigned long HapMaxEncodedLength(unsigned long inputBytes, unsigned int textureFormat, unsigned int chunkCount)
284+
{
285+
// Assume snappy, the worst case
286+
return hap_max_encoded_length(inputBytes, textureFormat, HapCompressorSnappy, chunkCount);
239287
}
240288

241289
unsigned int HapEncode(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int textureFormat,
242-
unsigned int compressor, void *outputBuffer, unsigned long outputBufferBytes,
243-
unsigned long *outputBufferBytesUsed)
290+
unsigned int compressor, unsigned int chunkCount, void *outputBuffer,
291+
unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed)
244292
{
245-
size_t maxCompressedLength;
246-
size_t maxOutputBufferLength;
247-
size_t headerLength;
248-
void *compressedStart;
249-
size_t storedLength;
293+
size_t top_section_header_length;
294+
size_t top_section_length;
250295
unsigned int storedCompressor;
251296
unsigned int storedFormat;
252297

@@ -262,16 +307,14 @@ unsigned int HapEncode(const void *inputBuffer, unsigned long inputBufferBytes,
262307
|| (compressor != HapCompressorNone
263308
&& compressor != HapCompressorSnappy
264309
)
310+
|| outputBuffer == NULL
265311
)
266312
{
267313
return HapResult_Bad_Arguments;
268314
}
269-
270-
maxCompressedLength = compressor == HapCompressorSnappy ? snappy_max_compressed_length(inputBufferBytes) : inputBufferBytes;
271-
if (maxCompressedLength < inputBufferBytes)
315+
else if (outputBufferBytes < hap_max_encoded_length(inputBufferBytes, textureFormat, compressor, chunkCount))
272316
{
273-
// Sanity check in case a future Snappy promises to always compress
274-
maxCompressedLength = inputBufferBytes;
317+
return HapResult_Buffer_Too_Small;
275318
}
276319

277320
/*
@@ -283,56 +326,110 @@ unsigned int HapEncode(const void *inputBuffer, unsigned long inputBufferBytes,
283326
*/
284327
if (inputBufferBytes > kHapUInt24Max)
285328
{
286-
headerLength = 8U;
329+
top_section_header_length = 8U;
287330
}
288331
else
289332
{
290-
headerLength = 4U;
291-
}
292-
293-
maxOutputBufferLength = maxCompressedLength + headerLength;
294-
if (outputBufferBytes < maxOutputBufferLength
295-
|| outputBuffer == NULL)
296-
{
297-
return HapResult_Buffer_Too_Small;
333+
top_section_header_length = 4U;
298334
}
299-
compressedStart = ((uint8_t *)outputBuffer) + headerLength;
300-
335+
301336
if (compressor == HapCompressorSnappy)
302337
{
303-
snappy_status result;
304-
storedLength = outputBufferBytes;
305-
result = snappy_compress((const char *)inputBuffer, inputBufferBytes, (char *)compressedStart, &storedLength);
306-
if (result != SNAPPY_OK)
338+
/*
339+
We attempt to chunk as requested, and if resulting frame is larger than it is uncompressed then
340+
store frame uncompressed
341+
*/
342+
343+
size_t decode_instructions_length;
344+
size_t chunk_size, compress_buffer_remaining;
345+
uint8_t *second_stage_compressor_table;
346+
void *chunk_size_table;
347+
char *compressed_data;
348+
unsigned int i;
349+
350+
chunkCount = hap_limited_chunk_count_for_frame(inputBufferBytes, textureFormat, chunkCount);
351+
decode_instructions_length = hap_decode_instructions_length(chunkCount);
352+
353+
// Check we have space for the Decode Instructions Container
354+
if ((inputBufferBytes + decode_instructions_length + 4) > kHapUInt24Max)
307355
{
308-
return HapResult_Internal_Error;
356+
top_section_header_length = 8U;
357+
}
358+
359+
second_stage_compressor_table = ((uint8_t *)outputBuffer) + top_section_header_length + 4 + 4;
360+
chunk_size_table = ((uint8_t *)outputBuffer) + top_section_header_length + 4 + 4 + chunkCount + 4;
361+
362+
chunk_size = inputBufferBytes / chunkCount;
363+
364+
// write the Decode Instructions section header
365+
hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length, 4U, decode_instructions_length, kHapSectionDecodeInstructionsContainer);
366+
// write the Second Stage Compressor Table section header
367+
hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length + 4U, 4U, chunkCount, kHapSectionChunkSecondStageCompressorTable);
368+
// write the Chunk Size Table section header
369+
hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length + 4U + 4U + chunkCount, 4U, chunkCount * 4U, kHapSectionChunkSizeTable);
370+
371+
compressed_data = (char *)(((uint8_t *)outputBuffer) + top_section_header_length + 4 + decode_instructions_length);
372+
373+
compress_buffer_remaining = outputBufferBytes - top_section_header_length - 4 - decode_instructions_length;
374+
375+
top_section_length = 4 + decode_instructions_length;
376+
377+
for (i = 0; i < chunkCount; i++) {
378+
size_t chunk_packed_length = compress_buffer_remaining;
379+
const char *chunk_input_start = (const char *)(((uint8_t *)inputBuffer) + (chunk_size * i));
380+
if (compressor == HapCompressorSnappy)
381+
{
382+
snappy_status result = snappy_compress(chunk_input_start, chunk_size, (char *)compressed_data, &chunk_packed_length);
383+
if (result != SNAPPY_OK)
384+
{
385+
return HapResult_Internal_Error;
386+
}
387+
}
388+
389+
if (compressor == HapCompressorNone || chunk_packed_length >= chunk_size)
390+
{
391+
// store the chunk uncompressed
392+
memcpy(compressed_data, chunk_input_start, chunk_size);
393+
chunk_packed_length = chunk_size;
394+
second_stage_compressor_table[i] = kHapCompressorNone;
395+
}
396+
else
397+
{
398+
// ie we used snappy and saved some space
399+
second_stage_compressor_table[i] = kHapCompressorSnappy;
400+
}
401+
hap_write_4_byte_uint(((uint8_t *)chunk_size_table) + (i * 4), chunk_packed_length);
402+
compressed_data += chunk_packed_length;
403+
top_section_length += chunk_packed_length;
404+
compress_buffer_remaining -= chunk_packed_length;
405+
}
406+
407+
if (top_section_length < inputBufferBytes + top_section_header_length)
408+
{
409+
// use the complex storage because snappy compression saved space
410+
storedCompressor = kHapCompressorComplex;
411+
}
412+
else
413+
{
414+
// Signal to store the frame uncompressed
415+
compressor = HapCompressorNone;
309416
}
310-
storedCompressor = kHapCompressorSnappy;
311-
}
312-
else
313-
{
314-
// HapCompressorNone
315-
// Setting storedLength to 0 causes the frame to be used uncompressed
316-
storedLength = 0;
317417
}
318-
319-
/*
320-
If our "compressed" frame is no smaller than our input frame then store the input uncompressed.
321-
*/
322-
if (storedLength == 0 || storedLength >= inputBufferBytes)
418+
419+
if (compressor == HapCompressorNone)
323420
{
324-
memcpy(compressedStart, inputBuffer, inputBufferBytes);
325-
storedLength = inputBufferBytes;
421+
memcpy(((uint8_t *)outputBuffer) + top_section_header_length, inputBuffer, inputBufferBytes);
422+
top_section_length = inputBufferBytes;
326423
storedCompressor = kHapCompressorNone;
327424
}
328425

329426
storedFormat = hap_texture_format_identifier_for_format_constant(textureFormat);
330427

331-
hap_write_section_header(outputBuffer, headerLength, storedLength, hap_4_bit_packed_byte(storedCompressor, storedFormat));
428+
hap_write_section_header(outputBuffer, top_section_header_length, top_section_length, hap_4_bit_packed_byte(storedCompressor, storedFormat));
332429

333430
if (outputBufferBytesUsed != NULL)
334431
{
335-
*outputBufferBytesUsed = storedLength + headerLength;
432+
*outputBufferBytesUsed = top_section_length + top_section_header_length;
336433
}
337434

338435
return HapResult_No_Error;
@@ -591,7 +688,17 @@ unsigned int HapDecode(const void *inputBuffer, unsigned long inputBufferBytes,
591688
*/
592689
bytesUsed = running_uncompressed_chunk_size;
593690

594-
callback((HapDecodeWorkFunction)hap_decode_chunk, chunk_info, chunk_count, info);
691+
if (chunk_count == 1)
692+
{
693+
/*
694+
We don't invoke the callback for one chunk, just decode it directly
695+
*/
696+
hap_decode_chunk(chunk_info, 0);
697+
}
698+
else
699+
{
700+
callback((HapDecodeWorkFunction)hap_decode_chunk, chunk_info, chunk_count, info);
701+
}
595702

596703
/*
597704
Check to see if we encountered any errors and report one of them

external/hap/hap.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,18 @@ typedef void (*HapDecodeCallback)(HapDecodeWorkFunction function, void *p, unsig
6464
/*
6565
Returns the maximum size of an output buffer for an input buffer of inputBytes length.
6666
*/
67-
unsigned long HapMaxEncodedLength(unsigned long inputBytes);
67+
unsigned long HapMaxEncodedLength(unsigned long inputBytes, unsigned int textureFormat, unsigned int chunkCount);
6868

6969
/*
7070
Encodes inputBuffer which is a buffer containing texture data of format textureFormat, or returns an error.
7171
Use HapMaxEncodedLength() to discover the minimal value for outputBufferBytes.
72+
The frame will be compressed using compressor in chunkCount chunks to permit multithreaded decoding.
7273
If this returns HapResult_No_Error and outputBufferBytesUsed is not NULL then outputBufferBytesUsed will be set
7374
to the actual encoded length of the output buffer.
7475
*/
7576
unsigned int HapEncode(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int textureFormat,
76-
unsigned int compressor, void *outputBuffer, unsigned long outputBufferBytes,
77-
unsigned long *outputBufferBytesUsed);
77+
unsigned int compressor, unsigned int chunkCount, void *outputBuffer,
78+
unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed);
7879

7980
/*
8081
Decodes inputBuffer which is a Hap frame.

source/HapCompressor.c

+15-2
Original file line numberDiff line numberDiff line change
@@ -389,10 +389,22 @@ Hap_CGetMaxCompressionSize(
389389
long * size)
390390
{
391391
int dxtSize;
392+
unsigned int textureType;
392393
if( ! size )
393394
return paramErr;
395+
switch (glob->type) {
396+
case kHapAlphaCodecSubType:
397+
textureType = HapTextureFormat_RGBA_DXT5;
398+
break;
399+
case kHapYCoCgCodecSubType:
400+
textureType = HapTextureFormat_YCoCg_DXT5;
401+
break;
402+
default:
403+
textureType = HapTextureFormat_RGB_DXT1;
404+
break;
405+
}
394406
dxtSize = dxtBytesForDimensions(srcRect->right - srcRect->left, srcRect->bottom - srcRect->top, glob->type);
395-
*size = HapMaxEncodedLength(dxtSize);
407+
*size = HapMaxEncodedLength(dxtSize, textureType, 1);
396408

397409
return noErr;
398410
}
@@ -596,7 +608,7 @@ Hap_CPrepareToCompressFrames(
596608
// Work out the upper bound on encoded frame data size -- we'll allocate buffers of this size.
597609
wantedDXTSize = dxtBytesForDimensions(glob->width, glob->height, glob->type);
598610

599-
glob->maxEncodedDataSize = HapMaxEncodedLength(wantedDXTSize);
611+
glob->maxEncodedDataSize = HapMaxEncodedLength(wantedDXTSize, glob->dxtFormat, 1);
600612

601613
if (glob->compressTaskPool == NULL)
602614
{
@@ -732,6 +744,7 @@ static void Background_Encode(void *info)
732744
codec_src_length,
733745
glob->dxtFormat,
734746
HapCompressorSnappy,
747+
1,
735748
task->encodedFrameDataPtr,
736749
glob->maxEncodedDataSize,
737750
&(task->encodedFrameActualSize));

0 commit comments

Comments
 (0)