#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"
#include "builtin_functions.h"
#include "main/mtypes.h"
using namespace ir_builder;
#define imm1(x) new(mem_ctx) ir_constant((float) (x), 1)
#define imm3(x) new(mem_ctx) ir_constant((float) (x), 3)
static ir_rvalue *
blend_multiply(ir_variable *src, ir_variable *dst)
{
return mul(src, dst);
}
static ir_rvalue *
blend_screen(ir_variable *src, ir_variable *dst)
{
return sub(add(src, dst), mul(src, dst));
}
static ir_rvalue *
blend_overlay(ir_variable *src, ir_variable *dst)
{
void *mem_ctx = ralloc_parent(src);
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);
}
static ir_rvalue *
blend_darken(ir_variable *src, ir_variable *dst)
{
return min2(src, dst);
}
static ir_rvalue *
blend_lighten(ir_variable *src, ir_variable *dst)
{
return max2(src, dst);
}
static ir_rvalue *
blend_colordodge(ir_variable *src, ir_variable *dst)
{
void *mem_ctx = ralloc_parent(src);
return csel(lequal(dst, imm3(0)), imm3(0),
csel(gequal(src, imm3(1)), imm3(1),
min2(imm3(1), div(dst, sub(imm3(1), src)))));
}
static ir_rvalue *
blend_colorburn(ir_variable *src, ir_variable *dst)
{
void *mem_ctx = ralloc_parent(src);
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)))));
}
static ir_rvalue *
blend_hardlight(ir_variable *src, ir_variable *dst)
{
void *mem_ctx = ralloc_parent(src);
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);
}
static ir_rvalue *
blend_softlight(ir_variable *src, ir_variable *dst)
{
void *mem_ctx = ralloc_parent(src);
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));
}
static ir_rvalue *
blend_difference(ir_variable *src, ir_variable *dst)
{
return abs(sub(dst, src));
}
static ir_rvalue *
blend_exclusion(ir_variable *src, ir_variable *dst)
{
void *mem_ctx = ralloc_parent(src);
return add(src, sub(dst, mul(imm3(2), mul(src, dst))));
}
static ir_rvalue *
minv3(ir_variable *v)
{
return min2(min2(swizzle_x(v), swizzle_y(v)), swizzle_z(v));
}
static ir_rvalue *
maxv3(ir_variable *v)
{
return max2(max2(swizzle_x(v), swizzle_y(v)), swizzle_z(v));
}
static 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);
return dot(c, new(mem_ctx) ir_constant(glsl_type::vec3_type, &data));
}
static ir_rvalue *
satv3(ir_variable *c)
{
return sub(maxv3(c), minv3(c));
}
static void
set_lum(ir_factory *f,
ir_variable *color,
ir_variable *cbase,
ir_variable *clum)
{
void *mem_ctx = f->mem_ctx;
f->emit(assign(color, add(cbase, sub(lumv3(clum), lumv3(cbase)))));
ir_variable *llum = 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");
f->emit(assign(llum, lumv3(color)));
f->emit(assign(mincol, minv3(color)));
f->emit(assign(maxcol, maxv3(color)));
f->emit(if_tree(less(mincol, imm1(0)),
assign(color, add(llum, div(mul(sub(color, llum), llum),
sub(llum, mincol)))),
if_tree(greater(maxcol, imm1(1)),
assign(color, add(llum, div(mul(sub(color, llum),
sub(imm3(1), llum)),
sub(maxcol, llum)))))));
}
static void
set_lum_sat(ir_factory *f,
ir_variable *color,
ir_variable *cbase,
ir_variable *csat,
ir_variable *clum)
{
void *mem_ctx = f->mem_ctx;
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)));
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);
}
static ir_rvalue *
is_mode(ir_variable *mode, enum gl_advanced_blend_mode q)
{
return equal(mode, new(ralloc_parent(mode)) ir_constant(unsigned(q)));
}
static ir_variable *
calc_blend_result(ir_factory f,
ir_variable *mode,
ir_variable *fb,
ir_rvalue *blend_src,
GLbitfield blend_qualifiers)
{
void *mem_ctx = f.mem_ctx;
ir_variable *result = f.make_temp(glsl_type::vec4_type, "__blend_result");
ir_variable *src = f.make_temp(glsl_type::vec4_type, "__blend_src");
f.emit(assign(src, blend_src));
ir_if *if_blending = new(mem_ctx) ir_if(is_mode(mode, BLEND_NONE));
f.emit(if_blending);
if_blending->then_instructions.push_tail(assign(result, src));
f.instructions = &if_blending->else_instructions;
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");
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, csel(equal(swizzle_xyz(fb),
swizzle(fb, SWIZZLE_WWWW, 3)),
imm3(1),
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, csel(equal(swizzle_xyz(src),
swizzle(src, SWIZZLE_WWWW, 3)),
imm3(1),
div(swizzle_xyz(src), src_alpha)))));
ir_variable *factor = f.make_temp(glsl_type::vec3_type, "__blend_factor");
ir_factory casefactory = f;
unsigned choices = blend_qualifiers;
while (choices) {
enum gl_advanced_blend_mode choice = (enum gl_advanced_blend_mode)
(1u << u_bit_scan(&choices));
ir_if *iff = new(mem_ctx) ir_if(is_mode(mode, choice));
casefactory.emit(iff);
casefactory.instructions = &iff->then_instructions;
ir_rvalue *val = NULL;
switch (choice) {
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;
}
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))));
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));
return result;
}
static ir_dereference *
deref_output(ir_variable *var)
{
void *mem_ctx = ralloc_parent(var);
ir_dereference *val = new(mem_ctx) ir_dereference_variable(var);
if (val->type->is_array()) {
ir_constant *index = new(mem_ctx) ir_constant(0);
val = new(mem_ctx) ir_dereference_array(val, index);
}
return val;
}
static ir_function_signature *
get_main(gl_linked_shader *sh)
{
ir_function_signature *sig = NULL;
foreach_in_list(ir_instruction, ir, sh->ir) {
ir_function *f = ir->as_function();
if (f && strcmp(f->name, "main") == 0) {
exec_list void_parameters;
sig = f->matching_signature(NULL, &void_parameters, false);
break;
}
}
assert(sig != NULL);
return sig;
}
bool
lower_blend_equation_advanced(struct gl_linked_shader *sh, bool coherent)
{
if (sh->Program->sh.fs.BlendSupport == 0)
return false;
do_lower_jumps(sh->ir, false, false, true, false, false);
void *mem_ctx = ralloc_parent(sh->ir);
ir_variable *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;
fb->data.memory_coherent = coherent;
fb->data.how_declared = ir_var_hidden;
ir_variable *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;
sh->ir->push_head(fb);
sh->ir->push_head(mode);
ir_variable *outputs[4] = { NULL, NULL, NULL, NULL };
foreach_in_list(ir_instruction, ir, sh->ir) {
ir_variable *var = ir->as_variable();
if (!var || var->data.mode != ir_var_shader_out)
continue;
if (var->data.location == FRAG_RESULT_DATA0 ||
var->data.location == FRAG_RESULT_COLOR) {
const int components = var->type->without_array()->vector_elements;
for (int i = 0; i < components; i++) {
outputs[var->data.location_frac + i] = var;
}
}
}
ir_rvalue *blend_source;
if (outputs[0] && outputs[0]->type->without_array()->vector_elements == 4) {
blend_source = deref_output(outputs[0]);
} else {
ir_rvalue *blend_comps[4];
for (int i = 0; i < 4; i++) {
ir_variable *var = outputs[i];
if (var) {
blend_comps[i] = swizzle(deref_output(outputs[i]),
i - outputs[i]->data.location_frac, 1);
} else {
blend_comps[i] = new(mem_ctx) ir_constant(i < 3 ? 0.0f : 1.0f);
}
}
blend_source =
new(mem_ctx) ir_expression(ir_quadop_vector, glsl_type::vec4_type,
blend_comps[0], blend_comps[1],
blend_comps[2], blend_comps[3]);
}
ir_function_signature *main = get_main(sh);
ir_factory f(&main->body, mem_ctx);
ir_variable *result_dest =
calc_blend_result(f, mode, fb, blend_source,
sh->Program->sh.fs.BlendSupport);
for (int i = 0; i < 4; i++) {
if (!outputs[i])
continue;
f.emit(assign(deref_output(outputs[i]), swizzle(result_dest, i, 1),
1 << i));
}
validate_ir_tree(sh->ir);
return true;
}