Ejemplo n.º 1
0
	int ChoiceForm::AutotuneHelper(HashSet<String> & selectedChoices, EnumerableDictionary<String, Spire::Compiler::ShaderChoiceValue>& currentChoices, float timeBudget, bool countOnly)
	{
		auto choices = choiceControl->GetChoices(currentShaderName, currentChoices);
		List<Spire::Compiler::ShaderChoice> filteredChoices;
		for (auto & choice : choices)
			if (!currentChoices.ContainsKey(choice.ChoiceName))
				filteredChoices.Add(choice);
		choices = _Move(filteredChoices);
		// find first non-trivial choice
		Spire::Compiler::ShaderChoice * currentChoice = nullptr;
		for (auto & choice : choices)
		{
			if (choice.Options.Count() > 1 && selectedChoices.Contains(choice.ChoiceName))
			{
				currentChoice = &choice;
				break;
			}
		}
		if (currentChoice)
		{
			int count = 0;
			for (auto & opt : currentChoice->Options)
			{
				EnumerableDictionary<String, Spire::Compiler::ShaderChoiceValue> newChoices = currentChoices;
				newChoices[currentChoice->ChoiceName] = opt;
				count += AutotuneHelper(selectedChoices, newChoices, timeBudget, countOnly);
			}
			return count;
		}
		else
		{
			if (!countOnly)
			{
				printf("Testing shader variant...");
				// compile and evaluate
				auto schedule = GenerateSchedule(currentChoices, EnumerableDictionary<String, EnumerableDictionary<String, String>>());
				choiceControl->RecompileShader(currentShaderName, schedule);
				// render 1000 frames and measure time
				float minTime = 1e30f;
				for (int f = 0; f < 20; f++)
				{
					glFinish();
					auto timeP = CoreLib::Diagnostics::PerformanceCounter::Start();
					for (int i = 0; i < 10; i++)
					{
						choiceControl->RenderFrame();
					}
					glFinish();

					auto time = (float)CoreLib::Diagnostics::PerformanceCounter::ToSeconds(CoreLib::Diagnostics::PerformanceCounter::End(timeP)) * 100.0f;
					if (time < minTime)
						minTime = time;
				}


				choiceControl->UpdateWindow();
				auto frameData = ReadFrameData(choiceControl->RenderFrame());
				float error = MeasureError(referenceFrame, frameData);
				float value = 0;
				if (minTime < timeBudget)
					value = error;
				else
					value = 20.0f + minTime;
				if (value < currentBestValue)
				{
					currentBestValue = value;
					currentBestChoices = currentChoices;
				}
				autotuningLog << minTime << L"," << error<<EndLine;
				printf("%f ms, error: %f\n", minTime, error);
			}
			return 1;
		}
	}
Ejemplo n.º 2
0
			/* Generate a shader variant by applying mechanic choice rules and the choice file.
			   The choice file provides "preferred" definitions, as represented in ShaderComponentSymbol::Type::PinnedWorlds
		       The process resolves the component references by picking a pinned definition if one is available, or a definition
			   with the preferred import path as defined by import operator ordering.
			   After all references are resolved, all unreferenced definitions (dead code) are eliminated, 
			   resulting a shader variant ready for code generation.
			*/
			RefPtr<ShaderIR> GenerateShaderVariantIR(CompileResult & cresult, ShaderClosure * shader, Schedule & schedule, SymbolTable * symbolTable)
			{
				RefPtr<ShaderIR> result = new ShaderIR();
				result->Shader = shader;
				result->SymbolTable = symbolTable;
				// mark pinned worlds
				for (auto & comp : shader->Components)
				{
					for (auto & impl : comp.Value->Implementations)
					{
						for (auto & w : impl->Worlds)
						{
							if (impl->SrcPinnedWorlds.Contains(w) || impl->SyntaxNode->IsInline() || impl->ExportWorlds.Contains(w) || impl->SyntaxNode->IsInput())
							{
								comp.Value->Type->PinnedWorlds.Add(w);
							}
						}
					}
				}
				// apply choices
				Dictionary<String, ShaderComponentSymbol*> choiceComps;
				for (auto & comp : shader->AllComponents)
				{
					for (auto & choiceName : comp.Value.Symbol->ChoiceNames)
						choiceComps[choiceName] = comp.Value.Symbol;
				}
				HashSet<ShaderComponentImplSymbol*> pinnedImpl;
				for (auto & choice : schedule.Choices)
				{
					ShaderComponentSymbol * comp = nullptr;
					if (choiceComps.TryGetValue(choice.Key, comp))
					{
						comp->Type->PinnedWorlds.Clear();
						for (auto & selectedDef : choice.Value)
						{
							if (comp->Type->ConstrainedWorlds.Contains(selectedDef->WorldName))
							{
								comp->Type->PinnedWorlds.Add(selectedDef->WorldName);
								// find specified impl
								for (auto & impl : comp->Implementations)
								{
									if (impl->Worlds.Contains(selectedDef->WorldName))
										pinnedImpl.Add(impl.Ptr());
								}
							}
							else
							{
                                cresult.GetErrorWriter()->diagnose(selectedDef.Ptr()->Position, Diagnostics::worldIsNotAValidChoiceForKey, selectedDef->WorldName, choice.Key);
							}
						}
					}
				}
				for (auto & attribs : schedule.AddtionalAttributes)
				{
					ShaderComponentSymbol * comp = nullptr;
					if (choiceComps.TryGetValue(attribs.Key, comp))
					{
						// apply attributes
						for (auto & impl : comp->Implementations)
						{
                            for (auto & attrib : attribs.Value)
                            {
                                auto modifier = new SimpleAttribute();
                                modifier->Key = attrib.Key;
                                modifier->Value.Content = attrib.Value;

                                modifier->next = impl->SyntaxNode->modifiers.first;
                                impl->SyntaxNode->modifiers.first = modifier;
                            }
						}
					}
				}
				// generate definitions
				Dictionary<ShaderClosure*, ModuleInstanceIR*> moduleInstanceMap;
				auto createModuleInstance = [&](ShaderClosure * closure)
				{
					ModuleInstanceIR * inst;
					if (moduleInstanceMap.TryGetValue(closure, inst))
						return inst;
					List<String> namePath;
					
					auto parent = closure;
					while (parent)
					{
						if (parent->Name.Length())
							namePath.Add(parent->Name);
						else
							namePath.Add(parent->ModuleSyntaxNode->Name.Content);
						parent = parent->Parent;
					}
					StringBuilder sbBindingName;
					for (int i = namePath.Count() - 2; i >= 0; i--)
					{
						sbBindingName << namePath[i];
						if (i > 0)
							sbBindingName << ".";
					}
					// if this is the root module, its binding name is module name.
					if (namePath.Count() == 1)
						sbBindingName << namePath[0];
					inst = new ModuleInstanceIR();
					inst->SyntaxNode = closure->ModuleSyntaxNode;
					inst->BindingIndex = closure->BindingIndex;
					inst->UsingPosition = closure->UsingPosition;
					result->ModuleInstances.Add(inst);
					inst->BindingName = sbBindingName.ProduceString();
					moduleInstanceMap[closure] = inst;
					return inst;
				};
				for (auto & comp : shader->AllComponents)
				{
					EnumerableDictionary<String, ComponentDefinitionIR*> defs;
					Dictionary<String, ShaderComponentImplSymbol*> impls;
					for (auto & impl : comp.Value.Symbol->Implementations)
					{
						auto createComponentDef = [&](const String & w)
						{
							RefPtr<ComponentDefinitionIR> def = new ComponentDefinitionIR();
							def->OriginalName = comp.Value.Symbol->Name;
							def->UniqueKey = comp.Value.Symbol->UniqueKey;
							def->UniqueName = comp.Value.Symbol->UniqueName;
							def->Type = comp.Value.Symbol->Type->DataType;
							def->IsEntryPoint = (impl->ExportWorlds.Contains(w) || impl->SyntaxNode->IsParam() ||
								(shader->Pipeline->IsAbstractWorld(w) &&
								(impl->SyntaxNode->HasSimpleAttribute("Pinned") || shader->Pipeline->Worlds[w]()->HasSimpleAttribute("Pinned"))));
							CloneContext cloneCtx;
							def->SyntaxNode = impl->SyntaxNode->Clone(cloneCtx);
							def->World = w;
							def->ModuleInstance = createModuleInstance(comp.Value.Closure);
							return def;
						};
						// parameter component will only have one defintion that is shared by all worlds
						if (impl->SyntaxNode->IsParam())
						{
							auto def = createComponentDef("<uniform>");
							result->Definitions.Add(def);
							defs["<uniform>"] = def.Ptr();
						}
						else
						{
							for (auto & w : impl->Worlds)
							{
								auto def = createComponentDef(w);
								result->Definitions.Add(def);
								bool existingDefIsPinned = false;
								if (defs.ContainsKey(w))
									existingDefIsPinned = pinnedImpl.Contains(impls[w]());
								if (!existingDefIsPinned)
								{
									defs[w] = def.Ptr();
									impls[w] = impl.Ptr();
								}
							}
						}
					}
					result->DefinitionsByComponent[comp.Key] = defs;
				}
				bool changed = true;
				while (changed)
				{
					changed = false;
					result->ResolveComponentReference();
					result->EliminateDeadCode();
					// check circular references
					for (auto & def : result->Definitions)
					{
						if (def->Dependency.Contains(def.Ptr()))
						{
                            cresult.GetErrorWriter()->diagnose(def->SyntaxNode->Position, Diagnostics::componentDefinitionCircularity, def->OriginalName);
							return nullptr;
						}
					}
					/*
					// eliminate redundant (downstream) definitions, one at a time
					auto comps = result->GetComponentDependencyOrder();
					for (int i = comps.Count() - 1; i >= 0; i--)
					{
						auto comp = comps[i];
						auto & defs = result->DefinitionsByComponent[comp->UniqueName]();
						EnumerableHashSet<ComponentDefinitionIR*> removedDefs;
						for (auto & def : defs)
							if (!def.Value->IsEntryPoint && !comp->Type->PinnedWorlds.Contains(def.Value->World))
							{
								for (auto & otherDef : defs)
								{
									if (otherDef.Value != def.Value && !removedDefs.Contains(otherDef.Value)
										&& shader->Pipeline->IsWorldReachable(otherDef.Value->World, def.Value->World))
									{
										removedDefs.Add(def.Value);
										break;
									}
								}
							}
						if (removedDefs.Count())
						{
							result->RemoveDefinitions([&](ComponentDefinitionIR* def) {return removedDefs.Contains(def); });
							changed = true;
						}
					}
					*/
				}
				return result;
			}