opengl - 在绘制到屏幕之前,opengl 纹理中的意外像素发生变化

标签 opengl rust ffi unsafe raw-pointer

我正在编写一个能够在屏幕上绘制像素的框架。但是,现在我正在尝试更新屏幕,前 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

最佳答案

rand_pixel 函数在局部变量中创建了一个image::Image,然后返回指向它的指针。问题在于图像在函数末尾被丢弃,因此该指针悬空

取消引用此指针是未定义的行为,这显然在您的应用程序中表现为某些像素被绘制错误的颜色。 Image 被删除后,它用来存储像素的内存可以重新用于其他任何事情。似乎大部分还没有被重新使用,但一些数据已经写入了前几个字节。

在安全代码中,Rust 通过检查引用的生命周期不超过引用变量的范围来保护您免受此类错误。但是,当您使用不安全 代码并使用原始指针时,您必须自己确保内存安全。

一个可能的解决方法是使绘图函数采用对 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()
}

在您的 main 函数中,您拥有图像并通过可变引用传递它:

// 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

关于opengl - 在绘制到屏幕之前,opengl 纹理中的意外像素发生变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56414980/

相关文章:

c++ - 操作系统 : Symbols not found for architecture i386

opengl - 将光照贴图与漫反射纹理混合

c++ - 仅使用纹理坐标的棋盘着色器

rust - 为什么元组结构和枚举变体的行为类似于函数

generics - 为什么我不能用 let _ : Arc<dyn Trait> = value. into() 创建一个 trait 对象?

Rust:为 usize、u64、u32 等实现 From<_>

rust - 当需要安全函数而不包装不安全函数时,如何传递不安全函数?

javascript - 在 python 中调用 javascript?

opencv - 当我们在 OpenCV 中有深度和 rgb 垫时如何显示 3D 图像(从 Kinect 捕获)

Lua/Luajit : Indexing and named method at the same time?