Add GpuEntity struct expansion
This commit is contained in:
parent
c30b9c0ed0
commit
5fd82000cf
3 changed files with 45 additions and 8 deletions
|
|
@ -287,34 +287,69 @@ test "update respawns entity at edge when reaching center" {
|
|||
try std.testing.expect(on_left or on_right or on_top or on_bottom);
|
||||
}
|
||||
|
||||
// GPU entity for SSBO rendering (position + color only, no velocity)
|
||||
// GPU entity for SSBO rendering (16 bytes, matches compute shader layout)
|
||||
pub const GpuEntity = extern struct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
packed_vel: i32, // vx high 16 bits, vy low 16 bits (fixed-point 8.8)
|
||||
color: u32,
|
||||
};
|
||||
|
||||
// pack two f32 velocities into a single i32 (fixed-point 8.8 format)
|
||||
pub fn packVelocity(vx: f32, vy: f32) i32 {
|
||||
const vx_fixed: i16 = @intFromFloat(std.math.clamp(vx * 256.0, -32768.0, 32767.0));
|
||||
const vy_fixed: i16 = @intFromFloat(std.math.clamp(vy * 256.0, -32768.0, 32767.0));
|
||||
return (@as(i32, vx_fixed) << 16) | (@as(i32, vy_fixed) & 0xFFFF);
|
||||
}
|
||||
|
||||
test "GpuEntity struct has correct size for SSBO" {
|
||||
// SSBO layout: x(4) + y(4) + color(4) = 12 bytes
|
||||
try std.testing.expectEqual(@as(usize, 12), @sizeOf(GpuEntity));
|
||||
// SSBO layout: x(4) + y(4) + packed_vel(4) + color(4) = 16 bytes
|
||||
try std.testing.expectEqual(@as(usize, 16), @sizeOf(GpuEntity));
|
||||
}
|
||||
|
||||
test "GpuEntity can be created from Entity" {
|
||||
const entity = Entity{
|
||||
.x = 100.0,
|
||||
.y = 200.0,
|
||||
.vx = 1.5, // ignored for GPU
|
||||
.vy = -0.5, // ignored for GPU
|
||||
.vx = 1.5,
|
||||
.vy = -0.5,
|
||||
.color = 0x00FFFF,
|
||||
};
|
||||
|
||||
const gpu_entity = GpuEntity{
|
||||
.x = entity.x,
|
||||
.y = entity.y,
|
||||
.packed_vel = packVelocity(entity.vx, entity.vy),
|
||||
.color = entity.color,
|
||||
};
|
||||
|
||||
try std.testing.expectEqual(@as(f32, 100.0), gpu_entity.x);
|
||||
try std.testing.expectEqual(@as(f32, 200.0), gpu_entity.y);
|
||||
try std.testing.expectEqual(@as(u32, 0x00FFFF), gpu_entity.color);
|
||||
|
||||
// unpack and verify velocity (should round-trip within precision)
|
||||
const vx_unpacked = @as(f32, @floatFromInt(@as(i16, @truncate(gpu_entity.packed_vel >> 16)))) / 256.0;
|
||||
const vy_unpacked = @as(f32, @floatFromInt(@as(i16, @truncate(gpu_entity.packed_vel)))) / 256.0;
|
||||
try std.testing.expectApproxEqAbs(@as(f32, 1.5), vx_unpacked, 0.004);
|
||||
try std.testing.expectApproxEqAbs(@as(f32, -0.5), vy_unpacked, 0.004);
|
||||
}
|
||||
|
||||
test "packVelocity round-trips correctly" {
|
||||
// test positive values
|
||||
const packed1 = packVelocity(2.0, 1.5);
|
||||
const vx1 = @as(f32, @floatFromInt(@as(i16, @truncate(packed1 >> 16)))) / 256.0;
|
||||
const vy1 = @as(f32, @floatFromInt(@as(i16, @truncate(packed1)))) / 256.0;
|
||||
try std.testing.expectApproxEqAbs(@as(f32, 2.0), vx1, 0.004);
|
||||
try std.testing.expectApproxEqAbs(@as(f32, 1.5), vy1, 0.004);
|
||||
|
||||
// test negative values
|
||||
const packed2 = packVelocity(-1.0, -2.5);
|
||||
const vx2 = @as(f32, @floatFromInt(@as(i16, @truncate(packed2 >> 16)))) / 256.0;
|
||||
const vy2 = @as(f32, @floatFromInt(@as(i16, @truncate(packed2)))) / 256.0;
|
||||
try std.testing.expectApproxEqAbs(@as(f32, -1.0), vx2, 0.004);
|
||||
try std.testing.expectApproxEqAbs(@as(f32, -2.5), vy2, 0.004);
|
||||
|
||||
// test zero
|
||||
const packed3 = packVelocity(0.0, 0.0);
|
||||
try std.testing.expectEqual(@as(i32, 0), packed3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@
|
|||
layout(location = 0) in vec2 position;
|
||||
layout(location = 1) in vec2 texCoord;
|
||||
|
||||
// entity data from SSBO
|
||||
// entity data from SSBO (16 bytes, matches compute shader layout)
|
||||
struct Entity {
|
||||
float x;
|
||||
float y;
|
||||
int packedVel; // vx high 16 bits, vy low 16 bits (fixed-point 8.8), unused in vertex shader
|
||||
uint color;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ pub const SsboRenderer = struct {
|
|||
rl.gl.rlSetVertexAttribute(1, 2, rl.gl.rl_float, false, 4 * @sizeOf(f32), 2 * @sizeOf(f32));
|
||||
rl.gl.rlEnableVertexAttribute(1);
|
||||
|
||||
// create SSBO for entity data (12 bytes per entity, 1M entities = 12MB)
|
||||
// create SSBO for entity data (16 bytes per entity, 1M entities = 16MB)
|
||||
const ssbo_size: u32 = @intCast(sandbox.MAX_ENTITIES * @sizeOf(sandbox.GpuEntity));
|
||||
const ssbo_id = rl.gl.rlLoadShaderBuffer(ssbo_size, null, rl.gl.rl_dynamic_draw);
|
||||
if (ssbo_id == 0) {
|
||||
|
|
@ -142,7 +142,7 @@ pub const SsboRenderer = struct {
|
|||
// flush raylib's internal render batch before our custom GL calls
|
||||
rl.gl.rlDrawRenderBatchActive();
|
||||
|
||||
// copy entity data to GPU buffer (position + color only)
|
||||
// copy entity data to GPU buffer (position + packed velocity + color)
|
||||
{
|
||||
const zone = ztracy.ZoneN(@src(), "ssbo_copy");
|
||||
defer zone.End();
|
||||
|
|
@ -150,6 +150,7 @@ pub const SsboRenderer = struct {
|
|||
self.gpu_buffer[i] = .{
|
||||
.x = entity.x,
|
||||
.y = entity.y,
|
||||
.packed_vel = sandbox.packVelocity(entity.vx, entity.vy),
|
||||
.color = entity.color,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue