Commit ad4dddfc authored by Burlen Loring's avatar Burlen Loring
Browse files

Surface LIC : parallelization & fine tuning features

* Parallelization:
Support for Multiblock datasets and MPI parallel usage. Three compositing
strategies are provided, with a heuristic to automatically make a reasonable
selection between them. [1] INPLACE, [2] INPLACE_DISJOINT, [3] BALANCED.
The inplace strategy processes data where it falls given its parallel domain
decomposition and projection to the screen. For simple surfaces such as
slices where ranks/blocks data does not overlap in screen space this is the
most efficient approach. However where ranks/blocks data have large amount
of screen space overlap, this approach has high communication/compositing
and overhead and a large amount of redundant computation that's discarded.
The inplace-disjoint strategy was designed to address these issues. It
leaves data inplace as much as possible but will assign pixels to ranks
uniquely eliminating redundant computation. The communication costs are
the same as the inplace strategy however they are split into two temporally
disjoint parts with computation in between which tends to reduce congestion
and contention. The balanced strategy divides the screen into equally sized
disjoint regions and assigns each rank a region. This strategy can suffer
from load balancing issues, and because no account is made of the initial
data distribution it may have higher communication/compositing costs than
the others. The parallel implementations have been tested with IceT in
ParaView. The compositors uses non-blocking mpi communications, MPI
subarrays for describing non-contoguous memory extents, and communicates
efficiently by pasing mpi gpu mapped memory via extensions made to
vtkPixelBufferObject. An object factory is used to override the serial
objects with their parallel counterparts  when the parallel module is
linked.

* Contrast and Color Contrast Enhancement:
The new contrast enhancement feature increases both dynamic range contrast
while preserving patterns in the LIC. Contrast enhancment is implemented using
histogram stretching. The color contrast enhancement is made in HSL space on
the L channel to minimize color distortions. During the "enhanced" LIC mode
contrast enhancement is also applied after the first pass of LIC compution.
This helps strengthen streaking patterns.

* Enhanced scalar color shading:
A new mode has been added to the shader that shades surface geometry combining
both LIC and scalar colors. The new shader produces bright colors without
diminishing the visibility of the LIC by multiplying colored lit surface with
computed LIC texture. The original blending implementation is left in place
as it works well in many situations.

* Noise texture customization:
A new noise texture generator has been added to improve control over "streaking"
in the convolved texture. The noise textures produced are fully customizable over
9 degrees of freedom. [1] Noise distribution (Gaussian, Uniform, Perlin); [2] Noise
texture size [3] Noise grain size [4,5] Min/max noise values [6] Number of noise
levels; [7] Impulse noise probability; [8] Impulse noise background color; [9]
Noise generator seed

* Fragment masking controls:
The new fragment masking provides controls over the color and intensity of masked
fragments and their blending with scalars. Fragments are masked by threshold |V|<t,
where t is a user specified threshold value, and V are mask vectors. Masking vectors
can be optionally projected onto the surface or passed through un-modified so that
threshold is provided in the original vector units and matches scalar color shading.

* Vector Normalization Control:
The new vector normalization control lets the user turn on and off normalization
in the integrator. Disabling normalization can help highlight relative strengths
in strongly varying regions of a flow field by making the amount of smoothing that
occurs during convolution proportional to the flow strength.

* Antialiasing:
An optional anti-aliasing stage was added. In some cases the CE and EE stages
produce streaks with sharp jagged transitions from black to white. The AA stage
can be used to soften the result and smooth sharp transitions.

* Optimizations:
The implementation has been split into 8 major steps with the result of each stage
cached in a texture.[1] Vector projection and lighting calculation; [2] Gather;
[3] Compositingand guard pixel generation; [4] LIC Computation; [5] Scatter;
[6] Scalar coloring; [7] Color contrast enhance; [8] Depth test. During user
interaction stages that do not have modfied paramters or input data can be skiped
using the cahced result instead.  This can save large amount of time for expensive
operations such as projecting vectors, computing the LIC etc. Classes
were added to simplify monitoring of various system paramters that would trigger
an update, such as changes to lights, modelview or projection matrices, background
color, etc. A new framebuffer object has been introduced, designed specifically
for efficient buffer ping-pong, as well as efficiency improvements for setting
shader unfiorms on inner parts of buffer ping-pong loops.

* Porting and Testing:
The code has been ported to Apple + ATI systems, and to OS Mesa with the gallium
llvmpipe state tracker. A number of ctests have been introduced to excersize the
new features. Parallel features have testing coverage in ParaView.

Change-Id: I26f90a4e1e4e204289d38993d1e32fe5493b7a0d
parent ace2258e
......@@ -110,6 +110,7 @@ set(Module_SRCS
vtkPerlinNoise.cxx
vtkPiecewiseFunction.cxx
vtkPixel.cxx
vtkPixelExtent.cxx
vtkPlaneCollection.cxx
vtkPlane.cxx
vtkPlanes.cxx
......@@ -254,6 +255,7 @@ set_source_files_properties(
vtkMarchingCubesTriangleCases
vtkImageIterator
vtkImageProgressIterator
vtkPixelExtent.cxx
vtkVector
vtkColor
vtkRect
......
......@@ -16,6 +16,7 @@ vtk_add_test_cxx(NO_DATA NO_VALID NO_OUTPUT
TestInterpolationDerivs.cxx
TestInterpolationFunctions.cxx
TestPath.cxx
TestPixelExtent.cxx
TestPointLocators.cxx
TestPolyDataRemoveCell.cxx
TestPolygon.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestPixelExtent.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 "vtkPixelExtent.h"
#include "vtkPixelExtentIO.h"
#include <iostream>
#include <deque>
using std::cerr;
using std::endl;
using std::deque;
int TestPixelExtent(int argc, char* argv[])
{
(void)argc;
(void)argv;
cerr << "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)" << endl;
// small extent in the middle of the region of interest
vtkPixelExtent A(4,8,4,8);
// larger region that covers A
vtkPixelExtent B(A);
B.Grow(4);
// shift C to origin
vtkPixelExtent C(A);
C.Shift();
// shift D to upper right corner of larger region
vtkPixelExtent D(A);
int s1[2]={4,4};
D.Shift(s1);
bool testPass = true;
vtkPixelExtent tmp1;
vtkPixelExtent tmp2;
vtkPixelExtent tmp3;
// shift, intersect
tmp1 = C;
tmp2 = D;
tmp1 &= tmp2;
cerr << C << " & " << D << " = " << tmp1 << endl;
if (!tmp1.Empty())
{
cerr << "Test empty intersection failed" << endl;
testPass = false;
}
tmp1 = A;
int s2[2] = {-2,-2};
tmp1.Shift(s2);
tmp2 = A;
int s3[2] = {2,2};
tmp2.Shift(s3);
tmp3 = tmp1;
tmp3 &= tmp2;
cerr << tmp1 << " & " << tmp2 << " = " << tmp3 << endl;
if (!(tmp3 == vtkPixelExtent(6,6,6,6)))
{
cerr << "Test intersection failed" << endl;
testPass = false;
}
// shift, grow, union
tmp1 = C;
tmp2 = D;
tmp3 = tmp1;
tmp3 |= tmp2;
cerr << tmp1 << " | " << tmp2 << " = " << tmp3 << endl;
if (!(tmp3 == B))
{
cerr << "Test union fails" << endl;
testPass = false;
}
// subtraction
deque<vtkPixelExtent> tmp4;
vtkPixelExtent::Subtract(B, A, tmp4);
deque<vtkPixelExtent> tmp5;
tmp5.push_back(vtkPixelExtent(4, 8, 9, 12));
tmp5.push_back(vtkPixelExtent(9, 12, 9, 12));
tmp5.push_back(vtkPixelExtent(9, 12, 4, 8));
tmp5.push_back(vtkPixelExtent(0, 3, 4, 8));
tmp5.push_back(vtkPixelExtent(0, 3, 9, 12));
tmp5.push_back(vtkPixelExtent(4, 8, 0, 3));
tmp5.push_back(vtkPixelExtent(9, 12, 0, 3));
tmp5.push_back(vtkPixelExtent(0, 3, 0, 3));
size_t n = tmp4.size();
for (size_t i=0; i<n; ++i)
{
if (!(tmp4[i] == tmp5[i]))
{
cerr << "Test subtraction failed" << endl;
testPass = false;
break;
}
}
cerr << B << " - " << A << " = ";
if (n)
{
cerr << tmp4[0];
for (size_t i=1; i<n; ++i)
{
cerr << ", " << tmp4[i];
}
}
cerr << endl;
if (!testPass)
{
cerr << "Test fails" << endl;
return 1;
}
cerr << "Test passes" << endl;
return 0;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkPixelExtenth.h
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 "vtkPixelExtent.h"
using std::deque;
using std::ostream;
//-----------------------------------------------------------------------------
vtkPixelExtent vtkPixelExtent::Grow(
const vtkPixelExtent &inputExt,
const vtkPixelExtent &problemDomain,
int n)
{
vtkPixelExtent outputExt = vtkPixelExtent::Grow(inputExt, n);
outputExt &= problemDomain;
return outputExt;
}
//-----------------------------------------------------------------------------
vtkPixelExtent vtkPixelExtent::Grow(
const vtkPixelExtent &inputExt,
int n)
{
vtkPixelExtent outputExt(inputExt);
outputExt.Grow(0, n);
outputExt.Grow(1, n);
return outputExt;
}
//-----------------------------------------------------------------------------
vtkPixelExtent vtkPixelExtent::GrowLow(
const vtkPixelExtent &inputExt,
int q,
int n)
{
vtkPixelExtent outputExt(inputExt);
outputExt[2*q] -= n;
return outputExt;
}
//-----------------------------------------------------------------------------
vtkPixelExtent vtkPixelExtent::GrowHigh(
const vtkPixelExtent &inputExt,
int q,
int n)
{
vtkPixelExtent outputExt(inputExt);
outputExt[2*q+1] += n;
return outputExt;
}
//-----------------------------------------------------------------------------
vtkPixelExtent vtkPixelExtent::Shrink(
const vtkPixelExtent &inputExt,
int n)
{
return vtkPixelExtent::Grow(inputExt, -n);
}
//-----------------------------------------------------------------------------
vtkPixelExtent vtkPixelExtent::Shrink(
const vtkPixelExtent &inputExt,
const vtkPixelExtent &problemDomain,
int n)
{
vtkPixelExtent outputExt(inputExt);
outputExt.Grow(0, -n);
outputExt.Grow(1, -n);
// don't shrink at the problem domain
// because you don't grow outside the problem domain.
for (int i=0; i<4; ++i)
{
if (inputExt[i] == problemDomain[i])
{
outputExt[i] = problemDomain[i];
}
}
return outputExt;
}
//-----------------------------------------------------------------------------
vtkPixelExtent vtkPixelExtent::CellToNode(
const vtkPixelExtent &inputExt)
{
vtkPixelExtent outputExt(inputExt);
++outputExt[1];
++outputExt[3];
return outputExt;
}
//-----------------------------------------------------------------------------
vtkPixelExtent vtkPixelExtent::NodeToCell(const vtkPixelExtent &inputExt)
{
vtkPixelExtent outputExt(inputExt);
--outputExt[1];
--outputExt[3];
return outputExt;
}
//-----------------------------------------------------------------------------
void vtkPixelExtent::Shift(
int *ij,
int n)
{
ij[0] += n;
ij[1] += n;
}
//-----------------------------------------------------------------------------
void vtkPixelExtent::Shift(
int *ij,
int *n)
{
ij[0] += n[0];
ij[1] += n[1];
}
//-----------------------------------------------------------------------------
void vtkPixelExtent::Split(
int i1,
int j1,
const vtkPixelExtent &ext,
deque<vtkPixelExtent> &newExts)
{
// cell is inside, split results in as many as
// 4 new extents. check for each one.
int i0 = i1 - 1;
int j0 = j1 - 1;
int outside = 1;
// lower left
if (ext.Contains(i0, j0))
{
newExts.push_back(vtkPixelExtent(ext[0], i0, ext[2], j0));
outside = 0;
}
// lower right
if (ext.Contains(i1, j0))
{
newExts.push_back(vtkPixelExtent(i1, ext[1], ext[2], j0));
outside = 0;
}
// upper left
if (ext.Contains(i0, j1))
{
newExts.push_back(vtkPixelExtent(ext[0], i0, j1, ext[3]));
outside = 0;
}
// upper right
if (ext.Contains(i1, j1))
{
newExts.push_back(vtkPixelExtent(i1, ext[1], j1, ext[3]));
outside = 0;
}
// split cell is outside, pass through
if (outside)
{
newExts.push_back(ext);
return;
}
}
//-----------------------------------------------------------------------------
void vtkPixelExtent::Subtract(
const vtkPixelExtent &A,
vtkPixelExtent B,
deque<vtkPixelExtent> &C)
{
// split method requires split point inside the extent
vtkPixelExtent I(A);
I &= B;
if (I.Empty())
{
// do nothing if disjoint
C.push_back(A);
return;
}
if (B.Contains(A))
{
// if A is covered by B then remove A
return;
}
// split left and bellow this cells
I.CellToNode();
deque<vtkPixelExtent> tmpA0;
tmpA0.push_back(A);
for (int q=0; q<4; ++q)
{
const int ids[8] = {0,2, 1,2, 1,3, 0,3};
int qq = 2*q;
int i = I[ids[qq]];
int j = I[ids[qq+1]];
deque<vtkPixelExtent> tmpA1;
while ( !tmpA0.empty() )
{
vtkPixelExtent ext = tmpA0.back();
tmpA0.pop_back();
vtkPixelExtent::Split(i,j, ext, tmpA1);
}
tmpA0 = tmpA1;
}
// remove anything covered by B
size_t n = tmpA0.size();
for (size_t q=0; q<n; ++q)
{
const vtkPixelExtent &ext = tmpA0[q];
if (!B.Contains(ext))
{
C.push_back(ext);
}
}
}
// ----------------------------------------------------------------------------
void vtkPixelExtent::Merge(deque<vtkPixelExtent> &exts)
{
size_t ne = exts.size();
// working in point space simplifies things because
// points overlap in adjacent extents while cells do not
deque<vtkPixelExtent> tmpExts(ne);
for (size_t t=0; t<ne; ++t)
{
vtkPixelExtent ext(exts[t]);
ext.CellToNode();
tmpExts[t] = ext;
}
// one pass for each direction
for (int q=0; q<2; ++q)
{
int qq = 2*q;
// consider each extent as a target to be merged
for (size_t t=0; t<ne; ++t)
{
// if a merger occurs the merged extent is added
// as a new target with the constituents marked empty
// and the current pass is terminated early
int nextPass = 0;
// current target
vtkPixelExtent &ext0 = tmpExts[t];
if (ext0.Empty())
{
// was merged in preceeding pass
continue;
}
for (size_t c=0; c<ne; ++c)
{
if (c == t)
{
// don't attempt merge with self
continue;
}
// candidate
vtkPixelExtent &ext1 = tmpExts[c];
if (ext1.Empty())
{
// was merged in preceeding pass
continue;
}
// must be same size and coordinate in merge dir
if ( (ext0[qq] == ext1[qq]) && (ext0[qq+1] == ext1[qq+1]) )
{
// must overlap overlap
vtkPixelExtent ext2(ext0);
ext2 &= ext1;
if (!ext2.Empty())
{
// merge and add as new target
// in a later pass
vtkPixelExtent ext3(ext0);
ext3 |= ext1;
tmpExts.push_back(ext3);
++ne;
// mark the merged extents empty
ext0.Clear();
ext1.Clear();
// move to next target
nextPass = 1;
break;
}
}
if (nextPass)
{
break;
}
}
}
}
// discard merged targets copy to output
exts.clear();
for (size_t t=0; t<ne; ++t)
{
vtkPixelExtent &ext = tmpExts[t];
if (!ext.Empty())
{
ext.NodeToCell();
exts.push_back(ext);
}
}
}
//*****************************************************************************
ostream &operator<<(ostream &os, const vtkPixelExtent &ext)
{
if (ext.Empty())
{
os << "(empty)";
}
else
{
os
<< "("
<< ext[0] << ", "
<< ext[1] << ", "
<< ext[2] << ", "
<< ext[3] << ")";
}
return os;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkPixelExtenth.h
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.
=========================================================================*/
// .NAME vtkPixelExtent -- Index space representation of a pixel plane
// .SECTION Description
// Representation of a cartesian pixel plane and common operations
// on it. The implementation is intended to be fast and light
// so that it may be used in place of int[4] with little or no
// performance penalty.
//
// NOTE in most cases operation on an empty object produces
// incorrect results. If it an issue query Empty() first.
#ifndef __vtkPixelExtent_h
#define __vtkPixelExtent_h
#include "vtkCommonDataModelModule.h" // for export
#include <deque> // for inline impl
#include <algorithm> // for inline impl
#include <iostream> // for inline impl
#include <climits> // for inline impl
class VTKCOMMONDATAMODEL_EXPORT vtkPixelExtent
{
public:
vtkPixelExtent();
template<typename T>
vtkPixelExtent(const T *ext);
template<typename T>
vtkPixelExtent(T ilo, T ihi, T jlo, T jhi);
template<typename T>
vtkPixelExtent(T width, T height)
{ this->SetData(T(0), width-T(1), T(0), height-T(1)); }
vtkPixelExtent(const vtkPixelExtent &other);
vtkPixelExtent &operator=(const vtkPixelExtent &other);
// Description:
// Element access
int &operator[](int i){ return this->Data[i]; }
const int &operator[](int i) const { return this->Data[i]; }
// Description:
// Set the extent.
void SetData(const vtkPixelExtent &ext);
template<typename T>
void SetData(const T *ext);
template<typename T>
void SetData(T ilo, T ihi, T jlo, T jhi);
void Clear();
// Description:
// Direct access to internal data.
int *GetData(){ return this->Data; }
const int *GetData() const { return this->Data; }
template<typename T>
void GetData(T data[4]) const;
unsigned int *GetDataU()
{ return reinterpret_cast<unsigned int*>(this->Data); }
const unsigned int *GetDataU() const
{ return reinterpret_cast<const unsigned int*>(this->Data); }