Войти
ПрограммированиеСтатьиГрафика

Vulkan API «Hello Triangle» (5 стр)

Автор:

Ограждение и обновление экрана

Цикл, в котором мы будем выводить пустой экран, состоит из нескольких этапов:
•  Запрос на следующий кадр цепочки обмена
•  Ожидание обработки запроса
•  Отправка соответствующего буфера команд
•  Ожидание очереди команд
•  Вывод изображения на экран

Второй пункт является ключевым для этого раздела, т.к. ожидание на обработку запроса выполняется посредством объекта ограждения.

Ограждение

Ограждение может быть использовано для проверки выполнения очереди команд. Для нас оно интересно не столько из-за вопросов производительности, сколько из-за того, что этот объект необходим для запроса к цепочке обмена.

VkResult vkCreateFence(
    VkDevice        device,
    const VkFenceCreateInfo*    pCreateInfo,
    const VkAllocationCallbacks*    pAllocator,
    VkFence*        pFence);

typedef struct VkFenceCreateInfo {
    VkStructureType  sType;
    const void*    pNext;
    VkFenceCreateFlags  flags;
} VkFenceCreateInfo;

Значением поля flags можно инициализировать начальное состояние как активное или неактивное. За обнуление состояния или ожидание переключения в активное отвечаю следующие функции:

VkResult vkResetFences(
    VkDevice    device,
    uint32_t    fenceCount,
    const VkFence*  pFences);

VkResult vkWaitForFences(
    VkDevice    device,
    uint32_t    fenceCount,
    const VkFence*  pFences,
    VkBool32    waitAll,
    uint64_t    timeout);

Самым важным тут является то, что с помощью последней функции можно ожидать выполнения как одного условия, так и всех сразу.

VkFence vk_fence;
{
  VkFenceCreateInfo vk_fenceCreateInfo;
  {
    vk_fenceCreateInfo.sType = VkStructureType::VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
    vk_fenceCreateInfo.pNext = nullptr;
    vk_fenceCreateInfo.flags = 0;
  }

  if(vkCreateFence(vk_device, &vk_fenceCreateInfo, nullptr, &vk_fence) != VkResult::VK_SUCCESS)
  {
    throw std::exception("failed to create fence");
  }
}

Обновление экрана

Имея на руках все необходимые компоненты: очередь, буферы команд, ограждение и цепочку обмена - можно наконец залить экран цветом.

Первым делом необходимо получить индекс следующего кадра в цепочке обмена, с помощью функции:

VkResult vkAcquireNextImageKHR(
    VkDevice                                    device,
    VkSwapchainKHR                              swapchain,
    uint64_t                                    timeout,
    VkSemaphore                                 semaphore,
    VkFence                                     fence,
    uint32_t*                                   pImageIndex);

Поскольку у нас нет ни фиксированного времени ожидания, ни семафоров, на заполним параметры timeout и semaphore значениями UINT64_MAX и VK_NULL_HANDLE соответственно.

Следующий оставленный "на потом" пункт - отправка буферов команд в очередь:

VkResult vkQueueSubmit(
    VkQueue                                     queue,
    uint32_t                                    submitCount,
    const VkSubmitInfo*                         pSubmits,
    VkFence                                     fence);

typedef struct VkSubmitInfo {
    VkStructureType                sType;
    const void*                    pNext;
    uint32_t                       waitSemaphoreCount;
    const VkSemaphore*             pWaitSemaphores;
    const VkPipelineStageFlags*    pWaitDstStageMask;
    uint32_t                       commandBufferCount;
    const VkCommandBuffer*         pCommandBuffers;
    uint32_t                       signalSemaphoreCount;
    const VkSemaphore*             pSignalSemaphores;
} VkSubmitInfo;

Как видно из структуры, у нас есть целый ряд опций, которые можно использовать для эффективного управления выводом прямо на GPU: отправленный на обработку буфер может как ожидать на сигналы извне, так и сам сигнализировать о завершении (посредством семафоров).

Последний момент — вывод изображения на экран:

VkResult vkQueuePresentKHR(
    VkQueue                                     queue,
    const VkPresentInfoKHR*                     pPresentInfo);

typedef struct VkPresentInfoKHR {
    VkStructureType          sType;
    const void*              pNext;
    uint32_t                 waitSemaphoreCount;
    const VkSemaphore*       pWaitSemaphores;
    uint32_t                 swapchainCount;
    const VkSwapchainKHR*    pSwapchains;
    const uint32_t*          pImageIndices;
    VkResult*                pResults;
} VkPresentInfoKHR;

Разобравшись, можно приступить к модификации главного цикла:

uint32_t vk_swapchainImage;
{
  if(vkAcquireNextImageKHR(vk_device, vk_swapchainKHR, UINT64_MAX, VK_NULL_HANDLE, vk_fence, &vk_swapchainImage) != VkResult::VK_SUCCESS)
  {
    throw std::exception("failed to get next swapchain image");
  }
}

if(vkWaitForFences(vk_device, 1, &vk_fence, VK_TRUE, UINT64_MAX) != VkResult::VK_SUCCESS)
{
  throw std::exception("failed to wait for fence");
}
if(vkResetFences(vk_device, 1, &vk_fence) != VkResult::VK_SUCCESS)
{
  throw std::exception("failed to reset fence");
}

auto &vk_commandBuffer = vk_commandBuffers[vk_swapchainImage];

VkPipelineStageFlags vk_waitMask = VkPipelineStageFlagBits::VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
VkSubmitInfo vk_submitInfo;
{
  vk_submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  vk_submitInfo.pNext = nullptr;
  vk_submitInfo.waitSemaphoreCount = 0;
  vk_submitInfo.pWaitSemaphores = nullptr;
  vk_submitInfo.pWaitDstStageMask = &vk_waitMask;
  vk_submitInfo.commandBufferCount = 1;
  vk_submitInfo.pCommandBuffers = &vk_commandBuffer;
  vk_submitInfo.signalSemaphoreCount = 0;
  vk_submitInfo.pSignalSemaphores = nullptr;
}
if(vkQueueSubmit(vk_queue, 1, &vk_submitInfo, VK_NULL_HANDLE) != VkResult::VK_SUCCESS)
{
  throw std::exception("failed to submit command buffer");
}

if(vkQueueWaitIdle(vk_queue) != VkResult::VK_SUCCESS)
{
  throw std::exception("failed to wait for queue");
}

VkResult vk_result;
VkPresentInfoKHR vk_presentInfoKHR;
{
  vk_presentInfoKHR.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
  vk_presentInfoKHR.pNext = nullptr;
  vk_presentInfoKHR.waitSemaphoreCount = 0;
  vk_presentInfoKHR.pWaitSemaphores = nullptr;
  vk_presentInfoKHR.swapchainCount = 1;
  vk_presentInfoKHR.pSwapchains = &vk_swapchainKHR;
  vk_presentInfoKHR.pImageIndices = &vk_swapchainImage;
  vk_presentInfoKHR.pResults = &vk_result;
}
if(vkQueuePresentKHR(vk_queue, &vk_presentInfoKHR) != VkResult::VK_SUCCESS || vk_result != VkResult::VK_SUCCESS)
{
  throw std::exception("failed to present swapchain");
}

Если вы читаете это сообщение - поздравляю. Большая половина формальностей завершена и написанного кода достаточно для вывода пустого экрана средствами Vulkan API. Тем не менее, для вывода треугольника нам понадобится еще кое-какая часть внутренней инфраструктуры, такой как буферы данных, шейдеры и конвейер.

Код из статьи до этого момента можно найти здесь: Vulkan Tutorial part 1

Страницы: 1 2 3 4 5 6 7 Следующая »

#Vulkan, #основы

15 сентября 2016

Комментарии [69]