Commit 1bd9e2f8 by Chris Forbes

Tidy stencil state handling

Bug: b/128715612 Change-Id: I1e9859d2cf001bfb341a49ad4f8fc9ef52c9fa5b Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/27508Tested-by: 's avatarChris Forbes <chrisforbes@google.com> Presubmit-Ready: Chris Forbes <chrisforbes@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com>
parent 1cef4e69
...@@ -170,22 +170,9 @@ namespace sw ...@@ -170,22 +170,9 @@ namespace sw
stencilBuffer = nullptr; stencilBuffer = nullptr;
stencilEnable = false; stencilEnable = false;
stencilCompareMode = VK_COMPARE_OP_ALWAYS;
stencilReference = 0;
stencilMask = 0xFFFFFFFF;
stencilFailOperation = VK_STENCIL_OP_KEEP;
stencilPassOperation = VK_STENCIL_OP_KEEP;
stencilZFailOperation = VK_STENCIL_OP_KEEP;
stencilWriteMask = 0xFFFFFFFF;
twoSidedStencil = false; twoSidedStencil = false;
stencilCompareModeCCW = VK_COMPARE_OP_ALWAYS; frontStencil = {};
stencilReferenceCCW = 0; backStencil = {};
stencilMaskCCW = 0xFFFFFFFF;
stencilFailOperationCCW = VK_STENCIL_OP_KEEP;
stencilPassOperationCCW = VK_STENCIL_OP_KEEP;
stencilZFailOperationCCW = VK_STENCIL_OP_KEEP;
stencilWriteMaskCCW = 0xFFFFFFFF;
rasterizerDiscard = false; rasterizerDiscard = false;
......
...@@ -159,22 +159,9 @@ namespace sw ...@@ -159,22 +159,9 @@ namespace sw
DrawType drawType; DrawType drawType;
bool stencilEnable; bool stencilEnable;
VkCompareOp stencilCompareMode;
int stencilReference;
int stencilMask;
VkStencilOp stencilFailOperation;
VkStencilOp stencilPassOperation;
VkStencilOp stencilZFailOperation;
int stencilWriteMask;
bool twoSidedStencil; bool twoSidedStencil;
VkCompareOp stencilCompareModeCCW; VkStencilOpState frontStencil;
int stencilReferenceCCW; VkStencilOpState backStencil;
int stencilMaskCCW;
VkStencilOp stencilFailOperationCCW;
VkStencilOp stencilPassOperationCCW;
VkStencilOp stencilZFailOperationCCW;
int stencilWriteMaskCCW;
// Pixel processor states // Pixel processor states
VkCullModeFlags cullMode; VkCullModeFlags cullMode;
......
...@@ -292,22 +292,9 @@ namespace sw ...@@ -292,22 +292,9 @@ namespace sw
if(context->stencilActive()) if(context->stencilActive())
{ {
state.stencilActive = true; state.stencilActive = true;
state.stencilCompareMode = context->stencilCompareMode;
state.stencilFailOperation = context->stencilFailOperation;
state.stencilPassOperation = context->stencilPassOperation;
state.stencilZFailOperation = context->stencilZFailOperation;
state.noStencilMask = (context->stencilMask == 0xFF);
state.noStencilWriteMask = (context->stencilWriteMask == 0xFF);
state.stencilWriteMasked = (context->stencilWriteMask == 0x00);
state.twoSidedStencil = context->twoSidedStencil; state.twoSidedStencil = context->twoSidedStencil;
state.stencilCompareModeCCW = context->twoSidedStencil ? context->stencilCompareModeCCW : state.stencilCompareMode; state.frontStencil = context->frontStencil;
state.stencilFailOperationCCW = context->twoSidedStencil ? context->stencilFailOperationCCW : state.stencilFailOperation; state.backStencil = context->backStencil;
state.stencilPassOperationCCW = context->twoSidedStencil ? context->stencilPassOperationCCW : state.stencilPassOperation;
state.stencilZFailOperationCCW = context->twoSidedStencil ? context->stencilZFailOperationCCW : state.stencilZFailOperation;
state.noStencilMaskCCW = context->twoSidedStencil ? (context->stencilMaskCCW == 0xFF) : state.noStencilMask;
state.noStencilWriteMaskCCW = context->twoSidedStencil ? (context->stencilWriteMaskCCW == 0xFF) : state.noStencilWriteMask;
state.stencilWriteMaskedCCW = context->twoSidedStencil ? (context->stencilWriteMaskCCW == 0x00) : state.stencilWriteMasked;
} }
if(context->depthBufferActive()) if(context->depthBufferActive())
......
...@@ -39,21 +39,9 @@ namespace sw ...@@ -39,21 +39,9 @@ namespace sw
bool quadLayoutDepthBuffer; bool quadLayoutDepthBuffer;
bool stencilActive; bool stencilActive;
VkCompareOp stencilCompareMode;
VkStencilOp stencilFailOperation;
VkStencilOp stencilPassOperation;
VkStencilOp stencilZFailOperation;
bool noStencilMask;
bool noStencilWriteMask;
bool stencilWriteMasked;
bool twoSidedStencil; bool twoSidedStencil;
VkCompareOp stencilCompareModeCCW; VkStencilOpState frontStencil;
VkStencilOp stencilFailOperationCCW; VkStencilOpState backStencil;
VkStencilOp stencilPassOperationCCW;
VkStencilOp stencilZFailOperationCCW;
bool noStencilMaskCCW;
bool noStencilWriteMaskCCW;
bool stencilWriteMaskedCCW;
bool depthTestActive; bool depthTestActive;
bool occlusionEnabled; bool occlusionEnabled;
......
...@@ -328,8 +328,8 @@ namespace sw ...@@ -328,8 +328,8 @@ namespace sw
if(pixelState.stencilActive) if(pixelState.stencilActive)
{ {
data->stencil[0].set(context->stencilReference, context->stencilMask, context->stencilWriteMask); data->stencil[0].set(context->frontStencil.reference, context->frontStencil.compareMask, context->frontStencil.writeMask);
data->stencil[1].set(context->stencilReferenceCCW, context->stencilMaskCCW, context->stencilWriteMaskCCW); data->stencil[1].set(context->backStencil.reference, context->backStencil.compareMask, context->backStencil.writeMask);
} }
data->lineWidth = context->lineWidth; data->lineWidth = context->lineWidth;
......
...@@ -290,33 +290,33 @@ namespace sw ...@@ -290,33 +290,33 @@ namespace sw
} }
Byte8 value = *Pointer<Byte8>(buffer); Byte8 value = *Pointer<Byte8>(buffer);
Byte8 valueCCW = value; Byte8 valueBack = value;
if(!state.noStencilMask) if(state.frontStencil.compareMask != 0xff)
{ {
value &= *Pointer<Byte8>(data + OFFSET(DrawData,stencil[0].testMaskQ)); value &= *Pointer<Byte8>(data + OFFSET(DrawData,stencil[0].testMaskQ));
} }
stencilTest(value, state.stencilCompareMode, false); stencilTest(value, state.frontStencil.compareOp, false);
if(state.twoSidedStencil) if(state.twoSidedStencil)
{ {
if(!state.noStencilMaskCCW) if(state.backStencil.compareMask != 0xff)
{ {
valueCCW &= *Pointer<Byte8>(data + OFFSET(DrawData,stencil[1].testMaskQ)); valueBack &= *Pointer<Byte8>(data + OFFSET(DrawData,stencil[1].testMaskQ));
} }
stencilTest(valueCCW, state.stencilCompareModeCCW, true); stencilTest(valueBack, state.backStencil.compareOp, true);
value &= *Pointer<Byte8>(primitive + OFFSET(Primitive,clockwiseMask)); value &= *Pointer<Byte8>(primitive + OFFSET(Primitive,clockwiseMask));
valueCCW &= *Pointer<Byte8>(primitive + OFFSET(Primitive,invClockwiseMask)); valueBack &= *Pointer<Byte8>(primitive + OFFSET(Primitive,invClockwiseMask));
value |= valueCCW; value |= valueBack;
} }
sMask = SignMask(value) & cMask; sMask = SignMask(value) & cMask;
} }
void PixelRoutine::stencilTest(Byte8 &value, VkCompareOp stencilCompareMode, bool CCW) void PixelRoutine::stencilTest(Byte8 &value, VkCompareOp stencilCompareMode, bool isBack)
{ {
Byte8 equal; Byte8 equal;
...@@ -330,31 +330,31 @@ namespace sw ...@@ -330,31 +330,31 @@ namespace sw
break; break;
case VK_COMPARE_OP_LESS: // a < b ~ b > a case VK_COMPARE_OP_LESS: // a < b ~ b > a
value += Byte8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80); value += Byte8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
value = CmpGT(As<SByte8>(value), *Pointer<SByte8>(data + OFFSET(DrawData,stencil[CCW].referenceMaskedSignedQ))); value = CmpGT(As<SByte8>(value), *Pointer<SByte8>(data + OFFSET(DrawData,stencil[isBack].referenceMaskedSignedQ)));
break; break;
case VK_COMPARE_OP_EQUAL: case VK_COMPARE_OP_EQUAL:
value = CmpEQ(value, *Pointer<Byte8>(data + OFFSET(DrawData,stencil[CCW].referenceMaskedQ))); value = CmpEQ(value, *Pointer<Byte8>(data + OFFSET(DrawData,stencil[isBack].referenceMaskedQ)));
break; break;
case VK_COMPARE_OP_NOT_EQUAL: // a != b ~ !(a == b) case VK_COMPARE_OP_NOT_EQUAL: // a != b ~ !(a == b)
value = CmpEQ(value, *Pointer<Byte8>(data + OFFSET(DrawData,stencil[CCW].referenceMaskedQ))); value = CmpEQ(value, *Pointer<Byte8>(data + OFFSET(DrawData,stencil[isBack].referenceMaskedQ)));
value ^= Byte8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); value ^= Byte8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
break; break;
case VK_COMPARE_OP_LESS_OR_EQUAL: // a <= b ~ (b > a) || (a == b) case VK_COMPARE_OP_LESS_OR_EQUAL: // a <= b ~ (b > a) || (a == b)
equal = value; equal = value;
equal = CmpEQ(equal, *Pointer<Byte8>(data + OFFSET(DrawData,stencil[CCW].referenceMaskedQ))); equal = CmpEQ(equal, *Pointer<Byte8>(data + OFFSET(DrawData,stencil[isBack].referenceMaskedQ)));
value += Byte8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80); value += Byte8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
value = CmpGT(As<SByte8>(value), *Pointer<SByte8>(data + OFFSET(DrawData,stencil[CCW].referenceMaskedSignedQ))); value = CmpGT(As<SByte8>(value), *Pointer<SByte8>(data + OFFSET(DrawData,stencil[isBack].referenceMaskedSignedQ)));
value |= equal; value |= equal;
break; break;
case VK_COMPARE_OP_GREATER: // a > b case VK_COMPARE_OP_GREATER: // a > b
equal = *Pointer<Byte8>(data + OFFSET(DrawData,stencil[CCW].referenceMaskedSignedQ)); equal = *Pointer<Byte8>(data + OFFSET(DrawData,stencil[isBack].referenceMaskedSignedQ));
value += Byte8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80); value += Byte8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
equal = CmpGT(As<SByte8>(equal), As<SByte8>(value)); equal = CmpGT(As<SByte8>(equal), As<SByte8>(value));
value = equal; value = equal;
break; break;
case VK_COMPARE_OP_GREATER_OR_EQUAL: // a >= b ~ !(a < b) ~ !(b > a) case VK_COMPARE_OP_GREATER_OR_EQUAL: // a >= b ~ !(a < b) ~ !(b > a)
value += Byte8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80); value += Byte8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
value = CmpGT(As<SByte8>(value), *Pointer<SByte8>(data + OFFSET(DrawData,stencil[CCW].referenceMaskedSignedQ))); value = CmpGT(As<SByte8>(value), *Pointer<SByte8>(data + OFFSET(DrawData,stencil[isBack].referenceMaskedSignedQ)));
value ^= Byte8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); value ^= Byte8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
break; break;
default: default:
...@@ -729,15 +729,15 @@ namespace sw ...@@ -729,15 +729,15 @@ namespace sw
return; return;
} }
if(state.stencilPassOperation == VK_STENCIL_OP_KEEP && state.stencilZFailOperation == VK_STENCIL_OP_KEEP && state.stencilFailOperation == VK_STENCIL_OP_KEEP) if(state.frontStencil.passOp == VK_STENCIL_OP_KEEP && state.frontStencil.depthFailOp == VK_STENCIL_OP_KEEP && state.frontStencil.failOp == VK_STENCIL_OP_KEEP)
{ {
if(!state.twoSidedStencil || (state.stencilPassOperationCCW == VK_STENCIL_OP_KEEP && state.stencilZFailOperationCCW == VK_STENCIL_OP_KEEP && state.stencilFailOperationCCW == VK_STENCIL_OP_KEEP)) if(!state.twoSidedStencil || (state.backStencil.passOp == VK_STENCIL_OP_KEEP && state.backStencil.depthFailOp == VK_STENCIL_OP_KEEP && state.backStencil.failOp == VK_STENCIL_OP_KEEP))
{ {
return; return;
} }
} }
if(state.stencilWriteMasked && (!state.twoSidedStencil || state.stencilWriteMaskedCCW)) if((state.frontStencil.writeMask == 0) && (!state.twoSidedStencil || (state.backStencil.writeMask == 0)))
{ {
return; return;
} }
...@@ -752,9 +752,9 @@ namespace sw ...@@ -752,9 +752,9 @@ namespace sw
Byte8 bufferValue = *Pointer<Byte8>(buffer); Byte8 bufferValue = *Pointer<Byte8>(buffer);
Byte8 newValue; Byte8 newValue;
stencilOperation(newValue, bufferValue, state.stencilPassOperation, state.stencilZFailOperation, state.stencilFailOperation, false, zMask, sMask); stencilOperation(newValue, bufferValue, state.frontStencil, false, zMask, sMask);
if(!state.noStencilWriteMask) if(state.frontStencil.writeMask != 0)
{ {
Byte8 maskedValue = bufferValue; Byte8 maskedValue = bufferValue;
newValue &= *Pointer<Byte8>(data + OFFSET(DrawData,stencil[0].writeMaskQ)); newValue &= *Pointer<Byte8>(data + OFFSET(DrawData,stencil[0].writeMaskQ));
...@@ -764,21 +764,21 @@ namespace sw ...@@ -764,21 +764,21 @@ namespace sw
if(state.twoSidedStencil) if(state.twoSidedStencil)
{ {
Byte8 newValueCCW; Byte8 newValueBack;
stencilOperation(newValueCCW, bufferValue, state.stencilPassOperationCCW, state.stencilZFailOperationCCW, state.stencilFailOperationCCW, true, zMask, sMask); stencilOperation(newValueBack, bufferValue, state.backStencil, true, zMask, sMask);
if(!state.noStencilWriteMaskCCW) if(state.backStencil.writeMask != 0)
{ {
Byte8 maskedValue = bufferValue; Byte8 maskedValue = bufferValue;
newValueCCW &= *Pointer<Byte8>(data + OFFSET(DrawData,stencil[1].writeMaskQ)); newValueBack &= *Pointer<Byte8>(data + OFFSET(DrawData,stencil[1].writeMaskQ));
maskedValue &= *Pointer<Byte8>(data + OFFSET(DrawData,stencil[1].invWriteMaskQ)); maskedValue &= *Pointer<Byte8>(data + OFFSET(DrawData,stencil[1].invWriteMaskQ));
newValueCCW |= maskedValue; newValueBack |= maskedValue;
} }
newValue &= *Pointer<Byte8>(primitive + OFFSET(Primitive,clockwiseMask)); newValue &= *Pointer<Byte8>(primitive + OFFSET(Primitive,clockwiseMask));
newValueCCW &= *Pointer<Byte8>(primitive + OFFSET(Primitive,invClockwiseMask)); newValueBack &= *Pointer<Byte8>(primitive + OFFSET(Primitive,invClockwiseMask));
newValue |= newValueCCW; newValue |= newValueBack;
} }
newValue &= *Pointer<Byte8>(constants + OFFSET(Constants,maskB4Q) + 8 * cMask); newValue &= *Pointer<Byte8>(constants + OFFSET(Constants,maskB4Q) + 8 * cMask);
...@@ -788,27 +788,27 @@ namespace sw ...@@ -788,27 +788,27 @@ namespace sw
*Pointer<Byte4>(buffer) = Byte4(newValue); *Pointer<Byte4>(buffer) = Byte4(newValue);
} }
void PixelRoutine::stencilOperation(Byte8 &newValue, Byte8 &bufferValue, VkStencilOp stencilPassOperation, VkStencilOp stencilZFailOperation, VkStencilOp stencilFailOperation, bool CCW, Int &zMask, Int &sMask) void PixelRoutine::stencilOperation(Byte8 &newValue, Byte8 &bufferValue, VkStencilOpState const &ops, bool isBack, Int &zMask, Int &sMask)
{ {
Byte8 &pass = newValue; Byte8 &pass = newValue;
Byte8 fail; Byte8 fail;
Byte8 zFail; Byte8 zFail;
stencilOperation(pass, bufferValue, stencilPassOperation, CCW); stencilOperation(pass, bufferValue, ops.passOp, isBack);
if(stencilZFailOperation != stencilPassOperation) if(ops.depthFailOp != ops.passOp)
{ {
stencilOperation(zFail, bufferValue, stencilZFailOperation, CCW); stencilOperation(zFail, bufferValue, ops.depthFailOp, isBack);
} }
if(stencilFailOperation != stencilPassOperation || stencilFailOperation != stencilZFailOperation) if(ops.failOp != ops.passOp || ops.failOp != ops.depthFailOp)
{ {
stencilOperation(fail, bufferValue, stencilFailOperation, CCW); stencilOperation(fail, bufferValue, ops.failOp, isBack);
} }
if(stencilFailOperation != stencilPassOperation || stencilFailOperation != stencilZFailOperation) if(ops.failOp != ops.passOp || ops.failOp != ops.depthFailOp)
{ {
if(state.depthTestActive && stencilZFailOperation != stencilPassOperation) // zMask valid and values not the same if(state.depthTestActive && ops.depthFailOp != ops.passOp) // zMask valid and values not the same
{ {
pass &= *Pointer<Byte8>(constants + OFFSET(Constants,maskB4Q) + 8 * zMask); pass &= *Pointer<Byte8>(constants + OFFSET(Constants,maskB4Q) + 8 * zMask);
zFail &= *Pointer<Byte8>(constants + OFFSET(Constants,invMaskB4Q) + 8 * zMask); zFail &= *Pointer<Byte8>(constants + OFFSET(Constants,invMaskB4Q) + 8 * zMask);
...@@ -821,7 +821,7 @@ namespace sw ...@@ -821,7 +821,7 @@ namespace sw
} }
} }
void PixelRoutine::stencilOperation(Byte8 &output, Byte8 &bufferValue, VkStencilOp operation, bool CCW) void PixelRoutine::stencilOperation(Byte8 &output, Byte8 &bufferValue, VkStencilOp operation, bool isBack)
{ {
switch(operation) switch(operation)
{ {
...@@ -832,7 +832,7 @@ namespace sw ...@@ -832,7 +832,7 @@ namespace sw
output = Byte8(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); output = Byte8(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
break; break;
case VK_STENCIL_OP_REPLACE: case VK_STENCIL_OP_REPLACE:
output = *Pointer<Byte8>(data + OFFSET(DrawData,stencil[CCW].referenceQ)); output = *Pointer<Byte8>(data + OFFSET(DrawData,stencil[isBack].referenceQ));
break; break;
case VK_STENCIL_OP_INCREMENT_AND_CLAMP: case VK_STENCIL_OP_INCREMENT_AND_CLAMP:
output = AddSat(bufferValue, Byte8(1, 1, 1, 1, 1, 1, 1, 1)); output = AddSat(bufferValue, Byte8(1, 1, 1, 1, 1, 1, 1, 1));
......
...@@ -64,9 +64,9 @@ namespace sw ...@@ -64,9 +64,9 @@ namespace sw
private: private:
Float4 interpolateCentroid(Float4 &x, Float4 &y, Float4 &rhw, Pointer<Byte> planeEquation, bool flat, bool perspective); Float4 interpolateCentroid(Float4 &x, Float4 &y, Float4 &rhw, Pointer<Byte> planeEquation, bool flat, bool perspective);
void stencilTest(Pointer<Byte> &sBuffer, int q, Int &x, Int &sMask, Int &cMask); void stencilTest(Pointer<Byte> &sBuffer, int q, Int &x, Int &sMask, Int &cMask);
void stencilTest(Byte8 &value, VkCompareOp stencilCompareMode, bool CCW); void stencilTest(Byte8 &value, VkCompareOp stencilCompareMode, bool isBack);
void stencilOperation(Byte8 &newValue, Byte8 &bufferValue, VkStencilOp stencilPassOperation, VkStencilOp stencilZFailOperation, VkStencilOp stencilFailOperation, bool CCW, Int &zMask, Int &sMask); void stencilOperation(Byte8 &newValue, Byte8 &bufferValue, VkStencilOpState const &ops, bool isBack, Int &zMask, Int &sMask);
void stencilOperation(Byte8 &output, Byte8 &bufferValue, VkStencilOp operation, bool CCW); void stencilOperation(Byte8 &output, Byte8 &bufferValue, VkStencilOp operation, bool isBack);
Bool depthTest(Pointer<Byte> &zBuffer, int q, Int &x, Float4 &z, Int &sMask, Int &zMask, Int &cMask); Bool depthTest(Pointer<Byte> &zBuffer, int q, Int &x, Float4 &z, Int &sMask, Int &zMask, Int &cMask);
// Raster operations // Raster operations
......
...@@ -381,21 +381,8 @@ GraphicsPipeline::GraphicsPipeline(const VkGraphicsPipelineCreateInfo* pCreateIn ...@@ -381,21 +381,8 @@ GraphicsPipeline::GraphicsPipeline(const VkGraphicsPipelineCreateInfo* pCreateIn
context.stencilEnable = context.twoSidedStencil = depthStencilState->stencilTestEnable; context.stencilEnable = context.twoSidedStencil = depthStencilState->stencilTestEnable;
if(context.stencilEnable) if(context.stencilEnable)
{ {
context.stencilMask = depthStencilState->front.compareMask; context.frontStencil = depthStencilState->front;
context.stencilCompareMode = depthStencilState->front.compareOp; context.backStencil = depthStencilState->back;
context.stencilZFailOperation = depthStencilState->front.depthFailOp;
context.stencilFailOperation = depthStencilState->front.failOp;
context.stencilPassOperation = depthStencilState->front.passOp;
context.stencilReference = depthStencilState->front.reference;
context.stencilWriteMask = depthStencilState->front.writeMask;
context.stencilMaskCCW = depthStencilState->back.compareMask;
context.stencilCompareModeCCW = depthStencilState->back.compareOp;
context.stencilZFailOperationCCW = depthStencilState->back.depthFailOp;
context.stencilFailOperationCCW = depthStencilState->back.failOp;
context.stencilPassOperationCCW = depthStencilState->back.passOp;
context.stencilReferenceCCW = depthStencilState->back.reference;
context.stencilWriteMaskCCW = depthStencilState->back.writeMask;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment