Cleanup vulkan_create_texture

This commit is contained in:
twinaphex 2020-06-28 18:25:17 +02:00
parent 16ed4ce2d6
commit 9684bde88a

View File

@ -492,56 +492,100 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
{ {
unsigned i; unsigned i;
struct vk_texture tex; struct vk_texture tex;
VkMemoryAllocateInfo alloc;
VkMemoryRequirements mem_reqs; VkMemoryRequirements mem_reqs;
VkSubresourceLayout layout; VkSubresourceLayout layout;
VkDevice device = vk->context->device; VkImageCreateInfo info;
VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; VkSubmitInfo submit_info;
VkBufferCreateInfo buffer_info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; VkBufferCreateInfo buffer_info;
VkImageViewCreateInfo view = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; VkImageSubresource subresource;
VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; VkDevice device = vk->context->device;
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 };
memset(&tex, 0, sizeof(tex)); mem_reqs.size = 0;
mem_reqs.alignment = 0;
mem_reqs.memoryTypeBits = 0;
info.imageType = VK_IMAGE_TYPE_2D; alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
info.format = format; alloc.pNext = NULL;
info.extent.width = width; alloc.allocationSize = 0;
info.extent.height = height; alloc.memoryTypeIndex = 0;
info.extent.depth = 1;
info.arrayLayers = 1;
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buffer_info.size = width * height * vulkan_format_to_bpp(format); tex.type = VULKAN_TEXTURE_STREAMED;
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 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 info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
* static textures, samplers can be used to enable it dynamically. info.pNext = NULL;
*/ info.flags = 0;
if (type == VULKAN_TEXTURE_STATIC) info.imageType = VK_IMAGE_TYPE_2D;
{ info.format = format;
info.mipLevels = vulkan_num_miplevels(width, height); info.extent.width = width;
tex.mipmap = true; info.extent.height = height;
} info.extent.depth = 1;
else info.mipLevels = 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) if (type == VULKAN_TEXTURE_STREAMED)
{ {
VkFormatProperties format_properties; 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; VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
vkGetPhysicalDeviceFormatProperties( vkGetPhysicalDeviceFormatProperties(
vk->context->gpu, format, &format_properties); 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; type = VULKAN_TEXTURE_STAGING;
} }
} }
@ -549,58 +593,80 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
switch (type) switch (type)
{ {
case VULKAN_TEXTURE_STATIC: 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.tiling = VK_IMAGE_TILING_OPTIMAL;
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | info.usage = VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT; VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 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; break;
case VULKAN_TEXTURE_DYNAMIC: 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.tiling = VK_IMAGE_TILING_OPTIMAL;
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | info.usage = VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT; VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 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; break;
case VULKAN_TEXTURE_STREAMED: case VULKAN_TEXTURE_STREAMED:
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | 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.tiling = VK_IMAGE_TILING_LINEAR;
info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; 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; break;
case VULKAN_TEXTURE_STAGING: case VULKAN_TEXTURE_STAGING:
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
info.initialLayout = VK_IMAGE_LAYOUT_GENERAL; info.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
info.tiling = VK_IMAGE_TILING_LINEAR; 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; break;
case VULKAN_TEXTURE_READBACK: case VULKAN_TEXTURE_READBACK:
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
info.initialLayout = VK_IMAGE_LAYOUT_GENERAL; info.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
info.tiling = VK_IMAGE_TILING_LINEAR; 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; 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; alloc.allocationSize = mem_reqs.size;
switch (type) switch (type)
@ -614,7 +680,8 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
break; break;
default: 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( alloc.memoryTypeIndex = vulkan_find_memory_type_fallback(
&vk->context->memory_properties, &vk->context->memory_properties,
mem_reqs.memoryTypeBits, mem_reqs.memoryTypeBits,
@ -624,25 +691,29 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
tex.need_manual_cache_management = 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; VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0;
break; 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. */ * so fallback to copy path. */
if (type == VULKAN_TEXTURE_STREAMED && 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) VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0)
{ {
/* Recreate texture but for STAGING this time ... */ /* 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"); RARCH_LOG("[Vulkan]: GPU supports linear images as textures"
type = VULKAN_TEXTURE_STAGING; ", but not DEVICE_LOCAL. Falling back to copy path.\n");
type = VULKAN_TEXTURE_STAGING;
vkDestroyImage(device, tex.image, NULL); vkDestroyImage(device, tex.image, NULL);
tex.image = (VkImage)NULL; tex.image = (VkImage)VK_NULL_HANDLE;
info.initialLayout = VK_IMAGE_LAYOUT_GENERAL; 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); vkCreateBuffer(device, &buffer_info, NULL, &tex.buffer);
vkGetBufferMemoryRequirements(device, tex.buffer, &mem_reqs); 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. */ /* We're not reusing the objects themselves. */
if (old && old->view != VK_NULL_HANDLE) if (old)
vkDestroyImageView(vk->context->device, old->view, NULL);
if (old && old->image != VK_NULL_HANDLE)
{ {
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 #ifdef VULKAN_DEBUG_TEXTURE_ALLOC
vulkan_track_dealloc(old->image); vulkan_track_dealloc(old->image);
#endif #endif
} }
if (old && old->buffer != VK_NULL_HANDLE) if (old->buffer != VK_NULL_HANDLE)
vkDestroyBuffer(vk->context->device, old->buffer, NULL); vkDestroyBuffer(vk->context->device, old->buffer, NULL);
/* We can pilfer the old memory and move it over to the new texture. */ /* We can pilfer the old memory and move it over to the new texture. */
if (old && if (
old->memory_size >= mem_reqs.size && old->memory_size >= mem_reqs.size &&
old->memory_type == alloc.memoryTypeIndex) old->memory_type == alloc.memoryTypeIndex)
{ {
tex.memory = old->memory; tex.memory = old->memory;
tex.memory_size = old->memory_size; tex.memory_size = old->memory_size;
tex.memory_type = old->memory_type; tex.memory_type = old->memory_type;
if (old->mapped) if (old->mapped)
vkUnmapMemory(device, old->memory); 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 else
{ {
@ -691,13 +769,6 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
tex.memory_type = alloc.memoryTypeIndex; 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) if (tex.image)
vkBindImageMemory(device, tex.image, tex.memory, 0); vkBindImageMemory(device, tex.image, tex.memory, 0);
if (tex.buffer) 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) if (type != VULKAN_TEXTURE_STAGING && type != VULKAN_TEXTURE_READBACK)
{ {
view.image = tex.image; VkImageViewCreateInfo view;
view.viewType = VK_IMAGE_VIEW_TYPE_2D; view.sType =
view.format = format; 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) if (swizzle)
view.components = *swizzle; view.components = *swizzle;
else view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
{ view.subresourceRange.baseMipLevel = 0;
view.components.r = VK_COMPONENT_SWIZZLE_R; view.subresourceRange.levelCount = info.mipLevels;
view.components.g = VK_COMPONENT_SWIZZLE_G; view.subresourceRange.baseArrayLayer = 0;
view.components.b = VK_COMPONENT_SWIZZLE_B; view.subresourceRange.layerCount = 1;
view.components.a = VK_COMPONENT_SWIZZLE_A;
}
view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
view.subresourceRange.levelCount = info.mipLevels;
view.subresourceRange.layerCount = 1;
vkCreateImageView(device, &view, NULL, &tex.view); vkCreateImageView(device, &view, NULL, &tex.view);
} }
else 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) if (tex.image && info.tiling == VK_IMAGE_TILING_LINEAR)
vkGetImageSubresourceLayout(device, tex.image, &subresource, &layout); vkGetImageSubresourceLayout(device, tex.image, &subresource, &layout);
else if (tex.buffer) else if (tex.buffer)
{ {
layout.offset = 0; layout.size = buffer_info.size;
layout.size = buffer_info.size; layout.rowPitch = width *
layout.rowPitch = width * vulkan_format_to_bpp(format); vulkan_format_to_bpp(format);
} }
else
memset(&layout, 0, sizeof(layout));
tex.stride = layout.rowPitch; tex.stride = layout.rowPitch;
tex.offset = layout.offset; tex.offset = layout.offset;
tex.size = layout.size; tex.size = layout.size;
tex.layout = info.initialLayout; tex.layout = info.initialLayout;
tex.width = width; tex.width = width;
tex.height = height; tex.height = height;
tex.format = format; tex.format = format;
tex.type = type; tex.type = type;
if (initial && (type == VULKAN_TEXTURE_STREAMED || type == VULKAN_TEXTURE_STAGING)) if (initial)
{ {
unsigned y; switch (type)
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(&region, 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, &region);
if (tex.mipmap)
{ {
for (i = 1; i < info.mipLevels; i++) case VULKAN_TEXTURE_STREAMED:
{ case VULKAN_TEXTURE_STAGING:
VkImageBlit blit_region; {
unsigned src_width = MAX(width >> (i - 1), 1); unsigned y;
unsigned src_height = MAX(height >> (i - 1), 1); uint8_t *dst = NULL;
unsigned target_width = MAX(width >> i, 1); const uint8_t *src = NULL;
unsigned target_height = MAX(height >> i, 1); void *ptr = NULL;
memset(&blit_region, 0, sizeof(blit_region)); unsigned bpp = vulkan_format_to_bpp(tex.format);
unsigned stride = tex.width * bpp;
blit_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; vkMapMemory(device, tex.memory, tex.offset, tex.size, 0, &ptr);
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, dst = (uint8_t*)ptr;
* not actual transition. */ src = (const uint8_t*)initial;
vulkan_image_layout_transition(vk, staging, tex.image, for (y = 0; y < tex.height; y++, dst += tex.stride, src += stride)
VK_IMAGE_LAYOUT_GENERAL, memcpy(dst, src, width * bpp);
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, vulkan_sync_texture_to_gpu(vk, &tex);
tex.image, VK_IMAGE_LAYOUT_GENERAL, vkUnmapMemory(device, tex.memory);
tex.image, VK_IMAGE_LAYOUT_GENERAL, }
1, &blit_region, VK_FILTER_LINEAR); 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. */ cmd_info.sType =
vulkan_image_layout_transition(vk, staging, tex.image, VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
VK_IMAGE_LAYOUT_GENERAL, cmd_info.pNext = NULL;
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, cmd_info.commandPool = vk->staging_pool;
VK_ACCESS_TRANSFER_WRITE_BIT, cmd_info.level =
VK_ACCESS_SHADER_READ_BIT, VK_COMMAND_BUFFER_LEVEL_PRIMARY;
VK_PIPELINE_STAGE_TRANSFER_BIT, cmd_info.commandBufferCount = 1;
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);
}
vkEndCommandBuffer(staging); vkAllocateCommandBuffers(vk->context->device,
submit_info.commandBufferCount = 1; &cmd_info, &staging);
submit_info.pCommandBuffers = &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(&region, 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, &region);
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 #ifdef HAVE_THREADS
slock_lock(vk->context->queue_lock); slock_lock(vk->context->queue_lock);
#endif #endif
vkQueueSubmit(vk->context->queue, vkQueueSubmit(vk->context->queue,
1, &submit_info, VK_NULL_HANDLE); 1, &submit_info, VK_NULL_HANDLE);
/* TODO: Very crude, but texture uploads only happen /* TODO: Very crude, but texture uploads only happen
* during init, so waiting for GPU to complete transfer * during init, so waiting for GPU to complete transfer
* and blocking isn't a big deal. */ * and blocking isn't a big deal. */
vkQueueWaitIdle(vk->context->queue); vkQueueWaitIdle(vk->context->queue);
#ifdef HAVE_THREADS #ifdef HAVE_THREADS
slock_unlock(vk->context->queue_lock); slock_unlock(vk->context->queue_lock);
#endif #endif
vkFreeCommandBuffers(vk->context->device, vkFreeCommandBuffers(vk->context->device,
vk->staging_pool, 1, &staging); vk->staging_pool, 1, &staging);
vulkan_destroy_texture( vulkan_destroy_texture(
vk->context->device, &tmp); vk->context->device, &tmp);
tex.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; tex.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
break;
default:
break;
}
} }
return tex; return tex;
} }
@ -915,7 +1018,24 @@ void vulkan_destroy_texture(
if (tex->image) if (tex->image)
vulkan_track_dealloc(tex->image); vulkan_track_dealloc(tex->image);
#endif #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( static void vulkan_write_quad_descriptors(