DashBoomColorClash/scenes/PixelCountComputeShader.gd

157 lines
4.1 KiB
GDScript
Raw Permalink Normal View History

# based on:
# - https://www.youtube.com/watch?v=5CKvGYqagyI
# - https://pastebin.com/wXCqk1nQ
class_name PixelCountComputeShader extends Node
@export var paint_viewport_path: NodePath = NodePath()
var red_score := 0
var blue_score := 0
var pink_score := 0
var thread: Thread
var semaphore: Semaphore
var mutex: Mutex
var exit := false
var paint_texture: ViewportTexture
var paint_viewport: Viewport
var rd: RenderingDevice
var shader
var pipeline
var pixel_count_result_buffer
var pixel_count_uniform
var v_tex
var samp
var tex_uniform
var uniform_set
func _ready():
paint_viewport = get_node(paint_viewport_path)
paint_texture = paint_viewport.get_texture()
mutex = Mutex.new()
semaphore = Semaphore.new()
thread = Thread.new()
thread.start(_thread_calculate_score)
# We will be using our own RenderingDevice to handle the compute commands
rd = RenderingServer.create_local_rendering_device()
# Create shader and pipeline
var shader_file = load("res://materials/shader/PixelCountCompute.glsl")
var shader_spirv = shader_file.get_spirv()
shader = rd.shader_create_from_spirv(shader_spirv)
pipeline = rd.compute_pipeline_create(shader)
var pb = PackedInt32Array([0,0,0])
var pbb = pb.to_byte_array()
pixel_count_result_buffer = rd.storage_buffer_create(pbb.size(), pbb)
pixel_count_uniform = RDUniform.new()
pixel_count_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
pixel_count_uniform.binding = 0
pixel_count_uniform.add_id(pixel_count_result_buffer)
var img = paint_texture.get_image()
var ta = img.get_data()
var fmt = RDTextureFormat.new()
fmt.width = paint_texture.get_width()
fmt.height = paint_texture.get_height()
fmt.usage_bits = RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT | RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT
fmt.format = RenderingDevice.DATA_FORMAT_R8G8B8A8_SRGB
v_tex = rd.texture_create(fmt, RDTextureView.new(), [ta])
var samp_state = RDSamplerState.new()
samp_state.unnormalized_uvw = true
samp = rd.sampler_create(samp_state)
tex_uniform = RDUniform.new()
tex_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE
tex_uniform.binding = 1
tex_uniform.add_id(samp)
tex_uniform.add_id(v_tex)
uniform_set = rd.uniform_set_create([pixel_count_uniform, tex_uniform], shader, 0)
func recalculate_score():
await RenderingServer.frame_post_draw
semaphore.post()
func get_blue_score() -> int:
var result := blue_score
return result
func get_red_score() -> int:
var result := red_score
return result
func _thread_calculate_score():
while true:
semaphore.wait()
mutex.lock()
var should_exit := exit
mutex.unlock()
if should_exit:
break
mutex.lock()
var image := paint_texture.get_image()
#var image := paint_viewport.get_texture()
mutex.unlock()
var size = image.get_size()
var ta = image.get_data()
rd.texture_update(v_tex, 0, ta)
# Start compute list to start recording our compute commands
var compute_list = rd.compute_list_begin()
# Bind the pipeline, this tells the GPU what shader to use
rd.compute_list_bind_compute_pipeline(compute_list, pipeline)
# Binds the uniform set with the data we want to give our shader
rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0)
# Dispatch 1x1x1 (XxYxZ) work groups
rd.compute_list_dispatch(compute_list, 1024/4, 1024/4, 1)
#rd.compute_list_add_barrier(compute_list)
# Tell the GPU we are done with this compute task
rd.compute_list_end()
# Force the GPU to start our commands
rd.submit()
# Force the CPU to wait for the GPU to finish with the recorded commands
rd.sync()
mutex.lock()
# Now we can grab our data from the storage buffer
var byte_data = rd.buffer_get_data(pixel_count_result_buffer)
var output := byte_data.to_int32_array()
red_score = floor(output[0]/100)
blue_score = floor(output[1]/100)
pink_score = floor(output[2]/100)
var pb = PackedInt32Array([0,0,0])
var pbb = pb.to_byte_array()
rd.buffer_update(pixel_count_result_buffer, 0, pbb.size(), pbb)
mutex.unlock()
func _exit_tree():
mutex.lock()
exit = true
mutex.unlock()
semaphore.post()
thread.wait_to_finish()