mesa/src/gallium/drivers/zink/zink_context.c

3526 lines
138 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright 2018 Collabora Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "zink_context.h"
#include "zink_batch.h"
#include "zink_compiler.h"
#include "zink_fence.h"
#include "zink_framebuffer.h"
#include "zink_helpers.h"
#include "zink_program.h"
#include "zink_pipeline.h"
#include "zink_query.h"
#include "zink_render_pass.h"
#include "zink_resource.h"
#include "zink_screen.h"
#include "zink_state.h"
#include "zink_surface.h"
#include "zink_inlines.h"
#include "util/u_blitter.h"
#include "util/u_debug.h"
#include "util/format_srgb.h"
#include "util/format/u_format.h"
#include "util/u_framebuffer.h"
#include "util/u_helpers.h"
#include "util/u_inlines.h"
#include "util/u_thread.h"
#include "util/u_cpu_detect.h"
#include "util/strndup.h"
#include "nir.h"
#include "util/u_memory.h"
#include "util/u_upload_mgr.h"
#define XXH_INLINE_ALL
#include "util/xxhash.h"
static void
calc_descriptor_hash_sampler_state(struct zink_sampler_state *sampler_state)
{
void *hash_data = &sampler_state->sampler;
size_t data_size = sizeof(VkSampler);
sampler_state->hash = XXH32(hash_data, data_size, 0);
}
void
debug_describe_zink_buffer_view(char *buf, const struct zink_buffer_view *ptr)
{
sprintf(buf, "zink_buffer_view");
}
ALWAYS_INLINE static void
check_resource_for_batch_ref(struct zink_context *ctx, struct zink_resource *res)
{
if (!res->bind_count[0] && !res->bind_count[1])
zink_batch_reference_resource(&ctx->batch, res);
}
static void
zink_context_destroy(struct pipe_context *pctx)
{
struct zink_context *ctx = zink_context(pctx);
struct zink_screen *screen = zink_screen(pctx->screen);
if (screen->queue && !screen->device_lost && vkQueueWaitIdle(screen->queue) != VK_SUCCESS)
debug_printf("vkQueueWaitIdle failed\n");
util_blitter_destroy(ctx->blitter);
for (unsigned i = 0; i < ctx->fb_state.nr_cbufs; i++)
zink_surface_reference(screen, (struct zink_surface**)&ctx->fb_state.cbufs[i], NULL);
zink_surface_reference(screen, (struct zink_surface**)&ctx->fb_state.zsbuf, NULL);
pipe_resource_reference(&ctx->dummy_vertex_buffer, NULL);
pipe_resource_reference(&ctx->dummy_xfb_buffer, NULL);
zink_surface_reference(screen, (struct zink_surface**)&ctx->dummy_surface, NULL);
zink_buffer_view_reference(screen, &ctx->dummy_bufferview, NULL);
simple_mtx_destroy(&ctx->batch_mtx);
zink_clear_batch_state(ctx, ctx->batch.state);
zink_batch_state_reference(screen, &ctx->batch.state, NULL);
hash_table_foreach(&ctx->batch_states, entry) {
struct zink_batch_state *bs = entry->data;
zink_clear_batch_state(ctx, bs);
zink_batch_state_reference(screen, &bs, NULL);
}
util_dynarray_foreach(&ctx->free_batch_states, struct zink_batch_state*, bs) {
zink_clear_batch_state(ctx, *bs);
zink_batch_state_reference(screen, bs, NULL);
}
if (ctx->framebuffer) {
simple_mtx_lock(&screen->framebuffer_mtx);
struct hash_entry *entry = _mesa_hash_table_search(&screen->framebuffer_cache, &ctx->framebuffer->state);
if (zink_framebuffer_reference(screen, &ctx->framebuffer, NULL))
_mesa_hash_table_remove(&screen->framebuffer_cache, entry);
simple_mtx_unlock(&screen->framebuffer_mtx);
}
hash_table_foreach(ctx->render_pass_cache, he)
zink_destroy_render_pass(screen, he->data);
u_upload_destroy(pctx->stream_uploader);
u_upload_destroy(pctx->const_uploader);
slab_destroy_child(&ctx->transfer_pool);
_mesa_hash_table_destroy(ctx->program_cache, NULL);
_mesa_hash_table_destroy(ctx->compute_program_cache, NULL);
_mesa_hash_table_destroy(ctx->render_pass_cache, NULL);
slab_destroy_child(&ctx->transfer_pool_unsync);
screen->descriptors_deinit(ctx);
zink_descriptor_layouts_deinit(ctx);
p_atomic_dec(&screen->base.num_contexts);
ralloc_free(ctx);
}
static void
check_device_lost(struct zink_context *ctx)
{
if (!zink_screen(ctx->base.screen)->device_lost || ctx->is_device_lost)
return;
debug_printf("ZINK: device lost detected!\n");
if (ctx->reset.reset)
ctx->reset.reset(ctx->reset.data, PIPE_GUILTY_CONTEXT_RESET);
ctx->is_device_lost = true;
}
static enum pipe_reset_status
zink_get_device_reset_status(struct pipe_context *pctx)
{
struct zink_context *ctx = zink_context(pctx);
enum pipe_reset_status status = PIPE_NO_RESET;
if (ctx->is_device_lost) {
// Since we don't know what really happened to the hardware, just
// assume that we are in the wrong
status = PIPE_GUILTY_CONTEXT_RESET;
debug_printf("ZINK: device lost detected!\n");
if (ctx->reset.reset)
ctx->reset.reset(ctx->reset.data, status);
}
return status;
}
static void
zink_set_device_reset_callback(struct pipe_context *pctx,
const struct pipe_device_reset_callback *cb)
{
struct zink_context *ctx = zink_context(pctx);
if (cb)
ctx->reset = *cb;
else
memset(&ctx->reset, 0, sizeof(ctx->reset));
}
static void
zink_set_context_param(struct pipe_context *pctx, enum pipe_context_param param,
unsigned value)
{
struct zink_context *ctx = zink_context(pctx);
switch (param) {
case PIPE_CONTEXT_PARAM_PIN_THREADS_TO_L3_CACHE:
util_set_thread_affinity(zink_screen(ctx->base.screen)->flush_queue.threads[0],
util_get_cpu_caps()->L3_affinity_mask[value],
NULL, util_get_cpu_caps()->num_cpu_mask_bits);
break;
default:
break;
}
}
static VkSamplerMipmapMode
sampler_mipmap_mode(enum pipe_tex_mipfilter filter)
{
switch (filter) {
case PIPE_TEX_MIPFILTER_NEAREST: return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case PIPE_TEX_MIPFILTER_LINEAR: return VK_SAMPLER_MIPMAP_MODE_LINEAR;
case PIPE_TEX_MIPFILTER_NONE:
unreachable("PIPE_TEX_MIPFILTER_NONE should be dealt with earlier");
}
unreachable("unexpected filter");
}
static VkSamplerAddressMode
sampler_address_mode(enum pipe_tex_wrap filter)
{
switch (filter) {
case PIPE_TEX_WRAP_REPEAT: return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case PIPE_TEX_WRAP_CLAMP_TO_EDGE: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case PIPE_TEX_WRAP_CLAMP_TO_BORDER: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
case PIPE_TEX_WRAP_MIRROR_REPEAT: return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE: return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER: return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; /* not technically correct, but kinda works */
default: break;
}
unreachable("unexpected wrap");
}
static VkCompareOp
compare_op(enum pipe_compare_func op)
{
switch (op) {
case PIPE_FUNC_NEVER: return VK_COMPARE_OP_NEVER;
case PIPE_FUNC_LESS: return VK_COMPARE_OP_LESS;
case PIPE_FUNC_EQUAL: return VK_COMPARE_OP_EQUAL;
case PIPE_FUNC_LEQUAL: return VK_COMPARE_OP_LESS_OR_EQUAL;
case PIPE_FUNC_GREATER: return VK_COMPARE_OP_GREATER;
case PIPE_FUNC_NOTEQUAL: return VK_COMPARE_OP_NOT_EQUAL;
case PIPE_FUNC_GEQUAL: return VK_COMPARE_OP_GREATER_OR_EQUAL;
case PIPE_FUNC_ALWAYS: return VK_COMPARE_OP_ALWAYS;
}
unreachable("unexpected compare");
}
static inline bool
wrap_needs_border_color(unsigned wrap)
{
return wrap == PIPE_TEX_WRAP_CLAMP || wrap == PIPE_TEX_WRAP_CLAMP_TO_BORDER ||
wrap == PIPE_TEX_WRAP_MIRROR_CLAMP || wrap == PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER;
}
static VkBorderColor
get_border_color(const union pipe_color_union *color, bool is_integer, bool need_custom)
{
if (is_integer) {
if (color->ui[0] == 0 && color->ui[1] == 0 && color->ui[2] == 0 && color->ui[3] == 0)
return VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
if (color->ui[0] == 0 && color->ui[1] == 0 && color->ui[2] == 0 && color->ui[3] == 1)
return VK_BORDER_COLOR_INT_OPAQUE_BLACK;
if (color->ui[0] == 1 && color->ui[1] == 1 && color->ui[2] == 1 && color->ui[3] == 1)
return VK_BORDER_COLOR_INT_OPAQUE_WHITE;
return need_custom ? VK_BORDER_COLOR_INT_CUSTOM_EXT : VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
}
if (color->f[0] == 0 && color->f[1] == 0 && color->f[2] == 0 && color->f[3] == 0)
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
if (color->f[0] == 0 && color->f[1] == 0 && color->f[2] == 0 && color->f[3] == 1)
return VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
if (color->f[0] == 1 && color->f[1] == 1 && color->f[2] == 1 && color->f[3] == 1)
return VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
return need_custom ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
}
static void *
zink_create_sampler_state(struct pipe_context *pctx,
const struct pipe_sampler_state *state)
{
struct zink_screen *screen = zink_screen(pctx->screen);
bool need_custom = false;
VkSamplerCreateInfo sci = {0};
VkSamplerCustomBorderColorCreateInfoEXT cbci = {0};
sci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sci.magFilter = zink_filter(state->mag_img_filter);
sci.minFilter = zink_filter(state->min_img_filter);
VkSamplerReductionModeCreateInfo rci;
rci.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO;
rci.pNext = NULL;
switch (state->reduction_mode) {
case PIPE_TEX_REDUCTION_MIN:
rci.reductionMode = VK_SAMPLER_REDUCTION_MODE_MIN;
break;
case PIPE_TEX_REDUCTION_MAX:
rci.reductionMode = VK_SAMPLER_REDUCTION_MODE_MAX;
break;
default:
rci.reductionMode = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE;
break;
}
if (state->reduction_mode)
sci.pNext = &rci;
if (state->min_mip_filter != PIPE_TEX_MIPFILTER_NONE) {
sci.mipmapMode = sampler_mipmap_mode(state->min_mip_filter);
sci.minLod = state->min_lod;
sci.maxLod = state->max_lod;
} else {
sci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
sci.minLod = 0;
sci.maxLod = 0.25f;
}
sci.addressModeU = sampler_address_mode(state->wrap_s);
sci.addressModeV = sampler_address_mode(state->wrap_t);
sci.addressModeW = sampler_address_mode(state->wrap_r);
sci.mipLodBias = state->lod_bias;
need_custom |= wrap_needs_border_color(state->wrap_s);
need_custom |= wrap_needs_border_color(state->wrap_t);
need_custom |= wrap_needs_border_color(state->wrap_r);
if (state->compare_mode == PIPE_TEX_COMPARE_NONE)
sci.compareOp = VK_COMPARE_OP_NEVER;
else {
sci.compareOp = compare_op(state->compare_func);
sci.compareEnable = VK_TRUE;
}
bool is_integer = state->border_color_is_integer;
sci.borderColor = get_border_color(&state->border_color, is_integer, need_custom);
if (sci.borderColor > VK_BORDER_COLOR_INT_OPAQUE_WHITE && need_custom) {
if (screen->info.have_EXT_custom_border_color &&
screen->info.border_color_feats.customBorderColorWithoutFormat) {
cbci.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT;
cbci.format = VK_FORMAT_UNDEFINED;
/* these are identical unions */
memcpy(&cbci.customBorderColor, &state->border_color, sizeof(union pipe_color_union));
cbci.pNext = sci.pNext;
sci.pNext = &cbci;
UNUSED uint32_t check = p_atomic_inc_return(&screen->cur_custom_border_color_samplers);
assert(check <= screen->info.border_color_props.maxCustomBorderColorSamplers);
} else
sci.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; // TODO with custom shader if we're super interested?
}
sci.unnormalizedCoordinates = !state->normalized_coords;
if (state->max_anisotropy > 1) {
sci.maxAnisotropy = state->max_anisotropy;
sci.anisotropyEnable = VK_TRUE;
}
struct zink_sampler_state *sampler = CALLOC_STRUCT(zink_sampler_state);
if (!sampler)
return NULL;
if (vkCreateSampler(screen->dev, &sci, NULL, &sampler->sampler) != VK_SUCCESS) {
FREE(sampler);
return NULL;
}
util_dynarray_init(&sampler->desc_set_refs.refs, NULL);
calc_descriptor_hash_sampler_state(sampler);
sampler->custom_border_color = need_custom;
return sampler;
}
ALWAYS_INLINE static VkImageLayout
get_layout_for_binding(struct zink_resource *res, enum zink_descriptor_type type, bool is_compute)
{
if (res->obj->is_buffer)
return 0;
switch (type) {
case ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW:
return res->image_bind_count[is_compute] ?
VK_IMAGE_LAYOUT_GENERAL :
res->aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) ?
//Vulkan-Docs#1490
//(res->aspect == VK_IMAGE_ASPECT_DEPTH_BIT ? VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL :
//res->aspect == VK_IMAGE_ASPECT_STENCIL_BIT ? VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL :
(res->aspect == VK_IMAGE_ASPECT_DEPTH_BIT ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL :
res->aspect == VK_IMAGE_ASPECT_STENCIL_BIT ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL) :
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
case ZINK_DESCRIPTOR_TYPE_IMAGE:
return VK_IMAGE_LAYOUT_GENERAL;
default:
break;
}
return 0;
}
ALWAYS_INLINE static struct zink_surface *
get_imageview_for_binding(struct zink_context *ctx, enum pipe_shader_type stage, enum zink_descriptor_type type, unsigned idx)
{
switch (type) {
case ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW: {
struct zink_sampler_view *sampler_view = zink_sampler_view(ctx->sampler_views[stage][idx]);
return sampler_view->base.texture ? sampler_view->image_view : NULL;
}
case ZINK_DESCRIPTOR_TYPE_IMAGE: {
struct zink_image_view *image_view = &ctx->image_views[stage][idx];
return image_view->base.resource ? image_view->surface : NULL;
}
default:
break;
}
unreachable("ACK");
return VK_NULL_HANDLE;
}
ALWAYS_INLINE static struct zink_buffer_view *
get_bufferview_for_binding(struct zink_context *ctx, enum pipe_shader_type stage, enum zink_descriptor_type type, unsigned idx)
{
switch (type) {
case ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW: {
struct zink_sampler_view *sampler_view = zink_sampler_view(ctx->sampler_views[stage][idx]);
return sampler_view->base.texture ? sampler_view->buffer_view : NULL;
}
case ZINK_DESCRIPTOR_TYPE_IMAGE: {
struct zink_image_view *image_view = &ctx->image_views[stage][idx];
return image_view->base.resource ? image_view->buffer_view : NULL;
}
default:
break;
}
unreachable("ACK");
return VK_NULL_HANDLE;
}
ALWAYS_INLINE static void
update_descriptor_state_ubo(struct zink_context *ctx, enum pipe_shader_type shader, unsigned slot)
{
struct zink_screen *screen = zink_screen(ctx->base.screen);
bool have_null_descriptors = screen->info.rb2_feats.nullDescriptor;
const enum zink_descriptor_type type = ZINK_DESCRIPTOR_TYPE_UBO;
struct zink_resource *res = zink_get_resource_for_descriptor(ctx, type, shader, slot);
ctx->di.descriptor_res[type][shader][slot] = res;
ctx->di.ubos[shader][slot].offset = ctx->ubos[shader][slot].buffer_offset;
if (res) {
ctx->di.ubos[shader][slot].buffer = res->obj->buffer;
ctx->di.ubos[shader][slot].range = ctx->ubos[shader][slot].buffer_size;
assert(ctx->di.ubos[shader][slot].range <= screen->info.props.limits.maxUniformBufferRange);
} else {
VkBuffer null_buffer = zink_resource(ctx->dummy_vertex_buffer)->obj->buffer;
ctx->di.ubos[shader][slot].buffer = have_null_descriptors ? VK_NULL_HANDLE : null_buffer;
ctx->di.ubos[shader][slot].range = VK_WHOLE_SIZE;
}
if (!slot) {
if (res)
ctx->di.push_valid |= BITFIELD64_BIT(shader);
else
ctx->di.push_valid &= ~BITFIELD64_BIT(shader);
}
}
ALWAYS_INLINE static void
update_descriptor_state_ssbo(struct zink_context *ctx, enum pipe_shader_type shader, unsigned slot)
{
struct zink_screen *screen = zink_screen(ctx->base.screen);
bool have_null_descriptors = screen->info.rb2_feats.nullDescriptor;
const enum zink_descriptor_type type = ZINK_DESCRIPTOR_TYPE_SSBO;
struct zink_resource *res = zink_get_resource_for_descriptor(ctx, type, shader, slot);
ctx->di.descriptor_res[type][shader][slot] = res;
ctx->di.ssbos[shader][slot].offset = ctx->ssbos[shader][slot].buffer_offset;
if (res) {
ctx->di.ssbos[shader][slot].buffer = res->obj->buffer;
ctx->di.ssbos[shader][slot].range = ctx->ssbos[shader][slot].buffer_size;
} else {
VkBuffer null_buffer = zink_resource(ctx->dummy_vertex_buffer)->obj->buffer;
ctx->di.ssbos[shader][slot].buffer = have_null_descriptors ? VK_NULL_HANDLE : null_buffer;
ctx->di.ssbos[shader][slot].range = VK_WHOLE_SIZE;
}
}
ALWAYS_INLINE static void
update_descriptor_state_sampler(struct zink_context *ctx, enum pipe_shader_type shader, unsigned slot)
{
struct zink_screen *screen = zink_screen(ctx->base.screen);
bool have_null_descriptors = screen->info.rb2_feats.nullDescriptor;
const enum zink_descriptor_type type = ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW;
struct zink_resource *res = zink_get_resource_for_descriptor(ctx, type, shader, slot);
ctx->di.descriptor_res[type][shader][slot] = res;
if (res) {
if (res->obj->is_buffer) {
struct zink_buffer_view *bv = get_bufferview_for_binding(ctx, shader, type, slot);
ctx->di.tbos[shader][slot] = bv->buffer_view;
ctx->di.sampler_surfaces[shader][slot].bufferview = bv;
ctx->di.sampler_surfaces[shader][slot].is_buffer = true;
} else {
struct zink_surface *surface = get_imageview_for_binding(ctx, shader, type, slot);
ctx->di.textures[shader][slot].imageLayout = get_layout_for_binding(res, type, shader == PIPE_SHADER_COMPUTE);
ctx->di.textures[shader][slot].imageView = surface->image_view;
ctx->di.sampler_surfaces[shader][slot].surface = surface;
ctx->di.sampler_surfaces[shader][slot].is_buffer = false;
}
} else {
if (likely(have_null_descriptors)) {
ctx->di.textures[shader][slot].imageView = VK_NULL_HANDLE;
ctx->di.textures[shader][slot].imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ctx->di.tbos[shader][slot] = VK_NULL_HANDLE;
} else {
struct zink_surface *null_surface = zink_surface(ctx->dummy_surface);
struct zink_buffer_view *null_bufferview = ctx->dummy_bufferview;
ctx->di.textures[shader][slot].imageView = null_surface->image_view;
ctx->di.textures[shader][slot].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
ctx->di.tbos[shader][slot] = null_bufferview->buffer_view;
}
memset(&ctx->di.sampler_surfaces[shader][slot], 0, sizeof(ctx->di.sampler_surfaces[shader][slot]));
}
}
ALWAYS_INLINE static void
update_descriptor_state_image(struct zink_context *ctx, enum pipe_shader_type shader, unsigned slot)
{
struct zink_screen *screen = zink_screen(ctx->base.screen);
bool have_null_descriptors = screen->info.rb2_feats.nullDescriptor;
const enum zink_descriptor_type type = ZINK_DESCRIPTOR_TYPE_IMAGE;
struct zink_resource *res = zink_get_resource_for_descriptor(ctx, type, shader, slot);
ctx->di.descriptor_res[type][shader][slot] = res;
if (res) {
if (res->obj->is_buffer) {
struct zink_buffer_view *bv = get_bufferview_for_binding(ctx, shader, type, slot);
ctx->di.texel_images[shader][slot] = bv->buffer_view;
ctx->di.image_surfaces[shader][slot].bufferview = bv;
ctx->di.image_surfaces[shader][slot].is_buffer = true;
} else {
struct zink_surface *surface = get_imageview_for_binding(ctx, shader, type, slot);
ctx->di.images[shader][slot].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
ctx->di.images[shader][slot].imageView = surface->image_view;
ctx->di.image_surfaces[shader][slot].surface = surface;
ctx->di.image_surfaces[shader][slot].is_buffer = false;
}
} else {
if (likely(have_null_descriptors)) {
memset(&ctx->di.images[shader][slot], 0, sizeof(ctx->di.images[shader][slot]));
ctx->di.texel_images[shader][slot] = VK_NULL_HANDLE;
} else {
struct zink_surface *null_surface = zink_surface(ctx->dummy_surface);
struct zink_buffer_view *null_bufferview = ctx->dummy_bufferview;
ctx->di.images[shader][slot].imageView = null_surface->image_view;
ctx->di.images[shader][slot].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
ctx->di.texel_images[shader][slot] = null_bufferview->buffer_view;
}
memset(&ctx->di.image_surfaces[shader][slot], 0, sizeof(ctx->di.image_surfaces[shader][slot]));
}
}
static void
zink_bind_sampler_states(struct pipe_context *pctx,
enum pipe_shader_type shader,
unsigned start_slot,
unsigned num_samplers,
void **samplers)
{
struct zink_context *ctx = zink_context(pctx);
for (unsigned i = 0; i < num_samplers; ++i) {
struct zink_sampler_state *state = samplers[i];
if (ctx->sampler_states[shader][start_slot + i] != state)
zink_screen(pctx->screen)->context_invalidate_descriptor_state(ctx, shader, ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW, start_slot, 1);
ctx->sampler_states[shader][start_slot + i] = state;
ctx->di.textures[shader][start_slot + i].sampler = state ? state->sampler : VK_NULL_HANDLE;
if (state)
zink_batch_usage_set(&state->batch_uses, ctx->batch.state);
}
ctx->di.num_samplers[shader] = start_slot + num_samplers;
}
static void
zink_delete_sampler_state(struct pipe_context *pctx,
void *sampler_state)
{
struct zink_sampler_state *sampler = sampler_state;
struct zink_batch *batch = &zink_context(pctx)->batch;
zink_descriptor_set_refs_clear(&sampler->desc_set_refs, sampler_state);
util_dynarray_append(&batch->state->zombie_samplers, VkSampler,
sampler->sampler);
if (sampler->custom_border_color)
p_atomic_dec(&zink_screen(pctx->screen)->cur_custom_border_color_samplers);
FREE(sampler);
}
static VkComponentSwizzle
component_mapping(enum pipe_swizzle swizzle)
{
switch (swizzle) {
case PIPE_SWIZZLE_X: return VK_COMPONENT_SWIZZLE_R;
case PIPE_SWIZZLE_Y: return VK_COMPONENT_SWIZZLE_G;
case PIPE_SWIZZLE_Z: return VK_COMPONENT_SWIZZLE_B;
case PIPE_SWIZZLE_W: return VK_COMPONENT_SWIZZLE_A;
case PIPE_SWIZZLE_0: return VK_COMPONENT_SWIZZLE_ZERO;
case PIPE_SWIZZLE_1: return VK_COMPONENT_SWIZZLE_ONE;
case PIPE_SWIZZLE_NONE: return VK_COMPONENT_SWIZZLE_IDENTITY; // ???
default:
unreachable("unexpected swizzle");
}
}
static VkImageAspectFlags
sampler_aspect_from_format(enum pipe_format fmt)
{
if (util_format_is_depth_or_stencil(fmt)) {
const struct util_format_description *desc = util_format_description(fmt);
if (util_format_has_depth(desc))
return VK_IMAGE_ASPECT_DEPTH_BIT;
assert(util_format_has_stencil(desc));
return VK_IMAGE_ASPECT_STENCIL_BIT;
} else
return VK_IMAGE_ASPECT_COLOR_BIT;
}
static uint32_t
hash_bufferview(void *bvci)
{
size_t offset = offsetof(VkBufferViewCreateInfo, flags);
return _mesa_hash_data((char*)bvci + offset, sizeof(VkBufferViewCreateInfo) - offset);
}
static struct zink_buffer_view *
get_buffer_view(struct zink_context *ctx, struct zink_resource *res, enum pipe_format format, uint32_t offset, uint32_t range)
{
struct zink_screen *screen = zink_screen(ctx->base.screen);
struct zink_buffer_view *buffer_view = NULL;
VkBufferViewCreateInfo bvci = {0};
bvci.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
bvci.buffer = res->obj->buffer;
bvci.format = zink_get_format(screen, format);
assert(bvci.format);
bvci.offset = offset;
bvci.range = range;
uint32_t hash = hash_bufferview(&bvci);
simple_mtx_lock(&screen->bufferview_mtx);
struct hash_entry *he = _mesa_hash_table_search_pre_hashed(&screen->bufferview_cache, hash, &bvci);
if (he) {
buffer_view = he->data;
p_atomic_inc(&buffer_view->reference.count);
} else {
VkBufferView view;
if (vkCreateBufferView(screen->dev, &bvci, NULL, &view) != VK_SUCCESS)
goto out;
buffer_view = CALLOC_STRUCT(zink_buffer_view);
if (!buffer_view) {
vkDestroyBufferView(screen->dev, view, NULL);
goto out;
}
pipe_reference_init(&buffer_view->reference, 1);
util_dynarray_init(&buffer_view->desc_set_refs.refs, NULL);
buffer_view->bvci = bvci;
buffer_view->buffer_view = view;
buffer_view->hash = hash;
_mesa_hash_table_insert_pre_hashed(&screen->bufferview_cache, hash, &buffer_view->bvci, buffer_view);
}
out:
simple_mtx_unlock(&screen->bufferview_mtx);
return buffer_view;
}
static inline enum pipe_swizzle
clamp_void_swizzle(const struct util_format_description *desc, enum pipe_swizzle swizzle)
{
switch (swizzle) {
case PIPE_SWIZZLE_X:
case PIPE_SWIZZLE_Y:
case PIPE_SWIZZLE_Z:
case PIPE_SWIZZLE_W:
return desc->channel[swizzle].type == UTIL_FORMAT_TYPE_VOID ? PIPE_SWIZZLE_1 : swizzle;
default:
break;
}
return swizzle;
}
ALWAYS_INLINE static enum pipe_swizzle
clamp_zs_swizzle(enum pipe_swizzle swizzle)
{
switch (swizzle) {
case PIPE_SWIZZLE_X:
case PIPE_SWIZZLE_Y:
case PIPE_SWIZZLE_Z:
case PIPE_SWIZZLE_W:
return PIPE_SWIZZLE_X;
default:
break;
}
return swizzle;
}
static inline bool
format_is_usable_rgba_variant(const struct util_format_description *desc)
{
unsigned chan;
if(desc->block.width != 1 ||
desc->block.height != 1 ||
(desc->block.bits != 32 && desc->block.bits != 64))
return false;
if (desc->nr_channels != 4)
return false;
unsigned size = desc->channel[0].size;
for(chan = 0; chan < 4; ++chan) {
if(desc->channel[chan].size != size)
return false;
}
return true;
}
static struct pipe_sampler_view *
zink_create_sampler_view(struct pipe_context *pctx, struct pipe_resource *pres,
const struct pipe_sampler_view *state)
{
struct zink_screen *screen = zink_screen(pctx->screen);
struct zink_resource *res = zink_resource(pres);
struct zink_sampler_view *sampler_view = CALLOC_STRUCT(zink_sampler_view);
bool err;
sampler_view->base = *state;
sampler_view->base.texture = NULL;
pipe_resource_reference(&sampler_view->base.texture, pres);
sampler_view->base.reference.count = 1;
sampler_view->base.context = pctx;
if (state->target != PIPE_BUFFER) {
VkImageViewCreateInfo ivci;
struct pipe_surface templ = {0};
templ.u.tex.level = state->u.tex.first_level;
templ.format = state->format;
if (state->target != PIPE_TEXTURE_3D) {
templ.u.tex.first_layer = state->u.tex.first_layer;
templ.u.tex.last_layer = state->u.tex.last_layer;
}
ivci = create_ivci(screen, res, &templ, state->target);
ivci.subresourceRange.levelCount = state->u.tex.last_level - state->u.tex.first_level + 1;
ivci.subresourceRange.aspectMask = sampler_aspect_from_format(state->format);
/* samplers for stencil aspects of packed formats need to always use stencil swizzle */
if (ivci.subresourceRange.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
ivci.components.r = component_mapping(clamp_zs_swizzle(sampler_view->base.swizzle_r));
ivci.components.g = component_mapping(clamp_zs_swizzle(sampler_view->base.swizzle_g));
ivci.components.b = component_mapping(clamp_zs_swizzle(sampler_view->base.swizzle_b));
ivci.components.a = component_mapping(clamp_zs_swizzle(sampler_view->base.swizzle_a));
} else {
/* if we have e.g., R8G8B8X8, then we have to ignore alpha since we're just emulating
* these formats
*/
const struct util_format_description *desc = util_format_description(state->format);
if (format_is_usable_rgba_variant(desc)) {
sampler_view->base.swizzle_r = clamp_void_swizzle(desc, sampler_view->base.swizzle_r);
sampler_view->base.swizzle_g = clamp_void_swizzle(desc, sampler_view->base.swizzle_g);
sampler_view->base.swizzle_b = clamp_void_swizzle(desc, sampler_view->base.swizzle_b);
sampler_view->base.swizzle_a = clamp_void_swizzle(desc, sampler_view->base.swizzle_a);
}
ivci.components.r = component_mapping(sampler_view->base.swizzle_r);
ivci.components.g = component_mapping(sampler_view->base.swizzle_g);
ivci.components.b = component_mapping(sampler_view->base.swizzle_b);
ivci.components.a = component_mapping(sampler_view->base.swizzle_a);
}
assert(ivci.format);
sampler_view->image_view = (struct zink_surface*)zink_get_surface(zink_context(pctx), pres, &templ, &ivci);
err = !sampler_view->image_view;
} else {
sampler_view->buffer_view = get_buffer_view(zink_context(pctx), res, state->format, state->u.buf.offset, state->u.buf.size);
err = !sampler_view->buffer_view;
}
if (err) {
FREE(sampler_view);
return NULL;
}
return &sampler_view->base;
}
void
zink_destroy_buffer_view(struct zink_screen *screen, struct zink_buffer_view *buffer_view)
{
simple_mtx_lock(&screen->bufferview_mtx);
struct hash_entry *he = _mesa_hash_table_search_pre_hashed(&screen->bufferview_cache, buffer_view->hash, &buffer_view->bvci);
assert(he);
_mesa_hash_table_remove(&screen->bufferview_cache, he);
simple_mtx_unlock(&screen->bufferview_mtx);
vkDestroyBufferView(screen->dev, buffer_view->buffer_view, NULL);
zink_descriptor_set_refs_clear(&buffer_view->desc_set_refs, buffer_view);
FREE(buffer_view);
}
static void
zink_sampler_view_destroy(struct pipe_context *pctx,
struct pipe_sampler_view *pview)
{
struct zink_sampler_view *view = zink_sampler_view(pview);
if (pview->texture->target == PIPE_BUFFER)
zink_buffer_view_reference(zink_screen(pctx->screen), &view->buffer_view, NULL);
else {
zink_surface_reference(zink_screen(pctx->screen), &view->image_view, NULL);
}
pipe_resource_reference(&pview->texture, NULL);
FREE(view);
}
static void
zink_get_sample_position(struct pipe_context *ctx,
unsigned sample_count,
unsigned sample_index,
float *out_value)
{
/* TODO: handle this I guess */
assert(zink_screen(ctx->screen)->info.props.limits.standardSampleLocations);
/* from 26.4. Multisampling */
switch (sample_count) {
case 0:
case 1: {
float pos[][2] = { {0.5,0.5}, };
out_value[0] = pos[sample_index][0];
out_value[1] = pos[sample_index][1];
break;
}
case 2: {
float pos[][2] = { {0.75,0.75},
{0.25,0.25}, };
out_value[0] = pos[sample_index][0];
out_value[1] = pos[sample_index][1];
break;
}
case 4: {
float pos[][2] = { {0.375, 0.125},
{0.875, 0.375},
{0.125, 0.625},
{0.625, 0.875}, };
out_value[0] = pos[sample_index][0];
out_value[1] = pos[sample_index][1];
break;
}
case 8: {
float pos[][2] = { {0.5625, 0.3125},
{0.4375, 0.6875},
{0.8125, 0.5625},
{0.3125, 0.1875},
{0.1875, 0.8125},
{0.0625, 0.4375},
{0.6875, 0.9375},
{0.9375, 0.0625}, };
out_value[0] = pos[sample_index][0];
out_value[1] = pos[sample_index][1];
break;
}
case 16: {
float pos[][2] = { {0.5625, 0.5625},
{0.4375, 0.3125},
{0.3125, 0.625},
{0.75, 0.4375},
{0.1875, 0.375},
{0.625, 0.8125},
{0.8125, 0.6875},
{0.6875, 0.1875},
{0.375, 0.875},
{0.5, 0.0625},
{0.25, 0.125},
{0.125, 0.75},
{0.0, 0.5},
{0.9375, 0.25},
{0.875, 0.9375},
{0.0625, 0.0}, };
out_value[0] = pos[sample_index][0];
out_value[1] = pos[sample_index][1];
break;
}
default:
unreachable("unhandled sample count!");
}
}
static void
zink_set_polygon_stipple(struct pipe_context *pctx,
const struct pipe_poly_stipple *ps)
{
}
ALWAYS_INLINE static void
update_res_bind_count(struct zink_context *ctx, struct zink_resource *res, bool is_compute, bool decrement)
{
if (decrement) {
assert(res->bind_count[is_compute]);
if (!--res->bind_count[is_compute])
_mesa_set_remove_key(ctx->need_barriers[is_compute], res);
check_resource_for_batch_ref(ctx, res);
} else
res->bind_count[is_compute]++;
}
ALWAYS_INLINE static void
update_existing_vbo(struct zink_context *ctx, unsigned slot)
{
if (!ctx->vertex_buffers[slot].buffer.resource)
return;
struct zink_resource *res = zink_resource(ctx->vertex_buffers[slot].buffer.resource);
res->vbo_bind_mask &= ~BITFIELD_BIT(slot);
ctx->vbufs[slot] = VK_NULL_HANDLE;
ctx->vbuf_offsets[slot] = 0;
update_res_bind_count(ctx, res, false, true);
}
ALWAYS_INLINE static void
set_vertex_buffer_clamped(struct zink_context *ctx, unsigned slot)
{
const struct pipe_vertex_buffer *ctx_vb = &ctx->vertex_buffers[slot];
struct zink_resource *res = zink_resource(ctx_vb->buffer.resource);
struct zink_screen *screen = zink_screen(ctx->base.screen);
if (ctx_vb->buffer_offset > screen->info.props.limits.maxVertexInputAttributeOffset) {
/* buffer offset exceeds maximum: make a tmp buffer at this offset */
ctx->vbufs[slot] = zink_resource_tmp_buffer(screen, res, ctx_vb->buffer_offset, 0, &ctx->vbuf_offsets[slot]);
util_dynarray_append(&res->obj->tmp, VkBuffer, ctx->vbufs[slot]);
/* the driver is broken and sets a min alignment that's larger than its max offset: rebind as staging buffer */
if (unlikely(ctx->vbuf_offsets[slot] > screen->info.props.limits.maxVertexInputAttributeOffset)) {
static bool warned = false;
if (!warned)
debug_printf("zink: this vulkan driver is BROKEN! maxVertexInputAttributeOffset < VkMemoryRequirements::alignment\n");
warned = true;
}
} else {
ctx->vbufs[slot] = res->obj->buffer;
ctx->vbuf_offsets[slot] = ctx_vb->buffer_offset;
}
assert(ctx->vbufs[slot]);
}
static void
zink_set_vertex_buffers(struct pipe_context *pctx,
unsigned start_slot,
unsigned num_buffers,
unsigned unbind_num_trailing_slots,
bool take_ownership,
const struct pipe_vertex_buffer *buffers)
{
struct zink_context *ctx = zink_context(pctx);
uint32_t enabled_buffers = ctx->gfx_pipeline_state.vertex_buffers_enabled_mask;
enabled_buffers |= u_bit_consecutive(start_slot, num_buffers);
enabled_buffers &= ~u_bit_consecutive(start_slot + num_buffers, unbind_num_trailing_slots);
if (buffers) {
if (!zink_screen(pctx->screen)->info.have_EXT_extended_dynamic_state)
ctx->vertex_state_changed = true;
for (unsigned i = 0; i < num_buffers; ++i) {
const struct pipe_vertex_buffer *vb = buffers + i;
struct pipe_vertex_buffer *ctx_vb = &ctx->vertex_buffers[start_slot + i];
update_existing_vbo(ctx, start_slot + i);
if (!take_ownership)
pipe_resource_reference(&ctx_vb->buffer.resource, vb->buffer.resource);
else {
pipe_resource_reference(&ctx_vb->buffer.resource, NULL);
ctx_vb->buffer.resource = vb->buffer.resource;
}
if (vb->buffer.resource) {
struct zink_resource *res = zink_resource(vb->buffer.resource);
res->vbo_bind_mask |= BITFIELD_BIT(start_slot + i);
update_res_bind_count(ctx, res, false, false);
ctx_vb->stride = vb->stride;
ctx_vb->buffer_offset = vb->buffer_offset;
/* always barrier before possible rebind */
zink_resource_buffer_barrier(ctx, NULL, res, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT,
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT);
set_vertex_buffer_clamped(ctx, start_slot + i);
zink_batch_resource_usage_set(&ctx->batch, res, false);
}
}
} else {
if (!zink_screen(pctx->screen)->info.have_EXT_extended_dynamic_state)
ctx->vertex_state_changed = true;
for (unsigned i = 0; i < num_buffers; ++i) {
update_existing_vbo(ctx, start_slot + i);
pipe_resource_reference(&ctx->vertex_buffers[start_slot + i].buffer.resource, NULL);
}
}
for (unsigned i = 0; i < unbind_num_trailing_slots; i++) {
update_existing_vbo(ctx, start_slot + i);
pipe_resource_reference(&ctx->vertex_buffers[start_slot + i].buffer.resource, NULL);
}
ctx->gfx_pipeline_state.vertex_buffers_enabled_mask = enabled_buffers;
ctx->vertex_buffers_dirty = num_buffers > 0;
}
static void
zink_set_viewport_states(struct pipe_context *pctx,
unsigned start_slot,
unsigned num_viewports,
const struct pipe_viewport_state *state)
{
struct zink_context *ctx = zink_context(pctx);
for (unsigned i = 0; i < num_viewports; ++i)
ctx->vp_state.viewport_states[start_slot + i] = state[i];
ctx->vp_state.num_viewports = start_slot + num_viewports;
if (!zink_screen(pctx->screen)->info.have_EXT_extended_dynamic_state) {
if (ctx->gfx_pipeline_state.num_viewports != ctx->vp_state.num_viewports)
ctx->gfx_pipeline_state.dirty = true;
ctx->gfx_pipeline_state.num_viewports = ctx->vp_state.num_viewports;
}
ctx->vp_state_changed = true;
}
static void
zink_set_scissor_states(struct pipe_context *pctx,
unsigned start_slot, unsigned num_scissors,
const struct pipe_scissor_state *states)
{
struct zink_context *ctx = zink_context(pctx);
for (unsigned i = 0; i < num_scissors; i++)
ctx->vp_state.scissor_states[start_slot + i] = states[i];
ctx->scissor_changed = true;
}
static void
zink_set_inlinable_constants(struct pipe_context *pctx,
enum pipe_shader_type shader,
uint num_values, uint32_t *values)
{
struct zink_context *ctx = (struct zink_context *)pctx;
memcpy(ctx->inlinable_uniforms[shader], values, num_values * 4);
ctx->dirty_shader_stages |= 1 << shader;
ctx->inlinable_uniforms_valid_mask |= 1 << shader;
}
ALWAYS_INLINE static void
unbind_ubo(struct zink_context *ctx, struct zink_resource *res, enum pipe_shader_type pstage, unsigned slot)
{
if (!res)
return;
res->ubo_bind_mask[pstage] &= ~BITFIELD_BIT(slot);
res->ubo_bind_count[pstage == PIPE_SHADER_COMPUTE]--;
update_res_bind_count(ctx, res, pstage == PIPE_SHADER_COMPUTE, true);
}
static void
zink_set_constant_buffer(struct pipe_context *pctx,
enum pipe_shader_type shader, uint index,
bool take_ownership,
const struct pipe_constant_buffer *cb)
{
struct zink_context *ctx = zink_context(pctx);
bool update = false;
struct zink_resource *res = zink_resource(ctx->ubos[shader][index].buffer);
if (cb) {
struct pipe_resource *buffer = cb->buffer;
unsigned offset = cb->buffer_offset;
struct zink_screen *screen = zink_screen(pctx->screen);
if (cb->user_buffer) {
u_upload_data(ctx->base.const_uploader, 0, cb->buffer_size,
screen->info.props.limits.minUniformBufferOffsetAlignment,
cb->user_buffer, &offset, &buffer);
}
struct zink_resource *new_res = zink_resource(buffer);
if (new_res) {
if (new_res != res) {
unbind_ubo(ctx, res, shader, index);
new_res->bind_history |= BITFIELD_BIT(ZINK_DESCRIPTOR_TYPE_UBO);
new_res->bind_stages |= 1 << shader;
new_res->ubo_bind_count[shader == PIPE_SHADER_COMPUTE]++;
new_res->ubo_bind_mask[shader] |= BITFIELD_BIT(index);
update_res_bind_count(ctx, new_res, shader == PIPE_SHADER_COMPUTE, false);
}
zink_batch_resource_usage_set(&ctx->batch, new_res, false);
zink_fake_buffer_barrier(new_res, VK_ACCESS_UNIFORM_READ_BIT,
zink_pipeline_flags_from_pipe_stage(shader));
}
update |= ((index || screen->descriptor_mode == ZINK_DESCRIPTOR_MODE_LAZY) && ctx->ubos[shader][index].buffer_offset != offset) ||
!!res != !!buffer || (res && res->obj->buffer != new_res->obj->buffer) ||
ctx->ubos[shader][index].buffer_size != cb->buffer_size;
if (take_ownership) {
pipe_resource_reference(&ctx->ubos[shader][index].buffer, NULL);
ctx->ubos[shader][index].buffer = buffer;
} else {
pipe_resource_reference(&ctx->ubos[shader][index].buffer, buffer);
}
ctx->ubos[shader][index].buffer_offset = offset;
ctx->ubos[shader][index].buffer_size = cb->buffer_size;
ctx->ubos[shader][index].user_buffer = NULL;
if (cb->user_buffer)
pipe_resource_reference(&buffer, NULL);
if (index + 1 >= ctx->di.num_ubos[shader])
ctx->di.num_ubos[shader] = index + 1;
} else {
if (res)
unbind_ubo(ctx, res, shader, index);
update = !!ctx->ubos[shader][index].buffer;
pipe_resource_reference(&ctx->ubos[shader][index].buffer, NULL);
ctx->ubos[shader][index].buffer_offset = 0;
ctx->ubos[shader][index].buffer_size = 0;
ctx->ubos[shader][index].user_buffer = NULL;
if (ctx->di.num_ubos[shader] == index + 1)
ctx->di.num_ubos[shader]--;
}
if (index == 0) {
/* Invalidate current inlinable uniforms. */
ctx->inlinable_uniforms_valid_mask &= ~(1 << shader);
}
update_descriptor_state_ubo(ctx, shader, index);
if (update)
zink_screen(pctx->screen)->context_invalidate_descriptor_state(ctx, shader, ZINK_DESCRIPTOR_TYPE_UBO, index, 1);
}
ALWAYS_INLINE static void
unbind_ssbo(struct zink_context *ctx, struct zink_resource *res, enum pipe_shader_type pstage, unsigned slot, bool writable)
{
if (!res)
return;
res->ssbo_bind_mask[pstage] &= ~BITFIELD_BIT(slot);
update_res_bind_count(ctx, res, pstage == PIPE_SHADER_COMPUTE, true);
if (writable)
res->write_bind_count[pstage == PIPE_SHADER_COMPUTE]--;
}
static void
zink_set_shader_buffers(struct pipe_context *pctx,
enum pipe_shader_type p_stage,
unsigned start_slot, unsigned count,
const struct pipe_shader_buffer *buffers,
unsigned writable_bitmask)
{
struct zink_context *ctx = zink_context(pctx);
bool update = false;
unsigned max_slot = 0;
unsigned modified_bits = u_bit_consecutive(start_slot, count);
unsigned old_writable_mask = ctx->writable_ssbos[p_stage];
ctx->writable_ssbos[p_stage] &= ~modified_bits;
ctx->writable_ssbos[p_stage] |= writable_bitmask << start_slot;
for (unsigned i = 0; i < count; i++) {
struct pipe_shader_buffer *ssbo = &ctx->ssbos[p_stage][start_slot + i];
struct zink_resource *res = ssbo->buffer ? zink_resource(ssbo->buffer) : NULL;
bool was_writable = old_writable_mask & BITFIELD64_BIT(start_slot + i);
if (buffers && buffers[i].buffer) {
struct zink_resource *new_res = zink_resource(buffers[i].buffer);
if (new_res != res) {
unbind_ssbo(ctx, res, p_stage, i, was_writable);
new_res->bind_history |= BITFIELD_BIT(ZINK_DESCRIPTOR_TYPE_SSBO);
new_res->bind_stages |= 1 << p_stage;
new_res->ssbo_bind_mask[p_stage] |= BITFIELD_BIT(i);
update_res_bind_count(ctx, new_res, p_stage == PIPE_SHADER_COMPUTE, false);
}
VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT;
if (ctx->writable_ssbos[p_stage] & BITFIELD64_BIT(start_slot + i)) {
new_res->write_bind_count[p_stage == PIPE_SHADER_COMPUTE]++;
access |= VK_ACCESS_SHADER_WRITE_BIT;
}
pipe_resource_reference(&ssbo->buffer, &new_res->base.b);
zink_batch_resource_usage_set(&ctx->batch, new_res, access & VK_ACCESS_SHADER_WRITE_BIT);
ssbo->buffer_offset = buffers[i].buffer_offset;
ssbo->buffer_size = MIN2(buffers[i].buffer_size, new_res->base.b.width0 - ssbo->buffer_offset);
util_range_add(&new_res->base.b, &new_res->valid_buffer_range, ssbo->buffer_offset,
ssbo->buffer_offset + ssbo->buffer_size);
zink_fake_buffer_barrier(new_res, access,
zink_pipeline_flags_from_pipe_stage(p_stage));
update = true;
max_slot = MAX2(max_slot, start_slot + i);
} else {
update = !!res;
if (res)
unbind_ssbo(ctx, res, p_stage, i, was_writable);
pipe_resource_reference(&ssbo->buffer, NULL);
ssbo->buffer_offset = 0;
ssbo->buffer_size = 0;
}
update_descriptor_state_ssbo(ctx, p_stage, start_slot + i);
}
if (start_slot + count >= ctx->di.num_ssbos[p_stage])
ctx->di.num_ssbos[p_stage] = max_slot + 1;
if (update)
zink_screen(pctx->screen)->context_invalidate_descriptor_state(ctx, p_stage, ZINK_DESCRIPTOR_TYPE_SSBO, start_slot, count);
}
static void
update_binds_for_samplerviews(struct zink_context *ctx, struct zink_resource *res, bool is_compute)
{
VkImageLayout layout = get_layout_for_binding(res, ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW, is_compute);
if (is_compute) {
u_foreach_bit(slot, res->sampler_binds[PIPE_SHADER_COMPUTE]) {
if (ctx->di.textures[PIPE_SHADER_COMPUTE][slot].imageLayout != layout) {
update_descriptor_state_sampler(ctx, PIPE_SHADER_COMPUTE, slot);
zink_screen(ctx->base.screen)->context_invalidate_descriptor_state(ctx, PIPE_SHADER_COMPUTE, ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW, slot, 1);
}
}
} else {
for (unsigned i = 0; i < ZINK_SHADER_COUNT; i++) {
u_foreach_bit(slot, res->sampler_binds[i]) {
if (ctx->di.textures[i][slot].imageLayout != layout) {
update_descriptor_state_sampler(ctx, i, slot);
zink_screen(ctx->base.screen)->context_invalidate_descriptor_state(ctx, i, ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW, slot, 1);
}
}
}
}
}
static void
flush_pending_clears(struct zink_context *ctx, struct zink_resource *res)
{
if (res->fb_binds && ctx->clears_enabled)
zink_fb_clears_apply(ctx, &res->base.b);
}
static inline void
unbind_shader_image_counts(struct zink_context *ctx, struct zink_resource *res, bool is_compute, bool writable)
{
update_res_bind_count(ctx, res, is_compute, true);
if (writable)
res->write_bind_count[is_compute]--;
res->image_bind_count[is_compute]--;
/* if this was the last image bind, the sampler bind layouts must be updated */
if (!res->obj->is_buffer && !res->image_bind_count[is_compute] && res->bind_count[is_compute])
update_binds_for_samplerviews(ctx, res, is_compute);
}
ALWAYS_INLINE static void
check_for_layout_update(struct zink_context *ctx, struct zink_resource *res, bool is_compute)
{
VkImageLayout layout = res->bind_count[is_compute] ? zink_descriptor_util_image_layout_eval(res, is_compute) : VK_IMAGE_LAYOUT_UNDEFINED;
VkImageLayout other_layout = res->bind_count[!is_compute] ? zink_descriptor_util_image_layout_eval(res, !is_compute) : VK_IMAGE_LAYOUT_UNDEFINED;
if (res->bind_count[is_compute] && res->layout != layout)
_mesa_set_add(ctx->need_barriers[is_compute], res);
if (res->bind_count[!is_compute] && (layout != other_layout || res->layout != other_layout))
_mesa_set_add(ctx->need_barriers[!is_compute], res);
}
static void
unbind_shader_image(struct zink_context *ctx, enum pipe_shader_type stage, unsigned slot)
{
struct zink_image_view *image_view = &ctx->image_views[stage][slot];
bool is_compute = stage == PIPE_SHADER_COMPUTE;
if (!image_view->base.resource)
return;
struct zink_resource *res = zink_resource(image_view->base.resource);
unbind_shader_image_counts(ctx, res, is_compute, image_view->base.access & PIPE_IMAGE_ACCESS_WRITE);
if (image_view->base.resource->target == PIPE_BUFFER) {
if (zink_batch_usage_exists(image_view->buffer_view->batch_uses))
zink_batch_reference_bufferview(&ctx->batch, image_view->buffer_view);
zink_buffer_view_reference(zink_screen(ctx->base.screen), &image_view->buffer_view, NULL);
} else {
if (!res->image_bind_count[is_compute])
check_for_layout_update(ctx, res, is_compute);
if (zink_batch_usage_exists(image_view->surface->batch_uses))
zink_batch_reference_surface(&ctx->batch, image_view->surface);
zink_surface_reference(zink_screen(ctx->base.screen), &image_view->surface, NULL);
}
pipe_resource_reference(&image_view->base.resource, NULL);
image_view->base.resource = NULL;
image_view->surface = NULL;
}
static void
zink_set_shader_images(struct pipe_context *pctx,
enum pipe_shader_type p_stage,
unsigned start_slot, unsigned count,
unsigned unbind_num_trailing_slots,
const struct pipe_image_view *images)
{
struct zink_context *ctx = zink_context(pctx);
bool update = false;
for (unsigned i = 0; i < count; i++) {
struct zink_image_view *image_view = &ctx->image_views[p_stage][start_slot + i];
if (images && images[i].resource) {
struct zink_resource *res = zink_resource(images[i].resource);
struct zink_resource *old_res = zink_resource(image_view->base.resource);
if (!zink_resource_object_init_storage(ctx, res)) {
debug_printf("couldn't create storage image!");
continue;
}
if (res != old_res) {
if (old_res) {
unbind_shader_image_counts(ctx, old_res, p_stage == PIPE_SHADER_COMPUTE, image_view->base.access & PIPE_IMAGE_ACCESS_WRITE);
if (!old_res->obj->is_buffer && !old_res->image_bind_count[p_stage == PIPE_SHADER_COMPUTE])
check_for_layout_update(ctx, old_res, p_stage == PIPE_SHADER_COMPUTE);
}
res->bind_history |= BITFIELD_BIT(ZINK_DESCRIPTOR_TYPE_IMAGE);
res->bind_stages |= 1 << p_stage;
update_res_bind_count(ctx, res, p_stage == PIPE_SHADER_COMPUTE, false);
}
util_copy_image_view(&image_view->base, images + i);
VkAccessFlags access = 0;
if (image_view->base.access & PIPE_IMAGE_ACCESS_WRITE) {
zink_resource(image_view->base.resource)->write_bind_count[p_stage == PIPE_SHADER_COMPUTE]++;
access |= VK_ACCESS_SHADER_WRITE_BIT;
}
if (image_view->base.access & PIPE_IMAGE_ACCESS_READ) {
access |= VK_ACCESS_SHADER_READ_BIT;
}
res->image_bind_count[p_stage == PIPE_SHADER_COMPUTE]++;
if (images[i].resource->target == PIPE_BUFFER) {
image_view->buffer_view = get_buffer_view(ctx, res, images[i].format, images[i].u.buf.offset, images[i].u.buf.size);
assert(image_view->buffer_view);
util_range_add(&res->base.b, &res->valid_buffer_range, images[i].u.buf.offset,
images[i].u.buf.offset + images[i].u.buf.size);
zink_batch_usage_set(&image_view->buffer_view->batch_uses, ctx->batch.state);
zink_fake_buffer_barrier(res, access,
zink_pipeline_flags_from_pipe_stage(p_stage));
} else {
struct pipe_surface tmpl = {0};
tmpl.format = images[i].format;
tmpl.nr_samples = 1;
tmpl.u.tex.level = images[i].u.tex.level;
tmpl.u.tex.first_layer = images[i].u.tex.first_layer;
tmpl.u.tex.last_layer = images[i].u.tex.last_layer;
image_view->surface = zink_surface(pctx->create_surface(pctx, &res->base.b, &tmpl));
assert(image_view->surface);
/* if this is the first image bind and there are sampler binds, the image's sampler layout
* must be updated to GENERAL
*/
if (res->image_bind_count[p_stage == PIPE_SHADER_COMPUTE] == 1 &&
res->bind_count[p_stage == PIPE_SHADER_COMPUTE] > 1)
update_binds_for_samplerviews(ctx, res, p_stage == PIPE_SHADER_COMPUTE);
check_for_layout_update(ctx, res, p_stage == PIPE_SHADER_COMPUTE);
zink_batch_usage_set(&image_view->surface->batch_uses, ctx->batch.state);
flush_pending_clears(ctx, res);
}
zink_batch_resource_usage_set(&ctx->batch, zink_resource(image_view->base.resource),
zink_resource_access_is_write(access));
update = true;
} else if (image_view->base.resource) {
update |= !!image_view->base.resource;
unbind_shader_image(ctx, p_stage, start_slot + i);
}
update_descriptor_state_image(ctx, p_stage, start_slot + i);
}
for (unsigned i = 0; i < unbind_num_trailing_slots; i++) {
update |= !!ctx->image_views[p_stage][start_slot + count + i].base.resource;
unbind_shader_image(ctx, p_stage, start_slot + count + i);
update_descriptor_state_image(ctx, p_stage, start_slot + count + i);
}
ctx->di.num_images[p_stage] = start_slot + count;
if (update)
zink_screen(pctx->screen)->context_invalidate_descriptor_state(ctx, p_stage, ZINK_DESCRIPTOR_TYPE_IMAGE, start_slot, count);
}
ALWAYS_INLINE static void
check_samplerview_for_batch_ref(struct zink_context *ctx, struct zink_sampler_view *sv)
{
const struct zink_resource *res = zink_resource(sv->base.texture);
if ((res->obj->is_buffer && zink_batch_usage_exists(sv->buffer_view->batch_uses)) ||
(!res->obj->is_buffer && zink_batch_usage_exists(sv->image_view->batch_uses)))
zink_batch_reference_sampler_view(&ctx->batch, sv);
}
ALWAYS_INLINE static void
unbind_samplerview(struct zink_context *ctx, enum pipe_shader_type stage, unsigned slot)
{
struct zink_sampler_view *sv = zink_sampler_view(ctx->sampler_views[stage][slot]);
if (!sv || !sv->base.texture)
return;
struct zink_resource *res = zink_resource(sv->base.texture);
check_samplerview_for_batch_ref(ctx, sv);
update_res_bind_count(ctx, res, stage == PIPE_SHADER_COMPUTE, true);
res->sampler_binds[stage] &= ~BITFIELD_BIT(slot);
}
static void
zink_set_sampler_views(struct pipe_context *pctx,
enum pipe_shader_type shader_type,
unsigned start_slot,
unsigned num_views,
unsigned unbind_num_trailing_slots,
bool take_ownership,
struct pipe_sampler_view **views)
{
struct zink_context *ctx = zink_context(pctx);
unsigned i;
bool update = false;
for (i = 0; i < num_views; ++i) {
struct pipe_sampler_view *pview = views ? views[i] : NULL;
struct zink_sampler_view *a = zink_sampler_view(ctx->sampler_views[shader_type][start_slot + i]);
struct zink_sampler_view *b = zink_sampler_view(pview);
if (b && b->base.texture) {
struct zink_resource *res = zink_resource(b->base.texture);
if (!a || zink_resource(a->base.texture) != res) {
if (a)
unbind_samplerview(ctx, shader_type, start_slot + i);
update_res_bind_count(ctx, res, shader_type == PIPE_SHADER_COMPUTE, false);
res->bind_history |= BITFIELD64_BIT(ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW);
res->bind_stages |= 1 << shader_type;
} else if (a != b) {
check_samplerview_for_batch_ref(ctx, a);
}
if (res->base.b.target == PIPE_BUFFER) {
if (res->bind_history & BITFIELD64_BIT(ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW)) {
/* if this resource has been rebound while it wasn't set here,
* its backing resource will have changed and thus we need to update
* the bufferview
*/
struct zink_buffer_view *buffer_view = get_buffer_view(ctx, res, b->base.format, b->base.u.buf.offset, b->base.u.buf.size);
if (buffer_view == b->buffer_view)
p_atomic_dec(&buffer_view->reference.count);
else {
if (zink_batch_usage_exists(b->buffer_view->batch_uses))
zink_batch_reference_bufferview(&ctx->batch, b->buffer_view);
zink_buffer_view_reference(zink_screen(ctx->base.screen), &b->buffer_view, NULL);
b->buffer_view = buffer_view;
update = true;
}
}
zink_batch_usage_set(&b->buffer_view->batch_uses, ctx->batch.state);
zink_fake_buffer_barrier(res, VK_ACCESS_SHADER_READ_BIT,
zink_pipeline_flags_from_pipe_stage(shader_type));
if (!a || a->buffer_view->buffer_view != b->buffer_view->buffer_view)
update = true;
} else if (!res->obj->is_buffer) {
if (res->obj != b->image_view->obj) {
struct pipe_surface *psurf = &b->image_view->base;
VkImageView iv = b->image_view->image_view;
zink_rebind_surface(ctx, &psurf);
b->image_view = zink_surface(psurf);
update |= iv != b->image_view->image_view;
} else if (a != b)
update = true;
flush_pending_clears(ctx, res);
check_for_layout_update(ctx, res, shader_type == PIPE_SHADER_COMPUTE);
zink_batch_usage_set(&b->image_view->batch_uses, ctx->batch.state);
if (!a)
update = true;
}
res->sampler_binds[shader_type] |= BITFIELD_BIT(start_slot + i);
zink_batch_resource_usage_set(&ctx->batch, res, false);
} else if (a) {
unbind_samplerview(ctx, shader_type, start_slot + i);
update = true;
}
if (take_ownership) {
pipe_sampler_view_reference(&ctx->sampler_views[shader_type][start_slot + i], NULL);
ctx->sampler_views[shader_type][start_slot + i] = pview;
} else {
pipe_sampler_view_reference(&ctx->sampler_views[shader_type][start_slot + i], pview);
}
update_descriptor_state_sampler(ctx, shader_type, start_slot + i);
}
for (; i < num_views + unbind_num_trailing_slots; ++i) {
update |= !!ctx->sampler_views[shader_type][start_slot + i];
unbind_samplerview(ctx, shader_type, start_slot + i);
pipe_sampler_view_reference(
&ctx->sampler_views[shader_type][start_slot + i],
NULL);
update_descriptor_state_sampler(ctx, shader_type, start_slot + i);
}
ctx->di.num_sampler_views[shader_type] = start_slot + num_views;
if (update)
zink_screen(pctx->screen)->context_invalidate_descriptor_state(ctx, shader_type, ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW, start_slot, num_views);
}
static void
zink_set_stencil_ref(struct pipe_context *pctx,
const struct pipe_stencil_ref ref)
{
struct zink_context *ctx = zink_context(pctx);
ctx->stencil_ref = ref;
ctx->stencil_ref_changed = true;
}
static void
zink_set_clip_state(struct pipe_context *pctx,
const struct pipe_clip_state *pcs)
{
}
static void
zink_set_tess_state(struct pipe_context *pctx,
const float default_outer_level[4],
const float default_inner_level[2])
{
struct zink_context *ctx = zink_context(pctx);
memcpy(&ctx->default_inner_level, default_inner_level, sizeof(ctx->default_inner_level));
memcpy(&ctx->default_outer_level, default_outer_level, sizeof(ctx->default_outer_level));
}
static void
zink_set_patch_vertices(struct pipe_context *pctx, uint8_t patch_vertices)
{
struct zink_context *ctx = zink_context(pctx);
ctx->gfx_pipeline_state.patch_vertices = patch_vertices;
}
static uint32_t
hash_render_pass_state(const void *key)
{
struct zink_render_pass_state* s = (struct zink_render_pass_state*)key;
return _mesa_hash_data(key, offsetof(struct zink_render_pass_state, rts) + sizeof(s->rts[0]) * s->num_rts);
}
static bool
equals_render_pass_state(const void *a, const void *b)
{
const struct zink_render_pass_state *s_a = a, *s_b = b;
if (s_a->num_rts != s_b->num_rts)
return false;
return memcmp(a, b, offsetof(struct zink_render_pass_state, rts) + sizeof(s_a->rts[0]) * s_a->num_rts) == 0;
}
static struct zink_render_pass *
get_render_pass(struct zink_context *ctx)
{
struct zink_screen *screen = zink_screen(ctx->base.screen);
const struct pipe_framebuffer_state *fb = &ctx->fb_state;
struct zink_render_pass_state state = {0};
uint32_t clears = 0;
state.swapchain_init = ctx->new_swapchain;
for (int i = 0; i < fb->nr_cbufs; i++) {
struct pipe_surface *surf = fb->cbufs[i];
if (surf) {
state.rts[i].format = zink_get_format(screen, surf->format);
state.rts[i].samples = surf->texture->nr_samples > 0 ? surf->texture->nr_samples :
VK_SAMPLE_COUNT_1_BIT;
state.rts[i].clear_color = zink_fb_clear_enabled(ctx, i) && !zink_fb_clear_first_needs_explicit(&ctx->fb_clears[i]);
clears |= !!state.rts[i].clear_color ? PIPE_CLEAR_COLOR0 << i : 0;
state.rts[i].swapchain = surf->texture->bind & PIPE_BIND_SCANOUT;
} else {
state.rts[i].format = VK_FORMAT_R8_UINT;
state.rts[i].samples = MAX2(fb->samples, 1);
}
state.num_rts++;
}
state.num_cbufs = fb->nr_cbufs;
if (fb->zsbuf) {
struct zink_resource *zsbuf = zink_resource(fb->zsbuf->texture);
struct zink_framebuffer_clear *fb_clear = &ctx->fb_clears[PIPE_MAX_COLOR_BUFS];
state.rts[fb->nr_cbufs].format = zsbuf->format;
state.rts[fb->nr_cbufs].samples = zsbuf->base.b.nr_samples > 0 ? zsbuf->base.b.nr_samples : VK_SAMPLE_COUNT_1_BIT;
state.rts[fb->nr_cbufs].clear_color = zink_fb_clear_enabled(ctx, PIPE_MAX_COLOR_BUFS) &&
!zink_fb_clear_first_needs_explicit(fb_clear) &&
(zink_fb_clear_element(fb_clear, 0)->zs.bits & PIPE_CLEAR_DEPTH);
state.rts[fb->nr_cbufs].clear_stencil = zink_fb_clear_enabled(ctx, PIPE_MAX_COLOR_BUFS) &&
!zink_fb_clear_first_needs_explicit(fb_clear) &&
(zink_fb_clear_element(fb_clear, 0)->zs.bits & PIPE_CLEAR_STENCIL);
if (state.rts[fb->nr_cbufs].clear_color)
clears |= PIPE_CLEAR_DEPTH;
if (state.rts[fb->nr_cbufs].clear_stencil)
clears |= PIPE_CLEAR_STENCIL;
const uint64_t outputs_written = ctx->gfx_stages[PIPE_SHADER_FRAGMENT] ?
ctx->gfx_stages[PIPE_SHADER_FRAGMENT]->nir->info.outputs_written : 0;
bool needs_write = (ctx->dsa_state && ctx->dsa_state->hw_state.depth_write) ||
outputs_written & (BITFIELD64_BIT(FRAG_RESULT_DEPTH) | BITFIELD64_BIT(FRAG_RESULT_STENCIL));
state.rts[fb->nr_cbufs].needs_write = needs_write || state.rts[fb->nr_cbufs].clear_color || state.rts[fb->nr_cbufs].clear_stencil;
state.num_rts++;
}
state.have_zsbuf = fb->zsbuf != NULL;
assert(clears == ctx->rp_clears_enabled);
state.clears = clears;
uint32_t hash = hash_render_pass_state(&state);
struct hash_entry *entry = _mesa_hash_table_search_pre_hashed(ctx->render_pass_cache, hash,
&state);
struct zink_render_pass *rp;
if (entry) {
rp = entry->data;
assert(rp->state.clears == clears);
} else {
rp = zink_create_render_pass(screen, &state);
if (!_mesa_hash_table_insert_pre_hashed(ctx->render_pass_cache, hash, &rp->state, rp))
return NULL;
}
return rp;
}
static struct zink_framebuffer *
get_framebuffer(struct zink_context *ctx)
{
struct zink_screen *screen = zink_screen(ctx->base.screen);
struct pipe_surface *attachments[PIPE_MAX_COLOR_BUFS + 1] = {0};
struct zink_framebuffer_state state = {0};
for (int i = 0; i < ctx->fb_state.nr_cbufs; i++) {
struct pipe_surface *psurf = ctx->fb_state.cbufs[i];
state.attachments[i] = psurf ? zink_surface(psurf)->image_view : VK_NULL_HANDLE;
attachments[i] = psurf;
}
state.num_attachments = ctx->fb_state.nr_cbufs;
if (ctx->fb_state.zsbuf) {
struct pipe_surface *psurf = ctx->fb_state.zsbuf;
state.attachments[state.num_attachments] = psurf ? zink_surface(psurf)->image_view : VK_NULL_HANDLE;
attachments[state.num_attachments++] = psurf;
}
state.width = MAX2(ctx->fb_state.width, 1);
state.height = MAX2(ctx->fb_state.height, 1);
state.layers = MAX2(util_framebuffer_get_num_layers(&ctx->fb_state), 1);
state.samples = ctx->fb_state.samples;
struct zink_framebuffer *fb;
simple_mtx_lock(&screen->framebuffer_mtx);
struct hash_entry *entry = _mesa_hash_table_search(&screen->framebuffer_cache, &state);
if (entry) {
fb = (void*)entry->data;
struct zink_framebuffer *fb_ref = NULL;
/* this gains 1 ref every time we reuse it */
zink_framebuffer_reference(screen, &fb_ref, fb);
} else {
/* this adds 1 extra ref on creation because all newly-created framebuffers are
* going to be bound; necessary to handle framebuffers which have no "real" attachments
* and are only using null surfaces since the only ref they get is the extra one here
*/
fb = zink_create_framebuffer(ctx, &state, attachments);
_mesa_hash_table_insert(&screen->framebuffer_cache, &fb->state, fb);
}
simple_mtx_unlock(&screen->framebuffer_mtx);
return fb;
}
static void
setup_framebuffer(struct zink_context *ctx)
{
struct zink_screen *screen = zink_screen(ctx->base.screen);
struct zink_render_pass *rp = ctx->gfx_pipeline_state.render_pass;
if (ctx->gfx_pipeline_state.sample_locations_enabled && ctx->sample_locations_changed) {
unsigned samples = ctx->gfx_pipeline_state.rast_samples;
unsigned idx = util_logbase2_ceil(MAX2(samples, 1));
VkExtent2D grid_size = screen->maxSampleLocationGridSize[idx];
for (unsigned pixel = 0; pixel < grid_size.width * grid_size.height; pixel++) {
for (unsigned sample = 0; sample < samples; sample++) {
unsigned pixel_x = pixel % grid_size.width;
unsigned pixel_y = pixel / grid_size.width;
unsigned wi = pixel * samples + sample;
unsigned ri = (pixel_y * grid_size.width + pixel_x % grid_size.width);
ri = ri * samples + sample;
ctx->vk_sample_locations[wi].x = (ctx->sample_locations[ri] & 0xf) / 16.0f;
ctx->vk_sample_locations[wi].y = (16 - (ctx->sample_locations[ri] >> 4)) / 16.0f;
}
}
}
if (rp)
ctx->rp_changed |= ctx->rp_clears_enabled != rp->state.clears;
if (ctx->rp_changed)
rp = get_render_pass(ctx);
if (rp != ctx->gfx_pipeline_state.render_pass)
ctx->gfx_pipeline_state.dirty =
ctx->fb_changed = true;
ctx->rp_changed = false;
if (!ctx->fb_changed)
return;
zink_init_framebuffer(screen, ctx->framebuffer, rp);
ctx->fb_changed = false;
ctx->gfx_pipeline_state.render_pass = rp;
}
static unsigned
begin_render_pass(struct zink_context *ctx)
{
struct zink_batch *batch = &ctx->batch;
struct pipe_framebuffer_state *fb_state = &ctx->fb_state;
VkRenderPassBeginInfo rpbi = {0};
rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rpbi.renderPass = ctx->gfx_pipeline_state.render_pass->render_pass;
rpbi.renderArea.offset.x = 0;
rpbi.renderArea.offset.y = 0;
rpbi.renderArea.extent.width = fb_state->width;
rpbi.renderArea.extent.height = fb_state->height;
VkClearValue clears[PIPE_MAX_COLOR_BUFS + 1] = {0};
unsigned clear_buffers = 0;
uint32_t clear_validate = 0;
for (int i = 0; i < fb_state->nr_cbufs; i++) {
/* these are no-ops */
if (!fb_state->cbufs[i] || !zink_fb_clear_enabled(ctx, i))
continue;
/* these need actual clear calls inside the rp */
struct zink_framebuffer_clear_data *clear = zink_fb_clear_element(&ctx->fb_clears[i], 0);
if (zink_fb_clear_needs_explicit(&ctx->fb_clears[i])) {
clear_buffers |= (PIPE_CLEAR_COLOR0 << i);
if (zink_fb_clear_count(&ctx->fb_clears[i]) < 2 ||
zink_fb_clear_element_needs_explicit(clear))
continue;
}
/* we now know there's one clear that can be done here */
zink_fb_clear_util_unpack_clear_color(clear, fb_state->cbufs[i]->format, (void*)&clears[i].color);
rpbi.clearValueCount = i + 1;
clear_validate |= PIPE_CLEAR_COLOR0 << i;
assert(ctx->framebuffer->rp->state.clears);
}
if (fb_state->zsbuf && zink_fb_clear_enabled(ctx, PIPE_MAX_COLOR_BUFS)) {
struct zink_framebuffer_clear *fb_clear = &ctx->fb_clears[PIPE_MAX_COLOR_BUFS];
struct zink_framebuffer_clear_data *clear = zink_fb_clear_element(fb_clear, 0);
if (!zink_fb_clear_element_needs_explicit(clear)) {
clears[fb_state->nr_cbufs].depthStencil.depth = clear->zs.depth;
clears[fb_state->nr_cbufs].depthStencil.stencil = clear->zs.stencil;
rpbi.clearValueCount = fb_state->nr_cbufs + 1;
clear_validate |= clear->zs.bits;
assert(ctx->framebuffer->rp->state.clears);
}
if (zink_fb_clear_needs_explicit(fb_clear)) {
for (int j = !zink_fb_clear_element_needs_explicit(clear);
(clear_buffers & PIPE_CLEAR_DEPTHSTENCIL) != PIPE_CLEAR_DEPTHSTENCIL && j < zink_fb_clear_count(fb_clear);
j++)
clear_buffers |= zink_fb_clear_element(fb_clear, j)->zs.bits;
}
}
assert(clear_validate == ctx->framebuffer->rp->state.clears);
rpbi.pClearValues = &clears[0];
rpbi.framebuffer = ctx->framebuffer->fb;
assert(ctx->gfx_pipeline_state.render_pass && ctx->framebuffer);
zink_batch_reference_framebuffer(batch, ctx->framebuffer);
for (int i = 0; i < ctx->framebuffer->state.num_attachments; i++) {
if (ctx->framebuffer->surfaces[i]) {
struct zink_surface *surf = zink_surface(ctx->framebuffer->surfaces[i]);
zink_batch_resource_usage_set(batch, zink_resource(surf->base.texture), true);
zink_batch_usage_set(&surf->batch_uses, batch->state);
struct zink_resource *res = zink_resource(surf->base.texture);
VkAccessFlags access;
VkPipelineStageFlags pipeline;
VkImageLayout layout = zink_render_pass_attachment_get_barrier_info(ctx->gfx_pipeline_state.render_pass,
i, &pipeline, &access);
zink_resource_image_barrier(ctx, NULL, res, layout, access, pipeline);
}
}
vkCmdBeginRenderPass(batch->state->cmdbuf, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
batch->in_rp = true;
ctx->new_swapchain = false;
return clear_buffers;
}
void
zink_init_vk_sample_locations(struct zink_context *ctx, VkSampleLocationsInfoEXT *loc)
{
struct zink_screen *screen = zink_screen(ctx->base.screen);
unsigned idx = util_logbase2_ceil(MAX2(ctx->gfx_pipeline_state.rast_samples, 1));
loc->sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT;
loc->pNext = NULL;
loc->sampleLocationsPerPixel = 1 << idx;
loc->sampleLocationsCount = ctx->gfx_pipeline_state.rast_samples;
loc->sampleLocationGridSize = screen->maxSampleLocationGridSize[idx];
loc->pSampleLocations = ctx->vk_sample_locations;
}
static void
zink_evaluate_depth_buffer(struct pipe_context *pctx)
{
struct zink_context *ctx = zink_context(pctx);
if (!ctx->fb_state.zsbuf)
return;
struct zink_resource *res = zink_resource(ctx->fb_state.zsbuf->texture);
res->obj->needs_zs_evaluate = true;
zink_init_vk_sample_locations(ctx, &res->obj->zs_evaluate);
zink_batch_no_rp(ctx);
}
void
zink_begin_render_pass(struct zink_context *ctx, struct zink_batch *batch)
{
setup_framebuffer(ctx);
assert(ctx->gfx_pipeline_state.render_pass);
unsigned clear_buffers = begin_render_pass(ctx);
if (ctx->render_condition.query)
zink_start_conditional_render(ctx);
zink_clear_framebuffer(ctx, clear_buffers);
}
void
zink_end_render_pass(struct zink_context *ctx, struct zink_batch *batch)
{
if (batch->in_rp) {
if (ctx->render_condition.query)
zink_stop_conditional_render(ctx);
vkCmdEndRenderPass(batch->state->cmdbuf);
}
batch->in_rp = false;
}
static void
sync_flush(struct zink_context *ctx, struct zink_batch_state *bs)
{
if (zink_screen(ctx->base.screen)->threaded)
util_queue_fence_wait(&bs->flush_completed);
}
static inline VkAccessFlags
get_access_flags_for_binding(struct zink_context *ctx, enum zink_descriptor_type type, enum pipe_shader_type stage, unsigned idx)
{
VkAccessFlags flags = 0;
switch (type) {
case ZINK_DESCRIPTOR_TYPE_UBO:
return VK_ACCESS_UNIFORM_READ_BIT;
case ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW:
return VK_ACCESS_SHADER_READ_BIT;
case ZINK_DESCRIPTOR_TYPE_SSBO: {
flags = VK_ACCESS_SHADER_READ_BIT;
if (ctx->writable_ssbos[stage] & (1 << idx))
flags |= VK_ACCESS_SHADER_WRITE_BIT;
return flags;
}
case ZINK_DESCRIPTOR_TYPE_IMAGE: {
struct zink_image_view *image_view = &ctx->image_views[stage][idx];
if (image_view->base.access & PIPE_IMAGE_ACCESS_READ)
flags |= VK_ACCESS_SHADER_READ_BIT;
if (image_view->base.access & PIPE_IMAGE_ACCESS_WRITE)
flags |= VK_ACCESS_SHADER_WRITE_BIT;
return flags;
}
default:
break;
}
unreachable("ACK");
return 0;
}
static void
update_resource_refs_for_stage(struct zink_context *ctx, enum pipe_shader_type stage)
{
struct zink_batch *batch = &ctx->batch;
unsigned max_slot[] = {
[ZINK_DESCRIPTOR_TYPE_UBO] = ctx->di.num_ubos[stage],
[ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW] = ctx->di.num_samplers[stage],
[ZINK_DESCRIPTOR_TYPE_SSBO] = ctx->di.num_ssbos[stage],
[ZINK_DESCRIPTOR_TYPE_IMAGE] = ctx->di.num_images[stage]
};
for (unsigned i = 0; i < ZINK_DESCRIPTOR_TYPES; i++) {
for (unsigned j = 0; j < max_slot[i]; j++) {
if (ctx->di.descriptor_res[i][stage][j]) {
struct zink_resource *res = ctx->di.descriptor_res[i][stage][j];
if (!res)
continue;
bool is_write = zink_resource_access_is_write(get_access_flags_for_binding(ctx, i, stage, j));
zink_batch_resource_usage_set(batch, res, is_write);
struct zink_sampler_view *sv = zink_sampler_view(ctx->sampler_views[stage][j]);
struct zink_sampler_state *sampler_state = ctx->sampler_states[stage][j];
struct zink_image_view *iv = &ctx->image_views[stage][j];
if (sampler_state && i == ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW && j <= ctx->di.num_samplers[stage])
zink_batch_usage_set(&sampler_state->batch_uses, ctx->batch.state);
if (sv && i == ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW && j <= ctx->di.num_sampler_views[stage]) {
if (res->obj->is_buffer)
zink_batch_usage_set(&sv->buffer_view->batch_uses, ctx->batch.state);
else
zink_batch_usage_set(&sv->image_view->batch_uses, ctx->batch.state);
zink_batch_reference_sampler_view(batch, sv);
} else if (i == ZINK_DESCRIPTOR_TYPE_IMAGE && j <= ctx->di.num_images[stage]) {
if (res->obj->is_buffer)
zink_batch_usage_set(&iv->buffer_view->batch_uses, ctx->batch.state);
else
zink_batch_usage_set(&iv->surface->batch_uses, ctx->batch.state);
zink_batch_reference_image_view(batch, iv);
}
}
}
}
}
void
zink_update_descriptor_refs(struct zink_context *ctx, bool compute)
{
struct zink_batch *batch = &ctx->batch;
if (compute) {
update_resource_refs_for_stage(ctx, PIPE_SHADER_COMPUTE);
if (ctx->curr_compute)
zink_batch_reference_program(batch, &ctx->curr_compute->base);
} else {
for (unsigned i = 0; i < ZINK_SHADER_COUNT; i++)
update_resource_refs_for_stage(ctx, i);
unsigned vertex_buffers_enabled_mask = ctx->gfx_pipeline_state.vertex_buffers_enabled_mask;
unsigned last_vbo = util_last_bit(vertex_buffers_enabled_mask);
for (unsigned i = 0; i < last_vbo + 1; i++) {
if (ctx->vertex_buffers[i].buffer.resource)
zink_batch_resource_usage_set(batch, zink_resource(ctx->vertex_buffers[i].buffer.resource), false);
}
if (ctx->curr_program)
zink_batch_reference_program(batch, &ctx->curr_program->base);
}
}
static void
flush_batch(struct zink_context *ctx, bool sync)
{
struct zink_batch *batch = &ctx->batch;
if (ctx->clears_enabled)
/* start rp to do all the clears */
zink_begin_render_pass(ctx, batch);
zink_end_render_pass(ctx, batch);
zink_end_batch(ctx, batch);
ctx->deferred_fence = NULL;
if (sync)
sync_flush(ctx, ctx->batch.state);
if (ctx->batch.state->is_device_lost) {
check_device_lost(ctx);
} else {
zink_start_batch(ctx, batch);
if (zink_screen(ctx->base.screen)->info.have_EXT_transform_feedback && ctx->num_so_targets)
ctx->dirty_so_targets = true;
ctx->pipeline_changed[0] = ctx->pipeline_changed[1] = true;
zink_select_draw_vbo(ctx);
zink_select_launch_grid(ctx);
if (ctx->oom_stall)
zink_fence_wait(&ctx->base);
ctx->oom_flush = false;
ctx->oom_stall = false;
}
}
void
zink_flush_queue(struct zink_context *ctx)
{
flush_batch(ctx, true);
}
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 from_set_fb)
{
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);
if (from_set_fb && ctx->fb_state.cbufs[i] && ctx->fb_state.cbufs[i]->texture->bind & PIPE_BIND_SCANOUT)
ctx->new_swapchain = true;
}
rebind |= rebind_fb_surface(ctx, &ctx->fb_state.zsbuf, match_res);
return rebind;
}
static void
unbind_fb_surface(struct zink_context *ctx, struct pipe_surface *surf, bool changed)
{
if (!surf)
return;
if (changed) {
zink_fb_clears_apply(ctx, surf->texture);
if (zink_batch_usage_exists(zink_surface(surf)->batch_uses))
zink_batch_reference_surface(&ctx->batch, zink_surface(surf));
ctx->rp_changed = true;
}
struct zink_resource *res = zink_resource(surf->texture);
res->fb_binds--;
if (!res->fb_binds)
check_resource_for_batch_ref(ctx, res);
}
static void
zink_set_framebuffer_state(struct pipe_context *pctx,
const struct pipe_framebuffer_state *state)
{
struct zink_context *ctx = zink_context(pctx);
for (int i = 0; i < ctx->fb_state.nr_cbufs; i++) {
struct pipe_surface *surf = ctx->fb_state.cbufs[i];
unbind_fb_surface(ctx, surf, i >= state->nr_cbufs || surf != state->cbufs[i]);
}
if (ctx->fb_state.zsbuf) {
struct pipe_surface *surf = ctx->fb_state.zsbuf;
struct zink_resource *res = zink_resource(surf->texture);
bool changed = surf != state->zsbuf;
unbind_fb_surface(ctx, surf, changed);
if (changed && unlikely(res->obj->needs_zs_evaluate))
/* have to flush zs eval while the sample location data still exists,
* so just throw some random barrier */
zink_resource_image_barrier(ctx, NULL, res, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
/* renderpass changes if the number or types of attachments change */
ctx->rp_changed |= ctx->fb_state.nr_cbufs != state->nr_cbufs;
ctx->rp_changed |= !!ctx->fb_state.zsbuf != !!state->zsbuf;
unsigned w = ctx->fb_state.width;
unsigned h = ctx->fb_state.height;
util_copy_framebuffer_state(&ctx->fb_state, state);
unsigned prev_void_alpha_attachments = ctx->gfx_pipeline_state.void_alpha_attachments;
ctx->gfx_pipeline_state.void_alpha_attachments = 0;
for (int i = 0; i < ctx->fb_state.nr_cbufs; i++) {
struct pipe_surface *surf = ctx->fb_state.cbufs[i];
if (surf) {
zink_resource(surf->texture)->fb_binds++;
ctx->gfx_pipeline_state.void_alpha_attachments |= util_format_has_alpha1(surf->format) ? BITFIELD_BIT(i) : 0;
}
}
if (ctx->gfx_pipeline_state.void_alpha_attachments != prev_void_alpha_attachments)
ctx->gfx_pipeline_state.dirty = true;
if (ctx->fb_state.zsbuf) {
struct pipe_surface *surf = ctx->fb_state.zsbuf;
zink_resource(surf->texture)->fb_binds++;
}
if (ctx->fb_state.width != w || ctx->fb_state.height != h)
ctx->scissor_changed = true;
rebind_fb_state(ctx, NULL, true);
/* 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
*/
struct zink_framebuffer *fb = get_framebuffer(ctx);
if (ctx->framebuffer) {
struct zink_screen *screen = zink_screen(pctx->screen);
simple_mtx_lock(&screen->framebuffer_mtx);
struct hash_entry *he = _mesa_hash_table_search(&screen->framebuffer_cache, &ctx->framebuffer->state);
if (ctx->framebuffer && !ctx->framebuffer->state.num_attachments) {
/* if this has no attachments then its lifetime has ended */
_mesa_hash_table_remove(&screen->framebuffer_cache, he);
he = NULL;
}
/* a framebuffer loses 1 ref every time we unset it;
* we do NOT add refs here, as the ref has already been added in
* get_framebuffer()
*/
if (zink_framebuffer_reference(screen, &ctx->framebuffer, NULL) && he)
_mesa_hash_table_remove(&screen->framebuffer_cache, he);
simple_mtx_unlock(&screen->framebuffer_mtx);
}
ctx->fb_changed |= ctx->framebuffer != fb;
ctx->framebuffer = fb;
uint8_t rast_samples = util_framebuffer_get_num_samples(state);
/* in vulkan, gl_SampleMask needs to be explicitly ignored for sampleCount == 1 */
if ((ctx->gfx_pipeline_state.rast_samples > 1) != (rast_samples > 1))
ctx->dirty_shader_stages |= 1 << PIPE_SHADER_FRAGMENT;
if (ctx->gfx_pipeline_state.rast_samples != rast_samples) {
ctx->sample_locations_changed |= ctx->gfx_pipeline_state.sample_locations_enabled;
ctx->gfx_pipeline_state.dirty = true;
}
ctx->gfx_pipeline_state.rast_samples = rast_samples;
if (ctx->gfx_pipeline_state.num_attachments != state->nr_cbufs)
ctx->gfx_pipeline_state.dirty = true;
ctx->gfx_pipeline_state.num_attachments = state->nr_cbufs;
/* need to ensure we start a new rp on next draw */
zink_batch_no_rp(ctx);
/* this is an ideal time to oom flush since it won't split a renderpass */
if (ctx->oom_flush)
flush_batch(ctx, false);
}
static void
zink_set_blend_color(struct pipe_context *pctx,
const struct pipe_blend_color *color)
{
struct zink_context *ctx = zink_context(pctx);
memcpy(ctx->blend_constants, color->color, sizeof(float) * 4);
}
static void
zink_set_sample_mask(struct pipe_context *pctx, unsigned sample_mask)
{
struct zink_context *ctx = zink_context(pctx);
ctx->gfx_pipeline_state.sample_mask = sample_mask;
ctx->gfx_pipeline_state.dirty = true;
}
static void
zink_set_sample_locations(struct pipe_context *pctx, size_t size, const uint8_t *locations)
{
struct zink_context *ctx = zink_context(pctx);
ctx->gfx_pipeline_state.sample_locations_enabled = size && locations;
ctx->sample_locations_changed = ctx->gfx_pipeline_state.sample_locations_enabled;
if (size > sizeof(ctx->sample_locations))
size = sizeof(ctx->sample_locations);
memcpy(ctx->sample_locations, locations, size);
}
static VkAccessFlags
access_src_flags(VkImageLayout layout)
{
switch (layout) {
case VK_IMAGE_LAYOUT_UNDEFINED:
return 0;
case VK_IMAGE_LAYOUT_GENERAL:
return VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
return VK_ACCESS_SHADER_READ_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
return VK_ACCESS_TRANSFER_READ_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
return VK_ACCESS_TRANSFER_WRITE_BIT;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
return VK_ACCESS_HOST_WRITE_BIT;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
return 0;
default:
unreachable("unexpected layout");
}
}
static VkAccessFlags
access_dst_flags(VkImageLayout layout)
{
switch (layout) {
case VK_IMAGE_LAYOUT_UNDEFINED:
return 0;
case VK_IMAGE_LAYOUT_GENERAL:
return VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
return VK_ACCESS_SHADER_READ_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
return VK_ACCESS_TRANSFER_READ_BIT;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
return VK_ACCESS_SHADER_READ_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
return VK_ACCESS_TRANSFER_WRITE_BIT;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
return 0;
default:
unreachable("unexpected layout");
}
}
static VkPipelineStageFlags
pipeline_dst_stage(VkImageLayout layout)
{
switch (layout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
return VK_PIPELINE_STAGE_TRANSFER_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
return VK_PIPELINE_STAGE_TRANSFER_BIT;
case VK_IMAGE_LAYOUT_GENERAL:
return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
default:
return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
}
}
#define ALL_READ_ACCESS_FLAGS \
(VK_ACCESS_INDIRECT_COMMAND_READ_BIT | \
VK_ACCESS_INDEX_READ_BIT | \
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | \
VK_ACCESS_UNIFORM_READ_BIT | \
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | \
VK_ACCESS_SHADER_READ_BIT | \
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | \
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | \
VK_ACCESS_TRANSFER_READ_BIT |\
VK_ACCESS_HOST_READ_BIT |\
VK_ACCESS_MEMORY_READ_BIT |\
VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT |\
VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT |\
VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT |\
VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR |\
VK_ACCESS_SHADING_RATE_IMAGE_READ_BIT_NV |\
VK_ACCESS_FRAGMENT_DENSITY_MAP_READ_BIT_EXT |\
VK_ACCESS_COMMAND_PREPROCESS_READ_BIT_NV |\
VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV |\
VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV)
bool
zink_resource_access_is_write(VkAccessFlags flags)
{
return (flags & ALL_READ_ACCESS_FLAGS) != flags;
}
bool
zink_resource_image_needs_barrier(struct zink_resource *res, VkImageLayout new_layout, VkAccessFlags flags, VkPipelineStageFlags pipeline)
{
if (!pipeline)
pipeline = pipeline_dst_stage(new_layout);
if (!flags)
flags = access_dst_flags(new_layout);
return res->layout != new_layout || (res->access_stage & pipeline) != pipeline ||
(res->access & flags) != flags ||
zink_resource_access_is_write(res->access) ||
zink_resource_access_is_write(flags);
}
bool
zink_resource_image_barrier_init(VkImageMemoryBarrier *imb, struct zink_resource *res, VkImageLayout new_layout, VkAccessFlags flags, VkPipelineStageFlags pipeline)
{
if (!pipeline)
pipeline = pipeline_dst_stage(new_layout);
if (!flags)
flags = access_dst_flags(new_layout);
VkImageSubresourceRange isr = {
res->aspect,
0, VK_REMAINING_MIP_LEVELS,
0, VK_REMAINING_ARRAY_LAYERS
};
*imb = (VkImageMemoryBarrier){
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
res->access ? res->access : access_src_flags(res->layout),
flags,
res->layout,
new_layout,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
res->obj->image,
isr
};
return res->obj->needs_zs_evaluate || zink_resource_image_needs_barrier(res, new_layout, flags, pipeline);
}
static inline bool
is_shader_pipline_stage(VkPipelineStageFlags pipeline)
{
return pipeline & (VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT |
VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT |
VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
static void
resource_check_defer_buffer_barrier(struct zink_context *ctx, struct zink_resource *res, VkPipelineStageFlags pipeline)
{
assert(res->obj->is_buffer);
if (res->bind_count[0]) {
if ((res->obj->is_buffer && res->vbo_bind_mask && !(pipeline & VK_PIPELINE_STAGE_VERTEX_INPUT_BIT)) ||
((!res->obj->is_buffer || util_bitcount(res->vbo_bind_mask) != res->bind_count[0]) && !is_shader_pipline_stage(pipeline)))
/* gfx rebind */
_mesa_set_add(ctx->need_barriers[0], res);
}
if (res->bind_count[1] && !(pipeline & VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT))
/* compute rebind */
_mesa_set_add(ctx->need_barriers[1], res);
}
static inline VkCommandBuffer
get_cmdbuf(struct zink_context *ctx, struct zink_resource *res)
{
if ((res->access && !res->unordered_barrier) || !ctx->batch.in_rp) {
struct zink_batch *batch = zink_batch_no_rp(ctx);
assert(!batch->in_rp);
res->unordered_barrier = false;
return batch->state->cmdbuf;
}
res->unordered_barrier = true;
ctx->batch.state->has_barriers = true;
return ctx->batch.state->barrier_cmdbuf;
}
static void
resource_check_defer_image_barrier(struct zink_context *ctx, struct zink_resource *res, VkImageLayout layout, VkPipelineStageFlags pipeline)
{
assert(!res->obj->is_buffer);
bool is_compute = pipeline == VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
/* if this is a non-shader barrier and there are binds, always queue a shader barrier */
bool is_shader = is_shader_pipline_stage(pipeline);
if ((is_shader || !res->bind_count[is_compute]) &&
/* if no layout change is needed between gfx and compute, do nothing */
!res->bind_count[!is_compute] && (!is_compute || !res->fb_binds))
return;
if (res->bind_count[!is_compute] && is_shader) {
/* if the layout is the same between gfx and compute, do nothing */
if (layout == zink_descriptor_util_image_layout_eval(res, !is_compute))
return;
}
/* queue a layout change if a layout change will be needed */
if (res->bind_count[!is_compute])
_mesa_set_add(ctx->need_barriers[!is_compute], res);
/* also queue a layout change if this is a non-shader layout */
if (res->bind_count[is_compute] && !is_shader)
_mesa_set_add(ctx->need_barriers[is_compute], res);
}
void
zink_resource_image_barrier(struct zink_context *ctx, struct zink_batch *batch, struct zink_resource *res,
VkImageLayout new_layout, VkAccessFlags flags, VkPipelineStageFlags pipeline)
{
VkImageMemoryBarrier imb;
if (!pipeline)
pipeline = pipeline_dst_stage(new_layout);
if (!zink_resource_image_barrier_init(&imb, res, new_layout, flags, pipeline))
return;
/* only barrier if we're changing layout or doing something besides read -> read */
VkCommandBuffer cmdbuf = get_cmdbuf(ctx, res);
assert(new_layout);
if (res->obj->needs_zs_evaluate)
imb.pNext = &res->obj->zs_evaluate;
res->obj->needs_zs_evaluate = false;
vkCmdPipelineBarrier(
cmdbuf,
res->access_stage ? res->access_stage : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
pipeline,
0,
0, NULL,
0, NULL,
1, &imb
);
resource_check_defer_image_barrier(ctx, res, new_layout, pipeline);
if (res->unordered_barrier) {
res->access |= imb.dstAccessMask;
res->access_stage |= pipeline;
} else {
res->access = imb.dstAccessMask;
res->access_stage = pipeline;
}
res->layout = new_layout;
}
VkPipelineStageFlags
zink_pipeline_flags_from_stage(VkShaderStageFlagBits stage)
{
switch (stage) {
case VK_SHADER_STAGE_VERTEX_BIT:
return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
case VK_SHADER_STAGE_FRAGMENT_BIT:
return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
case VK_SHADER_STAGE_GEOMETRY_BIT:
return VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
return VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
return VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
case VK_SHADER_STAGE_COMPUTE_BIT:
return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
default:
unreachable("unknown shader stage bit");
}
}
ALWAYS_INLINE static VkPipelineStageFlags
pipeline_access_stage(VkAccessFlags flags)
{
if (flags & (VK_ACCESS_UNIFORM_READ_BIT |
VK_ACCESS_SHADER_READ_BIT |
VK_ACCESS_SHADER_WRITE_BIT))
return VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV |
VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV |
VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR |
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT |
VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT |
VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
return VK_PIPELINE_STAGE_TRANSFER_BIT;
}
ALWAYS_INLINE static bool
zink_resource_buffer_needs_barrier(struct zink_resource *res, VkAccessFlags flags, VkPipelineStageFlags pipeline)
{
if (!res->access || !res->access_stage)
return true;
if (!pipeline)
pipeline = pipeline_access_stage(flags);
return zink_resource_access_is_write(res->access) ||
zink_resource_access_is_write(flags) ||
((res->access_stage & pipeline) != pipeline && !(res->access_stage & (pipeline - 1))) ||
(res->access & flags) != flags;
}
void
zink_fake_buffer_barrier(struct zink_resource *res, VkAccessFlags flags, VkPipelineStageFlags pipeline)
{
res->access = flags;
res->access_stage = pipeline;
}
void
zink_resource_buffer_barrier(struct zink_context *ctx, struct zink_batch *batch, struct zink_resource *res, VkAccessFlags flags, VkPipelineStageFlags pipeline)
{
VkMemoryBarrier bmb;
if (!pipeline)
pipeline = pipeline_access_stage(flags);
if (!zink_resource_buffer_needs_barrier(res, flags, pipeline))
return;
bmb.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
bmb.pNext = NULL;
bmb.srcAccessMask = res->access;
bmb.dstAccessMask = flags;
VkCommandBuffer cmdbuf = get_cmdbuf(ctx, res);
/* only barrier if we're changing layout or doing something besides read -> read */
vkCmdPipelineBarrier(
cmdbuf,
res->access_stage ? res->access_stage : pipeline_access_stage(res->access),
pipeline,
0,
1, &bmb,
0, NULL,
0, NULL
);
resource_check_defer_buffer_barrier(ctx, res, pipeline);
if (res->unordered_barrier) {
res->access |= bmb.dstAccessMask;
res->access_stage |= pipeline;
} else {
res->access = bmb.dstAccessMask;
res->access_stage = pipeline;
}
}
bool
zink_resource_needs_barrier(struct zink_resource *res, VkImageLayout layout, VkAccessFlags flags, VkPipelineStageFlags pipeline)
{
if (res->base.b.target == PIPE_BUFFER)
return zink_resource_buffer_needs_barrier(res, flags, pipeline);
return zink_resource_image_needs_barrier(res, layout, flags, pipeline);
}
void
zink_resource_barrier(struct zink_context *ctx, struct zink_batch *batch, struct zink_resource *res, VkImageLayout layout, VkAccessFlags flags, VkPipelineStageFlags pipeline)
{
if (res->base.b.target == PIPE_BUFFER)
zink_resource_buffer_barrier(ctx, batch, res, flags, pipeline);
else
zink_resource_image_barrier(ctx, batch, res, layout, flags, pipeline);
}
VkShaderStageFlagBits
zink_shader_stage(enum pipe_shader_type type)
{
VkShaderStageFlagBits stages[] = {
[PIPE_SHADER_VERTEX] = VK_SHADER_STAGE_VERTEX_BIT,
[PIPE_SHADER_FRAGMENT] = VK_SHADER_STAGE_FRAGMENT_BIT,
[PIPE_SHADER_GEOMETRY] = VK_SHADER_STAGE_GEOMETRY_BIT,
[PIPE_SHADER_TESS_CTRL] = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
[PIPE_SHADER_TESS_EVAL] = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
[PIPE_SHADER_COMPUTE] = VK_SHADER_STAGE_COMPUTE_BIT,
};
return stages[type];
}
static uint32_t
hash_gfx_program(const void *key)
{
return _mesa_hash_data(key, sizeof(struct zink_shader *) * (ZINK_SHADER_COUNT));
}
static bool
equals_gfx_program(const void *a, const void *b)
{
return memcmp(a, b, sizeof(struct zink_shader *) * (ZINK_SHADER_COUNT)) == 0;
}
static void
zink_flush(struct pipe_context *pctx,
struct pipe_fence_handle **pfence,
unsigned flags)
{
struct zink_context *ctx = zink_context(pctx);
bool deferred = flags & PIPE_FLUSH_DEFERRED;
bool deferred_fence = false;
struct zink_batch *batch = &ctx->batch;
struct zink_fence *fence = NULL;
struct zink_screen *screen = zink_screen(ctx->base.screen);
unsigned submit_count = 0;
/* triggering clears will force has_work */
if (!deferred && ctx->clears_enabled)
/* start rp to do all the clears */
zink_begin_render_pass(ctx, batch);
if (!batch->has_work) {
if (pfence) {
/* reuse last fence */
fence = ctx->last_fence;
}
if (!deferred) {
struct zink_batch_state *last = zink_batch_state(ctx->last_fence);
if (last) {
sync_flush(ctx, last);
if (last->is_device_lost)
check_device_lost(ctx);
}
}
tc_driver_internal_flush_notify(ctx->tc);
} else {
fence = &batch->state->fence;
submit_count = batch->state->submit_count;
if (deferred && !(flags & PIPE_FLUSH_FENCE_FD) && pfence)
deferred_fence = true;
else
flush_batch(ctx, true);
}
if (pfence) {
struct zink_tc_fence *mfence;
if (flags & TC_FLUSH_ASYNC) {
mfence = zink_tc_fence(*pfence);
assert(mfence);
} else {
mfence = zink_create_tc_fence();
screen->base.fence_reference(&screen->base, pfence, NULL);
*pfence = (struct pipe_fence_handle *)mfence;
}
struct zink_batch_state *bs = zink_batch_state(fence);
zink_batch_state_reference(screen, NULL, bs);
mfence->fence = fence;
if (fence)
mfence->submit_count = submit_count;
if (deferred_fence) {
assert(fence);
mfence->deferred_ctx = pctx;
assert(!ctx->deferred_fence || ctx->deferred_fence == fence);
ctx->deferred_fence = fence;
}
if (!fence || flags & TC_FLUSH_ASYNC) {
if (!util_queue_fence_is_signalled(&mfence->ready))
util_queue_fence_signal(&mfence->ready);
}
}
if (fence) {
if (!(flags & (PIPE_FLUSH_DEFERRED | PIPE_FLUSH_ASYNC)))
sync_flush(ctx, zink_batch_state(fence));
if (flags & PIPE_FLUSH_END_OF_FRAME && !(flags & TC_FLUSH_ASYNC) && !deferred) {
/* if the first frame has not yet occurred, we need an explicit fence here
* in some cases in order to correctly draw the first frame, though it's
* unknown at this time why this is the case
*/
if (!ctx->first_frame_done)
zink_vkfence_wait(screen, fence, PIPE_TIMEOUT_INFINITE);
ctx->first_frame_done = true;
}
}
}
void
zink_fence_wait(struct pipe_context *pctx)
{
struct zink_context *ctx = zink_context(pctx);
if (ctx->batch.has_work)
pctx->flush(pctx, NULL, PIPE_FLUSH_HINT_FINISH);
if (ctx->last_fence) {
sync_flush(ctx, zink_batch_state(ctx->last_fence));
zink_vkfence_wait(zink_screen(ctx->base.screen), ctx->last_fence, PIPE_TIMEOUT_INFINITE);
zink_batch_reset_all(ctx);
}
}
void
zink_wait_on_batch(struct zink_context *ctx, uint32_t batch_id)
{
struct zink_batch_state *bs = ctx->batch.state;
assert(bs);
if (!batch_id || bs->fence.batch_id == batch_id)
/* not submitted yet */
flush_batch(ctx, true);
if (ctx->have_timelines) {
if (!zink_screen_timeline_wait(zink_screen(ctx->base.screen), batch_id, UINT64_MAX))
check_device_lost(ctx);
return;
}
simple_mtx_lock(&ctx->batch_mtx);
struct zink_fence *fence;
assert(batch_id || ctx->last_fence);
if (ctx->last_fence && (!batch_id || batch_id == zink_batch_state(ctx->last_fence)->fence.batch_id))
fence = ctx->last_fence;
else {
struct hash_entry *he = _mesa_hash_table_search_pre_hashed(&ctx->batch_states, batch_id, (void*)(uintptr_t)batch_id);
if (!he) {
simple_mtx_unlock(&ctx->batch_mtx);
/* if we can't find it, it either must have finished already or is on a different context */
if (!zink_screen_check_last_finished(zink_screen(ctx->base.screen), batch_id)) {
/* if it hasn't finished, it's on another context, so force a flush so there's something to wait on */
ctx->batch.has_work = true;
zink_fence_wait(&ctx->base);
}
return;
}
fence = he->data;
}
simple_mtx_unlock(&ctx->batch_mtx);
assert(fence);
sync_flush(ctx, zink_batch_state(fence));
zink_vkfence_wait(zink_screen(ctx->base.screen), fence, PIPE_TIMEOUT_INFINITE);
}
bool
zink_check_batch_completion(struct zink_context *ctx, uint32_t batch_id, bool have_lock)
{
assert(ctx->batch.state);
if (!batch_id)
/* not submitted yet */
return false;
if (zink_screen_check_last_finished(zink_screen(ctx->base.screen), batch_id))
return true;
if (ctx->have_timelines) {
bool success = zink_screen_timeline_wait(zink_screen(ctx->base.screen), batch_id, 0);
if (!success)
check_device_lost(ctx);
return success;
}
struct zink_fence *fence;
if (!have_lock)
simple_mtx_lock(&ctx->batch_mtx);
if (ctx->last_fence && batch_id == zink_batch_state(ctx->last_fence)->fence.batch_id)
fence = ctx->last_fence;
else {
struct hash_entry *he = _mesa_hash_table_search_pre_hashed(&ctx->batch_states, batch_id, (void*)(uintptr_t)batch_id);
/* if we can't find it, it either must have finished already or is on a different context */
if (!he) {
if (!have_lock)
simple_mtx_unlock(&ctx->batch_mtx);
/* return compare against last_finished, since this has info from all contexts */
return zink_screen_check_last_finished(zink_screen(ctx->base.screen), batch_id);
}
fence = he->data;
}
if (!have_lock)
simple_mtx_unlock(&ctx->batch_mtx);
assert(fence);
if (zink_screen(ctx->base.screen)->threaded &&
!util_queue_fence_is_signalled(&zink_batch_state(fence)->flush_completed))
return false;
return zink_vkfence_wait(zink_screen(ctx->base.screen), fence, 0);
}
static void
zink_texture_barrier(struct pipe_context *pctx, unsigned flags)
{
struct zink_context *ctx = zink_context(pctx);
if (!ctx->framebuffer || !ctx->framebuffer->state.num_attachments)
return;
VkMemoryBarrier bmb;
bmb.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
bmb.pNext = NULL;
bmb.srcAccessMask = 0;
bmb.dstAccessMask = 0;
struct zink_surface *surf = zink_surface(ctx->framebuffer->surfaces[ctx->framebuffer->state.num_attachments - 1]);
struct zink_resource *res = zink_resource(surf->base.texture);
zink_batch_no_rp(ctx);
if (res->aspect != VK_IMAGE_ASPECT_COLOR_BIT) {
VkMemoryBarrier dmb;
dmb.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
dmb.pNext = NULL;
dmb.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dmb.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(
ctx->batch.state->cmdbuf,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0,
1, &dmb,
0, NULL,
0, NULL
);
} else {
bmb.srcAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
bmb.dstAccessMask |= VK_ACCESS_SHADER_READ_BIT;
}
if (ctx->framebuffer->state.num_attachments > 1) {
bmb.srcAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
bmb.dstAccessMask |= VK_ACCESS_SHADER_READ_BIT;
}
if (bmb.srcAccessMask)
vkCmdPipelineBarrier(
ctx->batch.state->cmdbuf,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0,
1, &bmb,
0, NULL,
0, NULL
);
}
static inline void
mem_barrier(struct zink_batch *batch, VkPipelineStageFlags src_stage, VkPipelineStageFlags dst_stage, VkAccessFlags src, VkAccessFlags dst)
{
VkMemoryBarrier mb;
mb.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
mb.pNext = NULL;
mb.srcAccessMask = src;
mb.dstAccessMask = dst;
vkCmdPipelineBarrier(batch->state->cmdbuf, src_stage, dst_stage, 0, 1, &mb, 0, NULL, 0, NULL);
}
static void
zink_memory_barrier(struct pipe_context *pctx, unsigned flags)
{
struct zink_context *ctx = zink_context(pctx);
VkPipelineStageFlags all_flags = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT |
VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT |
VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
if (!(flags & ~PIPE_BARRIER_UPDATE))
return;
struct zink_batch *batch = &ctx->batch;
zink_end_render_pass(ctx, batch);
if (flags & PIPE_BARRIER_MAPPED_BUFFER) {
/* TODO: this should flush all persistent buffers in use as I think */
}
if (flags & (PIPE_BARRIER_TEXTURE | PIPE_BARRIER_SHADER_BUFFER | PIPE_BARRIER_IMAGE))
mem_barrier(batch, all_flags, all_flags, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
if (flags & PIPE_BARRIER_QUERY_BUFFER)
mem_barrier(batch, all_flags, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT);
if (flags & PIPE_BARRIER_VERTEX_BUFFER)
mem_barrier(batch, all_flags, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
VK_ACCESS_SHADER_WRITE_BIT,
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT);
if (flags & PIPE_BARRIER_INDEX_BUFFER)
mem_barrier(batch, all_flags, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
VK_ACCESS_SHADER_WRITE_BIT,
VK_ACCESS_INDEX_READ_BIT);
if (flags & PIPE_BARRIER_CONSTANT_BUFFER)
mem_barrier(batch, all_flags, all_flags,
VK_ACCESS_SHADER_WRITE_BIT,
VK_ACCESS_UNIFORM_READ_BIT);
if (flags & PIPE_BARRIER_INDIRECT_BUFFER)
mem_barrier(batch, all_flags, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT,
VK_ACCESS_SHADER_WRITE_BIT,
VK_ACCESS_INDIRECT_COMMAND_READ_BIT);
if (flags & PIPE_BARRIER_FRAMEBUFFER)
zink_texture_barrier(pctx, 0);
if (flags & PIPE_BARRIER_STREAMOUT_BUFFER)
mem_barrier(batch, all_flags,
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
VK_ACCESS_SHADER_WRITE_BIT,
VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT |
VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT);
}
static void
zink_flush_resource(struct pipe_context *pctx,
struct pipe_resource *pres)
{
struct zink_context *ctx = zink_context(pctx);
/* TODO: this is not futureproof and should be updated once proper
* WSI support is added
*/
if (pres->bind & (PIPE_BIND_SHARED | PIPE_BIND_SCANOUT))
pipe_resource_reference(&ctx->batch.state->flush_res, pres);
}
void
zink_copy_buffer(struct zink_context *ctx, struct zink_batch *batch, struct zink_resource *dst, struct zink_resource *src,
unsigned dst_offset, unsigned src_offset, unsigned size)
{
VkBufferCopy region;
region.srcOffset = src_offset;
region.dstOffset = dst_offset;
region.size = size;
if (!batch)
batch = zink_batch_no_rp(ctx);
assert(!batch->in_rp);
zink_batch_reference_resource_rw(batch, src, false);
zink_batch_reference_resource_rw(batch, dst, true);
util_range_add(&dst->base.b, &dst->valid_buffer_range, dst_offset, dst_offset + size);
zink_resource_buffer_barrier(ctx, batch, src, VK_ACCESS_TRANSFER_READ_BIT, 0);
zink_resource_buffer_barrier(ctx, batch, dst, VK_ACCESS_TRANSFER_WRITE_BIT, 0);
vkCmdCopyBuffer(batch->state->cmdbuf, src->obj->buffer, dst->obj->buffer, 1, &region);
}
void
zink_copy_image_buffer(struct zink_context *ctx, struct zink_batch *batch, struct zink_resource *dst, struct zink_resource *src,
unsigned dst_level, unsigned dstx, unsigned dsty, unsigned dstz,
unsigned src_level, const struct pipe_box *src_box, enum pipe_map_flags map_flags)
{
struct zink_resource *img = dst->base.b.target == PIPE_BUFFER ? src : dst;
struct zink_resource *buf = dst->base.b.target == PIPE_BUFFER ? dst : src;
if (!batch)
batch = zink_batch_no_rp(ctx);
bool buf2img = buf == src;
if (buf2img) {
zink_resource_image_barrier(ctx, batch, img, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, 0);
zink_resource_buffer_barrier(ctx, batch, buf, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
} else {
zink_resource_image_barrier(ctx, batch, img, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 0, 0);
zink_resource_buffer_barrier(ctx, batch, buf, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
util_range_add(&dst->base.b, &dst->valid_buffer_range, dstx, dstx + src_box->width);
}
VkBufferImageCopy region = {0};
region.bufferOffset = buf2img ? src_box->x : dstx;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.mipLevel = buf2img ? dst_level : src_level;
switch (img->base.b.target) {
case PIPE_TEXTURE_CUBE:
case PIPE_TEXTURE_CUBE_ARRAY:
case PIPE_TEXTURE_2D_ARRAY:
case PIPE_TEXTURE_1D_ARRAY:
/* these use layer */
region.imageSubresource.baseArrayLayer = buf2img ? dstz : src_box->z;
region.imageSubresource.layerCount = src_box->depth;
region.imageOffset.z = 0;
region.imageExtent.depth = 1;
break;
case PIPE_TEXTURE_3D:
/* this uses depth */
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset.z = buf2img ? dstz : src_box->z;
region.imageExtent.depth = src_box->depth;
break;
default:
/* these must only copy one layer */
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset.z = 0;
region.imageExtent.depth = 1;
}
region.imageOffset.x = buf2img ? dstx : src_box->x;
region.imageOffset.y = buf2img ? dsty : src_box->y;
region.imageExtent.width = src_box->width;
region.imageExtent.height = src_box->height;
zink_batch_reference_resource_rw(batch, img, buf2img);
zink_batch_reference_resource_rw(batch, buf, !buf2img);
/* we're using u_transfer_helper_deinterleave, which means we'll be getting PIPE_MAP_* usage
* to indicate whether to copy either the depth or stencil aspects
*/
unsigned aspects = 0;
if (map_flags) {
assert((map_flags & (PIPE_MAP_DEPTH_ONLY | PIPE_MAP_STENCIL_ONLY)) !=
(PIPE_MAP_DEPTH_ONLY | PIPE_MAP_STENCIL_ONLY));
if (map_flags & PIPE_MAP_DEPTH_ONLY)
aspects = VK_IMAGE_ASPECT_DEPTH_BIT;
else if (map_flags & PIPE_MAP_STENCIL_ONLY)
aspects = VK_IMAGE_ASPECT_STENCIL_BIT;
}
if (!aspects)
aspects = img->aspect;
while (aspects) {
int aspect = 1 << u_bit_scan(&aspects);
region.imageSubresource.aspectMask = aspect;
/* this may or may not work with multisampled depth/stencil buffers depending on the driver implementation:
*
* srcImage must have a sample count equal to VK_SAMPLE_COUNT_1_BIT
* - vkCmdCopyImageToBuffer spec
*
* dstImage must have a sample count equal to VK_SAMPLE_COUNT_1_BIT
* - vkCmdCopyBufferToImage spec
*/
if (buf2img)
vkCmdCopyBufferToImage(batch->state->cmdbuf, buf->obj->buffer, img->obj->image, img->layout, 1, &region);
else
vkCmdCopyImageToBuffer(batch->state->cmdbuf, img->obj->image, img->layout, buf->obj->buffer, 1, &region);
}
}
static void
zink_resource_copy_region(struct pipe_context *pctx,
struct pipe_resource *pdst,
unsigned dst_level, unsigned dstx, unsigned dsty, unsigned dstz,
struct pipe_resource *psrc,
unsigned src_level, const struct pipe_box *src_box)
{
struct zink_resource *dst = zink_resource(pdst);
struct zink_resource *src = zink_resource(psrc);
struct zink_context *ctx = zink_context(pctx);
if (dst->base.b.target != PIPE_BUFFER && src->base.b.target != PIPE_BUFFER) {
VkImageCopy region = {0};
if (util_format_get_num_planes(src->base.b.format) == 1 &&
util_format_get_num_planes(dst->base.b.format) == 1) {
/* If neither the calling commands srcImage nor the calling commands dstImage
* has a multi-planar image format then the aspectMask member of srcSubresource
* and dstSubresource must match
*
* -VkImageCopy spec
*/
assert(src->aspect == dst->aspect);
} else
unreachable("planar formats not yet handled");
zink_fb_clears_apply_or_discard(ctx, pdst, (struct u_rect){dstx, dstx + src_box->width, dsty, dsty + src_box->height}, false);
zink_fb_clears_apply_region(ctx, psrc, zink_rect_from_box(src_box));
region.srcSubresource.aspectMask = src->aspect;
region.srcSubresource.mipLevel = src_level;
switch (src->base.b.target) {
case PIPE_TEXTURE_CUBE:
case PIPE_TEXTURE_CUBE_ARRAY:
case PIPE_TEXTURE_2D_ARRAY:
case PIPE_TEXTURE_1D_ARRAY:
/* these use layer */
region.srcSubresource.baseArrayLayer = src_box->z;
region.srcSubresource.layerCount = src_box->depth;
region.srcOffset.z = 0;
region.extent.depth = 1;
break;
case PIPE_TEXTURE_3D:
/* this uses depth */
region.srcSubresource.baseArrayLayer = 0;
region.srcSubresource.layerCount = 1;
region.srcOffset.z = src_box->z;
region.extent.depth = src_box->depth;
break;
default:
/* these must only copy one layer */
region.srcSubresource.baseArrayLayer = 0;
region.srcSubresource.layerCount = 1;
region.srcOffset.z = 0;
region.extent.depth = 1;
}
region.srcOffset.x = src_box->x;
region.srcOffset.y = src_box->y;
region.dstSubresource.aspectMask = dst->aspect;
region.dstSubresource.mipLevel = dst_level;
switch (dst->base.b.target) {
case PIPE_TEXTURE_CUBE:
case PIPE_TEXTURE_CUBE_ARRAY:
case PIPE_TEXTURE_2D_ARRAY:
case PIPE_TEXTURE_1D_ARRAY:
/* these use layer */
region.dstSubresource.baseArrayLayer = dstz;
region.dstSubresource.layerCount = src_box->depth;
region.dstOffset.z = 0;
break;
case PIPE_TEXTURE_3D:
/* this uses depth */
region.dstSubresource.baseArrayLayer = 0;
region.dstSubresource.layerCount = 1;
region.dstOffset.z = dstz;
break;
default:
/* these must only copy one layer */
region.dstSubresource.baseArrayLayer = 0;
region.dstSubresource.layerCount = 1;
region.dstOffset.z = 0;
}
region.dstOffset.x = dstx;
region.dstOffset.y = dsty;
region.extent.width = src_box->width;
region.extent.height = src_box->height;
struct zink_batch *batch = zink_batch_no_rp(ctx);
zink_batch_reference_resource_rw(batch, src, false);
zink_batch_reference_resource_rw(batch, dst, true);
zink_resource_setup_transfer_layouts(ctx, src, dst);
vkCmdCopyImage(batch->state->cmdbuf, src->obj->image, src->layout,
dst->obj->image, dst->layout,
1, &region);
} else if (dst->base.b.target == PIPE_BUFFER &&
src->base.b.target == PIPE_BUFFER) {
zink_copy_buffer(ctx, NULL, dst, src, dstx, src_box->x, src_box->width);
} else
zink_copy_image_buffer(ctx, NULL, dst, src, dst_level, dstx, dsty, dstz, src_level, src_box, 0);
}
static struct pipe_stream_output_target *
zink_create_stream_output_target(struct pipe_context *pctx,
struct pipe_resource *pres,
unsigned buffer_offset,
unsigned buffer_size)
{
struct zink_so_target *t;
t = CALLOC_STRUCT(zink_so_target);
if (!t)
return NULL;
/* using PIPE_BIND_CUSTOM here lets us create a custom pipe buffer resource,
* which allows us to differentiate and use VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT
* as we must for this case
*/
t->counter_buffer = pipe_buffer_create(pctx->screen, PIPE_BIND_STREAM_OUTPUT | PIPE_BIND_CUSTOM, PIPE_USAGE_DEFAULT, 4);
if (!t->counter_buffer) {
FREE(t);
return NULL;
}
t->base.reference.count = 1;
t->base.context = pctx;
pipe_resource_reference(&t->base.buffer, pres);
t->base.buffer_offset = buffer_offset;
t->base.buffer_size = buffer_size;
zink_resource(t->base.buffer)->bind_history |= ZINK_RESOURCE_USAGE_STREAMOUT;
return &t->base;
}
static void
zink_stream_output_target_destroy(struct pipe_context *pctx,
struct pipe_stream_output_target *psot)
{
struct zink_so_target *t = (struct zink_so_target *)psot;
pipe_resource_reference(&t->counter_buffer, NULL);
pipe_resource_reference(&t->base.buffer, NULL);
FREE(t);
}
static void
zink_set_stream_output_targets(struct pipe_context *pctx,
unsigned num_targets,
struct pipe_stream_output_target **targets,
const unsigned *offsets)
{
struct zink_context *ctx = zink_context(pctx);
if (num_targets == 0) {
for (unsigned i = 0; i < ctx->num_so_targets; i++)
pipe_so_target_reference(&ctx->so_targets[i], NULL);
ctx->num_so_targets = 0;
} else {
for (unsigned i = 0; i < num_targets; i++) {
struct zink_so_target *t = zink_so_target(targets[i]);
pipe_so_target_reference(&ctx->so_targets[i], targets[i]);
if (!t)
continue;
struct zink_resource *res = zink_resource(t->counter_buffer);
if (offsets[0] == (unsigned)-1)
ctx->xfb_barrier |= zink_resource_buffer_needs_barrier(res,
VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT);
else
ctx->xfb_barrier |= zink_resource_buffer_needs_barrier(res,
VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT,
VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT);
}
for (unsigned i = num_targets; i < ctx->num_so_targets; i++)
pipe_so_target_reference(&ctx->so_targets[i], NULL);
ctx->num_so_targets = num_targets;
/* TODO: possibly avoid rebinding on resume if resuming from same buffers? */
ctx->dirty_so_targets = true;
}
}
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, false))
zink_batch_no_rp(ctx);
}
static void
rebind_buffer(struct zink_context *ctx, struct zink_resource *res)
{
const unsigned total_rebinds = res->bind_count[0] + res->bind_count[1];
unsigned num_rebinds = 0, num_image_rebinds_remaining[2] = {res->image_bind_count[0], res->image_bind_count[1]};
bool has_write = false;
if (res->vbo_bind_mask) {
u_foreach_bit(slot, res->vbo_bind_mask) {
if (ctx->vertex_buffers[slot].buffer.resource != &res->base.b) //wrong context
return;
set_vertex_buffer_clamped(ctx, slot);
num_rebinds++;
}
ctx->vertex_buffers_dirty = true;
}
for (unsigned shader = 0; num_rebinds < total_rebinds && shader < PIPE_SHADER_TYPES; shader++) {
u_foreach_bit(slot, res->ubo_bind_mask[shader]) {
if (&res->base.b != ctx->ubos[shader][slot].buffer) //wrong context
return;
update_descriptor_state_ubo(ctx, shader, slot);
zink_screen(ctx->base.screen)->context_invalidate_descriptor_state(ctx, shader, ZINK_DESCRIPTOR_TYPE_UBO, slot, 1);
num_rebinds++;
}
u_foreach_bit(slot, res->ssbo_bind_mask[shader]) {
struct pipe_shader_buffer *ssbo = &ctx->ssbos[shader][slot];
if (&res->base.b != ssbo->buffer) //wrong context
return;
has_write |= ctx->writable_ssbos[shader] & BITFIELD64_BIT(slot);
util_range_add(&res->base.b, &res->valid_buffer_range, ssbo->buffer_offset,
ssbo->buffer_offset + ssbo->buffer_size);
update_descriptor_state_ssbo(ctx, shader, slot);
zink_screen(ctx->base.screen)->context_invalidate_descriptor_state(ctx, shader, ZINK_DESCRIPTOR_TYPE_SSBO, slot, 1);
num_rebinds++;
}
u_foreach_bit(slot, res->sampler_binds[shader]) {
struct zink_sampler_view *sampler_view = zink_sampler_view(ctx->sampler_views[shader][slot]);
if (&res->base.b != sampler_view->base.texture) //wrong context
return;
if (zink_batch_usage_exists(sampler_view->buffer_view->batch_uses))
zink_batch_reference_bufferview(&ctx->batch, sampler_view->buffer_view);
zink_buffer_view_reference(zink_screen(ctx->base.screen), &sampler_view->buffer_view, NULL);
sampler_view->buffer_view = get_buffer_view(ctx, res, sampler_view->base.format,
sampler_view->base.u.buf.offset, sampler_view->base.u.buf.size);
update_descriptor_state_sampler(ctx, shader, slot);
zink_screen(ctx->base.screen)->context_invalidate_descriptor_state(ctx, shader, ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW, slot, 1);
num_rebinds++;
}
if (unlikely(num_image_rebinds_remaining[shader == PIPE_SHADER_COMPUTE])) {
for (unsigned slot = 0; num_image_rebinds_remaining[shader == PIPE_SHADER_COMPUTE] &&
slot < ctx->di.num_images[shader]; slot++) {
struct zink_resource *cres = zink_get_resource_for_descriptor(ctx, ZINK_DESCRIPTOR_TYPE_IMAGE, shader, slot);
if (res != cres)
continue;
struct zink_image_view *image_view = &ctx->image_views[shader][slot];
zink_descriptor_set_refs_clear(&image_view->buffer_view->desc_set_refs, image_view->buffer_view);
if (zink_batch_usage_exists(image_view->buffer_view->batch_uses))
zink_batch_reference_bufferview(&ctx->batch, image_view->buffer_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;
}
has_write |= image_view->base.access & PIPE_IMAGE_ACCESS_WRITE;
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);
util_range_add(&res->base.b, &res->valid_buffer_range, image_view->base.u.buf.offset,
image_view->base.u.buf.offset + image_view->base.u.buf.size);
update_descriptor_state_image(ctx, shader, slot);
zink_screen(ctx->base.screen)->context_invalidate_descriptor_state(ctx, shader, ZINK_DESCRIPTOR_TYPE_IMAGE, slot, 1);
num_image_rebinds_remaining[shader == PIPE_SHADER_COMPUTE]--;
}
}
}
zink_batch_resource_usage_set(&ctx->batch, res, has_write);
}
static bool
zink_resource_commit(struct pipe_context *pctx, struct pipe_resource *pres, unsigned level, struct pipe_box *box, bool commit)
{
struct zink_context *ctx = zink_context(pctx);
struct zink_resource *res = zink_resource(pres);
struct zink_screen *screen = zink_screen(pctx->screen);
/* if any current usage exists, flush the queue */
if (zink_resource_has_unflushed_usage(res))
zink_flush_queue(ctx);
bool ret = zink_bo_commit(screen, res, box->x, box->width, commit);
if (!ret)
check_device_lost(ctx);
return ret;
}
static void
rebind_image(struct zink_context *ctx, struct zink_resource *res)
{
zink_rebind_framebuffer(ctx, res);
if (!res->bind_count[0] && !res->bind_count[1])
return;
for (unsigned i = 0; i < PIPE_SHADER_TYPES; i++) {
if (res->sampler_binds[i]) {
for (unsigned j = 0; j < ctx->di.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.b) {
struct pipe_surface *psurf = &sv->image_view->base;
zink_rebind_surface(ctx, &psurf);
sv->image_view = zink_surface(psurf);
zink_screen(ctx->base.screen)->context_invalidate_descriptor_state(ctx, i, ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW, j, 1);
update_descriptor_state_sampler(ctx, i, j);
}
}
}
if (!res->image_bind_count[i == PIPE_SHADER_COMPUTE])
continue;
for (unsigned j = 0; j < ctx->di.num_images[i]; j++) {
if (zink_resource(ctx->image_views[i][j].base.resource) == res) {
zink_screen(ctx->base.screen)->context_invalidate_descriptor_state(ctx, i, ZINK_DESCRIPTOR_TYPE_IMAGE, j, 1);
update_descriptor_state_sampler(ctx, i, j);
_mesa_set_add(ctx->need_barriers[i == PIPE_SHADER_COMPUTE], res);
}
}
}
}
void
zink_resource_rebind(struct zink_context *ctx, struct zink_resource *res)
{
if (res->bind_history & ZINK_RESOURCE_USAGE_STREAMOUT)
ctx->dirty_so_targets = true;
/* force counter buffer reset */
res->bind_history &= ~ZINK_RESOURCE_USAGE_STREAMOUT;
if (!res->bind_count[0] && !res->bind_count[1])
return;
if (res->base.b.target == PIPE_BUFFER)
rebind_buffer(ctx, res);
else
rebind_image(ctx, res);
}
static void
zink_context_replace_buffer_storage(struct pipe_context *pctx, struct pipe_resource *dst,
struct pipe_resource *src, unsigned num_rebinds,
uint32_t rebind_mask, uint32_t delete_buffer_id)
{
struct zink_resource *d = zink_resource(dst);
struct zink_resource *s = zink_resource(src);
assert(d->internal_format == s->internal_format);
util_idalloc_mt_free(&zink_screen(pctx->screen)->buffer_ids, delete_buffer_id);
if (zink_resource_has_unflushed_usage(d))
zink_batch_reference_resource(&zink_context(pctx)->batch, d);
zink_resource_object_reference(zink_screen(pctx->screen), &d->obj, s->obj);
d->access = s->access;
d->access_stage = s->access_stage;
d->unordered_barrier = s->unordered_barrier;
zink_resource_rebind(zink_context(pctx), d);
}
static bool
zink_context_is_resource_busy(struct pipe_screen *pscreen, struct pipe_resource *pres, unsigned usage)
{
struct zink_screen *screen = zink_screen(pscreen);
struct zink_resource *res = zink_resource(pres);
uint32_t check_usage = 0;
if (usage & PIPE_MAP_READ)
check_usage |= ZINK_RESOURCE_ACCESS_WRITE;
if (usage & PIPE_MAP_WRITE)
check_usage |= ZINK_RESOURCE_ACCESS_RW;
return !zink_resource_usage_check_completion(screen, res, check_usage);
}
static void
zink_emit_string_marker(struct pipe_context *pctx,
const char *string, int len)
{
struct zink_screen *screen = zink_screen(pctx->screen);
struct zink_batch *batch = &zink_context(pctx)->batch;
/* make sure string is nul-terminated */
char buf[512], *temp = NULL;
if (len < ARRAY_SIZE(buf)) {
memcpy(buf, string, len);
buf[len] = '\0';
string = buf;
} else
string = temp = strndup(string, len);
VkDebugUtilsLabelEXT label = {
VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, NULL,
string,
{ 0 }
};
screen->vk.CmdInsertDebugUtilsLabelEXT(batch->state->cmdbuf, &label);
free(temp);
}
struct pipe_context *
zink_context_create(struct pipe_screen *pscreen, void *priv, unsigned flags)
{
struct zink_screen *screen = zink_screen(pscreen);
struct zink_context *ctx = rzalloc(NULL, struct zink_context);
if (!ctx)
goto fail;
ctx->have_timelines = screen->info.have_KHR_timeline_semaphore;
ctx->pipeline_changed[0] = ctx->pipeline_changed[1] = true;
ctx->gfx_pipeline_state.dirty = true;
ctx->compute_pipeline_state.dirty = true;
ctx->fb_changed = ctx->rp_changed = true;
zink_init_draw_functions(ctx, screen);
zink_init_grid_functions(ctx);
ctx->base.screen = pscreen;
ctx->base.priv = priv;
ctx->base.destroy = zink_context_destroy;
ctx->base.get_device_reset_status = zink_get_device_reset_status;
ctx->base.set_device_reset_callback = zink_set_device_reset_callback;
zink_context_state_init(&ctx->base);
ctx->base.create_sampler_state = zink_create_sampler_state;
ctx->base.bind_sampler_states = zink_bind_sampler_states;
ctx->base.delete_sampler_state = zink_delete_sampler_state;
ctx->base.create_sampler_view = zink_create_sampler_view;
ctx->base.set_sampler_views = zink_set_sampler_views;
ctx->base.sampler_view_destroy = zink_sampler_view_destroy;
ctx->base.get_sample_position = zink_get_sample_position;
ctx->base.set_sample_locations = zink_set_sample_locations;
zink_program_init(ctx);
ctx->base.set_polygon_stipple = zink_set_polygon_stipple;
ctx->base.set_vertex_buffers = zink_set_vertex_buffers;
ctx->base.set_viewport_states = zink_set_viewport_states;
ctx->base.set_scissor_states = zink_set_scissor_states;
ctx->base.set_inlinable_constants = zink_set_inlinable_constants;
ctx->base.set_constant_buffer = zink_set_constant_buffer;
ctx->base.set_shader_buffers = zink_set_shader_buffers;
ctx->base.set_shader_images = zink_set_shader_images;
ctx->base.set_framebuffer_state = zink_set_framebuffer_state;
ctx->base.set_stencil_ref = zink_set_stencil_ref;
ctx->base.set_clip_state = zink_set_clip_state;
ctx->base.set_blend_color = zink_set_blend_color;
ctx->base.set_tess_state = zink_set_tess_state;
ctx->base.set_patch_vertices = zink_set_patch_vertices;
ctx->base.set_sample_mask = zink_set_sample_mask;
ctx->base.clear = zink_clear;
ctx->base.clear_texture = zink_clear_texture;
ctx->base.clear_buffer = zink_clear_buffer;
ctx->base.clear_render_target = zink_clear_render_target;
ctx->base.clear_depth_stencil = zink_clear_depth_stencil;
ctx->base.fence_server_sync = zink_fence_server_sync;
ctx->base.flush = zink_flush;
ctx->base.memory_barrier = zink_memory_barrier;
ctx->base.texture_barrier = zink_texture_barrier;
ctx->base.evaluate_depth_buffer = zink_evaluate_depth_buffer;
ctx->base.resource_commit = zink_resource_commit;
ctx->base.resource_copy_region = zink_resource_copy_region;
ctx->base.blit = zink_blit;
ctx->base.create_stream_output_target = zink_create_stream_output_target;
ctx->base.stream_output_target_destroy = zink_stream_output_target_destroy;
ctx->base.set_stream_output_targets = zink_set_stream_output_targets;
ctx->base.flush_resource = zink_flush_resource;
ctx->base.emit_string_marker = zink_emit_string_marker;
zink_context_surface_init(&ctx->base);
zink_context_resource_init(&ctx->base);
zink_context_query_init(&ctx->base);
_mesa_set_init(&ctx->update_barriers[0][0], ctx, _mesa_hash_pointer, _mesa_key_pointer_equal);
_mesa_set_init(&ctx->update_barriers[1][0], ctx, _mesa_hash_pointer, _mesa_key_pointer_equal);
_mesa_set_init(&ctx->update_barriers[0][1], ctx, _mesa_hash_pointer, _mesa_key_pointer_equal);
_mesa_set_init(&ctx->update_barriers[1][1], ctx, _mesa_hash_pointer, _mesa_key_pointer_equal);
ctx->need_barriers[0] = &ctx->update_barriers[0][0];
ctx->need_barriers[1] = &ctx->update_barriers[1][0];
util_dynarray_init(&ctx->free_batch_states, ctx);
_mesa_hash_table_init(&ctx->batch_states, ctx, NULL, _mesa_key_pointer_equal);
ctx->gfx_pipeline_state.have_EXT_extended_dynamic_state = screen->info.have_EXT_extended_dynamic_state;
slab_create_child(&ctx->transfer_pool, &screen->transfer_pool);
slab_create_child(&ctx->transfer_pool_unsync, &screen->transfer_pool);
ctx->base.stream_uploader = u_upload_create_default(&ctx->base);
ctx->base.const_uploader = u_upload_create_default(&ctx->base);
for (int i = 0; i < ARRAY_SIZE(ctx->fb_clears); i++)
util_dynarray_init(&ctx->fb_clears[i].clears, ctx);
ctx->blitter = util_blitter_create(&ctx->base);
if (!ctx->blitter)
goto fail;
ctx->program_cache = _mesa_hash_table_create(NULL,
hash_gfx_program,
equals_gfx_program);
ctx->compute_program_cache = _mesa_hash_table_create(NULL,
_mesa_hash_pointer,
_mesa_key_pointer_equal);
ctx->render_pass_cache = _mesa_hash_table_create(NULL,
hash_render_pass_state,
equals_render_pass_state);
if (!ctx->program_cache || !ctx->compute_program_cache || !ctx->render_pass_cache)
goto fail;
const uint8_t data[] = {0};
ctx->dummy_vertex_buffer = pipe_buffer_create(&screen->base,
PIPE_BIND_VERTEX_BUFFER | PIPE_BIND_SHADER_IMAGE, PIPE_USAGE_IMMUTABLE, sizeof(data));
if (!ctx->dummy_vertex_buffer)
goto fail;
ctx->dummy_xfb_buffer = pipe_buffer_create(&screen->base,
PIPE_BIND_STREAM_OUTPUT, PIPE_USAGE_DEFAULT, sizeof(data));
if (!ctx->dummy_xfb_buffer)
goto fail;
ctx->dummy_surface = zink_surface_create_null(ctx, PIPE_TEXTURE_2D, 1, 1, 1);
if (!ctx->dummy_surface)
goto fail;
ctx->dummy_bufferview = get_buffer_view(ctx, zink_resource(ctx->dummy_vertex_buffer), PIPE_FORMAT_R8_UNORM, 0, sizeof(data));
if (!ctx->dummy_bufferview)
goto fail;
if (!zink_descriptor_layouts_init(ctx))
goto fail;
if (!screen->descriptors_init(ctx)) {
zink_screen_init_descriptor_funcs(screen, true);
if (!screen->descriptors_init(ctx))
goto fail;
}
ctx->have_timelines = screen->info.have_KHR_timeline_semaphore;
simple_mtx_init(&ctx->batch_mtx, mtx_plain);
zink_start_batch(ctx, &ctx->batch);
if (!ctx->batch.state)
goto fail;
pipe_buffer_write(&ctx->base, ctx->dummy_vertex_buffer, 0, sizeof(data), data);
pipe_buffer_write(&ctx->base, ctx->dummy_xfb_buffer, 0, sizeof(data), data);
for (unsigned i = 0; i < PIPE_SHADER_TYPES; i++) {
/* need to update these based on screen config for null descriptors */
for (unsigned j = 0; j < 32; j++) {
update_descriptor_state_ubo(ctx, i, j);
update_descriptor_state_sampler(ctx, i, j);
update_descriptor_state_ssbo(ctx, i, j);
update_descriptor_state_image(ctx, i, j);
}
}
p_atomic_inc(&screen->base.num_contexts);
zink_select_draw_vbo(ctx);
zink_select_launch_grid(ctx);
if (!(flags & PIPE_CONTEXT_PREFER_THREADED) || flags & PIPE_CONTEXT_COMPUTE_ONLY) {
return &ctx->base;
}
struct threaded_context *tc = (struct threaded_context*)threaded_context_create(&ctx->base, &screen->transfer_pool,
zink_context_replace_buffer_storage,
zink_create_tc_fence_for_tc,
zink_context_is_resource_busy, true, &ctx->tc);
if (tc && (struct zink_context*)tc != ctx) {
threaded_context_init_bytes_mapped_limit(tc, 4);
ctx->base.set_context_param = zink_set_context_param;
}
return (struct pipe_context*)tc;
fail:
if (ctx)
zink_context_destroy(&ctx->base);
return NULL;
}