Browse Source

metal: work towards shader uniform implementation.

Misc cleanup.
Alex Szpakowski 4 years ago
parent
commit
4b9c648c8b

+ 1 - 1
src/modules/graphics/Graphics.h

@@ -472,7 +472,7 @@ public:
 	 * @param width The viewport width.
 	 * @param height The viewport height.
 	 **/
-	virtual bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil) = 0;
+	virtual bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth) = 0;
 
 	/**
 	 * Un-sets the current graphics display mode (uninitializing objects if

+ 1 - 0
src/modules/graphics/metal/Buffer.mm

@@ -95,6 +95,7 @@ Buffer::Buffer(love::graphics::Graphics *gfx, id<MTLDevice> device, const Settin
 Buffer::~Buffer()
 { @autoreleasepool {
 	buffer = nil;
+	texture = nil;
 }}
 
 void *Buffer::map()

+ 3 - 1
src/modules/graphics/metal/Graphics.h

@@ -49,7 +49,7 @@ public:
 	love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
 
 	void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
-	bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil) override;
+	bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth) override;
 	void unSetMode() override;
 
 	void setActive(bool active) override;
@@ -195,6 +195,8 @@ private:
 	std::map<uint64, void *> cachedSamplers;
 	std::unordered_map<uint64, void *> cachedDepthStencilStates;
 
+	std::atomic<int64> completeCommandBufferIndex;
+
 }; // Graphics
 
 } // metal

+ 130 - 119
src/modules/graphics/metal/Graphics.mm

@@ -112,6 +112,21 @@ static MTLStencilOperation getMTLStencilOperation(StencilAction action)
 	return MTLStencilOperationKeep;
 }
 
+static inline id<MTLTexture> getMTLTexture(love::graphics::Texture *tex)
+{
+	return tex ? (__bridge id<MTLTexture>)(void *) tex->getHandle() : nil;
+}
+
+static inline id<MTLTexture> getMTLRenderTarget(love::graphics::Texture *tex)
+{
+	return tex ? (__bridge id<MTLTexture>)(void *) tex->getRenderTargetHandle() : nil;
+}
+
+static inline id<MTLBuffer> getMTLBuffer(love::graphics::Resource *res)
+{
+	return res ? (__bridge id<MTLBuffer>)(void *) res->getHandle() : nil;
+}
+
 love::graphics::Graphics *createInstance()
 {
 	love::graphics::Graphics *instance = nullptr;
@@ -190,7 +205,7 @@ Graphics::Graphics()
 			window->windowToDPICoords(&dpiW, &dpiH);
 
 			void *context = nullptr; // TODO
-			setMode(context, (int) dpiW, (int) dpiH, window->getPixelWidth(), window->getPixelHeight(), settings.stencil);
+			setMode(context, (int) dpiW, (int) dpiH, window->getPixelWidth(), window->getPixelHeight(), settings.stencil, settings.depth);
 		}
 	}
 }}
@@ -257,18 +272,18 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
 	}
 }
 
-bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil)
+bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth)
 { @autoreleasepool {
 	this->width = width;
 	this->height = height;
 	this->metalLayer = (__bridge CAMetalLayer *) context;
 
-	this->windowHasStencil = windowhasstencil;
+	this->windowHasStencil = backbufferstencil;
 
 	metalLayer.device = device;
 	metalLayer.pixelFormat = isGammaCorrect() ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
 
-	// TODO: set to NO when we have pending screen captures
+	// This is set to NO when there are pending screen captures.
 	metalLayer.framebufferOnly = YES;
 
 	setViewportSize(width, height, pixelwidth, pixelheight);
@@ -338,8 +353,17 @@ void Graphics::setActive(bool enable)
 id<MTLCommandBuffer> Graphics::useCommandBuffer()
 {
 	if (commandBuffer == nil)
+	{
 		commandBuffer = [commandQueue commandBuffer];
 
+		Graphics *pthis = this;
+		pthis->retain();
+		[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
+			pthis->completeCommandBufferIndex.fetch_add(1, std::memory_order_relaxed);
+			pthis->release();
+		}];
+	}
+
 	return commandBuffer;
 }
 
@@ -361,21 +385,30 @@ id<MTLRenderCommandEncoder> Graphics::useRenderEncoder()
 	{
 		submitBlitEncoder();
 
+		// Pass desc info for non-backbuffer render targets are set up in
+		// setRenderTagetsInternal.
 		const auto &rts = states.back().renderTargets;
-		if (rts.getFirstTarget().texture.get() != nullptr)
-		{
-			// TODO
-		}
-		else
+		if (rts.getFirstTarget().texture.get() == nullptr)
 		{
 			if (activeDrawable == nil)
+			{
+				// This is reset to YES after each frame.
+				// TODO: Does setting this reallocate memory?
+				if (!pendingScreenshotCallbacks.empty())
+					metalLayer.framebufferOnly = NO;
+
 				activeDrawable = [metalLayer nextDrawable];
+			}
+
 			passDesc.colorAttachments[0].texture = activeDrawable.texture;
+			passDesc.colorAttachments[0].level = 0;
+			passDesc.colorAttachments[0].slice = 0;
+			passDesc.colorAttachments[0].depthPlane = 0;
 		}
 
 		renderEncoder = [useCommandBuffer() renderCommandEncoderWithDescriptor:passDesc];
 
-		id<MTLBuffer> defaultbuffer = (__bridge id<MTLBuffer>)(void *)defaultAttributesBuffer->getHandle();
+		id<MTLBuffer> defaultbuffer = getMTLBuffer(defaultAttributesBuffer);
 		[renderEncoder setVertexBuffer:defaultbuffer offset:0 atIndex:DEFAULT_VERTEX_BUFFER_BINDING];
 
 		dirtyRenderState = STATEBIT_ALL;
@@ -622,37 +655,33 @@ void Graphics::applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, lo
 	size_t alignment = 16;
 #endif
 
-	// TODO: use the size of all uniforms
-	size_t size = sizeof(Shader::BuiltinUniformData);
+	size_t size = s->getLocalUniformBufferSize();
+	uint8 *bufferdata = s->getLocalUniformBufferData();
 
-	Shader::BuiltinUniformData data;
+	auto builtins = (Shader::BuiltinUniformData *) (bufferdata + s->getBuiltinUniformDataOffset());
 
-	data.transformMatrix = getTransform();
-	data.projectionMatrix = getProjection();
+	builtins->transformMatrix = getTransform();
+	builtins->projectionMatrix = getProjection();
 
 	// The normal matrix is the transpose of the inverse of the rotation portion
 	// (top-left 3x3) of the transform matrix.
 	{
-		Matrix3 normalmatrix = Matrix3(data.transformMatrix).transposedInverse();
+		Matrix3 normalmatrix = Matrix3(builtins->transformMatrix).transposedInverse();
 		const float *e = normalmatrix.getElements();
 		for (int i = 0; i < 3; i++)
 		{
-			data.normalMatrix[i].x = e[i * 3 + 0];
-			data.normalMatrix[i].y = e[i * 3 + 1];
-			data.normalMatrix[i].z = e[i * 3 + 2];
-			data.normalMatrix[i].w = 0.0f;
+			builtins->normalMatrix[i].x = e[i * 3 + 0];
+			builtins->normalMatrix[i].y = e[i * 3 + 1];
+			builtins->normalMatrix[i].z = e[i * 3 + 2];
+			builtins->normalMatrix[i].w = 0.0f;
 		}
 	}
 
 	// FIXME: should be active RT dimensions
-	data.screenSizeParams.x = getPixelWidth();
-	data.screenSizeParams.y = getPixelHeight();
+	builtins->screenSizeParams = Vector4(getPixelWidth(), getPixelHeight(), 1.0f, 0.0f);
 
-	data.screenSizeParams.z = 1.0f;
-	data.screenSizeParams.w = 0.0f;
-
-	data.constantColor = getColor();
-	gammaCorrectColor(data.constantColor);
+	builtins->constantColor = getColor();
+	gammaCorrectColor(builtins->constantColor);
 
 	if (uniformBuffer->getSize() < uniformBufferOffset + size)
 	{
@@ -666,9 +695,9 @@ void Graphics::applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, lo
 	if (uniformBufferData.data == nullptr)
 		uniformBufferData = uniformBuffer->map(uniformBuffer->getSize());
 
-	memcpy(uniformBufferData.data + uniformBufferOffset, &data, sizeof(Shader::BuiltinUniformData));
+	memcpy(uniformBufferData.data + uniformBufferOffset, bufferdata, size);
 
-	id<MTLBuffer> buffer = (__bridge id<MTLBuffer>)(void *)uniformBuffer->getHandle();
+	id<MTLBuffer> buffer = getMTLBuffer(uniformBuffer);
 	int index = Shader::getUniformBufferBinding();
 
 	// TODO: bind shader textures/samplers
@@ -679,6 +708,26 @@ void Graphics::applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, lo
 	uniformBufferOffset += alignUp(size, alignment);
 }
 
+static void setVertexBuffers(id<MTLRenderCommandEncoder> encoder, const BufferBindings *buffers)
+{
+	uint32 allbits = buffers->useBits;
+	uint32 i = 0;
+	while (allbits)
+	{
+		uint32 bit = 1u << i;
+
+		if (buffers->useBits & bit)
+		{
+			auto b = buffers->info[i];
+			id<MTLBuffer> buffer = getMTLBuffer(b.buffer);
+			[encoder setVertexBuffer:buffer offset:b.offset atIndex:i + VERTEX_BUFFER_BINDING_START];
+		}
+
+		i++;
+		allbits >>= 1;
+	}
+}
+
 void Graphics::draw(const DrawCommand &cmd)
 { @autoreleasepool {
 	id<MTLRenderCommandEncoder> encoder = useRenderEncoder();
@@ -690,29 +739,14 @@ void Graphics::draw(const DrawCommand &cmd)
 	if (texture == nullptr)
 		texture = defaultTextures[TEXTURE_2D];
 
-	id<MTLTexture> mtltexture = (__bridge id<MTLTexture>)(void *) texture->getHandle();
+	id<MTLTexture> mtltexture = getMTLTexture(texture);
 
 	[encoder setFragmentTexture:mtltexture atIndex:0];
 	[encoder setFragmentSamplerState:((Texture *)texture)->getMTLSampler() atIndex:0];
 
 	[encoder setCullMode:MTLCullModeNone];
 
-	uint32 allbits = cmd.buffers->useBits;
-	uint32 i = 0;
-	while (allbits)
-	{
-		uint32 bit = 1u << i;
-
-		if (cmd.buffers->useBits & bit)
-		{
-			auto b = cmd.buffers->info[i];
-			id<MTLBuffer> buffer = (__bridge id<MTLBuffer>)(void *)b.buffer->getHandle();
-			[encoder setVertexBuffer:buffer offset:b.offset atIndex:i + VERTEX_BUFFER_BINDING_START];
-		}
-
-		i++;
-		allbits >>= 1;
-	}
+	setVertexBuffers(encoder, cmd.buffers);
 
 	[encoder drawPrimitives:MTLPrimitiveTypeTriangle
 				vertexStart:cmd.vertexStart
@@ -731,36 +765,21 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
 	if (texture == nullptr)
 		texture = defaultTextures[TEXTURE_2D];
 
-	id<MTLTexture> mtltexture = (__bridge id<MTLTexture>)(void *) texture->getHandle();
+	id<MTLTexture> mtltexture = getMTLTexture(texture);
 
 	[encoder setFragmentTexture:mtltexture atIndex:0];
 	[encoder setFragmentSamplerState:((Texture *)texture)->getMTLSampler() atIndex:0];
 
 	[encoder setCullMode:MTLCullModeNone];
 
-	uint32 allbits = cmd.buffers->useBits;
-	uint32 i = 0;
-	while (allbits)
-	{
-		uint32 bit = 1u << i;
-
-		if (cmd.buffers->useBits & bit)
-		{
-			auto b = cmd.buffers->info[i];
-			id<MTLBuffer> buffer = (__bridge id<MTLBuffer>)(void *)b.buffer->getHandle();
-			[encoder setVertexBuffer:buffer offset:b.offset atIndex:i + VERTEX_BUFFER_BINDING_START];
-		}
-
-		i++;
-		allbits >>= 1;
-	}
+	setVertexBuffers(encoder, cmd.buffers);
 
 	auto indexType = cmd.indexType == INDEX_UINT32 ? MTLIndexTypeUInt32 : MTLIndexTypeUInt16;
 
 	[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
 						indexCount:cmd.indexCount
 						 indexType:indexType
-					   indexBuffer:(__bridge id<MTLBuffer>)(void*)cmd.indexBuffer->getHandle()
+					   indexBuffer:getMTLBuffer(cmd.indexBuffer)
 				 indexBufferOffset:cmd.indexBufferOffset
 					 instanceCount:cmd.instanceCount];
 }}
@@ -778,31 +797,16 @@ void Graphics::drawQuads(int start, int count, const VertexAttributes &attribute
 	if (texture == nullptr)
 		texture = defaultTextures[TEXTURE_2D];
 
-	id<MTLTexture> mtltexture = (__bridge id<MTLTexture>)(void *) texture->getHandle();
+	id<MTLTexture> mtltexture = getMTLTexture(texture);
 
 	[encoder setFragmentTexture:mtltexture atIndex:0];
 	[encoder setFragmentSamplerState:((Texture *)texture)->getMTLSampler() atIndex:0];
 
 	[encoder setCullMode:MTLCullModeNone];
 
-	uint32 allbits = buffers.useBits;
-	uint32 i = 0;
-	while (allbits)
-	{
-		uint32 bit = 1u << i;
-
-		if (buffers.useBits & bit)
-		{
-			auto b = buffers.info[i];
-			id<MTLBuffer> buffer = (__bridge id<MTLBuffer>)(void *)b.buffer->getHandle();
-			[encoder setVertexBuffer:buffer offset:b.offset atIndex:i + VERTEX_BUFFER_BINDING_START];
-		}
-
-		i++;
-		allbits >>= 1;
-	}
+	setVertexBuffers(encoder, &buffers);
 
-	id<MTLBuffer> ib = (__bridge id<MTLBuffer>)(void *) quadIndexBuffer->getHandle();
+	id<MTLBuffer> ib = getMTLBuffer(quadIndexBuffer);
 
 	// TODO: support for iOS devices that don't support base vertex.
 
@@ -827,56 +831,60 @@ void Graphics::drawQuads(int start, int count, const VertexAttributes &attribute
 	}
 }}
 
-void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas)
-{ @autoreleasepool {
-	const DisplayState &state = states.back();
-
-	endPass();
-
-	for (size_t i = 0; i < rts.colors.size(); i++)
-	{
-		auto rt = rts.colors[i];
-		auto tex = rt.texture;
-		auto desc = passDesc.colorAttachments[i];
+static inline void setAttachment(const Graphics::RenderTarget &rt, MTLRenderPassAttachmentDescriptor *desc)
+{
+	bool isvolume = rt.texture->getTextureType() == TEXTURE_VOLUME;
 
-		desc.texture = (__bridge id<MTLTexture>)(void*)tex->getRenderTargetHandle();
-		desc.level = rt.mipmap;
+	desc.texture = getMTLRenderTarget(rt.texture);
+	desc.level = rt.mipmap;
+	desc.slice = isvolume ? 0 : rt.slice;
+	desc.depthPlane = isvolume ? rt.slice : 0;
 
-		if (tex->getTextureType() == TEXTURE_VOLUME)
-		{
-			desc.slice = 0;
-			desc.depthPlane = rt.slice;
-		}
-		else
-		{
-			desc.slice = rt.slice;
-			desc.depthPlane = 0;
-		}
+	// Default to load until clear or discard is called.
+	desc.loadAction = MTLLoadActionLoad;
+	desc.storeAction = MTLStoreActionStore;
 
-		// Default to load until clear or discard is called.
-		desc.loadAction = MTLLoadActionLoad;
-		desc.storeAction = MTLStoreActionStore;
+	desc.resolveTexture = nil;
 
-		desc.resolveTexture = nil;
+	if (rt.texture->getMSAA() > 1)
+	{
+		// TODO
+		desc.resolveTexture = getMTLTexture(rt.texture);
 
-		if (tex->getMSAA() > 1)
-		{
-			// TODO
-			desc.resolveTexture = (__bridge id<MTLTexture>)(void*)tex->getHandle();
+		// TODO: This StoreAction is only supported sometimes.
+		desc.storeAction = MTLStoreActionStoreAndMultisampleResolve;
+	}
+}
 
-			// TODO: This StoreAction is only supported sometimes.
-			desc.storeAction = MTLStoreActionStoreAndMultisampleResolve;
-		}
+void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int /*pixelw*/, int /*pixelh*/, bool /*hasSRGBtexture*/)
+{ @autoreleasepool {
+	endPass();
 
+	// Set up render pass descriptor for the next useRenderEncoder call.
+	// The backbuffer will be set up in useRenderEncoder rather than here.
+	for (size_t i = 0; i < rts.colors.size(); i++)
+	{
+		auto desc = passDesc.colorAttachments[i];
+		setAttachment(rts.colors[i], desc);
 		passDesc.colorAttachments[i] = desc;
 	}
 
 	for (size_t i = rts.colors.size(); i < MAX_COLOR_RENDER_TARGETS; i++)
 		passDesc.colorAttachments[i] = nil;
 
-	// TODO: depth/stencil attachments
-	// TODO: projection matrix
-	// TODO: backbuffer
+	passDesc.depthAttachment = nil;
+	passDesc.stencilAttachment = nil;
+
+	if (rts.depthStencil.texture)
+	{
+		if (isPixelFormatDepth(rts.depthStencil.texture->getPixelFormat()))
+			setAttachment(rts.depthStencil, passDesc.depthAttachment);
+
+		if (isPixelFormatStencil(rts.depthStencil.texture->getPixelFormat()))
+			setAttachment(rts.depthStencil, passDesc.stencilAttachment);
+	}
+
+	projectionMatrix = Matrix4::ortho(0.0, (float) w, (float) h, 0.0, -10.0f, 10.0f);
 	dirtyRenderState = STATEBIT_ALL;
 }}
 
@@ -1093,6 +1101,9 @@ void Graphics::present(void *screenshotCallbackData)
 	if (window != nullptr)
 		window->swapBuffers();
 
+	// This is set to NO when there are pending screen captures.
+	metalLayer.framebufferOnly = YES;
+
 	activeDrawable = nil;
 
 	// Reset the per-frame stat counts.

+ 6 - 4
src/modules/graphics/metal/Shader.h

@@ -76,10 +76,10 @@ public:
 	int getVertexAttributeIndex(const std::string &name) override;
 	const UniformInfo *getUniformInfo(const std::string &name) const override;
 	const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override;
-	void updateUniform(const UniformInfo *info, int count) override {}
-	void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) override {}
-	void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override {}
-	bool hasUniform(const std::string &name) const override { return false; }
+	void updateUniform(const UniformInfo *info, int count) override;
+	void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) override;
+	void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override;
+	bool hasUniform(const std::string &name) const override;
 	ptrdiff_t getHandle() const override { return 0; }
 	void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override {}
 
@@ -89,6 +89,7 @@ public:
 
 	uint8 *getLocalUniformBufferData() { return localUniformBufferData; }
 	size_t getLocalUniformBufferSize() const { return localUniformBufferSize; }
+	size_t getBuiltinUniformDataOffset() const { return builtinUniformDataOffset; }
 
 private:
 
@@ -107,6 +108,7 @@ private:
 
 	uint8 *localUniformBufferData;
 	size_t localUniformBufferSize;
+	size_t builtinUniformDataOffset;
 
 	std::map<std::string, int> attributes;
 

+ 115 - 21
src/modules/graphics/metal/Shader.mm

@@ -131,6 +131,7 @@ Shader::Shader(id<MTLDevice> device, love::graphics::ShaderStage *vertex, love::
 	, builtinUniformInfo()
 	, localUniformBufferData(nullptr)
 	, localUniformBufferSize(0)
+	, builtinUniformDataOffset(0)
 { @autoreleasepool {
 	using namespace glslang;
 	using namespace spirv_cross;
@@ -166,11 +167,7 @@ Shader::Shader(id<MTLDevice> device, love::graphics::ShaderStage *vertex, love::
 		opt.validate = true;
 
 		std::vector<unsigned int> spirv;
-
-		{
-//			timer::ScopedTimer("shader stage");
-			GlslangToSpv(*intermediate, spirv, &logger, &opt);
-		}
+		GlslangToSpv(*intermediate, spirv, &logger, &opt);
 
 		std::string msgs = logger.getAllMessages();
 //		printf("spirv length: %ld, messages:\n%s\n", spirv.size(), msgs.c_str());
@@ -203,28 +200,99 @@ Shader::Shader(id<MTLDevice> device, love::graphics::ShaderStage *vertex, love::
 				{
 					if (it->second.ints[0] != binding)
 						throw love::Exception("texture binding mismatch for %s: %d vs %d", resource.name.c_str(), it->second.ints[0], binding);
+					continue;
 				}
-				else
-				{
-					UniformInfo u = {};
-					u.baseType = UNIFORM_SAMPLER;
-					u.name = resource.name;
-					u.location = 0;
-					u.textures = new love::graphics::Texture*[1];
-					u.textures[0] = nullptr;
-					u.data = malloc(sizeof(int) * 1);
-					u.ints[0] = binding;
-//					printf("binding for %s: %d\n", u.name.c_str(), binding);
-
-					uniforms[u.name] = u;
-				}
+
+				UniformInfo u = {};
+				u.baseType = UNIFORM_SAMPLER;
+				u.name = resource.name;
+				u.location = 0;
+				u.textures = new love::graphics::Texture*[1];
+				u.textures[0] = nullptr;
+				u.data = malloc(sizeof(int) * 1);
+				u.ints[0] = binding;
+//				printf("binding for %s: %d\n", u.name.c_str(), binding);
+
+				uniforms[u.name] = u;
 			}
 
 			for (const auto &resource : resources.uniform_buffers)
 			{
+				auto it = uniforms.find(resource.name);
+				if (it != uniforms.end())
+				{
+					continue;
+				}
+
 				if (resource.name == "love_UniformsPerDrawBuffer")
 				{
 					msl.set_decoration(resource.id, spv::DecorationBinding, 0);
+					const SPIRType &type = msl.get_type(resource.base_type_id);
+					const auto &membertypes = type.member_types;
+
+					size_t size = msl.get_declared_struct_size(type);
+
+					if (localUniformBufferSize != 0)
+					{
+						if (localUniformBufferSize != size)
+							throw love::Exception("Local uniform buffer size mismatch");
+						continue;
+					}
+
+					localUniformBufferData = new uint8[size];
+					localUniformBufferSize = size;
+
+					memset(localUniformBufferData, 0, size);
+
+					for (size_t uindex = 0; uindex < membertypes.size(); uindex++)
+					{
+						const auto &membertype = msl.get_type(membertypes[uindex]);
+						size_t membersize = msl.get_declared_struct_member_size(type, uindex);
+						size_t offset = msl.type_struct_member_offset(type, uindex);
+
+						UniformInfo u = {};
+						u.name = msl.get_name(membertypes[uindex]);
+						u.dataSize = membersize;
+						u.count = std::max<size_t>(1, membertype.array.size());
+
+						BuiltinUniform builtin = BUILTIN_MAX_ENUM;
+						if (getConstant(u.name.c_str(), builtin))
+						{
+							if (builtin == BUILTIN_UNIFORMS_PER_DRAW)
+								builtinUniformDataOffset = offset;
+						}
+
+						switch (membertype.basetype)
+						{
+						case SPIRType::Int:
+						case SPIRType::UInt:
+						case SPIRType::Float:
+							u.data = localUniformBufferData + offset;
+							if (membertype.columns == 1)
+							{
+								if (membertype.basetype == SPIRType::Int)
+									u.baseType = UNIFORM_INT;
+								else if (membertype.basetype == SPIRType::UInt)
+									u.baseType = UNIFORM_UINT;
+								else
+									u.baseType = UNIFORM_FLOAT;
+								u.components = membertype.vecsize;
+							}
+							else
+							{
+								u.baseType = UNIFORM_MATRIX;
+								u.matrix.rows = membertype.vecsize;
+								u.matrix.columns = membertype.columns;
+							}
+							break;
+						case SPIRType::Struct:
+							// TODO
+							break;
+						default:
+							break;
+						}
+					}
+
 				}
 			}
 
@@ -247,10 +315,14 @@ Shader::Shader(id<MTLDevice> device, love::graphics::ShaderStage *vertex, love::
 				}
 			}
 
-			printf("ubos: %d, storage: %d, inputs: %d, outputs: %d, images: %d, samplers: %d, push: %d\n", resources.uniform_buffers.size(), resources.storage_buffers.size(), resources.stage_inputs.size(), resources.stage_outputs.size(), resources.storage_images.size(), resources.sampled_images.size(), resources.push_constant_buffers.size());
+			printf("// ubos: %ld, storage: %ld, inputs: %ld, outputs: %ld, images: %ld, samplers: %ld, push: %ld\n", resources.uniform_buffers.size(), resources.storage_buffers.size(), resources.stage_inputs.size(), resources.stage_outputs.size(), resources.storage_images.size(), resources.sampled_images.size(), resources.push_constant_buffers.size());
 
 			CompilerMSL::Options options;
 
+			options.set_msl_version(2, 1);
+
+			options.texture_buffer_native = true;
+
 #ifdef LOVE_IOS
 			options.platform = CompilerMSL::Options::iOS;
 #else
@@ -260,7 +332,7 @@ Shader::Shader(id<MTLDevice> device, love::graphics::ShaderStage *vertex, love::
 			msl.set_msl_options(options);
 
 			std::string source = msl.compile();
-//			printf("MSL SOURCE for stage %d:\n\n%s\n\n", i, source.c_str());
+//			printf("// MSL SOURCE for stage %d:\n\n%s\n\n", i, source.c_str());
 
 			NSString *nssource = [[NSString alloc] initWithBytes:source.c_str()
 														  length:source.length()
@@ -280,6 +352,7 @@ Shader::Shader(id<MTLDevice> device, love::graphics::ShaderStage *vertex, love::
 			{
 				spv::StorageClass storage = msl.get_storage_class(var);
 				const std::string &name = msl.get_name(var);
+				printf("var: %s\n", name.c_str());
 
 				if (i == ShaderStage::STAGE_VERTEX && storage == spv::StorageClassInput)
 					attributes[name] = msl.get_decoration(var, spv::DecorationLocation);
@@ -338,6 +411,27 @@ const Shader::UniformInfo *Shader::getUniformInfo(BuiltinUniform builtin) const
 	return builtinUniformInfo[(int)builtin];
 }
 
+void Shader::updateUniform(const UniformInfo * /*info*/, int /*count*/)
+{
+	// Nothing needed here, All uniform data will be memcpy'd to the main
+	// uniform buffer before drawing.
+}
+
+void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count)
+{
+	// TODO
+}
+
+void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count)
+{
+	// TODO
+}
+
+bool Shader::hasUniform(const std::string &name) const
+{
+	return uniforms.find(name) != uniforms.end();
+}
+
 id<MTLRenderPipelineState> Shader::getCachedRenderPipeline(const RenderPipelineKey &key)
 {
 	auto it = cachedRenderPipelines.find(key);

+ 3 - 3
src/modules/graphics/opengl/Graphics.cpp

@@ -129,7 +129,7 @@ Graphics::Graphics()
 			window->windowToDPICoords(&dpiW, &dpiH);
 
 			void *context = nullptr; // TODO
-			setMode(context, (int) dpiW, (int) dpiH, window->getPixelWidth(), window->getPixelHeight(), settings.stencil);
+			setMode(context, (int) dpiW, (int) dpiH, window->getPixelWidth(), window->getPixelHeight(), settings.stencil, settings.depth);
 		}
 	}
 }
@@ -190,12 +190,12 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
 	}
 }
 
-bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil)
+bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int /*backbufferdepth*/)
 {
 	this->width = width;
 	this->height = height;
 
-	this->windowHasStencil = windowhasstencil;
+	this->windowHasStencil = backbufferstencil;
 
 	// Okay, setup OpenGL.
 	gl.initContext();

+ 1 - 1
src/modules/graphics/opengl/Graphics.h

@@ -63,7 +63,7 @@ public:
 	love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
 
 	void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
-	bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil) override;
+	bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, int backbufferdepth) override;
 	void unSetMode() override;
 
 	void setActive(bool active) override;

+ 1 - 0
src/modules/graphics/opengl/OpenGL.cpp

@@ -588,6 +588,7 @@ GLenum OpenGL::getGLBufferType(BufferType type)
 		case BUFFERTYPE_VERTEX: return GL_ARRAY_BUFFER;
 		case BUFFERTYPE_INDEX: return GL_ELEMENT_ARRAY_BUFFER;
 		case BUFFERTYPE_TEXEL: return GL_TEXTURE_BUFFER;
+		case BUFFERTYPE_UNIFORM: return GL_UNIFORM_BUFFER;
 		case BUFFERTYPE_MAX_ENUM: return GL_ZERO;
 	}
 

+ 1 - 1
src/modules/window/sdl/Window.cpp

@@ -603,7 +603,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 			context = (void *) SDL_Metal_GetLayer(metalView);
 #endif
 
-		graphics->setMode(context, (int) scaledw, (int) scaledh, pixelWidth, pixelHeight, f.stencil);
+		graphics->setMode(context, (int) scaledw, (int) scaledh, pixelWidth, pixelHeight, f.stencil, f.depth);
 	}
 
 #ifdef LOVE_ANDROID