-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Question] Minimal example with drm-rs(dev) and gbm.rs #8
Comments
I have transferred this issue, because integration with drm-rs is done in gbm.rs and therefor this is more suited to be discussed here. There is unfortunately no small example. Most of that happens in the current This code is going to change a lot and not use surfaces anymore and only buffers, but hopefully one of these links can give you some idea. If you want to modify one of the drm-rs examples to use gbm.rs instead of a dumb-buffer for rendering, I would happily accept a PR adding that to this repository. |
I'm a bit lost myself on this. So you get the display and device then initialize Are you supposed to use a framebuffer instead when you do it in rust, it's a bit confusing how to translate it all? |
No, not at all. If you want to use a surface (which depends on your use-case, there is a reason smithay is doing this manually with buffers and a drm framebuffer), that is totally doable! You basically create a You can then use the How you then get your Does that help? |
I got it working, it took a lot of trial and error and i just mimicked what was going on in the original code. I will post parts of the code here if it might help or if it might just be crap. DRM/GBM use drm::control::connector::State;
use drm::control::Device as ControlDevice;
use drm::SystemError;
use gbm::Surface;
use log::debug;
use crate::gl::GraphicsDisplaySize;
pub struct DrmState {
pub device: gbm::Device<Card>,
pub surface: Surface<()>,
pub crtc: drm::control::crtc::Info,
pub conn: drm::control::connector::Info,
pub display_size: GraphicsDisplaySize,
}
/// A simple wrapper for a device node.
pub struct Card(std::fs::File);
/// Simple helper methods for opening a `Card`.
impl Card {
pub fn open(path: &str) -> Self {
let mut options = std::fs::OpenOptions::new();
options.read(true);
options.write(true);
Card(options.open(path).unwrap())
}
}
impl drm::Device for Card {}
impl ControlDevice for Card {}
/// Implementing `AsRawFd` is a prerequisite to implementing the traits found
/// in this crate. Here, we are just calling `as_raw_fd()` on the inner File.
impl std::os::unix::io::AsRawFd for Card {
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
self.0.as_raw_fd()
}
}
fn get_crtc(gbm: &gbm::Device<Card>) -> Result<drm::control::crtc::Info, SystemError> {
for l in gbm.resource_handles()?.crtcs {
let crtc = gbm.get_crtc(l)?;
if crtc.mode().is_some() {
return Ok(crtc);
}
}
Err(SystemError::InvalidArgument)
}
fn get_conn(gbm: &gbm::Device<Card>) -> Result<drm::control::connector::Info, SystemError> {
for i in gbm.resource_handles()?.connectors {
let conn = gbm.get_connector(i, true)?;
if conn.state() == State::Connected {
return Ok(conn);
}
}
Err(SystemError::InvalidArgument)
}
impl DrmState {
pub fn swap_buffers(&self) {
let bo = unsafe { self.surface.lock_front_buffer().unwrap() };
let fb = self.device.add_framebuffer(&bo, 24, 32).unwrap();
self.device
.set_crtc(
self.crtc.handle(),
Some(fb),
(0, 0),
&[self.conn.handle()],
self.crtc.mode(),
)
.unwrap();
}
}
pub fn init_drm() -> DrmState {
use gbm::{BufferObjectFlags, Device, Format};
// ... init your drm device ...
let drm = Card::open("/dev/dri/card0");
// init a GBM device
let gbm = Device::new(drm).unwrap();
let crtc = get_crtc(&gbm).expect("drm: failed to get crtc");
let conn = get_conn(&gbm).expect("drm: failed to get connector");
// get display width/height
let (w, h) = crtc.mode().unwrap().size();
// create a surface
let surface = gbm
.create_surface::<()>(
w as u32,
h as u32,
Format::Xrgb8888,
BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
)
.unwrap();
debug!("crtc: {:?}", crtc);
debug!("current encoder: {:?}", conn.current_encoder());
DrmState {
device: gbm,
surface,
crtc,
conn,
display_size: GraphicsDisplaySize {
width: w as u32,
height: h as u32,
},
}
} EGL pub fn init_egl(
device: khronos_egl::NativeDisplayType,
surface: khronos_egl::NativeWindowType,
) -> EGLData {
let egl = khronos_egl::Instance::new(khronos_egl::Static);
let egl_display = egl.get_display(device).expect("egl: get_display failed");
egl.initialize(egl_display).expect("egl: initialize failed");
// egl config attributes
let egl_attribs = [
khronos_egl::RED_SIZE,
8,
khronos_egl::GREEN_SIZE,
8,
khronos_egl::BLUE_SIZE,
8,
khronos_egl::DEPTH_SIZE,
8,
khronos_egl::RENDERABLE_TYPE,
khronos_egl::OPENGL_ES2_BIT,
khronos_egl::NONE,
];
let mut configs = Vec::with_capacity(
egl.matching_config_count(egl_display, &egl_attribs)
.expect("egl: matching_counfig_count failed") as usize,
);
egl.choose_config(egl_display, &egl_attribs, &mut configs)
.expect("egl: choose_config failed");
let egl_config = configs
.iter()
.find(|e| {
if let Ok(id) = egl.get_config_attrib(egl_display, **e, khronos_egl::NATIVE_VISUAL_ID) {
id == gbm::Format::Xrgb8888 as i32
} else {
false
}
})
.cloned()
.expect("egl: no matching config found");
egl.bind_api(khronos_egl::OPENGL_ES_API)
.expect("egl: bind_api failed");
unsafe {
gl_loader_helper!(
glCreateShader,
glCompileShader,
glShaderSource,
glGetShaderiv,
glCreateProgram,
glAttachShader,
glLinkProgram,
glGetProgramiv,
glDeleteProgram,
glGetProgramInfoLog,
glUseProgram,
glClearColor,
glClear,
glGenBuffers,
glBindBuffer,
glGetAttribLocation,
glGetUniformLocation,
glBufferData,
glVertexAttribPointer,
glEnableVertexAttribArray,
glUniform2fv,
glUniform1f,
glDrawElements
);
};
let egl_context_attribs = [khronos_egl::CONTEXT_CLIENT_VERSION, 2, khronos_egl::NONE];
let egl_context = egl
.create_context(egl_display, egl_config, None, &egl_context_attribs)
.expect("egl: create_context failed");
debug!("egl context created");
let egl_buffer = unsafe {
egl.create_window_surface(egl_display, egl_config, surface, None)
.expect("egl: create_window_surface failed")
};
debug!("egl window surface created");
egl.make_current(
egl_display,
Some(egl_buffer),
Some(egl_buffer),
Some(egl_context),
)
.expect("egl: make_current failed");
EGLData {
egl,
egl_buffer,
egl_display,
}
} in egl here's the device/display i used. let egl_data = gl::init_egl(
drm_state.device.as_raw() as khronos_egl::NativeDisplayType,
drm_state.surface.as_raw() as khronos_egl::NativeWindowType,
); and when drawing i just swap egl buffers and then in the drm. |
That looks mostly fine (though I would have to see the whole code to judge if everything is used correctly), the only thing I would try to avoid is creating a new framebuffer for every swap (and never freeing them). The buffers returned by the GbmSurface actually cycle through, so you can use |
That's great that the impressions is that it seems mostly fine, so at least i'm not completely of track:)
I'm assuming you mean the Not sure what you mean with the I will try to adjust based on your feedback and also upload the project in some way:) EDIT: Do you mean something like this for the swap_buffers? let mut bo = unsafe { self.surface.lock_front_buffer().unwrap() };
let fb = bo
.userdata()
.unwrap()
.map(ToOwned::to_owned)
.unwrap_or_else(|| {
let fb = self.device.add_framebuffer(&bo, 24, 32).unwrap();
bo.set_userdata(fb).unwrap();
fb
});
self.device
.set_crtc(
self.crtc.handle(),
Some(fb),
(0, 0),
&[self.conn.handle()],
self.crtc.mode(),
)
.unwrap(); |
yes.
It is not unsafe from rust's perspective to leak memory! That is a safe operation (though mostly this is indeed prevented by
The
The other way around, you can create a framebuffer, associated with a front buffer, and attach that to the front buffer object. (Theoretically you could also just create two framebuffers and swap them, but there is no guarantee, that the
Yes that should work! Exactly! |
Can't you just wrap the id (
That feels odd, not saying it's not like that though, not sure i quite understand it.
Yay:) |
The problem with that is, that cleaning up requires the DrmDevice. We could store the file-descriptor from the device, that is necessary to do that, inside the proposed Believe me, we have experimented a lot with the api and this is the best we came up with...
Right, because libgbm (the C-library this binds to) internally frees the buffers.
Correct. And because the gbm-surface sometimes returns you buffers, that you have already seen this is just a convenient way to create exactly one framebuffer per buffer object in use. |
I want to use code in dev branch of drm-rs (which seems to be far ahead from master) with
gbm.rs
lib. It should be pretty straightforward I beleive (given that the draft already exists on gmb page), but just in case: is there any example that already does this?The text was updated successfully, but these errors were encountered: