Merge pull request #2805 from Themaister/master

Vulkan: Fix lots of dubious barrier usage and add initial SPIR-V shader reflection
This commit is contained in:
Twinaphex 2016-03-21 17:05:17 +01:00
commit dfeef5b78d
10 changed files with 626 additions and 113 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "deps/glslang/glslang"]
path = deps/glslang/glslang
url = git://github.com/KhronosGroup/glslang.git
[submodule "deps/spir2cross"]
path = deps/spir2cross
url = git://github.com/ARM-software/spir2cross

View File

@ -749,25 +749,31 @@ ifeq ($(HAVE_VULKAN), 1)
$(wildcard deps/glslang/glslang/glslang/MachineIndependent/preprocessor/*.cpp) \
$(wildcard deps/glslang/glslang/glslang/OSDependent/$(GLSLANG_PLATFORM)/*.cpp)
SPIR2CROSS_SOURCES := deps/spir2cross/spir2cross.cpp
DEFINES += \
-Ideps/glslang/glslang/glslang/OSDependent/$(GLSLANG_PLATFORM) \
-Ideps/glslang/glslang \
-Ideps/glslang/glslang/glslang/MachineIndependent \
-Ideps/glslang/glslang/glslang/Public \
-Ideps/glslang/glslang/SPIRV \
-Ideps/glslang
-Ideps/glslang \
-Ideps/spir2cross
CXXFLAGS += -Wno-switch -Wno-sign-compare -fno-strict-aliasing -Wno-maybe-uninitialized -Wno-reorder -I./gfx/include/vulkan
CFLAGS += -I./gfx/include/vulkan
GLSLANG_OBJ := $(GLSLANG_SOURCES:.cpp=.o)
SPIR2CROSS_OBJ := $(SPIR2CROSS_SOURCES:.cpp=.o)
OBJ += gfx/drivers/vulkan.o \
gfx/common/vulkan_common.o \
gfx/drivers_font/vulkan_raster_font.o \
gfx/drivers_shader/shader_vulkan.o \
gfx/drivers_shader/glslang_util.o \
$(GLSLANG_OBJ)
gfx/drivers_shader/slang_reflection.o \
$(GLSLANG_OBJ) \
$(SPIR2CROSS_OBJ)
ifeq ($(HAVE_MENU_COMMON), 1)
OBJ += menu/drivers_display/menu_display_vulkan.o
endif

View File

@ -186,7 +186,7 @@ static void vulkan_test_render(void)
VkImageMemoryBarrier prepare_rendering = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
prepare_rendering.srcAccessMask = 0;
prepare_rendering.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
prepare_rendering.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
prepare_rendering.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
prepare_rendering.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
prepare_rendering.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
@ -704,7 +704,7 @@ static void context_destroy(void)
static bool retro_init_hw_context(void)
{
hw_render.context_type = RETRO_HW_CONTEXT_VULKAN;
hw_render.version_major = VK_API_VERSION;
hw_render.version_major = VK_MAKE_VERSION(1, 0, 6);
hw_render.version_minor = 0;
hw_render.context_reset = context_reset;
hw_render.context_destroy = context_destroy;

1
deps/spir2cross vendored Submodule

@ -0,0 +1 @@
Subproject commit 0ae2bcc3d0edc60e03180f6080a168f78edc82ca

View File

@ -135,7 +135,7 @@ void vulkan_copy_staging_to_dynamic(vk_t *vk, VkCommandBuffer cmd,
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);
VK_PIPELINE_STAGE_TRANSFER_BIT);
memset(&region, 0, sizeof(region));
region.extent.width = dynamic->width;
@ -444,15 +444,15 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
vulkan_image_layout_transition(vk, staging, tmp.image,
VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_GENERAL,
VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
0, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
VK_PIPELINE_STAGE_TRANSFER_BIT);
vulkan_image_layout_transition(vk, staging, tex.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);
VK_PIPELINE_STAGE_TRANSFER_BIT);
memset(&region, 0, sizeof(region));
region.extent.width = width;
@ -473,7 +473,7 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
VKFUNC(vkEndCommandBuffer)(staging);
submit_info.commandBufferCount = 1;
@ -553,15 +553,35 @@ void vulkan_transition_texture(vk_t *vk, struct vk_texture *texture)
/* Transition to GENERAL layout for linear streamed textures.
* We're using linear textures here, so only
* GENERAL layout is supported.
* If we're already in GENERAL, add a host -> shader read memory barrier
* to invalidate texture caches.
*/
if (texture->layout != VK_IMAGE_LAYOUT_PREINITIALIZED)
if (texture->layout != VK_IMAGE_LAYOUT_PREINITIALIZED &&
texture->layout != VK_IMAGE_LAYOUT_GENERAL)
return;
vulkan_image_layout_transition(vk, vk->cmd, texture->image,
texture->layout, VK_IMAGE_LAYOUT_GENERAL,
VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
switch (texture->type)
{
case VULKAN_TEXTURE_STREAMED:
vulkan_image_layout_transition(vk, vk->cmd, texture->image,
texture->layout, VK_IMAGE_LAYOUT_GENERAL,
0, VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
break;
case VULKAN_TEXTURE_STAGING:
vulkan_image_layout_transition(vk, vk->cmd, texture->image,
texture->layout, VK_IMAGE_LAYOUT_GENERAL,
0, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT);
break;
default:
retro_assert(0 && "Attempting to transition invalid texture type.\n");
break;
}
texture->layout = VK_IMAGE_LAYOUT_GENERAL;
}
@ -1242,7 +1262,7 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
app.applicationVersion = 0;
app.pEngineName = "RetroArch";
app.engineVersion = 0;
app.apiVersion = VK_MAKE_VERSION(1, 0, 2);
app.apiVersion = VK_MAKE_VERSION(1, 0, 6);
info.pApplicationInfo = &app;
info.enabledExtensionCount = ARRAY_SIZE(instance_extensions);

View File

@ -978,6 +978,7 @@ static void *vulkan_init(const video_info_t *video,
vk->tex_fmt = video->rgb32
? VK_FORMAT_B8G8R8A8_UNORM : VK_FORMAT_R5G6B5_UNORM_PACK16;
vk->keep_aspect = video->force_aspect;
RARCH_LOG("[Vulkan]: Using %s format.\n", video->rgb32 ? "BGRA8888" : "RGB565");
/* Set the viewport to fix recording, since it needs to know
* the viewport sizes before we start running. */
@ -1334,7 +1335,7 @@ static void vulkan_readback(vk_t *vk)
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL,
0, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
VK_PIPELINE_STAGE_TRANSFER_BIT);
VKFUNC(vkCmdCopyImage)(vk->cmd, vk->chain->backbuffer.image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
@ -1350,6 +1351,22 @@ static void vulkan_readback(vk_t *vk)
VK_PIPELINE_STAGE_HOST_BIT);
}
static void vulkan_flush_caches(vk_t *vk)
{
VkMemoryBarrier barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_UNIFORM_READ_BIT;
VKFUNC(vkCmdPipelineBarrier)(vk->cmd,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT |
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
false,
1, &barrier,
0, NULL, 0, NULL);
}
static bool vulkan_frame(void *data, const void *frame,
unsigned frame_width, unsigned frame_height,
uint64_t frame_count,
@ -1406,6 +1423,8 @@ static bool vulkan_frame(void *data, const void *frame,
memset(&vk->tracker, 0, sizeof(vk->tracker));
vulkan_flush_caches(vk);
/* Upload texture */
retro_perf_start(&copy_frame);
if (frame && !vk->hw.enable)
@ -1526,12 +1545,12 @@ static bool vulkan_frame(void *data, const void *frame,
rp_info.clearValueCount = 1;
rp_info.pClearValues = &clear_value;
/* Prepare backbuffer for rendering */
/* Prepare backbuffer for rendering. We don't use WSI semaphores here. */
vulkan_image_layout_transition(vk, vk->cmd, chain->backbuffer.image,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
/* Begin render pass and set up viewport */
VKFUNC(vkCmdBeginRenderPass)(vk->cmd, &rp_info, VK_SUBPASS_CONTENTS_INLINE);
@ -1612,10 +1631,10 @@ static bool vulkan_frame(void *data, const void *frame,
chain->backbuffer.image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_MEMORY_READ_BIT,
0,
0,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
vk->readback.pending = false;
}
@ -1627,9 +1646,9 @@ static bool vulkan_frame(void *data, const void *frame,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_MEMORY_READ_BIT,
0,
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
}
retro_perf_start(&end_cmd);

View File

@ -24,14 +24,16 @@
#include "glslang.hpp"
#include "../../general.h"
#include "../../libretro-common/include/file/file_path.h"
using namespace std;
bool read_file(const char *path, vector<string> *output)
static bool read_shader_file(const char *path, vector<string> *output)
{
char *buf = nullptr;
char *buf = nullptr;
ssize_t len = 0;
struct string_list *list = NULL;
char include_path[PATH_MAX];
if (retro_read_file(path, (void**)&buf, &len) < 0)
{
@ -54,15 +56,39 @@ bool read_file(const char *path, vector<string> *output)
return false;
}
output->clear();
for (size_t i = 0; i < list->size; i++)
output->push_back(list->elems[i].data);
{
const char *line = list->elems[i].data;
if (strstr(line, "#include ") == line)
{
char *c = (char*)strchr(line, '"');
if (!c)
{
RARCH_ERR("Invalid include statement \"%s\".\n", line);
return false;
}
c++;
char *closing = (char*)strchr(c, '"');
if (!closing)
{
RARCH_ERR("Invalid include statement \"%s\".\n", line);
return false;
}
*closing = '\0';
fill_pathname_resolve_relative(include_path, path, c, sizeof(include_path));
if (!read_shader_file(include_path, output))
return false;
}
else
output->push_back(line);
}
string_list_free(list);
return true;
}
string build_stage_source(const vector<string> &lines, const char *stage)
static string build_stage_source(const vector<string> &lines, const char *stage)
{
ostringstream str;
bool active = true;
@ -99,7 +125,7 @@ bool glslang_compile_shader(const char *shader_path, glslang_output *output)
vector<string> lines;
RARCH_LOG("Compiling shader \"%s\".\n", shader_path);
if (!read_file(shader_path, &lines))
if (!read_shader_file(shader_path, &lines))
return false;
auto &header = lines.front();

View File

@ -25,6 +25,7 @@
#include "../drivers/vulkan_shaders/opaque.frag.inc"
#include "../video_shader_driver.h"
#include "../../verbosity.h"
#include "slang_reflection.hpp"
using namespace std;
@ -175,6 +176,18 @@ class Framebuffer
void init_render_pass();
};
struct CommonResources
{
CommonResources(VkDevice device,
const VkPhysicalDeviceMemoryProperties &memory_properties);
~CommonResources();
unique_ptr<Buffer> vbo_offscreen, vbo_final;
VkSampler samplers[2];
VkDevice device;
};
class Pass
{
public:
@ -228,15 +241,12 @@ class Pass
return pass_info.source_filter;
}
private:
struct UBO
void set_common_resources(const CommonResources &common)
{
float MVP[16];
float output_size[4];
float original_size[4];
float source_size[4];
};
this->common = &common;
}
private:
VkDevice device;
const VkPhysicalDeviceMemoryProperties &memory_properties;
VkPipelineCache cache;
@ -251,11 +261,10 @@ class Pass
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;
const CommonResources *common = nullptr;
Size2D current_framebuffer_size;
VkViewport current_viewport;
@ -270,7 +279,6 @@ class Pass
bool init_pipeline();
bool init_pipeline_layout();
bool init_buffers();
bool init_samplers();
void image_layout_transition(VkDevice device,
VkCommandBuffer cmd, VkImage image,
@ -285,10 +293,17 @@ class Pass
void set_texture(VkDescriptorSet set, unsigned binding,
const Texture &texture);
void set_semantic_texture(VkDescriptorSet set, slang_texture_semantic semantic,
const Texture &texture);
void set_uniform_buffer(VkDescriptorSet set, unsigned binding,
VkBuffer buffer,
VkDeviceSize offset,
VkDeviceSize range);
slang_reflection reflection;
void build_semantic_vec4(uint8_t *data, slang_texture_semantic semantic,
unsigned width, unsigned height);
};
// struct here since we're implementing the opaque typedef from C.
@ -330,6 +345,7 @@ struct vulkan_filter_chain
vector<unique_ptr<Pass>> passes;
vector<vulkan_filter_chain_pass_info> pass_info;
vector<vector<function<void ()>>> deferred_calls;
CommonResources common;
vulkan_filter_chain_texture input_texture;
@ -351,7 +367,8 @@ vulkan_filter_chain::vulkan_filter_chain(
const vulkan_filter_chain_create_info &info)
: device(info.device),
memory_properties(*info.memory_properties),
cache(info.pipeline_cache)
cache(info.pipeline_cache),
common(info.device, *info.memory_properties)
{
max_input_size = { info.max_input_size.width, info.max_input_size.height };
set_swapchain_info(info.swapchain);
@ -397,6 +414,7 @@ void vulkan_filter_chain::set_num_passes(unsigned num_passes)
{
passes.emplace_back(new Pass(device, memory_properties,
cache, deferred_calls.size(), i + 1 == num_passes));
passes.back()->set_common_resources(common);
}
}
@ -660,42 +678,48 @@ void Pass::clear_vk()
if (pipeline_layout != VK_NULL_HANDLE)
VKFUNC(vkDestroyPipelineLayout)(device, pipeline_layout, nullptr);
for (auto &samp : samplers)
{
if (samp != VK_NULL_HANDLE)
VKFUNC(vkDestroySampler)(device, samp, nullptr);
samp = VK_NULL_HANDLE;
}
pool = VK_NULL_HANDLE;
pipeline = VK_NULL_HANDLE;
set_layout = VK_NULL_HANDLE;
ubos.clear();
vbo.reset();
}
bool Pass::init_pipeline_layout()
{
unsigned i;
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 });
// Main UBO.
VkShaderStageFlags ubo_mask = 0;
if (reflection.ubo_stage_mask & SLANG_STAGE_VERTEX_MASK)
ubo_mask |= VK_SHADER_STAGE_VERTEX_BIT;
if (reflection.ubo_stage_mask & SLANG_STAGE_FRAGMENT_MASK)
ubo_mask |= VK_SHADER_STAGE_FRAGMENT_BIT;
bindings.push_back({ reflection.ubo_binding,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1,
ubo_mask, nullptr });
desc_counts.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, num_sync_indices });
desc_counts.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, num_sync_indices });
desc_counts.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, num_sync_indices });
// Semantic textures.
for (unsigned i = 0; i < 32; i++)
{
if (reflection.semantic_texture_mask & (1u << i))
{
auto &texture = reflection.semantic_textures[i];
VkShaderStageFlags stages = 0;
if (texture.stage_mask & SLANG_STAGE_VERTEX_MASK)
stages |= VK_SHADER_STAGE_VERTEX_BIT;
if (texture.stage_mask & SLANG_STAGE_FRAGMENT_MASK)
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
bindings.push_back({ texture.binding,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1,
stages, nullptr });
desc_counts.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, num_sync_indices });
}
}
VkDescriptorSetLayoutCreateInfo set_layout_info = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
@ -731,7 +755,7 @@ bool Pass::init_pipeline_layout()
sets.resize(num_sync_indices);
for (i = 0; i < num_sync_indices; i++)
for (unsigned i = 0; i < num_sync_indices; i++)
VKFUNC(vkAllocateDescriptorSets)(device, &alloc_info, &sets[i]);
return true;
@ -869,8 +893,40 @@ bool Pass::init_pipeline()
return true;
}
bool Pass::init_samplers()
CommonResources::CommonResources(VkDevice device,
const VkPhysicalDeviceMemoryProperties &memory_properties)
: device(device)
{
// 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.
const float vbo_data_offscreen[] = {
-1.0f, -1.0f, 0.0f, 0.0f,
-1.0f, +1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, +1.0f, 1.0f, 1.0f,
};
const float vbo_data_final[] = {
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, +1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f,
1.0f, +1.0f, 1.0f, 1.0f,
};
vbo_offscreen = unique_ptr<Buffer>(new Buffer(device,
memory_properties, sizeof(vbo_data_offscreen), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT));
void *ptr = vbo_offscreen->map();
memcpy(ptr, vbo_data_offscreen, sizeof(vbo_data_offscreen));
vbo_offscreen->unmap();
vbo_final = unique_ptr<Buffer>(new Buffer(device,
memory_properties, sizeof(vbo_data_final), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT));
ptr = vbo_final->map();
memcpy(ptr, vbo_data_final, sizeof(vbo_data_final));
vbo_final->unmap();
VkSamplerCreateInfo info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
info.magFilter = VK_FILTER_NEAREST;
info.minFilter = VK_FILTER_NEAREST;
@ -886,44 +942,29 @@ bool Pass::init_samplers()
info.unnormalizedCoordinates = false;
info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
if (VKFUNC(vkCreateSampler)(device,
&info, NULL, &samplers[VULKAN_FILTER_CHAIN_NEAREST]) != VK_SUCCESS)
return false;
VKFUNC(vkCreateSampler)(device,
&info, nullptr, &samplers[VULKAN_FILTER_CHAIN_NEAREST]);
info.magFilter = VK_FILTER_LINEAR;
info.minFilter = VK_FILTER_LINEAR;
if (VKFUNC(vkCreateSampler)(device,
&info, NULL, &samplers[VULKAN_FILTER_CHAIN_LINEAR]) != VK_SUCCESS)
return false;
VKFUNC(vkCreateSampler)(device,
&info, nullptr, &samplers[VULKAN_FILTER_CHAIN_LINEAR]);
}
return true;
CommonResources::~CommonResources()
{
for (auto &samp : samplers)
if (samp != VK_NULL_HANDLE)
VKFUNC(vkDestroySampler)(device, samp, nullptr);
}
bool Pass::init_buffers()
{
unsigned i;
// 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();
for (i = 0; i < num_sync_indices; i++)
for (unsigned 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));
void *ptr = vbo->map();
memcpy(ptr, vbo_data, sizeof(vbo_data));
vbo->unmap();
memory_properties, reflection.ubo_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT));
return true;
}
@ -937,15 +978,16 @@ bool Pass::build()
pass_info.rt_format));
}
memset(&reflection, 0, sizeof(reflection));
if (!slang_reflect_spirv(vertex_shader, fragment_shader, &reflection))
return false;
if (!init_pipeline())
return false;
if (!init_buffers())
return false;
if (!init_samplers())
return false;
return true;
}
@ -1001,7 +1043,7 @@ void Pass::set_texture(VkDescriptorSet set, unsigned binding,
const Texture &texture)
{
VkDescriptorImageInfo image_info;
image_info.sampler = samplers[texture.filter];
image_info.sampler = common->samplers[texture.filter];
image_info.imageView = texture.texture.view;
image_info.imageLayout = texture.texture.layout;
@ -1015,14 +1057,33 @@ void Pass::set_texture(VkDescriptorSet set, unsigned binding,
VKFUNC(vkUpdateDescriptorSets)(device, 1, &write, 0, nullptr);
}
void Pass::set_semantic_texture(VkDescriptorSet set,
slang_texture_semantic semantic, const Texture &texture)
{
if (reflection.semantic_texture_mask & (1u << semantic))
set_texture(set, reflection.semantic_textures[semantic].binding, texture);
}
void Pass::update_descriptor_set(
const Texture &original,
const Texture &source)
{
set_uniform_buffer(sets[sync_index], 0,
ubos[sync_index]->get_buffer(), 0, sizeof(UBO));
set_texture(sets[sync_index], 1, original);
set_texture(sets[sync_index], 2, source);
ubos[sync_index]->get_buffer(), 0, reflection.ubo_size);
set_semantic_texture(sets[sync_index], SLANG_TEXTURE_SEMANTIC_ORIGINAL, original);
set_semantic_texture(sets[sync_index], SLANG_TEXTURE_SEMANTIC_SOURCE, source);
}
void Pass::build_semantic_vec4(uint8_t *data, slang_texture_semantic semantic, unsigned width, unsigned height)
{
if (reflection.semantic_texture_ubo_mask & (1 << semantic))
{
build_vec4(
reinterpret_cast<float *>(data + reflection.semantic_textures[semantic].ubo_offset),
width,
height);
}
}
void Pass::build_commands(
@ -1046,19 +1107,20 @@ void Pass::build_commands(
current_framebuffer_size = size;
}
UBO *u = static_cast<UBO*>(ubos[sync_index]->map());
uint8_t *u = static_cast<uint8_t*>(ubos[sync_index]->map());
if (mvp)
memcpy(u->MVP, mvp, sizeof(float) * 16);
memcpy(u + reflection.mvp_offset, mvp, sizeof(float) * 16);
else
build_identity_matrix(u->MVP);
build_vec4(u->output_size,
current_framebuffer_size.width,
current_framebuffer_size.height);
build_vec4(u->original_size,
build_identity_matrix(reinterpret_cast<float *>(u + reflection.mvp_offset));
build_semantic_vec4(u, SLANG_TEXTURE_SEMANTIC_OUTPUT,
current_framebuffer_size.width, current_framebuffer_size.height);
build_semantic_vec4(u, SLANG_TEXTURE_SEMANTIC_ORIGINAL,
original.texture.width, original.texture.height);
build_vec4(u->source_size,
build_semantic_vec4(u, SLANG_TEXTURE_SEMANTIC_SOURCE,
source.texture.width, source.texture.height);
ubos[sync_index]->unmap();
update_descriptor_set(original, source);
@ -1074,9 +1136,9 @@ void Pass::build_commands(
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,
0,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
VkRenderPassBeginInfo rp_info = {
@ -1101,7 +1163,9 @@ void Pass::build_commands(
0, 1, &sets[sync_index], 0, nullptr);
VkDeviceSize offset = 0;
VKFUNC(vkCmdBindVertexBuffers)(cmd, 0, 1, &vbo->get_buffer(), &offset);
VKFUNC(vkCmdBindVertexBuffers)(cmd, 0, 1,
final_pass ? &common->vbo_final->get_buffer() : &common->vbo_offscreen->get_buffer(),
&offset);
if (final_pass)
{

View File

@ -0,0 +1,310 @@
/* 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 "spir2cross.hpp"
#include "slang_reflection.hpp"
#include <vector>
#include <stdio.h>
#include "../../verbosity.h"
using namespace std;
using namespace spir2cross;
static slang_texture_semantic slang_name_to_semantic(const string &name)
{
if (name == "Original")
return SLANG_TEXTURE_SEMANTIC_ORIGINAL;
else if (name == "Source")
return SLANG_TEXTURE_SEMANTIC_SOURCE;
else
return SLANG_TEXTURE_INVALID_SEMANTIC;
}
static bool find_uniform_offset(const Compiler &compiler, const Resource &resource, const char *name,
size_t *offset, unsigned *member_index)
{
auto &type = compiler.get_type(resource.type_id);
size_t num_members = type.member_types.size();
for (size_t i = 0; i < num_members; i++)
{
if (compiler.get_member_name(resource.type_id, i) == name)
{
*offset = compiler.get_member_decoration(resource.type_id, i, spv::DecorationOffset);
*member_index = i;
return true;
}
}
return false;
}
static bool find_semantic_uniform(slang_reflection *reflection, slang_texture_semantic index,
const Compiler &vertex_compiler, const vector<Resource> &vertex_resources,
const Compiler &fragment_compiler, const vector<Resource> &fragment_resources,
const char *name)
{
unsigned member_index = 0;
auto &semantic = reflection->semantic_textures[index];
size_t vertex_ubo_offset = size_t(-1);
size_t fragment_ubo_offset = size_t(-1);
// TODO: Do we want to expose Size uniforms if no stage is using the texture?
if (find_uniform_offset(vertex_compiler, vertex_resources[0], name,
&vertex_ubo_offset, &member_index))
{
auto &type = vertex_compiler.get_type(
vertex_compiler.get_type(vertex_resources[0].type_id).member_types[member_index]);
// Verify that the type is a vec4 to avoid any nasty surprises later.
bool is_vec4 = type.basetype == SPIRType::Float &&
type.array.empty() &&
type.vecsize == 4 &&
type.columns == 1;
if (!is_vec4)
{
RARCH_ERR("[slang]: Semantic uniform is not vec4.\n");
return false;
}
reflection->semantic_texture_ubo_mask |= 1 << index;
}
if (!fragment_resources.empty() &&
find_uniform_offset(fragment_compiler, fragment_resources[0], name,
&fragment_ubo_offset, &member_index))
{
auto &type = fragment_compiler.get_type(
fragment_compiler.get_type(fragment_resources[0].type_id).member_types[member_index]);
// Verify that the type is a vec4 to avoid any nasty surprises later.
bool is_vec4 = type.basetype == SPIRType::Float &&
type.array.empty() &&
type.vecsize == 4 &&
type.columns == 1;
if (!is_vec4)
{
RARCH_ERR("[slang]: Semantic uniform is not vec4.\n");
return false;
}
reflection->semantic_texture_ubo_mask |= 1 << index;
}
// Check for UBO offset mismatch between stages.
if (vertex_ubo_offset != size_t(-1) &&
fragment_ubo_offset != size_t(-1) &&
vertex_ubo_offset != fragment_ubo_offset)
{
RARCH_ERR("[slang]: Vertex (%u) and fragment (%u) UBO offset for %s mismatches.\n",
unsigned(vertex_ubo_offset), unsigned(fragment_ubo_offset), name);
return false;
}
semantic.ubo_offset = vertex_ubo_offset;
return true;
}
static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragment_compiler,
const ShaderResources &vertex, const ShaderResources &fragment,
slang_reflection *reflection)
{
// Validate use of unexpected types.
if (
!vertex.sampled_images.empty() ||
!vertex.storage_buffers.empty() ||
!vertex.subpass_inputs.empty() ||
!vertex.storage_images.empty() ||
!vertex.atomic_counters.empty() ||
!vertex.push_constant_buffers.empty() ||
!fragment.storage_buffers.empty() ||
!fragment.subpass_inputs.empty() ||
!fragment.storage_images.empty() ||
!fragment.atomic_counters.empty() ||
!fragment.push_constant_buffers.empty())
{
RARCH_ERR("[slang]: Invalid resource type detected.\n");
return false;
}
// Validate vertex input.
if (vertex.stage_inputs.size() != 2)
{
RARCH_ERR("[slang]: Vertex must have two attributes.\n");
return false;
}
uint32_t location_mask = 0;
for (auto &input : vertex.stage_inputs)
location_mask |= 1 << vertex_compiler.get_decoration(input.id, spv::DecorationLocation);
if (location_mask != 0x3)
{
RARCH_ERR("[slang]: The two vertex attributes do not use location = 0 and location = 1.\n");
return false;
}
// Validate the single uniform buffer.
if (vertex.uniform_buffers.size() != 1)
{
RARCH_ERR("[slang]: Vertex must use exactly one uniform buffer.\n");
return false;
}
if (fragment.uniform_buffers.size() > 1)
{
RARCH_ERR("[slang]: Fragment must use zero or one uniform buffer.\n");
return false;
}
if (vertex_compiler.get_decoration(vertex.uniform_buffers[0].id, spv::DecorationDescriptorSet) != 0)
{
RARCH_ERR("[slang]: Resources must use descriptor set #0.\n");
return false;
}
if (!fragment.uniform_buffers.empty() &&
fragment_compiler.get_decoration(fragment.uniform_buffers[0].id, spv::DecorationDescriptorSet) != 0)
{
RARCH_ERR("[slang]: Resources must use descriptor set #0.\n");
return false;
}
unsigned ubo_binding = vertex_compiler.get_decoration(vertex.uniform_buffers[0].id,
spv::DecorationBinding);
if (!fragment.uniform_buffers.empty() &&
ubo_binding != fragment_compiler.get_decoration(fragment.uniform_buffers[0].id, spv::DecorationBinding))
{
RARCH_ERR("[slang]: Vertex and fragment uniform buffer must have same binding.\n");
return false;
}
if (ubo_binding >= SLANG_NUM_BINDINGS)
{
RARCH_ERR("[slang]: Binding %u is out of range.\n", ubo_binding);
return false;
}
reflection->ubo_binding = ubo_binding;
reflection->ubo_stage_mask = SLANG_STAGE_VERTEX_MASK;
reflection->ubo_size = vertex_compiler.get_declared_struct_size(
vertex_compiler.get_type(vertex.uniform_buffers[0].type_id));
if (!fragment.uniform_buffers.empty())
{
reflection->ubo_stage_mask |= SLANG_STAGE_FRAGMENT_MASK;
reflection->ubo_size = max(reflection->ubo_size,
fragment_compiler.get_declared_struct_size(fragment_compiler.get_type(fragment.uniform_buffers[0].type_id)));
}
// Find two magic uniforms, MVP and OutputSize.
unsigned member_index = 0;
if (!find_uniform_offset(vertex_compiler, vertex.uniform_buffers[0], "MVP", &reflection->mvp_offset, &member_index))
{
RARCH_ERR("[slang]: Could not find offset for MVP matrix.\n");
return false;
}
if (!find_semantic_uniform(reflection, SLANG_TEXTURE_SEMANTIC_OUTPUT,
vertex_compiler, vertex.uniform_buffers,
fragment_compiler, fragment.uniform_buffers,
"OutputSize"))
{
return false;
}
////
uint32_t binding_mask = 1 << ubo_binding;
// On to textures.
for (auto &texture : fragment.sampled_images)
{
unsigned set = fragment_compiler.get_decoration(texture.id,
spv::DecorationDescriptorSet);
unsigned binding = fragment_compiler.get_decoration(texture.id,
spv::DecorationBinding);
if (set != 0)
{
RARCH_ERR("[slang]: Resources must use descriptor set #0.\n");
return false;
}
if (binding >= SLANG_NUM_BINDINGS)
{
RARCH_ERR("[slang]: Binding %u is out of range.\n", ubo_binding);
return false;
}
if (binding_mask & (1 << binding))
{
RARCH_ERR("[slang]: Binding %u is already in use.\n", binding);
return false;
}
binding_mask |= 1 << binding;
slang_texture_semantic index = slang_name_to_semantic(texture.name);
if (index == SLANG_TEXTURE_INVALID_SEMANTIC)
{
RARCH_ERR("[slang]: Non-semantic textures not supported.\n");
return false;
}
auto &semantic = reflection->semantic_textures[index];
semantic.binding = binding;
semantic.stage_mask = SLANG_STAGE_FRAGMENT_MASK;
reflection->semantic_texture_mask |= 1 << index;
char uniform_name[128];
snprintf(uniform_name, sizeof(uniform_name), "%sSize", texture.name.c_str());
find_semantic_uniform(reflection, index,
vertex_compiler, vertex.uniform_buffers,
fragment_compiler, fragment.uniform_buffers,
uniform_name);
}
return true;
}
bool slang_reflect_spirv(const std::vector<uint32_t> &vertex,
const std::vector<uint32_t> &fragment,
slang_reflection *reflection)
{
try
{
Compiler vertex_compiler(vertex);
Compiler fragment_compiler(fragment);
auto vertex_resources = vertex_compiler.get_shader_resources();
auto fragment_resources = fragment_compiler.get_shader_resources();
if (!slang_reflect(vertex_compiler, fragment_compiler,
vertex_resources, fragment_resources,
reflection))
{
RARCH_ERR("[slang]: Failed to reflect SPIR-V. Resource usage is inconsistent with expectations.\n");
return false;
}
return true;
}
catch (const std::exception &e)
{
RARCH_ERR("[slang]: spir2cross threw exception: %s.\n", e.what());
return false;
}
}

View File

@ -0,0 +1,64 @@
/* 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/>.
*/
#ifndef SLANG_REFLECTION_HPP
#define SLANG_REFLECTION_HPP
#include <string>
#include <stdint.h>
// Textures with built-in meaning.
enum slang_texture_semantic
{
SLANG_TEXTURE_SEMANTIC_ORIGINAL = 0,
SLANG_TEXTURE_SEMANTIC_SOURCE = 1,
SLANG_TEXTURE_SEMANTIC_OUTPUT = 2,
SLANG_TEXTURE_NUM_SEMANTICS,
SLANG_TEXTURE_INVALID_SEMANTIC = -1
};
enum slang_stage
{
SLANG_STAGE_VERTEX_MASK = 1 << 0,
SLANG_STAGE_FRAGMENT_MASK = 1 << 1
};
#define SLANG_NUM_BINDINGS 16
struct slang_texture_semantic_meta
{
size_t ubo_offset = 0;
unsigned binding = 0;
uint32_t stage_mask = 0;
};
struct slang_reflection
{
size_t ubo_size = 0;
size_t mvp_offset = 0;
unsigned ubo_binding = 0;
uint32_t ubo_stage_mask = 0;
slang_texture_semantic_meta semantic_textures[SLANG_TEXTURE_NUM_SEMANTICS];
uint32_t semantic_texture_mask = 0;
uint32_t semantic_texture_ubo_mask = 0;
};
bool slang_reflect_spirv(const std::vector<uint32_t> &vertex,
const std::vector<uint32_t> &fragment,
slang_reflection *reflection);
#endif