diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index e4aadc106..af62e8191 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -3,6 +3,7 @@ #include #include #include +#include "backend/backend.h" #include "backend/drm/drm.h" #include "backend/drm/fb.h" #include "backend/drm/renderer.h" diff --git a/backend/multi/backend.c b/backend/multi/backend.c index 740e1d6fa..caa6089b1 100644 --- a/backend/multi/backend.c +++ b/backend/multi/backend.c @@ -2,11 +2,15 @@ #include #include #include +#include +#include #include #include #include +#include "render/wlr_renderer.h" #include "backend/backend.h" #include "backend/multi.h" +#include "render/allocator/allocator.h" struct subbackend_state { struct wlr_backend *backend; @@ -58,6 +62,7 @@ static void multi_backend_destroy(struct wlr_backend *wlr_backend) { wl_container_of(backend->backends.next, sub, link); wlr_backend_destroy(sub->backend); } + wlr_multi_gpu_destroy(backend->multi_gpu); free(backend); } @@ -118,6 +123,7 @@ struct wlr_backend *wlr_multi_backend_create(struct wl_event_loop *loop) { } wl_list_init(&backend->backends); + backend->multi_gpu = wlr_multi_gpu_create(); wlr_backend_init(&backend->backend, &backend_impl); wl_signal_init(&backend->events.backend_add); @@ -225,3 +231,101 @@ void wlr_multi_for_each_backend(struct wlr_backend *_backend, callback(sub->backend, data); } } + +/* + * Create a wlr_multi_gpu struct and populate it with a renderer and allocator for each + * device in the system. This is done by finding all DRM nodes using drmGetDevices2. + */ +struct wlr_multi_gpu *wlr_multi_gpu_create(void) { + int flags = 0; + struct wlr_multi_gpu *multi_gpu = NULL; + int devices_len = drmGetDevices2(flags, NULL, 0); + + if (devices_len < 0) { + wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); + return NULL; + } + drmDevice **devices = calloc(devices_len, sizeof(*devices)); + if (devices == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + goto out; + } + devices_len = drmGetDevices2(flags, devices, devices_len); + if (devices_len < 0) { + wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); + goto out; + } + + multi_gpu = calloc(1, sizeof(struct wlr_multi_gpu)); + if (!multi_gpu) { + goto out; + } + wl_list_init(&multi_gpu->devices); + + for (int i = 0; i < devices_len; i++) { + drmDevice *dev = devices[i]; + if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { + const char *name = dev->nodes[DRM_NODE_RENDER]; + wlr_log(WLR_DEBUG, "Opening DRM render node '%s'", name); + int fd = open(name, O_RDWR | O_CLOEXEC); + if (fd < 0) { + wlr_log_errno(WLR_ERROR, "Failed to open '%s'", name); + goto out; + } + + // Create a renderer/allocator and add it as a new device + struct wlr_renderer *renderer = renderer_autocreate_with_drm_fd(fd); + if (!renderer) { + wlr_log(WLR_ERROR, "Failed to create multi-GPU renderer"); + goto fail; + } + + struct wlr_allocator *allocator = + allocator_autocreate_with_drm_fd(WLR_BUFFER_CAP_DMABUF, renderer, fd); + if (!allocator) { + wlr_log(WLR_ERROR, "Failed to create multi-GPU allocator"); + wlr_renderer_destroy(renderer); + goto fail; + } + + struct wlr_multi_gpu_device *device = calloc(1, sizeof(struct wlr_multi_gpu_device)); + if (!device) { + wlr_allocator_destroy(allocator); + wlr_renderer_destroy(renderer); + goto fail; + } + wl_list_insert(&multi_gpu->devices, &device->link); + device->renderer = renderer; + device->allocator = allocator; + } + } + + goto out; + +fail: + wlr_multi_gpu_destroy(multi_gpu); + multi_gpu = NULL; + +out: + for (int i = 0; i < devices_len; i++) { + drmFreeDevice(&devices[i]); + } + if (devices) { + free(devices); + } + + return multi_gpu; +} + +void wlr_multi_gpu_destroy(struct wlr_multi_gpu *multi_gpu) { + struct wlr_multi_gpu_device *device; + // Remove and destroy all devices + wl_list_for_each(device, &multi_gpu->devices, link) { + wlr_allocator_destroy(device->allocator); + wlr_renderer_destroy(device->renderer); + wl_list_remove(&device->link); + free(device); + } + + free(multi_gpu); +} diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index cea53f441..30486beb8 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -107,6 +107,7 @@ struct wlr_drm_backend { /* Only initialized on multi-GPU setups */ struct wlr_drm_renderer mgpu_renderer; + struct wlr_multi_gpu *multi_gpu; struct wlr_session *session; diff --git a/include/backend/multi.h b/include/backend/multi.h index 3ffd81406..993ccbe4b 100644 --- a/include/backend/multi.h +++ b/include/backend/multi.h @@ -4,10 +4,31 @@ #include #include #include +#include +#include + +struct wlr_multi_gpu_device { + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; + struct wl_list link; +}; + +/* + * Helper struct for tracking multiple renderers. This solves the + * problem of us having many renderers (primary, plus individual + * secondary GPU drm renderers) but not tracking them in one location. + * We can use this struct to access renderers for each GPU in + * the system all from one place. Will be populated by the renderer + * the compositor makes, plus every time a drm mgpu renderer is made. + */ +struct wlr_multi_gpu { + struct wl_list devices; +}; struct wlr_multi_backend { struct wlr_backend backend; + struct wlr_multi_gpu *multi_gpu; struct wl_list backends; struct wl_listener event_loop_destroy; diff --git a/include/wlr/backend/multi.h b/include/wlr/backend/multi.h index c4322d98b..8ae5e4763 100644 --- a/include/wlr/backend/multi.h +++ b/include/wlr/backend/multi.h @@ -32,4 +32,7 @@ bool wlr_multi_is_empty(struct wlr_backend *backend); void wlr_multi_for_each_backend(struct wlr_backend *backend, void (*callback)(struct wlr_backend *backend, void *data), void *data); +struct wlr_multi_gpu *wlr_multi_gpu_create(void); +void wlr_multi_gpu_destroy(struct wlr_multi_gpu *multi_gpu); + #endif diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index 08333a529..5a31ca7bb 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -14,13 +14,10 @@ #include #include #include +#include -struct wlr_backend; struct wlr_renderer_impl; -struct wlr_drm_format_set; struct wlr_buffer; -struct wlr_box; -struct wlr_fbox; /** * A renderer for basic 2D operations. @@ -39,6 +36,9 @@ struct wlr_renderer { // private state const struct wlr_renderer_impl *impl; + + /* The GPU list we are a part of, may be null if not created from multi backend */ + struct wlr_multi_gpu *multi_gpu; }; /** diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 513fecbd7..d20366a95 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -25,6 +25,7 @@ #endif // WLR_HAS_VULKAN_RENDERER #include "backend/backend.h" +#include "backend/multi.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" #include "util/env.h" @@ -285,6 +286,13 @@ out: if (own_drm_fd && drm_fd >= 0) { close(drm_fd); } + // If we have a multi GPU environment, then track this renderer + // for cross-GPU imports. + if (renderer && backend && wlr_backend_is_multi(backend)) { + struct wlr_multi_backend *multi = (struct wlr_multi_backend *)backend; + renderer->multi_gpu = multi->multi_gpu; + } + return renderer; }