mirror of
https://github.com/libretro/RetroArch
synced 2025-03-29 22:20:21 +00:00
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:
commit
dfeef5b78d
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
1
deps/spir2cross
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 0ae2bcc3d0edc60e03180f6080a168f78edc82ca
|
@ -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(®ion, 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(®ion, 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);
|
||||
|
@ -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(©_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);
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
{
|
||||
|
310
gfx/drivers_shader/slang_reflection.cpp
Normal file
310
gfx/drivers_shader/slang_reflection.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
64
gfx/drivers_shader/slang_reflection.hpp
Normal file
64
gfx/drivers_shader/slang_reflection.hpp
Normal 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user