1 // Created on: 2012-02-02
2 // Created by: Anton POLETAEV
3 // Copyright (c) 2012-2014 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
16 #include <OpenGl_GlCore15.hxx>
18 #include <OpenGl_FrameBuffer.hxx>
19 #include <OpenGl_LayerList.hxx>
20 #include <OpenGl_ShaderManager.hxx>
21 #include <OpenGl_Structure.hxx>
22 #include <OpenGl_VertexBuffer.hxx>
23 #include <OpenGl_View.hxx>
24 #include <OpenGl_Workspace.hxx>
26 #include <Graphic3d_GraphicDriver.hxx>
28 //=======================================================================
29 //function : OpenGl_LayerList
30 //purpose : Constructor
31 //=======================================================================
33 OpenGl_LayerList::OpenGl_LayerList (const Standard_Integer theNbPriorities)
34 : myDefaultLayerIndex (0),
35 myNbPriorities (theNbPriorities),
37 myImmediateNbStructures (0),
38 myModifStateOfRaytraceable (0),
39 myRenderOpaqueFilter (new OpenGl_OpaqueFilter()),
40 myRenderTranspFilter (new OpenGl_TransparentFilter())
42 // insert default priority layers
43 myLayers.Append (OpenGl_Layer (myNbPriorities));
44 myLayerIds.Bind (Graphic3d_ZLayerId_BotOSD, myLayers.Upper());
46 myLayers.Append (OpenGl_Layer (myNbPriorities));
47 myLayerIds.Bind (Graphic3d_ZLayerId_Default, myLayers.Upper());
49 myLayers.Append (OpenGl_Layer (myNbPriorities));
50 myLayerIds.Bind (Graphic3d_ZLayerId_Top, myLayers.Upper());
52 myLayers.Append (OpenGl_Layer (myNbPriorities));
53 myLayerIds.Bind (Graphic3d_ZLayerId_Topmost, myLayers.Upper());
55 myLayers.Append (OpenGl_Layer (myNbPriorities));
56 myLayerIds.Bind (Graphic3d_ZLayerId_TopOSD, myLayers.Upper());
58 myDefaultLayerIndex = myLayerIds.Find (Graphic3d_ZLayerId_Default);
60 myTransparentToProcess.Allocate (myLayers.Length());
63 //=======================================================================
64 //function : ~OpenGl_LayerList
65 //purpose : Destructor
66 //=======================================================================
68 OpenGl_LayerList::~OpenGl_LayerList()
72 //=======================================================================
75 //=======================================================================
77 void OpenGl_LayerList::AddLayer (const Graphic3d_ZLayerId theLayerId)
79 if (myLayerIds.IsBound (theLayerId))
85 myLayers.Append (OpenGl_Layer (myNbPriorities));
86 myLayerIds.Bind (theLayerId, myLayers.Length());
88 myTransparentToProcess.Allocate (myLayers.Length());
91 //=======================================================================
94 //=======================================================================
95 OpenGl_Layer& OpenGl_LayerList::Layer (const Graphic3d_ZLayerId theLayerId)
97 return myLayers.ChangeValue (myLayerIds.Find (theLayerId));
100 //=======================================================================
103 //=======================================================================
104 const OpenGl_Layer& OpenGl_LayerList::Layer (const Graphic3d_ZLayerId theLayerId) const
106 return myLayers.Value (myLayerIds.Find (theLayerId));
109 //=======================================================================
110 //function : RemoveLayer
112 //=======================================================================
114 void OpenGl_LayerList::RemoveLayer (const Graphic3d_ZLayerId theLayerId)
116 if (!myLayerIds.IsBound (theLayerId)
122 const Standard_Integer aRemovePos = myLayerIds.Find (theLayerId);
124 // move all displayed structures to first layer
125 const OpenGl_Layer& aLayerToMove = myLayers.Value (aRemovePos);
126 myLayers.ChangeFirst().Append (aLayerToMove);
129 myLayers.Remove (aRemovePos);
130 myLayerIds.UnBind (theLayerId);
132 // updated sequence indexes in map
133 for (OpenGl_LayerSeqIds::Iterator aMapIt (myLayerIds); aMapIt.More(); aMapIt.Next())
135 Standard_Integer& aSeqIdx = aMapIt.ChangeValue();
136 if (aSeqIdx > aRemovePos)
140 myDefaultLayerIndex = myLayerIds.Find (Graphic3d_ZLayerId_Default);
142 myTransparentToProcess.Allocate (myLayers.Length());
145 //=======================================================================
146 //function : AddStructure
148 //=======================================================================
150 void OpenGl_LayerList::AddStructure (const OpenGl_Structure* theStruct,
151 const Graphic3d_ZLayerId theLayerId,
152 const Standard_Integer thePriority,
153 Standard_Boolean isForChangePriority)
155 // add structure to associated layer,
156 // if layer doesn't exists, display structure in default layer
157 Standard_Integer aSeqPos = myLayers.Lower();
158 myLayerIds.Find (theLayerId, aSeqPos);
160 OpenGl_Layer& aLayer = myLayers.ChangeValue (aSeqPos);
161 aLayer.Add (theStruct, thePriority, isForChangePriority);
163 if (aLayer.IsImmediate())
165 ++myImmediateNbStructures;
168 // Note: In ray-tracing mode we don't modify modification
169 // state here. It is redundant, because the possible changes
170 // will be handled in the loop for structures
173 //=======================================================================
174 //function : RemoveStructure
176 //=======================================================================
178 void OpenGl_LayerList::RemoveStructure (const OpenGl_Structure* theStructure)
180 const Graphic3d_ZLayerId aLayerId = theStructure->ZLayer();
182 Standard_Integer aSeqPos = myLayers.Lower();
183 myLayerIds.Find (aLayerId, aSeqPos);
185 OpenGl_Layer& aLayer = myLayers.ChangeValue (aSeqPos);
186 Standard_Integer aPriority = -1;
188 // remove structure from associated list
189 // if the structure is not found there,
190 // scan through layers and remove it
191 if (aLayer.Remove (theStructure, aPriority))
194 if (aLayer.IsImmediate())
196 --myImmediateNbStructures;
199 if (aLayerId == Graphic3d_ZLayerId_Default
200 && theStructure->IsRaytracable())
202 ++myModifStateOfRaytraceable;
208 // scan through layers and remove it
209 Standard_Integer aSeqId = 1;
210 for (OpenGl_SequenceOfLayers::Iterator anIts (myLayers); anIts.More(); anIts.Next(), ++aSeqId)
212 OpenGl_Layer& aLayerEx = anIts.ChangeValue();
213 if (aSeqPos == aSeqId)
218 if (aLayerEx.Remove (theStructure, aPriority))
221 if (aLayerEx.IsImmediate())
223 --myImmediateNbStructures;
226 if (aSeqId == myDefaultLayerIndex
227 && theStructure->IsRaytracable())
229 ++myModifStateOfRaytraceable;
236 //=======================================================================
237 //function : InvalidateBVHData
239 //=======================================================================
240 void OpenGl_LayerList::InvalidateBVHData (const Graphic3d_ZLayerId theLayerId)
242 Standard_Integer aSeqPos = myLayers.Lower();
243 myLayerIds.Find (theLayerId, aSeqPos);
244 OpenGl_Layer& aLayer = myLayers.ChangeValue (aSeqPos);
245 aLayer.InvalidateBVHData();
248 //=======================================================================
249 //function : ChangeLayer
251 //=======================================================================
253 void OpenGl_LayerList::ChangeLayer (const OpenGl_Structure* theStructure,
254 const Graphic3d_ZLayerId theOldLayerId,
255 const Graphic3d_ZLayerId theNewLayerId)
257 Standard_Integer aSeqPos = myLayers.Lower();
258 myLayerIds.Find (theOldLayerId, aSeqPos);
259 OpenGl_Layer& aLayer = myLayers.ChangeValue (aSeqPos);
260 Standard_Integer aPriority = -1;
262 // take priority and remove structure from list found by <theOldLayerId>
263 // if the structure is not found there, scan through all other layers
264 if (aLayer.Remove (theStructure, aPriority, Standard_False))
266 if (theOldLayerId == Graphic3d_ZLayerId_Default
267 && theStructure->IsRaytracable())
269 ++myModifStateOfRaytraceable;
273 if (aLayer.IsImmediate())
275 --myImmediateNbStructures;
278 // isForChangePriority should be Standard_False below, because we want
279 // the BVH tree in the target layer to be updated with theStructure
280 AddStructure (theStructure, theNewLayerId, aPriority);
284 // scan through layers and remove it
285 Standard_Integer aSeqId = 1;
286 for (OpenGl_SequenceOfLayers::Iterator anIts (myLayers); anIts.More(); anIts.Next(), ++aSeqId)
288 if (aSeqPos == aSeqId)
293 // try to remove structure and get priority value from this layer
294 OpenGl_Layer& aLayerEx = anIts.ChangeValue();
295 if (aLayerEx.Remove (theStructure, aPriority, Standard_True))
297 if (aSeqId == myDefaultLayerIndex
298 && theStructure->IsRaytracable())
300 ++myModifStateOfRaytraceable;
304 if (aLayerEx.IsImmediate())
306 --myImmediateNbStructures;
309 // isForChangePriority should be Standard_False below, because we want
310 // the BVH tree in the target layer to be updated with theStructure
311 AddStructure (theStructure, theNewLayerId, aPriority);
317 //=======================================================================
318 //function : ChangePriority
320 //=======================================================================
321 void OpenGl_LayerList::ChangePriority (const OpenGl_Structure* theStructure,
322 const Graphic3d_ZLayerId theLayerId,
323 const Standard_Integer theNewPriority)
325 Standard_Integer aSeqPos = myLayers.Lower();
326 myLayerIds.Find (theLayerId, aSeqPos);
327 OpenGl_Layer& aLayer = myLayers.ChangeValue (aSeqPos);
328 Standard_Integer anOldPriority = -1;
330 if (aLayer.Remove (theStructure, anOldPriority, Standard_True))
333 if (aLayer.IsImmediate())
335 --myImmediateNbStructures;
338 AddStructure (theStructure, theLayerId, theNewPriority, Standard_True);
342 Standard_Integer aSeqId = 1;
343 for (OpenGl_SequenceOfLayers::Iterator anIts (myLayers); anIts.More(); anIts.Next(), ++aSeqId)
345 if (aSeqPos == aSeqId)
350 OpenGl_Layer& aLayerEx = anIts.ChangeValue();
351 if (aLayerEx.Remove (theStructure, anOldPriority, Standard_True))
354 if (aLayerEx.IsImmediate())
356 --myImmediateNbStructures;
359 AddStructure (theStructure, theLayerId, theNewPriority, Standard_True);
365 //=======================================================================
366 //function : SetLayerSettings
368 //=======================================================================
369 void OpenGl_LayerList::SetLayerSettings (const Graphic3d_ZLayerId theLayerId,
370 const Graphic3d_ZLayerSettings& theSettings)
372 OpenGl_Layer& aLayer = Layer (theLayerId);
373 if (aLayer.LayerSettings().IsImmediate() != theSettings.IsImmediate())
375 if (theSettings.IsImmediate())
377 myImmediateNbStructures += aLayer.NbStructures();
381 myImmediateNbStructures -= aLayer.NbStructures();
384 aLayer.SetLayerSettings (theSettings);
387 //=======================================================================
390 //=======================================================================
391 void OpenGl_LayerList::Render (const Handle(OpenGl_Workspace)& theWorkspace,
392 const Standard_Boolean theToDrawImmediate,
393 const OpenGl_LayerFilter theLayersToProcess,
394 OpenGl_FrameBuffer* theReadDrawFbo,
395 OpenGl_FrameBuffer* theOitAccumFbo) const
397 // Remember global settings for glDepth function and write mask.
398 OpenGl_GlobalLayerSettings aDefaultSettings;
400 const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
401 aCtx->core11fwd->glGetIntegerv (GL_DEPTH_FUNC, &aDefaultSettings.DepthFunc);
402 aCtx->core11fwd->glGetBooleanv (GL_DEPTH_WRITEMASK, &aDefaultSettings.DepthMask);
404 // Two render filters are used to support transparency draw. Opaque filter accepts
405 // only non-transparent OpenGl elements of a layer and counts number of skipped
406 // transparent ones. If the counter has positive value the layer is added into
407 // transparency post-processing stack. At the end of drawing or once the depth
408 // buffer is to be cleared the layers in the stack should be drawn using
409 // blending and depth mask settings and another transparency filter which accepts
410 // only transparent OpenGl elements of a layer. The stack <myTransparentToProcess>
411 // was preallocated before going into this method and has enough space to keep
412 // maximum number of references to layers, therefore it will not increase memory
413 // fragmentation during regular rendering.
414 const Handle(OpenGl_RenderFilter) aPrevFilter = theWorkspace->GetRenderFilter();
415 myRenderOpaqueFilter->SetPreviousFilter (aPrevFilter);
416 myRenderTranspFilter->SetPreviousFilter (aPrevFilter);
417 theWorkspace->SetRenderFilter (myRenderOpaqueFilter);
419 myTransparentToProcess.Clear();
421 OpenGl_LayerStack::iterator aStackIter (myTransparentToProcess.Origin());
422 Standard_Integer aSeqId = myLayers.Lower();
423 bool toClearDepth = false;
424 for (OpenGl_SequenceOfLayers::Iterator aLayerIter (myLayers); aLayerIter.More(); aLayerIter.Next(), ++aSeqId)
426 if (theLayersToProcess == OpenGl_LF_Bottom)
428 if (aSeqId >= myDefaultLayerIndex) continue;
430 else if (theLayersToProcess == OpenGl_LF_Upper)
432 if (aSeqId <= myDefaultLayerIndex) continue;
434 else if (theLayersToProcess == OpenGl_LF_Default)
436 if (aSeqId != myDefaultLayerIndex) continue;
439 const OpenGl_Layer& aLayer = aLayerIter.Value();
440 if (aLayer.IsImmediate() != theToDrawImmediate)
444 else if (aLayer.NbStructures() < 1)
446 // Make sure to clear depth of previous layers even if layer has no structures.
447 toClearDepth = toClearDepth || aLayer.LayerSettings().ToClearDepth();
451 // At this point the depth buffer may be set to clear by
452 // previous configuration of layers or configuration of the
453 // current layer. Additional rendering pass to handle transparent
454 // elements of recently drawn layers require use of current depth
455 // buffer so we put remaining layers for processing as one bunch before
456 // erasing the depth buffer.
458 || aLayer.LayerSettings().ToClearDepth())
460 if (!myTransparentToProcess.IsEmpty())
462 renderTransparent (theWorkspace, aStackIter, aDefaultSettings, theReadDrawFbo, theOitAccumFbo);
465 toClearDepth = false;
466 glDepthMask (GL_TRUE);
467 glClear (GL_DEPTH_BUFFER_BIT);
470 // Render opaque OpenGl elements of a layer and count the number of skipped.
471 // If a layer has skipped (e.g. transparent) elements it should be added into
472 // the transparency post-processing stack.
473 myRenderOpaqueFilter->SetSkippedCounter (0);
475 aLayer.Render (theWorkspace, aDefaultSettings);
477 if (myRenderOpaqueFilter->NbSkipped() > 0)
479 myTransparentToProcess.Push (&aLayer);
483 // Before finishing process the remaining collected layers with transparency.
484 if (!myTransparentToProcess.IsEmpty())
486 renderTransparent (theWorkspace, aStackIter, aDefaultSettings, theReadDrawFbo, theOitAccumFbo);
491 glDepthMask (GL_TRUE);
492 glClear (GL_DEPTH_BUFFER_BIT);
495 aCtx->core11fwd->glDepthMask (aDefaultSettings.DepthMask);
496 aCtx->core11fwd->glDepthFunc (aDefaultSettings.DepthFunc);
498 theWorkspace->SetRenderFilter (aPrevFilter);
501 //=======================================================================
502 //function : renderTransparent
503 //purpose : Render transparent objects using blending operator.
504 //=======================================================================
505 void OpenGl_LayerList::renderTransparent (const Handle(OpenGl_Workspace)& theWorkspace,
506 OpenGl_LayerStack::iterator& theLayerIter,
507 const OpenGl_GlobalLayerSettings& theGlobalSettings,
508 OpenGl_FrameBuffer* theReadDrawFbo,
509 OpenGl_FrameBuffer* theOitAccumFbo) const
511 // Blended order-independent transparency algorithm require several preconditions
512 // to be enabled. It should be requested by user, at least two outputs from
513 // fragment shader should be supported by GPU, so is the given framebuffer
514 // should contain two additional color buffers to handle accumulated color channels,
515 // blended alpha channel and weight factors - these accumulation buffers are required
516 // to implement commuting blend operator (at least OpenGl 2.0 should be available).
517 const bool isEnabledOit = theOitAccumFbo != NULL
518 && theOitAccumFbo->NbColorBuffers() >= 2
519 && theOitAccumFbo->ColorTexture (0)->IsValid()
520 && theOitAccumFbo->ColorTexture (1)->IsValid();
522 // Check if current iterator has already reached the end of the stack.
523 // This should happen if no additional layers has been added to
524 // the processing stack after last transparency pass.
525 if (theLayerIter == myTransparentToProcess.Back())
530 const Handle(OpenGl_Context) aCtx = theWorkspace->GetGlContext();
531 const Handle(OpenGl_ShaderManager)& aManager = aCtx->ShaderManager();
532 OpenGl_View* aView = theWorkspace->View();
533 const float aDepthFactor = aView != NULL ? aView->RenderingParams().OitDepthFactor : 0.0f;
535 theWorkspace->SetRenderFilter (myRenderTranspFilter);
537 aCtx->core11fwd->glEnable (GL_BLEND);
541 aManager->SetOitState (true, aDepthFactor);
543 theOitAccumFbo->BindBuffer (aCtx);
545 static const Standard_Integer aDrawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT0 + 1 };
546 aCtx->SetDrawBuffers (2, aDrawBuffers);
547 aCtx->core11fwd->glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
548 aCtx->core11fwd->glClear (GL_COLOR_BUFFER_BIT);
549 aCtx->core15fwd->glBlendFuncSeparate (GL_ONE, GL_ONE, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
553 aCtx->core11fwd->glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
556 // During blended order-independent transparency pass the depth test
557 // should be enabled to discard fragments covered by opaque geometry
558 // and depth writing should be disabled, because transparent fragments
559 // overal each other with non unitary coverage factor.
560 OpenGl_GlobalLayerSettings aGlobalSettings = theGlobalSettings;
561 aGlobalSettings.DepthMask = GL_FALSE;
562 aCtx->core11fwd->glDepthMask (GL_FALSE);
564 for (; theLayerIter != myTransparentToProcess.Back(); ++theLayerIter)
566 (*theLayerIter)->Render (theWorkspace, aGlobalSettings);
569 // Revert state of rendering.
572 aManager->SetOitState (false, aDepthFactor);
573 theOitAccumFbo->UnbindBuffer (aCtx);
576 theReadDrawFbo->BindBuffer (aCtx);
579 static const Standard_Integer aDrawBuffers[] = { GL_COLOR_ATTACHMENT0 };
580 aCtx->SetDrawBuffers (1, aDrawBuffers);
583 theWorkspace->SetRenderFilter (myRenderOpaqueFilter);
586 const Standard_Boolean isMSAA = theReadDrawFbo && theReadDrawFbo->NbSamples() > 0;
587 OpenGl_VertexBuffer* aVerts = theWorkspace->View()->initBlitQuad (Standard_False);
588 if (aVerts->IsValid() && aManager->BindOitCompositingProgram (isMSAA))
590 aCtx->core11fwd->glDepthFunc (GL_ALWAYS);
591 aCtx->core11fwd->glDepthMask (GL_FALSE);
593 // Bind full screen quad buffer and framebuffer resources.
594 aVerts->BindVertexAttrib (aCtx, Graphic3d_TOA_POS);
596 const Handle(OpenGl_Texture) aTextureBack = theWorkspace->DisableTexture();
598 theOitAccumFbo->ColorTexture (0)->Bind (aCtx, GL_TEXTURE0 + 0);
599 theOitAccumFbo->ColorTexture (1)->Bind (aCtx, GL_TEXTURE0 + 1);
601 // Draw full screen quad with special shader to compose the buffers.
602 aCtx->core11fwd->glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
603 aCtx->core11fwd->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
605 // Unbind OpenGL texture objects and shader program.
606 aVerts->UnbindVertexAttrib (aCtx, Graphic3d_TOA_POS);
607 theOitAccumFbo->ColorTexture (0)->Unbind (aCtx, GL_TEXTURE0 + 0);
608 theOitAccumFbo->ColorTexture (1)->Unbind (aCtx, GL_TEXTURE0 + 1);
609 aCtx->BindProgram (NULL);
611 if (!aTextureBack.IsNull())
613 theWorkspace->EnableTexture (aTextureBack);
618 aCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
619 "Initialization of OIT compositing pass has failed.\n"
620 " Blended order-independent transparency will not be available.\n");
623 Standard_Boolean& aOITFlag = isMSAA ? aView->myToDisableOITMSAA : aView->myToDisableOIT;
624 aOITFlag = Standard_True;
629 aCtx->core11fwd->glDisable (GL_BLEND);
630 aCtx->core11fwd->glBlendFunc (GL_ONE, GL_ZERO);
631 aCtx->core11fwd->glDepthMask (theGlobalSettings.DepthMask);
632 aCtx->core11fwd->glDepthFunc (theGlobalSettings.DepthFunc);
635 //=======================================================================
636 //class : OpenGl_OpaqueFilter
637 //function : ShouldRender
638 //purpose : Checks whether the element should be rendered or skipped.
639 //=======================================================================
640 Standard_Boolean OpenGl_LayerList::OpenGl_OpaqueFilter::ShouldRender (const Handle(OpenGl_Workspace)& theWorkspace,
641 const OpenGl_Element* theGlElement)
643 if (!myFilter.IsNull()
644 && !myFilter->ShouldRender (theWorkspace, theGlElement))
646 return Standard_False;
649 const OpenGl_PrimitiveArray* aPArray = dynamic_cast<const OpenGl_PrimitiveArray*> (theGlElement);
651 || aPArray->DrawMode() < OpenGl_PrimitiveArray::THE_FILLPRIM_FROM
652 || aPArray->DrawMode() > OpenGl_PrimitiveArray::THE_FILLPRIM_TO)
654 return Standard_True;
657 if (OpenGl_Context::CheckIsTransparent (theWorkspace->AspectFace(),
658 theWorkspace->HighlightStyle()))
661 return Standard_False;
664 return Standard_True;
667 //=======================================================================
668 //class : OpenGl_TransparentFilter
669 //function : ShouldRender
670 //purpose : Checks whether the element should be rendered or skipped.
671 //=======================================================================
672 Standard_Boolean OpenGl_LayerList::OpenGl_TransparentFilter::ShouldRender (const Handle(OpenGl_Workspace)& theWorkspace,
673 const OpenGl_Element* theGlElement)
675 if (!myFilter.IsNull()
676 && !myFilter->ShouldRender (theWorkspace, theGlElement))
678 return Standard_False;
681 const OpenGl_PrimitiveArray* aPArray = dynamic_cast<const OpenGl_PrimitiveArray*> (theGlElement);
683 || aPArray->DrawMode() < OpenGl_PrimitiveArray::THE_FILLPRIM_FROM
684 || aPArray->DrawMode() > OpenGl_PrimitiveArray::THE_FILLPRIM_TO)
686 return dynamic_cast<const OpenGl_AspectFace*> (theGlElement) != NULL;
689 return OpenGl_Context::CheckIsTransparent (theWorkspace->AspectFace(),
690 theWorkspace->HighlightStyle());