Browse Source

metal: implement depth/stencil state

Alex Szpakowski 5 years ago
parent
commit
9aa9319eb5

+ 18 - 6
src/modules/graphics/Graphics.cpp

@@ -144,7 +144,6 @@ Graphics::Graphics()
 	, pixelHeight(0)
 	, pixelHeight(0)
 	, created(false)
 	, created(false)
 	, active(true)
 	, active(true)
-	, writingToStencil(false)
 	, batchedDrawState()
 	, batchedDrawState()
 	, projectionMatrix()
 	, projectionMatrix()
 	, renderTargetSwitchCount(0)
 	, renderTargetSwitchCount(0)
@@ -420,7 +419,12 @@ void Graphics::restoreState(const DisplayState &s)
 	else
 	else
 		setScissor();
 		setScissor();
 
 
-	setStencilTest(s.stencilCompare, s.stencilTestValue);
+	if (s.stencil.action != STENCIL_KEEP)
+		drawToStencilBuffer(s.stencil.action, s.stencil.value);
+	else
+		stopDrawToStencilBuffer();
+
+	setStencilTest(s.stencil.compare, s.stencil.value);
 	setDepthMode(s.depthTest, s.depthWrite);
 	setDepthMode(s.depthTest, s.depthWrite);
 
 
 	setMeshCullMode(s.meshCullMode);
 	setMeshCullMode(s.meshCullMode);
@@ -464,8 +468,16 @@ void Graphics::restoreStateChecked(const DisplayState &s)
 			setScissor();
 			setScissor();
 	}
 	}
 
 
-	if (s.stencilCompare != cur.stencilCompare || s.stencilTestValue != cur.stencilTestValue)
-		setStencilTest(s.stencilCompare, s.stencilTestValue);
+	if (s.stencil.action != cur.stencil.action)
+	{
+		if (s.stencil.action != STENCIL_KEEP)
+			drawToStencilBuffer(s.stencil.action, s.stencil.value);
+		else
+			stopDrawToStencilBuffer();
+	}
+
+	if (s.stencil.compare != cur.stencil.compare || s.stencil.value != cur.stencil.value)
+		setStencilTest(s.stencil.compare, s.stencil.value);
 
 
 	if (s.depthTest != cur.depthTest || s.depthWrite != cur.depthWrite)
 	if (s.depthTest != cur.depthTest || s.depthWrite != cur.depthWrite)
 		setDepthMode(s.depthTest, s.depthWrite);
 		setDepthMode(s.depthTest, s.depthWrite);
@@ -903,8 +915,8 @@ void Graphics::setStencilTest()
 void Graphics::getStencilTest(CompareMode &compare, int &value) const
 void Graphics::getStencilTest(CompareMode &compare, int &value) const
 {
 {
 	const DisplayState &state = states.back();
 	const DisplayState &state = states.back();
-	compare = state.stencilCompare;
-	value = state.stencilTestValue;
+	compare = state.stencil.compare;
+	value = state.stencil.value;
 }
 }
 
 
 void Graphics::setDepthMode()
 void Graphics::setDepthMode()

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

@@ -898,8 +898,7 @@ protected:
 		bool scissor = false;
 		bool scissor = false;
 		Rect scissorRect = Rect();
 		Rect scissorRect = Rect();
 
 
-		CompareMode stencilCompare = COMPARE_ALWAYS;
-		int stencilTestValue = 0;
+		StencilState stencil;
 
 
 		CompareMode depthTest = COMPARE_ALWAYS;
 		CompareMode depthTest = COMPARE_ALWAYS;
 		bool depthWrite = false;
 		bool depthWrite = false;
@@ -981,8 +980,6 @@ protected:
 	bool created;
 	bool created;
 	bool active;
 	bool active;
 
 
-	bool writingToStencil;
-
 	StrongRef<love::graphics::Font> defaultFont;
 	StrongRef<love::graphics::Font> defaultFont;
 
 
 	std::vector<ScreenshotInfo> pendingScreenshotCallbacks;
 	std::vector<ScreenshotInfo> pendingScreenshotCallbacks;

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

@@ -195,6 +195,7 @@ private:
 	Texture *defaultTextures[TEXTURE_MAX_ENUM];
 	Texture *defaultTextures[TEXTURE_MAX_ENUM];
 
 
 	std::map<uint64, void *> cachedSamplers;
 	std::map<uint64, void *> cachedSamplers;
+	std::unordered_map<uint64, void *> cachedDepthStencilStates;
 
 
 }; // Graphics
 }; // Graphics
 
 

+ 85 - 29
src/modules/graphics/metal/Graphics.mm

@@ -95,6 +95,23 @@ static MTLCompareFunction getMTLCompareFunction(CompareMode mode)
 	return MTLCompareFunctionNever;
 	return MTLCompareFunctionNever;
 }
 }
 
 
+static MTLStencilOperation getMTLStencilOperation(StencilAction action)
+{
+	switch (action)
+	{
+		case STENCIL_KEEP: return MTLStencilOperationKeep;
+		case STENCIL_ZERO: return MTLStencilOperationZero;
+		case STENCIL_REPLACE: return MTLStencilOperationReplace;
+		case STENCIL_INCREMENT: return MTLStencilOperationIncrementClamp;
+		case STENCIL_DECREMENT: return MTLStencilOperationDecrementClamp;
+		case STENCIL_INCREMENT_WRAP: return MTLStencilOperationIncrementWrap;
+		case STENCIL_DECREMENT_WRAP: return MTLStencilOperationDecrementWrap;
+		case STENCIL_INVERT: return MTLStencilOperationInvert;
+		case STENCIL_MAX_ENUM: return MTLStencilOperationKeep;
+	}
+	return MTLStencilOperationKeep;
+}
+
 love::graphics::Graphics *createInstance()
 love::graphics::Graphics *createInstance()
 {
 {
 	love::graphics::Graphics *instance = nullptr;
 	love::graphics::Graphics *instance = nullptr;
@@ -192,6 +209,9 @@ Graphics::~Graphics()
 	for (auto &kvp : cachedSamplers)
 	for (auto &kvp : cachedSamplers)
 		CFBridgingRelease(kvp.second);
 		CFBridgingRelease(kvp.second);
 
 
+	for (auto &kvp : cachedDepthStencilStates)
+		CFBridgingRelease(kvp.second);
+
 	graphicsInstance = nullptr;
 	graphicsInstance = nullptr;
 }}
 }}
 
 
@@ -433,29 +453,38 @@ id<MTLSamplerState> Graphics::getCachedSampler(const SamplerState &s)
 
 
 id<MTLDepthStencilState> Graphics::getCachedDepthStencilState(const DepthState &depth, const StencilState &stencil)
 id<MTLDepthStencilState> Graphics::getCachedDepthStencilState(const DepthState &depth, const StencilState &stencil)
 {
 {
-	id<MTLDepthStencilState> state = nil;
+	uint64 key = (depth.compare << 0) | ((uint32)depth.write << 8)
+		| (stencil.action << 16) | (stencil.compare << 24)
+		| ((uint64)std::max(0, std::min(255, stencil.value)) << 32)
+		| ((uint64)std::min(255u, stencil.readMask) << 40)
+		| ((uint64)std::min(255u, stencil.writeMask) << 48);
 
 
-	{
-		MTLStencilDescriptor *stencildesc = [MTLStencilDescriptor new];
+	auto it = cachedDepthStencilStates.find(key);
+	if (it != cachedDepthStencilStates.end())
+		return (__bridge id<MTLDepthStencilState>) it->second;
 
 
-		stencildesc.stencilCompareFunction = getMTLCompareFunction(stencil.compare);
-		stencildesc.stencilFailureOperation = MTLStencilOperationKeep;
-		stencildesc.depthFailureOperation = MTLStencilOperationKeep;
-		stencildesc.depthStencilPassOperation = MTLStencilOperationKeep; // TODO
-		stencildesc.readMask = stencil.readMask;
-		stencildesc.writeMask = stencil.writeMask;
+	MTLStencilDescriptor *stencildesc = [MTLStencilDescriptor new];
 
 
-		MTLDepthStencilDescriptor *desc = [MTLDepthStencilDescriptor new];
+	stencildesc.stencilCompareFunction = getMTLCompareFunction(stencil.compare);
+	stencildesc.stencilFailureOperation = MTLStencilOperationKeep;
+	stencildesc.depthFailureOperation = MTLStencilOperationKeep;
+	stencildesc.depthStencilPassOperation = getMTLStencilOperation(stencil.action);
+	stencildesc.readMask = stencil.readMask;
+	stencildesc.writeMask = stencil.writeMask;
 
 
-		desc.depthCompareFunction = getMTLCompareFunction(depth.compare);
-		desc.depthWriteEnabled = depth.write;
-		desc.frontFaceStencil = stencildesc;
-		desc.backFaceStencil = stencildesc;
+	MTLDepthStencilDescriptor *desc = [MTLDepthStencilDescriptor new];
 
 
-		state = [device newDepthStencilStateWithDescriptor:desc];
-	}
+	desc.depthCompareFunction = getMTLCompareFunction(depth.compare);
+	desc.depthWriteEnabled = depth.write;
+	desc.frontFaceStencil = stencildesc;
+	desc.backFaceStencil = stencildesc;
+
+	id<MTLDepthStencilState> mtlstate = [device newDepthStencilStateWithDescriptor:desc];
 
 
-	return state;
+	if (mtlstate != nil)
+		cachedDepthStencilStates[key] = (void *) CFBridgingRetain(mtlstate);
+
+	return mtlstate;
 }
 }
 
 
 void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder, const vertex::Attributes &attributes)
 void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder, const vertex::Attributes &attributes)
@@ -559,7 +588,22 @@ void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder, const verte
 
 
 	if (dirtyState & (STATEBIT_DEPTH | STATEBIT_STENCIL))
 	if (dirtyState & (STATEBIT_DEPTH | STATEBIT_STENCIL))
 	{
 	{
-//		id<MTLDepthStencilState> dsstate = getCachedDepthStencilState(<#const DepthState &depth#>, <#const StencilState &stencil#>)
+		DepthState depth;
+		depth.compare = state.depthTest;
+		depth.write = state.depthWrite;
+
+		StencilState stencil = state.stencil;
+
+		if (stencil.action != STENCIL_KEEP)
+		{
+			// FIXME
+			stencil.compare = COMPARE_ALWAYS;
+		}
+
+		id<MTLDepthStencilState> mtlstate = getCachedDepthStencilState(depth, stencil);
+
+		[encoder setDepthStencilState:mtlstate];
+		[encoder setStencilReferenceValue:state.stencil.value];
 	}
 	}
 
 
 	dirtyRenderState = 0;
 	dirtyRenderState = 0;
@@ -888,8 +932,8 @@ void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth
 		auto color = MTLClearColorMake(c.value.r, c.value.g, c.value.b, c.value.a);
 		auto color = MTLClearColorMake(c.value.r, c.value.g, c.value.b, c.value.a);
 		for (int i = 0; i < MAX_COLOR_RENDER_TARGETS; i++)
 		for (int i = 0; i < MAX_COLOR_RENDER_TARGETS; i++)
 		{
 		{
-			passDesc.colorAttachments[0].clearColor = color;
-			passDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
+			passDesc.colorAttachments[i].clearColor = color;
+			passDesc.colorAttachments[i].loadAction = MTLLoadActionClear;
 		}
 		}
 	}
 	}
 
 
@@ -1098,7 +1142,8 @@ void Graphics::setScissor()
 
 
 void Graphics::drawToStencilBuffer(StencilAction action, int value)
 void Graphics::drawToStencilBuffer(StencilAction action, int value)
 {
 {
-	const auto &rts = states.back().renderTargets;
+	DisplayState &state = states.back();
+	const auto &rts = state.renderTargets;
 	love::graphics::Texture *dstexture = rts.depthStencil.texture.get();
 	love::graphics::Texture *dstexture = rts.depthStencil.texture.get();
 
 
 	if (!isRenderTargetActive() && !windowHasStencil)
 	if (!isRenderTargetActive() && !windowHasStencil)
@@ -1108,7 +1153,8 @@ void Graphics::drawToStencilBuffer(StencilAction action, int value)
 
 
 	flushBatchedDraws();
 	flushBatchedDraws();
 
 
-	writingToStencil = true;
+	state.stencil.action = action;
+	state.stencil.value = value;
 
 
 	dirtyRenderState |= STATEBIT_STENCIL;
 	dirtyRenderState |= STATEBIT_STENCIL;
 	// TODO
 	// TODO
@@ -1116,20 +1162,20 @@ void Graphics::drawToStencilBuffer(StencilAction action, int value)
 
 
 void Graphics::stopDrawToStencilBuffer()
 void Graphics::stopDrawToStencilBuffer()
 {
 {
-	if (!writingToStencil)
+	DisplayState &state = states.back();
+
+	if (state.stencil.action == STENCIL_KEEP)
 		return;
 		return;
 
 
 	flushBatchedDraws();
 	flushBatchedDraws();
 
 
-	writingToStencil = false;
-
-	const DisplayState &state = states.back();
+	state.stencil.action = STENCIL_KEEP;
 
 
 	// Revert the color write mask.
 	// Revert the color write mask.
 	setColorMask(state.colorMask);
 	setColorMask(state.colorMask);
 
 
 	// Use the user-set stencil test state when writes are disabled.
 	// Use the user-set stencil test state when writes are disabled.
-	setStencilTest(state.stencilCompare, state.stencilTestValue);
+	setStencilTest(state.stencil.compare, state.stencil.value);
 
 
 	dirtyRenderState |= STATEBIT_STENCIL;
 	dirtyRenderState |= STATEBIT_STENCIL;
 }
 }
@@ -1146,12 +1192,22 @@ void Graphics::setDepthMode(CompareMode compare, bool write)
 
 
 void Graphics::setFrontFaceWinding(vertex::Winding winding)
 void Graphics::setFrontFaceWinding(vertex::Winding winding)
 {
 {
-	// TODO
+	if (states.back().winding != winding)
+	{
+		flushBatchedDraws();
+		states.back().winding = winding;
+		dirtyRenderState |= STATEBIT_FACEWINDING;
+	}
 }
 }
 
 
 void Graphics::setColorMask(ColorChannelMask mask)
 void Graphics::setColorMask(ColorChannelMask mask)
 {
 {
-	// TODO
+	if (states.back().colorMask != mask)
+	{
+		flushBatchedDraws();
+		states.back().colorMask = mask;
+		dirtyRenderState |= STATEBIT_COLORMASK;
+	}
 }
 }
 
 
 void Graphics::setBlendState(const BlendState &blend)
 void Graphics::setBlendState(const BlendState &blend)

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

@@ -361,9 +361,7 @@ id<MTLRenderPipelineState> Shader::getCachedRenderPipeline(const RenderPipelineK
 
 
 	{
 	{
 		MTLVertexDescriptor *vertdesc = [MTLVertexDescriptor vertexDescriptor];
 		MTLVertexDescriptor *vertdesc = [MTLVertexDescriptor vertexDescriptor];
-
 		const auto &attributes = key.vertexAttributes;
 		const auto &attributes = key.vertexAttributes;
-		uint32 allbits = attributes.enableBits;
 
 
 		for (const auto &pair : this->attributes)
 		for (const auto &pair : this->attributes)
 		{
 		{

+ 13 - 11
src/modules/graphics/opengl/Graphics.cpp

@@ -1119,7 +1119,8 @@ void Graphics::setScissor()
 
 
 void Graphics::drawToStencilBuffer(StencilAction action, int value)
 void Graphics::drawToStencilBuffer(StencilAction action, int value)
 {
 {
-	const auto &rts = states.back().renderTargets;
+	DisplayState &state = states.back();
+	const auto &rts = state.renderTargets;
 	love::graphics::Texture *dstexture = rts.depthStencil.texture.get();
 	love::graphics::Texture *dstexture = rts.depthStencil.texture.get();
 
 
 	if (!isRenderTargetActive() && !windowHasStencil)
 	if (!isRenderTargetActive() && !windowHasStencil)
@@ -1129,7 +1130,8 @@ void Graphics::drawToStencilBuffer(StencilAction action, int value)
 
 
 	flushBatchedDraws();
 	flushBatchedDraws();
 
 
-	writingToStencil = true;
+	state.stencil.action = action;
+	state.stencil.value = value;
 
 
 	// Disable color writes but don't save the state for it.
 	// Disable color writes but don't save the state for it.
 	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
 	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
@@ -1169,33 +1171,33 @@ void Graphics::drawToStencilBuffer(StencilAction action, int value)
 
 
 void Graphics::stopDrawToStencilBuffer()
 void Graphics::stopDrawToStencilBuffer()
 {
 {
-	if (!writingToStencil)
+	DisplayState &state = states.back();
+
+	if (state.stencil.action == STENCIL_KEEP)
 		return;
 		return;
 
 
 	flushBatchedDraws();
 	flushBatchedDraws();
 
 
-	writingToStencil = false;
-
-	const DisplayState &state = states.back();
+	state.stencil.action = STENCIL_KEEP;
 
 
 	// Revert the color write mask.
 	// Revert the color write mask.
 	setColorMask(state.colorMask);
 	setColorMask(state.colorMask);
 
 
 	// Use the user-set stencil test state when writes are disabled.
 	// Use the user-set stencil test state when writes are disabled.
-	setStencilTest(state.stencilCompare, state.stencilTestValue);
+	setStencilTest(state.stencil.compare, state.stencil.value);
 }
 }
 
 
 void Graphics::setStencilTest(CompareMode compare, int value)
 void Graphics::setStencilTest(CompareMode compare, int value)
 {
 {
 	DisplayState &state = states.back();
 	DisplayState &state = states.back();
 
 
-	if (state.stencilCompare != compare || state.stencilTestValue != value)
+	if (state.stencil.compare != compare || state.stencil.value != value)
 		flushBatchedDraws();
 		flushBatchedDraws();
 
 
-	state.stencilCompare = compare;
-	state.stencilTestValue = value;
+	state.stencil.compare = compare;
+	state.stencil.value = value;
 
 
-	if (writingToStencil)
+	if (state.stencil.action != STENCIL_KEEP)
 		return;
 		return;
 
 
 	if (compare == COMPARE_ALWAYS)
 	if (compare == COMPARE_ALWAYS)

+ 0 - 10
src/modules/graphics/opengl/Shader.cpp

@@ -42,7 +42,6 @@ Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage
 	, program(0)
 	, program(0)
 	, builtinUniforms()
 	, builtinUniforms()
 	, builtinUniformInfo()
 	, builtinUniformInfo()
-	, builtinAttributes()
 	, lastPointSize(0.0f)
 	, lastPointSize(0.0f)
 {
 {
 	// load shader source and create program object
 	// load shader source and create program object
@@ -343,15 +342,6 @@ bool Shader::loadVolatile()
 	// Get all active uniform variables in this shader from OpenGL.
 	// Get all active uniform variables in this shader from OpenGL.
 	mapActiveUniforms();
 	mapActiveUniforms();
 
 
-	for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++)
-	{
-		const char *name = nullptr;
-		if (vertex::getConstant(BuiltinVertexAttribute(i), name))
-			builtinAttributes[i] = glGetAttribLocation(program, name);
-		else
-			builtinAttributes[i] = -1;
-	}
-
 	if (current == this)
 	if (current == this)
 	{
 	{
 		// make sure glUseProgram gets called.
 		// make sure glUseProgram gets called.

+ 0 - 3
src/modules/graphics/opengl/Shader.h

@@ -102,9 +102,6 @@ private:
 	GLint builtinUniforms[BUILTIN_MAX_ENUM];
 	GLint builtinUniforms[BUILTIN_MAX_ENUM];
 	UniformInfo *builtinUniformInfo[BUILTIN_MAX_ENUM];
 	UniformInfo *builtinUniformInfo[BUILTIN_MAX_ENUM];
 
 
-	// Location values for any generic vertex attribute variables.
-	GLint builtinAttributes[ATTRIB_MAX_ENUM];
-
 	std::map<std::string, GLint> attributes;
 	std::map<std::string, GLint> attributes;
 
 
 	// Uniform location buffer map
 	// Uniform location buffer map

+ 3 - 5
src/modules/graphics/wrap_GraphicsShader.lua

@@ -78,7 +78,7 @@ GLSL.UNIFORMS = [[
 // We *really* don't want to use mediump for these in vertex shaders though.
 // We *really* don't want to use mediump for these in vertex shaders though.
 #ifdef LOVE_USE_UNIFORM_BUFFERS
 #ifdef LOVE_USE_UNIFORM_BUFFERS
 layout (std140) uniform love_UniformsPerDrawBuffer {
 layout (std140) uniform love_UniformsPerDrawBuffer {
-	LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
+	highp vec4 love_UniformsPerDraw[13];
 };
 };
 #else
 #else
 uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
 uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13];
@@ -102,7 +102,6 @@ LOVE_HIGHP_OR_MEDIUMP vec4 ConstantColor;
 #define ViewNormalFromLocal NormalMatrix
 #define ViewNormalFromLocal NormalMatrix
 
 
 void love_initializeBuiltinUniforms() {
 void love_initializeBuiltinUniforms() {
-#if 1
 	TransformMatrix = mat4(
 	TransformMatrix = mat4(
 	   love_UniformsPerDraw[0],
 	   love_UniformsPerDraw[0],
 	   love_UniformsPerDraw[1],
 	   love_UniformsPerDraw[1],
@@ -125,7 +124,6 @@ void love_initializeBuiltinUniforms() {
 
 
 	love_ScreenSize = love_UniformsPerDraw[11];
 	love_ScreenSize = love_UniformsPerDraw[11];
 	ConstantColor = love_UniformsPerDraw[12];
 	ConstantColor = love_UniformsPerDraw[12];
-#endif
 }
 }
 ]]
 ]]
 
 
@@ -394,8 +392,8 @@ local function isPixelCode(code)
 	if code:match("vec4%s+effect%s*%(") then
 	if code:match("vec4%s+effect%s*%(") then
 		return true
 		return true
 	elseif code:match("void%s+effect%s*%(") then -- custom effect function
 	elseif code:match("void%s+effect%s*%(") then -- custom effect function
-		local multirendertargets = (code:match("love_RenderTargets") ~= nil) or (code:match("love_Canvases") ~= nil)
-		return true, true, multirendertargets
+		local mrt = (code:match("love_RenderTargets") ~= nil) or (code:match("love_Canvases") ~= nil)
+		return true, true, mrt
 	else
 	else
 		return false
 		return false
 	end
 	end