diff options
author | Samuel Pitoiset <samuel.pitoiset@gmail.com> | 2018-04-06 10:23:56 +0200 |
---|---|---|
committer | Samuel Pitoiset <samuel.pitoiset@gmail.com> | 2018-04-06 10:23:56 +0200 |
commit | acee7b888285c635f17b36cd13f8a6b6065b88e2 (patch) | |
tree | 88104e0ffc430b031acbe9fcf7b28ddf4b828488 /run.c |
initial commit; import existing files
Signed-off-by: Samuel Pitoiset <samuel.pitoiset@gmail.com>
Diffstat (limited to 'run.c')
-rw-r--r-- | run.c | 539 |
1 files changed, 539 insertions, 0 deletions
@@ -0,0 +1,539 @@ +/* + * Copyright © 2018 Valve Corporation + * + * Based in part on shader-db which is: + * Copyright © 2014 Intel Corporation + * Copyright © 2015 Advanced Micro Devices, Inc. + * + * 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 (including the next + * paragraph) 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. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <ftw.h> +#include <time.h> + +#include "serialize.h" + +#define unlikely(x) __builtin_expect(!!(x), 0) + +/* Pipeline tests. */ +static unsigned pipeline_test_size = 1 << 15; /* next-pow-2(num pipelines in db) */ +static unsigned pipeline_test_length; +static struct pipeline_test { + char *filename; + off_t filesize; +} *pipeline_test; + +static int +gather_pipeline_test(const char *fpath, const struct stat *sb, int typeflag) +{ + static const char *ext = ".pipeline_test"; + + if (strlen(fpath) >= strlen(ext) && + memcmp(fpath + strlen(fpath) - strlen(ext), ext, strlen(ext)) == 0) { + if (unlikely(!S_ISREG(sb->st_mode))) { + fprintf(stderr, "ERROR: %s is not a regular file\n", fpath); + return -1; + } + + if (unlikely(pipeline_test_size <= pipeline_test_length)) { + pipeline_test_size *= 2; + pipeline_test = + realloc(pipeline_test, + pipeline_test_size * sizeof(struct pipeline_test)); + } + + pipeline_test[pipeline_test_length].filename = malloc(strlen(fpath) + 1); + memcpy(pipeline_test[pipeline_test_length].filename, fpath, strlen(fpath) + 1); + pipeline_test[pipeline_test_length].filesize = sb->st_size; + pipeline_test_length++; + } + + return 0; +} + +/* Shader stats */ +static PFN_vkGetShaderInfoAMD vkGetShaderInfo = VK_NULL_HANDLE; + +struct shader_stats +{ + unsigned num_sgprs; + unsigned num_vgprs; + unsigned num_spilled_sgprs; + unsigned num_spilled_vgprs; + unsigned priv_mem_vgprs; + unsigned code_size; + unsigned lds; + unsigned scratch; + unsigned max_waves; +}; + +#define PARSE_STAT(key, value) \ + line = strtok(NULL, "\n"); \ + if (sscanf(line, key, value) != 1) \ + return -1; + +static int +parse_shader_stats(char *buf, struct shader_stats *stats) +{ + char *line; + + line = strtok(buf, "\n"); + while(line) { + if (!strcmp(line, "*** SHADER STATS ***")) + break; + line = strtok(NULL, "\n"); + } + + if (unlikely(!line)) + return -1; + + PARSE_STAT("SGPRS: %d\n", &stats->num_sgprs); + PARSE_STAT("VGPRS: %d\n", &stats->num_vgprs); + PARSE_STAT("Spilled SGPRs: %d\n", &stats->num_spilled_sgprs); + PARSE_STAT("Spilled VGPRs: %d\n", &stats->num_spilled_vgprs); + PARSE_STAT("PrivMem VGPRS: %d\n", &stats->priv_mem_vgprs); + PARSE_STAT("Code Size: %d bytes\n", &stats->code_size); + PARSE_STAT("LDS: %d blocks\n", &stats->lds); + PARSE_STAT("Scratch: %d bytes per wave\n", &stats->scratch); + PARSE_STAT("Max Waves: %d\n", &stats->max_waves); + + return 0; +} + +static bool +is_shader_stage_valid(VkDevice device, VkPipeline pipeline, + VkShaderStageFlagBits stage) +{ + VkResult result; + size_t size; + + result = vkGetShaderInfo(device, pipeline, stage, + VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, &size, NULL); + if (result == VK_ERROR_FEATURE_NOT_PRESENT){ + /* The spec doesn't state what to do when the stage is invalid, and RADV + * returns VK_ERROR_FEATURE_NOT_PRESENT in this situation, mostly for + * merged shaders on GFX9. + */ + return false; + } + + return true; +} + +static int +get_shader_stats(VkDevice device, VkPipeline pipeline, + VkShaderStageFlagBits stage, + struct shader_stats *stats) +{ + VkResult result; + size_t size; + char *buf; + int ret = 0; + + result = vkGetShaderInfo(device, pipeline, stage, + VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, &size, NULL); + if (unlikely(result != VK_SUCCESS)) + return -1; + + buf = malloc(size); + if (unlikely(!buf)) + return -1; + + result = vkGetShaderInfo(device, pipeline, stage, + VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, &size, buf); + if (unlikely(result != VK_SUCCESS)) { + ret = -1; + goto fail; + } + + if (unlikely(parse_shader_stats(buf, stats) < 0)) { + ret = -1; + goto fail; + } + +fail: + free(buf); + return ret; +} + +static void +print_shader_stats(const char *pipeline_name, VkShaderStageFlagBits stage, + const struct shader_stats *stats) +{ + printf("%s - ", pipeline_name); + printf("Shader Stats: "); + printf("SGPRS: %d ", stats->num_sgprs); + printf("VGPRS: %d ", stats->num_vgprs); + printf("Code Size: %d ", stats->code_size); + printf("LDS: %d ", stats->lds); + printf("Scratch: %d ", stats->scratch); + printf("Max Waves: %d ", stats->max_waves); + printf("Spilled SGPRs: %d ", stats->num_spilled_sgprs); + printf("Spilled VGPRs: %d ", stats->num_spilled_vgprs); + printf("PrivMem VGPRs: %d ", stats->priv_mem_vgprs); + printf("\n"); +} + +static VkResult +create_graphics_pipeline(VkDevice device, struct pipeline_info *info, + VkPipelineLayout layout, VkPipeline *pipeline) + +{ + VkGraphicsPipelineCreateInfo pipelineInfo = {}; + VkRenderPass renderPass = VK_NULL_HANDLE; + VkResult result; + + /* Render pass. */ + result = vkCreateRenderPass(device, &info->renderPassInfo, + NULL, &renderPass); + if (unlikely(result != VK_SUCCESS)) + return result; + + /* Graphics pipeline. */ + pipelineInfo.stageCount = info->stageCount; + pipelineInfo.pStages = info->pShaderStagesInfo; + pipelineInfo.pVertexInputState = &info->vertexInputState; + pipelineInfo.pInputAssemblyState = &info->inputAssemblyState; + pipelineInfo.pTessellationState = &info->tessellationState; + pipelineInfo.pViewportState = &info->viewportState; + pipelineInfo.pRasterizationState = &info->rasterizationState; + pipelineInfo.pMultisampleState = &info->multisampleState; + pipelineInfo.pDepthStencilState = &info->depthStencilState; + pipelineInfo.pColorBlendState = &info->colorBlendState; + pipelineInfo.pDynamicState = &info->dynamicState; + pipelineInfo.layout = layout; + pipelineInfo.renderPass = renderPass; + + result = vkCreateGraphicsPipelines(device, NULL, 1, &pipelineInfo, + NULL, pipeline); + + vkDestroyRenderPass(device, renderPass, NULL); + return result; +} + +static int +create_compute_pipeline(VkDevice device, struct pipeline_info *info, + VkPipelineLayout layout, VkPipeline *pipeline) +{ + VkComputePipelineCreateInfo pipelineInfo = {}; + + /* Compute pipeline. */ + pipelineInfo.stage = *info->pShaderStagesInfo; + pipelineInfo.layout = layout; + + return vkCreateComputePipelines(device, NULL, 1, &pipelineInfo, + NULL, pipeline); +} +static int +create_pipeline(VkDevice device, const char *pipeline_name, + struct pipeline_info *info) +{ + VkPipelineLayout layout = VK_NULL_HANDLE; + VkPipeline pipeline = VK_NULL_HANDLE; + VkResult result; + int ret = 0; + + /* Shader modules. */ + for (uint32_t i = 0; i < info->stageCount; i++) { + result = vkCreateShaderModule(device, &info->pShaderModulesInfo[i], + NULL, &info->pShaderStagesInfo[i].module); + if (unlikely(result != VK_SUCCESS)) { + ret = -1; + goto fail; + } + } + + /* Descriptor set layouts. */ + VkDescriptorSetLayout *pSetLayouts = + calloc(info->pipelineLayoutInfo.setLayoutCount, sizeof(*pSetLayouts)); + if (unlikely(!pSetLayouts)) { + ret = -1; + goto fail; + } + + for (uint32_t i = 0; i < info->pipelineLayoutInfo.setLayoutCount; i++) { + result = vkCreateDescriptorSetLayout(device, &info->pSetLayoutsInfo[i], + NULL, &pSetLayouts[i]); + if (unlikely(result != VK_SUCCESS)) { + ret = -1; + goto fail; + } + } + + /* Attach descriptor set layouts to the pipeline. */ + info->pipelineLayoutInfo.pSetLayouts = pSetLayouts; + + /* Pipeline layout. */ + result = vkCreatePipelineLayout(device, &info->pipelineLayoutInfo, + NULL, &layout); + if (unlikely(result != VK_SUCCESS)) { + ret = -1; + goto fail; + } + + /* Graphics/Compute pipeline. */ + if (info->bindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS) { + result = create_graphics_pipeline(device, info, layout, &pipeline); + } else { + assert(info->bindPoint == VK_PIPELINE_BIND_POINT_COMPUTE); + result = create_compute_pipeline(device, info, layout, &pipeline); + } + + if (unlikely(result != VK_SUCCESS)) { + ret = -1; + goto fail; + } + + /* Shader stats. */ + for (uint32_t i = 0; i < info->stageCount; i++) { + VkPipelineShaderStageCreateInfo *pCreateInfo = &info->pShaderStagesInfo[i]; + VkShaderStageFlagBits stage = pCreateInfo->stage; + struct shader_stats stats = {}; + + if (!is_shader_stage_valid(device, pipeline, stage)) + continue; + + ret = get_shader_stats(device, pipeline, stage, &stats); + if (unlikely(ret < 0)) { + fprintf(stderr, "Failed to get shader stats!\n"); + goto fail; + } + + print_shader_stats(pipeline_name, stage, &stats); + } + +fail: + for (uint32_t i = 0; i < info->stageCount; i++) + vkDestroyShaderModule(device, info->pShaderStagesInfo[i].module, NULL); + for (uint32_t i = 0; i < info->pipelineLayoutInfo.setLayoutCount; i++) + vkDestroyDescriptorSetLayout(device, pSetLayouts[i], NULL); + vkDestroyPipelineLayout(device, layout, NULL); + vkDestroyPipeline(device, pipeline, NULL); + free(pSetLayouts); + + return ret; +} + +static void +free_pipeline(struct pipeline_info *pipeline) +{ + for (uint32_t i = 0; i < pipeline->pipelineLayoutInfo.setLayoutCount; i++) { + VkDescriptorSetLayoutCreateInfo *pInfo = &pipeline->pSetLayoutsInfo[i]; + free((void *)pInfo->pBindings); + } + free(pipeline->pSetLayoutsInfo); + free(pipeline->pShaderStagesInfo); + free(pipeline->pShaderModulesInfo); + free((void *)pipeline->renderPassInfo.pSubpasses); /* XXX*/ + free(pipeline); +} + +static int +run(VkDevice device, const char *pipeline_name, const char *data, off_t size) +{ + struct pipeline_info *pipeline; + struct blob_reader metadata; + int ret = 0; + + blob_reader_init(&metadata, data, size); + + pipeline = calloc(1, sizeof(*pipeline)); + if (unlikely(!pipeline)) + return -1; + + if (unlikely(!deserialize_pipeline(pipeline, &metadata))) { + fprintf(stderr, "Failed to deserialize pipeline, corrupted data?\n"); + return -1; + } + + ret = create_pipeline(device, pipeline_name, pipeline); + if (unlikely(ret < 0)) { + fprintf(stderr, "Failed to create pipeline!\n"); + goto fail; + } + +fail: + free_pipeline(pipeline); + return ret; +} + +static void +print_usage(const char *prog_name) +{ + fprintf(stderr, + "Usage: %s <directories and *.pipeline_test files>\n", + prog_name); +} + +int main(int argc, char **argv) +{ + const char *extensionNames[] = { "VK_AMD_shader_info" }; + VkQueueFamilyProperties queue_family; + VkPhysicalDevice *physical_devices; + struct timespec start, end; + uint32_t device_count; + uint32_t queue_count = 1; + VkInstance instance; + VkDevice device; + VkResult result; + int ret = 0; + + if (argc < 2) { + fprintf(stderr, "No directories specified!\n"); + print_usage(argv[0]); + return -1; + } + + /** + * Instance creation. + */ + VkInstanceCreateInfo instanceCreateInfo = {}; + instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + + result = vkCreateInstance(&instanceCreateInfo, NULL, &instance); + if (unlikely(result != VK_SUCCESS)) { + fprintf(stderr, "Failed to create instance (%d).\n", result); + return -1; + } + + /** + * Device creation. + */ + /* Get number of devices. */ + result = vkEnumeratePhysicalDevices(instance, &device_count, NULL); + fprintf(stderr, "Number of devices: %d\n", device_count); + + physical_devices = malloc(sizeof(*physical_devices) * device_count); + + /* Get physical devices. */ + result = vkEnumeratePhysicalDevices(instance, &device_count, + physical_devices); + if (unlikely(result != VK_SUCCESS)) { + fprintf(stderr, "Failed to enumerate physical devices (%d).\n", result); + ret = -1; + goto fail_device; + } + + VkPhysicalDeviceProperties device_properties; + vkGetPhysicalDeviceProperties(physical_devices[0], &device_properties); + fprintf(stderr, "GPU: %s\n", device_properties.deviceName); + + /* Get queue properties. */ + vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[0], &queue_count, + &queue_family); + assert(queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT); + + /* Create logical device. */ + VkDeviceQueueCreateInfo queueCreateInfo = {}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = 0; + queueCreateInfo.queueCount = 1; + VkDeviceCreateInfo deviceCreateInfo = {}; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.queueCreateInfoCount = 1; + deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; + deviceCreateInfo.enabledExtensionCount = 1; + deviceCreateInfo.ppEnabledExtensionNames = extensionNames; + + result = vkCreateDevice(physical_devices[0], &deviceCreateInfo, + NULL, &device); + if (unlikely(result != VK_SUCCESS)) { + if (result == VK_ERROR_EXTENSION_NOT_PRESENT) + fprintf(stderr, "VK_AMD_shader_info is required!\n"); + fprintf(stderr, "Failed to create device (%d).\n", result); + ret = -1; + goto fail_device; + } + + vkGetShaderInfo = + (PFN_vkGetShaderInfoAMD)vkGetDeviceProcAddr(device, + "vkGetShaderInfoAMD"); + + /** + * Runner. + */ + /* Gather all pipeline tests. */ + pipeline_test = malloc(pipeline_test_size * sizeof(struct pipeline_test)); + for (int i = 1; i < argc; i++) { + ftw(argv[i], gather_pipeline_test, 100); + } + + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start); + + /* Process each pipeline tests. */ + for (unsigned i = 0; i < pipeline_test_length; i++) { + const char *current_pipeline_name = pipeline_test[i].filename; + off_t filesize = pipeline_test[i].filesize; + char *data; + int fd; + + fprintf(stderr, "--> %s\n", current_pipeline_name); + + fd = open(current_pipeline_name, O_RDONLY); + if (unlikely(fd == -1)) { + perror("open"); + continue; + } + + data = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0); + if (unlikely(data == MAP_FAILED)) { + perror("mmap"); + continue; + } + + if (unlikely(close(fd) == -1)) { + perror("close"); + continue; + } + + if (unlikely(run(device, current_pipeline_name, data, filesize) < 0)) + continue; + + if (unlikely(munmap(data, filesize) == -1)) { + perror("munmap"); + continue; + } + + free(pipeline_test[i].filename); + } + + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end); + printf("Process took %.2lf seconds and compiled %u pipelines\n", + (end.tv_sec - start.tv_sec) + 10e-9 * (end.tv_nsec - start.tv_nsec), + pipeline_test_length); + + free(pipeline_test); + + vkDestroyDevice(device, NULL); +fail_device: + free(physical_devices); + vkDestroyInstance(instance, NULL); + + return ret; +} |