nvk/heap: Upload shaders on the CPU when we have a map

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26622>
This commit is contained in:
Faith Ekstrand
2024-02-16 15:58:49 -06:00
parent d4623da9a3
commit 9ed7f8c54e
+34 -25
View File
@@ -168,8 +168,10 @@ nvk_heap_alloc_locked(struct nvk_device *dev, struct nvk_heap *heap,
*addr_out = heap->bos[bo_idx].addr + bo_offset;
if (map_out != NULL) {
assert(heap->bos[bo_idx].map != NULL);
*map_out = (char *)heap->bos[bo_idx].map + bo_offset;
if (heap->bos[bo_idx].map != NULL)
*map_out = (char *)heap->bos[bo_idx].map + bo_offset;
else
*map_out = NULL;
}
return VK_SUCCESS;
@@ -223,35 +225,42 @@ nvk_heap_upload(struct nvk_device *dev, struct nvk_heap *heap,
uint64_t *addr_out)
{
simple_mtx_lock(&heap->mutex);
void *map = NULL;
VkResult result = nvk_heap_alloc_locked(dev, heap, size, alignment,
addr_out, NULL /* map */);
addr_out, &map);
simple_mtx_unlock(&heap->mutex);
if (result != VK_SUCCESS)
return result;
/* Now, kick off an upload of the shader data.
*
* This is a queued operation that the driver ensures happens before any
* more client work via semaphores. Because this is asynchronous and heap
* allocations are synchronous we have to be a bit careful here. The heap
* only ever tracks the current known CPU state of everything while the
* upload queue makes that state valid at some point in the future.
*
* This can be especially tricky for very fast upload/free cycles such as
* if the client compiles a shader, throws it away without using it, and
* then compiles another shader that ends up at the same address. What
* makes this all correct is the fact that the everything on the upload
* queue happens in a well-defined device-wide order. In this case the
* first shader will get uploaded and then the second will get uploaded
* over top of it. As long as we don't free the memory out from under the
* upload queue, everything will end up in the correct state by the time
* the client's shaders actually execute.
*/
result = nvk_upload_queue_upload(dev, &dev->upload, *addr_out, data, size);
if (result != VK_SUCCESS) {
nvk_heap_free(dev, heap, *addr_out, size);
return result;
if (map != NULL && (heap->map_flags & NOUVEAU_WS_BO_WR)) {
/* If we have a map, copy directly with memcpy */
memcpy(map, data, size);
} else {
/* Otherwise, kick off an upload with the upload queue.
*
* This is a queued operation that the driver ensures happens before any
* more client work via semaphores. Because this is asynchronous and
* heap allocations are synchronous we have to be a bit careful here.
* The heap only ever tracks the current known CPU state of everything
* while the upload queue makes that state valid at some point in the
* future.
*
* This can be especially tricky for very fast upload/free cycles such
* as if the client compiles a shader, throws it away without using it,
* and then compiles another shader that ends up at the same address.
* What makes this all correct is the fact that the everything on the
* upload queue happens in a well-defined device-wide order. In this
* case the first shader will get uploaded and then the second will get
* uploaded over top of it. As long as we don't free the memory out
* from under the upload queue, everything will end up in the correct
* state by the time the client's shaders actually execute.
*/
result = nvk_upload_queue_upload(dev, &dev->upload, *addr_out, data, size);
if (result != VK_SUCCESS) {
nvk_heap_free(dev, heap, *addr_out, size);
return result;
}
}
return VK_SUCCESS;