16 int texWidth, texHeight, texChannels;
18 std::vector<unsigned char> pixels =
Image::loadImage(
"../textures/chipi.bmp", texWidth, texHeight, texChannels);
20 std::vector<unsigned char> pixels =
Image::loadImage(
"textures/chipi.bmp", texWidth, texHeight, texChannels);
22 VkDeviceSize imageSize = texWidth * texHeight * 4;
23 mipLevels =
static_cast<uint32_t
>(std::floor(std::log2(std::max(texWidth, texHeight)))) + 1;
26 throw std::runtime_error(
"Failed to load texture image !");
29 VkBuffer stagingBuffer;
30 VkDeviceMemory stagingBufferMemory;
31 createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
34 vkMapMemory(
device, stagingBufferMemory, 0, imageSize, 0, &data);
35 memcpy(data, pixels.data(),
static_cast<size_t>(imageSize));
36 vkUnmapMemory(
device, stagingBufferMemory);
40 createImage(texWidth, texHeight,
mipLevels, VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
textureImage,
textureImageMemory);
45 vkDestroyBuffer(
device, stagingBuffer,
nullptr);
46 vkFreeMemory(
device, stagingBufferMemory,
nullptr);
53 VkFormatProperties formatProperties;
54 vkGetPhysicalDeviceFormatProperties(
physicalDevice, imageFormat, &formatProperties);
56 if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
57 throw std::runtime_error(
"texture image format does not support linear blitting!");
62 VkImageMemoryBarrier barrier{};
63 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
64 barrier.image = image;
65 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
66 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
67 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
68 barrier.subresourceRange.baseArrayLayer = 0;
69 barrier.subresourceRange.layerCount = 1;
70 barrier.subresourceRange.levelCount = 1;
72 int32_t mipWidth = texWidth;
73 int32_t mipHeight = texHeight;
75 for (uint32_t i = 1; i <
mipLevels; i++) {
76 barrier.subresourceRange.baseMipLevel = i - 1;
77 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
78 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
79 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
80 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
82 vkCmdPipelineBarrier(commandBuffer,
83 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
89 blit.srcOffsets[0] = {0, 0, 0};
90 blit.srcOffsets[1] = {mipWidth, mipHeight, 1};
91 blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
92 blit.srcSubresource.mipLevel = i - 1;
93 blit.srcSubresource.baseArrayLayer = 0;
94 blit.srcSubresource.layerCount = 1;
95 blit.dstOffsets[0] = {0, 0, 0};
96 blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 };
97 blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
98 blit.dstSubresource.mipLevel = i;
99 blit.dstSubresource.baseArrayLayer = 0;
100 blit.dstSubresource.layerCount = 1;
102 vkCmdBlitImage(commandBuffer,
103 image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
104 image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
108 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
109 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
110 barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
111 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
113 vkCmdPipelineBarrier(commandBuffer,
114 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
119 if (mipWidth > 1) mipWidth /= 2;
120 if (mipHeight > 1) mipHeight /= 2;
123 barrier.subresourceRange.baseMipLevel =
mipLevels - 1;
124 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
125 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
126 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
127 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
129 vkCmdPipelineBarrier(commandBuffer,
130 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
138void VulkanApp::createImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
139 VkImageCreateInfo imageInfo{};
140 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
141 imageInfo.imageType = VK_IMAGE_TYPE_2D;
142 imageInfo.extent.width = width;
143 imageInfo.extent.height = height;
144 imageInfo.extent.depth = 1;
146 imageInfo.arrayLayers = 1;
147 imageInfo.format = format;
148 imageInfo.tiling = tiling;
149 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
150 imageInfo.usage = usage;
151 imageInfo.samples = numSamples;
152 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
154 if (vkCreateImage(
device, &imageInfo,
nullptr, &image) != VK_SUCCESS) {
155 throw std::runtime_error(
"Failed to create image !");
158 VkMemoryRequirements memRequirements;
159 vkGetImageMemoryRequirements(
device, image, &memRequirements);
161 VkMemoryAllocateInfo allocInfo{};
162 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
163 allocInfo.allocationSize = memRequirements.size;
164 allocInfo.memoryTypeIndex =
findMemoryType(memRequirements.memoryTypeBits, properties);
166 if (vkAllocateMemory(
device, &allocInfo,
nullptr, &imageMemory) != VK_SUCCESS) {
167 throw std::runtime_error(
"Failed to allocate image memory!");
170 vkBindImageMemory(
device, image, imageMemory, 0);
178 VkImageMemoryBarrier barrier{};
179 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
180 barrier.oldLayout = oldLayout;
181 barrier.newLayout = newLayout;
182 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
183 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
184 barrier.image = image;
185 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
186 barrier.subresourceRange.baseMipLevel = 0;
187 barrier.subresourceRange.levelCount =
mipLevels;
188 barrier.subresourceRange.baseArrayLayer = 0;
189 barrier.subresourceRange.layerCount = 1;
191 VkPipelineStageFlags sourceStage;
192 VkPipelineStageFlags destinationStage;
194 if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
195 barrier.srcAccessMask = 0;
196 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
198 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
199 destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
200 }
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
201 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
202 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
204 sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
205 destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
207 throw std::invalid_argument(
"Unsupported layout transition !");
210 vkCmdPipelineBarrier(
212 sourceStage, destinationStage,
225 VkBufferImageCopy region{};
226 region.bufferOffset = 0;
227 region.bufferRowLength = 0;
228 region.bufferImageHeight = 0;
229 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
230 region.imageSubresource.mipLevel = 0;
231 region.imageSubresource.baseArrayLayer = 0;
232 region.imageSubresource.layerCount = 1;
233 region.imageOffset = {0, 0, 0};
234 region.imageExtent = {
240 vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
254 VkPhysicalDeviceProperties physicalDeviceProperties;
255 vkGetPhysicalDeviceProperties(
physicalDevice, &physicalDeviceProperties);
257 VkSampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts;
258 if (counts & VK_SAMPLE_COUNT_64_BIT) {
return VK_SAMPLE_COUNT_64_BIT; }
259 if (counts & VK_SAMPLE_COUNT_32_BIT) {
return VK_SAMPLE_COUNT_32_BIT; }
260 if (counts & VK_SAMPLE_COUNT_16_BIT) {
return VK_SAMPLE_COUNT_16_BIT; }
261 if (counts & VK_SAMPLE_COUNT_8_BIT) {
return VK_SAMPLE_COUNT_8_BIT; }
262 if (counts & VK_SAMPLE_COUNT_4_BIT) {
return VK_SAMPLE_COUNT_4_BIT; }
263 if (counts & VK_SAMPLE_COUNT_2_BIT) {
return VK_SAMPLE_COUNT_2_BIT; }
265 return VK_SAMPLE_COUNT_1_BIT;
273 VkImageViewCreateInfo viewInfo{};
274 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
275 viewInfo.image = image;
276 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
277 viewInfo.format = format;
278 viewInfo.subresourceRange.aspectMask = aspectFlags;
279 viewInfo.subresourceRange.baseMipLevel = 0;
280 viewInfo.subresourceRange.levelCount =
mipLevels;
281 viewInfo.subresourceRange.baseArrayLayer = 0;
282 viewInfo.subresourceRange.layerCount = 1;
284 VkImageView imageView;
285 if (vkCreateImageView(
device, &viewInfo,
nullptr, &imageView) != VK_SUCCESS) {
286 throw std::runtime_error(
"Failed to create image view !");
293 VkPhysicalDeviceProperties properties{};
296 VkSamplerCreateInfo samplerInfo{};
297 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
298 samplerInfo.magFilter = VK_FILTER_LINEAR;
299 samplerInfo.minFilter = VK_FILTER_LINEAR;
300 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
301 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
302 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
303 samplerInfo.anisotropyEnable = VK_TRUE;
304 samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;
305 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
306 samplerInfo.unnormalizedCoordinates = VK_FALSE;
307 samplerInfo.compareEnable = VK_FALSE;
308 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
309 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
310 samplerInfo.minLod = 0.0f;
311 samplerInfo.maxLod = VK_LOD_CLAMP_NONE;
312 samplerInfo.mipLodBias = 0.0f;
315 throw std::runtime_error(
"Failed to create texture sampler !");