1
#include "Common/Vulkan/VulkanImage.h"
2
#include "Common/Vulkan/VulkanMemory.h"
4
VkResult VulkanTexture::Create(int w, int h, VkFormat format) {
9
VkFormatProperties formatProps;
10
vkGetPhysicalDeviceFormatProperties(vulkan_->GetPhysicalDevice(), format, &formatProps);
12
// See if we can use a linear tiled image for a texture, if not, we will need a staging image for the texture data.
13
// Linear tiling is usually only supported for 2D non-array textures.
14
// needStaging = (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) ? true : false;
21
void VulkanTexture::CreateMappableImage() {
22
// If we already have a mappableImage, forget it.
24
vulkan_->Delete().QueueDeleteImage(mappableImage);
25
mappableImage = VK_NULL_HANDLE;
28
vulkan_->Delete().QueueDeleteDeviceMemory(mappableMemory);
29
mappableMemory = VK_NULL_HANDLE;
32
bool U_ASSERT_ONLY pass;
34
VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
35
image_create_info.imageType = VK_IMAGE_TYPE_2D;
36
image_create_info.format = format_;
37
image_create_info.extent.width = tex_width;
38
image_create_info.extent.height = tex_height;
39
image_create_info.extent.depth = 1;
40
image_create_info.mipLevels = 1;
41
image_create_info.arrayLayers = 1;
42
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
43
image_create_info.tiling = VK_IMAGE_TILING_LINEAR;
44
image_create_info.usage = needStaging ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT : VK_IMAGE_USAGE_SAMPLED_BIT;
45
image_create_info.queueFamilyIndexCount = 0;
46
image_create_info.pQueueFamilyIndices = NULL;
47
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
48
image_create_info.flags = 0;
49
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
51
VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
52
mem_alloc.allocationSize = 0;
53
mem_alloc.memoryTypeIndex = 0;
55
// Create a mappable image. It will be the texture if linear images are ok to be textures
56
// or it will be the staging image if they are not.
57
VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &mappableImage);
58
assert(res == VK_SUCCESS);
60
vkGetImageMemoryRequirements(vulkan_->GetDevice(), mappableImage, &mem_reqs);
61
assert(res == VK_SUCCESS);
63
mem_alloc.allocationSize = mem_reqs.size;
65
// Find the memory type that is host mappable.
66
pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &mem_alloc.memoryTypeIndex);
69
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mappableMemory);
70
assert(res == VK_SUCCESS);
72
res = vkBindImageMemory(vulkan_->GetDevice(), mappableImage, mappableMemory, 0);
73
assert(res == VK_SUCCESS);
76
uint8_t *VulkanTexture::Lock(int level, int *rowPitch) {
77
CreateMappableImage();
79
VkImageSubresource subres = {};
80
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
82
subres.arrayLayer = 0;
84
VkSubresourceLayout layout;
87
// Get the subresource layout so we know what the row pitch is
88
vkGetImageSubresourceLayout(vulkan_->GetDevice(), mappableImage, &subres, &layout);
89
VkResult res = vkMapMemory(vulkan_->GetDevice(), mappableMemory, layout.offset, layout.size, 0, &data);
90
assert(res == VK_SUCCESS);
92
*rowPitch = (int)layout.rowPitch;
93
return (uint8_t *)data;
96
void VulkanTexture::Unlock() {
97
vkUnmapMemory(vulkan_->GetDevice(), mappableMemory);
99
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
101
// if we already have an image, queue it for destruction and forget it.
104
// If we can use the linear tiled image as a texture, just do it
105
image = mappableImage;
106
mem = mappableMemory;
107
TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
108
// Make sure we don't accidentally delete the main image.
109
mappableImage = VK_NULL_HANDLE;
110
mappableMemory = VK_NULL_HANDLE;
112
VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
113
image_create_info.imageType = VK_IMAGE_TYPE_2D;
114
image_create_info.format = format_;
115
image_create_info.extent.width = tex_width;
116
image_create_info.extent.height = tex_height;
117
image_create_info.extent.depth = 1;
118
image_create_info.mipLevels = 1;
119
image_create_info.arrayLayers = 1;
120
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
121
image_create_info.queueFamilyIndexCount = 0;
122
image_create_info.pQueueFamilyIndices = NULL;
123
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
124
image_create_info.flags = 0;
125
// The mappable image cannot be our texture, so create an optimally tiled image and blit to it
126
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
127
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
128
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
130
VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image);
131
assert(res == VK_SUCCESS);
133
vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs);
135
VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
136
mem_alloc.memoryTypeIndex = 0;
137
mem_alloc.allocationSize = mem_reqs.size;
139
// Find memory type - don't specify any mapping requirements
140
bool pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex);
143
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem);
144
assert(res == VK_SUCCESS);
146
res = vkBindImageMemory(vulkan_->GetDevice(), image, mem, 0);
147
assert(res == VK_SUCCESS);
149
// Since we're going to blit from the mappable image, set its layout to SOURCE_OPTIMAL
150
TransitionImageLayout(cmd, mappableImage,
151
VK_IMAGE_ASPECT_COLOR_BIT,
152
VK_IMAGE_LAYOUT_PREINITIALIZED,
153
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
155
TransitionImageLayout(cmd, image,
156
VK_IMAGE_ASPECT_COLOR_BIT,
157
VK_IMAGE_LAYOUT_UNDEFINED,
158
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
160
VkImageCopy copy_region;
161
copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
162
copy_region.srcSubresource.mipLevel = 0;
163
copy_region.srcSubresource.baseArrayLayer = 0;
164
copy_region.srcSubresource.layerCount = 1;
165
copy_region.srcOffset.x = 0;
166
copy_region.srcOffset.y = 0;
167
copy_region.srcOffset.z = 0;
168
copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
169
copy_region.dstSubresource.mipLevel = 0;
170
copy_region.dstSubresource.baseArrayLayer = 0;
171
copy_region.dstSubresource.layerCount = 1;
172
copy_region.dstOffset.x = 0;
173
copy_region.dstOffset.y = 0;
174
copy_region.dstOffset.z = 0;
175
copy_region.extent.width = tex_width;
176
copy_region.extent.height = tex_height;
177
copy_region.extent.depth = 1;
179
// Put the copy command into the command buffer
181
mappableImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
182
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
185
assert(res == VK_SUCCESS);
187
// Set the layout for the texture image from DESTINATION_OPTIMAL to SHADER_READ_ONLY
188
TransitionImageLayout(cmd, image,
189
VK_IMAGE_ASPECT_COLOR_BIT,
190
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
191
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
193
// Then drop the temporary mappable image - although should not be necessary...
194
vulkan_->Delete().QueueDeleteImage(mappableImage);
195
vulkan_->Delete().QueueDeleteDeviceMemory(mappableMemory);
197
mappableImage = VK_NULL_HANDLE;
198
mappableMemory = VK_NULL_HANDLE;
201
VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
202
view_info.image = image;
203
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
204
view_info.format = format_;
205
view_info.components.r = VK_COMPONENT_SWIZZLE_R;
206
view_info.components.g = VK_COMPONENT_SWIZZLE_G;
207
view_info.components.b = VK_COMPONENT_SWIZZLE_B;
208
view_info.components.a = VK_COMPONENT_SWIZZLE_A;
209
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
210
view_info.subresourceRange.baseMipLevel = 0;
211
view_info.subresourceRange.levelCount = 1;
212
view_info.subresourceRange.baseArrayLayer = 0;
213
view_info.subresourceRange.layerCount = 1;
214
VkResult res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view);
215
assert(res == VK_SUCCESS);
218
void VulkanTexture::Wipe() {
220
vulkan_->Delete().QueueDeleteImage(image);
221
image = VK_NULL_HANDLE;
224
vulkan_->Delete().QueueDeleteImageView(view);
225
view = VK_NULL_HANDLE;
227
if (mem && !allocator_) {
228
vulkan_->Delete().QueueDeleteDeviceMemory(mem);
229
mem = VK_NULL_HANDLE;
231
allocator_->Free(mem, offset_);
232
mem = VK_NULL_HANDLE;
236
static bool IsDepthStencilFormat(VkFormat format) {
238
case VK_FORMAT_D16_UNORM:
239
case VK_FORMAT_D16_UNORM_S8_UINT:
240
case VK_FORMAT_D24_UNORM_S8_UINT:
241
case VK_FORMAT_D32_SFLOAT:
242
case VK_FORMAT_D32_SFLOAT_S8_UINT:
249
bool VulkanTexture::CreateDirect(int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) {
252
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
259
VkImageAspectFlags aspect = IsDepthStencilFormat(format) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
261
VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
262
image_create_info.imageType = VK_IMAGE_TYPE_2D;
263
image_create_info.format = format_;
264
image_create_info.extent.width = tex_width;
265
image_create_info.extent.height = tex_height;
266
image_create_info.extent.depth = 1;
267
image_create_info.mipLevels = numMips;
268
image_create_info.arrayLayers = 1;
269
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
270
image_create_info.flags = 0;
271
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
272
image_create_info.usage = usage;
273
if (initialLayout == VK_IMAGE_LAYOUT_PREINITIALIZED) {
274
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
276
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
279
VkResult res = vkCreateImage(vulkan_->GetDevice(), &image_create_info, NULL, &image);
280
if (res != VK_SUCCESS) {
281
assert(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
285
// Write a command to transition the image to the requested layout, if it's not already that layout.
286
if (initialLayout != VK_IMAGE_LAYOUT_UNDEFINED && initialLayout != VK_IMAGE_LAYOUT_PREINITIALIZED) {
287
TransitionImageLayout(cmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, initialLayout);
290
vkGetImageMemoryRequirements(vulkan_->GetDevice(), image, &mem_reqs);
293
offset_ = allocator_->Allocate(mem_reqs, &mem);
294
if (offset_ == VulkanDeviceAllocator::ALLOCATE_FAILED) {
298
VkMemoryAllocateInfo mem_alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
299
mem_alloc.memoryTypeIndex = 0;
300
mem_alloc.allocationSize = mem_reqs.size;
302
// Find memory type - don't specify any mapping requirements
303
bool pass = vulkan_->MemoryTypeFromProperties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex);
306
res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem);
307
if (res != VK_SUCCESS) {
308
assert(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
315
res = vkBindImageMemory(vulkan_->GetDevice(), image, mem, offset_);
316
if (res != VK_SUCCESS) {
317
assert(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
321
// Create the view while we're at it.
322
VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
323
view_info.image = image;
324
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
325
view_info.format = format_;
327
view_info.components = *mapping;
329
view_info.components.r = VK_COMPONENT_SWIZZLE_R;
330
view_info.components.g = VK_COMPONENT_SWIZZLE_G;
331
view_info.components.b = VK_COMPONENT_SWIZZLE_B;
332
view_info.components.a = VK_COMPONENT_SWIZZLE_A;
334
view_info.subresourceRange.aspectMask = aspect;
335
view_info.subresourceRange.baseMipLevel = 0;
336
view_info.subresourceRange.levelCount = numMips;
337
view_info.subresourceRange.baseArrayLayer = 0;
338
view_info.subresourceRange.layerCount = 1;
340
res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view);
341
if (res != VK_SUCCESS) {
342
assert(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
348
void VulkanTexture::UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) {
349
VkBufferImageCopy copy_region = {};
350
copy_region.bufferOffset = offset;
351
copy_region.bufferRowLength = (uint32_t)rowLength;
352
copy_region.bufferImageHeight = 0; // 2D
353
copy_region.imageExtent.width = mipWidth;
354
copy_region.imageExtent.height = mipHeight;
355
copy_region.imageExtent.depth = 1;
356
copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
357
copy_region.imageSubresource.mipLevel = mip;
358
copy_region.imageSubresource.baseArrayLayer = 0;
359
copy_region.imageSubresource.layerCount = 1;
361
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
362
vkCmdCopyBufferToImage(cmd, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
365
void VulkanTexture::EndCreate() {
366
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
367
TransitionImageLayout(cmd, image,
368
VK_IMAGE_ASPECT_COLOR_BIT,
369
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
370
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
373
void VulkanTexture::Destroy() {
375
vulkan_->Delete().QueueDeleteImageView(view);
378
vulkan_->Delete().QueueDeleteImage(image);
379
if (mappableImage == image) {
380
mappableImage = VK_NULL_HANDLE;
383
if (mem && !allocator_) {
384
vulkan_->Delete().QueueDeleteDeviceMemory(mem);
385
if (mappableMemory == mem) {
386
mappableMemory = VK_NULL_HANDLE;
389
allocator_->Free(mem, offset_);
392
view = VK_NULL_HANDLE;
393
image = VK_NULL_HANDLE;
394
mem = VK_NULL_HANDLE;