VK_EXT_blend_operation_advanced minimal implementation
This CL adds support for VK_EXT_blend_operation_advanced,
with limited features:
- VkPipelineColorBlendAdvancedStateCreateInfoEXT::blendOverlap
must be VK_BLEND_OVERLAP_UNCORRELATED_EXT
- VkPipelineColorBlendAdvancedStateCreateInfoEXT::dstPremultiplied
must be VK_TRUE
- VkPipelineColorBlendAdvancedStateCreateInfoEXT::srcPremultiplied
must be VK_TRUE
Tests: dEQP-VK.api.info.vulkan1p2_limits_validation.ext_blend_operation_advanced
Tests: dEQP-VK.pipeline.blend_operation_advanced.*
Bug: b/203652972
Change-Id: I046452c4399e4895dd98689ce237b50353dcf24a
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/58348
Commit-Queue: Alexis Hétu <sugoi@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Device/Context.cpp b/src/Device/Context.cpp
index 550a815..64148a5 100644
--- a/src/Device/Context.cpp
+++ b/src/Device/Context.cpp
@@ -531,6 +531,30 @@
blendConstants.w = colorBlendState->blendConstants[3];
}
+ const VkBaseInStructure *extensionColorBlendInfo = reinterpret_cast<const VkBaseInStructure *>(colorBlendState->pNext);
+ while(extensionColorBlendInfo)
+ {
+ switch(extensionColorBlendInfo->sType)
+ {
+ case VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT:
+ {
+ const VkPipelineColorBlendAdvancedStateCreateInfoEXT *colorBlendAdvancedCreateInfo = reinterpret_cast<const VkPipelineColorBlendAdvancedStateCreateInfoEXT *>(extensionColorBlendInfo);
+ ASSERT(colorBlendAdvancedCreateInfo->blendOverlap == VK_BLEND_OVERLAP_UNCORRELATED_EXT);
+ ASSERT(colorBlendAdvancedCreateInfo->dstPremultiplied == VK_TRUE);
+ ASSERT(colorBlendAdvancedCreateInfo->srcPremultiplied == VK_TRUE);
+ }
+ break;
+ case VK_STRUCTURE_TYPE_MAX_ENUM:
+ // dEQP tests that this value is ignored.
+ break;
+ default:
+ UNSUPPORTED("pCreateInfo->colorBlendState->pNext sType = %s", vk::Stringify(extensionColorBlendInfo->sType).c_str());
+ break;
+ }
+
+ extensionColorBlendInfo = extensionColorBlendInfo->pNext;
+ }
+
ASSERT(colorBlendState->attachmentCount <= sw::MAX_COLOR_BUFFERS);
for(auto i = 0u; i < colorBlendState->attachmentCount; i++)
{
@@ -725,8 +749,22 @@
case VK_BLEND_OP_REVERSE_SUBTRACT:
return blendState[index].sourceBlendFactor;
case VK_BLEND_OP_MIN:
- return VK_BLEND_FACTOR_ONE;
case VK_BLEND_OP_MAX:
+ case VK_BLEND_OP_MULTIPLY_EXT:
+ case VK_BLEND_OP_SCREEN_EXT:
+ case VK_BLEND_OP_OVERLAY_EXT:
+ case VK_BLEND_OP_DARKEN_EXT:
+ case VK_BLEND_OP_LIGHTEN_EXT:
+ case VK_BLEND_OP_COLORDODGE_EXT:
+ case VK_BLEND_OP_COLORBURN_EXT:
+ case VK_BLEND_OP_HARDLIGHT_EXT:
+ case VK_BLEND_OP_SOFTLIGHT_EXT:
+ case VK_BLEND_OP_DIFFERENCE_EXT:
+ case VK_BLEND_OP_EXCLUSION_EXT:
+ case VK_BLEND_OP_HSL_HUE_EXT:
+ case VK_BLEND_OP_HSL_SATURATION_EXT:
+ case VK_BLEND_OP_HSL_COLOR_EXT:
+ case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
return VK_BLEND_FACTOR_ONE;
default:
ASSERT(false);
@@ -748,8 +786,22 @@
case VK_BLEND_OP_REVERSE_SUBTRACT:
return blendState[index].destBlendFactor;
case VK_BLEND_OP_MIN:
- return VK_BLEND_FACTOR_ONE;
case VK_BLEND_OP_MAX:
+ case VK_BLEND_OP_MULTIPLY_EXT:
+ case VK_BLEND_OP_SCREEN_EXT:
+ case VK_BLEND_OP_OVERLAY_EXT:
+ case VK_BLEND_OP_DARKEN_EXT:
+ case VK_BLEND_OP_LIGHTEN_EXT:
+ case VK_BLEND_OP_COLORDODGE_EXT:
+ case VK_BLEND_OP_COLORBURN_EXT:
+ case VK_BLEND_OP_HARDLIGHT_EXT:
+ case VK_BLEND_OP_SOFTLIGHT_EXT:
+ case VK_BLEND_OP_DIFFERENCE_EXT:
+ case VK_BLEND_OP_EXCLUSION_EXT:
+ case VK_BLEND_OP_HSL_HUE_EXT:
+ case VK_BLEND_OP_HSL_SATURATION_EXT:
+ case VK_BLEND_OP_HSL_COLOR_EXT:
+ case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
return VK_BLEND_FACTOR_ONE;
default:
ASSERT(false);
@@ -862,9 +914,23 @@
}
}
case VK_BLEND_OP_MIN:
- return VK_BLEND_OP_MIN;
case VK_BLEND_OP_MAX:
- return VK_BLEND_OP_MAX;
+ case VK_BLEND_OP_MULTIPLY_EXT:
+ case VK_BLEND_OP_SCREEN_EXT:
+ case VK_BLEND_OP_OVERLAY_EXT:
+ case VK_BLEND_OP_DARKEN_EXT:
+ case VK_BLEND_OP_LIGHTEN_EXT:
+ case VK_BLEND_OP_COLORDODGE_EXT:
+ case VK_BLEND_OP_COLORBURN_EXT:
+ case VK_BLEND_OP_HARDLIGHT_EXT:
+ case VK_BLEND_OP_SOFTLIGHT_EXT:
+ case VK_BLEND_OP_DIFFERENCE_EXT:
+ case VK_BLEND_OP_EXCLUSION_EXT:
+ case VK_BLEND_OP_HSL_HUE_EXT:
+ case VK_BLEND_OP_HSL_SATURATION_EXT:
+ case VK_BLEND_OP_HSL_COLOR_EXT:
+ case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
+ return blendState[index].blendOperation;
default:
ASSERT(false);
}
@@ -883,8 +949,22 @@
case VK_BLEND_OP_REVERSE_SUBTRACT:
return blendState[index].sourceBlendFactorAlpha;
case VK_BLEND_OP_MIN:
- return VK_BLEND_FACTOR_ONE;
case VK_BLEND_OP_MAX:
+ case VK_BLEND_OP_MULTIPLY_EXT:
+ case VK_BLEND_OP_SCREEN_EXT:
+ case VK_BLEND_OP_OVERLAY_EXT:
+ case VK_BLEND_OP_DARKEN_EXT:
+ case VK_BLEND_OP_LIGHTEN_EXT:
+ case VK_BLEND_OP_COLORDODGE_EXT:
+ case VK_BLEND_OP_COLORBURN_EXT:
+ case VK_BLEND_OP_HARDLIGHT_EXT:
+ case VK_BLEND_OP_SOFTLIGHT_EXT:
+ case VK_BLEND_OP_DIFFERENCE_EXT:
+ case VK_BLEND_OP_EXCLUSION_EXT:
+ case VK_BLEND_OP_HSL_HUE_EXT:
+ case VK_BLEND_OP_HSL_SATURATION_EXT:
+ case VK_BLEND_OP_HSL_COLOR_EXT:
+ case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
return VK_BLEND_FACTOR_ONE;
default:
ASSERT(false);
@@ -904,8 +984,22 @@
case VK_BLEND_OP_REVERSE_SUBTRACT:
return blendState[index].destBlendFactorAlpha;
case VK_BLEND_OP_MIN:
- return VK_BLEND_FACTOR_ONE;
case VK_BLEND_OP_MAX:
+ case VK_BLEND_OP_MULTIPLY_EXT:
+ case VK_BLEND_OP_SCREEN_EXT:
+ case VK_BLEND_OP_OVERLAY_EXT:
+ case VK_BLEND_OP_DARKEN_EXT:
+ case VK_BLEND_OP_LIGHTEN_EXT:
+ case VK_BLEND_OP_COLORDODGE_EXT:
+ case VK_BLEND_OP_COLORBURN_EXT:
+ case VK_BLEND_OP_HARDLIGHT_EXT:
+ case VK_BLEND_OP_SOFTLIGHT_EXT:
+ case VK_BLEND_OP_DIFFERENCE_EXT:
+ case VK_BLEND_OP_EXCLUSION_EXT:
+ case VK_BLEND_OP_HSL_HUE_EXT:
+ case VK_BLEND_OP_HSL_SATURATION_EXT:
+ case VK_BLEND_OP_HSL_COLOR_EXT:
+ case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
return VK_BLEND_FACTOR_ONE;
default:
ASSERT(false);
@@ -1019,6 +1113,24 @@
return VK_BLEND_OP_MIN;
case VK_BLEND_OP_MAX:
return VK_BLEND_OP_MAX;
+ case VK_BLEND_OP_MULTIPLY_EXT:
+ case VK_BLEND_OP_SCREEN_EXT:
+ case VK_BLEND_OP_OVERLAY_EXT:
+ case VK_BLEND_OP_DARKEN_EXT:
+ case VK_BLEND_OP_LIGHTEN_EXT:
+ case VK_BLEND_OP_COLORDODGE_EXT:
+ case VK_BLEND_OP_COLORBURN_EXT:
+ case VK_BLEND_OP_HARDLIGHT_EXT:
+ case VK_BLEND_OP_SOFTLIGHT_EXT:
+ case VK_BLEND_OP_DIFFERENCE_EXT:
+ case VK_BLEND_OP_EXCLUSION_EXT:
+ case VK_BLEND_OP_HSL_HUE_EXT:
+ case VK_BLEND_OP_HSL_SATURATION_EXT:
+ case VK_BLEND_OP_HSL_COLOR_EXT:
+ case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
+ // All of the currently supported advanced blend modes compute the alpha the same way
+ // Use VK_BLEND_OP_MULTIPLY_EXT as a placeholder
+ return VK_BLEND_OP_MULTIPLY_EXT;
default:
ASSERT(false);
}
diff --git a/src/Pipeline/PixelRoutine.cpp b/src/Pipeline/PixelRoutine.cpp
index 8053391..ab1187c 100644
--- a/src/Pipeline/PixelRoutine.cpp
+++ b/src/Pipeline/PixelRoutine.cpp
@@ -1948,6 +1948,243 @@
}
}
+Float4 PixelRoutine::blendOpOverlay(Float4 &src, Float4 &dst)
+{
+ Int4 largeDst = CmpGT(dst, Float4(0.5f));
+ return As<Float4>(
+ (~largeDst &
+ As<Int4>(Float4(2.0f) * src * dst)) |
+ (largeDst &
+ As<Int4>(Float4(1.0f) - (Float4(2.0f) * (Float4(1.0f) - src) * (Float4(1.0f) - dst)))));
+}
+
+Float4 PixelRoutine::blendOpColorDodge(Float4 &src, Float4 &dst)
+{
+ Int4 srcBelowOne = CmpLT(src, Float4(1.0f));
+ Int4 positiveDst = CmpGT(dst, Float4(0.0f));
+ return As<Float4>(positiveDst & ((~srcBelowOne &
+ As<Int4>(Float4(1.0f))) |
+ (srcBelowOne &
+ As<Int4>(Min(Float4(1.0f), (dst / (Float4(1.0f) - src)))))));
+}
+
+Float4 PixelRoutine::blendOpColorBurn(Float4 &src, Float4 &dst)
+{
+ Int4 dstBelowOne = CmpLT(dst, Float4(1.0f));
+ Int4 positiveSrc = CmpGT(src, Float4(0.0f));
+ return As<Float4>(
+ (~dstBelowOne &
+ As<Int4>(Float4(1.0f))) |
+ (dstBelowOne & positiveSrc &
+ As<Int4>(Float4(1.0f) - Min(Float4(1.0f), (Float4(1.0f) - dst) / src))));
+}
+
+Float4 PixelRoutine::blendOpHardlight(Float4 &src, Float4 &dst)
+{
+ Int4 largeSrc = CmpGT(src, Float4(0.5f));
+ return As<Float4>(
+ (~largeSrc &
+ As<Int4>(Float4(2.0f) * src * dst)) |
+ (largeSrc &
+ As<Int4>(Float4(1.0f) - (Float4(2.0f) * (Float4(1.0f) - src) * (Float4(1.0f) - dst)))));
+}
+
+Float4 PixelRoutine::blendOpSoftlight(Float4 &src, Float4 &dst)
+{
+ Int4 largeSrc = CmpGT(src, Float4(0.5f));
+ Int4 largeDst = CmpGT(dst, Float4(0.25f));
+
+ return As<Float4>(
+ (~largeSrc &
+ As<Int4>(dst - ((Float4(1.0f) - (Float4(2.0f) * src)) * dst * (Float4(1.0f) - dst)))) |
+ (largeSrc & ((~largeDst &
+ As<Int4>(dst + (((Float4(2.0f) * src) - Float4(1.0f)) * dst * ((((Float4(16.0f) * dst) - Float4(12.0f)) * dst) + Float4(3.0f))))) |
+ (largeDst &
+ As<Int4>(dst + (((Float4(2.0f) * src) - Float4(1.0f)) * (Sqrt(dst) - dst)))))));
+}
+
+Float4 PixelRoutine::maxRGB(Vector4f &c)
+{
+ return Max(Max(c.x, c.y), c.z);
+}
+
+Float4 PixelRoutine::minRGB(Vector4f &c)
+{
+ return Min(Min(c.x, c.y), c.z);
+}
+
+void PixelRoutine::setLumSat(Vector4f &cbase, Vector4f &csat, Vector4f &clum, Float4 &x, Float4 &y, Float4 &z)
+{
+ Float4 minbase = minRGB(cbase);
+ Float4 sbase = maxRGB(cbase) - minbase;
+ Float4 ssat = maxRGB(csat) - minRGB(csat);
+ Int4 isNonZero = CmpGT(sbase, Float4(0.0f));
+ Vector4f color;
+ color.x = As<Float4>(isNonZero & As<Int4>((cbase.x - minbase) * ssat / sbase));
+ color.y = As<Float4>(isNonZero & As<Int4>((cbase.y - minbase) * ssat / sbase));
+ color.z = As<Float4>(isNonZero & As<Int4>((cbase.z - minbase) * ssat / sbase));
+ setLum(color, clum, x, y, z);
+}
+
+Float4 PixelRoutine::lumRGB(Vector4f &c)
+{
+ return c.x * Float4(0.3f) + c.y * Float4(0.59f) + c.z * Float4(0.11f);
+}
+
+Float4 PixelRoutine::computeLum(Float4 &color, Float4 &lum, Float4 &mincol, Float4 &maxcol, Int4 &negative, Int4 &aboveOne)
+{
+ return As<Float4>(
+ (negative &
+ As<Int4>(lum + ((color - lum) * lum) / (lum - mincol))) |
+ (~negative &
+ ((aboveOne &
+ As<Int4>(lum + ((color - lum) * (Float4(1.0f) - lum)) / (Float4(maxcol) - lum))) |
+ (~aboveOne &
+ As<Int4>(color)))));
+}
+
+void PixelRoutine::setLum(Vector4f &cbase, Vector4f &clum, Float4 &x, Float4 &y, Float4 &z)
+{
+ Float4 lbase = lumRGB(cbase);
+ Float4 llum = lumRGB(clum);
+ Float4 ldiff = llum - lbase;
+
+ Vector4f color;
+ color.x = cbase.x + ldiff;
+ color.y = cbase.y + ldiff;
+ color.z = cbase.z + ldiff;
+
+ Float4 lum = lumRGB(color);
+ Float4 mincol = minRGB(color);
+ Float4 maxcol = maxRGB(color);
+
+ Int4 negative = CmpLT(mincol, Float4(0.0f));
+ Int4 aboveOne = CmpGT(maxcol, Float4(1.0f));
+
+ x = computeLum(color.x, lum, mincol, maxcol, negative, aboveOne);
+ y = computeLum(color.y, lum, mincol, maxcol, negative, aboveOne);
+ z = computeLum(color.z, lum, mincol, maxcol, negative, aboveOne);
+}
+
+void PixelRoutine::premultiply(Vector4f &c)
+{
+ Int4 nonZeroAlpha = CmpNEQ(c.w, Float4(0.0f));
+ c.x = As<Float4>(nonZeroAlpha & As<Int4>(c.x / c.w));
+ c.y = As<Float4>(nonZeroAlpha & As<Int4>(c.y / c.w));
+ c.z = As<Float4>(nonZeroAlpha & As<Int4>(c.z / c.w));
+}
+
+Vector4f PixelRoutine::computeAdvancedBlendMode(int index, const Vector4f &src, const Vector4f &dst, const Vector4f &srcFactor, const Vector4f &dstFactor)
+{
+ Vector4f srcColor = src;
+ srcColor.x *= srcFactor.x;
+ srcColor.y *= srcFactor.y;
+ srcColor.z *= srcFactor.z;
+ srcColor.w *= srcFactor.w;
+
+ Vector4f dstColor = dst;
+ dstColor.x *= dstFactor.x;
+ dstColor.y *= dstFactor.y;
+ dstColor.z *= dstFactor.z;
+ dstColor.w *= dstFactor.w;
+
+ premultiply(srcColor);
+ premultiply(dstColor);
+
+ Vector4f blendedColor;
+
+ switch(state.blendState[index].blendOperation)
+ {
+ case VK_BLEND_OP_MULTIPLY_EXT:
+ blendedColor.x = (srcColor.x * dstColor.x);
+ blendedColor.y = (srcColor.y * dstColor.y);
+ blendedColor.z = (srcColor.z * dstColor.z);
+ break;
+ case VK_BLEND_OP_SCREEN_EXT:
+ blendedColor.x = srcColor.x + dstColor.x - (srcColor.x * dstColor.x);
+ blendedColor.y = srcColor.y + dstColor.y - (srcColor.y * dstColor.y);
+ blendedColor.z = srcColor.z + dstColor.z - (srcColor.z * dstColor.z);
+ break;
+ case VK_BLEND_OP_OVERLAY_EXT:
+ blendedColor.x = blendOpOverlay(srcColor.x, dstColor.x);
+ blendedColor.y = blendOpOverlay(srcColor.y, dstColor.y);
+ blendedColor.z = blendOpOverlay(srcColor.z, dstColor.z);
+ break;
+ case VK_BLEND_OP_DARKEN_EXT:
+ blendedColor.x = Min(srcColor.x, dstColor.x);
+ blendedColor.y = Min(srcColor.y, dstColor.y);
+ blendedColor.z = Min(srcColor.z, dstColor.z);
+ break;
+ case VK_BLEND_OP_LIGHTEN_EXT:
+ blendedColor.x = Max(srcColor.x, dstColor.x);
+ blendedColor.y = Max(srcColor.y, dstColor.y);
+ blendedColor.z = Max(srcColor.z, dstColor.z);
+ break;
+ case VK_BLEND_OP_COLORDODGE_EXT:
+ blendedColor.x = blendOpColorDodge(srcColor.x, dstColor.x);
+ blendedColor.y = blendOpColorDodge(srcColor.y, dstColor.y);
+ blendedColor.z = blendOpColorDodge(srcColor.z, dstColor.z);
+ break;
+ case VK_BLEND_OP_COLORBURN_EXT:
+ blendedColor.x = blendOpColorBurn(srcColor.x, dstColor.x);
+ blendedColor.y = blendOpColorBurn(srcColor.y, dstColor.y);
+ blendedColor.z = blendOpColorBurn(srcColor.z, dstColor.z);
+ break;
+ case VK_BLEND_OP_HARDLIGHT_EXT:
+ blendedColor.x = blendOpHardlight(srcColor.x, dstColor.x);
+ blendedColor.y = blendOpHardlight(srcColor.y, dstColor.y);
+ blendedColor.z = blendOpHardlight(srcColor.z, dstColor.z);
+ break;
+ case VK_BLEND_OP_SOFTLIGHT_EXT:
+ blendedColor.x = blendOpSoftlight(srcColor.x, dstColor.x);
+ blendedColor.y = blendOpSoftlight(srcColor.y, dstColor.y);
+ blendedColor.z = blendOpSoftlight(srcColor.z, dstColor.z);
+ break;
+ case VK_BLEND_OP_DIFFERENCE_EXT:
+ blendedColor.x = Abs(srcColor.x - dstColor.x);
+ blendedColor.y = Abs(srcColor.y - dstColor.y);
+ blendedColor.z = Abs(srcColor.z - dstColor.z);
+ break;
+ case VK_BLEND_OP_EXCLUSION_EXT:
+ blendedColor.x = srcColor.x + dstColor.x - (srcColor.x * dstColor.x * Float4(2.0f));
+ blendedColor.y = srcColor.y + dstColor.y - (srcColor.y * dstColor.y * Float4(2.0f));
+ blendedColor.z = srcColor.z + dstColor.z - (srcColor.z * dstColor.z * Float4(2.0f));
+ break;
+ case VK_BLEND_OP_HSL_HUE_EXT:
+ setLumSat(srcColor, dstColor, dstColor, blendedColor.x, blendedColor.y, blendedColor.z);
+ break;
+ case VK_BLEND_OP_HSL_SATURATION_EXT:
+ setLumSat(dstColor, srcColor, dstColor, blendedColor.x, blendedColor.y, blendedColor.z);
+ break;
+ case VK_BLEND_OP_HSL_COLOR_EXT:
+ setLum(srcColor, dstColor, blendedColor.x, blendedColor.y, blendedColor.z);
+ break;
+ case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
+ setLum(dstColor, srcColor, blendedColor.x, blendedColor.y, blendedColor.z);
+ break;
+ default:
+ UNSUPPORTED("Unsupported advanced VkBlendOp: %d", int(state.blendState[index].blendOperation));
+ break;
+ }
+
+ Float4 p = srcColor.w * dstColor.w;
+ blendedColor.x *= p;
+ blendedColor.y *= p;
+ blendedColor.z *= p;
+
+ p = srcColor.w * (Float4(1.0f) - dstColor.w);
+ blendedColor.x += srcColor.x * p;
+ blendedColor.y += srcColor.y * p;
+ blendedColor.z += srcColor.z * p;
+
+ p = dstColor.w * (Float4(1.0f) - srcColor.w);
+ blendedColor.x += dstColor.x * p;
+ blendedColor.y += dstColor.y * p;
+ blendedColor.z += dstColor.z * p;
+
+ return blendedColor;
+}
+
bool PixelRoutine::blendFactorCanExceedFormatRange(VkBlendFactor blendFactor, vk::Format format)
{
switch(blendFactor)
@@ -2162,6 +2399,8 @@
blendFactorRGB(sourceFactor, sourceColor, destColor, state.blendState[index].sourceBlendFactor, format);
blendFactorRGB(destFactor, sourceColor, destColor, state.blendState[index].destBlendFactor, format);
+ blendFactorAlpha(sourceFactor.w, sourceColor.w, destColor.w, state.blendState[index].sourceBlendFactorAlpha, format);
+ blendFactorAlpha(destFactor.w, sourceColor.w, destColor.w, state.blendState[index].destBlendFactorAlpha, format);
Vector4f blendedColor;
@@ -2207,13 +2446,27 @@
blendedColor.y = Float4(0.0f);
blendedColor.z = Float4(0.0f);
break;
+ case VK_BLEND_OP_MULTIPLY_EXT:
+ case VK_BLEND_OP_SCREEN_EXT:
+ case VK_BLEND_OP_OVERLAY_EXT:
+ case VK_BLEND_OP_DARKEN_EXT:
+ case VK_BLEND_OP_LIGHTEN_EXT:
+ case VK_BLEND_OP_COLORDODGE_EXT:
+ case VK_BLEND_OP_COLORBURN_EXT:
+ case VK_BLEND_OP_HARDLIGHT_EXT:
+ case VK_BLEND_OP_SOFTLIGHT_EXT:
+ case VK_BLEND_OP_DIFFERENCE_EXT:
+ case VK_BLEND_OP_EXCLUSION_EXT:
+ case VK_BLEND_OP_HSL_HUE_EXT:
+ case VK_BLEND_OP_HSL_SATURATION_EXT:
+ case VK_BLEND_OP_HSL_COLOR_EXT:
+ case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
+ blendedColor = computeAdvancedBlendMode(index, sourceColor, destColor, sourceFactor, destFactor);
+ break;
default:
UNSUPPORTED("VkBlendOp: %d", int(state.blendState[index].blendOperation));
}
- blendFactorAlpha(sourceFactor.w, sourceColor.w, destColor.w, state.blendState[index].sourceBlendFactorAlpha, format);
- blendFactorAlpha(destFactor.w, sourceColor.w, destColor.w, state.blendState[index].destBlendFactorAlpha, format);
-
switch(state.blendState[index].blendOperationAlpha)
{
case VK_BLEND_OP_ADD:
@@ -2240,6 +2493,11 @@
case VK_BLEND_OP_ZERO_EXT:
blendedColor.w = Float4(0.0f);
break;
+ case VK_BLEND_OP_MULTIPLY_EXT:
+ // All of the currently supported advanced blend modes compute the alpha the same way
+ // Use VK_BLEND_OP_MULTIPLY_EXT as a placeholder
+ blendedColor.w = sourceColor.w + destColor.w - (sourceColor.w * destColor.w);
+ break;
default:
UNSUPPORTED("VkBlendOp: %d", int(state.blendState[index].blendOperationAlpha));
}
diff --git a/src/Pipeline/PixelRoutine.hpp b/src/Pipeline/PixelRoutine.hpp
index 3f410e7..ba5f28d 100644
--- a/src/Pipeline/PixelRoutine.hpp
+++ b/src/Pipeline/PixelRoutine.hpp
@@ -77,6 +77,19 @@
void blendFactorRGB(Vector4f &blendFactorRGB, const Vector4f &sourceColor, const Vector4f &destColor, VkBlendFactor colorBlendFactor, vk::Format format);
void blendFactorAlpha(Float4 &blendFactorAlpha, const Float4 &sourceAlpha, const Float4 &destAlpha, VkBlendFactor alphaBlendFactor, vk::Format format);
bool blendFactorCanExceedFormatRange(VkBlendFactor blendFactor, vk::Format format);
+ Vector4f computeAdvancedBlendMode(int index, const Vector4f &src, const Vector4f &dst, const Vector4f &srcFactor, const Vector4f &dstFactor);
+ Float4 blendOpOverlay(Float4 &src, Float4 &dst);
+ Float4 blendOpColorDodge(Float4 &src, Float4 &dst);
+ Float4 blendOpColorBurn(Float4 &src, Float4 &dst);
+ Float4 blendOpHardlight(Float4 &src, Float4 &dst);
+ Float4 blendOpSoftlight(Float4 &src, Float4 &dst);
+ void setLumSat(Vector4f &cbase, Vector4f &csat, Vector4f &clum, Float4 &x, Float4 &y, Float4 &z);
+ void setLum(Vector4f &cbase, Vector4f &clum, Float4 &x, Float4 &y, Float4 &z);
+ Float4 computeLum(Float4 &color, Float4 &lum, Float4 &mincol, Float4 &maxcol, Int4 &negative, Int4 &aboveOne);
+ Float4 maxRGB(Vector4f &c);
+ Float4 minRGB(Vector4f &c);
+ Float4 lumRGB(Vector4f &c);
+ void premultiply(Vector4f &c);
void writeStencil(Pointer<Byte> &sBuffer, const Int &x, const Int sMask[4], const Int zMask[4], const Int cMask[4], const SampleSet &samples);
void writeDepth(Pointer<Byte> &zBuffer, const Int &x, const Int zMask[4], const SampleSet &samples);
void occlusionSampleCount(const Int zMask[4], const Int sMask[4], const SampleSet &samples);
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index be9e649..fe8f840 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -332,12 +332,17 @@
features->depthClipEnable = VK_TRUE;
}
-static void getPhysicalDevicCustomBorderColorFeaturesExt(VkPhysicalDeviceCustomBorderColorFeaturesEXT *features)
+static void getPhysicalDeviceCustomBorderColorFeaturesExt(VkPhysicalDeviceCustomBorderColorFeaturesEXT *features)
{
features->customBorderColors = VK_TRUE;
features->customBorderColorWithoutFormat = VK_TRUE;
}
+static void getPhysicalDeviceBlendOperationAdvancedFeaturesExt(VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT *features)
+{
+ features->advancedBlendCoherentOperations = VK_FALSE;
+}
+
static void getPhysicalDevice4444FormatsFeaturesExt(VkPhysicalDevice4444FormatsFeaturesEXT *features)
{
features->formatA4R4G4B4 = VK_TRUE;
@@ -437,7 +442,10 @@
getPhysicalDeviceDepthClipEnableFeaturesExt(reinterpret_cast<VkPhysicalDeviceDepthClipEnableFeaturesEXT *>(curExtension));
break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT:
- getPhysicalDevicCustomBorderColorFeaturesExt(reinterpret_cast<VkPhysicalDeviceCustomBorderColorFeaturesEXT *>(curExtension));
+ getPhysicalDeviceCustomBorderColorFeaturesExt(reinterpret_cast<VkPhysicalDeviceCustomBorderColorFeaturesEXT *>(curExtension));
+ break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT:
+ getPhysicalDeviceBlendOperationAdvancedFeaturesExt(reinterpret_cast<VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT *>(curExtension));
break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT:
getPhysicalDevice4444FormatsFeaturesExt(reinterpret_cast<struct VkPhysicalDevice4444FormatsFeaturesEXT *>(curExtension));
@@ -1034,6 +1042,18 @@
properties->maxCustomBorderColorSamplers = MAX_SAMPLER_ALLOCATION_COUNT;
}
+void PhysicalDevice::getProperties(VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT *properties) const
+{
+ // Note: advancedBlendMaxColorAttachments could already support sw::MAX_COLOR_BUFFERS as is,
+ // but using a value of 1 is enough for ANGLE to implement GL_KHR_blend_equation_advanced
+ properties->advancedBlendMaxColorAttachments = 1;
+ properties->advancedBlendIndependentBlend = VK_FALSE;
+ properties->advancedBlendNonPremultipliedSrcColor = VK_FALSE;
+ properties->advancedBlendNonPremultipliedDstColor = VK_FALSE;
+ properties->advancedBlendCorrelatedOverlap = VK_FALSE;
+ properties->advancedBlendAllOperations = VK_FALSE;
+}
+
template<typename T>
static void getSamplerFilterMinmaxProperties(T *properties)
{
@@ -1121,6 +1141,7 @@
InstantiateHasExtendedFeatures(VkPhysicalDeviceVulkan11Features);
InstantiateHasExtendedFeatures(VkPhysicalDeviceVulkan12Features);
InstantiateHasExtendedFeatures(VkPhysicalDeviceDepthClipEnableFeaturesEXT);
+InstantiateHasExtendedFeatures(VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT);
#undef InstantiateHasExtendedFeatures
void PhysicalDevice::GetFormatProperties(Format format, VkFormatProperties *pFormatProperties)
diff --git a/src/Vulkan/VkPhysicalDevice.hpp b/src/Vulkan/VkPhysicalDevice.hpp
index 068011a..080d1af 100644
--- a/src/Vulkan/VkPhysicalDevice.hpp
+++ b/src/Vulkan/VkPhysicalDevice.hpp
@@ -68,6 +68,7 @@
void getProperties(VkPhysicalDeviceDescriptorIndexingProperties *properties) const;
void getProperties(VkPhysicalDeviceDepthStencilResolveProperties *properties) const;
void getProperties(VkPhysicalDeviceCustomBorderColorPropertiesEXT *properties) const;
+ void getProperties(VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT *properties) const;
void getProperties(VkPhysicalDeviceVulkan11Properties *properties) const;
static void GetFormatProperties(Format format, VkFormatProperties *pFormatProperties);
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 2017350..5faaf75 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -371,6 +371,8 @@
{ { VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME, VK_EXT_IMAGE_ROBUSTNESS_SPEC_VERSION } },
// Useful for D3D emulation
{ { VK_EXT_4444_FORMATS_EXTENSION_NAME, VK_EXT_4444_FORMATS_SPEC_VERSION } },
+ // Used by ANGLE to support GL_KHR_blend_equation_advanced
+ { { VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME, VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION } },
#ifndef __ANDROID__
// We fully support the KHR_swapchain v70 additions, so just track the spec version.
{ { VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_SPEC_VERSION } },
@@ -917,6 +919,16 @@
}
}
break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT:
+ {
+ const auto *blendOpFeatures = reinterpret_cast<const VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT *>(extensionCreateInfo);
+ bool hasFeatures = vk::Cast(physicalDevice)->hasExtendedFeatures(blendOpFeatures);
+ if(!hasFeatures)
+ {
+ return VK_ERROR_FEATURE_NOT_PRESENT;
+ }
+ }
+ break;
// These structs are supported, but no behavior changes based on their feature bools
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES:
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES:
@@ -3253,6 +3265,12 @@
vk::Cast(physicalDevice)->getProperties(properties);
}
break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT:
+ {
+ auto properties = reinterpret_cast<VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT *>(extensionProperties);
+ vk::Cast(physicalDevice)->getProperties(properties);
+ }
+ break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES_KHR:
break;
default: