diff --git a/bin/data/shaders/display.blit.frag.glsl b/bin/data/shaders/display.blit.frag.glsl new file mode 100644 index 00000000..64e61a6a --- /dev/null +++ b/bin/data/shaders/display.blit.frag.glsl @@ -0,0 +1,23 @@ +#version 450 +#extension GL_EXT_samplerless_texture_functions : require + +layout (binding = 1) uniform sampler samp; +layout (binding = 2) uniform texture2D albedoLeftTexture; +layout (binding = 3) uniform texture2D albedoRightTexture; + +layout (location = 0) in vec2 inUv; + +layout (location = 0) out vec4 outAlbedoSpecular; + +layout( push_constant ) uniform PushBlock { + uint pass; +} PushConstant; + +void main() { + if ( PushConstant.pass == 0 ) { + outAlbedoSpecular.rgb = texture(sampler2D(albedoLeftTexture, samp), inUv).rgb; + } else { + outAlbedoSpecular.rgb = texture(sampler2D(albedoRightTexture, samp), inUv).rgb; + } + outAlbedoSpecular.a = 1; +} \ No newline at end of file diff --git a/bin/data/shaders/display.blit.vert.glsl b/bin/data/shaders/display.blit.vert.glsl new file mode 100644 index 00000000..e9638741 --- /dev/null +++ b/bin/data/shaders/display.blit.vert.glsl @@ -0,0 +1,16 @@ +#version 450 + +layout (location = 0) in vec2 inPos; +layout (location = 1) in vec2 inUv; + +layout (location = 0) out vec2 outUv; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + outUv = inUv; + + gl_Position = vec4(inPos.xy, 0.0, 1.0); +} \ No newline at end of file diff --git a/bin/data/shaders/display.subpass.frag.glsl b/bin/data/shaders/display.subpass.frag.glsl index 782fe5d1..aa5af603 100644 --- a/bin/data/shaders/display.subpass.frag.glsl +++ b/bin/data/shaders/display.subpass.frag.glsl @@ -17,9 +17,9 @@ layout (location = 0) out vec4 outFragColor; struct Light { vec3 position; - float power; - vec3 color; float radius; + vec3 color; + float power; int type; int shadowed; mat4 view; diff --git a/bin/data/shaders/raytracing.comp.glsl b/bin/data/shaders/raytracing.comp.glsl index 44669233..72f02e20 100644 --- a/bin/data/shaders/raytracing.comp.glsl +++ b/bin/data/shaders/raytracing.comp.glsl @@ -1,408 +1,280 @@ #version 450 layout (local_size_x = 32, local_size_y = 32) in; -layout (binding = 0, rgba8) uniform writeonly image2D resultImage; -#define EPSILON 0.00001 +layout (constant_id = 0) const uint LIGHTS = 16; +layout (constant_id = 1) const uint EYES = 2; +layout (binding = 0, rgba8) uniform writeonly image2D resultImage[EYES]; + +#define RAY_MARCH false +#define MARCH_STEPS 32 +#define EPSILON 0.0001 #define MAXLEN 1000.0 #define SHADOW 0.5 -#define RAYBOUNCES 4 -#define REFLECTIONS true -#define REFLECTIONSTRENGTH 0.4 -#define REFLECTIONFALLOFF 0.5 +#define SHADOWS true +#define RAYBOUNCES 0 +#define REFLECTION_STRENGTH 0.4 +#define REFLECTION_FALLOFF 0.5 -#define UINT32_MAX 0xFFFFFFFF +layout( push_constant ) uniform PushBlock { + uint pass; +} PushConstant; -#define TREE_SIZE 8 -#define TREE_STACK 32 -#define PRIMITIVE_TYPE_EMPTY UINT32_MAX -#define PRIMITIVE_TYPE_CUBE 1 -#define PRIMITIVE_TYPE_LEAF 2 -#define PRIMITIVE_TYPE_TREE 3 -#define PRIMITIVE_TYPE_ROOT 4 - -struct Camera { - vec3 position; - mat4 view; - float aspectRatio; +struct Ray { + vec3 origin; + vec3 direction; }; - -struct StartEnd { - uint start; - uint end; +struct Result { + vec3 color; + float t; + int id; }; - -struct Cube { - vec4 position; - uint type; -// uint _padding[3]; -}; - -struct Tree { - vec4 position; - uint type; - uint children[TREE_SIZE]; -// uint _padding[3]; -}; - struct Light { vec3 position; + float radius; vec3 color; + float power; + vec2 type; + mat4 view; + mat4 projection; }; -layout (binding = 1) uniform UBO { - Camera camera; - StartEnd cubes; - StartEnd lights; - uint root; +struct State { + vec3 viewPosition; + Ray ray; + Result result; +} state; + +layout (binding = 2) uniform UBO { + mat4 matrices[2]; + vec4 ambient; + Light lights[LIGHTS]; } ubo; -layout (std140, binding = 2) buffer Cubes { - Cube cubes[ ]; -}; -layout (std430, binding = 3) buffer Trees { - Tree trees[ ]; -}; -layout (std140, binding = 4) buffer Lights { - Light lights[ ]; -}; -layout (binding = 5) uniform sampler2D samplerTexture; -// Lighting ========================================================= -void reflectRay(inout vec3 rayD, in vec3 mormal) { - rayD = rayD + 2.0 * -dot(mormal, rayD) * mormal; +struct Shape { + vec4 values; + vec4 albedoSpecular; + int type; +}; +layout (std140, binding = 3) buffer Shapes { + Shape shapes[]; +}; + +void reflectRay(inout vec3 rayD, in vec3 normal) { + rayD = rayD + 2.0 * -dot(normal, rayD) * normal; } +// Lighting ========================================================= float lightDiffuse(vec3 normal, vec3 lightDir) { return clamp(dot(normal, lightDir), 0.1, 1.0); } float lightSpecular(vec3 normal, vec3 lightDir, float specularFactor) { - vec3 viewVec = normalize(ubo.camera.position); + vec3 viewVec = normalize(state.viewPosition); vec3 halfVec = normalize(lightDir + viewVec); return pow(clamp(dot(normal, halfVec), 0.0, 1.0), specularFactor); } -// Cube =========================================================== -float cubeIntersect(vec3 rayO, vec3 rayD, vec3 rayDRecip, Cube cube) { - float t[10]; - t[1] = ( cube.position.x - cube.position.w - rayO.x) * rayDRecip.x; - t[2] = ( cube.position.x + cube.position.w - rayO.x) * rayDRecip.x; - t[3] = ( cube.position.y - cube.position.w - rayO.y) * rayDRecip.y; - t[4] = ( cube.position.y + cube.position.w - rayO.y) * rayDRecip.y; - t[5] = ( cube.position.z - cube.position.w - rayO.z) * rayDRecip.z; - t[6] = ( cube.position.z + cube.position.w - rayO.z) * rayDRecip.z; - t[7] = max(max(min(t[1], t[2]), min(t[3], t[4])), min(t[5], t[6])); - t[8] = min(min(max(t[1], t[2]), max(t[3], t[4])), max(t[5], t[6])); - t[9] = (t[8] < 0 || t[7] > t[8]) ? 0.0 : t[7]; - return t[9]; -} -uint intersect(in vec3 rayO, in vec3 rayD, inout float resT, uint start, uint end) { - uint id = UINT32_MAX; - vec3 rayDRecip = 1.0f / rayD; - for (uint i = start; i < end && i < ubo.cubes.end && i < cubes.length(); i++) { - Cube cube = cubes[i]; - if ( cube.type == PRIMITIVE_TYPE_EMPTY ) continue; - float tcube = cubeIntersect(rayO, rayD, rayDRecip, cube); - if ((tcube > EPSILON) && (tcube < resT)) { - id = i; - resT = tcube; - } - } - return id; -} -// Tree =========================================================== -float treeIntersect( in vec3 rayO, in vec3 rayD, in vec3 rayDRecip, Tree tree ) { - Cube treecube; - treecube.type = tree.type; - treecube.position = tree.position; - float t = cubeIntersect( rayO, rayD, rayDRecip, treecube ); +// Sphere =========================================================== +float sphereIntersect(in vec3 rayO, in vec3 rayD, in Shape shape ) { + vec3 oc = rayO - shape.values.xyz; + float b = 2.0 * dot(oc, rayD); + float c = dot(oc, oc) - shape.values.w * shape.values.w; + float h = b*b - 4.0*c; + if (h < 0.0) return -1.0; + float t = (-b - sqrt(h)) / 2.0; return t; } -struct StackIterator { - uint tree; - uint child; -}; -struct Stack { - int pointer; - StackIterator container[TREE_STACK]; -}; -Stack stack; - -StackIterator popStack( ) { - StackIterator top = stack.container[stack.pointer]; - stack.container[stack.pointer--] = StackIterator( UINT32_MAX, UINT32_MAX ); - return top; +vec3 sphereNormal(in vec3 position, in Shape shape) { + return (position - shape.values.xyz) / shape.values.w; } -void pushStack( StackIterator item ) { - stack.container[++stack.pointer] = item; +float sphereSDF( vec3 position, in Shape shape ) { + return length(position - shape.values.xyz) - shape.values.w; } -uint intersectTreecursive( in vec3 rayO, in vec3 rayD, in vec3 rayDRecip, inout float resT, uint root ) { - uint id = UINT32_MAX; - if ( root == UINT32_MAX ) return id; - // set up stack - stack.pointer = -1; - for ( uint i = 0; i < TREE_STACK; ++i ) stack.container[i] = StackIterator( UINT32_MAX, UINT32_MAX ); - pushStack(StackIterator( root, UINT32_MAX )); - - while ( true ) { - StackIterator it = popStack(); - // end of stack - if ( it.tree == UINT32_MAX ) break; - Tree tree = trees[it.tree]; - // invalid tree - if ( tree.type == PRIMITIVE_TYPE_EMPTY ) break; - // new tree, parse collision - if ( it.child == UINT32_MAX ) { - float t = treeIntersect( rayO, rayD, rayDRecip, tree ); - // bad intersection with this tree, continue with next iteration - if ( t <= EPSILON || t >= resT ) continue; - // push back with new stack - it.child = 0; - pushStack( it ); - // continue with next iteration - continue; - } else if ( it.child >= TREE_SIZE ) { - // no new children, continue with next iteration - continue; - } else { - // is leaf - if ( tree.type == PRIMITIVE_TYPE_LEAF ) { - // check children for a match - for ( uint i = 0; i < TREE_SIZE; ++i ) { - uint branchId = tree.children[i]; - // unallocated, skip - if ( branchId == UINT32_MAX ) continue; - Cube primitive = cubes[branchId]; - if ( primitive.type == PRIMITIVE_TYPE_EMPTY ) continue; - float t = cubeIntersect( rayO, rayD, rayDRecip, primitive ); - // branch intersects with ray, set as new parent - if ( (t <= EPSILON) || (t >= resT) ) continue; - id = branchId; - resT = t; - } - // continue with next iteration - continue; - } - // parse children - uint branchId = tree.children[it.child++]; - // add new iterator to the stack - pushStack( it ); - // unused child, continue with next iteration - if ( branchId == UINT32_MAX ) continue; - // tree branch, push to stack - // the first if block will check its collision - it.tree = branchId; - it.child = UINT32_MAX; - pushStack( it ); - continue; - } +// Plane =========================================================== +float planeIntersect(in vec3 rayO, in vec3 rayD, in Shape shape) { + float d = dot(rayD, shape.values.xyz); + if (d == 0.0) return 0.0; + float t = -(shape.values.w + dot(rayO, shape.values.xyz)) / d; + return t < 0.0 ? 0.0 : t; +} +vec3 planeNormal(in vec3 position, in Shape shape) { + return shape.values.xyz; +} +float planeSDF( vec3 position, in Shape shape ) { + return dot( position, shape.values.xyz ) + shape.values.w; +} +// Generic ========================================================= +float shapeIntersect(in vec3 rayO, in vec3 rayD, in Shape shape) { + if ( shape.type == 1 ) return sphereIntersect( rayO, rayD, shape ); + if ( shape.type == 2 ) return planeIntersect( rayO, rayD, shape ); + return 0.0; +} +vec3 shapeNormal( vec3 position, in Shape shape ) { + if ( shape.type == 1 ) return sphereNormal(position, shape); + if ( shape.type == 2 ) return planeNormal(position, shape); + return vec3(0.0); +} +float shapeSDF( vec3 position, in Shape shape ) { + if ( shape.type == 1 ) return sphereSDF(position, shape); + if ( shape.type == 2 ) return planeSDF(position, shape); + return MAXLEN; +} +// Intersect ======================================================= +int intersect(in vec3 rayO, in vec3 rayD, inout float resT) { + int id = -1; + for (int i = 0; i < shapes.length(); i++) { + Shape shape = shapes[i]; + float tShape = shapeIntersect(rayO, rayD, shape); + if ((tShape > EPSILON) && (tShape < resT)) { + id = i; + resT = tShape; + } } return id; } - -uint intersectTree( in vec3 rayO, in vec3 rayD, in vec3 rayDRecip, inout float resT ) { - uint id = UINT32_MAX; - // traverse the tree branches to the leaf - uint index = 0; - while ( true ) { - Tree tree = trees[index++]; - if ( tree.type == PRIMITIVE_TYPE_EMPTY ) break; - if ( tree.type != PRIMITIVE_TYPE_LEAF ) continue; - float tcube = treeIntersect( rayO, rayD, rayDRecip, tree ); - // ray fails collision with parent tree - if ( (tcube <= EPSILON) || (tcube >= resT) ) continue; - // ray intersects with parent tree, check children branches - for ( uint i = 0; i < TREE_SIZE; ++i ) { - uint branchId = tree.children[i]; - // unallocated, skip - if ( branchId == UINT32_MAX ) continue; - Cube primitive = cubes[branchId]; - if ( primitive.type == PRIMITIVE_TYPE_EMPTY ) continue; - tcube = cubeIntersect( rayO, rayD, rayDRecip, primitive ); - // branch intersects with ray, set as new parent - if ( (tcube <= EPSILON) || (tcube >= resT) ) continue; - id = branchId; - resT = tcube; - } - } - return id; -} - -float calcShadow(in vec3 rayO, in vec3 rayD, in uint objectId, inout float t, uint start, uint end) { - vec3 rayDRecip = 1.0f / rayD; - for (uint i = ubo.cubes.start; i < ubo.cubes.end && i < cubes.length(); i++) { - if (i == objectId) continue; - float tCube = cubeIntersect(rayO, rayD, rayDRecip, cubes[i]); - if ((tCube > EPSILON) && (tCube < t)) { - t = tCube; +float calcShadow(in vec3 rayO, in vec3 rayD, in int objectID, inout float resT) { + for (int i = 0; i < shapes.length(); i++) { + if ( i == objectID ) continue; + Shape shape = shapes[i]; + float tShape = shapeIntersect(rayO, rayD, shape); + if ((tShape > EPSILON) && (tShape < resT)) { + resT = tShape; return SHADOW; } - } + } return 1.0; } -float calcShadowTree(in vec3 rayO, in vec3 rayD, in vec3 rayDRecip, in uint objectId, inout float t){ - // traverse the tree branches to the leaf - uint index = 0; - while ( true ) { - Tree tree = trees[index++]; - if ( tree.type == PRIMITIVE_TYPE_EMPTY ) break; - if ( tree.type != PRIMITIVE_TYPE_LEAF ) continue; - float tcube = treeIntersect( rayO, rayD, rayDRecip, tree ); - // ray fails collision with parent tree - if ( (tcube <= EPSILON) || (tcube >= t) ) continue; - // ray intersects with parent tree, check children branches - for ( uint i = 0; i < TREE_SIZE; ++i ) { - uint branchId = tree.children[i]; - // unallocated, skip - if ( branchId == UINT32_MAX ) continue; - Cube primitive = cubes[branchId]; - if ( primitive.type == PRIMITIVE_TYPE_EMPTY ) continue; - if ( branchId == objectId ) continue; - tcube = cubeIntersect( rayO, rayD, rayDRecip, primitive ); - // branch intersects with ray, set as new parent - if ( (tcube <= EPSILON) || (tcube >= t) ) continue; - t = tcube; +// Marching ======================================== +int intersectMarch( in vec3 rayO, in vec3 rayD, inout float resT ) { + resT = 0; + for (int i = 0; i < MARCH_STEPS; ++i) { + vec3 position = resT * rayD + rayO; + float tNearest = MAXLEN; + int objectID = -1; + for ( int j = 0; j < shapes.length(); ++j ) { + Shape shape = shapes[j]; + float tShape = shapeSDF(position, shape); + // if ((tShape > EPSILON) && (tShape < tNearest)) { + if ( tShape < tNearest ) { + objectID = j; + tNearest = tShape; + } + } + if (tNearest < EPSILON) { + return objectID; + } + if (resT > MAXLEN) break; + resT += tNearest; + } + resT = MAXLEN; + return -1; +} +float calcShadowMarch( in vec3 rayO, in vec3 rayD, in int objectID, inout float resT ) { + float distance = resT; + resT = 0; + for (int i = 0; i < MARCH_STEPS; ++i) { + vec3 position = resT * rayD + rayO; + float tNearest = distance; + int objectID = -1; + for ( int j = 0; j < shapes.length(); ++j ) { + if ( j == objectID ) continue; + Shape shape = shapes[j]; + float tShape = shapeSDF(position, shape); + // if ((tShape > EPSILON) && (tShape < tNearest)) { + if ( tShape < tNearest ) { + objectID = j; + tNearest = tShape; + } + } + if (tNearest < EPSILON) { return SHADOW; } + if (resT > distance) break; + resT += tNearest; } + resT = distance; return 1.0; } vec3 fog(in float t, in vec3 color) { - return mix(color, vec3(0, 0, 0), clamp(sqrt(t*t)/20.0, 0.0, 1.0)); + vec3 fogColor = vec3(0.1); + return mix(color, fogColor.rgb, clamp(sqrt(t*t)/20.0, 0.0, 1.0)); } -vec3 renderScene(inout vec3 rayO, inout vec3 rayD, inout uint id) { - float t = MAXLEN; - vec3 rayDRecip = 1.0f / rayD; - - // Get intersected object ID - uint objectID = intersect(rayO, rayD, t, ubo.cubes.start, ubo.cubes.end ); -// uint objectID = intersectTree( rayO, rayD, rayDRecip, t ); -// uint objectID = intersectTreecursive( rayO, rayD, rayDRecip, t, ubo.root ); - - vec3 color = vec3(0.0); - if ( objectID == UINT32_MAX ) return color; +Result renderScene(inout vec3 rayO, inout vec3 rayD ) { + Result result; + result.color = vec3(0.0); + result.t = MAXLEN; + result.id = RAY_MARCH ? intersectMarch(rayO, rayD, result.t) : intersect(rayO, rayD, result.t); + if (result.id == -1) return result; - vec3 pos = rayO + t * rayD; - vec3 normal; + Shape shape = shapes[result.id]; + vec3 position = rayO + result.t * rayD; + vec3 normal = shapeNormal( position, shape ); - // Cubes - if ( id == UINT32_MAX ) return color; - id = objectID; - // Hit Data - { - Cube cube = cubes[objectID]; - vec2 mappedUv = vec2(0, 0); { - float min_distance = MAXLEN; - vec3 point = cube.position.xyz - pos; - float distance = abs(cube.position.w - abs(point.x)); - if (distance < min_distance) { - min_distance = distance; - normal = vec3(-1, 0, 0); - if ( cube.position.w + point.x <= EPSILON ) normal *= -1; + for ( uint i = 0; i < LIGHTS; ++i ) { + Light light = ubo.lights[i]; + if ( light.radius <= EPSILON ) continue; + if ( light.power <= EPSILON ) continue; - mappedUv.x = point.y - cube.position.y; - mappedUv.y = point.z - cube.position.z; - } - distance = abs(cube.position.w - abs(point.y)); - if (distance < min_distance) { - min_distance = distance; - normal = vec3(0, -1, 0); - if ( cube.position.w + point.y <= EPSILON ) normal *= -1; + vec3 L = light.position - position; + float dist = length(L); + vec3 D = normalize(L); + float attenuation = light.radius / (pow(dist, 2.0) + 1.0);; + attenuation = 1; + // if ( dist > light.radius ) continue; + + vec4 albedoSpecular = shape.albedoSpecular; - mappedUv.x = point.x - cube.position.x; - mappedUv.y = point.z - cube.position.z; - } - distance = abs(cube.position.w - abs(point.z)); - if (distance < min_distance) { - min_distance = distance; - normal = vec3(0, 0, -1); - if ( cube.position.w + point.z <= EPSILON ) normal *= -1; + float d_dot = lightDiffuse(normal, D); + float s_factor = lightSpecular(normal, D, albedoSpecular.a); + vec3 color = (light.color * albedoSpecular.rgb) * d_dot + s_factor; - mappedUv.x = point.x - cube.position.x; - mappedUv.y = point.y - cube.position.y; - } - mappedUv -= 2.0f / 4.0f; - mappedUv *= 1.0f / 4.0f; - mappedUv.x = mod(mappedUv.x, 1.0f / 4.0f); - mappedUv.y = mod(mappedUv.y, 1.0f / 4.0f); - } - vec3 textureMapped = texture(samplerTexture, mappedUv).rgb; - color = textureMapped; - } - // Lighting - { - int di = 0; - vec4 diffuses[256]; - for ( int j = 0; j < lights.length(); ++j ) { - Light light = lights[j]; - if ( light.color.r <= EPSILON && light.color.g <= EPSILON && light.color.b <= EPSILON ) continue; - vec3 lightVec = normalize(light.position - pos); - float diffuse = lightDiffuse(normal, lightVec); - float specular = lightSpecular(normal, lightVec, 2000.0f); - diffuses[di].x = length(light.position - pos); - diffuses[di].gba = diffuse * light.color + specular; - ++di; - } - vec4 a = vec4(MAXLEN, vec3(1.0)); - vec4 b = vec4(MAXLEN, vec3(1.0)); - vec4 c = vec4(MAXLEN, vec3(1.0)); - for ( int j = 0; j < lights.length(); ++j ) { - vec4 current = diffuses[j]; - if ( current.y <= EPSILON ) continue; - // use slot a - if ( current.x < a.x ) { - c = b; - b = a; - a = current; - } else if ( current.x < b.x ) { - c = b; - b = current; - } else if ( current.x < c.x ) { - c = current; - } - } - vec3 avg = (a.gba * 0.5 + b.gba * 0.3 + c.gba * 0.2); - color *= avg; - } - // Shadows - { - vec3 lightVec = vec3(0, 4, 0) - pos; - t = length(lightVec); - color *= calcShadow(pos, lightVec, id, t, ubo.cubes.start, ubo.cubes.end); - // color *= calcShadowTree(pos, lightVec, rayDRecip, id, t); + // Shadows + float tShadow = dist; + float shadowed = 1; + if ( SHADOWS ) + shadowed = RAY_MARCH ? calcShadowMarch(position, D, result.id, tShadow) : calcShadow(position, D, result.id, tShadow); + result.color += color * light.power * attenuation * shadowed; } // Fog - // color = fog(t, color); - + // result.color = fog(t, result.color); + // Reflect ray for next render pass reflectRay(rayD, normal); - rayO = pos; + rayO = position; - return color; + return result; } void main() { - ivec2 dim = imageSize(resultImage); - vec2 uv = vec2(gl_GlobalInvocationID.xy) / dim; + for ( int pass = 0; pass < EYES; ++pass ) { + { + vec2 uv = vec2(gl_GlobalInvocationID.xy) / imageSize(resultImage[pass]); + vec4 near4 = ubo.matrices[pass] * (vec4(2.0 * uv - 1.0, -1.0, 1.0)); + vec4 far4 = ubo.matrices[pass] * (vec4(2.0 * uv - 1.0, 1.0, 1.0)); + vec3 near3 = near4.xyz / near4.w; + vec3 far3 = far4.xyz / far4.w; - vec3 rayO = ubo.camera.position; // * vec3(1,1,-1); - vec3 rayD = normalize(vec3((-1.0 + 2.0 * uv) * vec2(ubo.camera.aspectRatio, 1.0), -1.0)); - rayD = (ubo.camera.view * vec4( rayD, 0.0 )).xyz; - - // Basic color path - uint id = 0; - vec3 finalColor = renderScene(rayO, rayD, id); - - // Reflection - if ( REFLECTIONS ) { - float reflectionStrength = REFLECTIONSTRENGTH; - for (int i = 0; i < RAYBOUNCES; i++) { - vec3 reflectionColor = renderScene(rayO, rayD, id); - finalColor = (1.0 - reflectionStrength) * finalColor + reflectionStrength * mix(reflectionColor, finalColor, 1.0 - reflectionStrength); - reflectionStrength *= REFLECTIONFALLOFF; + state.viewPosition = near3; + state.ray.origin = near3; + state.ray.direction = normalize( far3 - near3 ); } + + // Basic color path + state.result = renderScene(state.ray.origin, state.ray.direction); + vec3 finalColor = state.result.color; + + // Reflection + float reflectionStrength = REFLECTION_STRENGTH; + for (int i = 0; i < RAYBOUNCES; i++) { + Result result = renderScene(state.ray.origin, state.ray.direction); + vec3 reflectionColor = result.color; + + finalColor = (1.0 - reflectionStrength) * finalColor + reflectionStrength * mix(reflectionColor, finalColor, 1.0 - reflectionStrength); + reflectionStrength *= REFLECTION_FALLOFF; + } + + imageStore(resultImage[pass], ivec2(gl_GlobalInvocationID.xy), vec4(finalColor, 1.0)); } - - imageStore(resultImage, ivec2(gl_GlobalInvocationID.xy), vec4(finalColor, 0.0)); } \ No newline at end of file diff --git a/bin/data/shaders/texture.frag.glsl b/bin/data/shaders/texture.frag.glsl index 94c293b3..6ebfac38 100644 --- a/bin/data/shaders/texture.frag.glsl +++ b/bin/data/shaders/texture.frag.glsl @@ -5,7 +5,7 @@ layout (location = 0) in vec2 inUV; layout (location = 0) out vec4 outFragColor; void main() { - vec4 sampledColor = texture(samplerColor, vec2(inUV.s, 1.0 - inUV.t)); + vec4 sampledColor = texture(samplerColor, inUV); if ( sampledColor.r <= 0.000001 && sampledColor.g <= 0.000001 && sampledColor.b <= 0.000001 ) discard; outFragColor = sampledColor; } \ No newline at end of file diff --git a/engine/inc/uf/ext/vulkan/device.h b/engine/inc/uf/ext/vulkan/device.h index 95032d00..6e79de89 100644 --- a/engine/inc/uf/ext/vulkan/device.h +++ b/engine/inc/uf/ext/vulkan/device.h @@ -12,21 +12,33 @@ namespace ext { VkSurfaceKHR surface; VkPhysicalDevice physicalDevice; VkDevice logicalDevice; - VkCommandPool commandPool = VK_NULL_HANDLE; + struct { + VkCommandPool graphics = VK_NULL_HANDLE; + VkCommandPool compute = VK_NULL_HANDLE; + VkCommandPool transfer = VK_NULL_HANDLE; + } commandPool; VkPhysicalDeviceProperties properties; VkPhysicalDeviceFeatures features; VkPhysicalDeviceFeatures enabledFeatures; VkPhysicalDeviceMemoryProperties memoryProperties; + VkPhysicalDeviceProperties2 properties2; + VkPhysicalDeviceFeatures2 features2; + VkPhysicalDeviceFeatures2 enabledFeatures2; + VkPhysicalDeviceMemoryProperties2 memoryProperties2; + VkPipelineCache pipelineCache; std::vector queueFamilyProperties; std::vector supportedExtensions; - VkQueue graphicsQueue; - VkQueue presentQueue; - VkQueue computeQueue; + struct { + VkQueue graphics; + VkQueue present; + VkQueue compute; + VkQueue transfer; + } queues; uf::Window* window; @@ -41,7 +53,6 @@ namespace ext { uint32_t present; uint32_t compute; uint32_t transfer; - } queueFamilyIndices; operator VkDevice() { return this->logicalDevice; }; @@ -51,7 +62,7 @@ namespace ext { int rate( VkPhysicalDevice device ); VkCommandBuffer createCommandBuffer( VkCommandBufferLevel level, bool begin = false ); - void flushCommandBuffer( VkCommandBuffer commandBuffer, VkQueue queue, bool free = true ); + void flushCommandBuffer( VkCommandBuffer commandBuffer, bool free = true ); VkResult createBuffer( VkBufferUsageFlags usageFlags, diff --git a/engine/inc/uf/ext/vulkan/rendermode.h b/engine/inc/uf/ext/vulkan/rendermode.h index cb0bb824..c757ff56 100644 --- a/engine/inc/uf/ext/vulkan/rendermode.h +++ b/engine/inc/uf/ext/vulkan/rendermode.h @@ -32,6 +32,7 @@ namespace ext { virtual void render(); virtual void destroy(); virtual void synchronize( uint64_t = UINT64_MAX ); + virtual void pipelineBarrier( VkCommandBuffer, uint8_t = -1 ); }; } } \ No newline at end of file diff --git a/engine/inc/uf/ext/vulkan/rendermodes/compute.h b/engine/inc/uf/ext/vulkan/rendermodes/compute.h new file mode 100644 index 00000000..b85dd51b --- /dev/null +++ b/engine/inc/uf/ext/vulkan/rendermodes/compute.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace ext { + namespace vulkan { + struct UF_API ComputeRenderMode : public ext::vulkan::RenderMode { + ext::vulkan::Graphic blitter, compute; + pod::Vector2ui dispatchSize = { 32, 32 }; + + // RAII + virtual std::string getType() const; + + virtual void createCommandBuffers(); + virtual void initialize( Device& device ); + virtual void tick(); + virtual void render(); + virtual void destroy(); + virtual void pipelineBarrier( VkCommandBuffer, uint8_t = -1 ); + }; + } +} \ No newline at end of file diff --git a/engine/inc/uf/ext/vulkan/rendermodes/rendertarget.h b/engine/inc/uf/ext/vulkan/rendermodes/rendertarget.h index fc1fe087..d56866e7 100644 --- a/engine/inc/uf/ext/vulkan/rendermodes/rendertarget.h +++ b/engine/inc/uf/ext/vulkan/rendermodes/rendertarget.h @@ -20,6 +20,7 @@ namespace ext { virtual void tick(); virtual void destroy(); virtual void render(); + virtual void pipelineBarrier( VkCommandBuffer, uint8_t = -1 ); }; } } \ No newline at end of file diff --git a/engine/inc/uf/ext/vulkan/texture.h b/engine/inc/uf/ext/vulkan/texture.h index 3cbc6d5f..3105d83e 100644 --- a/engine/inc/uf/ext/vulkan/texture.h +++ b/engine/inc/uf/ext/vulkan/texture.h @@ -89,7 +89,6 @@ namespace ext { void loadFromFile( std::string filename, Device& device, - VkQueue copyQueue, VkFormat format = VK_FORMAT_R8G8B8A8_UNORM, VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, @@ -98,7 +97,6 @@ namespace ext { void loadFromImage( uf::Image& image, Device& device, - VkQueue copyQueue, VkFormat format = VK_FORMAT_R8G8B8A8_UNORM, VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, @@ -118,7 +116,6 @@ namespace ext { uint32_t texWidth, uint32_t texHeight, Device& device, - VkQueue copyQueue, VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT, VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ); @@ -126,7 +123,6 @@ namespace ext { Device& device, uint32_t texWidth, uint32_t texHeight, - VkQueue copyQueue, VkFormat format = VK_FORMAT_R8G8B8A8_UNORM ); void aliasAttachment( const RenderTarget::Attachment& attachment, bool = true ); diff --git a/engine/inc/uf/ext/vulkan/vulkan.h b/engine/inc/uf/ext/vulkan/vulkan.h index 06de000f..f0c0860c 100644 --- a/engine/inc/uf/ext/vulkan/vulkan.h +++ b/engine/inc/uf/ext/vulkan/vulkan.h @@ -45,6 +45,8 @@ namespace ext { extern UF_API bool validation; extern UF_API std::vector validationFilters; extern UF_API std::vector requestedDeviceFeatures; + extern UF_API std::vector requestedDeviceExtensions; + extern UF_API std::vector requestedInstanceExtensions; extern UF_API Device device; typedef VmaAllocator Allocator; extern UF_API Allocator allocator; @@ -60,9 +62,11 @@ namespace ext { extern UF_API std::vector renderModes; extern UF_API std::vector scenes; + bool UF_API hasRenderMode( const std::string&, bool = true ); RenderMode& UF_API addRenderMode( RenderMode*, const std::string& = "" ); RenderMode& UF_API getRenderMode( const std::string&, bool = true ); std::vector UF_API getRenderModes( const std::string&, bool = true ); + std::vector UF_API getRenderModes( const std::vector&, bool = true ); void UF_API removeRenderMode( RenderMode*, bool = true ); void UF_API initialize( uint8_t = 0 ); diff --git a/engine/src/engine/scene/scene.cpp b/engine/src/engine/scene/scene.cpp index 1085f697..f46a4e02 100644 --- a/engine/src/engine/scene/scene.cpp +++ b/engine/src/engine/scene/scene.cpp @@ -5,6 +5,7 @@ #include #include +#include #include UF_OBJECT_REGISTER_CPP(Scene) @@ -25,7 +26,16 @@ void uf::Scene::tick() { std::vector blitters; auto& renderMode = ext::vulkan::getRenderMode("", true); - if ( renderMode.getType() == "Deferred (Stereoscopic)" ) { + bool hasCompute = ext::vulkan::hasRenderMode("C:RT:" + std::to_string(this->getUid()), true); + if ( hasCompute ) { + // auto& renderMode = ext::vulkan::getRenderMode("C:RT:" + std::to_string(this->getUid()), true); + // auto* renderModePointer = (ext::vulkan::ComputeRenderMode*) &renderMode; + // if ( renderModePointer->compute.initialized ) { + // blitters.push_back(&renderModePointer->compute); + // } else { + // hasCompute = false; + // } + } else if ( renderMode.getType() == "Deferred (Stereoscopic)" ) { auto* renderModePointer = (ext::vulkan::StereoscopicDeferredRenderMode*) &renderMode; blitters.push_back(&renderModePointer->blitters.left); blitters.push_back(&renderModePointer->blitters.right); @@ -134,18 +144,18 @@ void uf::Scene::tick() { if ( entity == &controller ) light.position.y += 2; - light.position.w = metadata["light"]["power"].asFloat(); + light.position.w = metadata["light"]["radius"].asFloat(); light.color.x = metadata["light"]["color"][0].asFloat(); light.color.y = metadata["light"]["color"][1].asFloat(); light.color.z = metadata["light"]["color"][2].asFloat(); - light.color.w = metadata["light"]["radius"].asFloat(); + light.color.w = metadata["light"]["power"].asFloat(); light.type.x = metadata["light"]["type"].asUInt64(); light.type.y = metadata["light"]["shadows"]["enabled"].asBool(); - if ( entity->hasComponent() ) { + if ( !hasCompute && entity->hasComponent() ) { auto& renderMode = entity->getComponent(); auto& renderTarget = renderMode.renderTarget; diff --git a/engine/src/ext/openvr/openvr.cpp b/engine/src/ext/openvr/openvr.cpp index d7875f5d..2f71e7de 100644 --- a/engine/src/ext/openvr/openvr.cpp +++ b/engine/src/ext/openvr/openvr.cpp @@ -288,7 +288,7 @@ void ext::openvr::tick() { graphic.initializeGeometry(mesh); auto& texture = graphic.material.textures.emplace_back(); - texture.fromBuffers( (void*) queued.texture->rubTextureMapData, len, VK_FORMAT_R8G8B8A8_UNORM, queued.texture->unWidth, queued.texture->unHeight, ext::vulkan::device, ext::vulkan::device.graphicsQueue ); + texture.fromBuffers( (void*) queued.texture->rubTextureMapData, len, VK_FORMAT_R8G8B8A8_UNORM, queued.texture->unWidth, queued.texture->unHeight, ext::vulkan::device ); } // clear { @@ -455,7 +455,7 @@ void ext::openvr::submit() { bool invert = swapEyes; vulkanData.m_pDevice = ( VkDevice_T * ) ext::vulkan::device; vulkanData.m_pPhysicalDevice = ( VkPhysicalDevice_T * ) ext::vulkan::device.physicalDevice; vulkanData.m_pInstance = ( VkInstance_T *) ext::vulkan::device.instance; - vulkanData.m_pQueue = ( VkQueue_T * ) ext::vulkan::device.presentQueue; + vulkanData.m_pQueue = ( VkQueue_T * ) ext::vulkan::device.queues.present; vulkanData.m_nQueueFamilyIndex = ext::vulkan::device.queueFamilyIndices.present; vulkanData.m_nWidth = width; diff --git a/engine/src/ext/vulkan/buffer.cpp b/engine/src/ext/vulkan/buffer.cpp index 41285702..aec03530 100644 --- a/engine/src/ext/vulkan/buffer.cpp +++ b/engine/src/ext/vulkan/buffer.cpp @@ -176,7 +176,7 @@ size_t ext::vulkan::Buffers::initializeBuffer( void* data, VkDeviceSize length, return index; } - VkQueue queue = device->graphicsQueue; + // VkQueue queue = device->queues.transfer; Buffer staging; VkDeviceSize storageBufferSize = length; device->createBuffer( @@ -199,7 +199,7 @@ size_t ext::vulkan::Buffers::initializeBuffer( void* data, VkDeviceSize length, VkBufferCopy copyRegion = {}; copyRegion.size = storageBufferSize; vkCmdCopyBuffer(copyCmd, staging.buffer, buffer.buffer, 1, ©Region); - device->flushCommandBuffer(copyCmd, queue, true); + device->flushCommandBuffer(copyCmd, true); staging.destroy(); // buffers.push_back( std::move(buffer) ); @@ -225,7 +225,7 @@ void ext::vulkan::Buffers::updateBuffer( void* data, VkDeviceSize length, Buffer buffer.unmap(); return; } - VkQueue queue = device->graphicsQueue; + // VkQueue queue = device->queues.transfer; Buffer staging; device->createBuffer( VK_BUFFER_USAGE_TRANSFER_SRC_BIT, @@ -240,6 +240,6 @@ void ext::vulkan::Buffers::updateBuffer( void* data, VkDeviceSize length, Buffer VkBufferCopy copyRegion = {}; copyRegion.size = length; vkCmdCopyBuffer(copyCmd, staging.buffer, buffer.buffer, 1, ©Region); - device->flushCommandBuffer(copyCmd, queue, true); + device->flushCommandBuffer(copyCmd, true); staging.destroy(); } \ No newline at end of file diff --git a/engine/src/ext/vulkan/device.cpp b/engine/src/ext/vulkan/device.cpp index d800bdb5..4dc42a0b 100644 --- a/engine/src/ext/vulkan/device.cpp +++ b/engine/src/ext/vulkan/device.cpp @@ -258,7 +258,7 @@ int ext::vulkan::Device::rate( VkPhysicalDevice device ) { } VkCommandBuffer ext::vulkan::Device::createCommandBuffer( VkCommandBufferLevel level, bool begin ){ - VkCommandBufferAllocateInfo cmdBufAllocateInfo = ext::vulkan::initializers::commandBufferAllocateInfo( commandPool, level, 1 ); + VkCommandBufferAllocateInfo cmdBufAllocateInfo = ext::vulkan::initializers::commandBufferAllocateInfo( commandPool.transfer, level, 1 ); VkCommandBuffer commandBuffer; VK_CHECK_RESULT( vkAllocateCommandBuffers( logicalDevice, &cmdBufAllocateInfo, &commandBuffer ) ); @@ -270,7 +270,7 @@ VkCommandBuffer ext::vulkan::Device::createCommandBuffer( VkCommandBufferLevel l return commandBuffer; } -void ext::vulkan::Device::flushCommandBuffer( VkCommandBuffer commandBuffer, VkQueue queue, bool free ) { +void ext::vulkan::Device::flushCommandBuffer( VkCommandBuffer commandBuffer, bool free ) { if ( commandBuffer == VK_NULL_HANDLE ) return; VK_CHECK_RESULT( vkEndCommandBuffer( commandBuffer ) ); @@ -285,13 +285,13 @@ void ext::vulkan::Device::flushCommandBuffer( VkCommandBuffer commandBuffer, VkQ VK_CHECK_RESULT(vkCreateFence(logicalDevice, &fenceInfo, nullptr, &fence)); // Submit to the queue - VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence)); + VK_CHECK_RESULT(vkQueueSubmit(device.queues.transfer, 1, &submitInfo, fence)); // Wait for the fence to signal that command buffer has finished executing VK_CHECK_RESULT(vkWaitForFences(logicalDevice, 1, &fence, VK_TRUE, DEFAULT_FENCE_TIMEOUT)); vkDestroyFence(logicalDevice, fence, nullptr); - if ( free ) vkFreeCommandBuffers(logicalDevice, commandPool, 1, &commandBuffer); + if ( free ) vkFreeCommandBuffers(logicalDevice, commandPool.transfer, 1, &commandBuffer); } VkResult ext::vulkan::Device::createBuffer( VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer* buffer, VkDeviceMemory* memory, void *data ) { @@ -406,8 +406,10 @@ void ext::vulkan::Device::initialize() { } // // Get extensions - // OpenVR Support std::vector requestedExtensions = window->getExtensions( ext::vulkan::validation ); + // Load any requested extensions + requestedExtensions.insert( requestedExtensions.end(), ext::vulkan::requestedInstanceExtensions.begin(), ext::vulkan::requestedInstanceExtensions.end() ); + // OpenVR Support if ( ext::openvr::enabled ) VRExtensions(requestedExtensions); { @@ -508,11 +510,20 @@ void ext::vulkan::Device::initialize() { } // Update properties { - vkGetPhysicalDeviceProperties( this->physicalDevice, &properties ); - // Features should be checked by the examples before using them - vkGetPhysicalDeviceFeatures( this->physicalDevice, &features ); - // Memory properties are used regularly for creating all kinds of buffers - vkGetPhysicalDeviceMemoryProperties( this->physicalDevice, &memoryProperties ); + { + vkGetPhysicalDeviceProperties( this->physicalDevice, &properties ); + // Features should be checked by the examples before using them + vkGetPhysicalDeviceFeatures( this->physicalDevice, &features ); + // Memory properties are used regularly for creating all kinds of buffers + vkGetPhysicalDeviceMemoryProperties( this->physicalDevice, &memoryProperties ); + } + { + vkGetPhysicalDeviceProperties2( this->physicalDevice, &properties2 ); + // Features should be checked by the examples before using them + vkGetPhysicalDeviceFeatures2( this->physicalDevice, &features2 ); + // Memory properties are used regularly for creating all kinds of buffers + vkGetPhysicalDeviceMemoryProperties2( this->physicalDevice, &memoryProperties2 ); + } // Queue family properties, used for setting up requested queues upon device creation uint32_t queueFamilyCount; vkGetPhysicalDeviceQueueFamilyProperties( this->physicalDevice, &queueFamilyCount, nullptr ); @@ -524,43 +535,45 @@ void ext::vulkan::Device::initialize() { { bool useSwapChain = true; VkQueueFlags requestedQueueTypes = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; + std::vector requestedExtensions; std::vector deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; + requestedExtensions.insert( requestedExtensions.end(), ext::vulkan::requestedInstanceExtensions.begin(), ext::vulkan::requestedInstanceExtensions.end() ); /* OpenVR support */ if ( ext::openvr::enabled && vr::VRCompositor() ) { uint32_t nBufferSize = vr::VRCompositor()->GetVulkanDeviceExtensionsRequired( ( VkPhysicalDevice_T * ) this->physicalDevice, nullptr, 0 ); if ( nBufferSize > 0 ) { char pExtensionStr[nBufferSize]; pExtensionStr[0] = 0; vr::VRCompositor()->GetVulkanDeviceExtensionsRequired( ( VkPhysicalDevice_T * ) this->physicalDevice, pExtensionStr, nBufferSize ); - std::vector extensions = uf::string::split( pExtensionStr, " " ); - - // Allocate enough ExtensionProperties to support all extensions being enabled - uint32_t extensionsCount = 0; - uint32_t enabledExtensionsCount = 0; - - VK_CHECK_RESULT(vkEnumerateDeviceExtensionProperties( this->physicalDevice, NULL, &extensionsCount, NULL )); - std::vector extensionProperties(extensionsCount); - VK_CHECK_RESULT( vkEnumerateDeviceExtensionProperties( this->physicalDevice, NULL, &extensionsCount, &extensionProperties[0] ) ); - - for ( size_t i = 0; i < extensions.size(); ++i ) { - bool found = false; - uint32_t index = 0; - for ( index = 0; index < extensionsCount; index++ ) { - if ( strcmp( extensions[i].c_str(), extensionProperties[index].extensionName ) == 0 ) { - for ( auto alreadyAdded : deviceExtensions ) { - if ( strcmp( extensions[i].c_str(), alreadyAdded ) == 0 ) { - found = true; - break; - } + std::vector vrExtensions = uf::string::split( pExtensionStr, " " ); + requestedExtensions.insert( requestedExtensions.end(), vrExtensions.begin(), vrExtensions.end() ); + } + // Allocate enough ExtensionProperties to support all extensions being enabled + uint32_t extensionsCount = 0; + uint32_t enabledExtensionsCount = 0; + + VK_CHECK_RESULT(vkEnumerateDeviceExtensionProperties( this->physicalDevice, NULL, &extensionsCount, NULL )); + std::vector extensionProperties(extensionsCount); + VK_CHECK_RESULT( vkEnumerateDeviceExtensionProperties( this->physicalDevice, NULL, &extensionsCount, &extensionProperties[0] ) ); + + for ( size_t i = 0; i < requestedExtensions.size(); ++i ) { + bool found = false; + uint32_t index = 0; + for ( index = 0; index < extensionsCount; index++ ) { + if ( strcmp( requestedExtensions[i].c_str(), extensionProperties[index].extensionName ) == 0 ) { + for ( auto alreadyAdded : deviceExtensions ) { + if ( strcmp( requestedExtensions[i].c_str(), alreadyAdded ) == 0 ) { + found = true; + break; } - if ( found ) break; - found = true; - deviceExtensions.push_back(extensionProperties[index].extensionName); } + if ( found ) break; + found = true; + deviceExtensions.push_back(extensionProperties[index].extensionName); } - if ( !found ) std::cout << "Vulkan missing requested extension " << extensions[index] << std::endl; } + if ( !found ) std::cout << "Vulkan missing requested extension " << requestedExtensions[index] << std::endl; } } @@ -648,8 +661,26 @@ void ext::vulkan::Device::initialize() { cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; cmdPoolInfo.queueFamilyIndex = queueFamilyIndices.graphics; cmdPoolInfo.flags = createFlags; - if ( vkCreateCommandPool( this->logicalDevice, &cmdPoolInfo, nullptr, &commandPool ) != VK_SUCCESS ) - throw std::runtime_error("failed to create command pool!"); + if ( vkCreateCommandPool( this->logicalDevice, &cmdPoolInfo, nullptr, &commandPool.graphics ) != VK_SUCCESS ) + throw std::runtime_error("failed to create command pool for graphics!"); + } + { + VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = queueFamilyIndices.compute; + cmdPoolInfo.flags = createFlags; + if ( vkCreateCommandPool( this->logicalDevice, &cmdPoolInfo, nullptr, &commandPool.compute ) != VK_SUCCESS ) + throw std::runtime_error("failed to create command pool for compute!"); + } + { + VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = queueFamilyIndices.transfer; + cmdPoolInfo.flags = createFlags; + if ( vkCreateCommandPool( this->logicalDevice, &cmdPoolInfo, nullptr, &commandPool.transfer ) != VK_SUCCESS ) + throw std::runtime_error("failed to create command pool for transfer!"); } // Set queue { @@ -681,9 +712,10 @@ void ext::vulkan::Device::initialize() { device.queueFamilyIndices.present = presentQueueNodeIndex; - vkGetDeviceQueue( device, device.queueFamilyIndices.graphics, 0, &graphicsQueue ); - vkGetDeviceQueue( device, device.queueFamilyIndices.present, 0, &presentQueue ); - vkGetDeviceQueue( device, device.queueFamilyIndices.compute, 0, &computeQueue ); + vkGetDeviceQueue( device, device.queueFamilyIndices.graphics, 0, &queues.graphics ); + vkGetDeviceQueue( device, device.queueFamilyIndices.present, 0, &queues.present ); + vkGetDeviceQueue( device, device.queueFamilyIndices.compute, 0, &queues.compute ); + vkGetDeviceQueue( device, device.queueFamilyIndices.transfer, 0, &queues.transfer ); } // Set formats { @@ -750,9 +782,17 @@ void ext::vulkan::Device::destroy() { vkDestroyPipelineCache( this->logicalDevice, this->pipelineCache, nullptr ); this->pipelineCache = nullptr; } - if ( this->commandPool ) { - vkDestroyCommandPool( this->logicalDevice, this->commandPool, nullptr ); - this->commandPool = nullptr; + if ( this->commandPool.graphics ) { + vkDestroyCommandPool( this->logicalDevice, this->commandPool.graphics, nullptr ); + this->commandPool.graphics = nullptr; + } + if ( this->commandPool.compute ) { + vkDestroyCommandPool( this->logicalDevice, this->commandPool.compute, nullptr ); + this->commandPool.compute = nullptr; + } + if ( this->commandPool.transfer ) { + vkDestroyCommandPool( this->logicalDevice, this->commandPool.transfer, nullptr ); + this->commandPool.transfer = nullptr; } if ( this->logicalDevice ) { vkDestroyDevice( this->logicalDevice, nullptr ); diff --git a/engine/src/ext/vulkan/graphic.cpp b/engine/src/ext/vulkan/graphic.cpp index 339a2837..31b91866 100644 --- a/engine/src/ext/vulkan/graphic.cpp +++ b/engine/src/ext/vulkan/graphic.cpp @@ -61,10 +61,17 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const std::st auto parseResource = [&]( const spirv_cross::Resource& resource, VkDescriptorType descriptorType ) { switch ( descriptorType ) { case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { - const auto& base_type = comp.get_type(resource.base_type_id); auto& uniform = uniforms.emplace_back(); + const auto& base_type = comp.get_type(resource.base_type_id); uniform.create( comp.get_declared_struct_size(base_type) ); } break; + /* + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { + auto& uniform = uniforms.emplace_back(); + // const auto& base_type = comp.get_type(resource.base_type_id); + // uniform.create( comp.get_declared_struct_size(base_type) ); + } break; + */ } const auto& type = comp.get_type(resource.type_id); @@ -73,8 +80,9 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const std::st descriptorSetLayoutBindings.push_back( ext::vulkan::initializers::descriptorSetLayoutBinding( descriptorType, stage, comp.get_decoration(resource.id, spv::DecorationBinding), size ) ); }; - // std::cout << "Found resource: "#type " with binding: " << comp.get_decoration(resource.id, spv::DecorationBinding) << std::endl;\ + // std::cout << "["< blendAttachmentStates; - auto& subpass = renderTarget.passes[graphic.descriptor.subpass]; - /* - for ( auto& color : subpass.colors ) { - VkPipelineColorBlendAttachmentState blendAttachmentState = ext::vulkan::initializers::pipelineColorBlendAttachmentState( - 0xf, - VK_TRUE - ); - blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; - blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; - blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; - blendAttachmentStates.push_back(blendAttachmentState); - } - */ - for ( auto& color : subpass.colors ) { - blendAttachmentStates.push_back(renderTarget.attachments[color.attachment].blendState); - } - // require blending if independentBlend is not an enabled feature - if ( !device.enabledFeatures.independentBlend ) { - for ( size_t i = 1; i < blendAttachmentStates.size(); ++i ) { - blendAttachmentStates[i] = blendAttachmentStates[0]; + + if ( renderMode.getType() != "Swapchain" ) { + auto& subpass = renderTarget.passes[graphic.descriptor.subpass]; + for ( auto& color : subpass.colors ) { + blendAttachmentStates.push_back(renderTarget.attachments[color.attachment].blendState); } + // require blending if independentBlend is not an enabled feature + if ( !device.enabledFeatures.independentBlend ) { + for ( size_t i = 1; i < blendAttachmentStates.size(); ++i ) { + blendAttachmentStates[i] = blendAttachmentStates[0]; + } + } + } else { + graphic.descriptor.subpass = 0; + + VkBool32 blendEnabled = VK_FALSE; + VkColorComponentFlags writeMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT; + + if ( true ) { + blendEnabled = VK_TRUE; + writeMask |= VK_COLOR_COMPONENT_A_BIT; + } + + VkPipelineColorBlendAttachmentState blendAttachmentState = ext::vulkan::initializers::pipelineColorBlendAttachmentState( + writeMask, + blendEnabled + ); + if ( blendEnabled == VK_TRUE ) { + blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; + blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; + } + blendAttachmentStates.push_back(blendAttachmentState); } VkPipelineColorBlendStateCreateInfo colorBlendState = ext::vulkan::initializers::pipelineColorBlendStateCreateInfo( @@ -377,7 +412,11 @@ void ext::vulkan::Pipeline::initialize( Graphic& graphic ) { } } void ext::vulkan::Pipeline::record( Graphic& graphic, VkCommandBuffer commandBuffer ) { + auto bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; for ( auto& shader : graphic.material.shaders ) { + if ( shader.descriptor.stage == VK_SHADER_STAGE_COMPUTE_BIT ) { + bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE; + } size_t offset = 0; for ( auto& pushConstant : shader.pushConstants ) { struct PushConstant { @@ -395,10 +434,10 @@ void ext::vulkan::Pipeline::record( Graphic& graphic, VkCommandBuffer commandBuf } } // Bind descriptor sets describing shader binding points - vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); + vkCmdBindDescriptorSets(commandBuffer, bindPoint, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); // Bind the rendering pipeline // The pipeline (state object) contains all states of the rendering pipeline, binding it will set all the states specified at pipeline creation time - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindPipeline(commandBuffer, bindPoint, pipeline); } void ext::vulkan::Pipeline::update( Graphic& graphic ) { // generate fallback empty texture @@ -416,19 +455,22 @@ void ext::vulkan::Pipeline::update( Graphic& graphic ) { RenderMode& renderMode = ext::vulkan::getRenderMode(graphic.descriptor.renderMode, true); auto& renderTarget = renderMode.getRenderTarget(graphic.descriptor.renderTarget ); - auto& subpass = renderTarget.passes[graphic.descriptor.subpass]; - for ( auto& input : subpass.inputs ) { - inputDescriptors.push_back(ext::vulkan::initializers::descriptorImageInfo( - renderTarget.attachments[input.attachment].view, - // input.layout - input.layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : input.layout - )); + if ( graphic.descriptor.subpass < renderTarget.passes.size() ) { + auto& subpass = renderTarget.passes[graphic.descriptor.subpass]; + for ( auto& input : subpass.inputs ) { + inputDescriptors.push_back(ext::vulkan::initializers::descriptorImageInfo( + renderTarget.attachments[input.attachment].view, + // input.layout + input.layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : input.layout + )); + } } { for ( auto& shader : graphic.material.shaders ) { auto textures = graphic.material.textures.begin(); auto samplers = graphic.material.samplers.begin(); - auto buffers = shader.buffers.begin(); + auto buffersUniforms = shader.buffers.begin(); + auto buffersStorage = graphic.buffers.begin(); auto attachments = inputDescriptors.begin(); for ( auto& layout : shader.descriptorSetLayoutBindings ) { @@ -436,7 +478,8 @@ void ext::vulkan::Pipeline::update( Graphic& graphic ) { if ( layout.descriptorCount > 1 ) { switch ( layout.descriptorType ) { case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: { + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: { size_t imageInfosStart = imageInfos.size(); // assume we have a texture, and fill it in the slots as defaults for ( size_t i = 0; i < layout.descriptorCount; ++i ) { @@ -467,7 +510,8 @@ void ext::vulkan::Pipeline::update( Graphic& graphic ) { VkDescriptorImageInfo* imageInfo = NULL; switch ( layout.descriptorType ) { case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: { + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: { if ( textures == graphic.material.textures.end() ) { imageInfo = &emptyTexture.descriptor; break; @@ -482,15 +526,27 @@ void ext::vulkan::Pipeline::update( Graphic& graphic ) { imageInfo = &((samplers++)->descriptor.info); } break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { - if ( buffers == shader.buffers.end() ) { - std::cout << "buffers == shader.buffers.end()" << std::endl; + if ( buffersUniforms == shader.buffers.end() ) { + std::cout << "buffersUniforms == shader.buffers.end()" << std::endl; break; } writeDescriptorSets.push_back(ext::vulkan::initializers::writeDescriptorSet( descriptorSet, layout.descriptorType, layout.binding, - &((buffers++)->descriptor) + &((buffersUniforms++)->descriptor) + )); + } break; + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { + if ( buffersStorage == graphic.buffers.end() ) { + std::cout << "buffersStorage == graphic.buffers.end()" << std::endl; + break; + } + writeDescriptorSets.push_back(ext::vulkan::initializers::writeDescriptorSet( + descriptorSet, + layout.descriptorType, + layout.binding, + &((buffersStorage++)->descriptor) )); } break; case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { diff --git a/engine/src/ext/vulkan/rendermode.cpp b/engine/src/ext/vulkan/rendermode.cpp index c2f053e6..2341cd4f 100644 --- a/engine/src/ext/vulkan/rendermode.cpp +++ b/engine/src/ext/vulkan/rendermode.cpp @@ -49,7 +49,8 @@ void ext::vulkan::RenderMode::createCommandBuffers() { scene->process(filter); } - VK_CHECK_RESULT(vkWaitForFences(*device, fences.size(), fences.data(), VK_TRUE, UINT64_MAX)); + this->synchronize(); + //VK_CHECK_RESULT(vkWaitForFences(*device, fences.size(), fences.data(), VK_TRUE, UINT64_MAX)); createCommandBuffers( graphics ); } void ext::vulkan::RenderMode::createCommandBuffers( const std::vector& graphics ) { @@ -77,13 +78,13 @@ void ext::vulkan::RenderMode::render() { submitInfo.commandBufferCount = 1; // Submit to the graphics queue passing a wait fence - VK_CHECK_RESULT(vkQueueSubmit(device->graphicsQueue, 1, &submitInfo, fences[currentBuffer])); + VK_CHECK_RESULT(vkQueueSubmit(device->queues.graphics, 1, &submitInfo, fences[currentBuffer])); // Present the current buffer to the swap chain // Pass the semaphore signaled by the command buffer submission from the submit info as the wait semaphore for swap chain presentation // This ensures that the image is not presented to the windowing system until all commands have been submitted - VK_CHECK_RESULT(swapchain.queuePresent(device->presentQueue, currentBuffer, renderCompleteSemaphore)); - VK_CHECK_RESULT(vkQueueWaitIdle(device->presentQueue)); + VK_CHECK_RESULT(swapchain.queuePresent(device->queues.present, currentBuffer, renderCompleteSemaphore)); + VK_CHECK_RESULT(vkQueueWaitIdle(device->queues.present)); } void ext::vulkan::RenderMode::initialize( Device& device ) { @@ -101,7 +102,7 @@ void ext::vulkan::RenderMode::initialize( Device& device ) { commands.resize( swapchain.buffers ); VkCommandBufferAllocateInfo cmdBufAllocateInfo = ext::vulkan::initializers::commandBufferAllocateInfo( - device.commandPool, + this->getType() == "Compute" ? device.commandPool.compute : device.commandPool.graphics, VK_COMMAND_BUFFER_LEVEL_PRIMARY, static_cast(commands.size()) ); @@ -147,7 +148,7 @@ void ext::vulkan::RenderMode::destroy() { renderTarget.destroy(); if ( commands.size() > 0 ) { - vkFreeCommandBuffers( *device, device->commandPool, static_cast(commands.size()), commands.data()); + vkFreeCommandBuffers( *device, this->getType() == "Compute" ? device->commandPool.compute : device->commandPool.graphics, static_cast(commands.size()), commands.data()); } if ( renderCompleteSemaphore != VK_NULL_HANDLE ) { @@ -162,4 +163,6 @@ void ext::vulkan::RenderMode::destroy() { void ext::vulkan::RenderMode::synchronize( uint64_t timeout ) { if ( !device ) return; VK_CHECK_RESULT(vkWaitForFences( *device, fences.size(), fences.data(), VK_TRUE, timeout )); +} +void ext::vulkan::RenderMode::pipelineBarrier( VkCommandBuffer command, uint8_t stage ) { } \ No newline at end of file diff --git a/engine/src/ext/vulkan/rendermodes/compute.cpp b/engine/src/ext/vulkan/rendermodes/compute.cpp new file mode 100644 index 00000000..d2d06498 --- /dev/null +++ b/engine/src/ext/vulkan/rendermodes/compute.cpp @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +std::string ext::vulkan::ComputeRenderMode::getType() const { + return "Compute"; +} + +void ext::vulkan::ComputeRenderMode::initialize( Device& device ) { + ext::vulkan::RenderMode::initialize( device ); + +// this->width = 3840; +// this->height = 2160; + + { + auto width = this->width > 0 ? this->width : ext::vulkan::width; + auto height = this->height > 0 ? this->height : ext::vulkan::height; + + compute.device = &device; + compute.material.device = &device; + compute.descriptor.renderMode = this->name; + compute.material.initializeShaders({ + {"./data/shaders/raytracing.comp.spv", VK_SHADER_STAGE_COMPUTE_BIT}, + }); + + for ( size_t i = 0; i < (ext::openvr::context ? 2 : 1); ++i ) { + Texture2D& texture = compute.material.textures.emplace_back(); + texture.asRenderTarget( device, width, height ); + } + + { + auto& scene = uf::scene::getCurrentScene(); + auto& metadata = scene.getComponent(); + + auto& shader = compute.material.shaders.front(); + struct SpecializationConstant { + int32_t eyes = 2; + int32_t maxLights = 16; + }; + auto& specializationConstants = shader.specializationConstants.get(); + specializationConstants.maxLights = metadata["system"]["config"]["engine"]["scenes"]["max lights"].asUInt64(); + specializationConstants.eyes = ext::openvr::context ? 2 : 1; + } + + // update buffers + if ( !compute.buffers.empty() ) { + auto& pipeline = compute.getPipeline(); + pipeline.update( compute ); + } + } + { + uf::BaseMesh mesh; + mesh.vertices = { + { {-1.0f, 1.0f}, {0.0f, 1.0f}, }, + { {-1.0f, -1.0f}, {0.0f, 0.0f}, }, + { {1.0f, -1.0f}, {1.0f, 0.0f}, }, + { {1.0f, 1.0f}, {1.0f, 1.0f}, } + }; + mesh.indices = { + 0, 1, 2, 2, 3, 0 + }; + blitter.device = &device; + blitter.material.device = &device; + blitter.descriptor.subpass = 1; + + blitter.descriptor.depthTest.test = false; + blitter.descriptor.depthTest.write = false; + + blitter.initializeGeometry( mesh ); + blitter.material.initializeShaders({ + {"./data/shaders/display.blit.vert.spv", VK_SHADER_STAGE_VERTEX_BIT}, + {"./data/shaders/display.blit.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT} + }); + for ( auto& computeTexture : compute.material.textures ) { + Texture2D& texture = blitter.material.textures.emplace_back(); + texture = computeTexture; + texture.device = NULL; + } + blitter.initializePipeline(); + } +} +void ext::vulkan::ComputeRenderMode::render() { + if ( compute.buffers.empty() ) return; + + // Submit compute commands + // Use a fence to ensure that compute command buffer has finished executing before using it again + VK_CHECK_RESULT(vkWaitForFences( *device, 1, &fences[currentBuffer], VK_TRUE, UINT64_MAX )); + VK_CHECK_RESULT(vkResetFences( *device, 1, &fences[currentBuffer] )); + + VkSubmitInfo submitInfo = ext::vulkan::initializers::submitInfo(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commands[currentBuffer]; + + VK_CHECK_RESULT(vkQueueSubmit(device->queues.compute, 1, &submitInfo, fences[currentBuffer])); +} +void ext::vulkan::ComputeRenderMode::tick() { + ext::vulkan::RenderMode::tick(); + + if ( ext::vulkan::resized ) { + // auto-resize texture + if ( this->width == 0 && this->height == 0 ) { + auto width = this->width > 0 ? this->width : ext::vulkan::width; + auto height = this->height > 0 ? this->height : ext::vulkan::height; + for ( auto& texture : compute.material.textures ) { + texture.destroy(); + } + compute.material.textures.clear(); + for ( size_t i = 0; i < (ext::openvr::context ? 2 : 1); ++i ) { + Texture2D& texture = compute.material.textures.emplace_back(); + texture.asRenderTarget( *device, width, height ); + } + } + // update blitter descriptor set + if ( blitter.initialized ) { + blitter.material.textures.clear(); + for ( auto& computeTexture : compute.material.textures ) { + Texture2D& texture = blitter.material.textures.emplace_back(); + texture = computeTexture; + texture.device = NULL; + } + auto& pipeline = blitter.getPipeline(); + pipeline.update( blitter ); + } + // if ( compute.initialized ) { + if ( !compute.buffers.empty() ) { + auto& pipeline = compute.getPipeline(); + pipeline.update( compute ); + } + } +} +void ext::vulkan::ComputeRenderMode::destroy() { + ext::vulkan::RenderMode::destroy(); + blitter.destroy(); + compute.destroy(); +} +void ext::vulkan::ComputeRenderMode::pipelineBarrier( VkCommandBuffer commandBuffer, uint8_t state ) { + if ( compute.buffers.empty() ) return; + + VkImageMemoryBarrier imageMemoryBarrier = {}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + + for ( auto& texture : compute.material.textures ) { + VkPipelineStageFlags srcStageMask, dstStageMask; + imageMemoryBarrier.image = texture.image; + imageMemoryBarrier.oldLayout = texture.descriptor.imageLayout; + imageMemoryBarrier.newLayout = texture.descriptor.imageLayout; + + switch ( state ) { + case 0: { + imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_SHADER_READ_BIT; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } break; + case 1: { + imageMemoryBarrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_SHADER_READ_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + } break; + // ensure + default: { + imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + } + } + + vkCmdPipelineBarrier( commandBuffer, + srcStageMask, dstStageMask, + VK_FLAGS_NONE, + 0, NULL, + 0, NULL, + 1, &imageMemoryBarrier + ); + + texture.descriptor.imageLayout = imageMemoryBarrier.newLayout; + } +} +void ext::vulkan::ComputeRenderMode::createCommandBuffers( ) { + this->synchronize(); + + float width = this->width > 0 ? this->width : ext::vulkan::width; + float height = this->height > 0 ? this->height : ext::vulkan::height; + + if ( compute.buffers.empty() ) return; + + VkCommandBufferBeginInfo cmdBufInfo = ext::vulkan::initializers::commandBufferBeginInfo(); + auto& pipeline = compute.getPipeline(); + for (size_t i = 0; i < commands.size(); ++i) { + VK_CHECK_RESULT(vkBeginCommandBuffer(commands[i], &cmdBufInfo)); + pipeline.record(compute, commands[i]); + vkCmdDispatch(commands[i], width / dispatchSize.x, height / dispatchSize.y, 1); + VK_CHECK_RESULT(vkEndCommandBuffer(commands[i])); + } +} \ No newline at end of file diff --git a/engine/src/ext/vulkan/rendermodes/deferred.cpp b/engine/src/ext/vulkan/rendermodes/deferred.cpp index f54362f6..74a9df7f 100644 --- a/engine/src/ext/vulkan/rendermodes/deferred.cpp +++ b/engine/src/ext/vulkan/rendermodes/deferred.cpp @@ -100,6 +100,17 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) { {"./data/shaders/display.subpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT}, {"./data/shaders/display.subpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT} }); + { + auto& scene = uf::scene::getCurrentScene(); + auto& metadata = scene.getComponent(); + + auto& shader = blitter.material.shaders.back(); + struct SpecializationConstant { + int32_t maxLights = 16; + }; + auto& specializationConstants = shader.specializationConstants.get(); + specializationConstants.maxLights = metadata["system"]["config"]["engine"]["scenes"]["max lights"].asUInt64(); + } blitter.initializePipeline(); } } @@ -128,7 +139,7 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const std::vector layers = ext::vulkan::getRenderModes("RenderTarget", false); +*/ + + std::vector layers = ext::vulkan::getRenderModes(std::vector{"RenderTarget", "Compute"}, false); for (size_t i = 0; i < commands.size(); ++i) { VK_CHECK_RESULT(vkBeginCommandBuffer(commands[i], &cmdBufInfo)); @@ -182,8 +194,9 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const std::vectorpipelineBarrier( commands[i], 0 ); + /* if ( layer->getName() == "" ) continue; RenderTarget& renderTarget = layer->renderTarget; for ( auto& attachment : renderTarget.attachments ) { @@ -197,6 +210,7 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const std::vectorpipelineBarrier( commands[i], 1 ); + /* if ( layer->getName() == "" ) continue; RenderTarget& renderTarget = layer->renderTarget; for ( auto& attachment : renderTarget.attachments ) { @@ -247,6 +263,7 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const std::vectorgraphicsQueue, 1, &submitInfo, fences[currentBuffer])); + VK_CHECK_RESULT(vkQueueSubmit(device->queues.graphics, 1, &submitInfo, fences[currentBuffer])); /* VkSemaphoreWaitInfo waitInfo = {}; waitInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO; @@ -155,6 +156,60 @@ void ext::vulkan::RenderTargetRenderMode::render() { VK_CHECK_RESULT(vkWaitSemaphores( *device, &waitInfo, UINT64_MAX )); */ } +void ext::vulkan::RenderTargetRenderMode::pipelineBarrier( VkCommandBuffer commandBuffer, uint8_t state ) { + VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics; //VK_QUEUE_FAMILY_IGNORED + imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics; //VK_QUEUE_FAMILY_IGNORED + imageMemoryBarrier.subresourceRange.baseMipLevel = 0; + imageMemoryBarrier.subresourceRange.levelCount = 1; + imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; + imageMemoryBarrier.subresourceRange.layerCount = 1; + imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + + for ( auto& attachment : renderTarget.attachments ) { + if ( !(attachment.usage & VK_IMAGE_USAGE_SAMPLED_BIT) ) continue; + if ( (attachment.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ) continue; + + VkPipelineStageFlags srcStageMask, dstStageMask; + imageMemoryBarrier.image = attachment.image; + imageMemoryBarrier.oldLayout = attachment.layout; + imageMemoryBarrier.newLayout = attachment.layout; + + switch ( state ) { + case 0: { + imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_SHADER_READ_BIT; + imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } break; + case 1: { + imageMemoryBarrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_SHADER_READ_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + imageMemoryBarrier.newLayout = attachment.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + } break; + // ensure + default: { + imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + } + } + + vkCmdPipelineBarrier( commandBuffer, + srcStageMask, dstStageMask, + VK_FLAGS_NONE, + 0, NULL, + 0, NULL, + 1, &imageMemoryBarrier + ); + + attachment.layout = imageMemoryBarrier.newLayout; + } +} void ext::vulkan::RenderTargetRenderMode::createCommandBuffers( const std::vector& graphics ) { // destroy if exists float width = this->width > 0 ? this->width : ext::vulkan::width; diff --git a/engine/src/ext/vulkan/rendermodes/stereoscopic_deferred.cpp b/engine/src/ext/vulkan/rendermodes/stereoscopic_deferred.cpp index 0762c084..f2ef00d7 100644 --- a/engine/src/ext/vulkan/rendermodes/stereoscopic_deferred.cpp +++ b/engine/src/ext/vulkan/rendermodes/stereoscopic_deferred.cpp @@ -116,6 +116,17 @@ void ext::vulkan::StereoscopicDeferredRenderMode::initialize( Device& device ) { {"./data/shaders/display.subpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT}, {"./data/shaders/display.subpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT} }); + { + auto& scene = uf::scene::getCurrentScene(); + auto& metadata = scene.getComponent(); + + auto& shader = blitter.material.shaders.back(); + struct SpecializationConstant { + int32_t maxLights = 16; + }; + auto& specializationConstants = shader.specializationConstants.get(); + specializationConstants.maxLights = metadata["system"]["config"]["engine"]["scenes"]["max lights"].asUInt64(); + } blitter.initializePipeline(); } } @@ -169,7 +180,7 @@ void ext::vulkan::StereoscopicDeferredRenderMode::createCommandBuffers( const st imageMemoryBarrier.subresourceRange.layerCount = 1; imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - std::vector layers = ext::vulkan::getRenderModes("RenderTarget", false); + std::vector layers = ext::vulkan::getRenderModes(std::vector{"RenderTarget", "Compute"}, false); for (size_t i = 0; i < commands.size(); ++i) { VK_CHECK_RESULT(vkBeginCommandBuffer(commands[i], &cmdBufInfo)); @@ -225,6 +236,8 @@ void ext::vulkan::StereoscopicDeferredRenderMode::createCommandBuffers( const st // transition layers for read for ( auto layer : layers ) { + layer->pipelineBarrier( commands[i], 0 ); + /* if ( layer->getName() == "" ) continue; RenderTarget& renderTarget = layer->renderTarget; for ( auto& attachment : renderTarget.attachments ) { @@ -238,6 +251,7 @@ void ext::vulkan::StereoscopicDeferredRenderMode::createCommandBuffers( const st vkCmdPipelineBarrier( commands[i], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT , VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); attachment.layout = imageMemoryBarrier.newLayout; } + */ } vkCmdBeginRenderPass(commands[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); @@ -275,6 +289,8 @@ void ext::vulkan::StereoscopicDeferredRenderMode::createCommandBuffers( const st vkCmdEndRenderPass(commands[i]); for ( auto layer : layers ) { + layer->pipelineBarrier( commands[i], 1 ); + /* if ( layer->getName() == "" ) continue; RenderTarget& renderTarget = layer->renderTarget; for ( auto& attachment : renderTarget.attachments ) { @@ -288,6 +304,7 @@ void ext::vulkan::StereoscopicDeferredRenderMode::createCommandBuffers( const st vkCmdPipelineBarrier( commands[i], VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT , 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); attachment.layout = imageMemoryBarrier.newLayout; } + */ } } // Blit eye to swapchain diff --git a/engine/src/ext/vulkan/texture.cpp b/engine/src/ext/vulkan/texture.cpp index eb447fab..ce7f99fb 100644 --- a/engine/src/ext/vulkan/texture.cpp +++ b/engine/src/ext/vulkan/texture.cpp @@ -198,12 +198,11 @@ void ext::vulkan::Texture2D::loadFromFile( VkImageLayout imageLayout, bool forceLinear ) { - return loadFromFile( filename, ext::vulkan::device, ext::vulkan::device.graphicsQueue, format, imageUsageFlags, imageLayout, forceLinear ); + return loadFromFile( filename, ext::vulkan::device, format, imageUsageFlags, imageLayout, forceLinear ); } void ext::vulkan::Texture2D::loadFromFile( std::string filename, Device& device, - VkQueue copyQueue, VkFormat format, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout, @@ -274,7 +273,6 @@ void ext::vulkan::Texture2D::loadFromFile( image.getDimensions()[0], image.getDimensions()[1], device, - copyQueue, imageUsageFlags, imageLayout ); @@ -286,12 +284,11 @@ void ext::vulkan::Texture2D::loadFromImage( VkImageLayout imageLayout, bool forceLinear ) { - return loadFromImage( image, ext::vulkan::device, ext::vulkan::device.graphicsQueue, format, imageUsageFlags, imageLayout, forceLinear ); + return loadFromImage( image, ext::vulkan::device, format, imageUsageFlags, imageLayout, forceLinear ); } void ext::vulkan::Texture2D::loadFromImage( uf::Image& image, Device& device, - VkQueue copyQueue, VkFormat format, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout, @@ -346,7 +343,6 @@ void ext::vulkan::Texture2D::loadFromImage( image.getDimensions()[0], image.getDimensions()[1], device, - copyQueue, imageUsageFlags, imageLayout ); @@ -358,14 +354,12 @@ void ext::vulkan::Texture2D::fromBuffers( uint32_t texWidth, uint32_t texHeight, Device& device, - VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout ) { this->initialize(device, texWidth, texHeight); this->mips = 1; - // Use a separate command buffer for texture loading VkCommandBuffer copyCmd = device.createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); @@ -455,7 +449,7 @@ void ext::vulkan::Texture2D::fromBuffers( imageLayout, subresourceRange ); - device.flushCommandBuffer(copyCmd, copyQueue); + device.flushCommandBuffer(copyCmd); // Clean up staging resources /* vkFreeMemory(device.logicalDevice, staging.memory, nullptr); @@ -481,10 +475,10 @@ void ext::vulkan::Texture2D::fromBuffers( this->updateDescriptors(); } -void ext::vulkan::Texture2D::asRenderTarget( Device& device, uint32_t width, uint32_t height, VkQueue copyQueue, VkFormat format ) { +void ext::vulkan::Texture2D::asRenderTarget( Device& device, uint32_t width, uint32_t height, VkFormat format ) { // Prepare blit target texture this->initialize( device, width, height ); - + // Get device properties for the requested texture format VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(device.physicalDevice, format, &formatProperties); @@ -530,7 +524,7 @@ void ext::vulkan::Texture2D::asRenderTarget( Device& device, uint32_t width, uin imageLayout ); - device.flushCommandBuffer(layoutCmd, copyQueue, true); + device.flushCommandBuffer(layoutCmd, true); // Create sampler sampler.initialize( device ); diff --git a/engine/src/ext/vulkan/vulkan.cpp b/engine/src/ext/vulkan/vulkan.cpp index 6b85248f..da90c41e 100644 --- a/engine/src/ext/vulkan/vulkan.cpp +++ b/engine/src/ext/vulkan/vulkan.cpp @@ -14,6 +14,8 @@ uint32_t ext::vulkan::height = 720; bool ext::vulkan::validation = true; std::vector ext::vulkan::validationFilters; std::vector ext::vulkan::requestedDeviceFeatures; +std::vector ext::vulkan::requestedDeviceExtensions; +std::vector ext::vulkan::requestedInstanceExtensions; ext::vulkan::Device ext::vulkan::device; ext::vulkan::Allocator ext::vulkan::allocator; ext::vulkan::Swapchain ext::vulkan::swapchain; @@ -159,17 +161,15 @@ void ext::vulkan::alignedFree(void* data) { #endif } -namespace { - bool hasRenderMode( const std::string& name, bool isName ) { - for ( auto& renderMode: ext::vulkan::renderModes ) { - if ( isName ) { - if ( renderMode->getName() == name ) return true; - } else { - if ( renderMode->getType() == name ) return true; - } +bool ext::vulkan::hasRenderMode( const std::string& name, bool isName ) { + for ( auto& renderMode: ext::vulkan::renderModes ) { + if ( isName ) { + if ( renderMode->getName() == name ) return true; + } else { + if ( renderMode->getType() == name ) return true; } - return false; } + return false; } ext::vulkan::RenderMode& ext::vulkan::addRenderMode( ext::vulkan::RenderMode* mode, const std::string& name ) { @@ -180,7 +180,7 @@ ext::vulkan::RenderMode& ext::vulkan::addRenderMode( ext::vulkan::RenderMode* mo return *mode; } ext::vulkan::RenderMode& ext::vulkan::getRenderMode( const std::string& name, bool isName ) { - RenderMode* target = renderModes[renderModes.size()-1]; + RenderMode* target = renderModes[0]; for ( auto& renderMode: renderModes ) { if ( isName ) { if ( renderMode->getName() == "" ) target = renderMode; @@ -199,9 +199,12 @@ ext::vulkan::RenderMode& ext::vulkan::getRenderMode( const std::string& name, bo return *target; } std::vector ext::vulkan::getRenderModes( const std::string& name, bool isName ) { + return ext::vulkan::getRenderModes({name}, isName); +} +std::vector ext::vulkan::getRenderModes( const std::vector& names, bool isName ) { std::vector targets; for ( auto& renderMode: renderModes ) { - if ( ( isName && renderMode->getName() == name ) || renderMode->getType() == name ) { + if ( ( isName && std::find(names.begin(), names.end(), renderMode->getName()) != names.end() ) || std::find(names.begin(), names.end(), renderMode->getType()) != names.end() ) { targets.push_back(renderMode); // std::cout << "Requestings RenderMode `" << name << "`, got `" << renderMode->getName() << "` (" << renderMode->getType() << ")" << std::endl; } @@ -233,7 +236,7 @@ void ext::vulkan::initialize( uint8_t stage ) { }; Texture2D::empty.sampler.descriptor.filter.min = VK_FILTER_NEAREST; Texture2D::empty.sampler.descriptor.filter.mag = VK_FILTER_NEAREST; - Texture2D::empty.fromBuffers( (void*) &pixels[0], pixels.size(), VK_FORMAT_R8G8B8A8_UNORM, 2, 2, ext::vulkan::device, ext::vulkan::device.graphicsQueue, VK_IMAGE_USAGE_SAMPLED_BIT ); + Texture2D::empty.fromBuffers( (void*) &pixels[0], pixels.size(), VK_FORMAT_R8G8B8A8_UNORM, 2, 2, ext::vulkan::device, VK_IMAGE_USAGE_SAMPLED_BIT ); } for ( auto& renderMode : renderModes ) { if ( !renderMode ) continue; @@ -303,6 +306,11 @@ void ext::vulkan::tick() { } void ext::vulkan::render() { // ext::vulkan::mutex.lock(); + if ( hasRenderMode("Gui", true) ) { + RenderMode& primary = getRenderMode("Gui", true); + auto it = std::find( renderModes.begin(), renderModes.end(), &primary ); + if ( it + 1 != renderModes.end() ) std::rotate( it, it + 1, renderModes.end() ); + } if ( hasRenderMode("", true) ) { RenderMode& primary = getRenderMode("", true); auto it = std::find( renderModes.begin(), renderModes.end(), &primary ); diff --git a/ext/main.cpp b/ext/main.cpp index 11ef47b4..d48b3833 100644 --- a/ext/main.cpp +++ b/ext/main.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -107,26 +108,35 @@ void EXT_API ext::initialize() { for ( int i = 0; i < ::config["engine"]["ext"]["vulkan"]["validation"]["filters"].size(); ++i ) { ext::vulkan::validationFilters.push_back( ::config["engine"]["ext"]["vulkan"]["validation"]["filters"][i].asString() ); } + for ( int i = 0; i < ::config["engine"]["ext"]["vulkan"]["extensions"]["device"].size(); ++i ) { + ext::vulkan::requestedDeviceExtensions.push_back( ::config["engine"]["ext"]["vulkan"]["extensions"]["device"][i].asString() ); + } + for ( int i = 0; i < ::config["engine"]["ext"]["vulkan"]["extensions"]["instance"].size(); ++i ) { + ext::vulkan::requestedInstanceExtensions.push_back( ::config["engine"]["ext"]["vulkan"]["extensions"]["instance"][i].asString() ); + } for ( int i = 0; i < ::config["engine"]["ext"]["vulkan"]["features"].size(); ++i ) { ext::vulkan::requestedDeviceFeatures.push_back( ::config["engine"]["ext"]["vulkan"]["features"][i].asString() ); } - // - // ext::vulkan::DeferredRenderingGraphic::maxLights = ::config["engine"]["scenes"]["max lights"].asUInt(); - // ext::openvr::enabled = ::config["engine"]["ext"]["vr"]["enable"].asBool(); ext::openvr::swapEyes = ::config["engine"]["ext"]["vr"]["swap eyes"].asBool(); - // ext::openvr::dominantEye = ::config["engine"]["ext"]["vr"]["dominatEye"].asString() == "left" ? 0 : 1; - if ( ::config["engine"]["ext"]["vr"]["dominatEye"].asString() == "left" ) ext::openvr::dominantEye = 0; - if ( ::config["engine"]["ext"]["vr"]["dominatEye"].asString() == "right" ) ext::openvr::dominantEye = 1; + if ( ::config["engine"]["ext"]["vr"]["dominatEye"].isNumeric() ) + ext::openvr::dominantEye = ::config["engine"]["ext"]["vr"]["dominatEye"].asUInt64(); + else if ( ::config["engine"]["ext"]["vr"]["dominatEye"].asString() == "left" ) ext::openvr::dominantEye = 0; + else if ( ::config["engine"]["ext"]["vr"]["dominatEye"].asString() == "right" ) ext::openvr::dominantEye = 1; ext::openvr::driver.manifest = ::config["engine"]["ext"]["vr"]["manifest"].asString(); if ( ext::openvr::enabled ) { ::config["engine"]["render modes"]["stereo deferred"] = true; - // ext::vulkan::validation = false; - // if ( ::config["engine"]["ext"]["vr"]["invert winding order"].asBool() ) - // ext::vulkan::GraphicOld::DEFAULT_WINDING_ORDER = VK_FRONT_FACE_COUNTER_CLOCKWISE; } } + + /* Create initial scene (kludge) */ { + uf::Scene* scene = new uf::Scene; + uf::scene::scenes.push_back(scene); + auto& metadata = scene->getComponent(); + metadata["system"]["config"] = ::config; + } + /* Initialize Vulkan */ { // ext::vulkan::width = ::config["window"]["size"]["x"].asInt(); // ext::vulkan::height = ::config["window"]["size"]["y"].asInt(); @@ -137,11 +147,15 @@ void EXT_API ext::initialize() { ext::vulkan::addRenderMode( renderMode, "Gui" ); renderMode->blitter.descriptor.subpass = 1; } + if ( ::config["engine"]["render modes"]["stereo deferred"].asBool() ) ext::vulkan::addRenderMode( new ext::vulkan::StereoscopicDeferredRenderMode, "" ); else if ( ::config["engine"]["render modes"]["deferred"].asBool() ) ext::vulkan::addRenderMode( new ext::vulkan::DeferredRenderMode, "" ); + // if ( ::config["engine"]["render modes"]["compute"].asBool() ) + // ext::vulkan::addRenderMode( new ext::vulkan::ComputeRenderMode, "C:RT:0" ); + if ( ext::openvr::enabled ) { ext::openvr::initialize(); @@ -168,19 +182,24 @@ void EXT_API ext::initialize() { } /* Initialize root scene*/ { - uf::scene::loadScene( ::config["engine"]["scenes"]["start"].asString() ); + uf::scene::unloadScene(); + auto& scene = uf::scene::loadScene( ::config["engine"]["scenes"]["start"].asString() ); + auto& metadata = scene.getComponent(); + metadata["system"]["config"] = ::config; } /* Add hooks */ { uf::hooks.addHook( "game:LoadScene", [&](const std::string& event)->std::string{ uf::Serializer json = event; uf::scene::unloadScene(); - uf::scene::loadScene( json["scene"].asString() ); + auto& scene = uf::scene::loadScene( json["scene"].asString() ); + auto& metadata = scene.getComponent(); + metadata["system"]["config"] = ::config; return "true"; }); uf::hooks.addHook( "system:Quit", [&](const std::string& event)->std::string{ - std::cout << event << std::endl; + std::cout << "system:Quit: " << event << std::endl; ext::ready = false; return "true"; }); diff --git a/ext/scenes/raytrace/scene.cpp b/ext/scenes/raytrace/scene.cpp new file mode 100644 index 00000000..bfdf63d3 --- /dev/null +++ b/ext/scenes/raytrace/scene.cpp @@ -0,0 +1,320 @@ +#include "scene.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "../../ext.h" +#include "../../gui/gui.h" + +EXT_OBJECT_REGISTER_CPP(RaytracedScene) +void ext::RaytracedScene::initialize() { + uf::Scene::initialize(); + uf::Serializer& metadata = this->getComponent(); + uf::Asset& assetLoader = this->getComponent(); + + { + auto& renderMode = this->getComponent(); + std::string name = "C:RT:" + std::to_string((int) this->getUid()); + ext::vulkan::addRenderMode( &renderMode, name ); + if ( metadata["light"]["shadows"]["resolution"].isArray() ) { + renderMode.width = metadata["light"]["shadows"]["resolution"][0].asUInt64(); + renderMode.height = metadata["light"]["shadows"]["resolution"][1].asUInt64(); + } else { + renderMode.width = metadata["light"]["shadows"]["resolution"].asUInt64(); + renderMode.height = metadata["light"]["shadows"]["resolution"].asUInt64(); + } + { + struct Shape { + pod::Vector4f values; + pod::Vector3f diffuse; + float specular; + uint32_t id; + pod::Vector3ui _pad; + }; + + std::vector shapes; + { + shapes.push_back( {{1.75f, -0.5f, 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f}, 32.0f, 1} ); + shapes.push_back( {{0.0f, 1.0f, -0.5f, 1.0f }, {0.65f, 0.77f, 0.97f}, 32.0f, 1} ); + shapes.push_back( {{-1.75f, -0.75f, -0.5f, 1.25f }, { 0.9f, 0.76f, 0.46f}, 32.0f, 1} ); + } + { + float roomDim = 12.0f; + shapes.push_back( {{0.0f, 1.0f, 0.0f, roomDim}, {1.0f, 1.0f, 1.0f}, 32.0f, 2} ); + shapes.push_back( {{0.0f, -1.0f, 0.0f, roomDim}, {1.0f, 1.0f, 1.0f}, 32.0f, 2} ); + shapes.push_back( {{0.0f, 0.0f, 1.0f, roomDim}, {1.0f, 1.0f, 1.0f}, 32.0f, 2} ); + shapes.push_back( {{0.0f, 0.0f, -1.0f, roomDim}, {0.0f, 0.0f, 0.0f}, 32.0f, 2} ); + shapes.push_back( {{-1.0f, 0.0f, 0.0f, roomDim}, {1.0f, 0.0f, 0.0f}, 32.0f, 2} ); + shapes.push_back( {{1.0f, 0.0f, 0.0f, roomDim}, {0.0f, 1.0f, 0.0f}, 32.0f, 2} ); + } + renderMode.compute.device = &ext::vulkan::device; + renderMode.compute.initializeBuffer( + (void*) shapes.data(), + shapes.size() * sizeof(Shape), + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + true + ); + } + } + + this->addHook( "system:Quit.%UID%", [&](const std::string& event)->std::string{ + std::cout << event << std::endl; + ext::ready = false; + return "true"; + }); + { + static uf::Timer timer(false); + if ( !timer.running() ) timer.start(); + this->addHook( "world:Entity.LoadAsset", [&](const std::string& event)->std::string{ + uf::Serializer json = event; + + std::string asset = json["asset"].asString(); + std::string uid = json["uid"].asString(); + + assetLoader.load(asset, "asset:Load." + uid); + + return "true"; + }); + } + { + static uf::Timer timer(false); + if ( !timer.running() ) timer.start(); + this->addHook( "menu:Pause", [&](const std::string& event)->std::string{ + if ( timer.elapsed().asDouble() < 1 ) return "false"; + timer.reset(); + + uf::Serializer json = event; + ext::Gui* manager = (ext::Gui*) this->findByName("Gui Manager"); + if ( !manager ) return "false"; + uf::Serializer payload; + ext::Gui* gui = (ext::Gui*) manager->findByUid( (payload["uid"] = manager->loadChild("/scenes/world/gui/pause/menu.json", false)).asUInt64() ); + uf::Serializer& metadata = gui->getComponent(); + metadata["menu"] = json["menu"]; + gui->initialize(); + return payload; + }); + } + + /* store viewport size */ { + metadata["window"]["size"]["x"] = ext::vulkan::width; + metadata["window"]["size"]["y"] = ext::vulkan::height; + + this->addHook( "window:Resized", [&](const std::string& event)->std::string{ + uf::Serializer json = event; + + pod::Vector2ui size; { + size.x = json["window"]["size"]["x"].asUInt64(); + size.y = json["window"]["size"]["y"].asUInt64(); + } + + metadata["window"] = json["window"]; + + return "true"; + }); + } + + // lock control + { + uf::Serializer payload; + payload["state"] = true; + uf::hooks.call("window:Mouse.CursorVisibility", payload); + uf::hooks.call("window:Mouse.Lock"); + } +} + +void ext::RaytracedScene::render() { + uf::Scene::render(); +} +void ext::RaytracedScene::destroy() { + if ( this->hasComponent() ) { + auto& renderMode = this->getComponent(); + ext::vulkan::removeRenderMode( &renderMode, false ); + } + uf::Scene::destroy(); +} +void ext::RaytracedScene::tick() { + uf::Scene::tick(); + + uf::Serializer& metadata = this->getComponent(); + uf::Asset& assetLoader = this->getComponent(); +#if 1 + if ( this->hasComponent() ) { + auto& renderMode = this->getComponent(); + /* Add lights to scene */ if ( renderMode.compute.initialized ) { + struct UniformDescriptor { + alignas(16) pod::Matrix4f matrices[2]; + alignas(16) pod::Vector4f ambient; + struct Light { + alignas(16) pod::Vector4f position; + alignas(16) pod::Vector4f color; + alignas(8) pod::Vector2i type; + alignas(16) pod::Matrix4f view; + alignas(16) pod::Matrix4f projection; + } lights; + }; + + struct SpecializationConstant { + int32_t eyes = 2; + int32_t maxLights = 16; + } specializationConstants; + + auto& shader = renderMode.compute.material.shaders.front(); + specializationConstants = shader.specializationConstants.get(); + + auto& scene = uf::scene::getCurrentScene(); + auto& controller = *scene.getController(); + auto& camera = controller.getComponent(); + auto& transform = controller.getComponent>(); + + auto& userdata = shader.uniforms.front(); + size_t uniforms_len = userdata.data().len; + uint8_t* uniforms_buffer = (uint8_t*) (void*) userdata; + UniformDescriptor* uniforms = (UniformDescriptor*) uniforms_buffer; + for ( size_t i = 0; i < 2; ++i ) { + uniforms->matrices[i] = uf::matrix::inverse( camera.getProjection(i) * camera.getView(i) ); + } + + // uniforms->lights.position = { 0, 0, 0, 32 }; + // uniforms->lights.color = { 1, 1, 1, 1 }; + + std::vector entities; + std::function filter = [&]( uf::Entity* entity ) { + if ( !entity || entity->getName() != "Light" ) return; + entities.push_back(entity); + }; + for ( uf::Scene* scene : ext::vulkan::scenes ) { if ( !scene ) continue; + scene->process(filter); + } + { + const pod::Vector3& position = controller.getComponent>().position; + std::sort( entities.begin(), entities.end(), [&]( const uf::Entity* l, const uf::Entity* r ){ + if ( !l ) return false; if ( !r ) return true; + if ( !l->hasComponent>() ) return false; if ( !r->hasComponent>() ) return true; + return uf::vector::magnitude( uf::vector::subtract( l->getComponent>().position, position ) ) < uf::vector::magnitude( uf::vector::subtract( r->getComponent>().position, position ) ); + } ); + } + + { + uf::Serializer& metadata = controller.getComponent(); + if ( metadata["light"]["should"].asBool() ) entities.push_back(&controller); + } + UniformDescriptor::Light* lights = (UniformDescriptor::Light*) &uniforms_buffer[sizeof(UniformDescriptor) - sizeof(UniformDescriptor::Light)]; + for ( size_t i = 0; i < specializationConstants.maxLights && i < entities.size(); ++i ) { + UniformDescriptor::Light& light = lights[i]; + uf::Entity* entity = entities[i]; + + pod::Transform<>& transform = entity->getComponent>(); + uf::Serializer& metadata = entity->getComponent(); + uf::Camera& camera = entity->getComponent(); + + light.position.x = transform.position.x; + light.position.y = transform.position.y; + light.position.z = transform.position.z; + + if ( entity == &controller ) light.position.y += 2; + + light.position.w = metadata["light"]["radius"].asFloat(); + + light.color.x = metadata["light"]["color"][0].asFloat(); + light.color.y = metadata["light"]["color"][1].asFloat(); + light.color.z = metadata["light"]["color"][2].asFloat(); + + light.color.w = metadata["light"]["power"].asFloat(); + } + + + shader.updateBuffer( (void*) uniforms_buffer, uniforms_len, 0, false ); + } + } +#endif + /* Regain control if nothing requests it */ { + ext::Gui* menu = (ext::Gui*) this->findByName("Gui: Menu"); + if ( !menu ) { + uf::Serializer payload; + payload["state"] = false; + uf::hooks.call("window:Mouse.CursorVisibility", payload); + uf::hooks.call("window:Mouse.Lock"); + } + } + + /* Updates Sound Listener */ { + pod::Transform<>& transform = this->getController()->getComponent>(); + + ext::oal.listener( "POSITION", { transform.position.x, transform.position.y, transform.position.z } ); + ext::oal.listener( "VELOCITY", { 0, 0, 0 } ); + ext::oal.listener( "ORIENTATION", { 0, 0, 1, 1, 0, 0 } ); + } + + /* Collision */ { + bool local = false; + bool sort = false; + bool useStrongest = false; + // pod::Thread& thread = uf::thread::fetchWorker(); + pod::Thread& thread = uf::thread::has("Physics") ? uf::thread::get("Physics") : uf::thread::create( "Physics", true, false ); + auto function = [&]() -> int { + std::vector entities; + std::function filter = [&]( uf::Entity* entity ) { + auto& metadata = entity->getComponent(); + if ( !metadata["system"]["physics"]["collision"].isNull() && !metadata["system"]["physics"]["collision"].asBool() ) return; + if ( entity->hasComponent() ) + entities.push_back((uf::Object*) entity); + }; + this->process(filter); + auto onCollision = []( pod::Collider::Manifold& manifold, uf::Object* a, uf::Object* b ){ + uf::Serializer payload; + payload["normal"][0] = manifold.normal.x; + payload["normal"][1] = manifold.normal.y; + payload["normal"][2] = manifold.normal.z; + payload["entity"] = b->getUid(); + payload["depth"] = -manifold.depth; + a->callHook("world:Collision.%UID%", payload); + + payload["entity"] = a->getUid(); + payload["depth"] = manifold.depth; + b->callHook("world:Collision.%UID%", payload); + }; + auto testColliders = [&]( uf::Collider& colliderA, uf::Collider& colliderB, uf::Object* a, uf::Object* b, bool useStrongest ){ + pod::Collider::Manifold strongest; + auto manifolds = colliderA.intersects(colliderB); + for ( auto manifold : manifolds ) { + if ( manifold.colliding && manifold.depth > 0 ) { + if ( !useStrongest ) onCollision(manifold, a, b); + else if ( strongest.depth < manifold.depth ) strongest = manifold; + } + } + if ( useStrongest && strongest.colliding ) onCollision(strongest, a, b); + }; + // collide with others + for ( auto* _a : entities ) { + uf::Object& entityA = *_a; + for ( auto* _b : entities ) { if ( _a == _b ) continue; + uf::Object& entityB = *_b; + testColliders( entityA.getComponent(), entityB.getComponent(), &entityA, &entityB, useStrongest ); + } + } + + return 0; + }; + if ( local ) function(); else uf::thread::add( thread, function, true ); + } +} \ No newline at end of file diff --git a/ext/scenes/raytrace/scene.h b/ext/scenes/raytrace/scene.h new file mode 100644 index 00000000..4563a59e --- /dev/null +++ b/ext/scenes/raytrace/scene.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include + +namespace ext { + class EXT_API RaytracedScene : public uf::Scene { + public: + virtual void initialize(); + virtual void tick(); + virtual void render(); + virtual void destroy(); + }; +} \ No newline at end of file diff --git a/ext/scenes/world/world.cpp b/ext/scenes/world/world.cpp index 9c1bb015..9e2bd434 100644 --- a/ext/scenes/world/world.cpp +++ b/ext/scenes/world/world.cpp @@ -293,26 +293,6 @@ void ext::World::tick() { ext::oal.listener( "VELOCITY", { 0, 0, 0 } ); ext::oal.listener( "ORIENTATION", { 0, 0, 1, 1, 0, 0 } ); } - - /**/ if ( false ) { - uf::Serializer& metadata = this->getComponent(); - auto& scene = *this; - std::vector blitters; - auto& renderMode = ext::vulkan::getRenderMode("", true); - if ( renderMode.getType() == "Deferred (Stereoscopic)" ) { - auto* renderModePointer = (ext::vulkan::StereoscopicDeferredRenderMode*) &renderMode; - blitters.push_back(&renderModePointer->blitters.left); - blitters.push_back(&renderModePointer->blitters.right); - } else if ( renderMode.getType() == "Deferred" ) { - auto* renderModePointer = (ext::vulkan::DeferredRenderMode*) &renderMode; - blitters.push_back(&renderModePointer->blitter); - } - for ( size_t _ = 0; _ < blitters.size(); ++_ ) { - auto& blitter = *blitters[_]; - blitter.getPipeline().update( blitter ); - } - } - } void ext::World::render() {