/** * Runs a test with the specified base filename. This will load * BaseFilename.hlsl from the test directory, compile it, and compare the output * with the contents of BaseFilename.out in the test directory. The test passes * the compilation output exactly matches the .out file. If the test fails, the * compilation output is written to BaseFilename.fail in the tests directory. * * true is returned if the test was run successfally, otherwise false is returned. */ bool RunTest(const char* BaseFilename) { char Filename[MAX_PATH]; EHlslShaderFrequency ShaderFrequency = HSF_InvalidFrequency; char* HlslSource = NULL; char* CompiledGlsl = NULL; char* ErrorLog = NULL; bool bPass = true; dprintf("Running %s...", BaseFilename); if (BaseFilename[0] == 'v' && BaseFilename[1] == 's' && BaseFilename[2] == '_') { ShaderFrequency = HSF_VertexShader; } else if (BaseFilename[0] == 'p' && BaseFilename[1] == 's' && BaseFilename[2] == '_') { ShaderFrequency = HSF_PixelShader; } else if (BaseFilename[0] == 'g' && BaseFilename[1] == 's' && BaseFilename[2] == '_') { ShaderFrequency = HSF_GeometryShader; } else if (BaseFilename[0] == 'h' && BaseFilename[1] == 's' && BaseFilename[2] == '_') { ShaderFrequency = HSF_HullShader; } else if (BaseFilename[0] == 'd' && BaseFilename[1] == 's' && BaseFilename[2] == '_') { ShaderFrequency = HSF_DomainShader; } else if (BaseFilename[0] == 'c' && BaseFilename[1] == 's' && BaseFilename[2] == '_') { ShaderFrequency = HSF_ComputeShader; } if (ShaderFrequency == HSF_InvalidFrequency) { dprintf("test must start with vs_, ps_, gs_, or cs\n"); return false; } _snprintf_s(Filename, MAX_PATH, "%s%s.hlsl", GTestDirectory, BaseFilename); HlslSource = LoadFileToString(Filename); if (HlslSource == NULL) { dprintf("can't open HLSL source '%s'\n", Filename); return false; } const int testConfigs = 2; struct TestConfig { EHlslCompileTarget target; const char* extension; const char* label; }; TestConfig targets[testConfigs] = { { HCT_FeatureLevelSM4, "", "GLSL 1.50" }, { HCT_FeatureLevelSM5, "_gl4", "GLSL 4.30" } }; dprintf( "\n"); for ( int i = 0; i < testConfigs; i++ ) { char OutFilename[MAX_PATH]; dprintf( " %s...", targets[i].label); unsigned int CCFlags = HLSLCC_PackUniforms | HLSLCC_DX11ClipSpace; FGlslCodeBackend GlslBackEnd(CCFlags); //@todo-rco: Fix me! FGlslLanguageSpec GlslLanguageSpec(false); HlslCrossCompile( Filename, HlslSource, "TestMain", ShaderFrequency, &GlslBackEnd, &GlslLanguageSpec, CCFlags, targets[i].target, &CompiledGlsl, &ErrorLog ); char* TestOutput = _asprintf( "----------------------------------------------------------------------\n" "%s\n----------------------------------------------------------------------\n%s", ErrorLog ? ErrorLog : "no errors", CompiledGlsl ? CompiledGlsl : "no compiler output" ); bool bTargetPass = false; bool bRebase = Rebase; if (TestOutput) { _snprintf_s(OutFilename, MAX_PATH, "%s%s%s.out", GTestDirectory, BaseFilename, targets[i].extension); char* ExpectedOutput = LoadFileToString(OutFilename); if (ExpectedOutput) { if (strcmp(TestOutput, ExpectedOutput) == 0) { dprintf("succeeded\n"); bTargetPass = true; } else { // check to see if it differs by just the version const char* TrimmedTestOutput; const char* TrimmedExpectedOutput; TrimmedTestOutput = strstr( TestOutput, "// Compiled by HLSLCC"); TrimmedExpectedOutput = strstr( ExpectedOutput, "// Compiled by HLSLCC"); TrimmedTestOutput = TrimmedTestOutput ? strstr( TrimmedTestOutput, "\n") : NULL; TrimmedExpectedOutput = TrimmedExpectedOutput ? strstr( TrimmedExpectedOutput, "\n") : NULL; if ( TrimmedTestOutput && TrimmedExpectedOutput && strcmp(TrimmedTestOutput, TrimmedExpectedOutput) == 0 ) { dprintf("conditional success, update version numbers\n"); bTargetPass = true; bRebase = bRebase & true; } else { dprintf("failed\n", Filename); } } free(ExpectedOutput); } else { dprintf("can't open expected output '%s'\n", OutFilename); // don't rebase files that don't have a former version bRebase = false; } if (!bTargetPass || bRebase) { FILE* fp = NULL; _snprintf_s(OutFilename, MAX_PATH, "%s%s%s.%s", GTestDirectory, BaseFilename, targets[i].extension, bRebase ? "out" : "fail"); fopen_s(&fp, OutFilename, "w"); if (fp) { fprintf(fp, "%s", TestOutput); fclose(fp); dprintf("\toutput written to '%s'\n", OutFilename); } else { dprintf("\toutput couldn't be written to '%s':\n%s\n", OutFilename, TestOutput); } } free(TestOutput); } if (CompiledGlsl) { free(CompiledGlsl); } if (ErrorLog) { free(ErrorLog); } //pass means all targets passed bPass &= bTargetPass; } return bPass; }
static int32 Run(const FRunInfo& RunInfo) { ILanguageSpec* Language = nullptr; FCodeBackend* Backend = nullptr; uint32 Flags = 0; Flags |= RunInfo.bRunCPP ? 0 : HLSLCC_NoPreprocess; FGlslLanguageSpec GlslLanguage(RunInfo.Target == HCT_FeatureLevelES2); FGlslCodeBackend GlslBackend(Flags); FMetalLanguageSpec MetalLanguage; FMetalCodeBackend MetalBackend(Flags); switch (RunInfo.BackEnd) { case CCT::FRunInfo::BE_Metal: Language = &MetalLanguage; Backend = &MetalBackend; break; case CCT::FRunInfo::BE_OpenGL: Language = &GlslLanguage; Backend = &GlslBackend; Flags |= HLSLCC_DX11ClipSpace; break; default: return 1; } FString HLSLShaderSource; if (RunInfo.bUseNew) { if (RunInfo.bList) { if (!FFileHelper::LoadFileToString(HLSLShaderSource, *RunInfo.InputFile)) { UE_LOG(LogCrossCompilerTool, Error, TEXT("Couldn't load Input file '%s'!"), *RunInfo.InputFile); return 1; } TArray<FString> List; if (!FFileHelper::LoadANSITextFileToStrings(*RunInfo.InputFile, &IFileManager::Get(), List)) { return 1; } int32 Count = 0; for (auto& File : List) { FString HLSLShader; if (!FFileHelper::LoadFileToString(HLSLShader, *File)) { UE_LOG(LogCrossCompilerTool, Error, TEXT("Couldn't load Input file '%s'!"), *File); continue; } UE_LOG(LogCrossCompilerTool, Log, TEXT("%d: %s"), Count++, *File); if (!CrossCompiler::Parser::Parse(HLSLShader, File, false)) { UE_LOG(LogCrossCompilerTool, Log, TEXT("Error compiling '%s'!"), *File); return 1; } } } else { if (RunInfo.bRunCPP) { //GMalloc->DumpAllocatorStats(*FGenericPlatformOutputDevices::GetLog()); if (!Preprocess(RunInfo.InputFile, HLSLShaderSource)) { UE_LOG(LogCrossCompilerTool, Log, TEXT("Error during preprocessor on '%s'!"), *RunInfo.InputFile); return 1; } //GMalloc->DumpAllocatorStats(*FGenericPlatformOutputDevices::GetLog()); } else { if (!FFileHelper::LoadFileToString(HLSLShaderSource, *RunInfo.InputFile)) { UE_LOG(LogCrossCompilerTool, Error, TEXT("Couldn't load Input file '%s'!"), *RunInfo.InputFile); return 1; } } if (RunInfo.bPreprocessOnly) { return 0; } if (!CrossCompiler::Parser::Parse(HLSLShaderSource, *RunInfo.InputFile, true)) { UE_LOG(LogCrossCompilerTool, Log, TEXT("Error compiling '%s'!"), *RunInfo.InputFile); return 1; } } //Scanner.Dump(); return 0; } else { if (!FFileHelper::LoadFileToString(HLSLShaderSource, *RunInfo.InputFile)) { UE_LOG(LogCrossCompilerTool, Error, TEXT("Couldn't load Input file '%s'!"), *RunInfo.InputFile); return 1; } } ANSICHAR* ShaderSource = 0; ANSICHAR* ErrorLog = 0; int Result = HlslCrossCompile( TCHAR_TO_ANSI(*RunInfo.InputFile), TCHAR_TO_ANSI(*HLSLShaderSource), TCHAR_TO_ANSI(*RunInfo.Entry), RunInfo.Frequency, Backend, Language, Flags, RunInfo.Target, &ShaderSource, &ErrorLog ); if (ErrorLog) { FString OutError(ANSI_TO_TCHAR(ErrorLog)); UE_LOG(LogCrossCompilerTool, Warning, TEXT("%s"), *OutError); } if (ShaderSource) { FString OutSource(ANSI_TO_TCHAR(ShaderSource)); UE_LOG(LogCrossCompilerTool, Display, TEXT("%s"), *OutSource); if (RunInfo.OutputFile.Len() > 0) { FFileHelper::SaveStringToFile(OutSource, *RunInfo.OutputFile); } } free(ShaderSource); free(ErrorLog); return 0; }
static int32 Run(const FRunInfo& RunInfo) { ILanguageSpec* Language = nullptr; FCodeBackend* Backend = nullptr; int32 Flags = 0; Flags |= RunInfo.bRunCPP ? 0 : HLSLCC_NoPreprocess; FGlslLanguageSpec GlslLanguage(RunInfo.Target == HCT_FeatureLevelES2); FGlslCodeBackend GlslBackend(Flags); FMetalLanguageSpec MetalLanguage; FMetalCodeBackend MetalBackend(Flags); switch (RunInfo.BackEnd) { case CCT::FRunInfo::BE_Metal: Language = &MetalLanguage; Backend = &MetalBackend; break; case CCT::FRunInfo::BE_OpenGL: Language = &GlslLanguage; Backend = &GlslBackend; Flags |= HLSLCC_DX11ClipSpace; break; default: return 1; } FString HLSLShaderSource; if (!FFileHelper::LoadFileToString(HLSLShaderSource, *RunInfo.InputFile)) { UE_LOG(LogCrossCompilerTool, Error, TEXT("Couldn't load Input file!")); return 1; } ANSICHAR* ShaderSource = 0; ANSICHAR* ErrorLog = 0; int Result = HlslCrossCompile( TCHAR_TO_ANSI(*RunInfo.InputFile), TCHAR_TO_ANSI(*HLSLShaderSource), TCHAR_TO_ANSI(*RunInfo.Entry), RunInfo.Frequency, Backend, Language, Flags, RunInfo.Target, &ShaderSource, &ErrorLog ); if (ErrorLog) { FString OutError(ANSI_TO_TCHAR(ErrorLog)); UE_LOG(LogCrossCompilerTool, Warning, TEXT("%s"), *OutError); } if (ShaderSource) { FString OutSource(ANSI_TO_TCHAR(ShaderSource)); UE_LOG(LogCrossCompilerTool, Display, TEXT("%s"), *OutSource); if (RunInfo.OutputFile.Len() > 0) { FFileHelper::SaveStringToFile(OutSource, *RunInfo.OutputFile); } } free(ShaderSource); free(ErrorLog); return 0; }
void CompileShader_Metal(const FShaderCompilerInput& Input,FShaderCompilerOutput& Output,const FString& WorkingDirectory) { FString PreprocessedShader; FShaderCompilerDefinitions AdditionalDefines; EHlslCompileTarget HlslCompilerTarget = HCT_FeatureLevelES3_1; AdditionalDefines.SetDefine(TEXT("IOS"), 1); AdditionalDefines.SetDefine(TEXT("row_major"), TEXT("")); static FName NAME_SF_METAL(TEXT("SF_METAL")); static FName NAME_SF_METAL_MRT(TEXT("SF_METAL_MRT")); if (Input.ShaderFormat == NAME_SF_METAL) { AdditionalDefines.SetDefine(TEXT("METAL_PROFILE"), 1); } else if (Input.ShaderFormat == NAME_SF_METAL_MRT) { AdditionalDefines.SetDefine(TEXT("METAL_MRT_PROFILE"), 1); } else { Output.bSucceeded = false; new(Output.Errors) FShaderCompilerError(*FString::Printf(TEXT("Invalid shader format '%s' passed to compiler."), *Input.ShaderFormat.ToString())); return; } const bool bDumpDebugInfo = (Input.DumpDebugInfoPath != TEXT("") && IFileManager::Get().DirectoryExists(*Input.DumpDebugInfoPath)); AdditionalDefines.SetDefine(TEXT("COMPILER_SUPPORTS_ATTRIBUTES"), (uint32)1); if (PreprocessShader(PreprocessedShader, Output, Input, AdditionalDefines)) { char* MetalShaderSource = NULL; char* ErrorLog = NULL; const EHlslShaderFrequency FrequencyTable[] = { HSF_VertexShader, HSF_InvalidFrequency, HSF_InvalidFrequency, HSF_PixelShader, HSF_InvalidFrequency, HSF_ComputeShader }; const EHlslShaderFrequency Frequency = FrequencyTable[Input.Target.Frequency]; if (Frequency == HSF_InvalidFrequency) { Output.bSucceeded = false; FShaderCompilerError* NewError = new(Output.Errors) FShaderCompilerError(); NewError->StrippedErrorMessage = FString::Printf( TEXT("%s shaders not supported for use in Metal."), GLFrequencyStringTable[Input.Target.Frequency] ); return; } // This requires removing the HLSLCC_NoPreprocess flag later on! if (!RemoveUniformBuffersFromSource(PreprocessedShader)) { return; } // Write out the preprocessed file and a batch file to compile it if requested (DumpDebugInfoPath is valid) if (bDumpDebugInfo) { FArchive* FileWriter = IFileManager::Get().CreateFileWriter(*(Input.DumpDebugInfoPath / Input.SourceFilename + TEXT(".usf"))); if (FileWriter) { auto AnsiSourceFile = StringCast<ANSICHAR>(*PreprocessedShader); FileWriter->Serialize((ANSICHAR*)AnsiSourceFile.Get(), AnsiSourceFile.Length()); FileWriter->Close(); delete FileWriter; } } uint32 CCFlags = HLSLCC_NoPreprocess | HLSLCC_PackUniforms; //CCFlags |= HLSLCC_FlattenUniformBuffers | HLSLCC_FlattenUniformBufferStructures; if (bDumpDebugInfo) { const FString MetalFile = (Input.DumpDebugInfoPath / TEXT("Output.metal")); const FString USFFile = (Input.DumpDebugInfoPath / Input.SourceFilename) + TEXT(".usf"); const FString CCBatchFileContents = CreateCommandLineHLSLCC(USFFile, MetalFile, *Input.EntryPointName, Frequency, CCFlags); if (!CCBatchFileContents.IsEmpty()) { FFileHelper::SaveStringToFile(CCBatchFileContents, *(Input.DumpDebugInfoPath / TEXT("CrossCompile.bat"))); } } // Required as we added the RemoveUniformBuffersFromSource() function (the cross-compiler won't be able to interpret comments w/o a preprocessor) CCFlags &= ~HLSLCC_NoPreprocess; FMetalCodeBackend MetalBackEnd(CCFlags); FMetalLanguageSpec MetalLanguageSpec; int32 Result = HlslCrossCompile( TCHAR_TO_ANSI(*Input.SourceFilename), TCHAR_TO_ANSI(*PreprocessedShader), TCHAR_TO_ANSI(*Input.EntryPointName), Frequency, &MetalBackEnd, &MetalLanguageSpec, CCFlags, HlslCompilerTarget, &MetalShaderSource, &ErrorLog ); int32 SourceLen = MetalShaderSource ? FCStringAnsi::Strlen(MetalShaderSource) : 0; if (bDumpDebugInfo) { if (SourceLen > 0) { FArchive* FileWriter = IFileManager::Get().CreateFileWriter(*(Input.DumpDebugInfoPath / Input.SourceFilename + TEXT(".metal"))); if (FileWriter) { FileWriter->Serialize(MetalShaderSource, SourceLen + 1); FileWriter->Close(); delete FileWriter; } } } if (Result != 0) { Output.Target = Input.Target; BuildMetalShaderOutput(Output, Input, MetalShaderSource, SourceLen, Output.Errors); } else { FString Tmp = ANSI_TO_TCHAR(ErrorLog); TArray<FString> ErrorLines; Tmp.ParseIntoArray(ErrorLines, TEXT("\n"), true); for (int32 LineIndex = 0; LineIndex < ErrorLines.Num(); ++LineIndex) { const FString& Line = ErrorLines[LineIndex]; ParseHlslccError(Output.Errors, Line); } } if (MetalShaderSource) { free(MetalShaderSource); } if (ErrorLog) { free(ErrorLog); } } }