Browse Source

astcenc: Misc improvements and optimizations

BlueCube3310 7 months ago
parent
commit
6f363b989a

+ 77 - 83
modules/astcenc/image_compress_astcenc.cpp

@@ -36,41 +36,39 @@
 #include <astcenc.h>
 
 void _compress_astc(Image *r_img, Image::ASTCFormat p_format) {
-	uint64_t start_time = OS::get_singleton()->get_ticks_msec();
+	const uint64_t start_time = OS::get_singleton()->get_ticks_msec();
 
-	// TODO: See how to handle lossy quality.
-
-	Image::Format img_format = r_img->get_format();
-	if (Image::is_format_compressed(img_format)) {
+	if (r_img->is_compressed()) {
 		return; // Do not compress, already compressed.
 	}
 
-	bool is_hdr = false;
-	if ((img_format >= Image::FORMAT_RH) && (img_format <= Image::FORMAT_RGBE9995)) {
-		is_hdr = true;
+	const Image::Format src_format = r_img->get_format();
+	const bool is_hdr = src_format >= Image::FORMAT_RF && src_format <= Image::FORMAT_RGBE9995;
+
+	if (src_format >= Image::FORMAT_RH && src_format <= Image::FORMAT_RGBAH) {
+		r_img->convert(Image::FORMAT_RGBAH);
+	} else if (src_format >= Image::FORMAT_RF && src_format <= Image::FORMAT_RGBE9995) {
 		r_img->convert(Image::FORMAT_RGBAF);
 	} else {
 		r_img->convert(Image::FORMAT_RGBA8);
 	}
 
 	// Determine encoder output format from our enum.
+	const astcenc_profile profile = is_hdr ? ASTCENC_PRF_HDR : ASTCENC_PRF_LDR;
 
-	Image::Format target_format = Image::FORMAT_RGBA8;
-	astcenc_profile profile = ASTCENC_PRF_LDR;
+	Image::Format target_format = Image::FORMAT_MAX;
 	unsigned int block_x = 4;
 	unsigned int block_y = 4;
 
 	if (p_format == Image::ASTCFormat::ASTC_FORMAT_4x4) {
 		if (is_hdr) {
 			target_format = Image::FORMAT_ASTC_4x4_HDR;
-			profile = ASTCENC_PRF_HDR;
 		} else {
 			target_format = Image::FORMAT_ASTC_4x4;
 		}
 	} else if (p_format == Image::ASTCFormat::ASTC_FORMAT_8x8) {
 		if (is_hdr) {
 			target_format = Image::FORMAT_ASTC_8x8_HDR;
-			profile = ASTCENC_PRF_HDR;
 		} else {
 			target_format = Image::FORMAT_ASTC_8x8;
 		}
@@ -79,8 +77,7 @@ void _compress_astc(Image *r_img, Image::ASTCFormat p_format) {
 	}
 
 	// Compress image data and (if required) mipmaps.
-
-	const bool mipmaps = r_img->has_mipmaps();
+	const bool has_mipmaps = r_img->has_mipmaps();
 	int width = r_img->get_width();
 	int height = r_img->get_height();
 	int required_width = (width % block_x) != 0 ? width + (block_x - (width % block_x)) : width;
@@ -93,11 +90,10 @@ void _compress_astc(Image *r_img, Image::ASTCFormat p_format) {
 		height = required_height;
 	}
 
-	print_verbose(vformat("astcenc: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), mipmaps ? ", with mipmaps" : ""));
+	print_verbose(vformat("astcenc: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), has_mipmaps ? ", with mipmaps" : ""));
 
 	// Initialize astcenc.
-
-	int64_t dest_size = Image::get_image_data_size(width, height, target_format, mipmaps);
+	const int64_t dest_size = Image::get_image_data_size(width, height, target_format, has_mipmaps);
 	Vector<uint8_t> dest_data;
 	dest_data.resize(dest_size);
 	uint8_t *dest_write = dest_data.ptrw();
@@ -113,42 +109,44 @@ void _compress_astc(Image *r_img, Image::ASTCFormat p_format) {
 			vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status)));
 
 	// Context allocation.
-
 	astcenc_context *context;
 	const unsigned int thread_count = 1; // Godot compresses multiple images each on a thread, which is more efficient for large amount of images imported.
 	status = astcenc_context_alloc(&config, thread_count, &context);
 	ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
 			vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status)));
 
-	Vector<uint8_t> image_data = r_img->get_data();
+	const int mip_count = has_mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
+	const uint8_t *src_data = r_img->ptr();
 
-	int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
 	for (int i = 0; i < mip_count + 1; i++) {
 		int src_mip_w, src_mip_h;
-		int64_t src_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i, src_mip_w, src_mip_h);
+		const int64_t src_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i, src_mip_w, src_mip_h);
+		const uint8_t *mip_data = &src_data[src_ofs];
 
-		const uint8_t *slices = &image_data.ptr()[src_ofs];
+		const int64_t dst_ofs = Image::get_image_mipmap_offset(width, height, target_format, i);
+		uint8_t *dest_mip_write = &dest_write[dst_ofs];
 
-		int dst_mip_w, dst_mip_h;
-		int64_t dst_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, dst_mip_w, dst_mip_h);
 		// Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer).
 		if (unlikely(dst_ofs % 8 != 0)) {
 			astcenc_context_free(context);
 			ERR_FAIL_MSG("astcenc: Mip offset is not a multiple of 8.");
 		}
-		uint8_t *dest_mip_write = (uint8_t *)&dest_write[dst_ofs];
 
 		// Compress image.
-
 		astcenc_image image;
 		image.dim_x = src_mip_w;
 		image.dim_y = src_mip_h;
 		image.dim_z = 1;
-		image.data_type = ASTCENC_TYPE_U8;
-		if (is_hdr) {
+
+		if (r_img->get_format() == Image::FORMAT_RGBA8) {
+			image.data_type = ASTCENC_TYPE_U8;
+		} else if (r_img->get_format() == Image::FORMAT_RGBAH) {
+			image.data_type = ASTCENC_TYPE_F16;
+		} else {
 			image.data_type = ASTCENC_TYPE_F32;
 		}
-		image.data = (void **)(&slices);
+
+		image.data = (void **)(&mip_data);
 
 		// Compute the number of ASTC blocks in each dimension.
 		unsigned int block_count_x = (src_mip_w + block_x - 1) / block_x;
@@ -160,56 +158,59 @@ void _compress_astc(Image *r_img, Image::ASTCFormat p_format) {
 		};
 
 		status = astcenc_compress_image(context, &image, &swizzle, dest_mip_write, comp_len, 0);
-
 		ERR_BREAK_MSG(status != ASTCENC_SUCCESS,
 				vformat("astcenc: ASTC image compression failed: %s.", astcenc_get_error_string(status)));
+
 		astcenc_compress_reset(context);
 	}
 
 	astcenc_context_free(context);
 
 	// Replace original image with compressed one.
-
-	r_img->set_data(width, height, mipmaps, target_format, dest_data);
+	r_img->set_data(width, height, has_mipmaps, target_format, dest_data);
 
 	print_verbose(vformat("astcenc: Encoding took %d ms.", OS::get_singleton()->get_ticks_msec() - start_time));
 }
 
 void _decompress_astc(Image *r_img) {
-	uint64_t start_time = OS::get_singleton()->get_ticks_msec();
+	const uint64_t start_time = OS::get_singleton()->get_ticks_msec();
 
 	// Determine decompression parameters from image format.
+	const Image::Format src_format = r_img->get_format();
 
-	Image::Format img_format = r_img->get_format();
 	bool is_hdr = false;
 	unsigned int block_x = 0;
 	unsigned int block_y = 0;
-	if (img_format == Image::FORMAT_ASTC_4x4) {
-		block_x = 4;
-		block_y = 4;
-		is_hdr = false;
-	} else if (img_format == Image::FORMAT_ASTC_4x4_HDR) {
-		block_x = 4;
-		block_y = 4;
-		is_hdr = true;
-	} else if (img_format == Image::FORMAT_ASTC_8x8) {
-		block_x = 8;
-		block_y = 8;
-		is_hdr = false;
-	} else if (img_format == Image::FORMAT_ASTC_8x8_HDR) {
-		block_x = 8;
-		block_y = 8;
-		is_hdr = true;
-	} else {
-		ERR_FAIL_MSG("astcenc: Cannot decompress Image with a non-ASTC format.");
+
+	switch (src_format) {
+		case Image::FORMAT_ASTC_4x4: {
+			block_x = 4;
+			block_y = 4;
+			is_hdr = false;
+		} break;
+		case Image::FORMAT_ASTC_4x4_HDR: {
+			block_x = 4;
+			block_y = 4;
+			is_hdr = true;
+		} break;
+		case Image::FORMAT_ASTC_8x8: {
+			block_x = 8;
+			block_y = 8;
+			is_hdr = false;
+		} break;
+		case Image::FORMAT_ASTC_8x8_HDR: {
+			block_x = 8;
+			block_y = 8;
+			is_hdr = true;
+		} break;
+		default: {
+			ERR_FAIL_MSG(vformat("astcenc: Cannot decompress Image with a non-ASTC format: %s.", Image::get_format_name(src_format)));
+		} break;
 	}
 
 	// Initialize astcenc.
+	const astcenc_profile profile = is_hdr ? ASTCENC_PRF_HDR : ASTCENC_PRF_LDR;
 
-	astcenc_profile profile = ASTCENC_PRF_LDR;
-	if (is_hdr) {
-		profile = ASTCENC_PRF_HDR;
-	}
 	astcenc_config config;
 	const float quality = ASTCENC_PRE_MEDIUM;
 
@@ -218,7 +219,6 @@ void _decompress_astc(Image *r_img) {
 			vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status)));
 
 	// Context allocation.
-
 	astcenc_context *context = nullptr;
 	const unsigned int thread_count = 1;
 
@@ -226,49 +226,44 @@ void _decompress_astc(Image *r_img) {
 	ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
 			vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status)));
 
-	Image::Format target_format = is_hdr ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8;
+	const Image::Format target_format = is_hdr ? Image::FORMAT_RGBAH : Image::FORMAT_RGBA8;
 
-	const bool mipmaps = r_img->has_mipmaps();
+	const bool has_mipmaps = r_img->has_mipmaps();
 	int width = r_img->get_width();
 	int height = r_img->get_height();
-	int64_t dest_size = Image::get_image_data_size(width, height, target_format, mipmaps);
+
+	const int64_t dest_size = Image::get_image_data_size(width, height, target_format, has_mipmaps);
 	Vector<uint8_t> dest_data;
 	dest_data.resize(dest_size);
 	uint8_t *dest_write = dest_data.ptrw();
 
 	// Decompress image.
-
-	Vector<uint8_t> image_data = r_img->get_data();
-	int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
+	const int mip_count = has_mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
+	const uint8_t *src_data = r_img->ptr();
 
 	for (int i = 0; i < mip_count + 1; i++) {
-		int src_mip_w, src_mip_h;
+		const int64_t src_ofs = Image::get_image_mipmap_offset(width, height, src_format, i);
+		const uint8_t *mip_data = &src_data[src_ofs];
 
-		int64_t src_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i, src_mip_w, src_mip_h);
-		const uint8_t *src_data = &image_data.ptr()[src_ofs];
 		int64_t src_size;
 		if (i == mip_count) {
-			src_size = image_data.size() - src_ofs;
+			src_size = r_img->get_data_size() - src_ofs;
 		} else {
-			int auxw, auxh;
-			src_size = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i + 1, auxw, auxh) - src_ofs;
+			src_size = Image::get_image_mipmap_offset(width, height, src_format, i + 1) - src_ofs;
 		}
 
 		int dst_mip_w, dst_mip_h;
-		int64_t dst_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, dst_mip_w, dst_mip_h);
+		const int64_t dst_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, dst_mip_w, dst_mip_h);
+
 		// Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer).
 		ERR_FAIL_COND(dst_ofs % 8 != 0);
-		uint8_t *dest_mip_write = (uint8_t *)&dest_write[dst_ofs];
+		uint8_t *dest_mip_write = &dest_write[dst_ofs];
 
 		astcenc_image image;
 		image.dim_x = dst_mip_w;
 		image.dim_y = dst_mip_h;
 		image.dim_z = 1;
-		image.data_type = ASTCENC_TYPE_U8;
-		if (is_hdr) {
-			target_format = Image::FORMAT_RGBAF;
-			image.data_type = ASTCENC_TYPE_F32;
-		}
+		image.data_type = is_hdr ? ASTCENC_TYPE_F16 : ASTCENC_TYPE_U8;
 
 		image.data = (void **)(&dest_mip_write);
 
@@ -276,18 +271,17 @@ void _decompress_astc(Image *r_img) {
 			ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
 		};
 
-		status = astcenc_decompress_image(context, src_data, src_size, &image, &swizzle, 0);
-		ERR_BREAK_MSG(status != ASTCENC_SUCCESS,
-				vformat("astcenc: ASTC decompression failed: %s.", astcenc_get_error_string(status)));
-		ERR_BREAK_MSG(image.dim_z > 1,
-				"astcenc: ASTC decompression failed because this is a 3D texture, which is not supported.");
+		status = astcenc_decompress_image(context, mip_data, src_size, &image, &swizzle, 0);
+		ERR_BREAK_MSG(status != ASTCENC_SUCCESS, vformat("astcenc: ASTC decompression failed: %s.", astcenc_get_error_string(status)));
+		ERR_BREAK_MSG(image.dim_z > 1, "astcenc: ASTC decompression failed because this is a 3D texture, which is not supported.");
+
 		astcenc_compress_reset(context);
 	}
+
 	astcenc_context_free(context);
 
 	// Replace original image with compressed one.
-
-	r_img->set_data(width, height, mipmaps, target_format, dest_data);
+	r_img->set_data(width, height, has_mipmaps, target_format, dest_data);
 
 	print_verbose(vformat("astcenc: Decompression took %d ms.", OS::get_singleton()->get_ticks_msec() - start_time));
 }

+ 16 - 6
servers/rendering/renderer_rd/storage_rd/texture_storage.cpp

@@ -2185,10 +2185,15 @@ Ref<Image> TextureStorage::_validate_texture_format(const Ref<Image> &p_image, T
 				}
 			} else {
 				//not supported, reconvert
-				r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
-				r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
 				image->decompress();
-				image->convert(Image::FORMAT_RGBA8);
+				if (p_image->get_format() == Image::FORMAT_ASTC_4x4) {
+					r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+					r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
+					image->convert(Image::FORMAT_RGBA8);
+				} else {
+					r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+					image->convert(Image::FORMAT_RGBAH);
+				}
 			}
 			r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
 			r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
@@ -2205,10 +2210,15 @@ Ref<Image> TextureStorage::_validate_texture_format(const Ref<Image> &p_image, T
 				}
 			} else {
 				//not supported, reconvert
-				r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
-				r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
 				image->decompress();
-				image->convert(Image::FORMAT_RGBA8);
+				if (p_image->get_format() == Image::FORMAT_ASTC_8x8) {
+					r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+					r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
+					image->convert(Image::FORMAT_RGBA8);
+				} else {
+					r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+					image->convert(Image::FORMAT_RGBAH);
+				}
 			}
 			r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
 			r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;