2
0
Эх сурвалжийг харах

Added automatic generation of GLSL shader code via XShaderCompiler

BearishSun 8 жил өмнө
parent
commit
d7dd1f5c2a

+ 1 - 1
Source/BansheeSL/CMakeLists.txt

@@ -66,7 +66,7 @@ endif()
 target_link_libraries(BansheeSL PRIVATE ${XShaderCompiler_LIBRARIES})
 
 ## Local libs
-target_link_libraries(BansheeSL BansheeEngine BansheeUtility BansheeCore)
+target_link_libraries(BansheeSL PRIVATE BansheeEngine BansheeUtility BansheeCore)
 
 # IDE specific
 set_property(TARGET BansheeSL PROPERTY FOLDER Plugins)

+ 2 - 1
Source/BansheeSL/Include/BsSLFXCompiler.h

@@ -80,7 +80,8 @@ namespace bs
 
 	public:
 		/**	Transforms a source file written in BSL FX syntax into a Shader object. */
-		static BSLFXCompileResult compile(const String& source, const UnorderedMap<String, String>& defines);
+		static BSLFXCompileResult compile(const String& name, const String& source, 
+			const UnorderedMap<String, String>& defines);
 
 	private:
 		/** Converts the provided source into an abstract syntax tree using the lexer & parser for BSL FX syntax. */

+ 142 - 2
Source/BansheeSL/Source/BsSLFXCompiler.cpp

@@ -14,6 +14,12 @@
 #include "BsMatrix4.h"
 #include "BsBuiltinResources.h"
 
+#include "Xsc/Xsc.h"
+
+//DEBUG ONLY
+#include "BsFileSystem.h"
+#include "BsDataStream.h"
+
 extern "C" {
 #include "BsMMAlloc.h"
 #include "BsParserFX.h"
@@ -68,7 +74,70 @@ namespace bs
 		}
 	}
 
-	BSLFXCompileResult BSLFXCompiler::compile(const String& source, const UnorderedMap<String, String>& defines)
+	// Convert HLSL code to GLSL
+	String HLSLtoGLSL(const String& hlsl, GpuProgramType type, UINT32& startBindingSlot)
+	{
+		SPtr<StringStream> input = bs_shared_ptr_new<StringStream>();
+		*input << hlsl;
+
+		Xsc::ShaderInput inputDesc;
+		inputDesc.entryPoint = "main";
+		inputDesc.shaderVersion = Xsc::InputShaderVersion::HLSL5;
+		inputDesc.sourceCode = input;
+
+		switch (type)
+		{
+		case GPT_VERTEX_PROGRAM:
+			inputDesc.shaderTarget = Xsc::ShaderTarget::VertexShader;
+			break;
+		case GPT_GEOMETRY_PROGRAM:
+			inputDesc.shaderTarget = Xsc::ShaderTarget::GeometryShader;
+			break;
+		case GPT_HULL_PROGRAM:
+			inputDesc.shaderTarget = Xsc::ShaderTarget::TessellationControlShader;
+			break;
+		case GPT_DOMAIN_PROGRAM:
+			inputDesc.shaderTarget = Xsc::ShaderTarget::TessellationEvaluationShader;
+			break;
+		case GPT_FRAGMENT_PROGRAM:
+			inputDesc.shaderTarget = Xsc::ShaderTarget::FragmentShader;
+			break;
+		}
+
+		StringStream output;
+
+		Xsc::ShaderOutput outputDesc;
+		outputDesc.shaderVersion = Xsc::OutputShaderVersion::VKSL450;
+		outputDesc.sourceCode = &output;
+		outputDesc.options.autoBinding = true;
+		outputDesc.options.autoBindingStartSlot = startBindingSlot;
+		outputDesc.nameMangling.inputPrefix = "bs_";
+		outputDesc.nameMangling.useAlwaysSemantics = true;
+
+		Xsc::StdLog log;
+		Xsc::Reflection::ReflectionData reflectionData;
+		if (!Xsc::CompileShader(inputDesc, outputDesc, &log, &reflectionData))
+		{
+			LOGERR("Shader cross compilation failed.");
+			return "";
+		}
+
+		INT32 maxBindingSlot = 0;
+		for (auto& entry : reflectionData.constantBuffers)
+			maxBindingSlot = std::max(maxBindingSlot, entry.location);
+
+		for (auto& entry : reflectionData.textures)
+			maxBindingSlot = std::max(maxBindingSlot, entry.location);
+
+		for (auto& entry : reflectionData.storageBuffers)
+			maxBindingSlot = std::max(maxBindingSlot, entry.location);
+
+		startBindingSlot = maxBindingSlot;
+		return output.str();
+	}
+
+	BSLFXCompileResult BSLFXCompiler::compile(const String& name, const String& source, 
+		const UnorderedMap<String, String>& defines)
 	{
 		BSLFXCompileResult output;
 
@@ -115,7 +184,7 @@ namespace bs
 				codeString = codeString->next;
 			}
 
-			output = parseShader("Shader", parseState, codeBlocks);
+			output = parseShader(name, parseState, codeBlocks);
 
 			StringStream gpuProgError;
 			bool hasError = false;
@@ -1338,6 +1407,7 @@ namespace bs
 
 
 		// Actually parse techniques
+		bool hasGLSLTechnique = false;
 		for (auto& entry : techniqueData)
 		{
 			const TechniqueMetaData& metaData = entry.second.metaData;
@@ -1352,10 +1422,80 @@ namespace bs
 			}
 
 			parseTechnique(entry.first, codeBlocks, entry.second);
+
+			if (entry.second.metaData.language == "glsl")
+				hasGLSLTechnique = true;
 		}
 
 		bs_stack_free(techniqueWasParsed);
 
+		// If no GLSL technique, auto-generate them for each non-base technique
+		if(!hasGLSLTechnique)
+		{
+			UINT32 end = (UINT32)techniqueData.size();
+			for(UINT32 i = 0; i < end; i++)
+			{
+				const TechniqueMetaData& metaData = techniqueData[i].second.metaData;
+				if (!metaData.baseName.empty())
+					continue;
+
+				TechniqueData copy = techniqueData[i].second;
+				copy.metaData.language = "glsl";
+				for(auto& passData : copy.passes)
+				{
+					UINT32 nextFreeBindingSlot = 0;
+					if (!passData.vertexCode.empty())
+					{
+						String hlslCode = passData.commonCode + passData.vertexCode;
+						passData.vertexCode = HLSLtoGLSL(hlslCode, GPT_VERTEX_PROGRAM, nextFreeBindingSlot);
+
+						auto ds = FileSystem::createAndOpenFile("E:\\GenShaders\\" + name + "_VS.glsl");
+						ds->writeString(passData.vertexCode);
+					}
+
+					if (!passData.fragmentCode.empty())
+					{
+						String hlslCode = passData.commonCode + passData.fragmentCode;
+						passData.fragmentCode = HLSLtoGLSL(hlslCode, GPT_FRAGMENT_PROGRAM, nextFreeBindingSlot);
+
+						auto ds = FileSystem::createAndOpenFile("E:\\GenShaders\\" + name + "_FS.glsl");
+						ds->writeString(passData.fragmentCode);
+					}
+
+					if (!passData.geometryCode.empty())
+					{
+						String hlslCode = passData.commonCode + passData.geometryCode;
+						passData.geometryCode = HLSLtoGLSL(hlslCode, GPT_GEOMETRY_PROGRAM, nextFreeBindingSlot);
+					}
+
+					if (!passData.hullCode.empty())
+					{
+						String hlslCode = passData.commonCode + passData.hullCode;
+						passData.hullCode = HLSLtoGLSL(hlslCode, GPT_HULL_PROGRAM, nextFreeBindingSlot);
+					}
+
+					if (!passData.domainCode.empty())
+					{
+						String hlslCode = passData.commonCode + passData.domainCode;
+						passData.domainCode = HLSLtoGLSL(hlslCode, GPT_DOMAIN_PROGRAM, nextFreeBindingSlot);
+					}
+
+					if (!passData.computeCode.empty())
+					{
+						String hlslCode = passData.commonCode + passData.computeCode;
+						passData.computeCode = HLSLtoGLSL(hlslCode, GPT_COMPUTE_PROGRAM, nextFreeBindingSlot);
+
+						auto ds = FileSystem::createAndOpenFile("E:\\GenShaders\\" + name + "_CS.glsl");
+						ds->writeString(passData.computeCode);
+					}
+
+					passData.commonCode = "";
+				}
+
+				techniqueData.push_back(std::make_pair(techniqueData[i].first, copy));
+			}
+		}
+
 		Vector<SPtr<Technique>> techniques;
 		for(auto& entry : techniqueData)
 		{

+ 3 - 2
Source/BansheeSL/Source/BsSLImporter.cpp

@@ -38,10 +38,11 @@ namespace bs
 		String source = stream->getAsString();
 
 		SPtr<const ShaderImportOptions> io = std::static_pointer_cast<const ShaderImportOptions>(importOptions);
-		BSLFXCompileResult result = BSLFXCompiler::compile(source, io->getDefines());
+		WString shaderName = filePath.getWFilename(false);
+		BSLFXCompileResult result = BSLFXCompiler::compile(toString(shaderName), source, io->getDefines());
 
 		if (result.shader != nullptr)
-			result.shader->setName(filePath.getWFilename(false));
+			result.shader->setName(shaderName);
 		else
 		{
 			String file;

+ 5 - 2
Source/CMake/Modules/FindXShaderCompiler.cmake

@@ -49,7 +49,7 @@ else()
 	message(STATUS "...XShaderCompiler OK.")
 endif()
 
-if(XShaderCompiler)
+if(XShaderCompiler_FOUND)
 	add_library(XShaderCompiler STATIC IMPORTED)
 	set_target_properties(XShaderCompiler PROPERTIES IMPORTED_LOCATION_DEBUG "${XShaderCompiler_LIBRARY_DEBUG}")
 	set_target_properties(XShaderCompiler PROPERTIES IMPORTED_LOCATION_OPTIMIZEDDEBUG "${XShaderCompiler_LIBRARY_DEBUG}")
@@ -63,4 +63,7 @@ mark_as_advanced(
 	XShaderCompiler_LIBRARY_DEBUG)
 
 set(XShaderCompiler_INCLUDE_DIRS ${XShaderCompiler_INCLUDE_DIR})
-set(XShaderCompiler_LIBRARIES XShaderCompiler)
+set(XShaderCompiler_LIBRARIES XShaderCompiler)
+
+set(XShaderCompiler_INCLUDE_DIRS ${XShaderCompiler_INCLUDE_DIR} PARENT_SCOPE)
+set(XShaderCompiler_LIBRARIES XShaderCompiler PARENT_SCOPE)