gallium: add pipe cap for scissored clears and pass scissor state to clear() hook
[mesa.git] / src / gallium / drivers / vc4 / vc4_job.c
index cb20853fa497abbac0b8b4fd06a4dfbed8dce8a6..30dfebdb8c21a5c72d7b0e3408f54bff676a7418 100644 (file)
  */
 
 #include <xf86drm.h>
+#include "vc4_cl_dump.h"
 #include "vc4_context.h"
+#include "util/hash_table.h"
 
-void
-vc4_job_init(struct vc4_job *job)
+static void
+vc4_job_free(struct vc4_context *vc4, struct vc4_job *job)
 {
+        struct vc4_bo **referenced_bos = job->bo_pointers.base;
+        for (int i = 0; i < cl_offset(&job->bo_handles) / 4; i++) {
+                vc4_bo_unreference(&referenced_bos[i]);
+        }
+
+        _mesa_hash_table_remove_key(vc4->jobs, &job->key);
+
+        if (job->color_write) {
+                _mesa_hash_table_remove_key(vc4->write_jobs,
+                                            job->color_write->texture);
+                pipe_surface_reference(&job->color_write, NULL);
+        }
+        if (job->msaa_color_write) {
+                _mesa_hash_table_remove_key(vc4->write_jobs,
+                                            job->msaa_color_write->texture);
+                pipe_surface_reference(&job->msaa_color_write, NULL);
+        }
+        if (job->zs_write) {
+                _mesa_hash_table_remove_key(vc4->write_jobs,
+                                            job->zs_write->texture);
+                pipe_surface_reference(&job->zs_write, NULL);
+        }
+        if (job->msaa_zs_write) {
+                _mesa_hash_table_remove_key(vc4->write_jobs,
+                                            job->msaa_zs_write->texture);
+                pipe_surface_reference(&job->msaa_zs_write, NULL);
+        }
+
+        pipe_surface_reference(&job->color_read, NULL);
+        pipe_surface_reference(&job->zs_read, NULL);
+
+        if (vc4->job == job)
+                vc4->job = NULL;
+
+        ralloc_free(job);
+}
+
+static struct vc4_job *
+vc4_job_create(struct vc4_context *vc4)
+{
+        struct vc4_job *job = rzalloc(vc4, struct vc4_job);
+
         vc4_init_cl(job, &job->bcl);
         vc4_init_cl(job, &job->shader_rec);
         vc4_init_cl(job, &job->uniforms);
         vc4_init_cl(job, &job->bo_handles);
         vc4_init_cl(job, &job->bo_pointers);
-        vc4_job_reset(job);
+
+        job->draw_min_x = ~0;
+        job->draw_min_y = ~0;
+        job->draw_max_x = 0;
+        job->draw_max_y = 0;
+
+        job->last_gem_handle_hindex = ~0;
+
+        if (vc4->perfmon)
+                job->perfmon = vc4->perfmon;
+
+        return job;
 }
 
 void
-vc4_job_reset(struct vc4_job *job)
+vc4_flush_jobs_writing_resource(struct vc4_context *vc4,
+                                struct pipe_resource *prsc)
 {
-        struct vc4_bo **referenced_bos = job->bo_pointers.base;
-        for (int i = 0; i < cl_offset(&job->bo_handles) / 4; i++) {
-                vc4_bo_unreference(&referenced_bos[i]);
+        struct hash_entry *entry = _mesa_hash_table_search(vc4->write_jobs,
+                                                           prsc);
+        if (entry) {
+                struct vc4_job *job = entry->data;
+                vc4_job_submit(vc4, job);
         }
-        vc4_reset_cl(&job->bcl);
-        vc4_reset_cl(&job->shader_rec);
-        vc4_reset_cl(&job->uniforms);
-        vc4_reset_cl(&job->bo_handles);
-        vc4_reset_cl(&job->bo_pointers);
-        job->shader_rec_count = 0;
+}
 
-        job->needs_flush = false;
-        job->draw_calls_queued = 0;
+void
+vc4_flush_jobs_reading_resource(struct vc4_context *vc4,
+                                struct pipe_resource *prsc)
+{
+        struct vc4_resource *rsc = vc4_resource(prsc);
 
-        job->resolve = 0;
-        job->cleared = 0;
+        vc4_flush_jobs_writing_resource(vc4, prsc);
 
-        job->draw_min_x = ~0;
-        job->draw_min_y = ~0;
-        job->draw_max_x = 0;
-        job->draw_max_y = 0;
+        hash_table_foreach(vc4->jobs, entry) {
+                struct vc4_job *job = entry->data;
 
-        pipe_surface_reference(&job->color_write, NULL);
-        pipe_surface_reference(&job->color_read, NULL);
-        pipe_surface_reference(&job->msaa_color_write, NULL);
-        pipe_surface_reference(&job->zs_write, NULL);
-        pipe_surface_reference(&job->zs_read, NULL);
-        pipe_surface_reference(&job->msaa_zs_write, NULL);
+                struct vc4_bo **referenced_bos = job->bo_pointers.base;
+                bool found = false;
+                for (int i = 0; i < cl_offset(&job->bo_handles) / 4; i++) {
+                        if (referenced_bos[i] == rsc->bo) {
+                                found = true;
+                                break;
+                        }
+                }
+                if (found) {
+                        vc4_job_submit(vc4, job);
+                        continue;
+                }
+
+                /* Also check for the Z/color buffers, since the references to
+                 * those are only added immediately before submit.
+                 */
+                if (job->color_read && !(job->cleared & PIPE_CLEAR_COLOR)) {
+                        struct vc4_resource *ctex =
+                                vc4_resource(job->color_read->texture);
+                        if (ctex->bo == rsc->bo) {
+                                vc4_job_submit(vc4, job);
+                                continue;
+                        }
+                }
+
+                if (job->zs_read && !(job->cleared &
+                                      (PIPE_CLEAR_DEPTH | PIPE_CLEAR_STENCIL))) {
+                        struct vc4_resource *ztex =
+                                vc4_resource(job->zs_read->texture);
+                        if (ztex->bo == rsc->bo) {
+                                vc4_job_submit(vc4, job);
+                                continue;
+                        }
+                }
+        }
+}
+
+/**
+ * Returns a vc4_job struture for tracking V3D rendering to a particular FBO.
+ *
+ * If we've already started rendering to this FBO, then return old same job,
+ * otherwise make a new one.  If we're beginning rendering to an FBO, make
+ * sure that any previous reads of the FBO (or writes to its color/Z surfaces)
+ * have been flushed.
+ */
+struct vc4_job *
+vc4_get_job(struct vc4_context *vc4,
+            struct pipe_surface *cbuf, struct pipe_surface *zsbuf)
+{
+        /* Return the existing job for this FBO if we have one */
+        struct vc4_job_key local_key = {.cbuf = cbuf, .zsbuf = zsbuf};
+        struct hash_entry *entry = _mesa_hash_table_search(vc4->jobs,
+                                                           &local_key);
+        if (entry)
+                return entry->data;
+
+        /* Creating a new job.  Make sure that any previous jobs reading or
+         * writing these buffers are flushed.
+         */
+        if (cbuf)
+                vc4_flush_jobs_reading_resource(vc4, cbuf->texture);
+        if (zsbuf)
+                vc4_flush_jobs_reading_resource(vc4, zsbuf->texture);
+
+        struct vc4_job *job = vc4_job_create(vc4);
+
+        if (cbuf) {
+                if (cbuf->texture->nr_samples > 1) {
+                        job->msaa = true;
+                        pipe_surface_reference(&job->msaa_color_write, cbuf);
+                } else {
+                        pipe_surface_reference(&job->color_write, cbuf);
+                }
+        }
+
+        if (zsbuf) {
+                if (zsbuf->texture->nr_samples > 1) {
+                        job->msaa = true;
+                        pipe_surface_reference(&job->msaa_zs_write, zsbuf);
+                } else {
+                        pipe_surface_reference(&job->zs_write, zsbuf);
+                }
+        }
+
+        if (job->msaa) {
+                job->tile_width = 32;
+                job->tile_height = 32;
+        } else {
+                job->tile_width = 64;
+                job->tile_height = 64;
+        }
+
+        if (cbuf)
+                _mesa_hash_table_insert(vc4->write_jobs, cbuf->texture, job);
+        if (zsbuf)
+                _mesa_hash_table_insert(vc4->write_jobs, zsbuf->texture, job);
+
+        job->key.cbuf = cbuf;
+        job->key.zsbuf = zsbuf;
+        _mesa_hash_table_insert(vc4->jobs, &job->key, job);
+
+        return job;
+}
+
+struct vc4_job *
+vc4_get_job_for_fbo(struct vc4_context *vc4)
+{
+        if (vc4->job)
+                return vc4->job;
+
+        struct pipe_surface *cbuf = vc4->framebuffer.cbufs[0];
+        struct pipe_surface *zsbuf = vc4->framebuffer.zsbuf;
+        struct vc4_job *job = vc4_get_job(vc4, cbuf, zsbuf);
+
+        /* The dirty flags are tracking what's been updated while vc4->job has
+         * been bound, so set them all to ~0 when switching between jobs.  We
+         * also need to reset all state at the start of rendering.
+         */
+        vc4->dirty = ~0;
+
+        /* Set up the read surfaces in the job.  If they aren't actually
+         * getting read (due to a clear starting the frame), job->cleared will
+         * mask out the read.
+         */
+        pipe_surface_reference(&job->color_read, cbuf);
+        pipe_surface_reference(&job->zs_read, zsbuf);
+
+        /* If we're binding to uninitialized buffers, no need to load their
+         * contents before drawing.
+         */
+        if (cbuf) {
+                struct vc4_resource *rsc = vc4_resource(cbuf->texture);
+                if (!rsc->writes)
+                        job->cleared |= PIPE_CLEAR_COLOR0;
+        }
+
+        if (zsbuf) {
+                struct vc4_resource *rsc = vc4_resource(zsbuf->texture);
+                if (!rsc->writes)
+                        job->cleared |= PIPE_CLEAR_DEPTH | PIPE_CLEAR_STENCIL;
+        }
+
+        job->draw_tiles_x = DIV_ROUND_UP(vc4->framebuffer.width,
+                                         job->tile_width);
+        job->draw_tiles_y = DIV_ROUND_UP(vc4->framebuffer.height,
+                                         job->tile_height);
+
+        /* Initialize the job with the raster order flags -- each draw will
+         * check that we haven't changed the flags, since that requires a
+         * flush.
+         */
+        if (vc4->rasterizer)
+                job->flags = vc4->rasterizer->tile_raster_order_flags;
+
+        vc4->job = job;
+
+        return job;
 }
 
 static void
@@ -166,15 +370,14 @@ void
 vc4_job_submit(struct vc4_context *vc4, struct vc4_job *job)
 {
         if (!job->needs_flush)
-                return;
+                goto done;
 
         /* The RCL setup would choke if the draw bounds cause no drawing, so
          * just drop the drawing if that's the case.
          */
         if (job->draw_max_x <= job->draw_min_x ||
             job->draw_max_y <= job->draw_min_y) {
-                vc4_job_reset(job);
-                return;
+                goto done;
         }
 
         if (vc4_debug & VC4_DEBUG_CL) {
@@ -188,13 +391,11 @@ vc4_job_submit(struct vc4_context *vc4, struct vc4_job *job)
                  * until the FLUSH completes.
                  */
                 cl_ensure_space(&job->bcl, 8);
-                struct vc4_cl_out *bcl = cl_start(&job->bcl);
-                cl_u8(&bcl, VC4_PACKET_INCREMENT_SEMAPHORE);
+                cl_emit(&job->bcl, INCREMENT_SEMAPHORE, incr);
                 /* The FLUSH caps all of our bin lists with a
                  * VC4_PACKET_RETURN.
                  */
-                cl_u8(&bcl, VC4_PACKET_FLUSH);
-                cl_end(&job->bcl, bcl);
+                cl_emit(&job->bcl, FLUSH, flush);
         }
         struct drm_vc4_submit_cl submit = {
                 .color_read.hindex = ~0,
@@ -253,6 +454,8 @@ vc4_job_submit(struct vc4_context *vc4, struct vc4_job *job)
         submit.shader_rec_count = job->shader_rec_count;
         submit.uniforms = (uintptr_t)job->uniforms.base;
         submit.uniforms_size = cl_offset(&job->uniforms);
+       if (job->perfmon)
+               submit.perfmonid = job->perfmon->id;
 
         assert(job->draw_min_x != ~0 && job->draw_min_y != ~0);
         submit.min_x_tile = job->draw_min_x / job->tile_width;
@@ -268,15 +471,25 @@ vc4_job_submit(struct vc4_context *vc4, struct vc4_job *job)
                 submit.clear_z = job->clear_depth;
                 submit.clear_s = job->clear_stencil;
         }
+        submit.flags |= job->flags;
+
+        if (vc4->screen->has_syncobj) {
+                submit.out_sync = vc4->job_syncobj;
+
+                if (vc4->in_fence_fd >= 0) {
+                        /* This replaces the fence in the syncobj. */
+                        drmSyncobjImportSyncFile(vc4->fd, vc4->in_syncobj,
+                                                 vc4->in_fence_fd);
+                        submit.in_sync = vc4->in_syncobj;
+                        close(vc4->in_fence_fd);
+                        vc4->in_fence_fd = -1;
+                }
+        }
 
         if (!(vc4_debug & VC4_DEBUG_NORAST)) {
                 int ret;
 
-#ifndef USE_VC4_SIMULATOR
-                ret = drmIoctl(vc4->fd, DRM_IOCTL_VC4_SUBMIT_CL, &submit);
-#else
-                ret = vc4_simulator_flush(vc4, &submit);
-#endif
+                ret = vc4_ioctl(vc4->fd, DRM_IOCTL_VC4_SUBMIT_CL, &submit);
                 static bool warned = false;
                 if (ret && !warned) {
                         fprintf(stderr, "Draw call returned %s.  "
@@ -284,6 +497,8 @@ vc4_job_submit(struct vc4_context *vc4, struct vc4_job *job)
                         warned = true;
                 } else if (!ret) {
                         vc4->last_emit_seqno = submit.seqno;
+                        if (job->perfmon)
+                                job->perfmon->last_seqno = submit.seqno;
                 }
         }
 
@@ -304,5 +519,49 @@ vc4_job_submit(struct vc4_context *vc4, struct vc4_job *job)
                 }
         }
 
-        vc4_job_reset(vc4->job);
+done:
+        vc4_job_free(vc4, job);
 }
+
+static bool
+vc4_job_compare(const void *a, const void *b)
+{
+        return memcmp(a, b, sizeof(struct vc4_job_key)) == 0;
+}
+
+static uint32_t
+vc4_job_hash(const void *key)
+{
+        return _mesa_hash_data(key, sizeof(struct vc4_job_key));
+}
+
+int
+vc4_job_init(struct vc4_context *vc4)
+{
+        vc4->jobs = _mesa_hash_table_create(vc4,
+                                            vc4_job_hash,
+                                            vc4_job_compare);
+        vc4->write_jobs = _mesa_hash_table_create(vc4,
+                                                  _mesa_hash_pointer,
+                                                  _mesa_key_pointer_equal);
+
+        if (vc4->screen->has_syncobj) {
+                /* Create the syncobj as signaled since with no job executed
+                 * there is nothing to wait on.
+                 */
+                int ret = drmSyncobjCreate(vc4->fd,
+                                           DRM_SYNCOBJ_CREATE_SIGNALED,
+                                           &vc4->job_syncobj);
+                if (ret) {
+                        /* If the screen indicated syncobj support, we should
+                         * be able to create a signaled syncobj.
+                         * At this point it is too late to pretend the screen
+                         * has no syncobj support.
+                         */
+                        return ret;
+                }
+        }
+
+        return 0;
+}
+