This commit is contained in:
Bill Hollings 2017-01-28 12:54:49 -05:00
commit 05be822512
32 changed files with 1299 additions and 33 deletions

View File

@ -25,3 +25,4 @@ script:
- PATH=./glslang/StandAlone:./SPIRV-Tools/tools:$PATH
- ./test_shaders.py shaders
- ./test_shaders.py --metal shaders-msl
- ./test_shaders.py --hlsl shaders-hlsl

View File

@ -45,11 +45,16 @@ add_library(spirv-cross-msl STATIC
${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.hpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.cpp)
add_library(spirv-cross-hlsl STATIC
${CMAKE_CURRENT_SOURCE_DIR}/spirv_hlsl.hpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_hlsl.cpp)
add_executable(spirv-cross main.cpp)
target_link_libraries(spirv-cross spirv-cross-glsl spirv-cross-cpp spirv-cross-msl spirv-cross-core)
target_link_libraries(spirv-cross spirv-cross-glsl spirv-cross-cpp spirv-cross-msl spirv-cross-hlsl spirv-cross-core)
target_link_libraries(spirv-cross-glsl spirv-cross-core)
target_link_libraries(spirv-cross-msl spirv-cross-glsl)
target_link_libraries(spirv-cross-cpp spirv-cross-glsl)
target_link_libraries(spirv-cross-hlsl spirv-cross-glsl)
target_include_directories(spirv-cross-core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
set(spirv-compiler-options "")
@ -75,11 +80,13 @@ target_compile_options(spirv-cross-core PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross-glsl PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross-msl PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross-cpp PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross-hlsl PRIVATE ${spirv-compiler-options})
target_compile_options(spirv-cross PRIVATE ${spirv-compiler-options})
target_compile_definitions(spirv-cross-core PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross-glsl PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross-msl PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross-cpp PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross-hlsl PRIVATE ${spirv-compiler-defines})
target_compile_definitions(spirv-cross PRIVATE ${spirv-compiler-defines})
# Set up tests, using only the simplest modes of the test_shaders
@ -96,6 +103,9 @@ if (${PYTHONINTERP_FOUND})
add_test(NAME spirv-cross-test-metal
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal
${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl)
add_test(NAME spirv-cross-test-hlsl
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl
${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl)
endif()
else()
message(WARNING "Testing disabled. Could not find python3. If you have python3 installed try running "

View File

@ -8,6 +8,7 @@ SPIRV-Cross is a tool designed for parsing and converting SPIR-V to other shader
- Convert SPIR-V to readable, usable and efficient GLSL
- Convert SPIR-V to readable, usable and efficient Metal Shading Language (MSL) [EXPERIMENTAL]
- Convert SPIR-V to readable, usable and efficient HLSL [EXPERIMENTAL]
- Convert SPIR-V to debuggable C++ [EXPERIMENTAL]
- Reflection API to simplify the creation of Vulkan pipeline layouts
- Reflection API to modify and tweak OpDecorations
@ -211,6 +212,10 @@ See `./test_shaders.py --help` for more.
To test the roundtrip path GLSL -> SPIR-V -> MSL, `--metal` can be added, e.g. `./test_shaders.py --metal shaders-msl`.
### HLSL backend
To test the roundtrip path GLSL -> SPIR-V -> HLSL, `--hlsl` can be added, e.g. `./test_shaders.py --hlsl shaders-hlsl`.
### Updating regression tests
When legitimate changes are found, use `--update` flag to update regression files.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
#include "spirv_cpp.hpp"
#include "spirv_msl.hpp"
#include "spirv_hlsl.hpp"
#include <algorithm>
#include <cstdio>
#include <cstring>
@ -430,6 +431,7 @@ struct CLIArguments
uint32_t iterations = 1;
bool cpp = false;
bool metal = false;
bool hlsl = false;
bool vulkan_semantics = false;
bool remove_unused = false;
bool cfg_analysis = true;
@ -440,9 +442,9 @@ static void print_help()
fprintf(stderr, "Usage: spirv-cross [--output <output path>] [SPIR-V file] [--es] [--no-es] [--no-cfg-analysis] "
"[--version <GLSL "
"version>] [--dump-resources] [--help] [--force-temporary] [--cpp] [--cpp-interface-name <name>] "
"[--metal] [--vulkan-semantics] [--flatten-ubo] [--fixup-clipspace] [--iterations iter] [--pls-in "
"format input-name] [--pls-out format output-name] [--remap source_name target_name components] "
"[--extension ext] [--entry name] [--remove-unused-variables] "
"[--metal] [--hlsl] [--vulkan-semantics] [--flatten-ubo] [--fixup-clipspace] [--iterations iter] "
"[--pls-in format input-name] [--pls-out format output-name] [--remap source_name target_name "
"components] [--extension ext] [--entry name] [--remove-unused-variables] "
"[--remap-variable-type <variable_name> <new_variable_type>]\n");
}
@ -561,6 +563,7 @@ int main(int argc, char *argv[])
cbs.add("--cpp", [&args](CLIParser &) { args.cpp = true; });
cbs.add("--cpp-interface-name", [&args](CLIParser &parser) { args.cpp_interface_name = parser.next_string(); });
cbs.add("--metal", [&args](CLIParser &) { args.metal = true; });
cbs.add("--hlsl", [&args](CLIParser &) { args.hlsl = true; });
cbs.add("--vulkan-semantics", [&args](CLIParser &) { args.vulkan_semantics = true; });
cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); });
cbs.add("--entry", [&args](CLIParser &parser) { args.entry = parser.next_string(); });
@ -622,6 +625,8 @@ int main(int argc, char *argv[])
}
else if (args.metal)
compiler = unique_ptr<CompilerMSL>(new CompilerMSL(read_spirv_file(args.input)));
else if (args.hlsl)
compiler = unique_ptr<CompilerHLSL>(new CompilerHLSL(read_spirv_file(args.input)));
else
{
combined_image_samplers = !args.vulkan_semantics;

View File

@ -127,6 +127,7 @@
<ClCompile Include="..\spirv_cpp.cpp" />
<ClCompile Include="..\spirv_cross.cpp" />
<ClCompile Include="..\spirv_glsl.cpp" />
<ClCompile Include="..\spirv_hlsl.cpp" />
<ClCompile Include="..\spirv_msl.cpp" />
<ClCompile Include="..\spirv_cfg.cpp" />
</ItemGroup>
@ -137,6 +138,7 @@
<ClInclude Include="..\spirv_cross.hpp" />
<ClInclude Include="..\spirv_glsl.hpp" />
<ClInclude Include="..\spirv.hpp" />
<ClInclude Include="..\spirv_hlsl.hpp" />
<ClInclude Include="..\spirv_msl.hpp" />
<ClInclude Include="..\spirv_cfg.hpp" />
</ItemGroup>

View File

@ -33,6 +33,9 @@
<ClCompile Include="..\spirv_cfg.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\spirv_hlsl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\GLSL.std.450.h">
@ -59,5 +62,8 @@
<ClInclude Include="..\spirv_cfg.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\spirv_hlsl.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -0,0 +1,31 @@
uniform sampler2D uTex;
static float4 FragColor;
static float4 vColor;
static float2 vTex;
struct InputFrag
{
float4 vColor : TEXCOORD0;
float2 vTex : TEXCOORD1;
};
struct OutputFrag
{
float4 FragColor : COLOR;
};
void frag_main()
{
FragColor = vColor * tex2D(uTex, vTex);
}
OutputFrag main(InputFrag input)
{
vColor = input.vColor;
vTex = input.vTex;
frag_main();
OutputFrag output;
output.FragColor = FragColor;
return output;
}

View File

@ -0,0 +1,46 @@
struct _UBO
{
float4x4 uMVP;
};
cbuffer UBO
{
_UBO _16;
};
uniform float4 gl_HalfPixel;
static float4 gl_Position;
static float4 aVertex;
static float3 vNormal;
static float3 aNormal;
struct InputVert
{
float3 aNormal : TEXCOORD0;
float4 aVertex : TEXCOORD1;
};
struct OutputVert
{
float3 vNormal : TEXCOORD0;
float4 gl_Position : POSITION;
};
void vert_main()
{
gl_Position = mul(aVertex, _16.uMVP);
vNormal = aNormal;
}
OutputVert main(InputVert input)
{
aVertex = input.aVertex;
aNormal = input.aNormal;
vert_main();
OutputVert output;
output.gl_Position = gl_Position;
output.vNormal = vNormal;
output.gl_Position.x = output.gl_Position.x - gl_HalfPixel.x * output.gl_Position.w;
output.gl_Position.y = output.gl_Position.y + gl_HalfPixel.y * output.gl_Position.w;
return output;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -0,0 +1,13 @@
#version 310 es
precision mediump float;
in vec4 vColor;
in vec2 vTex;
layout(binding = 0) uniform sampler2D uTex;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = vColor * texture(uTex, vTex);
}

View File

@ -0,0 +1,15 @@
#version 310 es
layout(std140) uniform UBO
{
uniform mat4 uMVP;
};
in vec4 aVertex;
in vec3 aNormal;
out vec3 vNormal;
void main()
{
gl_Position = uMVP * aVertex;
vNormal = aNormal;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016 ARM Limited
* Copyright 2016-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016 ARM Limited
* Copyright 2016-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -817,6 +817,11 @@ const SPIRType &Compiler::get_type(uint32_t id) const
return get<SPIRType>(id);
}
const SPIRType &Compiler::get_type_from_variable(uint32_t id) const
{
return get<SPIRType>(get<SPIRVariable>(id).basetype);
}
void Compiler::set_member_decoration(uint32_t id, uint32_t index, Decoration decoration, uint32_t argument)
{
meta.at(id).members.resize(max(meta[id].members.size(), size_t(index) + 1));

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -151,6 +151,8 @@ public:
// Mostly used with Resource::type_id and Resource::base_type_id to parse the underlying type of a resource.
const SPIRType &get_type(uint32_t id) const;
const SPIRType &get_type_from_variable(uint32_t id) const;
// Gets the underlying storage class for an OpVariable.
spv::StorageClass get_storage_class(uint32_t id) const;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -1889,7 +1889,7 @@ string CompilerGLSL::constant_expression_vector(const SPIRConstant &c, uint32_t
if (c.vector_size() > 1)
res += type_to_glsl(type) + "(";
bool splat = c.vector_size() > 1;
bool splat = backend.use_constructor_splatting && c.vector_size() > 1;
if (splat)
{
if (type_to_std430_base_size(type) == 8)
@ -4125,7 +4125,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
// Only splat if we have vector constructors.
// Arrays and structs must be initialized properly in full.
bool composite = !out_type.array.empty() || out_type.basetype == SPIRType::Struct;
bool splat = in_type.vecsize == 1 && in_type.columns == 1 && !composite;
bool splat = in_type.vecsize == 1 && in_type.columns == 1 && !composite && backend.use_constructor_splatting;
if (splat)
{

View File

@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 ARM Limited
* Copyright 2015-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -176,6 +176,9 @@ protected:
uint32_t offset, uint32_t bias, uint32_t comp, uint32_t sample,
bool *p_forward);
virtual std::string clean_func_name(std::string func_name);
virtual void emit_buffer_block(const SPIRVariable &type);
virtual void emit_push_constant_block(const SPIRVariable &var);
virtual void emit_uniform(const SPIRVariable &var);
std::unique_ptr<std::ostringstream> buffer;
@ -266,15 +269,14 @@ protected:
bool explicit_struct_type = false;
bool use_initializer_list = false;
bool native_row_major_matrix = true;
bool use_constructor_splatting = true;
} backend;
void emit_struct(SPIRType &type);
void emit_resources();
void emit_buffer_block(const SPIRVariable &type);
void emit_buffer_block_native(const SPIRVariable &var);
void emit_buffer_block_legacy(const SPIRVariable &var);
void emit_buffer_block_flattened(const SPIRVariable &type);
void emit_push_constant_block(const SPIRVariable &var);
void emit_push_constant_block_vulkan(const SPIRVariable &var);
void emit_push_constant_block_glsl(const SPIRVariable &var);
void emit_interface_block(const SPIRVariable &type);
@ -282,7 +284,6 @@ protected:
void emit_specialization_constant(const SPIRConstant &constant);
std::string emit_continue_block(uint32_t continue_block);
bool attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method method);
void emit_uniform(const SPIRVariable &var);
void propagate_loop_dominators(const SPIRBlock &block);
void branch(uint32_t from, uint32_t to);

1019
spirv_hlsl.cpp Normal file

File diff suppressed because it is too large Load Diff

74
spirv_hlsl.hpp Normal file
View File

@ -0,0 +1,74 @@
/*
* Copyright 2016-2017 Robert Konrad
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SPIRV_HLSL_HPP
#define SPIRV_HLSL_HPP
#include "spirv_glsl.hpp"
#include <utility>
#include <vector>
namespace spirv_cross
{
class CompilerHLSL : public CompilerGLSL
{
public:
struct Options
{
uint32_t shader_model = 30; // TODO: map ps_4_0_level_9_0,... somehow
bool fixup_clipspace = false;
bool flip_vert_y = false;
};
CompilerHLSL(std::vector<uint32_t> spirv_)
: CompilerGLSL(move(spirv_))
{
}
const Options &get_options() const
{
return options;
}
void set_options(Options &opts)
{
options = opts;
}
std::string compile() override;
private:
std::string type_to_glsl(const SPIRType &type) override;
void emit_function_prototype(SPIRFunction &func, uint64_t return_flags) override;
void emit_hlsl_entry_point();
void emit_header() override;
void emit_resources();
void emit_interface_block_globally(const SPIRVariable &type);
void emit_interface_block_in_struct(const SPIRVariable &type, uint32_t &binding_number, bool builtins);
void emit_texture_op(const Instruction &i) override;
void emit_instruction(const Instruction &instruction) override;
void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
uint32_t count) override;
void emit_buffer_block(const SPIRVariable &type) override;
void emit_push_constant_block(const SPIRVariable &var) override;
void emit_uniform(const SPIRVariable &var) override;
Options options;
bool requires_op_fmod = false;
};
}
#endif

View File

@ -432,16 +432,16 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage)
// particularly if the offsets are all equal.
MemberSorter::SortAspect sort_aspect =
(storage == StorageClassInput) ? MemberSorter::LocationReverse : MemberSorter::Location;
MemberSorter memberSorter(ib_type, meta[ib_type_id], sort_aspect);
memberSorter.sort();
MemberSorter member_sorter(ib_type, meta[ib_type_id], sort_aspect);
member_sorter.sort();
// Sort input or output variables alphabetical
auto &execution = get_entry_point();
if ((execution.model == ExecutionModelFragment && storage == StorageClassInput) ||
(execution.model == ExecutionModelVertex && storage == StorageClassOutput))
{
MemberSorter memberSorter(ib_type, meta[ib_type.self], MemberSorter::Alphabetical);
memberSorter.sort();
MemberSorter member_sorter_io(ib_type, meta[ib_type.self], MemberSorter::Alphabetical);
member_sorter_io.sort();
}
return ib_var_id;

View File

@ -81,6 +81,25 @@ def cross_compile_msl(shader):
return (spirv_path, msl_path)
def validate_shader_hlsl(shader):
subprocess.check_call(['glslangValidator', '-e', 'main', '-D', '-V', shader])
if os.path.exists('fxc'):
subprocess.check_call(['fxc', shader])
def cross_compile_hlsl(shader):
spirv_f, spirv_path = tempfile.mkstemp()
hlsl_f, hlsl_path = tempfile.mkstemp(suffix = os.path.basename(shader))
os.close(spirv_f)
os.close(hlsl_f)
subprocess.check_call(['glslangValidator', '-V', '-o', spirv_path, shader])
spirv_cross_path = './spirv-cross'
subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', hlsl_path, spirv_path, '--hlsl'])
subprocess.check_call(['spirv-val', spirv_path])
validate_shader_hlsl(hlsl_path)
return (spirv_path, hlsl_path)
def validate_shader(shader, vulkan):
if vulkan:
subprocess.check_call(['glslangValidator', '-V', shader])
@ -240,6 +259,13 @@ def test_shader_msl(stats, shader, update, keep):
regression_check(shader, msl, update, keep)
os.remove(spirv)
def test_shader_hlsl(stats, shader, update, keep):
joined_path = os.path.join(shader[0], shader[1])
print('Testing HLSL shader:', joined_path)
spirv, msl = cross_compile_hlsl(joined_path)
regression_check(shader, msl, update, keep)
os.remove(spirv)
def test_shaders_helper(stats, shader_dir, update, malisc, keep, backend):
for root, dirs, files in os.walk(os.path.join(shader_dir)):
for i in files:
@ -247,6 +273,8 @@ def test_shaders_helper(stats, shader_dir, update, malisc, keep, backend):
relpath = os.path.relpath(path, shader_dir)
if backend == 'metal':
test_shader_msl(stats, (shader_dir, relpath), update, keep)
elif backend == 'hlsl':
test_shader_hlsl(stats, (shader_dir, relpath), update, keep)
else:
test_shader(stats, (shader_dir, relpath), update, keep)
@ -274,6 +302,9 @@ def main():
parser.add_argument('--metal',
action = 'store_true',
help = 'Test Metal backend.')
parser.add_argument('--hlsl',
action = 'store_true',
help = 'Test HLSL backend.')
args = parser.parse_args()
if not args.folder:
@ -283,7 +314,7 @@ def main():
if os.path.exists(METALC):
subprocess.check_call([METALC, '--version'])
test_shaders(args.folder, args.update, args.malisc, args.keep, 'metal' if args.metal else 'glsl')
test_shaders(args.folder, args.update, args.malisc, args.keep, 'metal' if args.metal else ('hlsl' if args.hlsl else 'glsl'))
if args.malisc:
print('Stats in stats.csv!')
print('Tests completed!')