blob: 7628af68860ee6652e7a96f9e563b2c0eb786d5d [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/public/lib/escher/hmd/pose_buffer_latching_shader.h"
#include "garnet/public/lib/escher/escher.h"
#include "garnet/public/lib/escher/hmd/pose_buffer.h"
#include "garnet/public/lib/escher/renderer/frame.h"
#include "garnet/public/lib/escher/resources/resource_recycler.h"
#include "garnet/public/lib/escher/scene/camera.h"
#include "garnet/public/lib/escher/vk/buffer.h"
#include "garnet/public/lib/escher/vk/gpu_allocator.h"
#include "garnet/public/lib/escher/vk/texture.h"
namespace escher {
namespace hmd {
namespace {
constexpr char g_kernel_src[] = R"GLSL(
#version 450
#extension GL_ARB_separate_shader_objects : enable
struct Pose {
vec4 quaternion;
vec3 position;
uint reserved;
};
layout(push_constant) uniform PushConstants {
uint latch_index;
};
layout (binding = 0) uniform VPMatrices {
mat4 left_view_transform;
mat4 left_projection_matrix;
mat4 right_view_transform;
mat4 right_projection_matrix;
};
layout (binding = 1) buffer PoseBuffer {
Pose poses[];
};
layout (binding = 2) buffer OutputBuffer {
Pose latched_pose;
mat4 left_vp_matrix;
mat4 right_vp_matrix;
};
// interpreted from GLM's mat3_cast
mat3 quaternion_to_mat3(vec4 q)
{
mat3 result;
float qxx = q.x * q.x;
float qyy = q.y * q.y;
float qzz = q.z * q.z;
float qxz = q.x * q.z;
float qxy = q.x * q.y;
float qyz = q.y * q.z;
float qwx = q.w * q.x;
float qwy = q.w * q.y;
float qwz = q.w * q.z;
result[0][0] = float(1) - float(2) * (qyy + qzz);
result[0][1] = float(2) * (qxy + qwz);
result[0][2] = float(2) * (qxz - qwy);
result[1][0] = float(2) * (qxy - qwz);
result[1][1] = float(1) - float(2) * (qxx + qzz);
result[1][2] = float(2) * (qyz + qwx);
result[2][0] = float(2) * (qxz + qwy);
result[2][1] = float(2) * (qyz - qwx);
result[2][2] = float(1) - float(2) * (qxx + qyy);
return result;
}
mat4 translate(vec3 t){
return mat4(
vec4(1.0, 0.0, 0.0, 0.0),
vec4(0.0, 1.0, 0.0, 0.0),
vec4(0.0, 0.0, 1.0, 0.0),
vec4(t.x, t.y, t.z, 1.0)
);
}
void main() {
latched_pose = poses[latch_index];
left_vp_matrix = left_projection_matrix *
mat4(quaternion_to_mat3(latched_pose.quaternion)) *
translate(latched_pose.position) * left_view_transform;
right_vp_matrix = right_projection_matrix *
mat4(quaternion_to_mat3(latched_pose.quaternion)) *
translate(latched_pose.position) * right_view_transform;
}
)GLSL";
}
static constexpr size_t k4x4MatrixSize = 16 * sizeof(float);
PoseBufferLatchingShader::PoseBufferLatchingShader(EscherWeakPtr escher)
: escher_(std::move(escher)) {}
BufferPtr PoseBufferLatchingShader::LatchPose(const FramePtr& frame,
const Camera& camera,
PoseBuffer pose_buffer,
int64_t latch_time,
bool host_accessible_output) {
return LatchStereoPose(frame, camera, camera, pose_buffer, latch_time,
host_accessible_output);
}
BufferPtr PoseBufferLatchingShader::LatchStereoPose(
const FramePtr& frame, const Camera& left_camera,
const Camera& right_camera, PoseBuffer pose_buffer, int64_t latch_time,
bool host_accessible_output) {
vk::DeviceSize buffer_size = 2 * k4x4MatrixSize + sizeof(Pose);
const vk::MemoryPropertyFlags kOutputMemoryPropertyFlags =
host_accessible_output ? (vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent)
: (vk::MemoryPropertyFlagBits::eDeviceLocal);
const vk::BufferUsageFlags kOutputBufferUsageFlags =
vk::BufferUsageFlagBits::eUniformBuffer |
vk::BufferUsageFlagBits::eStorageBuffer;
auto output_buffer = frame->gpu_allocator()->AllocateBuffer(
escher_->resource_recycler(), buffer_size, kOutputBufferUsageFlags,
kOutputMemoryPropertyFlags);
const vk::MemoryPropertyFlags kVpMemoryPropertyFlags =
vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent;
const vk::BufferUsageFlags kVpBufferUsageFlags =
vk::BufferUsageFlagBits::eUniformBuffer;
auto vp_matrices_buffer = frame->gpu_allocator()->AllocateBuffer(
escher_->resource_recycler(), 4 * k4x4MatrixSize, kVpBufferUsageFlags,
kVpMemoryPropertyFlags);
auto command_buffer = frame->command_buffer();
// This should be guaranteed by checks at a higher layer. For example,
// Scenic checks this in Session::ApplySetCamerPoseBufferCmd().
FXL_DCHECK(latch_time >= pose_buffer.base_time);
uint32_t latch_index =
((latch_time - pose_buffer.base_time) / pose_buffer.time_interval) %
pose_buffer.num_entries;
FXL_DCHECK(vp_matrices_buffer->host_ptr() != nullptr);
glm::mat4* vp_matrices =
reinterpret_cast<glm::mat4*>(vp_matrices_buffer->host_ptr());
vp_matrices[0] = left_camera.transform();
vp_matrices[1] = left_camera.projection();
vp_matrices[2] = right_camera.transform();
vp_matrices[3] = right_camera.projection();
if (!kernel_) {
kernel_ = std::make_unique<impl::ComputeShader>(
escher_, std::vector<vk::ImageLayout>{},
std::vector<vk::DescriptorType>{vk::DescriptorType::eUniformBuffer,
vk::DescriptorType::eStorageBuffer,
vk::DescriptorType::eStorageBuffer},
sizeof(latch_index), g_kernel_src);
}
std::vector<TexturePtr> textures;
std::vector<BufferPtr> buffers;
buffers.push_back(vp_matrices_buffer);
buffers.push_back(pose_buffer.buffer);
buffers.push_back(output_buffer);
kernel_->Dispatch(textures, buffers, command_buffer, 1, 1, 1, &latch_index);
return output_buffer;
}
} // namespace hmd
} // namespace escher