generated from krampus/template-godot4
Clear total calculation is now multithreaded.
Note: This relies on the rendering thread model changed to "Threaded", which is an experimental feature. This may cause big problems in the future!! See: https://docs.godotengine.org/en/stable/tutorials/performance/thread_safe_apis.html#rendering
This commit is contained in:
parent
26ba514eb4
commit
c676393e6d
@ -110,3 +110,7 @@ interact={
|
||||
3d_render/layer_2="HUD"
|
||||
3d_physics/layer_1="Physics"
|
||||
3d_physics/layer_2="Interactive"
|
||||
|
||||
[rendering]
|
||||
|
||||
driver/threads/thread_model=2
|
||||
|
@ -27,13 +27,6 @@ func enable() -> void:
|
||||
interactive.enabled = true
|
||||
|
||||
|
||||
func _on_gunk_body_painted() -> void:
|
||||
if not enabled:
|
||||
var clear_total := gunk_body.get_clear_total()
|
||||
if clear_total >= CLEAR_THRESHOLD:
|
||||
enable()
|
||||
|
||||
|
||||
func _activate() -> void:
|
||||
animation_player.play("activate")
|
||||
activated.emit()
|
||||
@ -43,3 +36,8 @@ func _activate() -> void:
|
||||
|
||||
func _animation_finished(_anim_name: StringName) -> void:
|
||||
interactive.enabled = true
|
||||
|
||||
|
||||
func _on_gunk_body_clear_total_updated(clear_total: float) -> void:
|
||||
if not enabled and clear_total >= CLEAR_THRESHOLD:
|
||||
enable()
|
||||
|
@ -241,5 +241,5 @@ script = ExtResource("10_qw6jt")
|
||||
shape = SubResource("BoxShape3D_6maql")
|
||||
|
||||
[connection signal="animation_finished" from="AnimationPlayer" to="." method="_animation_finished"]
|
||||
[connection signal="painted" from="GunkBody" to="." method="_on_gunk_body_painted"]
|
||||
[connection signal="clear_total_updated" from="GunkBody" to="." method="_on_gunk_body_clear_total_updated"]
|
||||
[connection signal="activated" from="Interactive" to="." method="_activate"]
|
||||
|
@ -1,12 +1,14 @@
|
||||
class_name GunkBody extends StaticBody3D
|
||||
## StaticBody3D with an associated "gunkable" mesh.
|
||||
|
||||
signal painted
|
||||
## Emitted from the main thread after the clear total is asynchronously updated.
|
||||
signal clear_total_updated(clear_total: float)
|
||||
|
||||
const CONTINUITY_LIMIT := 16
|
||||
const BUFFER_LIMIT := 3
|
||||
const FACE_EPSILON := 0.01
|
||||
const MASK_COLOR := Color.RED
|
||||
const CLEAR_TOTAL_EPSILON := 1
|
||||
|
||||
@export var mask_dim := 256
|
||||
@export var mesh_instance: MeshInstance3D
|
||||
@ -20,7 +22,14 @@ var _multiline_buffer := PackedVector2Array()
|
||||
var _multiline_width := 1.0
|
||||
|
||||
var _clear_total := 0.0
|
||||
var _clear_total_dirty := true
|
||||
var _prev_clear_total := 0.0
|
||||
|
||||
# _clear_total is async computed in separate thread
|
||||
var _mask_tx: Texture2D
|
||||
var _thread: Thread
|
||||
var _mutex: Mutex = Mutex.new()
|
||||
var _semaphore: Semaphore = Semaphore.new()
|
||||
var _exit_thread := false
|
||||
|
||||
@onready var mesh: ArrayMesh = mesh_instance.mesh
|
||||
@onready var gunk_mat: ShaderMaterial = mesh_instance.mesh.surface_get_material(0).next_pass
|
||||
@ -36,6 +45,56 @@ func _ready() -> void:
|
||||
gunk_mat.set_shader_parameter("gunk_mask", mask_viewport.get_texture())
|
||||
meshtool.create_from_surface(mesh, 0)
|
||||
|
||||
_thread = Thread.new()
|
||||
_thread.start(_async_compute_clear_total)
|
||||
|
||||
|
||||
func _trigger_recompute_deferred() -> void:
|
||||
_mutex.lock()
|
||||
_mask_tx = mask_viewport.get_texture()
|
||||
#_mask_img = mask_viewport.get_texture().get_image()
|
||||
_mutex.unlock()
|
||||
_semaphore.post()
|
||||
|
||||
|
||||
func _async_compute_clear_total() -> void:
|
||||
while true:
|
||||
_semaphore.wait()
|
||||
|
||||
# check exit flag
|
||||
_mutex.lock()
|
||||
var exiting := _exit_thread
|
||||
_mutex.unlock()
|
||||
|
||||
if exiting:
|
||||
break
|
||||
|
||||
# Get mask from GPU
|
||||
# TODO: does this need mutex protection?
|
||||
_mutex.lock()
|
||||
var mask_img := _mask_tx.get_image()
|
||||
_mutex.unlock()
|
||||
mask_img.convert(Image.FORMAT_R8)
|
||||
var px_data := mask_img.get_data()
|
||||
var px_sum := 0
|
||||
for px in px_data:
|
||||
px_sum += px
|
||||
var new_total := px_sum / 255.0
|
||||
|
||||
# Write total
|
||||
_mutex.lock()
|
||||
_clear_total = new_total
|
||||
_mutex.unlock()
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
# Flag processing flag to cleanly exit
|
||||
_mutex.lock()
|
||||
_exit_thread = true
|
||||
_mutex.unlock()
|
||||
_semaphore.post()
|
||||
_thread.wait_to_finish()
|
||||
|
||||
|
||||
func clear_all() -> void:
|
||||
mask_control.queue_draw(
|
||||
@ -51,16 +110,12 @@ func clear_all() -> void:
|
||||
## This will use a cached result unless the mask has been painted since the last calculation.
|
||||
## Be aware that cache misses are potentially quite expensive.
|
||||
func get_clear_total() -> float:
|
||||
if _clear_total_dirty:
|
||||
var mask_img := mask_viewport.get_texture().get_image()
|
||||
mask_img.convert(Image.FORMAT_R8)
|
||||
var px_data := mask_img.get_data()
|
||||
var px_sum := 0
|
||||
for px in px_data:
|
||||
px_sum += px
|
||||
_clear_total = px_sum / 255.0
|
||||
_clear_total_dirty = false
|
||||
return _clear_total
|
||||
# Protect with mutex
|
||||
_mutex.lock()
|
||||
var total := _clear_total
|
||||
_mutex.unlock()
|
||||
|
||||
return total
|
||||
|
||||
|
||||
## Transform cartesian coordinates to barycentric wrt the given triangle.
|
||||
@ -182,6 +237,14 @@ func add_to_multiline(
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
# Check if processing thread has updated the clear total
|
||||
_mutex.lock()
|
||||
var new_total := _clear_total
|
||||
_mutex.unlock()
|
||||
if abs(new_total - _prev_clear_total) > CLEAR_TOTAL_EPSILON:
|
||||
clear_total_updated.emit(new_total)
|
||||
_prev_clear_total = new_total
|
||||
|
||||
# If paint_continuous wasn't called last frame, stop the current polyline.
|
||||
if not _continued_paint_this_frame:
|
||||
_polyline_buffer = []
|
||||
@ -199,5 +262,5 @@ func _process(_delta: float) -> void:
|
||||
|
||||
|
||||
func _on_mask_painted() -> void:
|
||||
_clear_total_dirty = true
|
||||
painted.emit()
|
||||
# XXX any problem with posting each frame?
|
||||
call_deferred("_trigger_recompute_deferred")
|
||||
|
Loading…
x
Reference in New Issue
Block a user