• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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