mirror of
				https://gitlab.freedesktop.org/wayland/wayland.git
				synced 2025-11-03 09:01:42 -05:00 
			
		
		
		
	shm: Allocate shm buffers through new wl_shm_pool interface
There's a big cost to setting up and tearing down a mmap and faulting in the pages to back it. For cases where we're continuously reallocating shm wl_buffers (resizing a surface, typically) it is a big performance improvement to be able to reuse a mmap area. This change makes the shm buffer allocation a two step process: first allocate a wl_shm_pool, then allocate a buffer from the pool. The wl_shm_pool encapsulate the shared memory pool, and lets clients allocate wl_buffers backed by chunks of that memory. Buffers are allocated at an offset into the pool, so it's possible to create multiple buffers from one pool, for example for icons or cursor images.
This commit is contained in:
		
							parent
							
								
									68cf7a1a42
								
							
						
					
					
						commit
						aa777e5b10
					
				
					 2 changed files with 156 additions and 63 deletions
				
			
		| 
						 | 
					@ -135,6 +135,46 @@
 | 
				
			||||||
    </request>
 | 
					    </request>
 | 
				
			||||||
  </interface>
 | 
					  </interface>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <interface name="wl_shm_pool" version="1">
 | 
				
			||||||
 | 
					    <description summary="a shared memory pool">
 | 
				
			||||||
 | 
					      The wl_shm_pool object encapsulates a piece of memory shared
 | 
				
			||||||
 | 
					      between the compsitor and client.  Through the wl_shm_pool
 | 
				
			||||||
 | 
					      object, the client can allocate shared memory wl_buffer objects.
 | 
				
			||||||
 | 
					      The objects will share the same underlying mapped memory.
 | 
				
			||||||
 | 
					      Reusing the mapped memory avoids the setup/teardown overhead and
 | 
				
			||||||
 | 
					      is useful when interactively resizing a surface or for many
 | 
				
			||||||
 | 
					      small buffers.
 | 
				
			||||||
 | 
					    </description>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <request name="create_buffer">
 | 
				
			||||||
 | 
					      <description summary="create wl_buffer from pool">
 | 
				
			||||||
 | 
						Create a wl_buffer from the pool.  The buffer is created a
 | 
				
			||||||
 | 
						offset bytes into the pool and has width and height as
 | 
				
			||||||
 | 
						specified.  The stride arguments specifies the number of bytes
 | 
				
			||||||
 | 
						from beginning of one row to the beginning of the next.  The
 | 
				
			||||||
 | 
						format is the pixel format of the buffer and must be one of
 | 
				
			||||||
 | 
						those advertised through the wl_shm.format event.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						A buffer will keep a reference to the pool it was created from
 | 
				
			||||||
 | 
						so it is valid to destroy the pool immediatedly after creating
 | 
				
			||||||
 | 
						a buffer from it.
 | 
				
			||||||
 | 
					      </description>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <arg name="id" type="new_id" interface="wl_buffer"/>
 | 
				
			||||||
 | 
					      <arg name="offset" type="int"/>
 | 
				
			||||||
 | 
					      <arg name="width" type="int"/>
 | 
				
			||||||
 | 
					      <arg name="height" type="int"/>
 | 
				
			||||||
 | 
					      <arg name="stride" type="int"/>
 | 
				
			||||||
 | 
					      <arg name="format" type="uint"/>
 | 
				
			||||||
 | 
					    </request>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <request name="destroy" type="destructor">
 | 
				
			||||||
 | 
					      <description summary="destroy the pool">
 | 
				
			||||||
 | 
						Destroy the pool.
 | 
				
			||||||
 | 
					      </description>
 | 
				
			||||||
 | 
					    </request>
 | 
				
			||||||
 | 
					  </interface>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <interface name="wl_shm" version="1">
 | 
					  <interface name="wl_shm" version="1">
 | 
				
			||||||
    <description summary="shared memory support">
 | 
					    <description summary="shared memory support">
 | 
				
			||||||
      Support for shared memory buffers.
 | 
					      Support for shared memory buffers.
 | 
				
			||||||
| 
						 | 
					@ -151,22 +191,17 @@
 | 
				
			||||||
      <entry name="xrgb8888" value="1"/>
 | 
					      <entry name="xrgb8888" value="1"/>
 | 
				
			||||||
    </enum>
 | 
					    </enum>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <request name="create_buffer">
 | 
					    <request name="create_pool">
 | 
				
			||||||
      <description summary="create a wl_buffer">
 | 
					      <description summary="create a shm pool">
 | 
				
			||||||
	Transfer a shm buffer to the server.  The allocated buffer
 | 
						This creates wl_shm_pool object, which can be used to create
 | 
				
			||||||
	would include at least stride * height bytes starting at the
 | 
						shared memory based wl_buffer objects.  The server will mmap
 | 
				
			||||||
	beginning of fd.  The file descriptor is transferred over the
 | 
						size bytes of the passed fd, to use as backing memory for then
 | 
				
			||||||
	socket using AF_UNIX magical features. width, height, stride
 | 
						pool.
 | 
				
			||||||
	and format describe the respective properties of the pixel
 | 
					 | 
				
			||||||
	data contained in the buffer.
 | 
					 | 
				
			||||||
      </description>
 | 
					      </description>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <arg name="id" type="new_id" interface="wl_buffer"/>
 | 
					      <arg name="id" type="new_id" interface="wl_shm_pool"/>
 | 
				
			||||||
      <arg name="fd" type="fd"/>
 | 
					      <arg name="fd" type="fd"/>
 | 
				
			||||||
      <arg name="width" type="int"/>
 | 
					      <arg name="size" type="int"/>
 | 
				
			||||||
      <arg name="height" type="int"/>
 | 
					 | 
				
			||||||
      <arg name="stride" type="int"/>
 | 
					 | 
				
			||||||
      <arg name="format" type="uint"/>
 | 
					 | 
				
			||||||
    </request>
 | 
					    </request>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <event name="format">
 | 
					    <event name="format">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,21 +33,40 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "wayland-server.h"
 | 
					#include "wayland-server.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct wl_shm_pool {
 | 
				
			||||||
 | 
						struct wl_resource resource;
 | 
				
			||||||
 | 
						int refcount;
 | 
				
			||||||
 | 
						char *data;
 | 
				
			||||||
 | 
						int size;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct wl_shm_buffer {
 | 
					struct wl_shm_buffer {
 | 
				
			||||||
	struct wl_buffer buffer;
 | 
						struct wl_buffer buffer;
 | 
				
			||||||
	int32_t stride;
 | 
						int32_t stride;
 | 
				
			||||||
	uint32_t format;
 | 
						uint32_t format;
 | 
				
			||||||
	void *data;
 | 
						void *data;
 | 
				
			||||||
 | 
						struct wl_shm_pool *pool;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					shm_pool_unref(struct wl_shm_pool *pool)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pool->refcount--;
 | 
				
			||||||
 | 
						if (pool->refcount)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						munmap(pool->data, pool->size);
 | 
				
			||||||
 | 
						free(pool);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
destroy_buffer(struct wl_resource *resource)
 | 
					destroy_buffer(struct wl_resource *resource)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct wl_shm_buffer *buffer =
 | 
						struct wl_shm_buffer *buffer =
 | 
				
			||||||
		container_of(resource, struct wl_shm_buffer, buffer.resource);
 | 
							container_of(resource, struct wl_shm_buffer, buffer.resource);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	munmap(buffer->data, buffer->stride * buffer->buffer.height);
 | 
						if (buffer->pool)
 | 
				
			||||||
 | 
							shm_pool_unref(buffer->pool);
 | 
				
			||||||
	free(buffer);
 | 
						free(buffer);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,43 +80,14 @@ static const struct wl_buffer_interface shm_buffer_interface = {
 | 
				
			||||||
	shm_buffer_destroy
 | 
						shm_buffer_destroy
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct wl_shm_buffer *
 | 
					 | 
				
			||||||
wl_shm_buffer_init(struct wl_client *client, uint32_t id,
 | 
					 | 
				
			||||||
		   int32_t width, int32_t height,
 | 
					 | 
				
			||||||
		   int32_t stride, uint32_t format, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct wl_shm_buffer *buffer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	buffer = calloc(1, sizeof *buffer);
 | 
					 | 
				
			||||||
	if (buffer == NULL)
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	buffer->buffer.width = width;
 | 
					 | 
				
			||||||
	buffer->buffer.height = height;
 | 
					 | 
				
			||||||
	buffer->format = format;
 | 
					 | 
				
			||||||
	buffer->stride = stride;
 | 
					 | 
				
			||||||
	buffer->data = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	buffer->buffer.resource.object.id = id;
 | 
					 | 
				
			||||||
	buffer->buffer.resource.object.interface = &wl_buffer_interface;
 | 
					 | 
				
			||||||
	buffer->buffer.resource.object.implementation = (void (**)(void))
 | 
					 | 
				
			||||||
		&shm_buffer_interface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	buffer->buffer.resource.data = buffer;
 | 
					 | 
				
			||||||
	buffer->buffer.resource.client = client;
 | 
					 | 
				
			||||||
	buffer->buffer.resource.destroy = destroy_buffer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return buffer;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
shm_create_buffer(struct wl_client *client, struct wl_resource *resource,
 | 
					shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource,
 | 
				
			||||||
		  uint32_t id, int fd, int32_t width, int32_t height,
 | 
							       uint32_t id, int32_t offset,
 | 
				
			||||||
		  int32_t stride, uint32_t format)
 | 
							       int32_t width, int32_t height,
 | 
				
			||||||
 | 
							       int32_t stride, uint32_t format)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct wl_shm_pool *pool = resource->data;
 | 
				
			||||||
	struct wl_shm_buffer *buffer;
 | 
						struct wl_shm_buffer *buffer;
 | 
				
			||||||
	void *data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (format) {
 | 
						switch (format) {
 | 
				
			||||||
	case WL_SHM_FORMAT_ARGB8888:
 | 
						case WL_SHM_FORMAT_ARGB8888:
 | 
				
			||||||
| 
						 | 
					@ -107,43 +97,111 @@ shm_create_buffer(struct wl_client *client, struct wl_resource *resource,
 | 
				
			||||||
		wl_resource_post_error(resource,
 | 
							wl_resource_post_error(resource,
 | 
				
			||||||
				       WL_SHM_ERROR_INVALID_FORMAT,
 | 
									       WL_SHM_ERROR_INVALID_FORMAT,
 | 
				
			||||||
				       "invalid format");
 | 
									       "invalid format");
 | 
				
			||||||
		close(fd);
 | 
					 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (width < 0 || height < 0 || stride < width) {
 | 
						if (offset < 0 || width <= 0 || height <= 0 || stride < width ||
 | 
				
			||||||
 | 
						    INT32_MAX / stride <= height ||
 | 
				
			||||||
 | 
						    offset > pool->size - stride * height) {
 | 
				
			||||||
		wl_resource_post_error(resource,
 | 
							wl_resource_post_error(resource,
 | 
				
			||||||
				       WL_SHM_ERROR_INVALID_STRIDE,
 | 
									       WL_SHM_ERROR_INVALID_STRIDE,
 | 
				
			||||||
				       "invalid width, height or stride (%dx%d, %u)",
 | 
									       "invalid width, height or stride (%dx%d, %u)",
 | 
				
			||||||
				       width, height, stride);
 | 
									       width, height, stride);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buffer = malloc(sizeof *buffer);
 | 
				
			||||||
 | 
						if (buffer == NULL) {
 | 
				
			||||||
 | 
							wl_resource_post_no_memory(resource);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buffer->buffer.width = width;
 | 
				
			||||||
 | 
						buffer->buffer.height = height;
 | 
				
			||||||
 | 
						buffer->format = format;
 | 
				
			||||||
 | 
						buffer->stride = stride;
 | 
				
			||||||
 | 
						buffer->data = pool->data + offset;
 | 
				
			||||||
 | 
						buffer->pool = pool;
 | 
				
			||||||
 | 
						pool->refcount++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buffer->buffer.resource.object.id = id;
 | 
				
			||||||
 | 
						buffer->buffer.resource.object.interface = &wl_buffer_interface;
 | 
				
			||||||
 | 
						buffer->buffer.resource.object.implementation = (void (**)(void))
 | 
				
			||||||
 | 
							&shm_buffer_interface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buffer->buffer.resource.data = buffer;
 | 
				
			||||||
 | 
						buffer->buffer.resource.client = resource->client;
 | 
				
			||||||
 | 
						buffer->buffer.resource.destroy = destroy_buffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wl_client_add_resource(client, &buffer->buffer.resource);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					destroy_pool(struct wl_resource *resource)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct wl_shm_pool *pool = resource->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						shm_pool_unref(pool);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					shm_pool_destroy(struct wl_client *client, struct wl_resource *resource)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						wl_resource_destroy(resource, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct wl_shm_pool_interface shm_pool_interface = {
 | 
				
			||||||
 | 
						shm_pool_create_buffer,
 | 
				
			||||||
 | 
						shm_pool_destroy
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					shm_create_pool(struct wl_client *client, struct wl_resource *resource,
 | 
				
			||||||
 | 
							uint32_t id, int fd, int32_t size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct wl_shm_pool *pool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pool = malloc(sizeof *pool);
 | 
				
			||||||
 | 
						if (pool == NULL) {
 | 
				
			||||||
 | 
							wl_resource_post_no_memory(resource);
 | 
				
			||||||
		close(fd);
 | 
							close(fd);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data = mmap(NULL, stride * height,
 | 
						if (size <= 0) {
 | 
				
			||||||
		    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 | 
							wl_resource_post_error(resource,
 | 
				
			||||||
 | 
									       WL_SHM_ERROR_INVALID_STRIDE,
 | 
				
			||||||
 | 
									       "invalid size (%d)", size);
 | 
				
			||||||
 | 
							close(fd);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pool->refcount = 1;
 | 
				
			||||||
 | 
						pool->size = size;
 | 
				
			||||||
 | 
						pool->data = mmap(NULL, size,
 | 
				
			||||||
 | 
								  PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 | 
				
			||||||
	close(fd);
 | 
						close(fd);
 | 
				
			||||||
	if (data == MAP_FAILED) {
 | 
						if (pool->data == MAP_FAILED) {
 | 
				
			||||||
		wl_resource_post_error(resource,
 | 
							wl_resource_post_error(resource,
 | 
				
			||||||
				       WL_SHM_ERROR_INVALID_FD,
 | 
									       WL_SHM_ERROR_INVALID_FD,
 | 
				
			||||||
				       "failed mmap fd %d", fd);
 | 
									       "failed mmap fd %d", fd);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	buffer = wl_shm_buffer_init(client, id,
 | 
						pool->resource.object.id = id;
 | 
				
			||||||
				    width, height, stride, format, data);
 | 
						pool->resource.object.interface = &wl_shm_pool_interface;
 | 
				
			||||||
	if (buffer == NULL) {
 | 
						pool->resource.object.implementation =
 | 
				
			||||||
		munmap(data, stride * height);
 | 
							(void (**)(void)) &shm_pool_interface;
 | 
				
			||||||
		wl_resource_post_no_memory(resource);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wl_client_add_resource(client, &buffer->buffer.resource);
 | 
						pool->resource.data = pool;
 | 
				
			||||||
 | 
						pool->resource.client = client;
 | 
				
			||||||
 | 
						pool->resource.destroy = destroy_pool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wl_client_add_resource(client, &pool->resource);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct wl_shm_interface shm_interface = {
 | 
					static const struct wl_shm_interface shm_interface = {
 | 
				
			||||||
	shm_create_buffer
 | 
						shm_create_pool
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue