<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Fri, Mar 24, 2017 at 5:53 AM, Iago Toral <span dir="ltr"><<a href="mailto:itoral@igalia.com" target="_blank">itoral@igalia.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="HOEnZb"><div class="h5">On Wed, 2017-03-22 at 21:01 -0700, Jason Ekstrand wrote:<br>
---<br>
src/intel/vulkan/anv_private.<wbr>h | 6 ++<br>
src/intel/vulkan/genX_cmd_<wbr>buffer.c | 141<br>
++++++++++++++++++++++++++++++<wbr>++++++-<br>
src/intel/vulkan/genX_<wbr>pipeline.c | 10 ++-<br>
3 files changed, 152 insertions(+), 5 deletions(-)<br>
<br>
<br>
diff --git a/src/intel/vulkan/anv_<wbr>private.h<br>
b/src/intel/vulkan/anv_<wbr>private.h<br>
index b402bc3..253dce2 100644<br>
--- a/src/intel/vulkan/anv_<wbr>private.h<br>
+++ b/src/intel/vulkan/anv_<wbr>private.h<br>
@@ -1980,6 +1980,12 @@ struct anv_subpass {<br>
bool <wbr> has_<wbr>resolve;<br>
};<br>
<br>
+static inline unsigned<br>
+anv_subpass_view_count(const struct anv_subpass *subpass)<br>
+{<br>
+ return MAX2(1, _mesa_bitcount(subpass->view_<wbr>mask));<br>
+}<br>
+<br>
enum anv_subpass_usage {<br>
ANV_SUBPASS_USAGE_DRAW = (1 << 0),<br>
ANV_SUBPASS_USAGE_INPUT = (1 << 1),<br>
diff --git a/src/intel/vulkan/genX_cmd_<wbr>buffer.c<br>
b/src/intel/vulkan/genX_cmd_<wbr>buffer.c<br>
index aafe7fd..8c21c33 100644<br>
--- a/src/intel/vulkan/genX_cmd_<wbr>buffer.c<br>
+++ b/src/intel/vulkan/genX_cmd_<wbr>buffer.c<br>
@@ -26,6 +26,7 @@<br>
<br>
#include "anv_private.h"<br>
#include "vk_format_info.h"<br>
+#include "util/vk_util.h"<br>
<br>
#include "common/gen_l3_config.h"<br>
#include "genxml/gen_macros.h"<br>
@@ -50,6 +51,17 @@ emit_lri(struct anv_batch *batch, uint32_t reg,<br>
uint32_t imm)<br>
}<br>
}<br>
<br>
+#if GEN_IS_HASWELL || GEN_GEN >= 8<br>
+static void<br>
+emit_lrr(struct anv_batch *batch, uint32_t dst, uint32_t src)<br>
+{<br>
+ anv_batch_emit(batch, GENX(MI_LOAD_REGISTER_REG), lrr) {<br>
+ lrr.<wbr>SourceRegisterAddress = src;<br>
+ lrr.<wbr>DestinationRegisterAddress = dst;<br>
+ }<br>
+}<br>
+#endif<br>
+<br>
void<br>
genX(cmd_buffer_emit_state_<wbr>base_address)(struct anv_cmd_buffer<br>
*cmd_buffer)<br>
{<br>
@@ -1525,7 +1537,13 @@ genX(cmd_buffer_flush_state)(<wbr>struct<br>
anv_cmd_buffer *cmd_buffer)<br>
.<wbr>MemoryObjectControlState = GENX(MOCS),<br>
#else<br>
.BufferAccessType = pipeline->instancing_enable[<wbr>vb] ?<br>
INSTANCEDATA : VERTEXDATA,<br>
- .<wbr>InstanceDataStepRate = 1,<br>
+ /* Our implementation of VK_KHR_multiview uses<br>
instancing to draw<br>
+ * the different views. If the client asks for<br>
instancing, we<br>
+ * need to use the Instance Data Step Rate to ensure<br>
that we<br>
+ * repeat the client's per-instance data once for each<br>
view.<br>
+ */<br>
+ .<wbr>InstanceDataStepRate =<br>
+ _mesa_<wbr>bitcount(pipeline->subpass-><wbr>view_mask),<br>
<br>
</div></div>mmm... shouldn't this be:<br>
<br>
.InstanceDataStepRate = anv_subpass_view_count(<wbr>pipeline->subpass);<br>
<br>
so that we set this to 1 when multiview is not in use? (view_mask == 0)<br></blockquote><div><br></div><div>Good call! You're absolutely right. This line is older than anv_subpass_view_count, I think.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div class="h5"><br>
.<wbr>VertexBufferMemoryObjectContro<wbr>lState = GENX(MOCS),<br>
#endif<br>
<br>
@@ -1715,6 +1733,11 @@ void genX(CmdDraw)(<br>
if (vs_prog_data->uses_drawid)<br>
emit_draw_index(cmd_<wbr>buffer, 0);<br>
<br>
+ /* Our implementation of VK_KHR_multiview uses instancing to draw<br>
the<br>
+ * different views. We need to multiply instanceCount by the<br>
view count.<br>
+ */<br>
+ instanceCount *= anv_subpass_view_count(cmd_<wbr>buffer-<br>
>state.subpass);<br>
+<br>
anv_batch_emit(&cmd_<wbr>buffer->batch, GENX(3DPRIMITIVE), prim) {<br>
prim.VertexAccessType <wbr> = SEQUENTIAL;<br>
prim.<wbr>PrimitiveTopologyType = pipeline->topology;<br>
@@ -1748,6 +1771,11 @@ void genX(CmdDrawIndexed)(<br>
if (vs_prog_data->uses_drawid)<br>
emit_draw_index(cmd_<wbr>buffer, 0);<br>
<br>
+ /* Our implementation of VK_KHR_multiview uses instancing to draw<br>
the<br>
+ * different views. We need to multiply instanceCount by the<br>
view count.<br>
+ */<br>
+ instanceCount *= anv_subpass_view_count(cmd_<wbr>buffer-<br>
>state.subpass);<br>
+<br>
anv_batch_emit(&cmd_<wbr>buffer->batch, GENX(3DPRIMITIVE), prim) {<br>
prim.VertexAccessType <wbr> = RANDOM;<br>
prim.<wbr>PrimitiveTopologyType = pipeline->topology;<br>
@@ -1767,6 +1795,90 @@ void genX(CmdDrawIndexed)(<br>
#define GEN7_3DPRIM_START_INSTANCE <wbr> 0x243C<br>
#define GEN7_3DPRIM_BASE_VERTEX <wbr> 0x2440<br>
<br>
+/* MI_MATH only exists on Haswell+ */<br>
+#if GEN_IS_HASWELL || GEN_GEN >= 8<br>
+<br>
+#define alu_opcode(v) __gen_uint((v)<wbr>, 20, 31)<br>
+#define alu_operand1(v) __gen_uint((v), 10, 19)<br>
+#define alu_operand2(v) __gen_uint((v), 0, 9)<br>
+#define alu(opcode, operand1, operand2) \<br>
+ alu_opcode(opcode) | alu_operand1(operand1) |<br>
alu_operand2(operand2)<br>
+<br>
+#define OPCODE_NOOP 0x000<br>
+#define OPCODE_LOAD 0x080<br>
+#define OPCODE_LOADINV 0x480<br>
+#define OPCODE_LOAD0 0x081<br>
+#define OPCODE_LOAD1 0x481<br>
+#define OPCODE_ADD 0x100<br>
+#define OPCODE_SUB 0x101<br>
+#define OPCODE_AND 0x102<br>
+#define OPCODE_OR 0x103<br>
+#define OPCODE_XOR 0x104<br>
+#define OPCODE_STORE 0x180<br>
+#define OPCODE_STOREINV 0x580<br>
+<br>
+#define OPERAND_R0 0x00<br>
+#define OPERAND_R1 0x01<br>
+#define OPERAND_R2 0x02<br>
+#define OPERAND_R3 0x03<br>
+#define OPERAND_R4 0x04<br>
+#define OPERAND_SRCA 0x20<br>
+#define OPERAND_SRCB 0x21<br>
+#define OPERAND_ACCU 0x31<br>
+#define OPERAND_ZF 0x32<br>
+#define OPERAND_CF 0x33<br>
+<br>
+#define CS_GPR(n) (0x2600 + (n) * 8)<br>
+<br>
+/* Emit dwords to multiply GPR0 by N */<br>
+static void<br>
+build_alu_multiply_gpr0(<wbr>uint32_t *dw, unsigned *dw_count, uint32_t<br>
N)<br>
+{<br>
+ VK_OUTARRAY_MAKE(out, dw, dw_count);<br>
+<br>
+#define append_alu(opcode, operand1, operand2) \<br>
+ vk_outarray_append(&out, alu_dw) *alu_dw = alu(opcode, operand1,<br>
operand2)<br>
+<br>
+ assert(N > 0);<br>
+ unsigned top_bit = 31 - __builtin_clz(N);<br>
+ for (int i = top_bit - 1; i >= 0; i--) {<br>
+ /* We get our initial data in GPR0 and we write the final data<br>
out to<br>
+ * GPR0 but we use GPR1 as our scratch register.<br>
+ */<br>
+ unsigned src_reg = i == top_bit - 1 ? OPERAND_R0 : OPERAND_R1;<br>
+ unsigned dst_reg = i == 0 ? OPERAND_R0 : OPERAND_R1;<br>
+<br>
+ /* Shift the current value left by 1 */<br>
+ append_alu(OPCODE_LOAD, OPERAND_SRCA, src_reg);<br>
+ append_alu(OPCODE_LOAD, OPERAND_SRCB, src_reg);<br>
+ append_alu(OPCODE_AND, 0, 0);<br>
+<br>
+ if (N & (1 << i)) {<br>
+ /* Store ACCU to R1 and add R0 to R1 */<br>
+ append_alu(OPCODE_<wbr>STORE, OPERAND_R1, OPERAND_ACCU);<br>
+ append_alu(OPCODE_<wbr>LOAD, OPERAND_SRCA, OPERAND_R0);<br>
+ append_alu(OPCODE_<wbr>LOAD, OPERAND_SRCB, OPERAND_R1);<br>
+ append_alu(OPCODE_<wbr>AND, 0, 0);<br>
+ }<br>
+<br>
+ append_alu(OPCODE_<wbr>STORE, dst_reg, OPERAND_ACCU);<br>
+ }<br>
+<br>
+#undef append_alu<br>
+}<br>
+<br>
+static void<br>
+emit_mul_gpr0(struct anv_batch *batch, uint32_t N)<br>
+{<br>
+ uint32_t num_dwords;<br>
> + build_alu_multiply_gpr0(<wbr>NULL, &num_dwords, N);<br>
<br>
</div></div>I don't get this, it seems that the expectation of this code is<br>
that build_alu_multiply_gpr0() fills in num_dwords when we call it<br>
with NULL in the first argument, but I don't see where it does that,<br>
what am I missing?<br></blockquote><div><br></div><div>build_alu_multiply_gpr0 has the same semantics as the Vulkan query functions that return arrays. If you call it with NULL, it returns the number of dwords required. Otherwise, it assumes num_dwords is the number of dwords that it has available to write. I made use of vk_outarray to get this behavior. Yeah, it's a bit deceptive, but it worked out really well. :-) Feel free to tell me that it's a bit too clever and that I should make it simpler.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="h5">
+ <br>
+ uint32_t *dw = anv_batch_emitn(batch, 1 + num_dwords,<br>
GENX(MI_MATH));<br>
+ build_alu_multiply_gpr0(<wbr>dw, &num_dwords, N);<br>
+}<br>
+<br>
+#endif /* GEN_IS_HASWELL || GEN_GEN >= 8 */<br>
+<br>
static void<br>
load_indirect_parameters(<wbr>struct anv_cmd_buffer *cmd_buffer,<br>
<wbr>struct anv_buffer *buffer, uint64_t offset,<br>
@@ -1777,7 +1889,22 @@ load_indirect_parameters(<wbr>struct anv_cmd_buffer<br>
*cmd_buffer,<br>
uint32_t bo_offset = buffer->offset + offset;<br>
<br>
emit_lrm(batch, GEN7_3DPRIM_VERTEX_COUNT, bo, bo_offset);<br>
- emit_lrm(batch, GEN7_3DPRIM_INSTANCE_COUNT, bo, bo_offset + 4);<br>
+<br>
+ unsigned view_count = anv_subpass_view_count(cmd_<wbr>buffer-<br>
>state.subpass);<br>
+ if (view_count > 1) {<br>
+#if GEN_IS_HASWELL || GEN_GEN >= 8<br>
+ emit_lrm(batch, CS_GPR(0), bo, bo_offset + 4);<br>
+ emit_mul_gpr0(batch, view_count);<br>
+ emit_lrr(batch, GEN7_3DPRIM_INSTANCE_COUNT, CS_GPR(0));<br>
+#else<br>
+ anv_finishme("Multiview + indirect draw requires MI_MATH\n"<br>
+ "MI_MATH is not supported on Ivy Bridge");<br>
+ emit_lrm(batch, GEN7_3DPRIM_INSTANCE_COUNT, bo, bo_offset +<br>
4);<br>
+#endif<br>
+ } else {<br>
+ emit_lrm(batch, GEN7_3DPRIM_INSTANCE_COUNT, bo, bo_offset +<br>
4);<br>
+ }<br>
+<br>
emit_lrm(batch, GEN7_3DPRIM_START_VERTEX, bo, bo_offset + 8);<br>
<br>
if (indexed) {<br>
@@ -2531,6 +2658,16 @@ genX(cmd_buffer_set_subpass)(<wbr>struct<br>
anv_cmd_buffer *cmd_buffer,<br>
<br>
cmd_buffer->state.dirty |= ANV_CMD_DIRTY_RENDER_TARGETS;<br>
<br>
+ /* Our implementation of VK_KHR_multiview uses instancing to draw<br>
the<br>
+ * different views. If the client asks for instancing, we need<br>
to use the<br>
+ * Instance Data Step Rate to ensure that we repeat the client's<br>
+ * per-instance data once for each view. Since this bit is in<br>
+ * VERTEX_BUFFER_STATE on gen7, we need to dirty vertex buffers<br>
at the top<br>
+ * of each subpass.<br>
+ */<br>
+ if (GEN_GEN == 7)<br>
+ cmd_buffer->state.vb_<wbr>dirty |= ~0;<br>
> +<br>
<br>
</div></div>Shouldn't we do:<br>
<br>
if (GEN_GEN == 7 && subpass->view_mask != 0)<br>
cmd_buffer->state.vb_dirty |= ~0;<br>
<br>
So this doesn't affect cases that don't use multiview? </blockquote><div><br></div>Yes, we could do that because we're guaranteed that, for each render pass, either all subpass view masks are 0 or all are non-zero. This seems safer though and I doubt flushing vertex buffers at the subpass boundary will hurt anyone.<br></div><div class="gmail_quote"><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="h5">
> /* Perform transitions to the subpass layout before any writes<br>
> have<br>
* occurred.<br>
*/<br>
diff --git a/src/intel/vulkan/genX_<wbr>pipeline.c<br>
b/src/intel/vulkan/genX_<wbr>pipeline.c<br>
index 85a9e4f..9aaa8a0 100644<br>
--- a/src/intel/vulkan/genX_<wbr>pipeline.c<br>
+++ b/src/intel/vulkan/genX_<wbr>pipeline.c<br>
@@ -155,9 +155,13 @@ emit_vertex_input(struct anv_pipeline *pipeline,<br>
anv_batch_emit(&<wbr>pipeline->batch, GENX(3DSTATE_VF_INSTANCING),<br>
vfi) {<br>
vfi.InstancingEnable = pipeline->instancing_enable[<wbr>desc-<br>
>binding];<br>
vfi.<wbr>VertexElementIndex = slot;<br>
- /* Vulkan so far doesn't have an instance divisor, so<br>
- * this is always 1 (ignored if not instancing). */<br>
- vfi.<wbr>InstanceDataStepRate = 1;<br>
+ /* Our implementation of VK_KHX_multiview uses instancing<br>
to draw<br>
+ * the different views. If the client asks for instancing,<br>
we<br>
+ * need to use the Instance Data Step Rate to ensure that<br>
we<br>
+ * repeat the client's per-instance data once for each<br>
view.<br>
+ */<br>
+ vfi.<wbr>InstanceDataStepRate =<br>
> + _mesa_bitcount(<wbr>pipeline->subpass->view_mask);<br>
<br>
</div></div>Same comment as above, shouldn't this be:<br>
<br>
vfi.InstanceDataStepRate = anv_subpass_view_count(<wbr>pipeline->subpass);<br></blockquote><div><br></div><div>Yup. Thanks! I've fixed both locally.<br></div></div></div></div>