diff --git a/Source/astcenc.h b/Source/astcenc.h index 9affff8d94ebf29716a8194030db188e2dc258fa..56540130307f4123e81e206b098ab1df872b7cce 100644 --- a/Source/astcenc.h +++ b/Source/astcenc.h @@ -162,6 +162,17 @@ #include #include +#define QUALITY_CONTROL (1) +#if QUALITY_CONTROL +enum ComponentRGBA { + R_COM = 0, + G_COM, + B_COM, + A_COM, + RGBA_COM +}; +#endif + #if defined(ASTCENC_DYNAMIC_LIBRARY) #if defined(_MSC_VER) #define ASTCENC_PUBLIC extern "C" __declspec(dllexport) @@ -213,6 +224,9 @@ enum astcenc_error { /** @brief The call failed due to an issue with diagnostic tracing. */ ASTCENC_ERR_DTRACE_FAILURE, #endif +#if QUALITY_CONTROL + ASTCENC_ERR_BAD_QUALITY_CHECK +#endif }; /** @@ -541,6 +555,9 @@ struct astcenc_config */ struct astcenc_image { + /** @brief The stride dimension of the image, in texels. */ + unsigned int dim_stride; + /** @brief The X dimension of the image, in texels. */ unsigned int dim_x; @@ -707,6 +724,10 @@ ASTCENC_PUBLIC astcenc_error astcenc_compress_image( const astcenc_swizzle* swizzle, uint8_t* data_out, size_t data_len, +#if QUALITY_CONTROL + bool calQualityEnable, + int32_t *mse[RGBA_COM], +#endif unsigned int thread_index); /** diff --git a/Source/astcenc_compress_symbolic.cpp b/Source/astcenc_compress_symbolic.cpp index c7d3a8c703f062b0498b5ea9d11cc2385c30107b..44fddbc103fa0d5aa87140e4f1b4573d677b0930 100644 --- a/Source/astcenc_compress_symbolic.cpp +++ b/Source/astcenc_compress_symbolic.cpp @@ -1164,7 +1164,14 @@ void compress_block( const astcenc_context& ctx, const image_block& blk, physical_compressed_block& pcb, - compression_working_buffers& tmpbuf) +#if QUALITY_CONTROL + compression_working_buffers& tmpbuf, + bool calQualityEnable, + int32_t *mseBlock[RGBA_COM] +#else + compression_working_buffers& tmpbuf +#endif + ) { astcenc_profile decode_mode = ctx.config.profile; symbolic_compressed_block scb; @@ -1230,6 +1237,11 @@ void compress_block( trace_add_data("exit", "quality hit"); symbolic_to_physical(bsd, scb, pcb); +#if QUALITY_CONTROL + if (calQualityEnable) { + *mseBlock[R_COM] = *mseBlock[G_COM] = *mseBlock[B_COM] = *mseBlock[A_COM]; + } +#endif return; } @@ -1416,6 +1428,23 @@ END_OF_TESTS: // Compress to a physical block symbolic_to_physical(bsd, scb, pcb); +#if QUALITY_CONTROL + if (calQualityEnable) { + image_block decBlk = blk; + decompress_symbolic_block(ctx.config.profile, bsd, blk.xpos, blk.ypos, blk.zpos, scb, decBlk); + vint4 colorSumDiff = vint4::zero(); + for (size_t ii = 0; ii < bsd.texel_count; ii++) { + vint4 colorRef = float_to_int_rtn(blk.texel(ii) * 255.0f / 65535.0f); + vint4 colorTest = float_to_int_rtn(min(decBlk.texel(ii), 1.0f) * 255.0f); + vint4 colorDiff = colorRef - colorTest; + colorSumDiff += colorDiff * colorDiff; + } + *mseBlock[R_COM] = colorSumDiff.lane<0>(); + *mseBlock[G_COM] = colorSumDiff.lane<1>(); + *mseBlock[B_COM] = colorSumDiff.lane<2>(); + *mseBlock[A_COM] = colorSumDiff.lane<3>(); + } +#endif } #endif diff --git a/Source/astcenc_compute_variance.cpp b/Source/astcenc_compute_variance.cpp index 02281a19fd1815a7429b6e57ab253549c158eac3..2284e90df71cc050076603ee5c4b5e98b952618f 100644 --- a/Source/astcenc_compute_variance.cpp +++ b/Source/astcenc_compute_variance.cpp @@ -182,10 +182,10 @@ static void compute_pixel_region_variance( int x_src = (x - 1) + offset_x - kernel_radius_xy; x_src = astc::clamp(x_src, 0, static_cast(img->dim_x - 1)); - data[0] = data8[(4 * img->dim_x * y_src) + (4 * x_src )]; - data[1] = data8[(4 * img->dim_x * y_src) + (4 * x_src + 1)]; - data[2] = data8[(4 * img->dim_x * y_src) + (4 * x_src + 2)]; - data[3] = data8[(4 * img->dim_x * y_src) + (4 * x_src + 3)]; + data[0] = data8[(4 * img->dim_stride * y_src) + (4 * x_src )]; + data[1] = data8[(4 * img->dim_stride * y_src) + (4 * x_src + 1)]; + data[2] = data8[(4 * img->dim_stride * y_src) + (4 * x_src + 2)]; + data[3] = data8[(4 * img->dim_stride * y_src) + (4 * x_src + 3)]; uint8_t r = data[swz.r]; uint8_t g = data[swz.g]; diff --git a/Source/astcenc_entry.cpp b/Source/astcenc_entry.cpp index 3c21c55c281e718d4b3cebd58e7aa9a4e07c1046..4f547b22102f70f67bac635d942fd41254556f1c 100644 --- a/Source/astcenc_entry.cpp +++ b/Source/astcenc_entry.cpp @@ -806,7 +806,13 @@ static void compress_image( unsigned int thread_index, const astcenc_image& image, const astcenc_swizzle& swizzle, +#if QUALITY_CONTROL + uint8_t* buffer, + bool calQualityEnable, + int32_t *mse[RGBA_COM] +#else uint8_t* buffer +#endif ) { const block_size_descriptor& bsd = *ctx.bsd; astcenc_profile decode_mode = ctx.config.profile; @@ -932,7 +938,19 @@ static void compress_image( int offset = ((z * yblocks + y) * xblocks + x) * 16; uint8_t *bp = buffer + offset; physical_compressed_block* pcb = reinterpret_cast(bp); +#if QUALITY_CONTROL + int32_t *mseBlock[RGBA_COM] = {nullptr, nullptr, nullptr, nullptr}; + if (calQualityEnable) { + int offset = (z * yblocks + y) * xblocks + x; + mseBlock[R_COM] = mse[R_COM] + offset; + mseBlock[G_COM] = mse[G_COM] + offset; + mseBlock[B_COM] = mse[B_COM] + offset; + mseBlock[A_COM] = mse[A_COM] + offset; + } + compress_block(ctx, blk, *pcb, temp_buffers, calQualityEnable, mseBlock); +#else compress_block(ctx, blk, *pcb, temp_buffers); +#endif } ctx.manage_compress.complete_task_assignment(count); @@ -948,6 +966,10 @@ astcenc_error astcenc_compress_image( const astcenc_swizzle* swizzle, uint8_t* data_out, size_t data_len, +#if QUALITY_CONTROL + bool calQualityEnable, + int32_t *mse[RGBA_COM], +#endif unsigned int thread_index ) { #if defined(ASTCENC_DECOMPRESS_ONLY) @@ -1022,9 +1044,11 @@ astcenc_error astcenc_compress_image( // Wait for compute_averages to complete before compressing ctx->manage_avg.wait(); - +#if QUALITY_CONTROL + compress_image(*ctx, thread_index, image, *swizzle, data_out, calQualityEnable, mse); +#else compress_image(*ctx, thread_index, image, *swizzle, data_out); - +#endif // Wait for compress to complete before freeing memory ctx->manage_compress.wait(); diff --git a/Source/astcenc_image.cpp b/Source/astcenc_image.cpp index ff8c6755c88f0493840cc66c5143e937487e30ab..1c9f394f6550beec87c93a55b39264c4c09e01a7 100644 --- a/Source/astcenc_image.cpp +++ b/Source/astcenc_image.cpp @@ -280,7 +280,7 @@ void fetch_image_block_fast_ldr( unsigned int xsize = img.dim_x; unsigned int ysize = img.dim_y; - + unsigned int stride = img.dim_stride; blk.xpos = xpos; blk.ypos = ypos; blk.zpos = zpos; @@ -300,7 +300,7 @@ void fetch_image_block_fast_ldr( { unsigned int xi = astc::min(x, xsize - 1); - vint4 datavi = vint4(plane + (4 * xsize * yi) + (4 * xi)); + vint4 datavi = vint4(plane + (4 * stride * yi) + (4 * xi)); vfloat4 datav = int_to_float(datavi) * (65535.0f / 255.0f); // Compute block metadata diff --git a/Source/astcenc_internal.h b/Source/astcenc_internal.h index aa7f6001b22554f00ba7de672b3d807f52d48854..e93750c6816e8c9a46d3ae3fcfb3b16de6e818fd 100644 --- a/Source/astcenc_internal.h +++ b/Source/astcenc_internal.h @@ -1351,6 +1351,9 @@ struct avg_args pixel_region_args arg; // The above has a reference to the image altread? + /** @brief The image Stride dimensions. */ + unsigned int img_size_stride; + /** @brief The image X dimensions. */ unsigned int img_size_x; @@ -2284,7 +2287,14 @@ void compress_block( const astcenc_context& ctx, const image_block& blk, physical_compressed_block& pcb, - compression_working_buffers& tmpbuf); +#if QUALITY_CONTROL + compression_working_buffers& tmpbuf, + bool calQualityEnable, + int32_t *mseBlock[RGBA_COM] +#else + compression_working_buffers& tmpbuf +#endif + ); /** * @brief Decompress a symbolic block in to an image block. diff --git a/Source/astcenc_vecmathlib_avx2_8.h b/Source/astcenc_vecmathlib_avx2_8.h old mode 100755 new mode 100644 diff --git a/Source/astcenc_vecmathlib_common_4.h b/Source/astcenc_vecmathlib_common_4.h old mode 100755 new mode 100644 diff --git a/Source/astcenc_vecmathlib_neon_4.h b/Source/astcenc_vecmathlib_neon_4.h old mode 100755 new mode 100644 diff --git a/Source/astcenc_vecmathlib_sse_4.h b/Source/astcenc_vecmathlib_sse_4.h old mode 100755 new mode 100644 diff --git a/Source/astcenccli_toplevel.cpp b/Source/astcenccli_toplevel.cpp index 91e08f0907ba8b6a7e5474dc7319aab8518fb63e..bff4b73511903b1ee84e9f5df0c54a26d2dba11d 100644 --- a/Source/astcenccli_toplevel.cpp +++ b/Source/astcenccli_toplevel.cpp @@ -139,6 +139,10 @@ struct compression_workload uint8_t* data_out; size_t data_len; astcenc_error error; +#if QUALITY_CONTROL + bool calQualityEnable; + int32_t *mse[RGBA_COM]; +#endif }; /** @@ -198,7 +202,11 @@ static void compression_workload_runner( compression_workload* work = static_cast(payload); astcenc_error error = astcenc_compress_image( work->context, work->image, &work->swizzle, - work->data_out, work->data_len, thread_id); + work->data_out, work->data_len, +#if QUALITY_CONTROL + work->calQualityEnable, work->mse, +#endif + thread_id); // This is a racy update, so which error gets returned is a random, but it // will reliably report an error if an error occurs @@ -1329,6 +1337,53 @@ static void image_preprocess_premultiply( } } +#if QUALITY_CONTROL +constexpr double MAX_PSNR = 99.9; +constexpr double MAX_VALUE = 255; +constexpr double THRESHOLD_R = 30.0; +constexpr double THRESHOLD_G = 30.0; +constexpr double THRESHOLD_B = 30.0; +constexpr double THRESHOLD_A = 30.0; +constexpr double THRESHOLD_RGB = 30.0; +constexpr double LOG_BASE = 10.0; + +bool CheckQuality(int32_t* mseIn[RGBA_COM], int blockNum, int blockXYZ) +{ + double psnr[RGBA_COM + 1]; + double threshold[RGBA_COM + 1] = { THRESHOLD_R, THRESHOLD_G, THRESHOLD_B, THRESHOLD_A, THRESHOLD_RGB}; + uint64_t mseTotal[RGBA_COM + 1] = { 0, 0, 0, 0, 0}; + for (int i = R_COM; i < RGBA_COM; i++) { + int32_t* mse = mseIn[i]; + for (int j = 0; j < blockNum; j++) { + mseTotal[i] += *mse; + if(i != A_COM) mseTotal[RGBA_COM] += *mse; + mse++; + } + } + for (int i = R_COM; i < RGBA_COM; i++) { + if (mseTotal[i] == 0) { + psnr[i] = MAX_PSNR; + continue; + } + double mseRgb = (double)mseTotal[i] / (blockNum * blockXYZ); + psnr[i] = LOG_BASE * log((double)(MAX_VALUE * MAX_VALUE) / mseRgb) / log(LOG_BASE); + } + if (mseTotal[RGBA_COM] == 0) { + psnr[RGBA_COM] = MAX_PSNR; + } + else { + double mseRgb = (double)mseTotal[RGBA_COM] / (blockNum * blockXYZ * (RGBA_COM - 1)); + psnr[RGBA_COM] = LOG_BASE * log((double)(MAX_VALUE * MAX_VALUE) / mseRgb) / log(LOG_BASE); + } + printf("astc psnr r%f g%f b%f a%f rgb%f\n", + psnr[R_COM], psnr[G_COM], psnr[B_COM], psnr[A_COM], + psnr[RGBA_COM]); + return (psnr[R_COM] > threshold[R_COM]) && (psnr[G_COM] > threshold[G_COM]) + && (psnr[B_COM] > threshold[B_COM]) && (psnr[A_COM] > threshold[A_COM]) + && (psnr[RGBA_COM] > threshold[RGBA_COM]); +} +#endif + /** * @brief The main entry point. * @@ -1584,12 +1639,25 @@ int main( compression_workload work; work.context = codec_context; + image_uncomp_in->dim_stride = image_uncomp_in->dim_x; work.image = image_uncomp_in; work.swizzle = cli_config.swz_encode; work.data_out = buffer; work.data_len = buffer_size; work.error = ASTCENC_SUCCESS; - +#if QUALITY_CONTROL + work.calQualityEnable = true; + work.mse[R_COM] = work.mse[G_COM] = work.mse[B_COM] = work.mse[A_COM] = nullptr; + if (work.calQualityEnable) { + for (int i = R_COM; i < RGBA_COM; i++) { + work.mse[i] = (int32_t*)calloc(blocks_x * blocks_y, sizeof(int32_t)); + if (!work.mse[i]) { + printf("quality control calloc failed"); + return -1; + } + } + } +#endif // Only launch worker threads for multi-threaded use - it makes basic // single-threaded profiling and debugging a little less convoluted if (cli_config.thread_count > 1) @@ -1600,7 +1668,11 @@ int main( { work.error = astcenc_compress_image( work.context, work.image, &work.swizzle, - work.data_out, work.data_len, 0); + work.data_out, work.data_len, +#if QUALITY_CONTROL + work.calQualityEnable, work.mse, +#endif + 0); } if (work.error != ASTCENC_SUCCESS) @@ -1608,7 +1680,18 @@ int main( printf("ERROR: Codec compress failed: %s\n", astcenc_get_error_string(work.error)); return 1; } - +#if QUALITY_CONTROL + if (work.calQualityEnable && !CheckQuality(work.mse, blocks_x * blocks_y, config.block_x * config.block_y)) { + work.error = ASTCENC_ERR_BAD_QUALITY_CHECK; + } + if (work.calQualityEnable) { + for (int i = R_COM; i < RGBA_COM; i++) { + if (work.mse[i]) { + free(work.mse[i]); + } + } + } +#endif image_comp.block_x = config.block_x; image_comp.block_y = config.block_y; image_comp.block_z = config.block_z;