Ответ 1
Я думаю, что мигание вызвано заменой буфера: в flashes.rs
очищается только первый буфер, который нужно вставить. Второй - все нули или оставшаяся память gpu, если вам не повезло. В соответствии с OpenGL wiki там нет хорошего способа вызова graphics::clear
:
Современная программа OpenGL должна всегда использовать двойную буферизацию., Буферы всегда должны быть очищены. На гораздо более старом оборудовании был способ уйти, не очищая сцену, но даже на полу-недавнее оборудование, это фактически сделает вещи медленнее. Поэтому всегда сделайте ясно.
Вместо этого обычный способ состоит в том, чтобы скопировать ваши изменения в текстуру или рендеринг буфера, а затем нарисовать это на экране точно так, как вы описали.
Я не мог найти никакого способа сделать это изнутри opengl_graphics
либо (нет вызовов в gl::GenFramebuffers
в любом месте в нем), но относительно просто настроить его с помощью raw gl
вызовов. (Я использовал текстуры вместо renderbuffers, потому что они имеют значительное преимущество в поддержке высокоуровневых методов, таких как Image::draw
.)
extern crate piston;
extern crate opengl_graphics;
extern crate graphics;
extern crate sdl2_window;
extern crate gl;
use opengl_graphics::{ GlGraphics, OpenGL, Texture, TextureSettings };
use graphics::{ Context, Graphics, Transformed };
use graphics::image::Image;
use piston::input::*;
use piston::event_loop::*;
use piston::window::Window;
use sdl2_window::Sdl2Window as AppWindow;
use gl::types::GLuint;
static CURSOR_POS_COLOR: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
fn main() {
let opengl = OpenGL::V3_2;
let mut window: AppWindow = piston::window::WindowSettings::new("Example for StackOverflow", [600, 600])
.exit_on_esc(true).opengl(opengl).build().expect("window");
let ref mut gl = GlGraphics::new(opengl);
let (mut mx, mut my) = (0., 0.);
let draw_size = window.draw_size();
// It would also be possible to create a texture by hand using gl::GenTextures and call
// gl::TexImage2D with a null pointer for the data argument, which would require another unsafe
// block but would save this allocation
let texture_buf = vec![0u8; draw_size.width as usize * draw_size.height as usize];
let texture = Texture::from_memory_alpha(&texture_buf, draw_size.width, draw_size.height,
&TextureSettings::new()).expect("texture");
let fbo;
unsafe {
let mut fbos: [GLuint; 1] = [0];
// Create a Framebuffer Object that we can draw to later
gl::GenFramebuffers(1, fbos.as_mut_ptr());
fbo = fbos[0];
// Switch to it as the active framebuffer
gl::BindFramebuffer(gl::FRAMEBUFFER, fbo);
// Set up the framebuffer object so that draws to it will go to the texture
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0, // draw colors, not depth or stencil data
gl::TEXTURE_2D, // the texture type
texture.get_id(),
0); // mipmap level
}
let mut events = Events::new(EventSettings::new().lazy(true));
while let Some(e) = events.next(&mut window) {
e.mouse_cursor(|x, y| {
mx = x; my = y;
});
e.render(|args| {
// Switch to the texture framebuffer and draw the cursor
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, fbo);
}
gl.draw(args.viewport(), |c, g| {
draw_cursor_pos([mx, my], &c, g);
});
// Switch to the window framebuffer and draw the texture
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
}
gl.draw(args.viewport(), |c, g| {
graphics::clear([1f32, 1f32, 1f32, 0f32], g);
// I can't entirely explain this. We already applied the viewport transform when
// we were rendering the cursor, so I think the texture is right-side-up for GL,
// but piston::Image is expecting an image laid out in screen coordinates.
// Since there is an offset in the viewport transform, the flip has to be applied
// first, otherwise it would flip across the origin.
let flipped = c.transform.prepend_transform(graphics::math::scale(1., -1.));
Image::new().draw(&texture, &c.draw_state, flipped, g);
});
});
}
}
fn draw_cursor_pos<G: Graphics>(
cursor: [f64; 2],
c: &Context,
g: &mut G,
) {
graphics::ellipse(
CURSOR_POS_COLOR,
graphics::ellipse::circle(cursor[0], cursor[1], 4.0),
c.transform,
g
);
}
В качестве альтернативы, бэкэнд gfx
имеет многообещающий метод Factory::CreateRenderTarget
. Мое оборудование не поддерживает его, но я считаю, что его использование выглядело бы примерно так:
extern crate piston;
extern crate graphics;
extern crate piston_window;
extern crate gfx_core;
use graphics::{ Context, Graphics, Transformed };
use graphics::image::Image;
use piston::input::*;
use piston::event_loop::*;
use piston::window::Window;
use piston_window::{ PistonWindow, OpenGL, G2dTexture };
use gfx_core::factory::Factory;
use gfx_core::texture::{ SamplerInfo, FilterMethod, WrapMode, Size };
static CURSOR_POS_COLOR: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
fn main() {
let opengl = OpenGL::V2_1;
let window_settings =
piston::window::WindowSettings::new("Example for StackOverflow", [600, 600])
.opengl(opengl)
.exit_on_esc(true);
let mut window: PistonWindow = window_settings.build().expect("window");
window.set_lazy(true);
let size = window.draw_size();
let (texture_handle, shader_view, target) = window.factory.create_render_target(size.width as Size, size.height as Size)
.expect("render target");
let sampler = window.factory.create_sampler(SamplerInfo::new(FilterMethod::Scale, WrapMode::Tile));
let texture = G2dTexture {
surface: texture_handle,
sampler: sampler,
view: shader_view,
};
let stencil = window.factory.create_depth_stencil_view_only(size.width as Size, size.height as Size)
.expect("stencil");
let (mut mx, mut my) = (0., 0.);
while let Some(e) = window.next() {
e.mouse_cursor(|x, y| {
mx = x; my = y;
});
if let Some(args) = e.render_args() {
window.g2d.draw(&mut window.encoder, &target, &stencil, args.viewport(), |c, g| {
draw_cursor_pos([mx, my], &c, g);
});
window.draw_2d(&e, |c, g| {
graphics::clear([1f32, 1f32, 1f32, 0f32], g);
let flipped = c.transform.prepend_transform(graphics::math::scale(1., -1.));
Image::new().draw(&texture, &c.draw_state, flipped, g);
});
}
}
}
fn draw_cursor_pos<G: Graphics>(
cursor: [f64; 2],
c: &Context,
g: &mut G,
) {
graphics::ellipse(
CURSOR_POS_COLOR,
graphics::ellipse::circle(cursor[0], cursor[1], 4.0),
c.transform,
g
);
}