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
34VkImageViewCreateInfo
35create_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
106static void
107init_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
118static struct zink_surface *
119create_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
159static uint32_t
160hash_ivci(const void *key)
161{
162   return _mesa_hash_data((char*)key + offsetof(VkImageViewCreateInfo, flags), sizeof(VkImageViewCreateInfo) - offsetof(VkImageViewCreateInfo, flags));
163}
164
165struct pipe_surface *
166zink_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
200static struct pipe_surface *
201wrap_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
212static struct pipe_surface *
213zink_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 */
257static void
258surface_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
277void
278zink_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
306static void
307zink_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
316bool
317zink_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
367struct pipe_surface *
368zink_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
393void
394zink_context_surface_init(struct pipe_context *context)
395{
396   context->create_surface = zink_create_surface;
397   context->surface_destroy = zink_surface_destroy;
398}
399