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 = res->need_2D_zs ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_1D;
49       break;
50 
51    case PIPE_TEXTURE_1D_ARRAY:
52       ivci.viewType = res->need_2D_zs ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : 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    assert(ivci.viewType != VK_IMAGE_VIEW_TYPE_3D || ivci.subresourceRange.baseArrayLayer == 0);
102    assert(ivci.viewType != VK_IMAGE_VIEW_TYPE_3D || ivci.subresourceRange.layerCount == 1);
103    ivci.viewType = zink_surface_clamp_viewtype(ivci.viewType, templ->u.tex.first_layer, templ->u.tex.last_layer, res->base.b.array_size);
104 
105    return ivci;
106 }
107 
108 static void
init_surface_info(struct zink_surface * surface,struct zink_resource * res,VkImageViewCreateInfo * ivci)109 init_surface_info(struct zink_surface *surface, struct zink_resource *res, VkImageViewCreateInfo *ivci)
110 {
111    VkImageViewUsageCreateInfo *usage_info = (VkImageViewUsageCreateInfo *)ivci->pNext;
112    surface->info.flags = res->obj->vkflags;
113    surface->info.usage = usage_info ? usage_info->usage : res->obj->vkusage;
114    surface->info.width = surface->base.width;
115    surface->info.height = surface->base.height;
116    surface->info.layerCount = ivci->subresourceRange.layerCount;
117    surface->info.format = ivci->format;
118    surface->info_hash = _mesa_hash_data(&surface->info, sizeof(surface->info));
119 }
120 
121 static struct zink_surface *
create_surface(struct pipe_context * pctx,struct pipe_resource * pres,const struct pipe_surface * templ,VkImageViewCreateInfo * ivci)122 create_surface(struct pipe_context *pctx,
123                struct pipe_resource *pres,
124                const struct pipe_surface *templ,
125                VkImageViewCreateInfo *ivci)
126 {
127    struct zink_screen *screen = zink_screen(pctx->screen);
128    struct zink_resource *res = zink_resource(pres);
129    unsigned int level = templ->u.tex.level;
130 
131    struct zink_surface *surface = CALLOC_STRUCT(zink_surface);
132    if (!surface)
133       return NULL;
134 
135    VkImageViewUsageCreateInfo usage_info;
136    usage_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO;
137    usage_info.pNext = NULL;
138    VkFormatFeatureFlags feats = res->optimal_tiling ?
139                                 screen->format_props[templ->format].optimalTilingFeatures :
140                                 screen->format_props[templ->format].linearTilingFeatures;
141    VkImageUsageFlags attachment = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
142    usage_info.usage = res->obj->vkusage & ~attachment;
143    if ((res->obj->vkusage & attachment) &&
144        !(feats & (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))) {
145       ivci->pNext = &usage_info;
146    }
147 
148    pipe_resource_reference(&surface->base.texture, pres);
149    pipe_reference_init(&surface->base.reference, 1);
150    surface->base.context = pctx;
151    surface->base.format = templ->format;
152    surface->base.width = u_minify(pres->width0, level);
153    assert(surface->base.width);
154    surface->base.height = u_minify(pres->height0, level);
155    assert(surface->base.height);
156    surface->base.nr_samples = templ->nr_samples;
157    surface->base.u.tex.level = level;
158    surface->base.u.tex.first_layer = templ->u.tex.first_layer;
159    surface->base.u.tex.last_layer = templ->u.tex.last_layer;
160    surface->obj = zink_resource(pres)->obj;
161    util_dynarray_init(&surface->framebuffer_refs, NULL);
162    util_dynarray_init(&surface->desc_set_refs.refs, NULL);
163 
164    init_surface_info(surface, res, ivci);
165 
166    if (VKSCR(CreateImageView)(screen->dev, ivci, NULL,
167                          &surface->image_view) != VK_SUCCESS) {
168       mesa_loge("ZINK: vkCreateImageView failed");
169       FREE(surface);
170       return NULL;
171    }
172 
173    return surface;
174 }
175 
176 static uint32_t
hash_ivci(const void * key)177 hash_ivci(const void *key)
178 {
179    return _mesa_hash_data((char*)key + offsetof(VkImageViewCreateInfo, flags), sizeof(VkImageViewCreateInfo) - offsetof(VkImageViewCreateInfo, flags));
180 }
181 
182 struct pipe_surface *
zink_get_surface(struct zink_context * ctx,struct pipe_resource * pres,const struct pipe_surface * templ,VkImageViewCreateInfo * ivci)183 zink_get_surface(struct zink_context *ctx,
184             struct pipe_resource *pres,
185             const struct pipe_surface *templ,
186             VkImageViewCreateInfo *ivci)
187 {
188    struct zink_surface *surface = NULL;
189    struct zink_resource *res = zink_resource(pres);
190    uint32_t hash = hash_ivci(ivci);
191 
192    simple_mtx_lock(&res->surface_mtx);
193    struct hash_entry *entry = _mesa_hash_table_search_pre_hashed(&res->surface_cache, hash, ivci);
194 
195    if (!entry) {
196       /* create a new surface */
197       surface = create_surface(&ctx->base, pres, templ, ivci);
198       surface->base.nr_samples = 0;
199       surface->hash = hash;
200       surface->ivci = *ivci;
201       entry = _mesa_hash_table_insert_pre_hashed(&res->surface_cache, hash, &surface->ivci, surface);
202       if (!entry) {
203          simple_mtx_unlock(&res->surface_mtx);
204          return NULL;
205       }
206 
207       surface = entry->data;
208    } else {
209       surface = entry->data;
210       p_atomic_inc(&surface->base.reference.count);
211    }
212    simple_mtx_unlock(&res->surface_mtx);
213 
214    return &surface->base;
215 }
216 
217 static struct pipe_surface *
wrap_surface(struct pipe_context * pctx,struct pipe_surface * psurf)218 wrap_surface(struct pipe_context *pctx, struct pipe_surface *psurf)
219 {
220    struct zink_ctx_surface *csurf = CALLOC_STRUCT(zink_ctx_surface);
221    csurf->base = *psurf;
222    pipe_reference_init(&csurf->base.reference, 1);
223    csurf->surf = (struct zink_surface*)psurf;
224    csurf->base.context = pctx;
225 
226    return &csurf->base;
227 }
228 
229 static struct pipe_surface *
zink_create_surface(struct pipe_context * pctx,struct pipe_resource * pres,const struct pipe_surface * templ)230 zink_create_surface(struct pipe_context *pctx,
231                     struct pipe_resource *pres,
232                     const struct pipe_surface *templ)
233 {
234    struct zink_resource *res = zink_resource(pres);
235    bool is_array = templ->u.tex.last_layer != templ->u.tex.first_layer;
236    enum pipe_texture_target target_2d[] = {PIPE_TEXTURE_2D, PIPE_TEXTURE_2D_ARRAY};
237    VkImageViewCreateInfo ivci = create_ivci(zink_screen(pctx->screen), res, templ,
238                                             pres->target == PIPE_TEXTURE_3D ? target_2d[is_array] : pres->target);
239 
240    struct pipe_surface *psurf = zink_get_surface(zink_context(pctx), pres, templ, &ivci);
241    if (!psurf)
242       return NULL;
243 
244    struct zink_ctx_surface *csurf = (struct zink_ctx_surface*)wrap_surface(pctx, psurf);
245 
246    if (templ->nr_samples) {
247       /* transient fb attachment: not cached */
248       struct pipe_resource rtempl = *pres;
249       rtempl.nr_samples = templ->nr_samples;
250       rtempl.bind |= ZINK_BIND_TRANSIENT;
251       struct zink_resource *transient = zink_resource(pctx->screen->resource_create(pctx->screen, &rtempl));
252       if (!transient)
253          return NULL;
254       ivci.image = transient->obj->image;
255       csurf->transient = (struct zink_ctx_surface*)wrap_surface(pctx, (struct pipe_surface*)create_surface(pctx, &transient->base.b, templ, &ivci));
256       if (!csurf->transient) {
257          pipe_resource_reference((struct pipe_resource**)&transient, NULL);
258          pipe_surface_release(pctx, &psurf);
259          return NULL;
260       }
261       pipe_resource_reference((struct pipe_resource**)&transient, NULL);
262    }
263 
264    return &csurf->base;
265 }
266 
267 /* framebuffers are owned by their surfaces, so each time a surface that's part of a cached fb
268  * is destroyed, it has to unref all the framebuffers it's attached to in order to avoid leaking
269  * all the framebuffers
270  *
271  * surfaces are always batch-tracked, so it is impossible for a framebuffer to be destroyed
272  * while it is in use
273  */
274 static void
surface_clear_fb_refs(struct zink_screen * screen,struct pipe_surface * psurface)275 surface_clear_fb_refs(struct zink_screen *screen, struct pipe_surface *psurface)
276 {
277    struct zink_surface *surface = zink_surface(psurface);
278    util_dynarray_foreach(&surface->framebuffer_refs, struct zink_framebuffer*, fb_ref) {
279       struct zink_framebuffer *fb = *fb_ref;
280       for (unsigned i = 0; i < fb->state.num_attachments; i++) {
281          if (fb->surfaces[i] == psurface) {
282             simple_mtx_lock(&screen->framebuffer_mtx);
283             fb->surfaces[i] = NULL;
284             _mesa_hash_table_remove_key(&screen->framebuffer_cache, &fb->state);
285             zink_framebuffer_reference(screen, &fb, NULL);
286             simple_mtx_unlock(&screen->framebuffer_mtx);
287             break;
288          }
289       }
290    }
291    util_dynarray_fini(&surface->framebuffer_refs);
292 }
293 
294 void
zink_destroy_surface(struct zink_screen * screen,struct pipe_surface * psurface)295 zink_destroy_surface(struct zink_screen *screen, struct pipe_surface *psurface)
296 {
297    struct zink_surface *surface = zink_surface(psurface);
298    struct zink_resource *res = zink_resource(psurface->texture);
299    if (!psurface->nr_samples) {
300       simple_mtx_lock(&res->surface_mtx);
301       if (psurface->reference.count) {
302          /* got a cache hit during deletion */
303          simple_mtx_unlock(&res->surface_mtx);
304          return;
305       }
306       struct hash_entry *he = _mesa_hash_table_search_pre_hashed(&res->surface_cache, surface->hash, &surface->ivci);
307       assert(he);
308       assert(he->data == surface);
309       _mesa_hash_table_remove(&res->surface_cache, he);
310       simple_mtx_unlock(&res->surface_mtx);
311    }
312    if (!screen->info.have_KHR_imageless_framebuffer)
313       surface_clear_fb_refs(screen, psurface);
314    zink_descriptor_set_refs_clear(&surface->desc_set_refs, surface);
315    util_dynarray_fini(&surface->framebuffer_refs);
316    pipe_resource_reference(&psurface->texture, NULL);
317    if (surface->simage_view)
318       VKSCR(DestroyImageView)(screen->dev, surface->simage_view, NULL);
319    VKSCR(DestroyImageView)(screen->dev, surface->image_view, NULL);
320    FREE(surface);
321 }
322 
323 static void
zink_surface_destroy(struct pipe_context * pctx,struct pipe_surface * psurface)324 zink_surface_destroy(struct pipe_context *pctx,
325                      struct pipe_surface *psurface)
326 {
327    struct zink_ctx_surface *csurf = (struct zink_ctx_surface *)psurface;
328    zink_surface_reference(zink_screen(pctx->screen), &csurf->surf, NULL);
329    pipe_surface_release(pctx, (struct pipe_surface**)&csurf->transient);
330    FREE(csurf);
331 }
332 
333 bool
zink_rebind_surface(struct zink_context * ctx,struct pipe_surface ** psurface)334 zink_rebind_surface(struct zink_context *ctx, struct pipe_surface **psurface)
335 {
336    struct zink_surface *surface = zink_surface(*psurface);
337    struct zink_resource *res = zink_resource((*psurface)->texture);
338    struct zink_screen *screen = zink_screen(ctx->base.screen);
339    if (surface->simage_view)
340       return false;
341    VkImageViewCreateInfo ivci = create_ivci(screen,
342                                             zink_resource((*psurface)->texture), (*psurface), surface->base.texture->target);
343    uint32_t hash = hash_ivci(&ivci);
344 
345    simple_mtx_lock(&res->surface_mtx);
346    struct hash_entry *new_entry = _mesa_hash_table_search_pre_hashed(&res->surface_cache, hash, &ivci);
347    if (zink_batch_usage_exists(surface->batch_uses))
348       zink_batch_reference_surface(&ctx->batch, surface);
349    surface_clear_fb_refs(screen, *psurface);
350    zink_descriptor_set_refs_clear(&surface->desc_set_refs, surface);
351    if (new_entry) {
352       /* reuse existing surface; old one will be cleaned up naturally */
353       struct zink_surface *new_surface = new_entry->data;
354       simple_mtx_unlock(&res->surface_mtx);
355       zink_batch_usage_set(&new_surface->batch_uses, ctx->batch.state);
356       zink_surface_reference(screen, (struct zink_surface**)psurface, new_surface);
357       return true;
358    }
359    struct hash_entry *entry = _mesa_hash_table_search_pre_hashed(&res->surface_cache, surface->hash, &surface->ivci);
360    assert(entry);
361    _mesa_hash_table_remove(&res->surface_cache, entry);
362    VkImageView image_view;
363    if (VKSCR(CreateImageView)(screen->dev, &ivci, NULL, &image_view) != VK_SUCCESS) {
364       mesa_loge("ZINK: failed to create new imageview");
365       simple_mtx_unlock(&res->surface_mtx);
366       return false;
367    }
368    surface->hash = hash;
369    surface->ivci = ivci;
370    entry = _mesa_hash_table_insert_pre_hashed(&res->surface_cache, surface->hash, &surface->ivci, surface);
371    assert(entry);
372    surface->simage_view = surface->image_view;
373    surface->image_view = image_view;
374    surface->obj = zink_resource(surface->base.texture)->obj;
375    /* update for imageless fb */
376    surface->info.flags = res->obj->vkflags;
377    surface->info.usage = res->obj->vkusage;
378    surface->info_hash = _mesa_hash_data(&surface->info, sizeof(surface->info));
379    zink_batch_usage_set(&surface->batch_uses, ctx->batch.state);
380    simple_mtx_unlock(&res->surface_mtx);
381    return true;
382 }
383 
384 struct pipe_surface *
zink_surface_create_null(struct zink_context * ctx,enum pipe_texture_target target,unsigned width,unsigned height,unsigned samples)385 zink_surface_create_null(struct zink_context *ctx, enum pipe_texture_target target, unsigned width, unsigned height, unsigned samples)
386 {
387    struct pipe_surface surf_templ = {0};
388 
389    struct pipe_resource *pres;
390    struct pipe_resource templ = {0};
391    templ.width0 = width;
392    templ.height0 = height;
393    templ.depth0 = 1;
394    templ.format = PIPE_FORMAT_R8_UINT;
395    templ.target = target;
396    templ.bind = PIPE_BIND_RENDER_TARGET;
397    templ.nr_samples = samples;
398 
399    pres = ctx->base.screen->resource_create(ctx->base.screen, &templ);
400    if (!pres)
401       return NULL;
402 
403    surf_templ.format = PIPE_FORMAT_R8_UINT;
404    surf_templ.nr_samples = 0;
405    struct pipe_surface *psurf = ctx->base.create_surface(&ctx->base, pres, &surf_templ);
406    pipe_resource_reference(&pres, NULL);
407    return psurf;
408 }
409 
410 void
zink_context_surface_init(struct pipe_context * context)411 zink_context_surface_init(struct pipe_context *context)
412 {
413    context->create_surface = zink_create_surface;
414    context->surface_destroy = zink_surface_destroy;
415 }
416