1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright © 2014-2017 Broadcom
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci *
11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci * Software.
14bf215546Sopenharmony_ci *
15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21bf215546Sopenharmony_ci * IN THE SOFTWARE.
22bf215546Sopenharmony_ci */
23bf215546Sopenharmony_ci
24bf215546Sopenharmony_ci/** @file v3d_job.c
25bf215546Sopenharmony_ci *
26bf215546Sopenharmony_ci * Functions for submitting V3D render jobs to the kernel.
27bf215546Sopenharmony_ci */
28bf215546Sopenharmony_ci
29bf215546Sopenharmony_ci#include <xf86drm.h>
30bf215546Sopenharmony_ci#include "v3d_context.h"
31bf215546Sopenharmony_ci/* The OQ/semaphore packets are the same across V3D versions. */
32bf215546Sopenharmony_ci#define V3D_VERSION 33
33bf215546Sopenharmony_ci#include "broadcom/cle/v3dx_pack.h"
34bf215546Sopenharmony_ci#include "broadcom/common/v3d_macros.h"
35bf215546Sopenharmony_ci#include "util/hash_table.h"
36bf215546Sopenharmony_ci#include "util/ralloc.h"
37bf215546Sopenharmony_ci#include "util/set.h"
38bf215546Sopenharmony_ci#include "broadcom/clif/clif_dump.h"
39bf215546Sopenharmony_ci
40bf215546Sopenharmony_civoid
41bf215546Sopenharmony_civ3d_job_free(struct v3d_context *v3d, struct v3d_job *job)
42bf215546Sopenharmony_ci{
43bf215546Sopenharmony_ci        set_foreach(job->bos, entry) {
44bf215546Sopenharmony_ci                struct v3d_bo *bo = (struct v3d_bo *)entry->key;
45bf215546Sopenharmony_ci                v3d_bo_unreference(&bo);
46bf215546Sopenharmony_ci        }
47bf215546Sopenharmony_ci
48bf215546Sopenharmony_ci        _mesa_hash_table_remove_key(v3d->jobs, &job->key);
49bf215546Sopenharmony_ci
50bf215546Sopenharmony_ci        if (job->write_prscs) {
51bf215546Sopenharmony_ci                set_foreach(job->write_prscs, entry) {
52bf215546Sopenharmony_ci                        const struct pipe_resource *prsc = entry->key;
53bf215546Sopenharmony_ci
54bf215546Sopenharmony_ci                        _mesa_hash_table_remove_key(v3d->write_jobs, prsc);
55bf215546Sopenharmony_ci                }
56bf215546Sopenharmony_ci        }
57bf215546Sopenharmony_ci
58bf215546Sopenharmony_ci        for (int i = 0; i < job->nr_cbufs; i++) {
59bf215546Sopenharmony_ci                if (job->cbufs[i]) {
60bf215546Sopenharmony_ci                        _mesa_hash_table_remove_key(v3d->write_jobs,
61bf215546Sopenharmony_ci                                                    job->cbufs[i]->texture);
62bf215546Sopenharmony_ci                        pipe_surface_reference(&job->cbufs[i], NULL);
63bf215546Sopenharmony_ci                }
64bf215546Sopenharmony_ci        }
65bf215546Sopenharmony_ci        if (job->zsbuf) {
66bf215546Sopenharmony_ci                struct v3d_resource *rsc = v3d_resource(job->zsbuf->texture);
67bf215546Sopenharmony_ci                if (rsc->separate_stencil)
68bf215546Sopenharmony_ci                        _mesa_hash_table_remove_key(v3d->write_jobs,
69bf215546Sopenharmony_ci                                                    &rsc->separate_stencil->base);
70bf215546Sopenharmony_ci
71bf215546Sopenharmony_ci                _mesa_hash_table_remove_key(v3d->write_jobs,
72bf215546Sopenharmony_ci                                            job->zsbuf->texture);
73bf215546Sopenharmony_ci                pipe_surface_reference(&job->zsbuf, NULL);
74bf215546Sopenharmony_ci        }
75bf215546Sopenharmony_ci        if (job->bbuf)
76bf215546Sopenharmony_ci                pipe_surface_reference(&job->bbuf, NULL);
77bf215546Sopenharmony_ci
78bf215546Sopenharmony_ci        if (v3d->job == job)
79bf215546Sopenharmony_ci                v3d->job = NULL;
80bf215546Sopenharmony_ci
81bf215546Sopenharmony_ci        v3d_destroy_cl(&job->bcl);
82bf215546Sopenharmony_ci        v3d_destroy_cl(&job->rcl);
83bf215546Sopenharmony_ci        v3d_destroy_cl(&job->indirect);
84bf215546Sopenharmony_ci        v3d_bo_unreference(&job->tile_alloc);
85bf215546Sopenharmony_ci        v3d_bo_unreference(&job->tile_state);
86bf215546Sopenharmony_ci
87bf215546Sopenharmony_ci        ralloc_free(job);
88bf215546Sopenharmony_ci}
89bf215546Sopenharmony_ci
90bf215546Sopenharmony_cistruct v3d_job *
91bf215546Sopenharmony_civ3d_job_create(struct v3d_context *v3d)
92bf215546Sopenharmony_ci{
93bf215546Sopenharmony_ci        struct v3d_job *job = rzalloc(v3d, struct v3d_job);
94bf215546Sopenharmony_ci
95bf215546Sopenharmony_ci        job->v3d = v3d;
96bf215546Sopenharmony_ci
97bf215546Sopenharmony_ci        v3d_init_cl(job, &job->bcl);
98bf215546Sopenharmony_ci        v3d_init_cl(job, &job->rcl);
99bf215546Sopenharmony_ci        v3d_init_cl(job, &job->indirect);
100bf215546Sopenharmony_ci
101bf215546Sopenharmony_ci        job->draw_min_x = ~0;
102bf215546Sopenharmony_ci        job->draw_min_y = ~0;
103bf215546Sopenharmony_ci        job->draw_max_x = 0;
104bf215546Sopenharmony_ci        job->draw_max_y = 0;
105bf215546Sopenharmony_ci
106bf215546Sopenharmony_ci        job->bos = _mesa_set_create(job,
107bf215546Sopenharmony_ci                                    _mesa_hash_pointer,
108bf215546Sopenharmony_ci                                    _mesa_key_pointer_equal);
109bf215546Sopenharmony_ci        return job;
110bf215546Sopenharmony_ci}
111bf215546Sopenharmony_ci
112bf215546Sopenharmony_civoid
113bf215546Sopenharmony_civ3d_job_add_bo(struct v3d_job *job, struct v3d_bo *bo)
114bf215546Sopenharmony_ci{
115bf215546Sopenharmony_ci        if (!bo)
116bf215546Sopenharmony_ci                return;
117bf215546Sopenharmony_ci
118bf215546Sopenharmony_ci        if (_mesa_set_search(job->bos, bo))
119bf215546Sopenharmony_ci                return;
120bf215546Sopenharmony_ci
121bf215546Sopenharmony_ci        v3d_bo_reference(bo);
122bf215546Sopenharmony_ci        _mesa_set_add(job->bos, bo);
123bf215546Sopenharmony_ci        job->referenced_size += bo->size;
124bf215546Sopenharmony_ci
125bf215546Sopenharmony_ci        uint32_t *bo_handles = (void *)(uintptr_t)job->submit.bo_handles;
126bf215546Sopenharmony_ci
127bf215546Sopenharmony_ci        if (job->submit.bo_handle_count >= job->bo_handles_size) {
128bf215546Sopenharmony_ci                job->bo_handles_size = MAX2(4, job->bo_handles_size * 2);
129bf215546Sopenharmony_ci                bo_handles = reralloc(job, bo_handles,
130bf215546Sopenharmony_ci                                      uint32_t, job->bo_handles_size);
131bf215546Sopenharmony_ci                job->submit.bo_handles = (uintptr_t)(void *)bo_handles;
132bf215546Sopenharmony_ci        }
133bf215546Sopenharmony_ci        bo_handles[job->submit.bo_handle_count++] = bo->handle;
134bf215546Sopenharmony_ci}
135bf215546Sopenharmony_ci
136bf215546Sopenharmony_civoid
137bf215546Sopenharmony_civ3d_job_add_write_resource(struct v3d_job *job, struct pipe_resource *prsc)
138bf215546Sopenharmony_ci{
139bf215546Sopenharmony_ci        struct v3d_context *v3d = job->v3d;
140bf215546Sopenharmony_ci
141bf215546Sopenharmony_ci        if (!job->write_prscs) {
142bf215546Sopenharmony_ci                job->write_prscs = _mesa_set_create(job,
143bf215546Sopenharmony_ci                                                    _mesa_hash_pointer,
144bf215546Sopenharmony_ci                                                    _mesa_key_pointer_equal);
145bf215546Sopenharmony_ci        }
146bf215546Sopenharmony_ci
147bf215546Sopenharmony_ci        _mesa_set_add(job->write_prscs, prsc);
148bf215546Sopenharmony_ci        _mesa_hash_table_insert(v3d->write_jobs, prsc, job);
149bf215546Sopenharmony_ci}
150bf215546Sopenharmony_ci
151bf215546Sopenharmony_civoid
152bf215546Sopenharmony_civ3d_flush_jobs_using_bo(struct v3d_context *v3d, struct v3d_bo *bo)
153bf215546Sopenharmony_ci{
154bf215546Sopenharmony_ci        hash_table_foreach(v3d->jobs, entry) {
155bf215546Sopenharmony_ci                struct v3d_job *job = entry->data;
156bf215546Sopenharmony_ci
157bf215546Sopenharmony_ci                if (_mesa_set_search(job->bos, bo))
158bf215546Sopenharmony_ci                        v3d_job_submit(v3d, job);
159bf215546Sopenharmony_ci        }
160bf215546Sopenharmony_ci}
161bf215546Sopenharmony_ci
162bf215546Sopenharmony_civoid
163bf215546Sopenharmony_civ3d_job_add_tf_write_resource(struct v3d_job *job, struct pipe_resource *prsc)
164bf215546Sopenharmony_ci{
165bf215546Sopenharmony_ci        v3d_job_add_write_resource(job, prsc);
166bf215546Sopenharmony_ci
167bf215546Sopenharmony_ci        if (!job->tf_write_prscs)
168bf215546Sopenharmony_ci                job->tf_write_prscs = _mesa_pointer_set_create(job);
169bf215546Sopenharmony_ci
170bf215546Sopenharmony_ci        _mesa_set_add(job->tf_write_prscs, prsc);
171bf215546Sopenharmony_ci}
172bf215546Sopenharmony_ci
173bf215546Sopenharmony_cistatic bool
174bf215546Sopenharmony_civ3d_job_writes_resource_from_tf(struct v3d_job *job,
175bf215546Sopenharmony_ci                                struct pipe_resource *prsc)
176bf215546Sopenharmony_ci{
177bf215546Sopenharmony_ci        if (!job->tf_enabled)
178bf215546Sopenharmony_ci                return false;
179bf215546Sopenharmony_ci
180bf215546Sopenharmony_ci        if (!job->tf_write_prscs)
181bf215546Sopenharmony_ci                return false;
182bf215546Sopenharmony_ci
183bf215546Sopenharmony_ci        return _mesa_set_search(job->tf_write_prscs, prsc) != NULL;
184bf215546Sopenharmony_ci}
185bf215546Sopenharmony_ci
186bf215546Sopenharmony_civoid
187bf215546Sopenharmony_civ3d_flush_jobs_writing_resource(struct v3d_context *v3d,
188bf215546Sopenharmony_ci                                struct pipe_resource *prsc,
189bf215546Sopenharmony_ci                                enum v3d_flush_cond flush_cond,
190bf215546Sopenharmony_ci                                bool is_compute_pipeline)
191bf215546Sopenharmony_ci{
192bf215546Sopenharmony_ci        struct hash_entry *entry = _mesa_hash_table_search(v3d->write_jobs,
193bf215546Sopenharmony_ci                                                           prsc);
194bf215546Sopenharmony_ci        struct v3d_resource *rsc = v3d_resource(prsc);
195bf215546Sopenharmony_ci
196bf215546Sopenharmony_ci        /* We need to sync if graphics pipeline reads a resource written
197bf215546Sopenharmony_ci         * by the compute pipeline. The same would be needed for the case of
198bf215546Sopenharmony_ci         * graphics-compute dependency but nowadays all compute jobs
199bf215546Sopenharmony_ci         * are serialized with the previous submitted job.
200bf215546Sopenharmony_ci         */
201bf215546Sopenharmony_ci        if (!is_compute_pipeline && rsc->bo != NULL && rsc->compute_written) {
202bf215546Sopenharmony_ci           v3d->sync_on_last_compute_job = true;
203bf215546Sopenharmony_ci           rsc->compute_written = false;
204bf215546Sopenharmony_ci        }
205bf215546Sopenharmony_ci
206bf215546Sopenharmony_ci        if (!entry)
207bf215546Sopenharmony_ci                return;
208bf215546Sopenharmony_ci
209bf215546Sopenharmony_ci        struct v3d_job *job = entry->data;
210bf215546Sopenharmony_ci
211bf215546Sopenharmony_ci        bool needs_flush;
212bf215546Sopenharmony_ci        switch (flush_cond) {
213bf215546Sopenharmony_ci        case V3D_FLUSH_ALWAYS:
214bf215546Sopenharmony_ci                needs_flush = true;
215bf215546Sopenharmony_ci                break;
216bf215546Sopenharmony_ci        case V3D_FLUSH_NOT_CURRENT_JOB:
217bf215546Sopenharmony_ci                needs_flush = !v3d->job || v3d->job != job;
218bf215546Sopenharmony_ci                break;
219bf215546Sopenharmony_ci        case V3D_FLUSH_DEFAULT:
220bf215546Sopenharmony_ci        default:
221bf215546Sopenharmony_ci                /* For writes from TF in the same job we use the "Wait for TF"
222bf215546Sopenharmony_ci                 * feature provided by the hardware so we don't want to flush.
223bf215546Sopenharmony_ci                 * The exception to this is when the caller is about to map the
224bf215546Sopenharmony_ci                 * resource since in that case we don't have a 'Wait for TF'
225bf215546Sopenharmony_ci                 * command the in command stream. In this scenario the caller
226bf215546Sopenharmony_ci                 * is expected to set 'always_flush' to True.
227bf215546Sopenharmony_ci                 */
228bf215546Sopenharmony_ci                needs_flush = !v3d_job_writes_resource_from_tf(job, prsc);
229bf215546Sopenharmony_ci        }
230bf215546Sopenharmony_ci
231bf215546Sopenharmony_ci        if (needs_flush)
232bf215546Sopenharmony_ci                v3d_job_submit(v3d, job);
233bf215546Sopenharmony_ci}
234bf215546Sopenharmony_ci
235bf215546Sopenharmony_civoid
236bf215546Sopenharmony_civ3d_flush_jobs_reading_resource(struct v3d_context *v3d,
237bf215546Sopenharmony_ci                                struct pipe_resource *prsc,
238bf215546Sopenharmony_ci                                enum v3d_flush_cond flush_cond,
239bf215546Sopenharmony_ci                                bool is_compute_pipeline)
240bf215546Sopenharmony_ci{
241bf215546Sopenharmony_ci        struct v3d_resource *rsc = v3d_resource(prsc);
242bf215546Sopenharmony_ci
243bf215546Sopenharmony_ci        /* We only need to force the flush on TF writes, which is the only
244bf215546Sopenharmony_ci         * case where we might skip the flush to use the 'Wait for TF'
245bf215546Sopenharmony_ci         * command. Here we are flushing for a read, which means that the
246bf215546Sopenharmony_ci         * caller intends to write to the resource, so we don't care if
247bf215546Sopenharmony_ci         * there was a previous TF write to it.
248bf215546Sopenharmony_ci         */
249bf215546Sopenharmony_ci        v3d_flush_jobs_writing_resource(v3d, prsc, flush_cond,
250bf215546Sopenharmony_ci                                        is_compute_pipeline);
251bf215546Sopenharmony_ci
252bf215546Sopenharmony_ci        hash_table_foreach(v3d->jobs, entry) {
253bf215546Sopenharmony_ci                struct v3d_job *job = entry->data;
254bf215546Sopenharmony_ci
255bf215546Sopenharmony_ci                if (!_mesa_set_search(job->bos, rsc->bo))
256bf215546Sopenharmony_ci                        continue;
257bf215546Sopenharmony_ci
258bf215546Sopenharmony_ci                bool needs_flush;
259bf215546Sopenharmony_ci                switch (flush_cond) {
260bf215546Sopenharmony_ci                case V3D_FLUSH_NOT_CURRENT_JOB:
261bf215546Sopenharmony_ci                        needs_flush = !v3d->job || v3d->job != job;
262bf215546Sopenharmony_ci                        break;
263bf215546Sopenharmony_ci                case V3D_FLUSH_ALWAYS:
264bf215546Sopenharmony_ci                case V3D_FLUSH_DEFAULT:
265bf215546Sopenharmony_ci                default:
266bf215546Sopenharmony_ci                        needs_flush = true;
267bf215546Sopenharmony_ci                }
268bf215546Sopenharmony_ci
269bf215546Sopenharmony_ci                if (needs_flush)
270bf215546Sopenharmony_ci                        v3d_job_submit(v3d, job);
271bf215546Sopenharmony_ci
272bf215546Sopenharmony_ci                /* Reminder: v3d->jobs is safe to keep iterating even
273bf215546Sopenharmony_ci                 * after deletion of an entry.
274bf215546Sopenharmony_ci                 */
275bf215546Sopenharmony_ci                continue;
276bf215546Sopenharmony_ci        }
277bf215546Sopenharmony_ci}
278bf215546Sopenharmony_ci
279bf215546Sopenharmony_ci/**
280bf215546Sopenharmony_ci * Returns a v3d_job struture for tracking V3D rendering to a particular FBO.
281bf215546Sopenharmony_ci *
282bf215546Sopenharmony_ci * If we've already started rendering to this FBO, then return the same job,
283bf215546Sopenharmony_ci * otherwise make a new one.  If we're beginning rendering to an FBO, make
284bf215546Sopenharmony_ci * sure that any previous reads of the FBO (or writes to its color/Z surfaces)
285bf215546Sopenharmony_ci * have been flushed.
286bf215546Sopenharmony_ci */
287bf215546Sopenharmony_cistruct v3d_job *
288bf215546Sopenharmony_civ3d_get_job(struct v3d_context *v3d,
289bf215546Sopenharmony_ci            uint32_t nr_cbufs,
290bf215546Sopenharmony_ci            struct pipe_surface **cbufs,
291bf215546Sopenharmony_ci            struct pipe_surface *zsbuf,
292bf215546Sopenharmony_ci            struct pipe_surface *bbuf)
293bf215546Sopenharmony_ci{
294bf215546Sopenharmony_ci        /* Return the existing job for this FBO if we have one */
295bf215546Sopenharmony_ci        struct v3d_job_key local_key = {
296bf215546Sopenharmony_ci                .cbufs = {
297bf215546Sopenharmony_ci                        cbufs[0],
298bf215546Sopenharmony_ci                        cbufs[1],
299bf215546Sopenharmony_ci                        cbufs[2],
300bf215546Sopenharmony_ci                        cbufs[3],
301bf215546Sopenharmony_ci                },
302bf215546Sopenharmony_ci                .zsbuf = zsbuf,
303bf215546Sopenharmony_ci                .bbuf = bbuf,
304bf215546Sopenharmony_ci        };
305bf215546Sopenharmony_ci        struct hash_entry *entry = _mesa_hash_table_search(v3d->jobs,
306bf215546Sopenharmony_ci                                                           &local_key);
307bf215546Sopenharmony_ci        if (entry)
308bf215546Sopenharmony_ci                return entry->data;
309bf215546Sopenharmony_ci
310bf215546Sopenharmony_ci        /* Creating a new job.  Make sure that any previous jobs reading or
311bf215546Sopenharmony_ci         * writing these buffers are flushed.
312bf215546Sopenharmony_ci         */
313bf215546Sopenharmony_ci        struct v3d_job *job = v3d_job_create(v3d);
314bf215546Sopenharmony_ci        job->nr_cbufs = nr_cbufs;
315bf215546Sopenharmony_ci
316bf215546Sopenharmony_ci        for (int i = 0; i < job->nr_cbufs; i++) {
317bf215546Sopenharmony_ci                if (cbufs[i]) {
318bf215546Sopenharmony_ci                        v3d_flush_jobs_reading_resource(v3d, cbufs[i]->texture,
319bf215546Sopenharmony_ci                                                        V3D_FLUSH_DEFAULT,
320bf215546Sopenharmony_ci                                                        false);
321bf215546Sopenharmony_ci                        pipe_surface_reference(&job->cbufs[i], cbufs[i]);
322bf215546Sopenharmony_ci
323bf215546Sopenharmony_ci                        if (cbufs[i]->texture->nr_samples > 1)
324bf215546Sopenharmony_ci                                job->msaa = true;
325bf215546Sopenharmony_ci                }
326bf215546Sopenharmony_ci        }
327bf215546Sopenharmony_ci        if (zsbuf) {
328bf215546Sopenharmony_ci                v3d_flush_jobs_reading_resource(v3d, zsbuf->texture,
329bf215546Sopenharmony_ci                                                V3D_FLUSH_DEFAULT,
330bf215546Sopenharmony_ci                                                false);
331bf215546Sopenharmony_ci                pipe_surface_reference(&job->zsbuf, zsbuf);
332bf215546Sopenharmony_ci                if (zsbuf->texture->nr_samples > 1)
333bf215546Sopenharmony_ci                        job->msaa = true;
334bf215546Sopenharmony_ci        }
335bf215546Sopenharmony_ci        if (bbuf) {
336bf215546Sopenharmony_ci                pipe_surface_reference(&job->bbuf, bbuf);
337bf215546Sopenharmony_ci                if (bbuf->texture->nr_samples > 1)
338bf215546Sopenharmony_ci                        job->msaa = true;
339bf215546Sopenharmony_ci        }
340bf215546Sopenharmony_ci
341bf215546Sopenharmony_ci        for (int i = 0; i < job->nr_cbufs; i++) {
342bf215546Sopenharmony_ci                if (cbufs[i])
343bf215546Sopenharmony_ci                        _mesa_hash_table_insert(v3d->write_jobs,
344bf215546Sopenharmony_ci                                                cbufs[i]->texture, job);
345bf215546Sopenharmony_ci        }
346bf215546Sopenharmony_ci        if (zsbuf) {
347bf215546Sopenharmony_ci                _mesa_hash_table_insert(v3d->write_jobs, zsbuf->texture, job);
348bf215546Sopenharmony_ci
349bf215546Sopenharmony_ci                struct v3d_resource *rsc = v3d_resource(zsbuf->texture);
350bf215546Sopenharmony_ci                if (rsc->separate_stencil) {
351bf215546Sopenharmony_ci                        v3d_flush_jobs_reading_resource(v3d,
352bf215546Sopenharmony_ci                                                        &rsc->separate_stencil->base,
353bf215546Sopenharmony_ci                                                        V3D_FLUSH_DEFAULT,
354bf215546Sopenharmony_ci                                                        false);
355bf215546Sopenharmony_ci                        _mesa_hash_table_insert(v3d->write_jobs,
356bf215546Sopenharmony_ci                                                &rsc->separate_stencil->base,
357bf215546Sopenharmony_ci                                                job);
358bf215546Sopenharmony_ci                }
359bf215546Sopenharmony_ci        }
360bf215546Sopenharmony_ci
361bf215546Sopenharmony_ci       job->double_buffer =
362bf215546Sopenharmony_ci               unlikely(V3D_DEBUG & V3D_DEBUG_DOUBLE_BUFFER) && !job->msaa;
363bf215546Sopenharmony_ci
364bf215546Sopenharmony_ci        memcpy(&job->key, &local_key, sizeof(local_key));
365bf215546Sopenharmony_ci        _mesa_hash_table_insert(v3d->jobs, &job->key, job);
366bf215546Sopenharmony_ci
367bf215546Sopenharmony_ci        return job;
368bf215546Sopenharmony_ci}
369bf215546Sopenharmony_ci
370bf215546Sopenharmony_cistruct v3d_job *
371bf215546Sopenharmony_civ3d_get_job_for_fbo(struct v3d_context *v3d)
372bf215546Sopenharmony_ci{
373bf215546Sopenharmony_ci        if (v3d->job)
374bf215546Sopenharmony_ci                return v3d->job;
375bf215546Sopenharmony_ci
376bf215546Sopenharmony_ci        uint32_t nr_cbufs = v3d->framebuffer.nr_cbufs;
377bf215546Sopenharmony_ci        struct pipe_surface **cbufs = v3d->framebuffer.cbufs;
378bf215546Sopenharmony_ci        struct pipe_surface *zsbuf = v3d->framebuffer.zsbuf;
379bf215546Sopenharmony_ci        struct v3d_job *job = v3d_get_job(v3d, nr_cbufs, cbufs, zsbuf, NULL);
380bf215546Sopenharmony_ci
381bf215546Sopenharmony_ci        if (v3d->framebuffer.samples >= 1) {
382bf215546Sopenharmony_ci                job->msaa = true;
383bf215546Sopenharmony_ci                job->double_buffer = false;
384bf215546Sopenharmony_ci        }
385bf215546Sopenharmony_ci
386bf215546Sopenharmony_ci        v3d_get_tile_buffer_size(job->msaa, job->double_buffer,
387bf215546Sopenharmony_ci                                 job->nr_cbufs, job->cbufs, job->bbuf,
388bf215546Sopenharmony_ci                                 &job->tile_width, &job->tile_height,
389bf215546Sopenharmony_ci                                 &job->internal_bpp);
390bf215546Sopenharmony_ci
391bf215546Sopenharmony_ci        /* The dirty flags are tracking what's been updated while v3d->job has
392bf215546Sopenharmony_ci         * been bound, so set them all to ~0 when switching between jobs.  We
393bf215546Sopenharmony_ci         * also need to reset all state at the start of rendering.
394bf215546Sopenharmony_ci         */
395bf215546Sopenharmony_ci        v3d->dirty = ~0;
396bf215546Sopenharmony_ci
397bf215546Sopenharmony_ci        /* If we're binding to uninitialized buffers, no need to load their
398bf215546Sopenharmony_ci         * contents before drawing.
399bf215546Sopenharmony_ci         */
400bf215546Sopenharmony_ci        for (int i = 0; i < nr_cbufs; i++) {
401bf215546Sopenharmony_ci                if (cbufs[i]) {
402bf215546Sopenharmony_ci                        struct v3d_resource *rsc = v3d_resource(cbufs[i]->texture);
403bf215546Sopenharmony_ci                        if (!rsc->writes)
404bf215546Sopenharmony_ci                                job->clear |= PIPE_CLEAR_COLOR0 << i;
405bf215546Sopenharmony_ci                }
406bf215546Sopenharmony_ci        }
407bf215546Sopenharmony_ci
408bf215546Sopenharmony_ci        if (zsbuf) {
409bf215546Sopenharmony_ci                struct v3d_resource *rsc = v3d_resource(zsbuf->texture);
410bf215546Sopenharmony_ci                if (!rsc->writes)
411bf215546Sopenharmony_ci                        job->clear |= PIPE_CLEAR_DEPTH;
412bf215546Sopenharmony_ci
413bf215546Sopenharmony_ci                if (rsc->separate_stencil)
414bf215546Sopenharmony_ci                        rsc = rsc->separate_stencil;
415bf215546Sopenharmony_ci
416bf215546Sopenharmony_ci                if (!rsc->writes)
417bf215546Sopenharmony_ci                        job->clear |= PIPE_CLEAR_STENCIL;
418bf215546Sopenharmony_ci        }
419bf215546Sopenharmony_ci
420bf215546Sopenharmony_ci        job->draw_tiles_x = DIV_ROUND_UP(v3d->framebuffer.width,
421bf215546Sopenharmony_ci                                         job->tile_width);
422bf215546Sopenharmony_ci        job->draw_tiles_y = DIV_ROUND_UP(v3d->framebuffer.height,
423bf215546Sopenharmony_ci                                         job->tile_height);
424bf215546Sopenharmony_ci
425bf215546Sopenharmony_ci        v3d->job = job;
426bf215546Sopenharmony_ci
427bf215546Sopenharmony_ci        return job;
428bf215546Sopenharmony_ci}
429bf215546Sopenharmony_ci
430bf215546Sopenharmony_cistatic void
431bf215546Sopenharmony_civ3d_clif_dump(struct v3d_context *v3d, struct v3d_job *job)
432bf215546Sopenharmony_ci{
433bf215546Sopenharmony_ci        if (!(unlikely(V3D_DEBUG & (V3D_DEBUG_CL |
434bf215546Sopenharmony_ci                                    V3D_DEBUG_CL_NO_BIN |
435bf215546Sopenharmony_ci                                    V3D_DEBUG_CLIF))))
436bf215546Sopenharmony_ci                return;
437bf215546Sopenharmony_ci
438bf215546Sopenharmony_ci        struct clif_dump *clif = clif_dump_init(&v3d->screen->devinfo,
439bf215546Sopenharmony_ci                                                stderr,
440bf215546Sopenharmony_ci                                                V3D_DEBUG & (V3D_DEBUG_CL |
441bf215546Sopenharmony_ci                                                             V3D_DEBUG_CL_NO_BIN),
442bf215546Sopenharmony_ci                                                V3D_DEBUG & V3D_DEBUG_CL_NO_BIN);
443bf215546Sopenharmony_ci
444bf215546Sopenharmony_ci        set_foreach(job->bos, entry) {
445bf215546Sopenharmony_ci                struct v3d_bo *bo = (void *)entry->key;
446bf215546Sopenharmony_ci                char *name = ralloc_asprintf(NULL, "%s_0x%x",
447bf215546Sopenharmony_ci                                             bo->name, bo->offset);
448bf215546Sopenharmony_ci
449bf215546Sopenharmony_ci                v3d_bo_map(bo);
450bf215546Sopenharmony_ci                clif_dump_add_bo(clif, name, bo->offset, bo->size, bo->map);
451bf215546Sopenharmony_ci
452bf215546Sopenharmony_ci                ralloc_free(name);
453bf215546Sopenharmony_ci        }
454bf215546Sopenharmony_ci
455bf215546Sopenharmony_ci        clif_dump(clif, &job->submit);
456bf215546Sopenharmony_ci
457bf215546Sopenharmony_ci        clif_dump_destroy(clif);
458bf215546Sopenharmony_ci}
459bf215546Sopenharmony_ci
460bf215546Sopenharmony_cistatic void
461bf215546Sopenharmony_civ3d_read_and_accumulate_primitive_counters(struct v3d_context *v3d)
462bf215546Sopenharmony_ci{
463bf215546Sopenharmony_ci        assert(v3d->prim_counts);
464bf215546Sopenharmony_ci
465bf215546Sopenharmony_ci        perf_debug("stalling on TF counts readback\n");
466bf215546Sopenharmony_ci        struct v3d_resource *rsc = v3d_resource(v3d->prim_counts);
467bf215546Sopenharmony_ci        if (v3d_bo_wait(rsc->bo, PIPE_TIMEOUT_INFINITE, "prim-counts")) {
468bf215546Sopenharmony_ci                uint32_t *map = v3d_bo_map(rsc->bo) + v3d->prim_counts_offset;
469bf215546Sopenharmony_ci                v3d->tf_prims_generated += map[V3D_PRIM_COUNTS_TF_WRITTEN];
470bf215546Sopenharmony_ci                /* When we only have a vertex shader we determine the primitive
471bf215546Sopenharmony_ci                 * count in the CPU so don't update it here again.
472bf215546Sopenharmony_ci                 */
473bf215546Sopenharmony_ci                if (v3d->prog.gs)
474bf215546Sopenharmony_ci                        v3d->prims_generated += map[V3D_PRIM_COUNTS_WRITTEN];
475bf215546Sopenharmony_ci        }
476bf215546Sopenharmony_ci}
477bf215546Sopenharmony_ci
478bf215546Sopenharmony_ci/**
479bf215546Sopenharmony_ci * Submits the job to the kernel and then reinitializes it.
480bf215546Sopenharmony_ci */
481bf215546Sopenharmony_civoid
482bf215546Sopenharmony_civ3d_job_submit(struct v3d_context *v3d, struct v3d_job *job)
483bf215546Sopenharmony_ci{
484bf215546Sopenharmony_ci        struct v3d_screen *screen = v3d->screen;
485bf215546Sopenharmony_ci
486bf215546Sopenharmony_ci        if (!job->needs_flush)
487bf215546Sopenharmony_ci                goto done;
488bf215546Sopenharmony_ci
489bf215546Sopenharmony_ci        /* The GL_PRIMITIVES_GENERATED query is included with
490bf215546Sopenharmony_ci         * OES_geometry_shader.
491bf215546Sopenharmony_ci         */
492bf215546Sopenharmony_ci        job->needs_primitives_generated =
493bf215546Sopenharmony_ci                v3d->n_primitives_generated_queries_in_flight > 0 &&
494bf215546Sopenharmony_ci                v3d->prog.gs;
495bf215546Sopenharmony_ci
496bf215546Sopenharmony_ci        if (job->needs_primitives_generated)
497bf215546Sopenharmony_ci                v3d_ensure_prim_counts_allocated(v3d);
498bf215546Sopenharmony_ci
499bf215546Sopenharmony_ci        if (screen->devinfo.ver >= 41)
500bf215546Sopenharmony_ci                v3d41_emit_rcl(job);
501bf215546Sopenharmony_ci        else
502bf215546Sopenharmony_ci                v3d33_emit_rcl(job);
503bf215546Sopenharmony_ci
504bf215546Sopenharmony_ci        if (cl_offset(&job->bcl) > 0) {
505bf215546Sopenharmony_ci                if (screen->devinfo.ver >= 41)
506bf215546Sopenharmony_ci                        v3d41_bcl_epilogue(v3d, job);
507bf215546Sopenharmony_ci                else
508bf215546Sopenharmony_ci                        v3d33_bcl_epilogue(v3d, job);
509bf215546Sopenharmony_ci        }
510bf215546Sopenharmony_ci
511bf215546Sopenharmony_ci        /* While the RCL will implicitly depend on the last RCL to have
512bf215546Sopenharmony_ci         * finished, we also need to block on any previous TFU job we may have
513bf215546Sopenharmony_ci         * dispatched.
514bf215546Sopenharmony_ci         */
515bf215546Sopenharmony_ci        job->submit.in_sync_rcl = v3d->out_sync;
516bf215546Sopenharmony_ci
517bf215546Sopenharmony_ci        /* Update the sync object for the last rendering by our context. */
518bf215546Sopenharmony_ci        job->submit.out_sync = v3d->out_sync;
519bf215546Sopenharmony_ci
520bf215546Sopenharmony_ci        job->submit.bcl_end = job->bcl.bo->offset + cl_offset(&job->bcl);
521bf215546Sopenharmony_ci        job->submit.rcl_end = job->rcl.bo->offset + cl_offset(&job->rcl);
522bf215546Sopenharmony_ci
523bf215546Sopenharmony_ci        if (v3d->active_perfmon) {
524bf215546Sopenharmony_ci                assert(screen->has_perfmon);
525bf215546Sopenharmony_ci                job->submit.perfmon_id = v3d->active_perfmon->kperfmon_id;
526bf215546Sopenharmony_ci        }
527bf215546Sopenharmony_ci
528bf215546Sopenharmony_ci        /* If we are submitting a job with a different perfmon, we need to
529bf215546Sopenharmony_ci         * ensure the previous one fully finishes before starting this;
530bf215546Sopenharmony_ci         * otherwise it would wrongly mix counter results.
531bf215546Sopenharmony_ci         */
532bf215546Sopenharmony_ci        if (v3d->active_perfmon != v3d->last_perfmon) {
533bf215546Sopenharmony_ci                v3d->last_perfmon = v3d->active_perfmon;
534bf215546Sopenharmony_ci                job->submit.in_sync_bcl = v3d->out_sync;
535bf215546Sopenharmony_ci        }
536bf215546Sopenharmony_ci
537bf215546Sopenharmony_ci        job->submit.flags = 0;
538bf215546Sopenharmony_ci        if (job->tmu_dirty_rcl && screen->has_cache_flush)
539bf215546Sopenharmony_ci                job->submit.flags |= DRM_V3D_SUBMIT_CL_FLUSH_CACHE;
540bf215546Sopenharmony_ci
541bf215546Sopenharmony_ci        /* On V3D 4.1, the tile alloc/state setup moved to register writes
542bf215546Sopenharmony_ci         * instead of binner packets.
543bf215546Sopenharmony_ci         */
544bf215546Sopenharmony_ci        if (screen->devinfo.ver >= 41) {
545bf215546Sopenharmony_ci                v3d_job_add_bo(job, job->tile_alloc);
546bf215546Sopenharmony_ci                job->submit.qma = job->tile_alloc->offset;
547bf215546Sopenharmony_ci                job->submit.qms = job->tile_alloc->size;
548bf215546Sopenharmony_ci
549bf215546Sopenharmony_ci                v3d_job_add_bo(job, job->tile_state);
550bf215546Sopenharmony_ci                job->submit.qts = job->tile_state->offset;
551bf215546Sopenharmony_ci        }
552bf215546Sopenharmony_ci
553bf215546Sopenharmony_ci        v3d_clif_dump(v3d, job);
554bf215546Sopenharmony_ci
555bf215546Sopenharmony_ci        if (!(unlikely(V3D_DEBUG & V3D_DEBUG_NORAST))) {
556bf215546Sopenharmony_ci                int ret;
557bf215546Sopenharmony_ci
558bf215546Sopenharmony_ci                ret = v3d_ioctl(v3d->fd, DRM_IOCTL_V3D_SUBMIT_CL, &job->submit);
559bf215546Sopenharmony_ci                static bool warned = false;
560bf215546Sopenharmony_ci                if (ret && !warned) {
561bf215546Sopenharmony_ci                        fprintf(stderr, "Draw call returned %s.  "
562bf215546Sopenharmony_ci                                        "Expect corruption.\n", strerror(errno));
563bf215546Sopenharmony_ci                        warned = true;
564bf215546Sopenharmony_ci                } else if (!ret) {
565bf215546Sopenharmony_ci                        if (v3d->active_perfmon)
566bf215546Sopenharmony_ci                                v3d->active_perfmon->job_submitted = true;
567bf215546Sopenharmony_ci                }
568bf215546Sopenharmony_ci
569bf215546Sopenharmony_ci                /* If we are submitting a job in the middle of transform
570bf215546Sopenharmony_ci                 * feedback or there is a primitives generated query with a
571bf215546Sopenharmony_ci                 * geometry shader then we need to read the primitive counts
572bf215546Sopenharmony_ci                 * and accumulate them, otherwise they will be reset at the
573bf215546Sopenharmony_ci                 * start of the next draw when we emit the Tile Binning Mode
574bf215546Sopenharmony_ci                 * Configuration packet.
575bf215546Sopenharmony_ci                 *
576bf215546Sopenharmony_ci                 * If the job doesn't have any TF draw calls, then we know
577bf215546Sopenharmony_ci                 * the primitive count must be zero and we can skip stalling
578bf215546Sopenharmony_ci                 * for this. This also fixes a problem because it seems that
579bf215546Sopenharmony_ci                 * in this scenario the counters are not reset with the Tile
580bf215546Sopenharmony_ci                 * Binning Mode Configuration packet, which would translate
581bf215546Sopenharmony_ci                 * to us reading an obsolete (possibly non-zero) value from
582bf215546Sopenharmony_ci                 * the GPU counters.
583bf215546Sopenharmony_ci                 */
584bf215546Sopenharmony_ci                if (job->needs_primitives_generated ||
585bf215546Sopenharmony_ci                    (v3d->streamout.num_targets &&
586bf215546Sopenharmony_ci                     job->tf_draw_calls_queued > 0))
587bf215546Sopenharmony_ci                        v3d_read_and_accumulate_primitive_counters(v3d);
588bf215546Sopenharmony_ci        }
589bf215546Sopenharmony_ci
590bf215546Sopenharmony_cidone:
591bf215546Sopenharmony_ci        v3d_job_free(v3d, job);
592bf215546Sopenharmony_ci}
593bf215546Sopenharmony_ci
594bf215546Sopenharmony_cistatic bool
595bf215546Sopenharmony_civ3d_job_compare(const void *a, const void *b)
596bf215546Sopenharmony_ci{
597bf215546Sopenharmony_ci        return memcmp(a, b, sizeof(struct v3d_job_key)) == 0;
598bf215546Sopenharmony_ci}
599bf215546Sopenharmony_ci
600bf215546Sopenharmony_cistatic uint32_t
601bf215546Sopenharmony_civ3d_job_hash(const void *key)
602bf215546Sopenharmony_ci{
603bf215546Sopenharmony_ci        return _mesa_hash_data(key, sizeof(struct v3d_job_key));
604bf215546Sopenharmony_ci}
605bf215546Sopenharmony_ci
606bf215546Sopenharmony_civoid
607bf215546Sopenharmony_civ3d_job_init(struct v3d_context *v3d)
608bf215546Sopenharmony_ci{
609bf215546Sopenharmony_ci        v3d->jobs = _mesa_hash_table_create(v3d,
610bf215546Sopenharmony_ci                                            v3d_job_hash,
611bf215546Sopenharmony_ci                                            v3d_job_compare);
612bf215546Sopenharmony_ci        v3d->write_jobs = _mesa_hash_table_create(v3d,
613bf215546Sopenharmony_ci                                                  _mesa_hash_pointer,
614bf215546Sopenharmony_ci                                                  _mesa_key_pointer_equal);
615bf215546Sopenharmony_ci}
616bf215546Sopenharmony_ci
617