Lugdunum  0.1.0
Texture.cpp
Go to the documentation of this file.
2 
10 
11 namespace lug {
12 namespace Graphics {
13 namespace Vulkan {
14 namespace Builder {
15 namespace Texture {
16 
17 Resource::SharedPtr<::lug::Graphics::Render::Texture> build(const ::lug::Graphics::Builder::Texture& builder) {
18  // Constructor of Texture is private, we can't use std::make_unique
19  std::unique_ptr<Resource> resource{new Vulkan::Render::Texture(builder._name)};
20  Vulkan::Render::Texture* texture = static_cast<Vulkan::Render::Texture*>(resource.get());
21 
22  if (!builder._layers.size()) {
23  LUG_LOG.error("Vulkan::Texture::build: Not layers added");
24  return nullptr;
25  }
26 
27  Vulkan::Renderer& renderer = static_cast<Vulkan::Renderer&>(builder._renderer);
28  API::Device &device = renderer.getDevice();
29 
30  // Get transfer queue family and retrieve the first queue
31  const API::Queue* transferQueue = nullptr;
32  {
33  transferQueue = device.getQueue("queue_transfer");
34  if (!transferQueue) {
35  LUG_LOG.error("Vulkan::Texture::build: Can't find transfer queue");
36  return nullptr;
37  }
38  }
39 
40  // Create command pool of transfer queue
41  API::CommandPool transferQueueCommandPool;
42  {
43  VkResult result{VK_SUCCESS};
44  API::Builder::CommandPool commandPoolBuilder(device, *transferQueue->getQueueFamily());
45  if (!commandPoolBuilder.build(transferQueueCommandPool, &result)) {
46  LUG_LOG.error("Vulkan::Texture::build: Can't create a command pool: {}", result);
47  return nullptr;
48  }
49  }
50 
51  // Create the API::Image
52  {
53  API::Builder::Image imageBuilder(device);
54 
55  const VkFormat format = [](Render::Texture::Format format) {
56  switch(format) {
58  return VK_FORMAT_R8G8B8A8_UNORM;
59 
61  return VK_FORMAT_R16G16_SFLOAT;
62 
64  return VK_FORMAT_R16G16B16_SFLOAT;
65 
67  return VK_FORMAT_R32G32B32A32_SFLOAT;
68 
69  default:
70  return VK_FORMAT_UNDEFINED;
71  };
72  }(builder._format);
73 
74  // TODO: Take the usage from the builder (TRANSFER_DST only if we want to copy image to it, etc)
75  imageBuilder.setUsage(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
76  imageBuilder.setPreferedFormats({format});
77  imageBuilder.setFeatureFlags(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
78  imageBuilder.setQueueFamilyIndices({ transferQueue->getQueueFamily()->getIdx() });
79  imageBuilder.setTiling(VK_IMAGE_TILING_OPTIMAL);
80  imageBuilder.setMipLevels(builder._mipLevels);
81  imageBuilder.setArrayLayers(static_cast<uint32_t>(builder._layers.size()));
82 
83  API::Builder::DeviceMemory deviceMemoryBuilder(device);
84  deviceMemoryBuilder.setMemoryFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
85 
86  VkExtent3D extent{
87  /* extent.width */ static_cast<uint32_t>(builder._width),
88  /* extent.height */ static_cast<uint32_t>(builder._height),
89  /* extent.depth */ 1
90  };
91 
92  imageBuilder.setExtent(extent);
93 
94  if (builder._type == ::lug::Graphics::Builder::Texture::Type::CubeMap) {
95  imageBuilder.setCreateFlags(VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT);
96  }
97 
98  {
99  VkResult result{VK_SUCCESS};
100  if (!imageBuilder.build(texture->_image, &result)) {
101  LUG_LOG.error("Vulkan::Texture::build: Can't create the image: {}", result);
102  return nullptr;
103  }
104 
105  if (!deviceMemoryBuilder.addImage(texture->_image)) {
106  LUG_LOG.error("Vulkan::Texture::build: Can't add image to device memory");
107  return nullptr;
108  }
109 
110  result = VK_SUCCESS;
111  if (!deviceMemoryBuilder.build(texture->_deviceMemory, &result)) {
112  LUG_LOG.error("Vulkan::Texture::build: Can't create buffer device memory: {}", result);
113  return nullptr;
114  }
115  }
116  }
117 
118  // Create image view
119  {
120  API::Builder::ImageView imageViewBuilder(device, texture->_image);
121 
122  imageViewBuilder.setFormat(texture->_image.getFormat());
123  imageViewBuilder.setAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT);
124  imageViewBuilder.setLayerCount(static_cast<uint32_t>(builder._layers.size()));
125  imageViewBuilder.setLevelCount(builder._mipLevels);
126 
127  if (builder._type == ::lug::Graphics::Builder::Texture::Type::CubeMap) {
128  imageViewBuilder.setViewType(VK_IMAGE_VIEW_TYPE_CUBE);
129  }
130 
131  VkResult result{VK_SUCCESS};
132  if (!imageViewBuilder.build(texture->_imageView, &result)) {
133  LUG_LOG.error("Vulkan::Texture::build: Can't create image view: {}", result);
134  return nullptr;
135  }
136  }
137 
138  uint32_t nbLayersWithData = 0;
139  for (const auto& layer : builder._layers) {
140  if (layer.data) {
141  ++nbLayersWithData;
142  }
143  }
144 
145  // Create staging buffers for image upload
146  // The number of layers is not neccessarily equals to builder._layers.size() (5 layers and 2 filenames)
147  if (nbLayersWithData)
148  {
149  const VkDeviceSize layerSize = builder._width * builder._height * Render::Texture::formatToSize(builder._format);
150  const VkDeviceSize bufferSize = layerSize * nbLayersWithData;
151 
152  API::Buffer stagingBuffer;
153  API::DeviceMemory stagingBufferMemory;
154  std::set<uint32_t> queueFamilyIndices = { transferQueue->getQueueFamily()->getIdx() };
155 
156  // Create staging buffer
157  {
158  API::Builder::Buffer bufferBuilder(device);
159  bufferBuilder.setQueueFamilyIndices(queueFamilyIndices);
160  bufferBuilder.setSize(bufferSize);
161  bufferBuilder.setUsage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
162  bufferBuilder.setExclusive(VK_SHARING_MODE_EXCLUSIVE);
163 
164  VkResult result{VK_SUCCESS};
165  if (!bufferBuilder.build(stagingBuffer, &result)) {
166  LUG_LOG.error("Vulkan::Texture::build: Can't create staging buffer: {}", result);
167  return nullptr;
168  }
169  }
170 
171  // Create staging buffer memory
172  {
173  API::Builder::DeviceMemory deviceMemoryBuilder(device);
174  deviceMemoryBuilder.setMemoryFlags(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
175  deviceMemoryBuilder.addBuffer(stagingBuffer);
176 
177  VkResult result{VK_SUCCESS};
178  if (!deviceMemoryBuilder.build(stagingBufferMemory, &result)) {
179  LUG_LOG.error("Vulkan::Texture::build: Can't create staging buffer device memory: {}", result);
180  return nullptr;
181  }
182  }
183 
184  // Update buffer data
185  {
186  VkDeviceSize pixelsOffset{0};
187  for (const auto& layer : builder._layers) {
188  if (!layer.data) {
189  continue;
190  }
191 
192  stagingBuffer.updateData(layer.data, layerSize, pixelsOffset);
193  pixelsOffset += layerSize;
194  }
195  }
196 
197 
198  // Copy buffer data to font image
199  {
200  VkResult result{VK_SUCCESS};
201  API::CommandBuffer commandBuffer;
202 
203  API::Builder::CommandBuffer commandBufferBuilder(device, transferQueueCommandPool);
204  commandBufferBuilder.setLevel(VK_COMMAND_BUFFER_LEVEL_PRIMARY);
205 
206  if (!commandBufferBuilder.build(commandBuffer, &result)) {
207  LUG_LOG.error("Gui::init: Can't create the command buffer: {}", result);
208  return nullptr;
209  }
210 
211  // Create fence
212  API::Fence fence;
213  {
214  API::Builder::Fence fenceBuilder(device);
215 
216  if (!fenceBuilder.build(fence, &result)) {
217  LUG_LOG.error("Vulkan::Texture::build: Can't create swapchain fence: {}", result);
218  return nullptr;
219  }
220  }
221 
222  commandBuffer.begin();
223 
224  // Prepare for transfer
225  {
226  API::CommandBuffer::CmdPipelineBarrier pipelineBarrier;
227  pipelineBarrier.imageMemoryBarriers.resize(1);
228  pipelineBarrier.imageMemoryBarriers[0].srcAccessMask = 0;
229  pipelineBarrier.imageMemoryBarriers[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
230  pipelineBarrier.imageMemoryBarriers[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
231  pipelineBarrier.imageMemoryBarriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
232  pipelineBarrier.imageMemoryBarriers[0].image = &texture->_image;
233  pipelineBarrier.imageMemoryBarriers[0].subresourceRange.layerCount = static_cast<uint32_t>(builder._layers.size());
234 
235  commandBuffer.pipelineBarrier(pipelineBarrier);
236  }
237 
238  std::vector<VkBufferImageCopy> bufferCopyRegions(nbLayersWithData);
239  VkDeviceSize pixelsOffset{0};
240  uint32_t i = 0;
241  for (uint32_t layerNb = 0; layerNb < builder._layers.size(); ++layerNb) {
242  if (!builder._layers[layerNb].data) {
243  continue;
244  }
245 
246  // Copy
247  bufferCopyRegions[i++] = {
248  /* bufferCopyRegion.bufferOffset */ pixelsOffset,
249  /* bufferCopyRegion.bufferRowLength */ 0,
250  /* bufferCopyRegion.bufferImageHeight */ 0,
251  {
252  /* bufferCopyRegion.imageSubresource.aspectMask */ VK_IMAGE_ASPECT_COLOR_BIT,
253  /* bufferCopyRegion.imageSubresource.mipLevel */ 0,
254  /* bufferCopyRegion.imageSubresource.baseArrayLayer */ layerNb,
255  /* bufferCopyRegion.imageSubresource.layerCount */ 1
256  },
257  {
258  /* bufferCopyRegion.imageOffset.x */ 0,
259  /* bufferCopyRegion.imageOffset.y */ 0,
260  /* bufferCopyRegion.imageOffset.z */ 0,
261  },
262  {
263  /* bufferCopyRegion.imageExtent.width */ static_cast<uint32_t>(builder._width),
264  /* bufferCopyRegion.imageExtent.height */ static_cast<uint32_t>(builder._height),
265  /* bufferCopyRegion.imageExtent.depth */ 1
266  }
267  };
268 
269  pixelsOffset += layerSize;
270  }
271 
272 
273  // TODO write this function commandBuffer.copyBufferToImage();
274 
275  vkCmdCopyBufferToImage(
276  static_cast<VkCommandBuffer>(commandBuffer),
277  static_cast<VkBuffer>(stagingBuffer),
278  static_cast<VkImage>(texture->_image),
279  VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
280  static_cast<uint32_t>(bufferCopyRegions.size()),
281  bufferCopyRegions.data()
282  );
283 
284  // Prepare for shader read
285  {
286  API::CommandBuffer::CmdPipelineBarrier pipelineBarrier;
287  pipelineBarrier.imageMemoryBarriers.resize(1);
288  pipelineBarrier.imageMemoryBarriers[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
289  pipelineBarrier.imageMemoryBarriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
290  pipelineBarrier.imageMemoryBarriers[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
291  pipelineBarrier.imageMemoryBarriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
292  pipelineBarrier.imageMemoryBarriers[0].image = &texture->_image;
293  pipelineBarrier.imageMemoryBarriers[0].subresourceRange.layerCount = static_cast<uint32_t>(builder._layers.size());
294 
295  commandBuffer.pipelineBarrier(pipelineBarrier);
296  }
297 
298  if (commandBuffer.end() == false) {
299  LUG_LOG.error("Vulkan::Texture::build: Failed to end commandBuffer");
300  return nullptr;
301  }
302 
303  if (transferQueue->submit(commandBuffer, {}, {}, {}, static_cast<VkFence>(fence)) == false) {
304  LUG_LOG.error("Vulkan::Texture::build: Can't submit commandBuffer");
305  return nullptr;
306  }
307 
308  // TODO(saveman71): set a define for the fence timeout
309  if (!fence.wait()) {
310  LUG_LOG.error("Vulkan::Texture::build: Can't vkWaitForFences");
311  return nullptr;
312  }
313 
314  // Properly destroy everything in the right order
315  fence.destroy();
316  stagingBufferMemory.destroy();
317  stagingBuffer.destroy();
318  commandBuffer.destroy();
319  }
320  }
321 
322  // Create Sampler
323  {
324  API::Builder::Sampler samplerBuilder(device);
325 
326  const auto& wrappingModeToVulkan = [](Render::Texture::WrappingMode wrappingMode){
327  switch(wrappingMode) {
329  return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
331  return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
333  return VK_SAMPLER_ADDRESS_MODE_REPEAT;
334  }
335 
336  return VkSamplerAddressMode{};
337  };
338 
339  samplerBuilder.setAddressModeU(wrappingModeToVulkan(builder._wrapS));
340  samplerBuilder.setAddressModeV(wrappingModeToVulkan(builder._wrapT));
341  samplerBuilder.setAddressModeW(wrappingModeToVulkan(builder._wrapW));
342 
343  const auto& filterToVulkan = [](Render::Texture::Filter filter){
344  switch(filter) {
346  return VK_FILTER_NEAREST;
348  return VK_FILTER_LINEAR;
349  }
350 
351  return VkFilter{};
352  };
353 
354  samplerBuilder.setMinFilter(filterToVulkan(builder._minFilter));
355  samplerBuilder.setMagFilter(filterToVulkan(builder._magFilter));
356 
357  samplerBuilder.setMipmapMode([](Render::Texture::Filter filter){
358  switch(filter) {
360  return VK_SAMPLER_MIPMAP_MODE_NEAREST;
362  return VK_SAMPLER_MIPMAP_MODE_LINEAR;
363  }
364 
365  return VkSamplerMipmapMode{};
366  }(builder._mipMapFilter));
367 
368  samplerBuilder.setMaxLod(static_cast<float>(builder._mipLevels));
369 
370  VkResult result{VK_SUCCESS};
371  if (!samplerBuilder.build(texture->_sampler, &result)) {
372  LUG_LOG.error("Gui::initFontsTexture: Can't create image view: {}", result);
373  return nullptr;
374  }
375  }
376 
377  return builder._renderer.getResourceManager()->add<::lug::Graphics::Render::Texture>(std::move(resource));
378 }
379 
380 } // Texture
381 } // Builder
382 } // Vulkan
383 } // Graphics
384 } // lug
void setAspectFlags(VkImageAspectFlags aspectFlags)
Definition: ImageView.inl:9
bool build(API::DeviceMemory &instance, VkResult *returnResult=nullptr)
void setMipmapMode(VkSamplerMipmapMode mipmapMode)
Definition: Sampler.inl:9
bool build(API::Sampler &instance, VkResult *returnResult=nullptr)
Definition: Sampler.cpp:13
bool build(API::ImageView &instance, VkResult *returnResult=nullptr)
Definition: ImageView.cpp:14
bool build(API::CommandBuffer &instance, VkResult *returnResult=nullptr)
void setMipLevels(uint32_t mipLevels)
Definition: Image.inl:21
void setAddressModeU(VkSamplerAddressMode addressModeU)
Definition: Sampler.inl:13
void setFeatureFlags(VkFormatFeatureFlags featureFlags)
Definition: Image.inl:13
void pipelineBarrier(const CmdPipelineBarrier &parameters, VkDependencyFlags dependencyFlags=VK_DEPENDENCY_BY_REGION_BIT, VkPipelineStageFlags srcStageMask=VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VkPipelineStageFlags dstStageMask=VK_PIPELINE_STAGE_ALL_COMMANDS_BIT) const
void setQueueFamilyIndices(std::set< uint32_t > queueFamilyIndices)
Definition: Buffer.inl:17
const API::Queue * getQueue(const std::string &queueName) const
Definition: Device.cpp:70
void setExtent(VkExtent3D extent)
Definition: Image.inl:17
Resource::SharedPtr< lug::Graphics::Render::Texture > build(const ::lug::Graphics::Builder::Texture &builder)
Definition: Texture.cpp:17
bool submit(const CommandBuffer &commandBuffer, const std::vector< VkSemaphore > &signalSemaphores={}, const std::vector< VkSemaphore > &waitSemaphores={}, const std::vector< VkPipelineStageFlags > &waitDstStageMasks={}, VkFence fence=VK_NULL_HANDLE) const
Definition: Queue.cpp:32
void setViewType(VkImageViewType viewType)
Definition: ImageView.inl:5
void setMagFilter(VkFilter magFilter)
Definition: Sampler.inl:1
bool build(API::Fence &instance, VkResult *returnResult=nullptr)
Definition: Fence.cpp:13
void setLevel(VkCommandBufferLevel level)
static size_t formatToSize(Render::Texture::Format format)
Definition: Texture.inl:33
bool build(API::Buffer &instance, VkResult *returnResult=nullptr)
Definition: Buffer.cpp:15
Dummy class for a shared pointer.
Definition: Resource.hpp:66
void setSize(VkDeviceSize size)
Definition: Buffer.inl:1
void setAddressModeW(VkSamplerAddressMode addressModeW)
Definition: Sampler.inl:21
void setAddressModeV(VkSamplerAddressMode addressModeV)
Definition: Sampler.inl:17
void setTiling(VkImageTiling tiling)
Definition: Image.inl:33
const QueueFamily * getQueueFamily() const
Definition: Queue.cpp:86
void setUsage(VkImageUsageFlags usage)
Definition: Image.inl:37
void setQueueFamilyIndices(const std::set< uint32_t > &queueFamilyIndices)
Definition: Image.inl:45
VkFormat getFormat() const
Definition: Image.inl:13
void setMinFilter(VkFilter minFilter)
Definition: Sampler.inl:5
void setCreateFlags(VkImageCreateFlags createFlags)
Definition: Image.inl:1
void setMemoryFlags(VkMemoryPropertyFlags flags)
Definition: DeviceMemory.inl:1
#define LUG_LOG
Definition: Logger.hpp:73
void setUsage(VkBufferUsageFlags usage)
Definition: Buffer.inl:9
void setPreferedFormats(const std::set< VkFormat > &preferedFormats)
Definition: Image.inl:9
bool build(API::Image &instance, VkResult *returnResult=nullptr)
Definition: Image.cpp:16
bool begin(VkCommandBufferUsageFlags flags=VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) const
void setArrayLayers(uint32_t arrayLayers)
Definition: Image.inl:25
bool updateData(const void *data, VkDeviceSize size, VkDeviceSize offset=0) const
Definition: Buffer.cpp:67