Use a shared copy of placeholder textures, tweak placeholder appearance

This reduces memory usage a bit in case multiple placeholders were
requested, e.g. when using multiple NoiseTextures with no noise property
defined.

The placeholder texture's appearance was also changed from a plain magenta
color to a checkerboard alternating between magenta and black pixels.
This makes it easier to spot when the placeholder texture ends up
being used in a complex scene (usually by accident).
The texture's dimensions remain identical to keep the physical size
identical in 2D.
This commit is contained in:
Hugo Locurcio 2024-10-18 14:28:57 +02:00
parent 04692d83cb
commit 35a20fa96a
4 changed files with 71 additions and 63 deletions

View File

@ -230,6 +230,32 @@ TextureStorage::TextureStorage() {
sdf_shader.shader_version = sdf_shader.shader.version_create(); sdf_shader.shader_version = sdf_shader.shader.version_create();
} }
// Initialize texture placeholder data for the `texture_*_placeholder_initialize()` methods.
constexpr int placeholder_size = 4;
texture_2d_placeholder = Image::create_empty(placeholder_size, placeholder_size, false, Image::FORMAT_RGBA8);
// Draw a magenta/black checkerboard pattern.
for (int i = 0; i < placeholder_size * placeholder_size; i++) {
const int x = i % placeholder_size;
const int y = i / placeholder_size;
texture_2d_placeholder->set_pixel(x, y, (x + y) % 2 == 0 ? Color(1, 0, 1) : Color(0, 0, 0));
}
texture_2d_array_placeholder.push_back(texture_2d_placeholder);
for (int i = 0; i < 6; i++) {
cubemap_placeholder.push_back(texture_2d_placeholder);
}
Ref<Image> texture_2d_placeholder_rotated;
texture_2d_placeholder_rotated.instantiate();
texture_2d_placeholder_rotated->copy_from(texture_2d_placeholder);
texture_2d_placeholder_rotated->rotate_90(CLOCKWISE);
for (int i = 0; i < 4; i++) {
// Alternate checkerboard pattern on odd layers (by using a copy that is rotated 90 degrees).
texture_3d_placeholder.push_back(i % 2 == 0 ? texture_2d_placeholder : texture_2d_placeholder_rotated);
}
#ifdef GL_API_ENABLED #ifdef GL_API_ENABLED
if (RasterizerGLES3::is_gles_over_gl()) { if (RasterizerGLES3::is_gles_over_gl()) {
glEnable(GL_PROGRAM_POINT_SIZE); glEnable(GL_PROGRAM_POINT_SIZE);
@ -1014,46 +1040,19 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) {
} }
void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) {
//this could be better optimized to reuse an existing image , done this way texture_2d_initialize(p_texture, texture_2d_placeholder);
//for now to get it working
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 1, 1));
texture_2d_initialize(p_texture, image);
} }
void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) { void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RS::TextureLayeredType p_layered_type) {
//this could be better optimized to reuse an existing image , done this way
//for now to get it working
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 1, 1));
Vector<Ref<Image>> images;
if (p_layered_type == RS::TEXTURE_LAYERED_2D_ARRAY) { if (p_layered_type == RS::TEXTURE_LAYERED_2D_ARRAY) {
images.push_back(image); texture_2d_layered_initialize(p_texture, texture_2d_array_placeholder, p_layered_type);
} else { } else {
//cube texture_2d_layered_initialize(p_texture, cubemap_placeholder, p_layered_type);
for (int i = 0; i < 6; i++) {
images.push_back(image);
}
} }
texture_2d_layered_initialize(p_texture, images, p_layered_type);
} }
void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) { void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) {
//this could be better optimized to reuse an existing image , done this way texture_3d_initialize(p_texture, Image::FORMAT_RGBA8, 4, 4, 4, false, texture_3d_placeholder);
//for now to get it working
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 1, 1));
Vector<Ref<Image>> images;
//cube
for (int i = 0; i < 4; i++) {
images.push_back(image);
}
texture_3d_initialize(p_texture, Image::FORMAT_RGBA8, 4, 4, 4, false, images);
} }
Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {

View File

@ -521,6 +521,11 @@ public:
virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override; virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_update(RID p_proxy, RID p_base) override; virtual void texture_proxy_update(RID p_proxy, RID p_base) override;
Ref<Image> texture_2d_placeholder;
Vector<Ref<Image>> texture_2d_array_placeholder;
Vector<Ref<Image>> cubemap_placeholder;
Vector<Ref<Image>> texture_3d_placeholder;
//these two APIs can be used together or in combination with the others. //these two APIs can be used together or in combination with the others.
virtual void texture_2d_placeholder_initialize(RID p_texture) override; virtual void texture_2d_placeholder_initialize(RID p_texture) override;
virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override; virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override;

View File

@ -518,6 +518,32 @@ TextureStorage::TextureStorage() {
rt_sdf.pipelines[i] = RD::get_singleton()->compute_pipeline_create(rt_sdf.shader.version_get_shader(rt_sdf.shader_version, i)); rt_sdf.pipelines[i] = RD::get_singleton()->compute_pipeline_create(rt_sdf.shader.version_get_shader(rt_sdf.shader_version, i));
} }
} }
// Initialize texture placeholder data for the `texture_*_placeholder_initialize()` methods.
constexpr int placeholder_size = 4;
texture_2d_placeholder = Image::create_empty(placeholder_size, placeholder_size, false, Image::FORMAT_RGBA8);
// Draw a magenta/black checkerboard pattern.
for (int i = 0; i < placeholder_size * placeholder_size; i++) {
const int x = i % placeholder_size;
const int y = i / placeholder_size;
texture_2d_placeholder->set_pixel(x, y, (x + y) % 2 == 0 ? Color(1, 0, 1) : Color(0, 0, 0));
}
texture_2d_array_placeholder.push_back(texture_2d_placeholder);
for (int i = 0; i < 6; i++) {
cubemap_placeholder.push_back(texture_2d_placeholder);
}
Ref<Image> texture_2d_placeholder_rotated;
texture_2d_placeholder_rotated.instantiate();
texture_2d_placeholder_rotated->copy_from(texture_2d_placeholder);
texture_2d_placeholder_rotated->rotate_90(CLOCKWISE);
for (int i = 0; i < 4; i++) {
// Alternate checkerboard pattern on odd layers (by using a copy that is rotated 90 degrees).
texture_3d_placeholder.push_back(i % 2 == 0 ? texture_2d_placeholder : texture_2d_placeholder_rotated);
}
} }
TextureStorage::~TextureStorage() { TextureStorage::~TextureStorage() {
@ -1365,46 +1391,19 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) {
//these two APIs can be used together or in combination with the others. //these two APIs can be used together or in combination with the others.
void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) {
//this could be better optimized to reuse an existing image , done this way texture_2d_initialize(p_texture, texture_2d_placeholder);
//for now to get it working
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 1, 1));
texture_2d_initialize(p_texture, image);
} }
void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RS::TextureLayeredType p_layered_type) { void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RS::TextureLayeredType p_layered_type) {
//this could be better optimized to reuse an existing image , done this way
//for now to get it working
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 1, 1));
Vector<Ref<Image>> images;
if (p_layered_type == RS::TEXTURE_LAYERED_2D_ARRAY) { if (p_layered_type == RS::TEXTURE_LAYERED_2D_ARRAY) {
images.push_back(image); texture_2d_layered_initialize(p_texture, texture_2d_array_placeholder, p_layered_type);
} else { } else {
//cube texture_2d_layered_initialize(p_texture, cubemap_placeholder, p_layered_type);
for (int i = 0; i < 6; i++) {
images.push_back(image);
}
} }
texture_2d_layered_initialize(p_texture, images, p_layered_type);
} }
void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) { void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) {
//this could be better optimized to reuse an existing image , done this way texture_3d_initialize(p_texture, Image::FORMAT_RGBA8, 4, 4, 4, false, texture_3d_placeholder);
//for now to get it working
Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 1, 1));
Vector<Ref<Image>> images;
//cube
for (int i = 0; i < 4; i++) {
images.push_back(image);
}
texture_3d_initialize(p_texture, Image::FORMAT_RGBA8, 4, 4, 4, false, images);
} }
Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {

View File

@ -520,6 +520,11 @@ public:
virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override; virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_update(RID p_proxy, RID p_base) override; virtual void texture_proxy_update(RID p_proxy, RID p_base) override;
Ref<Image> texture_2d_placeholder;
Vector<Ref<Image>> texture_2d_array_placeholder;
Vector<Ref<Image>> cubemap_placeholder;
Vector<Ref<Image>> texture_3d_placeholder;
//these two APIs can be used together or in combination with the others. //these two APIs can be used together or in combination with the others.
virtual void texture_2d_placeholder_initialize(RID p_texture) override; virtual void texture_2d_placeholder_initialize(RID p_texture) override;
virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override; virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override;