@ -74,6 +74,10 @@
# define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64
# endif
# ifndef GBM_BO_USE_LINEAR
# define GBM_BO_USE_LINEAR (1 << 4)
# endif
/**
* A small wrapper to print information into the ' drm - backend ' debug scope .
*
@ -547,6 +551,10 @@ struct drm_output {
struct wl_listener recorder_frame_listener ;
struct wl_event_source * pageflip_timer ;
bool virtual ;
submit_frame_cb virtual_submit_frame ;
} ;
static const char * const aspect_ratio_as_string [ ] = {
@ -859,6 +867,9 @@ drm_output_update_msc(struct drm_output *output, unsigned int seq);
static void
drm_output_destroy ( struct weston_output * output_base ) ;
static void
drm_virtual_output_destroy ( struct weston_output * output_base ) ;
/**
* Returns true if the plane can be used on the given output for its current
* repaint cycle .
@ -868,6 +879,9 @@ drm_plane_is_available(struct drm_plane *plane, struct drm_output *output)
{
assert ( plane - > state_cur ) ;
if ( output - > virtual )
return false ;
/* The plane still has a request not yet completed by the kernel. */
if ( ! plane - > state_cur - > complete )
return false ;
@ -2668,6 +2682,8 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
}
wl_list_for_each ( output_state , & pending_state - > output_list , link ) {
if ( output_state - > output - > virtual )
continue ;
if ( mode = = DRM_STATE_APPLY_SYNC )
assert ( output_state - > dpms = = WESTON_DPMS_OFF ) ;
ret | = drm_output_apply_state_atomic ( output_state , req , & flags ) ;
@ -2777,6 +2793,12 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
struct drm_output * output = output_state - > output ;
int ret ;
if ( output - > virtual ) {
drm_output_assign_state ( output_state ,
DRM_STATE_APPLY_ASYNC ) ;
continue ;
}
ret = drm_output_apply_state_legacy ( output_state ) ;
if ( ret ! = 0 ) {
weston_log ( " Couldn't apply state for output %s \n " ,
@ -2855,6 +2877,8 @@ drm_output_repaint(struct weston_output *output_base,
struct drm_output_state * state = NULL ;
struct drm_plane_state * scanout_state ;
assert ( ! output - > virtual ) ;
if ( output - > disable_pending | | output - > destroy_pending )
goto err ;
@ -3729,7 +3753,7 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
drm_debug ( b , " \t [repaint] preparing state for output %s (%lu) \n " ,
output_base - > name , ( unsigned long ) output_base - > id ) ;
if ( ! b - > sprites_are_broken ) {
if ( ! b - > sprites_are_broken & & ! output - > virtual ) {
state = drm_output_propose_state ( output_base , pending_state ,
DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY ) ;
if ( ! state ) {
@ -4448,6 +4472,63 @@ drm_plane_destroy(struct drm_plane *plane)
free ( plane ) ;
}
/**
* Create a drm_plane for virtual output
*
* Call drm_virtual_plane_destroy to clean up the plane .
*
* @ param b DRM compositor backend
* @ param output Output to create internal plane for
*/
static struct drm_plane *
drm_virtual_plane_create ( struct drm_backend * b , struct drm_output * output )
{
struct drm_plane * plane ;
/* num of formats is one */
plane = zalloc ( sizeof ( * plane ) + sizeof ( plane - > formats [ 0 ] ) ) ;
if ( ! plane ) {
weston_log ( " %s: out of memory \n " , __func__ ) ;
return NULL ;
}
plane - > type = WDRM_PLANE_TYPE_PRIMARY ;
plane - > backend = b ;
plane - > state_cur = drm_plane_state_alloc ( NULL , plane ) ;
plane - > state_cur - > complete = true ;
plane - > formats [ 0 ] . format = output - > gbm_format ;
plane - > count_formats = 1 ;
if ( output - > gbm_bo_flags & GBM_BO_USE_LINEAR ) {
uint64_t * modifiers = zalloc ( sizeof * modifiers ) ;
if ( modifiers ) {
* modifiers = DRM_FORMAT_MOD_LINEAR ;
plane - > formats [ 0 ] . modifiers = modifiers ;
plane - > formats [ 0 ] . count_modifiers = 1 ;
}
}
weston_plane_init ( & plane - > base , b - > compositor , 0 , 0 ) ;
wl_list_insert ( & b - > plane_list , & plane - > link ) ;
return plane ;
}
/**
* Destroy one DRM plane
*
* @ param plane Plane to deallocate ( will be freed )
*/
static void
drm_virtual_plane_destroy ( struct drm_plane * plane )
{
drm_plane_state_free ( plane - > state_cur , true ) ;
weston_plane_release ( & plane - > base ) ;
wl_list_remove ( & plane - > link ) ;
if ( plane - > formats [ 0 ] . modifiers )
free ( plane - > formats [ 0 ] . modifiers ) ;
free ( plane ) ;
}
/**
* Initialise sprites ( overlay planes )
*
@ -4681,6 +4762,8 @@ drm_output_init_backlight(struct drm_output *output)
*
* If we are called as part of repaint , we simply set the relevant bit in
* state and return .
*
* This function is never called on a virtual output .
*/
static void
drm_set_dpms ( struct weston_output * output_base , enum dpms_enum level )
@ -4691,6 +4774,8 @@ drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
struct drm_output_state * state ;
int ret ;
assert ( ! output - > virtual ) ;
if ( output - > state_cur - > dpms = = level )
return ;
@ -5591,6 +5676,9 @@ drm_output_set_mode(struct weston_output *base,
struct drm_mode * current ;
if ( output - > virtual )
return - 1 ;
if ( drm_output_update_modelist_from_heads ( output ) < 0 )
return - 1 ;
@ -5948,6 +6036,8 @@ drm_output_enable(struct weston_output *base)
drmModeRes * resources ;
int ret ;
assert ( ! output - > virtual ) ;
resources = drmModeGetResources ( b - > drm . fd ) ;
if ( ! resources ) {
weston_log ( " drmModeGetResources failed \n " ) ;
@ -6045,6 +6135,8 @@ drm_output_destroy(struct weston_output *base)
struct drm_output * output = to_drm_output ( base ) ;
struct drm_backend * b = to_drm_backend ( base - > compositor ) ;
assert ( ! output - > virtual ) ;
if ( output - > page_flip_pending | | output - > vblank_pending | |
output - > atomic_complete_pending ) {
output - > destroy_pending = 1 ;
@ -6073,6 +6165,8 @@ drm_output_disable(struct weston_output *base)
{
struct drm_output * output = to_drm_output ( base ) ;
assert ( ! output - > virtual ) ;
if ( output - > page_flip_pending | | output - > vblank_pending | |
output - > atomic_complete_pending ) {
output - > disable_pending = 1 ;
@ -6909,12 +7003,269 @@ renderer_switch_binding(struct weston_keyboard *keyboard,
switch_to_gl_renderer ( b ) ;
}
static void
drm_virtual_output_start_repaint_loop ( struct weston_output * output_base )
{
weston_output_finish_frame ( output_base , NULL ,
WP_PRESENTATION_FEEDBACK_INVALID ) ;
}
static int
drm_virtual_output_submit_frame ( struct drm_output * output ,
struct drm_fb * fb )
{
struct drm_backend * b = to_drm_backend ( output - > base . compositor ) ;
int fd , ret ;
assert ( fb - > num_planes = = 1 ) ;
ret = drmPrimeHandleToFD ( b - > drm . fd , fb - > handles [ 0 ] , DRM_CLOEXEC , & fd ) ;
if ( ret ) {
weston_log ( " drmPrimeHandleFD failed, errno=%d \n " , errno ) ;
return - 1 ;
}
drm_fb_ref ( fb ) ;
ret = output - > virtual_submit_frame ( & output - > base , fd , fb - > strides [ 0 ] ,
fb ) ;
if ( ret < 0 ) {
drm_fb_unref ( fb ) ;
close ( fd ) ;
}
return ret ;
}
static int
drm_virtual_output_repaint ( struct weston_output * output_base ,
pixman_region32_t * damage ,
void * repaint_data )
{
struct drm_pending_state * pending_state = repaint_data ;
struct drm_output_state * state = NULL ;
struct drm_output * output = to_drm_output ( output_base ) ;
struct drm_plane * scanout_plane = output - > scanout_plane ;
struct drm_plane_state * scanout_state ;
assert ( output - > virtual ) ;
if ( output - > disable_pending | | output - > destroy_pending )
goto err ;
/* Drop frame if there isn't free buffers */
if ( ! gbm_surface_has_free_buffers ( output - > gbm_surface ) ) {
weston_log ( " %s: Drop frame!! \n " , __func__ ) ;
return - 1 ;
}
assert ( ! output - > state_last ) ;
/* If planes have been disabled in the core, we might not have
* hit assign_planes at all , so might not have valid output state
* here . */
state = drm_pending_state_get_output ( pending_state , output ) ;
if ( ! state )
state = drm_output_state_duplicate ( output - > state_cur ,
pending_state ,
DRM_OUTPUT_STATE_CLEAR_PLANES ) ;
drm_output_render ( state , damage ) ;
scanout_state = drm_output_state_get_plane ( state , scanout_plane ) ;
if ( ! scanout_state | | ! scanout_state - > fb )
goto err ;
if ( drm_virtual_output_submit_frame ( output , scanout_state - > fb ) < 0 )
goto err ;
return 0 ;
err :
drm_output_state_free ( state ) ;
return - 1 ;
}
static void
drm_virtual_output_deinit ( struct weston_output * base )
{
struct drm_output * output = to_drm_output ( base ) ;
drm_output_fini_egl ( output ) ;
drm_virtual_plane_destroy ( output - > scanout_plane ) ;
}
static void
drm_virtual_output_destroy ( struct weston_output * base )
{
struct drm_output * output = to_drm_output ( base ) ;
assert ( output - > virtual ) ;
if ( output - > base . enabled )
drm_virtual_output_deinit ( & output - > base ) ;
weston_output_release ( & output - > base ) ;
drm_output_state_free ( output - > state_cur ) ;
free ( output ) ;
}
static int
drm_virtual_output_enable ( struct weston_output * output_base )
{
struct drm_output * output = to_drm_output ( output_base ) ;
struct drm_backend * b = to_drm_backend ( output_base - > compositor ) ;
assert ( output - > virtual ) ;
if ( b - > use_pixman ) {
weston_log ( " Not support pixman renderer on Virtual output \n " ) ;
goto err ;
}
if ( ! output - > virtual_submit_frame ) {
weston_log ( " The virtual_submit_frame hook is not set \n " ) ;
goto err ;
}
output - > scanout_plane = drm_virtual_plane_create ( b , output ) ;
if ( ! output - > scanout_plane ) {
weston_log ( " Failed to find primary plane for output %s \n " ,
output - > base . name ) ;
return - 1 ;
}
if ( drm_output_init_egl ( output , b ) < 0 ) {
weston_log ( " Failed to init output gl state \n " ) ;
goto err ;
}
output - > base . start_repaint_loop = drm_virtual_output_start_repaint_loop ;
output - > base . repaint = drm_virtual_output_repaint ;
output - > base . assign_planes = drm_assign_planes ;
output - > base . set_dpms = NULL ;
output - > base . switch_mode = NULL ;
output - > base . gamma_size = 0 ;
output - > base . set_gamma = NULL ;
weston_compositor_stack_plane ( b - > compositor ,
& output - > scanout_plane - > base ,
& b - > compositor - > primary_plane ) ;
return 0 ;
err :
return - 1 ;
}
static int
drm_virtual_output_disable ( struct weston_output * base )
{
struct drm_output * output = to_drm_output ( base ) ;
assert ( output - > virtual ) ;
if ( output - > base . enabled )
drm_virtual_output_deinit ( & output - > base ) ;
return 0 ;
}
static struct weston_output *
drm_virtual_output_create ( struct weston_compositor * c , char * name )
{
struct drm_output * output ;
output = zalloc ( sizeof * output ) ;
if ( ! output )
return NULL ;
output - > virtual = true ;
output - > gbm_bo_flags = GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING ;
weston_output_init ( & output - > base , c , name ) ;
output - > base . enable = drm_virtual_output_enable ;
output - > base . destroy = drm_virtual_output_destroy ;
output - > base . disable = drm_virtual_output_disable ;
output - > base . attach_head = NULL ;
output - > state_cur = drm_output_state_alloc ( output , NULL ) ;
weston_compositor_add_pending_output ( & output - > base , c ) ;
return & output - > base ;
}
static uint32_t
drm_virtual_output_set_gbm_format ( struct weston_output * base ,
const char * gbm_format )
{
struct drm_output * output = to_drm_output ( base ) ;
struct drm_backend * b = to_drm_backend ( base - > compositor ) ;
if ( parse_gbm_format ( gbm_format , b - > gbm_format , & output - > gbm_format ) = = - 1 )
output - > gbm_format = b - > gbm_format ;
return output - > gbm_format ;
}
static void
drm_virtual_output_set_submit_frame_cb ( struct weston_output * output_base ,
submit_frame_cb cb )
{
struct drm_output * output = to_drm_output ( output_base ) ;
output - > virtual_submit_frame = cb ;
}
static int
drm_virtual_output_get_fence_fd ( struct weston_output * output_base )
{
return gl_renderer - > create_fence_fd ( output_base ) ;
}
static void
drm_virtual_output_buffer_released ( struct drm_fb * fb )
{
drm_fb_unref ( fb ) ;
}
static void
drm_virtual_output_finish_frame ( struct weston_output * output_base ,
struct timespec * stamp ,
uint32_t presented_flags )
{
struct drm_output * output = to_drm_output ( output_base ) ;
struct drm_plane_state * ps ;
wl_list_for_each ( ps , & output - > state_cur - > plane_list , link )
ps - > complete = true ;
drm_output_state_free ( output - > state_last ) ;
output - > state_last = NULL ;
weston_output_finish_frame ( & output - > base , stamp , presented_flags ) ;
/* We can't call this from frame_notify, because the output's
* repaint needed flag is cleared just after that */
if ( output - > recorder )
weston_output_schedule_repaint ( & output - > base ) ;
}
static const struct weston_drm_output_api api = {
drm_output_set_mode ,
drm_output_set_gbm_format ,
drm_output_set_seat ,
} ;
static const struct weston_drm_virtual_output_api virt_api = {
drm_virtual_output_create ,
drm_virtual_output_set_gbm_format ,
drm_virtual_output_set_submit_frame_cb ,
drm_virtual_output_get_fence_fd ,
drm_virtual_output_buffer_released ,
drm_virtual_output_finish_frame
} ;
static struct drm_backend *
drm_backend_create ( struct weston_compositor * compositor ,
struct weston_drm_backend_config * config )
@ -7079,6 +7430,14 @@ drm_backend_create(struct weston_compositor *compositor,
goto err_udev_monitor ;
}
ret = weston_plugin_api_register ( compositor ,
WESTON_DRM_VIRTUAL_OUTPUT_API_NAME ,
& virt_api , sizeof ( virt_api ) ) ;
if ( ret < 0 ) {
weston_log ( " Failed to register virtual output API. \n " ) ;
goto err_udev_monitor ;
}
return b ;
err_udev_monitor :