#include "intel_buffer_objects.h"
#include "main/transformfeedback.h"
-static void
-upload_3dstate_so_buffers(struct brw_context *brw)
+void
+gen7_begin_transform_feedback(struct gl_context *ctx, GLenum mode,
+ struct gl_transform_feedback_object *obj)
{
- struct intel_context *intel = &brw->intel;
- struct gl_context *ctx = &intel->ctx;
- /* BRW_NEW_VERTEX_PROGRAM */
- const struct gl_shader_program *vs_prog =
- ctx->Shader.CurrentVertexProgram;
- const struct gl_transform_feedback_info *linked_xfb_info =
- &vs_prog->LinkedTransformFeedback;
- /* _NEW_TRANSFORM_FEEDBACK */
- struct gl_transform_feedback_object *xfb_obj =
- ctx->TransformFeedback.CurrentObject;
- int i;
-
- /* Set up the up to 4 output buffers. These are the ranges defined in the
- * gl_transform_feedback_object.
- */
- for (i = 0; i < 4; i++) {
- struct intel_buffer_object *bufferobj =
- intel_buffer_object(xfb_obj->Buffers[i]);
- drm_intel_bo *bo;
- uint32_t start, end;
- uint32_t stride;
-
- if (!xfb_obj->Buffers[i]) {
- /* The pitch of 0 in this command indicates that the buffer is
- * unbound and won't be written to.
- */
- BEGIN_BATCH(4);
- OUT_BATCH(_3DSTATE_SO_BUFFER << 16 | (4 - 2));
- OUT_BATCH((i << SO_BUFFER_INDEX_SHIFT));
- OUT_BATCH(0);
- OUT_BATCH(0);
- ADVANCE_BATCH();
-
- continue;
- }
+ struct brw_context *brw = brw_context(ctx);
+ struct brw_transform_feedback_object *brw_obj =
+ (struct brw_transform_feedback_object *) obj;
- bo = intel_bufferobj_buffer(intel, bufferobj, INTEL_WRITE_PART);
- stride = linked_xfb_info->BufferStride[i] * 4;
+ assert(brw->screen->devinfo.gen == 7);
- start = xfb_obj->Offset[i];
- assert(start % 4 == 0);
- end = ALIGN(start + xfb_obj->Size[i], 4);
- assert(end <= bo->size);
+ /* Store the starting value of the SO_NUM_PRIMS_WRITTEN counters. */
+ brw_save_primitives_written_counters(brw, brw_obj);
- /* If we don't have hardware contexts, then we reset our offsets at the
- * start of every batch, so we track the number of vertices written in
- * software and increment our pointers by that many.
- */
- if (!intel->hw_ctx) {
- start += brw->sol.offset_0_batch_start * stride;
- assert(start <= end);
+ /* Reset the SO buffer offsets to 0. */
+ if (!can_do_pipelined_register_writes(brw->screen)) {
+ intel_batchbuffer_flush(brw);
+ brw->batch.needs_sol_reset = true;
+ } else {
+ for (int i = 0; i < 4; i++) {
+ brw_load_register_imm32(brw, GEN7_SO_WRITE_OFFSET(i), 0);
}
-
- BEGIN_BATCH(4);
- OUT_BATCH(_3DSTATE_SO_BUFFER << 16 | (4 - 2));
- OUT_BATCH((i << SO_BUFFER_INDEX_SHIFT) | stride);
- OUT_RELOC(bo, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER, start);
- OUT_RELOC(bo, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER, end);
- ADVANCE_BATCH();
}
+
+ brw_obj->primitive_mode = mode;
}
-/**
- * Outputs the 3DSTATE_SO_DECL_LIST command.
- *
- * The data output is a series of 64-bit entries containing a SO_DECL per
- * stream. We only have one stream of rendering coming out of the GS unit, so
- * we only emit stream 0 (low 16 bits) SO_DECLs.
- */
-static void
-upload_3dstate_so_decl_list(struct brw_context *brw,
- const struct brw_vue_map *vue_map)
+void
+gen7_end_transform_feedback(struct gl_context *ctx,
+ struct gl_transform_feedback_object *obj)
{
- struct intel_context *intel = &brw->intel;
- struct gl_context *ctx = &intel->ctx;
- /* BRW_NEW_VERTEX_PROGRAM */
- const struct gl_shader_program *vs_prog =
- ctx->Shader.CurrentVertexProgram;
- /* _NEW_TRANSFORM_FEEDBACK */
- const struct gl_transform_feedback_info *linked_xfb_info =
- &vs_prog->LinkedTransformFeedback;
- int i;
- uint16_t so_decl[128];
- int buffer_mask = 0;
- int next_offset[4] = {0, 0, 0, 0};
-
- STATIC_ASSERT(ARRAY_SIZE(so_decl) >= MAX_PROGRAM_OUTPUTS);
-
- /* Construct the list of SO_DECLs to be emitted. The formatting of the
- * command is feels strange -- each dword pair contains a SO_DECL per stream.
+ /* After EndTransformFeedback, it's likely that the client program will try
+ * to draw using the contents of the transform feedback buffer as vertex
+ * input. In order for this to work, we need to flush the data through at
+ * least the GS stage of the pipeline, and flush out the render cache. For
+ * simplicity, just do a full flush.
*/
- for (i = 0; i < linked_xfb_info->NumOutputs; i++) {
- int buffer = linked_xfb_info->Outputs[i].OutputBuffer;
- uint16_t decl = 0;
- int varying = linked_xfb_info->Outputs[i].OutputRegister;
- unsigned component_mask =
- (1 << linked_xfb_info->Outputs[i].NumComponents) - 1;
-
- /* gl_PointSize is stored in VARYING_SLOT_PSIZ.w. */
- if (varying == VARYING_SLOT_PSIZ) {
- assert(linked_xfb_info->Outputs[i].NumComponents == 1);
- component_mask <<= 3;
- } else {
- component_mask <<= linked_xfb_info->Outputs[i].ComponentOffset;
- }
-
- buffer_mask |= 1 << buffer;
-
- decl |= buffer << SO_DECL_OUTPUT_BUFFER_SLOT_SHIFT;
- decl |= vue_map->varying_to_slot[varying] <<
- SO_DECL_REGISTER_INDEX_SHIFT;
- decl |= component_mask << SO_DECL_COMPONENT_MASK_SHIFT;
-
- /* This assert should be true until GL_ARB_transform_feedback_instanced
- * is added and we start using the hole flag.
- */
- assert(linked_xfb_info->Outputs[i].DstOffset == next_offset[buffer]);
-
- next_offset[buffer] += linked_xfb_info->Outputs[i].NumComponents;
-
- so_decl[i] = decl;
- }
-
- BEGIN_BATCH(linked_xfb_info->NumOutputs * 2 + 3);
- OUT_BATCH(_3DSTATE_SO_DECL_LIST << 16 |
- (linked_xfb_info->NumOutputs * 2 + 1));
-
- OUT_BATCH((buffer_mask << SO_STREAM_TO_BUFFER_SELECTS_0_SHIFT) |
- (0 << SO_STREAM_TO_BUFFER_SELECTS_1_SHIFT) |
- (0 << SO_STREAM_TO_BUFFER_SELECTS_2_SHIFT) |
- (0 << SO_STREAM_TO_BUFFER_SELECTS_3_SHIFT));
+ struct brw_context *brw = brw_context(ctx);
+ struct brw_transform_feedback_object *brw_obj =
+ (struct brw_transform_feedback_object *) obj;
- OUT_BATCH((linked_xfb_info->NumOutputs << SO_NUM_ENTRIES_0_SHIFT) |
- (0 << SO_NUM_ENTRIES_1_SHIFT) |
- (0 << SO_NUM_ENTRIES_2_SHIFT) |
- (0 << SO_NUM_ENTRIES_3_SHIFT));
+ /* Store the ending value of the SO_NUM_PRIMS_WRITTEN counters. */
+ if (!obj->Paused)
+ brw_save_primitives_written_counters(brw, brw_obj);
- for (i = 0; i < linked_xfb_info->NumOutputs; i++) {
- OUT_BATCH(so_decl[i]);
- OUT_BATCH(0);
- }
+ /* We've reached the end of a transform feedback begin/end block. This
+ * means that future DrawTransformFeedback() calls will need to pick up the
+ * results of the current counter, and that it's time to roll back the
+ * current primitive counter to zero.
+ */
+ brw_obj->previous_counter = brw_obj->counter;
+ brw_reset_transform_feedback_counter(&brw_obj->counter);
- ADVANCE_BATCH();
+ /* EndTransformFeedback() means that we need to update the number of
+ * vertices written. Since it's only necessary if DrawTransformFeedback()
+ * is called and it means mapping a buffer object, we delay computing it
+ * until it's absolutely necessary to try and avoid stalls.
+ */
+ brw_obj->vertices_written_valid = false;
}
-static void
-upload_3dstate_streamout(struct brw_context *brw, bool active,
- const struct brw_vue_map *vue_map)
+void
+gen7_pause_transform_feedback(struct gl_context *ctx,
+ struct gl_transform_feedback_object *obj)
{
- struct intel_context *intel = &brw->intel;
- struct gl_context *ctx = &intel->ctx;
- /* _NEW_TRANSFORM_FEEDBACK */
- struct gl_transform_feedback_object *xfb_obj =
- ctx->TransformFeedback.CurrentObject;
- uint32_t dw1 = 0, dw2 = 0;
- int i;
-
- /* _NEW_RASTERIZER_DISCARD */
- if (ctx->RasterDiscard)
- dw1 |= SO_RENDERING_DISABLE;
-
- if (active) {
- int urb_entry_read_offset = 0;
- int urb_entry_read_length = (vue_map->num_slots + 1) / 2 -
- urb_entry_read_offset;
-
- dw1 |= SO_FUNCTION_ENABLE;
- dw1 |= SO_STATISTICS_ENABLE;
-
- /* _NEW_LIGHT */
- if (ctx->Light.ProvokingVertex != GL_FIRST_VERTEX_CONVENTION)
- dw1 |= SO_REORDER_TRAILING;
-
- for (i = 0; i < 4; i++) {
- if (xfb_obj->Buffers[i]) {
- dw1 |= SO_BUFFER_ENABLE(i);
- }
- }
-
- /* We always read the whole vertex. This could be reduced at some
- * point by reading less and offsetting the register index in the
- * SO_DECLs.
- */
- dw2 |= urb_entry_read_offset << SO_STREAM_0_VERTEX_READ_OFFSET_SHIFT;
- dw2 |= (urb_entry_read_length - 1) <<
- SO_STREAM_0_VERTEX_READ_LENGTH_SHIFT;
- }
-
- BEGIN_BATCH(3);
- OUT_BATCH(_3DSTATE_STREAMOUT << 16 | (3 - 2));
- OUT_BATCH(dw1);
- OUT_BATCH(dw2);
- ADVANCE_BATCH();
-}
+ struct brw_context *brw = brw_context(ctx);
+ struct brw_transform_feedback_object *brw_obj =
+ (struct brw_transform_feedback_object *) obj;
-static void
-upload_sol_state(struct brw_context *brw)
-{
- struct intel_context *intel = &brw->intel;
- struct gl_context *ctx = &intel->ctx;
- /* _NEW_TRANSFORM_FEEDBACK */
- bool active = _mesa_is_xfb_active_and_unpaused(ctx);
+ /* Flush any drawing so that the counters have the right values. */
+ brw_emit_mi_flush(brw);
- if (active) {
- upload_3dstate_so_buffers(brw);
- /* BRW_NEW_VUE_MAP_GEOM_OUT */
- upload_3dstate_so_decl_list(brw, &brw->vue_map_geom_out);
+ assert(brw->screen->devinfo.gen == 7);
- /* If we don't have hardware contexts, then some other client may have
- * changed the SO write offsets, and we need to rewrite them.
- */
- if (!intel->hw_ctx)
- intel->batch.needs_sol_reset = true;
+ /* Save the SOL buffer offset register values. */
+ for (int i = 0; i < 4; i++) {
+ BEGIN_BATCH(3);
+ OUT_BATCH(MI_STORE_REGISTER_MEM | (3 - 2));
+ OUT_BATCH(GEN7_SO_WRITE_OFFSET(i));
+ OUT_RELOC(brw_obj->offset_bo, RELOC_WRITE, i * sizeof(uint32_t));
+ ADVANCE_BATCH();
}
- /* Finally, set up the SOL stage. This command must always follow updates to
- * the nonpipelined SOL state (3DSTATE_SO_BUFFER, 3DSTATE_SO_DECL_LIST) or
- * MMIO register updates (current performed by the kernel at each batch
- * emit).
+ /* Store the temporary ending value of the SO_NUM_PRIMS_WRITTEN counters.
+ * While this operation is paused, other transform feedback actions may
+ * occur, which will contribute to the counters. We need to exclude that
+ * from our counts.
*/
- upload_3dstate_streamout(brw, active, &brw->vue_map_geom_out);
+ brw_save_primitives_written_counters(brw, brw_obj);
}
-const struct brw_tracked_state gen7_sol_state = {
- .dirty = {
- .mesa = (_NEW_RASTERIZER_DISCARD |
- _NEW_LIGHT |
- _NEW_TRANSFORM_FEEDBACK),
- .brw = (BRW_NEW_BATCH |
- BRW_NEW_VERTEX_PROGRAM |
- BRW_NEW_VUE_MAP_GEOM_OUT)
- },
- .emit = upload_sol_state,
-};
-
void
-gen7_end_transform_feedback(struct gl_context *ctx,
- struct gl_transform_feedback_object *obj)
+gen7_resume_transform_feedback(struct gl_context *ctx,
+ struct gl_transform_feedback_object *obj)
{
- /* Because we have to rely on the kernel to reset our SO write offsets, and
- * we only get to do it once per batchbuffer, flush the batch after feedback
- * so another transform feedback can get the write offset reset it needs.
- *
- * This also covers any cache flushing required.
- */
struct brw_context *brw = brw_context(ctx);
- struct intel_context *intel = &brw->intel;
+ struct brw_transform_feedback_object *brw_obj =
+ (struct brw_transform_feedback_object *) obj;
+
+ assert(brw->screen->devinfo.gen == 7);
+
+ /* Reload the SOL buffer offset registers. */
+ for (int i = 0; i < 4; i++) {
+ BEGIN_BATCH(3);
+ OUT_BATCH(GEN7_MI_LOAD_REGISTER_MEM | (3 - 2));
+ OUT_BATCH(GEN7_SO_WRITE_OFFSET(i));
+ OUT_RELOC(brw_obj->offset_bo, RELOC_WRITE, i * sizeof(uint32_t));
+ ADVANCE_BATCH();
+ }
- intel_batchbuffer_flush(intel);
+ /* Store the new starting value of the SO_NUM_PRIMS_WRITTEN counters. */
+ brw_save_primitives_written_counters(brw, brw_obj);
}