|
@@ -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<RDShaderFile> 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<RDShaderFile> 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<RDShaderFile> 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<RDShaderFile> 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<String, BetsyShader> &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,227 +286,152 @@ 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";
|
|
|
-
|
|
|
- case BETSY_FORMAT_BC3:
|
|
|
- return "BC3";
|
|
|
-
|
|
|
- case BETSY_FORMAT_BC4_SIGNED:
|
|
|
- case BETSY_FORMAT_BC4_UNSIGNED:
|
|
|
- return "BC4";
|
|
|
-
|
|
|
- case BETSY_FORMAT_BC6_SIGNED:
|
|
|
- case BETSY_FORMAT_BC6_UNSIGNED:
|
|
|
- return "BC6";
|
|
|
-
|
|
|
- default:
|
|
|
- return "";
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) {
|
|
|
- uint64_t start_time = OS::get_singleton()->get_ticks_msec();
|
|
|
-
|
|
|
- // Return an error so that the compression can fall back to cpu compression
|
|
|
- if (compress_rd == nullptr) {
|
|
|
- return ERR_CANT_CREATE;
|
|
|
- }
|
|
|
-
|
|
|
- if (r_img->is_compressed()) {
|
|
|
- return ERR_INVALID_DATA;
|
|
|
- }
|
|
|
-
|
|
|
- Error err = OK;
|
|
|
-
|
|
|
- // Destination format.
|
|
|
- Image::Format dest_format = Image::FORMAT_MAX;
|
|
|
- RD::DataFormat dst_rd_format = RD::DATA_FORMAT_MAX;
|
|
|
-
|
|
|
- String version = "";
|
|
|
-
|
|
|
- switch (p_format) {
|
|
|
- case BETSY_FORMAT_BC1:
|
|
|
- version = "standard";
|
|
|
- dst_rd_format = RD::DATA_FORMAT_R32G32_UINT;
|
|
|
- dest_format = Image::FORMAT_DXT1;
|
|
|
- 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<RDShaderFile> 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;
|
|
|
- {
|
|
|
- src_texture_format.array_layers = 1;
|
|
|
- src_texture_format.depth = 1;
|
|
|
- src_texture_format.mipmaps = 1;
|
|
|
- src_texture_format.texture_type = RD::TEXTURE_TYPE_2D;
|
|
|
- src_texture_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
|
|
|
- }
|
|
|
-
|
|
|
+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);
|
|
|
- src_texture_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
|
|
|
+ r_format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
|
|
|
break;
|
|
|
|
|
|
case Image::FORMAT_LA8:
|
|
|
r_img->convert(Image::FORMAT_RGBA8);
|
|
|
- src_texture_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
|
|
|
+ r_format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
|
|
|
break;
|
|
|
|
|
|
case Image::FORMAT_R8:
|
|
|
- src_texture_format.format = RD::DATA_FORMAT_R8_UNORM;
|
|
|
+ r_format = RD::DATA_FORMAT_R8_UNORM;
|
|
|
break;
|
|
|
|
|
|
case Image::FORMAT_RG8:
|
|
|
- src_texture_format.format = RD::DATA_FORMAT_R8G8_UNORM;
|
|
|
+ r_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;
|
|
|
+ r_format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
|
|
|
break;
|
|
|
|
|
|
case Image::FORMAT_RGBA8:
|
|
|
- src_texture_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
|
|
|
+ r_format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
|
|
|
break;
|
|
|
|
|
|
case Image::FORMAT_RH:
|
|
|
- src_texture_format.format = RD::DATA_FORMAT_R16_SFLOAT;
|
|
|
+ r_format = RD::DATA_FORMAT_R16_SFLOAT;
|
|
|
break;
|
|
|
|
|
|
case Image::FORMAT_RGH:
|
|
|
- src_texture_format.format = RD::DATA_FORMAT_R16G16_SFLOAT;
|
|
|
+ r_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;
|
|
|
+ r_format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
|
|
|
break;
|
|
|
|
|
|
case Image::FORMAT_RGBAH:
|
|
|
- src_texture_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
|
|
|
+ r_format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
|
|
|
break;
|
|
|
|
|
|
case Image::FORMAT_RF:
|
|
|
- src_texture_format.format = RD::DATA_FORMAT_R32_SFLOAT;
|
|
|
+ r_format = RD::DATA_FORMAT_R32_SFLOAT;
|
|
|
break;
|
|
|
|
|
|
case Image::FORMAT_RGF:
|
|
|
- src_texture_format.format = RD::DATA_FORMAT_R32G32_SFLOAT;
|
|
|
+ r_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;
|
|
|
+ r_format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
|
|
|
break;
|
|
|
|
|
|
case Image::FORMAT_RGBAF:
|
|
|
- src_texture_format.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
|
|
|
+ r_format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
|
|
|
break;
|
|
|
|
|
|
case Image::FORMAT_RGBE9995:
|
|
|
- src_texture_format.format = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32;
|
|
|
+ r_format = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32;
|
|
|
break;
|
|
|
|
|
|
default: {
|
|
|
- return err;
|
|
|
+ return ERR_UNAVAILABLE;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ return OK;
|
|
|
+}
|
|
|
+
|
|
|
+Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) {
|
|
|
+ uint64_t start_time = OS::get_singleton()->get_ticks_msec();
|
|
|
+
|
|
|
+ // Return an error so that the compression can fall back to cpu compression
|
|
|
+ if (compress_rd == nullptr) {
|
|
|
+ return ERR_CANT_CREATE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (r_img->is_compressed()) {
|
|
|
+ return ERR_INVALID_DATA;
|
|
|
+ }
|
|
|
+
|
|
|
+ Error err = OK;
|
|
|
+
|
|
|
+ // Destination format.
|
|
|
+ Image::Format dest_format = BETSY_TO_IMAGE_FORMAT[p_format];
|
|
|
+ RD::DataFormat dst_rd_format = BETSY_TO_RD_FORMAT[p_format];
|
|
|
+
|
|
|
+ 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_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;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // src_texture format information.
|
|
|
+ RD::TextureFormat src_texture_format;
|
|
|
+ {
|
|
|
+ src_texture_format.array_layers = 1;
|
|
|
+ src_texture_format.depth = 1;
|
|
|
+ src_texture_format.mipmaps = 1;
|
|
|
+ src_texture_format.texture_type = RD::TEXTURE_TYPE_2D;
|
|
|
+ src_texture_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = get_src_texture_format(r_img, src_texture_format.format);
|
|
|
+
|
|
|
+ if (err != OK) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
// For the destination format just copy the source format and change the usage bits.
|
|
|
RD::TextureFormat dst_texture_format = src_texture_format;
|
|
|
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<uint8_t> 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<RD::Uniform> uniforms;
|
|
|
{
|
|
|
+ Vector<RD::Uniform> 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;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- 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_end();
|
|
|
|
|
|
- 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) {
|
|
|
+ compress_rd->submit();
|
|
|
+ compress_rd->sync();
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- 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_rid = &dst_texture_primary;
|
|
|
|
|
|
- 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;
|
|
|
+ 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;
|
|
|
|
|
|
- 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 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(BC1PushConstant));
|
|
|
- compress_rd->compute_list_dispatch(compute_list, get_next_multiple(width, 32) / 32, get_next_multiple(height, 32) / 32, 1);
|
|
|
- } break;
|
|
|
+ {
|
|
|
+ Vector<RD::Uniform> 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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_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();
|
|
|
+ }
|
|
|
|
|
|
- compress_rd->compute_list_end();
|
|
|
+ // Stitching
|
|
|
|
|
|
- compress_rd->submit();
|
|
|
- compress_rd->sync();
|
|
|
+ // 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<RD::Uniform> 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);
|
|
|
+ }
|
|
|
|
|
|
// Copy data from the GPU to the buffer.
|
|
|
- const Vector<uint8_t> texture_data = compress_rd->texture_get_data(dst_texture, 0);
|
|
|
+ const Vector<uint8_t> 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;
|
|
|
}
|