pd-rs / crankstart

A barely functional, wildly incomplete and basically undocumented Rust crate whose aim is to let you write Games for the Playdate handheld gaming system in Rust.
MIT License
235 stars 24 forks source link

Segfault with set_use_custom_draw #36

Open tjkirch opened 1 year ago

tjkirch commented 1 year ago

That said, set_use_custom_draw does seem like the right approach for this. If you can reproduce the segfaults we should figure out why they occur.

Originally posted by @rtsuk in https://github.com/pd-rs/crankstart/issues/35#issuecomment-1483709249


Here's a minimal reproduction:

#![no_std]
extern crate alloc;

use {
    alloc::boxed::Box,
    anyhow::Error,
    crankstart::{
        crankstart_game,
        graphics::Graphics,
        sprite::{Sprite, SpriteManager},
        Game, Playdate,
    },
    crankstart_sys::{LCDBitmapFlip, PDRect},
};

struct State {
    // If the sprite is not stored, the crash doesn't happen
    _sprite: Sprite,
}

impl State {
    pub fn new(_playdate: &Playdate) -> Result<Box<Self>, Error> {
        // Load an image, assign it to a custom-draw sprite, add the sprite.
        // Crash doesn't happen unless a bitmap is assigned to the sprite.
        let bitmap = Graphics::get().load_bitmap("graphics/wedge")?;
        let sprite_manager = SpriteManager::get_mut();
        let mut sprite = sprite_manager.new_sprite()?;
        // No crash unless it's custom-draw.
        sprite.set_use_custom_draw()?;
        sprite.set_image(bitmap, LCDBitmapFlip::kBitmapUnflipped)?;
        sprite_manager.add_sprite(&sprite)?;
        Ok(Box::new(Self { _sprite: sprite }))
    }
}

impl Game for State {
    fn update(&mut self, _playdate: &mut Playdate) -> Result<(), Error> {
        Ok(())
    }

    fn update_sprite(&mut self, _: &mut Sprite, _: &mut Playdate) -> Result<(), Error> {
        Ok(())
    }

    // Same crash happens with or without this definition:
    fn draw_sprite(&self, _: &Sprite, _: &PDRect, _: &PDRect, _: &Playdate) -> Result<(), Error> {
        Ok(())
    }
}

crankstart_game!(State);

The result:

Loading C API game: target/Golf Date.pdx/pdex.so
16:23:47: Loading: OK

Thread 1 "PlaydateSimulat" received signal SIGSEGV, Segmentation fault.
0x0000555555968c88 in _compositeRow ()
(gdb) bt
#0  0x0000555555968c88 in _compositeRow ()
#1  0x00005555559735da in LCDBitmap_drawBitmap ()
#2  0x0000555555980d70 in LCD_drawBitmap ()
#3  0x00005555559828b9 in LCDSprite_draw ()
#4  0x000055555597d71c in LCDDisplayList_drawScreenRect ()
Dwarf Error: Cannot find DIE at 0xccf referenced from DIE at 0x1349f [in module target/Golf Date.pdx/pdex.so]

The backtrace is the same whether I define draw_sprite or not. Is it not being hooked up properly? Or is draw_sprite required to take some action that the SDK depends upon having happened?

rtsuk commented 1 year ago

I don't recall seeing any C examples that use custom draw and then also set an image on the sprite. I'm not sure what the Playdate will do in that case.

tjkirch commented 1 year ago

If it's just something that shouldn't be done, I could make a doc comment :) A more mechanical fix might involve a bool on Sprite that tracks whether set_use_custom_draw has been called and whether an image has been set, failing if one or the other has already happened...

rtsuk commented 1 year ago

The backtrace suggests the crash is in the Playdate code, so maybe that's not a good thing to do? One could write a C version and see what happens. That, or ask on the forums.