blob: 88db761eba2bf3584dd050968c78119ca512afca [file] [log] [blame]
Jason Sams536923d2010-05-18 13:35:45 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "rsContext.h"
18#include "rsScriptC.h"
19#include "rsMatrix.h"
Jason Sams536923d2010-05-18 13:35:45 -070020
Jason Sams536923d2010-05-18 13:35:45 -070021#include "utils/Timers.h"
22
23#define GL_GLEXT_PROTOTYPES
24
25#include <GLES/gl.h>
26#include <GLES/glext.h>
27#include <GLES2/gl2.h>
28#include <GLES2/gl2ext.h>
29
30#include <time.h>
31
32using namespace android;
33using namespace android::renderscript;
34
35#define GET_TLS() Context::ScriptTLSStruct * tls = \
36 (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \
37 Context * rsc = tls->mContext; \
38 ScriptC * sc = (ScriptC *) tls->mScript
39
40
41//////////////////////////////////////////////////////////////////////////////
Jason Sams536923d2010-05-18 13:35:45 -070042// Context
43//////////////////////////////////////////////////////////////////////////////
44
45static void SC_bindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va)
46{
Jason Samsf166d9b2010-09-30 18:15:52 -070047 CHECK_OBJ_OR_NULL(va);
48 CHECK_OBJ(vpf);
Jason Sams536923d2010-05-18 13:35:45 -070049 GET_TLS();
50 rsi_ProgramBindTexture(rsc,
51 static_cast<ProgramFragment *>(vpf),
52 slot,
53 static_cast<Allocation *>(va));
54
55}
56
57static void SC_bindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs)
58{
Jason Samsf166d9b2010-09-30 18:15:52 -070059 CHECK_OBJ_OR_NULL(vs);
60 CHECK_OBJ(vpf);
Jason Sams536923d2010-05-18 13:35:45 -070061 GET_TLS();
62 rsi_ProgramBindSampler(rsc,
63 static_cast<ProgramFragment *>(vpf),
64 slot,
65 static_cast<Sampler *>(vs));
66
67}
68
69static void SC_bindProgramStore(RsProgramStore pfs)
70{
Jason Samsf166d9b2010-09-30 18:15:52 -070071 CHECK_OBJ_OR_NULL(pfs);
Jason Sams536923d2010-05-18 13:35:45 -070072 GET_TLS();
73 rsi_ContextBindProgramStore(rsc, pfs);
74}
75
76static void SC_bindProgramFragment(RsProgramFragment pf)
77{
Jason Samsf166d9b2010-09-30 18:15:52 -070078 CHECK_OBJ_OR_NULL(pf);
Jason Sams536923d2010-05-18 13:35:45 -070079 GET_TLS();
80 rsi_ContextBindProgramFragment(rsc, pf);
81}
82
83static void SC_bindProgramVertex(RsProgramVertex pv)
84{
Jason Samsf166d9b2010-09-30 18:15:52 -070085 CHECK_OBJ_OR_NULL(pv);
Jason Sams536923d2010-05-18 13:35:45 -070086 GET_TLS();
87 rsi_ContextBindProgramVertex(rsc, pv);
88}
89
90static void SC_bindProgramRaster(RsProgramRaster pv)
91{
Jason Samsf166d9b2010-09-30 18:15:52 -070092 CHECK_OBJ_OR_NULL(pv);
Jason Sams536923d2010-05-18 13:35:45 -070093 GET_TLS();
94 rsi_ContextBindProgramRaster(rsc, pv);
95}
96
97//////////////////////////////////////////////////////////////////////////////
98// VP
99//////////////////////////////////////////////////////////////////////////////
100
Jim Millerd8e76202010-07-28 14:46:22 -0700101static void SC_vpLoadProjectionMatrix(const rsc_Matrix *m)
102{
103 GET_TLS();
Alex Sakhartchoukb89aaac2010-09-23 16:16:33 -0700104 rsc->getVertex()->setProjectionMatrix(rsc, m);
Jim Millerd8e76202010-07-28 14:46:22 -0700105}
106
Jason Sams536923d2010-05-18 13:35:45 -0700107static void SC_vpLoadModelMatrix(const rsc_Matrix *m)
108{
109 GET_TLS();
Alex Sakhartchoukb89aaac2010-09-23 16:16:33 -0700110 rsc->getVertex()->setModelviewMatrix(rsc, m);
Jason Sams536923d2010-05-18 13:35:45 -0700111}
112
113static void SC_vpLoadTextureMatrix(const rsc_Matrix *m)
114{
115 GET_TLS();
Alex Sakhartchoukb89aaac2010-09-23 16:16:33 -0700116 rsc->getVertex()->setTextureMatrix(rsc, m);
Jason Sams536923d2010-05-18 13:35:45 -0700117}
118
119
Jason Sams442a6472010-08-04 17:50:20 -0700120static void SC_pfConstantColor(RsProgramFragment vpf, float r, float g, float b, float a)
121{
Alex Sakhartchoukb89aaac2010-09-23 16:16:33 -0700122 GET_TLS();
Jason Samsf166d9b2010-09-30 18:15:52 -0700123 CHECK_OBJ(vpf);
Jason Sams442a6472010-08-04 17:50:20 -0700124 ProgramFragment *pf = static_cast<ProgramFragment *>(vpf);
Alex Sakhartchoukb89aaac2010-09-23 16:16:33 -0700125 pf->setConstantColor(rsc, r, g, b, a);
Jason Sams442a6472010-08-04 17:50:20 -0700126}
127
Alex Sakhartchoukcbed7522010-08-16 17:40:10 -0700128static void SC_vpGetProjectionMatrix(rsc_Matrix *m)
129{
130 GET_TLS();
Alex Sakhartchoukb89aaac2010-09-23 16:16:33 -0700131 rsc->getVertex()->getProjectionMatrix(rsc, m);
Alex Sakhartchoukcbed7522010-08-16 17:40:10 -0700132}
133
Jason Sams536923d2010-05-18 13:35:45 -0700134
135//////////////////////////////////////////////////////////////////////////////
136// Drawing
137//////////////////////////////////////////////////////////////////////////////
138
Jason Sams536923d2010-05-18 13:35:45 -0700139static void SC_drawQuadTexCoords(float x1, float y1, float z1,
140 float u1, float v1,
141 float x2, float y2, float z2,
142 float u2, float v2,
143 float x3, float y3, float z3,
144 float u3, float v3,
145 float x4, float y4, float z4,
146 float u4, float v4)
147{
148 GET_TLS();
149 if (!rsc->setupCheck()) {
150 return;
151 }
152
153 //LOGE("Quad");
154 //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1);
155 //LOGE("%4.2f, %4.2f, %4.2f", x2, y2, z2);
156 //LOGE("%4.2f, %4.2f, %4.2f", x3, y3, z3);
157 //LOGE("%4.2f, %4.2f, %4.2f", x4, y4, z4);
158
159 float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4};
160 const float tex[] = {u1,v1, u2,v2, u3,v3, u4,v4};
161
162 VertexArray va;
Alex Sakhartchouk4378f112010-09-29 09:49:13 -0700163 va.add(GL_FLOAT, 3, 12, false, (uint32_t)vtx, "ATTRIB_position");
164 va.add(GL_FLOAT, 2, 8, false, (uint32_t)tex, "ATTRIB_texture0");
Jason Sams8cb39de2010-06-01 15:47:01 -0700165 va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache);
Jason Sams536923d2010-05-18 13:35:45 -0700166
167 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
168}
169
170static void SC_drawQuad(float x1, float y1, float z1,
171 float x2, float y2, float z2,
172 float x3, float y3, float z3,
173 float x4, float y4, float z4)
174{
175 SC_drawQuadTexCoords(x1, y1, z1, 0, 1,
176 x2, y2, z2, 1, 1,
177 x3, y3, z3, 1, 0,
178 x4, y4, z4, 0, 0);
179}
180
181static void SC_drawSpriteScreenspace(float x, float y, float z, float w, float h)
182{
183 GET_TLS();
184 ObjectBaseRef<const ProgramVertex> tmp(rsc->getVertex());
185 rsc->setVertex(rsc->getDefaultProgramVertex());
186 //rsc->setupCheck();
187
188 //GLint crop[4] = {0, h, w, -h};
189
190 float sh = rsc->getHeight();
191
192 SC_drawQuad(x, sh - y, z,
193 x+w, sh - y, z,
194 x+w, sh - (y+h), z,
195 x, sh - (y+h), z);
196 rsc->setVertex((ProgramVertex *)tmp.get());
197}
Jason Sams6d1cf412010-06-17 18:05:38 -0700198/*
Jason Sams536923d2010-05-18 13:35:45 -0700199static void SC_drawSprite(float x, float y, float z, float w, float h)
200{
201 GET_TLS();
202 float vin[3] = {x, y, z};
203 float vout[4];
204
205 //LOGE("ds in %f %f %f", x, y, z);
206 rsc->getVertex()->transformToScreen(rsc, vout, vin);
207 //LOGE("ds out %f %f %f %f", vout[0], vout[1], vout[2], vout[3]);
208 vout[0] /= vout[3];
209 vout[1] /= vout[3];
210 vout[2] /= vout[3];
211
212 vout[0] *= rsc->getWidth() / 2;
213 vout[1] *= rsc->getHeight() / 2;
214 vout[0] += rsc->getWidth() / 2;
215 vout[1] += rsc->getHeight() / 2;
216
217 vout[0] -= w/2;
218 vout[1] -= h/2;
219
220 //LOGE("ds out2 %f %f %f", vout[0], vout[1], vout[2]);
221
222 // U, V, W, H
223 SC_drawSpriteScreenspace(vout[0], vout[1], z, h, w);
224 //rsc->setupCheck();
225}
Jason Sams6d1cf412010-06-17 18:05:38 -0700226*/
Jason Sams536923d2010-05-18 13:35:45 -0700227
228static void SC_drawRect(float x1, float y1,
229 float x2, float y2, float z)
230{
231 //LOGE("SC_drawRect %f,%f %f,%f %f", x1, y1, x2, y2, z);
232 SC_drawQuad(x1, y2, z,
233 x2, y2, z,
234 x2, y1, z,
235 x1, y1, z);
236}
237
Alex Sakhartchouk164aaed2010-07-01 16:14:06 -0700238static void SC_drawMesh(RsMesh vsm)
239{
Jason Samsf166d9b2010-09-30 18:15:52 -0700240 CHECK_OBJ(vsm);
Alex Sakhartchouk164aaed2010-07-01 16:14:06 -0700241 GET_TLS();
242 Mesh *sm = static_cast<Mesh *>(vsm);
Jason Sams536923d2010-05-18 13:35:45 -0700243 if (!rsc->setupCheck()) {
244 return;
245 }
246 sm->render(rsc);
247}
248
Alex Sakhartchouk164aaed2010-07-01 16:14:06 -0700249static void SC_drawMeshPrimitive(RsMesh vsm, uint32_t primIndex)
Jason Sams536923d2010-05-18 13:35:45 -0700250{
Jason Samsf166d9b2010-09-30 18:15:52 -0700251 CHECK_OBJ(vsm);
Jason Sams536923d2010-05-18 13:35:45 -0700252 GET_TLS();
Alex Sakhartchouk164aaed2010-07-01 16:14:06 -0700253 Mesh *sm = static_cast<Mesh *>(vsm);
Jason Sams536923d2010-05-18 13:35:45 -0700254 if (!rsc->setupCheck()) {
255 return;
256 }
Alex Sakhartchouk164aaed2010-07-01 16:14:06 -0700257 sm->renderPrimitive(rsc, primIndex);
258}
259
260static void SC_drawMeshPrimitiveRange(RsMesh vsm, uint32_t primIndex, uint32_t start, uint32_t len)
261{
Jason Samsf166d9b2010-09-30 18:15:52 -0700262 CHECK_OBJ(vsm);
Alex Sakhartchouk164aaed2010-07-01 16:14:06 -0700263 GET_TLS();
264 Mesh *sm = static_cast<Mesh *>(vsm);
265 if (!rsc->setupCheck()) {
266 return;
267 }
268 sm->renderPrimitiveRange(rsc, primIndex, start, len);
Jason Sams536923d2010-05-18 13:35:45 -0700269}
270
Alex Sakhartchouka80145d2010-08-13 14:32:23 -0700271static void SC_meshComputeBoundingBox(RsMesh vsm, float *minX, float *minY, float *minZ,
272 float *maxX, float *maxY, float *maxZ)
273{
Jason Samsf166d9b2010-09-30 18:15:52 -0700274 CHECK_OBJ(vsm);
Alex Sakhartchouka80145d2010-08-13 14:32:23 -0700275 GET_TLS();
276 Mesh *sm = static_cast<Mesh *>(vsm);
277 sm->computeBBox();
278 *minX = sm->mBBoxMin[0];
279 *minY = sm->mBBoxMin[1];
280 *minZ = sm->mBBoxMin[2];
281 *maxX = sm->mBBoxMax[0];
282 *maxY = sm->mBBoxMax[1];
283 *maxZ = sm->mBBoxMax[2];
284}
285
Jason Sams536923d2010-05-18 13:35:45 -0700286
287//////////////////////////////////////////////////////////////////////////////
288//
289//////////////////////////////////////////////////////////////////////////////
290
291
292static void SC_color(float r, float g, float b, float a)
293{
294 GET_TLS();
Jason Sams442a6472010-08-04 17:50:20 -0700295 ProgramFragment *pf = (ProgramFragment *)rsc->getFragment();
Alex Sakhartchoukb89aaac2010-09-23 16:16:33 -0700296 pf->setConstantColor(rsc, r, g, b, a);
Jason Sams536923d2010-05-18 13:35:45 -0700297}
298
Jason Samsd79b2e92010-05-19 17:22:57 -0700299static void SC_uploadToTexture2(RsAllocation va, uint32_t baseMipLevel)
Jason Sams536923d2010-05-18 13:35:45 -0700300{
Jason Samsf166d9b2010-09-30 18:15:52 -0700301 CHECK_OBJ(va);
Jason Sams536923d2010-05-18 13:35:45 -0700302 GET_TLS();
303 rsi_AllocationUploadToTexture(rsc, va, false, baseMipLevel);
304}
Jason Samsd79b2e92010-05-19 17:22:57 -0700305static void SC_uploadToTexture(RsAllocation va)
306{
Jason Samsf166d9b2010-09-30 18:15:52 -0700307 CHECK_OBJ(va);
Jason Samsd79b2e92010-05-19 17:22:57 -0700308 GET_TLS();
309 rsi_AllocationUploadToTexture(rsc, va, false, 0);
310}
Jason Sams536923d2010-05-18 13:35:45 -0700311
312static void SC_uploadToBufferObject(RsAllocation va)
313{
Jason Samsf166d9b2010-09-30 18:15:52 -0700314 CHECK_OBJ(va);
Jason Sams536923d2010-05-18 13:35:45 -0700315 GET_TLS();
316 rsi_AllocationUploadToBufferObject(rsc, va);
317}
318
Jason Sams536923d2010-05-18 13:35:45 -0700319static void SC_ClearColor(float r, float g, float b, float a)
320{
Jason Sams536923d2010-05-18 13:35:45 -0700321 GET_TLS();
Alex Sakhartchoukfeede2a2010-10-01 10:54:06 -0700322 rsc->setupProgramStore();
Jason Samsd79b2e92010-05-19 17:22:57 -0700323
324 glClearColor(r, g, b, a);
325 glClear(GL_COLOR_BUFFER_BIT);
326}
327
328static void SC_ClearDepth(float v)
329{
330 GET_TLS();
Alex Sakhartchoukfeede2a2010-10-01 10:54:06 -0700331 rsc->setupProgramStore();
Jason Samsd79b2e92010-05-19 17:22:57 -0700332
333 glClearDepthf(v);
334 glClear(GL_DEPTH_BUFFER_BIT);
Jason Sams536923d2010-05-18 13:35:45 -0700335}
336
337static uint32_t SC_getWidth()
338{
339 GET_TLS();
340 return rsc->getWidth();
341}
342
343static uint32_t SC_getHeight()
344{
345 GET_TLS();
346 return rsc->getHeight();
347}
348
Alex Sakhartchouk9b949fc2010-06-24 17:15:34 -0700349static void SC_DrawTextAlloc(RsAllocation va, int x, int y)
350{
Jason Samsf166d9b2010-09-30 18:15:52 -0700351 CHECK_OBJ(va);
Alex Sakhartchouk9b949fc2010-06-24 17:15:34 -0700352 GET_TLS();
353 Allocation *alloc = static_cast<Allocation *>(va);
354 rsc->mStateFont.renderText(alloc, x, y);
355}
356
357static void SC_DrawText(const char *text, int x, int y)
358{
359 GET_TLS();
360 rsc->mStateFont.renderText(text, x, y);
361}
362
363static void SC_BindFont(RsFont font)
364{
Jason Samsf166d9b2010-09-30 18:15:52 -0700365 CHECK_OBJ(font);
Alex Sakhartchouk9b949fc2010-06-24 17:15:34 -0700366 GET_TLS();
367 rsi_ContextBindFont(rsc, font);
368}
Jason Sams536923d2010-05-18 13:35:45 -0700369
Alex Sakhartchoukfb10c162010-08-04 14:45:48 -0700370static void SC_FontColor(float r, float g, float b, float a)
371{
372 GET_TLS();
373 rsc->mStateFont.setFontColor(r, g, b, a);
374}
375
Jason Sams536923d2010-05-18 13:35:45 -0700376//////////////////////////////////////////////////////////////////////////////
377// Class implementation
378//////////////////////////////////////////////////////////////////////////////
379
380// llvm name mangling ref
381// <builtin-type> ::= v # void
382// ::= b # bool
383// ::= c # char
384// ::= a # signed char
385// ::= h # unsigned char
386// ::= s # short
387// ::= t # unsigned short
388// ::= i # int
389// ::= j # unsigned int
390// ::= l # long
391// ::= m # unsigned long
392// ::= x # long long, __int64
393// ::= y # unsigned long long, __int64
394// ::= f # float
395// ::= d # double
396
397static ScriptCState::SymbolTable_t gSyms[] = {
Jason Samsf0690c42010-07-29 17:31:14 -0700398 { "_Z22rsgBindProgramFragment19rs_program_fragment", (void *)&SC_bindProgramFragment },
399 { "_Z19rsgBindProgramStore16rs_program_store", (void *)&SC_bindProgramStore },
400 { "_Z20rsgBindProgramVertex17rs_program_vertex", (void *)&SC_bindProgramVertex },
401 { "_Z20rsgBindProgramRaster17rs_program_raster", (void *)&SC_bindProgramRaster },
402 { "_Z14rsgBindSampler19rs_program_fragmentj10rs_sampler", (void *)&SC_bindSampler },
403 { "_Z14rsgBindTexture19rs_program_fragmentj13rs_allocation", (void *)&SC_bindTexture },
Jason Samsd79b2e92010-05-19 17:22:57 -0700404
Jason Samsf0690c42010-07-29 17:31:14 -0700405 { "_Z36rsgProgramVertexLoadProjectionMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadProjectionMatrix },
406 { "_Z31rsgProgramVertexLoadModelMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadModelMatrix },
407 { "_Z33rsgProgramVertexLoadTextureMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadTextureMatrix },
Jason Samsd79b2e92010-05-19 17:22:57 -0700408
Alex Sakhartchoukcbed7522010-08-16 17:40:10 -0700409 { "_Z35rsgProgramVertexGetProjectionMatrixP12rs_matrix4x4", (void *)&SC_vpGetProjectionMatrix },
410
Jason Sams442a6472010-08-04 17:50:20 -0700411 { "_Z31rsgProgramFragmentConstantColor19rs_program_fragmentffff", (void *)&SC_pfConstantColor },
412
Jason Samsf0690c42010-07-29 17:31:14 -0700413 { "_Z11rsgGetWidthv", (void *)&SC_getWidth },
414 { "_Z12rsgGetHeightv", (void *)&SC_getHeight },
Jason Samsd79b2e92010-05-19 17:22:57 -0700415
Jason Samsf0690c42010-07-29 17:31:14 -0700416 { "_Z18rsgUploadToTexture13rs_allocationj", (void *)&SC_uploadToTexture2 },
Jason Sams96ed4cf2010-06-15 12:15:57 -0700417 { "_Z18rsgUploadToTexture13rs_allocation", (void *)&SC_uploadToTexture },
Jason Samsf0690c42010-07-29 17:31:14 -0700418 { "_Z23rsgUploadToBufferObject13rs_allocation", (void *)&SC_uploadToBufferObject },
Jason Samsd79b2e92010-05-19 17:22:57 -0700419
Jason Samsf0690c42010-07-29 17:31:14 -0700420 { "_Z11rsgDrawRectfffff", (void *)&SC_drawRect },
421 { "_Z11rsgDrawQuadffffffffffff", (void *)&SC_drawQuad },
422 { "_Z20rsgDrawQuadTexCoordsffffffffffffffffffff", (void *)&SC_drawQuadTexCoords },
423 { "_Z24rsgDrawSpriteScreenspacefffff", (void *)&SC_drawSpriteScreenspace },
Jason Samsd79b2e92010-05-19 17:22:57 -0700424
Alex Sakhartchouk164aaed2010-07-01 16:14:06 -0700425 { "_Z11rsgDrawMesh7rs_mesh", (void *)&SC_drawMesh },
Jason Samsf0690c42010-07-29 17:31:14 -0700426 { "_Z11rsgDrawMesh7rs_meshj", (void *)&SC_drawMeshPrimitive },
427 { "_Z11rsgDrawMesh7rs_meshjjj", (void *)&SC_drawMeshPrimitiveRange },
Alex Sakhartchouka80145d2010-08-13 14:32:23 -0700428 { "_Z25rsgMeshComputeBoundingBox7rs_meshPfS0_S0_S0_S0_S0_", (void *)&SC_meshComputeBoundingBox },
Alex Sakhartchouk164aaed2010-07-01 16:14:06 -0700429
Jason Samsf0690c42010-07-29 17:31:14 -0700430 { "_Z13rsgClearColorffff", (void *)&SC_ClearColor },
431 { "_Z13rsgClearDepthf", (void *)&SC_ClearDepth },
Jason Samsd79b2e92010-05-19 17:22:57 -0700432
Alex Sakhartchouk9b949fc2010-06-24 17:15:34 -0700433 { "_Z11rsgDrawTextPKcii", (void *)&SC_DrawText },
434 { "_Z11rsgDrawText13rs_allocationii", (void *)&SC_DrawTextAlloc },
435
Jason Samsf0690c42010-07-29 17:31:14 -0700436 { "_Z11rsgBindFont7rs_font", (void *)&SC_BindFont },
Alex Sakhartchoukfb10c162010-08-04 14:45:48 -0700437 { "_Z12rsgFontColorffff", (void *)&SC_FontColor },
Alex Sakhartchouk9b949fc2010-06-24 17:15:34 -0700438
Jason Sams536923d2010-05-18 13:35:45 -0700439 // misc
Jason Samsf0690c42010-07-29 17:31:14 -0700440 { "_Z5colorffff", (void *)&SC_color },
Jason Sams536923d2010-05-18 13:35:45 -0700441
Jason Sams536923d2010-05-18 13:35:45 -0700442 { NULL, NULL }
443};
444
445const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbolGL(const char *sym)
446{
447 ScriptCState::SymbolTable_t *syms = gSyms;
448
449 while (syms->mPtr) {
450 if (!strcmp(syms->mName, sym)) {
451 return syms;
452 }
453 syms++;
454 }
455 return NULL;
456}
457