use super::server::{LockBound, LockedServerCompositor, ServerCompositor};
use super::{
CompositionGpuImportedImageSynchronizationCapabilities, Compositor, ICompositionGpuImportedObject,
ICompositionGpuInterop, ICompositionImportableSharedGpuContextImage, ICompositionImportableSharedGpuContextSemaphore,
ICompositionImportedGpuImage, ICompositionImportedGpuSemaphore, ServerJobError, ServerJobTask,
};
use crate::platform::{
IExternalObjectsHandleWrapRenderInterfaceContextFeature, IExternalObjectsRenderInterfaceContextFeature,
IExternalObjectsWrappedGpuHandle, IPlatformHandle, IPlatformRenderInterfaceContext,
IPlatformRenderInterfaceImportedImage, IPlatformRenderInterfaceImportedSemaphore, PlatformGraphicsContextLostException,
PlatformGraphicsDrmFormat, PlatformGraphicsExternalImageProperties, PlatformHandle,
};
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
fn same_context(a: &Rc<dyn IPlatformRenderInterfaceContext>, b: &Rc<dyn IPlatformRenderInterfaceContext>) -> bool {
std::ptr::addr_eq(Rc::as_ptr(a), Rc::as_ptr(b))
}
struct InteropState {
context: Rc<dyn IPlatformRenderInterfaceContext>,
external_objects: Rc<dyn IExternalObjectsRenderInterfaceContextFeature>,
external_objects_with_handle_wrap: Option<Rc<dyn IExternalObjectsHandleWrapRenderInterfaceContextFeature>>,
}
impl InteropState {
fn imported_object_base(&self) -> ServerGpuImportedObjectBase {
ServerGpuImportedObjectBase {
context: self.context.clone(),
feature: self.external_objects.clone(),
import_result: RefCell::new(None),
}
}
}
pub(crate) struct CompositionInteropServerPart {
state: LockBound<InteropState>,
device_luid: Option<Vec<u8>>,
device_uuid: Option<Vec<u8>>,
}
pub struct CompositionInterop {
compositor: Rc<Compositor>,
state: LockBound<InteropState>,
device_luid: RefCell<Option<Vec<u8>>>,
device_uuid: RefCell<Option<Vec<u8>>>,
}
impl CompositionInterop {
pub fn new(
compositor: &Rc<Compositor>,
external_objects: Rc<dyn IExternalObjectsRenderInterfaceContextFeature>,
) -> Rc<CompositionInterop> {
match Self::try_new(compositor, external_objects) {
Some(interop) => interop,
None => panic!("the context of the render interface is created by the render thread, which has none"),
}
}
pub(crate) fn try_new(
compositor: &Rc<Compositor>,
external_objects: Rc<dyn IExternalObjectsRenderInterfaceContextFeature>,
) -> Option<Rc<CompositionInterop>> {
let locked = compositor.locked_server();
let may_create_context = !compositor.is_confined_to_render_thread();
let part =
compositor.with_server(|server| Self::server_part(&locked, server, external_objects, may_create_context))?;
Some(Self::from_server_part(compositor, part))
}
pub(crate) fn server_part(
locked: &Arc<LockedServerCompositor>,
server: &ServerCompositor,
external_objects: Rc<dyn IExternalObjectsRenderInterfaceContextFeature>,
may_create_context: bool,
) -> Option<CompositionInteropServerPart> {
let context = if may_create_context {
server.render_interface().value()
} else {
server.render_interface().existing_backend_context()?
};
let external_objects_with_handle_wrap = {
let features: &dyn crate::platform::IOptionalFeatureProvider = &*context;
features.try_get::<dyn IExternalObjectsHandleWrapRenderInterfaceContextFeature>()
};
Some(CompositionInteropServerPart {
device_luid: external_objects.device_luid(),
device_uuid: external_objects.device_uuid(),
state: LockBound::new(
locked,
server,
InteropState { context, external_objects, external_objects_with_handle_wrap },
),
})
}
pub(crate) fn from_server_part(compositor: &Rc<Compositor>, part: CompositionInteropServerPart) -> Rc<Self> {
Rc::new(CompositionInterop {
compositor: compositor.clone(),
device_luid: RefCell::new(part.device_luid),
device_uuid: RefCell::new(part.device_uuid),
state: part.state,
})
}
fn with_state<R>(&self, f: impl FnOnce(&ServerCompositor, &InteropState) -> R) -> R {
self.compositor.with_server(|server| {
let server: &ServerCompositor = server;
f(server, self.state.get(server))
})
}
}
impl ICompositionGpuInterop for CompositionInterop {
fn supported_image_handle_types(&self) -> Vec<String> {
self.with_state(|_, state| state.external_objects.supported_image_handle_types())
}
fn supported_semaphore_types(&self) -> Vec<String> {
self.with_state(|_, state| state.external_objects.supported_semaphore_types())
}
fn supported_dma_buf_formats(&self) -> Option<Vec<PlatformGraphicsDrmFormat>> {
self.with_state(|_, state| state.external_objects.supported_dma_buf_formats())
}
fn get_synchronization_capabilities(&self, image_handle_type: &str) -> CompositionGpuImportedImageSynchronizationCapabilities {
self.with_state(|_, state| state.external_objects.get_synchronization_capabilities(image_handle_type))
}
fn import_image(
&self,
handle: Rc<dyn IPlatformHandle>,
properties: PlatformGraphicsExternalImageProperties,
) -> Rc<dyn ICompositionImportedGpuImage> {
let server = self.with_state(|server, state| {
let wrapped = state
.external_objects_with_handle_wrap
.as_ref()
.and_then(|wrap| wrap.wrap_image_handle_on_any_thread(&handle, properties.clone()));
let source = ImageSource::Handle(ImportHandle::new(wrapped, &*handle), properties);
Arc::new(self.compositor.bind_to_lock(server, ServerImportedGpuImage::new(state, source)))
});
CompositionImportedGpuImage::new(&self.compositor, server)
}
fn import_shared_image(&self, image: Arc<dyn ICompositionImportableSharedGpuContextImage>) -> Rc<dyn ICompositionImportedGpuImage> {
let source = ImageSource::Shared(image);
let server = self.with_state(|server, state| {
Arc::new(self.compositor.bind_to_lock(server, ServerImportedGpuImage::new(state, source)))
});
CompositionImportedGpuImage::new(&self.compositor, server)
}
fn import_semaphore(&self, handle: Rc<dyn IPlatformHandle>) -> Rc<dyn ICompositionImportedGpuSemaphore> {
let server = self.with_state(|server, state| {
let wrapped = state
.external_objects_with_handle_wrap
.as_ref()
.and_then(|wrap| wrap.wrap_semaphore_handle_on_any_thread(&handle));
let handle = ImportHandle::new(wrapped, &*handle);
Arc::new(self.compositor.bind_to_lock(server, ServerImportedGpuSemaphore::new(state, handle)))
});
CompositionImportedGpuSemaphore::new(&self.compositor, server)
}
fn import_shared_semaphore(
&self,
_image: Rc<dyn ICompositionImportableSharedGpuContextSemaphore>,
) -> Rc<dyn ICompositionImportedGpuImage> {
panic!("Specified method is not supported.");
}
fn is_lost(&self) -> bool {
self.with_state(|_, state| state.context.is_lost())
}
fn device_luid(&self) -> Option<Vec<u8>> {
self.device_luid.borrow().clone()
}
fn set_device_luid(&self, value: Option<Vec<u8>>) {
*self.device_luid.borrow_mut() = value;
}
fn device_uuid(&self) -> Option<Vec<u8>> {
self.device_uuid.borrow().clone()
}
fn set_device_uuid(&self, value: Option<Vec<u8>>) {
*self.device_uuid.borrow_mut() = value;
}
}
enum ImportHandle {
Wrapped(Rc<dyn IExternalObjectsWrappedGpuHandle>),
Copied(PlatformHandle),
}
impl ImportHandle {
fn new(wrapped: Option<Rc<dyn IExternalObjectsWrappedGpuHandle>>, handle: &dyn IPlatformHandle) -> ImportHandle {
match wrapped {
Some(wrapped) => ImportHandle::Wrapped(wrapped),
None => ImportHandle::Copied(PlatformHandle::new(handle.handle(), handle.handle_descriptor())),
}
}
fn platform_handle(&self) -> Rc<dyn IPlatformHandle> {
match self {
ImportHandle::Wrapped(wrapped) => wrapped.clone().as_platform_handle(),
ImportHandle::Copied(handle) => Rc::new(handle.clone()),
}
}
fn dispose(self) {
if let ImportHandle::Wrapped(wrapped) = self {
wrapped.dispose();
}
}
}
enum ImageSource {
Handle(ImportHandle, PlatformGraphicsExternalImageProperties),
Shared(Arc<dyn ICompositionImportableSharedGpuContextImage>),
}
impl ImageSource {
fn import(
&self,
feature: &dyn IExternalObjectsRenderInterfaceContextFeature,
) -> Result<Rc<dyn IPlatformRenderInterfaceImportedImage>, ServerJobError> {
match self {
ImageSource::Handle(handle, properties) => Ok(feature.import_image(handle.platform_handle(), properties.clone())),
ImageSource::Shared(image) => Ok(feature.import_shared_image(image.clone())),
}
}
fn dispose(self) {
if let ImageSource::Handle(handle, _) = self {
handle.dispose();
}
}
}
pub struct ServerGpuImportedObjectBase {
context: Rc<dyn IPlatformRenderInterfaceContext>,
feature: Rc<dyn IExternalObjectsRenderInterfaceContextFeature>,
import_result: RefCell<Option<Result<(), ServerJobError>>>,
}
impl ServerGpuImportedObjectBase {
pub fn context(&self) -> &Rc<dyn IPlatformRenderInterfaceContext> {
&self.context
}
pub fn feature(&self) -> &Rc<dyn IExternalObjectsRenderInterfaceContextFeature> {
&self.feature
}
pub fn import_result(&self) -> Option<Result<(), ServerJobError>> {
self.import_result.borrow().clone()
}
fn is_current_context(&self, server: &ServerCompositor) -> bool {
same_context(&server.render_interface().value(), &self.context)
}
}
pub trait IServerGpuImportedObject: 'static {
fn base(&self) -> &ServerGpuImportedObjectBase;
fn import(&self, server: &ServerCompositor) -> Result<(), ServerJobError>;
fn dispose(&self);
}
pub struct CompositionGpuImportedObjectBase<T: IServerGpuImportedObject> {
compositor: Rc<Compositor>,
server: Arc<LockBound<T>>,
import_completed: ServerJobTask<()>,
}
impl<T: IServerGpuImportedObject> CompositionGpuImportedObjectBase<T> {
fn new(compositor: &Rc<Compositor>, server: Arc<LockBound<T>>) -> Self {
let object = server.clone();
let import_completed = compositor.invoke_server_job_async(
move |compositor| {
let object = object.get(compositor);
let result = object.import(compositor);
*object.base().import_result.borrow_mut() = Some(result.clone());
result
},
false,
);
Self { compositor: compositor.clone(), server, import_completed }
}
pub fn compositor(&self) -> &Rc<Compositor> {
&self.compositor
}
pub(crate) fn server(&self) -> Arc<LockBound<T>> {
self.server.clone()
}
pub fn import_completed(&self) -> ServerJobTask<()> {
self.import_completed.clone()
}
pub fn is_lost(&self) -> bool {
self.compositor.with_server(|server| self.server.get(server).base().context.is_lost())
}
fn dispose_async(&self) -> ServerJobTask<()> {
let object = self.server.clone();
self.compositor.invoke_server_job_async(
move |compositor| {
let object = object.get(compositor);
if matches!(object.base().import_result(), Some(Ok(()))) {
object.dispose();
}
Ok(())
},
false,
)
}
}
pub struct ServerImportedGpuImage {
base: ServerGpuImportedObjectBase,
source: RefCell<Option<ImageSource>>,
image: RefCell<Option<Rc<dyn IPlatformRenderInterfaceImportedImage>>>,
}
impl ServerImportedGpuImage {
fn new(state: &InteropState, source: ImageSource) -> ServerImportedGpuImage {
ServerImportedGpuImage {
base: state.imported_object_base(),
source: RefCell::new(Some(source)),
image: RefCell::new(None),
}
}
pub fn image(&self) -> Rc<dyn IPlatformRenderInterfaceImportedImage> {
match self.image.borrow().clone() {
Some(image) => image,
None => panic!("Cannot access a disposed object.\nObject name: 'CompositionImportedGpuImage'."),
}
}
pub fn context(&self) -> &Rc<dyn IPlatformRenderInterfaceContext> {
&self.base.context
}
pub fn is_usable(&self, server: &ServerCompositor) -> bool {
self.image.borrow().is_some() && self.base.is_current_context(server)
}
}
impl IServerGpuImportedObject for ServerImportedGpuImage {
fn base(&self) -> &ServerGpuImportedObjectBase {
&self.base
}
fn import(&self, server: &ServerCompositor) -> Result<(), ServerJobError> {
let source = self.source.borrow_mut().take();
let current = server.render_interface().ensure_current();
let result = (|| {
if !self.base.is_current_context(server) {
return Err(Arc::new(PlatformGraphicsContextLostException) as ServerJobError);
}
if let Some(source) = &source {
*self.image.borrow_mut() = Some(source.import(&*self.base.feature)?);
}
Ok(())
})();
current.dispose();
if let Some(source) = source {
source.dispose();
}
result
}
fn dispose(&self) {
if let Some(image) = self.image.borrow_mut().take() {
image.dispose();
}
}
}
pub struct CompositionImportedGpuImage {
base: CompositionGpuImportedObjectBase<ServerImportedGpuImage>,
}
impl CompositionImportedGpuImage {
fn new(compositor: &Rc<Compositor>, server: Arc<LockBound<ServerImportedGpuImage>>) -> Rc<CompositionImportedGpuImage> {
Rc::new(CompositionImportedGpuImage { base: CompositionGpuImportedObjectBase::new(compositor, server) })
}
pub fn base(&self) -> &CompositionGpuImportedObjectBase<ServerImportedGpuImage> {
&self.base
}
pub(crate) fn server(&self) -> Arc<LockBound<ServerImportedGpuImage>> {
self.base.server()
}
}
impl ICompositionGpuImportedObject for CompositionImportedGpuImage {
fn import_completed(&self) -> ServerJobTask<()> {
self.base.import_completed()
}
fn is_lost(&self) -> bool {
self.base.is_lost()
}
fn dispose_async(&self) -> ServerJobTask<()> {
self.base.dispose_async()
}
}
impl ICompositionImportedGpuImage for CompositionImportedGpuImage {
fn as_imported_gpu_image(&self) -> Option<&CompositionImportedGpuImage> {
Some(self)
}
}
pub struct ServerImportedGpuSemaphore {
base: ServerGpuImportedObjectBase,
handle: RefCell<Option<ImportHandle>>,
semaphore: RefCell<Option<Rc<dyn IPlatformRenderInterfaceImportedSemaphore>>>,
}
impl ServerImportedGpuSemaphore {
fn new(state: &InteropState, handle: ImportHandle) -> ServerImportedGpuSemaphore {
ServerImportedGpuSemaphore {
base: state.imported_object_base(),
handle: RefCell::new(Some(handle)),
semaphore: RefCell::new(None),
}
}
pub fn semaphore(&self) -> Rc<dyn IPlatformRenderInterfaceImportedSemaphore> {
match self.semaphore.borrow().clone() {
Some(semaphore) => semaphore,
None => panic!("Cannot access a disposed object.\nObject name: 'CompositionImportedGpuSemaphore'."),
}
}
pub fn is_usable(&self, server: &ServerCompositor) -> bool {
self.semaphore.borrow().is_some() && self.base.is_current_context(server)
}
}
impl IServerGpuImportedObject for ServerImportedGpuSemaphore {
fn base(&self) -> &ServerGpuImportedObjectBase {
&self.base
}
fn import(&self, _server: &ServerCompositor) -> Result<(), ServerJobError> {
let handle = self.handle.borrow_mut().take();
if let Some(handle) = handle {
*self.semaphore.borrow_mut() = Some(self.base.feature.import_semaphore(handle.platform_handle()));
handle.dispose();
}
Ok(())
}
fn dispose(&self) {
if let Some(semaphore) = self.semaphore.borrow_mut().take() {
semaphore.dispose();
}
}
}
pub struct CompositionImportedGpuSemaphore {
base: CompositionGpuImportedObjectBase<ServerImportedGpuSemaphore>,
}
impl CompositionImportedGpuSemaphore {
fn new(
compositor: &Rc<Compositor>,
server: Arc<LockBound<ServerImportedGpuSemaphore>>,
) -> Rc<CompositionImportedGpuSemaphore> {
Rc::new(CompositionImportedGpuSemaphore { base: CompositionGpuImportedObjectBase::new(compositor, server) })
}
pub fn base(&self) -> &CompositionGpuImportedObjectBase<ServerImportedGpuSemaphore> {
&self.base
}
pub(crate) fn server(&self) -> Arc<LockBound<ServerImportedGpuSemaphore>> {
self.base.server()
}
}
impl ICompositionGpuImportedObject for CompositionImportedGpuSemaphore {
fn import_completed(&self) -> ServerJobTask<()> {
self.base.import_completed()
}
fn is_lost(&self) -> bool {
self.base.is_lost()
}
fn dispose_async(&self) -> ServerJobTask<()> {
self.base.dispose_async()
}
}
impl ICompositionImportedGpuSemaphore for CompositionImportedGpuSemaphore {
fn as_imported_gpu_semaphore(&self) -> Option<&CompositionImportedGpuSemaphore> {
Some(self)
}
}