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"] [submodule "deps/glslang/glslang"]
path = deps/glslang/glslang path = deps/glslang/glslang
url = git://github.com/KhronosGroup/glslang.git 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/MachineIndependent/preprocessor/*.cpp) \
$(wildcard deps/glslang/glslang/glslang/OSDependent/$(GLSLANG_PLATFORM)/*.cpp) $(wildcard deps/glslang/glslang/glslang/OSDependent/$(GLSLANG_PLATFORM)/*.cpp)
SPIR2CROSS_SOURCES := deps/spir2cross/spir2cross.cpp
DEFINES += \ DEFINES += \
-Ideps/glslang/glslang/glslang/OSDependent/$(GLSLANG_PLATFORM) \ -Ideps/glslang/glslang/glslang/OSDependent/$(GLSLANG_PLATFORM) \
-Ideps/glslang/glslang \ -Ideps/glslang/glslang \
-Ideps/glslang/glslang/glslang/MachineIndependent \ -Ideps/glslang/glslang/glslang/MachineIndependent \
-Ideps/glslang/glslang/glslang/Public \ -Ideps/glslang/glslang/glslang/Public \
-Ideps/glslang/glslang/SPIRV \ -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 CXXFLAGS += -Wno-switch -Wno-sign-compare -fno-strict-aliasing -Wno-maybe-uninitialized -Wno-reorder -I./gfx/include/vulkan
CFLAGS += -I./gfx/include/vulkan CFLAGS += -I./gfx/include/vulkan
GLSLANG_OBJ := $(GLSLANG_SOURCES:.cpp=.o) GLSLANG_OBJ := $(GLSLANG_SOURCES:.cpp=.o)
SPIR2CROSS_OBJ := $(SPIR2CROSS_SOURCES:.cpp=.o)
OBJ += gfx/drivers/vulkan.o \ OBJ += gfx/drivers/vulkan.o \
gfx/common/vulkan_common.o \ gfx/common/vulkan_common.o \
gfx/drivers_font/vulkan_raster_font.o \ gfx/drivers_font/vulkan_raster_font.o \
gfx/drivers_shader/shader_vulkan.o \ gfx/drivers_shader/shader_vulkan.o \
gfx/drivers_shader/glslang_util.o \ gfx/drivers_shader/glslang_util.o \
$(GLSLANG_OBJ) gfx/drivers_shader/slang_reflection.o \
$(GLSLANG_OBJ) \
$(SPIR2CROSS_OBJ)
ifeq ($(HAVE_MENU_COMMON), 1) ifeq ($(HAVE_MENU_COMMON), 1)
OBJ += menu/drivers_display/menu_display_vulkan.o OBJ += menu/drivers_display/menu_display_vulkan.o
endif endif

View File

@ -186,7 +186,7 @@ static void vulkan_test_render(void)
VkImageMemoryBarrier prepare_rendering = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; VkImageMemoryBarrier prepare_rendering = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
prepare_rendering.srcAccessMask = 0; 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.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
prepare_rendering.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; prepare_rendering.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
prepare_rendering.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; prepare_rendering.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
@ -704,7 +704,7 @@ static void context_destroy(void)
static bool retro_init_hw_context(void) static bool retro_init_hw_context(void)
{ {
hw_render.context_type = RETRO_HW_CONTEXT_VULKAN; 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.version_minor = 0;
hw_render.context_reset = context_reset; hw_render.context_reset = context_reset;
hw_render.context_destroy = context_destroy; 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, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0, VK_ACCESS_TRANSFER_WRITE_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_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)); memset(&region, 0, sizeof(region));
region.extent.width = dynamic->width; 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, vulkan_image_layout_transition(vk, staging, tmp.image,
VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_GENERAL, 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_TOP_OF_PIPE_BIT); VK_PIPELINE_STAGE_TRANSFER_BIT);
vulkan_image_layout_transition(vk, staging, tex.image, vulkan_image_layout_transition(vk, staging, tex.image,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
0, VK_ACCESS_TRANSFER_WRITE_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_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)); memset(&region, 0, sizeof(region));
region.extent.width = width; region.extent.width = width;
@ -473,7 +473,7 @@ struct vk_texture vulkan_create_texture(vk_t *vk,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
VKFUNC(vkEndCommandBuffer)(staging); VKFUNC(vkEndCommandBuffer)(staging);
submit_info.commandBufferCount = 1; 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. /* Transition to GENERAL layout for linear streamed textures.
* We're using linear textures here, so only * We're using linear textures here, so only
* GENERAL layout is supported. * 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; return;
vulkan_image_layout_transition(vk, vk->cmd, texture->image, switch (texture->type)
texture->layout, VK_IMAGE_LAYOUT_GENERAL, {
VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, case VULKAN_TEXTURE_STREAMED:
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vulkan_image_layout_transition(vk, vk->cmd, texture->image,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); 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; texture->layout = VK_IMAGE_LAYOUT_GENERAL;
} }
@ -1242,7 +1262,7 @@ bool vulkan_context_init(gfx_ctx_vulkan_data_t *vk,
app.applicationVersion = 0; app.applicationVersion = 0;
app.pEngineName = "RetroArch"; app.pEngineName = "RetroArch";
app.engineVersion = 0; app.engineVersion = 0;
app.apiVersion = VK_MAKE_VERSION(1, 0, 2); app.apiVersion = VK_MAKE_VERSION(1, 0, 6);
info.pApplicationInfo = &app; info.pApplicationInfo = &app;
info.enabledExtensionCount = ARRAY_SIZE(instance_extensions); 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->tex_fmt = video->rgb32
? VK_FORMAT_B8G8R8A8_UNORM : VK_FORMAT_R5G6B5_UNORM_PACK16; ? VK_FORMAT_B8G8R8A8_UNORM : VK_FORMAT_R5G6B5_UNORM_PACK16;
vk->keep_aspect = video->force_aspect; 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 /* Set the viewport to fix recording, since it needs to know
* the viewport sizes before we start running. */ * 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, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL,
0, VK_ACCESS_TRANSFER_WRITE_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_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, VKFUNC(vkCmdCopyImage)(vk->cmd, vk->chain->backbuffer.image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
@ -1350,6 +1351,22 @@ static void vulkan_readback(vk_t *vk)
VK_PIPELINE_STAGE_HOST_BIT); 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, static bool vulkan_frame(void *data, const void *frame,
unsigned frame_width, unsigned frame_height, unsigned frame_width, unsigned frame_height,
uint64_t frame_count, uint64_t frame_count,
@ -1406,6 +1423,8 @@ static bool vulkan_frame(void *data, const void *frame,
memset(&vk->tracker, 0, sizeof(vk->tracker)); memset(&vk->tracker, 0, sizeof(vk->tracker));
vulkan_flush_caches(vk);
/* Upload texture */ /* Upload texture */
retro_perf_start(&copy_frame); retro_perf_start(&copy_frame);
if (frame && !vk->hw.enable) if (frame && !vk->hw.enable)
@ -1526,12 +1545,12 @@ static bool vulkan_frame(void *data, const void *frame,
rp_info.clearValueCount = 1; rp_info.clearValueCount = 1;
rp_info.pClearValues = &clear_value; 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, vulkan_image_layout_transition(vk, vk->cmd, chain->backbuffer.image,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 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_TOP_OF_PIPE_BIT); VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
/* Begin render pass and set up viewport */ /* Begin render pass and set up viewport */
VKFUNC(vkCmdBeginRenderPass)(vk->cmd, &rp_info, VK_SUBPASS_CONTENTS_INLINE); 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, chain->backbuffer.image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_ACCESS_TRANSFER_READ_BIT, 0,
VK_ACCESS_MEMORY_READ_BIT, 0,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
vk->readback.pending = false; 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_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_MEMORY_READ_BIT, 0,
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 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); retro_perf_start(&end_cmd);

View File

@ -24,14 +24,16 @@
#include "glslang.hpp" #include "glslang.hpp"
#include "../../general.h" #include "../../general.h"
#include "../../libretro-common/include/file/file_path.h"
using namespace std; 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; ssize_t len = 0;
struct string_list *list = NULL; struct string_list *list = NULL;
char include_path[PATH_MAX];
if (retro_read_file(path, (void**)&buf, &len) < 0) if (retro_read_file(path, (void**)&buf, &len) < 0)
{ {
@ -54,15 +56,39 @@ bool read_file(const char *path, vector<string> *output)
return false; return false;
} }
output->clear();
for (size_t i = 0; i < list->size; i++) 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); string_list_free(list);
return true; 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; ostringstream str;
bool active = true; bool active = true;
@ -99,7 +125,7 @@ bool glslang_compile_shader(const char *shader_path, glslang_output *output)
vector<string> lines; vector<string> lines;
RARCH_LOG("Compiling shader \"%s\".\n", shader_path); RARCH_LOG("Compiling shader \"%s\".\n", shader_path);
if (!read_file(shader_path, &lines)) if (!read_shader_file(shader_path, &lines))
return false; return false;
auto &header = lines.front(); auto &header = lines.front();

View File

@ -25,6 +25,7 @@
#include "../drivers/vulkan_shaders/opaque.frag.inc" #include "../drivers/vulkan_shaders/opaque.frag.inc"
#include "../video_shader_driver.h" #include "../video_shader_driver.h"
#include "../../verbosity.h" #include "../../verbosity.h"
#include "slang_reflection.hpp"
using namespace std; using namespace std;
@ -175,6 +176,18 @@ class Framebuffer
void init_render_pass(); 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 class Pass
{ {
public: public:
@ -228,15 +241,12 @@ class Pass
return pass_info.source_filter; return pass_info.source_filter;
} }
private: void set_common_resources(const CommonResources &common)
struct UBO
{ {
float MVP[16]; this->common = &common;
float output_size[4]; }
float original_size[4];
float source_size[4];
};
private:
VkDevice device; VkDevice device;
const VkPhysicalDeviceMemoryProperties &memory_properties; const VkPhysicalDeviceMemoryProperties &memory_properties;
VkPipelineCache cache; VkPipelineCache cache;
@ -251,11 +261,10 @@ class Pass
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
VkDescriptorSetLayout set_layout = VK_NULL_HANDLE; VkDescriptorSetLayout set_layout = VK_NULL_HANDLE;
VkDescriptorPool pool = VK_NULL_HANDLE; VkDescriptorPool pool = VK_NULL_HANDLE;
VkSampler samplers[2] = { VK_NULL_HANDLE, VK_NULL_HANDLE };
vector<unique_ptr<Buffer>> ubos; vector<unique_ptr<Buffer>> ubos;
unique_ptr<Buffer> vbo;
vector<VkDescriptorSet> sets; vector<VkDescriptorSet> sets;
const CommonResources *common = nullptr;
Size2D current_framebuffer_size; Size2D current_framebuffer_size;
VkViewport current_viewport; VkViewport current_viewport;
@ -270,7 +279,6 @@ class Pass
bool init_pipeline(); bool init_pipeline();
bool init_pipeline_layout(); bool init_pipeline_layout();
bool init_buffers(); bool init_buffers();
bool init_samplers();
void image_layout_transition(VkDevice device, void image_layout_transition(VkDevice device,
VkCommandBuffer cmd, VkImage image, VkCommandBuffer cmd, VkImage image,
@ -285,10 +293,17 @@ class Pass
void set_texture(VkDescriptorSet set, unsigned binding, void set_texture(VkDescriptorSet set, unsigned binding,
const Texture &texture); const Texture &texture);
void set_semantic_texture(VkDescriptorSet set, slang_texture_semantic semantic,
const Texture &texture);
void set_uniform_buffer(VkDescriptorSet set, unsigned binding, void set_uniform_buffer(VkDescriptorSet set, unsigned binding,
VkBuffer buffer, VkBuffer buffer,
VkDeviceSize offset, VkDeviceSize offset,
VkDeviceSize range); 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. // 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<unique_ptr<Pass>> passes;
vector<vulkan_filter_chain_pass_info> pass_info; vector<vulkan_filter_chain_pass_info> pass_info;
vector<vector<function<void ()>>> deferred_calls; vector<vector<function<void ()>>> deferred_calls;
CommonResources common;
vulkan_filter_chain_texture input_texture; vulkan_filter_chain_texture input_texture;
@ -351,7 +367,8 @@ vulkan_filter_chain::vulkan_filter_chain(
const vulkan_filter_chain_create_info &info) const vulkan_filter_chain_create_info &info)
: device(info.device), : device(info.device),
memory_properties(*info.memory_properties), 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 }; max_input_size = { info.max_input_size.width, info.max_input_size.height };
set_swapchain_info(info.swapchain); 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, passes.emplace_back(new Pass(device, memory_properties,
cache, deferred_calls.size(), i + 1 == num_passes)); 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) if (pipeline_layout != VK_NULL_HANDLE)
VKFUNC(vkDestroyPipelineLayout)(device, pipeline_layout, nullptr); 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; pool = VK_NULL_HANDLE;
pipeline = VK_NULL_HANDLE; pipeline = VK_NULL_HANDLE;
set_layout = VK_NULL_HANDLE; set_layout = VK_NULL_HANDLE;
ubos.clear(); ubos.clear();
vbo.reset();
} }
bool Pass::init_pipeline_layout() bool Pass::init_pipeline_layout()
{ {
unsigned i;
vector<VkDescriptorSetLayoutBinding> bindings; vector<VkDescriptorSetLayoutBinding> bindings;
vector<VkDescriptorPoolSize> desc_counts; vector<VkDescriptorPoolSize> desc_counts;
// TODO: Expand this a lot, need to reflect shaders to figure this out. // Main UBO.
bindings.push_back({ 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VkShaderStageFlags ubo_mask = 0;
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, if (reflection.ubo_stage_mask & SLANG_STAGE_VERTEX_MASK)
nullptr }); ubo_mask |= VK_SHADER_STAGE_VERTEX_BIT;
if (reflection.ubo_stage_mask & SLANG_STAGE_FRAGMENT_MASK)
bindings.push_back({ 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, ubo_mask |= VK_SHADER_STAGE_FRAGMENT_BIT;
VK_SHADER_STAGE_FRAGMENT_BIT,
nullptr });
bindings.push_back({ 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1,
VK_SHADER_STAGE_FRAGMENT_BIT,
nullptr });
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_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 = { VkDescriptorSetLayoutCreateInfo set_layout_info = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
@ -731,7 +755,7 @@ bool Pass::init_pipeline_layout()
sets.resize(num_sync_indices); 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]); VKFUNC(vkAllocateDescriptorSets)(device, &alloc_info, &sets[i]);
return true; return true;
@ -869,8 +893,40 @@ bool Pass::init_pipeline()
return true; 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 }; VkSamplerCreateInfo info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
info.magFilter = VK_FILTER_NEAREST; info.magFilter = VK_FILTER_NEAREST;
info.minFilter = VK_FILTER_NEAREST; info.minFilter = VK_FILTER_NEAREST;
@ -886,44 +942,29 @@ bool Pass::init_samplers()
info.unnormalizedCoordinates = false; info.unnormalizedCoordinates = false;
info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
if (VKFUNC(vkCreateSampler)(device, VKFUNC(vkCreateSampler)(device,
&info, NULL, &samplers[VULKAN_FILTER_CHAIN_NEAREST]) != VK_SUCCESS) &info, nullptr, &samplers[VULKAN_FILTER_CHAIN_NEAREST]);
return false;
info.magFilter = VK_FILTER_LINEAR; info.magFilter = VK_FILTER_LINEAR;
info.minFilter = VK_FILTER_LINEAR; info.minFilter = VK_FILTER_LINEAR;
if (VKFUNC(vkCreateSampler)(device, VKFUNC(vkCreateSampler)(device,
&info, NULL, &samplers[VULKAN_FILTER_CHAIN_LINEAR]) != VK_SUCCESS) &info, nullptr, &samplers[VULKAN_FILTER_CHAIN_LINEAR]);
return false; }
return true; CommonResources::~CommonResources()
{
for (auto &samp : samplers)
if (samp != VK_NULL_HANDLE)
VKFUNC(vkDestroySampler)(device, samp, nullptr);
} }
bool Pass::init_buffers() 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(); 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, ubos.emplace_back(new Buffer(device,
memory_properties, sizeof(UBO), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)); memory_properties, reflection.ubo_size, 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();
return true; return true;
} }
@ -937,15 +978,16 @@ bool Pass::build()
pass_info.rt_format)); pass_info.rt_format));
} }
memset(&reflection, 0, sizeof(reflection));
if (!slang_reflect_spirv(vertex_shader, fragment_shader, &reflection))
return false;
if (!init_pipeline()) if (!init_pipeline())
return false; return false;
if (!init_buffers()) if (!init_buffers())
return false; return false;
if (!init_samplers())
return false;
return true; return true;
} }
@ -1001,7 +1043,7 @@ void Pass::set_texture(VkDescriptorSet set, unsigned binding,
const Texture &texture) const Texture &texture)
{ {
VkDescriptorImageInfo image_info; VkDescriptorImageInfo image_info;
image_info.sampler = samplers[texture.filter]; image_info.sampler = common->samplers[texture.filter];
image_info.imageView = texture.texture.view; image_info.imageView = texture.texture.view;
image_info.imageLayout = texture.texture.layout; 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); 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( void Pass::update_descriptor_set(
const Texture &original, const Texture &original,
const Texture &source) const Texture &source)
{ {
set_uniform_buffer(sets[sync_index], 0, set_uniform_buffer(sets[sync_index], 0,
ubos[sync_index]->get_buffer(), 0, sizeof(UBO)); ubos[sync_index]->get_buffer(), 0, reflection.ubo_size);
set_texture(sets[sync_index], 1, original);
set_texture(sets[sync_index], 2, source); 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( void Pass::build_commands(
@ -1046,19 +1107,20 @@ void Pass::build_commands(
current_framebuffer_size = size; 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) if (mvp)
memcpy(u->MVP, mvp, sizeof(float) * 16); memcpy(u + reflection.mvp_offset, mvp, sizeof(float) * 16);
else else
build_identity_matrix(u->MVP); build_identity_matrix(reinterpret_cast<float *>(u + reflection.mvp_offset));
build_vec4(u->output_size,
current_framebuffer_size.width, build_semantic_vec4(u, SLANG_TEXTURE_SEMANTIC_OUTPUT,
current_framebuffer_size.height); current_framebuffer_size.width, current_framebuffer_size.height);
build_vec4(u->original_size, build_semantic_vec4(u, SLANG_TEXTURE_SEMANTIC_ORIGINAL,
original.texture.width, original.texture.height); 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); source.texture.width, source.texture.height);
ubos[sync_index]->unmap(); ubos[sync_index]->unmap();
update_descriptor_set(original, source); update_descriptor_set(original, source);
@ -1074,9 +1136,9 @@ void Pass::build_commands(
framebuffer->get_image(), framebuffer->get_image(),
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_ACCESS_SHADER_READ_BIT, 0,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
VkRenderPassBeginInfo rp_info = { VkRenderPassBeginInfo rp_info = {
@ -1101,7 +1163,9 @@ void Pass::build_commands(
0, 1, &sets[sync_index], 0, nullptr); 0, 1, &sets[sync_index], 0, nullptr);
VkDeviceSize offset = 0; 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) 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