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 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev