// @(#)root/gl:$Id$
// Author: Matevz Tadel, Aug 2009

/*************************************************************************
 * Copyright (C) 1995-2007, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TGLFBO.h"
#include <TMath.h>
#include <TString.h>
#include <TError.h>

#include <GL/glew.h>

#include <stdexcept>

//______________________________________________________________________________
//
// Frame-buffer object.
//
// Requires GL-1.5.
//
// Taken from Gled project, see:
//   http://www.gled.org/cgi-bin/viewcvs.cgi/trunk/libsets/GledCore/Pupils/
// See also:
//   http://www.opengl.org/registry/specs/EXT/framebuffer_object.txt

ClassImp(TGLFBO);

Bool_t TGLFBO::fgRescaleToPow2       = kTRUE; // For ATI.
Bool_t TGLFBO::fgMultiSampleNAWarned = kFALSE;

//______________________________________________________________________________
TGLFBO::TGLFBO() :
   fFrameBuffer  (0),
   fColorTexture (0),
   fDepthBuffer  (0),
   fMSFrameBuffer(0),
   fMSColorBuffer(0),
   fW (-1),
   fH (-1),
   fReqW (-1),
   fReqH (-1),
   fMSSamples  (0),
   fMSCoverageSamples (0),
   fWScale     (1),
   fHScale     (1),
   fIsRescaled (kFALSE)
{
   // Constructor.
}

//______________________________________________________________________________
TGLFBO::~TGLFBO()
{
   // Destructor.

   Release();
}

//______________________________________________________________________________
void TGLFBO::Init(int w, int h, int ms_samples)
{
   // Acquire GL resources for given width, height and number of
   // multi-sampling samples.

   static const std::string eh("TGLFBO::Init ");

   // Should be replaced with ARB_framebuffer_object (SLC6).
   if (!GLEW_EXT_framebuffer_object)
   {
      throw std::runtime_error(eh + "GL_EXT_framebuffer_object extension required for FBO.");
   }

   fReqW = w; fReqH = h;

   fIsRescaled = kFALSE;
   if (fgRescaleToPow2)
   {
      Int_t nw = 1 << TMath::CeilNint(TMath::Log2(w));
      Int_t nh = 1 << TMath::CeilNint(TMath::Log2(h));
      if (nw != w || nh != h)
      {
         fWScale = ((Float_t)w) / nw;
         fHScale = ((Float_t)h) / nh;
         w = nw; h = nh;
         fIsRescaled = kTRUE;
      }
   }

   if (ms_samples > 0 && ! GLEW_EXT_framebuffer_multisample)
   {
      if (!fgMultiSampleNAWarned)
      {
         Info(eh.c_str(), "GL implementation does not support multi-sampling for FBOs.");
         fgMultiSampleNAWarned = kTRUE;
      }
      ms_samples = 0;
   }

   if (fFrameBuffer != 0)
   {
      if (fW == w && fH == h && fMSSamples == ms_samples)
         return;
      Release();
   }

   Int_t maxSize;
   glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &maxSize);
   if (w > maxSize || h > maxSize)
   {
      throw std::runtime_error(eh + Form("maximum size supported by GL implementation is %d.", maxSize));
   }

   fW = w; fH = h; fMSSamples = ms_samples;

   if (fMSSamples > 0)
   {
      if (GLEW_NV_framebuffer_multisample_coverage)
      {
         GLint n_modes;
         glGetIntegerv(GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV, &n_modes);
         GLint *modes = new GLint[2*n_modes];
         glGetIntegerv(GL_MULTISAMPLE_COVERAGE_MODES_NV, modes);

         for (int i = 0; i < n_modes; ++i)
         {
            if (modes[i*2+1] == fMSSamples && modes[i*2] > fMSCoverageSamples)
               fMSCoverageSamples = modes[i*2];
         }

         delete [] modes;
      }
      if (gDebug > 0) {
         Info(eh.c_str(), "InitMultiSample coverage_samples=%d, color_samples=%d.", fMSCoverageSamples, fMSSamples);
      }
      InitMultiSample();
   }
   else
   {
      if (gDebug > 0) {
         Info(eh.c_str(), "InitStandard (no multi-sampling).");
      }
      InitStandard();
   }

   GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);

   glBindFramebufferEXT (GL_FRAMEBUFFER_EXT,  0);
   glBindTexture        (GL_TEXTURE_2D,       0);

   switch (status)
   {
      case GL_FRAMEBUFFER_COMPLETE_EXT:
         if (gDebug > 0)
            printf("%sConstructed TGLFBO ... all fine.\n", eh.c_str());
         break;
      case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
         Release();
         throw std::runtime_error(eh + "Constructed TGLFBO not supported, choose different formats.");
         break;
      default:
         Release();
         throw std::runtime_error(eh + "Constructed TGLFBO is not complete, unexpected error.");
         break;
   }
}

//______________________________________________________________________________
void TGLFBO::Release()
{
   // Release the allocated GL resources.

   glDeleteFramebuffersEXT (1, &fFrameBuffer);
   glDeleteRenderbuffersEXT(1, &fDepthBuffer);

   if (fMSFrameBuffer) glDeleteFramebuffersEXT (1, &fMSFrameBuffer);
   if (fMSColorBuffer) glDeleteRenderbuffersEXT(1, &fMSColorBuffer);
   if (fColorTexture)  glDeleteTextures        (1, &fColorTexture);

   fW = fH = -1; fMSSamples = fMSCoverageSamples = 0;
   fFrameBuffer = fColorTexture = fDepthBuffer = fMSFrameBuffer = fMSColorBuffer = 0;

}

//______________________________________________________________________________
void TGLFBO::Bind()
{
   // Bind the frame-buffer object.

   if (fMSSamples > 0) {
      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fMSFrameBuffer);
      // On by default
      //   glEnable(GL_MULTISAMPLE);
      // Experimenting:
      //   glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
      //   glEnable(GL_SAMPLE_COVERAGE_ARB);
   } else {
      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFrameBuffer);
   }
}

//______________________________________________________________________________
void TGLFBO::Unbind()
{
   // Unbind the frame-buffer object.

   if (fMSSamples > 0)
   {
      glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, fMSFrameBuffer);
      glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fFrameBuffer);
      glBlitFramebufferEXT(0, 0, fW, fH, 0, 0, fW, fH, GL_COLOR_BUFFER_BIT, GL_NEAREST);
   }

   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

//______________________________________________________________________________
void TGLFBO::BindTexture()
{
   // Bind texture.

   glPushAttrib(GL_TEXTURE_BIT);
   glBindTexture(GL_TEXTURE_2D, fColorTexture);
   glEnable(GL_TEXTURE_2D);

   if (fIsRescaled)
   {
      glMatrixMode(GL_TEXTURE);
      glPushMatrix();
      glScalef(fWScale, fHScale, 1);
      glMatrixMode(GL_MODELVIEW);
   }
}

//______________________________________________________________________________
void TGLFBO::UnbindTexture()
{
   // Unbind texture.

   if (fIsRescaled)
   {
      glMatrixMode(GL_TEXTURE);
      glPopMatrix();
      glMatrixMode(GL_MODELVIEW);
   }

   glPopAttrib();
}

//______________________________________________________________________________
void TGLFBO::SetAsReadBuffer()
{
   glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, fFrameBuffer);
}

//==============================================================================

//______________________________________________________________________________
void TGLFBO::InitStandard()
{
   glGenFramebuffersEXT(1, &fFrameBuffer);
   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFrameBuffer);

   fDepthBuffer  = CreateAndAttachRenderBuffer(GL_DEPTH_COMPONENT24, GL_DEPTH_ATTACHMENT);
   fColorTexture = CreateAndAttachColorTexture();
}

//______________________________________________________________________________
void TGLFBO::InitMultiSample()
{
   glGenFramebuffersEXT(1, &fMSFrameBuffer);
   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fMSFrameBuffer);

   fMSColorBuffer = CreateAndAttachRenderBuffer(GL_RGBA8,             GL_COLOR_ATTACHMENT0);
   fDepthBuffer   = CreateAndAttachRenderBuffer(GL_DEPTH_COMPONENT24, GL_DEPTH_ATTACHMENT);
   // fDepthBuffer   = CreateAndAttachRenderBuffer(GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL_ATTACHMENT);

   glGenFramebuffersEXT(1, &fFrameBuffer);
   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFrameBuffer);

   fColorTexture = CreateAndAttachColorTexture();
}

//______________________________________________________________________________
UInt_t TGLFBO::CreateAndAttachRenderBuffer(Int_t format, Int_t type)
{
   UInt_t id = 0;

   glGenRenderbuffersEXT(1, &id);
   glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id);

   if (fMSSamples > 0)
   {
      if (fMSCoverageSamples > 0)
         glRenderbufferStorageMultisampleCoverageNV(GL_RENDERBUFFER_EXT, fMSCoverageSamples, fMSSamples, format, fW, fH);
      else
         glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, fMSSamples, format, fW, fH);
   }
   else
   {
      glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, fW, fH);
   }

   glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, type, GL_RENDERBUFFER_EXT, id);

   return id;
}

//______________________________________________________________________________
UInt_t TGLFBO::CreateAndAttachColorTexture()
{
   // Initialize color-texture and attach it to current FB.

   UInt_t id = 0;

   glGenTextures(1, &id);

   glBindTexture(GL_TEXTURE_2D, id);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, fW, fH, 0, GL_RGBA,
                GL_UNSIGNED_BYTE, NULL);

   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
                             GL_TEXTURE_2D, id, 0);

   return id;
}

//______________________________________________________________________________
Bool_t TGLFBO::GetRescaleToPow2()
{
   // Return state of fgRescaleToPow2 static member.

   return fgRescaleToPow2;
}

//______________________________________________________________________________
void TGLFBO::SetRescaleToPow2(Bool_t r)
{
   // Set state of fgRescaleToPow2 static member.
   // Default is kTRUE as this works better on older hardware, especially ATI.

   fgRescaleToPow2 = r;
}
 TGLFBO.cxx:1
 TGLFBO.cxx:2
 TGLFBO.cxx:3
 TGLFBO.cxx:4
 TGLFBO.cxx:5
 TGLFBO.cxx:6
 TGLFBO.cxx:7
 TGLFBO.cxx:8
 TGLFBO.cxx:9
 TGLFBO.cxx:10
 TGLFBO.cxx:11
 TGLFBO.cxx:12
 TGLFBO.cxx:13
 TGLFBO.cxx:14
 TGLFBO.cxx:15
 TGLFBO.cxx:16
 TGLFBO.cxx:17
 TGLFBO.cxx:18
 TGLFBO.cxx:19
 TGLFBO.cxx:20
 TGLFBO.cxx:21
 TGLFBO.cxx:22
 TGLFBO.cxx:23
 TGLFBO.cxx:24
 TGLFBO.cxx:25
 TGLFBO.cxx:26
 TGLFBO.cxx:27
 TGLFBO.cxx:28
 TGLFBO.cxx:29
 TGLFBO.cxx:30
 TGLFBO.cxx:31
 TGLFBO.cxx:32
 TGLFBO.cxx:33
 TGLFBO.cxx:34
 TGLFBO.cxx:35
 TGLFBO.cxx:36
 TGLFBO.cxx:37
 TGLFBO.cxx:38
 TGLFBO.cxx:39
 TGLFBO.cxx:40
 TGLFBO.cxx:41
 TGLFBO.cxx:42
 TGLFBO.cxx:43
 TGLFBO.cxx:44
 TGLFBO.cxx:45
 TGLFBO.cxx:46
 TGLFBO.cxx:47
 TGLFBO.cxx:48
 TGLFBO.cxx:49
 TGLFBO.cxx:50
 TGLFBO.cxx:51
 TGLFBO.cxx:52
 TGLFBO.cxx:53
 TGLFBO.cxx:54
 TGLFBO.cxx:55
 TGLFBO.cxx:56
 TGLFBO.cxx:57
 TGLFBO.cxx:58
 TGLFBO.cxx:59
 TGLFBO.cxx:60
 TGLFBO.cxx:61
 TGLFBO.cxx:62
 TGLFBO.cxx:63
 TGLFBO.cxx:64
 TGLFBO.cxx:65
 TGLFBO.cxx:66
 TGLFBO.cxx:67
 TGLFBO.cxx:68
 TGLFBO.cxx:69
 TGLFBO.cxx:70
 TGLFBO.cxx:71
 TGLFBO.cxx:72
 TGLFBO.cxx:73
 TGLFBO.cxx:74
 TGLFBO.cxx:75
 TGLFBO.cxx:76
 TGLFBO.cxx:77
 TGLFBO.cxx:78
 TGLFBO.cxx:79
 TGLFBO.cxx:80
 TGLFBO.cxx:81
 TGLFBO.cxx:82
 TGLFBO.cxx:83
 TGLFBO.cxx:84
 TGLFBO.cxx:85
 TGLFBO.cxx:86
 TGLFBO.cxx:87
 TGLFBO.cxx:88
 TGLFBO.cxx:89
 TGLFBO.cxx:90
 TGLFBO.cxx:91
 TGLFBO.cxx:92
 TGLFBO.cxx:93
 TGLFBO.cxx:94
 TGLFBO.cxx:95
 TGLFBO.cxx:96
 TGLFBO.cxx:97
 TGLFBO.cxx:98
 TGLFBO.cxx:99
 TGLFBO.cxx:100
 TGLFBO.cxx:101
 TGLFBO.cxx:102
 TGLFBO.cxx:103
 TGLFBO.cxx:104
 TGLFBO.cxx:105
 TGLFBO.cxx:106
 TGLFBO.cxx:107
 TGLFBO.cxx:108
 TGLFBO.cxx:109
 TGLFBO.cxx:110
 TGLFBO.cxx:111
 TGLFBO.cxx:112
 TGLFBO.cxx:113
 TGLFBO.cxx:114
 TGLFBO.cxx:115
 TGLFBO.cxx:116
 TGLFBO.cxx:117
 TGLFBO.cxx:118
 TGLFBO.cxx:119
 TGLFBO.cxx:120
 TGLFBO.cxx:121
 TGLFBO.cxx:122
 TGLFBO.cxx:123
 TGLFBO.cxx:124
 TGLFBO.cxx:125
 TGLFBO.cxx:126
 TGLFBO.cxx:127
 TGLFBO.cxx:128
 TGLFBO.cxx:129
 TGLFBO.cxx:130
 TGLFBO.cxx:131
 TGLFBO.cxx:132
 TGLFBO.cxx:133
 TGLFBO.cxx:134
 TGLFBO.cxx:135
 TGLFBO.cxx:136
 TGLFBO.cxx:137
 TGLFBO.cxx:138
 TGLFBO.cxx:139
 TGLFBO.cxx:140
 TGLFBO.cxx:141
 TGLFBO.cxx:142
 TGLFBO.cxx:143
 TGLFBO.cxx:144
 TGLFBO.cxx:145
 TGLFBO.cxx:146
 TGLFBO.cxx:147
 TGLFBO.cxx:148
 TGLFBO.cxx:149
 TGLFBO.cxx:150
 TGLFBO.cxx:151
 TGLFBO.cxx:152
 TGLFBO.cxx:153
 TGLFBO.cxx:154
 TGLFBO.cxx:155
 TGLFBO.cxx:156
 TGLFBO.cxx:157
 TGLFBO.cxx:158
 TGLFBO.cxx:159
 TGLFBO.cxx:160
 TGLFBO.cxx:161
 TGLFBO.cxx:162
 TGLFBO.cxx:163
 TGLFBO.cxx:164
 TGLFBO.cxx:165
 TGLFBO.cxx:166
 TGLFBO.cxx:167
 TGLFBO.cxx:168
 TGLFBO.cxx:169
 TGLFBO.cxx:170
 TGLFBO.cxx:171
 TGLFBO.cxx:172
 TGLFBO.cxx:173
 TGLFBO.cxx:174
 TGLFBO.cxx:175
 TGLFBO.cxx:176
 TGLFBO.cxx:177
 TGLFBO.cxx:178
 TGLFBO.cxx:179
 TGLFBO.cxx:180
 TGLFBO.cxx:181
 TGLFBO.cxx:182
 TGLFBO.cxx:183
 TGLFBO.cxx:184
 TGLFBO.cxx:185
 TGLFBO.cxx:186
 TGLFBO.cxx:187
 TGLFBO.cxx:188
 TGLFBO.cxx:189
 TGLFBO.cxx:190
 TGLFBO.cxx:191
 TGLFBO.cxx:192
 TGLFBO.cxx:193
 TGLFBO.cxx:194
 TGLFBO.cxx:195
 TGLFBO.cxx:196
 TGLFBO.cxx:197
 TGLFBO.cxx:198
 TGLFBO.cxx:199
 TGLFBO.cxx:200
 TGLFBO.cxx:201
 TGLFBO.cxx:202
 TGLFBO.cxx:203
 TGLFBO.cxx:204
 TGLFBO.cxx:205
 TGLFBO.cxx:206
 TGLFBO.cxx:207
 TGLFBO.cxx:208
 TGLFBO.cxx:209
 TGLFBO.cxx:210
 TGLFBO.cxx:211
 TGLFBO.cxx:212
 TGLFBO.cxx:213
 TGLFBO.cxx:214
 TGLFBO.cxx:215
 TGLFBO.cxx:216
 TGLFBO.cxx:217
 TGLFBO.cxx:218
 TGLFBO.cxx:219
 TGLFBO.cxx:220
 TGLFBO.cxx:221
 TGLFBO.cxx:222
 TGLFBO.cxx:223
 TGLFBO.cxx:224
 TGLFBO.cxx:225
 TGLFBO.cxx:226
 TGLFBO.cxx:227
 TGLFBO.cxx:228
 TGLFBO.cxx:229
 TGLFBO.cxx:230
 TGLFBO.cxx:231
 TGLFBO.cxx:232
 TGLFBO.cxx:233
 TGLFBO.cxx:234
 TGLFBO.cxx:235
 TGLFBO.cxx:236
 TGLFBO.cxx:237
 TGLFBO.cxx:238
 TGLFBO.cxx:239
 TGLFBO.cxx:240
 TGLFBO.cxx:241
 TGLFBO.cxx:242
 TGLFBO.cxx:243
 TGLFBO.cxx:244
 TGLFBO.cxx:245
 TGLFBO.cxx:246
 TGLFBO.cxx:247
 TGLFBO.cxx:248
 TGLFBO.cxx:249
 TGLFBO.cxx:250
 TGLFBO.cxx:251
 TGLFBO.cxx:252
 TGLFBO.cxx:253
 TGLFBO.cxx:254
 TGLFBO.cxx:255
 TGLFBO.cxx:256
 TGLFBO.cxx:257
 TGLFBO.cxx:258
 TGLFBO.cxx:259
 TGLFBO.cxx:260
 TGLFBO.cxx:261
 TGLFBO.cxx:262
 TGLFBO.cxx:263
 TGLFBO.cxx:264
 TGLFBO.cxx:265
 TGLFBO.cxx:266
 TGLFBO.cxx:267
 TGLFBO.cxx:268
 TGLFBO.cxx:269
 TGLFBO.cxx:270
 TGLFBO.cxx:271
 TGLFBO.cxx:272
 TGLFBO.cxx:273
 TGLFBO.cxx:274
 TGLFBO.cxx:275
 TGLFBO.cxx:276
 TGLFBO.cxx:277
 TGLFBO.cxx:278
 TGLFBO.cxx:279
 TGLFBO.cxx:280
 TGLFBO.cxx:281
 TGLFBO.cxx:282
 TGLFBO.cxx:283
 TGLFBO.cxx:284
 TGLFBO.cxx:285
 TGLFBO.cxx:286
 TGLFBO.cxx:287
 TGLFBO.cxx:288
 TGLFBO.cxx:289
 TGLFBO.cxx:290
 TGLFBO.cxx:291
 TGLFBO.cxx:292
 TGLFBO.cxx:293
 TGLFBO.cxx:294
 TGLFBO.cxx:295
 TGLFBO.cxx:296
 TGLFBO.cxx:297
 TGLFBO.cxx:298
 TGLFBO.cxx:299
 TGLFBO.cxx:300
 TGLFBO.cxx:301
 TGLFBO.cxx:302
 TGLFBO.cxx:303
 TGLFBO.cxx:304
 TGLFBO.cxx:305
 TGLFBO.cxx:306
 TGLFBO.cxx:307
 TGLFBO.cxx:308
 TGLFBO.cxx:309
 TGLFBO.cxx:310
 TGLFBO.cxx:311
 TGLFBO.cxx:312
 TGLFBO.cxx:313
 TGLFBO.cxx:314
 TGLFBO.cxx:315
 TGLFBO.cxx:316
 TGLFBO.cxx:317
 TGLFBO.cxx:318
 TGLFBO.cxx:319
 TGLFBO.cxx:320
 TGLFBO.cxx:321
 TGLFBO.cxx:322
 TGLFBO.cxx:323
 TGLFBO.cxx:324
 TGLFBO.cxx:325
 TGLFBO.cxx:326
 TGLFBO.cxx:327
 TGLFBO.cxx:328
 TGLFBO.cxx:329
 TGLFBO.cxx:330
 TGLFBO.cxx:331
 TGLFBO.cxx:332
 TGLFBO.cxx:333
 TGLFBO.cxx:334
 TGLFBO.cxx:335
 TGLFBO.cxx:336
 TGLFBO.cxx:337
 TGLFBO.cxx:338
 TGLFBO.cxx:339
 TGLFBO.cxx:340
 TGLFBO.cxx:341
 TGLFBO.cxx:342
 TGLFBO.cxx:343
 TGLFBO.cxx:344
 TGLFBO.cxx:345
 TGLFBO.cxx:346
 TGLFBO.cxx:347
 TGLFBO.cxx:348
 TGLFBO.cxx:349
 TGLFBO.cxx:350
 TGLFBO.cxx:351
 TGLFBO.cxx:352