diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 449a290f3d..a8950a49f5 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -86,6 +86,48 @@ void vulkan_map_persistent_texture(VkDevice device, struct vk_texture *texture) vkMapMemory(device, texture->memory, texture->offset, texture->size, 0, &texture->mapped); } +void vulkan_copy_staging_to_dynamic(vk_t *vk, VkCommandBuffer cmd, + struct vk_texture *dynamic, + struct vk_texture *staging) +{ + VkImageCopy region; + retro_assert(dynamic->type == VULKAN_TEXTURE_DYNAMIC); + retro_assert(staging->type == VULKAN_TEXTURE_STAGING); + + vulkan_transition_texture(vk, staging); + + /* We don't have to sync against previous TRANSFER, since we observed the completion + * by fences. If we have a single texture_optimal, we would need to sync against + * previous transfers to avoid races. + * + * We would also need to optionally maintain extra textures due to changes in resolution, + * so this seems like the sanest and simplest solution. */ + vulkan_image_layout_transition(vk, vk->cmd, dynamic->image, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_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; + + vkCmdCopyImage(vk->cmd, + staging->image, VK_IMAGE_LAYOUT_GENERAL, + dynamic->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ®ion); + + vulkan_image_layout_transition(vk, vk->cmd, dynamic->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); +} + #ifdef VULKAN_DEBUG_TEXTURE_ALLOC static VkImage vk_images[4 * 1024]; static unsigned vk_count; @@ -131,25 +173,17 @@ struct vk_texture vulkan_create_texture(vk_t *vk, VkFormat format, const void *initial, const VkComponentMapping *swizzle, enum vk_texture_type type) { - /* TODO: Evaluate how we should do texture uploads on discrete cards optimally. - * For integrated GPUs, using linear texture is highly desirable to avoid extra copies, but - * we might need to take a DMA transfer with block interleave on desktop GPUs. - * - * Also, Vulkan drivers are not required to support sampling from linear textures - * (only TRANSFER), but seems to work fine on GPUs I've tested so far. */ - struct vk_texture tex; VkMemoryRequirements mem_reqs; VkSubresourceLayout layout; - VkDevice device = vk->context->device; - VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_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 }; - - if (type == VULKAN_TEXTURE_STATIC && !initial) - retro_assert(0 && "Static textures must have initial data.\n"); + VkDevice device = vk->context->device; + VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_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 }; memset(&tex, 0, sizeof(tex)); @@ -160,46 +194,104 @@ struct vk_texture vulkan_create_texture(vk_t *vk, info.extent.depth = 1; info.mipLevels = 1; info.arrayLayers = 1; - info.samples = VK_SAMPLE_COUNT_1_BIT; - info.tiling = type != VULKAN_TEXTURE_STATIC - ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; - info.usage = VK_IMAGE_USAGE_SAMPLED_BIT; - if (type == VULKAN_TEXTURE_STATIC) - info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - if (type == VULKAN_TEXTURE_READBACK) - info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; - info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.samples = VK_SAMPLE_COUNT_1_BIT; - /* We'll transition this on first use for streamed textures. */ - info.initialLayout = (type == VULKAN_TEXTURE_STREAMED) ? - VK_IMAGE_LAYOUT_PREINITIALIZED : - VK_IMAGE_LAYOUT_UNDEFINED; + if (type == VULKAN_TEXTURE_STREAMED) + { + VkFormatProperties format_properties; + 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) + { + RARCH_LOG("[Vulkan]: GPU does not support using linear images as textures. Falling back to copy path.\n"); + type = VULKAN_TEXTURE_STAGING; + } + } + + switch (type) + { + case VULKAN_TEXTURE_STATIC: + 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; + info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + break; + + case VULKAN_TEXTURE_DYNAMIC: + 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; + info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + break; + + case VULKAN_TEXTURE_STREAMED: + info.usage = VK_IMAGE_USAGE_SAMPLED_BIT; + info.tiling = VK_IMAGE_TILING_LINEAR; + info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + break; + + case VULKAN_TEXTURE_STAGING: + info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + 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; + info.tiling = VK_IMAGE_TILING_LINEAR; + info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + break; + } vkCreateImage(device, &info, NULL, &tex.image); #if 0 vulkan_track_alloc(tex.image); #endif - vkGetImageMemoryRequirements(device, tex.image, &mem_reqs); - alloc.allocationSize = mem_reqs.size; - if (type == VULKAN_TEXTURE_STATIC) + switch (type) { - alloc.memoryTypeIndex = - vulkan_find_memory_type_fallback(&vk->context->memory_properties, - mem_reqs.memoryTypeBits, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); + case VULKAN_TEXTURE_STATIC: + case VULKAN_TEXTURE_DYNAMIC: + alloc.memoryTypeIndex = vulkan_find_memory_type_fallback(&vk->context->memory_properties, + mem_reqs.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); + break; + + default: + alloc.memoryTypeIndex = vulkan_find_memory_type_fallback(&vk->context->memory_properties, + mem_reqs.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | + VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + break; } - else + + /* 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_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0) { - alloc.memoryTypeIndex = - vulkan_find_memory_type_fallback(&vk->context->memory_properties, + /* 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; + vkDestroyImage(device, tex.image, NULL); + info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + vkCreateImage(device, &info, NULL, &tex.image); + vkGetImageMemoryRequirements(device, tex.image, &mem_reqs); + alloc.allocationSize = mem_reqs.size; + alloc.memoryTypeIndex = vulkan_find_memory_type_fallback(&vk->context->memory_properties, mem_reqs.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); } @@ -270,8 +362,9 @@ struct vk_texture vulkan_create_texture(vk_t *vk, tex.width = width; tex.height = height; tex.format = format; + tex.type = type; - if (initial && type == VULKAN_TEXTURE_STREAMED) + if (initial && (type == VULKAN_TEXTURE_STREAMED || type == VULKAN_TEXTURE_STAGING)) { unsigned x, y; uint8_t *dst = NULL; @@ -293,19 +386,14 @@ struct vk_texture vulkan_create_texture(vk_t *vk, { VkImageCopy region; VkCommandBuffer staging; - VkCommandBufferAllocateInfo info = { - VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - VkCommandBufferBeginInfo begin_info = { - VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - unsigned bpp = vulkan_format_to_bpp(tex.format); - struct vk_texture tmp = vulkan_create_texture(vk, NULL, - width, height, format, initial, NULL, VULKAN_TEXTURE_STREAMED); + unsigned bpp = vulkan_format_to_bpp(tex.format); + struct vk_texture tmp = vulkan_create_texture(vk, NULL, + width, height, format, initial, NULL, VULKAN_TEXTURE_STAGING); - info.commandPool = vk->staging_pool; - info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - info.commandBufferCount = 1; - vkAllocateCommandBuffers(vk->context->device, &info, &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); @@ -1358,6 +1446,8 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk, /* Limit latency. */ if (desired_swapchain_images > 3) desired_swapchain_images = 3; + if (desired_swapchain_images < surface_properties.minImageCount) + desired_swapchain_images = surface_properties.minImageCount; if ((surface_properties.maxImageCount > 0) && (desired_swapchain_images > surface_properties.maxImageCount)) diff --git a/gfx/common/vulkan_common.h b/gfx/common/vulkan_common.h index 5496a3dfd1..1a952d2ff6 100644 --- a/gfx/common/vulkan_common.h +++ b/gfx/common/vulkan_common.h @@ -72,8 +72,22 @@ enum vk_texture_type { + /* We will use the texture as a sampled linear texture. */ VULKAN_TEXTURE_STREAMED = 0, + + /* We will use the texture as a linear texture, but only + * for copying to a DYNAMIC texture. */ + VULKAN_TEXTURE_STAGING, + + /* We will use the texture as an optimally tiled texture, + * and we will update the texture by copying from STAGING + * textures. */ + VULKAN_TEXTURE_DYNAMIC, + + /* We will upload content once. */ VULKAN_TEXTURE_STATIC, + + /* We will use the texture for reading back transfers from GPU. */ VULKAN_TEXTURE_READBACK }; @@ -193,6 +207,7 @@ struct vk_texture uint32_t memory_type; VkImageLayout layout; + enum vk_texture_type type; bool default_smooth; }; @@ -264,6 +279,7 @@ struct vk_per_frame { struct vk_image backbuffer; struct vk_texture texture; + struct vk_texture texture_optimal; struct vk_buffer_chain vbo; struct vk_buffer_chain ubo; struct vk_descriptor_manager descriptor_manager; @@ -356,6 +372,9 @@ typedef struct vk struct { struct vk_texture textures[VULKAN_MAX_SWAPCHAIN_IMAGES]; + struct vk_texture textures_optimal[VULKAN_MAX_SWAPCHAIN_IMAGES]; + bool dirty[VULKAN_MAX_SWAPCHAIN_IMAGES]; + float alpha; unsigned last_index; bool enable; @@ -425,6 +444,10 @@ void vulkan_transition_texture(vk_t *vk, struct vk_texture *texture); void vulkan_map_persistent_texture(VkDevice device, struct vk_texture *tex); void vulkan_destroy_texture(VkDevice device, struct vk_texture *tex); +void vulkan_copy_staging_to_dynamic(vk_t *vk, VkCommandBuffer cmd, + struct vk_texture *dynamic, + struct vk_texture *staging); + /* VBO will be written to here. */ void vulkan_draw_quad(vk_t *vk, const struct vk_draw_quad *quad); diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 129ba55d09..31aa7f63af 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -461,6 +461,13 @@ static void vulkan_init_textures(vk_t *vk) NULL, NULL, VULKAN_TEXTURE_STREAMED); vulkan_map_persistent_texture(vk->context->device, &vk->swapchain[i].texture); + + if (vk->swapchain[i].texture.type == VULKAN_TEXTURE_STAGING) + { + vk->swapchain[i].texture_optimal = vulkan_create_texture(vk, NULL, + vk->tex_w, vk->tex_h, vk->tex_fmt, + NULL, NULL, VULKAN_TEXTURE_DYNAMIC); + } } } } @@ -471,9 +478,12 @@ static void vulkan_deinit_textures(vk_t *vk) unsigned i; for (i = 0; i < vk->num_swapchain_images; i++) + { if (vk->swapchain[i].texture.memory != VK_NULL_HANDLE) - vulkan_destroy_texture(vk->context->device, - &vk->swapchain[i].texture); + vulkan_destroy_texture(vk->context->device, &vk->swapchain[i].texture); + if (vk->swapchain[i].texture_optimal.memory != VK_NULL_HANDLE) + vulkan_destroy_texture(vk->context->device, &vk->swapchain[i].texture_optimal); + } } static void vulkan_deinit_command_buffers(vk_t *vk) @@ -671,9 +681,12 @@ static void vulkan_deinit_menu(vk_t *vk) { unsigned i; for (i = 0; i < VULKAN_MAX_SWAPCHAIN_IMAGES; i++) + { if (vk->menu.textures[i].memory) - vulkan_destroy_texture(vk->context->device, - &vk->menu.textures[i]); + vulkan_destroy_texture(vk->context->device, &vk->menu.textures[i]); + if (vk->menu.textures_optimal[i].memory) + vulkan_destroy_texture(vk->context->device, &vk->menu.textures_optimal[i]); + } } static void vulkan_free(void *data) @@ -1323,6 +1336,13 @@ static bool vulkan_frame(void *data, const void *frame, vulkan_buffer_chain_discard(&chain->vbo); vulkan_buffer_chain_discard(&chain->ubo); + /* Start recording the command buffer. */ + vk->cmd = chain->cmd; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkResetCommandBuffer(vk->cmd, 0); + vkBeginCommandBuffer(vk->cmd, &begin_info); + memset(&vk->tracker, 0, sizeof(vk->tracker)); + /* Upload texture */ retro_perf_start(©_frame); if (frame && !vk->hw.enable) @@ -1336,9 +1356,16 @@ static bool vulkan_frame(void *data, const void *frame, || chain->texture.height != frame_height) { chain->texture = vulkan_create_texture(vk, &chain->texture, - frame_width, frame_height, chain->texture.format, - NULL, NULL, VULKAN_TEXTURE_STREAMED); + frame_width, frame_height, chain->texture.format, NULL, NULL, + chain->texture_optimal.memory ? VULKAN_TEXTURE_STAGING : VULKAN_TEXTURE_STREAMED); vulkan_map_persistent_texture(vk->context->device, &chain->texture); + + if (chain->texture.type == VULKAN_TEXTURE_STAGING) + { + chain->texture_optimal = vulkan_create_texture(vk, &chain->texture_optimal, + frame_width, frame_height, chain->texture_optimal.format, + NULL, NULL, VULKAN_TEXTURE_DYNAMIC); + } } if (frame != chain->texture.mapped) @@ -1352,17 +1379,17 @@ static bool vulkan_frame(void *data, const void *frame, memcpy(dst, src, frame_width * bpp); } + /* If we have an optimal texture, copy to that now. */ + if (chain->texture_optimal.memory != VK_NULL_HANDLE) + { + vulkan_copy_staging_to_dynamic(vk, vk->cmd, + &chain->texture_optimal, &chain->texture); + } + vk->last_valid_index = frame_index; } retro_perf_stop(©_frame); - /* Start recording the command buffer. */ - vk->cmd = chain->cmd; - begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - vkResetCommandBuffer(vk->cmd, 0); - vkBeginCommandBuffer(vk->cmd, &begin_info); - memset(&vk->tracker, 0, sizeof(vk->tracker)); - /* Notify filter chain about the new sync index. */ vulkan_filter_chain_notify_sync_index(vk->filter_chain, frame_index); @@ -1399,9 +1426,11 @@ static bool vulkan_frame(void *data, const void *frame, } else { - struct vk_texture *tex = & - vk->swapchain[vk->last_valid_index].texture; - vulkan_transition_texture(vk, tex); + struct vk_texture *tex = &vk->swapchain[vk->last_valid_index].texture; + if (vk->swapchain[vk->last_valid_index].texture_optimal.memory != VK_NULL_HANDLE) + tex = &vk->swapchain[vk->last_valid_index].texture_optimal; + else + vulkan_transition_texture(vk, tex); input.view = tex->view; input.layout = tex->layout; @@ -1450,16 +1479,30 @@ static bool vulkan_frame(void *data, const void *frame, if (vk->menu.textures[vk->menu.last_index].image != VK_NULL_HANDLE) { struct vk_draw_quad quad; + struct vk_texture *optimal = &vk->menu.textures_optimal[vk->menu.last_index]; vulkan_set_viewport(vk, width, height, vk->menu.full_screen, false); quad.pipeline = vk->pipelines.alpha_blend; - quad.texture = &vk->menu.textures[vk->menu.last_index]; - quad.sampler = vk->samplers.linear; - quad.mvp = &vk->mvp_no_rot; - quad.color.r = 1.0f; - quad.color.g = 1.0f; - quad.color.b = 1.0f; - quad.color.a = vk->menu.alpha; + quad.texture = &vk->menu.textures[vk->menu.last_index]; + + if (optimal->memory != VK_NULL_HANDLE) + { + if (vk->menu.dirty[vk->menu.last_index]) + { + vulkan_copy_staging_to_dynamic(vk, vk->cmd, + optimal, + quad.texture); + vk->menu.dirty[vk->menu.last_index] = false; + } + quad.texture = optimal; + } + + quad.sampler = vk->samplers.linear; + quad.mvp = &vk->mvp_no_rot; + quad.color.r = 1.0f; + quad.color.g = 1.0f; + quad.color.b = 1.0f; + quad.color.a = vk->menu.alpha; vulkan_draw_quad(vk, &quad); } } @@ -1657,6 +1700,13 @@ static bool vulkan_get_current_sw_framebuffer(void *data, framebuffer->width, framebuffer->height, chain->texture.format, NULL, NULL, VULKAN_TEXTURE_STREAMED); vulkan_map_persistent_texture(vk->context->device, &chain->texture); + + if (chain->texture.type == VULKAN_TEXTURE_STAGING) + { + chain->texture_optimal = vulkan_create_texture(vk, &chain->texture_optimal, + framebuffer->width, framebuffer->height, chain->texture_optimal.format, + NULL, NULL, VULKAN_TEXTURE_DYNAMIC); + } } framebuffer->data = chain->texture.mapped; @@ -1690,9 +1740,10 @@ static void vulkan_set_texture_frame(void *data, { uint8_t *ptr; unsigned x, y; - vk_t *vk = (vk_t*)data; - unsigned index = vk->context->current_swapchain_index; - struct vk_texture *texture = &vk->menu.textures[index]; + vk_t *vk = (vk_t*)data; + unsigned index = vk->context->current_swapchain_index; + struct vk_texture *texture = &vk->menu.textures[index]; + struct vk_texture *texture_optimal = &vk->menu.textures_optimal[index]; const VkComponentMapping br_swizzle = { VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, @@ -1709,7 +1760,8 @@ static void vulkan_set_texture_frame(void *data, texture->memory ? texture : NULL, width, height, rgb32 ? VK_FORMAT_B8G8R8A8_UNORM : VK_FORMAT_B4G4R4A4_UNORM_PACK16, - NULL, rgb32 ? NULL : &br_swizzle, VULKAN_TEXTURE_STREAMED); + NULL, rgb32 ? NULL : &br_swizzle, + texture_optimal->memory ? VULKAN_TEXTURE_STAGING : VULKAN_TEXTURE_STREAMED); vkMapMemory(vk->context->device, texture->memory, texture->offset, texture->size, 0, (void**)&ptr); @@ -1723,6 +1775,18 @@ static void vulkan_set_texture_frame(void *data, vkUnmapMemory(vk->context->device, texture->memory); vk->menu.alpha = alpha; vk->menu.last_index = index; + + if (texture->type == VULKAN_TEXTURE_STAGING) + { + *texture_optimal = vulkan_create_texture(vk, + texture_optimal->memory ? texture_optimal : NULL, + width, height, + rgb32 ? VK_FORMAT_B8G8R8A8_UNORM : VK_FORMAT_B4G4R4A4_UNORM_PACK16, + NULL, rgb32 ? NULL : &br_swizzle, + VULKAN_TEXTURE_DYNAMIC); + } + + vk->menu.dirty[index] = true; } static void vulkan_set_texture_enable(void *data, bool state, bool full_screen) diff --git a/menu/drivers_display/menu_display_vulkan.c b/menu/drivers_display/menu_display_vulkan.c index cb11361724..41406ddbb2 100644 --- a/menu/drivers_display/menu_display_vulkan.c +++ b/menu/drivers_display/menu_display_vulkan.c @@ -182,14 +182,30 @@ static void menu_display_vk_draw_bg(void *data) static void menu_display_vk_restore_clear_color(void) { - //glClearColor(0.0f, 0.0f, 0.0f, 0.00f); } static void menu_display_vk_clear_color(void *data) { - (void)data; - /* FIXME: This makes little sense in Vulkan. - * We shouldn't be clearing mid-screen nilly willy. */ + VkClearRect rect; + VkClearAttachment attachment = { VK_IMAGE_ASPECT_COLOR_BIT }; + menu_display_ctx_clearcolor_t *clearcolor = + (menu_display_ctx_clearcolor_t*)data; + + vk_t *vk = vk_get_ptr(); + if (!vk || !clearcolor) + return; + + attachment.clearValue.color.float32[0] = clearcolor->r; + attachment.clearValue.color.float32[1] = clearcolor->g; + attachment.clearValue.color.float32[2] = clearcolor->b; + attachment.clearValue.color.float32[3] = clearcolor->a; + + memset(&rect, 0, sizeof(rect)); + rect.rect.extent.width = vk->context->swapchain_width; + rect.rect.extent.height = vk->context->swapchain_height; + rect.layerCount = 1; + + vkCmdClearAttachments(vk->cmd, 1, &attachment, 1, &rect); } static const float *menu_display_vk_get_tex_coords(void)