{
    This file is part of Chentrah,
    Copyright (C) 2004-2014 Anton Rzheshevski (chebmaster@mail.ru).

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/

 **********************************************************************}


unit mo_fbo;

{$include mo_globaldefs.h}

interface

uses
  sysutils, Classes, math, md5,  chepersy, mo_hub, typinfo, mo_classes,
    mo_gmathbase, mo_resources;

procedure RegClasses;

{
MrShoor	Удалить	Постоялец	www	«»	12 янв. 2014	7:54	#7
Cheb
На всякий случай, у FBO есть еще одна проблема, в которую пока ты не вступил. Как повторить:
1. Создаем FBO и аттачим в него COLOR и DEPHT24_STENCIL8 текстуру.
2. Рисуем что-то
3. Деаттачим из этого FBO наш COLOR и DEPHT24_STENCIL8
4. Аттачим заново. Наблюдаем в DEPHT24_STENCIL8 мусор на многих ATI карточках.

а ведь DEPHT24_STENCIL8 иногда надо пропихнуть в другой FBO, чтобы он пошел в связке с другими COLOR-ами.
Решение - создавать один FBO, и в нем менять COLOR текстуры не трогая DEPHT24_STENCIL8
}

type

  TFboDepthRenderbuffer = class (TGenericDynamicResource)
  protected
    f_format: GLuint;
    f_width, f_height: integer;
  public
    property width: integer read f_width;
    property height: integer read f_height;
    constructor Create(w, h: integer; _format: GLuint);
    procedure RegisterFields; override;

    procedure FreeResource; override;
    procedure GenResource; override;
  end;

  TFbo = class (TGenericDynamicResource)
  protected
    f_texture: TDynamic2dTexture;
    f_depthbuffer: TFboDepthRenderbuffer;
    procedure CreateBuffers (w, h: integer); virtual;
    procedure Fallback; virtual;
  public
    property texture: TDynamic2dTexture read f_texture;
    property depthbuffer: TFboDepthRenderbuffer read f_depthbuffer;
    constructor Create(_width, _height: integer);
    destructor Destroy; override;
    procedure RegisterFields; override;

    procedure Bind; virtual;

    function Width: integer;
    function Height: integer;

    procedure FreeResource; override;
    procedure GenResource; override;

    function TextureFormat: GLuint; virtual;
    function TextureImageFormat: GLuint; virtual; //the strict EXT version fails without setting it :(
    function DepthBufferFormat: GLuint; virtual;

  end;


implementation
  uses mo_module;

{ TFboDepthRenderbuffer }

constructor TFboDepthRenderbuffer.Create(w, h: integer; _format: GLuint);
begin
  Assert(Assigned(@glGenRenderbuffers), '@glGenRenderbuffers = NIL');
  f_format:= _format;
  f_width:= w;
  f_height:= h;
  GenResource;
end;

procedure TFboDepthRenderbuffer.RegisterFields;
begin
  inherited;
  ListFields ([
    'f_format', @f_format, typeinfo(GLuint),
    'f_width', @f_width,
    'f_height', @f_height, typeinfo(integer)
  ]);
end;

procedure TFboDepthRenderbuffer.FreeResource;
begin
  glDeleteRenderbuffers(1, @_handle);
end;

procedure TFboDepthRenderbuffer.GenResource;
begin
  glGenRenderbuffers(1, @_handle);
  CheckGlError;
  glBindRenderbuffer(GL_RENDERBUFFER_EXT, _handle);
  CheckGlError;
  glRenderbufferStorage(GL_RENDERBUFFER_EXT, f_format, f_width, f_height);
  CheckGlError;
end;

{ TFbo }


procedure TFbo.CreateBuffers (w, h: integer);
begin
  try
    f_texture:= TDynamic2dTexture.Create(w, h, TextureFormat, false, GL_LINEAR, GL_LINEAR, TextureImageFormat);
    f_depthbuffer:= TFboDepthRenderBuffer.Create(w, h, DepthBufferFormat);
    SetLength(Depends, 2);
    Depends[0]:= f_texture;
    Depends[1]:= f_depthbuffer;
  except
    if (w <> 256) or (h <> 256) then begin
      AddLog(
        RuEn(
          'Не удалось создать текстуру фреймбуфера %0x%1, попытка отката на 256x256'#10#13#10#13'%2',
          'Failed to create a framebuffer texture %0x%1, trying to fall back to 256x256'#10#13#10#13'%2'),
        [w, h, StopDying()]);
      Fallback;
    end
    else
      Die(
        RuEn(
          'Не удалось создать текстуру фреймбуфера',
          'Failed to create a framebuffer texture'));
  end;
end;

constructor TFbo.Create(_width, _height: integer);
begin
  CreateBuffers(_width, _height);
  if _handle = 0 then GenResource;
end;

procedure TFbo.FreeResource;
begin
  if _handle = 0 then Exit;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
  glDeleteFramebuffers(1, @_handle);
  _handle:= 0;
end;

procedure TFbo.Bind;
begin
  glBindTexture(GL_TEXTURE_2D, 0);
  glBindFramebuffer(GL_FRAMEBUFFER_EXT, _handle);
end;

procedure TFbo.GenResource;
var
  status: GLenum;
  w, h: integer;
begin
  if Assigned(Module) and Assigned(Module.QualityAssurer) then Module.QualityAssurer.Suspend;
  glGenFramebuffers(1, @_handle);
  glBindFramebuffer(GL_FRAMEBUFFER_EXT, _handle);
  //Attach 2D texture to this FBO
 { if not texture.IsResident then Die(RuEn(
    'Не удалось создать объект фреймбуфера, текстура для рендеринга нерезидентна',
    'Failed to create a framebuffer object, the render texture is not resident'));
  }
  texture.Bind;
  glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, f_texture.Handle, 0);

  glBindRenderbuffer(GL_RENDERBUFFER_EXT, f_depthbuffer.Handle);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, f_depthbuffer.Handle);
addlog('TFbo.GenResource: %0 x %1',[texture.width, texture.height]);
  status:= glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
  if status <> GL_FRAMEBUFFER_COMPLETE_EXT  then begin
    w:= Width;
    h:= Height;
    FreeResource;
//    MotherState^.StateTrashedRestartRequired:= Yes;
//    MotherState^.ExitRequested:= Yes; //cuz opengl just hangs
    if (w <> 256) or (h <> 256) then begin
      AddLog(
        RuEn(
          'Не удалось создать объект фреймбуфера %1x%2 (код %0), попытка отката на 256x256',
          'Failed to create a framebuffer object %1x%2(code %0), trying to fall back to 256x256'),
        [intToHex(status, 8), w, h]);
      Fallback;
    end
    else
      Die(
        RuEn(
          'Не удалось создать объект фреймбуфера, код %0',
          'Failed to create a framebuffer object, code %0'),
        [intToHex(status, 8)]);
  end;
  glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
end;

procedure TFbo.Fallback;
begin
  if Assigned(f_texture) then f_texture.Free;
  if Assigned(f_depthbuffer) then f_depthbuffer.Free;
  Module.QualityAssurer.EmergencyDrop;
  CreateBuffers(256, 256);
  GenResource;
end;

function TFbo.TextureFormat: GLuint;
begin
  Result:= GL_RGBA16;//GL_RGBA8;
  //guess what, the ancient gF FX 5200 can't rgba8 but can rgba16
  //*** TODO: make it a program-wide check, to determine best format automatically
end;

function TFbo.TextureImageFormat: GLuint;
begin
  Result:= GL_bgra;//GL_BGRA;
  //W.T.F ? Any other values result in GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT
end;

function TFbo.DepthBufferFormat: GLuint;
begin
  Result:= GL_DEPTH_COMPONENT24; //GL_DEPTH_COMPONENT24;
end;



procedure TFbo.RegisterFields;
begin
  inherited;
  ListFields([
    'texture', @f_texture, typeinfo(TDynamic2dTexture),
    'depth_buffer', @f_depthbuffer, typeinfo(TFboDepthRenderbuffer)
  ]);
end;

destructor TFbo.Destroy;
begin
  if Assigned(f_texture) then f_texture.Free;
  if Assigned(f_depthbuffer) then f_depthbuffer.Free;
  SetLength(Depends, 0);
  inherited;
end;

function TFbo.Width: integer;
begin
  if not Assigned(f_texture) then Exit(0);
  Result:= f_texture.Width;
end;

function TFbo.Height: integer;
begin
  if not Assigned(f_texture) then Exit(0);
  Result:= f_texture.Height;
end;

procedure RegClasses;
begin
  RegClass(TFboDepthRenderbuffer);
  RegClass(TFbo);
end;



end.
