blob: 6831d2c4eff381b5050b86daad171f59a0ba0e89 [file] [log] [blame]
/*
* 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 <olv@lunarg.com>
*/
#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_OCCLUSION_PREDICATE:
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_OCCLUSION_PREDICATE:
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;
}