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