/**
* The following diagram shows how we partition the URB:
*
- * 8kB 8kB Rest of the URB space
- * ____-____ ____-____ _________________-_________________
- * / \ / \ / \
+ * 16kB or 32kB Rest of the URB space
+ * __________-__________ _________________-_________________
+ * / \ / \
* +-------------------------------------------------------------+
- * | VS Push | FS Push | VS |
- * | Constants | Constants | Handles |
+ * | VS/FS/GS Push | VS/GS URB |
+ * | Constants | Entries |
* +-------------------------------------------------------------+
*
* Notably, push constants must be stored at the beginning of the URB
* GT1/GT2 have a maximum constant buffer size of 16kB, while Haswell GT3
* doubles this (32kB).
*
- * Currently we split the constant buffer space evenly between VS and FS.
- * This is probably not ideal, but simple.
+ * Ivybridge and Haswell GT1/GT2 allow push constants to be located (and
+ * sized) in increments of 1kB. Haswell GT3 requires them to be located and
+ * sized in increments of 2kB.
+ *
+ * Currently we split the constant buffer space evenly among whatever stages
+ * are active. This is probably not ideal, but simple.
*
* Ivybridge GT1 and Haswell GT1 have 128kB of URB space.
* Ivybridge GT2 and Haswell GT2 have 256kB of URB space.
* See "Volume 2a: 3D Pipeline," section 1.8, "Volume 1b: Configurations",
* and the documentation for 3DSTATE_PUSH_CONSTANT_ALLOC_xS.
*/
-void
+static void
gen7_allocate_push_constants(struct brw_context *brw)
{
- unsigned size = 8;
- if (brw->is_haswell && brw->gt == 3)
- size = 16;
+ unsigned avail_size = 16;
+ unsigned multiplier =
+ (brw->gen >= 8 || (brw->is_haswell && brw->gt == 3)) ? 2 : 1;
+
+ /* BRW_NEW_GEOMETRY_PROGRAM */
+ bool gs_present = brw->geometry_program;
+
+ unsigned vs_size, gs_size;
+ if (gs_present) {
+ vs_size = avail_size / 3;
+ avail_size -= vs_size;
+ gs_size = avail_size / 2;
+ avail_size -= gs_size;
+ } else {
+ vs_size = avail_size / 2;
+ avail_size -= vs_size;
+ gs_size = 0;
+ }
+ unsigned fs_size = avail_size;
+
+ gen7_emit_push_constant_state(brw, multiplier * vs_size,
+ multiplier * gs_size, multiplier * fs_size);
- BEGIN_BATCH(4);
+ /* From p115 of the Ivy Bridge PRM (3.2.1.4 3DSTATE_PUSH_CONSTANT_ALLOC_VS):
+ *
+ * Programming Restriction:
+ *
+ * The 3DSTATE_CONSTANT_VS must be reprogrammed prior to the next
+ * 3DPRIMITIVE command after programming the
+ * 3DSTATE_PUSH_CONSTANT_ALLOC_VS.
+ *
+ * Similar text exists for the other 3DSTATE_PUSH_CONSTANT_ALLOC_*
+ * commands.
+ */
+ brw->ctx.NewDriverState |= BRW_NEW_PUSH_CONSTANT_ALLOCATION;
+}
+
+void
+gen7_emit_push_constant_state(struct brw_context *brw, unsigned vs_size,
+ unsigned gs_size, unsigned fs_size)
+{
+ unsigned offset = 0;
+
+ BEGIN_BATCH(6);
OUT_BATCH(_3DSTATE_PUSH_CONSTANT_ALLOC_VS << 16 | (2 - 2));
- OUT_BATCH(size);
+ OUT_BATCH(vs_size | offset << GEN7_PUSH_CONSTANT_BUFFER_OFFSET_SHIFT);
+ offset += vs_size;
+
+ OUT_BATCH(_3DSTATE_PUSH_CONSTANT_ALLOC_GS << 16 | (2 - 2));
+ OUT_BATCH(gs_size | offset << GEN7_PUSH_CONSTANT_BUFFER_OFFSET_SHIFT);
+ offset += gs_size;
OUT_BATCH(_3DSTATE_PUSH_CONSTANT_ALLOC_PS << 16 | (2 - 2));
- OUT_BATCH(size | size << GEN7_PUSH_CONSTANT_BUFFER_OFFSET_SHIFT);
+ OUT_BATCH(fs_size | offset << GEN7_PUSH_CONSTANT_BUFFER_OFFSET_SHIFT);
ADVANCE_BATCH();
+
+ /* From p292 of the Ivy Bridge PRM (11.2.4 3DSTATE_PUSH_CONSTANT_ALLOC_PS):
+ *
+ * A PIPE_CONTOL command with the CS Stall bit set must be programmed
+ * in the ring after this instruction.
+ *
+ * No such restriction exists for Haswell or Baytrail.
+ */
+ if (brw->gen < 8 && !brw->is_haswell && !brw->is_baytrail)
+ gen7_emit_cs_stall_flush(brw);
}
+const struct brw_tracked_state gen7_push_constant_space = {
+ .dirty = {
+ .mesa = 0,
+ .brw = BRW_NEW_CONTEXT | BRW_NEW_GEOMETRY_PROGRAM,
+ },
+ .emit = gen7_allocate_push_constants,
+};
+
static void
gen7_upload_urb(struct brw_context *brw)
{
- const int push_size_kB = brw->is_haswell && brw->gt == 3 ? 32 : 16;
+ const int push_size_kB =
+ (brw->gen >= 8 || (brw->is_haswell && brw->gt == 3)) ? 32 : 16;
- /* CACHE_NEW_VS_PROG */
+ /* BRW_NEW_VS_PROG_DATA */
unsigned vs_size = MAX2(brw->vs.prog_data->base.urb_entry_size, 1);
unsigned vs_entry_size_bytes = vs_size * 64;
- /* BRW_NEW_GEOMETRY_PROGRAM, CACHE_NEW_GS_PROG */
+ /* BRW_NEW_GEOMETRY_PROGRAM, BRW_NEW_GS_PROG_DATA */
bool gs_present = brw->geometry_program;
unsigned gs_size = gs_present ? brw->gs.prog_data->base.urb_entry_size : 1;
unsigned gs_entry_size_bytes = gs_size * 64;
+ /* If we're just switching between programs with the same URB requirements,
+ * skip the rest of the logic.
+ */
+ if (!(brw->ctx.NewDriverState & BRW_NEW_CONTEXT) &&
+ brw->urb.vsize == vs_size &&
+ brw->urb.gs_present == gs_present &&
+ brw->urb.gsize == gs_size) {
+ return;
+ }
+ brw->urb.vsize = vs_size;
+ brw->urb.gs_present = gs_present;
+ brw->urb.gsize = gs_size;
+
/* From p35 of the Ivy Bridge PRM (section 1.7.1: 3DSTATE_URB_GS):
*
* VS Number of URB Entries must be divisible by 8 if the VS URB Entry
* additional space it could actually make use of).
*/
- /* VS always requires at least 32 URB entries */
+ /* VS has a lower limit on the number of URB entries */
unsigned vs_chunks =
- ALIGN(32 * vs_entry_size_bytes, chunk_size_bytes) / chunk_size_bytes;
+ ALIGN(brw->urb.min_vs_entries * vs_entry_size_bytes, chunk_size_bytes) /
+ chunk_size_bytes;
unsigned vs_wants =
ALIGN(brw->urb.max_vs_entries * vs_entry_size_bytes,
chunk_size_bytes) / chunk_size_bytes - vs_chunks;
remaining_space = total_wants;
if (remaining_space > 0) {
unsigned vs_additional = (unsigned)
- round(vs_wants * (((double) remaining_space) / total_wants));
+ roundf(vs_wants * (((float) remaining_space) / total_wants));
vs_chunks += vs_additional;
remaining_space -= vs_additional;
gs_chunks += remaining_space;
/* Finally, sanity check to make sure we have at least the minimum number
* of entries needed for each stage.
*/
- assert(nr_vs_entries >= 32);
+ assert(nr_vs_entries >= brw->urb.min_vs_entries);
if (gs_present)
assert(nr_gs_entries >= 2);
brw->urb.vs_start = push_constant_chunks;
brw->urb.gs_start = push_constant_chunks + vs_chunks;
- gen7_emit_vs_workaround_flush(brw);
+ if (brw->gen == 7 && !brw->is_haswell && !brw->is_baytrail)
+ gen7_emit_vs_workaround_flush(brw);
gen7_emit_urb_state(brw,
brw->urb.nr_vs_entries, vs_size, brw->urb.vs_start,
brw->urb.nr_gs_entries, gs_size, brw->urb.gs_start);
const struct brw_tracked_state gen7_urb = {
.dirty = {
.mesa = 0,
- .brw = BRW_NEW_CONTEXT | BRW_NEW_GEOMETRY_PROGRAM,
- .cache = (CACHE_NEW_VS_PROG | CACHE_NEW_GS_PROG),
+ .brw = BRW_NEW_CONTEXT |
+ BRW_NEW_GEOMETRY_PROGRAM |
+ BRW_NEW_GS_PROG_DATA |
+ BRW_NEW_VS_PROG_DATA,
},
.emit = gen7_upload_urb,
};