2
0

GLDynamicTexture.pas 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. (*
  5. Adds a dynamic texture image, which allows for easy updating of
  6. texture data.
  7. *)
  8. unit GLDynamicTexture;
  9. interface
  10. {$I GLScene.inc}
  11. uses
  12. System.Types,
  13. System.Classes,
  14. System.SysUtils,
  15. OpenGLTOkens,
  16. GLCrossPlatform,
  17. GLContext,
  18. GLTexture,
  19. GLTextureFormat,
  20. GLGraphics;
  21. type
  22. // Allows for fast updating of the texture at runtime
  23. TGLDynamicTextureImage = class(TGLBlankImage)
  24. private
  25. FUpdating: integer;
  26. FTexSize: integer;
  27. FBuffer: pointer;
  28. FPBO: TGLBufferObjectHandle;
  29. FData: pointer;
  30. FDirtyRect: TRect;
  31. FUseBGR: boolean;
  32. FUsePBO: boolean;
  33. procedure SetDirtyRectangle(const Value: TRect);
  34. procedure SetUsePBO(const Value: boolean);
  35. protected
  36. function GetTexSize: integer;
  37. function GetBitsPerPixel: integer;
  38. function GetDataFormat: integer;
  39. function GetTextureFormat: integer;
  40. procedure FreePBO;
  41. procedure FreeBuffer;
  42. property BitsPerPixel: integer read GetBitsPerPixel;
  43. property DataFormat: integer read GetDataFormat;
  44. property TextureFormat: integer read GetTextureFormat;
  45. public
  46. constructor Create(AOwner: TPersistent); override;
  47. class function FriendlyName : String; override;
  48. class function FriendlyDescription : String; override;
  49. procedure NotifyChange(Sender: TObject); override;
  50. // Must be called before using the Data pointer. Rendering context must be active!
  51. procedure BeginUpdate;
  52. // Must be called after data is changed. This will upload the new data
  53. procedure EndUpdate;
  54. // Pointer to buffer data. Will be nil outside a BeginUpdate / EndUpdate block
  55. property Data: pointer read FData;
  56. (* Marks the dirty rectangle inside the texture. BeginUpdate sets
  57. it to ((0, 0), (Width, Height)), ie the entire texture.
  58. Override it if you're only changing a small piece of the texture.
  59. Note that the Data pointer is relative to the DirtyRectangle,
  60. NOT the entire texture. *)
  61. property DirtyRectangle: TRect read FDirtyRect write SetDirtyRectangle;
  62. // Indicates that the data is stored as BGR(A) instead of RGB(A). The default is BGR(A)
  63. property UseBGR: boolean read FUseBGR write FUseBGR;
  64. // Enables or disables use of a PBO. Default is true
  65. property UsePBO: boolean read FUsePBO write SetUsePBO;
  66. end;
  67. //---------------------------------------------------------
  68. implementation
  69. //---------------------------------------------------------
  70. uses
  71. GLVectorGeometry;
  72. { TGLDynamicTextureImage }
  73. procedure TGLDynamicTextureImage.BeginUpdate;
  74. var
  75. LTarget: TGLTextureTarget;
  76. begin
  77. Assert(FUpdating >= 0, 'Unbalanced begin/end update');
  78. FUpdating:= FUpdating + 1;
  79. if FUpdating > 1 then
  80. exit;
  81. // initialization
  82. if not (assigned(FPBO) or assigned(FBuffer)) then
  83. begin
  84. // cache so we know if it's changed
  85. FTexSize:= GetTexSize;
  86. if FUsePBO and TGLUnpackPBOHandle.IsSupported then
  87. begin
  88. FPBO:= TGLUnpackPBOHandle.CreateAndAllocate;
  89. // initialize buffer
  90. FPBO.BindBufferData(nil, FTexSize, GL_STREAM_DRAW_ARB);
  91. // unbind so we don't upload the data from it, which is unnecessary
  92. FPBO.UnBind;
  93. end
  94. else
  95. begin
  96. // fall back to regular memory buffer if PBO's aren't supported
  97. FBuffer:= AllocMem(FTexSize);
  98. end;
  99. // Force creation of texture
  100. // This is a bit of a hack, should be a better way...
  101. LTarget := TGLTexture(OwnerTexture).TextureHandle.Target;
  102. CurrentGLContext.GLStates.TextureBinding[0, LTarget] := TGLTexture(OwnerTexture).Handle;
  103. case LTarget of
  104. ttNoShape: ;
  105. ttTexture1D: ;
  106. ttTexture2D:
  107. gl.TexImage2D(GL_TEXTURE_2D, 0, TGLTexture(OwnerTexture).OpenGLTextureFormat, Width, Height, 0, TextureFormat, GL_UNSIGNED_BYTE, nil);
  108. ttTexture3D: ;
  109. ttTexture1DArray: ;
  110. ttTexture2DArray: ;
  111. ttTextureRect: ;
  112. ttTextureBuffer: ;
  113. ttTextureCube: ;
  114. ttTexture2DMultisample: ;
  115. ttTexture2DMultisampleArray: ;
  116. ttTextureCubeArray: ;
  117. end;
  118. end;
  119. gl.CheckError;
  120. if assigned(FPBO) then
  121. begin
  122. FPBO.Bind;
  123. FData:= FPBO.MapBuffer(GL_WRITE_ONLY_ARB);
  124. end
  125. else
  126. begin
  127. FData:= FBuffer;
  128. end;
  129. gl.CheckError;
  130. FDirtyRect:= GetGLRect(0, 0, Width, Height);
  131. end;
  132. constructor TGLDynamicTextureImage.Create(AOwner: TPersistent);
  133. begin
  134. inherited Create(AOwner);
  135. FUseBGR:= true;
  136. FUsePBO:= true;
  137. end;
  138. procedure TGLDynamicTextureImage.EndUpdate;
  139. var
  140. d: pointer;
  141. LTarget: TGLTextureTarget;
  142. begin
  143. Assert(FUpdating > 0, 'Unbalanced begin/end update');
  144. FUpdating:= FUpdating - 1;
  145. if FUpdating > 0 then
  146. exit;
  147. if assigned(FPBO) then
  148. begin
  149. FPBO.UnmapBuffer;
  150. // pointer will act as an offset when using PBO
  151. d:= nil;
  152. end
  153. else
  154. begin
  155. d:= FBuffer;
  156. end;
  157. LTarget := TGLTexture(OwnerTexture).TextureHandle.Target;
  158. CurrentGLContext.GLStates.TextureBinding[0, LTarget] := TGLTexture(OwnerTexture).Handle;
  159. case LTarget of
  160. ttNoShape: ;
  161. ttTexture1D: ;
  162. ttTexture2D:
  163. begin
  164. gl.TexSubImage2D(GL_TEXTURE_2D, 0,
  165. FDirtyRect.Left, FDirtyRect.Top,
  166. FDirtyRect.Right-FDirtyRect.Left,
  167. FDirtyRect.Bottom-FDirtyRect.Top,
  168. TextureFormat, DataFormat, d);
  169. end;
  170. ttTexture3D: ;
  171. ttTexture1DArray: ;
  172. ttTexture2DArray: ;
  173. ttTextureRect: ;
  174. ttTextureBuffer: ;
  175. ttTextureCube: ;
  176. ttTexture2DMultisample: ;
  177. ttTexture2DMultisampleArray: ;
  178. ttTextureCubeArray: ;
  179. end;
  180. if assigned(FPBO) then
  181. FPBO.UnBind;
  182. FData:= nil;
  183. gl.CheckError;
  184. end;
  185. procedure TGLDynamicTextureImage.FreeBuffer;
  186. begin
  187. if assigned(FBuffer) then
  188. begin
  189. FreeMem(FBuffer);
  190. FBuffer:= nil;
  191. end;
  192. end;
  193. procedure TGLDynamicTextureImage.FreePBO;
  194. begin
  195. if assigned(FPBO) then
  196. begin
  197. FPBO.Free;
  198. FPBO:= nil;
  199. end;
  200. end;
  201. class function TGLDynamicTextureImage.FriendlyName : String;
  202. begin
  203. Result:='Dynamic Texture';
  204. end;
  205. class function TGLDynamicTextureImage.FriendlyDescription : String;
  206. begin
  207. Result:='Dynamic Texture - optimised for changes at runtime';
  208. end;
  209. function TGLDynamicTextureImage.GetBitsPerPixel: integer;
  210. begin
  211. Result := 8 * GetTextureElementSize( TGLTexture(OwnerTexture).TextureFormatEx );
  212. end;
  213. function TGLDynamicTextureImage.GetDataFormat: integer;
  214. var
  215. data, color: Cardinal;
  216. begin
  217. FindCompatibleDataFormat(TGLTexture(OwnerTexture).TextureFormatEx, color, data);
  218. Result := data;
  219. end;
  220. function TGLDynamicTextureImage.GetTexSize: integer;
  221. begin
  222. result:= Width * Height * BitsPerPixel div 8;
  223. end;
  224. function TGLDynamicTextureImage.GetTextureFormat: integer;
  225. var
  226. data, color: Cardinal;
  227. begin
  228. FindCompatibleDataFormat(TGLTexture(OwnerTexture).TextureFormatEx, color, data);
  229. if FUseBGR then
  230. case color of
  231. GL_RGB: color := GL_BGR;
  232. GL_RGBA: color := GL_BGRA;
  233. end;
  234. Result := color;
  235. end;
  236. procedure TGLDynamicTextureImage.NotifyChange(Sender: TObject);
  237. begin
  238. if FTexSize <> GetTexSize then
  239. begin
  240. FreePBO;
  241. FreeBuffer;
  242. end;
  243. inherited;
  244. end;
  245. procedure TGLDynamicTextureImage.SetDirtyRectangle(const Value: TRect);
  246. begin
  247. FDirtyRect.Left:= MaxInteger(Value.Left, 0);
  248. FDirtyRect.Top:= MaxInteger(Value.Top, 0);
  249. FDirtyRect.Right:= MinInteger(Value.Right, Width);
  250. FDirtyRect.Bottom:= MinInteger(Value.Bottom, Height);
  251. end;
  252. procedure TGLDynamicTextureImage.SetUsePBO(const Value: boolean);
  253. begin
  254. Assert(FUpdating = 0, 'Cannot change PBO settings while updating');
  255. if FUsePBO <> Value then
  256. begin
  257. FUsePBO := Value;
  258. if not FUsePBO then
  259. FreePBO
  260. else
  261. FreeBuffer;
  262. end;
  263. end;
  264. initialization
  265. RegisterGLTextureImageClass(TGLDynamicTextureImage);
  266. end.