From 5a653d8dd40ac70a2dfb26a838db196b7b24f67e Mon Sep 17 00:00:00 2001 From: Connor Abbott Date: Tue, 28 Jan 2025 14:37:32 -0500 Subject: [PATCH] tu: Split out viewport faking from per-view viewports For VK_VALVE_fragment_density_map_layered, we need to split out whether to enable per-view viewports and whether viewport 0 should be splatted to all viewports. We also have to split this out for VK_QCOM_multiview_per_view_viewport, where the splatting needs to be disabled. To avoid conflicts between them, plumb through "fake_single_viewport" which will be needed for the former extension and needs to be modified for the latter extension. Part-of: --- src/freedreno/vulkan/tu_cmd_buffer.cc | 5 ++++- src/freedreno/vulkan/tu_cmd_buffer.h | 10 ++++++++++ src/freedreno/vulkan/tu_pipeline.cc | 17 +++++++++++------ src/freedreno/vulkan/tu_pipeline.h | 11 +++++++++++ 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/freedreno/vulkan/tu_cmd_buffer.cc b/src/freedreno/vulkan/tu_cmd_buffer.cc index 6fc400a8d3a..8d0d3b4fc93 100644 --- a/src/freedreno/vulkan/tu_cmd_buffer.cc +++ b/src/freedreno/vulkan/tu_cmd_buffer.cc @@ -4491,8 +4491,11 @@ tu_CmdBindPipeline(VkCommandBuffer commandBuffer, pipeline->shaders[MESA_SHADER_FRAGMENT]->fs.has_fdm; } - if (pipeline->program.per_view_viewport != cmd->state.per_view_viewport) { + if (pipeline->program.per_view_viewport != cmd->state.per_view_viewport || + pipeline->program.fake_single_viewport != cmd->state.fake_single_viewport) { cmd->state.per_view_viewport = pipeline->program.per_view_viewport; + cmd->state.fake_single_viewport = + pipeline->program.fake_single_viewport; cmd->state.dirty |= TU_CMD_DIRTY_PER_VIEW_VIEWPORT; } diff --git a/src/freedreno/vulkan/tu_cmd_buffer.h b/src/freedreno/vulkan/tu_cmd_buffer.h index 89fe0fee00d..a2570255ab2 100644 --- a/src/freedreno/vulkan/tu_cmd_buffer.h +++ b/src/freedreno/vulkan/tu_cmd_buffer.h @@ -289,6 +289,13 @@ struct tu_render_pass_state bool has_zpass_done_sample_count_write_in_rp; bool disable_gmem; bool sysmem_single_prim_mode; + + /* This is set if, at any point in the render pass, we were not able to + * duplicate the viewport per-view due to the user using multiple viewports + * and instead we used the state from view 0 to transform each viewport. If + * this happens at any point then all views must contain the same FDM + * fragment size. + */ bool shared_viewport; /* Track whether conditional predicate for COND_REG_EXEC is changed in draw_cs */ @@ -447,7 +454,10 @@ struct tu_cmd_state uint32_t max_vbs_bound; bool has_fdm; + /* See tu_pipeline::per_view_viewport */ bool per_view_viewport; + /* See tu_pipeline::fake_single_viewport */ + bool fake_single_viewport; /* saved states to re-emit in TU_CMD_DIRTY_DRAW_STATE case */ struct tu_draw_state dynamic_state[TU_DYNAMIC_STATE_COUNT]; diff --git a/src/freedreno/vulkan/tu_pipeline.cc b/src/freedreno/vulkan/tu_pipeline.cc index 4e9115184db..80c29d6119d 100644 --- a/src/freedreno/vulkan/tu_pipeline.cc +++ b/src/freedreno/vulkan/tu_pipeline.cc @@ -2321,6 +2321,7 @@ tu_emit_program_state(struct tu_cs *sub_cs, !last_shader->writes_viewport && shaders[MESA_SHADER_FRAGMENT]->fs.has_fdm && dev->physical_device->info->a6xx.has_per_view_viewport; + prog->fake_single_viewport = prog->per_view_viewport; prog->writes_shading_rate = last_shader->writes_shading_rate; prog->reads_shading_rate = fs->reads_shading_rate; prog->accesses_smask = fs->reads_smask || fs->writes_smask; @@ -2552,7 +2553,10 @@ tu6_emit_viewport(struct tu_cs *cs, struct apply_viewport_state { struct vk_viewport_state vp; struct vk_rasterization_state rs; + /* See tu_render_pass_state::shared_viewport */ bool share_scale; + /* See tu_pipeline::fake_single_viewport */ + bool fake_single_viewport; }; /* It's a hardware restriction that the window offset (i.e. common_bin_offset) @@ -2609,15 +2613,14 @@ fdm_apply_viewports(struct tu_cmd_buffer *cmd, struct tu_cs *cs, void *data, * same across all views, we can pick any view. However the number * of viewports and number of views is not guaranteed the same, so we * need to pick the 0'th view which always exists to be safe. - * - * Conversly, if we're not using shared scaling then the rasterizer in - * the original pipeline is using only the first viewport, so we need to - * replicate it across all viewports. */ VkExtent2D frag_area = state->share_scale ? frag_areas[0] : frag_areas[i]; VkRect2D bin = state->share_scale ? bins[0] : bins[i]; + /* Implement fake_single_viewport by replicating viewport 0 across all + * views. + */ VkViewport viewport = - state->share_scale ? state->vp.viewports[i] : state->vp.viewports[0]; + state->fake_single_viewport ? state->vp.viewports[0] : state->vp.viewports[i]; if (frag_area.width == 1 && frag_area.height == 1 && common_bin_offset.x == bin.offset.x && common_bin_offset.y == bin.offset.y) { @@ -2653,6 +2656,7 @@ tu6_emit_viewport_fdm(struct tu_cs *cs, struct tu_cmd_buffer *cmd, .vp = *vp, .rs = *rs, .share_scale = !cmd->state.per_view_viewport, + .fake_single_viewport = cmd->state.fake_single_viewport, }; if (!state.share_scale) state.vp.viewport_count = num_views; @@ -2722,7 +2726,7 @@ fdm_apply_scissors(struct tu_cmd_buffer *cmd, struct tu_cs *cs, void *data, VkExtent2D frag_area = state->share_scale ? frag_areas[0] : frag_areas[i]; VkRect2D bin = state->share_scale ? bins[0] : bins[i]; VkRect2D scissor = - state->share_scale ? state->vp.scissors[i] : state->vp.scissors[0]; + state->fake_single_viewport ? state->vp.scissors[0] : state->vp.scissors[i]; /* Transform the scissor following the viewport. It's unclear how this * is supposed to handle cases where the scissor isn't aligned to the @@ -2765,6 +2769,7 @@ tu6_emit_scissor_fdm(struct tu_cs *cs, struct tu_cmd_buffer *cmd, struct apply_viewport_state state = { .vp = *vp, .share_scale = !cmd->state.per_view_viewport, + .fake_single_viewport = cmd->state.fake_single_viewport, }; if (!state.share_scale) state.vp.scissor_count = num_views; diff --git a/src/freedreno/vulkan/tu_pipeline.h b/src/freedreno/vulkan/tu_pipeline.h index b1227ee1b1b..53ec4723b3d 100644 --- a/src/freedreno/vulkan/tu_pipeline.h +++ b/src/freedreno/vulkan/tu_pipeline.h @@ -109,7 +109,18 @@ struct tu_program_state unsigned dynamic_descriptor_offsets[MAX_SETS]; + /* Whether the per-view-viewport feature should be enabled in HW. This + * implicitly adds gl_ViewIndex to gl_ViewportIndex so that from a HW + * point of view (but not necessarily the user's point of view!) there + * is a viewport per view. + */ bool per_view_viewport; + /* If per_view_viewport is true and this is true, the app has provided + * a single viewport and we need to fake it by duplicating the viewport + * across views before transforming each viewport separately using FDM + * state. + */ + bool fake_single_viewport; bool writes_shading_rate; bool reads_shading_rate; bool accesses_smask;