2016-08-14 17:21:43 +03:00
|
|
|
/*
|
|
|
|
* Copyright 2016 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "spirv_hlsl.hpp"
|
2016-08-18 13:40:35 +03:00
|
|
|
#include "GLSL.std.450.h"
|
2016-08-15 21:33:10 +03:00
|
|
|
#include <algorithm>
|
2016-08-14 17:21:43 +03:00
|
|
|
|
|
|
|
using namespace spv;
|
|
|
|
using namespace spirv_cross;
|
|
|
|
using namespace std;
|
|
|
|
|
2016-08-15 21:33:10 +03:00
|
|
|
namespace
|
|
|
|
{
|
2016-08-18 01:51:12 +03:00
|
|
|
struct VariableComparator {
|
|
|
|
VariableComparator(const std::vector<Meta>& meta) : meta(meta) { }
|
|
|
|
|
|
|
|
bool operator () (SPIRVariable* var1, SPIRVariable* var2)
|
|
|
|
{
|
|
|
|
return meta[var1->self].decoration.alias.compare(meta[var2->self].decoration.alias) < 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::vector<Meta>& meta;
|
2016-08-15 21:33:10 +03:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-08-14 21:28:52 +03:00
|
|
|
string CompilerHLSL::type_to_glsl(const SPIRType &type)
|
|
|
|
{
|
|
|
|
// Ignore the pointer type since GLSL doesn't have pointers.
|
|
|
|
|
|
|
|
switch (type.basetype)
|
|
|
|
{
|
|
|
|
case SPIRType::Struct:
|
|
|
|
// Need OpName lookup here to get a "sensible" name for a struct.
|
|
|
|
if (backend.explicit_struct_type)
|
|
|
|
return join("struct ", to_name(type.self));
|
|
|
|
else
|
|
|
|
return to_name(type.self);
|
|
|
|
|
|
|
|
case SPIRType::Image:
|
|
|
|
case SPIRType::SampledImage:
|
|
|
|
return image_type_glsl(type);
|
|
|
|
|
|
|
|
case SPIRType::Sampler:
|
|
|
|
// Not really used.
|
|
|
|
return "sampler";
|
|
|
|
|
|
|
|
case SPIRType::Void:
|
|
|
|
return "void";
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type.vecsize == 1 && type.columns == 1) // Scalar builtin
|
|
|
|
{
|
|
|
|
switch (type.basetype)
|
|
|
|
{
|
|
|
|
case SPIRType::Boolean:
|
|
|
|
return "bool";
|
|
|
|
case SPIRType::Int:
|
|
|
|
return backend.basic_int_type;
|
|
|
|
case SPIRType::UInt:
|
|
|
|
return backend.basic_uint_type;
|
|
|
|
case SPIRType::AtomicCounter:
|
|
|
|
return "atomic_uint";
|
|
|
|
case SPIRType::Float:
|
|
|
|
return "float";
|
|
|
|
case SPIRType::Double:
|
|
|
|
return "double";
|
|
|
|
case SPIRType::Int64:
|
|
|
|
return "int64_t";
|
|
|
|
case SPIRType::UInt64:
|
|
|
|
return "uint64_t";
|
|
|
|
default:
|
|
|
|
return "???";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type.vecsize > 1 && type.columns == 1) // Vector builtin
|
|
|
|
{
|
|
|
|
switch (type.basetype)
|
|
|
|
{
|
|
|
|
case SPIRType::Boolean:
|
|
|
|
return join("bfloat", type.vecsize);
|
|
|
|
case SPIRType::Int:
|
|
|
|
return join("ifloat", type.vecsize);
|
|
|
|
case SPIRType::UInt:
|
|
|
|
return join("ufloat", type.vecsize);
|
|
|
|
case SPIRType::Float:
|
|
|
|
return join("float", type.vecsize);
|
|
|
|
case SPIRType::Double:
|
|
|
|
return join("double", type.vecsize);
|
|
|
|
case SPIRType::Int64:
|
|
|
|
return join("i64vec", type.vecsize);
|
|
|
|
case SPIRType::UInt64:
|
|
|
|
return join("u64vec", type.vecsize);
|
|
|
|
default:
|
|
|
|
return "???";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (type.basetype)
|
|
|
|
{
|
|
|
|
case SPIRType::Boolean:
|
2016-08-15 00:54:51 +03:00
|
|
|
return join("bool", type.columns, "x", type.vecsize);
|
2016-08-14 21:28:52 +03:00
|
|
|
case SPIRType::Int:
|
2016-08-15 00:54:51 +03:00
|
|
|
return join("int", type.columns, "x", type.vecsize);
|
2016-08-14 21:28:52 +03:00
|
|
|
case SPIRType::UInt:
|
2016-08-15 00:54:51 +03:00
|
|
|
return join("uint", type.columns, "x", type.vecsize);
|
2016-08-14 21:28:52 +03:00
|
|
|
case SPIRType::Float:
|
2016-08-15 00:54:51 +03:00
|
|
|
return join("float", type.columns, "x", type.vecsize);
|
2016-08-14 21:28:52 +03:00
|
|
|
case SPIRType::Double:
|
2016-08-15 00:54:51 +03:00
|
|
|
return join("double", type.columns, "x", type.vecsize);
|
2016-08-14 23:02:38 +03:00
|
|
|
// Matrix types not supported for int64/uint64.
|
2016-08-14 21:28:52 +03:00
|
|
|
default:
|
|
|
|
return "???";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-14 18:58:56 +03:00
|
|
|
void CompilerHLSL::emit_header()
|
|
|
|
{
|
|
|
|
auto &execution = get_entry_point();
|
|
|
|
|
|
|
|
for (auto &header : header_lines)
|
|
|
|
statement(header);
|
2016-08-14 23:02:38 +03:00
|
|
|
|
2016-08-18 13:54:22 +03:00
|
|
|
if (header_lines.size() > 0)
|
|
|
|
{
|
|
|
|
statement("");
|
|
|
|
}
|
2016-08-14 18:58:56 +03:00
|
|
|
}
|
|
|
|
|
2016-08-14 22:33:32 +03:00
|
|
|
void CompilerHLSL::emit_interface_block_globally(const SPIRVariable &var)
|
|
|
|
{
|
|
|
|
auto &execution = get_entry_point();
|
|
|
|
auto &type = get<SPIRType>(var.basetype);
|
|
|
|
|
|
|
|
add_resource_name(var.self);
|
|
|
|
statement("static ", variable_decl(var), ";");
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, uint32_t &binding_number)
|
2016-08-14 18:58:56 +03:00
|
|
|
{
|
|
|
|
auto &execution = get_entry_point();
|
|
|
|
auto &type = get<SPIRType>(var.basetype);
|
|
|
|
|
2016-08-14 23:02:38 +03:00
|
|
|
const char *binding = "TEXCOORD";
|
2016-08-15 00:09:06 +03:00
|
|
|
bool use_binding_number = true;
|
|
|
|
if (execution.model == ExecutionModelFragment && var.storage == StorageClassOutput)
|
2016-08-14 18:58:56 +03:00
|
|
|
{
|
|
|
|
binding = "COLOR";
|
2016-08-15 00:09:06 +03:00
|
|
|
use_binding_number = false;
|
2016-08-14 18:58:56 +03:00
|
|
|
}
|
2016-08-15 00:54:51 +03:00
|
|
|
else if (execution.model == ExecutionModelVertex && var.storage == StorageClassOutput && is_builtin_variable(var))
|
|
|
|
{
|
2016-08-18 01:51:12 +03:00
|
|
|
if (options.shader_model <= 30)
|
|
|
|
{
|
|
|
|
binding = "POSITION";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
binding = "SV_POSITION";
|
|
|
|
}
|
2016-08-15 00:54:51 +03:00
|
|
|
use_binding_number = false;
|
|
|
|
}
|
2016-08-14 23:02:38 +03:00
|
|
|
|
2016-08-14 22:33:32 +03:00
|
|
|
auto &m = meta[var.self].decoration;
|
2016-08-15 00:09:06 +03:00
|
|
|
if (use_binding_number)
|
|
|
|
{
|
|
|
|
statement(variable_decl(type, m.alias), " : ", binding, binding_number, ";");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
statement(variable_decl(type, m.alias), " : ", binding, ";");
|
|
|
|
}
|
2016-08-14 18:58:56 +03:00
|
|
|
|
|
|
|
if (!is_builtin_variable(var) && !var.remapped_variable)
|
|
|
|
{
|
|
|
|
++binding_number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompilerHLSL::emit_resources()
|
|
|
|
{
|
|
|
|
auto &execution = get_entry_point();
|
2016-08-14 23:02:38 +03:00
|
|
|
|
2016-08-14 18:58:56 +03:00
|
|
|
// Emit PLS blocks if we have such variables.
|
|
|
|
if (!pls_inputs.empty() || !pls_outputs.empty())
|
|
|
|
emit_pls();
|
|
|
|
|
|
|
|
// Output all basic struct types which are not Block or BufferBlock as these are declared inplace
|
|
|
|
// when such variables are instantiated.
|
|
|
|
for (auto &id : ids)
|
|
|
|
{
|
|
|
|
if (id.get_type() == TypeType)
|
|
|
|
{
|
|
|
|
auto &type = id.get<SPIRType>();
|
|
|
|
if (type.basetype == SPIRType::Struct && type.array.empty() && !type.pointer &&
|
2016-08-14 23:02:38 +03:00
|
|
|
(meta[type.self].decoration.decoration_flags &
|
|
|
|
((1ull << DecorationBlock) | (1ull << DecorationBufferBlock))) == 0)
|
2016-08-14 18:58:56 +03:00
|
|
|
{
|
|
|
|
emit_struct(type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-14 23:02:38 +03:00
|
|
|
|
2016-08-14 18:58:56 +03:00
|
|
|
bool emitted = false;
|
|
|
|
|
2016-08-18 01:51:12 +03:00
|
|
|
if (execution.model == ExecutionModelVertex && options.shader_model <= 30)
|
2016-08-16 01:27:39 +03:00
|
|
|
{
|
|
|
|
statement("uniform float4 gl_HalfPixel;");
|
|
|
|
emitted = true;
|
|
|
|
}
|
|
|
|
|
2016-08-14 18:58:56 +03:00
|
|
|
// Output Uniform Constants (values, samplers, images, etc).
|
|
|
|
for (auto &id : ids)
|
|
|
|
{
|
|
|
|
if (id.get_type() == TypeVariable)
|
|
|
|
{
|
|
|
|
auto &var = id.get<SPIRVariable>();
|
|
|
|
auto &type = get<SPIRType>(var.basetype);
|
|
|
|
|
|
|
|
if (var.storage != StorageClassFunction && !is_builtin_variable(var) && !var.remapped_variable &&
|
2016-08-14 23:02:38 +03:00
|
|
|
type.pointer &&
|
|
|
|
(type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter))
|
2016-08-14 18:58:56 +03:00
|
|
|
{
|
|
|
|
emit_uniform(var);
|
|
|
|
emitted = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (emitted)
|
|
|
|
statement("");
|
|
|
|
emitted = false;
|
|
|
|
|
2016-08-14 22:33:32 +03:00
|
|
|
for (auto &id : ids)
|
|
|
|
{
|
|
|
|
if (id.get_type() == TypeVariable)
|
|
|
|
{
|
|
|
|
auto &var = id.get<SPIRVariable>();
|
|
|
|
auto &type = get<SPIRType>(var.basetype);
|
|
|
|
|
2016-08-14 23:02:38 +03:00
|
|
|
if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
|
|
|
|
(var.storage == StorageClassInput || var.storage == StorageClassOutput) &&
|
|
|
|
interface_variable_exists_in_entry_point(var.self))
|
2016-08-14 22:33:32 +03:00
|
|
|
{
|
|
|
|
emit_interface_block_globally(var);
|
|
|
|
emitted = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (emitted)
|
|
|
|
statement("");
|
|
|
|
emitted = false;
|
|
|
|
|
2016-08-14 18:58:56 +03:00
|
|
|
if (execution.model == ExecutionModelVertex)
|
|
|
|
{
|
|
|
|
statement("struct InputVert");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
statement("struct InputFrag");
|
|
|
|
}
|
|
|
|
begin_scope();
|
|
|
|
uint32_t binding_number = 0;
|
2016-08-15 21:33:10 +03:00
|
|
|
std::vector<SPIRVariable*> variables;
|
2016-08-14 18:58:56 +03:00
|
|
|
for (auto &id : ids)
|
|
|
|
{
|
|
|
|
if (id.get_type() == TypeVariable)
|
|
|
|
{
|
|
|
|
auto &var = id.get<SPIRVariable>();
|
|
|
|
auto &type = get<SPIRType>(var.basetype);
|
|
|
|
|
2016-08-14 23:02:38 +03:00
|
|
|
if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
|
|
|
|
var.storage == StorageClassInput && interface_variable_exists_in_entry_point(var.self))
|
2016-08-14 18:58:56 +03:00
|
|
|
{
|
2016-08-15 21:33:10 +03:00
|
|
|
variables.push_back(&var);
|
2016-08-14 18:58:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-15 21:33:10 +03:00
|
|
|
sort(variables.begin(), variables.end(), VariableComparator(meta));
|
|
|
|
for (auto var : variables)
|
|
|
|
{
|
|
|
|
emit_interface_block_in_struct(*var, binding_number);
|
|
|
|
}
|
2016-08-14 18:58:56 +03:00
|
|
|
end_scope_decl();
|
|
|
|
statement("");
|
|
|
|
|
|
|
|
if (execution.model == ExecutionModelVertex)
|
|
|
|
{
|
|
|
|
statement("struct OutputVert");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
statement("struct OutputFrag");
|
|
|
|
}
|
|
|
|
begin_scope();
|
|
|
|
binding_number = 0;
|
2016-08-15 21:33:10 +03:00
|
|
|
variables.clear();
|
2016-08-14 18:58:56 +03:00
|
|
|
for (auto &id : ids)
|
|
|
|
{
|
|
|
|
if (id.get_type() == TypeVariable)
|
|
|
|
{
|
|
|
|
auto &var = id.get<SPIRVariable>();
|
|
|
|
auto &type = get<SPIRType>(var.basetype);
|
|
|
|
|
2016-08-14 23:02:38 +03:00
|
|
|
if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
|
|
|
|
var.storage == StorageClassOutput && interface_variable_exists_in_entry_point(var.self))
|
2016-08-14 18:58:56 +03:00
|
|
|
{
|
2016-08-15 21:33:10 +03:00
|
|
|
variables.push_back(&var);
|
2016-08-14 18:58:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-15 21:33:10 +03:00
|
|
|
sort(variables.begin(), variables.end(), VariableComparator(meta));
|
|
|
|
for (auto var : variables)
|
|
|
|
{
|
|
|
|
emit_interface_block_in_struct(*var, binding_number);
|
|
|
|
}
|
2016-08-14 18:58:56 +03:00
|
|
|
end_scope_decl();
|
|
|
|
statement("");
|
|
|
|
|
|
|
|
// Global variables.
|
|
|
|
for (auto global : global_variables)
|
|
|
|
{
|
|
|
|
auto &var = get<SPIRVariable>(global);
|
|
|
|
if (var.storage != StorageClassOutput)
|
|
|
|
{
|
|
|
|
add_resource_name(var.self);
|
|
|
|
statement("static ", variable_decl(var), ";");
|
|
|
|
emitted = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (emitted)
|
|
|
|
statement("");
|
2016-08-18 13:54:22 +03:00
|
|
|
|
|
|
|
if (requires_op_fmod)
|
|
|
|
{
|
|
|
|
statement("float mod(float x, float y)");
|
|
|
|
begin_scope();
|
|
|
|
statement("return x - y * floor(x / y);");
|
|
|
|
end_scope();
|
|
|
|
statement("");
|
|
|
|
}
|
2016-08-14 18:58:56 +03:00
|
|
|
}
|
|
|
|
|
2016-08-14 22:33:32 +03:00
|
|
|
void CompilerHLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_flags)
|
|
|
|
{
|
|
|
|
auto &execution = get_entry_point();
|
|
|
|
// Avoid shadow declarations.
|
|
|
|
local_variable_names = resource_names;
|
|
|
|
|
|
|
|
string decl;
|
|
|
|
|
|
|
|
auto &type = get<SPIRType>(func.return_type);
|
|
|
|
decl += flags_to_precision_qualifiers_glsl(type, return_flags);
|
|
|
|
decl += type_to_glsl(type);
|
|
|
|
decl += " ";
|
|
|
|
|
|
|
|
if (func.self == entry_point)
|
|
|
|
{
|
|
|
|
if (execution.model == ExecutionModelVertex)
|
|
|
|
{
|
|
|
|
decl += "vert_main";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
decl += "frag_main";
|
|
|
|
}
|
|
|
|
processing_entry_point = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
decl += to_name(func.self);
|
|
|
|
|
|
|
|
decl += "(";
|
|
|
|
for (auto &arg : func.arguments)
|
|
|
|
{
|
|
|
|
// Might change the variable name if it already exists in this function.
|
|
|
|
// SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation
|
|
|
|
// to use same name for variables.
|
|
|
|
// Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates.
|
|
|
|
add_local_variable_name(arg.id);
|
|
|
|
|
|
|
|
decl += argument_decl(arg);
|
|
|
|
if (&arg != &func.arguments.back())
|
|
|
|
decl += ", ";
|
|
|
|
|
|
|
|
// Hold a pointer to the parameter so we can invalidate the readonly field if needed.
|
|
|
|
auto *var = maybe_get<SPIRVariable>(arg.id);
|
|
|
|
if (var)
|
|
|
|
var->parameter = &arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
decl += ")";
|
|
|
|
statement(decl);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompilerHLSL::emit_hlsl_entry_point()
|
|
|
|
{
|
|
|
|
auto &execution = get_entry_point();
|
2016-08-14 23:02:38 +03:00
|
|
|
const char *post = "Frag";
|
2016-08-14 22:33:32 +03:00
|
|
|
if (execution.model == ExecutionModelVertex)
|
|
|
|
{
|
|
|
|
post = "Vert";
|
|
|
|
}
|
|
|
|
|
|
|
|
statement("Output", post, " main(Input", post, " input)");
|
|
|
|
begin_scope();
|
|
|
|
|
|
|
|
for (auto &id : ids)
|
|
|
|
{
|
|
|
|
if (id.get_type() == TypeVariable)
|
|
|
|
{
|
|
|
|
auto &var = id.get<SPIRVariable>();
|
|
|
|
auto &type = get<SPIRType>(var.basetype);
|
|
|
|
|
2016-08-14 23:02:38 +03:00
|
|
|
if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
|
|
|
|
var.storage == StorageClassInput && interface_variable_exists_in_entry_point(var.self))
|
2016-08-14 22:33:32 +03:00
|
|
|
{
|
|
|
|
auto &m = meta[var.self].decoration;
|
|
|
|
statement(m.alias, " = input.", m.alias, ";");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (execution.model == ExecutionModelVertex)
|
|
|
|
{
|
|
|
|
statement("vert_main();");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
statement("frag_main();");
|
|
|
|
}
|
|
|
|
|
|
|
|
statement("Output", post, " output;");
|
|
|
|
|
|
|
|
for (auto &id : ids)
|
|
|
|
{
|
|
|
|
if (id.get_type() == TypeVariable)
|
|
|
|
{
|
|
|
|
auto &var = id.get<SPIRVariable>();
|
|
|
|
auto &type = get<SPIRType>(var.basetype);
|
|
|
|
|
2016-08-14 23:02:38 +03:00
|
|
|
if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
|
|
|
|
var.storage == StorageClassOutput && interface_variable_exists_in_entry_point(var.self))
|
2016-08-14 22:33:32 +03:00
|
|
|
{
|
|
|
|
auto &m = meta[var.self].decoration;
|
|
|
|
statement("output.", m.alias, " = ", m.alias, ";");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-16 01:27:39 +03:00
|
|
|
if (execution.model == ExecutionModelVertex)
|
|
|
|
{
|
2016-08-18 01:51:12 +03:00
|
|
|
if (options.shader_model <= 30)
|
|
|
|
{
|
|
|
|
statement("output.gl_Position.x = output.gl_Position.x - gl_HalfPixel.x * output.gl_Position.w;");
|
|
|
|
statement("output.gl_Position.y = output.gl_Position.y + gl_HalfPixel.y * output.gl_Position.w;");
|
|
|
|
}
|
2016-08-16 01:27:39 +03:00
|
|
|
statement("output.gl_Position.z = (output.gl_Position.z + output.gl_Position.w) * 0.5;");
|
|
|
|
}
|
|
|
|
|
2016-08-14 22:33:32 +03:00
|
|
|
statement("return output;");
|
|
|
|
|
|
|
|
end_scope();
|
2016-08-15 00:09:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void CompilerHLSL::emit_texture_op(const Instruction &i)
|
|
|
|
{
|
|
|
|
auto ops = stream(i);
|
|
|
|
auto op = static_cast<Op>(i.op);
|
|
|
|
uint32_t length = i.length;
|
|
|
|
|
|
|
|
if (i.offset + length > spirv.size())
|
|
|
|
throw CompilerError("Compiler::parse() opcode out of range.");
|
|
|
|
|
|
|
|
uint32_t result_type = ops[0];
|
|
|
|
uint32_t id = ops[1];
|
|
|
|
uint32_t img = ops[2];
|
|
|
|
uint32_t coord = ops[3];
|
|
|
|
uint32_t dref = 0;
|
|
|
|
uint32_t comp = 0;
|
|
|
|
bool gather = false;
|
|
|
|
bool proj = false;
|
|
|
|
const uint32_t *opt = nullptr;
|
|
|
|
|
|
|
|
switch (op)
|
|
|
|
{
|
|
|
|
case OpImageSampleDrefImplicitLod:
|
|
|
|
case OpImageSampleDrefExplicitLod:
|
|
|
|
dref = ops[4];
|
|
|
|
opt = &ops[5];
|
|
|
|
length -= 5;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OpImageSampleProjDrefImplicitLod:
|
|
|
|
case OpImageSampleProjDrefExplicitLod:
|
|
|
|
dref = ops[4];
|
|
|
|
proj = true;
|
|
|
|
opt = &ops[5];
|
|
|
|
length -= 5;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OpImageDrefGather:
|
|
|
|
dref = ops[4];
|
|
|
|
opt = &ops[5];
|
|
|
|
gather = true;
|
|
|
|
length -= 5;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OpImageGather:
|
|
|
|
comp = ops[4];
|
|
|
|
opt = &ops[5];
|
|
|
|
gather = true;
|
|
|
|
length -= 5;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OpImageSampleProjImplicitLod:
|
|
|
|
case OpImageSampleProjExplicitLod:
|
|
|
|
opt = &ops[4];
|
|
|
|
length -= 4;
|
|
|
|
proj = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
opt = &ops[4];
|
|
|
|
length -= 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto &imgtype = expression_type(img);
|
|
|
|
uint32_t coord_components = 0;
|
|
|
|
switch (imgtype.image.dim)
|
|
|
|
{
|
|
|
|
case spv::Dim1D:
|
|
|
|
coord_components = 1;
|
|
|
|
break;
|
|
|
|
case spv::Dim2D:
|
|
|
|
coord_components = 2;
|
|
|
|
break;
|
|
|
|
case spv::Dim3D:
|
|
|
|
coord_components = 3;
|
|
|
|
break;
|
|
|
|
case spv::DimCube:
|
|
|
|
coord_components = 3;
|
|
|
|
break;
|
|
|
|
case spv::DimBuffer:
|
|
|
|
coord_components = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
coord_components = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (proj)
|
|
|
|
coord_components++;
|
|
|
|
if (imgtype.image.arrayed)
|
|
|
|
coord_components++;
|
|
|
|
|
|
|
|
uint32_t bias = 0;
|
|
|
|
uint32_t lod = 0;
|
|
|
|
uint32_t grad_x = 0;
|
|
|
|
uint32_t grad_y = 0;
|
|
|
|
uint32_t coffset = 0;
|
|
|
|
uint32_t offset = 0;
|
|
|
|
uint32_t coffsets = 0;
|
|
|
|
uint32_t sample = 0;
|
|
|
|
uint32_t flags = 0;
|
|
|
|
|
|
|
|
if (length)
|
|
|
|
{
|
|
|
|
flags = opt[0];
|
|
|
|
opt++;
|
|
|
|
length--;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto test = [&](uint32_t &v, uint32_t flag) {
|
|
|
|
if (length && (flags & flag))
|
|
|
|
{
|
|
|
|
v = *opt++;
|
|
|
|
length--;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
test(bias, ImageOperandsBiasMask);
|
|
|
|
test(lod, ImageOperandsLodMask);
|
|
|
|
test(grad_x, ImageOperandsGradMask);
|
|
|
|
test(grad_y, ImageOperandsGradMask);
|
|
|
|
test(coffset, ImageOperandsConstOffsetMask);
|
|
|
|
test(offset, ImageOperandsOffsetMask);
|
|
|
|
test(coffsets, ImageOperandsConstOffsetsMask);
|
|
|
|
test(sample, ImageOperandsSampleMask);
|
|
|
|
|
|
|
|
string expr;
|
|
|
|
string texop;
|
|
|
|
|
|
|
|
if (op == OpImageFetch)
|
|
|
|
texop += "texelFetch";
|
|
|
|
else
|
|
|
|
{
|
|
|
|
texop += "tex2D";
|
|
|
|
|
|
|
|
if (gather)
|
|
|
|
texop += "Gather";
|
|
|
|
if (coffsets)
|
|
|
|
texop += "Offsets";
|
|
|
|
if (proj)
|
|
|
|
texop += "Proj";
|
|
|
|
if (grad_x || grad_y)
|
|
|
|
texop += "Grad";
|
|
|
|
if (lod)
|
|
|
|
texop += "Lod";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (coffset || offset)
|
|
|
|
texop += "Offset";
|
|
|
|
|
|
|
|
if (is_legacy())
|
|
|
|
texop = legacy_tex_op(texop, imgtype);
|
|
|
|
|
|
|
|
expr += texop;
|
|
|
|
expr += "(";
|
|
|
|
expr += to_expression(img);
|
|
|
|
|
|
|
|
bool swizz_func = backend.swizzle_is_function;
|
|
|
|
auto swizzle = [swizz_func](uint32_t comps, uint32_t in_comps) -> const char * {
|
|
|
|
if (comps == in_comps)
|
|
|
|
return "";
|
|
|
|
|
|
|
|
switch (comps)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
return ".x";
|
|
|
|
case 2:
|
|
|
|
return swizz_func ? ".xy()" : ".xy";
|
|
|
|
case 3:
|
|
|
|
return swizz_func ? ".xyz()" : ".xyz";
|
|
|
|
default:
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
bool forward = should_forward(coord);
|
|
|
|
|
|
|
|
// The IR can give us more components than we need, so chop them off as needed.
|
|
|
|
auto coord_expr = to_expression(coord) + swizzle(coord_components, expression_type(coord).vecsize);
|
|
|
|
|
|
|
|
// TODO: implement rest ... A bit intensive.
|
|
|
|
|
|
|
|
if (dref)
|
|
|
|
{
|
|
|
|
forward = forward && should_forward(dref);
|
|
|
|
|
|
|
|
// SPIR-V splits dref and coordinate.
|
|
|
|
if (coord_components == 4) // GLSL also splits the arguments in two.
|
|
|
|
{
|
|
|
|
expr += ", ";
|
|
|
|
expr += to_expression(coord);
|
|
|
|
expr += ", ";
|
|
|
|
expr += to_expression(dref);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Create a composite which merges coord/dref into a single vector.
|
|
|
|
auto type = expression_type(coord);
|
|
|
|
type.vecsize = coord_components + 1;
|
|
|
|
expr += ", ";
|
|
|
|
expr += type_to_glsl_constructor(type);
|
|
|
|
expr += "(";
|
|
|
|
expr += coord_expr;
|
|
|
|
expr += ", ";
|
|
|
|
expr += to_expression(dref);
|
|
|
|
expr += ")";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
expr += ", ";
|
|
|
|
expr += coord_expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (grad_x || grad_y)
|
|
|
|
{
|
|
|
|
forward = forward && should_forward(grad_x);
|
|
|
|
forward = forward && should_forward(grad_y);
|
|
|
|
expr += ", ";
|
|
|
|
expr += to_expression(grad_x);
|
|
|
|
expr += ", ";
|
|
|
|
expr += to_expression(grad_y);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lod)
|
|
|
|
{
|
|
|
|
forward = forward && should_forward(lod);
|
|
|
|
expr += ", ";
|
|
|
|
expr += to_expression(lod);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (coffset)
|
|
|
|
{
|
|
|
|
forward = forward && should_forward(coffset);
|
|
|
|
expr += ", ";
|
|
|
|
expr += to_expression(coffset);
|
|
|
|
}
|
|
|
|
else if (offset)
|
|
|
|
{
|
|
|
|
forward = forward && should_forward(offset);
|
|
|
|
expr += ", ";
|
|
|
|
expr += to_expression(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bias)
|
|
|
|
{
|
|
|
|
forward = forward && should_forward(bias);
|
|
|
|
expr += ", ";
|
|
|
|
expr += to_expression(bias);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (comp)
|
|
|
|
{
|
|
|
|
forward = forward && should_forward(comp);
|
|
|
|
expr += ", ";
|
|
|
|
expr += to_expression(comp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sample)
|
|
|
|
{
|
|
|
|
expr += ", ";
|
|
|
|
expr += to_expression(sample);
|
|
|
|
}
|
|
|
|
|
|
|
|
expr += ")";
|
2016-08-14 22:33:32 +03:00
|
|
|
|
2016-08-15 00:09:06 +03:00
|
|
|
emit_op(result_type, id, expr, forward, false);
|
2016-08-14 22:33:32 +03:00
|
|
|
}
|
|
|
|
|
2016-08-15 00:54:51 +03:00
|
|
|
void CompilerHLSL::emit_binary_func_op_transpose_first(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
|
|
|
|
const char *op)
|
|
|
|
{
|
|
|
|
bool forward = should_forward(op0) && should_forward(op1);
|
|
|
|
emit_op(result_type, result_id, join(op, "(transpose(", to_expression(op0), "), ", to_expression(op1), ")"), forward, false);
|
|
|
|
|
|
|
|
if (forward && forced_temporaries.find(result_id) == end(forced_temporaries))
|
|
|
|
{
|
|
|
|
inherit_expression_dependencies(result_id, op0);
|
|
|
|
inherit_expression_dependencies(result_id, op1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-18 12:45:50 +03:00
|
|
|
void CompilerHLSL::emit_binary_func_op_transpose_second(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
|
|
|
|
const char *op)
|
|
|
|
{
|
|
|
|
bool forward = should_forward(op0) && should_forward(op1);
|
|
|
|
emit_op(result_type, result_id, join(op, "(", to_expression(op0), ", transpose(", to_expression(op1), "))"), forward, false);
|
|
|
|
|
|
|
|
if (forward && forced_temporaries.find(result_id) == end(forced_temporaries))
|
|
|
|
{
|
|
|
|
inherit_expression_dependencies(result_id, op0);
|
|
|
|
inherit_expression_dependencies(result_id, op1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompilerHLSL::emit_binary_func_op_transpose_all(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
|
|
|
|
const char *op)
|
|
|
|
{
|
|
|
|
bool forward = should_forward(op0) && should_forward(op1);
|
|
|
|
emit_op(result_type, result_id, join("transpose(", op, "(transpose(", to_expression(op0), "), transpose(", to_expression(op1), ")))"), forward, false);
|
|
|
|
|
|
|
|
if (forward && forced_temporaries.find(result_id) == end(forced_temporaries))
|
|
|
|
{
|
|
|
|
inherit_expression_dependencies(result_id, op0);
|
|
|
|
inherit_expression_dependencies(result_id, op1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-18 13:40:35 +03:00
|
|
|
void CompilerHLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t count)
|
|
|
|
{
|
|
|
|
GLSLstd450 op = static_cast<GLSLstd450>(eop);
|
|
|
|
|
|
|
|
switch (op)
|
|
|
|
{
|
|
|
|
case GLSLstd450InverseSqrt:
|
|
|
|
{
|
|
|
|
emit_unary_func_op(result_type, id, args[0], "rsqrt");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GLSLstd450Fract:
|
|
|
|
{
|
|
|
|
emit_unary_func_op(result_type, id, args[0], "frac");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GLSLstd450FMix:
|
|
|
|
case GLSLstd450IMix:
|
|
|
|
{
|
|
|
|
emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "lerp");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case GLSLstd450Atan2:
|
|
|
|
{
|
|
|
|
emit_binary_func_op(result_type, id, args[1], args[0], "atan2");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-15 00:54:51 +03:00
|
|
|
void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
|
|
|
{
|
|
|
|
auto ops = stream(instruction);
|
|
|
|
auto opcode = static_cast<Op>(instruction.op);
|
|
|
|
uint32_t length = instruction.length;
|
|
|
|
|
|
|
|
#define BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op)
|
|
|
|
#define BOP_CAST(op, type, skip_cast) emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, skip_cast)
|
|
|
|
#define UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op)
|
|
|
|
#define QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op)
|
|
|
|
#define TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op)
|
|
|
|
#define BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op)
|
|
|
|
#define BFOP_CAST(op, type, skip_cast) emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, skip_cast)
|
|
|
|
#define BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op)
|
|
|
|
#define UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op)
|
|
|
|
|
|
|
|
switch (opcode)
|
|
|
|
{
|
|
|
|
case OpMatrixTimesVector:
|
|
|
|
{
|
|
|
|
emit_binary_func_op_transpose_first(ops[0], ops[1], ops[2], ops[3], "mul");
|
|
|
|
break;
|
|
|
|
}
|
2016-08-18 12:45:50 +03:00
|
|
|
case OpVectorTimesMatrix:
|
|
|
|
{
|
|
|
|
emit_binary_func_op_transpose_second(ops[0], ops[1], ops[2], ops[3], "mul");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case OpMatrixTimesMatrix:
|
|
|
|
{
|
|
|
|
emit_binary_func_op_transpose_all(ops[0], ops[1], ops[2], ops[3], "mul");
|
|
|
|
break;
|
|
|
|
}
|
2016-08-18 13:54:22 +03:00
|
|
|
case OpFMod:
|
|
|
|
{
|
|
|
|
requires_op_fmod = true;
|
|
|
|
CompilerGLSL::emit_instruction(instruction);
|
|
|
|
break;
|
|
|
|
}
|
2016-08-15 00:54:51 +03:00
|
|
|
default:
|
|
|
|
CompilerGLSL::emit_instruction(instruction);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-14 17:21:43 +03:00
|
|
|
string CompilerHLSL::compile()
|
|
|
|
{
|
|
|
|
// Do not deal with ES-isms like precision, older extensions and such.
|
2016-08-18 01:51:12 +03:00
|
|
|
CompilerGLSL::options.es = false;
|
|
|
|
CompilerGLSL::options.version = 450;
|
2016-08-14 17:21:43 +03:00
|
|
|
backend.float_literal_suffix = true;
|
|
|
|
backend.double_literal_suffix = false;
|
|
|
|
backend.long_long_literal_suffix = true;
|
|
|
|
backend.uint32_t_literal_suffix = true;
|
2016-08-15 00:09:06 +03:00
|
|
|
backend.basic_int_type = "int";
|
|
|
|
backend.basic_uint_type = "uint";
|
|
|
|
backend.swizzle_is_function = false;
|
2016-08-14 17:21:43 +03:00
|
|
|
backend.shared_is_implied = true;
|
|
|
|
backend.flexible_member_array_supported = false;
|
|
|
|
backend.explicit_struct_type = true;
|
|
|
|
backend.use_initializer_list = true;
|
2016-09-23 19:57:18 +03:00
|
|
|
backend.use_constructor_splatting = false;
|
2016-08-14 23:02:38 +03:00
|
|
|
|
2016-08-14 17:21:43 +03:00
|
|
|
uint32_t pass_count = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (pass_count >= 3)
|
|
|
|
throw CompilerError("Over 3 compilation loops detected. Must be a bug!");
|
|
|
|
|
|
|
|
reset();
|
|
|
|
|
|
|
|
// Move constructor for this type is broken on GCC 4.9 ...
|
|
|
|
buffer = unique_ptr<ostringstream>(new ostringstream());
|
|
|
|
|
|
|
|
emit_header();
|
|
|
|
emit_resources();
|
|
|
|
|
|
|
|
emit_function(get<SPIRFunction>(entry_point), 0);
|
2016-08-14 22:33:32 +03:00
|
|
|
emit_hlsl_entry_point();
|
2016-08-14 17:21:43 +03:00
|
|
|
|
|
|
|
pass_count++;
|
|
|
|
} while (force_recompile);
|
|
|
|
|
|
|
|
return buffer->str();
|
|
|
|
}
|