Browse Source

metal: error when generated SPIR-V shader IR can't be parsed

Sasha Szpakowski 1 year ago
parent
commit
bf6a6f1237
1 changed files with 205 additions and 200 deletions
  1. 205 200
      src/modules/graphics/metal/Shader.mm

+ 205 - 200
src/modules/graphics/metal/Shader.mm

@@ -30,6 +30,7 @@
 #include "libraries/spirv_cross/spirv_reflect.hpp"
 
 #include <algorithm>
+#include <memory>
 
 namespace love
 {
@@ -456,278 +457,282 @@ void Shader::compileFromGLSLang(id<MTLDevice> device, const glslang::TProgram &p
 		std::string msgs = logger.getAllMessages();
 //		printf("spirv length: %ld, messages:\n%s\n", spirv.size(), msgs.c_str());
 
+//		printf("GLSL INPUT SOURCE:\n\n%s\n\n", pixel->getSource().c_str());
+
+		std::unique_ptr<CompilerMSL> mslpointer;
+
 		try
 		{
-//			printf("GLSL INPUT SOURCE:\n\n%s\n\n", pixel->getSource().c_str());
+			mslpointer.reset(new CompilerMSL(std::move(spirv)));
+		}
+		catch (std::exception &e)
+		{
+			throw love::Exception("Error parsing SPIR-V shader source: %s", e.what());
+		}
 
-			CompilerMSL msl(std::move(spirv));
+		auto &msl = *mslpointer;
 
-			auto interfacevars = msl.get_active_interface_variables();
+		auto interfacevars = msl.get_active_interface_variables();
 
-			msl.set_enabled_interface_variables(interfacevars);
+		msl.set_enabled_interface_variables(interfacevars);
 
-			ShaderResources resources = msl.get_shader_resources();
+		ShaderResources resources = msl.get_shader_resources();
 
-			for (const auto &resource : resources.storage_images)
-			{
-				addImage(msl, resource, UNIFORM_STORAGETEXTURE);
-			}
+		for (const auto &resource : resources.storage_images)
+		{
+			addImage(msl, resource, UNIFORM_STORAGETEXTURE);
+		}
 
-			for (const auto &resource : resources.sampled_images)
-			{
-				addImage(msl, resource, UNIFORM_SAMPLER);
-			}
+		for (const auto &resource : resources.sampled_images)
+		{
+			addImage(msl, resource, UNIFORM_SAMPLER);
+		}
 
-			for (const auto &resource : resources.uniform_buffers)
-			{
-				MSLResourceBinding binding;
-				binding.stage = msl.get_execution_model();
-				binding.binding = msl.get_decoration(resource.id, spv::DecorationBinding);
-				binding.desc_set = msl.get_decoration(resource.id, spv::DecorationDescriptorSet);
+		for (const auto &resource : resources.uniform_buffers)
+		{
+			MSLResourceBinding binding;
+			binding.stage = msl.get_execution_model();
+			binding.binding = msl.get_decoration(resource.id, spv::DecorationBinding);
+			binding.desc_set = msl.get_decoration(resource.id, spv::DecorationDescriptorSet);
 
-				if (resource.name == "gl_DefaultUniformBlock")
-				{
-					binding.msl_buffer = getUniformBufferBinding();
-					msl.add_msl_resource_binding(binding);
+			if (resource.name == "gl_DefaultUniformBlock")
+			{
+				binding.msl_buffer = getUniformBufferBinding();
+				msl.add_msl_resource_binding(binding);
 
-					const SPIRType &type = msl.get_type(resource.base_type_id);
-					size_t size = msl.get_declared_struct_size(type);
+				const SPIRType &type = msl.get_type(resource.base_type_id);
+				size_t size = msl.get_declared_struct_size(type);
 
-					if (localUniformBufferSize != 0)
-					{
-						if (localUniformBufferSize != size)
-							throw love::Exception("Local uniform buffer size mismatch");
-						continue;
-					}
+				if (localUniformBufferSize != 0)
+				{
+					if (localUniformBufferSize != size)
+						throw love::Exception("Local uniform buffer size mismatch");
+					continue;
+				}
 
-					localUniformStagingData = new uint8[size];
-					localUniformBufferData = new uint8[size];
-					localUniformBufferSize = size;
+				localUniformStagingData = new uint8[size];
+				localUniformBufferData = new uint8[size];
+				localUniformBufferSize = size;
 
-					memset(localUniformStagingData, 0, size);
-					memset(localUniformBufferData, 0, size);
+				memset(localUniformStagingData, 0, size);
+				memset(localUniformBufferData, 0, size);
 
-					std::string basename("");
-					buildLocalUniforms(msl, type, 0, basename);
-				}
-				else
-				{
-					binding.msl_buffer = metalBufferIndices[stageindex]++;
-					msl.add_msl_resource_binding(binding);
-				}
+				std::string basename("");
+				buildLocalUniforms(msl, type, 0, basename);
 			}
-
-			for (const auto &resource : resources.storage_buffers)
+			else
 			{
-				MSLResourceBinding binding;
-				binding.stage = msl.get_execution_model();
-				binding.binding = msl.get_decoration(resource.id, spv::DecorationBinding);
-				binding.desc_set = msl.get_decoration(resource.id, spv::DecorationDescriptorSet);
 				binding.msl_buffer = metalBufferIndices[stageindex]++;
 				msl.add_msl_resource_binding(binding);
+			}
+		}
 
-				auto it = uniforms.find(resource.name);
-				if (it != uniforms.end())
-					continue;
+		for (const auto &resource : resources.storage_buffers)
+		{
+			MSLResourceBinding binding;
+			binding.stage = msl.get_execution_model();
+			binding.binding = msl.get_decoration(resource.id, spv::DecorationBinding);
+			binding.desc_set = msl.get_decoration(resource.id, spv::DecorationDescriptorSet);
+			binding.msl_buffer = metalBufferIndices[stageindex]++;
+			msl.add_msl_resource_binding(binding);
+
+			auto it = uniforms.find(resource.name);
+			if (it != uniforms.end())
+				continue;
 
-				const SPIRType &type = msl.get_type(resource.type_id);
+			const SPIRType &type = msl.get_type(resource.type_id);
 
-				UniformInfo u = {};
-				u.baseType = UNIFORM_STORAGEBUFFER;
-				u.components = 1;
-				u.name = resource.name;
-				u.count = type.array.empty() ? 1 : type.array[0];
+			UniformInfo u = {};
+			u.baseType = UNIFORM_STORAGEBUFFER;
+			u.components = 1;
+			u.name = resource.name;
+			u.count = type.array.empty() ? 1 : type.array[0];
 
-				if (!fillUniformReflectionData(u))
-					continue;
+			if (!fillUniformReflectionData(u))
+				continue;
 
-				u.buffers = new love::graphics::Buffer*[u.count];
-				u.dataSize = sizeof(int) * u.count;
-				u.data = malloc(u.dataSize);
+			u.buffers = new love::graphics::Buffer*[u.count];
+			u.dataSize = sizeof(int) * u.count;
+			u.data = malloc(u.dataSize);
 
-				for (int i = 0; i < u.count; i++)
-				{
-					u.ints[i] = -1; // Initialized below, after compiling.
-					u.buffers[i] = nullptr;
-				}
-
-				uniforms[u.name] = u;
+			for (int i = 0; i < u.count; i++)
+			{
+				u.ints[i] = -1; // Initialized below, after compiling.
+				u.buffers[i] = nullptr;
 			}
 
-			if (stageindex == SHADERSTAGE_VERTEX)
+			uniforms[u.name] = u;
+		}
+
+		if (stageindex == SHADERSTAGE_VERTEX)
+		{
+			int nextattributeindex = ATTRIB_MAX_ENUM;
+
+			for (const auto &var : interfacevars)
 			{
-				int nextattributeindex = ATTRIB_MAX_ENUM;
+				spv::StorageClass storage = msl.get_storage_class(var);
+				const std::string &name = msl.get_name(var);
 
-				for (const auto &var : interfacevars)
+				if (storage == spv::StorageClassInput)
 				{
-					spv::StorageClass storage = msl.get_storage_class(var);
-					const std::string &name = msl.get_name(var);
-
-					if (storage == spv::StorageClassInput)
-					{
-						int index = 0;
+					int index = 0;
 
-						BuiltinVertexAttribute builtinattribute;
-						if (graphics::getConstant(name.c_str(), builtinattribute))
-							index = (int) builtinattribute;
-						else
-							index = nextattributeindex++;
+					BuiltinVertexAttribute builtinattribute;
+					if (graphics::getConstant(name.c_str(), builtinattribute))
+						index = (int) builtinattribute;
+					else
+						index = nextattributeindex++;
 
-						msl.set_decoration(var, spv::DecorationLocation, index);
-						attributes[name] = msl.get_decoration(var, spv::DecorationLocation);
-					}
+					msl.set_decoration(var, spv::DecorationLocation, index);
+					attributes[name] = msl.get_decoration(var, spv::DecorationLocation);
 				}
+			}
 
-				for (const auto &varying : resources.stage_outputs)
-				{
-//					printf("vertex shader output %s: %d\n", inp.name.c_str(), msl.get_decoration(inp.id, spv::DecorationLocation));
-					varyings[varying.name] = nextVaryingLocation;
-					msl.set_decoration(varying.id, spv::DecorationLocation, nextVaryingLocation++);
-				}
+			for (const auto &varying : resources.stage_outputs)
+			{
+//				printf("vertex shader output %s: %d\n", inp.name.c_str(), msl.get_decoration(inp.id, spv::DecorationLocation));
+				varyings[varying.name] = nextVaryingLocation;
+				msl.set_decoration(varying.id, spv::DecorationLocation, nextVaryingLocation++);
 			}
-			else if (stageindex == SHADERSTAGE_PIXEL)
+		}
+		else if (stageindex == SHADERSTAGE_PIXEL)
+		{
+			for (const auto &varying : resources.stage_inputs)
 			{
-				for (const auto &varying : resources.stage_inputs)
-				{
-					const auto it = varyings.find(varying.name);
-					if (it != varyings.end())
-						msl.set_decoration(varying.id, spv::DecorationLocation, it->second);
-				}
+				const auto it = varyings.find(varying.name);
+				if (it != varyings.end())
+					msl.set_decoration(varying.id, spv::DecorationLocation, it->second);
 			}
+		}
 
-			CompilerMSL::Options options;
-			options.set_msl_version(2, 1);
-			options.texture_buffer_native = true;
+		CompilerMSL::Options options;
+		options.set_msl_version(2, 1);
+		options.texture_buffer_native = true;
 #ifdef LOVE_IOS
-			options.platform = CompilerMSL::Options::iOS;
+		options.platform = CompilerMSL::Options::iOS;
 #else
-			options.platform = CompilerMSL::Options::macOS;
+		options.platform = CompilerMSL::Options::macOS;
 #endif
 
-			msl.set_msl_options(options);
+		msl.set_msl_options(options);
 
-			std::string source = msl.compile();
-//			printf("// MSL SOURCE for stage %d:\n\n%s\n\n", stageindex, source.c_str());
+		std::string source = msl.compile();
+//		printf("// MSL SOURCE for stage %d:\n\n%s\n\n", stageindex, source.c_str());
 
-			NSString *nssource = [[NSString alloc] initWithBytes:source.c_str()
-														  length:source.length()
-														encoding:NSUTF8StringEncoding];
+		NSString *nssource = [[NSString alloc] initWithBytes:source.c_str()
+													  length:source.length()
+													encoding:NSUTF8StringEncoding];
 
-			MTLCompileOptions *opts = [MTLCompileOptions new];
+		MTLCompileOptions *opts = [MTLCompileOptions new];
 
-			// Silences warning. We already only use metal on these OS versions.
-			if (@available(macOS 10.14, iOS 12.0, *))
-				opts.languageVersion = MTLLanguageVersion2_1;
+		// Silences warning. We already only use metal on these OS versions.
+		if (@available(macOS 10.14, iOS 12.0, *))
+			opts.languageVersion = MTLLanguageVersion2_1;
 
-			NSError *err = nil;
-			id<MTLLibrary> library = [device newLibraryWithSource:nssource options:opts error:&err];
-			if (library == nil && err != nil)
-			{
-				NSLog(@"errors: %@", err);
-				throw love::Exception("Error compiling converted Metal shader code");
-			}
+		NSError *err = nil;
+		id<MTLLibrary> library = [device newLibraryWithSource:nssource options:opts error:&err];
+		if (library == nil && err != nil)
+		{
+			NSLog(@"errors: %@", err);
+			throw love::Exception("Error compiling converted Metal shader code");
+		}
 
-			functions[stageindex] = [library newFunctionWithName:library.functionNames[0]];
+		functions[stageindex] = [library newFunctionWithName:library.functionNames[0]];
 
-			std::string debugname = getShaderStageDebugName((ShaderStageType)stageindex);
-			if (!debugname.empty())
-				functions[stageindex].label = @(debugname.c_str());
+		std::string debugname = getShaderStageDebugName((ShaderStageType)stageindex);
+		if (!debugname.empty())
+			functions[stageindex].label = @(debugname.c_str());
 
-			auto setTextureBinding = [this](CompilerMSL &msl, int stageindex, const spirv_cross::Resource &resource) -> void
-			{
-				auto it = uniforms.find(resource.name);
-				if (it == uniforms.end())
-					return;
+		auto setTextureBinding = [this](CompilerMSL &msl, int stageindex, const spirv_cross::Resource &resource) -> void
+		{
+			auto it = uniforms.find(resource.name);
+			if (it == uniforms.end())
+				return;
 
-				UniformInfo &u = it->second;
+			UniformInfo &u = it->second;
 
-				uint32 texturebinding = msl.get_automatic_msl_resource_binding(resource.id);
-				uint32 samplerbinding = msl.get_automatic_msl_resource_binding_secondary(resource.id);
+			uint32 texturebinding = msl.get_automatic_msl_resource_binding(resource.id);
+			uint32 samplerbinding = msl.get_automatic_msl_resource_binding_secondary(resource.id);
 
-				if (texturebinding == (uint32)-1)
-				{
-					// No valid binding, the uniform was likely optimized out because it's not used.
-					uniforms.erase(resource.name);
-					return;
-				}
+			if (texturebinding == (uint32)-1)
+			{
+				// No valid binding, the uniform was likely optimized out because it's not used.
+				uniforms.erase(resource.name);
+				return;
+			}
 
-				for (int i = 0; i < u.count; i++)
+			for (int i = 0; i < u.count; i++)
+			{
+				if (u.ints[i] == -1)
 				{
-					if (u.ints[i] == -1)
+					u.ints[i] = (int)textureBindings.size();
+					TextureBinding b = {};
+					b.access = u.access;
+
+					if (u.baseType == UNIFORM_SAMPLER)
 					{
-						u.ints[i] = (int)textureBindings.size();
-						TextureBinding b = {};
-						b.access = u.access;
-
-						if (u.baseType == UNIFORM_SAMPLER)
-						{
-							BuiltinUniform builtin = BUILTIN_MAX_ENUM;
-							if (getConstant(u.name.c_str(), builtin) && builtin == BUILTIN_TEXTURE_MAIN)
-								b.isMainTexture = true;
-						}
-
-						for (uint8 &stagebinding : b.textureStages)
-							stagebinding = LOVE_UINT8_MAX;
-						for (uint8 &stagebinding : b.samplerStages)
-							stagebinding = LOVE_UINT8_MAX;
-
-						textureBindings.push_back(b);
+						BuiltinUniform builtin = BUILTIN_MAX_ENUM;
+						if (getConstant(u.name.c_str(), builtin) && builtin == BUILTIN_TEXTURE_MAIN)
+							b.isMainTexture = true;
 					}
 
-					auto &b = textureBindings[u.ints[i]];
-					b.textureStages[stageindex] = (uint8) texturebinding;
-					b.samplerStages[stageindex] = (uint8) samplerbinding;
+					for (uint8 &stagebinding : b.textureStages)
+						stagebinding = LOVE_UINT8_MAX;
+					for (uint8 &stagebinding : b.samplerStages)
+						stagebinding = LOVE_UINT8_MAX;
+
+					textureBindings.push_back(b);
 				}
-			};
 
-			for (const auto &resource : resources.sampled_images)
-			{
-				setTextureBinding(msl, stageindex, resource);
+				auto &b = textureBindings[u.ints[i]];
+				b.textureStages[stageindex] = (uint8) texturebinding;
+				b.samplerStages[stageindex] = (uint8) samplerbinding;
 			}
+		};
 
-			for (const auto &resource : resources.storage_images)
-			{
-				setTextureBinding(msl, stageindex, resource);
-			}
+		for (const auto &resource : resources.sampled_images)
+		{
+			setTextureBinding(msl, stageindex, resource);
+		}
 
-			for (const auto &resource : resources.storage_buffers)
-			{
-				auto it = uniforms.find(resource.name);
-				if (it == uniforms.end())
-					continue;
+		for (const auto &resource : resources.storage_images)
+		{
+			setTextureBinding(msl, stageindex, resource);
+		}
 
-				UniformInfo &u = it->second;
+		for (const auto &resource : resources.storage_buffers)
+		{
+			auto it = uniforms.find(resource.name);
+			if (it == uniforms.end())
+				continue;
 
-				uint32 bufferbinding = msl.get_automatic_msl_resource_binding(resource.id);
-				if (bufferbinding == (uint32)-1)
-				{
-					// No valid binding, the uniform was likely optimized out because it's not used.
-					uniforms.erase(resource.name);
-					continue;
-				}
+			UniformInfo &u = it->second;
 
-				for (int i = 0; i < u.count; i++)
-				{
-					if (u.ints[i] == -1)
-					{
-						u.ints[i] = (int)bufferBindings.size();
-						BufferBinding b = {};
-						b.access = u.access;
+			uint32 bufferbinding = msl.get_automatic_msl_resource_binding(resource.id);
+			if (bufferbinding == (uint32)-1)
+			{
+				// No valid binding, the uniform was likely optimized out because it's not used.
+				uniforms.erase(resource.name);
+				continue;
+			}
 
-						for (uint8 &stagebinding : b.stages)
-							stagebinding = LOVE_UINT8_MAX;
+			for (int i = 0; i < u.count; i++)
+			{
+				if (u.ints[i] == -1)
+				{
+					u.ints[i] = (int)bufferBindings.size();
+					BufferBinding b = {};
+					b.access = u.access;
 
-						bufferBindings.push_back(b);
-					}
+					for (uint8 &stagebinding : b.stages)
+						stagebinding = LOVE_UINT8_MAX;
 
-					bufferBindings[u.ints[i]].stages[stageindex] = (uint8) bufferbinding;
+					bufferBindings.push_back(b);
 				}
+
+				bufferBindings[u.ints[i]].stages[stageindex] = (uint8) bufferbinding;
 			}
 		}
-		catch (std::exception &e)
-		{
-			printf("Error parsing SPIR-V shader source: %s\n", e.what());
-		}
 	}
 
 	// Initialize default resource bindings.