@flighthq/render-gl 0.3.0-next.1032.e2b99fc → 0.3.0-next.1220.6982c21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/contract.d.ts +4 -0
  2. package/dist/contract.d.ts.map +1 -1
  3. package/dist/contract.js +4 -0
  4. package/dist/contract.js.map +1 -1
  5. package/dist/enableGlRenderStateGuards.d.ts +4 -0
  6. package/dist/enableGlRenderStateGuards.d.ts.map +1 -0
  7. package/dist/enableGlRenderStateGuards.js +30 -0
  8. package/dist/enableGlRenderStateGuards.js.map +1 -0
  9. package/dist/enableGlTextureResolverGuards.d.ts +4 -0
  10. package/dist/enableGlTextureResolverGuards.d.ts.map +1 -0
  11. package/dist/enableGlTextureResolverGuards.js +8 -0
  12. package/dist/enableGlTextureResolverGuards.js.map +1 -0
  13. package/dist/explainGlTextureResolution.d.ts +3 -0
  14. package/dist/explainGlTextureResolution.d.ts.map +1 -0
  15. package/dist/explainGlTextureResolution.js +12 -0
  16. package/dist/explainGlTextureResolution.js.map +1 -0
  17. package/dist/glCompressedTexture.js +1 -1
  18. package/dist/glCompressedTexture.js.map +1 -1
  19. package/dist/glDraw.d.ts +2 -2
  20. package/dist/glDraw.d.ts.map +1 -1
  21. package/dist/glDraw.js +17 -14
  22. package/dist/glDraw.js.map +1 -1
  23. package/dist/glExternalTexture.d.ts.map +1 -1
  24. package/dist/glExternalTexture.js +23 -18
  25. package/dist/glExternalTexture.js.map +1 -1
  26. package/dist/glFullscreenPass.js +3 -4
  27. package/dist/glFullscreenPass.js.map +1 -1
  28. package/dist/glLinearToSrgbPass.js +3 -3
  29. package/dist/glLinearToSrgbPass.js.map +1 -1
  30. package/dist/glPresentRenderTarget.js +3 -3
  31. package/dist/glPresentRenderTarget.js.map +1 -1
  32. package/dist/glRenderPass.d.ts.map +1 -1
  33. package/dist/glRenderPass.js +82 -36
  34. package/dist/glRenderPass.js.map +1 -1
  35. package/dist/glRenderState.d.ts +11 -1
  36. package/dist/glRenderState.d.ts.map +1 -1
  37. package/dist/glRenderState.js +180 -5
  38. package/dist/glRenderState.js.map +1 -1
  39. package/dist/glRenderStateBracket.d.ts.map +1 -1
  40. package/dist/glRenderStateBracket.js +16 -4
  41. package/dist/glRenderStateBracket.js.map +1 -1
  42. package/dist/glRenderTarget.d.ts +10 -1
  43. package/dist/glRenderTarget.d.ts.map +1 -1
  44. package/dist/glRenderTarget.js +129 -60
  45. package/dist/glRenderTarget.js.map +1 -1
  46. package/dist/glRenderTargetPool.d.ts +4 -1
  47. package/dist/glRenderTargetPool.d.ts.map +1 -1
  48. package/dist/glRenderTargetPool.js +32 -23
  49. package/dist/glRenderTargetPool.js.map +1 -1
  50. package/dist/glRenderTexture.d.ts +4 -1
  51. package/dist/glRenderTexture.d.ts.map +1 -1
  52. package/dist/glRenderTexture.js +75 -17
  53. package/dist/glRenderTexture.js.map +1 -1
  54. package/dist/glRenderTexturePool.d.ts +7 -0
  55. package/dist/glRenderTexturePool.d.ts.map +1 -0
  56. package/dist/glRenderTexturePool.js +87 -0
  57. package/dist/glRenderTexturePool.js.map +1 -0
  58. package/dist/glTestHelper.js +2 -2
  59. package/dist/glTestHelper.js.map +1 -1
  60. package/dist/glTextureResolver.d.ts +3 -3
  61. package/dist/glTextureResolver.d.ts.map +1 -1
  62. package/dist/glTextureResolver.js +32 -31
  63. package/dist/glTextureResolver.js.map +1 -1
  64. package/dist/glTextureUpload.d.ts +2 -2
  65. package/dist/glTextureUpload.d.ts.map +1 -1
  66. package/dist/glTextureUpload.js +1 -1
  67. package/dist/glTextureUpload.js.map +1 -1
  68. package/dist/glTextureVideoUpload.d.ts +2 -2
  69. package/dist/glTextureVideoUpload.d.ts.map +1 -1
  70. package/dist/glTextureVideoUpload.js +3 -3
  71. package/dist/glTextureVideoUpload.js.map +1 -1
  72. package/dist/index.d.ts +1 -1
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +1 -1
  75. package/dist/index.js.map +1 -1
  76. package/package.json +9 -7
  77. package/src/enableGlRenderStateGuards.test.ts +27 -0
  78. package/src/enableGlTextureResolverGuards.test.ts +58 -0
  79. package/src/explainGlTextureResolution.test.ts +32 -0
  80. package/src/glCompressedTexture.test.ts +2 -2
  81. package/src/glDraw.test.ts +17 -18
  82. package/src/glExternalTexture.test.ts +3 -3
  83. package/src/glFullscreenPass.test.ts +13 -0
  84. package/src/glLinearToSrgbPass.test.ts +13 -0
  85. package/src/glPresentRenderTarget.test.ts +15 -1
  86. package/src/glReadback.test.ts +13 -0
  87. package/src/glRenderPass.test.ts +63 -0
  88. package/src/glRenderState.test.ts +135 -0
  89. package/src/glRenderStateBracket.test.ts +31 -1
  90. package/src/glRenderTarget.test.ts +186 -3
  91. package/src/glRenderTargetPool.test.ts +110 -5
  92. package/src/glRenderTexture.test.ts +71 -11
  93. package/src/glRenderTexturePool.test.ts +112 -0
  94. package/src/glTextureResolver.test.ts +73 -51
  95. package/src/glTextureUpload.test.ts +2 -2
  96. package/src/glTextureVideoUpload.test.ts +5 -5
@@ -0,0 +1,112 @@
1
+ import { explainGlRenderTexture, isGlRenderTextureReady, writeGlRenderTextureTarget } from './glRenderTexture';
2
+ import {
3
+ acquireGlRenderTexture,
4
+ createGlRenderTexturePool,
5
+ destroyGlRenderTexturePool,
6
+ releaseGlRenderTexture,
7
+ withGlRenderTextures,
8
+ } from './glRenderTexturePool';
9
+ import { createGlState } from './glTestHelper';
10
+
11
+ describe('acquireGlRenderTexture', () => {
12
+ it('reuses a released handle and applies current dimensions on every acquisition', () => {
13
+ const { state } = createGlState();
14
+ const pool = createGlRenderTexturePool();
15
+ const first = acquireGlRenderTexture(state, pool, { width: 32, height: 16 });
16
+ writeGlRenderTextureTarget(state, first, () => {});
17
+ releaseGlRenderTexture(state, pool, first);
18
+
19
+ expect(explainGlRenderTexture(state, first).status).toBe('released');
20
+ expect(isGlRenderTextureReady(state, first)).toBe(false);
21
+
22
+ const second = acquireGlRenderTexture(state, pool, { width: 80, height: 48 });
23
+ expect(second).toBe(first);
24
+ expect(second.source.width).toBe(80);
25
+ expect(second.source.height).toBe(48);
26
+ expect(explainGlRenderTexture(state, second).status).toBe('unrendered');
27
+
28
+ writeGlRenderTextureTarget(state, second, () => {});
29
+ expect(explainGlRenderTexture(state, second)).toEqual({ width: 80, height: 48, status: 'ready' });
30
+ });
31
+
32
+ it('does not allow a pool to cross WebGL contexts', () => {
33
+ const a = createGlState();
34
+ const b = createGlState();
35
+ const pool = createGlRenderTexturePool();
36
+ acquireGlRenderTexture(a.state, pool, { width: 8, height: 8 });
37
+
38
+ expect(() => acquireGlRenderTexture(b.state, pool, { width: 8, height: 8 })).toThrow('cannot cross WebGL contexts');
39
+ });
40
+ });
41
+
42
+ describe('createGlRenderTexturePool', () => {
43
+ it('creates an empty usable pool', () => {
44
+ const pool = createGlRenderTexturePool();
45
+ expect(pool.context).toBeNull();
46
+ expect(pool.destroyed).toBe(false);
47
+ expect(pool.free).toEqual([]);
48
+ expect(pool.leased.size).toBe(0);
49
+ });
50
+ });
51
+
52
+ describe('destroyGlRenderTexturePool', () => {
53
+ it('destroys hidden targets for both free and outstanding leases at shutdown', () => {
54
+ const { state, gl } = createGlState();
55
+ const pool = createGlRenderTexturePool();
56
+ const free = acquireGlRenderTexture(state, pool, { width: 8, height: 8 });
57
+ const leased = acquireGlRenderTexture(state, pool, { width: 16, height: 16 });
58
+ writeGlRenderTextureTarget(state, free, () => {});
59
+ writeGlRenderTextureTarget(state, leased, () => {});
60
+ releaseGlRenderTexture(state, pool, free);
61
+ const deletes = vi.mocked(gl.deleteTexture).mock.calls.length;
62
+
63
+ destroyGlRenderTexturePool(state, pool);
64
+
65
+ expect(gl.deleteTexture).toHaveBeenCalledTimes(deletes + 2);
66
+ expect(pool.free).toHaveLength(0);
67
+ expect(pool.leased.size).toBe(0);
68
+ });
69
+
70
+ it('rejects acquisition after destruction', () => {
71
+ const { state } = createGlState();
72
+ const pool = createGlRenderTexturePool();
73
+ destroyGlRenderTexturePool(state, pool);
74
+ expect(() => acquireGlRenderTexture(state, pool, { width: 8, height: 8 })).toThrow('has been destroyed');
75
+ });
76
+ });
77
+
78
+ describe('releaseGlRenderTexture', () => {
79
+ it('rejects a double release', () => {
80
+ const { state } = createGlState();
81
+ const pool = createGlRenderTexturePool();
82
+ const texture = acquireGlRenderTexture(state, pool, { width: 8, height: 8 });
83
+ releaseGlRenderTexture(state, pool, texture);
84
+
85
+ expect(() => releaseGlRenderTexture(state, pool, texture)).toThrow('texture is not leased');
86
+ });
87
+ });
88
+
89
+ describe('withGlRenderTextures', () => {
90
+ it('releases every lease when the bracket callback throws', () => {
91
+ const { state } = createGlState();
92
+ const pool = createGlRenderTexturePool();
93
+
94
+ expect(() =>
95
+ withGlRenderTextures(
96
+ state,
97
+ pool,
98
+ [
99
+ { width: 8, height: 8 },
100
+ { width: 8, height: 8 },
101
+ { width: 8, height: 8 },
102
+ ],
103
+ () => {
104
+ throw new Error('capture failed');
105
+ },
106
+ ),
107
+ ).toThrow('capture failed');
108
+
109
+ expect(pool.leased.size).toBe(0);
110
+ expect(pool.free).toHaveLength(3);
111
+ });
112
+ });
@@ -1,10 +1,10 @@
1
- import type { ImageResource, RenderTexture, TextureLike } from '@flighthq/types/contract';
1
+ import { createEntity } from '@flighthq/entity/contract';
2
+ import type { Image, RenderTexture, TextureLike, TextureSource } from '@flighthq/types/contract';
2
3
  import {
3
- BitmapTextureBackingKind,
4
- CompressedImageTextureBackingKind,
5
- ImageTextureBackingKind,
6
- RenderTextureBackingKind,
7
- VideoTextureBackingKind,
4
+ BitmapTextureSourceKind,
5
+ CompressedImageTextureSourceKind,
6
+ ImageTextureSourceKind,
7
+ RenderTargetTextureSourceKind,
8
8
  } from '@flighthq/types/contract';
9
9
 
10
10
  import { getGlRenderStateRuntime } from './glRenderState';
@@ -15,12 +15,12 @@ import {
15
15
  registerGlBitmapTextureResolver,
16
16
  registerGlCompressedImageTextureResolver,
17
17
  registerGlRenderTextureResolver,
18
+ registerStandardGlTextureResolvers,
18
19
  registerGlTextureResolver,
19
- registerGlVideoTextureResolver,
20
20
  resolveGlTexture,
21
21
  } from './glTextureResolver';
22
22
 
23
- function textureWithImage(image: ImageResource | null): TextureLike {
23
+ function textureWithImage(image: TextureSource | null): TextureLike {
24
24
  return {
25
25
  colorSpace: 'srgb',
26
26
  flipX: false,
@@ -34,7 +34,8 @@ function textureWithImage(image: ImageResource | null): TextureLike {
34
34
  wrapU: 'clamp-to-edge',
35
35
  wrapV: 'clamp-to-edge',
36
36
  },
37
- storage: { dimension: '2d', image },
37
+ dimension: '2d',
38
+ source: image,
38
39
  uvOffset: { x: 0, y: 0 },
39
40
  uvRotation: 0,
40
41
  uvScale: { x: 1, y: 1 },
@@ -42,46 +43,59 @@ function textureWithImage(image: ImageResource | null): TextureLike {
42
43
  } as unknown as TextureLike;
43
44
  }
44
45
 
45
- function imageResource(
46
- source: CanvasImageSource = document.createElement('img'),
47
- kind = ImageTextureBackingKind,
48
- ): ImageResource {
46
+ function imageResource(source: CanvasImageSource = document.createElement('img')): Image {
49
47
  return {
50
48
  height: 1,
51
- kind,
49
+ kind: ImageTextureSourceKind,
52
50
  source,
53
51
  version: 0,
54
52
  width: 1,
55
- } as unknown as ImageResource;
53
+ } as unknown as Image;
54
+ }
55
+
56
+ function textureSource(kind: string): TextureSource {
57
+ return {
58
+ height: 1,
59
+ kind,
60
+ version: 0,
61
+ width: 1,
62
+ } as unknown as TextureSource;
56
63
  }
57
64
 
58
65
  function textureWithTarget(): TextureLike {
59
66
  const texture = textureWithImage(null);
60
67
  texture.colorSpace = 'linear';
61
- texture.storage.target = { colorSpace: 'linear', height: 8, kind: RenderTextureBackingKind, width: 8 };
68
+ if (texture.dimension !== '2d') throw new Error('test texture must be 2d');
69
+ texture.source = createEntity({
70
+ colorSpace: 'linear' as const,
71
+ height: 8,
72
+ kind: RenderTargetTextureSourceKind,
73
+ version: 0,
74
+ width: 8,
75
+ });
62
76
  return texture;
63
77
  }
64
78
 
65
79
  describe('registerGlBitmapTextureResolver', () => {
66
- it('registers only the Bitmap backing key', () => {
80
+ it('registers only the Bitmap source key', () => {
67
81
  const { state } = createGlState();
68
82
  registerGlBitmapTextureResolver(state);
69
- expect([...getGlRenderStateRuntime(state).glTextureResolverRegistry!.keys()]).toEqual([BitmapTextureBackingKind]);
83
+ expect([...getGlRenderStateRuntime(state).glTextureResolverRegistry!.keys()]).toEqual([BitmapTextureSourceKind]);
70
84
  });
71
85
  });
72
86
 
73
87
  describe('registerGlCompressedImageTextureResolver', () => {
74
- it('registers only the CompressedImage backing key', () => {
88
+ it('registers only the CompressedImage source key', () => {
75
89
  const { state } = createGlState();
76
90
  registerGlCompressedImageTextureResolver(state);
77
91
  expect([...getGlRenderStateRuntime(state).glTextureResolverRegistry!.keys()]).toEqual([
78
- CompressedImageTextureBackingKind,
92
+ CompressedImageTextureSourceKind,
79
93
  ]);
80
94
  });
81
95
  });
82
96
 
83
97
  describe('registerGlImageTextureResolver', () => {
84
- it('registers the 2D image matcher and resolves through the backing-keyed upload cache', () => {
98
+ it('registers the 2D image matcher and resolves through the source-keyed upload cache', () => {
85
99
  const { state, gl } = createGlState();
86
100
  const texture = textureWithImage(imageResource());
87
101
  registerGlImageTextureResolver(state);
@@ -94,11 +108,31 @@ describe('registerGlImageTextureResolver', () => {
94
108
  expect(gl.texImage2D).toHaveBeenCalledOnce();
95
109
  });
96
110
 
97
- it('returns null for an unbound image backing', () => {
111
+ it('returns null for an unbound image source', () => {
98
112
  const { state } = createGlState();
99
113
  registerGlImageTextureResolver(state);
100
114
  expect(resolveGlTexture(state, textureWithImage(null))).toBeNull();
101
115
  });
116
+
117
+ it('uploads a host video through the image source kind and gates by version', () => {
118
+ const { state, gl } = createGlState();
119
+ const video = imageResource({
120
+ readyState: 4,
121
+ videoHeight: 240,
122
+ videoWidth: 320,
123
+ } as HTMLVideoElement);
124
+ const texture = textureWithImage(video);
125
+ registerGlImageTextureResolver(state);
126
+
127
+ const first = resolveGlTexture(state, texture);
128
+ const uploads = (gl.texImage2D as ReturnType<typeof vi.fn>).mock.calls.length;
129
+ const second = resolveGlTexture(state, texture);
130
+
131
+ expect(first).not.toBeNull();
132
+ expect(second).toBe(first);
133
+ expect(gl.texImage2D).toHaveBeenCalledWith(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video.source);
134
+ expect(gl.texImage2D).toHaveBeenCalledTimes(uploads);
135
+ });
102
136
  });
103
137
 
104
138
  describe('registerGlRenderTextureResolver', () => {
@@ -123,59 +157,47 @@ describe('registerGlTextureResolver', () => {
123
157
  it('keeps registrations state-scoped, replaces by string kind, and removes with null', () => {
124
158
  const { state: a } = createGlState();
125
159
  const { state: b } = createGlState();
126
- const backingKind = 'acme.generated';
160
+ const sourceKind = 'acme.generated';
127
161
  const first = vi.fn(() => ({ first: true }) as unknown as WebGLTexture);
128
162
  const second = vi.fn(() => ({ second: true }) as unknown as WebGLTexture);
129
- const image = imageResource();
130
- image.kind = backingKind;
131
- const texture = textureWithImage(image);
163
+ const source = textureSource(sourceKind);
164
+ const texture = textureWithImage(source);
132
165
 
133
- registerGlTextureResolver(a, backingKind, first);
166
+ registerGlTextureResolver(a, sourceKind, first);
134
167
  expect(resolveGlTexture(a, texture)).not.toBeNull();
135
168
  expect(resolveGlTexture(b, texture)).toBeNull();
136
169
 
137
- registerGlTextureResolver(a, backingKind, second);
170
+ registerGlTextureResolver(a, sourceKind, second);
138
171
  expect(resolveGlTexture(a, texture)).toEqual({ second: true });
139
172
  expect(getGlRenderStateRuntime(a).glTextureResolverRegistry?.size).toBe(1);
140
173
 
141
- registerGlTextureResolver(a, backingKind, null);
174
+ registerGlTextureResolver(a, sourceKind, null);
142
175
  expect(resolveGlTexture(a, texture)).toBeNull();
143
176
  });
144
177
 
145
178
  it('uses one exact keyed lookup and does not fall through to another kind', () => {
146
179
  const { state } = createGlState();
147
- const image = imageResource();
148
- image.kind = 'acme.specific';
180
+ const image = textureSource('acme.specific');
149
181
  registerGlTextureResolver(state, 'image', () => ({ kind: 'image' }) as unknown as WebGLTexture);
150
182
  registerGlTextureResolver(state, 'acme.specific', () => ({ kind: 'specific' }) as unknown as WebGLTexture);
151
183
  expect(resolveGlTexture(state, textureWithImage(image))).toEqual({ kind: 'specific' });
152
184
  });
153
185
  });
154
186
 
155
- describe('registerGlVideoTextureResolver', () => {
156
- it('specializes the general image resolver and gates uploads by backing version', () => {
157
- const { state, gl } = createGlState();
158
- const video = imageResource(
159
- { readyState: 4, videoHeight: 240, videoWidth: 320 } as HTMLVideoElement,
160
- VideoTextureBackingKind,
161
- );
162
- const texture = textureWithImage(video);
163
- registerGlImageTextureResolver(state);
164
- registerGlVideoTextureResolver(state);
165
-
166
- const first = resolveGlTexture(state, texture);
167
- const uploads = (gl.texImage2D as ReturnType<typeof vi.fn>).mock.calls.length;
168
- const second = resolveGlTexture(state, texture);
169
-
170
- expect(first).not.toBeNull();
171
- expect(second).toBe(first);
172
- expect(gl.texImage2D).toHaveBeenCalledWith(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video.source);
173
- expect(gl.texImage2D).toHaveBeenCalledTimes(uploads);
187
+ describe('registerStandardGlTextureResolvers', () => {
188
+ it('registers bitmap, image, and render texture sources without compressed images', () => {
189
+ const { state } = createGlState();
190
+ registerStandardGlTextureResolvers(state);
191
+ expect([...getGlRenderStateRuntime(state).glTextureResolverRegistry!.keys()]).toEqual([
192
+ BitmapTextureSourceKind,
193
+ ImageTextureSourceKind,
194
+ RenderTargetTextureSourceKind,
195
+ ]);
174
196
  });
175
197
  });
176
198
 
177
199
  describe('resolveGlTexture', () => {
178
- it('returns null when the state has no registered backing resolver', () => {
200
+ it('returns null when the state has no registered source resolver', () => {
179
201
  const { state } = createGlState();
180
202
  expect(resolveGlTexture(state, textureWithImage(imageResource()))).toBeNull();
181
203
  });
@@ -1,4 +1,4 @@
1
- import type { ImageResource } from '@flighthq/types/contract';
1
+ import type { Image } from '@flighthq/types/contract';
2
2
 
3
3
  import { uploadGlTextureData, uploadGlTextureElement, uploadGlTextureImageResource } from './glTextureUpload';
4
4
 
@@ -34,7 +34,7 @@ describe('uploadGlTextureImageResource', () => {
34
34
  it('takes the element path when the resource carries a source', () => {
35
35
  const gl = makeGl();
36
36
  const source = {} as TexImageSource;
37
- const image = { source, width: 4, height: 4 } as unknown as ImageResource;
37
+ const image = { source, width: 4, height: 4 } as unknown as Image;
38
38
  uploadGlTextureImageResource(gl, gl.TEXTURE_2D, image);
39
39
  expect(gl.texImage2D).toHaveBeenCalledWith(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
40
40
  });
@@ -1,4 +1,4 @@
1
- import type { ImageResource } from '@flighthq/types/contract';
1
+ import type { Image } from '@flighthq/types/contract';
2
2
 
3
3
  import { uploadGlTextureVideoFrame } from './glTextureVideoUpload';
4
4
 
@@ -11,12 +11,12 @@ function makeGl(): WebGL2RenderingContext {
11
11
  } as unknown as WebGL2RenderingContext;
12
12
  }
13
13
 
14
- // A video ImageResource whose only relevant fields are version and the borrowed host element.
15
- function makeVideoImage(version: number, readyState = 4, videoWidth = 320, videoHeight = 240): ImageResource {
14
+ // A video Image whose only relevant fields are version and the borrowed host element.
15
+ function makeVideoImage(version: number, readyState = 4, videoWidth = 320, videoHeight = 240): Image {
16
16
  return {
17
17
  source: { readyState, videoWidth, videoHeight } as unknown as HTMLVideoElement,
18
18
  version,
19
- } as unknown as ImageResource;
19
+ } as unknown as Image;
20
20
  }
21
21
 
22
22
  describe('uploadGlTextureVideoFrame', () => {
@@ -46,7 +46,7 @@ describe('uploadGlTextureVideoFrame', () => {
46
46
 
47
47
  it('skips when the element is null', () => {
48
48
  const gl = makeGl();
49
- const image = { source: null, version: 2 } as unknown as ImageResource;
49
+ const image = { source: null, version: 2 } as unknown as Image;
50
50
  expect(uploadGlTextureVideoFrame(gl, image, -1)).toBe(-1);
51
51
  expect(gl.texImage2D).not.toHaveBeenCalled();
52
52
  });