Merge pull request #3316 from Themaister/master

Vulkan: Add shader parameters
This commit is contained in:
Twinaphex 2016-08-01 22:16:33 +02:00 committed by GitHub
commit 1ec84ca44d
7 changed files with 293 additions and 42 deletions

View File

@ -16,6 +16,7 @@
#include <stdio.h> #include <stdio.h>
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <algorithm>
#include <streams/file_stream.h> #include <streams/file_stream.h>
#include <lists/string_list.h> #include <lists/string_list.h>
@ -252,6 +253,9 @@ static glslang_format glslang_find_format(const char *fmt)
static bool glslang_parse_meta(const vector<string> &lines, glslang_meta *meta) static bool glslang_parse_meta(const vector<string> &lines, glslang_meta *meta)
{ {
char id[64] = {};
char desc[64] = {};
*meta = glslang_meta{}; *meta = glslang_meta{};
for (auto &line : lines) for (auto &line : lines)
{ {
@ -268,6 +272,46 @@ static bool glslang_parse_meta(const vector<string> &lines, glslang_meta *meta)
str++; str++;
meta->name = str; meta->name = str;
} }
else if (line.find("#pragma parameter ") == 0)
{
float initial, minimum, maximum, step;
int ret = sscanf(line.c_str(), "#pragma parameter %63s \"%63[^\"]\" %f %f %f %f",
id, desc, &initial, &minimum, &maximum, &step);
if (ret == 5)
{
step = 0.1f * (maximum - minimum);
ret = 6;
}
if (ret == 6)
{
auto itr = find_if(begin(meta->parameters), end(meta->parameters), [&](const glslang_parameter &param) {
return param.id == id;
});
// Allow duplicate #pragma parameter, but only if they are exactly the same.
if (itr != end(meta->parameters))
{
if (itr->desc != desc ||
itr->initial != initial ||
itr->minimum != minimum ||
itr->maximum != maximum ||
itr->step != step)
{
RARCH_ERR("[slang]: Duplicate parameters found for \"%s\", but arguments do not match.\n", id);
return false;
}
}
else
meta->parameters.push_back({ id, desc, initial, minimum, maximum, step });
}
else
{
RARCH_ERR("[slang]: Invalid #pragma parameter line: \"%s\".\n", line.c_str());
return false;
}
}
else if (line.find("#pragma format ") == 0) else if (line.find("#pragma format ") == 0)
{ {
if (meta->rt_format != SLANG_FORMAT_UNKNOWN) if (meta->rt_format != SLANG_FORMAT_UNKNOWN)

View File

@ -63,8 +63,19 @@ enum glslang_format
SLANG_FORMAT_UNKNOWN SLANG_FORMAT_UNKNOWN
}; };
struct glslang_parameter
{
std::string id;
std::string desc;
float initial;
float minimum;
float maximum;
float step;
};
struct glslang_meta struct glslang_meta
{ {
std::vector<glslang_parameter> parameters;
std::string name; std::string name;
glslang_format rt_format = SLANG_FORMAT_UNKNOWN; glslang_format rt_format = SLANG_FORMAT_UNKNOWN;
}; };

View File

@ -19,6 +19,7 @@
#include <memory> #include <memory>
#include <functional> #include <functional>
#include <utility> #include <utility>
#include <algorithm>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
@ -323,7 +324,7 @@ struct CommonResources
unordered_map<string, slang_texture_semantic_map> texture_semantic_map; unordered_map<string, slang_texture_semantic_map> texture_semantic_map;
unordered_map<string, slang_texture_semantic_map> texture_semantic_uniform_map; unordered_map<string, slang_texture_semantic_map> texture_semantic_uniform_map;
unordered_map<string, slang_semantic> semantic_map; unique_ptr<video_shader> shader_preset;
VkDevice device; VkDevice device;
}; };
@ -432,6 +433,8 @@ class Pass
pass_number = pass; pass_number = pass;
} }
void add_parameter(unsigned parameter_index, const std::string &id);
void end_frame(); void end_frame();
void allocate_buffers(); void allocate_buffers();
@ -488,6 +491,7 @@ class Pass
void build_semantic_vec4(uint8_t *data, slang_semantic semantic, void build_semantic_vec4(uint8_t *data, slang_semantic semantic,
unsigned width, unsigned height); unsigned width, unsigned height);
void build_semantic_uint(uint8_t *data, slang_semantic semantic, uint32_t value); void build_semantic_uint(uint8_t *data, slang_semantic semantic, uint32_t value);
void build_semantic_parameter(uint8_t *data, unsigned index, float value);
void build_semantic_texture_vec4(uint8_t *data, void build_semantic_texture_vec4(uint8_t *data,
slang_texture_semantic semantic, slang_texture_semantic semantic,
unsigned width, unsigned height); unsigned width, unsigned height);
@ -505,6 +509,16 @@ class Pass
size_t ubo_offset = 0; size_t ubo_offset = 0;
string pass_name; string pass_name;
struct Parameter
{
string id;
unsigned index;
unsigned semantic_index;
};
vector<Parameter> parameters;
vector<Parameter> filtered_parameters;
}; };
// struct here since we're implementing the opaque typedef from C. // struct here since we're implementing the opaque typedef from C.
@ -516,12 +530,12 @@ struct vulkan_filter_chain
inline void set_shader_preset(unique_ptr<video_shader> shader) inline void set_shader_preset(unique_ptr<video_shader> shader)
{ {
shader_preset = move(shader); common.shader_preset = move(shader);
} }
inline video_shader *get_shader_preset() inline video_shader *get_shader_preset()
{ {
return shader_preset.get(); return common.shader_preset.get();
} }
void set_pass_info(unsigned pass, void set_pass_info(unsigned pass,
@ -544,6 +558,7 @@ struct vulkan_filter_chain
void set_pass_name(unsigned pass, const char *name); void set_pass_name(unsigned pass, const char *name);
void add_static_texture(unique_ptr<StaticTexture> texture); void add_static_texture(unique_ptr<StaticTexture> texture);
void add_parameter(unsigned pass, unsigned parameter_index, const std::string &id);
void release_staging_buffers(); void release_staging_buffers();
private: private:
@ -563,8 +578,6 @@ struct vulkan_filter_chain
vulkan_filter_chain_swapchain_info swapchain_info; vulkan_filter_chain_swapchain_info swapchain_info;
unsigned current_sync_index; unsigned current_sync_index;
unique_ptr<video_shader> shader_preset;
void flush(); void flush();
void set_num_passes(unsigned passes); void set_num_passes(unsigned passes);
@ -610,6 +623,11 @@ void vulkan_filter_chain::set_swapchain_info(
set_num_sync_indices(info.num_indices); set_num_sync_indices(info.num_indices);
} }
void vulkan_filter_chain::add_parameter(unsigned pass, unsigned index, const std::string &id)
{
passes[pass]->add_parameter(index, id);
}
void vulkan_filter_chain::set_num_sync_indices(unsigned num_indices) void vulkan_filter_chain::set_num_sync_indices(unsigned num_indices)
{ {
execute_deferred(); execute_deferred();
@ -852,7 +870,6 @@ bool vulkan_filter_chain::init_alias()
{ {
common.texture_semantic_map.clear(); common.texture_semantic_map.clear();
common.texture_semantic_uniform_map.clear(); common.texture_semantic_uniform_map.clear();
common.semantic_map.clear();
unsigned i = 0; unsigned i = 0;
for (auto &pass : passes) for (auto &pass : passes)
@ -1213,6 +1230,11 @@ Pass::~Pass()
clear_vk(); clear_vk();
} }
void Pass::add_parameter(unsigned index, const std::string &id)
{
parameters.push_back({ id, index, unsigned(parameters.size()) });
}
void Pass::set_shader(VkShaderStageFlags stage, void Pass::set_shader(VkShaderStageFlags stage,
const uint32_t *spirv, const uint32_t *spirv,
size_t spirv_words) size_t spirv_words)
@ -1693,15 +1715,33 @@ bool Pass::build()
pass_info.rt_format, pass_info.max_levels)); pass_info.rt_format, pass_info.max_levels));
} }
unordered_map<string, slang_semantic_map> semantic_map;
unsigned j = 0;
for (auto &param : parameters)
{
if (!set_unique_map(semantic_map, param.id,
slang_semantic_map{ SLANG_SEMANTIC_FLOAT_PARAMETER, j }))
return false;
j++;
}
reflection = slang_reflection{}; reflection = slang_reflection{};
reflection.pass_number = pass_number; reflection.pass_number = pass_number;
reflection.texture_semantic_map = &common->texture_semantic_map; reflection.texture_semantic_map = &common->texture_semantic_map;
reflection.texture_semantic_uniform_map = &common->texture_semantic_uniform_map; reflection.texture_semantic_uniform_map = &common->texture_semantic_uniform_map;
reflection.semantic_map = &common->semantic_map; reflection.semantic_map = &semantic_map;
if (!slang_reflect_spirv(vertex_shader, fragment_shader, &reflection)) if (!slang_reflect_spirv(vertex_shader, fragment_shader, &reflection))
return false; return false;
// Filter out parameters which we will never use anyways.
filtered_parameters.clear();
for (unsigned i = 0; i < reflection.semantic_float_parameters.size(); i++)
{
if (reflection.semantic_float_parameters[i].uniform)
filtered_parameters.push_back(parameters[i]);
}
if (!init_pipeline()) if (!init_pipeline())
return false; return false;
@ -1801,6 +1841,13 @@ void Pass::build_semantic_vec4(uint8_t *data, slang_semantic semantic,
} }
} }
void Pass::build_semantic_parameter(uint8_t *data, unsigned index, float value)
{
// We will have filtered out stale parameters.
if (data)
*reinterpret_cast<float*>(data + reflection.semantic_float_parameters[index].ubo_offset) = value;
}
void Pass::build_semantic_uint(uint8_t *data, slang_semantic semantic, void Pass::build_semantic_uint(uint8_t *data, slang_semantic semantic,
uint32_t value) uint32_t value)
{ {
@ -1853,6 +1900,13 @@ void Pass::build_semantics(VkDescriptorSet set, uint8_t *buffer,
// ORIGINAL_HISTORY[0] is an alias of ORIGINAL. // ORIGINAL_HISTORY[0] is an alias of ORIGINAL.
build_semantic_texture_array(set, buffer, SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY, 0, original); build_semantic_texture_array(set, buffer, SLANG_TEXTURE_SEMANTIC_ORIGINAL_HISTORY, 0, original);
// Parameters.
for (auto &param : filtered_parameters)
{
float value = common->shader_preset->parameters[param.index].current;
build_semantic_parameter(buffer, param.semantic_index, value);
}
// Previous inputs. // Previous inputs.
unsigned i = 0; unsigned i = 0;
for (auto &texture : common->original_history) for (auto &texture : common->original_history)
@ -2736,7 +2790,6 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset(
return nullptr; return nullptr;
video_shader_resolve_relative(shader.get(), path); video_shader_resolve_relative(shader.get(), path);
video_shader_resolve_parameters(conf.get(), shader.get());
bool last_pass_is_fbo = shader->pass[shader->passes - 1].fbo.valid; bool last_pass_is_fbo = shader->pass[shader->passes - 1].fbo.valid;
auto tmpinfo = *info; auto tmpinfo = *info;
@ -2749,6 +2802,8 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset(
if (shader->luts && !vulkan_filter_chain_load_luts(info, chain.get(), shader.get())) if (shader->luts && !vulkan_filter_chain_load_luts(info, chain.get(), shader.get()))
return nullptr; return nullptr;
shader->num_parameters = 0;
for (unsigned i = 0; i < shader->passes; i++) for (unsigned i = 0; i < shader->passes; i++)
{ {
const video_shader_pass *pass = &shader->pass[i]; const video_shader_pass *pass = &shader->pass[i];
@ -2765,6 +2820,49 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset(
return nullptr; return nullptr;
} }
for (auto &meta_param : output.meta.parameters)
{
if (shader->num_parameters >= GFX_MAX_PARAMETERS)
{
RARCH_ERR("[Vulkan]: Exceeded maximum number of parameters.\n");
return nullptr;
}
auto itr = find_if(shader->parameters, shader->parameters + shader->num_parameters,
[&](const video_shader_parameter &param) {
return meta_param.id == param.id;
});
if (itr != shader->parameters + shader->num_parameters)
{
// Allow duplicate #pragma parameter, but only if they are exactly the same.
if (meta_param.desc != itr->desc ||
meta_param.initial != itr->initial ||
meta_param.minimum != itr->minimum ||
meta_param.maximum != itr->maximum ||
meta_param.step != itr->step)
{
RARCH_ERR("[Vulkan]: Duplicate parameters found for \"%s\", but arguments do not match.\n",
itr->id);
return nullptr;
}
chain->add_parameter(i, itr - shader->parameters, meta_param.id);
}
else
{
auto &param = shader->parameters[shader->num_parameters];
strlcpy(param.id, meta_param.id.c_str(), sizeof(param.id));
strlcpy(param.desc, meta_param.desc.c_str(), sizeof(param.desc));
param.current = meta_param.initial;
param.initial = meta_param.initial;
param.minimum = meta_param.minimum;
param.maximum = meta_param.maximum;
param.step = meta_param.step;
chain->add_parameter(i, shader->num_parameters, meta_param.id);
shader->num_parameters++;
}
}
chain->set_shader(i, chain->set_shader(i,
VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_VERTEX_BIT,
output.vertex.data(), output.vertex.data(),
@ -2917,6 +3015,9 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset(
sizeof(opaque_frag) / sizeof(uint32_t)); sizeof(opaque_frag) / sizeof(uint32_t));
} }
if (!video_shader_resolve_current_parameters(conf.get(), shader.get()))
return nullptr;
chain->set_shader_preset(move(shader)); chain->set_shader_preset(move(shader));
if (!chain->init()) if (!chain->init())

View File

@ -134,13 +134,18 @@ static slang_texture_semantic slang_uniform_name_to_texture_semantic(
} }
static slang_semantic slang_uniform_name_to_semantic( static slang_semantic slang_uniform_name_to_semantic(
const unordered_map<string, slang_semantic> &semantic_map, const unordered_map<string, slang_semantic_map> &semantic_map,
const string &name) const string &name, unsigned *index)
{ {
auto itr = semantic_map.find(name); auto itr = semantic_map.find(name);
if (itr != end(semantic_map)) if (itr != end(semantic_map))
return itr->second; {
*index = itr->second.index;
return itr->second.semantic;
}
// No builtin semantics are arrayed.
*index = 0;
unsigned i = 0; unsigned i = 0;
for (auto n : semantic_uniform_names) for (auto n : semantic_uniform_names)
{ {
@ -183,6 +188,29 @@ static bool set_ubo_texture_offset(slang_reflection *reflection,
return true; return true;
} }
static bool set_ubo_float_parameter_offset(slang_reflection *reflection,
unsigned index, size_t offset, unsigned num_components)
{
resize_minimum(reflection->semantic_float_parameters, index + 1);
auto &sem = reflection->semantic_float_parameters[index];
if (sem.uniform)
{
if (sem.ubo_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(offset));
return false;
}
}
sem.uniform = true;
sem.ubo_offset = offset;
sem.num_components = num_components;
return true;
}
static bool set_ubo_offset(slang_reflection *reflection, slang_semantic semantic, static bool set_ubo_offset(slang_reflection *reflection, slang_semantic semantic,
size_t offset, unsigned num_components) size_t offset, unsigned num_components)
{ {
@ -230,6 +258,10 @@ static bool validate_type_for_semantic(const SPIRType &type, slang_semantic sem)
// uint // uint
return type.basetype == SPIRType::UInt && type.vecsize == 1 && type.columns == 1; return type.basetype == SPIRType::UInt && type.vecsize == 1 && type.columns == 1;
case SLANG_SEMANTIC_FLOAT_PARAMETER:
// float
return type.basetype == SPIRType::Float && type.vecsize == 1 && type.columns == 1;
default: default:
// vec4 // vec4
return type.basetype == SPIRType::Float && type.vecsize == 4 && type.columns == 1; return type.basetype == SPIRType::Float && type.vecsize == 4 && type.columns == 1;
@ -253,8 +285,9 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r
auto &name = compiler.get_member_name(resource.base_type_id, range.index); auto &name = compiler.get_member_name(resource.base_type_id, range.index);
auto &type = compiler.get_type(compiler.get_type(resource.base_type_id).member_types[range.index]); auto &type = compiler.get_type(compiler.get_type(resource.base_type_id).member_types[range.index]);
unsigned sem_index = 0;
unsigned tex_sem_index = 0; unsigned tex_sem_index = 0;
auto sem = slang_uniform_name_to_semantic(*reflection->semantic_map, name); auto sem = slang_uniform_name_to_semantic(*reflection->semantic_map, name, &sem_index);
auto tex_sem = slang_uniform_name_to_texture_semantic(*reflection->texture_semantic_uniform_map, auto tex_sem = slang_uniform_name_to_texture_semantic(*reflection->texture_semantic_uniform_map,
name, &tex_sem_index); name, &tex_sem_index);
@ -273,8 +306,18 @@ static bool add_active_buffer_ranges(const Compiler &compiler, const Resource &r
return false; return false;
} }
if (!set_ubo_offset(reflection, sem, range.offset, type.vecsize)) switch (sem)
return false; {
case SLANG_SEMANTIC_FLOAT_PARAMETER:
if (!set_ubo_float_parameter_offset(reflection, sem_index, range.offset, type.vecsize))
return false;
break;
default:
if (!set_ubo_offset(reflection, sem, range.offset, type.vecsize))
return false;
break;
}
} }
else if (tex_sem != SLANG_INVALID_TEXTURE_SEMANTIC) else if (tex_sem != SLANG_INVALID_TEXTURE_SEMANTIC)
{ {
@ -510,6 +553,16 @@ static bool slang_reflect(const Compiler &vertex_compiler, const Compiler &fragm
} }
} }
RARCH_LOG("[slang]:\n");
RARCH_LOG("[slang]: Parameters:\n");
unsigned i = 0;
for (auto &param : reflection->semantic_float_parameters)
{
if (param.uniform)
RARCH_LOG("[slang]: #%u (Offset: %u)\n", i, param.ubo_offset);
i++;
}
return true; return true;
} }

View File

@ -59,10 +59,16 @@ enum slang_texture_semantic
enum slang_semantic enum slang_semantic
{ {
// mat4, MVP
SLANG_SEMANTIC_MVP = 0, SLANG_SEMANTIC_MVP = 0,
// vec4, viewport size of current pass
SLANG_SEMANTIC_OUTPUT = 1, SLANG_SEMANTIC_OUTPUT = 1,
// vec4, viewport size of final pass
SLANG_SEMANTIC_FINAL_VIEWPORT = 2, SLANG_SEMANTIC_FINAL_VIEWPORT = 2,
// uint, frame count with modulo
SLANG_SEMANTIC_FRAME_COUNT = 3, SLANG_SEMANTIC_FRAME_COUNT = 3,
// float, user defined parameter, arrayed
SLANG_SEMANTIC_FLOAT_PARAMETER = 4,
SLANG_NUM_SEMANTICS, SLANG_NUM_SEMANTICS,
SLANG_INVALID_SEMANTIC = -1 SLANG_INVALID_SEMANTIC = -1
@ -100,6 +106,12 @@ struct slang_texture_semantic_map
unsigned index; unsigned index;
}; };
struct slang_semantic_map
{
slang_semantic semantic;
unsigned index;
};
struct slang_reflection struct slang_reflection
{ {
slang_reflection(); slang_reflection();
@ -110,10 +122,11 @@ struct slang_reflection
std::vector<slang_texture_semantic_meta> semantic_textures[SLANG_NUM_TEXTURE_SEMANTICS]; std::vector<slang_texture_semantic_meta> semantic_textures[SLANG_NUM_TEXTURE_SEMANTICS];
slang_semantic_meta semantics[SLANG_NUM_SEMANTICS]; slang_semantic_meta semantics[SLANG_NUM_SEMANTICS];
std::vector<slang_semantic_meta> semantic_float_parameters;
const std::unordered_map<std::string, slang_texture_semantic_map> *texture_semantic_map = nullptr; const std::unordered_map<std::string, slang_texture_semantic_map> *texture_semantic_map = nullptr;
const std::unordered_map<std::string, slang_texture_semantic_map> *texture_semantic_uniform_map = nullptr; const std::unordered_map<std::string, slang_texture_semantic_map> *texture_semantic_uniform_map = nullptr;
const std::unordered_map<std::string, slang_semantic> *semantic_map = nullptr; const std::unordered_map<std::string, slang_semantic_map> *semantic_map = nullptr;
unsigned pass_number = 0; unsigned pass_number = 0;
}; };

View File

@ -391,6 +391,48 @@ static struct video_shader_parameter *video_shader_parse_find_parameter(
return NULL; return NULL;
} }
/**
* video_shader_set_current_parameters:
* @conf : Preset file to read from.
* @shader : Shader passes handle.
*
* Reads the current value for all parameters from config file.
*
* Returns: true (1) if successful, otherwise false (0).
**/
bool video_shader_resolve_current_parameters(config_file_t *conf,
struct video_shader *shader)
{
if (!conf)
return false;
/* Read in parameters which override the defaults. */
char parameters[4096] = {0};
const char *id = NULL;
char *save = NULL;
if (!config_get_array(conf, "parameters",
parameters, sizeof(parameters)))
return true;
for (id = strtok_r(parameters, ";", &save); id;
id = strtok_r(NULL, ";", &save))
{
struct video_shader_parameter *parameter = (struct video_shader_parameter*)
video_shader_parse_find_parameter(shader->parameters, shader->num_parameters, id);
if (!parameter)
{
RARCH_WARN("[CGP/GLSLP]: Parameter %s is set in the preset, but no shader uses this parameter, ignoring.\n", id);
continue;
}
if (!config_get_float(conf, id, &parameter->current))
RARCH_WARN("[CGP/GLSLP]: Parameter %s is not set in preset.\n", id);
}
return true;
}
/** /**
* video_shader_resolve_parameters: * video_shader_resolve_parameters:
* @conf : Preset file to read from. * @conf : Preset file to read from.
@ -447,33 +489,8 @@ bool video_shader_resolve_parameters(config_file_t *conf,
filestream_close(file); filestream_close(file);
} }
if (conf) if (conf && !video_shader_resolve_current_parameters(conf, shader))
{ return false;
/* Read in parameters which override the defaults. */
char parameters[4096] = {0};
const char *id = NULL;
char *save = NULL;
if (!config_get_array(conf, "parameters",
parameters, sizeof(parameters)))
return true;
for (id = strtok_r(parameters, ";", &save); id;
id = strtok_r(NULL, ";", &save))
{
struct video_shader_parameter *parameter = (struct video_shader_parameter*)
video_shader_parse_find_parameter(shader->parameters, shader->num_parameters, id);
if (!parameter)
{
RARCH_WARN("[CGP/GLSLP]: Parameter %s is set in the preset, but no shader uses this parameter, ignoring.\n", id);
continue;
}
if (!config_get_float(conf, id, &parameter->current))
RARCH_WARN("[CGP/GLSLP]: Parameter %s is not set in preset.\n", id);
}
}
return true; return true;
} }

View File

@ -192,6 +192,18 @@ void video_shader_write_conf_cgp(config_file_t *conf,
void video_shader_resolve_relative(struct video_shader *shader, void video_shader_resolve_relative(struct video_shader *shader,
const char *ref_path); const char *ref_path);
/**
* video_shader_resolve_parameters:
* @conf : Preset file to read from.
* @shader : Shader passes handle.
*
* Reads the current value for all parameters from config file.
*
* Returns: true (1) if successful, otherwise false (0).
**/
bool video_shader_resolve_current_parameters(config_file_t *conf,
struct video_shader *shader);
/** /**
* video_shader_resolve_parameters: * video_shader_resolve_parameters:
* @conf : Preset file to read from. * @conf : Preset file to read from.