panvk: rework collect_cache_flush_info

src_access defines the availability op and the host-to-device domain op.
dst_access defines the visibility op and the device-to-host domain op.
They should be treated separately.

Add a low-level helper, add_memory_dependency, to translate access flags
to panvk_cache_flush_info.

Update collect_cache_flush_info to use add_memory_dependency.  Also
replace the custom subqueue access flag mappings by
vk_filter_{src,dst}_access_flags2.

The main difference is that barriers such as

  .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
  .dstAccessMask = VK_ACCESS_2_NONE,

or

 .srcAccessMask = VK_ACCESS_2_NONE,
 .dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT,

are no longer ignored.

Signed-off-by: Chia-I Wu <olvaffe@gmail.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/32074>
This commit is contained in:
Chia-I Wu
2024-11-08 09:09:36 -08:00
committed by Marge Bot
parent 4a598e946c
commit f1f3b56834
+59 -70
View File
@@ -213,6 +213,57 @@ get_subqueue_stages(enum panvk_subqueue_id subqueue)
}
}
static void
add_memory_dependency(struct panvk_cache_flush_info *cache_flush,
VkAccessFlags2 src_access, VkAccessFlags2 dst_access)
{
/* Note on the cache organization:
*
* - L2 cache is unified, so all changes to this cache are automatically
* visible to all GPU sub-components (shader cores, tiler, ...). This
* means we only need to flush when the host (AKA CPU) is involved.
* - LS caches (which are basically just read-write L1 caches) are coherent
* with each other and with the L2 cache, so again, we only need to flush
* when the host is involved.
* - Other read-only L1 caches (like the ones in front of the texture unit)
* are not coherent with the LS or L2 caches, and thus need to be
* invalidated any time a write happens.
*
* Translating to the Vulkan memory model:
*
* - The device domain is the L2 cache.
* - An availability operation from device writes to the device domain is
* nop.
* - A visibility operation from the device domain to device accesses that
* are coherent with L2/LS is nop.
* - A visibility operation from the device domain to device accesses that
* are incoherent with L2/LS invalidates the other RO L1 caches.
* - A host-to-device domain operation invalidates all caches.
* - A device-to-host domain operation flushes L2/LS.
*/
const VkAccessFlags2 ro_l1_access =
VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_SHADER_SAMPLED_READ_BIT;
/* visibility op */
if (dst_access & ro_l1_access)
cache_flush->others |= true;
/* host-to-device domain op */
if (src_access & VK_ACCESS_2_HOST_WRITE_BIT) {
cache_flush->l2 |= MALI_CS_FLUSH_MODE_CLEAN_AND_INVALIDATE;
cache_flush->lsc |= MALI_CS_FLUSH_MODE_CLEAN_AND_INVALIDATE;
cache_flush->others |= true;
}
/* device-to-host domain op */
if (dst_access & (VK_ACCESS_2_HOST_READ_BIT | VK_ACCESS_2_HOST_WRITE_BIT)) {
cache_flush->l2 |= MALI_CS_FLUSH_MODE_CLEAN;
cache_flush->lsc |= MALI_CS_FLUSH_MODE_CLEAN;
}
}
static bool
src_stages_need_draw_flush(VkPipelineStageFlags2 stages)
{
@@ -258,77 +309,15 @@ src_stages_to_subqueue_sb_mask(enum panvk_subqueue_id subqueue,
static void
collect_cache_flush_info(enum panvk_subqueue_id subqueue,
struct panvk_cache_flush_info *cache_flush,
VkPipelineStageFlags2 src_stages,
VkPipelineStageFlags2 dst_stages,
VkAccessFlags2 src_access, VkAccessFlags2 dst_access)
{
static const VkAccessFlags2 dev_writes[PANVK_SUBQUEUE_COUNT] = {
[PANVK_SUBQUEUE_VERTEX_TILER] =
VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT,
[PANVK_SUBQUEUE_FRAGMENT] =
VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT |
VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
VK_ACCESS_2_TRANSFER_WRITE_BIT,
[PANVK_SUBQUEUE_COMPUTE] =
VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT,
};
static const VkAccessFlags2 dev_reads[PANVK_SUBQUEUE_COUNT] = {
[PANVK_SUBQUEUE_VERTEX_TILER] =
VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_2_INDEX_READ_BIT |
VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_2_UNIFORM_READ_BIT |
VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_SHADER_SAMPLED_READ_BIT |
VK_ACCESS_2_SHADER_STORAGE_READ_BIT,
[PANVK_SUBQUEUE_FRAGMENT] =
VK_ACCESS_2_UNIFORM_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_SHADER_SAMPLED_READ_BIT |
VK_ACCESS_2_SHADER_STORAGE_READ_BIT,
[PANVK_SUBQUEUE_COMPUTE] = VK_ACCESS_2_UNIFORM_READ_BIT |
VK_ACCESS_2_TRANSFER_READ_BIT |
VK_ACCESS_2_SHADER_SAMPLED_READ_BIT |
VK_ACCESS_2_SHADER_STORAGE_READ_BIT,
};
/* limit access to the subqueue and host */
const VkPipelineStageFlags2 subqueue_stages =
get_subqueue_stages(subqueue) | VK_PIPELINE_STAGE_2_HOST_BIT;
src_access = vk_filter_src_access_flags2(subqueue_stages, src_access);
dst_access = vk_filter_dst_access_flags2(subqueue_stages, dst_access);
/* Note on the cache organization:
* - L2 cache is unified, so all changes to this cache are automatically
* visible to all GPU sub-components (shader cores, tiler, ...). This
* means we only need to flush when the host (AKA CPU) is involved.
* - LS caches (which are basically just read-write L1 caches) are coherent
* with each other and with the L2 cache, so again, we only need to flush
* when the host is involved.
* - Other read-only L1 caches (like the ones in front of the texture unit)
* are not coherent with the LS or L2 caches, and thus need to be
* invalidated any time a write happens.
*/
#define ACCESS_HITS_RO_L1_CACHE \
(VK_ACCESS_2_SHADER_SAMPLED_READ_BIT | \
VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | \
VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | \
VK_ACCESS_2_TRANSFER_READ_BIT)
if ((dev_writes[subqueue] & src_access) &&
(dev_reads[subqueue] & ACCESS_HITS_RO_L1_CACHE & dst_access))
cache_flush->others |= true;
/* If the host wrote something, we need to clean/invalidate everything. */
if ((src_stages & VK_PIPELINE_STAGE_2_HOST_BIT) &&
(src_access & VK_ACCESS_2_HOST_WRITE_BIT) &&
((dev_reads[subqueue] | dev_writes[subqueue]) & dst_access)) {
cache_flush->l2 |= MALI_CS_FLUSH_MODE_CLEAN_AND_INVALIDATE;
cache_flush->lsc |= MALI_CS_FLUSH_MODE_CLEAN_AND_INVALIDATE;
cache_flush->others |= true;
}
/* If the host needs to read something we wrote, we need to clean
* everything. */
if ((dst_stages & VK_PIPELINE_STAGE_2_HOST_BIT) &&
(dst_access & VK_ACCESS_2_HOST_READ_BIT) &&
(dev_writes[subqueue] & src_access)) {
cache_flush->l2 |= MALI_CS_FLUSH_MODE_CLEAN;
cache_flush->lsc |= MALI_CS_FLUSH_MODE_CLEAN;
}
add_memory_dependency(cache_flush, src_access, dst_access);
}
static void
@@ -347,8 +336,8 @@ collect_cs_deps(struct panvk_cmd_buffer *cmdbuf,
continue;
deps->src[i].wait_sb_mask |= sb_mask;
collect_cache_flush_info(i, &deps->src[i].cache_flush, src_stages,
dst_stages, src_access, dst_access);
collect_cache_flush_info(i, &deps->src[i].cache_flush, src_access,
dst_access);
wait_subqueue_mask |= BITFIELD_BIT(i);
}