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