| /* |
| * Copyright © 2017 Intel Corporation |
| * |
| * 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. |
| */ |
| |
| /** |
| * @file iris_draw.c |
| * |
| * The main driver hooks for drawing and launching compute shaders. |
| */ |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include "pipe/p_defines.h" |
| #include "pipe/p_state.h" |
| #include "pipe/p_context.h" |
| #include "pipe/p_screen.h" |
| #include "util/u_inlines.h" |
| #include "util/u_transfer.h" |
| #include "util/u_upload_mgr.h" |
| #include "intel/compiler/brw_compiler.h" |
| #include "intel/compiler/brw_eu_defines.h" |
| #include "iris_context.h" |
| #include "iris_defines.h" |
| |
| static bool |
| prim_is_points_or_lines(const struct pipe_draw_info *draw) |
| { |
| /* We don't need to worry about adjacency - it can only be used with |
| * geometry shaders, and we don't care about this info when GS is on. |
| */ |
| return draw->mode == PIPE_PRIM_POINTS || |
| draw->mode == PIPE_PRIM_LINES || |
| draw->mode == PIPE_PRIM_LINE_LOOP || |
| draw->mode == PIPE_PRIM_LINE_STRIP; |
| } |
| |
| /** |
| * Record the current primitive mode and restart information, flagging |
| * related packets as dirty if necessary. |
| * |
| * This must be called before updating compiled shaders, because the patch |
| * information informs the TCS key. |
| */ |
| static void |
| iris_update_draw_info(struct iris_context *ice, |
| const struct pipe_draw_info *info) |
| { |
| struct iris_screen *screen = (struct iris_screen *)ice->ctx.screen; |
| const struct brw_compiler *compiler = screen->compiler; |
| |
| if (ice->state.prim_mode != info->mode) { |
| ice->state.prim_mode = info->mode; |
| ice->state.dirty |= IRIS_DIRTY_VF_TOPOLOGY; |
| |
| |
| /* For XY Clip enables */ |
| bool points_or_lines = prim_is_points_or_lines(info); |
| if (points_or_lines != ice->state.prim_is_points_or_lines) { |
| ice->state.prim_is_points_or_lines = points_or_lines; |
| ice->state.dirty |= IRIS_DIRTY_CLIP; |
| } |
| } |
| |
| if (info->mode == PIPE_PRIM_PATCHES && |
| ice->state.vertices_per_patch != info->vertices_per_patch) { |
| ice->state.vertices_per_patch = info->vertices_per_patch; |
| ice->state.dirty |= IRIS_DIRTY_VF_TOPOLOGY; |
| |
| /* 8_PATCH TCS needs this for key->input_vertices */ |
| if (compiler->use_tcs_8_patch) |
| ice->state.stage_dirty |= IRIS_STAGE_DIRTY_UNCOMPILED_TCS; |
| |
| /* Flag constants dirty for gl_PatchVerticesIn if needed. */ |
| const struct shader_info *tcs_info = |
| iris_get_shader_info(ice, MESA_SHADER_TESS_CTRL); |
| if (tcs_info && |
| tcs_info->system_values_read & (1ull << SYSTEM_VALUE_VERTICES_IN)) { |
| ice->state.stage_dirty |= IRIS_STAGE_DIRTY_CONSTANTS_TCS; |
| ice->state.shaders[MESA_SHADER_TESS_CTRL].sysvals_need_upload = true; |
| } |
| } |
| |
| if (ice->state.primitive_restart != info->primitive_restart || |
| ice->state.cut_index != info->restart_index) { |
| ice->state.dirty |= IRIS_DIRTY_VF; |
| ice->state.primitive_restart = info->primitive_restart; |
| ice->state.cut_index = info->restart_index; |
| } |
| } |
| |
| /** |
| * Update shader draw parameters, flagging VF packets as dirty if necessary. |
| */ |
| static void |
| iris_update_draw_parameters(struct iris_context *ice, |
| const struct pipe_draw_info *info) |
| { |
| bool changed = false; |
| |
| if (ice->state.vs_uses_draw_params) { |
| struct iris_state_ref *draw_params = &ice->draw.draw_params; |
| |
| if (info->indirect) { |
| pipe_resource_reference(&draw_params->res, info->indirect->buffer); |
| draw_params->offset = |
| info->indirect->offset + (info->index_size ? 12 : 8); |
| |
| changed = true; |
| ice->draw.params_valid = false; |
| } else { |
| int firstvertex = info->index_size ? info->index_bias : info->start; |
| |
| if (!ice->draw.params_valid || |
| ice->draw.params.firstvertex != firstvertex || |
| ice->draw.params.baseinstance != info->start_instance) { |
| |
| changed = true; |
| ice->draw.params.firstvertex = firstvertex; |
| ice->draw.params.baseinstance = info->start_instance; |
| ice->draw.params_valid = true; |
| |
| u_upload_data(ice->ctx.stream_uploader, 0, |
| sizeof(ice->draw.params), 4, &ice->draw.params, |
| &draw_params->offset, &draw_params->res); |
| } |
| } |
| } |
| |
| if (ice->state.vs_uses_derived_draw_params) { |
| struct iris_state_ref *derived_params = &ice->draw.derived_draw_params; |
| int is_indexed_draw = info->index_size ? -1 : 0; |
| |
| if (ice->draw.derived_params.drawid != info->drawid || |
| ice->draw.derived_params.is_indexed_draw != is_indexed_draw) { |
| |
| changed = true; |
| ice->draw.derived_params.drawid = info->drawid; |
| ice->draw.derived_params.is_indexed_draw = is_indexed_draw; |
| |
| u_upload_data(ice->ctx.stream_uploader, 0, |
| sizeof(ice->draw.derived_params), 4, |
| &ice->draw.derived_params, |
| &derived_params->offset, &derived_params->res); |
| } |
| } |
| |
| if (changed) { |
| ice->state.dirty |= IRIS_DIRTY_VERTEX_BUFFERS | |
| IRIS_DIRTY_VERTEX_ELEMENTS | |
| IRIS_DIRTY_VF_SGVS; |
| } |
| } |
| |
| static void |
| iris_indirect_draw_vbo(struct iris_context *ice, |
| const struct pipe_draw_info *dinfo) |
| { |
| struct iris_batch *batch = &ice->batches[IRIS_BATCH_RENDER]; |
| struct pipe_draw_info info = *dinfo; |
| |
| if (info.indirect->indirect_draw_count && |
| ice->state.predicate == IRIS_PREDICATE_STATE_USE_BIT) { |
| /* Upload MI_PREDICATE_RESULT to GPR15.*/ |
| batch->screen->vtbl.load_register_reg64(batch, CS_GPR(15), MI_PREDICATE_RESULT); |
| } |
| |
| const uint64_t orig_dirty = ice->state.dirty; |
| const uint64_t orig_stage_dirty = ice->state.stage_dirty; |
| |
| for (int i = 0; i < info.indirect->draw_count; i++) { |
| info.drawid = i; |
| |
| iris_batch_maybe_flush(batch, 1500); |
| |
| iris_update_draw_parameters(ice, &info); |
| |
| batch->screen->vtbl.upload_render_state(ice, batch, &info); |
| |
| ice->state.dirty &= ~IRIS_ALL_DIRTY_FOR_RENDER; |
| ice->state.stage_dirty &= ~IRIS_ALL_STAGE_DIRTY_FOR_RENDER; |
| |
| info.indirect->offset += info.indirect->stride; |
| } |
| |
| if (info.indirect->indirect_draw_count && |
| ice->state.predicate == IRIS_PREDICATE_STATE_USE_BIT) { |
| /* Restore MI_PREDICATE_RESULT. */ |
| batch->screen->vtbl.load_register_reg64(batch, MI_PREDICATE_RESULT, CS_GPR(15)); |
| } |
| |
| /* Put this back for post-draw resolves, we'll clear it again after. */ |
| ice->state.dirty = orig_dirty; |
| ice->state.stage_dirty = orig_stage_dirty; |
| } |
| |
| static void |
| iris_simple_draw_vbo(struct iris_context *ice, |
| const struct pipe_draw_info *draw) |
| { |
| struct iris_batch *batch = &ice->batches[IRIS_BATCH_RENDER]; |
| |
| iris_batch_maybe_flush(batch, 1500); |
| |
| iris_update_draw_parameters(ice, draw); |
| |
| batch->screen->vtbl.upload_render_state(ice, batch, draw); |
| } |
| |
| /** |
| * The pipe->draw_vbo() driver hook. Performs a draw on the GPU. |
| */ |
| void |
| iris_draw_vbo(struct pipe_context *ctx, const struct pipe_draw_info *info) |
| { |
| struct iris_context *ice = (struct iris_context *) ctx; |
| struct iris_screen *screen = (struct iris_screen*)ice->ctx.screen; |
| const struct gen_device_info *devinfo = &screen->devinfo; |
| struct iris_batch *batch = &ice->batches[IRIS_BATCH_RENDER]; |
| |
| if (ice->state.predicate == IRIS_PREDICATE_STATE_DONT_RENDER) |
| return; |
| |
| /* We can't safely re-emit 3DSTATE_SO_BUFFERS because it may zero the |
| * write offsets, changing the behavior. |
| */ |
| if (INTEL_DEBUG & DEBUG_REEMIT) { |
| ice->state.dirty |= IRIS_ALL_DIRTY_FOR_RENDER & ~IRIS_DIRTY_SO_BUFFERS; |
| ice->state.stage_dirty |= IRIS_ALL_STAGE_DIRTY_FOR_RENDER; |
| } |
| |
| iris_update_draw_info(ice, info); |
| |
| if (devinfo->gen == 9) |
| gen9_toggle_preemption(ice, batch, info); |
| |
| iris_update_compiled_shaders(ice); |
| |
| if (ice->state.dirty & IRIS_DIRTY_RENDER_RESOLVES_AND_FLUSHES) { |
| bool draw_aux_buffer_disabled[BRW_MAX_DRAW_BUFFERS] = { }; |
| for (gl_shader_stage stage = 0; stage < MESA_SHADER_COMPUTE; stage++) { |
| if (ice->shaders.prog[stage]) |
| iris_predraw_resolve_inputs(ice, batch, draw_aux_buffer_disabled, |
| stage, true); |
| } |
| iris_predraw_resolve_framebuffer(ice, batch, draw_aux_buffer_disabled); |
| } |
| |
| iris_binder_reserve_3d(ice); |
| |
| batch->screen->vtbl.update_surface_base_address(batch, &ice->state.binder); |
| |
| iris_handle_always_flush_cache(batch); |
| |
| if (info->indirect) |
| iris_indirect_draw_vbo(ice, info); |
| else |
| iris_simple_draw_vbo(ice, info); |
| |
| iris_handle_always_flush_cache(batch); |
| |
| iris_postdraw_update_resolve_tracking(ice, batch); |
| |
| ice->state.dirty &= ~IRIS_ALL_DIRTY_FOR_RENDER; |
| ice->state.stage_dirty &= ~IRIS_ALL_STAGE_DIRTY_FOR_RENDER; |
| } |
| |
| static void |
| iris_update_grid_size_resource(struct iris_context *ice, |
| const struct pipe_grid_info *grid) |
| { |
| const struct iris_screen *screen = (void *) ice->ctx.screen; |
| const struct isl_device *isl_dev = &screen->isl_dev; |
| struct iris_state_ref *grid_ref = &ice->state.grid_size; |
| struct iris_state_ref *state_ref = &ice->state.grid_surf_state; |
| |
| const struct iris_compiled_shader *shader = ice->shaders.prog[MESA_SHADER_COMPUTE]; |
| bool grid_needs_surface = shader->bt.used_mask[IRIS_SURFACE_GROUP_CS_WORK_GROUPS]; |
| bool grid_updated = false; |
| |
| if (grid->indirect) { |
| pipe_resource_reference(&grid_ref->res, grid->indirect); |
| grid_ref->offset = grid->indirect_offset; |
| |
| /* Zero out the grid size so that the next non-indirect grid launch will |
| * re-upload it properly. |
| */ |
| memset(ice->state.last_grid, 0, sizeof(ice->state.last_grid)); |
| grid_updated = true; |
| } else if (memcmp(ice->state.last_grid, grid->grid, sizeof(grid->grid)) != 0) { |
| memcpy(ice->state.last_grid, grid->grid, sizeof(grid->grid)); |
| u_upload_data(ice->state.dynamic_uploader, 0, sizeof(grid->grid), 4, |
| grid->grid, &grid_ref->offset, &grid_ref->res); |
| grid_updated = true; |
| } |
| |
| /* If we changed the grid, the old surface state is invalid. */ |
| if (grid_updated) |
| pipe_resource_reference(&state_ref->res, NULL); |
| |
| /* Skip surface upload if we don't need it or we already have one */ |
| if (!grid_needs_surface || state_ref->res) |
| return; |
| |
| struct iris_bo *grid_bo = iris_resource_bo(grid_ref->res); |
| |
| void *surf_map = NULL; |
| u_upload_alloc(ice->state.surface_uploader, 0, isl_dev->ss.size, |
| isl_dev->ss.align, &state_ref->offset, &state_ref->res, |
| &surf_map); |
| state_ref->offset += |
| iris_bo_offset_from_base_address(iris_resource_bo(state_ref->res)); |
| isl_buffer_fill_state(&screen->isl_dev, surf_map, |
| .address = grid_ref->offset + grid_bo->gtt_offset, |
| .size_B = sizeof(grid->grid), |
| .format = ISL_FORMAT_RAW, |
| .stride_B = 1, |
| .mocs = iris_mocs(grid_bo, isl_dev, |
| ISL_SURF_USAGE_CONSTANT_BUFFER_BIT)); |
| |
| ice->state.stage_dirty |= IRIS_STAGE_DIRTY_BINDINGS_CS; |
| } |
| |
| void |
| iris_launch_grid(struct pipe_context *ctx, const struct pipe_grid_info *grid) |
| { |
| struct iris_context *ice = (struct iris_context *) ctx; |
| struct iris_batch *batch = &ice->batches[IRIS_BATCH_COMPUTE]; |
| |
| if (ice->state.predicate == IRIS_PREDICATE_STATE_DONT_RENDER) |
| return; |
| |
| if (INTEL_DEBUG & DEBUG_REEMIT) { |
| ice->state.dirty |= IRIS_ALL_DIRTY_FOR_COMPUTE; |
| ice->state.stage_dirty |= IRIS_ALL_STAGE_DIRTY_FOR_COMPUTE; |
| } |
| |
| if (ice->state.dirty & IRIS_DIRTY_COMPUTE_RESOLVES_AND_FLUSHES) |
| iris_predraw_resolve_inputs(ice, batch, NULL, MESA_SHADER_COMPUTE, false); |
| |
| iris_batch_maybe_flush(batch, 1500); |
| |
| iris_update_compiled_compute_shader(ice); |
| |
| if (memcmp(ice->state.last_block, grid->block, sizeof(grid->block)) != 0) { |
| memcpy(ice->state.last_block, grid->block, sizeof(grid->block)); |
| ice->state.stage_dirty |= IRIS_STAGE_DIRTY_CONSTANTS_CS; |
| ice->state.shaders[MESA_SHADER_COMPUTE].sysvals_need_upload = true; |
| } |
| |
| iris_update_grid_size_resource(ice, grid); |
| |
| iris_binder_reserve_compute(ice); |
| batch->screen->vtbl.update_surface_base_address(batch, &ice->state.binder); |
| |
| if (ice->state.compute_predicate) { |
| batch->screen->vtbl.load_register_mem64(batch, MI_PREDICATE_RESULT, |
| ice->state.compute_predicate, 0); |
| ice->state.compute_predicate = NULL; |
| } |
| |
| iris_handle_always_flush_cache(batch); |
| |
| batch->screen->vtbl.upload_compute_state(ice, batch, grid); |
| |
| iris_handle_always_flush_cache(batch); |
| |
| ice->state.dirty &= ~IRIS_ALL_DIRTY_FOR_COMPUTE; |
| ice->state.stage_dirty &= ~IRIS_ALL_STAGE_DIRTY_FOR_COMPUTE; |
| |
| /* Note: since compute shaders can't access the framebuffer, there's |
| * no need to call iris_postdraw_update_resolve_tracking. |
| */ |
| } |