zink: create separate vk image/buffer objects for shader image use

the STORAGE_TEXEL and STORAGE_IMAGE bits can't be accurately applied due
to opengl allowing all resources to be used everywhere, so instead we can
create a separate object on demand which is used only by shaders and gets
extra barriers inferred along with the base object to avoid desync whenever
it is used

Reviewed-by: Dave Airlie <airlied@redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/9788>
This commit is contained in:
Mike Blumenkrantz 2020-12-12 01:45:29 -05:00
parent 475c1bda8e
commit 69cdb6b776
6 changed files with 196 additions and 10 deletions

View file

@ -984,6 +984,10 @@ zink_set_shader_images(struct pipe_context *pctx,
if (images && images[i].resource) {
util_dynarray_init(&image_view->desc_set_refs.refs, NULL);
struct zink_resource *res = zink_resource(images[i].resource);
if (!zink_resource_object_init_storage(ctx, res)) {
debug_printf("couldn't create storage image!");
continue;
}
res->bind_history |= BITFIELD_BIT(ZINK_DESCRIPTOR_TYPE_IMAGE);
res->bind_stages |= 1 << p_stage;
util_copy_image_view(&image_view->base, images + i);
@ -1055,6 +1059,12 @@ zink_set_sampler_views(struct pipe_context *pctx,
sampler_view_buffer_clear(ctx, b);
b->buffer_view = buffer_view;
}
} else if (!res->obj->is_buffer) {
if (res->obj != b->image_view->obj) {
struct pipe_surface *psurf = &b->image_view->base;
zink_rebind_surface(ctx, &psurf);
b->image_view = zink_surface(psurf);
}
}
res->bind_history |= BITFIELD_BIT(ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW);
res->bind_stages |= 1 << shader_type;
@ -1375,6 +1385,27 @@ zink_flush_queue(struct zink_context *ctx)
flush_batch(ctx);
}
static bool
rebind_fb_surface(struct zink_context *ctx, struct pipe_surface **surf, struct zink_resource *match_res)
{
if (!*surf)
return false;
struct zink_resource *surf_res = zink_resource((*surf)->texture);
if ((match_res == surf_res) || surf_res->obj != zink_surface(*surf)->obj)
return zink_rebind_surface(ctx, surf);
return false;
}
static bool
rebind_fb_state(struct zink_context *ctx, struct zink_resource *match_res)
{
bool rebind = false;
for (int i = 0; i < ctx->fb_state.nr_cbufs; i++)
rebind |= rebind_fb_surface(ctx, &ctx->fb_state.cbufs[i], match_res);
rebind |= rebind_fb_surface(ctx, &ctx->fb_state.zsbuf, match_res);
return rebind;
}
static void
zink_set_framebuffer_state(struct pipe_context *pctx,
const struct pipe_framebuffer_state *state)
@ -1398,6 +1429,7 @@ zink_set_framebuffer_state(struct pipe_context *pctx,
}
util_copy_framebuffer_state(&ctx->fb_state, state);
rebind_fb_state(ctx, NULL);
/* get_framebuffer adds a ref if the fb is reused or created;
* always do get_framebuffer first to avoid deleting the same fb
* we're about to use
@ -1632,6 +1664,7 @@ zink_resource_image_barrier(struct zink_context *ctx, struct zink_batch *batch,
/* only barrier if we're changing layout or doing something besides read -> read */
batch = zink_batch_no_rp(ctx);
assert(!batch->in_rp);
assert(new_layout);
vkCmdPipelineBarrier(
batch->state->cmdbuf,
res->access_stage ? res->access_stage : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
@ -2326,6 +2359,22 @@ zink_set_stream_output_targets(struct pipe_context *pctx,
}
}
void
zink_rebind_framebuffer(struct zink_context *ctx, struct zink_resource *res)
{
if (!ctx->framebuffer)
return;
for (unsigned i = 0; i < ctx->framebuffer->state.num_attachments; i++) {
if (!ctx->framebuffer->surfaces[i] ||
zink_resource(ctx->framebuffer->surfaces[i]->texture) != res)
continue;
zink_rebind_surface(ctx, &ctx->framebuffer->surfaces[i]);
zink_batch_no_rp(ctx);
}
if (rebind_fb_state(ctx, res))
zink_batch_no_rp(ctx);
}
void
zink_resource_rebind(struct zink_context *ctx, struct zink_resource *res)
{
@ -2368,6 +2417,10 @@ zink_resource_rebind(struct zink_context *ctx, struct zink_resource *res)
struct zink_image_view *image_view = &ctx->image_views[shader][i];
zink_descriptor_set_refs_clear(&image_view->desc_set_refs, image_view);
zink_buffer_view_reference(zink_screen(ctx->base.screen), &image_view->buffer_view, NULL);
if (!zink_resource_object_init_storage(ctx, res)) {
debug_printf("couldn't create storage image!");
continue;
}
image_view->buffer_view = get_buffer_view(ctx, res, image_view->base.format,
image_view->base.u.buf.offset, image_view->base.u.buf.size);
assert(image_view->buffer_view);

View file

@ -323,6 +323,9 @@ zink_rect_from_box(const struct pipe_box *box)
void
zink_resource_rebind(struct zink_context *ctx, struct zink_resource *res);
void
zink_rebind_framebuffer(struct zink_context *ctx, struct zink_resource *res);
void
zink_draw_vbo(struct pipe_context *pctx,
const struct pipe_draw_info *dinfo,

View file

@ -123,10 +123,13 @@ void
zink_destroy_resource_object(struct zink_screen *screen, struct zink_resource_object *obj)
{
assert(!obj->map_count);
if (obj->is_buffer)
if (obj->is_buffer) {
if (obj->sbuffer)
vkDestroyBuffer(screen->dev, obj->sbuffer, NULL);
vkDestroyBuffer(screen->dev, obj->buffer, NULL);
else
} else {
vkDestroyImage(screen->dev, obj->image, NULL);
}
zink_descriptor_set_refs_clear(&obj->desc_set_refs, obj);
cache_or_free_mem(screen, obj);
@ -201,9 +204,6 @@ create_bci(struct zink_screen *screen, const struct pipe_resource *templ, unsign
VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
VkFormatProperties props = screen->format_props[templ->format];
if (props.bufferFeatures & VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT)
bci.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
}
if (bind & PIPE_BIND_VERTEX_BUFFER)
@ -222,6 +222,9 @@ create_bci(struct zink_screen *screen, const struct pipe_resource *templ, unsign
if (bind & PIPE_BIND_SHADER_BUFFER)
bci.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
if (bind & PIPE_BIND_SHADER_IMAGE)
bci.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
if (bind & PIPE_BIND_COMMAND_ARGS_BUFFER)
bci.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
@ -294,12 +297,8 @@ create_ici(struct zink_screen *screen, const struct pipe_resource *templ, unsign
VK_IMAGE_USAGE_SAMPLED_BIT;
if ((templ->nr_samples <= 1 || screen->info.feats.features.shaderStorageImageMultisample) &&
(bind & PIPE_BIND_SHADER_IMAGE ||
(bind & PIPE_BIND_SAMPLER_VIEW && templ->flags & PIPE_RESOURCE_FLAG_TEXTURING_MORE_LIKELY))) {
(bind & PIPE_BIND_SHADER_IMAGE)) {
VkFormatProperties props = screen->format_props[templ->format];
/* gallium doesn't provide any way to actually know whether this will be used as a shader image,
* so we have to just assume and set the bit if it's available
*/
if ((ici.tiling == VK_IMAGE_TILING_LINEAR && props.linearTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) ||
(ici.tiling == VK_IMAGE_TILING_OPTIMAL && props.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT))
ici.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
@ -929,6 +928,81 @@ zink_resource_get_separate_stencil(struct pipe_resource *pres)
}
bool
zink_resource_object_init_storage(struct zink_context *ctx, struct zink_resource *res)
{
struct zink_screen *screen = zink_screen(ctx->base.screen);
/* base resource already has the cap */
if (res->base.bind & PIPE_BIND_SHADER_IMAGE)
return true;
if (res->obj->is_buffer) {
if (res->obj->sbuffer)
return true;
VkBufferCreateInfo bci = create_bci(screen, &res->base, res->base.bind | PIPE_BIND_SHADER_IMAGE);
VkBuffer buffer;
if (vkCreateBuffer(screen->dev, &bci, NULL, &buffer) != VK_SUCCESS)
return false;
vkBindBufferMemory(screen->dev, buffer, res->obj->mem, res->obj->offset);
res->obj->sbuffer = res->obj->buffer;
res->obj->buffer = buffer;
} else {
zink_fb_clears_apply_region(ctx, &res->base, (struct u_rect){0, res->base.width0, 0, res->base.height0});
zink_resource_image_barrier(ctx, NULL, res, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 0, 0);
res->base.bind |= PIPE_BIND_SHADER_IMAGE;
struct zink_resource_object *old_obj = res->obj;
struct zink_resource_object *new_obj = resource_object_create(screen, &res->base, NULL, &res->optimal_tiling);
if (!new_obj) {
debug_printf("new backing resource alloc failed!");
res->base.bind &= ~PIPE_BIND_SHADER_IMAGE;
return false;
}
struct zink_resource staging = *res;
staging.obj = old_obj;
res->obj = new_obj;
zink_descriptor_set_refs_clear(&old_obj->desc_set_refs, old_obj);
for (unsigned i = 0; i <= res->base.last_level; i++) {
struct pipe_box box = {0, 0, 0,
u_minify(res->base.width0, i),
u_minify(res->base.height0, i), res->base.array_size};
box.depth = util_num_layers(&res->base, i);
ctx->base.resource_copy_region(&ctx->base, &res->base, i, 0, 0, 0, &staging.base, i, &box);
}
zink_resource_object_reference(screen, &old_obj, NULL);
}
if (res->bind_history & BITFIELD64_BIT(ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW)) {
for (unsigned shader = 0; shader < PIPE_SHADER_TYPES; shader++) {
if (res->bind_stages & (1 << shader)) {
for (unsigned i = 0; i < ZINK_DESCRIPTOR_TYPE_IMAGE; i++) {
if (res->bind_history & BITFIELD64_BIT(i))
zink_context_invalidate_descriptor_state(ctx, shader, i);
}
}
}
}
if (res->obj->is_buffer)
zink_resource_rebind(ctx, res);
else {
zink_rebind_framebuffer(ctx, res);
/* this will be cleaned up in future commits */
if (res->bind_history & BITFIELD_BIT(ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW)) {
for (unsigned i = 0; i < PIPE_SHADER_TYPES; i++) {
for (unsigned j = 0; j < ctx->num_sampler_views[i]; j++) {
struct zink_sampler_view *sv = zink_sampler_view(ctx->sampler_views[i][j]);
if (sv && sv->base.texture == &res->base) {
struct pipe_surface *psurf = &sv->image_view->base;
zink_rebind_surface(ctx, &psurf);
sv->image_view = zink_surface(psurf);
zink_context_invalidate_descriptor_state(ctx, i, ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW);
}
}
}
}
}
return true;
}
void
zink_resource_setup_transfer_layouts(struct zink_context *ctx, struct zink_resource *src, struct zink_resource *dst)
{

View file

@ -57,6 +57,10 @@ struct zink_resource_object {
VkBuffer buffer;
VkImage image;
};
VkBuffer sbuffer;
bool storage_init; //layout was set for image
VkDeviceMemory mem;
uint32_t mem_hash;
struct mem_key mkey;
@ -154,4 +158,6 @@ zink_resource_object_reference(struct zink_screen *screen,
if (dst) *dst = src;
}
bool
zink_resource_object_init_storage(struct zink_context *ctx, struct zink_resource *res);
#endif

View file

@ -116,6 +116,7 @@ create_surface(struct pipe_context *pctx,
surface->base.u.tex.level = level;
surface->base.u.tex.first_layer = templ->u.tex.first_layer;
surface->base.u.tex.last_layer = templ->u.tex.last_layer;
surface->obj = zink_resource(pres)->obj;
util_dynarray_init(&surface->framebuffer_refs, NULL);
if (vkCreateImageView(screen->dev, ivci, NULL,
@ -218,11 +219,14 @@ zink_destroy_surface(struct zink_screen *screen, struct pipe_surface *psurface)
simple_mtx_lock(&screen->surface_mtx);
struct hash_entry *he = _mesa_hash_table_search_pre_hashed(&screen->surface_cache, surface->hash, &surface->ivci);
assert(he);
assert(he->data == surface);
_mesa_hash_table_remove(&screen->surface_cache, he);
simple_mtx_unlock(&screen->surface_mtx);
surface_clear_fb_refs(screen, psurface);
util_dynarray_fini(&surface->framebuffer_refs);
pipe_resource_reference(&psurface->texture, NULL);
if (surface->simage_view)
vkDestroyImageView(screen->dev, surface->simage_view, NULL);
vkDestroyImageView(screen->dev, surface->image_view, NULL);
FREE(surface);
}
@ -234,6 +238,47 @@ zink_surface_destroy(struct pipe_context *pctx,
zink_destroy_surface(zink_screen(pctx->screen), psurface);
}
bool
zink_rebind_surface(struct zink_context *ctx, struct pipe_surface **psurface)
{
struct zink_surface *surface = zink_surface(*psurface);
struct zink_screen *screen = zink_screen(ctx->base.screen);
if (surface->simage_view)
return false;
VkImageViewCreateInfo ivci = create_ivci(screen,
zink_resource((*psurface)->texture), (*psurface));
uint32_t hash = hash_ivci(&ivci);
simple_mtx_lock(&screen->surface_mtx);
struct hash_entry *new_entry = _mesa_hash_table_search_pre_hashed(&screen->surface_cache, hash, &ivci);
surface_clear_fb_refs(screen, *psurface);
if (new_entry) {
/* reuse existing surface; old one will be cleaned up naturally */
struct zink_surface *new_surface = new_entry->data;
simple_mtx_unlock(&screen->surface_mtx);
zink_surface_reference(screen, (struct zink_surface**)psurface, new_surface);
return true;
}
struct hash_entry *entry = _mesa_hash_table_search_pre_hashed(&screen->surface_cache, surface->hash, &surface->ivci);
assert(entry);
_mesa_hash_table_remove(&screen->surface_cache, entry);
VkImageView image_view;
if (vkCreateImageView(screen->dev, &ivci, NULL, &image_view) != VK_SUCCESS) {
debug_printf("zink: failed to create new imageview");
simple_mtx_unlock(&screen->surface_mtx);
return false;
}
surface->hash = hash;
surface->ivci = ivci;
entry = _mesa_hash_table_insert_pre_hashed(&screen->surface_cache, surface->hash, &surface->ivci, surface);
assert(entry);
surface->simage_view = surface->image_view;
surface->image_view = image_view;
surface->obj = zink_resource(surface->base.texture)->obj;
simple_mtx_unlock(&screen->surface_mtx);
return true;
}
void
zink_context_surface_init(struct pipe_context *context)
{

View file

@ -34,6 +34,8 @@ struct zink_surface {
struct pipe_surface base;
VkImageViewCreateInfo ivci;
VkImageView image_view;
VkImageView simage_view;//old iview after storage replacement/rebind
void *obj; //backing resource object
uint32_t hash;
struct zink_batch_usage batch_uses;
struct util_dynarray framebuffer_refs;
@ -93,4 +95,7 @@ zink_surface_clamp_viewtype(VkImageViewType viewType, unsigned first_layer, unsi
}
return viewType;
}
bool
zink_rebind_surface(struct zink_context *ctx, struct pipe_surface **psurface);
#endif