mirror of
https://github.com/CTCaer/RetroArch.git
synced 2024-12-15 06:50:32 +00:00
Vulkan: Begin hooking up SPIR-V reflection.
This commit is contained in:
parent
72cdd37ad2
commit
1822f3bf90
@ -771,6 +771,7 @@ ifeq ($(HAVE_VULKAN), 1)
|
||||
gfx/drivers_font/vulkan_raster_font.o \
|
||||
gfx/drivers_shader/shader_vulkan.o \
|
||||
gfx/drivers_shader/glslang_util.o \
|
||||
gfx/drivers_shader/slang_reflection.o \
|
||||
$(GLSLANG_OBJ) \
|
||||
$(SPIR2CROSS_OBJ)
|
||||
ifeq ($(HAVE_MENU_COMMON), 1)
|
||||
|
@ -25,10 +25,9 @@
|
||||
#include "../drivers/vulkan_shaders/opaque.frag.inc"
|
||||
#include "../video_shader_driver.h"
|
||||
#include "../../verbosity.h"
|
||||
#include "spir2cross.hpp"
|
||||
#include "slang_reflection.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace spir2cross;
|
||||
|
||||
static uint32_t find_memory_type(
|
||||
const VkPhysicalDeviceMemoryProperties &mem_props,
|
||||
@ -248,14 +247,6 @@ class Pass
|
||||
}
|
||||
|
||||
private:
|
||||
struct UBO
|
||||
{
|
||||
float MVP[16];
|
||||
float output_size[4];
|
||||
float original_size[4];
|
||||
float source_size[4];
|
||||
};
|
||||
|
||||
VkDevice device;
|
||||
const VkPhysicalDeviceMemoryProperties &memory_properties;
|
||||
VkPipelineCache cache;
|
||||
@ -306,6 +297,10 @@ class Pass
|
||||
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.
|
||||
@ -952,7 +947,7 @@ bool Pass::init_buffers()
|
||||
ubos.clear();
|
||||
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));
|
||||
memory_properties, reflection.ubo_size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -969,6 +964,10 @@ bool Pass::build()
|
||||
if (!init_pipeline())
|
||||
return false;
|
||||
|
||||
memset(&reflection, 0, sizeof(reflection));
|
||||
if (!slang_reflect_spirv(vertex_shader, fragment_shader, &reflection))
|
||||
return false;
|
||||
|
||||
if (!init_buffers())
|
||||
return false;
|
||||
|
||||
@ -1046,11 +1045,22 @@ void Pass::update_descriptor_set(
|
||||
const Texture &source)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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(
|
||||
DeferredDisposer &disposer,
|
||||
VkCommandBuffer cmd,
|
||||
@ -1072,19 +1082,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);
|
||||
|
242
gfx/drivers_shader/slang_reflection.cpp
Normal file
242
gfx/drivers_shader/slang_reflection.cpp
Normal file
@ -0,0 +1,242 @@
|
||||
/* 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 slang_reflect(const Compiler &vertex_compiler, const Compiler &fragment_compiler,
|
||||
const ShaderResources &vertex, const ShaderResources &fragment,
|
||||
slang_reflection *reflection)
|
||||
{
|
||||
unsigned member_index = 0;
|
||||
|
||||
// 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("Invalid resource type detected.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate vertex input.
|
||||
if (vertex.stage_inputs.size() != 2)
|
||||
{
|
||||
RARCH_ERR("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("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("Vertex must use exactly one uniform buffer.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fragment.uniform_buffers.size() > 1)
|
||||
{
|
||||
RARCH_ERR("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("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("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("Vertex and fragment uniform buffer must have same binding.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ubo_binding >= SLANG_NUM_BINDINGS)
|
||||
{
|
||||
RARCH_ERR("Binding %u is out of range.\n", ubo_binding);
|
||||
return false;
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
if (!find_uniform_offset(vertex_compiler, vertex.uniform_buffers[0], "MVP", &reflection->mvp_offset, &member_index))
|
||||
{
|
||||
RARCH_ERR("Could not find offset for MVP matrix.\n");
|
||||
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("Resources must use descriptor set #0.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (binding >= SLANG_NUM_BINDINGS)
|
||||
{
|
||||
RARCH_ERR("Binding %u is out of range.\n", ubo_binding);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (binding_mask & (1 << binding))
|
||||
{
|
||||
RARCH_ERR("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("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;
|
||||
|
||||
// TODO: Do we want to expose Size uniforms if no stage is using the texture?
|
||||
char uniform_name[128];
|
||||
snprintf(uniform_name, sizeof(uniform_name), "%sSize", texture.name.c_str());
|
||||
if (find_uniform_offset(fragment_compiler, fragment.uniform_buffers[0], uniform_name,
|
||||
&semantic.ubo_offset, &member_index))
|
||||
{
|
||||
auto &type = fragment_compiler.get_type(
|
||||
fragment_compiler.get_type(fragment.uniform_buffers[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("Semantic uniform is not vec4.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
reflection->semantic_texture_ubo_mask |= 1 << index;
|
||||
}
|
||||
}
|
||||
|
||||
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("Failed to reflect SPIR-V. Resource usage is inconsistent with expectations.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
RARCH_ERR("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…
Reference in New Issue
Block a user