Last active
August 7, 2024 23:07
-
-
Save partybusiness/17321fb20c5d3b8429ea97acedf64717 to your computer and use it in GitHub Desktop.
New version of Conway's Game of Life for Godot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[compute] | |
#version 450 | |
// Invocations in the (x, y, z) dimension | |
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; | |
layout(r32f, set = 0, binding = 0) uniform restrict image2D input_image; | |
layout(push_constant, std430) uniform Params { | |
uint width; | |
} size_data; | |
void main() { | |
uint width = size_data.width; | |
ivec2 uv = ivec2(gl_GlobalInvocationID.xy); | |
uint count = 0; | |
count += uint(round(imageLoad(input_image, ivec2(uv.x,(uv.y + 1)%width)).r)); | |
count += uint(round(imageLoad(input_image, ivec2(uv.x,(uv.y - 1 + width)%width)).r)); | |
count += uint(round(imageLoad(input_image, ivec2((uv.x + 1)%width,uv.y)).r)); | |
count += uint(round(imageLoad(input_image, ivec2((uv.x - 1 + width)%width,uv.y)).r)); | |
count += uint(round(imageLoad(input_image, ivec2((uv.x + 1)%width,(uv.y + 1)%width)).r)); | |
count += uint(round(imageLoad(input_image, ivec2((uv.x - 1 + width)%width,(uv.y + 1)%width)).r)); | |
count += uint(round(imageLoad(input_image, ivec2((uv.x - 1 + width)%width,(uv.y - 1 + width)%width)).r)); | |
count += uint(round(imageLoad(input_image, ivec2((uv.x + 1)%width,(uv.y - 1 + width)%width)).r)); | |
float result = 0.0; | |
if ((count < 2) || (count > 3)) { | |
} else { | |
if (count == 3) { | |
result = 1.0; | |
} else { | |
result = round(imageLoad(input_image, uv).r); //stays the same | |
} | |
} | |
imageStore(input_image, uv, vec4(result, 0.0, 0.0, 1.0)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[compute] | |
#version 450 | |
// Invocations in the (x, y, z) dimension | |
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; | |
layout(r32f, set = 0, binding = 0) uniform restrict image2D input_image; | |
layout(push_constant, std430) uniform Params { | |
uint tx; | |
uint ty; | |
} params; | |
// The code we want to execute in each invocation | |
void main() { | |
ivec2 uv = ivec2(gl_GlobalInvocationID.xy); | |
if (params.tx == uv.x && params.ty == uv.y) | |
imageStore(input_image, uv, vec4(1.0 - imageLoad(input_image, uv).r , 1.0, 1.0, 1.0)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extends TextureRect | |
@export var display_material:ShaderMaterial | |
var rd:RenderingDevice # = RenderingServer.create_local_rendering_device() | |
var buffer:RID | |
var pipeline:RID | |
var uniform_set:RID | |
var draw_pipeline:RID | |
var draw_uniform_set:RID | |
var counter:float = 0.5 | |
var display_texture:Texture2DRD | |
var img_width:int = 64 | |
var image_width_constant : PackedInt32Array | |
var test_mode:int = 0 | |
var running:bool = true | |
func _ready(): | |
rd = RenderingServer.get_rendering_device() | |
# Load GLSL shader | |
var compute_shader_file:RDShaderFile = load("res://life/compute_life.glsl") | |
var shader_spirv:RDShaderSPIRV = compute_shader_file.get_spirv() | |
var shader:RID = rd.shader_create_from_spirv(shader_spirv) | |
#create image width constant for compute shader | |
image_width_constant = PackedInt32Array() | |
image_width_constant.push_back(img_width) | |
image_width_constant.push_back(0) #padding | |
image_width_constant.push_back(0) | |
image_width_constant.push_back(0) | |
#create texture | |
var tf : RDTextureFormat = RDTextureFormat.new() | |
tf.format = RenderingDevice.DATA_FORMAT_R32_SFLOAT | |
tf.texture_type = RenderingDevice.TEXTURE_TYPE_2D | |
tf.width = img_width | |
tf.height = img_width | |
tf.depth = 1 | |
tf.array_layers = 1 | |
tf.mipmaps = 1 | |
tf.usage_bits = RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT + RenderingDevice.TEXTURE_USAGE_COLOR_ATTACHMENT_BIT + RenderingDevice.TEXTURE_USAGE_STORAGE_BIT + RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT | |
display_texture = Texture2DRD.new() | |
display_texture.texture_rd_rid = rd.texture_create(tf, RDTextureView.new(), []) | |
#assign texture to display | |
texture = display_texture | |
#get uniform with texture ready for shaders | |
var uniform = RDUniform.new() | |
uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE | |
uniform.binding = 0 | |
uniform.add_id(display_texture.texture_rd_rid) | |
uniform_set = rd.uniform_set_create([uniform], shader, 0) | |
# Create a compute pipeline | |
pipeline = rd.compute_pipeline_create(shader) | |
#set up drawing | |
var draw_shader:RID = rd.shader_create_from_spirv(load("res://life/draw_dot.glsl").get_spirv()) | |
uniform = RDUniform.new() | |
uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE | |
uniform.binding = 0 | |
uniform.add_id(display_texture.texture_rd_rid) | |
draw_uniform_set = rd.uniform_set_create([uniform], draw_shader, 0) | |
draw_pipeline = rd.compute_pipeline_create(draw_shader) | |
#initial randomization | |
var random_shader:RID = rd.shader_create_from_spirv(load("res://life/randomize_life.glsl").get_spirv()) | |
var randuniform = RDUniform.new() | |
randuniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE | |
randuniform.binding = 0 | |
randuniform.add_id(display_texture.texture_rd_rid) | |
var randuniform_set = rd.uniform_set_create([uniform], random_shader, 0) | |
var randpipeline = rd.compute_pipeline_create(random_shader) | |
var compute_list := rd.compute_list_begin() | |
rd.compute_list_bind_compute_pipeline(compute_list, randpipeline) | |
rd.compute_list_bind_uniform_set(compute_list, randuniform_set, 0) | |
rd.compute_list_dispatch(compute_list, img_width/8, img_width/8, 1) | |
rd.compute_list_end() | |
func _input(event): | |
if Input.is_action_just_pressed("Pause"): | |
running = !running | |
if !running && Input.is_action_just_pressed("Draw"): | |
var mouse_pos = get_viewport().get_mouse_position() | |
mouse_pos.x = int(mouse_pos.x * img_width / size.x) | |
mouse_pos.y = int(mouse_pos.y * img_width / size.y) | |
draw_dot(mouse_pos.x, mouse_pos.y) | |
if Input.is_action_just_pressed("Tick"): | |
run_tick() | |
func _process(delta): | |
if !running: | |
return | |
if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): | |
var mouse_pos = get_viewport().get_mouse_position() | |
mouse_pos.x = int(mouse_pos.x * img_width / size.x) | |
mouse_pos.y = int(mouse_pos.y * img_width / size.y) | |
draw_dot(mouse_pos.x, mouse_pos.y) | |
counter-=delta | |
if counter<0: | |
counter += 0.1 | |
run_tick() | |
func run_tick(): | |
#run one tick of the game | |
var compute_list := rd.compute_list_begin() | |
rd.compute_list_bind_compute_pipeline(compute_list, pipeline) | |
rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0) | |
rd.compute_list_set_push_constant(compute_list, image_width_constant.to_byte_array(), image_width_constant.size() * 4) | |
rd.compute_list_dispatch(compute_list, img_width/8, img_width/8, 1) | |
rd.compute_list_end() | |
func draw_dot(x:int, y:int): | |
#flips the given pixel in the shader | |
var push_constant : PackedInt32Array = PackedInt32Array() | |
push_constant.push_back(x) | |
push_constant.push_back(y) | |
push_constant.push_back(0) #padding to multiple of 16? skipping these will result in error: | |
push_constant.push_back(0) # "this compute pipeline requires (16) bytes of push constant data" | |
# but it's clearly using only the first 8 bytes | |
var compute_list := rd.compute_list_begin() | |
rd.compute_list_bind_compute_pipeline(compute_list, draw_pipeline) | |
rd.compute_list_bind_uniform_set(compute_list, draw_uniform_set, 0) | |
rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(), push_constant.size() * 4) | |
rd.compute_list_dispatch(compute_list, img_width/8, img_width/8, 1) | |
rd.compute_list_end() | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
shader_type canvas_item; | |
void fragment() { | |
COLOR.rgb = vec3(step(0.5,texture(TEXTURE,UV + 0.5).r)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[compute] | |
#version 450 | |
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; | |
layout(r32f, set = 0, binding = 0) uniform restrict image2D output_image; | |
float random (vec2 uv) { | |
return fract(sin(dot(uv.xy, | |
vec2(12.9898,78.233))) * 43758.5453123); | |
} | |
void main() { | |
ivec2 uv = ivec2(gl_GlobalInvocationID.xy); | |
float r = random(uv); | |
imageStore(output_image, uv, vec4(r, 0.8, 1.0, 1.0)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I had done a Conway's before to learn about compute shaders, but then I heard about Texture2DRD which enables multiple shaders to reference the same texture just through RID, which means it can stay on the GPU. So I rewrote it to take advantage of that.