nvk: Use nvkmd_ctx for queue submit

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/30033>
This commit is contained in:
Faith Ekstrand 2024-07-05 13:20:20 -05:00 committed by Marge Bot
parent 996b152375
commit 8a516394f9
4 changed files with 183 additions and 408 deletions

View file

@ -48,7 +48,6 @@ nvk_files = files(
'nvk_query_pool.h',
'nvk_queue.c',
'nvk_queue.h',
'nvk_queue_drm_nouveau.c',
'nvk_sampler.c',
'nvk_sampler.h',
'nvk_shader.c',

View file

@ -11,10 +11,6 @@
#include "nvk_physical_device.h"
#include "nv_push.h"
#include "nouveau_context.h"
#include <xf86drm.h>
#include "nv_push_cl9039.h"
#include "nv_push_cl9097.h"
#include "nv_push_cl90b5.h"
@ -282,38 +278,87 @@ nvk_queue_submit_bind(struct nvk_queue *queue,
}
static VkResult
nvk_queue_submit(struct vk_queue *vk_queue,
struct vk_queue_submit *submit)
nvk_queue_submit_exec(struct nvk_queue *queue,
struct vk_queue_submit *submit)
{
struct nvk_queue *queue = container_of(vk_queue, struct nvk_queue, vk);
struct nvk_device *dev = nvk_queue_device(queue);
struct nvk_physical_device *pdev = nvk_device_physical(dev);
VkResult result;
if (vk_queue_is_lost(&queue->vk))
return VK_ERROR_DEVICE_LOST;
if (submit->buffer_bind_count > 0 ||
submit->image_bind_count > 0 ||
submit->image_opaque_bind_count > 0) {
assert(submit->command_buffer_count == 0);
result = nvk_queue_submit_bind(queue, submit);
if (result != VK_SUCCESS)
return vk_queue_set_lost(&queue->vk, "Bind operation failed");
return VK_SUCCESS;
}
result = nvk_queue_state_update(dev, &queue->state);
if (result != VK_SUCCESS) {
return vk_queue_set_lost(&queue->vk, "Failed to update queue base "
"pointers pushbuf");
}
const bool sync = pdev->debug_flags & NVK_DEBUG_PUSH_SYNC;
result = nvk_queue_submit_drm_nouveau(queue, submit, sync);
uint64_t upload_time_point;
result = nvk_upload_queue_flush(dev, &dev->upload, &upload_time_point);
if (result != VK_SUCCESS)
return result;
if (upload_time_point > 0) {
struct vk_sync_wait wait = {
.sync = dev->upload.sync,
.stage_mask = ~0,
.wait_value = upload_time_point,
};
result = nvkmd_ctx_wait(queue->exec_ctx, &queue->vk.base, 1, &wait);
if (result != VK_SUCCESS)
goto fail;
}
result = nvkmd_ctx_wait(queue->exec_ctx, &queue->vk.base,
submit->wait_count, submit->waits);
if (result != VK_SUCCESS)
goto fail;
if (queue->state.push.mem != NULL) {
struct nvkmd_ctx_exec exec = {
.addr = queue->state.push.mem->va->addr,
.size_B = queue->state.push.dw_count * 4,
};
result = nvkmd_ctx_exec(queue->exec_ctx, &queue->vk.base, 1, &exec);
if (result != VK_SUCCESS)
goto fail;
}
for (unsigned i = 0; i < submit->command_buffer_count; i++) {
struct nvk_cmd_buffer *cmd =
container_of(submit->command_buffers[i], struct nvk_cmd_buffer, vk);
const uint32_t max_execs =
util_dynarray_num_elements(&cmd->pushes, struct nvk_cmd_push);
STACK_ARRAY(struct nvkmd_ctx_exec, execs, max_execs);
uint32_t exec_count = 0;
util_dynarray_foreach(&cmd->pushes, struct nvk_cmd_push, push) {
if (push->range == 0)
continue;
execs[exec_count++] = (struct nvkmd_ctx_exec) {
.addr = push->addr,
.size_B = push->range,
.no_prefetch = push->no_prefetch,
};
}
result = nvkmd_ctx_exec(queue->exec_ctx, &queue->vk.base,
exec_count, execs);
STACK_ARRAY_FINISH(execs);
if (result != VK_SUCCESS)
goto fail;
}
result = nvkmd_ctx_signal(queue->exec_ctx, &queue->vk.base,
submit->signal_count, submit->signals);
if (result != VK_SUCCESS)
goto fail;
if (sync) {
result = nvkmd_ctx_sync(queue->exec_ctx, &queue->vk.base);
if (result != VK_SUCCESS)
goto fail;
}
fail:
if ((sync && result != VK_SUCCESS) ||
(pdev->debug_flags & NVK_DEBUG_PUSH_DUMP)) {
nvk_queue_state_dump_push(dev, &queue->state, stderr);
@ -326,6 +371,83 @@ nvk_queue_submit(struct vk_queue *vk_queue,
}
}
return result;
}
static VkResult
nvk_queue_submit(struct vk_queue *vk_queue,
struct vk_queue_submit *submit)
{
struct nvk_queue *queue = container_of(vk_queue, struct nvk_queue, vk);
struct nvk_device *dev = nvk_queue_device(queue);
VkResult result;
if (vk_queue_is_lost(&queue->vk))
return VK_ERROR_DEVICE_LOST;
if (submit->buffer_bind_count > 0 ||
submit->image_bind_count > 0 ||
submit->image_opaque_bind_count > 0) {
assert(submit->command_buffer_count == 0);
result = nvk_queue_submit_bind(queue, submit);
if (result != VK_SUCCESS)
return vk_queue_set_lost(&queue->vk, "Bind operation failed");
} else {
result = nvk_queue_state_update(dev, &queue->state);
if (result != VK_SUCCESS) {
return vk_queue_set_lost(&queue->vk, "Failed to update queue base "
"pointers pushbuf");
}
result = nvk_queue_submit_exec(queue, submit);
if (result != VK_SUCCESS)
return vk_queue_set_lost(&queue->vk, "Submit failed");
}
return VK_SUCCESS;
}
static VkResult
nvk_queue_submit_simple(struct nvk_queue *queue,
uint32_t dw_count, const uint32_t *dw)
{
struct nvk_device *dev = nvk_queue_device(queue);
struct nvk_physical_device *pdev = nvk_device_physical(dev);
VkResult result;
if (vk_queue_is_lost(&queue->vk))
return VK_ERROR_DEVICE_LOST;
struct nvkmd_mem *push_mem;
result = nvkmd_dev_alloc_mapped_mem(dev->nvkmd, &dev->vk.base,
dw_count * 4, 0,
NVKMD_MEM_GART | NVKMD_MEM_NO_SHARE,
NVKMD_MEM_MAP_WR, &push_mem);
if (result != VK_SUCCESS)
return result;
memcpy(push_mem->map, dw, dw_count * 4);
const struct nvkmd_ctx_exec exec = {
.addr = push_mem->va->addr,
.size_B = dw_count * 4,
};
result = nvkmd_ctx_exec(queue->exec_ctx, &queue->vk.base, 1, &exec);
if (result == VK_SUCCESS)
result = nvkmd_ctx_sync(queue->exec_ctx, &queue->vk.base);
nvkmd_mem_unref(push_mem);
const bool debug_sync = pdev->debug_flags & NVK_DEBUG_PUSH_SYNC;
if ((debug_sync && result != VK_SUCCESS) ||
(pdev->debug_flags & NVK_DEBUG_PUSH_DUMP)) {
struct nv_push push = {
.start = (uint32_t *)dw,
.end = (uint32_t *)dw + dw_count,
};
vk_push_print(stderr, &push, &pdev->info);
}
if (result != VK_SUCCESS)
return vk_queue_set_lost(&queue->vk, "Submit failed");
@ -334,7 +456,7 @@ nvk_queue_submit(struct vk_queue *vk_queue,
static VkResult
nvk_queue_init_context_state(struct nvk_queue *queue,
VkQueueFlags queue_flags)
enum nvkmd_engines engines)
{
struct nvk_device *dev = nvk_queue_device(queue);
struct nvk_physical_device *pdev = nvk_device_physical(dev);
@ -357,13 +479,13 @@ nvk_queue_init_context_state(struct nvk_queue *queue,
});
}
if (queue_flags & VK_QUEUE_GRAPHICS_BIT) {
if (engines & NVKMD_ENGINE_3D) {
result = nvk_push_draw_state_init(queue, p);
if (result != VK_SUCCESS)
return result;
}
if (queue_flags & VK_QUEUE_COMPUTE_BIT) {
if (engines & NVKMD_ENGINE_COMPUTE) {
result = nvk_push_dispatch_state_init(queue, p);
if (result != VK_SUCCESS)
return result;
@ -384,31 +506,38 @@ nvk_queue_init(struct nvk_device *dev, struct nvk_queue *queue,
const struct nvk_queue_family *queue_family =
&pdev->queue_families[pCreateInfo->queueFamilyIndex];
VkQueueFlags queue_flags = queue_family->queue_flags;
/* We rely on compute shaders for queries */
if (queue_family->queue_flags & VK_QUEUE_GRAPHICS_BIT)
queue_flags |= VK_QUEUE_COMPUTE_BIT;
/* We currently rely on 3D engine MMEs for indirect dispatch */
if (queue_family->queue_flags & VK_QUEUE_COMPUTE_BIT)
queue_flags |= VK_QUEUE_GRAPHICS_BIT;
result = vk_queue_init(&queue->vk, &dev->vk, pCreateInfo, index_in_family);
if (result != VK_SUCCESS)
return result;
queue->vk.driver_submit = nvk_queue_submit;
nvk_queue_state_init(&queue->state);
if (queue_flags & VK_QUEUE_GRAPHICS_BIT) {
enum nvkmd_engines engines = 0;
if (queue_family->queue_flags & VK_QUEUE_GRAPHICS_BIT) {
engines |= NVKMD_ENGINE_3D;
/* We rely on compute shaders for queries */
engines |= NVKMD_ENGINE_COMPUTE;
}
if (queue_family->queue_flags & VK_QUEUE_COMPUTE_BIT) {
engines |= NVKMD_ENGINE_COMPUTE;
/* We currently rely on 3D engine MMEs for indirect dispatch */
engines |= NVKMD_ENGINE_3D;
}
if (queue_family->queue_flags & VK_QUEUE_TRANSFER_BIT)
engines |= NVKMD_ENGINE_COPY;
if (engines) {
result = nvkmd_dev_create_ctx(dev->nvkmd, &dev->vk.base,
engines, &queue->exec_ctx);
if (result != VK_SUCCESS)
goto fail_init;
result = nvkmd_dev_alloc_mem(dev->nvkmd, &dev->vk.base,
4096, 0,
NVKMD_MEM_LOCAL | NVKMD_MEM_NO_SHARE,
&queue->draw_cb0);
if (result != VK_SUCCESS)
goto fail_state;
goto fail_exec_ctx;
result = nvk_upload_queue_fill(dev, &dev->upload,
queue->draw_cb0->va->addr, 0,
@ -424,25 +553,24 @@ nvk_queue_init(struct nvk_device *dev, struct nvk_queue *queue,
goto fail_draw_cb0;
}
result = nvk_queue_init_drm_nouveau(dev, queue, queue_flags);
result = nvk_queue_init_context_state(queue, engines);
if (result != VK_SUCCESS)
goto fail_bind_ctx;
result = nvk_queue_init_context_state(queue, queue_flags);
if (result != VK_SUCCESS)
goto fail_drm;
queue->vk.driver_submit = nvk_queue_submit;
return VK_SUCCESS;
fail_drm:
nvk_queue_finish_drm_nouveau(dev, queue);
fail_bind_ctx:
if (queue->bind_ctx != NULL)
nvkmd_ctx_destroy(queue->bind_ctx);
fail_draw_cb0:
if (queue->draw_cb0 != NULL)
nvkmd_mem_unref(queue->draw_cb0);
fail_state:
fail_exec_ctx:
if (queue->exec_ctx != NULL)
nvkmd_ctx_destroy(queue->exec_ctx);
fail_init:
nvk_queue_state_finish(dev, &queue->state);
vk_queue_finish(&queue->vk);
@ -457,52 +585,9 @@ nvk_queue_finish(struct nvk_device *dev, struct nvk_queue *queue)
nvkmd_mem_unref(queue->draw_cb0);
}
nvk_queue_state_finish(dev, &queue->state);
nvk_queue_finish_drm_nouveau(dev, queue);
if (queue->bind_ctx != NULL)
nvkmd_ctx_destroy(queue->bind_ctx);
if (queue->exec_ctx != NULL)
nvkmd_ctx_destroy(queue->exec_ctx);
vk_queue_finish(&queue->vk);
}
VkResult
nvk_queue_submit_simple(struct nvk_queue *queue,
uint32_t dw_count, const uint32_t *dw)
{
struct nvk_device *dev = nvk_queue_device(queue);
struct nvk_physical_device *pdev = nvk_device_physical(dev);
struct nouveau_ws_bo *push_bo;
VkResult result;
if (vk_queue_is_lost(&queue->vk))
return VK_ERROR_DEVICE_LOST;
void *push_map;
push_bo = nouveau_ws_bo_new_mapped(dev->ws_dev, dw_count * 4, 0,
NOUVEAU_WS_BO_GART |
NOUVEAU_WS_BO_MAP |
NOUVEAU_WS_BO_NO_SHARE,
NOUVEAU_WS_BO_WR, &push_map);
if (push_bo == NULL)
return vk_error(queue, VK_ERROR_OUT_OF_DEVICE_MEMORY);
memcpy(push_map, dw, dw_count * 4);
result = nvk_queue_submit_simple_drm_nouveau(queue, dw_count, push_bo);
const bool debug_sync = pdev->debug_flags & NVK_DEBUG_PUSH_SYNC;
if ((debug_sync && result != VK_SUCCESS) ||
(pdev->debug_flags & NVK_DEBUG_PUSH_DUMP)) {
struct nv_push push = {
.start = (uint32_t *)dw,
.end = (uint32_t *)dw + dw_count,
};
vk_push_print(stderr, &push, &pdev->info);
}
nouveau_ws_bo_unmap(push_bo, push_map);
nouveau_ws_bo_destroy(push_bo);
if (result != VK_SUCCESS)
return vk_queue_set_lost(&queue->vk, "Submit failed");
return VK_SUCCESS;
}

View file

@ -46,12 +46,8 @@ VkResult nvk_queue_state_update(struct nvk_device *dev,
struct nvk_queue {
struct vk_queue vk;
struct {
struct nouveau_ws_context *ws_ctx;
uint32_t syncobj;
} drm;
struct nvkmd_ctx *bind_ctx;
struct nvkmd_ctx *exec_ctx;
struct nvk_queue_state state;
@ -77,23 +73,4 @@ VkResult nvk_push_draw_state_init(struct nvk_queue *queue,
VkResult nvk_push_dispatch_state_init(struct nvk_queue *queue,
struct nv_push *p);
/* this always syncs, so only use when that doesn't matter */
VkResult nvk_queue_submit_simple(struct nvk_queue *queue,
uint32_t dw_count, const uint32_t *dw);
VkResult nvk_queue_init_drm_nouveau(struct nvk_device *dev,
struct nvk_queue *queue,
VkQueueFlags queue_flags);
void nvk_queue_finish_drm_nouveau(struct nvk_device *dev,
struct nvk_queue *queue);
VkResult nvk_queue_submit_simple_drm_nouveau(struct nvk_queue *queue,
uint32_t push_dw_count,
struct nouveau_ws_bo *push_bo);
VkResult nvk_queue_submit_drm_nouveau(struct nvk_queue *queue,
struct vk_queue_submit *submit,
bool sync);
#endif

View file

@ -1,286 +0,0 @@
/*
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
* SPDX-License-Identifier: MIT
*/
#include "nvk_queue.h"
#include "nvk_cmd_buffer.h"
#include "nvk_cmd_pool.h"
#include "nvk_device.h"
#include "nvk_buffer.h"
#include "nvk_image.h"
#include "nvk_device_memory.h"
#include "nvk_physical_device.h"
#include "nvkmd/nvkmd.h"
#include "nvkmd/nouveau/nvkmd_nouveau.h"
#include "nouveau_context.h"
#include "drm-uapi/nouveau_drm.h"
#include "vk_drm_syncobj.h"
#include <xf86drm.h>
#define NVK_PUSH_MAX_SYNCS 256
#define NVK_PUSH_MAX_BINDS 4096
#define NVK_PUSH_MAX_PUSH 1024
struct push_builder {
uint32_t max_push;
struct drm_nouveau_sync req_wait[NVK_PUSH_MAX_SYNCS];
struct drm_nouveau_sync req_sig[NVK_PUSH_MAX_SYNCS];
struct drm_nouveau_exec_push req_push[NVK_PUSH_MAX_PUSH];
struct drm_nouveau_exec req;
};
static void
push_builder_init(struct nvk_queue *queue,
struct push_builder *pb)
{
struct nvk_device *dev = nvk_queue_device(queue);
pb->max_push = MIN2(NVK_PUSH_MAX_PUSH, dev->ws_dev->max_push);
pb->req = (struct drm_nouveau_exec) {
.channel = queue->drm.ws_ctx->channel,
.push_count = 0,
.wait_count = 0,
.sig_count = 0,
.push_ptr = (uintptr_t)&pb->req_push,
.wait_ptr = (uintptr_t)&pb->req_wait,
.sig_ptr = (uintptr_t)&pb->req_sig,
};
}
static void
push_add_syncobj_wait(struct push_builder *pb,
uint32_t syncobj,
uint64_t wait_value)
{
assert(pb->req.wait_count < NVK_PUSH_MAX_SYNCS);
pb->req_wait[pb->req.wait_count++] = (struct drm_nouveau_sync) {
.flags = wait_value ? DRM_NOUVEAU_SYNC_TIMELINE_SYNCOBJ :
DRM_NOUVEAU_SYNC_SYNCOBJ,
.handle = syncobj,
.timeline_value = wait_value,
};
}
static void
push_add_sync_wait(struct push_builder *pb,
struct vk_sync_wait *wait)
{
struct vk_drm_syncobj *sync = vk_sync_as_drm_syncobj(wait->sync);
assert(sync != NULL);
push_add_syncobj_wait(pb, sync->syncobj, wait->wait_value);
}
static void
push_add_sync_signal(struct push_builder *pb,
struct vk_sync_signal *sig)
{
struct vk_drm_syncobj *sync = vk_sync_as_drm_syncobj(sig->sync);
assert(sync);
assert(pb->req.sig_count < NVK_PUSH_MAX_SYNCS);
pb->req_sig[pb->req.sig_count++] = (struct drm_nouveau_sync) {
.flags = sig->signal_value ? DRM_NOUVEAU_SYNC_TIMELINE_SYNCOBJ :
DRM_NOUVEAU_SYNC_SYNCOBJ,
.handle = sync->syncobj,
.timeline_value = sig->signal_value,
};
}
static void
push_add_push(struct push_builder *pb, uint64_t addr, uint32_t range,
bool no_prefetch)
{
/* This is the hardware limit on all current GPUs */
assert((addr % 4) == 0 && (range % 4) == 0);
assert(range < (1u << 23));
uint32_t flags = 0;
if (no_prefetch)
flags |= DRM_NOUVEAU_EXEC_PUSH_NO_PREFETCH;
assert(pb->req.push_count < pb->max_push);
pb->req_push[pb->req.push_count++] = (struct drm_nouveau_exec_push) {
.va = addr,
.va_len = range,
.flags = flags,
};
}
static VkResult
push_submit(struct nvk_queue *queue, struct push_builder *pb, bool sync)
{
struct nvk_device *dev = nvk_queue_device(queue);
int err;
if (sync) {
assert(pb->req.sig_count < NVK_PUSH_MAX_SYNCS);
pb->req_sig[pb->req.sig_count++] = (struct drm_nouveau_sync) {
.flags = DRM_NOUVEAU_SYNC_SYNCOBJ,
.handle = queue->drm.syncobj,
.timeline_value = 0,
};
}
err = drmCommandWriteRead(dev->ws_dev->fd,
DRM_NOUVEAU_EXEC,
&pb->req, sizeof(pb->req));
if (err) {
VkResult result = VK_ERROR_UNKNOWN;
if (err == -ENODEV)
result = VK_ERROR_DEVICE_LOST;
return vk_errorf(queue, result,
"DRM_NOUVEAU_EXEC failed: %m");
}
if (sync) {
err = drmSyncobjWait(dev->ws_dev->fd,
&queue->drm.syncobj, 1, INT64_MAX,
DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT,
NULL);
if (err) {
return vk_errorf(queue, VK_ERROR_UNKNOWN,
"DRM_SYNCOBJ_WAIT failed: %m");
}
/* Push an empty again, just to check for errors */
struct drm_nouveau_exec empty = {
.channel = pb->req.channel,
};
err = drmCommandWriteRead(dev->ws_dev->fd,
DRM_NOUVEAU_EXEC,
&empty, sizeof(empty));
if (err) {
return vk_errorf(queue, VK_ERROR_DEVICE_LOST,
"DRM_NOUVEAU_EXEC failed: %m");
}
}
return VK_SUCCESS;
}
VkResult
nvk_queue_init_drm_nouveau(struct nvk_device *dev,
struct nvk_queue *queue,
VkQueueFlags queue_flags)
{
VkResult result;
int err;
enum nouveau_ws_engines engines = 0;
if (queue_flags & VK_QUEUE_GRAPHICS_BIT)
engines |= NOUVEAU_WS_ENGINE_3D;
if (queue_flags & VK_QUEUE_COMPUTE_BIT)
engines |= NOUVEAU_WS_ENGINE_COMPUTE;
if (queue_flags & VK_QUEUE_TRANSFER_BIT)
engines |= NOUVEAU_WS_ENGINE_COPY;
err = nouveau_ws_context_create(dev->ws_dev, engines, &queue->drm.ws_ctx);
if (err != 0) {
if (err == -ENOSPC)
return vk_error(dev, VK_ERROR_TOO_MANY_OBJECTS);
else
return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
}
err = drmSyncobjCreate(dev->ws_dev->fd, 0, &queue->drm.syncobj);
if (err < 0) {
result = vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
goto fail_context;
}
return VK_SUCCESS;
fail_context:
nouveau_ws_context_destroy(queue->drm.ws_ctx);
return result;
}
void
nvk_queue_finish_drm_nouveau(struct nvk_device *dev,
struct nvk_queue *queue)
{
ASSERTED int err = drmSyncobjDestroy(dev->ws_dev->fd, queue->drm.syncobj);
assert(err == 0);
nouveau_ws_context_destroy(queue->drm.ws_ctx);
}
VkResult
nvk_queue_submit_simple_drm_nouveau(struct nvk_queue *queue,
uint32_t push_dw_count,
struct nouveau_ws_bo *push_bo)
{
struct push_builder pb;
push_builder_init(queue, &pb);
push_add_push(&pb, push_bo->offset, push_dw_count * 4, false);
return push_submit(queue, &pb, true);
}
static void
push_add_queue_state(struct push_builder *pb, struct nvk_queue_state *qs)
{
if (qs->push.mem)
push_add_push(pb, qs->push.mem->va->addr, qs->push.dw_count * 4, false);
}
VkResult
nvk_queue_submit_drm_nouveau(struct nvk_queue *queue,
struct vk_queue_submit *submit,
bool sync)
{
struct nvk_device *dev = nvk_queue_device(queue);
struct push_builder pb;
VkResult result;
uint64_t upload_time_point;
result = nvk_upload_queue_flush(dev, &dev->upload, &upload_time_point);
if (result != VK_SUCCESS)
return result;
push_builder_init(queue, &pb);
if (upload_time_point > 0) {
push_add_sync_wait(&pb, &(struct vk_sync_wait) {
.sync = dev->upload.sync,
.stage_mask = ~0,
.wait_value = upload_time_point,
});
}
for (uint32_t i = 0; i < submit->wait_count; i++)
push_add_sync_wait(&pb, &submit->waits[i]);
push_add_queue_state(&pb, &queue->state);
assert(submit->buffer_bind_count == 0);
assert(submit->image_bind_count == 0);
assert(submit->image_opaque_bind_count == 0);
for (unsigned i = 0; i < submit->command_buffer_count; i++) {
struct nvk_cmd_buffer *cmd =
container_of(submit->command_buffers[i], struct nvk_cmd_buffer, vk);
util_dynarray_foreach(&cmd->pushes, struct nvk_cmd_push, push) {
if (push->range == 0)
continue;
if (pb.req.push_count >= pb.max_push) {
result = push_submit(queue, &pb, sync);
if (result != VK_SUCCESS)
return result;
push_builder_init(queue, &pb);
}
push_add_push(&pb, push->addr, push->range, push->no_prefetch);
}
}
for (uint32_t i = 0; i < submit->signal_count; i++)
push_add_sync_signal(&pb, &submit->signals[i]);
return push_submit(queue, &pb, sync);
}