Encode upper left ui grid quadrant to byte array and pass as push constant to compute shader

This commit is contained in:
Garrett Gunnell 2025-08-11 19:40:48 -07:00
parent 057a4e1ca5
commit bbf1224d7d
7 changed files with 97 additions and 44 deletions

View file

@ -14,8 +14,10 @@ layout(binding = 1) uniform UniformBufferObject {
};
layout(push_constant, std430) uniform Params {
vec2 raster_size;
vec2 seeds;
uint64_t _UpperLeftMask;
uint64_t _UpperRightMask;
uint64_t _LowerLeftMask;
uint64_t _LowerRightMask;
};
float pseudo(vec2 v) {
@ -26,8 +28,8 @@ float pseudo(vec2 v) {
[numthreads(8, 8, 1)]
void Seed() {
ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = ivec2(raster_size);
if (uv.x >= size.x || uv.y >= size.y) return;
vec2 seeds = vec2(0.0, 0.0);
float seed = pseudo(vec2(float(uv.x) * 0.1 + seeds.x, float(uv.y) * 0.1 + seeds.x * 3));
@ -70,25 +72,29 @@ uint sum_uint_bits(uint64_t i) {
[numthreads(8, 8, 1)]
void Automaton() {
ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = ivec2(raster_size);
if (uv.x >= size.x || uv.y >= size.y) return;
int cell = int(imageLoad(_AutomatonFrom, uv).r);
uint64_t top_row_mask = 18374686479671623680UL;
uint64_t left_column_mask = 9259542123273814144UL;
// GAME OF LIFE
// uint64_t upper_left_mask = 770;
uint64_t upper_left_mask = 69540876599103UL;
// uint64_t upper_right_mask = 49216 & ~(left_column_mask);
uint64_t upper_right_mask = 278163506396412UL & ~(left_column_mask);
// uint64_t lower_left_mask = 144959613005987840UL & ~(top_row_mask);
uint64_t lower_left_mask = 4557430888798814208UL & ~(top_row_mask);
// uint64_t lower_right_mask = 4665729213955833856UL & ~(top_row_mask | left_column_mask);
uint64_t lower_right_mask = 18229723555195256832UL & ~(top_row_mask | left_column_mask);
// BUGS
// uint64_t upper_left_mask = 69540876599103UL;
// uint64_t upper_right_mask = 278163506396412UL & ~(left_column_mask);
// uint64_t lower_left_mask = 4557430888798814208UL & ~(top_row_mask);
// uint64_t lower_right_mask = 18229723555195256832UL & ~(top_row_mask | left_column_mask);
uint64_t upper_left_mask = _UpperLeftMask;
uint64_t upper_right_mask = _UpperRightMask & ~(left_column_mask);
uint64_t lower_left_mask = _LowerLeftMask & ~(top_row_mask);
uint64_t lower_right_mask = _LowerRightMask & ~(top_row_mask | left_column_mask);
uint64_t upper_left_quadrant_bits = encode_tile_to_uint(uv) & upper_left_mask;
uint64_t upper_right_quadrant_bits = encode_tile_to_uint(uv + ivec2(7, 0)) & upper_right_mask;
@ -102,8 +108,8 @@ void Automaton() {
neighbor_count += sum_uint_bits(lower_right_quadrant_bits);
// for (int x = -neighborhood_radius; x <= neighborhood_radius; ++x) {
// for (int y = -neighborhood_radius; y <= neighborhood_radius; ++y) {
// for (int x = -1; x <= 1; ++x) {
// for (int y = -1; y <= 1; ++y) {
// if (x == 0 && y == 0) continue;
// ivec2 texcoords = uv + ivec2(x, y);
@ -113,13 +119,13 @@ void Automaton() {
// }
// }
// if (neighbor_count <= 1) cell = 0;
// if (neighbor_count == 3) cell = 1;
// if (neighbor_count >= 4) cell = 0;
if (neighbor_count <= 1) cell = 0;
if (neighbor_count == 3) cell = 1;
if (neighbor_count >= 4) cell = 0;
if (neighbor_count <= 33) cell = 0;
if (34 <= neighbor_count && neighbor_count <= 45) cell = 1;
if (58 <= neighbor_count && neighbor_count <= 121) cell = 0;
// if (neighbor_count <= 33) cell = 0;
// if (34 <= neighbor_count && neighbor_count <= 45) cell = 1;
// if (58 <= neighbor_count && neighbor_count <= 121) cell = 0;
imageStore(_AutomatonTo, uv, vec4(cell));
}
@ -127,9 +133,6 @@ void Automaton() {
[numthreads(8, 8, 1)]
void Blit() {
ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = ivec2(raster_size);
if (uv.x >= size.x || uv.y >= size.y) return;
float automata = imageLoad(_AutomatonTo, uv / 2).r;

View file

@ -4,6 +4,14 @@ class_name Neighborhood
@export var spawn_range : Vector2i
@export var stable_range : Vector2i
enum Quadrant { UPPER_LEFT, UPPER_RIGHT, LOWER_LEFT, LOWER_RIGHT }
var quadrant_strings : Array = Array([[], [], [], []])
var neighborhood_bytes : PackedByteArray = PackedByteArray()
func _init() -> void:
neighborhood_bytes.resize(32)
func get_spawn_range() -> Vector2i:
return spawn_range
@ -13,9 +21,22 @@ func get_stable_range() -> Vector2i:
return stable_range
func add_to_spawn_range(v : Vector2i):
func add_to_spawn_range(v : Vector2i) -> void:
spawn_range = spawn_range + v
func add_to_stable_range(v : Vector2i):
stable_range = stable_range + v
func add_to_stable_range(v : Vector2i) -> void:
stable_range = stable_range + v
func set_quadrant_strings(quadrant : Quadrant, byte_strings : Array) -> void:
quadrant_strings[quadrant] = Array(byte_strings)
func encode_quadrant_byte(quadrant : Quadrant, value : int, byte_offset : int) -> void:
var index = quadrant * 8 + byte_offset
neighborhood_bytes.encode_u8(index, value)
func get_neighborhood_bytes() -> PackedByteArray:
return neighborhood_bytes

View file

@ -48,7 +48,7 @@ needs_motion_vectors = false
needs_normal_roughness = false
script = ExtResource("1_vlji8")
pause = false
update_speed = 0.05
update_speed = 0.006
exposure = Vector4(2, 1, 1, 1)
metadata/_custom_type_script = "uid://drfxlavovcgta"

View file

@ -1,4 +1,4 @@
extends AcerolaPanel
func on_pressed() -> void:
get_node("../../.").encode_grid_to_bit_string()
get_node("../../.").encode_grid_to_neighborhood_bytes()

View file

@ -28,6 +28,10 @@ var timer = 0.0
var current_seed : int = 0
var needs_seeding = true
var push_constant : PackedByteArray = PackedByteArray()
var neighborhood_wizard : Node
func _init():
effect_callback_type = EFFECT_CALLBACK_TYPE_POST_TRANSPARENT
rd = RenderingServer.get_rendering_device()
@ -101,7 +105,10 @@ func _render_callback(p_effect_callback_type, p_render_data):
reseed = false
# Vulkan has a feature known as push constants which are like uniform sets but for very small amounts of data
var push_constant : PackedFloat32Array = PackedFloat32Array([size.x, size.y, current_seed, 0.0])
push_constant = neighborhood_wizard.get_quadrant()
# push_constant = PackedByteArray()
# push_constant.resize(16)
for view in range(render_scene_buffers.get_view_count()):
var input_image = render_scene_buffers.get_color_layer(view)
@ -114,7 +121,7 @@ func _render_callback(p_effect_callback_type, p_render_data):
exposure_compute.set_texture(2, previous_generation)
exposure_compute.set_texture(3, next_generation)
exposure_compute.set_uniform_buffer(1, uniform_array)
exposure_compute.set_push_constant(push_constant.to_byte_array())
exposure_compute.set_push_constant(push_constant)
# Dispatch the compute kernel
if (needs_seeding):
@ -139,3 +146,6 @@ func set_seed(new_seed : int):
func get_world_texture() -> RID:
return world_texture;
func set_push_constant(buffer : PackedByteArray):
push_constant = buffer

View file

@ -3,6 +3,8 @@ class_name GameMaster
var automata_compositor_effect : CompositorEffect
var neighborhood_wizard : Node
var current_seed : int = 0
func _ready() -> void:
@ -15,6 +17,9 @@ func _ready() -> void:
automata_compositor_effect = environment.compositor.compositor_effects[0]
neighborhood_wizard = root_node.get_node("Node3D/UI/Neighborhoods/Neighborhood 1")
automata_compositor_effect.neighborhood_wizard = neighborhood_wizard
func _process(delta: float) -> void:
pass

View file

@ -1,12 +1,16 @@
extends Node
class_name NeighborhoodWizard
var neighborhood : Neighborhood
var grid : TileMapLayer
var upper_left_quadrant : PackedByteArray
func _ready() -> void:
neighborhood = Neighborhood.new()
grid = get_node("Grid/Actual Grid")
encode_grid_to_neighborhood_bytes()
func get_spawn_range() -> Vector2i:
@ -25,29 +29,39 @@ func add_to_stable_range(v : Vector2i) -> void:
neighborhood.add_to_stable_range(v)
func encode_grid_to_bit_string() -> void:
var bit_string = ""
var full_bit_string = ""
func encode_grid_to_neighborhood_bytes() -> void:
var byte_strings = Array()
var byte_array = PackedByteArray()
byte_array.resize(8)
# Top Left Quadrant
var row = 0;
for y in range(-7, 1):
bit_string = ""
for y in range(0, 8):
var byte_string = ""
for x in range(-7, 1):
var grid_coord = Vector2i(x, y)
var grid_coord = Vector2i(x, -y)
var cell = grid.get_cell_atlas_coords(grid_coord).x
bit_string += "0" if cell == 0 else "1"
byte_string += "0" if cell == 0 else "1"
print(bit_string + " -> " + str(bit_string.bin_to_int()))
byte_array.encode_u8(row, bit_string.bin_to_int())
row += 1
full_bit_string += bit_string
byte_strings.append(byte_string)
neighborhood.encode_quadrant_byte(neighborhood.Quadrant.UPPER_LEFT, byte_string.bin_to_int(), y)
byte_array.encode_u8(y, byte_string.bin_to_int())
var full_byte_string = ""
for i in range(0, 8):
print(byte_strings[7 - i] + " -> " + str(byte_strings[7 - i].bin_to_int()))
full_byte_string += byte_strings[7 - i]
print(PackedInt64Array(byte_strings))
print(byte_array)
print(full_bit_string)
print(neighborhood.get_neighborhood_bytes())
upper_left_quadrant = PackedByteArray(byte_array)
func get_quadrant() -> PackedByteArray:
return neighborhood.get_neighborhood_bytes()