You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Disabled by default, set r.Mobile.VirtualTextures=1 to enable known issues: RVT compression is not implementted, Android OpenGL will have R and G channels swapped for uncompressed streaming VT, no sRGB support #jira UE-79955 #rb jeremy.moore #ROBOMERGE-SOURCE: CL 11070636 via CL 11070637 #ROBOMERGE-BOT: (v637-11041722) [CL 11070638 by dmitriy dyomin in Main branch]
3651 lines
125 KiB
C++
3651 lines
125 KiB
C++
// Copyright 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) ]]"},
|
|
{"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) ]]"},
|
|
{"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::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) ]]"},
|
|
{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() || (Var->type->HlslName && (!strncmp(Var->type->HlslName, "RWStructuredBuffer<", 19) || !strncmp(Var->type->HlslName, "StructuredBuffer<", 17))));
|
|
bool bIsByteAddressBuffer = Var->type->sampler_buffer && (Var->type->HlslName && (!strncmp(Var->type->HlslName, "RWByteAddressBuffer", 19) || !strncmp(Var->type->HlslName, "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() && (Var->type->HlslName && (!strncmp(Var->type->HlslName, "RWStructuredBuffer<", 19) || !strncmp(Var->type->HlslName, "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
|
|
{
|
|
// New variable declaration including its initialization must be inserted *after* the original variable has been initialized.
|
|
// So look ahead until we find the <ir_assignment> node that initializes "OldVar".
|
|
ir_assignment* OldVarInit = nullptr;
|
|
for (exec_node* Node = OldVar->next; Node && !OldVarInit; Node = Node->next)
|
|
{
|
|
ir_instruction* Instr = static_cast<ir_instruction*>(Node);
|
|
if (Instr->ir_type == ir_type_assignment)
|
|
{
|
|
ir_assignment* AssignStmt = static_cast<ir_assignment*>(Instr);
|
|
if (AssignStmt->lhs->variable_referenced() == OldVar)
|
|
{
|
|
OldVarInit = AssignStmt;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OldVarInit != nullptr)
|
|
{
|
|
OldVarInit->insert_after(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);
|
|
}
|
|
}
|
|
}
|
|
}
|