mirror of
https://github.com/libretro/RetroArch
synced 2025-04-07 13:23:32 +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"]
|
[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
|
||||||
|
@ -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
|
||||||
|
@ -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
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,
|
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(®ion, 0, sizeof(region));
|
memset(®ion, 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(®ion, 0, sizeof(region));
|
memset(®ion, 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);
|
||||||
|
@ -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(©_frame);
|
retro_perf_start(©_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);
|
||||||
|
@ -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();
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
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