Merge pull request #71 from mahkoh/jorth/vulkan
Implement a vulkan renderer
This commit is contained in:
commit
d725a1eac2
88 changed files with 5342 additions and 904 deletions
|
|
@ -4,7 +4,7 @@ sources:
|
||||||
tasks:
|
tasks:
|
||||||
- install: |
|
- install: |
|
||||||
sudo pacman -Syu --noconfirm
|
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
|
rustup toolchain install stable
|
||||||
- build: |
|
- build: |
|
||||||
cd jay
|
cd jay
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ sources:
|
||||||
tasks:
|
tasks:
|
||||||
- install: |
|
- install: |
|
||||||
sudo pacman -Syu --noconfirm
|
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
|
rustup toolchain install stable
|
||||||
- configure: |
|
- configure: |
|
||||||
sudo rmmod bochs
|
sudo rmmod bochs
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ sources:
|
||||||
tasks:
|
tasks:
|
||||||
- install: |
|
- install: |
|
||||||
sudo pacman -Syu --noconfirm
|
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
|
rustup toolchain install stable
|
||||||
- test: |
|
- test: |
|
||||||
cd jay
|
cd jay
|
||||||
|
|
|
||||||
540
Cargo.lock
generated
540
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
44
Cargo.toml
44
Cargo.toml
|
|
@ -15,37 +15,43 @@ panic = "abort"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
uapi = "0.2.10"
|
uapi = "0.2.10"
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.56"
|
||||||
ahash = "0.8.2"
|
ahash = "0.8.7"
|
||||||
log = { version = "0.4.16", features = ["std"] }
|
log = { version = "0.4.20", features = ["std"] }
|
||||||
futures-util = "0.3.19"
|
futures-util = "0.3.30"
|
||||||
num-traits = "0.2.14"
|
num-traits = "0.2.17"
|
||||||
num-derive = "0.4.1"
|
num-derive = "0.4.1"
|
||||||
bitflags = "2.4.1"
|
bitflags = "2.4.2"
|
||||||
libloading = "0.8.1"
|
libloading = "0.8.1"
|
||||||
bstr = { version = "1.1.0", default-features = false, features = ["std"] }
|
bstr = { version = "1.9.0", default-features = false, features = ["std"] }
|
||||||
isnt = "0.1.0"
|
isnt = "0.1.0"
|
||||||
once_cell = "1.9.0"
|
once_cell = "1.19.0"
|
||||||
rand = "0.8.4"
|
rand = "0.8.5"
|
||||||
smallvec = { version = "1.8.0", features = ["const_generics", "const_new", "union"] }
|
smallvec = { version = "1.11.1", features = ["const_generics", "const_new", "union"] }
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.5.0"
|
||||||
bincode = "2.0.0-rc.1"
|
bincode = "2.0.0-rc.3"
|
||||||
jay-config = { path = "jay-config" }
|
jay-config = { path = "jay-config" }
|
||||||
default-config = { path = "default-config" }
|
default-config = { path = "default-config" }
|
||||||
algorithms = { path = "algorithms" }
|
algorithms = { path = "algorithms" }
|
||||||
pin-project = "1.0.10"
|
pin-project = "1.1.4"
|
||||||
clap = { version = "4.0.29", features = ["derive", "wrap_help"] }
|
clap = { version = "4.4.18", features = ["derive", "wrap_help"] }
|
||||||
clap_complete = "4.0.6"
|
clap_complete = "4.4.10"
|
||||||
humantime = "2.1.0"
|
humantime = "2.1.0"
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
backtrace = "0.3.64"
|
backtrace = "0.3.69"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.33"
|
||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
|
arrayvec = "0.7.4"
|
||||||
|
indexmap = "2.2.0"
|
||||||
|
ash = "0.37.3"
|
||||||
|
gpu-alloc = "0.6.0"
|
||||||
|
gpu-alloc-ash = "0.6.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
repc = "0.1.1"
|
repc = "0.1.1"
|
||||||
anyhow = "1.0.52"
|
anyhow = "1.0.79"
|
||||||
bstr = { version = "1.1.0", default-features = false, features = ["std"] }
|
bstr = { version = "1.9.0", default-features = false, features = ["std"] }
|
||||||
|
shaderc = "0.8.3"
|
||||||
|
|
||||||
#[profile.dev.build-override]
|
#[profile.dev.build-override]
|
||||||
#opt-level = 3
|
#opt-level = 3
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ use std::{
|
||||||
mod egl;
|
mod egl;
|
||||||
mod enums;
|
mod enums;
|
||||||
mod tokens;
|
mod tokens;
|
||||||
|
mod vulkan;
|
||||||
mod wire;
|
mod wire;
|
||||||
mod wire_dbus;
|
mod wire_dbus;
|
||||||
mod wire_xcon;
|
mod wire_xcon;
|
||||||
|
|
@ -47,6 +48,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
wire_xcon::main()?;
|
wire_xcon::main()?;
|
||||||
enums::main()?;
|
enums::main()?;
|
||||||
egl::main()?;
|
egl::main()?;
|
||||||
|
vulkan::main()?;
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build/build.rs");
|
println!("cargo:rerun-if-changed=build/build.rs");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
38
build/vulkan.rs
Normal file
38
build/vulkan.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ use {
|
||||||
timer::Timer,
|
timer::Timer,
|
||||||
video::{
|
video::{
|
||||||
connector_type::{ConnectorType, CON_UNKNOWN},
|
connector_type::{ConnectorType, CON_UNKNOWN},
|
||||||
Connector, DrmDevice, Mode,
|
Connector, DrmDevice, GfxApi, Mode,
|
||||||
},
|
},
|
||||||
Axis, Direction, ModifiedKeySym, PciId, Workspace,
|
Axis, Direction, ModifiedKeySym, PciId, Workspace,
|
||||||
},
|
},
|
||||||
|
|
@ -506,6 +506,10 @@ impl Client {
|
||||||
self.send(&ClientMessage::MakeRenderDevice { device });
|
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 {
|
pub fn connector_connected(&self, connector: Connector) -> bool {
|
||||||
let res = self.send_with_response(&ClientMessage::ConnectorConnected { connector });
|
let res = self.send_with_response(&ClientMessage::ConnectorConnected { connector });
|
||||||
get_response!(res, false, ConnectorConnected { connected });
|
get_response!(res, false, ConnectorConnected { connected });
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use {
|
||||||
logging::LogLevel,
|
logging::LogLevel,
|
||||||
theme::{colors::Colorable, sized::Resizable, Color},
|
theme::{colors::Colorable, sized::Resizable, Color},
|
||||||
timer::Timer,
|
timer::Timer,
|
||||||
video::{connector_type::ConnectorType, Connector, DrmDevice},
|
video::{connector_type::ConnectorType, Connector, DrmDevice, GfxApi},
|
||||||
Axis, Direction, PciId, Workspace,
|
Axis, Direction, PciId, Workspace,
|
||||||
},
|
},
|
||||||
bincode::{BorrowDecode, Decode, Encode},
|
bincode::{BorrowDecode, Decode, Encode},
|
||||||
|
|
@ -334,6 +334,10 @@ pub enum ClientMessage<'a> {
|
||||||
device: InputDevice,
|
device: InputDevice,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
},
|
},
|
||||||
|
SetGfxApi {
|
||||||
|
device: Option<DrmDevice>,
|
||||||
|
api: GfxApi,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Encode, Decode, Debug)]
|
#[derive(Encode, Decode, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -362,4 +362,30 @@ impl DrmDevice {
|
||||||
pub fn make_render_device(self) {
|
pub fn make_render_device(self) {
|
||||||
get!().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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use {
|
||||||
ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
|
ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL},
|
||||||
video::drm::{ConnectorType, DrmError, DrmVersion},
|
video::drm::{ConnectorType, DrmError, DrmVersion},
|
||||||
},
|
},
|
||||||
|
jay_config::video::GfxApi,
|
||||||
std::{
|
std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
error::Error,
|
error::Error,
|
||||||
|
|
@ -217,6 +218,7 @@ pub enum InputEvent {
|
||||||
pub enum DrmEvent {
|
pub enum DrmEvent {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
Removed,
|
Removed,
|
||||||
|
GfxApiChanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BackendDrmDevice {
|
pub trait BackendDrmDevice {
|
||||||
|
|
@ -224,6 +226,8 @@ pub trait BackendDrmDevice {
|
||||||
fn event(&self) -> Option<DrmEvent>;
|
fn event(&self) -> Option<DrmEvent>;
|
||||||
fn on_change(&self, cb: Rc<dyn Fn()>);
|
fn on_change(&self, cb: Rc<dyn Fn()>);
|
||||||
fn dev_t(&self) -> c::dev_t;
|
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>;
|
fn version(&self) -> Result<DrmVersion, DrmError>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,14 @@ pub enum MetalError {
|
||||||
DevicePauseSignalHandler(#[source] DbusError),
|
DevicePauseSignalHandler(#[source] DbusError),
|
||||||
#[error("Could not create device-resumed signal handler")]
|
#[error("Could not create device-resumed signal handler")]
|
||||||
DeviceResumeSignalHandler(#[source] DbusError),
|
DeviceResumeSignalHandler(#[source] DbusError),
|
||||||
|
#[error("Device render context does not support required format {0}")]
|
||||||
|
MissingDevFormat(&'static str),
|
||||||
|
#[error("Render context does not support required format {0}")]
|
||||||
|
MissingRenderFormat(&'static str),
|
||||||
|
#[error("Device cannot scan out any buffers writable by its GFX API (format {0})")]
|
||||||
|
MissingDevModifier(&'static str),
|
||||||
|
#[error("Device GFX API cannot read any buffers writable by the render GFX API (format {0})")]
|
||||||
|
MissingRenderModifier(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MetalBackend {
|
pub struct MetalBackend {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ use {
|
||||||
edid::Descriptor,
|
edid::Descriptor,
|
||||||
format::{Format, ARGB8888, XRGB8888},
|
format::{Format, ARGB8888, XRGB8888},
|
||||||
gfx_api::{GfxContext, GfxFramebuffer, GfxTexture},
|
gfx_api::{GfxContext, GfxFramebuffer, GfxTexture},
|
||||||
gfx_apis::create_gfx_context,
|
|
||||||
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC},
|
ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC},
|
||||||
renderer::RenderResult,
|
renderer::RenderResult,
|
||||||
state::State,
|
state::State,
|
||||||
|
|
@ -28,11 +27,13 @@ use {
|
||||||
DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT,
|
DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT,
|
||||||
},
|
},
|
||||||
gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT},
|
gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING, GBM_BO_USE_SCANOUT},
|
||||||
ModifiedFormat, INVALID_MODIFIER,
|
Modifier, INVALID_MODIFIER,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ahash::{AHashMap, AHashSet},
|
ahash::{AHashMap, AHashSet},
|
||||||
bstr::{BString, ByteSlice},
|
bstr::{BString, ByteSlice},
|
||||||
|
indexmap::{indexset, IndexSet},
|
||||||
|
jay_config::video::GfxApi,
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
ffi::CString,
|
ffi::CString,
|
||||||
|
|
@ -74,7 +75,8 @@ pub struct MetalDrmDevice {
|
||||||
pub cursor_height: u64,
|
pub cursor_height: u64,
|
||||||
pub gbm: GbmDevice,
|
pub gbm: GbmDevice,
|
||||||
pub handle_events: HandleEvents,
|
pub handle_events: HandleEvents,
|
||||||
pub ctx: Rc<MetalRenderContext>,
|
pub ctx: CloneCell<Rc<MetalRenderContext>>,
|
||||||
|
pub on_change: OnChange<crate::backend::DrmEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackendDrmDevice for MetalDrmDevice {
|
impl BackendDrmDevice for MetalDrmDevice {
|
||||||
|
|
@ -83,19 +85,27 @@ impl BackendDrmDevice for MetalDrmDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&self) -> Option<crate::backend::DrmEvent> {
|
fn event(&self) -> Option<crate::backend::DrmEvent> {
|
||||||
None
|
self.on_change.events.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_change(&self, _cb: Rc<dyn Fn()>) {
|
fn on_change(&self, cb: Rc<dyn Fn()>) {
|
||||||
// nothing
|
self.on_change.on_change.set(Some(cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dev_t(&self) -> dev_t {
|
fn dev_t(&self) -> dev_t {
|
||||||
self.devnum
|
self.devnum
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_render_device(self: Rc<Self>) {
|
fn make_render_device(&self) {
|
||||||
self.backend.make_render_device(&self, true);
|
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> {
|
fn version(&self) -> Result<DrmVersion, DrmError> {
|
||||||
|
|
@ -161,8 +171,6 @@ pub struct MetalConnector {
|
||||||
|
|
||||||
pub connector_id: ConnectorId,
|
pub connector_id: ConnectorId,
|
||||||
|
|
||||||
pub events: SyncQueue<ConnectorEvent>,
|
|
||||||
|
|
||||||
pub buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
|
pub buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
|
||||||
pub next_buffer: NumCell<usize>,
|
pub next_buffer: NumCell<usize>,
|
||||||
|
|
||||||
|
|
@ -181,7 +189,7 @@ pub struct MetalConnector {
|
||||||
|
|
||||||
pub crtc: CloneCell<Option<Rc<MetalCrtc>>>,
|
pub crtc: CloneCell<Option<Rc<MetalCrtc>>>,
|
||||||
|
|
||||||
pub on_change: OnChange,
|
pub on_change: OnChange<ConnectorEvent>,
|
||||||
|
|
||||||
pub present_trigger: AsyncEvent,
|
pub present_trigger: AsyncEvent,
|
||||||
|
|
||||||
|
|
@ -270,12 +278,30 @@ impl Debug for ConnectorFutures {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
pub struct OnChange<T> {
|
||||||
pub struct OnChange {
|
|
||||||
pub on_change: CloneCell<Option<Rc<dyn Fn()>>>,
|
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 {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self.on_change.get() {
|
match self.on_change.get() {
|
||||||
None => f.write_str("None"),
|
None => f.write_str("None"),
|
||||||
|
|
@ -310,7 +336,8 @@ impl MetalConnector {
|
||||||
}) as _),
|
}) as _),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
self.send_event(ConnectorEvent::HardwareCursor(hc));
|
self.on_change
|
||||||
|
.send_event(ConnectorEvent::HardwareCursor(hc));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connected(&self) -> bool {
|
fn connected(&self) -> bool {
|
||||||
|
|
@ -320,13 +347,6 @@ impl MetalConnector {
|
||||||
&& self.primary_plane.get().is_some()
|
&& 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) {
|
pub fn schedule_present(&self) {
|
||||||
self.present_trigger.trigger();
|
self.present_trigger.trigger();
|
||||||
}
|
}
|
||||||
|
|
@ -360,17 +380,16 @@ impl MetalConnector {
|
||||||
if let Some(node) = self.state.root.outputs.get(&self.connector_id) {
|
if let Some(node) = self.state.root.outputs.get(&self.connector_id) {
|
||||||
let mut rr = self.render_result.borrow_mut();
|
let mut rr = self.render_result.borrow_mut();
|
||||||
let render_fb = buffer.render_fb();
|
let render_fb = buffer.render_fb();
|
||||||
render_fb.render(
|
render_fb.render_node(
|
||||||
&*node,
|
&*node,
|
||||||
&self.state,
|
&self.state,
|
||||||
Some(node.global.pos.get()),
|
Some(node.global.pos.get()),
|
||||||
true,
|
Some(&mut rr),
|
||||||
&mut rr,
|
|
||||||
node.preferred_scale.get(),
|
node.preferred_scale.get(),
|
||||||
!self.cursor_enabled.get(),
|
!self.cursor_enabled.get(),
|
||||||
);
|
);
|
||||||
if let Some(tex) = &buffer.dev_tex {
|
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(..) {
|
for fr in rr.frame_requests.drain(..) {
|
||||||
fr.send_done();
|
fr.send_done();
|
||||||
|
|
@ -393,20 +412,21 @@ impl MetalConnector {
|
||||||
let buffer = &buffers[self.cursor_front_buffer.get() % buffers.len()];
|
let buffer = &buffers[self.cursor_front_buffer.get() % buffers.len()];
|
||||||
if swap_buffer {
|
if swap_buffer {
|
||||||
if let Some(tex) = &buffer.dev_tex {
|
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();
|
||||||
changes.change_object(plane.id, |c| {
|
changes.change_object(plane.id, |c| {
|
||||||
c.change(plane.fb_id, buffer.drm.id().0 as _);
|
c.change(plane.fb_id, buffer.drm.id().0 as _);
|
||||||
c.change(plane.crtc_id.id, crtc.id.0 as _);
|
c.change(plane.crtc_id.id, crtc.id.0 as _);
|
||||||
c.change(plane.crtc_x.id, self.cursor_x.get() as _);
|
c.change(plane.crtc_x.id, self.cursor_x.get() as _);
|
||||||
c.change(plane.crtc_y.id, self.cursor_y.get() as _);
|
c.change(plane.crtc_y.id, self.cursor_y.get() as _);
|
||||||
c.change(plane.crtc_w.id, buffer.render_tex.width() as _);
|
c.change(plane.crtc_w.id, width as _);
|
||||||
c.change(plane.crtc_h.id, buffer.render_tex.height() as _);
|
c.change(plane.crtc_h.id, height as _);
|
||||||
c.change(plane.src_x.id, 0);
|
c.change(plane.src_x.id, 0);
|
||||||
c.change(plane.src_y.id, 0);
|
c.change(plane.src_y.id, 0);
|
||||||
c.change(plane.src_w.id, (buffer.render_tex.width() as u64) << 16);
|
c.change(plane.src_w.id, (width as u64) << 16);
|
||||||
c.change(plane.src_h.id, (buffer.render_tex.height() as u64) << 16);
|
c.change(plane.src_h.id, (height as u64) << 16);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
changes.change_object(plane.id, |c| {
|
changes.change_object(plane.id, |c| {
|
||||||
|
|
@ -444,7 +464,7 @@ impl Connector for MetalConnector {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&self) -> Option<ConnectorEvent> {
|
fn event(&self) -> Option<ConnectorEvent> {
|
||||||
self.events.pop()
|
self.on_change.events.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_change(&self, cb: Rc<dyn Fn()>) {
|
fn on_change(&self, cb: Rc<dyn Fn()>) {
|
||||||
|
|
@ -506,6 +526,12 @@ pub enum PlaneType {
|
||||||
Cursor,
|
Cursor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PlaneFormat {
|
||||||
|
_format: &'static Format,
|
||||||
|
modifiers: IndexSet<Modifier>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MetalPlane {
|
pub struct MetalPlane {
|
||||||
pub id: DrmPlane,
|
pub id: DrmPlane,
|
||||||
|
|
@ -514,7 +540,7 @@ pub struct MetalPlane {
|
||||||
pub ty: PlaneType,
|
pub ty: PlaneType,
|
||||||
|
|
||||||
pub possible_crtcs: u32,
|
pub possible_crtcs: u32,
|
||||||
pub formats: AHashMap<u32, &'static Format>,
|
pub formats: AHashMap<u32, PlaneFormat>,
|
||||||
|
|
||||||
pub assigned: Cell<bool>,
|
pub assigned: Cell<bool>,
|
||||||
|
|
||||||
|
|
@ -570,7 +596,6 @@ fn create_connector(
|
||||||
dev: dev.clone(),
|
dev: dev.clone(),
|
||||||
backend: backend.clone(),
|
backend: backend.clone(),
|
||||||
connector_id: backend.state.connector_ids.next(),
|
connector_id: backend.state.connector_ids.next(),
|
||||||
events: Default::default(),
|
|
||||||
buffers: Default::default(),
|
buffers: Default::default(),
|
||||||
next_buffer: Default::default(),
|
next_buffer: Default::default(),
|
||||||
enabled: Cell::new(true),
|
enabled: Cell::new(true),
|
||||||
|
|
@ -756,19 +781,36 @@ fn create_crtc(
|
||||||
|
|
||||||
fn create_plane(plane: DrmPlane, master: &Rc<DrmMaster>) -> Result<MetalPlane, DrmError> {
|
fn create_plane(plane: DrmPlane, master: &Rc<DrmMaster>) -> Result<MetalPlane, DrmError> {
|
||||||
let info = master.get_plane_info(plane)?;
|
let info = master.get_plane_info(plane)?;
|
||||||
|
let props = collect_properties(master, plane)?;
|
||||||
let mut formats = AHashMap::new();
|
let mut formats = AHashMap::new();
|
||||||
for format in info.format_types {
|
if let Some((_, v)) = props.props.get(b"IN_FORMATS".as_bstr()) {
|
||||||
if let Some(f) = crate::format::formats().get(&format) {
|
for format in master.get_in_formats(*v as _)? {
|
||||||
formats.insert(format, *f);
|
if format.modifiers.is_empty() {
|
||||||
} else {
|
continue;
|
||||||
// log::warn!(
|
}
|
||||||
// "{:?} supports unknown format '{:?}'",
|
if let Some(f) = crate::format::formats().get(&format.format) {
|
||||||
// plane,
|
formats.insert(
|
||||||
// crate::format::debug(format)
|
format.format,
|
||||||
// );
|
PlaneFormat {
|
||||||
|
_format: f,
|
||||||
|
modifiers: format.modifiers,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for format in info.format_types {
|
||||||
|
if let Some(f) = crate::format::formats().get(&format) {
|
||||||
|
formats.insert(
|
||||||
|
format,
|
||||||
|
PlaneFormat {
|
||||||
|
_format: f,
|
||||||
|
modifiers: indexset![INVALID_MODIFIER],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let props = collect_properties(master, plane)?;
|
|
||||||
let ty = match props.props.get(b"type".as_bstr()) {
|
let ty = match props.props.get(b"type".as_bstr()) {
|
||||||
Some((def, val)) => match &def.ty {
|
Some((def, val)) => match &def.ty {
|
||||||
DrmPropertyType::Enum { values, .. } => 'ty: {
|
DrmPropertyType::Enum { values, .. } => 'ty: {
|
||||||
|
|
@ -886,7 +928,7 @@ impl MetalBackend {
|
||||||
if let Some(r) = ctx
|
if let Some(r) = ctx
|
||||||
.gfx
|
.gfx
|
||||||
.reset_status()
|
.reset_status()
|
||||||
.or_else(|| dev.ctx.gfx.reset_status())
|
.or_else(|| dev.ctx.get().gfx.reset_status())
|
||||||
{
|
{
|
||||||
fatal!("EGL context has been reset: {:?}", r);
|
fatal!("EGL context has been reset: {:?}", r);
|
||||||
}
|
}
|
||||||
|
|
@ -964,9 +1006,9 @@ impl MetalBackend {
|
||||||
dev.futures.remove(&c);
|
dev.futures.remove(&c);
|
||||||
if let Some(c) = dev.connectors.remove(&c) {
|
if let Some(c) = dev.connectors.remove(&c) {
|
||||||
if c.connect_sent.get() {
|
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();
|
let mut preserve = Preserve::default();
|
||||||
|
|
@ -988,7 +1030,7 @@ impl MetalBackend {
|
||||||
|| old.connection != ConnectorStatus::Connected
|
|| old.connection != ConnectorStatus::Connected
|
||||||
|| !old.is_same_monitor(&dd)
|
|| !old.is_same_monitor(&dd)
|
||||||
{
|
{
|
||||||
c.send_event(ConnectorEvent::Disconnected);
|
c.on_change.send_event(ConnectorEvent::Disconnected);
|
||||||
c.connect_sent.set(false);
|
c.connect_sent.set(false);
|
||||||
} else if preserve_any {
|
} else if preserve_any {
|
||||||
preserve.connectors.insert(c.id);
|
preserve.connectors.insert(c.id);
|
||||||
|
|
@ -1030,15 +1072,17 @@ impl MetalBackend {
|
||||||
modes.push(mode);
|
modes.push(mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
connector.send_event(ConnectorEvent::Connected(MonitorInfo {
|
connector
|
||||||
modes,
|
.on_change
|
||||||
manufacturer: dd.monitor_manufacturer.clone(),
|
.send_event(ConnectorEvent::Connected(MonitorInfo {
|
||||||
product: dd.monitor_name.clone(),
|
modes,
|
||||||
serial_number: dd.monitor_serial_number.clone(),
|
manufacturer: dd.monitor_manufacturer.clone(),
|
||||||
initial_mode: dd.mode.clone().unwrap().to_backend(),
|
product: dd.monitor_name.clone(),
|
||||||
width_mm: dd.mm_width as _,
|
serial_number: dd.monitor_serial_number.clone(),
|
||||||
height_mm: dd.mm_height as _,
|
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.connect_sent.set(true);
|
||||||
connector.send_hardware_cursor();
|
connector.send_hardware_cursor();
|
||||||
}
|
}
|
||||||
|
|
@ -1091,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,
|
Ok(r) => r,
|
||||||
Err(e) => return Err(MetalError::CreateRenderContex(e)),
|
Err(e) => return Err(MetalError::CreateRenderContex(e)),
|
||||||
};
|
};
|
||||||
|
|
@ -1124,7 +1168,8 @@ impl MetalBackend {
|
||||||
handle_events: HandleEvents {
|
handle_events: HandleEvents {
|
||||||
handle_events: Cell::new(None),
|
handle_events: Cell::new(None),
|
||||||
},
|
},
|
||||||
ctx,
|
ctx: CloneCell::new(ctx),
|
||||||
|
on_change: Default::default(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let (connectors, futures) = get_connectors(self, &dev, &resources.connectors)?;
|
let (connectors, futures) = get_connectors(self, &dev, &resources.connectors)?;
|
||||||
|
|
@ -1416,28 +1461,68 @@ impl MetalBackend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_render_device(&self, dev: &Rc<MetalDrmDevice>, log: bool) -> bool {
|
fn make_render_device(&self, dev: &MetalDrmDevice, force: bool) {
|
||||||
if let Some(ctx) = self.ctx.get() {
|
if !force {
|
||||||
if ctx.dev_id == dev.id {
|
if let Some(ctx) = self.ctx.get() {
|
||||||
return true;
|
if ctx.dev_id == dev.id {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.state.set_render_ctx(Some(dev.ctx.gfx.clone()));
|
let ctx = dev.ctx.get();
|
||||||
self.ctx.set(Some(dev.ctx.clone()));
|
self.state.set_render_ctx(Some(ctx.gfx.clone()));
|
||||||
let mut preserve = Preserve::default();
|
self.ctx.set(Some(ctx));
|
||||||
for dev in self.device_holder.drm_devices.lock().values() {
|
for dev in self.device_holder.drm_devices.lock().values() {
|
||||||
if let Err(e) = self.init_drm_device(dev, &mut preserve) {
|
self.re_init_drm_device(&dev);
|
||||||
if log {
|
}
|
||||||
log::error!("Could not initialize device: {}", ErrorFmt(e));
|
}
|
||||||
}
|
|
||||||
|
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() {
|
dev.on_change
|
||||||
self.start_connector(connector, false);
|
.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(
|
fn init_drm_device(
|
||||||
|
|
@ -1570,30 +1655,52 @@ impl MetalBackend {
|
||||||
fn create_scanout_buffers(
|
fn create_scanout_buffers(
|
||||||
&self,
|
&self,
|
||||||
dev: &Rc<MetalDrmDevice>,
|
dev: &Rc<MetalDrmDevice>,
|
||||||
format: &ModifiedFormat,
|
format: &Format,
|
||||||
|
plane_modifiers: &IndexSet<Modifier>,
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
ctx: &MetalRenderContext,
|
ctx: &MetalRenderContext,
|
||||||
cursor: bool,
|
cursor: bool,
|
||||||
) -> Result<[RenderBuffer; 2], MetalError> {
|
) -> Result<[RenderBuffer; 2], MetalError> {
|
||||||
let create = || self.create_scanout_buffer(dev, format, width, height, ctx, cursor);
|
let create =
|
||||||
|
|| self.create_scanout_buffer(dev, format, plane_modifiers, width, height, ctx, cursor);
|
||||||
Ok([create()?, create()?])
|
Ok([create()?, create()?])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_scanout_buffer(
|
fn create_scanout_buffer(
|
||||||
&self,
|
&self,
|
||||||
dev: &Rc<MetalDrmDevice>,
|
dev: &Rc<MetalDrmDevice>,
|
||||||
format: &ModifiedFormat,
|
format: &Format,
|
||||||
|
plane_modifiers: &IndexSet<Modifier>,
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
render_ctx: &MetalRenderContext,
|
render_ctx: &MetalRenderContext,
|
||||||
cursor: bool,
|
cursor: bool,
|
||||||
) -> Result<RenderBuffer, MetalError> {
|
) -> Result<RenderBuffer, MetalError> {
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
let possible_modifiers: Vec<_> = dev_gfx_format
|
||||||
|
.write_modifiers
|
||||||
|
.iter()
|
||||||
|
.filter(|m| plane_modifiers.contains(*m))
|
||||||
|
.copied()
|
||||||
|
.collect();
|
||||||
|
if possible_modifiers.is_empty() {
|
||||||
|
log::warn!("Scanout modifiers: {:?}", plane_modifiers);
|
||||||
|
log::warn!("DEV GFX modifiers: {:?}", dev_gfx_format.write_modifiers);
|
||||||
|
return Err(MetalError::MissingDevModifier(format.name));
|
||||||
|
}
|
||||||
let mut usage = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
|
let mut usage = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
|
||||||
if cursor {
|
if cursor {
|
||||||
usage |= GBM_BO_USE_LINEAR;
|
usage |= GBM_BO_USE_LINEAR;
|
||||||
};
|
};
|
||||||
let dev_bo = dev.gbm.create_bo(width, height, format, usage);
|
let dev_bo = dev
|
||||||
|
.gbm
|
||||||
|
.create_bo(width, height, format, &possible_modifiers, usage);
|
||||||
let dev_bo = match dev_bo {
|
let dev_bo = match dev_bo {
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
Err(e) => return Err(MetalError::ScanoutBuffer(e)),
|
Err(e) => return Err(MetalError::ScanoutBuffer(e)),
|
||||||
|
|
@ -1602,7 +1709,7 @@ impl MetalBackend {
|
||||||
Ok(fb) => Rc::new(fb),
|
Ok(fb) => Rc::new(fb),
|
||||||
Err(e) => return Err(MetalError::Framebuffer(e)),
|
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,
|
Ok(img) => img,
|
||||||
Err(e) => return Err(MetalError::ImportImage(e)),
|
Err(e) => return Err(MetalError::ImportImage(e)),
|
||||||
};
|
};
|
||||||
|
|
@ -1619,8 +1726,31 @@ impl MetalBackend {
|
||||||
(None, render_tex, None)
|
(None, render_tex, None)
|
||||||
} else {
|
} else {
|
||||||
// Create a _bridge_ BO in the render device
|
// Create a _bridge_ BO in the render device
|
||||||
|
let render_gfx_formats = render_ctx.gfx.formats();
|
||||||
|
let render_gfx_format = match render_gfx_formats.get(&format.drm) {
|
||||||
|
None => return Err(MetalError::MissingRenderFormat(format.name)),
|
||||||
|
Some(f) => f,
|
||||||
|
};
|
||||||
|
let possible_modifiers: Vec<_> = render_gfx_format
|
||||||
|
.write_modifiers
|
||||||
|
.iter()
|
||||||
|
.filter(|m| dev_gfx_format.read_modifiers.contains(*m))
|
||||||
|
.copied()
|
||||||
|
.collect();
|
||||||
|
if possible_modifiers.is_empty() {
|
||||||
|
log::warn!(
|
||||||
|
"Render GFX modifiers: {:?}",
|
||||||
|
render_gfx_format.write_modifiers
|
||||||
|
);
|
||||||
|
log::warn!("DEV GFX modifiers: {:?}", dev_gfx_format.read_modifiers);
|
||||||
|
return Err(MetalError::MissingRenderModifier(format.name));
|
||||||
|
}
|
||||||
usage = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR;
|
usage = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR;
|
||||||
let render_bo = render_ctx.gfx.gbm().create_bo(width, height, format, usage);
|
let render_bo =
|
||||||
|
render_ctx
|
||||||
|
.gfx
|
||||||
|
.gbm()
|
||||||
|
.create_bo(width, height, format, &possible_modifiers, usage);
|
||||||
let render_bo = match render_bo {
|
let render_bo = match render_bo {
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
Err(e) => return Err(MetalError::ScanoutBuffer(e)),
|
Err(e) => return Err(MetalError::ScanoutBuffer(e)),
|
||||||
|
|
@ -1640,7 +1770,7 @@ impl MetalBackend {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Import the bridge BO into the current device
|
// 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,
|
Ok(img) => img,
|
||||||
Err(e) => return Err(MetalError::ImportImage(e)),
|
Err(e) => return Err(MetalError::ImportImage(e)),
|
||||||
};
|
};
|
||||||
|
|
@ -1717,46 +1847,45 @@ impl MetalBackend {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let primary_plane = 'primary_plane: {
|
let (primary_plane, primary_modifiers) = 'primary_plane: {
|
||||||
for plane in crtc.possible_planes.values() {
|
for plane in crtc.possible_planes.values() {
|
||||||
if plane.ty == PlaneType::Primary
|
if plane.ty == PlaneType::Primary && !plane.assigned.get() {
|
||||||
&& !plane.assigned.get()
|
if let Some(format) = plane.formats.get(&XRGB8888.drm) {
|
||||||
&& plane.formats.contains_key(&XRGB8888.drm)
|
break 'primary_plane (plane.clone(), &format.modifiers);
|
||||||
{
|
}
|
||||||
break 'primary_plane plane.clone();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Err(MetalError::NoPrimaryPlaneForConnector);
|
return Err(MetalError::NoPrimaryPlaneForConnector);
|
||||||
};
|
};
|
||||||
let buffers = Rc::new(self.create_scanout_buffers(
|
let buffers = Rc::new(self.create_scanout_buffers(
|
||||||
&connector.dev,
|
&connector.dev,
|
||||||
&ModifiedFormat {
|
XRGB8888,
|
||||||
format: XRGB8888,
|
primary_modifiers,
|
||||||
modifier: INVALID_MODIFIER,
|
|
||||||
},
|
|
||||||
mode.hdisplay as _,
|
mode.hdisplay as _,
|
||||||
mode.vdisplay as _,
|
mode.vdisplay as _,
|
||||||
ctx,
|
ctx,
|
||||||
false,
|
false,
|
||||||
)?);
|
)?);
|
||||||
let mut cursor_plane = None;
|
let mut cursor_plane = None;
|
||||||
|
let mut cursor_modifiers = &IndexSet::new();
|
||||||
for plane in crtc.possible_planes.values() {
|
for plane in crtc.possible_planes.values() {
|
||||||
if plane.ty == PlaneType::Cursor
|
if plane.ty == PlaneType::Cursor
|
||||||
&& !plane.assigned.get()
|
&& !plane.assigned.get()
|
||||||
&& plane.formats.contains_key(&ARGB8888.drm)
|
&& plane.formats.contains_key(&ARGB8888.drm)
|
||||||
{
|
{
|
||||||
cursor_plane = Some(plane.clone());
|
if let Some(format) = plane.formats.get(&ARGB8888.drm) {
|
||||||
break;
|
cursor_plane = Some(plane.clone());
|
||||||
|
cursor_modifiers = &format.modifiers;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut cursor_buffers = None;
|
let mut cursor_buffers = None;
|
||||||
if cursor_plane.is_some() {
|
if cursor_plane.is_some() {
|
||||||
let res = self.create_scanout_buffers(
|
let res = self.create_scanout_buffers(
|
||||||
&connector.dev,
|
&connector.dev,
|
||||||
&ModifiedFormat {
|
ARGB8888,
|
||||||
format: ARGB8888,
|
cursor_modifiers,
|
||||||
modifier: INVALID_MODIFIER,
|
|
||||||
},
|
|
||||||
connector.dev.cursor_width as _,
|
connector.dev.cursor_width as _,
|
||||||
connector.dev.cursor_height as _,
|
connector.dev.cursor_height as _,
|
||||||
ctx,
|
ctx,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ use {
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
format::XRGB8888,
|
format::XRGB8888,
|
||||||
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
|
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
|
||||||
gfx_apis::create_gfx_context,
|
|
||||||
renderer::RenderResult,
|
renderer::RenderResult,
|
||||||
state::State,
|
state::State,
|
||||||
time::now_usec,
|
time::now_usec,
|
||||||
|
|
@ -20,8 +19,8 @@ use {
|
||||||
},
|
},
|
||||||
video::{
|
video::{
|
||||||
drm::{ConnectorType, Drm, DrmError, DrmVersion},
|
drm::{ConnectorType, Drm, DrmError, DrmVersion},
|
||||||
gbm::{GbmDevice, GbmError, GBM_BO_USE_RENDERING},
|
gbm::{GbmDevice, GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
|
||||||
ModifiedFormat, INVALID_MODIFIER,
|
INVALID_MODIFIER, LINEAR_MODIFIER,
|
||||||
},
|
},
|
||||||
wire_xcon::{
|
wire_xcon::{
|
||||||
ChangeProperty, ChangeWindowAttributes, ConfigureNotify, CreateCursor, CreatePixmap,
|
ChangeProperty, ChangeWindowAttributes, ConfigureNotify, CreateCursor, CreatePixmap,
|
||||||
|
|
@ -50,6 +49,7 @@ use {
|
||||||
Event, XEvent, Xcon, XconError,
|
Event, XEvent, Xcon, XconError,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
jay_config::video::GfxApi,
|
||||||
std::{
|
std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
|
|
@ -118,6 +118,8 @@ pub enum XBackendError {
|
||||||
QueryDevice(#[source] XconError),
|
QueryDevice(#[source] XconError),
|
||||||
#[error("Could not fstat the drm device")]
|
#[error("Could not fstat the drm device")]
|
||||||
DrmDeviceFstat(#[source] Errno),
|
DrmDeviceFstat(#[source] Errno),
|
||||||
|
#[error("Render device does not support XRGB8888 format")]
|
||||||
|
XRGB8888,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create(state: &Rc<State>) -> Result<Rc<XBackend>, XBackendError> {
|
pub async fn create(state: &Rc<State>) -> Result<Rc<XBackend>, XBackendError> {
|
||||||
|
|
@ -179,7 +181,7 @@ pub async fn create(state: &Rc<State>) -> Result<Rc<XBackend>, XBackendError> {
|
||||||
Err(e) => return Err(XBackendError::DrmDeviceFstat(e)),
|
Err(e) => return Err(XBackendError::DrmDeviceFstat(e)),
|
||||||
};
|
};
|
||||||
let gbm = GbmDevice::new(&drm)?;
|
let gbm = GbmDevice::new(&drm)?;
|
||||||
let ctx = match create_gfx_context(&drm) {
|
let ctx = match state.create_gfx_context(&drm, None) {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => return Err(XBackendError::CreateEgl(e)),
|
Err(e) => return Err(XBackendError::CreateEgl(e)),
|
||||||
};
|
};
|
||||||
|
|
@ -376,15 +378,25 @@ impl XBackend {
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
) -> Result<[XImage; 2], XBackendError> {
|
) -> Result<[XImage; 2], XBackendError> {
|
||||||
let format = ModifiedFormat {
|
|
||||||
format: XRGB8888,
|
|
||||||
modifier: INVALID_MODIFIER,
|
|
||||||
};
|
|
||||||
let mut images = [None, None];
|
let mut images = [None, None];
|
||||||
|
let formats = self.ctx.formats();
|
||||||
|
let format = match formats.get(&XRGB8888.drm) {
|
||||||
|
Some(f) => f,
|
||||||
|
None => return Err(XBackendError::XRGB8888),
|
||||||
|
};
|
||||||
|
let mut usage = GBM_BO_USE_RENDERING;
|
||||||
|
let modifier = if format.write_modifiers.contains(&LINEAR_MODIFIER) {
|
||||||
|
&[LINEAR_MODIFIER]
|
||||||
|
} else if format.write_modifiers.contains(&INVALID_MODIFIER) {
|
||||||
|
usage |= GBM_BO_USE_LINEAR;
|
||||||
|
&[INVALID_MODIFIER]
|
||||||
|
} else {
|
||||||
|
panic!("Neither linear nor invalid modifier is supported");
|
||||||
|
};
|
||||||
for image in &mut images {
|
for image in &mut images {
|
||||||
let bo = self
|
let bo = self
|
||||||
.gbm
|
.gbm
|
||||||
.create_bo(width, height, &format, GBM_BO_USE_RENDERING)?;
|
.create_bo(width, height, XRGB8888, modifier, usage)?;
|
||||||
let dma = bo.dmabuf();
|
let dma = bo.dmabuf();
|
||||||
assert!(dma.planes.len() == 1);
|
assert!(dma.planes.len() == 1);
|
||||||
let plane = dma.planes.first().unwrap();
|
let plane = dma.planes.first().unwrap();
|
||||||
|
|
@ -723,12 +735,11 @@ impl XBackend {
|
||||||
if let Some(node) = self.state.root.outputs.get(&output.id) {
|
if let Some(node) = self.state.root.outputs.get(&output.id) {
|
||||||
let mut rr = self.render_result.borrow_mut();
|
let mut rr = self.render_result.borrow_mut();
|
||||||
let fb = image.fb.get();
|
let fb = image.fb.get();
|
||||||
fb.render(
|
fb.render_node(
|
||||||
&*node,
|
&*node,
|
||||||
&self.state,
|
&self.state,
|
||||||
Some(node.global.pos.get()),
|
Some(node.global.pos.get()),
|
||||||
true,
|
Some(rr.deref_mut()),
|
||||||
rr.deref_mut(),
|
|
||||||
node.preferred_scale.get(),
|
node.preferred_scale.get(),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
@ -965,11 +976,20 @@ impl BackendDrmDevice for XDrmDevice {
|
||||||
self.dev
|
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");
|
log::warn!("make_render_device is not supported by the X backend");
|
||||||
// nothing
|
// 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> {
|
fn version(&self) -> Result<DrmVersion, DrmError> {
|
||||||
self.backend.gbm.drm.version()
|
self.backend.gbm.drm.version()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,9 @@ use {
|
||||||
tools::tool_client::{with_tool_client, Handle, ToolClient},
|
tools::tool_client::{with_tool_client, Handle, ToolClient},
|
||||||
utils::{errorfmt::ErrorFmt, queue::AsyncQueue},
|
utils::{errorfmt::ErrorFmt, queue::AsyncQueue},
|
||||||
video::{
|
video::{
|
||||||
dmabuf::{DmaBuf, DmaBufPlane},
|
dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
|
||||||
drm::Drm,
|
drm::Drm,
|
||||||
gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
|
gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
|
||||||
INVALID_MODIFIER,
|
|
||||||
},
|
},
|
||||||
wire::{
|
wire::{
|
||||||
jay_compositor::TakeScreenshot,
|
jay_compositor::TakeScreenshot,
|
||||||
|
|
@ -81,16 +80,18 @@ pub fn buf_to_qoi(buf: &Dmabuf) -> Vec<u8> {
|
||||||
fatal!("Could not create a gbm device: {}", ErrorFmt(e));
|
fatal!("Could not create a gbm device: {}", ErrorFmt(e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let mut planes = PlaneVec::new();
|
||||||
|
planes.push(DmaBufPlane {
|
||||||
|
offset: buf.offset,
|
||||||
|
stride: buf.stride,
|
||||||
|
fd: buf.fd.clone(),
|
||||||
|
});
|
||||||
let dmabuf = DmaBuf {
|
let dmabuf = DmaBuf {
|
||||||
width: buf.width as _,
|
width: buf.width as _,
|
||||||
height: buf.height as _,
|
height: buf.height as _,
|
||||||
format: XRGB8888,
|
format: XRGB8888,
|
||||||
modifier: INVALID_MODIFIER,
|
modifier: (buf.modifier_hi as u64) << 32 | (buf.modifier_lo as u64),
|
||||||
planes: vec![DmaBufPlane {
|
planes,
|
||||||
offset: buf.offset,
|
|
||||||
stride: buf.stride,
|
|
||||||
fd: buf.fd.clone(),
|
|
||||||
}],
|
|
||||||
};
|
};
|
||||||
let bo = match gbm.import_dmabuf(&dmabuf, GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING) {
|
let bo = match gbm.import_dmabuf(&dmabuf, GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING) {
|
||||||
Ok(bo) => Rc::new(bo),
|
Ok(bo) => Rc::new(bo),
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ use {
|
||||||
},
|
},
|
||||||
ahash::AHashSet,
|
ahash::AHashSet,
|
||||||
forker::ForkerProxy,
|
forker::ForkerProxy,
|
||||||
|
jay_config::video::GfxApi,
|
||||||
std::{cell::Cell, env, future::Future, ops::Deref, rc::Rc, sync::Arc, time::Duration},
|
std::{cell::Cell, env, future::Future, ops::Deref, rc::Rc, sync::Arc, time::Duration},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::c,
|
uapi::c,
|
||||||
|
|
@ -197,6 +198,7 @@ fn start_compositor2(
|
||||||
render_ctx_watchers: Default::default(),
|
render_ctx_watchers: Default::default(),
|
||||||
workspace_watchers: Default::default(),
|
workspace_watchers: Default::default(),
|
||||||
default_workspace_capture: Cell::new(true),
|
default_workspace_capture: Cell::new(true),
|
||||||
|
default_gfx_api: Cell::new(GfxApi::OpenGl),
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
@ -410,6 +412,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
desired_output: CloneCell::new(dummy_output.global.output_id.clone()),
|
desired_output: CloneCell::new(dummy_output.global.output_id.clone()),
|
||||||
jay_workspaces: Default::default(),
|
jay_workspaces: Default::default(),
|
||||||
capture: Cell::new(false),
|
capture: Cell::new(false),
|
||||||
|
title_texture: Cell::new(None),
|
||||||
});
|
});
|
||||||
dummy_workspace.output_link.set(Some(
|
dummy_workspace.output_link.set(Some(
|
||||||
dummy_output.workspaces.add_last(dummy_workspace.clone()),
|
dummy_output.workspaces.add_last(dummy_workspace.clone()),
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ use {
|
||||||
logging::LogLevel,
|
logging::LogLevel,
|
||||||
theme::{colors::Colorable, sized::Resizable},
|
theme::{colors::Colorable, sized::Resizable},
|
||||||
timer::Timer as JayTimer,
|
timer::Timer as JayTimer,
|
||||||
video::{Connector, DrmDevice},
|
video::{Connector, DrmDevice, GfxApi},
|
||||||
Axis, Direction, Workspace,
|
Axis, Direction, Workspace,
|
||||||
},
|
},
|
||||||
libloading::Library,
|
libloading::Library,
|
||||||
|
|
@ -582,6 +582,14 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
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) {
|
fn handle_get_default_workspace_capture(&self) {
|
||||||
self.respond(Response::GetDefaultWorkspaceCapture {
|
self.respond(Response::GetDefaultWorkspaceCapture {
|
||||||
capture: self.state.default_workspace_capture.get(),
|
capture: self.state.default_workspace_capture.get(),
|
||||||
|
|
@ -1309,6 +1317,9 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::SetNaturalScrollingEnabled { device, enabled } => self
|
ClientMessage::SetNaturalScrollingEnabled { device, enabled } => self
|
||||||
.handle_set_natural_scrolling_enabled(device, enabled)
|
.handle_set_natural_scrolling_enabled(device, enabled)
|
||||||
.wrn("set_natural_scrolling_enabled")?,
|
.wrn("set_natural_scrolling_enabled")?,
|
||||||
|
ClientMessage::SetGfxApi { device, api } => {
|
||||||
|
self.handle_set_gfx_api(device, api).wrn("set_gfx_api")?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -310,7 +310,7 @@ impl CursorImageScaled {
|
||||||
extents: Rect::new_sized(-xhot, -yhot, width, height).unwrap(),
|
extents: Rect::new_sized(-xhot, -yhot, width, height).unwrap(),
|
||||||
tex: ctx
|
tex: ctx
|
||||||
.clone()
|
.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,
|
&img.tex,
|
||||||
extents.x1(),
|
extents.x1(),
|
||||||
extents.y1(),
|
extents.y1(),
|
||||||
ARGB8888,
|
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
scale,
|
scale,
|
||||||
|
|
@ -384,7 +383,6 @@ impl Cursor for StaticCursor {
|
||||||
&img.tex,
|
&img.tex,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
ARGB8888,
|
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
renderer.scale(),
|
renderer.scale(),
|
||||||
|
|
@ -422,7 +420,6 @@ impl Cursor for AnimatedCursor {
|
||||||
&img.tex,
|
&img.tex,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
ARGB8888,
|
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
renderer.scale(),
|
renderer.scale(),
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ impl DrmFeedback {
|
||||||
fn create_fd_data(ctx: &dyn GfxContext) -> Vec<u8> {
|
fn create_fd_data(ctx: &dyn GfxContext) -> Vec<u8> {
|
||||||
let mut vec = vec![];
|
let mut vec = vec![];
|
||||||
for (format, info) in &*ctx.formats() {
|
for (format, info) in &*ctx.formats() {
|
||||||
for modifier in info.modifiers.keys() {
|
for modifier in &info.read_modifiers {
|
||||||
vec.write_u32::<NativeEndian>(*format).unwrap();
|
vec.write_u32::<NativeEndian>(*format).unwrap();
|
||||||
vec.write_u32::<NativeEndian>(0).unwrap();
|
vec.write_u32::<NativeEndian>(0).unwrap();
|
||||||
vec.write_u64::<NativeEndian>(*modifier).unwrap();
|
vec.write_u64::<NativeEndian>(*modifier).unwrap();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::utils::{bitflags::BitflagsExt, ptr_ext::PtrExt, stack::Stack},
|
crate::utils::{
|
||||||
|
bitflags::BitflagsExt, clonecell::UnsafeCellCloneSafe, ptr_ext::PtrExt, stack::Stack,
|
||||||
|
},
|
||||||
bstr::{BString, ByteSlice},
|
bstr::{BString, ByteSlice},
|
||||||
std::{
|
std::{
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
|
|
@ -393,6 +395,8 @@ pub enum EdidParseContext {
|
||||||
VideoInputDefinition,
|
VideoInputDefinition,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl UnsafeCellCloneSafe for EdidParseContext {}
|
||||||
|
|
||||||
struct EdidPushedContext {
|
struct EdidPushedContext {
|
||||||
stack: Rc<Stack<(usize, EdidParseContext)>>,
|
stack: Rc<Stack<(usize, EdidParseContext)>>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,23 @@ use {
|
||||||
gfx_apis::gl::sys::{GLint, GL_BGRA_EXT, GL_RGBA, GL_UNSIGNED_BYTE},
|
gfx_apis::gl::sys::{GLint, GL_BGRA_EXT, GL_RGBA, GL_UNSIGNED_BYTE},
|
||||||
pipewire::pw_pod::{
|
pipewire::pw_pod::{
|
||||||
SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx, SpaVideoFormat, SPA_VIDEO_FORMAT_BGRA,
|
SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx, SpaVideoFormat, SPA_VIDEO_FORMAT_BGRA,
|
||||||
SPA_VIDEO_FORMAT_NV12, SPA_VIDEO_FORMAT_RGBA,
|
SPA_VIDEO_FORMAT_RGBA,
|
||||||
},
|
},
|
||||||
utils::debug_fn::debug_fn,
|
utils::debug_fn::debug_fn,
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
ash::vk,
|
||||||
once_cell::sync::Lazy,
|
once_cell::sync::Lazy,
|
||||||
std::fmt::{Debug, Write},
|
std::fmt::{Debug, Write},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq)]
|
||||||
pub struct Format {
|
pub struct Format {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
pub bpp: u32,
|
pub bpp: u32,
|
||||||
pub gl_format: GLint,
|
pub gl_format: GLint,
|
||||||
pub gl_type: GLint,
|
pub gl_type: GLint,
|
||||||
|
pub vk_format: vk::Format,
|
||||||
pub drm: u32,
|
pub drm: u32,
|
||||||
pub wl_id: Option<u32>,
|
pub wl_id: Option<u32>,
|
||||||
pub external_only_guess: bool,
|
pub external_only_guess: bool,
|
||||||
|
|
@ -26,6 +28,12 @@ pub struct Format {
|
||||||
pub pipewire: SpaVideoFormat,
|
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(|| {
|
static FORMATS_MAP: Lazy<AHashMap<u32, &'static Format>> = Lazy::new(|| {
|
||||||
let mut map = AHashMap::new();
|
let mut map = AHashMap::new();
|
||||||
for format in FORMATS {
|
for format in FORMATS {
|
||||||
|
|
@ -80,39 +88,44 @@ pub fn map_wayland_format_id(id: u32) -> u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub static ARGB8888: &Format = &FORMATS[0];
|
pub static ARGB8888: &Format = &Format {
|
||||||
pub static XRGB8888: &Format = &FORMATS[1];
|
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] = &[
|
pub static FORMATS: &[Format] = &[
|
||||||
Format {
|
*ARGB8888,
|
||||||
name: "argb8888",
|
*XRGB8888,
|
||||||
bpp: 4,
|
// *NV12,
|
||||||
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,
|
|
||||||
},
|
|
||||||
Format {
|
Format {
|
||||||
name: "abgr8888",
|
name: "abgr8888",
|
||||||
bpp: 4,
|
bpp: 4,
|
||||||
gl_format: GL_RGBA,
|
gl_format: GL_RGBA,
|
||||||
gl_type: GL_UNSIGNED_BYTE,
|
gl_type: GL_UNSIGNED_BYTE,
|
||||||
|
vk_format: vk::Format::R8G8B8A8_SRGB,
|
||||||
drm: fourcc_code('A', 'B', '2', '4'),
|
drm: fourcc_code('A', 'B', '2', '4'),
|
||||||
wl_id: None,
|
wl_id: None,
|
||||||
external_only_guess: false,
|
external_only_guess: false,
|
||||||
|
|
@ -125,6 +138,7 @@ pub static FORMATS: &[Format] = &[
|
||||||
bpp: 4,
|
bpp: 4,
|
||||||
gl_format: GL_RGBA,
|
gl_format: GL_RGBA,
|
||||||
gl_type: GL_UNSIGNED_BYTE,
|
gl_type: GL_UNSIGNED_BYTE,
|
||||||
|
vk_format: vk::Format::R8G8B8A8_SRGB,
|
||||||
drm: fourcc_code('X', 'B', '2', '4'),
|
drm: fourcc_code('X', 'B', '2', '4'),
|
||||||
wl_id: None,
|
wl_id: None,
|
||||||
external_only_guess: false,
|
external_only_guess: false,
|
||||||
|
|
@ -132,18 +146,18 @@ pub static FORMATS: &[Format] = &[
|
||||||
shm_supported: true,
|
shm_supported: true,
|
||||||
pipewire: SPA_VIDEO_FORMAT_RGBx,
|
pipewire: SPA_VIDEO_FORMAT_RGBx,
|
||||||
},
|
},
|
||||||
Format {
|
// Format {
|
||||||
name: "nv12",
|
// name: "nv12",
|
||||||
bpp: 1, // wrong but only used for shm
|
// bpp: 1, // wrong but only used for shm
|
||||||
gl_format: 0, // wrong but only used for shm
|
// gl_format: 0, // wrong but only used for shm
|
||||||
gl_type: GL_UNSIGNED_BYTE, // wrong but only used for shm
|
// gl_type: GL_UNSIGNED_BYTE, // wrong but only used for shm
|
||||||
drm: fourcc_code('N', 'V', '1', '2'),
|
// drm: fourcc_code('N', 'V', '1', '2'),
|
||||||
wl_id: None,
|
// wl_id: None,
|
||||||
external_only_guess: true,
|
// external_only_guess: true,
|
||||||
has_alpha: false,
|
// has_alpha: false,
|
||||||
shm_supported: false,
|
// shm_supported: false,
|
||||||
pipewire: SPA_VIDEO_FORMAT_NV12,
|
// pipewire: SPA_VIDEO_FORMAT_NV12,
|
||||||
},
|
// },
|
||||||
// Format {
|
// Format {
|
||||||
// id: fourcc_code('C', '8', ' ', ' '),
|
// id: fourcc_code('C', '8', ' ', ' '),
|
||||||
// name: "c8",
|
// name: "c8",
|
||||||
|
|
|
||||||
177
src/gfx_api.rs
177
src/gfx_api.rs
|
|
@ -1,16 +1,19 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cursor::Cursor,
|
cursor::Cursor,
|
||||||
|
fixed::Fixed,
|
||||||
format::Format,
|
format::Format,
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
renderer::{renderer_base::RendererBase, RenderResult},
|
renderer::{renderer_base::RendererBase, RenderResult, Renderer},
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
state::State,
|
state::State,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
tree::Node,
|
tree::Node,
|
||||||
video::{dmabuf::DmaBuf, gbm::GbmDevice},
|
video::{dmabuf::DmaBuf, gbm::GbmDevice, Modifier},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
indexmap::IndexSet,
|
||||||
|
jay_config::video::GfxApi,
|
||||||
std::{
|
std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
|
|
@ -24,7 +27,6 @@ use {
|
||||||
|
|
||||||
pub enum GfxApiOpt {
|
pub enum GfxApiOpt {
|
||||||
Sync,
|
Sync,
|
||||||
Clear(Clear),
|
|
||||||
FillRect(FillRect),
|
FillRect(FillRect),
|
||||||
CopyTexture(CopyTexture),
|
CopyTexture(CopyTexture),
|
||||||
}
|
}
|
||||||
|
|
@ -79,6 +81,7 @@ impl BufferPoints {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct AbsoluteRect {
|
pub struct AbsoluteRect {
|
||||||
pub x1: f32,
|
pub x1: f32,
|
||||||
pub x2: f32,
|
pub x2: f32,
|
||||||
|
|
@ -86,10 +89,7 @@ pub struct AbsoluteRect {
|
||||||
pub y2: f32,
|
pub y2: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Clear {
|
#[derive(Debug)]
|
||||||
pub color: Color,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FillRect {
|
pub struct FillRect {
|
||||||
pub rect: AbsoluteRect,
|
pub rect: AbsoluteRect,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
|
@ -97,7 +97,6 @@ pub struct FillRect {
|
||||||
|
|
||||||
pub struct CopyTexture {
|
pub struct CopyTexture {
|
||||||
pub tex: Rc<dyn GfxTexture>,
|
pub tex: Rc<dyn GfxTexture>,
|
||||||
pub format: &'static Format,
|
|
||||||
pub source: BufferPoints,
|
pub source: BufferPoints,
|
||||||
pub target: AbsoluteRect,
|
pub target: AbsoluteRect,
|
||||||
}
|
}
|
||||||
|
|
@ -113,18 +112,11 @@ pub enum ResetStatus {
|
||||||
pub trait GfxFramebuffer: Debug {
|
pub trait GfxFramebuffer: Debug {
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
|
||||||
fn clear(&self);
|
fn take_render_ops(&self) -> Vec<GfxApiOpt>;
|
||||||
|
|
||||||
fn clear_with(&self, r: f32, g: f32, b: f32, a: f32);
|
fn size(&self) -> (i32, i32);
|
||||||
|
|
||||||
fn copy_texture(
|
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>);
|
||||||
&self,
|
|
||||||
state: &State,
|
|
||||||
texture: &Rc<dyn GfxTexture>,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
alpha: bool,
|
|
||||||
);
|
|
||||||
|
|
||||||
fn copy_to_shm(
|
fn copy_to_shm(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -134,22 +126,122 @@ pub trait GfxFramebuffer: Debug {
|
||||||
height: i32,
|
height: i32,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
shm: &[Cell<u8>],
|
shm: &[Cell<u8>],
|
||||||
);
|
) -> Result<(), GfxError>;
|
||||||
|
|
||||||
fn render_custom(&self, scale: Scale, f: &mut dyn FnMut(&mut RendererBase));
|
fn format(&self) -> &'static Format;
|
||||||
|
}
|
||||||
|
|
||||||
fn render(
|
impl dyn GfxFramebuffer {
|
||||||
|
pub fn clear(&self) {
|
||||||
|
self.clear_with(0.0, 0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_with(&self, r: f32, g: f32, b: f32, a: f32) {
|
||||||
|
let ops = self.take_render_ops();
|
||||||
|
self.render(ops, Some(&Color { r, g, b, a }));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 mut renderer = RendererBase {
|
||||||
|
ops: &mut ops,
|
||||||
|
scaled: false,
|
||||||
|
scale,
|
||||||
|
scalef: 1.0,
|
||||||
|
};
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_custom(
|
||||||
|
&self,
|
||||||
|
scale: Scale,
|
||||||
|
clear: Option<&Color>,
|
||||||
|
f: &mut dyn FnMut(&mut RendererBase),
|
||||||
|
) {
|
||||||
|
let mut ops = self.take_render_ops();
|
||||||
|
let mut renderer = RendererBase {
|
||||||
|
ops: &mut ops,
|
||||||
|
scaled: scale != 1,
|
||||||
|
scale,
|
||||||
|
scalef: scale.to_f64(),
|
||||||
|
};
|
||||||
|
f(&mut renderer);
|
||||||
|
self.render(ops, clear);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_node(
|
||||||
&self,
|
&self,
|
||||||
node: &dyn Node,
|
node: &dyn Node,
|
||||||
state: &State,
|
state: &State,
|
||||||
cursor_rect: Option<Rect>,
|
cursor_rect: Option<Rect>,
|
||||||
on_output: bool,
|
result: Option<&mut RenderResult>,
|
||||||
result: &mut RenderResult,
|
|
||||||
scale: Scale,
|
scale: Scale,
|
||||||
render_hardware_cursor: bool,
|
render_hardware_cursor: bool,
|
||||||
);
|
) {
|
||||||
|
let mut ops = self.take_render_ops();
|
||||||
|
let (width, height) = self.size();
|
||||||
|
let mut renderer = Renderer {
|
||||||
|
base: RendererBase {
|
||||||
|
ops: &mut ops,
|
||||||
|
scaled: scale != 1,
|
||||||
|
scale,
|
||||||
|
scalef: scale.to_f64(),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
result,
|
||||||
|
logical_extents: node.node_absolute_position().at_point(0, 0),
|
||||||
|
physical_extents: Rect::new(0, 0, width, height).unwrap(),
|
||||||
|
};
|
||||||
|
node.node_render(&mut renderer, 0, 0, i32::MAX, i32::MAX);
|
||||||
|
if let Some(rect) = cursor_rect {
|
||||||
|
let seats = state.globals.lock_seats();
|
||||||
|
for seat in seats.values() {
|
||||||
|
if let Some(cursor) = seat.get_cursor() {
|
||||||
|
let (mut x, mut y) = seat.get_position();
|
||||||
|
if let Some(dnd_icon) = seat.dnd_icon() {
|
||||||
|
let extents = dnd_icon.extents.get().move_(
|
||||||
|
x.round_down() + dnd_icon.buf_x.get(),
|
||||||
|
y.round_down() + dnd_icon.buf_y.get(),
|
||||||
|
);
|
||||||
|
if extents.intersects(&rect) {
|
||||||
|
let (x, y) = rect.translate(extents.x1(), extents.y1());
|
||||||
|
renderer.render_surface(&dnd_icon, x, y, i32::MAX, i32::MAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if render_hardware_cursor || !seat.hardware_cursor() {
|
||||||
|
cursor.tick();
|
||||||
|
x -= Fixed::from_int(rect.x1());
|
||||||
|
y -= Fixed::from_int(rect.y1());
|
||||||
|
cursor.render(&mut renderer, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let c = state.theme.colors.background.get();
|
||||||
|
self.render(ops, Some(&c));
|
||||||
|
}
|
||||||
|
|
||||||
fn render_hardware_cursor(&self, cursor: &dyn Cursor, state: &State, scale: Scale);
|
pub fn render_hardware_cursor(&self, cursor: &dyn Cursor, state: &State, scale: Scale) {
|
||||||
|
let mut ops = self.take_render_ops();
|
||||||
|
let (width, height) = self.size();
|
||||||
|
let mut renderer = Renderer {
|
||||||
|
base: RendererBase {
|
||||||
|
ops: &mut ops,
|
||||||
|
scaled: scale != 1,
|
||||||
|
scale,
|
||||||
|
scalef: scale.to_f64(),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
result: None,
|
||||||
|
logical_extents: Rect::new_empty(0, 0),
|
||||||
|
physical_extents: Rect::new(0, 0, width, height).unwrap(),
|
||||||
|
};
|
||||||
|
cursor.render_hardware_cursor(&mut renderer);
|
||||||
|
self.render(ops, Some(&Color::TRANSPARENT));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GfxImage {
|
pub trait GfxImage {
|
||||||
|
|
@ -162,28 +254,37 @@ pub trait GfxImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GfxTexture: Debug {
|
pub trait GfxTexture: Debug {
|
||||||
fn width(&self) -> i32;
|
fn size(&self) -> (i32, i32);
|
||||||
fn height(&self) -> i32;
|
|
||||||
fn as_any(&self) -> &dyn Any;
|
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 {
|
pub trait GfxContext: Debug {
|
||||||
fn take_render_ops(&self) -> Vec<GfxApiOpt>;
|
|
||||||
|
|
||||||
fn reset_status(&self) -> Option<ResetStatus>;
|
fn reset_status(&self) -> Option<ResetStatus>;
|
||||||
|
|
||||||
fn supports_external_texture(&self) -> bool;
|
|
||||||
|
|
||||||
fn render_node(&self) -> Rc<CString>;
|
fn render_node(&self) -> Rc<CString>;
|
||||||
|
|
||||||
fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>>;
|
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 dmabuf_img(self: Rc<Self>, buf: &DmaBuf) -> Result<Rc<dyn GfxImage>, GfxError>;
|
||||||
|
|
||||||
fn shmem_texture(
|
fn shmem_texture(
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
|
old: Option<Rc<dyn GfxTexture>>,
|
||||||
data: &[Cell<u8>],
|
data: &[Cell<u8>],
|
||||||
format: &'static Format,
|
format: &'static Format,
|
||||||
width: i32,
|
width: i32,
|
||||||
|
|
@ -192,19 +293,15 @@ pub trait GfxContext: Debug {
|
||||||
) -> Result<Rc<dyn GfxTexture>, GfxError>;
|
) -> Result<Rc<dyn GfxTexture>, GfxError>;
|
||||||
|
|
||||||
fn gbm(&self) -> &GbmDevice;
|
fn gbm(&self) -> &GbmDevice;
|
||||||
|
|
||||||
|
fn gfx_api(&self) -> GfxApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GfxFormat {
|
pub struct GfxFormat {
|
||||||
pub format: &'static Format,
|
pub format: &'static Format,
|
||||||
pub implicit_external_only: bool,
|
pub read_modifiers: IndexSet<Modifier>,
|
||||||
pub modifiers: AHashMap<u64, GfxModifier>,
|
pub write_modifiers: IndexSet<Modifier>,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct GfxModifier {
|
|
||||||
pub modifier: u64,
|
|
||||||
pub external_only: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error)]
|
#[derive(Error)]
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,49 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
async_engine::AsyncEngine,
|
||||||
gfx_api::{GfxContext, GfxError},
|
gfx_api::{GfxContext, GfxError},
|
||||||
|
io_uring::IoUring,
|
||||||
|
utils::errorfmt::ErrorFmt,
|
||||||
video::drm::Drm,
|
video::drm::Drm,
|
||||||
},
|
},
|
||||||
|
jay_config::video::GfxApi,
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod gl;
|
pub mod gl;
|
||||||
|
mod vulkan;
|
||||||
|
|
||||||
pub fn create_gfx_context(drm: &Drm) -> Result<Rc<dyn GfxContext>, GfxError> {
|
pub fn create_gfx_context(
|
||||||
gl::create_gfx_context(drm)
|
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!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ macro_rules! egl_transparent {
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::Format,
|
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
BufferPoints, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
BufferPoints, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
||||||
},
|
},
|
||||||
|
|
@ -28,11 +27,10 @@ use {
|
||||||
gl::texture::image_target,
|
gl::texture::image_target,
|
||||||
renderer::{context::GlRenderContext, framebuffer::Framebuffer, texture::Texture},
|
renderer::{context::GlRenderContext, framebuffer::Framebuffer, texture::Texture},
|
||||||
sys::{
|
sys::{
|
||||||
glActiveTexture, glBindTexture, glClear, glClearColor, glDisable,
|
glActiveTexture, glBindTexture, glDisable, glDisableVertexAttribArray,
|
||||||
glDisableVertexAttribArray, glDrawArrays, glEnable, glEnableVertexAttribArray,
|
glDrawArrays, glEnable, glEnableVertexAttribArray, glTexParameteri, glUniform1i,
|
||||||
glTexParameteri, glUniform1i, glUniform4f, glUseProgram, glVertexAttribPointer,
|
glUniform4f, glUseProgram, glVertexAttribPointer, GL_BLEND, GL_FALSE, GL_FLOAT,
|
||||||
GL_BLEND, GL_COLOR_BUFFER_BIT, GL_FALSE, GL_FLOAT, GL_LINEAR, GL_TEXTURE0,
|
GL_LINEAR, GL_TEXTURE0, GL_TEXTURE_MIN_FILTER, GL_TRIANGLES, GL_TRIANGLE_STRIP,
|
||||||
GL_TEXTURE_MIN_FILTER, GL_TRIANGLES, GL_TRIANGLE_STRIP,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
theme::Color,
|
theme::Color,
|
||||||
|
|
@ -127,6 +125,10 @@ enum RenderError {
|
||||||
ExternalOnly,
|
ExternalOnly,
|
||||||
#[error("OpenGL context does not support external textures")]
|
#[error("OpenGL context does not support external textures")]
|
||||||
ExternalUnsupported,
|
ExternalUnsupported,
|
||||||
|
#[error("OpenGL context does not support any formats")]
|
||||||
|
NoSupportedFormats,
|
||||||
|
#[error("Unsupported operation")]
|
||||||
|
UnsupportedOperation,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -164,13 +166,6 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GfxApiOpt::Clear(c) => {
|
|
||||||
if has_ops!() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
clear(&c.color);
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
GfxApiOpt::FillRect(f) => {
|
GfxApiOpt::FillRect(f) => {
|
||||||
fill_rect.push(f);
|
fill_rect.push(f);
|
||||||
i += 1;
|
i += 1;
|
||||||
|
|
@ -220,27 +215,11 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) {
|
||||||
let y1 = 2.0 * (tex.target.y1 / height) - 1.0;
|
let y1 = 2.0 * (tex.target.y1 / height) - 1.0;
|
||||||
let x2 = 2.0 * (tex.target.x2 / width) - 1.0;
|
let x2 = 2.0 * (tex.target.x2 / width) - 1.0;
|
||||||
let y2 = 2.0 * (tex.target.y2 / height) - 1.0;
|
let y2 = 2.0 * (tex.target.y2 / height) - 1.0;
|
||||||
render_texture(
|
render_texture(&fb.ctx, &tex.tex.as_gl(), x1, y1, x2, y2, &tex.source)
|
||||||
&fb.ctx,
|
|
||||||
&tex.tex.as_gl(),
|
|
||||||
tex.format,
|
|
||||||
x1,
|
|
||||||
y1,
|
|
||||||
x2,
|
|
||||||
y2,
|
|
||||||
&tex.source,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(c: &Color) {
|
|
||||||
unsafe {
|
|
||||||
glClearColor(c.r, c.g, c.b, c.a);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill_boxes3(ctx: &GlRenderContext, boxes: &[f32], color: &Color) {
|
fn fill_boxes3(ctx: &GlRenderContext, boxes: &[f32], color: &Color) {
|
||||||
unsafe {
|
unsafe {
|
||||||
glUseProgram(ctx.fill_prog.prog);
|
glUseProgram(ctx.fill_prog.prog);
|
||||||
|
|
@ -262,7 +241,6 @@ fn fill_boxes3(ctx: &GlRenderContext, boxes: &[f32], color: &Color) {
|
||||||
fn render_texture(
|
fn render_texture(
|
||||||
ctx: &GlRenderContext,
|
ctx: &GlRenderContext,
|
||||||
texture: &Texture,
|
texture: &Texture,
|
||||||
format: &Format,
|
|
||||||
x1: f32,
|
x1: f32,
|
||||||
y1: f32,
|
y1: f32,
|
||||||
x2: f32,
|
x2: f32,
|
||||||
|
|
@ -288,7 +266,7 @@ fn render_texture(
|
||||||
},
|
},
|
||||||
false => &ctx.tex_internal,
|
false => &ctx.tex_internal,
|
||||||
};
|
};
|
||||||
let prog = match format.has_alpha {
|
let prog = match texture.gl.format.has_alpha {
|
||||||
true => {
|
true => {
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
&progs.alpha
|
&progs.alpha
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
gfx_api::ResetStatus,
|
gfx_api::{GfxFormat, ResetStatus},
|
||||||
gfx_apis::gl::{
|
gfx_apis::gl::{
|
||||||
egl::{
|
egl::{
|
||||||
display::EglDisplay,
|
display::EglDisplay,
|
||||||
|
|
@ -17,6 +17,7 @@ use {
|
||||||
RenderError,
|
RenderError,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ahash::AHashMap,
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -25,6 +26,7 @@ pub struct EglContext {
|
||||||
pub dpy: Rc<EglDisplay>,
|
pub dpy: Rc<EglDisplay>,
|
||||||
pub ext: GlExt,
|
pub ext: GlExt,
|
||||||
pub ctx: EGLContext,
|
pub ctx: EGLContext,
|
||||||
|
pub formats: Rc<AHashMap<u32, GfxFormat>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for EglContext {
|
impl Drop for EglContext {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::{formats, Format},
|
format::{formats, Format},
|
||||||
gfx_api::{GfxFormat, GfxModifier},
|
gfx_api::GfxFormat,
|
||||||
gfx_apis::gl::{
|
gfx_apis::gl::{
|
||||||
egl::{
|
egl::{
|
||||||
context::EglContext,
|
context::EglContext,
|
||||||
|
|
@ -30,16 +30,30 @@ use {
|
||||||
},
|
},
|
||||||
RenderError,
|
RenderError,
|
||||||
},
|
},
|
||||||
video::{dmabuf::DmaBuf, drm::Drm, gbm::GbmDevice, INVALID_MODIFIER},
|
video::{dmabuf::DmaBuf, drm::Drm, gbm::GbmDevice, Modifier, INVALID_MODIFIER},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
indexmap::{IndexMap, IndexSet},
|
||||||
std::{ptr, rc::Rc},
|
std::{ptr, rc::Rc},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EglFormat {
|
||||||
|
pub format: &'static Format,
|
||||||
|
pub implicit_external_only: bool,
|
||||||
|
pub modifiers: IndexMap<u64, EglModifier>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EglModifier {
|
||||||
|
pub modifier: u64,
|
||||||
|
pub external_only: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EglDisplay {
|
pub struct EglDisplay {
|
||||||
pub exts: DisplayExt,
|
pub exts: DisplayExt,
|
||||||
pub formats: Rc<AHashMap<u32, GfxFormat>>,
|
pub formats: AHashMap<u32, EglFormat>,
|
||||||
pub gbm: Rc<GbmDevice>,
|
pub gbm: Rc<GbmDevice>,
|
||||||
pub dpy: EGLDisplay,
|
pub dpy: EGLDisplay,
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +75,7 @@ impl EglDisplay {
|
||||||
}
|
}
|
||||||
let mut dpy = EglDisplay {
|
let mut dpy = EglDisplay {
|
||||||
exts: DisplayExt::empty(),
|
exts: DisplayExt::empty(),
|
||||||
formats: Rc::new(AHashMap::new()),
|
formats: AHashMap::new(),
|
||||||
gbm: Rc::new(gbm),
|
gbm: Rc::new(gbm),
|
||||||
dpy,
|
dpy,
|
||||||
};
|
};
|
||||||
|
|
@ -89,7 +103,7 @@ impl EglDisplay {
|
||||||
if !dpy.exts.intersects(DisplayExt::KHR_SURFACELESS_CONTEXT) {
|
if !dpy.exts.intersects(DisplayExt::KHR_SURFACELESS_CONTEXT) {
|
||||||
return Err(RenderError::SurfacelessContext);
|
return Err(RenderError::SurfacelessContext);
|
||||||
}
|
}
|
||||||
dpy.formats = Rc::new(query_formats(dpy.dpy)?);
|
dpy.formats = query_formats(dpy.dpy)?;
|
||||||
|
|
||||||
Ok(Rc::new(dpy))
|
Ok(Rc::new(dpy))
|
||||||
}
|
}
|
||||||
|
|
@ -109,27 +123,62 @@ impl EglDisplay {
|
||||||
log::warn!("EGL display does not support gpu reset notifications");
|
log::warn!("EGL display does not support gpu reset notifications");
|
||||||
}
|
}
|
||||||
attrib.push(EGL_NONE);
|
attrib.push(EGL_NONE);
|
||||||
unsafe {
|
let ctx = unsafe {
|
||||||
let ctx = eglCreateContext(
|
eglCreateContext(
|
||||||
self.dpy,
|
self.dpy,
|
||||||
EGLConfig::none(),
|
EGLConfig::none(),
|
||||||
EGLContext::none(),
|
EGLContext::none(),
|
||||||
attrib.as_ptr(),
|
attrib.as_ptr(),
|
||||||
);
|
)
|
||||||
if ctx.is_none() {
|
};
|
||||||
return Err(RenderError::CreateContext);
|
if ctx.is_none() {
|
||||||
}
|
return Err(RenderError::CreateContext);
|
||||||
let mut ctx = EglContext {
|
|
||||||
dpy: self.clone(),
|
|
||||||
ext: GlExt::empty(),
|
|
||||||
ctx,
|
|
||||||
};
|
|
||||||
ctx.ext = ctx.with_current(|| Ok(get_gl_ext()))?;
|
|
||||||
if !ctx.ext.contains(GlExt::GL_OES_EGL_IMAGE) {
|
|
||||||
return Err(RenderError::OesEglImage);
|
|
||||||
}
|
|
||||||
Ok(Rc::new(ctx))
|
|
||||||
}
|
}
|
||||||
|
let mut ctx = EglContext {
|
||||||
|
dpy: self.clone(),
|
||||||
|
ext: GlExt::empty(),
|
||||||
|
ctx,
|
||||||
|
formats: Default::default(),
|
||||||
|
};
|
||||||
|
ctx.ext = ctx.with_current(|| Ok(get_gl_ext()))?;
|
||||||
|
if !ctx.ext.contains(GlExt::GL_OES_EGL_IMAGE) {
|
||||||
|
return Err(RenderError::OesEglImage);
|
||||||
|
}
|
||||||
|
ctx.formats = {
|
||||||
|
let mut formats = AHashMap::new();
|
||||||
|
let supports_external_only = ctx.ext.contains(GlExt::GL_OES_EGL_IMAGE_EXTERNAL);
|
||||||
|
for (&drm, format) in &self.formats {
|
||||||
|
if format.implicit_external_only && !supports_external_only {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut read_modifiers = IndexSet::new();
|
||||||
|
let mut write_modifiers = IndexSet::new();
|
||||||
|
for modifier in format.modifiers.values() {
|
||||||
|
if modifier.external_only && !supports_external_only {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !modifier.external_only {
|
||||||
|
write_modifiers.insert(modifier.modifier);
|
||||||
|
}
|
||||||
|
read_modifiers.insert(modifier.modifier);
|
||||||
|
}
|
||||||
|
if !read_modifiers.is_empty() || !write_modifiers.is_empty() {
|
||||||
|
formats.insert(
|
||||||
|
drm,
|
||||||
|
GfxFormat {
|
||||||
|
format: format.format,
|
||||||
|
read_modifiers,
|
||||||
|
write_modifiers,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rc::new(formats)
|
||||||
|
};
|
||||||
|
if ctx.formats.is_empty() {
|
||||||
|
return Err(RenderError::NoSupportedFormats);
|
||||||
|
}
|
||||||
|
Ok(Rc::new(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::gfx_apis::gl) fn import_dmabuf(
|
pub(in crate::gfx_apis::gl) fn import_dmabuf(
|
||||||
|
|
@ -214,6 +263,7 @@ impl EglDisplay {
|
||||||
width: buf.width,
|
width: buf.width,
|
||||||
height: buf.height,
|
height: buf.height,
|
||||||
external_only: format.external_only,
|
external_only: format.external_only,
|
||||||
|
format: buf.format,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +278,7 @@ impl Drop for EglDisplay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn query_formats(dpy: EGLDisplay) -> Result<AHashMap<u32, GfxFormat>, RenderError> {
|
unsafe fn query_formats(dpy: EGLDisplay) -> Result<AHashMap<u32, EglFormat>, RenderError> {
|
||||||
let mut vec = vec![];
|
let mut vec = vec![];
|
||||||
let mut num = 0;
|
let mut num = 0;
|
||||||
let res = PROCS.eglQueryDmaBufFormatsEXT(dpy, num, ptr::null_mut(), &mut num);
|
let res = PROCS.eglQueryDmaBufFormatsEXT(dpy, num, ptr::null_mut(), &mut num);
|
||||||
|
|
@ -248,7 +298,7 @@ unsafe fn query_formats(dpy: EGLDisplay) -> Result<AHashMap<u32, GfxFormat>, Ren
|
||||||
let (modifiers, external_only) = query_modifiers(dpy, fmt, format)?;
|
let (modifiers, external_only) = query_modifiers(dpy, fmt, format)?;
|
||||||
res.insert(
|
res.insert(
|
||||||
format.drm,
|
format.drm,
|
||||||
GfxFormat {
|
EglFormat {
|
||||||
format,
|
format,
|
||||||
implicit_external_only: external_only,
|
implicit_external_only: external_only,
|
||||||
modifiers,
|
modifiers,
|
||||||
|
|
@ -263,7 +313,7 @@ unsafe fn query_modifiers(
|
||||||
dpy: EGLDisplay,
|
dpy: EGLDisplay,
|
||||||
gl_format: EGLint,
|
gl_format: EGLint,
|
||||||
format: &'static Format,
|
format: &'static Format,
|
||||||
) -> Result<(AHashMap<u64, GfxModifier>, bool), RenderError> {
|
) -> Result<(IndexMap<Modifier, EglModifier>, bool), RenderError> {
|
||||||
let mut mods = vec![];
|
let mut mods = vec![];
|
||||||
let mut ext_only = vec![];
|
let mut ext_only = vec![];
|
||||||
let mut num = 0;
|
let mut num = 0;
|
||||||
|
|
@ -293,11 +343,11 @@ unsafe fn query_modifiers(
|
||||||
}
|
}
|
||||||
mods.set_len(num as usize);
|
mods.set_len(num as usize);
|
||||||
ext_only.set_len(num as usize);
|
ext_only.set_len(num as usize);
|
||||||
let mut res = AHashMap::new();
|
let mut res = IndexMap::new();
|
||||||
for (modifier, ext_only) in mods.iter().copied().zip(ext_only.iter().copied()) {
|
for (modifier, ext_only) in mods.iter().copied().zip(ext_only.iter().copied()) {
|
||||||
res.insert(
|
res.insert(
|
||||||
modifier as _,
|
modifier as _,
|
||||||
GfxModifier {
|
EglModifier {
|
||||||
modifier: modifier as _,
|
modifier: modifier as _,
|
||||||
external_only: ext_only == EGL_TRUE,
|
external_only: ext_only == EGL_TRUE,
|
||||||
},
|
},
|
||||||
|
|
@ -309,7 +359,7 @@ unsafe fn query_modifiers(
|
||||||
}
|
}
|
||||||
res.insert(
|
res.insert(
|
||||||
INVALID_MODIFIER,
|
INVALID_MODIFIER,
|
||||||
GfxModifier {
|
EglModifier {
|
||||||
modifier: INVALID_MODIFIER,
|
modifier: INVALID_MODIFIER,
|
||||||
external_only,
|
external_only,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::gfx_apis::gl::egl::{
|
crate::{
|
||||||
display::EglDisplay,
|
format::Format,
|
||||||
sys::{EGLImageKHR, EGL_FALSE},
|
gfx_apis::gl::egl::{
|
||||||
PROCS,
|
display::EglDisplay,
|
||||||
|
sys::{EGLImageKHR, EGL_FALSE},
|
||||||
|
PROCS,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
@ -13,6 +16,7 @@ pub struct EglImage {
|
||||||
pub width: i32,
|
pub width: i32,
|
||||||
pub height: i32,
|
pub height: i32,
|
||||||
pub external_only: bool,
|
pub external_only: bool,
|
||||||
|
pub format: &'static Format,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for EglImage {
|
impl Drop for EglImage {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct GlFrameBuffer {
|
pub struct GlFrameBuffer {
|
||||||
pub _rb: Option<Rc<GlRenderBuffer>>,
|
pub rb: Rc<GlRenderBuffer>,
|
||||||
pub _tex: Option<Rc<GlTexture>>,
|
pub _tex: Option<Rc<GlTexture>>,
|
||||||
pub ctx: Rc<EglContext>,
|
pub ctx: Rc<EglContext>,
|
||||||
pub width: i32,
|
pub width: i32,
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ impl GlRenderBuffer {
|
||||||
let status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
let status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
let fb = GlFrameBuffer {
|
let fb = GlFrameBuffer {
|
||||||
_rb: Some(self.clone()),
|
rb: self.clone(),
|
||||||
_tex: None,
|
_tex: None,
|
||||||
ctx: self.ctx.clone(),
|
ctx: self.ctx.clone(),
|
||||||
fbo,
|
fbo,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ pub struct GlTexture {
|
||||||
pub width: i32,
|
pub width: i32,
|
||||||
pub height: i32,
|
pub height: i32,
|
||||||
pub external_only: bool,
|
pub external_only: bool,
|
||||||
|
pub format: &'static Format,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn image_target(external_only: bool) -> GLenum {
|
pub fn image_target(external_only: bool) -> GLenum {
|
||||||
|
|
@ -58,6 +59,7 @@ impl GlTexture {
|
||||||
width: img.width,
|
width: img.width,
|
||||||
height: img.height,
|
height: img.height,
|
||||||
external_only: img.external_only,
|
external_only: img.external_only,
|
||||||
|
format: img.format,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,6 +103,7 @@ impl GlTexture {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
external_only: false,
|
external_only: false,
|
||||||
|
format,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,14 @@ use {
|
||||||
renderer::{framebuffer::Framebuffer, image::Image},
|
renderer::{framebuffer::Framebuffer, image::Image},
|
||||||
GfxGlState, RenderError, Texture,
|
GfxGlState, RenderError, Texture,
|
||||||
},
|
},
|
||||||
video::{
|
video::{dmabuf::DmaBuf, drm::Drm, gbm::GbmDevice},
|
||||||
dmabuf::DmaBuf,
|
|
||||||
drm::{Drm, NodeType},
|
|
||||||
gbm::GbmDevice,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
jay_config::video::GfxApi,
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
ffi::CString,
|
ffi::CString,
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
mem,
|
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
},
|
},
|
||||||
uapi::ustr,
|
uapi::ustr,
|
||||||
|
|
@ -82,19 +78,11 @@ impl GlRenderContext {
|
||||||
self.ctx.reset_status()
|
self.ctx.reset_status()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn supports_external_texture(&self) -> bool {
|
|
||||||
self.ctx.ext.contains(GlExt::GL_OES_EGL_IMAGE_EXTERNAL)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(in crate::gfx_apis::gl) fn from_drm_device(drm: &Drm) -> Result<Self, RenderError> {
|
pub(in crate::gfx_apis::gl) fn from_drm_device(drm: &Drm) -> Result<Self, RenderError> {
|
||||||
let nodes = drm.get_nodes()?;
|
let node = drm
|
||||||
let node = match nodes
|
.get_render_node()?
|
||||||
.get(&NodeType::Render)
|
.ok_or(RenderError::NoRenderNode)
|
||||||
.or_else(|| nodes.get(&NodeType::Primary))
|
.map(Rc::new)?;
|
||||||
{
|
|
||||||
None => return Err(RenderError::NoRenderNode),
|
|
||||||
Some(path) => Rc::new(path.to_owned()),
|
|
||||||
};
|
|
||||||
let dpy = EglDisplay::create(drm)?;
|
let dpy = EglDisplay::create(drm)?;
|
||||||
if !dpy.formats.contains_key(&XRGB8888.drm) {
|
if !dpy.formats.contains_key(&XRGB8888.drm) {
|
||||||
return Err(RenderError::XRGB888);
|
return Err(RenderError::XRGB888);
|
||||||
|
|
@ -161,7 +149,7 @@ impl GlRenderContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>> {
|
pub fn formats(&self) -> Rc<AHashMap<u32, GfxFormat>> {
|
||||||
self.ctx.dpy.formats.clone()
|
self.ctx.formats.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dmabuf_fb(self: &Rc<Self>, buf: &DmaBuf) -> Result<Rc<Framebuffer>, RenderError> {
|
fn dmabuf_fb(self: &Rc<Self>, buf: &DmaBuf) -> Result<Rc<Framebuffer>, RenderError> {
|
||||||
|
|
@ -203,18 +191,10 @@ impl GlRenderContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GfxContext for GlRenderContext {
|
impl GfxContext for GlRenderContext {
|
||||||
fn take_render_ops(&self) -> Vec<GfxApiOpt> {
|
|
||||||
mem::take(&mut self.gfx_ops.borrow_mut())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset_status(&self) -> Option<ResetStatus> {
|
fn reset_status(&self) -> Option<ResetStatus> {
|
||||||
self.reset_status()
|
self.reset_status()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supports_external_texture(&self) -> bool {
|
|
||||||
self.supports_external_texture()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_node(&self) -> Rc<CString> {
|
fn render_node(&self) -> Rc<CString> {
|
||||||
self.render_node()
|
self.render_node()
|
||||||
}
|
}
|
||||||
|
|
@ -239,6 +219,7 @@ impl GfxContext for GlRenderContext {
|
||||||
|
|
||||||
fn shmem_texture(
|
fn shmem_texture(
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
|
_old: Option<Rc<dyn GfxTexture>>,
|
||||||
data: &[Cell<u8>],
|
data: &[Cell<u8>],
|
||||||
format: &'static Format,
|
format: &'static Format,
|
||||||
width: i32,
|
width: i32,
|
||||||
|
|
@ -254,4 +235,8 @@ impl GfxContext for GlRenderContext {
|
||||||
fn gbm(&self) -> &GbmDevice {
|
fn gbm(&self) -> &GbmDevice {
|
||||||
&self.gbm
|
&self.gbm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gfx_api(&self) -> GfxApi {
|
||||||
|
GfxApi::OpenGl
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cursor::Cursor,
|
format::Format,
|
||||||
fixed::Fixed,
|
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer},
|
||||||
format::{Format, ARGB8888, XRGB8888},
|
|
||||||
gfx_api::{GfxFramebuffer, GfxTexture},
|
|
||||||
gfx_apis::gl::{
|
gfx_apis::gl::{
|
||||||
gl::{
|
gl::{
|
||||||
frame_buffer::GlFrameBuffer,
|
frame_buffer::GlFrameBuffer,
|
||||||
|
|
@ -16,16 +14,13 @@ use {
|
||||||
run_ops,
|
run_ops,
|
||||||
sys::{glBlendFunc, glFlush, glReadnPixels, GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
|
sys::{glBlendFunc, glFlush, glReadnPixels, GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
|
||||||
},
|
},
|
||||||
rect::Rect,
|
theme::Color,
|
||||||
renderer::{renderer_base::RendererBase, RenderResult, Renderer},
|
|
||||||
scale::Scale,
|
|
||||||
state::State,
|
|
||||||
tree::Node,
|
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
|
mem,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -42,72 +37,6 @@ impl Debug for Framebuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Framebuffer {
|
impl Framebuffer {
|
||||||
pub fn clear(&self) {
|
|
||||||
self.clear_with(0.0, 0.0, 0.0, 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_with(&self, r: f32, g: f32, b: f32, a: f32) {
|
|
||||||
let _ = self.ctx.ctx.with_current(|| {
|
|
||||||
unsafe {
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
|
|
||||||
glViewport(0, 0, self.gl.width, self.gl.height);
|
|
||||||
glClearColor(r, g, b, a);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_texture(
|
|
||||||
&self,
|
|
||||||
state: &State,
|
|
||||||
texture: &Rc<dyn GfxTexture>,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
alpha: bool,
|
|
||||||
) {
|
|
||||||
let mut ops = self.ctx.gfx_ops.borrow_mut();
|
|
||||||
ops.clear();
|
|
||||||
let scale = Scale::from_int(1);
|
|
||||||
let extents = Rect::new_sized(0, 0, self.gl.width, self.gl.height).unwrap();
|
|
||||||
let mut renderer = Renderer {
|
|
||||||
base: RendererBase {
|
|
||||||
ops: &mut ops,
|
|
||||||
scaled: false,
|
|
||||||
scale,
|
|
||||||
scalef: 1.0,
|
|
||||||
},
|
|
||||||
state,
|
|
||||||
on_output: false,
|
|
||||||
result: &mut RenderResult::default(),
|
|
||||||
logical_extents: extents,
|
|
||||||
physical_extents: extents,
|
|
||||||
};
|
|
||||||
let format = match alpha {
|
|
||||||
true => ARGB8888,
|
|
||||||
false => XRGB8888,
|
|
||||||
};
|
|
||||||
renderer
|
|
||||||
.base
|
|
||||||
.render_texture(texture, x, y, format, None, None, scale, i32::MAX, i32::MAX);
|
|
||||||
let _ = self.ctx.ctx.with_current(|| {
|
|
||||||
unsafe {
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
|
|
||||||
glViewport(0, 0, self.gl.width, self.gl.height);
|
|
||||||
if alpha {
|
|
||||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
}
|
|
||||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
}
|
|
||||||
run_ops(self, &ops);
|
|
||||||
unsafe {
|
|
||||||
glFlush();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_to_shm(
|
pub fn copy_to_shm(
|
||||||
&self,
|
&self,
|
||||||
x: i32,
|
x: i32,
|
||||||
|
|
@ -137,121 +66,15 @@ impl Framebuffer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_custom(&self, scale: Scale, f: &mut dyn FnMut(&mut RendererBase)) {
|
pub fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) {
|
||||||
let mut ops = self.ctx.gfx_ops.borrow_mut();
|
|
||||||
ops.clear();
|
|
||||||
let mut renderer = RendererBase {
|
|
||||||
ops: &mut ops,
|
|
||||||
scaled: scale != 1,
|
|
||||||
scale,
|
|
||||||
scalef: scale.to_f64(),
|
|
||||||
};
|
|
||||||
f(&mut renderer);
|
|
||||||
let _ = self.ctx.ctx.with_current(|| {
|
let _ = self.ctx.ctx.with_current(|| {
|
||||||
unsafe {
|
unsafe {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
|
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
|
||||||
glViewport(0, 0, self.gl.width, self.gl.height);
|
glViewport(0, 0, self.gl.width, self.gl.height);
|
||||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
if let Some(c) = clear {
|
||||||
}
|
glClearColor(c.r, c.g, c.b, c.a);
|
||||||
run_ops(self, &ops);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
unsafe {
|
|
||||||
glFlush();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(
|
|
||||||
&self,
|
|
||||||
node: &dyn Node,
|
|
||||||
state: &State,
|
|
||||||
cursor_rect: Option<Rect>,
|
|
||||||
on_output: bool,
|
|
||||||
result: &mut RenderResult,
|
|
||||||
scale: Scale,
|
|
||||||
render_hardware_cursor: bool,
|
|
||||||
) {
|
|
||||||
let mut ops = self.ctx.gfx_ops.borrow_mut();
|
|
||||||
ops.clear();
|
|
||||||
let mut renderer = Renderer {
|
|
||||||
base: RendererBase {
|
|
||||||
ops: &mut ops,
|
|
||||||
scaled: scale != 1,
|
|
||||||
scale,
|
|
||||||
scalef: scale.to_f64(),
|
|
||||||
},
|
|
||||||
state,
|
|
||||||
on_output,
|
|
||||||
result,
|
|
||||||
logical_extents: node.node_absolute_position().at_point(0, 0),
|
|
||||||
physical_extents: Rect::new(0, 0, self.gl.width, self.gl.height).unwrap(),
|
|
||||||
};
|
|
||||||
node.node_render(&mut renderer, 0, 0, i32::MAX, i32::MAX);
|
|
||||||
if let Some(rect) = cursor_rect {
|
|
||||||
let seats = state.globals.lock_seats();
|
|
||||||
for seat in seats.values() {
|
|
||||||
if let Some(cursor) = seat.get_cursor() {
|
|
||||||
let (mut x, mut y) = seat.get_position();
|
|
||||||
if let Some(dnd_icon) = seat.dnd_icon() {
|
|
||||||
let extents = dnd_icon.extents.get().move_(
|
|
||||||
x.round_down() + dnd_icon.buf_x.get(),
|
|
||||||
y.round_down() + dnd_icon.buf_y.get(),
|
|
||||||
);
|
|
||||||
if extents.intersects(&rect) {
|
|
||||||
let (x, y) = rect.translate(extents.x1(), extents.y1());
|
|
||||||
renderer.render_surface(&dnd_icon, x, y, i32::MAX, i32::MAX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if render_hardware_cursor || !seat.hardware_cursor() {
|
|
||||||
cursor.tick();
|
|
||||||
x -= Fixed::from_int(rect.x1());
|
|
||||||
y -= Fixed::from_int(rect.y1());
|
|
||||||
cursor.render(&mut renderer, x, y);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
let _ = self.ctx.ctx.with_current(|| {
|
|
||||||
let c = state.theme.colors.background.get();
|
|
||||||
unsafe {
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
|
|
||||||
glViewport(0, 0, self.gl.width, self.gl.height);
|
|
||||||
glClearColor(c.r, c.g, c.b, 1.0);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
}
|
|
||||||
run_ops(self, &ops);
|
|
||||||
unsafe {
|
|
||||||
glFlush();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_hardware_cursor(&self, cursor: &dyn Cursor, state: &State, scale: Scale) {
|
|
||||||
let mut ops = self.ctx.gfx_ops.borrow_mut();
|
|
||||||
ops.clear();
|
|
||||||
let mut res = RenderResult::default();
|
|
||||||
let mut renderer = Renderer {
|
|
||||||
base: RendererBase {
|
|
||||||
ops: &mut ops,
|
|
||||||
scaled: scale != 1,
|
|
||||||
scale,
|
|
||||||
scalef: scale.to_f64(),
|
|
||||||
},
|
|
||||||
state,
|
|
||||||
on_output: false,
|
|
||||||
result: &mut res,
|
|
||||||
logical_extents: Rect::new_empty(0, 0),
|
|
||||||
physical_extents: Rect::new(0, 0, self.gl.width, self.gl.height).unwrap(),
|
|
||||||
};
|
|
||||||
cursor.render_hardware_cursor(&mut renderer);
|
|
||||||
let _ = self.ctx.ctx.with_current(|| {
|
|
||||||
unsafe {
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
|
|
||||||
glViewport(0, 0, self.gl.width, self.gl.height);
|
|
||||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
}
|
}
|
||||||
run_ops(self, &ops);
|
run_ops(self, &ops);
|
||||||
|
|
@ -260,6 +83,7 @@ impl Framebuffer {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
*self.ctx.gfx_ops.borrow_mut() = ops;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,23 +92,18 @@ impl GfxFramebuffer for Framebuffer {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&self) {
|
fn take_render_ops(&self) -> Vec<GfxApiOpt> {
|
||||||
self.clear()
|
let mut ops = mem::take(&mut *self.ctx.gfx_ops.borrow_mut());
|
||||||
|
ops.clear();
|
||||||
|
ops
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_with(&self, r: f32, g: f32, b: f32, a: f32) {
|
fn size(&self) -> (i32, i32) {
|
||||||
self.clear_with(r, g, b, a)
|
(self.gl.width, self.gl.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_texture(
|
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) {
|
||||||
&self,
|
self.render(ops, clear);
|
||||||
state: &State,
|
|
||||||
texture: &Rc<dyn GfxTexture>,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
alpha: bool,
|
|
||||||
) {
|
|
||||||
self.copy_texture(state, texture, x, y, alpha)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_to_shm(
|
fn copy_to_shm(
|
||||||
|
|
@ -295,36 +114,12 @@ impl GfxFramebuffer for Framebuffer {
|
||||||
height: i32,
|
height: i32,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
shm: &[Cell<u8>],
|
shm: &[Cell<u8>],
|
||||||
) {
|
) -> Result<(), GfxError> {
|
||||||
self.copy_to_shm(x, y, width, height, format, shm)
|
self.copy_to_shm(x, y, width, height, format, shm);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_custom(&self, scale: Scale, f: &mut dyn FnMut(&mut RendererBase)) {
|
fn format(&self) -> &'static Format {
|
||||||
self.render_custom(scale, f)
|
self.gl.rb.img.format
|
||||||
}
|
|
||||||
|
|
||||||
fn render(
|
|
||||||
&self,
|
|
||||||
node: &dyn Node,
|
|
||||||
state: &State,
|
|
||||||
cursor_rect: Option<Rect>,
|
|
||||||
on_output: bool,
|
|
||||||
result: &mut RenderResult,
|
|
||||||
scale: Scale,
|
|
||||||
render_hardware_cursor: bool,
|
|
||||||
) {
|
|
||||||
self.render(
|
|
||||||
node,
|
|
||||||
state,
|
|
||||||
cursor_rect,
|
|
||||||
on_output,
|
|
||||||
result,
|
|
||||||
scale,
|
|
||||||
render_hardware_cursor,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_hardware_cursor(&self, cursor: &dyn Cursor, state: &State, scale: Scale) {
|
|
||||||
self.render_hardware_cursor(cursor, state, scale)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
gfx_api::GfxTexture,
|
format::Format,
|
||||||
gfx_apis::gl::{gl::texture::GlTexture, renderer::context::GlRenderContext},
|
gfx_api::{GfxError, GfxTexture},
|
||||||
|
gfx_apis::gl::{gl::texture::GlTexture, renderer::context::GlRenderContext, RenderError},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
|
cell::Cell,
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
},
|
},
|
||||||
|
|
@ -32,15 +34,28 @@ impl Texture {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GfxTexture for Texture {
|
impl GfxTexture for Texture {
|
||||||
fn width(&self) -> i32 {
|
fn size(&self) -> (i32, i32) {
|
||||||
self.width()
|
(self.width(), self.height())
|
||||||
}
|
|
||||||
|
|
||||||
fn height(&self) -> i32 {
|
|
||||||
self.height()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
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
266
src/gfx_apis/vulkan.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
128
src/gfx_apis/vulkan/allocator.rs
Normal file
128
src/gfx_apis/vulkan/allocator.rs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/gfx_apis/vulkan/command.rs
Normal file
69
src/gfx_apis/vulkan/command.rs
Normal 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,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/gfx_apis/vulkan/descriptor.rs
Normal file
49
src/gfx_apis/vulkan/descriptor.rs
Normal 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(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
343
src/gfx_apis/vulkan/device.rs
Normal file
343
src/gfx_apis/vulkan/device.rs
Normal 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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/gfx_apis/vulkan/fence.rs
Normal file
49
src/gfx_apis/vulkan/fence.rs
Normal 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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
277
src/gfx_apis/vulkan/format.rs
Normal file
277
src/gfx_apis/vulkan/format.rs
Normal 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,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
582
src/gfx_apis/vulkan/image.rs
Normal file
582
src/gfx_apis/vulkan/image.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
228
src/gfx_apis/vulkan/instance.rs
Normal file
228
src/gfx_apis/vulkan/instance.rs
Normal 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;
|
||||||
182
src/gfx_apis/vulkan/pipeline.rs
Normal file
182
src/gfx_apis/vulkan/pipeline.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1019
src/gfx_apis/vulkan/renderer.rs
Normal file
1019
src/gfx_apis/vulkan/renderer.rs
Normal file
File diff suppressed because it is too large
Load diff
42
src/gfx_apis/vulkan/sampler.rs
Normal file
42
src/gfx_apis/vulkan/sampler.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/gfx_apis/vulkan/semaphore.rs
Normal file
54
src/gfx_apis/vulkan/semaphore.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/gfx_apis/vulkan/shaders.rs
Normal file
67
src/gfx_apis/vulkan/shaders.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/gfx_apis/vulkan/shaders/fill.frag
Normal file
11
src/gfx_apis/vulkan/shaders/fill.frag
Normal 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;
|
||||||
|
}
|
||||||
18
src/gfx_apis/vulkan/shaders/fill.vert
Normal file
18
src/gfx_apis/vulkan/shaders/fill.vert
Normal 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);
|
||||||
|
}
|
||||||
9
src/gfx_apis/vulkan/shaders/tex.frag
Normal file
9
src/gfx_apis/vulkan/shaders/tex.frag
Normal 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);
|
||||||
|
}
|
||||||
21
src/gfx_apis/vulkan/shaders/tex.vert
Normal file
21
src/gfx_apis/vulkan/shaders/tex.vert
Normal 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);
|
||||||
|
}
|
||||||
103
src/gfx_apis/vulkan/staging.rs
Normal file
103
src/gfx_apis/vulkan/staging.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/gfx_apis/vulkan/util.rs
Normal file
17
src/gfx_apis/vulkan/util.rs
Normal 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)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -143,6 +143,7 @@ impl JayCompositor {
|
||||||
dmabuf.height,
|
dmabuf.height,
|
||||||
plane.offset,
|
plane.offset,
|
||||||
plane.stride,
|
plane.stride,
|
||||||
|
dmabuf.modifier,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,13 @@ use {
|
||||||
video::{
|
video::{
|
||||||
dmabuf::DmaBuf,
|
dmabuf::DmaBuf,
|
||||||
gbm::{GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
|
gbm::{GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
|
||||||
ModifiedFormat, INVALID_MODIFIER,
|
Modifier, INVALID_MODIFIER, LINEAR_MODIFIER,
|
||||||
},
|
},
|
||||||
wire::{jay_screencast::*, JayScreencastId},
|
wire::{jay_screencast::*, JayScreencastId},
|
||||||
},
|
},
|
||||||
ahash::AHashSet,
|
ahash::AHashSet,
|
||||||
|
indexmap::{indexset, IndexSet},
|
||||||
|
once_cell::sync::Lazy,
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
|
|
@ -163,9 +165,7 @@ impl JayScreencast {
|
||||||
let mut buffer = self.buffers.borrow_mut();
|
let mut buffer = self.buffers.borrow_mut();
|
||||||
for (idx, buffer) in buffer.deref_mut().iter_mut().enumerate() {
|
for (idx, buffer) in buffer.deref_mut().iter_mut().enumerate() {
|
||||||
if buffer.free {
|
if buffer.free {
|
||||||
buffer
|
buffer.fb.copy_texture(texture, 0, 0);
|
||||||
.fb
|
|
||||||
.copy_texture(&self.client.state, texture, 0, 0, false);
|
|
||||||
self.client.event(Ready {
|
self.client.event(Ready {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
idx: idx as _,
|
idx: idx as _,
|
||||||
|
|
@ -195,21 +195,37 @@ impl JayScreencast {
|
||||||
|
|
||||||
pub fn realloc(&self, ctx: &Rc<dyn GfxContext>) -> Result<(), JayScreencastError> {
|
pub fn realloc(&self, ctx: &Rc<dyn GfxContext>) -> Result<(), JayScreencastError> {
|
||||||
let mut buffers = vec![];
|
let mut buffers = vec![];
|
||||||
|
let formats = ctx.formats();
|
||||||
|
let format = match formats.get(&XRGB8888.drm) {
|
||||||
|
Some(f) => f,
|
||||||
|
_ => return Err(JayScreencastError::XRGB8888),
|
||||||
|
};
|
||||||
if let Some(output) = self.output.get() {
|
if let Some(output) = self.output.get() {
|
||||||
let mode = output.global.mode.get();
|
let mode = output.global.mode.get();
|
||||||
let num = 3;
|
let num = 3;
|
||||||
for _ in 0..num {
|
for _ in 0..num {
|
||||||
let format = ModifiedFormat {
|
let mut usage = GBM_BO_USE_RENDERING;
|
||||||
format: XRGB8888,
|
let modifiers = match self.linear.get() {
|
||||||
modifier: INVALID_MODIFIER,
|
true if format.write_modifiers.contains(&LINEAR_MODIFIER) => {
|
||||||
|
static MODS: Lazy<IndexSet<Modifier>> =
|
||||||
|
Lazy::new(|| indexset![LINEAR_MODIFIER]);
|
||||||
|
&MODS
|
||||||
|
}
|
||||||
|
true if format.write_modifiers.contains(&INVALID_MODIFIER) => {
|
||||||
|
usage |= GBM_BO_USE_LINEAR;
|
||||||
|
static MODS: Lazy<IndexSet<Modifier>> =
|
||||||
|
Lazy::new(|| indexset![INVALID_MODIFIER]);
|
||||||
|
&MODS
|
||||||
|
}
|
||||||
|
true => return Err(JayScreencastError::Modifier),
|
||||||
|
false if format.write_modifiers.is_empty() => {
|
||||||
|
return Err(JayScreencastError::XRGB8888Writing)
|
||||||
|
}
|
||||||
|
false => &format.write_modifiers,
|
||||||
};
|
};
|
||||||
let mut flags = GBM_BO_USE_RENDERING;
|
let buffer =
|
||||||
if self.linear.get() {
|
ctx.gbm()
|
||||||
flags |= GBM_BO_USE_LINEAR;
|
.create_bo(mode.width, mode.height, XRGB8888, modifiers, usage)?;
|
||||||
}
|
|
||||||
let buffer = ctx
|
|
||||||
.gbm()
|
|
||||||
.create_bo(mode.width, mode.height, &format, flags)?;
|
|
||||||
let fb = ctx.clone().dmabuf_img(buffer.dmabuf())?.to_framebuffer()?;
|
let fb = ctx.clone().dmabuf_img(buffer.dmabuf())?.to_framebuffer()?;
|
||||||
buffers.push(ScreencastBuffer {
|
buffers.push(ScreencastBuffer {
|
||||||
dmabuf: buffer.dmabuf().clone(),
|
dmabuf: buffer.dmabuf().clone(),
|
||||||
|
|
@ -443,6 +459,12 @@ pub enum JayScreencastError {
|
||||||
GbmError(#[from] GbmError),
|
GbmError(#[from] GbmError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
GfxError(#[from] GfxError),
|
GfxError(#[from] GfxError),
|
||||||
|
#[error("Render context does not support XRGB8888 format")]
|
||||||
|
XRGB8888,
|
||||||
|
#[error("Render context does not support XRGB8888 format for rendering")]
|
||||||
|
XRGB8888Writing,
|
||||||
|
#[error("Render context supports neither linear or invalid modifier")]
|
||||||
|
Modifier,
|
||||||
}
|
}
|
||||||
efrom!(JayScreencastError, MsgParserError);
|
efrom!(JayScreencastError, MsgParserError);
|
||||||
efrom!(JayScreencastError, ClientError);
|
efrom!(JayScreencastError, ClientError);
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ impl JayScreenshot {
|
||||||
height: i32,
|
height: i32,
|
||||||
offset: u32,
|
offset: u32,
|
||||||
stride: u32,
|
stride: u32,
|
||||||
|
modifier: u64,
|
||||||
) {
|
) {
|
||||||
self.client.event(Dmabuf {
|
self.client.event(Dmabuf {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
|
|
@ -33,6 +34,8 @@ impl JayScreenshot {
|
||||||
height: height as _,
|
height: height as _,
|
||||||
offset,
|
offset,
|
||||||
stride,
|
stride,
|
||||||
|
modifier_lo: modifier as u32,
|
||||||
|
modifier_hi: (modifier >> 32) as u32,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -149,8 +149,15 @@ impl WlBuffer {
|
||||||
if self.render_ctx_version.replace(ctx_version) == ctx_version {
|
if self.render_ctx_version.replace(ctx_version) == ctx_version {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.texture.set(None);
|
let had_texture = self.texture.set(None).is_some();
|
||||||
self.famebuffer.set(None);
|
self.famebuffer.set(None);
|
||||||
|
self.reset_storage_after_gfx_context_change();
|
||||||
|
if had_texture {
|
||||||
|
self.update_texture_or_log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_storage_after_gfx_context_change(&self) {
|
||||||
let mut storage = self.storage.borrow_mut();
|
let mut storage = self.storage.borrow_mut();
|
||||||
if let Some(storage) = &mut *storage {
|
if let Some(storage) = &mut *storage {
|
||||||
if let WlBufferStorage::Shm { .. } = storage {
|
if let WlBufferStorage::Shm { .. } = storage {
|
||||||
|
|
@ -167,7 +174,7 @@ impl WlBuffer {
|
||||||
Ok(image) => image,
|
Ok(image) => image,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!(
|
log::error!(
|
||||||
"Cannot re-import wl_buffer after graphics context reset: {}",
|
"Cannot re-import wl_buffer after graphics context change: {}",
|
||||||
ErrorFmt(e)
|
ErrorFmt(e)
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|
@ -177,7 +184,13 @@ impl WlBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_texture(&self) -> Result<(), WlBufferError> {
|
pub fn update_texture_or_log(&self) {
|
||||||
|
if let Err(e) = self.update_texture() {
|
||||||
|
log::warn!("Could not update texture: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_texture(&self) -> Result<(), WlBufferError> {
|
||||||
let storage = self.storage.borrow_mut();
|
let storage = self.storage.borrow_mut();
|
||||||
let storage = match storage.deref() {
|
let storage = match storage.deref() {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
|
|
@ -185,10 +198,10 @@ impl WlBuffer {
|
||||||
};
|
};
|
||||||
match storage {
|
match storage {
|
||||||
WlBufferStorage::Shm { mem, stride } => {
|
WlBufferStorage::Shm { mem, stride } => {
|
||||||
self.texture.set(None);
|
let old = self.texture.take();
|
||||||
if let Some(ctx) = self.client.state.render_ctx.get() {
|
if let Some(ctx) = self.client.state.render_ctx.get() {
|
||||||
let tex = mem.access(|mem| {
|
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));
|
self.texture.set(Some(tex));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use {
|
||||||
object::Object,
|
object::Object,
|
||||||
utils::buffd::{MsgParser, MsgParserError},
|
utils::buffd::{MsgParser, MsgParserError},
|
||||||
video::{
|
video::{
|
||||||
dmabuf::{DmaBuf, DmaBufPlane},
|
dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
|
||||||
INVALID_MODIFIER,
|
INVALID_MODIFIER,
|
||||||
},
|
},
|
||||||
wire::{wl_drm::*, WlDrmId},
|
wire::{wl_drm::*, WlDrmId},
|
||||||
|
|
@ -123,7 +123,7 @@ impl WlDrm {
|
||||||
height: req.height,
|
height: req.height,
|
||||||
format,
|
format,
|
||||||
modifier: INVALID_MODIFIER,
|
modifier: INVALID_MODIFIER,
|
||||||
planes: vec![],
|
planes: PlaneVec::new(),
|
||||||
};
|
};
|
||||||
if req.stride0 > 0 {
|
if req.stride0 > 0 {
|
||||||
dmabuf.planes.push(DmaBufPlane {
|
dmabuf.planes.push(DmaBufPlane {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ use {
|
||||||
buffd::{MsgParser, MsgParserError},
|
buffd::{MsgParser, MsgParserError},
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
|
errorfmt::ErrorFmt,
|
||||||
linkedlist::LinkedList,
|
linkedlist::LinkedList,
|
||||||
},
|
},
|
||||||
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
|
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
|
||||||
|
|
@ -220,8 +221,10 @@ impl WlOutputGlobal {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let rect = capture.rect;
|
let rect = capture.rect;
|
||||||
if let Some(WlBufferStorage::Shm { mem, .. }) = wl_buffer.storage.borrow_mut().deref() {
|
if let Some(WlBufferStorage::Shm { mem, stride }) =
|
||||||
let res = mem.access(|mem| {
|
wl_buffer.storage.borrow_mut().deref()
|
||||||
|
{
|
||||||
|
let acc = mem.access(|mem| {
|
||||||
fb.copy_to_shm(
|
fb.copy_to_shm(
|
||||||
rect.x1(),
|
rect.x1(),
|
||||||
rect.y1(),
|
rect.y1(),
|
||||||
|
|
@ -229,10 +232,39 @@ impl WlOutputGlobal {
|
||||||
rect.height(),
|
rect.height(),
|
||||||
XRGB8888,
|
XRGB8888,
|
||||||
mem,
|
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 {
|
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);
|
// capture.send_flags(FLAGS_Y_INVERT);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -244,13 +276,7 @@ impl WlOutputGlobal {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fb.copy_texture(
|
fb.copy_texture(tex, -capture.rect.x1(), -capture.rect.y1());
|
||||||
&self.state,
|
|
||||||
tex,
|
|
||||||
-capture.rect.x1(),
|
|
||||||
-capture.rect.y1(),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if capture.with_damage.get() {
|
if capture.with_damage.get() {
|
||||||
capture.send_damage();
|
capture.send_damage();
|
||||||
|
|
|
||||||
|
|
@ -766,7 +766,7 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(buffer) = buffer_change {
|
if let Some(buffer) = buffer_change {
|
||||||
let _ = buffer.update_texture();
|
buffer.update_texture_or_log();
|
||||||
self.buffer.set(Some(buffer));
|
self.buffer.set(Some(buffer));
|
||||||
self.buf_x.fetch_add(dx);
|
self.buf_x.fetch_add(dx);
|
||||||
self.buf_y.fetch_add(dy);
|
self.buf_y.fetch_add(dy);
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use {
|
||||||
buffd::{MsgParser, MsgParserError},
|
buffd::{MsgParser, MsgParserError},
|
||||||
errorfmt::ErrorFmt,
|
errorfmt::ErrorFmt,
|
||||||
},
|
},
|
||||||
video::dmabuf::{DmaBuf, DmaBufPlane},
|
video::dmabuf::{DmaBuf, DmaBufPlane, PlaneVec, MAX_PLANES},
|
||||||
wire::{zwp_linux_buffer_params_v1::*, WlBufferId, ZwpLinuxBufferParamsV1Id},
|
wire::{zwp_linux_buffer_params_v1::*, WlBufferId, ZwpLinuxBufferParamsV1Id},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
|
@ -27,7 +27,7 @@ const INTERLACED: u32 = 2;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const BOTTOM_FIRST: u32 = 4;
|
const BOTTOM_FIRST: u32 = 4;
|
||||||
|
|
||||||
const MAX_PLANE: u32 = 3;
|
const MAX_PLANE: u32 = MAX_PLANES as u32 - 1;
|
||||||
|
|
||||||
pub struct ZwpLinuxBufferParamsV1 {
|
pub struct ZwpLinuxBufferParamsV1 {
|
||||||
pub id: ZwpLinuxBufferParamsV1Id,
|
pub id: ZwpLinuxBufferParamsV1Id,
|
||||||
|
|
@ -110,7 +110,7 @@ impl ZwpLinuxBufferParamsV1 {
|
||||||
Some(m) => m,
|
Some(m) => m,
|
||||||
_ => return Err(ZwpLinuxBufferParamsV1Error::NoPlanes),
|
_ => return Err(ZwpLinuxBufferParamsV1Error::NoPlanes),
|
||||||
};
|
};
|
||||||
if !format.modifiers.contains_key(&modifier) {
|
if !format.read_modifiers.contains(&modifier) {
|
||||||
return Err(ZwpLinuxBufferParamsV1Error::InvalidModifier(modifier));
|
return Err(ZwpLinuxBufferParamsV1Error::InvalidModifier(modifier));
|
||||||
}
|
}
|
||||||
let mut dmabuf = DmaBuf {
|
let mut dmabuf = DmaBuf {
|
||||||
|
|
@ -118,7 +118,7 @@ impl ZwpLinuxBufferParamsV1 {
|
||||||
height,
|
height,
|
||||||
format: format.format,
|
format: format.format,
|
||||||
modifier,
|
modifier,
|
||||||
planes: vec![],
|
planes: PlaneVec::new(),
|
||||||
};
|
};
|
||||||
let mut planes: Vec<_> = self.planes.borrow_mut().drain().map(|v| v.1).collect();
|
let mut planes: Vec<_> = self.planes.borrow_mut().drain().map(|v| v.1).collect();
|
||||||
planes.sort_by_key(|a| a.plane_idx);
|
planes.sort_by_key(|a| a.plane_idx);
|
||||||
|
|
|
||||||
|
|
@ -42,16 +42,10 @@ impl ZwpLinuxDmabufV1Global {
|
||||||
if let Some(ctx) = client.state.render_ctx.get() {
|
if let Some(ctx) = client.state.render_ctx.get() {
|
||||||
let formats = ctx.formats();
|
let formats = ctx.formats();
|
||||||
for format in formats.values() {
|
for format in formats.values() {
|
||||||
if format.implicit_external_only && !ctx.supports_external_texture() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
obj.send_format(format.format.drm);
|
obj.send_format(format.format.drm);
|
||||||
if version >= MODIFIERS_SINCE_VERSION {
|
if version >= MODIFIERS_SINCE_VERSION {
|
||||||
for modifier in format.modifiers.values() {
|
for &modifier in &format.read_modifiers {
|
||||||
if modifier.external_only && !ctx.supports_external_texture() {
|
obj.send_modifier(format.format.drm, modifier);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
obj.send_modifier(format.format.drm, modifier.modifier);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ use {
|
||||||
compositor::TestFuture,
|
compositor::TestFuture,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::GfxError,
|
gfx_api::GfxError,
|
||||||
gfx_apis::create_gfx_context,
|
|
||||||
it::test_error::TestResult,
|
it::test_error::TestResult,
|
||||||
state::State,
|
state::State,
|
||||||
time::now_usec,
|
time::now_usec,
|
||||||
|
|
@ -178,7 +177,7 @@ impl TestBackend {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let drm = Drm::open_existing(file);
|
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,
|
Ok(ctx) => ctx,
|
||||||
Err(e) => return Err(TestBackendError::RenderContext(e)),
|
Err(e) => return Err(TestBackendError::RenderContext(e)),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
c_variadic, // https://github.com/rust-lang/rust/issues/44930
|
c_variadic, // https://github.com/rust-lang/rust/issues/44930
|
||||||
thread_local, // https://github.com/rust-lang/rust/issues/29594
|
thread_local, // https://github.com/rust-lang/rust/issues/29594
|
||||||
extern_types, // https://github.com/rust-lang/rust/issues/43467
|
extern_types, // https://github.com/rust-lang/rust/issues/43467
|
||||||
|
c_str_literals, // https://github.com/rust-lang/rust/issues/105723
|
||||||
)]
|
)]
|
||||||
#![allow(
|
#![allow(
|
||||||
clippy::len_zero,
|
clippy::len_zero,
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ use {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
jay_config::video::GfxApi,
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
|
|
@ -169,16 +170,17 @@ impl UsrJayRenderCtxOwner for PortalDisplay {
|
||||||
}
|
}
|
||||||
if self.render_ctx.get().is_none() {
|
if self.render_ctx.get().is_none() {
|
||||||
let drm = Drm::open_existing(fd);
|
let drm = Drm::open_existing(fd);
|
||||||
let ctx = match create_gfx_context(&drm) {
|
let ctx =
|
||||||
Ok(c) => c,
|
match create_gfx_context(&self.state.eng, &self.state.ring, &drm, GfxApi::OpenGl) {
|
||||||
Err(e) => {
|
Ok(c) => c,
|
||||||
log::error!(
|
Err(e) => {
|
||||||
"Could not create render context from drm device: {}",
|
log::error!(
|
||||||
ErrorFmt(e)
|
"Could not create render context from drm device: {}",
|
||||||
);
|
ErrorFmt(e)
|
||||||
return;
|
);
|
||||||
}
|
return;
|
||||||
};
|
}
|
||||||
|
};
|
||||||
let ctx = Rc::new(PortalRenderCtx { dev_id, ctx });
|
let ctx = Rc::new(PortalRenderCtx { dev_id, ctx });
|
||||||
self.render_ctx.set(Some(ctx.clone()));
|
self.render_ctx.set(Some(ctx.clone()));
|
||||||
self.state.render_ctxs.set(dev_id, Rc::downgrade(&ctx));
|
self.state.render_ctxs.set(dev_id, Rc::downgrade(&ctx));
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ use {
|
||||||
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
},
|
},
|
||||||
video::dmabuf::DmaBuf,
|
video::dmabuf::{DmaBuf, PlaneVec},
|
||||||
wire::jay_screencast::Ready,
|
wire::jay_screencast::Ready,
|
||||||
wire_dbus::{
|
wire_dbus::{
|
||||||
org,
|
org,
|
||||||
|
|
@ -86,7 +86,7 @@ pub struct StartedScreencast {
|
||||||
session: Rc<ScreencastSession>,
|
session: Rc<ScreencastSession>,
|
||||||
node: Rc<PwClientNode>,
|
node: Rc<PwClientNode>,
|
||||||
port: Rc<PwClientNodePort>,
|
port: Rc<PwClientNodePort>,
|
||||||
buffers: RefCell<Vec<DmaBuf>>,
|
buffers: RefCell<PlaneVec<DmaBuf>>,
|
||||||
dpy: Rc<PortalDisplay>,
|
dpy: Rc<PortalDisplay>,
|
||||||
jay_screencast: Rc<UsrJayScreencast>,
|
jay_screencast: Rc<UsrJayScreencast>,
|
||||||
}
|
}
|
||||||
|
|
@ -276,7 +276,7 @@ impl ScreencastSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsrJayScreencastOwner for StartedScreencast {
|
impl UsrJayScreencastOwner for StartedScreencast {
|
||||||
fn buffers(&self, buffers: Vec<DmaBuf>) {
|
fn buffers(&self, buffers: PlaneVec<DmaBuf>) {
|
||||||
if buffers.len() == 0 {
|
if buffers.len() == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,18 @@ use {
|
||||||
cursor::KnownCursor,
|
cursor::KnownCursor,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
format::ARGB8888,
|
format::ARGB8888,
|
||||||
gfx_api::{GfxContext, GfxFramebuffer, GfxTexture},
|
gfx_api::{GfxContext, GfxFramebuffer},
|
||||||
ifs::zwlr_layer_shell_v1::OVERLAY,
|
ifs::zwlr_layer_shell_v1::OVERLAY,
|
||||||
portal::ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
|
portal::ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
|
||||||
renderer::renderer_base::RendererBase,
|
renderer::renderer_base::RendererBase,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
text::{self, TextMeasurement},
|
text::{self, TextMeasurement, TextTexture},
|
||||||
theme::Color,
|
theme::Color,
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||||
errorfmt::ErrorFmt, rc_eq::rc_eq,
|
errorfmt::ErrorFmt, rc_eq::rc_eq,
|
||||||
},
|
},
|
||||||
video::{gbm::GBM_BO_USE_RENDERING, ModifiedFormat, INVALID_MODIFIER},
|
video::gbm::GBM_BO_USE_RENDERING,
|
||||||
wire::{
|
wire::{
|
||||||
wp_fractional_scale_v1::PreferredScale, zwlr_layer_surface_v1::Configure,
|
wp_fractional_scale_v1::PreferredScale, zwlr_layer_surface_v1::Configure,
|
||||||
ZwpLinuxBufferParamsV1Id,
|
ZwpLinuxBufferParamsV1Id,
|
||||||
|
|
@ -118,7 +118,7 @@ pub struct Button {
|
||||||
pub bg_hover_color: Cell<Color>,
|
pub bg_hover_color: Cell<Color>,
|
||||||
pub text: RefCell<String>,
|
pub text: RefCell<String>,
|
||||||
pub font: RefCell<Cow<'static, str>>,
|
pub font: RefCell<Cow<'static, str>>,
|
||||||
pub tex: CloneCell<Option<Rc<dyn GfxTexture>>>,
|
pub tex: CloneCell<Option<TextTexture>>,
|
||||||
pub owner: CloneCell<Option<Rc<dyn ButtonOwner>>>,
|
pub owner: CloneCell<Option<Rc<dyn ButtonOwner>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,10 +162,12 @@ impl GuiElement for Button {
|
||||||
_max_width: f32,
|
_max_width: f32,
|
||||||
_max_height: f32,
|
_max_height: f32,
|
||||||
) -> (f32, f32) {
|
) -> (f32, f32) {
|
||||||
|
let old_tex = self.tex.take();
|
||||||
let font = self.font.borrow_mut();
|
let font = self.font.borrow_mut();
|
||||||
let text = self.text.borrow_mut();
|
let text = self.text.borrow_mut();
|
||||||
let tex = text::render_fitting2(
|
let tex = text::render_fitting2(
|
||||||
ctx,
|
ctx,
|
||||||
|
old_tex,
|
||||||
None,
|
None,
|
||||||
&font,
|
&font,
|
||||||
&text,
|
&text,
|
||||||
|
|
@ -213,10 +215,9 @@ impl GuiElement for Button {
|
||||||
if let Some(tex) = self.tex.get() {
|
if let Some(tex) = self.tex.get() {
|
||||||
let (tx, ty) = r.scale_point_f(x1 + self.tex_off_x.get(), y1 + self.tex_off_y.get());
|
let (tx, ty) = r.scale_point_f(x1 + self.tex_off_x.get(), y1 + self.tex_off_y.get());
|
||||||
r.render_texture(
|
r.render_texture(
|
||||||
&tex,
|
&tex.texture,
|
||||||
tx.round() as _,
|
tx.round() as _,
|
||||||
ty.round() as _,
|
ty.round() as _,
|
||||||
ARGB8888,
|
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
r.scale(),
|
r.scale(),
|
||||||
|
|
@ -260,7 +261,7 @@ pub struct Label {
|
||||||
pub data: GuiElementData,
|
pub data: GuiElementData,
|
||||||
pub font: RefCell<Cow<'static, str>>,
|
pub font: RefCell<Cow<'static, str>>,
|
||||||
pub text: RefCell<String>,
|
pub text: RefCell<String>,
|
||||||
pub tex: CloneCell<Option<Rc<dyn GfxTexture>>>,
|
pub tex: CloneCell<Option<TextTexture>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Label {
|
impl Default for Label {
|
||||||
|
|
@ -286,10 +287,12 @@ impl GuiElement for Label {
|
||||||
_max_width: f32,
|
_max_width: f32,
|
||||||
_max_height: f32,
|
_max_height: f32,
|
||||||
) -> (f32, f32) {
|
) -> (f32, f32) {
|
||||||
|
let old_tex = self.tex.take();
|
||||||
let text = self.text.borrow_mut();
|
let text = self.text.borrow_mut();
|
||||||
let font = self.font.borrow_mut();
|
let font = self.font.borrow_mut();
|
||||||
let tex = text::render_fitting2(
|
let tex = text::render_fitting2(
|
||||||
ctx,
|
ctx,
|
||||||
|
old_tex,
|
||||||
None,
|
None,
|
||||||
&font,
|
&font,
|
||||||
&text,
|
&text,
|
||||||
|
|
@ -300,7 +303,10 @@ impl GuiElement for Label {
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
let (tex, width, height) = match tex {
|
let (tex, width, height) = match tex {
|
||||||
Some((t, _)) => (Some(t.clone()), t.width(), t.height()),
|
Some((t, _)) => {
|
||||||
|
let (width, height) = t.texture.size();
|
||||||
|
(Some(t.clone()), width, height)
|
||||||
|
}
|
||||||
_ => (None, 0, 0),
|
_ => (None, 0, 0),
|
||||||
};
|
};
|
||||||
self.tex.set(tex);
|
self.tex.set(tex);
|
||||||
|
|
@ -311,10 +317,9 @@ impl GuiElement for Label {
|
||||||
if let Some(tex) = self.tex.get() {
|
if let Some(tex) = self.tex.get() {
|
||||||
let (tx, ty) = r.scale_point_f(x, y);
|
let (tx, ty) = r.scale_point_f(x, y);
|
||||||
r.render_texture(
|
r.render_texture(
|
||||||
&tex,
|
&tex.texture,
|
||||||
tx.round() as _,
|
tx.round() as _,
|
||||||
ty.round() as _,
|
ty.round() as _,
|
||||||
ARGB8888,
|
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
r.scale(),
|
r.scale(),
|
||||||
|
|
@ -638,12 +643,12 @@ impl WindowData {
|
||||||
self.have_frame.set(false);
|
self.have_frame.set(false);
|
||||||
buf.free.set(false);
|
buf.free.set(false);
|
||||||
|
|
||||||
buf.fb.render_custom(self.scale.get(), &mut |r| {
|
buf.fb
|
||||||
r.clear(&Color::from_gray(0));
|
.render_custom(self.scale.get(), Some(&Color::from_gray(0)), &mut |r| {
|
||||||
if let Some(content) = self.content.get() {
|
if let Some(content) = self.content.get() {
|
||||||
content.render_at(r, 0.0, 0.0)
|
content.render_at(r, 0.0, 0.0)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.surface.attach(&buf.wl);
|
self.surface.attach(&buf.wl);
|
||||||
self.surface.commit();
|
self.surface.commit();
|
||||||
|
|
@ -693,16 +698,26 @@ impl WindowData {
|
||||||
self.frame_missed.set(true);
|
self.frame_missed.set(true);
|
||||||
let width = (self.width.get() as f64 * self.scale.get().to_f64()).round() as i32;
|
let width = (self.width.get() as f64 * self.scale.get().to_f64()).round() as i32;
|
||||||
let height = (self.height.get() as f64 * self.scale.get().to_f64()).round() as i32;
|
let height = (self.height.get() as f64 * self.scale.get().to_f64()).round() as i32;
|
||||||
|
let formats = ctx.ctx.formats();
|
||||||
|
let format = match formats.get(&ARGB8888.drm) {
|
||||||
|
None => {
|
||||||
|
log::error!("Render context does not support ARGB8888 format");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Some(f) => f,
|
||||||
|
};
|
||||||
|
if format.write_modifiers.is_empty() {
|
||||||
|
log::error!("Render context cannot render to ARGB8888 format");
|
||||||
|
return;
|
||||||
|
}
|
||||||
for _ in 0..NUM_BUFFERS {
|
for _ in 0..NUM_BUFFERS {
|
||||||
let format = ModifiedFormat {
|
let bo = match ctx.ctx.gbm().create_bo(
|
||||||
format: ARGB8888,
|
width,
|
||||||
modifier: INVALID_MODIFIER,
|
height,
|
||||||
};
|
ARGB8888,
|
||||||
let bo = match ctx
|
&format.write_modifiers,
|
||||||
.ctx
|
GBM_BO_USE_RENDERING,
|
||||||
.gbm()
|
) {
|
||||||
.create_bo(width, height, &format, GBM_BO_USE_RENDERING)
|
|
||||||
{
|
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Could not allocate dmabuf: {}", ErrorFmt(e));
|
log::error!("Could not allocate dmabuf: {}", ErrorFmt(e));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::ARGB8888,
|
|
||||||
gfx_api::{BufferPoints, GfxApiOpt},
|
gfx_api::{BufferPoints, GfxApiOpt},
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_buffer::WlBuffer,
|
wl_buffer::WlBuffer,
|
||||||
|
|
@ -45,8 +44,7 @@ impl Debug for RenderResult {
|
||||||
pub struct Renderer<'a> {
|
pub struct Renderer<'a> {
|
||||||
pub base: RendererBase<'a>,
|
pub base: RendererBase<'a>,
|
||||||
pub state: &'a State,
|
pub state: &'a State,
|
||||||
pub on_output: bool,
|
pub result: Option<&'a mut RenderResult>,
|
||||||
pub result: &'a mut RenderResult,
|
|
||||||
pub logical_extents: Rect,
|
pub logical_extents: Rect,
|
||||||
pub physical_extents: Rect,
|
pub physical_extents: Rect,
|
||||||
}
|
}
|
||||||
|
|
@ -141,25 +139,15 @@ impl Renderer<'_> {
|
||||||
let scale = output.preferred_scale.get();
|
let scale = output.preferred_scale.get();
|
||||||
for title in &rd.titles {
|
for title in &rd.titles {
|
||||||
let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
|
let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
|
||||||
self.base.render_texture(
|
self.base
|
||||||
&title.tex,
|
.render_texture(&title.tex, x, y, None, None, scale, i32::MAX, i32::MAX);
|
||||||
x,
|
|
||||||
y,
|
|
||||||
ARGB8888,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
scale,
|
|
||||||
i32::MAX,
|
|
||||||
i32::MAX,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if let Some(status) = &rd.status {
|
if let Some(status) = &rd.status {
|
||||||
let (x, y) = self.base.scale_point(x + status.tex_x, y + status.tex_y);
|
let (x, y) = self.base.scale_point(x + status.tex_x, y + status.tex_y);
|
||||||
self.base.render_texture(
|
self.base.render_texture(
|
||||||
&status.tex,
|
&status.tex.texture,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
ARGB8888,
|
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
scale,
|
scale,
|
||||||
|
|
@ -198,13 +186,13 @@ impl Renderer<'_> {
|
||||||
&Color::from_rgba_straight(20, 20, 20, 255),
|
&Color::from_rgba_straight(20, 20, 20, 255),
|
||||||
);
|
);
|
||||||
if let Some(tex) = placeholder.textures.get(&self.base.scale) {
|
if let Some(tex) = placeholder.textures.get(&self.base.scale) {
|
||||||
let x = x + (pos.width() - tex.width()) / 2;
|
let (tex_width, tex_height) = tex.texture.size();
|
||||||
let y = y + (pos.height() - tex.height()) / 2;
|
let x = x + (pos.width() - tex_width) / 2;
|
||||||
|
let y = y + (pos.height() - tex_height) / 2;
|
||||||
self.base.render_texture(
|
self.base.render_texture(
|
||||||
&tex,
|
&tex.texture,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
ARGB8888,
|
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
self.base.scale,
|
self.base.scale,
|
||||||
|
|
@ -238,10 +226,9 @@ impl Renderer<'_> {
|
||||||
for title in titles {
|
for title in titles {
|
||||||
let (x, y) = self.base.scale_point(x + title.x, y + title.y);
|
let (x, y) = self.base.scale_point(x + title.x, y + title.y);
|
||||||
self.base.render_texture(
|
self.base.render_texture(
|
||||||
&title.tex,
|
&title.tex.texture,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
ARGB8888,
|
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
self.base.scale,
|
self.base.scale,
|
||||||
|
|
@ -367,14 +354,14 @@ impl Renderer<'_> {
|
||||||
} else {
|
} else {
|
||||||
self.render_buffer(&buffer, x, y, *tpoints, size, max_width, max_height);
|
self.render_buffer(&buffer, x, y, *tpoints, size, max_width, max_height);
|
||||||
}
|
}
|
||||||
if self.on_output {
|
if let Some(result) = self.result.as_deref_mut() {
|
||||||
{
|
{
|
||||||
let mut fr = surface.frame_requests.borrow_mut();
|
let mut fr = surface.frame_requests.borrow_mut();
|
||||||
self.result.frame_requests.extend(fr.drain(..));
|
result.frame_requests.extend(fr.drain(..));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let mut fbs = surface.presentation_feedback.borrow_mut();
|
let mut fbs = surface.presentation_feedback.borrow_mut();
|
||||||
self.result.presentation_feedbacks.extend(fbs.drain(..));
|
result.presentation_feedbacks.extend(fbs.drain(..));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -394,7 +381,6 @@ impl Renderer<'_> {
|
||||||
&tex,
|
&tex,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
buffer.format,
|
|
||||||
Some(tpoints),
|
Some(tpoints),
|
||||||
Some(tsize),
|
Some(tsize),
|
||||||
self.base.scale,
|
self.base.scale,
|
||||||
|
|
@ -407,6 +393,8 @@ impl Renderer<'_> {
|
||||||
{
|
{
|
||||||
self.base.fill_boxes(&[rect], color);
|
self.base.fill_boxes(&[rect], color);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log::info!("live buffer has neither a texture nor is a single-pixel buffer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -440,10 +428,9 @@ impl Renderer<'_> {
|
||||||
if let Some(title) = floating.title_textures.get(&self.base.scale) {
|
if let Some(title) = floating.title_textures.get(&self.base.scale) {
|
||||||
let (x, y) = self.base.scale_point(x + bw, y + bw);
|
let (x, y) = self.base.scale_point(x + bw, y + bw);
|
||||||
self.base.render_texture(
|
self.base.render_texture(
|
||||||
&title,
|
&title.texture,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
ARGB8888,
|
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
self.base.scale,
|
self.base.scale,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::Format,
|
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AbsoluteRect, BufferPoint, BufferPoints, Clear, CopyTexture, FillRect, GfxApiOpt,
|
AbsoluteRect, BufferPoint, BufferPoints, CopyTexture, FillRect, GfxApiOpt, GfxTexture,
|
||||||
GfxTexture,
|
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
|
|
@ -62,10 +60,6 @@ impl RendererBase<'_> {
|
||||||
rect
|
rect
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self, c: &Color) {
|
|
||||||
self.ops.push(GfxApiOpt::Clear(Clear { color: *c }))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fill_boxes(&mut self, boxes: &[Rect], color: &Color) {
|
pub fn fill_boxes(&mut self, boxes: &[Rect], color: &Color) {
|
||||||
self.fill_boxes2(boxes, color, 0, 0);
|
self.fill_boxes2(boxes, color, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +117,6 @@ impl RendererBase<'_> {
|
||||||
texture: &Rc<dyn GfxTexture>,
|
texture: &Rc<dyn GfxTexture>,
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
format: &'static Format,
|
|
||||||
tpoints: Option<BufferPoints>,
|
tpoints: Option<BufferPoints>,
|
||||||
tsize: Option<(i32, i32)>,
|
tsize: Option<(i32, i32)>,
|
||||||
tscale: Scale,
|
tscale: Scale,
|
||||||
|
|
@ -140,7 +133,7 @@ impl RendererBase<'_> {
|
||||||
let (twidth, theight) = if let Some(size) = tsize {
|
let (twidth, theight) = if let Some(size) = tsize {
|
||||||
size
|
size
|
||||||
} else {
|
} else {
|
||||||
let (mut w, mut h) = (texture.width(), texture.height());
|
let (mut w, mut h) = texture.size();
|
||||||
if tscale != self.scale {
|
if tscale != self.scale {
|
||||||
let tscale = tscale.to_f64();
|
let tscale = tscale.to_f64();
|
||||||
w = (w as f64 * self.scalef / tscale).round() as _;
|
w = (w as f64 * self.scalef / tscale).round() as _;
|
||||||
|
|
@ -186,7 +179,6 @@ impl RendererBase<'_> {
|
||||||
|
|
||||||
self.ops.push(GfxApiOpt::CopyTexture(CopyTexture {
|
self.ops.push(GfxApiOpt::CopyTexture(CopyTexture {
|
||||||
tex: texture.clone(),
|
tex: texture.clone(),
|
||||||
format,
|
|
||||||
source: texcoord,
|
source: texcoord,
|
||||||
target: AbsoluteRect {
|
target: AbsoluteRect {
|
||||||
x1: x,
|
x1: x,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use {
|
||||||
video::{
|
video::{
|
||||||
drm::DrmError,
|
drm::DrmError,
|
||||||
gbm::{GbmBo, GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
|
gbm::{GbmBo, GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
|
||||||
ModifiedFormat, INVALID_MODIFIER,
|
INVALID_MODIFIER, LINEAR_MODIFIER,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
std::{ops::Deref, rc::Rc},
|
std::{ops::Deref, rc::Rc},
|
||||||
|
|
@ -27,6 +27,10 @@ pub enum ScreenshooterError {
|
||||||
RenderError(#[from] GfxError),
|
RenderError(#[from] GfxError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
DrmError(#[from] DrmError),
|
DrmError(#[from] DrmError),
|
||||||
|
#[error("Render context does not support XRGB8888")]
|
||||||
|
XRGB8888,
|
||||||
|
#[error("Render context supports neither linear nor invalid modifier for XRGB8888 rendering")]
|
||||||
|
Linear,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Screenshot {
|
pub struct Screenshot {
|
||||||
|
|
@ -43,24 +47,31 @@ pub fn take_screenshot(state: &State) -> Result<Screenshot, ScreenshooterError>
|
||||||
if extents.is_empty() {
|
if extents.is_empty() {
|
||||||
return Err(ScreenshooterError::EmptyDisplay);
|
return Err(ScreenshooterError::EmptyDisplay);
|
||||||
}
|
}
|
||||||
let format = ModifiedFormat {
|
let formats = ctx.formats();
|
||||||
format: XRGB8888,
|
let mut usage = GBM_BO_USE_RENDERING;
|
||||||
modifier: INVALID_MODIFIER,
|
let modifiers = match formats.get(&XRGB8888.drm) {
|
||||||
|
None => return Err(ScreenshooterError::XRGB8888),
|
||||||
|
Some(f) if f.write_modifiers.contains(&LINEAR_MODIFIER) => &[LINEAR_MODIFIER],
|
||||||
|
Some(f) if f.write_modifiers.contains(&INVALID_MODIFIER) => {
|
||||||
|
usage |= GBM_BO_USE_LINEAR;
|
||||||
|
&[INVALID_MODIFIER]
|
||||||
|
}
|
||||||
|
Some(_) => return Err(ScreenshooterError::Linear),
|
||||||
};
|
};
|
||||||
let gbm = ctx.gbm();
|
let gbm = ctx.gbm();
|
||||||
let bo = gbm.create_bo(
|
let bo = gbm.create_bo(
|
||||||
extents.width(),
|
extents.width(),
|
||||||
extents.height(),
|
extents.height(),
|
||||||
&format,
|
XRGB8888,
|
||||||
GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR,
|
modifiers,
|
||||||
|
usage,
|
||||||
)?;
|
)?;
|
||||||
let fb = ctx.clone().dmabuf_fb(bo.dmabuf())?;
|
let fb = ctx.clone().dmabuf_fb(bo.dmabuf())?;
|
||||||
fb.render(
|
fb.render_node(
|
||||||
state.root.deref(),
|
state.root.deref(),
|
||||||
state,
|
state,
|
||||||
Some(state.root.extents.get()),
|
Some(state.root.extents.get()),
|
||||||
false,
|
None,
|
||||||
&mut Default::default(),
|
|
||||||
Scale::from_int(1),
|
Scale::from_int(1),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
29
src/state.rs
29
src/state.rs
|
|
@ -14,7 +14,8 @@ use {
|
||||||
dbus::Dbus,
|
dbus::Dbus,
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
forker::ForkerProxy,
|
forker::ForkerProxy,
|
||||||
gfx_api::GfxContext,
|
gfx_api::{GfxContext, GfxError},
|
||||||
|
gfx_apis::create_gfx_context,
|
||||||
globals::{Globals, GlobalsError, WaylandGlobal},
|
globals::{Globals, GlobalsError, WaylandGlobal},
|
||||||
ifs::{
|
ifs::{
|
||||||
ext_session_lock_v1::ExtSessionLockV1,
|
ext_session_lock_v1::ExtSessionLockV1,
|
||||||
|
|
@ -45,6 +46,7 @@ use {
|
||||||
errorfmt::ErrorFmt, fdcloser::FdCloser, linkedlist::LinkedList, numcell::NumCell,
|
errorfmt::ErrorFmt, fdcloser::FdCloser, linkedlist::LinkedList, numcell::NumCell,
|
||||||
queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
|
queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
|
||||||
},
|
},
|
||||||
|
video::drm::Drm,
|
||||||
wheel::Wheel,
|
wheel::Wheel,
|
||||||
wire::{
|
wire::{
|
||||||
JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId, ZwpLinuxDmabufFeedbackV1Id,
|
JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId, ZwpLinuxDmabufFeedbackV1Id,
|
||||||
|
|
@ -54,7 +56,7 @@ use {
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
bstr::ByteSlice,
|
bstr::ByteSlice,
|
||||||
jay_config::PciId,
|
jay_config::{video::GfxApi, PciId},
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
|
|
@ -131,6 +133,7 @@ pub struct State {
|
||||||
pub render_ctx_watchers: CopyHashMap<(ClientId, JayRenderCtxId), Rc<JayRenderCtx>>,
|
pub render_ctx_watchers: CopyHashMap<(ClientId, JayRenderCtxId), Rc<JayRenderCtx>>,
|
||||||
pub workspace_watchers: CopyHashMap<(ClientId, JayWorkspaceWatcherId), Rc<JayWorkspaceWatcher>>,
|
pub workspace_watchers: CopyHashMap<(ClientId, JayWorkspaceWatcherId), Rc<JayWorkspaceWatcher>>,
|
||||||
pub default_workspace_capture: Cell<bool>,
|
pub default_workspace_capture: Cell<bool>,
|
||||||
|
pub default_gfx_api: Cell<GfxApi>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
@ -237,6 +240,7 @@ impl DrmDevData {
|
||||||
struct UpdateTextTexturesVisitor;
|
struct UpdateTextTexturesVisitor;
|
||||||
impl NodeVisitorBase for UpdateTextTexturesVisitor {
|
impl NodeVisitorBase for UpdateTextTexturesVisitor {
|
||||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||||
|
node.children.iter().for_each(|c| c.title_tex.clear());
|
||||||
node.schedule_compute_render_data();
|
node.schedule_compute_render_data();
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
|
|
@ -245,16 +249,31 @@ impl NodeVisitorBase for UpdateTextTexturesVisitor {
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||||
|
node.title_textures.clear();
|
||||||
node.schedule_render_titles();
|
node.schedule_render_titles();
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
||||||
|
node.textures.clear();
|
||||||
node.update_texture();
|
node.update_texture();
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
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) {
|
pub fn add_output_scale(&self, scale: Scale) {
|
||||||
if self.scales.add(scale) {
|
if self.scales.add(scale) {
|
||||||
self.output_scales_changed();
|
self.output_scales_changed();
|
||||||
|
|
@ -339,11 +358,17 @@ impl State {
|
||||||
impl NodeVisitorBase for Walker {
|
impl NodeVisitorBase for Walker {
|
||||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||||
node.render_data.borrow_mut().titles.clear();
|
node.render_data.borrow_mut().titles.clear();
|
||||||
|
node.children.iter().for_each(|c| c.title_tex.clear());
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
fn visit_workspace(&mut self, node: &Rc<WorkspaceNode>) {
|
||||||
|
node.title_texture.set(None);
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
||||||
node.render_data.borrow_mut().titles.clear();
|
node.render_data.borrow_mut().titles.clear();
|
||||||
node.render_data.borrow_mut().status.take();
|
node.render_data.borrow_mut().status.take();
|
||||||
|
node.hardware_cursor.set(None);
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||||
|
|
|
||||||
|
|
@ -103,11 +103,13 @@ impl DrvDevHandler {
|
||||||
if let Some(config) = self.state.config.get() {
|
if let Some(config) = self.state.config.get() {
|
||||||
config.new_drm_dev(self.id);
|
config.new_drm_dev(self.id);
|
||||||
}
|
}
|
||||||
|
self.log_gfx_api();
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
#[allow(clippy::never_loop)]
|
#[allow(clippy::never_loop)]
|
||||||
while let Some(event) = self.data.dev.event() {
|
while let Some(event) = self.data.dev.event() {
|
||||||
match event {
|
match event {
|
||||||
DrmEvent::Removed => break 'outer,
|
DrmEvent::Removed => break 'outer,
|
||||||
|
DrmEvent::GfxApiChanged => self.log_gfx_api(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ae.triggered().await;
|
ae.triggered().await;
|
||||||
|
|
@ -121,4 +123,13 @@ impl DrvDevHandler {
|
||||||
self.data.handler.set(None);
|
self.data.handler.set(None);
|
||||||
self.state.drm_devs.remove(&self.id);
|
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(""),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
91
src/text.rs
91
src/text.rs
|
|
@ -11,8 +11,13 @@ use {
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
|
utils::clonecell::UnsafeCellCloneSafe,
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
borrow::Cow,
|
||||||
|
ops::{Deref, Neg},
|
||||||
|
rc::Rc,
|
||||||
},
|
},
|
||||||
std::{ops::Neg, rc::Rc},
|
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -32,6 +37,47 @@ pub enum TextError {
|
||||||
ImageData(#[source] PangoError),
|
ImageData(#[source] PangoError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
struct Config<'a> {
|
||||||
|
x: i32,
|
||||||
|
y: Option<i32>,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
padding: i32,
|
||||||
|
font: Cow<'a, str>,
|
||||||
|
text: Cow<'a, str>,
|
||||||
|
color: Color,
|
||||||
|
ellipsize: bool,
|
||||||
|
markup: bool,
|
||||||
|
scale: Option<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Config<'a> {
|
||||||
|
fn to_static(self) -> Config<'static> {
|
||||||
|
Config {
|
||||||
|
x: self.x,
|
||||||
|
y: self.y,
|
||||||
|
width: self.width,
|
||||||
|
height: self.height,
|
||||||
|
padding: self.padding,
|
||||||
|
font: Cow::Owned(self.font.into_owned()),
|
||||||
|
text: Cow::Owned(self.text.into_owned()),
|
||||||
|
color: self.color,
|
||||||
|
ellipsize: self.ellipsize,
|
||||||
|
markup: self.markup,
|
||||||
|
scale: self.scale,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TextTexture {
|
||||||
|
config: Rc<Config<'static>>,
|
||||||
|
pub texture: Rc<dyn GfxTexture>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl UnsafeCellCloneSafe for TextTexture {}
|
||||||
|
|
||||||
struct Data {
|
struct Data {
|
||||||
image: Rc<CairoImageSurface>,
|
image: Rc<CairoImageSurface>,
|
||||||
cctx: Rc<CairoContext>,
|
cctx: Rc<CairoContext>,
|
||||||
|
|
@ -95,20 +141,22 @@ pub fn measure(
|
||||||
|
|
||||||
pub fn render(
|
pub fn render(
|
||||||
ctx: &Rc<dyn GfxContext>,
|
ctx: &Rc<dyn GfxContext>,
|
||||||
|
old: Option<TextTexture>,
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
font: &str,
|
font: &str,
|
||||||
text: &str,
|
text: &str,
|
||||||
color: Color,
|
color: Color,
|
||||||
scale: Option<f64>,
|
scale: Option<f64>,
|
||||||
) -> Result<Rc<dyn GfxTexture>, TextError> {
|
) -> Result<TextTexture, TextError> {
|
||||||
render2(
|
render2(
|
||||||
ctx, 1, None, width, height, 1, font, text, color, true, false, scale,
|
ctx, old, 1, None, width, height, 1, font, text, color, true, false, scale,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render2(
|
fn render2(
|
||||||
ctx: &Rc<dyn GfxContext>,
|
ctx: &Rc<dyn GfxContext>,
|
||||||
|
old: Option<TextTexture>,
|
||||||
x: i32,
|
x: i32,
|
||||||
y: Option<i32>,
|
y: Option<i32>,
|
||||||
width: i32,
|
width: i32,
|
||||||
|
|
@ -120,7 +168,25 @@ fn render2(
|
||||||
ellipsize: bool,
|
ellipsize: bool,
|
||||||
markup: bool,
|
markup: bool,
|
||||||
scale: Option<f64>,
|
scale: Option<f64>,
|
||||||
) -> Result<Rc<dyn GfxTexture>, TextError> {
|
) -> Result<TextTexture, TextError> {
|
||||||
|
let config = Config {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
padding,
|
||||||
|
font: Cow::Borrowed(font),
|
||||||
|
text: Cow::Borrowed(text),
|
||||||
|
color,
|
||||||
|
ellipsize,
|
||||||
|
markup,
|
||||||
|
scale,
|
||||||
|
};
|
||||||
|
if let Some(old2) = &old {
|
||||||
|
if old2.config.deref() == &config {
|
||||||
|
return Ok(old.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
let data = create_data(font, width, height, scale)?;
|
let data = create_data(font, width, height, scale)?;
|
||||||
if ellipsize {
|
if ellipsize {
|
||||||
data.layout
|
data.layout
|
||||||
|
|
@ -144,25 +210,30 @@ fn render2(
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(e) => return Err(TextError::ImageData(e)),
|
Err(e) => return Err(TextError::ImageData(e)),
|
||||||
};
|
};
|
||||||
|
let old = old.map(|o| o.texture);
|
||||||
match ctx
|
match ctx
|
||||||
.clone()
|
.clone()
|
||||||
.shmem_texture(bytes, ARGB8888, width, height, data.image.stride())
|
.shmem_texture(old, bytes, ARGB8888, width, height, data.image.stride())
|
||||||
{
|
{
|
||||||
Ok(t) => Ok(t),
|
Ok(t) => Ok(TextTexture {
|
||||||
|
config: Rc::new(config.to_static()),
|
||||||
|
texture: t,
|
||||||
|
}),
|
||||||
Err(e) => Err(TextError::RenderError(e)),
|
Err(e) => Err(TextError::RenderError(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_fitting(
|
pub fn render_fitting(
|
||||||
ctx: &Rc<dyn GfxContext>,
|
ctx: &Rc<dyn GfxContext>,
|
||||||
|
old: Option<TextTexture>,
|
||||||
height: Option<i32>,
|
height: Option<i32>,
|
||||||
font: &str,
|
font: &str,
|
||||||
text: &str,
|
text: &str,
|
||||||
color: Color,
|
color: Color,
|
||||||
markup: bool,
|
markup: bool,
|
||||||
scale: Option<f64>,
|
scale: Option<f64>,
|
||||||
) -> Result<Rc<dyn GfxTexture>, TextError> {
|
) -> Result<TextTexture, TextError> {
|
||||||
render_fitting2(ctx, height, font, text, color, markup, scale, false).map(|(a, _)| a)
|
render_fitting2(ctx, old, height, font, text, color, markup, scale, false).map(|(a, _)| a)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Default)]
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
|
|
@ -174,6 +245,7 @@ pub struct TextMeasurement {
|
||||||
|
|
||||||
pub fn render_fitting2(
|
pub fn render_fitting2(
|
||||||
ctx: &Rc<dyn GfxContext>,
|
ctx: &Rc<dyn GfxContext>,
|
||||||
|
old: Option<TextTexture>,
|
||||||
height: Option<i32>,
|
height: Option<i32>,
|
||||||
font: &str,
|
font: &str,
|
||||||
text: &str,
|
text: &str,
|
||||||
|
|
@ -181,7 +253,7 @@ pub fn render_fitting2(
|
||||||
markup: bool,
|
markup: bool,
|
||||||
scale: Option<f64>,
|
scale: Option<f64>,
|
||||||
include_measurements: bool,
|
include_measurements: bool,
|
||||||
) -> Result<(Rc<dyn GfxTexture>, TextMeasurement), TextError> {
|
) -> Result<(TextTexture, TextMeasurement), TextError> {
|
||||||
let measurement = measure(font, text, markup, scale, include_measurements)?;
|
let measurement = measure(font, text, markup, scale, include_measurements)?;
|
||||||
let y = match height {
|
let y = match height {
|
||||||
Some(_) => None,
|
Some(_) => None,
|
||||||
|
|
@ -189,6 +261,7 @@ pub fn render_fitting2(
|
||||||
};
|
};
|
||||||
let res = render2(
|
let res = render2(
|
||||||
ctx,
|
ctx,
|
||||||
|
old,
|
||||||
measurement.ink_rect.x1().neg(),
|
measurement.ink_rect.x1().neg(),
|
||||||
y,
|
y,
|
||||||
measurement.ink_rect.width(),
|
measurement.ink_rect.width(),
|
||||||
|
|
|
||||||
28
src/theme.rs
28
src/theme.rs
|
|
@ -38,6 +38,13 @@ fn to_u8(c: f32) -> u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
|
pub const TRANSPARENT: Self = Self {
|
||||||
|
r: 0.0,
|
||||||
|
g: 0.0,
|
||||||
|
b: 0.0,
|
||||||
|
a: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn from_gray(g: u8) -> Self {
|
pub fn from_gray(g: u8) -> Self {
|
||||||
Self::from_rgb(g, g, g)
|
Self::from_rgb(g, g, g)
|
||||||
}
|
}
|
||||||
|
|
@ -77,6 +84,27 @@ impl Color {
|
||||||
pub fn to_rgba_premultiplied(self) -> [u8; 4] {
|
pub fn to_rgba_premultiplied(self) -> [u8; 4] {
|
||||||
[to_u8(self.r), to_u8(self.g), to_u8(self.b), to_u8(self.a)]
|
[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 {
|
impl From<jay_config::theme::Color> for Color {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ use {
|
||||||
backend::KeyState,
|
backend::KeyState,
|
||||||
cursor::KnownCursor,
|
cursor::KnownCursor,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::GfxTexture,
|
|
||||||
ifs::wl_seat::{
|
ifs::wl_seat::{
|
||||||
collect_kb_foci, collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId,
|
collect_kb_foci, collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId,
|
||||||
WlSeatGlobal, BTN_LEFT,
|
WlSeatGlobal, BTN_LEFT,
|
||||||
|
|
@ -12,7 +11,7 @@ use {
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
state::State,
|
state::State,
|
||||||
text,
|
text::{self, TextTexture},
|
||||||
tree::{
|
tree::{
|
||||||
walker::NodeVisitor, ContainingNode, Direction, FindTreeResult, FoundNode, Node,
|
walker::NodeVisitor, ContainingNode, Direction, FindTreeResult, FoundNode, Node,
|
||||||
NodeId, ToplevelData, ToplevelNode, WorkspaceNode,
|
NodeId, ToplevelData, ToplevelNode, WorkspaceNode,
|
||||||
|
|
@ -24,7 +23,7 @@ use {
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
rc_eq::rc_eq,
|
rc_eq::rc_eq,
|
||||||
scroller::Scroller,
|
scroller::Scroller,
|
||||||
smallmap::SmallMapMut,
|
smallmap::{SmallMap, SmallMapMut},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
|
@ -77,7 +76,7 @@ tree_id!(ContainerNodeId);
|
||||||
pub struct ContainerTitle {
|
pub struct ContainerTitle {
|
||||||
pub x: i32,
|
pub x: i32,
|
||||||
pub y: i32,
|
pub y: i32,
|
||||||
pub tex: Rc<dyn GfxTexture>,
|
pub tex: TextTexture,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -128,6 +127,7 @@ pub struct ContainerChild {
|
||||||
pub node: Rc<dyn ToplevelNode>,
|
pub node: Rc<dyn ToplevelNode>,
|
||||||
pub active: Cell<bool>,
|
pub active: Cell<bool>,
|
||||||
title: RefCell<String>,
|
title: RefCell<String>,
|
||||||
|
pub title_tex: SmallMap<Scale, TextTexture, 2>,
|
||||||
pub title_rect: Cell<Rect>,
|
pub title_rect: Cell<Rect>,
|
||||||
focus_history: Cell<Option<LinkedNode<NodeRef<ContainerChild>>>>,
|
focus_history: Cell<Option<LinkedNode<NodeRef<ContainerChild>>>>,
|
||||||
|
|
||||||
|
|
@ -182,6 +182,7 @@ impl ContainerNode {
|
||||||
content: Default::default(),
|
content: Default::default(),
|
||||||
factor: Cell::new(1.0),
|
factor: Cell::new(1.0),
|
||||||
title: Default::default(),
|
title: Default::default(),
|
||||||
|
title_tex: Default::default(),
|
||||||
title_rect: Default::default(),
|
title_rect: Default::default(),
|
||||||
focus_history: Default::default(),
|
focus_history: Default::default(),
|
||||||
}),
|
}),
|
||||||
|
|
@ -289,6 +290,7 @@ impl ContainerNode {
|
||||||
content: Default::default(),
|
content: Default::default(),
|
||||||
factor: Default::default(),
|
factor: Default::default(),
|
||||||
title: Default::default(),
|
title: Default::default(),
|
||||||
|
title_tex: Default::default(),
|
||||||
title_rect: Default::default(),
|
title_rect: Default::default(),
|
||||||
focus_history: Default::default(),
|
focus_history: Default::default(),
|
||||||
});
|
});
|
||||||
|
|
@ -678,6 +680,7 @@ impl ContainerNode {
|
||||||
}
|
}
|
||||||
let title = child.title.borrow_mut();
|
let title = child.title.borrow_mut();
|
||||||
for (scale, _) in scales.iter() {
|
for (scale, _) in scales.iter() {
|
||||||
|
let old_tex = child.title_tex.remove(scale);
|
||||||
let titles = rd.titles.get_or_default_mut(*scale);
|
let titles = rd.titles.get_or_default_mut(*scale);
|
||||||
'render_title: {
|
'render_title: {
|
||||||
let mut th = th;
|
let mut th = th;
|
||||||
|
|
@ -693,12 +696,24 @@ impl ContainerNode {
|
||||||
break 'render_title;
|
break 'render_title;
|
||||||
}
|
}
|
||||||
if let Some(ctx) = &ctx {
|
if let Some(ctx) = &ctx {
|
||||||
match text::render(ctx, width, th, &font, title.deref(), color, scalef) {
|
match text::render(
|
||||||
Ok(t) => titles.push(ContainerTitle {
|
ctx,
|
||||||
x: rect.x1(),
|
old_tex,
|
||||||
y: rect.y1(),
|
width,
|
||||||
tex: t,
|
th,
|
||||||
}),
|
&font,
|
||||||
|
title.deref(),
|
||||||
|
color,
|
||||||
|
scalef,
|
||||||
|
) {
|
||||||
|
Ok(t) => {
|
||||||
|
child.title_tex.insert(*scale, t.clone());
|
||||||
|
titles.push(ContainerTitle {
|
||||||
|
x: rect.x1(),
|
||||||
|
y: rect.y1(),
|
||||||
|
tex: t,
|
||||||
|
})
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Could not render title {}: {}", title, ErrorFmt(e));
|
log::error!("Could not render title {}: {}", title, ErrorFmt(e));
|
||||||
}
|
}
|
||||||
|
|
@ -1268,6 +1283,7 @@ impl ContainingNode for ContainerNode {
|
||||||
content: Default::default(),
|
content: Default::default(),
|
||||||
factor: Cell::new(node.factor.get()),
|
factor: Cell::new(node.factor.get()),
|
||||||
title: Default::default(),
|
title: Default::default(),
|
||||||
|
title_tex: Default::default(),
|
||||||
title_rect: Cell::new(node.title_rect.get()),
|
title_rect: Cell::new(node.title_rect.get()),
|
||||||
focus_history: Cell::new(None),
|
focus_history: Cell::new(None),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,12 @@ use {
|
||||||
backend::KeyState,
|
backend::KeyState,
|
||||||
cursor::KnownCursor,
|
cursor::KnownCursor,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::GfxTexture,
|
|
||||||
ifs::wl_seat::{NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT},
|
ifs::wl_seat::{NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
state::State,
|
state::State,
|
||||||
text,
|
text::{self, TextTexture},
|
||||||
tree::{
|
tree::{
|
||||||
walker::NodeVisitor, ContainingNode, FindTreeResult, FoundNode, Node, NodeId,
|
walker::NodeVisitor, ContainingNode, FindTreeResult, FoundNode, Node, NodeId,
|
||||||
StackedNode, ToplevelNode, WorkspaceNode,
|
StackedNode, ToplevelNode, WorkspaceNode,
|
||||||
|
|
@ -44,7 +43,7 @@ pub struct FloatNode {
|
||||||
pub layout_scheduled: Cell<bool>,
|
pub layout_scheduled: Cell<bool>,
|
||||||
pub render_titles_scheduled: Cell<bool>,
|
pub render_titles_scheduled: Cell<bool>,
|
||||||
pub title: RefCell<String>,
|
pub title: RefCell<String>,
|
||||||
pub title_textures: CopyHashMap<Scale, Rc<dyn GfxTexture>>,
|
pub title_textures: CopyHashMap<Scale, TextTexture>,
|
||||||
seats: RefCell<AHashMap<SeatId, SeatState>>,
|
seats: RefCell<AHashMap<SeatId, SeatState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,7 +178,6 @@ impl FloatNode {
|
||||||
let bw = theme.sizes.border_width.get();
|
let bw = theme.sizes.border_width.get();
|
||||||
let font = theme.font.borrow_mut();
|
let font = theme.font.borrow_mut();
|
||||||
let title = self.title.borrow_mut();
|
let title = self.title.borrow_mut();
|
||||||
self.title_textures.clear();
|
|
||||||
let pos = self.position.get();
|
let pos = self.position.get();
|
||||||
if pos.width() <= 2 * bw || title.is_empty() {
|
if pos.width() <= 2 * bw || title.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -190,6 +188,7 @@ impl FloatNode {
|
||||||
};
|
};
|
||||||
let scales = self.state.scales.lock();
|
let scales = self.state.scales.lock();
|
||||||
for (scale, _) in scales.iter() {
|
for (scale, _) in scales.iter() {
|
||||||
|
let old_tex = self.title_textures.remove(scale);
|
||||||
let mut th = th;
|
let mut th = th;
|
||||||
let mut scalef = None;
|
let mut scalef = None;
|
||||||
let mut width = pos.width() - 2 * bw;
|
let mut width = pos.width() - 2 * bw;
|
||||||
|
|
@ -202,7 +201,7 @@ impl FloatNode {
|
||||||
if th == 0 || width == 0 {
|
if th == 0 || width == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let texture = match text::render(&ctx, width, th, &font, &title, tc, scalef) {
|
let texture = match text::render(&ctx, old_tex, width, th, &font, &title, tc, scalef) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Could not render title {}: {}", title, ErrorFmt(e));
|
log::error!("Could not render title {}: {}", title, ErrorFmt(e));
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ use {
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
state::State,
|
state::State,
|
||||||
text,
|
text::{self, TextTexture},
|
||||||
tree::{
|
tree::{
|
||||||
walker::NodeVisitor, Direction, FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode,
|
walker::NodeVisitor, Direction, FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode,
|
||||||
},
|
},
|
||||||
|
|
@ -165,6 +165,7 @@ impl OutputNode {
|
||||||
let output_width = self.global.pos.get().width();
|
let output_width = self.global.pos.get().width();
|
||||||
rd.underline = Rect::new_sized(0, th, output_width, 1).unwrap();
|
rd.underline = Rect::new_sized(0, th, output_width, 1).unwrap();
|
||||||
for ws in self.workspaces.iter() {
|
for ws in self.workspaces.iter() {
|
||||||
|
let old_tex = ws.title_texture.take();
|
||||||
let mut title_width = th;
|
let mut title_width = th;
|
||||||
'create_texture: {
|
'create_texture: {
|
||||||
if let Some(ctx) = self.state.render_ctx.get() {
|
if let Some(ctx) = self.state.render_ctx.get() {
|
||||||
|
|
@ -177,6 +178,7 @@ impl OutputNode {
|
||||||
};
|
};
|
||||||
let title = match text::render_fitting(
|
let title = match text::render_fitting(
|
||||||
&ctx,
|
&ctx,
|
||||||
|
old_tex,
|
||||||
Some(texture_height),
|
Some(texture_height),
|
||||||
&font,
|
&font,
|
||||||
&ws.name,
|
&ws.name,
|
||||||
|
|
@ -190,8 +192,9 @@ impl OutputNode {
|
||||||
break 'create_texture;
|
break 'create_texture;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
ws.title_texture.set(Some(title.clone()));
|
||||||
let mut x = pos + 1;
|
let mut x = pos + 1;
|
||||||
let mut width = title.width();
|
let (mut width, _) = title.texture.size();
|
||||||
if let Some(scale) = scale {
|
if let Some(scale) = scale {
|
||||||
width = (width as f64 / scale).round() as _;
|
width = (width as f64 / scale).round() as _;
|
||||||
}
|
}
|
||||||
|
|
@ -205,7 +208,7 @@ impl OutputNode {
|
||||||
x2: pos + title_width,
|
x2: pos + title_width,
|
||||||
tex_x: x,
|
tex_x: x,
|
||||||
tex_y: 0,
|
tex_y: 0,
|
||||||
tex: title,
|
tex: title.texture,
|
||||||
ws: ws.deref().clone(),
|
ws: ws.deref().clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -224,6 +227,7 @@ impl OutputNode {
|
||||||
pos += title_width;
|
pos += title_width;
|
||||||
}
|
}
|
||||||
'set_status: {
|
'set_status: {
|
||||||
|
let old_tex = rd.status.take().map(|s| s.tex);
|
||||||
let ctx = match self.state.render_ctx.get() {
|
let ctx = match self.state.render_ctx.get() {
|
||||||
Some(ctx) => ctx,
|
Some(ctx) => ctx,
|
||||||
_ => break 'set_status,
|
_ => break 'set_status,
|
||||||
|
|
@ -235,6 +239,7 @@ impl OutputNode {
|
||||||
let tc = self.state.theme.colors.bar_text.get();
|
let tc = self.state.theme.colors.bar_text.get();
|
||||||
let title = match text::render_fitting(
|
let title = match text::render_fitting(
|
||||||
&ctx,
|
&ctx,
|
||||||
|
old_tex,
|
||||||
Some(texture_height),
|
Some(texture_height),
|
||||||
&font,
|
&font,
|
||||||
&status,
|
&status,
|
||||||
|
|
@ -248,7 +253,7 @@ impl OutputNode {
|
||||||
break 'set_status;
|
break 'set_status;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut width = title.width();
|
let (mut width, _) = title.texture.size();
|
||||||
if let Some(scale) = scale {
|
if let Some(scale) = scale {
|
||||||
width = (width as f64 / scale).round() as _;
|
width = (width as f64 / scale).round() as _;
|
||||||
}
|
}
|
||||||
|
|
@ -324,6 +329,7 @@ impl OutputNode {
|
||||||
desired_output: CloneCell::new(self.global.output_id.clone()),
|
desired_output: CloneCell::new(self.global.output_id.clone()),
|
||||||
jay_workspaces: Default::default(),
|
jay_workspaces: Default::default(),
|
||||||
capture: self.state.default_workspace_capture.clone(),
|
capture: self.state.default_workspace_capture.clone(),
|
||||||
|
title_texture: Default::default(),
|
||||||
});
|
});
|
||||||
ws.output_link
|
ws.output_link
|
||||||
.set(Some(self.workspaces.add_last(ws.clone())));
|
.set(Some(self.workspaces.add_last(ws.clone())));
|
||||||
|
|
@ -473,7 +479,7 @@ pub struct OutputTitle {
|
||||||
pub struct OutputStatus {
|
pub struct OutputStatus {
|
||||||
pub tex_x: i32,
|
pub tex_x: i32,
|
||||||
pub tex_y: i32,
|
pub tex_y: i32,
|
||||||
pub tex: Rc<dyn GfxTexture>,
|
pub tex: TextTexture,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,12 @@ use {
|
||||||
client::Client,
|
client::Client,
|
||||||
cursor::KnownCursor,
|
cursor::KnownCursor,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::GfxTexture,
|
|
||||||
ifs::wl_seat::{NodeSeatState, WlSeatGlobal},
|
ifs::wl_seat::{NodeSeatState, WlSeatGlobal},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
state::State,
|
state::State,
|
||||||
text,
|
text::{self, TextTexture},
|
||||||
tree::{
|
tree::{
|
||||||
Direction, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, ToplevelData,
|
Direction, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, ToplevelData,
|
||||||
ToplevelNode,
|
ToplevelNode,
|
||||||
|
|
@ -25,7 +24,7 @@ pub struct PlaceholderNode {
|
||||||
id: PlaceholderNodeId,
|
id: PlaceholderNodeId,
|
||||||
toplevel: ToplevelData,
|
toplevel: ToplevelData,
|
||||||
destroyed: Cell<bool>,
|
destroyed: Cell<bool>,
|
||||||
pub textures: SmallMap<Scale, Rc<dyn GfxTexture>, 2>,
|
pub textures: SmallMap<Scale, TextTexture, 2>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlaceholderNode {
|
impl PlaceholderNode {
|
||||||
|
|
@ -47,11 +46,11 @@ impl PlaceholderNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_texture(&self) {
|
pub fn update_texture(&self) {
|
||||||
self.textures.clear();
|
|
||||||
if let Some(ctx) = self.toplevel.state.render_ctx.get() {
|
if let Some(ctx) = self.toplevel.state.render_ctx.get() {
|
||||||
let scales = self.toplevel.state.scales.lock();
|
let scales = self.toplevel.state.scales.lock();
|
||||||
let rect = self.toplevel.pos.get();
|
let rect = self.toplevel.pos.get();
|
||||||
for (scale, _) in scales.iter() {
|
for (scale, _) in scales.iter() {
|
||||||
|
let old_tex = self.textures.remove(scale);
|
||||||
let mut width = rect.width();
|
let mut width = rect.width();
|
||||||
let mut height = rect.height();
|
let mut height = rect.height();
|
||||||
if *scale != 1 {
|
if *scale != 1 {
|
||||||
|
|
@ -63,6 +62,7 @@ impl PlaceholderNode {
|
||||||
let font = format!("monospace {}", width / 10);
|
let font = format!("monospace {}", width / 10);
|
||||||
match text::render_fitting(
|
match text::render_fitting(
|
||||||
&ctx,
|
&ctx,
|
||||||
|
old_tex,
|
||||||
Some(height),
|
Some(height),
|
||||||
&font,
|
&font,
|
||||||
"Fullscreen",
|
"Fullscreen",
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use {
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
|
text::TextTexture,
|
||||||
tree::{
|
tree::{
|
||||||
container::ContainerNode, walker::NodeVisitor, ContainingNode, Direction,
|
container::ContainerNode, walker::NodeVisitor, ContainingNode, Direction,
|
||||||
FindTreeResult, FoundNode, Node, NodeId, NodeVisitorBase, OutputNode, StackedNode,
|
FindTreeResult, FoundNode, Node, NodeId, NodeVisitorBase, OutputNode, StackedNode,
|
||||||
|
|
@ -43,6 +44,7 @@ pub struct WorkspaceNode {
|
||||||
pub desired_output: CloneCell<Rc<OutputId>>,
|
pub desired_output: CloneCell<Rc<OutputId>>,
|
||||||
pub jay_workspaces: CopyHashMap<(ClientId, JayWorkspaceId), Rc<JayWorkspace>>,
|
pub jay_workspaces: CopyHashMap<(ClientId, JayWorkspaceId), Rc<JayWorkspace>>,
|
||||||
pub capture: Cell<bool>,
|
pub capture: Cell<bool>,
|
||||||
|
pub title_texture: Cell<Option<TextTexture>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkspaceNode {
|
impl WorkspaceNode {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
fn default() -> Self {
|
||||||
Self::new(Default::default())
|
Self::new(Default::default())
|
||||||
}
|
}
|
||||||
|
|
@ -77,6 +77,8 @@ unsafe impl<T> UnsafeCellCloneSafe for NodeRef<T> {}
|
||||||
unsafe impl UnsafeCellCloneSafe for () {}
|
unsafe impl UnsafeCellCloneSafe for () {}
|
||||||
unsafe impl UnsafeCellCloneSafe for u64 {}
|
unsafe impl UnsafeCellCloneSafe for u64 {}
|
||||||
unsafe impl UnsafeCellCloneSafe for i32 {}
|
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) {}
|
unsafe impl<A: UnsafeCellCloneSafe, B: UnsafeCellCloneSafe> UnsafeCellCloneSafe for (A, B) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
use {
|
use {
|
||||||
crate::utils::ptr_ext::{MutPtrExt, PtrExt},
|
crate::utils::{
|
||||||
|
clonecell::UnsafeCellCloneSafe,
|
||||||
|
ptr_ext::{MutPtrExt, PtrExt},
|
||||||
|
},
|
||||||
std::{cell::UnsafeCell, mem},
|
std::{cell::UnsafeCell, mem},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -28,7 +31,7 @@ impl<T> Stack<T> {
|
||||||
|
|
||||||
pub fn to_vec(&self) -> Vec<T>
|
pub fn to_vec(&self) -> Vec<T>
|
||||||
where
|
where
|
||||||
T: Clone,
|
T: UnsafeCellCloneSafe,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let v = self.vec.get().deref();
|
let v = self.vec.get().deref();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
use crate::format::Format;
|
|
||||||
|
|
||||||
pub mod dmabuf;
|
pub mod dmabuf;
|
||||||
pub mod drm;
|
pub mod drm;
|
||||||
pub mod gbm;
|
pub mod gbm;
|
||||||
|
|
@ -9,9 +7,3 @@ pub type Modifier = u64;
|
||||||
pub const INVALID_MODIFIER: Modifier = 0x00ff_ffff_ffff_ffff;
|
pub const INVALID_MODIFIER: Modifier = 0x00ff_ffff_ffff_ffff;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub const LINEAR_MODIFIER: Modifier = 0;
|
pub const LINEAR_MODIFIER: Modifier = 0;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct ModifiedFormat {
|
|
||||||
pub format: &'static Format,
|
|
||||||
pub modifier: Modifier,
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
use {crate::format::Format, std::rc::Rc, uapi::OwnedFd};
|
use {
|
||||||
|
crate::{format::Format, utils::oserror::OsError, video::Modifier},
|
||||||
|
arrayvec::ArrayVec,
|
||||||
|
std::rc::Rc,
|
||||||
|
uapi::{c::ioctl, OwnedFd, _IOW, _IOWR},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DmaBufPlane {
|
pub struct DmaBufPlane {
|
||||||
|
|
@ -12,6 +17,81 @@ pub struct DmaBuf {
|
||||||
pub width: i32,
|
pub width: i32,
|
||||||
pub height: i32,
|
pub height: i32,
|
||||||
pub format: &'static Format,
|
pub format: &'static Format,
|
||||||
pub modifier: u64,
|
pub modifier: Modifier,
|
||||||
pub planes: Vec<DmaBufPlane>,
|
pub planes: PlaneVec<DmaBufPlane>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ use {
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
bstr::{BString, ByteSlice},
|
bstr::{BString, ByteSlice},
|
||||||
|
indexmap::IndexSet,
|
||||||
std::{
|
std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
ffi::CString,
|
ffi::CString,
|
||||||
|
|
@ -35,8 +36,11 @@ use crate::{
|
||||||
utils::{buf::Buf, errorfmt::ErrorFmt, stack::Stack, syncqueue::SyncQueue, vec_ext::VecExt},
|
utils::{buf::Buf, errorfmt::ErrorFmt, stack::Stack, syncqueue::SyncQueue, vec_ext::VecExt},
|
||||||
video::{
|
video::{
|
||||||
dmabuf::DmaBuf,
|
dmabuf::DmaBuf,
|
||||||
drm::sys::{get_version, DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH},
|
drm::sys::{
|
||||||
INVALID_MODIFIER,
|
drm_format_modifier, drm_format_modifier_blob, get_version, DRM_CAP_CURSOR_HEIGHT,
|
||||||
|
DRM_CAP_CURSOR_WIDTH, FORMAT_BLOB_CURRENT,
|
||||||
|
},
|
||||||
|
Modifier, INVALID_MODIFIER,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
pub use sys::{
|
pub use sys::{
|
||||||
|
|
@ -106,6 +110,8 @@ pub enum DrmError {
|
||||||
InvalidRead,
|
InvalidRead,
|
||||||
#[error("Could not determine the drm version")]
|
#[error("Could not determine the drm version")]
|
||||||
Version(#[source] OsError),
|
Version(#[source] OsError),
|
||||||
|
#[error("Format of IN_FORMATS property is invalid")]
|
||||||
|
InFormats,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {
|
fn render_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {
|
||||||
|
|
@ -169,11 +175,24 @@ impl Drm {
|
||||||
get_nodes(self.fd.raw()).map_err(DrmError::GetNodes)
|
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> {
|
pub fn version(&self) -> Result<DrmVersion, DrmError> {
|
||||||
get_version(self.fd.raw()).map_err(DrmError::Version)
|
get_version(self.fd.raw()).map_err(DrmError::Version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct InFormat {
|
||||||
|
pub format: u32,
|
||||||
|
pub modifiers: IndexSet<Modifier>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DrmMaster {
|
pub struct DrmMaster {
|
||||||
drm: Drm,
|
drm: Drm,
|
||||||
u32_bufs: Stack<Vec<u32>>,
|
u32_bufs: Stack<Vec<u32>>,
|
||||||
|
|
@ -373,6 +392,64 @@ impl DrmMaster {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_in_formats(&self, property: u32) -> Result<Vec<InFormat>, DrmError> {
|
||||||
|
let blob = self.getblob_vec::<u8>(DrmBlob(property))?;
|
||||||
|
let header: drm_format_modifier_blob = match uapi::pod_read_init(blob.as_bytes()) {
|
||||||
|
Ok(h) => h,
|
||||||
|
Err(_) => {
|
||||||
|
log::error!("Header of IN_FORMATS blob doesn't fit in the blob");
|
||||||
|
return Err(DrmError::InFormats);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if header.version != FORMAT_BLOB_CURRENT {
|
||||||
|
log::error!(
|
||||||
|
"Header of IN_FORMATS has an invalid version: {}",
|
||||||
|
header.version
|
||||||
|
);
|
||||||
|
return Err(DrmError::InFormats);
|
||||||
|
}
|
||||||
|
let formats_start = header.formats_offset as usize;
|
||||||
|
let formats_end = formats_start
|
||||||
|
.wrapping_add((header.count_formats as usize).wrapping_mul(mem::size_of::<u32>()));
|
||||||
|
let modifiers_start = header.modifiers_offset as usize;
|
||||||
|
let modifiers_end = modifiers_start.wrapping_add(
|
||||||
|
(header.count_modifiers as usize).wrapping_mul(mem::size_of::<drm_format_modifier>()),
|
||||||
|
);
|
||||||
|
if blob.len() < formats_end || formats_end < formats_start {
|
||||||
|
log::error!("Formats of IN_FORMATS blob don't fit in the blob");
|
||||||
|
return Err(DrmError::InFormats);
|
||||||
|
}
|
||||||
|
if blob.len() < modifiers_end || modifiers_end < modifiers_start {
|
||||||
|
log::error!("Formats of IN_FORMATS blob don't fit in the blob");
|
||||||
|
return Err(DrmError::InFormats);
|
||||||
|
}
|
||||||
|
let mut formats: Vec<_> = uapi::pod_iter::<u32, _>(&blob[formats_start..formats_end])
|
||||||
|
.unwrap()
|
||||||
|
.map(|f| InFormat {
|
||||||
|
format: f,
|
||||||
|
modifiers: IndexSet::new(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let modifiers =
|
||||||
|
uapi::pod_iter::<drm_format_modifier, _>(&blob[modifiers_start..modifiers_end])
|
||||||
|
.unwrap();
|
||||||
|
for modifier in modifiers {
|
||||||
|
let offset = modifier.offset as usize;
|
||||||
|
let mut indices = modifier.formats;
|
||||||
|
while indices != 0 {
|
||||||
|
let idx = indices.trailing_zeros();
|
||||||
|
indices &= !(1 << idx);
|
||||||
|
let idx = idx as usize + offset;
|
||||||
|
if idx >= formats.len() {
|
||||||
|
log::error!("Modifier offset is out of bounds");
|
||||||
|
return Err(DrmError::InFormats);
|
||||||
|
}
|
||||||
|
formats[idx].modifiers.insert(modifier.modifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(formats)
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::await_holding_refcell_ref)]
|
#[allow(clippy::await_holding_refcell_ref)]
|
||||||
pub async fn event(&self) -> Result<Option<DrmEvent>, DrmError> {
|
pub async fn event(&self) -> Result<Option<DrmEvent>, DrmError> {
|
||||||
if self.events.is_empty() {
|
if self.events.is_empty() {
|
||||||
|
|
|
||||||
|
|
@ -1132,3 +1132,27 @@ pub fn get_version(fd: c::c_int) -> Result<DrmVersion, OsError> {
|
||||||
desc: desc.into(),
|
desc: desc.into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const FORMAT_BLOB_CURRENT: u32 = 1;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct drm_format_modifier_blob {
|
||||||
|
pub version: u32,
|
||||||
|
pub flags: u32,
|
||||||
|
pub count_formats: u32,
|
||||||
|
pub formats_offset: u32,
|
||||||
|
pub count_modifiers: u32,
|
||||||
|
pub modifiers_offset: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Pod for drm_format_modifier_blob {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct drm_format_modifier {
|
||||||
|
pub formats: u64,
|
||||||
|
pub offset: u32,
|
||||||
|
pub pad: u32,
|
||||||
|
pub modifier: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Pod for drm_format_modifier {}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
format::formats,
|
format::{formats, Format},
|
||||||
utils::oserror::OsError,
|
utils::oserror::OsError,
|
||||||
video::{
|
video::{
|
||||||
dmabuf::{DmaBuf, DmaBufPlane},
|
dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
|
||||||
drm::{Drm, DrmError},
|
drm::{Drm, DrmError},
|
||||||
ModifiedFormat, INVALID_MODIFIER,
|
Modifier, INVALID_MODIFIER,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
|
|
@ -27,13 +27,15 @@ pub enum GbmError {
|
||||||
#[error("Cloud not create a gbm device")]
|
#[error("Cloud not create a gbm device")]
|
||||||
CreateDevice,
|
CreateDevice,
|
||||||
#[error("Cloud not create a gbm buffer")]
|
#[error("Cloud not create a gbm buffer")]
|
||||||
CreateBo,
|
CreateBo(#[source] OsError),
|
||||||
#[error("gbm buffer has an unknown format")]
|
#[error("gbm buffer has an unknown format")]
|
||||||
UnknownFormat,
|
UnknownFormat,
|
||||||
#[error("Could not retrieve a drm-buf fd")]
|
#[error("Could not retrieve a drm-buf fd")]
|
||||||
DrmFd,
|
DrmFd,
|
||||||
#[error("Could not map bo")]
|
#[error("Could not map bo")]
|
||||||
MapBo(#[source] OsError),
|
MapBo(#[source] OsError),
|
||||||
|
#[error("Tried to allocate a buffer with no modifier")]
|
||||||
|
NoModifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Device = u8;
|
pub type Device = u8;
|
||||||
|
|
@ -161,7 +163,7 @@ unsafe fn export_bo(bo: *mut Bo) -> Result<DmaBuf, GbmError> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
planes: {
|
planes: {
|
||||||
let mut planes = vec![];
|
let mut planes = PlaneVec::new();
|
||||||
for plane in 0..gbm_bo_get_plane_count(bo) {
|
for plane in 0..gbm_bo_get_plane_count(bo) {
|
||||||
let offset = gbm_bo_get_offset(bo, plane);
|
let offset = gbm_bo_get_offset(bo, plane);
|
||||||
let stride = gbm_bo_get_stride_for_plane(bo, plane);
|
let stride = gbm_bo_get_stride_for_plane(bo, plane);
|
||||||
|
|
@ -195,30 +197,36 @@ impl GbmDevice {
|
||||||
self.dev
|
self.dev
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_bo(
|
pub fn create_bo<'a>(
|
||||||
&self,
|
&self,
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
format: &ModifiedFormat,
|
format: &Format,
|
||||||
usage: u32,
|
modifiers: impl IntoIterator<Item = &'a Modifier>,
|
||||||
|
mut usage: u32,
|
||||||
) -> Result<GbmBo, GbmError> {
|
) -> Result<GbmBo, GbmError> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let (modifiers, n_modifiers) = if format.modifier == INVALID_MODIFIER {
|
let modifiers: Vec<Modifier> = modifiers.into_iter().copied().collect();
|
||||||
|
if modifiers.is_empty() {
|
||||||
|
return Err(GbmError::NoModifier);
|
||||||
|
}
|
||||||
|
let (modifiers, n_modifiers) = if modifiers == [INVALID_MODIFIER] {
|
||||||
(ptr::null(), 0)
|
(ptr::null(), 0)
|
||||||
} else {
|
} else {
|
||||||
(&format.modifier as _, 1)
|
usage &= !GBM_BO_USE_LINEAR;
|
||||||
|
(modifiers.as_ptr() as _, modifiers.len() as _)
|
||||||
};
|
};
|
||||||
let bo = gbm_bo_create_with_modifiers2(
|
let bo = gbm_bo_create_with_modifiers2(
|
||||||
self.dev,
|
self.dev,
|
||||||
width as _,
|
width as _,
|
||||||
height as _,
|
height as _,
|
||||||
format.format.drm,
|
format.drm,
|
||||||
modifiers,
|
modifiers,
|
||||||
n_modifiers,
|
n_modifiers,
|
||||||
usage,
|
usage,
|
||||||
);
|
);
|
||||||
if bo.is_null() {
|
if bo.is_null() {
|
||||||
return Err(GbmError::CreateBo);
|
return Err(GbmError::CreateBo(OsError::default()));
|
||||||
}
|
}
|
||||||
let bo = BoHolder { bo };
|
let bo = BoHolder { bo };
|
||||||
let dma = export_bo(bo.bo)?;
|
let dma = export_bo(bo.bo)?;
|
||||||
|
|
@ -250,7 +258,7 @@ impl GbmDevice {
|
||||||
usage,
|
usage,
|
||||||
);
|
);
|
||||||
if bo.is_null() {
|
if bo.is_null() {
|
||||||
return Err(GbmError::CreateBo);
|
return Err(GbmError::CreateBo(OsError::default()));
|
||||||
}
|
}
|
||||||
let bo = BoHolder { bo };
|
let bo = BoHolder { bo };
|
||||||
Ok(GbmBo {
|
Ok(GbmBo {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use {
|
||||||
buffd::{MsgParser, MsgParserError},
|
buffd::{MsgParser, MsgParserError},
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
},
|
},
|
||||||
video::dmabuf::{DmaBuf, DmaBufPlane},
|
video::dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
|
||||||
wire::{jay_screencast::*, JayScreencastId},
|
wire::{jay_screencast::*, JayScreencastId},
|
||||||
wl_usr::{usr_ifs::usr_jay_output::UsrJayOutput, usr_object::UsrObject, UsrCon},
|
wl_usr::{usr_ifs::usr_jay_output::UsrJayOutput, usr_object::UsrObject, UsrCon},
|
||||||
},
|
},
|
||||||
|
|
@ -19,8 +19,8 @@ pub struct UsrJayScreencast {
|
||||||
pub con: Rc<UsrCon>,
|
pub con: Rc<UsrCon>,
|
||||||
pub owner: CloneCell<Option<Rc<dyn UsrJayScreencastOwner>>>,
|
pub owner: CloneCell<Option<Rc<dyn UsrJayScreencastOwner>>>,
|
||||||
|
|
||||||
pub pending_buffers: RefCell<Vec<DmaBuf>>,
|
pub pending_buffers: RefCell<PlaneVec<DmaBuf>>,
|
||||||
pub pending_planes: RefCell<Vec<DmaBufPlane>>,
|
pub pending_planes: RefCell<PlaneVec<DmaBufPlane>>,
|
||||||
|
|
||||||
pub pending_config: RefCell<UsrJayScreencastServerConfig>,
|
pub pending_config: RefCell<UsrJayScreencastServerConfig>,
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ pub struct UsrJayScreencastServerConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait UsrJayScreencastOwner {
|
pub trait UsrJayScreencastOwner {
|
||||||
fn buffers(&self, buffers: Vec<DmaBuf>) {
|
fn buffers(&self, buffers: PlaneVec<DmaBuf>) {
|
||||||
let _ = buffers;
|
let _ = buffers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ msg dmabuf = 0 {
|
||||||
height: u32,
|
height: u32,
|
||||||
offset: u32,
|
offset: u32,
|
||||||
stride: u32,
|
stride: u32,
|
||||||
|
modifier_lo: u32,
|
||||||
|
modifier_hi: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
msg error = 1 {
|
msg error = 1 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue