sphor_config.c (20224B)
1 /* Copyright (C) 2024-2026 Centre National de la Recherche Scientifique 2 * Copyright (C) 2024-2026 Clermont Auvergne INP 3 * Copyright (C) 2024-2026 INSA Lyon 4 * Copyright (C) 2024-2026 Institut Mines Télécom Albi-Carmaux 5 * Copyright (C) 2024-2026 Institut National Polytechnique de Toulouse 6 * Copyright (C) 2024-2026 |Méso|Star> (contact@meso-star.com) 7 * Copyright (C) 2024-2026 PhotonLyX (info@photonlyx.com) 8 * Copyright (C) 2024-2026 Université de Lorraine 9 * Copyright (C) 2024-2026 Université Paul Sabatier 10 * Copyright (C) 2024-2026 Université Toulouse - Jean Jaurès 11 * 12 * This program is free software: you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation, either version 3 of the License, or 15 * (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 24 25 #include "sphor.h" 26 #include "sphor_c.h" 27 #include "sphor_config.h" 28 #include "sphor_interface.h" 29 #include "sphor_sources.h" 30 31 #include <rsys/double3.h> 32 #include <rsys/rsys.h> 33 #include <star/sphin.h> 34 #include <star/s3d.h> 35 #include <star/suniq.h> 36 37 struct s3d_scene_view; 38 struct suniq; 39 40 const double UV_EPS = 1e-6; 41 42 /******************************************************************************* 43 * Helper functions 44 ******************************************************************************/ 45 /* Compare the vector "a" to the vector "b". It returns an integer indicating 46 * the result of the comparison, as follows: 47 * - 0, if the "a" and "b" are equal; 48 * - a negative value if "a" is less than "b"; 49 * - a positive value if "a" is greater than "b". */ 50 static INLINE int 51 vec3_cmp 52 (const double a[3], 53 const double b[3]) 54 { 55 int i; 56 ASSERT(NULL != a); 57 ASSERT(NULL != b); 58 59 FOR_EACH(i, 0, 3) { 60 if (a[i] < b[i]) { return -1; } 61 else if (a[i] > b[i]) { return +1; } 62 } 63 return 0; 64 } 65 66 static int 67 are_triangles_reversed 68 (const struct suniq_triangle* tri0, 69 const struct suniq_triangle* tri1) 70 { 71 int i = 0; 72 73 ASSERT(NULL != tri0); 74 ASSERT(NULL != tri1); 75 76 FOR_EACH(i, 0, 3) { 77 if (0 == vec3_cmp(tri0->vertices[0], tri1->vertices[i])) { break; } 78 } 79 ASSERT(i<3); 80 /* Check the order of the next vertex: 81 * If tri0->v1 matches the next vertex in tri1 (cyclically), 82 * the triangles have the same orientation (not reversed). 83 * Otherwise, the vertex order has been reversed. */ 84 if (0 == vec3_cmp(tri0->vertices[1], tri1->vertices[(i+1)%3])) { 85 return 0; /* Same orientation */ 86 } else { 87 ASSERT(0 == vec3_cmp(tri0->vertices[1], tri1->vertices[(i+2)%3])); 88 return 1; /* Reversed orientation */ 89 } 90 } 91 92 static res_T 93 register_interface 94 (struct sphor* sphor, 95 const size_t id, 96 struct interface** interface) 97 { 98 res_T res = RES_OK; 99 100 ASSERT(NULL != sphor); 101 ASSERT(NULL != interface); 102 ASSERT(id <= darray_interface_size_get(&sphor->interfaces)); 103 104 if (id == darray_interface_size_get(&sphor->interfaces)) { 105 res = darray_interface_push_back(&sphor->interfaces, &INTERFACE_NULL); 106 if (RES_OK != res) { return RES_BAD_ARG; } 107 } 108 *interface = darray_interface_data_get(&sphor->interfaces) + id; 109 110 return res; 111 } 112 113 /******************************************************************************* 114 * Volume setup 115 ******************************************************************************/ 116 static res_T 117 setup_interface_volume_geometry_triangle 118 (struct sphor* sphor, 119 struct suniq* suniq, 120 const struct sphin_geometry_descriptor* geom_desc, 121 const size_t ivolume, 122 const size_t itri) 123 { 124 size_t itri_suniq = 0; /* Triangle index used internally by suniq */ 125 struct suniq_triangle tri = SUNIQ_TRIANGLE_NULL; 126 struct suniq_triangle tri_suniq = SUNIQ_TRIANGLE_NULL; 127 struct interface* interface = NULL; 128 enum sphin_side side = SPHIN_SIDE_NONE__; 129 int flip = 0; 130 res_T res = RES_OK; 131 132 ASSERT(NULL != sphor); 133 ASSERT(NULL != suniq); 134 ASSERT(NULL != geom_desc); 135 136 setup_triangle(geom_desc, itri, &tri); 137 138 res = suniq_register_triangle(suniq, &tri, &itri_suniq); 139 if (RES_OK != res) { goto error; } 140 141 /* Retrieve the registered triangle and its properties */ 142 res = suniq_get_triangle(suniq, itri_suniq, &tri_suniq); 143 if (RES_OK != res) { goto error; } 144 145 res = register_interface(sphor, itri_suniq, &interface); 146 if (RES_OK != res) { goto error; } 147 148 /* Determine the side to append the surface based on whether the geometry is 149 * flipped and knowing the input side using the xor (^) operator. 150 * The value of the is_back variable is given by the truth table: 151 * 152 * +-----------+ 153 * | flip | 154 * +-----+-----+ 155 * | 0 | 1 | 156 * +------+-----+-----+-----+ 157 * | side | 0 | 0 | 1 | 158 * | == |-----+-----+-----+ 159 * | back | 1 | 1 | 0 | 160 * +------+-----+-----+-----+ */ 161 162 flip = are_triangles_reversed(&tri, &tri_suniq); 163 164 side = (size_t) flip ^ geom_desc->side; 165 166 if (INVALID_ID != interface->volumes[side]) { 167 ERROR(sphor, 168 "Redefinition of volume side in triangle from file '%s'.\n", 169 geom_desc->filename); 170 res = RES_BAD_ARG; 171 goto error; 172 } 173 interface->volumes[side] = ivolume; 174 175 exit: 176 return res; 177 error: 178 goto exit; 179 } 180 181 static res_T 182 setup_interfaces_volume_geometry 183 (struct sphor* sphor, 184 struct suniq* suniq, 185 const size_t ivolume, 186 const size_t igeom) 187 { 188 struct sphin_geometry* geom = NULL; 189 struct sphin_geometry_descriptor geom_desc = SPHIN_GEOMETRY_DESCRIPTOR_NULL; 190 struct sphin_volume* volume = NULL; 191 size_t itri = 0; 192 size_t n = 0; 193 res_T res = RES_OK; 194 195 ASSERT(NULL != sphor); 196 ASSERT(NULL != suniq); 197 ASSERT(RES_OK == sphin_config_get_volume_count(sphor->config, &n)); 198 ASSERT(ivolume < n); 199 200 res = sphin_config_get_volume(sphor->config, ivolume, &volume); 201 if (RES_OK != res) { goto error; } 202 203 ASSERT(RES_OK == sphin_volume_get_geometry_count(volume, &n)); 204 ASSERT(igeom < n); 205 206 res = sphin_volume_get_geometry(volume, igeom, &geom); 207 if (RES_OK != res) { goto error; } 208 209 res = sphin_geometry_get_desc(geom, &geom_desc); 210 if (RES_OK != res) { goto error; } 211 212 n = geom_desc.mesh.triangle_count; 213 FOR_EACH(itri, 0, n) { 214 res = setup_interface_volume_geometry_triangle 215 (sphor, suniq, &geom_desc, ivolume, itri); 216 if (RES_OK != res) { goto error; } 217 } 218 219 exit: 220 return res; 221 error: 222 goto exit; 223 } 224 225 static res_T 226 setup_interfaces_volume 227 (struct sphor* sphor, 228 struct suniq* suniq, 229 const size_t ivolume) 230 { 231 struct sphin_volume* volume = NULL; 232 size_t n = 0; 233 size_t igeom = 0; 234 res_T res = RES_OK; 235 236 ASSERT(NULL != sphor); 237 ASSERT(NULL != suniq); 238 ASSERT(RES_OK == sphin_config_get_volume_count(sphor->config, &n)); 239 ASSERT(ivolume < n); 240 241 res = sphin_config_get_volume(sphor->config, ivolume, &volume); 242 if (RES_OK != res) { goto error; } 243 244 res = sphin_volume_get_geometry_count(volume, &n); 245 if (RES_OK != res) { goto error; } 246 247 FOR_EACH(igeom, 0, n) { 248 res = setup_interfaces_volume_geometry 249 (sphor, suniq, ivolume, igeom); 250 if (RES_OK != res) { goto error; } 251 } 252 exit: 253 return res; 254 error: 255 goto exit; 256 } 257 258 /******************************************************************************* 259 * Surface setup 260 ******************************************************************************/ 261 static res_T 262 setup_interface_surface_geometry_triangle 263 (struct sphor* sphor, 264 struct suniq* suniq, 265 const struct sphin_geometry_descriptor* geom_desc, 266 const size_t isurface, 267 const size_t itri) 268 { 269 size_t itri_suniq = 0; /* Triangle index used internally by suniq */ 270 struct suniq_triangle tri = SUNIQ_TRIANGLE_NULL; 271 struct suniq_triangle tri_suniq = SUNIQ_TRIANGLE_NULL; 272 struct interface* interface = NULL; 273 int flip = 0; 274 short surf_count = 0; 275 enum sphin_side side = SPHIN_SIDE_NONE__; 276 res_T res = RES_OK; 277 278 ASSERT(NULL != sphor); 279 ASSERT(NULL != suniq); 280 ASSERT(NULL != geom_desc); 281 282 setup_triangle(geom_desc, itri, &tri); 283 284 res = suniq_register_triangle(suniq, &tri, &itri_suniq); 285 if (RES_OK != res) { goto error; } 286 287 /* Retrieve the registered triangle and its properties */ 288 res = suniq_get_triangle(suniq, itri_suniq, &tri_suniq); 289 if (RES_OK != res) { goto error; } 290 291 res = register_interface(sphor, itri_suniq, &interface); 292 if (RES_OK != res) { goto error; } 293 294 /* Determine the side to append the surface based on whether the geometry is 295 * flipped and knowing the input side using the xor (^) operator. 296 * The value of the is_back variable is given by the truth table: 297 * 298 * +-----------+ 299 * | flip | 300 * +-----+-----+ 301 * | 0 | 1 | 302 * +------+-----+-----+-----+ 303 * | side | 0 | 0 | 1 | 304 * | == |-----+-----+-----+ 305 * | back | 1 | 1 | 0 | 306 * +------+-----+-----+-----+ */ 307 308 flip = are_triangles_reversed(&tri, &tri_suniq); 309 310 side = (size_t)flip ^ geom_desc->side; 311 312 surf_count = interface->surface_count[side]; 313 314 if (MAX_SURFACE_COUNT <= surf_count) { 315 ERROR(sphor, 316 "Too many surfaces on the same side of interface " 317 "defined by triangle from file '%s'. " 318 "Maximum allowed: %d\n", 319 geom_desc->filename, MAX_SURFACE_COUNT); 320 res = RES_MEM_ERR; 321 goto error; 322 } 323 interface->surfaces[side][surf_count] = isurface; 324 interface->surface_count[side] += 1; 325 326 exit: 327 return res; 328 error: 329 goto exit; 330 } 331 332 static res_T 333 setup_interfaces_surface_geometry 334 (struct sphor* sphor, 335 struct suniq* suniq, 336 const size_t isurface, 337 const size_t igeom) 338 { 339 struct sphin_geometry* geom = NULL; 340 struct sphin_geometry_descriptor geom_desc = SPHIN_GEOMETRY_DESCRIPTOR_NULL; 341 struct sphin_surface* surface = NULL; 342 size_t itri = 0; 343 size_t n = 0; 344 res_T res = RES_OK; 345 346 ASSERT(NULL != sphor); 347 ASSERT(NULL != suniq); 348 ASSERT(RES_OK == sphin_config_get_surface_count(sphor->config, &n)); 349 ASSERT(isurface < n); 350 351 res = sphin_config_get_surface(sphor->config, isurface, &surface); 352 if (RES_OK != res) { goto error; } 353 354 ASSERT(RES_OK == sphin_surface_get_geometry_count(surface, &n)); 355 ASSERT(igeom < n); 356 357 res = sphin_surface_get_geometry(surface, igeom, &geom); 358 if (RES_OK != res) { goto error; } 359 360 res = sphin_geometry_get_desc(geom, &geom_desc); 361 if (RES_OK != res) { goto error; } 362 363 n = geom_desc.mesh.triangle_count; 364 FOR_EACH(itri, 0, n) { 365 res = setup_interface_surface_geometry_triangle 366 (sphor, suniq, &geom_desc, isurface, itri); 367 if (RES_OK != res) { goto error; } 368 } 369 370 exit: 371 return res; 372 error: 373 goto exit; 374 } 375 376 static res_T 377 setup_interfaces_surface 378 (struct sphor* sphor, 379 struct suniq* suniq, 380 const size_t isurface) 381 { 382 struct sphin_surface* surface = NULL; 383 size_t n = 0; 384 size_t igeom = 0; 385 res_T res = RES_OK; 386 387 ASSERT(NULL != sphor); 388 ASSERT(NULL != suniq); 389 ASSERT(RES_OK == sphin_config_get_surface_count(sphor->config, &n)); 390 ASSERT(isurface < n); 391 392 res = sphin_config_get_surface(sphor->config, isurface, &surface); 393 if (RES_OK != res) { goto error; } 394 395 res = sphin_surface_get_geometry_count(surface, &n); 396 if (RES_OK != res) { goto error; } 397 398 FOR_EACH(igeom, 0, n) { 399 res = setup_interfaces_surface_geometry 400 (sphor, suniq, isurface, igeom); 401 if (RES_OK != res) { goto error; } 402 } 403 exit: 404 return res; 405 error: 406 goto exit; 407 } 408 409 static res_T 410 setup_interfaces 411 (struct sphor* sphor, 412 struct suniq* suniq) 413 { 414 size_t id = 0; 415 size_t volume_count = 0; 416 size_t surface_count = 0; 417 res_T res = RES_OK; 418 419 ASSERT(NULL != sphor); 420 ASSERT(NULL != suniq); 421 /* Assumes that config is already loaded */ 422 ASSERT(NULL != sphor->config); 423 424 res = sphin_config_get_volume_count(sphor->config, &volume_count); 425 if (RES_OK != res) { goto error; } 426 res = sphin_config_get_surface_count(sphor->config, &surface_count); 427 if (RES_OK != res) { goto error; } 428 429 FOR_EACH(id, 0, volume_count) { 430 res = setup_interfaces_volume(sphor, suniq, id); 431 if (RES_OK != res) { goto error; } 432 } 433 434 FOR_EACH(id, 0, surface_count) { 435 res = setup_interfaces_surface(sphor, suniq, id); 436 if (RES_OK != res) { goto error; } 437 } 438 439 exit: 440 return res; 441 442 error: 443 goto exit; 444 } 445 446 static void 447 get_triangle_indices 448 (const unsigned tri_id, 449 unsigned indices[3], 450 void* data) 451 { 452 struct suniq_desc* desc = data; 453 size_t ids[3]; 454 455 ASSERT(NULL != data); 456 ASSERT(NULL != indices); 457 458 SUNIQ(desc_get_triangle_indices(desc, tri_id, ids)); 459 460 indices[0] = (unsigned)ids[0]; 461 indices[1] = (unsigned)ids[1]; 462 indices[2] = (unsigned)ids[2]; 463 } 464 465 static void 466 get_vertex_coords 467 (const unsigned vertex_id, 468 float coords[3], 469 void* data) 470 { 471 struct suniq_desc* desc = data; 472 double pos[3]; 473 474 ASSERT(NULL != data); 475 ASSERT(NULL != coords); 476 477 SUNIQ(desc_get_vertex(desc, vertex_id, pos)); 478 479 coords[0] = (float)pos[0]; 480 coords[1] = (float)pos[1]; 481 coords[2] = (float)pos[2]; 482 } 483 484 static res_T 485 load_config 486 (struct sphor* sphor, 487 const struct sphor_create_args* args) 488 { 489 struct sphin* sphin = NULL; 490 struct sphin_config* config = NULL; 491 struct sphin_create_args sphin_args = SPHIN_CREATE_ARGS_DEFAULT; 492 res_T res = RES_OK; 493 494 ASSERT(NULL != sphor); 495 ASSERT(NULL != args); 496 497 /* Create sphin struct */ 498 sphin_args.verbose = sphor->verbose; 499 sphin_args.logger = sphor->logger; 500 sphin_args.allocator = sphor->allocator; 501 502 res = sphin_create(&sphin_args, &sphin); 503 if (RES_OK != res) { goto error; } 504 505 /* Load config */ 506 if (NULL != args->input_filename) { 507 res = sphin_load(sphin, args->input_filename, &config); 508 } 509 else { 510 res = sphin_load_stream(sphin, stdin, "stdin", &config); 511 } 512 if (RES_OK != res) { goto error; } 513 514 exit: 515 if (NULL != sphin) { SPHIN(ref_put(sphin)); } 516 sphor->config = config; 517 return res; 518 error: 519 if (NULL != config) { 520 SPHIN(config_ref_put(config)); 521 config = NULL; 522 } 523 goto exit; 524 } 525 526 /* Function attached to the scene view that determines whether a hit is valid. 527 * Returns 0 if the hit is valid, 1 otherwise. */ 528 static int 529 hit_filter_function 530 (const struct s3d_hit* hit, 531 const float pos[3], 532 const float dir[3], 533 const float range[2], 534 void* primitive_id, 535 void* interface) 536 { 537 double u = 0; 538 double v = 0; 539 540 (void)pos; 541 (void)dir; 542 (void)interface; 543 544 size_t* prim_id = primitive_id; 545 546 /* Discard hits that do not respect the range — this may happen due to 547 * numerical precision errors in Embree. */ 548 if(hit->distance <= range[0] || hit->distance >= range[1]) 549 return 1; 550 551 /* Discard auto-intersection */ 552 if (hit->prim.prim_id == *prim_id) { return 1; } 553 554 /* Discard intersections of photons trapped in the edges of two primitives */ 555 u = hit->uv[0]; 556 v = hit->uv[1]; 557 if (hit->distance < UV_EPS 558 && (u < UV_EPS || v < UV_EPS || (u + v) > 1.0 - UV_EPS)) { 559 return 1; 560 } 561 562 return 0; 563 } 564 565 /******************************************************************************* 566 * Local functions 567 ******************************************************************************/ 568 res_T 569 setup_config 570 (struct sphor* sphor, 571 const struct sphor_create_args* args) 572 { 573 struct suniq* suniq = NULL; 574 575 res_T res = RES_OK; 576 577 ASSERT(NULL != sphor); 578 ASSERT(NULL != args); 579 580 res = suniq_create(sphor->allocator, &suniq); 581 if (RES_OK != res) { goto error; } 582 583 /* Parse input file */ 584 res = load_config(sphor, args); 585 if (RES_OK != res) { goto error; } 586 587 /* Attach physical properties to primitives */ 588 res = setup_interfaces(sphor, suniq); 589 if (RES_OK != res) { goto error; } 590 591 /* Setup the scene views for each source to accelerate the sampling of a 592 * position in a given source */ 593 res = setup_source_views(sphor, args, suniq); 594 if (RES_OK != res) { goto error; } 595 596 /* Compute distribution used to important sample a source with respect to 597 * their power */ 598 res = setup_source_distrib_power(sphor, args); 599 if (RES_OK != res) { goto error; } 600 601 /* Build accelerating structure for geometrical data */ 602 res = setup_geometry_accel_struct 603 (sphor, suniq, (S3D_TRACE | S3D_GET_PRIMITIVE), &sphor->scene_view); 604 if (RES_OK != res) { goto error; } 605 606 exit: 607 if (NULL != suniq) { SUNIQ(ref_put(suniq)); } 608 return res; 609 error: 610 goto exit; 611 } 612 613 void 614 setup_triangle 615 (const struct sphin_geometry_descriptor* geom_desc, 616 const size_t itri, 617 struct suniq_triangle* tri) 618 { 619 const double* coords[3] = {NULL, NULL, NULL}; 620 size_t ids[3] = {0, 0, 0}; 621 622 ASSERT(NULL != geom_desc); 623 ASSERT(NULL != tri); 624 ASSERT(itri < geom_desc->mesh.triangle_count); 625 626 /* Get the indices of the triangle vertices. They are stored in a one 627 * dimensional array. So, to retrieve the vertex indices of the i^th triangle, 628 * one has to multiply i by 3, i.e. the number of vertex per triangle : 629 * 630 * 0 1 2 3 4 5 6 7 8 631 * +---+---+---+---+---+---+---+---+---+----- 632 * indices |id0|id1|id2|id0|id1|id2|id0|id1|id2| ... 633 * +---+---+---+---+---+---+---+---+---+----- 634 * \__tri 0__/ \__tri 1__/ \__tri 2__/ */ 635 ids[0] = geom_desc->mesh.indices[itri*3/*#indices per triangle*/+0]; 636 ids[1] = geom_desc->mesh.indices[itri*3/*#indices per triangle*/+1]; 637 ids[2] = geom_desc->mesh.indices[itri*3/*#indices per triangle*/+2]; 638 639 /* Get coords for the triangle corresponding to the vertices retrieved. Like 640 * the indices, the coords are stored in an one dimensional array. 641 * 642 * 0 1 2 3 4 5 6 7 8 643 * +---+---+---+---+---+---+---+---+---+----- 644 * coords | x | y | z | x | y | z | x | y | z | ... 645 * +---+---+---+---+---+---+---+---+---+----- 646 * \__vert0__/ \__vert1__/ \__vert2__/ */ 647 coords[0] = geom_desc->mesh.coords + ids[0]*3/*#coords per vertex*/; 648 coords[1] = geom_desc->mesh.coords + ids[1]*3/*#coords per vertex*/; 649 coords[2] = geom_desc->mesh.coords + ids[2]*3/*#coords per vertex*/; 650 651 /* Set the retrieved coords to the triangle struct */ 652 d3_set(tri->vertices[0], coords[0]); 653 d3_set(tri->vertices[1], coords[1]); 654 d3_set(tri->vertices[2], coords[2]); 655 } 656 657 res_T 658 setup_geometry_accel_struct 659 (struct sphor* sphor, 660 struct suniq* suniq, 661 const int view_mask, /* Combination of s3d_scene_view flags */ 662 struct s3d_scene_view** out_view) 663 { 664 struct suniq_desc desc = SUNIQ_DESC_NULL; 665 struct s3d_vertex_data vertex_data; 666 struct s3d_device* device = NULL; 667 struct s3d_scene* scene = NULL; 668 struct s3d_scene_view* view = NULL; 669 struct s3d_shape* shape = NULL; 670 res_T res = RES_OK; 671 672 ASSERT(NULL != sphor); 673 ASSERT(NULL != suniq); 674 ASSERT(NULL != out_view); 675 ASSERT(0 != view_mask); 676 677 res = suniq_get_desc(suniq, &desc); 678 if (RES_OK != res) { goto error; } 679 680 if (0 == desc.ntriangles) { 681 ERROR(sphor, 682 "Could not start simulation: " 683 "surfaces and volumes should have a geometry\n"); 684 res = RES_BAD_ARG; 685 goto error; 686 } 687 688 res = s3d_device_create 689 (sphor->logger, sphor->allocator, sphor->verbose, &device); 690 if (RES_OK != res) { goto error; } 691 692 res = s3d_shape_create_mesh(device, &shape); 693 if (RES_OK != res) { goto error; } 694 695 vertex_data.usage = S3D_POSITION; 696 vertex_data.type = S3D_FLOAT3; 697 vertex_data.get = get_vertex_coords; 698 res = s3d_mesh_setup_indexed_vertices 699 (shape, (unsigned)desc.ntriangles, get_triangle_indices, 700 (unsigned)desc.nvertices, &vertex_data, 1, &desc); 701 if (RES_OK != res) { goto error; } 702 703 res = s3d_scene_create(device, &scene); 704 if (RES_OK != res) { goto error; } 705 706 res = s3d_mesh_set_hit_filter_function(shape, hit_filter_function, NULL); 707 if (RES_OK != res) { goto error; } 708 709 res = s3d_scene_attach_shape(scene, shape); 710 if (RES_OK != res) { goto error; } 711 712 res = s3d_scene_view_create(scene, view_mask, &view); 713 if (RES_OK != res) { goto error; } 714 715 exit: 716 if (NULL != device) { S3D(device_ref_put(device)); } 717 if (NULL != shape) { S3D(shape_ref_put(shape)); } 718 if (NULL != scene) { S3D(scene_ref_put(scene)); } 719 *out_view = view; 720 return res; 721 error: 722 if (NULL != view) { 723 S3D(scene_view_ref_put(view)); 724 view = NULL; 725 } 726 goto exit; 727 }