Vulkan: Add push constant support to slang.

Usually, the UBO is very small, so we can fit everything in fast-access
uniforms.
This commit is contained in:
Hans-Kristian Arntzen 2016-08-02 12:18:35 +02:00
parent 8f47e71b97
commit fb407796e9
4 changed files with 216 additions and 58 deletions

View File

@ -355,15 +355,18 @@ bool glslang::compile_spirv(const string &source, Stage stage, std::vector<uint3
const char *src = source.c_str();
shader.setStrings(&src, 1);
EShMessages messages = static_cast<EShMessages>(EShMsgDefault | EShMsgVulkanRules | EShMsgSpvRules);
string msg;
auto forbid_include = TShader::ForbidInclude();
if (!shader.preprocess(&process.GetResources(), 100, ENoProfile, false, false, EShMsgDefault, &msg, forbid_include))
if (!shader.preprocess(&process.GetResources(), 100, ENoProfile, false, false,
messages, &msg, forbid_include))
{
fprintf(stderr, "%s\n", msg.c_str());
return {};
}
if (!shader.parse(&process.GetResources(), 100, false, EShMsgDefault))
if (!shader.parse(&process.GetResources(), 100, false, messages))
{
RARCH_ERR("%s\n", shader.getInfoLog());
RARCH_ERR("%s\n", shader.getInfoDebugLog());
@ -372,7 +375,7 @@ bool glslang::compile_spirv(const string &source, Stage stage, std::vector<uint3
program.addShader(&shader);
if (!program.link(EShMsgDefault))
if (!program.link(messages))
{
RARCH_ERR("%s\n", program.getInfoLog());
RARCH_ERR("%s\n", program.getInfoDebugLog());

View File

@ -519,6 +519,13 @@ class Pass
vector<Parameter> parameters;
vector<Parameter> filtered_parameters;
struct PushConstant
{
VkShaderStageFlags stages = 0;
vector<uint32_t> buffer; // uint32_t to have correct alignment.
};
PushConstant push;
};
// struct here since we're implementing the opaque typedef from C.
@ -1394,6 +1401,25 @@ bool Pass::init_pipeline_layout()
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &set_layout;
// Push constants
VkPushConstantRange push_range = {};
if (reflection.push_constant_stage_mask && reflection.push_constant_size)
{
if (reflection.push_constant_stage_mask & SLANG_STAGE_VERTEX_MASK)
push_range.stageFlags |= VK_SHADER_STAGE_VERTEX_BIT;
if (reflection.push_constant_stage_mask & SLANG_STAGE_FRAGMENT_MASK)
push_range.stageFlags |= VK_SHADER_STAGE_FRAGMENT_BIT;
RARCH_LOG("[Vulkan]: Push Constant Block: %u bytes.\n", reflection.push_constant_size);
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = &push_range;
push.buffer.resize((reflection.push_constant_size + sizeof(uint32_t) - 1) / sizeof(uint32_t));
}
push.stages = push_range.stageFlags;
push_range.size = reflection.push_constant_size;
if (vkCreatePipelineLayout(device,
&layout_info, NULL, &pipeline_layout) != VK_SUCCESS)
return false;
@ -1738,8 +1764,11 @@ bool Pass::build()
filtered_parameters.clear();
for (unsigned i = 0; i < reflection.semantic_float_parameters.size(); i++)
{
if (reflection.semantic_float_parameters[i].uniform)
if (reflection.semantic_float_parameters[i].uniform ||
reflection.semantic_float_parameters[i].push_constant)
{
filtered_parameters.push_back(parameters[i]);
}
}
if (!init_pipeline())
@ -1804,38 +1833,52 @@ void Pass::set_semantic_texture_array(VkDescriptorSet set,
}
}
void Pass::build_semantic_texture_vec4(uint8_t *data, slang_texture_semantic semantic,
unsigned width, unsigned height)
void Pass::build_semantic_texture_array_vec4(uint8_t *data, slang_texture_semantic semantic,
unsigned index, unsigned width, unsigned height)
{
if (data && reflection.semantic_textures[semantic][0].uniform)
auto &refl = reflection.semantic_textures[semantic];
if (index >= refl.size())
return;
if (data && refl[index].uniform)
{
build_vec4(
reinterpret_cast<float *>(data + reflection.semantic_textures[semantic][0].ubo_offset),
reinterpret_cast<float *>(data + refl[index].ubo_offset),
width,
height);
}
if (refl[index].push_constant)
{
build_vec4(
reinterpret_cast<float *>(push.buffer.data() + (refl[index].push_constant_offset >> 2)),
width,
height);
}
}
void Pass::build_semantic_texture_array_vec4(uint8_t *data, slang_texture_semantic semantic,
unsigned index, unsigned width, unsigned height)
void Pass::build_semantic_texture_vec4(uint8_t *data, slang_texture_semantic semantic,
unsigned width, unsigned height)
{
if (data && index < reflection.semantic_textures[semantic].size() &&
reflection.semantic_textures[semantic][index].uniform)
{
build_vec4(
reinterpret_cast<float *>(data + reflection.semantic_textures[semantic][index].ubo_offset),
width,
height);
}
build_semantic_texture_array_vec4(data, semantic, 0, width, height);
}
void Pass::build_semantic_vec4(uint8_t *data, slang_semantic semantic,
unsigned width, unsigned height)
{
if (data && reflection.semantics[semantic].uniform)
auto &refl = reflection.semantics[semantic];
if (data && refl.uniform)
{
build_vec4(
reinterpret_cast<float *>(data + reflection.semantics[semantic].ubo_offset),
reinterpret_cast<float *>(data + refl.ubo_offset),
width,
height);
}
if (refl.push_constant)
{
build_vec4(
reinterpret_cast<float *>(push.buffer.data() + (refl.push_constant_offset >> 2)),
width,
height);
}
@ -1843,16 +1886,26 @@ void Pass::build_semantic_vec4(uint8_t *data, slang_semantic semantic,
void Pass::build_semantic_parameter(uint8_t *data, unsigned index, float value)
{
auto &refl = reflection.semantic_float_parameters[index];
// We will have filtered out stale parameters.
if (data)
*reinterpret_cast<float*>(data + reflection.semantic_float_parameters[index].ubo_offset) = value;
if (data && refl.uniform)
*reinterpret_cast<float*>(data + refl.ubo_offset) = value;
if (refl.push_constant)
*reinterpret_cast<float*>(push.buffer.data() + (refl.push_constant_offset >> 2)) = value;
}
void Pass::build_semantic_uint(uint8_t *data, slang_semantic semantic,
uint32_t value)
{
if (data && reflection.semantics[semantic].uniform)
auto &refl = reflection.semantics[semantic];
if (data && refl.uniform)
*reinterpret_cast<uint32_t*>(data + reflection.semantics[semantic].ubo_offset) = value;
if (refl.push_constant)
*reinterpret_cast<uint32_t*>(push.buffer.data() + (refl.push_constant_offset >> 2)) = value;
}
void Pass::build_semantic_texture(VkDescriptorSet set, uint8_t *buffer,
@ -1884,6 +1937,15 @@ void Pass::build_semantics(VkDescriptorSet set, uint8_t *buffer,
build_identity_matrix(reinterpret_cast<float *>(buffer + offset));
}
if (reflection.semantics[SLANG_SEMANTIC_MVP].push_constant)
{
size_t offset = reflection.semantics[SLANG_SEMANTIC_MVP].push_constant_offset;
if (mvp)
memcpy(push.buffer.data() + (offset >> 2), mvp, sizeof(float) * 16);
else
build_identity_matrix(reinterpret_cast<float *>(push.buffer.data() + (offset >> 2)));
}
// Output information
build_semantic_vec4(buffer, SLANG_SEMANTIC_OUTPUT,
current_framebuffer_size.width, current_framebuffer_size.height);
@ -2023,6 +2085,13 @@ void Pass::build_commands(
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
0, 1, &sets[sync_index], 0, nullptr);
if (push.stages != 0)
{
vkCmdPushConstants(cmd, pipeline_layout,
push.stages, 0, reflection.push_constant_size,
push.buffer.data());
}
VkDeviceSize offset = final_pass ? 16 * sizeof(float) : 0;
vkCmdBindVertexBuffers(cmd, 0, 1,
&common->vbo->get_buffer(),

View File

@ -166,77 +166,97 @@ static void resize_minimum(T &vec, unsigned minimum)
static bool set_ubo_texture_offset(slang_reflection *reflection,
slang_texture_semantic semantic, unsigned index,
size_t offset)
size_t offset, bool push_constant)
{
resize_minimum(reflection->semantic_textures[semantic], index + 1);
auto &sem = reflection->semantic_textures[semantic][index];
auto &active = push_constant ? sem.push_constant : sem.uniform;
auto &active_offset = push_constant ? sem.push_constant_offset : sem.ubo_offset;
if (sem.uniform)
if (active)
{
if (sem.ubo_offset != offset)
if (active_offset != offset)
{
RARCH_ERR("[slang]: Vertex and fragment have different offsets for same semantic %s #%u (%u vs. %u).\n",
texture_semantic_uniform_names[semantic],
index,
unsigned(sem.ubo_offset),
unsigned(active_offset),
unsigned(offset));
return false;
}
}
sem.uniform = true;
sem.ubo_offset = offset;
active = true;
active_offset = offset;
return true;
}
static bool set_ubo_float_parameter_offset(slang_reflection *reflection,
unsigned index, size_t offset, unsigned num_components)
unsigned index, size_t offset, unsigned num_components, bool push_constant)
{
resize_minimum(reflection->semantic_float_parameters, index + 1);
auto &sem = reflection->semantic_float_parameters[index];
auto &active = push_constant ? sem.push_constant : sem.uniform;
auto &active_offset = push_constant ? sem.push_constant_offset : sem.ubo_offset;
if (sem.uniform)
if (active)
{
if (sem.ubo_offset != offset)
if (active_offset != offset)
{
RARCH_ERR("[slang]: Vertex and fragment have different offsets for same parameter #%u (%u vs. %u).\n",
index,
unsigned(sem.ubo_offset),
unsigned(active_offset),
unsigned(offset));
return false;
}
}
sem.uniform = true;
sem.ubo_offset = offset;
if (sem.num_components != num_components && (sem.uniform || sem.push_constant))
{
RARCH_ERR("[slang]: Vertex and fragment have different components for same parameter #%u (%u vs. %u).\n",
index,
unsigned(sem.num_components),
unsigned(num_components));
return false;
}
active = true;
active_offset = offset;
sem.num_components = num_components;
return true;
}
static bool set_ubo_offset(slang_reflection *reflection, slang_semantic semantic,
size_t offset, unsigned num_components)
size_t offset, unsigned num_components, bool push_constant)
{
auto &sem = reflection->semantics[semantic];
auto &active = push_constant ? sem.push_constant : sem.uniform;
auto &active_offset = push_constant ? sem.push_constant_offset : sem.ubo_offset;
if (sem.uniform)
if (active)
{
if (sem.ubo_offset != offset)
if (active_offset != offset)
{
RARCH_ERR("[slang]: Vertex and fragment have different offsets for same semantic %s (%u vs. %u).\n",
semantic_uniform_names[semantic],
unsigned(sem.ubo_offset),
unsigned(active_offset),
unsigned(offset));
return false;
}
if (sem.num_components != num_components)
{
RARCH_ERR("[slang]: Vertex and fragment have different components for same semantic %s (%u vs. %u).\n",
semantic_uniform_names[semantic],
unsigned(sem.num_components),
unsigned(num_components));
}
}
sem.uniform = true;
sem.ubo_offset = offset;
if (sem.num_components != num_components && (sem.uniform || sem.push_constant))
{
RARCH_ERR("[slang]: Vertex and fragment have different components for same semantic %s (%u vs. %u).\n",
semantic_uniform_names[semantic],
unsigned(sem.num_components),
unsigned(num_components));
return false;
}
active = true;
active_offset = offset;
sem.num_components = num_components;
return true;
}
@ -276,7 +296,7 @@ static bool validate_type_for_texture_semantic(const SPIRType &type)
}
static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &resource,
slang_reflection *reflection)
slang_reflection *reflection, bool push_constant)
{
// Get which uniforms are actually in use by this shader.
auto ranges = compiler.get_active_buffer_ranges(resource.id);
@ -309,12 +329,12 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r
switch (sem)
{
case SLANG_SEMANTIC_FLOAT_PARAMETER:
if (!set_ubo_float_parameter_offset(reflection, sem_index, range.offset, type.vecsize))
if (!set_ubo_float_parameter_offset(reflection, sem_index, range.offset, type.vecsize, push_constant))
return false;
break;
default:
if (!set_ubo_offset(reflection, sem, range.offset, type.vecsize))
if (!set_ubo_offset(reflection, sem, range.offset, type.vecsize, push_constant))
return false;
break;
}
@ -327,7 +347,7 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r
return false;
}
if (!set_ubo_texture_offset(reflection, tex_sem, tex_sem_index, range.offset))
if (!set_ubo_texture_offset(reflection, tex_sem, tex_sem_index, range.offset, push_constant))
return false;
}
else
@ -350,12 +370,10 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
!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())
!fragment.atomic_counters.empty())
{
RARCH_ERR("[slang]: Invalid resource type detected.\n");
return false;
@ -403,8 +421,23 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
return false;
}
// Validate the single push constant buffer.
if (vertex.push_constant_buffers.size() > 1)
{
RARCH_ERR("[slang]: Vertex must use zero or one push constant buffers.\n");
return false;
}
if (fragment.push_constant_buffers.size() > 1)
{
RARCH_ERR("[slang]: Fragment must use zero or one push cosntant buffer.\n");
return false;
}
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;
uint32_t vertex_push = vertex.push_constant_buffers.empty() ? 0 : vertex.push_constant_buffers[0].id;
uint32_t fragment_push = fragment.push_constant_buffers.empty() ? 0 : fragment.push_constant_buffers[0].id;
if (vertex_ubo &&
vertex_compiler.get_decoration(vertex_ubo, spv::DecorationDescriptorSet) != 0)
@ -445,6 +478,8 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
reflection->ubo_binding = has_ubo ? ubo_binding : 0;
reflection->ubo_stage_mask = 0;
reflection->ubo_size = 0;
reflection->push_constant_size = 0;
reflection->push_constant_stage_mask = 0;
if (vertex_ubo)
{
@ -460,10 +495,35 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
fragment_compiler.get_declared_struct_size(fragment_compiler.get_type(fragment.uniform_buffers[0].base_type_id)));
}
// Find all relevant uniforms.
if (vertex_ubo && !add_active_buffer_ranges(vertex_compiler, vertex.uniform_buffers[0], reflection))
if (vertex_push)
{
reflection->push_constant_stage_mask |= SLANG_STAGE_VERTEX_MASK;
reflection->push_constant_size = max(reflection->push_constant_size,
vertex_compiler.get_declared_struct_size(vertex_compiler.get_type(vertex.push_constant_buffers[0].base_type_id)));
}
if (fragment_push)
{
reflection->push_constant_stage_mask |= SLANG_STAGE_FRAGMENT_MASK;
reflection->push_constant_size = max(reflection->push_constant_size,
fragment_compiler.get_declared_struct_size(fragment_compiler.get_type(fragment.push_constant_buffers[0].base_type_id)));
}
// Validate push constant size against Vulkan's minimum spec to avoid cross-vendor issues.
if (reflection->push_constant_size > 128)
{
RARCH_ERR("[slang]: Exceeded maximum size of 128 bytes for push constant buffer.\n");
return false;
if (fragment_ubo && !add_active_buffer_ranges(fragment_compiler, fragment.uniform_buffers[0], reflection))
}
// Find all relevant uniforms and push constants.
if (vertex_ubo && !add_active_buffer_ranges(vertex_compiler, vertex.uniform_buffers[0], reflection, false))
return false;
if (fragment_ubo && !add_active_buffer_ranges(fragment_compiler, fragment.uniform_buffers[0], reflection, false))
return false;
if (vertex_push && !add_active_buffer_ranges(vertex_compiler, vertex.push_constant_buffers[0], reflection, true))
return false;
if (fragment_push && !add_active_buffer_ranges(fragment_compiler, fragment.push_constant_buffers[0], reflection, true))
return false;
uint32_t binding_mask = has_ubo ? (1 << ubo_binding) : 0;
@ -529,6 +589,10 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
RARCH_LOG("[slang]: Uniforms (Vertex: %s, Fragment: %s):\n",
reflection->ubo_stage_mask & SLANG_STAGE_VERTEX_MASK ? "yes": "no",
reflection->ubo_stage_mask & SLANG_STAGE_FRAGMENT_MASK ? "yes": "no");
RARCH_LOG("[slang]: Push Constants (Vertex: %s, Fragment: %s):\n",
reflection->push_constant_stage_mask & SLANG_STAGE_VERTEX_MASK ? "yes": "no",
reflection->push_constant_stage_mask & SLANG_STAGE_FRAGMENT_MASK ? "yes": "no");
for (unsigned i = 0; i < SLANG_NUM_SEMANTICS; i++)
{
if (reflection->semantics[i].uniform)
@ -536,6 +600,12 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
RARCH_LOG("[slang]: %s (Offset: %u)\n", semantic_uniform_names[i],
unsigned(reflection->semantics[i].ubo_offset));
}
if (reflection->semantics[i].push_constant)
{
RARCH_LOG("[slang]: %s (PushOffset: %u)\n", semantic_uniform_names[i],
unsigned(reflection->semantics[i].push_constant_offset));
}
}
for (unsigned i = 0; i < SLANG_NUM_TEXTURE_SEMANTICS; i++)
@ -549,6 +619,13 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
index,
unsigned(sem.ubo_offset));
}
if (sem.push_constant)
{
RARCH_LOG("[slang]: %s (#%u) (PushOffset: %u)\n", texture_semantic_uniform_names[i],
index,
unsigned(sem.push_constant_offset));
}
index++;
}
}
@ -560,6 +637,8 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
{
if (param.uniform)
RARCH_LOG("[slang]: #%u (Offset: %u)\n", i, param.ubo_offset);
if (param.push_constant)
RARCH_LOG("[slang]: #%u (PushOffset: %u)\n", i, param.push_constant_offset);
i++;
}

View File

@ -86,18 +86,22 @@ enum slang_stage
struct slang_texture_semantic_meta
{
size_t ubo_offset = 0;
size_t push_constant_offset = 0;
unsigned binding = 0;
uint32_t stage_mask = 0;
bool texture = false;
bool uniform = false;
bool push_constant = false;
};
struct slang_semantic_meta
{
size_t ubo_offset = 0;
size_t push_constant_offset = 0;
unsigned num_components = 0;
bool uniform = false;
bool push_constant = false;
};
struct slang_texture_semantic_map
@ -117,8 +121,11 @@ struct slang_reflection
slang_reflection();
size_t ubo_size = 0;
size_t push_constant_size = 0;
unsigned ubo_binding = 0;
uint32_t ubo_stage_mask = 0;
uint32_t push_constant_stage_mask = 0;
std::vector<slang_texture_semantic_meta> semantic_textures[SLANG_NUM_TEXTURE_SEMANTICS];
slang_semantic_meta semantics[SLANG_NUM_SEMANTICS];