+static void
+zink_emit_xfb_counter_barrier(struct zink_context *ctx)
+{
+ /* Between the pause and resume there needs to be a memory barrier for the counter buffers
+ * with a source access of VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT
+ * at pipeline stage VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT
+ * to a destination access of VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT
+ * at pipeline stage VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT.
+ *
+ * - from VK_EXT_transform_feedback spec
+ */
+ VkBufferMemoryBarrier barriers[PIPE_MAX_SO_OUTPUTS] = {};
+ unsigned barrier_count = 0;
+
+ for (unsigned i = 0; i < ctx->num_so_targets; i++) {
+ struct zink_so_target *t = zink_so_target(ctx->so_targets[i]);
+ if (t->counter_buffer_valid) {
+ barriers[i].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
+ barriers[i].srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;
+ barriers[i].dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT;
+ barriers[i].buffer = zink_resource(t->counter_buffer)->buffer;
+ barriers[i].size = VK_WHOLE_SIZE;
+ barrier_count++;
+ }
+ }
+ struct zink_batch *batch = zink_batch_no_rp(ctx);
+ vkCmdPipelineBarrier(batch->cmdbuf,
+ VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
+ VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT,
+ 0,
+ 0, NULL,
+ barrier_count, barriers,
+ 0, NULL
+ );
+ ctx->xfb_barrier = false;
+}
+
+static void
+zink_emit_xfb_vertex_input_barrier(struct zink_context *ctx, struct zink_resource *res)
+{
+ /* A pipeline barrier is required between using the buffers as
+ * transform feedback buffers and vertex buffers to
+ * ensure all writes to the transform feedback buffers are visible
+ * when the data is read as vertex attributes.
+ * The source access is VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT
+ * and the destination access is VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT
+ * for the pipeline stages VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT
+ * and VK_PIPELINE_STAGE_VERTEX_INPUT_BIT respectively.
+ *
+ * - 20.3.1. Drawing Transform Feedback
+ */
+ VkBufferMemoryBarrier barriers[1] = {};
+ barriers[0].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
+ barriers[0].srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;
+ barriers[0].dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
+ barriers[0].buffer = res->buffer;
+ barriers[0].size = VK_WHOLE_SIZE;
+ struct zink_batch *batch = zink_batch_no_rp(ctx);
+ zink_batch_reference_resoure(batch, res);
+ vkCmdPipelineBarrier(batch->cmdbuf,
+ VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT,
+ VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,
+ 0,
+ 0, NULL,
+ ARRAY_SIZE(barriers), barriers,
+ 0, NULL
+ );
+ res->needs_xfb_barrier = false;
+}
+
+static void
+zink_emit_stream_output_targets(struct pipe_context *pctx)
+{
+ struct zink_context *ctx = zink_context(pctx);
+ struct zink_screen *screen = zink_screen(pctx->screen);
+ struct zink_batch *batch = zink_curr_batch(ctx);
+ VkBuffer buffers[PIPE_MAX_SO_OUTPUTS];
+ VkDeviceSize buffer_offsets[PIPE_MAX_SO_OUTPUTS];
+ VkDeviceSize buffer_sizes[PIPE_MAX_SO_OUTPUTS];
+
+ for (unsigned i = 0; i < ctx->num_so_targets; i++) {
+ struct zink_so_target *t = (struct zink_so_target *)ctx->so_targets[i];
+ buffers[i] = zink_resource(t->base.buffer)->buffer;
+ zink_batch_reference_resoure(batch, zink_resource(t->base.buffer));
+ buffer_offsets[i] = t->base.buffer_offset;
+ buffer_sizes[i] = t->base.buffer_size;
+ }
+
+ screen->vk_CmdBindTransformFeedbackBuffersEXT(batch->cmdbuf, 0, ctx->num_so_targets,
+ buffers, buffer_offsets,
+ buffer_sizes);
+ ctx->dirty_so_targets = false;
+}
+