VkResult VKAPI_CALL vkAllocateCommandBuffers(
    VkDevice                                    device,
    const VkCommandBufferAllocateInfo*          pAllocateInfo,
    VkCommandBuffer*                            pCommandBuffers)
{
    dispatch_key key = get_dispatch_key(device);
    layer_data *my_data = get_my_data_ptr(key, layer_data_map);
    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;
    VkResult result;
    startReadObject(my_data, device);
    startWriteObject(my_data, pAllocateInfo->commandPool);

    result = pTable->AllocateCommandBuffers(device, pAllocateInfo, pCommandBuffers);
    finishReadObject(my_data, device);
    finishWriteObject(my_data, pAllocateInfo->commandPool);

    // Record mapping from command buffer to command pool
    if (VK_SUCCESS == result) {
        for (int index=0;index<pAllocateInfo->commandBufferCount;index++) {
            loader_platform_thread_lock_mutex(&threadingLock);
            command_pool_map[pCommandBuffers[index]] = pAllocateInfo->commandPool;
            loader_platform_thread_unlock_mutex(&threadingLock);
        }
    }

    return result;
}
/* Various dispatchable objects will use the same underlying dispatch table if
 * they
 * are created from that "parent" object. Thus use pointer to dispatch table
 * as the key to these table maps.
 *    Instance -> PhysicalDevice
 *    Device -> CommandBuffer or Queue
 * If use the object themselves as key to map then implies Create entrypoints
 * have to be intercepted
 * and a new key inserted into map */
VkLayerDebugMarkerDispatchTable *initDebugMarkerTable(VkDevice device) {
    VkLayerDebugMarkerDispatchTable *pDebugMarkerTable;

    assert(device);
    VkLayerDispatchTable *pDisp = *(VkLayerDispatchTable **)device;

    std::unordered_map<void *,
                       VkLayerDebugMarkerDispatchTable *>::const_iterator it =
        tableDebugMarkerMap.find((void *)pDisp);
    if (it == tableDebugMarkerMap.end()) {
        pDebugMarkerTable = new VkLayerDebugMarkerDispatchTable;
        tableDebugMarkerMap[(void *)pDisp] = pDebugMarkerTable;
    } else {
        return it->second;
    }

    pDebugMarkerTable->CmdDbgMarkerBegin =
        (PFN_vkCmdDbgMarkerBegin)pDisp->GetDeviceProcAddr(
            device, "vkCmdDbgMarkerBegin");
    pDebugMarkerTable->CmdDbgMarkerEnd =
        (PFN_vkCmdDbgMarkerEnd)pDisp->GetDeviceProcAddr(device,
                                                        "vkCmdDbgMarkerEnd");
    pDebugMarkerTable->DbgSetObjectTag =
        (PFN_vkDbgSetObjectTag)pDisp->GetDeviceProcAddr(device,
                                                        "vkDbgSetObjectTag");
    pDebugMarkerTable->DbgSetObjectName =
        (PFN_vkDbgSetObjectName)pDisp->GetDeviceProcAddr(device,
                                                         "vkDbgSetObjectName");

    return pDebugMarkerTable;
}
예제 #3
0
VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
vkGetDeviceProcAddr(VkDevice dev, const char *funcName) {
#define ADD_HOOK(fn)                                                           \
    if (!strncmp(#fn, funcName, sizeof(#fn)))                                  \
    return (PFN_vkVoidFunction)fn

    ADD_HOOK(vkGetDeviceProcAddr);
    ADD_HOOK(vkDestroyDevice);
    ADD_HOOK(vkCreateSwapchainKHR);
    ADD_HOOK(vkGetSwapchainImagesKHR);
    ADD_HOOK(vkQueuePresentKHR);
    ADD_HOOK(vkDestroySwapchainKHR);
    ADD_HOOK(vkQueueSubmit);
#undef ADD_HOOK

    if (dev == NULL)
        return NULL;

    layer_data *dev_data;
    dev_data = get_my_data_ptr(get_dispatch_key(dev), layer_data_map);
    VkLayerDispatchTable *pTable = dev_data->device_dispatch_table;

    if (pTable->GetDeviceProcAddr == NULL)
        return NULL;
    return pTable->GetDeviceProcAddr(dev, funcName);
}
void VKAPI_CALL vkFreeCommandBuffers(
    VkDevice                                    device,
    VkCommandPool                               commandPool,
    uint32_t                                    commandBufferCount,
    const VkCommandBuffer*                      pCommandBuffers)
{
    dispatch_key key = get_dispatch_key(device);
    layer_data *my_data = get_my_data_ptr(key, layer_data_map);
    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;
    const bool lockCommandPool = false; // pool is already directly locked
    startReadObject(my_data, device);
    startWriteObject(my_data, commandPool);
    for (int index=0;index<commandBufferCount;index++) {
        startWriteObject(my_data, pCommandBuffers[index], lockCommandPool);
    }

    pTable->FreeCommandBuffers(device,commandPool,commandBufferCount,pCommandBuffers);
    finishReadObject(my_data, device);
    finishWriteObject(my_data, commandPool);
    for (int index=0;index<commandBufferCount;index++) {
        finishWriteObject(my_data, pCommandBuffers[index], lockCommandPool);
        loader_platform_thread_lock_mutex(&threadingLock);
        command_pool_map.erase(pCommandBuffers[index]);
        loader_platform_thread_unlock_mutex(&threadingLock);
    }
}
예제 #5
0
VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits,
                                                             VkFence fence) {
    layer_data *my_data = GetLayerDataPtr(get_dispatch_key(queue), layer_data_map);
    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;

    my_data->cmdBuffersThisFrame += submitCount;  // XXX WRONG

    return pTable->QueueSubmit(queue, submitCount, pSubmits, fence);
}
예제 #6
0
VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) {
    dispatch_key key = get_dispatch_key(device);
    layer_data *my_data = GetLayerDataPtr(key, layer_data_map);
    my_data->Cleanup();
    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;
    pTable->DeviceWaitIdle(device);
    pTable->DestroyDevice(device, pAllocator);
    delete pTable;
    layer_data_map.erase(key);
}
예제 #7
0
void WsiImageData::Cleanup(VkDevice dev) {
    layer_data *my_data =
        get_my_data_ptr(get_dispatch_key(dev), layer_data_map);
    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;

    // XXX: needs device data
    //    pTable->FreeCommandBuffers(dev, cmd, nullptr);
    pTable->DestroyFramebuffer(dev, framebuffer, nullptr);
    pTable->DestroyImageView(dev, view, nullptr);
    pTable->DestroyBuffer(dev, vertexBuffer, nullptr);
    pTable->FreeMemory(dev, vertexBufferMemory, nullptr);
}
예제 #8
0
void SwapChainData::Cleanup(VkDevice dev) {
    layer_data *my_data = GetLayerDataPtr(get_dispatch_key(dev), layer_data_map);
    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;

    for (uint32_t i = 0; i < presentableImages.size(); i++) {
        presentableImages[i]->Cleanup(dev);
        delete presentableImages[i];
    }

    presentableImages.clear();

    pTable->DestroyPipeline(dev, pipeline, nullptr);
    pTable->DestroyRenderPass(dev, render_pass, nullptr);
}
예제 #9
0
VKAPI_ATTR void VKAPI_CALL
DestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) {
    DeviceMapStruct *devMap = get_dev_info(device);
    assert(devMap);
    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
    pDisp->DestroyDevice(device, pAllocator);

    loader_platform_thread_lock_mutex(&globalLock);
    delete pDisp;
    delete devMap;

    deviceMap.erase(device);
    loader_platform_thread_unlock_mutex(&globalLock);
}
예제 #10
0
VKAPI_ATTR VkResult VKAPI_CALL
GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain,
                      uint32_t *pCount, VkImage *pSwapchainImages) {
    DeviceMapStruct *devMap = get_dev_info(device);
    assert(devMap);
    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
    VkResult result = pDisp->GetSwapchainImagesKHR(device, swapchain, pCount,
                                                   pSwapchainImages);

    // Save the swapchain images in a map if we are taking screenshots
    loader_platform_thread_lock_mutex(&globalLock);
    if (screenshotEnvQueried && screenshotFrames.empty()) {
        // No screenshots in the list to take
        loader_platform_thread_unlock_mutex(&globalLock);
        return result;
    }

    if (result == VK_SUCCESS && pSwapchainImages && !swapchainMap.empty() &&
        swapchainMap.find(swapchain) != swapchainMap.end()) {
        unsigned i;

        for (i = 0; i < *pCount; i++) {
            // Create a mapping for an image to a device, image extent, and
            // format
            if (imageMap[pSwapchainImages[i]] == NULL) {
                ImageMapStruct *imageMapElem = new ImageMapStruct;
                imageMap[pSwapchainImages[i]] = imageMapElem;
            }
            imageMap[pSwapchainImages[i]]->device =
                swapchainMap[swapchain]->device;
            imageMap[pSwapchainImages[i]]->imageExtent =
                swapchainMap[swapchain]->imageExtent;
            imageMap[pSwapchainImages[i]]->format =
                swapchainMap[swapchain]->format;
        }

        // Add list of images to swapchain to image map
        SwapchainMapStruct *swapchainMapElem = swapchainMap[swapchain];
        if (i >= 1 && swapchainMapElem) {
            VkImage *imageList = new VkImage[i];
            swapchainMapElem->imageList = imageList;
            for (unsigned j = 0; j < i; j++) {
                swapchainMapElem->imageList[j] = pSwapchainImages[j];
            }
        }
    }
    loader_platform_thread_unlock_mutex(&globalLock);
    return result;
}
예제 #11
0
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetDeviceProcAddr(VkDevice device, const char *funcName) {
    PFN_vkVoidFunction addr;
    layer_data *dev_data;

    assert(device);

    addr = layer_intercept_proc(funcName);
    if (addr)
        return addr;

    dev_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
    VkLayerDispatchTable *pTable = dev_data->device_dispatch_table;

    if (pTable->GetDeviceProcAddr == NULL)
        return NULL;
    return pTable->GetDeviceProcAddr(device, funcName);
}
VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice device, const char *funcName) {
    PFN_vkVoidFunction addr;
    layer_data *dev_data;
    if (device == VK_NULL_HANDLE) {
        return NULL;
    }

    addr = layer_intercept_proc(funcName);
    if (addr)
        return addr;

    dev_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
    VkLayerDispatchTable *pTable = dev_data->device_dispatch_table;

    if (pTable->GetDeviceProcAddr == NULL)
        return NULL;
    return pTable->GetDeviceProcAddr(device, funcName);
}
예제 #13
0
VKAPI_ATTR VkResult VKAPI_CALL CreateSwapchainKHR(
    VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo,
    const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain) {

    DeviceMapStruct *devMap = get_dev_info(device);
    assert(devMap);
    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;

    // This layer does an image copy later on, and the copy command expects the
    // transfer src bit to be on.
    VkSwapchainCreateInfoKHR myCreateInfo = *pCreateInfo;
    myCreateInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
    VkResult result = pDisp->CreateSwapchainKHR(device, &myCreateInfo,
                                                pAllocator, pSwapchain);

    // Save the swapchain in a map of we are taking screenshots.
    loader_platform_thread_lock_mutex(&globalLock);
    if (screenshotEnvQueried && screenshotFrames.empty()) {
        // No screenshots in the list to take
        loader_platform_thread_unlock_mutex(&globalLock);
        return result;
    }

    if (result == VK_SUCCESS) {
        // Create a mapping for a swapchain to a device, image extent, and
        // format
        SwapchainMapStruct *swapchainMapElem = new SwapchainMapStruct;
        swapchainMapElem->device = device;
        swapchainMapElem->imageExtent = pCreateInfo->imageExtent;
        swapchainMapElem->format = pCreateInfo->imageFormat;
        swapchainMap.insert(make_pair(*pSwapchain, swapchainMapElem));

        // Create a mapping for the swapchain object into the dispatch table
        // TODO is this needed? screenshot_device_table_map.emplace((void
        // *)pSwapchain, pTable);
    }
    loader_platform_thread_unlock_mutex(&globalLock);

    return result;
}
예제 #14
0
VKAPI_ATTR VkResult VKAPI_CALL CreateCommandPool(
    VkDevice device, const VkCommandPoolCreateInfo *pCreateInfo,
    const VkAllocationCallbacks *pAllocator, VkCommandPool *pCommandPool) {
    DeviceMapStruct *devMap = get_dev_info(device);
    assert(devMap);
    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
    VkResult result =
        pDisp->CreateCommandPool(device, pCreateInfo, pAllocator, pCommandPool);

    // Save the command pool on a map if we are taking screenshots.
    loader_platform_thread_lock_mutex(&globalLock);
    if (screenshotEnvQueried && screenshotFrames.empty()) {
        // No screenshots in the list to take
        loader_platform_thread_unlock_mutex(&globalLock);
        return result;
    }

    // Create a mapping from a device to a commandPool
    devMap->commandPool = *pCommandPool;
    loader_platform_thread_unlock_mutex(&globalLock);
    return result;
}
예제 #15
0
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
GetDeviceProcAddr(VkDevice dev, const char *funcName) {
    PFN_vkVoidFunction proc = intercept_core_device_command(funcName);
    if (proc)
        return proc;

    if (dev == NULL) {
        return NULL;
    }

    proc = intercept_khr_swapchain_command(funcName, dev);
    if (proc)
        return proc;

    DeviceMapStruct *devMap = get_dev_info(dev);
    assert(devMap);
    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;

    if (pDisp->GetDeviceProcAddr == NULL)
        return NULL;
    return pDisp->GetDeviceProcAddr(dev, funcName);
}
예제 #16
0
VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice dev, const char *funcName) {
    if (!strcmp(funcName, "vkGetDeviceProcAddr"))
        return (PFN_vkVoidFunction)vkGetDeviceProcAddr;
    if (!strcmp(funcName, "vkDestroyDevice"))
        return (PFN_vkVoidFunction)vkDestroyDevice;
    if (!strcmp(funcName, "vkGetDeviceQueue"))
        return (PFN_vkVoidFunction)vkGetDeviceQueue;
    if (!strcmp(funcName, "vkCreateRenderPass"))
        return (PFN_vkVoidFunction)vkCreateRenderPass;
    if (!strcmp(funcName, "vkCreateCommandPool"))
        return (PFN_vkVoidFunction)vkCreateCommandPool;
    if (!strcmp(funcName, "vkDestroyCommandPool"))
        return (PFN_vkVoidFunction)vkDestroyCommandPool;
    if (!strcmp(funcName, "vkResetCommandPool"))
        return (PFN_vkVoidFunction)vkResetCommandPool;
    if (!strcmp(funcName, "vkAllocateCommandBuffers"))
        return (PFN_vkVoidFunction)vkAllocateCommandBuffers;
    if (!strcmp(funcName, "vkFreeCommandBuffers"))
        return (PFN_vkVoidFunction)vkFreeCommandBuffers;
    if (!strcmp(funcName, "vkBeginCommandBuffer"))
        return (PFN_vkVoidFunction)vkBeginCommandBuffer;
    if (!strcmp(funcName, "vkCmdUpdateBuffer"))
        return (PFN_vkVoidFunction)vkCmdUpdateBuffer;
    if (!strcmp(funcName, "vkUpdateDescriptorSets"))
        return (PFN_vkVoidFunction)vkUpdateDescriptorSets;
    if (!strcmp(funcName, "vkCmdFillBuffer"))
        return (PFN_vkVoidFunction)vkCmdFillBuffer;

    if (dev == NULL)
        return NULL;

    layer_data *my_data = get_my_data_ptr(get_dispatch_key(dev), layer_data_map);
    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;
    {
        if (pTable->GetDeviceProcAddr == NULL)
            return NULL;
        return pTable->GetDeviceProcAddr(dev, funcName);
    }
}
예제 #17
0
VKAPI_ATTR VkResult VKAPI_CALL
AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo *pAllocateInfo, VkCommandBuffer *pCommandBuffers) {
    dispatch_key key = get_dispatch_key(device);
    layer_data *my_data = get_my_data_ptr(key, layer_data_map);
    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;
    VkResult result;
    startReadObject(my_data, device);
    startWriteObject(my_data, pAllocateInfo->commandPool);

    result = pTable->AllocateCommandBuffers(device, pAllocateInfo, pCommandBuffers);
    finishReadObject(my_data, device);
    finishWriteObject(my_data, pAllocateInfo->commandPool);

    // Record mapping from command buffer to command pool
    if (VK_SUCCESS == result) {
        for (uint32_t index = 0; index < pAllocateInfo->commandBufferCount; index++) {
            std::lock_guard<std::mutex> lock(global_lock);
            command_pool_map[pCommandBuffers[index]] = pAllocateInfo->commandPool;
        }
    }

    return result;
}
예제 #18
0
VKAPI_ATTR void VKAPI_CALL GetDeviceQueue(VkDevice device,
                                          uint32_t queueNodeIndex,
                                          uint32_t queueIndex,
                                          VkQueue *pQueue) {
    DeviceMapStruct *devMap = get_dev_info(device);
    assert(devMap);
    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
    pDisp->GetDeviceQueue(device, queueNodeIndex, queueIndex, pQueue);

    // Save the device queue in a map if we are taking screenshots.
    loader_platform_thread_lock_mutex(&globalLock);
    if (screenshotEnvQueried && screenshotFrames.empty()) {
        // No screenshots in the list to take
        loader_platform_thread_unlock_mutex(&globalLock);
        return;
    }

    VkDevice que = static_cast<VkDevice>(static_cast<void *>(*pQueue));
    deviceMap.emplace(que, devMap);

    // Create a mapping from a device to a queue
    devMap->queue = *pQueue;
    loader_platform_thread_unlock_mutex(&globalLock);
}
예제 #19
0
VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapChain, uint32_t *pCount,
                                                                       VkImage *pImages) {
    layer_data *my_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map);
    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;
    VkResult result = my_data->pfnGetSwapchainImagesKHR(device, swapChain, pCount, pImages);
    VkResult U_ASSERT_ONLY err;

    /* GetSwapChainImagesWSI may be called without an images buffer, in which
     * case it
     * just returns the count to the caller. We're only interested in acting on
     * the
     * /actual/ fetch of the images.
     */
    if (pImages) {
        auto data = (*my_data->swapChains)[swapChain];

        for (uint32_t i = 0; i < *pCount; i++) {
            /* Create attachment view for each */
            VkImageViewCreateInfo ivci;
            ivci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
            ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
            ivci.pNext = nullptr;
            ivci.format = data->format;
            ivci.components.r = VK_COMPONENT_SWIZZLE_R;
            ivci.components.g = VK_COMPONENT_SWIZZLE_G;
            ivci.components.b = VK_COMPONENT_SWIZZLE_B;
            ivci.components.a = VK_COMPONENT_SWIZZLE_A;
            ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
            ivci.subresourceRange.baseMipLevel = 0;
            ivci.subresourceRange.levelCount = 1;
            ivci.subresourceRange.baseArrayLayer = 0;
            ivci.subresourceRange.layerCount = 1;
            ivci.image = pImages[i];
            ivci.flags = 0;

            VkImageView v;
            pTable->CreateImageView(device, &ivci, nullptr, &v);

            /* Create framebuffer for each */
            VkFramebufferCreateInfo fci;
            fci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
            fci.pNext = nullptr;
            fci.flags = 0;
            fci.renderPass = data->render_pass;
            fci.attachmentCount = 1;
            fci.pAttachments = &v;
            fci.width = data->width;
            fci.height = data->height;
            fci.layers = 1;

            VkFramebuffer fb;
            pTable->CreateFramebuffer(device, &fci, nullptr, &fb);

            /* Create command buffer for each */
            VkCommandBufferAllocateInfo cbai;
            cbai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
            cbai.pNext = nullptr;
            cbai.commandPool = my_data->pool;
            cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
            cbai.commandBufferCount = 1;

            VkCommandBuffer cmd;
            pTable->AllocateCommandBuffers(device, &cbai, &cmd);

            /* We have just created a dispatchable object, but the dispatch
             * table has not been placed in the object yet.
             * When a "normal" application creates a command buffer,
             * the dispatch table is installed by the top-level binding
             * (trampoline.c).
             * But here, we have to do it ourselves. */

            if (!my_data->pfn_dev_init) {
                *((const void **)cmd) = *(void **)device;
            } else {
                err = my_data->pfn_dev_init(device, (void *)cmd);
                assert(!err);
            }

            /* Create vertex buffer */
            VkBufferCreateInfo bci;
            memset(&bci, 0, sizeof(bci));
            bci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
            bci.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
            bci.size = sizeof(vertex) * MAX_TEXT_VERTICES;

            VkBuffer buf;
            err = pTable->CreateBuffer(device, &bci, nullptr, &buf);
            assert(!err);

            VkMemoryRequirements mem_reqs;
            pTable->GetBufferMemoryRequirements(device, buf, &mem_reqs);
            assert(!err);

            VkMemoryAllocateInfo mem_alloc;
            memset(&mem_alloc, 0, sizeof(mem_alloc));
            mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
            mem_alloc.allocationSize = mem_reqs.size;
            mem_alloc.memoryTypeIndex = choose_memory_type(
                my_data->gpu, mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

            VkDeviceMemory mem;
            err = pTable->AllocateMemory(device, &mem_alloc, nullptr, &mem);
            assert(!err);

            err = pTable->BindBufferMemory(device, buf, mem, 0);
            assert(!err);

            auto imageData = new WsiImageData;
            imageData->image = pImages[i];
            imageData->view = v;
            imageData->framebuffer = fb;
            imageData->cmd = cmd;
            imageData->vertexBuffer = buf;
            imageData->vertexBufferMemory = mem;
            imageData->numVertices = 0;
            imageData->vertexBufferSize = mem_alloc.allocationSize;

            data->presentableImages.push_back(imageData);
        }
    }
    return result;
}
예제 #20
0
VkResult WrappedVulkan::vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo)
{
  if(m_State == WRITING_IDLE)
  {
    RenderDoc::Inst().Tick();

    GetResourceManager()->FlushPendingDirty();
  }

  m_FrameCounter++;    // first present becomes frame #1, this function is at the end of the frame

  if(pPresentInfo->swapchainCount > 1 && (m_FrameCounter % 100) == 0)
  {
    RDCWARN("Presenting multiple swapchains at once - only first will be processed");
  }

  vector<VkSwapchainKHR> unwrappedSwaps;
  vector<VkSemaphore> unwrappedSems;

  VkPresentInfoKHR unwrappedInfo = *pPresentInfo;

  for(uint32_t i = 0; i < unwrappedInfo.swapchainCount; i++)
    unwrappedSwaps.push_back(Unwrap(unwrappedInfo.pSwapchains[i]));
  for(uint32_t i = 0; i < unwrappedInfo.waitSemaphoreCount; i++)
    unwrappedSems.push_back(Unwrap(unwrappedInfo.pWaitSemaphores[i]));

  unwrappedInfo.pSwapchains = unwrappedInfo.swapchainCount ? &unwrappedSwaps[0] : NULL;
  unwrappedInfo.pWaitSemaphores = unwrappedInfo.waitSemaphoreCount ? &unwrappedSems[0] : NULL;

  // Don't support any extensions for present info
  RDCASSERT(pPresentInfo->pNext == NULL);

  VkResourceRecord *swaprecord = GetRecord(pPresentInfo->pSwapchains[0]);
  RDCASSERT(swaprecord->swapInfo);

  SwapchainInfo &swapInfo = *swaprecord->swapInfo;

  bool activeWindow = RenderDoc::Inst().IsActiveWindow(LayerDisp(m_Instance), swapInfo.wndHandle);

  // need to record which image was last flipped so we can get the correct backbuffer
  // for a thumbnail in EndFrameCapture
  swapInfo.lastPresent = pPresentInfo->pImageIndices[0];
  m_LastSwap = swaprecord->GetResourceID();

  if(m_State == WRITING_IDLE)
  {
    uint32_t overlay = RenderDoc::Inst().GetOverlayBits();

    if(overlay & eRENDERDOC_Overlay_Enabled)
    {
      VkRenderPass rp = swapInfo.rp;
      VkImage im = swapInfo.images[pPresentInfo->pImageIndices[0]].im;
      VkFramebuffer fb = swapInfo.images[pPresentInfo->pImageIndices[0]].fb;

      VkLayerDispatchTable *vt = ObjDisp(GetDev());

      TextPrintState textstate = {
          GetNextCmd(), rp, fb, swapInfo.extent.width, swapInfo.extent.height, swapInfo.format};

      VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
                                            VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};

      VkResult vkr = vt->BeginCommandBuffer(Unwrap(textstate.cmd), &beginInfo);
      RDCASSERTEQUAL(vkr, VK_SUCCESS);

      VkImageMemoryBarrier bbBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
                                        NULL,
                                        0,
                                        0,
                                        VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
                                        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
                                        VK_QUEUE_FAMILY_IGNORED,
                                        VK_QUEUE_FAMILY_IGNORED,
                                        Unwrap(im),
                                        {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};

      bbBarrier.srcAccessMask = VK_ACCESS_ALL_READ_BITS;
      bbBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

      DoPipelineBarrier(textstate.cmd, 1, &bbBarrier);

      GetDebugManager()->BeginText(textstate);

      int flags = activeWindow ? RenderDoc::eOverlay_ActiveWindow : 0;
      string overlayText = RenderDoc::Inst().GetOverlayText(RDC_Vulkan, m_FrameCounter, flags);

      if(!overlayText.empty())
        GetDebugManager()->RenderText(textstate, 0.0f, 0.0f, overlayText.c_str());

      GetDebugManager()->EndText(textstate);

      std::swap(bbBarrier.oldLayout, bbBarrier.newLayout);
      bbBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
      bbBarrier.dstAccessMask = VK_ACCESS_ALL_READ_BITS;

      DoPipelineBarrier(textstate.cmd, 1, &bbBarrier);

      ObjDisp(textstate.cmd)->EndCommandBuffer(Unwrap(textstate.cmd));

      SubmitCmds();

      FlushQ();
    }
  }

  VkResult vkr = ObjDisp(queue)->QueuePresentKHR(Unwrap(queue), &unwrappedInfo);

  if(!activeWindow)
    return vkr;

  RenderDoc::Inst().SetCurrentDriver(RDC_Vulkan);

  // kill any current capture that isn't application defined
  if(m_State == WRITING_CAPFRAME && !m_AppControlledCapture)
    RenderDoc::Inst().EndFrameCapture(LayerDisp(m_Instance), swapInfo.wndHandle);

  if(RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE)
  {
    RenderDoc::Inst().StartFrameCapture(LayerDisp(m_Instance), swapInfo.wndHandle);

    m_AppControlledCapture = false;
  }

  return vkr;
}
예제 #21
0
// Save an image to a PPM image file.
//
// This function issues commands to copy/convert the swapchain image
// from whatever compatible format the swapchain image uses
// to a single format (VK_FORMAT_R8G8B8A8_UNORM) so that the converted
// result can be easily written to a PPM file.
//
// Error handling: If there is a problem, this function should silently
// fail without affecting the Present operation going on in the caller.
// The numerous debug asserts are to catch programming errors and are not
// expected to assert.  Recovery and clean up are implemented for image memory
// allocation failures.
// (TODO) It would be nice to pass any failure info to DebugReport or something.
static void writePPM(const char *filename, VkImage image1) {

    VkResult err;
    bool pass;

    // Bail immediately if we can't find the image.
    if (imageMap.empty() || imageMap.find(image1) == imageMap.end())
        return;

    // Collect object info from maps.  This info is generally recorded
    // by the other functions hooked in this layer.
    VkDevice device = imageMap[image1]->device;
    VkPhysicalDevice physicalDevice = deviceMap[device]->physicalDevice;
    VkInstance instance = physDeviceMap[physicalDevice]->instance;
    VkQueue queue = deviceMap[device]->queue;
    DeviceMapStruct *devMap = get_dev_info(device);
    if (NULL == devMap) {
        assert(0);
        return;
    }
    VkLayerDispatchTable *pTableDevice = devMap->device_dispatch_table;
    VkLayerDispatchTable *pTableQueue =
        get_dev_info(static_cast<VkDevice>(static_cast<void *>(queue)))
            ->device_dispatch_table;
    VkLayerInstanceDispatchTable *pInstanceTable;
    pInstanceTable = instance_dispatch_table(instance);

    // Gather incoming image info and check image format for compatibility with
    // the target format.
    // This function supports both 24-bit and 32-bit swapchain images.
    VkFormat const target32bitFormat = VK_FORMAT_R8G8B8A8_UNORM;
    VkFormat const target24bitFormat = VK_FORMAT_R8G8B8_UNORM;
    uint32_t const width = imageMap[image1]->imageExtent.width;
    uint32_t const height = imageMap[image1]->imageExtent.height;
    VkFormat const format = imageMap[image1]->format;
    uint32_t const numChannels = vk_format_get_channel_count(format);
    if ((vk_format_get_compatibility_class(target24bitFormat) !=
         vk_format_get_compatibility_class(format)) &&
        (vk_format_get_compatibility_class(target32bitFormat) !=
         vk_format_get_compatibility_class(format))) {
        assert(0);
        return;
    }
    if ((3 != numChannels) && (4 != numChannels)) {
        assert(0);
        return;
    }

    // General Approach
    //
    // The idea here is to copy/convert the swapchain image into another image
    // that can be mapped and read by the CPU to produce a PPM file.
    // The image must be untiled and converted to a specific format for easy
    // parsing.  The memory for the final image must be host-visible.
    // Note that in Vulkan, a BLIT operation must be used to perform a format
    // conversion.
    //
    // Devices vary in their ability to blit to/from linear and optimal tiling.
    // So we must query the device properties to get this information.
    //
    // If the device cannot BLIT to a LINEAR image, then the operation must be
    // done in two steps:
    // 1) BLIT the swapchain image (image1) to a temp image (image2) that is
    // created with TILING_OPTIMAL.
    // 2) COPY image2 to another temp image (image3) that is created with
    // TILING_LINEAR.
    // 3) Map image 3 and write the PPM file.
    //
    // If the device can BLIT to a LINEAR image, then:
    // 1) BLIT the swapchain image (image1) to a temp image (image2) that is
    // created with TILING_LINEAR.
    // 2) Map image 2 and write the PPM file.
    //
    // There seems to be no way to tell if the swapchain image (image1) is tiled
    // or not.  We therefore assume that the BLIT operation can always read from
    // both linear and optimal tiled (swapchain) images.
    // There is therefore no point in looking at the BLIT_SRC properties.
    //
    // There is also the optimization where the incoming and target formats are
    // the same.  In this case, just do a COPY.

    VkFormatProperties targetFormatProps;
    pInstanceTable->GetPhysicalDeviceFormatProperties(
        physicalDevice,
        (3 == numChannels) ? target24bitFormat : target32bitFormat,
        &targetFormatProps);
    bool need2steps = false;
    bool copyOnly = false;
    if ((target24bitFormat == format) || (target32bitFormat == format)) {
        copyOnly = true;
    } else {
        bool const bltLinear = targetFormatProps.linearTilingFeatures &
                                       VK_FORMAT_FEATURE_BLIT_DST_BIT
                                   ? true
                                   : false;
        bool const bltOptimal = targetFormatProps.optimalTilingFeatures &
                                        VK_FORMAT_FEATURE_BLIT_DST_BIT
                                    ? true
                                    : false;
        if (!bltLinear && !bltOptimal) {
            // Cannot blit to either target tiling type.  It should be pretty
            // unlikely to have a device that cannot blit to either type.
            // But punt by just doing a copy and possibly have the wrong
            // colors.  This should be quite rare.
            copyOnly = true;
        } else if (!bltLinear && bltOptimal) {
            // Cannot blit to a linear target but can blt to optimal, so copy
            // after blit is needed.
            need2steps = true;
        }
        // Else bltLinear is available and only 1 step is needed.
    }

    // Put resources that need to be cleaned up in a struct with a destructor
    // so that things get cleaned up when this function is exited.
    WritePPMCleanupData data = {};
    data.device = device;
    data.pTableDevice = pTableDevice;

    // Set up the image creation info for both the blit and copy images, in case
    // both are needed.
    VkImageCreateInfo imgCreateInfo2 = {
        VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
        NULL,
        0,
        VK_IMAGE_TYPE_2D,
        VK_FORMAT_R8G8B8A8_UNORM,
        {width, height, 1},
        1,
        1,
        VK_SAMPLE_COUNT_1_BIT,
        VK_IMAGE_TILING_LINEAR,
        VK_IMAGE_USAGE_TRANSFER_DST_BIT,
        VK_SHARING_MODE_EXCLUSIVE,
        0,
        NULL,
        VK_IMAGE_LAYOUT_UNDEFINED,
    };
    VkImageCreateInfo imgCreateInfo3 = imgCreateInfo2;

    // If we need both images, set up image2 to be read/write and tiled.
    if (need2steps) {
        imgCreateInfo2.tiling = VK_IMAGE_TILING_OPTIMAL;
        imgCreateInfo2.usage =
            VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    }

    VkMemoryAllocateInfo memAllocInfo = {
        VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL,
        0, // allocationSize, queried later
        0  // memoryTypeIndex, queried later
    };
    VkMemoryRequirements memRequirements;
    VkPhysicalDeviceMemoryProperties memoryProperties;

    // Create image2 and allocate its memory.  It could be the intermediate or
    // final image.
    err =
        pTableDevice->CreateImage(device, &imgCreateInfo2, NULL, &data.image2);
    assert(!err);
    if (VK_SUCCESS != err)
        return;
    pTableDevice->GetImageMemoryRequirements(device, data.image2,
                                             &memRequirements);
    memAllocInfo.allocationSize = memRequirements.size;
    pInstanceTable->GetPhysicalDeviceMemoryProperties(physicalDevice,
                                                      &memoryProperties);
    pass = memory_type_from_properties(
        &memoryProperties, memRequirements.memoryTypeBits,
        need2steps ? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
                   : VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
        &memAllocInfo.memoryTypeIndex);
    assert(pass);
    err = pTableDevice->AllocateMemory(device, &memAllocInfo, NULL, &data.mem2);
    assert(!err);
    if (VK_SUCCESS != err)
        return;
    err = pTableQueue->BindImageMemory(device, data.image2, data.mem2, 0);
    assert(!err);
    if (VK_SUCCESS != err)
        return;

    // Create image3 and allocate its memory, if needed.
    if (need2steps) {
        err = pTableDevice->CreateImage(device, &imgCreateInfo3, NULL,
                                        &data.image3);
        assert(!err);
        if (VK_SUCCESS != err)
            return;
        pTableDevice->GetImageMemoryRequirements(device, data.image3,
                                                 &memRequirements);
        memAllocInfo.allocationSize = memRequirements.size;
        pInstanceTable->GetPhysicalDeviceMemoryProperties(physicalDevice,
                                                          &memoryProperties);
        pass = memory_type_from_properties(
            &memoryProperties, memRequirements.memoryTypeBits,
            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex);
        assert(pass);
        err = pTableDevice->AllocateMemory(device, &memAllocInfo, NULL,
                                           &data.mem3);
        assert(!err);
        if (VK_SUCCESS != err)
            return;
        err = pTableQueue->BindImageMemory(device, data.image3, data.mem3, 0);
        assert(!err);
        if (VK_SUCCESS != err)
            return;
    }

    // Set up the command buffer.  We get a command buffer from a pool we saved
    // in a hooked function, which would be the application's pool.
    const VkCommandBufferAllocateInfo allocCommandBufferInfo = {
        VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, NULL,
        deviceMap[device]->commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1};
    data.commandPool = deviceMap[device]->commandPool;
    err = pTableDevice->AllocateCommandBuffers(device, &allocCommandBufferInfo,
                                               &data.commandBuffer);
    assert(!err);
    if (VK_SUCCESS != err)
        return;

    VkDevice cmdBuf =
        static_cast<VkDevice>(static_cast<void *>(data.commandBuffer));
    deviceMap.emplace(cmdBuf, devMap);
    VkLayerDispatchTable *pTableCommandBuffer;
    pTableCommandBuffer = get_dev_info(cmdBuf)->device_dispatch_table;

    // We have just created a dispatchable object, but the dispatch table has
    // not been placed in the object yet.  When a "normal" application creates
    // a command buffer, the dispatch table is installed by the top-level api
    // binding (trampoline.c). But here, we have to do it ourselves.
    if (!devMap->pfn_dev_init) {
        *((const void **)data.commandBuffer) = *(void **)device;
    } else {
        err = devMap->pfn_dev_init(device, (void *)data.commandBuffer);
        assert(!err);
    }

    const VkCommandBufferBeginInfo commandBufferBeginInfo = {
        VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
        VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
    };
    err = pTableCommandBuffer->BeginCommandBuffer(data.commandBuffer,
                                                  &commandBufferBeginInfo);
    assert(!err);

    // This barrier is used to transition from/to present Layout
    VkImageMemoryBarrier presentMemoryBarrier = {
        VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
        NULL,
        VK_ACCESS_TRANSFER_WRITE_BIT,
        VK_ACCESS_TRANSFER_READ_BIT,
        VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
        VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
        VK_QUEUE_FAMILY_IGNORED,
        VK_QUEUE_FAMILY_IGNORED,
        image1,
        {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};

    // This barrier is used to transition from a newly-created layout to a blt
    // or copy destination layout.
    VkImageMemoryBarrier destMemoryBarrier = {
        VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
        NULL,
        0,
        VK_ACCESS_TRANSFER_WRITE_BIT,
        VK_IMAGE_LAYOUT_UNDEFINED,
        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
        VK_QUEUE_FAMILY_IGNORED,
        VK_QUEUE_FAMILY_IGNORED,
        data.image2,
        {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};

    // This barrier is used to transition a dest layout to general layout.
    VkImageMemoryBarrier generalMemoryBarrier = {
        VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
        NULL,
        VK_ACCESS_TRANSFER_WRITE_BIT,
        VK_ACCESS_TRANSFER_READ_BIT,
        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
        VK_IMAGE_LAYOUT_GENERAL,
        VK_QUEUE_FAMILY_IGNORED,
        VK_QUEUE_FAMILY_IGNORED,
        data.image2,
        {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};

    VkPipelineStageFlags srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
    VkPipelineStageFlags dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT;

    // The source image needs to be transitioned from present to transfer
    // source.
    pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages,
                                            dstStages, 0, 0, NULL, 0, NULL, 1,
                                            &presentMemoryBarrier);

    // image2 needs to be transitioned from its undefined state to transfer
    // destination.
    pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages,
                                            dstStages, 0, 0, NULL, 0, NULL, 1,
                                            &destMemoryBarrier);

    const VkImageCopy imageCopyRegion = {{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
                                         {0, 0, 0},
                                         {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
                                         {0, 0, 0},
                                         {width, height, 1}};

    if (copyOnly) {
        pTableCommandBuffer->CmdCopyImage(
            data.commandBuffer, image1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
            data.image2, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
            &imageCopyRegion);
    } else {
        VkImageBlit imageBlitRegion = {};
        imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        imageBlitRegion.srcSubresource.baseArrayLayer = 0;
        imageBlitRegion.srcSubresource.layerCount = 1;
        imageBlitRegion.srcSubresource.mipLevel = 0;
        imageBlitRegion.srcOffsets[1].x = width;
        imageBlitRegion.srcOffsets[1].y = height;
        imageBlitRegion.srcOffsets[1].z = 1;
        imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        imageBlitRegion.dstSubresource.baseArrayLayer = 0;
        imageBlitRegion.dstSubresource.layerCount = 1;
        imageBlitRegion.dstSubresource.mipLevel = 0;
        imageBlitRegion.dstOffsets[1].x = width;
        imageBlitRegion.dstOffsets[1].y = height;
        imageBlitRegion.dstOffsets[1].z = 1;

        pTableCommandBuffer->CmdBlitImage(
            data.commandBuffer, image1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
            data.image2, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
            &imageBlitRegion, VK_FILTER_NEAREST);
        if (need2steps) {
            // image 3 needs to be transitioned from its undefined state to a
            // transfer destination.
            destMemoryBarrier.image = data.image3;
            pTableCommandBuffer->CmdPipelineBarrier(
                data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL,
                1, &destMemoryBarrier);

            // Transition image2 so that it can be read for the upcoming copy to
            // image 3.
            destMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
            destMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
            destMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
            destMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
            destMemoryBarrier.image = data.image2;
            pTableCommandBuffer->CmdPipelineBarrier(
                data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL,
                1, &destMemoryBarrier);

            // This step essentially untiles the image.
            pTableCommandBuffer->CmdCopyImage(
                data.commandBuffer, data.image2,
                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, data.image3,
                VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopyRegion);
            generalMemoryBarrier.image = data.image3;
        }
    }

    // The destination needs to be transitioned from the optimal copy format to
    // the format we can read with the CPU.
    pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages,
                                            dstStages, 0, 0, NULL, 0, NULL, 1,
                                            &generalMemoryBarrier);

    // Restore the swap chain image layout to what it was before.
    // This may not be strictly needed, but it is generally good to restore
    // things to original state.
    presentMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
    presentMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
    presentMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
    presentMemoryBarrier.dstAccessMask = 0;
    pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages,
                                            dstStages, 0, 0, NULL, 0, NULL, 1,
                                            &presentMemoryBarrier);

    err = pTableCommandBuffer->EndCommandBuffer(data.commandBuffer);
    assert(!err);

    VkFence nullFence = {VK_NULL_HANDLE};
    VkSubmitInfo submitInfo;
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submitInfo.pNext = NULL;
    submitInfo.waitSemaphoreCount = 0;
    submitInfo.pWaitSemaphores = NULL;
    submitInfo.pWaitDstStageMask = NULL;
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &data.commandBuffer;
    submitInfo.signalSemaphoreCount = 0;
    submitInfo.pSignalSemaphores = NULL;

    err = pTableQueue->QueueSubmit(queue, 1, &submitInfo, nullFence);
    assert(!err);

    err = pTableQueue->QueueWaitIdle(queue);
    assert(!err);

    err = pTableDevice->DeviceWaitIdle(device);
    assert(!err);

    // Map the final image so that the CPU can read it.
    const VkImageSubresource sr = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0};
    VkSubresourceLayout srLayout;
    const char *ptr;
    if (!need2steps) {
        pTableDevice->GetImageSubresourceLayout(device, data.image2, &sr,
                                                &srLayout);
        err = pTableDevice->MapMemory(device, data.mem2, 0, VK_WHOLE_SIZE, 0,
                                      (void **)&ptr);
        assert(!err);
        if (VK_SUCCESS != err)
            return;
        data.mem2mapped = true;
    } else {
        pTableDevice->GetImageSubresourceLayout(device, data.image3, &sr,
                                                &srLayout);
        err = pTableDevice->MapMemory(device, data.mem3, 0, VK_WHOLE_SIZE, 0,
                                      (void **)&ptr);
        assert(!err);
        if (VK_SUCCESS != err)
            return;
        data.mem3mapped = true;
    }

    // Write the data to a PPM file.
    ofstream file(filename, ios::binary);
    file << "P6\n";
    file << width << "\n";
    file << height << "\n";
    file << 255 << "\n";

    ptr += srLayout.offset;
    if (3 == numChannels) {
        for (uint32_t y = 0; y < height; y++) {
            file.write(ptr, 3 * width);
            ptr += srLayout.rowPitch;
        }
    } else if (4 == numChannels) {
        for (uint32_t y = 0; y < height; y++) {
            const unsigned int *row = (const unsigned int *)ptr;
            for (uint32_t x = 0; x < width; x++) {
                file.write((char *)row, 3);
                row++;
            }
            ptr += srLayout.rowPitch;
        }
    }
    file.close();

    // Clean up handled by ~WritePPMCleanupData()
}
예제 #22
0
static void before_present(VkQueue queue, layer_data *my_data, SwapChainData *swapChain, unsigned imageIndex) {
    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;

    if (!my_data->fontUploadComplete) {
        VkSubmitInfo si = {};
        si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
        si.pNext = nullptr;
        si.waitSemaphoreCount = 0;
        si.commandBufferCount = 1;
        si.signalSemaphoreCount = 0;
        si.pCommandBuffers = &my_data->fontUploadCmdBuffer;

        pTable->QueueSubmit(queue, 1, &si, VK_NULL_HANDLE);
        my_data->fontUploadComplete = true;
#ifdef OVERLAY_DEBUG
        printf("Font image layout transition queued\n");
#endif
    }

    WsiImageData *id = swapChain->presentableImages[imageIndex];

    /* update the overlay content */

    vertex *vertices = nullptr;

    /* guaranteed not in flight due to WSI surface being available */
    VkResult U_ASSERT_ONLY err =
        pTable->MapMemory(my_data->dev, id->vertexBufferMemory, 0, id->vertexBufferSize, 0, (void **)&vertices);
    assert(!err);

    /* write vertices for string in here */
    id->numVertices = fill_vertex_buffer(my_data, vertices, imageIndex);

    pTable->UnmapMemory(my_data->dev, id->vertexBufferMemory);

    /* JIT record a command buffer to draw the overlay */

    VkCommandBufferBeginInfo cbbi;
    cbbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    cbbi.pNext = nullptr;
    cbbi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
    cbbi.pInheritanceInfo = nullptr;

    VkImageMemoryBarrier imb;
    imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    imb.pNext = nullptr;
    imb.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
    imb.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    imb.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
    imb.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    imb.image = id->image;
    imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    imb.subresourceRange.baseMipLevel = 0;
    imb.subresourceRange.levelCount = 1;
    imb.subresourceRange.baseArrayLayer = 0;
    imb.subresourceRange.layerCount = 1;
    imb.srcQueueFamilyIndex = my_data->graphicsQueueFamilyIndex;
    imb.dstQueueFamilyIndex = my_data->graphicsQueueFamilyIndex;

    VkRenderPassBeginInfo rpbi;
    rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
    rpbi.pNext = nullptr;
    rpbi.renderPass = swapChain->render_pass;
    rpbi.framebuffer = id->framebuffer;
    rpbi.renderArea.offset.x = 0;
    rpbi.renderArea.offset.y = 0;
    rpbi.renderArea.extent.width = swapChain->width;
    rpbi.renderArea.extent.height = swapChain->height;
    rpbi.clearValueCount = 0;
    rpbi.pClearValues = nullptr;

    pTable->BeginCommandBuffer(id->cmd, &cbbi);
    pTable->CmdPipelineBarrier(id->cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
                               0 /* dependency flags */, 0, nullptr, /* memory barriers */
                               0, nullptr,                           /* buffer memory barriers */
                               1, &imb);                             /* image memory barriers */
    pTable->CmdBeginRenderPass(id->cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);

    pTable->CmdBindPipeline(id->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, swapChain->pipeline);
    pTable->CmdBindDescriptorSets(id->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, my_data->pl, 0, 1, &my_data->desc_set, 0, nullptr);

    VkDeviceSize offsets[] = {0};
    VkBuffer buffers[] = {id->vertexBuffer};

    pTable->CmdBindVertexBuffers(id->cmd, 0, 1, buffers, offsets);

    pTable->CmdDraw(id->cmd, id->numVertices, 1, 0, 0);

    pTable->CmdEndRenderPass(id->cmd);
    pTable->EndCommandBuffer(id->cmd);

    /* Schedule this command buffer for execution. TODO: Do we need to protect
     * ourselves from an app that didn't wait for the presentation image to
     * be idle before mangling it? If the app is well-behaved, our command
     * buffer is guaranteed to have been retired before the app tries to
     * present it again.
     */
    VkSubmitInfo si = {};
    si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    si.pNext = nullptr;
    si.waitSemaphoreCount = 0;
    si.commandBufferCount = 1;
    si.signalSemaphoreCount = 0;
    si.pCommandBuffers = &id->cmd;
    pTable->WaitForFences(my_data->dev, 1, &my_data->fence, VK_TRUE, UINT64_MAX);
    pTable->ResetFences(my_data->dev, 1, &my_data->fence);
    pTable->QueueSubmit(queue, 1, &si, my_data->fence);

    /* Reset per-frame stats */
    my_data->cmdBuffersThisFrame = 0;
}
예제 #23
0
VkResult WrappedVulkan::vkQueuePresentKHR(
			VkQueue                                      queue,
			const VkPresentInfoKHR*                      pPresentInfo)
{
	if(m_State == WRITING_IDLE)
	{
		RenderDoc::Inst().Tick();

		GetResourceManager()->FlushPendingDirty();
	}
	
	m_FrameCounter++; // first present becomes frame #1, this function is at the end of the frame

	if(pPresentInfo->swapchainCount > 1 && (m_FrameCounter % 100) == 0)
	{
		RDCWARN("Presenting multiple swapchains at once - only first will be processed");
	}
	
	vector<VkSwapchainKHR> unwrappedSwaps;
	vector<VkSemaphore> unwrappedSems;
	
	VkPresentInfoKHR unwrappedInfo = *pPresentInfo;

	for(uint32_t i=0; i < unwrappedInfo.swapchainCount; i++)
		unwrappedSwaps.push_back(Unwrap(unwrappedInfo.pSwapchains[i]));
	for(uint32_t i=0; i < unwrappedInfo.waitSemaphoreCount; i++)
		unwrappedSems.push_back(Unwrap(unwrappedInfo.pWaitSemaphores[i]));

	unwrappedInfo.pSwapchains = unwrappedInfo.swapchainCount ? &unwrappedSwaps[0] : NULL;
	unwrappedInfo.pWaitSemaphores = unwrappedInfo.waitSemaphoreCount ? &unwrappedSems[0] : NULL;

	// Don't support any extensions for present info
	RDCASSERT(pPresentInfo->pNext == NULL);
	
	VkResourceRecord *swaprecord = GetRecord(pPresentInfo->pSwapchains[0]);
	RDCASSERT(swaprecord->swapInfo);

	SwapchainInfo &swapInfo = *swaprecord->swapInfo;

	bool activeWindow = RenderDoc::Inst().IsActiveWindow(LayerDisp(m_Instance), swapInfo.wndHandle);

	// need to record which image was last flipped so we can get the correct backbuffer
	// for a thumbnail in EndFrameCapture
	swapInfo.lastPresent = pPresentInfo->pImageIndices[0];
	m_LastSwap = swaprecord->GetResourceID();
	
	VkImage backbuffer = swapInfo.images[pPresentInfo->pImageIndices[0]].im;
	
	if(m_State == WRITING_IDLE)
	{
		m_FrameTimes.push_back(m_FrameTimer.GetMilliseconds());
		m_TotalTime += m_FrameTimes.back();
		m_FrameTimer.Restart();

		// update every second
		if(m_TotalTime > 1000.0)
		{
			m_MinFrametime = 10000.0;
			m_MaxFrametime = 0.0;
			m_AvgFrametime = 0.0;

			m_TotalTime = 0.0;

			for(size_t i=0; i < m_FrameTimes.size(); i++)
			{
				m_AvgFrametime += m_FrameTimes[i];
				if(m_FrameTimes[i] < m_MinFrametime)
					m_MinFrametime = m_FrameTimes[i];
				if(m_FrameTimes[i] > m_MaxFrametime)
					m_MaxFrametime = m_FrameTimes[i];
			}

			m_AvgFrametime /= double(m_FrameTimes.size());

			m_FrameTimes.clear();
		}
		
		uint32_t overlay = RenderDoc::Inst().GetOverlayBits();

		if(overlay & eRENDERDOC_Overlay_Enabled)
		{
			VkRenderPass rp = swapInfo.rp;
			VkImage im = swapInfo.images[pPresentInfo->pImageIndices[0]].im;
			VkFramebuffer fb = swapInfo.images[pPresentInfo->pImageIndices[0]].fb;

			VkLayerDispatchTable *vt = ObjDisp(GetDev());

			TextPrintState textstate = { GetNextCmd(), rp, fb, swapInfo.extent.width, swapInfo.extent.height };
			
			VkCommandBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT };

			VkResult vkr = vt->BeginCommandBuffer(Unwrap(textstate.cmd), &beginInfo);
			RDCASSERTEQUAL(vkr, VK_SUCCESS);

			VkImageMemoryBarrier bbBarrier = {
				VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL,
				0, 0, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
				VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
				Unwrap(im),
				{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
			};

			bbBarrier.srcAccessMask = VK_ACCESS_ALL_READ_BITS;
			bbBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

			DoPipelineBarrier(textstate.cmd, 1, &bbBarrier);

			GetDebugManager()->BeginText(textstate);

			if(activeWindow)
			{
				vector<RENDERDOC_InputButton> keys = RenderDoc::Inst().GetCaptureKeys();

				string overlayText = "Vulkan. ";

				for(size_t i=0; i < keys.size(); i++)
				{
					if(i > 0)
						overlayText += ", ";

					overlayText += ToStr::Get(keys[i]);
				}

				if(!keys.empty())
					overlayText += " to capture.";

				if(overlay & eRENDERDOC_Overlay_FrameNumber)
				{
					overlayText += StringFormat::Fmt(" Frame: %d.", m_FrameCounter);
				}
				if(overlay & eRENDERDOC_Overlay_FrameRate)
				{
					overlayText += StringFormat::Fmt(" %.2lf ms (%.2lf .. %.2lf) (%.0lf FPS)",
																					m_AvgFrametime, m_MinFrametime, m_MaxFrametime, 1000.0f/m_AvgFrametime);
				}

				float y=0.0f;

				if(!overlayText.empty())
				{
					GetDebugManager()->RenderText(textstate, 0.0f, y, overlayText.c_str());
					y += 1.0f;
				}

				if(overlay & eRENDERDOC_Overlay_CaptureList)
				{
					GetDebugManager()->RenderText(textstate, 0.0f, y, "%d Captures saved.\n", (uint32_t)m_FrameRecord.size());
					y += 1.0f;

					uint64_t now = Timing::GetUnixTimestamp();
					for(size_t i=0; i < m_FrameRecord.size(); i++)
					{
						if(now - m_FrameRecord[i].frameInfo.captureTime < 20)
						{
							GetDebugManager()->RenderText(textstate, 0.0f, y, "Captured frame %d.\n", m_FrameRecord[i].frameInfo.frameNumber);
							y += 1.0f;
						}
					}
				}

#if !defined(RELEASE)
				GetDebugManager()->RenderText(textstate, 0.0f, y, "%llu chunks - %.2f MB", Chunk::NumLiveChunks(), float(Chunk::TotalMem())/1024.0f/1024.0f);
				y += 1.0f;
#endif
			}
			else
			{
				vector<RENDERDOC_InputButton> keys = RenderDoc::Inst().GetFocusKeys();

				string str = "Vulkan. Inactive swapchain.";

				for(size_t i=0; i < keys.size(); i++)
				{
					if(i == 0)
						str += " ";
					else
						str += ", ";

					str += ToStr::Get(keys[i]);
				}

				if(!keys.empty())
					str += " to cycle between swapchains";
				
				GetDebugManager()->RenderText(textstate, 0.0f, 0.0f, str.c_str());
			}
			
			GetDebugManager()->EndText(textstate);
			
			std::swap(bbBarrier.oldLayout, bbBarrier.newLayout);
			bbBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
			bbBarrier.dstAccessMask = VK_ACCESS_ALL_READ_BITS;

			DoPipelineBarrier(textstate.cmd, 1, &bbBarrier);

			ObjDisp(textstate.cmd)->EndCommandBuffer(Unwrap(textstate.cmd));

			SubmitCmds();

			FlushQ();
		}
	}

	VkResult vkr = ObjDisp(queue)->QueuePresentKHR(Unwrap(queue), &unwrappedInfo);

	if(!activeWindow)
		return vkr;
	
	RenderDoc::Inst().SetCurrentDriver(RDC_Vulkan);

	// kill any current capture that isn't application defined
	if(m_State == WRITING_CAPFRAME && !m_AppControlledCapture)
		RenderDoc::Inst().EndFrameCapture(LayerDisp(m_Instance), swapInfo.wndHandle);

	if(RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE)
	{
		RenderDoc::Inst().StartFrameCapture(LayerDisp(m_Instance), swapInfo.wndHandle);

		m_AppControlledCapture = false;
	}

	return vkr;
}
예제 #24
0
VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo,
                                                                    const VkAllocationCallbacks *pAllocator,
                                                                    VkSwapchainKHR *pSwapChain) {
    layer_data *my_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map);
    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;
    VkResult result = my_data->pfnCreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapChain);

    if (result == VK_SUCCESS) {
        auto &data = (*my_data->swapChains)[*pSwapChain];
        data = new SwapChainData;
        data->width = pCreateInfo->imageExtent.width;
        data->height = pCreateInfo->imageExtent.height;
        data->format = pCreateInfo->imageFormat;

#ifdef OVERLAY_DEBUG
        printf(
            "Creating resources for scribbling on swapchain format %u width "
            "%u height %u\n",
            data->format, data->width, data->height);
#endif

        /* Create a renderpass for drawing into this swapchain */
        VkAttachmentDescription ad;
        ad.flags = 0;
        ad.format = data->format;
        ad.samples = VK_SAMPLE_COUNT_1_BIT;
        ad.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
        ad.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
        ad.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
        ad.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
        /* TODO: deal with the image possibly being in a different
         * layout - we need to care about general,
         * VK_IMAGE_LAYOUT_PRESENT_SOURCE_WSI,
         * etc etc
         */
        ad.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
        ad.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

        VkAttachmentReference ar;
        ar.attachment = 0;
        /* TODO: see previous layout comment */
        ar.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

        VkAttachmentReference dr;
        dr.attachment = VK_ATTACHMENT_UNUSED;
        dr.layout = VK_IMAGE_LAYOUT_UNDEFINED;

        VkSubpassDescription sd;
        sd.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
        sd.flags = 0;
        sd.inputAttachmentCount = 0;
        sd.pInputAttachments = nullptr;
        sd.colorAttachmentCount = 1;
        sd.pColorAttachments = &ar;
        sd.pResolveAttachments = nullptr;
        sd.pDepthStencilAttachment = &dr;
        /* TODO: do we need to mark the color attachment here? */
        sd.preserveAttachmentCount = 0;
        sd.pPreserveAttachments = nullptr;

        VkRenderPassCreateInfo rpci;
        rpci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
        rpci.pNext = nullptr;
        rpci.flags = 0;
        rpci.attachmentCount = 1;
        rpci.pAttachments = &ad;
        rpci.subpassCount = 1;
        rpci.pSubpasses = &sd;
        rpci.dependencyCount = 0;
        rpci.pDependencies = nullptr;

        pTable->CreateRenderPass(device, &rpci, nullptr, &data->render_pass);

        /* Create the pipeline to use in this renderpass */
        VkPipelineShaderStageCreateInfo stages[2];
        stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
        stages[0].pNext = nullptr;
        stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
        stages[0].module = my_data->vsShaderModule;
        stages[0].pName = "main";
        stages[0].flags = 0;
        stages[0].pSpecializationInfo = nullptr;

        stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
        stages[1].pNext = nullptr;
        stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
        stages[1].module = my_data->fsShaderModule;
        stages[1].pName = "main";
        stages[1].flags = 0;
        stages[1].pSpecializationInfo = nullptr;

        VkPipelineInputAssemblyStateCreateInfo piasci;
        memset(&piasci, 0, sizeof(piasci));
        piasci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
        piasci.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;

        VkViewport viewport;
        memset(&viewport, 0, sizeof(viewport));
        viewport.width = (float)data->width;
        viewport.height = (float)data->height;
        viewport.minDepth = 0.0f;
        viewport.maxDepth = 1.0f;

        VkRect2D scissor;
        memset(&scissor, 0, sizeof(scissor));
        scissor.extent.width = data->width;
        scissor.extent.height = data->height;

        VkPipelineViewportStateCreateInfo pvsci;
        memset(&pvsci, 0, sizeof(pvsci));
        pvsci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
        pvsci.viewportCount = 1;
        pvsci.pViewports = &viewport;
        pvsci.scissorCount = 1;
        pvsci.pScissors = &scissor;

        VkPipelineRasterizationStateCreateInfo prsci;
        memset(&prsci, 0, sizeof(prsci));
        prsci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
        prsci.polygonMode = VK_POLYGON_MODE_FILL;
        prsci.cullMode = VK_CULL_MODE_NONE;
        prsci.lineWidth = 1.0f;

        VkPipelineMultisampleStateCreateInfo pmsci;
        memset(&pmsci, 0, sizeof(pmsci));
        pmsci.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
        pmsci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;

        VkPipelineDepthStencilStateCreateInfo pdssci;
        memset(&pdssci, 0, sizeof(pdssci));
        pdssci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
        pdssci.minDepthBounds = 0.0f;
        pdssci.maxDepthBounds = 1.0f;

        VkPipelineColorBlendAttachmentState pcbas;
        memset(&pcbas, 0, sizeof(pcbas));
        pcbas.colorWriteMask =
            VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;

        VkPipelineColorBlendStateCreateInfo pcbsci;
        memset(&pcbsci, 0, sizeof(pcbsci));
        pcbsci.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
        pcbsci.attachmentCount = 1;
        pcbsci.pAttachments = &pcbas;
        pcbsci.blendConstants[0] = 1.0f;
        pcbsci.blendConstants[1] = 1.0f;
        pcbsci.blendConstants[2] = 1.0f;
        pcbsci.blendConstants[3] = 1.0f;

        VkVertexInputBindingDescription bindings[] = {
            {0, sizeof(vertex), VK_VERTEX_INPUT_RATE_VERTEX},
        };

        VkVertexInputAttributeDescription attribs[] = {
            {0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex, x)}, {1, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex, u)},
        };

        VkPipelineVertexInputStateCreateInfo pvisci;
        memset(&pvisci, 0, sizeof(pvisci));
        pvisci.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
        pvisci.vertexBindingDescriptionCount = sizeof(bindings) / sizeof(*bindings);
        pvisci.pVertexBindingDescriptions = &bindings[0];
        pvisci.vertexAttributeDescriptionCount = sizeof(attribs) / sizeof(*attribs);
        pvisci.pVertexAttributeDescriptions = &attribs[0];

        VkGraphicsPipelineCreateInfo gpci;
        gpci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
        gpci.pNext = nullptr;
        gpci.stageCount = sizeof(stages) / sizeof(*stages);
        gpci.pStages = &stages[0];

        gpci.pVertexInputState = &pvisci;
        gpci.pInputAssemblyState = &piasci;
        gpci.pTessellationState = nullptr;
        gpci.pViewportState = &pvsci;
        gpci.pRasterizationState = &prsci;
        gpci.pMultisampleState = &pmsci;
        gpci.pDepthStencilState = &pdssci;
        gpci.pColorBlendState = &pcbsci;
        gpci.pDynamicState = nullptr;
        gpci.flags = 0;
        gpci.layout = my_data->pl;
        gpci.renderPass = data->render_pass;
        gpci.subpass = 0;
        gpci.basePipelineHandle = VK_NULL_HANDLE;
        gpci.basePipelineIndex = 0;

        pTable->CreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &data->pipeline);
    }

    return result;
}
예제 #25
0
void layer_data::Cleanup() {
    VkLayerDispatchTable *pTable = this->device_dispatch_table;

    pTable->DestroySampler(dev, sampler, nullptr);
    pTable->DestroyDescriptorPool(dev, desc_pool, nullptr);
    pTable->DestroyPipelineLayout(dev, pl, nullptr);
    pTable->DestroyDescriptorSetLayout(dev, dsl, nullptr);
    pTable->DestroyImageView(dev, fontGlyphsImageView, nullptr);
    pTable->DestroyImage(dev, fontGlyphsImage, nullptr);
    pTable->FreeMemory(dev, fontGlyphsMemory, nullptr);

    pTable->FreeCommandBuffers(dev, pool, 1, &fontUploadCmdBuffer);
    pTable->DestroyCommandPool(dev, pool, nullptr);

    pTable->DestroyShaderModule(dev, vsShaderModule, nullptr);
    pTable->DestroyShaderModule(dev, fsShaderModule, nullptr);
    pTable->DestroyFence(dev, fence, nullptr);
}
예제 #26
0
static void after_device_create(VkPhysicalDevice gpu, VkDevice device, layer_data *data) {
    VkResult U_ASSERT_ONLY err;

    data->gpu = gpu;
    data->dev = device;
    data->frame = 0;
    data->cmdBuffersThisFrame = 0;

    VkLayerDispatchTable *pTable = data->device_dispatch_table;

    /* Get our WSI hooks in. */
    data->pfnCreateSwapchainKHR = (PFN_vkCreateSwapchainKHR)pTable->GetDeviceProcAddr(device, "vkCreateSwapchainKHR");
    data->pfnGetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR)pTable->GetDeviceProcAddr(device, "vkGetSwapchainImagesKHR");
    data->pfnQueuePresentKHR = (PFN_vkQueuePresentKHR)pTable->GetDeviceProcAddr(device, "vkQueuePresentKHR");
    data->pfnDestroySwapchainKHR = (PFN_vkDestroySwapchainKHR)pTable->GetDeviceProcAddr(device, "vkDestroySwapchainKHR");
    data->swapChains = new std::unordered_map<VkSwapchainKHR, SwapChainData *>;

    /* Command pool */
    VkCommandPoolCreateInfo cpci;
    cpci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
    cpci.pNext = nullptr;
    cpci.queueFamilyIndex = data->graphicsQueueFamilyIndex;
    cpci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
    err = pTable->CreateCommandPool(device, &cpci, nullptr, &data->pool);
    assert(!err);

    /* Create the objects we need */

    /* Compile the shaders */
    compile_shader(device, VULKAN_SAMPLES_BASE_DIR "/Layer-Samples/data/overlay-vert.spv", &data->vsShaderModule);
    compile_shader(device, VULKAN_SAMPLES_BASE_DIR "/Layer-Samples/data/overlay-frag.spv", &data->fsShaderModule);

    /* Upload the font bitmap */
    VkImageCreateInfo ici;
    memset(&ici, 0, sizeof(ici));
    ici.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
    ici.imageType = VK_IMAGE_TYPE_2D;
    ici.format = VK_FORMAT_R8_UNORM;
    ici.extent.width = FONT_ATLAS_SIZE;
    ici.extent.height = FONT_ATLAS_SIZE;
    ici.extent.depth = 1;
    ici.mipLevels = 1;
    ici.arrayLayers = 1;
    ici.samples = VK_SAMPLE_COUNT_1_BIT;
    ici.tiling = VK_IMAGE_TILING_LINEAR;
    ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
    ici.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;

    err = pTable->CreateImage(device, &ici, nullptr, &data->fontGlyphsImage);
    assert(!err);

    VkMemoryRequirements mem_reqs;
    pTable->GetImageMemoryRequirements(device, data->fontGlyphsImage, &mem_reqs);

    VkMemoryAllocateInfo mem_alloc;
    memset(&mem_alloc, 0, sizeof(mem_alloc));
    mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    mem_alloc.allocationSize = mem_reqs.size;
    mem_alloc.memoryTypeIndex = choose_memory_type(gpu, mem_reqs.memoryTypeBits,
                                                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

    err = pTable->AllocateMemory(device, &mem_alloc, nullptr, &data->fontGlyphsMemory);
    assert(!err);
    err = pTable->BindImageMemory(device, data->fontGlyphsImage, data->fontGlyphsMemory, 0);
    assert(!err);

    VkImageSubresource subres;
    subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    subres.mipLevel = 0;
    subres.arrayLayer = 0;
    VkSubresourceLayout layout;
    void *bits;

    pTable->GetImageSubresourceLayout(device, data->fontGlyphsImage, &subres, &layout);

    /* ensure we can directly upload into this layout */
    assert(!layout.offset);
    assert(layout.size >= FONT_ATLAS_SIZE * FONT_ATLAS_SIZE);
    assert(layout.rowPitch == FONT_ATLAS_SIZE);

    err = pTable->MapMemory(device, data->fontGlyphsMemory, 0, VK_WHOLE_SIZE, 0, &bits);
    assert(!err);

    /* Load the font glyphs directly into the mapped buffer */
    std::vector<unsigned char> fontData;
    get_file_contents(VULKAN_SAMPLES_BASE_DIR "/Layer-Samples/data/FreeSans.ttf", fontData);
    stbtt_BakeFontBitmap(&fontData[0], 0, FONT_SIZE_PIXELS, (unsigned char *)bits, FONT_ATLAS_SIZE, FONT_ATLAS_SIZE, 32, 96,
                         data->glyphs);

    pTable->UnmapMemory(device, data->fontGlyphsMemory);

    VkImageViewCreateInfo ivci;
    ivci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
    ivci.pNext = nullptr;
    ivci.format = ici.format;
    ivci.components.r = VK_COMPONENT_SWIZZLE_R;
    ivci.components.g = VK_COMPONENT_SWIZZLE_G;
    ivci.components.b = VK_COMPONENT_SWIZZLE_B;
    ivci.components.a = VK_COMPONENT_SWIZZLE_A;
    ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    ivci.subresourceRange.baseMipLevel = 0;
    ivci.subresourceRange.levelCount = 1;
    ivci.subresourceRange.baseArrayLayer = 0;
    ivci.subresourceRange.layerCount = 1;
    ivci.image = data->fontGlyphsImage;
    ivci.flags = 0;

    err = pTable->CreateImageView(device, &ivci, nullptr, &data->fontGlyphsImageView);
    assert(!err);

    /* transition from undefined layout to shader readonly so we can use it.
     * requires a command buffer. */
    VkCommandBufferAllocateInfo cbai;
    cbai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    cbai.pNext = nullptr;
    cbai.commandPool = data->pool;
    cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    cbai.commandBufferCount = 1;

    VkCommandBuffer cmd;
    err = pTable->AllocateCommandBuffers(device, &cbai, &cmd);
    assert(!err);

    /* We have just created a dispatchable object, but the dispatch table has
     * not been placed in the object yet.
     * When a "normal" application creates a command buffer,
     * the dispatch table is installed by the top-level binding (trampoline.c).
     * But here, we have to do it ourselves. */
    if (!data->pfn_dev_init) {
        *((const void **)cmd) = *(void **)device;
    } else {
        err = data->pfn_dev_init(device, (void *)cmd);
        assert(!err);
    }

    VkCommandBufferBeginInfo cbbi;
    cbbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    cbbi.pNext = nullptr;
    cbbi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
    cbbi.pInheritanceInfo = nullptr;

    err = pTable->BeginCommandBuffer(cmd, &cbbi);
    assert(!err);

    VkImageMemoryBarrier imb;
    imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    imb.pNext = nullptr;
    imb.dstAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
    imb.srcAccessMask = 0;
    imb.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    imb.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
    imb.image = data->fontGlyphsImage;
    imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    imb.subresourceRange.baseMipLevel = 0;
    imb.subresourceRange.levelCount = 1;
    imb.subresourceRange.baseArrayLayer = 0;
    imb.subresourceRange.layerCount = 1;
    imb.srcQueueFamilyIndex = data->graphicsQueueFamilyIndex;
    imb.dstQueueFamilyIndex = data->graphicsQueueFamilyIndex;

    pTable->CmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0 /* dependency flags */,
                               0, nullptr, /* memory barriers */
                               0, nullptr, /* buffer memory barriers */
                               1, &imb);   /* image memory barriers */

    pTable->EndCommandBuffer(cmd);
    data->fontUploadCmdBuffer = cmd;
    data->fontUploadComplete = false; /* we will schedule this at first present on this device */

#ifdef OVERLAY_DEBUG
    printf("Font upload done.\n");
#endif

    /* create a sampler to use with the texture */
    VkSamplerCreateInfo sci;
    sci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    sci.pNext = nullptr;
    sci.flags = 0;
    sci.magFilter = VK_FILTER_NEAREST;
    sci.minFilter = VK_FILTER_NEAREST;
    sci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
    sci.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    sci.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    sci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    sci.mipLodBias = 0.0f;
    sci.anisotropyEnable = false;
    sci.maxAnisotropy = 1;
    sci.compareEnable = false;
    sci.compareOp = VK_COMPARE_OP_NEVER;
    sci.minLod = 0.0f;
    sci.maxLod = 0.0f;
    sci.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
    sci.unnormalizedCoordinates = VK_FALSE;

    err = pTable->CreateSampler(device, &sci, nullptr, &data->sampler);
    assert(!err);

    /* descriptor set stuff so we can use the texture from a shader. */
    VkDescriptorSetLayoutBinding dslb[1];
    dslb[0].binding = 0;
    dslb[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
    dslb[0].descriptorCount = 1;
    dslb[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
    dslb[0].pImmutableSamplers = nullptr;

    VkDescriptorSetLayoutCreateInfo dslci;
    memset(&dslci, 0, sizeof(dslci));
    dslci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
    dslci.pNext = nullptr;
    dslci.bindingCount = 1;
    dslci.pBindings = dslb;

    err = pTable->CreateDescriptorSetLayout(device, &dslci, nullptr, &data->dsl);
    assert(!err);

    VkPipelineLayoutCreateInfo plci;
    memset(&plci, 0, sizeof(plci));
    plci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    plci.setLayoutCount = 1;
    plci.pSetLayouts = &data->dsl;

    err = pTable->CreatePipelineLayout(device, &plci, nullptr, &data->pl);
    assert(!err);

    VkDescriptorPoolSize dtc[1];
    dtc[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
    dtc[0].descriptorCount = 1;
    VkDescriptorPoolCreateInfo dpci;
    dpci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
    dpci.pNext = nullptr;
    dpci.flags = 0;
    dpci.maxSets = 1;
    dpci.poolSizeCount = 1;
    dpci.pPoolSizes = dtc;

    err = pTable->CreateDescriptorPool(device, &dpci, nullptr, &data->desc_pool);
    assert(!err);

    VkDescriptorSetAllocateInfo dsai;
    dsai.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
    dsai.pNext = nullptr;
    dsai.descriptorPool = data->desc_pool;
    dsai.descriptorSetCount = 1;
    dsai.pSetLayouts = &data->dsl;
    err = pTable->AllocateDescriptorSets(device, &dsai, &data->desc_set);
    assert(!err);

    VkDescriptorImageInfo descs[1];
    descs[0].sampler = data->sampler;
    descs[0].imageView = data->fontGlyphsImageView;
    descs[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;  // TODO: cube does this, is it correct?

    VkWriteDescriptorSet writes[1];
    memset(&writes, 0, sizeof(writes));
    writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
    writes[0].dstSet = data->desc_set;
    writes[0].dstBinding = 0;
    writes[0].descriptorCount = 1;
    writes[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
    writes[0].pImageInfo = descs;

    pTable->UpdateDescriptorSets(device, 1, writes, 0, nullptr);

    VkFenceCreateInfo fci;
    fci.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
    fci.pNext = NULL;
    fci.flags = VK_FENCE_CREATE_SIGNALED_BIT;
    pTable->CreateFence(device, &fci, NULL, &data->fence);
}
예제 #27
0
VKAPI_ATTR VkResult VKAPI_CALL
QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo) {
    static int frameNumber = 0;
    if (frameNumber == 10) {
        fflush(stdout); /* *((int*)0)=0; */
    }
    DeviceMapStruct *devMap = get_dev_info((VkDevice)queue);
    assert(devMap);
    VkLayerDispatchTable *pDisp = devMap->device_dispatch_table;
    VkResult result = pDisp->QueuePresentKHR(queue, pPresentInfo);
    loader_platform_thread_lock_mutex(&globalLock);

    if (!screenshotEnvQueried) {
        const char *_vk_screenshot = local_getenv("_VK_SCREENSHOT");
        if (_vk_screenshot && *_vk_screenshot) {
            string spec(_vk_screenshot), word;
            size_t start = 0, comma = 0;

            while (start < spec.size()) {
                int frameToAdd;
                comma = spec.find(',', start);
                if (comma == string::npos)
                    word = string(spec, start);
                else
                    word = string(spec, start, comma - start);
                frameToAdd = atoi(word.c_str());
                // Add the frame number to set, but only do it if the word
                // started with a digit and if
                // it's not already in the list
                if (*(word.c_str()) >= '0' && *(word.c_str()) <= '9') {
                    screenshotFrames.insert(frameToAdd);
                }
                if (comma == string::npos)
                    break;
                start = comma + 1;
            }
        }
        local_free_getenv(_vk_screenshot);
        screenshotEnvQueried = true;
    }

    if (result == VK_SUCCESS && !screenshotFrames.empty()) {
        set<int>::iterator it;
        it = screenshotFrames.find(frameNumber);
        if (it != screenshotFrames.end()) {
            string fileName;
            fileName = to_string(frameNumber) + ".ppm";

            VkImage image;
            VkSwapchainKHR swapchain;
            // We'll dump only one image: the first
            swapchain = pPresentInfo->pSwapchains[0];
            image = swapchainMap[swapchain]
                        ->imageList[pPresentInfo->pImageIndices[0]];
            writePPM(fileName.c_str(), image);
            screenshotFrames.erase(it);

            if (screenshotFrames.empty()) {
                // Free all our maps since we are done with them.
                for (auto it = swapchainMap.begin(); it != swapchainMap.end();
                     it++) {
                    SwapchainMapStruct *swapchainMapElem = it->second;
                    delete swapchainMapElem;
                }
                for (auto it = imageMap.begin(); it != imageMap.end(); it++) {
                    ImageMapStruct *imageMapElem = it->second;
                    delete imageMapElem;
                }
                for (auto it = physDeviceMap.begin(); it != physDeviceMap.end();
                     it++) {
                    PhysDeviceMapStruct *physDeviceMapElem = it->second;
                    delete physDeviceMapElem;
                }
                swapchainMap.clear();
                imageMap.clear();
                physDeviceMap.clear();
            }
        }
    }
    frameNumber++;
    loader_platform_thread_unlock_mutex(&globalLock);
    return result;
}
예제 #28
0
static void before_present(VkQueue queue, layer_data *my_data,
                           SwapChainData *swapChain, unsigned imageIndex) {
    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;

    if (!my_data->fontUploadComplete) {
        VkSubmitInfo si;
        si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
        si.pNext = nullptr;
        si.waitSemaphoreCount = 0;
        si.commandBufferCount = 1;
        si.signalSemaphoreCount = 0;
        si.pCommandBuffers = &my_data->fontUploadCmdBuffer;

        pTable->QueueSubmit(queue, 1, &si, VK_NULL_HANDLE);
        my_data->fontUploadComplete = true;
#ifdef OVERLAY_DEBUG
        printf("Font image layout transition queued\n");
#endif
    }

    WsiImageData *id = swapChain->presentableImages[imageIndex];

    /* update the overlay content */

    vertex *vertices = nullptr;

    /* guaranteed not in flight due to WSI surface being available */
    VkResult U_ASSERT_ONLY err =
        pTable->MapMemory(my_data->dev, id->vertexBufferMemory, 0,
                          id->vertexBufferSize, 0, (void **)&vertices);
    assert(!err);

    /* write vertices for string in here */
    id->numVertices = fill_vertex_buffer(my_data, vertices, imageIndex);

    pTable->UnmapMemory(my_data->dev, id->vertexBufferMemory);

    /* JIT record a command buffer to draw the overlay */

    VkCommandBufferBeginInfo cbbi;
    cbbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    cbbi.pNext = nullptr;
    cbbi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
    cbbi.pInheritanceInfo = nullptr;

    VkRenderPassBeginInfo rpbi;
    rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
    rpbi.pNext = nullptr;
    rpbi.renderPass = swapChain->render_pass;
    rpbi.framebuffer = id->framebuffer;
    rpbi.renderArea.offset.x = 0;
    rpbi.renderArea.offset.y = 0;
    rpbi.renderArea.extent.width = swapChain->width;
    rpbi.renderArea.extent.height = swapChain->height;
    rpbi.clearValueCount = 0;
    rpbi.pClearValues = nullptr;

    pTable->BeginCommandBuffer(id->cmd, &cbbi);
    pTable->CmdBeginRenderPass(id->cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);

    pTable->CmdBindPipeline(id->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
                            swapChain->pipeline);
    pTable->CmdBindDescriptorSets(id->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
                                  my_data->pl, 0, 1, &my_data->desc_set, 0,
                                  nullptr);

    VkDeviceSize offsets[] = {0};
    VkBuffer buffers[] = {id->vertexBuffer};

    pTable->CmdBindVertexBuffers(id->cmd, 0, 1, buffers, offsets);

    pTable->CmdDraw(id->cmd, id->numVertices, 1, 0, 0);

    pTable->CmdEndRenderPass(id->cmd);
    pTable->EndCommandBuffer(id->cmd);

    /* Schedule this command buffer for execution. TODO: Do we need to protect
     * ourselves from an app that didn't wait for the presentation image to
     * be idle before mangling it? If the app is well-behaved, our command
     * buffer is guaranteed to have been retired before the app tries to
     * present it again.
     */
    VkFence null_fence = VK_NULL_HANDLE;
    VkSubmitInfo si;
    si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    si.pNext = nullptr;
    si.waitSemaphoreCount = 0;
    si.commandBufferCount = 1;
    si.signalSemaphoreCount = 0;
    si.pCommandBuffers = &id->cmd;
    pTable->QueueSubmit(queue, 1, &si, null_fence);

    /* Reset per-frame stats */
    my_data->cmdBuffersThisFrame = 0;
}