Vulkan: Clean up semantic parsing a lot.

Split up into two kinds of semantics, textured and not.
This commit is contained in:
Hans-Kristian Arntzen 2016-03-22 13:42:47 +01:00
parent 2323cef8d1
commit 39149b36b2
3 changed files with 260 additions and 145 deletions

View File

@ -286,10 +286,6 @@ class Pass
VkAccessFlags srcAccess, VkAccessFlags dstAccess,
VkPipelineStageFlags srcStages, VkPipelineStageFlags dstStages);
void update_descriptor_set(
const Texture &original,
const Texture &source);
void set_texture(VkDescriptorSet set, unsigned binding,
const Texture &texture);
@ -302,8 +298,15 @@ class Pass
VkDeviceSize range);
slang_reflection reflection;
void build_semantic_vec4(uint8_t *data, slang_texture_semantic semantic,
void build_semantics(VkDescriptorSet set, uint8_t *buffer,
const float *mvp, const Texture &original, const Texture &source);
void build_semantic_vec4(uint8_t *data, slang_semantic semantic,
unsigned width, unsigned height);
void build_semantic_texture_vec4(uint8_t *data,
slang_texture_semantic semantic,
unsigned width, unsigned height);
void build_semantic_texture(VkDescriptorSet set, uint8_t *buffer,
slang_texture_semantic semantic, const Texture &texture);
};
// struct here since we're implementing the opaque typedef from C.
@ -702,7 +705,7 @@ bool Pass::init_pipeline_layout()
desc_counts.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, num_sync_indices });
// Semantic textures.
for (unsigned i = 0; i < 32; i++)
for (unsigned i = 0; i < SLANG_NUM_TEXTURE_SEMANTICS; i++)
{
if (reflection.semantic_texture_mask & (1u << i))
{
@ -1064,18 +1067,8 @@ void Pass::set_semantic_texture(VkDescriptorSet set,
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, 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)
void Pass::build_semantic_texture_vec4(uint8_t *data, slang_texture_semantic semantic,
unsigned width, unsigned height)
{
if (reflection.semantic_texture_ubo_mask & (1 << semantic))
{
@ -1086,6 +1079,46 @@ void Pass::build_semantic_vec4(uint8_t *data, slang_texture_semantic semantic, u
}
}
void Pass::build_semantic_vec4(uint8_t *data, slang_semantic semantic,
unsigned width, unsigned height)
{
if (reflection.semantic_ubo_mask & (1 << semantic))
{
build_vec4(
reinterpret_cast<float *>(data + reflection.semantics[semantic].ubo_offset),
width,
height);
}
}
void Pass::build_semantic_texture(VkDescriptorSet set, uint8_t *buffer,
slang_texture_semantic semantic, const Texture &texture)
{
build_semantic_texture_vec4(buffer, semantic,
texture.texture.width, texture.texture.height);
set_semantic_texture(set, semantic, texture);
}
void Pass::build_semantics(VkDescriptorSet set, uint8_t *buffer,
const float *mvp, const Texture &original, const Texture &source)
{
if (reflection.semantic_ubo_mask & (1u << SLANG_SEMANTIC_MVP))
{
size_t offset = reflection.semantics[SLANG_SEMANTIC_MVP].ubo_offset;
if (mvp)
memcpy(buffer + offset, mvp, sizeof(float) * 16);
else
build_identity_matrix(reinterpret_cast<float *>(buffer + offset));
}
build_semantic_vec4(buffer, SLANG_SEMANTIC_OUTPUT,
current_framebuffer_size.width, current_framebuffer_size.height);
build_semantic_vec4(buffer, SLANG_SEMANTIC_FINAL_VIEWPORT,
unsigned(current_viewport.width), unsigned(current_viewport.height));
build_semantic_texture(set, buffer, SLANG_TEXTURE_SEMANTIC_ORIGINAL, original);
build_semantic_texture(set, buffer, SLANG_TEXTURE_SEMANTIC_SOURCE, source);
}
void Pass::build_commands(
DeferredDisposer &disposer,
VkCommandBuffer cmd,
@ -1108,22 +1141,11 @@ void Pass::build_commands(
}
uint8_t *u = static_cast<uint8_t*>(ubos[sync_index]->map());
if (mvp)
memcpy(u + reflection.mvp_offset, mvp, sizeof(float) * 16);
else
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_semantic_vec4(u, SLANG_TEXTURE_SEMANTIC_SOURCE,
source.texture.width, source.texture.height);
build_semantics(sets[sync_index], u, mvp, original, source);
ubos[sync_index]->unmap();
update_descriptor_set(original, source);
set_uniform_buffer(sets[sync_index], 0,
ubos[sync_index]->get_buffer(), 0, reflection.ubo_size);
// The final pass is always executed inside
// another render pass since the frontend will

View File

@ -22,99 +22,161 @@
using namespace std;
using namespace spir2cross;
static slang_texture_semantic slang_name_to_semantic(const string &name)
static slang_texture_semantic slang_name_to_texture_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 const char *names[] = {
"Original",
"Source",
};
unsigned i = 0;
for (auto n : names)
{
if (name == n)
return static_cast<slang_texture_semantic>(i);
i++;
}
return SLANG_INVALID_TEXTURE_SEMANTIC;
}
static bool find_uniform_offset(const Compiler &compiler, const Resource &resource, const char *name,
size_t *offset, unsigned *member_index)
static slang_texture_semantic slang_uniform_name_to_texture_semantic(const string &name)
{
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++)
static const char *names[] = {
"OriginalSize",
"SourceSize",
};
unsigned i = 0;
for (auto n : names)
{
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;
}
if (name == n)
return static_cast<slang_texture_semantic>(i);
i++;
}
return false;
return SLANG_INVALID_TEXTURE_SEMANTIC;
}
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)
static slang_semantic slang_uniform_name_to_semantic(const string &name)
{
unsigned member_index = 0;
auto &semantic = reflection->semantic_textures[index];
static const char *names[] = {
"MVP",
"OutputSize",
"FinalViewportSize",
};
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))
unsigned i = 0;
for (auto n : names)
{
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 (name == n)
return static_cast<slang_semantic>(i);
i++;
}
if (!fragment_resources.empty() &&
find_uniform_offset(fragment_compiler, fragment_resources[0], name,
&fragment_ubo_offset, &member_index))
return SLANG_INVALID_SEMANTIC;
}
static bool set_ubo_texture_offset(slang_reflection *reflection, slang_texture_semantic semantic,
size_t offset)
{
if (reflection->semantic_texture_ubo_mask & (1u << semantic))
{
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)
if (reflection->semantic_textures[semantic].ubo_offset != offset)
{
RARCH_ERR("[slang]: Semantic uniform is not vec4.\n");
RARCH_ERR("[slang]: Vertex and fragment have different offsets for same semantic #%u (%u vs. %u).\n",
unsigned(semantic),
unsigned(reflection->semantic_textures[semantic].ubo_offset),
unsigned(offset));
return false;
}
reflection->semantic_texture_ubo_mask |= 1 << index;
}
reflection->semantic_texture_ubo_mask |= 1u << semantic;
reflection->semantic_textures[semantic].ubo_offset = offset;
return true;
}
// 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)
static bool set_ubo_offset(slang_reflection *reflection, slang_semantic semantic, size_t offset)
{
if (reflection->semantic_ubo_mask & (1u << semantic))
{
RARCH_ERR("[slang]: Vertex (%u) and fragment (%u) UBO offset for %s mismatches.\n",
unsigned(vertex_ubo_offset), unsigned(fragment_ubo_offset), name);
if (reflection->semantics[semantic].ubo_offset != offset)
{
RARCH_ERR("[slang]: Vertex and fragment have different offsets for same semantic #%u (%u vs. %u).\n",
unsigned(semantic),
unsigned(reflection->semantics[semantic].ubo_offset),
unsigned(offset));
return false;
}
}
reflection->semantic_ubo_mask |= 1u << semantic;
reflection->semantics[semantic].ubo_offset = offset;
return true;
}
static bool validate_type_for_semantic(const SPIRType &type, slang_semantic sem)
{
if (!type.array.empty())
return false;
if (type.basetype != SPIRType::Float && type.basetype != SPIRType::Int && type.basetype != SPIRType::UInt)
return false;
}
semantic.ubo_offset = vertex_ubo_offset;
switch (sem)
{
case SLANG_SEMANTIC_MVP:
// mat4
return type.basetype == SPIRType::Float && type.vecsize == 4 && type.columns == 4;
default:
// vec4
return type.basetype == SPIRType::Float && type.vecsize == 4 && type.columns == 1;
}
}
static bool validate_type_for_texture_semantic(const SPIRType &type)
{
if (!type.array.empty())
return false;
return type.basetype == SPIRType::Float && type.vecsize == 4 && type.columns == 1;
}
static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &resource,
slang_reflection *reflection)
{
// Get which uniforms are actually in use by this shader.
auto ranges = compiler.get_active_buffer_ranges(resource.id);
for (auto &range : ranges)
{
auto &name = compiler.get_member_name(resource.type_id, range.index);
auto &type = compiler.get_type(compiler.get_type(resource.type_id).member_types[range.index]);
slang_semantic sem = slang_uniform_name_to_semantic(name);
slang_texture_semantic tex_sem = slang_uniform_name_to_texture_semantic(name);
if (sem != SLANG_INVALID_SEMANTIC)
{
if (!validate_type_for_semantic(type, sem))
{
RARCH_ERR("[slang]: Underlying type of semantic is invalid.\n");
return false;
}
if (!set_ubo_offset(reflection, sem, range.offset))
return false;
}
else if (tex_sem != SLANG_INVALID_TEXTURE_SEMANTIC)
{
if (!validate_type_for_texture_semantic(type))
{
RARCH_ERR("[slang]: Underlying type of texture semantic is invalid.\n");
return false;
}
if (!set_ubo_texture_offset(reflection, tex_sem, range.offset))
return false;
}
else
{
// TODO: Handle invalid semantics as user defined.
}
}
return true;
}
@ -147,6 +209,18 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
return false;
}
if (fragment.stage_outputs.size() != 1)
{
RARCH_ERR("[slang]: Multiple render targets not supported.\n");
return false;
}
if (fragment_compiler.get_decoration(fragment.stage_outputs[0].id, spv::DecorationLocation) != 0)
{
RARCH_ERR("[slang]: Render target must use location = 0.\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);
@ -158,9 +232,9 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
}
// Validate the single uniform buffer.
if (vertex.uniform_buffers.size() != 1)
if (vertex.uniform_buffers.size() > 1)
{
RARCH_ERR("[slang]: Vertex must use exactly one uniform buffer.\n");
RARCH_ERR("[slang]: Vertex must use zero or one uniform buffer.\n");
return false;
}
@ -170,28 +244,44 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
return false;
}
if (vertex_compiler.get_decoration(vertex.uniform_buffers[0].id, spv::DecorationDescriptorSet) != 0)
uint32_t vertex_ubo = vertex.uniform_buffers.empty() ? 0 : vertex.uniform_buffers[0].id;
uint32_t fragment_ubo = fragment.uniform_buffers.empty() ? 0 : fragment.uniform_buffers[0].id;
if (vertex_ubo &&
vertex_compiler.get_decoration(vertex_ubo, 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)
if (fragment_ubo &&
fragment_compiler.get_decoration(fragment_ubo, 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))
if (!vertex_ubo && !fragment_ubo)
{
RARCH_ERR("[slang]: No UBO is in use. This cannot be a correct shader.\n");
return false;
}
unsigned vertex_ubo_binding = vertex_ubo ?
vertex_compiler.get_decoration(vertex_ubo, spv::DecorationBinding) : -1u;
unsigned fragment_ubo_binding = fragment_ubo ?
fragment_compiler.get_decoration(fragment_ubo, spv::DecorationBinding) : -1u;
if (vertex_ubo_binding != -1u &&
fragment_ubo_binding != -1u &&
vertex_ubo_binding != fragment_ubo_binding)
{
RARCH_ERR("[slang]: Vertex and fragment uniform buffer must have same binding.\n");
return false;
}
unsigned ubo_binding = vertex_ubo_binding != -1u ? vertex_ubo_binding : fragment_ubo_binding;
if (ubo_binding >= SLANG_NUM_BINDINGS)
{
RARCH_ERR("[slang]: Binding %u is out of range.\n", ubo_binding);
@ -199,33 +289,28 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
}
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));
reflection->ubo_stage_mask = 0;
reflection->ubo_size = 0;
if (!fragment.uniform_buffers.empty())
if (vertex_ubo)
{
reflection->ubo_stage_mask |= SLANG_STAGE_VERTEX_MASK;
reflection->ubo_size = max(reflection->ubo_size,
vertex_compiler.get_declared_struct_size(vertex_compiler.get_type(vertex.uniform_buffers[0].type_id)));
}
if (fragment_ubo)
{
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");
// Find all relevant uniforms.
if (vertex_ubo && !add_active_buffer_ranges(vertex_compiler, vertex.uniform_buffers[0], reflection))
return false;
}
if (!find_semantic_uniform(reflection, SLANG_TEXTURE_SEMANTIC_OUTPUT,
vertex_compiler, vertex.uniform_buffers,
fragment_compiler, fragment.uniform_buffers,
"OutputSize"))
{
if (fragment_ubo && !add_active_buffer_ranges(fragment_compiler, fragment.uniform_buffers[0], reflection))
return false;
}
////
uint32_t binding_mask = 1 << ubo_binding;
@ -256,11 +341,11 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
}
binding_mask |= 1 << binding;
slang_texture_semantic index = slang_name_to_semantic(texture.name);
slang_texture_semantic index = slang_name_to_texture_semantic(texture.name);
if (index == SLANG_TEXTURE_INVALID_SEMANTIC)
if (index == SLANG_INVALID_TEXTURE_SEMANTIC)
{
RARCH_ERR("[slang]: Non-semantic textures not supported.\n");
RARCH_ERR("[slang]: Non-semantic textures not supported yet.\n");
return false;
}
@ -268,13 +353,6 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
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;

View File

@ -24,10 +24,19 @@ 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
SLANG_NUM_TEXTURE_SEMANTICS,
SLANG_INVALID_TEXTURE_SEMANTIC = -1
};
enum slang_semantic
{
SLANG_SEMANTIC_MVP = 0,
SLANG_SEMANTIC_OUTPUT = 1,
SLANG_SEMANTIC_FINAL_VIEWPORT = 2,
SLANG_NUM_SEMANTICS,
SLANG_INVALID_SEMANTIC = -1
};
enum slang_stage
@ -44,16 +53,22 @@ struct slang_texture_semantic_meta
uint32_t stage_mask = 0;
};
struct slang_semantic_meta
{
size_t ubo_offset = 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];
slang_texture_semantic_meta semantic_textures[SLANG_NUM_TEXTURE_SEMANTICS];
slang_semantic_meta semantics[SLANG_NUM_SEMANTICS];
uint32_t semantic_texture_mask = 0;
uint32_t semantic_texture_ubo_mask = 0;
uint32_t semantic_ubo_mask = 0;
};
bool slang_reflect_spirv(const std::vector<uint32_t> &vertex,