1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright 2022 Google LLC
3bf215546Sopenharmony_ci * SPDX-License-Identifier: MIT
4bf215546Sopenharmony_ci */
5bf215546Sopenharmony_ci
6bf215546Sopenharmony_ci#include "vn_feedback.h"
7bf215546Sopenharmony_ci
8bf215546Sopenharmony_ci#include "vn_device.h"
9bf215546Sopenharmony_ci#include "vn_physical_device.h"
10bf215546Sopenharmony_ci#include "vn_queue.h"
11bf215546Sopenharmony_ci
12bf215546Sopenharmony_ci/* coherent buffer with bound and mapped memory */
13bf215546Sopenharmony_cistruct vn_feedback_buffer {
14bf215546Sopenharmony_ci   VkBuffer buffer;
15bf215546Sopenharmony_ci   VkDeviceMemory memory;
16bf215546Sopenharmony_ci   void *data;
17bf215546Sopenharmony_ci
18bf215546Sopenharmony_ci   struct list_head head;
19bf215546Sopenharmony_ci};
20bf215546Sopenharmony_ci
21bf215546Sopenharmony_cistatic uint32_t
22bf215546Sopenharmony_civn_get_memory_type_index(const VkPhysicalDeviceMemoryProperties *mem_props,
23bf215546Sopenharmony_ci                         uint32_t mem_type_bits,
24bf215546Sopenharmony_ci                         VkMemoryPropertyFlags required_mem_flags)
25bf215546Sopenharmony_ci{
26bf215546Sopenharmony_ci   u_foreach_bit(mem_type_index, mem_type_bits)
27bf215546Sopenharmony_ci   {
28bf215546Sopenharmony_ci      assert(mem_type_index < mem_props->memoryTypeCount);
29bf215546Sopenharmony_ci      if ((mem_props->memoryTypes[mem_type_index].propertyFlags &
30bf215546Sopenharmony_ci           required_mem_flags) == required_mem_flags)
31bf215546Sopenharmony_ci         return mem_type_index;
32bf215546Sopenharmony_ci   }
33bf215546Sopenharmony_ci
34bf215546Sopenharmony_ci   return UINT32_MAX;
35bf215546Sopenharmony_ci}
36bf215546Sopenharmony_ci
37bf215546Sopenharmony_cistatic VkResult
38bf215546Sopenharmony_civn_feedback_buffer_create(struct vn_device *dev,
39bf215546Sopenharmony_ci                          uint32_t size,
40bf215546Sopenharmony_ci                          const VkAllocationCallbacks *alloc,
41bf215546Sopenharmony_ci                          struct vn_feedback_buffer **out_feedback_buf)
42bf215546Sopenharmony_ci{
43bf215546Sopenharmony_ci   const bool exclusive = dev->queue_family_count == 1;
44bf215546Sopenharmony_ci   const VkPhysicalDeviceMemoryProperties *mem_props =
45bf215546Sopenharmony_ci      &dev->physical_device->memory_properties.memoryProperties;
46bf215546Sopenharmony_ci   VkDevice dev_handle = vn_device_to_handle(dev);
47bf215546Sopenharmony_ci   struct vn_feedback_buffer *feedback_buf;
48bf215546Sopenharmony_ci   VkResult result;
49bf215546Sopenharmony_ci
50bf215546Sopenharmony_ci   feedback_buf = vk_zalloc(alloc, sizeof(*feedback_buf), VN_DEFAULT_ALIGN,
51bf215546Sopenharmony_ci                            VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
52bf215546Sopenharmony_ci   if (!feedback_buf)
53bf215546Sopenharmony_ci      return VK_ERROR_OUT_OF_HOST_MEMORY;
54bf215546Sopenharmony_ci
55bf215546Sopenharmony_ci   /* use concurrent to avoid explicit queue family ownership transfer for
56bf215546Sopenharmony_ci    * device created with queues from multiple queue families
57bf215546Sopenharmony_ci    */
58bf215546Sopenharmony_ci   const VkBufferCreateInfo buf_create_info = {
59bf215546Sopenharmony_ci      .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
60bf215546Sopenharmony_ci      .size = size,
61bf215546Sopenharmony_ci      .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
62bf215546Sopenharmony_ci      .sharingMode =
63bf215546Sopenharmony_ci         exclusive ? VK_SHARING_MODE_EXCLUSIVE : VK_SHARING_MODE_CONCURRENT,
64bf215546Sopenharmony_ci      /* below favors the current venus protocol */
65bf215546Sopenharmony_ci      .queueFamilyIndexCount = exclusive ? 0 : dev->queue_family_count,
66bf215546Sopenharmony_ci      .pQueueFamilyIndices = exclusive ? NULL : dev->queue_families,
67bf215546Sopenharmony_ci   };
68bf215546Sopenharmony_ci   result = vn_CreateBuffer(dev_handle, &buf_create_info, alloc,
69bf215546Sopenharmony_ci                            &feedback_buf->buffer);
70bf215546Sopenharmony_ci   if (result != VK_SUCCESS)
71bf215546Sopenharmony_ci      goto out_free_feedback_buf;
72bf215546Sopenharmony_ci
73bf215546Sopenharmony_ci   struct vn_buffer *buf = vn_buffer_from_handle(feedback_buf->buffer);
74bf215546Sopenharmony_ci   const VkMemoryRequirements *mem_req =
75bf215546Sopenharmony_ci      &buf->requirements.memory.memoryRequirements;
76bf215546Sopenharmony_ci   const uint32_t mem_type_index =
77bf215546Sopenharmony_ci      vn_get_memory_type_index(mem_props, mem_req->memoryTypeBits,
78bf215546Sopenharmony_ci                               VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
79bf215546Sopenharmony_ci   if (mem_type_index >= mem_props->memoryTypeCount) {
80bf215546Sopenharmony_ci      result = VK_ERROR_INITIALIZATION_FAILED;
81bf215546Sopenharmony_ci      goto out_destroy_buffer;
82bf215546Sopenharmony_ci   }
83bf215546Sopenharmony_ci
84bf215546Sopenharmony_ci   const VkMemoryAllocateInfo mem_alloc_info = {
85bf215546Sopenharmony_ci      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
86bf215546Sopenharmony_ci      .allocationSize = mem_req->size,
87bf215546Sopenharmony_ci      .memoryTypeIndex = mem_type_index,
88bf215546Sopenharmony_ci   };
89bf215546Sopenharmony_ci   result = vn_AllocateMemory(dev_handle, &mem_alloc_info, alloc,
90bf215546Sopenharmony_ci                              &feedback_buf->memory);
91bf215546Sopenharmony_ci   if (result != VK_SUCCESS)
92bf215546Sopenharmony_ci      goto out_destroy_buffer;
93bf215546Sopenharmony_ci
94bf215546Sopenharmony_ci   const VkBindBufferMemoryInfo bind_info = {
95bf215546Sopenharmony_ci      .sType = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO,
96bf215546Sopenharmony_ci      .buffer = feedback_buf->buffer,
97bf215546Sopenharmony_ci      .memory = feedback_buf->memory,
98bf215546Sopenharmony_ci      .memoryOffset = 0,
99bf215546Sopenharmony_ci   };
100bf215546Sopenharmony_ci   result = vn_BindBufferMemory2(dev_handle, 1, &bind_info);
101bf215546Sopenharmony_ci   if (result != VK_SUCCESS)
102bf215546Sopenharmony_ci      goto out_free_memory;
103bf215546Sopenharmony_ci
104bf215546Sopenharmony_ci   result = vn_MapMemory(dev_handle, feedback_buf->memory, 0, VK_WHOLE_SIZE,
105bf215546Sopenharmony_ci                         0, &feedback_buf->data);
106bf215546Sopenharmony_ci   if (result != VK_SUCCESS)
107bf215546Sopenharmony_ci      goto out_free_memory;
108bf215546Sopenharmony_ci
109bf215546Sopenharmony_ci   *out_feedback_buf = feedback_buf;
110bf215546Sopenharmony_ci
111bf215546Sopenharmony_ci   return VK_SUCCESS;
112bf215546Sopenharmony_ci
113bf215546Sopenharmony_ciout_free_memory:
114bf215546Sopenharmony_ci   vn_FreeMemory(dev_handle, feedback_buf->memory, alloc);
115bf215546Sopenharmony_ci
116bf215546Sopenharmony_ciout_destroy_buffer:
117bf215546Sopenharmony_ci   vn_DestroyBuffer(dev_handle, feedback_buf->buffer, alloc);
118bf215546Sopenharmony_ci
119bf215546Sopenharmony_ciout_free_feedback_buf:
120bf215546Sopenharmony_ci   vk_free(alloc, feedback_buf);
121bf215546Sopenharmony_ci
122bf215546Sopenharmony_ci   return result;
123bf215546Sopenharmony_ci}
124bf215546Sopenharmony_ci
125bf215546Sopenharmony_cistatic void
126bf215546Sopenharmony_civn_feedback_buffer_destroy(struct vn_device *dev,
127bf215546Sopenharmony_ci                           struct vn_feedback_buffer *feedback_buf,
128bf215546Sopenharmony_ci                           const VkAllocationCallbacks *alloc)
129bf215546Sopenharmony_ci{
130bf215546Sopenharmony_ci   VkDevice dev_handle = vn_device_to_handle(dev);
131bf215546Sopenharmony_ci
132bf215546Sopenharmony_ci   vn_UnmapMemory(dev_handle, feedback_buf->memory);
133bf215546Sopenharmony_ci   vn_FreeMemory(dev_handle, feedback_buf->memory, alloc);
134bf215546Sopenharmony_ci   vn_DestroyBuffer(dev_handle, feedback_buf->buffer, alloc);
135bf215546Sopenharmony_ci   vk_free(alloc, feedback_buf);
136bf215546Sopenharmony_ci}
137bf215546Sopenharmony_ci
138bf215546Sopenharmony_cistatic VkResult
139bf215546Sopenharmony_civn_feedback_pool_grow_locked(struct vn_feedback_pool *pool)
140bf215546Sopenharmony_ci{
141bf215546Sopenharmony_ci   VN_TRACE_FUNC();
142bf215546Sopenharmony_ci   struct vn_feedback_buffer *feedback_buf = NULL;
143bf215546Sopenharmony_ci   VkResult result;
144bf215546Sopenharmony_ci
145bf215546Sopenharmony_ci   result = vn_feedback_buffer_create(pool->device, pool->size, pool->alloc,
146bf215546Sopenharmony_ci                                      &feedback_buf);
147bf215546Sopenharmony_ci   if (result != VK_SUCCESS)
148bf215546Sopenharmony_ci      return result;
149bf215546Sopenharmony_ci
150bf215546Sopenharmony_ci   pool->used = 0;
151bf215546Sopenharmony_ci
152bf215546Sopenharmony_ci   list_add(&feedback_buf->head, &pool->feedback_buffers);
153bf215546Sopenharmony_ci
154bf215546Sopenharmony_ci   return VK_SUCCESS;
155bf215546Sopenharmony_ci}
156bf215546Sopenharmony_ci
157bf215546Sopenharmony_ciVkResult
158bf215546Sopenharmony_civn_feedback_pool_init(struct vn_device *dev,
159bf215546Sopenharmony_ci                      struct vn_feedback_pool *pool,
160bf215546Sopenharmony_ci                      uint32_t size,
161bf215546Sopenharmony_ci                      const VkAllocationCallbacks *alloc)
162bf215546Sopenharmony_ci{
163bf215546Sopenharmony_ci   simple_mtx_init(&pool->mutex, mtx_plain);
164bf215546Sopenharmony_ci
165bf215546Sopenharmony_ci   pool->device = dev;
166bf215546Sopenharmony_ci   pool->alloc = alloc;
167bf215546Sopenharmony_ci   pool->size = size;
168bf215546Sopenharmony_ci   pool->used = size;
169bf215546Sopenharmony_ci   list_inithead(&pool->feedback_buffers);
170bf215546Sopenharmony_ci   list_inithead(&pool->free_slots);
171bf215546Sopenharmony_ci
172bf215546Sopenharmony_ci   /* no lock needed upon init */
173bf215546Sopenharmony_ci   return vn_feedback_pool_grow_locked(pool);
174bf215546Sopenharmony_ci}
175bf215546Sopenharmony_ci
176bf215546Sopenharmony_civoid
177bf215546Sopenharmony_civn_feedback_pool_fini(struct vn_feedback_pool *pool)
178bf215546Sopenharmony_ci{
179bf215546Sopenharmony_ci   list_for_each_entry_safe(struct vn_feedback_slot, slot, &pool->free_slots,
180bf215546Sopenharmony_ci                            head)
181bf215546Sopenharmony_ci      vk_free(pool->alloc, slot);
182bf215546Sopenharmony_ci
183bf215546Sopenharmony_ci   list_for_each_entry_safe(struct vn_feedback_buffer, feedback_buf,
184bf215546Sopenharmony_ci                            &pool->feedback_buffers, head)
185bf215546Sopenharmony_ci      vn_feedback_buffer_destroy(pool->device, feedback_buf, pool->alloc);
186bf215546Sopenharmony_ci
187bf215546Sopenharmony_ci   simple_mtx_destroy(&pool->mutex);
188bf215546Sopenharmony_ci}
189bf215546Sopenharmony_ci
190bf215546Sopenharmony_cistatic struct vn_feedback_buffer *
191bf215546Sopenharmony_civn_feedback_pool_alloc_locked(struct vn_feedback_pool *pool,
192bf215546Sopenharmony_ci                              uint32_t size,
193bf215546Sopenharmony_ci                              uint32_t *out_offset)
194bf215546Sopenharmony_ci{
195bf215546Sopenharmony_ci   VN_TRACE_FUNC();
196bf215546Sopenharmony_ci   const uint32_t aligned_size = align(size, 4);
197bf215546Sopenharmony_ci
198bf215546Sopenharmony_ci   if (unlikely(aligned_size > pool->size - pool->used)) {
199bf215546Sopenharmony_ci      VkResult result = vn_feedback_pool_grow_locked(pool);
200bf215546Sopenharmony_ci      if (result != VK_SUCCESS)
201bf215546Sopenharmony_ci         return NULL;
202bf215546Sopenharmony_ci
203bf215546Sopenharmony_ci      assert(aligned_size <= pool->size - pool->used);
204bf215546Sopenharmony_ci   }
205bf215546Sopenharmony_ci
206bf215546Sopenharmony_ci   *out_offset = pool->used;
207bf215546Sopenharmony_ci   pool->used += aligned_size;
208bf215546Sopenharmony_ci
209bf215546Sopenharmony_ci   return list_first_entry(&pool->feedback_buffers, struct vn_feedback_buffer,
210bf215546Sopenharmony_ci                           head);
211bf215546Sopenharmony_ci}
212bf215546Sopenharmony_ci
213bf215546Sopenharmony_cistruct vn_feedback_slot *
214bf215546Sopenharmony_civn_feedback_pool_alloc(struct vn_feedback_pool *pool,
215bf215546Sopenharmony_ci                       enum vn_feedback_type type)
216bf215546Sopenharmony_ci{
217bf215546Sopenharmony_ci   /* TODO Make slot size variable for VkQueryPool feedback. Currently it's
218bf215546Sopenharmony_ci    * MAX2(sizeof(VkResult), sizeof(uint64_t)).
219bf215546Sopenharmony_ci    */
220bf215546Sopenharmony_ci   static const uint32_t slot_size = 8;
221bf215546Sopenharmony_ci   struct vn_feedback_buffer *feedback_buf;
222bf215546Sopenharmony_ci   uint32_t offset;
223bf215546Sopenharmony_ci   struct vn_feedback_slot *slot;
224bf215546Sopenharmony_ci
225bf215546Sopenharmony_ci   simple_mtx_lock(&pool->mutex);
226bf215546Sopenharmony_ci   if (!list_is_empty(&pool->free_slots)) {
227bf215546Sopenharmony_ci      slot =
228bf215546Sopenharmony_ci         list_first_entry(&pool->free_slots, struct vn_feedback_slot, head);
229bf215546Sopenharmony_ci      list_del(&slot->head);
230bf215546Sopenharmony_ci      simple_mtx_unlock(&pool->mutex);
231bf215546Sopenharmony_ci
232bf215546Sopenharmony_ci      slot->type = type;
233bf215546Sopenharmony_ci      return slot;
234bf215546Sopenharmony_ci   }
235bf215546Sopenharmony_ci
236bf215546Sopenharmony_ci   slot = vk_alloc(pool->alloc, sizeof(*slot), VN_DEFAULT_ALIGN,
237bf215546Sopenharmony_ci                   VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
238bf215546Sopenharmony_ci   if (!slot) {
239bf215546Sopenharmony_ci      simple_mtx_unlock(&pool->mutex);
240bf215546Sopenharmony_ci      return NULL;
241bf215546Sopenharmony_ci   }
242bf215546Sopenharmony_ci
243bf215546Sopenharmony_ci   feedback_buf = vn_feedback_pool_alloc_locked(pool, slot_size, &offset);
244bf215546Sopenharmony_ci   simple_mtx_unlock(&pool->mutex);
245bf215546Sopenharmony_ci
246bf215546Sopenharmony_ci   if (!feedback_buf) {
247bf215546Sopenharmony_ci      vk_free(pool->alloc, slot);
248bf215546Sopenharmony_ci      return NULL;
249bf215546Sopenharmony_ci   }
250bf215546Sopenharmony_ci
251bf215546Sopenharmony_ci   slot->type = type;
252bf215546Sopenharmony_ci   slot->offset = offset;
253bf215546Sopenharmony_ci   slot->buffer = feedback_buf->buffer;
254bf215546Sopenharmony_ci   slot->data = feedback_buf->data + offset;
255bf215546Sopenharmony_ci
256bf215546Sopenharmony_ci   return slot;
257bf215546Sopenharmony_ci}
258bf215546Sopenharmony_ci
259bf215546Sopenharmony_civoid
260bf215546Sopenharmony_civn_feedback_pool_free(struct vn_feedback_pool *pool,
261bf215546Sopenharmony_ci                      struct vn_feedback_slot *slot)
262bf215546Sopenharmony_ci{
263bf215546Sopenharmony_ci   simple_mtx_lock(&pool->mutex);
264bf215546Sopenharmony_ci   list_add(&slot->head, &pool->free_slots);
265bf215546Sopenharmony_ci   simple_mtx_unlock(&pool->mutex);
266bf215546Sopenharmony_ci}
267bf215546Sopenharmony_ci
268bf215546Sopenharmony_civoid
269bf215546Sopenharmony_civn_feedback_event_cmd_record(VkCommandBuffer cmd_handle,
270bf215546Sopenharmony_ci                             VkEvent ev_handle,
271bf215546Sopenharmony_ci                             VkPipelineStageFlags stage_mask,
272bf215546Sopenharmony_ci                             VkResult status)
273bf215546Sopenharmony_ci{
274bf215546Sopenharmony_ci   /* For vkCmdSetEvent and vkCmdResetEvent feedback interception.
275bf215546Sopenharmony_ci    *
276bf215546Sopenharmony_ci    * The injection point is after the event call to avoid introducing
277bf215546Sopenharmony_ci    * unexpected src stage waiting for VK_PIPELINE_STAGE_HOST_BIT and
278bf215546Sopenharmony_ci    * VK_PIPELINE_STAGE_TRANSFER_BIT if they are not already being waited by
279bf215546Sopenharmony_ci    * vkCmdSetEvent or vkCmdResetEvent. On the other hand, the delay in the
280bf215546Sopenharmony_ci    * feedback signal is acceptable for the nature of VkEvent, and the event
281bf215546Sopenharmony_ci    * feedback cmds lifecycle is guarded by the intercepted command buffer.
282bf215546Sopenharmony_ci    */
283bf215546Sopenharmony_ci   struct vn_event *ev = vn_event_from_handle(ev_handle);
284bf215546Sopenharmony_ci   struct vn_feedback_slot *slot = ev->feedback_slot;
285bf215546Sopenharmony_ci
286bf215546Sopenharmony_ci   if (!slot)
287bf215546Sopenharmony_ci      return;
288bf215546Sopenharmony_ci
289bf215546Sopenharmony_ci   STATIC_ASSERT(sizeof(*slot->status) == 4);
290bf215546Sopenharmony_ci
291bf215546Sopenharmony_ci   const VkBufferMemoryBarrier buf_barrier_before = {
292bf215546Sopenharmony_ci      .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
293bf215546Sopenharmony_ci      .pNext = NULL,
294bf215546Sopenharmony_ci      .srcAccessMask =
295bf215546Sopenharmony_ci         VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
296bf215546Sopenharmony_ci      .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
297bf215546Sopenharmony_ci      .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
298bf215546Sopenharmony_ci      .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
299bf215546Sopenharmony_ci      .buffer = slot->buffer,
300bf215546Sopenharmony_ci      .offset = slot->offset,
301bf215546Sopenharmony_ci      .size = 4,
302bf215546Sopenharmony_ci   };
303bf215546Sopenharmony_ci   vn_CmdPipelineBarrier(cmd_handle,
304bf215546Sopenharmony_ci                         stage_mask | VK_PIPELINE_STAGE_HOST_BIT |
305bf215546Sopenharmony_ci                            VK_PIPELINE_STAGE_TRANSFER_BIT,
306bf215546Sopenharmony_ci                         VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 1,
307bf215546Sopenharmony_ci                         &buf_barrier_before, 0, NULL);
308bf215546Sopenharmony_ci   vn_CmdFillBuffer(cmd_handle, slot->buffer, slot->offset, 4, status);
309bf215546Sopenharmony_ci
310bf215546Sopenharmony_ci   const VkBufferMemoryBarrier buf_barrier_after = {
311bf215546Sopenharmony_ci      .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
312bf215546Sopenharmony_ci      .pNext = NULL,
313bf215546Sopenharmony_ci      .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
314bf215546Sopenharmony_ci      .dstAccessMask = VK_ACCESS_HOST_READ_BIT | VK_ACCESS_HOST_WRITE_BIT,
315bf215546Sopenharmony_ci      .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
316bf215546Sopenharmony_ci      .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
317bf215546Sopenharmony_ci      .buffer = slot->buffer,
318bf215546Sopenharmony_ci      .offset = slot->offset,
319bf215546Sopenharmony_ci      .size = 4,
320bf215546Sopenharmony_ci   };
321bf215546Sopenharmony_ci   vn_CmdPipelineBarrier(cmd_handle, VK_PIPELINE_STAGE_TRANSFER_BIT,
322bf215546Sopenharmony_ci                         VK_PIPELINE_STAGE_HOST_BIT, 0, 0, NULL, 1,
323bf215546Sopenharmony_ci                         &buf_barrier_after, 0, NULL);
324bf215546Sopenharmony_ci}
325bf215546Sopenharmony_ci
326bf215546Sopenharmony_cistatic VkResult
327bf215546Sopenharmony_civn_feedback_fence_cmd_record(VkCommandBuffer cmd_handle,
328bf215546Sopenharmony_ci                             struct vn_feedback_slot *slot)
329bf215546Sopenharmony_ci
330bf215546Sopenharmony_ci{
331bf215546Sopenharmony_ci   STATIC_ASSERT(sizeof(*slot->status) == 4);
332bf215546Sopenharmony_ci
333bf215546Sopenharmony_ci   static const VkCommandBufferBeginInfo begin_info = {
334bf215546Sopenharmony_ci      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
335bf215546Sopenharmony_ci      .pNext = NULL,
336bf215546Sopenharmony_ci      .flags = 0,
337bf215546Sopenharmony_ci      .pInheritanceInfo = NULL,
338bf215546Sopenharmony_ci   };
339bf215546Sopenharmony_ci   VkResult result = vn_BeginCommandBuffer(cmd_handle, &begin_info);
340bf215546Sopenharmony_ci   if (result != VK_SUCCESS)
341bf215546Sopenharmony_ci      return result;
342bf215546Sopenharmony_ci
343bf215546Sopenharmony_ci   static const VkMemoryBarrier mem_barrier_before = {
344bf215546Sopenharmony_ci      .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
345bf215546Sopenharmony_ci      .pNext = NULL,
346bf215546Sopenharmony_ci      /* make pending writes available to stay close to fence signal op */
347bf215546Sopenharmony_ci      .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
348bf215546Sopenharmony_ci      /* no need to make all memory visible for feedback update */
349bf215546Sopenharmony_ci      .dstAccessMask = 0,
350bf215546Sopenharmony_ci   };
351bf215546Sopenharmony_ci   const VkBufferMemoryBarrier buf_barrier_before = {
352bf215546Sopenharmony_ci      .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
353bf215546Sopenharmony_ci      .pNext = NULL,
354bf215546Sopenharmony_ci      /* slot memory has been made available via mem_barrier_before */
355bf215546Sopenharmony_ci      .srcAccessMask = 0,
356bf215546Sopenharmony_ci      .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
357bf215546Sopenharmony_ci      .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
358bf215546Sopenharmony_ci      .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
359bf215546Sopenharmony_ci      .buffer = slot->buffer,
360bf215546Sopenharmony_ci      .offset = slot->offset,
361bf215546Sopenharmony_ci      .size = 4,
362bf215546Sopenharmony_ci   };
363bf215546Sopenharmony_ci   vn_CmdPipelineBarrier(cmd_handle, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
364bf215546Sopenharmony_ci                         VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1,
365bf215546Sopenharmony_ci                         &mem_barrier_before, 1, &buf_barrier_before, 0,
366bf215546Sopenharmony_ci                         NULL);
367bf215546Sopenharmony_ci   vn_CmdFillBuffer(cmd_handle, slot->buffer, slot->offset, 4, VK_SUCCESS);
368bf215546Sopenharmony_ci
369bf215546Sopenharmony_ci   const VkBufferMemoryBarrier buf_barrier_after = {
370bf215546Sopenharmony_ci      .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
371bf215546Sopenharmony_ci      .pNext = NULL,
372bf215546Sopenharmony_ci      .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
373bf215546Sopenharmony_ci      .dstAccessMask = VK_ACCESS_HOST_READ_BIT | VK_ACCESS_HOST_WRITE_BIT,
374bf215546Sopenharmony_ci      .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
375bf215546Sopenharmony_ci      .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
376bf215546Sopenharmony_ci      .buffer = slot->buffer,
377bf215546Sopenharmony_ci      .offset = slot->offset,
378bf215546Sopenharmony_ci      .size = 4,
379bf215546Sopenharmony_ci   };
380bf215546Sopenharmony_ci   vn_CmdPipelineBarrier(cmd_handle, VK_PIPELINE_STAGE_TRANSFER_BIT,
381bf215546Sopenharmony_ci                         VK_PIPELINE_STAGE_HOST_BIT, 0, 0, NULL, 1,
382bf215546Sopenharmony_ci                         &buf_barrier_after, 0, NULL);
383bf215546Sopenharmony_ci
384bf215546Sopenharmony_ci   return vn_EndCommandBuffer(cmd_handle);
385bf215546Sopenharmony_ci}
386bf215546Sopenharmony_ci
387bf215546Sopenharmony_ciVkResult
388bf215546Sopenharmony_civn_feedback_fence_cmd_alloc(VkDevice dev_handle,
389bf215546Sopenharmony_ci                            struct vn_feedback_cmd_pool *pool,
390bf215546Sopenharmony_ci                            struct vn_feedback_slot *slot,
391bf215546Sopenharmony_ci                            VkCommandBuffer *out_cmd_handle)
392bf215546Sopenharmony_ci{
393bf215546Sopenharmony_ci   const VkCommandBufferAllocateInfo info = {
394bf215546Sopenharmony_ci      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
395bf215546Sopenharmony_ci      .pNext = NULL,
396bf215546Sopenharmony_ci      .commandPool = pool->pool,
397bf215546Sopenharmony_ci      .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
398bf215546Sopenharmony_ci      .commandBufferCount = 1,
399bf215546Sopenharmony_ci   };
400bf215546Sopenharmony_ci   VkCommandBuffer cmd_handle;
401bf215546Sopenharmony_ci   VkResult result;
402bf215546Sopenharmony_ci
403bf215546Sopenharmony_ci   simple_mtx_lock(&pool->mutex);
404bf215546Sopenharmony_ci   result = vn_AllocateCommandBuffers(dev_handle, &info, &cmd_handle);
405bf215546Sopenharmony_ci   if (result != VK_SUCCESS)
406bf215546Sopenharmony_ci      goto out_unlock;
407bf215546Sopenharmony_ci
408bf215546Sopenharmony_ci   result = vn_feedback_fence_cmd_record(cmd_handle, slot);
409bf215546Sopenharmony_ci   if (result != VK_SUCCESS) {
410bf215546Sopenharmony_ci      vn_FreeCommandBuffers(dev_handle, pool->pool, 1, &cmd_handle);
411bf215546Sopenharmony_ci      goto out_unlock;
412bf215546Sopenharmony_ci   }
413bf215546Sopenharmony_ci
414bf215546Sopenharmony_ci   *out_cmd_handle = cmd_handle;
415bf215546Sopenharmony_ci
416bf215546Sopenharmony_ciout_unlock:
417bf215546Sopenharmony_ci   simple_mtx_unlock(&pool->mutex);
418bf215546Sopenharmony_ci
419bf215546Sopenharmony_ci   return result;
420bf215546Sopenharmony_ci}
421bf215546Sopenharmony_ci
422bf215546Sopenharmony_civoid
423bf215546Sopenharmony_civn_feedback_fence_cmd_free(VkDevice dev_handle,
424bf215546Sopenharmony_ci                           struct vn_feedback_cmd_pool *pool,
425bf215546Sopenharmony_ci                           VkCommandBuffer cmd_handle)
426bf215546Sopenharmony_ci{
427bf215546Sopenharmony_ci   simple_mtx_lock(&pool->mutex);
428bf215546Sopenharmony_ci   vn_FreeCommandBuffers(dev_handle, pool->pool, 1, &cmd_handle);
429bf215546Sopenharmony_ci   simple_mtx_unlock(&pool->mutex);
430bf215546Sopenharmony_ci}
431bf215546Sopenharmony_ci
432bf215546Sopenharmony_ciVkResult
433bf215546Sopenharmony_civn_feedback_cmd_pools_init(struct vn_device *dev)
434bf215546Sopenharmony_ci{
435bf215546Sopenharmony_ci   const VkAllocationCallbacks *alloc = &dev->base.base.alloc;
436bf215546Sopenharmony_ci   VkDevice dev_handle = vn_device_to_handle(dev);
437bf215546Sopenharmony_ci   struct vn_feedback_cmd_pool *pools;
438bf215546Sopenharmony_ci   VkCommandPoolCreateInfo info = {
439bf215546Sopenharmony_ci      .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
440bf215546Sopenharmony_ci      .pNext = NULL,
441bf215546Sopenharmony_ci      .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
442bf215546Sopenharmony_ci   };
443bf215546Sopenharmony_ci
444bf215546Sopenharmony_ci   /* TODO will also condition on timeline semaphore feedback */
445bf215546Sopenharmony_ci   if (VN_PERF(NO_FENCE_FEEDBACK))
446bf215546Sopenharmony_ci      return VK_SUCCESS;
447bf215546Sopenharmony_ci
448bf215546Sopenharmony_ci   assert(dev->queue_family_count);
449bf215546Sopenharmony_ci
450bf215546Sopenharmony_ci   pools = vk_zalloc(alloc, sizeof(*pools) * dev->queue_family_count,
451bf215546Sopenharmony_ci                     VN_DEFAULT_ALIGN, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
452bf215546Sopenharmony_ci   if (!pools)
453bf215546Sopenharmony_ci      return VK_ERROR_OUT_OF_HOST_MEMORY;
454bf215546Sopenharmony_ci
455bf215546Sopenharmony_ci   for (uint32_t i = 0; i < dev->queue_family_count; i++) {
456bf215546Sopenharmony_ci      VkResult result;
457bf215546Sopenharmony_ci
458bf215546Sopenharmony_ci      info.queueFamilyIndex = dev->queue_families[i];
459bf215546Sopenharmony_ci      result = vn_CreateCommandPool(dev_handle, &info, alloc, &pools[i].pool);
460bf215546Sopenharmony_ci      if (result != VK_SUCCESS) {
461bf215546Sopenharmony_ci         for (uint32_t j = 0; j < i; j++) {
462bf215546Sopenharmony_ci            vn_DestroyCommandPool(dev_handle, pools[j].pool, alloc);
463bf215546Sopenharmony_ci            simple_mtx_destroy(&pools[j].mutex);
464bf215546Sopenharmony_ci         }
465bf215546Sopenharmony_ci
466bf215546Sopenharmony_ci         vk_free(alloc, pools);
467bf215546Sopenharmony_ci         return result;
468bf215546Sopenharmony_ci      }
469bf215546Sopenharmony_ci
470bf215546Sopenharmony_ci      simple_mtx_init(&pools[i].mutex, mtx_plain);
471bf215546Sopenharmony_ci   }
472bf215546Sopenharmony_ci
473bf215546Sopenharmony_ci   dev->cmd_pools = pools;
474bf215546Sopenharmony_ci
475bf215546Sopenharmony_ci   return VK_SUCCESS;
476bf215546Sopenharmony_ci}
477bf215546Sopenharmony_ci
478bf215546Sopenharmony_civoid
479bf215546Sopenharmony_civn_feedback_cmd_pools_fini(struct vn_device *dev)
480bf215546Sopenharmony_ci{
481bf215546Sopenharmony_ci   const VkAllocationCallbacks *alloc = &dev->base.base.alloc;
482bf215546Sopenharmony_ci   VkDevice dev_handle = vn_device_to_handle(dev);
483bf215546Sopenharmony_ci
484bf215546Sopenharmony_ci   if (!dev->cmd_pools)
485bf215546Sopenharmony_ci      return;
486bf215546Sopenharmony_ci
487bf215546Sopenharmony_ci   for (uint32_t i = 0; i < dev->queue_family_count; i++) {
488bf215546Sopenharmony_ci      vn_DestroyCommandPool(dev_handle, dev->cmd_pools[i].pool, alloc);
489bf215546Sopenharmony_ci      simple_mtx_destroy(&dev->cmd_pools[i].mutex);
490bf215546Sopenharmony_ci   }
491bf215546Sopenharmony_ci
492bf215546Sopenharmony_ci   vk_free(alloc, dev->cmd_pools);
493bf215546Sopenharmony_ci}
494