├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LISCENSE-MIT ├── README.md ├── assets ├── glsl │ ├── ao.rchit │ ├── ao.rgen │ ├── ao.rmiss │ ├── cube.frag │ ├── cube.vert │ ├── debug.rchit │ ├── debug.rgen │ ├── debug.rmiss │ ├── model.frag │ ├── model.vert │ ├── pathtrace.rchit │ ├── pathtrace.rgen │ ├── pathtrace.rmiss │ ├── payload.glsl │ ├── postprocess.glsl │ ├── sampling.glsl │ ├── triangle.frag │ └── triangle.vert ├── models │ ├── Duck.gltf │ ├── Duck0.bin │ ├── DuckCM.png │ ├── ToyCar.glb │ ├── cornell.gltf │ ├── cornell_copyright.txt │ ├── tunnel.gltf │ └── tunnel_data.bin └── textures │ ├── HDR_RGBA_0.png │ └── face.png ├── examples ├── 1-cube.rs ├── 2-model.rs ├── 3-ray-debug.rs ├── 4-ray-ao.rs └── 5-pathtrace.rs ├── rustfmt.toml └── src ├── buffer.rs ├── context.rs ├── descriptor.rs ├── lib.rs ├── pipeline.rs ├── pools.rs ├── prelude.rs ├── ray ├── acceleration.rs ├── mod.rs ├── pipeline.rs └── sbt.rs ├── renderer.rs ├── renderpass.rs ├── scene ├── camera.rs ├── mesh.rs └── mod.rs ├── swapchain.rs ├── texture.rs ├── util.rs └── window.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | target/ 3 | *.spv 4 | .idea/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sol" 3 | version = "0.1.0" 4 | authors = ["Eric RH "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | winit = "0.26.1" 9 | image = "0.24.1" 10 | ash = "0.36.0" 11 | ash-window = "0.9.0" 12 | gpu-allocator = "0.18.0" 13 | shaderc = "0.7" 14 | glam = { version = "0.20.2", features = ["serde"] } 15 | gltf = "1.0.0" 16 | 17 | # [profile.release] 18 | # debug = true -------------------------------------------------------------------------------- /LISCENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 sol-rs 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | _! To prevent work policy violations, this project is now archived. !_ 2 | 3 | # sol-rs ☀ 4 | 5 | `sol-rs` is a small rendering toolkit for Vulkan, with a focus on real-time raytracing (which is not currently available via other APIs such as WebGPU). It hosts convenience wrappers but also exposes [ash](https://github.com/MaikKlein/ash) directly. Tested on Windows/NVIDIA, but the non-raytracing samples also work on Mac via MoltenVK. 6 | 7 | However, this remains a personal sandbox for learning and experimentation so use at your own risk! 8 | 9 | ## Requirements 10 | [LunarG Vulkan SDK](https://lunarg.com/vulkan-sdk/) installation. 11 | 12 | On Windows, setting a `VULKAN_SDK` environment variable to the 1.3.204.1 SDK installation folder should prevent the need for building shaderc from source. Otherwise, additional installations such as [Ninja](https://ninja-build.org/) may be required. Please refer to the following [instructions](https://github.com/google/shaderc-rs/blob/master/README.md) if you run into shaderc-related issues. 13 | 14 | ## Examples 15 | 16 | [![screenshot](https://i.imgur.com/kFc6nr3.png)](https://github.com/num3ric/sol-rs/blob/master/examples/5-pathtrace.rs) 17 | `cargo run --release --example 5-pathtrace -- --model models/tunnel.gltf --sky` 18 | 19 | 20 | [![screenshot](https://i.imgur.com/uW3Tm4e.png)](https://github.com/num3ric/sol-rs/blob/master/examples/5-pathtrace.rs) 21 | `cargo run --release --example 5-pathtrace -- --model models/cornell.gltf` 22 | 23 | 24 | [![screenshot](https://i.imgur.com/yC1x7EZ.png)](https://github.com/num3ric/sol-rs/blob/master/examples/4-ray-ao.rs) 25 | `cargo run --release --example 4-ray-ao` 26 | 27 | 28 | [![screenshot](https://i.imgur.com/R72zQ5N.png)](https://github.com/num3ric/sol-rs/blob/master/examples/1-cube.rs) 29 | `cargo run --release --example 1-cube` 30 | 31 | 32 | ## References 33 | * [Vulkan-Samples](https://github.com/KhronosGroup/Vulkan-Samples) 34 | * [gltf-viewer-rs](https://github.com/adrien-ben/gltf-viewer-rs) 35 | * [vkex](https://github.com/chaoticbob/vkex) 36 | * [nvpro-samples](https://github.com/nvpro-samples) 37 | * [metal-ray-tracer](https://sergeyreznik.github.io/metal-ray-tracer/index.html) 38 | * [Google Dawn](https://dawn.googlesource.com/dawn/+/refs/heads/main) 39 | * [Cinder](https://github.com/cinder/Cinder) 40 | * [Nannou](https://github.com/nannou-org/nannou) 41 | -------------------------------------------------------------------------------- /assets/glsl/ao.rchit: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : require 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | #extension GL_EXT_scalar_block_layout : enable 5 | #extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable 6 | #include "payload.glsl" 7 | #include "sampling.glsl" 8 | 9 | struct ModelVertex { 10 | vec4 pos; 11 | vec4 color; 12 | vec4 normal; 13 | vec4 uv; 14 | }; 15 | 16 | struct SceneInstance 17 | { 18 | int id; 19 | int texture_offset; 20 | vec2 padding; 21 | mat4 transform; 22 | mat4 transform_it; 23 | }; 24 | 25 | layout(set = 0, binding = 0) uniform Scene { 26 | mat4 model; 27 | mat4 view; 28 | mat4 view_inverse; 29 | mat4 projection; 30 | mat4 projection_inverse; 31 | mat4 model_view_projection; 32 | uvec3 frame; 33 | } scene; 34 | 35 | layout(set = 1, binding = 2) uniform sampler2D blueNoise; 36 | layout(set = 1, binding = 3, scalar) buffer ScnDesc { SceneInstance i[]; } scnDesc; 37 | layout(set = 1, binding = 4, scalar) buffer Vertices { ModelVertex v[]; } vertices[]; 38 | layout(set = 1, binding = 5) buffer Indices { uint64_t i[]; } indices[]; 39 | 40 | layout(location = 0) rayPayloadInEXT Payload prd; 41 | 42 | hitAttributeEXT vec3 attribs; 43 | 44 | 45 | vec2 getBlueRand2( uint i ) 46 | { 47 | ivec2 texSize = textureSize( blueNoise, 0 ).xy; 48 | ivec2 blueCoord = ivec2( mod( gl_LaunchIDEXT.xy + nextRand2(prd.rng) * texSize, vec2( texSize ) ) ); 49 | vec4 blue = texelFetch( blueNoise, blueCoord, 0 ); 50 | return vec2( blue[i%4], blue[(i+1)%4] ); 51 | } 52 | 53 | void main() 54 | { 55 | // Object of this instance 56 | uint objId = scnDesc.i[gl_InstanceID].id; 57 | 58 | // Indices of the triangle 59 | ivec3 ind = ivec3(indices[objId].i[3 * gl_PrimitiveID + 0], // 60 | indices[objId].i[3 * gl_PrimitiveID + 1], // 61 | indices[objId].i[3 * gl_PrimitiveID + 2]); // 62 | // Vertex of the triangle 63 | ModelVertex v0 = vertices[objId].v[ind.x]; 64 | ModelVertex v1 = vertices[objId].v[ind.y]; 65 | ModelVertex v2 = vertices[objId].v[ind.z]; 66 | 67 | const vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y); 68 | 69 | // Computing the normal at hit position 70 | vec3 normal = v0.normal.xyz * barycentrics.x + v1.normal.xyz * barycentrics.y + v2.normal.xyz * barycentrics.z; 71 | // Transforming the normal to world space 72 | normal = normalize(vec3(scnDesc.i[gl_InstanceID].transform_it * vec4(normal, 0.0))); 73 | 74 | // Computing the coordinates of the hit position 75 | vec3 world_pos = v0.pos.xyz * barycentrics.x + v1.pos.xyz * barycentrics.y + v2.pos.xyz * barycentrics.z; 76 | // Transforming the position to world space 77 | world_pos = vec3(scnDesc.i[gl_InstanceID].transform * vec4(world_pos, 1.0)); 78 | 79 | prd.rayOrigin = world_pos + 0.00001 * gl_WorldRayDirectionEXT; 80 | vec2 Xi = getBlueRand2( prd.depth + prd.depth * prd.sampleId ); 81 | vec3 hitNorm = normal * sign(dot(-gl_WorldRayDirectionEXT, normal)); 82 | prd.rayDir = sampleCosineWeightedHemisphere(hitNorm, Xi); 83 | prd.rayRange = vec2(0.001f, 10.0f); 84 | if( prd.depth > 0 ){ 85 | prd.hitValue += vec3(1.0); 86 | } 87 | prd.depth++; 88 | } 89 | -------------------------------------------------------------------------------- /assets/glsl/ao.rgen: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : require 3 | 4 | #include "payload.glsl" 5 | #include "sampling.glsl" 6 | 7 | const bool DO_ACCUMULATION = true; 8 | layout(push_constant) uniform PushConstant { 9 | int accum_start_frame; 10 | } push; 11 | 12 | layout(set = 0, binding = 0) uniform Scene { 13 | mat4 model; 14 | mat4 view; 15 | mat4 view_inverse; 16 | mat4 projection; 17 | mat4 projection_inverse; 18 | mat4 model_view_projection; 19 | uvec3 frame; 20 | } scene; 21 | layout(set = 1, binding = 0) uniform accelerationStructureEXT topLevelAS; 22 | layout(set = 1, binding = 1, rgba8) uniform image2D image; 23 | 24 | layout(location = 0) rayPayloadEXT Payload prd; 25 | 26 | void preparePayload( inout Payload prd, vec3 origin, vec3 direction ) 27 | { 28 | prd.hitValue = vec3(0); 29 | prd.depth = 0; 30 | prd.done = 0; 31 | prd.rayOrigin = origin; 32 | prd.rayDir = direction; 33 | prd.rayRange = vec2(max(1.0f, length(origin.xyz)) * 1e-3f, 10000.0f); 34 | prd.roughness = 0; 35 | } 36 | 37 | void main() 38 | { 39 | uint rayFlags = gl_RayFlagsOpaqueEXT; 40 | //float tmin = max(1.0f, length(origin.xyz)) * 1e-3f; 41 | float tmin = 0.001f; 42 | int max_samples = 4; 43 | int sample_count = 4; 44 | vec3 ao = vec3(0); 45 | prd.rng = tea( gl_LaunchIDEXT.x + gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x, scene.frame.z ); 46 | for( int i=0; i max_samples ) 72 | break; 73 | } 74 | ao += 1.0f/float(max_samples) * prd.hitValue; 75 | } 76 | vec3 color = vec3(1.0f) - ao/float(sample_count); 77 | if( DO_ACCUMULATION ) { 78 | float a = 1.0f / float(scene.frame.z - push.accum_start_frame + 1); 79 | vec3 old_color = imageLoad(image, ivec2(gl_LaunchIDEXT.xy)).xyz; 80 | color = mix(old_color, color, a); 81 | } 82 | imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(color, 1.0f)); 83 | } 84 | -------------------------------------------------------------------------------- /assets/glsl/ao.rmiss: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : require 3 | #include "payload.glsl" 4 | 5 | layout(location = 0) rayPayloadInEXT Payload prd; 6 | 7 | void main() 8 | { 9 | prd.done = 1; 10 | } -------------------------------------------------------------------------------- /assets/glsl/cube.frag: -------------------------------------------------------------------------------- 1 | #version 400 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | 5 | layout(set = 0, binding = 1) uniform sampler2D uTexture; 6 | 7 | layout (location = 0) in vec4 color; 8 | layout (location = 1) in vec2 uv; 9 | layout (location = 0) out vec4 outColor; 10 | 11 | void main() { 12 | outColor = color + 0.25 * texture(uTexture, uv); 13 | } 14 | -------------------------------------------------------------------------------- /assets/glsl/cube.vert: -------------------------------------------------------------------------------- 1 | #version 400 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | 5 | layout(set = 0, binding = 0) uniform Scene { 6 | mat4 mvp; 7 | } scene; 8 | 9 | layout (location = 0) in vec4 pos; 10 | layout (location = 1) in vec4 inColor; 11 | layout (location = 2) in vec2 inUv; 12 | 13 | layout (location = 0) out vec4 outColor; 14 | layout (location = 1) out vec2 outUv; 15 | 16 | void main() { 17 | outColor = inColor; 18 | outUv = inUv; 19 | gl_Position = scene.mvp * pos; 20 | } -------------------------------------------------------------------------------- /assets/glsl/debug.rchit: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : require 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | #extension GL_EXT_scalar_block_layout : enable 5 | 6 | layout(location = 0) rayPayloadInEXT vec3 hitValue; 7 | hitAttributeEXT vec3 attribs; 8 | 9 | void main() 10 | { 11 | const vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y); 12 | hitValue = barycentrics; 13 | } -------------------------------------------------------------------------------- /assets/glsl/debug.rgen: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : require 3 | 4 | layout(set = 0, binding = 0) uniform Scene { 5 | mat4 model; 6 | mat4 view; 7 | mat4 view_inverse; 8 | mat4 projection; 9 | mat4 projection_inverse; 10 | mat4 model_view_projection; 11 | uvec3 frame; 12 | } scene; 13 | layout(set = 1, binding = 0) uniform accelerationStructureEXT topLevelAS; 14 | layout(set = 1, binding = 1, rgba8) uniform image2D image; 15 | 16 | layout(location = 0) rayPayloadEXT vec3 hitValue; 17 | 18 | void main() 19 | { 20 | const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5); 21 | const vec2 inUV = pixelCenter/vec2(gl_LaunchSizeEXT.xy); 22 | vec2 d = inUV * 2.0 - 1.0; 23 | 24 | vec4 origin = scene.view_inverse * vec4(0,0,0,1); 25 | vec4 target = scene.projection_inverse * vec4(d.x, d.y, 1, 1); 26 | vec4 direction = scene.view_inverse * vec4(normalize(target.xyz), 0); 27 | 28 | hitValue = direction.xyz; 29 | 30 | uint rayFlags = gl_RayFlagsOpaqueEXT; 31 | uint cullMask = 0xff; 32 | float tmin = 0.001; 33 | float tmax = 1000.0; 34 | traceRayEXT(topLevelAS, rayFlags, cullMask, 0 /*sbtRecordOffset*/, 0 /*sbtRecordStride*/, 0 /*missIndex*/, origin.xyz, tmin, direction.xyz, tmax, 0 /*payload*/); 35 | 36 | imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitValue, 0.0)); 37 | } -------------------------------------------------------------------------------- /assets/glsl/debug.rmiss: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : require 3 | 4 | layout(location = 0) rayPayloadInEXT vec3 hitValue; 5 | 6 | void main() 7 | { 8 | //hitValue = abs(hitValue);//vec3(0.5); 9 | } -------------------------------------------------------------------------------- /assets/glsl/model.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout (location = 0) in vec3 inNormal; 5 | layout (location = 1) in vec4 inColor; 6 | layout (location = 0) out vec4 outColor; 7 | 8 | void main() { 9 | float light = clamp(dot(normalize(inNormal), vec3(0,0,1)),0,1); 10 | outColor = light * inColor; 11 | } 12 | -------------------------------------------------------------------------------- /assets/glsl/model.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(set = 0, binding = 0) uniform Scene { 5 | mat4 mvp; 6 | mat4 normal; 7 | } scene; 8 | 9 | layout (location = 0) in vec4 pos; 10 | layout (location = 1) in vec4 inColor; 11 | layout (location = 2) in vec4 inNormal; 12 | layout (location = 3) in vec2 inUv; 13 | 14 | layout (location = 0) out vec3 outNormal; 15 | layout (location = 1) out vec4 outColor; 16 | 17 | void main() { 18 | outColor = inColor; 19 | outNormal = mat3(scene.normal) * inNormal.xyz; 20 | gl_Position = scene.mvp * pos; 21 | } -------------------------------------------------------------------------------- /assets/glsl/pathtrace.rchit: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : require 3 | #extension GL_EXT_nonuniform_qualifier : enable 4 | #extension GL_EXT_scalar_block_layout : enable 5 | #extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable 6 | #include "payload.glsl" 7 | #include "sampling.glsl" 8 | 9 | //TODO: https://github.com/nvpro-samples/vk_denoise/blob/master/shaders/pathtrace.rchit 10 | 11 | struct ModelVertex { 12 | vec4 pos; 13 | vec4 color; 14 | vec4 normal; 15 | vec4 uv; 16 | }; 17 | 18 | struct MaterialInfo { 19 | vec4 base_color; 20 | vec3 emissive; 21 | float padding0; 22 | float metallic; 23 | float roughness; 24 | float padding1; 25 | float padding2; 26 | }; 27 | 28 | struct SceneInstance 29 | { 30 | int id; 31 | int texture_offset; 32 | vec2 padding; 33 | mat4 transform; 34 | mat4 transform_it; 35 | }; 36 | 37 | layout(set = 0, binding = 0) uniform Scene { 38 | mat4 model; 39 | mat4 view; 40 | mat4 view_inverse; 41 | mat4 projection; 42 | mat4 projection_inverse; 43 | mat4 model_view_projection; 44 | uvec3 frame; 45 | } scene; 46 | 47 | layout(set = 1, binding = 3, scalar) buffer ScnDesc { SceneInstance i[]; } scnDesc; 48 | layout(set = 1, binding = 4, scalar) buffer Vertices { ModelVertex v[]; } vertices[]; 49 | layout(set = 1, binding = 5) buffer Indices { uint64_t i[]; } indices[]; 50 | layout(set = 1, binding = 6, scalar) buffer MatBuffer { MaterialInfo mat; } materials[]; 51 | 52 | layout(location = 0) rayPayloadInEXT Payload prd; 53 | 54 | hitAttributeEXT vec3 attribs; 55 | 56 | void main() 57 | { 58 | // Object of this instance 59 | uint objId = scnDesc.i[gl_InstanceID].id; 60 | // Indices of the triangle 61 | ivec3 ind = ivec3(indices[objId].i[3 * gl_PrimitiveID + 0], 62 | indices[objId].i[3 * gl_PrimitiveID + 1], 63 | indices[objId].i[3 * gl_PrimitiveID + 2]); 64 | // Vertex of the triangle 65 | ModelVertex v0 = vertices[objId].v[ind.x]; 66 | ModelVertex v1 = vertices[objId].v[ind.y]; 67 | ModelVertex v2 = vertices[objId].v[ind.z]; 68 | 69 | MaterialInfo mat = materials[gl_InstanceID].mat; 70 | 71 | if(mat.emissive.r >= 1.0 || mat.emissive.g >= 1.0 || mat.emissive.b >= 1.0) { 72 | prd.hitValue = mat.emissive; 73 | prd.done = 1; 74 | prd.depth++; 75 | return; 76 | } 77 | 78 | const vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y); 79 | // Computing the normal at hit position 80 | vec3 normal = v0.normal.xyz * barycentrics.x + v1.normal.xyz * barycentrics.y + v2.normal.xyz * barycentrics.z; 81 | // Transforming the normal to world space 82 | normal = normalize(vec3(scnDesc.i[gl_InstanceID].transform_it * vec4(normal, 0.0))); 83 | // Computing the coordinates of the hit position 84 | vec3 worldPos = v0.pos.xyz * barycentrics.x + v1.pos.xyz * barycentrics.y + v2.pos.xyz * barycentrics.z; 85 | // Transforming the position to world space 86 | worldPos = vec3(scnDesc.i[gl_InstanceID].transform * vec4(worldPos, 1.0)); 87 | 88 | vec3 vertex_color = v0.color.xyz * barycentrics.x + v1.color.xyz * barycentrics.y + v2.color.xyz * barycentrics.z; 89 | 90 | vec3 wI = normalize(gl_WorldRayDirectionEXT); 91 | vec3 nO = normal * sign( dot(normal, -wI) ); 92 | float alphaSquared = mat.roughness * mat.roughness; 93 | vec2 Xi = nextRand2(prd.rng); 94 | float rand = nextRand(prd.rng); 95 | 96 | prd.rayOrigin = worldPos + 0.0001 * nO; 97 | if( rand < mat.metallic ) { 98 | prd.rayDir = sampleGGXDistribution(reflect(gl_WorldRayDirectionEXT, nO), Xi, alphaSquared); 99 | prd.hitValue = mat.base_color.xyz * vertex_color; 100 | } 101 | else { 102 | vec3 m = sampleGGXDistribution(nO, Xi, alphaSquared); 103 | if( rand < fresnelDielectric(nO, m, 1.0/1.5) ) { 104 | prd.rayDir = reflect(gl_WorldRayDirectionEXT, m); 105 | prd.hitValue = vec3(1.0); 106 | } 107 | else { 108 | prd.rayDir = sampleCosineWeightedHemisphere(nO, Xi); 109 | prd.hitValue = mat.base_color.xyz * vertex_color; 110 | } 111 | } 112 | prd.depth++; 113 | } 114 | -------------------------------------------------------------------------------- /assets/glsl/pathtrace.rgen: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : require 3 | 4 | #include "payload.glsl" 5 | #include "sampling.glsl" 6 | #include "postprocess.glsl" 7 | 8 | const bool DO_ACCUMULATION = true; 9 | layout(push_constant) uniform PushConstant { 10 | int accum_start_frame; 11 | } push; 12 | 13 | layout(set = 0, binding = 0) uniform Scene { 14 | mat4 model; 15 | mat4 view; 16 | mat4 view_inverse; 17 | mat4 projection; 18 | mat4 projection_inverse; 19 | mat4 model_view_projection; 20 | uvec3 frame; 21 | } scene; 22 | layout(set = 1, binding = 0) uniform accelerationStructureEXT topLevelAS; 23 | layout(set = 1, binding = 1, rgba32f) uniform image2D accumImage; 24 | layout(set = 1, binding = 2, rgba8) uniform image2D renderImage; 25 | 26 | layout(location = 0) rayPayloadEXT Payload prd; 27 | 28 | void preparePayload( inout Payload prd, vec3 origin, vec3 direction ) 29 | { 30 | prd.hitValue = vec3(0); 31 | prd.depth = 0; 32 | prd.done = 0; 33 | prd.rayOrigin = origin; 34 | prd.rayDir = direction; 35 | prd.rayRange = vec2(max(1.0f, length(origin.xyz)) * 1e-3f, 10000.0f); 36 | prd.roughness = 0; 37 | } 38 | 39 | void main() 40 | { 41 | uint rayFlags = gl_RayFlagsOpaqueEXT; 42 | float tmin = 0.001f; 43 | int maxBounces = 32; 44 | int sampleCount = 8; 45 | vec3 pixelColor = vec3(0); 46 | 47 | prd.rng = tea( gl_LaunchIDEXT.x + gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x, scene.frame.z ); 48 | 49 | for( int i=0; i maxBounces ) { 82 | accumulatedRayColor = vec3(0.0f); 83 | break; 84 | } 85 | } 86 | pixelColor += accumulatedRayColor; 87 | } 88 | pixelColor = (1.0f / sampleCount) * pixelColor; 89 | if( DO_ACCUMULATION ) { 90 | float alpha = 1.0f / float(scene.frame.z + 1 - push.accum_start_frame); 91 | vec3 oldColor = imageLoad(accumImage, ivec2(gl_LaunchIDEXT.xy)).xyz; 92 | pixelColor = mix(oldColor, pixelColor, alpha); 93 | // Replace NaN components with zero to prevent black 94 | if(any(isnan(pixelColor))){ 95 | pixelColor = oldColor; 96 | } 97 | if(any(isinf(pixelColor))){ 98 | pixelColor = oldColor; 99 | } 100 | imageStore(accumImage, ivec2(gl_LaunchIDEXT.xy), vec4(pixelColor, 1.0f)); 101 | } 102 | pixelColor = gammaCorrect(pixelColor, 2.2); 103 | imageStore(renderImage, ivec2(gl_LaunchIDEXT.xy), vec4(pixelColor, 1.0f)); 104 | } 105 | -------------------------------------------------------------------------------- /assets/glsl/pathtrace.rmiss: -------------------------------------------------------------------------------- 1 | #version 460 2 | #extension GL_EXT_ray_tracing : require 3 | #include "payload.glsl" 4 | 5 | layout (constant_id = 0) const int ENABLE_SKYLIGHT = 0; 6 | layout(location = 0) rayPayloadInEXT Payload prd; 7 | 8 | void main() 9 | { 10 | if( bool(ENABLE_SKYLIGHT) ) { 11 | vec3 wI = normalize( gl_WorldRayDirectionEXT ); 12 | float t = smoothstep(0.35, 0.65, 0.5*(wI.y + 1)); 13 | vec3 skyColor = mix(vec3(0.58,0.45,0.25), vec3(0.3, 0.4, 0.5), t); 14 | bool isSun = dot(wI, normalize(vec3(0.0,1.0,-0.25))) > 0.99; 15 | prd.hitValue = mix(skyColor, vec3(120.0, 100.0, 50.0), float(isSun)); 16 | } 17 | else{ 18 | prd.hitValue = vec3(0.0); 19 | } 20 | prd.done = 1; 21 | } -------------------------------------------------------------------------------- /assets/glsl/payload.glsl: -------------------------------------------------------------------------------- 1 | #ifndef PAYLOAD_GLSL 2 | #define PAYLOAD_GLSL 3 | 4 | struct Payload 5 | { 6 | vec3 hitValue; 7 | uint depth; 8 | uint sampleId; 9 | uint done; 10 | vec3 rayOrigin; 11 | vec3 rayDir; 12 | vec2 rayRange; 13 | float roughness; 14 | uint rng; 15 | }; 16 | #endif 17 | -------------------------------------------------------------------------------- /assets/glsl/postprocess.glsl: -------------------------------------------------------------------------------- 1 | #ifndef POSTPROCESS_GLSL 2 | #define POSTPROCESS_GLSL 3 | 4 | // From http://filmicgames.com/archives/75 5 | vec3 Uncharted2Tonemap(vec3 x) 6 | { 7 | float A = 0.15; 8 | float B = 0.50; 9 | float C = 0.10; 10 | float D = 0.20; 11 | float E = 0.02; 12 | float F = 0.30; 13 | return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; 14 | } 15 | 16 | vec3 tonemapUncharted2( in vec3 color ) 17 | { 18 | const float W = 11.2; 19 | const float exposureBias = 2.0; 20 | vec3 curr = Uncharted2Tonemap(exposureBias * color); 21 | vec3 whiteScale = 1.0 / Uncharted2Tonemap(vec3(W)); 22 | return curr * whiteScale; 23 | } 24 | 25 | vec3 ACESFilm( vec3 x ) { 26 | float a = 2.51f; 27 | float b = 0.03f; 28 | float c = 2.43f; 29 | float d = 0.59f; 30 | float e = 0.14f; 31 | return clamp( ( x * ( a * x + b ) ) / ( x * ( c * x + d ) + e ), vec3(0), vec3(1) ); 32 | } 33 | 34 | vec3 exposure(vec3 color, float fstop) { 35 | return color * pow(2.0,fstop); 36 | } 37 | 38 | vec3 gammaCorrect( in vec3 color, float power ) 39 | { 40 | return pow( color, vec3(1.0f / power) ); 41 | } 42 | 43 | #endif -------------------------------------------------------------------------------- /assets/glsl/sampling.glsl: -------------------------------------------------------------------------------- 1 | #ifndef SAMPLING_GLSL 2 | #define SAMPLING_GLSL 3 | #extension GL_EXT_control_flow_attributes : enable 4 | #define M_PI 3.14159265359 5 | #define TWO_PI 6.28318530718 6 | 7 | #define saturate(x) clamp(x, 0.0, 1.0) 8 | 9 | float distanceSquared( vec3 u, vec3 v) 10 | { 11 | vec3 d = u - v; 12 | return dot( d, d ); 13 | } 14 | 15 | // Generate a random unsigned int from two unsigned int values, using 16 pairs 16 | // of rounds of the Tiny Encryption Algorithm. See Zafar, Olano, and Curtis, 17 | // "GPU Random Numbers via the Tiny Encryption Algorithm" 18 | uint tea(uint val0, uint val1) 19 | { 20 | uint v0 = val0; 21 | uint v1 = val1; 22 | uint s0 = 0; 23 | 24 | [[unroll]] 25 | for(uint n = 0; n < 16; n++) 26 | { 27 | s0 += 0x9e3779b9; 28 | v0 += ((v1 << 4) + 0xa341316c) ^ (v1 + s0) ^ ((v1 >> 5) + 0xc8013ea4); 29 | v1 += ((v0 << 4) + 0xad90777d) ^ (v0 + s0) ^ ((v0 >> 5) + 0x7e95761e); 30 | } 31 | return v0; 32 | } 33 | 34 | // Steps the RNG and returns a floating-point value between 0 and 1 inclusive. 35 | float nextRand(inout uint rng) 36 | { 37 | // Condensed version of pcg_output_rxs_m_xs_32_32, with simple conversion to floating-point [0,1]. 38 | rng = rng * 747796405 + 1; 39 | uint word = ((rng >> ((rng >> 28) + 4)) ^ rng) * 277803737; 40 | word = (word >> 22) ^ word; 41 | return float(word) / 4294967295.0f; 42 | } 43 | 44 | vec2 nextRand2(inout uint rng) 45 | { 46 | return vec2(nextRand(rng), nextRand(rng)); 47 | } 48 | 49 | // Sampling functions taken from metal-ray-tracer 50 | // Refer to: https://github.com/sergeyreznik/metal-ray-tracer/blob/part-5/source/Shaders/raytracing.h 51 | 52 | float fresnelDielectric(vec3 i, vec3 m, float eta) 53 | { 54 | float result = 1.0f; 55 | float cosThetaI = abs(dot(i, m)); 56 | float sinThetaOSquared = (eta * eta) * (1.0f - cosThetaI * cosThetaI); 57 | if (sinThetaOSquared <= 1.0f) { 58 | float cosThetaO = sqrt(saturate(1.0f - sinThetaOSquared)); 59 | float Rs = (cosThetaI - eta * cosThetaO) / (cosThetaI + eta * cosThetaO); 60 | float Rp = (eta * cosThetaI - cosThetaO) / (eta * cosThetaI + cosThetaO); 61 | result = 0.5f * (Rs * Rs + Rp * Rp); 62 | } 63 | return result; 64 | } 65 | 66 | void buildOrthonormalBasis(vec3 n, inout vec3 u, inout vec3 v) 67 | { 68 | float s = (n.z < 0.0 ? -1.0f : 1.0f); 69 | float a = -1.0f / (s + n.z); 70 | float b = n.x * n.y * a; 71 | u = vec3(1.0f + s * n.x * n.x * a, s * b, -s * n.x); 72 | v = vec3(b, s + n.y * n.y * a, -n.y); 73 | } 74 | 75 | vec3 alignToDirection(vec3 n, float cosTheta, float phi) 76 | { 77 | float sinTheta = sqrt(saturate(1.0f - cosTheta * cosTheta)); 78 | 79 | vec3 u; 80 | vec3 v; 81 | buildOrthonormalBasis(n, u, v); 82 | 83 | return (u * cos(phi) + v * sin(phi)) * sinTheta + n * cosTheta; 84 | } 85 | 86 | vec3 sampleGGXDistribution(vec3 n, vec2 Xi, float alphaSquared) 87 | { 88 | float cosTheta = sqrt(saturate((1.0f - Xi.x) / (Xi.x * (alphaSquared - 1.0f) + 1.0f))); 89 | return alignToDirection(n, cosTheta, Xi.y * TWO_PI); 90 | } 91 | 92 | vec3 sampleCosineWeightedHemisphere(vec3 n, vec2 Xi) 93 | { 94 | float cosTheta = sqrt(Xi.x); 95 | return alignToDirection(n, cosTheta, Xi.y * TWO_PI); 96 | } 97 | 98 | #endif -------------------------------------------------------------------------------- /assets/glsl/triangle.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout (location = 0) in vec4 color; 5 | layout (location = 0) out vec4 outColor; 6 | 7 | void main() { 8 | outColor = color; 9 | } 10 | -------------------------------------------------------------------------------- /assets/glsl/triangle.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(set = 0, binding = 0) uniform Scene { 5 | mat4 mvp; 6 | } scene; 7 | 8 | layout (location = 0) in vec4 pos; 9 | layout (location = 1) in vec4 inColor; 10 | layout (location = 2) in vec2 inUv; 11 | 12 | layout (location = 0) out vec4 outColor; 13 | 14 | void main() { 15 | outColor = inColor; 16 | gl_Position = scene.mvp * pos; 17 | } -------------------------------------------------------------------------------- /assets/models/Duck.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "asset": { 3 | "generator": "COLLADA2GLTF", 4 | "version": "2.0" 5 | }, 6 | "scene": 0, 7 | "scenes": [ 8 | { 9 | "nodes": [ 10 | 0 11 | ] 12 | } 13 | ], 14 | "nodes": [ 15 | { 16 | "children": [ 17 | 2, 18 | 1 19 | ], 20 | "matrix": [ 21 | 0.009999999776482582, 22 | 0.0, 23 | 0.0, 24 | 0.0, 25 | 0.0, 26 | 0.009999999776482582, 27 | 0.0, 28 | 0.0, 29 | 0.0, 30 | 0.0, 31 | 0.009999999776482582, 32 | 0.0, 33 | 0.0, 34 | 0.0, 35 | 0.0, 36 | 1.0 37 | ] 38 | }, 39 | { 40 | "matrix": [ 41 | -0.7289686799049377, 42 | 0.0, 43 | -0.6845470666885376, 44 | 0.0, 45 | -0.4252049028873444, 46 | 0.7836934328079224, 47 | 0.4527972936630249, 48 | 0.0, 49 | 0.5364750623703003, 50 | 0.6211478114128113, 51 | -0.571287989616394, 52 | 0.0, 53 | 400.1130065917969, 54 | 463.2640075683594, 55 | -431.0780334472656, 56 | 1.0 57 | ], 58 | "camera": 0 59 | }, 60 | { 61 | "mesh": 0 62 | } 63 | ], 64 | "cameras": [ 65 | { 66 | "perspective": { 67 | "aspectRatio": 1.5, 68 | "yfov": 0.6605925559997559, 69 | "zfar": 10000.0, 70 | "znear": 1.0 71 | }, 72 | "type": "perspective" 73 | } 74 | ], 75 | "meshes": [ 76 | { 77 | "primitives": [ 78 | { 79 | "attributes": { 80 | "NORMAL": 1, 81 | "POSITION": 2, 82 | "TEXCOORD_0": 3 83 | }, 84 | "indices": 0, 85 | "mode": 4, 86 | "material": 0 87 | } 88 | ], 89 | "name": "LOD3spShape" 90 | } 91 | ], 92 | "accessors": [ 93 | { 94 | "bufferView": 0, 95 | "byteOffset": 0, 96 | "componentType": 5123, 97 | "count": 12636, 98 | "max": [ 99 | 2398 100 | ], 101 | "min": [ 102 | 0 103 | ], 104 | "type": "SCALAR" 105 | }, 106 | { 107 | "bufferView": 1, 108 | "byteOffset": 0, 109 | "componentType": 5126, 110 | "count": 2399, 111 | "max": [ 112 | 0.9995989799499512, 113 | 0.999580979347229, 114 | 0.9984359741210938 115 | ], 116 | "min": [ 117 | -0.9990839958190918, 118 | -1.0, 119 | -0.9998319745063782 120 | ], 121 | "type": "VEC3" 122 | }, 123 | { 124 | "bufferView": 1, 125 | "byteOffset": 28788, 126 | "componentType": 5126, 127 | "count": 2399, 128 | "max": [ 129 | 96.17990112304688, 130 | 163.97000122070313, 131 | 53.92519760131836 132 | ], 133 | "min": [ 134 | -69.29850006103516, 135 | 9.929369926452637, 136 | -61.32819747924805 137 | ], 138 | "type": "VEC3" 139 | }, 140 | { 141 | "bufferView": 2, 142 | "byteOffset": 0, 143 | "componentType": 5126, 144 | "count": 2399, 145 | "max": [ 146 | 0.9833459854125976, 147 | 0.9800369739532472 148 | ], 149 | "min": [ 150 | 0.026409000158309938, 151 | 0.01996302604675293 152 | ], 153 | "type": "VEC2" 154 | } 155 | ], 156 | "materials": [ 157 | { 158 | "pbrMetallicRoughness": { 159 | "baseColorTexture": { 160 | "index": 0 161 | }, 162 | "metallicFactor": 0.0 163 | }, 164 | "emissiveFactor": [ 165 | 0.0, 166 | 0.0, 167 | 0.0 168 | ], 169 | "name": "blinn3-fx" 170 | } 171 | ], 172 | "textures": [ 173 | { 174 | "sampler": 0, 175 | "source": 0 176 | } 177 | ], 178 | "images": [ 179 | { 180 | "uri": "DuckCM.png" 181 | } 182 | ], 183 | "samplers": [ 184 | { 185 | "magFilter": 9729, 186 | "minFilter": 9986, 187 | "wrapS": 10497, 188 | "wrapT": 10497 189 | } 190 | ], 191 | "bufferViews": [ 192 | { 193 | "buffer": 0, 194 | "byteOffset": 76768, 195 | "byteLength": 25272, 196 | "target": 34963 197 | }, 198 | { 199 | "buffer": 0, 200 | "byteOffset": 0, 201 | "byteLength": 57576, 202 | "byteStride": 12, 203 | "target": 34962 204 | }, 205 | { 206 | "buffer": 0, 207 | "byteOffset": 57576, 208 | "byteLength": 19192, 209 | "byteStride": 8, 210 | "target": 34962 211 | } 212 | ], 213 | "buffers": [ 214 | { 215 | "byteLength": 102040, 216 | "uri": "Duck0.bin" 217 | } 218 | ] 219 | } 220 | -------------------------------------------------------------------------------- /assets/models/Duck0.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/num3ric/sol-rs/2988d9dd17d9596e04c7eb0c41b115ad79697146/assets/models/Duck0.bin -------------------------------------------------------------------------------- /assets/models/DuckCM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/num3ric/sol-rs/2988d9dd17d9596e04c7eb0c41b115ad79697146/assets/models/DuckCM.png -------------------------------------------------------------------------------- /assets/models/ToyCar.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/num3ric/sol-rs/2988d9dd17d9596e04c7eb0c41b115ad79697146/assets/models/ToyCar.glb -------------------------------------------------------------------------------- /assets/models/cornell_copyright.txt: -------------------------------------------------------------------------------- 1 | # A set of eight Cornell Boxes in OBJ format. 2 | # Note that the real box is not a perfect cube, so 3 | # the faces are imperfect in this data set. 4 | # 5 | # Created by Guedis Cardenas and Morgan McGuire at Williams College, 2011 6 | # Attribution 3.0 Unported (CC BY 3.0) License 7 | # http://creativecommons.org/licenses/by/3.0/ 8 | # 9 | # http://graphics.cs.williams.edu/data 10 | # 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/models/tunnel.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "asset":{ 3 | "version":"2.0", 4 | "generator":"Houdini GLTF 2.0 Exporter" 5 | }, 6 | "accessors":[ 7 | { 8 | "bufferView":0, 9 | "componentType":5123, 10 | "count":27360, 11 | "type":"SCALAR", 12 | "min":[0 13 | ], 14 | "max":[27359 15 | ] 16 | }, 17 | { 18 | "bufferView":1, 19 | "componentType":5126, 20 | "count":27360, 21 | "type":"VEC3", 22 | "min":[-0.683802128,-0.849524856,-1.72587192 23 | ], 24 | "max":[0.715879977,-0.173524782,0.341943145 25 | ] 26 | }, 27 | { 28 | "bufferView":2, 29 | "componentType":5126, 30 | "count":27360, 31 | "type":"VEC3", 32 | "min":[0.535391986,0.696695983,0.85799998 33 | ], 34 | "max":[0.535391986,0.696695983,0.85799998 35 | ] 36 | }, 37 | { 38 | "bufferView":3, 39 | "componentType":5126, 40 | "count":27360, 41 | "type":"VEC3", 42 | "min":[-0.999192297,-1,-0.999192297 43 | ], 44 | "max":[0.999192297,1,0.999192297 45 | ] 46 | }, 47 | { 48 | "bufferView":4, 49 | "componentType":5123, 50 | "count":11988, 51 | "type":"SCALAR", 52 | "min":[0 53 | ], 54 | "max":[11987 55 | ] 56 | }, 57 | { 58 | "bufferView":5, 59 | "componentType":5126, 60 | "count":11988, 61 | "type":"VEC3", 62 | "min":[-1,-0.999780655,-3.1559999 63 | ], 64 | "max":[1,0.999780655,10 65 | ] 66 | }, 67 | { 68 | "bufferView":6, 69 | "componentType":5126, 70 | "count":11988, 71 | "type":"VEC3", 72 | "min":[0.164000005,0,0 73 | ], 74 | "max":[1,0.755087197,0.0252009872 75 | ] 76 | }, 77 | { 78 | "bufferView":7, 79 | "componentType":5126, 80 | "count":11988, 81 | "type":"VEC3", 82 | "min":[-1,-1,-1 83 | ], 84 | "max":[1,1,1 85 | ] 86 | } 87 | ], 88 | "buffers":[ 89 | { 90 | "uri":"tunnel_data.bin", 91 | "byteLength":1495224, 92 | "name":"main_buffer" 93 | } 94 | ], 95 | "bufferViews":[ 96 | { 97 | "buffer":0, 98 | "byteLength":54720, 99 | "target":34963 100 | }, 101 | { 102 | "buffer":0, 103 | "byteOffset":54720, 104 | "byteLength":328320, 105 | "target":34962 106 | }, 107 | { 108 | "buffer":0, 109 | "byteOffset":383040, 110 | "byteLength":328320, 111 | "target":34962 112 | }, 113 | { 114 | "buffer":0, 115 | "byteOffset":711360, 116 | "byteLength":328320, 117 | "target":34962 118 | }, 119 | { 120 | "buffer":0, 121 | "byteOffset":1039680, 122 | "byteLength":23976, 123 | "target":34963 124 | }, 125 | { 126 | "buffer":0, 127 | "byteOffset":1063656, 128 | "byteLength":143856, 129 | "target":34962 130 | }, 131 | { 132 | "buffer":0, 133 | "byteOffset":1207512, 134 | "byteLength":143856, 135 | "target":34962 136 | }, 137 | { 138 | "buffer":0, 139 | "byteOffset":1351368, 140 | "byteLength":143856, 141 | "target":34962 142 | } 143 | ], 144 | "nodes":[ 145 | { 146 | "matrix":[-1,0,0,0,0,1,0,0,0,0,1,0,0,0,-4.80000019,1], 147 | "name":"tunnel", 148 | "mesh":0 149 | }, 150 | { 151 | "matrix" : [ 152 | -1, 0, 0, 0, 153 | 0, -0.9874407, -0.15799052, 0, 154 | 0, -0.15799052, 0.9874407, 0, 155 | 0, -0.7899526, -0.12639241, 1 ], 156 | "camera" : 0 157 | } 158 | ], 159 | "cameras": [ 160 | { 161 | "type": "perspective", 162 | "perspective": { 163 | "aspectRatio": 1.7777778, 164 | "yfov": 22.0, 165 | "znear": 0.01, 166 | "zfar": 100.0 167 | } 168 | } 169 | ], 170 | "meshes":[ 171 | { 172 | "primitives":[ 173 | { 174 | "attributes":{ 175 | "NORMAL":3, 176 | "COLOR_0":2, 177 | "POSITION":1 178 | }, 179 | "indices":0, 180 | "material":0 181 | }, 182 | { 183 | "attributes":{ 184 | "NORMAL":7, 185 | "COLOR_0":6, 186 | "POSITION":5 187 | }, 188 | "indices":4, 189 | "material":1 190 | } 191 | ] 192 | } 193 | ], 194 | "materials":[ 195 | { 196 | "name":"mat_spheres", 197 | "pbrMetallicRoughness":{ 198 | "metallicFactor":0, 199 | "roughnessFactor":0.3 200 | } 201 | }, 202 | { 203 | "name":"mat_tubes", 204 | "pbrMetallicRoughness":{ 205 | "metallicFactor":0, 206 | "roughnessFactor":0.850000024 207 | } 208 | } 209 | ], 210 | "scenes":[ 211 | { 212 | "nodes":[0, 1] 213 | } 214 | ], 215 | "scene":0 216 | } 217 | -------------------------------------------------------------------------------- /assets/models/tunnel_data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/num3ric/sol-rs/2988d9dd17d9596e04c7eb0c41b115ad79697146/assets/models/tunnel_data.bin -------------------------------------------------------------------------------- /assets/textures/HDR_RGBA_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/num3ric/sol-rs/2988d9dd17d9596e04c7eb0c41b115ad79697146/assets/textures/HDR_RGBA_0.png -------------------------------------------------------------------------------- /assets/textures/face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/num3ric/sol-rs/2988d9dd17d9596e04c7eb0c41b115ad79697146/assets/textures/face.png -------------------------------------------------------------------------------- /examples/1-cube.rs: -------------------------------------------------------------------------------- 1 | //#![windows_subsystem = "windows"] 2 | use sol::prelude::*; 3 | use sol::{scene, util}; 4 | 5 | pub struct PerFrameData { 6 | pub ubo: sol::Buffer, 7 | pub desc_set: sol::DescriptorSet, 8 | } 9 | pub struct AppData { 10 | pub vertex_buffer: sol::Buffer, 11 | pub texture: sol::Texture2d, 12 | pub desc_set_layout: sol::DescriptorSetLayout, 13 | pub pipeline_layout: sol::PipelineLayout, 14 | pub pipeline: sol::Pipeline, 15 | pub per_frame: Vec, 16 | pub manip: scene::CameraManip, 17 | } 18 | 19 | pub fn setup(app: &mut sol::App) -> AppData { 20 | let context = &app.renderer.context; 21 | 22 | let vertex_buffer = sol::Buffer::from_data( 23 | context.clone(), 24 | sol::BufferInfo::default() 25 | .usage(vk::BufferUsageFlags::VERTEX_BUFFER) 26 | .gpu_only(), 27 | &util::colored_cube_vertices(), 28 | ); 29 | let texture = sol::Texture2d::new(context.clone(), util::find_asset("textures/face.png").unwrap()); 30 | 31 | let mut desc_set_layout = sol::DescriptorSetLayout::new( 32 | context.clone(), 33 | sol::DescriptorSetLayoutInfo::default() 34 | .binding( 35 | 0, 36 | vk::DescriptorType::UNIFORM_BUFFER, 37 | vk::ShaderStageFlags::ALL, 38 | ) 39 | .binding( 40 | 1, 41 | vk::DescriptorType::COMBINED_IMAGE_SAMPLER, 42 | vk::ShaderStageFlags::ALL, 43 | ), 44 | ); 45 | let pipeline_layout = sol::PipelineLayout::new( 46 | context.clone(), 47 | sol::PipelineLayoutInfo::default().desc_set_layout(desc_set_layout.handle()), 48 | ); 49 | let pipeline = sol::Pipeline::new( 50 | context.clone(), 51 | sol::PipelineInfo::default() 52 | .layout(pipeline_layout.handle()) 53 | .render_pass_info(app.renderer.swapchain.get_transient_render_pass_info()) 54 | .vert(util::find_asset("glsl/cube.vert").unwrap()) 55 | .frag(util::find_asset("glsl/cube.frag").unwrap()) 56 | .vertex_type::(), 57 | ); 58 | 59 | let mut camera = scene::Camera::new(app.window.get_size()); 60 | camera.look_at(Vec3::splat(5.0), Vec3::ZERO, -Vec3::Y); 61 | 62 | let vp = camera.perspective_matrix() * camera.view_matrix(); 63 | let mut per_frame = Vec::::new(); 64 | for _ in 0..app.renderer.get_frames_count() { 65 | let ubo = sol::Buffer::from_data( 66 | context.clone(), 67 | sol::BufferInfo::default() 68 | .usage(vk::BufferUsageFlags::UNIFORM_BUFFER) 69 | .cpu_to_gpu(), 70 | &vp.to_cols_array(), 71 | ); 72 | let desc_set = desc_set_layout.get_or_create( 73 | sol::DescriptorSetInfo::default() 74 | .buffer(0, ubo.get_descriptor_info()) 75 | .image(1, texture.get_descriptor_info()), 76 | ); 77 | per_frame.push(PerFrameData { ubo, desc_set }); 78 | } 79 | 80 | AppData { 81 | vertex_buffer, 82 | texture, 83 | pipeline, 84 | desc_set_layout, 85 | pipeline_layout, 86 | per_frame, 87 | manip: scene::CameraManip { 88 | camera, 89 | input: scene::CameraInput::default(), 90 | }, 91 | } 92 | } 93 | 94 | pub fn window_event(_: &mut sol::App, data: &mut AppData, event: &winit::event::WindowEvent) { 95 | data.manip.update(&event); 96 | } 97 | 98 | pub fn render(app: &mut sol::App, data: &mut AppData) -> Result<(), sol::AppRenderError> { 99 | let (image_aquired_semaphore, cmd) = app.renderer.begin_frame_default()?; 100 | let ref camera = data.manip.camera; 101 | let vp = camera.perspective_matrix() * camera.view_matrix(); 102 | data.per_frame[app.renderer.active_frame_index] 103 | .ubo 104 | .update(&vp.to_cols_array()); 105 | let descriptor_sets = [data.per_frame[app.renderer.active_frame_index].desc_set.handle()]; 106 | let device = app.renderer.context.device(); 107 | unsafe { 108 | device.cmd_set_scissor(cmd, 0, &[app.window.get_rect()]); 109 | device.cmd_set_viewport(cmd, 0, &[app.window.get_viewport()]); 110 | device.cmd_bind_pipeline(cmd, vk::PipelineBindPoint::GRAPHICS, data.pipeline.handle()); 111 | device.cmd_bind_descriptor_sets( 112 | cmd, 113 | vk::PipelineBindPoint::GRAPHICS, 114 | data.pipeline_layout.handle(), 115 | 0, 116 | descriptor_sets.as_slice(), 117 | &[], 118 | ); 119 | device.cmd_bind_vertex_buffers(cmd, 0, &[data.vertex_buffer.handle()], &[0]); 120 | device.cmd_draw(cmd, data.vertex_buffer.get_element_count(), 1, 0, 1); 121 | } 122 | app.renderer.end_frame_default(image_aquired_semaphore, cmd) 123 | } 124 | 125 | pub fn prepare() -> sol::AppSettings { 126 | sol::AppSettings { 127 | name: "Cube App".to_string(), 128 | resolution: [900, 600], 129 | render: sol::RendererSettings { 130 | samples: 8, 131 | clear_color: vec4(13.0 / 255.0, 17.0 / 255.0, 23.0 / 255.0, 1.0), 132 | ..Default::default() 133 | }, 134 | } 135 | } 136 | 137 | pub fn main() { 138 | sol::App::build(setup) 139 | .prepare(prepare) 140 | .render(render) 141 | .window_event(window_event) 142 | .run(); 143 | } 144 | -------------------------------------------------------------------------------- /examples/2-model.rs: -------------------------------------------------------------------------------- 1 | //#![windows_subsystem = "windows"] 2 | use sol::prelude::*; 3 | use sol::scene; 4 | 5 | #[repr(C)] 6 | #[derive(Default, Copy, Clone)] 7 | pub struct SceneData { 8 | mvp: Mat4, 9 | normal: Mat4, 10 | } 11 | 12 | pub struct PerFrameData { 13 | pub ubo: sol::Buffer, 14 | pub desc_set: sol::DescriptorSet, 15 | } 16 | 17 | pub struct AppData { 18 | pub scene: scene::Scene, 19 | pub pipeline: sol::Pipeline, 20 | pub desc_set_layout: sol::DescriptorSetLayout, 21 | pub pipeline_layout: sol::PipelineLayout, 22 | pub per_frame: Vec, 23 | pub manip: scene::CameraManip, 24 | } 25 | 26 | pub fn setup(app: &mut sol::App) -> AppData { 27 | let context = &app.renderer.context; 28 | let scene = scene::load_scene( 29 | context.clone(), 30 | &sol::util::find_asset("models/Duck.gltf").unwrap(), 31 | ); 32 | 33 | let mut desc_set_layout = sol::DescriptorSetLayout::new( 34 | context.clone(), 35 | sol::DescriptorSetLayoutInfo::default().binding( 36 | 0, 37 | vk::DescriptorType::UNIFORM_BUFFER, 38 | vk::ShaderStageFlags::ALL, 39 | ), 40 | ); 41 | let pipeline_layout = sol::PipelineLayout::new( 42 | context.clone(), 43 | sol::PipelineLayoutInfo::default().desc_set_layout(desc_set_layout.handle()), 44 | ); 45 | let pipeline = sol::Pipeline::new( 46 | context.clone(), 47 | sol::PipelineInfo::default() 48 | .layout(pipeline_layout.handle()) 49 | .render_pass_info(app.renderer.swapchain.get_transient_render_pass_info()) 50 | .vert(sol::util::find_asset("glsl/model.vert").unwrap()) 51 | .frag(sol::util::find_asset("glsl/model.frag").unwrap()) 52 | .front_face(vk::FrontFace::CLOCKWISE) 53 | .vertex_type::(), 54 | ); 55 | 56 | let mut camera = scene::Camera::new(app.window.get_size()); 57 | camera.look_at(Vec3::splat(3.0), vec3(0.0, 0.5, 0.0), -Vec3::Y); 58 | 59 | let scene_data = SceneData { 60 | mvp: camera.perspective_matrix() * camera.view_matrix() * scene.meshes[0].transform, 61 | normal: (camera.view_matrix() * scene.meshes[0].transform) 62 | .inverse() 63 | .transpose(), 64 | }; 65 | 66 | let mut per_frame = Vec::::new(); 67 | for _ in 0..app.renderer.get_frames_count() { 68 | let ubo = sol::Buffer::from_data( 69 | context.clone(), 70 | sol::BufferInfo::default() 71 | .usage(vk::BufferUsageFlags::UNIFORM_BUFFER) 72 | .cpu_to_gpu(), 73 | &[scene_data], 74 | ); 75 | let desc_set = desc_set_layout 76 | .get_or_create(sol::DescriptorSetInfo::default().buffer(0, ubo.get_descriptor_info())); 77 | per_frame.push(PerFrameData { ubo, desc_set }); 78 | } 79 | AppData { 80 | scene, 81 | pipeline, 82 | desc_set_layout, 83 | pipeline_layout, 84 | per_frame, 85 | manip: scene::CameraManip { 86 | camera, 87 | input: scene::CameraInput::default(), 88 | }, 89 | } 90 | } 91 | 92 | pub fn window_event(_: &mut sol::App, data: &mut AppData, event: &winit::event::WindowEvent) { 93 | data.manip.update(&event); 94 | } 95 | 96 | pub fn render(app: &mut sol::App, data: &mut AppData) -> Result<(), sol::AppRenderError> { 97 | let (image_aquired_semaphore, cmd) = app.renderer.begin_frame_default()?; 98 | let ref camera = data.manip.camera; 99 | //TODO: move mesh transform in push constant? 100 | let scene_data = SceneData { 101 | mvp: camera.perspective_matrix() * camera.view_matrix() * data.scene.meshes[0].transform, 102 | normal: (camera.view_matrix() * data.scene.meshes[0].transform) 103 | .inverse() 104 | .transpose(), 105 | }; 106 | data.per_frame[app.renderer.active_frame_index] 107 | .ubo 108 | .update(&[scene_data]); 109 | let pipeline_layout = data.pipeline_layout.handle(); 110 | let descriptor_sets = [data.per_frame[app.renderer.active_frame_index].desc_set.handle()]; 111 | let device = app.renderer.context.device(); 112 | unsafe { 113 | device.cmd_set_scissor(cmd, 0, &[app.window.get_rect()]); 114 | device.cmd_set_viewport(cmd, 0, &[app.window.get_viewport()]); 115 | device.cmd_bind_pipeline(cmd, vk::PipelineBindPoint::GRAPHICS, data.pipeline.handle()); 116 | device.cmd_bind_descriptor_sets( 117 | cmd, 118 | vk::PipelineBindPoint::GRAPHICS, 119 | pipeline_layout, 120 | 0, 121 | descriptor_sets.as_slice(), 122 | &[], 123 | ); 124 | } 125 | data.scene.meshes.iter().for_each(|mesh| mesh.cmd_draw(cmd)); 126 | app.renderer.end_frame_default(image_aquired_semaphore, cmd) 127 | } 128 | 129 | pub fn prepare() -> sol::AppSettings { 130 | sol::AppSettings { 131 | name: "Model App".to_string(), 132 | resolution: [900, 600], 133 | render: sol::RendererSettings { 134 | samples: 8, 135 | clear_color: Vec4::splat(0.15), 136 | ..Default::default() 137 | }, 138 | } 139 | } 140 | 141 | pub fn main() { 142 | sol::App::build(setup) 143 | .prepare(prepare) 144 | .render(render) 145 | .window_event(window_event) 146 | .run(); 147 | } 148 | -------------------------------------------------------------------------------- /examples/3-ray-debug.rs: -------------------------------------------------------------------------------- 1 | //#![windows_subsystem = "windows"] 2 | use sol::prelude::*; 3 | use sol::ray; 4 | use sol::scene; 5 | use winit::event::WindowEvent; 6 | 7 | #[repr(C)] 8 | #[derive(Default, Copy, Clone)] 9 | struct SceneUniforms { 10 | model: Mat4, 11 | view: Mat4, 12 | view_inverse: Mat4, 13 | projection: Mat4, 14 | projection_inverse: Mat4, 15 | model_view_projection: Mat4, 16 | frame: UVec3, 17 | } 18 | 19 | impl SceneUniforms { 20 | pub fn from(camera: &scene::Camera, frame: UVec3) -> SceneUniforms { 21 | let vp = camera.perspective_matrix() * camera.view_matrix(); 22 | SceneUniforms { 23 | model: Mat4::IDENTITY, 24 | view: camera.view_matrix(), 25 | view_inverse: camera.view_matrix().inverse(), 26 | projection: camera.perspective_matrix(), 27 | projection_inverse: camera.perspective_matrix().inverse(), 28 | model_view_projection: vp, 29 | frame, 30 | } 31 | } 32 | } 33 | 34 | pub struct PerFrameData { 35 | pub ubo: sol::Buffer, 36 | pub desc_set: sol::DescriptorSet, 37 | } 38 | pub struct AppData { 39 | pub scene: scene::Scene, 40 | pub pipeline: ray::Pipeline, 41 | pub layout_scene: sol::DescriptorSetLayout, 42 | pub layout_pass: sol::DescriptorSetLayout, 43 | pub pipeline_layout: sol::PipelineLayout, 44 | pub per_frame: Vec, 45 | pub manip: scene::CameraManip, 46 | pub image_target: sol::Image2d, 47 | pub sbt: ray::ShaderBindingTable, 48 | pub scene_description: ray::SceneDescription, 49 | } 50 | 51 | fn create_image_target(context: &Arc, window: &sol::Window) -> sol::Image2d { 52 | let image_info = vk::ImageCreateInfo::builder() 53 | .image_type(vk::ImageType::TYPE_2D) 54 | .format(vk::Format::R8G8B8A8_UNORM) 55 | .extent(window.get_extent_3d()) 56 | .mip_levels(1) 57 | .array_layers(1) 58 | .samples(vk::SampleCountFlags::TYPE_1) 59 | .tiling(vk::ImageTiling::OPTIMAL) 60 | .usage(vk::ImageUsageFlags::STORAGE | vk::ImageUsageFlags::TRANSFER_SRC) 61 | .sharing_mode(vk::SharingMode::EXCLUSIVE); 62 | 63 | sol::Image2d::new( 64 | context.shared().clone(), 65 | &image_info, 66 | vk::ImageAspectFlags::COLOR, 67 | 1, 68 | "TargetRT" 69 | ) 70 | } 71 | 72 | pub fn setup(app: &mut sol::App) -> AppData { 73 | let context = &app.renderer.context; 74 | let scene = scene::load_scene( 75 | context.clone(), 76 | &sol::util::find_asset("models/Duck.gltf").unwrap(), 77 | ); 78 | let mut camera = scene::Camera::new(app.window.get_size()); 79 | camera.look_at(Vec3::splat(5.0), Vec3::ZERO, -Vec3::Y); 80 | 81 | let mut per_frame = Vec::::new(); 82 | 83 | let mut layout_scene = sol::DescriptorSetLayout::new( 84 | context.clone(), 85 | sol::DescriptorSetLayoutInfo::default().binding( 86 | 0, 87 | vk::DescriptorType::UNIFORM_BUFFER, 88 | vk::ShaderStageFlags::ALL, 89 | ), 90 | ); 91 | let layout_pass = sol::DescriptorSetLayout::new( 92 | context.clone(), 93 | sol::DescriptorSetLayoutInfo::default() 94 | .binding( 95 | 0, 96 | vk::DescriptorType::ACCELERATION_STRUCTURE_KHR, 97 | vk::ShaderStageFlags::RAYGEN_KHR, 98 | ) 99 | .binding( 100 | 1, 101 | vk::DescriptorType::STORAGE_IMAGE, 102 | vk::ShaderStageFlags::RAYGEN_KHR, 103 | ), 104 | ); 105 | 106 | let pipeline_layout = sol::PipelineLayout::new( 107 | context.clone(), 108 | sol::PipelineLayoutInfo::default() 109 | .desc_set_layouts(&[layout_scene.handle(), layout_pass.handle()]), 110 | ); 111 | 112 | let pipeline = ray::Pipeline::new( 113 | context.clone(), 114 | ray::PipelineInfo::default() 115 | .layout(pipeline_layout.handle()) 116 | .shader( 117 | sol::util::find_asset("glsl/debug.rgen").unwrap(), 118 | vk::ShaderStageFlags::RAYGEN_KHR, 119 | ) 120 | .shader( 121 | sol::util::find_asset("glsl/debug.rmiss").unwrap(), 122 | vk::ShaderStageFlags::MISS_KHR, 123 | ) 124 | .shader( 125 | sol::util::find_asset("glsl/debug.rchit").unwrap(), 126 | vk::ShaderStageFlags::CLOSEST_HIT_KHR, 127 | ) 128 | .name("debug_mat".to_string()), 129 | ); 130 | 131 | for _ in 0..app.renderer.get_frames_count() { 132 | let uniforms = SceneUniforms::from( 133 | &camera, 134 | uvec3( 135 | app.window.get_width(), 136 | app.window.get_height(), 137 | 0, 138 | ), 139 | ); 140 | let ubo = sol::Buffer::from_data( 141 | context.clone(), 142 | sol::BufferInfo::default() 143 | .usage(vk::BufferUsageFlags::UNIFORM_BUFFER) 144 | .cpu_to_gpu(), 145 | &[uniforms], 146 | ); 147 | let desc_set = layout_scene 148 | .get_or_create(sol::DescriptorSetInfo::default().buffer(0, ubo.get_descriptor_info())); 149 | per_frame.push(PerFrameData { ubo, desc_set }); 150 | } 151 | 152 | let scene_description = ray::SceneDescription::from_scene(context.clone(), &scene); 153 | 154 | let sbt = ray::ShaderBindingTable::new( 155 | context.clone(), 156 | pipeline.handle(), 157 | ray::ShaderBindingTableInfo::default() 158 | .raygen(0) 159 | .miss(1) 160 | .hitgroup(2), 161 | ); 162 | 163 | let image_target = create_image_target(&context, &app.window); 164 | 165 | AppData { 166 | scene, 167 | pipeline, 168 | layout_scene, 169 | layout_pass, 170 | pipeline_layout, 171 | per_frame, 172 | manip: scene::CameraManip { 173 | camera, 174 | input: scene::CameraInput::default(), 175 | }, 176 | image_target, 177 | sbt, 178 | scene_description, 179 | } 180 | } 181 | 182 | pub fn window_event(app: &mut sol::App, data: &mut AppData, event: &WindowEvent) { 183 | data.manip.update(&event); 184 | match event { 185 | WindowEvent::Resized(_) => { 186 | data.image_target = create_image_target(&app.renderer.context, &app.window); 187 | data.layout_pass.reset_pool(); 188 | } 189 | _ => {} 190 | } 191 | } 192 | 193 | pub fn render(app: &mut sol::App, data: &mut AppData) -> Result<(), sol::AppRenderError> { 194 | let (semaphore, frame_index) = app.renderer.acquire_next_image()?; 195 | 196 | let ref mut frame_ubo = data.per_frame[frame_index].ubo; 197 | frame_ubo.update(&[SceneUniforms::from( 198 | &data.manip.camera, 199 | uvec3( 200 | app.window.get_width(), 201 | app.window.get_height(), 202 | app.elapsed_ticks as u32, 203 | ), 204 | )]); 205 | 206 | let cmd = app.renderer.begin_command_buffer(); 207 | 208 | let desc_scene = data.per_frame[frame_index].desc_set.handle(); 209 | 210 | data.image_target.transition_image_layout( 211 | cmd, 212 | vk::ImageLayout::UNDEFINED, 213 | vk::ImageLayout::GENERAL, 214 | ); 215 | 216 | let image_info = vk::DescriptorImageInfo::builder() 217 | .image_view(data.image_target.get_image_view()) 218 | .image_layout(vk::ImageLayout::GENERAL) 219 | .build(); 220 | let desc_pass = data.layout_pass.get_or_create( 221 | sol::DescriptorSetInfo::default() 222 | .accel_struct(0, data.scene_description.tlas().handle()) 223 | .image(1, image_info), 224 | ); 225 | 226 | let device = app.renderer.context.device(); 227 | unsafe { 228 | device.cmd_set_scissor(cmd, 0, &[app.window.get_rect()]); 229 | device.cmd_set_viewport(cmd, 0, &[app.window.get_viewport()]); 230 | device.cmd_bind_pipeline( 231 | cmd, 232 | vk::PipelineBindPoint::RAY_TRACING_KHR, 233 | data.pipeline.handle(), 234 | ); 235 | device.cmd_bind_descriptor_sets( 236 | cmd, 237 | vk::PipelineBindPoint::RAY_TRACING_KHR, 238 | data.pipeline_layout.handle(), 239 | 0, 240 | &[desc_scene, desc_pass.handle()], 241 | &[], 242 | ); 243 | } 244 | data.sbt.cmd_trace_rays(cmd, app.window.get_extent_3d()); 245 | 246 | let present_image = app.renderer.swapchain.get_present_image(frame_index); 247 | data.image_target.cmd_blit_to(cmd, present_image, true); 248 | present_image.transition_image_layout( 249 | cmd, 250 | vk::ImageLayout::TRANSFER_DST_OPTIMAL, 251 | vk::ImageLayout::PRESENT_SRC_KHR, 252 | ); 253 | app.renderer.end_command_buffer(cmd); 254 | app.renderer.submit_and_present(cmd, semaphore) 255 | } 256 | 257 | pub fn prepare() -> sol::AppSettings { 258 | sol::AppSettings { 259 | name: "Raytracing App".to_string(), 260 | resolution: [900, 600], 261 | render: sol::RendererSettings { 262 | extensions: vec![vk::KhrGetPhysicalDeviceProperties2Fn::name()], 263 | ..Default::default() 264 | }, 265 | } 266 | } 267 | 268 | pub fn main() { 269 | sol::App::build(setup) 270 | .prepare(prepare) 271 | .render(render) 272 | .window_event(window_event) 273 | .run(); 274 | } 275 | -------------------------------------------------------------------------------- /examples/4-ray-ao.rs: -------------------------------------------------------------------------------- 1 | //#![windows_subsystem = "windows"] 2 | use sol::prelude::*; 3 | use sol::ray; 4 | use sol::scene; 5 | use winit::event::WindowEvent; 6 | 7 | #[repr(C)] 8 | #[derive(Default, Copy, Clone)] 9 | struct SceneUniforms { 10 | model: Mat4, 11 | view: Mat4, 12 | view_inverse: Mat4, 13 | projection: Mat4, 14 | projection_inverse: Mat4, 15 | model_view_projection: Mat4, 16 | frame: UVec3, 17 | } 18 | 19 | impl SceneUniforms { 20 | pub fn from(camera: &scene::Camera, frame: UVec3) -> SceneUniforms { 21 | let vp = camera.perspective_matrix() * camera.view_matrix(); 22 | SceneUniforms { 23 | model: Mat4::IDENTITY, 24 | view: camera.view_matrix(), 25 | view_inverse: camera.view_matrix().inverse(), 26 | projection: camera.perspective_matrix(), 27 | projection_inverse: camera.perspective_matrix().inverse(), 28 | model_view_projection: vp, 29 | frame, 30 | } 31 | } 32 | } 33 | 34 | pub struct PerFrameData { 35 | pub ubo: sol::Buffer, 36 | pub desc_set: sol::DescriptorSet, 37 | } 38 | pub struct AppData { 39 | pub scene: scene::Scene, 40 | pub pipeline_layout: sol::PipelineLayout, 41 | pub layout_scene: sol::DescriptorSetLayout, 42 | pub layout_pass: sol::DescriptorSetLayout, 43 | pub per_frame: Vec, 44 | pub manip: scene::CameraManip, 45 | 46 | // Raytracing tools & data 47 | pub scene_description: ray::SceneDescription, 48 | pub pipeline: ray::Pipeline, 49 | pub sbt: ray::ShaderBindingTable, 50 | pub accumulation_start_frame: u32, 51 | pub render_target: sol::Image2d, 52 | pub tex_blue_noise: sol::Texture2d, 53 | } 54 | 55 | fn create_image_target(context: &Arc, window: &sol::Window) -> sol::Image2d { 56 | let image_info = vk::ImageCreateInfo::builder() 57 | .image_type(vk::ImageType::TYPE_2D) 58 | .format(vk::Format::R32G32B32A32_SFLOAT) 59 | .extent(window.get_extent_3d()) 60 | .mip_levels(1) 61 | .array_layers(1) 62 | .samples(vk::SampleCountFlags::TYPE_1) 63 | .tiling(vk::ImageTiling::OPTIMAL) 64 | .usage(vk::ImageUsageFlags::STORAGE | vk::ImageUsageFlags::TRANSFER_SRC) 65 | .sharing_mode(vk::SharingMode::EXCLUSIVE); 66 | 67 | sol::Image2d::new( 68 | context.shared().clone(), 69 | &image_info, 70 | vk::ImageAspectFlags::COLOR, 71 | 1, 72 | "TargetRT" 73 | ) 74 | } 75 | 76 | pub fn setup(app: &mut sol::App) -> AppData { 77 | let context = &app.renderer.context; 78 | let mut scene = scene::load_scene( 79 | context.clone(), 80 | &sol::util::find_asset("models/ToyCar.glb").unwrap(), 81 | ); 82 | // Override transforms... 83 | for mesh in &mut scene.meshes { 84 | mesh.transform = glam::Mat4::from_scale(Vec3::splat(0.01)) 85 | * Mat4::from_rotation_x(std::f32::consts::FRAC_PI_2); 86 | } 87 | let scene_description = ray::SceneDescription::from_scene(context.clone(), &scene); 88 | 89 | let mut camera = scene::Camera::new(app.window.get_size()); 90 | camera.look_at(vec3(4.0, 1.0, 4.0), vec3(0.0, 0.5, 0.0), -Vec3::Y); 91 | 92 | let mut per_frame = Vec::::new(); 93 | 94 | let mut layout_scene = sol::DescriptorSetLayout::new( 95 | context.clone(), 96 | sol::DescriptorSetLayoutInfo::default().binding( 97 | 0, 98 | vk::DescriptorType::UNIFORM_BUFFER, 99 | vk::ShaderStageFlags::ALL, 100 | ), 101 | ); 102 | let instance_count = scene_description.get_instances_buffer().get_element_count(); 103 | let layout_pass = sol::DescriptorSetLayout::new( 104 | context.clone(), 105 | sol::DescriptorSetLayoutInfo::default() 106 | .binding( 107 | 0, 108 | vk::DescriptorType::ACCELERATION_STRUCTURE_KHR, 109 | vk::ShaderStageFlags::RAYGEN_KHR, 110 | ) 111 | .binding( 112 | 1, 113 | vk::DescriptorType::STORAGE_IMAGE, 114 | vk::ShaderStageFlags::RAYGEN_KHR, 115 | ) 116 | .binding( 117 | 2, 118 | vk::DescriptorType::COMBINED_IMAGE_SAMPLER, 119 | vk::ShaderStageFlags::ALL, 120 | ) 121 | .binding( 122 | 3, 123 | vk::DescriptorType::STORAGE_BUFFER, 124 | vk::ShaderStageFlags::CLOSEST_HIT_KHR, 125 | ) 126 | .bindings( 127 | 4, 128 | vk::DescriptorType::STORAGE_BUFFER, 129 | vk::ShaderStageFlags::CLOSEST_HIT_KHR, 130 | instance_count, 131 | ) 132 | .bindings( 133 | 5, 134 | vk::DescriptorType::STORAGE_BUFFER, 135 | vk::ShaderStageFlags::CLOSEST_HIT_KHR, 136 | instance_count, 137 | ), 138 | ); 139 | 140 | let pipeline_layout = sol::PipelineLayout::new( 141 | context.clone(), 142 | sol::PipelineLayoutInfo::default() 143 | .desc_set_layouts(&[layout_scene.handle(), layout_pass.handle()]) 144 | .push_constant_range( 145 | vk::PushConstantRange::builder() 146 | .stage_flags(vk::ShaderStageFlags::RAYGEN_KHR) 147 | .size(size_of::() as u32) 148 | .build(), 149 | ), 150 | ); 151 | let pipeline = ray::Pipeline::new( 152 | context.clone(), 153 | ray::PipelineInfo::default() 154 | .layout(pipeline_layout.handle()) 155 | .shader( 156 | sol::util::find_asset("glsl/ao.rgen").unwrap(), 157 | vk::ShaderStageFlags::RAYGEN_KHR, 158 | ) 159 | .shader( 160 | sol::util::find_asset("glsl/ao.rmiss").unwrap(), 161 | vk::ShaderStageFlags::MISS_KHR, 162 | ) 163 | .shader( 164 | sol::util::find_asset("glsl/ao.rchit").unwrap(), 165 | vk::ShaderStageFlags::CLOSEST_HIT_KHR, 166 | ) 167 | //.specialization(&0u32, 0) 168 | .name("AO_mat".to_string()), 169 | ); 170 | 171 | for _ in 0..app.renderer.get_frames_count() { 172 | let uniforms = SceneUniforms::from( 173 | &camera, 174 | uvec3(app.window.get_width(), app.window.get_height(), 0), 175 | ); 176 | let ubo = sol::Buffer::from_data( 177 | context.clone(), 178 | sol::BufferInfo::default() 179 | .usage(vk::BufferUsageFlags::UNIFORM_BUFFER) 180 | .cpu_to_gpu(), 181 | &[uniforms], 182 | ); 183 | let desc_set = layout_scene 184 | .get_or_create(sol::DescriptorSetInfo::default().buffer(0, ubo.get_descriptor_info())); 185 | per_frame.push(PerFrameData { ubo, desc_set }); 186 | } 187 | 188 | let sbt = ray::ShaderBindingTable::new( 189 | context.clone(), 190 | pipeline.handle(), 191 | ray::ShaderBindingTableInfo::default() 192 | .raygen(0) 193 | .miss(1) 194 | .hitgroup(2), 195 | ); 196 | 197 | let render_target = create_image_target(&context, &app.window); 198 | let tex_blue_noise = sol::Texture2d::new( 199 | context.clone(), 200 | sol::util::find_asset("textures/HDR_RGBA_0.png").unwrap(), 201 | ); 202 | 203 | AppData { 204 | scene, 205 | pipeline_layout, 206 | layout_scene, 207 | layout_pass, 208 | per_frame, 209 | manip: scene::CameraManip { 210 | camera, 211 | input: scene::CameraInput::default(), 212 | }, 213 | scene_description, 214 | pipeline, 215 | sbt, 216 | accumulation_start_frame: 0, 217 | render_target, 218 | tex_blue_noise, 219 | } 220 | } 221 | 222 | pub fn window_event(app: &mut sol::App, data: &mut AppData, event: &WindowEvent) { 223 | if data.manip.update(&event) { 224 | data.accumulation_start_frame = app.elapsed_ticks as u32; 225 | } 226 | match event { 227 | WindowEvent::Resized(_) => { 228 | data.render_target = create_image_target(&app.renderer.context, &app.window); 229 | data.accumulation_start_frame = app.elapsed_ticks as u32; 230 | data.layout_pass.reset_pool(); 231 | } 232 | _ => {} 233 | } 234 | } 235 | 236 | // pub fn update(app: &mut sol::App, data: &mut AppData) { 237 | // let t = app.elapsed_time.as_secs_f32(); 238 | // let transform = glam::Mat4::from_scale(Vec3::splat(0.01)) 239 | // * Mat4::from_rotation_x(std::f32::consts::FRAC_PI_2) 240 | // * glam::Mat4::from_translation(vec3(0.0, 40.0 * t.cos(), 0f32)); 241 | // data.scene_description.blas_transform(transform, 0); 242 | // data.scene_description.blas_transform(transform, 2); 243 | // data.scene_description.update(); 244 | // } 245 | 246 | pub fn render(app: &mut sol::App, data: &mut AppData) -> Result<(), sol::AppRenderError> { 247 | let (semaphore, frame_index) = app.renderer.acquire_next_image()?; 248 | 249 | let ref mut frame_ubo = data.per_frame[frame_index].ubo; 250 | frame_ubo.update(&[SceneUniforms::from( 251 | &data.manip.camera, 252 | uvec3(app.window.get_width(), app.window.get_height(), app.elapsed_ticks as u32), 253 | )]); 254 | 255 | let cmd = app.renderer.begin_command_buffer(); 256 | let device = app.renderer.context.device(); 257 | 258 | if data.accumulation_start_frame == app.elapsed_ticks as u32 { 259 | unsafe { 260 | device.cmd_push_constants( 261 | cmd, 262 | data.pipeline_layout.handle(), 263 | vk::ShaderStageFlags::RAYGEN_KHR, 264 | 0, 265 | &data.accumulation_start_frame.to_ne_bytes(), 266 | ) 267 | } 268 | } 269 | 270 | data.scene_description.tlas_regenerate(cmd); 271 | 272 | data.render_target.transition_image_layout( 273 | cmd, 274 | vk::ImageLayout::UNDEFINED, 275 | vk::ImageLayout::GENERAL, 276 | ); 277 | 278 | let desc_pass = data.layout_pass.get_or_create( 279 | sol::DescriptorSetInfo::default() 280 | .accel_struct(0, data.scene_description.tlas().handle()) 281 | .image(1, data.render_target.get_descriptor_info()) 282 | .image(2, data.tex_blue_noise.get_descriptor_info()) 283 | .buffer( 284 | 3, 285 | data.scene_description 286 | .get_instances_buffer() 287 | .get_descriptor_info(), 288 | ) 289 | .buffers(4, data.scene_description.get_vertex_descriptors().clone()) 290 | .buffers(5, data.scene_description.get_index_descriptors().clone()), 291 | ); 292 | 293 | let descriptor_sets = vec!(data.per_frame[frame_index].desc_set.handle(), desc_pass.handle()); 294 | unsafe { 295 | device.cmd_set_scissor(cmd, 0, &[app.window.get_rect()]); 296 | device.cmd_set_viewport(cmd, 0, &[app.window.get_viewport()]); 297 | device.cmd_bind_pipeline( 298 | cmd, 299 | vk::PipelineBindPoint::RAY_TRACING_KHR, 300 | data.pipeline.handle(), 301 | ); 302 | device.cmd_bind_descriptor_sets( 303 | cmd, 304 | vk::PipelineBindPoint::RAY_TRACING_KHR, 305 | data.pipeline_layout.handle(), 306 | 0, 307 | descriptor_sets.as_slice(), 308 | &[], 309 | ); 310 | } 311 | data.sbt.cmd_trace_rays(cmd, app.window.get_extent_3d()); 312 | 313 | let present_image = app.renderer.swapchain.get_present_image(frame_index); 314 | data.render_target.cmd_blit_to(cmd, present_image, true); 315 | present_image.transition_image_layout( 316 | cmd, 317 | vk::ImageLayout::TRANSFER_DST_OPTIMAL, 318 | vk::ImageLayout::PRESENT_SRC_KHR, 319 | ); 320 | app.renderer.end_command_buffer(cmd); 321 | app.renderer.submit_and_present(cmd, semaphore) 322 | } 323 | 324 | pub fn prepare() -> sol::AppSettings { 325 | sol::AppSettings { 326 | name: "Raytracing AO App".to_string(), 327 | resolution: [900, 600], 328 | render: sol::RendererSettings { 329 | extensions: vec![vk::KhrGetPhysicalDeviceProperties2Fn::name()], 330 | ..Default::default() 331 | }, 332 | } 333 | } 334 | 335 | pub fn main() { 336 | sol::App::build(setup) 337 | .prepare(prepare) 338 | // .update(update) 339 | .render(render) 340 | .window_event(window_event) 341 | .run(); 342 | } 343 | -------------------------------------------------------------------------------- /examples/5-pathtrace.rs: -------------------------------------------------------------------------------- 1 | //#![windows_subsystem = "windows"] 2 | use sol::prelude::*; 3 | use sol::ray; 4 | use sol::scene; 5 | use winit::event::WindowEvent; 6 | 7 | #[repr(C)] 8 | #[derive(Default, Copy, Clone)] 9 | struct SceneUniforms { 10 | model: Mat4, 11 | view: Mat4, 12 | view_inverse: Mat4, 13 | projection: Mat4, 14 | projection_inverse: Mat4, 15 | model_view_projection: Mat4, 16 | frame: UVec3, 17 | } 18 | 19 | impl SceneUniforms { 20 | pub fn from(camera: &scene::Camera, frame: UVec3) -> SceneUniforms { 21 | let vp = camera.perspective_matrix() * camera.view_matrix(); 22 | SceneUniforms { 23 | model: Mat4::IDENTITY, 24 | view: camera.view_matrix(), 25 | view_inverse: camera.view_matrix().inverse(), 26 | projection: camera.perspective_matrix(), 27 | projection_inverse: camera.perspective_matrix().inverse(), 28 | model_view_projection: vp, 29 | frame, 30 | } 31 | } 32 | } 33 | 34 | pub struct PerFrameData { 35 | pub ubo: sol::Buffer, 36 | pub desc_set: sol::DescriptorSet, 37 | } 38 | pub struct AppData { 39 | pub scene: scene::Scene, 40 | pub pipeline_layout: sol::PipelineLayout, 41 | pub layout_scene: sol::DescriptorSetLayout, 42 | pub layout_pass: sol::DescriptorSetLayout, 43 | pub per_frame: Vec, 44 | pub manip: scene::CameraManip, 45 | 46 | // Raytracing tools & data 47 | pub scene_description: ray::SceneDescription, 48 | pub pipeline: ray::Pipeline, 49 | pub sbt: ray::ShaderBindingTable, 50 | pub accumulation_start_frame: u32, 51 | pub accum_target: sol::Image2d, 52 | pub render_target: sol::Image2d, 53 | 54 | pub enable_sky: bool, //Temporary shader hack for sky/sun light 55 | } 56 | 57 | fn create_image_target( 58 | context: &Arc, 59 | window: &sol::Window, 60 | format: vk::Format, 61 | ) -> sol::Image2d { 62 | let image_info = vk::ImageCreateInfo::builder() 63 | .image_type(vk::ImageType::TYPE_2D) 64 | .format(format) 65 | .extent(window.get_extent_3d()) 66 | .mip_levels(1) 67 | .array_layers(1) 68 | .samples(vk::SampleCountFlags::TYPE_1) 69 | .tiling(vk::ImageTiling::OPTIMAL) 70 | .usage(vk::ImageUsageFlags::STORAGE | vk::ImageUsageFlags::TRANSFER_SRC) 71 | .sharing_mode(vk::SharingMode::EXCLUSIVE); 72 | 73 | sol::Image2d::new( 74 | context.shared().clone(), 75 | &image_info, 76 | vk::ImageAspectFlags::COLOR, 77 | 1, 78 | "TargetRT" 79 | ) 80 | } 81 | 82 | fn build_pipeline_sbt( 83 | context: &Arc, 84 | pipeline_layout: &sol::PipelineLayout, 85 | enable_sky: bool, 86 | ) -> (ray::Pipeline, ray::ShaderBindingTable) { 87 | let pipeline = ray::Pipeline::new( 88 | context.clone(), 89 | ray::PipelineInfo::default() 90 | .layout(pipeline_layout.handle()) 91 | .shader( 92 | sol::util::find_asset("glsl/pathtrace.rgen").unwrap(), 93 | vk::ShaderStageFlags::RAYGEN_KHR, 94 | ) 95 | .shader( 96 | sol::util::find_asset("glsl/pathtrace.rmiss").unwrap(), 97 | vk::ShaderStageFlags::MISS_KHR, 98 | ) 99 | .shader( 100 | sol::util::find_asset("glsl/pathtrace.rchit").unwrap(), 101 | vk::ShaderStageFlags::CLOSEST_HIT_KHR, 102 | ) 103 | .specialization(&[enable_sky as u32], 0) 104 | .name("AO_mat".to_string()), 105 | ); 106 | let sbt = ray::ShaderBindingTable::new( 107 | context.clone(), 108 | pipeline.handle(), 109 | ray::ShaderBindingTableInfo::default() 110 | .raygen(0) 111 | .miss(1) 112 | .hitgroup(2), 113 | ); 114 | 115 | (pipeline, sbt) 116 | } 117 | 118 | pub fn setup(app: &mut sol::App) -> AppData { 119 | let context = &app.renderer.context; 120 | let index = std::env::args().position(|arg| arg == "--model").unwrap(); 121 | let scene = scene::load_scene( 122 | context.clone(), 123 | &sol::util::find_asset(&std::env::args().nth(index + 1).expect("no gltf file given")) 124 | .unwrap(), 125 | ); 126 | let scene_description = ray::SceneDescription::from_scene(context.clone(), &scene); 127 | 128 | let camera = match scene.camera { 129 | Some(scene_camera) => { 130 | let mut cam = scene_camera; 131 | cam.set_window_size(app.window.get_size()); 132 | cam 133 | } 134 | None => scene::Camera::new(app.window.get_size()), 135 | }; 136 | 137 | let mut per_frame = Vec::::new(); 138 | 139 | let mut layout_scene = sol::DescriptorSetLayout::new( 140 | context.clone(), 141 | sol::DescriptorSetLayoutInfo::default().binding( 142 | 0, 143 | vk::DescriptorType::UNIFORM_BUFFER, 144 | vk::ShaderStageFlags::ALL, 145 | ), 146 | ); 147 | let instance_count = scene_description.get_instances_buffer().get_element_count(); 148 | let layout_pass = sol::DescriptorSetLayout::new( 149 | context.clone(), 150 | sol::DescriptorSetLayoutInfo::default() 151 | .binding( 152 | 0, 153 | vk::DescriptorType::ACCELERATION_STRUCTURE_KHR, 154 | vk::ShaderStageFlags::RAYGEN_KHR, 155 | ) 156 | .binding( 157 | 1, 158 | vk::DescriptorType::STORAGE_IMAGE, 159 | vk::ShaderStageFlags::RAYGEN_KHR, 160 | ) 161 | .binding( 162 | 2, 163 | vk::DescriptorType::STORAGE_IMAGE, 164 | vk::ShaderStageFlags::RAYGEN_KHR, 165 | ) 166 | .binding( 167 | 3, 168 | vk::DescriptorType::STORAGE_BUFFER, 169 | vk::ShaderStageFlags::CLOSEST_HIT_KHR, 170 | ) 171 | .bindings( 172 | 4, 173 | vk::DescriptorType::STORAGE_BUFFER, 174 | vk::ShaderStageFlags::CLOSEST_HIT_KHR, 175 | instance_count, 176 | ) 177 | .bindings( 178 | 5, 179 | vk::DescriptorType::STORAGE_BUFFER, 180 | vk::ShaderStageFlags::CLOSEST_HIT_KHR, 181 | instance_count, 182 | ) 183 | .bindings( 184 | 6, 185 | vk::DescriptorType::STORAGE_BUFFER, 186 | vk::ShaderStageFlags::CLOSEST_HIT_KHR, 187 | instance_count, 188 | ), 189 | ); 190 | 191 | for _ in 0..app.renderer.get_frames_count() { 192 | let uniforms = SceneUniforms::from( 193 | &camera, 194 | uvec3(app.window.get_width(), app.window.get_height(), 0), 195 | ); 196 | let ubo = sol::Buffer::from_data( 197 | context.clone(), 198 | sol::BufferInfo::default() 199 | .usage(vk::BufferUsageFlags::UNIFORM_BUFFER) 200 | .cpu_to_gpu(), 201 | &[uniforms], 202 | ); 203 | let desc_set = layout_scene 204 | .get_or_create(sol::DescriptorSetInfo::default().buffer(0, ubo.get_descriptor_info())); 205 | per_frame.push(PerFrameData { ubo, desc_set }); 206 | } 207 | 208 | let pipeline_layout = sol::PipelineLayout::new( 209 | context.clone(), 210 | sol::PipelineLayoutInfo::default() 211 | .desc_set_layouts(&[layout_scene.handle(), layout_pass.handle()]) 212 | .push_constant_range( 213 | vk::PushConstantRange::builder() 214 | .stage_flags(vk::ShaderStageFlags::RAYGEN_KHR) 215 | .size(size_of::() as u32) 216 | .build(), 217 | ), 218 | ); 219 | 220 | let enable_sky = std::env::args().any(|arg| arg == "--sky"); 221 | let (pipeline, sbt) = build_pipeline_sbt(&context, &pipeline_layout, enable_sky); 222 | let mut accum_target = 223 | create_image_target(&context, &app.window, vk::Format::R32G32B32A32_SFLOAT); 224 | 225 | let cmd = context.begin_single_time_cmd(); 226 | accum_target.transition_image_layout(cmd, vk::ImageLayout::UNDEFINED, vk::ImageLayout::GENERAL); 227 | context.end_single_time_cmd(cmd); 228 | 229 | let render_target = create_image_target(&context, &app.window, vk::Format::R8G8B8A8_UNORM); 230 | AppData { 231 | scene, 232 | pipeline_layout, 233 | layout_scene, 234 | layout_pass, 235 | per_frame, 236 | manip: scene::CameraManip { 237 | camera, 238 | input: scene::CameraInput::default(), 239 | }, 240 | scene_description, 241 | pipeline, 242 | sbt, 243 | accumulation_start_frame: 0, 244 | accum_target, 245 | render_target, 246 | enable_sky, 247 | } 248 | } 249 | 250 | pub fn window_event(app: &mut sol::App, data: &mut AppData, event: &WindowEvent) { 251 | if data.manip.update(&event) { 252 | data.accumulation_start_frame = app.elapsed_ticks as u32; 253 | } 254 | match event { 255 | WindowEvent::Resized(_) => { 256 | data.accum_target = create_image_target( 257 | &app.renderer.context, 258 | &app.window, 259 | vk::Format::R32G32B32A32_SFLOAT, 260 | ); 261 | data.render_target = create_image_target( 262 | &app.renderer.context, 263 | &app.window, 264 | vk::Format::R8G8B8A8_UNORM, 265 | ); 266 | data.accumulation_start_frame = app.elapsed_ticks as u32; 267 | data.layout_pass.reset_pool(); 268 | } 269 | WindowEvent::KeyboardInput { input, .. } => { 270 | if input.state == winit::event::ElementState::Pressed { 271 | if input.virtual_keycode == Some(winit::event::VirtualKeyCode::R) { 272 | unsafe { 273 | app.renderer 274 | .context 275 | .device() 276 | .queue_wait_idle(app.renderer.context.graphics_queue()) 277 | .unwrap(); 278 | } 279 | let (pipeline, sbt) = build_pipeline_sbt( 280 | &app.renderer.context, 281 | &data.pipeline_layout, 282 | data.enable_sky, 283 | ); 284 | data.pipeline = pipeline; 285 | data.sbt = sbt; 286 | data.accumulation_start_frame = app.elapsed_ticks as u32; 287 | } 288 | } 289 | } 290 | _ => {} 291 | } 292 | } 293 | 294 | pub fn render(app: &mut sol::App, data: &mut AppData) -> Result<(), sol::AppRenderError> { 295 | let (semaphore, frame_index) = app.renderer.acquire_next_image()?; 296 | 297 | let ref mut frame_ubo = data.per_frame[frame_index].ubo; 298 | frame_ubo.update(&[SceneUniforms::from( 299 | &data.manip.camera, 300 | uvec3(app.window.get_width(), app.window.get_height(), app.elapsed_ticks as u32) 301 | )]); 302 | 303 | let cmd = app.renderer.begin_command_buffer(); 304 | let device = app.renderer.context.device(); 305 | 306 | unsafe { 307 | device.cmd_push_constants( 308 | cmd, 309 | data.pipeline_layout.handle(), 310 | vk::ShaderStageFlags::RAYGEN_KHR, 311 | 0, 312 | &data.accumulation_start_frame.to_ne_bytes(), 313 | ) 314 | } 315 | 316 | data.scene_description.tlas_regenerate(cmd); 317 | 318 | data.render_target.transition_image_layout( 319 | cmd, 320 | vk::ImageLayout::UNDEFINED, 321 | vk::ImageLayout::GENERAL, 322 | ); 323 | 324 | let desc_pass = data.layout_pass.get_or_create( 325 | sol::DescriptorSetInfo::default() 326 | .accel_struct(0, data.scene_description.tlas().handle()) 327 | .image(1, data.accum_target.get_descriptor_info()) 328 | .image(2, data.render_target.get_descriptor_info()) 329 | .buffer( 330 | 3, 331 | data.scene_description 332 | .get_instances_buffer() 333 | .get_descriptor_info(), 334 | ) 335 | .buffers(4, data.scene_description.get_vertex_descriptors().clone()) 336 | .buffers(5, data.scene_description.get_index_descriptors().clone()) 337 | .buffers(6, data.scene_description.get_material_descriptors().clone()), 338 | ); 339 | 340 | let descriptor_sets = [data.per_frame[frame_index].desc_set.handle(), desc_pass.handle()]; 341 | unsafe { 342 | device.cmd_set_scissor(cmd, 0, &[app.window.get_rect()]); 343 | device.cmd_set_viewport(cmd, 0, &[app.window.get_viewport()]); 344 | device.cmd_bind_pipeline( 345 | cmd, 346 | vk::PipelineBindPoint::RAY_TRACING_KHR, 347 | data.pipeline.handle(), 348 | ); 349 | device.cmd_bind_descriptor_sets( 350 | cmd, 351 | vk::PipelineBindPoint::RAY_TRACING_KHR, 352 | data.pipeline_layout.handle(), 353 | 0, 354 | descriptor_sets.as_slice(), 355 | &[], 356 | ); 357 | } 358 | data.sbt.cmd_trace_rays(cmd, app.window.get_extent_3d()); 359 | 360 | let present_image = app.renderer.swapchain.get_present_image(frame_index); 361 | data.render_target.cmd_blit_to(cmd, present_image, true); 362 | present_image.transition_image_layout( 363 | cmd, 364 | vk::ImageLayout::TRANSFER_DST_OPTIMAL, 365 | vk::ImageLayout::PRESENT_SRC_KHR, 366 | ); 367 | app.renderer.end_command_buffer(cmd); 368 | app.renderer.submit_and_present(cmd, semaphore) 369 | } 370 | 371 | pub fn prepare() -> sol::AppSettings { 372 | sol::AppSettings { 373 | name: "Pathtrace App".to_string(), 374 | resolution: [1280, 720], 375 | render: sol::RendererSettings { 376 | extensions: vec![vk::KhrGetPhysicalDeviceProperties2Fn::name()], 377 | ..Default::default() 378 | }, 379 | } 380 | } 381 | 382 | pub fn main() { 383 | sol::App::build(setup) 384 | .prepare(prepare) 385 | .render(render) 386 | .window_event(window_event) 387 | .run(); 388 | } 389 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2018" -------------------------------------------------------------------------------- /src/buffer.rs: -------------------------------------------------------------------------------- 1 | use crate::{Context, Resource}; 2 | use ash::{util::Align, vk}; 3 | use std::sync::Arc; 4 | use std::{ffi::c_void, mem::align_of}; 5 | use gpu_allocator::{MemoryLocation, vulkan::{Allocation, AllocationCreateDesc}}; 6 | 7 | #[derive(Clone, Copy)] 8 | pub struct BufferInfo<'a> { 9 | pub name: &'a str, 10 | pub usage: vk::BufferUsageFlags, 11 | pub mem_usage: MemoryLocation, 12 | pub memory_type_bits: Option, 13 | pub index_type: Option, 14 | } 15 | 16 | impl Default for BufferInfo<'_> { 17 | fn default() -> Self { 18 | BufferInfo { 19 | name: "Buffer", 20 | usage: vk::BufferUsageFlags::default(), 21 | mem_usage: MemoryLocation::CpuToGpu, 22 | memory_type_bits: None, 23 | index_type: None, 24 | } 25 | } 26 | } 27 | 28 | impl<'a> BufferInfo<'a> { 29 | pub fn name(mut self, name: &'a str ) -> Self { 30 | self.name = name; 31 | self 32 | } 33 | pub fn usage(mut self, usage: vk::BufferUsageFlags) -> Self { 34 | self.usage = usage; 35 | self 36 | } 37 | pub fn usage_transfer_src(mut self) -> Self { 38 | self.usage |= vk::BufferUsageFlags::TRANSFER_SRC; 39 | self 40 | } 41 | pub fn usage_transfer_dst(mut self) -> Self { 42 | self.usage |= vk::BufferUsageFlags::TRANSFER_DST; 43 | self 44 | } 45 | pub fn usage_uniform_texel(mut self) -> Self { 46 | self.usage |= vk::BufferUsageFlags::UNIFORM_TEXEL_BUFFER; 47 | self 48 | } 49 | pub fn usage_storage_texel(mut self) -> Self { 50 | self.usage |= vk::BufferUsageFlags::STORAGE_TEXEL_BUFFER; 51 | self 52 | } 53 | pub fn usage_uniform(mut self) -> Self { 54 | self.usage |= vk::BufferUsageFlags::UNIFORM_BUFFER; 55 | self 56 | } 57 | pub fn usage_storage(mut self) -> Self { 58 | self.usage |= vk::BufferUsageFlags::STORAGE_BUFFER; 59 | self 60 | } 61 | pub fn usage_index(mut self) -> Self { 62 | self.usage |= vk::BufferUsageFlags::INDEX_BUFFER; 63 | self 64 | } 65 | pub fn usage_vertex(mut self) -> Self { 66 | self.usage |= vk::BufferUsageFlags::VERTEX_BUFFER; 67 | self 68 | } 69 | pub fn usage_indirect(mut self) -> Self { 70 | self.usage |= vk::BufferUsageFlags::INDIRECT_BUFFER; 71 | self 72 | } 73 | pub fn gpu_only(mut self) -> Self { 74 | self.mem_usage = MemoryLocation::GpuOnly; 75 | self 76 | } 77 | pub fn cpu_to_gpu(mut self) -> Self { 78 | self.mem_usage = MemoryLocation::CpuToGpu; 79 | self 80 | } 81 | pub fn gpu_to_cpu(mut self) -> Self { 82 | self.mem_usage = MemoryLocation::GpuToCpu; 83 | self 84 | } 85 | pub fn index_type(mut self, index_type: vk::IndexType) -> Self { 86 | self.index_type = Some(index_type); 87 | self 88 | } 89 | pub fn memory_type_bits(mut self, memory_type_bits: u32) -> Self { 90 | self.memory_type_bits = Some(memory_type_bits); 91 | self 92 | } 93 | } 94 | 95 | pub struct Buffer { 96 | context: Arc, 97 | handle: vk::Buffer, 98 | element_count: u32, 99 | allocation: Allocation, 100 | index_type: Option, 101 | } 102 | 103 | impl Buffer { 104 | pub fn new( 105 | context: Arc, 106 | info: BufferInfo, 107 | device_size: vk::DeviceSize, 108 | element_count: u32, 109 | ) -> Self { 110 | assert_ne!(device_size, 0); 111 | 112 | let create_info = vk::BufferCreateInfo::builder() 113 | .size(device_size) 114 | .usage(info.usage); 115 | 116 | let buffer = unsafe { context.device().create_buffer(&create_info, None) }.unwrap(); 117 | let mut requirements = unsafe { context.device().get_buffer_memory_requirements(buffer) }; 118 | if info.memory_type_bits.is_some() { 119 | requirements.memory_type_bits |= info.memory_type_bits.unwrap(); 120 | } 121 | 122 | let allocation = context.allocator() 123 | .lock() 124 | .unwrap() 125 | .allocate(&AllocationCreateDesc { 126 | name: info.name, 127 | requirements, 128 | location: info.mem_usage, 129 | linear: true, // Buffers are always linear 130 | }).unwrap(); 131 | 132 | // Bind memory to the buffer 133 | unsafe { context.device().bind_buffer_memory(buffer, allocation.memory(), allocation.offset()).unwrap() }; 134 | 135 | Buffer { 136 | context: context.clone(), 137 | handle: buffer, 138 | element_count, 139 | allocation, 140 | index_type: info.index_type, 141 | } 142 | } 143 | 144 | pub fn from_data( 145 | context: Arc, 146 | info: BufferInfo, 147 | data: &[T], 148 | ) -> Self { 149 | assert!(!data.is_empty()); 150 | 151 | let device_size = std::mem::size_of_val(data) as u64; 152 | let mut create_info = vk::BufferCreateInfo::builder() 153 | .size(device_size) 154 | .usage(info.usage); 155 | if info.mem_usage == MemoryLocation::GpuOnly { 156 | create_info.usage |= vk::BufferUsageFlags::TRANSFER_DST; 157 | } 158 | 159 | let buffer = unsafe { context.device().create_buffer(&create_info, None) }.unwrap(); 160 | let mut requirements = unsafe { context.device().get_buffer_memory_requirements(buffer) }; 161 | if info.memory_type_bits.is_some() { 162 | requirements.memory_type_bits |= info.memory_type_bits.unwrap(); 163 | } 164 | 165 | let allocation = context.allocator() 166 | .lock() 167 | .unwrap() 168 | .allocate(&AllocationCreateDesc { 169 | name: info.name, 170 | requirements, 171 | location: info.mem_usage, 172 | linear: true, // Buffers are always linear 173 | }).unwrap(); 174 | 175 | // Bind memory to the buffer 176 | unsafe { context.device().bind_buffer_memory(buffer, allocation.memory(), allocation.offset()).unwrap() }; 177 | 178 | let result = Buffer { 179 | context: context.clone(), 180 | handle: buffer, 181 | element_count: data.len() as u32, 182 | allocation, 183 | index_type: info.index_type, 184 | }; 185 | 186 | match info.mem_usage { 187 | MemoryLocation::GpuOnly => { 188 | let staging_buffer = Self::new( 189 | context.clone(), 190 | BufferInfo::default() 191 | .cpu_to_gpu() 192 | .usage(vk::BufferUsageFlags::TRANSFER_SRC), 193 | device_size, 194 | 1, 195 | ); 196 | staging_buffer.update(data); 197 | 198 | let cmd = context.begin_single_time_cmd(); 199 | let region = vk::BufferCopy::builder().size(device_size).build(); 200 | unsafe { 201 | context 202 | .device() 203 | .cmd_copy_buffer(cmd, staging_buffer.handle(), buffer, &[region]); 204 | } 205 | context.end_single_time_cmd(cmd); 206 | } 207 | _ => { 208 | result.update(data); 209 | } 210 | } 211 | result 212 | } 213 | 214 | pub fn update(&self, data: &[T]) { 215 | let size = std::mem::size_of_val(&data[0]) * data.len(); 216 | unsafe { 217 | let mapped_data = self.allocation.mapped_ptr().unwrap().as_ptr(); 218 | let mut mapped_slice = Align::new( 219 | mapped_data as *mut c_void, 220 | align_of::() as u64, 221 | size as u64, 222 | ); 223 | mapped_slice.copy_from_slice(data); 224 | } 225 | } 226 | 227 | pub fn map(&self) -> *mut u8 { 228 | self.allocation.mapped_ptr().unwrap().as_ptr() as *mut u8 229 | } 230 | 231 | pub fn get_descriptor_info(&self) -> vk::DescriptorBufferInfo { 232 | vk::DescriptorBufferInfo::builder() 233 | .buffer(self.handle) 234 | .offset(0) 235 | .range(vk::WHOLE_SIZE) 236 | .build() 237 | } 238 | 239 | pub fn get_descriptor_info_offset( 240 | &self, 241 | offset: vk::DeviceSize, 242 | range: vk::DeviceSize, 243 | ) -> vk::DescriptorBufferInfo { 244 | vk::DescriptorBufferInfo::builder() 245 | .buffer(self.handle) 246 | .offset(offset) 247 | .range(range) 248 | .build() 249 | } 250 | 251 | pub fn get_size(&self) -> vk::DeviceSize { 252 | self.allocation.size() 253 | } 254 | 255 | pub unsafe fn get_memory(&self) -> vk::DeviceMemory 256 | { 257 | self.allocation.memory() 258 | } 259 | 260 | pub fn get_offset(&self) -> vk::DeviceSize { 261 | self.allocation.offset() 262 | } 263 | 264 | pub fn get_element_count(&self) -> u32 { 265 | self.element_count 266 | } 267 | 268 | pub fn get_index_type(&self) -> vk::IndexType { 269 | self.index_type.unwrap_or_default() 270 | } 271 | 272 | pub fn get_alloc(&self) -> &Allocation { 273 | &self.allocation 274 | } 275 | 276 | pub fn get_device_address(&self) -> u64 { 277 | unsafe { 278 | self.context.device().get_buffer_device_address( 279 | &vk::BufferDeviceAddressInfo::builder().buffer(self.handle), 280 | ) 281 | } 282 | } 283 | } 284 | 285 | impl Resource for Buffer { 286 | fn handle(&self) -> vk::Buffer { 287 | self.handle 288 | } 289 | } 290 | 291 | impl Drop for Buffer { 292 | fn drop(&mut self) { 293 | unsafe { 294 | self.context.device().destroy_buffer(self.handle, None); 295 | } 296 | 297 | let to_drop = std::mem::replace(&mut self.allocation, Allocation::default()); 298 | self.context.allocator() 299 | .lock() 300 | .unwrap() 301 | .free(to_drop).unwrap(); 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /src/descriptor.rs: -------------------------------------------------------------------------------- 1 | use crate::Context; 2 | use ash::vk; 3 | use std::collections::HashMap; 4 | use std::sync::Arc; 5 | 6 | pub struct DescriptorSetInfo { 7 | pub buffer_infos: HashMap>, 8 | pub image_infos: HashMap>, 9 | pub acceleration_structures: HashMap>, 10 | } 11 | 12 | impl Default for DescriptorSetInfo { 13 | fn default() -> Self { 14 | DescriptorSetInfo { 15 | buffer_infos: HashMap::new(), 16 | image_infos: HashMap::new(), 17 | acceleration_structures: HashMap::new(), 18 | } 19 | } 20 | } 21 | impl Eq for DescriptorSetInfo {} 22 | 23 | fn do_vecs_match(a: &Vec, b: &Vec) -> bool { 24 | let matching = a.iter().zip(b).filter(|&(a, b)| a == b).count(); 25 | matching == a.len() && matching == b.len() 26 | } 27 | 28 | impl PartialEq for DescriptorSetInfo { 29 | //TODO: Do not use raw vulkan handles, neither here nor for hashing: custom uuid. 30 | fn eq(&self, other: &Self) -> bool { 31 | for (key, infos) in &self.buffer_infos { 32 | if !other.buffer_infos.contains_key(key) { 33 | return false; 34 | } 35 | if infos.len() != other.buffer_infos[key].len() { 36 | return false; 37 | } 38 | for (i, info) in infos.iter().enumerate() { 39 | let ref other_info = other.buffer_infos[key][i]; 40 | if info.buffer != other_info.buffer { 41 | return false; 42 | } 43 | if info.offset != other_info.offset { 44 | return false; 45 | } 46 | if info.range != other_info.range { 47 | return false; 48 | } 49 | } 50 | } 51 | for (key, infos) in &self.image_infos { 52 | if !other.image_infos.contains_key(key) { 53 | return false; 54 | } 55 | if infos.len() != other.image_infos[key].len() { 56 | return false; 57 | } 58 | for (i, info) in infos.iter().enumerate() { 59 | let ref other_info = other.image_infos[key][i]; 60 | if info.sampler != other_info.sampler { 61 | return false; 62 | } 63 | if info.image_view != other_info.image_view { 64 | return false; 65 | } 66 | // if info.image_layout != other_info.image_layout { 67 | // return false; 68 | // } 69 | } 70 | } 71 | for (key, structs) in &self.acceleration_structures { 72 | if !other.acceleration_structures.contains_key(key) { 73 | return false; 74 | } 75 | if structs.len() != other.acceleration_structures[key].len() { 76 | return false; 77 | } 78 | for (i, accel_struct) in structs.iter().enumerate() { 79 | if other.acceleration_structures[key][i] != *accel_struct { 80 | return false; 81 | } 82 | } 83 | } 84 | true 85 | } 86 | } 87 | 88 | impl DescriptorSetInfo { 89 | pub fn buffer(mut self, binding: u32, info: vk::DescriptorBufferInfo) -> Self { 90 | self.buffer_infos.insert(binding, vec![info]); 91 | self 92 | } 93 | 94 | pub fn buffers(mut self, binding: u32, infos: Vec) -> Self { 95 | self.buffer_infos.insert(binding, infos); 96 | self 97 | } 98 | 99 | pub fn image(mut self, binding: u32, info: vk::DescriptorImageInfo) -> Self { 100 | self.image_infos.insert(binding, vec![info]); 101 | self 102 | } 103 | 104 | pub fn images(mut self, binding: u32, infos: Vec) -> Self { 105 | self.image_infos.insert(binding, infos); 106 | self 107 | } 108 | 109 | pub fn accel_struct(mut self, binding: u32, accel_struct: vk::AccelerationStructureKHR) -> Self { 110 | self.acceleration_structures 111 | .insert(binding, vec![accel_struct]); 112 | self 113 | } 114 | pub fn accel_structs( 115 | mut self, 116 | binding: u32, 117 | accel_structs: Vec, 118 | ) -> Self { 119 | self.acceleration_structures.insert(binding, accel_structs); 120 | self 121 | } 122 | 123 | pub fn is_empty(&self) -> bool { 124 | self.image_infos.is_empty() 125 | && self.buffer_infos.is_empty() 126 | && self.acceleration_structures.is_empty() 127 | } 128 | } 129 | 130 | impl std::hash::Hash for DescriptorSetInfo { 131 | fn hash(&self, state: &mut H) { 132 | for (key, infos) in &self.buffer_infos { 133 | key.hash(state); 134 | for info in infos { 135 | info.buffer.hash(state); 136 | info.offset.hash(state); 137 | info.range.hash(state); 138 | } 139 | } 140 | for (key, infos) in &self.image_infos { 141 | key.hash(state); 142 | for info in infos { 143 | info.sampler.hash(state); 144 | info.image_view.hash(state); 145 | //info.image_layout.hash(state); 146 | } 147 | } 148 | for (key, structs) in &self.acceleration_structures { 149 | key.hash(state); 150 | for accel_struct in structs { 151 | accel_struct.hash(state); 152 | } 153 | } 154 | } 155 | } 156 | #[derive(Clone, Debug, Copy)] 157 | pub struct DescriptorSet { 158 | handle: vk::DescriptorSet, 159 | } 160 | 161 | impl crate::Resource for DescriptorSet { 162 | fn handle(&self) -> vk::DescriptorSet { 163 | self.handle 164 | } 165 | } 166 | 167 | pub struct DescriptorSetLayoutInfo { 168 | pub bindings: HashMap, 169 | pub flags: vk::DescriptorSetLayoutCreateFlags, 170 | pub min_max_sets: u32, 171 | } 172 | 173 | impl Default for DescriptorSetLayoutInfo { 174 | fn default() -> Self { 175 | DescriptorSetLayoutInfo { 176 | bindings: HashMap::new(), 177 | flags: vk::DescriptorSetLayoutCreateFlags::default(), 178 | min_max_sets: 64, 179 | } 180 | } 181 | } 182 | 183 | impl DescriptorSetLayoutInfo { 184 | pub fn binding( 185 | mut self, 186 | binding: u32, 187 | descritor_type: vk::DescriptorType, 188 | stage: vk::ShaderStageFlags, 189 | ) -> Self { 190 | self.bindings.insert(binding, (descritor_type, stage, 1)); 191 | self 192 | } 193 | pub fn bindings( 194 | mut self, 195 | binding: u32, 196 | descritor_type: vk::DescriptorType, 197 | stage: vk::ShaderStageFlags, 198 | count: u32, 199 | ) -> Self { 200 | self.bindings 201 | .insert(binding, (descritor_type, stage, count)); 202 | self 203 | } 204 | 205 | pub fn min_max_sets(mut self, min_max_sets: u32) -> Self { 206 | self.min_max_sets = min_max_sets; 207 | self 208 | } 209 | } 210 | 211 | pub struct DescriptorSetLayout { 212 | context: Arc, 213 | layout: vk::DescriptorSetLayout, 214 | pool: vk::DescriptorPool, 215 | info: DescriptorSetLayoutInfo, 216 | sets: HashMap, 217 | } 218 | 219 | impl DescriptorSetLayout { 220 | pub fn new(context: Arc, info: DescriptorSetLayoutInfo) -> Self { 221 | let n = info.bindings.len() as usize; 222 | let mut bindings: Vec = Vec::with_capacity(n); 223 | let mut pool_sizes: Vec = Vec::with_capacity(n); 224 | let max_sets = info.min_max_sets; //TODO: max with swapchain image count 225 | for src_binding in &info.bindings { 226 | bindings.push( 227 | vk::DescriptorSetLayoutBinding::builder() 228 | .binding(*src_binding.0) 229 | .descriptor_type((src_binding.1).0) 230 | .stage_flags((src_binding.1).1) 231 | .descriptor_count((src_binding.1).2) 232 | .build(), 233 | ); 234 | pool_sizes.push( 235 | vk::DescriptorPoolSize::builder() 236 | .ty((src_binding.1).0) 237 | .descriptor_count(max_sets * (src_binding.1).2) 238 | .build(), 239 | ); 240 | } 241 | 242 | let create_info = vk::DescriptorSetLayoutCreateInfo::builder() 243 | .flags(info.flags) 244 | .bindings(&bindings); 245 | unsafe { 246 | let layout = context 247 | .device() 248 | .create_descriptor_set_layout(&create_info, None) 249 | .expect("Failed to create DescriptorSetLayout"); 250 | 251 | let pool_create_info = vk::DescriptorPoolCreateInfo::builder() 252 | .flags(vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET) 253 | .max_sets(max_sets) 254 | .pool_sizes(&pool_sizes); 255 | let pool = context 256 | .device() 257 | .create_descriptor_pool(&pool_create_info, None) 258 | .expect("Failed to create DescriptorPool"); 259 | 260 | DescriptorSetLayout { 261 | context, 262 | layout, 263 | pool, 264 | info: info, 265 | sets: HashMap::::new(), 266 | } 267 | } 268 | } 269 | 270 | pub fn get_or_create(&mut self, info: DescriptorSetInfo) -> DescriptorSet { 271 | assert!(!info.is_empty()); 272 | 273 | if self.sets.contains_key(&info) { 274 | return self.sets[&info]; 275 | } 276 | 277 | unsafe { 278 | let result = DescriptorSet { 279 | handle: self 280 | .context 281 | .device() 282 | .allocate_descriptor_sets( 283 | &vk::DescriptorSetAllocateInfo::builder() 284 | .descriptor_pool(self.pool) 285 | .set_layouts(&[self.layout]) 286 | .build(), 287 | ) 288 | .expect("Failed to create descriptor sets.")[0], 289 | }; 290 | self.update_sets(result.handle, &info); 291 | self.sets.insert(info, result.clone()); 292 | result 293 | } 294 | } 295 | 296 | pub fn get_descriptor_type(&self, binding: u32) -> vk::DescriptorType { 297 | self.info.bindings[&binding].0 298 | } 299 | 300 | pub fn get_shader_stage(&self, binding: u32) -> vk::ShaderStageFlags { 301 | self.info.bindings[&binding].1 302 | } 303 | 304 | pub fn get_descriptor_count(&self, binding: u32) -> u32 { 305 | self.info.bindings[&binding].2 306 | } 307 | 308 | fn update_sets(&self, set: vk::DescriptorSet, info: &DescriptorSetInfo) { 309 | let capacity = 310 | info.buffer_infos.len() + info.image_infos.len() + info.acceleration_structures.len(); 311 | let mut write_descriptor_sets = Vec::::with_capacity(capacity); 312 | for (binding, info) in &info.buffer_infos { 313 | write_descriptor_sets.push( 314 | vk::WriteDescriptorSet::builder() 315 | .dst_set(set) 316 | .dst_binding(*binding) 317 | .dst_array_element(0) 318 | .descriptor_type(self.get_descriptor_type(*binding)) 319 | .buffer_info(info) 320 | .build(), 321 | ); 322 | } 323 | 324 | for (binding, info) in &info.image_infos { 325 | write_descriptor_sets.push( 326 | vk::WriteDescriptorSet::builder() 327 | .dst_set(set) 328 | .dst_binding(*binding) 329 | .dst_array_element(0) 330 | .descriptor_type(self.get_descriptor_type(*binding)) 331 | .image_info(info) 332 | .build(), 333 | ); 334 | } 335 | 336 | for (binding, accel_structs) in &info.acceleration_structures { 337 | let mut accel_info = vk::WriteDescriptorSetAccelerationStructureKHR::builder() 338 | .acceleration_structures(&accel_structs) 339 | .build(); 340 | let mut accel_write = vk::WriteDescriptorSet::builder() 341 | .dst_set(set) 342 | .dst_binding(*binding) 343 | .dst_array_element(0) 344 | .descriptor_type(self.get_descriptor_type(*binding)) 345 | .push_next(&mut accel_info) 346 | .build(); 347 | // This is only set by the builder for images, buffers, or views; need to set explicitly after 348 | accel_write.descriptor_count = 1; 349 | write_descriptor_sets.push(accel_write); 350 | } 351 | 352 | unsafe { 353 | self.context 354 | .device() 355 | .update_descriptor_sets(&write_descriptor_sets, &[]); 356 | } 357 | } 358 | 359 | pub fn reset_pool(&self) { 360 | unsafe { 361 | let flags = vk::DescriptorPoolResetFlags::default(); 362 | self.context 363 | .device() 364 | .reset_descriptor_pool(self.pool, flags) 365 | .expect("Failed to reset descriptor pool."); 366 | } 367 | } 368 | } 369 | 370 | impl crate::Resource for DescriptorSetLayout { 371 | fn handle(&self) -> vk::DescriptorSetLayout { 372 | self.layout 373 | } 374 | } 375 | 376 | impl Drop for DescriptorSetLayout { 377 | fn drop(&mut self) { 378 | unsafe { 379 | self.context 380 | .device() 381 | .destroy_descriptor_set_layout(self.layout, None); 382 | self.context 383 | .device() 384 | .destroy_descriptor_pool(self.pool, None); 385 | } 386 | } 387 | } 388 | 389 | pub struct PipelineLayoutInfo { 390 | pub flags: vk::PipelineLayoutCreateFlags, 391 | pub desc_set_layouts: Vec, 392 | pub push_constant_ranges: Vec, 393 | } 394 | 395 | impl Default for PipelineLayoutInfo { 396 | fn default() -> Self { 397 | PipelineLayoutInfo { 398 | flags: vk::PipelineLayoutCreateFlags::default(), 399 | desc_set_layouts: Vec::new(), 400 | push_constant_ranges: Vec::new(), 401 | } 402 | } 403 | } 404 | 405 | impl PipelineLayoutInfo { 406 | pub fn desc_set_layout(mut self, set_layout: vk::DescriptorSetLayout) -> Self { 407 | self.desc_set_layouts = vec![set_layout]; 408 | self 409 | } 410 | pub fn desc_set_layouts(mut self, set_layouts: &[vk::DescriptorSetLayout]) -> Self { 411 | self.desc_set_layouts.extend_from_slice(set_layouts); 412 | self 413 | } 414 | pub fn push_constant_range(mut self, push_constant_range: vk::PushConstantRange) -> Self { 415 | self.push_constant_ranges = vec![push_constant_range]; 416 | self 417 | } 418 | pub fn push_constant_ranges(mut self, push_constant_ranges: &[vk::PushConstantRange]) -> Self { 419 | self.push_constant_ranges 420 | .extend_from_slice(push_constant_ranges); 421 | self 422 | } 423 | } 424 | 425 | pub struct PipelineLayout { 426 | context: Arc, 427 | layout: vk::PipelineLayout, 428 | info: PipelineLayoutInfo, 429 | } 430 | 431 | impl PipelineLayout { 432 | pub fn new(context: Arc, info: PipelineLayoutInfo) -> Self { 433 | let create_info = vk::PipelineLayoutCreateInfo::builder() 434 | .set_layouts(&info.desc_set_layouts) 435 | .push_constant_ranges(&info.push_constant_ranges) 436 | .build(); 437 | unsafe { 438 | let layout = context 439 | .device() 440 | .create_pipeline_layout(&create_info, None) 441 | .expect("Failed to create pipeline layout."); 442 | PipelineLayout { 443 | context, 444 | layout, 445 | info: info, 446 | } 447 | } 448 | } 449 | } 450 | 451 | impl crate::Resource for PipelineLayout { 452 | fn handle(&self) -> vk::PipelineLayout { 453 | self.layout 454 | } 455 | } 456 | 457 | impl Drop for PipelineLayout { 458 | fn drop(&mut self) { 459 | unsafe { 460 | self.context 461 | .device() 462 | .destroy_pipeline_layout(self.layout, None); 463 | } 464 | } 465 | } 466 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use winit::{ 4 | event::{ElementState, Event, ModifiersState, VirtualKeyCode, WindowEvent}, 5 | event_loop::{ControlFlow, EventLoop}, 6 | }; 7 | 8 | use std::ops::Drop; 9 | use std::time::{Duration, SystemTime}; 10 | 11 | mod buffer; 12 | mod context; 13 | mod descriptor; 14 | mod pipeline; 15 | mod pools; 16 | pub mod prelude; 17 | mod renderer; 18 | mod renderpass; 19 | pub mod scene; 20 | mod swapchain; 21 | mod texture; 22 | pub mod util; 23 | mod window; 24 | pub mod ray; 25 | 26 | pub use crate::buffer::*; 27 | pub use crate::context::*; 28 | pub use crate::descriptor::*; 29 | pub use crate::pipeline::*; 30 | pub use crate::pools::*; 31 | pub use crate::renderer::*; 32 | pub use crate::renderpass::*; 33 | pub use crate::swapchain::*; 34 | pub use crate::texture::*; 35 | pub use crate::window::*; 36 | pub use ash; 37 | pub use glam; 38 | pub use winit; 39 | 40 | // Simple offset_of macro akin to C++ offsetof 41 | #[macro_export] 42 | macro_rules! offset_of { 43 | ($base:path, $field:ident) => {{ 44 | #[allow(unused_unsafe)] 45 | unsafe { 46 | let b: $base = std::mem::zeroed(); 47 | (&b.$field as *const _ as isize) - (&b as *const _ as isize) 48 | } 49 | }}; 50 | } 51 | 52 | pub trait Resource { 53 | fn handle(&self) -> T; 54 | } 55 | 56 | pub trait Vertex { 57 | fn stride() -> u32; 58 | fn format_offset() -> Vec<(ash::vk::Format, u32)>; 59 | } 60 | 61 | pub struct App { 62 | pub settings: AppSettings, 63 | pub renderer: AppRenderer, 64 | pub window: Window, 65 | pub elapsed_time: Duration, 66 | pub elapsed_ticks: u64, 67 | } 68 | 69 | impl App { 70 | pub fn build(setup: SetupFn) -> AppBuilder { 71 | AppBuilder { 72 | prepare: None, 73 | setup, 74 | update: None, 75 | window_event: None, 76 | render: None, 77 | } 78 | } 79 | 80 | pub fn new(settings: AppSettings, event_loop: &EventLoop<()>) -> Self { 81 | let mut window = Window::new( 82 | settings.resolution[0], 83 | settings.resolution[1], 84 | settings.name.clone(), 85 | &event_loop, 86 | ); 87 | let renderer = AppRenderer::new(&mut window, settings.clone().render); 88 | App { 89 | settings, 90 | renderer, 91 | window, 92 | elapsed_time: Duration::default(), 93 | elapsed_ticks: 0, 94 | } 95 | } 96 | 97 | pub fn recreate_swapchain(&mut self) { 98 | self.renderer.recreate_swapchain(&self.window); 99 | } 100 | } 101 | 102 | pub type PrepareFn = fn() -> AppSettings; 103 | pub type SetupFn = fn(&mut App) -> T; // TODO: how do we specify FnOnce here? 104 | pub type UpdateFn = fn(&mut App, &mut T); 105 | pub type RenderFn = fn(&mut App, &mut T) -> Result<(), AppRenderError>; 106 | pub type WindowEventFn = fn(&mut App, &mut T, event: &WindowEvent); 107 | 108 | #[derive(Clone, Debug)] 109 | pub struct AppSettings { 110 | pub name: String, 111 | pub resolution: [u32; 2], 112 | pub render: RendererSettings, 113 | } 114 | 115 | impl Default for AppSettings { 116 | fn default() -> Self { 117 | AppSettings { 118 | name: "App".to_string(), 119 | resolution: [1280, 720], 120 | render: RendererSettings::default(), 121 | } 122 | } 123 | } 124 | pub struct AppBuilder { 125 | pub prepare: Option, 126 | pub setup: SetupFn, 127 | pub update: Option>, 128 | pub window_event: Option>, 129 | pub render: Option>, 130 | } 131 | 132 | impl AppBuilder { 133 | pub fn prepare(mut self, prepare: PrepareFn) -> Self { 134 | self.prepare = Some(prepare); 135 | self 136 | } 137 | 138 | pub fn update(mut self, update: UpdateFn) -> Self { 139 | self.update = Some(update); 140 | self 141 | } 142 | 143 | pub fn render(mut self, render: RenderFn) -> Self { 144 | self.render = Some(render); 145 | self 146 | } 147 | 148 | pub fn window_event(mut self, window_event: WindowEventFn) -> Self { 149 | self.window_event = Some(window_event); 150 | self 151 | } 152 | 153 | pub fn run(self) { 154 | main_loop(self); 155 | } 156 | } 157 | 158 | fn main_loop(builder: AppBuilder) { 159 | let event_loop = EventLoop::new(); 160 | let mut settings = AppSettings::default(); 161 | match builder.prepare { 162 | Some(prepare) => { 163 | settings = prepare(); 164 | } 165 | None => {} 166 | } 167 | let mut app = App::new(settings, &event_loop); 168 | let mut app_data = (builder.setup)(&mut app); 169 | let mut dirty_swapchain = false; 170 | 171 | let now = SystemTime::now(); 172 | let mut modifiers = ModifiersState::default(); 173 | 174 | event_loop.run(move |event, _, control_flow| { 175 | *control_flow = ControlFlow::Poll; 176 | 177 | if !app.window.is_minimized() { 178 | 179 | if dirty_swapchain { 180 | app.recreate_swapchain(); 181 | dirty_swapchain = false; 182 | } 183 | 184 | match event { 185 | Event::WindowEvent { event, .. } => { 186 | match event { 187 | WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, 188 | WindowEvent::KeyboardInput { input, .. } => { 189 | if input.state == ElementState::Pressed { 190 | if input.virtual_keycode == Some(VirtualKeyCode::Q) 191 | && (modifiers.ctrl() || modifiers.logo()) 192 | { 193 | *control_flow = ControlFlow::Exit; 194 | } 195 | } 196 | } 197 | WindowEvent::MouseInput { .. } => {} 198 | WindowEvent::ModifiersChanged(m) => modifiers = m, 199 | _ => (), 200 | } 201 | match builder.window_event { 202 | Some(event_fn) => { 203 | event_fn(&mut app, &mut app_data, &event); 204 | } 205 | None => {} 206 | } 207 | } 208 | Event::MainEventsCleared => { 209 | let now = now.elapsed().unwrap(); 210 | if app.elapsed_ticks % 10 == 0 { 211 | let cpu_time = now.as_millis() as f32 - app.elapsed_time.as_millis() as f32; 212 | let title = format!("{} | cpu:{:.1} ms, gpu:{:.1} ms", app.settings.name, cpu_time, app.renderer.gpu_frame_time); 213 | app.window.set_title(&title); 214 | } 215 | app.elapsed_time = now; 216 | 217 | match builder.update { 218 | Some(update_fn) => { 219 | update_fn(&mut app, &mut app_data); 220 | } 221 | None => {} 222 | } 223 | 224 | dirty_swapchain = match builder.render { 225 | Some(render_fn) => { 226 | matches!( 227 | render_fn(&mut app, &mut app_data), 228 | Err(AppRenderError::DirtySwapchain) 229 | ) 230 | } 231 | None => false, 232 | }; 233 | 234 | app.elapsed_ticks += 1; 235 | } 236 | Event::Suspended => println!("Suspended."), 237 | Event::Resumed => println!("Resumed."), 238 | Event::LoopDestroyed => unsafe { 239 | app.renderer.context.device().device_wait_idle().unwrap(); 240 | }, 241 | _ => {} 242 | } 243 | } 244 | }); 245 | } 246 | -------------------------------------------------------------------------------- /src/pools.rs: -------------------------------------------------------------------------------- 1 | use crate::{Resource, SharedContext}; 2 | use ash::{vk}; 3 | use std::cell::{Cell, RefCell}; 4 | use std::sync::Arc; 5 | 6 | // Based on: https://github.com/KhronosGroup/Vulkan-Samples/blob/master/framework/semaphore_pool.h 7 | pub struct SemaphorePool { 8 | shared_context: Arc, 9 | semaphores: Vec, 10 | active_count: usize, 11 | } 12 | 13 | impl SemaphorePool { 14 | pub fn new(shared_context: Arc) -> Self { 15 | SemaphorePool { 16 | shared_context, 17 | semaphores: Vec::new(), 18 | active_count: 0, 19 | } 20 | } 21 | 22 | pub fn request_semaphore(&mut self) -> vk::Semaphore { 23 | if self.active_count < self.semaphores.len() { 24 | let index = self.active_count; 25 | self.active_count = self.active_count + 1; 26 | return self.semaphores[index]; 27 | } else { 28 | unsafe { 29 | let semaphore_create_info = vk::SemaphoreCreateInfo::default(); 30 | let semaphore = self 31 | .shared_context 32 | .device() 33 | .create_semaphore(&semaphore_create_info, None) 34 | .unwrap(); 35 | 36 | self.semaphores.push(semaphore.clone()); 37 | return semaphore; 38 | } 39 | } 40 | } 41 | 42 | pub fn get_active_count(&self) -> usize { 43 | self.active_count 44 | } 45 | 46 | pub fn reset(&mut self) { 47 | self.active_count = 0; 48 | } 49 | } 50 | 51 | impl Drop for SemaphorePool { 52 | fn drop(&mut self) { 53 | self.reset(); 54 | unsafe { 55 | self.semaphores.iter().for_each(|s| { 56 | self.shared_context.device().destroy_semaphore(*s, None); 57 | }); 58 | } 59 | } 60 | } 61 | 62 | pub struct CommandPool { 63 | context: Arc, 64 | pool: vk::CommandPool, 65 | command_buffers: RefCell>, 66 | active_count: Cell, 67 | } 68 | 69 | impl CommandPool { 70 | pub fn new(context: Arc, queue_family_index: u32) -> Self { 71 | let pool_create_info = vk::CommandPoolCreateInfo::builder() 72 | .flags(vk::CommandPoolCreateFlags::TRANSIENT) 73 | .queue_family_index(queue_family_index); 74 | unsafe { 75 | let pool = context 76 | .device() 77 | .create_command_pool(&pool_create_info, None) 78 | .unwrap(); 79 | CommandPool { 80 | context, 81 | pool, 82 | command_buffers: RefCell::new(Vec::new()), 83 | active_count: Cell::new(0), 84 | } 85 | } 86 | } 87 | 88 | pub fn reset(&self) { 89 | unsafe { 90 | self.context 91 | .device() 92 | .reset_command_pool(self.pool, vk::CommandPoolResetFlags::default()) 93 | .expect("Reset command buffer failed."); 94 | 95 | self.active_count.set(0); 96 | } 97 | } 98 | 99 | pub fn request_command_buffer(&self) -> vk::CommandBuffer { 100 | let mut buffers = self.command_buffers.try_borrow_mut().unwrap(); 101 | if self.active_count.get() < buffers.len() { 102 | let index = self.active_count.get(); 103 | self.active_count.set(index + 1); 104 | return buffers[index]; 105 | } else { 106 | unsafe { 107 | let create_info = vk::CommandBufferAllocateInfo::builder() 108 | .command_buffer_count(1) 109 | .command_pool(self.pool) 110 | .level(vk::CommandBufferLevel::PRIMARY); 111 | let command_buffer = self 112 | .context 113 | .device() 114 | .allocate_command_buffers(&create_info) 115 | .unwrap()[0]; 116 | 117 | buffers.push(command_buffer.clone()); 118 | return command_buffer; 119 | } 120 | } 121 | } 122 | } 123 | 124 | impl Resource for CommandPool { 125 | fn handle(&self) -> vk::CommandPool { 126 | self.pool 127 | } 128 | } 129 | 130 | impl Drop for CommandPool { 131 | fn drop(&mut self) { 132 | unsafe { 133 | self.command_buffers.get_mut().clear(); 134 | self.context.device().destroy_command_pool(self.pool, None); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::{ 2 | ash::{vk}, 3 | glam::*, 4 | winit, Resource, Vertex, 5 | }; 6 | pub use std::{default::Default, mem::size_of, result::Result, sync::Arc}; 7 | -------------------------------------------------------------------------------- /src/ray/mod.rs: -------------------------------------------------------------------------------- 1 | mod pipeline; 2 | pub use pipeline::*; 3 | 4 | mod acceleration; 5 | pub use acceleration::*; 6 | 7 | mod sbt; 8 | pub use sbt::*; 9 | 10 | use ash::vk; 11 | use std::collections::HashMap; 12 | use std::sync::Arc; 13 | 14 | use crate::{Context, Vertex}; 15 | 16 | #[repr(C)] 17 | #[derive(Default, Copy, Clone)] 18 | pub struct SceneInstance { 19 | id: u32, 20 | texture_offset: u32, 21 | padding: glam::Vec2, 22 | transform: glam::Mat4, 23 | transform_it: glam::Mat4, 24 | } 25 | 26 | impl SceneInstance { 27 | pub fn update_transform(&mut self, transform: glam::Mat4) { 28 | self.transform = transform; 29 | self.transform_it = transform.inverse().transpose(); 30 | } 31 | 32 | pub fn get_transform(&self) -> &glam::Mat4 { 33 | &self.transform 34 | } 35 | } 36 | 37 | // Scene description buffers used by the raytracing hit shader 38 | pub struct SceneDescription { 39 | blas: Vec, 40 | tlas: TLAS, 41 | instances: Vec, 42 | instances_buffer: crate::Buffer, 43 | vertex_descriptors: Vec, 44 | index_descriptors: Vec, 45 | mat_descriptors: Vec, 46 | blas_to_instances: HashMap>, 47 | } 48 | 49 | impl SceneDescription { 50 | pub fn from_scene(context: Arc, scene: &crate::scene::Scene) -> Self { 51 | let meshes = scene.meshes.iter().collect::>(); 52 | let mut transforms = Vec::::new(); 53 | meshes.iter().for_each(|mesh| { 54 | transforms.push(mesh.transform); 55 | }); 56 | Self::from_meshes(context, meshes, transforms, Some(&scene.material_buffer)) 57 | } 58 | 59 | pub fn from_meshes( 60 | context: Arc, 61 | meshes: Vec<&crate::scene::Mesh>, 62 | mesh_transforms: Vec, 63 | material_buffer: Option<&crate::Buffer>, 64 | ) -> Self { 65 | let cmd = context.begin_single_time_cmd(); 66 | let mut blas = Vec::::new(); 67 | let mut instances = Vec::::new(); 68 | let mut vertex_descriptors = Vec::::new(); 69 | let mut index_descriptors = Vec::::new(); 70 | let mut mat_descriptors = Vec::::new(); 71 | let mut blas_to_instances = HashMap::>::new(); 72 | 73 | // let min = context 74 | // .get_physical_device_limits() 75 | // .min_storage_buffer_offset_alignment; 76 | // println!("min storage align {:?}", min); 77 | 78 | meshes.iter().enumerate().for_each(|(i, mesh)| { 79 | for primitive in &mesh.primitive_sections { 80 | let mut geo_intances = Vec::::new(); 81 | let mut instance_indices = Vec::::new(); 82 | 83 | let (index_buffer, index_count, index_offset_size) = match &mesh.index_buffer { 84 | Some(buffer) => ( 85 | Some(buffer.get_device_address()), 86 | Some(primitive.get_index_count()), 87 | Some(primitive.get_index_offset_size::()), 88 | ), 89 | None => (None, None, None), 90 | }; 91 | geo_intances.push(GeometryInstance { 92 | vertex_buffer: mesh.vertex_buffer.get_device_address(), 93 | vertex_count: primitive.get_vertex_count(), 94 | vertex_offset_size: primitive.get_vertex_offset_size(), 95 | vertex_offset: primitive.get_vertex_offset(), 96 | index_buffer, 97 | index_count, 98 | index_offset_size, 99 | transform: glam::Mat4::IDENTITY, //TODO: Does this work?? 100 | }); 101 | 102 | vertex_descriptors.push(primitive.get_vertex_descriptor(&mesh.vertex_buffer)); 103 | match &mesh.index_storage { 104 | Some(buffer) => { 105 | index_descriptors.push(primitive.get_index_descriptor::(buffer)); 106 | } 107 | None => {} 108 | } 109 | match &material_buffer { 110 | Some(buffer) => mat_descriptors.push(primitive.get_material_descriptor(buffer)), 111 | None => {} 112 | }; 113 | let instance = SceneInstance { 114 | id: instances.len() as u32, 115 | transform: mesh_transforms[i], 116 | transform_it: mesh_transforms[i].inverse().transpose(), 117 | ..Default::default() 118 | }; 119 | instance_indices.push(instance.id as usize); 120 | instances.push(instance); 121 | 122 | // TODO: support multiple instances per BLAS (move out of primitive loop here) 123 | 124 | // Bottom-level acceleration structure 125 | blas.push(BLAS::new( 126 | context.clone(), 127 | cmd, 128 | geo_intances, 129 | mesh_transforms[i], 130 | crate::scene::ModelVertex::stride() as u64, 131 | true, 132 | )); 133 | blas_to_instances.insert(i as usize, instance_indices); 134 | } 135 | }); 136 | 137 | let tlas = TLAS::new(context.clone(), cmd, &blas); 138 | context.end_single_time_cmd(cmd); 139 | 140 | let instances_buffer = crate::Buffer::from_data( 141 | context.clone(), 142 | crate::BufferInfo::default().cpu_to_gpu().usage_storage(), 143 | &instances, 144 | ); 145 | 146 | SceneDescription { 147 | blas, 148 | tlas, 149 | instances, 150 | instances_buffer, 151 | vertex_descriptors, 152 | index_descriptors, 153 | mat_descriptors, 154 | blas_to_instances, 155 | } 156 | } 157 | 158 | pub fn tlas(&self) -> &TLAS { 159 | &self.tlas 160 | } 161 | 162 | pub fn blas_transform(&mut self, transform: glam::Mat4, index: usize) { 163 | self.blas[index].set_transform(transform); 164 | for instance_index in &self.blas_to_instances[&index] { 165 | self.instances[*instance_index].update_transform(transform); 166 | } 167 | } 168 | 169 | pub fn blas_transforms(&mut self, transforms: &[glam::Mat4]) { 170 | transforms 171 | .iter() 172 | .enumerate() 173 | .for_each(|(index, transform)| { 174 | self.blas_transform(transform.clone(), index); 175 | }); 176 | } 177 | 178 | pub fn tlas_regenerate(&mut self, cmd: vk::CommandBuffer) { 179 | self.tlas 180 | .regenerate(cmd, &self.blas); 181 | } 182 | 183 | pub fn blas(&self) -> &Vec { 184 | &self.blas 185 | } 186 | 187 | pub fn get_instances_buffer(&self) -> &crate::Buffer { 188 | &self.instances_buffer 189 | } 190 | 191 | pub fn update(&mut self) { 192 | self.instances_buffer.update(&self.instances) 193 | } 194 | 195 | pub fn get_vertex_descriptors(&self) -> &Vec { 196 | &self.vertex_descriptors 197 | } 198 | 199 | pub fn get_index_descriptors(&self) -> &Vec { 200 | &self.index_descriptors 201 | } 202 | 203 | pub fn get_material_descriptors(&self) -> &Vec { 204 | &self.mat_descriptors 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/ray/pipeline.rs: -------------------------------------------------------------------------------- 1 | use crate::{pipeline::Shader, Context, Resource}; 2 | use ash::{vk}; 3 | use std::{ffi::CString, path::PathBuf, sync::Arc}; 4 | 5 | pub struct PipelineInfo { 6 | pub layout: vk::PipelineLayout, 7 | pub shaders: Vec<(PathBuf, vk::ShaderStageFlags)>, 8 | pub name: String, 9 | pub specialization_data: Vec, 10 | pub specialization_entries: Vec, 11 | } 12 | 13 | impl Default for PipelineInfo { 14 | fn default() -> Self { 15 | PipelineInfo { 16 | layout: vk::PipelineLayout::default(), 17 | shaders: Vec::new(), 18 | name: "".to_string(), 19 | specialization_data: Vec::new(), 20 | specialization_entries: Vec::new(), 21 | } 22 | } 23 | } 24 | 25 | impl PipelineInfo { 26 | pub fn layout(mut self, layout: vk::PipelineLayout) -> Self { 27 | self.layout = layout; 28 | self 29 | } 30 | pub fn shader(mut self, path: PathBuf, stage_flags: vk::ShaderStageFlags) -> Self { 31 | self.shaders.push((path, stage_flags)); 32 | self 33 | } 34 | pub fn name(mut self, name: String) -> Self { 35 | self.name = name.to_string(); 36 | self 37 | } 38 | pub fn specialization(mut self, data: &T, constant_id: u32) -> Self { 39 | let slice = unsafe { 40 | std::slice::from_raw_parts(data as *const T as *const u8, std::mem::size_of_val(data)) 41 | }; 42 | self.specialization_data = slice.to_vec(); 43 | self.specialization_entries.push( 44 | vk::SpecializationMapEntry::builder() 45 | .constant_id(constant_id) 46 | .offset(0) 47 | .size(self.specialization_data.len()) 48 | .build(), 49 | ); 50 | self 51 | } 52 | } 53 | 54 | pub struct Pipeline { 55 | context: Arc, 56 | info: PipelineInfo, 57 | pipeline: vk::Pipeline, 58 | } 59 | 60 | impl Pipeline { 61 | pub fn new(context: Arc, info: PipelineInfo) -> Self { 62 | let mut shaders = Vec::::new(); 63 | let mut stages = Vec::new(); 64 | let mut groups = Vec::new(); 65 | let shader_entry_name = CString::new("main").unwrap(); 66 | for (index, shader_info) in info.shaders.iter().enumerate() { 67 | let shader = Shader::new(context.clone(), shader_info.0.clone(), shader_info.1); 68 | if info.specialization_entries.is_empty() { 69 | stages.push(shader.get_create_info(&shader_entry_name)); 70 | } else { 71 | stages.push( 72 | shader.get_create_info_with_specialization( 73 | &shader_entry_name, 74 | &vk::SpecializationInfo::builder() 75 | .map_entries(&info.specialization_entries) 76 | .data(&info.specialization_data), 77 | ), 78 | ); 79 | } 80 | shaders.push(shader); 81 | 82 | let mut group = vk::RayTracingShaderGroupCreateInfoKHR::builder() 83 | .general_shader(vk::SHADER_UNUSED_KHR) 84 | .closest_hit_shader(vk::SHADER_UNUSED_KHR) 85 | .any_hit_shader(vk::SHADER_UNUSED_KHR) 86 | .intersection_shader(vk::SHADER_UNUSED_KHR) 87 | .build(); 88 | if shader_info.1 == vk::ShaderStageFlags::CLOSEST_HIT_KHR { 89 | group.ty = vk::RayTracingShaderGroupTypeKHR::TRIANGLES_HIT_GROUP; 90 | group.closest_hit_shader = index as u32; 91 | } else { 92 | group.ty = vk::RayTracingShaderGroupTypeKHR::GENERAL; 93 | group.general_shader = index as u32; 94 | } 95 | groups.push(group); 96 | } 97 | // TODO: fetch from somewhere 98 | let max_recursion_depth = 8; 99 | let create_info = vk::RayTracingPipelineCreateInfoKHR::builder() 100 | .stages(&stages) 101 | .groups(&groups) 102 | .max_pipeline_ray_recursion_depth(max_recursion_depth) 103 | .layout(info.layout) 104 | .build(); 105 | let pipeline = unsafe { 106 | context 107 | .ray_tracing() 108 | .create_ray_tracing_pipelines( 109 | vk::DeferredOperationKHR::null(), 110 | vk::PipelineCache::null(), 111 | &[create_info], 112 | None 113 | ) 114 | .expect("Unable to create graphics pipeline")[0] 115 | }; 116 | 117 | Pipeline { 118 | context, 119 | info, 120 | pipeline, 121 | } 122 | } 123 | 124 | pub fn update_specialization(&mut self, data: &T) { 125 | let slice = unsafe { 126 | std::slice::from_raw_parts(data as *const T as *const u8, std::mem::size_of_val(data)) 127 | }; 128 | self.info.specialization_data = slice.to_vec(); 129 | } 130 | } 131 | 132 | impl Resource for Pipeline { 133 | fn handle(&self) -> vk::Pipeline { 134 | self.pipeline 135 | } 136 | } 137 | 138 | impl Drop for Pipeline { 139 | fn drop(&mut self) { 140 | unsafe { 141 | self.context.device().destroy_pipeline(self.pipeline, None); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/ray/sbt.rs: -------------------------------------------------------------------------------- 1 | use crate::{Buffer, BufferInfo, Context}; 2 | use ash::vk; 3 | use std::sync::Arc; 4 | 5 | // https://developer.nvidia.com/rtx/raytracing/vkray_helpers 6 | // https://nvpro-samples.github.io/vk_raytracing_tutorial_KHR/#shaderbindingtable 7 | // This implementation is now mostly lifted from https://github.com/EmbarkStudios/kajiya/blob/main/crates/lib/kajiya-backend/src/vulkan/ray_tracing.rs 8 | 9 | pub fn align_up(x: u32, a: u32) -> u32 { 10 | (x + (a - 1)) & !(a - 1) 11 | } 12 | 13 | pub struct ShaderBindingTableInfo { 14 | pub raygen_indices: Vec, 15 | pub miss_indices: Vec, 16 | pub hit_group_indices: Vec, 17 | } 18 | 19 | impl Default for ShaderBindingTableInfo { 20 | fn default() -> Self { 21 | ShaderBindingTableInfo { 22 | raygen_indices: Vec::new(), 23 | miss_indices: Vec::new(), 24 | hit_group_indices: Vec::new(), 25 | } 26 | } 27 | } 28 | 29 | impl ShaderBindingTableInfo { 30 | pub fn raygen(mut self, index: u64) -> Self { 31 | self.raygen_indices.push(index); 32 | self 33 | } 34 | pub fn miss(mut self, index: u64) -> Self { 35 | self.miss_indices.push(index); 36 | self 37 | } 38 | pub fn hitgroup(mut self, index: u64) -> Self { 39 | self.hit_group_indices.push(index); 40 | self 41 | } 42 | 43 | fn raygen_count(&self) -> usize { 44 | self.miss_indices.len() 45 | } 46 | fn miss_count(&self) -> usize { 47 | self.raygen_indices.len() 48 | } 49 | fn hitgroup_count(&self) -> usize { 50 | self.hit_group_indices.len() 51 | } 52 | fn get_total_group_count(&self) -> usize { 53 | self.raygen_count() + self.miss_count() + self.hitgroup_count() 54 | } 55 | } 56 | 57 | // Always internally stores raygen -> miss -> hit groups. 58 | pub struct ShaderBindingTable { 59 | context: Arc, 60 | pub raygen_sbt_address: vk::StridedDeviceAddressRegionKHR, 61 | pub raygen_sbt_buffer: Option, 62 | pub miss_sbt_address: vk::StridedDeviceAddressRegionKHR, 63 | pub miss_sbt_buffer: Option, 64 | pub hit_sbt_address: vk::StridedDeviceAddressRegionKHR, 65 | pub hit_sbt_buffer: Option, 66 | pub callable_sbt_address: vk::StridedDeviceAddressRegionKHR, 67 | pub callable_sbt_buffer: Option, 68 | } 69 | 70 | impl ShaderBindingTable { 71 | pub fn new(context: Arc, pipeline: vk::Pipeline, info: ShaderBindingTableInfo) -> Self { 72 | let shader_group_handle_size = 73 | unsafe{ context.ray_tracing_properties().shader_group_handle_size as usize }; 74 | let group_count = info.get_total_group_count() as usize; 75 | let group_handles_size = (shader_group_handle_size * group_count) as usize; 76 | 77 | let group_handles: Vec = unsafe { 78 | context.ray_tracing() 79 | .get_ray_tracing_shader_group_handles( 80 | pipeline, 81 | 0, 82 | group_count as _, 83 | group_handles_size, 84 | ).unwrap() 85 | }; 86 | 87 | let prog_size = shader_group_handle_size; 88 | 89 | let create_binding_table = 90 | |context: Arc, entry_offset: u32, entry_count: u32| 91 | -> Option { 92 | if 0 == entry_count { 93 | return None; 94 | } 95 | 96 | let mut sbt_data = 97 | vec![0u8; (entry_count as usize * prog_size) as _]; 98 | 99 | for dst in 0..(entry_count as usize) { 100 | let src = dst + entry_offset as usize; 101 | sbt_data 102 | [dst * prog_size..dst * prog_size + shader_group_handle_size] 103 | .copy_from_slice( 104 | &group_handles[src * shader_group_handle_size 105 | ..src * shader_group_handle_size + shader_group_handle_size], 106 | ); 107 | } 108 | 109 | Some(Buffer::from_data( 110 | context.clone(), 111 | BufferInfo::default().gpu_only().usage( 112 | vk::BufferUsageFlags::TRANSFER_SRC 113 | | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS 114 | | vk::BufferUsageFlags::SHADER_BINDING_TABLE_KHR, 115 | ), 116 | &sbt_data 117 | )) 118 | }; 119 | 120 | let raygen_sbt_buffer = create_binding_table(context.clone(), 0, info.raygen_count() as u32); 121 | let miss_sbt_buffer = create_binding_table(context.clone(), 122 | info.raygen_count() as u32, 123 | info.miss_count() as u32); 124 | let hit_sbt_buffer = create_binding_table(context.clone(), 125 | (info.raygen_count() + info.miss_count()) as u32, 126 | info.hitgroup_count() as u32, 127 | ); 128 | 129 | ShaderBindingTable { 130 | context, 131 | raygen_sbt_address: vk::StridedDeviceAddressRegionKHR { 132 | device_address: raygen_sbt_buffer 133 | .as_ref() 134 | .map(|b| b.get_device_address()) 135 | .unwrap_or(0), 136 | stride: prog_size as u64, 137 | size: (prog_size * info.raygen_count() as usize) as u64, 138 | }, 139 | raygen_sbt_buffer, 140 | miss_sbt_address: vk::StridedDeviceAddressRegionKHR { 141 | device_address: miss_sbt_buffer 142 | .as_ref() 143 | .map(|b| b.get_device_address()) 144 | .unwrap_or(0), 145 | stride: prog_size as u64, 146 | size: (prog_size * info.miss_count() as usize) as u64, 147 | }, 148 | miss_sbt_buffer, 149 | hit_sbt_address: vk::StridedDeviceAddressRegionKHR { 150 | device_address: hit_sbt_buffer 151 | .as_ref() 152 | .map(|b| b.get_device_address()) 153 | .unwrap_or(0), 154 | stride: prog_size as u64, 155 | size: (prog_size * info.hitgroup_count() as usize) as u64, 156 | }, 157 | hit_sbt_buffer, 158 | callable_sbt_address: vk::StridedDeviceAddressRegionKHR { 159 | device_address: Default::default(), 160 | stride: 0, 161 | size: 0, 162 | }, 163 | callable_sbt_buffer: None, 164 | } 165 | } 166 | 167 | pub fn cmd_trace_rays(&self, cmd: vk::CommandBuffer, extent: vk::Extent3D) { 168 | unsafe { 169 | self.context.ray_tracing().cmd_trace_rays( 170 | cmd, 171 | &self.raygen_sbt_address, 172 | &self.miss_sbt_address, 173 | &self.hit_sbt_address, 174 | &self.callable_sbt_address, 175 | extent.width, 176 | extent.height, 177 | extent.depth, 178 | ); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/renderer.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use ash::vk; 3 | use std::sync::Arc; 4 | use std::{ffi::CStr, mem::ManuallyDrop}; 5 | 6 | struct AppFrameData { 7 | pub index: usize, 8 | pub in_flight_fence: vk::Fence, 9 | pub semaphore_pool: SemaphorePool, 10 | } 11 | 12 | pub enum AppRenderError { 13 | DirtySwapchain, 14 | } 15 | 16 | static QUERY_POOL_SIZE: u32 = 128; 17 | static QUERY_BEGIN_FRAME: u32 = 0; 18 | static QUERY_END_FRAME: u32 = 1; 19 | 20 | #[derive(Clone, Debug)] 21 | pub struct RendererSettings { 22 | pub samples: u8, 23 | pub depth: bool, 24 | pub clear_color: glam::Vec4, 25 | pub present_mode: vk::PresentModeKHR, 26 | //TODO: Implement frames in flight number that differs from swapchain count 27 | //pub frames_in_flight: usize, 28 | pub extensions: Vec<&'static CStr>, 29 | pub device_extensions: Vec<&'static CStr>, 30 | } 31 | 32 | impl Default for RendererSettings { 33 | fn default() -> Self { 34 | RendererSettings { 35 | samples: 1, 36 | depth: true, 37 | clear_color: glam::Vec4::ZERO, 38 | present_mode: vk::PresentModeKHR::FIFO, 39 | //frames_in_flight: 2, 40 | extensions: Vec::new(), 41 | device_extensions: Vec::new(), 42 | } 43 | } 44 | } 45 | 46 | pub struct AppRenderer { 47 | pub context: Arc, 48 | pub swapchain: ManuallyDrop, 49 | pub renderpass: RenderPass, 50 | pub active_frame_index: usize, 51 | frames: Vec, 52 | framebuffers: Vec, 53 | clear_values: [vk::ClearValue; 2], 54 | settings: RendererSettings, 55 | query_pool: vk::QueryPool, 56 | pub gpu_frame_time: f32, 57 | } 58 | 59 | impl AppRenderer { 60 | pub fn new(window: &mut Window, settings: RendererSettings) -> Self { 61 | unsafe { 62 | let shared_context = Arc::new(SharedContext::new(window, &settings)); 63 | let mut swapchain = Swapchain::new(shared_context.clone(), &window, &settings); 64 | let context = Arc::new(Context::new( 65 | shared_context.clone(), 66 | swapchain.get_image_count(), 67 | )); 68 | swapchain.transition_depth_images(&context); 69 | let renderpass = swapchain.create_compatible_render_pass(); 70 | let framebuffers = swapchain.create_framebuffers(&renderpass, &window); 71 | 72 | let fence_create_info = 73 | vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::SIGNALED); 74 | let mut frames = Vec::::new(); 75 | for i in 0..swapchain.get_image_count() { 76 | let frame = AppFrameData { 77 | index: i, 78 | in_flight_fence: shared_context 79 | .device() 80 | .create_fence(&fence_create_info, None) 81 | .expect("Create fence failed."), 82 | semaphore_pool: SemaphorePool::new(shared_context.clone()), 83 | }; 84 | frames.push(frame); 85 | } 86 | let clear_values = [ 87 | vk::ClearValue { 88 | color: vk::ClearColorValue { 89 | float32: settings.clear_color.into(), 90 | }, 91 | }, 92 | vk::ClearValue { 93 | depth_stencil: vk::ClearDepthStencilValue { 94 | depth: 1.0, 95 | stencil: 0, 96 | }, 97 | }, 98 | ]; 99 | 100 | let query_create_info = vk::QueryPoolCreateInfo::builder() 101 | .query_type(vk::QueryType::TIMESTAMP) 102 | .query_count(QUERY_POOL_SIZE); 103 | let query_pool = context 104 | .device() 105 | .create_query_pool(&query_create_info, None) 106 | .expect("Failed to create query pool."); 107 | 108 | AppRenderer { 109 | swapchain: ManuallyDrop::new(swapchain), 110 | frames, 111 | renderpass, 112 | framebuffers, 113 | clear_values, 114 | context, 115 | active_frame_index: 0, 116 | settings, 117 | query_pool, 118 | gpu_frame_time: 0.0, 119 | } 120 | } 121 | } 122 | 123 | pub fn wait_for_and_reset_fence(&self, fence: vk::Fence) { 124 | unsafe { 125 | let fences = [fence]; 126 | self.context 127 | .device() 128 | .wait_for_fences(&fences, true, std::u64::MAX) 129 | .expect("Wait for fence failed."); 130 | 131 | self.context.device().reset_fences(&fences).unwrap(); 132 | } 133 | } 134 | 135 | pub fn recreate_swapchain(&mut self, window: &Window) { 136 | unsafe { 137 | self.context.device().device_wait_idle().unwrap(); 138 | } 139 | 140 | for framebuffer in self.framebuffers.iter() { 141 | unsafe { 142 | self.context 143 | .device() 144 | .destroy_framebuffer(*framebuffer, None); 145 | } 146 | } 147 | 148 | unsafe { 149 | ManuallyDrop::drop(&mut self.swapchain); 150 | } 151 | 152 | self.swapchain = ManuallyDrop::new(Swapchain::new( 153 | self.context.shared().clone(), 154 | window, 155 | &self.settings, 156 | )); 157 | self.swapchain.transition_depth_images(&self.context); 158 | 159 | self.framebuffers = self 160 | .swapchain 161 | .create_framebuffers(&self.renderpass, &window); 162 | } 163 | 164 | pub fn acquire_next_image(&mut self) -> Result<(vk::Semaphore, usize), AppRenderError> { 165 | unsafe { 166 | let aquired_semaphore = self.frames[self.active_frame_index] 167 | .semaphore_pool 168 | .request_semaphore(); 169 | let result = self.swapchain.swapchain_loader.acquire_next_image( 170 | self.swapchain.handle(), 171 | std::u64::MAX, 172 | aquired_semaphore, 173 | vk::Fence::null(), 174 | ); 175 | let image_index = match result { 176 | Ok((image_index, _)) => image_index, 177 | Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => { 178 | return Err(AppRenderError::DirtySwapchain); 179 | } 180 | Err(vk::Result::SUBOPTIMAL_KHR) => { 181 | return Err(AppRenderError::DirtySwapchain); 182 | } 183 | Err(error) => panic!("Error while acquiring next image. Cause: {}", error), 184 | }; 185 | 186 | self.active_frame_index = image_index as usize; 187 | self.frames[self.active_frame_index].semaphore_pool.reset(); 188 | self.wait_for_and_reset_fence(self.frames[self.active_frame_index].in_flight_fence); 189 | 190 | Ok((aquired_semaphore, self.active_frame_index)) 191 | } 192 | } 193 | 194 | pub fn begin_command_buffer(&mut self) -> vk::CommandBuffer { 195 | let cmd = self.context.request_command_buffer(self.active_frame_index); 196 | unsafe { 197 | let begin_info = vk::CommandBufferBeginInfo::builder() 198 | .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); 199 | self.context 200 | .device() 201 | .begin_command_buffer(cmd, &begin_info) 202 | .expect("Begin frame commands."); 203 | 204 | self.context 205 | .device() 206 | .cmd_reset_query_pool(cmd, self.query_pool, 0, QUERY_POOL_SIZE); 207 | 208 | self.context.device().cmd_write_timestamp( 209 | cmd, 210 | vk::PipelineStageFlags::BOTTOM_OF_PIPE, 211 | self.query_pool, 212 | QUERY_BEGIN_FRAME, 213 | ); 214 | } 215 | cmd 216 | } 217 | 218 | pub fn end_command_buffer(&self, cmd: vk::CommandBuffer) { 219 | unsafe { 220 | self.context.device().cmd_write_timestamp( 221 | cmd, 222 | vk::PipelineStageFlags::BOTTOM_OF_PIPE, 223 | self.query_pool, 224 | QUERY_END_FRAME, 225 | ); 226 | self.context 227 | .device() 228 | .end_command_buffer(cmd) 229 | .expect("End frame commands."); 230 | } 231 | } 232 | 233 | pub fn begin_renderpass(&self, command_buffer: vk::CommandBuffer, extent: vk::Extent2D) { 234 | unsafe { 235 | let render_pass_begin_info = vk::RenderPassBeginInfo::builder() 236 | .render_pass(self.renderpass.handle()) 237 | .framebuffer(self.framebuffers[self.active_frame_index]) 238 | .render_area(vk::Rect2D { 239 | offset: vk::Offset2D { x: 0, y: 0 }, 240 | extent, 241 | }) 242 | .clear_values(&self.clear_values) 243 | .build(); 244 | self.context.device().cmd_begin_render_pass( 245 | command_buffer, 246 | &render_pass_begin_info, 247 | vk::SubpassContents::INLINE, 248 | ); 249 | } 250 | } 251 | 252 | pub fn end_renderpass(&self, command_buffer: vk::CommandBuffer) { 253 | unsafe { 254 | self.context.device().cmd_end_render_pass(command_buffer); 255 | } 256 | } 257 | 258 | pub fn submit_and_present( 259 | &mut self, 260 | command_buffer: vk::CommandBuffer, 261 | wait_semaphore: vk::Semaphore, 262 | ) -> Result<(), AppRenderError> { 263 | let rendering_complete_semaphore = self.submit_frame( 264 | &[command_buffer], 265 | &[wait_semaphore], 266 | &[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT], 267 | ); 268 | self.present_frame(rendering_complete_semaphore)?; 269 | 270 | let mut query_data = [0u32; 2]; 271 | unsafe { 272 | self.context 273 | .device() 274 | .get_query_pool_results( 275 | self.query_pool, 276 | 0, 277 | 2, 278 | &mut query_data, 279 | vk::QueryResultFlags::WAIT, 280 | ) 281 | .expect("Failed to read query results"); 282 | } 283 | let begin_time = query_data[0] as f32 284 | * self.context.get_physical_device_limits().timestamp_period 285 | * 1e-6; 286 | let end_time = query_data[1] as f32 287 | * self.context.get_physical_device_limits().timestamp_period 288 | * 1e-6; 289 | self.gpu_frame_time = end_time - begin_time; 290 | Ok(()) 291 | } 292 | 293 | pub fn submit_frame( 294 | &mut self, 295 | command_buffers: &[vk::CommandBuffer], 296 | wait_semaphores: &[vk::Semaphore], 297 | stage_flags: &[vk::PipelineStageFlags], 298 | ) -> vk::Semaphore { 299 | unsafe { 300 | let rendering_complete_semaphore = self.frames[self.active_frame_index] 301 | .semaphore_pool 302 | .request_semaphore(); 303 | let signal_semaphores = [rendering_complete_semaphore]; 304 | let submit_info = vk::SubmitInfo::builder() 305 | .wait_semaphores(wait_semaphores) 306 | .wait_dst_stage_mask(stage_flags) 307 | .command_buffers(command_buffers) 308 | .signal_semaphores(&signal_semaphores); 309 | 310 | self.context 311 | .device() 312 | .queue_submit( 313 | self.context.graphics_queue(), 314 | &[submit_info.build()], 315 | self.frames[self.active_frame_index].in_flight_fence, 316 | ) 317 | .expect("queue submit failed."); 318 | 319 | rendering_complete_semaphore 320 | } 321 | } 322 | 323 | pub fn present_frame(&self, wait_semaphore: vk::Semaphore) -> Result<(), AppRenderError> { 324 | let wait_semaphores = [wait_semaphore]; 325 | let swapchains = [self.swapchain.handle()]; 326 | let image_indices = [self.active_frame_index as u32]; 327 | let present_info = vk::PresentInfoKHR::builder() 328 | .wait_semaphores(&wait_semaphores) 329 | .swapchains(&swapchains) 330 | .image_indices(&image_indices); 331 | 332 | unsafe { 333 | let result = self 334 | .swapchain 335 | .swapchain_loader 336 | .queue_present(self.context.present_queue(), &present_info); 337 | 338 | match result { 339 | Ok(_) => {} 340 | Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => { 341 | return Err(AppRenderError::DirtySwapchain); 342 | } 343 | Err(vk::Result::SUBOPTIMAL_KHR) => { 344 | return Err(AppRenderError::DirtySwapchain); 345 | } 346 | Err(error) => panic!("Error while presenting image. Cause: {}", error), 347 | }; 348 | 349 | Ok(()) 350 | } 351 | } 352 | 353 | pub fn begin_frame_default( 354 | &mut self, 355 | ) -> Result<(vk::Semaphore, vk::CommandBuffer), AppRenderError> { 356 | let (image_aquired_semaphore, _) = self.acquire_next_image()?; 357 | let cmd = self.begin_command_buffer(); 358 | self.begin_renderpass(cmd, self.swapchain.get_extent()); 359 | Ok((image_aquired_semaphore, cmd)) 360 | } 361 | 362 | pub fn end_frame_default( 363 | &mut self, 364 | image_aquired_semaphore: vk::Semaphore, 365 | cmd: vk::CommandBuffer, 366 | ) -> Result<(), AppRenderError> { 367 | self.end_renderpass(cmd); 368 | self.end_command_buffer(cmd); 369 | self.submit_and_present(cmd, image_aquired_semaphore)?; 370 | Ok(()) 371 | } 372 | 373 | pub fn get_renderpass(&self) -> vk::RenderPass { 374 | self.renderpass.handle() 375 | } 376 | 377 | pub fn get_frames_count(&self) -> usize { 378 | self.frames.len() 379 | } 380 | } 381 | 382 | impl Drop for AppRenderer { 383 | fn drop(&mut self) { 384 | unsafe { 385 | let ctx = self.context.as_ref(); 386 | let device = ctx.device(); 387 | 388 | device.destroy_query_pool(self.query_pool, None); 389 | 390 | device.device_wait_idle().unwrap(); 391 | 392 | for framebuffer in self.framebuffers.iter() { 393 | device.destroy_framebuffer(*framebuffer, None); 394 | } 395 | 396 | self.frames.iter().for_each(|fence| { 397 | device.destroy_fence(fence.in_flight_fence, None); 398 | }); 399 | 400 | ManuallyDrop::drop(&mut self.swapchain); 401 | } 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /src/renderpass.rs: -------------------------------------------------------------------------------- 1 | use crate::{Image2d, Resource, SharedContext}; 2 | use ash::{vk}; 3 | use std::sync::Arc; 4 | 5 | #[derive(Default)] 6 | pub struct RenderPassInfo<'a> { 7 | pub color_images: Vec<&'a Image2d>, 8 | pub depth_stencil_image: Option<&'a Image2d>, 9 | pub resolve_images: Vec<&'a Image2d>, 10 | pub present: bool, 11 | pub samples: vk::SampleCountFlags, 12 | pub final_layout: vk::ImageLayout, 13 | } 14 | 15 | #[derive(Clone, Default)] 16 | pub struct TransientRenderPassInfo { 17 | pub color_formats: Vec, 18 | pub depth_stencil_format: Option, 19 | pub resolve_formats: Vec, 20 | pub samples: vk::SampleCountFlags, 21 | } 22 | 23 | pub struct RenderPass { 24 | context: Arc, 25 | render_pass: vk::RenderPass, 26 | } 27 | 28 | impl RenderPass { 29 | pub fn new(context: Arc, info: RenderPassInfo) -> Self { 30 | unsafe { 31 | let mut index = 0u32; 32 | let mut attachments_desc = Vec::::new(); 33 | 34 | let mut color_attachment_refs = Vec::::new(); 35 | for color_image in info.color_images { 36 | let mut layout = vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL; 37 | if info.present && info.resolve_images.is_empty() { 38 | layout = info.final_layout; 39 | } 40 | attachments_desc.push( 41 | vk::AttachmentDescription::builder() 42 | .format(color_image.get_format()) 43 | .samples(info.samples) 44 | .load_op(vk::AttachmentLoadOp::CLEAR) 45 | .store_op(vk::AttachmentStoreOp::STORE) 46 | .final_layout(layout) 47 | .build(), 48 | ); 49 | color_attachment_refs.push(vk::AttachmentReference { 50 | attachment: index, 51 | layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, 52 | }); 53 | index += 1; 54 | } 55 | 56 | let mut depth_attachment_refs = Vec::::new(); 57 | match info.depth_stencil_image { 58 | Some(image) => { 59 | attachments_desc.push( 60 | vk::AttachmentDescription::builder() 61 | .format(image.get_format()) 62 | .samples(info.samples) 63 | .load_op(vk::AttachmentLoadOp::CLEAR) 64 | .initial_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL) 65 | .final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL) 66 | .build(), 67 | ); 68 | depth_attachment_refs.push(vk::AttachmentReference { 69 | attachment: index, 70 | layout: vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 71 | }); 72 | index += 1; 73 | } 74 | None => {} 75 | } 76 | 77 | let mut resolve_attachment_refs = Vec::::new(); 78 | for resolve_image in &info.resolve_images { 79 | attachments_desc.push( 80 | vk::AttachmentDescription::builder() 81 | .format(resolve_image.get_format()) 82 | .samples(vk::SampleCountFlags::TYPE_1) 83 | .load_op(vk::AttachmentLoadOp::DONT_CARE) 84 | .store_op(vk::AttachmentStoreOp::STORE) 85 | .final_layout(info.final_layout) 86 | .build(), 87 | ); 88 | resolve_attachment_refs.push(vk::AttachmentReference { 89 | attachment: index, 90 | layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, 91 | }); 92 | index += 1; 93 | } 94 | 95 | let dependencies = [vk::SubpassDependency { 96 | src_subpass: vk::SUBPASS_EXTERNAL, 97 | src_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, 98 | dst_access_mask: vk::AccessFlags::COLOR_ATTACHMENT_READ 99 | | vk::AccessFlags::COLOR_ATTACHMENT_WRITE, 100 | dst_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, 101 | ..Default::default() 102 | }]; 103 | 104 | let mut subpass_builder = vk::SubpassDescription::builder() 105 | .color_attachments(&color_attachment_refs) 106 | .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS); 107 | if info.depth_stencil_image.is_some() { 108 | subpass_builder = 109 | subpass_builder.depth_stencil_attachment(&depth_attachment_refs[0]) 110 | } 111 | if !info.resolve_images.is_empty() { 112 | subpass_builder = subpass_builder.resolve_attachments(&resolve_attachment_refs); 113 | } 114 | let subpasses = [subpass_builder.build()]; 115 | 116 | let create_info = vk::RenderPassCreateInfo::builder() 117 | .attachments(&attachments_desc) 118 | .subpasses(&subpasses) 119 | .dependencies(&dependencies); 120 | let render_pass = context 121 | .device() 122 | .create_render_pass(&create_info, None) 123 | .unwrap(); 124 | Self { 125 | context, 126 | render_pass, 127 | } 128 | } 129 | } 130 | 131 | pub fn new_transient(context: Arc, info: TransientRenderPassInfo) -> Self { 132 | let mut index = 0u32; 133 | let mut attachments_desc = Vec::::new(); 134 | let mut color_attachment_refs = Vec::::new(); 135 | for color_format in info.color_formats { 136 | attachments_desc.push( 137 | vk::AttachmentDescription::builder() 138 | .format(color_format) 139 | .samples(info.samples) 140 | .load_op(vk::AttachmentLoadOp::DONT_CARE) 141 | .final_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL) //ignored 142 | .build(), 143 | ); 144 | color_attachment_refs.push(vk::AttachmentReference { 145 | attachment: index, 146 | layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, 147 | }); 148 | index += 1; 149 | break; 150 | } 151 | 152 | let mut depth_attachment_refs = Vec::::new(); 153 | match info.depth_stencil_format { 154 | Some(format) => { 155 | attachments_desc.push( 156 | vk::AttachmentDescription::builder() 157 | .format(format) 158 | .samples(info.samples) 159 | .load_op(vk::AttachmentLoadOp::DONT_CARE) 160 | .final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL) //ignored 161 | .build(), 162 | ); 163 | depth_attachment_refs.push(vk::AttachmentReference { 164 | attachment: index, 165 | layout: vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 166 | }); 167 | index += 1; 168 | } 169 | None => {} 170 | } 171 | 172 | let mut resolve_attachment_refs = Vec::::new(); 173 | for resolve_format in &info.resolve_formats { 174 | attachments_desc.push( 175 | vk::AttachmentDescription::builder() 176 | .format(resolve_format.clone()) 177 | .samples(vk::SampleCountFlags::TYPE_1) 178 | .load_op(vk::AttachmentLoadOp::DONT_CARE) 179 | .final_layout(vk::ImageLayout::PRESENT_SRC_KHR) //ignored 180 | .build(), 181 | ); 182 | resolve_attachment_refs.push(vk::AttachmentReference { 183 | attachment: index, 184 | layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, 185 | }); 186 | //index += 1; 187 | break; 188 | } 189 | let mut subpass_builder = vk::SubpassDescription::builder() 190 | .color_attachments(&color_attachment_refs) 191 | .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS); 192 | if info.depth_stencil_format.is_some() { 193 | subpass_builder = subpass_builder.depth_stencil_attachment(&depth_attachment_refs[0]) 194 | } 195 | if !info.resolve_formats.is_empty() { 196 | subpass_builder = subpass_builder.resolve_attachments(&resolve_attachment_refs); 197 | } 198 | let subpasses = [subpass_builder.build()]; 199 | let render_pass = unsafe { 200 | context 201 | .device() 202 | .create_render_pass( 203 | &vk::RenderPassCreateInfo::builder() 204 | .attachments(&attachments_desc) 205 | .subpasses(&subpasses) 206 | .dependencies(&[vk::SubpassDependency { 207 | src_subpass: vk::SUBPASS_EXTERNAL, 208 | src_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, 209 | dst_access_mask: vk::AccessFlags::COLOR_ATTACHMENT_READ 210 | | vk::AccessFlags::COLOR_ATTACHMENT_WRITE, 211 | dst_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, 212 | ..Default::default() 213 | }]), 214 | None, 215 | ) 216 | .unwrap() 217 | }; 218 | Self { 219 | context, 220 | render_pass, 221 | } 222 | } 223 | 224 | pub fn new_raw(context: Arc, create_info: &vk::RenderPassCreateInfo) -> Self { 225 | unsafe { 226 | let render_pass = context 227 | .device() 228 | .create_render_pass(create_info, None) 229 | .unwrap(); 230 | Self { 231 | context, 232 | render_pass, 233 | } 234 | } 235 | } 236 | } 237 | 238 | impl Resource for RenderPass { 239 | fn handle(&self) -> vk::RenderPass { 240 | self.render_pass 241 | } 242 | } 243 | 244 | impl Drop for RenderPass { 245 | fn drop(&mut self) { 246 | unsafe { 247 | self.context 248 | .device() 249 | .destroy_render_pass(self.render_pass, None); 250 | } 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/scene/camera.rs: -------------------------------------------------------------------------------- 1 | use glam::*; 2 | use winit::event::WindowEvent; 3 | 4 | enum Modes { 5 | Examine, 6 | Fly, 7 | Walk, 8 | Trackball, 9 | } 10 | enum Actions { 11 | None, 12 | Orbit, 13 | Dolly, 14 | Pan, 15 | LookAround, 16 | } 17 | #[derive(Default, Debug, Clone, Copy)] 18 | pub struct CameraInput { 19 | pub lmb: bool, 20 | pub mmb: bool, 21 | pub rmb: bool, 22 | pub shift: bool, 23 | pub ctrl: bool, 24 | pub alt: bool, 25 | } 26 | 27 | impl CameraInput { 28 | pub fn is_mouse_down(&self) -> bool { 29 | self.lmb || self.mmb || self.rmb 30 | } 31 | } 32 | 33 | #[derive(Default, Debug, Clone, Copy)] 34 | pub struct Camera { 35 | input: CameraInput, 36 | position: Vec3, 37 | center: Vec3, 38 | up: Vec3, 39 | vfov: f32, 40 | z_near: f32, 41 | z_far: f32, 42 | view_matrix: Mat4, 43 | persp_matrix: Mat4, 44 | mouse_pos: Vec2, 45 | window_size: Vec2, 46 | speed: f32, 47 | } 48 | 49 | fn is_zero(value: f32) -> bool { 50 | value.abs() < f32::EPSILON 51 | } 52 | 53 | impl Camera { 54 | pub fn new(window_size: Vec2) -> Self { 55 | let mut camera = Camera { 56 | input: CameraInput::default(), 57 | position: Vec3::splat(10.0), 58 | center: Vec3::ZERO, 59 | up: -Vec3::Y, 60 | vfov: 35.0, 61 | z_near: 0.1, 62 | z_far: 1000.0, 63 | view_matrix: Mat4::IDENTITY, 64 | persp_matrix: Mat4::IDENTITY, 65 | mouse_pos: Vec2::ZERO, 66 | window_size, 67 | speed: 30.0, 68 | }; 69 | camera.update_persp(); 70 | camera 71 | } 72 | 73 | pub fn from_view(view:Mat4, yfov:f32, z_near:f32, z_far:f32) -> Self { 74 | let view_inverse = view.inverse(); 75 | let position = view_inverse * vec4(0.0,0.0,0.0,1.0); 76 | let up = view_inverse * vec4(0.0,1.0,0.0,0.0); 77 | let center = position + view_inverse * vec4(0.0,0.0,-4.0,0.0); 78 | 79 | let camera = Camera { 80 | input: CameraInput::default(), 81 | position: position.xyz(), 82 | center: center.xyz(), 83 | up: up.xyz(), 84 | vfov: yfov, 85 | z_near, 86 | z_far, 87 | view_matrix: view, 88 | persp_matrix: Mat4::IDENTITY, 89 | mouse_pos: Vec2::ZERO, 90 | window_size: vec2(1920.0, 1080.0), 91 | speed: 30.0, 92 | }; 93 | camera 94 | } 95 | } 96 | 97 | impl Camera { 98 | fn update_view(&mut self) { 99 | self.view_matrix = Mat4::look_at_rh(self.position, self.center, self.up); 100 | } 101 | 102 | fn update_persp(&mut self) { 103 | let aspect = self.window_size.x / self.window_size.y; 104 | self.persp_matrix = 105 | Mat4::perspective_rh(self.vfov.to_radians(), aspect, self.z_near, self.z_far); 106 | } 107 | 108 | pub fn look_at(&mut self, eye: Vec3, center: Vec3, up: Vec3) { 109 | self.position = eye; 110 | self.center = center; 111 | self.up = up; 112 | self.update_view(); 113 | } 114 | 115 | pub fn set_window_size(&mut self, window_size: Vec2) { 116 | self.window_size = window_size; 117 | self.update_persp(); 118 | } 119 | 120 | pub fn set_mouse_pos(&mut self, x: f32, y: f32) { 121 | self.mouse_pos = vec2(x, y); 122 | } 123 | 124 | pub fn set_vfov(&mut self, vfov: f32) { 125 | self.vfov = vfov; 126 | self.update_persp(); 127 | } 128 | 129 | pub fn mouse_move(&mut self, x: f32, y: f32, input: &CameraInput) -> bool { 130 | let mut moved = false; 131 | let mut action = Actions::None; 132 | if input.lmb { 133 | if ((input.ctrl) && (input.shift)) || input.alt { 134 | action = Actions::Orbit; 135 | } else if input.shift { 136 | action = Actions::Dolly; 137 | } else if input.ctrl { 138 | action = Actions::Pan; 139 | } else { 140 | action = Actions::LookAround; 141 | } 142 | } else if input.mmb { 143 | action = Actions::Pan; 144 | } else if input.rmb { 145 | action = Actions::Dolly; 146 | } 147 | 148 | let dx = (x - self.mouse_pos.x) / self.window_size.x; 149 | let dy = (y - self.mouse_pos.y) / self.window_size.y; 150 | match action { 151 | Actions::None => {} 152 | Actions::Orbit => { 153 | self.orbit(dx, -dy); 154 | moved = true; 155 | } 156 | Actions::Dolly => { 157 | self.dolly(0.0, dy); 158 | moved = true; 159 | } 160 | Actions::Pan => { 161 | self.pan(dx, -dy); 162 | moved = true; 163 | } 164 | Actions::LookAround => {} 165 | } 166 | if moved { 167 | self.update_view(); 168 | } 169 | self.mouse_pos = vec2(x, y); 170 | 171 | moved 172 | } 173 | 174 | pub fn mouse_wheel(&mut self, value: i32) { 175 | let fval = value as f32; 176 | let dx = fval * fval.abs() / self.window_size.x; 177 | self.dolly(0.0, -dx * self.speed); 178 | self.update_view(); 179 | } 180 | 181 | fn pan(&mut self, dx: f32, dy: f32) { 182 | let z = self.position - self.center; 183 | let length = z.length() / 0.785; // 45 degrees 184 | let z = z.normalize(); 185 | let mut x = self.up.cross(z).normalize(); 186 | let mut y = z.cross(x).normalize(); 187 | 188 | x *= -dx * length; 189 | y *= dy * length; 190 | 191 | self.position += x + y; 192 | self.center += x + y; 193 | } 194 | 195 | fn orbit(&mut self, mut dx: f32, mut dy: f32) { 196 | if is_zero(dx) && is_zero(dy) { 197 | return; 198 | } 199 | 200 | // Full width will do a full turn 201 | dx *= std::f32::consts::TAU; 202 | dy *= std::f32::consts::TAU; 203 | 204 | // Get the camera 205 | let origin = self.center; 206 | let position = self.position; 207 | 208 | // Get the length of sight 209 | let mut center_to_eye = position - origin; 210 | let radius = center_to_eye.length(); 211 | center_to_eye = center_to_eye.normalize(); 212 | 213 | // Find the rotation around the UP axis (Y) 214 | let axe_z = center_to_eye; 215 | let rot_y = Mat4::from_axis_angle(self.up, -dx); 216 | 217 | // Apply the (Y) rotation to the eye-center vector 218 | let mut tmp = rot_y.mul_vec4(vec4(center_to_eye.x, center_to_eye.y, center_to_eye.z, 0.0)); 219 | center_to_eye = vec3(tmp.x, tmp.y, tmp.z); 220 | 221 | // Find the rotation around the X vector: cross between eye-center and up (X) 222 | let axe_x = self.up.cross(axe_z).normalize(); 223 | let rot_x = Mat4::from_axis_angle(axe_x, -dy); 224 | 225 | // Apply the (X) rotation to the eye-center vector 226 | tmp = rot_x.mul_vec4(vec4(center_to_eye.x, center_to_eye.y, center_to_eye.z, 0.0)); 227 | let vect_rot = vec3(tmp.x, tmp.y, tmp.z); 228 | if vect_rot.x.signum() == center_to_eye.x.signum() { 229 | center_to_eye = vect_rot; 230 | } 231 | 232 | // Make the vector as long as it was originally 233 | center_to_eye *= radius; 234 | 235 | // Finding the new position 236 | self.position = origin + center_to_eye; 237 | } 238 | 239 | fn dolly(&mut self, dx: f32, dy: f32) { 240 | let mut z = self.center - self.position; 241 | let mut length = z.length(); 242 | if is_zero(length) { 243 | return; 244 | } 245 | 246 | // Use the larger movement. 247 | let dd = if dx.abs() > dy.abs() { dx } else { -dy }; 248 | let mut factor = self.speed * dd / length; 249 | 250 | // Adjust speed based on distance. 251 | length /= 10.0; 252 | length = length.max(0.001); 253 | factor *= length; 254 | 255 | // Don't move to or through the point of interest. 256 | if factor >= 1.0 { 257 | return; 258 | } 259 | 260 | z *= factor; 261 | self.position += z; 262 | } 263 | 264 | pub fn view_matrix(&self) -> Mat4 { 265 | self.view_matrix 266 | } 267 | 268 | pub fn perspective_matrix(&self) -> Mat4 { 269 | self.persp_matrix 270 | } 271 | } 272 | 273 | pub struct CameraManip { 274 | pub input: CameraInput, 275 | pub camera: Camera, 276 | } 277 | 278 | impl CameraManip { 279 | pub fn update(&mut self, window_event: &WindowEvent) -> bool { 280 | let mut moved = false; 281 | match window_event { 282 | WindowEvent::Resized(winit::dpi::PhysicalSize { width, height }) => { 283 | self.camera 284 | .set_window_size(vec2(*width as f32, *height as f32)); 285 | } 286 | WindowEvent::ModifiersChanged(m) => { 287 | self.input.alt = m.alt(); 288 | self.input.ctrl = m.ctrl() || m.logo(); 289 | self.input.shift = m.shift(); 290 | } 291 | WindowEvent::CursorMoved { position, .. } => { 292 | let pos = vec2(position.x as f32, position.y as f32); 293 | if self.input.is_mouse_down() { 294 | moved = self.camera.mouse_move(pos.x, pos.y, &self.input); 295 | } else { 296 | self.camera.set_mouse_pos(pos.x, pos.y); 297 | } 298 | } 299 | WindowEvent::MouseWheel { delta, .. } => { 300 | match delta { 301 | winit::event::MouseScrollDelta::PixelDelta(_) => { 302 | //camera.mouse_wheel(d.x.max(d.y) as i32); 303 | } 304 | winit::event::MouseScrollDelta::LineDelta(_, y) => { 305 | self.camera.mouse_wheel(*y as i32); 306 | moved = true; 307 | } 308 | }; 309 | } 310 | WindowEvent::MouseInput { state, button, .. } => { 311 | let is_down = match state { 312 | winit::event::ElementState::Pressed => true, 313 | winit::event::ElementState::Released => false, 314 | }; 315 | match button { 316 | winit::event::MouseButton::Left => { 317 | self.input.lmb = is_down; 318 | } 319 | winit::event::MouseButton::Right => { 320 | self.input.rmb = is_down; 321 | } 322 | winit::event::MouseButton::Middle => { 323 | self.input.mmb = is_down; 324 | } 325 | _ => {} 326 | } 327 | } 328 | _ => {} 329 | } 330 | moved 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /src/scene/mesh.rs: -------------------------------------------------------------------------------- 1 | use super::PrimitiveSection; 2 | use crate::{offset_of, Buffer, Context, Resource, Vertex}; 3 | use ash::{vk}; 4 | use std::sync::Arc; 5 | 6 | //TODO: solve non-vec4-aligned issues.. 7 | #[repr(C)] 8 | #[derive(Clone, Debug, Copy)] 9 | pub struct ModelVertex { 10 | pub pos: glam::Vec4, 11 | pub color: glam::Vec4, 12 | pub normal: glam::Vec4, 13 | pub uv: glam::Vec4, 14 | } 15 | 16 | impl Default for ModelVertex { 17 | fn default() -> Self { 18 | ModelVertex { 19 | pos: glam::vec4(0f32, 0.0, 0.0, 1.0), 20 | color: glam::Vec4::splat(1.0), 21 | normal: glam::Vec4::ZERO, 22 | uv: glam::Vec4::ZERO, 23 | } 24 | } 25 | } 26 | 27 | impl Vertex for ModelVertex { 28 | fn stride() -> u32 { 29 | std::mem::size_of::() as u32 30 | } 31 | fn format_offset() -> Vec<(vk::Format, u32)> { 32 | vec![ 33 | ( 34 | vk::Format::R32G32B32A32_SFLOAT, 35 | offset_of!(ModelVertex, pos) as u32, 36 | ), 37 | ( 38 | vk::Format::R32G32B32A32_SFLOAT, 39 | offset_of!(ModelVertex, color) as u32, 40 | ), 41 | ( 42 | vk::Format::R32G32B32A32_SFLOAT, 43 | offset_of!(ModelVertex, normal) as u32, 44 | ), 45 | ( 46 | vk::Format::R32G32B32A32_SFLOAT, 47 | offset_of!(ModelVertex, uv) as u32, 48 | ), 49 | ] 50 | } 51 | } 52 | 53 | pub struct Mesh { 54 | pub context: Arc, 55 | pub name: String, 56 | pub vertex_buffer: Buffer, 57 | pub index_buffer: Option, 58 | pub index_storage: Option, 59 | pub transform: glam::Mat4, 60 | pub primitive_sections: Vec, 61 | } 62 | 63 | impl Mesh { 64 | pub fn cmd_draw(&self, cmd: vk::CommandBuffer) { 65 | let device = self.context.device(); 66 | unsafe { 67 | match &self.index_buffer { 68 | Some(indices) => { 69 | for section in &self.primitive_sections { 70 | device.cmd_bind_vertex_buffers( 71 | cmd, 72 | 0, 73 | &[self.vertex_buffer.handle()], 74 | &[section.get_vertex_offset_size()], 75 | ); 76 | device.cmd_bind_index_buffer( 77 | cmd, 78 | indices.handle(), 79 | section.get_index_offset_size::(), 80 | vk::IndexType::UINT32, 81 | ); 82 | device.cmd_draw_indexed(cmd, section.get_index_count(), 1, 0, 0, 1); 83 | } 84 | } 85 | None => { 86 | for section in &self.primitive_sections { 87 | device.cmd_bind_vertex_buffers( 88 | cmd, 89 | 0, 90 | &[self.vertex_buffer.handle()], 91 | &[section.get_vertex_offset_size()], 92 | ); 93 | device.cmd_draw(cmd, section.get_vertex_count(), 1, 0, 1); 94 | } 95 | } 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/scene/mod.rs: -------------------------------------------------------------------------------- 1 | mod camera; 2 | pub use camera::*; 3 | 4 | // Much of this was directly based on: 5 | // https://github.com/adrien-ben/gltf-viewer-rs/blob/master/model/src/mesh.rs 6 | 7 | mod mesh; 8 | pub use mesh::*; 9 | 10 | use crate::{Buffer, BufferInfo, Context}; 11 | use ash::vk; 12 | use gltf::{ 13 | buffer::Buffer as GltfBuffer, 14 | mesh::{Reader, Semantic}, 15 | }; 16 | use std::path::PathBuf; 17 | use std::sync::Arc; 18 | 19 | #[repr(C)] 20 | #[derive(Clone, Copy, Debug, Default)] 21 | pub struct MaterialInfo { 22 | pub base_color: glam::Vec4, 23 | pub emissive_factor: glam::Vec3, 24 | pub padding0: f32, 25 | pub metallic_factor: f32, 26 | pub roughness_factor: f32, 27 | pub padding1: f32, 28 | pub padding2: f32, 29 | } 30 | 31 | #[derive(Clone, Copy, Debug)] 32 | pub struct BufferPart { 33 | pub offset: usize, 34 | pub element_count: usize, 35 | } 36 | 37 | #[derive(Clone, Copy, Debug)] 38 | pub struct PrimitiveSection { 39 | index: usize, 40 | vertices: BufferPart, 41 | indices: Option, 42 | material_index: Option, 43 | //aabb: AABB, 44 | } 45 | 46 | impl PrimitiveSection { 47 | pub fn get_index_descriptor(&self, buffer: &Buffer) -> vk::DescriptorBufferInfo { 48 | let size = std::mem::size_of::() as u64; 49 | buffer.get_descriptor_info_offset( 50 | self.indices.unwrap().offset as u64 * size, 51 | self.indices.unwrap().element_count as u64 * size, 52 | ) 53 | } 54 | 55 | pub fn get_vertex_descriptor(&self, buffer: &Buffer) -> vk::DescriptorBufferInfo { 56 | let size = std::mem::size_of::() as u64; 57 | buffer.get_descriptor_info_offset( 58 | self.vertices.offset as u64 * size, 59 | self.vertices.element_count as u64 * size, 60 | ) 61 | } 62 | 63 | pub fn get_material_descriptor(&self, buffer: &Buffer) -> vk::DescriptorBufferInfo { 64 | let size = std::mem::size_of::() as u64; 65 | buffer.get_descriptor_info_offset(self.material_index.unwrap() as u64 * size, size) 66 | } 67 | 68 | pub fn get_vertices(&self) -> &BufferPart { 69 | &self.vertices 70 | } 71 | 72 | pub fn get_vertex_count(&self) -> u32 { 73 | self.vertices.element_count as u32 74 | } 75 | 76 | pub fn get_vertex_offset(&self) -> u32 { 77 | self.vertices.offset as u32 78 | } 79 | 80 | pub fn get_vertex_offset_size(&self) -> vk::DeviceSize { 81 | let size = std::mem::size_of::() as u64; 82 | self.vertices.offset as u64 * size 83 | } 84 | 85 | pub fn get_indices(&self) -> &Option { 86 | &self.indices 87 | } 88 | 89 | pub fn get_index_count(&self) -> u32 { 90 | self.indices.unwrap().element_count as u32 91 | } 92 | 93 | pub fn get_index_offset_size(&self) -> vk::DeviceSize { 94 | let size = std::mem::size_of::() as u64; 95 | self.indices.unwrap().offset as u64 * size 96 | } 97 | } 98 | 99 | pub struct Scene { 100 | pub meshes: Vec, 101 | pub materials: Vec, 102 | pub material_buffer: Buffer, 103 | pub camera: Option, 104 | } 105 | 106 | fn find_mesh(node: &gltf::Node, transforms: &mut Vec, mesh_index: usize) -> bool { 107 | transforms.push(glam::Mat4::from_cols_array_2d(&node.transform().matrix())); 108 | let found = match node.mesh() { 109 | Some(node_mesh) => node_mesh.index() == mesh_index, 110 | None => false, 111 | }; 112 | if found { 113 | return true; 114 | } 115 | for ref child in node.children() { 116 | if find_mesh(child, transforms, mesh_index) { 117 | return true; 118 | } 119 | } 120 | transforms.pop(); 121 | return false; 122 | } 123 | 124 | fn calc_mesh_global_transform(gltf: &gltf::Document, mesh_index: usize) -> glam::Mat4 { 125 | let mut global_transform = glam::Mat4::IDENTITY; 126 | let mut transforms = Vec::::new(); 127 | for node in gltf.nodes() { 128 | if find_mesh(&node, &mut transforms, mesh_index) { 129 | transforms.iter().for_each(|t| { 130 | global_transform = global_transform * *t; 131 | }); 132 | break; 133 | } 134 | } 135 | global_transform 136 | } 137 | 138 | pub fn load_scene(context: Arc, filepath: &PathBuf) -> Scene { 139 | let mut meshes = Vec::::new(); 140 | let (gltf, buffers, _) = gltf::import(filepath).unwrap(); 141 | 142 | //println!("{:#?}", gltf); 143 | 144 | let mut materials = Vec::::new(); 145 | for mat in gltf.materials() { 146 | materials.push(MaterialInfo { 147 | base_color: glam::Vec4::from_slice( 148 | &mat.pbr_metallic_roughness().base_color_factor(), 149 | ), 150 | //double_sided: mat.double_sided(), 151 | metallic_factor: mat.pbr_metallic_roughness().metallic_factor(), 152 | roughness_factor: mat.pbr_metallic_roughness().roughness_factor(), 153 | emissive_factor: glam::Vec3::from_slice(&mat.emissive_factor()), 154 | ..Default::default() 155 | }); 156 | } 157 | let material_buffer = Buffer::from_data( 158 | context.clone(), 159 | BufferInfo::default().usage_storage().gpu_only(), 160 | &materials, 161 | ); 162 | 163 | for mesh in gltf.meshes() { 164 | let mut mesh_indices = Vec::::new(); 165 | let mut mesh_vertices = Vec::::new(); 166 | let mut primitive_sections = Vec::::new(); 167 | 168 | // println!("Mesh #{}", mesh.index()); 169 | 170 | for (primitive_index, primitive) in mesh.primitives().enumerate() { 171 | // println!("- Primitive #{}", primitive.index()); 172 | 173 | let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); 174 | let offset = mesh_vertices.len(); 175 | 176 | if let Some(_) = primitive.get(&Semantic::Positions) { 177 | let positions = read_positions(&reader); 178 | let normals = read_normals(&reader); 179 | let tex_coords_0 = read_tex_coords(&reader, 0); 180 | let colors = read_colors(&reader); 181 | 182 | positions.iter().enumerate().for_each(|(index, position)| { 183 | let pos = *position; 184 | let norm = *normals.get(index).unwrap_or(&[0.0, 1.0, 0.0]); 185 | let uv = *tex_coords_0.get(index).unwrap_or(&[0.0, 0.0]); 186 | let col = *colors.get(index).unwrap_or(&[1.0, 1.0, 1.0, 1.0]); 187 | mesh_vertices.push(ModelVertex { 188 | pos: glam::vec4(pos[0], pos[1], pos[2], 1.0), 189 | normal: glam::vec4(norm[0], norm[1], norm[2], 1.0), 190 | color: glam::vec4(col[0], col[1], col[2], col[3]), 191 | uv: glam::vec4(uv[0], uv[1], 0.0, 0.0), 192 | }); 193 | }); 194 | }; 195 | 196 | primitive_sections.push(PrimitiveSection { 197 | index: primitive_index, 198 | vertices: BufferPart { 199 | offset, 200 | element_count: mesh_vertices.len() - offset, 201 | }, 202 | indices: None, 203 | material_index: primitive.material().index(), 204 | }); 205 | // println!(" Vertices {:?}", (offset, mesh_vertices.len() - offset)); 206 | 207 | if let Some(iter) = reader.read_indices() { 208 | let offset = mesh_indices.len(); 209 | mesh_indices.extend(iter.into_u32()); 210 | primitive_sections.last_mut().unwrap().indices = Some(BufferPart { 211 | offset, 212 | element_count: mesh_indices.len() - offset, 213 | }); 214 | // println!(" Indices {:?}", (offset, mesh_indices.len() - offset)); 215 | } 216 | } 217 | 218 | let mut index_buffer = None; 219 | let mut index_storage = None; 220 | 221 | if !mesh_indices.is_empty() { 222 | index_buffer = Some(Buffer::from_data( 223 | context.clone(), 224 | BufferInfo::default().usage_index().gpu_only(), 225 | &mesh_indices, 226 | )); 227 | 228 | let storage_indices: Vec = mesh_indices.iter().map(|i| *i as u64).collect(); 229 | index_storage = Some(Buffer::from_data( 230 | context.clone(), 231 | BufferInfo::default().usage_storage().gpu_only(), 232 | &storage_indices, 233 | )); 234 | } 235 | let vertex_buffer = Buffer::from_data( 236 | context.clone(), 237 | BufferInfo::default() 238 | .usage_vertex() 239 | .usage_storage() 240 | .gpu_only(), 241 | &mesh_vertices, 242 | ); 243 | 244 | let global_transform = calc_mesh_global_transform(&gltf, mesh.index()); 245 | 246 | let name = match mesh.name() { 247 | Some(name) => name.to_owned(), 248 | None => String::new(), 249 | }; 250 | meshes.push(Mesh { 251 | context: context.clone(), 252 | name, 253 | index_buffer, 254 | index_storage, 255 | vertex_buffer, 256 | transform: global_transform, 257 | primitive_sections, 258 | }); 259 | } 260 | 261 | let mut camera = None; 262 | for gltf_camera in gltf.cameras() { 263 | match gltf_camera.projection() { 264 | gltf::camera::Projection::Orthographic(_) => {} 265 | gltf::camera::Projection::Perspective(persp) => { 266 | for node in gltf.nodes() { 267 | let found = match node.camera() { 268 | Some(node_camera) => node_camera.index() == gltf_camera.index(), 269 | None => false, 270 | }; 271 | if found { 272 | let view_matrix = 273 | glam::Mat4::from_cols_array_2d(&node.transform().matrix()); 274 | camera = Some(Camera::from_view( 275 | view_matrix, 276 | persp.yfov(), 277 | persp.znear(), 278 | persp.zfar().unwrap_or(100.0), 279 | )); 280 | break; 281 | } 282 | } 283 | } 284 | } 285 | //Support for the first (default) camera only 286 | break; 287 | } 288 | 289 | Scene { 290 | meshes, 291 | materials, 292 | material_buffer, 293 | camera, 294 | } 295 | } 296 | 297 | fn read_indices<'a, 's, F>(reader: &Reader<'a, 's, F>) -> Option> 298 | where 299 | F: Clone + Fn(GltfBuffer<'a>) -> Option<&'s [u8]>, 300 | { 301 | reader 302 | .read_indices() 303 | .map(|indices| indices.into_u32().collect::>()) 304 | } 305 | 306 | fn read_positions<'a, 's, F>(reader: &Reader<'a, 's, F>) -> Vec<[f32; 3]> 307 | where 308 | F: Clone + Fn(GltfBuffer<'a>) -> Option<&'s [u8]>, 309 | { 310 | reader 311 | .read_positions() 312 | .expect("Position primitives should be present") 313 | .collect() 314 | } 315 | 316 | fn read_normals<'a, 's, F>(reader: &Reader<'a, 's, F>) -> Vec<[f32; 3]> 317 | where 318 | F: Clone + Fn(GltfBuffer<'a>) -> Option<&'s [u8]>, 319 | { 320 | reader 321 | .read_normals() 322 | .map_or(vec![], |normals| normals.collect()) 323 | } 324 | 325 | fn read_tex_coords<'a, 's, F>(reader: &Reader<'a, 's, F>, channel: u32) -> Vec<[f32; 2]> 326 | where 327 | F: Clone + Fn(GltfBuffer<'a>) -> Option<&'s [u8]>, 328 | { 329 | reader 330 | .read_tex_coords(channel) 331 | .map_or(vec![], |coords| coords.into_f32().collect()) 332 | } 333 | 334 | fn read_colors<'a, 's, F>(reader: &Reader<'a, 's, F>) -> Vec<[f32; 4]> 335 | where 336 | F: Clone + Fn(GltfBuffer<'a>) -> Option<&'s [u8]>, 337 | { 338 | reader 339 | .read_colors(0) 340 | .map_or(vec![], |colors| colors.into_rgba_f32().collect()) 341 | } 342 | -------------------------------------------------------------------------------- /src/swapchain.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Context, Image2d, RenderPass, RenderPassInfo, RendererSettings, Resource, SharedContext, 3 | TransientRenderPassInfo, Window, 4 | }; 5 | use ash::vk; 6 | use ash::{extensions::khr}; 7 | use std::sync::Arc; 8 | 9 | pub struct Swapchain { 10 | context: Arc, 11 | pub swapchain_loader: khr::Swapchain, 12 | swapchain: vk::SwapchainKHR, 13 | present_images: Vec, 14 | depth_stencil_images: Vec, 15 | resolve_images: Vec, 16 | sample_count: vk::SampleCountFlags, 17 | extent: vk::Extent2D, 18 | } 19 | 20 | impl Swapchain { 21 | pub fn new(context: Arc, window: &Window, settings: &RendererSettings) -> Self { 22 | unsafe { 23 | let mut sample_count = vk::SampleCountFlags::TYPE_1; 24 | if settings.samples == 2 { 25 | sample_count = vk::SampleCountFlags::TYPE_2; 26 | } else if settings.samples == 4 { 27 | sample_count = vk::SampleCountFlags::TYPE_4; 28 | } else if settings.samples == 8 { 29 | sample_count = vk::SampleCountFlags::TYPE_8; 30 | } else if settings.samples == 16 { 31 | sample_count = vk::SampleCountFlags::TYPE_16; 32 | } else if settings.samples == 32 { 33 | sample_count = vk::SampleCountFlags::TYPE_32; 34 | } else if settings.samples == 64 { 35 | sample_count = vk::SampleCountFlags::TYPE_64; 36 | } 37 | let pdevice = context.physical_device(); 38 | let surface_capabilities = window.get_surface_capabilities(pdevice); 39 | let mut desired_image_count = surface_capabilities.min_image_count + 1; 40 | if surface_capabilities.max_image_count > 0 41 | && desired_image_count > surface_capabilities.max_image_count 42 | { 43 | desired_image_count = surface_capabilities.max_image_count; 44 | } 45 | let extent = window.get_surface_extent(pdevice); 46 | let surface_format = window.get_surface_format(pdevice); 47 | let pre_transform = if surface_capabilities 48 | .supported_transforms 49 | .contains(vk::SurfaceTransformFlagsKHR::IDENTITY) 50 | { 51 | vk::SurfaceTransformFlagsKHR::IDENTITY 52 | } else { 53 | surface_capabilities.current_transform 54 | }; 55 | let image_format = surface_format.format; 56 | let present_mode = window.get_surface_present_mode(pdevice, settings.present_mode); 57 | let swapchain_loader = khr::Swapchain::new(context.instance(), context.device()); 58 | let swapchain_create_info = vk::SwapchainCreateInfoKHR::builder() 59 | .surface(window.surface()) 60 | .min_image_count(desired_image_count) 61 | .image_color_space(surface_format.color_space) 62 | .image_format(image_format) 63 | .image_extent(extent) 64 | .image_usage( 65 | vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_DST, 66 | ) 67 | .image_sharing_mode(vk::SharingMode::EXCLUSIVE) 68 | .pre_transform(pre_transform) 69 | .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) 70 | .present_mode(present_mode) 71 | .clipped(true) 72 | .image_array_layers(1); 73 | let swapchain = swapchain_loader 74 | .create_swapchain(&swapchain_create_info, None) 75 | .unwrap(); 76 | 77 | let swapchain_images = swapchain_loader.get_swapchain_images(swapchain).unwrap(); 78 | let present_images: Vec = swapchain_images 79 | .iter() 80 | .map(|image| Image2d::from_swapchain(context.clone(), *image, extent, image_format)) 81 | .collect(); 82 | 83 | let mut depth_stencil_images = Vec::::new(); 84 | if settings.depth { 85 | for _ in 0..present_images.len() { 86 | let depth_image_create_info = vk::ImageCreateInfo::builder() 87 | .image_type(vk::ImageType::TYPE_2D) 88 | .format(vk::Format::D16_UNORM) 89 | .extent(window.get_extent_3d()) 90 | .mip_levels(1) 91 | .array_layers(1) 92 | .samples(sample_count) 93 | .tiling(vk::ImageTiling::OPTIMAL) 94 | .usage(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT) 95 | .sharing_mode(vk::SharingMode::EXCLUSIVE); 96 | depth_stencil_images.push(Image2d::new( 97 | context.clone(), 98 | &depth_image_create_info, 99 | vk::ImageAspectFlags::DEPTH, 100 | 1, 101 | "SwapchainDepthStencil" 102 | )); 103 | } 104 | } 105 | 106 | let mut resolve_images = Vec::::new(); 107 | if settings.samples > 1 { 108 | for _ in 0..present_images.len() { 109 | let image_create_info = vk::ImageCreateInfo::builder() 110 | .image_type(vk::ImageType::TYPE_2D) 111 | .format(image_format) 112 | .extent(window.get_extent_3d()) 113 | .mip_levels(1) 114 | .array_layers(1) 115 | .samples(sample_count) 116 | .tiling(vk::ImageTiling::OPTIMAL) 117 | .usage( 118 | vk::ImageUsageFlags::TRANSIENT_ATTACHMENT 119 | | vk::ImageUsageFlags::COLOR_ATTACHMENT, 120 | ) 121 | .sharing_mode(vk::SharingMode::EXCLUSIVE); 122 | resolve_images.push(Image2d::new( 123 | context.clone(), 124 | &image_create_info, 125 | vk::ImageAspectFlags::COLOR, 126 | 1, 127 | "SwapchainResolve" 128 | )); 129 | } 130 | } 131 | 132 | Swapchain { 133 | context, 134 | swapchain_loader, 135 | swapchain, 136 | present_images, 137 | depth_stencil_images, 138 | resolve_images, 139 | sample_count, 140 | extent, 141 | } 142 | } 143 | } 144 | 145 | pub fn get_image_count(&self) -> usize { 146 | self.present_images.len() 147 | } 148 | 149 | pub fn get_present_image(&mut self, index: usize) -> &mut Image2d { 150 | &mut self.present_images[index] 151 | } 152 | 153 | pub fn get_sample_count(&self) -> vk::SampleCountFlags { 154 | self.sample_count 155 | } 156 | 157 | pub fn create_compatible_render_pass(&self) -> RenderPass { 158 | let color_images = vec![&self.present_images[0]]; 159 | let mut resolve_images = Vec::<&Image2d>::new(); 160 | match self.resolve_images.iter().nth(0) { 161 | Some(image) => resolve_images.push(image), 162 | None => {} 163 | }; 164 | let depth_stencil_image = match self.depth_stencil_images.iter().nth(0) { 165 | Some(image) => Some(image), 166 | None => None, 167 | }; 168 | RenderPass::new( 169 | self.context.clone(), 170 | RenderPassInfo { 171 | color_images, 172 | depth_stencil_image, 173 | resolve_images, 174 | present: true, 175 | samples: self.sample_count, 176 | final_layout: vk::ImageLayout::PRESENT_SRC_KHR, 177 | }, 178 | ) 179 | } 180 | 181 | pub fn get_transient_render_pass_info(&self) -> TransientRenderPassInfo { 182 | let mut resolve_formats = Vec::::new(); 183 | match self.resolve_images.iter().nth(0) { 184 | Some(image) => resolve_formats.push(image.get_format()), 185 | None => {} 186 | }; 187 | let depth_stencil_format = match self.depth_stencil_images.iter().nth(0) { 188 | Some(image) => Some(image.get_format()), 189 | None => None, 190 | }; 191 | TransientRenderPassInfo { 192 | color_formats: vec![self.present_images[0].get_format()], 193 | depth_stencil_format, 194 | resolve_formats, 195 | samples: self.sample_count, 196 | } 197 | } 198 | 199 | pub fn create_framebuffers( 200 | &self, 201 | renderpass: &RenderPass, 202 | window: &Window, 203 | ) -> Vec { 204 | let mut framebuffers = Vec::::new(); 205 | for i in 0..self.get_image_count() { 206 | let mut attachments = Vec::::new(); 207 | if self.resolve_images.is_empty() { 208 | attachments.push(self.present_images[i].get_image_view()); 209 | if !self.depth_stencil_images.is_empty() { 210 | attachments.push(self.depth_stencil_images[i].get_image_view()); 211 | } 212 | } else { 213 | attachments.push(self.resolve_images[i].get_image_view()); 214 | if !self.depth_stencil_images.is_empty() { 215 | attachments.push(self.depth_stencil_images[i].get_image_view()); 216 | } 217 | attachments.push(self.present_images[i].get_image_view()); 218 | } 219 | let frame_buffer_create_info = vk::FramebufferCreateInfo::builder() 220 | .render_pass(renderpass.handle()) 221 | .attachments(&attachments) 222 | .width(window.get_extent().width) 223 | .height(window.get_extent().height) 224 | .layers(1); 225 | unsafe { 226 | framebuffers.push( 227 | self.context 228 | .device() 229 | .create_framebuffer(&frame_buffer_create_info, None) 230 | .unwrap(), 231 | ); 232 | } 233 | } 234 | framebuffers 235 | } 236 | 237 | pub fn transition_depth_images(&mut self, context: &Arc) { 238 | let cmd = context.begin_single_time_cmd(); 239 | self.depth_stencil_images 240 | .iter_mut() 241 | .for_each(|depth_stencil_image| { 242 | depth_stencil_image.transition_image_layout( 243 | cmd, 244 | vk::ImageLayout::UNDEFINED, 245 | vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 246 | ); 247 | }); 248 | context.end_single_time_cmd(cmd); 249 | } 250 | 251 | pub fn get_extent(&self) -> vk::Extent2D { 252 | self.extent 253 | } 254 | } 255 | 256 | impl Resource for Swapchain { 257 | fn handle(&self) -> vk::SwapchainKHR { 258 | self.swapchain 259 | } 260 | } 261 | 262 | impl Drop for Swapchain { 263 | fn drop(&mut self) { 264 | unsafe { 265 | // Since images are created by the swapchain, this automatically destroys them as well. 266 | self.swapchain_loader.destroy_swapchain(self.swapchain, None); 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use crate::{offset_of, Vertex}; 2 | use std::path::PathBuf; 3 | use ash::vk; 4 | use glam::{vec2, vec4}; 5 | 6 | #[derive(Clone, Debug, Copy, Default)] 7 | pub struct BasicVertex { 8 | pub pos: glam::Vec4, 9 | pub color: glam::Vec4, 10 | pub uv: glam::Vec2, 11 | } 12 | 13 | pub fn find_asset(filename: &str) -> Option { 14 | let mut file_path = std::env::current_exe().unwrap(); 15 | for _ in 0..5 { 16 | match file_path.parent() { 17 | None => return None, 18 | Some(parent) => { 19 | let assets_folder = parent.join("assets"); 20 | if assets_folder.exists() { 21 | let asset_path = assets_folder.join(filename); 22 | if asset_path.exists() { 23 | return Some(asset_path); 24 | } 25 | } 26 | file_path = parent.to_path_buf() 27 | } 28 | } 29 | } 30 | None 31 | } 32 | 33 | impl Vertex for BasicVertex { 34 | fn stride() -> u32 { 35 | std::mem::size_of::() as u32 36 | } 37 | 38 | fn format_offset() -> Vec<(vk::Format, u32)> { 39 | vec![ 40 | ( 41 | vk::Format::R32G32B32A32_SFLOAT, 42 | offset_of!(BasicVertex, pos) as u32, 43 | ), 44 | ( 45 | vk::Format::R32G32B32A32_SFLOAT, 46 | offset_of!(BasicVertex, color) as u32, 47 | ), 48 | ( 49 | vk::Format::R32G32_SFLOAT, 50 | offset_of!(BasicVertex, uv) as u32, 51 | ), 52 | ] 53 | } 54 | } 55 | 56 | fn get_cube_face_colors() -> [glam::Vec4;6] { 57 | [ 58 | glam::vec4(0.549, 0.702, 0.412, 1.0), 59 | glam::vec4(0.957, 0.886, 0.522, 1.0), 60 | glam::vec4(0.957, 0.635, 0.349, 1.0), 61 | glam::vec4(0.180, 0.251, 0.341, 1.0), 62 | glam::vec4(0.737, 0.294, 0.318, 1.0), 63 | glam::vec4(0.357, 0.557, 0.490, 1.0), 64 | ] 65 | } 66 | 67 | 68 | pub fn colored_cube_vertices() -> [BasicVertex; 36] { 69 | let colors = get_cube_face_colors(); 70 | // green face 71 | [ 72 | BasicVertex { 73 | pos: vec4(-1.0, -1.0, 1.0, 1.0), 74 | color: colors[0], 75 | uv: vec2(1.0, 1.0), 76 | }, 77 | BasicVertex { 78 | pos: vec4(-1.0, 1.0, 1.0, 1.0), 79 | color: colors[0], 80 | uv: vec2(0.0, 1.0), 81 | }, 82 | BasicVertex { 83 | pos: vec4(1.0, -1.0, 1.0, 1.0), 84 | color: colors[0], 85 | uv: vec2(1.0, 0.0), 86 | }, 87 | BasicVertex { 88 | pos: vec4(1.0, -1.0, 1.0, 1.0), 89 | color: colors[0], 90 | uv: vec2(1.0, 0.0), 91 | }, 92 | BasicVertex { 93 | pos: vec4(-1.0, 1.0, 1.0, 1.0), 94 | color: colors[0], 95 | uv: vec2(0.0, 1.0), 96 | }, 97 | BasicVertex { 98 | pos: vec4(1.0, 1.0, 1.0, 1.0), 99 | color: colors[0], 100 | uv: vec2(0.0, 0.0), 101 | }, 102 | // yellow face 103 | BasicVertex { 104 | pos: vec4(-1.0, -1.0, -1.0, 1.0), 105 | color: colors[1], 106 | uv: vec2(1.0, 1.0), 107 | }, 108 | BasicVertex { 109 | pos: vec4(1.0, -1.0, -1.0, 1.0), 110 | color: colors[1], 111 | uv: vec2(0.0, 1.0), 112 | }, 113 | BasicVertex { 114 | pos: vec4(-1.0, 1.0, -1.0, 1.0), 115 | color: colors[1], 116 | uv: vec2(1.0, 0.0), 117 | }, 118 | BasicVertex { 119 | pos: vec4(-1.0, 1.0, -1.0, 1.0), 120 | color: colors[1], 121 | uv: vec2(1.0, 0.0), 122 | }, 123 | BasicVertex { 124 | pos: vec4(1.0, -1.0, -1.0, 1.0), 125 | color: colors[1], 126 | uv: vec2(0.0, 1.0), 127 | }, 128 | BasicVertex { 129 | pos: vec4(1.0, 1.0, -1.0, 1.0), 130 | color: colors[1], 131 | uv: vec2(0.0, 0.0), 132 | }, 133 | // orange face 134 | BasicVertex { 135 | pos: vec4(-1.0, 1.0, 1.0, 1.0), 136 | color: colors[2], 137 | uv: vec2(1.0, 1.0), 138 | }, 139 | BasicVertex { 140 | pos: vec4(-1.0, -1.0, 1.0, 1.0), 141 | color: colors[2], 142 | uv: vec2(0.0, 1.0), 143 | }, 144 | BasicVertex { 145 | pos: vec4(-1.0, 1.0, -1.0, 1.0), 146 | color: colors[2], 147 | uv: vec2(1.0, 0.0), 148 | }, 149 | BasicVertex { 150 | pos: vec4(-1.0, 1.0, -1.0, 1.0), 151 | color: colors[2], 152 | uv: vec2(1.0, 0.0), 153 | }, 154 | BasicVertex { 155 | pos: vec4(-1.0, -1.0, 1.0, 1.0), 156 | color: colors[2], 157 | uv: vec2(0.0, 1.0), 158 | }, 159 | BasicVertex { 160 | pos: vec4(-1.0, -1.0, -1.0, 1.0), 161 | color: colors[2], 162 | uv: vec2(0.0, 0.0), 163 | }, 164 | // dark blue face 165 | BasicVertex { 166 | pos: vec4(1.0, 1.0, 1.0, 1.0), 167 | color: colors[3], 168 | uv: vec2(1.0, 1.0), 169 | }, 170 | BasicVertex { 171 | pos: vec4(1.0, 1.0, -1.0, 1.0), 172 | color: colors[3], 173 | uv: vec2(0.0, 1.0), 174 | }, 175 | BasicVertex { 176 | pos: vec4(1.0, -1.0, 1.0, 1.0), 177 | color: colors[3], 178 | uv: vec2(1.0, 0.0), 179 | }, 180 | BasicVertex { 181 | pos: vec4(1.0, -1.0, 1.0, 1.0), 182 | color: colors[3], 183 | uv: vec2(1.0, 0.0), 184 | }, 185 | BasicVertex { 186 | pos: vec4(1.0, 1.0, -1.0, 1.0), 187 | color: colors[3], 188 | uv: vec2(0.0, 1.0), 189 | }, 190 | BasicVertex { 191 | pos: vec4(1.0, -1.0, -1.0, 1.0), 192 | color: colors[3], 193 | uv: vec2(0.0, 0.0), 194 | }, 195 | // red face 196 | BasicVertex { 197 | pos: vec4(1.0, 1.0, 1.0, 1.0), 198 | color: colors[4], 199 | uv: vec2(1.0, 1.0), 200 | }, 201 | BasicVertex { 202 | pos: vec4(-1.0, 1.0, 1.0, 1.0), 203 | color: colors[4], 204 | uv: vec2(0.0, 1.0), 205 | }, 206 | BasicVertex { 207 | pos: vec4(1.0, 1.0, -1.0, 1.0), 208 | color: colors[4], 209 | uv: vec2(1.0, 0.0), 210 | }, 211 | BasicVertex { 212 | pos: vec4(1.0, 1.0, -1.0, 1.0), 213 | color: colors[4], 214 | uv: vec2(1.0, 0.0), 215 | }, 216 | BasicVertex { 217 | pos: vec4(-1.0, 1.0, 1.0, 1.0), 218 | color: colors[4], 219 | uv: vec2(0.0, 1.0), 220 | }, 221 | BasicVertex { 222 | pos: vec4(-1.0, 1.0, -1.0, 1.0), 223 | color: colors[4], 224 | uv: vec2(0.0, 0.0), 225 | }, 226 | // wintergreen face 227 | BasicVertex { 228 | pos: vec4(1.0, -1.0, 1.0, 1.0), 229 | color: colors[5], 230 | uv: vec2(1.0, 1.0), 231 | }, 232 | BasicVertex { 233 | pos: vec4(1.0, -1.0, -1.0, 1.0), 234 | color: colors[5], 235 | uv: vec2(0.0, 1.0), 236 | }, 237 | BasicVertex { 238 | pos: vec4(-1.0, -1.0, 1.0, 1.0), 239 | color: colors[5], 240 | uv: vec2(1.0, 0.0), 241 | }, 242 | BasicVertex { 243 | pos: vec4(-1.0, -1.0, 1.0, 1.0), 244 | color: colors[5], 245 | uv: vec2(1.0, 0.0), 246 | }, 247 | BasicVertex { 248 | pos: vec4(1.0, -1.0, -1.0, 1.0), 249 | color: colors[5], 250 | uv: vec2(0.0, 1.0), 251 | }, 252 | BasicVertex { 253 | pos: vec4(-1.0, -1.0, -1.0, 1.0), 254 | color: colors[5], 255 | uv: vec2(0.0, 0.0), 256 | }, 257 | ] 258 | } 259 | -------------------------------------------------------------------------------- /src/window.rs: -------------------------------------------------------------------------------- 1 | use ash::{extensions::khr::Surface, vk}; 2 | use glam::Vec2; 3 | use winit::{event_loop::EventLoop, window::WindowBuilder}; 4 | pub struct Window { 5 | handle: winit::window::Window, 6 | surface_loader: Option, 7 | surface: Option, 8 | } 9 | 10 | impl Window { 11 | pub fn new>( 12 | width: u32, 13 | height: u32, 14 | title: S, 15 | event_loop: &EventLoop<()>, 16 | ) -> Self { 17 | let window = WindowBuilder::new() 18 | .with_title(title) 19 | .with_inner_size(winit::dpi::LogicalSize::new(width as f64, height as f64)) 20 | //.with_decorations(false) 21 | .build(event_loop) 22 | .unwrap(); 23 | Window { 24 | handle: window, 25 | surface_loader: None, 26 | surface: None, 27 | } 28 | } 29 | 30 | pub fn create_surface(&mut self, entry: &ash::Entry, instance: &ash::Instance) { 31 | self.surface_loader = Some(Surface::new(entry, instance)); 32 | unsafe { 33 | self.surface = 34 | Some(ash_window::create_surface(entry, instance, &self.handle, None).unwrap()); 35 | } 36 | } 37 | 38 | pub fn handle(&self) -> &winit::window::Window { 39 | &self.handle 40 | } 41 | 42 | pub fn surface(&self) -> vk::SurfaceKHR { 43 | self.surface.unwrap() 44 | } 45 | 46 | pub fn surface_loader(&self) -> &Surface { 47 | self.surface_loader.as_ref().unwrap() 48 | } 49 | 50 | pub fn set_title(&mut self, title: &str) { 51 | self.handle.set_title(title); 52 | } 53 | 54 | pub unsafe fn get_surface_support( 55 | &self, 56 | pdevice: vk::PhysicalDevice, 57 | queue_index: u32, 58 | ) -> bool { 59 | self.surface_loader 60 | .as_ref() 61 | .unwrap() 62 | .get_physical_device_surface_support(pdevice, queue_index, self.surface.unwrap()) 63 | .unwrap() 64 | } 65 | 66 | pub unsafe fn get_surface_capabilities( 67 | &self, 68 | physical_device: vk::PhysicalDevice, 69 | ) -> vk::SurfaceCapabilitiesKHR { 70 | self.surface_loader 71 | .as_ref() 72 | .unwrap() 73 | .get_physical_device_surface_capabilities(physical_device, self.surface.unwrap()) 74 | .unwrap() 75 | } 76 | 77 | pub unsafe fn get_surface_format( 78 | &self, 79 | physical_device: vk::PhysicalDevice, 80 | ) -> vk::SurfaceFormatKHR { 81 | self.surface_loader 82 | .as_ref() 83 | .unwrap() 84 | .get_physical_device_surface_formats(physical_device, self.surface.unwrap()) 85 | .unwrap()[0] 86 | } 87 | 88 | pub unsafe fn get_surface_present_mode( 89 | &self, 90 | physical_device: vk::PhysicalDevice, 91 | desired: vk::PresentModeKHR, 92 | ) -> vk::PresentModeKHR { 93 | let present_modes = self 94 | .surface_loader 95 | .as_ref() 96 | .unwrap() 97 | .get_physical_device_surface_present_modes(physical_device, self.surface.unwrap()) 98 | .unwrap(); 99 | present_modes 100 | .iter() 101 | .cloned() 102 | .find(|&mode| mode == desired) 103 | .unwrap_or(vk::PresentModeKHR::FIFO) 104 | } 105 | 106 | pub unsafe fn get_surface_extent(&self, physical_device: vk::PhysicalDevice) -> vk::Extent2D { 107 | let capabilities = self.get_surface_capabilities(physical_device); 108 | let extent = self.get_extent(); 109 | match capabilities.current_extent.width { 110 | std::u32::MAX => extent, 111 | _ => capabilities.current_extent, 112 | } 113 | } 114 | 115 | pub fn get_size(&self) -> Vec2 { 116 | let sz = self.handle.inner_size(); 117 | Vec2::new(sz.width as f32, sz.height as f32) 118 | } 119 | 120 | pub fn get_width(&self) -> u32 { 121 | self.handle.inner_size().width 122 | } 123 | 124 | pub fn get_height(&self) -> u32 { 125 | self.handle.inner_size().height 126 | } 127 | 128 | pub fn get_extent(&self) -> vk::Extent2D { 129 | let sz = self.handle.inner_size(); 130 | vk::Extent2D { 131 | width: sz.width as u32, 132 | height: sz.height as u32, 133 | } 134 | } 135 | 136 | pub fn get_extent_3d(&self) -> vk::Extent3D { 137 | let extent = self.get_extent(); 138 | vk::Extent3D { 139 | width: extent.width, 140 | height: extent.height, 141 | depth: 1, 142 | } 143 | } 144 | 145 | pub fn get_viewport(&self) -> vk::Viewport { 146 | let sz = self.handle.inner_size(); 147 | vk::Viewport::builder() 148 | .width(sz.width as f32) 149 | .height(sz.height as f32) 150 | .min_depth(0.0) 151 | .max_depth(1.0) 152 | .build() 153 | } 154 | 155 | pub fn get_viewport_gl(&self) -> vk::Viewport { 156 | let sz = self.handle.inner_size(); 157 | vk::Viewport::builder() 158 | .x(0.0) 159 | .y(sz.height as f32) 160 | .width(sz.width as f32) 161 | .height(-(sz.height as f32)) 162 | .min_depth(0.0) 163 | .max_depth(1.0) 164 | .build() 165 | } 166 | 167 | pub fn get_rect(&self) -> vk::Rect2D { 168 | vk::Rect2D::builder().extent(self.get_extent()).build() 169 | } 170 | 171 | pub fn destroy_surface(&mut self) { 172 | unsafe { 173 | match self.surface_loader.as_mut() { 174 | Some(sl) => sl.destroy_surface(self.surface.unwrap(), None), 175 | None => {} 176 | } 177 | } 178 | self.surface_loader = None; 179 | self.surface = None; 180 | } 181 | 182 | pub fn is_minimized(&self) -> bool { 183 | let sz = self.handle.inner_size(); 184 | sz.width == 0 && sz.height == 0 185 | } 186 | } 187 | 188 | impl Drop for Window { 189 | fn drop(&mut self) { 190 | self.destroy_surface(); 191 | } 192 | } 193 | --------------------------------------------------------------------------------