Files
RedBear-OS/local/recipes/gpu/redox-drm/source/src/drivers/intel/execbuffer.rs
T
vasilito a52632f69d fix: bits_pthread cbindgen needs stddef.h for size_t type
The generated bits/pthread.h uses size_t but had no includes.
Also added openat cache vars to m4 recipe for gnulib cross-compilation.
2026-06-01 17:00:53 +03:00

266 lines
8.7 KiB
Rust

use log::{debug, info, warn};
use super::fence::FenceTimeline;
use super::gtt::IntelGtt;
use super::ring::{IntelRing, RingType};
use super::syncobj::SyncobjManager;
use crate::driver::{
ContextHandle, DriverError, IntelGemExecObject2, IntelGemExecbuffer2,
IntelGemExecbuffer2Result, IntelGemRelocationEntry, Result, SyncobjHandle,
EXEC_OBJECT_PINNED, EXEC_OBJECT_WRITE, I915_EXEC_BLT, I915_EXEC_BSD, I915_EXEC_RENDER,
I915_EXEC_VEBOX,
};
use crate::gem::GemManager;
const MAX_EXEC_OBJECTS: usize = 256;
struct PinnedObject {
handle: u32,
gpu_offset: u64,
size: u64,
is_write: bool,
}
pub struct ExecbufferContext<'a> {
gtt: &'a mut IntelGtt,
gem: &'a mut GemManager,
render_ring: &'a mut IntelRing,
blitter_ring: Option<&'a mut IntelRing>,
fence_timeline: &'a FenceTimeline,
syncobj_mgr: &'a mut SyncobjManager,
}
impl<'a> ExecbufferContext<'a> {
pub fn new(
gtt: &'a mut IntelGtt,
gem: &'a mut GemManager,
render_ring: &'a mut IntelRing,
blitter_ring: Option<&'a mut IntelRing>,
fence_timeline: &'a FenceTimeline,
syncobj_mgr: &'a mut SyncobjManager,
) -> Self {
Self { gtt, gem, render_ring, blitter_ring, fence_timeline, syncobj_mgr }
}
pub fn execute(
&mut self,
exec: &IntelGemExecbuffer2,
objects: &[IntelGemExecObject2],
relocs: &[IntelGemRelocationEntry],
batch_data: &[u8],
) -> Result<IntelGemExecbuffer2Result> {
if exec.buffer_count as usize > MAX_EXEC_OBJECTS {
return Err(DriverError::InvalidArgument("execbuffer buffer_count exceeds limit"));
}
if exec.buffer_count as usize != objects.len() {
return Err(DriverError::InvalidArgument(
"execbuffer buffer_count does not match objects array length",
));
}
if batch_data.is_empty() && exec.batch_len == 0 {
return Err(DriverError::InvalidArgument("execbuffer batch is empty"));
}
let mut pinned = self.pin_objects(objects)?;
self.apply_relocations(&pinned, objects, relocs, batch_data)?;
let ring = self.select_ring(exec.flags)?;
ring.submit_batch(&batch_data_to_u32s(batch_data)?)?;
let seqno = ring.last_seqno();
let ring_type = ring.ring_type();
drop(ring);
let fence_handle = self.allocate_fence(seqno)?;
debug!(
"redox-drm-intel: execbuffer2 submitted — {} objects, {} batch bytes, ring {:?}, seqno {}, fence {}",
objects.len(),
batch_data.len(),
ring_type,
seqno,
fence_handle
);
self.update_pinned_objects(&pinned, objects, batch_data)?;
Ok(IntelGemExecbuffer2Result {
seqno,
fence_handle,
})
}
fn pin_objects(&mut self, objects: &[IntelGemExecObject2]) -> Result<Vec<PinnedObject>> {
let mut pinned = Vec::with_capacity(objects.len());
for obj in objects {
let gpu_offset = if obj.flags & EXEC_OBJECT_PINNED != 0 {
obj.offset
} else {
let (phys_addr, size) = {
let gem_obj = self.gem.object(obj.handle)?;
(gem_obj.phys_addr as u64, gem_obj.size)
};
let gpu_addr = self.gtt.alloc_range(size)?;
if let Err(e) = self.gtt.map_range(gpu_addr, phys_addr, size, 1 << 1) {
let _ = self.gtt.release_range(gpu_addr, size);
return Err(e);
}
gpu_addr
};
let size = self.gem.object(obj.handle)?.size;
pinned.push(PinnedObject {
handle: obj.handle,
gpu_offset,
size,
is_write: obj.flags & EXEC_OBJECT_WRITE != 0,
});
}
Ok(pinned)
}
fn apply_relocations(
&self,
pinned: &[PinnedObject],
objects: &[IntelGemExecObject2],
relocs: &[IntelGemRelocationEntry],
batch_data: &[u8],
) -> Result<()> {
if relocs.is_empty() {
return Ok(());
}
let mut patched = batch_data.to_vec();
for reloc in relocs {
let target_offset = self.resolve_gpu_offset(pinned, objects, reloc.target_handle)?;
let patched_addr = target_offset.wrapping_add(u64::from(reloc.delta));
let patch_offset = reloc.offset as usize;
let end = patch_offset.checked_add(4).ok_or_else(|| {
DriverError::InvalidArgument("relocation offset overflows batch buffer")
})?;
if end > patched.len() {
return Err(DriverError::InvalidArgument(
"relocation offset exceeds batch buffer length",
));
}
let current = u32::from_le_bytes(
patched[patch_offset..end]
.try_into()
.unwrap_or([0u8; 4]),
);
let expected = (reloc.presumed_offset as u32)
.wrapping_add(reloc.delta);
if reloc.presumed_offset != 0 && current != expected {
info!(
"redox-drm-intel: relocation mismatch at offset {} — expected {:#010x}, found {:#010x}; re-patching",
patch_offset, expected, current
);
}
let value = (patched_addr as u32).wrapping_add(reloc.delta);
patched[patch_offset..end].copy_from_slice(&value.to_le_bytes());
debug!(
"redox-drm-intel: relocation {:?} → target_handle={}, gpu_offset={:#010x}, delta={}, patched_addr={:#010x}",
reloc, reloc.target_handle, target_offset, reloc.delta, patched_addr
);
}
Ok(())
}
fn resolve_gpu_offset(
&self,
pinned: &[PinnedObject],
objects: &[IntelGemExecObject2],
target_handle: u32,
) -> Result<u64> {
if let Some(p) = pinned.iter().find(|p| p.handle == target_handle) {
return Ok(p.gpu_offset);
}
let obj = objects
.iter()
.find(|o| o.handle == target_handle)
.ok_or_else(|| {
DriverError::NotFound(format!(
"relocation target handle {} not found in exec object list",
target_handle
))
})?;
if obj.flags & EXEC_OBJECT_PINNED != 0 {
return Ok(obj.offset);
}
Err(DriverError::InvalidArgument(
"relocation target not pinned and not found in resolved list",
))
}
fn select_ring(&mut self, flags: u64) -> Result<&mut IntelRing> {
let engine_mask = flags & 0x3F;
if engine_mask & I915_EXEC_RENDER != 0 {
Ok(self.render_ring)
} else if engine_mask & I915_EXEC_BLT != 0 {
self.blitter_ring
.as_deref_mut()
.ok_or_else(|| DriverError::Unsupported("blitter ring not initialized"))
} else if engine_mask & I915_EXEC_BSD != 0 {
Err(DriverError::Unsupported("BSD/video ring not supported"))
} else if engine_mask & I915_EXEC_VEBOX != 0 {
Err(DriverError::Unsupported("VEBOX ring not supported"))
} else {
Ok(self.render_ring)
}
}
fn allocate_fence(&mut self, seqno: u64) -> Result<SyncobjHandle> {
let handle = self.syncobj_mgr.create(0)?;
self.syncobj_mgr.signal(&[handle])?;
debug!(
"redox-drm-intel: fence seqno {} mapped to syncobj handle {}",
seqno, handle
);
Ok(handle)
}
fn update_pinned_objects(
&self,
pinned: &[PinnedObject],
objects: &[IntelGemExecObject2],
_batch_data: &[u8],
) -> Result<()> {
for p in pinned {
if let Some(obj) = objects.iter().find(|o| o.handle == p.handle) {
if p.is_write {
if let Ok(gem_obj) = self.gem.object(p.handle) {
debug!(
"redox-drm-intel: write-domain object handle {} size {} preserved after execbuffer",
p.handle, gem_obj.size
);
}
}
}
}
Ok(())
}
}
fn batch_data_to_u32s(data: &[u8]) -> Result<Vec<u32>> {
let dword_count = data.len() / 4;
let mut result = Vec::with_capacity(dword_count);
for chunk in data.chunks_exact(4) {
let arr: [u8; 4] = chunk.try_into().map_err(|_| {
DriverError::InvalidArgument("batch data not 4-byte aligned")
})?;
result.push(u32::from_le_bytes(arr));
}
Ok(result)
}