Initial support for GEM buffers.

master
itycodes 2 months ago
parent ec52b1d413
commit f3a2c86935

2
Cargo.lock generated

@ -82,7 +82,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "intel_gpu_uapi"
name = "intel_gpu"
version = "0.1.0"
dependencies = [
"bindgen",

@ -1,5 +1,5 @@
[package]
name = "intel_gpu_uapi"
name = "intel_gpu"
version = "0.1.0"
edition = "2021"

@ -1,14 +1,68 @@
use std::{ffi::OsString, os::fd::{AsRawFd, OwnedFd}};
use crate::uapi::i915::DrmGemHandle;
use super::super::uapi;
use std::path::Path;
use std::fs::File;
pub use uapi::i915::GemIoctlError;
#[derive(Debug)]
pub struct DrmDeviceNode {
pub fd: OwnedFd,
pub path: Option<OsString>,
}
#[derive(Debug)]
pub struct GemHandle<'a> {
pub handle: DrmGemHandle,
pub node: &'a DrmDeviceNode,
}
/// An owned GEM handle that will be closed when dropped
impl GemHandle<'_> {
pub fn new(node: &DrmDeviceNode, size: u64) -> Option<GemHandle> {
uapi::i915::make_gem(node.fd.as_raw_fd(), size).map(|handle| GemHandle {
handle: handle,
node: &node,
})
}
pub fn from_handle(node: &DrmDeviceNode, handle: DrmGemHandle) -> Result<GemHandle, GemIoctlError> {
// Avoid invoking GemHandle::is_valid() here to prevent the drop() method from trying to drop an invalid handle
let is_valid = uapi::i915::gem_is_valid(node.fd.as_raw_fd(), handle);
if is_valid.is_ok() && is_valid.unwrap() {
let res = GemHandle {
handle: handle,
node: &node,
};
return Ok(res);
} else {
return Err(is_valid.err().unwrap_or(GemIoctlError::PermissionDenied));
}
}
pub fn close(&mut self) -> Result<(), i32> {
return uapi::i915::close_gem(self.node.fd.as_raw_fd(), self.handle);
}
pub fn has_tiling(&self) -> Result<bool, uapi::i915::GemIoctlError> {
uapi::i915::gem_has_tiling(self.node.fd.as_raw_fd(), self.handle)
}
pub fn is_valid(&self) -> Result<bool, GemIoctlError> {
uapi::i915::gem_is_valid(self.node.fd.as_raw_fd(), self.handle)
}
}
impl Drop for GemHandle<'_> {
fn drop(&mut self) {
// Ignoring the close failing as an invalid Gem handle's drop is a no-op
let _ = self.close();
}
}
#[derive(Debug)]
pub struct Device {
pub node: DrmDeviceNode,

@ -29,6 +29,11 @@ pub struct EngineInfo {
pub capabilities: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DrmGemHandle {
pub handle: u32,
}
fn engine_class_from_u16(engine_class: u16) -> EngineClass {
match engine_class {
0 => EngineClass::Render,
@ -83,32 +88,39 @@ impl EngineInfo {
}
}
pub unsafe fn do_query<T>(fd: RawFd, query_id: u32) -> Option<*mut T> {
let mut query_item = native::drm_i915_query_item {
query_id: query_id as u64,
length: 0,
data_ptr: core::ptr::null_mut::<c_void>() as u64,
flags: 0,
};
let mut query = native::drm_i915_query {
items_ptr: (&mut query_item) as *mut native::drm_i915_query_item as u64,
num_items: 1,
flags: 0,
};
let res = libc::ioctl(fd.as_raw_fd(), native::DRM_IOCTL_I915_QUERY, &mut query);
if res != 0 {
return None;
}
let result_info = libc::malloc(query_item.length as usize)
as *mut T;
libc::memset(result_info as *mut c_void, 0, query_item.length as usize);
query_item.data_ptr = result_info as u64;
let res = libc::ioctl(fd.as_raw_fd(), native::DRM_IOCTL_I915_QUERY, &mut query);
if res != 0 {
return None;
}
Some(result_info)
}
pub fn get_engines(fd: RawFd) -> Option<Vec<EngineInfo>> {
unsafe {
let mut query_item = native::drm_i915_query_item {
query_id: native::DRM_I915_QUERY_ENGINE_INFO as u64,
length: 0,
data_ptr: core::ptr::null_mut::<c_void>() as u64,
flags: 0,
};
let mut query = native::drm_i915_query {
items_ptr: (&mut query_item) as *mut native::drm_i915_query_item as u64,
num_items: 1,
flags: 0,
};
let res = libc::ioctl(fd.as_raw_fd(), native::DRM_IOCTL_I915_QUERY, &mut query);
if res != 0 {
return None;
}
let engine_info = libc::malloc(query_item.length as usize)
as *mut native::drm_i915_query_engine_info;
libc::memset(engine_info as *mut c_void, 0, query_item.length as usize);
query_item.data_ptr = engine_info as u64;
let res = libc::ioctl(fd.as_raw_fd(), native::DRM_IOCTL_I915_QUERY, &mut query);
if res != 0 {
return None;
}
let engine_data = engine_info.as_mut().unwrap();
let engine_data =
do_query::<native::drm_i915_query_engine_info>(fd,
native::DRM_I915_QUERY_ENGINE_INFO)?
.as_mut().unwrap();
let engines = engine_data.engines.as_mut_slice(engine_data.num_engines as usize);
let mut engines_res: Vec<EngineInfo> = Vec::new();
for i in 0..engine_data.num_engines {
@ -134,6 +146,94 @@ pub fn get_param(fd: RawFd, param_id: i32) -> Option<i32> {
}
}
pub fn get_context_param(fd: RawFd, context_id: u32, param_id: u32) -> Option<u64> {
unsafe {
let mut param = native::drm_i915_gem_context_param {
ctx_id: context_id,
param: param_id as u64,
value: 0,
size: 0,
};
let res = libc::ioctl(fd, native::DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM, &mut param);
if res != 0 {
return None;
}
Some(param.value)
}
}
pub fn make_gem(fd: RawFd, size: u64) -> Option<DrmGemHandle> {
unsafe {
let mut create = native::drm_i915_gem_create {
size: size,
handle: 0,
pad: 0,
};
let res = libc::ioctl(fd, native::DRM_IOCTL_I915_GEM_CREATE, &mut create);
if res != 0 {
return None;
}
let handle = DrmGemHandle {
handle: create.handle,
};
Some(handle)
}
}
pub fn close_gem(fd: RawFd, handle: DrmGemHandle) -> Result<(), i32> {
unsafe {
let mut close = native::drm_gem_close {
handle: handle.handle,
pad: 0,
};
let res = libc::ioctl(fd, native::DRM_IOCTL_GEM_CLOSE, &mut close);
if res != 0 {
return Err(res);
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GemIoctlError {
InvalidHandle,
PermissionDenied,
Unknown(i32),
}
pub fn gem_has_tiling(fd: RawFd, handle: DrmGemHandle) -> Result<bool, GemIoctlError> {
unsafe {
let mut tiling = native::drm_i915_gem_set_tiling {
handle: handle.handle,
tiling_mode: 0,
stride: 0,
swizzle_mode: 0,
};
let res = libc::ioctl(fd, native::DRM_IOCTL_I915_GEM_SET_TILING, &mut tiling);
match -res {
0 => Ok(true),
libc::ENOENT => Err(GemIoctlError::InvalidHandle),
libc::EPERM => Err(GemIoctlError::PermissionDenied),
libc::EINVAL => Ok(false),
_ => Err(GemIoctlError::Unknown(res)),
}
}
}
pub fn gem_is_valid(fd: RawFd, handle: DrmGemHandle) -> Result<bool, GemIoctlError> {
let res = gem_has_tiling(fd, handle);
if res.is_ok() && res.unwrap() {
return Ok(true);
}
// For some reason the kernel returns -EPERM instead of -ENOENT when the handle is invalid...
if res.is_err() && res.unwrap_err() == GemIoctlError::PermissionDenied {
return Ok(false);
}
return Err(res.unwrap_err());
}
pub fn find_node() -> Option<DrmDeviceNode> {
let inodes = fs::read_dir("/dev/dri").ok()?;
for inode in inodes {

@ -1,4 +1,4 @@
use intel_gpu_uapi::*;
use intel_gpu::*;
use gpu::i915;
use std::os::fd::{AsRawFd, OwnedFd, RawFd};
use std::fs::File;
@ -79,11 +79,37 @@ fn get_vendor_for_fd(fd: RawFd) -> String{
#[test]
fn test_i915_gpu_get_param() {
let device = i915::find_device().expect("Failed to find i915 device");
let drm_version = &device.driver;
assert_eq!(drm_version.name.to_str().unwrap(), "i915");
assert_eq!(drm_version.desc.to_str().unwrap(), "Intel Graphics");
let chipset_id = device.get_param(native::I915_PARAM_CHIPSET_ID).expect("Failed to get param");
let chip_id = format!("{:#04x}", chipset_id);
let dev_id = get_vendor_for_fd(device.node.fd.as_raw_fd());
assert_eq!(dev_id, chip_id);
}
#[test]
fn test_i915_gpu_gem_lifecycle() {
let device = i915::find_device().expect("Failed to find i915 device");
let gem = i915::GemHandle::new(&device.node, 4096).expect("Failed to create gem handle");
let tmp = gem.handle.clone();
assert!(gem.is_valid().unwrap());
drop(gem);
assert!(i915::GemHandle::from_handle(&device.node, tmp).is_err());
}
#[test]
fn test_i915_gpu_gem_from_handle() {
let device = i915::find_device().expect("Failed to find i915 device");
let gem = i915::GemHandle::new(&device.node, 4096).expect("Failed to create gem handle");
let tmp = gem.handle.clone();
let gem_2 = i915::GemHandle::from_handle(&device.node, tmp).expect("Failed to create gem handle from handle");
assert_eq!(gem.handle, gem_2.handle);
assert!(gem.is_valid().unwrap());
drop(gem);
assert!(!gem_2.is_valid().unwrap());
}
#[test]
fn test_i915_gpu_gem_has_tiling() {
let device = i915::find_device().expect("Failed to find i915 device");
let gem = i915::GemHandle::new(&device.node, 4096).expect("Failed to create gem handle");
assert!(gem.has_tiling().unwrap());
}

@ -1,4 +1,4 @@
use intel_gpu_uapi::*;
use intel_gpu::*;
use std::ffi::c_void;
use std::ffi::CStr;
use nix::{self};

@ -1,6 +1,6 @@
#![allow(unused_imports)]
use intel_gpu_uapi::*;
use intel_gpu::*;
use uapi::i915;
use std::fs;
use std::{fs::File, os::fd::{AsRawFd, RawFd}};
@ -8,7 +8,9 @@ use std::{fs::File, os::fd::{AsRawFd, RawFd}};
#[test]
fn test_i915_uapi_get_version() {
let node = i915::find_node().expect("Failed to find i915 fd");
let _drm_version = uapi::get_drm_version(node.fd.as_raw_fd()).expect("Failed to get drm version");
let drm_version = uapi::get_drm_version(node.fd.as_raw_fd()).expect("Failed to get drm version");
assert_eq!(drm_version.name.to_str().unwrap(), "i915");
assert_eq!(drm_version.desc.to_str().unwrap(), "Intel Graphics");
}
#[test]
@ -40,5 +42,39 @@ fn test_i915_uapi_native_engine_info() {
#[test]
fn test_i915_find_fd() {
let fd = i915::find_fd().expect("Failed to find i915 fd");
let _drm_version = uapi::get_drm_version(fd.as_raw_fd()).expect("Failed to get drm version");
let drm_version = uapi::get_drm_version(fd.as_raw_fd()).expect("Failed to get drm version");
assert_eq!(drm_version.name.to_str().unwrap(), "i915");
assert_eq!(drm_version.desc.to_str().unwrap(), "Intel Graphics");
}
#[test]
fn test_i915_uapi_get_context_param() {
let node = i915::find_node().expect("Failed to find i915 fd");
// Mesa uses context id of 0 for init so it's surely okay, right?
let param = i915::get_context_param(node.fd.as_raw_fd(),
0,
native::I915_CONTEXT_PARAM_GTT_SIZE as u32)
.expect("Failed to get context param");
assert!(param > 0);
}
#[test]
fn test_i915_uapi_gem_lifecycle() {
let node = i915::find_node().expect("Failed to find i915 fd");
let gem = i915::make_gem(node.fd.as_raw_fd(), 4096).expect("Failed to make gem");
assert!(gem.handle > 0);
assert!(i915::gem_is_valid(node.fd.as_raw_fd(), gem).unwrap());
i915::close_gem(node.fd.as_raw_fd(), gem).expect("Failed to close gem");
assert!(!i915::gem_is_valid(node.fd.as_raw_fd(), gem).unwrap());
let invalid_fd = File::open("/dev/null").expect("Failed to open /dev/null");
assert!(i915::make_gem(invalid_fd.as_raw_fd(), 4096).is_none());
}
#[test]
fn test_i915_uapi_gem_tiling() {
let node = i915::find_node().expect("Failed to find i915 fd");
let gem = i915::make_gem(node.fd.as_raw_fd(), 4096).expect("Failed to make gem");
// TODO figure out which devices this holds for
assert!(i915::gem_has_tiling(node.fd.as_raw_fd(), gem).is_ok_and(|e| e == true));
i915::close_gem(node.fd.as_raw_fd(), gem).expect("Failed to close gem");
}
Loading…
Cancel
Save