c/render: Add cylinder and equirect2 shaders

Co-authored-by: Simon Zeni <simon.zeni@collabora.com>
Co-authored-by: Charlton Rodda <charlton.rodda@collabora.com>
This commit is contained in:
Jakob Bornecrantz 2023-10-12 18:05:37 +01:00
parent 505a54bf8f
commit 3b174b0dab
8 changed files with 525 additions and 0 deletions

View file

@ -95,6 +95,10 @@ if(XRT_HAVE_VULKAN)
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

View file

@ -755,6 +755,53 @@ render_gfx_render_pass_init(struct render_gfx_render_pass *rgrp,
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 @@ render_gfx_render_pass_init(struct render_gfx_render_pass *rgrp,
&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 @@ render_gfx_render_pass_close(struct render_gfx_render_pass *rgrp)
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);
@ -1163,6 +1215,50 @@ render_gfx_mesh_draw(struct render_gfx *rr, uint32_t mesh_index, VkDescriptorSet
*
*/
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,
@ -1207,6 +1303,46 @@ render_gfx_layer_quad_alloc_and_write(struct render_gfx *rr,
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
render_gfx_layer_projection(struct render_gfx *rr, bool premultiplied_alpha, VkDescriptorSet descriptor_set)
{

View file

@ -155,6 +155,12 @@ struct render_shaders
* 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 render_gfx_render_pass
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;
@ -872,6 +885,36 @@ struct render_gfx_mesh_ubo_data
struct xrt_matrix_4x4 transform;
};
/*!
* 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.
*/
@ -951,6 +994,32 @@ render_gfx_mesh_alloc_and_write(struct render_gfx *rr,
void
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.
@ -977,6 +1046,26 @@ render_gfx_layer_quad_alloc_and_write(struct render_gfx *rr,
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,
* allocate @p descriptor_set and ubo with

View file

@ -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 @@ render_shaders_load(struct render_shaders *s, struct vk_bundle *vk)
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 @@ render_shaders_close(struct render_shaders *s, struct vk_bundle *vk)
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);

View file

@ -0,0 +1,17 @@
// Copyright 2023, Collabora Ltd.
// Author: Jakob Bornecrantz <jakob@collabora.com>
// 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);
}

View file

@ -0,0 +1,88 @@
// Copyright 2023, Collabora Ltd.
// Author: Jakob Bornecrantz <jakob@collabora.com>
// 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;
}

View file

@ -0,0 +1,113 @@
// Copyright 2023, Collabora Ltd.
// Author: Jakob Bornecrantz <jakob@collabora.com>
// Author: Charlton Rodda <charlton.rodda@collabora.com>
// Author: Simon Zeni <simon.zeni@collabora.com>
// 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
}
}

View file

@ -0,0 +1,65 @@
// Copyright 2023, Collabora Ltd.
// Author: Jakob Bornecrantz <jakob@collabora.com>
// Author: Charlton Rodda <charlton.rodda@collabora.com>
// Author: Simon Zeni <simon.zeni@collabora.com>
// 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);
}