Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7216342

Browse files
committed
Remove VDeleter from texture mapping chapters
1 parent 4b532b8 commit 7216342

7 files changed

Lines changed: 582 additions & 503 deletions

File tree

06_Texture_mapping/00_Images.md

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ as texels and we'll use that name from this point on. Add the following two
148148
variables in the `createTextureImage` function:
149149

150150
```c++
151-
VDeleter<VkImage> stagingImage{device, vkDestroyImage};
152-
VDeleter<VkDeviceMemory> stagingImageMemory{device, vkFreeMemory};
151+
VkImage stagingImage;
152+
VkDeviceMemory stagingImageMemory;
153153
```
154154

155155
The parameters for an image are specified in a `VkImageCreateInfo` struct:
@@ -244,7 +244,7 @@ avoid allocating memory to store large volumes of "air" values. We won't be
244244
using it in this tutorial, so leave it to its default value of `0`.
245245

246246
```c++
247-
if (vkCreateImage(device, &imageInfo, nullptr, stagingImage.replace()) != VK_SUCCESS) {
247+
if (vkCreateImage(device, &imageInfo, nullptr, &stagingImage) != VK_SUCCESS) {
248248
throw std::runtime_error("failed to create image!");
249249
}
250250
```
@@ -266,7 +266,7 @@ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
266266
allocInfo.allocationSize = memRequirements.size;
267267
allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
268268

269-
if (vkAllocateMemory(device, &allocInfo, nullptr, stagingImageMemory.replace()) != VK_SUCCESS) {
269+
if (vkAllocateMemory(device, &allocInfo, nullptr, &stagingImageMemory) != VK_SUCCESS) {
270270
throw std::runtime_error("failed to allocate image memory!");
271271
}
272272

@@ -378,7 +378,7 @@ for buffers. Create the function and move the image object creation and memory
378378
allocation to it:
379379

380380
```c++
381-
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VDeleter<VkImage>& image, VDeleter<VkDeviceMemory>& imageMemory) {
381+
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
382382
VkImageCreateInfo imageInfo = {};
383383
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
384384
imageInfo.imageType = VK_IMAGE_TYPE_2D;
@@ -394,7 +394,7 @@ void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling
394394
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
395395
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
396396

397-
if (vkCreateImage(device, &imageInfo, nullptr, image.replace()) != VK_SUCCESS) {
397+
if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) {
398398
throw std::runtime_error("failed to create image!");
399399
}
400400

@@ -406,7 +406,7 @@ void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling
406406
allocInfo.allocationSize = memRequirements.size;
407407
allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
408408

409-
if (vkAllocateMemory(device, &allocInfo, nullptr, imageMemory.replace()) != VK_SUCCESS) {
409+
if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
410410
throw std::runtime_error("failed to allocate image memory!");
411411
}
412412

@@ -430,8 +430,8 @@ void createTextureImage() {
430430
throw std::runtime_error("failed to load texture image!");
431431
}
432432
433-
VDeleter<VkImage> stagingImage{device, vkDestroyImage};
434-
VDeleter<VkDeviceMemory> stagingImageMemory{device, vkFreeMemory};
433+
VkImage stagingImage;
434+
VkDeviceMemory stagingImageMemory;
435435
createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingImage, stagingImageMemory);
436436
437437
VkImageSubresource subresource = {};
@@ -465,10 +465,10 @@ The next step is to create the actual texture image. Define two new class
465465
members to hold the handle to the image and its memory:
466466

467467
```c++
468-
VDeleter<VkCommandPool> commandPool{device, vkDestroyCommandPool};
469-
VDeleter<VkImage> textureImage{device, vkDestroyImage};
470-
VDeleter<VkDeviceMemory> textureImageMemory{device, vkFreeMemory};
471-
VDeleter<VkBuffer> vertexBuffer{device, vkDestroyBuffer};
468+
VkCommandPool commandPool;
469+
VkImage textureImage;
470+
VkDeviceMemory textureImageMemory;
471+
VkBuffer vertexBuffer;
472472
```
473473

474474
The final texture image can now be created using the same function:
@@ -787,6 +787,32 @@ it's also possible to use a buffer and copy pixels from it using
787787
performance on [some hardware](https://developer.nvidia.com/vulkan-memory-management)
788788
if you need to update the data in an image often.
789789

790+
## Cleanup
791+
792+
Finish the `createTextureImage` function by cleaning up the staging image and
793+
its memory at the end:
794+
795+
```c++
796+
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
797+
798+
vkDestroyImage(device, stagingImage, nullptr);
799+
vkFreeMemory(device, stagingImageMemory, nullptr);
800+
}
801+
```
802+
803+
The main texture image is used until the end of the program:
804+
805+
```c++
806+
void cleanup() {
807+
cleanupSwapChain();
808+
809+
vkDestroyImage(device, textureImage, nullptr);
810+
vkFreeMemory(device, textureImageMemory, nullptr);
811+
812+
...
813+
}
814+
```
815+
790816
The image now contains the texture, but we still need a way to access it from
791817
the graphics pipeline. We'll work on that in the next chapter.
792818

06_Texture_mapping/01_Image_view_and_sampler.md

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Add a class member to hold a `VkImageView` for the texture image and create a
1313
new function `createTextureImageView` where we'll create it:
1414

1515
```c++
16-
VDeleter<VkImageView> textureImageView{device, vkDestroyImageView};
16+
VkImageView textureImageView;
1717

1818
...
1919

@@ -53,7 +53,7 @@ I've left out the explicit `viewInfo.components` initialization, because
5353
image view by calling `vkCreateImageView`:
5454

5555
```c++
56-
if (vkCreateImageView(device, &viewInfo, nullptr, textureImageView.replace()) != VK_SUCCESS) {
56+
if (vkCreateImageView(device, &viewInfo, nullptr, &textureImageView) != VK_SUCCESS) {
5757
throw std::runtime_error("failed to create texture image view!");
5858
}
5959
```
@@ -62,7 +62,7 @@ Because so much of the logic is duplicated from `createImageViews`, you may wish
6262
to abstract it into a new `createImageView` function:
6363

6464
```c++
65-
void createImageView(VkImage image, VkFormat format, VDeleter<VkImageView>& imageView) {
65+
VkImageView createImageView(VkImage image, VkFormat format) {
6666
VkImageViewCreateInfo viewInfo = {};
6767
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
6868
viewInfo.image = image;
@@ -74,32 +74,48 @@ void createImageView(VkImage image, VkFormat format, VDeleter<VkImageView>& imag
7474
viewInfo.subresourceRange.baseArrayLayer = 0;
7575
viewInfo.subresourceRange.layerCount = 1;
7676

77-
if (vkCreateImageView(device, &viewInfo, nullptr, imageView.replace()) != VK_SUCCESS) {
77+
VkImageView imageView;
78+
if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
7879
throw std::runtime_error("failed to create texture image view!");
7980
}
81+
82+
return imageView;
8083
}
8184
```
8285
8386
The `createTextureImageView` function can now be simplified to:
8487
8588
```c++
8689
void createTextureImageView() {
87-
createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, textureImageView);
90+
textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM);
8891
}
8992
```
9093

9194
And `createImageViews` can be simplified to:
9295

9396
```c++
9497
void createImageViews() {
95-
swapChainImageViews.resize(swapChainImages.size(), VDeleter<VkImageView>{device, vkDestroyImageView});
98+
swapChainImageViews.resize(swapChainImages.size());
9699

97100
for (uint32_t i = 0; i < swapChainImages.size(); i++) {
98-
createImageView(swapChainImages[i], swapChainImageFormat, swapChainImageViews[i]);
101+
swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat);
99102
}
100103
}
101104
```
102105

106+
Make sure to destroy the image view at the end of the program, right before
107+
destroying the image itself:
108+
109+
```c++
110+
void cleanup() {
111+
cleanupSwapChain();
112+
113+
vkDestroyImageView(device, textureImageView, nullptr);
114+
115+
vkDestroyImage(device, textureImage, nullptr);
116+
vkFreeMemory(device, textureImageMemory, nullptr);
117+
```
118+
103119
## Samplers
104120
105121
It is possible for shaders to read texels directly from images, but that is not
@@ -258,15 +274,15 @@ hold the handle of the sampler object and create the sampler with
258274
`vkCreateSampler`:
259275

260276
```c++
261-
VDeleter<VkImageView> textureImageView{device, vkDestroyImageView};
262-
VDeleter<VkSampler> textureSampler{device, vkDestroySampler};
277+
VkImageView textureImageView;
278+
VkSampler textureSampler;
263279

264280
...
265281

266282
void createTextureSampler() {
267283
...
268284

269-
if (vkCreateSampler(device, &samplerInfo, nullptr, textureSampler.replace()) != VK_SUCCESS) {
285+
if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
270286
throw std::runtime_error("failed to create texture sampler!");
271287
}
272288
}
@@ -278,6 +294,53 @@ can be applied to any image you want, whether it is 1D, 2D or 3D. This is
278294
different from many older APIs, which combined texture images and filtering into
279295
a single state.
280296

297+
Destroy the sampler at the end of the program when we'll no longer be accessing
298+
the image:
299+
300+
```c++
301+
void cleanup() {
302+
cleanupSwapChain();
303+
304+
vkDestroySampler(device, textureSampler, nullptr);
305+
vkDestroyImageView(device, textureImageView, nullptr);
306+
307+
...
308+
}
309+
```
310+
311+
## Anisotropy device feature
312+
313+
If you run your program right now, you'll see a validation layer message like
314+
this:
315+
316+
![](/images/validation_layer_anisotropy.png)
317+
318+
That's because anisotropic filtering is actually an optional device feature. We
319+
need to update the `createLogicalDevice` function to request it:
320+
321+
```c++
322+
VkPhysicalDeviceFeatures deviceFeatures = {};
323+
deviceFeatures.samplerAnisotropy = VK_TRUE;
324+
```
325+
326+
And even though it is very unlikely that a modern graphics card will not support
327+
it, we should update `isDeviceSuitable` to check if it is available:
328+
329+
```c++
330+
bool isDeviceSuitable(VkPhysicalDevice device) {
331+
...
332+
333+
VkPhysicalDeviceFeatures supportedFeatures;
334+
vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
335+
336+
return indices.isComplete() && extensionsSupported && supportedFeatures.samplerAnisotropy;
337+
}
338+
```
339+
340+
The `vkGetPhysicalDeviceFeatures` repurposes the `VkPhysicalDeviceFeatures`
341+
struct to indicate which features are supported rather than requested by setting
342+
the boolean values.
343+
281344
In the next chapter we will expose the image and sampler objects to the shaders
282345
to draw the texture onto the square.
283346

06_Texture_mapping/02_Combined_image_sampler.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
2828
std::array<VkDescriptorSetLayoutBinding, 2> bindings = {uboLayoutBinding, samplerLayoutBinding};
2929
VkDescriptorSetLayoutCreateInfo layoutInfo = {};
3030
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
31-
layoutInfo.bindingCount = bindings.size();
31+
layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
3232
layoutInfo.pBindings = bindings.data();
3333
```
3434
@@ -53,7 +53,7 @@ poolSizes[1].descriptorCount = 1;
5353
5454
VkDescriptorPoolCreateInfo poolInfo = {};
5555
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
56-
poolInfo.poolSizeCount = poolSizes.size();
56+
poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
5757
poolInfo.pPoolSizes = poolSizes.data();
5858
poolInfo.maxSets = 1;
5959
```
@@ -92,7 +92,7 @@ descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
9292
descriptorWrites[1].descriptorCount = 1;
9393
descriptorWrites[1].pImageInfo = &imageInfo;
9494

95-
vkUpdateDescriptorSets(device, descriptorWrites.size(), descriptorWrites.data(), 0, nullptr);
95+
vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
9696
```
9797
9898
The descriptor must be updated with this image info, just like the buffer. This

0 commit comments

Comments
 (0)