mirror of https://github.com/snes9xgit/snes9x.git
734 lines
22 KiB
C++
734 lines
22 KiB
C++
#include "slang_preset.hpp"
|
|
#include "../external/SPIRV-Cross/spirv.hpp"
|
|
#include "slang_helpers.hpp"
|
|
#include "slang_preset_ini.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <cstdio>
|
|
#include <vector>
|
|
#include <cctype>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <filesystem>
|
|
#include <future>
|
|
#include "../external/SPIRV-Cross/spirv_cross.hpp"
|
|
#include "../external/SPIRV-Cross/spirv_glsl.hpp"
|
|
#include "slang_shader.hpp"
|
|
|
|
using std::string;
|
|
using std::to_string;
|
|
|
|
SlangPreset::SlangPreset()
|
|
{
|
|
}
|
|
|
|
SlangPreset::~SlangPreset()
|
|
{
|
|
}
|
|
|
|
bool SlangPreset::load_preset_file(string filename)
|
|
{
|
|
if (!ends_with(filename, ".slangp"))
|
|
return false;
|
|
|
|
IniFile conf;
|
|
|
|
if (!conf.load_file(filename))
|
|
return false;
|
|
|
|
int num_passes = conf.get_int("shaders", 0);
|
|
|
|
if (num_passes <= 0)
|
|
return false;
|
|
|
|
passes.resize(num_passes);
|
|
|
|
int index;
|
|
auto key = [&](string s) -> string { return s + to_string(index); };
|
|
auto iGetBool = [&](string s, bool def = false) -> bool {
|
|
return conf.get_bool(key(s), def);
|
|
};
|
|
auto iGetString = [&](string s, string def = "") -> string {
|
|
return conf.get_string(key(s), def);
|
|
};
|
|
auto iGetFloat = [&](string s, float def = 1.0f) -> float {
|
|
return conf.get_float(key(s), def);
|
|
};
|
|
auto iGetInt = [&](string s, int def = 0) -> int {
|
|
return conf.get_int(key(s), def);
|
|
};
|
|
|
|
for (index = 0; index < num_passes; index++)
|
|
{
|
|
auto &shader = passes[index];
|
|
shader.filename = iGetString("shader", "");
|
|
canonicalize(shader.filename, conf.get_source(key("shader")));
|
|
shader.alias = iGetString("alias", "");
|
|
shader.filter_linear = iGetBool("filter_linear");
|
|
shader.mipmap_input = iGetBool("mipmap_input");
|
|
shader.float_framebuffer = iGetBool("float_framebuffer");
|
|
shader.srgb_framebuffer = iGetBool("srgb_framebuffer");
|
|
shader.frame_count_mod = iGetInt("frame_count_mod", 0);
|
|
shader.wrap_mode = iGetString("wrap_mode");
|
|
// Is this correct? It gives priority to _x and _y scale types.
|
|
string scale_type = iGetString("scale_type", "undefined");
|
|
shader.scale_type_x = iGetString("scale_type_x", scale_type);
|
|
shader.scale_type_y = iGetString("scale_type_y", scale_type);
|
|
shader.scale_x = iGetFloat("scale_x", 1.0f);
|
|
shader.scale_y = iGetFloat("scale_y", 1.0f);
|
|
if (conf.exists(key("scale")))
|
|
{
|
|
float scale = iGetFloat("scale");
|
|
shader.scale_x = scale;
|
|
shader.scale_y = scale;
|
|
}
|
|
}
|
|
|
|
string texture_string = conf.get_string("textures", "");
|
|
if (!texture_string.empty())
|
|
{
|
|
auto texture_list = split_string(texture_string, ';');
|
|
|
|
for (auto &id : texture_list)
|
|
{
|
|
Texture texture;
|
|
texture.id = trim(id);
|
|
textures.push_back(texture);
|
|
}
|
|
|
|
for (auto &t : textures)
|
|
{
|
|
t.wrap_mode = conf.get_string(t.id + "_wrap_mode", "");
|
|
t.mipmap = conf.get_bool(t.id + "_mipmap", false);
|
|
t.linear = conf.get_bool(t.id + "_linear", false);
|
|
t.filename = conf.get_string(t.id, "");
|
|
canonicalize(t.filename, conf.get_source(t.id));
|
|
}
|
|
}
|
|
|
|
SlangShader::initialize_glslang();
|
|
std::vector<std::future<bool>> futures;
|
|
for (size_t i = 0; i < passes.size(); i++)
|
|
{
|
|
futures.push_back(std::async(std::launch::async, [this, i]() -> bool {
|
|
return passes[i].load_file();
|
|
}));
|
|
}
|
|
if (!std::all_of(futures.begin(), futures.end(), [](auto &f) { return f.get(); }))
|
|
return false;
|
|
|
|
gather_parameters();
|
|
|
|
for (auto &p : parameters)
|
|
{
|
|
auto value_str = conf.get_string(p.id, "");
|
|
if (!value_str.empty())
|
|
{
|
|
p.val = atof(value_str.c_str());
|
|
if (p.val < p.min)
|
|
p.val = p.min;
|
|
else if (p.val > p.max)
|
|
p.val = p.max;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Aggregates the parameters from individual stages and separate shader files,
|
|
resolving duplicates.
|
|
*/
|
|
void SlangPreset::gather_parameters()
|
|
{
|
|
parameters.clear();
|
|
for (auto &s : passes)
|
|
{
|
|
for (auto &p : s.parameters)
|
|
{
|
|
auto it = std::find_if(parameters.begin(), parameters.end(), [&p](SlangShader::Parameter &needle) {
|
|
return (needle.id == p.id);
|
|
});
|
|
|
|
if (it == parameters.end())
|
|
parameters.push_back(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Print to stdout the entire shader preset chain and parameters, minus source
|
|
and SPIRV output
|
|
*/
|
|
void SlangPreset::print()
|
|
{
|
|
printf("Number of Shaders: %zu\n", passes.size());
|
|
for (size_t i = 0; i < passes.size(); i++)
|
|
{
|
|
auto &s = passes[i];
|
|
printf(" Shader \n");
|
|
printf(" filename: %s\n", s.filename.c_str());
|
|
printf(" alias: %s\n", s.alias.c_str());
|
|
printf(" filter_linear: %d\n", s.filter_linear);;
|
|
printf(" mipmap_input: %d\n", s.mipmap_input);
|
|
printf(" float_framebuffer: %d\n", s.float_framebuffer);
|
|
printf(" srgb_framebuffer: %d\n", s.srgb_framebuffer);
|
|
printf(" frame_count_mod: %d\n", s.frame_count_mod);
|
|
printf(" wrap_mode: %s\n", s.wrap_mode.c_str());
|
|
printf(" scale_type_x: %s\n", s.scale_type_x.c_str());
|
|
printf(" scale_type_y: %s\n", s.scale_type_y.c_str());
|
|
printf(" scale_x: %f\n", s.scale_x);
|
|
printf(" scale_y: %f\n", s.scale_y);
|
|
printf(" pragma lines: ");
|
|
for (auto &p : s.pragma_stage_lines)
|
|
printf("%zu ", p);
|
|
printf("\n");
|
|
printf(" Number of parameters: %zu\n", s.parameters.size());
|
|
for (auto &p : s.parameters)
|
|
{
|
|
printf(" %s \"%s\" min: %f max: %f val: %f step: %f digits: %d\n",
|
|
p.id.c_str(), p.name.c_str(), p.min, p.max, p.val, p.step, p.significant_digits);
|
|
}
|
|
|
|
printf(" Uniforms: %zu\n", s.uniforms.size());
|
|
printf(" UBO size: %zu, binding: %d\n", s.ubo_size, s.ubo_binding);
|
|
printf(" Push Constant block size: %zu\n", s.push_constant_block_size);
|
|
for (auto &u : s.uniforms)
|
|
{
|
|
const char *strings[] = {
|
|
"Output Size",
|
|
"Previous Frame Size",
|
|
"Pass Size",
|
|
"Pass Feedback Size",
|
|
"Lut Size",
|
|
"MVP Matrix",
|
|
"Frame Count",
|
|
"Parameter"
|
|
};
|
|
|
|
const char *block = u.block == SlangShader::Uniform::UBO ? "UBO" : "Push Constants";
|
|
const char *type = strings[u.type];
|
|
|
|
printf(" ");
|
|
switch (u.type)
|
|
{
|
|
case SlangShader::Uniform::Type::ViewportSize:
|
|
printf("%s at offset %zu in %s\n", type, u.offset, block);
|
|
break;
|
|
case SlangShader::Uniform::Type::PreviousFrameSize:
|
|
case SlangShader::Uniform::Type::PassSize:
|
|
case SlangShader::Uniform::Type::PassFeedbackSize:
|
|
case SlangShader::Uniform::Type::LutSize:
|
|
printf("%s %d at offset %zu in %s\n", type, u.specifier, u.offset, block);
|
|
break;
|
|
case SlangShader::Uniform::Type::MVP:
|
|
case SlangShader::Uniform::Type::FrameCount:
|
|
printf("%s at offset %zu in %s\n", type, u.offset, block);
|
|
break;
|
|
case SlangShader::Uniform::Type::Parameter:
|
|
printf("%s #%d named \"%s\" at offset %zu in %s\n", type, u.specifier, parameters[u.specifier].id.c_str(), u.offset, block);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf(" Samplers: %zu\n", s.samplers.size());
|
|
for (auto &sampler : s.samplers)
|
|
{
|
|
const char *strings[] =
|
|
{
|
|
"Previous Frame",
|
|
"Pass",
|
|
"Pass Feedback",
|
|
"Lut",
|
|
};
|
|
|
|
const char *type = strings[sampler.type];
|
|
|
|
printf(" ");
|
|
switch (sampler.type)
|
|
{
|
|
case SlangShader::Sampler::Type::PreviousFrame:
|
|
case SlangShader::Sampler::Type::Pass:
|
|
case SlangShader::Sampler::Type::PassFeedback:
|
|
printf("%s %d at binding %d\n", type, sampler.specifier, sampler.binding);
|
|
break;
|
|
case SlangShader::Sampler::Type::Lut:
|
|
printf("%s %d \"%s\" at binding %d\n", type, sampler.specifier, textures[sampler.specifier].id.c_str(), sampler.binding);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("Num textures: %zu\n", textures.size());
|
|
for (size_t i = 0; i < textures.size(); i++)
|
|
{
|
|
auto &t = textures[i];
|
|
printf(" Texture %zu\n", i);
|
|
printf(" id: %s\n", t.id.c_str());
|
|
printf(" filename: %s\n", t.filename.c_str());
|
|
printf(" wrap_mode: %s\n", t.wrap_mode.c_str());
|
|
printf(" mipmap: %d\n", t.mipmap);
|
|
printf(" linear: %d\n", t.linear);
|
|
}
|
|
|
|
printf("Parameters: %zu count\n", parameters.size());
|
|
for (size_t i = 0; i < parameters.size(); i++)
|
|
{
|
|
auto &p = parameters[i];
|
|
|
|
printf(" Parameter: %zu\n", i);
|
|
printf(" id: %s\n", p.id.c_str());
|
|
printf(" name: %s\n", p.name.c_str());
|
|
printf(" min: %f\n", p.min);
|
|
printf(" max: %f\n", p.max);
|
|
printf(" step: %f\n", p.step);
|
|
printf(" sigdigits: %d\n", p.significant_digits);
|
|
printf(" value: %f\n", p.val);
|
|
}
|
|
|
|
printf("Oldest previous frame used: %d\n", oldest_previous_frame);
|
|
}
|
|
|
|
bool SlangPreset::match_sampler_semantic(const string &name, int pass, SlangShader::Sampler::Type &type, int &specifier)
|
|
{
|
|
auto match_with_specifier = [&name, &specifier](string prefix) -> bool {
|
|
if (name.compare(0, prefix.length(), prefix) != 0)
|
|
return false;
|
|
|
|
if (name.length() <= prefix.length())
|
|
return false;
|
|
|
|
for (auto iter = name.begin() + prefix.length(); iter < name.end(); iter++)
|
|
{
|
|
if (!std::isdigit(*iter))
|
|
return false;
|
|
}
|
|
|
|
specifier = std::stoi(name.substr(prefix.length()));
|
|
return true;
|
|
};
|
|
|
|
if (name == "Original")
|
|
{
|
|
type = SlangShader::Sampler::Type::Pass;
|
|
specifier = -1;
|
|
return true;
|
|
}
|
|
else if (name == "Source")
|
|
{
|
|
type = SlangShader::Sampler::Type::Pass;
|
|
specifier = pass - 1;
|
|
return true;
|
|
}
|
|
else if (match_with_specifier("OriginalHistory"))
|
|
{
|
|
type = SlangShader::Sampler::Type::PreviousFrame;
|
|
return true;
|
|
}
|
|
else if (match_with_specifier("PassOutput"))
|
|
{
|
|
type = SlangShader::Sampler::Type::Pass;
|
|
return true;
|
|
}
|
|
else if (match_with_specifier("PassFeedback"))
|
|
{
|
|
type = SlangShader::Sampler::Type::PassFeedback;
|
|
return true;
|
|
}
|
|
else if (match_with_specifier("User"))
|
|
{
|
|
type = SlangShader::Sampler::Type::Lut;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
for (size_t i = 0; i < passes.size(); i++)
|
|
{
|
|
if (passes[i].alias == name)
|
|
{
|
|
type = SlangShader::Sampler::Type::Pass;
|
|
specifier = i;
|
|
return true;
|
|
}
|
|
else if (passes[i].alias + "Feedback" == name)
|
|
{
|
|
type = SlangShader::Sampler::Type::PassFeedback;
|
|
specifier = i;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < textures.size(); i++)
|
|
{
|
|
if (name == textures[i].id)
|
|
{
|
|
type = SlangShader::Sampler::Type::Lut;
|
|
specifier = i;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SlangPreset::match_buffer_semantic(const string &name, int pass, SlangShader::Uniform::Type &type, int &specifier)
|
|
{
|
|
if (name == "MVP")
|
|
{
|
|
type = SlangShader::Uniform::Type::MVP;
|
|
return true;
|
|
}
|
|
|
|
if (name == "FrameCount")
|
|
{
|
|
type = SlangShader::Uniform::Type::FrameCount;
|
|
return true;
|
|
}
|
|
|
|
if (name == "FinalViewportSize")
|
|
{
|
|
type = SlangShader::Uniform::Type::ViewportSize;
|
|
return true;
|
|
}
|
|
|
|
if (name == "FrameDirection")
|
|
{
|
|
type = SlangShader::Uniform::Type::FrameDirection;
|
|
return true;
|
|
}
|
|
|
|
if (name.find("Size") != string::npos)
|
|
{
|
|
auto match = [&name, &specifier](string prefix) -> bool {
|
|
if (name.compare(0, prefix.length(), prefix) != 0)
|
|
return false;
|
|
|
|
if (name.compare(prefix.length(), 4, "Size") != 0)
|
|
return false;
|
|
|
|
if (prefix.length() + 4 < name.length())
|
|
specifier = std::stoi(name.substr(prefix.length() + 4));
|
|
|
|
return true;
|
|
};
|
|
|
|
if (match("Original"))
|
|
{
|
|
type = SlangShader::Uniform::Type::PassSize;
|
|
specifier = -1;
|
|
return true;
|
|
}
|
|
else if (match("Source"))
|
|
{
|
|
type = SlangShader::Uniform::Type::PassSize;
|
|
specifier = pass - 1;
|
|
return true;
|
|
}
|
|
else if (match("Output"))
|
|
{
|
|
type = SlangShader::Uniform::Type::PassSize;
|
|
specifier = pass;
|
|
return true;
|
|
}
|
|
else if (match("OriginalHistory"))
|
|
{
|
|
type = SlangShader::Uniform::Type::PreviousFrameSize;
|
|
return true;
|
|
}
|
|
else if (match("PassOutput"))
|
|
{
|
|
type = SlangShader::Uniform::Type::PassSize;
|
|
return true;
|
|
}
|
|
else if (match("PassFeedback"))
|
|
{
|
|
type = SlangShader::Uniform::Type::PassFeedbackSize;
|
|
return true;
|
|
}
|
|
else if (match("User"))
|
|
{
|
|
type = SlangShader::Uniform::Type::LutSize;
|
|
return true;
|
|
}
|
|
|
|
for (size_t i = 0; i < passes.size(); i++)
|
|
{
|
|
if (match(passes[i].alias))
|
|
{
|
|
type = SlangShader::Uniform::Type::PassSize;
|
|
specifier = i;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < textures.size(); i++)
|
|
{
|
|
if (match(textures[i].id))
|
|
{
|
|
type = SlangShader::Uniform::Type::LutSize;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < parameters.size(); i++)
|
|
{
|
|
if (name == parameters[i].id)
|
|
{
|
|
type = SlangShader::Uniform::Type::Parameter;
|
|
specifier = i;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Introspect an individual shader pass, collecting external resource info
|
|
in order to build uniform blocks.
|
|
*/
|
|
bool SlangPreset::introspect_shader(SlangShader &shader, int pass, SlangShader::Stage stage)
|
|
{
|
|
spirv_cross::CompilerGLSL cross(stage == SlangShader::Stage::Vertex ? shader.vertex_shader_spirv : shader.fragment_shader_spirv);
|
|
|
|
auto res = cross.get_shader_resources();
|
|
|
|
if (res.push_constant_buffers.size() > 1)
|
|
{
|
|
printf("%s: Too many push constant buffers.\n", shader.filename.c_str());
|
|
return false;
|
|
}
|
|
else if (res.uniform_buffers.size() > 1)
|
|
{
|
|
printf("%s: Too many uniform buffers.\n", shader.filename.c_str());
|
|
return false;
|
|
}
|
|
|
|
auto exists = [&shader](const SlangShader::Uniform &uniform) -> bool {
|
|
for (const auto &u : shader.uniforms)
|
|
{
|
|
if (u.block == uniform.block &&
|
|
u.offset == uniform.offset &&
|
|
u.specifier == uniform.specifier &&
|
|
u.type == uniform.type)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
if (res.push_constant_buffers.size() == 0)
|
|
{
|
|
shader.push_constant_block_size = 0;
|
|
}
|
|
else
|
|
{
|
|
auto &pcb = res.push_constant_buffers[0];
|
|
auto &pcb_type = cross.get_type(pcb.base_type_id);
|
|
shader.push_constant_block_size = cross.get_declared_struct_size(pcb_type);
|
|
|
|
for (size_t i = 0; i < pcb_type.member_types.size(); i++)
|
|
{
|
|
auto name = cross.get_member_name(pcb.base_type_id, i);
|
|
auto offset = cross.get_member_decoration(pcb.base_type_id, i, spv::DecorationOffset);
|
|
|
|
SlangShader::Uniform::Type semantic_type;
|
|
int specifier;
|
|
if (match_buffer_semantic(name, pass, semantic_type, specifier))
|
|
{
|
|
SlangShader::Uniform uniform{ SlangShader::Uniform::Block::PushConstant,
|
|
offset,
|
|
semantic_type,
|
|
specifier };
|
|
|
|
if (!exists(uniform))
|
|
shader.uniforms.push_back(uniform);
|
|
}
|
|
else
|
|
{
|
|
printf("%s: Failed to match push constant semantic: \"%s\"\n", shader.filename.c_str(), name.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (res.uniform_buffers.size() == 0)
|
|
{
|
|
shader.ubo_size = 0;
|
|
}
|
|
else
|
|
{
|
|
auto &ubo = res.uniform_buffers[0];
|
|
auto &ubo_type = cross.get_type(ubo.base_type_id);
|
|
shader.ubo_size = cross.get_declared_struct_size(ubo_type);
|
|
shader.ubo_binding = cross.get_decoration(ubo.base_type_id, spv::DecorationBinding);
|
|
|
|
for (size_t i = 0; i < ubo_type.member_types.size(); i++)
|
|
{
|
|
auto name = cross.get_member_name(ubo.base_type_id, i);
|
|
auto offset = cross.get_member_decoration(ubo.base_type_id, i, spv::DecorationOffset);
|
|
|
|
SlangShader::Uniform::Type semantic_type;
|
|
int specifier;
|
|
if (match_buffer_semantic(name, pass, semantic_type, specifier))
|
|
{
|
|
SlangShader::Uniform uniform{ SlangShader::Uniform::Block::UBO,
|
|
offset,
|
|
semantic_type,
|
|
specifier };
|
|
|
|
if (!exists(uniform))
|
|
shader.uniforms.push_back(uniform);
|
|
}
|
|
else
|
|
{
|
|
printf("%s: Failed to match uniform buffer semantic: \"%s\"\n", shader.filename.c_str(), name.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (res.sampled_images.size() == 0 && stage == SlangShader::Stage::Fragment)
|
|
{
|
|
printf("No sampled images found in fragment shader.\n");
|
|
return false;
|
|
}
|
|
|
|
if (res.sampled_images.size() > 0 && stage == SlangShader::Stage::Vertex)
|
|
{
|
|
printf("Sampled image found in vertex shader.\n");
|
|
return false;
|
|
}
|
|
|
|
if (stage == SlangShader::Stage::Fragment)
|
|
{
|
|
for (auto &image : res.sampled_images)
|
|
{
|
|
SlangShader::Sampler::Type semantic_type;
|
|
int specifier;
|
|
|
|
if (match_sampler_semantic(image.name, pass, semantic_type, specifier))
|
|
{
|
|
int binding = cross.get_decoration(image.id, spv::DecorationBinding);
|
|
shader.samplers.push_back({ semantic_type, specifier, binding });
|
|
}
|
|
else
|
|
{
|
|
printf("%s: Failed to match sampler semantic: \"%s\"\n", shader.filename.c_str(), image.name.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Introspect all of preset's shaders.
|
|
*/
|
|
bool SlangPreset::introspect()
|
|
{
|
|
for (size_t i = 0; i < passes.size(); i++)
|
|
{
|
|
if (!introspect_shader(passes[i], i, SlangShader::Stage::Vertex))
|
|
return false;
|
|
if (!introspect_shader(passes[i], i, SlangShader::Stage::Fragment))
|
|
return false;
|
|
}
|
|
|
|
oldest_previous_frame = 0;
|
|
uses_feedback = false;
|
|
last_pass_uses_feedback = false;
|
|
|
|
for (auto &p : passes)
|
|
{
|
|
for (auto &s : p.samplers)
|
|
{
|
|
if (s.type == SlangShader::Sampler::PreviousFrame && s.specifier > oldest_previous_frame)
|
|
oldest_previous_frame = s.specifier;
|
|
if (s.type == SlangShader::Sampler::PassFeedback)
|
|
{
|
|
uses_feedback = true;
|
|
if (s.specifier == (int)passes.size() - 1)
|
|
last_pass_uses_feedback = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SlangPreset::save_to_file(std::string filename)
|
|
{
|
|
std::ofstream out(filename);
|
|
if (!out.is_open())
|
|
return false;
|
|
|
|
auto outs = [&](std::string key, std::string value) { out << key << " = \"" << value << "\"\n"; };
|
|
auto outb = [&](std::string key, bool value) { outs(key, value ? "true" : "false"); };
|
|
auto outa = [&](std::string key, auto value) { outs(key, to_string(value)); };
|
|
|
|
outa("shaders", passes.size());
|
|
|
|
for (size_t i = 0; i < passes.size(); i++)
|
|
{
|
|
auto &pass = passes[i];
|
|
auto indexed = [i](std::string str) { return str + to_string(i); };
|
|
outs(indexed("shader"), pass.filename);
|
|
outb(indexed("filter_linear"), pass.filter_linear);
|
|
outs(indexed("wrap_mode"), pass.wrap_mode);
|
|
outs(indexed("alias"), pass.alias);
|
|
outb(indexed("float_framebuffer"), pass.float_framebuffer);
|
|
outb(indexed("srgb_framebuffer"), pass.srgb_framebuffer);
|
|
outb(indexed("mipmap_input"), pass.mipmap_input);
|
|
outs(indexed("scale_type_x"), pass.scale_type_x);
|
|
outs(indexed("scale_type_y"), pass.scale_type_y);
|
|
outa(indexed("scale_x"), pass.scale_x);
|
|
outa(indexed("scale_y"), pass.scale_y);
|
|
outa(indexed("frame_count_mod"), pass.frame_count_mod);
|
|
}
|
|
|
|
if (parameters.size() > 0)
|
|
{
|
|
std::string parameter_list = "";
|
|
for (size_t i = 0; i < parameters.size(); i++)
|
|
{
|
|
parameter_list += parameters[i].id;
|
|
if (i < parameters.size() - 1)
|
|
parameter_list += ";";
|
|
}
|
|
outs("parameters", parameter_list);
|
|
}
|
|
|
|
for (auto &item : parameters)
|
|
outa(item.id, item.val);
|
|
|
|
if (textures.size() > 0)
|
|
{
|
|
std::string texture_list = "";
|
|
for (size_t i = 0; i < textures.size(); i++)
|
|
{
|
|
texture_list += textures[i].id;
|
|
if (i < textures.size() - 1)
|
|
texture_list += ";";
|
|
}
|
|
outs("textures", texture_list);
|
|
}
|
|
|
|
for (auto &item : textures)
|
|
{
|
|
outs(item.id, item.filename);
|
|
outb(item.id + "_linear", item.linear);
|
|
outs(item.id + "_wrap_mode", item.wrap_mode);
|
|
outb(item.id + "_mipmap", item.mipmap);
|
|
}
|
|
|
|
out.close();
|
|
|
|
return true;
|
|
} |