diff --git a/CMakeLists.txt b/CMakeLists.txt index 846ec7fd34c442f9327c9fad577f6c61fddc2cb5..2a4788b92f122add4d6188a4e73ee4724dd77f54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -700,7 +700,7 @@ message("======================================================================= ${SRC_DIR}/strategy/stereo_strategy.hpp ${SRC_DIR}/strategy/stereo_strategy.cpp ${SRC_DIR}/strategy/native_stereo_forward.hpp ${SRC_DIR}/strategy/native_stereo_forward.cpp ${SRC_DIR}/strategy/native_stereo_deferred.hpp ${SRC_DIR}/strategy/native_stereo_deferred.cpp - # ${SRC_DIR}/strategy/multiview_stereo.hpp ${SRC_DIR}/strategy/multiview_stereo.cpp + ${SRC_DIR}/strategy/multi_view_stereo.hpp ${SRC_DIR}/strategy/multi_view_stereo.cpp ${SRC_DIR}/strategy/depth_peeling_reprojection_stereo.hpp ${SRC_DIR}/strategy/depth_peeling_reprojection_stereo.cpp #Stream @@ -734,8 +734,8 @@ message("======================================================================= strategy/native_deferred/native_stereo_deferred.vert strategy/native_deferred/native_stereo_deferred.frag - strategy/multiview/multiview_stereo.vert - strategy/multiview/multiview_stereo.frag + strategy/multi_view/multi_view_stereo.vert + strategy/multi_view/multi_view_stereo.frag strategy/depth_peeling_reprojection/depth_peeling_reprojection_stereo.vert strategy/depth_peeling_reprojection/depth_peeling_reprojection_stereo.geom diff --git a/liblava/block/render_pass.cpp b/liblava/block/render_pass.cpp index c38ad76959e46552e5fff10b7fee8e524b389df7..562654d9c909b84c98efefb3fbce21d7c9aff9b1 100644 --- a/liblava/block/render_pass.cpp +++ b/liblava/block/render_pass.cpp @@ -12,7 +12,7 @@ namespace lava { on_destroyed = [&]() { on_target_destroyed(); }; } - bool render_pass::create(VkAttachmentsRef target_attachments, rect area) { + bool render_pass::create(VkAttachmentsRef target_attachments, rect area, void* next) { std::vector<VkAttachmentDescription> attachment_descriptions; for (auto& attachment : attachments) @@ -30,6 +30,7 @@ namespace lava { VkRenderPassCreateInfo const create_info{ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .pNext = next, .attachmentCount = to_ui32(attachment_descriptions.size()), .pAttachments = attachment_descriptions.data(), .subpassCount = to_ui32(subpass_descriptions.size()), diff --git a/liblava/block/render_pass.hpp b/liblava/block/render_pass.hpp index 2ebbdcadf813d0fa0eaffbf44b9463b28ac68fda..c58cccd13df5a1329d9491187c9f0938df770905 100644 --- a/liblava/block/render_pass.hpp +++ b/liblava/block/render_pass.hpp @@ -16,7 +16,7 @@ namespace lava { explicit render_pass(device_ptr device); - bool create(VkAttachmentsRef target_attachments, rect area); + bool create(VkAttachmentsRef target_attachments, rect area, void* next = nullptr); void destroy(); void process(VkCommandBuffer cmd_buf, index frame); diff --git a/res/dpr/strategy/multi_view/multi_view_stereo.frag b/res/dpr/strategy/multi_view/multi_view_stereo.frag new file mode 100644 index 0000000000000000000000000000000000000000..4b7eae36af1519a9667e17a95944680340a7f7e5 --- /dev/null +++ b/res/dpr/strategy/multi_view/multi_view_stereo.frag @@ -0,0 +1,57 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require +#extension GL_EXT_multiview : require + +#define MATERIAL_DESCRIPTOR_SET 3 +#define LIGHT_DESCRIPTOR_SET 4 +#define SHADOW_DESCRIPTOR_SET 5 + +#include "math_library.glsl" +#include "material_library.glsl" +#include "light_library.glsl" + +#include "per_frame.inc" + +layout(location = 0) out vec4 outFragColor; + +layout(location = 0) in vec3 inPos; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec3 inTangent; +layout(location = 3) in vec2 inUV; + +layout(set = 0, binding = 0) uniform LeftFrame +{ + PerFrameData left_frame; +}; + +layout(set = 1, binding = 0) uniform RightFrame +{ + PerFrameData right_frame; +}; + +void main() +{ + Material material = lookup_material(inUV); + + if (material.opacity < EPSILON) + { + discard; + } + + vec3 eye_position = vec3(0.0); + + if (gl_ViewIndex == 0) //Left Eye + { + eye_position = left_frame.eyePosition; + } + + else if (gl_ViewIndex == 1) //Right Eye + { + eye_position = right_frame.eyePosition; + } + + vec3 normal = lookup_normal(inUV, inNormal, inTangent); + vec3 lighting = apply_local_lighting(eye_position, inPos, normal, material); + + outFragColor = vec4(lighting, 1.0); +} diff --git a/res/dpr/strategy/multi_view/multi_view_stereo.vert b/res/dpr/strategy/multi_view/multi_view_stereo.vert new file mode 100644 index 0000000000000000000000000000000000000000..e7849d5da5fb9e8b39694df7039ccfead112128c --- /dev/null +++ b/res/dpr/strategy/multi_view/multi_view_stereo.vert @@ -0,0 +1,54 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require +#extension GL_EXT_multiview : require + +#include "mesh_data.inc" +#include "per_frame.inc" + +layout(location = 0) out vec3 outPos; +layout(location = 1) out vec3 outNormal; +layout(location = 2) out vec3 outTangent; +layout(location = 3) out vec2 outUV; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +layout(location = 0) in vec3 inPos; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec3 inTangent; +layout(location = 3) in vec2 inUV; + +layout(set = 0, binding = 0) uniform LeftFrame +{ + PerFrameData left_frame; +}; + +layout(set = 1, binding = 0) uniform RightFrame +{ + PerFrameData right_frame; +}; + +layout(set = 2, binding = 0) uniform Mesh +{ + MeshData meshData; +}; + +void main() +{ + outPos = vec3(meshData.localToWorldSpace * vec4(inPos, 1.0)); + outNormal = normalize((meshData.vectorToWorldSpace * vec4(inNormal, 0.0)).xyz); + outTangent = normalize((meshData.vectorToWorldSpace * vec4(inTangent, 0.0)).xyz); + outUV = inUV; + + if (gl_ViewIndex == 0) //Left Eye + { + gl_Position = left_frame.viewProjection * vec4(outPos, 1.0); + } + + else if (gl_ViewIndex == 1) //Right Eye + { + gl_Position = right_frame.viewProjection * vec4(outPos, 1.0); + } +} diff --git a/res/dpr/strategy/multiview/multiview_stereo.frag b/res/dpr/strategy/multiview/multiview_stereo.frag deleted file mode 100644 index 1111d048c3ca4391f8db247a17e7274fe3a454e5..0000000000000000000000000000000000000000 --- a/res/dpr/strategy/multiview/multiview_stereo.frag +++ /dev/null @@ -1,31 +0,0 @@ -#version 450 core -#extension GL_GOOGLE_include_directive : require - -#include "per_frame.inc" - -layout(location = 0) in vec3 inPos; -layout(location = 1) in vec3 inNormal; -layout(location = 2) in vec3 inTangent; -layout(location = 3) in vec2 inUV; - -layout(set = 0, binding = 0) uniform Frame { - PerFrameData frame; -}; - -layout(set = 1, binding = 0) uniform sampler2D samplerDiffuse; -layout(set = 1, binding = 1) uniform sampler2D samplerNormal; - -layout(location = 0) out vec4 outFragColor; - -void main() { - vec3 vertexNormal = normalize(inNormal); - vec3 vertexTangent = normalize(inTangent); - vec3 vertexBitangent = cross(vertexTangent, vertexNormal); - mat3 tangentToWorldSpace = mat3(vertexTangent, vertexBitangent, vertexNormal); - vec3 normalMapNormal = texture(samplerNormal, inUV).xyz * 2.0 - 1.0; - vec3 normal = tangentToWorldSpace * normalMapNormal; - vec4 diffuse = texture(samplerDiffuse, inUV); - float nDotL = dot(inNormal, normalize(vec3(0.1, -1.0, 0.13))); - outFragColor = vec4(max(0.23, nDotL) * diffuse.rgb, 1.0f); - // outFragColor = vec4(inNormal, 1.0); -} diff --git a/res/dpr/strategy/multiview/multiview_stereo.vert b/res/dpr/strategy/multiview/multiview_stereo.vert deleted file mode 100644 index ac6f5f08f6faf9d5058d37f5d5ef8a986d864e9d..0000000000000000000000000000000000000000 --- a/res/dpr/strategy/multiview/multiview_stereo.vert +++ /dev/null @@ -1,30 +0,0 @@ -#version 450 core -#extension GL_GOOGLE_include_directive : require - -#include "per_frame.inc" - -layout(location = 0) in vec3 inPos; -layout(location = 1) in vec3 inNormal; -layout(location = 2) in vec3 inTangent; -layout(location = 3) in vec2 inUV; - -layout(set = 0, binding = 0) uniform Frame { - PerFrameData frame; -}; - -layout(location = 0) out vec3 outPos; -layout(location = 1) out vec3 outNormal; -layout(location = 2) out vec3 outTangent; -layout(location = 3) out vec2 outUV; - -out gl_PerVertex { - vec4 gl_Position; -}; - -void main() { - gl_Position = frame.viewProjection * vec4(inPos.xyz, 1.0); - outPos = inPos; - outNormal = inNormal; - outTangent = inTangent; - outUV = inUV; -} diff --git a/src/strategy/multi_view_stereo.cpp b/src/strategy/multi_view_stereo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73546895f37c7fe466a4ebe9114a650509777911 --- /dev/null +++ b/src/strategy/multi_view_stereo.cpp @@ -0,0 +1,413 @@ +#include "multi_view_stereo.hpp" +#include "vr_application.hpp" + +bool MultiViewStereo::on_setup_instance(lava::frame_config& config) +{ + config.info.req_api_version = lava::api_version::v1_1; + + return true; +} + +bool MultiViewStereo::on_setup_device(lava::device::create_param& parameters) +{ + memset(&this->features, 0, sizeof(this->features)); + this->features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; + this->features.multiview = VK_TRUE; + + parameters.next = &this->features; + + return true; +} + +bool MultiViewStereo::on_create() +{ + this->shadow_cache = make_shadow_cache(); + + if (!this->shadow_cache->create(this->get_scene(), ShadowCacheSettings())) + { + return false; + } + + if (!this->create_pipeline_layout()) + { + return false; + } + + if (!this->create_render_pass()) + { + return false; + } + + if (!this->create_pipeline()) + { + return false; + } + + return true; +} + +void MultiViewStereo::on_destroy() +{ + if (this->shadow_cache != nullptr) + { + this->shadow_cache->destroy(); + } + + if (this->pipeline_layout != nullptr) + { + this->pipeline_layout->destroy(); + } + + if (this->pipeline != nullptr) + { + this->pipeline->destroy(); + } + + if (this->render_pass != nullptr) + { + this->render_pass->destroy(); + } + + if (this->color_buffer != nullptr) + { + this->color_buffer->destroy(); + } + + if (this->depth_buffer != nullptr) + { + this->depth_buffer->destroy(); + } +} + +bool MultiViewStereo::on_interface() +{ + return true; +} + +bool MultiViewStereo::on_update(lava::delta delta_time) +{ + return true; +} + +bool MultiViewStereo::on_render(VkCommandBuffer command_buffer, lava::index frame) +{ + this->get_headset()->submit_metadata(FrameMetadata()); + + this->get_pass_timer()->begin_pass(command_buffer, "shadow"); + this->shadow_cache->compute_shadow(command_buffer, frame); + this->get_pass_timer()->end_pass(command_buffer); + + this->get_pass_timer()->begin_pass(command_buffer, "multi_eye"); + this->render_pass->process(command_buffer, 0); + this->get_pass_timer()->end_pass(command_buffer); + + VkImageMemoryBarrier left_barrier; + left_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + left_barrier.pNext = nullptr; + left_barrier.srcAccessMask = 0; + left_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + left_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + left_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + left_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + left_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + left_barrier.image = this->get_headset()->get_framebuffer(EYE_LEFT)->get(); + left_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + left_barrier.subresourceRange.baseMipLevel = 0; + left_barrier.subresourceRange.levelCount = 1; + left_barrier.subresourceRange.baseArrayLayer = 0; + left_barrier.subresourceRange.layerCount = 1; + + VkImageMemoryBarrier right_barrier; + right_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + right_barrier.pNext = nullptr; + right_barrier.srcAccessMask = 0; + right_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + right_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + right_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + right_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + right_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + right_barrier.image = this->get_headset()->get_framebuffer(EYE_RIGHT)->get(); + right_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + right_barrier.subresourceRange.baseMipLevel = 0; + right_barrier.subresourceRange.levelCount = 1; + right_barrier.subresourceRange.baseArrayLayer = 0; + right_barrier.subresourceRange.layerCount = 1; + + std::array<VkImageMemoryBarrier, 2> barriers = + { + left_barrier, + right_barrier + }; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, barriers.size(), barriers.data()); + + VkImageSubresourceLayers subresource; + subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresource.mipLevel = 0; + subresource.baseArrayLayer = 0; + subresource.layerCount = 1; + + VkOffset3D offset; + offset.x = 0; + offset.y = 0; + offset.z = 0; + + VkExtent3D extent; + extent.width = this->get_headset()->get_resolution().x; + extent.height = this->get_headset()->get_resolution().y; + extent.depth = 1; + + VkImageCopy left_copy; + left_copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + left_copy.srcSubresource.mipLevel = 0; + left_copy.srcSubresource.baseArrayLayer = 0; + left_copy.srcSubresource.layerCount = 1; + left_copy.srcOffset = offset; + left_copy.dstSubresource = subresource; + left_copy.dstOffset = offset; + left_copy.extent = extent; + + VkImageCopy right_copy; + right_copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + right_copy.srcSubresource.mipLevel = 0; + right_copy.srcSubresource.baseArrayLayer = 1; + right_copy.srcSubresource.layerCount = 1; + right_copy.srcOffset = offset; + right_copy.dstSubresource = subresource; + right_copy.dstOffset = offset; + right_copy.extent = extent; + + vkCmdCopyImage(command_buffer, this->color_buffer->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, this->get_headset()->get_framebuffer(EYE_LEFT)->get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &left_copy); + vkCmdCopyImage(command_buffer, this->color_buffer->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, this->get_headset()->get_framebuffer(EYE_RIGHT)->get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &right_copy); + + this->get_frame_capture()->capture_image(command_buffer, this->get_headset()->get_framebuffer(EYE_LEFT), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "left_eye"); + this->get_companion_window()->submit_image(command_buffer, EYE_LEFT, this->get_headset()->get_framebuffer(EYE_LEFT), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + this->get_headset()->submit_frame(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, EYE_LEFT); + + this->get_frame_capture()->capture_image(command_buffer, this->get_headset()->get_framebuffer(EYE_RIGHT), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "right_eye"); + this->get_companion_window()->submit_image(command_buffer, EYE_RIGHT, this->get_headset()->get_framebuffer(EYE_RIGHT), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + this->get_headset()->submit_frame(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, EYE_RIGHT); + + return true; +} + +uint32_t MultiViewStereo::get_max_remote_frame_ids() const +{ + return 2; +} + +const char* MultiViewStereo::get_name() const +{ + return "Multi-View Stereo"; +} + +bool MultiViewStereo::create_pipeline_layout() +{ + this->pipeline_layout = lava::make_pipeline_layout(); + this->pipeline_layout->add(this->get_stereo_transform()->get_descriptor()); + this->pipeline_layout->add(this->get_stereo_transform()->get_descriptor()); + this->pipeline_layout->add(this->get_scene()->get_mesh_descriptor()); + this->pipeline_layout->add(this->get_scene()->get_material_descriptor()); + this->pipeline_layout->add(this->get_scene()->get_light_descriptor()); + this->pipeline_layout->add(this->shadow_cache->get_descriptor()); + + if (!this->pipeline_layout->create(this->get_device())) + { + lava::log()->error("Can't create pipeline layout for multi-view stereo!"); + + return false; + } + + return true; +} + +bool MultiViewStereo::create_render_pass() +{ + this->depth_buffer = lava::make_image(VK_FORMAT_D32_SFLOAT); + this->depth_buffer->set_usage(VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); + this->depth_buffer->set_tiling(VK_IMAGE_TILING_OPTIMAL); + this->depth_buffer->set_view_type(VK_IMAGE_VIEW_TYPE_2D_ARRAY); + this->depth_buffer->set_layer_count(2); + + if (!this->depth_buffer->create(this->get_device(), this->get_headset()->get_resolution())) + { + lava::log()->error("Can't create layered depth buffer for multi-view stereo!"); + + return false; + } + + this->color_buffer = lava::make_image(this->get_headset()->get_format()); + this->color_buffer->set_usage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT); + this->color_buffer->set_tiling(VK_IMAGE_TILING_OPTIMAL); + this->color_buffer->set_view_type(VK_IMAGE_VIEW_TYPE_2D_ARRAY); + this->color_buffer->set_layer_count(2); + + if (!this->color_buffer->create(this->get_device(), this->get_headset()->get_resolution())) + { + lava::log()->error("Can't create layered color buffer for multi-view stereo!"); + + return false; + } + + this->depth_attachment = lava::make_attachment(VK_FORMAT_D32_SFLOAT); + this->depth_attachment->set_op(VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE); + this->depth_attachment->set_stencil_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE); + this->depth_attachment->set_layouts(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + + this->color_attachment = lava::make_attachment(this->get_headset()->get_format()); + this->color_attachment->set_op(VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE); + this->color_attachment->set_stencil_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE); + this->color_attachment->set_layouts(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + VkClearValue depth_clear_value; + depth_clear_value.depthStencil.depth = 1.0f; + depth_clear_value.depthStencil.stencil = 0; + + VkClearValue color_clear_value; + color_clear_value.color.float32[0] = 0.0f; + color_clear_value.color.float32[1] = 0.0f; + color_clear_value.color.float32[2] = 0.0f; + color_clear_value.color.float32[3] = 1.0f; + + lava::subpass::ptr subpass = lava::make_subpass(); + subpass->set_depth_stencil_attachment(0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + subpass->set_color_attachment(1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // wait for previous operations to finish reading before clearing attachments + // we need a memory barrier because this isn't a standard write-after-read hazard + // subpass deps have an implicit attachment layout transition, so the dst access mask must be correct + lava::subpass_dependency::ptr subpass_begin_dependency = lava::make_subpass_dependency(VK_SUBPASS_EXTERNAL, 0); + subpass_begin_dependency->set_stage_mask(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT); + subpass_begin_dependency->set_access_mask(0, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); + + // don't run any transfer operations before we're done writing to attachments + // make attachment writes visible to subsequent reads + lava::subpass_dependency::ptr subpass_end_dependency = lava::make_subpass_dependency(0, VK_SUBPASS_EXTERNAL); + subpass_end_dependency->set_stage_mask(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + subpass_end_dependency->set_access_mask(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); + + this->render_pass = lava::make_render_pass(this->get_device()); + this->render_pass->add(subpass); + this->render_pass->add(subpass_begin_dependency); + this->render_pass->add(subpass_end_dependency); + this->render_pass->add(this->depth_attachment); + this->render_pass->add(this->color_attachment); + this->render_pass->set_clear_values( + { + depth_clear_value, + color_clear_value + }); + + lava::rect framebuffer_area = + { + glm::vec2(0.0f), + this->get_headset()->get_resolution() + }; + + lava::VkImageViews framebuffer_views = + { + this->depth_buffer->get_view(), + this->color_buffer->get_view() + }; + + uint32_t view_mask = 0x03; //NOTE: First and second bit set, meaning that the first and second layer of the framebuffer will be modified. + uint32_t correleation_mask = 0x03; //NOTE: First and second bit set, meaning that left and right eye should be rendered concurrently if possible. + + VkRenderPassMultiviewCreateInfo multi_view_info; + multi_view_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO; + multi_view_info.pNext = nullptr; + multi_view_info.subpassCount = 1; + multi_view_info.pViewMasks = &view_mask; + multi_view_info.dependencyCount = 0; + multi_view_info.pViewOffsets = nullptr; + multi_view_info.correlationMaskCount = 1; + multi_view_info.pCorrelationMasks = &correleation_mask; + + if (!this->render_pass->create({ framebuffer_views }, framebuffer_area, &multi_view_info)) + { + lava::log()->error("Can't create render pass for multi-view stereo!"); + + return false; + } + + return true; +} + +bool MultiViewStereo::create_pipeline() +{ + VkPipelineColorBlendAttachmentState blend_state; + blend_state.blendEnable = VK_FALSE; + blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO; + blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + blend_state.colorBlendOp = VK_BLEND_OP_ADD; + blend_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + blend_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + blend_state.alphaBlendOp = VK_BLEND_OP_ADD; + blend_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + this->pipeline = lava::make_graphics_pipeline(this->get_device()); + this->pipeline->set_layout(this->pipeline_layout); + this->pipeline->set_rasterization_cull_mode(VK_CULL_MODE_BACK_BIT); + this->pipeline->set_depth_test_and_write(true, true); + this->pipeline->set_depth_compare_op(VK_COMPARE_OP_LESS); + this->pipeline->add_color_blend_attachment(blend_state); + + Scene::set_vertex_input(this->pipeline.get()); + + if (!this->pipeline->add_shader(lava::file_data("dpr/binary/multi_view_stereo_vertex.spirv"), VK_SHADER_STAGE_VERTEX_BIT)) + { + lava::log()->error("Can't load vertex shader for multi-view stereo!"); + + return false; + } + + if (!this->pipeline->add_shader(lava::file_data("dpr/binary/multi_view_stereo_fragment.spirv"), VK_SHADER_STAGE_FRAGMENT_BIT)) + { + lava::log()->error("Can't load fragment shader for multi-view stereo!"); + + return false; + } + + this->pipeline->on_process = [this](VkCommandBuffer command_buffer) + { + this->pipeline_function(command_buffer); + }; + + if (!this->pipeline->create(this->render_pass->get())) + { + lava::log()->error("Can't create pipeline for native stereo forward!"); + + return false; + } + + this->render_pass->add_front(this->pipeline); + + return true; +} + +void MultiViewStereo::pipeline_function(VkCommandBuffer command_buffer) +{ + uint32_t frame_index = this->get_application()->get_frame_index(); + + this->pipeline_layout->bind(command_buffer, this->get_stereo_transform()->get_descriptor_set(EYE_LEFT, frame_index), 0); + this->pipeline_layout->bind(command_buffer, this->get_stereo_transform()->get_descriptor_set(EYE_RIGHT, frame_index), 1); + this->pipeline_layout->bind(command_buffer, this->get_scene()->get_light_descriptor_set(frame_index), 4); + this->pipeline_layout->bind(command_buffer, this->shadow_cache->get_descriptor_set(), 5); + + const std::vector<SceneMaterial>& materials = this->get_scene()->get_materials(); + + for (const SceneMesh& mesh : this->get_scene()->get_meshes()) + { + const SceneMaterial& material = materials[mesh.material_index]; + + this->pipeline_layout->bind(command_buffer, material.descriptor_set, 3); + this->pipeline_layout->bind(command_buffer, mesh.descriptor_set[frame_index], 2); + + mesh.mesh->bind_draw(command_buffer); + } +} + +MultiViewStereo::Ptr make_multi_view_stereo() +{ + return std::make_shared<MultiViewStereo>(); +} \ No newline at end of file diff --git a/src/strategy/multi_view_stereo.hpp b/src/strategy/multi_view_stereo.hpp new file mode 100644 index 0000000000000000000000000000000000000000..895a6fb387f1baa0a180969b0b2b5b0d2c4682ca --- /dev/null +++ b/src/strategy/multi_view_stereo.hpp @@ -0,0 +1,51 @@ +#pragma once +#include <memory> +#include <array> + +#include "stereo_strategy.hpp" +#include "utility/shadow_cache.hpp" + +class MultiViewStereo final : public StereoStrategy +{ +public: + typedef std::shared_ptr<MultiViewStereo> Ptr; + +public: + MultiViewStereo() = default; + + bool on_setup_instance(lava::frame_config& config) override; + bool on_setup_device(lava::device::create_param& parameters) override; + + bool on_create() override; + void on_destroy() override; + + bool on_interface() override; + bool on_update(lava::delta delta_time) override; + bool on_render(VkCommandBuffer command_buffer, lava::index frame) override; + + uint32_t get_max_remote_frame_ids() const override; + const char* get_name() const override; + +private: + bool create_pipeline_layout(); + bool create_render_pass(); + bool create_pipeline(); + + void pipeline_function(VkCommandBuffer command_buffer); + +private: + VkPhysicalDeviceMultiviewFeatures features; + + ShadowCache::Ptr shadow_cache; + lava::pipeline_layout::ptr pipeline_layout; + + lava::graphics_pipeline::ptr pipeline; + lava::render_pass::ptr render_pass; + + lava::attachment::ptr depth_attachment; + lava::attachment::ptr color_attachment; + lava::image::ptr color_buffer; + lava::image::ptr depth_buffer; +}; + +MultiViewStereo::Ptr make_multi_view_stereo(); \ No newline at end of file diff --git a/src/strategy/multiview_stereo.cpp b/src/strategy/multiview_stereo.cpp deleted file mode 100644 index 8470eac20102c166cbd83d26406a5da5b6d681e7..0000000000000000000000000000000000000000 --- a/src/strategy/multiview_stereo.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "multiview_stereo.hpp" -#include "vr_app.hpp" - -using namespace lava; - -multiview_stereo::multiview_stereo(vr_app* app) -: stereo_strategy(app) { -} - -multiview_stereo::~multiview_stereo() { - pipeline_layout_->destroy(); -} - -bool multiview_stereo::create() { - pipeline_layout_ = make_pipeline_layout(); - pipeline_layout_->add(app()->per_frame_descriptor()); - pipeline_layout_->add(app()->scene_descriptor()); - if (!pipeline_layout_->create(app()->device())) { - return false; - } - - create_render_pass(app()->right_eye_framebuffer(), app()->right_eye_framebuffer()); - - return true; -} - -void multiview_stereo::render(VkCommandBuffer command_buffer, lava::index frame) { - // begin - auto origin = area.get_origin(); - auto size = area.get_size(); - - VkRenderPassBeginInfo const info{ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .renderPass = vk_render_pass, - .framebuffer = framebuffers[frame], - .renderArea = { { origin.x, origin.y }, { size.x, size.y } }, - .clearValueCount = to_ui32(clear_values.size()), - .pClearValues = clear_values.data(), - }; - - device().call().vkCmdBeginRenderPass(command_buffer, &info, VK_SUBPASS_CONTENTS_INLINE); - - // process - ui32 count = 0; - - for (auto& subpass : subpasses) { - if (count > 0) - device().call().vkCmdNextSubpass(command_buffer, VK_SUBPASS_CONTENTS_INLINE); - - if (!subpass->activated()) - continue; - - subpass->process(command_buffer, area.get_size()); - - ++count; - } - - // end - device().call().vkCmdEndRenderPass(command_buffer); -} - -bool multiview_stereo::create_render_pass(const stereo_framebuffer::ptr& left_eye_framebuffer, const stereo_framebuffer::ptr& right_eye_framebuffer) { - for (auto& attachment : attachments) { - attachment_descriptions.push_back(attachment->get_description()); - } - - std::vector<VkSubpassDescription> subpass_descriptions; - - for (auto& subpass : subpasses) - subpass_descriptions.push_back(subpass->get_description()); - - std::vector<VkSubpassDependency> subpass_dependencies; - - for (auto& dependency : dependencies) - subpass_dependencies.push_back(dependency->get_dependency()); - - VkRenderPassCreateInfo const create_info{ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - .attachmentCount = to_ui32(attachment_descriptions.size()), - .pAttachments = attachment_descriptions.data(), - .subpassCount = to_ui32(subpass_descriptions.size()), - .pSubpasses = subpass_descriptions.data(), - .dependencyCount = to_ui32(subpass_dependencies.size()), - .pDependencies = subpass_dependencies.data(), - }; - - if (!check(device->call().vkCreateRenderPass(device->get(), &create_info, memory::alloc(), &vk_render_pass))) { - log()->error("create render pass"); - return false; - } - - return true; -} diff --git a/src/strategy/multiview_stereo.hpp b/src/strategy/multiview_stereo.hpp deleted file mode 100644 index fe6abb16732003b293d386ff7db159eeac321677..0000000000000000000000000000000000000000 --- a/src/strategy/multiview_stereo.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "stereo_strategy.hpp" - -class multiview_stereo final : public stereo_strategy { -public: - multiview_stereo(vr_app* app); - ~multiview_stereo(); - - bool create(); - virtual void render(VkCommandBuffer command_buffer, lava::index frame) override; - -private: - lava::pipeline_layout::ptr pipeline_layout_; - VkRenderPass vk_render_pass = VK_NULL_HANDLE; - lava::attachment::list attachments; - lava::subpass_dependency::list dependencies; - lava::subpass::list subpasses; - lava::rect area; - - bool create_render_pass(const stereo_framebuffer::ptr& left_eye_framebuffer, const stereo_framebuffer::ptr& right_eye_framebuffer); -}; \ No newline at end of file diff --git a/src/strategy/stereo_strategy.cpp b/src/strategy/stereo_strategy.cpp index ce41253737f53b05660e4a8e984406313a7fa4e8..eb359580ddcbc0d0938a68a82b28c4acaeabfb79 100644 --- a/src/strategy/stereo_strategy.cpp +++ b/src/strategy/stereo_strategy.cpp @@ -1,6 +1,7 @@ #include "stereo_strategy.hpp" #include "native_stereo_forward.hpp" #include "native_stereo_deferred.hpp" +#include "multi_view_stereo.hpp" #include "depth_peeling_reprojection_stereo.hpp" #include "vr_application.hpp" @@ -72,6 +73,9 @@ StereoStrategy::Ptr make_stereo_strategy(VRApplication* application, StereoStrat case STEREO_STRATEGY_TYPE_NATIVE_DEFERRED: strategy = make_native_stereo_deferred(); break; + case STEREO_STRATEGY_TYPE_MULTI_VIEW: + strategy = make_multi_view_stereo(); + break; case STEREO_STRATEGY_TYPE_DEPTH_PEELING_REPROJECTION: strategy = make_depth_peeling_reprojection_stereo(); break; @@ -90,6 +94,7 @@ std::vector<StereoStrategy::Ptr> make_all_stereo_strategies(VRApplication* appli std::vector<StereoStrategy::Ptr> strategies; strategies.push_back(make_native_stereo_forward()); strategies.push_back(make_native_stereo_deferred()); + strategies.push_back(make_multi_view_stereo()); strategies.push_back(make_depth_peeling_reprojection_stereo()); for (StereoStrategy::Ptr strategy : strategies) diff --git a/src/strategy/stereo_strategy.hpp b/src/strategy/stereo_strategy.hpp index 28eb54f6e78de9694085f75919beaa833dcb3142..2f565c26afde0fbaceb64fd53a3c66639f6ad988 100644 --- a/src/strategy/stereo_strategy.hpp +++ b/src/strategy/stereo_strategy.hpp @@ -29,6 +29,7 @@ enum StereoStrategyType { STEREO_STRATEGY_TYPE_NATIVE_FORWARD, STEREO_STRATEGY_TYPE_NATIVE_DEFERRED, + STEREO_STRATEGY_TYPE_MULTI_VIEW, STEREO_STRATEGY_TYPE_DEPTH_PEELING_REPROJECTION }; diff --git a/src/utility/command_parser.cpp b/src/utility/command_parser.cpp index dfa2d4f309c881f77b3f6fe8b5ea643bafd01098..44c1294364a09becdde57e29591fb5db2445a2a3 100644 --- a/src/utility/command_parser.cpp +++ b/src/utility/command_parser.cpp @@ -263,7 +263,7 @@ void CommandParser::show_help() std::cout << " --headset={headset} The headset that should be used." << std::endl; std::cout << " Options: emulated (default), openvr, openxr, remote" << std::endl; std::cout << " --stereo-strategy={strategy} The stereo strategy that should be used." << std::endl; - std::cout << " Options: native-forward (default), native-deferred, dpr" << std::endl; + std::cout << " Options: native-forward (default), native-deferred, multi-view, dpr" << std::endl; std::cout << " --animation={animation_name} The name of the animation that should be played." << std::endl; std::cout << " This parameter is only allowed during a benchmark." << std::endl; std::cout << " --animation-index={animation_index} The index of the animation that should be played." << std::endl; @@ -325,6 +325,11 @@ bool CommandParser::set_stero_strategy(const std::string& name) this->stereo_strategy = STEREO_STRATEGY_TYPE_NATIVE_FORWARD; } + else if (name == "multi-view") + { + this->stereo_strategy = STEREO_STRATEGY_TYPE_MULTI_VIEW; + } + else if (name == "dpr") { this->stereo_strategy = STEREO_STRATEGY_TYPE_DEPTH_PEELING_REPROJECTION;