diff --git a/core/io/image.cpp b/core/io/image.cpp index 128bbf3e6f0..f466848a526 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -2788,8 +2788,7 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels } break; case COMPRESS_S3TC: { - // BC3 is unsupported currently. - if ((p_channels == USED_CHANNELS_R || p_channels == USED_CHANNELS_RGB || p_channels == USED_CHANNELS_L) && _image_compress_bc_rd_func) { + if (_image_compress_bc_rd_func) { Error result = _image_compress_bc_rd_func(this, p_channels); // If the image was compressed successfully, we return here. If not, we fall back to the default compression scheme. diff --git a/modules/betsy/SCsub b/modules/betsy/SCsub index 621ba58ae3f..9ed5fc4ee80 100644 --- a/modules/betsy/SCsub +++ b/modules/betsy/SCsub @@ -8,6 +8,7 @@ env_betsy = env_modules.Clone() env_betsy.GLSL_HEADER("bc6h.glsl") env_betsy.GLSL_HEADER("bc1.glsl") env_betsy.GLSL_HEADER("bc4.glsl") +env_betsy.GLSL_HEADER("alpha_stitch.glsl") env_betsy.Depends(Glob("*.glsl.gen.h"), ["#glsl_builders.py"]) # Thirdparty source files diff --git a/modules/betsy/alpha_stitch.glsl b/modules/betsy/alpha_stitch.glsl new file mode 100644 index 00000000000..4a9ee58d97b --- /dev/null +++ b/modules/betsy/alpha_stitch.glsl @@ -0,0 +1,24 @@ +// RGB and Alpha components of ETC2 RGBA are computed separately. +// This compute shader merely stitches them together to form the final result +// It's also used by RG11 driver to stitch two R11 into one RG11 + +#[compute] +#version 450 + +#include "CrossPlatformSettings_piece_all.glsl" +#include "UavCrossPlatform_piece_all.glsl" + +layout(local_size_x = 8, // + local_size_y = 8, // + local_size_z = 1) in; + +layout(binding = 0) uniform usampler2D srcRGB; +layout(binding = 1) uniform usampler2D srcAlpha; +layout(binding = 2, rgba32ui) uniform restrict writeonly uimage2D dstTexture; + +void main() { + uint2 rgbBlock = OGRE_Load2D(srcRGB, int2(gl_GlobalInvocationID.xy), 0).xy; + uint2 alphaBlock = OGRE_Load2D(srcAlpha, int2(gl_GlobalInvocationID.xy), 0).xy; + + imageStore(dstTexture, int2(gl_GlobalInvocationID.xy), uint4(rgbBlock.xy, alphaBlock.xy)); +} diff --git a/modules/betsy/image_compress_betsy.cpp b/modules/betsy/image_compress_betsy.cpp index 1ad9bed721a..35f61521f4b 100644 --- a/modules/betsy/image_compress_betsy.cpp +++ b/modules/betsy/image_compress_betsy.cpp @@ -34,6 +34,7 @@ #include "betsy_bc1.h" +#include "alpha_stitch.glsl.gen.h" #include "bc1.glsl.gen.h" #include "bc4.glsl.gen.h" #include "bc6h.glsl.gen.h" @@ -42,6 +43,42 @@ static Mutex betsy_mutex; static BetsyCompressor *betsy = nullptr; +static const BetsyShaderType FORMAT_TO_TYPE[BETSY_FORMAT_MAX] = { + BETSY_SHADER_BC1_STANDARD, + BETSY_SHADER_BC1_DITHER, + BETSY_SHADER_BC1_STANDARD, + BETSY_SHADER_BC4_SIGNED, + BETSY_SHADER_BC4_UNSIGNED, + BETSY_SHADER_BC4_SIGNED, + BETSY_SHADER_BC4_UNSIGNED, + BETSY_SHADER_BC6_SIGNED, + BETSY_SHADER_BC6_UNSIGNED, +}; + +static const RD::DataFormat BETSY_TO_RD_FORMAT[BETSY_FORMAT_MAX] = { + RD::DATA_FORMAT_R32G32_UINT, + RD::DATA_FORMAT_R32G32_UINT, + RD::DATA_FORMAT_R32G32_UINT, + RD::DATA_FORMAT_R32G32_UINT, + RD::DATA_FORMAT_R32G32_UINT, + RD::DATA_FORMAT_R32G32_UINT, + RD::DATA_FORMAT_R32G32_UINT, + RD::DATA_FORMAT_R32G32B32A32_UINT, + RD::DATA_FORMAT_R32G32B32A32_UINT, +}; + +static const Image::Format BETSY_TO_IMAGE_FORMAT[BETSY_FORMAT_MAX] = { + Image::FORMAT_DXT1, + Image::FORMAT_DXT1, + Image::FORMAT_DXT5, + Image::FORMAT_RGTC_R, + Image::FORMAT_RGTC_R, + Image::FORMAT_RGTC_RG, + Image::FORMAT_RGTC_RG, + Image::FORMAT_BPTC_RGBF, + Image::FORMAT_BPTC_RGBFU, +}; + void BetsyCompressor::_init() { if (!DisplayServer::can_create_rendering_device()) { return; @@ -95,6 +132,94 @@ void BetsyCompressor::_init() { } src_sampler = compress_rd->sampler_create(src_sampler_state); + + // Initialize RDShaderFiles. + { + Ref bc1_shader; + bc1_shader.instantiate(); + Error err = bc1_shader->parse_versions_from_text(bc1_shader_glsl); + + if (err != OK) { + bc1_shader->print_errors("Betsy BC1 compress shader"); + } + + // Standard BC1 compression. + cached_shaders[BETSY_SHADER_BC1_STANDARD].compiled = compress_rd->shader_create_from_spirv(bc1_shader->get_spirv_stages("standard")); + ERR_FAIL_COND(cached_shaders[BETSY_SHADER_BC1_STANDARD].compiled.is_null()); + + cached_shaders[BETSY_SHADER_BC1_STANDARD].pipeline = compress_rd->compute_pipeline_create(cached_shaders[BETSY_SHADER_BC1_STANDARD].compiled); + ERR_FAIL_COND(cached_shaders[BETSY_SHADER_BC1_STANDARD].pipeline.is_null()); + + // Dither BC1 variant. Unused, so comment out for now. + //cached_shaders[BETSY_SHADER_BC1_DITHER].compiled = compress_rd->shader_create_from_spirv(bc1_shader->get_spirv_stages("dithered")); + //ERR_FAIL_COND(cached_shaders[BETSY_SHADER_BC1_DITHER].compiled.is_null()); + + //cached_shaders[BETSY_SHADER_BC1_DITHER].pipeline = compress_rd->compute_pipeline_create(cached_shaders[BETSY_SHADER_BC1_DITHER].compiled); + //ERR_FAIL_COND(cached_shaders[BETSY_SHADER_BC1_DITHER].pipeline.is_null()); + } + + { + Ref bc4_shader; + bc4_shader.instantiate(); + Error err = bc4_shader->parse_versions_from_text(bc4_shader_glsl); + + if (err != OK) { + bc4_shader->print_errors("Betsy BC4 compress shader"); + } + + // Signed BC4 compression. Unused, so comment out for now. + //cached_shaders[BETSY_SHADER_BC4_SIGNED].compiled = compress_rd->shader_create_from_spirv(bc4_shader->get_spirv_stages("signed")); + //ERR_FAIL_COND(cached_shaders[BETSY_SHADER_BC4_SIGNED].compiled.is_null()); + + //cached_shaders[BETSY_SHADER_BC4_SIGNED].pipeline = compress_rd->compute_pipeline_create(cached_shaders[BETSY_SHADER_BC4_SIGNED].compiled); + //ERR_FAIL_COND(cached_shaders[BETSY_SHADER_BC4_SIGNED].pipeline.is_null()); + + // Unsigned BC4 compression. + cached_shaders[BETSY_SHADER_BC4_UNSIGNED].compiled = compress_rd->shader_create_from_spirv(bc4_shader->get_spirv_stages("unsigned")); + ERR_FAIL_COND(cached_shaders[BETSY_SHADER_BC4_UNSIGNED].compiled.is_null()); + + cached_shaders[BETSY_SHADER_BC4_UNSIGNED].pipeline = compress_rd->compute_pipeline_create(cached_shaders[BETSY_SHADER_BC4_UNSIGNED].compiled); + ERR_FAIL_COND(cached_shaders[BETSY_SHADER_BC4_UNSIGNED].pipeline.is_null()); + } + + { + Ref bc6h_shader; + bc6h_shader.instantiate(); + Error err = bc6h_shader->parse_versions_from_text(bc6h_shader_glsl); + + if (err != OK) { + bc6h_shader->print_errors("Betsy BC6 compress shader"); + } + + // Signed BC6 compression. + cached_shaders[BETSY_SHADER_BC6_SIGNED].compiled = compress_rd->shader_create_from_spirv(bc6h_shader->get_spirv_stages("signed")); + ERR_FAIL_COND(cached_shaders[BETSY_SHADER_BC6_SIGNED].compiled.is_null()); + + cached_shaders[BETSY_SHADER_BC6_SIGNED].pipeline = compress_rd->compute_pipeline_create(cached_shaders[BETSY_SHADER_BC6_SIGNED].compiled); + ERR_FAIL_COND(cached_shaders[BETSY_SHADER_BC6_SIGNED].pipeline.is_null()); + + // Unsigned BC6 compression. + cached_shaders[BETSY_SHADER_BC6_UNSIGNED].compiled = compress_rd->shader_create_from_spirv(bc6h_shader->get_spirv_stages("unsigned")); + ERR_FAIL_COND(cached_shaders[BETSY_SHADER_BC6_UNSIGNED].compiled.is_null()); + + cached_shaders[BETSY_SHADER_BC6_UNSIGNED].pipeline = compress_rd->compute_pipeline_create(cached_shaders[BETSY_SHADER_BC6_UNSIGNED].compiled); + ERR_FAIL_COND(cached_shaders[BETSY_SHADER_BC6_UNSIGNED].pipeline.is_null()); + } + + { + Ref alpha_stitch_shader; + alpha_stitch_shader.instantiate(); + Error err = alpha_stitch_shader->parse_versions_from_text(alpha_stitch_shader_glsl); + + if (err != OK) { + alpha_stitch_shader->print_errors("Betsy alpha stitch shader"); + } + cached_shaders[BETSY_SHADER_ALPHA_STITCH].compiled = compress_rd->shader_create_from_spirv(alpha_stitch_shader->get_spirv_stages()); + ERR_FAIL_COND(cached_shaders[BETSY_SHADER_ALPHA_STITCH].compiled.is_null()); + + cached_shaders[BETSY_SHADER_ALPHA_STITCH].pipeline = compress_rd->compute_pipeline_create(cached_shaders[BETSY_SHADER_ALPHA_STITCH].compiled); + ERR_FAIL_COND(cached_shaders[BETSY_SHADER_ALPHA_STITCH].pipeline.is_null()); + } } void BetsyCompressor::init() { @@ -129,12 +254,11 @@ void BetsyCompressor::_thread_exit() { compress_rd->free(src_sampler); // Clear the shader cache, pipelines will be unreferenced automatically. - for (KeyValue &E : cached_shaders) { - if (E.value.compiled.is_valid()) { - compress_rd->free(E.value.compiled); + for (int i = 0; i < BETSY_SHADER_MAX; i++) { + if (cached_shaders[i].compiled.is_valid()) { + compress_rd->free(cached_shaders[i].compiled); } } - cached_shaders.clear(); } } @@ -162,26 +286,79 @@ static int get_next_multiple(int n, int m) { return n + (m - (n % m)); } -static String get_shader_name(BetsyFormat p_format) { - switch (p_format) { - case BETSY_FORMAT_BC1: - case BETSY_FORMAT_BC1_DITHER: - return "BC1"; +static Error get_src_texture_format(Image *r_img, RD::DataFormat &r_format) { + switch (r_img->get_format()) { + case Image::FORMAT_L8: + r_img->convert(Image::FORMAT_RGBA8); + r_format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + break; - case BETSY_FORMAT_BC3: - return "BC3"; + case Image::FORMAT_LA8: + r_img->convert(Image::FORMAT_RGBA8); + r_format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + break; - case BETSY_FORMAT_BC4_SIGNED: - case BETSY_FORMAT_BC4_UNSIGNED: - return "BC4"; + case Image::FORMAT_R8: + r_format = RD::DATA_FORMAT_R8_UNORM; + break; - case BETSY_FORMAT_BC6_SIGNED: - case BETSY_FORMAT_BC6_UNSIGNED: - return "BC6"; + case Image::FORMAT_RG8: + r_format = RD::DATA_FORMAT_R8G8_UNORM; + break; - default: - return ""; + case Image::FORMAT_RGB8: + r_img->convert(Image::FORMAT_RGBA8); + r_format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + break; + + case Image::FORMAT_RGBA8: + r_format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + break; + + case Image::FORMAT_RH: + r_format = RD::DATA_FORMAT_R16_SFLOAT; + break; + + case Image::FORMAT_RGH: + r_format = RD::DATA_FORMAT_R16G16_SFLOAT; + break; + + case Image::FORMAT_RGBH: + r_img->convert(Image::FORMAT_RGBAH); + r_format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + break; + + case Image::FORMAT_RGBAH: + r_format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + break; + + case Image::FORMAT_RF: + r_format = RD::DATA_FORMAT_R32_SFLOAT; + break; + + case Image::FORMAT_RGF: + r_format = RD::DATA_FORMAT_R32G32_SFLOAT; + break; + + case Image::FORMAT_RGBF: + r_img->convert(Image::FORMAT_RGBAF); + r_format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + break; + + case Image::FORMAT_RGBAF: + r_format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + break; + + case Image::FORMAT_RGBE9995: + r_format = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; + break; + + default: { + return ERR_UNAVAILABLE; + } } + + return OK; } Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) { @@ -199,102 +376,26 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) { Error err = OK; // Destination format. - Image::Format dest_format = Image::FORMAT_MAX; - RD::DataFormat dst_rd_format = RD::DATA_FORMAT_MAX; + Image::Format dest_format = BETSY_TO_IMAGE_FORMAT[p_format]; + RD::DataFormat dst_rd_format = BETSY_TO_RD_FORMAT[p_format]; - String version = ""; + BetsyShaderType shader_type = FORMAT_TO_TYPE[p_format]; + BetsyShader shader = cached_shaders[shader_type]; + BetsyShader secondary_shader; // The secondary shader is used for alpha blocks. For BC it's BC4U and for ETC it's ETC2_RU (8-bit variant). + BetsyShader stitch_shader; + bool needs_alpha_block = false; switch (p_format) { - case BETSY_FORMAT_BC1: - version = "standard"; - dst_rd_format = RD::DATA_FORMAT_R32G32_UINT; - dest_format = Image::FORMAT_DXT1; + case BETSY_FORMAT_BC3: + case BETSY_FORMAT_BC5_UNSIGNED: + needs_alpha_block = true; + secondary_shader = cached_shaders[BETSY_SHADER_BC4_UNSIGNED]; + stitch_shader = cached_shaders[BETSY_SHADER_ALPHA_STITCH]; break; - - case BETSY_FORMAT_BC1_DITHER: - version = "dithered"; - dst_rd_format = RD::DATA_FORMAT_R32G32_UINT; - dest_format = Image::FORMAT_DXT1; - break; - - case BETSY_FORMAT_BC4_UNSIGNED: - version = "unsigned"; - dst_rd_format = RD::DATA_FORMAT_R32G32_UINT; - dest_format = Image::FORMAT_RGTC_R; - break; - - case BETSY_FORMAT_BC6_SIGNED: - version = "signed"; - dst_rd_format = RD::DATA_FORMAT_R32G32B32A32_UINT; - dest_format = Image::FORMAT_BPTC_RGBF; - break; - - case BETSY_FORMAT_BC6_UNSIGNED: - version = "unsigned"; - dst_rd_format = RD::DATA_FORMAT_R32G32B32A32_UINT; - dest_format = Image::FORMAT_BPTC_RGBFU; - break; - default: - err = ERR_INVALID_PARAMETER; break; } - const String shader_name = get_shader_name(p_format) + "-" + version; - BetsyShader shader; - - if (cached_shaders.has(shader_name)) { - shader = cached_shaders[shader_name]; - - } else { - Ref source; - source.instantiate(); - - switch (p_format) { - case BETSY_FORMAT_BC1: - case BETSY_FORMAT_BC1_DITHER: - err = source->parse_versions_from_text(bc1_shader_glsl); - break; - - case BETSY_FORMAT_BC4_SIGNED: - case BETSY_FORMAT_BC4_UNSIGNED: - err = source->parse_versions_from_text(bc4_shader_glsl); - break; - - case BETSY_FORMAT_BC6_SIGNED: - case BETSY_FORMAT_BC6_UNSIGNED: - err = source->parse_versions_from_text(bc6h_shader_glsl); - break; - - default: - err = ERR_INVALID_PARAMETER; - break; - } - - if (err != OK) { - source->print_errors("Betsy compress shader"); - return err; - } - - // Compile the shader, return early if invalid. - shader.compiled = compress_rd->shader_create_from_spirv(source->get_spirv_stages(version)); - if (shader.compiled.is_null()) { - return ERR_CANT_CREATE; - } - - // Compile the pipeline, return early if invalid. - shader.pipeline = compress_rd->compute_pipeline_create(shader.compiled); - if (shader.pipeline.is_null()) { - return ERR_CANT_CREATE; - } - - cached_shaders[shader_name] = shader; - } - - if (shader.compiled.is_null() || shader.pipeline.is_null()) { - return ERR_INVALID_DATA; - } - // src_texture format information. RD::TextureFormat src_texture_format; { @@ -305,75 +406,10 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) { src_texture_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; } - switch (r_img->get_format()) { - case Image::FORMAT_L8: - r_img->convert(Image::FORMAT_RGBA8); - src_texture_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; - break; + err = get_src_texture_format(r_img, src_texture_format.format); - case Image::FORMAT_LA8: - r_img->convert(Image::FORMAT_RGBA8); - src_texture_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; - break; - - case Image::FORMAT_R8: - src_texture_format.format = RD::DATA_FORMAT_R8_UNORM; - break; - - case Image::FORMAT_RG8: - src_texture_format.format = RD::DATA_FORMAT_R8G8_UNORM; - break; - - case Image::FORMAT_RGB8: - r_img->convert(Image::FORMAT_RGBA8); - src_texture_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; - break; - - case Image::FORMAT_RGBA8: - src_texture_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; - break; - - case Image::FORMAT_RH: - src_texture_format.format = RD::DATA_FORMAT_R16_SFLOAT; - break; - - case Image::FORMAT_RGH: - src_texture_format.format = RD::DATA_FORMAT_R16G16_SFLOAT; - break; - - case Image::FORMAT_RGBH: - r_img->convert(Image::FORMAT_RGBAH); - src_texture_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; - break; - - case Image::FORMAT_RGBAH: - src_texture_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; - break; - - case Image::FORMAT_RF: - src_texture_format.format = RD::DATA_FORMAT_R32_SFLOAT; - break; - - case Image::FORMAT_RGF: - src_texture_format.format = RD::DATA_FORMAT_R32G32_SFLOAT; - break; - - case Image::FORMAT_RGBF: - r_img->convert(Image::FORMAT_RGBAF); - src_texture_format.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; - break; - - case Image::FORMAT_RGBAF: - src_texture_format.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; - break; - - case Image::FORMAT_RGBE9995: - src_texture_format.format = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; - break; - - default: { - return err; - } + if (err != OK) { + return err; } // For the destination format just copy the source format and change the usage bits. @@ -381,8 +417,21 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) { dst_texture_format.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; dst_texture_format.format = dst_rd_format; + RD::TextureFormat dst_texture_format_alpha; + RD::TextureFormat dst_texture_format_combined; + + if (needs_alpha_block) { + dst_texture_format_combined = dst_texture_format; + dst_texture_format_combined.format = RD::DATA_FORMAT_R32G32B32A32_UINT; + + dst_texture_format.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT; + + dst_texture_format_alpha = dst_texture_format; + dst_texture_format_alpha.format = RD::DATA_FORMAT_R32G32_UINT; + } + // Encoding table setup. - if (dest_format == Image::FORMAT_DXT1 && dxt1_encoding_table_buffer.is_null()) { + if ((dest_format == Image::FORMAT_DXT1 || dest_format == Image::FORMAT_DXT5) && dxt1_encoding_table_buffer.is_null()) { Vector data; data.resize(1024 * 4); memcpy(data.ptrw(), dxt1_encoding_table, 1024 * 4); @@ -421,94 +470,189 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) { // Create the textures on the GPU. RID src_texture = compress_rd->texture_create(src_texture_format, RD::TextureView(), src_images); - RID dst_texture = compress_rd->texture_create(dst_texture_format, RD::TextureView()); + RID dst_texture_primary = compress_rd->texture_create(dst_texture_format, RD::TextureView()); - Vector uniforms; { + Vector uniforms; { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; - u.binding = 0; - u.append_id(src_sampler); - u.append_id(src_texture); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 1; - u.append_id(dst_texture); - uniforms.push_back(u); + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + u.binding = 0; + u.append_id(src_sampler); + u.append_id(src_texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + u.append_id(dst_texture_primary); + uniforms.push_back(u); + } + + if (dest_format == Image::FORMAT_DXT1 || dest_format == Image::FORMAT_DXT5) { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 2; + u.append_id(dxt1_encoding_table_buffer); + uniforms.push_back(u); + } } - if (dest_format == Image::FORMAT_DXT1) { - RD::Uniform u; - u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.binding = 2; - u.append_id(dxt1_encoding_table_buffer); - uniforms.push_back(u); + RID uniform_set = compress_rd->uniform_set_create(uniforms, shader.compiled, 0); + RD::ComputeListID compute_list = compress_rd->compute_list_begin(); + + compress_rd->compute_list_bind_compute_pipeline(compute_list, shader.pipeline); + compress_rd->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + + switch (shader_type) { + case BETSY_SHADER_BC6_SIGNED: + case BETSY_SHADER_BC6_UNSIGNED: { + BC6PushConstant push_constant; + push_constant.sizeX = 1.0f / width; + push_constant.sizeY = 1.0f / height; + + compress_rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(BC6PushConstant)); + compress_rd->compute_list_dispatch(compute_list, get_next_multiple(width, 32) / 32, get_next_multiple(height, 32) / 32, 1); + } break; + + case BETSY_SHADER_BC1_STANDARD: { + BC1PushConstant push_constant; + push_constant.num_refines = 2; + + compress_rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(BC1PushConstant)); + compress_rd->compute_list_dispatch(compute_list, get_next_multiple(width, 32) / 32, get_next_multiple(height, 32) / 32, 1); + } break; + + case BETSY_SHADER_BC4_UNSIGNED: { + BC4PushConstant push_constant; + push_constant.channel_idx = 0; + + compress_rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(BC4PushConstant)); + compress_rd->compute_list_dispatch(compute_list, 1, get_next_multiple(width, 16) / 16, get_next_multiple(height, 16) / 16); + } break; + + default: { + } break; + } + + compress_rd->compute_list_end(); + + if (!needs_alpha_block) { + compress_rd->submit(); + compress_rd->sync(); } } - RID uniform_set = compress_rd->uniform_set_create(uniforms, shader.compiled, 0); - RD::ComputeListID compute_list = compress_rd->compute_list_begin(); + RID *dst_texture_rid = &dst_texture_primary; - compress_rd->compute_list_bind_compute_pipeline(compute_list, shader.pipeline); - compress_rd->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + if (needs_alpha_block) { + // Set the destination texture width and size. + dst_texture_format_alpha.height = (height + 3) >> 2; + dst_texture_format_alpha.width = (width + 3) >> 2; - switch (dest_format) { - case Image::FORMAT_BPTC_RGBFU: - case Image::FORMAT_BPTC_RGBF: { - BC6PushConstant push_constant; - push_constant.sizeX = 1.0f / width; - push_constant.sizeY = 1.0f / height; - push_constant.padding[0] = 0; - push_constant.padding[1] = 0; + RID dst_texture_alpha = compress_rd->texture_create(dst_texture_format_alpha, RD::TextureView()); - compress_rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(BC6PushConstant)); - compress_rd->compute_list_dispatch(compute_list, get_next_multiple(width, 32) / 32, get_next_multiple(height, 32) / 32, 1); - } break; + { + Vector uniforms; + { + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + u.binding = 0; + u.append_id(src_sampler); + u.append_id(src_texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + u.append_id(dst_texture_alpha); + uniforms.push_back(u); + } + } - case Image::FORMAT_DXT1: { - BC1PushConstant push_constant; - push_constant.num_refines = 2; - push_constant.padding[0] = 0; - push_constant.padding[1] = 0; - push_constant.padding[2] = 0; + RID uniform_set = compress_rd->uniform_set_create(uniforms, secondary_shader.compiled, 0); + RD::ComputeListID compute_list = compress_rd->compute_list_begin(); - compress_rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(BC1PushConstant)); - compress_rd->compute_list_dispatch(compute_list, get_next_multiple(width, 32) / 32, get_next_multiple(height, 32) / 32, 1); - } break; + compress_rd->compute_list_bind_compute_pipeline(compute_list, secondary_shader.pipeline); + compress_rd->compute_list_bind_uniform_set(compute_list, uniform_set, 0); - case Image::FORMAT_RGTC_R: { BC4PushConstant push_constant; - push_constant.channel_idx = 0; - push_constant.padding[0] = 0; - push_constant.padding[1] = 0; - push_constant.padding[2] = 0; + push_constant.channel_idx = dest_format == Image::FORMAT_DXT5 ? 3 : 1; compress_rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(BC4PushConstant)); compress_rd->compute_list_dispatch(compute_list, 1, get_next_multiple(width, 16) / 16, get_next_multiple(height, 16) / 16); - } break; - default: { - } break; + compress_rd->compute_list_end(); + } + + // Stitching + + // Set the destination texture width and size. + dst_texture_format_combined.height = (height + 3) >> 2; + dst_texture_format_combined.width = (width + 3) >> 2; + + RID dst_texture_combined = compress_rd->texture_create(dst_texture_format_combined, RD::TextureView()); + + { + Vector uniforms; + { + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + u.binding = 0; + u.append_id(src_sampler); + u.append_id(dest_format == Image::FORMAT_DXT5 ? dst_texture_alpha : dst_texture_primary); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + u.binding = 1; + u.append_id(src_sampler); + u.append_id(dest_format == Image::FORMAT_DXT5 ? dst_texture_primary : dst_texture_alpha); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 2; + u.append_id(dst_texture_combined); + uniforms.push_back(u); + } + } + + RID uniform_set = compress_rd->uniform_set_create(uniforms, stitch_shader.compiled, 0); + RD::ComputeListID compute_list = compress_rd->compute_list_begin(); + + compress_rd->compute_list_bind_compute_pipeline(compute_list, stitch_shader.pipeline); + compress_rd->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + compress_rd->compute_list_dispatch(compute_list, get_next_multiple(width, 32) / 32, get_next_multiple(height, 32) / 32, 1); + + compress_rd->compute_list_end(); + + compress_rd->submit(); + compress_rd->sync(); + } + + dst_texture_rid = &dst_texture_combined; + + compress_rd->free(dst_texture_primary); + compress_rd->free(dst_texture_alpha); } - compress_rd->compute_list_end(); - - compress_rd->submit(); - compress_rd->sync(); - // Copy data from the GPU to the buffer. - const Vector texture_data = compress_rd->texture_get_data(dst_texture, 0); + const Vector texture_data = compress_rd->texture_get_data(*dst_texture_rid, 0); int64_t dst_ofs = Image::get_image_mipmap_offset(r_img->get_width(), r_img->get_height(), dest_format, i); memcpy(dst_data_ptr + dst_ofs, texture_data.ptr(), texture_data.size()); // Free the source and dest texture. - compress_rd->free(dst_texture); compress_rd->free(src_texture); + compress_rd->free(*dst_texture_rid); } src_images.clear(); @@ -516,7 +660,13 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) { // Set the compressed data to the image. r_img->set_data(r_img->get_width(), r_img->get_height(), r_img->has_mipmaps(), dest_format, dst_data); - print_verbose(vformat("Betsy: Encoding took %d ms.", OS::get_singleton()->get_ticks_msec() - start_time)); + print_verbose( + vformat("Betsy: Encoding a %dx%d image with %d mipmaps as %s took %d ms.", + r_img->get_width(), + r_img->get_height(), + r_img->get_mipmap_count(), + Image::get_format_name(dest_format), + OS::get_singleton()->get_ticks_msec() - start_time)); return OK; } @@ -560,10 +710,19 @@ Error _betsy_compress_s3tc(Image *r_img, Image::UsedChannels p_channels) { result = betsy->compress(BETSY_FORMAT_BC1, r_img); break; + case Image::USED_CHANNELS_RGBA: + case Image::USED_CHANNELS_LA: + result = betsy->compress(BETSY_FORMAT_BC3, r_img); + break; + case Image::USED_CHANNELS_R: result = betsy->compress(BETSY_FORMAT_BC4_UNSIGNED, r_img); break; + case Image::USED_CHANNELS_RG: + result = betsy->compress(BETSY_FORMAT_BC5_UNSIGNED, r_img); + break; + default: break; } diff --git a/modules/betsy/image_compress_betsy.h b/modules/betsy/image_compress_betsy.h index ab7b7858030..f3812c5c9a3 100644 --- a/modules/betsy/image_compress_betsy.h +++ b/modules/betsy/image_compress_betsy.h @@ -52,24 +52,38 @@ enum BetsyFormat { BETSY_FORMAT_BC3, BETSY_FORMAT_BC4_SIGNED, BETSY_FORMAT_BC4_UNSIGNED, + BETSY_FORMAT_BC5_SIGNED, + BETSY_FORMAT_BC5_UNSIGNED, BETSY_FORMAT_BC6_SIGNED, BETSY_FORMAT_BC6_UNSIGNED, + BETSY_FORMAT_MAX, +}; + +enum BetsyShaderType { + BETSY_SHADER_BC1_STANDARD, + BETSY_SHADER_BC1_DITHER, + BETSY_SHADER_BC4_SIGNED, + BETSY_SHADER_BC4_UNSIGNED, + BETSY_SHADER_BC6_SIGNED, + BETSY_SHADER_BC6_UNSIGNED, + BETSY_SHADER_ALPHA_STITCH, + BETSY_SHADER_MAX, }; struct BC6PushConstant { float sizeX; float sizeY; - uint32_t padding[2]; + uint32_t padding[2] = { 0 }; }; struct BC1PushConstant { uint32_t num_refines; - uint32_t padding[3]; + uint32_t padding[3] = { 0 }; }; struct BC4PushConstant { uint32_t channel_idx; - uint32_t padding[3]; + uint32_t padding[3] = { 0 }; }; void free_device(); @@ -90,7 +104,7 @@ class BetsyCompressor : public Object { // Resources shared by all compression formats. RenderingDevice *compress_rd = nullptr; RenderingContextDriver *compress_rcd = nullptr; - HashMap cached_shaders; + BetsyShader cached_shaders[BETSY_SHADER_MAX]; RID src_sampler; // Format-specific resources. @@ -101,6 +115,7 @@ class BetsyCompressor : public Object { void _thread_loop(); void _thread_exit(); + Error _get_shader(BetsyFormat p_format, const String &p_version, BetsyShader &r_shader); Error _compress(BetsyFormat p_format, Image *r_img); public: