The open source OpenXR runtime
at main 700 lines 25 kB view raw
1// Copyright 2019-2024, Collabora, Ltd. 2// Copyright 2025, NVIDIA CORPORATION. 3// SPDX-License-Identifier: BSL-1.0 4/*! 5 * @file 6 * @brief Compositor render implementation. 7 * @author Jakob Bornecrantz <jakob@collabora.com> 8 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 9 * @ingroup comp_util 10 */ 11 12#pragma once 13 14#include "xrt/xrt_defines.h" 15#include "xrt/xrt_vulkan_includes.h" // IWYU pragma: keep 16 17#include "render/render_interface.h" 18#include "util/u_misc.h" 19 20#include <assert.h> 21 22 23#ifdef __cplusplus 24extern "C" { 25#endif 26 27struct comp_layer; 28struct render_compute; 29struct render_gfx; 30struct render_gfx_target_resources; 31 32 33/*! 34 * @defgroup comp_render 35 * @brief Renders, aka "layer squashers" and distortion application. 36 * 37 * Two parallel implementations of the render module exist: 38 * 39 * - one uses graphics shaders (aka GFX, @ref comp_render_gfx, @ref comp_render_gfx.c) 40 * - the other uses compute shaders (aka CS, @ref comp_render_cs, @ref comp_render_cs.c) 41 * 42 * Their abilities are effectively equivalent, although the graphics version disregards depth 43 * data, while the compute shader does use it somewhat. 44 * 45 * @note In general this module requires that swapchains in your supplied @ref comp_layer layers 46 * implement @ref comp_swapchain in addition to just @ref xrt_swapchain. 47 */ 48 49/* 50 * 51 * Input data struct. 52 * 53 */ 54 55/*! 56 * @name Input data structs 57 * @{ 58 */ 59 60/*! 61 * The input data needed for a single view, shared between both GFX and CS 62 * paths. 63 * 64 * To fully render a single view two "rendering" might be needed, the 65 * first being the layer squashing, and the second is the distortion step. The 66 * target for the layer squashing is referred to as "layer" or "scratch" and 67 * prefixed with `layer` if needs be. The other final step is referred to as 68 * "distortion target" or just "target", and is prefixed with `target`. 69 * 70 * @ingroup comp_render 71 */ 72struct comp_render_view_data 73{ 74 //! New world pose of this view at the beginng of scanout. 75 struct xrt_pose world_pose_scanout_begin; 76 77 //! New world pose of this view at the end of scanout. 78 struct xrt_pose world_pose_scanout_end; 79 80 //! New eye pose of this view. 81 struct xrt_pose eye_pose; 82 83 /*! 84 * New fov of this view, used for the layer scratch image. Needs to 85 * match distortion parameters if distortion is used. 86 */ 87 struct xrt_fov fov; 88 89 /*! 90 * Go from UV to tanangle for both the target and layer image since 91 * they share the same fov, this needs to match @p fov. 92 */ 93 struct xrt_normalized_rect pre_transform; 94 95 struct 96 { 97 /*! 98 * The layer image for this view (aka scratch image), 99 * used for barrier operations. 100 */ 101 VkImage image; 102 103 /*! 104 * Pre-view layer target viewport_data (where in the image we 105 * should render the view). 106 */ 107 struct render_viewport_data viewport_data; 108 109 struct 110 { 111 //! Per-view layer target resources. 112 struct render_gfx_target_resources *rtr; 113 } gfx; 114 115 struct 116 { 117 /*! 118 * View into layer image (aka scratch image), for used 119 * as a storage tagert of the CS (write) path. 120 */ 121 VkImageView storage_view; 122 } cs; 123 } squash; // When used as a destination. 124 125 struct 126 { 127 /*! 128 * When sampling from the layer image (aka scratch image), how 129 * should we transform it to get to the pixels correctly. 130 * 131 * Ignored when doing a fast path and reading directly from 132 * the app projection layer. 133 */ 134 struct xrt_normalized_rect norm_rect; 135 136 /*! 137 * View into layer image (aka scratch image) when sampling the 138 * image, used for both GFX (read) and CS (read) paths. 139 * 140 * Ignored when doing a fast path and reading directly from 141 * the app projection layer. 142 */ 143 VkImageView sample_view; 144 145 } squash_as_src; // The input to the target/distortion shaders. 146 147 struct 148 { 149 // Distortion target viewport data (aka target). 150 struct render_viewport_data viewport_data; 151 152 struct 153 { 154 //! Distortion target vertex rotation information. 155 struct xrt_matrix_2x2 vertex_rot; 156 } gfx; 157 } target; // When used as a destination. 158}; 159 160/*! 161 * The input data needed for a complete layer squashing distortion rendering 162 * to a target. This struct is shared between GFX and CS paths. 163 * 164 * @ingroup comp_render 165 */ 166struct comp_render_dispatch_data 167{ 168 struct comp_render_view_data views[XRT_MAX_VIEWS]; 169 170 /*! 171 * The number of squash views currently in this dispatch data. 172 */ 173 uint32_t squash_view_count; 174 175 176 //! Fast path can be disabled for mirroing so needs to be an argument. 177 bool fast_path; 178 179 //! Very often true, can be disabled for debugging. 180 bool do_timewarp; 181 182 struct 183 { 184 //! Has this struct been setup to use the target. 185 bool initialized; 186 187 /*! 188 * The number of target views currently, when calling dispatch 189 * this has to be either zero or the same number as 190 * squash_view_count, see also the target.initialized field. 191 */ 192 uint32_t view_count; 193 194 //! Members used only by GFX @ref comp_render_gfx 195 struct 196 { 197 //! The resources needed for the target. 198 struct render_gfx_target_resources *rtr; 199 } gfx; 200 201 //! Members used only by CS @ref comp_render_cs 202 struct 203 { 204 //! Target image for distortion, used for barrier. 205 VkImage image; 206 207 //! Target image view for distortion. 208 VkImageView storage_view; 209 } cs; 210 } target; 211}; 212 213/*! 214 * Initialize structure for use without the target step. 215 * 216 * @param[out] data Common render dispatch data. Will be zeroed and initialized. 217 * @param fast_path Whether we will use the "fast path" avoiding layer squashing. 218 * @param do_timewarp Whether timewarp (reprojection) will be performed. 219 */ 220static inline void 221comp_render_initial_init(struct comp_render_dispatch_data *data, bool fast_path, bool do_timewarp) 222{ 223 U_ZERO(data); 224 225 data->fast_path = fast_path; 226 data->do_timewarp = do_timewarp; 227} 228 229/*! 230 * Shared implementation setting up common view params between GFX and CS. 231 * 232 * Private implementation method, do not use outside of more-specific add_view calls! 233 * 234 * @param data Common render dispatch data, will be updated 235 * @param world_pose New world pose of this view. 236 * Populates @ref comp_render_view_data::world_pose 237 * @param eye_pose New eye pose of this view 238 * Populates @ref comp_render_view_data::eye_pose 239 * @param fov Assigned to fov in the view data, and used to compute @ref comp_render_view_data::target_pre_transform 240 * Populates @ref comp_render_view_data::fov 241 * @param squash_image Scratch image for this view 242 * Populates @ref comp_render_view_data::squash::image 243 * @param squash_viewport_data Where in the image to render the view 244 * Populates @ref comp_render_view_data::squash::viewport_data 245 * @param squash_as_src_sample_view The image view into the scratch image for sampling. 246 * Populates @ref comp_render_view_data::squash_as_src::sample_view 247 * @param squash_as_src_norm_rect How to transform when sampling from the scratch image. 248 * Populates @ref comp_render_view_data::squash_as_src::norm_rect 249 * @param target_viewport_data Distortion target viewport data (aka target) 250 * Populates @ref comp_render_view_data::target::viewport_data 251 252 * @return Pointer to the @ref comp_render_view_data we have been populating, for additional setup. 253 */ 254static inline struct comp_render_view_data * 255comp_render_dispatch_add_squash_view(struct comp_render_dispatch_data *data, 256 const struct xrt_pose *world_pose_scanout_begin, 257 const struct xrt_pose *world_pose_scanout_end, 258 const struct xrt_pose *eye_pose, 259 const struct xrt_fov *fov, 260 VkImage squash_image, 261 const struct render_viewport_data *squash_viewport_data) 262{ 263 uint32_t i = data->squash_view_count++; 264 265 assert(i < ARRAY_SIZE(data->views)); 266 267 struct comp_render_view_data *view = &data->views[i]; 268 269 render_calc_uv_to_tangent_lengths_rect(fov, &view->pre_transform); 270 271 // Common 272 view->world_pose_scanout_begin = *world_pose_scanout_begin; 273 view->world_pose_scanout_end = *world_pose_scanout_end; 274 view->eye_pose = *eye_pose; 275 view->fov = *fov; 276 277 // When writing into the squash (aka scratch) image. 278 view->squash.image = squash_image; 279 view->squash.viewport_data = *squash_viewport_data; 280 281 return view; 282} 283 284static inline struct comp_render_view_data * 285comp_render_dispatch_add_target_view(struct comp_render_dispatch_data *data, 286 VkImageView squash_as_src_sample_view, 287 const struct xrt_normalized_rect *squash_as_src_norm_rect, 288 const struct render_viewport_data *target_viewport_data) 289{ 290 uint32_t i = data->target.view_count++; 291 292 assert(i < data->squash_view_count); 293 assert(i < ARRAY_SIZE(data->views)); 294 295 struct comp_render_view_data *view = &data->views[i]; 296 297 // When using the squash (aka scratch) image as a source. 298 view->squash_as_src.sample_view = squash_as_src_sample_view; 299 view->squash_as_src.norm_rect = *squash_as_src_norm_rect; 300 301 // When writing into the target. 302 view->target.viewport_data = *target_viewport_data; 303 304 return view; 305} 306 307/*! @} */ 308 309/* 310 * 311 * Gfx functions. 312 * 313 */ 314 315/*! 316 * 317 * @defgroup comp_render_gfx 318 * 319 * GFX renderer control and dispatch - uses graphics shaders. 320 * 321 * Depends on the common @ref comp_render_dispatch_data, as well as the resources 322 * @ref render_gfx_target_resources (often called `rtr`), and @ref render_gfx. 323 * 324 * @ingroup comp_render 325 * @{ 326 */ 327 328/*! 329 * Initialize structure for use of the GFX renderer. 330 * 331 * @param[in,out] data Common render dispatch data. 332 * @param target_rtr GFX-specific resources for the entire framebuffer. Must be populated before call. 333 */ 334static inline void 335comp_render_gfx_add_target(struct comp_render_dispatch_data *data, struct render_gfx_target_resources *target_rtr) 336{ 337 // Error tracking. 338 data->target.initialized = true; 339 340 // When writing into the target. 341 data->target.gfx.rtr = target_rtr; 342} 343 344/*! 345 * Add view to the common data, as required by the GFX renderer. 346 * 347 * @param[in,out] data Common render dispatch data, will be updated 348 * @param world_pose New world pose of this view. 349 * Populates @ref comp_render_view_data::world_pose 350 * @param eye_pose New eye pose of this view 351 * Populates @ref comp_render_view_data::eye_pose 352 * @param fov Assigned to fov in the view data, and used to 353 * compute @ref comp_render_view_data::pre_transform - also 354 * populates @ref comp_render_view_data::fov 355 * @param layer_image Scratch image for this view 356 * Populates @ref comp_render_view_data::squash::image 357 * @param later_rtr Will be associated with this view. GFX-specific 358 * @param layer_viewport_data Where in the image to render the view 359 * Populates @ref comp_render_view_data::squash::viewport_data 360 * @param squash_as_src_norm_rect How to transform when sampling from the scratch image. 361 * Populates @ref comp_render_view_data::squash_as_src::norm_rect 362 * @param squash_as_src_sample_view The image view into the scratch image for sampling. 363 * Populates @ref comp_render_view_data::squash_as_src::sample_view 364 * @param target_vertex_rot 365 * Populates @ref comp_render_view_data::target.gfx.vertex_rot 366 * @param target_viewport_data Distortion target viewport data (aka target) 367 * Populates @ref comp_render_view_data::target.viewport_data 368 */ 369static inline void 370comp_render_gfx_add_squash_view(struct comp_render_dispatch_data *data, 371 const struct xrt_pose *world_pose, 372 const struct xrt_pose *eye_pose, 373 const struct xrt_fov *fov, 374 VkImage squash_image, 375 struct render_gfx_target_resources *squash_rtr, 376 const struct render_viewport_data *layer_viewport_data) 377{ 378 struct comp_render_view_data *view = comp_render_dispatch_add_squash_view( // 379 data, // 380 world_pose, // 381 world_pose, // 382 eye_pose, // 383 fov, // 384 squash_image, // 385 layer_viewport_data); // 386 387 // When writing into the squash (aka scratch) image. 388 view->squash.gfx.rtr = squash_rtr; 389} 390 391static inline void 392comp_render_gfx_add_target_view(struct comp_render_dispatch_data *data, 393 VkImageView squash_as_src_sample_view, 394 const struct xrt_normalized_rect *squash_as_src_norm_rect, 395 const struct xrt_matrix_2x2 *target_vertex_rot, 396 const struct render_viewport_data *target_viewport_data) 397{ 398 struct comp_render_view_data *view = comp_render_dispatch_add_target_view( // 399 data, // 400 squash_as_src_sample_view, // 401 squash_as_src_norm_rect, // 402 target_viewport_data); // 403 404 // When writing into the target. 405 view->target.gfx.vertex_rot = *target_vertex_rot; 406} 407 408/*! 409 * Dispatch the (graphics pipeline) layer squasher, on any number of views. 410 * 411 * All source layer images needs to be in the correct image layout, no barrier 412 * is inserted for them. The target images are barriered from undefined to general 413 * so they can be written to, then to the layout defined by @p transition_to. 414 * 415 * Expected layouts: 416 * 417 * - Layer images: `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` 418 * - Target images: Any 419 * 420 * After call layouts: 421 * 422 * - Layer images: `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` 423 * - Target images: @p transition_to 424 * 425 * @note Swapchains in the @p layers must implement @ref comp_swapchain in 426 * addition to just @ref xrt_swapchain, as this function downcasts to @ref comp_swapchain ! 427 * 428 * @param render Graphics renderer object 429 * @param[in] layers Layers to render, see note. 430 * @param[in] layer_count Number of elements in @p layers array. 431 * @param[in] d Common render dispatch data 432 * @param[in] transition_to Desired image layout for target images 433 */ 434void 435comp_render_gfx_layers(struct render_gfx *render, 436 const struct comp_layer *layers, 437 uint32_t layer_count, 438 const struct comp_render_dispatch_data *d, 439 VkImageLayout transition_to); 440 441/*! 442 * Writes the needed commands to the @ref render_gfx to do a full composition with distortion. 443 * 444 * Takes a set of layers, new device poses, scratch 445 * images with associated @ref render_gfx_target_resources and writes the needed 446 * commands to the @ref render_gfx to do a full composition with distortion. 447 * The scratch images are optionally used to squash layers should it not be 448 * possible to do a @p comp_render_dispatch_data::fast_path. Will use the render 449 * passes of the targets which set the layout. 450 * 451 * The render passes of @p comp_render_dispatch_data::views::rtr must be created 452 * with a final_layout of `VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL` or there will 453 * be validation errors. 454 * 455 * Expected layouts: 456 * 457 * - Layer images: `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` 458 * - Scratch images: Any (as per the @ref render_gfx_render_pass) 459 * - Target image: Any (as per the @ref render_gfx_render_pass) 460 * 461 * After call layouts: 462 * 463 * - Layer images: `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` 464 * - Scratch images: `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` 465 * - Target image: What the render pass of @p rtr specifies. 466 * 467 * @note Swapchains in the @p layers must implement @ref comp_swapchain in 468 * addition to just @ref xrt_swapchain, as this function downcasts to @ref comp_swapchain ! 469 * 470 * @param render GFX render object 471 * @param[in] layers Layers to render, see note. 472 * @param[in] layer_count Number of elements in @p layers array. 473 * @param[in] d Common render dispatch data 474 */ 475void 476comp_render_gfx_dispatch(struct render_gfx *render, 477 const struct comp_layer *layers, 478 const uint32_t layer_count, 479 const struct comp_render_dispatch_data *d); 480 481/* end of comp_render_gfx group */ 482 483/*! @} */ 484 485 486/* 487 * 488 * CS functions. 489 * 490 */ 491 492/*! 493 * 494 * @defgroup comp_render_cs 495 * 496 * CS renderer control and dispatch - uses compute shaders 497 * 498 * Depends on @ref render_compute 499 * 500 * @ingroup comp_render 501 * @{ 502 */ 503 504/*! 505 * Add the target info, as required by the CS renderer. 506 * 507 * @param[in,out] data Common render dispatch data. 508 * @param target_image Image to render into 509 * @param target_storage_view Corresponding image view 510 */ 511static inline void 512comp_render_cs_add_target(struct comp_render_dispatch_data *data, VkImage target_image, VkImageView target_storage_view) 513{ 514 // Error tracking. 515 data->target.initialized = true; 516 517 // When writing into the target. 518 data->target.cs.image = target_image; 519 data->target.cs.storage_view = target_storage_view; 520} 521 522/*! 523 * Add view to the common data, as required by the CS renderer. 524 * 525 * @param[in,out] data Common render dispatch data, will be updated 526 * @param world_pose_scanout_begin New world pose of this view. 527 * Populates @ref comp_render_view_data::world_pose 528 * @param world_pose_scanout_end New world pose of this view. 529 * Populates @ref comp_render_view_data::world_pose 530 * @param eye_pose New eye pose of this view 531 * Populates @ref comp_render_view_data::eye_pose_scanout_end 532 * @param fov Assigned to fov in the view data, and used to compute @ref comp_render_view_data::pre_transform. 533 * Populates @ref comp_render_view_data::fov 534 * @param squash_image Scratch image for this view 535 * Populates @ref comp_render_view_data::squash::image 536 * @param squash_storage_view Image view into the scratch image for storage, CS specific 537 * @param squash_viewport_data Where in the image to render the view 538 * Populates @ref comp_render_view_data::squash::viewport_data 539 * @param squash_as_src_sample_view The image view into the scratch image for sampling. 540 * Populates @ref comp_render_view_data::squash_as_src::sample_view 541 * @param squash_as_src_norm_rect How to transform when sampling from the scratch image. 542 * Populates @ref comp_render_view_data::squash_as_src::norm_rect 543 * @param target_viewport_data Distortion target viewport data (aka target) 544 * Populates @ref comp_render_view_data::target::viewport_data 545 */ 546static inline void 547comp_render_cs_add_squash_view(struct comp_render_dispatch_data *data, 548 const struct xrt_pose *world_pose_scanout_begin, 549 const struct xrt_pose *world_pose_scanout_end, 550 const struct xrt_pose *eye_pose, 551 const struct xrt_fov *fov, 552 VkImage squash_image, 553 VkImageView squash_storage_view, 554 const struct render_viewport_data *squash_viewport_data) 555{ 556 struct comp_render_view_data *view = comp_render_dispatch_add_squash_view( // 557 data, // 558 world_pose_scanout_begin, // 559 world_pose_scanout_end, // 560 eye_pose, // 561 fov, // 562 squash_image, // 563 squash_viewport_data); // 564 565 // When writing into the squash (aka scratch) image. 566 view->squash.cs.storage_view = squash_storage_view; 567} 568 569static inline void 570comp_render_cs_add_target_view(struct comp_render_dispatch_data *data, 571 VkImageView squash_as_src_sample_view, 572 const struct xrt_normalized_rect *squash_as_src_norm_rect, 573 const struct render_viewport_data *target_viewport_data) 574{ 575 struct comp_render_view_data *view = comp_render_dispatch_add_target_view( // 576 data, // 577 squash_as_src_sample_view, // 578 squash_as_src_norm_rect, // 579 target_viewport_data); // 580 (void)view; 581} 582 583/*! 584 * Dispatch the layer squasher for a single view. 585 * 586 * All source layer images and target image needs to be in the correct image 587 * layout, no barrier is inserted at all. The @p view_index argument is needed 588 * to grab a pre-allocated UBO from the @ref render_resources and to correctly 589 * select left/right data from various layers. 590 * 591 * Expected layouts: 592 * 593 * - Layer images: `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` 594 * - Target images: `VK_IMAGE_LAYOUT_GENERAL` 595 * 596 * @note Swapchains in the @p layers must implement @ref comp_swapchain in 597 * addition to just @ref xrt_swapchain, as this function downcasts to @ref comp_swapchain ! 598 * 599 * @param render Compute renderer object 600 * @param view_index Index of the view 601 * @param layers Layers to render, see note. 602 * @param layer_count Number of elements in @p layers array. 603 * @param pre_transform 604 * @param world_pose 605 * @param eye_pose 606 * @param target_image 607 * @param target_image_view 608 * @param target_view 609 * @param do_timewarp 610 */ 611void 612comp_render_cs_layer(struct render_compute *render, 613 uint32_t view_index, 614 const struct comp_layer *layers, 615 const uint32_t layer_count, 616 const struct xrt_normalized_rect *pre_transform, 617 const struct xrt_pose *world_pose_scanout_begin, 618 const struct xrt_pose *world_pose_scanout_end, 619 const struct xrt_pose *eye_pose, 620 const VkImage target_image, 621 const VkImageView target_image_view, 622 const struct render_viewport_data *target_view, 623 bool do_timewarp); 624 625/*! 626 * Dispatch the layer squasher, on any number of views. 627 * 628 * All source layer images needs to be in the correct image layout, no barrier 629 * is inserted for them. The target images are barriered from undefined to general 630 * so they can be written to, then to the layout defined by @p transition_to. 631 * 632 * Expected layouts: 633 * 634 * - Layer images: `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` 635 * - Target images: Any 636 * 637 * After call layouts: 638 * 639 * - Layer images: `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` 640 * - Target images: @p transition_to 641 * 642 * @note Swapchains in the @p layers must implement @ref comp_swapchain in 643 * addition to just @ref xrt_swapchain, as this function downcasts to @ref comp_swapchain ! 644 * 645 * @param render Compute renderer object 646 * @param[in] layers Layers to render, see note. 647 * @param[in] layer_count Number of elements in @p layers array. 648 * @param[in] d Common render dispatch data 649 * @param[in] transition_to Desired image layout for target images 650 */ 651void 652comp_render_cs_layers(struct render_compute *render, 653 const struct comp_layer *layers, 654 const uint32_t layer_count, 655 const struct comp_render_dispatch_data *d, 656 VkImageLayout transition_to); 657 658/*! 659 * Write commands to @p render to do a full composition with distortion. 660 * 661 * Helper function that takes a set of layers, new device poses, a scratch 662 * images and writes the needed commands to the @ref render_compute to do a full 663 * composition with distortion. The scratch images are optionally used to squash 664 * layers should it not be possible to do a fast_path. Will insert barriers to 665 * change the scratch images and target images to the needed layout. 666 * 667 * 668 * Expected layouts: 669 * 670 * - Layer images: `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` 671 * - Scratch images: Any 672 * - Target image: Any 673 * 674 * After call layouts: 675 * 676 * - Layer images: `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` 677 * - Scratch images: `VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL` 678 * - Target image: `VK_IMAGE_LAYOUT_PRESENT_SRC_KHR` 679 * 680 * @note Swapchains in the @p layers must implement @ref comp_swapchain in 681 * addition to just @ref xrt_swapchain, as this function downcasts to @ref comp_swapchain ! 682 * 683 * @param render Compute renderer object 684 * @param[in] layers Layers to render, see note. 685 * @param[in] layer_count Number of elements in @p layers array. 686 * @param[in] d Common render dispatch data 687 * 688 */ 689void 690comp_render_cs_dispatch(struct render_compute *render, 691 const struct comp_layer *layers, 692 const uint32_t layer_count, 693 const struct comp_render_dispatch_data *d); 694 695/* end of comp_render_cs group */ 696/*! @} */ 697 698#ifdef __cplusplus 699} 700#endif