From 3b174b0dabdc656d74dfaa45a65b3c47a727c626 Mon Sep 17 00:00:00 2001 From: Jakob Bornecrantz Date: Thu, 12 Oct 2023 17:05:37 +0000 Subject: [PATCH] c/render: Add cylinder and equirect2 shaders Co-authored-by: Simon Zeni Co-authored-by: Charlton Rodda --- src/xrt/compositor/CMakeLists.txt | 4 ++++ src/xrt/compositor/render/render_gfx.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xrt/compositor/render/render_interface.h | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xrt/compositor/render/render_shaders.c | 13 +++++++++++++ src/xrt/compositor/shaders/layer_cylinder.frag | 17 +++++++++++++++++ src/xrt/compositor/shaders/layer_cylinder.vert | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xrt/compositor/shaders/layer_equirect2.frag | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xrt/compositor/shaders/layer_equirect2.vert | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 file(s) changed, 525 insertion(s)(+), 0 deletion(s)(-) diff --git a/src/xrt/compositor/CMakeLists.txt b/src/xrt/compositor/CMakeLists.txt --- a/src/xrt/compositor/CMakeLists.txt +++ b/src/xrt/compositor/CMakeLists.txt @@ -95,6 +95,10 @@ shaders/mesh.vert shaders/layer.frag shaders/layer.vert + shaders/layer_cylinder.frag + shaders/layer_cylinder.vert + shaders/layer_equirect2.frag + shaders/layer_equirect2.vert shaders/layer_projection.vert shaders/layer_quad.vert shaders/layer_shared.frag diff --git a/src/xrt/compositor/render/render_gfx.c b/src/xrt/compositor/render/render_gfx.c --- a/src/xrt/compositor/render/render_gfx.c +++ b/src/xrt/compositor/render/render_gfx.c @@ -755,6 +755,53 @@ const VkBlendFactor blend_factor_premultiplied_alpha = VK_BLEND_FACTOR_ONE; const VkBlendFactor blend_factor_unpremultiplied_alpha = VK_BLEND_FACTOR_SRC_ALPHA; + // Cylinder + ret = create_layer_pipeline( // + vk, // vk + rgrp->render_pass, // render_pass + r->gfx.layer.shared.pipeline_layout, // pipeline_layout + r->pipeline_cache, // pipeline_cache + blend_factor_premultiplied_alpha, // src_blend_factor + r->shaders->layer_cylinder_vert, // module_vert + r->shaders->layer_cylinder_frag, // module_frag + &rgrp->layer.cylinder_premultiplied_alpha); // out_pipeline + VK_CHK_WITH_RET(ret, "create_layer_pipeline", false); + + ret = create_layer_pipeline( // + vk, // vk + rgrp->render_pass, // render_pass + r->gfx.layer.shared.pipeline_layout, // pipeline_layout + r->pipeline_cache, // pipeline_cache + blend_factor_unpremultiplied_alpha, // src_blend_factor + r->shaders->layer_cylinder_vert, // module_vert + r->shaders->layer_cylinder_frag, // module_frag + &rgrp->layer.cylinder_unpremultiplied_alpha); // out_pipeline + VK_CHK_WITH_RET(ret, "create_layer_pipeline", false); + + // Equirect2 + ret = create_layer_pipeline( // + vk, // vk + rgrp->render_pass, // render_pass + r->gfx.layer.shared.pipeline_layout, // pipeline_layout + r->pipeline_cache, // pipeline_cache + blend_factor_premultiplied_alpha, // src_blend_factor + r->shaders->layer_equirect2_vert, // module_vert + r->shaders->layer_equirect2_frag, // module_frag + &rgrp->layer.equirect2_premultiplied_alpha); // out_pipeline + VK_CHK_WITH_RET(ret, "create_layer_pipeline", false); + + ret = create_layer_pipeline( // + vk, // vk + rgrp->render_pass, // render_pass + r->gfx.layer.shared.pipeline_layout, // pipeline_layout + r->pipeline_cache, // pipeline_cache + blend_factor_unpremultiplied_alpha, // src_blend_factor + r->shaders->layer_equirect2_vert, // module_vert + r->shaders->layer_equirect2_frag, // module_frag + &rgrp->layer.equirect2_unpremultiplied_alpha); // out_pipeline + VK_CHK_WITH_RET(ret, "create_layer_pipeline", false); + + // Projection. ret = create_layer_pipeline( // vk, // vk rgrp->render_pass, // render_pass @@ -777,6 +824,7 @@ &rgrp->layer.proj_unpremultiplied_alpha); // out_pipeline VK_CHK_WITH_RET(ret, "create_layer_pipeline", false); + // Quad ret = create_layer_pipeline( // vk, // vk rgrp->render_pass, // render_pass @@ -818,6 +866,10 @@ D(Pipeline, rgrp->mesh.pipeline); D(Pipeline, rgrp->mesh.pipeline_timewarp); + D(Pipeline, rgrp->layer.cylinder_premultiplied_alpha); + D(Pipeline, rgrp->layer.cylinder_unpremultiplied_alpha); + D(Pipeline, rgrp->layer.equirect2_premultiplied_alpha); + D(Pipeline, rgrp->layer.equirect2_unpremultiplied_alpha); D(Pipeline, rgrp->layer.proj_premultiplied_alpha); D(Pipeline, rgrp->layer.proj_unpremultiplied_alpha); D(Pipeline, rgrp->layer.quad_premultiplied_alpha); @@ -1164,6 +1216,50 @@ */ XRT_CHECK_RESULT VkResult +render_gfx_layer_cylinder_alloc_and_write(struct render_gfx *rr, + const struct render_gfx_layer_cylinder_data *data, + VkSampler src_sampler, + VkImageView src_image_view, + VkDescriptorSet *out_descriptor_set) +{ + struct render_resources *r = rr->r; + + return do_ubo_and_src_alloc_and_write( // + rr, // rr + RENDER_BINDING_LAYER_SHARED_UBO, // ubo_binding + data, // ubo_ptr + sizeof(*data), // ubo_size + RENDER_BINDING_LAYER_SHARED_SRC, // src_binding + src_sampler, // src_sampler + src_image_view, // src_image_view + r->gfx.ubo_and_src_descriptor_pool, // descriptor_pool + r->gfx.layer.shared.descriptor_set_layout, // descriptor_set_layout + out_descriptor_set); // out_descriptor_set +} + +XRT_CHECK_RESULT VkResult +render_gfx_layer_equirect2_alloc_and_write(struct render_gfx *rr, + const struct render_gfx_layer_equirect2_data *data, + VkSampler src_sampler, + VkImageView src_image_view, + VkDescriptorSet *out_descriptor_set) +{ + struct render_resources *r = rr->r; + + return do_ubo_and_src_alloc_and_write( // + rr, // rr + RENDER_BINDING_LAYER_SHARED_UBO, // ubo_binding + data, // ubo_ptr + sizeof(*data), // ubo_size + RENDER_BINDING_LAYER_SHARED_SRC, // src_binding + src_sampler, // src_sampler + src_image_view, // src_image_view + r->gfx.ubo_and_src_descriptor_pool, // descriptor_pool + r->gfx.layer.shared.descriptor_set_layout, // descriptor_set_layout + out_descriptor_set); // out_descriptor_set +} + +XRT_CHECK_RESULT VkResult render_gfx_layer_projection_alloc_and_write(struct render_gfx *rr, const struct render_gfx_layer_projection_data *data, VkSampler src_sampler, @@ -1205,6 +1301,46 @@ r->gfx.ubo_and_src_descriptor_pool, // descriptor_pool r->gfx.layer.shared.descriptor_set_layout, // descriptor_set_layout out_descriptor_set); // out_descriptor_set +} + +void +render_gfx_layer_cylinder(struct render_gfx *rr, bool premultiplied_alpha, VkDescriptorSet descriptor_set) +{ + VkPipeline pipeline = // + premultiplied_alpha // + ? rr->rtr->rgrp->layer.cylinder_premultiplied_alpha // + : rr->rtr->rgrp->layer.cylinder_unpremultiplied_alpha; // + + // One per degree. + uint32_t subdivisions = 360; + + // One edge on either endstop and one between each subdivision. + uint32_t edges = subdivisions + 1; + + // With triangle strip we get 2 vertices per edge. + uint32_t vertex_count = edges * 2; + + dispatch_no_vbo( // + rr, // rr + vertex_count, // vertex_count + pipeline, // pipeline + descriptor_set); // descriptor_set +} + +void +render_gfx_layer_equirect2(struct render_gfx *rr, bool premultiplied_alpha, VkDescriptorSet descriptor_set) +{ + VkPipeline pipeline = // + premultiplied_alpha // + ? rr->rtr->rgrp->layer.equirect2_premultiplied_alpha // + : rr->rtr->rgrp->layer.equirect2_unpremultiplied_alpha; // + + // Hardcoded to 4 vertices. + dispatch_no_vbo( // + rr, // rr + 4, // vertex_count + pipeline, // pipeline + descriptor_set); // descriptor_set } void diff --git a/src/xrt/compositor/render/render_interface.h b/src/xrt/compositor/render/render_interface.h --- a/src/xrt/compositor/render/render_interface.h +++ b/src/xrt/compositor/render/render_interface.h @@ -155,6 +155,12 @@ * New layer renderer. */ + VkShaderModule layer_cylinder_vert; + VkShaderModule layer_cylinder_frag; + + VkShaderModule layer_equirect2_vert; + VkShaderModule layer_equirect2_frag; + VkShaderModule layer_projection_vert; VkShaderModule layer_quad_vert; VkShaderModule layer_shared_frag; @@ -721,8 +727,15 @@ struct { + VkPipeline cylinder_premultiplied_alpha; + VkPipeline cylinder_unpremultiplied_alpha; + + VkPipeline equirect2_premultiplied_alpha; + VkPipeline equirect2_unpremultiplied_alpha; + VkPipeline proj_premultiplied_alpha; VkPipeline proj_unpremultiplied_alpha; + VkPipeline quad_premultiplied_alpha; VkPipeline quad_unpremultiplied_alpha; } layer; @@ -873,6 +886,36 @@ }; /*! + * UBO data that is sent to the layer cylinder shader. + */ +struct render_gfx_layer_cylinder_data +{ + struct xrt_normalized_rect post_transform; + struct xrt_matrix_4x4 mvp; + float radius; + float central_angle; + float aspect_ratio; + float _pad; +}; + +/*! + * UBO data that is sent to the layer equirect2 shader. + */ +struct render_gfx_layer_equirect2_data +{ + struct xrt_normalized_rect post_transform; + struct xrt_matrix_4x4 mv_inverse; + + //! See @ref render_calc_uv_to_tangent_lengths_rect. + struct xrt_normalized_rect to_tangent; + + float radius; + float central_horizontal_angle; + float upper_vertical_angle; + float lower_vertical_angle; +}; + +/*! * UBO data that is sent to the layer projection shader. */ struct render_gfx_layer_projection_data @@ -952,6 +995,32 @@ render_gfx_mesh_draw(struct render_gfx *rr, uint32_t mesh_index, VkDescriptorSet descriptor_set, bool do_timewarp); /*! + * Allocate and write a UBO and descriptor_set to be used for cylinder layer + * rendering, the content of @p data need to be valid at the time of the call. + * + * @public @memberof render_gfx + */ +XRT_CHECK_RESULT VkResult +render_gfx_layer_cylinder_alloc_and_write(struct render_gfx *rr, + const struct render_gfx_layer_cylinder_data *data, + VkSampler src_sampler, + VkImageView src_image_view, + VkDescriptorSet *out_descriptor_set); + +/*! + * Allocate and write a UBO and descriptor_set to be used for equirect2 layer + * rendering, the content of @p data need to be valid at the time of the call. + * + * @public @memberof render_gfx + */ +XRT_CHECK_RESULT VkResult +render_gfx_layer_equirect2_alloc_and_write(struct render_gfx *rr, + const struct render_gfx_layer_equirect2_data *data, + VkSampler src_sampler, + VkImageView src_image_view, + VkDescriptorSet *out_descriptor_set); + +/*! * Allocate and write a UBO and descriptor_set to be used for projection layer * rendering, the content of @p data need to be valid at the time of the call. * @@ -976,6 +1045,26 @@ VkSampler src_sampler, VkImageView src_image_view, VkDescriptorSet *out_descriptor_set); + +/*! + * Dispatch a cylinder layer shader into the current target and view, + * allocate @p descriptor_set and ubo with + * @ref render_gfx_layer_cylinder_alloc_and_write. + * + * @public @memberof render_gfx + */ +void +render_gfx_layer_cylinder(struct render_gfx *rr, bool premultiplied_alpha, VkDescriptorSet descriptor_set); + +/*! + * Dispatch a equirect2 layer shader into the current target and view, + * allocate @p descriptor_set and ubo with + * @ref render_gfx_layer_equirect2_alloc_and_write. + * + * @public @memberof render_gfx + */ +void +render_gfx_layer_equirect2(struct render_gfx *rr, bool premultiplied_alpha, VkDescriptorSet descriptor_set); /*! * Dispatch a projection layer shader into the current target and view, diff --git a/src/xrt/compositor/render/render_shaders.c b/src/xrt/compositor/render/render_shaders.c --- a/src/xrt/compositor/render/render_shaders.c +++ b/src/xrt/compositor/render/render_shaders.c @@ -36,6 +36,11 @@ #include "shaders/equirect1.vert.h" #include "shaders/equirect2.frag.h" #include "shaders/equirect2.vert.h" +#include "shaders/layer_cylinder.frag.h" +#include "shaders/layer_cylinder.vert.h" +#include "shaders/layer_equirect2.frag.h" +#include "shaders/layer_equirect2.vert.h" +#include "shaders/layer_projection.vert.h" #include "shaders/layer_projection.vert.h" #include "shaders/layer_quad.vert.h" #include "shaders/layer_shared.frag.h" @@ -132,6 +137,10 @@ LOAD(layer_vert); LOAD(layer_frag); + LOAD(layer_cylinder_frag); + LOAD(layer_cylinder_vert); + LOAD(layer_equirect2_frag); + LOAD(layer_equirect2_vert); LOAD(layer_projection_vert); LOAD(layer_quad_vert); LOAD(layer_shared_frag); @@ -161,6 +170,10 @@ D(ShaderModule, s->layer_vert); D(ShaderModule, s->layer_frag); + D(ShaderModule, s->layer_cylinder_frag); + D(ShaderModule, s->layer_cylinder_vert); + D(ShaderModule, s->layer_equirect2_frag); + D(ShaderModule, s->layer_equirect2_vert); D(ShaderModule, s->layer_projection_vert); D(ShaderModule, s->layer_quad_vert); D(ShaderModule, s->layer_shared_frag); diff --git a/src/xrt/compositor/shaders/layer_cylinder.frag b/src/xrt/compositor/shaders/layer_cylinder.frag new file mode 100644 --- /dev/null +++ b/src/xrt/compositor/shaders/layer_cylinder.frag @@ -0,0 +1,17 @@ +// Copyright 2023, Collabora Ltd. +// Author: Jakob Bornecrantz +// SPDX-License-Identifier: BSL-1.0 + +#version 460 + + +layout (binding = 1) uniform sampler2D image; + +layout (location = 0) in vec2 uv; +layout (location = 0) out vec4 out_color; + + +void main () +{ + out_color = texture(image, uv); +} diff --git a/src/xrt/compositor/shaders/layer_cylinder.vert b/src/xrt/compositor/shaders/layer_cylinder.vert new file mode 100644 --- /dev/null +++ b/src/xrt/compositor/shaders/layer_cylinder.vert @@ -0,0 +1,88 @@ +// Copyright 2023, Collabora Ltd. +// Author: Jakob Bornecrantz +// SPDX-License-Identifier: BSL-1.0 + +#version 460 + + +// Number of subdivisions. +layout(constant_id = 0) const uint subdivision_count = 360; + +layout (binding = 0, std140) uniform Config +{ + vec4 post_transform; + mat4 mvp; + float radius; + float central_angle; + float aspect_ratio; + float _pad; +} ubo; + +layout (location = 0) out vec2 out_uv; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + + +vec2 get_uv_for_vertex() +{ + // One edge on either endstop and one between each subdivision. + uint edges = subdivision_count + 1; + + // Goes from [0 .. 2^31], two vertices per edge. + uint x_u32 = bitfieldExtract(uint(gl_VertexIndex), 1, 31); + + // Goes from [0 .. 1] every other vertex. + uint y_u32 = bitfieldExtract(uint(gl_VertexIndex), 0, 1); + + // Starts at zero to get to [0 .. 1], there is two vertices per edge. + float x = float(x_u32) / float(edges); + + // Already in [0 .. 1] just transform to float. + float y = float(y_u32); + + return vec2(x, y); +} + +vec3 get_position_for_uv(vec2 uv) +{ + float radius = ubo.radius; + float angle = ubo.central_angle; + float ratio = ubo.aspect_ratio; + + // [0 .. 1] to [-0.5 .. 0.5] + float mixed_u = uv.x - 0.5; + + // [-0.5 .. 0.5] to [-angle / 2 .. angle / 2]. + float a = mixed_u * angle; + + // [0 .. 1] to [0.5 .. -0.5] notice fliped sign to be in OpenXR space. + float mixed_v = 0.5 - uv.y; + + // This the total height acording to the spec. + float total_height = (angle * radius) / ratio; + + // Calculate the position. + float x = sin(a) * radius; // At angle zero at x = 0. + float y = total_height * mixed_v; + float z = -cos(a) * radius; // At angle zero at z = -1. + + return vec3(x, y, z); +} + +void main() +{ + // We now get a unmodified UV position. + vec2 raw_uv = get_uv_for_vertex(); + + // Get the position for the raw UV. + vec3 position = get_position_for_uv(raw_uv); + + // To deal with OpenGL flip and sub image view. + vec2 uv = fma(raw_uv, ubo.post_transform.zw, ubo.post_transform.xy); + + gl_Position = ubo.mvp * vec4(position, 1.0); + out_uv = uv; +} diff --git a/src/xrt/compositor/shaders/layer_equirect2.frag b/src/xrt/compositor/shaders/layer_equirect2.frag new file mode 100644 --- /dev/null +++ b/src/xrt/compositor/shaders/layer_equirect2.frag @@ -0,0 +1,113 @@ +// Copyright 2023, Collabora Ltd. +// Author: Jakob Bornecrantz +// Author: Charlton Rodda +// Author: Simon Zeni +// SPDX-License-Identifier: BSL-1.0 + +#version 460 + +layout (binding = 0, std140) uniform Config +{ + vec4 post_transform; + mat4 mv_inverse; + vec4 to_tangent; + float radius; + float central_horizontal_angle; + float upper_vertical_angle; + float lower_vertical_angle; +} ubo; + +layout (binding = 1) uniform sampler2D image; + +layout (location = 0) flat in vec3 in_camera_position; +layout (location = 1) in vec3 in_camera_ray_unnormalized; +layout (location = 0) out vec4 out_color; + +const float PI = acos(-1); + + +vec2 sphere_intersect(vec3 ray_origin, vec3 ray_direction, vec3 sphere_center, float radius) +{ + vec3 ray_sphere_diff = ray_origin - sphere_center; + + float B = dot(ray_sphere_diff, ray_direction); + + vec3 QC = ray_sphere_diff - B * ray_direction; + + float H = radius * radius - dot(QC, QC); + + if (H < 0.0) { + return vec2(-1.0); // no intersection + } + + H = sqrt(H); + + return vec2(-B - H, -B + H); +} + +void main () +{ + vec3 ray_origin = in_camera_position; + vec3 ray_dir = normalize(in_camera_ray_unnormalized); + + vec3 dir_from_sph; + // CPU code will set +INFINITY to zero. + if (ubo.radius == 0) { + dir_from_sph = ray_dir; + } else { + vec2 distances = sphere_intersect(ray_origin, ray_dir, vec3(0, 0, 0), ubo.radius); + + if (distances.y < 0) { + out_color = vec4(0.0); + return; + } + + vec3 pos = ray_origin + (ray_dir * distances.y); + dir_from_sph = normalize(pos); + } + + float lon = atan(dir_from_sph.x, -dir_from_sph.z) / (2 * PI) + 0.5; + float lat = acos(dir_from_sph.y) / PI; + +#ifdef DEBUG + int lon_int = int(lon * 1000.0); + int lat_int = int(lat * 1000.0); + + if (lon < 0.001 && lon > -0.001) { + out_color = vec4(1, 0, 0, 1); + } else if (lon_int % 50 == 0) { + out_color = vec4(1, 1, 1, 1); + } else if (lat_int % 50 == 0) { + out_color = vec4(1, 1, 1, 1); + } else { + out_color = vec4(lon, lat, 0, 1); + } +#endif + + float chan = ubo.central_horizontal_angle / (PI * 2.0f); + + // Normalize [0, 2π] to [0, 1] + float uhan = 0.5 + chan / 2.0f; + float lhan = 0.5 - chan / 2.0f; + + // Normalize [-π/2, π/2] to [0, 1] + float uvan = ubo.upper_vertical_angle / PI + 0.5f; + float lvan = ubo.lower_vertical_angle / PI + 0.5f; + + if (lat < uvan && lat > lvan && lon < uhan && lon > lhan) { + // map configured display region to whole texture + vec2 ll_offset = vec2(lhan, lvan); + vec2 ll_extent = vec2(uhan - lhan, uvan - lvan); + vec2 sample_point = (vec2(lon, lat) - ll_offset) / ll_extent; + + vec2 uv_sub = fma(sample_point, ubo.post_transform.zw, ubo.post_transform.xy); + +#ifdef DEBUG + out_color += texture(image, uv_sub) / 2.0; +#else + out_color = texture(image, uv_sub); + } else { + out_color = vec4(0, 0, 0, 0); +#endif + } +} diff --git a/src/xrt/compositor/shaders/layer_equirect2.vert b/src/xrt/compositor/shaders/layer_equirect2.vert new file mode 100644 --- /dev/null +++ b/src/xrt/compositor/shaders/layer_equirect2.vert @@ -0,0 +1,65 @@ +// Copyright 2023, Collabora Ltd. +// Author: Jakob Bornecrantz +// Author: Charlton Rodda +// Author: Simon Zeni +// SPDX-License-Identifier: BSL-1.0 + +#version 460 + +layout (binding = 0, std140) uniform Config +{ + vec4 post_transform; + mat4 mv_inverse; + vec4 to_tangent; + float radius; + float central_horizontal_angle; + float upper_vertical_angle; + float lower_vertical_angle; +} ubo; + +layout (location = 0) flat out vec3 out_camera_position; +layout (location = 1) out vec3 out_camera_ray_unnormalized; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +vec2 pos[4] = { + vec2(0, 0), + vec2(0, 1), + vec2(1, 0), + vec2(1, 1), +}; + +vec3 intersection_with_unit_plane(vec2 uv_0_to_1) +{ + // [0 .. 1] to tangent lengths (at unit Z). + vec2 tangent_factors = fma(uv_0_to_1, ubo.to_tangent.zw, ubo.to_tangent.xy); + + // With Z at the unit plane and flip y for OpenXR coordinate system. + vec3 point_on_unit_plane = vec3(tangent_factors.x, -tangent_factors.y, -1); + + return point_on_unit_plane; +} + +void main() +{ + vec2 uv = pos[gl_VertexIndex % 4]; + + // Get camera position in model space. + out_camera_position = (ubo.mv_inverse * vec4(0, 0, 0, 1)).xyz; + + // Get the point on the Z=-1 plane in view space that this pixel's ray intersects. + vec3 out_camera_ray_in_view_space = intersection_with_unit_plane(uv); + + // Transform into model space. Normalising here doesn't work because + // the values are interpeted linearly in space, where a normal + // doesn't move linearly in space, so do that in the fragment shader. + out_camera_ray_unnormalized = (ubo.mv_inverse * vec4(out_camera_ray_in_view_space, 0)).xyz; + + // Go from [0 .. 1] to [-1 .. 1] to fill whole view in NDC. + vec2 pos = fma(uv, vec2(2.0), vec2(-1.0)); + + gl_Position = vec4(pos, 0.f, 1.f); +} -- tangled.sh