Неожиданные изменения пикселей в текстуре OpenGL перед отображением на экране - PullRequest
3 голосов
/ 02 июня 2019

Я пишу каркас, чтобы можно было рисовать пиксели на экране. Однако теперь, когда я пытаюсь обновить экран, первые 4 пикселя показывают случайные цвета.

У меня не было этой проблемы, когда я просто отправлял указатель на данные изображения в мой оконный модуль, однако теперь, когда я передаю функцию рисования в качестве закрытия, это начало происходить. Я не думаю, что это проблема в моей функции рисования, поскольку я писал в эти пиксели, и они все еще были изменены.

Моя основная функция рисования:

// Define drawing function
fn rand_pixel(image_width: usize, image_height: usize) -> *const std::ffi::c_void
{
    // Define colors
    let black_pixel = image::Color { red: 0, green: 0, blue: 0 };
    let white_pixel = image::Color { red: 255, green: 255, blue: 255 };

    // Create blank image
    let mut pixels: image::Image = image::Image::new(image_width, image_height, black_pixel);

    // Get two random numbers
    use rand::Rng;
    let mut rng = rand::thread_rng();
    let rand_x = rng.gen::<usize>() % image_width;
    let rand_y = rng.gen::<usize>() % image_height;

    // Change pixels
    pixels.set_pixel(rand_x, rand_y, white_pixel);

    // return pointer
    pixels.get_ptr()
}

Моя процедура рисования окон:

// Execute draw function
let image_ptr = draw_function();

unsafe 
{
    // Create texture
    let mut tex_id: gl::types::GLuint = 0;
    gl::GenTextures(1, &mut tex_id);
    gl::BindTexture(gl::TEXTURE_2D, tex_id);

    // Move pixels to texture
    gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA as i32, 
        image_width, image_height, 0, gl::RGBA, gl::UNSIGNED_BYTE, image_ptr);

    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);

    // Create read framebuffer
    let mut read_fbo_id: gl::types::GLuint = 0;
    gl::GenFramebuffers(1, &mut read_fbo_id);
    gl::BindFramebuffer(gl::READ_FRAMEBUFFER, read_fbo_id);

    // Bind texture to read framebuffer
    gl::FramebufferTexture2D(gl::READ_FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, tex_id, 0);

    // Transfer from read framebuffer to draw framebuffer
    gl::BlitFramebuffer(0, 0, image_width, image_height, 
        0, 0, window_width as i32, window_height as i32, 
        gl::COLOR_BUFFER_BIT, gl::NEAREST);

    // Clear out read framebuffer
    gl::BindFramebuffer(gl::READ_FRAMEBUFFER, 0);
    gl::DeleteFramebuffers(1, &mut read_fbo_id);
};

// Update screen
let _ = windowed_context.swap_buffers();
std::thread::sleep(std::time::Duration::from_millis(15));

Полный проект можно найти: https://github.com/Pandabear314/sam_graphics

Я ожидаю черный экран с одним белым пикселем, который случайным образом рисует на экране каждый кадр. Однако левый нижний угол содержит 4 случайных пикселя. Первый и третий пиксели меняются, а второй и четвертый не меняются.

Изображение, показывающее результат: http://prntscr.com/nwl2gk

1 Ответ

4 голосов
/ 02 июня 2019

Функция rand_pixel создает image::Image в локальной переменной, а затем возвращает указатель на нее.Проблема в том, что изображение отбрасывается в конце функции, поэтому этот указатель остается висящим .

Разыменование этого указателя равно Неопределенное поведение , что, по-видимому,проявляется в вашем приложении, когда некоторые пиксели рисуются неправильным цветом.После удаления Image память, которую он использовал для хранения пикселей, может быть повторно использована для чего-либо еще.Кажется, что большая часть его еще не была повторно использована, но некоторые данные были записаны в первые несколько байтов.

В безопасном коде Rust защищает вас от ошибок такого рода, проверяя, что время жизни ссылокне превышайте область действия указанных переменных.Но когда вы погружаетесь в код unsafe и используете необработанные указатели, вы должны сами обеспечить безопасность памяти.

Возможное исправление состоит в том, чтобы заставить функцию рисования принимать изменяемую ссылку на Image вместо владенияоно само по себе:

fn rand_pixel(pixels: &mut image::Image) -> *const std::ffi::c_void {
    // Define colors
    let black_pixel = image::Color { red: 0, green: 0, blue: 0 };
    let white_pixel = image::Color { red: 255, green: 255, blue: 255 };

    // Overwrite with a blank image
    *pixels = image::Image::new(pixels.width, pixels.height, black_pixel);

    // Get two random numbers
    use rand::Rng;
    let mut rng = rand::thread_rng();
    let rand_x = rng.gen() % pixels.width;
    let rand_y = rng.gen() % pixels.height;

    // Change pixels
    pixels.set_pixel(rand_x, rand_y, white_pixel);

    // return pointer (still valid because the image lives longer than this function)
    pixels.get_ptr()
}

В вашей основной функции вы владеете изображением и передаете его через изменяемую ссылку:

// this variable lives longer than the call to window::create_window
let mut pixels = image::Image::new(image_width, image_height, image::Color { red: 0, green: 0, blue: 0} );

// Define draw function
let draw_fun = || rand_pixel(&mut pixels);

// Create new window
window::create_window(1600.0, 900.0, image_width as i32, image_height as i32, draw_fun);

Обратите внимание, вам также придется изменить подпись create_window принять FnMut вместо Fn.

...