@ -26,13 +26,17 @@
# include "config.h"
# include <assert.h>
# include <errno.h>
# include <stdint.h>
# include <unistd.h>
# include <string.h>
# include <sys/mman.h>
# include <sys/types.h>
# include <libweston/libweston.h>
# include "linux-dmabuf.h"
# include "linux-dmabuf-unstable-v1-server-protocol.h"
# include "shared/os-compatibility.h"
# include "libweston-internal.h"
# include "shared/weston-drm-fourcc.h"
@ -395,6 +399,369 @@ err_out:
wl_resource_post_no_memory ( linux_dmabuf_resource ) ;
}
/** Creates dma-buf feedback tranche
*
* The tranche is added to dma - buf feedback ' s tranche list
*
* @ param dmabuf_feedback The dma - buf feedback object to which the tranche is added
* @ param format_table The dma - buf feedback formats table
* @ param target_device The target device of the new tranche
* @ param flags The flags of the new tranche
* @ param preference The preference of the new tranche
* @ return The tranche created , or NULL on failure
*/
WL_EXPORT struct weston_dmabuf_feedback_tranche *
weston_dmabuf_feedback_tranche_create ( struct weston_dmabuf_feedback * dmabuf_feedback ,
struct weston_dmabuf_feedback_format_table * format_table ,
dev_t target_device , uint32_t flags ,
enum weston_dmabuf_feedback_tranche_preference preference )
{
struct weston_dmabuf_feedback_tranche * tranche , * ptr ;
struct wl_list * pos ;
tranche = zalloc ( sizeof ( * tranche ) ) ;
if ( ! tranche ) {
weston_log ( " %s: out of memory \n " , __func__ ) ;
return NULL ;
}
tranche - > target_device = target_device ;
tranche - > flags = flags ;
tranche - > preference = preference ;
/* Get the formats indices array */
if ( flags = = 0 ) {
if ( wl_array_copy ( & tranche - > formats_indices ,
& format_table - > renderer_formats_indices ) < 0 ) {
weston_log ( " %s: out of memory \n " , __func__ ) ;
goto err ;
}
} else if ( flags & ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT ) {
/* we still don't support scanout tranches */
assert ( 0 ) ;
} else {
weston_log ( " error: for now we just have renderer and scanout "
" tranches, can't create other type of tranche \n " ) ;
goto err ;
}
/* The list of tranches is ordered by preference.
* Highest preference comes first . */
pos = & dmabuf_feedback - > tranche_list ;
wl_list_for_each ( ptr , & dmabuf_feedback - > tranche_list , link ) {
pos = & ptr - > link ;
if ( ptr - > preference < = tranche - > preference )
break ;
}
wl_list_insert ( pos - > prev , & tranche - > link ) ;
return tranche ;
err :
free ( tranche ) ;
return NULL ;
}
static void
weston_dmabuf_feedback_tranche_destroy ( struct weston_dmabuf_feedback_tranche * tranche )
{
wl_array_release ( & tranche - > formats_indices ) ;
wl_list_remove ( & tranche - > link ) ;
free ( tranche ) ;
}
static int
format_table_add_renderer_formats ( struct weston_dmabuf_feedback_format_table * format_table ,
const struct weston_drm_format_array * renderer_formats )
{
struct weston_drm_format * fmt ;
unsigned int num_modifiers ;
const uint64_t * modifiers ;
uint16_t index , * index_ptr ;
unsigned int size ;
unsigned int i ;
size = sizeof ( index ) *
weston_drm_format_array_count_pairs ( renderer_formats ) ;
if ( ! wl_array_add ( & format_table - > renderer_formats_indices , size ) ) {
weston_log ( " %s: out of memory \n " , __func__ ) ;
return - 1 ;
}
index = 0 ;
wl_array_for_each ( fmt , & renderer_formats - > arr ) {
modifiers = weston_drm_format_get_modifiers ( fmt , & num_modifiers ) ;
for ( i = 0 ; i < num_modifiers ; i + + ) {
format_table - > data [ index ] . format = fmt - > format ;
format_table - > data [ index ] . modifier = modifiers [ i ] ;
index + + ;
}
}
index = 0 ;
wl_array_for_each ( index_ptr , & format_table - > renderer_formats_indices )
* index_ptr = index + + ;
return 0 ;
}
/** Creates dma-buf feedback format table
*
* @ param renderer_formats The formats that compose the table
* @ return The dma - buf feedback format table , or NULL on failure
*/
WL_EXPORT struct weston_dmabuf_feedback_format_table *
weston_dmabuf_feedback_format_table_create ( const struct weston_drm_format_array * renderer_formats )
{
struct weston_dmabuf_feedback_format_table * format_table ;
int ret ;
format_table = zalloc ( sizeof ( * format_table ) ) ;
if ( ! format_table ) {
weston_log ( " %s: out of memory \n " , __func__ ) ;
return NULL ;
}
wl_array_init ( & format_table - > renderer_formats_indices ) ;
/* Creates formats file table and mmap it */
format_table - > size = weston_drm_format_array_count_pairs ( renderer_formats ) *
sizeof ( * format_table - > data ) ;
format_table - > fd = os_create_anonymous_file ( format_table - > size ) ;
if ( format_table - > fd < 0 ) {
weston_log ( " error: failed to create format table file: %s \n " ,
strerror ( errno ) ) ;
goto err_fd ;
}
format_table - > data = mmap ( NULL , format_table - > size , PROT_READ | PROT_WRITE ,
MAP_SHARED , format_table - > fd , 0 ) ;
if ( format_table - > data = = MAP_FAILED ) {
weston_log ( " error: mmap for format table failed: %s \n " ,
strerror ( errno ) ) ;
goto err_mmap ;
}
/* Add renderer formats to file table */
ret = format_table_add_renderer_formats ( format_table , renderer_formats ) ;
if ( ret < 0 )
goto err_formats ;
return format_table ;
err_formats :
munmap ( format_table - > data , format_table - > size ) ;
err_mmap :
close ( format_table - > fd ) ;
err_fd :
wl_array_release ( & format_table - > renderer_formats_indices ) ;
free ( format_table ) ;
return NULL ;
}
/** Destroys dma-buf feedback formats table
*
* @ param format_table The dma - buf feedback format table to destroy
*/
WL_EXPORT void
weston_dmabuf_feedback_format_table_destroy ( struct weston_dmabuf_feedback_format_table * format_table )
{
wl_array_release ( & format_table - > renderer_formats_indices ) ;
munmap ( format_table - > data , format_table - > size ) ;
close ( format_table - > fd ) ;
free ( format_table ) ;
}
/** Creates dma-buf feedback object
*
* @ param main_device The main device of the dma - buf feedback
* @ return The dma - buf feedback object created , or NULL on failure
*/
WL_EXPORT struct weston_dmabuf_feedback *
weston_dmabuf_feedback_create ( dev_t main_device )
{
struct weston_dmabuf_feedback * dmabuf_feedback ;
dmabuf_feedback = zalloc ( sizeof ( * dmabuf_feedback ) ) ;
if ( ! dmabuf_feedback ) {
weston_log ( " %s: out of memory \n " , __func__ ) ;
return NULL ;
}
dmabuf_feedback - > main_device = main_device ;
wl_list_init ( & dmabuf_feedback - > tranche_list ) ;
wl_list_init ( & dmabuf_feedback - > resource_list ) ;
return dmabuf_feedback ;
}
/** Destroy dma-buf feedback object
*
* @ param dmabuf_feedback The dma - buf feedback object to destroy
*/
WL_EXPORT void
weston_dmabuf_feedback_destroy ( struct weston_dmabuf_feedback * dmabuf_feedback )
{
struct weston_dmabuf_feedback_tranche * tranche , * tranche_tmp ;
struct wl_resource * res , * res_tmp ;
wl_list_for_each_safe ( tranche , tranche_tmp , & dmabuf_feedback - > tranche_list , link )
weston_dmabuf_feedback_tranche_destroy ( tranche ) ;
wl_resource_for_each_safe ( res , res_tmp , & dmabuf_feedback - > resource_list ) {
wl_list_remove ( wl_resource_get_link ( res ) ) ;
wl_list_init ( wl_resource_get_link ( res ) ) ;
}
free ( dmabuf_feedback ) ;
}
static void
weston_dmabuf_feedback_send ( struct weston_dmabuf_feedback * dmabuf_feedback ,
struct weston_dmabuf_feedback_format_table * format_table ,
struct wl_resource * res , bool advertise_format_table )
{
struct weston_dmabuf_feedback_tranche * tranche ;
struct wl_array device ;
dev_t * dev ;
/* main_device and target_device events need a dev_t as parameter,
* but we can ' t use this directly to communicate with the Wayland
* client . The solution is to use a wl_array , which is supported by
* Wayland , and add the dev_t as an element of the array . */
wl_array_init ( & device ) ;
dev = wl_array_add ( & device , sizeof ( * dev ) ) ;
if ( ! dev ) {
wl_resource_post_no_memory ( res ) ;
return ;
}
/* format_table event - In Weston, we never modify the dma-buf feedback
* format table . So we have this flag in order to advertise the format
* table only if the client has just subscribed to receive the events
* for this feedback object . When we need to re - send the feedback events
* for this client , the table event won ' t be sent . */
if ( advertise_format_table )
zwp_linux_dmabuf_feedback_v1_send_format_table ( res , format_table - > fd ,
format_table - > size ) ;
/* main_device event */
* dev = dmabuf_feedback - > main_device ;
zwp_linux_dmabuf_feedback_v1_send_main_device ( res , & device ) ;
/* send events for each tranche */
wl_list_for_each ( tranche , & dmabuf_feedback - > tranche_list , link ) {
/* tranche_target_device event */
* dev = tranche - > target_device ;
zwp_linux_dmabuf_feedback_v1_send_tranche_target_device ( res , & device ) ;
/* tranche_flags event */
zwp_linux_dmabuf_feedback_v1_send_tranche_flags ( res , tranche - > flags ) ;
/* tranche_formats event */
zwp_linux_dmabuf_feedback_v1_send_tranche_formats ( res , & tranche - > formats_indices ) ;
/* tranche_done_event */
zwp_linux_dmabuf_feedback_v1_send_tranche_done ( res ) ;
}
/* compositor_done_event */
zwp_linux_dmabuf_feedback_v1_send_done ( res ) ;
wl_array_release ( & device ) ;
}
static void
dmabuf_feedback_resource_destroy ( struct wl_resource * resource )
{
wl_list_remove ( wl_resource_get_link ( resource ) ) ;
}
static void
dmabuf_feedback_destroy ( struct wl_client * client , struct wl_resource * resource )
{
wl_resource_destroy ( resource ) ;
}
static const struct zwp_linux_dmabuf_feedback_v1_interface
zwp_linux_dmabuf_feedback_implementation = {
dmabuf_feedback_destroy
} ;
static struct wl_resource *
dmabuf_feedback_resource_create ( struct wl_resource * dmabuf_resource ,
struct wl_client * client , uint32_t dmabuf_feedback_id )
{
struct wl_resource * dmabuf_feedback_res ;
uint32_t version ;
version = wl_resource_get_version ( dmabuf_resource ) ;
dmabuf_feedback_res =
wl_resource_create ( client , & zwp_linux_dmabuf_feedback_v1_interface ,
version , dmabuf_feedback_id ) ;
if ( ! dmabuf_feedback_res )
return NULL ;
wl_list_init ( wl_resource_get_link ( dmabuf_feedback_res ) ) ;
wl_resource_set_implementation ( dmabuf_feedback_res ,
& zwp_linux_dmabuf_feedback_implementation ,
NULL , dmabuf_feedback_resource_destroy ) ;
return dmabuf_feedback_res ;
}
static void
linux_dmabuf_get_default_feedback ( struct wl_client * client ,
struct wl_resource * dmabuf_resource ,
uint32_t dmabuf_feedback_id )
{
struct weston_compositor * compositor =
wl_resource_get_user_data ( dmabuf_resource ) ;
struct wl_resource * dmabuf_feedback_resource ;
dmabuf_feedback_resource =
dmabuf_feedback_resource_create ( dmabuf_resource ,
client , dmabuf_feedback_id ) ;
if ( ! dmabuf_feedback_resource ) {
wl_resource_post_no_memory ( dmabuf_resource ) ;
return ;
}
weston_dmabuf_feedback_send ( compositor - > default_dmabuf_feedback ,
compositor - > dmabuf_feedback_format_table ,
dmabuf_feedback_resource , true ) ;
}
static void
linux_dmabuf_get_per_surface_feedback ( struct wl_client * client ,
struct wl_resource * dmabuf_resource ,
uint32_t dmabuf_feedback_id ,
struct wl_resource * surface_resource )
{
struct weston_surface * surface =
wl_resource_get_user_data ( surface_resource ) ;
struct wl_resource * dmabuf_feedback_resource ;
dmabuf_feedback_resource =
dmabuf_feedback_resource_create ( dmabuf_resource ,
client , dmabuf_feedback_id ) ;
if ( ! dmabuf_feedback_resource ) {
wl_resource_post_no_memory ( dmabuf_resource ) ;
return ;
}
/* Surface dma-buf feedback is dynamic and may need to be resent to
* clients when they change . So we need to keep the resources list */
wl_list_insert ( & surface - > dmabuf_feedback - > resource_list ,
wl_resource_get_link ( dmabuf_feedback_resource ) ) ;
weston_dmabuf_feedback_send ( surface - > dmabuf_feedback ,
surface - > compositor - > dmabuf_feedback_format_table ,
dmabuf_feedback_resource , true ) ;
}
/** Get the linux_dmabuf_buffer from a wl_buffer resource
*
* If the given wl_buffer resource was created through the linux_dmabuf
@ -469,7 +836,9 @@ linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer)
static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = {
linux_dmabuf_destroy ,
linux_dmabuf_create_params
linux_dmabuf_create_params ,
linux_dmabuf_get_default_feedback ,
linux_dmabuf_get_per_surface_feedback
} ;
static void
@ -494,12 +863,17 @@ bind_linux_dmabuf(struct wl_client *client,
wl_resource_set_implementation ( resource , & linux_dmabuf_implementation ,
compositor , NULL ) ;
/* Advertise formats/modifiers. From version 4 onwards, we should not send
* zwp_linux_dmabuf_v1_send_modifier and zwp_linux_dmabuf_v1_send_format
* events , instead we must send the dma - buf feedback events . */
if ( version > = 4 )
return ;
/* If we got here, it means that the renderer is able to import dma-buf
* buffers , and so it must have get_supported_formats ( ) set . */
assert ( compositor - > renderer - > get_supported_formats ! = NULL ) ;
supported_formats = compositor - > renderer - > get_supported_formats ( compositor ) ;
/* Advertise the formats/modifiers */
wl_array_for_each ( fmt , & supported_formats - > arr ) {
modifiers = weston_drm_format_get_modifiers ( fmt , & num_modifiers ) ;
for ( i = 0 ; i < num_modifiers ; i + + ) {
@ -533,9 +907,16 @@ bind_linux_dmabuf(struct wl_client *client,
WL_EXPORT int
linux_dmabuf_setup ( struct weston_compositor * compositor )
{
int max_version ;
/* If we were able to create the default dma-buf feedback for the
* compositor , that means that we are able to advertise dma - buf feedback
* events . In such case we support the version 4 of the protocol . */
max_version = compositor - > default_dmabuf_feedback ? 4 : 3 ;
if ( ! wl_global_create ( compositor - > wl_display ,
& zwp_linux_dmabuf_v1_interface , 3 ,
compositor , bind_linux_dmabuf ) )
& zwp_linux_dmabuf_v1_interface ,
max_version , compositor , bind_linux_dmabuf ) )
return - 1 ;
return 0 ;