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 "util/u_memory.h"
29
30 #include "ilo_builder.h"
31 #include "ilo_builder_render.h" /* for ilo_builder_batch_patch_sba() */
32
33 enum ilo_builder_writer_flags {
34 /*
35 * When this bit is set, ilo_builder_begin() will not realllocate. New
36 * data will be appended instead.
37 */
38 WRITER_FLAG_APPEND = 1 << 0,
39
40 /*
41 * When this bit is set, the writer grows when full. When not, callers
42 * must make sure the writer never needs to grow.
43 */
44 WRITER_FLAG_GROW = 1 << 1,
45
46 /*
47 * The writer will be mapped directly.
48 */
49 WRITER_FLAG_MAP = 1 << 2,
50 };
51
52 /**
53 * Set the initial size and flags of a writer.
54 */
55 static void
ilo_builder_writer_init(struct ilo_builder * builder,enum ilo_builder_writer_type which)56 ilo_builder_writer_init(struct ilo_builder *builder,
57 enum ilo_builder_writer_type which)
58 {
59 struct ilo_builder_writer *writer = &builder->writers[which];
60
61 switch (which) {
62 case ILO_BUILDER_WRITER_BATCH:
63 writer->size = sizeof(uint32_t) * 8192;
64 break;
65 case ILO_BUILDER_WRITER_INSTRUCTION:
66 /*
67 * The EUs pretch some instructions. But since the kernel invalidates
68 * the instruction cache between batch buffers, we can set
69 * WRITER_FLAG_APPEND without worrying the EUs would see invalid
70 * instructions prefetched.
71 */
72 writer->flags = WRITER_FLAG_APPEND | WRITER_FLAG_GROW;
73 writer->size = 8192;
74 break;
75 default:
76 assert(!"unknown builder writer");
77 return;
78 break;
79 }
80
81 if (builder->dev->has_llc)
82 writer->flags |= WRITER_FLAG_MAP;
83 }
84
85 /**
86 * Free all resources used by a writer. Note that the initial size is not
87 * reset.
88 */
89 static void
ilo_builder_writer_reset(struct ilo_builder * builder,enum ilo_builder_writer_type which)90 ilo_builder_writer_reset(struct ilo_builder *builder,
91 enum ilo_builder_writer_type which)
92 {
93 struct ilo_builder_writer *writer = &builder->writers[which];
94
95 if (writer->ptr) {
96 if (writer->flags & WRITER_FLAG_MAP)
97 intel_bo_unmap(writer->bo);
98 else
99 FREE(writer->ptr);
100
101 writer->ptr = NULL;
102 }
103
104 intel_bo_unref(writer->bo);
105 writer->bo = NULL;
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
ilo_builder_writer_discard(struct ilo_builder * builder,enum ilo_builder_writer_type which)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 *
alloc_writer_bo(struct intel_winsys * winsys,enum ilo_builder_writer_type which,unsigned size)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 *
map_writer_bo(struct intel_bo * bo,unsigned flags)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
ilo_builder_writer_alloc_and_map(struct ilo_builder * builder,enum ilo_builder_writer_type which)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 intel_bo_unref(writer->bo);
172 writer->bo = bo;
173 } else if (writer->bo) {
174 /* reuse the old bo */
175 ilo_builder_writer_discard(builder, which);
176 } else {
177 return false;
178 }
179
180 writer->used = 0;
181 writer->stolen = 0;
182 writer->item_used = 0;
183 }
184
185 /* map the bo or allocate the staging system memory */
186 if (writer->flags & WRITER_FLAG_MAP)
187 writer->ptr = map_writer_bo(writer->bo, writer->flags);
188 else if (!writer->ptr)
189 writer->ptr = MALLOC(writer->size);
190
191 return (writer->ptr != NULL);
192 }
193
194 /**
195 * Unmap the buffer for submission.
196 */
197 static bool
ilo_builder_writer_unmap(struct ilo_builder * builder,enum ilo_builder_writer_type which)198 ilo_builder_writer_unmap(struct ilo_builder *builder,
199 enum ilo_builder_writer_type which)
200 {
201 struct ilo_builder_writer *writer = &builder->writers[which];
202 unsigned offset;
203 int err = 0;
204
205 if (writer->flags & WRITER_FLAG_MAP) {
206 intel_bo_unmap(writer->bo);
207 writer->ptr = NULL;
208 return true;
209 }
210
211 offset = builder->begin_used[which];
212 if (writer->used > offset) {
213 err = intel_bo_pwrite(writer->bo, offset, writer->used - offset,
214 (char *) writer->ptr + offset);
215 }
216
217 if (writer->stolen && !err) {
218 const unsigned offset = writer->size - writer->stolen;
219 err = intel_bo_pwrite(writer->bo, offset, writer->stolen,
220 (const char *) writer->ptr + offset);
221 }
222
223 /* keep writer->ptr */
224
225 return !err;
226 }
227
228 /**
229 * Grow a mapped writer to at least \p new_size.
230 */
231 bool
ilo_builder_writer_grow(struct ilo_builder * builder,enum ilo_builder_writer_type which,unsigned new_size,bool preserve)232 ilo_builder_writer_grow(struct ilo_builder *builder,
233 enum ilo_builder_writer_type which,
234 unsigned new_size, bool preserve)
235 {
236 struct ilo_builder_writer *writer = &builder->writers[which];
237 struct intel_bo *new_bo;
238 void *new_ptr;
239
240 if (!(writer->flags & WRITER_FLAG_GROW))
241 return false;
242
243 /* stolen data may already be referenced and cannot be moved */
244 if (writer->stolen)
245 return false;
246
247 if (new_size < writer->size << 1)
248 new_size = writer->size << 1;
249 /* STATE_BASE_ADDRESS requires page-aligned buffers */
250 new_size = align(new_size, 4096);
251
252 new_bo = alloc_writer_bo(builder->winsys, which, new_size);
253 if (!new_bo)
254 return false;
255
256 /* map and copy the data over */
257 if (writer->flags & WRITER_FLAG_MAP) {
258 new_ptr = map_writer_bo(new_bo, writer->flags);
259
260 /*
261 * When WRITER_FLAG_APPEND and WRITER_FLAG_GROW are both set, we may end
262 * up copying between two GTT-mapped BOs. That is slow. The issue
263 * could be solved by adding intel_bo_map_async(), or callers may choose
264 * to manually grow the writer without preserving the data.
265 */
266 if (new_ptr && preserve)
267 memcpy(new_ptr, writer->ptr, writer->used);
268 } else if (preserve) {
269 new_ptr = REALLOC(writer->ptr, writer->size, new_size);
270 } else {
271 new_ptr = MALLOC(new_size);
272 }
273
274 if (!new_ptr) {
275 intel_bo_unref(new_bo);
276 return false;
277 }
278
279 if (writer->flags & WRITER_FLAG_MAP)
280 intel_bo_unmap(writer->bo);
281 else if (!preserve)
282 FREE(writer->ptr);
283
284 intel_bo_unref(writer->bo);
285
286 writer->size = new_size;
287 writer->bo = new_bo;
288 writer->ptr = new_ptr;
289
290 return true;
291 }
292
293 /**
294 * Record an item for later decoding.
295 */
296 bool
ilo_builder_writer_record(struct ilo_builder * builder,enum ilo_builder_writer_type which,enum ilo_builder_item_type type,unsigned offset,unsigned size)297 ilo_builder_writer_record(struct ilo_builder *builder,
298 enum ilo_builder_writer_type which,
299 enum ilo_builder_item_type type,
300 unsigned offset, unsigned size)
301 {
302 struct ilo_builder_writer *writer = &builder->writers[which];
303 struct ilo_builder_item *item;
304
305 if (writer->item_used == writer->item_alloc) {
306 const unsigned new_alloc = (writer->item_alloc) ?
307 writer->item_alloc << 1 : 256;
308 struct ilo_builder_item *items;
309
310 items = REALLOC(writer->items,
311 sizeof(writer->items[0]) * writer->item_alloc,
312 sizeof(writer->items[0]) * new_alloc);
313 if (!items)
314 return false;
315
316 writer->items = items;
317 writer->item_alloc = new_alloc;
318 }
319
320 item = &writer->items[writer->item_used++];
321 item->type = type;
322 item->offset = offset;
323 item->size = size;
324
325 return true;
326 }
327
328 /**
329 * Initialize the builder.
330 */
331 void
ilo_builder_init(struct ilo_builder * builder,const struct ilo_dev * dev,struct intel_winsys * winsys)332 ilo_builder_init(struct ilo_builder *builder,
333 const struct ilo_dev *dev,
334 struct intel_winsys *winsys)
335 {
336 unsigned i;
337
338 assert(ilo_is_zeroed(builder, sizeof(*builder)));
339
340 builder->dev = dev;
341 builder->winsys = winsys;
342
343 /* gen6_SURFACE_STATE() may override this */
344 switch (ilo_dev_gen(dev)) {
345 case ILO_GEN(8):
346 builder->mocs = GEN8_MOCS_MT_WB | GEN8_MOCS_CT_L3;
347 break;
348 case ILO_GEN(7.5):
349 case ILO_GEN(7):
350 builder->mocs = GEN7_MOCS_L3_WB;
351 break;
352 default:
353 builder->mocs = 0;
354 break;
355 }
356
357 for (i = 0; i < ILO_BUILDER_WRITER_COUNT; i++)
358 ilo_builder_writer_init(builder, i);
359 }
360
361 /**
362 * Reset the builder and free all resources used. After resetting, the
363 * builder behaves as if it is newly initialized, except for potentially
364 * larger initial bo sizes.
365 */
366 void
ilo_builder_reset(struct ilo_builder * builder)367 ilo_builder_reset(struct ilo_builder *builder)
368 {
369 unsigned i;
370
371 for (i = 0; i < ILO_BUILDER_WRITER_COUNT; i++)
372 ilo_builder_writer_reset(builder, i);
373 }
374
375 /**
376 * Allocate and map the BOs. It may re-allocate or reuse existing BOs if
377 * there is any.
378 *
379 * Most builder functions can only be called after ilo_builder_begin() and
380 * before ilo_builder_end().
381 */
382 bool
ilo_builder_begin(struct ilo_builder * builder)383 ilo_builder_begin(struct ilo_builder *builder)
384 {
385 unsigned i;
386
387 for (i = 0; i < ILO_BUILDER_WRITER_COUNT; i++) {
388 if (!ilo_builder_writer_alloc_and_map(builder, i)) {
389 ilo_builder_reset(builder);
390 return false;
391 }
392
393 builder->begin_used[i] = builder->writers[i].used;
394 }
395
396 builder->unrecoverable_error = false;
397 builder->sba_instruction_pos = 0;
398
399 return true;
400 }
401
402 /**
403 * Unmap BOs and make sure the written data landed the BOs. The batch buffer
404 * ready for submission is returned.
405 */
406 struct intel_bo *
ilo_builder_end(struct ilo_builder * builder,unsigned * used)407 ilo_builder_end(struct ilo_builder *builder, unsigned *used)
408 {
409 struct ilo_builder_writer *bat;
410 unsigned i;
411
412 ilo_builder_batch_patch_sba(builder);
413
414 assert(ilo_builder_validate(builder, 0, NULL));
415
416 for (i = 0; i < ILO_BUILDER_WRITER_COUNT; i++) {
417 if (!ilo_builder_writer_unmap(builder, i))
418 builder->unrecoverable_error = true;
419 }
420
421 if (builder->unrecoverable_error)
422 return NULL;
423
424 bat = &builder->writers[ILO_BUILDER_WRITER_BATCH];
425
426 *used = bat->used;
427
428 return bat->bo;
429 }
430
431 /**
432 * Return true if the builder is in a valid state, after accounting for the
433 * additional BOs specified. The additional BOs can be listed to avoid
434 * snapshotting and restoring when they are known ahead of time.
435 *
436 * The number of additional BOs should not be more than a few. Like two, for
437 * copying between two BOs.
438 *
439 * Callers must make sure the builder is in a valid state when
440 * ilo_builder_end() is called.
441 */
442 bool
ilo_builder_validate(struct ilo_builder * builder,unsigned bo_count,struct intel_bo ** bos)443 ilo_builder_validate(struct ilo_builder *builder,
444 unsigned bo_count, struct intel_bo **bos)
445 {
446 const unsigned max_bo_count = 2;
447 struct intel_bo *bos_to_submit[ILO_BUILDER_WRITER_COUNT + max_bo_count];
448 int i;
449
450 for (i = 0; i < ILO_BUILDER_WRITER_COUNT; i++)
451 bos_to_submit[i] = builder->writers[i].bo;
452
453 if (bo_count) {
454 assert(bo_count <= max_bo_count);
455 if (bo_count > max_bo_count)
456 return false;
457
458 memcpy(&bos_to_submit[ILO_BUILDER_WRITER_COUNT],
459 bos, sizeof(*bos) * bo_count);
460 i += bo_count;
461 }
462
463 return intel_winsys_can_submit_bo(builder->winsys, bos_to_submit, i);
464 }
465
466 /**
467 * Take a snapshot of the writer state.
468 */
469 void
ilo_builder_batch_snapshot(const struct ilo_builder * builder,struct ilo_builder_snapshot * snapshot)470 ilo_builder_batch_snapshot(const struct ilo_builder *builder,
471 struct ilo_builder_snapshot *snapshot)
472 {
473 const enum ilo_builder_writer_type which = ILO_BUILDER_WRITER_BATCH;
474 const struct ilo_builder_writer *writer = &builder->writers[which];
475
476 snapshot->reloc_count = intel_bo_get_reloc_count(writer->bo);
477 snapshot->used = writer->used;
478 snapshot->stolen = writer->stolen;
479 snapshot->item_used = writer->item_used;
480 }
481
482 /**
483 * Restore the writer state to when the snapshot was taken, except that it
484 * does not (unnecessarily) shrink BOs or the item array.
485 */
486 void
ilo_builder_batch_restore(struct ilo_builder * builder,const struct ilo_builder_snapshot * snapshot)487 ilo_builder_batch_restore(struct ilo_builder *builder,
488 const struct ilo_builder_snapshot *snapshot)
489 {
490 const enum ilo_builder_writer_type which = ILO_BUILDER_WRITER_BATCH;
491 struct ilo_builder_writer *writer = &builder->writers[which];
492
493 intel_bo_truncate_relocs(writer->bo, snapshot->reloc_count);
494 writer->used = snapshot->used;
495 writer->stolen = snapshot->stolen;
496 writer->item_used = snapshot->item_used;
497 }
498