virtual CompileUnit Parse(CompileResult & result, String source, String fileName, IncludeHandler* includeHandler, Dictionary<String,String> const& preprocesorDefinitions) override { auto tokens = PreprocessSource(source, fileName, result.GetErrorWriter(), includeHandler, preprocesorDefinitions); CompileUnit rs; rs.SyntaxNode = ParseProgram(tokens, result.GetErrorWriter(), fileName); return rs; }
int wmain(int argc, wchar_t* argv[]) { String fileName = argv[1]; String outputDir = Path::GetDirectoryName(fileName); CompileOptions options; for (int i = 2; i < argc; i++) { if (i < argc - 1) { if (String(argv[i]) == L"-out") outputDir = argv[i + 1]; else if (String(argv[i]) == L"-symbol") options.SymbolToCompile = argv[i + 1]; else if (String(argv[i]) == L"-schedule") options.ScheduleFileName = argv[i + 1]; } if (String(argv[i]) == L"-genchoice") options.Mode = CompilerMode::GenerateChoice; } auto sourceDir = Path::GetDirectoryName(fileName); String schedule; if (options.ScheduleFileName.Length()) { try { schedule = File::ReadAllText(options.ScheduleFileName); options.ScheduleSource = schedule; } catch (IOException) { printf("Cannot open schedule file '%s'.\n", options.ScheduleFileName.ToMultiByteString()); return -1; } } CompileResult result; auto files = SpireLib::CompileShaderSourceFromFile(result, fileName, options); for (auto & f : files) { try { f.SaveToFile(Path::Combine(outputDir, f.MetaData.ShaderName + L".cse")); } catch (Exception &) { result.GetErrorWriter()->Error(4, L"cannot write output file \'" + Path::Combine(outputDir, f.MetaData.ShaderName + L".cse") + L"\'.", CodePosition(0, 0, L"")); } } result.PrintError(true); if (result.Success) return 0; return 1; }
bool ShaderLib::CompileFrom(String symbolName, String sourceFileName, String schedule) { CompileResult result; CompileOptions options; options.ScheduleSource = schedule; options.SymbolToCompile = symbolName; options.Mode = CompilerMode::ProduceShader; auto shaderLibs = CompileShaderSourceFromFile(result, sourceFileName, options); if (result.Success) { for (auto & lib : shaderLibs) { if (lib.MetaData.ShaderName == symbolName) { FromString(shaderLibs[0].ToString()); return true; } } } result.PrintError(true); return false; }
/* 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; }
virtual void Compile(CompileResult & result, CompilationContext & context, List<CompileUnit> & units, const CompileOptions & options) override { RefPtr<ProgramSyntaxNode> programSyntaxNode = new ProgramSyntaxNode(); for (auto & unit : units) { programSyntaxNode->Include(unit.SyntaxNode.Ptr()); } SymbolTable & symTable = context.Symbols; auto & shaderClosures = context.ShaderClosures; RefPtr<SyntaxVisitor> visitor = CreateSemanticsVisitor(&symTable, result.GetErrorWriter()); try { programSyntaxNode->Accept(visitor.Ptr()); if (result.GetErrorCount() > 0) return; // if user specified a template shader symbol, instantiate the template now String symbolToCompile = options.SymbolToCompile; if (symbolToCompile.Length()) { auto templateShaders = programSyntaxNode->GetMembersOfType<TemplateShaderSyntaxNode>(); for (auto & ts : templateShaders) if (ts->Name.Content == symbolToCompile) { auto shader = InstantiateShaderTemplate(result.GetErrorWriter(), &symTable, ts.Ptr(), options.TemplateShaderArguments); if (shader) { programSyntaxNode->Members.Add(shader); symbolToCompile = shader->Name.Content; programSyntaxNode->Accept(visitor.Ptr()); } break; } } visitor = nullptr; symTable.EvalFunctionReferenceClosure(); if (result.GetErrorCount() > 0) return; for (auto & shader : symTable.ShaderDependenceOrder) { if (shader->IsAbstract) continue; if (!shaderClosures.ContainsKey(shader->SyntaxNode->Name.Content)) { auto shaderClosure = CreateShaderClosure(result.GetErrorWriter(), &symTable, shader); FlattenShaderClosure(result.GetErrorWriter(), &symTable, shaderClosure.Ptr()); shaderClosures.Add(shader->SyntaxNode->Name.Content, shaderClosure); } } ResolveAttributes(&symTable); if (result.GetErrorCount() > 0) return; CodeGenBackend * backend = nullptr; switch(options.Target) { case CodeGenTarget::SPIRV: backend = backends["spirv"]().Ptr(); break; case CodeGenTarget::GLSL: backend = backends["glsl"]().Ptr(); break; case CodeGenTarget::GLSL_Vulkan: backend = backends["glsl_vk"]().Ptr(); break; case CodeGenTarget::GLSL_Vulkan_OneDesc: backend = backends["glsl_vk_onedesc"]().Ptr(); break; case CodeGenTarget::HLSL: backend = backends["hlsl"]().Ptr(); break; default: // TODO: emit an appropriate diagnostic return; } Schedule schedule; if (options.ScheduleSource != "") { schedule = Schedule::Parse(options.ScheduleSource, options.ScheduleFileName, result.GetErrorWriter()); } for (auto shader : shaderClosures) { // generate shader variant from schedule file, and also apply mechanic deduction rules if (!shader.Value->IR) shader.Value->IR = GenerateShaderVariantIR(result, shader.Value.Ptr(), schedule, &symTable); } if (options.Mode == CompilerMode::ProduceShader) { if (result.GetErrorWriter()->GetErrorCount() > 0) return; // generate IL code RefPtr<ICodeGenerator> codeGen = CreateCodeGenerator(&symTable, result); if (context.Program) { result.Program->Functions = context.Program->Functions; result.Program->Shaders = context.Program->Shaders; result.Program->Structs = context.Program->Structs; result.Program->ConstantPool = context.Program->ConstantPool; } for (auto & s : programSyntaxNode->GetStructs()) codeGen->ProcessStruct(s.Ptr()); for (auto & func : programSyntaxNode->GetFunctions()) codeGen->ProcessFunction(func.Ptr()); for (auto & shader : shaderClosures) { InsertImplicitImportOperators(result.GetErrorWriter(), shader.Value->IR.Ptr()); } if (result.GetErrorCount() > 0) return; for (auto & shader : shaderClosures) { codeGen->ProcessShader(shader.Value->IR.Ptr()); } if (result.GetErrorCount() > 0) return; // emit target code EnumerableHashSet<String> symbolsToGen; for (auto & unit : units) { for (auto & shader : unit.SyntaxNode->GetShaders()) if (!shader->IsModule) symbolsToGen.Add(shader->Name.Content); for (auto & func : unit.SyntaxNode->GetFunctions()) symbolsToGen.Add(func->Name.Content); } auto IsSymbolToGen = [&](String & shaderName) { if (symbolsToGen.Contains(shaderName)) return true; for (auto & symbol : symbolsToGen) if (shaderName.StartsWith(symbol)) return true; return false; }; for (auto & shader : result.Program->Shaders) { if ((symbolToCompile.Length() == 0 && IsSymbolToGen(shader->Name)) || EscapeCodeName(symbolToCompile) == shader->Name) { StringBuilder glslBuilder; Dictionary<String, String> targetCode; result.CompiledSource[shader->Name] = backend->GenerateShader(result, &symTable, shader.Ptr(), result.GetErrorWriter()); } } } else if (options.Mode == CompilerMode::GenerateChoice) { for (auto shader : shaderClosures) { if (options.SymbolToCompile.Length() == 0 || shader.Value->Name == options.SymbolToCompile) { auto &worldOrder = shader.Value->Pipeline->GetWorldTopologyOrder(); for (auto & comp : shader.Value->AllComponents) { ShaderChoice choice; if (comp.Value.Symbol->ChoiceNames.Count() == 0) continue; if (comp.Value.Symbol->IsRequire()) continue; choice.ChoiceName = comp.Value.Symbol->ChoiceNames.First(); for (auto & impl : comp.Value.Symbol->Implementations) { for (auto w : impl->Worlds) if (comp.Value.Symbol->Type->ConstrainedWorlds.Contains(w)) choice.Options.Add(ShaderChoiceValue(w)); } if (auto defs = shader.Value->IR->DefinitionsByComponent.TryGetValue(comp.Key)) { int latestWorldOrder = -1; for (auto & def : *defs) { int order = worldOrder.IndexOf(def.Key); if (latestWorldOrder < order) { choice.DefaultValue = def.Key; latestWorldOrder = order; } } } result.Choices.Add(choice); } } } } else { result.GetErrorWriter()->diagnose(CodePosition(), Diagnostics::unsupportedCompilerMode); return; } context.Program = result.Program; } catch (int) { } catch (...) { throw; } return; }