vtkOpenGLFXAAFilter.cxx 14 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkOpenGLFXAAFilter.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

#include "vtkOpenGLFXAAFilter.h"

#include "vtk_glew.h"

20
#include "vtkFXAAOptions.h"
21 22 23 24
#include "vtkObjectFactory.h"
#include "vtkOpenGLBufferObject.h"
#include "vtkOpenGLError.h"
#include "vtkOpenGLRenderer.h"
25
#include "vtkOpenGLRenderTimer.h"
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
#include "vtkOpenGLRenderUtilities.h"
#include "vtkOpenGLRenderWindow.h"
#include "vtkOpenGLShaderCache.h"
#include "vtkOpenGLVertexArrayObject.h"
#include "vtkShaderProgram.h"
#include "vtkTextureObject.h"
#include "vtkTimerLog.h"
#include "vtkTypeTraits.h"

#include <algorithm>
#include <cassert>

// Our fragment shader:
#include "vtkFXAAFilterFS.h"

// Define to perform/dump benchmarking info:
//#define FXAA_BENCHMARK

vtkStandardNewMacro(vtkOpenGLFXAAFilter)

//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::PrintSelf(std::ostream &os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

  os << indent << "RelativeContrastThreshold: "
     << this->RelativeContrastThreshold << "\n";
  os << indent << "HardContrastThreshold: "
     << this->HardContrastThreshold << "\n";
  os << indent << "SubpixelBlendLimit: " <<
        this->SubpixelBlendLimit << "\n";
  os << indent << "SubpixelContrastThreshold: "
     << this->SubpixelContrastThreshold << "\n";
  os << indent << "EndpointSearchIterations: "
     << this->EndpointSearchIterations << "\n";
  os << indent << "UseHighQualityEndpoints: "
     << this->UseHighQualityEndpoints << "\n";

  os << indent << "DebugOptionValue: ";
  switch (this->DebugOptionValue)
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
    default:
    case vtkFXAAOptions::FXAA_NO_DEBUG:
      os << "FXAA_NO_DEBUG\n";
      break;
    case vtkFXAAOptions::FXAA_DEBUG_SUBPIXEL_ALIASING:
      os << "FXAA_DEBUG_SUBPIXEL_ALIASING\n";
      break;
    case vtkFXAAOptions::FXAA_DEBUG_EDGE_DIRECTION:
      os << "FXAA_DEBUG_EDGE_DIRECTION\n";
      break;
    case vtkFXAAOptions::FXAA_DEBUG_EDGE_NUM_STEPS:
      os << "FXAA_DEBUG_EDGE_NUM_STEPS\n";
      break;
    case vtkFXAAOptions::FXAA_DEBUG_EDGE_DISTANCE:
      os << "FXAA_DEBUG_EDGE_DISTANCE\n";
      break;
    case vtkFXAAOptions::FXAA_DEBUG_EDGE_SAMPLE_OFFSET:
      os << "FXAA_DEBUG_EDGE_SAMPLE_OFFSET\n";
      break;
    case vtkFXAAOptions::FXAA_DEBUG_ONLY_SUBPIX_AA:
      os << "FXAA_DEBUG_ONLY_SUBPIX_AA\n";
      break;
    case vtkFXAAOptions::FXAA_DEBUG_ONLY_EDGE_AA:
      os << "FXAA_DEBUG_ONLY_EDGE_AA\n";
      break;
92
  }
93 94 95 96 97 98 99 100
}

//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::Execute(vtkOpenGLRenderer *ren)
{
  assert(ren);
  this->Renderer = ren;

101
  this->StartTimeQuery(this->PreparationTimer);
102 103
  this->Prepare();
  this->LoadInput();
104
  this->EndTimeQuery(this->PreparationTimer);
105

106
  this->StartTimeQuery(this->FXAATimer);
107
  this->ApplyFilter();
108
  this->EndTimeQuery(this->FXAATimer);
109 110

  this->Finalize();
111
  this->PrintBenchmark();
112 113 114 115 116 117 118 119 120 121

  this->Renderer = NULL;
}

//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::ReleaseGraphicsResources()
{
  this->FreeGLObjects();
}

122 123 124 125 126 127 128 129 130 131 132 133 134 135
//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::UpdateConfiguration(vtkFXAAOptions *opts)
{
  // Use the setters -- some of these options will trigger a shader rebuild
  // when they change, and the setters hold the logic for determining this.
  this->SetRelativeContrastThreshold(opts->GetRelativeContrastThreshold());
  this->SetHardContrastThreshold(opts->GetHardContrastThreshold());
  this->SetSubpixelBlendLimit(opts->GetSubpixelBlendLimit());
  this->SetSubpixelContrastThreshold(opts->GetSubpixelContrastThreshold());
  this->SetEndpointSearchIterations(opts->GetEndpointSearchIterations());
  this->SetUseHighQualityEndpoints(opts->GetUseHighQualityEndpoints());
  this->SetDebugOptionValue(opts->GetDebugOptionValue());
}

136 137 138 139
//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::SetUseHighQualityEndpoints(bool val)
{
  if (this->UseHighQualityEndpoints != val)
140
  {
141 142 143
    this->NeedToRebuildShader = true;
    this->Modified();
    this->UseHighQualityEndpoints = val;
144
  }
145 146 147
}

//------------------------------------------------------------------------------
148
void vtkOpenGLFXAAFilter::SetDebugOptionValue(vtkFXAAOptions::DebugOption opt)
149 150
{
  if (this->DebugOptionValue != opt)
151
  {
152 153 154
    this->NeedToRebuildShader = true;
    this->Modified();
    this->DebugOptionValue = opt;
155
  }
156 157 158 159 160 161
}

//------------------------------------------------------------------------------
vtkOpenGLFXAAFilter::vtkOpenGLFXAAFilter()
  : BlendState(false),
    DepthTestState(false),
162 163
    PreparationTimer(new vtkOpenGLRenderTimer),
    FXAATimer(new vtkOpenGLRenderTimer),
164 165 166 167 168 169
    RelativeContrastThreshold(1.f/8.f),
    HardContrastThreshold(1.f/16.f),
    SubpixelBlendLimit(3.f/4.f),
    SubpixelContrastThreshold(1.f/4.f),
    EndpointSearchIterations(12),
    UseHighQualityEndpoints(true),
170
    DebugOptionValue(vtkFXAAOptions::FXAA_NO_DEBUG),
171 172 173 174 175 176 177 178 179 180 181 182 183 184
    NeedToRebuildShader(true),
    Renderer(NULL),
    Input(NULL),
    Program(NULL),
    VAO(NULL),
    VBO(NULL)
{
  std::fill(this->Viewport, this->Viewport + 4, 0);
}

//------------------------------------------------------------------------------
vtkOpenGLFXAAFilter::~vtkOpenGLFXAAFilter()
{
  this->FreeGLObjects();
185 186
  delete PreparationTimer;
  delete FXAATimer;
187 188 189 190 191 192 193 194 195 196
}

//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::Prepare()
{
  this->Renderer->GetTiledSizeAndOrigin(&this->Viewport[2], &this->Viewport[3],
                                        &this->Viewport[0], &this->Viewport[1]);

  // Check if we need to create a new working texture:
  if (this->Input)
197
  {
198 199 200 201
    unsigned int rendererWidth = static_cast<unsigned int>(this->Viewport[2]);
    unsigned int rendererHeight = static_cast<unsigned int>(this->Viewport[3]);
    if (this->Input->GetWidth()  != rendererWidth ||
        this->Input->GetHeight() != rendererHeight)
202
    {
203 204
      this->FreeGLObjects();
    }
205
  }
206 207

  if (!this->Input)
208
  {
209
    this->CreateGLObjects();
210
  }
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226

  this->BlendState = glIsEnabled(GL_BLEND) == GL_TRUE;
  this->DepthTestState = glIsEnabled(GL_DEPTH_TEST) == GL_TRUE;

  glDisable(GL_BLEND);
  glDisable(GL_DEPTH_TEST);

  vtkOpenGLCheckErrorMacro("Error after saving GL state.");
}

//------------------------------------------------------------------------------
// Delete the vtkObject subclass pointed at by ptr if it is set.
namespace {
template <typename T> void DeleteHelper(T *& ptr)
{
  if (ptr)
227
  {
228 229
    ptr->Delete();
    ptr = NULL;
230
  }
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
}
} // end anon namespace

//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::FreeGLObjects()
{
  DeleteHelper(this->Input);
//  DeleteHelper(this->Program); // Managed by the shader cache
  DeleteHelper(this->VAO);
  DeleteHelper(this->VBO);
}

//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::CreateGLObjects()
{
  assert(!this->Input);
  this->Input = vtkTextureObject::New();
  this->Input->SetContext(static_cast<vtkOpenGLRenderWindow*>(
                            this->Renderer->GetRenderWindow()));
  this->Input->SetFormat(GL_RGB);
251 252 253

  // ES doesn't support GL_RGB8, and OpenGL 3 doesn't support GL_RGB.
  // What a world.
254
#if defined(GL_ES_VERSION_3_0)
255 256
  this->Input->SetInternalFormat(GL_RGB);
#else // OpenGL ES
257
  this->Input->SetInternalFormat(GL_RGB8);
258
#endif // OpenGL ES
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290

  // Required for FXAA, since we interpolate texels for blending.
  this->Input->SetMinificationFilter(vtkTextureObject::Linear);
  this->Input->SetMagnificationFilter(vtkTextureObject::Linear);

  // Clamp to edge, since we'll be sampling off-texture texels:
  this->Input->SetWrapS(vtkTextureObject::ClampToEdge);
  this->Input->SetWrapT(vtkTextureObject::ClampToEdge);
  this->Input->SetWrapR(vtkTextureObject::ClampToEdge);

  this->Input->Allocate2D(this->Viewport[2], this->Viewport[3], 4,
                          vtkTypeTraits<vtkTypeUInt8>::VTK_TYPE_ID);
}

//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::LoadInput()
{
  this->Input->CopyFromFrameBuffer(this->Viewport[0], this->Viewport[1], 0, 0,
                                   this->Viewport[2], this->Viewport[3]);
}

//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::ApplyFilter()
{
  typedef vtkOpenGLRenderUtilities GLUtil;

  vtkOpenGLRenderWindow *renWin = static_cast<vtkOpenGLRenderWindow*>(
        this->Renderer->GetRenderWindow());

  this->Input->Activate();

  if (this->NeedToRebuildShader)
291
  {
292 293 294 295
    DeleteHelper(this->VAO);
    DeleteHelper(this->VBO);
    this->Program = NULL; // Don't free, shader cache manages these.
    this->NeedToRebuildShader = false;
296
  }
297 298

  if (!this->Program)
299
  {
300 301 302 303 304 305
    std::string fragShader = vtkFXAAFilterFS;
    this->SubstituteFragmentShader(fragShader);
    this->Program = renWin->GetShaderCache()->ReadyShaderProgram(
          GLUtil::GetFullScreenQuadVertexShader().c_str(),
          fragShader.c_str(),
          GLUtil::GetFullScreenQuadGeometryShader().c_str());
306
  }
307
  else
308
  {
309
    renWin->GetShaderCache()->ReadyShaderProgram(this->Program);
310
  }
311 312

  if (!this->VAO)
313
  {
314 315 316
    this->VBO = vtkOpenGLBufferObject::New();
    this->VAO = vtkOpenGLVertexArrayObject::New();
    GLUtil::PrepFullScreenVAO(this->VBO, this->VAO, this->Program);
317
  }
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344

  this->Program->SetUniformi("Input", this->Input->GetTextureUnit());
  float invTexSize[2] = { 1.f / static_cast<float>(this->Viewport[2]),
                          1.f / static_cast<float>(this->Viewport[3]) };
  this->Program->SetUniform2f("InvTexSize", invTexSize);

  this->Program->SetUniformf("RelativeContrastThreshold",
                             this->RelativeContrastThreshold);
  this->Program->SetUniformf("HardContrastThreshold",
                             this->HardContrastThreshold);
  this->Program->SetUniformf("SubpixelBlendLimit",
                             this->SubpixelBlendLimit);
  this->Program->SetUniformf("SubpixelContrastThreshold",
                             this->SubpixelContrastThreshold);
  this->Program->SetUniformi("EndpointSearchIterations",
                             this->EndpointSearchIterations);

  this->VAO->Bind();
  GLUtil::DrawFullScreenQuad();
  this->VAO->Release();
  this->Input->Deactivate();
}

//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::SubstituteFragmentShader(std::string &fragShader)
{
  if (this->UseHighQualityEndpoints)
345
  {
346 347
    vtkShaderProgram::Substitute(fragShader, "//VTK::EndpointAlgo::Def",
                                 "#define FXAA_USE_HIGH_QUALITY_ENDPOINTS");
348
  }
349 350

#define DEBUG_OPT_CASE(optName) \
351
  case vtkFXAAOptions::optName: \
352 353 354 355 356 357
    vtkShaderProgram::Substitute(fragShader, "//VTK::DebugOptions::Def", \
                                 "#define " #optName); \
    break


  switch (this->DebugOptionValue)
358
  {
359
    default:
360
    case vtkFXAAOptions::FXAA_NO_DEBUG:
361 362 363 364 365 366 367 368
      break;
    DEBUG_OPT_CASE(FXAA_DEBUG_SUBPIXEL_ALIASING);
    DEBUG_OPT_CASE(FXAA_DEBUG_EDGE_DIRECTION);
    DEBUG_OPT_CASE(FXAA_DEBUG_EDGE_NUM_STEPS);
    DEBUG_OPT_CASE(FXAA_DEBUG_EDGE_DISTANCE);
    DEBUG_OPT_CASE(FXAA_DEBUG_EDGE_SAMPLE_OFFSET);
    DEBUG_OPT_CASE(FXAA_DEBUG_ONLY_SUBPIX_AA);
    DEBUG_OPT_CASE(FXAA_DEBUG_ONLY_EDGE_AA);
369
  }
370 371 372 373 374 375 376 377

#undef DEBUG_OPT_CASE
}

//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::Finalize()
{
  if (this->BlendState)
378
  {
379
    glEnable(GL_BLEND);
380
  }
381
  if (this->DepthTestState)
382
  {
383
    glEnable(GL_DEPTH_TEST);
384
  }
385 386 387 388 389

  vtkOpenGLCheckErrorMacro("Error after restoring GL state.");
}

//------------------------------------------------------------------------------
390
void vtkOpenGLFXAAFilter::StartTimeQuery(vtkOpenGLRenderTimer *timer)
391
{
392 393 394
  // Since it may take a few frames for the results to become available,
  // check if we've started the timer already.
  if (!timer->Started())
395
  {
396
    timer->Start();
397
  }
398 399 400
}

//------------------------------------------------------------------------------
401
void vtkOpenGLFXAAFilter::EndTimeQuery(vtkOpenGLRenderTimer *timer)
402
{
403 404 405
  // Since it may take a few frames for the results to become available,
  // check if we've stopped the timer already.
  if (!timer->Stopped())
406
  {
407
    timer->Stop();
408
  }
409 410 411 412 413
}

//------------------------------------------------------------------------------
void vtkOpenGLFXAAFilter::PrintBenchmark()
{
414 415
  if (this->PreparationTimer->Ready() &&
      this->FXAATimer->Ready())
416
  {
417

418
#ifdef FXAA_BENCHMARK
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
    int numPixels = this->Input->GetWidth() * this->Input->GetHeight();
    float ptime = this->PreparationTimer->GetElapsedMilliseconds();
    float ftime = this->FXAATimer->GetElapsedMilliseconds();
    float ttime = ptime + ftime;

    float ptimePerPixel = (this->PreparationTimer->GetElapsedNanoseconds() /
                           static_cast<float>(numPixels));
    float ftimePerPixel = (this->FXAATimer->GetElapsedNanoseconds() /
                           static_cast<float>(numPixels));
    float ttimePerPixel =  ptimePerPixel + ftimePerPixel;

    std::cerr << "FXAA Info:\n"
              << " - Number of pixels: " << numPixels << "\n"
              << " - Preparation time: " << ptime << "ms ("
              << ptimePerPixel << "ns per pixel)\n"
              << " - FXAA time: " << ftime << "ms ("
              << ftimePerPixel << "ns per pixel)\n"
              << " - Total time: " << ttime << "ms ("
              << ttimePerPixel << "ns per pixel)\n";
438
#endif // FXAA_BENCHMARK
439 440 441

    this->PreparationTimer->Reset();
    this->FXAATimer->Reset();
442
  }
443
}