diff --git a/README.md b/README.md index bec6ca4..3cfb144 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,18 @@ Vulkan Flocking: compute and shading in one pipeline! **University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 6** -* (TODO) YOUR NAME HERE - Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +* Daniel Krupka + Debian testing (stretch), Intel(R) Core(TM) i7-4710HQ CPU @ 2.50GHz 8GB, GTX 850M - ### (TODO: Your README) - - Include screenshots, analysis, etc. (Remember, this is public, so don't put - anything here that you don't want to share with the world.) +* Questions: +** Vulkan requires explicit descriptors for most things as, like memory, pipelines and commands and the like +are pre-allocated or pre-registered in order to guarantee that on-the-fly resource allocation/validation/controls +do not occur and cause unexpected latencies. +** Instancing, e.g. changing textures and parameters without changing shaders. +** Resources must be properly fenced to ensure that they are not invalidly in flight (e.g. being overwritten while being +used) in multiple contexts (e.g. different GPUs, different command queues). +** The largest benefit is more efficient visualization of compute results, e.g. displaying path tracer results without +much copying framebuffers from CUDA to GL. ### Credits diff --git a/data/shaders/computeparticles/particle.comp b/data/shaders/computeparticles/particle.comp index b7dc2f7..4f97312 100644 --- a/data/shaders/computeparticles/particle.comp +++ b/data/shaders/computeparticles/particle.comp @@ -58,6 +58,39 @@ void main() vec2 vPos = particlesA[index].pos.xy; vec2 vVel = particlesA[index].vel.xy; + vec2 ctr, v1, v2, v3; + float k1=0.0, k2=0.0, k3=0.0; + for (int i = 0; i < ubo.particleCount; i++) { + if (i == index) + continue; + + float dist = length(particlesA[i].pos.xy - vPos); + + if (dist < ubo.rule1Distance) { + ctr += particlesA[i].pos.xy; + k1++; + } + + if (dist < ubo.rule2Distance) { + v2 += vPos - particlesA[i].pos.xy; + k2++; + } + + if (dist < ubo.rule3Distance) { + v3 += particlesA[i].vel.xy; + k3++; + } + } + + vec2 dVel; + if (k1 > 0) + dVel += ubo.rule1Scale * (ctr/k1 - vPos); + if (k2 > 0) + dVel += ubo.rule2Scale * v2; + if (k3 > 0) + dVel += ubo.rule3Scale * (v3/k3 - vVel); + vVel += dVel; + // clamp velocity for a more pleasing simulation. vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1); diff --git a/data/shaders/computeparticles/particle.comp.spv b/data/shaders/computeparticles/particle.comp.spv index 059ab59..a0cc2cf 100644 Binary files a/data/shaders/computeparticles/particle.comp.spv and b/data/shaders/computeparticles/particle.comp.spv differ diff --git a/vulkanBoids/vulkanBoids.cpp b/vulkanBoids/vulkanBoids.cpp index 9b2f122..698a9f4 100644 --- a/vulkanBoids/vulkanBoids.cpp +++ b/vulkanBoids/vulkanBoids.cpp @@ -48,7 +48,7 @@ class VulkanExample : public VulkanExampleBase bool animate = true; // LOOK: this struct contains descriptions of how the vertex buffer should be interpreted by - // a strictly graphics pipeline. + // a strictly graphics pipeline. struct { // inputState encapsulates bindingDescriptions and attributeDescriptions VkPipelineVertexInputStateCreateInfo inputState; @@ -158,6 +158,7 @@ class VulkanExample : public VulkanExampleBase { particle.pos = glm::vec2(rDistribution(rGenerator), rDistribution(rGenerator)); // TODO: add randomized velocities with a slight scale here, something like 0.1f. + particle.vel = 0.1f * glm::vec2(rDistribution(rGenerator), rDistribution(rGenerator)); } VkDeviceSize storageBufferSize = particleBuffer.size() * sizeof(Particle); @@ -235,7 +236,7 @@ class VulkanExample : public VulkanExampleBase vertices.attributeDescriptions[0] = vkTools::initializers::vertexInputAttributeDescription( VERTEX_BUFFER_BIND_ID, - 0, // corresponds to `layout (location = 0) in` in particle.vert + 0, // corresponds to `layout (location = 0) in` in particle.vert VK_FORMAT_R32G32_SFLOAT, // what kind of data? vec2 offsetof(Particle, pos)); // offset into each Particle struct // Location 1 : Velocity @@ -244,7 +245,7 @@ class VulkanExample : public VulkanExampleBase VERTEX_BUFFER_BIND_ID, 1, VK_FORMAT_R32G32_SFLOAT, - offsetof(Particle, pos)); // TODO: change this so that we can color the particles based on velocity. + offsetof(Particle, vel)); // TODO: change this so that we can color the particles based on velocity. // vertices.inputState encapsulates everything we need for these particular buffers to // interface with the graphics pipeline. @@ -540,13 +541,34 @@ class VulkanExample : public VulkanExampleBase compute.descriptorSets[0], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2, - &compute.uniformBuffer.descriptor) + &compute.uniformBuffer.descriptor), // TODO: write the second descriptorSet, using the top for reference. // We want the descriptorSets to be used for flip-flopping: // on one frame, we use one descriptorSet with the compute pass, // on the next frame, we use the other. // What has to be different about how the second descriptorSet is written here? + + // Binding 0 : Particle position storage buffer + vkTools::initializers::writeDescriptorSet( + compute.descriptorSets[1], // LOOK: which descriptor set to write to? + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + 0, // LOOK: which binding in the descriptor set Layout? + &compute.storageBufferA.descriptor), // LOOK: which SSBO? + + // Binding 1 : Particle position storage buffer + vkTools::initializers::writeDescriptorSet( + compute.descriptorSets[1], + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + 1, + &compute.storageBufferB.descriptor), + + // Binding 2 : Uniform buffer + vkTools::initializers::writeDescriptorSet( + compute.descriptorSets[1], + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 2, + &compute.uniformBuffer.descriptor) }; vkUpdateDescriptorSets(device, static_cast(computeWriteDescriptorSets.size()), computeWriteDescriptorSets.data(), 0, NULL); @@ -590,6 +612,8 @@ class VulkanExample : public VulkanExampleBase // We also want to flip what SSBO we draw with in the next // pass through the graphics pipeline. // Feel free to use std::swap here. You should need it twice. + std::swap(compute.descriptorSets[0], compute.descriptorSets[1]); + std::swap(compute.storageBufferA, compute.storageBufferB); } // Record command buffers for drawing using the graphics pipeline @@ -671,7 +695,7 @@ class VulkanExample : public VulkanExampleBase bufferBarrier.size = compute.storageBufferA.descriptor.range; bufferBarrier.srcAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; // Vertex shader invocations have finished reading from the buffer bufferBarrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; // Compute shader wants to write to the buffer - + // Compute and graphics queue may have different queue families (see VulkanDevice::createLogicalDevice) // For the barrier to work across different queues, we need to set their family indices bufferBarrier.srcQueueFamilyIndex = vulkanDevice->queueFamilyIndices.graphics; // Required as compute and graphics queue may have different families