diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index a355651c8d..74f6e64e38 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -492,56 +492,100 @@ struct vk_texture vulkan_create_texture(vk_t *vk, { unsigned i; struct vk_texture tex; + VkMemoryAllocateInfo alloc; VkMemoryRequirements mem_reqs; VkSubresourceLayout layout; - VkDevice device = vk->context->device; - VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - VkBufferCreateInfo buffer_info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - VkImageViewCreateInfo view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - VkImageSubresource subresource = { VK_IMAGE_ASPECT_COLOR_BIT }; - VkCommandBufferAllocateInfo cmd_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + VkImageCreateInfo info; + VkSubmitInfo submit_info; + VkBufferCreateInfo buffer_info; + VkImageSubresource subresource; + VkDevice device = vk->context->device; - memset(&tex, 0, sizeof(tex)); + mem_reqs.size = 0; + mem_reqs.alignment = 0; + mem_reqs.memoryTypeBits = 0; - info.imageType = VK_IMAGE_TYPE_2D; - info.format = format; - info.extent.width = width; - info.extent.height = height; - info.extent.depth = 1; - info.arrayLayers = 1; - info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc.pNext = NULL; + alloc.allocationSize = 0; + alloc.memoryTypeIndex = 0; - buffer_info.size = width * height * vulkan_format_to_bpp(format); - buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + tex.type = VULKAN_TEXTURE_STREAMED; + tex.default_smooth = false; + tex.need_manual_cache_management = false; + tex.mipmap = false; + tex.memory_type = 0; + tex.width = 0; + tex.height = 0; + tex.offset = 0; + tex.stride = 0; + tex.size = 0; + tex.mapped = NULL; + tex.image = VK_NULL_HANDLE; + tex.view = VK_NULL_HANDLE; + tex.memory = VK_NULL_HANDLE; + tex.buffer = VK_NULL_HANDLE; + tex.format = VK_FORMAT_UNDEFINED; + tex.memory_size = 0; + tex.layout = VK_IMAGE_LAYOUT_UNDEFINED; - /* For simplicity, always build mipmaps for - * static textures, samplers can be used to enable it dynamically. - */ - if (type == VULKAN_TEXTURE_STATIC) - { - info.mipLevels = vulkan_num_miplevels(width, height); - tex.mipmap = true; - } - else - info.mipLevels = 1; + info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + info.pNext = NULL; + info.flags = 0; + info.imageType = VK_IMAGE_TYPE_2D; + info.format = format; + info.extent.width = width; + info.extent.height = height; + info.extent.depth = 1; + info.mipLevels = 1; + info.arrayLayers = 1; + info.samples = VK_SAMPLE_COUNT_1_BIT; + info.tiling = VK_IMAGE_TILING_OPTIMAL; + info.usage = 0; + info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.queueFamilyIndexCount = 0; + info.pQueueFamilyIndices = NULL; + info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - info.samples = VK_SAMPLE_COUNT_1_BIT; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.pNext = NULL; + buffer_info.flags = 0; + buffer_info.size = width * height * + vulkan_format_to_bpp(format); + buffer_info.usage = 0; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + buffer_info.queueFamilyIndexCount = 0; + buffer_info.pQueueFamilyIndices = NULL; + + subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresource.mipLevel = 0; + subresource.arrayLayer = 0; + + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = NULL; + submit_info.waitSemaphoreCount = 0; + submit_info.pWaitSemaphores = NULL; + submit_info.pWaitDstStageMask = NULL; + submit_info.commandBufferCount = 0; + submit_info.pCommandBuffers = NULL; + submit_info.signalSemaphoreCount = 0; + submit_info.pSignalSemaphores = NULL; if (type == VULKAN_TEXTURE_STREAMED) { VkFormatProperties format_properties; - const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | + const VkFormatFeatureFlags required = + VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT; vkGetPhysicalDeviceFormatProperties( vk->context->gpu, format, &format_properties); - if ((format_properties.linearTilingFeatures & required) != required) + if ((format_properties.linearTilingFeatures + & required) != required) { - RARCH_LOG("[Vulkan]: GPU does not support using linear images as textures. Falling back to copy path.\n"); + RARCH_LOG("[Vulkan]: GPU does not support using" + "linear images as textures. Falling back to copy path.\n"); type = VULKAN_TEXTURE_STAGING; } } @@ -549,58 +593,80 @@ struct vk_texture vulkan_create_texture(vk_t *vk, switch (type) { case VULKAN_TEXTURE_STATIC: - retro_assert(initial && "Static textures must have initial data.\n"); + /* For simplicity, always build mipmaps for + * static textures, samplers can be used to + * enable it dynamically. + */ + info.mipLevels = vulkan_num_miplevels(width, height); + tex.mipmap = true; + + retro_assert(initial && + "Static textures must have initial data.\n"); info.tiling = VK_IMAGE_TILING_OPTIMAL; info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + vkCreateImage(device, &info, NULL, &tex.image); +#if 0 + vulkan_track_alloc(tex.image); +#endif + vkGetImageMemoryRequirements(device, tex.image, &mem_reqs); break; case VULKAN_TEXTURE_DYNAMIC: - retro_assert(!initial && "Dynamic textures must not have initial data.\n"); + retro_assert(!initial && + "Dynamic textures must not have initial data.\n"); info.tiling = VK_IMAGE_TILING_OPTIMAL; info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + vkCreateImage(device, &info, NULL, &tex.image); +#if 0 + vulkan_track_alloc(tex.image); +#endif + vkGetImageMemoryRequirements(device, tex.image, &mem_reqs); break; case VULKAN_TEXTURE_STREAMED: info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + VK_IMAGE_USAGE_TRANSFER_SRC_BIT; info.tiling = VK_IMAGE_TILING_LINEAR; info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + + vkCreateImage(device, &info, NULL, &tex.image); +#if 0 + vulkan_track_alloc(tex.image); +#endif + vkGetImageMemoryRequirements(device, tex.image, &mem_reqs); break; case VULKAN_TEXTURE_STAGING: buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; info.initialLayout = VK_IMAGE_LAYOUT_GENERAL; info.tiling = VK_IMAGE_TILING_LINEAR; + + /* Linear staging textures are not guaranteed to be supported, + * use buffers instead. */ + vkCreateBuffer(device, &buffer_info, NULL, &tex.buffer); + vkGetBufferMemoryRequirements(device, tex.buffer, &mem_reqs); break; case VULKAN_TEXTURE_READBACK: buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; info.initialLayout = VK_IMAGE_LAYOUT_GENERAL; info.tiling = VK_IMAGE_TILING_LINEAR; + + /* Linear staging textures are not guaranteed to be supported, + * use buffers instead. */ + vkCreateBuffer(device, &buffer_info, NULL, &tex.buffer); + vkGetBufferMemoryRequirements(device, tex.buffer, &mem_reqs); break; } - if (type != VULKAN_TEXTURE_STAGING && type != VULKAN_TEXTURE_READBACK) - { - vkCreateImage(device, &info, NULL, &tex.image); -#if 0 - vulkan_track_alloc(tex.image); -#endif - vkGetImageMemoryRequirements(device, tex.image, &mem_reqs); - } - else - { - /* Linear staging textures are not guaranteed to be supported, - * use buffers instead. */ - vkCreateBuffer(device, &buffer_info, NULL, &tex.buffer); - vkGetBufferMemoryRequirements(device, tex.buffer, &mem_reqs); - } alloc.allocationSize = mem_reqs.size; switch (type) @@ -614,7 +680,8 @@ struct vk_texture vulkan_create_texture(vk_t *vk, break; default: - /* Try to find a memory type which is cached, even if it means manual cache management. */ + /* Try to find a memory type which is cached, + * even if it means manual cache management. */ alloc.memoryTypeIndex = vulkan_find_memory_type_fallback( &vk->context->memory_properties, mem_reqs.memoryTypeBits, @@ -624,25 +691,29 @@ struct vk_texture vulkan_create_texture(vk_t *vk, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); tex.need_manual_cache_management = - (vk->context->memory_properties.memoryTypes[alloc.memoryTypeIndex].propertyFlags & + (vk->context->memory_properties. + memoryTypes[alloc.memoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0; break; } - /* If the texture is STREAMED and it's not DEVICE_LOCAL, we expect to hit a slower path, + /* If the texture is STREAMED and it's not DEVICE_LOCAL, + * we expect to hit a slower path, * so fallback to copy path. */ if (type == VULKAN_TEXTURE_STREAMED && - (vk->context->memory_properties.memoryTypes[alloc.memoryTypeIndex].propertyFlags & + (vk->context->memory_properties. + memoryTypes[alloc.memoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0) { /* Recreate texture but for STAGING this time ... */ - RARCH_LOG("[Vulkan]: GPU supports linear images as textures, but not DEVICE_LOCAL. Falling back to copy path.\n"); - type = VULKAN_TEXTURE_STAGING; + RARCH_LOG("[Vulkan]: GPU supports linear images as textures" + ", but not DEVICE_LOCAL. Falling back to copy path.\n"); + type = VULKAN_TEXTURE_STAGING; vkDestroyImage(device, tex.image, NULL); - tex.image = (VkImage)NULL; - info.initialLayout = VK_IMAGE_LAYOUT_GENERAL; + tex.image = (VkImage)VK_NULL_HANDLE; + info.initialLayout = VK_IMAGE_LAYOUT_GENERAL; - buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; vkCreateBuffer(device, &buffer_info, NULL, &tex.buffer); vkGetBufferMemoryRequirements(device, tex.buffer, &mem_reqs); @@ -658,31 +729,38 @@ struct vk_texture vulkan_create_texture(vk_t *vk, } /* We're not reusing the objects themselves. */ - if (old && old->view != VK_NULL_HANDLE) - vkDestroyImageView(vk->context->device, old->view, NULL); - if (old && old->image != VK_NULL_HANDLE) + if (old) { - vkDestroyImage(vk->context->device, old->image, NULL); + if (old->view != VK_NULL_HANDLE) + vkDestroyImageView(vk->context->device, old->view, NULL); + if (old->image != VK_NULL_HANDLE) + { + vkDestroyImage(vk->context->device, old->image, NULL); #ifdef VULKAN_DEBUG_TEXTURE_ALLOC - vulkan_track_dealloc(old->image); + vulkan_track_dealloc(old->image); #endif - } - if (old && old->buffer != VK_NULL_HANDLE) - vkDestroyBuffer(vk->context->device, old->buffer, NULL); + } + if (old->buffer != VK_NULL_HANDLE) + vkDestroyBuffer(vk->context->device, old->buffer, NULL); - /* We can pilfer the old memory and move it over to the new texture. */ - if (old && - old->memory_size >= mem_reqs.size && - old->memory_type == alloc.memoryTypeIndex) - { - tex.memory = old->memory; - tex.memory_size = old->memory_size; - tex.memory_type = old->memory_type; + /* We can pilfer the old memory and move it over to the new texture. */ + if ( + old->memory_size >= mem_reqs.size && + old->memory_type == alloc.memoryTypeIndex) + { + tex.memory = old->memory; + tex.memory_size = old->memory_size; + tex.memory_type = old->memory_type; - if (old->mapped) - vkUnmapMemory(device, old->memory); + if (old->mapped) + vkUnmapMemory(device, old->memory); - old->memory = VK_NULL_HANDLE; + old->memory = VK_NULL_HANDLE; + } + + if (old->memory != VK_NULL_HANDLE) + vkFreeMemory(device, old->memory, NULL); + memset(old, 0, sizeof(*old)); } else { @@ -691,13 +769,6 @@ struct vk_texture vulkan_create_texture(vk_t *vk, tex.memory_type = alloc.memoryTypeIndex; } - if (old) - { - if (old->memory != VK_NULL_HANDLE) - vkFreeMemory(device, old->memory, NULL); - memset(old, 0, sizeof(*old)); - } - if (tex.image) vkBindImageMemory(device, tex.image, tex.memory, 0); if (tex.buffer) @@ -705,193 +776,225 @@ struct vk_texture vulkan_create_texture(vk_t *vk, if (type != VULKAN_TEXTURE_STAGING && type != VULKAN_TEXTURE_READBACK) { - view.image = tex.image; - view.viewType = VK_IMAGE_VIEW_TYPE_2D; - view.format = format; + VkImageViewCreateInfo view; + view.sType = + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view.pNext = NULL; + view.flags = 0; + view.image = tex.image; + view.viewType = VK_IMAGE_VIEW_TYPE_2D; + view.format = format; + view.components.r = VK_COMPONENT_SWIZZLE_R; + view.components.g = VK_COMPONENT_SWIZZLE_G; + view.components.b = VK_COMPONENT_SWIZZLE_B; + view.components.a = VK_COMPONENT_SWIZZLE_A; if (swizzle) - view.components = *swizzle; - else - { - view.components.r = VK_COMPONENT_SWIZZLE_R; - view.components.g = VK_COMPONENT_SWIZZLE_G; - view.components.b = VK_COMPONENT_SWIZZLE_B; - view.components.a = VK_COMPONENT_SWIZZLE_A; - } - view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - view.subresourceRange.levelCount = info.mipLevels; - view.subresourceRange.layerCount = 1; + view.components = *swizzle; + view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view.subresourceRange.baseMipLevel = 0; + view.subresourceRange.levelCount = info.mipLevels; + view.subresourceRange.baseArrayLayer = 0; + view.subresourceRange.layerCount = 1; vkCreateImageView(device, &view, NULL, &tex.view); } else - tex.view = VK_NULL_HANDLE; + tex.view = VK_NULL_HANDLE; + + layout.offset = 0; + layout.size = 0; + layout.rowPitch = 0; + layout.arrayPitch = 0; + layout.depthPitch = 0; if (tex.image && info.tiling == VK_IMAGE_TILING_LINEAR) vkGetImageSubresourceLayout(device, tex.image, &subresource, &layout); else if (tex.buffer) { - layout.offset = 0; - layout.size = buffer_info.size; - layout.rowPitch = width * vulkan_format_to_bpp(format); + layout.size = buffer_info.size; + layout.rowPitch = width * + vulkan_format_to_bpp(format); } - else - memset(&layout, 0, sizeof(layout)); - tex.stride = layout.rowPitch; - tex.offset = layout.offset; - tex.size = layout.size; - tex.layout = info.initialLayout; + tex.stride = layout.rowPitch; + tex.offset = layout.offset; + tex.size = layout.size; + tex.layout = info.initialLayout; tex.width = width; tex.height = height; tex.format = format; tex.type = type; - if (initial && (type == VULKAN_TEXTURE_STREAMED || type == VULKAN_TEXTURE_STAGING)) + if (initial) { - unsigned y; - uint8_t *dst = NULL; - const uint8_t *src = NULL; - void *ptr = NULL; - unsigned bpp = vulkan_format_to_bpp(tex.format); - unsigned stride = tex.width * bpp; - - vkMapMemory(device, tex.memory, tex.offset, tex.size, 0, &ptr); - - dst = (uint8_t*)ptr; - src = (const uint8_t*)initial; - for (y = 0; y < tex.height; y++, dst += tex.stride, src += stride) - memcpy(dst, src, width * bpp); - - vulkan_sync_texture_to_gpu(vk, &tex); - vkUnmapMemory(device, tex.memory); - } - else if (initial && type == VULKAN_TEXTURE_STATIC) - { - VkBufferImageCopy region; - VkCommandBuffer staging; - struct vk_texture tmp = vulkan_create_texture(vk, NULL, - width, height, format, initial, NULL, VULKAN_TEXTURE_STAGING); - - cmd_info.commandPool = vk->staging_pool; - cmd_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmd_info.commandBufferCount = 1; - - vkAllocateCommandBuffers(vk->context->device, &cmd_info, &staging); - - begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - - vkBeginCommandBuffer(staging, &begin_info); - - /* If doing mipmapping on upload, keep in general so we can easily do transfers to - * and transfers from the images without having to - * mess around with lots of extra transitions at per-level granularity. - */ - vulkan_image_layout_transition(vk, - staging, - tex.image, - VK_IMAGE_LAYOUT_UNDEFINED, - tex.mipmap ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 0, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT); - - memset(®ion, 0, sizeof(region)); - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.layerCount = 1; - region.imageExtent.width = width; - region.imageExtent.height = height; - region.imageExtent.depth = 1; - - vkCmdCopyBufferToImage(staging, - tmp.buffer, - tex.image, - tex.mipmap ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ®ion); - - if (tex.mipmap) + switch (type) { - for (i = 1; i < info.mipLevels; i++) - { - VkImageBlit blit_region; - unsigned src_width = MAX(width >> (i - 1), 1); - unsigned src_height = MAX(height >> (i - 1), 1); - unsigned target_width = MAX(width >> i, 1); - unsigned target_height = MAX(height >> i, 1); - memset(&blit_region, 0, sizeof(blit_region)); + case VULKAN_TEXTURE_STREAMED: + case VULKAN_TEXTURE_STAGING: + { + unsigned y; + uint8_t *dst = NULL; + const uint8_t *src = NULL; + void *ptr = NULL; + unsigned bpp = vulkan_format_to_bpp(tex.format); + unsigned stride = tex.width * bpp; - blit_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blit_region.srcSubresource.mipLevel = i - 1; - blit_region.srcSubresource.baseArrayLayer = 0; - blit_region.srcSubresource.layerCount = 1; - blit_region.dstSubresource = blit_region.srcSubresource; - blit_region.dstSubresource.mipLevel = i; - blit_region.srcOffsets[1].x = src_width; - blit_region.srcOffsets[1].y = src_height; - blit_region.srcOffsets[1].z = 1; - blit_region.dstOffsets[1].x = target_width; - blit_region.dstOffsets[1].y = target_height; - blit_region.dstOffsets[1].z = 1; + vkMapMemory(device, tex.memory, tex.offset, tex.size, 0, &ptr); - /* Only injects execution and memory barriers, - * not actual transition. */ - vulkan_image_layout_transition(vk, staging, tex.image, - VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_TRANSFER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT); + dst = (uint8_t*)ptr; + src = (const uint8_t*)initial; + for (y = 0; y < tex.height; y++, dst += tex.stride, src += stride) + memcpy(dst, src, width * bpp); - vkCmdBlitImage(staging, - tex.image, VK_IMAGE_LAYOUT_GENERAL, - tex.image, VK_IMAGE_LAYOUT_GENERAL, - 1, &blit_region, VK_FILTER_LINEAR); - } + vulkan_sync_texture_to_gpu(vk, &tex); + vkUnmapMemory(device, tex.memory); + } + break; + case VULKAN_TEXTURE_STATIC: + { + VkBufferImageCopy region; + VkCommandBuffer staging; + VkCommandBufferAllocateInfo cmd_info; + VkCommandBufferBeginInfo begin_info; + struct vk_texture tmp = + vulkan_create_texture(vk, NULL, + width, height, format, initial, + NULL, VULKAN_TEXTURE_STAGING); - /* Complete our texture. */ - vulkan_image_layout_transition(vk, staging, tex.image, - VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - } - else - { - vulkan_image_layout_transition(vk, staging, tex.image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - } + cmd_info.sType = + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmd_info.pNext = NULL; + cmd_info.commandPool = vk->staging_pool; + cmd_info.level = + VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd_info.commandBufferCount = 1; - vkEndCommandBuffer(staging); - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &staging; + vkAllocateCommandBuffers(vk->context->device, + &cmd_info, &staging); + + begin_info.sType = + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.pNext = NULL; + begin_info.flags = + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + begin_info.pInheritanceInfo = NULL; + + vkBeginCommandBuffer(staging, &begin_info); + + /* If doing mipmapping on upload, keep in general + * so we can easily do transfers to + * and transfers from the images without having to + * mess around with lots of extra transitions at + * per-level granularity. + */ + vulkan_image_layout_transition(vk, + staging, + tex.image, + VK_IMAGE_LAYOUT_UNDEFINED, + tex.mipmap + ? VK_IMAGE_LAYOUT_GENERAL + : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT); + + memset(®ion, 0, sizeof(region)); + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.layerCount = 1; + region.imageExtent.width = width; + region.imageExtent.height = height; + region.imageExtent.depth = 1; + + vkCmdCopyBufferToImage(staging, + tmp.buffer, + tex.image, + tex.mipmap + ? VK_IMAGE_LAYOUT_GENERAL + : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ®ion); + + if (tex.mipmap) + { + for (i = 1; i < info.mipLevels; i++) + { + VkImageBlit blit_region; + unsigned src_width = MAX(width >> (i - 1), 1); + unsigned src_height = MAX(height >> (i - 1), 1); + unsigned target_width = MAX(width >> i, 1); + unsigned target_height = MAX(height >> i, 1); + memset(&blit_region, 0, sizeof(blit_region)); + + blit_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit_region.srcSubresource.mipLevel = i - 1; + blit_region.srcSubresource.baseArrayLayer = 0; + blit_region.srcSubresource.layerCount = 1; + blit_region.dstSubresource = blit_region.srcSubresource; + blit_region.dstSubresource.mipLevel = i; + blit_region.srcOffsets[1].x = src_width; + blit_region.srcOffsets[1].y = src_height; + blit_region.srcOffsets[1].z = 1; + blit_region.dstOffsets[1].x = target_width; + blit_region.dstOffsets[1].y = target_height; + blit_region.dstOffsets[1].z = 1; + + /* Only injects execution and memory barriers, + * not actual transition. */ + vulkan_image_layout_transition(vk, staging, tex.image, + VK_IMAGE_LAYOUT_GENERAL, + VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT); + + vkCmdBlitImage(staging, + tex.image, VK_IMAGE_LAYOUT_GENERAL, + tex.image, VK_IMAGE_LAYOUT_GENERAL, + 1, &blit_region, VK_FILTER_LINEAR); + } + } + + /* Complete our texture. */ + vulkan_image_layout_transition(vk, staging, tex.image, + tex.mipmap + ? VK_IMAGE_LAYOUT_GENERAL + : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL + , + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + + vkEndCommandBuffer(staging); + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &staging; #ifdef HAVE_THREADS - slock_lock(vk->context->queue_lock); + slock_lock(vk->context->queue_lock); #endif - vkQueueSubmit(vk->context->queue, - 1, &submit_info, VK_NULL_HANDLE); + vkQueueSubmit(vk->context->queue, + 1, &submit_info, VK_NULL_HANDLE); - /* TODO: Very crude, but texture uploads only happen - * during init, so waiting for GPU to complete transfer - * and blocking isn't a big deal. */ - vkQueueWaitIdle(vk->context->queue); + /* TODO: Very crude, but texture uploads only happen + * during init, so waiting for GPU to complete transfer + * and blocking isn't a big deal. */ + vkQueueWaitIdle(vk->context->queue); #ifdef HAVE_THREADS - slock_unlock(vk->context->queue_lock); + slock_unlock(vk->context->queue_lock); #endif - vkFreeCommandBuffers(vk->context->device, - vk->staging_pool, 1, &staging); - vulkan_destroy_texture( - vk->context->device, &tmp); - tex.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + vkFreeCommandBuffers(vk->context->device, + vk->staging_pool, 1, &staging); + vulkan_destroy_texture( + vk->context->device, &tmp); + tex.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + break; + default: + break; + } } return tex; } @@ -915,7 +1018,24 @@ void vulkan_destroy_texture( if (tex->image) vulkan_track_dealloc(tex->image); #endif - memset(tex, 0, sizeof(*tex)); + tex->type = VULKAN_TEXTURE_STREAMED; + tex->default_smooth = false; + tex->need_manual_cache_management = false; + tex->mipmap = false; + tex->memory_type = 0; + tex->width = 0; + tex->height = 0; + tex->offset = 0; + tex->stride = 0; + tex->size = 0; + tex->mapped = NULL; + tex->image = VK_NULL_HANDLE; + tex->view = VK_NULL_HANDLE; + tex->memory = VK_NULL_HANDLE; + tex->buffer = VK_NULL_HANDLE; + tex->format = VK_FORMAT_UNDEFINED; + tex->memory_size = 0; + tex->layout = VK_IMAGE_LAYOUT_UNDEFINED; } static void vulkan_write_quad_descriptors(