scop
a small 3D object loader
Loading...
Searching...
No Matches
VulkanTexture.cpp
Go to the documentation of this file.
1/* ************************************************************************** */
2/* */
3/* ::: :::::::: */
4/* VulkanTexture.cpp :+: :+: :+: */
5/* +:+ +:+ +:+ */
6/* By: rbourgea <rbourgea@student.42.fr> +#+ +:+ +#+ */
7/* +#+#+#+#+#+ +#+ */
8/* Created: 2024/01/14 19:54:29 by rbourgea #+# #+# */
9/* Updated: 2024/04/08 10:39:54 by rbourgea ### ########.fr */
10/* */
11/* ************************************************************************** */
12
13#include "VulkanApp.hpp"
14
16 int texWidth, texHeight, texChannels;
17#ifdef __APPLE__
18 std::vector<unsigned char> pixels = Image::loadImage("../textures/chipi.bmp", texWidth, texHeight, texChannels);
19#else
20 std::vector<unsigned char> pixels = Image::loadImage("textures/chipi.bmp", texWidth, texHeight, texChannels);
21#endif
22 VkDeviceSize imageSize = texWidth * texHeight * 4;
23 mipLevels = static_cast<uint32_t>(std::floor(std::log2(std::max(texWidth, texHeight)))) + 1;
24
25 if (pixels.empty()) {
26 throw std::runtime_error("Failed to load texture image !");
27 }
28
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);
32
33 void* data;
34 vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data);
35 memcpy(data, pixels.data(), static_cast<size_t>(imageSize));
36 vkUnmapMemory(device, stagingBufferMemory);
37
38 Image::freeImage(pixels);
39
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);
41
42 transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels);
43 copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
44
45 vkDestroyBuffer(device, stagingBuffer, nullptr);
46 vkFreeMemory(device, stagingBufferMemory, nullptr);
47
48 generateMipmaps(textureImage, VK_FORMAT_R8G8B8A8_SRGB, texWidth, texHeight, mipLevels);
49}
50
51void VulkanApp::generateMipmaps(VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels) {
52 // Check if image format supports linear blitting
53 VkFormatProperties formatProperties;
54 vkGetPhysicalDeviceFormatProperties(physicalDevice, imageFormat, &formatProperties);
55
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!");
58 }
59
60 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
61
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;
71
72 int32_t mipWidth = texWidth;
73 int32_t mipHeight = texHeight;
74
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;
81
82 vkCmdPipelineBarrier(commandBuffer,
83 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
84 0, nullptr,
85 0, nullptr,
86 1, &barrier);
87
88 VkImageBlit blit{};
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;
101
102 vkCmdBlitImage(commandBuffer,
103 image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
104 image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
105 1, &blit,
106 VK_FILTER_LINEAR);
107
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;
112
113 vkCmdPipelineBarrier(commandBuffer,
114 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
115 0, nullptr,
116 0, nullptr,
117 1, &barrier);
118
119 if (mipWidth > 1) mipWidth /= 2;
120 if (mipHeight > 1) mipHeight /= 2;
121 }
122
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;
128
129 vkCmdPipelineBarrier(commandBuffer,
130 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
131 0, nullptr,
132 0, nullptr,
133 1, &barrier);
134
135 endSingleTimeCommands(commandBuffer);
136}
137
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;
145 imageInfo.mipLevels = mipLevels;
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;
153
154 if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) {
155 throw std::runtime_error("Failed to create image !");
156 }
157
158 VkMemoryRequirements memRequirements;
159 vkGetImageMemoryRequirements(device, image, &memRequirements);
160
161 VkMemoryAllocateInfo allocInfo{};
162 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
163 allocInfo.allocationSize = memRequirements.size;
164 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
165
166 if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
167 throw std::runtime_error("Failed to allocate image memory!");
168 }
169
170 vkBindImageMemory(device, image, imageMemory, 0);
171}
172
173void VulkanApp::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels) {
174 (void)format;
175
176 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
177
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;
190
191 VkPipelineStageFlags sourceStage;
192 VkPipelineStageFlags destinationStage;
193
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;
197
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;
203
204 sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
205 destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
206 } else {
207 throw std::invalid_argument("Unsupported layout transition !");
208 }
209
210 vkCmdPipelineBarrier(
211 commandBuffer,
212 sourceStage, destinationStage,
213 0,
214 0, nullptr,
215 0, nullptr,
216 1, &barrier
217 );
218
219 endSingleTimeCommands(commandBuffer);
220}
221
222void VulkanApp::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
223 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
224
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 = {
235 width,
236 height,
237 1
238 };
239
240 vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
241
242 endSingleTimeCommands(commandBuffer);
243}
244
247
248 for (uint32_t i = 0; i < swapChainImages.size(); i++) {
249 swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);
250 }
251}
252
253VkSampleCountFlagBits VulkanApp::getMaxUsableSampleCount() {
254 VkPhysicalDeviceProperties physicalDeviceProperties;
255 vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties);
256
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; }
264
265 return VK_SAMPLE_COUNT_1_BIT;
266}
267
269 textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, mipLevels);
270}
271
272VkImageView VulkanApp::createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels) {
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;
283
284 VkImageView imageView;
285 if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
286 throw std::runtime_error("Failed to create image view !");
287 }
288
289 return imageView;
290}
291
293 VkPhysicalDeviceProperties properties{};
294 vkGetPhysicalDeviceProperties(physicalDevice, &properties);
295
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; // Optional
311 samplerInfo.maxLod = VK_LOD_CLAMP_NONE;
312 samplerInfo.mipLodBias = 0.0f; // Optional
313
314 if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
315 throw std::runtime_error("Failed to create texture sampler !");
316 }
317}
318
319VkFormat VulkanApp::findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {
320 for (VkFormat format : candidates) {
321 VkFormatProperties props;
322 vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);
323
324 if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
325 return format;
326 } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) {
327 return format;
328 }
329 }
330
331 throw std::runtime_error("Failed to find supported format !");
332}
333
335 return findSupportedFormat(
336 {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT},
337 VK_IMAGE_TILING_OPTIMAL,
338 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
339 );
340}
341
342bool VulkanApp::hasStencilComponent(VkFormat format) {
343 return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
344}
345
347 float new_dissolve = vertices[0].dissolveTexture + (disable_textures ? -1.f : 1.f) * 0.01f;
348
349 if (disable_textures && new_dissolve < 0) {
350 new_dissolve = 0;
351 transition_over = true;
352 } else if (!disable_textures && new_dissolve > 1) {
353 new_dissolve = 1;
354 transition_over = true;
355 }
356
357 for (auto& vertex : vertices) {
358 vertex.dissolveTexture = new_dissolve;
359 }
360
361 if (colorMode == DARK) {
363 }
365}
static void freeImage(std::vector< unsigned char > &imageData)
Definition Image.hpp:74
static std::vector< unsigned char > loadImage(const char *filename, int &width, int &height, int &channels)
Definition Image.hpp:23
uint32_t mipLevels
VkDevice device
VkImageView textureImageView
void createImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage &image, VkDeviceMemory &imageMemory)
void updateVertexBuffer()
VkPhysicalDevice physicalDevice
VkFormat findSupportedFormat(const std::vector< VkFormat > &candidates, VkImageTiling tiling, VkFormatFeatureFlags features)
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels)
void createTextureImageView()
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
VkSampler textureSampler
VkImage textureImage
VkDeviceMemory textureImageMemory
std::vector< VkImage > swapChainImages
ColorMode colorMode
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, VkDeviceMemory &bufferMemory)
void transitionTextures()
void createTextureImage()
bool hasStencilComponent(VkFormat format)
void createImageViews()
void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height)
void enableDarkMode()
VkCommandBuffer beginSingleTimeCommands()
VkFormat findDepthFormat()
void endSingleTimeCommands(VkCommandBuffer commandBuffer)
void createTextureSampler()
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels)
std::vector< Vertex > vertices
std::vector< VkImageView > swapChainImageViews
void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels)
bool disable_textures
bool transition_over
VkFormat swapChainImageFormat
VkSampleCountFlagBits getMaxUsableSampleCount()