xref: /aosp_15_r20/external/mesa3d/src/nouveau/vulkan/nvk_heap.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1*61046927SAndroid Build Coastguard Worker /*
2*61046927SAndroid Build Coastguard Worker  * Copyright © 2022 Collabora Ltd. and Red Hat Inc.
3*61046927SAndroid Build Coastguard Worker  * SPDX-License-Identifier: MIT
4*61046927SAndroid Build Coastguard Worker  */
5*61046927SAndroid Build Coastguard Worker #include "nvk_heap.h"
6*61046927SAndroid Build Coastguard Worker 
7*61046927SAndroid Build Coastguard Worker #include "nvk_device.h"
8*61046927SAndroid Build Coastguard Worker #include "nvk_physical_device.h"
9*61046927SAndroid Build Coastguard Worker #include "nvk_queue.h"
10*61046927SAndroid Build Coastguard Worker 
11*61046927SAndroid Build Coastguard Worker #include "util/macros.h"
12*61046927SAndroid Build Coastguard Worker 
13*61046927SAndroid Build Coastguard Worker #include "nv_push.h"
14*61046927SAndroid Build Coastguard Worker #include "nv_push_cl90b5.h"
15*61046927SAndroid Build Coastguard Worker 
16*61046927SAndroid Build Coastguard Worker VkResult
nvk_heap_init(struct nvk_device * dev,struct nvk_heap * heap,enum nvkmd_mem_flags mem_flags,enum nvkmd_mem_map_flags map_flags,uint32_t overalloc,bool contiguous)17*61046927SAndroid Build Coastguard Worker nvk_heap_init(struct nvk_device *dev, struct nvk_heap *heap,
18*61046927SAndroid Build Coastguard Worker               enum nvkmd_mem_flags mem_flags,
19*61046927SAndroid Build Coastguard Worker               enum nvkmd_mem_map_flags map_flags,
20*61046927SAndroid Build Coastguard Worker               uint32_t overalloc, bool contiguous)
21*61046927SAndroid Build Coastguard Worker {
22*61046927SAndroid Build Coastguard Worker    VkResult result;
23*61046927SAndroid Build Coastguard Worker 
24*61046927SAndroid Build Coastguard Worker    memset(heap, 0, sizeof(*heap));
25*61046927SAndroid Build Coastguard Worker 
26*61046927SAndroid Build Coastguard Worker    heap->mem_flags = mem_flags;
27*61046927SAndroid Build Coastguard Worker    if (map_flags)
28*61046927SAndroid Build Coastguard Worker       heap->mem_flags |= NVKMD_MEM_CAN_MAP;
29*61046927SAndroid Build Coastguard Worker    heap->map_flags = map_flags;
30*61046927SAndroid Build Coastguard Worker    heap->overalloc = overalloc;
31*61046927SAndroid Build Coastguard Worker 
32*61046927SAndroid Build Coastguard Worker    if (contiguous) {
33*61046927SAndroid Build Coastguard Worker       result = nvkmd_dev_alloc_va(dev->nvkmd, &dev->vk.base,
34*61046927SAndroid Build Coastguard Worker                                   0 /* va_flags */, 0 /* pte_kind */,
35*61046927SAndroid Build Coastguard Worker                                   NVK_HEAP_MAX_SIZE, 0 /* align_B */,
36*61046927SAndroid Build Coastguard Worker                                   0 /* fixed_addr */,
37*61046927SAndroid Build Coastguard Worker                                   &heap->contig_va);
38*61046927SAndroid Build Coastguard Worker       if (result != VK_SUCCESS)
39*61046927SAndroid Build Coastguard Worker          return result;
40*61046927SAndroid Build Coastguard Worker    }
41*61046927SAndroid Build Coastguard Worker 
42*61046927SAndroid Build Coastguard Worker    simple_mtx_init(&heap->mutex, mtx_plain);
43*61046927SAndroid Build Coastguard Worker    util_vma_heap_init(&heap->heap, 0, 0);
44*61046927SAndroid Build Coastguard Worker 
45*61046927SAndroid Build Coastguard Worker    heap->total_size = 0;
46*61046927SAndroid Build Coastguard Worker    heap->mem_count = 0;
47*61046927SAndroid Build Coastguard Worker 
48*61046927SAndroid Build Coastguard Worker    return VK_SUCCESS;
49*61046927SAndroid Build Coastguard Worker }
50*61046927SAndroid Build Coastguard Worker 
51*61046927SAndroid Build Coastguard Worker void
nvk_heap_finish(struct nvk_device * dev,struct nvk_heap * heap)52*61046927SAndroid Build Coastguard Worker nvk_heap_finish(struct nvk_device *dev, struct nvk_heap *heap)
53*61046927SAndroid Build Coastguard Worker {
54*61046927SAndroid Build Coastguard Worker    /* Freeing the VA will unbind all the memory */
55*61046927SAndroid Build Coastguard Worker    if (heap->contig_va)
56*61046927SAndroid Build Coastguard Worker       nvkmd_va_free(heap->contig_va);
57*61046927SAndroid Build Coastguard Worker 
58*61046927SAndroid Build Coastguard Worker    for (uint32_t mem_idx = 0; mem_idx < heap->mem_count; mem_idx++)
59*61046927SAndroid Build Coastguard Worker       nvkmd_mem_unref(heap->mem[mem_idx].mem);
60*61046927SAndroid Build Coastguard Worker 
61*61046927SAndroid Build Coastguard Worker    util_vma_heap_finish(&heap->heap);
62*61046927SAndroid Build Coastguard Worker    simple_mtx_destroy(&heap->mutex);
63*61046927SAndroid Build Coastguard Worker }
64*61046927SAndroid Build Coastguard Worker 
65*61046927SAndroid Build Coastguard Worker static uint64_t
encode_vma(uint32_t mem_idx,uint64_t mem_offset)66*61046927SAndroid Build Coastguard Worker encode_vma(uint32_t mem_idx, uint64_t mem_offset)
67*61046927SAndroid Build Coastguard Worker {
68*61046927SAndroid Build Coastguard Worker    assert(mem_idx < UINT16_MAX - 1);
69*61046927SAndroid Build Coastguard Worker    assert(mem_offset < (1ull << 48));
70*61046927SAndroid Build Coastguard Worker    return ((uint64_t)(mem_idx + 1) << 48) | mem_offset;
71*61046927SAndroid Build Coastguard Worker }
72*61046927SAndroid Build Coastguard Worker 
73*61046927SAndroid Build Coastguard Worker static uint32_t
vma_mem_idx(uint64_t offset)74*61046927SAndroid Build Coastguard Worker vma_mem_idx(uint64_t offset)
75*61046927SAndroid Build Coastguard Worker {
76*61046927SAndroid Build Coastguard Worker    offset = offset >> 48;
77*61046927SAndroid Build Coastguard Worker    assert(offset > 0);
78*61046927SAndroid Build Coastguard Worker    return offset - 1;
79*61046927SAndroid Build Coastguard Worker }
80*61046927SAndroid Build Coastguard Worker 
81*61046927SAndroid Build Coastguard Worker static uint64_t
vma_mem_offset(uint64_t offset)82*61046927SAndroid Build Coastguard Worker vma_mem_offset(uint64_t offset)
83*61046927SAndroid Build Coastguard Worker {
84*61046927SAndroid Build Coastguard Worker    return offset & BITFIELD64_MASK(48);
85*61046927SAndroid Build Coastguard Worker }
86*61046927SAndroid Build Coastguard Worker 
87*61046927SAndroid Build Coastguard Worker static VkResult
nvk_heap_grow_locked(struct nvk_device * dev,struct nvk_heap * heap)88*61046927SAndroid Build Coastguard Worker nvk_heap_grow_locked(struct nvk_device *dev, struct nvk_heap *heap)
89*61046927SAndroid Build Coastguard Worker {
90*61046927SAndroid Build Coastguard Worker    VkResult result;
91*61046927SAndroid Build Coastguard Worker 
92*61046927SAndroid Build Coastguard Worker    if (heap->mem_count >= NVK_HEAP_MAX_BO_COUNT) {
93*61046927SAndroid Build Coastguard Worker       return vk_errorf(dev, VK_ERROR_OUT_OF_DEVICE_MEMORY,
94*61046927SAndroid Build Coastguard Worker                        "Heap has already hit its maximum size");
95*61046927SAndroid Build Coastguard Worker    }
96*61046927SAndroid Build Coastguard Worker 
97*61046927SAndroid Build Coastguard Worker    /* First two BOs are MIN_SIZE, double after that */
98*61046927SAndroid Build Coastguard Worker    const uint64_t new_mem_size =
99*61046927SAndroid Build Coastguard Worker       NVK_HEAP_MIN_SIZE << (MAX2(heap->mem_count, 1) - 1);
100*61046927SAndroid Build Coastguard Worker 
101*61046927SAndroid Build Coastguard Worker    struct nvkmd_mem *mem;
102*61046927SAndroid Build Coastguard Worker    if (heap->map_flags) {
103*61046927SAndroid Build Coastguard Worker       result = nvkmd_dev_alloc_mapped_mem(dev->nvkmd, &dev->vk.base,
104*61046927SAndroid Build Coastguard Worker                                           new_mem_size, 0, heap->mem_flags,
105*61046927SAndroid Build Coastguard Worker                                           heap->map_flags, &mem);
106*61046927SAndroid Build Coastguard Worker    } else {
107*61046927SAndroid Build Coastguard Worker       result = nvkmd_dev_alloc_mem(dev->nvkmd, &dev->vk.base,
108*61046927SAndroid Build Coastguard Worker                                    new_mem_size, 0, heap->mem_flags, &mem);
109*61046927SAndroid Build Coastguard Worker    }
110*61046927SAndroid Build Coastguard Worker    if (result != VK_SUCCESS)
111*61046927SAndroid Build Coastguard Worker       return result;
112*61046927SAndroid Build Coastguard Worker 
113*61046927SAndroid Build Coastguard Worker    assert(mem->size_B == new_mem_size);
114*61046927SAndroid Build Coastguard Worker 
115*61046927SAndroid Build Coastguard Worker    uint64_t addr;
116*61046927SAndroid Build Coastguard Worker    if (heap->contig_va != NULL) {
117*61046927SAndroid Build Coastguard Worker       result = nvkmd_va_bind_mem(heap->contig_va, &dev->vk.base,
118*61046927SAndroid Build Coastguard Worker                                  heap->total_size, mem, 0, new_mem_size);
119*61046927SAndroid Build Coastguard Worker       if (result != VK_SUCCESS) {
120*61046927SAndroid Build Coastguard Worker          nvkmd_mem_unref(mem);
121*61046927SAndroid Build Coastguard Worker          return result;
122*61046927SAndroid Build Coastguard Worker       }
123*61046927SAndroid Build Coastguard Worker       addr = heap->contig_va->addr + heap->total_size;
124*61046927SAndroid Build Coastguard Worker 
125*61046927SAndroid Build Coastguard Worker       /* For contiguous heaps, we can now free the padding from the previous
126*61046927SAndroid Build Coastguard Worker        * BO because the BO we just added will provide the needed padding. For
127*61046927SAndroid Build Coastguard Worker        * non-contiguous heaps, we have to leave each BO padded individually.
128*61046927SAndroid Build Coastguard Worker        */
129*61046927SAndroid Build Coastguard Worker       if (heap->mem_count > 0) {
130*61046927SAndroid Build Coastguard Worker          struct nvkmd_mem *prev_mem = heap->mem[heap->mem_count - 1].mem;
131*61046927SAndroid Build Coastguard Worker          assert(heap->overalloc < prev_mem->size_B);
132*61046927SAndroid Build Coastguard Worker          const uint64_t pad_vma =
133*61046927SAndroid Build Coastguard Worker             encode_vma(heap->mem_count - 1, prev_mem->size_B - heap->overalloc);
134*61046927SAndroid Build Coastguard Worker          util_vma_heap_free(&heap->heap, pad_vma, heap->overalloc);
135*61046927SAndroid Build Coastguard Worker       }
136*61046927SAndroid Build Coastguard Worker    } else {
137*61046927SAndroid Build Coastguard Worker       addr = mem->va->addr;
138*61046927SAndroid Build Coastguard Worker    }
139*61046927SAndroid Build Coastguard Worker 
140*61046927SAndroid Build Coastguard Worker    uint64_t vma = encode_vma(heap->mem_count, 0);
141*61046927SAndroid Build Coastguard Worker    assert(heap->overalloc < new_mem_size);
142*61046927SAndroid Build Coastguard Worker    util_vma_heap_free(&heap->heap, vma, new_mem_size - heap->overalloc);
143*61046927SAndroid Build Coastguard Worker 
144*61046927SAndroid Build Coastguard Worker    heap->mem[heap->mem_count++] = (struct nvk_heap_mem) {
145*61046927SAndroid Build Coastguard Worker       .mem = mem,
146*61046927SAndroid Build Coastguard Worker       .addr = addr,
147*61046927SAndroid Build Coastguard Worker    };
148*61046927SAndroid Build Coastguard Worker    heap->total_size += new_mem_size;
149*61046927SAndroid Build Coastguard Worker 
150*61046927SAndroid Build Coastguard Worker    return VK_SUCCESS;
151*61046927SAndroid Build Coastguard Worker }
152*61046927SAndroid Build Coastguard Worker 
153*61046927SAndroid Build Coastguard Worker static VkResult
nvk_heap_alloc_locked(struct nvk_device * dev,struct nvk_heap * heap,uint64_t size,uint32_t alignment,uint64_t * addr_out,void ** map_out)154*61046927SAndroid Build Coastguard Worker nvk_heap_alloc_locked(struct nvk_device *dev, struct nvk_heap *heap,
155*61046927SAndroid Build Coastguard Worker                       uint64_t size, uint32_t alignment,
156*61046927SAndroid Build Coastguard Worker                       uint64_t *addr_out, void **map_out)
157*61046927SAndroid Build Coastguard Worker {
158*61046927SAndroid Build Coastguard Worker    while (1) {
159*61046927SAndroid Build Coastguard Worker       uint64_t vma = util_vma_heap_alloc(&heap->heap, size, alignment);
160*61046927SAndroid Build Coastguard Worker       if (vma != 0) {
161*61046927SAndroid Build Coastguard Worker          uint32_t mem_idx = vma_mem_idx(vma);
162*61046927SAndroid Build Coastguard Worker          uint64_t mem_offset = vma_mem_offset(vma);
163*61046927SAndroid Build Coastguard Worker 
164*61046927SAndroid Build Coastguard Worker          assert(mem_idx < heap->mem_count);
165*61046927SAndroid Build Coastguard Worker          assert(heap->mem[mem_idx].mem != NULL);
166*61046927SAndroid Build Coastguard Worker          assert(mem_offset + size <= heap->mem[mem_idx].mem->size_B);
167*61046927SAndroid Build Coastguard Worker 
168*61046927SAndroid Build Coastguard Worker          *addr_out = heap->mem[mem_idx].addr + mem_offset;
169*61046927SAndroid Build Coastguard Worker          if (map_out != NULL) {
170*61046927SAndroid Build Coastguard Worker             if (heap->mem[mem_idx].mem->map != NULL)
171*61046927SAndroid Build Coastguard Worker                *map_out = (char *)heap->mem[mem_idx].mem->map + mem_offset;
172*61046927SAndroid Build Coastguard Worker             else
173*61046927SAndroid Build Coastguard Worker                *map_out = NULL;
174*61046927SAndroid Build Coastguard Worker          }
175*61046927SAndroid Build Coastguard Worker 
176*61046927SAndroid Build Coastguard Worker          return VK_SUCCESS;
177*61046927SAndroid Build Coastguard Worker       }
178*61046927SAndroid Build Coastguard Worker 
179*61046927SAndroid Build Coastguard Worker       VkResult result = nvk_heap_grow_locked(dev, heap);
180*61046927SAndroid Build Coastguard Worker       if (result != VK_SUCCESS)
181*61046927SAndroid Build Coastguard Worker          return result;
182*61046927SAndroid Build Coastguard Worker    }
183*61046927SAndroid Build Coastguard Worker }
184*61046927SAndroid Build Coastguard Worker 
185*61046927SAndroid Build Coastguard Worker static void
nvk_heap_free_locked(struct nvk_device * dev,struct nvk_heap * heap,uint64_t addr,uint64_t size)186*61046927SAndroid Build Coastguard Worker nvk_heap_free_locked(struct nvk_device *dev, struct nvk_heap *heap,
187*61046927SAndroid Build Coastguard Worker                      uint64_t addr, uint64_t size)
188*61046927SAndroid Build Coastguard Worker {
189*61046927SAndroid Build Coastguard Worker    assert(addr + size > addr);
190*61046927SAndroid Build Coastguard Worker 
191*61046927SAndroid Build Coastguard Worker    for (uint32_t mem_idx = 0; mem_idx < heap->mem_count; mem_idx++) {
192*61046927SAndroid Build Coastguard Worker       if (addr < heap->mem[mem_idx].addr)
193*61046927SAndroid Build Coastguard Worker          continue;
194*61046927SAndroid Build Coastguard Worker 
195*61046927SAndroid Build Coastguard Worker       uint64_t mem_offset = addr - heap->mem[mem_idx].addr;
196*61046927SAndroid Build Coastguard Worker       if (mem_offset >= heap->mem[mem_idx].mem->size_B)
197*61046927SAndroid Build Coastguard Worker          continue;
198*61046927SAndroid Build Coastguard Worker 
199*61046927SAndroid Build Coastguard Worker       assert(mem_offset + size <= heap->mem[mem_idx].mem->size_B);
200*61046927SAndroid Build Coastguard Worker       uint64_t vma = encode_vma(mem_idx, mem_offset);
201*61046927SAndroid Build Coastguard Worker 
202*61046927SAndroid Build Coastguard Worker       util_vma_heap_free(&heap->heap, vma, size);
203*61046927SAndroid Build Coastguard Worker       return;
204*61046927SAndroid Build Coastguard Worker    }
205*61046927SAndroid Build Coastguard Worker    assert(!"Failed to find heap BO");
206*61046927SAndroid Build Coastguard Worker }
207*61046927SAndroid Build Coastguard Worker 
208*61046927SAndroid Build Coastguard Worker VkResult
nvk_heap_alloc(struct nvk_device * dev,struct nvk_heap * heap,uint64_t size,uint32_t alignment,uint64_t * addr_out,void ** map_out)209*61046927SAndroid Build Coastguard Worker nvk_heap_alloc(struct nvk_device *dev, struct nvk_heap *heap,
210*61046927SAndroid Build Coastguard Worker                uint64_t size, uint32_t alignment,
211*61046927SAndroid Build Coastguard Worker                uint64_t *addr_out, void **map_out)
212*61046927SAndroid Build Coastguard Worker {
213*61046927SAndroid Build Coastguard Worker    simple_mtx_lock(&heap->mutex);
214*61046927SAndroid Build Coastguard Worker    VkResult result = nvk_heap_alloc_locked(dev, heap, size, alignment,
215*61046927SAndroid Build Coastguard Worker                                            addr_out, map_out);
216*61046927SAndroid Build Coastguard Worker    simple_mtx_unlock(&heap->mutex);
217*61046927SAndroid Build Coastguard Worker 
218*61046927SAndroid Build Coastguard Worker    return result;
219*61046927SAndroid Build Coastguard Worker }
220*61046927SAndroid Build Coastguard Worker 
221*61046927SAndroid Build Coastguard Worker VkResult
nvk_heap_upload(struct nvk_device * dev,struct nvk_heap * heap,const void * data,size_t size,uint32_t alignment,uint64_t * addr_out)222*61046927SAndroid Build Coastguard Worker nvk_heap_upload(struct nvk_device *dev, struct nvk_heap *heap,
223*61046927SAndroid Build Coastguard Worker                 const void *data, size_t size, uint32_t alignment,
224*61046927SAndroid Build Coastguard Worker                 uint64_t *addr_out)
225*61046927SAndroid Build Coastguard Worker {
226*61046927SAndroid Build Coastguard Worker    simple_mtx_lock(&heap->mutex);
227*61046927SAndroid Build Coastguard Worker    void *map = NULL;
228*61046927SAndroid Build Coastguard Worker    VkResult result = nvk_heap_alloc_locked(dev, heap, size, alignment,
229*61046927SAndroid Build Coastguard Worker                                            addr_out, &map);
230*61046927SAndroid Build Coastguard Worker    simple_mtx_unlock(&heap->mutex);
231*61046927SAndroid Build Coastguard Worker 
232*61046927SAndroid Build Coastguard Worker    if (result != VK_SUCCESS)
233*61046927SAndroid Build Coastguard Worker       return result;
234*61046927SAndroid Build Coastguard Worker 
235*61046927SAndroid Build Coastguard Worker    if (map != NULL && (heap->map_flags & NVKMD_MEM_MAP_WR)) {
236*61046927SAndroid Build Coastguard Worker       /* If we have a map, copy directly with memcpy */
237*61046927SAndroid Build Coastguard Worker       memcpy(map, data, size);
238*61046927SAndroid Build Coastguard Worker    } else {
239*61046927SAndroid Build Coastguard Worker       /* Otherwise, kick off an upload with the upload queue.
240*61046927SAndroid Build Coastguard Worker        *
241*61046927SAndroid Build Coastguard Worker        * This is a queued operation that the driver ensures happens before any
242*61046927SAndroid Build Coastguard Worker        * more client work via semaphores.  Because this is asynchronous and
243*61046927SAndroid Build Coastguard Worker        * heap allocations are synchronous we have to be a bit careful here.
244*61046927SAndroid Build Coastguard Worker        * The heap only ever tracks the current known CPU state of everything
245*61046927SAndroid Build Coastguard Worker        * while the upload queue makes that state valid at some point in the
246*61046927SAndroid Build Coastguard Worker        * future.
247*61046927SAndroid Build Coastguard Worker        *
248*61046927SAndroid Build Coastguard Worker        * This can be especially tricky for very fast upload/free cycles such
249*61046927SAndroid Build Coastguard Worker        * as if the client compiles a shader, throws it away without using it,
250*61046927SAndroid Build Coastguard Worker        * and then compiles another shader that ends up at the same address.
251*61046927SAndroid Build Coastguard Worker        * What makes this all correct is the fact that the everything on the
252*61046927SAndroid Build Coastguard Worker        * upload queue happens in a well-defined device-wide order.  In this
253*61046927SAndroid Build Coastguard Worker        * case the first shader will get uploaded and then the second will get
254*61046927SAndroid Build Coastguard Worker        * uploaded over top of it.  As long as we don't free the memory out
255*61046927SAndroid Build Coastguard Worker        * from under the upload queue, everything will end up in the correct
256*61046927SAndroid Build Coastguard Worker        * state by the time the client's shaders actually execute.
257*61046927SAndroid Build Coastguard Worker        */
258*61046927SAndroid Build Coastguard Worker       result = nvk_upload_queue_upload(dev, &dev->upload, *addr_out, data, size);
259*61046927SAndroid Build Coastguard Worker       if (result != VK_SUCCESS) {
260*61046927SAndroid Build Coastguard Worker          nvk_heap_free(dev, heap, *addr_out, size);
261*61046927SAndroid Build Coastguard Worker          return result;
262*61046927SAndroid Build Coastguard Worker       }
263*61046927SAndroid Build Coastguard Worker    }
264*61046927SAndroid Build Coastguard Worker 
265*61046927SAndroid Build Coastguard Worker    return VK_SUCCESS;
266*61046927SAndroid Build Coastguard Worker }
267*61046927SAndroid Build Coastguard Worker 
268*61046927SAndroid Build Coastguard Worker void
nvk_heap_free(struct nvk_device * dev,struct nvk_heap * heap,uint64_t addr,uint64_t size)269*61046927SAndroid Build Coastguard Worker nvk_heap_free(struct nvk_device *dev, struct nvk_heap *heap,
270*61046927SAndroid Build Coastguard Worker               uint64_t addr, uint64_t size)
271*61046927SAndroid Build Coastguard Worker {
272*61046927SAndroid Build Coastguard Worker    simple_mtx_lock(&heap->mutex);
273*61046927SAndroid Build Coastguard Worker    nvk_heap_free_locked(dev, heap, addr, size);
274*61046927SAndroid Build Coastguard Worker    simple_mtx_unlock(&heap->mutex);
275*61046927SAndroid Build Coastguard Worker }
276