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