diff --git a/CMakeLists.txt b/CMakeLists.txt index f4bda62d6a924d510b445e29987a4db91e9703b9..f49269a8c2825e1736005e0c25df8ec93ddb0cf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -717,7 +717,8 @@ message("======================================================================= ${SRC_DIR}/utility/extern_fence.hpp ${SRC_DIR}/utility/extern_fence.cpp ${SRC_DIR}/utility/frame_capture.hpp ${SRC_DIR}/utility/frame_capture.cpp ${SRC_DIR}/utility/geometry_buffer.hpp ${SRC_DIR}/utility/geometry_buffer.cpp - ${SRC_DIR}/utility/latency.hpp ${SRC_DIR}/utility/latency.cpp + ${SRC_DIR}/utility/indirect_cache.hpp ${SRC_DIR}/utility/indirect_cache.cpp + ${SRC_DIR}/utility/latency.hpp ${SRC_DIR}/utility/latency.cpp ${SRC_DIR}/utility/pass_timer.hpp ${SRC_DIR}/utility/pass_timer.cpp ${SRC_DIR}/utility/shadow_cache.hpp ${SRC_DIR}/utility/shadow_cache.cpp ${SRC_DIR}/utility/statistic.hpp ${SRC_DIR}/utility/statistic.cpp @@ -752,9 +753,24 @@ message("======================================================================= strategy/depth_peeling_reprojection/dpr_reproject.tese strategy/depth_peeling_reprojection/dpr_reproject.frag - utility/deferred_local_light.vert - utility/deferred_local_light.frag - utility/deferred_local_light_with_shadow.frag + utility/deferred_lighting.vert + utility/deferred_lighting.frag + utility/deferred_lighting_shadow.frag + utility/deferred_lighting_indirect.frag + utility/deferred_lighting_indirect_shadow.frag + + utility/indirect_capture.vert + utility/indirect_capture.frag + + utility/indirect_geometry.vert + utility/indirect_geometry.geom + utility/indirect_geometry.frag + + utility/indirect_injection.vert + utility/indirect_injection.geom + utility/indirect_injection.frag + + utility/indirect_propagation.comp utility/shadow.vert utility/shadow.geom diff --git a/cmake/Shaders.cmake b/cmake/Shaders.cmake index 06d9d111495a6aa23f0b7398698641bea9bdac6b..f7a56645bdcc65b71c7ecc1574a286094e9dc33b 100644 --- a/cmake/Shaders.cmake +++ b/cmake/Shaders.cmake @@ -16,6 +16,8 @@ function(shader_extension_to_name name extension) set(${name} "geometry" PARENT_SCOPE) elseif (extension STREQUAL ".frag") set(${name} "fragment" PARENT_SCOPE) + elseif (extension STREQUAL ".comp") + set(${name} "compute" PARENT_SCOPE) else () message(SEND_ERROR "Invalid extension: ${extension}") endif () diff --git a/liblava/block/pipeline.hpp b/liblava/block/pipeline.hpp index 49e9713f0596a2ea76d5660e4b1bad249ca7d846..a3b8fa1435b3bfd102227fee6398aaf214d96323 100644 --- a/liblava/block/pipeline.hpp +++ b/liblava/block/pipeline.hpp @@ -284,6 +284,9 @@ namespace lava { void set_viewport(VkViewport value) { viewport = value; } + void set_viewport_count(uint32_t count){ + info.viewport_state.viewportCount = count; + } VkRect2D get_scissor() const { return scissor; @@ -291,6 +294,9 @@ namespace lava { void set_scissor(VkRect2D value) { scissor = value; } + void set_scissor_count(uint32_t count) { + info.viewport_state.scissorCount = count; + } size_type get_size_type() const { return size_type; diff --git a/liblava/resource/image.hpp b/liblava/resource/image.hpp index 98308ff264ab3dbe3639690b5e350fd3944b0550..b24e850e0a77bd642445d3e2db3e3b2c10dcb519 100644 --- a/liblava/resource/image.hpp +++ b/liblava/resource/image.hpp @@ -51,6 +51,14 @@ namespace lava { VkImageSubresourceRange const& get_subresource_range() const { return subresource_range; } + VkImageSubresourceLayers get_subresource_layers(ui32 levelOffset = 0) const { + return VkImageSubresourceLayers{ + .aspectMask = subresource_range.aspectMask, + .mipLevel = subresource_range.baseMipLevel + levelOffset, + .baseArrayLayer = subresource_range.baseArrayLayer, + .layerCount = subresource_range.layerCount + }; + } void set_flags(VkImageCreateFlagBits flags) { info.flags = flags; @@ -78,6 +86,13 @@ namespace lava { info.arrayLayers = layers; } + void set_base_mip_level(ui32 baseMipLevel) { + subresource_range.baseMipLevel = baseMipLevel; + } + void set_base_array_layer(ui32 baseArrayLayer) { + subresource_range.baseArrayLayer = baseArrayLayer; + } + void set_component(VkComponentMapping mapping = {}) { view_info.components = mapping; } diff --git a/res/dpr/data/indirect_data.inc b/res/dpr/data/indirect_data.inc new file mode 100644 index 0000000000000000000000000000000000000000..0b774ae44f087fbdc3eee3068d4445662bac2123 --- /dev/null +++ b/res/dpr/data/indirect_data.inc @@ -0,0 +1,19 @@ +#ifndef SHADER_INCLUDE_INDIRECT_DATA +#define SHADER_INCLUDE_INDIRECT_DATA + +struct IndirectDomain +{ + uvec3 indirect_resolution; + float cell_size; + + uvec3 geometry_resolution; + uint padding1; + + vec3 min; + uint padding2; + + vec3 max; + uint padding3; +}; + +#endif \ No newline at end of file diff --git a/res/dpr/data/light_data.inc b/res/dpr/data/light_data.inc index ee7aa41bd435481d03c6788f0e6f3beeebb0ad78..30b641e9c267413a0b3e00df2c4e89aa72356054 100644 --- a/res/dpr/data/light_data.inc +++ b/res/dpr/data/light_data.inc @@ -5,14 +5,14 @@ #define LIGHT_TYPE_DIRECTIONAL 1 #define LIGHT_TYPE_SPOT 2 -#define LIGHT_MAX_SHADOW_MATRIX_COUNT 6 //At least 6 for point lights +#define MAX_LIGHT_SHADOW_MATRIX_COUNT 6 //At least 6 for point lights struct LightParameter { vec3 ambient; //where ambient has the unit watt/meter^2 * str - uint count; - float exposure; + + uint light_count; }; struct LightData @@ -26,10 +26,12 @@ struct LightData vec3 direction; float inner_angle; - vec3 padding; + vec2 near_size; uint type_index; + uint padding; - mat4 shadow_matrix[LIGHT_MAX_SHADOW_MATRIX_COUNT]; + mat4 shadow_matrix[MAX_LIGHT_SHADOW_MATRIX_COUNT]; + mat4 inv_shadow_matrix[MAX_LIGHT_SHADOW_MATRIX_COUNT]; }; #endif \ No newline at end of file diff --git a/res/dpr/data/material_data.inc b/res/dpr/data/material_data.inc new file mode 100644 index 0000000000000000000000000000000000000000..7a952c297fed86347710e17e02d096a2011700d6 --- /dev/null +++ b/res/dpr/data/material_data.inc @@ -0,0 +1,22 @@ +#ifndef SHADER_INCLUDE_MATERIAL_DATA +#define SHADER_INCLUDE_MATERIAL_DATA + +//NOTE: Struct needs to be multiple of 256 +struct MaterialData +{ + vec3 base_color; + float opacity; + + vec3 emissive; + float roughness; + + uvec3 padding1; + float metallic; + + uvec4 padding2; + mat4 padding3; + mat4 padding4; + mat4 padding5; +}; + +#endif \ No newline at end of file diff --git a/res/dpr/data/mesh_data.inc b/res/dpr/data/mesh_data.inc index 7ae6b1952676a224856ab1004c50410305510643..d45aa5728a9d28106d45a84e9f8064fa3ac1e899 100644 --- a/res/dpr/data/mesh_data.inc +++ b/res/dpr/data/mesh_data.inc @@ -1,6 +1,13 @@ -struct MeshData { +#ifndef SHADER_INCLUDE_MESH_DATA +#define SHADER_INCLUDE_MESH_DATA + +//NOTE: Struct needs to be multiple of 256 +struct MeshData +{ mat4 localToWorldSpace; mat4 vectorToWorldSpace; mat4 padding1; mat4 padding2; }; + +#endif \ No newline at end of file diff --git a/res/dpr/data/per_frame.inc b/res/dpr/data/per_frame.inc index 4aef982cbcbc7e1b8949cf57d411eebd265ed69b..345f2fca07221b30072ebd81520372e7d190f939 100644 --- a/res/dpr/data/per_frame.inc +++ b/res/dpr/data/per_frame.inc @@ -1,3 +1,6 @@ +#ifndef SHADER_INCLUDE_PER_FRAME_DATA +#define SHADER_INCLUDE_PER_FRAME_DATA + struct PerFrameData { mat4 projection; @@ -7,4 +10,6 @@ struct PerFrameData mat4 invView; mat4 invProjection; vec3 eyePosition; -}; \ No newline at end of file +}; + +#endif \ No newline at end of file diff --git a/res/dpr/strategy/multi_view/multi_view_stereo.frag b/res/dpr/strategy/multi_view/multi_view_stereo.frag index 4b7eae36af1519a9667e17a95944680340a7f7e5..9dd9da3b03a32adb155e1f29ab714d25e745c76a 100644 --- a/res/dpr/strategy/multi_view/multi_view_stereo.frag +++ b/res/dpr/strategy/multi_view/multi_view_stereo.frag @@ -5,6 +5,7 @@ #define MATERIAL_DESCRIPTOR_SET 3 #define LIGHT_DESCRIPTOR_SET 4 #define SHADOW_DESCRIPTOR_SET 5 +#define INDIRECT_DESCRIPTOR_SET 6 #include "math_library.glsl" #include "material_library.glsl" @@ -51,7 +52,7 @@ void main() } vec3 normal = lookup_normal(inUV, inNormal, inTangent); - vec3 lighting = apply_local_lighting(eye_position, inPos, normal, material); + vec3 lighting = apply_lighting(eye_position, inPos, normal, material); outFragColor = vec4(lighting, 1.0); } diff --git a/res/dpr/strategy/native_forward/native_stereo_forward.frag b/res/dpr/strategy/native_forward/native_stereo_forward.frag index b3434468e7c446144fa28865eae318e154005e93..1245f0d2f37929b9f5255c0f09cc1c3772fc7e4b 100644 --- a/res/dpr/strategy/native_forward/native_stereo_forward.frag +++ b/res/dpr/strategy/native_forward/native_stereo_forward.frag @@ -4,6 +4,7 @@ #define MATERIAL_DESCRIPTOR_SET 1 #define LIGHT_DESCRIPTOR_SET 3 #define SHADOW_DESCRIPTOR_SET 4 +#define INDIRECT_DESCRIPTOR_SET 5 #include "math_library.glsl" #include "material_library.glsl" @@ -33,7 +34,7 @@ void main() } vec3 normal = lookup_normal(inUV, inNormal, inTangent); - vec3 lighting = apply_local_lighting(frame.eyePosition, inPos, normal, material); + vec3 lighting = apply_lighting(frame.eyePosition, inPos, normal, material); outFragColor = vec4(lighting, 1.0); } diff --git a/res/dpr/utility/deferred_lighting.frag b/res/dpr/utility/deferred_lighting.frag new file mode 100644 index 0000000000000000000000000000000000000000..c571998b2e6ba1f8f178d4fb29b14154bdf70506 --- /dev/null +++ b/res/dpr/utility/deferred_lighting.frag @@ -0,0 +1,6 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#define LIGHT_DESCRIPTOR_SET 1 + +#include "deferred_lighting_base.glsl" \ No newline at end of file diff --git a/res/dpr/utility/deferred_local_light.vert b/res/dpr/utility/deferred_lighting.vert similarity index 100% rename from res/dpr/utility/deferred_local_light.vert rename to res/dpr/utility/deferred_lighting.vert diff --git a/res/dpr/utility/deferred_local_light.frag b/res/dpr/utility/deferred_lighting_base.glsl similarity index 87% rename from res/dpr/utility/deferred_local_light.frag rename to res/dpr/utility/deferred_lighting_base.glsl index 9178686076a0725bc560ccc6dbcc836a62dfa6b1..ac28b69110b89fa1e1d03dc256db67946d306bdb 100644 --- a/res/dpr/utility/deferred_local_light.frag +++ b/res/dpr/utility/deferred_lighting_base.glsl @@ -1,8 +1,3 @@ -#version 450 core -#extension GL_GOOGLE_include_directive : require - -#define LIGHT_DESCRIPTOR_SET 1 - #include "math_library.glsl" #include "light_library.glsl" #include "per_frame.inc" @@ -41,6 +36,6 @@ void main() material.emissive.xy = unpackHalf2x16(packUnorm2x16(buffer_normal.zw)); material.emissive.z = unpackHalf2x16(packUnorm4x8(buffer_material.zwzw)).x; - vec3 lighting = apply_local_lighting(per_frame.eyePosition, surface_position.xyz, surface_normal, material); + vec3 lighting = apply_lighting(per_frame.eyePosition, surface_position.xyz, surface_normal, material); outColor = vec4(lighting, 1.0); } \ No newline at end of file diff --git a/res/dpr/utility/deferred_lighting_indirect.frag b/res/dpr/utility/deferred_lighting_indirect.frag new file mode 100644 index 0000000000000000000000000000000000000000..5b03f084e2fbaf51f8ebdca640ad0746c90e2a18 --- /dev/null +++ b/res/dpr/utility/deferred_lighting_indirect.frag @@ -0,0 +1,7 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#define LIGHT_DESCRIPTOR_SET 1 +#define INDIRECT_DESCRIPTOR_SET 3 + +#include "deferred_lighting_base.glsl" \ No newline at end of file diff --git a/res/dpr/utility/deferred_lighting_indirect_shadow.frag b/res/dpr/utility/deferred_lighting_indirect_shadow.frag new file mode 100644 index 0000000000000000000000000000000000000000..a8a28ae3d8b62591c7957b0b5a14c5fd387e7638 --- /dev/null +++ b/res/dpr/utility/deferred_lighting_indirect_shadow.frag @@ -0,0 +1,8 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#define LIGHT_DESCRIPTOR_SET 1 +#define SHADOW_DESCRIPTOR_SET 3 +#define INDIRECT_DESCRIPTOR_SET 4 + +#include "deferred_lighting_base.glsl" \ No newline at end of file diff --git a/res/dpr/utility/deferred_lighting_shadow.frag b/res/dpr/utility/deferred_lighting_shadow.frag new file mode 100644 index 0000000000000000000000000000000000000000..f61a91b62072e01a0c439e86a46ba381023d9609 --- /dev/null +++ b/res/dpr/utility/deferred_lighting_shadow.frag @@ -0,0 +1,7 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#define LIGHT_DESCRIPTOR_SET 1 +#define SHADOW_DESCRIPTOR_SET 3 + +#include "deferred_lighting_base.glsl" \ No newline at end of file diff --git a/res/dpr/utility/deferred_local_light_with_shadow.frag b/res/dpr/utility/deferred_local_light_with_shadow.frag deleted file mode 100644 index daf4506237dbf804c40a4a7c30b84de1df1b1dc4..0000000000000000000000000000000000000000 --- a/res/dpr/utility/deferred_local_light_with_shadow.frag +++ /dev/null @@ -1,47 +0,0 @@ -#version 450 core -#extension GL_GOOGLE_include_directive : require - -#define LIGHT_DESCRIPTOR_SET 1 -#define SHADOW_DESCRIPTOR_SET 3 - -#include "math_library.glsl" -#include "light_library.glsl" -#include "per_frame.inc" - -layout(location = 0) out vec4 outColor; -layout(location = 0) in vec2 inCoord; - -layout(set = 0, binding = 0) uniform sampler2D samplerDepthBuffer; -layout(set = 0, binding = 1) uniform sampler2D samplerColorBuffer; -layout(set = 0, binding = 2) uniform sampler2D samplerNormalBuffer; -layout(set = 0, binding = 3) uniform sampler2D samplerMaterialBuffer; - -layout(set = 2, binding = 0) uniform PerFrameBuffer -{ - PerFrameData per_frame; -}; - -void main() -{ - float buffer_depth = texture(samplerDepthBuffer, inCoord).x; - vec4 buffer_color = texture(samplerColorBuffer, inCoord); - vec4 buffer_normal = texture(samplerNormalBuffer, inCoord); - vec4 buffer_material = texture(samplerMaterialBuffer, inCoord); - - vec4 surface_position = per_frame.invProjection * (vec4(inCoord * 2.0 - 1.0, buffer_depth, 1.0)); - surface_position /= surface_position.w; - surface_position = per_frame.invView * surface_position; - - vec3 surface_normal = decode_normal(buffer_normal.xy); - - Material material; - material.base_color = buffer_color.xyz; - material.occlusion = buffer_color.w; - material.roughness = buffer_material.x; - material.metallic = buffer_material.y; - material.emissive.xy = unpackHalf2x16(packUnorm2x16(buffer_normal.zw)); - material.emissive.z = unpackHalf2x16(packUnorm4x8(buffer_material.zwzw)).x; - - vec3 lighting = apply_local_lighting(per_frame.eyePosition, surface_position.xyz, surface_normal, material); - outColor = vec4(lighting, 1.0); -} \ No newline at end of file diff --git a/res/dpr/utility/encode_color_conversion.frag b/res/dpr/utility/encode_color_conversion.frag index 7e39b74b8929e86ff2476a022dcda5d302685c49..9125d168879650d312f8b721cf9e1cf868322bc4 100644 --- a/res/dpr/utility/encode_color_conversion.frag +++ b/res/dpr/utility/encode_color_conversion.frag @@ -15,13 +15,19 @@ void main() vec3(0.114, 0.436, -0.10001) //third column ); + float gamma = 2.2; + float u_max = 0.436; + float v_max = 0.615; + int height = textureSize(samplerInput, 0).y; ivec2 coord = ivec2(gl_FragCoord.xy); coord.y = height - coord.y; - vec3 color_rgb = texelFetch(samplerInput, coord, 0).xyz; - vec3 color_yuv = convert_matrix * color_rgb; + vec3 color_rgb_linear = texelFetch(samplerInput, coord, 0).xyz; + vec3 color_rgb_gamma = pow(color_rgb_linear, vec3(1.0 / gamma)); //Slight change in brighness when compared to emulated headset. Maybe due to different gamma compression when using sRGB. + vec3 color_yuv = convert_matrix * color_rgb_gamma; outLuma = color_yuv.x; - outChroma = color_yuv.yz + 0.5; + outChroma.x = ((color_yuv.y / u_max) + 1.0) / 2.0; + outChroma.y = ((color_yuv.z / v_max) + 1.0) / 2.0; } diff --git a/res/dpr/utility/indirect_capture.frag b/res/dpr/utility/indirect_capture.frag new file mode 100644 index 0000000000000000000000000000000000000000..13f91c7c05e81b7f2d15be95d1ea644c3ede23cf --- /dev/null +++ b/res/dpr/utility/indirect_capture.frag @@ -0,0 +1,55 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#define MATERIAL_DESCRIPTOR_SET 2 + +#include "light_data.inc" +#include "math_library.glsl" +#include "material_library.glsl" + +layout(location = 0) out vec4 outFlux; +layout(location = 1) out vec2 outNormal; + +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 = 1, binding = 0) uniform LightParameterBuffer +{ + LightParameter light_parameter; +}; + +layout(set = 1, binding = 1, std430) readonly buffer LightDataBuffer +{ + LightData lights[]; +}; + +layout(push_constant) uniform Constants +{ + uint light_index; + uint capture_resolution; +}; + +float comnpute_pixel_area() +{ + vec2 near_size = lights[light_index].near_size; + vec2 pixel_size = near_size / float(capture_resolution); + + return pixel_size.x * pixel_size.y; //NOTE: Where pixel_size is in meters +} + +void main() +{ + vec4 base_color_opacity = lookup_base_color_opacity(inUV); + + if (base_color_opacity.w < EPSILON) + { + discard; + } + + vec3 normal = lookup_normal(inUV, inNormal, inTangent); + + outFlux = vec4(lights[light_index].color * base_color_opacity.xyz * comnpute_pixel_area(), 1.0); //NOTE: Where color is in watt/meter^2 and where outFlux is in watt + outNormal = encode_normal(normal); +} \ No newline at end of file diff --git a/res/dpr/utility/indirect_capture.vert b/res/dpr/utility/indirect_capture.vert new file mode 100644 index 0000000000000000000000000000000000000000..56fa702bc96dad85633cfa672e69b79e2f32f096 --- /dev/null +++ b/res/dpr/utility/indirect_capture.vert @@ -0,0 +1,47 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#include "light_data.inc" +#include "mesh_data.inc" + +layout(set = 0, binding = 0) uniform MeshBuffer +{ + MeshData mesh; +}; + +layout(set = 1, binding = 0) uniform LightParameterBuffer +{ + LightParameter light_parameter; +}; + +layout(set = 1, binding = 1, std430) readonly buffer LightDataBuffer +{ + LightData lights[]; +}; + +layout(push_constant) uniform Constants +{ + uint light_index; + uint capture_resolution; +}; + +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(location = 0) out vec3 outPos; +layout(location = 1) out vec3 outNormal; +layout(location = 2) out vec3 outTangent; +layout(location = 3) out vec2 outUV; + +void main() +{ + outPos = vec3(mesh.localToWorldSpace * vec4(inPos, 1.0)); + outNormal = normalize((mesh.vectorToWorldSpace * vec4(inNormal, 0.0)).xyz); + outTangent = normalize((mesh.vectorToWorldSpace * vec4(inTangent, 0.0)).xyz); + outUV = inUV; + + mat4 shadow_matrix = lights[light_index].shadow_matrix[0]; + gl_Position = shadow_matrix * vec4(outPos, 1.0); +} diff --git a/res/dpr/utility/indirect_geometry.frag b/res/dpr/utility/indirect_geometry.frag new file mode 100644 index 0000000000000000000000000000000000000000..13f593f00ce2f8f747de63db3791684cd2030e98 --- /dev/null +++ b/res/dpr/utility/indirect_geometry.frag @@ -0,0 +1,66 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#include "indirect_data.inc" +#include "indirect_library.glsl" + +layout(set = 1, binding = 0) uniform IndirectDomainBuffer +{ + IndirectDomain indirect_domain; +}; + +layout(set = 1, binding = 1, r32ui) uniform uimage3D image_geometry_xy_distribution; +layout(set = 1, binding = 2, r32ui) uniform uimage3D image_geometry_zw_distribution; + +layout(push_constant) uniform Constants +{ + uvec3 voxel_resolution; +}; + +layout(location = 0) in vec3 inCoord; +layout(location = 1) in vec3 inNormal; + +#define atomicAdd(image, coord, value) \ +{ \ + uint new_value = packHalf2x16(value); \ + uint previous_value = 0; \ + uint current_value = 0; \ + \ + while((current_value = imageAtomicCompSwap(image, coord, previous_value, new_value)) != previous_value) \ + { \ + previous_value = current_value; \ + new_value = packHalf2x16(value + unpackHalf2x16(current_value)); \ + } \ +} + +void main() +{ + ivec3 cell_coord = ivec3(inCoord * indirect_domain.geometry_resolution); + + vec3 normal = normalize(inNormal); + vec4 distribution = spherical_harmonic_cosine_lobe(normal); + + vec3 coverage = vec3(indirect_domain.geometry_resolution) / vec3(voxel_resolution); + float opacity = 0.0; + + if(gl_ViewportIndex == 0) + { + opacity = coverage.y * coverage.z; + } + + else if(gl_ViewportIndex == 1) + { + opacity = coverage.x * coverage.z; + } + + else + { + opacity = coverage.x * coverage.y; + } + + //NOTE: Where opacity is in percent + vec4 geometry_distribution = opacity * distribution; + + atomicAdd(image_geometry_xy_distribution, cell_coord, geometry_distribution.xy); + atomicAdd(image_geometry_zw_distribution, cell_coord, geometry_distribution.zw); +} \ No newline at end of file diff --git a/res/dpr/utility/indirect_geometry.geom b/res/dpr/utility/indirect_geometry.geom new file mode 100644 index 0000000000000000000000000000000000000000..1227568cf0eec0284736944132414eb74c867d4c --- /dev/null +++ b/res/dpr/utility/indirect_geometry.geom @@ -0,0 +1,79 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#include "indirect_data.inc" + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +layout(set = 1, binding = 0) uniform IndirectDomainBuffer +{ + IndirectDomain indirect_domain; +}; + +layout(location = 0) in vec3 inNormal[]; + +layout(location = 0) out vec3 outCoord; +layout(location = 1) out vec3 outNormal; + +void main() +{ + vec3 position1 = gl_in[0].gl_Position.xyz; + vec3 position2 = gl_in[1].gl_Position.xyz; + vec3 position3 = gl_in[2].gl_Position.xyz; + + vec3 coord1 = (position1 - indirect_domain.min) / (indirect_domain.max - indirect_domain.min); + vec3 coord2 = (position2 - indirect_domain.min) / (indirect_domain.max - indirect_domain.min); + vec3 coord3 = (position3 - indirect_domain.min) / (indirect_domain.max - indirect_domain.min); + + vec2 screen_coord1 = vec2(0.0); + vec2 screen_coord2 = vec2(0.0); + vec2 screen_coord3 = vec2(0.0); + + vec3 normal_abs = abs(cross(position2 - position1, position3 - position1)); + float normal_max = max(normal_abs.x, max(normal_abs.y, normal_abs.z)); + + if(normal_abs.x == normal_max) + { + screen_coord1 = coord1.yz; + screen_coord2 = coord2.yz; + screen_coord3 = coord3.yz; + + gl_ViewportIndex = 0; + } + + else if(normal_abs.y == normal_max) + { + screen_coord1 = coord1.xz; + screen_coord2 = coord2.xz; + screen_coord3 = coord3.xz; + + gl_ViewportIndex = 1; + } + + else + { + screen_coord1 = coord1.xy; + screen_coord2 = coord2.xy; + screen_coord3 = coord3.xy; + + gl_ViewportIndex = 2; + } + + outCoord = coord1; + outNormal = inNormal[0]; + gl_Position = vec4(screen_coord1 * 2.0 - 1.0, 0.5, 1.0); + EmitVertex(); + + outCoord = coord2; + outNormal = inNormal[1]; + gl_Position = vec4(screen_coord2 * 2.0 - 1.0, 0.5, 1.0); + EmitVertex(); + + outCoord = coord3; + outNormal = inNormal[2]; + gl_Position = vec4(screen_coord3 * 2.0 - 1.0, 0.5, 1.0); + EmitVertex(); + + EndPrimitive(); +} \ No newline at end of file diff --git a/res/dpr/utility/indirect_geometry.vert b/res/dpr/utility/indirect_geometry.vert new file mode 100644 index 0000000000000000000000000000000000000000..284167343a04b548ada96d746696ffadaa424bd0 --- /dev/null +++ b/res/dpr/utility/indirect_geometry.vert @@ -0,0 +1,23 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#include "mesh_data.inc" + +layout(set = 0, binding = 0) uniform MeshBuffer +{ + MeshData mesh; +}; + +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(location = 0) out vec3 outNormal; + +void main() +{ + outNormal = normalize((mesh.vectorToWorldSpace * vec4(inNormal, 0.0)).xyz); + + gl_Position = mesh.localToWorldSpace * vec4(inPos, 1.0); +} diff --git a/res/dpr/utility/indirect_injection.frag b/res/dpr/utility/indirect_injection.frag new file mode 100644 index 0000000000000000000000000000000000000000..c7444ae5fb4ed6b5765c0d5b9ab245f39c19b0ad --- /dev/null +++ b/res/dpr/utility/indirect_injection.frag @@ -0,0 +1,22 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#include "indirect_library.glsl" + +layout(location = 0) out vec4 outRedDistribution; +layout(location = 1) out vec4 outGreenDistribution; +layout(location = 2) out vec4 outBlueDistribution; + +layout(location = 0) in vec3 inFlux; +layout(location = 1) in vec3 inNormal; + +void main() +{ + vec3 normal = normalize(inNormal); + vec4 distribution = spherical_harmonic_cosine_lobe(normal); + + //NOTE: Where inFlux is in watt + outRedDistribution = inFlux.x * distribution; //NOTE: Additive blending + outGreenDistribution = inFlux.y * distribution; //NOTE: Additive blending + outBlueDistribution = inFlux.z * distribution; //NOTE: Additive blending +} \ No newline at end of file diff --git a/res/dpr/utility/indirect_injection.geom b/res/dpr/utility/indirect_injection.geom new file mode 100644 index 0000000000000000000000000000000000000000..93ca3bbf2196dfaf5507ac6f838c6c600568fb19 --- /dev/null +++ b/res/dpr/utility/indirect_injection.geom @@ -0,0 +1,34 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#include "indirect_data.inc" + +layout(points) in; +layout(points, max_vertices = 1) out; + +layout(set = 1, binding = 3) uniform IndirectDomainBuffer +{ + IndirectDomain indirect_domain; +}; + +layout(location = 0) in vec3 inFlux[]; +layout(location = 1) in vec3 inNormal[]; + +layout(location = 0) out vec3 outFlux; +layout(location = 1) out vec3 outNormal; + +void main() +{ + vec3 position = gl_in[0].gl_Position.xyz; + position = (position - indirect_domain.min) / (indirect_domain.max - indirect_domain.min); + position.xy = position.xy * 2.0 - 1.0; + position.z = position.z * indirect_domain.indirect_resolution.z; + + outFlux = inFlux[0]; + outNormal = inNormal[0]; + gl_Position = vec4(position.xy, 0.5, 1.0); + gl_Layer = int(position.z); + + EmitVertex(); + EndPrimitive(); +} \ No newline at end of file diff --git a/res/dpr/utility/indirect_injection.vert b/res/dpr/utility/indirect_injection.vert new file mode 100644 index 0000000000000000000000000000000000000000..153957f7ea79394cff526372f38432ecfd3e8347 --- /dev/null +++ b/res/dpr/utility/indirect_injection.vert @@ -0,0 +1,52 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#include "light_data.inc" +#include "indirect_data.inc" +#include "math_library.glsl" + +layout(set = 0, binding = 0) uniform LightParameterBuffer +{ + LightParameter light_parameter; +}; + +layout(set = 0, binding = 1, std430) readonly buffer LightDataBuffer +{ + LightData lights[]; +}; + +layout(set = 1, binding = 0) uniform sampler2D sampler_capture_depth; +layout(set = 1, binding = 1) uniform sampler2D sampler_capture_flux; +layout(set = 1, binding = 2) uniform sampler2D sampler_capture_normal; + +layout(push_constant) uniform Constants +{ + uint light_index; + uint capture_resolution; + float cell_size; +}; + +layout(location = 0) out vec3 outFlux; +layout(location = 1) out vec3 outNormal; + +void main() +{ + ivec2 coord = ivec2(gl_VertexIndex / capture_resolution, gl_VertexIndex % capture_resolution); + + float depth = texelFetch(sampler_capture_depth, coord, 0).x; + vec3 flux = texelFetch(sampler_capture_flux, coord, 0).xyz; + vec3 normal = decode_normal(texelFetch(sampler_capture_normal, coord, 0).xy); + + vec2 screen_coord = vec2(coord + 0.5) / vec2(capture_resolution); + screen_coord = screen_coord * 2.0 - 1.0; + + vec4 position = lights[light_index].inv_shadow_matrix[0] * vec4(screen_coord, depth, 1.0); + position /= position.w; + + //NOTE: Shift the virtual point light along the normal in order to avoid self lighting and shadowing + position.xyz += 0.5 * cell_size * normal; + + outFlux = flux; + outNormal = normal; + gl_Position = position; +} diff --git a/res/dpr/utility/indirect_library.glsl b/res/dpr/utility/indirect_library.glsl new file mode 100644 index 0000000000000000000000000000000000000000..6d439a61dfa11d8e83aa0993b4606a348d70d514 --- /dev/null +++ b/res/dpr/utility/indirect_library.glsl @@ -0,0 +1,49 @@ +#ifndef SHADER_INCLUDE_INDIRECT_LIBRARY +#define SHADER_INCLUDE_INDIRECT_LIBRARY + +#include "math_library.glsl" + +//NOTE: Standard factors for real valued spherical harmonics making them orhonormal. +#define SPHERICAL_HARMONIC_BAND0_FACTOR (0.5 * sqrt(1.0 / PI)) +#define SPHERICAL_HARMONIC_BAND1_FACTOR sqrt(3.0 / (4.0 * PI)) + +//NOTE: Integration of the spherical harmoinc base functions using the standard factors +#define SPHERICAL_HARMONIC_BAND0_INTEGRATED (2.0 * sqrt(PI)) +#define SPHERICAL_HARMONIC_BAND1_INTEGRATED 0.0 + +//NOTE: The given factors where computed for the function I(theta, phi) = max(cos(theta), 0) / PI. +// This function was choosen, since the integral over it is one. +// Therefore the function describes a distribution. +#define SPHERICAL_HARMONIC_COSINE_LOBE_BAND0_FACTOR (0.5 * sqrt(1.0 / PI)) +#define SPHERICAL_HARMONIC_COSINE_LOBE_BAND1_FACTOR sqrt(1.0 / (3.0 * PI)) + +//NOTE: Assume direction is allready normalize +float spherical_harmonic_evaluate(vec3 direction, vec4 spherical_harmonic) +{ + return dot(spherical_harmonic, vec4(SPHERICAL_HARMONIC_BAND0_FACTOR, + SPHERICAL_HARMONIC_BAND1_FACTOR * direction.y, + SPHERICAL_HARMONIC_BAND1_FACTOR * direction.z, + SPHERICAL_HARMONIC_BAND1_FACTOR * direction.x)); +} + +//NOTE: Assume direction is allready normalize +// The compute spherical harmonic represents the function I(theta, phi) = max(cos(theta), 0) / PI, +// when it is rotated towards "direction". +vec4 spherical_harmonic_cosine_lobe(vec3 direction) +{ + return vec4(SPHERICAL_HARMONIC_COSINE_LOBE_BAND0_FACTOR, + SPHERICAL_HARMONIC_COSINE_LOBE_BAND1_FACTOR * (-direction.y), + SPHERICAL_HARMONIC_COSINE_LOBE_BAND1_FACTOR * direction.z, + SPHERICAL_HARMONIC_COSINE_LOBE_BAND1_FACTOR * (-direction.x)); +} + +//NOTE: Integrate the given spherical haromic +float spherical_harmonic_integrate(vec4 spherical_harmonic) +{ + return dot(spherical_harmonic, vec4(SPHERICAL_HARMONIC_BAND0_INTEGRATED, + SPHERICAL_HARMONIC_BAND1_INTEGRATED, + SPHERICAL_HARMONIC_BAND1_INTEGRATED, + SPHERICAL_HARMONIC_BAND1_INTEGRATED)); +} + +#endif \ No newline at end of file diff --git a/res/dpr/utility/indirect_propagation.comp b/res/dpr/utility/indirect_propagation.comp new file mode 100644 index 0000000000000000000000000000000000000000..8ed550f60eaf449524420c47eff444b64486cc93 --- /dev/null +++ b/res/dpr/utility/indirect_propagation.comp @@ -0,0 +1,182 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : require + +#include "indirect_data.inc" +#include "indirect_library.glsl" + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 2) in; + +layout(set = 0, binding = 0, rgba16f) uniform readonly image3D image_src_red_distribution; +layout(set = 0, binding = 1, rgba16f) uniform readonly image3D image_src_green_distribution; +layout(set = 0, binding = 2, rgba16f) uniform readonly image3D image_src_blue_distribution; + +layout(set = 0, binding = 3, rgba16f) uniform writeonly image3D image_dst_red_distribution; +layout(set = 0, binding = 4, rgba16f) uniform writeonly image3D image_dst_green_distribution; +layout(set = 0, binding = 5, rgba16f) uniform writeonly image3D image_dst_blue_distribution; + +layout(set = 0, binding = 6, rgba16f) uniform image3D image_red_indirect; +layout(set = 0, binding = 7, rgba16f) uniform image3D image_green_indirect; +layout(set = 0, binding = 8, rgba16f) uniform image3D image_blue_indirect; + +layout(set = 0, binding = 9) uniform sampler3D sampler_geometry_xy_distribution; +layout(set = 0, binding = 10) uniform sampler3D sampler_geometry_zw_distribution; + +layout(set = 0, binding = 11) uniform IndirectDomainBuffer +{ + IndirectDomain indirect_domain; +}; + +const ivec3 neighbour_offsets[6] = ivec3[6] +( + ivec3( 1, 0, 0), //+X + ivec3(-1, 0, 0), //-X + ivec3( 0, 1, 0), //+Y + ivec3( 0,-1, 0), //-Y + ivec3( 0, 0, 1), //+Z + ivec3( 0, 0,-1) //-Z +); + +const mat3 neighbour_orientations[6] = mat3[6] +( + mat3(vec3(-1.0, 0.0, 0.0), vec3( 0.0, 1.0, 0.0), vec3( 0.0, 0.0, 1.0)), //+X + mat3(vec3( 1.0, 0.0, 0.0), vec3( 0.0, 1.0, 0.0), vec3( 0.0, 0.0, 1.0)), //-X + mat3(vec3( 0.0,-1.0, 0.0), vec3( 1.0, 0.0, 0.0), vec3( 0.0, 0.0, 1.0)), //+Y + mat3(vec3( 0.0, 1.0, 0.0), vec3( 1.0, 0.0, 0.0), vec3( 0.0, 0.0, 1.0)), //-Y + mat3(vec3( 0.0, 0.0,-1.0), vec3( 0.0, 1.0, 0.0), vec3( 1.0, 0.0, 0.0)), //+Z + mat3(vec3( 0.0, 0.0, 1.0), vec3( 0.0, 1.0, 0.0), vec3( 1.0, 0.0, 0.0)) //-Z +); + +//NOTE: All directions derived for the case -X +// A side eval direction correspond to the center directions of that intensity cone. +const vec3 direct_eval_direction = vec3(1.0, 0.0, 0.0); +const vec3 side_eval_directions[4] = vec3[4] +( + vec3(0.85065080, 0.52573111, 0.0), + vec3(0.85065080,-0.52573111, 0.0), + vec3(0.85065080, 0.0, 0.52573111), + vec3(0.85065080, 0.0,-0.52573111) +); + +//NOTE: All directions derived for the case -X +const vec3 direct_emit_direction = vec3(1.0, 0.0, 0.0); +const vec3 side_emit_directions[4] = vec3[4] +( + vec3(0.0, 1.0, 0.0), + vec3(0.0,-1.0, 0.0), + vec3(0.0, 0.0, 1.0), + vec3(0.0, 0.0,-1.0) +); + +//NOTE: Solid angle for direct and side face. +const float direct_solid_angle = 0.40066968; +const float side_solid_angle = 0.42343135; + +bool check_bounds(ivec3 coord) +{ + return all(lessThanEqual(ivec3(0), coord)) && all(lessThan(coord, indirect_domain.indirect_resolution)); +} + +vec4 load_geometry_distribution(vec3 geometry_coord) +{ + vec3 coord = geometry_coord / vec3(indirect_domain.indirect_resolution); + + vec4 geometry_distribution = vec4(0.0); + geometry_distribution.xy = texture(sampler_geometry_xy_distribution, coord).xy; + geometry_distribution.zw = texture(sampler_geometry_zw_distribution, coord).xy; + + return geometry_distribution / max(1.0, spherical_harmonic_integrate(geometry_distribution)); +} + +void propagate_neighbour(ivec3 cell_coord, uint neighbour, inout vec4 red_distribution, inout vec4 green_distribution, inout vec4 blue_distribution) +{ + ivec3 neighbour_coord = cell_coord + neighbour_offsets[neighbour]; + mat3 neighbour_orientation = neighbour_orientations[neighbour]; + + if(!check_bounds(neighbour_coord)) + { + return; + } + + //----- Visibility ---------------------------------------------- + + vec3 geometry_coord = (vec3(cell_coord) + vec3(neighbour_coord)) / 2.0; + vec4 geometry_distribution = load_geometry_distribution(geometry_coord); + vec3 geometry_direction = neighbour_orientation * (-direct_eval_direction); + + float opacity = spherical_harmonic_evaluate(geometry_direction, geometry_distribution); + float visibility = clamp(1.0 - opacity, 0.0, 1.0); + + //----- Side Faces ---------------------------------------------- + + vec4 neighbour_red_distribution = imageLoad(image_src_red_distribution, neighbour_coord); + vec4 neighbour_green_distribution = imageLoad(image_src_green_distribution, neighbour_coord); + vec4 neighbour_blue_distribution = imageLoad(image_src_blue_distribution, neighbour_coord); + + for(uint side = 0; side < 4; side++) + { + vec3 eval_direction = neighbour_orientation * side_eval_directions[side]; + vec3 emit_direction = neighbour_orientation * side_emit_directions[side]; + + vec3 intensity = vec3(0.0); //NOTE: Where intensity is in watt / str + intensity.x = max(0.0, spherical_harmonic_evaluate(eval_direction, neighbour_red_distribution)); + intensity.y = max(0.0, spherical_harmonic_evaluate(eval_direction, neighbour_green_distribution)); + intensity.z = max(0.0, spherical_harmonic_evaluate(eval_direction, neighbour_blue_distribution)); + + //NOTE: Where side_solid_angle * intensity is in watt + red_distribution += (side_solid_angle * intensity.x) * visibility * spherical_harmonic_cosine_lobe(emit_direction); + green_distribution += (side_solid_angle * intensity.y) * visibility * spherical_harmonic_cosine_lobe(emit_direction); + blue_distribution += (side_solid_angle * intensity.z) * visibility * spherical_harmonic_cosine_lobe(emit_direction); + } + + //----- Direct Face ----------------------------------------------- + + vec3 eval_direction = neighbour_orientation * direct_eval_direction; + vec3 emit_direction = neighbour_orientation * direct_emit_direction; + + vec3 intensity = vec3(0.0); //NOTE: Where intensity is in watt / str + intensity.x = max(0.0, spherical_harmonic_evaluate(eval_direction, neighbour_red_distribution)); + intensity.y = max(0.0, spherical_harmonic_evaluate(eval_direction, neighbour_green_distribution)); + intensity.z = max(0.0, spherical_harmonic_evaluate(eval_direction, neighbour_blue_distribution)); + + //NOTE: Where side_solid_angle * intensity is in watt + red_distribution += (direct_solid_angle * intensity.x) * visibility * spherical_harmonic_cosine_lobe(emit_direction); + green_distribution += (direct_solid_angle * intensity.y) * visibility * spherical_harmonic_cosine_lobe(emit_direction); + blue_distribution += (direct_solid_angle * intensity.z) * visibility * spherical_harmonic_cosine_lobe(emit_direction); + + //----------------------------------------------------------------- +} + +void main() +{ + ivec3 cell_coord = ivec3(gl_GlobalInvocationID); + + if(!check_bounds(cell_coord)) + { + return; + } + + vec4 red_distribution = vec4(0.0); + vec4 green_distribution = vec4(0.0); + vec4 blue_distribution = vec4(0.0); + + for(uint neighbour = 0; neighbour < 6; neighbour++) + { + propagate_neighbour(cell_coord, neighbour, red_distribution, green_distribution, blue_distribution); + } + + imageStore(image_dst_red_distribution, cell_coord, red_distribution); + imageStore(image_dst_green_distribution, cell_coord, green_distribution); + imageStore(image_dst_blue_distribution, cell_coord, blue_distribution); + + vec4 red_indirect = imageLoad(image_red_indirect, cell_coord); + vec4 green_indirect = imageLoad(image_green_indirect, cell_coord); + vec4 blue_indirect = imageLoad(image_blue_indirect, cell_coord); + + red_indirect += red_distribution; + green_indirect += green_distribution; + blue_indirect += blue_distribution; + + imageStore(image_red_indirect, cell_coord, red_indirect); + imageStore(image_green_indirect, cell_coord, green_indirect); + imageStore(image_blue_indirect, cell_coord, blue_indirect); +} \ No newline at end of file diff --git a/res/dpr/utility/light_library.glsl b/res/dpr/utility/light_library.glsl index cbb3670eaa1c935baba3457a2ac78eaeb7bd1fba..89eb1e0b3f25333e134e7b5c442083e85640c0c9 100644 --- a/res/dpr/utility/light_library.glsl +++ b/res/dpr/utility/light_library.glsl @@ -2,21 +2,24 @@ Before including the light-library it is neccessary to set the define LIGHT_DESCRIPTOR_SET. This define specifies the descriptor set in which the light information is stored. If the define is not set, an error is emitted. - After including the light-library, the function apply_local_lighting(...) can be used to compute local lighting. + After including the light-library, the function apply_lighting(...) can be used to compute lighting. The used lighting model is physically based and was taken from the GLTF-Specification. When the light calculation should also include shadows, the define SHADOW_DESCRIPTOR_SET has to be set. - This defines specifies the descriptor set for the shadow cache. + This define specifies the descriptor set for the shadow cache. + If also indirect lighting should be included, the define INDIRECT_DESCRIPTOR_SET has to be set. + This define specifies the descriptor set for the indirect cache. Example: #define LIGHT_DESCRIPTOR_SET 42 //Required #define SHADOW_DESCRIPTOR_SET 43 //Optional + #define INDIRECT_DESCRIPTOR_SET 44 //Optional #include "light_library.glsl" void main() { //... lookup material and lookup normal from normal map - vec3 lighting = apply_local_lighting(view_position, surface_position, surface_normal, material); + vec3 lighting = apply_lighting(view_position, surface_position, surface_normal, material); //... write to framebuffer } @@ -31,8 +34,10 @@ #include "math_library.glsl" #include "material_struct.glsl" +#include "indirect_library.glsl" #include "light_data.inc" #include "shadow_data.inc" +#include "indirect_data.inc" layout(set = LIGHT_DESCRIPTOR_SET, binding = 0) uniform LightParameterBuffer { @@ -55,6 +60,17 @@ layout(set = SHADOW_DESCRIPTOR_SET, binding = 3) uniform ShadowParameterBuffer }; #endif +#ifdef INDIRECT_DESCRIPTOR_SET +layout(set = INDIRECT_DESCRIPTOR_SET, binding = 0) uniform sampler3D sampler_indirect_red; +layout(set = INDIRECT_DESCRIPTOR_SET, binding = 1) uniform sampler3D sampler_indirect_green; +layout(set = INDIRECT_DESCRIPTOR_SET, binding = 2) uniform sampler3D sampler_indirect_blue; + +layout(set = INDIRECT_DESCRIPTOR_SET, binding = 3) uniform IndirectDomainBuffer +{ + IndirectDomain indirect_domain; +}; +#endif + //-- Shadow Functions ------------------------------------------------------------------------------ #ifdef SHADOW_DESCRIPTOR_SET @@ -269,6 +285,29 @@ vec3 compute_brdf(vec3 light_direction, vec3 normal_direction, vec3 view_directi return diffuse + specular; } +//-- Indirect Functions ---------------------------------------------------------------------------- + +#ifdef INDIRECT_DESCRIPTOR_SET +vec3 get_indirect_radiance(vec3 surface_position, vec3 normal_direction) +{ + vec3 cell_coord = (surface_position - indirect_domain.min) / (indirect_domain.max - indirect_domain.min); + + vec4 red_distribution = texture(sampler_indirect_red, cell_coord); + vec4 green_distribution = texture(sampler_indirect_green, cell_coord); + vec4 blue_distribution = texture(sampler_indirect_blue, cell_coord); + + //Where center_distance_square is in meter^2 + const float center_distance_square = pow(indirect_domain.cell_size / 2.0, 2); + + vec3 indirect_radiance = vec3(0.0); //Where indirect light is in watts / (meter^2 * str) + indirect_radiance.x = max(0.0, spherical_harmonic_evaluate(-normal_direction, red_distribution)) / center_distance_square; + indirect_radiance.y = max(0.0, spherical_harmonic_evaluate(-normal_direction, green_distribution)) / center_distance_square; + indirect_radiance.z = max(0.0, spherical_harmonic_evaluate(-normal_direction, blue_distribution)) / center_distance_square; + + return indirect_radiance; +} +#endif + //-- Tone-Mapping Functions ------------------------------------------------------------------------ // Exposure based tone mapping //https://learnopengl.com/Advanced-Lighting/HDR @@ -280,27 +319,33 @@ vec3 compute_tone_mapping(vec3 lighting, float exposure) //-- Public Function ------------------------------------------------------------------------------- -vec3 apply_local_lighting(vec3 view_position, vec3 surface_position, vec3 surface_normal, Material material) +vec3 apply_lighting(vec3 view_position, vec3 surface_position, vec3 surface_normal, Material material) { vec3 lighting = material.emissive; //where emissive is in watts/meter^2 *str - if (dot(lighting, vec3(1.0)) < EPSILON) //apply direct lighting only if the material is not emissive + if (dot(lighting, vec3(1.0)) < EPSILON) //apply direct and indirect lighting only if the material is not emissive { + vec3 normal_direction = normalize(surface_normal); + vec3 view_direction = normalize(view_position - surface_position); + lighting += material.base_color * light_parameter.ambient; //where ambient is in watts/meter^2 *str - for (uint light = 0; light < light_parameter.count; light++) + for (uint light = 0; light < light_parameter.light_count; light++) { - vec3 view_direction = normalize(view_position - surface_position); - vec3 normal_direction = normalize(surface_normal); vec3 light_direction = get_light_direction(light, surface_position); - #ifdef SHADOW_DESCRIPTOR_SET +#ifdef SHADOW_DESCRIPTOR_SET lighting += compute_brdf(light_direction, normal_direction, view_direction, material) * get_light_radiance(light, surface_position) * get_shadow(light, surface_position) * dot(normal_direction, light_direction); - #else +#else lighting += compute_brdf(light_direction, normal_direction, view_direction, material) * get_light_radiance(light, surface_position) * dot(normal_direction, light_direction); - #endif +#endif } +#ifdef INDIRECT_DESCRIPTOR_SET + material.roughness = 1.0; //NOTE: Set to one, since light propagation volumes can only deliver diffuse indirect. + lighting += compute_brdf(normal_direction, normal_direction, view_direction, material) * get_indirect_radiance(surface_position, normal_direction) * dot(normal_direction, normal_direction); +#endif + lighting *= (1.0 - material.occlusion); } diff --git a/res/dpr/utility/material_library.glsl b/res/dpr/utility/material_library.glsl index 1ba7d28330253ebc73a045a032355515b6ab79f9..9639a76d528da5e5eb81f1447f1a69fde17e086e 100644 --- a/res/dpr/utility/material_library.glsl +++ b/res/dpr/utility/material_library.glsl @@ -33,6 +33,7 @@ #error "The material descripotor-set is not specified" #endif +#include "material_data.inc" #include "material_struct.glsl" layout(set = MATERIAL_DESCRIPTOR_SET, binding = 0) uniform sampler2D sampler_color_opacity; @@ -40,6 +41,11 @@ layout(set = MATERIAL_DESCRIPTOR_SET, binding = 1) uniform sampler2D sampler_mat layout(set = MATERIAL_DESCRIPTOR_SET, binding = 2) uniform sampler2D sampler_normal; layout(set = MATERIAL_DESCRIPTOR_SET, binding = 3) uniform sampler2D sampler_emissive; +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 4) uniform MaterialDataBuffer +{ + MaterialData material_data; +}; + Material lookup_material(vec2 uv_coord) { vec4 color_opacity = texture(sampler_color_opacity, uv_coord); @@ -47,12 +53,12 @@ Material lookup_material(vec2 uv_coord) vec3 emissive_color = texture(sampler_emissive, uv_coord).xyz; Material material; - material.base_color = color_opacity.xyz; - material.opacity = color_opacity.w; + material.base_color = color_opacity.xyz * material_data.base_color; + material.opacity = color_opacity.w * material_data.opacity; material.occlusion = material_factor.x; - material.roughness = material_factor.y; - material.metallic = material_factor.z; - material.emissive = emissive_color; + material.roughness = material_factor.y * material_data.roughness; + material.metallic = material_factor.z * material_data.metallic; + material.emissive = emissive_color * material_data.emissive; return material; } @@ -69,4 +75,9 @@ vec3 lookup_normal(vec2 uv_coord, vec3 surface_normal, vec3 surface_tangent) return mat3(tangent, bitangent, normal) * map_normal; } +vec4 lookup_base_color_opacity(vec2 uv_coord) +{ + return texture(sampler_color_opacity, uv_coord); +} + #endif \ No newline at end of file diff --git a/src/headset/emulated_headset.cpp b/src/headset/emulated_headset.cpp index cff8020b9903566b37b5f96a527661840287d812..3bd74ecdad9d65e96c617d1c9c4a0d113dc0546c 100644 --- a/src/headset/emulated_headset.cpp +++ b/src/headset/emulated_headset.cpp @@ -19,12 +19,7 @@ bool EmulatedHeadset::on_create() this->compute_matrices(EYE_LEFT); this->compute_matrices(EYE_RIGHT); - if (!this->create_framebuffer(EYE_LEFT)) - { - return false; - } - - if (!this->create_framebuffer(EYE_RIGHT)) + if (!this->create_framebuffer()) { return false; } @@ -34,8 +29,7 @@ bool EmulatedHeadset::on_create() void EmulatedHeadset::on_destroy() { - this->destroy_framebuffer(EYE_LEFT); - this->destroy_framebuffer(EYE_RIGHT); + this->destroy_framebuffer(); } bool EmulatedHeadset::on_interface() @@ -115,7 +109,7 @@ void EmulatedHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout } VkImageCopy image_region; - image_region.srcSubresource = subresource_layers; + image_region.srcSubresource = frame_image->get_subresource_layers(); image_region.srcOffset.x = 0; image_region.srcOffset.y = 0; image_region.srcOffset.z = 0; @@ -142,6 +136,19 @@ void EmulatedHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout VkFormat EmulatedHeadset::get_format() const { + switch (this->get_application()->get_target()->get_format()) + { + case VK_FORMAT_B8G8R8A8_UNORM: + case VK_FORMAT_B8G8R8A8_SRGB: + return VK_FORMAT_B8G8R8A8_SRGB; + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R8G8B8A8_SRGB: + return VK_FORMAT_R8G8B8A8_SRGB; + default: + lava::log()->warn("Can't derive correct sRGB color format for headset framebuffer!"); + break; + } + return this->get_application()->get_target()->get_format(); } @@ -175,6 +182,11 @@ lava::image::ptr EmulatedHeadset::get_framebuffer(FrameId frame_id) const return this->framebuffers[frame_id]; } +lava::image::ptr EmulatedHeadset::get_framebuffer_array() const +{ + return this->framebuffer_array; +} + const char* EmulatedHeadset::get_name() const { return "Emulated Headset"; @@ -185,25 +197,51 @@ bool EmulatedHeadset::is_attached(Controller controller) const return true; } -bool EmulatedHeadset::create_framebuffer(Eye eye) +bool EmulatedHeadset::create_framebuffer() { - lava::image::ptr framebuffer = lava::make_image(this->get_format()); - framebuffer->set_usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + this->framebuffer_array = lava::make_image(this->get_format()); + this->framebuffer_array->set_layer_count(this->framebuffers.size()); + this->framebuffer_array->set_view_type(VK_IMAGE_VIEW_TYPE_2D_ARRAY); + this->framebuffer_array->set_usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); - if (!framebuffer->create(this->get_application()->get_device(), this->resolution)) + if (!this->framebuffer_array->create(this->get_application()->get_device(), this->resolution)) { return false; } - this->framebuffers[eye] = framebuffer; + for (uint32_t index = 0; index < this->framebuffers.size(); index++) + { + lava::image::ptr framebuffer = lava::make_image(this->get_format(), this->framebuffer_array->get()); + framebuffer->set_base_array_layer(index); + framebuffer->set_view_type(VK_IMAGE_VIEW_TYPE_2D); + + if (!framebuffer->create(this->get_application()->get_device(), this->resolution)) + { + return false; + } + + this->framebuffers[index] = framebuffer; + } return true; } -void EmulatedHeadset::destroy_framebuffer(Eye eye) +void EmulatedHeadset::destroy_framebuffer() { - this->framebuffers[eye]->destroy(); - this->framebuffers[eye] = nullptr; + for (lava::image::ptr& framebuffer : this->framebuffers) + { + if (framebuffer != nullptr) + { + framebuffer->destroy(true); + framebuffer = nullptr; + } + } + + if (this->framebuffer_array != nullptr) + { + this->framebuffer_array->destroy(); + this->framebuffer_array = nullptr; + } } void EmulatedHeadset::compute_matrices(Eye eye) diff --git a/src/headset/emulated_headset.hpp b/src/headset/emulated_headset.hpp index 096ee8d36b9d0713e41e52096bab92e139675721..dc6617eebaefbb05a08637b3f3632c80340e6d7d 100644 --- a/src/headset/emulated_headset.hpp +++ b/src/headset/emulated_headset.hpp @@ -31,14 +31,15 @@ public: const glm::mat4& get_controller_matrix(Controller controller) const override; lava::image::ptr get_framebuffer(FrameId frame_id) const override; + lava::image::ptr get_framebuffer_array() const override; const char* get_name() const override; bool is_attached(Controller controller) const override; private: - bool create_framebuffer(Eye eye); - void destroy_framebuffer(Eye eye); + bool create_framebuffer(); + void destroy_framebuffer(); void compute_matrices(Eye eye); @@ -49,6 +50,7 @@ private: std::array<glm::mat4, 2> projection_matrices; std::array<glm::mat4, 2> controller_matrices; std::array<lava::image::ptr, 2> framebuffers; + lava::image::ptr framebuffer_array; float near_plane = 0.1f; float far_plane = 1000.0f; diff --git a/src/headset/headset.cpp b/src/headset/headset.cpp index a90f140b34c2a676c78e7a86cc81a6b95aaa2cfe..52e0f0aa3b128479601980cecb2f47c1317751a9 100644 --- a/src/headset/headset.cpp +++ b/src/headset/headset.cpp @@ -24,6 +24,11 @@ bool Headset::on_setup_device(lava::device::create_param& parameters) return true; } +void Headset::on_shutdown() +{ + +} + void Headset::submit_metadata(const FrameMetadata& metadata) { diff --git a/src/headset/headset.hpp b/src/headset/headset.hpp index 766d0950bcd611a3e446e14761c6fecfed951f4b..c9ed19eb96e7cbebb7319d744921b928139f8f8a 100644 --- a/src/headset/headset.hpp +++ b/src/headset/headset.hpp @@ -69,9 +69,13 @@ public: virtual bool on_create() = 0; // This function is called during the shutdown of the application and every time the active stereo strategy changes. - // A headset can use this function to destroy all reqources that it has created. + // A headset can use this function to destroy all resources that it has created. virtual void on_destroy() = 0; + // This function is only called during the shutdown of the application. + // A headset can use this function to destroy all remaining resources. + virtual void on_shutdown(); + // This function is called every frame when the interface needs to be rendered. // A headset can use this function to manage its own interface. virtual bool on_interface() = 0; @@ -105,12 +109,21 @@ public: // Returns the controller matrix of the given controller that should be used for rendering. virtual const glm::mat4& get_controller_matrix(Controller controller) const = 0; - // Returns the framebuffer image for the selected eye. + // Returns the framebuffer image for the selected frame id. // In case of an non-remote headset the frame id has to be ether EYE_LEFT or EYE_RIGHT. - // Otherwise the frame id must not exceed the the maximum number of remote frame ids that was set by the active stereo strategy. + // Otherwise the frame id must not exceed the maximum number of remote frame ids that was set by the active stereo strategy. // The images provided by the headset should support sampling, src and dest transfer operations as well as color attachment operations. + // It is imporant to note, that the provided image handle is the same as the image array handle of the headset's entire framebuffer. + // When using the image handle directly, the provided subresource information should be used to select the correct layer. virtual lava::image::ptr get_framebuffer(FrameId frame_id) const = 0; + // Returns the image array which defines the entire framebuffer of the headset. + // The frame id can be used to access the layer in which the framebuffer of that id is stored. + // In case of a non-remote headset, the number of layers has to be two. + // Otherwise the the number of layers is equal to the maximum number of remote frame ids that was set by the active stereo strategy. + // The array image provided by the headset should support sampling, src and dest transfer operations as well as color attachment operations. + virtual lava::image::ptr get_framebuffer_array() const = 0; + // Returns the name of the headset. virtual const char* get_name() const = 0; diff --git a/src/headset/openvr_headset.cpp b/src/headset/openvr_headset.cpp index 3b0a4b79c17d08f4d0408425ba9e4a092742ab9c..5a2e89afc6b2aa78e5b71bbdaa5dd245b43c863b 100644 --- a/src/headset/openvr_headset.cpp +++ b/src/headset/openvr_headset.cpp @@ -57,12 +57,7 @@ bool OpenVRHeadset::on_create() this->compute_matrices(EYE_LEFT); this->compute_matrices(EYE_RIGHT); - if (!this->create_framebuffer(EYE_LEFT)) - { - return false; - } - - if (!this->create_framebuffer(EYE_RIGHT)) + if (!this->create_framebuffer()) { return false; } @@ -72,9 +67,11 @@ bool OpenVRHeadset::on_create() void OpenVRHeadset::on_destroy() { - this->destroy_framebuffer(EYE_LEFT); - this->destroy_framebuffer(EYE_RIGHT); + this->destroy_framebuffer(); +} +void OpenVRHeadset::on_shutdown() +{ vr::VR_Shutdown(); } @@ -142,7 +139,7 @@ void OpenVRHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f lava::device_ptr device = this->get_application()->get_device(); - vr::VRVulkanTextureData_t texture_data; + vr::VRVulkanTextureArrayData_t texture_data; texture_data.m_nImage = (uint64_t)frame_image->get(); texture_data.m_pDevice = device->get(); texture_data.m_pPhysicalDevice = device->get_physical_device()->get(); @@ -153,17 +150,19 @@ void OpenVRHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f texture_data.m_nHeight = this->resolution.y; texture_data.m_nFormat = this->get_format(); texture_data.m_nSampleCount = VK_SAMPLE_COUNT_1_BIT; + texture_data.m_unArrayIndex = frame_id; + texture_data.m_unArraySize = this->framebuffers.size(); vr::Texture_t texture; texture.handle = &texture_data; texture.eType = vr::TextureType_Vulkan; texture.eColorSpace = vr::ColorSpace_Auto; - vr::EVRCompositorError error = vr::VRCompositor()->Submit((vr::EVREye) frame_id, &texture); + vr::EVRCompositorError error = vr::VRCompositor()->Submit((vr::EVREye)frame_id, &texture, nullptr, vr::Submit_VulkanTextureWithArrayData); if (error != vr::VRCompositorError_None) { - lava::log()->error("Can't submit image to OpenVR. Error code '{}'", error); + lava::log()->error("Can't submit image to OpenVR. Maybe due to headset standby. Error code '{}'", error); } } @@ -202,6 +201,11 @@ lava::image::ptr OpenVRHeadset::get_framebuffer(FrameId frame_id) const return this->framebuffers[frame_id]; } +lava::image::ptr OpenVRHeadset::get_framebuffer_array() const +{ + return this->framebuffer_array; +} + const char* OpenVRHeadset::get_name() const { return "OpenVR Headset"; @@ -212,25 +216,51 @@ bool OpenVRHeadset::is_attached(Controller controller) const return false; } -bool OpenVRHeadset::create_framebuffer(Eye eye) +bool OpenVRHeadset::create_framebuffer() { - lava::image::ptr framebuffer = lava::make_image(this->get_format()); - framebuffer->set_usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + this->framebuffer_array = lava::make_image(this->get_format()); + this->framebuffer_array->set_layer_count(this->framebuffers.size()); + this->framebuffer_array->set_view_type(VK_IMAGE_VIEW_TYPE_2D_ARRAY); + this->framebuffer_array->set_usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); - if (!framebuffer->create(this->get_application()->get_device(), this->resolution)) + if (!this->framebuffer_array->create(this->get_application()->get_device(), this->resolution)) { return false; } - this->framebuffers[eye] = framebuffer; + for (uint32_t index = 0; index < this->framebuffers.size(); index++) + { + lava::image::ptr framebuffer = lava::make_image(this->get_format(), this->framebuffer_array->get()); + framebuffer->set_base_array_layer(index); + framebuffer->set_view_type(VK_IMAGE_VIEW_TYPE_2D); + + if (!framebuffer->create(this->get_application()->get_device(), this->resolution)) + { + return false; + } + + this->framebuffers[index] = framebuffer; + } return true; } -void OpenVRHeadset::destroy_framebuffer(Eye eye) +void OpenVRHeadset::destroy_framebuffer() { - this->framebuffers[eye]->destroy(); - this->framebuffers[eye] = nullptr; + for (lava::image::ptr& framebuffer : this->framebuffers) + { + if (framebuffer != nullptr) + { + framebuffer->destroy(true); + framebuffer = nullptr; + } + } + + if (this->framebuffer_array != nullptr) + { + this->framebuffer_array->destroy(); + this->framebuffer_array = nullptr; + } } void OpenVRHeadset::compute_matrices(Eye eye) diff --git a/src/headset/openvr_headset.hpp b/src/headset/openvr_headset.hpp index 9b06fe7513f13f2f971045af6c3c226b202ae59d..cc2580113802048010aee851f3ab9a2a3affb56d 100644 --- a/src/headset/openvr_headset.hpp +++ b/src/headset/openvr_headset.hpp @@ -22,6 +22,7 @@ public: bool on_create() override; void on_destroy() override; + void on_shutdown() override; bool on_interface() override; bool on_update(lava::delta delta_time) override; @@ -36,14 +37,15 @@ public: const glm::mat4& get_controller_matrix(Controller controller) const override; lava::image::ptr get_framebuffer(FrameId frame_id) const override; + lava::image::ptr get_framebuffer_array() const override; const char* get_name() const override; bool is_attached(Controller controller) const override; private: - bool create_framebuffer(Eye eye); - void destroy_framebuffer(Eye eye); + bool create_framebuffer(); + void destroy_framebuffer(); void compute_matrices(Eye eye); @@ -58,6 +60,7 @@ private: std::array<glm::mat4, 2> projection_matrices; std::array<glm::mat4, 2> controller_matrices; std::array<lava::image::ptr, 2> framebuffers; + lava::image::ptr framebuffer_array; float near_plane = 0.1f; float far_plane = 1000.0f; diff --git a/src/headset/openxr_headset.cpp b/src/headset/openxr_headset.cpp index ff4e8af6674f6606755cc4131c49da4cd55aec03..0b94906b457a395def2490096f4b8da7d903d6ed 100644 --- a/src/headset/openxr_headset.cpp +++ b/src/headset/openxr_headset.cpp @@ -129,6 +129,7 @@ bool OpenXRHeadset::on_setup_instance(lava::frame_config& config) } this->instance_extensions = OpenXRHeadset::split_string(extension_string); + this->instance_extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); //NOTE: For some reason this extension is not added for (const std::string& extension : this->instance_extensions) { @@ -273,6 +274,10 @@ void OpenXRHeadset::on_destroy() xrDestroySpace(this->space); xrDestroySession(this->session); +} + +void OpenXRHeadset::on_shutdown() +{ xrDestroyInstance(this->instance); } @@ -325,7 +330,7 @@ void OpenXRHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f { VkImageMemoryBarrier frame_barrier = lava::image_memory_barrier(frame_image->get(), frame_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); frame_barrier.srcAccessMask = 0; - frame_barrier.dstAccessMask = 0; + frame_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; frame_barrier.subresourceRange = frame_image->get_subresource_range(); image_barriers.push_back(frame_barrier); @@ -333,7 +338,7 @@ void OpenXRHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f VkImageMemoryBarrier swapchain_begin_barrier = lava::image_memory_barrier(swapchain_image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); swapchain_begin_barrier.srcAccessMask = 0; - swapchain_begin_barrier.dstAccessMask = 0; + swapchain_begin_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; swapchain_begin_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; swapchain_begin_barrier.subresourceRange.baseMipLevel = 0; swapchain_begin_barrier.subresourceRange.levelCount = 1; @@ -342,13 +347,10 @@ void OpenXRHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f image_barriers.push_back(swapchain_begin_barrier); - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, image_barriers.size(), image_barriers.data()); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, image_barriers.size(), image_barriers.data()); VkImageCopy copy_region; - copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy_region.srcSubresource.mipLevel = 0; - copy_region.srcSubresource.baseArrayLayer = 0; - copy_region.srcSubresource.layerCount = 1; + copy_region.srcSubresource = frame_image->get_subresource_layers(), copy_region.srcOffset.x = 0; copy_region.srcOffset.y = 0; copy_region.srcOffset.z = 0; @@ -365,8 +367,9 @@ void OpenXRHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f vkCmdCopyImage(command_buffer, frame_image->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swapchain_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); - VkImageMemoryBarrier swapchain_end_barrier = lava::image_memory_barrier(swapchain_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - swapchain_end_barrier.srcAccessMask = 0; + //NOTE: For some reason the image layout has to be VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL even though the OpenXR specification says that it has to be VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + VkImageMemoryBarrier swapchain_end_barrier = lava::image_memory_barrier(swapchain_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + swapchain_end_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; swapchain_end_barrier.dstAccessMask = 0; swapchain_end_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; swapchain_end_barrier.subresourceRange.baseMipLevel = 0; @@ -374,7 +377,7 @@ void OpenXRHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f swapchain_end_barrier.subresourceRange.baseArrayLayer = 0; swapchain_end_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &swapchain_end_barrier); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &swapchain_end_barrier); this->release_image(frame_id); @@ -422,6 +425,11 @@ lava::image::ptr OpenXRHeadset::get_framebuffer(FrameId frame_id) const return this->framebuffers[frame_id]; } +lava::image::ptr OpenXRHeadset::get_framebuffer_array() const +{ + return this->framebuffer_array; +} + const char* OpenXRHeadset::get_name() const { return "OpenXR Headset"; @@ -434,10 +442,23 @@ bool OpenXRHeadset::is_attached(Controller controller) const bool OpenXRHeadset::create_framebuffers() { + this->framebuffer_array = lava::make_image(this->get_format()); + this->framebuffer_array->set_layer_count(this->framebuffers.size()); + this->framebuffer_array->set_view_type(VK_IMAGE_VIEW_TYPE_2D_ARRAY); + this->framebuffer_array->set_usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + + if (!this->framebuffer_array->create(this->get_application()->get_device(), this->resolution)) + { + lava::log()->error("OpenXR: Can't create framebuffer array!"); + + return false; + } + for (uint32_t index = 0; index < this->framebuffers.size(); index++) { - lava::image::ptr framebuffer = lava::make_image(this->get_format()); - framebuffer->set_usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + lava::image::ptr framebuffer = lava::make_image(this->get_format(), this->framebuffer_array->get()); + framebuffer->set_base_array_layer(index); + framebuffer->set_view_type(VK_IMAGE_VIEW_TYPE_2D); if (!framebuffer->create(this->get_application()->get_device(), this->resolution)) { @@ -454,9 +475,19 @@ bool OpenXRHeadset::create_framebuffers() void OpenXRHeadset::destroy_framebuffers() { - for (uint32_t index = 0; index < this->framebuffers.size(); index++) + for (lava::image::ptr& framebuffer : this->framebuffers) + { + if (framebuffer != nullptr) + { + framebuffer->destroy(true); + framebuffer = nullptr; + } + } + + if (this->framebuffer_array != nullptr) { - this->framebuffers[index]->destroy(); + this->framebuffer_array->destroy(); + this->framebuffer_array = nullptr; } } @@ -729,8 +760,7 @@ bool OpenXRHeadset::update_views() XrQuaternionf quaternion = view.pose.orientation; XrVector3f position = view.pose.position; - glm::mat4 pose_transform = (glm::mat4)glm::inverse(glm::quat(quaternion.w, quaternion.x, quaternion.y, quaternion.z)); - pose_transform[3] = glm::vec4(-position.x, -position.y, -position.z, 1.0f); + glm::mat4 pose_transform = (glm::mat4) glm::inverse(glm::quat(quaternion.w, quaternion.x, quaternion.y, quaternion.z)) * glm::translate(glm::mat4(1.0f), glm::vec3(-position.x, -position.y, -position.z)); this->head_to_eye_matrices[index] = glm::diagonal4x4(glm::vec4(1.0f, -1.0f, 1.0f, 1.0f)) * pose_transform; diff --git a/src/headset/openxr_headset.hpp b/src/headset/openxr_headset.hpp index 0ac0836cd96cbaa3bc05e567b6dce03dfb14a777..659a4cf40c852411cc68871fc6ebbfa2a570736b 100644 --- a/src/headset/openxr_headset.hpp +++ b/src/headset/openxr_headset.hpp @@ -22,6 +22,7 @@ public: bool on_create() override; void on_destroy() override; + void on_shutdown() override; bool on_interface() override; bool on_update(lava::delta delta_time) override; @@ -36,6 +37,7 @@ public: const glm::mat4& get_controller_matrix(Controller controller) const override; lava::image::ptr get_framebuffer(FrameId frame_id) const override; + lava::image::ptr get_framebuffer_array() const override; const char* get_name() const override; @@ -78,6 +80,7 @@ private: std::array<uint32_t, 2> swapchain_indices; std::array<std::vector<VkImage>, 2> swapchain_images; std::array<lava::image::ptr, 2> framebuffers; + lava::image::ptr framebuffer_array; std::vector<std::string> instance_extensions; std::vector<std::string> device_extensions; diff --git a/src/headset/remote_headset.cpp b/src/headset/remote_headset.cpp index 158f076b7ff6850f07899dd9d3cce5aa5eb6965f..f6a04ea23c7ccedfeb7a6c1536722875958d873c 100644 --- a/src/headset/remote_headset.cpp +++ b/src/headset/remote_headset.cpp @@ -296,6 +296,11 @@ lava::image::ptr RemoteHeadset::get_framebuffer(FrameId frame_id) const return this->framebuffers[frame_id]; } +lava::image::ptr RemoteHeadset::get_framebuffer_array() const +{ + return this->framebuffer_array; +} + const char* RemoteHeadset::get_name() const { return "Remote Headset"; @@ -365,32 +370,49 @@ bool RemoteHeadset::create_transport() bool RemoteHeadset::create_framebuffers() { - this->framebuffers.resize(this->frame_id_count); + this->framebuffers.resize(this->frame_id_count); - for (uint32_t index = 0; index < this->framebuffers.size(); index++) - { - lava::image::ptr framebuffer = lava::make_image(this->get_format()); - framebuffer->set_usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + this->framebuffer_array = lava::make_image(this->get_format()); + this->framebuffer_array->set_layer_count(this->framebuffers.size()); + this->framebuffer_array->set_view_type(VK_IMAGE_VIEW_TYPE_2D_ARRAY); + this->framebuffer_array->set_usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + + if (!this->framebuffer_array->create(this->get_application()->get_device(), this->resolution)) + { + return false; + } + + for (uint32_t index = 0; index < this->framebuffers.size(); index++) + { + lava::image::ptr framebuffer = lava::make_image(this->get_format(), this->framebuffer_array->get()); + framebuffer->set_base_array_layer(index); + framebuffer->set_view_type(VK_IMAGE_VIEW_TYPE_2D); if (!framebuffer->create(this->get_application()->get_device(), this->resolution)) - { + { return false; } this->framebuffers[index] = framebuffer; - } - + } + return true; } void RemoteHeadset::destroy_framebuffers() { - for (uint32_t index = 0; index < this->framebuffers.size(); index++) - { - this->framebuffers[index]->destroy(); - } + for (lava::image::ptr& framebuffer : this->framebuffers) + { + framebuffer->destroy(true); + } + + this->framebuffers.clear(); - this->framebuffers.clear(); + if (this->framebuffer_array != nullptr) + { + this->framebuffer_array->destroy(); + this->framebuffer_array = nullptr; + } } bool RemoteHeadset::create_encoders() diff --git a/src/headset/remote_headset.hpp b/src/headset/remote_headset.hpp index 14ef4498787e25cf8dcf48a20eab48b3d87d70d8..7c4cd5190cf6d368a22007e0589c7c27bae14ca3 100644 --- a/src/headset/remote_headset.hpp +++ b/src/headset/remote_headset.hpp @@ -43,6 +43,7 @@ public: const glm::mat4& get_controller_matrix(Controller controller) const override; lava::image::ptr get_framebuffer(FrameId frame_id) const override; + lava::image::ptr get_framebuffer_array() const override; const char* get_name() const override; @@ -106,6 +107,7 @@ private: uint32_t frame_id_count = 0; uint32_t transform_id = 0; //NOTE: Protected by transport_mutex + lava::image::ptr framebuffer_array; std::vector<lava::image::ptr> framebuffers; std::vector<Encoder::Ptr> encoders; uint32_t encoder_mode = ENCODER_MODE_CONSTANT_QUALITY; diff --git a/src/scene.cpp b/src/scene.cpp index aae07fc9e6018383978693c46e5e597b222db877..099625ecbc2cff7f3a25ee77ebdc5878b14dbdc3 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -73,7 +73,7 @@ bool Scene::create(const SceneConfig& config, lava::device_ptr device, lava::sta return false; } - if (!this->create_material_descriptors(device)) + if (!this->create_material_buffer(device)) { return false; } @@ -160,6 +160,7 @@ bool Scene::interface() } ImGui::Checkbox("Animation Active", &this->animation_active); + ImGui::Checkbox("Camera Active", &this->camera_active); ImGui::Checkbox("Animation Loop", &this->animation_loop); ImGui::SliderFloat("Animation Time", &this->animation_time, 0.0f, active_animation.duration); ImGui::DragFloat("Playback Speed", &this->animation_playback_speed, 1.0f, -100.0f, 100.0); @@ -428,6 +429,11 @@ bool Scene::is_animation_active() const return this->animation_active; } +bool Scene::is_camera_active() const +{ + return this->camera_active && !this->cameras.empty(); +} + lava::descriptor::ptr Scene::get_mesh_descriptor() const { return this->mesh_descriptor; @@ -478,6 +484,16 @@ const std::vector<SceneAnimation>& Scene::get_animations() const return this->animations; } +const glm::vec3& Scene::get_scene_min() const +{ + return this->scene_min; +} + +const glm::vec3& Scene::get_scene_max() const +{ + return this->scene_max; +} + void Scene::set_vertex_input(lava::graphics_pipeline* pipeline) { pipeline->set_vertex_input_binding({ 0, sizeof(lava::vertex), VK_VERTEX_INPUT_RATE_VERTEX }); @@ -523,7 +539,8 @@ void Scene::create_default_light() light_data.outer_angle = 0.0f; light_data.direction = default_light.initial_direction; light_data.inner_angle = 0.0f; - light_data.padding = glm::vec3(0.0f); + light_data.near_size = glm::vec2(0.0f); + light_data.padding = 0; switch (light_data.type) { @@ -588,6 +605,7 @@ bool Scene::create_descriptor_layouts(lava::device_ptr device, uint32_t frame_co this->material_descriptor->add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT); this->material_descriptor->add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT); this->material_descriptor->add_binding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT); + this->material_descriptor->add_binding(4, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT); if (!this->material_descriptor->create(device)) { @@ -599,13 +617,34 @@ bool Scene::create_descriptor_layouts(lava::device_ptr device, uint32_t frame_co return true; } -bool Scene::create_material_descriptors(lava::device_ptr device) +bool Scene::create_material_buffer(lava::device_ptr device) { + std::vector<glsl::MaterialData> material_data; + material_data.reserve(this->materials.size()); + + for (const SceneMaterial& material : this->materials) + { + material_data.push_back(material.material_data); + } + + this->material_data_buffer = lava::make_buffer(); + + if (!this->material_data_buffer->create_mapped(device, material_data.data(), sizeof(glsl::MaterialData) * material_data.size(), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)) + { + lava::log()->error("Can't create material data buffer!"); + + return false; + } + std::vector<VkWriteDescriptorSet> descriptor_writes; - descriptor_writes.reserve(this->materials.size() * 4); + descriptor_writes.reserve(this->materials.size() * 5); + + std::vector<VkDescriptorBufferInfo> buffer_infos; + buffer_infos.reserve(this->materials.size()); - for (SceneMaterial& material : this->materials) + for (uint32_t index = 0; index < this->materials.size(); index++) { + SceneMaterial& material = this->materials[index]; material.descriptor_set = this->material_descriptor->allocate_set(this->descriptor_pool->get()); std::vector<lava::texture::ptr> textures; @@ -616,20 +655,35 @@ bool Scene::create_material_descriptors(lava::device_ptr device) for (uint32_t binding = 0; binding < textures.size(); binding++) { - VkWriteDescriptorSet descriptor_write; - descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptor_write.pNext = nullptr; - descriptor_write.dstSet = material.descriptor_set; - descriptor_write.dstBinding = binding; - descriptor_write.dstArrayElement = 0; - descriptor_write.descriptorCount = 1; - descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptor_write.pImageInfo = textures[binding]->get_descriptor_info(); - descriptor_write.pBufferInfo = nullptr; - descriptor_write.pTexelBufferView = nullptr; - - descriptor_writes.push_back(descriptor_write); + VkWriteDescriptorSet& texture_write = descriptor_writes.emplace_back(); + texture_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + texture_write.pNext = nullptr; + texture_write.dstSet = material.descriptor_set; + texture_write.dstBinding = binding; + texture_write.dstArrayElement = 0; + texture_write.descriptorCount = 1; + texture_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + texture_write.pImageInfo = textures[binding]->get_descriptor_info(); + texture_write.pBufferInfo = nullptr; + texture_write.pTexelBufferView = nullptr; } + + VkDescriptorBufferInfo& buffer_info = buffer_infos.emplace_back(); + buffer_info.buffer = this->material_data_buffer->get(); + buffer_info.offset = sizeof(glsl::MaterialData) * index; + buffer_info.range = sizeof(glsl::MaterialData); + + VkWriteDescriptorSet& buffer_write = descriptor_writes.emplace_back(); + buffer_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + buffer_write.pNext = nullptr; + buffer_write.dstSet = material.descriptor_set; + buffer_write.dstBinding = 4; + buffer_write.dstArrayElement = 0; + buffer_write.descriptorCount = 1; + buffer_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + buffer_write.pImageInfo = nullptr; + buffer_write.pBufferInfo = &buffer_info; + buffer_write.pTexelBufferView = nullptr; } vkUpdateDescriptorSets(device->get(), descriptor_writes.size(), descriptor_writes.data(), 0, nullptr); @@ -641,7 +695,7 @@ bool Scene::create_light_buffer(lava::device_ptr device, uint32_t frame_count) { glsl::LightParameter light_parameter; light_parameter.ambient = this->ambient_color; - light_parameter.count = this->lights.size(); + light_parameter.light_count = this->lights.size(); light_parameter.exposure = this->exposure; this->light_parameter_buffer = lava::make_buffer(); @@ -811,7 +865,7 @@ bool Scene::load_scene(const std::string& file_name, SceneUnit unit, SceneOrient return false; } - if (!this->load_meshes(scene, scale, 0, device)) + if (!this->load_meshes(scene, scale, 0, true, device)) { return false; } @@ -877,7 +931,7 @@ bool Scene::load_controller(const std::string& file_name, SceneUnit unit, SceneO return false; } - if (!this->load_meshes(scene, scale, base_material, device)) + if (!this->load_meshes(scene, scale, base_material, false, device)) { return false; } @@ -961,6 +1015,18 @@ bool Scene::load_sky_sphere(const std::string& file_name, uint32_t ring_count, u sky_material.normal = this->dummy_normal_texture; sky_material.emissive = this->sky_emissive_texture; + glsl::MaterialData& sky_material_data = sky_material.material_data; + sky_material_data.base_color = glm::vec3(1.0f); + sky_material_data.opacity = 1.0f; + sky_material_data.emissive = glm::vec3(1.0f); + sky_material_data.roughness = 1.0f; + sky_material_data.padding1 = glm::uvec3(0); + sky_material_data.metallic = 1.0f; + sky_material_data.padding2 = glm::uvec4(0); + sky_material_data.padding3 = glm::mat4(0.0f); + sky_material_data.padding4 = glm::mat4(0.0f); + sky_material_data.padding5 = glm::mat4(0.0f); + if (ring_count < 3) { lava::log()->error("Invalid number of rings for sky-sphere. The number of rings needs to be at least 3!"); @@ -1043,9 +1109,10 @@ bool Scene::load_sky_sphere(const std::string& file_name, uint32_t ring_count, u sky_mesh.node_index = INVALID_NODE; sky_mesh.visible = true; sky_mesh.cast_shadow = false; + sky_mesh.scene_bounds = false; sky_mesh.material_index = sky_material_index; - sky_mesh.local_min = center; //NOTE: The sky-sphere should not influence the scene bounds - sky_mesh.local_max = center; + sky_mesh.local_min = center - glm::vec3(radius); + sky_mesh.local_max = center + glm::vec3(radius); sky_mesh.mesh = lava::make_mesh(); sky_mesh.mesh->add_data(geometry); @@ -1178,6 +1245,15 @@ bool Scene::load_lights(const aiScene* scene, float scale, const std::map<std::s return false; } + float light_scale = 1.0; + + if (light->mType == aiLightSource_DIRECTIONAL) + { + //NOTE: If the scene is in centimeters assume that directional light intensity is given as watt / cm^2. + // In this case the intensity has to be converted into watt / meter^2 + light_scale = glm::pow(scale, 1); //It should be a 2 instead of a 1 + } + SceneLight& scene_light = this->lights.emplace_back(); scene_light.node_index = node_indices.at(light->mName.C_Str()); scene_light.initial_position = glm::make_vec3(&light->mPosition.x) * scale; @@ -1185,11 +1261,12 @@ bool Scene::load_lights(const aiScene* scene, float scale, const std::map<std::s glsl::LightData& light_data = scene_light.data; light_data.position = scene_light.initial_position; - light_data.color = glm::make_vec3(&light->mColorDiffuse.r); + light_data.color = glm::make_vec3(&light->mColorDiffuse.r) * light_scale; light_data.outer_angle = light->mAngleOuterCone; light_data.direction = scene_light.initial_direction; light_data.inner_angle = light->mAngleInnerCone; - light_data.padding = glm::vec3(0.0f); + light_data.near_size = glm::vec2(0.0f); + light_data.padding = 0; switch (light->mType) { @@ -1213,7 +1290,7 @@ bool Scene::load_lights(const aiScene* scene, float scale, const std::map<std::s return true; } -bool Scene::load_meshes(const aiScene* scene, float scale, uint32_t base_material, lava::device_ptr device) +bool Scene::load_meshes(const aiScene* scene, float scale, uint32_t base_material, bool scene_bounds, lava::device_ptr device) { for (uint32_t index = 0; index < scene->mNumMeshes; index++) { @@ -1263,6 +1340,7 @@ bool Scene::load_meshes(const aiScene* scene, float scale, uint32_t base_materia } SceneMesh& scene_mesh = this->meshes.emplace_back(); + scene_mesh.scene_bounds = scene_bounds; scene_mesh.material_index = base_material + mesh->mMaterialIndex; scene_mesh.local_min = glm::make_vec3(&mesh->mAABB.mMin.x) * scale; scene_mesh.local_max = glm::make_vec3(&mesh->mAABB.mMax.x) * scale; @@ -1288,14 +1366,47 @@ bool Scene::load_materials(const aiScene* scene, const std::string& base_name, l SceneMaterial& scene_material = this->materials.emplace_back(); scene_material.emissive = this->dummy_emissive_texture; + aiColor3D diffuse_color = aiColor3D(0.0f, 0.0f, 0.0f); + material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse_color); + + aiColor3D emissive_color = aiColor3D(0.0f, 0.0f, 0.0f); + material->Get(AI_MATKEY_COLOR_EMISSIVE, emissive_color); + + float opacity = 1.0f; + material->Get(AI_MATKEY_OPACITY, opacity); + + float shininess = 0.0f; + material->Get(AI_MATKEY_SHININESS, shininess); + + float reflectivity = 0.0f; + material->Get(AI_MATKEY_REFLECTIVITY, reflectivity); + + glsl::MaterialData& material_data = scene_material.material_data; + material_data.base_color = glm::vec3(1.0f); + material_data.opacity = 1.0f; + material_data.emissive = glm::make_vec3(&emissive_color.r); + material_data.roughness = 1.0f; + material_data.padding1 = glm::uvec3(0); + material_data.metallic = 0.0f; + material_data.padding2 = glm::uvec4(0); + material_data.padding3 = glm::mat4(0); + material_data.padding4 = glm::mat4(0); + material_data.padding5 = glm::mat4(0); + if (!this->load_texture(material, base_name, aiTextureType_DIFFUSE, device, staging, scene_material.diffuse)) { scene_material.diffuse = this->dummy_diffuse_texture; + + material_data.base_color = glm::make_vec3(&diffuse_color.r); + material_data.opacity = glm::clamp(opacity, 0.0f, 1.0f); } if (!this->load_texture(material, base_name, aiTextureType_SPECULAR, device, staging, scene_material.specular)) { scene_material.specular = this->dummy_specular_texture; + + material_data.roughness = glm::clamp(1.0f - (glm::sqrt(shininess) / 10.0f), 0.0f, 1.0f); + material_data.metallic = glm::clamp(reflectivity, 0.0f, 1.0f); } if (!this->load_texture(material, base_name, aiTextureType_NORMALS, device, staging, scene_material.normal)) @@ -1356,11 +1467,29 @@ void Scene::compute_scene_bounds() { if (!this->meshes.empty()) { - this->scene_min = this->meshes.front().global_min; - this->scene_max = this->meshes.front().global_max; + this->scene_min = glm::vec3(0.0f); + this->scene_max = glm::vec3(0.0f); for (const SceneMesh& mesh : this->meshes) { + if (!mesh.scene_bounds) + { + continue; + } + + this->scene_min = mesh.global_min; + this->scene_max = mesh.global_max; + + break; + } + + for (const SceneMesh& mesh : this->meshes) + { + if (!mesh.scene_bounds) + { + continue; + } + this->scene_min = glm::min(mesh.global_min, this->scene_min); this->scene_max = glm::max(mesh.global_max, this->scene_max); } @@ -1412,7 +1541,7 @@ void Scene::compute_light_matrices(SceneLight& light) { glsl::LightData& light_data = light.data; - for (uint32_t index = 0; index < LIGHT_MAX_SHADOW_MATRIX_COUNT; index++) + for (uint32_t index = 0; index < MAX_LIGHT_SHADOW_MATRIX_COUNT; index++) { light_data.shadow_matrix[index] = glm::mat4(1.0f); } @@ -1447,22 +1576,24 @@ void Scene::compute_light_matrices(SceneLight& light) for (uint32_t index = 0; index < directions.size(); index++) { glm::mat4 view_matrix = glm::lookAt(light_data.position, light_data.position + directions[index], sides[index]); + glm::mat4 shadow_matrix = projection_matrix * view_matrix; - light_data.shadow_matrix[index] = projection_matrix * view_matrix; + light_data.shadow_matrix[index] = shadow_matrix; + light_data.inv_shadow_matrix[index] = glm::inverse(shadow_matrix); } } else if (light_data.type == LIGHT_TYPE_DIRECTIONAL) { glm::vec3 direction = glm::normalize(light_data.direction); - glm::vec3 side = glm::vec3(1.0f, 0.0f, 0.0f); + glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); - if (glm::dot(direction, side) > 0.9) + if (glm::dot(direction, up) > 0.9) { - side = glm::vec3(0.0f, 1.0f, 0.0f); + up = glm::vec3(1.0f, 0.0f, 0.0f); } - glm::mat4 view_matrix = glm::lookAt(glm::vec3(0.0f), direction, side); + glm::mat4 view_matrix = glm::lookAt(glm::vec3(0.0f), direction, up); std::array<glm::vec3, 8> box_points = { @@ -1490,9 +1621,13 @@ void Scene::compute_light_matrices(SceneLight& light) light_max = glm::max(light_max, point); } + glm::vec2 near_size = light_max - light_min; glm::mat4 projection_matrix = glm::ortho(light_min.x, light_max.x, light_min.y, light_max.y, -light_max.z, -light_min.z); + glm::mat4 shadow_matrix = projection_matrix * view_matrix; - light_data.shadow_matrix[0] = projection_matrix * view_matrix; + light_data.near_size = near_size; + light_data.shadow_matrix[0] = shadow_matrix; + light_data.inv_shadow_matrix[0] = glm::inverse(shadow_matrix); } else if (light_data.type == LIGHT_TYPE_SPOT) @@ -1513,8 +1648,10 @@ void Scene::compute_light_matrices(SceneLight& light) glm::mat4 view_matrix = glm::lookAt(position, position + direction, side); glm::mat4 projection_matrix = glm::perspective(2.0f * angle, 1.0f, 10.0f, scene_diagonal); + glm::mat4 shadow_matrix = projection_matrix * view_matrix; - light_data.shadow_matrix[0] = projection_matrix * view_matrix; + light_data.shadow_matrix[0] = shadow_matrix; + light_data.inv_shadow_matrix[0] = glm::inverse(shadow_matrix); } } diff --git a/src/scene.hpp b/src/scene.hpp index 5f28946a6a3b98d5b1c3f0312f87e4098eeb1bd7..a53d398aab611caa1c9b0145f14754afeb304310 100644 --- a/src/scene.hpp +++ b/src/scene.hpp @@ -13,6 +13,7 @@ using namespace glm; using uint = glm::uint32; #include "res/dpr/data/mesh_data.inc" #include "res/dpr/data/light_data.inc" +#include "res/dpr/data/material_data.inc" } #define INVALID_NODE -1 @@ -79,6 +80,7 @@ struct SceneMesh uint32_t material_index = 0; bool visible = true; bool cast_shadow = true; + bool scene_bounds = true; glm::vec3 local_min = glm::vec3(0.0f); glm::vec3 local_max = glm::vec3(0.0f); glm::vec3 global_min = glm::vec3(0.0f); @@ -102,6 +104,7 @@ struct SceneMaterial lava::texture::ptr specular; lava::texture::ptr normal; lava::texture::ptr emissive; + glsl::MaterialData material_data; VkDescriptorSet descriptor_set = VK_NULL_HANDLE; }; @@ -147,6 +150,7 @@ public: const glm::mat4& get_animation_view_matrix() const; bool is_animation_complete() const; bool is_animation_active() const; + bool is_camera_active() const; lava::descriptor::ptr get_mesh_descriptor() const; lava::descriptor::ptr get_light_descriptor() const; @@ -161,6 +165,9 @@ public: const std::vector<SceneMaterial>& get_materials() const; const std::vector<SceneAnimation>& get_animations() const; + const glm::vec3& get_scene_min() const; + const glm::vec3& get_scene_max() const; + static void set_vertex_input(lava::graphics_pipeline* pipeline); static void set_vertex_input_only_position(lava::graphics_pipeline* pipeline); @@ -169,7 +176,7 @@ private: void create_default_light(); bool create_descriptor_layouts(lava::device_ptr device, uint32_t frame_count); - bool create_material_descriptors(lava::device_ptr device); + bool create_material_buffer(lava::device_ptr device); bool create_light_buffer(lava::device_ptr device, uint32_t frame_count); bool create_mesh_buffer(lava::device_ptr device, uint32_t frame_count); @@ -181,7 +188,7 @@ private: bool load_cameras(const aiScene* scene, float scale, const std::map<std::string, SceneNodeIndex>& node_indices); bool load_animations(const aiScene* scene, float scale, const std::map<std::string, SceneNodeIndex>& node_indices); bool load_lights(const aiScene* scene, float scale, const std::map<std::string, SceneNodeIndex>& node_indices); - bool load_meshes(const aiScene* scene, float scale, uint32_t base_material, lava::device_ptr device); + bool load_meshes(const aiScene* scene, float scale, uint32_t base_material, bool scene_bounds, lava::device_ptr device); bool load_materials(const aiScene* scene, const std::string& base_name, lava::device_ptr device, lava::staging& staging); bool load_texture(const aiMaterial* material, const std::string& base_name, aiTextureType type, lava::device_ptr device, lava::staging& staging, lava::texture::ptr& texture); @@ -196,7 +203,6 @@ private: static bool compute_scale(SceneUnit unit, float& scale); private: - SceneNodeIndex camera_index = 0; SceneNodeIndex controller_left = INVALID_NODE; SceneNodeIndex controller_right = INVALID_NODE; @@ -209,11 +215,14 @@ private: float animation_time = 0.0f; float animation_playback_speed = 1.0f; + int32_t camera_index = 0; + bool camera_active = true; + glm::vec3 scene_min = glm::vec3(0.0f); glm::vec3 scene_max = glm::vec3(0.0f); - const glm::vec3 ambient_color = glm::vec3(5.0f); //NOTE: This factor is used so that the Bistro scene looks okay. - float exposure = 0.1f; //NOTE: This factor is used so that the Bistro scene looks okay. + const glm::vec3 ambient_color = glm::vec3(0.0f); //NOTE: Indirect should be compute using an indirect cache. + float exposure = 1.0f; bool exposure_changed = false; uint32_t light_directional_count = 0; @@ -230,6 +239,7 @@ private: std::vector<lava::buffer::ptr> mesh_data_buffer; std::vector<lava::buffer::ptr> light_data_buffer; lava::buffer::ptr light_parameter_buffer; + lava::buffer::ptr material_data_buffer; lava::descriptor::pool::ptr descriptor_pool; lava::descriptor::ptr mesh_descriptor; diff --git a/src/strategy/depth_peeling_reprojection_stereo.cpp b/src/strategy/depth_peeling_reprojection_stereo.cpp index c1dcc2c49c3972f639745e044bcc5de2b9b13394..8e46214e8cbb3527ea6fe815ff79e0c8d897313b 100644 --- a/src/strategy/depth_peeling_reprojection_stereo.cpp +++ b/src/strategy/depth_peeling_reprojection_stereo.cpp @@ -52,22 +52,16 @@ bool DepthPeelingReprojectionStereo::on_render(VkCommandBuffer command_buffer, l { this->write_buffer(frame); - VkImageSubresourceRange const image_range{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .levelCount = 1, - .layerCount = 1, - }; - insert_image_memory_barrier(get_device(), command_buffer, this->get_headset()->get_framebuffer(EYE_LEFT)->get(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - image_range); + this->get_headset()->get_framebuffer(EYE_LEFT)->get_subresource_range()); insert_image_memory_barrier(get_device(), command_buffer, this->get_headset()->get_framebuffer(EYE_RIGHT)->get(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - image_range); + this->get_headset()->get_framebuffer(EYE_LEFT)->get_subresource_range()); std::array<uint8_t, 1> metadata = { 0x00 }; this->get_headset()->submit_metadata(metadata); @@ -314,12 +308,7 @@ void DepthPeelingReprojectionStereo::render_companion_window(VkCommandBuffer com .y = 0, .z = 0, }, - .dstSubresource = VkImageSubresourceLayers { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = 1, - }, + .dstSubresource = get_headset()->get_framebuffer(EYE_LEFT)->get_subresource_layers(), .dstOffset = VkOffset3D { .x = 0, .y = 0, @@ -379,7 +368,7 @@ void DepthPeelingReprojectionStereo::render_companion_window(VkCommandBuffer com VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - image_range); + get_headset()->get_framebuffer(EYE_LEFT)->get_subresource_range()); // insert_image_memory_barrier(app()->device(), command_buffer, app()->right_eye_framebuffer()->color_image->get(), // VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT, // VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, diff --git a/src/strategy/multi_view_stereo.cpp b/src/strategy/multi_view_stereo.cpp index 690cc2142971a4774bdc63ed4bd759fcee6c01ab..47ba1a35b44a5e8f1cfff7aeece646db205c9600 100644 --- a/src/strategy/multi_view_stereo.cpp +++ b/src/strategy/multi_view_stereo.cpp @@ -21,13 +21,6 @@ bool MultiViewStereo::on_setup_device(lava::device::create_param& parameters) 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; @@ -48,11 +41,6 @@ bool MultiViewStereo::on_create() void MultiViewStereo::on_destroy() { - if (this->shadow_cache != nullptr) - { - this->shadow_cache->destroy(); - } - if (this->pipeline_layout != nullptr) { this->pipeline_layout->destroy(); @@ -68,11 +56,6 @@ void MultiViewStereo::on_destroy() this->render_pass->destroy(); } - if (this->color_buffer != nullptr) - { - this->color_buffer->destroy(); - } - if (this->depth_buffer != nullptr) { this->depth_buffer->destroy(); @@ -95,117 +78,20 @@ bool MultiViewStereo::on_render(VkCommandBuffer command_buffer, lava::index fram this->get_headset()->submit_metadata(metadata); this->get_pass_timer()->begin_pass(command_buffer, "shadow"); - this->shadow_cache->compute_shadow(command_buffer, frame); + this->get_application()->get_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> begin_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, begin_barriers.size(), begin_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); - - left_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - left_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - left_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - left_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - - right_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - right_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - right_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - right_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - - std::array<VkImageMemoryBarrier, 2> end_barriers = - { - left_barrier, - right_barrier - }; - - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, end_barriers.size(), end_barriers.data()); - - this->get_frame_capture()->capture_image(command_buffer, this->get_headset()->get_framebuffer(EYE_LEFT), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, "left_eye"); - this->get_companion_window()->submit_image(command_buffer, EYE_LEFT, this->get_headset()->get_framebuffer(EYE_LEFT), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - this->get_headset()->submit_frame(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, EYE_LEFT); + this->get_frame_capture()->capture_image(command_buffer, this->get_headset()->get_framebuffer(EYE_LEFT), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, "left_eye"); + this->get_companion_window()->submit_image(command_buffer, EYE_LEFT, this->get_headset()->get_framebuffer(EYE_LEFT), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + this->get_headset()->submit_frame(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, EYE_LEFT); - this->get_frame_capture()->capture_image(command_buffer, this->get_headset()->get_framebuffer(EYE_RIGHT), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, "right_eye"); - this->get_companion_window()->submit_image(command_buffer, EYE_RIGHT, this->get_headset()->get_framebuffer(EYE_RIGHT), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - this->get_headset()->submit_frame(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, EYE_RIGHT); + this->get_frame_capture()->capture_image(command_buffer, this->get_headset()->get_framebuffer(EYE_RIGHT), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, "right_eye"); + this->get_companion_window()->submit_image(command_buffer, EYE_RIGHT, this->get_headset()->get_framebuffer(EYE_RIGHT), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + this->get_headset()->submit_frame(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, EYE_RIGHT); return true; } @@ -228,7 +114,8 @@ bool MultiViewStereo::create_pipeline_layout() 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()); + this->pipeline_layout->add(this->get_application()->get_shadow_cache()->get_descriptor()); + this->pipeline_layout->add(this->get_application()->get_indirect_cache()->get_descriptor()); if (!this->pipeline_layout->create(this->get_device())) { @@ -255,19 +142,6 @@ bool MultiViewStereo::create_render_pass() 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); @@ -276,7 +150,7 @@ bool MultiViewStereo::create_render_pass() 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); + this->color_attachment->set_layouts(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); VkClearValue depth_clear_value; depth_clear_value.depthStencil.depth = 1.0f; @@ -326,7 +200,7 @@ bool MultiViewStereo::create_render_pass() lava::VkImageViews framebuffer_views = { this->depth_buffer->get_view(), - this->color_buffer->get_view() + this->get_headset()->get_framebuffer_array()->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. @@ -411,7 +285,8 @@ void MultiViewStereo::pipeline_function(VkCommandBuffer command_buffer) 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); + this->pipeline_layout->bind(command_buffer, this->get_application()->get_shadow_cache()->get_descriptor_set(), 5); + this->pipeline_layout->bind(command_buffer, this->get_application()->get_indirect_cache()->get_descriptor_set(), 6); const std::vector<SceneMaterial>& materials = this->get_scene()->get_materials(); diff --git a/src/strategy/multi_view_stereo.hpp b/src/strategy/multi_view_stereo.hpp index 895a6fb387f1baa0a180969b0b2b5b0d2c4682ca..6c91be3a903afa644dcee3956a70fad3fcc5e3d6 100644 --- a/src/strategy/multi_view_stereo.hpp +++ b/src/strategy/multi_view_stereo.hpp @@ -3,7 +3,6 @@ #include <array> #include "stereo_strategy.hpp" -#include "utility/shadow_cache.hpp" class MultiViewStereo final : public StereoStrategy { @@ -36,15 +35,13 @@ private: 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::attachment::ptr depth_attachment; lava::image::ptr depth_buffer; }; diff --git a/src/strategy/native_stereo_deferred.cpp b/src/strategy/native_stereo_deferred.cpp index 87d65e20db0fd7cc093d026e1122c9b675ac2d39..37be5aa60c0d1ea2c4b61a8f2722a1500250b041 100644 --- a/src/strategy/native_stereo_deferred.cpp +++ b/src/strategy/native_stereo_deferred.cpp @@ -3,13 +3,6 @@ bool NativeStereoDeferred::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; @@ -40,11 +33,6 @@ bool NativeStereoDeferred::on_create() void NativeStereoDeferred::on_destroy() { - if (this->shadow_cache != nullptr) - { - this->shadow_cache->destroy(); - } - if (this->pipeline_layout != nullptr) { this->pipeline_layout->destroy(); @@ -85,7 +73,7 @@ bool NativeStereoDeferred::on_render(VkCommandBuffer command_buffer, lava::index this->get_headset()->submit_metadata(metadata); this->get_pass_timer()->begin_pass(command_buffer, "shadow"); - this->shadow_cache->compute_shadow(command_buffer, frame); + this->get_application()->get_shadow_cache()->compute_shadow(command_buffer, frame); this->get_pass_timer()->end_pass(command_buffer); this->get_pass_timer()->begin_pass(command_buffer, "left_eye"); @@ -93,7 +81,7 @@ bool NativeStereoDeferred::on_render(VkCommandBuffer command_buffer, lava::index this->get_pass_timer()->end_pass(command_buffer); this->get_pass_timer()->begin_pass(command_buffer, "left_eye_lighting"); - this->eye_passes[0].geometry_buffer->apply_local_lighting(command_buffer, frame, this->get_stereo_transform()->get_descriptor_set(EYE_LEFT, get_application()->get_frame_index())); + this->eye_passes[0].geometry_buffer->apply_lighting(command_buffer, frame, this->get_stereo_transform()->get_descriptor_set(EYE_LEFT, get_application()->get_frame_index())); this->get_pass_timer()->end_pass(command_buffer); this->get_frame_capture()->capture_image(command_buffer, this->get_headset()->get_framebuffer(EYE_LEFT), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, "left_eye"); @@ -105,7 +93,7 @@ bool NativeStereoDeferred::on_render(VkCommandBuffer command_buffer, lava::index this->get_pass_timer()->end_pass(command_buffer); this->get_pass_timer()->begin_pass(command_buffer, "right_eye_lighting"); - this->eye_passes[1].geometry_buffer->apply_local_lighting(command_buffer, frame, this->get_stereo_transform()->get_descriptor_set(EYE_RIGHT, get_application()->get_frame_index())); + this->eye_passes[1].geometry_buffer->apply_lighting(command_buffer, frame, this->get_stereo_transform()->get_descriptor_set(EYE_RIGHT, get_application()->get_frame_index())); this->get_pass_timer()->end_pass(command_buffer); this->get_frame_capture()->capture_image(command_buffer, this->get_headset()->get_framebuffer(EYE_RIGHT), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, "right_eye"); @@ -146,7 +134,7 @@ bool NativeStereoDeferred::create_render_pass(Eye eye) { GeometryBuffer::Ptr geometry_buffer = make_geometry_buffer(); - if (!geometry_buffer->create(this->get_headset()->get_framebuffer(eye), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, this->get_stereo_transform()->get_descriptor(), this->get_scene(), this->shadow_cache)) + if (!geometry_buffer->create(this->get_headset()->get_framebuffer(eye), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, this->get_stereo_transform()->get_descriptor(), this->get_scene(), this->get_application()->get_shadow_cache(), this->get_application()->get_indirect_cache())) { lava::log()->error("Can't create geometry buffer for native stereo deferred!"); diff --git a/src/strategy/native_stereo_deferred.hpp b/src/strategy/native_stereo_deferred.hpp index 0bb1bc86f2e5c94613b8536910a73a7f772398f6..0c5c48c22d5014f138ad599a064625da822180f5 100644 --- a/src/strategy/native_stereo_deferred.hpp +++ b/src/strategy/native_stereo_deferred.hpp @@ -4,7 +4,6 @@ #include "stereo_strategy.hpp" #include "utility/geometry_buffer.hpp" -#include "utility/shadow_cache.hpp" struct NativeStereoDeferredPass { @@ -39,7 +38,6 @@ private: void pipeline_function(VkCommandBuffer command_buffer, Eye eye); private: - ShadowCache::Ptr shadow_cache; lava::pipeline_layout::ptr pipeline_layout; std::array<NativeStereoDeferredPass, 2> eye_passes; }; diff --git a/src/strategy/native_stereo_forward.cpp b/src/strategy/native_stereo_forward.cpp index dfe86df34f8e25b97aa4b5fceddf824297e44bde..bbe14e4ed302bf212671ec3bbfea836ac518e82b 100644 --- a/src/strategy/native_stereo_forward.cpp +++ b/src/strategy/native_stereo_forward.cpp @@ -3,13 +3,6 @@ bool NativeStereoForward::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; @@ -40,11 +33,6 @@ bool NativeStereoForward::on_create() void NativeStereoForward::on_destroy() { - if (this->shadow_cache != nullptr) - { - this->shadow_cache->destroy(); - } - if (this->pipeline_layout != nullptr) { this->pipeline_layout->destroy(); @@ -85,7 +73,7 @@ bool NativeStereoForward::on_render(VkCommandBuffer command_buffer, lava::index this->get_headset()->submit_metadata(metadata); this->get_pass_timer()->begin_pass(command_buffer, "shadow"); - this->shadow_cache->compute_shadow(command_buffer, frame); + this->get_application()->get_shadow_cache()->compute_shadow(command_buffer, frame); this->get_pass_timer()->end_pass(command_buffer); this->get_pass_timer()->begin_pass(command_buffer, "left_eye"); @@ -124,7 +112,8 @@ bool NativeStereoForward::create_pipeline_layout() this->pipeline_layout->add(this->get_scene()->get_material_descriptor()); this->pipeline_layout->add(this->get_scene()->get_mesh_descriptor()); this->pipeline_layout->add(this->get_scene()->get_light_descriptor()); - this->pipeline_layout->add(this->shadow_cache->get_descriptor()); + this->pipeline_layout->add(this->get_application()->get_shadow_cache()->get_descriptor()); + this->pipeline_layout->add(this->get_application()->get_indirect_cache()->get_descriptor()); if (!this->pipeline_layout->create(this->get_device())) { @@ -285,7 +274,8 @@ void NativeStereoForward::pipeline_function(VkCommandBuffer command_buffer, Eye this->pipeline_layout->bind(command_buffer, this->get_stereo_transform()->get_descriptor_set(eye, frame_index), 0); this->pipeline_layout->bind(command_buffer, this->get_scene()->get_light_descriptor_set(frame_index), 3); - this->pipeline_layout->bind(command_buffer, this->shadow_cache->get_descriptor_set(), 4); + this->pipeline_layout->bind(command_buffer, this->get_application()->get_shadow_cache()->get_descriptor_set(), 4); + this->pipeline_layout->bind(command_buffer, this->get_application()->get_indirect_cache()->get_descriptor_set(), 5); const std::vector<SceneMaterial>& materials = this->get_scene()->get_materials(); diff --git a/src/strategy/native_stereo_forward.hpp b/src/strategy/native_stereo_forward.hpp index e8b2abb7056c2871625ff97be6e80e274091b2ea..96eff477a852419d29bb6767bf109d93d7698877 100644 --- a/src/strategy/native_stereo_forward.hpp +++ b/src/strategy/native_stereo_forward.hpp @@ -3,7 +3,6 @@ #include <array> #include "stereo_strategy.hpp" -#include "utility/shadow_cache.hpp" struct NativeStereoForwardPass { @@ -38,7 +37,6 @@ private: void pipeline_function(VkCommandBuffer command_buffer, Eye eye); private: - ShadowCache::Ptr shadow_cache; lava::pipeline_layout::ptr pipeline_layout; lava::attachment::ptr depth_attachment; lava::attachment::ptr color_attachment; diff --git a/src/utility/companion_window.cpp b/src/utility/companion_window.cpp index e959cfd8d9771765fe5e509f598eaedee1702502..078a674517246c398ed988f821d7e381927293cd 100644 --- a/src/utility/companion_window.cpp +++ b/src/utility/companion_window.cpp @@ -2,7 +2,24 @@ bool CompanionWindow::create(lava::device_ptr device, lava::render_target::ptr target) { - this->framebuffer = lava::make_image(target->get_format(), device, target->get_size()); + VkFormat format = target->get_format(); + + switch (format) + { + case VK_FORMAT_B8G8R8A8_UNORM: + case VK_FORMAT_B8G8R8A8_SRGB: + format = VK_FORMAT_B8G8R8A8_SRGB; + break; + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R8G8B8A8_SRGB: + format = VK_FORMAT_R8G8B8A8_SRGB; + break; + default: + lava::log()->warn("Can't derive correct sRGB color format for companion window!"); + break; + } + + this->framebuffer = lava::make_image(format, device, target->get_size()); if (this->framebuffer == nullptr) { @@ -129,7 +146,7 @@ void CompanionWindow::submit_image(VkCommandBuffer command_buffer, Eye eye, lava } VkImageBlit image_blit; - image_blit.srcSubresource = subresource_layers; + image_blit.srcSubresource = image->get_subresource_layers(); image_blit.srcOffsets[0].x = 0; image_blit.srcOffsets[0].y = 0; image_blit.srcOffsets[0].z = 0; diff --git a/src/utility/frame_capture.cpp b/src/utility/frame_capture.cpp index 08233f1858d773b3336a43c4131c8f20ac22c3f7..5c8680b53d5229de33baf8bb6c51bb8c69468748 100644 --- a/src/utility/frame_capture.cpp +++ b/src/utility/frame_capture.cpp @@ -60,19 +60,6 @@ void FrameCapture::capture_image(VkCommandBuffer command_buffer, lava::image::pt image_extend.height = image_size.y; image_extend.depth = 1; - VkImageSubresourceRange image_subresource_range; - image_subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - image_subresource_range.baseArrayLayer = 0; - image_subresource_range.baseMipLevel = 0; - image_subresource_range.layerCount = 1; - image_subresource_range.levelCount = 1; - - VkImageSubresourceLayers image_subresource_layers; - image_subresource_layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - image_subresource_layers.baseArrayLayer = 0; - image_subresource_layers.layerCount = 1; - image_subresource_layers.mipLevel = 0; - VkImageMemoryBarrier begin_barrier; begin_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; begin_barrier.pNext = nullptr; @@ -83,7 +70,7 @@ void FrameCapture::capture_image(VkCommandBuffer command_buffer, lava::image::pt begin_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; begin_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; begin_barrier.image = image->get(); - begin_barrier.subresourceRange = image_subresource_range; + begin_barrier.subresourceRange = image->get_subresource_range(); vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &begin_barrier); @@ -91,7 +78,7 @@ void FrameCapture::capture_image(VkCommandBuffer command_buffer, lava::image::pt region.bufferOffset = 0; region.bufferRowLength = 0; //Tightly packed region.bufferImageHeight = 0; - region.imageSubresource = image_subresource_layers; + region.imageSubresource = image->get_subresource_layers(); region.imageOffset = image_offset; region.imageExtent = image_extend; @@ -107,7 +94,7 @@ void FrameCapture::capture_image(VkCommandBuffer command_buffer, lava::image::pt end_image_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; end_image_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; end_image_barrier.image = image->get(); - end_image_barrier.subresourceRange = image_subresource_range; + end_image_barrier.subresourceRange = image->get_subresource_range(); VkBufferMemoryBarrier end_buffer_barrier; end_buffer_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; diff --git a/src/utility/geometry_buffer.cpp b/src/utility/geometry_buffer.cpp index d05b06b9066b4071a01d0b7e52b4d1df74e4af14..27ff69206ced121be6a6f4d0eadbfdc4d893bfb7 100644 --- a/src/utility/geometry_buffer.cpp +++ b/src/utility/geometry_buffer.cpp @@ -1,12 +1,13 @@ #include "geometry_buffer.hpp" -bool GeometryBuffer::create(lava::image::ptr output_image, VkImageLayout output_layout, lava::descriptor::ptr per_frame_descriptor, Scene::Ptr scene, ShadowCache::Ptr shadow_cache) +bool GeometryBuffer::create(lava::image::ptr output_image, VkImageLayout output_layout, lava::descriptor::ptr per_frame_descriptor, Scene::Ptr scene, ShadowCache::Ptr shadow_cache, IndirectCache::Ptr indirect_cache) { lava::device_ptr device = output_image->get_device(); glm::uvec2 size = output_image->get_size(); this->scene = scene; this->shadow_cache = shadow_cache; + this->indirect_cache = indirect_cache; this->output_image = output_image; this->output_layout = output_layout; @@ -138,7 +139,7 @@ void GeometryBuffer::destroy() this->output_layout = VK_IMAGE_LAYOUT_UNDEFINED; } -void GeometryBuffer::apply_local_lighting(VkCommandBuffer command_buffer, lava::index frame, VkDescriptorSet per_frame_descriptor_set) +void GeometryBuffer::apply_lighting(VkCommandBuffer command_buffer, lava::index frame, VkDescriptorSet per_frame_descriptor_set) { this->frame = frame; this->pipeline_layout->bind(command_buffer, per_frame_descriptor_set, 2); @@ -482,6 +483,11 @@ bool GeometryBuffer::create_pipeline(lava::device_ptr device, lava::descriptor:: this->pipeline_layout->add(this->shadow_cache->get_descriptor()); //descriptor-set index 3: shadow-information } + if (this->indirect_cache != nullptr) + { + this->pipeline_layout->add(this->indirect_cache->get_descriptor()); //descriptor-set index 3 or 4: shadow-information + } + if (!this->pipeline_layout->create(device)) { lava::log()->error("Can't create pipeline layout for deferred local light pass!"); @@ -504,31 +510,32 @@ bool GeometryBuffer::create_pipeline(lava::device_ptr device, lava::descriptor:: this->pipeline->set_input_assembly_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP); this->pipeline->add_color_blend_attachment(output_blend_state); - if (!this->pipeline->add_shader(lava::file_data("dpr/binary/deferred_local_light_vertex.spirv"), VK_SHADER_STAGE_VERTEX_BIT)) + if (!this->pipeline->add_shader(lava::file_data("dpr/binary/deferred_lighting_vertex.spirv"), VK_SHADER_STAGE_VERTEX_BIT)) { - lava::log()->error("Can't load vertex shader for deferred local light pass!"); + lava::log()->error("Can't load vertex shader for deferred lighting pass!"); return false; } - if (this->shadow_cache != nullptr) + std::string shader_name = "dpr/binary/deferred_lighting"; + + if (this->indirect_cache != nullptr) { - if (!this->pipeline->add_shader(lava::file_data("dpr/binary/deferred_local_light_with_shadow_fragment.spirv"), VK_SHADER_STAGE_FRAGMENT_BIT)) - { - lava::log()->error("Can't load fragment shader for deferred local light with shadow pass!"); + shader_name += "_indirect"; + } - return false; - } + if (this->shadow_cache != nullptr) + { + shader_name += "_shadow"; } - else + shader_name += "_fragment.spirv"; + + if (!this->pipeline->add_shader(lava::file_data(shader_name.c_str()), VK_SHADER_STAGE_FRAGMENT_BIT)) { - if (!this->pipeline->add_shader(lava::file_data("dpr/binary/deferred_local_light_fragment.spirv"), VK_SHADER_STAGE_FRAGMENT_BIT)) - { - lava::log()->error("Can't load fragment shader for deferred local light without shadow pass!"); + lava::log()->error("Can't load fragment shader for deferred lighting pass!"); - return false; - } + return false; } this->pipeline->on_process = [this](VkCommandBuffer command_buffer) @@ -553,9 +560,20 @@ void GeometryBuffer::pipline_function(VkCommandBuffer command_buffer) this->pipeline_layout->bind(command_buffer, this->buffer_descriptor_set, 0); this->pipeline_layout->bind(command_buffer, this->scene->get_light_descriptor_set(this->frame), 1); - if (this->shadow_cache != nullptr) + if (this->shadow_cache != nullptr && this->indirect_cache != nullptr) { this->pipeline_layout->bind(command_buffer, this->shadow_cache->get_descriptor_set(), 3); + this->pipeline_layout->bind(command_buffer, this->indirect_cache->get_descriptor_set(), 4); + } + + else if (this->shadow_cache != nullptr) + { + this->pipeline_layout->bind(command_buffer, this->shadow_cache->get_descriptor_set(), 3); + } + + else if (this->indirect_cache != nullptr) + { + this->pipeline_layout->bind(command_buffer, this->indirect_cache->get_descriptor_set(), 4); } vkCmdDraw(command_buffer, 4, 1, 0, 0); //draw fullscreen quad diff --git a/src/utility/geometry_buffer.hpp b/src/utility/geometry_buffer.hpp index d9a89437a6a47eb4a4da7c794488431a86b153e4..a8173f7b2396ac5f76b5598cd376dff2d4c78b43 100644 --- a/src/utility/geometry_buffer.hpp +++ b/src/utility/geometry_buffer.hpp @@ -1,20 +1,25 @@ /* - The geometry buffer is responsible for the allocation of the images of the geometry buffer and allows to compute and write the local lighting to a given output image. - During the creation of the geometry buffer, an output image has to be specified in which the local lighting will be written. + The geometry buffer is responsible for the allocation of the images of the geometry buffer and allows to compute and write the final lighting to a given output image. + During the creation of the geometry buffer, an output image has to be specified in which the final lighting will be written. After the geometry has been created successfully, the resources provided by the geometry buffer can be used to create a render pass in order to write into the geometry buffer. - Before calling the function apply_local_lighting(...) and computing the local lighting, it is neccessary to ensure that every writing process effecting the geometry buffer has been completed. + Before calling the function apply_lighting(...) and computing the final lighting, it is neccessary to ensure that every writing process effecting the geometry buffer has been completed. Such a synchronisation can be defined by adding a render pass dependency between the last and the extrenal subpass. - After the completion of apply_local_lighting(...) the local lighting can be read from the output image in a fragment shader without any additional synchronisations. - In order to also include shadows when executing apply_local_lighting(...), a shadow cache object has to be added to the create pareameters. + After the completion of apply_lighting(...) the final lighting can be read from the output image in a fragment shader without any additional synchronisations. + In order to also include shadows when executing apply_lighting(...), a shadow cache object has to be added to the create pareameters. + For additional indirect lighting, an indirect cache object can be added during the creation of the geometry buffer. Example: //Optional: Shadow Cache ShadowCache::Ptr shadow_cache = make_shadow_cache(); shadow_cache->create(get_scene(), ShadowCacheSettings()): + //Optional: Indirect Cache + IndirectCache::Ptr indirect_cache = make_indirect_cache(); + indirect_cache->create(get_scene(), IndirectCacheSettings()); + //Create render pass GeometryBuffer::Ptr geometry_buffer = make_geometry_buffer(); - geometry_buffer->create(output_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, get_stereo_transform()->get_descriptor(), get_scene(), Optional: shadow_cache); + geometry_buffer->create(output_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, get_stereo_transform()->get_descriptor(), get_scene(), Optional: shadow_cache, Optional: indirect_cache); lava::subpass_dependency::ptr subpass_dependency = lava::make_subpass_dependency(0, VK_SUBPASS_EXTERNAL); subpass_dependency->set_stage_mask(..., VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); @@ -46,7 +51,8 @@ render_pass->create({image_views}, ...); //During rendering - shadow_cache->compute_shadow(command_buffer, frame); //Optinal: Generate shadow maps + shadow_cache->compute_shadow(command_buffer, frame); //Optional: Generate shadow maps + indirect_cache->compute_indirect(command_buffer, frame); //Optional: Compute indirect lighting render_pass->process(command_buffer, 0); //Write to geometry buffer geometry_buffer->apply_local_lighting(command_buffer, per_frame_descriptor_set); //Compute local lighting and write it to the output image @@ -60,6 +66,7 @@ #include "scene.hpp" #include "shadow_cache.hpp" +#include "indirect_cache.hpp" class GeometryBuffer { @@ -69,11 +76,11 @@ public: public: GeometryBuffer() = default; - bool create(lava::image::ptr output_image, VkImageLayout output_layout, lava::descriptor::ptr per_frame_descriptor, Scene::Ptr scene, ShadowCache::Ptr shadow_cache = {}); + bool create(lava::image::ptr output_image, VkImageLayout output_layout, lava::descriptor::ptr per_frame_descriptor, Scene::Ptr scene, ShadowCache::Ptr shadow_cache = {}, IndirectCache::Ptr indirect_cache = {}); void destroy(); - // Apply local lighting based on the perspective defined by the per_frame_descriptor_set and write the results into the output image - void apply_local_lighting(VkCommandBuffer command_buffer, lava::index frame, VkDescriptorSet per_frame_descriptor_set); + // Apply lighting based on the perspective defined by the per_frame_descriptor_set and write the results into the output image + void apply_lighting(VkCommandBuffer command_buffer, lava::index frame, VkDescriptorSet per_frame_descriptor_set); // Images of the geometry buffer that can be used to create a render pass lava::image::ptr get_depth_image() const; @@ -106,6 +113,7 @@ private: private: Scene::Ptr scene; ShadowCache::Ptr shadow_cache; + IndirectCache::Ptr indirect_cache; uint32_t frame = 0; lava::image::ptr output_image; diff --git a/src/utility/indirect_cache.cpp b/src/utility/indirect_cache.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a4faa39bedd29ffee48492e66a91e8ea89652099 --- /dev/null +++ b/src/utility/indirect_cache.cpp @@ -0,0 +1,1579 @@ +#include "indirect_cache.hpp" + +namespace glsl +{ + using namespace glm; + using uint = glm::uint32; + +#include "res/dpr/data/indirect_data.inc" +} + +bool IndirectCache::create(Scene::Ptr scene, const IndirectCacheSettings& settings) +{ + lava::device_ptr device = scene->get_light_descriptor()->get_device(); + + this->scene = scene; + this->settings = settings; + + if(!this->compute_domain(scene, settings)) + { + return false; + } + + if (!this->create_buffers(device, settings)) + { + return false; + } + + if (!this->create_images(device, settings)) + { + return false; + } + + if (!this->create_samplers(device)) + { + return false; + } + + if (!this->create_descriptors(device)) + { + return false; + } + + if (!this->create_layouts(device, scene)) + { + return false; + } + + if (!this->create_geometry_pass(device)) + { + return false; + } + + if (!this->create_geometry_pipeline(device)) + { + return false; + } + + if (!this->create_capture_pass(device, settings)) + { + return false; + } + + if (!this->create_capture_pipeline(device)) + { + return false; + } + + if (!this->create_injection_pass(device)) + { + return false; + } + + if (!this->create_injection_pipeline(device)) + { + return false; + } + + if (!this->create_propagation_pipeline(device)) + { + return false; + } + + return true; +} + +void IndirectCache::destroy() +{ + lava::device_ptr device = this->scene->get_light_descriptor()->get_device(); + + if (this->geometry_pipeline != nullptr) + { + this->geometry_pipeline->destroy(); + } + + if (this->capture_pipeline != nullptr) + { + this->capture_pipeline->destroy(); + } + + if (this->injection_pipeline != nullptr) + { + this->capture_pipeline->destroy(); + } + + if (this->propagation_pipeline != nullptr) + { + this->propagation_pipeline->destroy(); + } + + if (this->geometry_pass != nullptr) + { + this->geometry_pass->destroy(); + } + + if (this->capture_pass != nullptr) + { + this->capture_pass->destroy(); + } + + if (this->injection_pass != nullptr) + { + this->injection_pass->destroy(); + } + + if (this->geometry_layout != nullptr) + { + this->geometry_layout->destroy(); + } + + if (this->capture_layout != nullptr) + { + this->capture_layout->destroy(); + } + + if (this->injection_layout != nullptr) + { + this->injection_layout->destroy(); + } + + if (this->propagation_layout != nullptr) + { + this->propagation_layout->destroy(); + } + + if (this->indirect_descriptor != nullptr) + { + this->indirect_descriptor->free(this->indirect_descriptor_set, this->descriptor_pool->get()); + this->indirect_descriptor->destroy(); + } + + if (this->geometry_descriptor != nullptr) + { + this->geometry_descriptor->free(this->geometry_descriptor_set, this->descriptor_pool->get()); + this->geometry_descriptor->destroy(); + } + + if (this->injection_descriptor != nullptr) + { + this->injection_descriptor->free(this->injection_descriptor_set, this->descriptor_pool->get()); + this->injection_descriptor->destroy(); + } + + if (this->propagation_descriptor != nullptr) + { + this->propagation_descriptor->free(this->propagation_descriptor_set[0], this->descriptor_pool->get()); + this->propagation_descriptor->free(this->propagation_descriptor_set[1], this->descriptor_pool->get()); + this->propagation_descriptor->destroy(); + } + + if (this->descriptor_pool != nullptr) + { + this->descriptor_pool->destroy(); + } + + if (this->capture_depth_image != nullptr) + { + this->capture_depth_image->destroy(); + } + + if (this->capture_flux_image != nullptr) + { + this->capture_flux_image->destroy(); + } + + if (this->capture_normal_image != nullptr) + { + this->capture_normal_image->destroy(); + } + + if (this->domain_buffer != nullptr) + { + this->domain_buffer->destroy(); + } + + vkDestroySampler(device->get(), this->indirect_sampler, lava::memory::alloc()); + vkDestroySampler(device->get(), this->geometry_sampler, lava::memory::alloc()); + vkDestroySampler(device->get(), this->injection_sampler, lava::memory::alloc()); + + for (uint32_t index = 0; index < this->distribution_red_image.size(); index++) + { + vkDestroyImageView(device->get(), this->distribution_red_image_view[index], lava::memory::alloc()); + vkDestroyImageView(device->get(), this->distribution_green_image_view[index], lava::memory::alloc()); + vkDestroyImageView(device->get(), this->distribution_blue_image_view[index], lava::memory::alloc()); + + vkDestroyImageView(device->get(), this->distribution_red_image_array_view[index], lava::memory::alloc()); + vkDestroyImageView(device->get(), this->distribution_green_image_array_view[index], lava::memory::alloc()); + vkDestroyImageView(device->get(), this->distribution_blue_image_array_view[index], lava::memory::alloc()); + + vkDestroyImage(device->get(), this->distribution_red_image[index], lava::memory::alloc()); + vkDestroyImage(device->get(), this->distribution_green_image[index], lava::memory::alloc()); + vkDestroyImage(device->get(), this->distribution_blue_image[index], lava::memory::alloc()); + + vmaFreeMemory(device->get_allocator()->get(), this->distribution_red_image_memory[index]); + vmaFreeMemory(device->get_allocator()->get(), this->distribution_green_image_memory[index]); + vmaFreeMemory(device->get_allocator()->get(), this->distribution_blue_image_memory[index]); + } + + for (uint32_t index = 0; index < this->distribution_geometry_image.size(); index++) + { + vkDestroyImageView(device->get(), this->distribution_geometry_image_view[index], lava::memory::alloc()); + vkDestroyImageView(device->get(), this->distribution_geometry_image_integer_view[index], lava::memory::alloc()); + + vkDestroyImage(device->get(), this->distribution_geometry_image[index], lava::memory::alloc()); + + vmaFreeMemory(device->get_allocator()->get(), this->distribution_geometry_image_memory[index]); + } +} + +void IndirectCache::compute_indirect(VkCommandBuffer command_buffer, lava::index frame) +{ + const std::vector<SceneLight>& lights = this->scene->get_lights(); + + std::vector<VkImageMemoryBarrier> clear_begin_barriers = IndirectCache::build_barriers(5, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + clear_begin_barriers[0].image = this->distribution_red_image[0]; + clear_begin_barriers[1].image = this->distribution_green_image[0]; + clear_begin_barriers[2].image = this->distribution_blue_image[0]; + clear_begin_barriers[3].image = this->distribution_geometry_image[0]; + clear_begin_barriers[4].image = this->distribution_geometry_image[1]; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, clear_begin_barriers.size(), clear_begin_barriers.data()); + + VkClearColorValue clear_color; + clear_color.float32[0] = 0.0f; + clear_color.float32[1] = 0.0f; + clear_color.float32[2] = 0.0f; + clear_color.float32[3] = 0.0f; + + VkImageSubresourceRange subresource_range; + subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresource_range.baseMipLevel = 0; + subresource_range.levelCount = 1; + subresource_range.baseArrayLayer = 0; + subresource_range.layerCount = 1; + + vkCmdClearColorImage(command_buffer, this->distribution_red_image[0], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range); + vkCmdClearColorImage(command_buffer, this->distribution_green_image[0], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range); + vkCmdClearColorImage(command_buffer, this->distribution_blue_image[0], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range); + vkCmdClearColorImage(command_buffer, this->distribution_geometry_image[0], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range); + vkCmdClearColorImage(command_buffer, this->distribution_geometry_image[1], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range); + + std::vector<VkImageMemoryBarrier> clear_end_barriers = IndirectCache::build_barriers(3, VK_ACCESS_TRANSFER_WRITE_BIT, 0, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + clear_end_barriers[0].image = this->distribution_red_image[0]; + clear_end_barriers[1].image = this->distribution_green_image[0]; + clear_end_barriers[2].image = this->distribution_blue_image[0]; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, clear_end_barriers.size(), clear_end_barriers.data()); + + std::vector<VkImageMemoryBarrier> geometry_begin_barriers = IndirectCache::build_barriers(2, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); + geometry_begin_barriers[0].image = this->distribution_geometry_image[0]; + geometry_begin_barriers[1].image = this->distribution_geometry_image[1]; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, geometry_begin_barriers.size(), geometry_begin_barriers.data()); + + this->geometry_pass->process(command_buffer, 0); + + std::vector<VkImageMemoryBarrier> geometry_end_barriers = IndirectCache::build_barriers(2, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + geometry_end_barriers[0].image = this->distribution_geometry_image[0]; + geometry_end_barriers[1].image = this->distribution_geometry_image[1]; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, geometry_end_barriers.size(), geometry_end_barriers.data()); + + for (uint32_t index = 0; index < lights.size(); index++) + { + const SceneLight& light = lights[index]; + + if (light.data.type != LIGHT_TYPE_DIRECTIONAL) + { + continue; + } + + this->frame = frame; + this->light_index = index; + this->capture_pass->process(command_buffer, 0); + this->injection_pass->process(command_buffer, 0); + } + + //NOTE: Memory barrier for source image provieded by injection render pass + // The final image layout of distribution_..._image[0] has to be VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL + + std::vector<VkImageMemoryBarrier> copy_barriers = IndirectCache::build_barriers(3, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + copy_barriers[0].image = this->distribution_red_image[2]; + copy_barriers[1].image = this->distribution_green_image[2]; + copy_barriers[2].image = this->distribution_blue_image[2]; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, copy_barriers.size(), copy_barriers.data()); + + VkImageSubresourceLayers subresource_layers; + subresource_layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresource_layers.mipLevel = 0; + subresource_layers.baseArrayLayer = 0; + subresource_layers.layerCount = 1; + + VkOffset3D offset; + offset.x = 0; + offset.y = 0; + offset.z = 0; + + VkExtent3D extend; + extend.width = this->domain_indirect_resolution.x; + extend.height = this->domain_indirect_resolution.y; + extend.depth = this->domain_indirect_resolution.z; + + VkImageCopy image_copy; + image_copy.srcSubresource = subresource_layers; + image_copy.srcOffset = offset; + image_copy.dstSubresource = subresource_layers; + image_copy.dstOffset = offset; + image_copy.extent = extend; + + vkCmdCopyImage(command_buffer, this->distribution_red_image[0], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, this->distribution_red_image[2], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); + vkCmdCopyImage(command_buffer, this->distribution_green_image[0], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, this->distribution_green_image[2], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); + vkCmdCopyImage(command_buffer, this->distribution_blue_image[0], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, this->distribution_blue_image[2], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); + + std::vector<VkImageMemoryBarrier> setup_src_barriers = IndirectCache::build_barriers(3, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); + setup_src_barriers[0].image = this->distribution_red_image[0]; + setup_src_barriers[1].image = this->distribution_green_image[0]; + setup_src_barriers[2].image = this->distribution_blue_image[0]; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, setup_src_barriers.size(), setup_src_barriers.data()); + + std::vector<VkImageMemoryBarrier> setup_dst_barriers = IndirectCache::build_barriers(3, 0, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); + setup_dst_barriers[0].image = this->distribution_red_image[1]; + setup_dst_barriers[1].image = this->distribution_green_image[1]; + setup_dst_barriers[2].image = this->distribution_blue_image[1]; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, setup_dst_barriers.size(), setup_dst_barriers.data()); + + std::vector<VkImageMemoryBarrier> setup_indirect_barriers = IndirectCache::build_barriers(3, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); + setup_indirect_barriers[0].image = this->distribution_red_image[2]; + setup_indirect_barriers[1].image = this->distribution_green_image[2]; + setup_indirect_barriers[2].image = this->distribution_blue_image[2]; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, setup_indirect_barriers.size(), setup_indirect_barriers.data()); + + this->propagation_pipeline->bind(command_buffer); + + for (uint32_t index = 0; index < this->propagation_iterations; index++) + { + uint32_t src_index = index % 2; + uint32_t dst_index = (index + 1) % 2; + + this->propagation_layout->bind(command_buffer, this->propagation_descriptor_set[src_index], 0, {}, VK_PIPELINE_BIND_POINT_COMPUTE); + + vkCmdDispatch(command_buffer, this->domain_work_groups.x, this->domain_work_groups.y, this->domain_work_groups.z); + + std::vector<VkImageMemoryBarrier> compue_src_barriers = IndirectCache::build_barriers(3, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL); + compue_src_barriers[0].image = this->distribution_red_image[src_index]; + compue_src_barriers[1].image = this->distribution_green_image[src_index]; + compue_src_barriers[2].image = this->distribution_blue_image[src_index]; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, compue_src_barriers.size(), compue_src_barriers.data()); + + std::vector<VkImageMemoryBarrier> compute_dst_barriers = IndirectCache::build_barriers(3, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL); + compute_dst_barriers[0].image = this->distribution_red_image[dst_index]; + compute_dst_barriers[1].image = this->distribution_green_image[dst_index]; + compute_dst_barriers[2].image = this->distribution_blue_image[dst_index]; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, compute_dst_barriers.size(), compute_dst_barriers.data()); + + std::vector<VkImageMemoryBarrier> compute_indirect_barriers = IndirectCache::build_barriers(3, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL); + compute_indirect_barriers[0].image = this->distribution_red_image[2]; + compute_indirect_barriers[1].image = this->distribution_green_image[2]; + compute_indirect_barriers[2].image = this->distribution_blue_image[2]; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, compute_indirect_barriers.size(), compute_indirect_barriers.data()); + } + + std::vector<VkImageMemoryBarrier> final_barriers = IndirectCache::build_barriers(3, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + final_barriers[0].image = this->distribution_red_image[2]; + final_barriers[1].image = this->distribution_green_image[2]; + final_barriers[2].image = this->distribution_blue_image[2]; + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, final_barriers.size(), final_barriers.data()); +} + +lava::descriptor::ptr IndirectCache::get_descriptor() const +{ + return this->indirect_descriptor; +} + +VkDescriptorSet IndirectCache::get_descriptor_set() const +{ + return this->indirect_descriptor_set; +} + +const IndirectCacheSettings& IndirectCache::get_settings() const +{ + return this->settings; +} + +bool IndirectCache::compute_domain(Scene::Ptr scene, const IndirectCacheSettings& settings) +{ + if (settings.voxel_resolution_scale <= 0) + { + lava::log()->error("Voxel resolution scale needs to be greater or equal to one!"); + + return false; + } + + glm::vec3 scene_min = scene->get_scene_min(); + glm::vec3 scene_max = scene->get_scene_max(); + + glm::vec3 scene_center = (scene_min + scene_max) / 2.0f; + glm::vec3 scene_size = scene_max - scene_min; + + const glm::uvec3 work_group_size = glm::uvec3(16, 16, 2); + + this->domain_indirect_resolution = glm::uvec3(glm::max(glm::ceil(scene_size / settings.cell_size), glm::vec3(1.0f))); + this->domain_geometry_resolution = this->domain_indirect_resolution + glm::uvec3(1); + this->domain_voxel_resolution = this->domain_geometry_resolution * settings.voxel_resolution_scale; + this->domain_min = scene_center - 0.5f * settings.cell_size * glm::vec3(this->domain_indirect_resolution); + this->domain_max = scene_center + 0.5f * settings.cell_size * glm::vec3(this->domain_indirect_resolution); + this->domain_work_groups = this->domain_indirect_resolution / work_group_size; + + if ((this->domain_indirect_resolution.x % work_group_size.x) > 0) + { + this->domain_work_groups.x += 1; + } + + if ((this->domain_indirect_resolution.y % work_group_size.y) > 0) + { + this->domain_work_groups.y += 1; + } + + if ((this->domain_indirect_resolution.z % work_group_size.z) > 0) + { + this->domain_work_groups.z += 1; + } + + this->propagation_iterations = 2 * glm::max(this->domain_indirect_resolution.x, glm::max(this->domain_indirect_resolution.y, this->domain_indirect_resolution.z)); + + return true; +} + +bool IndirectCache::create_buffers(lava::device_ptr device, const IndirectCacheSettings& settings) +{ + glsl::IndirectDomain indirect_domain; + indirect_domain.indirect_resolution = this->domain_indirect_resolution; + indirect_domain.cell_size = settings.cell_size; + indirect_domain.geometry_resolution = this->domain_geometry_resolution; + indirect_domain.padding1 = 0; + indirect_domain.min = this->domain_min; + indirect_domain.padding2 = 0; + indirect_domain.max = this->domain_max; + indirect_domain.padding3 = 0; + + this->domain_buffer = lava::make_buffer(); + + if (!this->domain_buffer->create_mapped(device, &indirect_domain, sizeof(indirect_domain), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)) + { + lava::log()->error("Can't create domain buffer for indirect cache!"); + + return false; + } + + return true; +} + +bool IndirectCache::create_images(lava::device_ptr device, const IndirectCacheSettings& settings) +{ + this->capture_depth_image = lava::make_image(VK_FORMAT_D16_UNORM); + this->capture_depth_image->set_usage(VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + + if (!this->capture_depth_image->create(device, glm::uvec2(settings.capture_resolution))) + { + lava::log()->error("Can't create depth capture image for indirect cache!"); + + return false; + } + + this->capture_flux_image = lava::make_image(VK_FORMAT_R16G16B16A16_SFLOAT); + this->capture_flux_image->set_usage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + + if (!this->capture_flux_image->create(device, glm::uvec2(settings.capture_resolution))) + { + lava::log()->error("Can't create flux capture image for indirect cache!"); + + return false; + } + + this->capture_normal_image = lava::make_image(VK_FORMAT_R16G16_UNORM); + this->capture_normal_image->set_usage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + + if (!this->capture_normal_image->create(device, glm::uvec2(settings.capture_resolution))) + { + lava::log()->error("Can't create normal capture image for indirect cache!"); + + return false; + } + + VmaAllocationCreateInfo allocation_info; + allocation_info.flags = 0; + allocation_info.usage = VMA_MEMORY_USAGE_GPU_ONLY; + allocation_info.requiredFlags = 0; + allocation_info.preferredFlags = 0; + allocation_info.memoryTypeBits = 0; + allocation_info.pool = nullptr; + allocation_info.pUserData = nullptr; + allocation_info.priority = 1.0f; + + VkImageCreateInfo image_create_info; + image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_create_info.pNext = nullptr; + image_create_info.flags = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; + image_create_info.imageType = VK_IMAGE_TYPE_3D; + image_create_info.format = VK_FORMAT_R16G16B16A16_SFLOAT; + image_create_info.extent.width = this->domain_indirect_resolution.x; + image_create_info.extent.height = this->domain_indirect_resolution.y; + image_create_info.extent.depth = this->domain_indirect_resolution.z; + image_create_info.mipLevels = 1; + image_create_info.arrayLayers = 1; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_create_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_create_info.queueFamilyIndexCount = 0; + image_create_info.pQueueFamilyIndices = nullptr; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VkImageViewCreateInfo view_create_info; + view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_create_info.pNext = nullptr; + view_create_info.flags = 0; + view_create_info.image = VK_NULL_HANDLE; + view_create_info.viewType = VK_IMAGE_VIEW_TYPE_3D; + view_create_info.format = VK_FORMAT_R16G16B16A16_SFLOAT; + view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + view_create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_create_info.subresourceRange.baseMipLevel = 0; + view_create_info.subresourceRange.levelCount = 1; + view_create_info.subresourceRange.baseArrayLayer = 0; + view_create_info.subresourceRange.layerCount = 1; + + VkImageViewCreateInfo array_view_create_info; + array_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + array_view_create_info.pNext = nullptr; + array_view_create_info.flags = 0; + array_view_create_info.image = VK_NULL_HANDLE; + array_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + array_view_create_info.format = VK_FORMAT_R16G16B16A16_SFLOAT; + array_view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + array_view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + array_view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + array_view_create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + array_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + array_view_create_info.subresourceRange.baseMipLevel = 0; + array_view_create_info.subresourceRange.levelCount = 1; + array_view_create_info.subresourceRange.baseArrayLayer = 0; + array_view_create_info.subresourceRange.layerCount = this->domain_indirect_resolution.z; + + for (uint32_t index = 0; index < this->distribution_red_image.size(); index++) + { + if (vmaCreateImage(device->get_allocator()->get(), &image_create_info, &allocation_info, &this->distribution_red_image[index], &this->distribution_red_image_memory[index], nullptr) != VK_SUCCESS) + { + lava::log()->error("Can't create red distribution image for indirect cache!"); + + return false; + } + + if (vmaCreateImage(device->get_allocator()->get(), &image_create_info, &allocation_info, &this->distribution_green_image[index], &this->distribution_green_image_memory[index], nullptr) != VK_SUCCESS) + { + lava::log()->error("Can't create green distribution image for indirect cache!"); + + return false; + } + + if (vmaCreateImage(device->get_allocator()->get(), &image_create_info, &allocation_info, &this->distribution_blue_image[index], &this->distribution_blue_image_memory[index], nullptr) != VK_SUCCESS) + { + lava::log()->error("Can't create blue distribution image for indirect cache!"); + + return false; + } + + view_create_info.image = this->distribution_red_image[index]; + array_view_create_info.image = this->distribution_red_image[index]; + + if (vkCreateImageView(device->get(), &view_create_info, lava::memory::alloc(), &this->distribution_red_image_view[index]) != VK_SUCCESS) + { + lava::log()->error("Can't create red distribution image view for indirect cache!"); + + return false; + } + + if (vkCreateImageView(device->get(), &array_view_create_info, lava::memory::alloc(), &this->distribution_red_image_array_view[index]) != VK_SUCCESS) + { + lava::log()->error("Can't create red distribution image array view for indirect cache!"); + + return false; + } + + view_create_info.image = this->distribution_green_image[index]; + array_view_create_info.image = this->distribution_green_image[index]; + + if (vkCreateImageView(device->get(), &view_create_info, lava::memory::alloc(), &this->distribution_green_image_view[index]) != VK_SUCCESS) + { + lava::log()->error("Can't create green distribution image view for indirect cache!"); + + return false; + } + + if (vkCreateImageView(device->get(), &array_view_create_info, lava::memory::alloc(), &this->distribution_green_image_array_view[index]) != VK_SUCCESS) + { + lava::log()->error("Can't create green distribution image array view for indirect cache!"); + + return false; + } + + view_create_info.image = this->distribution_blue_image[index]; + array_view_create_info.image = this->distribution_blue_image[index]; + + if (vkCreateImageView(device->get(), &view_create_info, lava::memory::alloc(), &this->distribution_blue_image_view[index]) != VK_SUCCESS) + { + lava::log()->error("Can't create blue distribution image view for indirect cache!"); + + return false; + } + + if (vkCreateImageView(device->get(), &array_view_create_info, lava::memory::alloc(), &this->distribution_blue_image_array_view[index]) != VK_SUCCESS) + { + lava::log()->error("Can't create blue distribution image array view for indirect cache!"); + + return false; + } + } + + image_create_info.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + image_create_info.format = VK_FORMAT_R16G16_SFLOAT; + image_create_info.extent.width = this->domain_geometry_resolution.x; + image_create_info.extent.height = this->domain_geometry_resolution.y; + image_create_info.extent.depth = this->domain_geometry_resolution.z; + image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + + view_create_info.format = VK_FORMAT_R16G16_SFLOAT; + + VkImageViewCreateInfo integer_view_create_info; + integer_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + integer_view_create_info.pNext = nullptr; + integer_view_create_info.flags = 0; + integer_view_create_info.image = VK_NULL_HANDLE; + integer_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_3D; + integer_view_create_info.format = VK_FORMAT_R32_UINT; + integer_view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + integer_view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + integer_view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + integer_view_create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + integer_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + integer_view_create_info.subresourceRange.baseMipLevel = 0; + integer_view_create_info.subresourceRange.levelCount = 1; + integer_view_create_info.subresourceRange.baseArrayLayer = 0; + integer_view_create_info.subresourceRange.layerCount = 1; + + for (uint32_t index = 0; index < this->distribution_geometry_image.size(); index++) + { + if (vmaCreateImage(device->get_allocator()->get(), &image_create_info, &allocation_info, &this->distribution_geometry_image[index], &this->distribution_geometry_image_memory[index], nullptr) != VK_SUCCESS) + { + lava::log()->error("Can't create geometry distribution image for indirect cache!"); + + return false; + } + + view_create_info.image = this->distribution_geometry_image[index]; + integer_view_create_info.image = this->distribution_geometry_image[index]; + + if (vkCreateImageView(device->get(), &view_create_info, lava::memory::alloc(), &this->distribution_geometry_image_view[index]) != VK_SUCCESS) + { + lava::log()->error("Can't create geometry distribution image view for indirect cache!"); + + return false; + } + + if (vkCreateImageView(device->get(), &integer_view_create_info, lava::memory::alloc(), &this->distribution_geometry_image_integer_view[index]) != VK_SUCCESS) + { + lava::log()->error("Can't create geometry distribution image integer view for indirect cache!"); + + return false; + } + } + + return true; +} + +bool IndirectCache::create_samplers(lava::device_ptr device) +{ + VkSamplerCreateInfo sampler_info; + sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_info.pNext = nullptr; + sampler_info.flags = 0; + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + sampler_info.mipLodBias = 0.0f; + sampler_info.anisotropyEnable = VK_FALSE; + sampler_info.maxAnisotropy = 1.0f; + sampler_info.compareEnable = VK_FALSE; + sampler_info.compareOp = VK_COMPARE_OP_LESS; + sampler_info.minLod = 0.0f; + sampler_info.maxLod = 0.0f; + sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; + sampler_info.unnormalizedCoordinates = VK_FALSE; + + if (vkCreateSampler(device->get(), &sampler_info, lava::memory::alloc(), &this->indirect_sampler) != VK_SUCCESS) + { + lava::log()->error("Can't create indirect sampler for indirect cache!"); + + return false; + } + + if (vkCreateSampler(device->get(), &sampler_info, lava::memory::alloc(), &this->geometry_sampler) != VK_SUCCESS) + { + lava::log()->error("Can't create geometry sampler for indirect cache!"); + + return false; + } + + sampler_info.magFilter = VK_FILTER_NEAREST; + sampler_info.minFilter = VK_FILTER_NEAREST; + sampler_info.unnormalizedCoordinates = VK_TRUE; + + if (vkCreateSampler(device->get(), &sampler_info, lava::memory::alloc(), &this->injection_sampler) != VK_SUCCESS) + { + lava::log()->error("Can't create injection sampler for indirect cache!"); + + return false; + } + + return true; +} + +bool IndirectCache::create_descriptors(lava::device_ptr device) +{ + lava::VkDescriptorPoolSizes descriptor_type_count = + { + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 5 }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 10 }, + { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 20 } + }; + + this->descriptor_pool = lava::make_descriptor_pool(); + + if (!this->descriptor_pool->create(device, descriptor_type_count, 5)) + { + lava::log()->error("Can't create descriptor pool for indirect cache!"); + + return false; + } + + this->indirect_descriptor = lava::make_descriptor(); + this->indirect_descriptor->add_binding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT); //descriptor-binding index 0: indirect red + this->indirect_descriptor->add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT); //descriptor-binding index 1: indirect green + this->indirect_descriptor->add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT); //descriptor-binding index 2: indirect blue + this->indirect_descriptor->add_binding(3, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT); //descriptor-binding index 3: indirect domain + + if (!this->indirect_descriptor->create(device)) + { + lava::log()->error("Can't create indirect descriptor set layout for indirect cache!"); + + return false; + } + + this->geometry_descriptor = lava::make_descriptor(); + this->geometry_descriptor->add_binding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); //descriptor-binding index 0: indirect domain + + this->geometry_descriptor->add_binding(1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_FRAGMENT_BIT); //descriptor-binding index 1: geometry distribution xy + this->geometry_descriptor->add_binding(2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_FRAGMENT_BIT); //descriptor-binding index 2: geometry distribution zw + + if (!this->geometry_descriptor->create(device)) + { + lava::log()->error("Can't create geometry descriptor set layout for indirect cache!"); + + return false; + } + + this->injection_descriptor = lava::make_descriptor(); + this->injection_descriptor->add_binding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_VERTEX_BIT); //descriptor-binding index 0: capture depth + this->injection_descriptor->add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_VERTEX_BIT); //descriptor-binding index 1: capture flux + this->injection_descriptor->add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_VERTEX_BIT); //descriptor-binding index 2: capture normal + this->injection_descriptor->add_binding(3, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_GEOMETRY_BIT); //descriptor-binding index 3: indirect domain + + if (!this->injection_descriptor->create(device)) + { + lava::log()->error("Can't create injection descriptor set layout for indirect cache!"); + + return false; + } + + this->propagation_descriptor = lava::make_descriptor(); + this->propagation_descriptor->add_binding(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 0: image red source distribution + this->propagation_descriptor->add_binding(1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 1: image green source distribution + this->propagation_descriptor->add_binding(2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 2: image blue source distribution + + this->propagation_descriptor->add_binding(3, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 3: image red destination distribution + this->propagation_descriptor->add_binding(4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 4: image green destination distribution + this->propagation_descriptor->add_binding(5, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 5: image blue destination distribution + + this->propagation_descriptor->add_binding(6, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 6: image red indirect + this->propagation_descriptor->add_binding(7, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 7: image green indirect + this->propagation_descriptor->add_binding(8, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 8: image blue indirect + + this->propagation_descriptor->add_binding(9, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 9: image xz geometrz distribution + this->propagation_descriptor->add_binding(10, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 10: image yw geometrz distribution + + this->propagation_descriptor->add_binding(11, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 11: indirect domain + + if (!this->propagation_descriptor->create(device)) + { + lava::log()->error("Can't create propagation descriptor set layout for indirect cache!"); + + return false; + } + + this->indirect_descriptor_set = this->indirect_descriptor->allocate(this->descriptor_pool->get()); + this->geometry_descriptor_set = this->geometry_descriptor->allocate(this->descriptor_pool->get()); + this->injection_descriptor_set = this->injection_descriptor->allocate(this->descriptor_pool->get()); + this->propagation_descriptor_set[0] = this->propagation_descriptor->allocate(this->descriptor_pool->get()); + this->propagation_descriptor_set[1] = this->propagation_descriptor->allocate(this->descriptor_pool->get()); + + std::vector<VkWriteDescriptorSet> descriptor_writes; + std::vector<VkDescriptorImageInfo> image_infos; + + descriptor_writes.reserve(35); + image_infos.reserve(30); + + std::vector<VkImageView> indirect_images = + { + this->distribution_red_image_view[2], + this->distribution_green_image_view[2], + this->distribution_blue_image_view[2], + }; + + for (uint32_t index = 0; index < indirect_images.size(); index++) + { + VkDescriptorImageInfo& image_info = image_infos.emplace_back(); + image_info.sampler = this->indirect_sampler; + image_info.imageView = indirect_images[index]; + image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet& descriptor_write = descriptor_writes.emplace_back(); + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.pNext = nullptr; + descriptor_write.dstSet = this->indirect_descriptor_set; + descriptor_write.dstBinding = index; + descriptor_write.dstArrayElement = 0; + descriptor_write.descriptorCount = 1; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_write.pImageInfo = &image_info; + descriptor_write.pBufferInfo = nullptr; + descriptor_write.pTexelBufferView = nullptr; + } + + for (uint32_t index = 0; index < this->distribution_geometry_image_integer_view.size(); index++) + { + VkDescriptorImageInfo& image_info = image_infos.emplace_back(); + image_info.sampler = nullptr; + image_info.imageView = this->distribution_geometry_image_integer_view[index]; + image_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + VkWriteDescriptorSet& descriptor_write = descriptor_writes.emplace_back(); + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.pNext = nullptr; + descriptor_write.dstSet = this->geometry_descriptor_set; + descriptor_write.dstBinding = index + 1; + descriptor_write.dstArrayElement = 0; + descriptor_write.descriptorCount = 1; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + descriptor_write.pImageInfo = &image_info; + descriptor_write.pBufferInfo = nullptr; + descriptor_write.pTexelBufferView = nullptr; + } + + std::vector<VkImageView> injection_images = + { + this->capture_depth_image->get_view(), + this->capture_flux_image->get_view(), + this->capture_normal_image->get_view() + }; + + for (uint32_t index = 0; index < injection_images.size(); index++) + { + VkDescriptorImageInfo& image_info = image_infos.emplace_back(); + image_info.sampler = this->injection_sampler; + image_info.imageView = injection_images[index]; + image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet& descriptor_write = descriptor_writes.emplace_back(); + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.pNext = nullptr; + descriptor_write.dstSet = this->injection_descriptor_set; + descriptor_write.dstBinding = index; + descriptor_write.dstArrayElement = 0; + descriptor_write.descriptorCount = 1; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_write.pImageInfo = &image_info; + descriptor_write.pBufferInfo = nullptr; + descriptor_write.pTexelBufferView = nullptr; + } + + std::vector<VkImageView> propagation_images = + { + this->distribution_red_image_view[0], + this->distribution_green_image_view[0], + this->distribution_blue_image_view[0], + this->distribution_red_image_view[1], + this->distribution_green_image_view[1], + this->distribution_blue_image_view[1], + this->distribution_red_image_view[2], + this->distribution_green_image_view[2], + this->distribution_blue_image_view[2] + }; + + for (uint32_t set_index = 0; set_index < this->propagation_descriptor_set.size(); set_index++) + { + for (uint32_t image_index = 0; image_index < propagation_images.size(); image_index++) + { + uint32_t binding = image_index; + + if (image_index < 6) + { + binding = (binding + set_index * 3) % 6; + } + + VkDescriptorImageInfo& image_info = image_infos.emplace_back(); + image_info.sampler = VK_NULL_HANDLE; + image_info.imageView = propagation_images[image_index]; + image_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + VkWriteDescriptorSet& descriptor_write = descriptor_writes.emplace_back(); + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.pNext = nullptr; + descriptor_write.dstSet = this->propagation_descriptor_set[set_index]; + descriptor_write.dstBinding = binding; + descriptor_write.dstArrayElement = 0; + descriptor_write.descriptorCount = 1; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + descriptor_write.pImageInfo = &image_info; + descriptor_write.pBufferInfo = nullptr; + descriptor_write.pTexelBufferView = nullptr; + } + + for (uint32_t image_index = 0; image_index < this->distribution_geometry_image_view.size(); image_index++) + { + VkDescriptorImageInfo& image_info = image_infos.emplace_back(); + image_info.sampler = this->geometry_sampler; + image_info.imageView = this->distribution_geometry_image_view[image_index]; + image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet& descriptor_write = descriptor_writes.emplace_back(); + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.pNext = nullptr; + descriptor_write.dstSet = this->propagation_descriptor_set[set_index]; + descriptor_write.dstBinding = 9 + image_index; + descriptor_write.dstArrayElement = 0; + descriptor_write.descriptorCount = 1; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptor_write.pImageInfo = &image_info; + descriptor_write.pBufferInfo = nullptr; + descriptor_write.pTexelBufferView = nullptr; + } + + VkWriteDescriptorSet& propagation_domain_write = descriptor_writes.emplace_back(); + propagation_domain_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + propagation_domain_write.pNext = nullptr; + propagation_domain_write.dstSet = this->propagation_descriptor_set[set_index]; + propagation_domain_write.dstBinding = 11; + propagation_domain_write.dstArrayElement = 0; + propagation_domain_write.descriptorCount = 1; + propagation_domain_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + propagation_domain_write.pImageInfo = nullptr; + propagation_domain_write.pBufferInfo = this->domain_buffer->get_descriptor_info(); + propagation_domain_write.pTexelBufferView = nullptr; + } + + VkWriteDescriptorSet& indirect_domain_write = descriptor_writes.emplace_back(); + indirect_domain_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + indirect_domain_write.pNext = nullptr; + indirect_domain_write.dstSet = this->indirect_descriptor_set; + indirect_domain_write.dstBinding = 3; + indirect_domain_write.dstArrayElement = 0; + indirect_domain_write.descriptorCount = 1; + indirect_domain_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + indirect_domain_write.pImageInfo = nullptr; + indirect_domain_write.pBufferInfo = this->domain_buffer->get_descriptor_info(); + indirect_domain_write.pTexelBufferView = nullptr; + + VkWriteDescriptorSet& geometry_domain_write = descriptor_writes.emplace_back(); + geometry_domain_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + geometry_domain_write.pNext = nullptr; + geometry_domain_write.dstSet = this->geometry_descriptor_set; + geometry_domain_write.dstBinding = 0; + geometry_domain_write.dstArrayElement = 0; + geometry_domain_write.descriptorCount = 1; + geometry_domain_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + geometry_domain_write.pImageInfo = nullptr; + geometry_domain_write.pBufferInfo = this->domain_buffer->get_descriptor_info(); + geometry_domain_write.pTexelBufferView = nullptr; + + VkWriteDescriptorSet& injection_domain_write = descriptor_writes.emplace_back(); + injection_domain_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + injection_domain_write.pNext = nullptr; + injection_domain_write.dstSet = this->injection_descriptor_set; + injection_domain_write.dstBinding = 3; + injection_domain_write.dstArrayElement = 0; + injection_domain_write.descriptorCount = 1; + injection_domain_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + injection_domain_write.pImageInfo = nullptr; + injection_domain_write.pBufferInfo = this->domain_buffer->get_descriptor_info(); + injection_domain_write.pTexelBufferView = nullptr; + + vkUpdateDescriptorSets(device->get(), descriptor_writes.size(), descriptor_writes.data(), 0, nullptr); + + return true; +} + +bool IndirectCache::create_layouts(lava::device_ptr device, Scene::Ptr scene) +{ + VkPushConstantRange geometry_range; + geometry_range.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + geometry_range.offset = 0; + geometry_range.size = sizeof(uint32_t) * 3; + + this->geometry_layout = lava::make_pipeline_layout(); + this->geometry_layout->add(scene->get_mesh_descriptor()); //set 0: mesh descriptor + this->geometry_layout->add(this->geometry_descriptor); //set 1: geometry descriptor + this->geometry_layout->add(geometry_range); + + if (!this->geometry_layout->create(device)) + { + lava::log()->error("Can't create geometry pipeline layout for indirect cache!"); + + return false; + } + + VkPushConstantRange capture_range; + capture_range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + capture_range.offset = 0; + capture_range.size = sizeof(uint32_t) * 2; + + this->capture_layout = lava::make_pipeline_layout(); + this->capture_layout->add(scene->get_mesh_descriptor()); //set 0: mesh descriptor + this->capture_layout->add(scene->get_light_descriptor()); //set 1: light descriptor + this->capture_layout->add(scene->get_material_descriptor()); //set 2: material descriptor + this->capture_layout->add(capture_range); + + if (!this->capture_layout->create(device)) + { + lava::log()->error("Can't create capture pipeline layout for indirect cache!"); + + return false; + } + + VkPushConstantRange injection_range; + injection_range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + injection_range.offset = 0; + injection_range.size = sizeof(uint32_t) * 2 + sizeof(float); + + this->injection_layout = lava::make_pipeline_layout(); + this->injection_layout->add(scene->get_light_descriptor()); //set 0: light descriptor + this->injection_layout->add(this->injection_descriptor); //set 1: injection descriptor + this->injection_layout->add(injection_range); + + if (!this->injection_layout->create(device)) + { + lava::log()->error("Can't create injection pipeline layout for indirect cache!"); + + return false; + } + + this->propagation_layout = lava::make_pipeline_layout(); + this->propagation_layout->add(this->propagation_descriptor); //set 0: propagation descriptor + + if (!this->propagation_layout->create(device)) + { + lava::log()->error("Can't create propagation pipeline layout for indirect cache!"); + + return false; + } + + return true; +} + +bool IndirectCache::create_geometry_pass(lava::device_ptr device) +{ + lava::subpass::ptr subpass = lava::make_subpass(); + + this->geometry_pass = lava::make_render_pass(device); + this->geometry_pass->add(subpass); + + uint32_t voxel_max_dimension = glm::max(this->domain_voxel_resolution.x, glm::max(this->domain_voxel_resolution.y, this->domain_voxel_resolution.z)); + + lava::rect framebuffer_area = + { + glm::vec2(0.0f), + glm::vec2(voxel_max_dimension) + }; + + lava::VkImageViews framebuffer_views = {}; + + if (!this->geometry_pass->create({ framebuffer_views }, framebuffer_area)) + { + lava::log()->error("Can't create geometry pass for indirect cache!"); + + return false; + } + + return true; +} + +bool IndirectCache::create_geometry_pipeline(lava::device_ptr device) +{ + this->geometry_pipeline = lava::make_graphics_pipeline(device); + this->geometry_pipeline->set_layout(this->geometry_layout); + this->geometry_pipeline->set_auto_size(false); + this->geometry_pipeline->set_viewport_count(3); + this->geometry_pipeline->set_scissor_count(3); + + scene->set_vertex_input(this->geometry_pipeline.get()); + + if (!this->geometry_pipeline->add_shader(lava::file_data("dpr/binary/indirect_geometry_vertex.spirv"), VK_SHADER_STAGE_VERTEX_BIT)) + { + return false; + } + + if (!this->geometry_pipeline->add_shader(lava::file_data("dpr/binary/indirect_geometry_geometry.spirv"), VK_SHADER_STAGE_GEOMETRY_BIT)) + { + return false; + } + + if (!this->geometry_pipeline->add_shader(lava::file_data("dpr/binary/indirect_geometry_fragment.spirv"), VK_SHADER_STAGE_FRAGMENT_BIT)) + { + return false; + } + + this->geometry_pipeline->on_process = [this](VkCommandBuffer command_buffer) + { + this->pipeline_geometry(command_buffer); + }; + + if (!this->geometry_pipeline->create(this->geometry_pass->get())) + { + return false; + } + + this->geometry_pass->add_front(this->geometry_pipeline); + + return true; +} + +bool IndirectCache::create_capture_pass(lava::device_ptr device, const IndirectCacheSettings& settings) +{ + VkClearValue clear_depth; + clear_depth.depthStencil.depth = 1.0; + clear_depth.depthStencil.stencil = 0; + + VkClearValue clear_color; + clear_color.color.float32[0] = 0.0f; + clear_color.color.float32[1] = 0.0f; + clear_color.color.float32[2] = 0.0f; + clear_color.color.float32[3] = 0.0f; + + lava::attachment::ptr depth_attachment = lava::make_attachment(VK_FORMAT_D16_UNORM); + depth_attachment->set_op(VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE); + depth_attachment->set_stencil_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE); + depth_attachment->set_layouts(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + lava::attachment::ptr flux_attachment = lava::make_attachment(VK_FORMAT_R16G16B16A16_SFLOAT); + flux_attachment->set_op(VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE); + flux_attachment->set_stencil_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE); + flux_attachment->set_layouts(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + lava::attachment::ptr normal_attachment = lava::make_attachment(VK_FORMAT_R16G16_UNORM); + normal_attachment->set_op(VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE); + normal_attachment->set_stencil_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE); + normal_attachment->set_layouts(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + VkAttachmentReference flux_attachment_reference; + flux_attachment_reference.attachment = 1; + flux_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference normal_attachment_reference; + normal_attachment_reference.attachment = 2; + normal_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + lava::subpass::ptr subpass = lava::make_subpass(); + subpass->set_depth_stencil_attachment(0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + subpass->set_color_attachments( + { + flux_attachment_reference, + normal_attachment_reference + }); + + 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_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + subpass_begin_dependency->set_access_mask(0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + + lava::subpass_dependency::ptr subpass_end_dependency = lava::make_subpass_dependency(0, VK_SUBPASS_EXTERNAL); + subpass_end_dependency->set_stage_mask(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); + subpass_end_dependency->set_access_mask(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); + + this->capture_pass = lava::make_render_pass(device); + this->capture_pass->add(subpass); + this->capture_pass->add(subpass_begin_dependency); + this->capture_pass->add(subpass_end_dependency); + this->capture_pass->add(depth_attachment); //location 0: depth + this->capture_pass->add(flux_attachment); //location 1: flux + this->capture_pass->add(normal_attachment); //location 2: normal + this->capture_pass->set_clear_values( + { + clear_depth, + clear_color, + clear_color + }); + + lava::rect framebuffer_area = + { + glm::vec2(0.0f), + glm::vec2(settings.capture_resolution) + }; + + lava::VkImageViews framebuffer_views = + { + this->capture_depth_image->get_view(), + this->capture_flux_image->get_view(), + this->capture_normal_image->get_view() + }; + + if (!this->capture_pass->create({ framebuffer_views }, framebuffer_area)) + { + lava::log()->error("Can't create capture pass for indirect cache!"); + + return false; + } + + return true; +} + +bool IndirectCache::create_capture_pipeline(lava::device_ptr device) +{ + 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->capture_pipeline = lava::make_graphics_pipeline(device); + this->capture_pipeline->set_layout(this->capture_layout); + this->capture_pipeline->set_rasterization_front_face(VK_FRONT_FACE_CLOCKWISE); + this->capture_pipeline->set_rasterization_cull_mode(VK_CULL_MODE_BACK_BIT); + this->capture_pipeline->set_depth_test_and_write(true, true); + this->capture_pipeline->set_depth_compare_op(VK_COMPARE_OP_LESS); + this->capture_pipeline->add_color_blend_attachment(blend_state); + this->capture_pipeline->add_color_blend_attachment(blend_state); + + scene->set_vertex_input(this->capture_pipeline.get()); + + if (!this->capture_pipeline->add_shader(lava::file_data("dpr/binary/indirect_capture_vertex.spirv"), VK_SHADER_STAGE_VERTEX_BIT)) + { + return false; + } + + if (!this->capture_pipeline->add_shader(lava::file_data("dpr/binary/indirect_capture_fragment.spirv"), VK_SHADER_STAGE_FRAGMENT_BIT)) + { + return false; + } + + this->capture_pipeline->on_process = [this](VkCommandBuffer command_buffer) + { + this->pipeline_capture(command_buffer); + }; + + if (!this->capture_pipeline->create(this->capture_pass->get())) + { + return false; + } + + this->capture_pass->add_front(this->capture_pipeline); + + return true; +} + +bool IndirectCache::create_injection_pass(lava::device_ptr device) +{ + lava::attachment::ptr attachment_red = lava::make_attachment(VK_FORMAT_R16G16B16A16_SFLOAT); + attachment_red->set_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE); + attachment_red->set_stencil_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE); + attachment_red->set_layouts(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + lava::attachment::ptr attachment_green = lava::make_attachment(VK_FORMAT_R16G16B16A16_SFLOAT); + attachment_green->set_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE); + attachment_green->set_stencil_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE); + attachment_green->set_layouts(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + lava::attachment::ptr attachment_blue = lava::make_attachment(VK_FORMAT_R16G16B16A16_SFLOAT); + attachment_blue->set_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE); + attachment_blue->set_stencil_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE); + attachment_blue->set_layouts(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + VkAttachmentReference attachment_red_reference; + attachment_red_reference.attachment = 0; + attachment_red_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference attachment_green_reference; + attachment_green_reference.attachment = 1; + attachment_green_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference attachment_blue_reference; + attachment_blue_reference.attachment = 2; + attachment_blue_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + lava::subpass::ptr subpass = lava::make_subpass(); + subpass->set_color_attachments( + { + attachment_red_reference, + attachment_green_reference, + attachment_blue_reference + }); + + 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); + subpass_begin_dependency->set_access_mask(0, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + + 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_TRANSFER_BIT); + subpass_end_dependency->set_access_mask(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); + + this->injection_pass = lava::make_render_pass(device); + this->injection_pass->add(subpass); + this->injection_pass->add(subpass_begin_dependency); + this->injection_pass->add(subpass_end_dependency); + this->injection_pass->add(attachment_red); //location 0: distribution red + this->injection_pass->add(attachment_green); //location 1: distribution green + this->injection_pass->add(attachment_blue); //location 2: distribution blue + this->injection_pass->set_layers(this->domain_indirect_resolution.z); + + lava::rect framebuffer_area = + { + glm::vec2(0.0f), + glm::vec2(this->domain_indirect_resolution.x, this->domain_indirect_resolution.y) + }; + + lava::VkImageViews framebuffer_views = + { + this->distribution_red_image_array_view[0], + this->distribution_green_image_array_view[0], + this->distribution_blue_image_array_view[0], + }; + + if (!this->injection_pass->create({ framebuffer_views }, framebuffer_area)) + { + lava::log()->error("Can't create injection pass for indirect cache!"); + + return false; + } + + return true; +} + +bool IndirectCache::create_injection_pipeline(lava::device_ptr device) +{ + VkPipelineColorBlendAttachmentState blend_state; + blend_state.blendEnable = VK_TRUE; + blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + blend_state.colorBlendOp = VK_BLEND_OP_ADD; + blend_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + blend_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + 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->injection_pipeline = lava::make_graphics_pipeline(device); + this->injection_pipeline->set_layout(this->injection_layout); + this->injection_pipeline->set_input_assembly_topology(VK_PRIMITIVE_TOPOLOGY_POINT_LIST); + this->injection_pipeline->add_color_blend_attachment(blend_state); + this->injection_pipeline->add_color_blend_attachment(blend_state); + this->injection_pipeline->add_color_blend_attachment(blend_state); + + if (!this->injection_pipeline->add_shader(lava::file_data("dpr/binary/indirect_injection_vertex.spirv"), VK_SHADER_STAGE_VERTEX_BIT)) + { + return false; + } + + if (!this->injection_pipeline->add_shader(lava::file_data("dpr/binary/indirect_injection_geometry.spirv"), VK_SHADER_STAGE_GEOMETRY_BIT)) + { + return false; + } + + if (!this->injection_pipeline->add_shader(lava::file_data("dpr/binary/indirect_injection_fragment.spirv"), VK_SHADER_STAGE_FRAGMENT_BIT)) + { + return false; + } + + this->injection_pipeline->on_process = [this](VkCommandBuffer command_buffer) + { + this->pipeline_injection(command_buffer); + }; + + if (!this->injection_pipeline->create(this->injection_pass->get())) + { + return false; + } + + this->injection_pass->add_front(this->injection_pipeline); + + return true; +} + +bool IndirectCache::create_propagation_pipeline(lava::device_ptr device) +{ + this->propagation_pipeline = lava::make_compute_pipeline(device); + this->propagation_pipeline->set_layout(this->propagation_layout); + + if (!this->propagation_pipeline->set_shader_stage(lava::file_data("dpr/binary/indirect_propagation_compute.spirv"), VK_SHADER_STAGE_COMPUTE_BIT)) + { + return false; + } + + if (!this->propagation_pipeline->create()) + { + return false; + } + + return true; +} + +void IndirectCache::pipeline_geometry(VkCommandBuffer command_buffer) +{ + std::array<uint32_t, 3> push_constants; + push_constants[0] = this->domain_voxel_resolution.x; + push_constants[1] = this->domain_voxel_resolution.y; + push_constants[2] = this->domain_voxel_resolution.z; + + vkCmdPushConstants(command_buffer, this->geometry_layout->get(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(uint32_t) * push_constants.size(), push_constants.data()); + + std::array<glm::uvec2, 3> resolutions = + { + glm::uvec2(this->domain_voxel_resolution.y, this->domain_voxel_resolution.z), + glm::uvec2(this->domain_voxel_resolution.x, this->domain_voxel_resolution.z), + glm::uvec2(this->domain_voxel_resolution.x, this->domain_voxel_resolution.y) + }; + + std::array<VkViewport, 3> viewports; + std::array<VkRect2D, 3> scissors; + + for (uint32_t index = 0; index < resolutions.size(); index++) + { + viewports[index].x = 0.0f; + viewports[index].y = 0.0f; + viewports[index].width = resolutions[index].x; + viewports[index].height = resolutions[index].y; + viewports[index].minDepth = 0.0f; + viewports[index].maxDepth = 1.0f; + + scissors[index].offset.x = 0; + scissors[index].offset.y = 0; + scissors[index].extent.width = resolutions[index].x; + scissors[index].extent.height = resolutions[index].y; + } + + vkCmdSetViewport(command_buffer, 0, 3, viewports.data()); + vkCmdSetScissor(command_buffer, 0, 3, scissors.data()); + + this->geometry_layout->bind(command_buffer, this->geometry_descriptor_set, 1); + + for (const SceneMesh& mesh : this->scene->get_meshes()) + { + if (!mesh.cast_shadow) + { + continue; + } + + this->geometry_layout->bind(command_buffer, mesh.descriptor_set[this->frame], 0); + + mesh.mesh->bind_draw(command_buffer, this->domain_geometry_resolution.z); + } +} + +void IndirectCache::pipeline_capture(VkCommandBuffer command_buffer) +{ + std::array<uint32_t, 2> push_constants; + push_constants[0] = this->light_index; + push_constants[1] = this->settings.capture_resolution; + + vkCmdPushConstants(command_buffer, this->capture_layout->get(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(uint32_t) * push_constants.size(), push_constants.data()); + + this->capture_layout->bind(command_buffer, this->scene->get_light_descriptor_set(this->frame), 1); + + const std::vector<SceneMaterial>& materials = this->scene->get_materials(); + + for (const SceneMesh& mesh : this->scene->get_meshes()) + { + if (!mesh.cast_shadow) + { + continue; + } + + const SceneMaterial& material = materials[mesh.material_index]; + this->capture_layout->bind(command_buffer, mesh.descriptor_set[this->frame], 0); + this->capture_layout->bind(command_buffer, material.descriptor_set, 2); + + mesh.mesh->bind_draw(command_buffer); + } +} + +void IndirectCache::pipeline_injection(VkCommandBuffer command_buffer) +{ + std::array<uint32_t, 2> push_constants; + push_constants[0] = this->light_index; + push_constants[1] = this->settings.capture_resolution; + + float cell_size = this->settings.cell_size; + + vkCmdPushConstants(command_buffer, this->injection_layout->get(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(uint32_t) * push_constants.size(), push_constants.data()); + vkCmdPushConstants(command_buffer, this->injection_layout->get(), VK_SHADER_STAGE_VERTEX_BIT, sizeof(uint32_t) * push_constants.size(), sizeof(cell_size), &cell_size); + + this->injection_layout->bind(command_buffer, this->scene->get_light_descriptor_set(this->frame), 0); + this->injection_layout->bind(command_buffer, this->injection_descriptor_set, 1); + + uint32_t point_count = this->settings.capture_resolution * this->settings.capture_resolution; + vkCmdDraw(command_buffer, point_count, 1, 0, 0); +} + +VkImageMemoryBarrier IndirectCache::build_barrier(VkAccessFlags src_access, VkAccessFlags dst_access, VkImageLayout old_layout, VkImageLayout new_layout) +{ + VkImageMemoryBarrier barrier; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.pNext = nullptr; + barrier.srcAccessMask = src_access; + barrier.dstAccessMask = dst_access; + barrier.oldLayout = old_layout; + barrier.newLayout = new_layout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = VK_NULL_HANDLE; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + + return barrier; +} + +std::vector<VkImageMemoryBarrier> IndirectCache::build_barriers(uint32_t count, VkAccessFlags src_access, VkAccessFlags dst_access, VkImageLayout old_layout, VkImageLayout new_layout) +{ + return std::vector<VkImageMemoryBarrier>(count, IndirectCache::build_barrier(src_access, dst_access, old_layout, new_layout)); +} + +IndirectCache::Ptr make_indirect_cache() +{ + return std::make_shared<IndirectCache>(); +} \ No newline at end of file diff --git a/src/utility/indirect_cache.hpp b/src/utility/indirect_cache.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1f5563eb04e9fa4f5f50d9f0b32b2dc2c2a9724c --- /dev/null +++ b/src/utility/indirect_cache.hpp @@ -0,0 +1,165 @@ +/* + An indirect cache can be used in order to pre-compute indirect light transfer using light propagation volumes. + During the creation of the indirect cache it is possible to set the cell size that should be used for the propagation volumes with the help of the IndirectCacheSettings struct. + Before the indirect cache can be used, it is neccessary to call the function compute_indirect(...). + After that, the indirect cache can be used by ether adding it to a geometry buffer or by attaching the descriptor set of the indirect cache to a pipeline. + In order to use the indirect cache during forward shading, it is neccessary to bind the descriptor of the cache to the binding point that was specified in the fragment shader by the define INDIRECT_DESCRIPTOR_SET. + Example (Deferred): + + IndirectCache::Ptr indirect_cache = make_indirect_cache(); + indirect_cache->create(get_scene(), IndirectCacheSettings()); + + GeometryBuffer::Ptr geometry_buffer = make_geometry_buffer(); + geometry_buffer->create(output_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, get_stereo_transform()->get_descriptor(), get_scene(), {}, indirect_cache); //Add the indirect cache to geometry buffer + + //During Rendering + indirect_cache->compute_indirect(command_buffer, frame); //Compute indirect lighting + + render_pass->process(command_buffer, 0); //Fill geometry buffer + geometry_buffer->apply_lighting(command_buffer, per_frame_descriptor_set); //Compute lighting with indirect lighting + + Example (Forward): + + IndirectCache::Ptr indirect_cache = make_indirect_cache(); + indirect_cache->create(get_scene(), IndirectCacheSettings()); + + pipeline_layout->add(indirect_cache->get_descriptor()); //Same binding point as INDIRECT_DESCRIPTOR_SET + + //During Rendering + indirect_cache->compute_indirect(command_buffer, frame); //Compute indirect lighting + + pipeline_layout->bind(command_buffer, indirect_cache->get_descriptor_set(), 42); //Same binding point as INDIRECT_DESCRIPTOR_SET +*/ + +#pragma once +#include <liblava/lava.hpp> +#include <glm/glm.hpp> +#include <memory> +#include <vector> +#include <array> +#include <cstdint> + +#include "scene.hpp" + +struct IndirectCacheSettings +{ + uint32_t capture_resolution = 1024; + uint32_t voxel_resolution_scale = 1; //Needs to be greater or equal to one + float cell_size = 1.0f; //Where cell size is in meters +}; + +class IndirectCache +{ +public: + typedef std::shared_ptr<IndirectCache> Ptr; + +public: + IndirectCache() = default; + + bool create(Scene::Ptr scene, const IndirectCacheSettings& settings); + void destroy(); + + void compute_indirect(VkCommandBuffer command_buffer, lava::index frame); + + lava::descriptor::ptr get_descriptor() const; + VkDescriptorSet get_descriptor_set() const; + + const IndirectCacheSettings& get_settings() const; + +private: + bool compute_domain(Scene::Ptr scene, const IndirectCacheSettings& settings); + + bool create_buffers(lava::device_ptr device, const IndirectCacheSettings& settings); + bool create_images(lava::device_ptr device, const IndirectCacheSettings& settings); + bool create_samplers(lava::device_ptr device); + bool create_descriptors(lava::device_ptr device); + bool create_layouts(lava::device_ptr device, Scene::Ptr scene); + bool create_geometry_pass(lava::device_ptr device); + bool create_geometry_pipeline(lava::device_ptr device); + bool create_capture_pass(lava::device_ptr device, const IndirectCacheSettings& settings); + bool create_capture_pipeline(lava::device_ptr device); + bool create_injection_pass(lava::device_ptr device); + bool create_injection_pipeline(lava::device_ptr device); + bool create_propagation_pipeline(lava::device_ptr device); + + void pipeline_geometry(VkCommandBuffer command_buffer); + void pipeline_capture(VkCommandBuffer command_buffer); + void pipeline_injection(VkCommandBuffer command_buffer); + + static VkImageMemoryBarrier build_barrier(VkAccessFlags src_access, VkAccessFlags dst_access, VkImageLayout old_layout, VkImageLayout new_layout); + static std::vector<VkImageMemoryBarrier> build_barriers(uint32_t count, VkAccessFlags src_access, VkAccessFlags dst_access, VkImageLayout old_layout, VkImageLayout new_layout); + +private: + Scene::Ptr scene; + IndirectCacheSettings settings; + + uint32_t frame = 0; + uint32_t light_index = 0; + + uint32_t propagation_iterations = 0; + + glm::vec3 domain_min = glm::vec3(0.0f); + glm::vec3 domain_max = glm::vec3(0.0f); + glm::uvec3 domain_indirect_resolution = glm::uvec3(0); + glm::uvec3 domain_geometry_resolution = glm::uvec3(0); + glm::uvec3 domain_voxel_resolution = glm::uvec3(0); + glm::uvec3 domain_work_groups = glm::uvec3(0); + + lava::descriptor::pool::ptr descriptor_pool; + lava::descriptor::ptr indirect_descriptor; + lava::descriptor::ptr geometry_descriptor; + lava::descriptor::ptr injection_descriptor; + lava::descriptor::ptr propagation_descriptor; + + VkDescriptorSet indirect_descriptor_set = VK_NULL_HANDLE; + VkDescriptorSet geometry_descriptor_set = VK_NULL_HANDLE; + VkDescriptorSet injection_descriptor_set = VK_NULL_HANDLE; + std::array<VkDescriptorSet, 2> propagation_descriptor_set; + + lava::pipeline_layout::ptr geometry_layout; + lava::pipeline_layout::ptr capture_layout; + lava::pipeline_layout::ptr injection_layout; + lava::pipeline_layout::ptr propagation_layout; + + lava::render_pass::ptr geometry_pass; + lava::render_pass::ptr capture_pass; + lava::render_pass::ptr injection_pass; + + lava::graphics_pipeline::ptr geometry_pipeline; + lava::graphics_pipeline::ptr capture_pipeline; + lava::graphics_pipeline::ptr injection_pipeline; + lava::compute_pipeline::ptr propagation_pipeline; + + VkSampler indirect_sampler = VK_NULL_HANDLE; + VkSampler geometry_sampler = VK_NULL_HANDLE; + VkSampler injection_sampler = VK_NULL_HANDLE; + + lava::image::ptr capture_depth_image; + lava::image::ptr capture_flux_image; + lava::image::ptr capture_normal_image; + + //NOTE: Two images for the propagation and one image for the accumulation + std::array<VmaAllocation, 3> distribution_red_image_memory; + std::array<VmaAllocation, 3> distribution_green_image_memory; + std::array<VmaAllocation, 3> distribution_blue_image_memory; + std::array<VmaAllocation, 2> distribution_geometry_image_memory; + + std::array<VkImage, 3> distribution_red_image; + std::array<VkImage, 3> distribution_green_image; + std::array<VkImage, 3> distribution_blue_image; + std::array<VkImage, 2> distribution_geometry_image; + + std::array<VkImageView, 3> distribution_red_image_view; + std::array<VkImageView, 3> distribution_green_image_view; + std::array<VkImageView, 3> distribution_blue_image_view; + std::array<VkImageView, 2> distribution_geometry_image_view; + + std::array<VkImageView, 3> distribution_red_image_array_view; + std::array<VkImageView, 3> distribution_green_image_array_view; + std::array<VkImageView, 3> distribution_blue_image_array_view; + std::array<VkImageView, 2> distribution_geometry_image_integer_view; + + lava::buffer::ptr domain_buffer; +}; + +IndirectCache::Ptr make_indirect_cache(); \ No newline at end of file diff --git a/src/utility/shadow_cache.hpp b/src/utility/shadow_cache.hpp index 895bbe73fa6e2bbb2220f745b90f156e0ef758d2..d0969a99306697919ed5d2091f293eac0bedb2a9 100644 --- a/src/utility/shadow_cache.hpp +++ b/src/utility/shadow_cache.hpp @@ -16,7 +16,7 @@ shadow_cache->compute_shadow(command_buffer, frame); //Generate shadow maps render_pass->process(command_buffer, 0); //Fill geometry buffer - geometry_buffer->apply_local_lighting(command_buffer, per_frame_descriptor_set); //Compute local lighting with shadows + geometry_buffer->apply_lighting(command_buffer, per_frame_descriptor_set); //Compute lighting with shadows Example (Forward): diff --git a/src/vr_application.cpp b/src/vr_application.cpp index 98860f5b13c6ebdb9700576959598955b0435df3..c1e56d438310a074c508522cc6a32c69af7ef0d0 100644 --- a/src/vr_application.cpp +++ b/src/vr_application.cpp @@ -68,6 +68,7 @@ bool VRApplication::setup(lava::name name, argh::parser cmd_line) parameters.features.geometryShader = true; parameters.features.tessellationShader = true; parameters.features.imageCubeArray = true; + parameters.features.fragmentStoresAndAtomics = true; }; this->app->on_create = [this]() @@ -166,6 +167,16 @@ StereoTransform::Ptr VRApplication::get_stereo_transform() const return this->stereo_transform; } +ShadowCache::Ptr VRApplication::get_shadow_cache() const +{ + return this->shadow_cache; +} + +IndirectCache::Ptr VRApplication::get_indirect_cache() const +{ + return this->indirect_cache; +} + lava::device_ptr VRApplication::get_device() const { return this->app->device; @@ -418,6 +429,24 @@ bool VRApplication::on_create() return false; } + this->shadow_cache = make_shadow_cache(); + + if (!this->shadow_cache->create(this->scene, ShadowCacheSettings())) + { + lava::log()->error("Can't create shadow cache!"); + + return false; + } + + this->indirect_cache = make_indirect_cache(); + + if (!this->indirect_cache->create(this->scene, IndirectCacheSettings())) + { + lava::log()->error("Can't create indirect cache!"); + + return false; + } + this->frame_time = make_statistic("Frame Time", 128); return this->create_config(); @@ -427,12 +456,27 @@ void VRApplication::on_destroy() { this->destroy_config(); + if (this->headset != nullptr) + { + this->headset->on_shutdown(); + } + if (this->pass_timer != nullptr) { this->pass_timer->write_to_file(this->app->device); this->pass_timer->destroy(this->app->device); } + if (this->indirect_cache != nullptr) + { + this->indirect_cache->destroy(); + } + + if (this->shadow_cache != nullptr) + { + this->shadow_cache->destroy(); + } + if (this->stereo_transform != nullptr) { this->stereo_transform->destroy(); @@ -604,7 +648,7 @@ bool VRApplication::on_update(lava::delta delta_time) this->stereo_transform->set_projection_matrix(EYE_LEFT, this->headset->get_projection_matrix(EYE_LEFT)); this->stereo_transform->set_projection_matrix(EYE_RIGHT, this->headset->get_projection_matrix(EYE_RIGHT)); - if (this->scene->is_animation_active()) + if (this->scene->is_animation_active() && this->scene->is_camera_active()) { this->stereo_transform->set_view_matrix(this->scene->get_animation_view_matrix()); } @@ -634,6 +678,14 @@ bool VRApplication::on_render(VkCommandBuffer command_buffer, lava::index frame) this->scene->write(frame); this->stereo_transform->write(frame); + if (!this->cache_filled) + { + this->shadow_cache->compute_shadow(command_buffer, frame); + this->indirect_cache->compute_indirect(command_buffer, frame); + + this->cache_filled = true; + } + this->begin_render(command_buffer, frame); StereoStrategy::Ptr strategy = this->get_stereo_strategy(); diff --git a/src/vr_application.hpp b/src/vr_application.hpp index a66273e357a903703b8312fb9be4c788ed9fd7c1..5ff7ab6275800a475f67cd4a956a6e8950cebd46 100644 --- a/src/vr_application.hpp +++ b/src/vr_application.hpp @@ -18,6 +18,8 @@ #include "utility/pass_timer.hpp" #include "utility/stereo_transform.hpp" #include "utility/statistic.hpp" +#include "utility/shadow_cache.hpp" +#include "utility/indirect_cache.hpp" class VRApplication { @@ -41,6 +43,9 @@ public: FrameCapture::Ptr get_frame_capture() const; StereoTransform::Ptr get_stereo_transform() const; + ShadowCache::Ptr get_shadow_cache() const; + IndirectCache::Ptr get_indirect_cache() const; + lava::device_ptr get_device() const; lava::render_target::ptr get_target() const; @@ -83,6 +88,10 @@ private: FrameCapture::Ptr frame_capture; StereoTransform::Ptr stereo_transform; + ShadowCache::Ptr shadow_cache; + IndirectCache::Ptr indirect_cache; + bool cache_filled = false; + CommandParser command_parser; bool created = false;