Browse Source

metal: work on depth/stencil textures, MSAA

Alex Szpakowski 4 years ago
parent
commit
a4d0761e84

+ 15 - 2
src/modules/graphics/metal/Graphics.h

@@ -65,8 +65,8 @@ public:
 
 
 	void present(void *screenshotCallbackData) override;
 	void present(void *screenshotCallbackData) override;
 
 
-	int getRequestedBackbufferMSAA() const override { return 0; } // TODO
-	int getBackbufferMSAA() const override { return 0; } // TODO
+	int getRequestedBackbufferMSAA() const override;
+	int getBackbufferMSAA() const override;
 
 
 	void setColor(Colorf c) override;
 	void setColor(Colorf c) override;
 
 
@@ -173,6 +173,13 @@ private:
 		bool macCatalyst[2+1];
 		bool macCatalyst[2+1];
 	};
 	};
 
 
+	struct AttachmentStoreActions
+	{
+		MTLStoreAction color[MAX_COLOR_RENDER_TARGETS];
+		MTLStoreAction depth;
+		MTLStoreAction stencil;
+	};
+
 	love::graphics::ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) override;
 	love::graphics::ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) override;
 	love::graphics::Shader *newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel) override;
 	love::graphics::Shader *newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel) override;
 	love::graphics::StreamBuffer *newStreamBuffer(BufferUsage usage, size_t size) override;
 	love::graphics::StreamBuffer *newStreamBuffer(BufferUsage usage, size_t size) override;
@@ -200,6 +207,12 @@ private:
 	VertexAttributes lastVertexAttributes;
 	VertexAttributes lastVertexAttributes;
 	bool windowHasStencil;
 	bool windowHasStencil;
 
 
+	StrongRef<love::graphics::Texture> backbufferMSAA;
+	StrongRef<love::graphics::Texture> backbufferDepthStencil;
+	int requestedBackbufferMSAA;
+
+	AttachmentStoreActions attachmentStoreActions;
+
 	StreamBuffer *uniformBuffer;
 	StreamBuffer *uniformBuffer;
 	StreamBuffer::MapInfo uniformBufferData;
 	StreamBuffer::MapInfo uniformBufferData;
 	size_t uniformBufferOffset;
 	size_t uniformBufferOffset;

+ 127 - 60
src/modules/graphics/metal/Graphics.mm

@@ -180,6 +180,8 @@ Graphics::Graphics()
 	, passDesc(nil)
 	, passDesc(nil)
 	, dirtyRenderState(STATEBIT_ALL)
 	, dirtyRenderState(STATEBIT_ALL)
 	, windowHasStencil(false)
 	, windowHasStencil(false)
+	, requestedBackbufferMSAA(0)
+	, attachmentStoreActions()
 	, uniformBufferOffset(0)
 	, uniformBufferOffset(0)
 	, defaultAttributesBuffer(nullptr)
 	, defaultAttributesBuffer(nullptr)
 	, defaultTextures()
 	, defaultTextures()
@@ -306,6 +308,24 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
 		// Set up the projection matrix
 		// Set up the projection matrix
 		projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0, -10.0f, 10.0f);
 		projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0, -10.0f, 10.0f);
 	}
 	}
+
+	Texture::Settings settings;
+	settings.width = width;
+	settings.height = height;
+	settings.dpiScale = (float)pixelheight / (float)height;
+	settings.msaa = getRequestedBackbufferMSAA();
+	settings.renderTarget = true;
+	settings.readable.set(false);
+
+	backbufferMSAA.set(nullptr);
+	if (settings.msaa > 1)
+	{
+		settings.format = isGammaCorrect() ? PIXELFORMAT_RGBA8_UNORM_sRGB : PIXELFORMAT_RGBA8_UNORM;
+		backbufferMSAA.set(newTexture(settings), Acquire::NORETAIN);
+	}
+
+	settings.format = PIXELFORMAT_DEPTH24_UNORM_STENCIL8;
+	backbufferDepthStencil.set(newTexture(settings), Acquire::NORETAIN);
 }
 }
 
 
 bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa)
 bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa)
@@ -315,6 +335,7 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
 	this->metalLayer = (__bridge CAMetalLayer *) context;
 	this->metalLayer = (__bridge CAMetalLayer *) context;
 
 
 	this->windowHasStencil = windowhasstencil;
 	this->windowHasStencil = windowhasstencil;
+	this->requestedBackbufferMSAA = msaa;
 
 
 	metalLayer.device = device;
 	metalLayer.device = device;
 	metalLayer.pixelFormat = isGammaCorrect() ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
 	metalLayer.pixelFormat = isGammaCorrect() ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
@@ -420,6 +441,29 @@ void Graphics::submitCommandBuffer()
 	}
 	}
 }
 }
 
 
+static inline void setAttachment(const Graphics::RenderTarget &rt, MTLRenderPassAttachmentDescriptor *desc, MTLStoreAction &storeaction)
+{
+	bool isvolume = rt.texture->getTextureType() == TEXTURE_VOLUME;
+
+	desc.texture = getMTLRenderTarget(rt.texture);
+	desc.level = rt.mipmap;
+	desc.slice = isvolume ? 0 : rt.slice;
+	desc.depthPlane = isvolume ? rt.slice : 0;
+
+	// Default to load until clear or discard is called.
+	desc.loadAction = MTLLoadActionLoad;
+	desc.storeAction = MTLStoreActionUnknown;
+	storeaction = MTLStoreActionStore;
+
+	desc.resolveTexture = nil;
+
+	if (rt.texture->getMSAA() > 1 && rt.texture->isReadable())
+	{
+		storeaction = MTLStoreActionStoreAndMultisampleResolve;
+		desc.resolveTexture = getMTLTexture(rt.texture);
+	}
+}
+
 id<MTLRenderCommandEncoder> Graphics::useRenderEncoder()
 id<MTLRenderCommandEncoder> Graphics::useRenderEncoder()
 {
 {
 	if (renderEncoder == nil)
 	if (renderEncoder == nil)
@@ -427,7 +471,7 @@ id<MTLRenderCommandEncoder> Graphics::useRenderEncoder()
 		submitBlitEncoder();
 		submitBlitEncoder();
 
 
 		// Pass desc info for non-backbuffer render targets are set up in
 		// Pass desc info for non-backbuffer render targets are set up in
-		// setRenderTagetsInternal.
+		// setRenderTargetsInternal.
 		const auto &rts = states.back().renderTargets;
 		const auto &rts = states.back().renderTargets;
 		if (rts.getFirstTarget().texture.get() == nullptr)
 		if (rts.getFirstTarget().texture.get() == nullptr)
 		{
 		{
@@ -441,14 +485,44 @@ id<MTLRenderCommandEncoder> Graphics::useRenderEncoder()
 				activeDrawable = [metalLayer nextDrawable];
 				activeDrawable = [metalLayer nextDrawable];
 			}
 			}
 
 
-			passDesc.colorAttachments[0].texture = activeDrawable.texture;
+			if (backbufferMSAA.get())
+			{
+				attachmentStoreActions.color[0] = MTLStoreActionMultisampleResolve;
+				passDesc.colorAttachments[0].texture = getMTLRenderTarget(backbufferMSAA);
+				passDesc.colorAttachments[0].resolveTexture = activeDrawable.texture;
+			}
+			else
+			{
+				attachmentStoreActions.color[0] = MTLStoreActionStore;
+				passDesc.colorAttachments[0].texture = activeDrawable.texture;
+				passDesc.colorAttachments[0].resolveTexture = nil;
+			}
+
+			passDesc.colorAttachments[0].storeAction = MTLStoreActionUnknown;
 			passDesc.colorAttachments[0].level = 0;
 			passDesc.colorAttachments[0].level = 0;
 			passDesc.colorAttachments[0].slice = 0;
 			passDesc.colorAttachments[0].slice = 0;
 			passDesc.colorAttachments[0].depthPlane = 0;
 			passDesc.colorAttachments[0].depthPlane = 0;
+
+			RenderTarget rt(backbufferDepthStencil);
+			setAttachment(rt, passDesc.depthAttachment, attachmentStoreActions.depth);
+			setAttachment(rt, passDesc.stencilAttachment, attachmentStoreActions.stencil);
+			attachmentStoreActions.depth = MTLStoreActionDontCare;
+			attachmentStoreActions.stencil = MTLStoreActionDontCare;
 		}
 		}
 
 
 		renderEncoder = [useCommandBuffer() renderCommandEncoderWithDescriptor:passDesc];
 		renderEncoder = [useCommandBuffer() renderCommandEncoderWithDescriptor:passDesc];
 
 
+		for (int i = 0; i < MAX_COLOR_RENDER_TARGETS; i++)
+		{
+			passDesc.colorAttachments[0].texture = nil;
+			passDesc.colorAttachments[0].resolveTexture = nil;
+		}
+
+		passDesc.depthAttachment.texture = nil;
+		passDesc.depthAttachment.resolveTexture = nil;
+		passDesc.stencilAttachment.texture = nil;
+		passDesc.stencilAttachment.resolveTexture = nil;
+
 		id<MTLBuffer> defaultbuffer = getMTLBuffer(defaultAttributesBuffer);
 		id<MTLBuffer> defaultbuffer = getMTLBuffer(defaultAttributesBuffer);
 		[renderEncoder setVertexBuffer:defaultbuffer offset:0 atIndex:DEFAULT_VERTEX_BUFFER_BINDING];
 		[renderEncoder setVertexBuffer:defaultbuffer offset:0 atIndex:DEFAULT_VERTEX_BUFFER_BINDING];
 
 
@@ -462,10 +536,24 @@ void Graphics::submitRenderEncoder()
 {
 {
 	if (renderEncoder != nil)
 	if (renderEncoder != nil)
 	{
 	{
+		const auto &actions = attachmentStoreActions;
+		const auto &rts = states.back().renderTargets;
+		bool isbackbuffer = rts.getFirstTarget().texture.get() == nullptr;
+
+		if (isbackbuffer)
+			[renderEncoder setColorStoreAction:actions.color[0] atIndex:0];
+
+		for (size_t i = 0; i < rts.colors.size(); i++)
+			[renderEncoder setColorStoreAction:actions.color[i] atIndex:i];
+
+		if (rts.depthStencil.texture.get() || rts.temporaryRTFlags != 0 || isbackbuffer)
+		{
+			[renderEncoder setDepthStoreAction:actions.depth];
+			[renderEncoder setStencilStoreAction:actions.stencil];
+		}
+
 		[renderEncoder endEncoding];
 		[renderEncoder endEncoding];
 		renderEncoder = nil;
 		renderEncoder = nil;
-
-		passDesc.colorAttachments[0].texture = nil;
 	}
 	}
 }
 }
 
 
@@ -658,7 +746,10 @@ void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder, const Verte
 				key.colorRenderTargetFormats |= (rts[i].texture->getPixelFormat()) << (8 * i);
 				key.colorRenderTargetFormats |= (rts[i].texture->getPixelFormat()) << (8 * i);
 
 
 			if (state.renderTargets.getFirstTarget().texture.get() == nullptr)
 			if (state.renderTargets.getFirstTarget().texture.get() == nullptr)
+			{
 				key.colorRenderTargetFormats = isGammaCorrect() ? PIXELFORMAT_BGRA8_UNORM_sRGB : PIXELFORMAT_BGRA8_UNORM;
 				key.colorRenderTargetFormats = isGammaCorrect() ? PIXELFORMAT_BGRA8_UNORM_sRGB : PIXELFORMAT_BGRA8_UNORM;
+				key.depthStencilFormat = backbufferDepthStencil->getPixelFormat();
+			}
 
 
 			// TODO: depth/stencil
 			// TODO: depth/stencil
 
 
@@ -897,41 +988,18 @@ void Graphics::drawQuads(int start, int count, const VertexAttributes &attribute
 	}
 	}
 }}
 }}
 
 
-static inline void setAttachment(const Graphics::RenderTarget &rt, MTLRenderPassAttachmentDescriptor *desc)
-{
-	bool isvolume = rt.texture->getTextureType() == TEXTURE_VOLUME;
-
-	desc.texture = getMTLRenderTarget(rt.texture);
-	desc.level = rt.mipmap;
-	desc.slice = isvolume ? 0 : rt.slice;
-	desc.depthPlane = isvolume ? rt.slice : 0;
-
-	// Default to load until clear or discard is called.
-	desc.loadAction = MTLLoadActionLoad;
-	desc.storeAction = MTLStoreActionStore;
-
-	desc.resolveTexture = nil;
-
-	if (rt.texture->getMSAA() > 1)
-	{
-		// TODO
-		desc.resolveTexture = getMTLTexture(rt.texture);
-
-		// 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*/)
 void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int /*pixelw*/, int /*pixelh*/, bool /*hasSRGBtexture*/)
 { @autoreleasepool {
 { @autoreleasepool {
 	endPass();
 	endPass();
 
 
+	bool isbackbuffer = rts.getFirstTarget().texture == nullptr;
+
 	// Set up render pass descriptor for the next useRenderEncoder call.
 	// Set up render pass descriptor for the next useRenderEncoder call.
 	// The backbuffer will be set up in useRenderEncoder rather than here.
 	// The backbuffer will be set up in useRenderEncoder rather than here.
 	for (size_t i = 0; i < rts.colors.size(); i++)
 	for (size_t i = 0; i < rts.colors.size(); i++)
 	{
 	{
 		auto desc = passDesc.colorAttachments[i];
 		auto desc = passDesc.colorAttachments[i];
-		setAttachment(rts.colors[i], desc);
+		setAttachment(rts.colors[i], desc, attachmentStoreActions.color[i]);
 		passDesc.colorAttachments[i] = desc;
 		passDesc.colorAttachments[i] = desc;
 	}
 	}
 
 
@@ -941,13 +1009,20 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
 	passDesc.depthAttachment = nil;
 	passDesc.depthAttachment = nil;
 	passDesc.stencilAttachment = nil;
 	passDesc.stencilAttachment = nil;
 
 
-	if (rts.depthStencil.texture)
+	auto ds = rts.depthStencil.texture;
+	if (isbackbuffer && ds == nullptr)
+		ds = backbufferDepthStencil;
+
+	if (ds != nullptr)
 	{
 	{
-		if (isPixelFormatDepth(rts.depthStencil.texture->getPixelFormat()))
-			setAttachment(rts.depthStencil, passDesc.depthAttachment);
+		RenderTarget rt = rts.depthStencil;
+		rt.texture = ds;
 
 
-		if (isPixelFormatStencil(rts.depthStencil.texture->getPixelFormat()))
-			setAttachment(rts.depthStencil, passDesc.stencilAttachment);
+		if (isPixelFormatDepth(ds->getPixelFormat()))
+			setAttachment(rt, passDesc.depthAttachment, attachmentStoreActions.depth);
+
+		if (isPixelFormatStencil(ds->getPixelFormat()))
+			setAttachment(rt, passDesc.stencilAttachment, attachmentStoreActions.stencil);
 	}
 	}
 
 
 	projectionMatrix = Matrix4::ortho(0.0, (float) w, (float) h, 0.0, -10.0f, 10.0f);
 	projectionMatrix = Matrix4::ortho(0.0, (float) w, (float) h, 0.0, -10.0f, 10.0f);
@@ -957,6 +1032,9 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
 
 
 void Graphics::endPass()
 void Graphics::endPass()
 {
 {
+	// Make sure the encoder gets set up, if nothing else has done it yet.
+	useRenderEncoder();
+
 	flushBatchedDraws();
 	flushBatchedDraws();
 
 
 	auto &rts = states.back().renderTargets;
 	auto &rts = states.back().renderTargets;
@@ -965,33 +1043,11 @@ void Graphics::endPass()
 	// Discard the depth/stencil buffer if we're using an internal cached one.
 	// Discard the depth/stencil buffer if we're using an internal cached one.
 	if (depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
 	if (depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
 		discard({}, true);
 		discard({}, true);
+	else if (!rts.getFirstTarget().texture.get())
+		discard({}, true); // Backbuffer
 
 
 	submitRenderEncoder();
 	submitRenderEncoder();
 
 
-	// Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
-	// don't have to worry about resolving to slices.
-	if (rts.colors.size() > 0 && rts.colors[0].texture->getMSAA() > 1)
-	{
-		int mip = rts.colors[0].mipmap;
-		int w = rts.colors[0].texture->getPixelWidth(mip);
-		int h = rts.colors[0].texture->getPixelHeight(mip);
-
-		for (int i = 0; i < (int) rts.colors.size(); i++)
-		{
-			Texture *c = (Texture *) rts.colors[i].texture.get();
-
-			if (!c->isReadable())
-				continue;
-
-			// TODO
-		}
-	}
-
-	if (depthstencil != nullptr && depthstencil->getMSAA() > 1 && depthstencil->isReadable())
-	{
-		// TODO
-	}
-
 	for (const auto &rt : rts.colors)
 	for (const auto &rt : rts.colors)
 	{
 	{
 		if (rt.texture->getMipmapsMode() == Texture::MIPMAPS_AUTO && rt.mipmap == 0)
 		if (rt.texture->getMipmapsMode() == Texture::MIPMAPS_AUTO && rt.mipmap == 0)
@@ -1191,6 +1247,16 @@ void Graphics::present(void *screenshotCallbackData)
 	}
 	}
 }}
 }}
 
 
+int Graphics::getRequestedBackbufferMSAA() const
+{
+	return requestedBackbufferMSAA;
+}
+
+int Graphics::getBackbufferMSAA() const
+{
+	return backbufferMSAA.get() ? backbufferMSAA->getMSAA() : 0;
+}
+
 void Graphics::setColor(Colorf c)
 void Graphics::setColor(Colorf c)
 {
 {
 	c.r = std::min(std::max(c.r, 0.0f), 1.0f);
 	c.r = std::min(std::max(c.r, 0.0f), 1.0f);
@@ -1495,6 +1561,7 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, boo
 			break;
 			break;
 		case PIXELFORMAT_DEPTH24_UNORM_STENCIL8:
 		case PIXELFORMAT_DEPTH24_UNORM_STENCIL8:
 			// TODO
 			// TODO
+			flags |= rt | sample | msaa;
 			break;
 			break;
 		case PIXELFORMAT_DEPTH32_FLOAT_STENCIL8:
 		case PIXELFORMAT_DEPTH32_FLOAT_STENCIL8:
 			if (families.apple[1])
 			if (families.apple[1])
@@ -1582,7 +1649,7 @@ Graphics::RendererInfo Graphics::getRendererInfo() const
 {
 {
 	RendererInfo info;
 	RendererInfo info;
 	info.name = "Metal";
 	info.name = "Metal";
-	info.version = "1"; // TODO
+	info.version = "2.1"; // TODO
 	info.vendor = ""; // TODO
 	info.vendor = ""; // TODO
 	info.device = device.name.UTF8String;
 	info.device = device.name.UTF8String;
 	return info;
 	return info;

+ 10 - 2
src/modules/graphics/metal/Shader.mm

@@ -910,8 +910,16 @@ id<MTLRenderPipelineState> Shader::getCachedRenderPipeline(const RenderPipelineK
 		desc.colorAttachments[i] = attachment;
 		desc.colorAttachments[i] = attachment;
 	}
 	}
 
 
-	// TODO: depth/stencil attachment formats
-	
+	auto dsformat = (PixelFormat) key.depthStencilFormat;
+	if (isPixelFormatDepthStencil(dsformat))
+	{
+		bool isSRGB = false;
+		auto formatdesc = Metal::convertPixelFormat(dsformat, isSRGB);
+		if (isPixelFormatDepth(dsformat))
+			desc.depthAttachmentPixelFormat = formatdesc.format;
+		if (isPixelFormatStencil(dsformat))
+			desc.stencilAttachmentPixelFormat = formatdesc.format;
+	}
 
 
 	{
 	{
 		MTLVertexDescriptor *vertdesc = [MTLVertexDescriptor vertexDescriptor];
 		MTLVertexDescriptor *vertdesc = [MTLVertexDescriptor vertexDescriptor];