/* * Mesa 3-D graphics library * * Copyright (C) 2012-2013 LunarG, Inc. * * 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 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. * * Authors: * Chia-I Wu */ #include "util/u_prim.h" #include "core/intel_winsys.h" #include "ilo_render.h" #include "ilo_blit.h" #include "ilo_context.h" #include "ilo_cp.h" #include "ilo_query.h" #include "ilo_shader.h" #include "ilo_state.h" #include "ilo_draw.h" static void ilo_draw_set_owner(struct ilo_context *ilo) { ilo_cp_set_owner(ilo->cp, INTEL_RING_RENDER, &ilo->draw.cp_owner); } static uint64_t query_timestamp_to_ns(const struct ilo_context *ilo, uint64_t timestamp) { /* see ilo_get_timestamp() */ return (timestamp & 0xffffffff) * 80; } /** * Process the bo and accumulate the result. The bo is emptied. */ static void query_process_bo(const struct ilo_context *ilo, struct ilo_query *q) { const uint64_t *vals; uint64_t tmp; int i; if (!q->used) return; vals = intel_bo_map(q->bo, false); if (!vals) { q->used = 0; return; } switch (q->type) { case PIPE_QUERY_OCCLUSION_COUNTER: case PIPE_QUERY_TIME_ELAPSED: case PIPE_QUERY_PRIMITIVES_GENERATED: case PIPE_QUERY_PRIMITIVES_EMITTED: assert(q->stride == sizeof(*vals) * 2); tmp = 0; for (i = 0; i < q->used; i++) tmp += vals[2 * i + 1] - vals[2 * i]; if (q->type == PIPE_QUERY_TIME_ELAPSED) tmp = query_timestamp_to_ns(ilo, tmp); q->result.u64 += tmp; break; case PIPE_QUERY_TIMESTAMP: assert(q->stride == sizeof(*vals)); q->result.u64 = query_timestamp_to_ns(ilo, vals[q->used - 1]); break; case PIPE_QUERY_PIPELINE_STATISTICS: assert(q->stride == sizeof(*vals) * 22); for (i = 0; i < q->used; i++) { struct pipe_query_data_pipeline_statistics *stats = &q->result.pipeline_statistics; const uint64_t *begin = vals + 22 * i; const uint64_t *end = begin + 11; stats->ia_vertices += end[0] - begin[0]; stats->ia_primitives += end[1] - begin[1]; stats->vs_invocations += end[2] - begin[2]; stats->gs_invocations += end[3] - begin[3]; stats->gs_primitives += end[4] - begin[4]; stats->c_invocations += end[5] - begin[5]; stats->c_primitives += end[6] - begin[6]; stats->ps_invocations += end[7] - begin[7]; stats->hs_invocations += end[8] - begin[8]; stats->ds_invocations += end[9] - begin[9]; stats->cs_invocations += end[10] - begin[10]; } break; default: break; } intel_bo_unmap(q->bo); q->used = 0; } static void query_begin_bo(struct ilo_context *ilo, struct ilo_query *q) { /* bo is full */ if (q->used >= q->count) query_process_bo(ilo, q); /* write the beginning value to the bo */ if (q->in_pairs) ilo_render_emit_query(ilo->render, q, q->stride * q->used); } static void query_end_bo(struct ilo_context *ilo, struct ilo_query *q) { uint32_t offset; assert(q->used < q->count); offset = q->stride * q->used; if (q->in_pairs) offset += q->stride >> 1; q->used++; /* write the ending value to the bo */ ilo_render_emit_query(ilo->render, q, offset); } bool ilo_init_draw_query(struct ilo_context *ilo, struct ilo_query *q) { unsigned bo_size; switch (q->type) { case PIPE_QUERY_OCCLUSION_COUNTER: case PIPE_QUERY_TIME_ELAPSED: case PIPE_QUERY_PRIMITIVES_GENERATED: case PIPE_QUERY_PRIMITIVES_EMITTED: q->stride = sizeof(uint64_t); q->in_pairs = true; break; case PIPE_QUERY_TIMESTAMP: q->stride = sizeof(uint64_t); q->in_pairs = false; break; case PIPE_QUERY_PIPELINE_STATISTICS: q->stride = sizeof(uint64_t) * 11; q->in_pairs = true; break; default: return false; break; } q->cmd_len = ilo_render_get_query_len(ilo->render, q->type); /* double cmd_len and stride if in pairs */ q->cmd_len <<= q->in_pairs; q->stride <<= q->in_pairs; bo_size = (q->stride > 4096) ? q->stride : 4096; q->bo = intel_winsys_alloc_bo(ilo->winsys, "query", bo_size, false); if (!q->bo) return false; q->count = bo_size / q->stride; return true; } void ilo_begin_draw_query(struct ilo_context *ilo, struct ilo_query *q) { ilo_draw_set_owner(ilo); /* need to submit first */ if (!ilo_builder_validate(&ilo->cp->builder, 1, &q->bo) || ilo_cp_space(ilo->cp) < q->cmd_len) { ilo_cp_submit(ilo->cp, "out of aperture or space"); assert(ilo_builder_validate(&ilo->cp->builder, 1, &q->bo)); assert(ilo_cp_space(ilo->cp) >= q->cmd_len); ilo_draw_set_owner(ilo); } /* reserve the space for ending/pausing the query */ ilo->draw.cp_owner.reserve += q->cmd_len >> q->in_pairs; query_begin_bo(ilo, q); if (q->in_pairs) list_add(&q->list, &ilo->draw.queries); } void ilo_end_draw_query(struct ilo_context *ilo, struct ilo_query *q) { ilo_draw_set_owner(ilo); /* reclaim the reserved space */ ilo->draw.cp_owner.reserve -= q->cmd_len >> q->in_pairs; assert(ilo->draw.cp_owner.reserve >= 0); query_end_bo(ilo, q); list_delinit(&q->list); } /** * Process the raw query data. */ void ilo_process_draw_query(struct ilo_context *ilo, struct ilo_query *q) { query_process_bo(ilo, q); } static void ilo_draw_own_cp(struct ilo_cp *cp, void *data) { struct ilo_context *ilo = data; /* multiply by 2 for both resuming and pausing */ if (ilo_cp_space(ilo->cp) < ilo->draw.cp_owner.reserve * 2) { ilo_cp_submit(ilo->cp, "out of space"); assert(ilo_cp_space(ilo->cp) >= ilo->draw.cp_owner.reserve * 2); } while (true) { struct ilo_builder_snapshot snapshot; struct ilo_query *q; ilo_builder_batch_snapshot(&ilo->cp->builder, &snapshot); /* resume queries */ LIST_FOR_EACH_ENTRY(q, &ilo->draw.queries, list) query_begin_bo(ilo, q); if (!ilo_builder_validate(&ilo->cp->builder, 0, NULL)) { ilo_builder_batch_restore(&ilo->cp->builder, &snapshot); if (ilo_builder_batch_used(&ilo->cp->builder)) { ilo_cp_submit(ilo->cp, "out of aperture"); continue; } } break; } assert(ilo_cp_space(ilo->cp) >= ilo->draw.cp_owner.reserve); } static void ilo_draw_release_cp(struct ilo_cp *cp, void *data) { struct ilo_context *ilo = data; struct ilo_query *q; assert(ilo_cp_space(ilo->cp) >= ilo->draw.cp_owner.reserve); /* pause queries */ LIST_FOR_EACH_ENTRY(q, &ilo->draw.queries, list) query_end_bo(ilo, q); } static bool draw_vbo(struct ilo_context *ilo, const struct ilo_state_vector *vec) { bool need_flush = false; bool success = true; int max_len, before_space; /* on Gen7 and Gen7.5, we need SOL_RESET to reset the SO write offsets */ if (ilo_dev_gen(ilo->dev) >= ILO_GEN(7) && ilo_dev_gen(ilo->dev) <= ILO_GEN(7.5) && (vec->dirty & ILO_DIRTY_SO) && vec->so.enabled && !vec->so.append_bitmask) { ilo_cp_submit(ilo->cp, "SOL_RESET"); ilo_cp_set_one_off_flags(ilo->cp, INTEL_EXEC_GEN7_SOL_RESET); } if (ilo_builder_batch_used(&ilo->cp->builder)) { /* * Without a better tracking mechanism, when the framebuffer changes, we * have to assume that the old framebuffer may be sampled from. If that * happens in the middle of a batch buffer, we need to insert manual * flushes. */ need_flush = (vec->dirty & ILO_DIRTY_FB); /* same to SO target changes */ need_flush |= (vec->dirty & ILO_DIRTY_SO); } ilo_draw_set_owner(ilo); /* make sure there is enough room first */ max_len = ilo_render_get_draw_len(ilo->render, vec); if (need_flush) max_len += ilo_render_get_flush_len(ilo->render); if (max_len > ilo_cp_space(ilo->cp)) { ilo_cp_submit(ilo->cp, "out of space"); need_flush = false; assert(max_len <= ilo_cp_space(ilo->cp)); } /* space available before emission */ before_space = ilo_cp_space(ilo->cp); if (need_flush) ilo_render_emit_flush(ilo->render); while (true) { struct ilo_builder_snapshot snapshot; ilo_builder_batch_snapshot(&ilo->cp->builder, &snapshot); ilo_render_emit_draw(ilo->render, vec); if (!ilo_builder_validate(&ilo->cp->builder, 0, NULL)) { ilo_builder_batch_restore(&ilo->cp->builder, &snapshot); /* flush and try again */ if (ilo_builder_batch_used(&ilo->cp->builder)) { ilo_cp_submit(ilo->cp, "out of aperture"); continue; } success = false; } break; } /* sanity check size estimation */ assert(before_space - ilo_cp_space(ilo->cp) <= max_len); return success; } void ilo_draw_rectlist(struct ilo_context *ilo) { int max_len, before_space; bool need_flush; need_flush = ilo_builder_batch_used(&ilo->cp->builder); ilo_draw_set_owner(ilo); max_len = ilo_render_get_rectlist_len(ilo->render, ilo->blitter); max_len += ilo_render_get_flush_len(ilo->render) * 2; if (max_len > ilo_cp_space(ilo->cp)) { ilo_cp_submit(ilo->cp, "out of space"); need_flush = false; assert(max_len <= ilo_cp_space(ilo->cp)); } before_space = ilo_cp_space(ilo->cp); /* * From the Sandy Bridge PRM, volume 2 part 1, page 313: * * "If other rendering operations have preceded this clear, a * PIPE_CONTROL with write cache flush enabled and Z-inhibit * disabled must be issued before the rectangle primitive used for * the depth buffer clear operation." * * From the Sandy Bridge PRM, volume 2 part 1, page 314: * * "Depth buffer clear pass must be followed by a PIPE_CONTROL * command with DEPTH_STALL bit set and Then followed by Depth * FLUSH" * * But the pipeline has to be flushed both before and after not only * because of these workarounds. We need them for reasons such as * * - we may sample from a texture that was rendered to * - we may sample from the fb shortly after * * Skip checking blitter->op and do the flushes. */ if (need_flush) ilo_render_emit_flush(ilo->render); while (true) { struct ilo_builder_snapshot snapshot; ilo_builder_batch_snapshot(&ilo->cp->builder, &snapshot); ilo_render_emit_rectlist(ilo->render, ilo->blitter); if (!ilo_builder_validate(&ilo->cp->builder, 0, NULL)) { ilo_builder_batch_restore(&ilo->cp->builder, &snapshot); /* flush and try again */ if (ilo_builder_batch_used(&ilo->cp->builder)) { ilo_cp_submit(ilo->cp, "out of aperture"); continue; } } break; } ilo_render_invalidate_hw(ilo->render); ilo_render_emit_flush(ilo->render); /* sanity check size estimation */ assert(before_space - ilo_cp_space(ilo->cp) <= max_len); } static void draw_vbo_with_sw_restart(struct ilo_context *ilo, const struct pipe_draw_info *info) { const struct ilo_ib_state *ib = &ilo->state_vector.ib; const struct ilo_vma *vma; union { const void *ptr; const uint8_t *u8; const uint16_t *u16; const uint32_t *u32; } u; /* we will draw with IB mapped */ if (ib->state.buffer) { vma = ilo_resource_get_vma(ib->state.buffer); u.ptr = intel_bo_map(vma->bo, false); if (u.ptr) u.u8 += vma->bo_offset + ib->state.offset; } else { vma = NULL; u.ptr = ib->state.user_buffer; } if (!u.ptr) return; #define DRAW_VBO_WITH_SW_RESTART(pipe, info, ptr) do { \ const unsigned end = (info)->start + (info)->count; \ struct pipe_draw_info subinfo; \ unsigned i; \ \ subinfo = *(info); \ subinfo.primitive_restart = false; \ for (i = (info)->start; i < end; i++) { \ if ((ptr)[i] == (info)->restart_index) { \ subinfo.count = i - subinfo.start; \ if (subinfo.count) \ (pipe)->draw_vbo(pipe, &subinfo); \ subinfo.start = i + 1; \ } \ } \ subinfo.count = i - subinfo.start; \ if (subinfo.count) \ (pipe)->draw_vbo(pipe, &subinfo); \ } while (0) switch (ib->state.index_size) { case 1: DRAW_VBO_WITH_SW_RESTART(&ilo->base, info, u.u8); break; case 2: DRAW_VBO_WITH_SW_RESTART(&ilo->base, info, u.u16); break; case 4: DRAW_VBO_WITH_SW_RESTART(&ilo->base, info, u.u32); break; default: assert(!"unsupported index size"); break; } #undef DRAW_VBO_WITH_SW_RESTART if (vma) intel_bo_unmap(vma->bo); } static bool draw_vbo_need_sw_restart(const struct ilo_context *ilo, const struct pipe_draw_info *info) { /* the restart index is fixed prior to GEN7.5 */ if (ilo_dev_gen(ilo->dev) < ILO_GEN(7.5)) { const unsigned cut_index = (ilo->state_vector.ib.state.index_size == 1) ? 0xff : (ilo->state_vector.ib.state.index_size == 2) ? 0xffff : (ilo->state_vector.ib.state.index_size == 4) ? 0xffffffff : 0; if (info->restart_index < cut_index) return true; } switch (info->mode) { case PIPE_PRIM_POINTS: case PIPE_PRIM_LINES: case PIPE_PRIM_LINE_STRIP: case PIPE_PRIM_TRIANGLES: case PIPE_PRIM_TRIANGLE_STRIP: /* these never need software fallback */ return false; case PIPE_PRIM_LINE_LOOP: case PIPE_PRIM_POLYGON: case PIPE_PRIM_QUAD_STRIP: case PIPE_PRIM_QUADS: case PIPE_PRIM_TRIANGLE_FAN: /* these need software fallback prior to GEN7.5 */ return (ilo_dev_gen(ilo->dev) < ILO_GEN(7.5)); default: /* the rest always needs software fallback */ return true; } } static void ilo_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info) { struct ilo_context *ilo = ilo_context(pipe); int vs_scratch_size, gs_scratch_size, fs_scratch_size; if (ilo_debug & ILO_DEBUG_DRAW) { if (info->indexed) { ilo_printf("indexed draw %s: " "index start %d, count %d, vertex range [%d, %d]\n", u_prim_name(info->mode), info->start, info->count, info->min_index, info->max_index); } else { ilo_printf("draw %s: vertex start %d, count %d\n", u_prim_name(info->mode), info->start, info->count); } ilo_state_vector_dump_dirty(&ilo->state_vector); } if (ilo_skip_rendering(ilo)) return; if (info->primitive_restart && info->indexed && draw_vbo_need_sw_restart(ilo, info)) { draw_vbo_with_sw_restart(ilo, info); return; } ilo_finalize_3d_states(ilo, info); /* upload kernels */ ilo_shader_cache_upload(ilo->shader_cache, &ilo->cp->builder); /* prepare scratch spaces */ ilo_shader_cache_get_max_scratch_sizes(ilo->shader_cache, &vs_scratch_size, &gs_scratch_size, &fs_scratch_size); ilo_render_prepare_scratch_spaces(ilo->render, vs_scratch_size, gs_scratch_size, fs_scratch_size); ilo_blit_resolve_framebuffer(ilo); /* If draw_vbo ever fails, return immediately. */ if (!draw_vbo(ilo, &ilo->state_vector)) return; /* clear dirty status */ ilo->state_vector.dirty = 0x0; /* avoid dangling pointer reference */ ilo->state_vector.draw = NULL; if (ilo_debug & ILO_DEBUG_NOCACHE) ilo_render_emit_flush(ilo->render); } static void ilo_texture_barrier(struct pipe_context *pipe) { struct ilo_context *ilo = ilo_context(pipe); if (ilo->cp->ring != INTEL_RING_RENDER) return; ilo_render_emit_flush(ilo->render); /* don't know why */ if (ilo_dev_gen(ilo->dev) >= ILO_GEN(7)) ilo_cp_submit(ilo->cp, "texture barrier"); } static void ilo_get_sample_position(struct pipe_context *pipe, unsigned sample_count, unsigned sample_index, float *out_value) { struct ilo_context *ilo = ilo_context(pipe); ilo_render_get_sample_position(ilo->render, sample_count, sample_index, &out_value[0], &out_value[1]); } void ilo_init_draw(struct ilo_context *ilo) { ilo->draw.cp_owner.own = ilo_draw_own_cp; ilo->draw.cp_owner.release = ilo_draw_release_cp; ilo->draw.cp_owner.data = (void *) ilo; ilo->draw.cp_owner.reserve = 0; list_inithead(&ilo->draw.queries); } /** * Initialize 3D-related functions. */ void ilo_init_draw_functions(struct ilo_context *ilo) { ilo->base.draw_vbo = ilo_draw_vbo; ilo->base.texture_barrier = ilo_texture_barrier; ilo->base.get_sample_position = ilo_get_sample_position; }