summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/RenderState.cpp
blob: 696574f109706dcb0d29125731c5a4fb75a400f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "VideoCommon/RenderState.h"

// If the framebuffer format has no alpha channel, it is assumed to
// ONE on blending. As the backends may emulate this framebuffer
// configuration with an alpha channel, we just drop all references
// to the destination alpha channel.
static BlendMode::BlendFactor RemoveDstAlphaUsage(BlendMode::BlendFactor factor)
{
  switch (factor)
  {
  case BlendMode::DSTALPHA:
    return BlendMode::ONE;
  case BlendMode::INVDSTALPHA:
    return BlendMode::ZERO;
  default:
    return factor;
  }
}

// We separate the blending parameter for rgb and alpha. For blending
// the alpha component, CLR and ALPHA are indentical. So just always
// use ALPHA as this makes it easier for the backends to use the second
// alpha value of dual source blending.
static BlendMode::BlendFactor RemoveSrcColorUsage(BlendMode::BlendFactor factor)
{
  switch (factor)
  {
  case BlendMode::SRCCLR:
    return BlendMode::SRCALPHA;
  case BlendMode::INVSRCCLR:
    return BlendMode::INVSRCALPHA;
  default:
    return factor;
  }
}

// Same as RemoveSrcColorUsage, but because of the overlapping enum,
// this must be written as another function.
static BlendMode::BlendFactor RemoveDstColorUsage(BlendMode::BlendFactor factor)
{
  switch (factor)
  {
  case BlendMode::DSTCLR:
    return BlendMode::DSTALPHA;
  case BlendMode::INVDSTCLR:
    return BlendMode::INVDSTALPHA;
  default:
    return factor;
  }
}

void BlendingState::Generate(const BPMemory& bp)
{
  // Start with everything disabled.
  hex = 0;

  bool target_has_alpha = bp.zcontrol.pixel_format == PEControl::RGBA6_Z24;
  bool alpha_test_may_success = bp.alpha_test.TestResult() != AlphaTest::FAIL;

  colorupdate = bp.blendmode.colorupdate && alpha_test_may_success;
  alphaupdate = bp.blendmode.alphaupdate && target_has_alpha && alpha_test_may_success;
  dstalpha = bp.dstalpha.enable && alphaupdate;
  usedualsrc = true;

  // The subtract bit has the highest priority
  if (bp.blendmode.subtract)
  {
    blendenable = true;
    subtractAlpha = subtract = true;
    srcfactoralpha = srcfactor = BlendMode::ONE;
    dstfactoralpha = dstfactor = BlendMode::ONE;

    if (dstalpha)
    {
      subtractAlpha = false;
      srcfactoralpha = BlendMode::ONE;
      dstfactoralpha = BlendMode::ZERO;
    }
  }

  // The blendenable bit has the middle priority
  else if (bp.blendmode.blendenable)
  {
    blendenable = true;
    srcfactor = bp.blendmode.srcfactor;
    dstfactor = bp.blendmode.dstfactor;
    if (!target_has_alpha)
    {
      // uses ONE instead of DSTALPHA
      srcfactor = RemoveDstAlphaUsage(srcfactor);
      dstfactor = RemoveDstAlphaUsage(dstfactor);
    }
    // replaces SRCCLR with SRCALPHA
    srcfactoralpha = RemoveSrcColorUsage(srcfactor);
    dstfactoralpha = RemoveDstColorUsage(dstfactor);

    if (dstalpha)
    {
      srcfactoralpha = BlendMode::ONE;
      dstfactoralpha = BlendMode::ZERO;
    }
  }

  // The logicop bit has the lowest priority
  else if (bp.blendmode.logicopenable)
  {
    if (bp.blendmode.logicmode == BlendMode::NOOP)
    {
      // Fast path for Kirby's Return to Dreamland, they use it with dstAlpha.
      colorupdate = false;
      alphaupdate = alphaupdate && dstalpha;
    }
    else
    {
      logicopenable = true;
      logicmode = bp.blendmode.logicmode;

      if (dstalpha)
      {
        // TODO: Not supported by backends.
      }
    }
  }
}