public void OnEventTest([Values] CodeGenMode mode) { SetupTestGraphMultipleFrames(mode, g => { ComponentQueryDeclarationModel query = GraphModel.CreateComponentQuery("g1"); TypeHandle positionType = typeof(Translation).GenerateTypeHandle(Stencil); query.AddComponent(Stencil, positionType, ComponentDefinitionFlags.None); IVariableModel queryInstance = GraphModel.CreateVariableNode(query, Vector2.zero); TypeHandle eventTypeHandle = typeof(UnitTestEvent).GenerateTypeHandle(Stencil); OnEventNodeModel onUpdateModel = GraphModel.CreateNode <OnEventNodeModel>("update", Vector2.zero, SpawnFlags.Default, n => n.EventTypeHandle = eventTypeHandle); GraphModel.CreateEdge(onUpdateModel.InstancePort, queryInstance.OutputPort); SetPropertyGroupNodeModel set = onUpdateModel.CreateStackedNode <SetPropertyGroupNodeModel>("set", 0); var member = new TypeMember(TypeHandle.Float, new List <string> { nameof(Translation.Value), nameof(Translation.Value.x) }); set.AddMember(member); IVariableModel posComponent = GraphModel.CreateVariableNode(onUpdateModel.FunctionParameterModels.Single(p => p.DataType == positionType), Vector2.zero); GraphModel.CreateEdge(set.InstancePort, posComponent.OutputPort); ((FloatConstantModel)set.InputConstantsById[member.GetId()]).value = 2f; }, // Add translation to even entities EachEntity((manager, i, e) => { if (e.Index % 2 == 0) { manager.AddComponent(e, typeof(Translation)); } }), // Send event on these EachEntity(del: (manager, i, e) => { if (e.Index % 2 == 0) { Assert.That(manager.GetComponentData <Translation>(e).Value.x, Is.EqualTo(0f)); } else { Assert.IsFalse(manager.HasComponent <Translation>(e)); } // Add event on all entities manager.AddBuffer <UnitTestEvent>(e).Add(new UnitTestEvent()); }), // Event handler should set t.x to 2 EachEntity((manager, i, e) => { if (e.Index % 2 == 0) { Assert.That(manager.GetComponentData <Translation>(e).Value.x, Is.EqualTo(2f)); } }), // Reset translation EachEntity((manager, i, e) => { if (e.Index % 2 == 0) { var t = manager.GetComponentData <Translation>(e); t.Value.x = 0; manager.SetComponentData(e, t); } }), // As we send events manually, the event system is not running and won't cleanup events. // Event handler should set t.x to 2 again EachEntity((manager, i, e) => { if (e.Index % 2 == 0) { Assert.That(manager.GetComponentData <Translation>(e).Value.x, Is.EqualTo(2f)); } }) ); }
BaseTypeSyntax MakeJobBaseType(RoslynEcsTranslator.IterationContext iterationContext, List <ParameterSyntax> functionParameters, out Type eventType) { var genericArguments = new List <TypeSyntax>(); eventType = null; int componentCount = 0; int bufferCount = 0; if (iterationContext.UpdateMode != UpdateMode.OnEnd) { if (iterationContext.UpdateMode == UpdateMode.OnEvent) // add DynamicBuffer<eventType> { eventType = ((OnEventNodeModel)iterationContext.Query).EventTypeHandle.Resolve(iterationContext.Stencil); var eventTypeSyntax = TypeSystem.BuildTypeSyntax(eventType); var bufferType = GenericName("DynamicBuffer") .WithTypeArgumentList(TypeArgumentList(SingletonSeparatedList(eventTypeSyntax))); var parameterSyntax = Parameter( Identifier(OnEventNodeModel.GetBufferName(eventType))) .WithType(bufferType); AddReadOnlyAttribute(ref parameterSyntax); bufferCount = 1; functionParameters.Add(parameterSyntax); genericArguments.Add(eventTypeSyntax); } foreach (ComponentDefinition definition in iterationContext.FlattenedComponentDefinitions().Where(def => !def.Subtract)) { if (!RoslynEcsTranslatorExtensions.ShouldGenerateComponentAccess(definition.TypeHandle, true, out Type resolvedType, iterationContext.Stencil, out bool isShared, out bool isGameObjectComponent) || isGameObjectComponent) { continue; } if (isShared) { throw new RoslynEcsTranslator.JobSystemNotCompatibleException("Shared Components are not supported in jobs yet"); } genericArguments.Add(TypeSystem.BuildTypeSyntax(resolvedType)); var parameterSyntax = Parameter( Identifier(GetComponentVariableName(iterationContext.Query, definition.TypeHandle))) .WithModifiers(TokenList(Token(SyntaxKind.RefKeyword))) .WithType(TypeSystem.BuildTypeSyntax(resolvedType)); if (!m_WrittenComponents.Contains(definition.TypeHandle)) { AddReadOnlyAttribute(ref parameterSyntax); } functionParameters.Add( parameterSyntax); componentCount++; } if (genericArguments.Count == 0) { return(null); } } string CleanGenericName(string s) { var index = s.IndexOf('`'); return(index == -1 ? s : s.Substring(0, index)); } // IJobForEachWitEntity_EBBCCC string suffix = componentCount > 0 || bufferCount > 0 ? $"_E{new String('B', bufferCount)}{new String('C', componentCount)}" : ""; return(SimpleBaseType( GenericName( Identifier(CleanGenericName(typeof(IJobForEachWithEntity <>).Name) + suffix)) .WithTypeArgumentList( TypeArgumentList( SeparatedList( genericArguments))))); ParameterSyntax AddReadOnlyAttribute(ref ParameterSyntax parameterSyntax) { return(parameterSyntax = parameterSyntax.WithAttributeLists( SingletonList( AttributeList( SingletonSeparatedList( Attribute( IdentifierName(nameof(ReadOnlyAttribute)))))))); } }
MemberDeclarationSyntax MakeJobUpdateOverride(List <ParameterSyntax> functionParameters, List <StatementSyntax> updateStatements, string trackingComponentName, Type eventType) { var blockSyntax = Block(updateStatements); switch (IterationContext.UpdateMode) { case UpdateMode.OnStart: blockSyntax = blockSyntax.AddStatements(GetEntityManipulationTranslator().AddComponent( this, IdentifierName(EntityName), DefaultExpression(IdentifierName(trackingComponentName)), IdentifierName(trackingComponentName), false).ToArray()); break; case UpdateMode.OnEvent: Assert.IsNotNull(eventType); const string eventIndexName = "event_idx"; string eventBufferName = OnEventNodeModel.GetBufferName(eventType); var eventDeclaration = RoslynBuilder.DeclareLocalVariable(eventType, "ev" /* TODO hardcoded event name*/, ElementAccessExpression( IdentifierName(eventBufferName)) .WithArgumentList( BracketedArgumentList( SingletonSeparatedList( Argument( IdentifierName(eventIndexName)))))); blockSyntax = Block(ForStatement(Block(blockSyntax.Statements.Insert(0, eventDeclaration))) .WithDeclaration( VariableDeclaration( PredefinedType( Token(SyntaxKind.IntKeyword))) .WithVariables( SingletonSeparatedList( VariableDeclarator( Identifier(eventIndexName)) .WithInitializer( EqualsValueClause( LiteralExpression( SyntaxKind.NumericLiteralExpression, Literal(0))))))) .WithCondition( BinaryExpression( SyntaxKind.LessThanExpression, IdentifierName(eventIndexName), MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(eventBufferName), IdentifierName("Length")))) .WithIncrementors( SingletonSeparatedList <ExpressionSyntax>( PostfixUnaryExpression( SyntaxKind.PostIncrementExpression, IdentifierName(eventIndexName))))); break; } var executeMethod = MethodDeclaration( PredefinedType( Token(SyntaxKind.VoidKeyword)), Identifier("Execute")) .WithModifiers( TokenList( Token(SyntaxKind.PublicKeyword))) .WithParameterList( ParameterList( SeparatedList(functionParameters))) .WithBody(blockSyntax); return(executeMethod); }
MemberDeclarationSyntax MakeJobUpdateOverride(List <ParameterSyntax> functionParameters, List <StatementSyntax> updateStatements, string trackingComponentName, Type eventType) { var blockSyntax = Block(updateStatements); switch (IterationContext.UpdateMode) { case UpdateMode.OnStart: blockSyntax = blockSyntax.AddStatements(GetEntityManipulationTranslator().AddComponent( this, IdentifierName(EntityName), DefaultExpression(IdentifierName(trackingComponentName)), IdentifierName(trackingComponentName), false).ToArray()); break; case UpdateMode.OnEvent: Assert.IsNotNull(eventType); const string eventIndexName = "event_idx"; string eventBufferName = OnEventNodeModel.GetBufferName(eventType); var eventDeclaration = RoslynBuilder.DeclareLocalVariable(eventType, "ev" /* TODO hardcoded event name*/, ElementAccessExpression( IdentifierName(eventBufferName)) .WithArgumentList( BracketedArgumentList( SingletonSeparatedList( Argument( IdentifierName(eventIndexName)))))); blockSyntax = Block(ForStatement(Block(blockSyntax.Statements.Insert(0, eventDeclaration))) .WithDeclaration( VariableDeclaration( PredefinedType( Token(SyntaxKind.IntKeyword))) .WithVariables( SingletonSeparatedList( VariableDeclarator( Identifier(eventIndexName)) .WithInitializer( EqualsValueClause( LiteralExpression( SyntaxKind.NumericLiteralExpression, Literal(0))))))) .WithCondition( BinaryExpression( SyntaxKind.LessThanExpression, IdentifierName(eventIndexName), MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(eventBufferName), IdentifierName("Length")))) .WithIncrementors( SingletonSeparatedList <ExpressionSyntax>( PostfixUnaryExpression( SyntaxKind.PostIncrementExpression, IdentifierName(eventIndexName))))); break; } var executeMethod = MethodDeclaration( PredefinedType( Token(SyntaxKind.VoidKeyword)), Identifier("Execute")) .WithModifiers( TokenList( Token(SyntaxKind.PublicKeyword))) .WithParameterList( ParameterList( SeparatedList(functionParameters))) .WithBody(blockSyntax); if ((TranslationOptions & RoslynEcsTranslator.TranslationOptions.Tracing) != 0) { ((SerializableGUID)IterationContext.Query.Guid).ToParts(out var guid1, out var guid2); StatementSyntax beginStatement = ExpressionStatement( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, m_RecorderJobFieldName, IdentifierName(nameof(GraphStream.Writer.BeginForEachIndex)))) .WithArgumentList( ArgumentList( SeparatedList( new[] { Argument(IdentifierName(EntityName)), Argument(IdentifierName(GetJobIndexParameterName())), Argument(LiteralExpression( SyntaxKind.NumericLiteralExpression, Literal(guid1))), Argument(LiteralExpression( SyntaxKind.NumericLiteralExpression, Literal(guid2))), })))) // .WithAdditionalAnnotations(Annotations.MakeTracingAnnotation(IterationContext.Query)) ; StatementSyntax endStatement = MakeTracingEndForEachIndexStatement(); executeMethod = executeMethod.WithBody(executeMethod.Body.WithStatements(executeMethod.Body.Statements.Insert(0, beginStatement).Add(endStatement))); } return(executeMethod); }