Expression are a bit different to translate from HLSL to PS5.
Consider the folllowing HLSL code snippet:
RayDesc r;
float3 x = r.Orgin.x;
On the Playstation 5 we can run into scenarios, Where for example, the 'r.Origin.x' needs to be translated into something like 'r.position.x';
Obviously this is an example and I can not share the actual variable names of the PS5 shader language.
Our parser parses a field expression (r.Origin.x) into a tree that looks like this:
.
/ \
r .
/ \
Org X
What I do to detect these HLSL names that need to be translated I walk the tree top-down.
We check the left-hand side first, In this case we first encounter the variable 'r', We keep canonical a record
of all variables declarations in this scope. So we know that variable 'r' is of type RayDesc. We now know that
this expression belongs to the RayDesc struct so we put a tag on the AST so that the converter backend knows
that it needs to be translated.
// Is the lefthand side of the field expression a value? (example: 'r')
if (field->left->expr_type == EXPR_TYPE_VALUE) {
Expr_value *value = static_cast(field->left);
// See if we can find the string value of the expression in the variable record.
if (record->variables.find(std::string(value->string_value)) != record->variables.end()) {
Ast_var_decl *decl = record->variables[std::string(value->string_value)];
// If the variable type is a indentifier, aka of a struct type.
if (decl->variable_type == TOKEN_TYPE_IDENTIFIER) {
// We find the struct type in a lookup hash table.
Struct_description *struct_description = find_struct_description(decl->struct_type_name);
// If it is a HLSL type that can be translated we put a little tag on it with the HLSL struct type name.
// so that the converter backend can generate the correct names for this expression.
if (struct_description) {
field->builtin_type_reference = decl->struct_type_name;
}
}
}
}
When generating the PS5 shader and we need to emit a field expression we check if we have
a builtin_type_reference set.
Struct_description *struct_description = find_struct_description(field->builtin_type_reference);
if (struct_description) {
// If we have a type reference, we emit the left side of the expression ('r' in our little example.)
// The right side of the field expression wil be emitted by a 'fix up' function which
// will walk down the field expression further emitting the correct PS5 names.
result += emit_expr(field->left) + "." + fix_up_field_expr_names(field->right, struct_description);
}
The fix_up_field_expr_names function is quite large covering a large amout of AST types (such as: values, field expression, function calls, paramaters etc)
This is a little example of how normal values are translated. example (r.Origin)
if (expr->expr_type == EXPR_TYPE_VALUE) {
Expr_value *v = static_cast(expr);
// We check if we can find the right side string value ('Origin' in our example) inside of the
// struct description
Member_description *member = find_member_desc_from_struct_desc(struct_desc, v->string_value);
if (member) {
// Emit the PS5 name to the result.
result += member->playstation_name;
}
else {
// If not, we have an expression which contains a name that has not been registered yet.
result += emit_expr(v);
}
}