diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a4788b92f122add4d6188a4e73ee4724dd77f54..5adb0e036fae93cc97b25643a8d9983ad1c363ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -714,6 +714,7 @@ 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/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 @@ -749,9 +750,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/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/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..8cc6515b286088406309783d789ace779181da9a 100644 --- a/res/dpr/utility/material_library.glsl +++ b/res/dpr/utility/material_library.glsl @@ -69,4 +69,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 ba0cd0ef17eaf6559304b2ab430ccdcbbb5222c8..9ef4bf68a9f7d2c796365b8a0f3aacee1618fb73 100644 --- a/src/headset/emulated_headset.cpp +++ b/src/headset/emulated_headset.cpp @@ -136,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(); } diff --git a/src/scene.cpp b/src/scene.cpp index aae07fc9e6018383978693c46e5e597b222db877..d7ecaf573f07ab1e7e499b8f6c5f31e0b0fde94a 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -478,6 +478,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 +533,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) { @@ -641,7 +652,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 +822,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 +888,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; } @@ -1043,9 +1054,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 +1190,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 +1206,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 +1235,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 +1285,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; @@ -1356,11 +1379,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 +1453,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 +1488,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 +1533,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 +1560,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..c96b6fbe4cf2b13095daf0b2ccfba7e892445a68 100644 --- a/src/scene.hpp +++ b/src/scene.hpp @@ -79,6 +79,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); @@ -161,6 +162,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); @@ -181,7 +185,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); @@ -212,8 +216,8 @@ private: 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; diff --git a/src/strategy/multi_view_stereo.cpp b/src/strategy/multi_view_stereo.cpp index 110b288bbc0fc9925d55bf74a008869143d2b6bc..577df49f78eb119abfaa3ce89249f3d9fec7470b 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(); @@ -88,10 +76,6 @@ bool MultiViewStereo::on_render(VkCommandBuffer command_buffer, lava::index fram { this->get_headset()->submit_metadata(FrameMetadata()); - this->get_pass_timer()->begin_pass(command_buffer, "shadow"); - this->shadow_cache->compute_shadow(command_buffer, frame); - this->get_pass_timer()->end_pass(command_buffer); - this->get_pass_timer()->begin_pass(command_buffer, "multi_eye"); this->render_pass->process(command_buffer, 0); this->get_pass_timer()->end_pass(command_buffer); @@ -125,7 +109,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())) { @@ -295,7 +280,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 53805d2c7145265d8516717ac4cb595a7017f12b..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,7 +35,6 @@ private: private: VkPhysicalDeviceMultiviewFeatures features; - ShadowCache::Ptr shadow_cache; lava::pipeline_layout::ptr pipeline_layout; lava::graphics_pipeline::ptr pipeline; diff --git a/src/strategy/native_stereo_deferred.cpp b/src/strategy/native_stereo_deferred.cpp index d8e5b1bac31823e1fa07caa0daddbc6e55373b8e..d891225c0fbc349a36efabe708bb063a0c312e42 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(); @@ -83,16 +71,12 @@ bool NativeStereoDeferred::on_render(VkCommandBuffer command_buffer, lava::index { this->get_headset()->submit_metadata(FrameMetadata()); - this->get_pass_timer()->begin_pass(command_buffer, "shadow"); - this->shadow_cache->compute_shadow(command_buffer, frame); - this->get_pass_timer()->end_pass(command_buffer); - this->get_pass_timer()->begin_pass(command_buffer, "left_eye"); this->eye_passes[0].render_pass->process(command_buffer, 0); 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"); @@ -104,7 +88,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"); @@ -145,7 +129,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 4547c9645e3a570844b1d920cd1583f629ce76d1..41b2e4ed9fe767f841bd1fc9b91dfb6838f8b800 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(); @@ -83,10 +71,6 @@ bool NativeStereoForward::on_render(VkCommandBuffer command_buffer, lava::index { this->get_headset()->submit_metadata(FrameMetadata()); - this->get_pass_timer()->begin_pass(command_buffer, "shadow"); - this->shadow_cache->compute_shadow(command_buffer, frame); - this->get_pass_timer()->end_pass(command_buffer); - this->get_pass_timer()->begin_pass(command_buffer, "left_eye"); this->eye_passes[0].render_pass->process(command_buffer, 0); this->get_pass_timer()->end_pass(command_buffer); @@ -123,7 +107,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())) { @@ -284,7 +269,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 d9121e1721c96fe76d94cc4c2262c29bc73c6805..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) { 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..cab6af7e537731e0c99a7037244e07a29935cfce 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): @@ -40,7 +40,7 @@ struct ShadowCacheSettings { - uint32_t resolution = 1024; + uint32_t resolution = 2048; bool use_directional = true; bool use_spot = true; diff --git a/src/vr_application.cpp b/src/vr_application.cpp index 98860f5b13c6ebdb9700576959598955b0435df3..f719dc9be530dc91a264bc5f0e74b8d970219666 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(); @@ -433,6 +462,16 @@ void VRApplication::on_destroy() 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(); @@ -634,6 +673,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;