diff --git a/src/xrt/compositor/CMakeLists.txt b/src/xrt/compositor/CMakeLists.txt index 4e137fabc..019e974fe 100644 --- a/src/xrt/compositor/CMakeLists.txt +++ b/src/xrt/compositor/CMakeLists.txt @@ -2,6 +2,8 @@ # SPDX-License-Identifier: BSL-1.0 spirv_shaders(SHADER_HEADERS + shaders/clear.comp + shaders/distortion.comp shaders/mesh.frag shaders/mesh.vert shaders/layer.frag @@ -34,6 +36,7 @@ set(MAIN_SOURCE_FILES main/comp_layer_renderer.h main/comp_layer_renderer.c render/comp_buffer.c + render/comp_compute.c render/comp_render.h render/comp_rendering.c render/comp_resources.c diff --git a/src/xrt/compositor/main/comp_compositor.c b/src/xrt/compositor/main/comp_compositor.c index 57b6660ac..4f083a4c4 100644 --- a/src/xrt/compositor/main/comp_compositor.c +++ b/src/xrt/compositor/main/comp_compositor.c @@ -405,18 +405,9 @@ compositor_layer_equirect2(struct xrt_compositor *xc, return do_single(xc, xdev, xsc, data); } -static xrt_result_t -compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_graphics_sync_handle_t sync_handle) +static void +do_graphics_layers(struct comp_compositor *c) { - COMP_TRACE_MARKER(); - - struct comp_compositor *c = comp_compositor(xc); - - COMP_SPEW(c, "LAYER_COMMIT at %8.3fms", ts_ms()); - - u_graphics_sync_unref(&sync_handle); - - // Always zero for now. uint32_t slot_id = 0; uint32_t num_layers = c->slots[slot_id].num_layers; @@ -490,6 +481,22 @@ compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_graphic assert(false); } } +} + +static xrt_result_t +compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_graphics_sync_handle_t sync_handle) +{ + COMP_TRACE_MARKER(); + + struct comp_compositor *c = comp_compositor(xc); + + COMP_SPEW(c, "LAYER_COMMIT at %8.3fms", ts_ms()); + + u_graphics_sync_unref(&sync_handle); + + if (!c->settings.use_compute) { + do_graphics_layers(c); + } comp_renderer_draw(c->r); @@ -742,6 +749,9 @@ static const char *required_device_extensions[] = { static const char *optional_device_extensions[] = { VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, +#ifdef VK_EXT_robustness2 + VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, +#endif }; @@ -894,22 +904,30 @@ compositor_init_vulkan(struct comp_compositor *c) VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT, // Default fallback. }; + bool use_compute = c->settings.use_compute; + + struct vk_device_features device_features = { + .shader_storage_image_write_without_format = true, + .null_descriptor = use_compute, + }; + // No other way then to try to see if realtime is available. for (size_t i = 0; i < ARRAY_SIZE(prios); i++) { ret = vk_create_device( // vk, // c->settings.selected_gpu_index, // - false, // compute_only + use_compute, // compute_only prios[i], // global_priority required_device_extensions, // ARRAY_SIZE(required_device_extensions), // optional_device_extensions, // ARRAY_SIZE(optional_device_extensions), // - NULL); // optional_device_features + &device_features); // optional_device_features // All ok! if (ret == VK_SUCCESS) { - COMP_INFO(c, "Created device/queue with %s priority.", prio_strs[i]); + COMP_INFO(c, "Created device and %s queue with %s priority.", + use_compute ? "compute" : "graphics", prio_strs[i]); break; } @@ -1112,11 +1130,13 @@ compositor_check_vulkan_caps(struct comp_compositor *c) return false; } + bool use_compute = c->settings.use_compute; + // follow same device selection logic as subsequent calls ret = vk_create_device( // temp_vk, // c->settings.selected_gpu_index, // - false, // compute_only + use_compute, // compute_only VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT, // global_priority required_device_extensions, // ARRAY_SIZE(required_device_extensions), // diff --git a/src/xrt/compositor/main/comp_compositor.h b/src/xrt/compositor/main/comp_compositor.h index cfb17f7ea..19e3b1ea7 100644 --- a/src/xrt/compositor/main/comp_compositor.h +++ b/src/xrt/compositor/main/comp_compositor.h @@ -139,6 +139,9 @@ enum comp_state struct comp_shaders { + VkShaderModule clear_comp; + VkShaderModule distortion_comp; + VkShaderModule mesh_vert; VkShaderModule mesh_frag; diff --git a/src/xrt/compositor/main/comp_renderer.c b/src/xrt/compositor/main/comp_renderer.c index 766376841..bc95c8ae0 100644 --- a/src/xrt/compositor/main/comp_renderer.c +++ b/src/xrt/compositor/main/comp_renderer.c @@ -308,13 +308,17 @@ renderer_create_renderings_and_fences(struct comp_renderer *r) struct vk_bundle *vk = &r->c->vk; - r->rrs = U_TYPED_ARRAY_CALLOC(struct comp_rendering, r->num_buffers); - r->fences = U_TYPED_ARRAY_CALLOC(VkFence, r->num_buffers); + bool use_compute = r->settings->use_compute; + if (!use_compute) { + r->rrs = U_TYPED_ARRAY_CALLOC(struct comp_rendering, r->num_buffers); - for (uint32_t i = 0; i < r->num_buffers; ++i) { - renderer_build_rendering(r, &r->rrs[i], i); + for (uint32_t i = 0; i < r->num_buffers; ++i) { + renderer_build_rendering(r, &r->rrs[i], i); + } } + r->fences = U_TYPED_ARRAY_CALLOC(VkFence, r->num_buffers); + for (uint32_t i = 0; i < r->num_buffers; i++) { VkFenceCreateInfo fence_info = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, @@ -438,14 +442,21 @@ renderer_ensure_images_and_renderings(struct comp_renderer *r, bool force_recrea // Make we sure we destroy all dependent things before creating new images. renderer_close_renderings_and_fences(r); - comp_target_create_images( // - r->c->target, // - r->c->settings.preferred.width, // - r->c->settings.preferred.height, // - r->settings->color_format, // - r->settings->color_space, // - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, // - r->settings->present_mode); // + VkImageUsageFlags image_usage = 0; + if (r->settings->use_compute) { + image_usage = VK_IMAGE_USAGE_STORAGE_BIT; + } else { + image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + + comp_target_create_images( // + r->c->target, // + r->c->settings.preferred.width, // + r->c->settings.preferred.height, // + r->settings->color_format, // + r->settings->color_space, // + image_usage, // + r->settings->present_mode); // r->num_buffers = r->c->target->num_images; @@ -501,7 +512,7 @@ renderer_wait_for_last_fence(struct comp_renderer *r) } static void -renderer_submit_queue(struct comp_renderer *r) +renderer_submit_queue(struct comp_renderer *r, VkCommandBuffer cmd) { COMP_TRACE_MARKER(); @@ -528,7 +539,7 @@ renderer_submit_queue(struct comp_renderer *r) .pWaitSemaphores = &r->semaphores.present_complete, .pWaitDstStageMask = stage_flags, .commandBufferCount = 1, - .pCommandBuffers = &r->rrs[r->acquired_buffer].cmd, + .pCommandBuffers = &cmd, .signalSemaphoreCount = 1, .pSignalSemaphores = &r->semaphores.render_complete, }; @@ -692,6 +703,128 @@ get_image_view(const struct comp_swapchain_image *image, enum xrt_layer_composit return image->views.no_alpha[array_index]; } +static void +dispatch_graphics(struct comp_renderer *r) +{ + COMP_TRACE_MARKER(); + + struct comp_compositor *c = r->c; + struct comp_target *ct = c->target; + + comp_target_mark_submit(ct, c->frame.rendering.id, os_monotonic_get_ns()); + + renderer_get_view_projection(r); + comp_layer_renderer_draw(r->lr); + + comp_target_update_timings(ct); + + renderer_submit_queue(r, r->rrs[r->acquired_buffer].cmd); +} + + +/* + * + * Compute + * + */ + +static void +do_projection_layers(struct comp_renderer *r, + struct comp_rendering_compute *crc, + const struct comp_layer *layer, + const struct xrt_layer_projection_view_data *lvd, + const struct xrt_layer_projection_view_data *rvd) +{ + const struct xrt_layer_data *data = &layer->data; + uint32_t left_array_index = lvd->sub.array_index; + uint32_t right_array_index = rvd->sub.array_index; + const struct comp_swapchain_image *left = &layer->scs[0]->images[lvd->sub.image_index]; + const struct comp_swapchain_image *right = &layer->scs[1]->images[rvd->sub.image_index]; + + struct comp_viewport_data views[2]; + calc_viewport_data(r, &views[0], &views[1]); + + VkImage target_image = r->c->target->images[r->acquired_buffer].handle; + VkImageView target_image_view = r->c->target->images[r->acquired_buffer].view; + + VkSampler src_samplers[2] = { + left->sampler, + right->sampler, + }; + + VkImageView src_image_views[2] = { + get_image_view(left, data->flags, left_array_index), + get_image_view(right, data->flags, right_array_index), + }; + + struct xrt_normalized_rect src_norm_rects[2] = {lvd->sub.norm_rect, rvd->sub.norm_rect}; + if (data->flip_y) { + src_norm_rects[0].h = -src_norm_rects[0].h; + src_norm_rects[0].y = 1 + src_norm_rects[0].y; + src_norm_rects[1].h = -src_norm_rects[1].h; + src_norm_rects[1].y = 1 + src_norm_rects[1].y; + } + + comp_rendering_compute_projection( // + crc, // + src_samplers, // + src_image_views, // + src_norm_rects, // + target_image, // + target_image_view, // + views); // +} + +static void +dispatch_compute(struct comp_renderer *r, struct comp_rendering_compute *crc) +{ + COMP_TRACE_MARKER(); + + struct comp_compositor *c = r->c; + struct comp_target *ct = c->target; + + comp_rendering_compute_init(c, &c->nr, crc); + comp_rendering_compute_begin(crc); + + struct comp_viewport_data views[2]; + calc_viewport_data(r, &views[0], &views[1]); + + VkImage target_image = r->c->target->images[r->acquired_buffer].handle; + VkImageView target_image_view = r->c->target->images[r->acquired_buffer].view; + + uint32_t slot_id = 0; + uint32_t num_layers = c->slots[slot_id].num_layers; + if (num_layers > 0 && c->slots[slot_id].layers[0].data.type == XRT_LAYER_STEREO_PROJECTION) { + int i = 0; + const struct comp_layer *layer = &c->slots[slot_id].layers[i]; + const struct xrt_layer_stereo_projection_data *stereo = &layer->data.stereo; + const struct xrt_layer_projection_view_data *lvd = &stereo->l; + const struct xrt_layer_projection_view_data *rvd = &stereo->r; + + do_projection_layers(r, crc, layer, lvd, rvd); + } else if (num_layers > 0 && c->slots[slot_id].layers[0].data.type == XRT_LAYER_STEREO_PROJECTION_DEPTH) { + int i = 0; + const struct comp_layer *layer = &c->slots[slot_id].layers[i]; + const struct xrt_layer_stereo_projection_depth_data *stereo = &layer->data.stereo_depth; + const struct xrt_layer_projection_view_data *lvd = &stereo->l; + const struct xrt_layer_projection_view_data *rvd = &stereo->r; + + do_projection_layers(r, crc, layer, lvd, rvd); + } else { + comp_rendering_compute_clear( // + crc, // + target_image, // + target_image_view, // + views); // + } + + comp_rendering_compute_end(crc); + + comp_target_mark_submit(ct, c->frame.rendering.id, os_monotonic_get_ns()); + + renderer_submit_queue(r, crc->cmd); +} + /* * @@ -914,14 +1047,13 @@ comp_renderer_draw(struct comp_renderer *r) comp_target_update_timings(ct); - comp_target_mark_submit(ct, c->frame.rendering.id, os_monotonic_get_ns()); - - renderer_get_view_projection(r); - comp_layer_renderer_draw(r->lr); - - comp_target_update_timings(ct); - - renderer_submit_queue(r); + bool use_compute = r->settings->use_compute; + struct comp_rendering_compute crc = {0}; + if (use_compute) { + dispatch_compute(r, &crc); + } else { + dispatch_graphics(r); + } renderer_present_swapchain_image(r, c->frame.rendering.desired_present_time_ns, c->frame.rendering.present_slop_ns); @@ -938,6 +1070,10 @@ comp_renderer_draw(struct comp_renderer *r) */ renderer_wait_gpu_idle(r); + if (use_compute) { + comp_rendering_compute_close(&crc); + } + /* * For direct mode this makes us wait until the last frame has been * actually shown to the user, this avoids us missing that we have diff --git a/src/xrt/compositor/main/comp_settings.c b/src/xrt/compositor/main/comp_settings.c index ed7be09bb..b02fa16b7 100644 --- a/src/xrt/compositor/main/comp_settings.c +++ b/src/xrt/compositor/main/comp_settings.c @@ -27,6 +27,7 @@ DEBUG_GET_ONCE_NUM_OPTION(scale_percentage, "XRT_COMPOSITOR_SCALE_PERCENTAGE", 1 DEBUG_GET_ONCE_BOOL_OPTION(xcb_fullscreen, "XRT_COMPOSITOR_XCB_FULLSCREEN", false) DEBUG_GET_ONCE_NUM_OPTION(xcb_display, "XRT_COMPOSITOR_XCB_DISPLAY", -1) DEBUG_GET_ONCE_NUM_OPTION(default_framerate, "XRT_COMPOSITOR_DEFAULT_FRAMERATE", 60) +DEBUG_GET_ONCE_BOOL_OPTION(compute, "XRT_COMPOSITOR_COMPUTE", false) // clang-format on void @@ -39,8 +40,15 @@ comp_settings_init(struct comp_settings *s, struct xrt_device *xdev) interval_ns = (1000 * 1000 * 1000) / default_framerate; } + s->use_compute = debug_get_bool_option_compute(); + + if (s->use_compute) { + s->color_format = VK_FORMAT_B8G8R8A8_UNORM; + } else { + s->color_format = VK_FORMAT_B8G8R8A8_SRGB; + } + s->display = debug_get_num_option_xcb_display(); - s->color_format = VK_FORMAT_B8G8R8A8_SRGB; s->color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; s->present_mode = VK_PRESENT_MODE_FIFO_KHR; s->window_type = WINDOW_AUTO; diff --git a/src/xrt/compositor/main/comp_settings.h b/src/xrt/compositor/main/comp_settings.h index 34eafa1cd..4501500b5 100644 --- a/src/xrt/compositor/main/comp_settings.h +++ b/src/xrt/compositor/main/comp_settings.h @@ -61,6 +61,8 @@ struct comp_settings { int display; + bool use_compute; + VkFormat color_format; VkColorSpaceKHR color_space; VkPresentModeKHR present_mode; diff --git a/src/xrt/compositor/main/comp_shaders.c b/src/xrt/compositor/main/comp_shaders.c index e0bdc2f85..113075e8d 100644 --- a/src/xrt/compositor/main/comp_shaders.c +++ b/src/xrt/compositor/main/comp_shaders.c @@ -20,6 +20,8 @@ #pragma GCC diagnostic ignored "-Wnewline-eof" +#include "shaders/clear.comp.h" +#include "shaders/distortion.comp.h" #include "shaders/layer.frag.h" #include "shaders/layer.vert.h" #include "shaders/equirect1.frag.h" @@ -78,6 +80,16 @@ shader_load(struct vk_bundle *vk, const uint32_t *code, size_t size, VkShaderMod bool comp_shaders_load(struct vk_bundle *vk, struct comp_shaders *s) { + C(shader_load(vk, // vk_bundle + shaders_clear_comp, // data + sizeof(shaders_clear_comp), // size + &s->clear_comp)); // out + + C(shader_load(vk, // vk_bundle + shaders_distortion_comp, // data + sizeof(shaders_distortion_comp), // size + &s->distortion_comp)); // out + C(shader_load(vk, // vk_bundle shaders_mesh_vert, // data sizeof(shaders_mesh_vert), // size @@ -128,6 +140,8 @@ comp_shaders_load(struct vk_bundle *vk, struct comp_shaders *s) void comp_shaders_close(struct vk_bundle *vk, struct comp_shaders *s) { + D(clear_comp); + D(distortion_comp); D(mesh_vert); D(mesh_frag); D(equirect1_vert); diff --git a/src/xrt/compositor/meson.build b/src/xrt/compositor/meson.build index 69957f81c..34e36834a 100644 --- a/src/xrt/compositor/meson.build +++ b/src/xrt/compositor/meson.build @@ -34,6 +34,7 @@ compositor_srcs = [ 'multi/comp_multi_private.h', 'multi/comp_multi_system.c', 'render/comp_buffer.c', + 'render/comp_compute.c', 'render/comp_render.h', 'render/comp_rendering.c', 'render/comp_resources.c', diff --git a/src/xrt/compositor/render/comp_compute.c b/src/xrt/compositor/render/comp_compute.c new file mode 100644 index 000000000..0d9ddaed8 --- /dev/null +++ b/src/xrt/compositor/render/comp_compute.c @@ -0,0 +1,661 @@ +// Copyright 2019-2021, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief The compositor compute based rendering code. + * @author Jakob Bornecrantz + * @ingroup comp_main + */ + +#include "math/m_api.h" +#include "main/comp_compositor.h" +#include "render/comp_render.h" + +#include + + +/* + * + * Defines + * + */ + +#define C(c) \ + do { \ + VkResult ret = c; \ + if (ret != VK_SUCCESS) { \ + return false; \ + } \ + } while (false) + +#define D(TYPE, thing) \ + if (thing != VK_NULL_HANDLE) { \ + vk->vkDestroy##TYPE(vk, vk->device, thing, NULL); \ + thing = VK_NULL_HANDLE; \ + } + +#define DD(pool, thing) \ + if (thing != VK_NULL_HANDLE) { \ + free_descriptor_set(vk, pool, thing); \ + thing = VK_NULL_HANDLE; \ + } + + +/* + * + * Helper functions. + * + */ + +/* + * For dispatching compute to the view, calculate the number of groups. + */ +static void +calc_dispatch_dims(const struct comp_viewport_data views[2], uint32_t *out_w, uint32_t *out_h) +{ +#define IMAX(a, b) ((a) > (b) ? (a) : (b)) + uint32_t w = IMAX(views[0].w, views[1].w); + uint32_t h = IMAX(views[0].h, views[1].h); +#undef IMAX + + // Power of two divide and round up. +#define P2_DIVIDE_ROUND_UP(v, div) ((v + (div - 1)) / div) + w = P2_DIVIDE_ROUND_UP(w, 8); + h = P2_DIVIDE_ROUND_UP(h, 8); +#undef P2_DIVIDE_ROUND_UP + + *out_w = w; + *out_h = h; +} + + +/* + * + * Vulkan helpers. + * + */ + +static VkResult +create_command_buffer(struct vk_bundle *vk, VkCommandBuffer *out_cmd) +{ + VkResult ret; + + VkCommandBufferAllocateInfo cmd_buffer_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = vk->cmd_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + + VkCommandBuffer cmd = VK_NULL_HANDLE; + + os_mutex_lock(&vk->cmd_pool_mutex); + + ret = vk->vkAllocateCommandBuffers( // + vk->device, // + &cmd_buffer_info, // + &cmd); // + + os_mutex_unlock(&vk->cmd_pool_mutex); + + if (ret != VK_SUCCESS) { + VK_ERROR(vk, "vkCreateFramebuffer failed: %s", vk_result_string(ret)); + return ret; + } + + *out_cmd = cmd; + + return VK_SUCCESS; +} + +static void +destroy_command_buffer(struct vk_bundle *vk, VkCommandBuffer command_buffer) +{ + os_mutex_lock(&vk->cmd_pool_mutex); + + vk->vkFreeCommandBuffers( // + vk->device, // device + vk->cmd_pool, // commandPool + 1, // commandBufferCount + &command_buffer); // pCommandBuffers + + os_mutex_unlock(&vk->cmd_pool_mutex); +} + +static VkResult +begin_command_buffer(struct vk_bundle *vk, VkCommandBuffer command_buffer) +{ + VkResult ret; + + VkCommandBufferBeginInfo command_buffer_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + }; + + ret = vk->vkBeginCommandBuffer( // + command_buffer, // + &command_buffer_info); // + if (ret != VK_SUCCESS) { + VK_ERROR(vk, "vkBeginCommandBuffer failed: %s", vk_result_string(ret)); + return ret; + } + + return VK_SUCCESS; +} + +static VkResult +end_command_buffer(struct vk_bundle *vk, VkCommandBuffer command_buffer) +{ + VkResult ret; + + // End the command buffer. + ret = vk->vkEndCommandBuffer( // + command_buffer); // + if (ret != VK_SUCCESS) { + VK_ERROR(vk, "vkEndCommandBuffer failed: %s", vk_result_string(ret)); + return ret; + } + + return VK_SUCCESS; +} + +static VkResult +create_descriptor_set(struct vk_bundle *vk, + VkDescriptorPool descriptor_pool, + VkDescriptorSetLayout descriptor_layout, + VkDescriptorSet *out_descriptor_set) +{ + VkResult ret; + + VkDescriptorSetAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = descriptor_pool, + .descriptorSetCount = 1, + .pSetLayouts = &descriptor_layout, + }; + + VkDescriptorSet descriptor_set = VK_NULL_HANDLE; + ret = vk->vkAllocateDescriptorSets( // + vk->device, // + &alloc_info, // + &descriptor_set); // + if (ret != VK_SUCCESS) { + VK_DEBUG(vk, "vkAllocateDescriptorSets failed: %s", vk_result_string(ret)); + return ret; + } + + *out_descriptor_set = descriptor_set; + + return VK_SUCCESS; +} + +XRT_MAYBE_UNUSED static void +update_compute_discriptor_set(struct vk_bundle *vk, + uint32_t src_binding, + VkSampler src_samplers[2], + VkImageView src_image_views[2], + uint32_t distortion_binding, + VkSampler distortion_samplers[6], + VkImageView distortion_image_views[6], + uint32_t target_binding, + VkImageView target_image_view, + uint32_t ubo_binding, + VkBuffer ubo_buffer, + VkDeviceSize ubo_size, + VkDescriptorSet descriptor_set) +{ + VkDescriptorImageInfo src_image_info[2] = { + { + .sampler = src_samplers[0], + .imageView = src_image_views[0], + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + { + .sampler = src_samplers[1], + .imageView = src_image_views[1], + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + }; + + VkDescriptorImageInfo distortion_image_info[6] = { + { + .sampler = distortion_samplers[0], + .imageView = distortion_image_views[0], + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + { + .sampler = distortion_samplers[1], + .imageView = distortion_image_views[1], + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + { + .sampler = distortion_samplers[2], + .imageView = distortion_image_views[2], + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + { + .sampler = distortion_samplers[3], + .imageView = distortion_image_views[3], + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + { + .sampler = distortion_samplers[4], + .imageView = distortion_image_views[4], + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + { + .sampler = distortion_samplers[5], + .imageView = distortion_image_views[5], + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }, + }; + + VkDescriptorImageInfo target_image_info = { + .imageView = target_image_view, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + + VkDescriptorBufferInfo buffer_info = { + .buffer = ubo_buffer, + .offset = 0, + .range = ubo_size, + }; + + VkWriteDescriptorSet write_descriptor_sets[4] = { + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptor_set, + .dstBinding = src_binding, + .descriptorCount = ARRAY_SIZE(src_image_info), + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = src_image_info, + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptor_set, + .dstBinding = distortion_binding, + .descriptorCount = ARRAY_SIZE(distortion_image_info), + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = distortion_image_info, + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptor_set, + .dstBinding = target_binding, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .pImageInfo = &target_image_info, + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptor_set, + .dstBinding = ubo_binding, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .pBufferInfo = &buffer_info, + }, + }; + + vk->vkUpdateDescriptorSets( // + vk->device, // + ARRAY_SIZE(write_descriptor_sets), // descriptorWriteCount + write_descriptor_sets, // pDescriptorWrites + 0, // descriptorCopyCount + NULL); // pDescriptorCopies +} + +XRT_MAYBE_UNUSED static void +update_compute_discriptor_set_target(struct vk_bundle *vk, + uint32_t target_binding, + VkImageView target_image_view, + uint32_t ubo_binding, + VkBuffer ubo_buffer, + VkDeviceSize ubo_size, + VkDescriptorSet descriptor_set) +{ + VkDescriptorImageInfo target_image_info = { + .imageView = target_image_view, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + + VkDescriptorBufferInfo buffer_info = { + .buffer = ubo_buffer, + .offset = 0, + .range = ubo_size, + }; + + VkWriteDescriptorSet write_descriptor_sets[2] = { + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptor_set, + .dstBinding = target_binding, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .pImageInfo = &target_image_info, + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptor_set, + .dstBinding = ubo_binding, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .pBufferInfo = &buffer_info, + }, + }; + + vk->vkUpdateDescriptorSets( // + vk->device, // + ARRAY_SIZE(write_descriptor_sets), // descriptorWriteCount + write_descriptor_sets, // pDescriptorWrites + 0, // descriptorCopyCount + NULL); // pDescriptorCopies +} + + +/* + * + * 'Exported' functions. + * + */ + +bool +comp_rendering_compute_init(struct comp_compositor *c, struct comp_resources *r, struct comp_rendering_compute *crc) +{ + assert(crc->c == NULL); + assert(crc->r == NULL); + + struct vk_bundle *vk = &c->vk; + crc->c = c; + crc->r = r; + + C(create_command_buffer(vk, &crc->cmd)); + + C(create_descriptor_set( // + vk, // + r->compute.descriptor_pool, // descriptor_pool + r->compute.descriptor_set_layout, // descriptor_set_layout + &crc->clear_descriptor_set)); // descriptor_set + + return true; +} + +bool +comp_rendering_compute_begin(struct comp_rendering_compute *crc) +{ + struct vk_bundle *vk = &crc->c->vk; + + C(begin_command_buffer(vk, crc->cmd)); + + return true; +} + +bool +comp_rendering_compute_end(struct comp_rendering_compute *crc) +{ + struct vk_bundle *vk = &crc->c->vk; + + C(end_command_buffer(vk, crc->cmd)); + + return true; +} + +void +comp_rendering_compute_close(struct comp_rendering_compute *crc) +{ + assert(crc->c != NULL); + assert(crc->r != NULL); + + struct vk_bundle *vk = &crc->c->vk; + + destroy_command_buffer(vk, crc->cmd); + + // Reclaimed by vkResetDescriptorPool. + crc->clear_descriptor_set = VK_NULL_HANDLE; + + vk->vkResetDescriptorPool(vk->device, crc->r->compute.descriptor_pool, 0); + + crc->c = NULL; + crc->r = NULL; +} + +void +comp_rendering_compute_projection(struct comp_rendering_compute *crc, + VkSampler src_samplers[2], + VkImageView src_image_views[2], + const struct xrt_normalized_rect src_norm_rects[2], + VkImage target_image, + VkImageView target_image_view, + const struct comp_viewport_data views[2]) +{ + assert(crc->c != NULL); + assert(crc->r != NULL); + + struct vk_bundle *vk = &crc->c->vk; + struct comp_resources *r = crc->r; + + + /* + * UBO + */ + + struct comp_ubo_compute_data *data = (struct comp_ubo_compute_data *)r->compute.ubo.mapped; + data->views[0] = views[0]; + data->views[1] = views[1]; + data->post_transforms[0] = src_norm_rects[0]; + data->post_transforms[1] = src_norm_rects[1]; + + + /* + * Source, target and distortion images. + */ + + VkImageSubresourceRange subresource_range = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + + vk_set_image_layout( // + vk, // + crc->cmd, // + target_image, // + 0, // + VK_ACCESS_SHADER_WRITE_BIT, // + VK_IMAGE_LAYOUT_UNDEFINED, // + VK_IMAGE_LAYOUT_GENERAL, // + subresource_range); // + + VkSampler sampler = r->compute.default_sampler; + VkSampler distortion_samplers[6] = { + sampler, sampler, sampler, sampler, sampler, sampler, + }; + + update_compute_discriptor_set( // + vk, // + r->compute.src_binding, // + src_samplers, // + src_image_views, // + r->compute.distortion_binding, // + distortion_samplers, // + r->distortion.image_views, // + r->compute.target_binding, // + target_image_view, // + r->compute.ubo_binding, // + r->compute.ubo.buffer, // + VK_WHOLE_SIZE, // + crc->clear_descriptor_set); // + + vk->vkCmdBindPipeline( // + crc->cmd, // commandBuffer + VK_PIPELINE_BIND_POINT_COMPUTE, // pipelineBindPoint + r->compute.distortion_pipeline); // pipeline + + vk->vkCmdBindDescriptorSets( // + crc->cmd, // commandBuffer + VK_PIPELINE_BIND_POINT_COMPUTE, // pipelineBindPoint + r->compute.pipeline_layout, // layout + 0, // firstSet + 1, // descriptorSetCount + &crc->clear_descriptor_set, // pDescriptorSets + 0, // dynamicOffsetCount + NULL); // pDynamicOffsets + + + uint32_t w = 0, h = 0; + calc_dispatch_dims(views, &w, &h); + assert(w != 0 && h != 0); + + vk->vkCmdDispatch( // + crc->cmd, // commandBuffer + w, // groupCountX + h, // groupCountY + 2); // groupCountZ + + VkImageMemoryBarrier memoryBarrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = target_image, + .subresourceRange = subresource_range, + }; + + vk->vkCmdPipelineBarrier( // + crc->cmd, // + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // + 0, // + 0, // + NULL, // + 0, // + NULL, // + 1, // + &memoryBarrier); // +} + +void +comp_rendering_compute_clear(struct comp_rendering_compute *crc, // + VkImage target_image, // + VkImageView target_image_view, // + const struct comp_viewport_data views[2]) // +{ + assert(crc->c != NULL); + assert(crc->r != NULL); + + struct vk_bundle *vk = &crc->c->vk; + struct comp_resources *r = crc->r; + + + /* + * UBO + */ + + // Calculate transforms. + struct xrt_matrix_4x4 transforms[2]; + for (uint32_t i = 0; i < 2; i++) { + math_matrix_4x4_identity(&transforms[i]); + } + + struct comp_ubo_compute_data *data = (struct comp_ubo_compute_data *)r->compute.ubo.mapped; + data->views[0] = views[0]; + data->views[1] = views[1]; + + + /* + * Source, target and distortion images. + */ + + VkImageSubresourceRange subresource_range = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + + vk_set_image_layout( // + vk, // + crc->cmd, // + target_image, // + 0, // + VK_ACCESS_SHADER_WRITE_BIT, // + VK_IMAGE_LAYOUT_UNDEFINED, // + VK_IMAGE_LAYOUT_GENERAL, // + subresource_range); // + + VkSampler sampler = r->compute.default_sampler; + VkSampler src_samplers[2] = {sampler, sampler}; + VkImageView src_image_views[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE}; + VkSampler distortion_samplers[6] = {sampler, sampler, sampler, sampler, sampler, sampler}; + VkImageView distortion_image_views[6] = {VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, + VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE}; + + update_compute_discriptor_set( // + vk, // + r->compute.src_binding, // + src_samplers, // + src_image_views, // + r->compute.distortion_binding, // + distortion_samplers, // + distortion_image_views, // + r->compute.target_binding, // + target_image_view, // + r->compute.ubo_binding, // + r->compute.ubo.buffer, // + VK_WHOLE_SIZE, // + crc->clear_descriptor_set); // + + vk->vkCmdBindPipeline( // + crc->cmd, // commandBuffer + VK_PIPELINE_BIND_POINT_COMPUTE, // pipelineBindPoint + r->compute.clear_pipeline); // pipeline + + vk->vkCmdBindDescriptorSets( // + crc->cmd, // commandBuffer + VK_PIPELINE_BIND_POINT_COMPUTE, // pipelineBindPoint + r->compute.pipeline_layout, // layout + 0, // firstSet + 1, // descriptorSetCount + &crc->clear_descriptor_set, // pDescriptorSets + 0, // dynamicOffsetCount + NULL); // pDynamicOffsets + + + uint32_t w = 0, h = 0; + calc_dispatch_dims(views, &w, &h); + assert(w != 0 && h != 0); + + vk->vkCmdDispatch( // + crc->cmd, // commandBuffer + w, // groupCountX + h, // groupCountY + 2); // groupCountZ + + VkImageMemoryBarrier memoryBarrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = target_image, + .subresourceRange = subresource_range, + }; + + vk->vkCmdPipelineBarrier( // + crc->cmd, // + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // + 0, // + 0, // + NULL, // + 0, // + NULL, // + 1, // + &memoryBarrier); // +} diff --git a/src/xrt/compositor/render/comp_render.h b/src/xrt/compositor/render/comp_render.h index 07d22b29c..3a8cae071 100644 --- a/src/xrt/compositor/render/comp_render.h +++ b/src/xrt/compositor/render/comp_render.h @@ -29,6 +29,19 @@ struct comp_swapchain_image; * @{ */ +/* + * + * Defines + * + */ + +//! How large in pixels the distortion image is. +#define COMP_DISTORTION_IMAGE_DIMENSIONS (128) + +//! How many distortion images we have, one for each channel (3 rgb) and per view, total 6. +#define COMP_DISTORTION_NUM_IMAGES (6) + + /* * * Buffer @@ -148,6 +161,54 @@ struct comp_resources uint32_t offset_indices[2]; uint32_t total_num_indices; } mesh; + + struct + { + //! Descriptor pool for compute work. + VkDescriptorPool descriptor_pool; + + //! The source projection view binding point. + uint32_t src_binding; + + //! Image storing the distortion. + uint32_t distortion_binding; + + //! Writing the image out too. + uint32_t target_binding; + + //! Uniform data binding. + uint32_t ubo_binding; + + //! Dummy sampler for null images. + VkSampler default_sampler; + + //! Descriptor set layout for compute distortion. + VkDescriptorSetLayout descriptor_set_layout; + + //! Pipeline layout used for compute distortion. + VkPipelineLayout pipeline_layout; + + //! Doesn't depend on target so is static. + VkPipeline clear_pipeline; + + //! Doesn't depend on target so is static. + VkPipeline distortion_pipeline; + + //! Target info. + struct comp_buffer ubo; + } compute; + + struct + { + //! Backing memory to distortion images. + VkDeviceMemory device_memories[COMP_DISTORTION_NUM_IMAGES]; + + //! Distortion images. + VkImage images[COMP_DISTORTION_NUM_IMAGES]; + + //! The views into the distortion images. + VkImageView image_views[COMP_DISTORTION_NUM_IMAGES]; + } distortion; }; /*! @@ -328,6 +389,117 @@ comp_draw_distortion(struct comp_rendering *rr, VkImageView image_view, struct comp_mesh_ubo_data *data); + +/* + * + * Compute distortion. + * + */ + +/*! + * A compute rendering is used to create command buffers needed to do one frame + * of compositor rendering using compute shaders, it holds onto resources used + * by the command buffer. + */ +struct comp_rendering_compute +{ + struct comp_compositor *c; + struct comp_resources *r; + + //! Command buffer where all commands are recorded. + VkCommandBuffer cmd; + + //! Clear descriptor set. + VkDescriptorSet clear_descriptor_set; + +#if 0 + struct + { + //! The data for this target. + struct comp_target_data data; + + //! Image view we are targeting, not owned by the rendering. + VkImageView image_view; + } targets[2]; + + //! Number of different targets, number of views are always two. + uint32_t num_targets; +#endif + + struct + { + int temp; + } view; + + //! The current view we are "rendering" to. + uint32_t current_view; +}; + +struct comp_rendering_compute_data +{ + struct + { + VkImageView source; + + VkImageView distortion; + + struct + { + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + } dst; + } views[2]; + + VkImageView target; +}; + +/*! + * UBO data that is sent to the compute distortion shaders. + */ +struct comp_ubo_compute_data +{ + struct comp_viewport_data views[2]; + struct xrt_normalized_rect pre_transforms[2]; + struct xrt_normalized_rect post_transforms[2]; + struct xrt_matrix_4x4 transforms[2]; +}; + +/*! + * Init struct and create resources needed for compute rendering. + */ +bool +comp_rendering_compute_init(struct comp_compositor *c, struct comp_resources *r, struct comp_rendering_compute *crc); + +/*! + * Frees all resources held by the compute rendering, does not free the struct itself. + */ +void +comp_rendering_compute_close(struct comp_rendering_compute *crc); + +bool +comp_rendering_compute_begin(struct comp_rendering_compute *crc); + +void +comp_rendering_compute_projection(struct comp_rendering_compute *crc, // + VkSampler src_samplers[2], // + VkImageView src_image_views[2], // + const struct xrt_normalized_rect src_rects[2], // + VkImage target_image, // + VkImageView target_image_view, // + const struct comp_viewport_data views[2]); // + +void +comp_rendering_compute_clear(struct comp_rendering_compute *crc, // + VkImage target_image, // + VkImageView target_image_view, // + const struct comp_viewport_data views[2]); // + +bool +comp_rendering_compute_end(struct comp_rendering_compute *crc); + + /*! * @} */ diff --git a/src/xrt/compositor/render/comp_resources.c b/src/xrt/compositor/render/comp_resources.c index 1af96fb3e..4892beffb 100644 --- a/src/xrt/compositor/render/comp_resources.c +++ b/src/xrt/compositor/render/comp_resources.c @@ -29,6 +29,13 @@ thing = VK_NULL_HANDLE; \ } +#define DF(TYPE, thing) \ + if (thing != VK_NULL_HANDLE) { \ + vk->vkFree##TYPE(vk->device, thing, NULL); \ + thing = VK_NULL_HANDLE; \ + } + + static VkResult create_pipeline_cache(struct vk_bundle *vk, VkPipelineCache *out_pipeline_cache) { @@ -255,6 +262,309 @@ init_mesh_vertex_buffers(struct vk_bundle *vk, } +/* + * + * Compute + * + */ + +static VkResult +create_compute_descriptor_set_layout(struct vk_bundle *vk, + uint32_t src_binding, + uint32_t distortion_binding, + uint32_t target_binding, + uint32_t ubo_binding, + VkDescriptorSetLayout *out_descriptor_set_layout) +{ + VkResult ret; + + VkDescriptorSetLayoutBinding set_layout_bindings[4] = { + { + .binding = src_binding, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 2, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + }, + { + .binding = distortion_binding, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 6, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + }, + { + .binding = target_binding, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + }, + { + .binding = ubo_binding, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + VkDescriptorSetLayoutCreateInfo set_layout_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = ARRAY_SIZE(set_layout_bindings), + .pBindings = set_layout_bindings, + }; + + VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE; + ret = vk->vkCreateDescriptorSetLayout( // + vk->device, // + &set_layout_info, // + NULL, // + &descriptor_set_layout); // + if (ret != VK_SUCCESS) { + VK_ERROR(vk, "vkCreateDescriptorSetLayout failed: %s", vk_result_string(ret)); + return ret; + } + + *out_descriptor_set_layout = descriptor_set_layout; + + return VK_SUCCESS; +} + +static VkResult +create_compute_pipeline(struct vk_bundle *vk, + VkPipelineCache pipeline_cache, + VkShaderModule shader, + VkPipelineLayout pipeline_layout, + VkPipeline *out_compute_pipeline) +{ + VkResult ret; + + VkPipelineShaderStageCreateInfo shader_stage_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = NULL, + .stage = VK_SHADER_STAGE_COMPUTE_BIT, + .module = shader, + .pName = "main", + }; + + VkComputePipelineCreateInfo pipeline_info = { + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + .stage = shader_stage_info, + .layout = pipeline_layout, + }; + + VkPipeline pipeline = VK_NULL_HANDLE; + ret = vk->vkCreateComputePipelines( // + vk->device, // + pipeline_cache, // + 1, // + &pipeline_info, // + NULL, // + &pipeline); // + if (ret != VK_SUCCESS) { + VK_DEBUG(vk, "vkCreateComputePipelines failed: %s", vk_result_string(ret)); + return ret; + } + + *out_compute_pipeline = pipeline; + + return VK_SUCCESS; +} + +static VkResult +create_distortion_image_and_view(struct vk_bundle *vk, + VkExtent2D extent, + VkDeviceMemory *out_device_memory, + VkImage *out_image, + VkImageView *out_image_view) +{ + VkFormat format = VK_FORMAT_R32G32_SFLOAT; + VkImage image = VK_NULL_HANDLE; + VkDeviceMemory device_memory = VK_NULL_HANDLE; + VkImageView image_view = VK_NULL_HANDLE; + + C(vk_create_image_simple( // + vk, // vk_bundle + extent, // extent + format, // format + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, // usage + &device_memory, // out_device_memory + &image)); // out_image + + VkImageSubresourceRange subresource_range = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + + C(vk_create_view( // + vk, // vk_bundle + image, // image + format, // format + subresource_range, // subresource_range + &image_view)); // out_image_view + + *out_device_memory = device_memory; + *out_image = image; + *out_image_view = image_view; + + return VK_SUCCESS; +} + +static VkResult +queue_upload_for_first_level_and_layer( + struct vk_bundle *vk, VkCommandBuffer cmd, VkBuffer src, VkImage dst, VkExtent2D extent) +{ + VkImageSubresourceRange subresource_range = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + + C(vk_set_image_layout( // + vk, // + cmd, // + dst, // + 0, // + VK_ACCESS_TRANSFER_WRITE_BIT, // + VK_IMAGE_LAYOUT_UNDEFINED, // + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // + subresource_range)); // + + VkImageSubresourceLayers subresource_layers = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }; + + VkBufferImageCopy region = { + .bufferOffset = 0, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = subresource_layers, + .imageOffset = {0, 0, 0}, + .imageExtent = {extent.width, extent.height, 1}, + }; + + vk->vkCmdCopyBufferToImage( // + cmd, // commandBuffer + src, // srcBuffer + dst, // dstImage + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // dstImageLayout + 1, // regionCount + ®ion); // pRegions + + C(vk_set_image_layout( // + vk, // + cmd, // + dst, // + 0, // + VK_ACCESS_SHADER_READ_BIT, // + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // + subresource_range)); // + + return VK_SUCCESS; +} + +static VkResult +create_and_queue_upload(struct vk_bundle *vk, + VkCommandBuffer cmd, + VkBuffer src_buffer, + VkDeviceMemory *out_image_device_memory, + VkImage *out_image, + VkImageView *out_image_view) +{ + VkExtent2D extent = {COMP_DISTORTION_IMAGE_DIMENSIONS, COMP_DISTORTION_IMAGE_DIMENSIONS}; + + VkDeviceMemory device_memory = VK_NULL_HANDLE; + VkImage image = VK_NULL_HANDLE; + VkImageView image_view = VK_NULL_HANDLE; + + C(create_distortion_image_and_view( // + vk, // vk_bundle + extent, // extent + &device_memory, // out_device_memory + &image, // out_image + &image_view)); // out_image_view + + C(queue_upload_for_first_level_and_layer( // + vk, // vk_bundle + cmd, // cmd + src_buffer, // src + image, // dst + extent)); // extent + + *out_image_device_memory = device_memory; + *out_image = image; + *out_image_view = image_view; + + return VK_SUCCESS; +} + +/*! + * Helper struct to make code easier to read. + */ +struct texture +{ + struct xrt_vec2 pixels[COMP_DISTORTION_IMAGE_DIMENSIONS][COMP_DISTORTION_IMAGE_DIMENSIONS]; +}; + +static XRT_MAYBE_UNUSED VkResult +create_and_file_in_distortion_buffer_for_view(struct vk_bundle *vk, + struct xrt_device *xdev, + struct comp_buffer *r_buffer, + struct comp_buffer *g_buffer, + struct comp_buffer *b_buffer, + uint32_t view) +{ + VkBufferUsageFlags usage_flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + VkMemoryPropertyFlags properties = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + + + VkDeviceSize size = sizeof(struct texture); + + C(comp_buffer_init(vk, r_buffer, usage_flags, properties, size)); + C(comp_buffer_init(vk, g_buffer, usage_flags, properties, size)); + C(comp_buffer_init(vk, b_buffer, usage_flags, properties, size)); + + C(comp_buffer_map(vk, r_buffer)); + C(comp_buffer_map(vk, g_buffer)); + C(comp_buffer_map(vk, b_buffer)); + + struct texture *r = r_buffer->mapped; + struct texture *g = g_buffer->mapped; + struct texture *b = b_buffer->mapped; + + for (int row = 0; row < COMP_DISTORTION_IMAGE_DIMENSIONS; row++) { + // This goes from 0 to 1.0 inclusive. + float v = (double)row / (double)COMP_DISTORTION_IMAGE_DIMENSIONS; + + for (int col = 0; col < COMP_DISTORTION_IMAGE_DIMENSIONS; col++) { + // This goes from 0 to 1.0 inclusive. + float u = (double)col / (double)COMP_DISTORTION_IMAGE_DIMENSIONS; + + struct xrt_uv_triplet result; + xrt_device_compute_distortion(xdev, view, u, v, &result); + + + r->pixels[row][col] = result.r; + g->pixels[row][col] = result.g; + b->pixels[row][col] = result.b; + } + } + + comp_buffer_unmap(vk, r_buffer); + comp_buffer_unmap(vk, g_buffer); + comp_buffer_unmap(vk, b_buffer); + + return VK_SUCCESS; +} + /* * * 'Exported' renderer functions. @@ -282,6 +592,11 @@ comp_resources_init(struct comp_compositor *c, struct comp_resources *r) r->mesh.offset_indices[0] = parts->distortion.mesh.offset_indices[0]; r->mesh.offset_indices[1] = parts->distortion.mesh.offset_indices[1]; + r->compute.src_binding = 0; + r->compute.distortion_binding = 1; + r->compute.target_binding = 2; + r->compute.ubo_binding = 3; + /* * Shared @@ -323,6 +638,97 @@ comp_resources_init(struct comp_compositor *c, struct comp_resources *r) } + /* + * Compute static. + */ + + C(vk_create_sampler( // + vk, // vk_bundle + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // clamp_mode + &r->compute.default_sampler)); // out_sampler + + C(create_descriptor_pool( // + vk, // vk_bundle + 1, // num_uniform_per_desc + 8, // num_sampler_per_desc + 1, // num_storage_per_desc + 1, // num_descs + false, // freeable + &r->compute.descriptor_pool)); // out_descriptor_pool + + C(create_compute_descriptor_set_layout( // + vk, // vk_bundle + r->compute.src_binding, // src_binding, + r->compute.distortion_binding, // distortion_binding, + r->compute.target_binding, // target_binding, + r->compute.ubo_binding, // ubo_binding, + &r->compute.descriptor_set_layout)); // out_descriptor_set_layout + + C(create_pipeline_layout( // + vk, // vk_bundle + r->compute.descriptor_set_layout, // descriptor_set_layout + &r->compute.pipeline_layout)); // out_pipeline_layout + + C(create_compute_pipeline( // + vk, // vk_bundle + r->pipeline_cache, // pipeline_cache + c->shaders.clear_comp, // shader + r->compute.pipeline_layout, // pipeline_layout + &r->compute.clear_pipeline)); // out_compute_pipeline + + C(create_compute_pipeline( // + vk, // vk_bundle + r->pipeline_cache, // pipeline_cache + c->shaders.distortion_comp, // shader + r->compute.pipeline_layout, // pipeline_layout + &r->compute.distortion_pipeline)); // out_compute_pipeline + + VkBufferUsageFlags ubo_usage_flags = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + VkMemoryPropertyFlags memory_property_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + size_t ubo_size = sizeof(struct comp_ubo_compute_data); + + C(comp_buffer_init( // + vk, // vk_bundle + &r->compute.ubo, // buffer + ubo_usage_flags, // usage_flags + memory_property_flags, // memory_property_flags + ubo_size)); // size + C(comp_buffer_map( // + vk, // vk_bundle + &r->compute.ubo)); // buffer + + + struct comp_buffer buffers[COMP_DISTORTION_NUM_IMAGES]; + + create_and_file_in_distortion_buffer_for_view(vk, c->xdev, &buffers[0], &buffers[2], &buffers[4], 0); + create_and_file_in_distortion_buffer_for_view(vk, c->xdev, &buffers[1], &buffers[3], &buffers[5], 1); + + VkCommandBuffer upload_buffer = VK_NULL_HANDLE; + C(vk_init_cmd_buffer(vk, &upload_buffer)); + + for (uint32_t i = 0; i < COMP_DISTORTION_NUM_IMAGES; i++) { + C(create_and_queue_upload( // + vk, // vk_bundle + upload_buffer, // cmd + buffers[i].buffer, // src_buffer + &r->distortion.device_memories[i], // out_image_device_memory + &r->distortion.images[i], // out_image + &r->distortion.image_views[i])); // out_image_view + } + + C(vk_submit_cmd_buffer(vk, upload_buffer)); + + os_mutex_lock(&vk->queue_mutex); + vk->vkDeviceWaitIdle(vk->device); + os_mutex_unlock(&vk->queue_mutex); + + for (uint32_t i = 0; i < ARRAY_SIZE(buffers); i++) { + comp_buffer_close(vk, &buffers[i]); + } + + /* * Done */ @@ -343,4 +749,21 @@ comp_resources_close(struct comp_compositor *c, struct comp_resources *r) D(DescriptorPool, r->mesh_descriptor_pool); comp_buffer_close(vk, &r->mesh.vbo); comp_buffer_close(vk, &r->mesh.ibo); + + D(DescriptorPool, r->compute.descriptor_pool); + D(DescriptorSetLayout, r->compute.descriptor_set_layout); + D(Pipeline, r->compute.clear_pipeline); + D(Pipeline, r->compute.distortion_pipeline); + D(PipelineLayout, r->compute.pipeline_layout); + D(Sampler, r->compute.default_sampler); + for (uint32_t i = 0; i < ARRAY_SIZE(r->distortion.image_views); i++) { + D(ImageView, r->distortion.image_views[i]); + } + for (uint32_t i = 0; i < ARRAY_SIZE(r->distortion.images); i++) { + D(Image, r->distortion.images[i]); + } + for (uint32_t i = 0; i < ARRAY_SIZE(r->distortion.images); i++) { + DF(Memory, r->distortion.device_memories[i]); + } + comp_buffer_close(vk, &r->compute.ubo); } diff --git a/src/xrt/compositor/shaders/clear.comp b/src/xrt/compositor/shaders/clear.comp new file mode 100644 index 000000000..4d3c45bcc --- /dev/null +++ b/src/xrt/compositor/shaders/clear.comp @@ -0,0 +1,34 @@ +// Copyright 2021, Collabora Ltd. +// Author: Jakob Bornecrantz +// SPDX-License-Identifier: BSL-1.0 + +#version 460 + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(set = 0, binding = 2) uniform writeonly restrict image2D target; +layout(set = 0, binding = 3) uniform restrict Config +{ + ivec4 views[2]; + vec4 pre_transform[2]; + vec4 post_transform[2]; + mat4 transform[2]; +} data; + +void main() +{ + uint ix = gl_GlobalInvocationID.x; + uint iy = gl_GlobalInvocationID.y; + uint iz = gl_GlobalInvocationID.z; + + ivec2 offset = ivec2(data.views[iz].xy); + ivec2 extent = ivec2(data.views[iz].zw); + + if (ix >= extent.x || iy >= extent.y) { + return; + } + + vec4 colour = vec4(vec3(0.2), 1.0); + + imageStore(target, ivec2(offset.x + ix, offset.y + iy), colour); +} diff --git a/src/xrt/compositor/shaders/distortion.comp b/src/xrt/compositor/shaders/distortion.comp new file mode 100644 index 000000000..822552a53 --- /dev/null +++ b/src/xrt/compositor/shaders/distortion.comp @@ -0,0 +1,86 @@ +// Copyright 2021, Collabora Ltd. +// Author: Jakob Bornecrantz +// SPDX-License-Identifier: BSL-1.0 + +#version 460 +#extension GL_GOOGLE_include_directive : require + +#include "srgb.inc.glsl" + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(set = 0, binding = 0) uniform sampler2D source[2]; +layout(set = 0, binding = 1) uniform sampler2D distortion[6]; +layout(set = 0, binding = 2) uniform writeonly restrict image2D target; +layout(set = 0, binding = 3, std140) uniform restrict Config +{ + ivec4 views[2]; + vec4 pre_transform[2]; + vec4 post_transform[2]; + mat4 transform[2]; +} ubo; + + +vec2 position_to_uv(ivec2 extent, uint ix, uint iy) +{ + float x = float(ix) / float(extent.x); + float y = float(iy) / float(extent.y); + + vec2 dist_uv = vec2(x, y); + +#define DIM (128.0) +#define STRETCH ((DIM - 1.0) / DIM) +#define OFFSET (1.0 / (DIM * 2.0)) + + dist_uv = (dist_uv * STRETCH) + OFFSET; + + return dist_uv; +} + +vec2 transform_uv(vec2 uv, uint iz) +{ + vec2 values = uv; + + // To deal with OpenGL flip and sub image view. + values.xy = values.xy * ubo.post_transform[iz].zw + ubo.post_transform[iz].xy; + + // Ready to be used. + return values.xy; +} + +void main() +{ + uint ix = gl_GlobalInvocationID.x; + uint iy = gl_GlobalInvocationID.y; + uint iz = gl_GlobalInvocationID.z; + + ivec2 offset = ivec2(ubo.views[iz].xy); + ivec2 extent = ivec2(ubo.views[iz].zw); + + if (ix >= extent.x || iy >= extent.y) { + return; + } + + vec2 dist_uv = position_to_uv(extent, ix, iy); + + vec2 r_uv = texture(distortion[iz + 0], dist_uv).xy; + vec2 g_uv = texture(distortion[iz + 2], dist_uv).xy; + vec2 b_uv = texture(distortion[iz + 4], dist_uv).xy; + + // Do any transformation needed. + r_uv = transform_uv(r_uv, iz); + g_uv = transform_uv(g_uv, iz); + b_uv = transform_uv(b_uv, iz); + + // Sample the source with distorted and chromatic abberation corrected samples. + vec4 colour = vec4( + texture(source[iz], r_uv).r, + texture(source[iz], g_uv).g, + texture(source[iz], b_uv).b, + 1); + + // Do colour correction here since there are no automatic conversion in hardware available. + colour = vec4(from_linear_to_srgb(colour.rgb), 1); + + imageStore(target, ivec2(offset.x + ix, offset.y + iy), colour); +} diff --git a/src/xrt/compositor/shaders/meson.build b/src/xrt/compositor/shaders/meson.build index 070be4246..7cbc72b52 100644 --- a/src/xrt/compositor/shaders/meson.build +++ b/src/xrt/compositor/shaders/meson.build @@ -2,6 +2,8 @@ # SPDX-License-Identifier: BSL-1.0 shader_srcs = [ + 'clear.comp', + 'distortion.comp', 'mesh.frag', 'mesh.vert', 'layer.vert', diff --git a/src/xrt/compositor/shaders/srgb.inc.glsl b/src/xrt/compositor/shaders/srgb.inc.glsl new file mode 100644 index 000000000..af1b90ed1 --- /dev/null +++ b/src/xrt/compositor/shaders/srgb.inc.glsl @@ -0,0 +1,22 @@ +// Copyright 2021, Collabora Ltd. +// Author: Jakob Bornecrantz +// SPDX-License-Identifier: BSL-1.0 + + +float from_linear_to_srgb_channel(float value) +{ + if (value < 0.0031308) { + return 12.92 * value; + } else { + return 1.055 * pow(value, 1.0 / 2.4) - 0.055; + } +} + +vec3 from_linear_to_srgb(vec3 linear_rgb) +{ + return vec3( + from_linear_to_srgb_channel(linear_rgb.r), + from_linear_to_srgb_channel(linear_rgb.g), + from_linear_to_srgb_channel(linear_rgb.b) + ); +}