GXS.DynamicTexture.pas 7.8 KB

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