The open source OpenXR runtime
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

c/main: Add compute distortion rendering backend

authored by

Jakob Bornecrantz and committed by
Jakob Bornecrantz
37950042 02e6b789

+1625 -38
+3
src/xrt/compositor/CMakeLists.txt
··· 2 2 # SPDX-License-Identifier: BSL-1.0 3 3 4 4 spirv_shaders(SHADER_HEADERS 5 + shaders/clear.comp 6 + shaders/distortion.comp 5 7 shaders/mesh.frag 6 8 shaders/mesh.vert 7 9 shaders/layer.frag ··· 34 36 main/comp_layer_renderer.h 35 37 main/comp_layer_renderer.c 36 38 render/comp_buffer.c 39 + render/comp_compute.c 37 40 render/comp_render.h 38 41 render/comp_rendering.c 39 42 render/comp_resources.c
+35 -15
src/xrt/compositor/main/comp_compositor.c
··· 405 405 return do_single(xc, xdev, xsc, data); 406 406 } 407 407 408 - static xrt_result_t 409 - compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_graphics_sync_handle_t sync_handle) 408 + static void 409 + do_graphics_layers(struct comp_compositor *c) 410 410 { 411 - COMP_TRACE_MARKER(); 412 - 413 - struct comp_compositor *c = comp_compositor(xc); 414 - 415 - COMP_SPEW(c, "LAYER_COMMIT at %8.3fms", ts_ms()); 416 - 417 - u_graphics_sync_unref(&sync_handle); 418 - 419 - 420 411 // Always zero for now. 421 412 uint32_t slot_id = 0; 422 413 uint32_t num_layers = c->slots[slot_id].num_layers; ··· 490 481 assert(false); 491 482 } 492 483 } 484 + } 485 + 486 + static xrt_result_t 487 + compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_graphics_sync_handle_t sync_handle) 488 + { 489 + COMP_TRACE_MARKER(); 490 + 491 + struct comp_compositor *c = comp_compositor(xc); 492 + 493 + COMP_SPEW(c, "LAYER_COMMIT at %8.3fms", ts_ms()); 494 + 495 + u_graphics_sync_unref(&sync_handle); 496 + 497 + if (!c->settings.use_compute) { 498 + do_graphics_layers(c); 499 + } 493 500 494 501 comp_renderer_draw(c->r); 495 502 ··· 742 749 static const char *optional_device_extensions[] = { 743 750 VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, 744 751 VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 752 + #ifdef VK_EXT_robustness2 753 + VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, 754 + #endif 745 755 }; 746 756 747 757 ··· 894 904 VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT, // Default fallback. 895 905 }; 896 906 907 + bool use_compute = c->settings.use_compute; 908 + 909 + struct vk_device_features device_features = { 910 + .shader_storage_image_write_without_format = true, 911 + .null_descriptor = use_compute, 912 + }; 913 + 897 914 // No other way then to try to see if realtime is available. 898 915 for (size_t i = 0; i < ARRAY_SIZE(prios); i++) { 899 916 ret = vk_create_device( // 900 917 vk, // 901 918 c->settings.selected_gpu_index, // 902 - false, // compute_only 919 + use_compute, // compute_only 903 920 prios[i], // global_priority 904 921 required_device_extensions, // 905 922 ARRAY_SIZE(required_device_extensions), // 906 923 optional_device_extensions, // 907 924 ARRAY_SIZE(optional_device_extensions), // 908 - NULL); // optional_device_features 925 + &device_features); // optional_device_features 909 926 910 927 // All ok! 911 928 if (ret == VK_SUCCESS) { 912 - COMP_INFO(c, "Created device/queue with %s priority.", prio_strs[i]); 929 + COMP_INFO(c, "Created device and %s queue with %s priority.", 930 + use_compute ? "compute" : "graphics", prio_strs[i]); 913 931 break; 914 932 } 915 933 ··· 1112 1130 return false; 1113 1131 } 1114 1132 1133 + bool use_compute = c->settings.use_compute; 1134 + 1115 1135 // follow same device selection logic as subsequent calls 1116 1136 ret = vk_create_device( // 1117 1137 temp_vk, // 1118 1138 c->settings.selected_gpu_index, // 1119 - false, // compute_only 1139 + use_compute, // compute_only 1120 1140 VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT, // global_priority 1121 1141 required_device_extensions, // 1122 1142 ARRAY_SIZE(required_device_extensions), //
+3
src/xrt/compositor/main/comp_compositor.h
··· 139 139 140 140 struct comp_shaders 141 141 { 142 + VkShaderModule clear_comp; 143 + VkShaderModule distortion_comp; 144 + 142 145 VkShaderModule mesh_vert; 143 146 VkShaderModule mesh_frag; 144 147
+158 -22
src/xrt/compositor/main/comp_renderer.c
··· 308 308 309 309 struct vk_bundle *vk = &r->c->vk; 310 310 311 - r->rrs = U_TYPED_ARRAY_CALLOC(struct comp_rendering, r->num_buffers); 312 - r->fences = U_TYPED_ARRAY_CALLOC(VkFence, r->num_buffers); 311 + bool use_compute = r->settings->use_compute; 312 + if (!use_compute) { 313 + r->rrs = U_TYPED_ARRAY_CALLOC(struct comp_rendering, r->num_buffers); 313 314 314 - for (uint32_t i = 0; i < r->num_buffers; ++i) { 315 - renderer_build_rendering(r, &r->rrs[i], i); 315 + for (uint32_t i = 0; i < r->num_buffers; ++i) { 316 + renderer_build_rendering(r, &r->rrs[i], i); 317 + } 316 318 } 319 + 320 + r->fences = U_TYPED_ARRAY_CALLOC(VkFence, r->num_buffers); 317 321 318 322 for (uint32_t i = 0; i < r->num_buffers; i++) { 319 323 VkFenceCreateInfo fence_info = { ··· 438 442 // Make we sure we destroy all dependent things before creating new images. 439 443 renderer_close_renderings_and_fences(r); 440 444 441 - comp_target_create_images( // 442 - r->c->target, // 443 - r->c->settings.preferred.width, // 444 - r->c->settings.preferred.height, // 445 - r->settings->color_format, // 446 - r->settings->color_space, // 447 - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, // 448 - r->settings->present_mode); // 445 + VkImageUsageFlags image_usage = 0; 446 + if (r->settings->use_compute) { 447 + image_usage = VK_IMAGE_USAGE_STORAGE_BIT; 448 + } else { 449 + image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 450 + } 451 + 452 + comp_target_create_images( // 453 + r->c->target, // 454 + r->c->settings.preferred.width, // 455 + r->c->settings.preferred.height, // 456 + r->settings->color_format, // 457 + r->settings->color_space, // 458 + image_usage, // 459 + r->settings->present_mode); // 449 460 450 461 r->num_buffers = r->c->target->num_images; 451 462 ··· 501 512 } 502 513 503 514 static void 504 - renderer_submit_queue(struct comp_renderer *r) 515 + renderer_submit_queue(struct comp_renderer *r, VkCommandBuffer cmd) 505 516 { 506 517 COMP_TRACE_MARKER(); 507 518 ··· 528 539 .pWaitSemaphores = &r->semaphores.present_complete, 529 540 .pWaitDstStageMask = stage_flags, 530 541 .commandBufferCount = 1, 531 - .pCommandBuffers = &r->rrs[r->acquired_buffer].cmd, 542 + .pCommandBuffers = &cmd, 532 543 .signalSemaphoreCount = 1, 533 544 .pSignalSemaphores = &r->semaphores.render_complete, 534 545 }; ··· 692 703 return image->views.no_alpha[array_index]; 693 704 } 694 705 706 + static void 707 + dispatch_graphics(struct comp_renderer *r) 708 + { 709 + COMP_TRACE_MARKER(); 710 + 711 + struct comp_compositor *c = r->c; 712 + struct comp_target *ct = c->target; 713 + 714 + comp_target_mark_submit(ct, c->frame.rendering.id, os_monotonic_get_ns()); 715 + 716 + renderer_get_view_projection(r); 717 + comp_layer_renderer_draw(r->lr); 718 + 719 + comp_target_update_timings(ct); 720 + 721 + renderer_submit_queue(r, r->rrs[r->acquired_buffer].cmd); 722 + } 723 + 724 + 725 + /* 726 + * 727 + * Compute 728 + * 729 + */ 730 + 731 + static void 732 + do_projection_layers(struct comp_renderer *r, 733 + struct comp_rendering_compute *crc, 734 + const struct comp_layer *layer, 735 + const struct xrt_layer_projection_view_data *lvd, 736 + const struct xrt_layer_projection_view_data *rvd) 737 + { 738 + const struct xrt_layer_data *data = &layer->data; 739 + uint32_t left_array_index = lvd->sub.array_index; 740 + uint32_t right_array_index = rvd->sub.array_index; 741 + const struct comp_swapchain_image *left = &layer->scs[0]->images[lvd->sub.image_index]; 742 + const struct comp_swapchain_image *right = &layer->scs[1]->images[rvd->sub.image_index]; 743 + 744 + struct comp_viewport_data views[2]; 745 + calc_viewport_data(r, &views[0], &views[1]); 746 + 747 + VkImage target_image = r->c->target->images[r->acquired_buffer].handle; 748 + VkImageView target_image_view = r->c->target->images[r->acquired_buffer].view; 749 + 750 + VkSampler src_samplers[2] = { 751 + left->sampler, 752 + right->sampler, 753 + }; 754 + 755 + VkImageView src_image_views[2] = { 756 + get_image_view(left, data->flags, left_array_index), 757 + get_image_view(right, data->flags, right_array_index), 758 + }; 759 + 760 + struct xrt_normalized_rect src_norm_rects[2] = {lvd->sub.norm_rect, rvd->sub.norm_rect}; 761 + if (data->flip_y) { 762 + src_norm_rects[0].h = -src_norm_rects[0].h; 763 + src_norm_rects[0].y = 1 + src_norm_rects[0].y; 764 + src_norm_rects[1].h = -src_norm_rects[1].h; 765 + src_norm_rects[1].y = 1 + src_norm_rects[1].y; 766 + } 767 + 768 + comp_rendering_compute_projection( // 769 + crc, // 770 + src_samplers, // 771 + src_image_views, // 772 + src_norm_rects, // 773 + target_image, // 774 + target_image_view, // 775 + views); // 776 + } 777 + 778 + static void 779 + dispatch_compute(struct comp_renderer *r, struct comp_rendering_compute *crc) 780 + { 781 + COMP_TRACE_MARKER(); 782 + 783 + struct comp_compositor *c = r->c; 784 + struct comp_target *ct = c->target; 785 + 786 + comp_rendering_compute_init(c, &c->nr, crc); 787 + comp_rendering_compute_begin(crc); 788 + 789 + struct comp_viewport_data views[2]; 790 + calc_viewport_data(r, &views[0], &views[1]); 791 + 792 + VkImage target_image = r->c->target->images[r->acquired_buffer].handle; 793 + VkImageView target_image_view = r->c->target->images[r->acquired_buffer].view; 794 + 795 + uint32_t slot_id = 0; 796 + uint32_t num_layers = c->slots[slot_id].num_layers; 797 + if (num_layers > 0 && c->slots[slot_id].layers[0].data.type == XRT_LAYER_STEREO_PROJECTION) { 798 + int i = 0; 799 + const struct comp_layer *layer = &c->slots[slot_id].layers[i]; 800 + const struct xrt_layer_stereo_projection_data *stereo = &layer->data.stereo; 801 + const struct xrt_layer_projection_view_data *lvd = &stereo->l; 802 + const struct xrt_layer_projection_view_data *rvd = &stereo->r; 803 + 804 + do_projection_layers(r, crc, layer, lvd, rvd); 805 + } else if (num_layers > 0 && c->slots[slot_id].layers[0].data.type == XRT_LAYER_STEREO_PROJECTION_DEPTH) { 806 + int i = 0; 807 + const struct comp_layer *layer = &c->slots[slot_id].layers[i]; 808 + const struct xrt_layer_stereo_projection_depth_data *stereo = &layer->data.stereo_depth; 809 + const struct xrt_layer_projection_view_data *lvd = &stereo->l; 810 + const struct xrt_layer_projection_view_data *rvd = &stereo->r; 811 + 812 + do_projection_layers(r, crc, layer, lvd, rvd); 813 + } else { 814 + comp_rendering_compute_clear( // 815 + crc, // 816 + target_image, // 817 + target_image_view, // 818 + views); // 819 + } 820 + 821 + comp_rendering_compute_end(crc); 822 + 823 + comp_target_mark_submit(ct, c->frame.rendering.id, os_monotonic_get_ns()); 824 + 825 + renderer_submit_queue(r, crc->cmd); 826 + } 827 + 695 828 696 829 /* 697 830 * ··· 914 1047 915 1048 comp_target_update_timings(ct); 916 1049 917 - comp_target_mark_submit(ct, c->frame.rendering.id, os_monotonic_get_ns()); 918 - 919 - renderer_get_view_projection(r); 920 - comp_layer_renderer_draw(r->lr); 921 - 922 - comp_target_update_timings(ct); 923 - 924 - renderer_submit_queue(r); 1050 + bool use_compute = r->settings->use_compute; 1051 + struct comp_rendering_compute crc = {0}; 1052 + if (use_compute) { 1053 + dispatch_compute(r, &crc); 1054 + } else { 1055 + dispatch_graphics(r); 1056 + } 925 1057 926 1058 renderer_present_swapchain_image(r, c->frame.rendering.desired_present_time_ns, 927 1059 c->frame.rendering.present_slop_ns); ··· 937 1069 * This is done after a swap so isn't time critical. 938 1070 */ 939 1071 renderer_wait_gpu_idle(r); 1072 + 1073 + if (use_compute) { 1074 + comp_rendering_compute_close(&crc); 1075 + } 940 1076 941 1077 /* 942 1078 * For direct mode this makes us wait until the last frame has been
+9 -1
src/xrt/compositor/main/comp_settings.c
··· 27 27 DEBUG_GET_ONCE_BOOL_OPTION(xcb_fullscreen, "XRT_COMPOSITOR_XCB_FULLSCREEN", false) 28 28 DEBUG_GET_ONCE_NUM_OPTION(xcb_display, "XRT_COMPOSITOR_XCB_DISPLAY", -1) 29 29 DEBUG_GET_ONCE_NUM_OPTION(default_framerate, "XRT_COMPOSITOR_DEFAULT_FRAMERATE", 60) 30 + DEBUG_GET_ONCE_BOOL_OPTION(compute, "XRT_COMPOSITOR_COMPUTE", false) 30 31 // clang-format on 31 32 32 33 void ··· 39 40 interval_ns = (1000 * 1000 * 1000) / default_framerate; 40 41 } 41 42 43 + s->use_compute = debug_get_bool_option_compute(); 44 + 45 + if (s->use_compute) { 46 + s->color_format = VK_FORMAT_B8G8R8A8_UNORM; 47 + } else { 48 + s->color_format = VK_FORMAT_B8G8R8A8_SRGB; 49 + } 50 + 42 51 s->display = debug_get_num_option_xcb_display(); 43 - s->color_format = VK_FORMAT_B8G8R8A8_SRGB; 44 52 s->color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; 45 53 s->present_mode = VK_PRESENT_MODE_FIFO_KHR; 46 54 s->window_type = WINDOW_AUTO;
+2
src/xrt/compositor/main/comp_settings.h
··· 61 61 { 62 62 int display; 63 63 64 + bool use_compute; 65 + 64 66 VkFormat color_format; 65 67 VkColorSpaceKHR color_space; 66 68 VkPresentModeKHR present_mode;
+14
src/xrt/compositor/main/comp_shaders.c
··· 20 20 #pragma GCC diagnostic ignored "-Wnewline-eof" 21 21 22 22 23 + #include "shaders/clear.comp.h" 24 + #include "shaders/distortion.comp.h" 23 25 #include "shaders/layer.frag.h" 24 26 #include "shaders/layer.vert.h" 25 27 #include "shaders/equirect1.frag.h" ··· 78 80 bool 79 81 comp_shaders_load(struct vk_bundle *vk, struct comp_shaders *s) 80 82 { 83 + C(shader_load(vk, // vk_bundle 84 + shaders_clear_comp, // data 85 + sizeof(shaders_clear_comp), // size 86 + &s->clear_comp)); // out 87 + 88 + C(shader_load(vk, // vk_bundle 89 + shaders_distortion_comp, // data 90 + sizeof(shaders_distortion_comp), // size 91 + &s->distortion_comp)); // out 92 + 81 93 C(shader_load(vk, // vk_bundle 82 94 shaders_mesh_vert, // data 83 95 sizeof(shaders_mesh_vert), // size ··· 128 140 void 129 141 comp_shaders_close(struct vk_bundle *vk, struct comp_shaders *s) 130 142 { 143 + D(clear_comp); 144 + D(distortion_comp); 131 145 D(mesh_vert); 132 146 D(mesh_frag); 133 147 D(equirect1_vert);
+1
src/xrt/compositor/meson.build
··· 34 34 'multi/comp_multi_private.h', 35 35 'multi/comp_multi_system.c', 36 36 'render/comp_buffer.c', 37 + 'render/comp_compute.c', 37 38 'render/comp_render.h', 38 39 'render/comp_rendering.c', 39 40 'render/comp_resources.c',
+661
src/xrt/compositor/render/comp_compute.c
··· 1 + // Copyright 2019-2021, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief The compositor compute based rendering code. 6 + * @author Jakob Bornecrantz <jakob@collabora.com> 7 + * @ingroup comp_main 8 + */ 9 + 10 + #include "math/m_api.h" 11 + #include "main/comp_compositor.h" 12 + #include "render/comp_render.h" 13 + 14 + #include <stdio.h> 15 + 16 + 17 + /* 18 + * 19 + * Defines 20 + * 21 + */ 22 + 23 + #define C(c) \ 24 + do { \ 25 + VkResult ret = c; \ 26 + if (ret != VK_SUCCESS) { \ 27 + return false; \ 28 + } \ 29 + } while (false) 30 + 31 + #define D(TYPE, thing) \ 32 + if (thing != VK_NULL_HANDLE) { \ 33 + vk->vkDestroy##TYPE(vk, vk->device, thing, NULL); \ 34 + thing = VK_NULL_HANDLE; \ 35 + } 36 + 37 + #define DD(pool, thing) \ 38 + if (thing != VK_NULL_HANDLE) { \ 39 + free_descriptor_set(vk, pool, thing); \ 40 + thing = VK_NULL_HANDLE; \ 41 + } 42 + 43 + 44 + /* 45 + * 46 + * Helper functions. 47 + * 48 + */ 49 + 50 + /* 51 + * For dispatching compute to the view, calculate the number of groups. 52 + */ 53 + static void 54 + calc_dispatch_dims(const struct comp_viewport_data views[2], uint32_t *out_w, uint32_t *out_h) 55 + { 56 + #define IMAX(a, b) ((a) > (b) ? (a) : (b)) 57 + uint32_t w = IMAX(views[0].w, views[1].w); 58 + uint32_t h = IMAX(views[0].h, views[1].h); 59 + #undef IMAX 60 + 61 + // Power of two divide and round up. 62 + #define P2_DIVIDE_ROUND_UP(v, div) ((v + (div - 1)) / div) 63 + w = P2_DIVIDE_ROUND_UP(w, 8); 64 + h = P2_DIVIDE_ROUND_UP(h, 8); 65 + #undef P2_DIVIDE_ROUND_UP 66 + 67 + *out_w = w; 68 + *out_h = h; 69 + } 70 + 71 + 72 + /* 73 + * 74 + * Vulkan helpers. 75 + * 76 + */ 77 + 78 + static VkResult 79 + create_command_buffer(struct vk_bundle *vk, VkCommandBuffer *out_cmd) 80 + { 81 + VkResult ret; 82 + 83 + VkCommandBufferAllocateInfo cmd_buffer_info = { 84 + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 85 + .commandPool = vk->cmd_pool, 86 + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, 87 + .commandBufferCount = 1, 88 + }; 89 + 90 + VkCommandBuffer cmd = VK_NULL_HANDLE; 91 + 92 + os_mutex_lock(&vk->cmd_pool_mutex); 93 + 94 + ret = vk->vkAllocateCommandBuffers( // 95 + vk->device, // 96 + &cmd_buffer_info, // 97 + &cmd); // 98 + 99 + os_mutex_unlock(&vk->cmd_pool_mutex); 100 + 101 + if (ret != VK_SUCCESS) { 102 + VK_ERROR(vk, "vkCreateFramebuffer failed: %s", vk_result_string(ret)); 103 + return ret; 104 + } 105 + 106 + *out_cmd = cmd; 107 + 108 + return VK_SUCCESS; 109 + } 110 + 111 + static void 112 + destroy_command_buffer(struct vk_bundle *vk, VkCommandBuffer command_buffer) 113 + { 114 + os_mutex_lock(&vk->cmd_pool_mutex); 115 + 116 + vk->vkFreeCommandBuffers( // 117 + vk->device, // device 118 + vk->cmd_pool, // commandPool 119 + 1, // commandBufferCount 120 + &command_buffer); // pCommandBuffers 121 + 122 + os_mutex_unlock(&vk->cmd_pool_mutex); 123 + } 124 + 125 + static VkResult 126 + begin_command_buffer(struct vk_bundle *vk, VkCommandBuffer command_buffer) 127 + { 128 + VkResult ret; 129 + 130 + VkCommandBufferBeginInfo command_buffer_info = { 131 + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 132 + }; 133 + 134 + ret = vk->vkBeginCommandBuffer( // 135 + command_buffer, // 136 + &command_buffer_info); // 137 + if (ret != VK_SUCCESS) { 138 + VK_ERROR(vk, "vkBeginCommandBuffer failed: %s", vk_result_string(ret)); 139 + return ret; 140 + } 141 + 142 + return VK_SUCCESS; 143 + } 144 + 145 + static VkResult 146 + end_command_buffer(struct vk_bundle *vk, VkCommandBuffer command_buffer) 147 + { 148 + VkResult ret; 149 + 150 + // End the command buffer. 151 + ret = vk->vkEndCommandBuffer( // 152 + command_buffer); // 153 + if (ret != VK_SUCCESS) { 154 + VK_ERROR(vk, "vkEndCommandBuffer failed: %s", vk_result_string(ret)); 155 + return ret; 156 + } 157 + 158 + return VK_SUCCESS; 159 + } 160 + 161 + static VkResult 162 + create_descriptor_set(struct vk_bundle *vk, 163 + VkDescriptorPool descriptor_pool, 164 + VkDescriptorSetLayout descriptor_layout, 165 + VkDescriptorSet *out_descriptor_set) 166 + { 167 + VkResult ret; 168 + 169 + VkDescriptorSetAllocateInfo alloc_info = { 170 + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, 171 + .descriptorPool = descriptor_pool, 172 + .descriptorSetCount = 1, 173 + .pSetLayouts = &descriptor_layout, 174 + }; 175 + 176 + VkDescriptorSet descriptor_set = VK_NULL_HANDLE; 177 + ret = vk->vkAllocateDescriptorSets( // 178 + vk->device, // 179 + &alloc_info, // 180 + &descriptor_set); // 181 + if (ret != VK_SUCCESS) { 182 + VK_DEBUG(vk, "vkAllocateDescriptorSets failed: %s", vk_result_string(ret)); 183 + return ret; 184 + } 185 + 186 + *out_descriptor_set = descriptor_set; 187 + 188 + return VK_SUCCESS; 189 + } 190 + 191 + XRT_MAYBE_UNUSED static void 192 + update_compute_discriptor_set(struct vk_bundle *vk, 193 + uint32_t src_binding, 194 + VkSampler src_samplers[2], 195 + VkImageView src_image_views[2], 196 + uint32_t distortion_binding, 197 + VkSampler distortion_samplers[6], 198 + VkImageView distortion_image_views[6], 199 + uint32_t target_binding, 200 + VkImageView target_image_view, 201 + uint32_t ubo_binding, 202 + VkBuffer ubo_buffer, 203 + VkDeviceSize ubo_size, 204 + VkDescriptorSet descriptor_set) 205 + { 206 + VkDescriptorImageInfo src_image_info[2] = { 207 + { 208 + .sampler = src_samplers[0], 209 + .imageView = src_image_views[0], 210 + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 211 + }, 212 + { 213 + .sampler = src_samplers[1], 214 + .imageView = src_image_views[1], 215 + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 216 + }, 217 + }; 218 + 219 + VkDescriptorImageInfo distortion_image_info[6] = { 220 + { 221 + .sampler = distortion_samplers[0], 222 + .imageView = distortion_image_views[0], 223 + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 224 + }, 225 + { 226 + .sampler = distortion_samplers[1], 227 + .imageView = distortion_image_views[1], 228 + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 229 + }, 230 + { 231 + .sampler = distortion_samplers[2], 232 + .imageView = distortion_image_views[2], 233 + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 234 + }, 235 + { 236 + .sampler = distortion_samplers[3], 237 + .imageView = distortion_image_views[3], 238 + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 239 + }, 240 + { 241 + .sampler = distortion_samplers[4], 242 + .imageView = distortion_image_views[4], 243 + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 244 + }, 245 + { 246 + .sampler = distortion_samplers[5], 247 + .imageView = distortion_image_views[5], 248 + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 249 + }, 250 + }; 251 + 252 + VkDescriptorImageInfo target_image_info = { 253 + .imageView = target_image_view, 254 + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, 255 + }; 256 + 257 + VkDescriptorBufferInfo buffer_info = { 258 + .buffer = ubo_buffer, 259 + .offset = 0, 260 + .range = ubo_size, 261 + }; 262 + 263 + VkWriteDescriptorSet write_descriptor_sets[4] = { 264 + { 265 + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 266 + .dstSet = descriptor_set, 267 + .dstBinding = src_binding, 268 + .descriptorCount = ARRAY_SIZE(src_image_info), 269 + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 270 + .pImageInfo = src_image_info, 271 + }, 272 + { 273 + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 274 + .dstSet = descriptor_set, 275 + .dstBinding = distortion_binding, 276 + .descriptorCount = ARRAY_SIZE(distortion_image_info), 277 + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 278 + .pImageInfo = distortion_image_info, 279 + }, 280 + { 281 + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 282 + .dstSet = descriptor_set, 283 + .dstBinding = target_binding, 284 + .descriptorCount = 1, 285 + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 286 + .pImageInfo = &target_image_info, 287 + }, 288 + { 289 + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 290 + .dstSet = descriptor_set, 291 + .dstBinding = ubo_binding, 292 + .descriptorCount = 1, 293 + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 294 + .pBufferInfo = &buffer_info, 295 + }, 296 + }; 297 + 298 + vk->vkUpdateDescriptorSets( // 299 + vk->device, // 300 + ARRAY_SIZE(write_descriptor_sets), // descriptorWriteCount 301 + write_descriptor_sets, // pDescriptorWrites 302 + 0, // descriptorCopyCount 303 + NULL); // pDescriptorCopies 304 + } 305 + 306 + XRT_MAYBE_UNUSED static void 307 + update_compute_discriptor_set_target(struct vk_bundle *vk, 308 + uint32_t target_binding, 309 + VkImageView target_image_view, 310 + uint32_t ubo_binding, 311 + VkBuffer ubo_buffer, 312 + VkDeviceSize ubo_size, 313 + VkDescriptorSet descriptor_set) 314 + { 315 + VkDescriptorImageInfo target_image_info = { 316 + .imageView = target_image_view, 317 + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, 318 + }; 319 + 320 + VkDescriptorBufferInfo buffer_info = { 321 + .buffer = ubo_buffer, 322 + .offset = 0, 323 + .range = ubo_size, 324 + }; 325 + 326 + VkWriteDescriptorSet write_descriptor_sets[2] = { 327 + { 328 + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 329 + .dstSet = descriptor_set, 330 + .dstBinding = target_binding, 331 + .descriptorCount = 1, 332 + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 333 + .pImageInfo = &target_image_info, 334 + }, 335 + { 336 + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 337 + .dstSet = descriptor_set, 338 + .dstBinding = ubo_binding, 339 + .descriptorCount = 1, 340 + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 341 + .pBufferInfo = &buffer_info, 342 + }, 343 + }; 344 + 345 + vk->vkUpdateDescriptorSets( // 346 + vk->device, // 347 + ARRAY_SIZE(write_descriptor_sets), // descriptorWriteCount 348 + write_descriptor_sets, // pDescriptorWrites 349 + 0, // descriptorCopyCount 350 + NULL); // pDescriptorCopies 351 + } 352 + 353 + 354 + /* 355 + * 356 + * 'Exported' functions. 357 + * 358 + */ 359 + 360 + bool 361 + comp_rendering_compute_init(struct comp_compositor *c, struct comp_resources *r, struct comp_rendering_compute *crc) 362 + { 363 + assert(crc->c == NULL); 364 + assert(crc->r == NULL); 365 + 366 + struct vk_bundle *vk = &c->vk; 367 + crc->c = c; 368 + crc->r = r; 369 + 370 + C(create_command_buffer(vk, &crc->cmd)); 371 + 372 + C(create_descriptor_set( // 373 + vk, // 374 + r->compute.descriptor_pool, // descriptor_pool 375 + r->compute.descriptor_set_layout, // descriptor_set_layout 376 + &crc->clear_descriptor_set)); // descriptor_set 377 + 378 + return true; 379 + } 380 + 381 + bool 382 + comp_rendering_compute_begin(struct comp_rendering_compute *crc) 383 + { 384 + struct vk_bundle *vk = &crc->c->vk; 385 + 386 + C(begin_command_buffer(vk, crc->cmd)); 387 + 388 + return true; 389 + } 390 + 391 + bool 392 + comp_rendering_compute_end(struct comp_rendering_compute *crc) 393 + { 394 + struct vk_bundle *vk = &crc->c->vk; 395 + 396 + C(end_command_buffer(vk, crc->cmd)); 397 + 398 + return true; 399 + } 400 + 401 + void 402 + comp_rendering_compute_close(struct comp_rendering_compute *crc) 403 + { 404 + assert(crc->c != NULL); 405 + assert(crc->r != NULL); 406 + 407 + struct vk_bundle *vk = &crc->c->vk; 408 + 409 + destroy_command_buffer(vk, crc->cmd); 410 + 411 + // Reclaimed by vkResetDescriptorPool. 412 + crc->clear_descriptor_set = VK_NULL_HANDLE; 413 + 414 + vk->vkResetDescriptorPool(vk->device, crc->r->compute.descriptor_pool, 0); 415 + 416 + crc->c = NULL; 417 + crc->r = NULL; 418 + } 419 + 420 + void 421 + comp_rendering_compute_projection(struct comp_rendering_compute *crc, 422 + VkSampler src_samplers[2], 423 + VkImageView src_image_views[2], 424 + const struct xrt_normalized_rect src_norm_rects[2], 425 + VkImage target_image, 426 + VkImageView target_image_view, 427 + const struct comp_viewport_data views[2]) 428 + { 429 + assert(crc->c != NULL); 430 + assert(crc->r != NULL); 431 + 432 + struct vk_bundle *vk = &crc->c->vk; 433 + struct comp_resources *r = crc->r; 434 + 435 + 436 + /* 437 + * UBO 438 + */ 439 + 440 + struct comp_ubo_compute_data *data = (struct comp_ubo_compute_data *)r->compute.ubo.mapped; 441 + data->views[0] = views[0]; 442 + data->views[1] = views[1]; 443 + data->post_transforms[0] = src_norm_rects[0]; 444 + data->post_transforms[1] = src_norm_rects[1]; 445 + 446 + 447 + /* 448 + * Source, target and distortion images. 449 + */ 450 + 451 + VkImageSubresourceRange subresource_range = { 452 + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 453 + .baseMipLevel = 0, 454 + .levelCount = VK_REMAINING_MIP_LEVELS, 455 + .baseArrayLayer = 0, 456 + .layerCount = VK_REMAINING_ARRAY_LAYERS, 457 + }; 458 + 459 + vk_set_image_layout( // 460 + vk, // 461 + crc->cmd, // 462 + target_image, // 463 + 0, // 464 + VK_ACCESS_SHADER_WRITE_BIT, // 465 + VK_IMAGE_LAYOUT_UNDEFINED, // 466 + VK_IMAGE_LAYOUT_GENERAL, // 467 + subresource_range); // 468 + 469 + VkSampler sampler = r->compute.default_sampler; 470 + VkSampler distortion_samplers[6] = { 471 + sampler, sampler, sampler, sampler, sampler, sampler, 472 + }; 473 + 474 + update_compute_discriptor_set( // 475 + vk, // 476 + r->compute.src_binding, // 477 + src_samplers, // 478 + src_image_views, // 479 + r->compute.distortion_binding, // 480 + distortion_samplers, // 481 + r->distortion.image_views, // 482 + r->compute.target_binding, // 483 + target_image_view, // 484 + r->compute.ubo_binding, // 485 + r->compute.ubo.buffer, // 486 + VK_WHOLE_SIZE, // 487 + crc->clear_descriptor_set); // 488 + 489 + vk->vkCmdBindPipeline( // 490 + crc->cmd, // commandBuffer 491 + VK_PIPELINE_BIND_POINT_COMPUTE, // pipelineBindPoint 492 + r->compute.distortion_pipeline); // pipeline 493 + 494 + vk->vkCmdBindDescriptorSets( // 495 + crc->cmd, // commandBuffer 496 + VK_PIPELINE_BIND_POINT_COMPUTE, // pipelineBindPoint 497 + r->compute.pipeline_layout, // layout 498 + 0, // firstSet 499 + 1, // descriptorSetCount 500 + &crc->clear_descriptor_set, // pDescriptorSets 501 + 0, // dynamicOffsetCount 502 + NULL); // pDynamicOffsets 503 + 504 + 505 + uint32_t w = 0, h = 0; 506 + calc_dispatch_dims(views, &w, &h); 507 + assert(w != 0 && h != 0); 508 + 509 + vk->vkCmdDispatch( // 510 + crc->cmd, // commandBuffer 511 + w, // groupCountX 512 + h, // groupCountY 513 + 2); // groupCountZ 514 + 515 + VkImageMemoryBarrier memoryBarrier = { 516 + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 517 + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, 518 + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, 519 + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, 520 + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 521 + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 522 + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 523 + .image = target_image, 524 + .subresourceRange = subresource_range, 525 + }; 526 + 527 + vk->vkCmdPipelineBarrier( // 528 + crc->cmd, // 529 + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // 530 + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // 531 + 0, // 532 + 0, // 533 + NULL, // 534 + 0, // 535 + NULL, // 536 + 1, // 537 + &memoryBarrier); // 538 + } 539 + 540 + void 541 + comp_rendering_compute_clear(struct comp_rendering_compute *crc, // 542 + VkImage target_image, // 543 + VkImageView target_image_view, // 544 + const struct comp_viewport_data views[2]) // 545 + { 546 + assert(crc->c != NULL); 547 + assert(crc->r != NULL); 548 + 549 + struct vk_bundle *vk = &crc->c->vk; 550 + struct comp_resources *r = crc->r; 551 + 552 + 553 + /* 554 + * UBO 555 + */ 556 + 557 + // Calculate transforms. 558 + struct xrt_matrix_4x4 transforms[2]; 559 + for (uint32_t i = 0; i < 2; i++) { 560 + math_matrix_4x4_identity(&transforms[i]); 561 + } 562 + 563 + struct comp_ubo_compute_data *data = (struct comp_ubo_compute_data *)r->compute.ubo.mapped; 564 + data->views[0] = views[0]; 565 + data->views[1] = views[1]; 566 + 567 + 568 + /* 569 + * Source, target and distortion images. 570 + */ 571 + 572 + VkImageSubresourceRange subresource_range = { 573 + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 574 + .baseMipLevel = 0, 575 + .levelCount = VK_REMAINING_MIP_LEVELS, 576 + .baseArrayLayer = 0, 577 + .layerCount = VK_REMAINING_ARRAY_LAYERS, 578 + }; 579 + 580 + vk_set_image_layout( // 581 + vk, // 582 + crc->cmd, // 583 + target_image, // 584 + 0, // 585 + VK_ACCESS_SHADER_WRITE_BIT, // 586 + VK_IMAGE_LAYOUT_UNDEFINED, // 587 + VK_IMAGE_LAYOUT_GENERAL, // 588 + subresource_range); // 589 + 590 + VkSampler sampler = r->compute.default_sampler; 591 + VkSampler src_samplers[2] = {sampler, sampler}; 592 + VkImageView src_image_views[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE}; 593 + VkSampler distortion_samplers[6] = {sampler, sampler, sampler, sampler, sampler, sampler}; 594 + VkImageView distortion_image_views[6] = {VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, 595 + VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE}; 596 + 597 + update_compute_discriptor_set( // 598 + vk, // 599 + r->compute.src_binding, // 600 + src_samplers, // 601 + src_image_views, // 602 + r->compute.distortion_binding, // 603 + distortion_samplers, // 604 + distortion_image_views, // 605 + r->compute.target_binding, // 606 + target_image_view, // 607 + r->compute.ubo_binding, // 608 + r->compute.ubo.buffer, // 609 + VK_WHOLE_SIZE, // 610 + crc->clear_descriptor_set); // 611 + 612 + vk->vkCmdBindPipeline( // 613 + crc->cmd, // commandBuffer 614 + VK_PIPELINE_BIND_POINT_COMPUTE, // pipelineBindPoint 615 + r->compute.clear_pipeline); // pipeline 616 + 617 + vk->vkCmdBindDescriptorSets( // 618 + crc->cmd, // commandBuffer 619 + VK_PIPELINE_BIND_POINT_COMPUTE, // pipelineBindPoint 620 + r->compute.pipeline_layout, // layout 621 + 0, // firstSet 622 + 1, // descriptorSetCount 623 + &crc->clear_descriptor_set, // pDescriptorSets 624 + 0, // dynamicOffsetCount 625 + NULL); // pDynamicOffsets 626 + 627 + 628 + uint32_t w = 0, h = 0; 629 + calc_dispatch_dims(views, &w, &h); 630 + assert(w != 0 && h != 0); 631 + 632 + vk->vkCmdDispatch( // 633 + crc->cmd, // commandBuffer 634 + w, // groupCountX 635 + h, // groupCountY 636 + 2); // groupCountZ 637 + 638 + VkImageMemoryBarrier memoryBarrier = { 639 + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 640 + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, 641 + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, 642 + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, 643 + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 644 + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 645 + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 646 + .image = target_image, 647 + .subresourceRange = subresource_range, 648 + }; 649 + 650 + vk->vkCmdPipelineBarrier( // 651 + crc->cmd, // 652 + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // 653 + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // 654 + 0, // 655 + 0, // 656 + NULL, // 657 + 0, // 658 + NULL, // 659 + 1, // 660 + &memoryBarrier); // 661 + }
+172
src/xrt/compositor/render/comp_render.h
··· 31 31 32 32 /* 33 33 * 34 + * Defines 35 + * 36 + */ 37 + 38 + //! How large in pixels the distortion image is. 39 + #define COMP_DISTORTION_IMAGE_DIMENSIONS (128) 40 + 41 + //! How many distortion images we have, one for each channel (3 rgb) and per view, total 6. 42 + #define COMP_DISTORTION_NUM_IMAGES (6) 43 + 44 + 45 + /* 46 + * 34 47 * Buffer 35 48 * 36 49 */ ··· 148 161 uint32_t offset_indices[2]; 149 162 uint32_t total_num_indices; 150 163 } mesh; 164 + 165 + struct 166 + { 167 + //! Descriptor pool for compute work. 168 + VkDescriptorPool descriptor_pool; 169 + 170 + //! The source projection view binding point. 171 + uint32_t src_binding; 172 + 173 + //! Image storing the distortion. 174 + uint32_t distortion_binding; 175 + 176 + //! Writing the image out too. 177 + uint32_t target_binding; 178 + 179 + //! Uniform data binding. 180 + uint32_t ubo_binding; 181 + 182 + //! Dummy sampler for null images. 183 + VkSampler default_sampler; 184 + 185 + //! Descriptor set layout for compute distortion. 186 + VkDescriptorSetLayout descriptor_set_layout; 187 + 188 + //! Pipeline layout used for compute distortion. 189 + VkPipelineLayout pipeline_layout; 190 + 191 + //! Doesn't depend on target so is static. 192 + VkPipeline clear_pipeline; 193 + 194 + //! Doesn't depend on target so is static. 195 + VkPipeline distortion_pipeline; 196 + 197 + //! Target info. 198 + struct comp_buffer ubo; 199 + } compute; 200 + 201 + struct 202 + { 203 + //! Backing memory to distortion images. 204 + VkDeviceMemory device_memories[COMP_DISTORTION_NUM_IMAGES]; 205 + 206 + //! Distortion images. 207 + VkImage images[COMP_DISTORTION_NUM_IMAGES]; 208 + 209 + //! The views into the distortion images. 210 + VkImageView image_views[COMP_DISTORTION_NUM_IMAGES]; 211 + } distortion; 151 212 }; 152 213 153 214 /*! ··· 327 388 VkSampler sampler, 328 389 VkImageView image_view, 329 390 struct comp_mesh_ubo_data *data); 391 + 392 + 393 + /* 394 + * 395 + * Compute distortion. 396 + * 397 + */ 398 + 399 + /*! 400 + * A compute rendering is used to create command buffers needed to do one frame 401 + * of compositor rendering using compute shaders, it holds onto resources used 402 + * by the command buffer. 403 + */ 404 + struct comp_rendering_compute 405 + { 406 + struct comp_compositor *c; 407 + struct comp_resources *r; 408 + 409 + //! Command buffer where all commands are recorded. 410 + VkCommandBuffer cmd; 411 + 412 + //! Clear descriptor set. 413 + VkDescriptorSet clear_descriptor_set; 414 + 415 + #if 0 416 + struct 417 + { 418 + //! The data for this target. 419 + struct comp_target_data data; 420 + 421 + //! Image view we are targeting, not owned by the rendering. 422 + VkImageView image_view; 423 + } targets[2]; 424 + 425 + //! Number of different targets, number of views are always two. 426 + uint32_t num_targets; 427 + #endif 428 + 429 + struct 430 + { 431 + int temp; 432 + } view; 433 + 434 + //! The current view we are "rendering" to. 435 + uint32_t current_view; 436 + }; 437 + 438 + struct comp_rendering_compute_data 439 + { 440 + struct 441 + { 442 + VkImageView source; 443 + 444 + VkImageView distortion; 445 + 446 + struct 447 + { 448 + uint32_t x; 449 + uint32_t y; 450 + uint32_t width; 451 + uint32_t height; 452 + } dst; 453 + } views[2]; 454 + 455 + VkImageView target; 456 + }; 457 + 458 + /*! 459 + * UBO data that is sent to the compute distortion shaders. 460 + */ 461 + struct comp_ubo_compute_data 462 + { 463 + struct comp_viewport_data views[2]; 464 + struct xrt_normalized_rect pre_transforms[2]; 465 + struct xrt_normalized_rect post_transforms[2]; 466 + struct xrt_matrix_4x4 transforms[2]; 467 + }; 468 + 469 + /*! 470 + * Init struct and create resources needed for compute rendering. 471 + */ 472 + bool 473 + comp_rendering_compute_init(struct comp_compositor *c, struct comp_resources *r, struct comp_rendering_compute *crc); 474 + 475 + /*! 476 + * Frees all resources held by the compute rendering, does not free the struct itself. 477 + */ 478 + void 479 + comp_rendering_compute_close(struct comp_rendering_compute *crc); 480 + 481 + bool 482 + comp_rendering_compute_begin(struct comp_rendering_compute *crc); 483 + 484 + void 485 + comp_rendering_compute_projection(struct comp_rendering_compute *crc, // 486 + VkSampler src_samplers[2], // 487 + VkImageView src_image_views[2], // 488 + const struct xrt_normalized_rect src_rects[2], // 489 + VkImage target_image, // 490 + VkImageView target_image_view, // 491 + const struct comp_viewport_data views[2]); // 492 + 493 + void 494 + comp_rendering_compute_clear(struct comp_rendering_compute *crc, // 495 + VkImage target_image, // 496 + VkImageView target_image_view, // 497 + const struct comp_viewport_data views[2]); // 498 + 499 + bool 500 + comp_rendering_compute_end(struct comp_rendering_compute *crc); 501 + 330 502 331 503 /*! 332 504 * @}
+423
src/xrt/compositor/render/comp_resources.c
··· 29 29 thing = VK_NULL_HANDLE; \ 30 30 } 31 31 32 + #define DF(TYPE, thing) \ 33 + if (thing != VK_NULL_HANDLE) { \ 34 + vk->vkFree##TYPE(vk->device, thing, NULL); \ 35 + thing = VK_NULL_HANDLE; \ 36 + } 37 + 38 + 32 39 static VkResult 33 40 create_pipeline_cache(struct vk_bundle *vk, VkPipelineCache *out_pipeline_cache) 34 41 { ··· 257 264 258 265 /* 259 266 * 267 + * Compute 268 + * 269 + */ 270 + 271 + static VkResult 272 + create_compute_descriptor_set_layout(struct vk_bundle *vk, 273 + uint32_t src_binding, 274 + uint32_t distortion_binding, 275 + uint32_t target_binding, 276 + uint32_t ubo_binding, 277 + VkDescriptorSetLayout *out_descriptor_set_layout) 278 + { 279 + VkResult ret; 280 + 281 + VkDescriptorSetLayoutBinding set_layout_bindings[4] = { 282 + { 283 + .binding = src_binding, 284 + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 285 + .descriptorCount = 2, 286 + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, 287 + }, 288 + { 289 + .binding = distortion_binding, 290 + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 291 + .descriptorCount = 6, 292 + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, 293 + }, 294 + { 295 + .binding = target_binding, 296 + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 297 + .descriptorCount = 1, 298 + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, 299 + }, 300 + { 301 + .binding = ubo_binding, 302 + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 303 + .descriptorCount = 1, 304 + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, 305 + }, 306 + }; 307 + 308 + VkDescriptorSetLayoutCreateInfo set_layout_info = { 309 + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, 310 + .bindingCount = ARRAY_SIZE(set_layout_bindings), 311 + .pBindings = set_layout_bindings, 312 + }; 313 + 314 + VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE; 315 + ret = vk->vkCreateDescriptorSetLayout( // 316 + vk->device, // 317 + &set_layout_info, // 318 + NULL, // 319 + &descriptor_set_layout); // 320 + if (ret != VK_SUCCESS) { 321 + VK_ERROR(vk, "vkCreateDescriptorSetLayout failed: %s", vk_result_string(ret)); 322 + return ret; 323 + } 324 + 325 + *out_descriptor_set_layout = descriptor_set_layout; 326 + 327 + return VK_SUCCESS; 328 + } 329 + 330 + static VkResult 331 + create_compute_pipeline(struct vk_bundle *vk, 332 + VkPipelineCache pipeline_cache, 333 + VkShaderModule shader, 334 + VkPipelineLayout pipeline_layout, 335 + VkPipeline *out_compute_pipeline) 336 + { 337 + VkResult ret; 338 + 339 + VkPipelineShaderStageCreateInfo shader_stage_info = { 340 + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 341 + .pNext = NULL, 342 + .stage = VK_SHADER_STAGE_COMPUTE_BIT, 343 + .module = shader, 344 + .pName = "main", 345 + }; 346 + 347 + VkComputePipelineCreateInfo pipeline_info = { 348 + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, 349 + .pNext = NULL, 350 + .flags = 0, 351 + .stage = shader_stage_info, 352 + .layout = pipeline_layout, 353 + }; 354 + 355 + VkPipeline pipeline = VK_NULL_HANDLE; 356 + ret = vk->vkCreateComputePipelines( // 357 + vk->device, // 358 + pipeline_cache, // 359 + 1, // 360 + &pipeline_info, // 361 + NULL, // 362 + &pipeline); // 363 + if (ret != VK_SUCCESS) { 364 + VK_DEBUG(vk, "vkCreateComputePipelines failed: %s", vk_result_string(ret)); 365 + return ret; 366 + } 367 + 368 + *out_compute_pipeline = pipeline; 369 + 370 + return VK_SUCCESS; 371 + } 372 + 373 + static VkResult 374 + create_distortion_image_and_view(struct vk_bundle *vk, 375 + VkExtent2D extent, 376 + VkDeviceMemory *out_device_memory, 377 + VkImage *out_image, 378 + VkImageView *out_image_view) 379 + { 380 + VkFormat format = VK_FORMAT_R32G32_SFLOAT; 381 + VkImage image = VK_NULL_HANDLE; 382 + VkDeviceMemory device_memory = VK_NULL_HANDLE; 383 + VkImageView image_view = VK_NULL_HANDLE; 384 + 385 + C(vk_create_image_simple( // 386 + vk, // vk_bundle 387 + extent, // extent 388 + format, // format 389 + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, // usage 390 + &device_memory, // out_device_memory 391 + &image)); // out_image 392 + 393 + VkImageSubresourceRange subresource_range = { 394 + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 395 + .baseMipLevel = 0, 396 + .levelCount = VK_REMAINING_MIP_LEVELS, 397 + .baseArrayLayer = 0, 398 + .layerCount = VK_REMAINING_ARRAY_LAYERS, 399 + }; 400 + 401 + C(vk_create_view( // 402 + vk, // vk_bundle 403 + image, // image 404 + format, // format 405 + subresource_range, // subresource_range 406 + &image_view)); // out_image_view 407 + 408 + *out_device_memory = device_memory; 409 + *out_image = image; 410 + *out_image_view = image_view; 411 + 412 + return VK_SUCCESS; 413 + } 414 + 415 + static VkResult 416 + queue_upload_for_first_level_and_layer( 417 + struct vk_bundle *vk, VkCommandBuffer cmd, VkBuffer src, VkImage dst, VkExtent2D extent) 418 + { 419 + VkImageSubresourceRange subresource_range = { 420 + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 421 + .baseMipLevel = 0, 422 + .levelCount = VK_REMAINING_MIP_LEVELS, 423 + .baseArrayLayer = 0, 424 + .layerCount = VK_REMAINING_ARRAY_LAYERS, 425 + }; 426 + 427 + C(vk_set_image_layout( // 428 + vk, // 429 + cmd, // 430 + dst, // 431 + 0, // 432 + VK_ACCESS_TRANSFER_WRITE_BIT, // 433 + VK_IMAGE_LAYOUT_UNDEFINED, // 434 + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // 435 + subresource_range)); // 436 + 437 + VkImageSubresourceLayers subresource_layers = { 438 + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 439 + .mipLevel = 0, 440 + .baseArrayLayer = 0, 441 + .layerCount = 1, 442 + }; 443 + 444 + VkBufferImageCopy region = { 445 + .bufferOffset = 0, 446 + .bufferRowLength = 0, 447 + .bufferImageHeight = 0, 448 + .imageSubresource = subresource_layers, 449 + .imageOffset = {0, 0, 0}, 450 + .imageExtent = {extent.width, extent.height, 1}, 451 + }; 452 + 453 + vk->vkCmdCopyBufferToImage( // 454 + cmd, // commandBuffer 455 + src, // srcBuffer 456 + dst, // dstImage 457 + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // dstImageLayout 458 + 1, // regionCount 459 + &region); // pRegions 460 + 461 + C(vk_set_image_layout( // 462 + vk, // 463 + cmd, // 464 + dst, // 465 + 0, // 466 + VK_ACCESS_SHADER_READ_BIT, // 467 + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // 468 + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // 469 + subresource_range)); // 470 + 471 + return VK_SUCCESS; 472 + } 473 + 474 + static VkResult 475 + create_and_queue_upload(struct vk_bundle *vk, 476 + VkCommandBuffer cmd, 477 + VkBuffer src_buffer, 478 + VkDeviceMemory *out_image_device_memory, 479 + VkImage *out_image, 480 + VkImageView *out_image_view) 481 + { 482 + VkExtent2D extent = {COMP_DISTORTION_IMAGE_DIMENSIONS, COMP_DISTORTION_IMAGE_DIMENSIONS}; 483 + 484 + VkDeviceMemory device_memory = VK_NULL_HANDLE; 485 + VkImage image = VK_NULL_HANDLE; 486 + VkImageView image_view = VK_NULL_HANDLE; 487 + 488 + C(create_distortion_image_and_view( // 489 + vk, // vk_bundle 490 + extent, // extent 491 + &device_memory, // out_device_memory 492 + &image, // out_image 493 + &image_view)); // out_image_view 494 + 495 + C(queue_upload_for_first_level_and_layer( // 496 + vk, // vk_bundle 497 + cmd, // cmd 498 + src_buffer, // src 499 + image, // dst 500 + extent)); // extent 501 + 502 + *out_image_device_memory = device_memory; 503 + *out_image = image; 504 + *out_image_view = image_view; 505 + 506 + return VK_SUCCESS; 507 + } 508 + 509 + /*! 510 + * Helper struct to make code easier to read. 511 + */ 512 + struct texture 513 + { 514 + struct xrt_vec2 pixels[COMP_DISTORTION_IMAGE_DIMENSIONS][COMP_DISTORTION_IMAGE_DIMENSIONS]; 515 + }; 516 + 517 + static XRT_MAYBE_UNUSED VkResult 518 + create_and_file_in_distortion_buffer_for_view(struct vk_bundle *vk, 519 + struct xrt_device *xdev, 520 + struct comp_buffer *r_buffer, 521 + struct comp_buffer *g_buffer, 522 + struct comp_buffer *b_buffer, 523 + uint32_t view) 524 + { 525 + VkBufferUsageFlags usage_flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; 526 + VkMemoryPropertyFlags properties = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; 527 + 528 + 529 + VkDeviceSize size = sizeof(struct texture); 530 + 531 + C(comp_buffer_init(vk, r_buffer, usage_flags, properties, size)); 532 + C(comp_buffer_init(vk, g_buffer, usage_flags, properties, size)); 533 + C(comp_buffer_init(vk, b_buffer, usage_flags, properties, size)); 534 + 535 + C(comp_buffer_map(vk, r_buffer)); 536 + C(comp_buffer_map(vk, g_buffer)); 537 + C(comp_buffer_map(vk, b_buffer)); 538 + 539 + struct texture *r = r_buffer->mapped; 540 + struct texture *g = g_buffer->mapped; 541 + struct texture *b = b_buffer->mapped; 542 + 543 + for (int row = 0; row < COMP_DISTORTION_IMAGE_DIMENSIONS; row++) { 544 + // This goes from 0 to 1.0 inclusive. 545 + float v = (double)row / (double)COMP_DISTORTION_IMAGE_DIMENSIONS; 546 + 547 + for (int col = 0; col < COMP_DISTORTION_IMAGE_DIMENSIONS; col++) { 548 + // This goes from 0 to 1.0 inclusive. 549 + float u = (double)col / (double)COMP_DISTORTION_IMAGE_DIMENSIONS; 550 + 551 + struct xrt_uv_triplet result; 552 + xrt_device_compute_distortion(xdev, view, u, v, &result); 553 + 554 + 555 + r->pixels[row][col] = result.r; 556 + g->pixels[row][col] = result.g; 557 + b->pixels[row][col] = result.b; 558 + } 559 + } 560 + 561 + comp_buffer_unmap(vk, r_buffer); 562 + comp_buffer_unmap(vk, g_buffer); 563 + comp_buffer_unmap(vk, b_buffer); 564 + 565 + return VK_SUCCESS; 566 + } 567 + 568 + /* 569 + * 260 570 * 'Exported' renderer functions. 261 571 * 262 572 */ ··· 281 591 r->mesh.total_num_indices = parts->distortion.mesh.total_num_indices; 282 592 r->mesh.offset_indices[0] = parts->distortion.mesh.offset_indices[0]; 283 593 r->mesh.offset_indices[1] = parts->distortion.mesh.offset_indices[1]; 594 + 595 + r->compute.src_binding = 0; 596 + r->compute.distortion_binding = 1; 597 + r->compute.target_binding = 2; 598 + r->compute.ubo_binding = 3; 284 599 285 600 286 601 /* ··· 324 639 325 640 326 641 /* 642 + * Compute static. 643 + */ 644 + 645 + C(vk_create_sampler( // 646 + vk, // vk_bundle 647 + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // clamp_mode 648 + &r->compute.default_sampler)); // out_sampler 649 + 650 + C(create_descriptor_pool( // 651 + vk, // vk_bundle 652 + 1, // num_uniform_per_desc 653 + 8, // num_sampler_per_desc 654 + 1, // num_storage_per_desc 655 + 1, // num_descs 656 + false, // freeable 657 + &r->compute.descriptor_pool)); // out_descriptor_pool 658 + 659 + C(create_compute_descriptor_set_layout( // 660 + vk, // vk_bundle 661 + r->compute.src_binding, // src_binding, 662 + r->compute.distortion_binding, // distortion_binding, 663 + r->compute.target_binding, // target_binding, 664 + r->compute.ubo_binding, // ubo_binding, 665 + &r->compute.descriptor_set_layout)); // out_descriptor_set_layout 666 + 667 + C(create_pipeline_layout( // 668 + vk, // vk_bundle 669 + r->compute.descriptor_set_layout, // descriptor_set_layout 670 + &r->compute.pipeline_layout)); // out_pipeline_layout 671 + 672 + C(create_compute_pipeline( // 673 + vk, // vk_bundle 674 + r->pipeline_cache, // pipeline_cache 675 + c->shaders.clear_comp, // shader 676 + r->compute.pipeline_layout, // pipeline_layout 677 + &r->compute.clear_pipeline)); // out_compute_pipeline 678 + 679 + C(create_compute_pipeline( // 680 + vk, // vk_bundle 681 + r->pipeline_cache, // pipeline_cache 682 + c->shaders.distortion_comp, // shader 683 + r->compute.pipeline_layout, // pipeline_layout 684 + &r->compute.distortion_pipeline)); // out_compute_pipeline 685 + 686 + VkBufferUsageFlags ubo_usage_flags = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; 687 + VkMemoryPropertyFlags memory_property_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | 688 + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | 689 + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; 690 + size_t ubo_size = sizeof(struct comp_ubo_compute_data); 691 + 692 + C(comp_buffer_init( // 693 + vk, // vk_bundle 694 + &r->compute.ubo, // buffer 695 + ubo_usage_flags, // usage_flags 696 + memory_property_flags, // memory_property_flags 697 + ubo_size)); // size 698 + C(comp_buffer_map( // 699 + vk, // vk_bundle 700 + &r->compute.ubo)); // buffer 701 + 702 + 703 + struct comp_buffer buffers[COMP_DISTORTION_NUM_IMAGES]; 704 + 705 + create_and_file_in_distortion_buffer_for_view(vk, c->xdev, &buffers[0], &buffers[2], &buffers[4], 0); 706 + create_and_file_in_distortion_buffer_for_view(vk, c->xdev, &buffers[1], &buffers[3], &buffers[5], 1); 707 + 708 + VkCommandBuffer upload_buffer = VK_NULL_HANDLE; 709 + C(vk_init_cmd_buffer(vk, &upload_buffer)); 710 + 711 + for (uint32_t i = 0; i < COMP_DISTORTION_NUM_IMAGES; i++) { 712 + C(create_and_queue_upload( // 713 + vk, // vk_bundle 714 + upload_buffer, // cmd 715 + buffers[i].buffer, // src_buffer 716 + &r->distortion.device_memories[i], // out_image_device_memory 717 + &r->distortion.images[i], // out_image 718 + &r->distortion.image_views[i])); // out_image_view 719 + } 720 + 721 + C(vk_submit_cmd_buffer(vk, upload_buffer)); 722 + 723 + os_mutex_lock(&vk->queue_mutex); 724 + vk->vkDeviceWaitIdle(vk->device); 725 + os_mutex_unlock(&vk->queue_mutex); 726 + 727 + for (uint32_t i = 0; i < ARRAY_SIZE(buffers); i++) { 728 + comp_buffer_close(vk, &buffers[i]); 729 + } 730 + 731 + 732 + /* 327 733 * Done 328 734 */ 329 735 ··· 343 749 D(DescriptorPool, r->mesh_descriptor_pool); 344 750 comp_buffer_close(vk, &r->mesh.vbo); 345 751 comp_buffer_close(vk, &r->mesh.ibo); 752 + 753 + D(DescriptorPool, r->compute.descriptor_pool); 754 + D(DescriptorSetLayout, r->compute.descriptor_set_layout); 755 + D(Pipeline, r->compute.clear_pipeline); 756 + D(Pipeline, r->compute.distortion_pipeline); 757 + D(PipelineLayout, r->compute.pipeline_layout); 758 + D(Sampler, r->compute.default_sampler); 759 + for (uint32_t i = 0; i < ARRAY_SIZE(r->distortion.image_views); i++) { 760 + D(ImageView, r->distortion.image_views[i]); 761 + } 762 + for (uint32_t i = 0; i < ARRAY_SIZE(r->distortion.images); i++) { 763 + D(Image, r->distortion.images[i]); 764 + } 765 + for (uint32_t i = 0; i < ARRAY_SIZE(r->distortion.images); i++) { 766 + DF(Memory, r->distortion.device_memories[i]); 767 + } 768 + comp_buffer_close(vk, &r->compute.ubo); 346 769 }
+34
src/xrt/compositor/shaders/clear.comp
··· 1 + // Copyright 2021, Collabora Ltd. 2 + // Author: Jakob Bornecrantz <jakob@collabora.com> 3 + // SPDX-License-Identifier: BSL-1.0 4 + 5 + #version 460 6 + 7 + layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; 8 + 9 + layout(set = 0, binding = 2) uniform writeonly restrict image2D target; 10 + layout(set = 0, binding = 3) uniform restrict Config 11 + { 12 + ivec4 views[2]; 13 + vec4 pre_transform[2]; 14 + vec4 post_transform[2]; 15 + mat4 transform[2]; 16 + } data; 17 + 18 + void main() 19 + { 20 + uint ix = gl_GlobalInvocationID.x; 21 + uint iy = gl_GlobalInvocationID.y; 22 + uint iz = gl_GlobalInvocationID.z; 23 + 24 + ivec2 offset = ivec2(data.views[iz].xy); 25 + ivec2 extent = ivec2(data.views[iz].zw); 26 + 27 + if (ix >= extent.x || iy >= extent.y) { 28 + return; 29 + } 30 + 31 + vec4 colour = vec4(vec3(0.2), 1.0); 32 + 33 + imageStore(target, ivec2(offset.x + ix, offset.y + iy), colour); 34 + }
+86
src/xrt/compositor/shaders/distortion.comp
··· 1 + // Copyright 2021, Collabora Ltd. 2 + // Author: Jakob Bornecrantz <jakob@collabora.com> 3 + // SPDX-License-Identifier: BSL-1.0 4 + 5 + #version 460 6 + #extension GL_GOOGLE_include_directive : require 7 + 8 + #include "srgb.inc.glsl" 9 + 10 + layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; 11 + 12 + layout(set = 0, binding = 0) uniform sampler2D source[2]; 13 + layout(set = 0, binding = 1) uniform sampler2D distortion[6]; 14 + layout(set = 0, binding = 2) uniform writeonly restrict image2D target; 15 + layout(set = 0, binding = 3, std140) uniform restrict Config 16 + { 17 + ivec4 views[2]; 18 + vec4 pre_transform[2]; 19 + vec4 post_transform[2]; 20 + mat4 transform[2]; 21 + } ubo; 22 + 23 + 24 + vec2 position_to_uv(ivec2 extent, uint ix, uint iy) 25 + { 26 + float x = float(ix) / float(extent.x); 27 + float y = float(iy) / float(extent.y); 28 + 29 + vec2 dist_uv = vec2(x, y); 30 + 31 + #define DIM (128.0) 32 + #define STRETCH ((DIM - 1.0) / DIM) 33 + #define OFFSET (1.0 / (DIM * 2.0)) 34 + 35 + dist_uv = (dist_uv * STRETCH) + OFFSET; 36 + 37 + return dist_uv; 38 + } 39 + 40 + vec2 transform_uv(vec2 uv, uint iz) 41 + { 42 + vec2 values = uv; 43 + 44 + // To deal with OpenGL flip and sub image view. 45 + values.xy = values.xy * ubo.post_transform[iz].zw + ubo.post_transform[iz].xy; 46 + 47 + // Ready to be used. 48 + return values.xy; 49 + } 50 + 51 + void main() 52 + { 53 + uint ix = gl_GlobalInvocationID.x; 54 + uint iy = gl_GlobalInvocationID.y; 55 + uint iz = gl_GlobalInvocationID.z; 56 + 57 + ivec2 offset = ivec2(ubo.views[iz].xy); 58 + ivec2 extent = ivec2(ubo.views[iz].zw); 59 + 60 + if (ix >= extent.x || iy >= extent.y) { 61 + return; 62 + } 63 + 64 + vec2 dist_uv = position_to_uv(extent, ix, iy); 65 + 66 + vec2 r_uv = texture(distortion[iz + 0], dist_uv).xy; 67 + vec2 g_uv = texture(distortion[iz + 2], dist_uv).xy; 68 + vec2 b_uv = texture(distortion[iz + 4], dist_uv).xy; 69 + 70 + // Do any transformation needed. 71 + r_uv = transform_uv(r_uv, iz); 72 + g_uv = transform_uv(g_uv, iz); 73 + b_uv = transform_uv(b_uv, iz); 74 + 75 + // Sample the source with distorted and chromatic abberation corrected samples. 76 + vec4 colour = vec4( 77 + texture(source[iz], r_uv).r, 78 + texture(source[iz], g_uv).g, 79 + texture(source[iz], b_uv).b, 80 + 1); 81 + 82 + // Do colour correction here since there are no automatic conversion in hardware available. 83 + colour = vec4(from_linear_to_srgb(colour.rgb), 1); 84 + 85 + imageStore(target, ivec2(offset.x + ix, offset.y + iy), colour); 86 + }
+2
src/xrt/compositor/shaders/meson.build
··· 2 2 # SPDX-License-Identifier: BSL-1.0 3 3 4 4 shader_srcs = [ 5 + 'clear.comp', 6 + 'distortion.comp', 5 7 'mesh.frag', 6 8 'mesh.vert', 7 9 'layer.vert',
+22
src/xrt/compositor/shaders/srgb.inc.glsl
··· 1 + // Copyright 2021, Collabora Ltd. 2 + // Author: Jakob Bornecrantz <jakob@collabora.com> 3 + // SPDX-License-Identifier: BSL-1.0 4 + 5 + 6 + float from_linear_to_srgb_channel(float value) 7 + { 8 + if (value < 0.0031308) { 9 + return 12.92 * value; 10 + } else { 11 + return 1.055 * pow(value, 1.0 / 2.4) - 0.055; 12 + } 13 + } 14 + 15 + vec3 from_linear_to_srgb(vec3 linear_rgb) 16 + { 17 + return vec3( 18 + from_linear_to_srgb_channel(linear_rgb.r), 19 + from_linear_to_srgb_channel(linear_rgb.g), 20 + from_linear_to_srgb_channel(linear_rgb.b) 21 + ); 22 + }