2
// This unit is part of the GLScene Engine https://github.com/glscene
5
Implements FBO support for GLScene.
7
Original author of the unit is Riz.
8
Modified by C4 and YarUnderoaker (hope, I didn't miss anybody).
11
16/10/11 - Yar - Fixes for depth-stencil texture
12
23/08/10 - Yar - Added OpenGLTokens to uses, replaced OpenGL1x functions to OpenGLAdapter
13
16/05/10 - Yar - Added multisampling support (thanks C4)
14
22/04/10 - Yar - Fixes after GLState revision
15
15/04/10 - Yar - Bugfix missing FBO state changing (thanks C4)
16
23/01/10 - Yar - Replaced TextureFormat to TextureFormatEx
17
22/01/10 - Yar - Adapted to Handles of GLContext,
18
texture target unification, level and layer control
19
11/11/09 - DaStr - Added $I GLScene.inc
20
09/11/09 - DaStr - Initial version (contributed to GLScene)
46
MaxColorAttachments = 32;
49
TGLRenderbuffer = class
51
FRenderbufferHandle: TGLRenderbufferHandle;
54
FStorageValid: Boolean;
55
function GetHandle: TGLuint;
56
procedure SetHeight(const Value: Integer);
57
procedure SetWidth(const Value: Integer);
60
function GetInternalFormat: cardinal; virtual; abstract;
62
procedure InvalidateStorage;
65
destructor Destroy; override;
69
{ Handle to the OpenGL render buffer object.
70
If the handle hasn't already been allocated, it will be allocated
71
by this call (ie. do not use if no OpenGL context is active!) }
72
property Handle: TGLuint read GetHandle;
73
property Width: Integer read FWidth write SetWidth;
74
property Height: Integer read FHeight write SetHeight;
77
TGLDepthRBO = class(TGLRenderbuffer)
79
FDepthPrecision: TGLDepthPrecision;
80
procedure SetDepthPrecision(const Value: TGLDepthPrecision);
82
function GetInternalFormat: cardinal; override;
86
property DepthPrecision: TGLDepthPrecision read FDepthPrecision write
90
TGLStencilPrecision = (spDefault, sp1bit, sp4bits, sp8bits, sp16bits);
92
TGLStencilRBO = class(TGLRenderbuffer)
94
FStencilPrecision: TGLStencilPrecision;
95
procedure SetStencilPrecision(const Value: TGLStencilPrecision);
97
function GetInternalFormat: cardinal; override;
101
property StencilPrecision: TGLStencilPrecision read FStencilPrecision write
105
TGLFrameBuffer = class
107
FFrameBufferHandle: TGLFramebufferHandle;
113
FTextureMipmap: cardinal;
114
FAttachedTexture: array[0..MaxColorAttachments - 1] of TGLTexture;
115
FDepthTexture: TGLTexture;
117
FSRBO: TGLStencilRBO;
119
function GetStatus: TGLFramebufferStatus;
120
procedure SetHeight(const Value: Integer);
121
procedure SetWidth(const Value: Integer);
122
procedure SetLayer(const Value: Integer);
123
procedure SetLevel(const Value: Integer);
125
procedure AttachTexture(
126
const attachment: TGLenum;
127
const textarget: TGLenum;
128
const texture: TGLuint;
130
const layer: TGLint); overload;
131
procedure ReattachTextures;
134
destructor Destroy; override;
136
// attaches a depth rbo to the fbo
137
// the depth buffer must have the same dimentions as the fbo
138
procedure AttachDepthBuffer(DepthBuffer: TGLDepthRBO); overload;
139
// detaches depth attachment from the fbo
140
procedure DetachDepthBuffer;
142
// attaches a stencil rbo to the fbo
143
// the stencil buffer must have the same dimentions as the fbo
144
procedure AttachStencilBuffer(StencilBuffer: TGLStencilRBO); overload;
145
// detaches stencil attachment from the fbo
146
procedure DetachStencilBuffer;
148
// attaches a depth texture to the fbo
149
// the depth texture must have the same dimentions as the fbo
150
procedure AttachDepthTexture(Texture: TGLTexture); overload;
151
procedure DetachDepthTexture;
153
procedure AttachTexture(n: Cardinal; Texture: TGLTexture); overload;
154
procedure DetachTexture(n: Cardinal);
156
function GetStringStatus(out clarification: string): TGLFramebufferStatus;
157
property Status: TGLFramebufferStatus read GetStatus;
162
procedure Render(var rci: TGLRenderContextInfo; baseObject:
164
procedure PostRender(const PostGenerateMipmap: Boolean);
166
property Handle: TGLFramebufferHandle read FFrameBufferHandle;
167
property Width: Integer read FWidth write SetWidth;
168
property Height: Integer read FHeight write SetHeight;
169
property Layer: Integer read FLayer write SetLayer;
170
property Level: Integer read FLevel write SetLevel;
177
constructor TGLRenderbuffer.Create;
180
FRenderbufferHandle := TGLRenderbufferHandle.Create;
185
destructor TGLRenderbuffer.Destroy;
187
FRenderbufferHandle.DestroyHandle;
188
FRenderbufferHandle.Free;
192
function TGLRenderbuffer.GetHandle: GLuint;
194
if FRenderbufferHandle.Handle = 0 then
195
FRenderbufferHandle.AllocateHandle;
196
Result := FRenderbufferHandle.Handle;
199
procedure TGLRenderbuffer.InvalidateStorage;
201
FStorageValid := False;
204
procedure TGLRenderbuffer.SetHeight(const Value: Integer);
206
if FHeight <> Value then
213
procedure TGLRenderbuffer.SetWidth(const Value: Integer);
215
if FWidth <> Value then
222
procedure TGLRenderbuffer.Bind;
224
internalFormat: cardinal;
226
FRenderbufferHandle.AllocateHandle;
227
FRenderbufferHandle.Bind;
228
if not FStorageValid then
230
internalFormat := GetInternalFormat;
231
FRenderbufferHandle.SetStorage(internalFormat, FWidth, FHeight);
235
procedure TGLRenderbuffer.Unbind;
237
FRenderbufferHandle.UnBind;
242
constructor TGLDepthRBO.Create;
245
FDepthPrecision := dpDefault;
248
function TGLDepthRBO.GetInternalFormat: cardinal;
250
case DepthPrecision of
251
dp24bits: Result := GL_DEPTH_COMPONENT24;
252
dp16bits: Result := GL_DEPTH_COMPONENT16;
253
dp32bits: Result := GL_DEPTH_COMPONENT32;
256
Result := GL_DEPTH_COMPONENT24_ARB;
260
procedure TGLDepthRBO.SetDepthPrecision(const Value: TGLDepthPrecision);
262
if FDepthPrecision <> Value then
264
FDepthPrecision := Value;
271
constructor TGLStencilRBO.Create;
274
FStencilPrecision := spDefault;
277
function TGLStencilRBO.GetInternalFormat: cardinal;
279
case StencilPrecision of
280
spDefault: Result := GL_STENCIL_INDEX;
281
sp1bit: Result := GL_STENCIL_INDEX1_EXT;
282
sp4bits: Result := GL_STENCIL_INDEX4_EXT;
283
sp8bits: Result := GL_STENCIL_INDEX8_EXT;
284
sp16bits: Result := GL_STENCIL_INDEX16_EXT;
287
Result := GL_STENCIL_INDEX;
291
procedure TGLStencilRBO.SetStencilPrecision(const Value: TGLStencilPrecision);
293
if FStencilPrecision <> Value then
295
FStencilPrecision := Value;
302
constructor TGLFrameBuffer.Create;
305
FFrameBufferHandle := TGLFrameBufferHandle.Create;
311
FTarget := GL_FRAMEBUFFER;
314
destructor TGLFrameBuffer.Destroy;
316
FFrameBufferHandle.DestroyHandle;
317
FFrameBufferHandle.Free;
321
procedure TGLFrameBuffer.AttachTexture(n: Cardinal; Texture: TGLTexture);
323
textarget: TGLTextureTarget;
325
Assert(n < MaxColorAttachments);
327
FAttachedTexture[n] := Texture;
328
textarget := Texture.Image.NativeTextureTarget;
329
// Store mipmaping requires
330
if not ((Texture.MinFilter in [miNearest, miLinear])
331
or (textarget = ttTextureRect)) then
332
FTextureMipmap := FTextureMipmap or (1 shl n);
334
if Texture.Image is TGLMultiSampleImage then
338
GL_COLOR_ATTACHMENT0_EXT + n,
339
DecodeGLTextureTarget(textarget),
344
procedure TGLFrameBuffer.AttachDepthBuffer(DepthBuffer: TGLDepthRBO);
346
procedure AttachDepthRB;
348
// forces initialization
351
GL.FramebufferRenderbuffer(FTarget, GL_DEPTH_ATTACHMENT_EXT,
352
GL_RENDERBUFFER_EXT, DepthBuffer.Handle);
356
dp: TGLDepthPrecision;
358
if Assigned(FDRBO) then
360
FDRBO := DepthBuffer;
365
// if default format didn't work, try something else
366
// crude, but might work
367
if (Status = fsUnsupported) and (DepthBuffer.DepthPrecision = dpDefault) then
369
// try the other formats
370
// best quality first
371
for dp := high(dp) downto low(dp) do
373
if dp = dpDefault then
376
DepthBuffer.DepthPrecision := dp;
380
if not (Status = fsUnsupported) then
388
procedure TGLFrameBuffer.AttachDepthTexture(Texture: TGLTexture);
390
FDepthTexture := Texture;
392
if FDepthTexture.Image is TGLMultisampleImage then
394
if not IsDepthFormat(FDepthTexture.TextureFormatEx) then
396
// Force texture properties to depth compatibility
397
FDepthTexture.TextureFormatEx := tfDEPTH_COMPONENT24;
398
TGLMultisampleImage(FDepthTexture.Image).Width := Width;
399
TGLMultisampleImage(FDepthTexture.Image).Height := Height;
405
if not IsDepthFormat(FDepthTexture.TextureFormatEx) then
407
// Force texture properties to depth compatibility
408
FDepthTexture.ImageClassName := TGLBlankImage.ClassName;
409
FDepthTexture.TextureFormatEx := tfDEPTH_COMPONENT24;
410
TGLBlankImage(FDepthTexture.Image).Width := Width;
411
TGLBlankImage(FDepthTexture.Image).Height := Height;
413
if FDepthTexture.TextureFormatEx = tfDEPTH24_STENCIL8 then
415
TGLBlankImage(FDepthTexture.Image).GetBitmap32.SetColorFormatDataType(GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8);
416
TGLBlankImage(FDepthTexture.Image).ColorFormat := GL_DEPTH_STENCIL;
420
TGLBlankImage(FDepthTexture.Image).GetBitmap32.SetColorFormatDataType(GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE);
421
TGLBlankImage(FDepthTexture.Image).ColorFormat := GL_DEPTH_COMPONENT;
423
// Depth texture mipmaping
424
if not ((FDepthTexture.MinFilter in [miNearest, miLinear])) then
425
FTextureMipmap := FTextureMipmap or Cardinal(1 shl MaxColorAttachments);
430
DecodeGLTextureTarget(FDepthTexture.Image.NativeTextureTarget),
431
FDepthTexture.Handle,
435
if FDepthTexture.TextureFormatEx = tfDEPTH24_STENCIL8 then
437
GL_STENCIL_ATTACHMENT,
438
DecodeGLTextureTarget(FDepthTexture.Image.NativeTextureTarget),
439
FDepthTexture.Handle,
444
procedure TGLFrameBuffer.DetachDepthTexture;
446
if Assigned(FDepthTexture) then
448
FTextureMipmap := FTextureMipmap and (not (1 shl MaxColorAttachments));
451
DecodeGLTextureTarget(FDepthTexture.Image.NativeTextureTarget),
453
FDepthTexture := nil;
457
procedure TGLFrameBuffer.AttachStencilBuffer(StencilBuffer: TGLStencilRBO);
459
procedure AttachStencilRB;
461
// forces initialization
463
StencilBuffer.Unbind;
464
GL.FramebufferRenderbuffer(FTarget, GL_STENCIL_ATTACHMENT,
465
GL_RENDERBUFFER_EXT, StencilBuffer.Handle);
469
sp: TGLStencilPrecision;
471
if Assigned(FSRBO) then
473
FSRBO := StencilBuffer;
478
// if default format didn't work, try something else
479
// crude, but might work
480
if (Status = fsUnsupported)
481
and (StencilBuffer.StencilPrecision = spDefault) then
483
// try the other formats
484
// best quality first
485
for sp := high(sp) downto low(sp) do
487
if sp = spDefault then
490
StencilBuffer.StencilPrecision := sp;
494
if not (Status = fsUnsupported) then
502
procedure TGLFrameBuffer.AttachTexture(
503
const attachment: TGLenum;
504
const textarget: TGLenum;
505
const texture: TGLuint;
507
const layer: TGLint);
512
RC := SafeCurrentGLContext;
513
storeDFB := RC.GLStates.DrawFrameBuffer;
514
if storeDFB <> FFrameBufferHandle.Handle then
517
with FFrameBufferHandle do
520
Attach1DTexture(FTarget, attachment, textarget, texture, level);
523
Attach2DTexture(FTarget, attachment, textarget, texture, level);
525
GL_TEXTURE_RECTANGLE: // Rectangle texture can't be leveled
526
Attach2DTexture(FTarget, attachment, textarget, texture, 0);
529
Attach3DTexture(FTarget, attachment, textarget, texture, level, layer);
532
Attach2DTexture(FTarget, attachment, GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer, texture, level);
534
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
535
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
536
GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
537
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
538
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
539
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
540
Attach2DTexture(FTarget, attachment, textarget, texture, level);
542
GL_TEXTURE_CUBE_MAP_ARRAY,
545
AttachLayer(FTarget, attachment, texture, level, layer);
547
GL_TEXTURE_2D_MULTISAMPLE: // Multisample texture can't be leveled
548
Attach2DTexture(FTarget, attachment, textarget, texture, 0);
550
GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
551
AttachLayer(FTarget, attachment, texture, 0, layer);
554
if storeDFB <> FFrameBufferHandle.Handle then
555
RC.GLStates.SetFrameBuffer(storeDFB);
558
procedure TGLFrameBuffer.Bind;
560
if Handle.IsDataNeedUpdate then
566
procedure TGLFrameBuffer.Unbind;
568
FFrameBufferHandle.UnBind;
571
procedure TGLFrameBuffer.DetachTexture(n: Cardinal);
573
// textarget ignored when binding 0
574
if Assigned(FAttachedTexture[n]) then
578
GL_COLOR_ATTACHMENT0 + n,
579
GL_TEXTURE_2D, // target does not matter
582
FTextureMipmap := FTextureMipmap and (not (1 shl n));
583
FAttachedTexture[n] := nil;
588
procedure TGLFrameBuffer.DetachDepthBuffer;
591
GL.FramebufferRenderbuffer(FTarget, GL_DEPTH_ATTACHMENT,
597
procedure TGLFrameBuffer.DetachStencilBuffer;
600
GL.FramebufferRenderbuffer(FTarget, GL_STENCIL_ATTACHMENT,
606
function TGLFrameBuffer.GetStatus: TGLFramebufferStatus;
610
status := GL.CheckFramebufferStatus(FTarget);
613
GL_FRAMEBUFFER_COMPLETE_EXT: Result := fsComplete;
614
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: Result := fsIncompleteAttachment;
615
GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: Result :=
616
fsIncompleteMissingAttachment;
617
GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT: Result :=
618
fsIncompleteDuplicateAttachment;
619
GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: Result := fsIncompleteDimensions;
620
GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: Result := fsIncompleteFormats;
621
GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: Result := fsIncompleteDrawBuffer;
622
GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: Result := fsIncompleteReadBuffer;
623
GL_FRAMEBUFFER_UNSUPPORTED_EXT: Result := fsUnsupported;
624
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: Result := fsIncompleteMultisample;
626
Result := fsStatusError;
630
function TGLFrameBuffer.GetStringStatus(out clarification: string):
631
TGLFramebufferStatus;
633
cFBOStatus: array[TGLFramebufferStatus] of string = (
635
'Incomplete attachment',
636
'Incomplete missing attachment',
637
'Incomplete duplicate attachment',
638
'Incomplete dimensions',
639
'Incomplete formats',
640
'Incomplete draw buffer',
641
'Incomplete read buffer',
643
'Incomplite multisample',
647
clarification := cFBOStatus[Result];
650
procedure TGLFrameBuffer.PostRender(const PostGenerateMipmap: Boolean);
653
textarget: TGLTextureTarget;
655
if (FTextureMipmap > 0) and PostGenerateMipmap then
657
for n := 0 to MaxColorAttachments - 1 do
658
if Assigned(FAttachedTexture[n]) then
660
if FTextureMipmap and (1 shl n) = 0 then
662
textarget := FAttachedTexture[n].Image.NativeTextureTarget;
663
with FFrameBufferHandle.RenderingContext.GLStates do
664
TextureBinding[ActiveTexture, textarget] :=
665
FAttachedTexture[n].Handle;
666
GL.GenerateMipmap(DecodeGLTextureTarget(textarget));
671
procedure TGLFrameBuffer.PreRender;
676
procedure TGLFrameBuffer.Render(var rci: TGLRenderContextInfo; baseObject:
679
backColor: TColorVector;
680
buffer: TGLSceneBuffer;
683
Assert(Status = fsComplete, 'Framebuffer not complete');
685
buffer := TGLSceneBuffer(rci.buffer);
687
backColor := ConvertWinColor(buffer.BackgroundColor);
688
GL.ClearColor(backColor.V[0], backColor.V[1], backColor.V[2],
689
buffer.BackgroundAlpha);
690
rci.GLStates.SetColorMask(cAllColorComponents);
691
rci.GLStates.DepthWriteMask := True;
692
GL.Clear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
694
baseObject.Render(rci);
698
procedure TGLFrameBuffer.SetHeight(const Value: Integer);
700
if FHeight <> Value then
706
procedure TGLFrameBuffer.SetWidth(const Value: Integer);
708
if FWidth <> Value then
714
procedure TGLFrameBuffer.ReattachTextures;
720
Handle.AllocateHandle;
722
// Reattach layered textures
725
for n := 0 to MaxColorAttachments - 1 do
726
if Assigned(FAttachedTexture[n]) then
729
GL_COLOR_ATTACHMENT0_EXT + n,
730
DecodeGLTextureTarget(FAttachedTexture[n].Image.NativeTextureTarget),
731
FAttachedTexture[n].Handle,
737
if Assigned(FDepthTexture) then
741
DecodeGLTextureTarget(FDepthTexture.Image.NativeTextureTarget),
742
FDepthTexture.Handle,
748
if Assigned(FDRBO) then
752
GL.FramebufferRenderbuffer(FTarget, GL_DEPTH_ATTACHMENT_EXT,
753
GL_RENDERBUFFER_EXT, FDRBO.Handle);
757
if Assigned(FSRBO) then
761
GL.FramebufferRenderbuffer(FTarget, GL_STENCIL_ATTACHMENT,
762
GL_RENDERBUFFER_EXT, FSRBO.Handle);
766
if not bEmpty and (GetStringStatus(s) <> fsComplete) then
767
GLSLogger.LogErrorFmt('Framebuffer error: %s. Deactivated', [s]);
769
Handle.NotifyDataUpdated;
772
procedure TGLFrameBuffer.SetLayer(const Value: Integer);
776
if FLayer <> Value then
779
RC := CurrentGLContext;
782
if RC.GLStates.DrawFrameBuffer = FFrameBufferHandle.Handle then
788
procedure TGLFrameBuffer.SetLevel(const Value: Integer);
792
if FLevel <> Value then
795
RC := CurrentGLContext;
798
if RC.GLStates.DrawFrameBuffer = FFrameBufferHandle.Handle then