Просмотр исходного кода

wayland: Use a pool to allocate icon image buffers

Allocate the buffers from a pool to avoid a separate mmap for each image.
Frank Praznik 4 месяцев назад
Родитель
Сommit
950a9a72ab

+ 0 - 52
src/video/wayland/SDL_waylandshmbuffer.c

@@ -114,58 +114,6 @@ static struct wl_buffer_listener buffer_listener = {
     buffer_handle_release
     buffer_handle_release
 };
 };
 
 
-bool Wayland_AllocSHMBuffer(int width, int height, Wayland_SHMBuffer *shmBuffer)
-{
-    SDL_VideoDevice *vd = SDL_GetVideoDevice();
-    SDL_VideoData *data = vd->internal;
-    const Uint32 SHM_FMT = WL_SHM_FORMAT_ARGB8888;
-
-    if (!shmBuffer) {
-        return SDL_InvalidParamError("shmBuffer");
-    }
-
-    const int stride = width * 4;
-    shmBuffer->shm_data_size = stride * height;
-
-    const int shm_fd = CreateTempFD(shmBuffer->shm_data_size);
-    if (shm_fd < 0) {
-        return SDL_SetError("Creating SHM buffer failed.");
-    }
-
-    shmBuffer->shm_data = mmap(NULL, shmBuffer->shm_data_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
-    if (shmBuffer->shm_data == MAP_FAILED) {
-        shmBuffer->shm_data = NULL;
-        close(shm_fd);
-        return SDL_SetError("mmap() failed.");
-    }
-
-    SDL_assert(shmBuffer->shm_data != NULL);
-
-    struct wl_shm_pool *shm_pool = wl_shm_create_pool(data->shm, shm_fd, shmBuffer->shm_data_size);
-    shmBuffer->wl_buffer = wl_shm_pool_create_buffer(shm_pool, 0, width, height, stride, SHM_FMT);
-    wl_buffer_add_listener(shmBuffer->wl_buffer, &buffer_listener, shmBuffer);
-
-    wl_shm_pool_destroy(shm_pool);
-    close(shm_fd);
-
-    return true;
-}
-
-void Wayland_ReleaseSHMBuffer(Wayland_SHMBuffer *shmBuffer)
-{
-    if (shmBuffer) {
-        if (shmBuffer->wl_buffer) {
-            wl_buffer_destroy(shmBuffer->wl_buffer);
-            shmBuffer->wl_buffer = NULL;
-        }
-        if (shmBuffer->shm_data) {
-            munmap(shmBuffer->shm_data, shmBuffer->shm_data_size);
-            shmBuffer->shm_data = NULL;
-        }
-        shmBuffer->shm_data_size = 0;
-    }
-}
-
 struct Wayland_SHMPool
 struct Wayland_SHMPool
 {
 {
     struct wl_shm_pool *shm_pool;
     struct wl_shm_pool *shm_pool;

+ 0 - 11
src/video/wayland/SDL_waylandshmbuffer.h

@@ -24,19 +24,8 @@
 #ifndef SDL_waylandshmbuffer_h_
 #ifndef SDL_waylandshmbuffer_h_
 #define SDL_waylandshmbuffer_h_
 #define SDL_waylandshmbuffer_h_
 
 
-typedef struct
-{
-    struct wl_buffer *wl_buffer;
-    void *shm_data;
-    int shm_data_size;
-} Wayland_SHMBuffer;
-
 typedef struct Wayland_SHMPool Wayland_SHMPool;
 typedef struct Wayland_SHMPool Wayland_SHMPool;
 
 
-// Allocates an SHM buffer with the format WL_SHM_FORMAT_ARGB8888
-extern bool Wayland_AllocSHMBuffer(int width, int height, Wayland_SHMBuffer *shmBuffer);
-extern void Wayland_ReleaseSHMBuffer(Wayland_SHMBuffer *shmBuffer);
-
 extern Wayland_SHMPool *Wayland_AllocSHMPool(int size);
 extern Wayland_SHMPool *Wayland_AllocSHMPool(int size);
 extern struct wl_buffer *Wayland_AllocBufferFromPool(Wayland_SHMPool *shmPool, int width, int height, void **data);
 extern struct wl_buffer *Wayland_AllocBufferFromPool(Wayland_SHMPool *shmPool, int width, int height, void **data);
 extern void Wayland_ReleaseSHMPool(Wayland_SHMPool *shmPool);
 extern void Wayland_ReleaseSHMPool(Wayland_SHMPool *shmPool);

+ 55 - 16
src/video/wayland/SDL_waylandwindow.c

@@ -2953,9 +2953,18 @@ void Wayland_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
     }
     }
 }
 }
 
 
+static int icon_sort_callback(const void *a, const void *b)
+{
+    SDL_Surface *s1 = (SDL_Surface *)a;
+    SDL_Surface *s2 = (SDL_Surface *)b;
+
+    return (s1->w * s1->h) <= (s2->w * s2->h) ? -1 : 1;
+}
+
 bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon)
 bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon)
 {
 {
     SDL_WindowData *wind = window->internal;
     SDL_WindowData *wind = window->internal;
+    Wayland_SHMPool *shm_pool = NULL;
     struct xdg_toplevel *toplevel = NULL;
     struct xdg_toplevel *toplevel = NULL;
 
 
     if (!_this->internal->xdg_toplevel_icon_manager_v1) {
     if (!_this->internal->xdg_toplevel_icon_manager_v1) {
@@ -2979,52 +2988,75 @@ bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa
     }
     }
 
 
     for (int i = 0; i < wind->icon_buffer_count; ++i) {
     for (int i = 0; i < wind->icon_buffer_count; ++i) {
-        Wayland_ReleaseSHMBuffer(&wind->icon_buffers[i]);
+        wl_buffer_destroy(wind->icon_buffers[i]);
     }
     }
-    SDL_free(wind->icon_buffers);
     wind->icon_buffer_count = 0;
     wind->icon_buffer_count = 0;
 
 
     wind->xdg_toplevel_icon_v1 = xdg_toplevel_icon_manager_v1_create_icon(_this->internal->xdg_toplevel_icon_manager_v1);
     wind->xdg_toplevel_icon_v1 = xdg_toplevel_icon_manager_v1_create_icon(_this->internal->xdg_toplevel_icon_manager_v1);
-    wind->icon_buffers = SDL_calloc(image_count, sizeof(Wayland_SHMBuffer));
+    wind->icon_buffers = SDL_realloc(wind->icon_buffers, image_count * sizeof(struct wl_buffer *));
     if (!wind->icon_buffers) {
     if (!wind->icon_buffers) {
         goto failure_cleanup;
         goto failure_cleanup;
     }
     }
 
 
+    // Calculate the size of the buffer pool.
+    size_t pool_size = 0;
+    for (int i = 0; i < image_count; ++i) {
+        // Ignore non-square images; if we got here, we know that at least the base image is square.
+        if (images[i]->w == images[i]->h) {
+            pool_size += images[i]->w * images[i]->h * 4;
+        } else {
+            SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "wayland: icon width and height must be equal, got %ix%i for image level %i; skipping", images[i]->w, images[i]->h, i);
+        }
+    }
+
+    // Sort the images in ascending order by size.
+    SDL_qsort(images, image_count, sizeof(SDL_Surface *), icon_sort_callback);
+
+    shm_pool = Wayland_AllocSHMPool(pool_size);
+    if (!shm_pool) {
+        SDL_SetError("wayland: failed to allocate SHM pool for the icon");
+        goto failure_cleanup;
+    }
+
     for (int i = 0; i < image_count; ++i) {
     for (int i = 0; i < image_count; ++i) {
         if (images[i]->w == images[i]->h) {
         if (images[i]->w == images[i]->h) {
             SDL_Surface *surface = images[i];
             SDL_Surface *surface = images[i];
-            Wayland_SHMBuffer *buffer = &wind->icon_buffers[wind->icon_buffer_count];
 
 
-            if (!Wayland_AllocSHMBuffer(surface->w, surface->h, buffer)) {
+            // Choose the largest image for each integer scale, ignoring any below the base size.
+            const int scale = (int)SDL_floor((double)surface->w / (double)icon->w);
+            if (!scale) {
+                continue;
+            }
+
+            void *buffer_mem;
+            struct wl_buffer *buffer = Wayland_AllocBufferFromPool(shm_pool, surface->w, surface->h, &buffer_mem);
+            if (!buffer) {
                 SDL_SetError("wayland: failed to allocate SHM buffer for the icon");
                 SDL_SetError("wayland: failed to allocate SHM buffer for the icon");
                 goto failure_cleanup;
                 goto failure_cleanup;
             }
             }
 
 
+            wind->icon_buffers[wind->icon_buffer_count++] = buffer;
+
             if (surface->format != SDL_PIXELFORMAT_ARGB8888) {
             if (surface->format != SDL_PIXELFORMAT_ARGB8888) {
                 surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
                 surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
                 if (!surface) {
                 if (!surface) {
+                    SDL_SetError("wayland: unable to convert surface to ARGB8888 format");
                     goto failure_cleanup;
                     goto failure_cleanup;
                 }
                 }
             }
             }
 
 
-            SDL_PremultiplyAlpha(surface->w, surface->h, surface->format, surface->pixels, surface->pitch, SDL_PIXELFORMAT_ARGB8888, buffer->shm_data, surface->w * 4, true);
+            SDL_PremultiplyAlpha(surface->w, surface->h, surface->format, surface->pixels, surface->pitch,
+                                 SDL_PIXELFORMAT_ARGB8888, buffer_mem, surface->w * 4, true);
 
 
-            const int scale = (int)SDL_ceil((double)surface->w / (double)icon->w);
-            xdg_toplevel_icon_v1_add_buffer(wind->xdg_toplevel_icon_v1, buffer->wl_buffer, scale);
+            xdg_toplevel_icon_v1_add_buffer(wind->xdg_toplevel_icon_v1, buffer, scale);
 
 
             // Clean up the temporary conversion surface.
             // Clean up the temporary conversion surface.
             if (surface != images[i]) {
             if (surface != images[i]) {
                 SDL_DestroySurface(surface);
                 SDL_DestroySurface(surface);
             }
             }
-
-            wind->icon_buffer_count++;
-        } else {
-            SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "wayland: icon width and height must be equal, got %ix%i for image level %i; skipping", images[i]->w, images[i]->h, i);
         }
         }
     }
     }
 
 
-    SDL_free(images);
-
 #ifdef HAVE_LIBDECOR_H
 #ifdef HAVE_LIBDECOR_H
     if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR && wind->shell_surface.libdecor.frame) {
     if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR && wind->shell_surface.libdecor.frame) {
         toplevel = libdecor_frame_get_xdg_toplevel(wind->shell_surface.libdecor.frame);
         toplevel = libdecor_frame_get_xdg_toplevel(wind->shell_surface.libdecor.frame);
@@ -3038,17 +3070,24 @@ bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa
         xdg_toplevel_icon_manager_v1_set_icon(_this->internal->xdg_toplevel_icon_manager_v1, toplevel, wind->xdg_toplevel_icon_v1);
         xdg_toplevel_icon_manager_v1_set_icon(_this->internal->xdg_toplevel_icon_manager_v1, toplevel, wind->xdg_toplevel_icon_v1);
     }
     }
 
 
+    Wayland_ReleaseSHMPool(shm_pool);
+    SDL_free(images);
+
     return true;
     return true;
 
 
 failure_cleanup:
 failure_cleanup:
 
 
+    SDL_free(images);
+
     if (wind->xdg_toplevel_icon_v1) {
     if (wind->xdg_toplevel_icon_v1) {
         xdg_toplevel_icon_v1_destroy(wind->xdg_toplevel_icon_v1);
         xdg_toplevel_icon_v1_destroy(wind->xdg_toplevel_icon_v1);
         wind->xdg_toplevel_icon_v1 = NULL;
         wind->xdg_toplevel_icon_v1 = NULL;
     }
     }
 
 
+    Wayland_ReleaseSHMPool(shm_pool);
+
     for (int i = 0; i < wind->icon_buffer_count; ++i) {
     for (int i = 0; i < wind->icon_buffer_count; ++i) {
-        Wayland_ReleaseSHMBuffer(&wind->icon_buffers[i]);
+        wl_buffer_destroy(wind->icon_buffers[i]);
     }
     }
     SDL_free(wind->icon_buffers);
     SDL_free(wind->icon_buffers);
     wind->icon_buffers = NULL;
     wind->icon_buffers = NULL;
@@ -3257,7 +3296,7 @@ void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
         }
         }
 
 
         for (int i = 0; i < wind->icon_buffer_count; ++i) {
         for (int i = 0; i < wind->icon_buffer_count; ++i) {
-            Wayland_ReleaseSHMBuffer(&wind->icon_buffers[i]);
+            wl_buffer_destroy(wind->icon_buffers[i]);
         }
         }
         SDL_free(wind->icon_buffers);
         SDL_free(wind->icon_buffers);
         wind->icon_buffer_count = 0;
         wind->icon_buffer_count = 0;

+ 1 - 1
src/video/wayland/SDL_waylandwindow.h

@@ -127,7 +127,7 @@ struct SDL_WindowData
     char *app_id;
     char *app_id;
     double scale_factor;
     double scale_factor;
 
 
-    Wayland_SHMBuffer *icon_buffers;
+    struct wl_buffer **icon_buffers;
     int icon_buffer_count;
     int icon_buffer_count;
 
 
     // Keyboard, pointer, and touch focus refcount.
     // Keyboard, pointer, and touch focus refcount.