diff --git a/gfx/common/vulkan_common.c b/gfx/common/vulkan_common.c index 68e4ca4f2e..7ad90db896 100644 --- a/gfx/common/vulkan_common.c +++ b/gfx/common/vulkan_common.c @@ -265,6 +265,18 @@ void vulkan_sync_texture_to_cpu(vk_t *vk, const struct vk_texture *tex) vkInvalidateMappedMemoryRanges(vk->context->device, 1, &range); } +static unsigned vulkan_num_miplevels(unsigned width, unsigned height) +{ + unsigned size = width > height ? width : height; + unsigned levels = 0; + while (size) + { + levels++; + size >>= 1; + } + return levels; +} + struct vk_texture vulkan_create_texture(vk_t *vk, struct vk_texture *old, unsigned width, unsigned height, @@ -284,6 +296,7 @@ struct vk_texture vulkan_create_texture(vk_t *vk, 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 }; + unsigned i; memset(&tex, 0, sizeof(tex)); @@ -292,8 +305,19 @@ struct vk_texture vulkan_create_texture(vk_t *vk, info.extent.width = width; info.extent.height = height; info.extent.depth = 1; - info.mipLevels = 1; info.arrayLayers = 1; + + /* 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.samples = VK_SAMPLE_COUNT_1_BIT; if (type == VULKAN_TEXTURE_STREAMED) @@ -317,7 +341,9 @@ struct vk_texture vulkan_create_texture(vk_t *vk, 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.usage = VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; break; @@ -545,13 +571,80 @@ struct vk_texture vulkan_create_texture(vk_t *vk, tex.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - 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); + if (tex.mipmap) + { + /* 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_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT); + + 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; + + vkCmdBlitImage(staging, + tex.image, VK_IMAGE_LAYOUT_GENERAL, + tex.image, VK_IMAGE_LAYOUT_GENERAL, + 1, &blit_region, VK_FILTER_LINEAR); + + if (i + 1 < info.mipLevels) + { + /* 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); + } + } + + /* 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); + } vkEndCommandBuffer(staging); submit_info.commandBufferCount = 1; diff --git a/gfx/common/vulkan_common.h b/gfx/common/vulkan_common.h index b182d5eb8b..a999be006a 100644 --- a/gfx/common/vulkan_common.h +++ b/gfx/common/vulkan_common.h @@ -173,6 +173,7 @@ struct vk_texture enum vk_texture_type type; bool default_smooth; bool need_manual_cache_management; + bool mipmap; }; struct vk_buffer @@ -352,6 +353,8 @@ typedef struct vk { VkSampler linear; VkSampler nearest; + VkSampler mipmap_nearest; + VkSampler mipmap_linear; } samplers; unsigned last_valid_index; diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 1e350b009f..15d31308f9 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -483,16 +483,31 @@ static void vulkan_init_samplers(vk_t *vk) vkCreateSampler(vk->context->device, &info, NULL, &vk->samplers.nearest); - info.magFilter = VK_FILTER_LINEAR; - info.minFilter = VK_FILTER_LINEAR; + info.magFilter = VK_FILTER_LINEAR; + info.minFilter = VK_FILTER_LINEAR; vkCreateSampler(vk->context->device, &info, NULL, &vk->samplers.linear); + + info.maxLod = VK_LOD_CLAMP_NONE; + info.magFilter = VK_FILTER_NEAREST; + info.minFilter = VK_FILTER_NEAREST; + info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + vkCreateSampler(vk->context->device, + &info, NULL, &vk->samplers.mipmap_nearest); + + info.magFilter = VK_FILTER_LINEAR; + info.minFilter = VK_FILTER_LINEAR; + info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + vkCreateSampler(vk->context->device, + &info, NULL, &vk->samplers.mipmap_linear); } static void vulkan_deinit_samplers(vk_t *vk) { vkDestroySampler(vk->context->device, vk->samplers.nearest, NULL); vkDestroySampler(vk->context->device, vk->samplers.linear, NULL); + vkDestroySampler(vk->context->device, vk->samplers.mipmap_nearest, NULL); + vkDestroySampler(vk->context->device, vk->samplers.mipmap_linear, NULL); } static void vulkan_init_buffers(vk_t *vk) @@ -1749,7 +1764,9 @@ static bool vulkan_frame(void *data, const void *frame, quad.texture = optimal; } - quad.sampler = vk->samplers.linear; + quad.sampler = optimal->mipmap ? + vk->samplers.mipmap_linear : vk->samplers.linear; + quad.mvp = &vk->mvp_no_rot; quad.color.r = 1.0f; quad.color.g = 1.0f; @@ -2137,10 +2154,9 @@ static uintptr_t vulkan_load_texture(void *video_data, void *data, image->width, image->height, VK_FORMAT_B8G8R8A8_UNORM, image->pixels, NULL, VULKAN_TEXTURE_STATIC); - /* TODO: Actually add mipmapping support. - * Optimal tiling would make sense here as well ... */ texture->default_smooth = filter_type == TEXTURE_FILTER_MIPMAP_LINEAR || filter_type == TEXTURE_FILTER_LINEAR; + texture->mipmap = filter_type == TEXTURE_FILTER_MIPMAP_LINEAR; return (uintptr_t)texture; } @@ -2370,7 +2386,8 @@ static void vulkan_render_overlay(vk_t *vk) memset(&call, 0, sizeof(call)); call.pipeline = vk->display.pipelines[3]; /* Strip with blend */ call.texture = &vk->overlay.images[i]; - call.sampler = vk->samplers.linear; + call.sampler = call.texture->mipmap ? + vk->samplers.mipmap_linear : vk->samplers.linear; call.uniform = &vk->mvp; call.uniform_size = sizeof(vk->mvp); call.vbo = ⦥ diff --git a/menu/drivers_display/menu_display_vulkan.c b/menu/drivers_display/menu_display_vulkan.c index 8fd817d7e1..04dfc8e5c4 100644 --- a/menu/drivers_display/menu_display_vulkan.c +++ b/menu/drivers_display/menu_display_vulkan.c @@ -193,8 +193,9 @@ static void menu_display_vk_draw(void *data) vk->display.pipelines[ to_display_pipeline(draw->prim_type, vk->display.blend)], texture, - texture->default_smooth - ? vk->samplers.linear : vk->samplers.nearest, + texture->mipmap ? + vk->samplers.mipmap_linear : + (texture->default_smooth ? vk->samplers.linear : vk->samplers.nearest), draw->matrix_data ? draw->matrix_data : menu_display_vk_get_default_mvp(), sizeof(math_matrix_4x4),