[Mesa-dev] [PATCH 11/16] glsl: Add a lowering pass to handle advanced blending modes.
Kenneth Graunke
kenneth at whitecape.org
Sat Aug 13 03:13:03 UTC 2016
Many GPUs cannot handle GL_KHR_blend_equation_advanced natively, and
need to emulate it in the pixel shader. This lowering pass implements
all the necessary math for advanced blending. It fetches the existing
framebuffer value using the MESA_shader_framebuffer_fetch built-in
variables, and the previous commit's state var uniform to select
which equation to use.
This is done at the GLSL IR level to make it easy for all drivers to
implement the GL_KHR_blend_equation_advanced extension and share code.
Drivers need to hook up MESA_shader_framebuffer_fetch functionality:
1. Hook up the fb_fetch_output variable
2. Implement BlendBarrier()
Then to get KHR_blend_equation_advanced, they simply need to:
3. Disable hardware blending based on ctx->Color._AdvancedBlendEnabled
4. Call this lowering pass.
Very little driver specific code should be required.
Signed-off-by: Kenneth Graunke <kenneth at whitecape.org>
---
src/compiler/Makefile.sources | 1 +
src/compiler/glsl/ir_optimization.h | 1 +
.../glsl/lower_blend_equation_advanced.cpp | 523 +++++++++++++++++++++
3 files changed, 525 insertions(+)
create mode 100644 src/compiler/glsl/lower_blend_equation_advanced.cpp
diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources
index 0ff9b23..fe26132 100644
--- a/src/compiler/Makefile.sources
+++ b/src/compiler/Makefile.sources
@@ -77,6 +77,7 @@ LIBGLSL_FILES = \
glsl/loop_analysis.h \
glsl/loop_controls.cpp \
glsl/loop_unroll.cpp \
+ glsl/lower_blend_equation_advanced.cpp \
glsl/lower_buffer_access.cpp \
glsl/lower_buffer_access.h \
glsl/lower_const_arrays_to_uniforms.cpp \
diff --git a/src/compiler/glsl/ir_optimization.h b/src/compiler/glsl/ir_optimization.h
index c29260a..3bd6928 100644
--- a/src/compiler/glsl/ir_optimization.h
+++ b/src/compiler/glsl/ir_optimization.h
@@ -151,6 +151,7 @@ void optimize_dead_builtin_variables(exec_list *instructions,
bool lower_tess_level(gl_linked_shader *shader);
bool lower_vertex_id(gl_linked_shader *shader);
+bool lower_blend_equation_advanced(gl_linked_shader *shader);
bool lower_subroutine(exec_list *instructions, struct _mesa_glsl_parse_state *state);
void propagate_invariance(exec_list *instructions);
diff --git a/src/compiler/glsl/lower_blend_equation_advanced.cpp b/src/compiler/glsl/lower_blend_equation_advanced.cpp
new file mode 100644
index 0000000..91a11f3
--- /dev/null
+++ b/src/compiler/glsl/lower_blend_equation_advanced.cpp
@@ -0,0 +1,523 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "ir.h"
+#include "ir_builder.h"
+#include "ir_optimization.h"
+#include "ir_hierarchical_visitor.h"
+#include "program/prog_instruction.h"
+#include "program/prog_statevars.h"
+#include "util/bitscan.h"
+
+namespace {
+
+using namespace ir_builder;
+
+class lower_blend_visitor : public ir_hierarchical_visitor {
+public:
+ explicit lower_blend_visitor(gl_linked_shader *sh)
+ : sh(sh), mem_ctx(ralloc_parent(sh->ir)), progress(false)
+ {
+ fb = new(mem_ctx) ir_variable(glsl_type::vec4_type, "__blend_fb_fetch",
+ ir_var_shader_out);
+ fb->data.location = FRAG_RESULT_DATA0;
+ fb->data.read_only = 1;
+ fb->data.fb_fetch_output = 1;
+
+ mode = new(mem_ctx) ir_variable(glsl_type::uint_type,
+ "gl_AdvancedBlendModeMESA",
+ ir_var_uniform);
+ mode->data.how_declared = ir_var_hidden;
+ mode->allocate_state_slots(1);
+ ir_state_slot *slot0 = &mode->get_state_slots()[0];
+ slot0->swizzle = SWIZZLE_XXXX;
+ slot0->tokens[0] = STATE_INTERNAL;
+ slot0->tokens[1] = STATE_ADVANCED_BLENDING_MODE;
+ for (int i = 2; i < STATE_LENGTH; i++)
+ slot0->tokens[i] = 0;
+ }
+
+ virtual ~lower_blend_visitor()
+ {
+ }
+
+ bool run()
+ {
+ sh->ir->push_head(fb);
+ sh->ir->push_head(mode);
+
+ visit_list_elements(this, sh->ir);
+ validate_ir_tree(sh->ir);
+ return progress;
+ }
+
+private:
+ ir_visitor_status visit_enter(ir_assignment *ir);
+
+ ir_constant *imm1(float x)
+ {
+ return new(mem_ctx) ir_constant(x, 1);
+ }
+
+ ir_constant *imm3(float x)
+ {
+ return new(mem_ctx) ir_constant(x, 3);
+ }
+
+ gl_linked_shader *sh;
+ void *mem_ctx;
+ bool progress;
+
+ ir_variable *mode; /* uniform */
+ ir_variable *fb;
+
+ void clip_color(ir_factory *f, ir_variable *color);
+ void set_lum(ir_factory *f, ir_variable *color,
+ ir_variable *cbase, ir_variable *clum);
+ void set_lum_sat(ir_factory *f, ir_variable *color,
+ ir_variable *cbase, ir_variable *csat, ir_variable *clum);
+
+ void lower(ir_assignment *ir);
+ ir_rvalue *is_mode(enum gl_blend_support_qualifier q);
+ ir_rvalue *blend_multiply(ir_variable *src, ir_variable *dst);
+ ir_rvalue *blend_screen(ir_variable *src, ir_variable *dst);
+ ir_rvalue *blend_overlay(ir_variable *src, ir_variable *dst);
+ ir_rvalue *blend_darken(ir_variable *src, ir_variable *dst);
+ ir_rvalue *blend_lighten(ir_variable *src, ir_variable *dst);
+ ir_rvalue *blend_colordodge(ir_variable *src, ir_variable *dst);
+ ir_rvalue *blend_colorburn(ir_variable *src, ir_variable *dst);
+ ir_rvalue *blend_hardlight(ir_variable *src, ir_variable *dst);
+ ir_rvalue *blend_softlight(ir_variable *src, ir_variable *dst);
+ ir_rvalue *blend_difference(ir_variable *src, ir_variable *dst);
+ ir_rvalue *blend_exclusion(ir_variable *src, ir_variable *dst);
+};
+
+}
+
+ir_rvalue *
+lower_blend_visitor::blend_multiply(ir_variable *src, ir_variable *dst)
+{
+ /* f(Cs,Cd) = Cs*Cd */
+ return mul(src, dst);
+}
+
+ir_rvalue *
+lower_blend_visitor::blend_screen(ir_variable *src, ir_variable *dst)
+{
+ /* f(Cs,Cd) = Cs+Cd-Cs*Cd */
+ return sub(add(src, dst), mul(src, dst));
+}
+
+ir_rvalue *
+lower_blend_visitor::blend_overlay(ir_variable *src, ir_variable *dst)
+{
+ /* f(Cs,Cd) = 2*Cs*Cd, if Cd <= 0.5
+ * 1-2*(1-Cs)*(1-Cd), otherwise
+ */
+ ir_rvalue *rule_1 = mul(imm3(2), mul(src, dst));
+ ir_rvalue *rule_2 =
+ sub(imm3(1), mul(imm3(2), mul(sub(imm3(1), src), sub(imm3(1), dst))));
+ return csel(lequal(dst, imm3(0.5f)), rule_1, rule_2);
+}
+
+ir_rvalue *
+lower_blend_visitor::blend_darken(ir_variable *src, ir_variable *dst)
+{
+ /* f(Cs,Cd) = min(Cs,Cd) */
+ return min2(src, dst);
+}
+
+ir_rvalue *
+lower_blend_visitor::blend_lighten(ir_variable *src, ir_variable *dst)
+{
+ /* f(Cs,Cd) = max(Cs,Cd) */
+ return max2(src, dst);
+}
+
+ir_rvalue *
+lower_blend_visitor::blend_colordodge(ir_variable *src, ir_variable *dst)
+{
+ /* f(Cs,Cd) =
+ * 0, if Cd <= 0
+ * min(1,Cd/(1-Cs)), if Cd > 0 and Cs < 1
+ * 1, if Cd > 0 and Cs >= 1
+ */
+ return csel(lequal(dst, imm3(0)), imm3(0),
+ csel(gequal(src, imm3(1)), imm3(1),
+ min2(imm3(1), div(dst, sub(imm3(1), src)))));
+}
+
+ir_rvalue *
+lower_blend_visitor::blend_colorburn(ir_variable *src, ir_variable *dst)
+{
+ /* f(Cs,Cd) =
+ * 1, if Cd >= 1
+ * 1 - min(1,(1-Cd)/Cs), if Cd < 1 and Cs > 0
+ * 0, if Cd < 1 and Cs <= 0
+ */
+ return csel(gequal(dst, imm3(1)), imm3(1),
+ csel(lequal(src, imm3(0)), imm3(0),
+ sub(imm3(1), min2(imm3(1), div(sub(imm3(1), dst), src)))));
+}
+
+ir_rvalue *
+lower_blend_visitor::blend_hardlight(ir_variable *src, ir_variable *dst)
+{
+ /* f(Cs,Cd) = 2*Cs*Cd, if Cs <= 0.5
+ * 1-2*(1-Cs)*(1-Cd), otherwise
+ */
+ ir_rvalue *rule_1 = mul(imm3(2), mul(src, dst));
+ ir_rvalue *rule_2 =
+ sub(imm3(1), mul(imm3(2), mul(sub(imm3(1), src), sub(imm3(1), dst))));
+ return csel(lequal(src, imm3(0.5f)), rule_1, rule_2);
+}
+
+ir_rvalue *
+lower_blend_visitor::blend_softlight(ir_variable *src, ir_variable *dst)
+{
+ /* f(Cs,Cd) =
+ * Cd-(1-2*Cs)*Cd*(1-Cd),
+ * if Cs <= 0.5
+ * Cd+(2*Cs-1)*Cd*((16*Cd-12)*Cd+3),
+ * if Cs > 0.5 and Cd <= 0.25
+ * Cd+(2*Cs-1)*(sqrt(Cd)-Cd),
+ * if Cs > 0.5 and Cd > 0.25
+ *
+ * We can simplify this to
+ *
+ * f(Cs,Cd) = Cd+(2*Cs-1)*g(Cs,Cd) where
+ * g(Cs,Cd) = Cd*Cd-Cd if Cs <= 0.5
+ * Cd*((16*Cd-12)*Cd+3) if Cs > 0.5 and Cd <= 0.25
+ * sqrt(Cd)-Cd, otherwise
+ */
+ ir_rvalue *factor_1 = mul(dst, sub(imm3(1), dst));
+ ir_rvalue *factor_2 =
+ mul(dst, add(mul(sub(mul(imm3(16), dst), imm3(12)), dst), imm3(3)));
+ ir_rvalue *factor_3 = sub(sqrt(dst), dst);
+ ir_rvalue *factor = csel(lequal(src, imm3(0.5f)), factor_1,
+ csel(lequal(dst, imm3(0.25f)),
+ factor_2, factor_3));
+ return add(dst, mul(sub(mul(imm3(2), src), imm3(1)), factor));
+}
+
+ir_rvalue *
+lower_blend_visitor::blend_difference(ir_variable *src, ir_variable *dst)
+{
+ return abs(sub(dst, src));
+}
+
+ir_rvalue *
+lower_blend_visitor::blend_exclusion(ir_variable *src, ir_variable *dst)
+{
+ return add(src, sub(dst, mul(imm3(2), mul(src, dst))));
+}
+
+/* Return the minimum of a vec3's components */
+ir_rvalue *
+minv3(ir_variable *v)
+{
+ return min2(min2(swizzle_x(v), swizzle_y(v)), swizzle_z(v));
+}
+
+/* Return the maximum of a vec3's components */
+ir_rvalue *
+maxv3(ir_variable *v)
+{
+ return max2(max2(swizzle_x(v), swizzle_y(v)), swizzle_z(v));
+}
+
+ir_rvalue *
+lumv3(ir_variable *c)
+{
+ ir_constant_data data;
+ data.f[0] = 0.30;
+ data.f[1] = 0.59;
+ data.f[2] = 0.11;
+
+ void *mem_ctx = ralloc_parent(c);
+
+ /* dot(c, vec3(0.30, 0.59, 0.11)) */
+ return dot(c, new(mem_ctx) ir_constant(glsl_type::vec3_type, &data));
+}
+
+ir_rvalue *
+satv3(ir_variable *c)
+{
+ return sub(maxv3(c), minv3(c));
+}
+
+/* If any color components are outside [0,1], adjust the color to
+ * get the components in range.
+ */
+void
+lower_blend_visitor::clip_color(ir_factory *f, ir_variable *color)
+{
+ ir_variable *lum = f->make_temp(glsl_type::float_type, "__blend_lum");
+ ir_variable *mincol = f->make_temp(glsl_type::float_type, "__blend_mincol");
+ ir_variable *maxcol = f->make_temp(glsl_type::float_type, "__blend_maxcol");
+ ir_variable *tmp = f->make_temp(glsl_type::vec3_type, "__blend_clip_tmp");
+
+ f->emit(assign(lum, lumv3(color)));
+ f->emit(assign(mincol, minv3(color)));
+ f->emit(assign(maxcol, maxv3(color)));
+ f->emit(assign(tmp, mul(sub(color, lum), lum)));
+ f->emit(if_tree(less(mincol, imm1(0)),
+ assign(color, add(lum, div(tmp, sub(lum, mincol))))));
+ f->emit(if_tree(greater(maxcol, imm1(1)),
+ assign(color, add(lum, div(tmp, sub(maxcol, lum))))));
+}
+
+/* Take the base RGB color <cbase> and override its luminosity
+ * with that of the RGB color <clum>.
+ */
+void
+lower_blend_visitor::set_lum(ir_factory *f,
+ ir_variable *color,
+ ir_variable *cbase,
+ ir_variable *clum)
+{
+ f->emit(assign(color, add(cbase, sub(lumv3(clum), lumv3(cbase)))));
+ clip_color(f, color);
+}
+
+/* Take the base RGB color <cbase> and override its saturation with
+ * that of the RGB color <csat>. The override the luminosity of the
+ * result with that of the RGB color <clum>.
+ */
+void
+lower_blend_visitor::set_lum_sat(ir_factory *f,
+ ir_variable *color,
+ ir_variable *cbase,
+ ir_variable *csat,
+ ir_variable *clum)
+{
+ ir_rvalue *minbase = minv3(cbase);
+ ir_rvalue *ssat = satv3(csat);
+
+ ir_variable *sbase = f->make_temp(glsl_type::float_type, "__blend_sbase");
+ f->emit(assign(sbase, satv3(cbase)));
+
+ /* Equivalent (modulo rounding errors) to setting the
+ * smallest (R,G,B) component to 0, the largest to <ssat>,
+ * and interpolating the "middle" component based on its
+ * original value relative to the smallest/largest.
+ */
+ f->emit(if_tree(greater(sbase, imm1(0)),
+ assign(color, div(mul(sub(cbase, minbase), ssat), sbase)),
+ assign(color, imm3(0))));
+ set_lum(f, color, color, clum);
+}
+
+ir_visitor_status
+lower_blend_visitor::visit_enter(ir_assignment *ir)
+{
+ ir_variable *lhs_var = ir->lhs->variable_referenced();
+ if (lhs_var->data.mode == ir_var_shader_out) {
+ lower(ir);
+ }
+
+ return visit_continue_with_parent;
+}
+
+ir_rvalue *
+lower_blend_visitor::is_mode(enum gl_blend_support_qualifier q)
+{
+ return equal(mode, new(mem_ctx) ir_constant(unsigned(q)));
+}
+
+void
+lower_blend_visitor::lower(ir_assignment *ir)
+{
+ /* Skip whole array assignments to FS outputs. This should only
+ * happen for the multiple render target case, where advanced
+ * blending isn't allowed. However, if blending is disabled,
+ * MRT is supposed to work normally, so we leave it untouched.
+ */
+ if (ir->rhs->type->is_array())
+ return;
+
+ /* <mareko> Kayden: if the shader writes fewer components, the result is
+ * undefined I think
+ * <Kayden> undefined \o/
+ *
+ * Skip non-vec4 values.
+ */
+ if (ir->rhs->type != glsl_type::vec4_type)
+ return;
+
+ /* Save the RHS to a temporary so we can reference it multiple times */
+ ir_variable *src =
+ new(mem_ctx) ir_variable(glsl_type::vec4_type, "__blend_src",
+ ir_var_temporary);
+ ir->insert_before(src);
+ ir->insert_before(assign(src, ir->rhs));
+
+ ir_variable *result =
+ new(mem_ctx) ir_variable(glsl_type::vec4_type, "__blend_result",
+ ir_var_temporary);
+
+ ir->insert_before(result);
+
+ ir->rhs = new(mem_ctx) ir_dereference_variable(result);
+
+ /* If we're not doing advanced blending, just write the original value. */
+ ir_if *if_blending = new(mem_ctx) ir_if(is_mode(BLEND_NONE));
+ ir->insert_before(if_blending);
+ if_blending->then_instructions.push_tail(assign(result, src));
+
+ ir_factory f(&if_blending->else_instructions, mem_ctx);
+
+ /* (Rs', Gs', Bs') =
+ * (0, 0, 0), if As == 0
+ * (Rs/As, Gs/As, Bs/As), otherwise
+ */
+ ir_variable *src_rgb = f.make_temp(glsl_type::vec3_type, "__blend_src_rgb");
+ ir_variable *src_alpha = f.make_temp(glsl_type::float_type, "__blend_src_a");
+
+ /* (Rd', Gd', Bd') =
+ * (0, 0, 0), if Ad == 0
+ * (Rd/Ad, Gd/Ad, Bd/Ad), otherwise
+ */
+ ir_variable *dst_rgb = f.make_temp(glsl_type::vec3_type, "__blend_dst_rgb");
+ ir_variable *dst_alpha = f.make_temp(glsl_type::float_type, "__blend_dst_a");
+
+ f.emit(assign(dst_alpha, swizzle_w(fb)));
+ f.emit(if_tree(equal(dst_alpha, imm1(0)),
+ assign(dst_rgb, imm3(0)),
+ assign(dst_rgb, div(swizzle_xyz(fb), dst_alpha))));
+
+ f.emit(assign(src_alpha, swizzle_w(src)));
+ f.emit(if_tree(equal(src_alpha, imm1(0)),
+ assign(src_rgb, imm3(0)),
+ assign(src_rgb, div(swizzle_xyz(src), src_alpha))));
+
+ ir_variable *factor = f.make_temp(glsl_type::vec3_type, "__blend_factor");
+
+ ir_factory casefactory = f;
+
+ unsigned choices = sh->info.BlendSupport;
+ while (choices) {
+ enum gl_blend_support_qualifier q = (enum gl_blend_support_qualifier)
+ (1u << u_bit_scan(&choices));
+
+ ir_if *iff = new(mem_ctx) ir_if(is_mode(q));
+ casefactory.emit(iff);
+ casefactory.instructions = &iff->then_instructions;
+
+ ir_rvalue *val = NULL;
+
+ switch (q) {
+ case BLEND_MULTIPLY:
+ val = blend_multiply(src_rgb, dst_rgb);
+ break;
+ case BLEND_SCREEN:
+ val = blend_screen(src_rgb, dst_rgb);
+ break;
+ case BLEND_OVERLAY:
+ val = blend_overlay(src_rgb, dst_rgb);
+ break;
+ case BLEND_DARKEN:
+ val = blend_darken(src_rgb, dst_rgb);
+ break;
+ case BLEND_LIGHTEN:
+ val = blend_lighten(src_rgb, dst_rgb);
+ break;
+ case BLEND_COLORDODGE:
+ val = blend_colordodge(src_rgb, dst_rgb);
+ break;
+ case BLEND_COLORBURN:
+ val = blend_colorburn(src_rgb, dst_rgb);
+ break;
+ case BLEND_HARDLIGHT:
+ val = blend_hardlight(src_rgb, dst_rgb);
+ break;
+ case BLEND_SOFTLIGHT:
+ val = blend_softlight(src_rgb, dst_rgb);
+ break;
+ case BLEND_DIFFERENCE:
+ val = blend_difference(src_rgb, dst_rgb);
+ break;
+ case BLEND_EXCLUSION:
+ val = blend_exclusion(src_rgb, dst_rgb);
+ break;
+ case BLEND_HSL_HUE:
+ set_lum_sat(&casefactory, factor, src_rgb, dst_rgb, dst_rgb);
+ break;
+ case BLEND_HSL_SATURATION:
+ set_lum_sat(&casefactory, factor, dst_rgb, src_rgb, dst_rgb);
+ break;
+ case BLEND_HSL_COLOR:
+ set_lum(&casefactory, factor, src_rgb, dst_rgb);
+ break;
+ case BLEND_HSL_LUMINOSITY:
+ set_lum(&casefactory, factor, dst_rgb, src_rgb);
+ break;
+ case BLEND_NONE:
+ case BLEND_ALL:
+ unreachable("not real cases");
+ }
+
+ if (val)
+ casefactory.emit(assign(factor, val));
+
+ casefactory.instructions = &iff->else_instructions;
+ }
+
+ /* p0(As,Ad) = As*Ad
+ * p1(As,Ad) = As*(1-Ad)
+ * p2(As,Ad) = Ad*(1-As)
+ */
+ ir_variable *p0 = f.make_temp(glsl_type::float_type, "__blend_p0");
+ ir_variable *p1 = f.make_temp(glsl_type::float_type, "__blend_p1");
+ ir_variable *p2 = f.make_temp(glsl_type::float_type, "__blend_p2");
+
+ f.emit(assign(p0, mul(src_alpha, dst_alpha)));
+ f.emit(assign(p1, mul(src_alpha, sub(imm1(1), dst_alpha))));
+ f.emit(assign(p2, mul(dst_alpha, sub(imm1(1), src_alpha))));
+
+ /* R = f(Rs',Rd')*p0(As,Ad) + Y*Rs'*p1(As,Ad) + Z*Rd'*p2(As,Ad)
+ * G = f(Gs',Gd')*p0(As,Ad) + Y*Gs'*p1(As,Ad) + Z*Gd'*p2(As,Ad)
+ * B = f(Bs',Bd')*p0(As,Ad) + Y*Bs'*p1(As,Ad) + Z*Bd'*p2(As,Ad)
+ * A = X*p0(As,Ad) + Y*p1(As,Ad) + Z*p2(As,Ad)
+ *
+ * <X, Y, Z> is always <1, 1, 1>, so we can ignore it.
+ *
+ * In vector form, this is:
+ * RGB = factor * p0 + Cs * p1 + Cd * p2
+ * A = p0 + p1 + p2
+ */
+ f.emit(assign(result,
+ add(add(mul(factor, p0), mul(src_rgb, p1)), mul(dst_rgb, p2)),
+ WRITEMASK_XYZ));
+ f.emit(assign(result, add(add(p0, p1), p2), WRITEMASK_W));
+}
+
+bool
+lower_blend_equation_advanced(struct gl_linked_shader *sh)
+{
+ if (sh->info.BlendSupport == 0)
+ return false;
+
+ lower_blend_visitor v(sh);
+ return v.run();
+}
--
2.9.0
More information about the mesa-dev
mailing list