/ src / Ryujinx.Graphics.OpenGL / Framebuffer.cs
Framebuffer.cs
  1  using OpenTK.Graphics.OpenGL;
  2  using Ryujinx.Graphics.GAL;
  3  using Ryujinx.Graphics.OpenGL.Image;
  4  using System;
  5  using System.Runtime.CompilerServices;
  6  
  7  namespace Ryujinx.Graphics.OpenGL
  8  {
  9      class Framebuffer : IDisposable
 10      {
 11          public int Handle { get; private set; }
 12          private int _clearFbHandle;
 13          private bool _clearFbInitialized;
 14  
 15          private FramebufferAttachment _lastDsAttachment;
 16  
 17          private readonly TextureView[] _colors;
 18          private TextureView _depthStencil;
 19  
 20          private int _colorsCount;
 21          private bool _dualSourceBlend;
 22  
 23          public Framebuffer()
 24          {
 25              Handle = GL.GenFramebuffer();
 26              _clearFbHandle = GL.GenFramebuffer();
 27  
 28              _colors = new TextureView[8];
 29          }
 30  
 31          public int Bind()
 32          {
 33              GL.BindFramebuffer(FramebufferTarget.Framebuffer, Handle);
 34              return Handle;
 35          }
 36  
 37          [MethodImpl(MethodImplOptions.AggressiveInlining)]
 38          public void AttachColor(int index, TextureView color)
 39          {
 40              if (_colors[index] == color)
 41              {
 42                  return;
 43              }
 44  
 45              FramebufferAttachment attachment = FramebufferAttachment.ColorAttachment0 + index;
 46  
 47              GL.FramebufferTexture(FramebufferTarget.Framebuffer, attachment, color?.Handle ?? 0, 0);
 48  
 49              _colors[index] = color;
 50          }
 51  
 52          public void AttachDepthStencil(TextureView depthStencil)
 53          {
 54              // Detach the last depth/stencil buffer if there is any.
 55              if (_lastDsAttachment != 0)
 56              {
 57                  GL.FramebufferTexture(FramebufferTarget.Framebuffer, _lastDsAttachment, 0, 0);
 58              }
 59  
 60              if (depthStencil != null)
 61              {
 62                  FramebufferAttachment attachment = GetAttachment(depthStencil.Format);
 63  
 64                  GL.FramebufferTexture(
 65                      FramebufferTarget.Framebuffer,
 66                      attachment,
 67                      depthStencil.Handle,
 68                      0);
 69  
 70                  _lastDsAttachment = attachment;
 71              }
 72              else
 73              {
 74                  _lastDsAttachment = 0;
 75              }
 76  
 77              _depthStencil = depthStencil;
 78          }
 79  
 80          public void SetDualSourceBlend(bool enable)
 81          {
 82              bool oldEnable = _dualSourceBlend;
 83  
 84              _dualSourceBlend = enable;
 85  
 86              // When dual source blend is used,
 87              // we can only have one draw buffer.
 88              if (enable)
 89              {
 90                  GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
 91              }
 92              else if (oldEnable)
 93              {
 94                  SetDrawBuffersImpl(_colorsCount);
 95              }
 96          }
 97  
 98          public void SetDrawBuffers(int colorsCount)
 99          {
100              if (_colorsCount != colorsCount && !_dualSourceBlend)
101              {
102                  SetDrawBuffersImpl(colorsCount);
103              }
104  
105              _colorsCount = colorsCount;
106          }
107  
108          private static void SetDrawBuffersImpl(int colorsCount)
109          {
110              DrawBuffersEnum[] drawBuffers = new DrawBuffersEnum[colorsCount];
111  
112              for (int index = 0; index < colorsCount; index++)
113              {
114                  drawBuffers[index] = DrawBuffersEnum.ColorAttachment0 + index;
115              }
116  
117              GL.DrawBuffers(colorsCount, drawBuffers);
118          }
119  
120          private static FramebufferAttachment GetAttachment(Format format)
121          {
122              if (FormatTable.IsPackedDepthStencil(format))
123              {
124                  return FramebufferAttachment.DepthStencilAttachment;
125              }
126              else if (FormatTable.IsDepthOnly(format))
127              {
128                  return FramebufferAttachment.DepthAttachment;
129              }
130              else
131              {
132                  return FramebufferAttachment.StencilAttachment;
133              }
134          }
135  
136          public int GetColorLayerCount(int index)
137          {
138              return _colors[index]?.Info.GetDepthOrLayers() ?? 0;
139          }
140  
141          public int GetDepthStencilLayerCount()
142          {
143              return _depthStencil?.Info.GetDepthOrLayers() ?? 0;
144          }
145  
146          public void AttachColorLayerForClear(int index, int layer)
147          {
148              TextureView color = _colors[index];
149  
150              if (!IsLayered(color))
151              {
152                  return;
153              }
154  
155              BindClearFb();
156              GL.FramebufferTextureLayer(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0 + index, color.Handle, 0, layer);
157          }
158  
159          public void DetachColorLayerForClear(int index)
160          {
161              TextureView color = _colors[index];
162  
163              if (!IsLayered(color))
164              {
165                  return;
166              }
167  
168              GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0 + index, 0, 0);
169              Bind();
170          }
171  
172          public void AttachDepthStencilLayerForClear(int layer)
173          {
174              TextureView depthStencil = _depthStencil;
175  
176              if (!IsLayered(depthStencil))
177              {
178                  return;
179              }
180  
181              BindClearFb();
182              GL.FramebufferTextureLayer(FramebufferTarget.Framebuffer, GetAttachment(depthStencil.Format), depthStencil.Handle, 0, layer);
183          }
184  
185          public void DetachDepthStencilLayerForClear()
186          {
187              TextureView depthStencil = _depthStencil;
188  
189              if (!IsLayered(depthStencil))
190              {
191                  return;
192              }
193  
194              GL.FramebufferTexture(FramebufferTarget.Framebuffer, GetAttachment(depthStencil.Format), 0, 0);
195              Bind();
196          }
197  
198          private void BindClearFb()
199          {
200              GL.BindFramebuffer(FramebufferTarget.Framebuffer, _clearFbHandle);
201  
202              if (!_clearFbInitialized)
203              {
204                  SetDrawBuffersImpl(Constants.MaxRenderTargets);
205                  _clearFbInitialized = true;
206              }
207          }
208  
209          private static bool IsLayered(TextureView view)
210          {
211              return view != null &&
212                     view.Target != Target.Texture1D &&
213                     view.Target != Target.Texture2D &&
214                     view.Target != Target.Texture2DMultisample &&
215                     view.Target != Target.TextureBuffer;
216          }
217  
218          public void Dispose()
219          {
220              if (Handle != 0)
221              {
222                  GL.DeleteFramebuffer(Handle);
223  
224                  Handle = 0;
225              }
226  
227              if (_clearFbHandle != 0)
228              {
229                  GL.DeleteFramebuffer(_clearFbHandle);
230  
231                  _clearFbHandle = 0;
232              }
233          }
234      }
235  }