Merge branch 'shm-mmap-offset' into 'main'

Add extended shared memory attach support

Closes #217

See merge request wayland/wayland!161
This commit is contained in:
Demi Marie Obenour 2025-09-16 23:51:46 -04:00
commit 118b02230d
4 changed files with 144 additions and 42 deletions

View file

@ -26,6 +26,7 @@ compiler_flags = [
'-Wstrict-prototypes',
'-Wmissing-prototypes',
'-fvisibility=hidden',
'-D_FILE_OFFSET_BITS=64',
]
cc = meson.get_compiler('c')

View file

@ -105,8 +105,10 @@ formats = {}
for ident, val in idents.items():
formats[drm_format_to_wl(ident)] = val.lower()
# Special case for ARGB8888 and XRGB8888
formats["argb8888"] = "0"
formats["xrgb8888"] = "1"
for (i, j) in enumerate(("argb8888", "xrgb8888")):
formats[j + "_new"] = formats[j]
descriptions[j + "_new"] = descriptions[j]
formats[j] = str(i)
print("Loaded {} formats from drm_fourcc.h".format(len(formats)), file=sys.stderr)

View file

@ -212,7 +212,7 @@
</request>
</interface>
<interface name="wl_shm_pool" version="2">
<interface name="wl_shm_pool" version="3">
<description summary="a shared memory pool">
The wl_shm_pool object encapsulates a piece of memory shared
between the compositor and client. Through the wl_shm_pool
@ -233,9 +233,16 @@
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
A buffer will keep a reference to the pool it was created from,
so it is valid to destroy the pool immediately after creating
a buffer from it.
The offset must not be negative. If it is negative, an
invalid_stride error is raised. This limits offsets to a
maximum of 2^31 - 1 bytes.
The version of the returned buffer is 1, regardless of the
version of the pool from which it was created.
</description>
<arg name="id" type="new_id" interface="wl_buffer" summary="buffer to create"/>
<arg name="offset" type="int" summary="buffer byte offset within the pool"/>
@ -267,6 +274,11 @@
file descriptor passed at creation time. It is the client's
responsibility to ensure that the file is at least as big as
the new pool size.
If the pool was created using version 3 or later of the wl_shm
interface, this request cannot be called once any buffers have
been created. Attempting to do so will result in an
already_mapped error.
</description>
<arg name="size" type="int" summary="new size of the pool, in bytes"/>
</request>
@ -292,27 +304,35 @@
<entry name="invalid_format" value="0" summary="buffer format is not known"/>
<entry name="invalid_stride" value="1" summary="invalid size or stride during pool or buffer creation"/>
<entry name="invalid_fd" value="2" summary="mmapping the file descriptor failed"/>
<entry name="already_mapped" value="3"
summary="a pool was created with version 3 or later of wl_shm, and an attempt was made to resize it after it had already been mapped"/>
</enum>
<enum name="format">
<description summary="pixel formats">
This describes the memory layout of an individual pixel.
All renderers should support argb8888 and xrgb8888 but any other
All compositors must support argb8888 and xrgb8888. Compositors
supporting wl_shm v3 must support argb8888_new and xrgb8888_new
as well, even with version 1 wl_shm_pool objects. All other
formats are optional and may not be supported by the particular
renderer in use.
The drm format codes match the macros defined in drm_fourcc.h, except
argb8888 and xrgb8888. The formats actually supported by the compositor
will be reported by the format event.
will be reported by the format event, and are not limited to those in
this enum. Starting with version 2, argb8888_new and xrgb8888_new values
are provided, which match the corresponding values in drm_fourcc.h.
For all wl_shm formats and unless specified in another protocol
extension, pre-multiplied alpha is used for pixel values.
All wl_shm formats use pre-multiplied alpha for pixel values, unless
another protocol extension states otherwise.
</description>
<!-- Note to protocol writers: don't update this list manually, instead
run the automated script that keeps it in sync with drm_fourcc.h. -->
<entry name="argb8888" value="0" summary="32-bit ARGB format, [31:0] A:R:G:B 8:8:8:8 little endian"/>
<entry name="xrgb8888" value="1" summary="32-bit RGB format, [31:0] x:R:G:B 8:8:8:8 little endian"/>
<entry name="argb8888_new" value="0x34325241" summary="[31:0] A:R:G:B 8:8:8:8 little endian"/>
<entry name="xrgb8888_new" value="0x34325258" summary="[31:0] x:R:G:B 8:8:8:8 little endian"/>
<entry name="c8" value="0x20203843" summary="8-bit color index format, [7:0] C"/>
<entry name="rgb332" value="0x38424752" summary="8-bit RGB format, [7:0] R:G:B 3:3:2"/>
<entry name="bgr233" value="0x38524742" summary="8-bit BGR format, [7:0] B:G:R 2:3:3"/>
@ -442,7 +462,12 @@
The pool can be used to create shared memory based buffer
objects. The server will mmap size bytes of the passed file
descriptor, to use as backing memory for the pool.
descriptor, to use as backing memory for the pool. If it is
unable to do so, an invalid_fd error is raised.
The size must be greater than zero. Sizes less or equal to zero
raise an invalid_stride error. This prevents the creation of
pools larger than 2^31 - 1 bytes.
</description>
<arg name="id" type="new_id" interface="wl_shm_pool" summary="pool to create"/>
<arg name="fd" type="fd" summary="file descriptor for the pool"/>
@ -468,6 +493,35 @@
Objects created via this interface remain unaffected.
</description>
</request>
<!-- Version 3 additions -->
<request name="create_pool2" since="3">
<description summary="create a shm pool">
Create a new wl_shm_pool object.
The pool can be used to create shared memory based buffer
objects. The server will mmap size bytes of the passed file
descriptor at the provided offset to use as backing memory for
the pool. If it is unable to do so, an invalid_fd error is
raised.
This request provides a strict superset of the functionality
provided by create_pool. In particular, calling this request
with offset_lo and offset_hi set to 0 is equivalent to calling
create_pool.
If the size is zero or greater than 2^31 - 1, an invalid_stride
error is raised. Compositors are not allowed to impose
arbitrary limits on the offset, beyond those imposed by the
operating system. In particular, the offset may be negative
when cast to an off_t, and may be larger than 2^32 - 1.
</description>
<arg name="id" type="new_id" interface="wl_shm_pool" summary="pool to create"/>
<arg name="fd" type="fd" summary="file descriptor for the pool"/>
<arg name="size" type="uint" summary="pool size, in bytes"/>
<arg name="offset_lo" type="uint" summary="low-order 32 bits of offset for mmap, in bytes"/>
<arg name="offset_hi" type="uint" summary="high-order 32 bits of offset for mmap, in bytes"/>
</request>
</interface>
<interface name="wl_buffer" version="1">

View file

@ -36,6 +36,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
@ -67,8 +68,8 @@ struct wl_shm_pool {
#ifndef MREMAP_MAYMOVE
/* The following three fields are needed for mremap() emulation. */
int mmap_fd;
int mmap_flags;
int mmap_prot;
#else
bool mapped_yet;
#endif
bool sigbus_is_impossible;
};
@ -91,7 +92,7 @@ struct wl_shm_buffer {
int32_t width, height;
int32_t stride;
uint32_t format;
int offset;
int32_t offset;
struct wl_shm_pool *pool;
};
@ -111,7 +112,7 @@ shm_pool_grow_mapping(struct wl_shm_pool *pool)
#else
data = wl_os_mremap_maymove(pool->mmap_fd, pool->data, &pool->size,
pool->new_size, pool->mmap_prot,
pool->mmap_flags);
MAP_SHARED);
if (pool->size != 0 && pool->resource != NULL) {
wl_resource_post_error(pool->resource,
WL_SHM_ERROR_INVALID_FD,
@ -164,7 +165,8 @@ shm_pool_unref(struct wl_shm_pool *pool, bool external)
munmap(pool->data, pool->size);
#ifndef MREMAP_MAYMOVE
close(pool->mmap_fd);
if (pool->mmap_fd != -1)
close(pool->mmap_fd);
#endif
free(pool);
}
@ -214,22 +216,15 @@ static const struct wl_buffer_interface shm_buffer_interface = {
};
static bool
format_is_supported(struct wl_client *client, uint32_t format)
extra_format_is_supported(struct wl_client *client, uint32_t format)
{
struct wl_display *display = wl_client_get_display(client);
struct wl_array *formats;
struct wl_array *formats = wl_display_get_additional_shm_formats(display);
uint32_t *p;
switch (format) {
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_XRGB8888:
return true;
default:
formats = wl_display_get_additional_shm_formats(display);
wl_array_for_each(p, formats)
if (*p == format)
return true;
}
wl_array_for_each(p, formats)
if (*p == format)
return true;
return false;
}
@ -254,7 +249,18 @@ shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource,
struct wl_shm_pool *pool = wl_resource_get_user_data(resource);
struct wl_shm_buffer *buffer;
if (!format_is_supported(client, format)) {
switch (format) {
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_ARGB8888_NEW:
format = WL_SHM_FORMAT_ARGB8888;
break;
case WL_SHM_FORMAT_XRGB8888:
case WL_SHM_FORMAT_XRGB8888_NEW:
format = WL_SHM_FORMAT_XRGB8888;
break;
default:
if (extra_format_is_supported(client, format))
break;
wl_resource_post_error(resource,
WL_SHM_ERROR_INVALID_FORMAT,
"invalid format 0x%x", format);
@ -266,11 +272,21 @@ shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource,
offset > pool->size - stride * height) {
wl_resource_post_error(resource,
WL_SHM_ERROR_INVALID_STRIDE,
"invalid width, height or stride (%dx%d, %u)",
width, height, stride);
"invalid offset, width, height or stride "
"(%" PRIi32 ", %" PRIi32 "x%" PRIi32 ", %" PRIu32 ")",
offset, width, height, stride);
return;
}
if (wl_resource_get_version(resource) > 2) {
#ifndef MREMAP_MAYMOVE
close(pool->mmap_fd);
pool->mmap_fd = -1;
#else
pool->mapped_yet = true;
#endif
}
buffer = zalloc(sizeof *buffer);
if (buffer == NULL) {
wl_client_post_no_memory(client);
@ -327,6 +343,7 @@ shm_pool_resize(struct wl_client *client, struct wl_resource *resource,
int32_t size)
{
struct wl_shm_pool *pool = wl_resource_get_user_data(resource);
uint32_t version = wl_resource_get_version(resource);
if (size < pool->size) {
wl_resource_post_error(resource,
@ -335,6 +352,23 @@ shm_pool_resize(struct wl_client *client, struct wl_resource *resource,
return;
}
assert(pool->internal_refcount > 0);
assert(pool->external_refcount >= 0);
if (version > 2 &&
#ifndef MREMAP_MAYMOVE
pool->mmap_fd == -1
#else
pool->mapped_yet
#endif
) {
wl_resource_post_error(
resource,
WL_SHM_ERROR_ALREADY_MAPPED,
"Cannot resize a pool of version greater than 1 "
"once it has been mapped.");
return;
}
pool->new_size = size;
/* If the compositor has taken references on this pool it
@ -353,23 +387,28 @@ static const struct wl_shm_pool_interface shm_pool_interface = {
};
static void
shm_create_pool(struct wl_client *client, struct wl_resource *resource,
uint32_t id, int fd, int32_t size)
shm_create_pool2(struct wl_client *client, struct wl_resource *resource,
uint32_t id, int fd, uint32_t size, uint32_t offset_low,
uint32_t offset_high)
{
struct wl_shm_pool *pool;
struct stat statbuf;
uint64_t offset;
uint32_t version;
int seals;
int prot;
int flags;
uint32_t version;
if (size <= 0) {
if (size <= 0 || size > (uint32_t) INT32_MAX) {
wl_resource_post_error(resource,
WL_SHM_ERROR_INVALID_STRIDE,
"invalid size (%d)", size);
"invalid size (%" PRIu32 ")", size);
goto err_close;
}
static_assert((uint64_t)(off_t) UINT64_MAX == UINT64_MAX,
"libwayland-server must be built with 64-bit (or more) off_t");
version = wl_resource_get_version(resource);
offset = (uint64_t) offset_high << 32 | (uint64_t) offset_low;
pool = zalloc(sizeof *pool);
if (pool == NULL) {
wl_client_post_no_memory(client);
@ -393,9 +432,7 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource,
pool->external_refcount = 0;
pool->size = size;
pool->new_size = size;
prot = PROT_READ | PROT_WRITE;
flags = MAP_SHARED;
pool->data = mmap(NULL, size, prot, flags, fd, 0);
pool->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t) offset);
if (pool->data == MAP_FAILED) {
wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FD,
"failed mmap fd %d: %s", fd,
@ -405,8 +442,6 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource,
#ifndef MREMAP_MAYMOVE
/* We may need to keep the fd, prot and flags to emulate mremap(). */
pool->mmap_fd = fd;
pool->mmap_prot = prot;
pool->mmap_flags = flags;
#else
close(fd);
#endif
@ -439,9 +474,17 @@ shm_release(struct wl_client *client, struct wl_resource *resource)
wl_resource_destroy(resource);
}
static void
shm_create_pool(struct wl_client *client, struct wl_resource *resource,
uint32_t id, int fd, int32_t size)
{
shm_create_pool2(client, resource, id, fd, (uint32_t) size, 0, 0);
}
static const struct wl_shm_interface shm_interface = {
shm_create_pool,
shm_release,
.create_pool = shm_create_pool,
.release = shm_release,
.create_pool2 = shm_create_pool2,
};
static void
@ -463,6 +506,8 @@ bind_shm(struct wl_client *client,
wl_shm_send_format(resource, WL_SHM_FORMAT_ARGB8888);
wl_shm_send_format(resource, WL_SHM_FORMAT_XRGB8888);
wl_shm_send_format(resource, WL_SHM_FORMAT_ARGB8888_NEW);
wl_shm_send_format(resource, WL_SHM_FORMAT_XRGB8888_NEW);
additional_formats = wl_display_get_additional_shm_formats(display);
wl_array_for_each(p, additional_formats)