Example #1
0
/**
 * 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;
}
Example #2
0
	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;
	}
Example #4
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);
		}
	}
}