Home | History | Annotate | Download | only in r600
      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 (at) 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/u_double_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_memory.h"
     34 #include "util/u_inlines.h"
     35 #include "util/u_framebuffer.h"
     36 #include "r600.h"
     37 #include "r600_resource.h"
     38 #include "r600_shader.h"
     39 #include "r600_pipe.h"
     40 #include "r600_formats.h"
     41 #include "compute_memory_pool.h"
     42 #include "evergreen_compute_internal.h"
     43 
     44 static struct r600_texture * create_pool_texture(struct r600_screen * screen,
     45 		unsigned size_in_dw)
     46 {
     47 
     48 	struct pipe_resource templ;
     49 	struct r600_texture * tex;
     50 
     51 	if (size_in_dw == 0) {
     52 		return NULL;
     53 	}
     54 	memset(&templ, 0, sizeof(templ));
     55 	templ.target = PIPE_TEXTURE_1D;
     56 	templ.format = PIPE_FORMAT_R32_UINT;
     57 	templ.bind = PIPE_BIND_CUSTOM;
     58 	templ.usage = PIPE_USAGE_IMMUTABLE;
     59 	templ.flags = 0;
     60 	templ.width0 = size_in_dw;
     61 	templ.height0 = 1;
     62 	templ.depth0 = 1;
     63 	templ.array_size = 1;
     64 
     65 	tex = (struct r600_texture *)r600_texture_create(
     66 						&screen->screen, &templ);
     67 	/* XXX: Propagate this error */
     68 	assert(tex && "Out of memory");
     69 	tex->is_rat = 1;
     70 	return tex;
     71 }
     72 
     73 /**
     74  * Creates a new pool
     75  */
     76 struct compute_memory_pool* compute_memory_pool_new(
     77 	struct r600_screen * rscreen)
     78 {
     79 	struct compute_memory_pool* pool = (struct compute_memory_pool*)
     80 				CALLOC(sizeof(struct compute_memory_pool), 1);
     81 
     82 	COMPUTE_DBG("* compute_memory_pool_new()\n");
     83 
     84 	pool->screen = rscreen;
     85 	return pool;
     86 }
     87 
     88 static void compute_memory_pool_init(struct compute_memory_pool * pool,
     89 	unsigned initial_size_in_dw)
     90 {
     91 
     92 	COMPUTE_DBG("* compute_memory_pool_init() initial_size_in_dw = %ld\n",
     93 		initial_size_in_dw);
     94 
     95 	/* XXX: pool->shadow is used when the buffer needs to be resized, but
     96 	 * resizing does not work at the moment.
     97 	 * pool->shadow = (uint32_t*)CALLOC(4, pool->size_in_dw);
     98 	 */
     99 	pool->next_id = 1;
    100 	pool->size_in_dw = initial_size_in_dw;
    101 	pool->bo = (struct r600_resource*)create_pool_texture(pool->screen,
    102 							pool->size_in_dw);
    103 }
    104 
    105 /**
    106  * Frees all stuff in the pool and the pool struct itself too
    107  */
    108 void compute_memory_pool_delete(struct compute_memory_pool* pool)
    109 {
    110 	COMPUTE_DBG("* compute_memory_pool_delete()\n");
    111 	free(pool->shadow);
    112 	if (pool->bo) {
    113 		pool->screen->screen.resource_destroy((struct pipe_screen *)
    114 			pool->screen, (struct pipe_resource *)pool->bo);
    115 	}
    116 	free(pool);
    117 }
    118 
    119 /**
    120  * Searches for an empty space in the pool, return with the pointer to the
    121  * allocatable space in the pool, returns -1 on failure.
    122  */
    123 int64_t compute_memory_prealloc_chunk(
    124 	struct compute_memory_pool* pool,
    125 	int64_t size_in_dw)
    126 {
    127 	assert(size_in_dw <= pool->size_in_dw);
    128 
    129 	struct compute_memory_item *item;
    130 
    131 	int last_end = 0;
    132 
    133 	COMPUTE_DBG("* compute_memory_prealloc_chunk() size_in_dw = %ld\n",
    134 		size_in_dw);
    135 
    136 	for (item = pool->item_list; item; item = item->next) {
    137 		if (item->start_in_dw > -1) {
    138 			if (item->start_in_dw-last_end > size_in_dw) {
    139 				return last_end;
    140 			}
    141 
    142 			last_end = item->start_in_dw + item->size_in_dw;
    143 			last_end += (1024 - last_end % 1024);
    144 		}
    145 	}
    146 
    147 	if (pool->size_in_dw - last_end < size_in_dw) {
    148 		return -1;
    149 	}
    150 
    151 	return last_end;
    152 }
    153 
    154 /**
    155  *  Search for the chunk where we can link our new chunk after it.
    156  */
    157 struct compute_memory_item* compute_memory_postalloc_chunk(
    158 	struct compute_memory_pool* pool,
    159 	int64_t start_in_dw)
    160 {
    161 	struct compute_memory_item* item;
    162 
    163 	COMPUTE_DBG("* compute_memory_postalloc_chunck() start_in_dw = %ld\n",
    164 		start_in_dw);
    165 
    166 	for (item = pool->item_list; item; item = item->next) {
    167 		if (item->next) {
    168 			if (item->start_in_dw < start_in_dw
    169 				&& item->next->start_in_dw > start_in_dw) {
    170 				return item;
    171 			}
    172 		}
    173 		else {
    174 			/* end of chain */
    175 			assert(item->start_in_dw < start_in_dw);
    176 			return item;
    177 		}
    178 	}
    179 
    180 	assert(0 && "unreachable");
    181 	return NULL;
    182 }
    183 
    184 /**
    185  * Reallocates pool, conserves data
    186  */
    187 void compute_memory_grow_pool(struct compute_memory_pool* pool,
    188 	struct pipe_context * pipe, int new_size_in_dw)
    189 {
    190 	COMPUTE_DBG("* compute_memory_grow_pool() new_size_in_dw = %d\n",
    191 		new_size_in_dw);
    192 
    193 	assert(new_size_in_dw >= pool->size_in_dw);
    194 
    195 	assert(!pool->bo && "Growing the global memory pool is not yet "
    196 		"supported.  You will see this message if you are trying to"
    197 		"use more than 64 kb of memory");
    198 
    199 	if (!pool->bo) {
    200 		compute_memory_pool_init(pool, 1024 * 16);
    201 	} else {
    202 		/* XXX: Growing memory pools does not work at the moment.  I think
    203 		 * it is because we are using fragment shaders to copy data to
    204 		 * the new texture and some of the compute registers are being
    205 		 * included in the 3D command stream. */
    206 		fprintf(stderr, "Warning: growing the global memory pool to"
    207 				"more than 64 kb is not yet supported\n");
    208 		new_size_in_dw += 1024 - (new_size_in_dw % 1024);
    209 
    210 		COMPUTE_DBG("  Aligned size = %d\n", new_size_in_dw);
    211 
    212 		compute_memory_shadow(pool, pipe, 1);
    213 		pool->shadow = (uint32_t*)realloc(pool->shadow, new_size_in_dw*4);
    214 		pool->size_in_dw = new_size_in_dw;
    215 		pool->screen->screen.resource_destroy(
    216 			(struct pipe_screen *)pool->screen,
    217 			(struct pipe_resource *)pool->bo);
    218 		pool->bo = (struct r600_resource*)create_pool_texture(
    219 							pool->screen,
    220 							pool->size_in_dw);
    221 		compute_memory_shadow(pool, pipe, 0);
    222 	}
    223 }
    224 
    225 /**
    226  * Copy pool from device to host, or host to device.
    227  */
    228 void compute_memory_shadow(struct compute_memory_pool* pool,
    229 	struct pipe_context * pipe, int device_to_host)
    230 {
    231 	struct compute_memory_item chunk;
    232 
    233 	COMPUTE_DBG("* compute_memory_shadow() device_to_host = %d\n",
    234 		device_to_host);
    235 
    236 	chunk.id = 0;
    237 	chunk.start_in_dw = 0;
    238 	chunk.size_in_dw = pool->size_in_dw;
    239 	chunk.prev = chunk.next = NULL;
    240 	compute_memory_transfer(pool, pipe, device_to_host, &chunk,
    241 				pool->shadow, 0, pool->size_in_dw*4);
    242 }
    243 
    244 /**
    245  * Allocates pending allocations in the pool
    246  */
    247 void compute_memory_finalize_pending(struct compute_memory_pool* pool,
    248 	struct pipe_context * pipe)
    249 {
    250 	struct compute_memory_item *pending_list = NULL, *end_p = NULL;
    251 	struct compute_memory_item *item, *next;
    252 
    253 	int64_t allocated = 0;
    254 	int64_t unallocated = 0;
    255 
    256 	COMPUTE_DBG("* compute_memory_finalize_pending()\n");
    257 
    258 	for (item = pool->item_list; item; item = item->next) {
    259 		COMPUTE_DBG("list: %i %p\n", item->start_in_dw, item->next);
    260 	}
    261 
    262 	for (item = pool->item_list; item; item = next) {
    263 		next = item->next;
    264 
    265 
    266 		if (item->start_in_dw == -1) {
    267 			if (end_p) {
    268 				end_p->next = item;
    269 			}
    270 			else {
    271 				pending_list = item;
    272 			}
    273 
    274 			if (item->prev) {
    275 				item->prev->next = next;
    276 			}
    277 			else {
    278 				pool->item_list = next;
    279 			}
    280 
    281 			if (next) {
    282 				next->prev = item->prev;
    283 			}
    284 
    285 			item->prev = end_p;
    286 			item->next = NULL;
    287 			end_p = item;
    288 
    289 			unallocated += item->size_in_dw+1024;
    290 		}
    291 		else {
    292 			allocated += item->size_in_dw;
    293 		}
    294 	}
    295 
    296 	if (pool->size_in_dw < allocated+unallocated) {
    297 		compute_memory_grow_pool(pool, pipe, allocated+unallocated);
    298 	}
    299 
    300 	for (item = pending_list; item; item = next) {
    301 		next = item->next;
    302 
    303 		int64_t start_in_dw;
    304 
    305 		while ((start_in_dw=compute_memory_prealloc_chunk(pool,
    306 						item->size_in_dw)) == -1) {
    307 			int64_t need = item->size_in_dw+2048 -
    308 						(pool->size_in_dw - allocated);
    309 
    310 			need += 1024 - (need % 1024);
    311 
    312 			if (need > 0) {
    313 				compute_memory_grow_pool(pool,
    314 						pipe,
    315 						pool->size_in_dw + need);
    316 			}
    317 			else {
    318 				need = pool->size_in_dw / 10;
    319 				need += 1024 - (need % 1024);
    320 				compute_memory_grow_pool(pool,
    321 						pipe,
    322 						pool->size_in_dw + need);
    323 			}
    324 		}
    325 
    326 		item->start_in_dw = start_in_dw;
    327 		item->next = NULL;
    328 		item->prev = NULL;
    329 
    330 		if (pool->item_list) {
    331 			struct compute_memory_item *pos;
    332 
    333 			pos = compute_memory_postalloc_chunk(pool, start_in_dw);
    334 			item->prev = pos;
    335 			item->next = pos->next;
    336 			pos->next = item;
    337 
    338 			if (item->next) {
    339 				item->next->prev = item;
    340 			}
    341 		}
    342 		else {
    343 			pool->item_list = item;
    344 		}
    345 
    346 		allocated += item->size_in_dw;
    347 	}
    348 }
    349 
    350 
    351 void compute_memory_free(struct compute_memory_pool* pool, int64_t id)
    352 {
    353 	struct compute_memory_item *item, *next;
    354 
    355 	COMPUTE_DBG("* compute_memory_free() id + %ld \n", id);
    356 
    357 	for (item = pool->item_list; item; item = next) {
    358 		next = item->next;
    359 
    360 		if (item->id == id) {
    361 			if (item->prev) {
    362 				item->prev->next = item->next;
    363 			}
    364 			else {
    365 				pool->item_list = item->next;
    366 			}
    367 
    368 			if (item->next) {
    369 				item->next->prev = item->prev;
    370 			}
    371 
    372 			free(item);
    373 
    374 			return;
    375 		}
    376 	}
    377 
    378 	fprintf(stderr, "Internal error, invalid id %ld "
    379 		"for compute_memory_free\n", id);
    380 
    381 	assert(0 && "error");
    382 }
    383 
    384 /**
    385  * Creates pending allocations
    386  */
    387 struct compute_memory_item* compute_memory_alloc(
    388 	struct compute_memory_pool* pool,
    389 	int64_t size_in_dw)
    390 {
    391 	struct compute_memory_item *new_item;
    392 
    393 	COMPUTE_DBG("* compute_memory_alloc() size_in_dw = %ld\n", size_in_dw);
    394 
    395 	new_item = (struct compute_memory_item *)
    396 				CALLOC(sizeof(struct compute_memory_item), 1);
    397 	new_item->size_in_dw = size_in_dw;
    398 	new_item->start_in_dw = -1; /* mark pending */
    399 	new_item->id = pool->next_id++;
    400 	new_item->pool = pool;
    401 
    402 	struct compute_memory_item *last_item;
    403 
    404 	if (pool->item_list) {
    405 		for (last_item = pool->item_list; last_item->next;
    406 						last_item = last_item->next);
    407 
    408 		last_item->next = new_item;
    409 		new_item->prev = last_item;
    410 	}
    411 	else {
    412 		pool->item_list = new_item;
    413 	}
    414 
    415 	return new_item;
    416 }
    417 
    418 /**
    419  * Transfer data host<->device, offset and size is in bytes
    420  */
    421 void compute_memory_transfer(
    422 	struct compute_memory_pool* pool,
    423 	struct pipe_context * pipe,
    424 	int device_to_host,
    425 	struct compute_memory_item* chunk,
    426 	void* data,
    427 	int offset_in_chunk,
    428 	int size)
    429 {
    430 	int64_t aligned_size = pool->size_in_dw;
    431 	struct pipe_resource* gart = (struct pipe_resource*)pool->bo;
    432 	int64_t internal_offset = chunk->start_in_dw*4 + offset_in_chunk;
    433 
    434 	struct pipe_transfer *xfer;
    435 	uint32_t *map;
    436 
    437 	assert(gart);
    438 
    439 	COMPUTE_DBG("* compute_memory_transfer() device_to_host = %d, "
    440 		"offset_in_chunk = %d, size = %d\n", device_to_host,
    441 		offset_in_chunk, size);
    442 
    443 	if (device_to_host)
    444 	{
    445 		xfer = pipe->get_transfer(pipe, gart, 0, PIPE_TRANSFER_READ,
    446 			&(struct pipe_box) { .width = aligned_size,
    447 			.height = 1, .depth = 1 });
    448 		assert(xfer);
    449 		map = pipe->transfer_map(pipe, xfer);
    450 		assert(map);
    451 		memcpy(data, map + internal_offset, size);
    452 		pipe->transfer_unmap(pipe, xfer);
    453 		pipe->transfer_destroy(pipe, xfer);
    454 	} else {
    455 		xfer = pipe->get_transfer(pipe, gart, 0, PIPE_TRANSFER_WRITE,
    456 			&(struct pipe_box) { .width = aligned_size,
    457 			.height = 1, .depth = 1 });
    458 		assert(xfer);
    459 		map = pipe->transfer_map(pipe, xfer);
    460 		assert(map);
    461 		memcpy(map + internal_offset, data, size);
    462 		pipe->transfer_unmap(pipe, xfer);
    463 		pipe->transfer_destroy(pipe, xfer);
    464 	}
    465 }
    466 
    467 /**
    468  * Transfer data between chunk<->data, it is for VRAM<->GART transfers
    469  */
    470 void compute_memory_transfer_direct(
    471 	struct compute_memory_pool* pool,
    472 	int chunk_to_data,
    473 	struct compute_memory_item* chunk,
    474 	struct r600_resource* data,
    475 	int offset_in_chunk,
    476 	int offset_in_data,
    477 	int size)
    478 {
    479 	///TODO: DMA
    480 }
    481