diff --git a/src/gallium/drivers/nouveau/nouveau_screen.c b/src/gallium/drivers/nouveau/nouveau_screen.c index 21b31e0..088ce9b 100644 --- a/src/gallium/drivers/nouveau/nouveau_screen.c +++ b/src/gallium/drivers/nouveau/nouveau_screen.c @@ -144,6 +144,7 @@ nouveau_screen_init(struct nouveau_screen *screen, struct nouveau_device *dev) if (nv_dbg) nouveau_mesa_debug = atoi(nv_dbg); + pipe_reference_init(&screen->reference, 1); if (dev->chipset < 0xc0) { data = &nv04_data; size = sizeof(nv04_data); diff --git a/src/gallium/drivers/nouveau/nouveau_screen.h b/src/gallium/drivers/nouveau/nouveau_screen.h index 7f15d10..f49f2c8 100644 --- a/src/gallium/drivers/nouveau/nouveau_screen.h +++ b/src/gallium/drivers/nouveau/nouveau_screen.h @@ -3,6 +3,7 @@ #include "pipe/p_screen.h" #include "util/u_memory.h" +#include "util/u_inlines.h" #ifdef DEBUG # define NOUVEAU_ENABLE_DRIVER_STATISTICS @@ -17,6 +18,7 @@ struct nouveau_bo; struct nouveau_screen { struct pipe_screen base; + struct pipe_reference reference; struct nouveau_device *device; struct nouveau_object *channel; struct nouveau_client *client; @@ -107,6 +109,8 @@ nouveau_screen(struct pipe_screen *pscreen) return (struct nouveau_screen *)pscreen; } +boolean nouveau_drm_screen_unref(struct nouveau_screen *screen); + boolean nouveau_screen_bo_get_handle(struct pipe_screen *pscreen, struct nouveau_bo *bo, diff --git a/src/gallium/drivers/nouveau/nv30/nv30_screen.c b/src/gallium/drivers/nouveau/nv30/nv30_screen.c index 787802d..f85b874 100644 --- a/src/gallium/drivers/nouveau/nv30/nv30_screen.c +++ b/src/gallium/drivers/nouveau/nv30/nv30_screen.c @@ -298,6 +298,9 @@ nv30_screen_destroy(struct pipe_screen *pscreen) { struct nv30_screen *screen = nv30_screen(pscreen); + if (!nouveau_drm_screen_unref(&screen->base)) + return; + if (screen->base.fence.current && screen->base.fence.current->state >= NOUVEAU_FENCE_STATE_EMITTED) { nouveau_fence_wait(screen->base.fence.current); diff --git a/src/gallium/drivers/nouveau/nv50/nv50_screen.c b/src/gallium/drivers/nouveau/nv50/nv50_screen.c index 2b6ec3a..f2e1225 100644 --- a/src/gallium/drivers/nouveau/nv50/nv50_screen.c +++ b/src/gallium/drivers/nouveau/nv50/nv50_screen.c @@ -284,6 +284,9 @@ nv50_screen_destroy(struct pipe_screen *pscreen) { struct nv50_screen *screen = nv50_screen(pscreen); + if (!nouveau_drm_screen_unref(&screen->base)) + return; + if (screen->base.fence.current) { nouveau_fence_wait(screen->base.fence.current); nouveau_fence_ref (NULL, &screen->base.fence.current); diff --git a/src/gallium/drivers/nouveau/nvc0/nvc0_screen.c b/src/gallium/drivers/nouveau/nvc0/nvc0_screen.c index 752d72b..8443db2 100644 --- a/src/gallium/drivers/nouveau/nvc0/nvc0_screen.c +++ b/src/gallium/drivers/nouveau/nvc0/nvc0_screen.c @@ -328,6 +328,9 @@ nvc0_screen_destroy(struct pipe_screen *pscreen) { struct nvc0_screen *screen = nvc0_screen(pscreen); + if (!nouveau_drm_screen_unref(&screen->base)) + return; + if (screen->base.fence.current) { nouveau_fence_wait(screen->base.fence.current); nouveau_fence_ref(NULL, &screen->base.fence.current); diff --git a/src/gallium/winsys/nouveau/drm/nouveau_drm_winsys.c b/src/gallium/winsys/nouveau/drm/nouveau_drm_winsys.c index e4f27f6..d3572c7 100644 --- a/src/gallium/winsys/nouveau/drm/nouveau_drm_winsys.c +++ b/src/gallium/winsys/nouveau/drm/nouveau_drm_winsys.c @@ -1,21 +1,66 @@ +#include #include "pipe/p_context.h" #include "pipe/p_state.h" #include "util/u_format.h" #include "util/u_memory.h" #include "util/u_inlines.h" +#include "util/u_hash_table.h" #include "nouveau_drm_public.h" #include "nouveau/nouveau_winsys.h" #include "nouveau/nouveau_screen.h" +static struct util_hash_table *fd_tab = NULL; + +boolean nouveau_drm_screen_unref(struct nouveau_screen *screen) +{ + bool ret = pipe_reference(&screen->reference, NULL); + if (ret && fd_tab) + util_hash_table_remove(fd_tab, intptr_to_pointer(screen->device->fd)); + return ret; +} + +static unsigned hash_fd(void *key) +{ + int fd = pointer_to_intptr(key); + struct stat stat; + fstat(fd, &stat); + + return stat.st_dev ^ stat.st_ino ^ stat.st_rdev; +} + +static int compare_fd(void *key1, void *key2) +{ + int fd1 = pointer_to_intptr(key1); + int fd2 = pointer_to_intptr(key2); + struct stat stat1, stat2; + fstat(fd1, &stat1); + fstat(fd2, &stat2); + + return stat1.st_dev != stat2.st_dev || + stat1.st_ino != stat2.st_ino || + stat1.st_rdev != stat2.st_rdev; +} + struct pipe_screen * nouveau_drm_screen_create(int fd) { struct nouveau_device *dev = NULL; struct pipe_screen *(*init)(struct nouveau_device *); + struct nouveau_screen *screen; int ret; + if (!fd_tab) { + fd_tab = util_hash_table_create(hash_fd, compare_fd); + } + + screen = util_hash_table_get(fd_tab, intptr_to_pointer(fd)); + if (screen) { + pipe_reference(NULL, &screen->reference); + return &screen->base; + } + ret = nouveau_device_wrap(fd, 0, &dev); if (ret) return NULL; @@ -45,5 +90,8 @@ nouveau_drm_screen_create(int fd) return NULL; } - return init(dev); + screen = (struct nouveau_screen*)init(dev); + if (screen) + util_hash_table_set(fd_tab, intptr_to_pointer(fd), screen); + return &screen->base; }