Not-Nik / raylib-zig

Manually tweaked, auto-generated raylib bindings for zig. https://github.com/raysan5/raylib
MIT License
703 stars 123 forks source link

Seg fault when using `rl.genMeshPlane` #165

Closed ExpertOfNil closed 4 weeks ago

ExpertOfNil commented 1 month ago

I'm porting a toy program I wrote in C (which works) to Zig and I'm getting a segmentation fault when I use rl.genMeshPlane:

const std = @import("std");
const rl = @import("raylib");
const xi = @cImport({
    @cInclude("/opt/XIMEA/include/xiApi.h");
});
const print = std.debug.print;
const expect = std.testing.expect;
const mem = std.mem;
const pi = std.math.pi;
const tan = std.math.tan;
const DEG2RAD: comptime_float = pi / 180.0;

const BACKGROUND_COLOR: rl.Color = .{ 18, 18, 18, 255 };
const WIN_W: c_int = 3840;
const WIN_H: c_int = 2160;
const FONT_SIZE: i32 = 12;

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const alloc = gpa.allocator();
    defer {
        const deinit_status = gpa.deinit();
        if (deinit_status == .leak) expect(false) catch @panic("Alloc FAIL");
    }

    var args = try std.process.argsWithAllocator(alloc);
    defer args.deinit();

    var zoom: i32 = 1;
    // Handle command-line args
    while (args.next()) |arg| {
        if (mem.eql(u8, arg, "-z")) {
            const argv = args.next() orelse {
                print("Missing option value for -z", .{});
                return;
            };
            zoom = std.fmt.parseInt(i32, argv, 1) catch {
                print("Invalid option value for -z: {s}", .{argv});
                return;
            };
        }
    }

    const win_w: i32 = WIN_W * zoom * 2;
    const win_h: i32 = WIN_H * zoom * 2;

    const fovy = 45.0;
    const img_sz: Vec2i = .{.x = WIN_W, .y = WIN_H};
    const img_sz_f = rl.Vector2.init(@floatFromInt(img_sz.x), @floatFromInt(img_sz.y));
    const calc_z = img_sz_f.y * 0.1 * 0.5 / @tan(DEG2RAD * fovy * 0.5);
    const camera: rl.Camera3D = .{
        .target = rl.Vector3.init(0.0, 0.0, 0.0),
        .up = rl.Vector3.init(0.0, 0.0, 1.0),
        .fovy = fovy,
        .projection = rl.CameraProjection.camera_perspective,
        .position = rl.Vector3.init(0.0, calc_z, 0.0),
    };

    print("Creating mesh...\n", .{});
    const mesh: rl.Mesh = rl.genMeshPlane(3840.0, 2160.0, 2, 2);
    defer rl.unloadMesh(mesh);
    print("Mesh created...\n", .{});

    const material: rl.Material = rl.loadMaterialDefault();
    const target: rl.RenderTexture2D = rl.loadRenderTexture(win_w, win_h);
    defer rl.unloadRenderTexture(target);
    rl.initWindow(WIN_W, WIN_H, "Shader Testing");
    defer rl.closeWindow();

    rl.setTargetFPS(20);
    // Render loop
    while (!rl.windowShouldClose()) {
        rl.beginTextureMode(target);
        {
            rl.clearBackground(rl.Color.ray_white);
            rl.beginMode3D(camera);
            {
                rl.drawMesh(mesh, material, rl.Matrix.identity());
            }
            rl.endMode3D();
        }
        rl.endTextureMode();

        rl.beginDrawing();
        {
            rl.clearBackground(rl.Color.ray_white);

            const rect_sz = rl.Vector2.init(
                @floatFromInt(target.texture.width), 
                @floatFromInt(target.texture.height),
            );
            const rect_pos = rl.Vector2.init(0.0, 0.0);
            rl.drawTextureRec(
                target.texture,
                rl.Rectangle.init(0.0, 0.0, rect_sz.x, rect_sz.y),
                rect_pos,
                rl.Color.white,
            );
        }
        rl.endDrawing();
    }
    return;
}

Output:

Creating mesh...
Segmentation fault at address 0x0
???:?:?: 0x0 in ??? (???)
run
└─ run shader-tests failure
error: the following command terminated unexpectedly:
/path/to/shader-tests/zig-out/bin/shader-tests
Build Summary: 8/10 steps succeeded; 1 failed (disable with --summary none)
run transitive failure
└─ run shader-tests failure
error: the following build command failed with exit code 1:
/path/to/shader-tests/.zig-cache/o/0882fb864884526a2e226b48116f04e4/build /path/to/zig-linux-x86_64-0.13.0/zig /path/to/shader-tests /path/to/shader-tests/.zig-cache /path/to/.cache/zig --seed 0x906ca03b -Z595a5c1c83ff71ed run
znichola commented 1 month ago

I was able to reproduce the bug, but not idea why it's happening.

The segfault looks to be in the raylib header, but I can't figure out what about the raylib wrapper broke something.

// rlgl.h

// Vertex data management
//-----------------------------------------------------------------------------------------
// Load a new attributes buffer
unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic)
{
    unsigned int id = 0;

#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
    printf("Before %u %p\n", id, &id);
    glGenBuffers(1, &id);
    printf("After %u %p\n", id, &id);
    glBindBuffer(GL_ARRAY_BUFFER, id);
    glBufferData(GL_ARRAY_BUFFER, size, buffer, dynamic? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
#endif

    return id;
}
zig build run
Creating mesh...
Before 0 0000007eb1bff054
Segmentation fault at address 0x0
C:\Users\Me\AppData\Local\zig\p\1220d1c8697d41a42d4eaaf3f8709865534d1f3d6ad63f8a27500fa881380651a1c5\src\rlgl.h:3788:0: 0x5ebbe2 in rlLoadVertexBuffer (raylib.lib)
    glGenBuffers(1, &id);

C:\Users\Me\AppData\Local\zig\p\1220d1c8697d41a42d4eaaf3f8709865534d1f3d6ad63f8a27500fa881380651a1c5\src\rmodels.c:1269:0: 0x51e90f in UploadMesh (raylib.lib)
    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION] = rlLoadVertexBuffer(vertices, mesh->vertexCount*3*sizeof(float), dynamic);   

C:\Users\Me\AppData\Local\zig\p\1220d1c8697d41a42d4eaaf3f8709865534d1f3d6ad63f8a27500fa881380651a1c5\src\rmodels.c:2625:0: 0x527049 in GenMeshPlane (raylib.lib)
    UploadMesh(&mesh, false);

C:\Users\Me\AppData\Local\zig\p\1220c3d910dbb1a252e36d6574ebff57c0a63310f4f519f723ee3dbe06ad287f8fd1\lib\raylib.zig:4520:29: 0x462586 in genMeshPlane (OOEP.exe.obj)
    return cdef.GenMeshPlane(width, length, @as(c_int, resX), @as(c_int, resZ));
                            ^
C:\Users\Me\Documents\Git\OSSP\src\main.zig:58:42: 0x4616e4 in main (OOEP.exe.obj)
    const mesh: rl.Mesh = rl.genMeshPlane(3840.0, 2160.0, 2, 2);     
znichola commented 1 month ago

After digging a bit, glGenBuffers is an openGL function, so I found it a bit strange there is a segfault there, using GPT apparently you do need to initialise the openGL context, and that happens within the rl.initWindow( function. So moving that above the function call has fixed this segfault, but the program still has a bug, a blank screen for a few seconds then it crashes. So maybe you have a slightly different version on your end, or can compare to the C version for any other differences.

ExpertOfNil commented 4 weeks ago

Thanks for the clues! I'll do some further comparisons and let you know if I find anything meaningful.

ExpertOfNil commented 3 weeks ago

@znichola I put together a minimal example in C, Odin, and Zig. The C and Odin versions work fine, but the Zig version still fails (seemingly) during mesh generation. Something I did note is that raylib-zig is using v5.5-dev, and the others are using v5.0 or v5.1-dev.

ExpertOfNil commented 3 weeks ago

Using the same static libraries as my C variant compiled and ran fine, as well: https://github.com/ExpertOfNil/shader-play-minimal/tree/raw-rl-bindings