1 /*
2  * Copyright 2018 Collabora Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * on the rights to use, copy, modify, merge, publish, distribute, sub
8  * license, and/or sell copies of the Software, and to permit persons to whom
9  * the Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21  * USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "zink_context.h"
25 #include "zink_framebuffer.h"
26 #include "zink_resource.h"
27 #include "zink_screen.h"
28 #include "zink_surface.h"
29 
30 #include "util/format/u_format.h"
31 #include "util/u_inlines.h"
32 #include "util/u_memory.h"
33 
34 VkImageViewCreateInfo
create_ivci(struct zink_screen * screen,struct zink_resource * res,const struct pipe_surface * templ,enum pipe_texture_target target)35 create_ivci(struct zink_screen *screen,
36             struct zink_resource *res,
37             const struct pipe_surface *templ,
38             enum pipe_texture_target target)
39 {
40    VkImageViewCreateInfo ivci;
41    /* zero holes since this is hashed */
42    memset(&ivci, 0, sizeof(VkImageViewCreateInfo));
43    ivci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
44    ivci.image = res->obj->image;
45 
46    switch (target) {
47    case PIPE_TEXTURE_1D:
48       ivci.viewType = VK_IMAGE_VIEW_TYPE_1D;
49       break;
50 
51    case PIPE_TEXTURE_1D_ARRAY:
52       ivci.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
53       break;
54 
55    case PIPE_TEXTURE_2D:
56    case PIPE_TEXTURE_RECT:
57       ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
58       break;
59 
60    case PIPE_TEXTURE_2D_ARRAY:
61       ivci.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
62       break;
63 
64    case PIPE_TEXTURE_CUBE:
65       ivci.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
66       break;
67 
68    case PIPE_TEXTURE_CUBE_ARRAY:
69       ivci.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
70       break;
71 
72    case PIPE_TEXTURE_3D:
73       ivci.viewType = VK_IMAGE_VIEW_TYPE_3D;
74       break;
75 
76    default:
77       unreachable("unsupported target");
78    }
79 
80    ivci.format = zink_get_format(screen, templ->format);
81    assert(ivci.format != VK_FORMAT_UNDEFINED);
82 
83    /* TODO: it's currently illegal to use non-identity swizzles for framebuffer attachments,
84     * but if that ever changes, this will be useful
85    const struct util_format_description *desc = util_format_description(templ->format);
86    ivci.components.r = zink_component_mapping(zink_clamp_void_swizzle(desc, PIPE_SWIZZLE_X));
87    ivci.components.g = zink_component_mapping(zink_clamp_void_swizzle(desc, PIPE_SWIZZLE_Y));
88    ivci.components.b = zink_component_mapping(zink_clamp_void_swizzle(desc, PIPE_SWIZZLE_Z));
89    ivci.components.a = zink_component_mapping(zink_clamp_void_swizzle(desc, PIPE_SWIZZLE_W));
90    */
91    ivci.components.r = VK_COMPONENT_SWIZZLE_R;
92    ivci.components.g = VK_COMPONENT_SWIZZLE_G;
93    ivci.components.b = VK_COMPONENT_SWIZZLE_B;
94    ivci.components.a = VK_COMPONENT_SWIZZLE_A;
95 
96    ivci.subresourceRange.aspectMask = res->aspect;
97    ivci.subresourceRange.baseMipLevel = templ->u.tex.level;
98    ivci.subresourceRange.levelCount = 1;
99    ivci.subresourceRange.baseArrayLayer = templ->u.tex.first_layer;
100    ivci.subresourceRange.layerCount = 1 + templ->u.tex.last_layer - templ->u.tex.first_layer;
101    ivci.viewType = zink_surface_clamp_viewtype(ivci.viewType, templ->u.tex.first_layer, templ->u.tex.last_layer, res->base.b.array_size);
102 
103    return ivci;
104 }
105 
106 static void
init_surface_info(struct zink_surface * surface,struct zink_resource * res,VkImageViewCreateInfo * ivci)107 init_surface_info(struct zink_surface *surface, struct zink_resource *res, VkImageViewCreateInfo *ivci)
108 {
109    surface->info.flags = res->obj->vkflags;
110    surface->info.usage = res->obj->vkusage;
111    surface->info.width = surface->base.width;
112    surface->info.height = surface->base.height;
113    surface->info.layerCount = ivci->subresourceRange.layerCount;
114    surface->info.format = ivci->format;
115    surface->info_hash = _mesa_hash_data(&surface->info, sizeof(surface->info));
116 }
117 
118 static struct zink_surface *
create_surface(struct pipe_context * pctx,struct pipe_resource * pres,const struct pipe_surface * templ,VkImageViewCreateInfo * ivci)119 create_surface(struct pipe_context *pctx,
120                struct pipe_resource *pres,
121                const struct pipe_surface *templ,
122                VkImageViewCreateInfo *ivci)
123 {
124    struct zink_screen *screen = zink_screen(pctx->screen);
125    struct zink_resource *res = zink_resource(pres);
126    unsigned int level = templ->u.tex.level;
127 
128    struct zink_surface *surface = CALLOC_STRUCT(zink_surface);
129    if (!surface)
130       return NULL;
131 
132    pipe_resource_reference(&surface->base.texture, pres);
133    pipe_reference_init(&surface->base.reference, 1);
134    surface->base.context = pctx;
135    surface->base.format = templ->format;
136    surface->base.width = u_minify(pres->width0, level);
137    assert(surface->base.width);
138    surface->base.height = u_minify(pres->height0, level);
139    assert(surface->base.height);
140    surface->base.nr_samples = templ->nr_samples;
141    surface->base.u.tex.level = level;
142    surface->base.u.tex.first_layer = templ->u.tex.first_layer;
143    surface->base.u.tex.last_layer = templ->u.tex.last_layer;
144    surface->obj = zink_resource(pres)->obj;
145    util_dynarray_init(&surface->framebuffer_refs, NULL);
146    util_dynarray_init(&surface->desc_set_refs.refs, NULL);
147 
148    init_surface_info(surface, res, ivci);
149 
150    if (VKSCR(CreateImageView)(screen->dev, ivci, NULL,
151                          &surface->image_view) != VK_SUCCESS) {
152       FREE(surface);
153       return NULL;
154    }
155 
156    return surface;
157 }
158 
159 static uint32_t
hash_ivci(const void * key)160 hash_ivci(const void *key)
161 {
162    return _mesa_hash_data((char*)key + offsetof(VkImageViewCreateInfo, flags), sizeof(VkImageViewCreateInfo) - offsetof(VkImageViewCreateInfo, flags));
163 }
164 
165 struct pipe_surface *
zink_get_surface(struct zink_context * ctx,struct pipe_resource * pres,const struct pipe_surface * templ,VkImageViewCreateInfo * ivci)166 zink_get_surface(struct zink_context *ctx,
167             struct pipe_resource *pres,
168             const struct pipe_surface *templ,
169             VkImageViewCreateInfo *ivci)
170 {
171    struct zink_surface *surface = NULL;
172    struct zink_resource *res = zink_resource(pres);
173    uint32_t hash = hash_ivci(ivci);
174 
175    simple_mtx_lock(&res->surface_mtx);
176    struct hash_entry *entry = _mesa_hash_table_search_pre_hashed(&res->surface_cache, hash, ivci);
177 
178    if (!entry) {
179       /* create a new surface */
180       surface = create_surface(&ctx->base, pres, templ, ivci);
181       surface->base.nr_samples = 0;
182       surface->hash = hash;
183       surface->ivci = *ivci;
184       entry = _mesa_hash_table_insert_pre_hashed(&res->surface_cache, hash, &surface->ivci, surface);
185       if (!entry) {
186          simple_mtx_unlock(&res->surface_mtx);
187          return NULL;
188       }
189 
190       surface = entry->data;
191    } else {
192       surface = entry->data;
193       p_atomic_inc(&surface->base.reference.count);
194    }
195    simple_mtx_unlock(&res->surface_mtx);
196 
197    return &surface->base;
198 }
199 
200 static struct pipe_surface *
wrap_surface(struct pipe_context * pctx,struct pipe_surface * psurf)201 wrap_surface(struct pipe_context *pctx, struct pipe_surface *psurf)
202 {
203    struct zink_ctx_surface *csurf = CALLOC_STRUCT(zink_ctx_surface);
204    csurf->base = *psurf;
205    pipe_reference_init(&csurf->base.reference, 1);
206    csurf->surf = (struct zink_surface*)psurf;
207    csurf->base.context = pctx;
208 
209    return &csurf->base;
210 }
211 
212 static struct pipe_surface *
zink_create_surface(struct pipe_context * pctx,struct pipe_resource * pres,const struct pipe_surface * templ)213 zink_create_surface(struct pipe_context *pctx,
214                     struct pipe_resource *pres,
215                     const struct pipe_surface *templ)
216 {
217 
218    VkImageViewCreateInfo ivci = create_ivci(zink_screen(pctx->screen),
219                                             zink_resource(pres), templ, pres->target);
220    if (pres->target == PIPE_TEXTURE_3D)
221       ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
222 
223    struct pipe_surface *psurf = zink_get_surface(zink_context(pctx), pres, templ, &ivci);
224    if (!psurf)
225       return NULL;
226 
227    struct zink_ctx_surface *csurf = (struct zink_ctx_surface*)wrap_surface(pctx, psurf);
228 
229    if (templ->nr_samples) {
230       /* transient fb attachment: not cached */
231       struct pipe_resource rtempl = *pres;
232       rtempl.nr_samples = templ->nr_samples;
233       rtempl.bind |= ZINK_BIND_TRANSIENT;
234       struct zink_resource *transient = zink_resource(pctx->screen->resource_create(pctx->screen, &rtempl));
235       if (!transient)
236          return NULL;
237       ivci.image = transient->obj->image;
238       csurf->transient = (struct zink_ctx_surface*)wrap_surface(pctx, (struct pipe_surface*)create_surface(pctx, &transient->base.b, templ, &ivci));
239       if (!csurf->transient) {
240          pipe_resource_reference((struct pipe_resource**)&transient, NULL);
241          pipe_surface_release(pctx, &psurf);
242          return NULL;
243       }
244       pipe_resource_reference((struct pipe_resource**)&transient, NULL);
245    }
246 
247    return &csurf->base;
248 }
249 
250 /* framebuffers are owned by their surfaces, so each time a surface that's part of a cached fb
251  * is destroyed, it has to unref all the framebuffers it's attached to in order to avoid leaking
252  * all the framebuffers
253  *
254  * surfaces are always batch-tracked, so it is impossible for a framebuffer to be destroyed
255  * while it is in use
256  */
257 static void
surface_clear_fb_refs(struct zink_screen * screen,struct pipe_surface * psurface)258 surface_clear_fb_refs(struct zink_screen *screen, struct pipe_surface *psurface)
259 {
260    struct zink_surface *surface = zink_surface(psurface);
261    util_dynarray_foreach(&surface->framebuffer_refs, struct zink_framebuffer*, fb_ref) {
262       struct zink_framebuffer *fb = *fb_ref;
263       for (unsigned i = 0; i < fb->state.num_attachments; i++) {
264          if (fb->surfaces[i] == psurface) {
265             simple_mtx_lock(&screen->framebuffer_mtx);
266             fb->surfaces[i] = NULL;
267             _mesa_hash_table_remove_key(&screen->framebuffer_cache, &fb->state);
268             zink_framebuffer_reference(screen, &fb, NULL);
269             simple_mtx_unlock(&screen->framebuffer_mtx);
270             break;
271          }
272       }
273    }
274    util_dynarray_fini(&surface->framebuffer_refs);
275 }
276 
277 void
zink_destroy_surface(struct zink_screen * screen,struct pipe_surface * psurface)278 zink_destroy_surface(struct zink_screen *screen, struct pipe_surface *psurface)
279 {
280    struct zink_surface *surface = zink_surface(psurface);
281    struct zink_resource *res = zink_resource(psurface->texture);
282    if (!psurface->nr_samples) {
283       simple_mtx_lock(&res->surface_mtx);
284       if (psurface->reference.count) {
285          /* got a cache hit during deletion */
286          simple_mtx_unlock(&res->surface_mtx);
287          return;
288       }
289       struct hash_entry *he = _mesa_hash_table_search_pre_hashed(&res->surface_cache, surface->hash, &surface->ivci);
290       assert(he);
291       assert(he->data == surface);
292       _mesa_hash_table_remove(&res->surface_cache, he);
293       simple_mtx_unlock(&res->surface_mtx);
294    }
295    if (!screen->info.have_KHR_imageless_framebuffer)
296       surface_clear_fb_refs(screen, psurface);
297    zink_descriptor_set_refs_clear(&surface->desc_set_refs, surface);
298    util_dynarray_fini(&surface->framebuffer_refs);
299    pipe_resource_reference(&psurface->texture, NULL);
300    if (surface->simage_view)
301       VKSCR(DestroyImageView)(screen->dev, surface->simage_view, NULL);
302    VKSCR(DestroyImageView)(screen->dev, surface->image_view, NULL);
303    FREE(surface);
304 }
305 
306 static void
zink_surface_destroy(struct pipe_context * pctx,struct pipe_surface * psurface)307 zink_surface_destroy(struct pipe_context *pctx,
308                      struct pipe_surface *psurface)
309 {
310    struct zink_ctx_surface *csurf = (struct zink_ctx_surface *)psurface;
311    zink_surface_reference(zink_screen(pctx->screen), &csurf->surf, NULL);
312    pipe_surface_release(pctx, (struct pipe_surface**)&csurf->transient);
313    FREE(csurf);
314 }
315 
316 bool
zink_rebind_surface(struct zink_context * ctx,struct pipe_surface ** psurface)317 zink_rebind_surface(struct zink_context *ctx, struct pipe_surface **psurface)
318 {
319    struct zink_surface *surface = zink_surface(*psurface);
320    struct zink_resource *res = zink_resource((*psurface)->texture);
321    struct zink_screen *screen = zink_screen(ctx->base.screen);
322    if (surface->simage_view)
323       return false;
324    VkImageViewCreateInfo ivci = create_ivci(screen,
325                                             zink_resource((*psurface)->texture), (*psurface), surface->base.texture->target);
326    uint32_t hash = hash_ivci(&ivci);
327 
328    simple_mtx_lock(&res->surface_mtx);
329    struct hash_entry *new_entry = _mesa_hash_table_search_pre_hashed(&res->surface_cache, hash, &ivci);
330    if (zink_batch_usage_exists(surface->batch_uses))
331       zink_batch_reference_surface(&ctx->batch, surface);
332    surface_clear_fb_refs(screen, *psurface);
333    zink_descriptor_set_refs_clear(&surface->desc_set_refs, surface);
334    if (new_entry) {
335       /* reuse existing surface; old one will be cleaned up naturally */
336       struct zink_surface *new_surface = new_entry->data;
337       simple_mtx_unlock(&res->surface_mtx);
338       zink_batch_usage_set(&new_surface->batch_uses, ctx->batch.state);
339       zink_surface_reference(screen, (struct zink_surface**)psurface, new_surface);
340       return true;
341    }
342    struct hash_entry *entry = _mesa_hash_table_search_pre_hashed(&res->surface_cache, surface->hash, &surface->ivci);
343    assert(entry);
344    _mesa_hash_table_remove(&res->surface_cache, entry);
345    VkImageView image_view;
346    if (VKSCR(CreateImageView)(screen->dev, &ivci, NULL, &image_view) != VK_SUCCESS) {
347       debug_printf("zink: failed to create new imageview");
348       simple_mtx_unlock(&res->surface_mtx);
349       return false;
350    }
351    surface->hash = hash;
352    surface->ivci = ivci;
353    entry = _mesa_hash_table_insert_pre_hashed(&res->surface_cache, surface->hash, &surface->ivci, surface);
354    assert(entry);
355    surface->simage_view = surface->image_view;
356    surface->image_view = image_view;
357    surface->obj = zink_resource(surface->base.texture)->obj;
358    /* update for imageless fb */
359    surface->info.flags = res->obj->vkflags;
360    surface->info.usage = res->obj->vkusage;
361    surface->info_hash = _mesa_hash_data(&surface->info, sizeof(surface->info));
362    zink_batch_usage_set(&surface->batch_uses, ctx->batch.state);
363    simple_mtx_unlock(&res->surface_mtx);
364    return true;
365 }
366 
367 struct pipe_surface *
zink_surface_create_null(struct zink_context * ctx,enum pipe_texture_target target,unsigned width,unsigned height,unsigned samples)368 zink_surface_create_null(struct zink_context *ctx, enum pipe_texture_target target, unsigned width, unsigned height, unsigned samples)
369 {
370    struct pipe_surface surf_templ = {0};
371 
372    struct pipe_resource *pres;
373    struct pipe_resource templ = {0};
374    templ.width0 = width;
375    templ.height0 = height;
376    templ.depth0 = 1;
377    templ.format = PIPE_FORMAT_R8_UINT;
378    templ.target = target;
379    templ.bind = PIPE_BIND_RENDER_TARGET;
380    templ.nr_samples = samples;
381 
382    pres = ctx->base.screen->resource_create(ctx->base.screen, &templ);
383    if (!pres)
384       return NULL;
385 
386    surf_templ.format = PIPE_FORMAT_R8_UINT;
387    surf_templ.nr_samples = 0;
388    struct pipe_surface *psurf = ctx->base.create_surface(&ctx->base, pres, &surf_templ);
389    pipe_resource_reference(&pres, NULL);
390    return psurf;
391 }
392 
393 void
zink_context_surface_init(struct pipe_context * context)394 zink_context_surface_init(struct pipe_context *context)
395 {
396    context->create_surface = zink_create_surface;
397    context->surface_destroy = zink_surface_destroy;
398 }
399