From dfddb3fef1c29f69766f6575771f7fdad21464cd Mon Sep 17 00:00:00 2001 From: Duncan Brawley Date: Fri, 23 Jan 2026 15:49:10 +0000 Subject: [PATCH] pvr: Add support for VK_KHR_pipeline_executable_properties This adds support for receiving additional statistics about PowerVR shaders for the Rogue architecture. vkGetPipelineExecutablePropertiesKHR and vkGetPipelineExecutableStatisticsKHR are fully supported. vkGetPipelineExecutableInternalRepresentationsKHR does not currently return any internal representations. Tests used: dEQP-VK.pipeline.monolithic.executable_properties.* Signed-off-by: Duncan Brawley Reviewed-by: Simon Perretta Part-of: --- docs/features.txt | 2 +- docs/relnotes/new_features.txt | 1 + src/imagination/vulkan/pvr_arch_pipeline.c | 203 +++++++++++++++++++ src/imagination/vulkan/pvr_physical_device.c | 4 + src/imagination/vulkan/pvr_pipeline.h | 8 + 5 files changed, 217 insertions(+), 1 deletion(-) diff --git a/docs/features.txt b/docs/features.txt index d0c0faf5f40..50566e86f24 100644 --- a/docs/features.txt +++ b/docs/features.txt @@ -563,7 +563,7 @@ Khronos extensions that are not part of any Vulkan version: VK_KHR_maintenance10 DONE (anv, nvk, radv) VK_KHR_performance_query DONE (anv, radv/gfx10.3+, tu, v3dv) VK_KHR_pipeline_binary DONE (anv, hk, nvk, panvk, radv) - VK_KHR_pipeline_executable_properties DONE (anv, hk, nvk, panvk, hasvk, radv, tu, v3dv) + VK_KHR_pipeline_executable_properties DONE (anv, hasvk, hk, nvk, panvk, pvr, radv, tu, v3dv) VK_KHR_pipeline_library DONE (anv, hk, lvp, nvk, panvk, radv, tu, vn) VK_KHR_present_id DONE (anv, hk, nvk, panvk, radv, tu, vn) VK_KHR_present_id2 DONE (anv, hk, nvk, panvk, pvr, radv, tu, v3dv, vn) diff --git a/docs/relnotes/new_features.txt b/docs/relnotes/new_features.txt index 47d2e8b2efd..792b60e1d39 100644 --- a/docs/relnotes/new_features.txt +++ b/docs/relnotes/new_features.txt @@ -9,3 +9,4 @@ VK_KHR_get_display_properties2 on panvk VK_EXT_acquire_drm_display on panvk VK_KHR_present_id on panvk VK_KHR_present_wait on panvk +VK_KHR_pipeline_executable_properties on pvr diff --git a/src/imagination/vulkan/pvr_arch_pipeline.c b/src/imagination/vulkan/pvr_arch_pipeline.c index e3969335f80..a32e7257429 100644 --- a/src/imagination/vulkan/pvr_arch_pipeline.c +++ b/src/imagination/vulkan/pvr_arch_pipeline.c @@ -59,6 +59,7 @@ #include "util/log.h" #include "util/macros.h" #include "util/ralloc.h" +#include "util/shader_stats.h" #include "util/u_dynarray.h" #include "util/u_math.h" #include "vk_alloc.h" @@ -1044,6 +1045,21 @@ static VkResult pvr_compute_pipeline_compile( pvr_compute_state_save(compute_pipeline, cs); + if (pCreateInfo->flags & VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR) { + struct pvr_stats stats = pco_get_pvr_stats(cs); + compute_pipeline->cs_stats = + vk_zalloc2(&device->vk.alloc, + allocator, + sizeof(stats), + 8, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + + if (!compute_pipeline->cs_stats) + return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); + + memcpy(compute_pipeline->cs_stats, &stats, sizeof(stats)); + } + result = pvr_gpu_upload_usc(device, pco_shader_binary_data(cs), pco_shader_binary_size(cs), @@ -1191,6 +1207,8 @@ static void pvr_compute_pipeline_destroy( pvr_pipeline_finish(device, &compute_pipeline->base); + vk_free2(&device->vk.alloc, allocator, compute_pipeline->cs_stats); + vk_free2(&device->vk.alloc, allocator, compute_pipeline); } @@ -1275,6 +1293,9 @@ pvr_graphics_pipeline_destroy(struct pvr_device *const device, pvr_pipeline_destroy_shader_data(&gfx_pipeline->vs_data); pvr_pipeline_destroy_shader_data(&gfx_pipeline->fs_data); + vk_free2(&device->vk.alloc, allocator, gfx_pipeline->vs_stats); + vk_free2(&device->vk.alloc, allocator, gfx_pipeline->fs_stats); + vk_free2(&device->vk.alloc, allocator, gfx_pipeline); } @@ -2913,6 +2934,20 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device, pvr_vertex_state_save(gfx_pipeline, *vs); + if (pCreateInfo->flags & VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR) { + struct pvr_stats stats = pco_get_pvr_stats(*vs); + gfx_pipeline->vs_stats = vk_zalloc2(&device->vk.alloc, + allocator, + sizeof(stats), + 8, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + + if (!gfx_pipeline->vs_stats) + return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); + + memcpy(gfx_pipeline->vs_stats, &stats, sizeof(stats)); + } + pvr_graphics_pipeline_setup_vertex_dma(gfx_pipeline, pCreateInfo->pVertexInputState, state->vi, @@ -2930,6 +2965,20 @@ pvr_graphics_pipeline_compile(struct pvr_device *const device, if (*fs) { pvr_fragment_state_save(gfx_pipeline, *fs); + if (pCreateInfo->flags & VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR) { + struct pvr_stats stats = pco_get_pvr_stats(*fs); + gfx_pipeline->fs_stats = vk_zalloc2(&device->vk.alloc, + allocator, + sizeof(stats), + 8, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + + if (!gfx_pipeline->fs_stats) + return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); + + memcpy(gfx_pipeline->fs_stats, &stats, sizeof(stats)); + } + pvr_graphics_pipeline_setup_fragment_coeff_program( gfx_pipeline, nir_shaders[MESA_SHADER_FRAGMENT]); @@ -3303,3 +3352,157 @@ void PVR_PER_ARCH(DestroyPipeline)(VkDevice _device, UNREACHABLE("Unknown pipeline type."); } } + +static uint32_t pvr_get_executable_count(struct pvr_pipeline *pipeline) +{ + uint32_t exe_count = 0; + + switch (pipeline->type) { + case PVR_PIPELINE_TYPE_GRAPHICS: { + struct pvr_graphics_pipeline *const gfx_pipeline = + to_pvr_graphics_pipeline(pipeline); + + for (mesa_shader_stage stage = 0; stage < MESA_SHADER_STAGES; ++stage) { + size_t stage_index = gfx_pipeline->stage_indices[stage]; + + if (stage_index != ~0) + exe_count++; + } + break; + } + + case PVR_PIPELINE_TYPE_COMPUTE: { + /* Compute pipelines always only have one executable. */ + exe_count = 1; + break; + } + + default: + UNREACHABLE("Unknown pipeline type."); + } + + return exe_count; +} + +VkResult pvr_GetPipelineExecutableStatisticsKHR( + UNUSED VkDevice _device, + const VkPipelineExecutableInfoKHR *pExecutableInfo, + uint32_t *pStatisticCount, + VkPipelineExecutableStatisticKHR *pStatistics) +{ + VK_FROM_HANDLE(pvr_pipeline, pipeline, pExecutableInfo->pipeline); + VK_OUTARRAY_MAKE_TYPED(VkPipelineExecutableStatisticKHR, + out, + pStatistics, + pStatisticCount); + + switch (pipeline->type) { + case PVR_PIPELINE_TYPE_GRAPHICS: { + struct pvr_graphics_pipeline *const gfx_pipeline = + to_pvr_graphics_pipeline(pipeline); + + if (pExecutableInfo->executableIndex == 0) { + vk_add_pvr_stats(out, gfx_pipeline->vs_stats); + } else { + assert(pExecutableInfo->executableIndex == 1); + vk_add_pvr_stats(out, gfx_pipeline->fs_stats); + } + break; + } + + case PVR_PIPELINE_TYPE_COMPUTE: { + struct pvr_compute_pipeline *const compute_pipeline = + to_pvr_compute_pipeline(pipeline); + + assert(pExecutableInfo->executableIndex == 0); + vk_add_pvr_stats(out, compute_pipeline->cs_stats); + break; + } + + default: + UNREACHABLE("Unknown pipeline type."); + } + + return vk_outarray_status(&out); +} + +VkResult pvr_GetPipelineExecutablePropertiesKHR( + VkDevice _device, + const VkPipelineInfoKHR *pPipelineInfo, + uint32_t *pExecutableCount, + VkPipelineExecutablePropertiesKHR *pProperties) +{ + VK_FROM_HANDLE(pvr_device, device, _device); + VK_FROM_HANDLE(pvr_pipeline, pipeline, pPipelineInfo->pipeline); + ASSERTED uint32_t actualExeCount = pvr_get_executable_count(pipeline); + + /* Due to individual shaders currently being stored as individual members + * of pvr_graphics_pipeline, if support is added for a new shader stage, + * this code will need to be updated. + */ + assert(actualExeCount >= 1 && actualExeCount <= 2); + + VK_OUTARRAY_MAKE_TYPED(VkPipelineExecutablePropertiesKHR, + out, + pProperties, + pExecutableCount); + + switch (pipeline->type) { + case PVR_PIPELINE_TYPE_GRAPHICS: { + struct pvr_graphics_pipeline *const gfx_pipeline = + to_pvr_graphics_pipeline(pipeline); + + vk_outarray_append_typed (VkPipelineExecutablePropertiesKHR, + &out, + vertProps) { + vertProps->stages |= VK_SHADER_STAGE_VERTEX_BIT; + VK_COPY_STR(vertProps->name, "vertex"); + VK_COPY_STR(vertProps->description, "Vulkan Vertex Shader"); + vertProps->subgroupSize = device->pdevice->vk.properties.subgroupSize; + } + + if (gfx_pipeline->stage_indices[MESA_SHADER_FRAGMENT] != ~0) { + vk_outarray_append_typed (VkPipelineExecutablePropertiesKHR, + &out, + fragProps) { + fragProps->stages |= VK_SHADER_STAGE_FRAGMENT_BIT; + VK_COPY_STR(fragProps->name, "fragment"); + VK_COPY_STR(fragProps->description, "Vulkan Fragment Shader"); + fragProps->subgroupSize = + device->pdevice->vk.properties.subgroupSize; + } + } + + break; + } + + case PVR_PIPELINE_TYPE_COMPUTE: { + vk_outarray_append_typed (VkPipelineExecutablePropertiesKHR, + &out, + compProps) { + compProps->stages = VK_SHADER_STAGE_COMPUTE_BIT; + VK_COPY_STR(compProps->name, "compute"); + VK_COPY_STR(compProps->description, "Vulkan Compute Shader"); + compProps->subgroupSize = device->pdevice->vk.properties.subgroupSize; + } + break; + } + default: + UNREACHABLE("Unknown pipeline type."); + } + + return vk_outarray_status(&out); +} + +VkResult pvr_GetPipelineExecutableInternalRepresentationsKHR( + UNUSED VkDevice _device, + UNUSED const VkPipelineExecutableInfoKHR *pExecutableInfo, + uint32_t *pInternalRepresentationCount, + UNUSED VkPipelineExecutableInternalRepresentationKHR + *pInternalRepresentations) +{ + pvr_finishme("Add support for requesting intermediate representations."); + *pInternalRepresentationCount = 0; + + return VK_SUCCESS; +} diff --git a/src/imagination/vulkan/pvr_physical_device.c b/src/imagination/vulkan/pvr_physical_device.c index 04decd75b55..ee60d74cf6e 100644 --- a/src/imagination/vulkan/pvr_physical_device.c +++ b/src/imagination/vulkan/pvr_physical_device.c @@ -150,6 +150,7 @@ static void pvr_physical_device_get_supported_extensions( .KHR_maintenance3 = true, .KHR_map_memory2 = true, .KHR_multiview = true, + .KHR_pipeline_executable_properties = true, .KHR_present_id2 = PVR_USE_WSI_PLATFORM, .KHR_present_wait2 = PVR_USE_WSI_PLATFORM, .KHR_relaxed_block_layout = true, @@ -468,6 +469,9 @@ static void pvr_physical_device_get_supported_features( /* Vulkan 1.2 / VK_KHR_dynamic_rendering */ .dynamicRendering = true, + + /* VK_KHR_pipeline_executable_properties */ + .pipelineExecutableInfo = true, }; } diff --git a/src/imagination/vulkan/pvr_pipeline.h b/src/imagination/vulkan/pvr_pipeline.h index d926ab9c1ed..8e4e2aa28e4 100644 --- a/src/imagination/vulkan/pvr_pipeline.h +++ b/src/imagination/vulkan/pvr_pipeline.h @@ -20,6 +20,7 @@ #include "pvr_common.h" #include "pvr_csb.h" #include "pvr_pds.h" +#include "util/shader_stats.h" struct pvr_suballoc_bo; @@ -109,6 +110,9 @@ struct pvr_compute_pipeline { uint32_t num_workgroups_data_patching_offset; uint32_t num_workgroups_indirect_src_patching_offset; uint32_t num_workgroups_indirect_src_dma_patching_offset; + + /* Debug Info */ + struct pvr_stats *cs_stats; }; struct pvr_graphics_pipeline { @@ -126,6 +130,10 @@ struct pvr_graphics_pipeline { struct pvr_vertex_shader_state vertex; struct pvr_fragment_shader_state fragment; } shader_state; + + /* Debug Info */ + struct pvr_stats *vs_stats; + struct pvr_stats *fs_stats; }; struct pvr_private_compute_pipeline {