r600g: improve flushed depth texture handling v2
[mesa.git] / src / gallium / drivers / r600 / compute_memory_pool.c
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/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_resource_texture * create_pool_texture(struct r600_screen * screen,
45 unsigned size_in_dw)
46 {
47
48 struct pipe_resource templ;
49 struct r600_resource_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_resource_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 int64_t initial_size_in_dw,
78 struct r600_screen * rscreen)
79 {
80 struct compute_memory_pool* pool = (struct compute_memory_pool*)
81 CALLOC(sizeof(struct compute_memory_pool), 1);
82
83 pool->next_id = 1;
84 pool->size_in_dw = initial_size_in_dw;
85 pool->screen = rscreen;
86 pool->bo = (struct r600_resource*)create_pool_texture(pool->screen,
87 pool->size_in_dw);
88 pool->shadow = (uint32_t*)CALLOC(4, pool->size_in_dw);
89
90 return pool;
91 }
92
93 /**
94 * Frees all stuff in the pool and the pool struct itself too
95 */
96 void compute_memory_pool_delete(struct compute_memory_pool* pool)
97 {
98 free(pool->shadow);
99 if (pool->bo) {
100 pool->screen->screen.resource_destroy((struct pipe_screen *)
101 pool->screen, (struct pipe_resource *)pool->bo);
102 }
103 free(pool);
104 }
105
106 /**
107 * Searches for an empty space in the pool, return with the pointer to the
108 * allocatable space in the pool, returns -1 on failure.
109 */
110 int64_t compute_memory_prealloc_chunk(
111 struct compute_memory_pool* pool,
112 int64_t size_in_dw)
113 {
114 assert(size_in_dw <= pool->size_in_dw);
115
116 struct compute_memory_item *item;
117
118 int last_end = 0;
119
120 for (item = pool->item_list; item; item = item->next) {
121 if (item->start_in_dw > -1) {
122 if (item->start_in_dw-last_end > size_in_dw) {
123 return last_end;
124 }
125
126 last_end = item->start_in_dw + item->size_in_dw;
127 last_end += (1024 - last_end % 1024);
128 }
129 }
130
131 if (pool->size_in_dw - last_end < size_in_dw) {
132 return -1;
133 }
134
135 return last_end;
136 }
137
138 /**
139 * Search for the chunk where we can link our new chunk after it.
140 */
141 struct compute_memory_item* compute_memory_postalloc_chunk(
142 struct compute_memory_pool* pool,
143 int64_t start_in_dw)
144 {
145 struct compute_memory_item* item;
146
147 for (item = pool->item_list; item; item = item->next) {
148 if (item->next) {
149 if (item->start_in_dw < start_in_dw
150 && item->next->start_in_dw > start_in_dw) {
151 return item;
152 }
153 }
154 else {
155 /* end of chain */
156 assert(item->start_in_dw < start_in_dw);
157 return item;
158 }
159 }
160
161 assert(0 && "unreachable");
162 return NULL;
163 }
164
165 /**
166 * Reallocates pool, conserves data
167 */
168 void compute_memory_grow_pool(struct compute_memory_pool* pool,
169 struct pipe_context * pipe, int new_size_in_dw)
170 {
171 assert(new_size_in_dw >= pool->size_in_dw);
172
173 new_size_in_dw += 1024 - (new_size_in_dw % 1024);
174
175 if (pool->bo) {
176 compute_memory_shadow(pool, pipe, 1);
177 }
178 pool->shadow = (uint32_t*)realloc(pool->shadow, new_size_in_dw*4);
179 pool->size_in_dw = new_size_in_dw;
180 if (pool->bo) {
181 pool->screen->screen.resource_destroy(
182 (struct pipe_screen *)pool->screen,
183 (struct pipe_resource *)pool->bo);
184 pool->bo = (struct r600_resource*)create_pool_texture(
185 pool->screen,
186 pool->size_in_dw);
187 compute_memory_shadow(pool, pipe, 0);
188 } else {
189 pool->bo = (struct r600_resource*)create_pool_texture(
190 pool->screen,
191 pool->size_in_dw);
192 }
193 }
194
195 /**
196 * Copy pool from device to host, or host to device.
197 */
198 void compute_memory_shadow(struct compute_memory_pool* pool,
199 struct pipe_context * pipe, int device_to_host)
200 {
201 struct compute_memory_item chunk;
202
203 chunk.id = 0;
204 chunk.start_in_dw = 0;
205 chunk.size_in_dw = pool->size_in_dw;
206 chunk.prev = chunk.next = NULL;
207 compute_memory_transfer(pool, pipe, device_to_host, &chunk,
208 pool->shadow, 0, pool->size_in_dw*4);
209 }
210
211 /**
212 * Allocates pending allocations in the pool
213 */
214 void compute_memory_finalize_pending(struct compute_memory_pool* pool,
215 struct pipe_context * pipe)
216 {
217 struct compute_memory_item *pending_list = NULL, *end_p = NULL;
218 struct compute_memory_item *item, *next;
219
220 int64_t allocated = 0;
221 int64_t unallocated = 0;
222
223 for (item = pool->item_list; item; item = item->next) {
224 COMPUTE_DBG("list: %i %p\n", item->start_in_dw, item->next);
225 }
226
227 for (item = pool->item_list; item; item = next) {
228 next = item->next;
229
230
231 if (item->start_in_dw == -1) {
232 if (end_p) {
233 end_p->next = item;
234 }
235 else {
236 pending_list = item;
237 }
238
239 if (item->prev) {
240 item->prev->next = next;
241 }
242 else {
243 pool->item_list = next;
244 }
245
246 if (next) {
247 next->prev = item->prev;
248 }
249
250 item->prev = end_p;
251 item->next = NULL;
252 end_p = item;
253
254 unallocated += item->size_in_dw+1024;
255 }
256 else {
257 allocated += item->size_in_dw;
258 }
259 }
260
261 if (pool->size_in_dw < allocated+unallocated) {
262 compute_memory_grow_pool(pool, pipe, allocated+unallocated);
263 }
264
265 for (item = pending_list; item; item = next) {
266 next = item->next;
267
268 int64_t start_in_dw;
269
270 while ((start_in_dw=compute_memory_prealloc_chunk(pool,
271 item->size_in_dw)) == -1) {
272 int64_t need = item->size_in_dw+2048 -
273 (pool->size_in_dw - allocated);
274
275 need += 1024 - (need % 1024);
276
277 if (need > 0) {
278 compute_memory_grow_pool(pool,
279 pipe,
280 pool->size_in_dw + need);
281 }
282 else {
283 need = pool->size_in_dw / 10;
284 need += 1024 - (need % 1024);
285 compute_memory_grow_pool(pool,
286 pipe,
287 pool->size_in_dw + need);
288 }
289 }
290
291 item->start_in_dw = start_in_dw;
292 item->next = NULL;
293 item->prev = NULL;
294
295 if (pool->item_list) {
296 struct compute_memory_item *pos;
297
298 pos = compute_memory_postalloc_chunk(pool, start_in_dw);
299 item->prev = pos;
300 item->next = pos->next;
301 pos->next = item;
302
303 if (item->next) {
304 item->next->prev = item;
305 }
306 }
307 else {
308 pool->item_list = item;
309 }
310
311 allocated += item->size_in_dw;
312 }
313 }
314
315
316 void compute_memory_free(struct compute_memory_pool* pool, int64_t id)
317 {
318 struct compute_memory_item *item, *next;
319
320 for (item = pool->item_list; item; item = next) {
321 next = item->next;
322
323 if (item->id == id) {
324 if (item->prev) {
325 item->prev->next = item->next;
326 }
327 else {
328 pool->item_list = item->next;
329 }
330
331 if (item->next) {
332 item->next->prev = item->prev;
333 }
334
335 free(item);
336
337 return;
338 }
339 }
340
341 fprintf(stderr, "Internal error, invalid id %ld "
342 "for compute_memory_free\n", id);
343
344 assert(0 && "error");
345 }
346
347 /**
348 * Creates pending allocations
349 */
350 struct compute_memory_item* compute_memory_alloc(
351 struct compute_memory_pool* pool,
352 int64_t size_in_dw)
353 {
354 struct compute_memory_item *new_item;
355
356 COMPUTE_DBG("Alloc: %i\n", size_in_dw);
357
358 new_item = (struct compute_memory_item *)
359 CALLOC(sizeof(struct compute_memory_item), 1);
360 new_item->size_in_dw = size_in_dw;
361 new_item->start_in_dw = -1; /* mark pending */
362 new_item->id = pool->next_id++;
363 new_item->pool = pool;
364
365 struct compute_memory_item *last_item;
366
367 if (pool->item_list) {
368 for (last_item = pool->item_list; last_item->next;
369 last_item = last_item->next);
370
371 last_item->next = new_item;
372 new_item->prev = last_item;
373 }
374 else {
375 pool->item_list = new_item;
376 }
377
378 return new_item;
379 }
380
381 /**
382 * Transfer data host<->device, offset and size is in bytes
383 */
384 void compute_memory_transfer(
385 struct compute_memory_pool* pool,
386 struct pipe_context * pipe,
387 int device_to_host,
388 struct compute_memory_item* chunk,
389 void* data,
390 int offset_in_chunk,
391 int size)
392 {
393 int64_t aligned_size = pool->size_in_dw;
394 struct pipe_resource* gart = (struct pipe_resource*)pool->bo;
395 int64_t internal_offset = chunk->start_in_dw*4 + offset_in_chunk;
396
397 struct pipe_transfer *xfer;
398 uint32_t *map;
399
400 assert(gart);
401
402 if (device_to_host)
403 {
404 xfer = pipe->get_transfer(pipe, gart, 0, PIPE_TRANSFER_READ,
405 &(struct pipe_box) { .width = aligned_size,
406 .height = 1, .depth = 1 });
407 assert(xfer);
408 map = pipe->transfer_map(pipe, xfer);
409 assert(map);
410 memcpy(data, map + internal_offset, size);
411 pipe->transfer_unmap(pipe, xfer);
412 pipe->transfer_destroy(pipe, xfer);
413 } else {
414 xfer = pipe->get_transfer(pipe, gart, 0, PIPE_TRANSFER_WRITE,
415 &(struct pipe_box) { .width = aligned_size,
416 .height = 1, .depth = 1 });
417 assert(xfer);
418 map = pipe->transfer_map(pipe, xfer);
419 assert(map);
420 memcpy(map + internal_offset, data, size);
421 pipe->transfer_unmap(pipe, xfer);
422 pipe->transfer_destroy(pipe, xfer);
423 }
424 }
425
426 /**
427 * Transfer data between chunk<->data, it is for VRAM<->GART transfers
428 */
429 void compute_memory_transfer_direct(
430 struct compute_memory_pool* pool,
431 int chunk_to_data,
432 struct compute_memory_item* chunk,
433 struct r600_resource* data,
434 int offset_in_chunk,
435 int offset_in_data,
436 int size)
437 {
438 ///TODO: DMA
439 }