2016-02-16 20:24:00 +01:00
|
|
|
/* RetroArch - A frontend for libretro.
|
|
|
|
* Copyright (C) 2010-2016 - Hans-Kristian Arntzen
|
|
|
|
*
|
|
|
|
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
|
|
|
* of the GNU General Public License as published by the Free Software Found-
|
|
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with RetroArch.
|
|
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "shader_vulkan.h"
|
|
|
|
#include "glslang_util.hpp"
|
|
|
|
#include <vector>
|
|
|
|
#include <memory>
|
|
|
|
#include <functional>
|
|
|
|
#include <utility>
|
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include "../drivers/vulkan_shaders/opaque.vert.inc"
|
|
|
|
#include "../drivers/vulkan_shaders/opaque.frag.inc"
|
|
|
|
|
|
|
|
#include "../../general.h"
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
static uint32_t find_memory_type(
|
|
|
|
const VkPhysicalDeviceMemoryProperties &mem_props,
|
2016-02-16 20:24:00 +01:00
|
|
|
uint32_t device_reqs, uint32_t host_reqs)
|
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < VK_MAX_MEMORY_TYPES; i++)
|
|
|
|
{
|
|
|
|
if ((device_reqs & (1u << i)) &&
|
|
|
|
(mem_props.memoryTypes[i].propertyFlags & host_reqs) == host_reqs)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
RARCH_ERR("[Vulkan]: Failed to find valid memory type. This should never happen.");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
static uint32_t find_memory_type_fallback(
|
|
|
|
const VkPhysicalDeviceMemoryProperties &mem_props,
|
2016-02-16 20:24:00 +01:00
|
|
|
uint32_t device_reqs, uint32_t host_reqs)
|
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < VK_MAX_MEMORY_TYPES; i++)
|
|
|
|
{
|
|
|
|
if ((device_reqs & (1u << i)) &&
|
|
|
|
(mem_props.memoryTypes[i].propertyFlags & host_reqs) == host_reqs)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return find_memory_type(mem_props, device_reqs, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void build_identity_matrix(float *data)
|
|
|
|
{
|
|
|
|
data[ 0] = 1.0f;
|
|
|
|
data[ 1] = 0.0f;
|
|
|
|
data[ 2] = 0.0f;
|
|
|
|
data[ 3] = 0.0f;
|
|
|
|
data[ 4] = 0.0f;
|
|
|
|
data[ 5] = 1.0f;
|
|
|
|
data[ 6] = 0.0f;
|
|
|
|
data[ 7] = 0.0f;
|
|
|
|
data[ 8] = 0.0f;
|
|
|
|
data[ 9] = 0.0f;
|
|
|
|
data[10] = 1.0f;
|
|
|
|
data[11] = 0.0f;
|
|
|
|
data[12] = 0.0f;
|
|
|
|
data[13] = 0.0f;
|
|
|
|
data[14] = 0.0f;
|
|
|
|
data[15] = 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void build_vec4(float *data, unsigned width, unsigned height)
|
|
|
|
{
|
|
|
|
data[0] = float(width);
|
|
|
|
data[1] = float(height);
|
|
|
|
data[2] = 1.0f / float(width);
|
|
|
|
data[3] = 1.0f / float(height);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Size2D
|
|
|
|
{
|
|
|
|
unsigned width, height;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Texture
|
|
|
|
{
|
|
|
|
vulkan_filter_chain_texture texture;
|
|
|
|
vulkan_filter_chain_filter filter;
|
|
|
|
};
|
|
|
|
|
|
|
|
class DeferredDisposer
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
DeferredDisposer(vector<function<void ()>> &calls) : calls(calls) {}
|
|
|
|
|
|
|
|
void defer(function<void ()> func)
|
|
|
|
{
|
|
|
|
calls.push_back(move(func));
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
vector<function<void ()>> &calls;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Buffer
|
|
|
|
{
|
|
|
|
public:
|
2016-02-20 16:36:54 +01:00
|
|
|
Buffer(VkDevice device,
|
|
|
|
const VkPhysicalDeviceMemoryProperties &mem_props,
|
2016-02-16 20:24:00 +01:00
|
|
|
size_t size, VkBufferUsageFlags usage);
|
|
|
|
~Buffer();
|
|
|
|
|
|
|
|
size_t get_size() const { return size; }
|
|
|
|
void *map();
|
|
|
|
void unmap();
|
|
|
|
|
|
|
|
const VkBuffer &get_buffer() const { return buffer; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
VkDevice device;
|
|
|
|
VkBuffer buffer;
|
|
|
|
VkDeviceMemory memory;
|
|
|
|
size_t size;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Framebuffer
|
|
|
|
{
|
|
|
|
public:
|
2016-02-20 16:36:54 +01:00
|
|
|
Framebuffer(VkDevice device,
|
|
|
|
const VkPhysicalDeviceMemoryProperties &mem_props,
|
2016-02-16 20:24:00 +01:00
|
|
|
const Size2D &max_size, VkFormat format);
|
|
|
|
|
|
|
|
~Framebuffer();
|
|
|
|
Framebuffer(Framebuffer&&) = delete;
|
|
|
|
void operator=(Framebuffer&&) = delete;
|
|
|
|
|
|
|
|
void set_size(DeferredDisposer &disposer, const Size2D &size);
|
|
|
|
|
|
|
|
const Size2D &get_size() const { return size; }
|
|
|
|
VkImage get_image() const { return image; }
|
|
|
|
VkImageView get_view() const { return view; }
|
|
|
|
VkFramebuffer get_framebuffer() const { return framebuffer; }
|
|
|
|
VkRenderPass get_render_pass() const { return render_pass; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
VkDevice device = VK_NULL_HANDLE;
|
|
|
|
const VkPhysicalDeviceMemoryProperties &memory_properties;
|
|
|
|
VkImage image = VK_NULL_HANDLE;
|
|
|
|
VkImageView view = VK_NULL_HANDLE;
|
|
|
|
Size2D size;
|
|
|
|
VkFormat format;
|
|
|
|
|
|
|
|
VkFramebuffer framebuffer = VK_NULL_HANDLE;
|
|
|
|
VkRenderPass render_pass = VK_NULL_HANDLE;
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
size_t size = 0;
|
|
|
|
uint32_t type = 0;
|
|
|
|
VkDeviceMemory memory = VK_NULL_HANDLE;
|
|
|
|
} memory;
|
|
|
|
|
|
|
|
void init(DeferredDisposer *disposer);
|
|
|
|
void init_framebuffer();
|
|
|
|
void init_render_pass();
|
|
|
|
};
|
|
|
|
|
|
|
|
class Pass
|
|
|
|
{
|
|
|
|
public:
|
2016-02-20 16:36:54 +01:00
|
|
|
Pass(VkDevice device,
|
|
|
|
const VkPhysicalDeviceMemoryProperties &memory_properties,
|
2016-02-16 20:24:00 +01:00
|
|
|
VkPipelineCache cache, unsigned num_sync_indices, bool final_pass) :
|
|
|
|
device(device),
|
|
|
|
memory_properties(memory_properties),
|
|
|
|
cache(cache),
|
|
|
|
num_sync_indices(num_sync_indices),
|
|
|
|
final_pass(final_pass)
|
|
|
|
{}
|
|
|
|
|
|
|
|
~Pass();
|
|
|
|
|
|
|
|
Pass(Pass&&) = delete;
|
|
|
|
void operator=(Pass&&) = delete;
|
|
|
|
|
|
|
|
const Framebuffer &get_framebuffer() const
|
|
|
|
{
|
|
|
|
return *framebuffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
Size2D set_pass_info(
|
|
|
|
const Size2D &max_original,
|
|
|
|
const Size2D &max_source,
|
|
|
|
const vulkan_filter_chain_swapchain_info &swapchain,
|
|
|
|
const vulkan_filter_chain_pass_info &info);
|
|
|
|
|
|
|
|
void set_shader(VkShaderStageFlags stage,
|
|
|
|
const uint32_t *spirv,
|
|
|
|
size_t spirv_words);
|
|
|
|
|
|
|
|
bool build();
|
|
|
|
|
|
|
|
void build_commands(
|
|
|
|
DeferredDisposer &disposer,
|
|
|
|
VkCommandBuffer cmd,
|
|
|
|
const Texture &original,
|
|
|
|
const Texture &source,
|
|
|
|
const VkViewport &vp,
|
|
|
|
const float *mvp);
|
|
|
|
|
|
|
|
void notify_sync_index(unsigned index)
|
|
|
|
{
|
|
|
|
sync_index = index;
|
|
|
|
}
|
|
|
|
|
|
|
|
vulkan_filter_chain_filter get_source_filter() const
|
|
|
|
{
|
|
|
|
return pass_info.source_filter;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
struct UBO
|
|
|
|
{
|
|
|
|
float MVP[16];
|
|
|
|
float output_size[4];
|
|
|
|
float original_size[4];
|
|
|
|
float source_size[4];
|
|
|
|
};
|
|
|
|
|
|
|
|
VkDevice device;
|
|
|
|
const VkPhysicalDeviceMemoryProperties &memory_properties;
|
|
|
|
VkPipelineCache cache;
|
|
|
|
unsigned num_sync_indices;
|
|
|
|
unsigned sync_index;
|
|
|
|
bool final_pass;
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
Size2D get_output_size(const Size2D &original_size,
|
|
|
|
const Size2D &max_source) const;
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
|
|
|
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
|
|
|
|
VkDescriptorSetLayout set_layout = VK_NULL_HANDLE;
|
|
|
|
VkDescriptorPool pool = VK_NULL_HANDLE;
|
|
|
|
VkSampler samplers[2] = { VK_NULL_HANDLE, VK_NULL_HANDLE };
|
|
|
|
|
|
|
|
vector<unique_ptr<Buffer>> ubos;
|
|
|
|
unique_ptr<Buffer> vbo;
|
|
|
|
vector<VkDescriptorSet> sets;
|
|
|
|
|
|
|
|
Size2D current_framebuffer_size;
|
|
|
|
VkViewport current_viewport;
|
|
|
|
vulkan_filter_chain_pass_info pass_info;
|
|
|
|
|
|
|
|
vector<uint32_t> vertex_shader;
|
|
|
|
vector<uint32_t> fragment_shader;
|
|
|
|
unique_ptr<Framebuffer> framebuffer;
|
|
|
|
VkRenderPass swapchain_render_pass;
|
|
|
|
|
|
|
|
void clear_vk();
|
|
|
|
bool init_pipeline();
|
|
|
|
bool init_pipeline_layout();
|
|
|
|
bool init_buffers();
|
|
|
|
bool init_samplers();
|
|
|
|
|
|
|
|
void image_layout_transition(VkDevice device,
|
|
|
|
VkCommandBuffer cmd, VkImage image,
|
|
|
|
VkImageLayout old_layout, VkImageLayout new_layout,
|
|
|
|
VkAccessFlags srcAccess, VkAccessFlags dstAccess,
|
|
|
|
VkPipelineStageFlags srcStages, VkPipelineStageFlags dstStages);
|
|
|
|
|
|
|
|
void update_descriptor_set(
|
|
|
|
const Texture &original,
|
|
|
|
const Texture &source);
|
|
|
|
|
|
|
|
void set_texture(VkDescriptorSet set, unsigned binding,
|
|
|
|
const Texture &texture);
|
|
|
|
|
|
|
|
void set_uniform_buffer(VkDescriptorSet set, unsigned binding,
|
|
|
|
VkBuffer buffer,
|
|
|
|
VkDeviceSize offset,
|
|
|
|
VkDeviceSize range);
|
|
|
|
};
|
|
|
|
|
|
|
|
// struct here since we're implementing the opaque typedef from C.
|
|
|
|
struct vulkan_filter_chain
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
vulkan_filter_chain(const vulkan_filter_chain_create_info &info);
|
|
|
|
~vulkan_filter_chain();
|
|
|
|
|
|
|
|
inline void set_shader_preset(unique_ptr<video_shader> shader)
|
|
|
|
{
|
|
|
|
shader_preset = move(shader);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline video_shader *get_shader_preset()
|
|
|
|
{
|
|
|
|
return shader_preset.get();
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
void set_pass_info(unsigned pass,
|
|
|
|
const vulkan_filter_chain_pass_info &info);
|
2016-02-16 20:24:00 +01:00
|
|
|
void set_shader(unsigned pass, VkShaderStageFlags stage,
|
|
|
|
const uint32_t *spirv, size_t spirv_words);
|
|
|
|
|
|
|
|
bool init();
|
2016-02-20 16:36:54 +01:00
|
|
|
bool update_swapchain_info(
|
|
|
|
const vulkan_filter_chain_swapchain_info &info);
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
void notify_sync_index(unsigned index);
|
|
|
|
void set_input_texture(const vulkan_filter_chain_texture &texture);
|
|
|
|
void build_offscreen_passes(VkCommandBuffer cmd, const VkViewport &vp);
|
2016-02-20 16:36:54 +01:00
|
|
|
void build_viewport_pass(VkCommandBuffer cmd,
|
|
|
|
const VkViewport &vp, const float *mvp);
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
private:
|
|
|
|
VkDevice device;
|
|
|
|
const VkPhysicalDeviceMemoryProperties &memory_properties;
|
|
|
|
VkPipelineCache cache;
|
|
|
|
vector<unique_ptr<Pass>> passes;
|
|
|
|
vector<vulkan_filter_chain_pass_info> pass_info;
|
|
|
|
vector<vector<function<void ()>>> deferred_calls;
|
|
|
|
|
|
|
|
vulkan_filter_chain_texture input_texture;
|
|
|
|
|
|
|
|
Size2D max_input_size;
|
|
|
|
vulkan_filter_chain_swapchain_info swapchain_info;
|
|
|
|
unsigned current_sync_index;
|
|
|
|
|
|
|
|
unique_ptr<video_shader> shader_preset;
|
|
|
|
|
|
|
|
void flush();
|
|
|
|
|
|
|
|
void set_num_passes(unsigned passes);
|
|
|
|
void execute_deferred();
|
|
|
|
void set_num_sync_indices(unsigned num_indices);
|
|
|
|
void set_swapchain_info(const vulkan_filter_chain_swapchain_info &info);
|
|
|
|
};
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
vulkan_filter_chain::vulkan_filter_chain(
|
|
|
|
const vulkan_filter_chain_create_info &info)
|
2016-02-16 20:24:00 +01:00
|
|
|
: device(info.device),
|
|
|
|
memory_properties(*info.memory_properties),
|
|
|
|
cache(info.pipeline_cache)
|
|
|
|
{
|
|
|
|
max_input_size = { info.max_input_size.width, info.max_input_size.height };
|
|
|
|
set_swapchain_info(info.swapchain);
|
|
|
|
set_num_passes(info.num_passes);
|
|
|
|
}
|
|
|
|
|
|
|
|
vulkan_filter_chain::~vulkan_filter_chain()
|
|
|
|
{
|
|
|
|
flush();
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
void vulkan_filter_chain::set_swapchain_info(
|
|
|
|
const vulkan_filter_chain_swapchain_info &info)
|
2016-02-16 20:24:00 +01:00
|
|
|
{
|
|
|
|
swapchain_info = info;
|
|
|
|
set_num_sync_indices(info.num_indices);
|
|
|
|
}
|
|
|
|
|
|
|
|
void vulkan_filter_chain::set_num_sync_indices(unsigned num_indices)
|
|
|
|
{
|
|
|
|
execute_deferred();
|
|
|
|
deferred_calls.resize(num_indices);
|
|
|
|
}
|
|
|
|
|
|
|
|
void vulkan_filter_chain::notify_sync_index(unsigned index)
|
|
|
|
{
|
|
|
|
auto &calls = deferred_calls[index];
|
|
|
|
for (auto &call : calls)
|
|
|
|
call();
|
|
|
|
calls.clear();
|
|
|
|
|
|
|
|
current_sync_index = index;
|
|
|
|
|
|
|
|
for (auto &pass : passes)
|
|
|
|
pass->notify_sync_index(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
void vulkan_filter_chain::set_num_passes(unsigned num_passes)
|
|
|
|
{
|
|
|
|
pass_info.resize(num_passes);
|
|
|
|
passes.reserve(num_passes);
|
|
|
|
for (unsigned i = 0; i < num_passes; i++)
|
|
|
|
{
|
|
|
|
passes.emplace_back(new Pass(device, memory_properties,
|
|
|
|
cache, deferred_calls.size(), i + 1 == num_passes));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
bool vulkan_filter_chain::update_swapchain_info(
|
|
|
|
const vulkan_filter_chain_swapchain_info &info)
|
2016-02-16 20:24:00 +01:00
|
|
|
{
|
|
|
|
flush();
|
|
|
|
set_swapchain_info(info);
|
|
|
|
return init();
|
|
|
|
}
|
|
|
|
|
|
|
|
void vulkan_filter_chain::set_pass_info(unsigned pass,
|
|
|
|
const vulkan_filter_chain_pass_info &info)
|
|
|
|
{
|
|
|
|
pass_info[pass] = info;
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
void vulkan_filter_chain::set_shader(
|
|
|
|
unsigned pass,
|
|
|
|
VkShaderStageFlags stage,
|
|
|
|
const uint32_t *spirv,
|
|
|
|
size_t spirv_words)
|
2016-02-16 20:24:00 +01:00
|
|
|
{
|
|
|
|
passes[pass]->set_shader(stage, spirv, spirv_words);
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
void vulkan_filter_chain::set_input_texture(
|
|
|
|
const vulkan_filter_chain_texture &texture)
|
2016-02-16 20:24:00 +01:00
|
|
|
{
|
|
|
|
input_texture = texture;
|
|
|
|
}
|
|
|
|
|
|
|
|
void vulkan_filter_chain::execute_deferred()
|
|
|
|
{
|
|
|
|
for (auto &calls : deferred_calls)
|
|
|
|
{
|
|
|
|
for (auto &call : calls)
|
|
|
|
call();
|
|
|
|
calls.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void vulkan_filter_chain::flush()
|
|
|
|
{
|
|
|
|
vkDeviceWaitIdle(device);
|
|
|
|
execute_deferred();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool vulkan_filter_chain::init()
|
|
|
|
{
|
|
|
|
Size2D source = max_input_size;
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < passes.size(); i++)
|
|
|
|
{
|
|
|
|
auto &pass = passes[i];
|
2016-02-20 16:36:54 +01:00
|
|
|
source = pass->set_pass_info(max_input_size,
|
|
|
|
source, swapchain_info, pass_info[i]);
|
2016-02-16 20:24:00 +01:00
|
|
|
if (!pass->build())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void vulkan_filter_chain::build_offscreen_passes(VkCommandBuffer cmd,
|
|
|
|
const VkViewport &vp)
|
|
|
|
{
|
2016-02-20 16:36:54 +01:00
|
|
|
unsigned i;
|
2016-02-16 20:24:00 +01:00
|
|
|
DeferredDisposer disposer(deferred_calls[current_sync_index]);
|
2016-02-20 16:36:54 +01:00
|
|
|
const Texture original = {
|
|
|
|
input_texture, passes.front()->get_source_filter() };
|
|
|
|
Texture source = {
|
|
|
|
input_texture, passes.front()->get_source_filter() };
|
2016-02-16 20:24:00 +01:00
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
for (i = 0; i < passes.size() - 1; i++)
|
2016-02-16 20:24:00 +01:00
|
|
|
{
|
|
|
|
passes[i]->build_commands(disposer, cmd,
|
|
|
|
original, source, vp, nullptr);
|
|
|
|
|
|
|
|
auto &fb = passes[i]->get_framebuffer();
|
2016-02-20 16:36:54 +01:00
|
|
|
source.texture.view = fb.get_view();
|
|
|
|
source.texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
source.texture.width = fb.get_size().width;
|
|
|
|
source.texture.height = fb.get_size().height;
|
|
|
|
source.filter = passes[i + 1]->get_source_filter();
|
2016-02-16 20:24:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
void vulkan_filter_chain::build_viewport_pass(
|
|
|
|
VkCommandBuffer cmd, const VkViewport &vp, const float *mvp)
|
2016-02-16 20:24:00 +01:00
|
|
|
{
|
|
|
|
Texture source;
|
2016-02-20 16:36:54 +01:00
|
|
|
DeferredDisposer disposer(deferred_calls[current_sync_index]);
|
|
|
|
const Texture original = {
|
|
|
|
input_texture, passes.front()->get_source_filter() };
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
if (passes.size() == 1)
|
|
|
|
source = { input_texture, passes.back()->get_source_filter() };
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto &fb = passes[passes.size() - 2]->get_framebuffer();
|
2016-02-20 16:36:54 +01:00
|
|
|
source.texture.view = fb.get_view();
|
|
|
|
source.texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
source.texture.width = fb.get_size().width;
|
|
|
|
source.texture.height = fb.get_size().height;
|
|
|
|
source.filter = passes.back()->get_source_filter();
|
2016-02-16 20:24:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
passes.back()->build_commands(disposer, cmd,
|
|
|
|
original, source, vp, mvp);
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
Buffer::Buffer(VkDevice device,
|
|
|
|
const VkPhysicalDeviceMemoryProperties &mem_props,
|
2016-02-16 20:24:00 +01:00
|
|
|
size_t size, VkBufferUsageFlags usage) :
|
|
|
|
device(device), size(size)
|
|
|
|
{
|
|
|
|
VkBufferCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
2016-02-20 16:36:54 +01:00
|
|
|
info.size = size;
|
|
|
|
info.usage = usage;
|
2016-02-16 20:24:00 +01:00
|
|
|
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
vkCreateBuffer(device, &info, nullptr, &buffer);
|
|
|
|
|
|
|
|
VkMemoryRequirements mem_reqs;
|
|
|
|
vkGetBufferMemoryRequirements(device, buffer, &mem_reqs);
|
|
|
|
|
|
|
|
VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
2016-02-20 16:36:54 +01:00
|
|
|
alloc.allocationSize = mem_reqs.size;
|
2016-02-16 20:24:00 +01:00
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
alloc.memoryTypeIndex = find_memory_type(
|
|
|
|
mem_props, mem_reqs.memoryTypeBits,
|
|
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
|
|
|
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
vkAllocateMemory(device, &alloc, NULL, &memory);
|
|
|
|
vkBindBufferMemory(device, buffer, memory, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *Buffer::map()
|
|
|
|
{
|
|
|
|
void *ptr = nullptr;
|
|
|
|
if (vkMapMemory(device, memory, 0, size, 0, &ptr) == VK_SUCCESS)
|
|
|
|
return ptr;
|
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Buffer::unmap()
|
|
|
|
{
|
|
|
|
vkUnmapMemory(device, memory);
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer::~Buffer()
|
|
|
|
{
|
|
|
|
if (memory != VK_NULL_HANDLE)
|
|
|
|
vkFreeMemory(device, memory, nullptr);
|
|
|
|
if (buffer != VK_NULL_HANDLE)
|
|
|
|
vkDestroyBuffer(device, buffer, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
Pass::~Pass()
|
|
|
|
{
|
|
|
|
clear_vk();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Pass::set_shader(VkShaderStageFlags stage,
|
|
|
|
const uint32_t *spirv,
|
|
|
|
size_t spirv_words)
|
|
|
|
{
|
|
|
|
if (stage == VK_SHADER_STAGE_VERTEX_BIT)
|
|
|
|
{
|
|
|
|
vertex_shader.clear();
|
2016-02-20 16:36:54 +01:00
|
|
|
vertex_shader.insert(end(vertex_shader),
|
|
|
|
spirv, spirv + spirv_words);
|
2016-02-16 20:24:00 +01:00
|
|
|
}
|
|
|
|
else if (stage == VK_SHADER_STAGE_FRAGMENT_BIT)
|
|
|
|
{
|
|
|
|
fragment_shader.clear();
|
2016-02-20 16:36:54 +01:00
|
|
|
fragment_shader.insert(end(fragment_shader),
|
|
|
|
spirv, spirv + spirv_words);
|
2016-02-16 20:24:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
Size2D Pass::get_output_size(const Size2D &original,
|
|
|
|
const Size2D &source) const
|
2016-02-16 20:24:00 +01:00
|
|
|
{
|
|
|
|
float width, height;
|
|
|
|
switch (pass_info.scale_type_x)
|
|
|
|
{
|
|
|
|
case VULKAN_FILTER_CHAIN_SCALE_ORIGINAL:
|
|
|
|
width = float(original.width) * pass_info.scale_x;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VULKAN_FILTER_CHAIN_SCALE_SOURCE:
|
|
|
|
width = float(source.width) * pass_info.scale_x;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VULKAN_FILTER_CHAIN_SCALE_VIEWPORT:
|
|
|
|
width = current_viewport.width * pass_info.scale_x;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VULKAN_FILTER_CHAIN_SCALE_ABSOLUTE:
|
|
|
|
width = pass_info.scale_x;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
width = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (pass_info.scale_type_y)
|
|
|
|
{
|
|
|
|
case VULKAN_FILTER_CHAIN_SCALE_ORIGINAL:
|
|
|
|
height = float(original.height) * pass_info.scale_y;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VULKAN_FILTER_CHAIN_SCALE_SOURCE:
|
|
|
|
height = float(source.height) * pass_info.scale_y;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VULKAN_FILTER_CHAIN_SCALE_VIEWPORT:
|
|
|
|
height = current_viewport.height * pass_info.scale_y;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VULKAN_FILTER_CHAIN_SCALE_ABSOLUTE:
|
|
|
|
height = pass_info.scale_y;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
height = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
return { unsigned(roundf(width)), unsigned(roundf(height)) };
|
|
|
|
}
|
|
|
|
|
|
|
|
Size2D Pass::set_pass_info(
|
|
|
|
const Size2D &max_original,
|
|
|
|
const Size2D &max_source,
|
|
|
|
const vulkan_filter_chain_swapchain_info &swapchain,
|
|
|
|
const vulkan_filter_chain_pass_info &info)
|
|
|
|
{
|
|
|
|
clear_vk();
|
|
|
|
|
|
|
|
current_viewport = swapchain.viewport;
|
|
|
|
pass_info = info;
|
|
|
|
|
|
|
|
num_sync_indices = swapchain.num_indices;
|
|
|
|
sync_index = 0;
|
|
|
|
|
|
|
|
current_framebuffer_size = get_output_size(max_original, max_source);
|
|
|
|
swapchain_render_pass = swapchain.render_pass;
|
|
|
|
return current_framebuffer_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Pass::clear_vk()
|
|
|
|
{
|
|
|
|
if (pool != VK_NULL_HANDLE)
|
|
|
|
vkDestroyDescriptorPool(device, pool, nullptr);
|
|
|
|
if (pipeline != VK_NULL_HANDLE)
|
|
|
|
vkDestroyPipeline(device, pipeline, nullptr);
|
|
|
|
if (set_layout != VK_NULL_HANDLE)
|
|
|
|
vkDestroyDescriptorSetLayout(device, set_layout, nullptr);
|
|
|
|
if (pipeline_layout != VK_NULL_HANDLE)
|
|
|
|
vkDestroyPipelineLayout(device, pipeline_layout, nullptr);
|
|
|
|
|
|
|
|
for (auto &samp : samplers)
|
|
|
|
{
|
|
|
|
if (samp != VK_NULL_HANDLE)
|
|
|
|
vkDestroySampler(device, samp, nullptr);
|
|
|
|
samp = VK_NULL_HANDLE;
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
pool = VK_NULL_HANDLE;
|
|
|
|
pipeline = VK_NULL_HANDLE;
|
2016-02-16 20:24:00 +01:00
|
|
|
set_layout = VK_NULL_HANDLE;
|
|
|
|
ubos.clear();
|
|
|
|
vbo.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Pass::init_pipeline_layout()
|
|
|
|
{
|
|
|
|
vector<VkDescriptorSetLayoutBinding> bindings;
|
|
|
|
vector<VkDescriptorPoolSize> desc_counts;
|
|
|
|
|
|
|
|
// TODO: Expand this a lot, need to reflect shaders to figure this out.
|
|
|
|
bindings.push_back({ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1,
|
|
|
|
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
|
|
nullptr });
|
|
|
|
|
|
|
|
bindings.push_back({ 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1,
|
|
|
|
VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
|
|
nullptr });
|
|
|
|
|
|
|
|
bindings.push_back({ 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1,
|
|
|
|
VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
|
|
nullptr });
|
|
|
|
|
|
|
|
desc_counts.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 });
|
|
|
|
desc_counts.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 });
|
|
|
|
desc_counts.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 });
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
VkDescriptorSetLayoutCreateInfo set_layout_info = {
|
|
|
|
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
2016-02-16 20:24:00 +01:00
|
|
|
set_layout_info.bindingCount = bindings.size();
|
|
|
|
set_layout_info.pBindings = bindings.data();
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
if (vkCreateDescriptorSetLayout(device,
|
|
|
|
&set_layout_info, NULL, &set_layout) != VK_SUCCESS)
|
2016-02-16 20:24:00 +01:00
|
|
|
return false;
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
VkPipelineLayoutCreateInfo layout_info = {
|
|
|
|
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
2016-02-16 20:24:00 +01:00
|
|
|
layout_info.setLayoutCount = 1;
|
|
|
|
layout_info.pSetLayouts = &set_layout;
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
if (vkCreatePipelineLayout(device,
|
|
|
|
&layout_info, NULL, &pipeline_layout) != VK_SUCCESS)
|
2016-02-16 20:24:00 +01:00
|
|
|
return false;
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
VkDescriptorPoolCreateInfo pool_info = {
|
|
|
|
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
|
2016-02-16 20:24:00 +01:00
|
|
|
pool_info.maxSets = num_sync_indices;
|
|
|
|
pool_info.poolSizeCount = desc_counts.size();
|
|
|
|
pool_info.pPoolSizes = desc_counts.data();
|
|
|
|
if (vkCreateDescriptorPool(device, &pool_info, nullptr, &pool) != VK_SUCCESS)
|
|
|
|
return false;
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
VkDescriptorSetAllocateInfo alloc_info = {
|
|
|
|
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
|
2016-02-16 20:24:00 +01:00
|
|
|
alloc_info.descriptorPool = pool;
|
|
|
|
alloc_info.descriptorSetCount = 1;
|
|
|
|
alloc_info.pSetLayouts = &set_layout;
|
|
|
|
|
|
|
|
sets.resize(num_sync_indices);
|
|
|
|
for (unsigned i = 0; i < num_sync_indices; i++)
|
|
|
|
vkAllocateDescriptorSets(device, &alloc_info, &sets[i]);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Pass::init_pipeline()
|
|
|
|
{
|
|
|
|
if (!init_pipeline_layout())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Input assembly
|
2016-02-20 16:36:54 +01:00
|
|
|
VkPipelineInputAssemblyStateCreateInfo input_assembly = {
|
|
|
|
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
|
2016-02-16 20:24:00 +01:00
|
|
|
input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
|
|
|
|
|
|
|
// VAO state
|
|
|
|
VkVertexInputAttributeDescription attributes[2] = {{0}};
|
|
|
|
VkVertexInputBindingDescription binding = {0};
|
|
|
|
|
|
|
|
attributes[0].location = 0;
|
2016-02-20 16:36:54 +01:00
|
|
|
attributes[0].binding = 0;
|
|
|
|
attributes[0].format = VK_FORMAT_R32G32_SFLOAT;
|
|
|
|
attributes[0].offset = 0;
|
2016-02-16 20:24:00 +01:00
|
|
|
attributes[1].location = 1;
|
2016-02-20 16:36:54 +01:00
|
|
|
attributes[1].binding = 0;
|
|
|
|
attributes[1].format = VK_FORMAT_R32G32_SFLOAT;
|
|
|
|
attributes[1].offset = 2 * sizeof(float);
|
|
|
|
|
|
|
|
binding.binding = 0;
|
|
|
|
binding.stride = 4 * sizeof(float);
|
|
|
|
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
|
|
|
|
|
|
|
VkPipelineVertexInputStateCreateInfo vertex_input = {
|
|
|
|
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
|
|
|
|
vertex_input.vertexBindingDescriptionCount = 1;
|
|
|
|
vertex_input.pVertexBindingDescriptions = &binding;
|
2016-02-16 20:24:00 +01:00
|
|
|
vertex_input.vertexAttributeDescriptionCount = 2;
|
2016-02-20 16:36:54 +01:00
|
|
|
vertex_input.pVertexAttributeDescriptions = attributes;
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
// Raster state
|
2016-02-20 16:36:54 +01:00
|
|
|
VkPipelineRasterizationStateCreateInfo raster = {
|
|
|
|
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
2016-02-16 20:24:00 +01:00
|
|
|
raster.polygonMode = VK_POLYGON_MODE_FILL;
|
|
|
|
raster.cullMode = VK_CULL_MODE_NONE;
|
|
|
|
raster.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
|
|
|
raster.depthClampEnable = false;
|
|
|
|
raster.rasterizerDiscardEnable = false;
|
|
|
|
raster.depthBiasEnable = false;
|
|
|
|
raster.lineWidth = 1.0f;
|
|
|
|
|
|
|
|
// Blend state
|
|
|
|
VkPipelineColorBlendAttachmentState blend_attachment = {0};
|
2016-02-20 16:36:54 +01:00
|
|
|
VkPipelineColorBlendStateCreateInfo blend = {
|
|
|
|
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
|
|
|
|
blend_attachment.blendEnable = false;
|
2016-02-16 20:24:00 +01:00
|
|
|
blend_attachment.colorWriteMask = 0xf;
|
2016-02-20 16:36:54 +01:00
|
|
|
blend.attachmentCount = 1;
|
|
|
|
blend.pAttachments = &blend_attachment;
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
// Viewport state
|
2016-02-20 16:36:54 +01:00
|
|
|
VkPipelineViewportStateCreateInfo viewport = {
|
|
|
|
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
|
2016-02-16 20:24:00 +01:00
|
|
|
viewport.viewportCount = 1;
|
2016-02-20 16:36:54 +01:00
|
|
|
viewport.scissorCount = 1;
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
// Depth-stencil state
|
2016-02-20 16:36:54 +01:00
|
|
|
VkPipelineDepthStencilStateCreateInfo depth_stencil = {
|
|
|
|
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
|
|
|
|
depth_stencil.depthTestEnable = false;
|
|
|
|
depth_stencil.depthWriteEnable = false;
|
2016-02-16 20:24:00 +01:00
|
|
|
depth_stencil.depthBoundsTestEnable = false;
|
2016-02-20 16:36:54 +01:00
|
|
|
depth_stencil.stencilTestEnable = false;
|
|
|
|
depth_stencil.minDepthBounds = 0.0f;
|
|
|
|
depth_stencil.maxDepthBounds = 1.0f;
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
// Multisample state
|
2016-02-20 16:36:54 +01:00
|
|
|
VkPipelineMultisampleStateCreateInfo multisample = {
|
|
|
|
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
|
2016-02-16 20:24:00 +01:00
|
|
|
multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
|
|
|
|
// Dynamic state
|
2016-02-20 16:36:54 +01:00
|
|
|
VkPipelineDynamicStateCreateInfo dynamic = {
|
|
|
|
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
|
|
|
|
static const VkDynamicState dynamics[] = {
|
|
|
|
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
|
|
|
|
dynamic.pDynamicStates = dynamics;
|
2016-02-16 20:24:00 +01:00
|
|
|
dynamic.dynamicStateCount = sizeof(dynamics) / sizeof(dynamics[0]);
|
|
|
|
|
|
|
|
// Shaders
|
|
|
|
VkPipelineShaderStageCreateInfo shader_stages[2] = {
|
|
|
|
{ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO },
|
|
|
|
{ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO },
|
|
|
|
};
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
VkShaderModuleCreateInfo module_info = {
|
|
|
|
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
|
|
|
module_info.codeSize = vertex_shader.size() * sizeof(uint32_t);
|
|
|
|
module_info.pCode = vertex_shader.data();
|
|
|
|
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
|
|
|
shader_stages[0].pName = "main";
|
2016-02-16 20:24:00 +01:00
|
|
|
vkCreateShaderModule(device, &module_info, NULL, &shader_stages[0].module);
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
module_info.codeSize = fragment_shader.size() * sizeof(uint32_t);
|
|
|
|
module_info.pCode = fragment_shader.data();
|
|
|
|
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
|
|
shader_stages[1].pName = "main";
|
2016-02-16 20:24:00 +01:00
|
|
|
vkCreateShaderModule(device, &module_info, NULL, &shader_stages[1].module);
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
VkGraphicsPipelineCreateInfo pipe = {
|
|
|
|
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
|
|
|
|
pipe.stageCount = 2;
|
|
|
|
pipe.pStages = shader_stages;
|
|
|
|
pipe.pVertexInputState = &vertex_input;
|
2016-02-16 20:24:00 +01:00
|
|
|
pipe.pInputAssemblyState = &input_assembly;
|
|
|
|
pipe.pRasterizationState = &raster;
|
2016-02-20 16:36:54 +01:00
|
|
|
pipe.pColorBlendState = &blend;
|
|
|
|
pipe.pMultisampleState = &multisample;
|
|
|
|
pipe.pViewportState = &viewport;
|
|
|
|
pipe.pDepthStencilState = &depth_stencil;
|
|
|
|
pipe.pDynamicState = &dynamic;
|
|
|
|
pipe.renderPass = final_pass ? swapchain_render_pass :
|
|
|
|
framebuffer->get_render_pass();
|
|
|
|
pipe.layout = pipeline_layout;
|
|
|
|
|
|
|
|
if (vkCreateGraphicsPipelines(device,
|
|
|
|
cache, 1, &pipe, NULL, &pipeline) != VK_SUCCESS)
|
2016-02-16 20:24:00 +01:00
|
|
|
{
|
|
|
|
vkDestroyShaderModule(device, shader_stages[0].module, NULL);
|
|
|
|
vkDestroyShaderModule(device, shader_stages[1].module, NULL);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
vkDestroyShaderModule(device, shader_stages[0].module, NULL);
|
|
|
|
vkDestroyShaderModule(device, shader_stages[1].module, NULL);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Pass::init_samplers()
|
|
|
|
{
|
|
|
|
VkSamplerCreateInfo info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
|
2016-02-20 16:36:54 +01:00
|
|
|
info.magFilter = VK_FILTER_NEAREST;
|
|
|
|
info.minFilter = VK_FILTER_NEAREST;
|
|
|
|
info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
|
|
info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
|
|
info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
|
|
info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
|
|
info.mipLodBias = 0.0f;
|
|
|
|
info.maxAnisotropy = 1.0f;
|
|
|
|
info.compareEnable = false;
|
|
|
|
info.minLod = 0.0f;
|
|
|
|
info.maxLod = 0.0f;
|
2016-02-16 20:24:00 +01:00
|
|
|
info.unnormalizedCoordinates = false;
|
2016-02-20 16:36:54 +01:00
|
|
|
info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
|
2016-02-16 20:24:00 +01:00
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
if (vkCreateSampler(device,
|
|
|
|
&info, NULL, &samplers[VULKAN_FILTER_CHAIN_NEAREST]) != VK_SUCCESS)
|
2016-02-16 20:24:00 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
info.magFilter = VK_FILTER_LINEAR;
|
|
|
|
info.minFilter = VK_FILTER_LINEAR;
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
if (vkCreateSampler(device,
|
|
|
|
&info, NULL, &samplers[VULKAN_FILTER_CHAIN_LINEAR]) != VK_SUCCESS)
|
2016-02-16 20:24:00 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Pass::init_buffers()
|
|
|
|
{
|
2016-02-20 16:36:54 +01:00
|
|
|
unsigned i;
|
2016-02-16 20:24:00 +01:00
|
|
|
// The final pass uses an MVP designed for [0, 1] range VBO.
|
|
|
|
// For in-between passes, we just go with identity matrices, so keep it simple.
|
|
|
|
float pos_min = final_pass ? 0.0f : -1.0f;
|
|
|
|
|
|
|
|
const float vbo_data[] = {
|
|
|
|
pos_min, pos_min, 0.0f, 0.0f,
|
|
|
|
pos_min, +1.0f, 0.0f, 1.0f,
|
|
|
|
1.0f, pos_min, 1.0f, 0.0f,
|
|
|
|
1.0f, +1.0f, 1.0f, 1.0f,
|
|
|
|
};
|
|
|
|
|
|
|
|
ubos.clear();
|
2016-02-20 16:36:54 +01:00
|
|
|
for (i = 0; i < num_sync_indices; i++)
|
|
|
|
ubos.emplace_back(new Buffer(device,
|
|
|
|
memory_properties, sizeof(UBO), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT));
|
|
|
|
vbo = unique_ptr<Buffer>(new Buffer(device,
|
|
|
|
memory_properties, sizeof(vbo_data), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT));
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
void *ptr = vbo->map();
|
|
|
|
memcpy(ptr, vbo_data, sizeof(vbo_data));
|
|
|
|
vbo->unmap();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Pass::build()
|
|
|
|
{
|
|
|
|
if (!final_pass)
|
|
|
|
{
|
|
|
|
framebuffer = unique_ptr<Framebuffer>(
|
2016-02-20 16:36:54 +01:00
|
|
|
new Framebuffer(device, memory_properties,
|
|
|
|
current_framebuffer_size,
|
2016-02-16 20:24:00 +01:00
|
|
|
pass_info.rt_format));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!init_pipeline())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!init_buffers())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!init_samplers())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Pass::image_layout_transition(VkDevice device,
|
|
|
|
VkCommandBuffer cmd, VkImage image,
|
|
|
|
VkImageLayout old_layout, VkImageLayout new_layout,
|
|
|
|
VkAccessFlags srcAccess, VkAccessFlags dstAccess,
|
|
|
|
VkPipelineStageFlags srcStages, VkPipelineStageFlags dstStages)
|
|
|
|
{
|
|
|
|
VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
barrier.srcAccessMask = srcAccess;
|
|
|
|
barrier.dstAccessMask = dstAccess;
|
|
|
|
barrier.oldLayout = old_layout;
|
|
|
|
barrier.newLayout = new_layout;
|
|
|
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
|
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
|
|
barrier.image = image;
|
2016-02-16 20:24:00 +01:00
|
|
|
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
barrier.subresourceRange.levelCount = 1;
|
|
|
|
barrier.subresourceRange.layerCount = 1;
|
|
|
|
|
|
|
|
vkCmdPipelineBarrier(cmd,
|
|
|
|
srcStages,
|
|
|
|
dstStages,
|
|
|
|
0,
|
|
|
|
0, nullptr,
|
|
|
|
0, nullptr,
|
|
|
|
1, &barrier);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Pass::set_uniform_buffer(VkDescriptorSet set, unsigned binding,
|
|
|
|
VkBuffer buffer,
|
|
|
|
VkDeviceSize offset,
|
|
|
|
VkDeviceSize range)
|
|
|
|
{
|
|
|
|
VkDescriptorBufferInfo buffer_info;
|
|
|
|
buffer_info.buffer = buffer;
|
|
|
|
buffer_info.offset = offset;
|
|
|
|
buffer_info.range = range;
|
|
|
|
|
|
|
|
VkWriteDescriptorSet write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
|
2016-02-20 16:36:54 +01:00
|
|
|
write.dstSet = set;
|
|
|
|
write.dstBinding = binding;
|
|
|
|
write.descriptorCount = 1;
|
|
|
|
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
2016-02-16 20:24:00 +01:00
|
|
|
write.pBufferInfo = &buffer_info;
|
|
|
|
|
|
|
|
vkUpdateDescriptorSets(device, 1, &write, 0, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Pass::set_texture(VkDescriptorSet set, unsigned binding,
|
|
|
|
const Texture &texture)
|
|
|
|
{
|
|
|
|
VkDescriptorImageInfo image_info;
|
|
|
|
image_info.sampler = samplers[texture.filter];
|
|
|
|
image_info.imageView = texture.texture.view;
|
|
|
|
image_info.imageLayout = texture.texture.layout;
|
|
|
|
|
|
|
|
VkWriteDescriptorSet write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
|
|
|
|
write.dstSet = set;
|
|
|
|
write.dstBinding = binding;
|
|
|
|
write.descriptorCount = 1;
|
|
|
|
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
|
|
write.pImageInfo = &image_info;
|
|
|
|
|
|
|
|
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Pass::update_descriptor_set(
|
|
|
|
const Texture &original,
|
|
|
|
const Texture &source)
|
|
|
|
{
|
2016-02-20 16:36:54 +01:00
|
|
|
set_uniform_buffer(sets[sync_index], 0,
|
|
|
|
ubos[sync_index]->get_buffer(), 0, sizeof(UBO));
|
2016-02-16 20:24:00 +01:00
|
|
|
set_texture(sets[sync_index], 1, original);
|
|
|
|
set_texture(sets[sync_index], 2, source);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Pass::build_commands(
|
|
|
|
DeferredDisposer &disposer,
|
|
|
|
VkCommandBuffer cmd,
|
|
|
|
const Texture &original,
|
|
|
|
const Texture &source,
|
|
|
|
const VkViewport &vp,
|
|
|
|
const float *mvp)
|
|
|
|
{
|
|
|
|
current_viewport = vp;
|
|
|
|
auto size = get_output_size(
|
|
|
|
{ original.texture.width, original.texture.height },
|
|
|
|
{ source.texture.width, source.texture.height });
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
if ( size.width != current_framebuffer_size.width
|
|
|
|
|| size.height != current_framebuffer_size.height)
|
2016-02-16 20:24:00 +01:00
|
|
|
{
|
|
|
|
if (framebuffer)
|
|
|
|
framebuffer->set_size(disposer, size);
|
|
|
|
current_framebuffer_size = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
UBO *u = static_cast<UBO*>(ubos[sync_index]->map());
|
|
|
|
|
|
|
|
if (mvp)
|
|
|
|
memcpy(u->MVP, mvp, sizeof(float) * 16);
|
|
|
|
else
|
|
|
|
build_identity_matrix(u->MVP);
|
2016-02-20 16:36:54 +01:00
|
|
|
build_vec4(u->output_size,
|
|
|
|
current_framebuffer_size.width,
|
|
|
|
current_framebuffer_size.height);
|
|
|
|
build_vec4(u->original_size,
|
|
|
|
original.texture.width, original.texture.height);
|
|
|
|
build_vec4(u->source_size,
|
|
|
|
source.texture.width, source.texture.height);
|
2016-02-16 20:24:00 +01:00
|
|
|
ubos[sync_index]->unmap();
|
|
|
|
|
|
|
|
update_descriptor_set(original, source);
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
// The final pass is always executed inside
|
|
|
|
// another render pass since the frontend will
|
|
|
|
// want to overlay various things on top for
|
|
|
|
// the passes that end up on-screen.
|
2016-02-16 20:24:00 +01:00
|
|
|
if (!final_pass)
|
|
|
|
{
|
|
|
|
// Render.
|
2016-02-20 16:36:54 +01:00
|
|
|
image_layout_transition(device, cmd,
|
|
|
|
framebuffer->get_image(),
|
|
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
|
|
VK_ACCESS_SHADER_READ_BIT,
|
|
|
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
|
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
|
|
|
|
|
|
|
|
VkRenderPassBeginInfo rp_info = {
|
|
|
|
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
|
2016-02-16 20:24:00 +01:00
|
|
|
VkClearValue clear_value;
|
2016-02-20 16:36:54 +01:00
|
|
|
clear_value.color.float32[0] = 0.0f;
|
|
|
|
clear_value.color.float32[1] = 0.0f;
|
|
|
|
clear_value.color.float32[2] = 0.0f;
|
|
|
|
clear_value.color.float32[3] = 1.0f;
|
|
|
|
rp_info.renderPass = framebuffer->get_render_pass();
|
|
|
|
rp_info.framebuffer = framebuffer->get_framebuffer();
|
|
|
|
rp_info.renderArea.extent.width = current_framebuffer_size.width;
|
2016-02-16 20:24:00 +01:00
|
|
|
rp_info.renderArea.extent.height = current_framebuffer_size.height;
|
2016-02-20 16:36:54 +01:00
|
|
|
rp_info.clearValueCount = 1;
|
|
|
|
rp_info.pClearValues = &clear_value;
|
2016-02-16 20:24:00 +01:00
|
|
|
vkCmdBeginRenderPass(cmd, &rp_info, VK_SUBPASS_CONTENTS_INLINE);
|
|
|
|
}
|
|
|
|
|
|
|
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
|
|
|
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
|
|
|
|
0, 1, &sets[sync_index], 0, nullptr);
|
|
|
|
|
|
|
|
VkDeviceSize offset = 0;
|
|
|
|
vkCmdBindVertexBuffers(cmd, 0, 1, &vbo->get_buffer(), &offset);
|
|
|
|
|
|
|
|
if (final_pass)
|
|
|
|
{
|
|
|
|
const VkRect2D sci = {
|
2016-02-20 16:36:54 +01:00
|
|
|
{
|
|
|
|
int32_t(current_viewport.x),
|
|
|
|
int32_t(current_viewport.y)
|
|
|
|
},
|
|
|
|
{
|
|
|
|
uint32_t(current_viewport.width),
|
|
|
|
uint32_t(current_viewport.height)
|
|
|
|
},
|
2016-02-16 20:24:00 +01:00
|
|
|
};
|
|
|
|
vkCmdSetViewport(cmd, 0, 1, ¤t_viewport);
|
|
|
|
vkCmdSetScissor(cmd, 0, 1, &sci);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const VkViewport vp = {
|
|
|
|
0.0f, 0.0f,
|
2016-02-20 16:36:54 +01:00
|
|
|
float(current_framebuffer_size.width),
|
|
|
|
float(current_framebuffer_size.height),
|
2016-02-16 20:24:00 +01:00
|
|
|
0.0f, 1.0f
|
|
|
|
};
|
|
|
|
const VkRect2D sci = {
|
|
|
|
{ 0, 0 },
|
2016-02-20 16:36:54 +01:00
|
|
|
{
|
|
|
|
current_framebuffer_size.width,
|
|
|
|
current_framebuffer_size.height
|
|
|
|
},
|
2016-02-16 20:24:00 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
vkCmdSetViewport(cmd, 0, 1, &vp);
|
|
|
|
vkCmdSetScissor(cmd, 0, 1, &sci);
|
|
|
|
}
|
|
|
|
|
|
|
|
vkCmdDraw(cmd, 4, 1, 0, 0);
|
|
|
|
|
|
|
|
if (!final_pass)
|
|
|
|
{
|
|
|
|
vkCmdEndRenderPass(cmd);
|
|
|
|
|
|
|
|
// Barrier to sync with next pass.
|
2016-02-20 16:36:54 +01:00
|
|
|
image_layout_transition(
|
|
|
|
device,
|
|
|
|
cmd,
|
|
|
|
framebuffer->get_image(),
|
|
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
|
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
|
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
|
|
VK_ACCESS_SHADER_READ_BIT,
|
|
|
|
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
|
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
2016-02-16 20:24:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
Framebuffer::Framebuffer(
|
|
|
|
VkDevice device,
|
|
|
|
const VkPhysicalDeviceMemoryProperties &mem_props,
|
2016-02-16 20:24:00 +01:00
|
|
|
const Size2D &max_size, VkFormat format) :
|
|
|
|
device(device),
|
|
|
|
memory_properties(mem_props),
|
|
|
|
size(max_size),
|
|
|
|
format(format)
|
|
|
|
{
|
2016-02-20 16:36:54 +01:00
|
|
|
RARCH_LOG("[Vulkan filter chain]: Creating framebuffer %u x %u.\n",
|
|
|
|
max_size.width, max_size.height);
|
2016-02-16 20:24:00 +01:00
|
|
|
init_render_pass();
|
|
|
|
init(nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Framebuffer::init(DeferredDisposer *disposer)
|
|
|
|
{
|
2016-02-20 16:36:54 +01:00
|
|
|
VkMemoryRequirements mem_reqs;
|
2016-02-16 20:24:00 +01:00
|
|
|
VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
2016-02-20 16:36:54 +01:00
|
|
|
info.imageType = VK_IMAGE_TYPE_2D;
|
|
|
|
info.format = format;
|
|
|
|
info.extent.width = size.width;
|
|
|
|
info.extent.height = size.height;
|
|
|
|
info.extent.depth = 1;
|
|
|
|
info.mipLevels = 1;
|
|
|
|
info.arrayLayers = 1;
|
|
|
|
info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
|
|
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT |
|
|
|
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
|
|
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
2016-02-16 20:24:00 +01:00
|
|
|
vkCreateImage(device, &info, nullptr, &image);
|
|
|
|
|
|
|
|
vkGetImageMemoryRequirements(device, image, &mem_reqs);
|
|
|
|
|
|
|
|
VkMemoryAllocateInfo alloc = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
2016-02-20 16:36:54 +01:00
|
|
|
alloc.allocationSize = mem_reqs.size;
|
|
|
|
alloc.memoryTypeIndex = find_memory_type_fallback(
|
|
|
|
memory_properties, mem_reqs.memoryTypeBits,
|
2016-02-16 20:24:00 +01:00
|
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
|
|
|
|
|
|
|
// Can reuse already allocated memory.
|
|
|
|
if (memory.size < mem_reqs.size || memory.type != alloc.memoryTypeIndex)
|
|
|
|
{
|
|
|
|
// Memory might still be in use since we don't want to totally stall
|
|
|
|
// the world for framebuffer recreation.
|
|
|
|
if (memory.memory != VK_NULL_HANDLE && disposer)
|
|
|
|
{
|
|
|
|
auto d = device;
|
|
|
|
auto m = memory.memory;
|
|
|
|
disposer->defer([=] { vkFreeMemory(d, m, nullptr); });
|
|
|
|
}
|
|
|
|
|
|
|
|
memory.type = alloc.memoryTypeIndex;
|
|
|
|
memory.size = mem_reqs.size;
|
|
|
|
vkAllocateMemory(device, &alloc, nullptr, &memory.memory);
|
|
|
|
}
|
|
|
|
|
|
|
|
vkBindImageMemory(device, image, memory.memory, 0);
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
VkImageViewCreateInfo view_info = {
|
|
|
|
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
|
|
|
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
view_info.format = format;
|
|
|
|
view_info.image = image;
|
|
|
|
view_info.subresourceRange.baseMipLevel = 0;
|
2016-02-16 20:24:00 +01:00
|
|
|
view_info.subresourceRange.baseArrayLayer = 0;
|
2016-02-20 16:36:54 +01:00
|
|
|
view_info.subresourceRange.levelCount = 1;
|
|
|
|
view_info.subresourceRange.layerCount = 1;
|
|
|
|
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
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;
|
2016-02-16 20:24:00 +01:00
|
|
|
vkCreateImageView(device, &view_info, nullptr, &view);
|
|
|
|
|
|
|
|
init_framebuffer();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Framebuffer::init_render_pass()
|
|
|
|
{
|
2016-02-20 16:36:54 +01:00
|
|
|
VkRenderPassCreateInfo rp_info = {
|
|
|
|
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
|
|
|
|
VkAttachmentReference color_ref = { 0,
|
|
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
// We will always write to the entire framebuffer,
|
|
|
|
// so we don't really need to clear.
|
|
|
|
VkAttachmentDescription attachment = {0};
|
2016-02-20 16:36:54 +01:00
|
|
|
attachment.format = format;
|
|
|
|
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
|
|
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
|
|
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
|
|
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
2016-02-16 20:24:00 +01:00
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
VkSubpassDescription subpass = {0};
|
2016-02-20 16:36:54 +01:00
|
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
2016-02-16 20:24:00 +01:00
|
|
|
subpass.colorAttachmentCount = 1;
|
2016-02-20 16:36:54 +01:00
|
|
|
subpass.pColorAttachments = &color_ref;
|
2016-02-16 20:24:00 +01:00
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
rp_info.attachmentCount = 1;
|
|
|
|
rp_info.pAttachments = &attachment;
|
|
|
|
rp_info.subpassCount = 1;
|
2016-02-16 20:24:00 +01:00
|
|
|
rp_info.pSubpasses = &subpass;
|
|
|
|
|
|
|
|
vkCreateRenderPass(device, &rp_info, nullptr, &render_pass);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Framebuffer::init_framebuffer()
|
|
|
|
{
|
2016-02-20 16:36:54 +01:00
|
|
|
VkFramebufferCreateInfo info = {
|
|
|
|
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
|
|
|
info.renderPass = render_pass;
|
2016-02-16 20:24:00 +01:00
|
|
|
info.attachmentCount = 1;
|
2016-02-20 16:36:54 +01:00
|
|
|
info.pAttachments = &view;
|
|
|
|
info.width = size.width;
|
|
|
|
info.height = size.height;
|
|
|
|
info.layers = 1;
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
vkCreateFramebuffer(device, &info, nullptr, &framebuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Framebuffer::set_size(DeferredDisposer &disposer, const Size2D &size)
|
|
|
|
{
|
|
|
|
this->size = size;
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
RARCH_LOG("[Vulkan filter chain]: Updating framebuffer size %u x %u.\n",
|
|
|
|
size.width, size.height);
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
// The current framebuffers, etc, might still be in use
|
|
|
|
// so defer deletion.
|
|
|
|
// We'll most likely be able to reuse the memory,
|
|
|
|
// so don't free it here.
|
|
|
|
//
|
|
|
|
// Fake lambda init captures for C++11.
|
|
|
|
//
|
2016-02-20 16:36:54 +01:00
|
|
|
auto d = device;
|
|
|
|
auto i = image;
|
|
|
|
auto v = view;
|
2016-02-16 20:24:00 +01:00
|
|
|
auto fb = framebuffer;
|
|
|
|
disposer.defer([=]
|
|
|
|
{
|
|
|
|
if (fb != VK_NULL_HANDLE)
|
|
|
|
vkDestroyFramebuffer(d, fb, nullptr);
|
|
|
|
if (v != VK_NULL_HANDLE)
|
|
|
|
vkDestroyImageView(d, v, nullptr);
|
|
|
|
if (i != VK_NULL_HANDLE)
|
|
|
|
vkDestroyImage(d, i, nullptr);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
init(&disposer);
|
|
|
|
}
|
|
|
|
|
|
|
|
Framebuffer::~Framebuffer()
|
|
|
|
{
|
|
|
|
if (framebuffer != VK_NULL_HANDLE)
|
|
|
|
vkDestroyFramebuffer(device, framebuffer, nullptr);
|
|
|
|
if (render_pass != VK_NULL_HANDLE)
|
|
|
|
vkDestroyRenderPass(device, render_pass, nullptr);
|
|
|
|
if (view != VK_NULL_HANDLE)
|
|
|
|
vkDestroyImageView(device, view, nullptr);
|
|
|
|
if (image != VK_NULL_HANDLE)
|
|
|
|
vkDestroyImage(device, image, nullptr);
|
|
|
|
if (memory.memory != VK_NULL_HANDLE)
|
|
|
|
vkFreeMemory(device, memory.memory, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// C glue
|
|
|
|
vulkan_filter_chain_t *vulkan_filter_chain_new(
|
|
|
|
const vulkan_filter_chain_create_info *info)
|
|
|
|
{
|
|
|
|
return new vulkan_filter_chain(*info);
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
vulkan_filter_chain_t *vulkan_filter_chain_create_default(
|
|
|
|
const struct vulkan_filter_chain_create_info *info,
|
|
|
|
vulkan_filter_chain_filter filter)
|
2016-02-16 20:24:00 +01:00
|
|
|
{
|
2016-02-20 16:36:54 +01:00
|
|
|
struct vulkan_filter_chain_pass_info pass_info;
|
|
|
|
auto tmpinfo = *info;
|
2016-02-16 20:24:00 +01:00
|
|
|
tmpinfo.num_passes = 1;
|
|
|
|
|
|
|
|
unique_ptr<vulkan_filter_chain> chain{ new vulkan_filter_chain(tmpinfo) };
|
|
|
|
if (!chain)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
memset(&pass_info, 0, sizeof(pass_info));
|
2016-02-20 16:36:54 +01:00
|
|
|
pass_info.scale_type_x = VULKAN_FILTER_CHAIN_SCALE_VIEWPORT;
|
|
|
|
pass_info.scale_type_y = VULKAN_FILTER_CHAIN_SCALE_VIEWPORT;
|
|
|
|
pass_info.scale_x = 1.0f;
|
|
|
|
pass_info.scale_y = 1.0f;
|
|
|
|
pass_info.rt_format = tmpinfo.swapchain.format;
|
2016-02-16 20:24:00 +01:00
|
|
|
pass_info.source_filter = filter;
|
|
|
|
chain->set_pass_info(0, pass_info);
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
chain->set_shader(0, VK_SHADER_STAGE_VERTEX_BIT,
|
|
|
|
(const uint32_t*)opaque_vert_spv,
|
|
|
|
opaque_vert_spv_len / sizeof(uint32_t));
|
|
|
|
chain->set_shader(0, VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
|
|
(const uint32_t*)opaque_frag_spv,
|
|
|
|
opaque_frag_spv_len / sizeof(uint32_t));
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
if (!chain->init())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return chain.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ConfigDeleter
|
|
|
|
{
|
|
|
|
void operator()(config_file_t *conf)
|
|
|
|
{
|
|
|
|
if (conf)
|
|
|
|
config_file_free(conf);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset(
|
|
|
|
const struct vulkan_filter_chain_create_info *info,
|
2016-02-16 20:24:00 +01:00
|
|
|
const char *path, vulkan_filter_chain_filter filter)
|
|
|
|
{
|
|
|
|
unique_ptr<video_shader> shader{ new video_shader() };
|
|
|
|
if (!shader)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
unique_ptr<config_file_t, ConfigDeleter> conf{ config_file_new(path) };
|
|
|
|
if (!path)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if (!video_shader_read_conf_cgp(conf.get(), shader.get()))
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
video_shader_resolve_relative(shader.get(), path);
|
|
|
|
video_shader_resolve_parameters(conf.get(), shader.get());
|
|
|
|
|
|
|
|
bool last_pass_is_fbo = shader->pass[shader->passes - 1].fbo.valid;
|
2016-02-20 16:36:54 +01:00
|
|
|
auto tmpinfo = *info;
|
|
|
|
tmpinfo.num_passes = shader->passes + (last_pass_is_fbo ? 1 : 0);
|
2016-02-16 20:24:00 +01:00
|
|
|
|
|
|
|
unique_ptr<vulkan_filter_chain> chain{ new vulkan_filter_chain(tmpinfo) };
|
|
|
|
if (!chain)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < shader->passes; i++)
|
|
|
|
{
|
|
|
|
const video_shader_pass *pass = &shader->pass[i];
|
|
|
|
struct vulkan_filter_chain_pass_info pass_info;
|
|
|
|
memset(&pass_info, 0, sizeof(pass_info));
|
|
|
|
|
|
|
|
glslang_output output;
|
|
|
|
if (!glslang_compile_shader(pass->source.path, &output))
|
|
|
|
{
|
2016-02-20 16:36:54 +01:00
|
|
|
RARCH_ERR("Failed to compile shader: \"%s\".\n",
|
|
|
|
pass->source.path);
|
2016-02-16 20:24:00 +01:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
chain->set_shader(i,
|
|
|
|
VK_SHADER_STAGE_VERTEX_BIT,
|
|
|
|
output.vertex.data(),
|
|
|
|
output.vertex.size());
|
|
|
|
|
|
|
|
chain->set_shader(i,
|
|
|
|
VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
|
|
output.fragment.data(),
|
|
|
|
output.fragment.size());
|
|
|
|
|
|
|
|
if (pass->filter == RARCH_FILTER_UNSPEC)
|
|
|
|
pass_info.source_filter = filter;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pass_info.source_filter =
|
|
|
|
pass->filter == RARCH_FILTER_LINEAR ? VULKAN_FILTER_CHAIN_LINEAR :
|
|
|
|
VULKAN_FILTER_CHAIN_NEAREST;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pass->fbo.valid)
|
|
|
|
{
|
2016-02-20 16:36:54 +01:00
|
|
|
pass_info.scale_type_x = i + 1 == shader->passes
|
|
|
|
? VULKAN_FILTER_CHAIN_SCALE_VIEWPORT
|
|
|
|
: VULKAN_FILTER_CHAIN_SCALE_SOURCE;
|
|
|
|
pass_info.scale_type_y = i + 1 == shader->passes
|
|
|
|
? VULKAN_FILTER_CHAIN_SCALE_VIEWPORT
|
|
|
|
: VULKAN_FILTER_CHAIN_SCALE_SOURCE;
|
2016-02-16 20:24:00 +01:00
|
|
|
pass_info.scale_x = 1.0f;
|
|
|
|
pass_info.scale_y = 1.0f;
|
2016-02-20 16:36:54 +01:00
|
|
|
pass_info.rt_format = i + 1 == shader->passes
|
|
|
|
? tmpinfo.swapchain.format
|
|
|
|
: VK_FORMAT_R8G8B8A8_UNORM;
|
2016-02-16 20:24:00 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO: Add more general format spec.
|
|
|
|
pass_info.rt_format = VK_FORMAT_R8G8B8A8_UNORM;
|
|
|
|
if (pass->fbo.srgb_fbo)
|
|
|
|
pass_info.rt_format = VK_FORMAT_R8G8B8A8_SRGB;
|
|
|
|
else if (pass->fbo.fp_fbo)
|
|
|
|
pass_info.rt_format = VK_FORMAT_R16G16B16A16_SFLOAT;
|
|
|
|
|
|
|
|
switch (pass->fbo.type_x)
|
|
|
|
{
|
|
|
|
case RARCH_SCALE_INPUT:
|
|
|
|
pass_info.scale_x = pass->fbo.scale_x;
|
|
|
|
pass_info.scale_type_x = VULKAN_FILTER_CHAIN_SCALE_SOURCE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RARCH_SCALE_ABSOLUTE:
|
|
|
|
pass_info.scale_x = float(pass->fbo.abs_x);
|
|
|
|
pass_info.scale_type_x = VULKAN_FILTER_CHAIN_SCALE_ABSOLUTE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RARCH_SCALE_VIEWPORT:
|
|
|
|
pass_info.scale_x = pass->fbo.scale_x;
|
|
|
|
pass_info.scale_type_x = VULKAN_FILTER_CHAIN_SCALE_VIEWPORT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (pass->fbo.type_y)
|
|
|
|
{
|
|
|
|
case RARCH_SCALE_INPUT:
|
|
|
|
pass_info.scale_y = pass->fbo.scale_y;
|
|
|
|
pass_info.scale_type_y = VULKAN_FILTER_CHAIN_SCALE_SOURCE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RARCH_SCALE_ABSOLUTE:
|
|
|
|
pass_info.scale_y = float(pass->fbo.abs_y);
|
|
|
|
pass_info.scale_type_y = VULKAN_FILTER_CHAIN_SCALE_ABSOLUTE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RARCH_SCALE_VIEWPORT:
|
|
|
|
pass_info.scale_y = pass->fbo.scale_y;
|
|
|
|
pass_info.scale_type_y = VULKAN_FILTER_CHAIN_SCALE_VIEWPORT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
chain->set_pass_info(i, pass_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last_pass_is_fbo)
|
|
|
|
{
|
|
|
|
struct vulkan_filter_chain_pass_info pass_info;
|
|
|
|
memset(&pass_info, 0, sizeof(pass_info));
|
|
|
|
pass_info.scale_type_x = VULKAN_FILTER_CHAIN_SCALE_VIEWPORT;
|
|
|
|
pass_info.scale_type_y = VULKAN_FILTER_CHAIN_SCALE_VIEWPORT;
|
|
|
|
pass_info.scale_x = 1.0f;
|
|
|
|
pass_info.scale_y = 1.0f;
|
|
|
|
pass_info.rt_format = tmpinfo.swapchain.format;
|
|
|
|
pass_info.source_filter = filter;
|
|
|
|
chain->set_pass_info(shader->passes, pass_info);
|
|
|
|
|
|
|
|
chain->set_shader(shader->passes,
|
|
|
|
VK_SHADER_STAGE_VERTEX_BIT,
|
|
|
|
(const uint32_t*)opaque_vert_spv,
|
|
|
|
opaque_vert_spv_len / sizeof(uint32_t));
|
|
|
|
|
|
|
|
chain->set_shader(shader->passes,
|
|
|
|
VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
|
|
(const uint32_t*)opaque_frag_spv,
|
|
|
|
opaque_frag_spv_len / sizeof(uint32_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
chain->set_shader_preset(move(shader));
|
|
|
|
|
|
|
|
if (!chain->init())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return chain.release();
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
struct video_shader *vulkan_filter_chain_get_preset(
|
|
|
|
vulkan_filter_chain_t *chain)
|
2016-02-16 20:24:00 +01:00
|
|
|
{
|
|
|
|
return chain->get_shader_preset();
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
void vulkan_filter_chain_free(
|
|
|
|
vulkan_filter_chain_t *chain)
|
2016-02-16 20:24:00 +01:00
|
|
|
{
|
|
|
|
delete chain;
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
void vulkan_filter_chain_set_shader(
|
|
|
|
vulkan_filter_chain_t *chain,
|
2016-02-16 20:24:00 +01:00
|
|
|
unsigned pass,
|
|
|
|
VkShaderStageFlags stage,
|
|
|
|
const uint32_t *spirv,
|
|
|
|
size_t spirv_words)
|
|
|
|
{
|
|
|
|
chain->set_shader(pass, stage, spirv, spirv_words);
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
void vulkan_filter_chain_set_pass_info(
|
|
|
|
vulkan_filter_chain_t *chain,
|
2016-02-16 20:24:00 +01:00
|
|
|
unsigned pass,
|
|
|
|
const struct vulkan_filter_chain_pass_info *info)
|
|
|
|
{
|
|
|
|
chain->set_pass_info(pass, *info);
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
bool vulkan_filter_chain_update_swapchain_info(
|
|
|
|
vulkan_filter_chain_t *chain,
|
2016-02-16 20:24:00 +01:00
|
|
|
const vulkan_filter_chain_swapchain_info *info)
|
|
|
|
{
|
|
|
|
return chain->update_swapchain_info(*info);
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
void vulkan_filter_chain_notify_sync_index(
|
|
|
|
vulkan_filter_chain_t *chain,
|
2016-02-16 20:24:00 +01:00
|
|
|
unsigned index)
|
|
|
|
{
|
|
|
|
chain->notify_sync_index(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool vulkan_filter_chain_init(vulkan_filter_chain_t *chain)
|
|
|
|
{
|
|
|
|
return chain->init();
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
void vulkan_filter_chain_set_input_texture(
|
|
|
|
vulkan_filter_chain_t *chain,
|
2016-02-16 20:24:00 +01:00
|
|
|
const struct vulkan_filter_chain_texture *texture)
|
|
|
|
{
|
|
|
|
chain->set_input_texture(*texture);
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
void vulkan_filter_chain_build_offscreen_passes(
|
|
|
|
vulkan_filter_chain_t *chain,
|
2016-02-16 20:24:00 +01:00
|
|
|
VkCommandBuffer cmd, const VkViewport *vp)
|
|
|
|
{
|
|
|
|
chain->build_offscreen_passes(cmd, *vp);
|
|
|
|
}
|
|
|
|
|
2016-02-20 16:36:54 +01:00
|
|
|
void vulkan_filter_chain_build_viewport_pass(
|
|
|
|
vulkan_filter_chain_t *chain,
|
2016-02-16 20:24:00 +01:00
|
|
|
VkCommandBuffer cmd, const VkViewport *vp, const float *mvp)
|
|
|
|
{
|
|
|
|
chain->build_viewport_pass(cmd, *vp, mvp);
|
|
|
|
}
|
|
|
|
|