X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fintel%2Fvulkan%2Fanv_dump.c;h=a84bcc9b35ee55444b0b746876019a5a2f88b0d5;hb=ac7eeebce4ae9107863623321b74b1c08389f180;hp=b7fa28be78779b48e1424f898d592d82f96cf6c0;hpb=3fd308a357b9057447d21c67718d13524dbbb40d;p=mesa.git diff --git a/src/intel/vulkan/anv_dump.c b/src/intel/vulkan/anv_dump.c index b7fa28be787..a84bcc9b35e 100644 --- a/src/intel/vulkan/anv_dump.c +++ b/src/intel/vulkan/anv_dump.c @@ -23,94 +23,137 @@ #include "anv_private.h" +#include "util/list.h" +#include "util/ralloc.h" + /* This file contains utility functions for help debugging. They can be * called from GDB or similar to help inspect images and buffers. + * + * To dump the framebuffers of an application after each render pass, all you + * have to do is the following + * + * 1) Start the application in GDB + * 2) Run until you get to the point where the rendering errors occur + * 3) Pause in GDB and set a breakpoint in anv_QueuePresentKHR + * 4) Continue until it reaches anv_QueuePresentKHR + * 5) Call anv_dump_start(queue->device, ANV_DUMP_FRAMEBUFFERS_BIT) + * 6) Continue until the next anv_QueuePresentKHR call + * 7) Call anv_dump_finish() to complete the dump and write files + * + * While it's a bit manual, the process does allow you to do some very + * valuable debugging by dumping every render target at the end of every + * render pass. It's worth noting that this assumes that the application + * creates all of the command buffers more-or-less in-order and between the + * two anv_QueuePresentKHR calls. */ -void -anv_dump_image_to_ppm(struct anv_device *device, - struct anv_image *image, unsigned miplevel, - unsigned array_layer, const char *filename) +struct dump_image { + struct list_head link; + + const char *filename; + + VkExtent2D extent; + VkImage image; + VkDeviceMemory memory; +}; + +static void +dump_image_init(struct anv_device *device, struct dump_image *image, + uint32_t width, uint32_t height, const char *filename) { VkDevice vk_device = anv_device_to_handle(device); - VkResult result; + MAYBE_UNUSED VkResult result; - VkExtent2D extent = { image->extent.width, image->extent.height }; - for (unsigned i = 0; i < miplevel; i++) { - extent.width = MAX2(1, extent.width / 2); - extent.height = MAX2(1, extent.height / 2); - } + image->filename = filename; + image->extent = (VkExtent2D) { width, height }; - VkImage copy_image; result = anv_CreateImage(vk_device, &(VkImageCreateInfo) { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = VK_FORMAT_R8G8B8A8_UNORM, - .extent = (VkExtent3D) { extent.width, extent.height, 1 }, + .extent = (VkExtent3D) { width, height, 1 }, .mipLevels = 1, .arrayLayers = 1, .samples = 1, .tiling = VK_IMAGE_TILING_LINEAR, .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT, .flags = 0, - }, NULL, ©_image); + }, NULL, &image->image); assert(result == VK_SUCCESS); VkMemoryRequirements reqs; - anv_GetImageMemoryRequirements(vk_device, copy_image, &reqs); + anv_GetImageMemoryRequirements(vk_device, image->image, &reqs); - VkDeviceMemory memory; result = anv_AllocateMemory(vk_device, &(VkMemoryAllocateInfo) { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = reqs.size, .memoryTypeIndex = 0, - }, NULL, &memory); + }, NULL, &image->memory); assert(result == VK_SUCCESS); - result = anv_BindImageMemory(vk_device, copy_image, memory, 0); + result = anv_BindImageMemory(vk_device, image->image, image->memory, 0); assert(result == VK_SUCCESS); +} - VkCommandPool commandPool; - result = anv_CreateCommandPool(vk_device, - &(VkCommandPoolCreateInfo) { - .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - .queueFamilyIndex = 0, - .flags = 0, - }, NULL, &commandPool); - assert(result == VK_SUCCESS); +static void +dump_image_finish(struct anv_device *device, struct dump_image *image) +{ + VkDevice vk_device = anv_device_to_handle(device); - VkCommandBuffer cmd; - result = anv_AllocateCommandBuffers(vk_device, - &(VkCommandBufferAllocateInfo) { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .commandPool = commandPool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandBufferCount = 1, - }, &cmd); - assert(result == VK_SUCCESS); + anv_DestroyImage(vk_device, image->image, NULL); + anv_FreeMemory(vk_device, image->memory, NULL); +} - result = anv_BeginCommandBuffer(cmd, - &(VkCommandBufferBeginInfo) { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, +static void +dump_image_do_blit(struct anv_device *device, struct dump_image *image, + struct anv_cmd_buffer *cmd_buffer, struct anv_image *src, + VkImageAspectFlagBits aspect, + unsigned miplevel, unsigned array_layer) +{ + ANV_CALL(CmdPipelineBarrier)(anv_cmd_buffer_to_handle(cmd_buffer), + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, 0, NULL, 0, NULL, 1, + &(VkImageMemoryBarrier) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = ~0, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = 0, + .dstQueueFamilyIndex = 0, + .image = anv_image_to_handle(src), + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = aspect, + .baseMipLevel = miplevel, + .levelCount = 1, + .baseArrayLayer = array_layer, + .layerCount = 1, + }, }); - assert(result == VK_SUCCESS); - anv_CmdBlitImage(cmd, - anv_image_to_handle(image), VK_IMAGE_LAYOUT_GENERAL, - copy_image, VK_IMAGE_LAYOUT_GENERAL, 1, + /* We need to do a blit so the image needs to be declared as sampled. The + * only thing these are used for is making sure we create the correct + * views, so it should be find to just stomp it and set it back. + */ + VkImageUsageFlags old_usage = src->usage; + src->usage |= VK_IMAGE_USAGE_SAMPLED_BIT; + + anv_CmdBlitImage(anv_cmd_buffer_to_handle(cmd_buffer), + anv_image_to_handle(src), VK_IMAGE_LAYOUT_GENERAL, + image->image, VK_IMAGE_LAYOUT_GENERAL, 1, &(VkImageBlit) { .srcSubresource = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .aspectMask = aspect, .mipLevel = miplevel, .baseArrayLayer = array_layer, .layerCount = 1, }, .srcOffsets = { { 0, 0, 0 }, - { extent.width, extent.height, 1 }, + { image->extent.width, image->extent.height, 1 }, }, .dstSubresource = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, @@ -120,11 +163,13 @@ anv_dump_image_to_ppm(struct anv_device *device, }, .dstOffsets = { { 0, 0, 0 }, - { extent.width, extent.height, 1 }, + { image->extent.width, image->extent.height, 1 }, }, }, VK_FILTER_NEAREST); - ANV_CALL(CmdPipelineBarrier)(cmd, + src->usage = old_usage; + + ANV_CALL(CmdPipelineBarrier)(anv_cmd_buffer_to_handle(cmd_buffer), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, true, 0, NULL, 0, NULL, 1, @@ -136,7 +181,7 @@ anv_dump_image_to_ppm(struct anv_device *device, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = 0, .dstQueueFamilyIndex = 0, - .image = copy_image, + .image = image->image, .subresourceRange = (VkImageSubresourceRange) { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, @@ -145,6 +190,97 @@ anv_dump_image_to_ppm(struct anv_device *device, .layerCount = 1, }, }); +} + +static void +dump_image_write_to_ppm(struct anv_device *device, struct dump_image *image) +{ + VkDevice vk_device = anv_device_to_handle(device); + MAYBE_UNUSED VkResult result; + + VkMemoryRequirements reqs; + anv_GetImageMemoryRequirements(vk_device, image->image, &reqs); + + uint8_t *map; + result = anv_MapMemory(vk_device, image->memory, 0, reqs.size, 0, (void **)&map); + assert(result == VK_SUCCESS); + + VkSubresourceLayout layout; + anv_GetImageSubresourceLayout(vk_device, image->image, + &(VkImageSubresource) { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .arrayLayer = 0, + }, &layout); + + map += layout.offset; + + FILE *file = fopen(image->filename, "wb"); + assert(file); + + uint8_t *row = malloc(image->extent.width * 3); + assert(row); + + fprintf(file, "P6\n%d %d\n255\n", image->extent.width, image->extent.height); + for (unsigned y = 0; y < image->extent.height; y++) { + for (unsigned x = 0; x < image->extent.width; x++) { + row[x * 3 + 0] = map[x * 4 + 0]; + row[x * 3 + 1] = map[x * 4 + 1]; + row[x * 3 + 2] = map[x * 4 + 2]; + } + fwrite(row, 3, image->extent.width, file); + + map += layout.rowPitch; + } + free(row); + fclose(file); + + anv_UnmapMemory(vk_device, image->memory); +} + +void +anv_dump_image_to_ppm(struct anv_device *device, + struct anv_image *image, unsigned miplevel, + unsigned array_layer, VkImageAspectFlagBits aspect, + const char *filename) +{ + VkDevice vk_device = anv_device_to_handle(device); + MAYBE_UNUSED VkResult result; + + const uint32_t width = anv_minify(image->extent.width, miplevel); + const uint32_t height = anv_minify(image->extent.height, miplevel); + + struct dump_image dump; + dump_image_init(device, &dump, width, height, filename); + + VkCommandPool commandPool; + result = anv_CreateCommandPool(vk_device, + &(VkCommandPoolCreateInfo) { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .queueFamilyIndex = 0, + .flags = 0, + }, NULL, &commandPool); + assert(result == VK_SUCCESS); + + VkCommandBuffer cmd; + result = anv_AllocateCommandBuffers(vk_device, + &(VkCommandBufferAllocateInfo) { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = commandPool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }, &cmd); + assert(result == VK_SUCCESS); + + result = anv_BeginCommandBuffer(cmd, + &(VkCommandBufferBeginInfo) { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }); + assert(result == VK_SUCCESS); + + dump_image_do_blit(device, &dump, anv_cmd_buffer_from_handle(cmd), image, + aspect, miplevel, array_layer); result = anv_EndCommandBuffer(cmd); assert(result == VK_SUCCESS); @@ -171,39 +307,128 @@ anv_dump_image_to_ppm(struct anv_device *device, anv_DestroyFence(vk_device, fence, NULL); anv_DestroyCommandPool(vk_device, commandPool, NULL); - uint8_t *map; - result = anv_MapMemory(vk_device, memory, 0, reqs.size, 0, (void **)&map); - assert(result == VK_SUCCESS); + dump_image_write_to_ppm(device, &dump); + dump_image_finish(device, &dump); +} - VkSubresourceLayout layout; - anv_GetImageSubresourceLayout(vk_device, copy_image, - &(VkImageSubresource) { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .mipLevel = 0, - .arrayLayer = 0, - }, &layout); +static pthread_mutex_t dump_mutex = PTHREAD_MUTEX_INITIALIZER; - map += layout.offset; +static enum anv_dump_action dump_actions = 0; - /* Now we can finally write the PPM file */ - FILE *file = fopen(filename, "wb"); - assert(file); +/* Used to prevent recursive dumping */ +static enum anv_dump_action dump_old_actions; - fprintf(file, "P6\n%d %d\n255\n", extent.width, extent.height); - for (unsigned y = 0; y < extent.height; y++) { - uint8_t row[extent.width * 3]; - for (unsigned x = 0; x < extent.width; x++) { - row[x * 3 + 0] = map[x * 4 + 0]; - row[x * 3 + 1] = map[x * 4 + 1]; - row[x * 3 + 2] = map[x * 4 + 2]; - } - fwrite(row, 3, extent.width, file); +struct list_head dump_list; +static void *dump_ctx; +static struct anv_device *dump_device; +static unsigned dump_count; - map += layout.rowPitch; +void +anv_dump_start(struct anv_device *device, enum anv_dump_action actions) +{ + pthread_mutex_lock(&dump_mutex); + + dump_device = device; + dump_actions = actions; + list_inithead(&dump_list); + dump_ctx = ralloc_context(NULL); + dump_count = 0; + + pthread_mutex_unlock(&dump_mutex); +} + +void +anv_dump_finish() +{ + anv_DeviceWaitIdle(anv_device_to_handle(dump_device)); + + pthread_mutex_lock(&dump_mutex); + + list_for_each_entry(struct dump_image, dump, &dump_list, link) { + dump_image_write_to_ppm(dump_device, dump); + dump_image_finish(dump_device, dump); + } + + dump_actions = 0; + dump_device = NULL; + list_inithead(&dump_list); + + ralloc_free(dump_ctx); + dump_ctx = NULL; + + pthread_mutex_unlock(&dump_mutex); +} + +static bool +dump_lock(enum anv_dump_action action) +{ + if (likely((dump_actions & action) == 0)) + return false; + + pthread_mutex_lock(&dump_mutex); + + /* Prevent recursive dumping */ + dump_old_actions = dump_actions; + dump_actions = 0; + + return true; +} + +static void +dump_unlock() +{ + dump_actions = dump_old_actions; + pthread_mutex_unlock(&dump_mutex); +} + +static void +dump_add_image(struct anv_cmd_buffer *cmd_buffer, struct anv_image *image, + VkImageAspectFlagBits aspect, + unsigned miplevel, unsigned array_layer, const char *filename) +{ + const uint32_t width = anv_minify(image->extent.width, miplevel); + const uint32_t height = anv_minify(image->extent.height, miplevel); + + struct dump_image *dump = ralloc(dump_ctx, struct dump_image); + + dump_image_init(cmd_buffer->device, dump, width, height, filename); + dump_image_do_blit(cmd_buffer->device, dump, cmd_buffer, image, + aspect, miplevel, array_layer); + + list_addtail(&dump->link, &dump_list); +} + +void +anv_dump_add_framebuffer(struct anv_cmd_buffer *cmd_buffer, + struct anv_framebuffer *fb) +{ + if (!dump_lock(ANV_DUMP_FRAMEBUFFERS_BIT)) + return; + + unsigned dump_idx = dump_count++; + + for (unsigned i = 0; i < fb->attachment_count; i++) { + struct anv_image_view *iview = fb->attachments[i]; + + uint32_t b; + for_each_bit(b, iview->image->aspects) { + VkImageAspectFlagBits aspect = (1 << b); + char suffix; + switch (aspect) { + case VK_IMAGE_ASPECT_COLOR_BIT: suffix = 'c'; break; + case VK_IMAGE_ASPECT_DEPTH_BIT: suffix = 'd'; break; + case VK_IMAGE_ASPECT_STENCIL_BIT: suffix = 's'; break; + default: + unreachable("Invalid aspect"); + } + + char *filename = ralloc_asprintf(dump_ctx, "framebuffer%04d-%d%c.ppm", + dump_idx, i, suffix); + + dump_add_image(cmd_buffer, (struct anv_image *)iview->image, aspect, + iview->base_mip, iview->base_layer, filename); + } } - fclose(file); - anv_UnmapMemory(vk_device, memory); - anv_DestroyImage(vk_device, copy_image, NULL); - anv_FreeMemory(vk_device, memory, NULL); + dump_unlock(); }