1
0
Fork 0
forked from wry/wry

render: implement a vulkan renderer

This commit is contained in:
Julian Orth 2024-02-03 15:19:20 +01:00
parent 4ba8550da8
commit cf332e8436
66 changed files with 4287 additions and 239 deletions

View file

@ -4,7 +4,7 @@ sources:
tasks:
- install: |
sudo pacman -Syu --noconfirm
sudo pacman -S --noconfirm rustup libinput pango mesa libxkbcommon
sudo pacman -S --noconfirm rustup libinput pango mesa libxkbcommon cmake
rustup toolchain install stable
- build: |
cd jay

View file

@ -4,7 +4,7 @@ sources:
tasks:
- install: |
sudo pacman -Syu --noconfirm
sudo pacman -S --noconfirm rustup libinput pango mesa libxkbcommon xorg-xwayland adwaita-icon-theme libxcursor
sudo pacman -S --noconfirm rustup libinput pango mesa libxkbcommon xorg-xwayland adwaita-icon-theme libxcursor cmake
rustup toolchain install stable
- configure: |
sudo rmmod bochs

View file

@ -4,7 +4,7 @@ sources:
tasks:
- install: |
sudo pacman -Syu --noconfirm
sudo pacman -S --noconfirm rustup libinput pango mesa libxkbcommon xorg-xwayland adwaita-icon-theme libxcursor
sudo pacman -S --noconfirm rustup libinput pango mesa libxkbcommon xorg-xwayland adwaita-icon-theme libxcursor cmake
rustup toolchain install stable
- test: |
cd jay

153
Cargo.lock generated
View file

@ -120,6 +120,15 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "ash"
version = "0.37.3+1.3.251"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
dependencies = [
"libloading 0.7.4",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -279,6 +288,15 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]]
name = "cmake"
version = "0.1.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
dependencies = [
"cc",
]
[[package]]
name = "colorchoice"
version = "1.0.0"
@ -340,15 +358,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
[[package]]
name = "futures-macro"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
dependencies = [
"proc-macro2",
"quote",
@ -357,15 +375,15 @@ dependencies = [
[[package]]
name = "futures-task"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
[[package]]
name = "futures-util"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
dependencies = [
"futures-core",
"futures-macro",
@ -392,6 +410,36 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
[[package]]
name = "gpu-alloc"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
dependencies = [
"bitflags 2.4.1",
"gpu-alloc-types",
]
[[package]]
name = "gpu-alloc-ash"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2424bc9be88170e1a56e57c25d3d0e2dfdd22e8f328e892786aeb4da1415732"
dependencies = [
"ash",
"gpu-alloc-types",
"tinyvec",
]
[[package]]
name = "gpu-alloc-types"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
dependencies = [
"bitflags 2.4.1",
]
[[package]]
name = "hashbrown"
version = "0.14.2"
@ -457,6 +505,7 @@ dependencies = [
"algorithms",
"anyhow",
"arrayvec",
"ash",
"backtrace",
"bincode",
"bitflags 2.4.1",
@ -468,11 +517,13 @@ dependencies = [
"default-config",
"dirs",
"futures-util",
"gpu-alloc",
"gpu-alloc-ash",
"humantime",
"indexmap",
"isnt",
"jay-config",
"libloading",
"libloading 0.8.1",
"log",
"num-derive",
"num-traits",
@ -481,6 +532,7 @@ dependencies = [
"pin-project",
"rand",
"repc",
"shaderc",
"smallvec",
"thiserror",
"uapi",
@ -515,6 +567,16 @@ version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if 1.0.0",
"winapi",
]
[[package]]
name = "libloading"
version = "0.8.1"
@ -785,6 +847,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e110b7d5a1335c2e801176c42a626a905c23eecdee104d9bdfbd6ea5f0b8368"
[[package]]
name = "roxmltree"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b"
dependencies = [
"xmlparser",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
@ -830,6 +901,27 @@ dependencies = [
"syn 2.0.38",
]
[[package]]
name = "shaderc"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31cef52787a0db5108788ea20bed13d6bf4b96287c5c5201e55725f7070f3443"
dependencies = [
"libc",
"shaderc-sys",
]
[[package]]
name = "shaderc-sys"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e8f8439fffcffd6efcd74197204addf935dbab5752696bd990a6cd36d54cf64"
dependencies = [
"cmake",
"libc",
"roxmltree",
]
[[package]]
name = "slab"
version = "0.4.9"
@ -903,6 +995,21 @@ dependencies = [
"syn 2.0.38",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "uapi"
version = "0.2.10"
@ -1013,6 +1120,28 @@ version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.51.1"
@ -1087,3 +1216,9 @@ name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "xmlparser"
version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"

View file

@ -43,11 +43,15 @@ chrono = "0.4.19"
parking_lot = "0.12.1"
arrayvec = "0.7.4"
indexmap = "2.1.0"
ash = "0.37.3"
gpu-alloc = "0.6.0"
gpu-alloc-ash = "0.6.0"
[build-dependencies]
repc = "0.1.1"
anyhow = "1.0.52"
bstr = { version = "1.1.0", default-features = false, features = ["std"] }
shaderc = "0.8.2"
#[profile.dev.build-override]
#opt-level = 3

View file

@ -25,6 +25,7 @@ use std::{
mod egl;
mod enums;
mod tokens;
mod vulkan;
mod wire;
mod wire_dbus;
mod wire_xcon;
@ -47,6 +48,7 @@ fn main() -> anyhow::Result<()> {
wire_xcon::main()?;
enums::main()?;
egl::main()?;
vulkan::main()?;
println!("cargo:rerun-if-changed=build/build.rs");
Ok(())

38
build/vulkan.rs Normal file
View file

@ -0,0 +1,38 @@
use {
crate::open,
anyhow::{bail, Context},
std::{io::Write, path::Path},
};
const ROOT: &str = "src/gfx_apis/vulkan/shaders";
pub fn main() -> anyhow::Result<()> {
println!("cargo:rerun-if-changed={}", ROOT);
for shader in std::fs::read_dir(ROOT)? {
let shader = shader?;
let name = shader.file_name().to_string_lossy().into_owned();
compile_shader(&name).context(name)?;
}
Ok(())
}
fn compile_shader(name: &str) -> anyhow::Result<()> {
let stage = match Path::new(name)
.extension()
.and_then(|e| e.to_str())
.unwrap_or("")
{
"frag" => shaderc::ShaderKind::Fragment,
"vert" => shaderc::ShaderKind::Vertex,
n => bail!("Unknown shader stage {}", n),
};
let src = std::fs::read_to_string(format!("{}/{}", ROOT, name))?;
let compiler = shaderc::Compiler::new().unwrap();
let binary = compiler
.compile_into_spirv(&src, stage, name, "main", None)
.unwrap();
let mut file = open(&format!("{}.spv", name))?;
file.write_all(binary.as_binary_u8())?;
file.flush()?;
Ok(())
}

View file

@ -15,7 +15,7 @@ use {
timer::Timer,
video::{
connector_type::{ConnectorType, CON_UNKNOWN},
Connector, DrmDevice, Mode,
Connector, DrmDevice, GfxApi, Mode,
},
Axis, Direction, ModifiedKeySym, PciId, Workspace,
},
@ -506,6 +506,10 @@ impl Client {
self.send(&ClientMessage::MakeRenderDevice { device });
}
pub fn set_gfx_api(&self, device: Option<DrmDevice>, api: GfxApi) {
self.send(&ClientMessage::SetGfxApi { device, api });
}
pub fn connector_connected(&self, connector: Connector) -> bool {
let res = self.send_with_response(&ClientMessage::ConnectorConnected { connector });
get_response!(res, false, ConnectorConnected { connected });

View file

@ -5,7 +5,7 @@ use {
logging::LogLevel,
theme::{colors::Colorable, sized::Resizable, Color},
timer::Timer,
video::{connector_type::ConnectorType, Connector, DrmDevice},
video::{connector_type::ConnectorType, Connector, DrmDevice, GfxApi},
Axis, Direction, PciId, Workspace,
},
bincode::{BorrowDecode, Decode, Encode},
@ -334,6 +334,10 @@ pub enum ClientMessage<'a> {
device: InputDevice,
enabled: bool,
},
SetGfxApi {
device: Option<DrmDevice>,
api: GfxApi,
},
}
#[derive(Encode, Decode, Debug)]

View file

@ -362,4 +362,30 @@ impl DrmDevice {
pub fn make_render_device(self) {
get!().make_render_device(self);
}
/// Sets the preferred graphics API for this device.
///
/// If the API cannot be used, the compositor will try other APIs.
pub fn set_gfx_api(self, gfx_api: GfxApi) {
get!().set_gfx_api(Some(self), gfx_api);
}
}
/// A graphics API.
#[non_exhaustive]
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum GfxApi {
OpenGl,
Vulkan,
}
/// Sets the default graphics API.
///
/// If the API cannot be used, the compositor will try other APIs.
///
/// This setting can be overwritten per-device with [DrmDevice::set_gfx_api].
///
/// This call has no effect on devices that have already been initialized.
pub fn set_gfx_api(gfx_api: GfxApi) {
get!().set_gfx_api(None, gfx_api);
}

View file

@ -6,6 +6,7 @@ use {
ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
video::drm::{ConnectorType, DrmError, DrmVersion},
},
jay_config::video::GfxApi,
std::{
any::Any,
error::Error,
@ -217,6 +218,7 @@ pub enum InputEvent {
pub enum DrmEvent {
#[allow(dead_code)]
Removed,
GfxApiChanged,
}
pub trait BackendDrmDevice {
@ -224,6 +226,8 @@ pub trait BackendDrmDevice {
fn event(&self) -> Option<DrmEvent>;
fn on_change(&self, cb: Rc<dyn Fn()>);
fn dev_t(&self) -> c::dev_t;
fn make_render_device(self: Rc<Self>);
fn make_render_device(&self);
fn set_gfx_api(&self, api: GfxApi);
fn gtx_api(&self) -> GfxApi;
fn version(&self) -> Result<DrmVersion, DrmError>;
}

View file

@ -9,7 +9,6 @@ use {
edid::Descriptor,
format::{Format, ARGB8888, XRGB8888},
gfx_api::{GfxContext, GfxFramebuffer, GfxTexture},
gfx_apis::create_gfx_context,
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC},
renderer::RenderResult,
state::State,
@ -34,6 +33,7 @@ use {
ahash::{AHashMap, AHashSet},
bstr::{BString, ByteSlice},
indexmap::{indexset, IndexSet},
jay_config::video::GfxApi,
std::{
cell::{Cell, RefCell},
ffi::CString,
@ -75,7 +75,8 @@ pub struct MetalDrmDevice {
pub cursor_height: u64,
pub gbm: GbmDevice,
pub handle_events: HandleEvents,
pub ctx: Rc<MetalRenderContext>,
pub ctx: CloneCell<Rc<MetalRenderContext>>,
pub on_change: OnChange<crate::backend::DrmEvent>,
}
impl BackendDrmDevice for MetalDrmDevice {
@ -84,19 +85,27 @@ impl BackendDrmDevice for MetalDrmDevice {
}
fn event(&self) -> Option<crate::backend::DrmEvent> {
None
self.on_change.events.pop()
}
fn on_change(&self, _cb: Rc<dyn Fn()>) {
// nothing
fn on_change(&self, cb: Rc<dyn Fn()>) {
self.on_change.on_change.set(Some(cb));
}
fn dev_t(&self) -> dev_t {
self.devnum
}
fn make_render_device(self: Rc<Self>) {
self.backend.make_render_device(&self, true);
fn make_render_device(&self) {
self.backend.make_render_device(&self, false);
}
fn set_gfx_api(&self, api: GfxApi) {
self.backend.set_gfx_api(self, api)
}
fn gtx_api(&self) -> GfxApi {
self.ctx.get().gfx.gfx_api()
}
fn version(&self) -> Result<DrmVersion, DrmError> {
@ -162,8 +171,6 @@ pub struct MetalConnector {
pub connector_id: ConnectorId,
pub events: SyncQueue<ConnectorEvent>,
pub buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
pub next_buffer: NumCell<usize>,
@ -182,7 +189,7 @@ pub struct MetalConnector {
pub crtc: CloneCell<Option<Rc<MetalCrtc>>>,
pub on_change: OnChange,
pub on_change: OnChange<ConnectorEvent>,
pub present_trigger: AsyncEvent,
@ -271,12 +278,30 @@ impl Debug for ConnectorFutures {
}
}
#[derive(Default)]
pub struct OnChange {
pub struct OnChange<T> {
pub on_change: CloneCell<Option<Rc<dyn Fn()>>>,
pub events: SyncQueue<T>,
}
impl Debug for OnChange {
impl<T> OnChange<T> {
pub fn send_event(&self, event: T) {
self.events.push(event);
if let Some(cb) = self.on_change.get() {
cb();
}
}
}
impl<T> Default for OnChange<T> {
fn default() -> Self {
Self {
on_change: Default::default(),
events: Default::default(),
}
}
}
impl<T> Debug for OnChange<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.on_change.get() {
None => f.write_str("None"),
@ -311,7 +336,8 @@ impl MetalConnector {
}) as _),
_ => None,
};
self.send_event(ConnectorEvent::HardwareCursor(hc));
self.on_change
.send_event(ConnectorEvent::HardwareCursor(hc));
}
fn connected(&self) -> bool {
@ -321,13 +347,6 @@ impl MetalConnector {
&& self.primary_plane.get().is_some()
}
fn send_event(&self, event: ConnectorEvent) {
self.events.push(event);
if let Some(oc) = self.on_change.on_change.get() {
oc();
}
}
pub fn schedule_present(&self) {
self.present_trigger.trigger();
}
@ -370,7 +389,7 @@ impl MetalConnector {
!self.cursor_enabled.get(),
);
if let Some(tex) = &buffer.dev_tex {
buffer.dev_fb.copy_texture(&self.state, tex, 0, 0, false);
buffer.dev_fb.copy_texture(tex, 0, 0);
}
for fr in rr.frame_requests.drain(..) {
fr.send_done();
@ -393,7 +412,7 @@ impl MetalConnector {
let buffer = &buffers[self.cursor_front_buffer.get() % buffers.len()];
if swap_buffer {
if let Some(tex) = &buffer.dev_tex {
buffer.dev_fb.copy_texture(&self.state, tex, 0, 0, true);
buffer.dev_fb.copy_texture(tex, 0, 0);
}
}
let (width, height) = buffer.dev_fb.size();
@ -445,7 +464,7 @@ impl Connector for MetalConnector {
}
fn event(&self) -> Option<ConnectorEvent> {
self.events.pop()
self.on_change.events.pop()
}
fn on_change(&self, cb: Rc<dyn Fn()>) {
@ -577,7 +596,6 @@ fn create_connector(
dev: dev.clone(),
backend: backend.clone(),
connector_id: backend.state.connector_ids.next(),
events: Default::default(),
buffers: Default::default(),
next_buffer: Default::default(),
enabled: Cell::new(true),
@ -910,7 +928,7 @@ impl MetalBackend {
if let Some(r) = ctx
.gfx
.reset_status()
.or_else(|| dev.ctx.gfx.reset_status())
.or_else(|| dev.ctx.get().gfx.reset_status())
{
fatal!("EGL context has been reset: {:?}", r);
}
@ -988,9 +1006,9 @@ impl MetalBackend {
dev.futures.remove(&c);
if let Some(c) = dev.connectors.remove(&c) {
if c.connect_sent.get() {
c.send_event(ConnectorEvent::Disconnected);
c.on_change.send_event(ConnectorEvent::Disconnected);
}
c.send_event(ConnectorEvent::Removed);
c.on_change.send_event(ConnectorEvent::Removed);
}
}
let mut preserve = Preserve::default();
@ -1012,7 +1030,7 @@ impl MetalBackend {
|| old.connection != ConnectorStatus::Connected
|| !old.is_same_monitor(&dd)
{
c.send_event(ConnectorEvent::Disconnected);
c.on_change.send_event(ConnectorEvent::Disconnected);
c.connect_sent.set(false);
} else if preserve_any {
preserve.connectors.insert(c.id);
@ -1054,15 +1072,17 @@ impl MetalBackend {
modes.push(mode);
}
}
connector.send_event(ConnectorEvent::Connected(MonitorInfo {
modes,
manufacturer: dd.monitor_manufacturer.clone(),
product: dd.monitor_name.clone(),
serial_number: dd.monitor_serial_number.clone(),
initial_mode: dd.mode.clone().unwrap().to_backend(),
width_mm: dd.mm_width as _,
height_mm: dd.mm_height as _,
}));
connector
.on_change
.send_event(ConnectorEvent::Connected(MonitorInfo {
modes,
manufacturer: dd.monitor_manufacturer.clone(),
product: dd.monitor_name.clone(),
serial_number: dd.monitor_serial_number.clone(),
initial_mode: dd.mode.clone().unwrap().to_backend(),
width_mm: dd.mm_width as _,
height_mm: dd.mm_height as _,
}));
connector.connect_sent.set(true);
connector.send_hardware_cursor();
}
@ -1115,7 +1135,7 @@ impl MetalBackend {
}
}
let gfx = match create_gfx_context(master) {
let gfx = match self.state.create_gfx_context(master, None) {
Ok(r) => r,
Err(e) => return Err(MetalError::CreateRenderContex(e)),
};
@ -1148,7 +1168,8 @@ impl MetalBackend {
handle_events: HandleEvents {
handle_events: Cell::new(None),
},
ctx,
ctx: CloneCell::new(ctx),
on_change: Default::default(),
});
let (connectors, futures) = get_connectors(self, &dev, &resources.connectors)?;
@ -1440,28 +1461,68 @@ impl MetalBackend {
}
}
fn make_render_device(&self, dev: &Rc<MetalDrmDevice>, log: bool) -> bool {
if let Some(ctx) = self.ctx.get() {
if ctx.dev_id == dev.id {
return true;
fn make_render_device(&self, dev: &MetalDrmDevice, force: bool) {
if !force {
if let Some(ctx) = self.ctx.get() {
if ctx.dev_id == dev.id {
return;
}
}
}
self.state.set_render_ctx(Some(dev.ctx.gfx.clone()));
self.ctx.set(Some(dev.ctx.clone()));
let mut preserve = Preserve::default();
let ctx = dev.ctx.get();
self.state.set_render_ctx(Some(ctx.gfx.clone()));
self.ctx.set(Some(ctx));
for dev in self.device_holder.drm_devices.lock().values() {
if let Err(e) = self.init_drm_device(dev, &mut preserve) {
if log {
log::error!("Could not initialize device: {}", ErrorFmt(e));
}
self.re_init_drm_device(&dev);
}
}
fn set_gfx_api(&self, dev: &MetalDrmDevice, api: GfxApi) {
if dev.ctx.get().gfx.gfx_api() == api {
return;
}
let gfx = match self.state.create_gfx_context(&dev.master, Some(api)) {
Ok(r) => r,
Err(e) => {
log::error!(
"Could not create a new graphics context for device {:?}: {}",
dev.devnode,
ErrorFmt(e)
);
return;
}
for connector in dev.connectors.lock().values() {
if connector.connected() {
self.start_connector(connector, false);
}
};
dev.on_change
.send_event(crate::backend::DrmEvent::GfxApiChanged);
dev.ctx.set(Rc::new(MetalRenderContext {
dev_id: dev.id,
gfx,
}));
let mut is_render_ctx = false;
if let Some(render_ctx) = self.ctx.get() {
if render_ctx.dev_id == dev.id {
is_render_ctx = true;
}
}
if is_render_ctx {
self.make_render_device(dev, true);
} else {
if let Some(dev) = self.device_holder.drm_devices.get(&dev.devnum) {
self.re_init_drm_device(&dev);
}
}
}
fn re_init_drm_device(&self, dev: &Rc<MetalDrmDeviceData>) {
let mut preserve = Preserve::default();
if let Err(e) = self.init_drm_device(dev, &mut preserve) {
log::error!("Could not initialize device: {}", ErrorFmt(e));
}
for connector in dev.connectors.lock().values() {
if connector.connected() {
self.start_connector(connector, false);
}
}
true
}
fn init_drm_device(
@ -1616,7 +1677,8 @@ impl MetalBackend {
render_ctx: &MetalRenderContext,
cursor: bool,
) -> Result<RenderBuffer, MetalError> {
let dev_gfx_formats = dev.ctx.gfx.formats();
let ctx = dev.ctx.get();
let dev_gfx_formats = ctx.gfx.formats();
let dev_gfx_format = match dev_gfx_formats.get(&format.drm) {
None => return Err(MetalError::MissingDevFormat(format.name)),
Some(f) => f,
@ -1647,7 +1709,7 @@ impl MetalBackend {
Ok(fb) => Rc::new(fb),
Err(e) => return Err(MetalError::Framebuffer(e)),
};
let dev_img = match dev.ctx.gfx.clone().dmabuf_img(dev_bo.dmabuf()) {
let dev_img = match ctx.gfx.clone().dmabuf_img(dev_bo.dmabuf()) {
Ok(img) => img,
Err(e) => return Err(MetalError::ImportImage(e)),
};
@ -1708,7 +1770,7 @@ impl MetalBackend {
};
// Import the bridge BO into the current device
let dev_img = match dev.ctx.gfx.clone().dmabuf_img(render_bo.dmabuf()) {
let dev_img = match ctx.gfx.clone().dmabuf_img(render_bo.dmabuf()) {
Ok(img) => img,
Err(e) => return Err(MetalError::ImportImage(e)),
};

View file

@ -10,7 +10,6 @@ use {
fixed::Fixed,
format::XRGB8888,
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
gfx_apis::create_gfx_context,
renderer::RenderResult,
state::State,
time::now_usec,
@ -50,6 +49,7 @@ use {
Event, XEvent, Xcon, XconError,
},
},
jay_config::video::GfxApi,
std::{
any::Any,
borrow::Cow,
@ -181,7 +181,7 @@ pub async fn create(state: &Rc<State>) -> Result<Rc<XBackend>, XBackendError> {
Err(e) => return Err(XBackendError::DrmDeviceFstat(e)),
};
let gbm = GbmDevice::new(&drm)?;
let ctx = match create_gfx_context(&drm) {
let ctx = match state.create_gfx_context(&drm, None) {
Ok(r) => r,
Err(e) => return Err(XBackendError::CreateEgl(e)),
};
@ -976,11 +976,20 @@ impl BackendDrmDevice for XDrmDevice {
self.dev
}
fn make_render_device(self: Rc<Self>) {
fn make_render_device(&self) {
log::warn!("make_render_device is not supported by the X backend");
// nothing
}
fn set_gfx_api(&self, _api: GfxApi) {
log::warn!("set_gfx_api is not supported by the X backend");
// nothing
}
fn gtx_api(&self) -> GfxApi {
self.backend.ctx.gfx_api()
}
fn version(&self) -> Result<DrmVersion, DrmError> {
self.backend.gbm.drm.version()
}

View file

@ -39,6 +39,7 @@ use {
},
ahash::AHashSet,
forker::ForkerProxy,
jay_config::video::GfxApi,
std::{cell::Cell, env, future::Future, ops::Deref, rc::Rc, sync::Arc, time::Duration},
thiserror::Error,
uapi::c,
@ -197,6 +198,7 @@ fn start_compositor2(
render_ctx_watchers: Default::default(),
workspace_watchers: Default::default(),
default_workspace_capture: Cell::new(true),
default_gfx_api: Cell::new(GfxApi::OpenGl),
});
state.tracker.register(ClientId::from_raw(0));
create_dummy_output(&state);

View file

@ -40,7 +40,7 @@ use {
logging::LogLevel,
theme::{colors::Colorable, sized::Resizable},
timer::Timer as JayTimer,
video::{Connector, DrmDevice},
video::{Connector, DrmDevice, GfxApi},
Axis, Direction, Workspace,
},
libloading::Library,
@ -582,6 +582,14 @@ impl ConfigProxyHandler {
Ok(())
}
fn handle_set_gfx_api(&self, device: Option<DrmDevice>, api: GfxApi) -> Result<(), CphError> {
match device {
Some(dev) => self.get_drm_device(dev)?.dev.set_gfx_api(api),
_ => self.state.default_gfx_api.set(api),
}
Ok(())
}
fn handle_get_default_workspace_capture(&self) {
self.respond(Response::GetDefaultWorkspaceCapture {
capture: self.state.default_workspace_capture.get(),
@ -1309,6 +1317,9 @@ impl ConfigProxyHandler {
ClientMessage::SetNaturalScrollingEnabled { device, enabled } => self
.handle_set_natural_scrolling_enabled(device, enabled)
.wrn("set_natural_scrolling_enabled")?,
ClientMessage::SetGfxApi { device, api } => {
self.handle_set_gfx_api(device, api).wrn("set_gfx_api")?
}
}
Ok(())
}

View file

@ -310,7 +310,7 @@ impl CursorImageScaled {
extents: Rect::new_sized(-xhot, -yhot, width, height).unwrap(),
tex: ctx
.clone()
.shmem_texture(data, ARGB8888, width, height, width * 4)?,
.shmem_texture(None, data, ARGB8888, width, height, width * 4)?,
}))
}
}
@ -363,7 +363,6 @@ fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed
&img.tex,
extents.x1(),
extents.y1(),
ARGB8888,
None,
None,
scale,
@ -384,7 +383,6 @@ impl Cursor for StaticCursor {
&img.tex,
0,
0,
ARGB8888,
None,
None,
renderer.scale(),
@ -422,7 +420,6 @@ impl Cursor for AnimatedCursor {
&img.tex,
0,
0,
ARGB8888,
None,
None,
renderer.scale(),

View file

@ -1,5 +1,7 @@
use {
crate::utils::{bitflags::BitflagsExt, ptr_ext::PtrExt, stack::Stack},
crate::utils::{
bitflags::BitflagsExt, clonecell::UnsafeCellCloneSafe, ptr_ext::PtrExt, stack::Stack,
},
bstr::{BString, ByteSlice},
std::{
fmt::{Debug, Formatter},
@ -393,6 +395,8 @@ pub enum EdidParseContext {
VideoInputDefinition,
}
unsafe impl UnsafeCellCloneSafe for EdidParseContext {}
struct EdidPushedContext {
stack: Rc<Stack<(usize, EdidParseContext)>>,
}

View file

@ -8,16 +8,18 @@ use {
utils::debug_fn::debug_fn,
},
ahash::AHashMap,
ash::vk,
once_cell::sync::Lazy,
std::fmt::{Debug, Write},
};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq)]
pub struct Format {
pub name: &'static str,
pub bpp: u32,
pub gl_format: GLint,
pub gl_type: GLint,
pub vk_format: vk::Format,
pub drm: u32,
pub wl_id: Option<u32>,
pub external_only_guess: bool,
@ -26,6 +28,12 @@ pub struct Format {
pub pipewire: SpaVideoFormat,
}
impl PartialEq for Format {
fn eq(&self, other: &Self) -> bool {
self.drm == other.drm
}
}
static FORMATS_MAP: Lazy<AHashMap<u32, &'static Format>> = Lazy::new(|| {
let mut map = AHashMap::new();
for format in FORMATS {
@ -80,39 +88,44 @@ pub fn map_wayland_format_id(id: u32) -> u32 {
}
#[allow(dead_code)]
pub static ARGB8888: &Format = &FORMATS[0];
pub static XRGB8888: &Format = &FORMATS[1];
pub static ARGB8888: &Format = &Format {
name: "argb8888",
bpp: 4,
gl_format: GL_BGRA_EXT,
gl_type: GL_UNSIGNED_BYTE,
vk_format: vk::Format::B8G8R8A8_SRGB,
drm: ARGB8888_DRM,
wl_id: Some(ARGB8888_ID),
external_only_guess: false,
has_alpha: true,
shm_supported: true,
pipewire: SPA_VIDEO_FORMAT_BGRA,
};
pub static XRGB8888: &Format = &Format {
name: "xrgb8888",
bpp: 4,
gl_format: GL_BGRA_EXT,
gl_type: GL_UNSIGNED_BYTE,
vk_format: vk::Format::B8G8R8A8_SRGB,
drm: XRGB8888_DRM,
wl_id: Some(XRGB8888_ID),
external_only_guess: false,
has_alpha: false,
shm_supported: true,
pipewire: SPA_VIDEO_FORMAT_BGRx,
};
pub static FORMATS: &[Format] = &[
Format {
name: "argb8888",
bpp: 4,
gl_format: GL_BGRA_EXT,
gl_type: GL_UNSIGNED_BYTE,
drm: ARGB8888_DRM,
wl_id: Some(ARGB8888_ID),
external_only_guess: false,
has_alpha: true,
shm_supported: true,
pipewire: SPA_VIDEO_FORMAT_BGRA,
},
Format {
name: "xrgb8888",
bpp: 4,
gl_format: GL_BGRA_EXT,
gl_type: GL_UNSIGNED_BYTE,
drm: XRGB8888_DRM,
wl_id: Some(XRGB8888_ID),
external_only_guess: false,
has_alpha: false,
shm_supported: true,
pipewire: SPA_VIDEO_FORMAT_BGRx,
},
*ARGB8888,
*XRGB8888,
// *NV12,
Format {
name: "abgr8888",
bpp: 4,
gl_format: GL_RGBA,
gl_type: GL_UNSIGNED_BYTE,
vk_format: vk::Format::R8G8B8A8_SRGB,
drm: fourcc_code('A', 'B', '2', '4'),
wl_id: None,
external_only_guess: false,
@ -125,6 +138,7 @@ pub static FORMATS: &[Format] = &[
bpp: 4,
gl_format: GL_RGBA,
gl_type: GL_UNSIGNED_BYTE,
vk_format: vk::Format::R8G8B8A8_SRGB,
drm: fourcc_code('X', 'B', '2', '4'),
wl_id: None,
external_only_guess: false,

View file

@ -2,7 +2,7 @@ use {
crate::{
cursor::Cursor,
fixed::Fixed,
format::{Format, ARGB8888, XRGB8888},
format::Format,
rect::Rect,
renderer::{renderer_base::RendererBase, RenderResult, Renderer},
scale::Scale,
@ -13,6 +13,7 @@ use {
},
ahash::AHashMap,
indexmap::IndexSet,
jay_config::video::GfxApi,
std::{
any::Any,
cell::Cell,
@ -80,6 +81,7 @@ impl BufferPoints {
}
}
#[derive(Debug)]
pub struct AbsoluteRect {
pub x1: f32,
pub x2: f32,
@ -87,6 +89,7 @@ pub struct AbsoluteRect {
pub y2: f32,
}
#[derive(Debug)]
pub struct FillRect {
pub rect: AbsoluteRect,
pub color: Color,
@ -94,7 +97,6 @@ pub struct FillRect {
pub struct CopyTexture {
pub tex: Rc<dyn GfxTexture>,
pub format: &'static Format,
pub source: BufferPoints,
pub target: AbsoluteRect,
}
@ -124,7 +126,9 @@ pub trait GfxFramebuffer: Debug {
height: i32,
format: &Format,
shm: &[Cell<u8>],
);
) -> Result<(), GfxError>;
fn format(&self) -> &'static Format;
}
impl dyn GfxFramebuffer {
@ -137,37 +141,17 @@ impl dyn GfxFramebuffer {
self.render(ops, Some(&Color { r, g, b, a }));
}
pub fn copy_texture(
&self,
state: &State,
texture: &Rc<dyn GfxTexture>,
x: i32,
y: i32,
alpha: bool,
) {
pub fn copy_texture(&self, texture: &Rc<dyn GfxTexture>, x: i32, y: i32) {
let mut ops = self.take_render_ops();
let scale = Scale::from_int(1);
let (width, height) = self.size();
let extents = Rect::new_sized(0, 0, width, height).unwrap();
let mut renderer = Renderer {
base: RendererBase {
ops: &mut ops,
scaled: false,
scale,
scalef: 1.0,
},
state,
result: None,
logical_extents: extents,
physical_extents: extents,
let mut renderer = RendererBase {
ops: &mut ops,
scaled: false,
scale,
scalef: 1.0,
};
let (format, clear) = match alpha {
true => (ARGB8888, Some(&Color::TRANSPARENT)),
false => (XRGB8888, None),
};
renderer
.base
.render_texture(texture, x, y, format, None, None, scale, i32::MAX, i32::MAX);
renderer.render_texture(texture, x, y, None, None, scale, i32::MAX, i32::MAX);
let clear = self.format().has_alpha.then_some(&Color::TRANSPARENT);
self.render(ops, clear);
}
@ -272,6 +256,17 @@ pub trait GfxImage {
pub trait GfxTexture: Debug {
fn size(&self) -> (i32, i32);
fn as_any(&self) -> &dyn Any;
fn into_any(self: Rc<Self>) -> Rc<dyn Any>;
fn read_pixels(
self: Rc<Self>,
x: i32,
y: i32,
width: i32,
height: i32,
stride: i32,
format: &'static Format,
shm: &[Cell<u8>],
) -> Result<(), GfxError>;
}
pub trait GfxContext: Debug {
@ -281,12 +276,15 @@ pub trait GfxContext: Debug {
fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>>;
fn dmabuf_fb(self: Rc<Self>, buf: &DmaBuf) -> Result<Rc<dyn GfxFramebuffer>, GfxError>;
fn dmabuf_fb(self: Rc<Self>, buf: &DmaBuf) -> Result<Rc<dyn GfxFramebuffer>, GfxError> {
self.dmabuf_img(buf)?.to_framebuffer()
}
fn dmabuf_img(self: Rc<Self>, buf: &DmaBuf) -> Result<Rc<dyn GfxImage>, GfxError>;
fn shmem_texture(
self: Rc<Self>,
old: Option<Rc<dyn GfxTexture>>,
data: &[Cell<u8>],
format: &'static Format,
width: i32,
@ -295,6 +293,8 @@ pub trait GfxContext: Debug {
) -> Result<Rc<dyn GfxTexture>, GfxError>;
fn gbm(&self) -> &GbmDevice;
fn gfx_api(&self) -> GfxApi;
}
#[derive(Debug)]

View file

@ -1,13 +1,49 @@
use {
crate::{
async_engine::AsyncEngine,
gfx_api::{GfxContext, GfxError},
io_uring::IoUring,
utils::errorfmt::ErrorFmt,
video::drm::Drm,
},
jay_config::video::GfxApi,
std::rc::Rc,
};
pub mod gl;
mod vulkan;
pub fn create_gfx_context(drm: &Drm) -> Result<Rc<dyn GfxContext>, GfxError> {
gl::create_gfx_context(drm)
pub fn create_gfx_context(
eng: &Rc<AsyncEngine>,
ring: &Rc<IoUring>,
drm: &Drm,
api: GfxApi,
) -> Result<Rc<dyn GfxContext>, GfxError> {
let mut apis = [GfxApi::OpenGl, GfxApi::Vulkan];
apis.sort_by_key(|&a| if a == api { -1 } else { a as i32 });
let mut last_err = None;
for api in apis {
let res = create_gfx_context_(eng, ring, drm, api);
match res {
Ok(_) => return res,
Err(e) => {
log::warn!("Could not create {:?} API: {}", api, ErrorFmt(&e));
last_err = Some(e);
}
}
}
Err(last_err.unwrap())
}
fn create_gfx_context_(
eng: &Rc<AsyncEngine>,
ring: &Rc<IoUring>,
drm: &Drm,
api: GfxApi,
) -> Result<Rc<dyn GfxContext>, GfxError> {
match api {
GfxApi::OpenGl => gl::create_gfx_context(drm),
GfxApi::Vulkan => vulkan::create_graphics_context(eng, ring, drm),
_ => unreachable!(),
}
}

View file

@ -20,7 +20,6 @@ macro_rules! egl_transparent {
use {
crate::{
format::Format,
gfx_api::{
BufferPoints, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
},
@ -128,6 +127,8 @@ enum RenderError {
ExternalUnsupported,
#[error("OpenGL context does not support any formats")]
NoSupportedFormats,
#[error("Unsupported operation")]
UnsupportedOperation,
}
#[derive(Default)]
@ -214,16 +215,7 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) {
let y1 = 2.0 * (tex.target.y1 / height) - 1.0;
let x2 = 2.0 * (tex.target.x2 / width) - 1.0;
let y2 = 2.0 * (tex.target.y2 / height) - 1.0;
render_texture(
&fb.ctx,
&tex.tex.as_gl(),
tex.format,
x1,
y1,
x2,
y2,
&tex.source,
)
render_texture(&fb.ctx, &tex.tex.as_gl(), x1, y1, x2, y2, &tex.source)
}
}
}
@ -249,7 +241,6 @@ fn fill_boxes3(ctx: &GlRenderContext, boxes: &[f32], color: &Color) {
fn render_texture(
ctx: &GlRenderContext,
texture: &Texture,
format: &Format,
x1: f32,
y1: f32,
x2: f32,
@ -275,7 +266,7 @@ fn render_texture(
},
false => &ctx.tex_internal,
};
let prog = match format.has_alpha {
let prog = match texture.gl.format.has_alpha {
true => {
glEnable(GL_BLEND);
&progs.alpha

View file

@ -263,6 +263,7 @@ impl EglDisplay {
width: buf.width,
height: buf.height,
external_only: format.external_only,
format: buf.format,
}))
}
}

View file

@ -1,8 +1,11 @@
use {
crate::gfx_apis::gl::egl::{
display::EglDisplay,
sys::{EGLImageKHR, EGL_FALSE},
PROCS,
crate::{
format::Format,
gfx_apis::gl::egl::{
display::EglDisplay,
sys::{EGLImageKHR, EGL_FALSE},
PROCS,
},
},
std::rc::Rc,
};
@ -13,6 +16,7 @@ pub struct EglImage {
pub width: i32,
pub height: i32,
pub external_only: bool,
pub format: &'static Format,
}
impl Drop for EglImage {

View file

@ -11,7 +11,7 @@ use {
};
pub struct GlFrameBuffer {
pub _rb: Option<Rc<GlRenderBuffer>>,
pub rb: Rc<GlRenderBuffer>,
pub _tex: Option<Rc<GlTexture>>,
pub ctx: Rc<EglContext>,
pub width: i32,

View file

@ -56,7 +56,7 @@ impl GlRenderBuffer {
let status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
let fb = GlFrameBuffer {
_rb: Some(self.clone()),
rb: self.clone(),
_tex: None,
ctx: self.ctx.clone(),
fbo,

View file

@ -23,6 +23,7 @@ pub struct GlTexture {
pub width: i32,
pub height: i32,
pub external_only: bool,
pub format: &'static Format,
}
pub fn image_target(external_only: bool) -> GLenum {
@ -58,6 +59,7 @@ impl GlTexture {
width: img.width,
height: img.height,
external_only: img.external_only,
format: img.format,
})
}
@ -101,6 +103,7 @@ impl GlTexture {
width,
height,
external_only: false,
format,
})
}
}

View file

@ -14,13 +14,10 @@ use {
renderer::{framebuffer::Framebuffer, image::Image},
GfxGlState, RenderError, Texture,
},
video::{
dmabuf::DmaBuf,
drm::{Drm, NodeType},
gbm::GbmDevice,
},
video::{dmabuf::DmaBuf, drm::Drm, gbm::GbmDevice},
},
ahash::AHashMap,
jay_config::video::GfxApi,
std::{
cell::{Cell, RefCell},
ffi::CString,
@ -82,14 +79,10 @@ impl GlRenderContext {
}
pub(in crate::gfx_apis::gl) fn from_drm_device(drm: &Drm) -> Result<Self, RenderError> {
let nodes = drm.get_nodes()?;
let node = match nodes
.get(&NodeType::Render)
.or_else(|| nodes.get(&NodeType::Primary))
{
None => return Err(RenderError::NoRenderNode),
Some(path) => Rc::new(path.to_owned()),
};
let node = drm
.get_render_node()?
.ok_or(RenderError::NoRenderNode)
.map(Rc::new)?;
let dpy = EglDisplay::create(drm)?;
if !dpy.formats.contains_key(&XRGB8888.drm) {
return Err(RenderError::XRGB888);
@ -226,6 +219,7 @@ impl GfxContext for GlRenderContext {
fn shmem_texture(
self: Rc<Self>,
_old: Option<Rc<dyn GfxTexture>>,
data: &[Cell<u8>],
format: &'static Format,
width: i32,
@ -241,4 +235,8 @@ impl GfxContext for GlRenderContext {
fn gbm(&self) -> &GbmDevice {
&self.gbm
}
fn gfx_api(&self) -> GfxApi {
GfxApi::OpenGl
}
}

View file

@ -1,7 +1,7 @@
use {
crate::{
format::Format,
gfx_api::{GfxApiOpt, GfxFramebuffer},
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer},
gfx_apis::gl::{
gl::{
frame_buffer::GlFrameBuffer,
@ -114,7 +114,12 @@ impl GfxFramebuffer for Framebuffer {
height: i32,
format: &Format,
shm: &[Cell<u8>],
) {
self.copy_to_shm(x, y, width, height, format, shm)
) -> Result<(), GfxError> {
self.copy_to_shm(x, y, width, height, format, shm);
Ok(())
}
fn format(&self) -> &'static Format {
self.gl.rb.img.format
}
}

View file

@ -1,10 +1,12 @@
use {
crate::{
gfx_api::GfxTexture,
gfx_apis::gl::{gl::texture::GlTexture, renderer::context::GlRenderContext},
format::Format,
gfx_api::{GfxError, GfxTexture},
gfx_apis::gl::{gl::texture::GlTexture, renderer::context::GlRenderContext, RenderError},
},
std::{
any::Any,
cell::Cell,
fmt::{Debug, Formatter},
rc::Rc,
},
@ -39,4 +41,21 @@ impl GfxTexture for Texture {
fn as_any(&self) -> &dyn Any {
self
}
fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
fn read_pixels(
self: Rc<Self>,
_x: i32,
_y: i32,
_width: i32,
_height: i32,
_stride: i32,
_format: &Format,
_shm: &[Cell<u8>],
) -> Result<(), GfxError> {
Err(RenderError::UnsupportedOperation.into())
}
}

266
src/gfx_apis/vulkan.rs Normal file
View file

@ -0,0 +1,266 @@
mod allocator;
mod command;
mod descriptor;
mod device;
mod fence;
mod format;
mod image;
mod instance;
mod pipeline;
mod renderer;
mod sampler;
mod semaphore;
mod shaders;
mod staging;
mod util;
use {
crate::{
async_engine::AsyncEngine,
format::Format,
gfx_api::{GfxContext, GfxError, GfxFormat, GfxImage, GfxTexture, ResetStatus},
gfx_apis::vulkan::{
image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer,
},
io_uring::IoUring,
utils::oserror::OsError,
video::{
dmabuf::DmaBuf,
drm::{Drm, DrmError},
gbm::{GbmDevice, GbmError},
},
},
ahash::AHashMap,
ash::{vk, LoadingError},
gpu_alloc::{AllocationError, MapError},
jay_config::video::GfxApi,
once_cell::sync::Lazy,
std::{
cell::Cell,
ffi::{CStr, CString},
rc::Rc,
sync::Arc,
},
thiserror::Error,
uapi::c::dev_t,
};
#[derive(Debug, Error)]
pub enum VulkanError {
#[error("Could not create a GBM device")]
Gbm(#[source] GbmError),
#[error("Could not load libvulkan.so")]
Load(#[source] Arc<LoadingError>),
#[error("Could not list instance extensions")]
InstanceExtensions(#[source] vk::Result),
#[error("Could not list instance layers")]
InstanceLayers(#[source] vk::Result),
#[error("Could not list device extensions")]
DeviceExtensions(#[source] vk::Result),
#[error("Could not create the device")]
CreateDevice(#[source] vk::Result),
#[error("Could not create a semaphore")]
CreateSemaphore(#[source] vk::Result),
#[error("Could not create a fence")]
CreateFence(#[source] vk::Result),
#[error("Could not create the buffer")]
CreateBuffer(#[source] vk::Result),
#[error("Could not create a shader module")]
CreateShaderModule(#[source] vk::Result),
#[error("Missing required instance extension {0:?}")]
MissingInstanceExtension(&'static CStr),
#[error("Could not allocate a command pool")]
AllocateCommandPool(#[source] vk::Result),
#[error("Could not allocate a command buffer")]
AllocateCommandBuffer(#[source] vk::Result),
#[error("Device does not have a graphics queue")]
NoGraphicsQueue,
#[error("Missing required device extension {0:?}")]
MissingDeviceExtension(&'static CStr),
#[error("Could not create an instance")]
CreateInstance(#[source] vk::Result),
#[error("Could not create a debug-utils messenger")]
Messenger(#[source] vk::Result),
#[error("Could not fstat the DRM FD")]
Fstat(#[source] OsError),
#[error("Could not enumerate the physical devices")]
EnumeratePhysicalDevices(#[source] vk::Result),
#[error("Could not find a vulkan device that matches dev_t {0}")]
NoDeviceFound(dev_t),
#[error("Could not load image properties")]
LoadImageProperties(#[source] vk::Result),
#[error("Device does not support rending and texturing from the XRGB8888 format")]
XRGB8888,
#[error("Device does not support syncobj import")]
SyncobjImport,
#[error("Could not start a command buffer")]
BeginCommandBuffer(vk::Result),
#[error("Could not end a command buffer")]
EndCommandBuffer(vk::Result),
#[error("Could not submit a command buffer")]
Submit(vk::Result),
#[error("Could not create a sampler")]
CreateSampler(#[source] vk::Result),
#[error("Could not create a pipeline layout")]
CreatePipelineLayout(#[source] vk::Result),
#[error("Could not create a descriptor set layout")]
CreateDescriptorSetLayout(#[source] vk::Result),
#[error("Could not create a pipeline")]
CreatePipeline(#[source] vk::Result),
#[error("The format is not supported")]
FormatNotSupported,
#[error("The modifier is not supported")]
ModifierNotSupported,
#[error("The modifier does not support this use-case")]
ModifierUseNotSupported,
#[error("The image has a non-positive size")]
NonPositiveImageSize,
#[error("The image is too large")]
ImageTooLarge,
#[error("Could not retrieve device properties")]
GetDeviceProperties(#[source] vk::Result),
#[error("The dmabuf has an incorrect number of planes")]
BadPlaneCount,
#[error("The dmabuf is disjoint but the modifier does not support disjoint buffers")]
DisjointNotSupported,
#[error("Could not create the image")]
CreateImage(#[source] vk::Result),
#[error("Could not create an image view")]
CreateImageView(#[source] vk::Result),
#[error("Could not query the memory fd properties")]
MemoryFdProperties(#[source] vk::Result),
#[error("There is no matching memory type")]
MemoryType,
#[error("Could not duplicate the DRM fd")]
Dupfd(#[source] OsError),
#[error("Could not allocate memory")]
AllocateMemory(#[source] vk::Result),
#[error("Could not allocate memory")]
AllocateMemory2(#[source] AllocationError),
#[error("Could not bind memory to the image")]
BindImageMemory(#[source] vk::Result),
#[error("The format does not support shared memory images")]
ShmNotSupported,
#[error("Could not bind memory to the buffer")]
BindBufferMemory(#[source] vk::Result),
#[error("Could not map the memory")]
MapMemory(#[source] MapError),
#[error("Could not flush modified memory")]
FlushMemory(#[source] vk::Result),
#[error("Could not export a sync file from a dma-buf")]
IoctlExportSyncFile(#[source] OsError),
#[error("Could not import a sync file into a semaphore")]
ImportSyncFile(#[source] vk::Result),
#[error("Could not import a sync file into a dma-buf")]
IoctlImportSyncFile(#[source] OsError),
#[error("Could not export a sync file from a semaphore")]
ExportSyncFile(#[source] vk::Result),
#[error("Could not fetch the render node of the device")]
FetchRenderNode(#[source] DrmError),
#[error("Device has no render node")]
NoRenderNode,
#[error("Overflow while calculating shm buffer size")]
ShmOverflow,
#[error("Shm stride does not match format or width")]
InvalidStride,
#[error("Shm stride and height do not match buffer size")]
InvalidBufferSize,
#[error("The shm parameters are invalid x={x}, y={y}, width={width}, height={height}, stride={stride}")]
InvalidShmParameters {
x: i32,
y: i32,
width: i32,
height: i32,
stride: i32,
},
#[error("Unsupported operation")]
UnsupportedOperation,
}
impl From<VulkanError> for GfxError {
fn from(value: VulkanError) -> Self {
Self(Box::new(value))
}
}
pub static VULKAN_VALIDATION: Lazy<bool> =
Lazy::new(|| std::env::var("JAY_VULKAN_VALIDATION").ok().as_deref() == Some("1"));
pub fn create_graphics_context(
eng: &Rc<AsyncEngine>,
ring: &Rc<IoUring>,
drm: &Drm,
) -> Result<Rc<dyn GfxContext>, GfxError> {
let instance = VulkanInstance::new(eng, ring, *VULKAN_VALIDATION)?;
let device = instance.create_device(drm)?;
let renderer = device.create_renderer()?;
Ok(Rc::new(Context(renderer)))
}
#[derive(Debug)]
struct Context(Rc<VulkanRenderer>);
impl GfxContext for Context {
fn reset_status(&self) -> Option<ResetStatus> {
None
}
fn render_node(&self) -> Rc<CString> {
self.0.device.render_node.clone()
}
fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>> {
self.0.formats.clone()
}
fn dmabuf_img(self: Rc<Self>, buf: &DmaBuf) -> Result<Rc<dyn GfxImage>, GfxError> {
self.0
.import_dmabuf(buf)
.map(|v| v as _)
.map_err(|e| e.into())
}
fn shmem_texture(
self: Rc<Self>,
old: Option<Rc<dyn GfxTexture>>,
data: &[Cell<u8>],
format: &'static Format,
width: i32,
height: i32,
stride: i32,
) -> Result<Rc<dyn GfxTexture>, GfxError> {
if let Some(old) = old {
let old = old.into_vk(&self.0.device.device);
let shm = match &old.ty {
VulkanImageMemory::DmaBuf(_) => unreachable!(),
VulkanImageMemory::Internal(shm) => shm,
};
if old.width as i32 == width
&& old.height as i32 == height
&& shm.stride as i32 == stride
&& old.format.vk_format == format.vk_format
{
shm.upload(data)?;
return Ok(old);
}
}
let tex = self
.0
.create_shm_texture(format, width, height, stride, data, false)?;
Ok(tex as _)
}
fn gbm(&self) -> &GbmDevice {
&self.0.device.gbm
}
fn gfx_api(&self) -> GfxApi {
GfxApi::Vulkan
}
}
impl Drop for Context {
fn drop(&mut self) {
self.0.on_drop();
}
}

View file

@ -0,0 +1,128 @@
use {
crate::{
gfx_apis::vulkan::{device::VulkanDevice, instance::API_VERSION, VulkanError},
utils::{numcell::NumCell, ptr_ext::MutPtrExt},
},
ash::vk::{DeviceMemory, DeviceSize, MemoryRequirements},
gpu_alloc::{Config, GpuAllocator, MemoryBlock, Request, UsageFlags},
gpu_alloc_ash::AshMemoryDevice,
std::{
cell::{Cell, UnsafeCell},
rc::Rc,
},
};
pub struct VulkanAllocator {
pub(super) device: Rc<VulkanDevice>,
pub(super) non_coherent_atom_mask: u64,
allocator: UnsafeCell<GpuAllocator<DeviceMemory>>,
total: NumCell<u64>,
}
pub struct VulkanAllocation {
pub(super) allocator: Rc<VulkanAllocator>,
pub(super) memory: DeviceMemory,
pub(super) offset: DeviceSize,
pub(super) mem: Option<*mut u8>,
pub(super) size: DeviceSize,
block: Cell<Option<MemoryBlock<DeviceMemory>>>,
}
impl Drop for VulkanAllocation {
fn drop(&mut self) {
unsafe {
self.allocator.total.fetch_sub(self.size);
let mut block = self.block.take().unwrap();
if let Some(_ptr) = self.mem {
// log::info!("free = {:?} - {:?} ({})", ptr, ptr.add(block.size() as usize), block.size());
block.unmap(AshMemoryDevice::wrap(&self.allocator.device.device));
}
self.allocator
.allocator
.get()
.deref_mut()
.dealloc(AshMemoryDevice::wrap(&self.allocator.device.device), block);
}
}
}
impl VulkanDevice {
pub fn create_allocator(self: &Rc<Self>) -> Result<Rc<VulkanAllocator>, VulkanError> {
let config = Config::i_am_prototyping();
let props = unsafe {
gpu_alloc_ash::device_properties(
&self.instance.instance,
API_VERSION,
self.physical_device,
)
};
let mut props = props.map_err(VulkanError::GetDeviceProperties)?;
props.buffer_device_address = false;
let non_coherent_atom_size = props.non_coherent_atom_size;
let allocator = GpuAllocator::new(config, props);
Ok(Rc::new(VulkanAllocator {
device: self.clone(),
non_coherent_atom_mask: non_coherent_atom_size - 1,
allocator: UnsafeCell::new(allocator),
total: Default::default(),
}))
}
}
impl VulkanAllocator {
fn allocator(&self) -> &mut GpuAllocator<DeviceMemory> {
unsafe { self.allocator.get().deref_mut() }
}
pub fn alloc(
self: &Rc<Self>,
req: &MemoryRequirements,
usage: UsageFlags,
map: bool,
) -> Result<VulkanAllocation, VulkanError> {
let request = Request {
size: req.size,
align_mask: req.alignment - 1,
usage,
memory_types: req.memory_type_bits,
};
let block = unsafe {
self.allocator()
.alloc(AshMemoryDevice::wrap(&self.device.device), request)
};
let mut block = block.map_err(VulkanError::AllocateMemory2)?;
let ptr = match map {
true => {
let ptr = unsafe {
block.map(
AshMemoryDevice::wrap(&self.device.device),
0,
block.size() as usize,
)
};
Some(ptr.map_err(VulkanError::MapMemory)?.as_ptr())
}
false => None,
};
self.total.fetch_add(block.size());
Ok(VulkanAllocation {
allocator: self.clone(),
memory: *block.memory(),
offset: block.offset(),
mem: ptr,
size: block.size(),
block: Cell::new(Some(block)),
})
}
}
impl Drop for VulkanAllocator {
fn drop(&mut self) {
unsafe {
self.allocator
.get()
.deref_mut()
.cleanup(AshMemoryDevice::wrap(&self.device.device));
}
}
}

View file

@ -0,0 +1,69 @@
use {
crate::gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
ash::vk::{
CommandBuffer, CommandBufferAllocateInfo, CommandBufferLevel, CommandPool,
CommandPoolCreateFlags, CommandPoolCreateInfo,
},
std::rc::Rc,
};
pub struct VulkanCommandPool {
pub(super) device: Rc<VulkanDevice>,
pub(super) pool: CommandPool,
}
pub struct VulkanCommandBuffer {
pub(super) pool: Rc<VulkanCommandPool>,
pub(super) buffer: CommandBuffer,
}
impl Drop for VulkanCommandPool {
fn drop(&mut self) {
unsafe {
self.device.device.destroy_command_pool(self.pool, None);
}
}
}
impl Drop for VulkanCommandBuffer {
fn drop(&mut self) {
unsafe {
self.pool
.device
.device
.free_command_buffers(self.pool.pool, &[self.buffer]);
}
}
}
impl VulkanCommandPool {
pub fn allocate_buffer(self: &Rc<Self>) -> Result<Rc<VulkanCommandBuffer>, VulkanError> {
let create_info = CommandBufferAllocateInfo::builder()
.command_pool(self.pool)
.command_buffer_count(1)
.level(CommandBufferLevel::PRIMARY);
let buffer = unsafe { self.device.device.allocate_command_buffers(&create_info) };
let mut buffer = buffer.map_err(VulkanError::AllocateCommandBuffer)?;
assert_eq!(buffer.len(), 1);
Ok(Rc::new(VulkanCommandBuffer {
pool: self.clone(),
buffer: buffer.pop().unwrap(),
}))
}
}
impl VulkanDevice {
pub fn create_command_pool(self: &Rc<Self>) -> Result<Rc<VulkanCommandPool>, VulkanError> {
let info = CommandPoolCreateInfo::builder()
.queue_family_index(self.graphics_queue_idx)
.flags(
CommandPoolCreateFlags::TRANSIENT | CommandPoolCreateFlags::RESET_COMMAND_BUFFER,
);
let pool = unsafe { self.device.create_command_pool(&info, None) };
let pool = pool.map_err(VulkanError::AllocateCommandPool)?;
Ok(Rc::new(VulkanCommandPool {
device: self.clone(),
pool,
}))
}
}

View file

@ -0,0 +1,49 @@
use {
crate::gfx_apis::vulkan::{device::VulkanDevice, sampler::VulkanSampler, VulkanError},
ash::vk::{
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateFlags,
DescriptorSetLayoutCreateInfo, DescriptorType, ShaderStageFlags,
},
std::{rc::Rc, slice},
};
pub(super) struct VulkanDescriptorSetLayout {
pub(super) device: Rc<VulkanDevice>,
pub(super) layout: DescriptorSetLayout,
pub(super) _sampler: Rc<VulkanSampler>,
}
impl Drop for VulkanDescriptorSetLayout {
fn drop(&mut self) {
unsafe {
self.device
.device
.destroy_descriptor_set_layout(self.layout, None);
}
}
}
impl VulkanDevice {
pub(super) fn create_descriptor_set_layout(
&self,
sampler: &Rc<VulkanSampler>,
) -> Result<Rc<VulkanDescriptorSetLayout>, VulkanError> {
let immutable_sampler = [sampler.sampler];
let binding = DescriptorSetLayoutBinding::builder()
.stage_flags(ShaderStageFlags::FRAGMENT)
.immutable_samplers(&immutable_sampler)
.descriptor_count(1)
.descriptor_type(DescriptorType::COMBINED_IMAGE_SAMPLER)
.build();
let create_info = DescriptorSetLayoutCreateInfo::builder()
.bindings(slice::from_ref(&binding))
.flags(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR_KHR);
let layout = unsafe { self.device.create_descriptor_set_layout(&create_info, None) };
let layout = layout.map_err(VulkanError::CreateDescriptorSetLayout)?;
Ok(Rc::new(VulkanDescriptorSetLayout {
device: sampler.device.clone(),
layout,
_sampler: sampler.clone(),
}))
}
}

View file

@ -0,0 +1,343 @@
use {
crate::{
format::XRGB8888,
gfx_apis::vulkan::{
format::VulkanFormat,
instance::{
map_extension_properties, ApiVersionDisplay, Extensions, VulkanInstance,
API_VERSION,
},
util::OnDrop,
VulkanError,
},
video::{drm::Drm, gbm::GbmDevice},
},
ahash::AHashMap,
arrayvec::ArrayVec,
ash::{
extensions::khr::{ExternalFenceFd, ExternalMemoryFd, ExternalSemaphoreFd, PushDescriptor},
vk::{
DeviceCreateInfo, DeviceMemory, DeviceQueueCreateInfo, ExtExternalMemoryDmaBufFn,
ExtImageDrmFormatModifierFn, ExtPhysicalDeviceDrmFn, ExtQueueFamilyForeignFn,
ExternalSemaphoreFeatureFlags, ExternalSemaphoreHandleTypeFlags,
ExternalSemaphoreProperties, KhrDriverPropertiesFn, KhrExternalFenceFdFn,
KhrExternalMemoryFdFn, KhrExternalSemaphoreFdFn, KhrPushDescriptorFn,
MemoryPropertyFlags, MemoryType, PhysicalDevice, PhysicalDeviceDriverProperties,
PhysicalDeviceDriverPropertiesKHR, PhysicalDeviceDrmPropertiesEXT,
PhysicalDeviceDynamicRenderingFeatures, PhysicalDeviceExternalSemaphoreInfo,
PhysicalDeviceProperties, PhysicalDeviceProperties2,
PhysicalDeviceSynchronization2Features, PhysicalDeviceTimelineSemaphoreFeatures, Queue,
QueueFlags, MAX_MEMORY_TYPES,
},
Device,
},
isnt::std_1::collections::IsntHashMap2Ext,
std::{
ffi::{CStr, CString},
rc::Rc,
},
uapi::Ustr,
};
pub struct VulkanDevice {
pub(super) physical_device: PhysicalDevice,
pub(super) render_node: Rc<CString>,
pub(super) gbm: GbmDevice,
pub(super) instance: Rc<VulkanInstance>,
pub(super) device: Device,
pub(super) external_memory_fd: ExternalMemoryFd,
pub(super) external_semaphore_fd: ExternalSemaphoreFd,
pub(super) external_fence_fd: ExternalFenceFd,
pub(super) push_descriptor: PushDescriptor,
pub(super) formats: AHashMap<u32, VulkanFormat>,
pub(super) memory_types: ArrayVec<MemoryType, MAX_MEMORY_TYPES>,
pub(super) graphics_queue: Queue,
pub(super) graphics_queue_idx: u32,
}
impl Drop for VulkanDevice {
fn drop(&mut self) {
unsafe {
self.device.destroy_device(None);
}
}
}
impl VulkanDevice {
pub(super) fn find_memory_type(
&self,
flags: MemoryPropertyFlags,
memory_type_bits: u32,
) -> Option<u32> {
for (idx, ty) in self.memory_types.iter().enumerate() {
if memory_type_bits & (1 << idx as u32) != 0 {
if ty.property_flags.contains(flags) {
return Some(idx as _);
}
}
}
None
}
}
struct FreeMem<'a>(&'a Device, DeviceMemory);
impl<'a> Drop for FreeMem<'a> {
fn drop(&mut self) {
unsafe {
self.0.free_memory(self.1, None);
}
}
}
impl VulkanInstance {
fn get_device_extensions(&self, phy_dev: PhysicalDevice) -> Result<Extensions, VulkanError> {
unsafe {
self.instance
.enumerate_device_extension_properties(phy_dev)
.map(map_extension_properties)
.map_err(VulkanError::DeviceExtensions)
}
}
fn find_dev(&self, drm: &Drm) -> Result<PhysicalDevice, VulkanError> {
let stat = match uapi::fstat(drm.raw()) {
Ok(s) => s,
Err(e) => return Err(VulkanError::Fstat(e.into())),
};
let dev = stat.st_rdev;
log::info!(
"Searching for vulkan device with devnum {}:{}",
uapi::major(dev),
uapi::minor(dev)
);
let phy_devs = unsafe { self.instance.enumerate_physical_devices() };
let phy_devs = match phy_devs {
Ok(d) => d,
Err(e) => return Err(VulkanError::EnumeratePhysicalDevices(e)),
};
let mut devices = vec![];
for phy_dev in phy_devs {
let props = unsafe { self.instance.get_physical_device_properties(phy_dev) };
if props.api_version < API_VERSION {
devices.push((props, None, None));
continue;
}
let extensions = match self.get_device_extensions(phy_dev) {
Ok(e) => e,
Err(e) => {
log::error!(
"Could not enumerate extensions of device with id {}: {:#}",
props.device_id,
e
);
devices.push((props, None, None));
continue;
}
};
if !extensions.contains_key(ExtPhysicalDeviceDrmFn::name()) {
devices.push((props, Some(extensions), None));
continue;
}
let has_driver_props = extensions.contains_key(KhrDriverPropertiesFn::name());
let mut drm_props = PhysicalDeviceDrmPropertiesEXT::builder().build();
let mut driver_props = PhysicalDeviceDriverPropertiesKHR::builder().build();
let mut props2 = PhysicalDeviceProperties2::builder().push_next(&mut drm_props);
if has_driver_props {
props2 = props2.push_next(&mut driver_props);
}
unsafe {
self.instance
.get_physical_device_properties2(phy_dev, &mut props2);
}
let primary_dev =
uapi::makedev(drm_props.primary_major as _, drm_props.primary_minor as _);
let render_dev =
uapi::makedev(drm_props.render_major as _, drm_props.render_minor as _);
if primary_dev == dev || render_dev == dev {
log::info!("Device with id {} matches", props.device_id);
log_device(&props, Some(&extensions), Some(&driver_props));
return Ok(phy_dev);
}
devices.push((props, Some(extensions), Some(driver_props)));
}
if devices.is_empty() {
log::warn!("Found no devices");
} else {
log::warn!("Found the following devices but none matches:");
for (props, extensions, driver_props) in devices.iter() {
log::warn!("Found the following devices but none matches:");
log::warn!("-----");
log_device(props, extensions.as_ref(), driver_props.as_ref());
}
}
Err(VulkanError::NoDeviceFound(dev))
}
fn find_graphics_queue(&self, phy_dev: PhysicalDevice) -> Result<u32, VulkanError> {
let props = unsafe {
self.instance
.get_physical_device_queue_family_properties(phy_dev)
};
props
.iter()
.position(|p| p.queue_flags.contains(QueueFlags::GRAPHICS))
.map(|v| v as _)
.ok_or(VulkanError::NoGraphicsQueue)
}
fn supports_semaphore_import(&self, phy_dev: PhysicalDevice) -> bool {
let mut props = ExternalSemaphoreProperties::builder().build();
let info = PhysicalDeviceExternalSemaphoreInfo::builder()
.handle_type(ExternalSemaphoreHandleTypeFlags::OPAQUE_FD)
.build();
unsafe {
self.instance
.get_physical_device_external_semaphore_properties(phy_dev, &info, &mut props);
}
props
.external_semaphore_features
.contains(ExternalSemaphoreFeatureFlags::IMPORTABLE)
}
pub fn create_device(self: &Rc<Self>, drm: &Drm) -> Result<Rc<VulkanDevice>, VulkanError> {
let render_node = drm
.get_render_node()
.map_err(VulkanError::FetchRenderNode)?
.ok_or(VulkanError::NoRenderNode)
.map(Rc::new)?;
let gbm = GbmDevice::new(drm).map_err(VulkanError::Gbm)?;
let phy_dev = self.find_dev(drm)?;
let extensions = self.get_device_extensions(phy_dev)?;
for &ext in REQUIRED_DEVICE_EXTENSIONS {
if extensions.not_contains_key(ext) {
return Err(VulkanError::MissingDeviceExtension(ext));
}
}
let graphics_queue_idx = self.find_graphics_queue(phy_dev)?;
if !self.supports_semaphore_import(phy_dev) {
return Err(VulkanError::SyncobjImport);
}
let enabled_extensions: Vec<_> = REQUIRED_DEVICE_EXTENSIONS
.iter()
.map(|n| n.as_ptr())
.collect();
let mut semaphore_features =
PhysicalDeviceTimelineSemaphoreFeatures::builder().timeline_semaphore(true);
let mut synchronization2_features =
PhysicalDeviceSynchronization2Features::builder().synchronization2(true);
let mut dynamic_rendering_features =
PhysicalDeviceDynamicRenderingFeatures::builder().dynamic_rendering(true);
let queue_create_info = DeviceQueueCreateInfo::builder()
.queue_family_index(graphics_queue_idx)
.queue_priorities(&[1.0])
.build();
let device_create_info = DeviceCreateInfo::builder()
.push_next(&mut semaphore_features)
.push_next(&mut synchronization2_features)
.push_next(&mut dynamic_rendering_features)
.queue_create_infos(std::slice::from_ref(&queue_create_info))
.enabled_extension_names(&enabled_extensions);
let device = unsafe {
self.instance
.create_device(phy_dev, &device_create_info, None)
};
let device = match device {
Ok(d) => d,
Err(e) => return Err(VulkanError::CreateDevice(e)),
};
let destroy_device = OnDrop(|| unsafe { device.destroy_device(None) });
let formats = self.load_formats(phy_dev)?;
let supports_xrgb8888 = formats
.get(&XRGB8888.drm)
.map(|f| {
let mut supports_rendering = false;
let mut supports_texturing = false;
f.modifiers.values().for_each(|v| {
supports_rendering |= v.render_max_extents.is_some();
supports_texturing |= v.texture_max_extents.is_some();
});
supports_rendering && supports_texturing
})
.unwrap_or(false);
if !supports_xrgb8888 {
return Err(VulkanError::XRGB8888);
}
destroy_device.forget();
let external_memory_fd = ExternalMemoryFd::new(&self.instance, &device);
let external_semaphore_fd = ExternalSemaphoreFd::new(&self.instance, &device);
let external_fence_fd = ExternalFenceFd::new(&self.instance, &device);
let push_descriptor = PushDescriptor::new(&self.instance, &device);
let memory_properties =
unsafe { self.instance.get_physical_device_memory_properties(phy_dev) };
let memory_types = memory_properties.memory_types
[..memory_properties.memory_type_count as _]
.iter()
.copied()
.collect();
let graphics_queue = unsafe { device.get_device_queue(graphics_queue_idx, 0) };
Ok(Rc::new(VulkanDevice {
physical_device: phy_dev,
render_node,
gbm,
instance: self.clone(),
device,
external_memory_fd,
external_semaphore_fd,
external_fence_fd,
push_descriptor,
formats,
memory_types,
graphics_queue,
graphics_queue_idx,
}))
}
}
const REQUIRED_DEVICE_EXTENSIONS: &[&CStr] = &[
KhrExternalMemoryFdFn::name(),
KhrExternalSemaphoreFdFn::name(),
KhrExternalFenceFdFn::name(),
ExtExternalMemoryDmaBufFn::name(),
ExtQueueFamilyForeignFn::name(),
ExtImageDrmFormatModifierFn::name(),
KhrPushDescriptorFn::name(),
];
fn log_device(
props: &PhysicalDeviceProperties,
extensions: Option<&Extensions>,
driver_props: Option<&PhysicalDeviceDriverProperties>,
) {
log::info!(" api version: {}", ApiVersionDisplay(props.api_version));
log::info!(
" driver version: {}",
ApiVersionDisplay(props.driver_version)
);
log::info!(" vendor id: {}", props.vendor_id);
log::info!(" device id: {}", props.device_id);
log::info!(" device type: {:?}", props.device_type);
unsafe {
log::info!(
" device name: {}",
Ustr::from_ptr(props.device_name.as_ptr()).display()
);
}
if props.api_version < API_VERSION {
log::warn!(" device does not support vulkan 1.3");
}
if let Some(extensions) = extensions {
if !extensions.contains_key(ExtPhysicalDeviceDrmFn::name()) {
log::warn!(" device does support not the VK_EXT_physical_device_drm extension");
}
}
if let Some(driver_props) = driver_props {
unsafe {
log::info!(
" driver: {} ({})",
Ustr::from_ptr(driver_props.driver_name.as_ptr()).display(),
Ustr::from_ptr(driver_props.driver_info.as_ptr()).display()
);
}
}
}

View file

@ -0,0 +1,49 @@
use {
crate::gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
ash::vk::{
ExportFenceCreateInfo, ExternalFenceHandleTypeFlags, Fence, FenceCreateInfo,
FenceGetFdInfoKHR,
},
std::rc::Rc,
uapi::OwnedFd,
};
pub struct VulkanFence {
pub(super) device: Rc<VulkanDevice>,
pub(super) fence: Fence,
}
impl Drop for VulkanFence {
fn drop(&mut self) {
unsafe {
self.device.device.destroy_fence(self.fence, None);
}
}
}
impl VulkanDevice {
pub fn create_fence(self: &Rc<Self>) -> Result<Rc<VulkanFence>, VulkanError> {
let fence = {
let mut export_info = ExportFenceCreateInfo::builder()
.handle_types(ExternalFenceHandleTypeFlags::SYNC_FD);
let create_info = FenceCreateInfo::builder().push_next(&mut export_info);
let fence = unsafe { self.device.create_fence(&create_info, None) };
fence.map_err(VulkanError::CreateFence)?
};
Ok(Rc::new(VulkanFence {
device: self.clone(),
fence,
}))
}
}
impl VulkanFence {
pub fn export_syncfile(&self) -> Result<Rc<OwnedFd>, VulkanError> {
let info = FenceGetFdInfoKHR::builder()
.fence(self.fence)
.handle_type(ExternalFenceHandleTypeFlags::SYNC_FD);
let res = unsafe { self.device.external_fence_fd.get_fence_fd(&info) };
res.map_err(VulkanError::ExportSyncFile)
.map(|fd| Rc::new(OwnedFd::new(fd)))
}
}

View file

@ -0,0 +1,277 @@
use {
crate::{
format::{Format, FORMATS},
gfx_apis::vulkan::{instance::VulkanInstance, VulkanError},
video::Modifier,
},
ahash::AHashMap,
ash::{
vk,
vk::{
DrmFormatModifierPropertiesEXT, DrmFormatModifierPropertiesListEXT,
ExternalImageFormatProperties, ExternalMemoryFeatureFlags,
ExternalMemoryHandleTypeFlags, FormatFeatureFlags, FormatProperties2,
ImageFormatProperties2, ImageTiling, ImageType, ImageUsageFlags, PhysicalDevice,
PhysicalDeviceExternalImageFormatInfo, PhysicalDeviceImageDrmFormatModifierInfoEXT,
PhysicalDeviceImageFormatInfo2, SharingMode,
},
},
isnt::std_1::collections::IsntHashMapExt,
};
#[derive(Debug)]
pub struct VulkanFormat {
pub format: &'static Format,
pub modifiers: AHashMap<Modifier, VulkanModifier>,
pub shm: Option<VulkanShmFormat>,
pub features: FormatFeatureFlags,
}
#[derive(Debug)]
pub struct VulkanFormatFeatures {
pub linear_sampling: bool,
}
#[derive(Debug)]
pub struct VulkanModifier {
pub modifier: Modifier,
pub planes: usize,
pub features: FormatFeatureFlags,
pub render_max_extents: Option<VulkanMaxExtents>,
pub texture_max_extents: Option<VulkanMaxExtents>,
}
#[derive(Copy, Clone, Debug)]
pub struct VulkanMaxExtents {
pub width: u32,
pub height: u32,
}
#[derive(Debug)]
pub struct VulkanShmFormat {
pub max_extents: VulkanMaxExtents,
}
const FRAMEBUFFER_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(
0 | FormatFeatureFlags::COLOR_ATTACHMENT.as_raw()
| FormatFeatureFlags::COLOR_ATTACHMENT_BLEND.as_raw(),
);
const TEX_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(
0 | FormatFeatureFlags::SAMPLED_IMAGE.as_raw()
| FormatFeatureFlags::TRANSFER_SRC.as_raw()
| FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR.as_raw(),
);
const SHM_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(
0 | FormatFeatureFlags::TRANSFER_SRC.as_raw()
| FormatFeatureFlags::TRANSFER_DST.as_raw()
| TEX_FEATURES.as_raw(),
);
const FRAMEBUFFER_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
0 | ImageUsageFlags::COLOR_ATTACHMENT.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(),
);
const TEX_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
0 | ImageUsageFlags::SAMPLED.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(),
);
const SHM_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
0 | ImageUsageFlags::TRANSFER_SRC.as_raw()
| ImageUsageFlags::TRANSFER_DST.as_raw()
| TEX_USAGE.as_raw(),
);
impl VulkanInstance {
pub(super) fn load_formats(
&self,
phy_dev: PhysicalDevice,
) -> Result<AHashMap<u32, VulkanFormat>, VulkanError> {
let mut res = AHashMap::new();
for format in FORMATS {
self.load_format(phy_dev, format, &mut res)?;
}
Ok(res)
}
fn load_format(
&self,
phy_dev: PhysicalDevice,
format: &'static Format,
dst: &mut AHashMap<u32, VulkanFormat>,
) -> Result<(), VulkanError> {
let mut modifier_props = DrmFormatModifierPropertiesListEXT::builder().build();
let mut format_properties = FormatProperties2::builder()
.push_next(&mut modifier_props)
.build();
unsafe {
self.instance.get_physical_device_format_properties2(
phy_dev,
format.vk_format,
&mut format_properties,
);
}
let shm = self.load_shm_format(phy_dev, format, &format_properties)?;
let modifiers = self.load_drm_format(phy_dev, format, &modifier_props)?;
if shm.is_some() || modifiers.is_not_empty() {
dst.insert(
format.drm,
VulkanFormat {
format,
modifiers,
shm,
features: format_properties.format_properties.optimal_tiling_features,
},
);
}
Ok(())
}
fn load_shm_format(
&self,
phy_dev: PhysicalDevice,
format: &Format,
props: &FormatProperties2,
) -> Result<Option<VulkanShmFormat>, VulkanError> {
if !props
.format_properties
.optimal_tiling_features
.contains(SHM_FEATURES)
{
return Ok(None);
}
let format_info = PhysicalDeviceImageFormatInfo2::builder()
.ty(ImageType::TYPE_2D)
.format(format.vk_format)
.tiling(ImageTiling::OPTIMAL)
.usage(SHM_USAGE);
let mut format_properties = ImageFormatProperties2::builder();
let res = unsafe {
self.instance.get_physical_device_image_format_properties2(
phy_dev,
&format_info,
&mut format_properties,
)
};
if let Err(e) = res {
return match e {
vk::Result::ERROR_FORMAT_NOT_SUPPORTED => Ok(None),
_ => Err(VulkanError::LoadImageProperties(e)),
};
}
Ok(Some(VulkanShmFormat {
max_extents: VulkanMaxExtents {
width: format_properties.image_format_properties.max_extent.width,
height: format_properties.image_format_properties.max_extent.height,
},
}))
}
fn load_drm_format(
&self,
phy_dev: PhysicalDevice,
format: &Format,
props: &DrmFormatModifierPropertiesListEXT,
) -> Result<AHashMap<Modifier, VulkanModifier>, VulkanError> {
if props.drm_format_modifier_count == 0 {
return Ok(AHashMap::new());
}
let mut drm_mods = vec![
DrmFormatModifierPropertiesEXT::default();
props.drm_format_modifier_count as usize
];
let mut modifier_props = DrmFormatModifierPropertiesListEXT::builder()
.drm_format_modifier_properties(&mut drm_mods)
.build();
let mut format_properties = FormatProperties2::builder()
.push_next(&mut modifier_props)
.build();
unsafe {
self.instance.get_physical_device_format_properties2(
phy_dev,
format.vk_format,
&mut format_properties,
);
};
let mut mods = AHashMap::new();
for modifier in drm_mods {
let render_max_extents = self.get_max_extents(
phy_dev,
format,
FRAMEBUFFER_FEATURES,
FRAMEBUFFER_USAGE,
&modifier,
)?;
let texture_max_extents =
self.get_max_extents(phy_dev, format, TEX_FEATURES, TEX_USAGE, &modifier)?;
mods.insert(
modifier.drm_format_modifier,
VulkanModifier {
modifier: modifier.drm_format_modifier,
planes: modifier.drm_format_modifier_plane_count as _,
features: modifier.drm_format_modifier_tiling_features,
render_max_extents,
texture_max_extents,
},
);
}
Ok(mods)
}
fn get_max_extents(
&self,
phy_dev: PhysicalDevice,
format: &Format,
features: FormatFeatureFlags,
usage: ImageUsageFlags,
props: &DrmFormatModifierPropertiesEXT,
) -> Result<Option<VulkanMaxExtents>, VulkanError> {
if !props.drm_format_modifier_tiling_features.contains(features) {
return Ok(None);
}
let mut modifier_info = PhysicalDeviceImageDrmFormatModifierInfoEXT::builder()
.drm_format_modifier(props.drm_format_modifier)
.sharing_mode(SharingMode::EXCLUSIVE)
.build();
let mut external_image_format_info = PhysicalDeviceExternalImageFormatInfo::builder()
.handle_type(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
.build();
let image_format_info = PhysicalDeviceImageFormatInfo2::builder()
.ty(ImageType::TYPE_2D)
.format(format.vk_format)
.usage(usage)
.tiling(ImageTiling::DRM_FORMAT_MODIFIER_EXT)
.push_next(&mut external_image_format_info)
.push_next(&mut modifier_info)
.build();
let mut external_image_format_props = ExternalImageFormatProperties::builder().build();
let mut image_format_props = ImageFormatProperties2::builder()
.push_next(&mut external_image_format_props)
.build();
let res = unsafe {
self.instance.get_physical_device_image_format_properties2(
phy_dev,
&image_format_info,
&mut image_format_props,
)
};
if let Err(e) = res {
return match e {
vk::Result::ERROR_FORMAT_NOT_SUPPORTED => Ok(None),
_ => Err(VulkanError::LoadImageProperties(e)),
};
}
let importable = external_image_format_props
.external_memory_properties
.external_memory_features
.contains(ExternalMemoryFeatureFlags::IMPORTABLE);
if !importable {
return Ok(None);
}
Ok(Some(VulkanMaxExtents {
width: image_format_props.image_format_properties.max_extent.width,
height: image_format_props.image_format_properties.max_extent.height,
}))
}
}

View file

@ -0,0 +1,582 @@
use {
crate::{
format::Format,
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture},
gfx_apis::vulkan::{
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanMaxExtents,
renderer::VulkanRenderer, util::OnDrop, VulkanError,
},
theme::Color,
utils::clonecell::CloneCell,
video::{
dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
Modifier,
},
},
ash::vk::{
BindImageMemoryInfo, BindImagePlaneMemoryInfo, ComponentMapping, ComponentSwizzle,
DeviceMemory, DeviceSize, Extent3D, ExternalMemoryHandleTypeFlags,
ExternalMemoryImageCreateInfo, FormatFeatureFlags, Image, ImageAspectFlags,
ImageCreateFlags, ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT,
ImageLayout, ImageMemoryRequirementsInfo2, ImagePlaneMemoryRequirementsInfo,
ImageSubresourceRange, ImageTiling, ImageType, ImageUsageFlags, ImageView,
ImageViewCreateInfo, ImageViewType, ImportMemoryFdInfoKHR, MemoryAllocateInfo,
MemoryDedicatedAllocateInfo, MemoryPropertyFlags, MemoryRequirements2, SampleCountFlags,
SharingMode, SubresourceLayout,
},
gpu_alloc::UsageFlags,
std::{
any::Any,
cell::{Cell, RefCell},
fmt::{Debug, Formatter},
mem,
rc::Rc,
},
};
pub struct VulkanDmaBufImageTemplate {
pub(super) renderer: Rc<VulkanRenderer>,
pub(super) format: &'static Format,
pub(super) width: u32,
pub(super) height: u32,
pub(super) modifier: Modifier,
pub(super) disjoint: bool,
pub(super) planes: PlaneVec<DmaBufPlane>,
pub(super) render_max_extents: Option<VulkanMaxExtents>,
pub(super) texture_max_extents: Option<VulkanMaxExtents>,
}
pub struct VulkanImage {
pub(super) renderer: Rc<VulkanRenderer>,
pub(super) format: &'static Format,
pub(super) width: u32,
pub(super) height: u32,
pub(super) stride: u32,
pub(super) texture_view: ImageView,
pub(super) render_view: Option<ImageView>,
pub(super) image: Image,
pub(super) is_undefined: Cell<bool>,
pub(super) ty: VulkanImageMemory,
pub(super) render_ops: CloneCell<Vec<GfxApiOpt>>,
}
pub enum VulkanImageMemory {
DmaBuf(VulkanDmaBufImage),
Internal(VulkanShmImage),
}
pub struct VulkanDmaBufImage {
pub(super) template: Rc<VulkanDmaBufImageTemplate>,
pub(super) mems: PlaneVec<DeviceMemory>,
}
pub struct VulkanShmImage {
pub(super) to_flush: RefCell<Option<Vec<u8>>>,
pub(super) size: DeviceSize,
pub(super) stride: u32,
pub(super) _allocation: VulkanAllocation,
}
impl Drop for VulkanDmaBufImage {
fn drop(&mut self) {
unsafe {
for &mem in &self.mems {
self.template.renderer.device.device.free_memory(mem, None);
}
}
}
}
impl Drop for VulkanImage {
fn drop(&mut self) {
unsafe {
self.renderer
.device
.device
.destroy_image_view(self.texture_view, None);
if let Some(render_view) = self.render_view {
self.renderer
.device
.device
.destroy_image_view(render_view, None);
}
self.renderer.device.device.destroy_image(self.image, None);
}
}
}
impl VulkanShmImage {
pub fn upload(&self, buffer: &[Cell<u8>]) -> Result<(), VulkanError> {
let buffer = unsafe {
std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len()).to_vec()
};
*self.to_flush.borrow_mut() = Some(buffer);
Ok(())
}
}
impl VulkanRenderer {
pub fn create_shm_texture(
self: &Rc<Self>,
format: &'static Format,
width: i32,
height: i32,
stride: i32,
data: &[Cell<u8>],
for_download: bool,
) -> Result<Rc<VulkanImage>, VulkanError> {
if width <= 0 || height <= 0 || stride <= 0 {
return Err(VulkanError::NonPositiveImageSize);
}
let width = width as u32;
let height = height as u32;
let stride = stride as u32;
if stride % format.bpp != 0 || stride / format.bpp < width {
return Err(VulkanError::InvalidStride);
}
let vk_format = self
.device
.formats
.get(&format.drm)
.ok_or(VulkanError::FormatNotSupported)?;
let shm = vk_format.shm.as_ref().ok_or(VulkanError::ShmNotSupported)?;
if width > shm.max_extents.width || height > shm.max_extents.height {
return Err(VulkanError::ImageTooLarge);
}
let size = stride.checked_mul(height).ok_or(VulkanError::ShmOverflow)?;
let usage = ImageUsageFlags::TRANSFER_SRC
| match for_download {
true => ImageUsageFlags::COLOR_ATTACHMENT,
false => ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER_DST,
};
let create_info = ImageCreateInfo::builder()
.image_type(ImageType::TYPE_2D)
.format(format.vk_format)
.mip_levels(1)
.array_layers(1)
.tiling(ImageTiling::OPTIMAL)
.samples(SampleCountFlags::TYPE_1)
.sharing_mode(SharingMode::EXCLUSIVE)
.initial_layout(ImageLayout::UNDEFINED)
.extent(Extent3D {
width,
height,
depth: 1,
})
.usage(usage)
.build();
let image = unsafe { self.device.device.create_image(&create_info, None) };
let image = image.map_err(VulkanError::CreateImage)?;
let destroy_image = OnDrop(|| unsafe { self.device.device.destroy_image(image, None) });
let memory_requirements =
unsafe { self.device.device.get_image_memory_requirements(image) };
let allocation =
self.allocator
.alloc(&memory_requirements, UsageFlags::FAST_DEVICE_ACCESS, false)?;
let res = unsafe {
self.device
.device
.bind_image_memory(image, allocation.memory, allocation.offset)
};
res.map_err(VulkanError::BindImageMemory)?;
let image_view_create_info = ImageViewCreateInfo::builder()
.image(image)
.format(format.vk_format)
.view_type(ImageViewType::TYPE_2D)
.subresource_range(ImageSubresourceRange {
aspect_mask: ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
});
let view = unsafe {
self.device
.device
.create_image_view(&image_view_create_info, None)
};
let view = view.map_err(VulkanError::CreateImageView)?;
let shm = VulkanShmImage {
to_flush: Default::default(),
size: size as u64,
stride,
_allocation: allocation,
};
shm.upload(data)?;
destroy_image.forget();
Ok(Rc::new(VulkanImage {
renderer: self.clone(),
format,
width,
height,
stride,
texture_view: view,
render_view: None,
image,
is_undefined: Cell::new(true),
ty: VulkanImageMemory::Internal(shm),
render_ops: Default::default(),
}))
}
pub fn import_dmabuf(
self: &Rc<Self>,
dmabuf: &DmaBuf,
) -> Result<Rc<VulkanDmaBufImageTemplate>, VulkanError> {
let format = self
.device
.formats
.get(&dmabuf.format.drm)
.ok_or(VulkanError::FormatNotSupported)?;
let modifier = format
.modifiers
.get(&dmabuf.modifier)
.ok_or(VulkanError::ModifierNotSupported)?;
if dmabuf.width <= 0 || dmabuf.height <= 0 {
return Err(VulkanError::NonPositiveImageSize);
}
let width = dmabuf.width as u32;
let height = dmabuf.height as u32;
let can_render = match &modifier.render_max_extents {
None => false,
Some(t) => width <= t.width && height <= t.height,
};
let can_texture = match &modifier.texture_max_extents {
None => false,
Some(t) => width <= t.width && height <= t.height,
};
if !can_render && !can_texture {
if modifier.render_max_extents.is_none() && modifier.texture_max_extents.is_none() {
return Err(VulkanError::ModifierUseNotSupported);
}
return Err(VulkanError::ImageTooLarge);
}
if modifier.planes != dmabuf.planes.len() {
return Err(VulkanError::BadPlaneCount);
}
let disjoint = dmabuf.is_disjoint();
if disjoint && !modifier.features.contains(FormatFeatureFlags::DISJOINT) {
return Err(VulkanError::DisjointNotSupported);
}
Ok(Rc::new(VulkanDmaBufImageTemplate {
renderer: self.clone(),
format: dmabuf.format,
width,
height,
modifier: dmabuf.modifier,
disjoint,
planes: dmabuf.planes.clone(),
render_max_extents: modifier.render_max_extents,
texture_max_extents: modifier.texture_max_extents,
}))
}
}
impl VulkanDevice {
pub fn create_image_view(
&self,
image: Image,
format: &'static Format,
for_rendering: bool,
) -> Result<ImageView, VulkanError> {
let create_info = ImageViewCreateInfo::builder()
.image(image)
.view_type(ImageViewType::TYPE_2D)
.format(format.vk_format)
.components(ComponentMapping {
r: ComponentSwizzle::IDENTITY,
g: ComponentSwizzle::IDENTITY,
b: ComponentSwizzle::IDENTITY,
a: match format.has_alpha || for_rendering {
true => ComponentSwizzle::IDENTITY,
false => ComponentSwizzle::ONE,
},
})
.subresource_range(ImageSubresourceRange {
aspect_mask: ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
});
let view = unsafe { self.device.create_image_view(&create_info, None) };
view.map_err(VulkanError::CreateImageView)
}
}
impl VulkanDmaBufImageTemplate {
pub fn create_framebuffer(self: &Rc<Self>) -> Result<Rc<VulkanImage>, VulkanError> {
self.create_image(true, None)
}
pub fn create_texture(
self: &Rc<Self>,
shm: Option<VulkanShmImage>,
) -> Result<Rc<VulkanImage>, VulkanError> {
self.create_image(false, shm)
}
fn create_image(
self: &Rc<Self>,
for_rendering: bool,
shm: Option<VulkanShmImage>,
) -> Result<Rc<VulkanImage>, VulkanError> {
let device = &self.renderer.device;
let max_extents = match for_rendering {
true => self.render_max_extents,
false => self.texture_max_extents,
};
let max_extents = max_extents.ok_or(VulkanError::ModifierUseNotSupported)?;
if self.width > max_extents.width || self.height > max_extents.height {
return Err(VulkanError::ImageTooLarge);
}
let image = {
let plane_layouts: PlaneVec<_> = self
.planes
.iter()
.map(|p| SubresourceLayout {
offset: p.offset as _,
row_pitch: p.stride as _,
size: 0,
array_pitch: 0,
depth_pitch: 0,
})
.collect();
let mut mod_info = ImageDrmFormatModifierExplicitCreateInfoEXT::builder()
.drm_format_modifier(self.modifier)
.plane_layouts(&plane_layouts)
.build();
let mut memory_image_create_info = ExternalMemoryImageCreateInfo::builder()
.handle_types(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
.build();
let flags = match self.disjoint {
true => ImageCreateFlags::DISJOINT,
false => ImageCreateFlags::empty(),
};
let usage = ImageUsageFlags::TRANSFER_SRC
| match (for_rendering, shm.is_some()) {
(true, _) => ImageUsageFlags::COLOR_ATTACHMENT,
(false, false) => ImageUsageFlags::SAMPLED,
(false, true) => ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER_DST,
};
let create_info = ImageCreateInfo::builder()
.image_type(ImageType::TYPE_2D)
.format(self.format.vk_format)
.mip_levels(1)
.array_layers(1)
.tiling(ImageTiling::DRM_FORMAT_MODIFIER_EXT)
.samples(SampleCountFlags::TYPE_1)
.sharing_mode(SharingMode::EXCLUSIVE)
.initial_layout(ImageLayout::UNDEFINED)
.extent(Extent3D {
width: self.width,
height: self.height,
depth: 1,
})
.usage(usage)
.flags(flags)
.push_next(&mut memory_image_create_info)
.push_next(&mut mod_info)
.build();
let image = unsafe { device.device.create_image(&create_info, None) };
image.map_err(VulkanError::CreateImage)?
};
let destroy_image = OnDrop(|| unsafe { device.device.destroy_image(image, None) });
let num_device_memories = match self.disjoint {
true => self.planes.len(),
false => 1,
};
let mut device_memories = PlaneVec::new();
let mut free_device_memories = PlaneVec::new();
let mut bind_image_plane_memory_infos = PlaneVec::new();
for plane_idx in 0..num_device_memories {
let dma_buf_plane = &self.planes[plane_idx];
let memory_fd_properties = unsafe {
device.external_memory_fd.get_memory_fd_properties(
ExternalMemoryHandleTypeFlags::DMA_BUF_EXT,
dma_buf_plane.fd.raw(),
)
};
let memory_fd_properties =
memory_fd_properties.map_err(VulkanError::MemoryFdProperties)?;
let mut image_memory_requirements_info =
ImageMemoryRequirementsInfo2::builder().image(image);
let mut image_plane_memory_requirements_info;
if self.disjoint {
let plane_aspect = match plane_idx {
0 => ImageAspectFlags::MEMORY_PLANE_0_EXT,
1 => ImageAspectFlags::MEMORY_PLANE_1_EXT,
2 => ImageAspectFlags::MEMORY_PLANE_2_EXT,
3 => ImageAspectFlags::MEMORY_PLANE_3_EXT,
_ => unreachable!(),
};
image_plane_memory_requirements_info =
ImagePlaneMemoryRequirementsInfo::builder().plane_aspect(plane_aspect);
image_memory_requirements_info = image_memory_requirements_info
.push_next(&mut image_plane_memory_requirements_info);
bind_image_plane_memory_infos.push(
BindImagePlaneMemoryInfo::builder()
.plane_aspect(plane_aspect)
.build(),
);
}
let mut memory_requirements = MemoryRequirements2::default();
unsafe {
device.device.get_image_memory_requirements2(
&image_memory_requirements_info,
&mut memory_requirements,
);
}
let memory_type_bits = memory_requirements.memory_requirements.memory_type_bits
& memory_fd_properties.memory_type_bits;
let memory_type_index = self
.renderer
.device
.find_memory_type(MemoryPropertyFlags::empty(), memory_type_bits)
.ok_or(VulkanError::MemoryType)?;
let fd = uapi::fcntl_dupfd_cloexec(dma_buf_plane.fd.raw(), 0)
.map_err(|e| VulkanError::Dupfd(e.into()))?;
let mut memory_dedicated_allocate_info =
MemoryDedicatedAllocateInfo::builder().image(image).build();
let mut import_memory_fd_info = ImportMemoryFdInfoKHR::builder()
.fd(fd.raw())
.handle_type(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
.build();
let memory_allocate_info = MemoryAllocateInfo::builder()
.allocation_size(memory_requirements.memory_requirements.size)
.memory_type_index(memory_type_index)
.push_next(&mut import_memory_fd_info)
.push_next(&mut memory_dedicated_allocate_info)
.build();
let device_memory =
unsafe { device.device.allocate_memory(&memory_allocate_info, None) };
let device_memory = device_memory.map_err(VulkanError::AllocateMemory)?;
fd.unwrap();
device_memories.push(device_memory);
free_device_memories.push(OnDrop(move || unsafe {
device.device.free_memory(device_memory, None)
}));
}
let mut bind_image_memory_infos = Vec::with_capacity(num_device_memories);
for (i, mem) in device_memories.iter().copied().enumerate() {
let mut info = BindImageMemoryInfo::builder().image(image).memory(mem);
if self.disjoint {
info = info.push_next(&mut bind_image_plane_memory_infos[i]);
}
bind_image_memory_infos.push(info.build());
}
let res = unsafe { device.device.bind_image_memory2(&bind_image_memory_infos) };
res.map_err(VulkanError::BindImageMemory)?;
let texture_view = device.create_image_view(image, self.format, false)?;
let render_view = device.create_image_view(image, self.format, true)?;
free_device_memories.drain(..).for_each(mem::forget);
mem::forget(destroy_image);
Ok(Rc::new(VulkanImage {
renderer: self.renderer.clone(),
texture_view,
render_view: Some(render_view),
image,
width: self.width,
height: self.height,
stride: 0,
render_ops: Default::default(),
ty: VulkanImageMemory::DmaBuf(VulkanDmaBufImage {
template: self.clone(),
mems: device_memories,
}),
format: self.format,
is_undefined: Cell::new(true),
}))
}
}
impl GfxImage for VulkanDmaBufImageTemplate {
fn to_framebuffer(self: Rc<Self>) -> Result<Rc<dyn GfxFramebuffer>, GfxError> {
self.create_framebuffer()
.map(|v| v as _)
.map_err(|e| e.into())
}
fn to_texture(self: Rc<Self>) -> Result<Rc<dyn GfxTexture>, GfxError> {
self.create_texture(None)
.map(|v| v as _)
.map_err(|e| e.into())
}
fn width(&self) -> i32 {
self.width as i32
}
fn height(&self) -> i32 {
self.height as i32
}
}
impl Debug for VulkanImage {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VulkanDmaBufImage").finish_non_exhaustive()
}
}
impl GfxFramebuffer for VulkanImage {
fn as_any(&self) -> &dyn Any {
self
}
fn take_render_ops(&self) -> Vec<GfxApiOpt> {
self.render_ops.take()
}
fn size(&self) -> (i32, i32) {
(self.width as _, self.height as _)
}
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) {
self.renderer.execute(self, &ops, clear).unwrap();
}
fn copy_to_shm(
&self,
_x: i32,
_y: i32,
_width: i32,
_height: i32,
_format: &Format,
_shm: &[Cell<u8>],
) -> Result<(), GfxError> {
return Err(VulkanError::UnsupportedOperation.into());
}
fn format(&self) -> &'static Format {
self.format
}
}
impl GfxTexture for VulkanImage {
fn size(&self) -> (i32, i32) {
(self.width as _, self.height as _)
}
fn as_any(&self) -> &dyn Any {
self
}
fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
fn read_pixels(
self: Rc<Self>,
x: i32,
y: i32,
width: i32,
height: i32,
stride: i32,
format: &'static Format,
shm: &[Cell<u8>],
) -> Result<(), GfxError> {
self.renderer
.read_pixels(&self, x, y, width, height, stride, format, shm)
.map_err(|e| e.into())
}
}

View file

@ -0,0 +1,228 @@
use {
crate::{
async_engine::AsyncEngine,
gfx_apis::vulkan::{util::OnDrop, VulkanError, VULKAN_VALIDATION},
io_uring::IoUring,
},
ahash::{AHashMap, AHashSet},
ash::{
extensions::ext::DebugUtils,
vk::{
api_version_major, api_version_minor, api_version_patch, api_version_variant,
ApplicationInfo, Bool32, DebugUtilsMessageSeverityFlagsEXT,
DebugUtilsMessageTypeFlagsEXT, DebugUtilsMessengerCallbackDataEXT,
DebugUtilsMessengerCreateInfoEXT, DebugUtilsMessengerEXT, ExtDebugUtilsFn,
ExtValidationFeaturesFn, ExtensionProperties, InstanceCreateInfo, LayerProperties,
ValidationFeaturesEXT, API_VERSION_1_3, FALSE,
},
Entry, Instance, LoadingError,
},
isnt::std_1::collections::IsntHashMap2Ext,
log::Level,
once_cell::sync::Lazy,
std::{
ffi::{c_void, CStr, CString},
fmt::{Display, Formatter},
iter::IntoIterator,
rc::Rc,
slice,
sync::Arc,
},
uapi::{ustr, Ustr},
};
pub struct VulkanInstance {
pub(super) _entry: &'static Entry,
pub(super) instance: Instance,
pub(super) debug_utils: DebugUtils,
pub(super) messenger: DebugUtilsMessengerEXT,
pub(super) eng: Rc<AsyncEngine>,
pub(super) ring: Rc<IoUring>,
}
impl VulkanInstance {
pub fn new(
eng: &Rc<AsyncEngine>,
ring: &Rc<IoUring>,
validation: bool,
) -> Result<Rc<Self>, VulkanError> {
static ENTRY: Lazy<Result<Entry, Arc<LoadingError>>> =
Lazy::new(|| unsafe { Entry::load() }.map_err(Arc::new));
let entry = match &*ENTRY {
Ok(e) => e,
Err(e) => return Err(VulkanError::Load(e.clone())),
};
let extensions = get_instance_extensions(entry, None)?;
for &ext in REQUIRED_INSTANCE_EXTENSIONS {
if extensions.not_contains_key(ext) {
return Err(VulkanError::MissingInstanceExtension(ext));
}
}
let mut enabled_extensions: Vec<_> = REQUIRED_INSTANCE_EXTENSIONS
.iter()
.map(|c| c.as_ptr())
.collect();
let app_info = ApplicationInfo::builder()
.api_version(API_VERSION)
.application_name(ustr!("jay").as_c_str().unwrap())
.application_version(1);
let mut severity = DebugUtilsMessageSeverityFlagsEXT::empty()
| DebugUtilsMessageSeverityFlagsEXT::ERROR
| DebugUtilsMessageSeverityFlagsEXT::WARNING;
if *VULKAN_VALIDATION {
severity |= DebugUtilsMessageSeverityFlagsEXT::INFO
| DebugUtilsMessageSeverityFlagsEXT::VERBOSE;
}
let types = DebugUtilsMessageTypeFlagsEXT::empty()
| DebugUtilsMessageTypeFlagsEXT::VALIDATION
| DebugUtilsMessageTypeFlagsEXT::PERFORMANCE
| DebugUtilsMessageTypeFlagsEXT::GENERAL;
let mut debug_info = DebugUtilsMessengerCreateInfoEXT::builder()
.message_severity(severity)
.message_type(types)
.pfn_user_callback(Some(debug_callback));
let validation_features = [
// ash::vk::ValidationFeatureEnableEXT::DEBUG_PRINTF,
// ash::vk::ValidationFeatureEnableEXT::BEST_PRACTICES,
// ash::vk::ValidationFeatureEnableEXT::SYNCHRONIZATION_VALIDATION,
// ash::vk::ValidationFeatureEnableEXT::GPU_ASSISTED,
];
let mut validation_info =
ValidationFeaturesEXT::builder().enabled_validation_features(&validation_features);
let mut create_info = InstanceCreateInfo::builder()
.application_info(&app_info)
.push_next(&mut debug_info);
let validation_layer_name = VALIDATION_LAYER.as_ptr();
if validation {
if get_available_layers(entry)?.contains(VALIDATION_LAYER) {
create_info =
create_info.enabled_layer_names(slice::from_ref(&validation_layer_name));
let extensions = get_instance_extensions(entry, Some(VALIDATION_LAYER))?;
if extensions.contains_key(ExtValidationFeaturesFn::name()) {
enabled_extensions.push(ExtValidationFeaturesFn::name().as_ptr());
create_info = create_info.push_next(&mut validation_info);
} else {
log::warn!("{:?} is not available", ExtValidationFeaturesFn::name(),);
}
} else {
log::warn!(
"Vulkan validation was requested but validation layers are not available"
);
}
}
create_info = create_info.enabled_extension_names(&enabled_extensions);
let instance = match unsafe { entry.create_instance(&create_info, None) } {
Ok(i) => i,
Err(e) => return Err(VulkanError::CreateInstance(e)),
};
let destroy_instance = OnDrop(|| unsafe { instance.destroy_instance(None) });
let debug_utils = DebugUtils::new(entry, &instance);
let messenger = unsafe { debug_utils.create_debug_utils_messenger(&debug_info, None) };
let messenger = match messenger {
Ok(m) => m,
Err(e) => return Err(VulkanError::Messenger(e)),
};
destroy_instance.forget();
Ok(Rc::new(Self {
_entry: entry,
instance,
debug_utils,
messenger,
eng: eng.clone(),
ring: ring.clone(),
}))
}
}
impl Drop for VulkanInstance {
fn drop(&mut self) {
unsafe {
self.debug_utils
.destroy_debug_utils_messenger(self.messenger, None);
self.instance.destroy_instance(None);
}
}
}
const REQUIRED_INSTANCE_EXTENSIONS: &[&CStr] = &[ExtDebugUtilsFn::name()];
const VALIDATION_LAYER: &CStr = c"VK_LAYER_KHRONOS_validation";
pub type Extensions = AHashMap<CString, u32>;
fn get_instance_extensions(entry: &Entry, layer: Option<&CStr>) -> Result<Extensions, VulkanError> {
entry
.enumerate_instance_extension_properties(layer)
.map_err(VulkanError::InstanceExtensions)
.map(map_extension_properties)
}
fn get_available_layers(entry: &Entry) -> Result<AHashSet<CString>, VulkanError> {
entry
.enumerate_instance_layer_properties()
.map_err(VulkanError::InstanceLayers)
.map(map_layer_properties)
}
fn map_layer_properties(props: Vec<LayerProperties>) -> AHashSet<CString> {
props
.into_iter()
.map(|e| unsafe { CStr::from_ptr(e.layer_name.as_ptr()).to_owned() })
.collect()
}
pub fn map_extension_properties(props: Vec<ExtensionProperties>) -> Extensions {
props
.into_iter()
.map(|e| {
let s = unsafe { CStr::from_ptr(e.extension_name.as_ptr()) };
(s.to_owned(), e.spec_version)
})
.collect()
}
unsafe extern "system" fn debug_callback(
message_severity: DebugUtilsMessageSeverityFlagsEXT,
_message_types: DebugUtilsMessageTypeFlagsEXT,
p_callback_data: *const DebugUtilsMessengerCallbackDataEXT,
_p_user_data: *mut c_void,
) -> Bool32 {
let _level = match message_severity {
DebugUtilsMessageSeverityFlagsEXT::ERROR => Level::Error,
DebugUtilsMessageSeverityFlagsEXT::WARNING => Level::Warn,
DebugUtilsMessageSeverityFlagsEXT::INFO => Level::Info,
DebugUtilsMessageSeverityFlagsEXT::VERBOSE => Level::Trace,
_ => Level::Warn,
};
let data = &*p_callback_data;
let message = Ustr::from_ptr(data.p_message);
let message_id_name = if data.p_message_id_name.is_null() {
ustr!("<null>")
} else {
Ustr::from_ptr(data.p_message_id_name)
};
log::log!(
Level::Info,
"VULKAN: {} ({})",
message.display(),
message_id_name.display()
);
FALSE
}
pub struct ApiVersionDisplay(pub u32);
impl Display for ApiVersionDisplay {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}.{}.{}.{}",
api_version_variant(self.0),
api_version_major(self.0),
api_version_minor(self.0),
api_version_patch(self.0),
)
}
}
pub const API_VERSION: u32 = API_VERSION_1_3;

View file

@ -0,0 +1,182 @@
use {
crate::{
format::ARGB8888,
gfx_apis::vulkan::{
descriptor::VulkanDescriptorSetLayout, device::VulkanDevice, shaders::VulkanShader,
util::OnDrop, VulkanError,
},
},
arrayvec::ArrayVec,
ash::vk::{
BlendFactor, BlendOp, ColorComponentFlags, CullModeFlags, DynamicState, FrontFace,
GraphicsPipelineCreateInfo, Pipeline, PipelineCache, PipelineColorBlendAttachmentState,
PipelineColorBlendStateCreateInfo, PipelineDynamicStateCreateInfo,
PipelineInputAssemblyStateCreateInfo, PipelineLayout, PipelineLayoutCreateInfo,
PipelineMultisampleStateCreateInfo, PipelineRasterizationStateCreateInfo,
PipelineRenderingCreateInfo, PipelineShaderStageCreateInfo,
PipelineVertexInputStateCreateInfo, PipelineViewportStateCreateInfo, PolygonMode,
PrimitiveTopology, PushConstantRange, SampleCountFlags, ShaderStageFlags,
},
std::{mem, rc::Rc, slice},
uapi::ustr,
};
pub(super) struct VulkanPipeline {
pub(super) vert: Rc<VulkanShader>,
pub(super) _frag: Rc<VulkanShader>,
pub(super) frag_push_offset: u32,
pub(super) pipeline_layout: PipelineLayout,
pub(super) pipeline: Pipeline,
pub(super) _frag_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
}
pub(super) struct PipelineCreateInfo {
pub(super) vert: Rc<VulkanShader>,
pub(super) frag: Rc<VulkanShader>,
pub(super) alpha: bool,
pub(super) frag_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
}
impl VulkanDevice {
pub(super) fn create_pipeline<V, F>(
&self,
info: PipelineCreateInfo,
) -> Result<Rc<VulkanPipeline>, VulkanError> {
self.create_pipeline_(info, mem::size_of::<V>() as _, mem::size_of::<F>() as _)
}
fn create_pipeline_(
&self,
info: PipelineCreateInfo,
vert_push_size: u32,
frag_push_size: u32,
) -> Result<Rc<VulkanPipeline>, VulkanError> {
let pipeline_layout = {
let mut push_constant_ranges = ArrayVec::<_, 2>::new();
let mut push_constant_offset = 0;
if vert_push_size > 0 {
push_constant_ranges.push(
PushConstantRange::builder()
.stage_flags(ShaderStageFlags::VERTEX)
.offset(0)
.size(vert_push_size)
.build(),
);
push_constant_offset += vert_push_size;
}
if frag_push_size > 0 {
push_constant_ranges.push(
PushConstantRange::builder()
.stage_flags(ShaderStageFlags::FRAGMENT)
.offset(push_constant_offset)
.size(frag_push_size)
.build(),
);
#[allow(unused_assignments)]
{
push_constant_offset += frag_push_size;
}
}
let mut descriptor_set_layouts = ArrayVec::<_, 1>::new();
descriptor_set_layouts
.extend(info.frag_descriptor_set_layout.as_ref().map(|l| l.layout));
let create_info = PipelineLayoutCreateInfo::builder()
.push_constant_ranges(&push_constant_ranges)
.set_layouts(&descriptor_set_layouts);
let layout = unsafe { self.device.create_pipeline_layout(&create_info, None) };
layout.map_err(VulkanError::CreatePipelineLayout)?
};
let destroy_layout =
OnDrop(|| unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) });
let pipeline = {
let main = ustr!("main").as_c_str().unwrap();
let stages = [
PipelineShaderStageCreateInfo::builder()
.stage(ShaderStageFlags::VERTEX)
.module(info.vert.module)
.name(main)
.build(),
PipelineShaderStageCreateInfo::builder()
.stage(ShaderStageFlags::FRAGMENT)
.module(info.frag.module)
.name(main)
.build(),
];
let input_assembly_state = PipelineInputAssemblyStateCreateInfo::builder()
.topology(PrimitiveTopology::TRIANGLE_STRIP);
let vertex_input_state = PipelineVertexInputStateCreateInfo::builder();
let rasterization_state = PipelineRasterizationStateCreateInfo::builder()
.polygon_mode(PolygonMode::FILL)
.cull_mode(CullModeFlags::BACK)
.line_width(1.0)
.front_face(FrontFace::COUNTER_CLOCKWISE);
let multisampling_state = PipelineMultisampleStateCreateInfo::builder()
.sample_shading_enable(false)
.rasterization_samples(SampleCountFlags::TYPE_1);
let mut blending = PipelineColorBlendAttachmentState::builder()
.color_write_mask(ColorComponentFlags::RGBA);
if info.alpha {
blending = blending
.blend_enable(true)
.src_color_blend_factor(BlendFactor::ONE)
.dst_color_blend_factor(BlendFactor::ONE_MINUS_SRC_ALPHA)
.color_blend_op(BlendOp::ADD)
.src_alpha_blend_factor(BlendFactor::ONE)
.dst_alpha_blend_factor(BlendFactor::ONE_MINUS_SRC_ALPHA)
.alpha_blend_op(BlendOp::ADD);
}
let color_blend_state = PipelineColorBlendStateCreateInfo::builder()
.attachments(slice::from_ref(&blending));
let dynamic_states = [DynamicState::VIEWPORT, DynamicState::SCISSOR];
let dynamic_state =
PipelineDynamicStateCreateInfo::builder().dynamic_states(&dynamic_states);
let viewport_state = PipelineViewportStateCreateInfo::builder()
.viewport_count(1)
.scissor_count(1);
let mut pipeline_rendering_create_info = PipelineRenderingCreateInfo::builder()
.color_attachment_formats(slice::from_ref(&ARGB8888.vk_format));
let create_info = GraphicsPipelineCreateInfo::builder()
.push_next(&mut pipeline_rendering_create_info)
.stages(&stages)
.input_assembly_state(&input_assembly_state)
.vertex_input_state(&vertex_input_state)
.rasterization_state(&rasterization_state)
.multisample_state(&multisampling_state)
.color_blend_state(&color_blend_state)
.dynamic_state(&dynamic_state)
.viewport_state(&viewport_state)
.layout(pipeline_layout);
let pipelines = unsafe {
self.device.create_graphics_pipelines(
PipelineCache::null(),
slice::from_ref(&create_info),
None,
)
};
let mut pipelines = pipelines
.map_err(|e| e.1)
.map_err(VulkanError::CreatePipeline)?;
assert_eq!(pipelines.len(), 1);
pipelines.pop().unwrap()
};
destroy_layout.forget();
Ok(Rc::new(VulkanPipeline {
vert: info.vert,
_frag: info.frag,
frag_push_offset: vert_push_size,
pipeline_layout,
pipeline,
_frag_descriptor_set_layout: info.frag_descriptor_set_layout,
}))
}
}
impl Drop for VulkanPipeline {
fn drop(&mut self) {
unsafe {
let device = &self.vert.device.device;
device.destroy_pipeline(self.pipeline, None);
device.destroy_pipeline_layout(self.pipeline_layout, None);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
use {
crate::gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
ash::vk::{
BorderColor, Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode,
},
std::rc::Rc,
};
pub struct VulkanSampler {
pub(super) device: Rc<VulkanDevice>,
pub(super) sampler: Sampler,
}
impl VulkanDevice {
pub(super) fn create_sampler(self: &Rc<Self>) -> Result<Rc<VulkanSampler>, VulkanError> {
let create_info = SamplerCreateInfo::builder()
.mag_filter(Filter::LINEAR)
.min_filter(Filter::LINEAR)
.mipmap_mode(SamplerMipmapMode::NEAREST)
.address_mode_u(SamplerAddressMode::REPEAT)
.address_mode_v(SamplerAddressMode::REPEAT)
.address_mode_w(SamplerAddressMode::REPEAT)
.max_anisotropy(1.0)
.min_lod(0.0)
.max_lod(0.25)
.border_color(BorderColor::FLOAT_TRANSPARENT_BLACK);
let sampler = unsafe { self.device.create_sampler(&create_info, None) };
let sampler = sampler.map_err(VulkanError::CreateSampler)?;
Ok(Rc::new(VulkanSampler {
device: self.clone(),
sampler,
}))
}
}
impl Drop for VulkanSampler {
fn drop(&mut self) {
unsafe {
self.device.device.destroy_sampler(self.sampler, None);
}
}
}

View file

@ -0,0 +1,54 @@
use {
crate::gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
ash::vk::{
ExternalSemaphoreHandleTypeFlags, ImportSemaphoreFdInfoKHR, Semaphore, SemaphoreCreateInfo,
SemaphoreImportFlags,
},
std::{mem, rc::Rc},
uapi::OwnedFd,
};
pub struct VulkanSemaphore {
pub(super) device: Rc<VulkanDevice>,
pub(super) semaphore: Semaphore,
}
impl Drop for VulkanSemaphore {
fn drop(&mut self) {
unsafe {
self.device.device.destroy_semaphore(self.semaphore, None);
}
}
}
impl VulkanDevice {
pub fn create_semaphore(self: &Rc<Self>) -> Result<Rc<VulkanSemaphore>, VulkanError> {
let sem = {
let create_info = SemaphoreCreateInfo::builder();
let sem = unsafe { self.device.create_semaphore(&create_info, None) };
sem.map_err(VulkanError::CreateSemaphore)?
};
Ok(Rc::new(VulkanSemaphore {
device: self.clone(),
semaphore: sem,
}))
}
}
impl VulkanSemaphore {
pub fn import_syncfile(&self, syncfile: OwnedFd) -> Result<(), VulkanError> {
let fd_info = ImportSemaphoreFdInfoKHR::builder()
.fd(syncfile.raw())
.flags(SemaphoreImportFlags::TEMPORARY)
.handle_type(ExternalSemaphoreHandleTypeFlags::SYNC_FD)
.semaphore(self.semaphore);
let res = unsafe {
self.device
.external_semaphore_fd
.import_semaphore_fd(&fd_info)
};
mem::forget(syncfile);
res.map_err(VulkanError::ImportSyncFile)?;
Ok(())
}
}

View file

@ -0,0 +1,67 @@
use {
crate::gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
ash::vk::{ShaderModule, ShaderModuleCreateInfo},
std::rc::Rc,
uapi::Packed,
};
pub const FILL_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/fill.vert.spv"));
pub const FILL_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/fill.frag.spv"));
pub const TEX_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/tex.vert.spv"));
pub const TEX_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/tex.frag.spv"));
pub struct VulkanShader {
pub(super) device: Rc<VulkanDevice>,
pub(super) module: ShaderModule,
}
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct FillVertPushConstants {
pub pos: [[f32; 2]; 4],
}
unsafe impl Packed for FillVertPushConstants {}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct FillFragPushConstants {
pub color: [f32; 4],
}
unsafe impl Packed for FillFragPushConstants {}
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct TexVertPushConstants {
pub pos: [[f32; 2]; 4],
pub tex_pos: [[f32; 2]; 4],
}
unsafe impl Packed for TexVertPushConstants {}
impl VulkanDevice {
pub(super) fn create_shader(
self: &Rc<Self>,
src: &[u8],
) -> Result<Rc<VulkanShader>, VulkanError> {
let src: Vec<u32> = uapi::pod_iter(src).unwrap().collect();
let create_info = ShaderModuleCreateInfo::builder().code(&src);
let module = unsafe { self.device.create_shader_module(&create_info, None) };
module
.map_err(VulkanError::CreateShaderModule)
.map(|m| VulkanShader {
device: self.clone(),
module: m,
})
.map(Rc::new)
}
}
impl Drop for VulkanShader {
fn drop(&mut self) {
unsafe {
self.device.device.destroy_shader_module(self.module, None);
}
}
}

View file

@ -0,0 +1,11 @@
#version 450
layout(push_constant, std430) uniform Data {
layout(offset = 32) vec4 color;
} data;
layout(location = 0) out vec4 out_color;
void main() {
out_color = data.color;
}

View file

@ -0,0 +1,18 @@
#version 450
//#extension GL_EXT_debug_printf : enable
layout(push_constant, std430) uniform Data {
layout(offset = 0) vec2 pos[4];
} data;
void main() {
vec2 pos;
switch (gl_VertexIndex) {
case 0: pos = data.pos[0]; break;
case 1: pos = data.pos[1]; break;
case 2: pos = data.pos[2]; break;
case 3: pos = data.pos[3]; break;
}
gl_Position = vec4(pos, 0.0, 1.0);
// debugPrintfEXT("gl_Position = %v4f", gl_Position);
}

View file

@ -0,0 +1,9 @@
#version 450
layout(set = 0, binding = 0) uniform sampler2D tex;
layout(location = 0) in vec2 tex_pos;
layout(location = 0) out vec4 out_color;
void main() {
out_color = textureLod(tex, tex_pos, 0);
}

View file

@ -0,0 +1,21 @@
#version 450
//#extension GL_EXT_debug_printf : enable
layout(push_constant, std430) uniform Data {
layout(offset = 0) vec2 pos[4];
layout(offset = 32) vec2 tex_pos[4];
} data;
layout(location = 0) out vec2 tex_pos;
void main() {
vec2 pos;
switch (gl_VertexIndex) {
case 0: pos = data.pos[0]; tex_pos = data.tex_pos[0]; break;
case 1: pos = data.pos[1]; tex_pos = data.tex_pos[1]; break;
case 2: pos = data.pos[2]; tex_pos = data.tex_pos[2]; break;
case 3: pos = data.pos[3]; tex_pos = data.tex_pos[3]; break;
}
gl_Position = vec4(pos, 0.0, 1.0);
// debugPrintfEXT("gl_Position = %v4f, tex_pos = %v2f", gl_Position, tex_pos);
}

View file

@ -0,0 +1,103 @@
use {
crate::gfx_apis::vulkan::{
allocator::VulkanAllocation, device::VulkanDevice, renderer::VulkanRenderer, util::OnDrop,
VulkanError,
},
ash::vk::{Buffer, BufferCreateInfo, BufferUsageFlags, MappedMemoryRange},
gpu_alloc::UsageFlags,
std::rc::Rc,
};
pub struct VulkanStagingBuffer {
pub(super) device: Rc<VulkanDevice>,
pub(super) allocation: VulkanAllocation,
pub(super) buffer: Buffer,
pub(super) size: u64,
}
impl VulkanRenderer {
pub(super) fn create_staging_buffer(
self: &Rc<Self>,
size: u64,
upload: bool,
download: bool,
transient: bool,
) -> Result<VulkanStagingBuffer, VulkanError> {
let mut vk_usage = BufferUsageFlags::empty();
let mut usage = UsageFlags::empty();
if upload {
vk_usage |= BufferUsageFlags::TRANSFER_SRC;
usage |= UsageFlags::UPLOAD;
}
if download {
vk_usage |= BufferUsageFlags::TRANSFER_DST;
usage |= UsageFlags::DOWNLOAD;
}
if transient {
usage |= UsageFlags::TRANSIENT;
}
let buffer = {
let create_info = BufferCreateInfo::builder().size(size).usage(vk_usage);
let buffer = unsafe { self.device.device.create_buffer(&create_info, None) };
buffer.map_err(VulkanError::CreateBuffer)?
};
let destroy_buffer = OnDrop(|| unsafe { self.device.device.destroy_buffer(buffer, None) });
let memory_requirements =
unsafe { self.device.device.get_buffer_memory_requirements(buffer) };
let allocation = self.allocator.alloc(&memory_requirements, usage, true)?;
{
let res = unsafe {
self.device
.device
.bind_buffer_memory(buffer, allocation.memory, allocation.offset)
};
res.map_err(VulkanError::BindBufferMemory)?;
}
destroy_buffer.forget();
Ok(VulkanStagingBuffer {
device: self.device.clone(),
allocation,
buffer,
size,
})
}
}
impl VulkanStagingBuffer {
pub fn upload<T, F>(&self, f: F) -> Result<T, VulkanError>
where
F: FnOnce(*mut u8, usize) -> T,
{
let t = f(self.allocation.mem.unwrap(), self.size as usize);
let range = self.range();
let res = unsafe { self.device.device.flush_mapped_memory_ranges(&[range]) };
res.map_err(VulkanError::FlushMemory).map(|_| t)
}
pub fn download<T, F>(&self, f: F) -> Result<T, VulkanError>
where
F: FnOnce(*const u8, usize) -> T,
{
let range = self.range();
let res = unsafe { self.device.device.invalidate_mapped_memory_ranges(&[range]) };
res.map_err(VulkanError::FlushMemory)?;
Ok(f(self.allocation.mem.unwrap(), self.size as usize))
}
fn range(&self) -> MappedMemoryRange {
let atom_mask = self.allocation.allocator.non_coherent_atom_mask;
MappedMemoryRange::builder()
.memory(self.allocation.memory)
.offset(self.allocation.offset & !atom_mask)
.size((self.allocation.size + atom_mask) & !atom_mask)
.build()
}
}
impl Drop for VulkanStagingBuffer {
fn drop(&mut self) {
unsafe {
self.device.device.destroy_buffer(self.buffer, None);
}
}
}

View file

@ -0,0 +1,17 @@
use std::mem;
pub struct OnDrop<F>(pub F)
where
F: FnMut() + Copy;
impl<F: FnMut() + Copy> OnDrop<F> {
pub fn forget(self) {
mem::forget(self);
}
}
impl<F: FnMut() + Copy> Drop for OnDrop<F> {
fn drop(&mut self) {
(self.0)();
}
}

View file

@ -165,9 +165,7 @@ impl JayScreencast {
let mut buffer = self.buffers.borrow_mut();
for (idx, buffer) in buffer.deref_mut().iter_mut().enumerate() {
if buffer.free {
buffer
.fb
.copy_texture(&self.client.state, texture, 0, 0, false);
buffer.fb.copy_texture(texture, 0, 0);
self.client.event(Ready {
self_id: self.id,
idx: idx as _,

View file

@ -198,10 +198,10 @@ impl WlBuffer {
};
match storage {
WlBufferStorage::Shm { mem, stride } => {
self.texture.set(None);
let old = self.texture.take();
if let Some(ctx) = self.client.state.render_ctx.get() {
let tex = mem.access(|mem| {
ctx.shmem_texture(mem, self.format, self.width, self.height, *stride)
ctx.shmem_texture(old, mem, self.format, self.width, self.height, *stride)
})??;
self.texture.set(Some(tex));
}

View file

@ -19,6 +19,7 @@ use {
buffd::{MsgParser, MsgParserError},
clonecell::CloneCell,
copyhashmap::CopyHashMap,
errorfmt::ErrorFmt,
linkedlist::LinkedList,
},
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
@ -220,8 +221,10 @@ impl WlOutputGlobal {
continue;
}
let rect = capture.rect;
if let Some(WlBufferStorage::Shm { mem, .. }) = wl_buffer.storage.borrow_mut().deref() {
let res = mem.access(|mem| {
if let Some(WlBufferStorage::Shm { mem, stride }) =
wl_buffer.storage.borrow_mut().deref()
{
let acc = mem.access(|mem| {
fb.copy_to_shm(
rect.x1(),
rect.y1(),
@ -229,10 +232,39 @@ impl WlOutputGlobal {
rect.height(),
XRGB8888,
mem,
);
)
});
let mut res = match acc {
Ok(res) => res,
Err(e) => {
capture.client.error(e);
continue;
}
};
if res.is_err() {
let acc = mem.access(|mem| {
tex.clone().read_pixels(
capture.rect.x1(),
capture.rect.y1(),
capture.rect.width(),
capture.rect.height(),
*stride,
wl_buffer.format,
mem,
)
});
res = match acc {
Ok(res) => res,
Err(e) => {
capture.client.error(e);
continue;
}
};
}
if let Err(e) = res {
capture.client.error(e);
log::warn!("Could not read texture to memory: {}", ErrorFmt(e));
capture.send_failed();
continue;
}
// capture.send_flags(FLAGS_Y_INVERT);
} else {
@ -244,13 +276,7 @@ impl WlOutputGlobal {
continue;
}
};
fb.copy_texture(
&self.state,
tex,
-capture.rect.x1(),
-capture.rect.y1(),
false,
);
fb.copy_texture(tex, -capture.rect.x1(), -capture.rect.y1());
}
if capture.with_damage.get() {
capture.send_damage();

View file

@ -10,7 +10,6 @@ use {
compositor::TestFuture,
fixed::Fixed,
gfx_api::GfxError,
gfx_apis::create_gfx_context,
it::test_error::TestResult,
state::State,
time::now_usec,
@ -178,7 +177,7 @@ impl TestBackend {
}
};
let drm = Drm::open_existing(file);
let ctx = match create_gfx_context(&drm) {
let ctx = match self.state.create_gfx_context(&drm, None) {
Ok(ctx) => ctx,
Err(e) => return Err(TestBackendError::RenderContext(e)),
};

View file

@ -2,6 +2,7 @@
c_variadic, // https://github.com/rust-lang/rust/issues/44930
thread_local, // https://github.com/rust-lang/rust/issues/29594
extern_types, // https://github.com/rust-lang/rust/issues/43467
c_str_literals, // https://github.com/rust-lang/rust/issues/105723
)]
#![allow(
clippy::len_zero,

View file

@ -35,6 +35,7 @@ use {
},
},
ahash::AHashMap,
jay_config::video::GfxApi,
std::{
cell::{Cell, RefCell},
ops::Deref,
@ -169,16 +170,17 @@ impl UsrJayRenderCtxOwner for PortalDisplay {
}
if self.render_ctx.get().is_none() {
let drm = Drm::open_existing(fd);
let ctx = match create_gfx_context(&drm) {
Ok(c) => c,
Err(e) => {
log::error!(
"Could not create render context from drm device: {}",
ErrorFmt(e)
);
return;
}
};
let ctx =
match create_gfx_context(&self.state.eng, &self.state.ring, &drm, GfxApi::OpenGl) {
Ok(c) => c,
Err(e) => {
log::error!(
"Could not create render context from drm device: {}",
ErrorFmt(e)
);
return;
}
};
let ctx = Rc::new(PortalRenderCtx { dev_id, ctx });
self.render_ctx.set(Some(ctx.clone()));
self.state.render_ctxs.set(dev_id, Rc::downgrade(&ctx));

View file

@ -218,7 +218,6 @@ impl GuiElement for Button {
&tex.texture,
tx.round() as _,
ty.round() as _,
ARGB8888,
None,
None,
r.scale(),
@ -321,7 +320,6 @@ impl GuiElement for Label {
&tex.texture,
tx.round() as _,
ty.round() as _,
ARGB8888,
None,
None,
r.scale(),

View file

@ -1,6 +1,5 @@
use {
crate::{
format::ARGB8888,
gfx_api::{BufferPoints, GfxApiOpt},
ifs::{
wl_buffer::WlBuffer,
@ -140,17 +139,8 @@ impl Renderer<'_> {
let scale = output.preferred_scale.get();
for title in &rd.titles {
let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
self.base.render_texture(
&title.tex,
x,
y,
ARGB8888,
None,
None,
scale,
i32::MAX,
i32::MAX,
);
self.base
.render_texture(&title.tex, x, y, None, None, scale, i32::MAX, i32::MAX);
}
if let Some(status) = &rd.status {
let (x, y) = self.base.scale_point(x + status.tex_x, y + status.tex_y);
@ -158,7 +148,6 @@ impl Renderer<'_> {
&status.tex.texture,
x,
y,
ARGB8888,
None,
None,
scale,
@ -204,7 +193,6 @@ impl Renderer<'_> {
&tex.texture,
x,
y,
ARGB8888,
None,
None,
self.base.scale,
@ -241,7 +229,6 @@ impl Renderer<'_> {
&title.tex.texture,
x,
y,
ARGB8888,
None,
None,
self.base.scale,
@ -394,7 +381,6 @@ impl Renderer<'_> {
&tex,
x,
y,
buffer.format,
Some(tpoints),
Some(tsize),
self.base.scale,
@ -407,6 +393,8 @@ impl Renderer<'_> {
{
self.base.fill_boxes(&[rect], color);
}
} else {
log::info!("live buffer has neither a texture nor is a single-pixel buffer");
}
}
@ -443,7 +431,6 @@ impl Renderer<'_> {
&title.texture,
x,
y,
ARGB8888,
None,
None,
self.base.scale,

View file

@ -1,6 +1,5 @@
use {
crate::{
format::Format,
gfx_api::{
AbsoluteRect, BufferPoint, BufferPoints, CopyTexture, FillRect, GfxApiOpt, GfxTexture,
},
@ -118,7 +117,6 @@ impl RendererBase<'_> {
texture: &Rc<dyn GfxTexture>,
x: i32,
y: i32,
format: &'static Format,
tpoints: Option<BufferPoints>,
tsize: Option<(i32, i32)>,
tscale: Scale,
@ -181,7 +179,6 @@ impl RendererBase<'_> {
self.ops.push(GfxApiOpt::CopyTexture(CopyTexture {
tex: texture.clone(),
format,
source: texcoord,
target: AbsoluteRect {
x1: x,

View file

@ -14,7 +14,8 @@ use {
dbus::Dbus,
drm_feedback::DrmFeedback,
forker::ForkerProxy,
gfx_api::GfxContext,
gfx_api::{GfxContext, GfxError},
gfx_apis::create_gfx_context,
globals::{Globals, GlobalsError, WaylandGlobal},
ifs::{
ext_session_lock_v1::ExtSessionLockV1,
@ -45,6 +46,7 @@ use {
errorfmt::ErrorFmt, fdcloser::FdCloser, linkedlist::LinkedList, numcell::NumCell,
queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
},
video::drm::Drm,
wheel::Wheel,
wire::{
JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId, ZwpLinuxDmabufFeedbackV1Id,
@ -54,7 +56,7 @@ use {
},
ahash::AHashMap,
bstr::ByteSlice,
jay_config::PciId,
jay_config::{video::GfxApi, PciId},
std::{
cell::{Cell, RefCell},
fmt::{Debug, Formatter},
@ -131,6 +133,7 @@ pub struct State {
pub render_ctx_watchers: CopyHashMap<(ClientId, JayRenderCtxId), Rc<JayRenderCtx>>,
pub workspace_watchers: CopyHashMap<(ClientId, JayWorkspaceWatcherId), Rc<JayWorkspaceWatcher>>,
pub default_workspace_capture: Cell<bool>,
pub default_gfx_api: Cell<GfxApi>,
}
// impl Drop for State {
@ -258,6 +261,19 @@ impl NodeVisitorBase for UpdateTextTexturesVisitor {
}
impl State {
pub fn create_gfx_context(
&self,
drm: &Drm,
api: Option<GfxApi>,
) -> Result<Rc<dyn GfxContext>, GfxError> {
create_gfx_context(
&self.eng,
&self.ring,
drm,
api.unwrap_or(self.default_gfx_api.get()),
)
}
pub fn add_output_scale(&self, scale: Scale) {
if self.scales.add(scale) {
self.output_scales_changed();

View file

@ -103,11 +103,13 @@ impl DrvDevHandler {
if let Some(config) = self.state.config.get() {
config.new_drm_dev(self.id);
}
self.log_gfx_api();
'outer: loop {
#[allow(clippy::never_loop)]
while let Some(event) = self.data.dev.event() {
match event {
DrmEvent::Removed => break 'outer,
DrmEvent::GfxApiChanged => self.log_gfx_api(),
}
}
ae.triggered().await;
@ -121,4 +123,13 @@ impl DrvDevHandler {
self.data.handler.set(None);
self.state.drm_devs.remove(&self.id);
}
fn log_gfx_api(&self) {
let api = self.data.dev.gtx_api();
log::info!(
"Using {:?} for device {}",
api,
self.data.devnode.as_deref().unwrap_or(""),
)
}
}

View file

@ -210,9 +210,10 @@ fn render2(
Ok(d) => d,
Err(e) => return Err(TextError::ImageData(e)),
};
let old = old.map(|o| o.texture);
match ctx
.clone()
.shmem_texture(bytes, ARGB8888, width, height, data.image.stride())
.shmem_texture(old, bytes, ARGB8888, width, height, data.image.stride())
{
Ok(t) => Ok(TextTexture {
config: Rc::new(config.to_static()),

View file

@ -84,6 +84,27 @@ impl Color {
pub fn to_rgba_premultiplied(self) -> [u8; 4] {
[to_u8(self.r), to_u8(self.g), to_u8(self.b), to_u8(self.a)]
}
#[allow(dead_code)]
pub fn to_array_srgb(self) -> [f32; 4] {
[self.r, self.g, self.b, self.a]
}
pub fn to_array_linear(self) -> [f32; 4] {
fn to_linear(srgb: f32) -> f32 {
if srgb <= 0.04045 {
srgb / 12.92
} else {
(srgb + 0.055 / 1.055).powf(2.4)
}
}
[
to_linear(self.r),
to_linear(self.g),
to_linear(self.b),
self.a,
]
}
}
impl From<jay_config::theme::Color> for Color {

View file

@ -59,7 +59,7 @@ impl<T> CloneCell<T> {
}
}
impl<T: Default + UnsafeCellCloneSafe> Default for CloneCell<T> {
impl<T: Default> Default for CloneCell<T> {
fn default() -> Self {
Self::new(Default::default())
}
@ -77,6 +77,8 @@ unsafe impl<T> UnsafeCellCloneSafe for NodeRef<T> {}
unsafe impl UnsafeCellCloneSafe for () {}
unsafe impl UnsafeCellCloneSafe for u64 {}
unsafe impl UnsafeCellCloneSafe for i32 {}
unsafe impl UnsafeCellCloneSafe for u32 {}
unsafe impl UnsafeCellCloneSafe for usize {}
unsafe impl<A: UnsafeCellCloneSafe, B: UnsafeCellCloneSafe> UnsafeCellCloneSafe for (A, B) {}

View file

@ -1,5 +1,8 @@
use {
crate::utils::ptr_ext::{MutPtrExt, PtrExt},
crate::utils::{
clonecell::UnsafeCellCloneSafe,
ptr_ext::{MutPtrExt, PtrExt},
},
std::{cell::UnsafeCell, mem},
};
@ -28,7 +31,7 @@ impl<T> Stack<T> {
pub fn to_vec(&self) -> Vec<T>
where
T: Clone,
T: UnsafeCellCloneSafe,
{
unsafe {
let v = self.vec.get().deref();

View file

@ -1,8 +1,8 @@
use {
crate::{format::Format, video::Modifier},
crate::{format::Format, utils::oserror::OsError, video::Modifier},
arrayvec::ArrayVec,
std::rc::Rc,
uapi::OwnedFd,
uapi::{c::ioctl, OwnedFd, _IOW, _IOWR},
};
#[derive(Clone)]
@ -24,3 +24,74 @@ pub struct DmaBuf {
pub const MAX_PLANES: usize = 4;
pub type PlaneVec<T> = ArrayVec<T, MAX_PLANES>;
impl DmaBuf {
pub fn is_disjoint(&self) -> bool {
if self.planes.len() <= 1 {
return false;
}
let stat = match uapi::fstat(self.planes[0].fd.raw()) {
Ok(s) => s,
_ => return true,
};
for plane in &self.planes[1..] {
let stat2 = match uapi::fstat(plane.fd.raw()) {
Ok(s) => s,
_ => return true,
};
if stat2.st_ino != stat.st_ino {
return true;
}
}
false
}
}
const DMA_BUF_BASE: u64 = b'b' as _;
#[allow(non_camel_case_types)]
#[repr(C)]
struct dma_buf_export_sync_file {
flags: u32,
fd: i32,
}
#[allow(non_camel_case_types)]
#[repr(C)]
struct dma_buf_import_sync_file {
flags: u32,
fd: i32,
}
pub const DMA_BUF_SYNC_READ: u32 = 1 << 0;
pub const DMA_BUF_SYNC_WRITE: u32 = 1 << 1;
const DMA_BUF_IOCTL_EXPORT_SYNC_FILE: u64 = _IOWR::<dma_buf_export_sync_file>(DMA_BUF_BASE, 2);
const DMA_BUF_IOCTL_IMPORT_SYNC_FILE: u64 = _IOW::<dma_buf_import_sync_file>(DMA_BUF_BASE, 3);
pub fn dma_buf_export_sync_file(dmabuf: &OwnedFd, flags: u32) -> Result<OwnedFd, OsError> {
let mut data = dma_buf_export_sync_file { flags, fd: -1 };
let res = unsafe { ioctl(dmabuf.raw(), DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &mut data) };
if res != 0 {
Err(OsError::default())
} else {
Ok(OwnedFd::new(data.fd))
}
}
pub fn dma_buf_import_sync_file(
dmabuf: &OwnedFd,
flags: u32,
sync_file: &OwnedFd,
) -> Result<(), OsError> {
let mut data = dma_buf_import_sync_file {
flags,
fd: sync_file.raw(),
};
let res = unsafe { ioctl(dmabuf.raw(), DMA_BUF_IOCTL_IMPORT_SYNC_FILE, &mut data) };
if res != 0 {
Err(OsError::default())
} else {
Ok(())
}
}

View file

@ -175,6 +175,14 @@ impl Drm {
get_nodes(self.fd.raw()).map_err(DrmError::GetNodes)
}
pub fn get_render_node(&self) -> Result<Option<CString>, DrmError> {
let nodes = self.get_nodes()?;
Ok(nodes
.get(&NodeType::Render)
.or_else(|| nodes.get(&NodeType::Primary))
.map(|c| c.to_owned()))
}
pub fn version(&self) -> Result<DrmVersion, DrmError> {
get_version(self.fd.raw()).map_err(DrmError::Version)
}