summaryrefslogtreecommitdiffstats
path: root/src/gallium/drivers/vc4/vc4_job.c
diff options
context:
space:
mode:
authorEric Anholt <[email protected]>2016-09-08 12:56:11 -0700
committerEric Anholt <[email protected]>2016-09-14 06:25:41 +0100
commitf597ac3966405934e13a9aaa18c73211b5a40c7c (patch)
treef7f09bead790e4cdd210e1ed0ca4c4b150dbe35e /src/gallium/drivers/vc4/vc4_job.c
parentf473348468ae1c68e7ef8eaf29f2cc51d17fbec7 (diff)
vc4: Implement job shuffling
Track rendering to each FBO independently and flush rendering only when necessary. This lets us avoid the overhead of storing and loading the frame when an application momentarily switches to rendering to some other texture in order to continue rendering the main scene. Improves glmark -b desktop:effect=shadow:windows=4 by 27% Improves glmark -b desktop:blur-radius=5:effect=blur:passes=1:separable=true:windows=4 by 17% While I haven't tested other apps, this should help X rendering a lot, and I've heard GLBenchmark needed it too.
Diffstat (limited to 'src/gallium/drivers/vc4/vc4_job.c')
-rw-r--r--src/gallium/drivers/vc4/vc4_job.c276
1 files changed, 245 insertions, 31 deletions
diff --git a/src/gallium/drivers/vc4/vc4_job.c b/src/gallium/drivers/vc4/vc4_job.c
index cb20853fa49..d39472ef131 100644
--- a/src/gallium/drivers/vc4/vc4_job.c
+++ b/src/gallium/drivers/vc4/vc4_job.c
@@ -28,49 +28,239 @@
#include <xf86drm.h>
#include "vc4_context.h"
+#include "util/hash_table.h"
-void
-vc4_job_init(struct vc4_job *job)
+static void
+remove_from_ht(struct hash_table *ht, void *key)
{
- 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);
+ struct hash_entry *entry = _mesa_hash_table_search(ht, key);
+ _mesa_hash_table_remove(ht, entry);
}
-void
-vc4_job_reset(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]);
}
- 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;
+ remove_from_ht(vc4->jobs, &job->key);
- job->resolve = 0;
- job->cleared = 0;
+ if (job->color_write) {
+ remove_from_ht(vc4->write_jobs, job->color_write->texture);
+ pipe_surface_reference(&job->color_write, NULL);
+ }
+ if (job->msaa_color_write) {
+ remove_from_ht(vc4->write_jobs, job->msaa_color_write->texture);
+ pipe_surface_reference(&job->msaa_color_write, NULL);
+ }
+ if (job->zs_write) {
+ remove_from_ht(vc4->write_jobs, job->zs_write->texture);
+ pipe_surface_reference(&job->zs_write, NULL);
+ }
+ if (job->msaa_zs_write) {
+ remove_from_ht(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);
job->draw_min_x = ~0;
job->draw_min_y = ~0;
job->draw_max_x = 0;
job->draw_max_y = 0;
- 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);
+ return job;
+}
+
+void
+vc4_flush_jobs_writing_resource(struct vc4_context *vc4,
+ struct pipe_resource *prsc)
+{
+ 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);
+ }
+}
+
+void
+vc4_flush_jobs_reading_resource(struct vc4_context *vc4,
+ struct pipe_resource *prsc)
+{
+ struct vc4_resource *rsc = vc4_resource(prsc);
+
+ vc4_flush_jobs_writing_resource(vc4, prsc);
+
+ struct hash_entry *entry;
+ hash_table_foreach(vc4->jobs, entry) {
+ struct vc4_job *job = entry->data;
+
+ struct vc4_bo **referenced_bos = job->bo_pointers.base;
+ for (int i = 0; i < cl_offset(&job->bo_handles) / 4; i++) {
+ if (referenced_bos[i] == rsc->bo) {
+ 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);
+
+ vc4->job = job;
+
+ return job;
}
static void
@@ -166,15 +356,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) {
@@ -275,7 +464,7 @@ vc4_job_submit(struct vc4_context *vc4, struct vc4_job *job)
#ifndef USE_VC4_SIMULATOR
ret = drmIoctl(vc4->fd, DRM_IOCTL_VC4_SUBMIT_CL, &submit);
#else
- ret = vc4_simulator_flush(vc4, &submit);
+ ret = vc4_simulator_flush(vc4, &submit, job);
#endif
static bool warned = false;
if (ret && !warned) {
@@ -304,5 +493,30 @@ 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));
+}
+
+void
+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);
}
+