52c4b2184358f27314029e2ee2508305c363f7f8
[mesa.git] / src / gallium / drivers / ilo / ilo_builder.c
1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 2014 LunarG, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 * Authors:
25 * Chia-I Wu <olv@lunarg.com>
26 */
27
28 #include "ilo_builder.h"
29 #include "ilo_builder_render.h" /* for ilo_builder_batch_patch_sba() */
30
31 enum ilo_builder_writer_flags {
32 /*
33 * When this bit is set, ilo_builder_begin() will not realllocate. New
34 * data will be appended instead.
35 */
36 WRITER_FLAG_APPEND = 1 << 0,
37
38 /*
39 * When this bit is set, the writer grows when full. When not, callers
40 * must make sure the writer never needs to grow.
41 */
42 WRITER_FLAG_GROW = 1 << 1,
43
44 /*
45 * The writer will be mapped directly.
46 */
47 WRITER_FLAG_MAP = 1 << 2,
48 };
49
50 /**
51 * Set the initial size and flags of a writer.
52 */
53 static void
54 ilo_builder_writer_init(struct ilo_builder *builder,
55 enum ilo_builder_writer_type which)
56 {
57 struct ilo_builder_writer *writer = &builder->writers[which];
58
59 switch (which) {
60 case ILO_BUILDER_WRITER_BATCH:
61 writer->size = sizeof(uint32_t) * 8192;
62 break;
63 case ILO_BUILDER_WRITER_INSTRUCTION:
64 /*
65 * The EUs pretch some instructions. But since the kernel invalidates
66 * the instruction cache between batch buffers, we can set
67 * WRITER_FLAG_APPEND without worrying the EUs would see invalid
68 * instructions prefetched.
69 */
70 writer->flags = WRITER_FLAG_APPEND | WRITER_FLAG_GROW;
71 writer->size = 8192;
72 break;
73 default:
74 assert(!"unknown builder writer");
75 return;
76 break;
77 }
78
79 if (builder->dev->has_llc)
80 writer->flags |= WRITER_FLAG_MAP;
81 }
82
83 /**
84 * Free all resources used by a writer. Note that the initial size is not
85 * reset.
86 */
87 static void
88 ilo_builder_writer_reset(struct ilo_builder *builder,
89 enum ilo_builder_writer_type which)
90 {
91 struct ilo_builder_writer *writer = &builder->writers[which];
92
93 if (writer->ptr) {
94 if (writer->flags & WRITER_FLAG_MAP)
95 intel_bo_unmap(writer->bo);
96 else
97 FREE(writer->ptr);
98
99 writer->ptr = NULL;
100 }
101
102 if (writer->bo) {
103 intel_bo_unreference(writer->bo);
104 writer->bo = NULL;
105 }
106
107 writer->used = 0;
108 writer->stolen = 0;
109
110 if (writer->items) {
111 FREE(writer->items);
112 writer->item_alloc = 0;
113 writer->item_used = 0;
114 }
115 }
116
117 /**
118 * Discard everything written so far.
119 */
120 void
121 ilo_builder_writer_discard(struct ilo_builder *builder,
122 enum ilo_builder_writer_type which)
123 {
124 struct ilo_builder_writer *writer = &builder->writers[which];
125
126 intel_bo_truncate_relocs(writer->bo, 0);
127 writer->used = 0;
128 writer->stolen = 0;
129 writer->item_used = 0;
130 }
131
132 static struct intel_bo *
133 alloc_writer_bo(struct intel_winsys *winsys,
134 enum ilo_builder_writer_type which,
135 unsigned size)
136 {
137 static const char *writer_names[ILO_BUILDER_WRITER_COUNT] = {
138 [ILO_BUILDER_WRITER_BATCH] = "batch",
139 [ILO_BUILDER_WRITER_INSTRUCTION] = "instruction",
140 };
141
142 return intel_winsys_alloc_bo(winsys, writer_names[which], size, true);
143 }
144
145 static void *
146 map_writer_bo(struct intel_bo *bo, unsigned flags)
147 {
148 assert(flags & WRITER_FLAG_MAP);
149
150 if (flags & WRITER_FLAG_APPEND)
151 return intel_bo_map_gtt_async(bo);
152 else
153 return intel_bo_map(bo, true);
154 }
155
156 /**
157 * Allocate and map the buffer for writing.
158 */
159 static bool
160 ilo_builder_writer_alloc_and_map(struct ilo_builder *builder,
161 enum ilo_builder_writer_type which)
162 {
163 struct ilo_builder_writer *writer = &builder->writers[which];
164
165 /* allocate a new bo when not appending */
166 if (!(writer->flags & WRITER_FLAG_APPEND) || !writer->bo) {
167 struct intel_bo *bo;
168
169 bo = alloc_writer_bo(builder->winsys, which, writer->size);
170 if (bo) {
171 if (writer->bo)
172 intel_bo_unreference(writer->bo);
173 writer->bo = bo;
174 } else if (writer->bo) {
175 /* reuse the old bo */
176 ilo_builder_writer_discard(builder, which);
177 } else {
178 return false;
179 }
180
181 writer->used = 0;
182 writer->stolen = 0;
183 writer->item_used = 0;
184 }
185
186 /* map the bo or allocate the staging system memory */
187 if (writer->flags & WRITER_FLAG_MAP)
188 writer->ptr = map_writer_bo(writer->bo, writer->flags);
189 else if (!writer->ptr)
190 writer->ptr = MALLOC(writer->size);
191
192 return (writer->ptr != NULL);
193 }
194
195 /**
196 * Unmap the buffer for submission.
197 */
198 static bool
199 ilo_builder_writer_unmap(struct ilo_builder *builder,
200 enum ilo_builder_writer_type which)
201 {
202 struct ilo_builder_writer *writer = &builder->writers[which];
203 unsigned offset;
204 int err = 0;
205
206 if (writer->flags & WRITER_FLAG_MAP) {
207 intel_bo_unmap(writer->bo);
208 writer->ptr = NULL;
209 return true;
210 }
211
212 offset = builder->begin_used[which];
213 if (writer->used > offset) {
214 err = intel_bo_pwrite(writer->bo, offset, writer->used - offset,
215 (char *) writer->ptr + offset);
216 }
217
218 if (writer->stolen && !err) {
219 const unsigned offset = writer->size - writer->stolen;
220 err = intel_bo_pwrite(writer->bo, offset, writer->stolen,
221 (const char *) writer->ptr + offset);
222 }
223
224 /* keep writer->ptr */
225
226 return !err;
227 }
228
229 /**
230 * Grow a mapped writer to at least \p new_size.
231 */
232 bool
233 ilo_builder_writer_grow(struct ilo_builder *builder,
234 enum ilo_builder_writer_type which,
235 unsigned new_size, bool preserve)
236 {
237 struct ilo_builder_writer *writer = &builder->writers[which];
238 struct intel_bo *new_bo;
239 void *new_ptr;
240
241 if (!(writer->flags & WRITER_FLAG_GROW))
242 return false;
243
244 /* stolen data may already be referenced and cannot be moved */
245 if (writer->stolen)
246 return false;
247
248 if (new_size < writer->size << 1)
249 new_size = writer->size << 1;
250 /* STATE_BASE_ADDRESS requires page-aligned buffers */
251 new_size = align(new_size, 4096);
252
253 new_bo = alloc_writer_bo(builder->winsys, which, new_size);
254 if (!new_bo)
255 return false;
256
257 /* map and copy the data over */
258 if (writer->flags & WRITER_FLAG_MAP) {
259 new_ptr = map_writer_bo(new_bo, writer->flags);
260
261 /*
262 * When WRITER_FLAG_APPEND and WRITER_FLAG_GROW are both set, we may end
263 * up copying between two GTT-mapped BOs. That is slow. The issue
264 * could be solved by adding intel_bo_map_async(), or callers may choose
265 * to manually grow the writer without preserving the data.
266 */
267 if (new_ptr && preserve)
268 memcpy(new_ptr, writer->ptr, writer->used);
269 } else if (preserve) {
270 new_ptr = REALLOC(writer->ptr, writer->size, new_size);
271 } else {
272 new_ptr = MALLOC(new_size);
273 }
274
275 if (!new_ptr) {
276 intel_bo_unreference(new_bo);
277 return false;
278 }
279
280 if (writer->flags & WRITER_FLAG_MAP)
281 intel_bo_unmap(writer->bo);
282 else if (!preserve)
283 FREE(writer->ptr);
284
285 intel_bo_unreference(writer->bo);
286
287 writer->size = new_size;
288 writer->bo = new_bo;
289 writer->ptr = new_ptr;
290
291 return true;
292 }
293
294 /**
295 * Record an item for later decoding.
296 */
297 bool
298 ilo_builder_writer_record(struct ilo_builder *builder,
299 enum ilo_builder_writer_type which,
300 enum ilo_builder_item_type type,
301 unsigned offset, unsigned size)
302 {
303 struct ilo_builder_writer *writer = &builder->writers[which];
304 struct ilo_builder_item *item;
305
306 if (writer->item_used == writer->item_alloc) {
307 const unsigned new_alloc = (writer->item_alloc) ?
308 writer->item_alloc << 1 : 256;
309 struct ilo_builder_item *items;
310
311 items = REALLOC(writer->items,
312 sizeof(writer->items[0]) * writer->item_alloc,
313 sizeof(writer->items[0]) * new_alloc);
314 if (!items)
315 return false;
316
317 writer->items = items;
318 writer->item_alloc = new_alloc;
319 }
320
321 item = &writer->items[writer->item_used++];
322 item->type = type;
323 item->offset = offset;
324 item->size = size;
325
326 return true;
327 }
328
329 /**
330 * Initialize the builder.
331 */
332 void
333 ilo_builder_init(struct ilo_builder *builder,
334 const struct ilo_dev_info *dev,
335 struct intel_winsys *winsys)
336 {
337 int i;
338
339 memset(builder, 0, sizeof(*builder));
340
341 builder->dev = dev;
342 builder->winsys = winsys;
343
344 for (i = 0; i < ILO_BUILDER_WRITER_COUNT; i++)
345 ilo_builder_writer_init(builder, i);
346 }
347
348 /**
349 * Reset the builder and free all resources used. After resetting, the
350 * builder behaves as if it is newly initialized, except for potentially
351 * larger initial bo sizes.
352 */
353 void
354 ilo_builder_reset(struct ilo_builder *builder)
355 {
356 int i;
357
358 for (i = 0; i < ILO_BUILDER_WRITER_COUNT; i++)
359 ilo_builder_writer_reset(builder, i);
360 }
361
362 /**
363 * Allocate and map the BOs. It may re-allocate or reuse existing BOs if
364 * there is any.
365 *
366 * Most builder functions can only be called after ilo_builder_begin() and
367 * before ilo_builder_end().
368 */
369 bool
370 ilo_builder_begin(struct ilo_builder *builder)
371 {
372 int i;
373
374 for (i = 0; i < ILO_BUILDER_WRITER_COUNT; i++) {
375 if (!ilo_builder_writer_alloc_and_map(builder, i)) {
376 ilo_builder_reset(builder);
377 return false;
378 }
379
380 builder->begin_used[i] = builder->writers[i].used;
381 }
382
383 builder->unrecoverable_error = false;
384 builder->sba_instruction_pos = 0;
385
386 return true;
387 }
388
389 /**
390 * Unmap BOs and make sure the written data landed the BOs. The batch buffer
391 * ready for submission is returned.
392 */
393 struct intel_bo *
394 ilo_builder_end(struct ilo_builder *builder, unsigned *used)
395 {
396 struct ilo_builder_writer *bat;
397 int i;
398
399 ilo_builder_batch_patch_sba(builder);
400
401 assert(ilo_builder_validate(builder, 0, NULL));
402
403 for (i = 0; i < ILO_BUILDER_WRITER_COUNT; i++) {
404 if (!ilo_builder_writer_unmap(builder, i))
405 builder->unrecoverable_error = true;
406 }
407
408 if (builder->unrecoverable_error)
409 return NULL;
410
411 bat = &builder->writers[ILO_BUILDER_WRITER_BATCH];
412
413 *used = bat->used;
414
415 return bat->bo;
416 }
417
418 /**
419 * Return true if the builder is in a valid state, after accounting for the
420 * additional BOs specified. The additional BOs can be listed to avoid
421 * snapshotting and restoring when they are known ahead of time.
422 *
423 * The number of additional BOs should not be more than a few. Like two, for
424 * copying between two BOs.
425 *
426 * Callers must make sure the builder is in a valid state when
427 * ilo_builder_end() is called.
428 */
429 bool
430 ilo_builder_validate(struct ilo_builder *builder,
431 unsigned bo_count, struct intel_bo **bos)
432 {
433 const unsigned max_bo_count = 2;
434 struct intel_bo *bos_to_submit[ILO_BUILDER_WRITER_COUNT + max_bo_count];
435 int i;
436
437 for (i = 0; i < ILO_BUILDER_WRITER_COUNT; i++)
438 bos_to_submit[i] = builder->writers[i].bo;
439
440 if (bo_count) {
441 assert(bo_count <= max_bo_count);
442 if (bo_count > max_bo_count)
443 return false;
444
445 memcpy(&bos_to_submit[ILO_BUILDER_WRITER_COUNT],
446 bos, sizeof(*bos) * bo_count);
447 i += bo_count;
448 }
449
450 return intel_winsys_can_submit_bo(builder->winsys, bos_to_submit, i);
451 }
452
453 /**
454 * Take a snapshot of the writer state.
455 */
456 void
457 ilo_builder_batch_snapshot(const struct ilo_builder *builder,
458 struct ilo_builder_snapshot *snapshot)
459 {
460 const enum ilo_builder_writer_type which = ILO_BUILDER_WRITER_BATCH;
461 const struct ilo_builder_writer *writer = &builder->writers[which];
462
463 snapshot->reloc_count = intel_bo_get_reloc_count(writer->bo);
464 snapshot->used = writer->used;
465 snapshot->stolen = writer->stolen;
466 snapshot->item_used = writer->item_used;
467 }
468
469 /**
470 * Restore the writer state to when the snapshot was taken, except that it
471 * does not (unnecessarily) shrink BOs or the item array.
472 */
473 void
474 ilo_builder_batch_restore(struct ilo_builder *builder,
475 const struct ilo_builder_snapshot *snapshot)
476 {
477 const enum ilo_builder_writer_type which = ILO_BUILDER_WRITER_BATCH;
478 struct ilo_builder_writer *writer = &builder->writers[which];
479
480 intel_bo_truncate_relocs(writer->bo, snapshot->reloc_count);
481 writer->used = snapshot->used;
482 writer->stolen = snapshot->stolen;
483 writer->item_used = snapshot->item_used;
484 }