• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Permission is hereby granted, free of charge, to any person obtaining a
3  * copy of this software and associated documentation files (the "Software"),
4  * to deal in the Software without restriction, including without limitation
5  * on the rights to use, copy, modify, merge, publish, distribute, sub
6  * license, and/or sell copies of the Software, and to permit persons to whom
7  * the Software is furnished to do so, subject to the following conditions:
8  *
9  * The above copyright notice and this permission notice (including the next
10  * paragraph) shall be included in all copies or substantial portions of the
11  * Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
16  * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
17  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
19  * USE OR OTHER DEALINGS IN THE SOFTWARE.
20  *
21  * Authors:
22  *      Adam Rak <adam.rak@streamnovation.com>
23  */
24 
25 #include "pipe/p_defines.h"
26 #include "pipe/p_state.h"
27 #include "pipe/p_context.h"
28 #include "util/u_blitter.h"
29 #include "util/list.h"
30 #include "util/u_transfer.h"
31 #include "util/u_surface.h"
32 #include "util/u_pack_color.h"
33 #include "util/u_math.h"
34 #include "util/u_memory.h"
35 #include "util/u_inlines.h"
36 #include "util/u_framebuffer.h"
37 #include "r600_shader.h"
38 #include "r600_pipe.h"
39 #include "r600_formats.h"
40 #include "compute_memory_pool.h"
41 #include "evergreen_compute.h"
42 #include "evergreen_compute_internal.h"
43 #include <inttypes.h>
44 
45 #define ITEM_ALIGNMENT 1024
46 
47 /* A few forward declarations of static functions */
48 static void compute_memory_shadow(struct compute_memory_pool* pool,
49 	struct pipe_context *pipe, int device_to_host);
50 
51 static void compute_memory_defrag(struct compute_memory_pool *pool,
52 	struct pipe_resource *src, struct pipe_resource *dst,
53 	struct pipe_context *pipe);
54 
55 static int compute_memory_promote_item(struct compute_memory_pool *pool,
56 	struct compute_memory_item *item, struct pipe_context *pipe,
57 	int64_t allocated);
58 
59 static void compute_memory_move_item(struct compute_memory_pool *pool,
60 	struct pipe_resource *src, struct pipe_resource *dst,
61 	struct compute_memory_item *item, uint64_t new_start_in_dw,
62 	struct pipe_context *pipe);
63 
64 static void compute_memory_transfer(struct compute_memory_pool* pool,
65 	struct pipe_context * pipe, int device_to_host,
66 	struct compute_memory_item* chunk, void* data,
67 	int offset_in_chunk, int size);
68 
69 /**
70  * Creates a new pool.
71  */
compute_memory_pool_new(struct r600_screen * rscreen)72 struct compute_memory_pool* compute_memory_pool_new(
73 	struct r600_screen * rscreen)
74 {
75 	struct compute_memory_pool* pool = (struct compute_memory_pool*)
76 				CALLOC(sizeof(struct compute_memory_pool), 1);
77 	if (!pool)
78 		return NULL;
79 
80 	COMPUTE_DBG(rscreen, "* compute_memory_pool_new()\n");
81 
82 	pool->screen = rscreen;
83 	pool->item_list = (struct list_head *)
84 				CALLOC(sizeof(struct list_head), 1);
85 	pool->unallocated_list = (struct list_head *)
86 				CALLOC(sizeof(struct list_head), 1);
87 	list_inithead(pool->item_list);
88 	list_inithead(pool->unallocated_list);
89 	return pool;
90 }
91 
92 /**
93  * Initializes the pool with a size of \a initial_size_in_dw.
94  * \param pool			The pool to be initialized.
95  * \param initial_size_in_dw	The initial size.
96  * \see compute_memory_grow_defrag_pool
97  */
compute_memory_pool_init(struct compute_memory_pool * pool,unsigned initial_size_in_dw)98 static void compute_memory_pool_init(struct compute_memory_pool * pool,
99 	unsigned initial_size_in_dw)
100 {
101 
102 	COMPUTE_DBG(pool->screen, "* compute_memory_pool_init() initial_size_in_dw = %u\n",
103 		initial_size_in_dw);
104 
105 	pool->size_in_dw = initial_size_in_dw;
106 	pool->bo = r600_compute_buffer_alloc_vram(pool->screen,
107 						  pool->size_in_dw * 4);
108 }
109 
110 /**
111  * Frees all stuff in the pool and the pool struct itself too.
112  */
compute_memory_pool_delete(struct compute_memory_pool * pool)113 void compute_memory_pool_delete(struct compute_memory_pool* pool)
114 {
115 	COMPUTE_DBG(pool->screen, "* compute_memory_pool_delete()\n");
116 	free(pool->shadow);
117 	r600_resource_reference(&pool->bo, NULL);
118 	/* In theory, all of the items were freed in compute_memory_free.
119 	 * Just delete the list heads
120 	 */
121 	free(pool->item_list);
122 	free(pool->unallocated_list);
123 	/* And then the pool itself */
124 	free(pool);
125 }
126 
127 /**
128  * Reallocates and defragments the pool, conserves data.
129  * \returns -1 if it fails, 0 otherwise
130  * \see compute_memory_finalize_pending
131  */
compute_memory_grow_defrag_pool(struct compute_memory_pool * pool,struct pipe_context * pipe,int new_size_in_dw)132 static int compute_memory_grow_defrag_pool(struct compute_memory_pool *pool,
133 	struct pipe_context *pipe, int new_size_in_dw)
134 {
135 	new_size_in_dw = align(new_size_in_dw, ITEM_ALIGNMENT);
136 
137 	COMPUTE_DBG(pool->screen, "* compute_memory_grow_defrag_pool() "
138 		"new_size_in_dw = %d (%d bytes)\n",
139 		new_size_in_dw, new_size_in_dw * 4);
140 
141 	assert(new_size_in_dw >= pool->size_in_dw);
142 
143 	if (!pool->bo) {
144 		compute_memory_pool_init(pool, MAX2(new_size_in_dw, 1024 * 16));
145 	} else {
146 		struct r600_resource *temp = NULL;
147 
148 		temp = r600_compute_buffer_alloc_vram(pool->screen, new_size_in_dw * 4);
149 
150 		if (temp != NULL) {
151 			struct pipe_resource *src = (struct pipe_resource *)pool->bo;
152 			struct pipe_resource *dst = (struct pipe_resource *)temp;
153 
154 			COMPUTE_DBG(pool->screen, "  Growing and defragmenting the pool "
155 					"using a temporary resource\n");
156 
157 			compute_memory_defrag(pool, src, dst, pipe);
158 
159 			/* Release the old buffer */
160 			r600_resource_reference(&pool->bo, NULL);
161 			pool->bo = temp;
162 			pool->size_in_dw = new_size_in_dw;
163 		}
164 		else {
165 			COMPUTE_DBG(pool->screen, "  The creation of the temporary resource failed\n"
166 				"  Falling back to using 'shadow'\n");
167 
168 			compute_memory_shadow(pool, pipe, 1);
169 			pool->shadow = realloc(pool->shadow, new_size_in_dw * 4);
170 			if (pool->shadow == NULL)
171 				return -1;
172 
173 			pool->size_in_dw = new_size_in_dw;
174 			/* Release the old buffer */
175 			r600_resource_reference(&pool->bo, NULL);
176 			pool->bo = r600_compute_buffer_alloc_vram(pool->screen, pool->size_in_dw * 4);
177 			compute_memory_shadow(pool, pipe, 0);
178 
179 			if (pool->status & POOL_FRAGMENTED) {
180 				struct pipe_resource *src = (struct pipe_resource *)pool->bo;
181 				compute_memory_defrag(pool, src, src, pipe);
182 			}
183 		}
184 	}
185 
186 	return 0;
187 }
188 
189 /**
190  * Copy pool from device to host, or host to device.
191  * \param device_to_host 1 for device->host, 0 for host->device
192  * \see compute_memory_grow_defrag_pool
193  */
compute_memory_shadow(struct compute_memory_pool * pool,struct pipe_context * pipe,int device_to_host)194 static void compute_memory_shadow(struct compute_memory_pool* pool,
195 	struct pipe_context * pipe, int device_to_host)
196 {
197 	struct compute_memory_item chunk;
198 
199 	COMPUTE_DBG(pool->screen, "* compute_memory_shadow() device_to_host = %d\n",
200 		device_to_host);
201 
202 	chunk.id = 0;
203 	chunk.start_in_dw = 0;
204 	chunk.size_in_dw = pool->size_in_dw;
205 	compute_memory_transfer(pool, pipe, device_to_host, &chunk,
206 				pool->shadow, 0, pool->size_in_dw*4);
207 }
208 
209 /**
210  * Moves all the items marked for promotion from the \a unallocated_list
211  * to the \a item_list.
212  * \return -1 if it fails, 0 otherwise
213  * \see evergreen_set_global_binding
214  */
compute_memory_finalize_pending(struct compute_memory_pool * pool,struct pipe_context * pipe)215 int compute_memory_finalize_pending(struct compute_memory_pool* pool,
216 	struct pipe_context * pipe)
217 {
218 	struct compute_memory_item *item, *next;
219 
220 	int64_t allocated = 0;
221 	int64_t unallocated = 0;
222 	int64_t last_pos;
223 
224 	int err = 0;
225 
226 	COMPUTE_DBG(pool->screen, "* compute_memory_finalize_pending()\n");
227 
228 	LIST_FOR_EACH_ENTRY(item, pool->item_list, link) {
229 		COMPUTE_DBG(pool->screen, "  + list: offset = %"PRIi64" id = %"PRIi64" size = %"PRIi64" "
230 			"(%"PRIi64" bytes)\n", item->start_in_dw, item->id,
231 			item->size_in_dw, item->size_in_dw * 4);
232 	}
233 
234 	/* Calculate the total allocated size */
235 	LIST_FOR_EACH_ENTRY(item, pool->item_list, link) {
236 		allocated += align(item->size_in_dw, ITEM_ALIGNMENT);
237 	}
238 
239 	/* Calculate the total unallocated size of the items that
240 	 * will be promoted to the pool */
241 	LIST_FOR_EACH_ENTRY(item, pool->unallocated_list, link) {
242 		if (item->status & ITEM_FOR_PROMOTING)
243 			unallocated += align(item->size_in_dw, ITEM_ALIGNMENT);
244 	}
245 
246 	if (unallocated == 0) {
247 		return 0;
248 	}
249 
250 	if (pool->size_in_dw < allocated + unallocated) {
251 		err = compute_memory_grow_defrag_pool(pool, pipe, allocated + unallocated);
252 		if (err == -1)
253 			return -1;
254 	}
255 	else if (pool->status & POOL_FRAGMENTED) {
256 		/* Loop through all unallocated items marked for promoting to
257 		 * insert them into an appropriate existing hole prior to defrag. */
258 		LIST_FOR_EACH_ENTRY_SAFE(item, next, pool->unallocated_list, link) {
259 			if (!(item->status & ITEM_FOR_PROMOTING))
260 				continue;
261 
262 			int64_t hole_start = 0, hole_size = 0;
263 			int64_t item_size = align(item->size_in_dw, ITEM_ALIGNMENT);
264 			struct compute_memory_item *alloc_item, *alloc_next;
265 			LIST_FOR_EACH_ENTRY_SAFE(alloc_item, alloc_next, pool->item_list, link) {
266 				if (alloc_item->start_in_dw == hole_start) {
267 					hole_start += align(alloc_item->size_in_dw, ITEM_ALIGNMENT);
268 					hole_size = 0;
269 				} else if (alloc_item->start_in_dw > hole_start) {
270 					hole_size = alloc_item->start_in_dw - hole_start;
271 				}
272 			}
273 
274 			/* Space after all items is also a hole. */
275 			if (hole_size == 0 && hole_start < pool->size_in_dw)
276 				hole_size = pool->size_in_dw - hole_start;
277 
278 			if (hole_size >= item_size) {
279 				if (compute_memory_promote_item(pool, item, pipe, hole_start) != -1) {
280 					item->status &= ~ITEM_FOR_PROMOTING;
281 					unallocated -= item_size;
282 					allocated += item_size;
283 				}
284 			}
285 		}
286 
287 		if (allocated == pool->size_in_dw)
288 			pool->status &= ~POOL_FRAGMENTED;
289 
290 		if (unallocated == 0)
291 			return 0;
292 
293 		struct pipe_resource *src = (struct pipe_resource *)pool->bo;
294 		compute_memory_defrag(pool, src, src, pipe);
295 	}
296 
297 	/* After defragmenting the pool, allocated is equal to the first available
298 	 * position for new items in the pool */
299 	last_pos = allocated;
300 
301 	/* Loop through all the unallocated items, check if they are marked
302 	 * for promoting, allocate space for them and add them to the item_list. */
303 	LIST_FOR_EACH_ENTRY_SAFE(item, next, pool->unallocated_list, link) {
304 		if (item->status & ITEM_FOR_PROMOTING) {
305 			err = compute_memory_promote_item(pool, item, pipe, last_pos);
306 			item->status &= ~ITEM_FOR_PROMOTING;
307 
308 			last_pos += align(item->size_in_dw, ITEM_ALIGNMENT);
309 
310 			if (err == -1)
311 				return -1;
312 		}
313 	}
314 
315 	return 0;
316 }
317 
318 /**
319  * Defragments the pool, so that there's no gap between items.
320  * \param pool	The pool to be defragmented
321  * \param src	The origin resource
322  * \param dst	The destination resource
323  * \see compute_memory_grow_defrag_pool and compute_memory_finalize_pending
324  */
compute_memory_defrag(struct compute_memory_pool * pool,struct pipe_resource * src,struct pipe_resource * dst,struct pipe_context * pipe)325 static void compute_memory_defrag(struct compute_memory_pool *pool,
326 	struct pipe_resource *src, struct pipe_resource *dst,
327 	struct pipe_context *pipe)
328 {
329 	struct compute_memory_item *item;
330 	int64_t last_pos;
331 
332 	COMPUTE_DBG(pool->screen, "* compute_memory_defrag()\n");
333 
334 	last_pos = 0;
335 	LIST_FOR_EACH_ENTRY(item, pool->item_list, link) {
336 		if (src != dst || item->start_in_dw != last_pos) {
337 			assert(last_pos <= item->start_in_dw);
338 
339 			compute_memory_move_item(pool, src, dst,
340 					item, last_pos, pipe);
341 		}
342 
343 		last_pos += align(item->size_in_dw, ITEM_ALIGNMENT);
344 	}
345 
346 	pool->status &= ~POOL_FRAGMENTED;
347 }
348 
349 /**
350  * Moves an item from the \a unallocated_list to the \a item_list.
351  * \param item	The item that will be promoted.
352  * \return -1 if it fails, 0 otherwise
353  * \see compute_memory_finalize_pending
354  */
compute_memory_promote_item(struct compute_memory_pool * pool,struct compute_memory_item * item,struct pipe_context * pipe,int64_t start_in_dw)355 static int compute_memory_promote_item(struct compute_memory_pool *pool,
356 		struct compute_memory_item *item, struct pipe_context *pipe,
357 		int64_t start_in_dw)
358 {
359 	struct pipe_screen *screen = (struct pipe_screen *)pool->screen;
360 	struct r600_context *rctx = (struct r600_context *)pipe;
361 	struct pipe_resource *src = (struct pipe_resource *)item->real_buffer;
362 	struct pipe_resource *dst = (struct pipe_resource *)pool->bo;
363 	struct pipe_box box;
364 
365 	COMPUTE_DBG(pool->screen, "* compute_memory_promote_item()\n"
366 			"  + Promoting Item: %"PRIi64" , starting at: %"PRIi64" (%"PRIi64" bytes) "
367 			"size: %"PRIi64" (%"PRIi64" bytes)\n\t\t\tnew start: %"PRIi64" (%"PRIi64" bytes)\n",
368 			item->id, item->start_in_dw, item->start_in_dw * 4,
369 			item->size_in_dw, item->size_in_dw * 4,
370 			start_in_dw, start_in_dw * 4);
371 
372 	/* Remove the item from the unallocated list */
373 	list_del(&item->link);
374 
375 	/* Add it back to the item_list */
376 	list_addtail(&item->link, pool->item_list);
377 	item->start_in_dw = start_in_dw;
378 
379 	if (src) {
380 		u_box_1d(0, item->size_in_dw * 4, &box);
381 
382 		rctx->b.b.resource_copy_region(pipe,
383 				dst, 0, item->start_in_dw * 4, 0 ,0,
384 				src, 0, &box);
385 
386 		/* We check if the item is mapped for reading.
387 		 * In this case, we need to keep the temporary buffer 'alive'
388 		 * because it is possible to keep a map active for reading
389 		 * while a kernel (that reads from it) executes */
390 		if (!(item->status & ITEM_MAPPED_FOR_READING) && !is_item_user_ptr(item)) {
391 			pool->screen->b.b.resource_destroy(screen, src);
392 			item->real_buffer = NULL;
393 		}
394 	}
395 
396 	return 0;
397 }
398 
399 /**
400  * Moves an item from the \a item_list to the \a unallocated_list.
401  * \param item	The item that will be demoted
402  * \see r600_compute_global_transfer_map
403  */
compute_memory_demote_item(struct compute_memory_pool * pool,struct compute_memory_item * item,struct pipe_context * pipe)404 void compute_memory_demote_item(struct compute_memory_pool *pool,
405 	struct compute_memory_item *item, struct pipe_context *pipe)
406 {
407 	struct r600_context *rctx = (struct r600_context *)pipe;
408 	struct pipe_resource *src = (struct pipe_resource *)pool->bo;
409 	struct pipe_resource *dst;
410 	struct pipe_box box;
411 
412 	COMPUTE_DBG(pool->screen, "* compute_memory_demote_item()\n"
413 			"  + Demoting Item: %"PRIi64", starting at: %"PRIi64" (%"PRIi64" bytes) "
414 			"size: %"PRIi64" (%"PRIi64" bytes)\n", item->id, item->start_in_dw,
415 			item->start_in_dw * 4, item->size_in_dw, item->size_in_dw * 4);
416 
417 	/* First, we remove the item from the item_list */
418 	list_del(&item->link);
419 
420 	/* Now we add it to the unallocated list */
421 	list_addtail(&item->link, pool->unallocated_list);
422 
423 	/* We check if the intermediate buffer exists, and if it
424 	 * doesn't, we create it again */
425 	if (item->real_buffer == NULL) {
426 		item->real_buffer = r600_compute_buffer_alloc_vram(
427 				pool->screen, item->size_in_dw * 4);
428 	}
429 
430 	dst = (struct pipe_resource *)item->real_buffer;
431 
432 	/* We transfer the memory from the item in the pool to the
433 	 * temporary buffer. Download is skipped for items:
434 	 * - Not mapped for reading or writing (PIPE_MAP_DISCARD_RANGE).
435 	 * - Not writable by the device. */
436 	if ((item->status & (ITEM_MAPPED_FOR_READING|ITEM_MAPPED_FOR_WRITING)) &&
437 		!(r600_resource(dst)->flags & RADEON_FLAG_READ_ONLY)) {
438 
439 		u_box_1d(item->start_in_dw * 4, item->size_in_dw * 4, &box);
440 
441 		rctx->b.b.resource_copy_region(pipe,
442 			dst, 0, 0, 0, 0,
443 			src, 0, &box);
444 	}
445 
446 	/* Remember to mark the buffer as 'pending' by setting start_in_dw to -1 */
447 	item->start_in_dw = -1;
448 
449 	if (item->link.next != pool->item_list) {
450 		pool->status |= POOL_FRAGMENTED;
451 	}
452 }
453 
454 /**
455  * Moves the item \a item forward from the resource \a src to the
456  * resource \a dst at \a new_start_in_dw
457  *
458  * This function assumes two things:
459  * 1) The item is \b only moved forward, unless src is different from dst
460  * 2) The item \b won't change it's position inside the \a item_list
461  *
462  * \param item			The item that will be moved
463  * \param new_start_in_dw	The new position of the item in \a item_list
464  * \see compute_memory_defrag
465  */
compute_memory_move_item(struct compute_memory_pool * pool,struct pipe_resource * src,struct pipe_resource * dst,struct compute_memory_item * item,uint64_t new_start_in_dw,struct pipe_context * pipe)466 static void compute_memory_move_item(struct compute_memory_pool *pool,
467 	struct pipe_resource *src, struct pipe_resource *dst,
468 	struct compute_memory_item *item, uint64_t new_start_in_dw,
469 	struct pipe_context *pipe)
470 {
471 	struct pipe_screen *screen = (struct pipe_screen *)pool->screen;
472 	struct r600_context *rctx = (struct r600_context *)pipe;
473 	struct pipe_box box;
474 
475 	COMPUTE_DBG(pool->screen, "* compute_memory_move_item()\n"
476 			"  + Moving item %"PRIi64" from %"PRIi64" (%"PRIi64" bytes) to %"PRIu64" (%"PRIu64" bytes)\n",
477 			item->id, item->start_in_dw, item->start_in_dw * 4,
478 			new_start_in_dw, new_start_in_dw * 4);
479 
480 	if (pool->item_list != item->link.prev) {
481 		ASSERTED struct compute_memory_item *prev;
482 		prev = container_of(item->link.prev, struct compute_memory_item, link);
483 		assert(prev->start_in_dw + prev->size_in_dw <= new_start_in_dw);
484 	}
485 
486 	u_box_1d(item->start_in_dw * 4, item->size_in_dw * 4, &box);
487 
488 	/* If the ranges don't overlap, or we are copying from one resource
489 	 * to another, we can just copy the item directly */
490 	if (src != dst || new_start_in_dw + item->size_in_dw <= item->start_in_dw) {
491 
492 		rctx->b.b.resource_copy_region(pipe,
493 			dst, 0, new_start_in_dw * 4, 0, 0,
494 			src, 0, &box);
495 	} else {
496 		/* The ranges overlap, we will try first to use an intermediate
497 		 * resource to move the item */
498 		struct pipe_resource *tmp = (struct pipe_resource *)
499 			r600_compute_buffer_alloc_vram(pool->screen, item->size_in_dw * 4);
500 
501 		if (tmp != NULL) {
502 			rctx->b.b.resource_copy_region(pipe,
503 				tmp, 0, 0, 0, 0,
504 				src, 0, &box);
505 
506 			box.x = 0;
507 
508 			rctx->b.b.resource_copy_region(pipe,
509 				dst, 0, new_start_in_dw * 4, 0, 0,
510 				tmp, 0, &box);
511 
512 			pool->screen->b.b.resource_destroy(screen, tmp);
513 
514 		} else {
515 			/* The allocation of the temporary resource failed,
516 			 * falling back to use mappings */
517 			uint32_t *map;
518 			int64_t offset;
519 			struct pipe_transfer *trans;
520 
521 			offset = item->start_in_dw - new_start_in_dw;
522 
523 			u_box_1d(new_start_in_dw * 4, (offset + item->size_in_dw) * 4, &box);
524 
525 			map = pipe->buffer_map(pipe, src, 0, PIPE_MAP_READ_WRITE,
526 				&box, &trans);
527 
528 			assert(map);
529 			assert(trans);
530 
531 			memmove(map, map + offset, item->size_in_dw * 4);
532 
533 			pipe->buffer_unmap(pipe, trans);
534 		}
535 	}
536 
537 	item->start_in_dw = new_start_in_dw;
538 }
539 
540 /**
541  * Frees one item for compute_memory_free()
542  */
compute_memory_free_item(struct pipe_screen * screen,struct compute_memory_item * item)543 static void compute_memory_free_item(struct pipe_screen *screen,
544 	struct compute_memory_item *item)
545 {
546 	struct pipe_resource *res = (struct pipe_resource *)item->real_buffer;
547 
548 	list_del(&item->link);
549 
550 	if (res && !is_item_user_ptr(item))
551 		screen->resource_destroy(screen, res);
552 
553 	free(item);
554 }
555 
556 /**
557  * Frees the memory associated to the item with id \a id from the pool.
558  * \param id	The id of the item to be freed.
559  */
compute_memory_free(struct compute_memory_pool * pool,int64_t id)560 void compute_memory_free(struct compute_memory_pool* pool, int64_t id)
561 {
562 	struct compute_memory_item *item, *next;
563 	struct pipe_screen *screen = (struct pipe_screen *)pool->screen;
564 
565 	COMPUTE_DBG(pool->screen, "* compute_memory_free() id + %"PRIi64" \n", id);
566 
567 	LIST_FOR_EACH_ENTRY_SAFE(item, next, pool->item_list, link) {
568 		if (item->id == id) {
569 			if (item->link.next != pool->item_list) {
570 				pool->status |= POOL_FRAGMENTED;
571 			}
572 
573 			compute_memory_free_item(screen, item);
574 			return;
575 		}
576 	}
577 
578 	LIST_FOR_EACH_ENTRY_SAFE(item, next, pool->unallocated_list, link) {
579 		if (item->id == id) {
580 			compute_memory_free_item(screen, item);
581 			return;
582 		}
583 	}
584 
585 	fprintf(stderr, "Internal error, invalid id %"PRIi64" "
586 		"for compute_memory_free\n", id);
587 
588 	assert(0 && "error");
589 }
590 
591 /**
592  * Creates pending allocations for new items, these items are
593  * placed in the unallocated_list.
594  * \param size_in_dw	The size, in double words, of the new item.
595  * \return The new item
596  * \see r600_compute_global_buffer_create
597  */
compute_memory_alloc(struct compute_memory_pool * pool,int64_t size_in_dw)598 struct compute_memory_item* compute_memory_alloc(
599 	struct compute_memory_pool* pool,
600 	int64_t size_in_dw)
601 {
602 	struct compute_memory_item *new_item = NULL;
603 
604 	COMPUTE_DBG(pool->screen, "* compute_memory_alloc() size_in_dw = %"PRIi64" (%"PRIi64" bytes)\n",
605 			size_in_dw, 4 * size_in_dw);
606 
607 	new_item = (struct compute_memory_item *)
608 				CALLOC(sizeof(struct compute_memory_item), 1);
609 	if (!new_item)
610 		return NULL;
611 
612 	new_item->size_in_dw = size_in_dw;
613 	new_item->start_in_dw = -1; /* mark pending */
614 	new_item->id = pool->next_id++;
615 	new_item->pool = pool;
616 	new_item->real_buffer = NULL;
617 
618 	list_addtail(&new_item->link, pool->unallocated_list);
619 
620 	COMPUTE_DBG(pool->screen, "  + Adding item %p id = %"PRIi64" size = %"PRIi64" (%"PRIi64" bytes)\n",
621 			new_item, new_item->id, new_item->size_in_dw,
622 			new_item->size_in_dw * 4);
623 	return new_item;
624 }
625 
626 /**
627  * Transfer data host<->device, offset and size is in bytes.
628  * \param device_to_host 1 for device->host, 0 for host->device.
629  * \see compute_memory_shadow
630  */
compute_memory_transfer(struct compute_memory_pool * pool,struct pipe_context * pipe,int device_to_host,struct compute_memory_item * chunk,void * data,int offset_in_chunk,int size)631 static void compute_memory_transfer(
632 	struct compute_memory_pool* pool,
633 	struct pipe_context * pipe,
634 	int device_to_host,
635 	struct compute_memory_item* chunk,
636 	void* data,
637 	int offset_in_chunk,
638 	int size)
639 {
640 	int64_t aligned_size = pool->size_in_dw;
641 	struct pipe_resource* gart = (struct pipe_resource*)pool->bo;
642 	int64_t internal_offset = chunk->start_in_dw*4 + offset_in_chunk;
643 
644 	struct pipe_transfer *xfer;
645 	uint32_t *map;
646 
647 	assert(gart);
648 
649 	COMPUTE_DBG(pool->screen, "* compute_memory_transfer() device_to_host = %d, "
650 		"offset_in_chunk = %d, size = %d\n", device_to_host,
651 		offset_in_chunk, size);
652 
653 	if (device_to_host) {
654 		map = pipe->buffer_map(pipe, gart, 0, PIPE_MAP_READ,
655 			&(struct pipe_box) { .width = aligned_size * 4,
656 			.height = 1, .depth = 1 }, &xfer);
657 		assert(xfer);
658 		assert(map);
659 		memcpy(data, map + internal_offset, size);
660 		pipe->buffer_unmap(pipe, xfer);
661 	} else {
662 		map = pipe->buffer_map(pipe, gart, 0, PIPE_MAP_WRITE,
663 			&(struct pipe_box) { .width = aligned_size * 4,
664 			.height = 1, .depth = 1 }, &xfer);
665 		assert(xfer);
666 		assert(map);
667 		memcpy(map + internal_offset, data, size);
668 		pipe->buffer_unmap(pipe, xfer);
669 	}
670 }
671