Skip to content

Commit efe0ee2

Browse files
committed
Add NoiseTextureGenerator singleton
Adds NoiseTextureGenerator singleton to facilitate the baking of noise textures using the WorkerThreadPool.
1 parent 149a4b4 commit efe0ee2

9 files changed

+610
-133
lines changed

doc/classes/@GlobalScope.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,6 +1611,9 @@
16111611
<member name="NavigationServer3D" type="NavigationServer3D" setter="" getter="">
16121612
The [NavigationServer3D] singleton.
16131613
</member>
1614+
<member name="NoiseTextureGenerator" type="NoiseTextureGenerator" setter="" getter="">
1615+
The [NoiseTextureGenerator] singleton.
1616+
</member>
16141617
<member name="OS" type="OS" setter="" getter="">
16151618
The [OS] singleton.
16161619
</member>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="NoiseTextureGenerator" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
3+
<brief_description>
4+
Singleton to facilitate the baking and updating of noise textures.
5+
</brief_description>
6+
<description>
7+
Baking and updating noise textures is a performance costly operation best done behind a loading screen or using background threads.
8+
Each bake function is available as a single-threaded version and as an async task version that uses the [WorkerThreadPool].
9+
10+
The single-threaded version will be executed instant but block the main thread until the baking of the noise image(s) is finished.
11+
The async version will use a background thread to do the baking and may take multiple frames until finished and synchronized back with the main thread.
12+
Noise textures that finished their bake using async threads are synced on the [signal RenderingServer.frame_pre_draw] signal.
13+
14+
On platforms that do not support threads (e.g. web) the async functions automatically do a fallback to the single-threaded function equivalent.
15+
Be aware of such fallback as spawning or updating noise textures at runtime without thread support can easily impact a projects frame rate.
16+
17+
Noise textures that are already baking can not be queued again for another bake until the current bake is finished.
18+
Use [method is_noise_texture_2d_baking] and [method is_noise_texture_3d_baking] to check the bake state of a noise texture before queuing them.
19+
Be aware that most noise textures queue themself automatically for an update when changing their properties but do so deferred with a delay.
20+
21+
Avoid connecting signals and callbacks to functions that immediately rebake a noise texture on a callback from the generator, or one of the emitted resource changed signals.
22+
Such signal spaghetti can cause a deadlock due to often creating an endless loop of baking and signal emit.
23+
</description>
24+
<tutorials>
25+
</tutorials>
26+
<methods>
27+
<method name="bake_noise_texture_2d" qualifiers="static">
28+
<return type="void" />
29+
<param index="0" name="noise_texture" type="NoiseTexture2D" />
30+
<description>
31+
Bakes the provided [param noise_texture] with the provided [param bake_function].
32+
</description>
33+
</method>
34+
<method name="bake_noise_texture_2d_async" qualifiers="static">
35+
<return type="void" />
36+
<param index="0" name="noise_texture" type="NoiseTexture2D" />
37+
<param index="1" name="callback" type="Callable" default="Callable()" />
38+
<description>
39+
Bakes the provided [param noise_texture] with the provided [param bake_function] as an async task running on a background thread. After the process is finished the optional [param callback] will be called.
40+
</description>
41+
</method>
42+
<method name="bake_noise_texture_3d" qualifiers="static">
43+
<return type="void" />
44+
<param index="0" name="noise_texture" type="NoiseTexture3D" />
45+
<description>
46+
Bakes the provided [param noise_texture] with the provided [param bake_function].
47+
</description>
48+
</method>
49+
<method name="bake_noise_texture_3d_async" qualifiers="static">
50+
<return type="void" />
51+
<param index="0" name="noise_texture" type="NoiseTexture3D" />
52+
<param index="1" name="callback" type="Callable" default="Callable()" />
53+
<description>
54+
Bakes the provided [param noise_texture] with the provided [param bake_function] as an async task running on a background thread. After the process is finished the optional [param callback] will be called.
55+
</description>
56+
</method>
57+
<method name="is_noise_texture_2d_baking" qualifiers="static">
58+
<return type="bool" />
59+
<param index="0" name="noise_texture" type="NoiseTexture2D" />
60+
<description>
61+
Returns [code]true[/code] when the provided noise texture is being baked on a background thread.
62+
</description>
63+
</method>
64+
<method name="is_noise_texture_3d_baking" qualifiers="static">
65+
<return type="bool" />
66+
<param index="0" name="noise_texture" type="NoiseTexture3D" />
67+
<description>
68+
Returns [code]true[/code] when the provided noise texture is being baked on a background thread.
69+
</description>
70+
</method>
71+
</methods>
72+
</class>

modules/noise/noise_texture_2d.cpp

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "noise_texture_2d.h"
3232

3333
#include "noise.h"
34+
#include "noise_texture_generator.h"
3435

3536
NoiseTexture2D::NoiseTexture2D() {
3637
noise = Ref<Noise>();
@@ -43,9 +44,6 @@ NoiseTexture2D::~NoiseTexture2D() {
4344
if (texture.is_valid()) {
4445
RS::get_singleton()->free(texture);
4546
}
46-
if (noise_thread.is_started()) {
47-
noise_thread.wait_to_finish();
48-
}
4947
}
5048

5149
void NoiseTexture2D::_bind_methods() {
@@ -113,34 +111,13 @@ void NoiseTexture2D::_validate_property(PropertyInfo &p_property) const {
113111
}
114112
}
115113

116-
void NoiseTexture2D::_set_texture_image(const Ref<Image> &p_image) {
117-
image = p_image;
118-
if (image.is_valid()) {
119-
if (texture.is_valid()) {
120-
RID new_texture = RS::get_singleton()->texture_2d_create(p_image);
121-
RS::get_singleton()->texture_replace(texture, new_texture);
122-
} else {
123-
texture = RS::get_singleton()->texture_2d_create(p_image);
124-
}
125-
RS::get_singleton()->texture_set_path(texture, get_path());
126-
}
127-
emit_changed();
128-
}
129-
130-
void NoiseTexture2D::_thread_done(const Ref<Image> &p_image) {
131-
_set_texture_image(p_image);
132-
noise_thread.wait_to_finish();
114+
void NoiseTexture2D::_bake_finished() {
133115
if (regen_queued) {
134-
noise_thread.start(_thread_function, this);
116+
NoiseTextureGenerator::get_singleton()->bake_noise_texture_2d_async(this, callable_mp(this, &NoiseTexture2D::_bake_finished));
135117
regen_queued = false;
136118
}
137119
}
138120

139-
void NoiseTexture2D::_thread_function(void *p_ud) {
140-
NoiseTexture2D *tex = static_cast<NoiseTexture2D *>(p_ud);
141-
callable_mp(tex, &NoiseTexture2D::_thread_done).call_deferred(tex->_generate_texture());
142-
}
143-
144121
void NoiseTexture2D::_queue_update() {
145122
if (update_queued) {
146123
return;
@@ -150,8 +127,7 @@ void NoiseTexture2D::_queue_update() {
150127
callable_mp(this, &NoiseTexture2D::_update_texture).call_deferred();
151128
}
152129

153-
Ref<Image> NoiseTexture2D::_generate_texture() {
154-
// Prevent memdelete due to unref() on other thread.
130+
Ref<Image> NoiseTexture2D::bake_noise_data() {
155131
Ref<Noise> ref_noise = noise;
156132

157133
if (ref_noise.is_null()) {
@@ -178,6 +154,20 @@ Ref<Image> NoiseTexture2D::_generate_texture() {
178154
return new_image;
179155
}
180156

157+
void NoiseTexture2D::set_data(const Ref<Image> &p_data) {
158+
image = p_data;
159+
if (image.is_valid()) {
160+
if (texture.is_valid()) {
161+
RID new_texture = RS::get_singleton()->texture_2d_create(image);
162+
RS::get_singleton()->texture_replace(texture, new_texture);
163+
} else {
164+
texture = RS::get_singleton()->texture_2d_create(image);
165+
}
166+
RS::get_singleton()->texture_set_path(texture, get_path());
167+
}
168+
emit_changed();
169+
}
170+
181171
Ref<Image> NoiseTexture2D::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradient> p_gradient) {
182172
int width = p_image->get_width();
183173
int height = p_image->get_height();
@@ -196,25 +186,16 @@ Ref<Image> NoiseTexture2D::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradi
196186
}
197187

198188
void NoiseTexture2D::_update_texture() {
199-
bool use_thread = true;
200-
#ifndef THREADS_ENABLED
201-
use_thread = false;
202-
#endif
203189
if (first_time) {
204-
use_thread = false;
205190
first_time = false;
206-
}
207-
if (use_thread) {
208-
if (!noise_thread.is_started()) {
209-
noise_thread.start(_thread_function, this);
191+
NoiseTextureGenerator::get_singleton()->bake_noise_texture_2d(this);
192+
} else {
193+
if (!NoiseTextureGenerator::get_singleton()->is_noise_texture_2d_baking(this)) {
194+
NoiseTextureGenerator::get_singleton()->bake_noise_texture_2d_async(this, callable_mp(this, &NoiseTexture2D::_bake_finished));
210195
regen_queued = false;
211196
} else {
212197
regen_queued = true;
213198
}
214-
215-
} else {
216-
Ref<Image> new_image = _generate_texture();
217-
_set_texture_image(new_image);
218199
}
219200
update_queued = false;
220201
}

modules/noise/noise_texture_2d.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ class NoiseTexture2D : public Texture2D {
4141
private:
4242
Ref<Image> image;
4343

44-
Thread noise_thread;
45-
4644
bool first_time = true;
4745
bool update_queued = false;
4846
bool regen_queued = false;
@@ -63,13 +61,10 @@ class NoiseTexture2D : public Texture2D {
6361
Ref<Gradient> color_ramp;
6462
Ref<Noise> noise;
6563

66-
void _thread_done(const Ref<Image> &p_image);
67-
static void _thread_function(void *p_ud);
64+
void _bake_finished();
6865

6966
void _queue_update();
70-
Ref<Image> _generate_texture();
7167
void _update_texture();
72-
void _set_texture_image(const Ref<Image> &p_image);
7368

7469
Ref<Image> _modulate_with_gradient(Ref<Image> p_image, Ref<Gradient> p_gradient);
7570

@@ -117,6 +112,9 @@ class NoiseTexture2D : public Texture2D {
117112
virtual RID get_rid() const override;
118113
virtual bool has_alpha() const override { return false; }
119114

115+
virtual Ref<Image> bake_noise_data();
116+
void set_data(const Ref<Image> &p_data);
117+
120118
virtual Ref<Image> get_image() const override;
121119

122120
NoiseTexture2D();

0 commit comments

Comments
 (0)