mirror of
https://github.com/libretro/RetroArch
synced 2025-01-30 12:32:52 +00:00
Vulkan maintenance fixes.
- Use VkBuffer instead of linear VkImage for staging uploads. Not guaranteed to be supported, and desktop IHVs recommend using buffers. - Fix validation error where swapchain recreation with reuse of the old VkSwapchainKHR does not create a fence properly, leading to validation error next time that swapchain index is observed.
This commit is contained in:
parent
1155e11cfc
commit
af534f0fa8
@ -184,13 +184,12 @@ void vulkan_copy_staging_to_dynamic(vk_t *vk, VkCommandBuffer cmd,
|
||||
struct vk_texture *dynamic,
|
||||
struct vk_texture *staging)
|
||||
{
|
||||
VkImageCopy region;
|
||||
VkBufferImageCopy region;
|
||||
|
||||
retro_assert(dynamic->type == VULKAN_TEXTURE_DYNAMIC);
|
||||
retro_assert(staging->type == VULKAN_TEXTURE_STAGING);
|
||||
|
||||
vulkan_sync_texture_to_gpu(vk, staging);
|
||||
vulkan_transition_texture(vk, cmd, staging);
|
||||
|
||||
/* We don't have to sync against previous TRANSFER,
|
||||
* since we observed the completion by fences.
|
||||
@ -208,15 +207,14 @@ void vulkan_copy_staging_to_dynamic(vk_t *vk, VkCommandBuffer cmd,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
||||
memset(®ion, 0, sizeof(region));
|
||||
region.extent.width = dynamic->width;
|
||||
region.extent.height = dynamic->height;
|
||||
region.extent.depth = 1;
|
||||
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.srcSubresource.layerCount = 1;
|
||||
region.dstSubresource = region.srcSubresource;
|
||||
region.imageExtent.width = dynamic->width;
|
||||
region.imageExtent.height = dynamic->height;
|
||||
region.imageExtent.depth = 1;
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
|
||||
vkCmdCopyImage(cmd,
|
||||
staging->image, VK_IMAGE_LAYOUT_GENERAL,
|
||||
vkCmdCopyBufferToImage(cmd,
|
||||
staging->buffer,
|
||||
dynamic->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, ®ion);
|
||||
|
||||
@ -323,6 +321,7 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
|
||||
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 };
|
||||
@ -338,6 +337,10 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
|
||||
info.extent.height = height;
|
||||
info.extent.depth = 1;
|
||||
info.arrayLayers = 1;
|
||||
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
buffer_info.size = width * height * vulkan_format_to_bpp(format);
|
||||
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
/* For simplicity, always build mipmaps for
|
||||
* static textures, samplers can be used to enable it dynamically.
|
||||
@ -355,7 +358,7 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
|
||||
if (type == VULKAN_TEXTURE_STREAMED)
|
||||
{
|
||||
VkFormatProperties format_properties;
|
||||
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(
|
||||
@ -396,23 +399,33 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
|
||||
break;
|
||||
|
||||
case VULKAN_TEXTURE_STAGING:
|
||||
info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
info.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
info.tiling = VK_IMAGE_TILING_LINEAR;
|
||||
info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||
break;
|
||||
|
||||
case VULKAN_TEXTURE_READBACK:
|
||||
info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
info.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
info.tiling = VK_IMAGE_TILING_LINEAR;
|
||||
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
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)
|
||||
@ -451,11 +464,12 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
|
||||
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 = NULL;
|
||||
info.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
|
||||
info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
vkCreateImage(device, &info, NULL, &tex.image);
|
||||
|
||||
vkGetImageMemoryRequirements(device, tex.image, &mem_reqs);
|
||||
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
vkCreateBuffer(device, &buffer_info, NULL, &tex.buffer);
|
||||
vkGetBufferMemoryRequirements(device, tex.buffer, &mem_reqs);
|
||||
|
||||
alloc.allocationSize = mem_reqs.size;
|
||||
alloc.memoryTypeIndex = vulkan_find_memory_type_fallback(
|
||||
@ -478,6 +492,8 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
|
||||
vulkan_track_dealloc(old->image);
|
||||
#endif
|
||||
}
|
||||
if (old && 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 &&
|
||||
@ -507,7 +523,10 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
|
||||
memset(old, 0, sizeof(*old));
|
||||
}
|
||||
|
||||
if (tex.image)
|
||||
vkBindImageMemory(device, tex.image, tex.memory, 0);
|
||||
if (tex.buffer)
|
||||
vkBindBufferMemory(device, tex.buffer, tex.memory, 0);
|
||||
|
||||
if (type != VULKAN_TEXTURE_STAGING && type != VULKAN_TEXTURE_READBACK)
|
||||
{
|
||||
@ -532,8 +551,14 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
|
||||
else
|
||||
tex.view = VK_NULL_HANDLE;
|
||||
|
||||
if (info.tiling == VK_IMAGE_TILING_LINEAR)
|
||||
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);
|
||||
}
|
||||
else
|
||||
memset(&layout, 0, sizeof(layout));
|
||||
|
||||
@ -568,7 +593,7 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
|
||||
}
|
||||
else if (initial && type == VULKAN_TEXTURE_STATIC)
|
||||
{
|
||||
VkImageCopy region;
|
||||
VkBufferImageCopy region;
|
||||
VkCommandBuffer staging;
|
||||
struct vk_texture tmp = vulkan_create_texture(vk, NULL,
|
||||
width, height, format, initial, NULL, VULKAN_TEXTURE_STAGING);
|
||||
@ -583,12 +608,6 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
|
||||
|
||||
vkBeginCommandBuffer(staging, &begin_info);
|
||||
|
||||
vulkan_image_layout_transition(vk, staging, tmp.image,
|
||||
VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_GENERAL,
|
||||
VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
|
||||
VK_PIPELINE_STAGE_HOST_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
||||
/* 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.
|
||||
@ -603,16 +622,14 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
||||
memset(®ion, 0, sizeof(region));
|
||||
region.extent.width = width;
|
||||
region.extent.height = height;
|
||||
region.extent.depth = 1;
|
||||
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.srcSubresource.layerCount = 1;
|
||||
region.dstSubresource = region.srcSubresource;
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
region.imageExtent.width = width;
|
||||
region.imageExtent.height = height;
|
||||
region.imageExtent.depth = 1;
|
||||
|
||||
vkCmdCopyImage(staging,
|
||||
tmp.image,
|
||||
VK_IMAGE_LAYOUT_GENERAL,
|
||||
vkCmdCopyBufferToImage(staging,
|
||||
tmp.buffer,
|
||||
tex.image,
|
||||
tex.mipmap ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, ®ion);
|
||||
@ -710,11 +727,17 @@ void vulkan_destroy_texture(
|
||||
{
|
||||
if (tex->mapped)
|
||||
vkUnmapMemory(device, tex->memory);
|
||||
vkFreeMemory(device, tex->memory, NULL);
|
||||
if (tex->view)
|
||||
vkDestroyImageView(device, tex->view, NULL);
|
||||
if (tex->image)
|
||||
vkDestroyImage(device, tex->image, NULL);
|
||||
if (tex->buffer)
|
||||
vkDestroyBuffer(device, tex->buffer, NULL);
|
||||
if (tex->memory)
|
||||
vkFreeMemory(device, tex->memory, NULL);
|
||||
|
||||
#ifdef VULKAN_DEBUG_TEXTURE_ALLOC
|
||||
if (tex->image)
|
||||
vulkan_track_dealloc(tex->image);
|
||||
#endif
|
||||
memset(tex, 0, sizeof(*tex));
|
||||
@ -762,6 +785,9 @@ static void vulkan_write_quad_descriptors(
|
||||
|
||||
void vulkan_transition_texture(vk_t *vk, VkCommandBuffer cmd, struct vk_texture *texture)
|
||||
{
|
||||
if (!texture->image)
|
||||
return;
|
||||
|
||||
/* Transition to GENERAL layout for linear streamed textures.
|
||||
* We're using linear textures here, so only
|
||||
* GENERAL layout is supported.
|
||||
@ -782,14 +808,6 @@ void vulkan_transition_texture(vk_t *vk, VkCommandBuffer cmd, struct vk_texture
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
break;
|
||||
|
||||
case VULKAN_TEXTURE_STAGING:
|
||||
vulkan_image_layout_transition(vk, cmd, texture->image,
|
||||
texture->layout, VK_IMAGE_LAYOUT_GENERAL,
|
||||
VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
|
||||
VK_PIPELINE_STAGE_HOST_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
break;
|
||||
|
||||
default:
|
||||
retro_assert(0 && "Attempting to transition invalid texture type.\n");
|
||||
break;
|
||||
@ -2455,6 +2473,22 @@ static void vulkan_acquire_wait_fences(gfx_ctx_vulkan_data_t *vk)
|
||||
vkCreateFence(vk->context.device, &fence_info, NULL, next_fence);
|
||||
}
|
||||
|
||||
static void vulkan_create_wait_fences(gfx_ctx_vulkan_data_t *vk)
|
||||
{
|
||||
VkFenceCreateInfo fence_info =
|
||||
{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < vk->context.num_swapchain_images; i++)
|
||||
{
|
||||
if (!vk->context.swapchain_fences[i])
|
||||
{
|
||||
vkCreateFence(vk->context.device, &fence_info, NULL,
|
||||
&vk->context.swapchain_fences[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vulkan_acquire_next_image(gfx_ctx_vulkan_data_t *vk)
|
||||
{
|
||||
unsigned index;
|
||||
@ -2577,6 +2611,7 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
|
||||
/* Do not bother creating a swapchain redundantly. */
|
||||
RARCH_LOG("[Vulkan]: Do not need to re-create swapchain.\n");
|
||||
vk->created_new_swapchain = false;
|
||||
vulkan_create_wait_fences(vk);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2814,5 +2849,6 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
|
||||
|
||||
/* Force driver to reset swapchain image handles. */
|
||||
vk->context.invalid_swapchain = true;
|
||||
vulkan_create_wait_fences(vk);
|
||||
return true;
|
||||
}
|
||||
|
@ -179,6 +179,7 @@ struct vk_texture
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
VkDeviceMemory memory;
|
||||
VkBuffer buffer;
|
||||
|
||||
VkFormat format;
|
||||
|
||||
|
@ -854,6 +854,8 @@ static void vulkan_init_static_resources(vk_t *vk)
|
||||
uint32_t blank[4 * 4];
|
||||
VkCommandPoolCreateInfo pool_info = {
|
||||
VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
|
||||
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||
|
||||
/* Create the pipeline cache. */
|
||||
VkPipelineCacheCreateInfo cache = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
|
||||
@ -1509,23 +1511,22 @@ static void vulkan_set_viewport(void *data, unsigned viewport_width,
|
||||
|
||||
static void vulkan_readback(vk_t *vk)
|
||||
{
|
||||
VkImageCopy region;
|
||||
VkBufferImageCopy region;
|
||||
struct vk_texture *staging;
|
||||
struct video_viewport vp;
|
||||
VkMemoryBarrier barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
|
||||
|
||||
vulkan_viewport_info(vk, &vp);
|
||||
memset(®ion, 0, sizeof(region));
|
||||
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.srcSubresource.layerCount = 1;
|
||||
region.dstSubresource = region.srcSubresource;
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
region.imageOffset.x = vp.x;
|
||||
region.imageOffset.y = vp.y;
|
||||
region.imageExtent.width = vp.width;
|
||||
region.imageExtent.height = vp.height;
|
||||
region.imageExtent.depth = 1;
|
||||
|
||||
region.srcOffset.x = vp.x;
|
||||
region.srcOffset.y = vp.y;
|
||||
region.extent.width = vp.width;
|
||||
region.extent.height = vp.height;
|
||||
region.extent.depth = 1;
|
||||
|
||||
/* FIXME: We won't actually get format conversion with vkCmdCopyImage, so have to check
|
||||
/* FIXME: We won't actually get format conversion with vkCmdCopyImageToBuffer, so have to check
|
||||
* properly for this. BGRA seems to be the default for all swapchains. */
|
||||
if (vk->context->swapchain_format != VK_FORMAT_B8G8R8A8_UNORM)
|
||||
RARCH_WARN("[Vulkan]: Backbuffer is not BGRA8888, readbacks might not work properly.\n");
|
||||
@ -1537,25 +1538,18 @@ static void vulkan_readback(vk_t *vk)
|
||||
VK_FORMAT_B8G8R8A8_UNORM,
|
||||
NULL, NULL, VULKAN_TEXTURE_READBACK);
|
||||
|
||||
/* Go through the long-winded dance of remapping image layouts. */
|
||||
vulkan_image_layout_transition(vk, vk->cmd, staging->image,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL,
|
||||
0, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
||||
vkCmdCopyImage(vk->cmd, vk->chain->backbuffer.image,
|
||||
vkCmdCopyImageToBuffer(vk->cmd, vk->chain->backbuffer.image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
staging->image,
|
||||
VK_IMAGE_LAYOUT_GENERAL,
|
||||
staging->buffer,
|
||||
1, ®ion);
|
||||
|
||||
/* Make the data visible to host. */
|
||||
vulkan_image_layout_transition(vk, vk->cmd, staging->image,
|
||||
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT,
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
|
||||
vkCmdPipelineBarrier(vk->cmd,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_HOST_BIT);
|
||||
VK_PIPELINE_STAGE_HOST_BIT, 0,
|
||||
1, &barrier, 0, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static void vulkan_inject_black_frame(vk_t *vk, video_frame_info_t *video_info)
|
||||
|
Loading…
x
Reference in New Issue
Block a user