SimpleGL  1.1.0
A framework for platform independent rendering
texture_image.cpp
Go to the documentation of this file.
1 
9 
10 #include <cstring>
11 
12 #include <stdexcept>
13 
15 
16 using namespace std;
17 
18 namespace sgl {
19 
20  TextureImage* TextureImage::current;
21 
22  TextureImage* TextureImage::getCurrent() {
23  return current;
24  }
25 
26  void TextureImage::setCurrent(TextureImage* current) {
27  /* Only set if current isn't a nullptr */
28  if (current != nullptr) {
29  TextureImage::current = current;
30  }
31  }
32 
33  TextureImage::TextureImage(uint32_t width, uint32_t height) {
34  VkDevice device = VulkanContext::getCurrent()->getDevice()->getHandle();
35  VkPhysicalDevice physicalDevice = VulkanContext::getCurrent()->getPhysicalDevice()->getHandle();
36  this->width = width;
37  this->height = height;
38 
39  /* Initialize a image create info */
40  VkImageCreateInfo createInfo = {};
41  createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
42  createInfo.imageType = VK_IMAGE_TYPE_2D;
43  createInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
44  createInfo.extent.width = width;
45  createInfo.extent.height = height;
46  createInfo.extent.depth = 1;
47  createInfo.mipLevels = 1;
48  createInfo.arrayLayers = 1;
49  createInfo.samples = VK_SAMPLE_COUNT_1_BIT;
50  createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
51  createInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
52  createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
53  createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
54 
55  /* Create the image */
56  if (vkCreateImage(device, &createInfo, nullptr, &handle) != VK_SUCCESS) {
57  throw runtime_error("Could not create a texture image!");
58  }
59 
60  /* Get memory requirements */
61  VkMemoryRequirements memoryRequirements;
62  vkGetImageMemoryRequirements(device, handle, &memoryRequirements);
63 
64  /* Get the device memory properties */
65  VkPhysicalDeviceMemoryProperties memoryProperties;
66  vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
67 
68  /* Determine memory type index */
69  uint32_t memoryTypeIndex = -1;
70  VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
71  for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) {
72  if ((memoryRequirements.memoryTypeBits & (1 << i)) &&
73  (memoryProperties.memoryTypes[i].propertyFlags & flags) == flags) {
74  memoryTypeIndex = i;
75  break;
76  }
77  }
78 
79  /* Initialize a memory allocate info */
80  VkMemoryAllocateInfo allocateInfo = {};
81  allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
82  allocateInfo.allocationSize = memoryRequirements.size;
83  allocateInfo.memoryTypeIndex = memoryTypeIndex;
84 
85  /* Allocate the texture image memory */
86  if (vkAllocateMemory(device, &allocateInfo, nullptr, &memory) != VK_SUCCESS) {
87  throw runtime_error("Could not allocate texture image memory!");
88  }
89 
90  /* Bind the texture image memory */
91  vkBindImageMemory(device, handle, memory, 0);
92  }
93 
94  TextureImage::~TextureImage(void) {
95  VkDevice device = VulkanContext::getCurrent()->getDevice()->getHandle();
96 
97  /* Destroy the texture objects and free the memory */
98  vkDestroySampler(device, textureSampler, nullptr);
99  vkDestroyImageView(device, imageView, nullptr);
100  vkDestroyImage(device, handle, nullptr);
101  vkFreeMemory(device, memory, nullptr);
102 
103  /* Set current to nullptr if this texture image was used */
104  if (TextureImage::current == this) {
105  TextureImage::current = nullptr;
106  }
107  }
108 
109  VkImage TextureImage::getHandle() {
110  return handle;
111  }
112 
113  VkImageView TextureImage::getImageView() {
114  return imageView;
115  }
116 
117  VkSampler TextureImage::getSampler() {
118  return textureSampler;
119  }
120 
121  uint32_t TextureImage::getWidth() {
122  return width;
123  }
124 
125  uint32_t TextureImage::getHeight() {
126  return height;
127  }
128 
129  void TextureImage::upload(std::vector<uint8_t> data) {
130  VkDevice device = VulkanContext::getCurrent()->getDevice()->getHandle();
131  VkPhysicalDevice physicalDevice = VulkanContext::getCurrent()->getPhysicalDevice()->getHandle();
132 
133  /* Initialize a staging buffer */
134  VkBuffer stagingBuffer;
135  VkDeviceMemory stagingBufferMemory;
136 
137  /* Initialize a buffer create info */
138  VkBufferCreateInfo bufferInfo = {};
139  bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
140  bufferInfo.size = data.size();
141  bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
142  bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
143 
144  /* Create a staging buffer */
145  if (vkCreateBuffer(device, &bufferInfo, nullptr, &stagingBuffer) != VK_SUCCESS) {
146  throw runtime_error("Could not create a staging buffer!");
147  }
148 
149  /* Get memory requirements */
150  VkMemoryRequirements memoryRequirements;
151  vkGetBufferMemoryRequirements(device, stagingBuffer, &memoryRequirements);
152 
153  /* Get the device memory properties */
154  VkPhysicalDeviceMemoryProperties memoryProperties;
155  vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
156 
157  /* Determine memory type index */
158  uint32_t memoryTypeIndex = -1;
159  VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
160  for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) {
161  if ((memoryRequirements.memoryTypeBits & (1 << i)) &&
162  (memoryProperties.memoryTypes[i].propertyFlags & flags) == flags) {
163  memoryTypeIndex = i;
164  break;
165  }
166  }
167 
168  /* Initialize a memory allocate info */
169  VkMemoryAllocateInfo allocateInfo = {};
170  allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
171  allocateInfo.allocationSize = memoryRequirements.size;
172  allocateInfo.memoryTypeIndex = memoryTypeIndex;
173 
174  /* Allocate staging buffer memory */
175  if (vkAllocateMemory(device, &allocateInfo, nullptr, &stagingBufferMemory) != VK_SUCCESS) {
176  throw std::runtime_error("failed to allocate buffer memory!");
177  }
178 
179  /* Bind the staging buffer memory */
180  vkBindBufferMemory(device, stagingBuffer, stagingBufferMemory, 0);
181 
182  /* Upload data to staging buffer */
183  void* mapData;
184  vkMapMemory(device, stagingBufferMemory, 0, data.size(), 0, &mapData);
185  memcpy(mapData, data.data(), data.size());
186  vkUnmapMemory(device, stagingBufferMemory);
187 
188  /* Upload image data from the staging buffer to the image with layout transitions */
189  perfomImageLayoutTransition(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
190  copyBufferToImage(stagingBuffer, handle);
191  perfomImageLayoutTransition(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
192 
193  /* Free the staging buffer and its memory */
194  vkDestroyBuffer(device, stagingBuffer, nullptr);
195  vkFreeMemory(device, stagingBufferMemory, nullptr);
196 
197  /* Initialize a image view create info */
198  VkImageViewCreateInfo viewInfo = {};
199  viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
200  viewInfo.image = handle;
201  viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
202  viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
203  viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
204  viewInfo.subresourceRange.baseMipLevel = 0;
205  viewInfo.subresourceRange.levelCount = 1;
206  viewInfo.subresourceRange.baseArrayLayer = 0;
207  viewInfo.subresourceRange.layerCount = 1;
208 
209  /* Create the image view */
210  if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
211  throw runtime_error("Could not create a texture image view!");
212  }
213 
214  /* Create a sampler create info */
215  VkSamplerCreateInfo samplerInfo = {};
216  samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
217  samplerInfo.magFilter = VK_FILTER_LINEAR;
218  samplerInfo.minFilter = VK_FILTER_LINEAR;
219  samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
220  samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
221  samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
222  samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
223  samplerInfo.mipLodBias = 0.0f;
224  samplerInfo.anisotropyEnable = VK_FALSE;
225  samplerInfo.maxAnisotropy = 1;
226  samplerInfo.compareEnable = VK_FALSE;
227  samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
228  samplerInfo.minLod = 0.0f;
229  samplerInfo.maxLod = 0.0f;
230  samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
231  samplerInfo.unnormalizedCoordinates = VK_FALSE;
232 
233  /* Create the sampler */
234  if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
235  throw runtime_error("Could not create a texture sampler!");
236  }
237  }
238 
239  void TextureImage::perfomImageLayoutTransition(VkImageLayout oldLayout, VkImageLayout newLayout) {
240  VkDevice device = VulkanContext::getCurrent()->getDevice()->getHandle();
241  VkCommandPool commandPool = VulkanContext::getCurrent()->getCommandPool()->getHandle();
242  VkQueue queue = VulkanContext::getCurrent()->getDevice()->getQueues().graphics;
243 
244  /* Initialize a command buffer allocate info */
245  VkCommandBufferAllocateInfo allocateInfo = {};
246  allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
247  allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
248  allocateInfo.commandPool = commandPool;
249  allocateInfo.commandBufferCount = 1;
250 
251  /* Allocate a temporary command buffer */
252  VkCommandBuffer commandBuffer;
253  vkAllocateCommandBuffers(device, &allocateInfo, &commandBuffer);
254 
255  /* Initialize a command buffer begin info */
256  VkCommandBufferBeginInfo beginInfo = {};
257  beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
258  beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
259 
260  /* Begin recording */
261  vkBeginCommandBuffer(commandBuffer, &beginInfo);
262 
263  /* Determine the transition barrier masks */
264  VkAccessFlags sourceAccessMask;
265  VkAccessFlags destinationAccessMask;
266  VkPipelineStageFlags sourceStage;
267  VkPipelineStageFlags destinationStage;
268  if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
269  sourceAccessMask = 0;
270  destinationAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
271  sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
272  destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
273  } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
274  sourceAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
275  destinationAccessMask = VK_ACCESS_SHADER_READ_BIT;
276  sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
277  destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
278  } else {
279  throw invalid_argument("Unsupported layout transition!");
280  }
281 
282  /* Initialize a image memory barrier */
283  VkImageMemoryBarrier barrier = {};
284  barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
285  barrier.srcAccessMask = sourceAccessMask;
286  barrier.dstAccessMask = destinationAccessMask;
287  barrier.oldLayout = oldLayout;
288  barrier.newLayout = newLayout;
289  barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
290  barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
291  barrier.image = handle;
292  barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
293  barrier.subresourceRange.baseMipLevel = 0;
294  barrier.subresourceRange.levelCount = 1;
295  barrier.subresourceRange.baseArrayLayer = 0;
296  barrier.subresourceRange.layerCount = 1;
297 
298  /* Pipeline barrier */
299  vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
300 
301  /* End recording */
302  vkEndCommandBuffer(commandBuffer);
303 
304  /* Initialize a submit info */
305  VkSubmitInfo submitInfo = {};
306  submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
307  submitInfo.commandBufferCount = 1;
308  submitInfo.pCommandBuffers = &commandBuffer;
309 
310  /* Submit the command */
311  vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
312  vkQueueWaitIdle(queue);
313 
314  /* Free the temporary command buffer */
315  vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
316  }
317 
318  void TextureImage::copyBufferToImage(VkBuffer stagingBuffer, VkImage image) {
319  VkDevice device = VulkanContext::getCurrent()->getDevice()->getHandle();
320  VkCommandPool commandPool = VulkanContext::getCurrent()->getCommandPool()->getHandle();
321  VkQueue queue = VulkanContext::getCurrent()->getDevice()->getQueues().graphics;
322 
323  /* Initialize a command buffer allocate info */
324  VkCommandBufferAllocateInfo allocateInfo = {};
325  allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
326  allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
327  allocateInfo.commandPool = commandPool;
328  allocateInfo.commandBufferCount = 1;
329 
330  /* Allocate a temporary command buffer */
331  VkCommandBuffer commandBuffer;
332  vkAllocateCommandBuffers(device, &allocateInfo, &commandBuffer);
333 
334  /* Initialize a command buffer begin info */
335  VkCommandBufferBeginInfo beginInfo = {};
336  beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
337  beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
338 
339  /* Begin recording */
340  vkBeginCommandBuffer(commandBuffer, &beginInfo);
341 
342  /* Initialize a buffer image copy */
343  VkBufferImageCopy region = {};
344  region.bufferOffset = 0;
345  region.bufferRowLength = 0;
346  region.bufferImageHeight = 0;
347  region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
348  region.imageSubresource.mipLevel = 0;
349  region.imageSubresource.baseArrayLayer = 0;
350  region.imageSubresource.layerCount = 1;
351  region.imageOffset = {0, 0, 0};
352  region.imageExtent = {width, height, 1};
353 
354  /* Copy buffer to image */
355  vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
356 
357  /* End recording */
358  vkEndCommandBuffer(commandBuffer);
359 
360  /* Initialize a submit info */
361  VkSubmitInfo submitInfo = {};
362  submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
363  submitInfo.commandBufferCount = 1;
364  submitInfo.pCommandBuffers = &commandBuffer;
365 
366  /* Submit the command */
367  vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
368  vkQueueWaitIdle(queue);
369 
370  /* Free the temporary command buffer */
371  vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
372  }
373 }
VkImage getHandle()
Returns the handle of the texture image.
Generic namespace for the SimpleGL framework.
Definition: application.hpp:18
This class wraps a Vulkan texture image.