[Mesa-dev] [PATCH 5/6] i965: check if we support pipelined register writes at screen creation time

Iago Toral Quiroga itoral at igalia.com
Tue Jan 3 10:42:55 UTC 2017


We need this feature to enable OpenGL 4.0 on gen7 hardware. Some hardware and
kernel combinations support this while other don't, so we check for it by
writing to a register and verifying the result.

Unfortunately, we need to know this at screen creation time when we don't have
a brw_context available, which means that we can't use all of our usual
infrastructure to emit batches and we need to replicate a small part of it for
this particular case.

The good thing is that because this is a very particular case we don't need to
replicate all the logic involved in our generic batch buffer emissions, since
we don't need to care about things like ring changes, exceeding the size of the
batch, rendering caches, etc, which greatly simplifies the amount of
infrastructure required.

We are also only handling gen7 here, because that is the only case where we
need to do this. If we need similar solutions on other hardware platforms in
the future we might need to add more code to brw_check_register_writes.c or
further refactor our intel_batchbuffer code to reduce its dependency from
brw_context.
---
 src/mesa/drivers/dri/i965/Makefile.sources         |   1 +
 .../drivers/dri/i965/brw_check_register_writes.c   | 212 +++++++++++++++++++++
 src/mesa/drivers/dri/i965/brw_context.h            |   3 +
 src/mesa/drivers/dri/i965/intel_extensions.c       |  76 +-------
 4 files changed, 219 insertions(+), 73 deletions(-)
 create mode 100644 src/mesa/drivers/dri/i965/brw_check_register_writes.c

diff --git a/src/mesa/drivers/dri/i965/Makefile.sources b/src/mesa/drivers/dri/i965/Makefile.sources
index 0a7ba1b..9514b9f 100644
--- a/src/mesa/drivers/dri/i965/Makefile.sources
+++ b/src/mesa/drivers/dri/i965/Makefile.sources
@@ -1,6 +1,7 @@
 i965_compiler_FILES = \
 	brw_cfg.cpp \
 	brw_cfg.h \
+	brw_check_register_writes.c \
 	brw_compiler.c \
 	brw_compiler.h \
 	brw_dead_control_flow.cpp \
diff --git a/src/mesa/drivers/dri/i965/brw_check_register_writes.c b/src/mesa/drivers/dri/i965/brw_check_register_writes.c
new file mode 100644
index 0000000..5f7351e
--- /dev/null
+++ b/src/mesa/drivers/dri/i965/brw_check_register_writes.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <intel_bufmgr.h>
+
+#include "brw_context.h"
+#include "intel_screen.h"
+#include "intel_batchbuffer.h"
+#include "brw_defines.h"
+
+#define __BEGIN_BATCH(n) do {                        \
+   ctx->batch->ring = RENDER_RING;                   \
+   uint32_t *___map = ctx->batch->map_next;          \
+   ctx->batch->map_next += (n);
+
+#define __OUT_BATCH(d) *___map++ = (d)
+
+#define __ADVANCE_BATCH()   \
+} while (0)
+
+#define __OUT_RELOC(buf, delta) do { \
+   uint32_t __offset = (___map - ctx->batch->map) * 4;                  \
+   __OUT_BATCH(intel_batchbuffer_reloc(ctx->batch, (buf), __offset,     \
+                                       (I915_GEM_DOMAIN_INSTRUCTION),   \
+                                       (I915_GEM_DOMAIN_INSTRUCTION),   \
+                                       (delta)));                       \
+} while (0)
+
+struct check_register_writes_context {
+   dri_bufmgr *bufmgr;
+   drm_intel_context *hw_ctx;
+   struct intel_batchbuffer *batch;
+   const struct gen_device_info *devinfo;
+};
+
+static void
+emit_mi_flush(struct check_register_writes_context *ctx)
+{
+   assert(ctx->batch->ring == RENDER_RING);
+   assert(ctx->devinfo->gen == 7);
+
+   __BEGIN_BATCH(5);
+   __OUT_BATCH(_3DSTATE_PIPE_CONTROL | (5 - 2));
+   __OUT_BATCH(0); /* flags */
+   __OUT_BATCH(0);
+   __OUT_BATCH(0);
+   __OUT_BATCH(0);
+   __ADVANCE_BATCH();
+}
+
+static int
+do_flush_locked(struct check_register_writes_context *ctx)
+{
+   drm_intel_bo_unmap(ctx->batch->bo);
+   int ret = drm_intel_gem_bo_context_exec(ctx->batch->bo, ctx->hw_ctx,
+                                           4 * USED_BATCH(*(ctx->batch)),
+                                           I915_EXEC_RENDER);
+   return ret;
+}
+
+static int
+batchbuffer_flush(struct check_register_writes_context *ctx)
+{
+   ctx->batch->reserved_space = 0;
+
+   /* Mark the end of the buffer. */
+   intel_batchbuffer_emit_dword(ctx->batch, MI_BATCH_BUFFER_END);
+   if (USED_BATCH(*(ctx->batch)) & 1) {
+      /* Round batchbuffer usage to 2 DWORDs. */
+      intel_batchbuffer_emit_dword(ctx->batch, MI_NOOP);
+   }
+
+   int ret = do_flush_locked(ctx);
+
+   return ret;
+}
+
+static struct check_register_writes_context *
+create_ctx(__DRIscreen *dri_screen)
+{
+   struct check_register_writes_context *ctx =
+      (struct check_register_writes_context *)
+         malloc(sizeof(struct check_register_writes_context));
+
+   struct intel_screen *screen =
+      (struct intel_screen *) dri_screen->driverPrivate;
+   ctx->devinfo = (const struct gen_device_info *) &screen->devinfo;
+
+   ctx->bufmgr = intel_bufmgr_gem_init(dri_screen->fd, BATCH_SZ);
+
+   ctx->hw_ctx = drm_intel_gem_context_create(ctx->bufmgr);
+
+   ctx->batch =
+      (struct intel_batchbuffer *) calloc(1, sizeof(struct intel_batchbuffer));
+
+   intel_batchbuffer_init(ctx->batch, ctx->bufmgr, ctx->devinfo->has_llc);
+
+   return ctx;
+}
+
+static void
+free_ctx(struct check_register_writes_context *ctx)
+{
+   intel_batchbuffer_free(ctx->batch);
+   free(ctx->batch);
+   drm_intel_gem_context_destroy(ctx->hw_ctx);
+   dri_bufmgr_destroy(ctx->bufmgr);
+   free(ctx);
+}
+
+/*
+ * Test if we can use MI_LOAD_REGISTER_MEM from an untrusted batchbuffer.
+ *
+ * Some combinations of hardware and kernel versions allow this feature,
+ * while others don't.  Instead of trying to enumerate every case, just
+ * try and write a register and see if works.
+ */
+int
+brw_can_do_pipelined_register_writes(__DRIscreen *dri_screen)
+{
+   /* gen >= 8 specifically allows these writes. gen <= 6 also
+    * doesn't block them.
+    */
+   struct intel_screen *screen =
+      (struct intel_screen *) dri_screen->driverPrivate;
+   if (screen->devinfo.gen != 7)
+      return true;
+
+   static int result = -1;
+   if (result != -1)
+      return result;
+
+   struct check_register_writes_context *ctx = create_ctx(dri_screen);
+
+   /* We use SO_WRITE_OFFSET0 since you're supposed to write it (unlike the
+    * statistics registers), and we already reset it to zero before using it.
+    */
+   const int reg = GEN7_SO_WRITE_OFFSET(0);
+   const int expected_value = 0x1337d0d0;
+   const int offset = 100;
+
+   /* The register we picked only exists on Gen7+. */
+   assert(screen->devinfo.gen == 7);
+
+   /* Set a value in a BO to a known quantity */
+   uint32_t *data;
+   drm_intel_bo *bo =
+      drm_intel_bo_alloc(ctx->bufmgr, "brw_can_do_pipelined_register_writes",
+                         4096, 4096);
+
+   drm_intel_bo_map(bo, true);
+   data = bo->virtual;
+   data[offset] = 0xffffffff;
+   drm_intel_bo_unmap(bo);
+
+   /* Write the register. */
+   __BEGIN_BATCH(3);
+   __OUT_BATCH(MI_LOAD_REGISTER_IMM | (3 - 2));
+   __OUT_BATCH(reg);
+   __OUT_BATCH(expected_value);
+   __ADVANCE_BATCH();
+
+   emit_mi_flush(ctx);
+
+   /* Save the register's value back to the buffer. */
+   __BEGIN_BATCH(3);
+   __OUT_BATCH(MI_STORE_REGISTER_MEM | (3 - 2));
+   __OUT_BATCH(reg);
+   __OUT_RELOC(bo, offset * sizeof(uint32_t));
+   __ADVANCE_BATCH();
+
+   batchbuffer_flush(ctx);
+
+   /* Check whether the value got written. */
+   drm_intel_bo_map(bo, false);
+   data = bo->virtual;
+   bool success = data[offset] == expected_value;
+   drm_intel_bo_unmap(bo);
+
+   result = success;
+
+   /* Cleanup */
+   drm_intel_bo_unreference(bo);
+   free_ctx(ctx);
+
+   return success;
+}
+
+#undef __BEGIN_BATCH
+#undef __OUT_BATCH
+#undef __OUT_RELOC
+#undef __ADVANCE_BATCH
diff --git a/src/mesa/drivers/dri/i965/brw_context.h b/src/mesa/drivers/dri/i965/brw_context.h
index 3146baf..da469e1 100644
--- a/src/mesa/drivers/dri/i965/brw_context.h
+++ b/src/mesa/drivers/dri/i965/brw_context.h
@@ -1807,6 +1807,9 @@ void brw_query_internal_format(struct gl_context *ctx, GLenum target,
                                GLenum internalFormat, GLenum pname,
                                GLint *params);
 
+/* brw_check_register_writes.c */
+int brw_can_do_pipelined_register_writes(__DRIscreen *dri_screen);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/mesa/drivers/dri/i965/intel_extensions.c b/src/mesa/drivers/dri/i965/intel_extensions.c
index bbb7942..5be8f3a 100644
--- a/src/mesa/drivers/dri/i965/intel_extensions.c
+++ b/src/mesa/drivers/dri/i965/intel_extensions.c
@@ -29,77 +29,6 @@
 #include "brw_defines.h"
 #include "intel_batchbuffer.h"
 
-/**
- * Test if we can use MI_LOAD_REGISTER_MEM from an untrusted batchbuffer.
- *
- * Some combinations of hardware and kernel versions allow this feature,
- * while others don't.  Instead of trying to enumerate every case, just
- * try and write a register and see if works.
- */
-static bool
-can_do_pipelined_register_writes(struct brw_context *brw)
-{
-   /**
-    * gen >= 8 specifically allows these writes. gen <= 6 also
-    * doesn't block them.
-    */
-   if (brw->gen != 7)
-      return true;
-
-   static int result = -1;
-   if (result != -1)
-      return result;
-
-   /* We use SO_WRITE_OFFSET0 since you're supposed to write it (unlike the
-    * statistics registers), and we already reset it to zero before using it.
-    */
-   const int reg = GEN7_SO_WRITE_OFFSET(0);
-   const int expected_value = 0x1337d0d0;
-   const int offset = 100;
-
-   /* The register we picked only exists on Gen7+. */
-   assert(brw->gen == 7);
-
-   uint32_t *data;
-   /* Set a value in a BO to a known quantity.  The workaround BO already
-    * exists and doesn't contain anything important, so we may as well use it.
-    */
-   drm_intel_bo_map(brw->workaround_bo, true);
-   data = brw->workaround_bo->virtual;
-   data[offset] = 0xffffffff;
-   drm_intel_bo_unmap(brw->workaround_bo);
-
-   /* Write the register. */
-   BEGIN_BATCH(3);
-   OUT_BATCH(MI_LOAD_REGISTER_IMM | (3 - 2));
-   OUT_BATCH(reg);
-   OUT_BATCH(expected_value);
-   ADVANCE_BATCH();
-
-   brw_emit_mi_flush(brw);
-
-   /* Save the register's value back to the buffer. */
-   BEGIN_BATCH(3);
-   OUT_BATCH(MI_STORE_REGISTER_MEM | (3 - 2));
-   OUT_BATCH(reg);
-   OUT_RELOC(brw->workaround_bo,
-             I915_GEM_DOMAIN_INSTRUCTION, I915_GEM_DOMAIN_INSTRUCTION,
-             offset * sizeof(uint32_t));
-   ADVANCE_BATCH();
-
-   intel_batchbuffer_flush(brw);
-
-   /* Check whether the value got written. */
-   drm_intel_bo_map(brw->workaround_bo, false);
-   data = brw->workaround_bo->virtual;
-   bool success = data[offset] == expected_value;
-   drm_intel_bo_unmap(brw->workaround_bo);
-
-   result = success;
-
-   return success;
-}
-
 static bool
 can_write_oacontrol(struct brw_context *brw)
 {
@@ -270,6 +199,9 @@ intelInitExtensions(struct gl_context *ctx)
    ctx->Extensions.OES_texture_half_float = true;
    ctx->Extensions.OES_texture_half_float_linear = true;
 
+   brw->can_do_pipelined_register_writes =
+      brw_can_do_pipelined_register_writes(brw->screen->driScrnPriv);
+
    if (brw->gen >= 8)
       ctx->Const.GLSLVersion = 450;
    else if (brw->gen >= 6)
@@ -337,8 +269,6 @@ intelInitExtensions(struct gl_context *ctx)
    }
 
    brw->predicate.supported = false;
-   brw->can_do_pipelined_register_writes =
-      can_do_pipelined_register_writes(brw);
 
    if (brw->gen >= 7) {
       ctx->Extensions.ARB_conservative_depth = true;
-- 
2.7.4



More information about the mesa-dev mailing list