v3d: Add support for handling OOM signals from the simulator.
[mesa.git] / src / gallium / drivers / v3d / v3d_job.c
1 /*
2 * Copyright © 2014-2017 Broadcom
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 /** @file v3d_job.c
25 *
26 * Functions for submitting VC5 render jobs to the kernel.
27 */
28
29 #include <xf86drm.h>
30 #include "v3d_context.h"
31 /* The OQ/semaphore packets are the same across V3D versions. */
32 #define V3D_VERSION 33
33 #include "broadcom/cle/v3dx_pack.h"
34 #include "broadcom/common/v3d_macros.h"
35 #include "util/hash_table.h"
36 #include "util/ralloc.h"
37 #include "util/set.h"
38 #include "broadcom/clif/clif_dump.h"
39
40 static void
41 remove_from_ht(struct hash_table *ht, void *key)
42 {
43 struct hash_entry *entry = _mesa_hash_table_search(ht, key);
44 _mesa_hash_table_remove(ht, entry);
45 }
46
47 static void
48 v3d_job_free(struct v3d_context *v3d, struct v3d_job *job)
49 {
50 set_foreach(job->bos, entry) {
51 struct v3d_bo *bo = (struct v3d_bo *)entry->key;
52 v3d_bo_unreference(&bo);
53 }
54
55 remove_from_ht(v3d->jobs, &job->key);
56
57 if (job->write_prscs) {
58 set_foreach(job->write_prscs, entry) {
59 const struct pipe_resource *prsc = entry->key;
60
61 remove_from_ht(v3d->write_jobs, (void *)prsc);
62 }
63 }
64
65 for (int i = 0; i < V3D_MAX_DRAW_BUFFERS; i++) {
66 if (job->cbufs[i]) {
67 remove_from_ht(v3d->write_jobs, job->cbufs[i]->texture);
68 pipe_surface_reference(&job->cbufs[i], NULL);
69 }
70 }
71 if (job->zsbuf) {
72 struct v3d_resource *rsc = v3d_resource(job->zsbuf->texture);
73 if (rsc->separate_stencil)
74 remove_from_ht(v3d->write_jobs,
75 &rsc->separate_stencil->base);
76
77 remove_from_ht(v3d->write_jobs, job->zsbuf->texture);
78 pipe_surface_reference(&job->zsbuf, NULL);
79 }
80
81 if (v3d->job == job)
82 v3d->job = NULL;
83
84 v3d_destroy_cl(&job->bcl);
85 v3d_destroy_cl(&job->rcl);
86 v3d_destroy_cl(&job->indirect);
87 v3d_bo_unreference(&job->tile_alloc);
88 v3d_bo_unreference(&job->tile_state);
89
90 ralloc_free(job);
91 }
92
93 static struct v3d_job *
94 v3d_job_create(struct v3d_context *v3d)
95 {
96 struct v3d_job *job = rzalloc(v3d, struct v3d_job);
97
98 job->v3d = v3d;
99
100 v3d_init_cl(job, &job->bcl);
101 v3d_init_cl(job, &job->rcl);
102 v3d_init_cl(job, &job->indirect);
103
104 job->draw_min_x = ~0;
105 job->draw_min_y = ~0;
106 job->draw_max_x = 0;
107 job->draw_max_y = 0;
108
109 job->bos = _mesa_set_create(job,
110 _mesa_hash_pointer,
111 _mesa_key_pointer_equal);
112 return job;
113 }
114
115 void
116 v3d_job_add_bo(struct v3d_job *job, struct v3d_bo *bo)
117 {
118 if (!bo)
119 return;
120
121 if (_mesa_set_search(job->bos, bo))
122 return;
123
124 v3d_bo_reference(bo);
125 _mesa_set_add(job->bos, bo);
126 job->referenced_size += bo->size;
127
128 uint32_t *bo_handles = (void *)(uintptr_t)job->submit.bo_handles;
129
130 if (job->submit.bo_handle_count >= job->bo_handles_size) {
131 job->bo_handles_size = MAX2(4, job->bo_handles_size * 2);
132 bo_handles = reralloc(job, bo_handles,
133 uint32_t, job->bo_handles_size);
134 job->submit.bo_handles = (uintptr_t)(void *)bo_handles;
135 }
136 bo_handles[job->submit.bo_handle_count++] = bo->handle;
137 }
138
139 void
140 v3d_job_add_write_resource(struct v3d_job *job, struct pipe_resource *prsc)
141 {
142 struct v3d_context *v3d = job->v3d;
143
144 if (!job->write_prscs) {
145 job->write_prscs = _mesa_set_create(job,
146 _mesa_hash_pointer,
147 _mesa_key_pointer_equal);
148 }
149
150 _mesa_set_add(job->write_prscs, prsc);
151 _mesa_hash_table_insert(v3d->write_jobs, prsc, job);
152 }
153
154 void
155 v3d_flush_jobs_writing_resource(struct v3d_context *v3d,
156 struct pipe_resource *prsc)
157 {
158 struct hash_entry *entry = _mesa_hash_table_search(v3d->write_jobs,
159 prsc);
160 if (entry) {
161 struct v3d_job *job = entry->data;
162 v3d_job_submit(v3d, job);
163 }
164 }
165
166 void
167 v3d_flush_jobs_reading_resource(struct v3d_context *v3d,
168 struct pipe_resource *prsc)
169 {
170 struct v3d_resource *rsc = v3d_resource(prsc);
171
172 v3d_flush_jobs_writing_resource(v3d, prsc);
173
174 hash_table_foreach(v3d->jobs, entry) {
175 struct v3d_job *job = entry->data;
176
177 if (_mesa_set_search(job->bos, rsc->bo)) {
178 v3d_job_submit(v3d, job);
179 /* Reminder: v3d->jobs is safe to keep iterating even
180 * after deletion of an entry.
181 */
182 continue;
183 }
184 }
185 }
186
187 static void
188 v3d_job_set_tile_buffer_size(struct v3d_job *job)
189 {
190 static const uint8_t tile_sizes[] = {
191 64, 64,
192 64, 32,
193 32, 32,
194 32, 16,
195 16, 16,
196 };
197 int tile_size_index = 0;
198 if (job->msaa)
199 tile_size_index += 2;
200
201 if (job->cbufs[3] || job->cbufs[2])
202 tile_size_index += 2;
203 else if (job->cbufs[1])
204 tile_size_index++;
205
206 int max_bpp = RENDER_TARGET_MAXIMUM_32BPP;
207 for (int i = 0; i < V3D_MAX_DRAW_BUFFERS; i++) {
208 if (job->cbufs[i]) {
209 struct v3d_surface *surf = v3d_surface(job->cbufs[i]);
210 max_bpp = MAX2(max_bpp, surf->internal_bpp);
211 }
212 }
213 job->internal_bpp = max_bpp;
214 STATIC_ASSERT(RENDER_TARGET_MAXIMUM_32BPP == 0);
215 tile_size_index += max_bpp;
216
217 assert(tile_size_index < ARRAY_SIZE(tile_sizes));
218 job->tile_width = tile_sizes[tile_size_index * 2 + 0];
219 job->tile_height = tile_sizes[tile_size_index * 2 + 1];
220 }
221
222 /**
223 * Returns a v3d_job struture for tracking V3D rendering to a particular FBO.
224 *
225 * If we've already started rendering to this FBO, then return the same job,
226 * otherwise make a new one. If we're beginning rendering to an FBO, make
227 * sure that any previous reads of the FBO (or writes to its color/Z surfaces)
228 * have been flushed.
229 */
230 struct v3d_job *
231 v3d_get_job(struct v3d_context *v3d,
232 struct pipe_surface **cbufs, struct pipe_surface *zsbuf)
233 {
234 /* Return the existing job for this FBO if we have one */
235 struct v3d_job_key local_key = {
236 .cbufs = {
237 cbufs[0],
238 cbufs[1],
239 cbufs[2],
240 cbufs[3],
241 },
242 .zsbuf = zsbuf,
243 };
244 struct hash_entry *entry = _mesa_hash_table_search(v3d->jobs,
245 &local_key);
246 if (entry)
247 return entry->data;
248
249 /* Creating a new job. Make sure that any previous jobs reading or
250 * writing these buffers are flushed.
251 */
252 struct v3d_job *job = v3d_job_create(v3d);
253
254 for (int i = 0; i < V3D_MAX_DRAW_BUFFERS; i++) {
255 if (cbufs[i]) {
256 v3d_flush_jobs_reading_resource(v3d, cbufs[i]->texture);
257 pipe_surface_reference(&job->cbufs[i], cbufs[i]);
258
259 if (cbufs[i]->texture->nr_samples > 1)
260 job->msaa = true;
261 }
262 }
263 if (zsbuf) {
264 v3d_flush_jobs_reading_resource(v3d, zsbuf->texture);
265 pipe_surface_reference(&job->zsbuf, zsbuf);
266 if (zsbuf->texture->nr_samples > 1)
267 job->msaa = true;
268 }
269
270 for (int i = 0; i < V3D_MAX_DRAW_BUFFERS; i++) {
271 if (cbufs[i])
272 _mesa_hash_table_insert(v3d->write_jobs,
273 cbufs[i]->texture, job);
274 }
275 if (zsbuf) {
276 _mesa_hash_table_insert(v3d->write_jobs, zsbuf->texture, job);
277
278 struct v3d_resource *rsc = v3d_resource(zsbuf->texture);
279 if (rsc->separate_stencil) {
280 v3d_flush_jobs_reading_resource(v3d,
281 &rsc->separate_stencil->base);
282 _mesa_hash_table_insert(v3d->write_jobs,
283 &rsc->separate_stencil->base,
284 job);
285 }
286 }
287
288 memcpy(&job->key, &local_key, sizeof(local_key));
289 _mesa_hash_table_insert(v3d->jobs, &job->key, job);
290
291 return job;
292 }
293
294 struct v3d_job *
295 v3d_get_job_for_fbo(struct v3d_context *v3d)
296 {
297 if (v3d->job)
298 return v3d->job;
299
300 struct pipe_surface **cbufs = v3d->framebuffer.cbufs;
301 struct pipe_surface *zsbuf = v3d->framebuffer.zsbuf;
302 struct v3d_job *job = v3d_get_job(v3d, cbufs, zsbuf);
303
304 if (v3d->framebuffer.samples >= 1)
305 job->msaa = true;
306
307 v3d_job_set_tile_buffer_size(job);
308
309 /* The dirty flags are tracking what's been updated while v3d->job has
310 * been bound, so set them all to ~0 when switching between jobs. We
311 * also need to reset all state at the start of rendering.
312 */
313 v3d->dirty = ~0;
314
315 /* If we're binding to uninitialized buffers, no need to load their
316 * contents before drawing.
317 */
318 for (int i = 0; i < 4; i++) {
319 if (cbufs[i]) {
320 struct v3d_resource *rsc = v3d_resource(cbufs[i]->texture);
321 if (!rsc->writes)
322 job->clear |= PIPE_CLEAR_COLOR0 << i;
323 }
324 }
325
326 if (zsbuf) {
327 struct v3d_resource *rsc = v3d_resource(zsbuf->texture);
328 if (!rsc->writes)
329 job->clear |= PIPE_CLEAR_DEPTH | PIPE_CLEAR_STENCIL;
330 }
331
332 job->draw_tiles_x = DIV_ROUND_UP(v3d->framebuffer.width,
333 job->tile_width);
334 job->draw_tiles_y = DIV_ROUND_UP(v3d->framebuffer.height,
335 job->tile_height);
336
337 v3d->job = job;
338
339 return job;
340 }
341
342 static void
343 v3d_clif_dump(struct v3d_context *v3d, struct v3d_job *job)
344 {
345 if (!(V3D_DEBUG & (V3D_DEBUG_CL | V3D_DEBUG_CLIF)))
346 return;
347
348 struct clif_dump *clif = clif_dump_init(&v3d->screen->devinfo,
349 stderr,
350 V3D_DEBUG & V3D_DEBUG_CL);
351
352 set_foreach(job->bos, entry) {
353 struct v3d_bo *bo = (void *)entry->key;
354 char *name = ralloc_asprintf(NULL, "%s_0x%x",
355 bo->name, bo->offset);
356
357 v3d_bo_map(bo);
358 clif_dump_add_bo(clif, name, bo->offset, bo->size, bo->map);
359
360 ralloc_free(name);
361 }
362
363 clif_dump(clif, &job->submit);
364
365 clif_dump_destroy(clif);
366 }
367
368 /**
369 * Submits the job to the kernel and then reinitializes it.
370 */
371 void
372 v3d_job_submit(struct v3d_context *v3d, struct v3d_job *job)
373 {
374 MAYBE_UNUSED struct v3d_screen *screen = v3d->screen;
375
376 if (!job->needs_flush)
377 goto done;
378
379 if (v3d->screen->devinfo.ver >= 41)
380 v3d41_emit_rcl(job);
381 else
382 v3d33_emit_rcl(job);
383
384 if (cl_offset(&job->bcl) > 0) {
385 if (screen->devinfo.ver >= 41)
386 v3d41_bcl_epilogue(v3d, job);
387 else
388 v3d33_bcl_epilogue(v3d, job);
389 }
390
391 /* While the RCL will implicitly depend on the last RCL to have
392 * finished, we also need to block on any previous TFU job we may have
393 * dispatched.
394 */
395 job->submit.in_sync_rcl = v3d->out_sync;
396
397 /* Update the sync object for the last rendering by our context. */
398 job->submit.out_sync = v3d->out_sync;
399
400 job->submit.bcl_end = job->bcl.bo->offset + cl_offset(&job->bcl);
401 job->submit.rcl_end = job->rcl.bo->offset + cl_offset(&job->rcl);
402
403 /* On V3D 4.1, the tile alloc/state setup moved to register writes
404 * instead of binner packets.
405 */
406 if (screen->devinfo.ver >= 41) {
407 v3d_job_add_bo(job, job->tile_alloc);
408 job->submit.qma = job->tile_alloc->offset;
409 job->submit.qms = job->tile_alloc->size;
410
411 v3d_job_add_bo(job, job->tile_state);
412 job->submit.qts = job->tile_state->offset;
413 }
414
415 v3d_clif_dump(v3d, job);
416
417 if (!(V3D_DEBUG & V3D_DEBUG_NORAST)) {
418 int ret;
419
420 ret = v3d_ioctl(v3d->fd, DRM_IOCTL_V3D_SUBMIT_CL, &job->submit);
421 static bool warned = false;
422 if (ret && !warned) {
423 fprintf(stderr, "Draw call returned %s. "
424 "Expect corruption.\n", strerror(errno));
425 warned = true;
426 }
427 }
428
429 done:
430 v3d_job_free(v3d, job);
431 }
432
433 static bool
434 v3d_job_compare(const void *a, const void *b)
435 {
436 return memcmp(a, b, sizeof(struct v3d_job_key)) == 0;
437 }
438
439 static uint32_t
440 v3d_job_hash(const void *key)
441 {
442 return _mesa_hash_data(key, sizeof(struct v3d_job_key));
443 }
444
445 void
446 v3d_job_init(struct v3d_context *v3d)
447 {
448 v3d->jobs = _mesa_hash_table_create(v3d,
449 v3d_job_hash,
450 v3d_job_compare);
451 v3d->write_jobs = _mesa_hash_table_create(v3d,
452 _mesa_hash_pointer,
453 _mesa_key_pointer_equal);
454 }
455