You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Texture*Array.Load needs to split the array index out of the vector coordinate for Metal.
- The GetDimensions function needs to support the array-length/depth output argument.
- RemovePackedVarReferences was inserting new variables at the top of the function, not the same scope as the source variable, which won't compile in cases where structs are used as temporaries and not function arguments.
#rb none
#ROBOMERGE-OWNER: ryan.vance
#ROBOMERGE-AUTHOR: mark.satterthwaite
#ROBOMERGE-SOURCE: CL 5502462 via CL 5503115 via CL 5535524
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)
[CL 5538907 by mark satterthwaite in Dev-VR branch]
3604 lines
122 KiB
C++
3604 lines
122 KiB
C++
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MetalUtils.h"
|
|
#include "MetalShaderFormat.h"
|
|
#include "MetalBackend.h"
|
|
|
|
#include "hlslcc.h"
|
|
#include "hlslcc_private.h"
|
|
#include "compiler.h"
|
|
#include "MetalUtils.h"
|
|
|
|
PRAGMA_DISABLE_SHADOW_VARIABLE_WARNINGS
|
|
#include "glsl_parser_extras.h"
|
|
PRAGMA_ENABLE_SHADOW_VARIABLE_WARNINGS
|
|
|
|
#include "hash_table.h"
|
|
#include "ir_rvalue_visitor.h"
|
|
#include "ast.h"
|
|
#include "PackUniformBuffers.h"
|
|
#include "IRDump.h"
|
|
#include "OptValueNumbering.h"
|
|
#include "ir_optimization.h"
|
|
|
|
const bool bExpandVSInputsToFloat4 = false;
|
|
const bool bGenerateVSInputDummies = false;
|
|
|
|
static int GetIndexSuffix(const char* Prefix, int PrefixLength, const char* Semantic)
|
|
{
|
|
check(Semantic);
|
|
|
|
if (!strncmp(Semantic, Prefix, PrefixLength))
|
|
{
|
|
Semantic += PrefixLength;
|
|
int Index = 0;
|
|
if (isdigit((unsigned char)*Semantic))
|
|
{
|
|
Index = (*Semantic) - '0';
|
|
|
|
++Semantic;
|
|
if (*Semantic == 0 || !isdigit((unsigned char)*Semantic))
|
|
{
|
|
return Index;
|
|
}
|
|
else if (isdigit((unsigned char)*Semantic))
|
|
{
|
|
Index = Index * 10 + (*Semantic) - '0';
|
|
++Semantic;
|
|
if (*Semantic == 0 || !isdigit((unsigned char)*Semantic))
|
|
{
|
|
return Index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int GetAttributeIndex(const char* Semantic)
|
|
{
|
|
return GetIndexSuffix("ATTRIBUTE", 9, Semantic);
|
|
}
|
|
|
|
static int GetInAttributeIndex(const char* Semantic)
|
|
{
|
|
return GetIndexSuffix("[[ attribute(ATTRIBUTE", 22, Semantic);
|
|
}
|
|
|
|
static int GetVSHSInAttributeIndex(const char* Semantic)
|
|
{
|
|
return GetIndexSuffix("[[ attribute(", 13, Semantic);
|
|
}
|
|
|
|
const glsl_type* PromoteHalfToFloatType(_mesa_glsl_parse_state* state, const glsl_type* type)
|
|
{
|
|
if (type->base_type == GLSL_TYPE_HALF)
|
|
{
|
|
return glsl_type::get_instance(GLSL_TYPE_FLOAT, type->vector_elements, type->matrix_columns);
|
|
}
|
|
else if (type->is_array())
|
|
{
|
|
auto* ElementType = type->element_type();
|
|
auto* NewElementType = PromoteHalfToFloatType(state, ElementType);
|
|
if (NewElementType != ElementType)
|
|
{
|
|
return glsl_type::get_array_instance(NewElementType, type->length);
|
|
}
|
|
}
|
|
else if (type->is_record())
|
|
{
|
|
auto* Fields = ralloc_array(state, glsl_struct_field, type->length);
|
|
bool bNeedNewType = false;
|
|
for (unsigned i = 0; i < type->length; ++i)
|
|
{
|
|
auto* NewMemberType = PromoteHalfToFloatType(state, type->fields.structure[i].type);
|
|
Fields[i] = type->fields.structure[i];
|
|
if (NewMemberType != type->fields.structure[i].type)
|
|
{
|
|
bNeedNewType = true;
|
|
Fields[i].type = NewMemberType;
|
|
}
|
|
}
|
|
|
|
if (bNeedNewType)
|
|
{
|
|
auto* NewType = glsl_type::get_record_instance(Fields, type->length, ralloc_asprintf(state, "%s_F", type->name));
|
|
// Hack: This way we tell this is a uniform buffer and we need to emit 'packed_'
|
|
((glsl_type*)NewType)->HlslName = "__PACKED__";
|
|
state->AddUserStruct(NewType);
|
|
return NewType;
|
|
}
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
void CreateNewAssignmentsFloat2Half(_mesa_glsl_parse_state* State, exec_list& NewAssignments, ir_variable* NewVar, ir_rvalue* RValue)
|
|
{
|
|
if (NewVar->type->is_matrix())
|
|
{
|
|
for (uint32 i = 0; i < NewVar->type->matrix_columns; ++i)
|
|
{
|
|
auto* NewF2H = new(State)ir_expression(ir_unop_f2h, new(State)ir_dereference_array(RValue, new(State)ir_constant(i)));
|
|
auto* NewAssignment = new(State)ir_assignment(new(State)ir_dereference_array(NewVar, new(State)ir_constant(i)), NewF2H);
|
|
NewAssignments.push_tail(NewAssignment);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto* NewF2H = new(State)ir_expression(ir_unop_f2h, RValue);
|
|
auto* NewAssignment = new(State)ir_assignment(new(State)ir_dereference_variable(NewVar), NewF2H);
|
|
|
|
NewAssignments.push_tail(NewAssignment);
|
|
}
|
|
}
|
|
|
|
static void CreateNewAssignmentsHalf2Float(_mesa_glsl_parse_state* State, exec_list& NewAssignments, ir_variable* NewVar, ir_rvalue* RValue)
|
|
{
|
|
if (NewVar->type->is_matrix())
|
|
{
|
|
for (uint32 i = 0; i < NewVar->type->matrix_columns; ++i)
|
|
{
|
|
auto* NewF2H = new(State)ir_expression(ir_unop_h2f, new(State)ir_dereference_array(RValue, new(State)ir_constant(i)));
|
|
auto* NewAssignment = new(State)ir_assignment(new(State)ir_dereference_array(NewVar, new(State)ir_constant(i)), NewF2H);
|
|
NewAssignments.push_tail(NewAssignment);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto* NewF2H = new(State)ir_expression(ir_unop_h2f, RValue);
|
|
auto* NewAssignment = new(State)ir_assignment(new(State)ir_dereference_variable(NewVar), NewF2H);
|
|
|
|
NewAssignments.push_tail(NewAssignment);
|
|
}
|
|
}
|
|
|
|
const glsl_type* GetFragColorTypeFromMetalOutputStruct(const glsl_type* OutputType)
|
|
{
|
|
const glsl_type* FragColorType = glsl_type::error_type;
|
|
const glsl_type* FragDepthType = glsl_type::error_type;
|
|
if (OutputType && OutputType->base_type == GLSL_TYPE_STRUCT)
|
|
{
|
|
for (unsigned j = 0; j < OutputType->length; j++)
|
|
{
|
|
if (OutputType->fields.structure[j].semantic)
|
|
{
|
|
//@todo-rco: MRTs?
|
|
if (!strncmp(OutputType->fields.structure[j].semantic, "[[ color(", 9))
|
|
{
|
|
FragColorType = OutputType->fields.structure[j].type;
|
|
break;
|
|
}
|
|
else if (!strncmp(OutputType->fields.structure[j].semantic, "[[ depth(", 9))
|
|
{
|
|
FragDepthType = OutputType->fields.structure[j].type;
|
|
}
|
|
}
|
|
}
|
|
if (FragColorType == glsl_type::error_type)
|
|
{
|
|
FragColorType = FragDepthType;
|
|
}
|
|
}
|
|
return FragColorType;
|
|
}
|
|
|
|
namespace MetalUtils
|
|
{
|
|
/** Information on system values. */
|
|
struct FSystemValue
|
|
{
|
|
const char* HlslSemantic;
|
|
const glsl_type* Type;
|
|
const char* MetalName;
|
|
ir_variable_mode Mode;
|
|
const char* MetalSemantic;
|
|
};
|
|
|
|
/** Vertex shader system values. */
|
|
static FSystemValue VertexSystemValueTable[] =
|
|
{
|
|
{"SV_VertexID", glsl_type::uint_type, "IN_VertexID", ir_var_in, "[[ vertex_id ]]"},
|
|
{"SV_InstanceID", glsl_type::uint_type, "IN_InstanceID", ir_var_in, "[[ instance_id ]]"},
|
|
{"SV_Position", glsl_type::vec4_type, "Position", ir_var_out, "[[ position POS_INVARIANT ]]"},
|
|
{"SV_RenderTargetArrayIndex", glsl_type::uint_type, "OUT_Layer", ir_var_out, "[[ render_target_array_index ]]"},
|
|
{"SV_ViewPortArrayIndex", glsl_type::uint_type, "OUT_Viewport", ir_var_out, "[[ viewport_array_index ]]"},
|
|
{NULL, NULL, NULL, ir_var_auto, nullptr}
|
|
};
|
|
|
|
/** Pixel shader system values. */
|
|
static FSystemValue MobilePixelSystemValueTable[] =
|
|
{
|
|
{"SV_Depth", glsl_type::float_type, "FragDepth", ir_var_out, "[[ depth(any) ]]"},
|
|
{"SV_DepthLessEqual", glsl_type::float_type, "FragDepth", ir_var_out, "[[ depth(less) ]]"},
|
|
{"SV_Position", glsl_type::vec4_type, "IN_FragCoord", ir_var_in, "position"},
|
|
{"SV_IsFrontFace", glsl_type::bool_type, "IN_FrontFacing", ir_var_in, "[[ front_facing ]]"},
|
|
//{"SV_PrimitiveID", glsl_type::int_type, "IN_PrimitiveID", ir_var_in, "[[ ]]"},
|
|
//{"SV_RenderTargetArrayIndex", glsl_type::uint_type, "IN_Layer", ir_var_in, "[[ render_target_array_index ]]"},
|
|
//{"SV_ViewPortArrayIndex", glsl_type::uint_type, "IN_Viewport", ir_var_in, "[[ viewport_array_index ]]"},
|
|
{"SV_Target0", glsl_type::half4_type, "FragColor0", ir_var_out, "[[ color(0) ]]"},
|
|
{"SV_Target1", glsl_type::half4_type, "FragColor1", ir_var_out, "[[ color(1) ]]"},
|
|
{"SV_Target2", glsl_type::half4_type, "FragColor2", ir_var_out, "[[ color(2) ]]"},
|
|
{"SV_Target3", glsl_type::half4_type, "FragColor3", ir_var_out, "[[ color(3) ]]"},
|
|
{"SV_Target4", glsl_type::half4_type, "FragColor4", ir_var_out, "[[ color(4) ]]"},
|
|
{"SV_Target5", glsl_type::half4_type, "FragColor5", ir_var_out, "[[ color(5) ]]"},
|
|
{"SV_Target6", glsl_type::half4_type, "FragColor6", ir_var_out, "[[ color(6) ]]"},
|
|
{"SV_Target7", glsl_type::half4_type, "FragColor7", ir_var_out, "[[ color(7) ]]"},
|
|
{"SV_Target0", glsl_type::half3_type, "FragColor0", ir_var_out, "[[ color(0) ]]"},
|
|
{"SV_Target1", glsl_type::half3_type, "FragColor1", ir_var_out, "[[ color(1) ]]"},
|
|
{"SV_Target2", glsl_type::half3_type, "FragColor2", ir_var_out, "[[ color(2) ]]"},
|
|
{"SV_Target3", glsl_type::half3_type, "FragColor3", ir_var_out, "[[ color(3) ]]"},
|
|
{"SV_Target4", glsl_type::half3_type, "FragColor4", ir_var_out, "[[ color(4) ]]"},
|
|
{"SV_Target5", glsl_type::half3_type, "FragColor5", ir_var_out, "[[ color(5) ]]"},
|
|
{"SV_Target6", glsl_type::half3_type, "FragColor6", ir_var_out, "[[ color(6) ]]"},
|
|
{"SV_Target7", glsl_type::half3_type, "FragColor7", ir_var_out, "[[ color(7) ]]"},
|
|
{"SV_Target0", glsl_type::half2_type, "FragColor0", ir_var_out, "[[ color(0) ]]"},
|
|
{"SV_Target1", glsl_type::half2_type, "FragColor1", ir_var_out, "[[ color(1) ]]"},
|
|
{"SV_Target2", glsl_type::half2_type, "FragColor2", ir_var_out, "[[ color(2) ]]"},
|
|
{"SV_Target3", glsl_type::half2_type, "FragColor3", ir_var_out, "[[ color(3) ]]"},
|
|
{"SV_Target4", glsl_type::half2_type, "FragColor4", ir_var_out, "[[ color(4) ]]"},
|
|
{"SV_Target5", glsl_type::half2_type, "FragColor5", ir_var_out, "[[ color(5) ]]"},
|
|
{"SV_Target6", glsl_type::half2_type, "FragColor6", ir_var_out, "[[ color(6) ]]"},
|
|
{"SV_Target7", glsl_type::half2_type, "FragColor7", ir_var_out, "[[ color(7) ]]"},
|
|
{"SV_Target0", glsl_type::float_type, "FragColor0", ir_var_out, "[[ color(0) ]]"},
|
|
{"SV_Target1", glsl_type::float_type, "FragColor1", ir_var_out, "[[ color(1) ]]"},
|
|
{"SV_Target2", glsl_type::float_type, "FragColor2", ir_var_out, "[[ color(2) ]]"},
|
|
{"SV_Target3", glsl_type::float_type, "FragColor3", ir_var_out, "[[ color(3) ]]"},
|
|
{"SV_Target4", glsl_type::float_type, "FragColor4", ir_var_out, "[[ color(4) ]]"},
|
|
{"SV_Target5", glsl_type::float_type, "FragColor5", ir_var_out, "[[ color(5) ]]"},
|
|
{"SV_Target6", glsl_type::float_type, "FragColor6", ir_var_out, "[[ color(6) ]]"},
|
|
{"SV_Target7", glsl_type::float_type, "FragColor7", ir_var_out, "[[ color(7) ]]"},
|
|
{NULL, NULL, NULL, ir_var_auto, nullptr}
|
|
};
|
|
|
|
/** Pixel shader system values. */
|
|
static FSystemValue DesktopPixelSystemValueTable[] =
|
|
{
|
|
{"SV_Depth", glsl_type::float_type, "FragDepth", ir_var_out, "[[ depth(any) ]]"},
|
|
{"SV_DepthLessEqual", glsl_type::float_type, "FragDepth", ir_var_out, "[[ depth(less) ]]"},
|
|
{"SV_Position", glsl_type::vec4_type, "IN_FragCoord", ir_var_in, "position"},
|
|
{"SV_IsFrontFace", glsl_type::bool_type, "IN_FrontFacing", ir_var_in, "[[ front_facing ]]"},
|
|
{"SV_Coverage", glsl_type::uint_type, "IN_Coverage", ir_var_in, "[[ sample_mask ]]"},
|
|
{"SV_Coverage", glsl_type::uint_type, "OUT_Coverage", ir_var_out, "[[ sample_mask ]]"},
|
|
//{"SV_PrimitiveID", glsl_type::int_type, "IN_PrimitiveID", ir_var_in, "[[ ]]"},
|
|
//{"SV_RenderTargetArrayIndex", glsl_type::uint_type, "IN_Layer", ir_var_in, "[[ render_target_array_index ]]"},
|
|
//{"SV_ViewPortArrayIndex", glsl_type::uint_type, "IN_Viewport", ir_var_in, "[[ viewport_array_index ]]"},
|
|
{"SV_Target0", glsl_type::vec4_type, "FragColor0", ir_var_out, "[[ color(0) ]]"},
|
|
{"SV_Target1", glsl_type::vec4_type, "FragColor1", ir_var_out, "[[ color(1) ]]"},
|
|
{"SV_Target2", glsl_type::vec4_type, "FragColor2", ir_var_out, "[[ color(2) ]]"},
|
|
{"SV_Target3", glsl_type::vec4_type, "FragColor3", ir_var_out, "[[ color(3) ]]"},
|
|
{"SV_Target4", glsl_type::vec4_type, "FragColor4", ir_var_out, "[[ color(4) ]]"},
|
|
{"SV_Target5", glsl_type::vec4_type, "FragColor5", ir_var_out, "[[ color(5) ]]"},
|
|
{"SV_Target6", glsl_type::vec4_type, "FragColor6", ir_var_out, "[[ color(6) ]]"},
|
|
{"SV_Target7", glsl_type::vec4_type, "FragColor7", ir_var_out, "[[ color(7) ]]"},
|
|
{"SV_Target0", glsl_type::vec3_type, "FragColor0", ir_var_out, "[[ color(0) ]]"},
|
|
{"SV_Target1", glsl_type::vec3_type, "FragColor1", ir_var_out, "[[ color(1) ]]"},
|
|
{"SV_Target2", glsl_type::vec3_type, "FragColor2", ir_var_out, "[[ color(2) ]]"},
|
|
{"SV_Target3", glsl_type::vec3_type, "FragColor3", ir_var_out, "[[ color(3) ]]"},
|
|
{"SV_Target4", glsl_type::vec3_type, "FragColor4", ir_var_out, "[[ color(4) ]]"},
|
|
{"SV_Target5", glsl_type::vec3_type, "FragColor5", ir_var_out, "[[ color(5) ]]"},
|
|
{"SV_Target6", glsl_type::vec3_type, "FragColor6", ir_var_out, "[[ color(6) ]]"},
|
|
{"SV_Target7", glsl_type::vec3_type, "FragColor7", ir_var_out, "[[ color(7) ]]"},
|
|
{"SV_Target0", glsl_type::vec2_type, "FragColor0", ir_var_out, "[[ color(0) ]]"},
|
|
{"SV_Target1", glsl_type::vec2_type, "FragColor1", ir_var_out, "[[ color(1) ]]"},
|
|
{"SV_Target2", glsl_type::vec2_type, "FragColor2", ir_var_out, "[[ color(2) ]]"},
|
|
{"SV_Target3", glsl_type::vec2_type, "FragColor3", ir_var_out, "[[ color(3) ]]"},
|
|
{"SV_Target4", glsl_type::vec2_type, "FragColor4", ir_var_out, "[[ color(4) ]]"},
|
|
{"SV_Target5", glsl_type::vec2_type, "FragColor5", ir_var_out, "[[ color(5) ]]"},
|
|
{"SV_Target6", glsl_type::vec2_type, "FragColor6", ir_var_out, "[[ color(6) ]]"},
|
|
{"SV_Target7", glsl_type::vec2_type, "FragColor7", ir_var_out, "[[ color(7) ]]"},
|
|
{"SV_Target0", glsl_type::float_type, "FragColor0", ir_var_out, "[[ color(0) ]]"},
|
|
{"SV_Target1", glsl_type::float_type, "FragColor1", ir_var_out, "[[ color(1) ]]"},
|
|
{"SV_Target2", glsl_type::float_type, "FragColor2", ir_var_out, "[[ color(2) ]]"},
|
|
{"SV_Target3", glsl_type::float_type, "FragColor3", ir_var_out, "[[ color(3) ]]"},
|
|
{"SV_Target4", glsl_type::float_type, "FragColor4", ir_var_out, "[[ color(4) ]]"},
|
|
{"SV_Target5", glsl_type::float_type, "FragColor5", ir_var_out, "[[ color(5) ]]"},
|
|
{"SV_Target6", glsl_type::float_type, "FragColor6", ir_var_out, "[[ color(6) ]]"},
|
|
{"SV_Target7", glsl_type::float_type, "FragColor7", ir_var_out, "[[ color(7) ]]"},
|
|
{"SV_Target0", glsl_type::uvec4_type, "FragColor0", ir_var_out, "[[ color(0) ]]"},
|
|
{"SV_Target1", glsl_type::uvec4_type, "FragColor1", ir_var_out, "[[ color(1) ]]"},
|
|
{"SV_Target2", glsl_type::uvec4_type, "FragColor2", ir_var_out, "[[ color(2) ]]"},
|
|
{"SV_Target3", glsl_type::uvec4_type, "FragColor3", ir_var_out, "[[ color(3) ]]"},
|
|
{"SV_Target4", glsl_type::uvec4_type, "FragColor4", ir_var_out, "[[ color(4) ]]"},
|
|
{"SV_Target5", glsl_type::uvec4_type, "FragColor5", ir_var_out, "[[ color(5) ]]"},
|
|
{"SV_Target6", glsl_type::uvec4_type, "FragColor6", ir_var_out, "[[ color(6) ]]"},
|
|
{"SV_Target7", glsl_type::uvec4_type, "FragColor7", ir_var_out, "[[ color(7) ]]"},
|
|
{"SV_Target0", glsl_type::uvec3_type, "FragColor0", ir_var_out, "[[ color(0) ]]"},
|
|
{"SV_Target1", glsl_type::uvec3_type, "FragColor1", ir_var_out, "[[ color(1) ]]"},
|
|
{"SV_Target2", glsl_type::uvec3_type, "FragColor2", ir_var_out, "[[ color(2) ]]"},
|
|
{"SV_Target3", glsl_type::uvec3_type, "FragColor3", ir_var_out, "[[ color(3) ]]"},
|
|
{"SV_Target4", glsl_type::uvec3_type, "FragColor4", ir_var_out, "[[ color(4) ]]"},
|
|
{"SV_Target5", glsl_type::uvec3_type, "FragColor5", ir_var_out, "[[ color(5) ]]"},
|
|
{"SV_Target6", glsl_type::uvec3_type, "FragColor6", ir_var_out, "[[ color(6) ]]"},
|
|
{"SV_Target7", glsl_type::uvec3_type, "FragColor7", ir_var_out, "[[ color(7) ]]"},
|
|
{"SV_Target0", glsl_type::uvec2_type, "FragColor0", ir_var_out, "[[ color(0) ]]"},
|
|
{"SV_Target1", glsl_type::uvec2_type, "FragColor1", ir_var_out, "[[ color(1) ]]"},
|
|
{"SV_Target2", glsl_type::uvec2_type, "FragColor2", ir_var_out, "[[ color(2) ]]"},
|
|
{"SV_Target3", glsl_type::uvec2_type, "FragColor3", ir_var_out, "[[ color(3) ]]"},
|
|
{"SV_Target4", glsl_type::uvec2_type, "FragColor4", ir_var_out, "[[ color(4) ]]"},
|
|
{"SV_Target5", glsl_type::uvec2_type, "FragColor5", ir_var_out, "[[ color(5) ]]"},
|
|
{"SV_Target6", glsl_type::uvec2_type, "FragColor6", ir_var_out, "[[ color(6) ]]"},
|
|
{"SV_Target7", glsl_type::uvec2_type, "FragColor7", ir_var_out, "[[ color(7) ]]"},
|
|
{"SV_Target0", glsl_type::uint_type, "FragColor0", ir_var_out, "[[ color(0) ]]"},
|
|
{"SV_Target1", glsl_type::uint_type, "FragColor1", ir_var_out, "[[ color(1) ]]"},
|
|
{"SV_Target2", glsl_type::uint_type, "FragColor2", ir_var_out, "[[ color(2) ]]"},
|
|
{"SV_Target3", glsl_type::uint_type, "FragColor3", ir_var_out, "[[ color(3) ]]"},
|
|
{"SV_Target4", glsl_type::uint_type, "FragColor4", ir_var_out, "[[ color(4) ]]"},
|
|
{"SV_Target5", glsl_type::uint_type, "FragColor5", ir_var_out, "[[ color(5) ]]"},
|
|
{"SV_Target6", glsl_type::uint_type, "FragColor6", ir_var_out, "[[ color(6) ]]"},
|
|
{"SV_Target7", glsl_type::uint_type, "FragColor7", ir_var_out, "[[ color(7) ]]"},
|
|
{NULL, NULL, NULL, ir_var_auto, nullptr}
|
|
};
|
|
|
|
/** Geometry shader system values. */
|
|
static FSystemValue GeometrySystemValueTable[] =
|
|
{
|
|
/*
|
|
{"SV_VertexID", glsl_type::int_type, "IN_VertexID", ir_var_in, false, false, false, false},
|
|
{"SV_InstanceID", glsl_type::int_type, "IN_InstanceID", ir_var_in, false, false, false, false},
|
|
{"SV_Position", glsl_type::vec4_type, "IN_Position", ir_var_in, false, true, true, false},
|
|
{"SV_Position", glsl_type::vec4_type, "OUT_Position", ir_var_out, false, false, true, false},
|
|
{"SV_RenderTargetArrayIndex", glsl_type::uint_type, "OUT_Layer", ir_var_out, false, false, false, false},
|
|
{"SV_PrimitiveID", glsl_type::int_type, "OUT_PrimitiveID", ir_var_out, false, false, false, false},
|
|
{"SV_PrimitiveID", glsl_type::int_type, "IN_PrimitiveIDIn", ir_var_in, false, false, false, false},
|
|
*/
|
|
{NULL, NULL, NULL, ir_var_auto, nullptr}
|
|
};
|
|
|
|
|
|
/** Hull shader system values. */
|
|
static FSystemValue HullSystemValueTable[] =
|
|
{
|
|
{"SV_VertexID", glsl_type::uint_type, "IN_VertexID", ir_var_in, "[[ vertex_id ]]"},
|
|
{"SV_InstanceID", glsl_type::uint_type, "IN_InstanceID", ir_var_in, "[[ instance_id ]]"},
|
|
/*
|
|
{"SV_OutputControlPointID", glsl_type::int_type, "gl_InvocationID", ir_var_in, false, false, false, false},
|
|
*/
|
|
// {"SV_OutputControlPointID", glsl_type::uint_type, "LocalInvocationID", ir_var_in, "[[ thread_position_in_threadgroup ]]"},
|
|
{NULL, NULL, NULL, ir_var_auto, nullptr}
|
|
};
|
|
|
|
/** Domain shader system values. */
|
|
static FSystemValue DomainSystemValueTable[] =
|
|
{
|
|
{"SV_Position", glsl_type::vec4_type, "IN_Position", ir_var_in, "[[ TODO ]]"},
|
|
{"SV_Position", glsl_type::vec4_type, "Position", ir_var_out, "[[ position POS_INVARIANT ]]"},
|
|
{"SV_DomainLocation", glsl_type::vec2_type, "PositionInPatch", ir_var_in, "[[ position_in_patch ]]"}, // @todo maybe add a NULL/void_type/error_type and set it using the passed in type for GenerateInputFromSemantic -- use it to simplify SV_Target as well
|
|
{"SV_DomainLocation", glsl_type::vec3_type, "PositionInPatch", ir_var_in, "[[ position_in_patch ]]"},
|
|
{"SV_RenderTargetArrayIndex", glsl_type::uint_type, "OUT_Layer", ir_var_out, "[[ render_target_array_index ]]"},
|
|
{"SV_ViewPortArrayIndex", glsl_type::uint_type, "OUT_Viewport", ir_var_out, "[[ viewport_array_index ]]"},
|
|
{NULL, NULL, NULL, ir_var_auto, nullptr}
|
|
};
|
|
|
|
/** Compute shader system values. */
|
|
static FSystemValue ComputeSystemValueTable[] =
|
|
{
|
|
// D3D, type, GL, param, Metal
|
|
{"SV_DispatchThreadID", glsl_type::uvec3_type, "thread_position_in_grid", ir_var_in, "[[ thread_position_in_grid ]]"},
|
|
{"SV_GroupID", glsl_type::uvec3_type, "threadgroup_position_in_grid", ir_var_in, "[[ threadgroup_position_in_grid ]]"},
|
|
{"SV_GroupIndex", glsl_type::uint_type, "thread_index_in_threadgroup", ir_var_in, "[[ thread_index_in_threadgroup ]]"},
|
|
{"SV_GroupThreadID", glsl_type::uvec3_type, "thread_position_in_threadgroup", ir_var_in, "[[ thread_position_in_threadgroup ]]"},
|
|
{NULL, NULL, NULL, ir_var_auto, nullptr}
|
|
};
|
|
|
|
FSystemValue* MobileSystemValueTable[HSF_FrequencyCount] =
|
|
{
|
|
VertexSystemValueTable,
|
|
MobilePixelSystemValueTable,
|
|
GeometrySystemValueTable,
|
|
HullSystemValueTable,
|
|
DomainSystemValueTable,
|
|
ComputeSystemValueTable
|
|
};
|
|
|
|
FSystemValue* DesktopSystemValueTable[HSF_FrequencyCount] =
|
|
{
|
|
VertexSystemValueTable,
|
|
DesktopPixelSystemValueTable,
|
|
GeometrySystemValueTable,
|
|
HullSystemValueTable,
|
|
DomainSystemValueTable,
|
|
ComputeSystemValueTable
|
|
};
|
|
|
|
char const* interpolant_qualifiers[2][4] = {{"", "center_perspective", "flat", "center_no_perspective"}, {"centroid_perspective", "centroid_perspective", "flat", "centroid_no_perspective"}};
|
|
|
|
static ir_rvalue* GenerateInputFromSemantic(EHlslShaderFrequency Frequency, EMetalGPUSemantics bIsDesktop, _mesa_glsl_parse_state* ParseState,
|
|
const char* Semantic, FSemanticQualifier Qualifier, const glsl_type* Type, char const* Name, exec_list* DeclInstructions, exec_list* PreCallInstructions)
|
|
{
|
|
if (!Semantic)
|
|
{
|
|
//todo-rco: More info!
|
|
_mesa_glsl_error(ParseState, "Missing input semantic!", Semantic);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!FCStringAnsi::Stricmp(Semantic, "SV_OutputControlPointID"))
|
|
{
|
|
//check(bIsTessellationVSHS);
|
|
auto Variable = ParseState->symbols->get_variable("SV_OutputControlPointID");
|
|
check(Variable);
|
|
return new (ParseState)ir_dereference_variable(Variable);
|
|
}
|
|
else
|
|
if (FCStringAnsi::Strnicmp(Semantic, "SV_", 3) == 0)
|
|
{
|
|
FSystemValue* SystemValues = (bIsDesktop == EMetalGPUSemanticsMobile) ? MobileSystemValueTable[Frequency] : DesktopSystemValueTable[Frequency];
|
|
for (int i = 0; SystemValues[i].HlslSemantic != NULL; ++i)
|
|
{
|
|
if (SystemValues[i].Mode == ir_var_in && FCStringAnsi::Stricmp(SystemValues[i].HlslSemantic, Semantic) == 0)
|
|
{
|
|
if (!FCStringAnsi::Stricmp(Semantic, "SV_DomainLocation") && Frequency == HSF_DomainShader)
|
|
{
|
|
// SV_DomainLocation is either float2 or float3 -- find the proper type
|
|
if (SystemValues[i].Type != Type)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ir_variable* Variable = ParseState->symbols->get_variable(SystemValues[i].MetalName);
|
|
if (!Variable)
|
|
{
|
|
Variable = new(ParseState) ir_variable(SystemValues[i].Type, SystemValues[i].MetalName, ir_var_in);
|
|
if (!FCStringAnsi::Stricmp(Semantic, "SV_Position"))
|
|
{
|
|
char const* interp = Frequency == HSF_PixelShader ? interpolant_qualifiers[Qualifier.Fields.bCentroid][Qualifier.Fields.InterpolationMode] : "";
|
|
Variable->semantic = ralloc_asprintf(ParseState, "[[ %s, %s ]]", SystemValues[i].MetalSemantic, interp);
|
|
}
|
|
else
|
|
{
|
|
Variable->semantic = SystemValues[i].MetalSemantic;
|
|
}
|
|
Variable->read_only = true;
|
|
Variable->origin_upper_left = false;
|
|
DeclInstructions->push_tail(Variable);
|
|
ParseState->symbols->add_variable(Variable);
|
|
}
|
|
ir_dereference_variable* VariableDeref = new(ParseState) ir_dereference_variable(Variable);
|
|
if (!FCStringAnsi::Stricmp(Semantic, "SV_Position") && Frequency == HSF_PixelShader)
|
|
{
|
|
// UE4 requires w instead of 1/w in SVPosition
|
|
auto* TempVariable = new(ParseState) ir_variable(Variable->type, nullptr, ir_var_temporary);
|
|
DeclInstructions->push_tail(TempVariable);
|
|
|
|
// Assign input to this variable
|
|
auto* TempVariableDeref = new(ParseState) ir_dereference_variable(TempVariable);
|
|
DeclInstructions->push_tail(
|
|
new(ParseState) ir_assignment(TempVariableDeref, VariableDeref)
|
|
);
|
|
|
|
// TempVariable.w = ( 1.0f / TempVariable.w );
|
|
DeclInstructions->push_tail(
|
|
new(ParseState) ir_assignment(
|
|
new(ParseState) ir_swizzle(TempVariableDeref->clone(ParseState, nullptr), 3, 0, 0, 0, 1),
|
|
new(ParseState) ir_expression(
|
|
ir_binop_div,
|
|
new(ParseState) ir_constant(1.0f),
|
|
new(ParseState) ir_swizzle(TempVariableDeref->clone(ParseState, nullptr), 3, 0, 0, 0, 1)
|
|
)
|
|
)
|
|
);
|
|
|
|
VariableDeref = TempVariableDeref->clone(ParseState, nullptr);
|
|
}
|
|
return VariableDeref;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we're here, no built-in variables matched.
|
|
bool bUseSlice = false;
|
|
bool bUseViewport = false;
|
|
bool bUseSampleID = false;
|
|
if (FCStringAnsi::Strnicmp(Semantic, "SV_", 3) == 0)
|
|
{
|
|
if (FCStringAnsi::Strnicmp(Semantic, "SV_RenderTargetArrayIndex", 25) == 0)
|
|
{
|
|
bUseSlice = true;
|
|
}
|
|
else if (FCStringAnsi::Strnicmp(Semantic, "SV_ViewPortArrayIndex", 21) == 0)
|
|
{
|
|
bUseViewport = true;
|
|
}
|
|
else if (FCStringAnsi::Strnicmp(Semantic, "SV_SampleIndex", 14) == 0)
|
|
{
|
|
bUseSampleID = true;
|
|
}
|
|
else
|
|
{
|
|
_mesa_glsl_warning(ParseState, "unrecognized system value input '%s'", Semantic);
|
|
}
|
|
}
|
|
|
|
ir_variable* Variable = new(ParseState)ir_variable(
|
|
Type,
|
|
Name ? Name : ralloc_asprintf(ParseState, "IN_%s", Semantic),
|
|
ir_var_in);
|
|
if (Frequency == HSF_VertexShader)
|
|
{
|
|
if (!FCStringAnsi::Strnicmp(Semantic, "ATTRIBUTE", 9))
|
|
{
|
|
Variable->semantic = ralloc_asprintf(ParseState, "[[ attribute(%s) ]]", Semantic);
|
|
}
|
|
else if (FCStringAnsi::Strnicmp(Semantic, "[[", 2))
|
|
{
|
|
_mesa_glsl_warning(ParseState, "Unrecognized input attribute '%s'", Semantic);
|
|
}
|
|
}
|
|
else if (bUseSlice)
|
|
{
|
|
check(Frequency == HSF_PixelShader);
|
|
Variable->semantic = ralloc_asprintf(ParseState, "[[ render_target_array_index ]]");
|
|
}
|
|
else if (bUseViewport)
|
|
{
|
|
check(Frequency == HSF_PixelShader);
|
|
Variable->semantic = ralloc_asprintf(ParseState, "[[ viewport_array_index ]]");
|
|
}
|
|
else if (bUseSampleID)
|
|
{
|
|
check(Frequency == HSF_PixelShader);
|
|
Variable->semantic = ralloc_asprintf(ParseState, "[[ sample_id ]]");
|
|
}
|
|
|
|
if (Variable->type->is_patch())
|
|
{
|
|
// do not add any semantics for patch-types
|
|
}
|
|
else
|
|
if (!Variable->semantic)
|
|
{
|
|
char const* interp = Frequency == HSF_PixelShader ? interpolant_qualifiers[Qualifier.Fields.bCentroid][Qualifier.Fields.InterpolationMode] : "";
|
|
Variable->semantic = ralloc_asprintf(ParseState, "[[ user(%s), %s ]]", Semantic, interp);
|
|
}
|
|
Variable->read_only = true;
|
|
Variable->centroid = Qualifier.Fields.bCentroid;
|
|
Variable->interpolation = Qualifier.Fields.InterpolationMode;
|
|
Variable->is_patch_constant = Qualifier.Fields.bIsPatchConstant;
|
|
DeclInstructions->push_tail(Variable);
|
|
ParseState->symbols->add_variable(Variable);
|
|
ir_dereference_variable* VariableDeref = new(ParseState)ir_dereference_variable(Variable);
|
|
return VariableDeref;
|
|
}
|
|
|
|
static void GenerateInputForVariable(EHlslShaderFrequency Frequency, EMetalGPUSemantics bIsDesktop, _mesa_glsl_parse_state* ParseState,
|
|
const char* InputSemantic, FSemanticQualifier Qualifier, ir_dereference* InputVariableDeref, exec_list* DeclInstructions, exec_list* PreCallInstructions)
|
|
{
|
|
const glsl_type* InputType = InputVariableDeref->type;
|
|
if (InputType->is_record())
|
|
{
|
|
for (uint32 i = 0; i < InputType->length; ++i)
|
|
{
|
|
const char* Semantic = nullptr;
|
|
const char* FieldSemantic = InputType->fields.structure[i].semantic;
|
|
if (InputSemantic && FieldSemantic)
|
|
{
|
|
_mesa_glsl_warning(ParseState, "semantic '%s' of field '%s' will be overridden by enclosing types' semantic '%s'",
|
|
InputType->fields.structure[i].semantic,
|
|
InputType->fields.structure[i].name,
|
|
InputSemantic);
|
|
FieldSemantic = nullptr;
|
|
}
|
|
else if (InputSemantic && !FieldSemantic)
|
|
{
|
|
Semantic = ralloc_asprintf(ParseState, "%s%d", InputSemantic, i);
|
|
_mesa_glsl_warning(ParseState, " creating semantic '%s' for struct field '%s'", Semantic, InputType->fields.structure[i].name);
|
|
}
|
|
else if (!InputSemantic && FieldSemantic)
|
|
{
|
|
Semantic = FieldSemantic;
|
|
}
|
|
else
|
|
{
|
|
Semantic = nullptr;
|
|
}
|
|
|
|
if (InputType->fields.structure[i].type->is_record() || Semantic)
|
|
{
|
|
ir_dereference_record* FieldDeref = new(ParseState)ir_dereference_record(
|
|
InputVariableDeref->clone(ParseState, NULL),
|
|
InputType->fields.structure[i].name);
|
|
|
|
FSemanticQualifier InQualifier = Qualifier;
|
|
InQualifier.Fields.bCentroid = InputType->fields.structure[i].centroid;
|
|
InQualifier.Fields.InterpolationMode = InputType->fields.structure[i].interpolation;
|
|
InQualifier.Fields.bIsPatchConstant = InputType->fields.structure[i].patchconstant;
|
|
GenerateInputForVariable(Frequency, bIsDesktop, ParseState, Semantic, InQualifier, FieldDeref, DeclInstructions, PreCallInstructions);
|
|
}
|
|
else
|
|
{
|
|
_mesa_glsl_error(
|
|
ParseState,
|
|
"field '%s' in input structure '%s' does not specify a semantic",
|
|
InputType->fields.structure[i].name,
|
|
InputType->name
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (InputType->is_array())
|
|
{
|
|
int BaseIndex = 0;
|
|
const char* Semantic = 0;
|
|
check(InputSemantic);
|
|
ParseSemanticAndIndex(ParseState, InputSemantic, &Semantic, &BaseIndex);
|
|
check(BaseIndex >= 0);
|
|
for (unsigned i = 0; i < InputType->length; ++i)
|
|
{
|
|
ir_dereference_array* ArrayDeref = new(ParseState)ir_dereference_array(
|
|
InputVariableDeref->clone(ParseState, NULL),
|
|
new(ParseState)ir_constant((unsigned)i)
|
|
);
|
|
GenerateInputForVariable(
|
|
Frequency,
|
|
bIsDesktop,
|
|
ParseState,
|
|
ralloc_asprintf(ParseState, "%s%d", Semantic, BaseIndex + i),
|
|
Qualifier,
|
|
ArrayDeref,
|
|
DeclInstructions,
|
|
PreCallInstructions);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FSemanticQualifier InQualifier = Qualifier;
|
|
ir_dereference_variable* DerefVariable = InputVariableDeref->as_dereference_variable();
|
|
ir_dereference_array* DerefArray = InputVariableDeref->as_dereference_array();
|
|
ir_constant* DerefIndex = DerefArray ? DerefArray->array_index->as_constant() : nullptr;
|
|
ir_dereference_record* DerefStruct = DerefArray ? DerefArray->array->as_dereference_record() : InputVariableDeref->as_dereference_record();
|
|
char const* Name = nullptr;
|
|
if (DerefVariable)
|
|
{
|
|
Name = (DerefVariable->var && DerefVariable->var->name) ? ralloc_asprintf(ParseState, DerefVariable->var->name) : nullptr;
|
|
}
|
|
else if (DerefIndex && DerefStruct)
|
|
{
|
|
Name = ralloc_asprintf(ParseState, "%s%d", DerefStruct->field, DerefIndex->value.u[0]);
|
|
|
|
int FieldIndex = DerefStruct->type->field_index(DerefStruct->field);
|
|
if (FieldIndex >= 0)
|
|
{
|
|
InQualifier.Fields.bCentroid = DerefStruct->type->fields.structure[FieldIndex].centroid;
|
|
InQualifier.Fields.InterpolationMode = DerefStruct->type->fields.structure[FieldIndex].interpolation;
|
|
InQualifier.Fields.bIsPatchConstant = DerefStruct->type->fields.structure[FieldIndex].patchconstant;
|
|
}
|
|
}
|
|
else if(DerefStruct)
|
|
{
|
|
Name = DerefStruct->field;
|
|
|
|
int FieldIndex = DerefStruct->type->field_index(DerefStruct->field);
|
|
if (FieldIndex >= 0)
|
|
{
|
|
InQualifier.Fields.bCentroid = DerefStruct->type->fields.structure[FieldIndex].centroid;
|
|
InQualifier.Fields.InterpolationMode = DerefStruct->type->fields.structure[FieldIndex].interpolation;
|
|
InQualifier.Fields.bIsPatchConstant = DerefStruct->type->fields.structure[FieldIndex].patchconstant;
|
|
}
|
|
}
|
|
|
|
ir_rvalue* SrcValue = GenerateInputFromSemantic(Frequency, bIsDesktop, ParseState, InputSemantic, InQualifier, InputType, Name, DeclInstructions, PreCallInstructions);
|
|
if (SrcValue)
|
|
{
|
|
YYLTYPE loc;
|
|
apply_type_conversion(InputType, SrcValue, PreCallInstructions, ParseState, true, &loc);
|
|
PreCallInstructions->push_tail(
|
|
new(ParseState) ir_assignment(InputVariableDeref->clone(ParseState, NULL),SrcValue));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ir_dereference_variable* GenerateInput(EHlslShaderFrequency Frequency, uint32 bIsDesktop, _mesa_glsl_parse_state* ParseState, const char* InputName, const char* InputSemantic, FSemanticQualifier Qualifier, const glsl_type* InputType, exec_list* DeclInstructions, exec_list* PreCallInstructions)
|
|
{
|
|
if((InputType->is_inputpatch()))
|
|
{
|
|
return GenerateInputFromSemantic(Frequency, (EMetalGPUSemantics)bIsDesktop, ParseState, InputSemantic, Qualifier, InputType, nullptr, DeclInstructions, PreCallInstructions)->as_dereference_variable();
|
|
}
|
|
ir_variable* TempVariable = new(ParseState)ir_variable(InputType, InputName ? ralloc_strdup(ParseState, InputName) : nullptr, ir_var_temporary);
|
|
ir_dereference_variable* TempVariableDeref = new(ParseState)ir_dereference_variable(TempVariable);
|
|
PreCallInstructions->push_tail(TempVariable);
|
|
GenerateInputForVariable(Frequency, (EMetalGPUSemantics)bIsDesktop, ParseState, InputSemantic, Qualifier, TempVariableDeref, DeclInstructions, PreCallInstructions);
|
|
return TempVariableDeref;
|
|
}
|
|
|
|
static ir_rvalue* GenerateOutputFromSemantic(EHlslShaderFrequency Frequency, uint32 bIsDesktop, _mesa_glsl_parse_state* ParseState,
|
|
const char* Semantic, FSemanticQualifier Qualifier, const glsl_type* Type, char const* Name, exec_list* DeclInstructions, const glsl_type** DestVariableType)
|
|
{
|
|
ir_variable* Variable = NULL;
|
|
|
|
if (FCStringAnsi::Strnicmp(Semantic, "SV_", 3) == 0)
|
|
{
|
|
FSystemValue* SystemValues = (bIsDesktop == EMetalGPUSemanticsMobile) ? MobileSystemValueTable[Frequency] : DesktopSystemValueTable[Frequency];
|
|
|
|
for (int i = 0; SystemValues[i].HlslSemantic != nullptr; ++i)
|
|
{
|
|
if (SystemValues[i].Mode == ir_var_out && FCStringAnsi::Stricmp(SystemValues[i].HlslSemantic, Semantic) == 0 && SystemValues[i].Type == Type)
|
|
{
|
|
Variable = new(ParseState) ir_variable(SystemValues[i].Type, SystemValues[i].MetalName, ir_var_out);
|
|
Variable->semantic = SystemValues[i].MetalSemantic;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Variable)
|
|
{
|
|
for (int i = 0; SystemValues[i].HlslSemantic != nullptr; ++i)
|
|
{
|
|
if (SystemValues[i].Mode == ir_var_out && FCStringAnsi::Stricmp(SystemValues[i].HlslSemantic, Semantic) == 0 && SystemValues[i].Type->vector_elements == Type->vector_elements)
|
|
{
|
|
Variable = new(ParseState) ir_variable(SystemValues[i].Type, SystemValues[i].MetalName, ir_var_out);
|
|
Variable->semantic = SystemValues[i].MetalSemantic;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Need to generate a single clip-distance for broken desktop drivers - done by simply dropping the higher clip-distances
|
|
// As it happens we already order them by importance (0: Global > 1: VR-instanced fallback > 2: vertex-shader-layer)
|
|
uint32 const ClipPrefixLen = 15;
|
|
FMetalLanguageSpec* Spec = (FMetalLanguageSpec*)ParseState->LanguageSpec;
|
|
|
|
// But for iOS/tvOS and future, non-broken desktop we can just remap the variable to the actual clip-distance-array
|
|
if (Semantic && FCStringAnsi::Strnicmp(Semantic, "SV_ClipDistance", ClipPrefixLen) == 0 && !Variable)
|
|
{
|
|
Variable = ParseState->symbols->get_variable("clip_distance_array");
|
|
|
|
uint32 const Count = Spec->GetClipDistanceCount();
|
|
check(Count > 0);
|
|
|
|
*DestVariableType = (Count > 1) ? glsl_type::get_array_instance(glsl_type::float_type, Count) : glsl_type::float_type;
|
|
if (!Variable)
|
|
{
|
|
Variable = new(ParseState)ir_variable(*DestVariableType, "clip_distance_array", ir_var_out);
|
|
Variable->semantic = ralloc_asprintf(ParseState, "[[ clip_distance ]]");
|
|
DeclInstructions->push_tail(Variable);
|
|
ParseState->symbols->add_variable(Variable);
|
|
}
|
|
|
|
ir_rvalue* VariableDeref = new(ParseState)ir_dereference_variable(Variable);
|
|
if (Count > 0)
|
|
{
|
|
uint32 Index = 0;
|
|
if (Semantic[ClipPrefixLen] >= '1' && Semantic[ClipPrefixLen] <= '7')
|
|
{
|
|
Index = Semantic[ClipPrefixLen] - '0';
|
|
}
|
|
ir_variable* IndexVar = nullptr;
|
|
for (uint32 i = 0; i < 8; i++)
|
|
{
|
|
check(i < Count);
|
|
char* IndexName = ralloc_asprintf(ParseState, "ClipDistanceIndex%u", i);
|
|
IndexVar = ParseState->symbols->get_variable(IndexName);
|
|
if (!IndexVar)
|
|
{
|
|
IndexVar = new(ParseState)ir_variable(*DestVariableType, IndexName, ir_var_const_in);
|
|
IndexVar->constant_value = new(ParseState) ir_constant((unsigned)i);
|
|
IndexVar->constant_initializer = new(ParseState) ir_constant((unsigned)i);
|
|
ParseState->symbols->add_variable(IndexVar);
|
|
break;
|
|
}
|
|
}
|
|
check(IndexVar);
|
|
ir_dereference_array* ArrayDeref = new(ParseState)ir_dereference_array(
|
|
VariableDeref,
|
|
IndexVar->constant_value->clone(ParseState, NULL)
|
|
);
|
|
return ArrayDeref;
|
|
}
|
|
else
|
|
{
|
|
return VariableDeref;
|
|
}
|
|
}
|
|
|
|
if (Semantic && FCStringAnsi::Strnicmp(Semantic, "SV_", 3) == 0 && !Variable)
|
|
{
|
|
_mesa_glsl_warning(ParseState, "unrecognized system value output '%s'", Semantic);
|
|
}
|
|
|
|
if (!Variable)
|
|
{
|
|
Variable = new(ParseState)ir_variable(Type, Name ? Name : ralloc_asprintf(ParseState, "OUT_%s", Semantic), ir_var_out);
|
|
Variable->semantic = ralloc_asprintf(ParseState, "[[ user(%s) ]]", Semantic);
|
|
if (Qualifier.Fields.bIsPatchConstant)
|
|
{
|
|
// Propagate the semantic straight through for things like SV_TessFactor and SV_InsideTessFactor as they aren't treated as
|
|
// system variables yet.
|
|
Variable->semantic = Semantic;
|
|
}
|
|
}
|
|
|
|
*DestVariableType = Variable->type;
|
|
DeclInstructions->push_tail(Variable);
|
|
ParseState->symbols->add_variable(Variable);
|
|
ir_rvalue* VariableDeref = new(ParseState)ir_dereference_variable(Variable);
|
|
return VariableDeref;
|
|
}
|
|
|
|
static void GenerateOutputForVariable(EHlslShaderFrequency Frequency, EMetalGPUSemantics bIsDesktop, _mesa_glsl_parse_state* ParseState,
|
|
const char* OutputSemantic, FSemanticQualifier Qualifier, ir_dereference* OutputVariableDeref, exec_list* DeclInstructions, exec_list* PostCallInstructions
|
|
/*int SemanticArraySize,int SemanticArrayIndex*/)
|
|
{
|
|
const glsl_type* OutputType = OutputVariableDeref->type;
|
|
if (OutputType->is_record())
|
|
{
|
|
for (uint32 i = 0; i < OutputType->length; ++i)
|
|
{
|
|
const char* FieldSemantic = OutputType->fields.structure[i].semantic;
|
|
const char* Semantic = nullptr;
|
|
if (OutputSemantic && FieldSemantic)
|
|
{
|
|
_mesa_glsl_warning(ParseState, "semantic '%s' of field '%s' will be overridden by enclosing types' semantic '%s'",
|
|
OutputType->fields.structure[i].semantic,
|
|
OutputType->fields.structure[i].name,
|
|
OutputSemantic);
|
|
FieldSemantic = nullptr;
|
|
}
|
|
else if (OutputSemantic && !FieldSemantic)
|
|
{
|
|
Semantic = ralloc_asprintf(ParseState, "%s%d", OutputSemantic, i);
|
|
_mesa_glsl_warning(ParseState, " creating semantic '%s' for struct field '%s'", Semantic, OutputType->fields.structure[i].name);
|
|
}
|
|
else if (!OutputSemantic && FieldSemantic)
|
|
{
|
|
Semantic = FieldSemantic;
|
|
}
|
|
else
|
|
{
|
|
Semantic = nullptr;
|
|
}
|
|
|
|
if (OutputType->fields.structure[i].type->is_record() || Semantic)
|
|
{
|
|
// Dereference the field and generate shader outputs for the field.
|
|
ir_dereference* FieldDeref = new(ParseState)ir_dereference_record(
|
|
OutputVariableDeref->clone(ParseState, NULL),
|
|
OutputType->fields.structure[i].name);
|
|
GenerateOutputForVariable(Frequency, bIsDesktop, ParseState, Semantic, Qualifier, FieldDeref, DeclInstructions, PostCallInstructions);
|
|
}
|
|
else
|
|
{
|
|
_mesa_glsl_error(
|
|
ParseState,
|
|
"field '%s' in output structure '%s' does not specify a semantic",
|
|
OutputType->fields.structure[i].name,
|
|
OutputType->name
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!OutputSemantic)
|
|
{
|
|
_mesa_glsl_error(ParseState, "Entry point does not specify a semantic for its return value");
|
|
}
|
|
else
|
|
{
|
|
if (OutputType->is_array())
|
|
{
|
|
int BaseIndex = 0;
|
|
const char* Semantic = 0;
|
|
|
|
ParseSemanticAndIndex(ParseState, OutputSemantic, &Semantic, &BaseIndex);
|
|
|
|
for (unsigned i = 0; i < OutputType->length; ++i)
|
|
{
|
|
ir_dereference_array* ArrayDeref = new(ParseState)ir_dereference_array(
|
|
OutputVariableDeref->clone(ParseState, NULL),
|
|
new(ParseState) ir_constant((unsigned)i)
|
|
);
|
|
GenerateOutputForVariable(Frequency, bIsDesktop, ParseState,
|
|
ralloc_asprintf(ParseState, "%s%d", Semantic, BaseIndex + i),
|
|
Qualifier,
|
|
ArrayDeref, DeclInstructions, PostCallInstructions);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
YYLTYPE loc;
|
|
ir_rvalue* Src = OutputVariableDeref->clone(ParseState, NULL);
|
|
const glsl_type* DestVariableType = NULL;
|
|
|
|
ir_dereference_array* DerefArray = OutputVariableDeref->as_dereference_array();
|
|
ir_constant* DerefIndex = DerefArray ? DerefArray->array_index->as_constant() : nullptr;
|
|
ir_dereference_record* DerefStruct = DerefArray ? DerefArray->array->as_dereference_record() : OutputVariableDeref->as_dereference_record();
|
|
char const* Name = nullptr;
|
|
if (DerefIndex && DerefStruct)
|
|
{
|
|
Name = ralloc_asprintf(ParseState, "%s%d", DerefStruct->field, DerefIndex->value.u[0]);
|
|
}
|
|
else if(DerefStruct)
|
|
{
|
|
Name = DerefStruct->field;
|
|
}
|
|
|
|
ir_rvalue* DestVariableDeref = GenerateOutputFromSemantic(Frequency, bIsDesktop, ParseState, OutputSemantic,
|
|
Qualifier,
|
|
OutputType, Name, DeclInstructions, &DestVariableType);
|
|
|
|
apply_type_conversion(DestVariableType, Src, PostCallInstructions, ParseState, true, &loc);
|
|
PostCallInstructions->push_tail(new(ParseState)ir_assignment(DestVariableDeref, Src));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ir_dereference_variable* GenerateOutput(EHlslShaderFrequency Frequency, uint32 bIsDesktop, _mesa_glsl_parse_state* ParseState,
|
|
const char* OutputSemantic, FSemanticQualifier Qualifier, const glsl_type* OutputType, exec_list* DeclInstructions, exec_list* PreCallInstructions, exec_list* PostCallInstructions)
|
|
{
|
|
// Generate a local variable to hold the output.
|
|
ir_variable* TempVariable = new(ParseState) ir_variable(OutputType, nullptr, ir_var_temporary);
|
|
ir_dereference_variable* TempVariableDeref = new(ParseState) ir_dereference_variable(TempVariable);
|
|
PreCallInstructions->push_tail(TempVariable);
|
|
|
|
GenerateOutputForVariable(Frequency, (EMetalGPUSemantics)bIsDesktop, ParseState, OutputSemantic, Qualifier, TempVariableDeref, DeclInstructions, PostCallInstructions);
|
|
|
|
return TempVariableDeref;
|
|
}
|
|
}
|
|
|
|
struct FFixIntrinsicsVisitor : public ir_rvalue_visitor
|
|
{
|
|
_mesa_glsl_parse_state* State;
|
|
bool bUsesFramebufferFetchES2;
|
|
int MRTFetchMask;
|
|
ir_variable* DestColorVar;
|
|
const glsl_type* DestColorType;
|
|
ir_variable* DestMRTColorVar[MAX_SIMULTANEOUS_RENDER_TARGETS];
|
|
EHlslShaderFrequency Frequency;
|
|
|
|
FFixIntrinsicsVisitor(_mesa_glsl_parse_state* InState, ir_function_signature* InMainSig, EHlslShaderFrequency InFrequency) :
|
|
State(InState),
|
|
bUsesFramebufferFetchES2(false),
|
|
MRTFetchMask(0),
|
|
DestColorVar(nullptr),
|
|
DestColorType(glsl_type::error_type),
|
|
Frequency(InFrequency)
|
|
{
|
|
DestColorType = GetFragColorTypeFromMetalOutputStruct(InMainSig->return_type);
|
|
memset(DestMRTColorVar, 0, sizeof(DestMRTColorVar));
|
|
}
|
|
|
|
//ir_visitor_status visit_leave(ir_expression* expr) override
|
|
virtual void handle_rvalue(ir_rvalue** RValue)
|
|
{
|
|
if (!RValue || !*RValue)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
// Fix .x swizzle of scalars...
|
|
auto* Swizzle = (*RValue)->as_swizzle();
|
|
if (Swizzle)
|
|
{
|
|
auto* Texture = Swizzle->val->as_texture();
|
|
if (Texture && Texture->op == ir_txf &&
|
|
Texture->sampler && Texture->sampler->type->sampler_buffer &&
|
|
Texture->sampler->type->inner_type && Texture->sampler->type->inner_type->is_scalar() &&
|
|
Swizzle->mask.x == 0 &&
|
|
Swizzle->mask.y == 0 &&
|
|
Swizzle->mask.z == 0 &&
|
|
Swizzle->mask.w == 0 &&
|
|
Swizzle->mask.num_components == 1 &&
|
|
Swizzle->mask.has_duplicates == 0)
|
|
{
|
|
*RValue = Texture;
|
|
}
|
|
}
|
|
|
|
auto* expr = (*RValue)->as_expression();
|
|
if (!expr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ir_expression_operation op = expr->operation;
|
|
|
|
if (op == ir_binop_mul && expr->type->is_matrix()
|
|
&& expr->operands[0]->type->is_matrix()
|
|
&& expr->operands[1]->type->is_matrix())
|
|
{
|
|
// Convert matrixCompMult to memberwise multiply
|
|
check(expr->operands[0]->type == expr->operands[1]->type);
|
|
auto* NewTemp = new(State)ir_variable(expr->operands[0]->type, nullptr, ir_var_temporary);
|
|
base_ir->insert_before(NewTemp);
|
|
for (uint32 Index = 0; Index < expr->operands[0]->type->matrix_columns; ++Index)
|
|
{
|
|
auto* NewMul = new(State)ir_expression(ir_binop_mul,
|
|
new(State)ir_dereference_array(expr->operands[0], new(State)ir_constant(Index)),
|
|
new(State)ir_dereference_array(expr->operands[1], new(State)ir_constant(Index)));
|
|
auto* NewAssign = new(State)ir_assignment(
|
|
new(State)ir_dereference_array(NewTemp, new(State)ir_constant(Index)),
|
|
NewMul);
|
|
base_ir->insert_before(NewAssign);
|
|
}
|
|
|
|
*RValue = new(State)ir_dereference_variable(NewTemp);
|
|
}
|
|
}
|
|
|
|
virtual ir_visitor_status visit_leave(ir_call* IR) override
|
|
{
|
|
if ((Frequency == HSF_PixelShader) && IR->use_builtin)
|
|
{
|
|
const char* CalleeName = IR->callee_name();
|
|
static auto ES2Len = strlen(FRAMEBUFFER_FETCH_ES2);
|
|
static auto MRTLen = strlen(FRAMEBUFFER_FETCH_MRT);
|
|
if (!strncmp(CalleeName, FRAMEBUFFER_FETCH_ES2, ES2Len))
|
|
{
|
|
// 'Upgrade' framebuffer fetch
|
|
check(IR->actual_parameters.is_empty());
|
|
bUsesFramebufferFetchES2 = true;
|
|
if (!DestColorVar)
|
|
{
|
|
// Generate new input variable for Metal semantics
|
|
if (DestColorType == glsl_type::error_type)
|
|
{
|
|
// When there are no depth writes and no color target writes then use float
|
|
DestColorType = glsl_type::float_type;
|
|
}
|
|
DestColorVar = new(State)ir_variable(glsl_type::get_instance(DestColorType->base_type, 4, 1), "gl_LastFragData", ir_var_in);
|
|
DestColorVar->semantic = "[[ color(0) ]]";
|
|
}
|
|
|
|
ir_rvalue* DestColor = new(State)ir_dereference_variable(DestColorVar);
|
|
if (IR->return_deref->type->base_type != DestColor->type->base_type)
|
|
{
|
|
DestColor = convert_component(DestColor, IR->return_deref->type);
|
|
}
|
|
auto* Assignment = new (State)ir_assignment(IR->return_deref, DestColor);
|
|
IR->insert_before(Assignment);
|
|
IR->remove();
|
|
}
|
|
else if (!strncmp(CalleeName, FRAMEBUFFER_FETCH_MRT, MRTLen))
|
|
{
|
|
int Index = atoi(CalleeName + MRTLen);
|
|
if (!DestMRTColorVar[Index])
|
|
{
|
|
DestMRTColorVar[Index] = new(State)ir_variable(glsl_type::get_instance(DestColorType->base_type, 4, 1), CalleeName, ir_var_in);
|
|
DestMRTColorVar[Index]->semantic = ralloc_asprintf(State, "[[ color(%d) ]]", Index);
|
|
}
|
|
|
|
ir_rvalue* DestColor = new(State) ir_dereference_variable(DestMRTColorVar[Index]);
|
|
if (IR->return_deref->type->base_type != DestColor->type->base_type)
|
|
{
|
|
DestColor = convert_component(DestColor, IR->return_deref->type);
|
|
}
|
|
auto* Assignment = new (State) ir_assignment(IR->return_deref, DestColor);
|
|
IR->insert_before(Assignment);
|
|
IR->remove();
|
|
}
|
|
}
|
|
|
|
return visit_continue;
|
|
}
|
|
};
|
|
|
|
void FMetalCodeBackend::FixIntrinsics(exec_list* ir, _mesa_glsl_parse_state* state, EHlslShaderFrequency InFrequency)
|
|
{
|
|
ir_function_signature* MainSig = GetMainFunction(ir);
|
|
check(MainSig);
|
|
|
|
FFixIntrinsicsVisitor Visitor(state,MainSig,InFrequency);
|
|
Visitor.run(&MainSig->body);
|
|
|
|
if (Visitor.bUsesFramebufferFetchES2)
|
|
{
|
|
check(Visitor.DestColorVar);
|
|
MainSig->parameters.push_tail(Visitor.DestColorVar);
|
|
}
|
|
|
|
for (int i = 0; i < MAX_SIMULTANEOUS_RENDER_TARGETS; ++i)
|
|
{
|
|
if (Visitor.DestMRTColorVar[i])
|
|
{
|
|
MainSig->parameters.push_tail(Visitor.DestMRTColorVar[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct FConvertUBVisitor : public ir_rvalue_visitor
|
|
{
|
|
_mesa_glsl_parse_state* State;
|
|
TStringIRVarMap& Map;
|
|
FConvertUBVisitor(_mesa_glsl_parse_state* InState, TStringIRVarMap& InMap) :
|
|
State(InState),
|
|
Map(InMap)
|
|
{
|
|
}
|
|
|
|
virtual ir_visitor_status visit_leave(class ir_atomic * ir) override
|
|
{
|
|
if (ir->operands[0])
|
|
{
|
|
handle_rvalue(&ir->operands[0]);
|
|
}
|
|
if (ir->operands[1])
|
|
{
|
|
handle_rvalue(&ir->operands[1]);
|
|
}
|
|
return visit_continue;
|
|
}
|
|
|
|
virtual void handle_rvalue(ir_rvalue** RValuePtr) override
|
|
{
|
|
if (!RValuePtr || !*RValuePtr)
|
|
{
|
|
return;
|
|
}
|
|
auto* ReferencedVar = (*RValuePtr)->variable_referenced();
|
|
if (ReferencedVar && ReferencedVar->mode == ir_var_uniform && ReferencedVar->semantic)
|
|
{
|
|
auto FoundIter = Map.find(ReferencedVar->semantic);
|
|
if (FoundIter != Map.end())
|
|
{
|
|
auto* StructVar = FoundIter->second;
|
|
StructVar->used = 1;
|
|
|
|
// Actually replace the variable
|
|
auto* DeRefVar = (*RValuePtr)->as_dereference_variable();
|
|
if (DeRefVar)
|
|
{
|
|
*RValuePtr = new(State)ir_dereference_record(StructVar, ReferencedVar->name);
|
|
}
|
|
else
|
|
{
|
|
check(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
void FMetalCodeBackend::MovePackedUniformsToMain(exec_list* ir, _mesa_glsl_parse_state* state, FBuffers& OutBuffers)
|
|
{
|
|
//IRDump(ir);
|
|
TStringIRVarMap CBVarMap;
|
|
|
|
// Now make a new struct type and global variable per uniform buffer
|
|
for (uint32 i = 0; i < state->num_uniform_blocks; ++i)
|
|
// for (auto& CB : state->CBuffersOriginal)
|
|
{
|
|
auto* CBP = state->FindCBufferByName(false, state->uniform_blocks[i]->name);
|
|
check(CBP);
|
|
auto& CB = *CBP;
|
|
if (!CB.Members.empty())
|
|
{
|
|
glsl_struct_field* Fields = ralloc_array(state, glsl_struct_field, (unsigned)CB.Members.size());
|
|
uint32 Index = 0;
|
|
for (auto& Member : CB.Members)
|
|
{
|
|
check(Member.Var);
|
|
Fields[Index++] = glsl_struct_field(Member.Var->type, ralloc_strdup(state, Member.Var->name));
|
|
}
|
|
|
|
auto* Type = glsl_type::get_record_instance(Fields, (unsigned)CB.Members.size(), ralloc_asprintf(state, "CB_%s", CB.Name.c_str()));
|
|
// Hack: This way we tell this is a uniform buffer and we need to emit 'packed_'
|
|
((glsl_type*)Type)->HlslName = "__PACKED__";
|
|
state->AddUserStruct(Type);
|
|
|
|
auto* Var = new(state)ir_variable(Type, ralloc_asprintf(state, "%s", CB.Name.c_str()), ir_var_uniform);
|
|
CBVarMap[CB.Name] = Var;
|
|
}
|
|
}
|
|
|
|
FConvertUBVisitor ConvertVisitor(state, CBVarMap);
|
|
ConvertVisitor.run(ir);
|
|
|
|
std::set<const glsl_type*> PendingTypes;
|
|
std::set<const glsl_type*> ProcessedTypes;
|
|
|
|
// Actually only save the used variables
|
|
for (auto& Pair : CBVarMap)
|
|
{
|
|
auto* Var = Pair.second;
|
|
if (Var->used)
|
|
{
|
|
// Go through each struct type and mark it as packed
|
|
ir->push_head(Var);
|
|
if (Var->type->is_record())
|
|
{
|
|
PendingTypes.insert(Var->type);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mark all structures as packed
|
|
while (!PendingTypes.empty())
|
|
{
|
|
auto* Type = *PendingTypes.begin();
|
|
PendingTypes.erase(PendingTypes.begin());
|
|
if (ProcessedTypes.find(Type) == ProcessedTypes.end())
|
|
{
|
|
ProcessedTypes.insert(Type);
|
|
((glsl_type*)Type)->HlslName = "__PACKED__";
|
|
|
|
for (uint32 i = 0; i < Type->length; ++i)
|
|
{
|
|
if (Type->fields.structure[i].type->is_record())
|
|
{
|
|
PendingTypes.insert(Type->fields.structure[i].type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ir_function_signature* MainSig = GetMainFunction(ir);
|
|
check(MainSig);
|
|
|
|
// Gather all globals still lying outside Main
|
|
foreach_iter(exec_list_iterator, iter, *ir)
|
|
{
|
|
auto* Instruction = ((ir_instruction*)iter.get());
|
|
auto* Var = Instruction->as_variable();
|
|
if (Var)
|
|
{
|
|
bool bIsBuffer = false;
|
|
bool bIsVec3 = (Var->type->inner_type && Var->type->inner_type->is_vector() && Var->type->inner_type->components() == 3);
|
|
bool bIsStructuredBuffer = Var->type->sampler_buffer && Var->type->inner_type && (Var->type->inner_type->is_record() || !strncmp(Var->type->name, "RWStructuredBuffer<", 19) || !strncmp(Var->type->name, "StructuredBuffer<", 17));
|
|
bool bIsByteAddressBuffer = Var->type->sampler_buffer && (!strncmp(Var->type->name, "RWByteAddressBuffer", 19) || !strncmp(Var->type->name, "ByteAddressBuffer", 17));
|
|
bool bIsInvariant = Var->invariant;
|
|
switch(TypedMode)
|
|
{
|
|
case EMetalTypeBufferModeRaw:
|
|
{
|
|
bIsBuffer = (!Var->type->is_sampler() && !Var->type->is_image()) || Var->type->sampler_buffer;
|
|
break;
|
|
}
|
|
case EMetalTypeBufferMode2DSRV:
|
|
case EMetalTypeBufferModeTBSRV:
|
|
{
|
|
bIsBuffer = (!Var->type->is_sampler() && !Var->type->is_image()) || (Var->type->sampler_buffer && (Var->type->is_image() || OutBuffers.AtomicVariables.find(Var) != OutBuffers.AtomicVariables.end() || bIsStructuredBuffer || bIsInvariant || bIsByteAddressBuffer)) || bIsVec3;
|
|
break;
|
|
}
|
|
case EMetalTypeBufferMode2D:
|
|
case EMetalTypeBufferModeTB:
|
|
{
|
|
bIsBuffer = (!Var->type->is_sampler() && !Var->type->is_image()) || (Var->type->sampler_buffer && (OutBuffers.AtomicVariables.find(Var) != OutBuffers.AtomicVariables.end() || bIsStructuredBuffer || bIsInvariant || bIsByteAddressBuffer)) || bIsVec3;
|
|
break;
|
|
}
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
if (Var->type->is_sampler() || Var->type->is_image() || Var->type->is_array() || Var->type->is_record() || Var->mode == ir_var_uniform)
|
|
{
|
|
if (bIsBuffer)
|
|
{
|
|
bool bReallyIsStructuredBuffer = Var->type->sampler_buffer && Var->type->inner_type && Var->type->inner_type->is_record() && (!strncmp(Var->type->name, "RWStructuredBuffer<", 19) || !strncmp(Var->type->name, "StructuredBuffer<", 17));
|
|
if (bReallyIsStructuredBuffer)
|
|
{
|
|
((glsl_type*)Var->type->inner_type)->HlslName = "__PACKED__";
|
|
}
|
|
|
|
OutBuffers.AddBuffer(Var);
|
|
}
|
|
else
|
|
{
|
|
OutBuffers.AddTexture(Var);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Var->remove();
|
|
MainSig->parameters.push_tail(Var);
|
|
}
|
|
}
|
|
}
|
|
|
|
OutBuffers.SortBuffers(state);
|
|
|
|
// And move them to main
|
|
for (auto Iter : OutBuffers.Buffers)
|
|
{
|
|
auto* Var = (ir_variable*)Iter;
|
|
if (Var)
|
|
{
|
|
Var->remove();
|
|
MainSig->parameters.push_tail(Var);
|
|
}
|
|
}
|
|
|
|
// And move them to main
|
|
for (auto Iter : OutBuffers.Textures)
|
|
{
|
|
auto* Var = (ir_variable*)Iter;
|
|
if (Var)
|
|
{
|
|
Var->remove();
|
|
MainSig->parameters.push_tail(Var);
|
|
}
|
|
}
|
|
//IRDump(ir, state);
|
|
}
|
|
|
|
|
|
void FMetalCodeBackend::PromoteInputsAndOutputsGlobalHalfToFloat(exec_list* Instructions, _mesa_glsl_parse_state* State, EHlslShaderFrequency Frequency)
|
|
{
|
|
//IRDump(Instructions);
|
|
ir_function_signature* EntryPointSig = GetMainFunction(Instructions);
|
|
check(EntryPointSig);
|
|
foreach_iter(exec_list_iterator, Iter, *Instructions)
|
|
{
|
|
ir_instruction* IR = (ir_instruction*)Iter.get();
|
|
auto* Variable = IR->as_variable();
|
|
if (Variable)
|
|
{
|
|
auto* NewType = PromoteHalfToFloatType(State, Variable->type);
|
|
if (Variable->type == NewType)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch (Variable->mode)
|
|
{
|
|
case ir_var_in:
|
|
{
|
|
auto* NewVar = new(State)ir_variable(NewType, Variable->name, ir_var_in);
|
|
NewVar->semantic = Variable->semantic;
|
|
Variable->insert_before(NewVar);
|
|
Variable->name = nullptr;
|
|
Variable->semantic = nullptr;
|
|
Variable->mode = ir_var_temporary;
|
|
Variable->remove();
|
|
exec_list Assignments;
|
|
Assignments.push_head(Variable);
|
|
CreateNewAssignmentsFloat2Half(State, Assignments, Variable, new(State)ir_dereference_variable(NewVar));
|
|
EntryPointSig->body.get_head()->insert_before(&Assignments);
|
|
}
|
|
break;
|
|
|
|
case ir_var_out:
|
|
{
|
|
if(bIsTessellationVSHS)
|
|
{
|
|
// do nothing
|
|
}
|
|
else
|
|
if (Frequency != HSF_PixelShader)
|
|
{
|
|
auto* NewVar = new(State)ir_variable(NewType, Variable->name, ir_var_out);
|
|
NewVar->semantic = Variable->semantic;
|
|
Variable->insert_before(NewVar);
|
|
Variable->name = nullptr;
|
|
Variable->semantic = nullptr;
|
|
Variable->mode = ir_var_temporary;
|
|
Variable->remove();
|
|
exec_list Assignments;
|
|
CreateNewAssignmentsHalf2Float(State, Assignments, NewVar, new(State)ir_dereference_variable(Variable));
|
|
EntryPointSig->body.push_head(Variable);
|
|
EntryPointSig->body.append_list(&Assignments);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool ProcessStageInVariables(_mesa_glsl_parse_state* ParseState, EMetalGPUSemantics bIsDesktop, EHlslShaderFrequency Frequency, ir_variable* Variable, TArray<glsl_struct_field>& OutStageInMembers, TIRVarSet& OutStageInVariables, unsigned int* OutVertexAttributesMask, TIRVarList& OutFunctionArguments)
|
|
{
|
|
// Don't move variables that are system values into the input structures
|
|
const auto* SystemValues = (bIsDesktop == EMetalGPUSemanticsMobile) ? MetalUtils::MobileSystemValueTable[Frequency] : MetalUtils::DesktopSystemValueTable[Frequency];
|
|
for(int i = 0; SystemValues[i].MetalSemantic != nullptr; ++i)
|
|
{
|
|
if (Variable->semantic && !FCStringAnsi::Stricmp(Variable->semantic, "SV_DomainLocation"))
|
|
{
|
|
check(Frequency == HSF_DomainShader);
|
|
}
|
|
else if (Variable->semantic && SystemValues[i].Mode == ir_var_in && FCStringAnsi::Stricmp(SystemValues[i].MetalSemantic, Variable->semantic) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (Frequency == HSF_VertexShader)
|
|
{
|
|
// Generate an uber struct
|
|
if (Variable->type->is_record())
|
|
{
|
|
check(0);
|
|
}
|
|
else
|
|
{
|
|
int AttributeIndex = GetInAttributeIndex(Variable->semantic);
|
|
if (AttributeIndex >= 0)
|
|
{
|
|
if (Variable->type->is_array())
|
|
{
|
|
check(Variable->type->element_type()->is_vector());
|
|
for (uint32 i = 0; i < Variable->type->length; ++i, ++AttributeIndex)
|
|
{
|
|
glsl_struct_field OutMember;
|
|
OutMember.type = Variable->type->element_type();
|
|
OutMember.semantic = ralloc_asprintf(ParseState, "ATTRIBUTE%d", AttributeIndex);
|
|
OutMember.name = ralloc_asprintf(ParseState, "ATTRIBUTE%d_%s", AttributeIndex, Variable->name);
|
|
OutMember.centroid = Variable->centroid;
|
|
OutMember.interpolation = Variable->interpolation;
|
|
OutMember.geometryinput = Variable->geometryinput;
|
|
OutMember.patchconstant = Variable->is_patch_constant;
|
|
|
|
if (OutVertexAttributesMask)
|
|
{
|
|
*OutVertexAttributesMask |= (1 << AttributeIndex);
|
|
}
|
|
|
|
OutStageInMembers.Add(OutMember);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
glsl_struct_field OutMember;
|
|
OutMember.type = Variable->type;
|
|
OutMember.semantic = ralloc_asprintf(ParseState, "ATTRIBUTE%d", AttributeIndex);
|
|
OutMember.name = Variable->name;
|
|
OutMember.centroid = Variable->centroid;
|
|
OutMember.interpolation = Variable->interpolation;
|
|
OutMember.geometryinput = Variable->geometryinput;
|
|
OutMember.patchconstant = Variable->is_patch_constant;
|
|
|
|
if (OutVertexAttributesMask)
|
|
{
|
|
*OutVertexAttributesMask |= (1 << AttributeIndex);
|
|
}
|
|
|
|
OutStageInMembers.Add(OutMember);
|
|
}
|
|
}
|
|
else if (!strcmp(Variable->name, "gl_VertexID") || !strcmp(Variable->name, "gl_InstanceID"))
|
|
{
|
|
OutFunctionArguments.push_back(Variable);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
_mesa_glsl_error(ParseState, "Unknown semantic for input attribute %s!\n", Variable->semantic ? Variable->semantic : "", Variable->name);
|
|
check(0);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
OutStageInVariables.insert(Variable);
|
|
return true;
|
|
}
|
|
else if(Frequency != HSF_HullShader && Frequency != HSF_DomainShader)
|
|
{
|
|
check(Frequency == HSF_PixelShader);
|
|
if (!strcmp(Variable->name, "gl_FrontFacing"))
|
|
{
|
|
// Make sure we add a semantic
|
|
Variable->semantic = "gl_FrontFacing";
|
|
return true;
|
|
}
|
|
}
|
|
|
|
glsl_struct_field Member;
|
|
Member.type = Variable->type;
|
|
Member.name = ralloc_strdup(ParseState, Variable->name);
|
|
Member.semantic = ralloc_strdup(ParseState, Variable->semantic ? Variable->semantic : Variable->name);
|
|
Member.centroid = Variable->centroid;
|
|
Member.interpolation = Variable->interpolation;
|
|
Member.geometryinput = Variable->geometryinput;
|
|
Member.patchconstant = Variable->is_patch_constant;
|
|
OutStageInMembers.Add(Member);
|
|
OutStageInVariables.insert(Variable);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/** Information on system values. */
|
|
struct FSystemValue
|
|
{
|
|
const char* Semantic;
|
|
const glsl_type* Type;
|
|
const char* GlslName;
|
|
ir_variable_mode Mode;
|
|
bool bOriginUpperLeft;
|
|
bool bArrayVariable;
|
|
};
|
|
|
|
/** Vertex shader system values. */
|
|
static FSystemValue VertexSystemValueTable[] =
|
|
{
|
|
{"SV_VertexID", glsl_type::uint_type, "gl_VertexID", ir_var_in, false, false},
|
|
{"SV_InstanceID", glsl_type::uint_type, "gl_InstanceID", ir_var_in, false, false},
|
|
{"SV_RenderTargetArrayIndex", glsl_type::uint_type, "OUT_Layer", ir_var_out, false, false},
|
|
{"SV_ViewPortArrayIndex", glsl_type::uint_type, "OUT_Viewport", ir_var_out, false, false},
|
|
//{ "SV_Position", glsl_type::vec4_type, "gl_Position", ir_var_out, false, true },
|
|
{NULL, NULL, NULL, ir_var_auto, false, false}
|
|
};
|
|
|
|
/** Pixel shader system values. */
|
|
static FSystemValue PixelSystemValueTable[] =
|
|
{
|
|
{"SV_Depth", glsl_type::float_type, "gl_FragDepth", ir_var_out, false, false},
|
|
{"SV_Position", glsl_type::vec4_type, "gl_FragCoord", ir_var_in, true, false},
|
|
{"SV_Coverage", glsl_type::uint_type, "IN_Coverage", ir_var_in, false, false},
|
|
{"SV_Coverage", glsl_type::uint_type, "OUT_Coverage", ir_var_out, false, false},
|
|
// { "SV_IsFrontFace", glsl_type::bool_type, "gl_FrontFacing", ir_var_in, false, true },
|
|
{"SV_PrimitiveID", glsl_type::int_type, "gl_PrimitiveID", ir_var_in, false, false},
|
|
{"SV_RenderTargetArrayIndex", glsl_type::uint_type, "IN_Layer", ir_var_in, false, false},
|
|
{"SV_ViewPortArrayIndex", glsl_type::uint_type, "IN_Viewport", ir_var_in, false, false},
|
|
// { "SV_RenderTargetArrayIndex", glsl_type::uint_type, "gl_Layer", ir_var_in, false, false },
|
|
// { "SV_Target0", glsl_type::vec4_type, "gl_FragColor", ir_var_out, false, true },
|
|
{ "SV_SampleIndex", glsl_type::uint_type, "IN_SampleID", ir_var_in, false, false },
|
|
{NULL, NULL, NULL, ir_var_auto, false, false}
|
|
};
|
|
|
|
static FSystemValue* SystemValueTable[] =
|
|
{
|
|
VertexSystemValueTable,
|
|
PixelSystemValueTable,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr
|
|
};
|
|
|
|
/**
|
|
* Generate a shader input.
|
|
* @param Frequency - The shader frequency.
|
|
* @param ParseState - Parse state.
|
|
* @param InputSemantic - The semantic name to generate.
|
|
* @param InputQualifier - Qualifiers applied to the semantic.
|
|
* @param InputType - Value type.
|
|
* @param DeclInstructions - IR to which declarations may be added.
|
|
* @param PreCallInstructions - IR to which instructions may be added before the
|
|
* entry point is called.
|
|
* @returns the IR variable deref for the semantic.
|
|
*/
|
|
static ir_dereference_variable* GenerateShaderInput(
|
|
EHlslShaderFrequency Frequency, EMetalGPUSemantics bIsDesktop,
|
|
_mesa_glsl_parse_state* ParseState,
|
|
const char* InputSemantic,
|
|
FSemanticQualifier Qualifier,
|
|
const glsl_type* InputType,
|
|
exec_list* DeclInstructions,
|
|
exec_list* PreCallInstructions)
|
|
{
|
|
ir_variable* TempVariable = new(ParseState)ir_variable(
|
|
InputType,
|
|
NULL,
|
|
ir_var_temporary);
|
|
ir_dereference_variable* TempVariableDeref = new(ParseState) ir_dereference_variable(TempVariable);
|
|
PreCallInstructions->push_tail(TempVariable);
|
|
|
|
check(!InputType->is_inputpatch() && !InputType->is_outputpatch());
|
|
ir_rvalue* SrcValue = MetalUtils::GenerateInputFromSemantic(Frequency, bIsDesktop, ParseState, InputSemantic, Qualifier, InputType, nullptr, DeclInstructions, PreCallInstructions);
|
|
if(SrcValue)
|
|
{
|
|
YYLTYPE loc ={0};
|
|
apply_type_conversion(InputType,SrcValue,PreCallInstructions,ParseState,true,&loc);
|
|
PreCallInstructions->push_tail(
|
|
new(ParseState)ir_assignment(
|
|
TempVariableDeref->clone(ParseState,NULL),
|
|
SrcValue
|
|
)
|
|
);
|
|
}
|
|
|
|
return TempVariableDeref;
|
|
}
|
|
|
|
|
|
/**
|
|
* Generate an output semantic.
|
|
* @param Frequency - The shader frequency.
|
|
* @param ParseState - Parse state.
|
|
* @param Semantic - The semantic name to generate.
|
|
* @param Type - Value type.
|
|
* @param DeclInstructions - IR to which declarations may be added.
|
|
* @returns the IR variable for the semantic.
|
|
*/
|
|
static ir_rvalue* GenShaderOutputSemantic(
|
|
EHlslShaderFrequency Frequency,
|
|
_mesa_glsl_parse_state* ParseState,
|
|
const char* Semantic,
|
|
const glsl_type* Type,
|
|
exec_list* DeclInstructions,
|
|
const glsl_type** DestVariableType)
|
|
{
|
|
check(Semantic);
|
|
|
|
FSystemValue* SystemValues = SystemValueTable[Frequency];
|
|
ir_variable* Variable = NULL;
|
|
|
|
if (FCStringAnsi::Strnicmp(Semantic, "SV_", 3) == 0)
|
|
{
|
|
for (int i = 0; SystemValues[i].Semantic != NULL; ++i)
|
|
{
|
|
if (SystemValues[i].Mode == ir_var_out
|
|
&& FCStringAnsi::Stricmp(SystemValues[i].Semantic, Semantic) == 0)
|
|
{
|
|
check(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Variable == NULL && Frequency == HSF_VertexShader)
|
|
{
|
|
const int PrefixLength = 15;
|
|
if (FCStringAnsi::Strnicmp(Semantic, "SV_ClipDistance", PrefixLength) == 0
|
|
&& Semantic[PrefixLength] >= '0'
|
|
&& Semantic[PrefixLength] <= '9')
|
|
{
|
|
check(0);
|
|
}
|
|
}
|
|
|
|
if (Variable == NULL && Frequency == HSF_PixelShader)
|
|
{
|
|
const int PrefixLength = 9;
|
|
if (FCStringAnsi::Strnicmp(Semantic, "SV_Target", PrefixLength) == 0
|
|
&& Semantic[PrefixLength] >= '0'
|
|
&& Semantic[PrefixLength] <= '7')
|
|
{
|
|
int OutputIndex = Semantic[PrefixLength] - '0';
|
|
Variable = new(ParseState)ir_variable(
|
|
Type,
|
|
ralloc_asprintf(ParseState, "out_Target%d", OutputIndex),
|
|
ir_var_out
|
|
);
|
|
}
|
|
}
|
|
|
|
// @todo Dead function?
|
|
check(0);
|
|
if (Variable == NULL && Frequency == HSF_HullShader)
|
|
{
|
|
const int PrefixLength = 13;
|
|
if (FCStringAnsi::Strnicmp(Semantic, "SV_TessFactor", PrefixLength) == 0
|
|
&& Semantic[PrefixLength] >= '0'
|
|
&& Semantic[PrefixLength] <= '3')
|
|
{
|
|
int OutputIndex = Semantic[PrefixLength] - '0';
|
|
Variable = new(ParseState)ir_variable(
|
|
Type,
|
|
ralloc_asprintf(ParseState, "gl_TessLevelOuter[%d]", OutputIndex),
|
|
ir_var_out
|
|
);
|
|
}
|
|
}
|
|
|
|
if (Variable == NULL && Frequency == HSF_HullShader)
|
|
{
|
|
const int PrefixLength = 19;
|
|
if (FCStringAnsi::Strnicmp(Semantic, "SV_InsideTessFactor", PrefixLength) == 0
|
|
&& Semantic[PrefixLength] >= '0'
|
|
&& Semantic[PrefixLength] <= '1')
|
|
{
|
|
int OutputIndex = Semantic[PrefixLength] - '0';
|
|
Variable = new(ParseState)ir_variable(
|
|
Type,
|
|
ralloc_asprintf(ParseState, "gl_TessLevelInner[%d]", OutputIndex),
|
|
ir_var_out
|
|
);
|
|
}
|
|
else if (FCStringAnsi::Stricmp(Semantic, "SV_InsideTessFactor") == 0)
|
|
{
|
|
Variable = new(ParseState)ir_variable(
|
|
Type,
|
|
ralloc_asprintf(ParseState, "gl_TessLevelInner[0]"),
|
|
ir_var_out
|
|
);
|
|
}
|
|
}
|
|
|
|
if (Variable == NULL && ParseState->bGenerateES)
|
|
{
|
|
check(0);
|
|
// Create a variable so that a struct will not get added
|
|
Variable = new(ParseState)ir_variable(Type, ralloc_asprintf(ParseState, "var_%s", Semantic), ir_var_out);
|
|
}
|
|
|
|
if (Variable)
|
|
{
|
|
// Up to this point, variables aren't contained in structs
|
|
*DestVariableType = Variable->type;
|
|
DeclInstructions->push_tail(Variable);
|
|
ParseState->symbols->add_variable(Variable);
|
|
Variable->centroid = false;//OutputQualifier.Fields.bCentroid;
|
|
Variable->interpolation = false;//OutputQualifier.Fields.InterpolationMode;
|
|
Variable->is_patch_constant = false;// OutputQualifier.Fields.bIsPatchConstant;
|
|
ir_rvalue* VariableDeref = new(ParseState)ir_dereference_variable(Variable);
|
|
return VariableDeref;
|
|
}
|
|
|
|
if (Semantic && FCStringAnsi::Strnicmp(Semantic, "SV_", 3) == 0)
|
|
{
|
|
_mesa_glsl_warning(ParseState, "unrecognized system value output '%s'",
|
|
Semantic);
|
|
}
|
|
|
|
*DestVariableType = Type;
|
|
|
|
// Create variable
|
|
glsl_struct_field *StructField = ralloc_array(ParseState, glsl_struct_field, 1);
|
|
|
|
memset(StructField, 0, sizeof(glsl_struct_field));
|
|
StructField[0].type = Type;
|
|
StructField[0].name = ralloc_strdup(ParseState, "Data");
|
|
|
|
const glsl_type* VariableType = glsl_type::get_record_instance(StructField, 1, ralloc_strdup(ParseState, Semantic));
|
|
|
|
Variable = new(ParseState)ir_variable(VariableType, ralloc_asprintf(ParseState, "out_%s", Semantic), ir_var_out);
|
|
Variable->centroid = false;//OutputQualifier.Fields.bCentroid;
|
|
Variable->interpolation = false;//OutputQualifier.Fields.InterpolationMode;
|
|
Variable->is_interface_block = true;
|
|
Variable->is_patch_constant = false;//OutputQualifier.Fields.bIsPatchConstant;
|
|
|
|
DeclInstructions->push_tail(Variable);
|
|
ParseState->symbols->add_variable(Variable);
|
|
|
|
ir_rvalue* VariableDeref = new(ParseState)ir_dereference_variable(Variable);
|
|
|
|
if (Frequency == HSF_HullShader /*&& !OutputQualifier.Fields.bIsPatchConstant*/)
|
|
{
|
|
check(0); // @todo Still a dead function?
|
|
_mesa_glsl_warning(ParseState, "Dead function called: %s:%d\n", __FILE__, __LINE__);
|
|
//VariableDeref = new(ParseState)ir_dereference_array(VariableDeref, new(ParseState)ir_dereference_variable(ParseState->symbols->get_variable("SV_OutputControlPointID")));
|
|
}
|
|
|
|
VariableDeref = new(ParseState)ir_dereference_record(VariableDeref, ralloc_strdup(ParseState, "Data"));
|
|
|
|
return VariableDeref;
|
|
}
|
|
|
|
|
|
/**
|
|
* Generate an output semantic.
|
|
* @param Frequency - The shader frequency.
|
|
* @param ParseState - Parse state.
|
|
* @param OutputSemantic - The semantic name to generate.
|
|
* @param OutputQualifier - Qualifiers applied to the semantic.
|
|
* @param OutputVariableDeref - Deref for the argument variable.
|
|
* @param DeclInstructions - IR to which declarations may be added.
|
|
* @param PostCallInstructions - IR to which instructions may be added after the
|
|
* entry point returns.
|
|
*/
|
|
void GenShaderOutputForVariable(
|
|
EHlslShaderFrequency Frequency,
|
|
_mesa_glsl_parse_state* ParseState,
|
|
const char* OutputSemantic,
|
|
ir_dereference* OutputVariableDeref,
|
|
exec_list* DeclInstructions,
|
|
exec_list* PostCallInstructions,
|
|
int SemanticArraySize,
|
|
int SemanticArrayIndex
|
|
)
|
|
{
|
|
const glsl_type* OutputType = OutputVariableDeref->type;
|
|
if (OutputType->is_record())
|
|
{
|
|
check(0);
|
|
}
|
|
else if (OutputType->is_array())
|
|
{
|
|
check(0);
|
|
}
|
|
else
|
|
{
|
|
if (OutputSemantic)
|
|
{
|
|
YYLTYPE loc = {0};
|
|
ir_rvalue* Src = OutputVariableDeref->clone(ParseState, NULL);
|
|
const glsl_type* DestVariableType = NULL;
|
|
ir_rvalue* DestVariableDeref = GenShaderOutputSemantic(Frequency, ParseState, OutputSemantic,
|
|
OutputType, DeclInstructions, &DestVariableType);
|
|
|
|
apply_type_conversion(DestVariableType, Src, PostCallInstructions, ParseState, true, &loc);
|
|
PostCallInstructions->push_tail(new(ParseState)ir_assignment(DestVariableDeref, Src));
|
|
}
|
|
else
|
|
{
|
|
_mesa_glsl_error(ParseState, "entry point does not specify a semantic for its return value");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Generate an output semantic.
|
|
* @param Frequency - The shader frequency.
|
|
* @param ParseState - Parse state.
|
|
* @param OutputSemantic - The semantic name to generate.
|
|
* @param OutputQualifier - Qualifiers applied to the semantic.
|
|
* @param OutputType - Value type.
|
|
* @param DeclInstructions - IR to which declarations may be added.
|
|
* @param PreCallInstructions - IR to which isntructions may be added before the
|
|
entry point is called.
|
|
* @param PostCallInstructions - IR to which instructions may be added after the
|
|
* entry point returns.
|
|
* @returns the IR variable deref for the semantic.
|
|
*/
|
|
static ir_dereference_variable* GenerateShaderOutput(
|
|
EHlslShaderFrequency Frequency,
|
|
_mesa_glsl_parse_state* ParseState,
|
|
const char* OutputSemantic,
|
|
const glsl_type* OutputType,
|
|
exec_list* DeclInstructions,
|
|
exec_list* PreCallInstructions,
|
|
exec_list* PostCallInstructions
|
|
)
|
|
{
|
|
// Generate a local variable to hold the output.
|
|
ir_variable* TempVariable = new(ParseState)ir_variable(
|
|
OutputType,
|
|
NULL,
|
|
ir_var_temporary);
|
|
ir_dereference_variable* TempVariableDeref = new(ParseState)ir_dereference_variable(TempVariable);
|
|
PreCallInstructions->push_tail(TempVariable);
|
|
GenShaderOutputForVariable(
|
|
Frequency,
|
|
ParseState,
|
|
OutputSemantic,
|
|
TempVariableDeref,
|
|
DeclInstructions,
|
|
PostCallInstructions,
|
|
0,
|
|
0
|
|
);
|
|
return TempVariableDeref;
|
|
}
|
|
|
|
void FMetalCodeBackend::build_iab_fields(_mesa_glsl_parse_state* ParseState, char const* n, struct glsl_type const* t, TArray<struct glsl_struct_field>& Fields, unsigned& FieldIndex, unsigned& BufferIndex, bool top, FBuffers const& Buffers)
|
|
{
|
|
switch(t->base_type)
|
|
{
|
|
case GLSL_TYPE_UINT:
|
|
case GLSL_TYPE_INT:
|
|
case GLSL_TYPE_HALF:
|
|
case GLSL_TYPE_FLOAT:
|
|
case GLSL_TYPE_BOOL:
|
|
case GLSL_TYPE_SAMPLER:
|
|
case GLSL_TYPE_IMAGE:
|
|
case GLSL_TYPE_SAMPLER_STATE:
|
|
case GLSL_TYPE_ARRAY:
|
|
{
|
|
// All ignored
|
|
break;
|
|
}
|
|
case GLSL_TYPE_STRUCT:
|
|
{
|
|
const char *name = n;
|
|
for (unsigned j = 0; j < t->length; j++)
|
|
{
|
|
switch (t->fields.structure[j].type->base_type)
|
|
{
|
|
case GLSL_TYPE_STRUCT:
|
|
{
|
|
const char *newname = ralloc_asprintf(ParseState, "%s_%s",
|
|
n,
|
|
t->fields.structure[j].name);
|
|
build_iab_fields(ParseState, newname, t->fields.structure[j].type, Fields, FieldIndex, BufferIndex, false, Buffers);
|
|
break;
|
|
}
|
|
case GLSL_TYPE_IMAGE:
|
|
case GLSL_TYPE_SAMPLER:
|
|
case GLSL_TYPE_SAMPLER_STATE:
|
|
{
|
|
const char *newname = ralloc_asprintf(ParseState, "%s_%s",
|
|
name,
|
|
t->fields.structure[j].name);
|
|
glsl_struct_field field(t->fields.structure[j].type, newname);
|
|
field.semantic = ralloc_asprintf(ParseState, "[[ id(%d) ]]", FieldIndex);
|
|
if (t->fields.structure[j].type->sampler_buffer)
|
|
{
|
|
FieldIndex += 2;
|
|
BufferIndex++;
|
|
}
|
|
else
|
|
{
|
|
FieldIndex++;
|
|
}
|
|
Fields.Add(field);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (top)
|
|
{
|
|
if (BufferIndex)
|
|
{
|
|
glsl_struct_field field(glsl_type::GetStructuredBufferInstance("StructuredBuffer", glsl_type::uint_type), ralloc_strdup(ParseState, "BufferSizes"));
|
|
field.semantic = ralloc_asprintf(ParseState, "[[ id(%d) ]]", FieldIndex++);
|
|
field.patchconstant = 1;
|
|
Fields.Add(field);
|
|
}
|
|
|
|
name = ralloc_asprintf(ParseState, "CB_%s", n);
|
|
struct glsl_type const* c = ParseState->symbols->get_type(name);
|
|
if (c)
|
|
{
|
|
glsl_struct_field field(c, ralloc_strdup(ParseState, n));
|
|
field.semantic = ralloc_asprintf(ParseState, "[[ id(%d) ]]", FieldIndex++);
|
|
Fields.Add(field);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case GLSL_TYPE_INPUTPATCH:
|
|
case GLSL_TYPE_OUTPUTPATCH:
|
|
{
|
|
// Unhandled because I don't think they should appear as globals...
|
|
check(false);
|
|
break;
|
|
}
|
|
case GLSL_TYPE_VOID:
|
|
case GLSL_TYPE_OUTPUTSTREAM:
|
|
case GLSL_TYPE_ERROR:
|
|
case GLSL_TYPE_MAX:
|
|
default:
|
|
{
|
|
// Invalid types
|
|
check(false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct glsl_type const* FMetalCodeBackend::create_iab_type(_mesa_glsl_parse_state* ParseState, struct glsl_type const* UBType, char const* n, FBuffers const& Buffers)
|
|
{
|
|
TArray<struct glsl_struct_field> IABFields;
|
|
unsigned FieldIndex = 0;
|
|
unsigned BufferIndex = 0;
|
|
build_iab_fields(ParseState, n, UBType, IABFields, FieldIndex, BufferIndex, true, Buffers);
|
|
glsl_struct_field *const fields = ralloc_array(ParseState, glsl_struct_field, IABFields.Num());
|
|
memcpy(fields, IABFields.GetData(), sizeof(glsl_struct_field) * IABFields.Num());
|
|
const glsl_type * Result = glsl_type::get_record_instance(fields,
|
|
IABFields.Num(),
|
|
ralloc_asprintf(ParseState, "IAB_%s", n));
|
|
ParseState->AddUserStruct(Result);
|
|
return Result;
|
|
}
|
|
|
|
void FMetalCodeBackend::InsertArgumentBuffers(exec_list* ir, _mesa_glsl_parse_state* state, FBuffers& Buffers)
|
|
{
|
|
ir_function_signature* EntryPointSig = GetMainFunction(ir);
|
|
check(EntryPointSig);
|
|
|
|
TMap<FString, ir_variable*> IABVars;
|
|
foreach_iter(exec_list_iterator, Iter, EntryPointSig->parameters)
|
|
{
|
|
ir_variable* Variable = (ir_variable*)Iter.get();
|
|
char const* Underscore = strchr(Variable->name, '_');
|
|
if (Underscore)
|
|
{
|
|
uintptr_t Diff = (Underscore - Variable->name);
|
|
char* Name = ralloc_strndup(state, Variable->name, Diff);
|
|
ir_variable* UB = state->symbols->get_variable(Name);
|
|
if (UB)
|
|
{
|
|
ir_variable* IABVariable = IABVars.FindRef(Name);
|
|
if (!IABVariable)
|
|
{
|
|
struct glsl_type const* UBType = UB->type;
|
|
struct glsl_type const* IABType = create_iab_type(state, UBType, Name, Buffers);
|
|
ir_variable* NewInputVariable = new(state)ir_variable(IABType, ralloc_asprintf(state, "IAB%s", Name), ir_var_uniform);
|
|
NewInputVariable->semantic = ralloc_asprintf(state, "%s", Name);
|
|
NewInputVariable->read_only = true;
|
|
state->symbols->add_variable(NewInputVariable);
|
|
|
|
IABVariable = NewInputVariable;
|
|
IABVars.Add(Name, NewInputVariable);
|
|
|
|
glsl_uniform_block* block = glsl_uniform_block::alloc(state, 1);
|
|
block->vars[0] = NewInputVariable;
|
|
block->name = ralloc_asprintf(state, NewInputVariable->name);
|
|
const glsl_uniform_block** blocks = reralloc(state, state->uniform_blocks,
|
|
const glsl_uniform_block *,
|
|
state->num_uniform_blocks + 1);
|
|
if (blocks != NULL)
|
|
{
|
|
blocks[state->num_uniform_blocks] = block;
|
|
state->uniform_blocks = blocks;
|
|
state->num_uniform_blocks++;
|
|
}
|
|
|
|
}
|
|
|
|
ir_dereference_variable* DerefVariable = new (state)ir_dereference_variable(Variable);
|
|
ir_dereference_record* DerefRecord = new (state)ir_dereference_record(IABVariable, ralloc_strdup(state, Variable->name));
|
|
int Index = IABVariable->type->field_index(DerefRecord->field);
|
|
if (Variable->type->base_type == GLSL_TYPE_SAMPLER)
|
|
{
|
|
IABVariable->type->fields.structure[Index].type = Variable->type;
|
|
}
|
|
ir_assignment* Assign = new (state)ir_assignment(DerefVariable, DerefRecord);
|
|
|
|
IABVariablesMap.Add(Variable, IABVariable);
|
|
TSet<uint8>& Mask = IABVariableMask.FindOrAdd(IABVariable);
|
|
Mask.Add(Index);
|
|
|
|
Variable->mode = ir_var_temporary;
|
|
Iter.remove();
|
|
|
|
for (_mesa_glsl_parse_state::TUniformList::iterator It = state->GlobalPackedArraysMap[EArrayType_Sampler].begin(); It != state->GlobalPackedArraysMap[EArrayType_Sampler].end(); ++It)
|
|
{
|
|
glsl_packed_uniform& Sampler = *It;
|
|
if (!strcmp(Variable->name, Sampler.Name.c_str()))
|
|
{
|
|
state->GlobalPackedArraysMap[EArrayType_Sampler].erase(It);
|
|
break;
|
|
}
|
|
}
|
|
for (_mesa_glsl_parse_state::TUniformList::iterator It = state->GlobalPackedArraysMap[EArrayType_Image].begin(); It != state->GlobalPackedArraysMap[EArrayType_Image].end(); ++It)
|
|
{
|
|
glsl_packed_uniform& Sampler = *It;
|
|
if (!strcmp(Variable->name, Sampler.Name.c_str()))
|
|
{
|
|
state->GlobalPackedArraysMap[EArrayType_Image].erase(It);
|
|
break;
|
|
}
|
|
}
|
|
Buffers.Remove(Variable);
|
|
|
|
state->symbols->add_variable(Variable);
|
|
EntryPointSig->body.push_head(Assign);
|
|
EntryPointSig->body.push_head(Variable);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach_iter(exec_list_iterator, Iter, EntryPointSig->parameters)
|
|
{
|
|
ir_variable* Variable = (ir_variable*)Iter.get();
|
|
char* Name = ralloc_strdup(state, Variable->name);
|
|
|
|
ir_variable* UB = state->symbols->get_variable(Name);
|
|
if (UB && !strncmp(Variable->type->name, "CB_", 3))
|
|
{
|
|
ir_variable* IABVariable = IABVars.FindRef(Name);
|
|
if (IABVariable)
|
|
{
|
|
ir_dereference_variable* DerefVariable = new (state)ir_dereference_variable(Variable);
|
|
ir_dereference_record* DerefRecord = new (state)ir_dereference_record(IABVariable, ralloc_strdup(state, Variable->name));
|
|
ir_assignment* Assign = new (state)ir_assignment(DerefVariable, DerefRecord);
|
|
|
|
Variable->mode = ir_var_ref;
|
|
Iter.remove();
|
|
|
|
Buffers.Replace(Variable, IABVariable);
|
|
IABVariablesMap.Add(Variable, IABVariable);
|
|
|
|
int Index = IABVariable->type->field_index(DerefRecord->field);
|
|
TSet<uint8>& Mask = IABVariableMask.FindOrAdd(IABVariable);
|
|
Mask.Add(Index);
|
|
|
|
state->symbols->add_variable(Variable);
|
|
EntryPointSig->body.push_head(Assign);
|
|
EntryPointSig->body.push_head(Variable);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto const& Pair : IABVars)
|
|
{
|
|
EntryPointSig->parameters.push_tail(Pair.Value);
|
|
if (Buffers.GetIndex(Pair.Value) == -1)
|
|
{
|
|
Buffers.AddBuffer(Pair.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FMetalCodeBackend::PackInputsAndOutputs(exec_list* Instructions, _mesa_glsl_parse_state* ParseState, EHlslShaderFrequency Frequency, exec_list& InputVars)
|
|
{
|
|
ir_function_signature* EntryPointSig = GetMainFunction(Instructions);
|
|
check(EntryPointSig);
|
|
|
|
exec_list DeclInstructions;
|
|
exec_list PreCallInstructions;
|
|
exec_list ArgInstructions;
|
|
exec_list PostCallInstructions;
|
|
ParseState->symbols->push_scope();
|
|
|
|
// Set of variables packed into a struct
|
|
TIRVarSet VSStageInVariables;
|
|
TIRVarSet PSStageInVariables;
|
|
TIRVarSet VSOutVariables;
|
|
TIRVarSet PSOutVariables;
|
|
|
|
// Return var/struct
|
|
ir_variable* VSOut = nullptr;
|
|
ir_variable* PSOut = nullptr;
|
|
|
|
// Input stage variables
|
|
ir_variable* VSStageIn = nullptr;
|
|
std::map<std::string, glsl_struct_field> OriginalVSStageInMembers;
|
|
ir_variable* PSStageIn = nullptr;
|
|
|
|
// Extra arguments needed for input (VertexID, etc)
|
|
TIRVarList VSInputArguments;
|
|
TIRVarList PSInputArguments;
|
|
TIRVarList CSInputArguments;
|
|
|
|
TIRVarSet DSStageInVariables;
|
|
TIRVarSet DSOutVariables;
|
|
ir_variable* DSOut = nullptr;
|
|
ir_variable* DSStageIn = nullptr;
|
|
TIRVarList DSInputArguments;
|
|
|
|
TIRVarSet DSPatchVariables;
|
|
ir_variable* DSPatch = nullptr;
|
|
|
|
ir_variable* internalPatchIDVar = nullptr;
|
|
|
|
if (Frequency == HSF_DomainShader)
|
|
{
|
|
// NOTE: possibly unused
|
|
// create and call GET_INTERNAL_PATCH_ID
|
|
{
|
|
ir_function *Function_GET_INTERNAL_PATCH_ID = NULL;
|
|
// create GET_INTERNAL_PATCH_ID
|
|
{
|
|
const glsl_type* retType = glsl_type::get_instance(GLSL_TYPE_UINT, 1, 1);
|
|
ir_function_signature* sig = new(ParseState)ir_function_signature(retType);
|
|
sig->is_builtin = true;
|
|
Function_GET_INTERNAL_PATCH_ID = new(ParseState)ir_function("GET_INTERNAL_PATCH_ID");
|
|
Function_GET_INTERNAL_PATCH_ID->add_signature(sig);
|
|
}
|
|
check(Function_GET_INTERNAL_PATCH_ID);
|
|
|
|
exec_list VoidParameter;
|
|
ir_function_signature * GetInternalPatchIDFunctionSig = Function_GET_INTERNAL_PATCH_ID->matching_signature(&VoidParameter);
|
|
|
|
internalPatchIDVar = new(ParseState) ir_variable(glsl_type::get_instance(GLSL_TYPE_UINT, 1, 1), nullptr, ir_var_temporary);
|
|
ir_dereference_variable* TempVariableDeref = new(ParseState) ir_dereference_variable(internalPatchIDVar);
|
|
PreCallInstructions.push_tail(internalPatchIDVar);
|
|
|
|
auto* Call = new(ParseState)ir_call(GetInternalPatchIDFunctionSig, TempVariableDeref, &VoidParameter);
|
|
PreCallInstructions.push_tail(Call);
|
|
}
|
|
}
|
|
|
|
if(bIsTessellationVSHS)
|
|
{
|
|
foreach_iter(exec_list_iterator, Iter, *Instructions)
|
|
{
|
|
ir_instruction* IR = (ir_instruction*)Iter.get();
|
|
auto* Variable = IR->as_variable();
|
|
if (Variable)
|
|
{
|
|
switch (Variable->mode)
|
|
{
|
|
case ir_var_out:
|
|
{
|
|
// do nothing here MovePackedUniformsToMain will move all these output arguments to the function signature
|
|
}
|
|
break;
|
|
|
|
case ir_var_in:
|
|
{
|
|
// do nothing here MovePackedUniformsToMain will move all these input arguments to the function signature
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fill up InputVars so "// @Inputs" will have stuff
|
|
for (unsigned i = 0; i < ParseState->num_user_structures; i++)
|
|
{
|
|
const glsl_type *const s = ParseState->user_structures[i];
|
|
|
|
if (strcmp(s->name, "InputVertexType") == 0)
|
|
{
|
|
for (unsigned j = 0; j < s->length; j++)
|
|
{
|
|
ir_variable* var = new(ParseState)ir_variable(s->fields.structure[j].type, ralloc_strdup(ParseState, s->fields.structure[j].name), ir_var_in);
|
|
int AttributeIndex = GetVSHSInAttributeIndex(s->fields.structure[j].semantic);
|
|
if (AttributeIndex >= 0)
|
|
{
|
|
var->semantic = ralloc_asprintf(ParseState, "IN_ATTRIBUTE%d", AttributeIndex);
|
|
}
|
|
else
|
|
{
|
|
var->semantic = ralloc_strdup(ParseState, s->fields.structure[j].semantic);
|
|
}
|
|
InputVars.push_tail(new(ParseState)extern_var(var));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (Frequency == HSF_VertexShader)
|
|
{
|
|
// Vertex Fetch to Vertex connector
|
|
TArray<glsl_struct_field> VSStageInMembers;
|
|
|
|
// Vertex Output connector. Gather position semantic & other outputs into a struct
|
|
TArray<glsl_struct_field> VSOutMembers;
|
|
|
|
foreach_iter(exec_list_iterator, Iter, *Instructions)
|
|
{
|
|
ir_instruction* IR = (ir_instruction*)Iter.get();
|
|
auto* Variable = IR->as_variable();
|
|
if (Variable)
|
|
{
|
|
switch (Variable->mode)
|
|
{
|
|
case ir_var_out:
|
|
{
|
|
glsl_struct_field Member;
|
|
Member.type = Variable->type;
|
|
Member.name = ralloc_strdup(ParseState, Variable->name);
|
|
Member.semantic = ralloc_strdup(ParseState, Variable->semantic ? Variable->semantic : Variable->name);
|
|
|
|
Member.centroid = Variable->centroid;
|
|
Member.interpolation = Variable->interpolation;
|
|
Member.geometryinput = Variable->geometryinput;
|
|
Member.patchconstant = Variable->is_patch_constant;
|
|
|
|
VSOutMembers.Add(Member);
|
|
VSOutVariables.insert(Variable);
|
|
}
|
|
break;
|
|
|
|
case ir_var_in:
|
|
if (!ProcessStageInVariables(ParseState, bIsDesktop, Frequency, Variable, VSStageInMembers, VSStageInVariables, nullptr, VSInputArguments))
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (VSStageInMembers.Num())
|
|
{
|
|
check(Frequency == HSF_VertexShader);
|
|
|
|
//@todo-rco: Make me nice...
|
|
int AttributesUsedMask = 0;
|
|
for (auto& Member : VSStageInMembers)
|
|
{
|
|
int Index = GetAttributeIndex(Member.semantic);
|
|
if (Index >= 0 && Index < 16)
|
|
{
|
|
AttributesUsedMask |= (1 << Index);
|
|
}
|
|
ir_variable* var = new(ParseState)ir_variable(Member.type, ralloc_strdup(ParseState, Member.name), ir_var_in);
|
|
var->semantic = ralloc_asprintf(ParseState, "IN_%s", Member.semantic);
|
|
InputVars.push_tail(new(ParseState)extern_var(var));
|
|
}
|
|
|
|
if (bGenerateVSInputDummies)
|
|
{
|
|
int Bit = 0;
|
|
for (int i = 0; i < 16; ++i)
|
|
{
|
|
if ((AttributesUsedMask & (1 << i)) == 0)
|
|
{
|
|
glsl_struct_field NewMember;
|
|
NewMember.name = ralloc_asprintf(ParseState, "__dummy%d", i);
|
|
NewMember.semantic = ralloc_asprintf(ParseState, "ATTRIBUTE%d", i);
|
|
NewMember.type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 4, 1);
|
|
VSStageInMembers.Add(NewMember);
|
|
}
|
|
}
|
|
}
|
|
|
|
VSStageInMembers.Sort(
|
|
[](glsl_struct_field const& A, glsl_struct_field const& B)
|
|
{
|
|
return GetAttributeIndex(A.semantic) < GetAttributeIndex(B.semantic);
|
|
});
|
|
|
|
// Convert all members to float4
|
|
if (bExpandVSInputsToFloat4)
|
|
{
|
|
for (auto& Member : VSStageInMembers)
|
|
{
|
|
OriginalVSStageInMembers[Member.name] = Member;
|
|
check(Member.type->matrix_columns == 1);
|
|
Member.type = glsl_type::get_instance(Member.type->base_type, 4, 1);
|
|
}
|
|
}
|
|
|
|
auto* Type = glsl_type::get_record_instance(&VSStageInMembers[0], (unsigned int)VSStageInMembers.Num(), "FVSStageIn");
|
|
VSStageIn = new(ParseState)ir_variable(Type, "__VSStageIn", ir_var_in);
|
|
// Hack: This way we tell we need to convert types from half to float
|
|
((glsl_type*)Type)->HlslName = "__STAGE_IN__";
|
|
ParseState->symbols->add_variable(VSStageIn);
|
|
|
|
if (!ParseState->AddUserStruct(Type))
|
|
{
|
|
YYLTYPE loc = {0};
|
|
_mesa_glsl_error(&loc, ParseState, "struct '%s' previously defined", Type->name);
|
|
}
|
|
}
|
|
|
|
if (VSOutMembers.Num() && bIsTessellationVSHS)
|
|
{
|
|
check(VSOutMembers.Num() == 1);
|
|
check(VSOutVariables.size() == 1);
|
|
VSOut = *VSOutVariables.begin();
|
|
VSOut->remove();
|
|
VSOut->mode = ir_var_temporary;
|
|
DeclInstructions.push_tail(VSOut);
|
|
}
|
|
else
|
|
if (VSOutMembers.Num())
|
|
{
|
|
auto* Type = glsl_type::get_record_instance(&VSOutMembers[0], (unsigned int)VSOutMembers.Num(), "FVSOut");
|
|
VSOut = new(ParseState)ir_variable(Type, "__VSOut", ir_var_temporary);
|
|
PostCallInstructions.push_tail(VSOut);
|
|
ParseState->symbols->add_variable(VSOut);
|
|
|
|
if (!ParseState->AddUserStruct(Type))
|
|
{
|
|
YYLTYPE loc = {0};
|
|
_mesa_glsl_error(&loc, ParseState, "struct '%s' previously defined", Type->name);
|
|
}
|
|
}
|
|
}
|
|
else if (Frequency == HSF_PixelShader)
|
|
{
|
|
// Vertex to Pixel connector
|
|
TArray<glsl_struct_field> PSStageInMembers;
|
|
|
|
// Pixel Output connector. Gather color & depth outputs into a struct
|
|
TArray<glsl_struct_field> PSOutMembers;
|
|
|
|
// Gather all inputs and generate the StageIn VS->PS connector
|
|
foreach_iter(exec_list_iterator, Iter, *Instructions)
|
|
{
|
|
ir_instruction* IR = (ir_instruction*)Iter.get();
|
|
auto* Variable = IR->as_variable();
|
|
if (Variable)
|
|
{
|
|
switch (Variable->mode)
|
|
{
|
|
case ir_var_out:
|
|
{
|
|
glsl_struct_field Member;
|
|
Member.type = Variable->type;
|
|
Member.name = ralloc_strdup(ParseState, Variable->name);
|
|
Member.semantic = ralloc_strdup(ParseState, Variable->semantic ? Variable->semantic : Variable->name);
|
|
Member.centroid = Variable->centroid;
|
|
Member.interpolation = Variable->interpolation;
|
|
Member.geometryinput = Variable->geometryinput;
|
|
Member.patchconstant = Variable->is_patch_constant;
|
|
PSOutMembers.Add(Member);
|
|
PSOutVariables.insert(Variable);
|
|
}
|
|
break;
|
|
|
|
case ir_var_in:
|
|
if (!ProcessStageInVariables(ParseState, bIsDesktop, Frequency, Variable, PSStageInMembers, PSStageInVariables, nullptr, PSInputArguments))
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PSStageInMembers.Num())
|
|
{
|
|
auto* Type = glsl_type::get_record_instance(&PSStageInMembers[0], (unsigned int)PSStageInMembers.Num(), "FPSStageIn");
|
|
// Hack: This way we tell we need to convert types from half to float
|
|
((glsl_type*)Type)->HlslName = "__STAGE_IN__";
|
|
PSStageIn = new(ParseState)ir_variable(Type, "__PSStageIn", ir_var_in);
|
|
ParseState->symbols->add_variable(PSStageIn);
|
|
|
|
if (!ParseState->AddUserStruct(Type))
|
|
{
|
|
YYLTYPE loc = {0};
|
|
_mesa_glsl_error(&loc, ParseState, "struct '%s' previously defined", Type->name);
|
|
}
|
|
}
|
|
|
|
if (PSOutMembers.Num())
|
|
{
|
|
auto* Type = glsl_type::get_record_instance(&PSOutMembers[0], (unsigned int)PSOutMembers.Num(), "FPSOut");
|
|
PSOut = new(ParseState)ir_variable(Type, "__PSOut", ir_var_temporary);
|
|
PostCallInstructions.push_tail(PSOut);
|
|
ParseState->symbols->add_variable(PSOut);
|
|
|
|
if (!ParseState->AddUserStruct(Type))
|
|
{
|
|
YYLTYPE loc = {0};
|
|
_mesa_glsl_error(&loc, ParseState, "struct '%s' previously defined", Type->name);
|
|
}
|
|
}
|
|
}
|
|
else if (Frequency == HSF_ComputeShader)
|
|
{
|
|
YYLTYPE loc = {0};
|
|
|
|
foreach_iter(exec_list_iterator, Iter, *Instructions)
|
|
{
|
|
ir_instruction* IR = (ir_instruction*)Iter.get();
|
|
auto* Variable = IR->as_variable();
|
|
if (Variable)
|
|
{
|
|
switch (Variable->mode)
|
|
{
|
|
case ir_var_out:
|
|
{
|
|
_mesa_glsl_error(&loc, ParseState, "Compute/Kernel shaders do not support out variables ('%s')!", Variable->name);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case ir_var_in:
|
|
{
|
|
TArray<glsl_struct_field> CSStageInMembers;
|
|
TIRVarSet CSStageInVariables;
|
|
if (!ProcessStageInVariables(ParseState, bIsDesktop, Frequency, Variable, CSStageInMembers, CSStageInVariables, nullptr, CSInputArguments))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (CSStageInMembers.Num() != 0 || CSStageInVariables.size() != 0)
|
|
{
|
|
_mesa_glsl_error(&loc, ParseState, "Compute/Kernel shaders do not support out stage_in variables or vertex attributes ('%s')!", Variable->name);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ir_var_shared:
|
|
{
|
|
// groupshared
|
|
Variable->remove();
|
|
DeclInstructions.push_head(Variable);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (Frequency == HSF_DomainShader)
|
|
{
|
|
// Vertex Fetch to Vertex connector
|
|
TArray<glsl_struct_field> DSStageInMembers;
|
|
TArray<glsl_struct_field> DSPatchMembers;
|
|
|
|
// Vertex Output connector. Gather position semantic & other outputs into a struct
|
|
TArray<glsl_struct_field> DSOutMembers;
|
|
|
|
foreach_iter(exec_list_iterator, Iter, *Instructions)
|
|
{
|
|
ir_instruction* IR = (ir_instruction*)Iter.get();
|
|
auto* Variable = IR->as_variable();
|
|
if (Variable)
|
|
{
|
|
switch (Variable->mode)
|
|
{
|
|
case ir_var_out:
|
|
{
|
|
glsl_struct_field Member;
|
|
Member.type = Variable->type;
|
|
Member.name = ralloc_strdup(ParseState, Variable->name);
|
|
Member.semantic = ralloc_strdup(ParseState, Variable->semantic ? Variable->semantic : Variable->name);
|
|
Member.centroid = Variable->centroid;
|
|
Member.interpolation = Variable->interpolation;
|
|
Member.geometryinput = Variable->geometryinput;
|
|
Member.patchconstant = Variable->is_patch_constant;
|
|
DSOutMembers.Add(Member);
|
|
DSOutVariables.insert(Variable);
|
|
}
|
|
break;
|
|
|
|
case ir_var_in:
|
|
if(Variable->type->is_patch())
|
|
{
|
|
glsl_struct_field Member;
|
|
Member.type = Variable->type;
|
|
Member.name = ralloc_strdup(ParseState, Variable->name);
|
|
Member.semantic = ralloc_strdup(ParseState, Variable->semantic ? Variable->semantic : Variable->name);
|
|
Member.centroid = Variable->centroid;
|
|
Member.interpolation = Variable->interpolation;
|
|
Member.geometryinput = Variable->geometryinput;
|
|
Member.patchconstant = Variable->is_patch_constant;
|
|
DSPatchMembers.Add(Member);
|
|
DSPatchVariables.insert(Variable);
|
|
}
|
|
else
|
|
if (!ProcessStageInVariables(ParseState, bIsDesktop, Frequency, Variable, DSStageInMembers, DSStageInVariables, nullptr, DSInputArguments))
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// track attribute#s
|
|
int onAttribute = 0;
|
|
|
|
if (DSStageInMembers.Num())
|
|
{
|
|
check(Frequency == HSF_DomainShader);
|
|
|
|
for (auto& Member : DSStageInMembers)
|
|
{
|
|
// NOTE: DS structs do not have to match...
|
|
for(auto &Variable : DSStageInVariables)
|
|
{
|
|
if(strcmp(Member.name, Variable->name) == 0)
|
|
{
|
|
Variable->name = ralloc_asprintf(ParseState, "OUT_ATTRIBUTE%d_%s", onAttribute, Variable->name);
|
|
break;
|
|
}
|
|
}
|
|
Member.name = ralloc_asprintf(ParseState, "OUT_ATTRIBUTE%d_%s", onAttribute, Member.name);
|
|
Member.semantic = ralloc_asprintf(ParseState, "[[ attribute(%d) ]]", onAttribute);
|
|
onAttribute++;
|
|
}
|
|
|
|
auto Type = glsl_type::get_record_instance(&DSStageInMembers[0], (unsigned int)DSStageInMembers.Num(), "FDSStageIn");
|
|
auto InType = glsl_type::get_array_instance(Type, 1000); // the size is meaningless
|
|
DSStageIn = new(ParseState)ir_variable(InType, "__DSStageIn", ir_var_in);
|
|
DSStageIn->semantic = ralloc_asprintf(ParseState, ""); // empty attribute for a buffer pointer means that it will be automatically choosen
|
|
ParseState->symbols->add_variable(DSStageIn);
|
|
ParseState->AddUserStruct(Type);
|
|
Instructions->push_head(DSStageIn);
|
|
//PatchConstOutSize = glslTypeSize(Type);
|
|
|
|
// copy from DSStageIn
|
|
for(auto &Variable : DSStageInVariables)
|
|
{
|
|
Variable->remove();
|
|
Variable->mode = ir_var_temporary;
|
|
DeclInstructions.push_tail(Variable);
|
|
check(Variable->name);
|
|
ir_dereference* DeRefArray = new(ParseState)ir_dereference_array(DSStageIn, new(ParseState)ir_constant((unsigned)0));
|
|
ir_dereference* DeRefMember = new(ParseState)ir_dereference_record(DeRefArray, Variable->name);
|
|
auto* Assign = new(ParseState)ir_assignment(new(ParseState)ir_dereference_variable(Variable), DeRefMember);
|
|
PreCallInstructions.push_tail(Assign);
|
|
}
|
|
}
|
|
|
|
if(DSPatchMembers.Num())
|
|
{
|
|
check(DSPatchMembers.Num() == 1);
|
|
check(DSPatchMembers[0].type->is_patch());
|
|
|
|
// generate...
|
|
// MainDomainArg[0] = __DSPatch[0]
|
|
// MainDomainArg[1] = __DSPatch[1]
|
|
// MainDomainArg[2] = __DSPatch[2]
|
|
{
|
|
check(DSPatchVariables.size() == 1);
|
|
auto Variable = *DSPatchVariables.begin();
|
|
Variable->remove();
|
|
Variable->mode = ir_var_temporary;
|
|
DeclInstructions.push_tail(Variable);
|
|
check(Variable->type->is_outputpatch());
|
|
check(ParseState->tessellation.outputcontrolpoints == Variable->type->patch_length);
|
|
const glsl_type* Type = NULL;
|
|
const glsl_type* InType = NULL;
|
|
int origOnAttribute = onAttribute; // should not have to do this...
|
|
for (int OutputVertex = 0; OutputVertex < ParseState->tessellation.outputcontrolpoints; ++OutputVertex)
|
|
{
|
|
int InnerAttribute = origOnAttribute;
|
|
exec_list MainDomainDeclInstructions;
|
|
exec_list PreMainDomainTempDeclInstructions;
|
|
exec_list PreMainDomainCallInstructions;
|
|
|
|
FSemanticQualifier Qualifier;
|
|
Qualifier.Fields.bCentroid = Variable->centroid;
|
|
Qualifier.Fields.InterpolationMode = Variable->interpolation;
|
|
Qualifier.Fields.bIsPatchConstant = Variable->is_patch_constant;
|
|
Qualifier.Fields.bIsTessellationVSHS = false;
|
|
|
|
// @todo there has to be a better way to handle this vs looping over GenerateInput...
|
|
auto deref = MetalUtils::GenerateInput(Frequency, bIsDesktop, ParseState, Variable->name, Variable->semantic, Qualifier, Variable->type->inner_type, &MainDomainDeclInstructions, &PreMainDomainCallInstructions);
|
|
|
|
// make a flat perControlPoint struct
|
|
ir_dereference_variable* OutputControlPointDeref = NULL;
|
|
{
|
|
TIRVarSet DSInVariables;
|
|
|
|
TArray<glsl_struct_field> DSInMembers;
|
|
|
|
foreach_iter(exec_list_iterator, Iter, MainDomainDeclInstructions)
|
|
{
|
|
ir_instruction* IR = (ir_instruction*)Iter.get();
|
|
auto* InnerVariable = IR->as_variable();
|
|
if (InnerVariable)
|
|
{
|
|
switch (InnerVariable->mode)
|
|
{
|
|
case ir_var_in:
|
|
{
|
|
check(!InnerVariable->type->is_array());
|
|
glsl_struct_field Member;
|
|
Member.type = InnerVariable->type;
|
|
InnerVariable->name = ralloc_asprintf(ParseState, "OUT_ATTRIBUTE%d_%s", InnerAttribute, InnerVariable->name);
|
|
Member.name = ralloc_strdup(ParseState, InnerVariable->name);
|
|
Member.semantic = ralloc_asprintf(ParseState, "[[ attribute(%d) ]]", InnerAttribute);
|
|
Member.centroid = InnerVariable->centroid;
|
|
Member.interpolation = InnerVariable->interpolation;
|
|
Member.geometryinput = InnerVariable->geometryinput;
|
|
Member.patchconstant = InnerVariable->is_patch_constant;
|
|
|
|
if (OutputVertex == 0)
|
|
{
|
|
PatchControlPointStructHash = HashCombine(HashCombine(GetTypeHash(InnerVariable->name), GetTypeHash(InnerVariable->type)), PatchControlPointStructHash);
|
|
}
|
|
|
|
InnerAttribute++;
|
|
DSInMembers.Add(Member);
|
|
DSInVariables.insert(InnerVariable);
|
|
}
|
|
break;
|
|
default:
|
|
check(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DSInMembers.Num())
|
|
{
|
|
if(OutputVertex == 0)
|
|
{
|
|
Type = glsl_type::get_record_instance(&DSInMembers[0], (unsigned int)DSInMembers.Num(), ralloc_asprintf(ParseState, "PatchControlPointOut_%u", PatchControlPointStructHash));
|
|
ParseState->AddUserStruct(Type);
|
|
InType = glsl_type::get_array_instance(Type, 1000); // the size is meaningless
|
|
}
|
|
|
|
auto OutputControlPointVar = new(ParseState)ir_variable(Type, nullptr, ir_var_temporary);
|
|
PreMainDomainTempDeclInstructions.push_tail(OutputControlPointVar);
|
|
OutputControlPointDeref = new(ParseState)ir_dereference_variable(OutputControlPointVar);
|
|
|
|
// copy to DSIn
|
|
for(auto &InnerVariable : DSInVariables)
|
|
{
|
|
InnerVariable->remove();
|
|
InnerVariable->mode = ir_var_temporary;
|
|
PreMainDomainTempDeclInstructions.push_tail(InnerVariable);
|
|
check(InnerVariable->name);
|
|
ir_dereference* DeRefMember = new(ParseState)ir_dereference_record(OutputControlPointVar, InnerVariable->name);
|
|
auto* Assign = new(ParseState)ir_assignment(new(ParseState)ir_dereference_variable(InnerVariable), DeRefMember);
|
|
PreMainDomainCallInstructions.push_head(Assign);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(OutputVertex == 0)
|
|
{
|
|
DSPatch = new(ParseState)ir_variable(InType, "__DSPatch", ir_var_in);
|
|
DSPatch->semantic = ralloc_asprintf(ParseState, ""); // empty attribute for a buffer pointer means that it will be automatically choosen
|
|
ParseState->symbols->add_variable(DSPatch);
|
|
Instructions->push_head(DSPatch);
|
|
}
|
|
|
|
ir_dereference_array* DSPatchDeref = new(ParseState)ir_dereference_array(
|
|
DSPatch,
|
|
new(ParseState)ir_constant((unsigned)OutputVertex)
|
|
);
|
|
|
|
DeclInstructions.append_list(&MainDomainDeclInstructions); // should be empty
|
|
PreCallInstructions.append_list(&PreMainDomainTempDeclInstructions);
|
|
PreCallInstructions.push_tail(
|
|
new (ParseState)ir_assignment(
|
|
OutputControlPointDeref,
|
|
DSPatchDeref
|
|
)
|
|
);
|
|
PreCallInstructions.append_list(&PreMainDomainCallInstructions);
|
|
PreCallInstructions.push_tail(
|
|
new (ParseState)ir_assignment(
|
|
new(ParseState)ir_dereference_array(
|
|
Variable,
|
|
new(ParseState)ir_constant((unsigned)OutputVertex)
|
|
),
|
|
deref
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DSOutMembers.Num())
|
|
{
|
|
auto* DSOutType = glsl_type::get_record_instance(&DSOutMembers[0], (unsigned int)DSOutMembers.Num(), "FDSOut");
|
|
DSOut = new(ParseState)ir_variable(DSOutType, "__DSOut", ir_var_temporary);
|
|
PostCallInstructions.push_tail(DSOut);
|
|
ParseState->symbols->add_variable(DSOut);
|
|
|
|
if (!ParseState->AddUserStruct(DSOutType))
|
|
{
|
|
YYLTYPE loc = {0};
|
|
_mesa_glsl_error(&loc, ParseState, "struct '%s' previously defined", DSOutType->name);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
check(0);
|
|
}
|
|
|
|
TIRVarList VarsToMoveToBody;
|
|
foreach_iter(exec_list_iterator, Iter, *Instructions)
|
|
{
|
|
ir_instruction* IR = (ir_instruction*)Iter.get();
|
|
auto* Variable = IR->as_variable();
|
|
if (Variable)
|
|
{
|
|
ir_dereference_variable* ArgVarDeref = NULL;
|
|
switch (Variable->mode)
|
|
{
|
|
case ir_var_in:
|
|
if (PSStageInVariables.find(Variable) != PSStageInVariables.end())
|
|
{
|
|
ir_dereference* DeRefMember = new(ParseState)ir_dereference_record(PSStageIn, Variable->name);
|
|
auto* Assign = new(ParseState)ir_assignment(new(ParseState)ir_dereference_variable(Variable), DeRefMember);
|
|
PreCallInstructions.push_tail(Assign);
|
|
VarsToMoveToBody.push_back(Variable);
|
|
}
|
|
else if (VSStageInVariables.find(Variable) != VSStageInVariables.end())
|
|
{
|
|
ir_rvalue* DeRefMember = new(ParseState)ir_dereference_record(VSStageIn, Variable->name);
|
|
unsigned int Mask = 0;
|
|
if (bExpandVSInputsToFloat4)
|
|
{
|
|
Mask = (1 << 4) - 1;
|
|
auto FoundMember = OriginalVSStageInMembers.find(Variable->name);
|
|
check(FoundMember != OriginalVSStageInMembers.end());
|
|
if (FoundMember->second.type)
|
|
{
|
|
check(FoundMember->second.type->vector_elements != 0);
|
|
Mask = (1 << FoundMember->second.type->vector_elements) - 1;
|
|
if (Mask != 15)
|
|
{
|
|
DeRefMember = new(ParseState)ir_swizzle(DeRefMember, 0, 1, 2, 3, FoundMember->second.type->vector_elements);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Mask = (1 << Variable->type->vector_elements) - 1;
|
|
}
|
|
auto* Assign = new(ParseState)ir_assignment(new(ParseState) ir_dereference_variable(Variable), DeRefMember, nullptr, Mask);
|
|
PreCallInstructions.push_tail(Assign);
|
|
VarsToMoveToBody.push_back(Variable);
|
|
}
|
|
else if(bIsTessellationVSHS)
|
|
{
|
|
// see above...
|
|
// do nothing here MovePackedUniformsToMain will move all these output arguments to the function signature
|
|
}
|
|
else if (DSStageInVariables.find(Variable) != DSStageInVariables.end())
|
|
{
|
|
// TOOD could merge the code above down here...
|
|
}
|
|
else if (DSPatchVariables.find(Variable) != DSPatchVariables.end())
|
|
{
|
|
// TOOD could merge the code above down here...
|
|
}
|
|
else
|
|
{
|
|
FSemanticQualifier Qualifier;
|
|
// At this point this should be a built-in system value
|
|
check(Variable->semantic);
|
|
ArgVarDeref = GenerateShaderInput(
|
|
Frequency, bIsDesktop,
|
|
ParseState,
|
|
Variable->semantic,
|
|
Qualifier,
|
|
Variable->type,
|
|
&DeclInstructions,
|
|
&PreCallInstructions
|
|
);
|
|
}
|
|
break;
|
|
|
|
case ir_var_out:
|
|
if (VSOutVariables.find(Variable) != VSOutVariables.end())
|
|
{
|
|
VarsToMoveToBody.push_back(Variable);
|
|
ir_dereference* DeRefMember = new(ParseState) ir_dereference_record(VSOut, Variable->name);
|
|
auto* Assign = new(ParseState) ir_assignment(DeRefMember, new(ParseState)ir_dereference_variable(Variable));
|
|
PostCallInstructions.push_tail(Assign);
|
|
}
|
|
else if (PSOutVariables.find(Variable) != PSOutVariables.end())
|
|
{
|
|
VarsToMoveToBody.push_back(Variable);
|
|
ir_dereference* DeRefMember = new(ParseState) ir_dereference_record(PSOut, Variable->name);
|
|
ir_rvalue* DeRefVar = new(ParseState) ir_dereference_variable(Variable);
|
|
auto* Assign = new(ParseState) ir_assignment(DeRefMember, DeRefVar);
|
|
PostCallInstructions.push_tail(Assign);
|
|
}
|
|
else if(bIsTessellationVSHS)
|
|
{
|
|
// see above...
|
|
// do nothing here MovePackedUniformsToMain will move all these output arguments to the function signature
|
|
}
|
|
else if (DSOutVariables.find(Variable) != DSOutVariables.end())
|
|
{
|
|
VarsToMoveToBody.push_back(Variable);
|
|
ir_dereference* DeRefMember = new(ParseState) ir_dereference_record(DSOut, Variable->name);
|
|
auto* Assign = new(ParseState) ir_assignment(DeRefMember, new(ParseState)ir_dereference_variable(Variable));
|
|
PostCallInstructions.push_tail(Assign);
|
|
}
|
|
else
|
|
{
|
|
ArgVarDeref = GenerateShaderOutput(
|
|
Frequency,
|
|
ParseState,
|
|
Variable->semantic,
|
|
Variable->type,
|
|
&DeclInstructions,
|
|
&PreCallInstructions,
|
|
&PostCallInstructions
|
|
);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
/*
|
|
_mesa_glsl_error(
|
|
ParseState,
|
|
"entry point parameter '%s' must be an input or output",
|
|
Variable->name
|
|
);
|
|
*/
|
|
}
|
|
if (ArgVarDeref)
|
|
{
|
|
ArgInstructions.push_tail(ArgVarDeref);
|
|
}
|
|
}
|
|
/*
|
|
else
|
|
{
|
|
_mesa_glsl_error(ParseState, "entry point parameter "
|
|
"'%s' does not specify a semantic", Variable->name);
|
|
}
|
|
*/
|
|
}
|
|
|
|
// The function's return value should have an output semantic if it's not void.
|
|
ir_dereference_variable* EntryPointReturn = NULL;
|
|
if (EntryPointSig->return_type->is_void() == false)
|
|
{
|
|
if (Frequency == HSF_PixelShader)
|
|
{
|
|
check(!PSOut);
|
|
PSOut = new(ParseState)ir_variable(EntryPointSig->return_type, "__PSOut", ir_var_temporary);
|
|
PreCallInstructions.push_tail(PSOut);
|
|
}
|
|
else if (Frequency == HSF_VertexShader)
|
|
{
|
|
check(!VSOut);
|
|
VSOut = new(ParseState)ir_variable(EntryPointSig->return_type, "__VSOut", ir_var_temporary);
|
|
PreCallInstructions.push_tail(VSOut);
|
|
}
|
|
else if(bIsTessellationVSHS)
|
|
{
|
|
check(0); // cannot get a return type here
|
|
}
|
|
else if (Frequency == HSF_DomainShader)
|
|
{
|
|
check(!DSOut);
|
|
DSOut = new(ParseState)ir_variable(EntryPointSig->return_type, "__DSOut", ir_var_temporary);
|
|
PreCallInstructions.push_tail(DSOut);
|
|
}
|
|
else
|
|
{
|
|
check(0);
|
|
}
|
|
}
|
|
|
|
ParseState->symbols->pop_scope();
|
|
|
|
// Build the void main() function for GLSL.
|
|
auto* ReturnType = glsl_type::void_type;
|
|
if (VSOut)
|
|
{
|
|
ReturnType = VSOut->type;
|
|
check(!EntryPointReturn);
|
|
EntryPointReturn = new(ParseState)ir_dereference_variable(VSOut);
|
|
PostCallInstructions.push_tail(new(ParseState)ir_return(new(ParseState)ir_dereference_variable(VSOut)));
|
|
EntryPointSig->return_type = ReturnType;
|
|
}
|
|
else if (PSOut)
|
|
{
|
|
ReturnType = PSOut->type;
|
|
check(!EntryPointReturn);
|
|
EntryPointReturn = new(ParseState)ir_dereference_variable(PSOut);
|
|
PostCallInstructions.push_tail(new(ParseState)ir_return(new(ParseState)ir_dereference_variable(PSOut)));
|
|
EntryPointSig->return_type = ReturnType;
|
|
}
|
|
else if (DSOut)
|
|
{
|
|
ReturnType = DSOut->type;
|
|
check(!EntryPointReturn);
|
|
EntryPointReturn = new(ParseState)ir_dereference_variable(DSOut);
|
|
PostCallInstructions.push_tail(new(ParseState)ir_return(new(ParseState)ir_dereference_variable(DSOut)));
|
|
EntryPointSig->return_type = ReturnType;
|
|
}
|
|
|
|
for (auto* Var : VarsToMoveToBody)
|
|
{
|
|
Var->remove();
|
|
if (Var->mode == ir_var_in || Var->mode == ir_var_out)
|
|
{
|
|
Var->mode = ir_var_temporary;
|
|
}
|
|
DeclInstructions.push_head(Var);
|
|
}
|
|
|
|
DeclInstructions.append_list(&PreCallInstructions);
|
|
DeclInstructions.append_list(&EntryPointSig->body);
|
|
DeclInstructions.append_list(&PostCallInstructions);
|
|
|
|
EntryPointSig->body.append_list(&DeclInstructions);
|
|
#if 0
|
|
Instructions->push_tail(MainFunction);
|
|
#endif // 0
|
|
|
|
// Now that we have a proper main(), move global setup to main().
|
|
if (VSStageIn)
|
|
{
|
|
EntryPointSig->parameters.push_tail(VSStageIn);
|
|
}
|
|
else if (PSStageIn)
|
|
{
|
|
EntryPointSig->parameters.push_tail(PSStageIn);
|
|
}
|
|
|
|
/*
|
|
MoveGlobalInstructionsToMain(Instructions);
|
|
*/
|
|
}
|
|
|
|
struct FConvertHalfToFloatUniformAndSamples final : public ir_rvalue_visitor
|
|
{
|
|
struct FPair
|
|
{
|
|
ir_rvalue** RValuePtr;
|
|
ir_instruction* InsertPoint;
|
|
};
|
|
typedef std::map<ir_rvalue*, std::list<FPair>, ir_type_compare<ir_rvalue>> TReplacedVarMap;
|
|
TReplacedVarMap ReplacedVars;
|
|
|
|
std::list<TReplacedVarMap> PendingReplacements;
|
|
TIRVarSet ReferencedUniforms;
|
|
|
|
_mesa_glsl_parse_state* State;
|
|
|
|
bool bIsMaster;
|
|
bool bConvertUniforms;
|
|
bool bConvertSamples;
|
|
|
|
FConvertHalfToFloatUniformAndSamples(_mesa_glsl_parse_state* InState, bool bInConvertUniforms, bool bInConvertSamples) :
|
|
State(InState),
|
|
bIsMaster(true),
|
|
bConvertUniforms(bInConvertUniforms),
|
|
bConvertSamples(bInConvertSamples)
|
|
{
|
|
}
|
|
|
|
virtual ~FConvertHalfToFloatUniformAndSamples()
|
|
{
|
|
}
|
|
|
|
void DoConvertOneMap(TReplacedVarMap& Map)
|
|
{
|
|
for (auto& TopIter : Map)
|
|
{
|
|
auto* RValue = TopIter.first;
|
|
auto& List = TopIter.second;
|
|
|
|
// Coerce this var into float
|
|
auto* OriginalVar = RValue->variable_referenced();
|
|
const auto* OriginalVarType = OriginalVar->type;
|
|
const auto* PromotedVarType = PromoteHalfToFloatType(State, OriginalVarType);
|
|
OriginalVar->type = PromotedVarType;
|
|
|
|
// Temp var and assignment
|
|
auto* NewVar = new(State)ir_variable(RValue->type, nullptr, ir_var_temporary);
|
|
RValue->type = PromoteHalfToFloatType(State, RValue->type);
|
|
exec_list NewAssignments;
|
|
CreateNewAssignmentsFloat2Half(State, NewAssignments, NewVar, RValue);
|
|
auto* BaseIR = List.front().InsertPoint;
|
|
|
|
// Store new instructions so we add a nice block in the asm
|
|
BaseIR->insert_before(NewVar);
|
|
BaseIR->insert_before(&NewAssignments);
|
|
|
|
for (auto& Iter : List)
|
|
{
|
|
*(Iter.RValuePtr) = new(State)ir_dereference_variable(NewVar);
|
|
}
|
|
}
|
|
|
|
// Go through all remaining parameters
|
|
for (auto& Var : ReferencedUniforms)
|
|
{
|
|
Var->type = PromoteHalfToFloatType(State, Var->type);
|
|
}
|
|
}
|
|
|
|
void DoConvert(exec_list* ir)
|
|
{
|
|
run(ir);
|
|
DoConvertOneMap(ReplacedVars);
|
|
|
|
if (bIsMaster)
|
|
{
|
|
for (auto& Map : PendingReplacements)
|
|
{
|
|
DoConvertOneMap(Map);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConvertBlock(exec_list* instructions)
|
|
{
|
|
FConvertHalfToFloatUniformAndSamples Visitor(State, bConvertUniforms, bConvertSamples);
|
|
Visitor.bIsMaster = false;
|
|
Visitor.run(instructions);
|
|
PendingReplacements.push_back(TReplacedVarMap());
|
|
PendingReplacements.back().swap(Visitor.ReplacedVars);
|
|
for (auto& Var : Visitor.ReferencedUniforms)
|
|
{
|
|
ReferencedUniforms.insert(Var);
|
|
}
|
|
}
|
|
|
|
virtual ir_visitor_status visit_enter(ir_if* ir) override
|
|
{
|
|
ir->condition->accept(this);
|
|
handle_rvalue(&ir->condition);
|
|
|
|
ConvertBlock(&ir->then_instructions);
|
|
ConvertBlock(&ir->else_instructions);
|
|
|
|
/* already descended into the children. */
|
|
return visit_continue_with_parent;
|
|
}
|
|
|
|
ir_visitor_status visit_enter(ir_loop* ir)
|
|
{
|
|
ConvertBlock(&ir->body_instructions);
|
|
|
|
/* already descended into the children. */
|
|
return visit_continue_with_parent;
|
|
}
|
|
|
|
virtual void handle_rvalue(ir_rvalue** RValuePtr) override
|
|
{
|
|
if (!RValuePtr || !*RValuePtr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto* RValue = *RValuePtr;
|
|
if (bConvertSamples && RValue->as_texture())
|
|
{
|
|
auto* Texture = RValue->as_texture();
|
|
if (Texture->coordinate && Texture->coordinate->type->base_type == GLSL_TYPE_HALF)
|
|
{
|
|
// Promote to float
|
|
Texture->coordinate = new(State)ir_expression(ir_unop_h2f, Texture->coordinate);
|
|
}
|
|
else if (Texture->coordinate && Texture->coordinate->type->base_type == GLSL_TYPE_INT)
|
|
{
|
|
// convert int to uint32
|
|
Texture->coordinate = new(State)ir_expression(ir_unop_i2u, Texture->coordinate);
|
|
}
|
|
}
|
|
// Skip swizzles, textures, etc
|
|
else if (bConvertUniforms && RValue->as_dereference())
|
|
{
|
|
auto* Var = RValue->variable_referenced();
|
|
if (Var && Var->mode == ir_var_uniform)
|
|
{
|
|
ReferencedUniforms.insert(Var);
|
|
if (RValue->type->base_type == GLSL_TYPE_HALF)
|
|
{
|
|
// Save this RValue and prep for later change
|
|
FPair Pair = {RValuePtr, base_ir};
|
|
for (auto& Iter : ReplacedVars)
|
|
{
|
|
auto* TestRValue = Iter.first;
|
|
if (RValue->ir_type == TestRValue->ir_type && AreEquivalent(RValue, TestRValue))
|
|
{
|
|
Iter.second.push_back(Pair);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ReplacedVars[RValue].push_back(Pair);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
void FMetalCodeBackend::ConvertHalfToFloatUniformsAndSamples(exec_list* ir, _mesa_glsl_parse_state* State, bool bConvertUniforms, bool bConvertSamples)
|
|
{
|
|
if (bConvertUniforms || bConvertSamples)
|
|
{
|
|
FConvertHalfToFloatUniformAndSamples ConvertHalfToFloatUniforms(State, bConvertUniforms, bConvertSamples);
|
|
ConvertHalfToFloatUniforms.DoConvert(ir);
|
|
}
|
|
}
|
|
|
|
struct FMetalInsertSamplerStates final : public ir_rvalue_visitor
|
|
{
|
|
_mesa_glsl_parse_state* State;
|
|
TMap<FString, ir_variable*> SamplerStates;
|
|
|
|
FMetalInsertSamplerStates(_mesa_glsl_parse_state* InState) : State(InState)
|
|
{}
|
|
virtual ~FMetalInsertSamplerStates() { }
|
|
|
|
virtual ir_visitor_status visit_leave(ir_texture * tex_op)
|
|
{
|
|
switch (tex_op->op)
|
|
{
|
|
case ir_tex:
|
|
case ir_txl:
|
|
case ir_txb:
|
|
case ir_txd:
|
|
case ir_txg:
|
|
if (tex_op->SamplerStateName && strlen(tex_op->SamplerStateName) > 0 && !tex_op->SamplerState)
|
|
{
|
|
ir_variable* Sampler = SamplerStates.FindRef(tex_op->SamplerStateName);
|
|
if (!Sampler)
|
|
{
|
|
Sampler = new (State)ir_variable(glsl_type::sampler_state_type, ralloc_strdup(State, tex_op->SamplerStateName), ir_var_uniform);
|
|
SamplerStates.Add(tex_op->SamplerStateName, Sampler);
|
|
}
|
|
ir_dereference_variable* Deref = new (State)ir_dereference_variable(Sampler);
|
|
tex_op->SamplerState = Deref;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return ir_rvalue_visitor::visit_leave(tex_op);
|
|
}
|
|
|
|
virtual void handle_rvalue(ir_rvalue** RValuePtr) override
|
|
{
|
|
}
|
|
};
|
|
|
|
void FMetalCodeBackend::InsertSamplerStates(exec_list* ir, _mesa_glsl_parse_state* State)
|
|
{
|
|
FMetalInsertSamplerStates Visitor(State);
|
|
Visitor.run(ir);
|
|
|
|
ir_function_signature* MainSig = GetMainFunction(ir);
|
|
check(MainSig);
|
|
for (auto const& Pair : Visitor.SamplerStates)
|
|
{
|
|
MainSig->parameters.push_tail(Pair.Value);
|
|
}
|
|
}
|
|
|
|
struct FMetalBreakPrecisionChangesVisitor final : public ir_rvalue_visitor
|
|
{
|
|
_mesa_glsl_parse_state* State;
|
|
|
|
std::map<ir_rvalue*, ir_variable*, ir_type_compare<ir_rvalue>> ReplacedVars;
|
|
|
|
FMetalBreakPrecisionChangesVisitor(_mesa_glsl_parse_state* InState) : State(InState) {}
|
|
|
|
virtual ~FMetalBreakPrecisionChangesVisitor() { }
|
|
|
|
void ConvertBlock(exec_list* instructions)
|
|
{
|
|
FMetalBreakPrecisionChangesVisitor Visitor(State);
|
|
Visitor.run(instructions);
|
|
}
|
|
|
|
virtual ir_visitor_status visit_enter(ir_if* ir) override
|
|
{
|
|
ir->condition->accept(this);
|
|
handle_rvalue(&ir->condition);
|
|
|
|
ConvertBlock(&ir->then_instructions);
|
|
ConvertBlock(&ir->else_instructions);
|
|
|
|
/* already descended into the children. */
|
|
return visit_continue_with_parent;
|
|
}
|
|
|
|
ir_visitor_status visit_enter(ir_loop* ir)
|
|
{
|
|
ConvertBlock(&ir->body_instructions);
|
|
|
|
/* already descended into the children. */
|
|
return visit_continue_with_parent;
|
|
}
|
|
|
|
virtual void handle_rvalue(ir_rvalue** RValuePtr) override
|
|
{
|
|
if (!RValuePtr || !*RValuePtr)
|
|
{
|
|
return;
|
|
}
|
|
bool bGenerateNewVar = false;
|
|
auto* RValue = *RValuePtr;
|
|
auto* Expression = RValue->as_expression();
|
|
auto* Constant = RValue->as_constant();
|
|
auto* DeRef = RValue->as_dereference();
|
|
auto* DeRefImage = RValue->as_dereference_image();
|
|
if (Expression)
|
|
{
|
|
switch (Expression->operation)
|
|
{
|
|
case ir_unop_h2f:
|
|
case ir_unop_f2h:
|
|
if (!Expression->operands[0]->as_texture())
|
|
{
|
|
bGenerateNewVar = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (Constant)
|
|
{
|
|
if (Constant->type->base_type == GLSL_TYPE_HALF)
|
|
{
|
|
bGenerateNewVar = true;
|
|
}
|
|
}
|
|
else if (DeRefImage)
|
|
{
|
|
auto* Var = DeRef->variable_referenced();
|
|
if (!in_assignee && Var && Var->type && Var->type->is_image())
|
|
{
|
|
// RW indices have to be unsigned
|
|
if (DeRefImage->image_index->type && DeRefImage->image_index->type->base_type == GLSL_TYPE_INT)
|
|
{
|
|
auto* NewType = glsl_type::get_instance(GLSL_TYPE_UINT, DeRefImage->image_index->type->vector_elements, DeRefImage->image_index->type->matrix_columns);
|
|
auto* NewExpression = new(State) ir_expression(ir_unop_i2u, NewType, DeRefImage->image_index);
|
|
DeRefImage->image_index = NewExpression;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bGenerateNewVar)
|
|
{
|
|
for (auto& Iter : ReplacedVars)
|
|
{
|
|
auto* TestRValue = Iter.first;
|
|
if (AreEquivalent(TestRValue, RValue))
|
|
{
|
|
*RValuePtr = new(State)ir_dereference_variable(Iter.second);
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto* NewVar = new(State)ir_variable(RValue->type, nullptr, ir_var_temporary);
|
|
auto* NewAssignment = new(State)ir_assignment(new(State)ir_dereference_variable(NewVar), RValue);
|
|
*RValuePtr = new(State)ir_dereference_variable(NewVar);
|
|
ReplacedVars[RValue] = NewVar;
|
|
base_ir->insert_before(NewVar);
|
|
base_ir->insert_before(NewAssignment);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
void FMetalCodeBackend::BreakPrecisionChangesVisitor(exec_list* ir, _mesa_glsl_parse_state* State)
|
|
{
|
|
FMetalBreakPrecisionChangesVisitor Visitor(State);
|
|
Visitor.run(ir);
|
|
}
|
|
|
|
struct FDeReferencePackedVarsVisitor final : public ir_rvalue_visitor
|
|
{
|
|
_mesa_glsl_parse_state* State;
|
|
int ExpressionDepth;
|
|
|
|
FDeReferencePackedVarsVisitor(_mesa_glsl_parse_state* InState)
|
|
: State(InState)
|
|
, ExpressionDepth(0)
|
|
{
|
|
}
|
|
|
|
virtual ~FDeReferencePackedVarsVisitor()
|
|
{
|
|
}
|
|
|
|
virtual ir_visitor_status visit_leave(ir_assignment * ir) override
|
|
{
|
|
auto* DerefRecord = ir->lhs->as_dereference_record();
|
|
if (DerefRecord)
|
|
{
|
|
auto* StructVar = ir->lhs->variable_referenced();
|
|
if (StructVar->type->base_type == GLSL_TYPE_IMAGE && StructVar->type->inner_type->is_record() && StructVar->type->inner_type->HlslName && !strcmp(StructVar->type->inner_type->HlslName, "__PACKED__"))
|
|
{
|
|
handle_rvalue((ir_rvalue**)&ir->lhs);
|
|
}
|
|
}
|
|
return ir_rvalue_visitor::visit_leave(ir);
|
|
}
|
|
|
|
virtual ir_visitor_status visit_enter(ir_expression* ir) override
|
|
{
|
|
++ExpressionDepth;
|
|
return ir_rvalue_visitor::visit_enter(ir);
|
|
}
|
|
|
|
virtual ir_visitor_status visit_leave(ir_expression* ir) override
|
|
{
|
|
for (uint32 i = 0; i < ir->get_num_operands(); ++i)
|
|
{
|
|
auto* Operand = ir->operands[i];
|
|
auto* DerefRecord = Operand->as_dereference_record();
|
|
if (DerefRecord)
|
|
{
|
|
handle_rvalue(&ir->operands[i]);
|
|
}
|
|
}
|
|
|
|
--ExpressionDepth;
|
|
return ir_rvalue_visitor::visit_leave(ir);
|
|
}
|
|
|
|
virtual ir_visitor_status visit_leave(ir_dereference_record * ir) override
|
|
{
|
|
auto* StructVar = ir->variable_referenced();
|
|
if (StructVar->type->base_type == GLSL_TYPE_IMAGE && StructVar->type->inner_type->is_record() && StructVar->type->inner_type->HlslName && !strcmp(StructVar->type->inner_type->HlslName, "__PACKED__"))
|
|
{
|
|
handle_rvalue((ir_rvalue**)&ir);
|
|
}
|
|
return ir_rvalue_visitor::visit_leave(ir);
|
|
}
|
|
|
|
virtual void handle_rvalue(ir_rvalue** RValuePtr) override
|
|
{
|
|
if (!RValuePtr || !*RValuePtr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto* DeRefRecord = (*RValuePtr)->as_dereference_record();
|
|
auto* Swizzle = (*RValuePtr)->as_swizzle();
|
|
auto* SwizzleValDeRefRecord = Swizzle ? Swizzle->val->as_dereference_record() : nullptr;
|
|
if (SwizzleValDeRefRecord)
|
|
{
|
|
auto* StructVar = Swizzle->variable_referenced();
|
|
if (StructVar->type->HlslName && !strcmp(StructVar->type->HlslName, "__PACKED__"))
|
|
{
|
|
if (SwizzleValDeRefRecord->type->vector_elements > 1 && SwizzleValDeRefRecord->type->vector_elements < 4)
|
|
{
|
|
auto* Var = GetVar(StructVar, SwizzleValDeRefRecord);
|
|
Swizzle->val = new(State)ir_dereference_variable(Var);
|
|
}
|
|
}
|
|
}
|
|
else if (DeRefRecord)
|
|
{
|
|
auto* StructVar = DeRefRecord->variable_referenced();
|
|
if (ExpressionDepth > 0 && StructVar->type->HlslName && !strcmp(StructVar->type->HlslName, "__PACKED__"))
|
|
{
|
|
if (DeRefRecord->type->vector_elements > 1 && DeRefRecord->type->vector_elements < 4)
|
|
{
|
|
auto* Var = GetVar(StructVar, DeRefRecord);
|
|
*RValuePtr = new(State)ir_dereference_variable(Var);
|
|
}
|
|
}
|
|
else if (StructVar->type->base_type == GLSL_TYPE_IMAGE && StructVar->type->inner_type->is_record() && StructVar->type->inner_type->HlslName && !strcmp(StructVar->type->inner_type->HlslName, "__PACKED__"))
|
|
{
|
|
if (DeRefRecord->type->vector_elements > 1 && DeRefRecord->type->vector_elements < 4)
|
|
{
|
|
auto* Var = GetVar(StructVar, DeRefRecord);
|
|
*RValuePtr = new(State)ir_dereference_variable(Var);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::map<ir_dereference_record*, ir_variable*, ir_type_compare<ir_dereference_record>> Replaced;
|
|
std::map<ir_variable*, ir_variable*, ir_type_compare<ir_variable>> Replacements;
|
|
|
|
ir_variable* GetVar(ir_variable* Orig, ir_dereference_record* ir)
|
|
{
|
|
ir_variable* Var = nullptr;
|
|
for (auto& Pair : Replaced)
|
|
{
|
|
if (Pair.first->IsEquivalent(ir))
|
|
{
|
|
Var = Pair.second;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Var)
|
|
{
|
|
Var = new(State)ir_variable(ir->type, nullptr, ir_var_temporary);
|
|
Replaced[ir] = Var;
|
|
Replacements[Var] = Orig;
|
|
}
|
|
return Var;
|
|
}
|
|
};
|
|
|
|
void FMetalCodeBackend::RemovePackedVarReferences(exec_list* ir, _mesa_glsl_parse_state* State)
|
|
{
|
|
FDeReferencePackedVarsVisitor Visitor(State);
|
|
Visitor.run(ir);
|
|
|
|
if (Visitor.Replaced.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
ir_function_signature* Main = GetMainFunction(ir);
|
|
for (auto& Pair : Visitor.Replacements)
|
|
{
|
|
auto* NewVar = Pair.first;
|
|
auto* OldVar = Pair.second;
|
|
if (OldVar->mode == ir_var_uniform || OldVar->mode == ir_var_in || OldVar->mode == ir_var_out)
|
|
{
|
|
Main->body.push_head(NewVar);
|
|
}
|
|
else
|
|
{
|
|
OldVar->insert_after(NewVar);
|
|
}
|
|
}
|
|
|
|
for (auto& OuterPair : Visitor.Replaced)
|
|
{
|
|
auto* NewVar = OuterPair.second;
|
|
auto* DeRefRecord = OuterPair.first;
|
|
auto* NewAssignment = new(State)ir_assignment(new(State)ir_dereference_variable(NewVar), DeRefRecord);
|
|
NewVar->insert_after(NewAssignment);
|
|
}
|
|
}
|
|
|
|
|
|
namespace MetalUtils
|
|
{
|
|
struct FBaseIDSystemValueTable
|
|
{
|
|
const char* TargetMetalName;
|
|
const char* BaseMetalName;
|
|
const char* BaseMetalSemantic;
|
|
const char* MappedMetalName;
|
|
TArray<ir_rvalue**> FoundItems;
|
|
};
|
|
|
|
struct FVertexStreamIDVisitor final : public ir_rvalue_visitor
|
|
{
|
|
FBaseIDSystemValueTable* ReplaceLookupTable;
|
|
|
|
FVertexStreamIDVisitor(FBaseIDSystemValueTable* LookUpTable)
|
|
: ReplaceLookupTable(LookUpTable)
|
|
{}
|
|
|
|
virtual ~FVertexStreamIDVisitor()
|
|
{}
|
|
|
|
virtual void handle_rvalue(ir_rvalue** RValuePtr) override
|
|
{
|
|
if (!RValuePtr || !*RValuePtr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ir_variable* Variable = (*RValuePtr)->variable_referenced();
|
|
|
|
// Needs to be correct mode and and name
|
|
if(Variable && Variable->mode == ir_var_in)
|
|
{
|
|
for(FBaseIDSystemValueTable* BaseIDRow = ReplaceLookupTable; BaseIDRow->TargetMetalName != NULL; ++BaseIDRow)
|
|
{
|
|
if(FCStringAnsi::Stricmp(BaseIDRow->TargetMetalName, Variable->name) == 0)
|
|
{
|
|
BaseIDRow->FoundItems.Push(RValuePtr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
void FMetalCodeBackend::FixupMetalBaseOffsets(exec_list* ir, _mesa_glsl_parse_state* ParseState, EHlslShaderFrequency Frequency)
|
|
{
|
|
using namespace MetalUtils;
|
|
|
|
if(bIsTessellationVSHS || Frequency != HSF_VertexShader)
|
|
{
|
|
return; // do nothing
|
|
}
|
|
|
|
FBaseIDSystemValueTable BaseIDLUT[] =
|
|
{
|
|
{"IN_VertexID", "IN_BaseVertex", "[[ base_vertex ]]", "Mapped_VertexID"},
|
|
{"IN_InstanceID", "IN_BaseInstance", "[[ base_instance ]]", "Mapped_InstanceID"},
|
|
{NULL, NULL, NULL, NULL}
|
|
};
|
|
|
|
ir_function_signature* EntryPointSig = GetMainFunction(ir);
|
|
check(EntryPointSig);
|
|
|
|
// Find existing references - only remap these
|
|
FVertexStreamIDVisitor Visitor(BaseIDLUT);
|
|
Visitor.run(&EntryPointSig->body);
|
|
|
|
// Update references if any new variables added
|
|
for(FBaseIDSystemValueTable* BaseIDRow = BaseIDLUT; BaseIDRow->TargetMetalName != NULL; ++BaseIDRow)
|
|
{
|
|
for(int32 i = 0;i < BaseIDRow->FoundItems.Num();++i)
|
|
{
|
|
ir_rvalue** RValue = BaseIDRow->FoundItems[i];
|
|
|
|
check(RValue);
|
|
check(*RValue);
|
|
ir_variable* Variable = (*RValue)->variable_referenced();
|
|
check(Variable);
|
|
|
|
// double check this is correct - should be though since it's in array
|
|
if(FCStringAnsi::Stricmp(BaseIDRow->TargetMetalName, Variable->name) == 0)
|
|
{
|
|
// Add new recomputed ID variable - should only add first time for this row
|
|
ir_variable* MappedIDVariable = ParseState->symbols->get_variable(BaseIDRow->MappedMetalName);
|
|
if(!MappedIDVariable)
|
|
{
|
|
// No mapped name make ensure we have added the base ID type
|
|
ir_variable* NewInputVariable = ParseState->symbols->get_variable(BaseIDRow->BaseMetalName);
|
|
if(!NewInputVariable)
|
|
{
|
|
NewInputVariable = new(ParseState) ir_variable(Variable->type, BaseIDRow->BaseMetalName, Variable->mode);
|
|
NewInputVariable->semantic = BaseIDRow->BaseMetalSemantic;
|
|
NewInputVariable->read_only = Variable->read_only;
|
|
ParseState->symbols->add_variable(NewInputVariable);
|
|
EntryPointSig->parameters.push_tail(NewInputVariable);
|
|
}
|
|
|
|
MappedIDVariable = new(ParseState) ir_variable(Variable->type, BaseIDRow->MappedMetalName, ir_var_auto);
|
|
MappedIDVariable->semantic = NULL;
|
|
MappedIDVariable->read_only = false;
|
|
ParseState->symbols->add_variable(MappedIDVariable);
|
|
|
|
// Update ID to take account of base ID
|
|
EntryPointSig->body.push_head(
|
|
new(ParseState)ir_assignment(
|
|
new(ParseState) ir_dereference_variable(MappedIDVariable),
|
|
new(ParseState) ir_expression(
|
|
ir_binop_sub,
|
|
new(ParseState) ir_dereference_variable(Variable),
|
|
new(ParseState) ir_dereference_variable(NewInputVariable)
|
|
)
|
|
)
|
|
);
|
|
|
|
// Add decl above computation
|
|
EntryPointSig->body.push_head(MappedIDVariable);
|
|
}
|
|
|
|
// Remap use of this variable to the new mapped variable
|
|
*RValue = new(ParseState)ir_dereference_variable(MappedIDVariable);
|
|
}
|
|
}
|
|
}
|
|
}
|