package gpu_tests import "../renderer" import "core:math" import glsl "core:math/linalg/glsl" import "core:testing" @(test) test_shadow_ubo_size :: proc(t: ^testing.T) { // Shadow UBO: 256 light_matrices + 16 shadow_count + 16 shadow_params // + 16 shadow_light_indices + 64 atlas_regions = 368 bytes data, // padded by Odin's struct alignment (mat4x4 array -> 64-byte align) ubo_size := size_of(renderer.Shadow_UBO_Data) // Must be >= 368 bytes (may be larger due to alignment) testing.expect(t, ubo_size >= 368, "shadow UBO must be at least 368 bytes") } @(test) test_shadow_push_constants_size :: proc(t: ^testing.T) { // Shadow push constants: 2x mat4 = 128 bytes testing.expect_value(t, size_of(renderer.Push_Constants_Shadow), 128) } @(test) test_light_matrix_computation :: proc(t: ^testing.T) { // Test that compute_light_matrix produces valid matrices light_dir := glsl.vec3{0, -1, 0} // sun straight down camera_target := glsl.vec3{0, 0, 0} half_extent: f32 = 10.0 depth: f32 = 20.0 mat := renderer.compute_light_matrix(light_dir, camera_target, half_extent, depth) // Result should be non-zero (valid matrix) all_zero := true for r in 0 ..< 4 { for c in 0 ..< 4 { if mat[r, c] != 0 { all_zero = false } } } testing.expect(t, !all_zero, "light matrix should not be all zeros") // The matrix should transform the camera target to somewhere near the center target_h := glsl.vec4{0, 0, 0, 1} result := mat * target_h // After projection, the result should be finite testing.expect(t, !math.is_nan(result.x), "projected x should be finite") testing.expect(t, !math.is_nan(result.y), "projected y should be finite") testing.expect(t, !math.is_nan(result.z), "projected z should be finite") } @(test) test_light_matrix_directional :: proc(t: ^testing.T) { // A directional light pointing down -Y should create a top-down view light_dir := glsl.vec3{0, -1, 0} camera_target := glsl.vec3{5, 0, 5} half_extent: f32 = 15.0 depth: f32 = 40.0 mat := renderer.compute_light_matrix(light_dir, camera_target, half_extent, depth) // Camera target should map near the center in clip space (near 0,0) target_h := glsl.vec4{5, 0, 5, 1} result := mat * target_h ndc := glsl.vec3{result.x / result.w, result.y / result.w, result.z / result.w} testing.expect(t, math.abs(ndc.x) < 0.5, "target should be near center X in clip space") testing.expect(t, math.abs(ndc.y) < 0.5, "target should be near center Y in clip space") } @(test) test_shadow_caster_max :: proc(t: ^testing.T) { // Ensure max shadow casters is reasonable testing.expect_value(t, renderer.MAX_SHADOW_CASTERS, 4) } @(test) test_light_data_casts_shadow :: proc(t: ^testing.T) { // Verify Light_Data has casts_shadow field ld := renderer.Light_Data { type = 0, enabled = true, position = {0, -1, 0}, casts_shadow = true, } testing.expect(t, ld.casts_shadow, "casts_shadow should be true") ld2 := renderer.Light_Data{} testing.expect(t, !ld2.casts_shadow, "casts_shadow should default to false") } @(test) test_atlas_region_single :: proc(t: ^testing.T) { // Single shadow caster gets the full atlas region := renderer.compute_atlas_uv_region(0, 1) testing.expect_value(t, region, [4]f32{0, 0, 1, 1}) } @(test) test_atlas_region_multiple :: proc(t: ^testing.T) { // Multiple shadow casters subdivide into quadrants r0 := renderer.compute_atlas_uv_region(0, 4) r1 := renderer.compute_atlas_uv_region(1, 4) r2 := renderer.compute_atlas_uv_region(2, 4) r3 := renderer.compute_atlas_uv_region(3, 4) testing.expect_value(t, r0, [4]f32{0, 0, 0.5, 0.5}) testing.expect_value(t, r1, [4]f32{0.5, 0, 0.5, 0.5}) testing.expect_value(t, r2, [4]f32{0, 0.5, 0.5, 0.5}) testing.expect_value(t, r3, [4]f32{0.5, 0.5, 0.5, 0.5}) } @(test) test_atlas_pixel_region :: proc(t: ^testing.T) { // Single caster gets full atlas in pixel space region := renderer.compute_atlas_region(0, 1, 4096) testing.expect_value(t, region.x, i32(0)) testing.expect_value(t, region.y, i32(0)) testing.expect_value(t, region.w, u32(4096)) testing.expect_value(t, region.h, u32(4096)) // Multiple casters get quadrants r1 := renderer.compute_atlas_region(1, 4, 4096) testing.expect_value(t, r1.x, i32(2048)) testing.expect_value(t, r1.y, i32(0)) testing.expect_value(t, r1.w, u32(2048)) testing.expect_value(t, r1.h, u32(2048)) }