Как создать один буфер для хранения нескольких текстур разного размера в Vulkan

В моем приложении Vulkan я хотел бы иметь один буфер памяти, в котором можно хранить несколько текстур разного размера. Затем я хотел бы иметь VkImageView, соответствующий каждой текстуре в буфере. Я точно не знаю, как я могу создать такой буфер, вот что я придумал:

// Create images
VkImage images[TEXTURE_COUNT];
for (int i = 0; i < TEXTURE_COUNT, i++) {
    VkImageCreateInfo imageCreateInfo{};

    // Specific to texture i
    imageCreateInfo.extent.width  = ...
    iamgeCreateInfo.extent.height = ...
    
    // Other imageCreateInfo properties are constant across all textures
    ...

    VkCreateImage(device, &imageCreateInfo, nullptr, &images[i]);
}

// Find total size of memory buffer & image offsets
int totalSize = 0;
int offsets[TEXTURE_COUNT];
for (int i = 0; i < TEXTURE_COUNT; i++) {
    VkMemoryRequirements memoryRequirements;
    vkGetImageMemoryRequirements(device, images[i], &memoryRequirements);
    offsets[i] = totalSize;
    totalSize += memoryRequirements.size;
}

// Get memory type index of memory buffer
VkMemoryRequirements firstImageMemoryRequirements;
vkGetImageMemoryRequirements(device, images[0], &firstImageMemoryRequirements);
int memoryTypeIndex = ... // Get memory type index using firstImageMemoryRequirements.memoryTypeBits

// Allocate memory
VkMemoryAllocateInfo memoryAllocateInfo{};
memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memoryAllocateInfo.allocationSize = totalSize;
memoryAllocateInfo.memoryTypeIndex = memoryTypeIndex;
VkMemory memory;
vkAllocateMemory(device, &memoryAllocateInfo, nullptr, &memory)

// Bind images to memory at corresponding offset
VkBindImageMemoryInfo bindImageMemoryInfos[TEXTURE_COUNT];
for (int i = 0; i < TEXTURE_COUNT, i++) {
    VkBindImageMemoryInfo bindImageMemoryInfo{};
    bindImageMemoryInfo.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
    bindImageMemoryInfo.image = images[i];
    bindImageMemoryInfo.memory = memory;
    bindImageMemoryInfo.memoryOffset = offsets[i];
    bindImageMemoryInfos[i] = bindImageMemoryInfo;
}
vkBindImageMemory2(device, TEXTURE_COUNT, &bindImageMemoryInfos);

// Create image views
VkImageView imageViews[TEXTURE_COUNT];
for (int i = 0; i < TEXTURE_COUNT; i++) {
    VkImageViewCreateInfo imageViewCreateInfo{};
    imageViewCreateInfo.image = images[i];
    ...

    VkCreateImageView(device, &imageViewCreateInfo, nullptr, &imageViews[i]);
}

// Now I have a bunch of image views tied to each texture,
// where each texture is stored in one memory buffer at a certain offset.

Это кажется разумным, или это не правильный путь? Одна вещь, которая кажется мне немного странной, это когда я получаю индекс типа памяти буфера памяти. Чтобы получить это, вам нужно изображение, которое у меня есть TEXTURE_COUNT, поэтому я просто выбираю первое изображение. Единственными свойствами изображения, которые очень зависят от текстуры, являются ширина и высота экстента, поэтому я надеюсь, что это не повлияет на биты типа памяти каждого изображения. Я предполагаю, что каждое изображение будет иметь одинаковые биты типа памяти, поэтому я могу получить индекс типа памяти, используя биты типа памяти первого изображения. Мысли об этом предположении были бы замечательными.

1. Я бы порекомендовал хотя бы проверить, что каждое изображение имеет один и тот же тип памяти, просто чтобы поймать глупые ошибки, такие как случайное включение другого типа изображения в ваш массив. 2. Обязательно должен накапливаться массив смещений? то есть строка offsets[i] = memoryRequirements.size; должна быть нулевой для первого элемента, размер этого изображения для второго элемента и так далее?

stridecolossus 13.12.2020 14:06

@stridecolossus Верно, у меня были неправильные смещения. Я изменил его на offsets[i] = totalSize;, и вы правильно заметили, что проверяете тип памяти, чтобы отлавливать ошибки.

CasualKyle 13.12.2020 19:07

К вашему сведению: не стоит называть выделение памяти «буфером», когда термин «буфер» уже имеет очень конкретное (и отчетливое) значение в Vulkan.

Nicol Bolas 13.12.2020 19:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
766
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Каждое используемое вами VkImage должно храниться в области памяти, соответствующей этому конкретному VkImage объекту. Это означает, что смещение/размер должны соответствовать выравниванию и размеру этого объекта изображения, а тип памяти, к которому он привязан, должен быть одним из типов памяти, с которыми может использоваться изображение.

Это необходимо запрашивать отдельно для каждого VkImage объекта, который вы используете. Или обычно; два идентичных объекта VkImage (т.е. созданные из одинаковых структур VkImageCreateInfo) будут иметь одинаковые требования, поэтому, если вы повторяете идентичные объекты VkImage, вам не нужно снова запрашивать их требования. Есть несколько других обстоятельств, которые позволяют изображениям с разными параметрами создания иметь одинаковые требования, поэтому, если вы хотите воспользоваться этим, вам нужно посмотреть подробности.

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

Кроме того, вы можете просто выделить память по мере необходимости в больших плитах. То есть, если вам нужно новое выделение памяти для определенного VkImage (либо потому, что последний блок заполнен, либо для изображения требуется новый тип памяти), вы выделяете его большой блок, затем вы можете поместить более поздние изображения в тот же хранилище. Это требует от вас отслеживания того, что вы поместили в какие блоки памяти.

Одна из проблем с вашим кодом заключается в том, что вы не принимаете во внимание требования к выравниванию памяти для изображений (VkMemoryRequirements::alignment). Вы также не рассматриваете возможность того, что не все изображения могут использовать одно и то же распределение; вы предполагаете, что все они могут использовать один и тот же тип памяти.

Поэтому вам нужно будет соответствующим образом изменить свой код.

При этом ограничения, которые Vulkan накладывает на реализации VkImage требований к памяти, включают в себя заявление, в котором фактически говорится, что тип памяти, требуемый изображением для изображений, будет одинаковым для всех цветовых форматов, при условии, что многие другие параметры создания одинаковы. Таким образом, вам не следует беспокоиться о разных типах памяти только для изменения размеров изображений или наличия изображений в разных форматах.

Вещи, которые могут подтолкнуть вас к использованию разных типов памяти, — это в основном параметры использования (и форматы цвета и глубины). Изображения, предназначенные для использования в качестве целей рендеринга, могут иметь собственные типы памяти.

«Вы также не рассматриваете возможность того, что не все изображения могут использовать одно и то же распределение; вы предполагаете, что все они могут использовать один и тот же тип памяти». - У меня есть комментарий во фрагменте кода: «Другие свойства imageCreateInfo постоянны для всех текстур», что означает, что все изображения имеют одинаковые требования, т. е. формат, размещение, использование и т. д. Единственная разница между изображениями ширина и высота, поэтому я считаю, что это не проблема, и, судя по вашему ответу, они могут использовать один и тот же тип памяти. Я мог бы передать это лучше. Спасибо за исчерпывающий ответ.

CasualKyle 13.12.2020 22:09

Другие вопросы по теме