Updates will be applied tomorrow - Jan 26th, at 12pm EST (UTC-0500). GitLlab could be a little slow between 12 - 12:30pm EST.

Commit b75bc715 authored by Ken Martin's avatar Ken Martin

Update VTK to always use a framebuffer for rendering

Modify VTK so that the render process always uses a
framebuffer. The basic approach is that render() calls

- Start (pushes the current bindings + binds fb)
- DoStereoRender
  - StereoUpdate()
  - StereoMidpoint() (blits for crystal eyes)
  - StereoComplete()
- End (pops the bindings)
- CopyResultingFrame
  - stereo pixel processing
  - Frame() (blits the resulting frame)

The depth buffer will be 32 bit fixed unless a
stencil buffer is requested. Then it will be
24 depth + 8 stencil. If multisamples is requested
VTK will try to create a framebuffer with multisamples.

Only one color buffer is created. For stereo rendering
the StereoMidpoint must be used to read or blit the left
eye prior to starting rendering of the right eye.

If you want color or depth values from an external
codebase to be used in the rendering process you must
write them into the framebuffer prior to calling Render()
in VTK. See vtkExternalOpenGLRenderWindow for an example
of this.

Probably still some optimization that will happen in a
further topic once this settles as there are some old
methods that no longer really are needed.
parent 77896059
2d6863bfba4da904385c8d571a99fc6855c92382d869ab3d2000f9967a7a7b9afdc26654e47da4253244bbee136749b09a9a207139d4849009710b29f941c703
63d03f2145de7797b4c4d01989be4854468ccdefb237f68c96b054c5425ddb0f4ccaa932bba8e1488998f4f036c39e874c4ee0cd42d13d1f2b268ddaa35a45db
c5b3b918f705c39b103c8b3da0f1d59c9648b08c67b798861c1061cb2d8ed53cd2f54664e2ef767ddc84144818f72d748d9191ec913a2f9d2bb1f90b50ff3da2
319073d7ed4258c838ad64d88afebe8e925a111ee878edc83c76c3b811e6258aadd78f060bdc953f19d28f99117bd01bbd78575296eaa97311c082eb909c221b
......@@ -29,7 +29,6 @@
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkTestUtilities.h"
#include "vtkUnsignedCharArray.h"
int TestEdgeFlags(int argc, char* argv[])
......
......@@ -109,6 +109,22 @@ vtkRenderWindow::~vtkRenderWindow()
}
}
void vtkRenderWindow::SetMultiSamples(int val)
{
if (val == 1)
{
val = 0;
}
if (val == this->MultiSamples)
{
return;
}
this->MultiSamples = val;
this->Modified();
}
//----------------------------------------------------------------------------
// Create an interactor that will work with this renderer.
vtkRenderWindowInteractor* vtkRenderWindow::MakeRenderWindowInteractor()
......@@ -275,15 +291,18 @@ void vtkRenderWindow::Render()
this->Interactor->Initialize();
}
this->Start(); // Ensure context exists
vtkRenderTimerLog::ScopedEventLogger event;
if (this->RenderTimer->GetLoggingEnabled())
{
this->Start(); // Ensure context exists
this->RenderTimer->MarkFrame();
event = this->RenderTimer->StartScopedEvent("vtkRenderWindow::Render");
}
this->DoStereoRender();
this->End(); // restores original bindings
this->CopyResultFrame();
// reset the buffer size without freeing any memory.
......@@ -302,7 +321,6 @@ void vtkRenderWindow::DoStereoRender()
{
vtkCollectionSimpleIterator rsit;
this->Start();
this->StereoUpdate();
if (!this->StereoRender || (this->StereoType != VTK_STEREO_RIGHT))
......
......@@ -156,6 +156,11 @@ public:
*/
virtual void Start() = 0;
/**
* Update the system, if needed, at end of render process
*/
virtual void End(){};
/**
* Finalize the rendering process.
*/
......@@ -644,8 +649,9 @@ public:
//@{
/**
* Set / Get the number of multisamples to use for hardware antialiasing.
* A value of 1 will be set to 0.
*/
vtkSetMacro(MultiSamples, int);
virtual void SetMultiSamples(int);
vtkGetMacro(MultiSamples, int);
//@}
......
......@@ -35,105 +35,6 @@ vtkExternalOpenGLCamera::vtkExternalOpenGLCamera()
this->UserProvidedViewTransform = false;
}
//----------------------------------------------------------------------------
// Implement base class method.
void vtkExternalOpenGLCamera::Render(vtkRenderer* ren)
{
vtkOpenGLClearErrorMacro();
int lowerLeft[2];
int usize, vsize;
vtkOpenGLRenderWindow* win = vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow());
vtkOpenGLState* ostate = win->GetState();
// find out if we should stereo render
this->Stereo = (ren->GetRenderWindow())->GetStereoRender();
ren->GetTiledSizeAndOrigin(&usize, &vsize, lowerLeft, lowerLeft + 1);
// Take the window position into account
for (int i = 0; i < 2; ++i)
{
lowerLeft[i] = lowerLeft[i] + ren->GetRenderWindow()->GetPosition()[i];
}
// if were on a stereo renderer draw to special parts of screen
if (this->Stereo)
{
switch ((ren->GetRenderWindow())->GetStereoType())
{
case VTK_STEREO_CRYSTAL_EYES:
if (this->LeftEye)
{
if (ren->GetRenderWindow()->GetDoubleBuffer())
{
ostate->vtkglDrawBuffer(static_cast<GLenum>(win->GetBackLeftBuffer()));
ostate->vtkglReadBuffer(static_cast<GLenum>(win->GetBackLeftBuffer()));
}
else
{
ostate->vtkglDrawBuffer(static_cast<GLenum>(win->GetFrontLeftBuffer()));
ostate->vtkglReadBuffer(static_cast<GLenum>(win->GetFrontLeftBuffer()));
}
}
else
{
if (ren->GetRenderWindow()->GetDoubleBuffer())
{
ostate->vtkglDrawBuffer(static_cast<GLenum>(win->GetBackRightBuffer()));
ostate->vtkglReadBuffer(static_cast<GLenum>(win->GetBackRightBuffer()));
}
else
{
ostate->vtkglDrawBuffer(static_cast<GLenum>(win->GetFrontRightBuffer()));
ostate->vtkglReadBuffer(static_cast<GLenum>(win->GetFrontRightBuffer()));
}
}
break;
case VTK_STEREO_LEFT:
this->LeftEye = 1;
break;
case VTK_STEREO_RIGHT:
this->LeftEye = 0;
break;
default:
break;
}
}
else
{
if (ren->GetRenderWindow()->GetDoubleBuffer())
{
ostate->vtkglDrawBuffer(static_cast<GLenum>(win->GetBackLeftBuffer()));
// Reading back buffer means back left. see OpenGL spec.
// because one can write to two buffers at a time but can only read from
// one buffer at a time.
ostate->vtkglReadBuffer(static_cast<GLenum>(win->GetBackLeftBuffer()));
}
else
{
ostate->vtkglDrawBuffer(static_cast<GLenum>(win->GetFrontLeftBuffer()));
// Reading front buffer means front left. see OpenGL spec.
// because one can write to two buffers at a time but can only read from
// one buffer at a time.
ostate->vtkglReadBuffer(static_cast<GLenum>(win->GetFrontLeftBuffer()));
}
}
ostate->vtkglViewport(lowerLeft[0], lowerLeft[1], usize, vsize);
ostate->vtkglEnable(GL_SCISSOR_TEST);
ostate->vtkglScissor(lowerLeft[0], lowerLeft[1], usize, vsize);
if ((ren->GetRenderWindow())->GetErase() && ren->GetErase())
{
ren->Clear();
}
vtkOpenGLCheckErrorMacro("failed after Render");
}
//----------------------------------------------------------------------------
void vtkExternalOpenGLCamera::SetViewTransformMatrix(const double elements[16])
{
......
......@@ -35,11 +35,6 @@ public:
vtkTypeMacro(vtkExternalOpenGLCamera, vtkOpenGLCamera);
void PrintSelf(ostream& os, vtkIndent indent) override;
/**
* Implement base class method.
*/
void Render(vtkRenderer* ren) override;
/**
* Set the view transform matrix
*/
......
......@@ -17,6 +17,8 @@
#include "vtkExternalOpenGLRenderWindow.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLFramebufferObject.h"
#include "vtkOpenGLState.h"
#include "vtkRenderer.h"
#include "vtkRendererCollection.h"
......@@ -26,6 +28,7 @@ vtkStandardNewMacro(vtkExternalOpenGLRenderWindow);
vtkExternalOpenGLRenderWindow::vtkExternalOpenGLRenderWindow()
{
this->AutomaticWindowPositionAndResize = 1;
this->UseExternalContent = true;
}
//----------------------------------------------------------------------------
......@@ -40,17 +43,24 @@ void vtkExternalOpenGLRenderWindow::Start(void)
// Use hardware acceleration
this->SetIsDirect(1);
auto ostate = this->GetState();
if (this->AutomaticWindowPositionAndResize)
{
int info[4];
glGetIntegerv(GL_VIEWPORT, info);
ostate->vtkglGetIntegerv(GL_VIEWPORT, info);
this->SetPosition(info[0], info[1]);
this->SetSize(info[2], info[3]);
}
// creates or resizes the framebuffer
this->Size[0] = (this->Size[0] > 0 ? this->Size[0] : 300);
this->Size[1] = (this->Size[1] > 0 ? this->Size[1] : 300);
this->CreateOffScreenFramebuffer(this->Size[0], this->Size[1]);
// For stereo, render the correct eye based on the OpenGL buffer mode
GLint bufferType;
glGetIntegerv(GL_DRAW_BUFFER, &bufferType);
ostate->vtkglGetIntegerv(GL_DRAW_BUFFER, &bufferType);
vtkCollectionSimpleIterator sit;
vtkRenderer* renderer;
for (this->GetRenderers()->InitTraversal(sit);
......@@ -66,6 +76,20 @@ void vtkExternalOpenGLRenderWindow::Start(void)
this->SetStereoTypeToLeft();
}
}
ostate->PushFramebufferBindings();
if (this->UseExternalContent)
{
const int destExtents[4] = { 0, this->Size[0], 0, this->Size[1] };
this->OffScreenFramebuffer->Bind(GL_DRAW_FRAMEBUFFER);
this->GetState()->vtkglViewport(0, 0, this->Size[0], this->Size[1]);
this->GetState()->vtkglScissor(0, 0, this->Size[0], this->Size[1]);
vtkOpenGLFramebufferObject::Blit(
destExtents, destExtents, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
}
this->OffScreenFramebuffer->Bind();
}
//----------------------------------------------------------------------------
......@@ -77,5 +101,6 @@ bool vtkExternalOpenGLRenderWindow::IsCurrent(void)
//----------------------------------------------------------------------------
void vtkExternalOpenGLRenderWindow::PrintSelf(ostream& os, vtkIndent indent)
{
os << indent << "UseExternalContent: " << this->UseExternalContent << endl;
this->Superclass::PrintSelf(os, indent);
}
......@@ -80,11 +80,23 @@ public:
vtkBooleanMacro(AutomaticWindowPositionAndResize, int);
//@}
//@{
/**
* Turn on/off a flag which enables/disables using the content from an
* outside applicaiton. When on the active read buffer is first blitted
* into VTK and becomes the starting poiint for VTK's rendering.
*/
vtkGetMacro(UseExternalContent, bool);
vtkSetMacro(UseExternalContent, bool);
vtkBooleanMacro(UseExternalContent, bool);
//@}
protected:
vtkExternalOpenGLRenderWindow();
~vtkExternalOpenGLRenderWindow() override;
int AutomaticWindowPositionAndResize;
bool UseExternalContent;
private:
vtkExternalOpenGLRenderWindow(const vtkExternalOpenGLRenderWindow&) = delete;
......
......@@ -937,15 +937,6 @@ void vtkCocoaRenderWindow::CreateGLContext()
attribs[i++] = NSOpenGLPFADepthSize;
attribs[i++] = (NSOpenGLPixelFormatAttribute)32;
if (this->MultiSamples != 0)
{
attribs[i++] = NSOpenGLPFASampleBuffers;
attribs[i++] = (NSOpenGLPixelFormatAttribute)1;
attribs[i++] = NSOpenGLPFASamples;
attribs[i++] = (NSOpenGLPixelFormatAttribute)(this->MultiSamples);
attribs[i++] = NSOpenGLPFAMultisample;
}
if (this->DoubleBuffer != 0)
{
attribs[i++] = NSOpenGLPFADoubleBuffer;
......@@ -980,21 +971,11 @@ void vtkCocoaRenderWindow::CreateGLContext()
// Try falling back to the software renderer
hardware = 0;
}
else if (this->MultiSamples == 0)
else
{
// after trying with no multisamples, we are done
vtkWarningMacro(<< "No OpenGL context whatsoever could be created!");
break;
}
else if (this->MultiSamples < 4)
{
// next time try with no multisamples
this->MultiSamples = 0;
}
else
{
this->MultiSamples /= 2;
}
}
}
......@@ -1273,7 +1254,6 @@ void vtkCocoaRenderWindow::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "MultiSamples: " << this->MultiSamples << endl;
os << indent << "CocoaManager: " << this->GetCocoaManager() << endl;
os << indent << "RootWindow (NSWindow): " << this->GetRootWindow() << endl;
os << indent << "WindowId (NSView): " << this->GetWindowId() << endl;
......
......@@ -435,7 +435,6 @@ void vtkIOSRenderWindow::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "MultiSamples: " << this->MultiSamples << endl;
os << indent << "RootWindow (UIWindow): " << this->GetRootWindow() << endl;
os << indent << "WindowId (UIView): " << this->GetWindowId() << endl;
os << indent << "ParentId: " << this->GetParentId() << endl;
......
......@@ -212,6 +212,19 @@ public:
this->Attachment = attachment;
}
int GetSamples()
{
if (this->Texture)
{
return this->Texture->GetSamples();
}
if (this->Renderbuffer)
{
return this->Renderbuffer->GetSamples();
}
return 0;
}
void GetSize(int (&size)[2])
{
if (this->Texture)
......@@ -562,6 +575,7 @@ bool vtkOpenGLFramebufferObject::Start(int width, int height)
void vtkOpenGLFramebufferObject::ActivateBuffers()
{
GLint maxbuffers;
// todo move to cache
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxbuffers);
GLenum* buffers = new GLenum[maxbuffers];
......@@ -1438,6 +1452,12 @@ void vtkOpenGLFramebufferObject::Download(
pbo->UnBind();
}
int vtkOpenGLFramebufferObject::GetMultiSamples()
{
int abuff = this->ActiveBuffers[0];
return this->ColorBuffers[abuff]->GetSamples();
}
bool vtkOpenGLFramebufferObject::PopulateFramebuffer(int width, int height)
{
return this->PopulateFramebuffer(width, height, true, 1, VTK_UNSIGNED_CHAR, true, 24, 0);
......@@ -1489,7 +1509,7 @@ bool vtkOpenGLFramebufferObject::PopulateFramebuffer(int width, int height, bool
depth->AllocateDepth(this->LastSize[0], this->LastSize[1], vtkTextureObject::Fixed16);
break;
case 32:
depth->AllocateDepth(this->LastSize[0], this->LastSize[1], vtkTextureObject::Float32);
depth->AllocateDepth(this->LastSize[0], this->LastSize[1], vtkTextureObject::Fixed32);
break;
case 24:
default:
......
......@@ -425,6 +425,8 @@ public:
*/
void Resize(int width, int height);
int GetMultiSamples();
protected:
/**
* Attach a specific buffer
......
......@@ -79,64 +79,40 @@ public:
DRAW = 2
};
FrameBufferHelper(EType type, vtkOpenGLRenderWindow* ren, int front, int right)
FrameBufferHelper(EType type, vtkOpenGLRenderWindow* rw, int, int)
: Type(type)
{
const GLint buf = front ? (right ? ren->GetFrontRightBuffer() : ren->GetFrontLeftBuffer())
: (right ? ren->GetBackRightBuffer() : ren->GetBackLeftBuffer());
this->State = ren->GetState();
// If offscreen buffers are in use, then use them, otherwise look at if the
// default frame-buffer id is provided (which happens when using external
// OpenGL context).
if (ren->GetUseOffScreenBuffers() && ren->GetOffScreenFramebuffer())
this->State = rw->GetState();
switch (type)
{
switch (type)
case READ:
{
case READ:
{
this->State->PushReadFramebufferBinding();
this->State->vtkBindFramebuffer(GL_READ_FRAMEBUFFER, ren->GetOffScreenFramebuffer());
ren->GetOffScreenFramebuffer()->ActivateReadBuffer(buf - GL_COLOR_ATTACHMENT0);
}
break;
case DRAW:
this->State->PushReadFramebufferBinding();
if (!rw->GetOffScreenFramebuffer()->GetFBOIndex())
{
this->State->PushDrawFramebufferBinding();
this->State->vtkBindFramebuffer(GL_DRAW_FRAMEBUFFER, ren->GetOffScreenFramebuffer());
ren->GetOffScreenFramebuffer()->ActivateDrawBuffer(buf - GL_COLOR_ATTACHMENT0);
vtkGenericWarningMacro("Error invoking helper with no framebuffer");
return;
}
break;
default:
assert(false);
this->State->vtkBindFramebuffer(GL_READ_FRAMEBUFFER, rw->GetOffScreenFramebuffer());
rw->GetOffScreenFramebuffer()->ActivateReadBuffer(0);
}
}
else
{
const unsigned int fb = (ren->GetDefaultFrameBufferId() ? ren->GetDefaultFrameBufferId() : 0);
switch (type)
{
case READ:
{
this->State->PushReadFramebufferBinding();
this->State->vtkglBindFramebuffer(GL_READ_FRAMEBUFFER, fb);
this->State->vtkglReadBuffer(buf);
}
break;
break;
case DRAW:
case DRAW:
{
this->State->PushDrawFramebufferBinding();
if (!rw->GetOffScreenFramebuffer()->GetFBOIndex())
{
this->State->PushDrawFramebufferBinding();
this->State->vtkglBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb);
this->State->vtkglDrawBuffer(buf);
vtkGenericWarningMacro("Error invoking helper with no framebuffer");
return;
}
break;
default:
assert(false);
this->State->vtkBindFramebuffer(GL_DRAW_FRAMEBUFFER, rw->GetOffScreenFramebuffer());
rw->GetOffScreenFramebuffer()->ActivateDrawBuffer(0);
}
break;
default:
assert(false);
}
}
......@@ -201,7 +177,7 @@ vtkOpenGLRenderWindow::vtkOpenGLRenderWindow()
this->WindowName = new char[strlen(defaultWindowName) + 1];
strcpy(this->WindowName, defaultWindowName);
this->OffScreenFramebuffer = nullptr;
this->OffScreenFramebuffer = vtkOpenGLFramebufferObject::New();
this->BackLeftBuffer = static_cast<unsigned int>(GL_BACK_LEFT);
this->BackRightBuffer = static_cast<unsigned int>(GL_BACK_RIGHT);
......@@ -226,6 +202,7 @@ vtkOpenGLRenderWindow::vtkOpenGLRenderWindow()
this->TQuad2DVBO = nullptr;
this->NoiseTextureObject = nullptr;
this->FirstRenderTime = -1;
this->LastMultiSamples = -1;
}
// free up memory & close the window
......@@ -307,10 +284,7 @@ void vtkOpenGLRenderWindow::ReleaseGraphicsResources(vtkRenderWindow* renWin)
{
this->PushContext();
if (this->OffScreenFramebuffer)
{
this->OffScreenFramebuffer->ReleaseGraphicsResources(renWin);
}
this->OffScreenFramebuffer->ReleaseGraphicsResources(renWin);
// release the registered resources
if (this->NoiseTextureObject)
......@@ -906,76 +880,13 @@ int vtkOpenGLRenderWindow::GetPixelData(
}
// does the current read buffer require resolving for reading pixels
bool vtkOpenGLRenderWindow::GetCurrentBufferNeedsResolving()
bool vtkOpenGLRenderWindow::GetBufferNeedsResolving()
{
GLint frameBufferBinding = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &frameBufferBinding);
// the default buffer does not require it so return false
if (frameBufferBinding == 0)
{
return false;
}
// OK we have a framebuffer,
// is it backed by textures or renderbuffers?
GLint backingType;
glGetFramebufferAttachmentParameteriv(
GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &backingType);
// OK it is renderbuffer backed, query samples and return
if (backingType == GL_RENDERBUFFER)
{
GLint samples = 0;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
return (samples > 0);
}
// must be a texture backed FO
GLint textureName;
glGetFramebufferAttachmentParameteriv(
GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &textureName);
// in OpenGL < 4.5 there is no way to cleanly check for multisamples
// given a texture name which is what we have here. In opengl 4.5
// we can use glGetTextureParameter
// So instead we try binding the texture we have to
// GL_TEXTURE_2D_MULTISAMPLE and see if we get an error
#ifdef GL_TEXTURE_2D_MULTISAMPLE
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureName);
bool hadError = false;
while (glGetError() != GL_NO_ERROR)
{
hadError = true;
}
// if no error binding to a multi sample buffer, assume multisamples
if (!hadError)
if (this->OffScreenFramebuffer->GetMultiSamples())
{
return true;
}
// try binding to a non multisample texture target
glBindTexture(GL_TEXTURE_2D, textureName);
hadError = false;
while (glGetError() != GL_NO_ERROR)
{
hadError = true;
}
// if no error binding to a non-multi sample buffer, assume no multisample
if (!hadError)
{
return false;
}
// other cases possible such as cube maps, array textures etc, we punt
// and take the safe ap[proach and return true
return true;
#else
// if GL_TEXTURE_MULTISAMPLE_2D is not defined
// assume not multisampled texture
return false;
#endif
}
int vtkOpenGLRenderWindow::ReadPixels(
......@@ -999,7 +910,7 @@ int vtkOpenGLRenderWindow::ReadPixels(
FrameBufferHelper helper(FrameBufferHelper::READ, this, front, right);
// Let's determine if we're reading from an FBO.
bool resolveMSAA = this->GetCurrentBufferNeedsResolving();
bool resolveMSAA = this->GetBufferNeedsResolving();
this->GetState()->vtkglDisable(GL_SCISSOR_TEST);
......@@ -1059,37 +970,49 @@ int vtkOpenGLRenderWindow::ReadPixels(