diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 4c788ac69f..1e350b009f 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -651,6 +651,8 @@ static bool vulkan_init_default_filter_chain(vk_t *vk) info.gpu = vk->context->gpu; info.memory_properties = &vk->context->memory_properties; info.pipeline_cache = vk->pipelines.cache; + info.queue = vk->context->queue; + info.command_pool = vk->swapchain[vk->context->current_swapchain_index].cmd_pool; info.max_input_size.width = vk->tex_w; info.max_input_size.height = vk->tex_h; info.swapchain.viewport = vk->vk_vp; @@ -683,6 +685,8 @@ static bool vulkan_init_filter_chain_preset(vk_t *vk, const char *shader_path) info.gpu = vk->context->gpu; info.memory_properties = &vk->context->memory_properties; info.pipeline_cache = vk->pipelines.cache; + info.queue = vk->context->queue; + info.command_pool = vk->swapchain[vk->context->current_swapchain_index].cmd_pool; info.max_input_size.width = vk->tex_w; info.max_input_size.height = vk->tex_h; info.swapchain.viewport = vk->vk_vp; diff --git a/gfx/drivers_shader/shader_vulkan.cpp b/gfx/drivers_shader/shader_vulkan.cpp index ce44047640..3885e4dc7d 100644 --- a/gfx/drivers_shader/shader_vulkan.cpp +++ b/gfx/drivers_shader/shader_vulkan.cpp @@ -27,6 +27,7 @@ #include "../video_shader_driver.h" #include "../../verbosity.h" #include "../../msg_hash.h" +#include "../../libretro-common/include/formats/image.h" using namespace std; @@ -164,6 +165,9 @@ class Buffer const VkBuffer &get_buffer() const { return buffer; } + Buffer(Buffer&&) = delete; + void operator=(Buffer&&) = delete; + private: VkDevice device; VkBuffer buffer; @@ -171,6 +175,44 @@ class Buffer size_t size; }; +class StaticTexture +{ + public: + StaticTexture(string id, + VkDevice device, + VkImage image, + VkImageView view, + VkDeviceMemory memory, + unique_ptr buffer); + ~StaticTexture(); + + StaticTexture(StaticTexture&&) = delete; + void operator=(StaticTexture&&) = delete; + + void release_staging_buffer() + { + buffer.reset(); + } + + void set_id(string name) + { + id = move(name); + } + + const string &get_id() const + { + return id; + } + + private: + VkDevice device; + VkImage image; + VkImageView view; + VkDeviceMemory memory; + unique_ptr buffer; + string id; +}; + class Framebuffer { public: @@ -234,6 +276,7 @@ struct CommonResources vector original_history; vector framebuffer_feedback; vector pass_outputs; + vector> luts; unordered_map texture_semantic_map; unordered_map texture_semantic_uniform_map; @@ -447,6 +490,9 @@ struct vulkan_filter_chain void set_frame_count_period(unsigned pass, unsigned period); void set_pass_name(unsigned pass, const char *name); + void add_static_texture(unique_ptr texture); + void release_staging_buffers(); + private: VkDevice device; VkPhysicalDevice gpu; @@ -572,6 +618,11 @@ void vulkan_filter_chain::set_input_texture( input_texture = texture; } +void vulkan_filter_chain::add_static_texture(unique_ptr texture) +{ + common.luts.push_back(move(texture)); +} + void vulkan_filter_chain::set_frame_count(uint64_t count) { for (auto &pass : passes) @@ -973,6 +1024,30 @@ void vulkan_filter_chain::build_viewport_pass( pass->end_frame(); } +StaticTexture::StaticTexture(string id, + VkDevice device, + VkImage image, + VkImageView view, + VkDeviceMemory memory, + unique_ptr buffer) + : id(move(id)), + device(device), + image(image), + view(view), + memory(memory), + buffer(move(buffer)) +{} + +StaticTexture::~StaticTexture() +{ + if (view != VK_NULL_HANDLE) + vkDestroyImageView(device, view, nullptr); + if (image != VK_NULL_HANDLE) + vkDestroyImage(device, image, nullptr); + if (memory != VK_NULL_HANDLE) + vkFreeMemory(device, memory, nullptr); +} + Buffer::Buffer(VkDevice device, const VkPhysicalDeviceMemoryProperties &mem_props, size_t size, VkBufferUsageFlags usage) : @@ -2099,6 +2174,158 @@ static VkFormat glslang_format_to_vk(glslang_format fmt) } } +static unique_ptr vulkan_filter_chain_load_lut(VkCommandBuffer cmd, + const struct vulkan_filter_chain_create_info *info, + vulkan_filter_chain *chain, + const video_shader_lut *shader) +{ + texture_image image; + VkMemoryRequirements mem_reqs; + VkImageCreateInfo image_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + VkImage tex = VK_NULL_HANDLE; + VkDeviceMemory memory = VK_NULL_HANDLE; + VkImageView view = VK_NULL_HANDLE; + VkBufferImageCopy region = {}; + void *ptr = nullptr; + unique_ptr buffer; + + if (!image_texture_load(&image, shader->path)) + return {}; + + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.format = VK_FORMAT_B8G8R8A8_UNORM; + image_info.extent.width = image.width; + image_info.extent.height = image.height; + image_info.extent.depth = 1; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; + image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + vkCreateImage(info->device, &image_info, nullptr, &tex); + vkGetImageMemoryRequirements(info->device, tex, &mem_reqs); + alloc.allocationSize = mem_reqs.size; + alloc.memoryTypeIndex = find_memory_type( + *info->memory_properties, + mem_reqs.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + if (vkAllocateMemory(info->device, &alloc, nullptr, &memory) != VK_SUCCESS) + goto error; + + vkBindImageMemory(info->device, tex, memory, 0); + + view_info.image = tex; + view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_info.format = VK_FORMAT_B8G8R8A8_UNORM; + view_info.components.r = VK_COMPONENT_SWIZZLE_R; + view_info.components.g = VK_COMPONENT_SWIZZLE_G; + view_info.components.b = VK_COMPONENT_SWIZZLE_B; + view_info.components.a = VK_COMPONENT_SWIZZLE_A; + view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_info.subresourceRange.levelCount = 1; + view_info.subresourceRange.layerCount = 1; + vkCreateImageView(info->device, &view_info, nullptr, &view); + + buffer = unique_ptr(new Buffer(info->device, *info->memory_properties, + image.width * image.height * sizeof(uint32_t), VK_BUFFER_USAGE_TRANSFER_SRC_BIT)); + ptr = buffer->map(); + memcpy(ptr, image.pixels, image.width * image.height * sizeof(uint32_t)); + buffer->unmap(); + image_texture_free(&image); + image.pixels = nullptr; + + image_layout_transition(cmd, tex, + 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_TRANSFER_BIT); + + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageExtent.width = image.width; + region.imageExtent.height = image.height; + region.imageExtent.depth = 1; + + vkCmdCopyBufferToImage(cmd, buffer->get_buffer(), tex, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ®ion); + + image_layout_transition(cmd, tex, + 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); + + return unique_ptr(new StaticTexture(shader->id, info->device, tex, view, memory, move(buffer))); + +error: + if (image.pixels) + image_texture_free(&image); + if (tex != VK_NULL_HANDLE) + vkDestroyImage(info->device, tex, nullptr); + if (view != VK_NULL_HANDLE) + vkDestroyImageView(info->device, view, nullptr); + if (memory != VK_NULL_HANDLE) + vkFreeMemory(info->device, memory, nullptr); + return {}; +} + +static bool vulkan_filter_chain_load_luts( + const struct vulkan_filter_chain_create_info *info, + vulkan_filter_chain *chain, + video_shader *shader) +{ + VkCommandBufferBeginInfo begin_info = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + VkSubmitInfo submit_info = { + VK_STRUCTURE_TYPE_SUBMIT_INFO }; + VkCommandBuffer cmd = VK_NULL_HANDLE; + VkCommandBufferAllocateInfo cmd_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; + bool recording = false; + + cmd_info.commandPool = info->command_pool; + cmd_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd_info.commandBufferCount = 1; + + vkAllocateCommandBuffers(info->device, &cmd_info, &cmd); + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkBeginCommandBuffer(cmd, &begin_info); + recording = true; + + for (unsigned i = 0; i < shader->luts; i++) + { + auto image = vulkan_filter_chain_load_lut(cmd, info, chain, &shader->lut[i]); + if (!image) + { + RARCH_ERR("[Vulkan]: Failed to load LUT \"%s\".\n", shader->lut[i].path); + goto error; + } + + chain->add_static_texture(move(image)); + } + + vkEndCommandBuffer(cmd); + recording = false; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &cmd; + vkQueueSubmit(info->queue, 1, &submit_info, VK_NULL_HANDLE); + vkQueueWaitIdle(info->queue); + vkFreeCommandBuffers(info->device, info->command_pool, 1, &cmd); + return true; + +error: + if (recording) + vkEndCommandBuffer(cmd); + if (cmd != VK_NULL_HANDLE) + vkFreeCommandBuffers(info->device, info->command_pool, 1, &cmd); + return false; +} + vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( const struct vulkan_filter_chain_create_info *info, const char *path, vulkan_filter_chain_filter filter) @@ -2125,6 +2352,9 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( if (!chain) return nullptr; + if (shader->luts && !vulkan_filter_chain_load_luts(info, chain.get(), shader.get())) + return nullptr; + for (unsigned i = 0; i < shader->passes; i++) { const video_shader_pass *pass = &shader->pass[i]; diff --git a/gfx/drivers_shader/shader_vulkan.h b/gfx/drivers_shader/shader_vulkan.h index 612d122349..274d0487df 100644 --- a/gfx/drivers_shader/shader_vulkan.h +++ b/gfx/drivers_shader/shader_vulkan.h @@ -81,6 +81,8 @@ struct vulkan_filter_chain_create_info VkPhysicalDevice gpu; const VkPhysicalDeviceMemoryProperties *memory_properties; VkPipelineCache pipeline_cache; + VkQueue queue; + VkCommandPool command_pool; unsigned num_passes; VkFormat original_format; diff --git a/gfx/drivers_shader/slang_reflection.hpp b/gfx/drivers_shader/slang_reflection.hpp index 9394b8a3b4..cfb7cb0801 100644 --- a/gfx/drivers_shader/slang_reflection.hpp +++ b/gfx/drivers_shader/slang_reflection.hpp @@ -48,6 +48,11 @@ enum slang_texture_semantic // Canonical name: "PassFeedback#", e.g. "PassFeedback2". SLANG_TEXTURE_SEMANTIC_PASS_FEEDBACK = 4, + // Inputs from static textures, defined by the user. + // There is no canonical name, and the only way to use these semantics are by + // remapping. + SLANG_TEXTURE_SEMANTIC_USER = 5, + SLANG_NUM_TEXTURE_SEMANTICS, SLANG_INVALID_TEXTURE_SEMANTIC = -1 };