3838#include " bc1.glsl.gen.h"
3939#include " bc4.glsl.gen.h"
4040#include " bc6h.glsl.gen.h"
41+ #include " rgb_to_rgba.glsl.gen.h"
4142#include " servers/display/display_server.h"
4243
4344static Mutex betsy_mutex;
@@ -220,6 +221,44 @@ void BetsyCompressor::_init() {
220221 cached_shaders[BETSY_SHADER_ALPHA_STITCH].pipeline = compress_rd->compute_pipeline_create (cached_shaders[BETSY_SHADER_ALPHA_STITCH].compiled );
221222 ERR_FAIL_COND (cached_shaders[BETSY_SHADER_ALPHA_STITCH].pipeline .is_null ());
222223 }
224+
225+ {
226+ Ref<RDShaderFile> rgb_to_rgba_shader;
227+ rgb_to_rgba_shader.instantiate ();
228+ Error err = rgb_to_rgba_shader->parse_versions_from_text (rgb_to_rgba_shader_glsl);
229+
230+ if (err != OK) {
231+ rgb_to_rgba_shader->print_errors (" Betsy RGB to RGBA shader" );
232+ }
233+
234+ // Float32.
235+ cached_shaders[BETSY_SHADER_RGB_TO_RGBA_FLOAT].compiled = compress_rd->shader_create_from_spirv (rgb_to_rgba_shader->get_spirv_stages (" version_float" ));
236+ ERR_FAIL_COND (cached_shaders[BETSY_SHADER_RGB_TO_RGBA_FLOAT].compiled .is_null ());
237+
238+ cached_shaders[BETSY_SHADER_RGB_TO_RGBA_FLOAT].pipeline = compress_rd->compute_pipeline_create (cached_shaders[BETSY_SHADER_RGB_TO_RGBA_FLOAT].compiled );
239+ ERR_FAIL_COND (cached_shaders[BETSY_SHADER_RGB_TO_RGBA_FLOAT].pipeline .is_null ());
240+
241+ // Float16.
242+ cached_shaders[BETSY_SHADER_RGB_TO_RGBA_HALF].compiled = compress_rd->shader_create_from_spirv (rgb_to_rgba_shader->get_spirv_stages (" version_half" ));
243+ ERR_FAIL_COND (cached_shaders[BETSY_SHADER_RGB_TO_RGBA_HALF].compiled .is_null ());
244+
245+ cached_shaders[BETSY_SHADER_RGB_TO_RGBA_HALF].pipeline = compress_rd->compute_pipeline_create (cached_shaders[BETSY_SHADER_RGB_TO_RGBA_HALF].compiled );
246+ ERR_FAIL_COND (cached_shaders[BETSY_SHADER_RGB_TO_RGBA_HALF].pipeline .is_null ());
247+
248+ // Unorm8.
249+ cached_shaders[BETSY_SHADER_RGB_TO_RGBA_UNORM8].compiled = compress_rd->shader_create_from_spirv (rgb_to_rgba_shader->get_spirv_stages (" version_unorm8" ));
250+ ERR_FAIL_COND (cached_shaders[BETSY_SHADER_RGB_TO_RGBA_UNORM8].compiled .is_null ());
251+
252+ cached_shaders[BETSY_SHADER_RGB_TO_RGBA_UNORM8].pipeline = compress_rd->compute_pipeline_create (cached_shaders[BETSY_SHADER_RGB_TO_RGBA_UNORM8].compiled );
253+ ERR_FAIL_COND (cached_shaders[BETSY_SHADER_RGB_TO_RGBA_UNORM8].pipeline .is_null ());
254+
255+ // Unorm16.
256+ cached_shaders[BETSY_SHADER_RGB_TO_RGBA_UNORM16].compiled = compress_rd->shader_create_from_spirv (rgb_to_rgba_shader->get_spirv_stages (" version_unorm16" ));
257+ ERR_FAIL_COND (cached_shaders[BETSY_SHADER_RGB_TO_RGBA_UNORM16].compiled .is_null ());
258+
259+ cached_shaders[BETSY_SHADER_RGB_TO_RGBA_UNORM16].pipeline = compress_rd->compute_pipeline_create (cached_shaders[BETSY_SHADER_RGB_TO_RGBA_UNORM16].compiled );
260+ ERR_FAIL_COND (cached_shaders[BETSY_SHADER_RGB_TO_RGBA_UNORM16].pipeline .is_null ());
261+ }
223262}
224263
225264void BetsyCompressor::init () {
@@ -284,7 +323,9 @@ static int get_next_multiple(int n, int m) {
284323 return n + (m - (n % m));
285324}
286325
287- static Error get_src_texture_format (Image *r_img, RD::DataFormat &r_format) {
326+ static Error get_src_texture_format (Image *r_img, RD::DataFormat &r_format, bool &r_is_rgb) {
327+ r_is_rgb = false ;
328+
288329 switch (r_img->get_format ()) {
289330 case Image::FORMAT_L8:
290331 r_img->convert (Image::FORMAT_RGBA8);
@@ -305,7 +346,7 @@ static Error get_src_texture_format(Image *r_img, RD::DataFormat &r_format) {
305346 break ;
306347
307348 case Image::FORMAT_RGB8:
308- r_img-> convert (Image::FORMAT_RGBA8) ;
349+ r_is_rgb = true ;
309350 r_format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
310351 break ;
311352
@@ -322,7 +363,7 @@ static Error get_src_texture_format(Image *r_img, RD::DataFormat &r_format) {
322363 break ;
323364
324365 case Image::FORMAT_RGBH:
325- r_img-> convert (Image::FORMAT_RGBAH) ;
366+ r_is_rgb = true ;
326367 r_format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
327368 break ;
328369
@@ -339,7 +380,7 @@ static Error get_src_texture_format(Image *r_img, RD::DataFormat &r_format) {
339380 break ;
340381
341382 case Image::FORMAT_RGBF:
342- r_img-> convert (Image::FORMAT_RGBAF) ;
383+ r_is_rgb = true ;
343384 r_format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
344385 break ;
345386
@@ -360,31 +401,14 @@ static Error get_src_texture_format(Image *r_img, RD::DataFormat &r_format) {
360401 break ;
361402
362403 case Image::FORMAT_RGB16:
363- r_img-> convert (Image::FORMAT_RGBA16) ;
404+ r_is_rgb = true ;
364405 r_format = RD::DATA_FORMAT_R16G16B16A16_UNORM;
365406 break ;
366407
367408 case Image::FORMAT_RGBA16:
368409 r_format = RD::DATA_FORMAT_R16G16B16A16_UNORM;
369410 break ;
370411
371- case Image::FORMAT_R16I:
372- r_format = RD::DATA_FORMAT_R16_UINT;
373- break ;
374-
375- case Image::FORMAT_RG16I:
376- r_format = RD::DATA_FORMAT_R16G16_UINT;
377- break ;
378-
379- case Image::FORMAT_RGB16I:
380- r_img->convert (Image::FORMAT_RGBA16I);
381- r_format = RD::DATA_FORMAT_R16G16B16A16_UINT;
382- break ;
383-
384- case Image::FORMAT_RGBA16I:
385- r_format = RD::DATA_FORMAT_R16G16B16A16_UINT;
386- break ;
387-
388412 default : {
389413 return ERR_UNAVAILABLE;
390414 }
@@ -445,7 +469,8 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) {
445469 src_texture_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
446470 }
447471
448- err = get_src_texture_format (r_img, src_texture_format.format );
472+ bool needs_rgb_to_rgba = false ;
473+ err = get_src_texture_format (r_img, src_texture_format.format , needs_rgb_to_rgba);
449474
450475 if (err != OK) {
451476 return err;
@@ -536,9 +561,79 @@ Error BetsyCompressor::_compress(BetsyFormat p_format, Image *r_img) {
536561 }
537562
538563 // Create the textures on the GPU.
539- RID src_texture = compress_rd-> texture_create (src_texture_format, RD::TextureView (), src_images) ;
564+ RID src_texture;
540565 RID dst_texture_primary = compress_rd->texture_create (dst_texture_format, RD::TextureView ());
541566
567+ if (needs_rgb_to_rgba) {
568+ // RGB textures cannot be sampled directly on most hardware, so we do a little trick involving a compute shader
569+ // which takes the input data as an SSBO and converts it directly into an RGBA image.
570+ BetsyShaderType rgb_shader_type = BETSY_SHADER_MAX;
571+
572+ switch (r_img->get_format ()) {
573+ case Image::FORMAT_RGB8:
574+ rgb_shader_type = BETSY_SHADER_RGB_TO_RGBA_UNORM8;
575+ break ;
576+ case Image::FORMAT_RGBH:
577+ rgb_shader_type = BETSY_SHADER_RGB_TO_RGBA_HALF;
578+ break ;
579+ case Image::FORMAT_RGBF:
580+ rgb_shader_type = BETSY_SHADER_RGB_TO_RGBA_FLOAT;
581+ break ;
582+ case Image::FORMAT_RGB16:
583+ rgb_shader_type = BETSY_SHADER_RGB_TO_RGBA_UNORM16;
584+ break ;
585+ default :
586+ break ;
587+ }
588+
589+ // The source 'RGB' buffer.
590+ RID source_buffer = compress_rd->storage_buffer_create (src_image_ptr[0 ].size (), src_image_ptr[0 ].span ());
591+
592+ RD::TextureFormat rgba_texture_format = src_texture_format;
593+ rgba_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;
594+ src_texture = compress_rd->texture_create (rgba_texture_format, RD::TextureView ());
595+
596+ Vector<RD::Uniform> uniforms;
597+ {
598+ {
599+ RD::Uniform u;
600+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
601+ u.binding = 0 ;
602+ u.append_id (source_buffer);
603+ uniforms.push_back (u);
604+ }
605+ {
606+ RD::Uniform u;
607+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
608+ u.binding = 1 ;
609+ u.append_id (src_texture);
610+ uniforms.push_back (u);
611+ }
612+ }
613+
614+ BetsyShader &rgb_shader = cached_shaders[rgb_shader_type];
615+
616+ RID uniform_set = compress_rd->uniform_set_create (uniforms, rgb_shader.compiled , 0 );
617+ RD::ComputeListID compute_list = compress_rd->compute_list_begin ();
618+
619+ compress_rd->compute_list_bind_compute_pipeline (compute_list, rgb_shader.pipeline );
620+ compress_rd->compute_list_bind_uniform_set (compute_list, uniform_set, 0 );
621+
622+ // Prepare the push constant with the mipmap's resolution.
623+ RGBToRGBAPushConstant push_constant;
624+ push_constant.width = width;
625+ push_constant.height = height;
626+
627+ compress_rd->compute_list_set_push_constant (compute_list, &push_constant, sizeof (RGBToRGBAPushConstant));
628+ compress_rd->compute_list_dispatch (compute_list, get_next_multiple (width, 8 ) / 8 , get_next_multiple (height, 8 ) / 8 , 1 );
629+
630+ compress_rd->compute_list_end ();
631+
632+ compress_rd->free_rid (source_buffer);
633+ } else {
634+ src_texture = compress_rd->texture_create (src_texture_format, RD::TextureView (), src_images);
635+ }
636+
542637 {
543638 Vector<RD::Uniform> uniforms;
544639 {
0 commit comments