Begin implementation of LUTs in Vulkan.

This commit is contained in:
Hans-Kristian Arntzen 2016-07-31 12:26:14 +02:00
parent c899f4c2ef
commit d39a3619d6
4 changed files with 241 additions and 0 deletions

View File

@ -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;

View File

@ -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> 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> buffer;
string id;
};
class Framebuffer
{
public:
@ -234,6 +276,7 @@ struct CommonResources
vector<Texture> original_history;
vector<Texture> framebuffer_feedback;
vector<Texture> pass_outputs;
vector<unique_ptr<StaticTexture>> luts;
unordered_map<string, slang_texture_semantic_map> texture_semantic_map;
unordered_map<string, slang_texture_semantic_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<StaticTexture> 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<StaticTexture> 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> 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<StaticTexture> 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> 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<Buffer>(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, &region);
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<StaticTexture>(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];

View File

@ -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;

View File

@ -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
};