Vulkan API — урок 46. Изображение текстуры

Этот урок идет скорее не как отдельный, а как небольшое дополнение к предыдущему.

Теперь абстрагируем создание изображения в функции createImage, как это было сделано ранее для буферов. Создадим функцию и переместим все операции создания в неё. Параметры width, height, format, tiling mode, usage и памяти лучше сделать изменяемыми, ведь они могут изменяться от одного объекта к другому.

void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter& image, VDeleter& imageMemory) {
    VkImageCreateInfo imageInfo = {};
    imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
    imageInfo.imageType = VK_IMAGE_TYPE_2D;
    imageInfo.extent.width = width;
    imageInfo.extent.height = height;
    imageInfo.extent.depth = 1;
    imageInfo.mipLevels = 1;
    imageInfo.arrayLayers = 1;
    imageInfo.format = format;
    imageInfo.tiling = tiling;
    imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
    imageInfo.usage = usage;
    imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
    imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

    if (vkCreateImage(device, &imageInfo, nullptr, image.replace()) != VK_SUCCESS) {
        throw std::runtime_error("failed to create image!");
    }

    VkMemoryRequirements memRequirements;
    vkGetImageMemoryRequirements(device, image, &memRequirements);

    VkMemoryAllocateInfo allocInfo = {};
    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    allocInfo.allocationSize = memRequirements.size;
    allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);

    if (vkAllocateMemory(device, &allocInfo, nullptr, imageMemory.replace()) != VK_SUCCESS) {
        throw std::runtime_error("failed to allocate image memory!");
    }

    vkBindImageMemory(device, image, imageMemory, 0);
}

Функция createTextureImage может быть упрощена до следующего вида:

void createTextureImage() {
    int texWidth, texHeight, texChannels;
    stbi_uc* pixels = stbi_load("textures/texture.jpg", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
    VkDeviceSize imageSize = texWidth * texHeight * 4;

    if (!pixels) {
        throw std::runtime_error("failed to load texture image!");
    }

    VDeleter stagingImage{device, vkDestroyImage};
    VDeleter stagingImageMemory{device, vkFreeMemory};
    createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingImage, stagingImageMemory);

    VkImageSubresource subresource = {};
    subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    subresource.mipLevel = 0;
    subresource.arrayLayer = 0;

    VkSubresourceLayout stagingImageLayout;
    vkGetImageSubresourceLayout(device, stagingImage, &subresource, &stagingImageLayout);

    void* data;
    vkMapMemory(device, stagingImageMemory, 0, imageSize, 0, &data);

    if (stagingImageLayout.rowPitch == texWidth * 4) {
        memcpy(data, pixels, (size_t) imageSize);
    } else {
        uint8_t* dataBytes = reinterpret_cast<uint8_t*>(data);

        for (int y = 0; y < texHeight; y++) {
            memcpy(&dataBytes[y * stagingImageLayout.rowPitch], &pixels[y * texWidth * 4], texWidth * 4);
        }
    }

    vkUnmapMemory(device, stagingImageMemory);

    stbi_image_free(pixels);
}

Далее создадим как таковое изображение текстуры, для чего определим два новых члена класса для хранения дескриптора (handle) изображения и его памяти:

VDeleter commandPool{device, vkDestroyCommandPool};
VDeleter textureImage{device, vkDestroyImage};
VDeleter textureImageMemory{device, vkFreeMemory};
VDeleter vertexBuffer{device, vkDestroyBuffer};

Финальное изображение теперь может быть создано используя ту же функцию:

createImage(
    texWidth, texHeight,
    VK_FORMAT_R8G8B8A8_UNORM,
    VK_IMAGE_TILING_OPTIMAL,
    VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
    VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
    textureImage,
    textureImageMemory
);

Размеры изображений должны совпадать, что думаю и без того понятно. Форматы так же должны быть совместимы, поскольку команда просто копирует строки данных изображения. Два формата цвета совместимы, если они имеют одинаковое чисто байт на пиксель. Глубина/stencil (которые еще обсудим подробнее) должны совпадать, tiling не обязательно. Изображение текстуры буде использовано в качестве конечной точки переноса, и тексели из него должны быть доступны в шейдере, для этого необходим флаг VK_IMAGE_USAGE_SAMPLED_BIT.

Main Admin

4 Comments

  1. Почему VK_IMAGE_USAGE_TRANSFER_DST_BIT? это же вроде изображение в оперативке из которого коппируют?

    • Тут ключевое значение имеет фрагмент DST — destination, т.е. пункт назначение, а SRC — source источник. Мы ведь тут создаем изображение, а не используем его.

      • из этого источника потом будет копирование в staging буфер, разве нет?

        • Досконально листинг не помню уже, но поиск по «VK_IMAGE_USAGE_TRANSFER_SRC_BIT» сразу привел к трем строкам:

          
          VDeleter stagingImage{device, vkDestroyImage};
          VDeleter stagingImageMemory{device, vkFreeMemory};
          createImage(texWidth, 
                      texHeight, 
                      VK_FORMAT_R8G8B8A8_UNORM, 
                      VK_IMAGE_TILING_LINEAR, 
                      VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 
                      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 
                      stagingImage, 
                      stagingImageMemory);

          Тут же у нас textureImage и stagingImage в одном уроке, это разные изображения, которые создаются с разными параметрами. Проще говоря все ПРИМЕРНО выглядит так: мы создаем одно изображение, которое быстрее будет работать для получения картинки из оперативки, а затем копируем картинку в другое изображение, которое будет быстрее работать во внутренних задачах видеокарты.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *