summaryrefslogtreecommitdiff
path: root/run.c
diff options
context:
space:
mode:
authorSamuel Pitoiset <samuel.pitoiset@gmail.com>2018-04-06 10:23:56 +0200
committerSamuel Pitoiset <samuel.pitoiset@gmail.com>2018-04-06 10:23:56 +0200
commitacee7b888285c635f17b36cd13f8a6b6065b88e2 (patch)
tree88104e0ffc430b031acbe9fcf7b28ddf4b828488 /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.c539
1 files changed, 539 insertions, 0 deletions
diff --git a/run.c b/run.c
new file mode 100644
index 0000000..3448895
--- /dev/null
+++ b/run.c
@@ -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;
+}