+
+
+static uint32_t radv_get_executable_count(const struct radv_pipeline *pipeline)
+{
+ uint32_t ret = 0;
+ for (int i = 0; i < MESA_SHADER_STAGES; ++i) {
+ if (!pipeline->shaders[i])
+ continue;
+
+ if (i == MESA_SHADER_GEOMETRY &&
+ !radv_pipeline_has_ngg(pipeline)) {
+ ret += 2u;
+ } else {
+ ret += 1u;
+ }
+
+ }
+ return ret;
+}
+
+static struct radv_shader_variant *
+radv_get_shader_from_executable_index(const struct radv_pipeline *pipeline, int index, gl_shader_stage *stage)
+{
+ for (int i = 0; i < MESA_SHADER_STAGES; ++i) {
+ if (!pipeline->shaders[i])
+ continue;
+ if (!index) {
+ *stage = i;
+ return pipeline->shaders[i];
+ }
+
+ --index;
+
+ if (i == MESA_SHADER_GEOMETRY &&
+ !radv_pipeline_has_ngg(pipeline)) {
+ if (!index) {
+ *stage = i;
+ return pipeline->gs_copy_shader;
+ }
+ --index;
+ }
+ }
+
+ *stage = -1;
+ return NULL;
+}
+
+/* Basically strlcpy (which does not exist on linux) specialized for
+ * descriptions. */
+static void desc_copy(char *desc, const char *src) {
+ int len = strlen(src);
+ assert(len < VK_MAX_DESCRIPTION_SIZE);
+ memcpy(desc, src, len);
+ memset(desc + len, 0, VK_MAX_DESCRIPTION_SIZE - len);
+}
+
+VkResult radv_GetPipelineExecutablePropertiesKHR(
+ VkDevice _device,
+ const VkPipelineInfoKHR* pPipelineInfo,
+ uint32_t* pExecutableCount,
+ VkPipelineExecutablePropertiesKHR* pProperties)
+{
+ RADV_FROM_HANDLE(radv_pipeline, pipeline, pPipelineInfo->pipeline);
+ const uint32_t total_count = radv_get_executable_count(pipeline);
+
+ if (!pProperties) {
+ *pExecutableCount = total_count;
+ return VK_SUCCESS;
+ }
+
+ const uint32_t count = MIN2(total_count, *pExecutableCount);
+ for (unsigned i = 0, executable_idx = 0;
+ i < MESA_SHADER_STAGES && executable_idx < count; ++i) {
+ if (!pipeline->shaders[i])
+ continue;
+ pProperties[executable_idx].stages = mesa_to_vk_shader_stage(i);
+ const char *name = NULL;
+ const char *description = NULL;
+ switch(i) {
+ case MESA_SHADER_VERTEX:
+ name = "Vertex Shader";
+ description = "Vulkan Vertex Shader";
+ break;
+ case MESA_SHADER_TESS_CTRL:
+ if (!pipeline->shaders[MESA_SHADER_VERTEX]) {
+ pProperties[executable_idx].stages |= VK_SHADER_STAGE_VERTEX_BIT;
+ name = "Vertex + Tessellation Control Shaders";
+ description = "Combined Vulkan Vertex and Tessellation Control Shaders";
+ } else {
+ name = "Tessellation Control Shader";
+ description = "Vulkan Tessellation Control Shader";
+ }
+ break;
+ case MESA_SHADER_TESS_EVAL:
+ name = "Tessellation Evaluation Shader";
+ description = "Vulkan Tessellation Evaluation Shader";
+ break;
+ case MESA_SHADER_GEOMETRY:
+ if (radv_pipeline_has_tess(pipeline) && !pipeline->shaders[MESA_SHADER_TESS_EVAL]) {
+ pProperties[executable_idx].stages |= VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
+ name = "Tessellation Evaluation + Geometry Shaders";
+ description = "Combined Vulkan Tessellation Evaluation and Geometry Shaders";
+ } else if (!radv_pipeline_has_tess(pipeline) && !pipeline->shaders[MESA_SHADER_VERTEX]) {
+ pProperties[executable_idx].stages |= VK_SHADER_STAGE_VERTEX_BIT;
+ name = "Vertex + Geometry Shader";
+ description = "Combined Vulkan Vertex and Geometry Shaders";
+ } else {
+ name = "Geometry Shader";
+ description = "Vulkan Geometry Shader";
+ }
+ break;
+ case MESA_SHADER_FRAGMENT:
+ name = "Fragment Shader";
+ description = "Vulkan Fragment Shader";
+ break;
+ case MESA_SHADER_COMPUTE:
+ name = "Compute Shader";
+ description = "Vulkan Compute Shader";
+ break;
+ }
+
+ pProperties[executable_idx].subgroupSize = pipeline->shaders[i]->info.wave_size;
+ desc_copy(pProperties[executable_idx].name, name);
+ desc_copy(pProperties[executable_idx].description, description);
+
+ ++executable_idx;
+ if (i == MESA_SHADER_GEOMETRY &&
+ !radv_pipeline_has_ngg(pipeline)) {
+ assert(pipeline->gs_copy_shader);
+ if (executable_idx >= count)
+ break;
+
+ pProperties[executable_idx].stages = VK_SHADER_STAGE_GEOMETRY_BIT;
+ pProperties[executable_idx].subgroupSize = 64;
+ desc_copy(pProperties[executable_idx].name, "GS Copy Shader");
+ desc_copy(pProperties[executable_idx].description,
+ "Extra shader stage that loads the GS output ringbuffer into the rasterizer");
+
+ ++executable_idx;
+ }
+ }
+
+ VkResult result = *pExecutableCount < total_count ? VK_INCOMPLETE : VK_SUCCESS;
+ *pExecutableCount = count;
+ return result;
+}
+
+VkResult radv_GetPipelineExecutableStatisticsKHR(
+ VkDevice _device,
+ const VkPipelineExecutableInfoKHR* pExecutableInfo,
+ uint32_t* pStatisticCount,
+ VkPipelineExecutableStatisticKHR* pStatistics)
+{
+ RADV_FROM_HANDLE(radv_device, device, _device);
+ RADV_FROM_HANDLE(radv_pipeline, pipeline, pExecutableInfo->pipeline);
+ gl_shader_stage stage;
+ struct radv_shader_variant *shader = radv_get_shader_from_executable_index(pipeline, pExecutableInfo->executableIndex, &stage);
+
+ enum chip_class chip_class = device->physical_device->rad_info.chip_class;
+ unsigned lds_increment = chip_class >= GFX7 ? 512 : 256;
+ unsigned max_waves = radv_get_max_waves(device, shader, stage);
+
+ VkPipelineExecutableStatisticKHR *s = pStatistics;
+ VkPipelineExecutableStatisticKHR *end = s + (pStatistics ? *pStatisticCount : 0);
+ VkResult result = VK_SUCCESS;
+
+ if (s < end) {
+ desc_copy(s->name, "SGPRs");
+ desc_copy(s->description, "Number of SGPR registers allocated per subgroup");
+ s->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
+ s->value.u64 = shader->config.num_sgprs;
+ }
+ ++s;
+
+ if (s < end) {
+ desc_copy(s->name, "VGPRs");
+ desc_copy(s->description, "Number of VGPR registers allocated per subgroup");
+ s->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
+ s->value.u64 = shader->config.num_vgprs;
+ }
+ ++s;
+
+ if (s < end) {
+ desc_copy(s->name, "Spilled SGPRs");
+ desc_copy(s->description, "Number of SGPR registers spilled per subgroup");
+ s->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
+ s->value.u64 = shader->config.spilled_sgprs;
+ }
+ ++s;
+
+ if (s < end) {
+ desc_copy(s->name, "Spilled VGPRs");
+ desc_copy(s->description, "Number of VGPR registers spilled per subgroup");
+ s->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
+ s->value.u64 = shader->config.spilled_vgprs;
+ }
+ ++s;
+
+ if (s < end) {
+ desc_copy(s->name, "PrivMem VGPRs");
+ desc_copy(s->description, "Number of VGPRs stored in private memory per subgroup");
+ s->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
+ s->value.u64 = shader->info.private_mem_vgprs;
+ }
+ ++s;
+
+ if (s < end) {
+ desc_copy(s->name, "Code size");
+ desc_copy(s->description, "Code size in bytes");
+ s->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
+ s->value.u64 = shader->exec_size;
+ }
+ ++s;
+
+ if (s < end) {
+ desc_copy(s->name, "LDS size");
+ desc_copy(s->description, "LDS size in bytes per workgroup");
+ s->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
+ s->value.u64 = shader->config.lds_size * lds_increment;
+ }
+ ++s;
+
+ if (s < end) {
+ desc_copy(s->name, "Scratch size");
+ desc_copy(s->description, "Private memory in bytes per subgroup");
+ s->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
+ s->value.u64 = shader->config.scratch_bytes_per_wave;
+ }
+ ++s;
+
+ if (s < end) {
+ desc_copy(s->name, "Subgroups per SIMD");
+ desc_copy(s->description, "The maximum number of subgroups in flight on a SIMD unit");
+ s->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR;
+ s->value.u64 = max_waves;
+ }
+ ++s;
+
+ if (!pStatistics)
+ *pStatisticCount = s - pStatistics;
+ else if (s > end) {
+ *pStatisticCount = end - pStatistics;
+ result = VK_INCOMPLETE;
+ } else {
+ *pStatisticCount = s - pStatistics;
+ }
+
+ return result;
+}
+
+static VkResult radv_copy_representation(void *data, size_t *data_size, const char *src)
+{
+ size_t total_size = strlen(src) + 1;
+
+ if (!data) {
+ *data_size = total_size;
+ return VK_SUCCESS;
+ }
+
+ size_t size = MIN2(total_size, *data_size);
+
+ memcpy(data, src, size);
+ if (size)
+ *((char*)data + size - 1) = 0;
+ return size < total_size ? VK_INCOMPLETE : VK_SUCCESS;
+}
+
+VkResult radv_GetPipelineExecutableInternalRepresentationsKHR(
+ VkDevice device,
+ const VkPipelineExecutableInfoKHR* pExecutableInfo,
+ uint32_t* pInternalRepresentationCount,
+ VkPipelineExecutableInternalRepresentationKHR* pInternalRepresentations)
+{
+ RADV_FROM_HANDLE(radv_pipeline, pipeline, pExecutableInfo->pipeline);
+ gl_shader_stage stage;
+ struct radv_shader_variant *shader = radv_get_shader_from_executable_index(pipeline, pExecutableInfo->executableIndex, &stage);
+
+ VkPipelineExecutableInternalRepresentationKHR *p = pInternalRepresentations;
+ VkPipelineExecutableInternalRepresentationKHR *end = p + (pInternalRepresentations ? *pInternalRepresentationCount : 0);
+ VkResult result = VK_SUCCESS;
+ /* optimized NIR */
+ if (p < end) {
+ p->isText = true;
+ desc_copy(p->name, "NIR Shader(s)");
+ desc_copy(p->description, "The optimized NIR shader(s)");
+ if (radv_copy_representation(p->pData, &p->dataSize, shader->nir_string) != VK_SUCCESS)
+ result = VK_INCOMPLETE;
+ }
+ ++p;
+
+ /* backend IR */
+ if (p < end) {
+ p->isText = true;
+ if (shader->aco_used) {
+ desc_copy(p->name, "ACO IR");
+ desc_copy(p->description, "The ACO IR after some optimizations");
+ } else {
+ desc_copy(p->name, "LLVM IR");
+ desc_copy(p->description, "The LLVM IR after some optimizations");
+ }
+ if (radv_copy_representation(p->pData, &p->dataSize, shader->ir_string) != VK_SUCCESS)
+ result = VK_INCOMPLETE;
+ }
+ ++p;
+
+ /* Disassembler */
+ if (p < end) {
+ p->isText = true;
+ desc_copy(p->name, "Assembly");
+ desc_copy(p->description, "Final Assembly");
+ if (radv_copy_representation(p->pData, &p->dataSize, shader->disasm_string) != VK_SUCCESS)
+ result = VK_INCOMPLETE;
+ }
+ ++p;
+
+ if (!pInternalRepresentations)
+ *pInternalRepresentationCount = p - pInternalRepresentations;
+ else if(p > end) {
+ result = VK_INCOMPLETE;
+ *pInternalRepresentationCount = end - pInternalRepresentations;
+ } else {
+ *pInternalRepresentationCount = p - pInternalRepresentations;
+ }
+
+ return result;
+}